diff --git a/.github/scripts/check_duplicates.py b/.github/scripts/check_duplicates.py new file mode 100644 index 00000000..9c919414 --- /dev/null +++ b/.github/scripts/check_duplicates.py @@ -0,0 +1,51 @@ +import sys + + +def find_duplicate_keys(file_path): + """ + Finds duplicate keys in a properties file and returns their occurrences. + + This function reads a properties file, identifies any keys that occur more than + once, and returns a dictionary with these keys and the line numbers of their occurrences. + + Parameters: + file_path (str): The path to the properties file to be checked. + + Returns: + dict: A dictionary where each key is a duplicated key in the file, and the value is a list + of line numbers where the key occurs. + """ + with open(file_path, "r", encoding="utf-8") as file: + lines = file.readlines() + + keys = {} + duplicates = {} + + for line_number, line in enumerate(lines, start=1): + line = line.strip() + if line and not line.startswith("#") and "=" in line: + key = line.split("=", 1)[0].strip() + if key in keys: + # If the key already exists, add the current line number + duplicates.setdefault(key, []).append(line_number) + # Also add the first instance of the key if not already done + if keys[key] not in duplicates[key]: + duplicates[key].insert(0, keys[key]) + else: + # Store the line number of the first instance of the key + keys[key] = line_number + + return duplicates + + +if __name__ == "__main__": + failed = False + for ar in sys.argv[1:]: + duplicates = find_duplicate_keys(ar) + if duplicates: + for key, lines in duplicates.items(): + lines_str = ", ".join(map(str, lines)) + print(f"{key} duplicated in {ar} on lines {lines_str}") + failed = True + if failed: + sys.exit(1) diff --git a/.github/scripts/check_tabulator.py b/.github/scripts/check_tabulator.py new file mode 100644 index 00000000..eb408ad6 --- /dev/null +++ b/.github/scripts/check_tabulator.py @@ -0,0 +1,84 @@ +"""check_tabulator.py""" +import argparse +import sys + + +def check_tabs(file_path): + """ + Checks for tabs in the specified file. + + Args: + file_path (str): The path to the file to be checked. + + Returns: + bool: True if tabs are found, False otherwise. + """ + with open(file_path, "r", encoding="utf-8") as file: + content = file.read() + + if "\t" in content: + print(f"Tab found in {file_path}") + return True + return False + + +def replace_tabs_with_spaces(file_path, replace_with=" "): + """ + Replaces tabs with a specified number of spaces in the file. + + Args: + file_path (str): The path to the file where tabs will be replaced. + replace_with (str): The character(s) to replace tabs with. Defaults to two spaces. + """ + with open(file_path, "r", encoding="utf-8") as file: + content = file.read() + + updated_content = content.replace("\t", replace_with) + + with open(file_path, "w", encoding="utf-8") as file: + file.write(updated_content) + + +def main(): + """ + Main function to replace tabs with spaces in the provided files. + The replacement character and files to check are taken from command line arguments. + """ + # Create ArgumentParser instance + parser = argparse.ArgumentParser( + description="Replace tabs in files with specified characters." + ) + + # Define optional argument `--replace_with` + parser.add_argument( + "--replace_with", + default=" ", + help="Character(s) to replace tabs with. Default is two spaces.", + ) + + # Define argument for file paths + parser.add_argument("files", metavar="FILE", nargs="+", help="Files to process.") + + # Parse arguments + args = parser.parse_args() + + # Extract replacement characters and files from the parsed arguments + replace_with = args.replace_with + files_checked = args.files + + error = False + + for file_path in files_checked: + if check_tabs(file_path): + replace_tabs_with_spaces(file_path, replace_with) + error = True + + if error: + print("Error: Originally found tabs in HTML files, now replaced.") + sys.exit(1) + + sys.exit(0) + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/gradle_to_chart.py b/.github/scripts/gradle_to_chart.py new file mode 100644 index 00000000..4d7a667f --- /dev/null +++ b/.github/scripts/gradle_to_chart.py @@ -0,0 +1,67 @@ +import re +import yaml + +# Paths to the files +chart_yaml_path = "chart/stirling-pdf/Chart.yaml" +gradle_path = "build.gradle" + + +def get_chart_version(path): + """ + Reads the appVersion from Chart.yaml. + + Args: + path (str): The file path to the Chart.yaml. + + Returns: + str: The appVersion if found, otherwise an empty string. + """ + with open(path, encoding="utf-8") as file: + chart_yaml = yaml.safe_load(file) + return chart_yaml.get("appVersion", "") + + +def get_gradle_version(path): + """ + Extracts the version from build.gradle. + + Args: + path (str): The file path to the build.gradle. + + Returns: + str: The version if found, otherwise an empty string. + """ + with open(path, encoding="utf-8") as file: + for line in file: + if "version =" in line: + # Extracts the value after 'version =' + return re.search(r'version\s*=\s*[\'"](.+?)[\'"]', line).group(1) + return "" + + +def update_chart_version(path, new_version): + """ + Updates the appVersion in Chart.yaml with a new version. + + Args: + path (str): The file path to the Chart.yaml. + new_version (str): The new version to update to. + """ + with open(path, encoding="utf-8") as file: + chart_yaml = yaml.safe_load(file) + chart_yaml["appVersion"] = new_version + with open(path, "w", encoding="utf-8") as file: + yaml.safe_dump(chart_yaml, file) + + +# Main logic +chart_version = get_chart_version(chart_yaml_path) +gradle_version = get_gradle_version(gradle_path) + +if chart_version != gradle_version: + print( + f"Versions do not match. Updating Chart.yaml from {chart_version} to {gradle_version}." + ) + update_chart_version(chart_yaml_path, gradle_version) +else: + print("Versions match. No update required.") diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 74d1f8f5..f4d37599 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,9 +2,15 @@ name: "Build repo" on: push: - branches: [ "main" ] + branches: ["main"] + paths-ignore: + - ".github/**" + - "**/*.md" pull_request: - branches: [ "main" ] + branches: ["main"] + paths-ignore: + - ".github/**" + - "**/*.md" jobs: build: @@ -19,16 +25,18 @@ jobs: fail-fast: false steps: - - name: Checkout repository - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: "17" + distribution: "temurin" - - uses: gradle/gradle-build-action@v2.4.2 - with: - gradle-version: 7.6 - arguments: build --no-build-cache + - uses: gradle/actions/setup-gradle@v3 + with: + gradle-version: 7.6 + + - name: Build with Gradle + run: ./gradlew build --no-build-cache diff --git a/.github/workflows/licenses-update.yml b/.github/workflows/licenses-update.yml index 991ccafd..a3996bc6 100644 --- a/.github/workflows/licenses-update.yml +++ b/.github/workflows/licenses-update.yml @@ -5,7 +5,7 @@ on: branches: - main paths: - - 'build.gradle' + - "build.gradle" permissions: contents: write @@ -17,13 +17,15 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: - java-version: '17' - distribution: 'adopt' + java-version: "17" + distribution: "adopt" + + - uses: gradle/actions/setup-gradle@v3 - name: Run Gradle Command run: ./gradlew clean generateLicenseReport @@ -32,17 +34,29 @@ jobs: run: | mv build/reports/dependency-license/index.json src/main/resources/static/3rdPartyLicenses.json - - name: Check for Changes - id: git-check + - name: Set up git config + run: | + git config --global user.email "GitHub Action " + git config --global user.name "GitHub Action " + + - name: Run git add run: | git add src/main/resources/static/3rdPartyLicenses.json - git diff --staged --exit-code || echo "changes=true" >> $GITHUB_ENV - - - name: Commit and Push Changes - if: env.changes == 'true' - run: | - git config --global user.name 'Stirling-PDF-Bot' - git config --global user.email 'Stirling-PDF-Bot@stirlingtools.com' - git commit -m "Update 3rd Party Licenses" - git push + git diff --staged --quiet || echo "CHANGES_DETECTED=true" >> $GITHUB_ENV + - name: Create Pull Request + if: env.CHANGES_DETECTED == 'true' + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "Update 3rd Party Licenses" + committer: GitHub Action + author: GitHub Action + signoff: true + branch: update-3rd-party-licenses + title: "Update 3rd Party Licenses" + body: | + Auto-generated by [create-pull-request][1] + [1]: https://github.com/peter-evans/create-pull-request + draft: false + delete-branch: true diff --git a/.github/workflows/push-docker.yml b/.github/workflows/push-docker.yml index 6dff5cbb..f8fc4200 100644 --- a/.github/workflows/push-docker.yml +++ b/.github/workflows/push-docker.yml @@ -6,6 +6,7 @@ on: branches: - master - main + permissions: contents: read packages: write @@ -13,139 +14,99 @@ jobs: push: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - - uses: actions/checkout@v3.5.2 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: "17" + distribution: "temurin" - - name: Set up JDK 17 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' + - uses: gradle/actions/setup-gradle@v3 + with: + gradle-version: 7.6 + - name: Run Gradle Command + run: ./gradlew clean build + env: + DOCKER_ENABLE_SECURITY: false - - uses: gradle/gradle-build-action@v2.4.2 - env: - DOCKER_ENABLE_SECURITY: false - with: - gradle-version: 7.6 - arguments: clean build + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3 - - name: Make Gradle wrapper executable - run: chmod +x gradlew + - name: Get version number + id: versionNumber + run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT - - name: Get version number - id: versionNumber - run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)" + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_API }} - - name: Login to Docker Hub - uses: docker/login-action@v2.1.0 - with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_API }} + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} - - name: Login to GitHub Container Registry - uses: docker/login-action@v2.1.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ github.token }} + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 - - name: Convert repository owner to lowercase - id: repoowner - run: echo "::set-output name=lowercase::$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" + - name: Convert repository owner to lowercase + id: repoowner + run: echo "lowercase=$(echo ${{ github.repository_owner }} | awk '{print tolower($0)}')" >> $GITHUB_OUTPUT - - name: Generate tags - id: meta - uses: docker/metadata-action@v4.4.0 - with: - images: | - ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }} - type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }} - type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }} + - name: Generate tags + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf + ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf + tags: | + type=raw,value=${{ steps.versionNumber.outputs.versionNumber }},enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }} - - name: Set up QEMU - uses: docker/setup-qemu-action@v2.1.0 + - name: Build and push main Dockerfile + uses: docker/build-push-action@v5 + with: + builder: ${{ steps.buildx.outputs.name }} + context: . + file: ./Dockerfile + push: true + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} + platforms: linux/amd64,linux/arm64/v8 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2.5.0 + - name: Generate tags ultra-lite + id: meta2 + uses: docker/metadata-action@v5 + if: github.ref != 'refs/heads/main' + with: + images: | + ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf + ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf + tags: | + type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }} - - name: Build and push main Dockerfile - uses: docker/build-push-action@v4.0.0 - with: - context: . - dockerfile: ./Dockerfile - push: true - cache-from: type=gha - cache-to: type=gha,mode=max - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: - VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} - platforms: linux/amd64,linux/arm64/v8 - - - - - name: Generate tags ultra-lite - id: meta2 - uses: docker/metadata-action@v4.4.0 - if: github.ref != 'refs/heads/main' - with: - images: | - ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }} - type=raw,value=latest-ultra-lite,enable=${{ github.ref == 'refs/heads/master' }} - - - - name: Build and push Dockerfile-ultra-lite - uses: docker/build-push-action@v4.0.0 - if: github.ref != 'refs/heads/main' - with: - context: . - file: ./Dockerfile-ultra-lite - push: true - cache-from: type=gha - cache-to: type=gha,mode=max - tags: ${{ steps.meta2.outputs.tags }} - labels: ${{ steps.meta2.outputs.labels }} - build-args: - VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} - platforms: linux/amd64,linux/arm64/v8 - - - - - name: Generate tags lite - id: meta3 - uses: docker/metadata-action@v4.4.0 - if: github.ref != 'refs/heads/main' - with: - images: | - ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf - ghcr.io/${{ steps.repoowner.outputs.lowercase }}/s-pdf - tags: | - type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-lite,enable=${{ github.ref == 'refs/heads/master' }} - type=raw,value=latest-lite,enable=${{ github.ref == 'refs/heads/master' }} - - - - name: Build and push Dockerfile-lite - uses: docker/build-push-action@v4.0.0 - if: github.ref != 'refs/heads/main' - with: - context: . - file: ./Dockerfile-lite - push: true - cache-from: type=gha - cache-to: type=gha,mode=max - tags: ${{ steps.meta3.outputs.tags }} - labels: ${{ steps.meta3.outputs.labels }} - build-args: - VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} - platforms: linux/amd64,linux/arm64/v8 - - name: Build and Push Helm Chart - run: | - helm package chart/stirling-pdf - helm push stirling-pdf-chart-1.0.0.tgz oci://registry-1.docker.io/frooodle + - name: Build and push Dockerfile-ultra-lite + uses: docker/build-push-action@v5 + if: github.ref != 'refs/heads/main' + with: + context: . + file: ./Dockerfile-ultra-lite + push: true + cache-from: type=gha + cache-to: type=gha,mode=max + tags: ${{ steps.meta2.outputs.tags }} + labels: ${{ steps.meta2.outputs.labels }} + build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }} + platforms: linux/amd64,linux/arm64/v8 diff --git a/.github/workflows/releaseArtifacts.yml b/.github/workflows/releaseArtifacts.yml index 5042141a..f9b5f2b0 100644 --- a/.github/workflows/releaseArtifacts.yml +++ b/.github/workflows/releaseArtifacts.yml @@ -1,6 +1,7 @@ name: Release Artifacts on: + workflow_dispatch: release: types: [created] permissions: @@ -14,44 +15,61 @@ jobs: enable_security: [true, false] include: - enable_security: true - file_suffix: '-with-login' + file_suffix: "-with-login" - enable_security: false - file_suffix: '' + file_suffix: "" steps: - - uses: actions/checkout@v3.5.2 + - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: "17" + distribution: "temurin" - - name: Grant execute permission for gradlew - run: chmod +x gradlew + - uses: gradle/actions/setup-gradle@v3 + with: + gradle-version: 7.6 - - name: Generate jar (With Security=${{ matrix.enable_security }}) - run: ./gradlew clean createExe - env: - DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }} + - name: Generate jar (With Security=${{ matrix.enable_security }}) + run: ./gradlew clean createExe + env: + DOCKER_ENABLE_SECURITY: ${{ matrix.enable_security }} - - name: Upload binaries to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ./build/launch4j/Stirling-PDF.exe - asset_name: Stirling-PDF${{ matrix.file_suffix }}.exe - tag: ${{ github.ref }} - overwrite: true + - name: Get version number + id: versionNumber + run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT - - name: Get version number - id: versionNumber - run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)" + - name: Rename binarie + if: matrix.file_suffix != '' + run: cp ./build/launch4j/Stirling-PDF.exe ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe - - name: Upload jar binaries to release - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar - asset_name: Stirling-PDF${{ matrix.file_suffix }}.jar - tag: ${{ github.ref }} - overwrite: true + - name: Upload Assets binarie + uses: actions/upload-artifact@v4 + with: + path: ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe + name: Stirling-PDF${{ matrix.file_suffix }}.exe + overwrite: true + retention-days: 1 + if-no-files-found: error + - name: Upload binaries to release + uses: softprops/action-gh-release@v2 + with: + files: ./build/launch4j/Stirling-PDF${{ matrix.file_suffix }}.exe + + - name: Rename jar binaries + run: cp ./build/libs/Stirling-PDF-${{ steps.versionNumber.outputs.versionNumber }}.jar ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar + + - name: Upload Assets jar binaries + uses: actions/upload-artifact@v4 + with: + path: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar + name: Stirling-PDF${{ matrix.file_suffix }}.jar + overwrite: true + retention-days: 1 + if-no-files-found: error + + - name: Upload jar binaries to release + uses: softprops/action-gh-release@v2 + with: + files: ./build/libs/Stirling-PDF${{ matrix.file_suffix }}.jar diff --git a/.github/workflows/swagger.yml b/.github/workflows/swagger.yml index 60c7aa1d..88e6df7e 100644 --- a/.github/workflows/swagger.yml +++ b/.github/workflows/swagger.yml @@ -5,33 +5,35 @@ on: push: branches: - master + jobs: push: - runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 - - uses: actions/checkout@v3.5.2 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: "17" + distribution: "temurin" - - name: Set up JDK 17 - uses: actions/setup-java@v3.11.0 - with: - java-version: '17' - distribution: 'temurin' + - uses: gradle/actions/setup-gradle@v3 - - name: Grant execute permission for gradlew - run: chmod +x gradlew + - name: Generate Swagger documentation + run: ./gradlew generateOpenApiDocs - - name: Generate Swagger documentation - run: ./gradlew generateOpenApiDocs + - name: Upload Swagger Documentation to SwaggerHub + run: ./gradlew swaggerhubUpload + env: + SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }} - - name: Upload Swagger Documentation to SwaggerHub - run: ./gradlew swaggerhubUpload - env: - SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }} + - name: Get version number + id: versionNumber + run: echo "versionNumber=$(./gradlew printVersion --quiet | tail -1)" >> $GITHUB_OUTPUT - - name: Set API version as published and default on SwaggerHub - run: | - curl -X PUT -H "Authorization: ${SWAGGERHUB_API_KEY}" "https://api.swaggerhub.com/apis/Frooodle/Stirling-PDF/${{ steps.versionNumber.outputs.versionNumber }}/settings/lifecycle" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"published\":true,\"default\":true}" - env: - SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }} \ No newline at end of file + - name: Set API version as published and default on SwaggerHub + run: | + curl -X PUT -H "Authorization: ${SWAGGERHUB_API_KEY}" "https://api.swaggerhub.com/apis/Frooodle/Stirling-PDF/${{ steps.versionNumber.outputs.versionNumber }}/settings/lifecycle" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"published\":true,\"default\":true}" + env: + SWAGGERHUB_API_KEY: ${{ secrets.SWAGGERHUB_API_KEY }} diff --git a/.github/workflows/sync_files.yml b/.github/workflows/sync_files.yml new file mode 100644 index 00000000..ac1556cd --- /dev/null +++ b/.github/workflows/sync_files.yml @@ -0,0 +1,90 @@ +name: Sync Files + +on: + push: + branches: + - main + paths: + - "build.gradle" + - "src/main/resources/messages_*.properties" + - "scripts/translation_status.toml" + +permissions: + contents: write + pull-requests: write + +jobs: + sync-versions: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + - name: Set up Python + uses: actions/setup-python@v5.1.0 + with: + python-version: "3.x" + - name: Install dependencies + run: pip install pyyaml + - name: Sync versions + run: python .github/scripts/gradle_to_chart.py + - name: Set up git config + run: | + git config --global user.email "GitHub Action " + git config --global user.name "GitHub Action " + - name: Run git add + run: | + git add . + git diff --staged --quiet || git commit -m ":floppy_disk: Sync Versions + > Made via sync_files.yml" || echo "no changes" + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6.0.1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: Update files + committer: GitHub Action + author: GitHub Action + signoff: true + branch: sync_version + title: ":floppy_disk: Update Version" + body: | + Auto-generated by [create-pull-request][1] + + [1]: https://github.com/peter-evans/create-pull-request + draft: false + delete-branch: true + sync-readme: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + - name: Set up Python + uses: actions/setup-python@v5.1.0 + with: + python-version: "3.x" + - name: Install dependencies + run: pip install tomlkit + - name: Sync README + run: python scripts/counter_translation.py + - name: Set up git config + run: | + git config --global user.email "GitHub Action " + git config --global user.name "GitHub Action " + - name: Run git add + run: | + git add . + git diff --staged --quiet || git commit -m ":memo: Sync README + > Made via sync_files.yml" || echo "no changes" + - name: Create Pull Request + uses: peter-evans/create-pull-request@v6.0.1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: Update files + committer: GitHub Action + author: GitHub Action + signoff: true + branch: sync_readme + title: ":memo: Update README: Translation Progress Table" + body: | + Auto-generated by [create-pull-request][1] + + [1]: https://github.com/peter-evans/create-pull-request + draft: false + delete-branch: true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1920a339..eef33335 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,54 +3,36 @@ name: Docker Compose Tests on: pull_request: paths: - - 'src/**' - - '**.gradle' - - '!src/main/java/resources/messages*' - - 'exampleYmlFiles/**' - - 'Dockerfile' - - 'Dockerfile**' + - "src/**" + - "**.gradle" + - "!src/main/java/resources/messages*" + - "exampleYmlFiles/**" + - "Dockerfile" + - "Dockerfile**" jobs: test: runs-on: ubuntu-latest steps: - - name: Checkout Repository - uses: actions/checkout@v2 + - name: Checkout Repository + uses: actions/checkout@v4 - - name: Set up Java 17 - uses: actions/setup-java@v2 - with: - java-version: '17' - distribution: 'adopt' + - name: Set up Java 17 + uses: actions/setup-java@v4 + with: + java-version: "17" + distribution: "adopt" - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - - name: Run Docker Compose Tests - run: | - chmod +x ./gradlew + - name: Install Docker Compose + run: | + sudo curl -SL "https://github.com/docker/compose/releases/download/v2.26.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + # sudo chmod +x /usr/local/bin/docker-compose - - name: Get version number - id: versionNumber - run: echo "::set-output name=versionNumber::$(./gradlew printVersion --quiet | tail -1)" - - - - name: Cache Docker layers - uses: actions/cache@v2 - with: - path: /tmp/.buildx-cache - key: ${{ runner.os }}-buildx-${{ steps.versionNumber.outputs.versionNumber }} - restore-keys: | - ${{ runner.os }}-buildx- - - - name: Install Docker Compose - run: | - sudo curl -L "https://github.com/docker/compose/releases/download/v2.6.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - - - - name: Run Docker Compose Tests - run: | - chmod +x ./test.sh - ./test.sh + - name: Run Docker Compose Tests + run: | + chmod +x ./test.sh + ./test.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..e397fc5d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,37 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.2.1 + hooks: + - id: ruff + args: + - --fix + - --line-length=127 + files: ^((.github/scripts)/.+)?[^/]+\.py$ + - id: ruff-format + files: ^((.github/scripts)/.+)?[^/]+\.py$ + - repo: https://github.com/codespell-project/codespell + rev: v2.2.6 + hooks: + - id: codespell + args: + - --ignore-words-list= + - --skip="./.*,*.csv,*.json,*.ambr" + - --quiet-level=2 + files: \.(properties|html|css|js|py|md)$ + exclude: (.vscode|.devcontainer|src/main/resources|Dockerfile) + - repo: local + hooks: + - id: check-duplicate-properties-keys + name: Check Duplicate Properties Keys + entry: python .github/scripts/check_duplicates.py + language: python + files: ^(src)/.+\.properties$ + - repo: local + hooks: + - id: check-html-tabs + name: Check HTML for tabs + # args: ["--replace_with= "] + entry: python .github/scripts/check_tabulator.py + language: python + exclude: ^src/main/resources/static/pdfjs/ + files: ^.*(\.html|\.css|\.js)$ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3befe050..646bdddb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,10 @@ Please make sure your Pull Request adheres to the following guidelines: If you would like to add or modify a translation, please see [How to add new languages to Stirling-PDF](HowToAddNewLanguage.md). Also, please create a Pull Request so others can use it! +## Docs + +Documentation for Stirling-PDF is handled in a seperate repository. Please see [Docs repository](https://github.com/Stirling-Tools/Stirling-Tools.github.io) or use "edit this page"-button at the bottom of each page at [https://stirlingtools.com/docs/](https://stirlingtools.com/docs/). + ## Fixing Bugs or Adding a New Feature First, make sure you've read the section [Pull Requests](#pull-requests). diff --git a/Dockerfile b/Dockerfile index 4a520613..651a16d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,46 @@ # Main stage -FROM alpine:3.19.1 +FROM alpine:20240329 + +# Copy necessary files +COPY scripts /scripts +COPY pipeline /pipeline +COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/ +#COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto/ +COPY build/libs/*.jar app.jar + +ARG VERSION_TAG + + +# Set Environment Variables +ENV DOCKER_ENABLE_SECURITY=false \ + VERSION_TAG=$VERSION_TAG \ + JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \ + HOME=/home/stirlingpdfuser \ + PUID=1000 \ + PGID=1000 \ + UMASK=022 + # JDK for app RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \ echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \ echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \ + apk update && \ apk add --no-cache \ ca-certificates \ tzdata \ tini \ + openssl \ +openssl-dev \ bash \ curl \ openjdk17-jre \ + su-exec \ + shadow \ # Doc conversion libreoffice@testing \ +# pdftohtml + poppler-utils \ # OCR MY PDF (unpaper for descew and other advanced featues) ocrmypdf \ tesseract-ocr-data-eng \ @@ -24,46 +51,20 @@ RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /et wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \ # uno unoconv and HTML pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \ - mv /usr/share/tessdata /usr/share/tessdata-original - - - -ARG VERSION_TAG - -# Set Environment Variables -ENV DOCKER_ENABLE_SECURITY=false \ - HOME=/home/stirlingpdfuser \ - VERSION_TAG=$VERSION_TAG \ - JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" -# PUID=1000 \ -# PGID=1000 \ -# UMASK=022 \ - -# Copy necessary files -COPY scripts /scripts -COPY pipeline /pipeline -COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto -COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto -COPY build/libs/*.jar app.jar - -# Create user and group -##RUN groupadd -g $PGID stirlingpdfgroup && \ -## useradd -u $PUID -g stirlingpdfgroup -s /bin/sh stirlingpdfuser && \ -## mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME && \ -# Set up necessary directories and permissions -RUN mkdir -p /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \ -##&& \ -## chown -R stirlingpdfuser:stirlingpdfgroup /scripts /usr/share/fonts/opentype/noto /usr/share/tesseract-ocr /configs /customFiles && \ -## chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/tesseract-ocr-original && \ -# Set font cache and permissions + mv /usr/share/tessdata /usr/share/tessdata-original && \ + mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \ fc-cache -f -v && \ - chmod +x /scripts/* -## chown stirlingpdfuser:stirlingpdfgroup /app.jar && \ -## chmod +x /scripts/init.sh + chmod +x /scripts/* && \ + chmod +x /scripts/init.sh && \ +# User permissions + addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \ + chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline && \ + chown stirlingpdfuser:stirlingpdfgroup /app.jar && \ + tesseract --list-langs && \ + rm -rf /var/cache/apk/* EXPOSE 8080 # Set user and run command -##USER stirlingpdfuser ENTRYPOINT ["tini", "--", "/scripts/init.sh"] CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"] diff --git a/Dockerfile-lite b/Dockerfile-lite deleted file mode 100644 index f7dfa219..00000000 --- a/Dockerfile-lite +++ /dev/null @@ -1,61 +0,0 @@ -# use alpine -FROM alpine:3.19.1 - -ARG VERSION_TAG - -# Set Environment Variables -ENV DOCKER_ENABLE_SECURITY=false \ - HOME=/home/stirlingpdfuser \ - VERSION_TAG=$VERSION_TAG \ - JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" -# PUID=1000 \ -# PGID=1000 \ -# UMASK=022 \ - -# Copy necessary files -COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh -COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh -COPY pipeline /pipeline -COPY src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto -COPY src/main/resources/static/fonts/*.otf /usr/share/fonts/opentype/noto -COPY build/libs/*.jar app.jar - -RUN echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \ - echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \ - echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \ - apk add --no-cache \ - ca-certificates \ - tzdata \ - tini \ - bash \ - curl \ - openjdk17-jre \ -# Doc conversion - libreoffice@testing \ -# python and pip - python3 && \ - wget https://bootstrap.pypa.io/get-pip.py -qO - | python3 - --break-system-packages --no-cache-dir --upgrade && \ -# uno unoconv and HTML - pip install --break-system-packages --no-cache-dir --upgrade unoconv WeasyPrint && \ -# Create user and group -#RUN groupadd -g $PGID stirlingpdfgroup && \ -# useradd -u $PUID -g stirlingpdfgroup -s /bin/sh stirlingpdfuser && \ -# mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME -# Set up necessary directories and permissions - mkdir -p /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders && \ -# chown -R stirlingpdfuser:stirlingpdfgroup /usr/share/fonts/opentype/noto /configs /customFiles -# Set font cache and permissions - fc-cache -f -v && \ - chmod +x /scripts/*.sh -# chown stirlingpdfuser:stirlingpdfgroup /app.jar - -# Set environment variables -ENV ENDPOINTS_GROUPS_TO_REMOVE=OpenCV,OCRmyPDF -ENV DOCKER_ENABLE_SECURITY=false - -EXPOSE 8080 - -# Run the application -#USER stirlingpdfuser -ENTRYPOINT ["tini", "--", "/scripts/init-without-ocr.sh"] -CMD ["java", "-Dfile.encoding=UTF-8", "-jar", "/app.jar"] diff --git a/Dockerfile-ultra-lite b/Dockerfile-ultra-lite index d602a361..eed8d783 100644 --- a/Dockerfile-ultra-lite +++ b/Dockerfile-ultra-lite @@ -7,10 +7,10 @@ ARG VERSION_TAG ENV DOCKER_ENABLE_SECURITY=false \ HOME=/home/stirlingpdfuser \ VERSION_TAG=$VERSION_TAG \ - JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" -# PUID=1000 \ -# PGID=1000 \ -# UMASK=022 \ + JAVA_TOOL_OPTIONS="$JAVA_TOOL_OPTIONS -XX:MaxRAMPercentage=75" \ + PUID=1000 \ + PGID=1000 \ + UMASK=022 # Copy necessary files COPY scripts/download-security-jar.sh /scripts/download-security-jar.sh @@ -18,16 +18,10 @@ COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh COPY pipeline /pipeline COPY build/libs/*.jar app.jar -# Create user and group using Alpine's addgroup and adduser -#RUN addgroup -g $PGID stirlingpdfgroup && \ -# adduser -u $PUID -G stirlingpdfgroup -s /bin/sh -D stirlingpdfuser && \ -# mkdir -p $HOME && chown stirlingpdfuser:stirlingpdfgroup $HOME + # Set up necessary directories and permissions -#RUN mkdir -p /scripts /configs /customFiles && \ -# chown -R stirlingpdfuser:stirlingpdfgroup /scripts /configs /customFiles /logs /pipeline /pipeline/defaultWebUIConfigs /pipeline/watchedFolders /pipeline/finishedFolders + RUN mkdir /configs /logs /customFiles && \ -# Set font cache and permissions -#RUN chown stirlingpdfuser:stirlingpdfgroup /app.jar chmod +x /scripts/*.sh && \ apk add --no-cache \ ca-certificates \ @@ -35,10 +29,16 @@ RUN mkdir /configs /logs /customFiles && \ tini \ bash \ curl \ + su-exec \ + shadow \ openjdk17-jre && \ echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \ echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \ - echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories + echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \ + # User permissions + addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \ + chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /configs /customFiles /pipeline && \ + chown stirlingpdfuser:stirlingpdfgroup /app.jar # Set environment variables ENV ENDPOINTS_GROUPS_TO_REMOVE=CLI diff --git a/FolderScanning.md b/FolderScanning.md index 6b743a7f..140c9f5f 100644 --- a/FolderScanning.md +++ b/FolderScanning.md @@ -1,13 +1,5 @@ ## User Guide for Local Directory Scanning and File Processing -### Whilst Pipelines are in alpha... -You must enable this alpha functionality by setting -```yaml -system: - enableAlphaFunctionality: true -``` -To true like in the above for your `/config/settings.yml` file, after restarting Stirling-PDF you should see both UI and folder scanning enabled. - ### Setting Up Watched Folders: - Create a folder where you want your files to be monitored. This is your 'watched folder'. - The default directory for this is `./pipeline/watchedFolders/` diff --git a/HowToAddNewLanguage.md b/HowToAddNewLanguage.md index 9f8e3f39..73cad0e9 100644 --- a/HowToAddNewLanguage.md +++ b/HowToAddNewLanguage.md @@ -12,7 +12,7 @@ https://github.com/Stirling-Tools/Stirling-PDF/blob/main/src/main/resources/temp and add a flag svg file to https://github.com/Stirling-Tools/Stirling-PDF/tree/main/src/main/resources/static/images/flags Any SVG flags are fine, i got most of mine from [here](https://flagicons.lipis.dev/) -If your language isnt represented by a flag just find whichever closely matches it, such as for Arabic i chose Saudi Arabia +If your language isn't represented by a flag just find whichever closely matches it, such as for Arabic i chose Saudi Arabia For example to add Polish you would add @@ -32,7 +32,20 @@ Copy and rename it to messages_{your data-language-code here}.properties, in the Then simply translate all property entries within that file and make a PR into main for others to use! -If you do not have a java IDE i am happy to verify the changes worked once you raise PR (but wont be able to verify the translations themselves) +If you do not have a java IDE i am happy to verify the changes worked once you raise PR (but won't be able to verify the translations themselves) +## Handling Untranslatable Strings +Sometimes, certain strings in the properties file may not require translation because they are the same in the target language or are universal (like names of protocols, certain terminologies, etc.). To ensure accurate statistics for language progress, these strings should be added to the `ignore_translation.toml` file located in the `scripts` directory. This will exclude them from the translation progress calculations. +For example, if the English string error=Error does not need translation in Polish, add it to the ignore_translation.toml under the Polish section: + +```toml +[pl_PL] +ignore = [ + "language.direction", # Existing entries + "error" # Add new entries here +] +``` + +Make sure to place the entry under the correct language section. This helps maintain the accuracy of translation progress statistics and ensures that the translation tool or scripts do not misinterpret the completion rate. diff --git a/HowToUseOCR.md b/HowToUseOCR.md index 388ab438..8607c28d 100644 --- a/HowToUseOCR.md +++ b/HowToUseOCR.md @@ -2,8 +2,8 @@ This document provides instructions on how to add additional language packs for the OCR tab in Stirling-PDF, both inside and outside of Docker. -## My OCR used to work and now doesnt! -Please update your tesseract docker volume path version from 4.00 to 5 +## My OCR used to work and now doesn't! +The paths have changed for the tessadata locations on new docker images, please use ``/usr/share/tessdata`` (Others should still work for backwards compatibility but might not) ## How does the OCR Work Stirling-PDF uses [OCRmyPDF](https://github.com/ocrmypdf/OCRmyPDF) which in turn uses tesseract for its text recognition. diff --git a/LocalRunGuide.md b/LocalRunGuide.md index 3c9b2716..e0851d62 100644 --- a/LocalRunGuide.md +++ b/LocalRunGuide.md @@ -42,7 +42,7 @@ For Debian-based systems, you can use the following command: ```bash sudo apt-get update -sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ java-17-openjdk python3 python3-pip +sudo apt-get install -y git automake autoconf libtool libleptonica-dev pkg-config zlib1g-dev make g++ openjdk-17-jdk python3 python3-pip ``` For Fedora-based systems use this command: @@ -65,7 +65,7 @@ sudo make install ``` ### Step 3: Install Additional Software -Next we need to install LibreOffice for conversions, ocrmypdf for OCR, and opencv for patern recognition functionality. +Next we need to install LibreOffice for conversions, ocrmypdf for OCR, and opencv for pattern recognition functionality. Install the following software: @@ -95,7 +95,7 @@ For Debian-based systems, you can use the following command: ```bash sudo apt-get install -y libreoffice-writer libreoffice-calc libreoffice-impress unpaper ocrmypdf -pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint +pip3 install uno opencv-python-headless unoconv pngquant WeasyPrint --break-system-packages ``` For Fedora: @@ -264,7 +264,7 @@ sudo systemctl restart stirlingpdf.service Remember to set the necessary environment variables before running the project if you want to customize the application the list can be seen in the main readme. -You can do this in the terminal by using the `export` command or -D arguements to java -jar command: +You can do this in the terminal by using the `export` command or -D argument to java -jar command: ```bash export APP_HOME_NAME="Stirling PDF" diff --git a/PipelineFeature.md b/PipelineFeature.md index 8d51c3c5..7edbb089 100644 --- a/PipelineFeature.md +++ b/PipelineFeature.md @@ -1,13 +1,6 @@ # Pipeline Configuration and Usage Tutorial - -## Whilst Pipelines are in alpha... -You must enable this alpha functionality by setting -```yaml -system: - enableAlphaFunctionality: true -``` -To true like in the above for your `/config/settings.yml` file, after restarting Stirling-PDF you should see both UI and folder scanning enabled. - +- Configure the pipeline config file and input files to run files against it +- For reuse, download the config file and re-upload it when needed, or place it in /pipeline/defaultWebUIConfigs/ to auto-load in the web UI for all users ## Steps to Configure and Use Your Pipeline @@ -40,3 +33,12 @@ To true like in the above for your `/config/settings.yml` file, after restarting 10. **Note on Web UI Limitations** - The current web UI version does not support operations that require multiple different types of inputs, such as adding a separate image to a PDF. + + +### Current Limitations +- Cannot have more than one of the same operation +- Cannot input additional files via UI +- All files and operations run in serial mode + + + \ No newline at end of file diff --git a/README.md b/README.md index 5876a7e2..31b34009 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,35 @@ -


Stirling-PDF

-

+

+

Stirling-PDF

[![Docker Pulls](https://img.shields.io/docker/pulls/frooodle/s-pdf)](https://hub.docker.com/r/frooodle/s-pdf) [![Discord](https://img.shields.io/discord/1068636748814483718?label=Discord)](https://discord.gg/Cn8pWhQRxZ) [![Docker Image Version (tag latest semver)](https://img.shields.io/docker/v/frooodle/s-pdf/latest)](https://github.com/Stirling-Tools/Stirling-PDF/) [![GitHub Repo stars](https://img.shields.io/github/stars/stirling-tools/stirling-pdf?style=social)](https://github.com/Stirling-Tools/stirling-pdf) [![Paypal Donate](https://img.shields.io/badge/Paypal%20Donate-yellow?style=flat&logo=paypal)](https://www.paypal.com/paypalme/froodleplex) -[![Github Sponser](https://img.shields.io/badge/Github%20Sponsor-yellow?style=flat&logo=github)](https://github.com/sponsors/Frooodle) +[![Github Sponsor](https://img.shields.io/badge/Github%20Sponsor-yellow?style=flat&logo=github)](https://github.com/sponsors/Frooodle) [![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/Stirling-Tools/Stirling-PDF/tree/digitalOcean&refcode=c3210994b1af) -This is a powerful locally hosted web based PDF manipulation tool using docker that allows you to perform various operations on PDF files, such as splitting merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application started as a 100% ChatGPT-made application and has evolved to include a wide range of features to handle all your PDF needs. +This is a robust, locally hosted web-based PDF manipulation tool using Docker. It enables you to carry out various operations on PDF files, including splitting, merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application has evolved to encompass a comprehensive set of features, addressing all your PDF requirements. -Stirling PDF makes no outbound calls for any record keeping or tracking. +Stirling PDF does not initiate any outbound calls for record-keeping or tracking purposes. All files and PDFs exist either exclusively on the client side, reside in server memory only during task execution, or temporarily reside in a file solely for the execution of the task. Any file downloaded by the user will have been deleted from the server by that point. -![stirling-home](images/stirling-home.png) +![stirling-home](images/stirling-home.jpg) ## Features + - Dark mode support. - Custom download options (see [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/images/settings.png) for example) - Parallel file processing and downloads - API for integration with external scripts - Optional Login and Authentication support (see [here](https://github.com/Stirling-Tools/Stirling-PDF/tree/main#login-authentication) for documentation) - ## **PDF Features** ### **Page Operations** + - View and modify PDFs - View multi page PDFs with custom viewing sorting and searching. Plus on page edit features like annotate, draw and adding text and images. (Using PDF.js with Joxit and Liberation.Liberation fonts) - Full interactive GUI for merging/splitting/rotating/moving PDFs and their pages. - Merge multiple PDFs together into a single resultant file. @@ -45,6 +46,7 @@ All files and PDFs exist either exclusively on the client side, reside in server - Convert PDF to a single page. ### **Conversion Operations** + - Convert PDFs to and from images. - Convert any common file to PDF (using LibreOffice). - Convert PDF to Word/Powerpoint/Others (using LibreOffice). @@ -53,6 +55,7 @@ All files and PDFs exist either exclusively on the client side, reside in server - Markdown to PDF. ### **Security & Permissions** + - Add and remove passwords. - Change/set PDF Permissions. - Add watermark(s). @@ -61,6 +64,7 @@ All files and PDFs exist either exclusively on the client side, reside in server - Auto-redact text. ### **Other Operations** + - Add/Generate/Write signatures. - Repair PDFs. - Detect and remove blank pages. @@ -77,11 +81,11 @@ All files and PDFs exist either exclusively on the client side, reside in server - Flatten PDFs. - Get all information on a PDF to view or export as JSON. - For a overview of the tasks and the technology each uses please view [Endpoint-groups.md](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md) Demo of the app is available [here](https://stirlingpdf.io). username: demo, password: demo ## Technologies used + - Spring Boot + Thymeleaf - [PDFBox](https://github.com/apache/pdfbox/tree/trunk) - [LibreOffice](https://www.libreoffice.org/discover/libreoffice/) for advanced conversions @@ -94,19 +98,21 @@ Demo of the app is available [here](https://stirlingpdf.io). username: demo, pas ## How to use ### Locally + Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/LocalRunGuide.md ### Docker / Podman + https://hub.docker.com/r/frooodle/s-pdf -Stirling PDF has 3 different versions, a Full version, Lite, and ultra-Lite. Depending on the types of features you use you may want a smaller image to save on space. +Stirling PDF has 2 different versions, a Full version and ultra-Lite version. Depending on the types of features you use you may want a smaller image to save on space. To see what the different versions offer please look at our [version mapping](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Version-groups.md) For people that don't mind about space optimization just use the latest tag. ![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest?label=Stirling-PDF%20Full) -![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest-lite?label=Stirling-PDF%20Lite) ![Docker Image Size (tag)](https://img.shields.io/docker/image-size/frooodle/s-pdf/latest-ultra-lite?label=Stirling-PDF%20Ultra-Lite) Docker Run + ```bash docker run -d \ -p 8080:8080 \ @@ -114,6 +120,8 @@ docker run -d \ -v /location/of/extraConfigs:/configs \ -v /location/of/logs:/logs \ -e DOCKER_ENABLE_SECURITY=false \ + -e INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \ + -e LANGS=en_GB \ --name stirling-pdf \ frooodle/s-pdf:latest @@ -122,7 +130,9 @@ docker run -d \ -v /location/of/customFiles:/customFiles \ ``` + Docker Compose + ```yaml version: '3.3' services: @@ -137,59 +147,68 @@ services: # - /location/of/logs:/logs/ environment: - DOCKER_ENABLE_SECURITY=false + - INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false + - LANGS=en_GB ``` Note: Podman is CLI-compatible with Docker, so simply replace "docker" with "podman". ## Enable OCR/Compression feature + Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md ## Supported Languages -Stirling PDF currently supports 26! -- English (English) (en_GB) -- English (US) (en_US) -- Arabic (العربية) (ar_AR) -- German (Deutsch) (de_DE) -- French (Français) (fr_FR) -- Spanish (Español) (es_ES) -- Simplified Chinese (简体中文) (zh_CN) -- Traditional Chinese (繁體中文) (zh_TW) -- Catalan (Català) (ca_CA) -- Italian (Italiano) (it_IT) -- Swedish (Svenska) (sv_SE) -- Polish (Polski) (pl_PL) -- Romanian (Română) (ro_RO) -- Korean (한국어) (ko_KR) -- Portuguese Brazilian (Português) (pt_BR) -- Russian (Русский) (ru_RU) -- Basque (Euskara) (eu_ES) -- Japanese (日本語) (ja_JP) -- Dutch (Nederlands) (nl_NL) -- Greek (el_GR) -- Turkish (Türkçe) (tr_TR) -- Indonesia (Bahasa Indonesia) (id_ID) -- Hindi (हिंदी) (hi_IN) -- Hungarian (Magyar) (hu_HU) -- Bulgarian (Български) (bg_BG) -- Sebian Latin alphabet (Srpski) (sr-Latn-RS) +Stirling PDF currently supports 27! + +| Language | Progress | +| ------------------------------------------- | -------------------------------------- | +| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) | +| English (US) (en_US) | ![100%](https://geps.dev/progress/100) | +| Arabic (العربية) (ar_AR) | ![42%](https://geps.dev/progress/42) | +| German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) | +| French (Français) (fr_FR) | ![91%](https://geps.dev/progress/91) | +| Spanish (Español) (es_ES) | ![99%](https://geps.dev/progress/99) | +| Simplified Chinese (简体中文) (zh_CN) | ![98%](https://geps.dev/progress/98) | +| Traditional Chinese (繁體中文) (zh_TW) | ![98%](https://geps.dev/progress/98) | +| Catalan (Català) (ca_CA) | ![51%](https://geps.dev/progress/51) | +| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) | +| Swedish (Svenska) (sv_SE) | ![42%](https://geps.dev/progress/42) | +| Polish (Polski) (pl_PL) | ![44%](https://geps.dev/progress/44) | +| Romanian (Română) (ro_RO) | ![41%](https://geps.dev/progress/41) | +| Korean (한국어) (ko_KR) | ![91%](https://geps.dev/progress/91) | +| Portuguese Brazilian (Português) (pt_BR) | ![64%](https://geps.dev/progress/64) | +| Russian (Русский) (ru_RU) | ![90%](https://geps.dev/progress/90) | +| Basque (Euskara) (eu_ES) | ![66%](https://geps.dev/progress/66) | +| Japanese (日本語) (ja_JP) | ![91%](https://geps.dev/progress/91) | +| Dutch (Nederlands) (nl_NL) | ![88%](https://geps.dev/progress/88) | +| Greek (Ελληνικά) (el_GR) | ![89%](https://geps.dev/progress/89) | +| Turkish (Türkçe) (tr_TR) | ![99%](https://geps.dev/progress/99) | +| Indonesia (Bahasa Indonesia) (id_ID) | ![82%](https://geps.dev/progress/82) | +| Hindi (हिंदी) (hi_IN) | ![82%](https://geps.dev/progress/82) | +| Hungarian (Magyar) (hu_HU) | ![81%](https://geps.dev/progress/81) | +| Bulgarian (Български) (bg_BG) | ![75%](https://geps.dev/progress/75) | +| Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![84%](https://geps.dev/progress/84) | +| Ukrainian (Українська) (uk_UA) | ![90%](https://geps.dev/progress/90) | ## Contributing (creating issues, translations, fixing bugs, etc.) Please see our [Contributing Guide](CONTRIBUTING.md)! ## Customisation + Stirling PDF allows easy customization of the app. Includes things like -- Custom application name -- Custom slogans, icons, images, and even custom HTML (via file overrides) +- Custom application name +- Custom slogans, icons, HTML, images CSS etc (via file overrides) There are two options for this, either using the generated settings file ``settings.yml`` This file is located in the ``/configs`` directory and follows standard YAML formatting Environment variables are also supported and would override the settings file For example in the settings.yml you have + ```yaml system: defaultLocale: 'en-US' @@ -198,6 +217,7 @@ system: To have this via an environment variable you would have ``SYSTEM_DEFAULTLOCALE`` The Current list of settings is + ```yaml security: enableLogin: false # set to 'true' to enable login @@ -207,6 +227,9 @@ system: defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc) googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow customStaticFilePath: '/customFiles/static/' # Directory path for custom static files + showUpdate: true # see when a new update is available + showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true' + customHTMLFiles: false # enable to have files placed in /customFiles/templates override the existing template html files #ui: # appName: exampleAppName # Application's visible name @@ -220,25 +243,35 @@ endpoints: metrics: enabled: true # 'true' to enable Info APIs endpoints (view http://localhost:8080/swagger-ui/index.html#/API to learn more), 'false' to disable ``` + +There is an additional config file ``/configs/custom_settings.yml`` were users familiar with java and spring application.properties can input their own settings on-top of Stirling-PDFs existing ones + ### Extra notes + - Endpoints. Currently, the endpoints ENDPOINTS_TO_REMOVE and GROUPS_TO_REMOVE can include comma separate lists of endpoints and groups to disable as example ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages would disable both image-to-pdf and remove pages, GROUPS_TO_REMOVE=LibreOffice Would disable all things that use LibreOffice. You can see a list of all endpoints and groups [here](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/Endpoint-groups.md) - customStaticFilePath. Customise static files such as the app logo by placing files in the /customFiles/static/ directory. An example of customising app logo is placing a /customFiles/static/favicon.svg to override current SVG. This can be used to change any images/icons/css/fonts/js etc in Stirling-PDF ### Environment only parameters + - ``SYSTEM_ROOTURIPATH`` ie set to ``/pdf-app`` to Set the application's root URI to ``localhost:8080/pdf-app`` - ``SYSTEM_CONNECTIONTIMEOUTMINUTES`` to set custom connection timeout values - ``DOCKER_ENABLE_SECURITY`` to tell docker to download security jar (required as true for auth login) +- ``INSTALL_BOOK_AND_ADVANCED_HTML_OPS`` to download calibre onto stirling-pdf enabling pdf to/from book and advanced html conversion +- ``LANGS`` to define custom font libraries to install for use for document conversions ## API + For those wanting to use Stirling-PDFs backend API to link with their own custom scripting to edit PDFs you can view all existing API documentation [here](https://app.swaggerhub.com/apis-docs/Stirling-Tools/Stirling-PDF/) or navigate to /swagger-ui/index.html of your stirling-pdf instance for your versions documentation (Or by following the API button in your settings of Stirling-PDF) - ## Login authentication + ![stirling-login](images/login-light.png) + ### Prerequisites: + - User must have the folder ./configs volumed within docker so that it is retained during updates. -- Docker uses must download the security jar version by setting ``DOCKER_ENABLE_SECURITY`` to ``true`` in environment variables. +- Docker users must download the security jar version by setting ``DOCKER_ENABLE_SECURITY`` to ``true`` in environment variables. - Then either enable login via the settings.yml file or via setting ``SECURITY_ENABLE_LOGIN`` to ``true`` - Now the initial user will be generated with username ``admin`` and password ``stirling``. On login you will be forced to change the password to a new one. You can also use the environment variables ``SECURITY_INITIALLOGIN_USERNAME`` and ``SECURITY_INITIALLOGIN_PASSWORD`` to set your own straight away (Recommended to remove them after user creation). @@ -252,20 +285,22 @@ To add new users go to the bottom of Account settings and hit 'Admin Settings', For API usage you must provide a header with 'X-API-Key' and the associated API key for that user. - ## FAQ ### Q1: What are your planned features? + - Progress bar/Tracking - Full custom logic pipelines to combine multiple operations together. - Folder support with auto scanning to perform operations on - Redact text (Via UI not just automated way) - Add Forms - Multi page layout (Stich PDF pages together) support x rows y columns and custom page sizing -- Fill forms mannual and automatic +- Fill forms manually or automatically ### Q2: Why is my application downloading .htm files? + This is an issue caused commonly by your NGINX configuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. ``client_max_body_size SIZE;`` Where "SIZE" is 50M for example for 50MB files. ### Q3: Why is my download timing out + NGINX has timeout values by default so if you are running Stirling-PDF behind NGINX you may need to set a timeout value such as adding the config ``proxy_read_timeout 3600;`` diff --git a/Version-groups.md b/Version-groups.md index 5515e16c..8c37e22d 100644 --- a/Version-groups.md +++ b/Version-groups.md @@ -1,64 +1,52 @@ -|Technology | Ultra-Lite | Lite | Full | -|----------------|:----------:|:----:|:----:| -| Java | ✔️ | ✔️ | ✔️ | -| JavaScript | ✔️ | ✔️ | ✔️ | -| Libre | | ✔️ | ✔️ | -| Python | | | ✔️ | -| OpenCV | | | ✔️ | -| OCRmyPDF | | | ✔️ | +| Technology | Ultra-Lite | Full | +|----------------|:----------:|:----:| +| Java | ✔️ | ✔️ | +| JavaScript | ✔️ | ✔️ | +| Libre | | ✔️ | +| Python | | ✔️ | +| OpenCV | | ✔️ | +| OCRmyPDF | | ✔️ | - - - - -Operation | Ultra-Lite | Lite | Full ---------------------|------------|------|----- -add-page-numbers | ✔️ | ✔️ | ✔️ -add-password | ✔️ | ✔️ | ✔️ -add-image | ✔️ | ✔️ | ✔️ -add-watermark | ✔️ | ✔️ | ✔️ -adjust-contrast | ✔️ | ✔️ | ✔️ -auto-split-pdf | ✔️ | ✔️ | ✔️ -auto-redact | ✔️ | ✔️ | ✔️ -auto-rename | ✔️ | ✔️ | ✔️ -cert-sign | ✔️ | ✔️ | ✔️ -crop | ✔️ | ✔️ | ✔️ -change-metadata | ✔️ | ✔️ | ✔️ -change-permissions | ✔️ | ✔️ | ✔️ -compare | ✔️ | ✔️ | ✔️ -extract-page | ✔️ | ✔️ | ✔️ -extract-images | ✔️ | ✔️ | ✔️ -flatten | ✔️ | ✔️ | ✔️ -get-info-on-pdf | ✔️ | ✔️ | ✔️ -img-to-pdf | ✔️ | ✔️ | ✔️ -markdown-to-pdf | ✔️ | ✔️ | ✔️ -merge-pdfs | ✔️ | ✔️ | ✔️ -multi-page-layout | ✔️ | ✔️ | ✔️ -overlay-pdf | ✔️ | ✔️ | ✔️ -pdf-organizer | ✔️ | ✔️ | ✔️ -pdf-to-csv | ✔️ | ✔️ | ✔️ -pdf-to-img | ✔️ | ✔️ | ✔️ -pdf-to-single-page | ✔️ | ✔️ | ✔️ -remove-pages | ✔️ | ✔️ | ✔️ -remove-password | ✔️ | ✔️ | ✔️ -rotate-pdf | ✔️ | ✔️ | ✔️ -sanitize-pdf | ✔️ | ✔️ | ✔️ -scale-pages | ✔️ | ✔️ | ✔️ -sign | ✔️ | ✔️ | ✔️ -show-javascript | ✔️ | ✔️ | ✔️ -split-by-size-or-count | ✔️ | ✔️ | ✔️ -split-pdf-by-sections | ✔️ | ✔️ | ✔️ -split-pdfs | ✔️ | ✔️ | ✔️ -file-to-pdf | | ✔️ | ✔️ -pdf-to-html | | ✔️ | ✔️ -pdf-to-presentation | | ✔️ | ✔️ -pdf-to-text | | ✔️ | ✔️ -pdf-to-word | | ✔️ | ✔️ -pdf-to-xml | | ✔️ | ✔️ -repair | | ✔️ | ✔️ -xlsx-to-pdf | | ✔️ | ✔️ -compress-pdf | | | ✔️ -extract-image-scans | | | ✔️ -ocr-pdf | | | ✔️ -pdf-to-pdfa | | | ✔️ -remove-blanks | | | ✔️ +Operation | Ultra-Lite | Full +-------------------------|------------|----- +add-page-numbers | ✔️ | ✔️ +add-password | ✔️ | ✔️ +add-image | ✔️ | ✔️ +add-watermark | ✔️ | ✔️ +adjust-contrast | ✔️ | ✔️ +auto-split-pdf | ✔️ | ✔️ +auto-redact | ✔️ | ✔️ +auto-rename | ✔️ | ✔️ +cert-sign | ✔️ | ✔️ +crop | ✔️ | ✔️ +change-metadata | ✔️ | ✔️ +change-permissions | ✔️ | ✔️ +compare | ✔️ | ✔️ +extract-page | ✔️ | ✔️ +extract-images | ✔️ | ✔️ +flatten | ✔️ | ✔️ +get-info-on-pdf | ✔️ | ✔️ +img-to-pdf | ✔️ | ✔️ +markdown-to-pdf | ✔️ | ✔️ +merge-pdfs | ✔️ | ✔️ +multi-page-layout | ✔️ | ✔️ +overlay-pdf | ✔️ | ✔️ +pdf-organizer | ✔️ | ✔️ +pdf-to-csv | ✔️ | ✔️ +pdf-to-img | ✔️ | ✔️ +pdf-to-single-page | ✔️ | ✔️ +remove-pages | ✔️ | ✔️ +remove-password | ✔️ | ✔️ +rotate-pdf | ✔️ | ✔️ +sanitize-pdf | ✔️ | ✔️ +scale-pages | ✔️ | ✔️ +sign | ✔️ | ✔️ +show-javascript | ✔️ | ✔️ +split-by-size-or-count | ✔️ | ✔️ +split-pdf-by-sections | ✔️ | ✔️ +split-pdfs | ✔️ | ✔️ +compress-pdf | | ✔️ +extract-image-scans | | ✔️ +ocr-pdf | | ✔️ +pdf-to-pdfa | | ✔️ +remove-blanks | | ✔️ diff --git a/build.gradle b/build.gradle index 727b2b53..66817266 100644 --- a/build.gradle +++ b/build.gradle @@ -1,18 +1,18 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.2.2' + id 'org.springframework.boot' version '3.2.4' id 'io.spring.dependency-management' version '1.1.3' id 'org.springdoc.openapi-gradle-plugin' version '1.8.0' id "io.swagger.swaggerhub" version "1.3.2" id 'edu.sc.seis.launch4j' version '3.0.5' id 'com.diffplug.spotless' version '6.25.0' - id 'com.github.jk1.dependency-license-report' version '2.5' + id 'com.github.jk1.dependency-license-report' version '2.6' } import com.github.jk1.license.render.* group = 'stirling.software' -version = '0.21.0' +version = '0.23.2' sourceCompatibility = '17' repositories { @@ -20,7 +20,6 @@ repositories { } - licenseReport { renderers = [new JsonReportRenderer()] } @@ -48,7 +47,6 @@ openApi { outputFileName = "SwaggerDoc.json" } - launch4j { icon = "${projectDir}/src/main/resources/static/favicon.ico" @@ -87,26 +85,27 @@ spotless { dependencies { //security updates - implementation 'ch.qos.logback:logback-classic:1.4.14' - implementation 'ch.qos.logback:logback-core:1.4.14' - implementation 'org.springframework:spring-webmvc:6.1.2' + implementation 'ch.qos.logback:logback-classic:1.5.3' + implementation 'ch.qos.logback:logback-core:1.5.3' + implementation 'org.springframework:spring-webmvc:6.1.5' - implementation("io.github.pixee:java-security-toolkit:1.1.2") + implementation("io.github.pixee:java-security-toolkit:1.1.3") implementation 'org.yaml:snakeyaml:2.2' - implementation 'org.springframework.boot:spring-boot-starter-web:3.2.2' - implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.2' + implementation 'org.springframework.boot:spring-boot-starter-web:3.2.4' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.2.4' if (System.getenv('DOCKER_ENABLE_SECURITY') != 'false') { - implementation 'org.springframework.boot:spring-boot-starter-security:3.2.2' + implementation 'org.springframework.boot:spring-boot-starter-security:3.2.4' implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.2.RELEASE' - implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.2" + implementation "org.springframework.boot:spring-boot-starter-data-jpa:3.2.4" + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client:3.2.4' //2.2.x requires rebuild of DB file.. need migration path implementation "com.h2database:h2:2.1.214" } - testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.2' + testImplementation 'org.springframework.boot:spring-boot-starter-test:3.2.4' // Batik implementation 'org.apache.xmlgraphics:batik-all:1.17' @@ -139,28 +138,30 @@ dependencies { exclude group: 'commons-logging', module: 'commons-logging' } - implementation ('org.apache.pdfbox:pdfbox:3.0.1'){ + implementation ('org.apache.pdfbox:pdfbox:3.0.2'){ exclude group: 'commons-logging', module: 'commons-logging' } - implementation ('org.apache.pdfbox:xmpbox:3.0.1'){ + implementation ('org.apache.pdfbox:xmpbox:3.0.2'){ exclude group: 'commons-logging', module: 'commons-logging' } implementation 'org.bouncycastle:bcprov-jdk18on:1.77' implementation 'org.bouncycastle:bcpkix-jdk18on:1.77' - implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.2' - implementation 'io.micrometer:micrometer-core:1.12.2' - implementation group: 'com.google.zxing', name: 'core', version: '3.5.2' + implementation 'org.springframework.boot:spring-boot-starter-actuator:3.2.4' + implementation 'io.micrometer:micrometer-core:1.12.4' + implementation group: 'com.google.zxing', name: 'core', version: '3.5.3' // https://mvnrepository.com/artifact/org.commonmark/commonmark - implementation 'org.commonmark:commonmark:0.21.0' - implementation 'org.commonmark:commonmark-ext-gfm-tables:0.21.0' + implementation 'org.commonmark:commonmark:0.22.0' + implementation 'org.commonmark:commonmark-ext-gfm-tables:0.22.0' // https://mvnrepository.com/artifact/com.github.vladimir-bukhtoyarov/bucket4j-core implementation 'com.github.vladimir-bukhtoyarov:bucket4j-core:7.6.0' - developmentOnly("org.springframework.boot:spring-boot-devtools:3.2.2") - compileOnly 'org.projectlombok:lombok:1.18.30' - annotationProcessor 'org.projectlombok:lombok:1.18.28' + implementation 'com.fathzer:javaluator:3.0.3' + + developmentOnly("org.springframework.boot:spring-boot-devtools:3.2.4") + compileOnly 'org.projectlombok:lombok:1.18.32' + annotationProcessor 'org.projectlombok:lombok:1.18.32' } tasks.withType(JavaCompile) { diff --git a/chart/stirling-pdf/Chart.yaml b/chart/stirling-pdf/Chart.yaml index 686d2422..52220bcd 100644 --- a/chart/stirling-pdf/Chart.yaml +++ b/chart/stirling-pdf/Chart.yaml @@ -1,6 +1,7 @@ apiVersion: v2 -appVersion: 0.14.2 -description: locally hosted web application that allows you to perform various operations on PDF files +appVersion: 0.23.2 +description: locally hosted web application that allows you to perform various operations + on PDF files home: https://github.com/Stirling-Tools/Stirling-PDF keywords: - stirling-pdf diff --git a/chart/stirling-pdf/values.yaml b/chart/stirling-pdf/values.yaml index 3c4ca888..e9e00c3e 100644 --- a/chart/stirling-pdf/values.yaml +++ b/chart/stirling-pdf/values.yaml @@ -16,11 +16,11 @@ commonLabels: {} # team_name: dev envs: [] -# - name: PP_HOME_NAME +# - name: UI_APP_NAME # value: "Stirling PDF" -# - name: APP_HOME_DESCRIPTION +# - name: UI_HOME_DESCRIPTION # value: "Your locally hosted one-stop-shop for all your PDF needs." -# - name: APP_NAVBAR_NAME +# - name: UI_APP_NAVBAR_NAME # value: "Stirling PDF" # - name: ALLOW_GOOGLE_VISIBILITY # value: "true" diff --git a/exampleYmlFiles/docker-compose-latest-lite-security.yml b/exampleYmlFiles/docker-compose-latest-lite-security.yml deleted file mode 100644 index 90e4c5bf..00000000 --- a/exampleYmlFiles/docker-compose-latest-lite-security.yml +++ /dev/null @@ -1,31 +0,0 @@ -version: '3.3' -services: - stirling-pdf: - container_name: Stirling-PDF-Lite-Security - image: frooodle/s-pdf:latest-lite - deploy: - resources: - limits: - memory: 2G - healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -q 'Please sign in'"] - interval: 5s - timeout: 10s - retries: 16 - ports: - - 8080:8080 - volumes: - - /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw - - /stirling/latest/config:/configs:rw - - /stirling/latest/logs:/logs:rw - environment: - DOCKER_ENABLE_SECURITY: "true" - SECURITY_ENABLELOGIN: "true" - SYSTEM_DEFAULTLOCALE: en-US - UI_APPNAME: Stirling-PDF-Lite - UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest with Security - UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest - SYSTEM_MAXFILESIZE: "100" - METRICS_ENABLED: "true" - SYSTEM_GOOGLEVISIBILITY: "true" - restart: on-failure:5 diff --git a/exampleYmlFiles/docker-compose-latest-lite.yml b/exampleYmlFiles/docker-compose-latest-lite.yml deleted file mode 100644 index e40f873d..00000000 --- a/exampleYmlFiles/docker-compose-latest-lite.yml +++ /dev/null @@ -1,30 +0,0 @@ -version: '3.3' -services: - stirling-pdf: - container_name: Stirling-PDF-Lite - image: frooodle/s-pdf:latest-lite - deploy: - resources: - limits: - memory: 2G - healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -qv 'Please sign in'"] - interval: 5s - timeout: 10s - retries: 16 - ports: - - 8080:8080 - volumes: - - /stirling/latest/config:/configs:rw - - /stirling/latest/logs:/logs:rw - environment: - DOCKER_ENABLE_SECURITY: "false" - SECURITY_ENABLELOGIN: "false" - SYSTEM_DEFAULTLOCALE: en-US - UI_APPNAME: Stirling-PDF-Lite - UI_HOMEDESCRIPTION: Demo site for Stirling-PDF-Lite Latest - UI_APPNAMENAVBAR: Stirling-PDF-Lite Latest - SYSTEM_MAXFILESIZE: "100" - METRICS_ENABLED: "true" - SYSTEM_GOOGLEVISIBILITY: "true" - restart: on-failure:5 diff --git a/exampleYmlFiles/docker-compose-latest-security-with-sso.yml b/exampleYmlFiles/docker-compose-latest-security-with-sso.yml new file mode 100644 index 00000000..41241b15 --- /dev/null +++ b/exampleYmlFiles/docker-compose-latest-security-with-sso.yml @@ -0,0 +1,39 @@ +version: '3.3' +services: + stirling-pdf: + container_name: Stirling-PDF-Security + image: frooodle/s-pdf:latest + deploy: + resources: + limits: + memory: 4G + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:8080/api/v1/info/status | grep -q 'UP' && curl -fL http://localhost:8080/ | grep -q 'Please sign in'"] + interval: 5s + timeout: 10s + retries: 16 + ports: + - 8080:8080 + volumes: + - /stirling/latest/data:/usr/share/tessdata:rw + - /stirling/latest/config:/configs:rw + - /stirling/latest/logs:/logs:rw + environment: + DOCKER_ENABLE_SECURITY: "true" + SECURITY_ENABLELOGIN: "true" + SECURITY_OAUTH2_ENABLED: "true" + SECURITY_OAUTH2_AUTOCREATEUSER: "true" # This is set to true to allow auto-creation of non-existing users in Striling-PDF + SECURITY_OAUTH2_ISSUER: "https://accounts.google.com" # Change with any other provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point + SECURITY_OAUTH2_CLIENTID: ".apps.googleusercontent.com" # Client ID from your provider + SECURITY_OAUTH2_CLIENTSECRET: "" # Client Secret from your provider + PUID: 1002 + PGID: 1002 + UMASK: "022" + SYSTEM_DEFAULTLOCALE: en-US + UI_APPNAME: Stirling-PDF + UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security + UI_APPNAMENAVBAR: Stirling-PDF Latest + SYSTEM_MAXFILESIZE: "100" + METRICS_ENABLED: "true" + SYSTEM_GOOGLEVISIBILITY: "true" + restart: on-failure:5 diff --git a/exampleYmlFiles/docker-compose-latest-security.yml b/exampleYmlFiles/docker-compose-latest-security.yml index 513bb582..82014b8e 100644 --- a/exampleYmlFiles/docker-compose-latest-security.yml +++ b/exampleYmlFiles/docker-compose-latest-security.yml @@ -15,12 +15,15 @@ services: ports: - 8080:8080 volumes: - - /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw + - /stirling/latest/data:/usr/share/tessdata:rw - /stirling/latest/config:/configs:rw - /stirling/latest/logs:/logs:rw environment: DOCKER_ENABLE_SECURITY: "true" SECURITY_ENABLELOGIN: "true" + PUID: 1002 + PGID: 1002 + UMASK: "022" SYSTEM_DEFAULTLOCALE: en-US UI_APPNAME: Stirling-PDF UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest with Security diff --git a/exampleYmlFiles/docker-compose-latest-ultra-lite-security.yml b/exampleYmlFiles/docker-compose-latest-ultra-lite-security.yml index b7848696..c0dd09ce 100644 --- a/exampleYmlFiles/docker-compose-latest-ultra-lite-security.yml +++ b/exampleYmlFiles/docker-compose-latest-ultra-lite-security.yml @@ -15,7 +15,7 @@ services: ports: - 8080:8080 volumes: - - /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw + - /stirling/latest/data:/usr/share/tessdata:rw - /stirling/latest/config:/configs:rw - /stirling/latest/logs:/logs:rw environment: diff --git a/exampleYmlFiles/docker-compose-latest.yml b/exampleYmlFiles/docker-compose-latest.yml index d506e424..c2593332 100644 --- a/exampleYmlFiles/docker-compose-latest.yml +++ b/exampleYmlFiles/docker-compose-latest.yml @@ -15,12 +15,14 @@ services: ports: - 8080:8080 volumes: - - /stirling/latest/data:/usr/share/tesseract-ocr/5/tessdata:rw + - /stirling/latest/data:/usr/share/tessdata:rw - /stirling/latest/config:/configs:rw - /stirling/latest/logs:/logs:rw environment: DOCKER_ENABLE_SECURITY: "false" SECURITY_ENABLELOGIN: "false" + LANGS: "en_GB,en_US,ar_AR,de_DE,fr_FR,es_ES,zh_CN,zh_TW,ca_CA,it_IT,sv_SE,pl_PL,ro_RO,ko_KR,pt_BR,ru_RU,el_GR,hi_IN,hu_HU,tr_TR,id_ID" + INSTALL_BOOK_AND_ADVANCED_HTML_OPS: "true" SYSTEM_DEFAULTLOCALE: en-US UI_APPNAME: Stirling-PDF UI_HOMEDESCRIPTION: Demo site for Stirling-PDF Latest diff --git a/images/stirling-home.jpg b/images/stirling-home.jpg new file mode 100644 index 00000000..0b28f6ee Binary files /dev/null and b/images/stirling-home.jpg differ diff --git a/images/stirling-home.png b/images/stirling-home.png deleted file mode 100644 index c01af6f9..00000000 Binary files a/images/stirling-home.png and /dev/null differ diff --git a/pipeline/defaultWebUIConfigs/split-rotate-auto-rename.json b/pipeline/defaultWebUIConfigs/split-rotate-auto-rename.json index 3a989296..71a08e98 100644 --- a/pipeline/defaultWebUIConfigs/split-rotate-auto-rename.json +++ b/pipeline/defaultWebUIConfigs/split-rotate-auto-rename.json @@ -6,7 +6,8 @@ "parameters": { "horizontalDivisions": 2, "verticalDivisions": 2, - "fileInput": "automated" + "fileInput": "automated", + "merge": false } }, { @@ -30,4 +31,4 @@ }, "outputDir": "{outputFolder}", "outputFileName": "{filename}" -} \ No newline at end of file +} diff --git a/scripts/counter_translation.py b/scripts/counter_translation.py new file mode 100644 index 00000000..a4b6255b --- /dev/null +++ b/scripts/counter_translation.py @@ -0,0 +1,192 @@ +"""A script to update language progress status in README.md based on +properties file comparison. + +This script compares default properties file with others in a directory to +determine language progress. +It then updates README.md based on provided progress list. + +Author: Ludy87 + +Example: + To use this script, simply run it from command line: + $ python counter_translation.py +""" # noqa: D205 + +import glob +import os +import re + +import tomlkit +import tomlkit.toml_file + + +def convert_to_multiline(data: tomlkit.TOMLDocument) -> tomlkit.TOMLDocument: + """Converts 'ignore' and 'missing' arrays to multiline arrays and sorts the first-level keys of the TOML document. + Enhances readability and consistency in the TOML file by ensuring arrays contain unique and sorted entries. + + Parameters: + data (tomlkit.TOMLDocument): The original TOML document containing the data. + + Returns: + tomlkit.TOMLDocument: A new TOML document with sorted keys and properly formatted arrays. + """ # noqa: D205 + sorted_data = tomlkit.document() + for key in sorted(data.keys()): + value = data[key] + if isinstance(value, dict): + new_table = tomlkit.table() + for subkey in ("ignore", "missing"): + if subkey in value: + # Convert the list to a set to remove duplicates, sort it, and convert to multiline for readability + unique_sorted_array = sorted(set(value[subkey])) + array = tomlkit.array() + array.multiline(True) + for item in unique_sorted_array: + array.append(item) + new_table[subkey] = array + sorted_data[key] = new_table + else: + # Add other types of data unchanged + sorted_data[key] = value + return sorted_data + + +def write_readme(progress_list: list[tuple[str, int]]) -> None: + """Updates the progress status in the README.md file based + on the provided progress list. + + Parameters: + progress_list (list[tuple[str, int]]): A list of tuples containing + language and progress percentage. + + Returns: + None + """ # noqa: D205 + with open("README.md", encoding="utf-8") as file: + content = file.readlines() + + for i, line in enumerate(content[2:], start=2): + for progress in progress_list: + language, value = progress + if language in line: + if match := re.search(r"\!\[(\d+(\.\d+)?)%\]\(.*\)", line): + content[i] = line.replace( + match.group(0), + f"![{value}%](https://geps.dev/progress/{value})", + ) + + with open("README.md", "w", encoding="utf-8") as file: + file.writelines(content) + + +def compare_files(default_file_path, file_paths, translation_status_file) -> list[tuple[str, int]]: + """Compares the default properties file with other + properties files in the directory. + + Parameters: + default_file_path (str): The path to the default properties file. + files_directory (str): The directory containing other properties files. + + Returns: + list[tuple[str, int]]: A list of tuples containing + language and progress percentage. + """ # noqa: D205 + num_lines = sum( + 1 for line in open(default_file_path, encoding="utf-8") if line.strip() and not line.strip().startswith("#") + ) + + result_list = [] + sort_translation_status: tomlkit.TOMLDocument + + # read toml + with open(translation_status_file, encoding="utf-8") as f: + sort_translation_status = tomlkit.parse(f.read()) + + for file_path in file_paths: + language = os.path.basename(file_path).split("messages_", 1)[1].split(".properties", 1)[0] + + fails = 0 + if "en_GB" in language or "en_US" in language: + result_list.append(("en_GB", 100)) + result_list.append(("en_US", 100)) + continue + + if language not in sort_translation_status: + sort_translation_status[language] = tomlkit.table() + + if ( + "ignore" not in sort_translation_status[language] + or len(sort_translation_status[language].get("ignore", [])) < 1 + ): + sort_translation_status[language]["ignore"] = tomlkit.array(["language.direction"]) + + # if "missing" not in sort_translation_status[language]: + # sort_translation_status[language]["missing"] = tomlkit.array() + # elif "language.direction" in sort_translation_status[language]["missing"]: + # sort_translation_status[language]["missing"].remove("language.direction") + + with open(default_file_path, encoding="utf-8") as default_file, open(file_path, encoding="utf-8") as file: + for _ in range(5): + next(default_file) + try: + next(file) + except StopIteration: + fails = num_lines + + for line_num, (line_default, line_file) in enumerate(zip(default_file, file), start=6): + try: + # Ignoring empty lines and lines start with # + if line_default.strip() == "" or line_default.startswith("#"): + continue + + default_key, default_value = line_default.split("=", 1) + file_key, file_value = line_file.split("=", 1) + if ( + default_value.strip() == file_value.strip() + and default_key.strip() not in sort_translation_status[language]["ignore"] + ): + print(f"{language}: Line {line_num} is missing the translation.") + # if default_key.strip() not in sort_translation_status[language]["missing"]: + # missing_array = tomlkit.array() + # missing_array.append(default_key.strip()) + # missing_array.multiline(True) + # sort_translation_status[language]["missing"].extend(missing_array) + fails += 1 + # elif default_key.strip() in sort_translation_status[language]["ignore"]: + # if default_key.strip() in sort_translation_status[language]["missing"]: + # sort_translation_status[language]["missing"].remove(default_key.strip()) + if default_value.strip() != file_value.strip(): + # if default_key.strip() in sort_translation_status[language]["missing"]: + # sort_translation_status[language]["missing"].remove(default_key.strip()) + if default_key.strip() in sort_translation_status[language]["ignore"]: + sort_translation_status[language]["ignore"].remove(default_key.strip()) + + except IndexError: + pass + + print(f"{language}: {fails} out of {num_lines} lines are not translated.") + result_list.append( + ( + language, + int((num_lines - fails) * 100 / num_lines), + ) + ) + translation_status = convert_to_multiline(sort_translation_status) + with open(translation_status_file, "w", encoding="utf-8") as file: + file.write(tomlkit.dumps(translation_status)) + + unique_data = list(set(result_list)) + unique_data.sort(key=lambda x: x[1], reverse=True) + + return unique_data + + +if __name__ == "__main__": + directory = os.path.join(os.getcwd(), "src", "main", "resources") + messages_file_paths = glob.glob(os.path.join(directory, "messages_*.properties")) + reference_file = os.path.join(directory, "messages_en_GB.properties") + + scripts_directory = os.path.join(os.getcwd(), "scripts") + translation_state_file = os.path.join(scripts_directory, "translation_status.toml") + + write_readme(compare_files(reference_file, messages_file_paths, translation_state_file)) diff --git a/scripts/detect-blank-pages.py b/scripts/detect-blank-pages.py deleted file mode 100644 index 4ca724c2..00000000 --- a/scripts/detect-blank-pages.py +++ /dev/null @@ -1,37 +0,0 @@ -import cv2 -import sys -import argparse -import numpy as np - -def is_blank_image(image_path, threshold=10, white_percent=99, white_value=255, blur_size=5): - image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) - - if image is None: - print(f"Error: Unable to read the image file: {image_path}") - return False - - # Apply Gaussian blur to reduce noise - blurred_image = cv2.GaussianBlur(image, (blur_size, blur_size), 0) - - _, thresholded_image = cv2.threshold(blurred_image, white_value - threshold, white_value, cv2.THRESH_BINARY) - - # Calculate the percentage of white pixels in the thresholded image - white_pixels = np.sum(thresholded_image == white_value) - white_pixel_percentage = (white_pixels / thresholded_image.size) * 100 - - print(f"Page has white pixel percent of {white_pixel_percentage}") - return white_pixel_percentage >= white_percent - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Detect if an image is considered blank or not.') - parser.add_argument('image_path', help='The path to the image file.') - parser.add_argument('-t', '--threshold', type=int, default=10, help='Threshold for determining white pixels. The default value is 10.') - parser.add_argument('-w', '--white_percent', type=float, default=99, help='The percentage of white pixels for an image to be considered blank. The default value is 99.') - args = parser.parse_args() - - blank = is_blank_image(args.image_path, args.threshold, args.white_percent) - - # Return code 1: The image is considered blank. - # Return code 0: The image is not considered blank. - sys.exit(int(blank)) diff --git a/scripts/download-security-jar.sh b/scripts/download-security-jar.sh index e786ce6b..42ca2b5a 100644 --- a/scripts/download-security-jar.sh +++ b/scripts/download-security-jar.sh @@ -14,6 +14,8 @@ if [ "$DOCKER_ENABLE_SECURITY" = "true" ] && [ "$VERSION_TAG" != "alpha" ]; then if [ $? -eq 0 ]; then # checks if curl was successful rm -f app.jar ln -s app-security.jar app.jar + chown stirlingpdfuser:stirlingpdfgroup app.jar || true + chmod 755 app.jar || true fi fi fi diff --git a/scripts/init-without-ocr.sh b/scripts/init-without-ocr.sh index 2aced6a4..761dd08d 100644 --- a/scripts/init-without-ocr.sh +++ b/scripts/init-without-ocr.sh @@ -1,6 +1,34 @@ -#!/bin/sh +#!/bin/bash + +# Update the user and group IDs as per environment variables +if [ ! -z "$PUID" ] && [ "$PUID" != "$(id -u stirlingpdfuser)" ]; then + usermod -o -u "$PUID" stirlingpdfuser || true +fi + + +if [ ! -z "$PGID" ] && [ "$PGID" != "$(getent group stirlingpdfgroup | cut -d: -f3)" ]; then + groupmod -o -g "$PGID" stirlingpdfgroup || true +fi +umask "$UMASK" || true + +if [[ "$INSTALL_BOOK_AND_ADVANCED_HTML_OPS" == "true" ]]; then + apk add --no-cache calibre@testing +fi /scripts/download-security-jar.sh -# Run the main command -exec "$@" \ No newline at end of file +if [[ -n "$LANGS" ]]; then + /scripts/installFonts.sh $LANGS +fi + +echo "Setting permissions and ownership for necessary directories..." +# Attempt to change ownership of directories and files +if chown -R stirlingpdfuser:stirlingpdfgroup $HOME /logs /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline /app.jar; then + chmod -R 755 /logs /scripts /usr/share/fonts/opentype/noto /configs /customFiles /pipeline /app.jar || true + # If chown succeeds, execute the command as stirlingpdfuser + exec su-exec stirlingpdfuser "$@" +else + # If chown fails, execute the command without changing the user context + echo "[WARN] Chown failed, running as host user" + exec "$@" +fi diff --git a/scripts/init.sh b/scripts/init.sh index ea8fd359..b0e2a095 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -17,14 +17,15 @@ fi if [[ -n "$TESSERACT_LANGS" ]]; then # Convert comma-separated values to a space-separated list LANGS=$(echo $TESSERACT_LANGS | tr ',' ' ') - + pattern='^[a-zA-Z]{2,4}(_[a-zA-Z]{2,4})?$' # Install each language pack for LANG in $LANGS; do - apt-get install -y "tesseract-ocr-$LANG" + if [[ $LANG =~ $pattern ]]; then + apk add --no-cache "tesseract-ocr-data-$LANG" + else + echo "Skipping invalid language code" + fi done fi -/scripts/download-security-jar.sh - -# Run the main command -exec "$@" +/scripts/init-without-ocr.sh "$@" \ No newline at end of file diff --git a/scripts/installFonts.sh b/scripts/installFonts.sh new file mode 100644 index 00000000..d7eb7af6 --- /dev/null +++ b/scripts/installFonts.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +LANGS=$1 + +# Function to install a font package +install_font() { + echo "Installing font package: $1" + if ! apk add "$1" --no-cache; then + echo "Failed to install $1" + fi +} + +# Install common fonts used across many languages +#common_fonts=( +# font-terminus +# font-dejavu +# font-noto +# font-noto-cjk +# font-awesome +# font-noto-extra +#) +# +#for font in "${common_fonts[@]}"; do +# install_font $font +#done + +# Map languages to specific font packages +declare -A language_fonts=( + ["ar_AR"]="font-noto-arabic" + ["zh_CN"]="font-isas-misc" + ["zh_TW"]="font-isas-misc" + ["ja_JP"]="font-noto font-noto-thai font-noto-tibetan font-ipa font-sony-misc font-jis-misc" + ["ru_RU"]="font-vollkorn font-misc-cyrillic font-mutt-misc font-screen-cyrillic font-winitzki-cyrillic font-cronyx-cyrillic" + ["sr_LATN_RS"]="font-vollkorn font-misc-cyrillic font-mutt-misc font-screen-cyrillic font-winitzki-cyrillic font-cronyx-cyrillic" + ["uk_UA"]="font-vollkorn font-misc-cyrillic font-mutt-misc font-screen-cyrillic font-winitzki-cyrillic font-cronyx-cyrillic" + ["ko_KR"]="font-noto font-noto-thai font-noto-tibetan" + ["el_GR"]="font-noto" + ["hi_IN"]="font-noto-devanagari" + ["bg_BG"]="font-vollkorn font-misc-cyrillic" + ["GENERAL"]="font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra" +) + +# Install fonts for other languages which generally do not need special packages beyond 'font-noto' +other_langs=("en_GB" "en_US" "de_DE" "fr_FR" "es_ES" "ca_CA" "it_IT" "pt_BR" "nl_NL" "sv_SE" "pl_PL" "ro_RO" "hu_HU" "tr_TR" "id_ID" "eu_ES") +if [[ $LANGS == "ALL" ]]; then + # Install all fonts from the language_fonts map + for fonts in "${language_fonts[@]}"; do + for font in $fonts; do + install_font $font + done + done +else + # Split comma-separated languages and install necessary fonts + IFS=',' read -ra LANG_CODES <<< "$LANGS" + for code in "${LANG_CODES[@]}"; do + if [[ " ${other_langs[@]} " =~ " ${code} " ]]; then + install_font font-noto + else + fonts_to_install=${language_fonts[$code]} + if [ ! -z "$fonts_to_install" ]; then + for font in $fonts_to_install; do + install_font $font + done + fi + fi + done +fi diff --git a/scripts/translation_status.toml b/scripts/translation_status.toml new file mode 100644 index 00000000..335ef567 --- /dev/null +++ b/scripts/translation_status.toml @@ -0,0 +1,154 @@ +[ar_AR] +ignore = [ + 'language.direction', +] + +[bg_BG] +ignore = [ + 'language.direction', +] + +[ca_CA] +ignore = [ + 'language.direction', +] + +[de_DE] +ignore = [ + 'AddStampRequest.alphabet', + 'AddStampRequest.position', + 'PDFToBook.selectText.1', + 'PDFToText.tags', + 'addPageNumbers.selectText.3', + 'alphabet', + 'certSign.name', + 'language.direction', + 'licenses.version', + 'pipeline.title', + 'pipelineOptions.pipelineHeader', + 'sponsor', + 'text', + 'watermark.type.1', +] + +[el_GR] +ignore = [ + 'language.direction', +] + +[es_ES] +ignore = [ + 'adminUserSettings.roles', + 'color', + 'language.direction', + 'no', + 'showJS.tags', +] + +[eu_ES] +ignore = [ + 'language.direction', +] + +[fr_FR] +ignore = [ + 'language.direction', +] + +[hi_IN] +ignore = [ + 'language.direction', +] + +[hu_HU] +ignore = [ + 'language.direction', +] + +[id_ID] +ignore = [ + 'language.direction', +] + +[it_IT] +ignore = [ + 'font', + 'language.direction', + 'no', + 'password', + 'pipeline.title', + 'pipelineOptions.pipelineHeader', + 'removePassword.selectText.2', + 'showJS.tags', + 'sponsor', +] + +[ja_JP] +ignore = [ + 'language.direction', +] + +[ko_KR] +ignore = [ + 'language.direction', +] + +[nl_NL] +ignore = [ + 'language.direction', +] + +[pl_PL] +ignore = [ + 'language.direction', +] + +[pt_BR] +ignore = [ + 'language.direction', +] + +[pt_PT] +ignore = [ + 'language.direction', +] + +[ro_RO] +ignore = [ + 'language.direction', +] + +[ru_RU] +ignore = [ + 'language.direction', +] + +[sr_LATN_RS] +ignore = [ + 'language.direction', +] + +[sv_SE] +ignore = [ + 'language.direction', +] + +[tr_TR] +ignore = [ + 'language.direction', +] + +[uk_UA] +ignore = [ + 'language.direction', +] + +[zh_CN] +ignore = [ + 'language.direction', +] + +[zh_TW] +ignore = [ + 'language.direction', +] diff --git a/src/main/java/stirling/software/SPDF/SPdfApplication.java b/src/main/java/stirling/software/SPDF/SPdfApplication.java index 62cbfe10..fb682998 100644 --- a/src/main/java/stirling/software/SPDF/SPdfApplication.java +++ b/src/main/java/stirling/software/SPDF/SPdfApplication.java @@ -1,10 +1,17 @@ package stirling.software.SPDF; +import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.core.env.Environment; @@ -14,14 +21,25 @@ import io.github.pixee.security.SystemCommand; import jakarta.annotation.PostConstruct; import stirling.software.SPDF.config.ConfigInitializer; -import stirling.software.SPDF.utils.GeneralUtils; +import stirling.software.SPDF.model.ApplicationProperties; @SpringBootApplication @EnableScheduling public class SPdfApplication { + private static final Logger logger = LoggerFactory.getLogger(SPdfApplication.class); + @Autowired private Environment env; + @Autowired ApplicationProperties applicationProperties; + + private static String serverPortStatic; + + @Value("${server.port:8080}") + public void setServerPortStatic(String port) { + SPdfApplication.serverPortStatic = port; + } + @PostConstruct public void init() { // Check if the BROWSER_OPEN environment variable is set to true @@ -30,7 +48,7 @@ public class SPdfApplication { if (browserOpen) { try { - String url = "http://localhost:" + getPort(); + String url = "http://localhost:" + getNonStaticPort(); String os = System.getProperty("os.name").toLowerCase(); Runtime rt = Runtime.getRuntime(); @@ -39,45 +57,75 @@ public class SPdfApplication { SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url); } } catch (Exception e) { - e.printStackTrace(); + logger.error("Error opening browser: {}", e.getMessage()); } } + logger.info("Running configs {}", applicationProperties.toString()); } - public static void main(String[] args) { + public static void main(String[] args) throws IOException, InterruptedException { + SpringApplication app = new SpringApplication(SPdfApplication.class); app.addInitializers(new ConfigInitializer()); + Map propertyFiles = new HashMap<>(); + + // stirling pdf settings file if (Files.exists(Paths.get("configs/settings.yml"))) { - app.setDefaultProperties( - Collections.singletonMap( - "spring.config.additional-location", "file:configs/settings.yml")); + propertyFiles.put("spring.config.additional-location", "file:configs/settings.yml"); } else { - System.out.println( + logger.warn( "External configuration file 'configs/settings.yml' does not exist. Using default configuration and environment configuration instead."); } + + // custom javs settings file + if (Files.exists(Paths.get("configs/custom_settings.yml"))) { + String existing = propertyFiles.getOrDefault("spring.config.additional-location", ""); + if (!existing.isEmpty()) { + existing += ","; + } + propertyFiles.put( + "spring.config.additional-location", + existing + "file:configs/custom_settings.yml"); + } else { + logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist."); + } + + if (!propertyFiles.isEmpty()) { + app.setDefaultProperties( + Collections.singletonMap( + "spring.config.additional-location", + propertyFiles.get("spring.config.additional-location"))); + } + app.run(args); try { Thread.sleep(1000); } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + Thread.currentThread().interrupt(); + throw new RuntimeException("Thread interrupted while sleeping", e); } - GeneralUtils.createDir("customFiles/static/"); - GeneralUtils.createDir("customFiles/templates/"); - - System.out.println("Stirling-PDF Started."); - - String url = "http://localhost:" + getPort(); - System.out.println("Navigate to " + url); + try { + Files.createDirectories(Path.of("customFiles/static/")); + Files.createDirectories(Path.of("customFiles/templates/")); + } catch (Exception e) { + logger.error("Error creating directories: {}", e.getMessage()); + } + printStartupLogs(); } - public static String getPort() { - String port = System.getProperty("local.server.port"); - if (port == null || port.isEmpty()) { - port = "8080"; - } - return port; + private static void printStartupLogs() { + logger.info("Stirling-PDF Started."); + String url = "http://localhost:" + getStaticPort(); + logger.info("Navigate to {}", url); + } + + public static String getStaticPort() { + return serverPortStatic; + } + + public String getNonStaticPort() { + return serverPortStatic; } } diff --git a/src/main/java/stirling/software/SPDF/config/AppConfig.java b/src/main/java/stirling/software/SPDF/config/AppConfig.java index 65e98b7f..16618e1e 100644 --- a/src/main/java/stirling/software/SPDF/config/AppConfig.java +++ b/src/main/java/stirling/software/SPDF/config/AppConfig.java @@ -6,18 +6,35 @@ import java.nio.file.Paths; import java.util.Properties; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.thymeleaf.spring6.SpringTemplateEngine; import stirling.software.SPDF.model.ApplicationProperties; @Configuration +@Lazy public class AppConfig { @Autowired ApplicationProperties applicationProperties; + @Bean + @ConditionalOnProperty( + name = "system.customHTMLFiles", + havingValue = "true", + matchIfMissing = false) + public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) { + SpringTemplateEngine templateEngine = new SpringTemplateEngine(); + templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader)); + return templateEngine; + } + @Bean(name = "loginEnabled") public boolean loginEnabled() { return applicationProperties.getSecurity().getEnableLogin(); @@ -79,9 +96,16 @@ public class AppConfig { @Bean(name = "bookAndHtmlFormatsInstalled") public boolean bookAndHtmlFormatsInstalled() { - return applicationProperties - .getSystem() - .getCustomApplications() - .isInstallBookAndHtmlFormats(); + String installOps = System.getProperty("INSTALL_BOOK_AND_ADVANCED_HTML_OPS"); + if (installOps == null) { + installOps = System.getenv("INSTALL_BOOK_AND_ADVANCED_HTML_OPS"); + } + return "true".equalsIgnoreCase(installOps); + } + + @ConditionalOnMissingClass("stirling.software.SPDF.config.security.SecurityConfiguration") + @Bean(name = "activSecurity") + public boolean missingActivSecurity() { + return false; } } diff --git a/src/main/java/stirling/software/SPDF/config/AppUpdateService.java b/src/main/java/stirling/software/SPDF/config/AppUpdateService.java new file mode 100644 index 00000000..7c7a9a49 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/AppUpdateService.java @@ -0,0 +1,25 @@ +package stirling.software.SPDF.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; + +import stirling.software.SPDF.model.ApplicationProperties; + +@Service +class AppUpdateService { + + @Autowired private ApplicationProperties applicationProperties; + + @Autowired(required = false) + ShowAdminInterface showAdmin; + + @Bean(name = "shouldShow") + @Scope("request") + public boolean shouldShow() { + boolean showUpdate = applicationProperties.getSystem().getShowUpdate(); + boolean showAdminResult = (showAdmin != null) ? showAdmin.getShowUpdateOnlyAdmins() : true; + return showUpdate && showAdminResult; + } +} diff --git a/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java b/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java index a5d05999..394baeb1 100644 --- a/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java +++ b/src/main/java/stirling/software/SPDF/config/ConfigInitializer.java @@ -1,20 +1,18 @@ package stirling.software.SPDF.config; -import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; +import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; -import java.util.Optional; +import java.util.Map; import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; @@ -26,12 +24,12 @@ public class ConfigInitializer public void initialize(ConfigurableApplicationContext applicationContext) { try { ensureConfigExists(); - } catch (IOException e) { + } catch (Exception e) { throw new RuntimeException("Failed to initialize application configuration", e); } } - public void ensureConfigExists() throws IOException { + public void ensureConfigExists() throws IOException, URISyntaxException { // Define the path to the external config directory Path destPath = Paths.get("configs", "settings.yml"); @@ -51,93 +49,154 @@ public class ConfigInitializer } } } else { - // If user file exists, we need to merge it with the template from the classpath - List templateLines; - try (InputStream in = - getClass().getClassLoader().getResourceAsStream("settings.yml.template")) { - templateLines = - new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)) - .lines() - .collect(Collectors.toList()); - } + Path templatePath = + Paths.get( + getClass() + .getClassLoader() + .getResource("settings.yml.template") + .toURI()); + Path userPath = Paths.get("configs", "settings.yml"); - mergeYamlFiles(templateLines, destPath, destPath); + List templateLines = Files.readAllLines(templatePath); + List userLines = + Files.exists(userPath) ? Files.readAllLines(userPath) : new ArrayList<>(); + + Map templateEntries = extractEntries(templateLines); + Map userEntries = extractEntries(userLines); + + List mergedLines = mergeConfigs(templateLines, templateEntries, userEntries); + mergedLines = cleanInvalidYamlEntries(mergedLines); + Files.write(userPath, mergedLines); + } + + Path customSettingsPath = Paths.get("configs", "custom_settings.yml"); + if (!Files.exists(customSettingsPath)) { + Files.createFile(customSettingsPath); } } - public void mergeYamlFiles(List templateLines, Path userFilePath, Path outputPath) - throws IOException { - List userLines = Files.readAllLines(userFilePath); + private static Map extractEntries(List lines) { + Map entries = new HashMap<>(); + StringBuilder currentEntry = new StringBuilder(); + String currentKey = null; + int blockIndent = -1; + + for (String line : lines) { + if (line.trim().isEmpty()) { + if (currentKey != null) { + currentEntry.append(line).append("\n"); + } + continue; + } + + int indentLevel = getIndentationLevel(line); + if (line.trim().startsWith("#")) { + if (indentLevel <= blockIndent || blockIndent == -1) { + if (currentKey != null) { + entries.put(currentKey, currentEntry.toString().trim()); + currentEntry = new StringBuilder(); + } + currentKey = line.trim().replaceAll("#", "").split(":")[0].trim(); + blockIndent = indentLevel; + } + currentEntry.append(line).append("\n"); + } else if (indentLevel == 0 || indentLevel <= blockIndent) { + if (currentKey != null) { + entries.put(currentKey, currentEntry.toString().trim()); + currentEntry = new StringBuilder(); + } + currentKey = line.split(":")[0].trim(); + blockIndent = indentLevel; + currentEntry.append(line).append("\n"); + } else { + currentEntry.append(line).append("\n"); + } + } + + if (currentKey != null) { + entries.put(currentKey, currentEntry.toString().trim()); + } + + return entries; + } + + private static List mergeConfigs( + List templateLines, + Map templateEntries, + Map userEntries) { List mergedLines = new ArrayList<>(); - boolean insideAutoGenerated = false; - boolean beforeFirstKey = true; + Set handledKeys = new HashSet<>(); - Function isCommented = line -> line.trim().startsWith("#"); - Function extractKey = - line -> { - String[] parts = line.split(":"); - return parts.length > 0 ? parts[0].trim().replace("#", "").trim() : ""; - }; - - Set userKeys = userLines.stream().map(extractKey).collect(Collectors.toSet()); + String currentBlockKey = null; + int blockIndent = -1; for (String line : templateLines) { - String key = extractKey.apply(line); - - if ("AutomaticallyGenerated:".equalsIgnoreCase(line.trim())) { - insideAutoGenerated = true; - mergedLines.add(line); - continue; - } else if (insideAutoGenerated && line.trim().isEmpty()) { - insideAutoGenerated = false; + if (line.trim().isEmpty()) { mergedLines.add(line); continue; } - if (beforeFirstKey && (isCommented.apply(line) || line.trim().isEmpty())) { - // Handle top comments and empty lines before the first key. + int indentLevel = getIndentationLevel(line); + if (indentLevel == 0 || (indentLevel <= blockIndent && !line.trim().startsWith("#"))) { + currentBlockKey = line.split(":")[0].trim(); + blockIndent = indentLevel; + } + + if (userEntries.containsKey(currentBlockKey) + && !handledKeys.contains(currentBlockKey)) { + mergedLines.add(userEntries.get(currentBlockKey)); + handledKeys.add(currentBlockKey); + } else if (!handledKeys.contains(currentBlockKey)) { mergedLines.add(line); - continue; - } - - if (!key.isEmpty()) beforeFirstKey = false; - - if (userKeys.contains(key)) { - // If user has any version (commented or uncommented) of this key, skip the - // template line - Optional userValue = - userLines.stream() - .filter( - l -> - extractKey.apply(l).equalsIgnoreCase(key) - && !isCommented.apply(l)) - .findFirst(); - if (userValue.isPresent()) mergedLines.add(userValue.get()); - continue; - } - - if (isCommented.apply(line) || line.trim().isEmpty() || !userKeys.contains(key)) { - mergedLines.add( - line); // If line is commented, empty or key not present in user's file, - // retain the - // template line - continue; } } - // Add any additional uncommented user lines that are not present in the - // template - for (String userLine : userLines) { - String userKey = extractKey.apply(userLine); - boolean isPresentInTemplate = - templateLines.stream() - .map(extractKey) - .anyMatch(templateKey -> templateKey.equalsIgnoreCase(userKey)); - if (!isPresentInTemplate && !isCommented.apply(userLine)) { - mergedLines.add(userLine); + return mergedLines; + } + + private static List cleanInvalidYamlEntries(List lines) { + List cleanedLines = new ArrayList<>(); + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + String trimmedLine = line.trim(); + + if (trimmedLine.startsWith("#") + || !trimmedLine.endsWith(":") + || trimmedLine.contains(" ")) { + cleanedLines.add(line); + continue; } + + if (isKeyWithoutChildrenOrValue(i, lines)) { + continue; + } + + cleanedLines.add(line); + } + return cleanedLines; + } + + private static boolean isKeyWithoutChildrenOrValue(int currentIndex, List lines) { + if (currentIndex + 1 < lines.size()) { + String currentLine = lines.get(currentIndex); + String nextLine = lines.get(currentIndex + 1); + int currentIndentation = getIndentationLevel(currentLine); + int nextIndentation = getIndentationLevel(nextLine); + + // If the next line is less or equally indented, it's not a child or value + return nextIndentation <= currentIndentation; } - Files.write(outputPath, mergedLines, StandardCharsets.UTF_8); + // If it's the last line, then it definitely has no children or value + return true; + } + + private static int getIndentationLevel(String line) { + int count = 0; + for (char ch : line.toCharArray()) { + if (ch == ' ') count++; + else break; + } + return count; } } diff --git a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index f82c189f..b145b478 100644 --- a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -129,7 +129,7 @@ public class EndpointConfiguration { addEndpointToGroup("Other", "sign"); addEndpointToGroup("Other", "flatten"); addEndpointToGroup("Other", "repair"); - addEndpointToGroup("Other", "remove-blanks"); + addEndpointToGroup("Other", REMOVE_BLANKS); addEndpointToGroup("Other", "remove-annotations"); addEndpointToGroup("Other", "compare"); addEndpointToGroup("Other", "add-page-numbers"); @@ -140,14 +140,12 @@ public class EndpointConfiguration { // CLI addEndpointToGroup("CLI", "compress-pdf"); addEndpointToGroup("CLI", "extract-image-scans"); - addEndpointToGroup("CLI", "remove-blanks"); addEndpointToGroup("CLI", "repair"); addEndpointToGroup("CLI", "pdf-to-pdfa"); addEndpointToGroup("CLI", "file-to-pdf"); addEndpointToGroup("CLI", "xlsx-to-pdf"); addEndpointToGroup("CLI", "pdf-to-word"); addEndpointToGroup("CLI", "pdf-to-presentation"); - addEndpointToGroup("CLI", "pdf-to-text"); addEndpointToGroup("CLI", "pdf-to-html"); addEndpointToGroup("CLI", "pdf-to-xml"); addEndpointToGroup("CLI", "ocr-pdf"); @@ -155,6 +153,7 @@ public class EndpointConfiguration { addEndpointToGroup("CLI", "url-to-pdf"); addEndpointToGroup("CLI", "book-to-pdf"); addEndpointToGroup("CLI", "pdf-to-book"); + addEndpointToGroup("CLI", "pdf-to-rtf"); // Calibre addEndpointToGroup("Calibre", "book-to-pdf"); @@ -162,13 +161,13 @@ public class EndpointConfiguration { // python addEndpointToGroup("Python", "extract-image-scans"); - addEndpointToGroup("Python", "remove-blanks"); + addEndpointToGroup("Python", REMOVE_BLANKS); addEndpointToGroup("Python", "html-to-pdf"); addEndpointToGroup("Python", "url-to-pdf"); // openCV addEndpointToGroup("OpenCV", "extract-image-scans"); - addEndpointToGroup("OpenCV", "remove-blanks"); + addEndpointToGroup("OpenCV", REMOVE_BLANKS); // LibreOffice addEndpointToGroup("LibreOffice", "repair"); @@ -176,7 +175,7 @@ public class EndpointConfiguration { addEndpointToGroup("LibreOffice", "xlsx-to-pdf"); addEndpointToGroup("LibreOffice", "pdf-to-word"); addEndpointToGroup("LibreOffice", "pdf-to-presentation"); - addEndpointToGroup("LibreOffice", "pdf-to-text"); + addEndpointToGroup("LibreOffice", "pdf-to-rtf"); addEndpointToGroup("LibreOffice", "pdf-to-html"); addEndpointToGroup("LibreOffice", "pdf-to-xml"); @@ -218,6 +217,8 @@ public class EndpointConfiguration { addEndpointToGroup("Java", "split-by-size-or-count"); addEndpointToGroup("Java", "overlay-pdf"); addEndpointToGroup("Java", "split-pdf-by-sections"); + addEndpointToGroup("Java", REMOVE_BLANKS); + addEndpointToGroup("Java", "pdf-to-text"); // Javascript addEndpointToGroup("Javascript", "pdf-organizer"); @@ -244,4 +245,6 @@ public class EndpointConfiguration { } } } + + private static final String REMOVE_BLANKS = "remove-blanks"; } diff --git a/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java b/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java new file mode 100644 index 00000000..be6e28a7 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/FileFallbackTemplateResolver.java @@ -0,0 +1,48 @@ +package stirling.software.SPDF.config; + +import java.io.IOException; +import java.util.Map; + +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.thymeleaf.IEngineConfiguration; +import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver; +import org.thymeleaf.templateresource.ClassLoaderTemplateResource; +import org.thymeleaf.templateresource.FileTemplateResource; +import org.thymeleaf.templateresource.ITemplateResource; + +public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver { + + private final ResourceLoader resourceLoader; + + public FileFallbackTemplateResolver(ResourceLoader resourceLoader) { + super(); + this.resourceLoader = resourceLoader; + setSuffix(".html"); + } + + // Note this does not work in local IDE, Prod jar only. + @Override + protected ITemplateResource computeTemplateResource( + IEngineConfiguration configuration, + String ownerTemplate, + String template, + String resourceName, + String characterEncoding, + Map templateResolutionAttributes) { + Resource resource = + resourceLoader.getResource("file:./customFiles/templates/" + resourceName); + try { + if (resource.exists() && resource.isReadable()) { + return new FileTemplateResource(resource.getFile().getPath(), characterEncoding); + } + } catch (IOException e) { + + } + + return new ClassLoaderTemplateResource( + Thread.currentThread().getContextClassLoader(), + "classpath:/templates/" + resourceName, + characterEncoding); + } +} diff --git a/src/main/java/stirling/software/SPDF/config/PostStartupProcesses.java b/src/main/java/stirling/software/SPDF/config/PostStartupProcesses.java deleted file mode 100644 index 2041cf95..00000000 --- a/src/main/java/stirling/software/SPDF/config/PostStartupProcesses.java +++ /dev/null @@ -1,69 +0,0 @@ -package stirling.software.SPDF.config; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Component; - -import jakarta.annotation.PostConstruct; -import stirling.software.SPDF.model.ApplicationProperties; -import stirling.software.SPDF.utils.ProcessExecutor; -import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; - -@Component -public class PostStartupProcesses { - - @Autowired ApplicationProperties applicationProperties; - - @Autowired - @Qualifier("RunningInDocker") - private boolean runningInDocker; - - @Autowired - @Qualifier("bookAndHtmlFormatsInstalled") - private boolean bookAndHtmlFormatsInstalled; - - private static final Logger logger = LoggerFactory.getLogger(PostStartupProcesses.class); - - @PostConstruct - public void runInstallCommandBasedOnEnvironment() throws IOException, InterruptedException { - List> commands = new ArrayList<>(); - // Checking for DOCKER_INSTALL_BOOK_FORMATS environment variable - if (bookAndHtmlFormatsInstalled) { - List tmpList = new ArrayList<>(); - - tmpList = new ArrayList<>(); - tmpList.addAll(Arrays.asList("apk add --no-cache calibre")); - commands.add(tmpList); - } - - if (!commands.isEmpty()) { - // Run the command - if (runningInDocker) { - List tmpList = new ArrayList<>(); - - for (List list : commands) { - ProcessExecutorResult returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.INSTALL_APP, true) - .runCommandWithOutputHandling(list); - logger.info("RC for app installs {}", returnCode.getRc()); - } - } else { - - logger.info( - "Not running inside Docker so skipping automated install process with command."); - } - - } else { - if (runningInDocker) { - logger.info("No custom apps to install."); - } - } - } -} diff --git a/src/main/java/stirling/software/SPDF/config/ShowAdminInterface.java b/src/main/java/stirling/software/SPDF/config/ShowAdminInterface.java new file mode 100644 index 00000000..e49376e2 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/ShowAdminInterface.java @@ -0,0 +1,7 @@ +package stirling.software.SPDF.config; + +public interface ShowAdminInterface { + default boolean getShowUpdateOnlyAdmins() { + return true; + } +} diff --git a/src/main/java/stirling/software/SPDF/config/security/AppUpdateAuthService.java b/src/main/java/stirling/software/SPDF/config/security/AppUpdateAuthService.java new file mode 100644 index 00000000..0da07c61 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/security/AppUpdateAuthService.java @@ -0,0 +1,46 @@ +package stirling.software.SPDF.config.security; + +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +import stirling.software.SPDF.config.ShowAdminInterface; +import stirling.software.SPDF.model.ApplicationProperties; +import stirling.software.SPDF.model.User; +import stirling.software.SPDF.repository.UserRepository; + +@Service +class AppUpdateAuthService implements ShowAdminInterface { + + @Autowired private UserRepository userRepository; + @Autowired private ApplicationProperties applicationProperties; + + public boolean getShowUpdateOnlyAdmins() { + boolean showUpdate = applicationProperties.getSystem().getShowUpdate(); + if (!showUpdate) { + return showUpdate; + } + + boolean showUpdateOnlyAdmin = applicationProperties.getSystem().getShowUpdateOnlyAdmin(); + + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication == null || !authentication.isAuthenticated()) { + return !showUpdateOnlyAdmin; + } + + if (authentication.getName().equalsIgnoreCase("anonymousUser")) { + return !showUpdateOnlyAdmin; + } + + Optional user = userRepository.findByUsername(authentication.getName()); + if (user.isPresent() && showUpdateOnlyAdmin) { + return "ROLE_ADMIN".equals(user.get().getRolesAsString()); + } + + return showUpdate; + } +} diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java b/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java index cbdf7d26..f4f5a37d 100644 --- a/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java +++ b/src/main/java/stirling/software/SPDF/config/security/CustomAuthenticationFailureHandler.java @@ -1,6 +1,7 @@ package stirling.software.SPDF.config.security; import java.io.IOException; +import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.BadCredentialsException; @@ -12,15 +13,19 @@ import org.springframework.stereotype.Component; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import stirling.software.SPDF.model.User; @Component public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { @Autowired private final LoginAttemptService loginAttemptService; - @Autowired - public CustomAuthenticationFailureHandler(LoginAttemptService loginAttemptService) { + @Autowired private final UserService userService; // Inject the UserService + + public CustomAuthenticationFailureHandler( + LoginAttemptService loginAttemptService, UserService userService) { this.loginAttemptService = loginAttemptService; + this.userService = userService; } @Override @@ -33,17 +38,27 @@ public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationF logger.error("Failed login attempt from IP: " + ip); String username = request.getParameter("username"); - if (loginAttemptService.loginAttemptCheck(username)) { - setDefaultFailureUrl("/login?error=locked"); - - } else { - if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) { - setDefaultFailureUrl("/login?error=badcredentials"); - } else if (exception.getClass().isAssignableFrom(LockedException.class)) { + if (!isDemoUser(username)) { + if (loginAttemptService.loginAttemptCheck(username)) { setDefaultFailureUrl("/login?error=locked"); + + } else { + if (exception.getClass().isAssignableFrom(LockedException.class)) { + setDefaultFailureUrl("/login?error=locked"); + } } } + if (exception.getClass().isAssignableFrom(BadCredentialsException.class)) { + setDefaultFailureUrl("/login?error=badcredentials"); + } super.onAuthenticationFailure(request, response, exception); } + + private boolean isDemoUser(String username) { + Optional user = userService.findByUsernameIgnoreCase(username); + return user.isPresent() + && user.get().getAuthorities().stream() + .anyMatch(authority -> "ROLE_DEMO_USER".equals(authority.getAuthority())); + } } diff --git a/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java new file mode 100644 index 00000000..300eee00 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/security/CustomLogoutSuccessHandler.java @@ -0,0 +1,39 @@ +package stirling.software.SPDF.config.security; + +import java.io.IOException; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +public class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + + @Override + public void onLogoutSuccess( + HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { + HttpSession session = request.getSession(false); + if (session != null) { + String sessionId = session.getId(); + sessionRegistry().removeSessionInformation(sessionId); + } + + if (request.getParameter("oauth2AutoCreateDisabled") != null) { + response.sendRedirect( + request.getContextPath() + "/login?error=oauth2AutoCreateDisabled"); + } else { + response.sendRedirect(request.getContextPath() + "/login?logout=true"); + } + } +} diff --git a/src/main/java/stirling/software/SPDF/config/security/FirstLoginFilter.java b/src/main/java/stirling/software/SPDF/config/security/FirstLoginFilter.java index b272327a..5936ea3f 100644 --- a/src/main/java/stirling/software/SPDF/config/security/FirstLoginFilter.java +++ b/src/main/java/stirling/software/SPDF/config/security/FirstLoginFilter.java @@ -39,7 +39,7 @@ public class FirstLoginFilter extends OncePerRequestFilter { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.isAuthenticated()) { - Optional user = userService.findByUsername(authentication.getName()); + Optional user = userService.findByUsernameIgnoreCase(authentication.getName()); if ("GET".equalsIgnoreCase(method) && user.isPresent() && user.get().isFirstLogin() diff --git a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java index 3b396b15..b483ba10 100644 --- a/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java +++ b/src/main/java/stirling/software/SPDF/config/security/InitialSecuritySetup.java @@ -38,7 +38,7 @@ public class InitialSecuritySetup { initialUsername, initialPassword, Role.ADMIN.getRoleId(), true); } } - if (!userService.usernameExists(Role.INTERNAL_API_USER.getRoleId())) { + if (!userService.usernameExistsIgnoreCase(Role.INTERNAL_API_USER.getRoleId())) { userService.saveUser( Role.INTERNAL_API_USER.getRoleId(), UUID.randomUUID().toString(), @@ -50,7 +50,7 @@ public class InitialSecuritySetup { @PostConstruct public void initSecretKey() throws IOException { String secretKey = applicationProperties.getAutomaticallyGenerated().getKey(); - if (secretKey == null || secretKey.isEmpty()) { + if (!isValidUUID(secretKey)) { secretKey = UUID.randomUUID().toString(); // Generating a random UUID as the secret key saveKeyToConfig(secretKey); } @@ -85,4 +85,16 @@ public class InitialSecuritySetup { // Write back to the file Files.write(path, lines); } + + private boolean isValidUUID(String uuid) { + if (uuid == null) { + return false; + } + try { + UUID.fromString(uuid); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } } diff --git a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java index ca88dcb9..8eb80cdc 100644 --- a/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/security/SecurityConfiguration.java @@ -1,7 +1,11 @@ package stirling.software.SPDF.config.security; +import java.io.IOException; +import java.util.*; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Lazy; @@ -9,15 +13,35 @@ import org.springframework.security.authentication.dao.DaoAuthenticationProvider import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.registration.ClientRegistrations; +import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.security.web.savedrequest.NullRequestCache; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import stirling.software.SPDF.model.ApplicationProperties; +import stirling.software.SPDF.model.User; import stirling.software.SPDF.repository.JPATokenRepositoryImpl; @Configuration @@ -38,12 +62,19 @@ public class SecurityConfiguration { @Qualifier("loginEnabled") public boolean loginEnabledValue; + @Autowired ApplicationProperties applicationProperties; + @Autowired private UserAuthenticationFilter userAuthenticationFilter; @Autowired private LoginAttemptService loginAttemptService; @Autowired private FirstLoginFilter firstLoginFilter; + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); @@ -53,6 +84,15 @@ public class SecurityConfiguration { http.csrf(csrf -> csrf.disable()); http.addFilterBefore(rateLimitingFilter(), UsernamePasswordAuthenticationFilter.class); http.addFilterAfter(firstLoginFilter, UsernamePasswordAuthenticationFilter.class); + http.sessionManagement( + sessionManagement -> + sessionManagement + .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) + .maximumSessions(10) + .maxSessionsPreventsLogin(false) + .sessionRegistry(sessionRegistry()) + .expiredUrl("/login?logout=true")); + http.formLogin( formLogin -> formLogin @@ -62,16 +102,27 @@ public class SecurityConfiguration { .defaultSuccessUrl("/") .failureHandler( new CustomAuthenticationFailureHandler( - loginAttemptService)) + loginAttemptService, userService)) .permitAll()) .requestCache(requestCache -> requestCache.requestCache(new NullRequestCache())) .logout( logout -> logout.logoutRequestMatcher( new AntPathRequestMatcher("/logout")) - .logoutSuccessUrl("/login?logout=true") + .logoutSuccessHandler(new CustomLogoutSuccessHandler()) .invalidateHttpSession(true) // Invalidate session - .deleteCookies("JSESSIONID", "remember-me")) + .deleteCookies("JSESSIONID", "remember-me") + .addLogoutHandler( + (request, response, authentication) -> { + HttpSession session = + request.getSession(false); + if (session != null) { + String sessionId = session.getId(); + sessionRegistry() + .removeSessionInformation( + sessionId); + } + })) .rememberMe( rememberMeConfigurer -> rememberMeConfigurer // Use the configurator directly @@ -95,6 +146,7 @@ public class SecurityConfiguration { : uri; return trimmedUri.startsWith("/login") + || trimmedUri.startsWith("/oauth") || trimmedUri.endsWith(".svg") || trimmedUri.startsWith( "/register") @@ -111,6 +163,49 @@ public class SecurityConfiguration { .authenticated()) .userDetailsService(userDetailsService) .authenticationProvider(authenticationProvider()); + + // Handle OAUTH2 Logins + if (applicationProperties.getSecurity().getOAUTH2().getEnabled()) { + + http.oauth2Login( + oauth2 -> + oauth2.loginPage("/oauth2") + /* + This Custom handler is used to check if the OAUTH2 user trying to log in, already exists in the database. + If user exists, login proceeds as usual. If user does not exist, then it is autocreated but only if 'OAUTH2AutoCreateUser' + is set as true, else login fails with an error message advising the same. + */ + .successHandler( + new AuthenticationSuccessHandler() { + @Override + public void onAuthenticationSuccess( + HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) + throws ServletException, IOException { + OAuth2User oauthUser = + (OAuth2User) + authentication + .getPrincipal(); + if (userService.processOAuth2PostLogin( + oauthUser.getAttribute("email"), + applicationProperties + .getSecurity() + .getOAUTH2() + .getAutoCreateUser())) { + response.sendRedirect("/"); + } else { + response.sendRedirect( + "/logout?oauth2AutoCreateDisabled=true"); + } + } + }) + // Add existing Authorities from the database + .userInfoEndpoint( + userInfoEndpoint -> + userInfoEndpoint.userAuthoritiesMapper( + userAuthoritiesMapper()))); + } } else { http.csrf(csrf -> csrf.disable()) .authorizeHttpRequests(authz -> authz.anyRequest().permitAll()); @@ -119,6 +214,65 @@ public class SecurityConfiguration { return http.build(); } + // Client Registration Repository for OAUTH2 OIDC Login + @Bean + @ConditionalOnProperty( + value = "security.oauth2.enabled", + havingValue = "true", + matchIfMissing = false) + public ClientRegistrationRepository clientRegistrationRepository() { + return new InMemoryClientRegistrationRepository(this.oidcClientRegistration()); + } + + private ClientRegistration oidcClientRegistration() { + return ClientRegistrations.fromOidcIssuerLocation( + applicationProperties.getSecurity().getOAUTH2().getIssuer()) + .registrationId("oidc") + .clientId(applicationProperties.getSecurity().getOAUTH2().getClientId()) + .clientSecret(applicationProperties.getSecurity().getOAUTH2().getClientSecret()) + .scope("openid", "profile", "email") + .userNameAttributeName("email") + .clientName("OIDC") + .build(); + } + + /* + This following function is to grant Authorities to the OAUTH2 user from the values stored in the database. + This is required for the internal; 'hasRole()' function to give out the correct role. + */ + @Bean + @ConditionalOnProperty( + value = "security.oauth2.enabled", + havingValue = "true", + matchIfMissing = false) + GrantedAuthoritiesMapper userAuthoritiesMapper() { + return (authorities) -> { + Set mappedAuthorities = new HashSet<>(); + + authorities.forEach( + authority -> { + // Add existing OAUTH2 Authorities + mappedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority())); + + // Add Authorities from database for existing user, if user is present. + if (authority instanceof OAuth2UserAuthority oauth2Auth) { + Optional userOpt = + userService.findByUsernameIgnoreCase( + (String) oauth2Auth.getAttributes().get("email")); + if (userOpt.isPresent()) { + User user = userOpt.get(); + if (user != null) { + mappedAuthorities.add( + new SimpleGrantedAuthority( + userService.findRole(user).getAuthority())); + } + } + } + }); + return mappedAuthorities; + }; + } + @Bean public IPRateLimitingFilter rateLimitingFilter() { int maxRequestsPerIp = 1000000; // Example limit TODO add config level @@ -137,4 +291,9 @@ public class SecurityConfiguration { public PersistentTokenRepository persistentTokenRepository() { return new JPATokenRepositoryImpl(); } + + @Bean + public boolean activSecurity() { + return true; + } } diff --git a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java index 47423eb6..244efed3 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserAuthenticationFilter.java @@ -82,7 +82,7 @@ public class UserAuthenticationFilter extends OncePerRequestFilter { response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.getWriter() .write( - "Authentication required. Please provide a X-API-KEY in request header.\nThis is found in Settings -> Account Settings -> API Key\nAlternativly you can disable authentication if this is unexpected"); + "Authentication required. Please provide a X-API-KEY in request header.\nThis is found in Settings -> Account Settings -> API Key\nAlternatively you can disable authentication if this is unexpected"); return; } } diff --git a/src/main/java/stirling/software/SPDF/config/security/UserBasedRateLimitingFilter.java b/src/main/java/stirling/software/SPDF/config/security/UserBasedRateLimitingFilter.java index 7b3b9b4e..89107697 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserBasedRateLimitingFilter.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserBasedRateLimitingFilter.java @@ -1,5 +1,6 @@ package stirling.software.SPDF.config.security; +import io.github.pixee.security.Newlines; import java.io.IOException; import java.time.Duration; import java.util.Map; @@ -125,12 +126,12 @@ public class UserBasedRateLimitingFilter extends OncePerRequestFilter { ConsumptionProbe probe = userBucket.tryConsumeAndReturnRemaining(1); if (probe.isConsumed()) { - response.setHeader("X-Rate-Limit-Remaining", Long.toString(probe.getRemainingTokens())); + response.setHeader("X-Rate-Limit-Remaining", Newlines.stripAll(Long.toString(probe.getRemainingTokens()))); filterChain.doFilter(request, response); } else { long waitForRefill = probe.getNanosToWaitForRefill() / 1_000_000_000; response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value()); - response.setHeader("X-Rate-Limit-Retry-After-Seconds", String.valueOf(waitForRefill)); + response.setHeader("X-Rate-Limit-Retry-After-Seconds", Newlines.stripAll(String.valueOf(waitForRefill))); response.getWriter().write("Rate limit exceeded for POST requests."); } } diff --git a/src/main/java/stirling/software/SPDF/config/security/UserService.java b/src/main/java/stirling/software/SPDF/config/security/UserService.java index 60b3ebef..7d57f8f6 100644 --- a/src/main/java/stirling/software/SPDF/config/security/UserService.java +++ b/src/main/java/stirling/software/SPDF/config/security/UserService.java @@ -21,6 +21,7 @@ import stirling.software.SPDF.controller.api.pipeline.UserServiceInterface; import stirling.software.SPDF.model.Authority; import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.User; +import stirling.software.SPDF.repository.AuthorityRepository; import stirling.software.SPDF.repository.UserRepository; @Service @@ -28,8 +29,28 @@ public class UserService implements UserServiceInterface { @Autowired private UserRepository userRepository; + @Autowired private AuthorityRepository authorityRepository; + @Autowired private PasswordEncoder passwordEncoder; + // Handle OAUTH2 login and user auto creation. + public boolean processOAuth2PostLogin(String username, boolean autoCreateUser) { + Optional existUser = userRepository.findByUsernameIgnoreCase(username); + if (existUser.isPresent()) { + return true; + } + if (autoCreateUser) { + User user = new User(); + user.setUsername(username); + user.setEnabled(true); + user.setFirstLogin(false); + user.addAuthority(new Authority(Role.USER.getRoleId(), user)); + userRepository.save(user); + return true; + } + return false; + } + public Authentication getAuthentication(String apiKey) { User user = getUserByApiKey(apiKey); if (user == null) { @@ -62,7 +83,7 @@ public class UserService implements UserServiceInterface { public User addApiKeyToUser(String username) { User user = userRepository - .findByUsername(username) + .findByUsernameIgnoreCase(username) .orElseThrow(() -> new UsernameNotFoundException("User not found")); user.setApiKey(generateApiKey()); @@ -76,7 +97,7 @@ public class UserService implements UserServiceInterface { public String getApiKeyForUser(String username) { User user = userRepository - .findByUsername(username) + .findByUsernameIgnoreCase(username) .orElseThrow(() -> new UsernameNotFoundException("User not found")); return user.getApiKey(); } @@ -103,7 +124,7 @@ public class UserService implements UserServiceInterface { } public boolean validateApiKeyForUser(String username, String apiKey) { - Optional userOpt = userRepository.findByUsername(username); + Optional userOpt = userRepository.findByUsernameIgnoreCase(username); return userOpt.isPresent() && userOpt.get().getApiKey().equals(apiKey); } @@ -136,7 +157,7 @@ public class UserService implements UserServiceInterface { } public void deleteUser(String username) { - Optional userOpt = userRepository.findByUsername(username); + Optional userOpt = userRepository.findByUsernameIgnoreCase(username); if (userOpt.isPresent()) { for (Authority authority : userOpt.get().getAuthorities()) { if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) { @@ -151,12 +172,16 @@ public class UserService implements UserServiceInterface { return userRepository.findByUsername(username).isPresent(); } + public boolean usernameExistsIgnoreCase(String username) { + return userRepository.findByUsernameIgnoreCase(username).isPresent(); + } + public boolean hasUsers() { return userRepository.count() > 0; } public void updateUserSettings(String username, Map updates) { - Optional userOpt = userRepository.findByUsername(username); + Optional userOpt = userRepository.findByUsernameIgnoreCase(username); if (userOpt.isPresent()) { User user = userOpt.get(); Map settingsMap = user.getSettings(); @@ -176,6 +201,14 @@ public class UserService implements UserServiceInterface { return userRepository.findByUsername(username); } + public Optional findByUsernameIgnoreCase(String username) { + return userRepository.findByUsernameIgnoreCase(username); + } + + public Authority findRole(User user) { + return authorityRepository.findByUserId(user.getId()); + } + public void changeUsername(User user, String newUsername) { user.setUsername(newUsername); userRepository.save(user); @@ -191,7 +224,17 @@ public class UserService implements UserServiceInterface { userRepository.save(user); } + public void changeRole(User user, String newRole) { + Authority userAuthority = this.findRole(user); + userAuthority.setAuthority(newRole); + authorityRepository.save(userAuthority); + } + public boolean isPasswordCorrect(User user, String currentPassword) { return passwordEncoder.matches(currentPassword, user.getPassword()); } + + public boolean isUsernameValid(String username) { + return username.matches("[a-zA-Z0-9]+"); + } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java index ad742137..1719d325 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -38,7 +38,7 @@ public class MergeController { private static final Logger logger = LoggerFactory.getLogger(MergeController.class); - private PDDocument mergeDocuments(List documents) throws IOException { + public PDDocument mergeDocuments(List documents) throws IOException { PDDocument mergedDoc = new PDDocument(); for (PDDocument doc : documents) { for (PDPage page : doc.getPages()) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java index 88bdcd91..a1e80af3 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java @@ -2,6 +2,7 @@ package stirling.software.SPDF.controller.api; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.apache.pdfbox.Loader; @@ -50,7 +51,9 @@ public class RearrangePagesPDFController { String[] pageOrderArr = pagesToDelete.split(","); List pagesToRemove = - GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages()); + GeneralUtils.parsePageList(pageOrderArr, document.getNumberOfPages(), false); + + Collections.sort(pagesToRemove); for (int i = pagesToRemove.size() - 1; i >= 0; i--) { int pageIndex = pagesToRemove.get(i); @@ -192,7 +195,7 @@ public class RearrangePagesPDFController { if (sortType != null && sortType.length() > 0) { newPageOrder = processSortTypes(sortType, totalPages); } else { - newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages); + newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false); } logger.info("newPageOrder = " + newPageOrder); logger.info("totalPages = " + totalPages); diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java index a69dea34..51efa644 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java @@ -27,7 +27,9 @@ import io.github.pixee.security.Filenames; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import stirling.software.SPDF.model.PdfMetadata; import stirling.software.SPDF.model.api.PDFWithPageNums; +import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -49,10 +51,17 @@ public class SplitPDFController { // open the pdf document PDDocument document = Loader.loadPDF(file.getBytes()); + PdfMetadata metadata = PdfUtils.extractMetadataFromPdf(document); + int totalPages = document.getNumberOfPages(); + List pageNumbers = request.getPageNumbersList(document, false); + System.out.println( + pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(","))); + if (!pageNumbers.contains(totalPages - 1)) { + // Create a mutable ArrayList so we can add to it + pageNumbers = new ArrayList<>(pageNumbers); + pageNumbers.add(totalPages - 1); + } - List pageNumbers = request.getPageNumbersList(document, true); - if (!pageNumbers.contains(document.getNumberOfPages() - 1)) - pageNumbers.add(document.getNumberOfPages() - 1); logger.info( "Splitting PDF into pages: {}", pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(","))); @@ -65,10 +74,13 @@ public class SplitPDFController { for (int i = previousPageNumber; i <= splitPoint; i++) { PDPage page = document.getPage(i); splitDocument.addPage(page); - logger.debug("Adding page {} to split document", i); + logger.info("Adding page {} to split document", i); } previousPageNumber = splitPoint + 1; + // Transfer metadata to split pdf + PdfUtils.setMetadataToPdf(splitDocument, metadata); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); splitDocument.save(baos); diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java index 3deb798e..3682aaf7 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java @@ -53,8 +53,21 @@ public class SplitPdfBySectionsController { // Process the PDF based on split parameters int horiz = request.getHorizontalDivisions() + 1; int verti = request.getVerticalDivisions() + 1; - + boolean merge = request.isMerge(); List splitDocuments = splitPdfPages(sourceDocument, verti, horiz); + + String filename = + Filenames.toSimpleFileName(file.getOriginalFilename()) + .replaceFirst("[.][^.]+$", ""); + if (merge) { + MergeController mergeController = new MergeController(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mergeController.mergeDocuments(splitDocuments).save(baos); + return WebResponseUtils.bytesToWebResponse( + baos.toByteArray(), + filename + "_split.pdf", + MediaType.APPLICATION_OCTET_STREAM); + } for (PDDocument doc : splitDocuments) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); doc.save(baos); @@ -65,9 +78,6 @@ public class SplitPdfBySectionsController { sourceDocument.close(); Path zipFile = Files.createTempFile("split_documents", ".zip"); - String filename = - Filenames.toSimpleFileName(file.getOriginalFilename()) - .replaceFirst("[.][^.]+$", ""); byte[] data; try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java index 45d2dd38..40ac2d16 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySizeController.java @@ -4,8 +4,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -41,117 +39,137 @@ public class SplitPdfBySizeController { + " if 10MB and each page is 1MB and you enter 2MB then 5 docs each 2MB (rounded so that it accepts 1.9MB but not 2.1MB) Input:PDF Output:ZIP-PDF Type:SISO") public ResponseEntity autoSplitPdf(@ModelAttribute SplitPdfBySizeOrCountRequest request) throws Exception { - List splitDocumentsBoas = new ArrayList(); MultipartFile file = request.getFileInput(); - PDDocument sourceDocument = Loader.loadPDF(file.getBytes()); - - // 0 = size, 1 = page count, 2 = doc count - int type = request.getSplitType(); - String value = request.getSplitValue(); - - if (type == 0) { // Split by size - long maxBytes = GeneralUtils.convertSizeToBytes(value); - long currentSize = 0; - PDDocument currentDoc = new PDDocument(); - - for (PDPage page : sourceDocument.getPages()) { - ByteArrayOutputStream pageOutputStream = new ByteArrayOutputStream(); - PDDocument tempDoc = new PDDocument(); - tempDoc.addPage(page); - tempDoc.save(pageOutputStream); - tempDoc.close(); - - long pageSize = pageOutputStream.size(); - if (currentSize + pageSize > maxBytes) { - // Save and reset current document - splitDocumentsBoas.add(currentDocToByteArray(currentDoc)); - currentDoc = new PDDocument(); - currentSize = 0; - } - - currentDoc.addPage(page); - currentSize += pageSize; - } - // Add the last document if it contains any pages - if (currentDoc.getPages().getCount() != 0) { - splitDocumentsBoas.add(currentDocToByteArray(currentDoc)); - } - } else if (type == 1) { // Split by page count - int pageCount = Integer.parseInt(value); - int currentPageCount = 0; - PDDocument currentDoc = new PDDocument(); - - for (PDPage page : sourceDocument.getPages()) { - currentDoc.addPage(page); - currentPageCount++; - - if (currentPageCount == pageCount) { - // Save and reset current document - splitDocumentsBoas.add(currentDocToByteArray(currentDoc)); - currentDoc = new PDDocument(); - currentPageCount = 0; - } - } - // Add the last document if it contains any pages - if (currentDoc.getPages().getCount() != 0) { - splitDocumentsBoas.add(currentDocToByteArray(currentDoc)); - } - } else if (type == 2) { // Split by doc count - int documentCount = Integer.parseInt(value); - int totalPageCount = sourceDocument.getNumberOfPages(); - int pagesPerDocument = totalPageCount / documentCount; - int extraPages = totalPageCount % documentCount; - int currentPageIndex = 0; - - for (int i = 0; i < documentCount; i++) { - PDDocument currentDoc = new PDDocument(); - int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0); - - for (int j = 0; j < pagesToAdd; j++) { - currentDoc.addPage(sourceDocument.getPage(currentPageIndex++)); - } - - splitDocumentsBoas.add(currentDocToByteArray(currentDoc)); - } - } else { - throw new IllegalArgumentException("Invalid argument for split type"); - } - - sourceDocument.close(); - Path zipFile = Files.createTempFile("split_documents", ".zip"); String filename = Filenames.toSimpleFileName(file.getOriginalFilename()) .replaceFirst("[.][^.]+$", ""); - byte[] data; + byte[] data = null; + try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile)); + PDDocument sourceDocument = Loader.loadPDF(file.getBytes())) { - try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) { - for (int i = 0; i < splitDocumentsBoas.size(); i++) { - String fileName = filename + "_" + (i + 1) + ".pdf"; - ByteArrayOutputStream baos = splitDocumentsBoas.get(i); - byte[] pdf = baos.toByteArray(); + int type = request.getSplitType(); + String value = request.getSplitValue(); - ZipEntry pdfEntry = new ZipEntry(fileName); - zipOut.putNextEntry(pdfEntry); - zipOut.write(pdf); - zipOut.closeEntry(); + if (type == 0) { + long maxBytes = GeneralUtils.convertSizeToBytes(value); + handleSplitBySize(sourceDocument, maxBytes, zipOut, filename); + } else if (type == 1) { + int pageCount = Integer.parseInt(value); + handleSplitByPageCount(sourceDocument, pageCount, zipOut, filename); + } else if (type == 2) { + int documentCount = Integer.parseInt(value); + handleSplitByDocCount(sourceDocument, documentCount, zipOut, filename); + } else { + throw new IllegalArgumentException("Invalid argument for split type"); } + } catch (Exception e) { e.printStackTrace(); } finally { data = Files.readAllBytes(zipFile); - Files.delete(zipFile); + Files.deleteIfExists(zipFile); } return WebResponseUtils.bytesToWebResponse( data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM); } - private ByteArrayOutputStream currentDocToByteArray(PDDocument document) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - document.save(baos); - document.close(); - return baos; + private void handleSplitBySize( + PDDocument sourceDocument, long maxBytes, ZipOutputStream zipOut, String baseFilename) + throws IOException { + long currentSize = 0; + PDDocument currentDoc = new PDDocument(); + int fileIndex = 1; + + for (int pageIndex = 0; pageIndex < sourceDocument.getNumberOfPages(); pageIndex++) { + PDPage page = sourceDocument.getPage(pageIndex); + ByteArrayOutputStream pageOutputStream = new ByteArrayOutputStream(); + + try (PDDocument tempDoc = new PDDocument()) { + PDPage importedPage = tempDoc.importPage(page); // This creates a new PDPage object + tempDoc.save(pageOutputStream); + } + + long pageSize = pageOutputStream.size(); + if (currentSize + pageSize > maxBytes) { + if (currentDoc.getNumberOfPages() > 0) { + saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++); + currentDoc.close(); // Make sure to close the document + currentDoc = new PDDocument(); + currentSize = 0; + } + } + + PDPage newPage = new PDPage(page.getCOSObject()); // Re-create the page + currentDoc.addPage(newPage); + currentSize += pageSize; + } + + if (currentDoc.getNumberOfPages() != 0) { + saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++); + currentDoc.close(); + } + } + + private void handleSplitByPageCount( + PDDocument sourceDocument, int pageCount, ZipOutputStream zipOut, String baseFilename) + throws IOException { + int currentPageCount = 0; + PDDocument currentDoc = new PDDocument(); + int fileIndex = 1; + for (PDPage page : sourceDocument.getPages()) { + currentDoc.addPage(page); + currentPageCount++; + + if (currentPageCount == pageCount) { + // Save and reset current document + saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++); + currentDoc = new PDDocument(); + currentPageCount = 0; + } + } + // Add the last document if it contains any pages + if (currentDoc.getPages().getCount() != 0) { + saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++); + } + } + + private void handleSplitByDocCount( + PDDocument sourceDocument, + int documentCount, + ZipOutputStream zipOut, + String baseFilename) + throws IOException { + int totalPageCount = sourceDocument.getNumberOfPages(); + int pagesPerDocument = totalPageCount / documentCount; + int extraPages = totalPageCount % documentCount; + int currentPageIndex = 0; + int fileIndex = 1; + for (int i = 0; i < documentCount; i++) { + PDDocument currentDoc = new PDDocument(); + int pagesToAdd = pagesPerDocument + (i < extraPages ? 1 : 0); + + for (int j = 0; j < pagesToAdd; j++) { + currentDoc.addPage(sourceDocument.getPage(currentPageIndex++)); + } + + saveDocumentToZip(currentDoc, zipOut, baseFilename, fileIndex++); + } + } + + private void saveDocumentToZip( + PDDocument document, ZipOutputStream zipOut, String baseFilename, int index) + throws IOException { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + document.save(outStream); + document.close(); // Close the document to free resources + + // Create a new zip entry + ZipEntry zipEntry = new ZipEntry(baseFilename + "_" + index + ".pdf"); + zipOut.putNextEntry(zipEntry); + zipOut.write(outStream.toByteArray()); + zipOut.closeEntry(); } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/UserController.java b/src/main/java/stirling/software/SPDF/controller/api/UserController.java index ce15d19c..f5b7b159 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/UserController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/UserController.java @@ -10,6 +10,9 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; +import org.springframework.security.core.session.SessionInformation; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -28,7 +31,6 @@ import jakarta.servlet.http.HttpServletResponse; import stirling.software.SPDF.config.security.UserService; import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.User; -import stirling.software.SPDF.model.api.user.UpdateUserDetails; import stirling.software.SPDF.model.api.user.UsernameAndPass; @Controller @@ -41,7 +43,7 @@ public class UserController { @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") @PostMapping("/register") public String register(@ModelAttribute UsernameAndPass requestModel, Model model) { - if (userService.usernameExists(requestModel.getUsername())) { + if (userService.usernameExistsIgnoreCase(requestModel.getUsername())) { model.addAttribute("error", "Username already exists"); return "register"; } @@ -50,66 +52,25 @@ public class UserController { return "redirect:/login?registered=true"; } - @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") - @PostMapping("/change-username-and-password") - public RedirectView changeUsernameAndPassword( - Principal principal, - @ModelAttribute UpdateUserDetails requestModel, - HttpServletRequest request, - HttpServletResponse response, - RedirectAttributes redirectAttributes) { - - String currentPassword = requestModel.getPassword(); - String newPassword = requestModel.getNewPassword(); - String newUsername = requestModel.getNewUsername(); - - if (principal == null) { - return new RedirectView("/change-creds?messageType=notAuthenticated"); - } - - Optional userOpt = userService.findByUsername(principal.getName()); - - if (userOpt == null || userOpt.isEmpty()) { - return new RedirectView("/change-creds?messageType=userNotFound"); - } - - User user = userOpt.get(); - - if (!userService.isPasswordCorrect(user, currentPassword)) { - return new RedirectView("/change-creds?messageType=incorrectPassword"); - } - - if (!user.getUsername().equals(newUsername) && userService.usernameExists(newUsername)) { - return new RedirectView("/change-creds?messageType=usernameExists"); - } - - userService.changePassword(user, newPassword); - if (newUsername != null - && newUsername.length() > 0 - && !user.getUsername().equals(newUsername)) { - userService.changeUsername(user, newUsername); - } - userService.changeFirstUse(user, false); - - // Logout using Spring's utility - new SecurityContextLogoutHandler().logout(request, response, null); - - return new RedirectView("/login?messageType=credsUpdated"); - } - @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") @PostMapping("/change-username") public RedirectView changeUsername( Principal principal, - @RequestParam String currentPassword, - @RequestParam String newUsername, + @RequestParam(name = "currentPassword") String currentPassword, + @RequestParam(name = "newUsername") String newUsername, HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes) { + + if (!userService.isUsernameValid(newUsername)) { + return new RedirectView("/account?messageType=invalidUsername"); + } + if (principal == null) { return new RedirectView("/account?messageType=notAuthenticated"); } + // The username MUST be unique when renaming Optional userOpt = userService.findByUsername(principal.getName()); if (userOpt == null || userOpt.isEmpty()) { @@ -118,6 +79,10 @@ public class UserController { User user = userOpt.get(); + if (user.getUsername().equals(newUsername)) { + return new RedirectView("/account?messageType=usernameExists"); + } + if (!userService.isPasswordCorrect(user, currentPassword)) { return new RedirectView("/account?messageType=incorrectPassword"); } @@ -133,15 +98,48 @@ public class UserController { // Logout using Spring's utility new SecurityContextLogoutHandler().logout(request, response, null); - return new RedirectView("/login?messageType=credsUpdated"); + return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED); + } + + @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") + @PostMapping("/change-password-on-login") + public RedirectView changePasswordOnLogin( + Principal principal, + @RequestParam(name = "currentPassword") String currentPassword, + @RequestParam(name = "newPassword") String newPassword, + HttpServletRequest request, + HttpServletResponse response, + RedirectAttributes redirectAttributes) { + if (principal == null) { + return new RedirectView("/change-creds?messageType=notAuthenticated"); + } + + Optional userOpt = userService.findByUsernameIgnoreCase(principal.getName()); + + if (userOpt == null || userOpt.isEmpty()) { + return new RedirectView("/change-creds?messageType=userNotFound"); + } + + User user = userOpt.get(); + + if (!userService.isPasswordCorrect(user, currentPassword)) { + return new RedirectView("/change-creds?messageType=incorrectPassword"); + } + + userService.changePassword(user, newPassword); + userService.changeFirstUse(user, false); + // Logout using Spring's utility + new SecurityContextLogoutHandler().logout(request, response, null); + + return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED); } @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") @PostMapping("/change-password") public RedirectView changePassword( Principal principal, - @RequestParam String currentPassword, - @RequestParam String newPassword, + @RequestParam(name = "currentPassword") String currentPassword, + @RequestParam(name = "newPassword") String newPassword, HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes) { @@ -149,7 +147,7 @@ public class UserController { return new RedirectView("/account?messageType=notAuthenticated"); } - Optional userOpt = userService.findByUsername(principal.getName()); + Optional userOpt = userService.findByUsernameIgnoreCase(principal.getName()); if (userOpt == null || userOpt.isEmpty()) { return new RedirectView("/account?messageType=userNotFound"); @@ -166,7 +164,7 @@ public class UserController { // Logout using Spring's utility new SecurityContextLogoutHandler().logout(request, response, null); - return new RedirectView("/login?messageType=credsUpdated"); + return new RedirectView(LOGIN_MESSAGETYPE_CREDSUPDATED); } @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") @@ -192,13 +190,25 @@ public class UserController { @PreAuthorize("hasRole('ROLE_ADMIN')") @PostMapping("/admin/saveUser") public RedirectView saveUser( - @RequestParam String username, - @RequestParam String password, - @RequestParam String role, + @RequestParam(name = "username") String username, + @RequestParam(name = "password") String password, + @RequestParam(name = "role") String role, @RequestParam(name = "forceChange", required = false, defaultValue = "false") boolean forceChange) { - if (userService.usernameExists(username)) { + if (!userService.isUsernameValid(username)) { + return new RedirectView("/addUsers?messageType=invalidUsername"); + } + + Optional userOpt = userService.findByUsernameIgnoreCase(username); + + if (userOpt.isPresent()) { + User user = userOpt.get(); + if (user != null && user.getUsername().equalsIgnoreCase(username)) { + return new RedirectView("/addUsers?messageType=usernameExists"); + } + } + if (userService.usernameExistsIgnoreCase(username)) { return new RedirectView("/addUsers?messageType=usernameExists"); } try { @@ -217,20 +227,80 @@ public class UserController { return new RedirectView("/addUsers"); // Redirect to account page after adding the user } + @PreAuthorize("hasRole('ROLE_ADMIN')") + @PostMapping("/admin/changeRole") + public RedirectView changeRole( + @RequestParam(name = "username") String username, + @RequestParam(name = "role") String role, + Authentication authentication) { + + Optional userOpt = userService.findByUsernameIgnoreCase(username); + + if (!userOpt.isPresent()) { + return new RedirectView("/addUsers?messageType=userNotFound"); + } + if (!userService.usernameExistsIgnoreCase(username)) { + return new RedirectView("/addUsers?messageType=userNotFound"); + } + // Get the currently authenticated username + String currentUsername = authentication.getName(); + + // Check if the provided username matches the current session's username + if (currentUsername.equalsIgnoreCase(username)) { + return new RedirectView("/addUsers?messageType=downgradeCurrentUser"); + } + try { + // Validate the role + Role roleEnum = Role.fromString(role); + if (roleEnum == Role.INTERNAL_API_USER) { + // If the role is INTERNAL_API_USER, reject the request + return new RedirectView("/addUsers?messageType=invalidRole"); + } + } catch (IllegalArgumentException e) { + // If the role ID is not valid, redirect with an error message + return new RedirectView("/addUsers?messageType=invalidRole"); + } + User user = userOpt.get(); + + userService.changeRole(user, role); + return new RedirectView("/addUsers"); // Redirect to account page after adding the user + } + @PreAuthorize("hasRole('ROLE_ADMIN')") @PostMapping("/admin/deleteUser/{username}") - public String deleteUser(@PathVariable String username, Authentication authentication) { + public RedirectView deleteUser( + @PathVariable(name = "username") String username, Authentication authentication) { + + if (!userService.usernameExistsIgnoreCase(username)) { + return new RedirectView("/addUsers?messageType=deleteUsernameExists"); + } // Get the currently authenticated username String currentUsername = authentication.getName(); // Check if the provided username matches the current session's username - if (currentUsername.equals(username)) { - throw new IllegalArgumentException("Cannot delete currently logined in user."); + if (currentUsername.equalsIgnoreCase(username)) { + return new RedirectView("/addUsers?messageType=deleteCurrentUser"); } - + invalidateUserSessions(username); userService.deleteUser(username); - return "redirect:/addUsers"; + return new RedirectView("/addUsers"); + } + + @Autowired private SessionRegistry sessionRegistry; + + private void invalidateUserSessions(String username) { + for (Object principal : sessionRegistry.getAllPrincipals()) { + if (principal instanceof UserDetails) { + UserDetails userDetails = (UserDetails) principal; + if (userDetails.getUsername().equals(username)) { + for (SessionInformation session : + sessionRegistry.getAllSessions(principal, false)) { + session.expireNow(); + } + } + } + } } @PreAuthorize("!hasAuthority('ROLE_DEMO_USER')") @@ -261,4 +331,6 @@ public class UserController { } return ResponseEntity.ok(apiKey); } + + private static final String LOGIN_MESSAGETYPE_CREDSUPDATED = "/login?messageType=credsUpdated"; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java index 05784a15..41e6520d 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertBookToPDFController.java @@ -37,7 +37,7 @@ public class ConvertBookToPDFController { if (!bookAndHtmlFormatsInstalled) { throw new IllegalArgumentException( - "bookAndHtmlFormatsInstalled flag is False, this functionality is not avaiable"); + "bookAndHtmlFormatsInstalled flag is False, this functionality is not available"); } if (fileInput == null) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java index 86a70472..9fa357f7 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertImgPDFController.java @@ -6,10 +6,6 @@ import java.net.URLConnection; import org.apache.pdfbox.rendering.ImageType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.core.io.ByteArrayResource; -import org.springframework.core.io.Resource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -39,7 +35,7 @@ public class ConvertImgPDFController { summary = "Convert PDF to image(s)", description = "This endpoint converts a PDF file to image(s) with the specified image format, color type, and DPI. Users can choose to get a single image or multiple images. Input:PDF Output:Image Type:SI-Conditional") - public ResponseEntity convertToImage(@ModelAttribute ConvertToImageRequest request) + public ResponseEntity convertToImage(@ModelAttribute ConvertToImageRequest request) throws IOException { MultipartFile file = request.getFileInput(); String imageFormat = request.getImageFormat(); @@ -76,22 +72,15 @@ public class ConvertImgPDFController { // TODO Auto-generated catch block e.printStackTrace(); } + if (singleImage) { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat))); - ResponseEntity response = - new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK); - return response; + String docName = filename + "." + imageFormat; + MediaType mediaType = MediaType.parseMediaType(getMediaType(imageFormat)); + return WebResponseUtils.bytesToWebResponse(result, docName, mediaType); } else { - ByteArrayResource resource = new ByteArrayResource(result); - // return the Resource in the response - return ResponseEntity.ok() - .header( - HttpHeaders.CONTENT_DISPOSITION, - "attachment; filename=" + filename + "_convertedToImages.zip") - .contentType(MediaType.APPLICATION_OCTET_STREAM) - .contentLength(resource.contentLength()) - .body(resource); + String zipFilename = filename + "_convertedToImages.zip"; + return WebResponseUtils.bytesToWebResponse( + result, zipFilename, MediaType.APPLICATION_OCTET_STREAM); } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java index 28793d48..c8b9dd4d 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToBookController.java @@ -45,7 +45,7 @@ public class ConvertPDFToBookController { if (!bookAndHtmlFormatsInstalled) { throw new IllegalArgumentException( - "bookAndHtmlFormatsInstalled flag is False, this functionality is not avaiable"); + "bookAndHtmlFormatsInstalled flag is False, this functionality is not available"); } if (fileInput == null) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToHtml.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToHtml.java new file mode 100644 index 00000000..beafd389 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToHtml.java @@ -0,0 +1,32 @@ +package stirling.software.SPDF.controller.api.converters; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +import stirling.software.SPDF.model.api.PDFFile; +import stirling.software.SPDF.utils.PDFToFile; + +@RestController +@Tag(name = "Convert", description = "Convert APIs") +@RequestMapping("/api/v1/convert") +public class ConvertPDFToHtml { + + @PostMapping(consumes = "multipart/form-data", value = "/pdf/html") + @Operation( + summary = "Convert PDF to HTML", + description = + "This endpoint converts a PDF file to HTML format. Input:PDF Output:HTML Type:SISO") + public ResponseEntity processPdfToHTML(@ModelAttribute PDFFile request) + throws Exception { + MultipartFile inputFile = request.getFileInput(); + PDFToFile pdfToFile = new PDFToFile(); + return pdfToFile.processPdfToHtml(inputFile); + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java index 798c5f44..030ede95 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToOffice.java @@ -29,18 +29,6 @@ import stirling.software.SPDF.utils.WebResponseUtils; @Tag(name = "Convert", description = "Convert APIs") public class ConvertPDFToOffice { - @PostMapping(consumes = "multipart/form-data", value = "/pdf/html") - @Operation( - summary = "Convert PDF to HTML", - description = - "This endpoint converts a PDF file to HTML format. Input:PDF Output:HTML Type:SISO") - public ResponseEntity processPdfToHTML(@ModelAttribute PDFFile request) - throws Exception { - MultipartFile inputFile = request.getFileInput(); - PDFToFile pdfToFile = new PDFToFile(); - return pdfToFile.processPdfToOfficeFormat(inputFile, "html", "writer_pdf_import"); - } - @PostMapping(consumes = "multipart/form-data", value = "/pdf/presentation") @Operation( summary = "Convert PDF to Presentation format", diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java index 41498413..aec4e347 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertPDFToPDFA.java @@ -16,7 +16,7 @@ import io.github.pixee.security.Filenames; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; -import stirling.software.SPDF.model.api.PDFFile; +import stirling.software.SPDF.model.api.converters.PdfToPdfARequest; import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; import stirling.software.SPDF.utils.WebResponseUtils; @@ -31,8 +31,10 @@ public class ConvertPDFToPDFA { summary = "Convert a PDF to a PDF/A", description = "This endpoint converts a PDF file to a PDF/A file. PDF/A is a format designed for long-term archiving of digital documents. Input:PDF Output:PDF Type:SISO") - public ResponseEntity pdfToPdfA(@ModelAttribute PDFFile request) throws Exception { + public ResponseEntity pdfToPdfA(@ModelAttribute PdfToPdfARequest request) + throws Exception { MultipartFile inputFile = request.getFileInput(); + String outputFormat = request.getOutputFormat(); // Save the uploaded file to a temporary location Path tempInputFile = Files.createTempFile("input_", ".pdf"); @@ -47,7 +49,7 @@ public class ConvertPDFToPDFA { command.add("--skip-text"); command.add("--tesseract-timeout=0"); command.add("--output-type"); - command.add("pdfa"); + command.add(outputFormat.toString()); command.add(tempInputFile.toString()); command.add(tempOutputFile.toString()); diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java index c2d25973..815018e8 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ConvertWebsiteToPDF.java @@ -6,8 +6,6 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; @@ -28,10 +26,6 @@ import stirling.software.SPDF.utils.WebResponseUtils; @RequestMapping("/api/v1/convert") public class ConvertWebsiteToPDF { - @Autowired - @Qualifier("bookAndHtmlFormatsInstalled") - private boolean bookAndHtmlFormatsInstalled; - @PostMapping(consumes = "multipart/form-data", value = "/url/pdf") @Operation( summary = "Convert a URL to a PDF", @@ -53,11 +47,7 @@ public class ConvertWebsiteToPDF { // Prepare the OCRmyPDF command List command = new ArrayList<>(); - if (!bookAndHtmlFormatsInstalled) { - command.add("weasyprint"); - } else { - command.add("wkhtmltopdf"); - } + command.add("weasyprint"); command.add(URL); command.add(tempOutputFile.toString()); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java index 2dc6c8a3..f0fb7b02 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoRenameController.java @@ -130,7 +130,7 @@ public class AutoRenameController { // Sanitize the header string by removing characters not allowed in a filename. if (header != null && header.length() < 255) { - header = header.replaceAll("[/\\\\?%*:|\"<>]", ""); + header = header.replaceAll("[/\\\\?%*:|\"<>]", "").trim(); return WebResponseUtils.pdfDocToWebResponse(document, header + ".pdf"); } else { logger.info("File has no good title to be found"); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java index 32f39c04..0b578d8f 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/AutoSplitPdfController.java @@ -58,7 +58,7 @@ public class AutoSplitPdfController { PDDocument document = Loader.loadPDF(file.getBytes()); PDFRenderer pdfRenderer = new PDFRenderer(document); - + pdfRenderer.setSubsamplingAllowed(true); List splitDocuments = new ArrayList<>(); List splitDocumentsBoas = new ArrayList<>(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java index a813ba79..eaac72a0 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/BlankPageController.java @@ -59,7 +59,7 @@ public class BlankPageController { List pagesToKeepIndex = new ArrayList<>(); int pageIndex = 0; PDFRenderer pdfRenderer = new PDFRenderer(document); - + pdfRenderer.setSubsamplingAllowed(true); for (PDPage page : pages) { logger.info("checking page " + pageIndex); textStripper.setStartPage(pageIndex + 1); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java index 82a2c72f..9e3d6a99 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java @@ -2,9 +2,7 @@ package stirling.software.SPDF.controller.api.misc; import java.awt.Image; import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -75,194 +73,208 @@ public class CompressController { long inputFileSize = Files.size(tempInputFile); // Prepare the output file path - Path tempOutputFile = Files.createTempFile("output_", ".pdf"); - - // Determine initial optimization level based on expected size reduction, only if in - // autoMode - if (autoMode) { - double sizeReductionRatio = expectedOutputSize / (double) inputFileSize; - if (sizeReductionRatio > 0.7) { - optimizeLevel = 1; - } else if (sizeReductionRatio > 0.5) { - optimizeLevel = 2; - } else if (sizeReductionRatio > 0.35) { - optimizeLevel = 3; - } else { - optimizeLevel = 3; - } - } - - boolean sizeMet = false; - while (!sizeMet && optimizeLevel <= 4) { - // Prepare the Ghostscript command - List command = new ArrayList<>(); - command.add("gs"); - command.add("-sDEVICE=pdfwrite"); - command.add("-dCompatibilityLevel=1.4"); - - switch (optimizeLevel) { - case 1: - command.add("-dPDFSETTINGS=/prepress"); - break; - case 2: - command.add("-dPDFSETTINGS=/printer"); - break; - case 3: - command.add("-dPDFSETTINGS=/ebook"); - break; - case 4: - command.add("-dPDFSETTINGS=/screen"); - break; - default: - command.add("-dPDFSETTINGS=/default"); - } - - command.add("-dNOPAUSE"); - command.add("-dQUIET"); - command.add("-dBATCH"); - command.add("-sOutputFile=" + tempOutputFile.toString()); - command.add(tempInputFile.toString()); - - ProcessExecutorResult returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT) - .runCommandWithOutputHandling(command); - - // Check if file size is within expected size or not auto mode so instantly finish - long outputFileSize = Files.size(tempOutputFile); - if (outputFileSize <= expectedOutputSize || !autoMode) { - sizeMet = true; - } else { - // Increase optimization level for next iteration - optimizeLevel++; - if (autoMode && optimizeLevel > 3) { - System.out.println("Skipping level 4 due to bad results in auto mode"); - sizeMet = true; - } else if (optimizeLevel == 5) { + Path tempOutputFile = null; + byte[] pdfBytes; + try { + tempOutputFile = Files.createTempFile("output_", ".pdf"); + // Determine initial optimization level based on expected size reduction, only if in + // autoMode + if (autoMode) { + double sizeReductionRatio = expectedOutputSize / (double) inputFileSize; + if (sizeReductionRatio > 0.7) { + optimizeLevel = 1; + } else if (sizeReductionRatio > 0.5) { + optimizeLevel = 2; + } else if (sizeReductionRatio > 0.35) { + optimizeLevel = 3; } else { - System.out.println( - "Increasing ghostscript optimisation level to " + optimizeLevel); + optimizeLevel = 3; } } - } - if (expectedOutputSize != null && autoMode) { - long outputFileSize = Files.size(tempOutputFile); - if (outputFileSize > expectedOutputSize) { - try (PDDocument doc = Loader.loadPDF(new File(tempOutputFile.toString()))) { - long previousFileSize = 0; - double scaleFactor = 1.0; - while (true) { - for (PDPage page : doc.getPages()) { - PDResources res = page.getResources(); + boolean sizeMet = false; + while (!sizeMet && optimizeLevel <= 4) { + // Prepare the Ghostscript command + List command = new ArrayList<>(); + command.add("gs"); + command.add("-sDEVICE=pdfwrite"); + command.add("-dCompatibilityLevel=1.4"); - for (COSName name : res.getXObjectNames()) { - PDXObject xobj = res.getXObject(name); - if (xobj instanceof PDImageXObject) { - PDImageXObject image = (PDImageXObject) xobj; + switch (optimizeLevel) { + case 1: + command.add("-dPDFSETTINGS=/prepress"); + break; + case 2: + command.add("-dPDFSETTINGS=/printer"); + break; + case 3: + command.add("-dPDFSETTINGS=/ebook"); + break; + case 4: + command.add("-dPDFSETTINGS=/screen"); + break; + default: + command.add("-dPDFSETTINGS=/default"); + } - // Get the image in BufferedImage format - BufferedImage bufferedImage = image.getImage(); + command.add("-dNOPAUSE"); + command.add("-dQUIET"); + command.add("-dBATCH"); + command.add("-sOutputFile=" + tempOutputFile.toString()); + command.add(tempInputFile.toString()); - // Calculate the new dimensions - int newWidth = (int) (bufferedImage.getWidth() * scaleFactor); - int newHeight = (int) (bufferedImage.getHeight() * scaleFactor); + ProcessExecutorResult returnCode = + ProcessExecutor.getInstance(ProcessExecutor.Processes.GHOSTSCRIPT) + .runCommandWithOutputHandling(command); - // If the new dimensions are zero, skip this iteration - if (newWidth == 0 || newHeight == 0) { - continue; + // Check if file size is within expected size or not auto mode so instantly finish + long outputFileSize = Files.size(tempOutputFile); + if (outputFileSize <= expectedOutputSize || !autoMode) { + sizeMet = true; + } else { + // Increase optimization level for next iteration + optimizeLevel++; + if (autoMode && optimizeLevel > 4) { + System.out.println("Skipping level 5 due to bad results in auto mode"); + sizeMet = true; + } else { + System.out.println( + "Increasing ghostscript optimisation level to " + optimizeLevel); + } + } + } + + if (expectedOutputSize != null && autoMode) { + long outputFileSize = Files.size(tempOutputFile); + byte[] fileBytes = Files.readAllBytes(tempOutputFile); + if (outputFileSize > expectedOutputSize) { + try (PDDocument doc = Loader.loadPDF(fileBytes)) { + long previousFileSize = 0; + double scaleFactorConst = 0.9f; + double scaleFactor = 0.9f; + while (true) { + for (PDPage page : doc.getPages()) { + PDResources res = page.getResources(); + if (res != null && res.getXObjectNames() != null) { + for (COSName name : res.getXObjectNames()) { + PDXObject xobj = res.getXObject(name); + if (xobj != null && xobj instanceof PDImageXObject) { + PDImageXObject image = (PDImageXObject) xobj; + + // Get the image in BufferedImage format + BufferedImage bufferedImage = image.getImage(); + + // Calculate the new dimensions + int newWidth = + (int) + (bufferedImage.getWidth() + * scaleFactorConst); + int newHeight = + (int) + (bufferedImage.getHeight() + * scaleFactorConst); + + // If the new dimensions are zero, skip this iteration + if (newWidth == 0 || newHeight == 0) { + continue; + } + + // Otherwise, proceed with the scaling + Image scaledImage = + bufferedImage.getScaledInstance( + newWidth, + newHeight, + Image.SCALE_SMOOTH); + + // Convert the scaled image back to a BufferedImage + BufferedImage scaledBufferedImage = + new BufferedImage( + newWidth, + newHeight, + BufferedImage.TYPE_INT_RGB); + scaledBufferedImage + .getGraphics() + .drawImage(scaledImage, 0, 0, null); + + // Compress the scaled image + ByteArrayOutputStream compressedImageStream = + new ByteArrayOutputStream(); + ImageIO.write( + scaledBufferedImage, + "jpeg", + compressedImageStream); + byte[] imageBytes = compressedImageStream.toByteArray(); + compressedImageStream.close(); + + PDImageXObject compressedImage = + PDImageXObject.createFromByteArray( + doc, + imageBytes, + image.getCOSObject().toString()); + + // Replace the image in the resources with the + // compressed + // version + res.put(name, compressedImage); + } } - - // Otherwise, proceed with the scaling - Image scaledImage = - bufferedImage.getScaledInstance( - newWidth, newHeight, Image.SCALE_SMOOTH); - - // Convert the scaled image back to a BufferedImage - BufferedImage scaledBufferedImage = - new BufferedImage( - newWidth, - newHeight, - BufferedImage.TYPE_INT_RGB); - scaledBufferedImage - .getGraphics() - .drawImage(scaledImage, 0, 0, null); - - // Compress the scaled image - ByteArrayOutputStream compressedImageStream = - new ByteArrayOutputStream(); - ImageIO.write( - scaledBufferedImage, "jpeg", compressedImageStream); - byte[] imageBytes = compressedImageStream.toByteArray(); - compressedImageStream.close(); - - // Convert compressed image back to PDImageXObject - ByteArrayInputStream bais = - new ByteArrayInputStream(imageBytes); - PDImageXObject compressedImage = - PDImageXObject.createFromByteArray( - doc, - imageBytes, - image.getCOSObject().toString()); - - // Replace the image in the resources with the compressed - // version - res.put(name, compressedImage); } } - } - // save the document to tempOutputFile again - doc.save(tempOutputFile.toString()); + // save the document to tempOutputFile again + doc.save(tempOutputFile.toString()); - long currentSize = Files.size(tempOutputFile); - // Check if the overall PDF size is still larger than expectedOutputSize - if (currentSize > expectedOutputSize) { - // Log the current file size and scaleFactor + long currentSize = Files.size(tempOutputFile); + // Check if the overall PDF size is still larger than expectedOutputSize + if (currentSize > expectedOutputSize) { + // Log the current file size and scaleFactor - System.out.println( - "Current file size: " - + FileUtils.byteCountToDisplaySize(currentSize)); - System.out.println("Current scale factor: " + scaleFactor); + System.out.println( + "Current file size: " + + FileUtils.byteCountToDisplaySize(currentSize)); + System.out.println("Current scale factor: " + scaleFactor); - // The file is still too large, reduce scaleFactor and try again - scaleFactor *= 0.9; // reduce scaleFactor by 10% - // Avoid scaleFactor being too small, causing the image to shrink to 0 - if (scaleFactor < 0.2 || previousFileSize == currentSize) { - throw new RuntimeException( - "Could not reach the desired size without excessively degrading image quality, lowest size recommended is " - + FileUtils.byteCountToDisplaySize(currentSize) - + ", " - + currentSize - + " bytes"); + // The file is still too large, reduce scaleFactor and try again + scaleFactor *= 0.9f; // reduce scaleFactor by 10% + // Avoid scaleFactor being too small, causing the image to shrink to + // 0 + if (scaleFactor < 0.2f || previousFileSize == currentSize) { + throw new RuntimeException( + "Could not reach the desired size without excessively degrading image quality, lowest size recommended is " + + FileUtils.byteCountToDisplaySize(currentSize) + + ", " + + currentSize + + " bytes"); + } + previousFileSize = currentSize; + } else { + // The file is small enough, break the loop + break; } - previousFileSize = currentSize; - } else { - // The file is small enough, break the loop - break; } } } } + + // Read the optimized PDF file + pdfBytes = Files.readAllBytes(tempOutputFile); + + // Check if optimized file is larger than the original + if (pdfBytes.length > inputFileSize) { + // Log the occurrence + logger.warn( + "Optimized file is larger than the original. Returning the original file instead."); + + // Read the original file again + pdfBytes = Files.readAllBytes(tempInputFile); + } + } finally { + // Clean up the temporary files + Files.delete(tempInputFile); + Files.delete(tempOutputFile); } - // Read the optimized PDF file - byte[] pdfBytes = Files.readAllBytes(tempOutputFile); - - // Check if optimized file is larger than the original - if (pdfBytes.length > inputFileSize) { - // Log the occurrence - logger.warn( - "Optimized file is larger than the original. Returning the original file instead."); - - // Read the original file again - pdfBytes = Files.readAllBytes(tempInputFile); - } - - // Clean up the temporary files - Files.delete(tempInputFile); - Files.delete(tempOutputFile); - // Return the optimized PDF as a response String outputFilename = Filenames.toSimpleFileName(inputFile.getOriginalFilename()) diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java index da684280..9ed00dd8 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImageScansController.java @@ -9,7 +9,6 @@ import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -73,111 +72,151 @@ public class ExtractImageScansController { List images = new ArrayList<>(); - // Check if input file is a PDF - if ("pdf".equalsIgnoreCase(extension)) { - // Load PDF document - try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) { - PDFRenderer pdfRenderer = new PDFRenderer(document); - int pageCount = document.getNumberOfPages(); - images = new ArrayList<>(); + List tempImageFiles = new ArrayList<>(); + Path tempInputFile = null; + Path tempZipFile = null; + List tempDirs = new ArrayList<>(); - // Create images of all pages - for (int i = 0; i < pageCount; i++) { - // Create temp file to save the image - Path tempFile = Files.createTempFile("image_", ".png"); + try { + // Check if input file is a PDF + if ("pdf".equalsIgnoreCase(extension)) { + // Load PDF document + try (PDDocument document = Loader.loadPDF(form.getFileInput().getBytes())) { + PDFRenderer pdfRenderer = new PDFRenderer(document); + pdfRenderer.setSubsamplingAllowed(true); + int pageCount = document.getNumberOfPages(); + images = new ArrayList<>(); - // Render image and save as temp file - BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300); - ImageIO.write(image, "png", tempFile.toFile()); + // Create images of all pages + for (int i = 0; i < pageCount; i++) { + // Create temp file to save the image + Path tempFile = Files.createTempFile("image_", ".png"); - // Add temp file path to images list - images.add(tempFile.toString()); + // Render image and save as temp file + BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300); + ImageIO.write(image, "png", tempFile.toFile()); + + // Add temp file path to images list + images.add(tempFile.toString()); + tempImageFiles.add(tempFile); + } } + } else { + tempInputFile = Files.createTempFile("input_", "." + extension); + Files.copy( + form.getFileInput().getInputStream(), + tempInputFile, + StandardCopyOption.REPLACE_EXISTING); + // Add input file path to images list + images.add(tempInputFile.toString()); } - } else { - Path tempInputFile = Files.createTempFile("input_", "." + extension); - Files.copy( - form.getFileInput().getInputStream(), - tempInputFile, - StandardCopyOption.REPLACE_EXISTING); - // Add input file path to images list - images.add(tempInputFile.toString()); - } - List processedImageBytes = new ArrayList<>(); + List processedImageBytes = new ArrayList<>(); - // Process each image - for (int i = 0; i < images.size(); i++) { + // Process each image + for (int i = 0; i < images.size(); i++) { - Path tempDir = Files.createTempDirectory("openCV_output"); - List command = - new ArrayList<>( - Arrays.asList( - "python3", - "./scripts/split_photos.py", - images.get(i), - tempDir.toString(), - "--angle_threshold", - String.valueOf(form.getAngleThreshold()), - "--tolerance", - String.valueOf(form.getTolerance()), - "--min_area", - String.valueOf(form.getMinArea()), - "--min_contour_area", - String.valueOf(form.getMinContourArea()), - "--border_size", - String.valueOf(form.getBorderSize()))); + Path tempDir = Files.createTempDirectory("openCV_output"); + tempDirs.add(tempDir); + List command = + new ArrayList<>( + Arrays.asList( + "python3", + "./scripts/split_photos.py", + images.get(i), + tempDir.toString(), + "--angle_threshold", + String.valueOf(form.getAngleThreshold()), + "--tolerance", + String.valueOf(form.getTolerance()), + "--min_area", + String.valueOf(form.getMinArea()), + "--min_contour_area", + String.valueOf(form.getMinContourArea()), + "--border_size", + String.valueOf(form.getBorderSize()))); - // Run CLI command - ProcessExecutorResult returnCode = - ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV) - .runCommandWithOutputHandling(command); + // Run CLI command + ProcessExecutorResult returnCode = + ProcessExecutor.getInstance(ProcessExecutor.Processes.PYTHON_OPENCV) + .runCommandWithOutputHandling(command); - // Read the output photos in temp directory - List tempOutputFiles = Files.list(tempDir).sorted().collect(Collectors.toList()); - for (Path tempOutputFile : tempOutputFiles) { - byte[] imageBytes = Files.readAllBytes(tempOutputFile); - processedImageBytes.add(imageBytes); + // Read the output photos in temp directory + List tempOutputFiles = Files.list(tempDir).sorted().toList(); + for (Path tempOutputFile : tempOutputFiles) { + byte[] imageBytes = Files.readAllBytes(tempOutputFile); + processedImageBytes.add(imageBytes); + } + // Clean up the temporary directory + FileUtils.deleteDirectory(tempDir.toFile()); } - // Clean up the temporary directory - FileUtils.deleteDirectory(tempDir.toFile()); - } - // Create zip file if multiple images - if (processedImageBytes.size() > 1) { - String outputZipFilename = fileName.replaceFirst("[.][^.]+$", "") + "_processed.zip"; - Path tempZipFile = Files.createTempFile("output_", ".zip"); + // Create zip file if multiple images + if (processedImageBytes.size() > 1) { + String outputZipFilename = + fileName.replaceFirst(REPLACEFIRST, "") + "_processed.zip"; + tempZipFile = Files.createTempFile("output_", ".zip"); - try (ZipOutputStream zipOut = - new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) { - // Add processed images to the zip - for (int i = 0; i < processedImageBytes.size(); i++) { - ZipEntry entry = - new ZipEntry( - fileName.replaceFirst("[.][^.]+$", "") - + "_" - + (i + 1) - + ".png"); - zipOut.putNextEntry(entry); - zipOut.write(processedImageBytes.get(i)); - zipOut.closeEntry(); + try (ZipOutputStream zipOut = + new ZipOutputStream(new FileOutputStream(tempZipFile.toFile()))) { + // Add processed images to the zip + for (int i = 0; i < processedImageBytes.size(); i++) { + ZipEntry entry = + new ZipEntry( + fileName.replaceFirst(REPLACEFIRST, "") + + "_" + + (i + 1) + + ".png"); + zipOut.putNextEntry(entry); + zipOut.write(processedImageBytes.get(i)); + zipOut.closeEntry(); + } + } + + byte[] zipBytes = Files.readAllBytes(tempZipFile); + + // Clean up the temporary zip file + Files.delete(tempZipFile); + + return WebResponseUtils.bytesToWebResponse( + zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM); + } else { + // Return the processed image as a response + byte[] imageBytes = processedImageBytes.get(0); + return WebResponseUtils.bytesToWebResponse( + imageBytes, + fileName.replaceFirst(REPLACEFIRST, "") + ".png", + MediaType.IMAGE_PNG); + } + } finally { + // Cleanup logic for all temporary files and directories + tempImageFiles.forEach( + path -> { + try { + Files.deleteIfExists(path); + } catch (IOException e) { + logger.error("Failed to delete temporary image file: " + path, e); + } + }); + + if (tempZipFile != null && Files.exists(tempZipFile)) { + try { + Files.delete(tempZipFile); + } catch (IOException e) { + logger.error("Failed to delete temporary zip file: " + tempZipFile, e); } } - byte[] zipBytes = Files.readAllBytes(tempZipFile); - - // Clean up the temporary zip file - Files.delete(tempZipFile); - - return WebResponseUtils.bytesToWebResponse( - zipBytes, outputZipFilename, MediaType.APPLICATION_OCTET_STREAM); - } else { - // Return the processed image as a response - byte[] imageBytes = processedImageBytes.get(0); - return WebResponseUtils.bytesToWebResponse( - imageBytes, - fileName.replaceFirst("[.][^.]+$", "") + ".png", - MediaType.IMAGE_PNG); + tempDirs.forEach( + dir -> { + try { + FileUtils.deleteDirectory(dir.toFile()); + } catch (IOException e) { + logger.error("Failed to delete temporary directory: " + dir, e); + } + }); } } + + private static final String REPLACEFIRST = "[.][^.]+$"; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/FakeScanControllerWIP.java b/src/main/java/stirling/software/SPDF/controller/api/misc/FakeScanControllerWIP.java index 789bf17e..2400c0d3 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/FakeScanControllerWIP.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/FakeScanControllerWIP.java @@ -1,27 +1,29 @@ package stirling.software.SPDF.controller.api.misc; +import java.awt.AlphaComposite; +import java.awt.BasicStroke; import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.RenderingHints; import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; -import java.awt.image.RescaleOp; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.IOException; import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; import java.util.Random; -import javax.imageio.ImageIO; - import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDPage; -import org.apache.pdfbox.pdmodel.PDPageContentStream; -import org.apache.pdfbox.pdmodel.common.PDRectangle; -import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; +import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.PDFRenderer; @@ -29,6 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; @@ -39,6 +42,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import stirling.software.SPDF.model.api.PDFFile; +import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.WebResponseUtils; @RestController @@ -48,97 +52,39 @@ public class FakeScanControllerWIP { private static final Logger logger = LoggerFactory.getLogger(FakeScanControllerWIP.class); - // TODO + // TODO finish + @PostMapping(consumes = "multipart/form-data", value = "/fake-scan") @Hidden - // @PostMapping(consumes = "multipart/form-data", value = "/fakeScan") @Operation( summary = "Repair a PDF file", description = "This endpoint repairs a given PDF file by running Ghostscript command. The PDF is first saved to a temporary location, repaired, read back, and then returned as a response.") - public ResponseEntity repairPdf(@ModelAttribute PDFFile request) throws IOException { + public ResponseEntity fakeScan(@ModelAttribute PDFFile request) throws IOException { MultipartFile inputFile = request.getFileInput(); + // Load the PDF document PDDocument document = Loader.loadPDF(inputFile.getBytes()); - PDFRenderer pdfRenderer = new PDFRenderer(document); - for (int page = 0; page < document.getNumberOfPages(); ++page) { - BufferedImage image = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB); - ImageIO.write(image, "png", new File("scanned-" + (page + 1) + ".png")); + PDFRenderer renderer = new PDFRenderer(document); + List images = new ArrayList<>(); + // Convert each page to an image + for (int i = 0; i < document.getNumberOfPages(); i++) { + BufferedImage image = renderer.renderImageWithDPI(i, 150, ImageType.GRAY); + images.add(processImage(image)); } document.close(); - // Constants - int scannedness = 90; // Value between 0 and 100 - int dirtiness = 0; // Value between 0 and 100 - - // Load the source image - BufferedImage sourceImage = ImageIO.read(new File("scanned-1.png")); - - // Create the destination image - BufferedImage destinationImage = - new BufferedImage( - sourceImage.getWidth(), sourceImage.getHeight(), sourceImage.getType()); - - // Apply a brightness and contrast effect based on the "scanned-ness" - float scaleFactor = 1.0f + (scannedness / 100.0f) * 0.5f; // Between 1.0 and 1.5 - float offset = scannedness * 1.5f; // Between 0 and 150 - BufferedImageOp op = new RescaleOp(scaleFactor, offset, null); - op.filter(sourceImage, destinationImage); - - // Apply a rotation effect - double rotationRequired = - Math.toRadians( - (new SecureRandom().nextInt(3 - 1) - + 1)); // Random angle between 1 and 3 degrees - double locationX = destinationImage.getWidth() / 2; - double locationY = destinationImage.getHeight() / 2; - AffineTransform tx = - AffineTransform.getRotateInstance(rotationRequired, locationX, locationY); - AffineTransformOp rotateOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR); - destinationImage = rotateOp.filter(destinationImage, null); - - // Apply a blur effect based on the "scanned-ness" - float blurIntensity = scannedness / 100.0f * 0.2f; // Between 0.0 and 0.2 - float[] matrix = { - blurIntensity, blurIntensity, blurIntensity, - blurIntensity, blurIntensity, blurIntensity, - blurIntensity, blurIntensity, blurIntensity - }; - BufferedImageOp blurOp = - new ConvolveOp(new Kernel(3, 3, matrix), ConvolveOp.EDGE_NO_OP, null); - destinationImage = blurOp.filter(destinationImage, null); - - // Add noise to the image based on the "dirtiness" - Random random = new SecureRandom(); - for (int y = 0; y < destinationImage.getHeight(); y++) { - for (int x = 0; x < destinationImage.getWidth(); x++) { - if (random.nextInt(100) < dirtiness) { - // Change the pixel color to black randomly based on the "dirtiness" - destinationImage.setRGB(x, y, Color.BLACK.getRGB()); - } - } - } - - // Save the image - ImageIO.write(destinationImage, "PNG", new File("scanned-1.png")); - - PDDocument documentOut = new PDDocument(); - for (int page = 1; page <= document.getNumberOfPages(); ++page) { - BufferedImage bim = ImageIO.read(new File("scanned-" + page + ".png")); - - // Adjust the dimensions of the page - PDPage pdPage = new PDPage(new PDRectangle(bim.getWidth() - 1, bim.getHeight() - 1)); - documentOut.addPage(pdPage); - - PDImageXObject pdImage = LosslessFactory.createFromImage(documentOut, bim); - PDPageContentStream contentStream = new PDPageContentStream(documentOut, pdPage); - - // Draw the image with a slight offset and enlarged dimensions - contentStream.drawImage(pdImage, -1, -1, bim.getWidth() + 2, bim.getHeight() + 2); - contentStream.close(); - } + // Create a new PDF document with the processed images ByteArrayOutputStream baos = new ByteArrayOutputStream(); - documentOut.save(baos); - documentOut.close(); + PDDocument newDocument = new PDDocument(); + for (BufferedImage img : images) { + // PDPageContentStream contentStream = new PDPageContentStream(newDocument, new + // PDPage()); + PDImageXObject pdImage = JPEGFactory.createFromImage(newDocument, img); + PdfUtils.addImageToDocument(newDocument, pdImage, "maintainAspectRatio", false); + } + + newDocument.save(baos); + newDocument.close(); // Return the optimized PDF as a response String outputFilename = @@ -147,4 +93,232 @@ public class FakeScanControllerWIP { + "_scanned.pdf"; return WebResponseUtils.boasToWebResponse(baos, outputFilename); } + + public BufferedImage processImage(BufferedImage image) { + // Rotation + + image = softenEdges(image, 50); + image = rotate(image, 1); + + image = applyGaussianBlur(image, 0.5); + addGaussianNoise(image, 0.5); + image = linearStretch(image); + addDustAndHairs(image, 3); + return image; + } + + private BufferedImage rotate(BufferedImage image, double rotation) { + + double rotationRequired = Math.toRadians(rotation); + double locationX = image.getWidth() / 2; + double locationY = image.getHeight() / 2; + AffineTransform tx = + AffineTransform.getRotateInstance(rotationRequired, locationX, locationY); + AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BICUBIC); + return op.filter(image, null); + } + + private BufferedImage applyGaussianBlur(BufferedImage image, double sigma) { + int radius = 3; // Fixed radius size for simplicity + + int size = 2 * radius + 1; + float[] data = new float[size * size]; + double sum = 0.0; + + for (int i = -radius; i <= radius; i++) { + for (int j = -radius; j <= radius; j++) { + double xDistance = i * i; + double yDistance = j * j; + double g = Math.exp(-(xDistance + yDistance) / (2 * sigma * sigma)); + data[(i + radius) * size + j + radius] = (float) g; + sum += g; + } + } + + // Normalize the kernel + for (int i = 0; i < data.length; i++) { + data[i] /= sum; + } + + Kernel kernel = new Kernel(size, size, data); + BufferedImageOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null); + return op.filter(image, null); + } + + public BufferedImage softenEdges(BufferedImage image, int featherRadius) { + int width = image.getWidth(); + int height = image.getHeight(); + BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + + Graphics2D g2 = output.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2.setRenderingHint( + RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + + g2.drawImage(image, 0, 0, null); + g2.setComposite(AlphaComposite.DstIn); + + // Top edge + g2.setPaint( + new GradientPaint( + 0, + 0, + new Color(0, 0, 0, 1f), + 0, + featherRadius * 2, + new Color(0, 0, 0, 0f))); + g2.fillRect(0, 0, width, featherRadius); + + // Bottom edge + g2.setPaint( + new GradientPaint( + 0, + height - featherRadius * 2, + new Color(0, 0, 0, 0f), + 0, + height, + new Color(0, 0, 0, 1f))); + g2.fillRect(0, height - featherRadius, width, featherRadius); + + // Left edge + g2.setPaint( + new GradientPaint( + 0, + 0, + new Color(0, 0, 0, 1f), + featherRadius * 2, + 0, + new Color(0, 0, 0, 0f))); + g2.fillRect(0, 0, featherRadius, height); + + // Right edge + g2.setPaint( + new GradientPaint( + width - featherRadius * 2, + 0, + new Color(0, 0, 0, 0f), + width, + 0, + new Color(0, 0, 0, 1f))); + g2.fillRect(width - featherRadius, 0, featherRadius, height); + + g2.dispose(); + + return output; + } + + private void addDustAndHairs(BufferedImage image, float intensity) { + int width = image.getWidth(); + int height = image.getHeight(); + Graphics2D g2d = image.createGraphics(); + Random random = new SecureRandom(); + + // Set rendering hints for better quality + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // Calculate the number of artifacts based on intensity + int numSpots = (int) (intensity * 10); + int numHairs = (int) (intensity * 20); + + // Add spots with more variable sizes + g2d.setColor(new Color(100, 100, 100, 50)); // Semi-transparent gray + for (int i = 0; i < numSpots; i++) { + int x = random.nextInt(width); + int y = random.nextInt(height); + int ovalSize = 1 + random.nextInt(3); // Base size + variable component + if (random.nextFloat() > 0.9) { + // 10% chance to get a larger spot + ovalSize += random.nextInt(3); + } + g2d.fill(new Ellipse2D.Double(x, y, ovalSize, ovalSize)); + } + + // Add hairs + g2d.setStroke(new BasicStroke(0.5f)); // Thin stroke for hairs + g2d.setColor(new Color(80, 80, 80, 40)); // Slightly lighter and more transparent + for (int i = 0; i < numHairs; i++) { + int x1 = random.nextInt(width); + int y1 = random.nextInt(height); + int x2 = x1 + random.nextInt(20) - 10; // Random length and direction + int y2 = y1 + random.nextInt(20) - 10; + Path2D.Double hair = new Path2D.Double(); + hair.moveTo(x1, y1); + hair.curveTo(x1, y1, (x1 + x2) / 2, (y1 + y2) / 2, x2, y2); + g2d.draw(hair); + } + + g2d.dispose(); + } + + private void addGaussianNoise(BufferedImage image, double strength) { + Random rand = new SecureRandom(); + int width = image.getWidth(); + int height = image.getHeight(); + + for (int i = 0; i < width; i++) { + for (int j = 0; j < height; j++) { + int rgba = image.getRGB(i, j); + int alpha = (rgba >> 24) & 0xff; + int red = (rgba >> 16) & 0xff; + int green = (rgba >> 8) & 0xff; + int blue = rgba & 0xff; + + // Apply Gaussian noise + red = (int) (red + rand.nextGaussian() * strength); + green = (int) (green + rand.nextGaussian() * strength); + blue = (int) (blue + rand.nextGaussian() * strength); + + // Clamping values to the 0-255 range + red = Math.min(Math.max(0, red), 255); + green = Math.min(Math.max(0, green), 255); + blue = Math.min(Math.max(0, blue), 255); + + image.setRGB(i, j, (alpha << 24) | (red << 16) | (green << 8) | blue); + } + } + } + + public BufferedImage linearStretch(BufferedImage image) { + int width = image.getWidth(); + int height = image.getHeight(); + int min = 255; + int max = 0; + + // First pass: find the min and max grayscale values + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int rgb = image.getRGB(x, y); + int gray = + (int) + (((rgb >> 16) & 0xff) * 0.299 + + ((rgb >> 8) & 0xff) * 0.587 + + (rgb & 0xff) * 0.114); // Convert to grayscale + if (gray < min) min = gray; + if (gray > max) max = gray; + } + } + + // Second pass: stretch the histogram + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int rgb = image.getRGB(x, y); + int alpha = (rgb >> 24) & 0xff; + int red = (rgb >> 16) & 0xff; + int green = (rgb >> 8) & 0xff; + int blue = rgb & 0xff; + + // Apply linear stretch to each channel + red = (int) (((red - min) / (float) (max - min)) * 255); + green = (int) (((green - min) / (float) (max - min)) * 255); + blue = (int) (((blue - min) / (float) (max - min)) * 255); + + // Set new RGB value maintaining the alpha channel + rgb = (alpha << 24) | (red << 16) | (green << 8) | blue; + image.setRGB(x, y, rgb); + } + } + + return image; + } } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java new file mode 100644 index 00000000..bdc0a478 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java @@ -0,0 +1,84 @@ +package stirling.software.SPDF.controller.api.misc; + +import java.awt.image.BufferedImage; +import java.io.IOException; + +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.graphics.image.JPEGFactory; +import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; +import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; +import org.apache.pdfbox.rendering.ImageType; +import org.apache.pdfbox.rendering.PDFRenderer; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import io.github.pixee.security.Filenames; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +import stirling.software.SPDF.model.PdfMetadata; +import stirling.software.SPDF.model.api.misc.FlattenRequest; +import stirling.software.SPDF.utils.PdfUtils; +import stirling.software.SPDF.utils.WebResponseUtils; + +@RestController +@RequestMapping("/api/v1/misc") +@Tag(name = "Misc", description = "Miscellaneous APIs") +public class FlattenController { + + @PostMapping(consumes = "multipart/form-data", value = "/flatten") + @Operation( + summary = "Flatten PDF form fields or full page", + description = + "Flattening just PDF form fields or converting each page to images to make text unselectable. Input: PDF, Output: PDF. Type: SISO") + public ResponseEntity flatten(@ModelAttribute FlattenRequest request) throws Exception { + MultipartFile file = request.getFileInput(); + + PDDocument document = Loader.loadPDF(file.getBytes()); + PdfMetadata metadata = PdfUtils.extractMetadataFromPdf(document); + Boolean flattenOnlyForms = request.getFlattenOnlyForms(); + + if (Boolean.TRUE.equals(flattenOnlyForms)) { + PDAcroForm acroForm = document.getDocumentCatalog().getAcroForm(); + if (acroForm != null) { + acroForm.flatten(); + } + return WebResponseUtils.pdfDocToWebResponse( + document, Filenames.toSimpleFileName(file.getOriginalFilename())); + } else { + // flatten whole page aka convert each page to image and readd it (making text + // unselectable) + PDFRenderer pdfRenderer = new PDFRenderer(document); + PDDocument newDocument = new PDDocument(); + int numPages = document.getNumberOfPages(); + for (int i = 0; i < numPages; i++) { + try { + BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300, ImageType.RGB); + PDPage page = new PDPage(); + page.setMediaBox(document.getPage(i).getMediaBox()); + newDocument.addPage(page); + try (PDPageContentStream contentStream = + new PDPageContentStream(newDocument, page)) { + PDImageXObject pdImage = JPEGFactory.createFromImage(newDocument, image); + float pageWidth = page.getMediaBox().getWidth(); + float pageHeight = page.getMediaBox().getHeight(); + + contentStream.drawImage(pdImage, 0, 0, pageWidth, pageHeight); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + PdfUtils.setMetadataToPdf(newDocument, metadata); + return WebResponseUtils.pdfDocToWebResponse( + newDocument, Filenames.toSimpleFileName(file.getOriginalFilename())); + } + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java index 542e3c6d..07947587 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/OCRController.java @@ -41,7 +41,7 @@ public class OCRController { private static final Logger logger = LoggerFactory.getLogger(OCRController.class); public List getAvailableTesseractLanguages() { - String tessdataDir = "/usr/share/tesseract-ocr/5/tessdata"; + String tessdataDir = "/usr/share/tessdata"; File[] files = new File(tessdataDir).listFiles(); if (files == null) { return Collections.emptyList(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/PrintFileController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/PrintFileController.java new file mode 100644 index 00000000..bc0a6715 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/PrintFileController.java @@ -0,0 +1,105 @@ +package stirling.software.SPDF.controller.api.misc; + +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; +import java.io.IOException; +import java.util.Arrays; + +import javax.imageio.ImageIO; +import javax.print.PrintService; +import javax.print.PrintServiceLookup; + +import org.apache.pdfbox.Loader; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.printing.PDFPageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import io.swagger.v3.oas.annotations.tags.Tag; + +import stirling.software.SPDF.model.api.misc.PrintFileRequest; + +@RestController +@RequestMapping("/api/v1/misc") +@Tag(name = "Misc", description = "Miscellaneous APIs") +public class PrintFileController { + + // TODO + // @PostMapping(value = "/print-file", consumes = "multipart/form-data") + // @Operation( + // summary = "Prints PDF/Image file to a set printer", + // description = + // "Input of PDF or Image along with a printer name/URL/IP to match against to + // send it to (Fire and forget) Input:Any Output:N/A Type:SISO") + public ResponseEntity printFile(@ModelAttribute PrintFileRequest request) + throws IOException { + MultipartFile file = request.getFileInput(); + String printerName = request.getPrinterName(); + String contentType = file.getContentType(); + try { + // Find matching printer + PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null); + PrintService selectedService = + Arrays.stream(services) + .filter( + service -> + service.getName().toLowerCase().contains(printerName)) + .findFirst() + .orElseThrow( + () -> + new IllegalArgumentException( + "No matching printer found")); + + System.out.println("Selected Printer: " + selectedService.getName()); + + if ("application/pdf".equals(contentType)) { + PDDocument document = Loader.loadPDF(file.getBytes()); + PrinterJob job = PrinterJob.getPrinterJob(); + job.setPrintService(selectedService); + job.setPageable(new PDFPageable(document)); + job.print(); + document.close(); + } else if (contentType.startsWith("image/")) { + BufferedImage image = ImageIO.read(file.getInputStream()); + PrinterJob job = PrinterJob.getPrinterJob(); + job.setPrintService(selectedService); + job.setPrintable( + new Printable() { + public int print( + Graphics graphics, PageFormat pageFormat, int pageIndex) + throws PrinterException { + if (pageIndex != 0) { + return NO_SUCH_PAGE; + } + Graphics2D g2d = (Graphics2D) graphics; + g2d.translate( + pageFormat.getImageableX(), pageFormat.getImageableY()); + g2d.drawImage( + image, + 0, + 0, + (int) pageFormat.getImageableWidth(), + (int) pageFormat.getImageableHeight(), + null); + return PAGE_EXISTS; + } + }); + job.print(); + } + return new ResponseEntity<>( + "File printed successfully to " + selectedService.getName(), HttpStatus.OK); + } catch (Exception e) { + System.err.println("Failed to print: " + e.getMessage()); + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java index 3b541b6c..e5d326a9 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/StampController.java @@ -88,7 +88,7 @@ public class StampController { // Load the input PDF PDDocument document = Loader.loadPDF(pdfFile.getBytes()); - List pageNumbers = request.getPageNumbersList(document, false); + List pageNumbers = request.getPageNumbersList(document, true); for (int pageIndex : pageNumbers) { int zeroBasedIndex = pageIndex - 1; diff --git a/src/main/java/stirling/software/SPDF/controller/api/pipeline/ApiDocService.java b/src/main/java/stirling/software/SPDF/controller/api/pipeline/ApiDocService.java index 02c37e5c..c773c976 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/pipeline/ApiDocService.java +++ b/src/main/java/stirling/software/SPDF/controller/api/pipeline/ApiDocService.java @@ -36,7 +36,7 @@ public class ApiDocService { private String getApiDocsUrl() { String contextPath = servletContext.getContextPath(); - String port = SPdfApplication.getPort(); + String port = SPdfApplication.getStaticPort(); return "http://localhost:" + port + contextPath + "/v1/api-docs"; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineController.java b/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineController.java index 0a53daf0..dfa45096 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineController.java @@ -12,7 +12,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ModelAttribute; @@ -50,9 +49,6 @@ public class PipelineController { @PostMapping("/handleData") public ResponseEntity handleData(@ModelAttribute HandleDataRequest request) throws JsonMappingException, JsonProcessingException { - if (!Boolean.TRUE.equals(applicationProperties.getSystem().getEnableAlphaFunctionality())) { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } MultipartFile[] files = request.getFileInput(); String jsonString = request.getJson(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineDirectoryProcessor.java b/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineDirectoryProcessor.java index 80fdd71c..c61b29e9 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineDirectoryProcessor.java +++ b/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineDirectoryProcessor.java @@ -26,7 +26,6 @@ import org.springframework.stereotype.Service; import com.fasterxml.jackson.databind.ObjectMapper; -import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.PipelineConfig; import stirling.software.SPDF.model.PipelineOperation; @@ -36,7 +35,6 @@ public class PipelineDirectoryProcessor { private static final Logger logger = LoggerFactory.getLogger(PipelineDirectoryProcessor.class); @Autowired private ObjectMapper objectMapper; @Autowired private ApiDocService apiDocService; - @Autowired private ApplicationProperties applicationProperties; final String watchedFoldersDir = "./pipeline/watchedFolders/"; final String finishedFoldersDir = "./pipeline/finishedFolders/"; @@ -45,9 +43,6 @@ public class PipelineDirectoryProcessor { @Scheduled(fixedRate = 60000) public void scanFolders() { - if (!Boolean.TRUE.equals(applicationProperties.getSystem().getEnableAlphaFunctionality())) { - return; - } Path watchedFolderPath = Paths.get(watchedFoldersDir); if (!Files.exists(watchedFolderPath)) { try { diff --git a/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java b/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java index c327cdd8..515dd4fe 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java +++ b/src/main/java/stirling/software/SPDF/controller/api/pipeline/PipelineProcessor.java @@ -64,7 +64,7 @@ public class PipelineProcessor { private String getBaseUrl() { String contextPath = servletContext.getContextPath(); - String port = SPdfApplication.getPort(); + String port = SPdfApplication.getStaticPort(); return "http://localhost:" + port + contextPath + "/"; } diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java b/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java index 0f482647..8a2a67c0 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/RedactController.java @@ -83,6 +83,7 @@ public class RedactController { if (convertPDFToImage) { PDDocument imageDocument = new PDDocument(); PDFRenderer pdfRenderer = new PDFRenderer(document); + pdfRenderer.setSubsamplingAllowed(true); for (int page = 0; page < document.getNumberOfPages(); ++page) { BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB); PDPage newPage = new PDPage(new PDRectangle(bim.getWidth(), bim.getHeight())); diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java b/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java index 6dfbf044..dd2c79da 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/SanitizeController.java @@ -54,33 +54,32 @@ public class SanitizeController { boolean removeLinks = request.isRemoveLinks(); boolean removeFonts = request.isRemoveFonts(); - try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) { - if (removeJavaScript) { - sanitizeJavaScript(document); - } - - if (removeEmbeddedFiles) { - sanitizeEmbeddedFiles(document); - } - - if (removeMetadata) { - sanitizeMetadata(document); - } - - if (removeLinks) { - sanitizeLinks(document); - } - - if (removeFonts) { - sanitizeFonts(document); - } - - return WebResponseUtils.pdfDocToWebResponse( - document, - Filenames.toSimpleFileName(inputFile.getOriginalFilename()) - .replaceFirst("[.][^.]+$", "") - + "_sanitized.pdf"); + PDDocument document = Loader.loadPDF(inputFile.getBytes()); + if (removeJavaScript) { + sanitizeJavaScript(document); } + + if (removeEmbeddedFiles) { + sanitizeEmbeddedFiles(document); + } + + if (removeMetadata) { + sanitizeMetadata(document); + } + + if (removeLinks) { + sanitizeLinks(document); + } + + if (removeFonts) { + sanitizeFonts(document); + } + + return WebResponseUtils.pdfDocToWebResponse( + document, + Filenames.toSimpleFileName(inputFile.getOriginalFilename()) + .replaceFirst("[.][^.]+$", "") + + "_sanitized.pdf"); } private void sanitizeJavaScript(PDDocument document) throws IOException { @@ -140,25 +139,29 @@ public class SanitizeController { for (PDPage page : allPages) { PDResources res = page.getResources(); - - // Remove embedded files from the PDF - res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles")); + if (res != null && res.getCOSObject() != null) { + res.getCOSObject().removeItem(COSName.getPDFName("EmbeddedFiles")); + } } } private void sanitizeMetadata(PDDocument document) { - PDMetadata metadata = document.getDocumentCatalog().getMetadata(); - if (metadata != null) { - document.getDocumentCatalog().setMetadata(null); + if (document.getDocumentCatalog() != null) { + PDMetadata metadata = document.getDocumentCatalog().getMetadata(); + if (metadata != null) { + document.getDocumentCatalog().setMetadata(null); + } } } private void sanitizeLinks(PDDocument document) throws IOException { for (PDPage page : document.getPages()) { for (PDAnnotation annotation : page.getAnnotations()) { - if (annotation instanceof PDAnnotationLink) { + if (annotation != null && annotation instanceof PDAnnotationLink) { PDAction action = ((PDAnnotationLink) annotation).getAction(); - if (action instanceof PDActionLaunch || action instanceof PDActionURI) { + if (action != null + && (action instanceof PDActionLaunch + || action instanceof PDActionURI)) { ((PDAnnotationLink) annotation).setAction(null); } } @@ -168,7 +171,11 @@ public class SanitizeController { private void sanitizeFonts(PDDocument document) { for (PDPage page : document.getPages()) { - page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font")); + if (page != null + && page.getResources() != null + && page.getResources().getCOSObject() != null) { + page.getResources().getCOSObject().removeItem(COSName.getPDFName("Font")); + } } } } diff --git a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java index 614dd8a0..bc43bd4b 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/AccountWebController.java @@ -2,12 +2,14 @@ package stirling.software.SPDF.controller.web; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -18,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletRequest; +import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.Authority; import stirling.software.SPDF.model.Role; import stirling.software.SPDF.model.User; @@ -27,12 +30,19 @@ import stirling.software.SPDF.repository.UserRepository; @Tag(name = "Account Security", description = "Account Security APIs") public class AccountWebController { + @Autowired ApplicationProperties applicationProperties; + @GetMapping("/login") public String login(HttpServletRequest request, Model model, Authentication authentication) { if (authentication != null && authentication.isAuthenticated()) { return "redirect:/"; } + model.addAttribute( + "oAuth2Enabled", applicationProperties.getSecurity().getOAUTH2().getEnabled()); + + model.addAttribute("currentPage", "login"); + if (request.getParameter("error") != null) { model.addAttribute("error", request.getParameter("error")); @@ -53,6 +63,7 @@ public class AccountWebController { public String showAddUserForm(Model model, Authentication authentication) { List allUsers = userRepository.findAll(); Iterator iterator = allUsers.iterator(); + Map roleDetails = Role.getAllRoleDetails(); while (iterator.hasNext()) { User user = iterator.next(); @@ -60,6 +71,7 @@ public class AccountWebController { for (Authority authority : user.getAuthorities()) { if (authority.getAuthority().equals(Role.INTERNAL_API_USER.getRoleId())) { iterator.remove(); + roleDetails.remove(Role.INTERNAL_API_USER.getRoleId()); break; // Break out of the inner loop once the user is removed } } @@ -68,6 +80,7 @@ public class AccountWebController { model.addAttribute("users", allUsers); model.addAttribute("currentUsername", authentication.getName()); + model.addAttribute("roleDetails", roleDetails); return "addUsers"; } @@ -79,17 +92,32 @@ public class AccountWebController { } if (authentication != null && authentication.isAuthenticated()) { Object principal = authentication.getPrincipal(); + String username = null; if (principal instanceof UserDetails) { // Cast the principal object to UserDetails UserDetails userDetails = (UserDetails) principal; // Retrieve username and other attributes - String username = userDetails.getUsername(); + username = userDetails.getUsername(); + // Add oAuth2 Login attributes to the model + model.addAttribute("oAuth2Login", false); + } + if (principal instanceof OAuth2User) { + // Cast the principal object to OAuth2User + OAuth2User userDetails = (OAuth2User) principal; + + // Retrieve username and other attributes + username = userDetails.getAttribute("email"); + + // Add oAuth2 Login attributes to the model + model.addAttribute("oAuth2Login", true); + } + if (username != null) { // Fetch user details from the database Optional user = - userRepository.findByUsername( + userRepository.findByUsernameIgnoreCase( username); // Assuming findByUsername method exists if (!user.isPresent()) { // Handle error appropriately @@ -112,6 +140,7 @@ public class AccountWebController { model.addAttribute("role", user.get().getRolesAsString()); model.addAttribute("settings", settingsJson); model.addAttribute("changeCredsFlag", user.get().isFirstLogin()); + model.addAttribute("currentPage", "account"); } } else { return "redirect:/"; @@ -138,7 +167,7 @@ public class AccountWebController { // Fetch user details from the database Optional user = - userRepository.findByUsername( + userRepository.findByUsernameIgnoreCase( username); // Assuming findByUsername method exists if (!user.isPresent()) { // Handle error appropriately diff --git a/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java b/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java index a7c10908..0e1fdf55 100644 --- a/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java +++ b/src/main/java/stirling/software/SPDF/controller/web/OtherWebController.java @@ -17,6 +17,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; @Controller @Tag(name = "Misc", description = "Miscellaneous APIs") public class OtherWebController { + @GetMapping("/compress-pdf") @Hidden public String compressPdfForm(Model model) { @@ -53,6 +54,13 @@ public class OtherWebController { return "misc/add-page-numbers"; } + @GetMapping("/fake-scan") + @Hidden + public String fakeScanForm(Model model) { + model.addAttribute("currentPage", "fake-scan"); + return "misc/fake-scan"; + } + @GetMapping("/extract-images") @Hidden public String extractImagesForm(Model model) { @@ -81,8 +89,15 @@ public class OtherWebController { return "misc/compare"; } + @GetMapping("/print-file") + @Hidden + public String printFileForm(Model model) { + model.addAttribute("currentPage", "print-file"); + return "misc/print-file"; + } + public List getAvailableTesseractLanguages() { - String tessdataDir = "/usr/share/tesseract-ocr/5/tessdata"; + String tessdataDir = "/usr/share/tessdata"; File[] files = new File(tessdataDir).listFiles(); if (files == null) { return Collections.emptyList(); diff --git a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java index 34c60bbe..ce109299 100644 --- a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java +++ b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java @@ -118,6 +118,7 @@ public class ApplicationProperties { private Boolean enableLogin; private Boolean csrfDisabled; private InitialLogin initialLogin; + private OAUTH2 oauth2; private int loginAttemptCount; private long loginResetTimeMinutes; @@ -145,6 +146,14 @@ public class ApplicationProperties { this.initialLogin = initialLogin; } + public OAUTH2 getOAUTH2() { + return oauth2 != null ? oauth2 : new OAUTH2(); + } + + public void setOAUTH2(OAUTH2 oauth2) { + this.oauth2 = oauth2; + } + public Boolean getEnableLogin() { return enableLogin; } @@ -165,6 +174,8 @@ public class ApplicationProperties { public String toString() { return "Security [enableLogin=" + enableLogin + + ", oauth2=" + + oauth2 + ", initialLogin=" + initialLogin + ", csrfDisabled=" @@ -202,6 +213,70 @@ public class ApplicationProperties { + "]"; } } + + public static class OAUTH2 { + + private boolean enabled; + private String issuer; + private String clientId; + private String clientSecret; + private boolean autoCreateUser; + + public boolean getEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getIssuer() { + return issuer; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public boolean getAutoCreateUser() { + return autoCreateUser; + } + + public void setAutoCreateUser(boolean autoCreateUser) { + this.autoCreateUser = autoCreateUser; + } + + @Override + public String toString() { + return "OAUTH2 [enabled=" + + enabled + + ", issuer=" + + issuer + + ", clientId=" + + clientId + + ", clientSecret=" + + (clientSecret != null && !clientSecret.isEmpty() ? "MASKED" : "NULL") + + ", autoCreateUser=" + + autoCreateUser + + "]"; + } + } } public static class System { @@ -210,7 +285,33 @@ public class ApplicationProperties { private String rootURIPath; private String customStaticFilePath; private Integer maxFileSize; - private CustomApplications customApplications; + private boolean showUpdate; + private Boolean showUpdateOnlyAdmin; + private boolean customHTMLFiles; + + public boolean isCustomHTMLFiles() { + return customHTMLFiles; + } + + public void setCustomHTMLFiles(boolean customHTMLFiles) { + this.customHTMLFiles = customHTMLFiles; + } + + public boolean getShowUpdateOnlyAdmin() { + return showUpdateOnlyAdmin; + } + + public void setShowUpdateOnlyAdmin(boolean showUpdateOnlyAdmin) { + this.showUpdateOnlyAdmin = showUpdateOnlyAdmin; + } + + public boolean getShowUpdate() { + return showUpdate; + } + + public void setShowUpdate(boolean showUpdate) { + this.showUpdate = showUpdate; + } private Boolean enableAlphaFunctionality; @@ -262,14 +363,6 @@ public class ApplicationProperties { this.maxFileSize = maxFileSize; } - public CustomApplications getCustomApplications() { - return customApplications != null ? customApplications : new CustomApplications(); - } - - public void setCustomApplications(CustomApplications customApplications) { - this.customApplications = customApplications; - } - @Override public String toString() { return "System [defaultLocale=" @@ -282,31 +375,14 @@ public class ApplicationProperties { + customStaticFilePath + ", maxFileSize=" + maxFileSize - + ", customApplications=" - + customApplications + ", enableAlphaFunctionality=" + enableAlphaFunctionality + + ", showUpdate=" + + showUpdate + + ", showUpdateOnlyAdmin=" + + showUpdateOnlyAdmin + "]"; } - - public static class CustomApplications { - private boolean installBookAndHtmlFormats; - - public boolean isInstallBookAndHtmlFormats() { - return installBookAndHtmlFormats; - } - - public void setInstallBookAndHtmlFormats(boolean installBookAndHtmlFormats) { - this.installBookAndHtmlFormats = installBookAndHtmlFormats; - } - - @Override - public String toString() { - return "CustomApplications [installBookAndHtmlFormats=" - + installBookAndHtmlFormats - + "]"; - } - } } public static class Ui { diff --git a/src/main/java/stirling/software/SPDF/model/PdfMetadata.java b/src/main/java/stirling/software/SPDF/model/PdfMetadata.java new file mode 100644 index 00000000..35054bc8 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/model/PdfMetadata.java @@ -0,0 +1,19 @@ +package stirling.software.SPDF.model; + +import java.util.Calendar; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class PdfMetadata { + private String author; + private String producer; + private String title; + private String creator; + private String subject; + private String keywords; + private Calendar creationDate; + private Calendar modificationDate; +} diff --git a/src/main/java/stirling/software/SPDF/model/Role.java b/src/main/java/stirling/software/SPDF/model/Role.java index 5100e9dd..02e7dd5f 100644 --- a/src/main/java/stirling/software/SPDF/model/Role.java +++ b/src/main/java/stirling/software/SPDF/model/Role.java @@ -1,34 +1,43 @@ package stirling.software.SPDF.model; +import java.util.LinkedHashMap; +import java.util.Map; + public enum Role { // Unlimited access - ADMIN("ROLE_ADMIN", Integer.MAX_VALUE, Integer.MAX_VALUE), + ADMIN("ROLE_ADMIN", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.admin"), // Unlimited access - USER("ROLE_USER", Integer.MAX_VALUE, Integer.MAX_VALUE), + USER("ROLE_USER", Integer.MAX_VALUE, Integer.MAX_VALUE, "adminUserSettings.user"), // 40 API calls Per Day, 40 web calls - LIMITED_API_USER("ROLE_LIMITED_API_USER", 40, 40), + LIMITED_API_USER("ROLE_LIMITED_API_USER", 40, 40, "adminUserSettings.apiUser"), // 20 API calls Per Day, 20 web calls - EXTRA_LIMITED_API_USER("ROLE_EXTRA_LIMITED_API_USER", 20, 20), + EXTRA_LIMITED_API_USER("ROLE_EXTRA_LIMITED_API_USER", 20, 20, "adminUserSettings.extraApiUser"), // 0 API calls per day and 20 web calls - WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20), + WEB_ONLY_USER("ROLE_WEB_ONLY_USER", 0, 20, "adminUserSettings.webOnlyUser"), - INTERNAL_API_USER("STIRLING-PDF-BACKEND-API-USER", Integer.MAX_VALUE, Integer.MAX_VALUE), + INTERNAL_API_USER( + "STIRLING-PDF-BACKEND-API-USER", + Integer.MAX_VALUE, + Integer.MAX_VALUE, + "adminUserSettings.internalApiUser"), - DEMO_USER("ROLE_DEMO_USER", 100, 100); + DEMO_USER("ROLE_DEMO_USER", 100, 100, "adminUserSettings.demoUser"); private final String roleId; private final int apiCallsPerDay; private final int webCallsPerDay; + private final String roleName; - Role(String roleId, int apiCallsPerDay, int webCallsPerDay) { + Role(String roleId, int apiCallsPerDay, int webCallsPerDay, String roleName) { this.roleId = roleId; this.apiCallsPerDay = apiCallsPerDay; this.webCallsPerDay = webCallsPerDay; + this.roleName = roleName; } public String getRoleId() { @@ -43,6 +52,27 @@ public enum Role { return webCallsPerDay; } + public String getRoleName() { + return roleName; + } + + public static String getRoleNameByRoleId(String roleId) { + // Using the fromString method to get the Role enum based on the roleId + Role role = fromString(roleId); + // Return the roleName of the found Role enum + return role.getRoleName(); + } + + // Method to retrieve all role IDs and role names + public static Map getAllRoleDetails() { + // Using LinkedHashMap to preserve order + Map roleDetails = new LinkedHashMap<>(); + for (Role role : Role.values()) { + roleDetails.put(role.getRoleId(), role.getRoleName()); + } + return roleDetails; + } + public static Role fromString(String roleId) { for (Role role : Role.values()) { if (role.getRoleId().equalsIgnoreCase(roleId)) { diff --git a/src/main/java/stirling/software/SPDF/model/User.java b/src/main/java/stirling/software/SPDF/model/User.java index 253b33da..f6f9e532 100644 --- a/src/main/java/stirling/software/SPDF/model/User.java +++ b/src/main/java/stirling/software/SPDF/model/User.java @@ -44,6 +44,9 @@ public class User { @Column(name = "isFirstLogin") private Boolean isFirstLogin = false; + @Column(name = "roleName") + private String roleName; + @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user") private Set authorities = new HashSet<>(); @@ -53,6 +56,10 @@ public class User { @CollectionTable(name = "user_settings", joinColumns = @JoinColumn(name = "user_id")) private Map settings = new HashMap<>(); // Key-value pairs of settings. + public String getRoleName() { + return Role.getRoleNameByRoleId(getRolesAsString()); + } + public boolean isFirstLogin() { return isFirstLogin != null && isFirstLogin; } diff --git a/src/main/java/stirling/software/SPDF/model/api/PDFWithPageNums.java b/src/main/java/stirling/software/SPDF/model/api/PDFWithPageNums.java index 5e15d64d..7f94791e 100644 --- a/src/main/java/stirling/software/SPDF/model/api/PDFWithPageNums.java +++ b/src/main/java/stirling/software/SPDF/model/api/PDFWithPageNums.java @@ -33,13 +33,13 @@ public class PDFWithPageNums extends PDFFile { // TODO Auto-generated catch block e.printStackTrace(); } - return GeneralUtils.parsePageString(pageNumbers, pageCount, zeroCount); + return GeneralUtils.parsePageList(pageNumbers, pageCount, zeroCount); } @Hidden public List getPageNumbersList(PDDocument doc, boolean zeroCount) { int pageCount = 0; pageCount = doc.getNumberOfPages(); - return GeneralUtils.parsePageString(pageNumbers, pageCount, zeroCount); + return GeneralUtils.parsePageList(pageNumbers, pageCount, zeroCount); } } diff --git a/src/main/java/stirling/software/SPDF/model/api/SplitPdfBySectionsRequest.java b/src/main/java/stirling/software/SPDF/model/api/SplitPdfBySectionsRequest.java index fcc5d5ba..1ff834b4 100644 --- a/src/main/java/stirling/software/SPDF/model/api/SplitPdfBySectionsRequest.java +++ b/src/main/java/stirling/software/SPDF/model/api/SplitPdfBySectionsRequest.java @@ -15,4 +15,7 @@ public class SplitPdfBySectionsRequest extends PDFFile { @Schema(description = "Number of vertical divisions for each PDF page", example = "2") private int verticalDivisions; + + @Schema(description = "Merge the split documents into a single PDF", example = "true") + private boolean merge; } diff --git a/src/main/java/stirling/software/SPDF/model/api/converters/PdfToPdfARequest.java b/src/main/java/stirling/software/SPDF/model/api/converters/PdfToPdfARequest.java new file mode 100644 index 00000000..59535314 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/model/api/converters/PdfToPdfARequest.java @@ -0,0 +1,17 @@ +package stirling.software.SPDF.model.api.converters; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import stirling.software.SPDF.model.api.PDFFile; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PdfToPdfARequest extends PDFFile { + + @Schema( + description = "The output PDF/A type", + allowableValues = {"pdfa", "pdfa-1"}) + private String outputFormat; +} diff --git a/src/main/java/stirling/software/SPDF/model/api/misc/FlattenRequest.java b/src/main/java/stirling/software/SPDF/model/api/misc/FlattenRequest.java new file mode 100644 index 00000000..c87d1974 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/model/api/misc/FlattenRequest.java @@ -0,0 +1,17 @@ +package stirling.software.SPDF.model.api.misc; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import stirling.software.SPDF.model.api.PDFFile; + +@Data +@EqualsAndHashCode(callSuper = true) +public class FlattenRequest extends PDFFile { + + @Schema( + description = + "True to flatten only the forms, false to flatten full PDF (Convert page to image)") + private Boolean flattenOnlyForms; +} diff --git a/src/main/java/stirling/software/SPDF/model/api/misc/PrintFileRequest.java b/src/main/java/stirling/software/SPDF/model/api/misc/PrintFileRequest.java new file mode 100644 index 00000000..d91c7a9e --- /dev/null +++ b/src/main/java/stirling/software/SPDF/model/api/misc/PrintFileRequest.java @@ -0,0 +1,15 @@ +package stirling.software.SPDF.model.api.misc; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import stirling.software.SPDF.model.api.PDFFile; + +@Data +@EqualsAndHashCode(callSuper = true) +public class PrintFileRequest extends PDFFile { + + @Schema(description = "Name of printer to match against", required = true) + private String printerName; +} diff --git a/src/main/java/stirling/software/SPDF/pdf/TextFinder.java b/src/main/java/stirling/software/SPDF/pdf/TextFinder.java index cdfb5501..f9e339c2 100644 --- a/src/main/java/stirling/software/SPDF/pdf/TextFinder.java +++ b/src/main/java/stirling/software/SPDF/pdf/TextFinder.java @@ -19,6 +19,16 @@ public class TextFinder extends PDFTextStripper { private final boolean wholeWordSearch; private final List textOccurrences = new ArrayList<>(); + private class MatchInfo { + int startIndex; + int matchLength; + + MatchInfo(int startIndex, int matchLength) { + this.startIndex = startIndex; + this.matchLength = matchLength; + } + } + public TextFinder(String searchText, boolean useRegex, boolean wholeWordSearch) throws IOException { this.searchText = searchText.toLowerCase(); @@ -27,36 +37,37 @@ public class TextFinder extends PDFTextStripper { setSortByPosition(true); } - private List findOccurrencesInText(String searchText, String content) { - List indexes = new ArrayList<>(); + private List findOccurrencesInText(String searchText, String content) { + List matches = new ArrayList<>(); + Pattern pattern; if (useRegex) { // Use regex-based search pattern = wholeWordSearch - ? Pattern.compile("(\\b|_|\\.)" + searchText + "(\\b|_|\\.)") + ? Pattern.compile("\\b" + searchText + "\\b") : Pattern.compile(searchText); } else { // Use normal text search pattern = wholeWordSearch - ? Pattern.compile( - "(\\b|_|\\.)" + Pattern.quote(searchText) + "(\\b|_|\\.)") + ? Pattern.compile("\\b" + Pattern.quote(searchText) + "\\b") : Pattern.compile(Pattern.quote(searchText)); } Matcher matcher = pattern.matcher(content); while (matcher.find()) { - indexes.add(matcher.start()); + matches.add(new MatchInfo(matcher.start(), matcher.end() - matcher.start())); } - return indexes; + return matches; } @Override protected void writeString(String text, List textPositions) { - for (Integer index : findOccurrencesInText(searchText, text.toLowerCase())) { - if (index + searchText.length() <= textPositions.size()) { + for (MatchInfo match : findOccurrencesInText(searchText, text.toLowerCase())) { + int index = match.startIndex; + if (index + match.matchLength <= textPositions.size()) { // Initial values based on the first character TextPosition first = textPositions.get(index); float minX = first.getX(); @@ -65,7 +76,7 @@ public class TextFinder extends PDFTextStripper { float maxY = first.getY() + first.getHeight(); // Loop over the rest of the characters and adjust bounding box values - for (int i = index; i < index + searchText.length(); i++) { + for (int i = index; i < index + match.matchLength; i++) { TextPosition position = textPositions.get(i); minX = Math.min(minX, position.getX()); minY = Math.min(minY, position.getY()); diff --git a/src/main/java/stirling/software/SPDF/repository/AuthorityRepository.java b/src/main/java/stirling/software/SPDF/repository/AuthorityRepository.java index bbf32a07..41c251e1 100644 --- a/src/main/java/stirling/software/SPDF/repository/AuthorityRepository.java +++ b/src/main/java/stirling/software/SPDF/repository/AuthorityRepository.java @@ -9,4 +9,6 @@ import stirling.software.SPDF.model.Authority; public interface AuthorityRepository extends JpaRepository { // Set findByUsername(String username); Set findByUser_Username(String username); + + Authority findByUserId(long user_id); } diff --git a/src/main/java/stirling/software/SPDF/repository/UserRepository.java b/src/main/java/stirling/software/SPDF/repository/UserRepository.java index d63c4cba..ad65f27d 100644 --- a/src/main/java/stirling/software/SPDF/repository/UserRepository.java +++ b/src/main/java/stirling/software/SPDF/repository/UserRepository.java @@ -7,6 +7,8 @@ import org.springframework.data.jpa.repository.JpaRepository; import stirling.software.SPDF.model.User; public interface UserRepository extends JpaRepository { + Optional findByUsernameIgnoreCase(String username); + Optional findByUsername(String username); User findByApiKey(String apiKey); diff --git a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java index d1a58087..61b72da6 100644 --- a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java @@ -12,11 +12,12 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.springframework.web.multipart.MultipartFile; +import com.fathzer.soft.javaluator.DoubleEvaluator; + import io.github.pixee.security.HostValidator; import io.github.pixee.security.Urls; @@ -87,6 +88,7 @@ public class GeneralUtils { } sizeStr = sizeStr.trim().toUpperCase(); + sizeStr = sizeStr.replace(",", ".").replace(" ", ""); try { if (sizeStr.endsWith("KB")) { return (long) @@ -115,91 +117,115 @@ public class GeneralUtils { return null; } - public static List parsePageString(String pageOrder, int totalPages) { - return parsePageString(pageOrder, totalPages, false); - } - - public static List parsePageString( - String pageOrder, int totalPages, boolean isOneBased) { - if (pageOrder == null || pageOrder.isEmpty()) { - return Collections.singletonList(1); + public static List parsePageList(String pages, int totalPages, boolean oneBased) { + if (pages == null) { + return List.of(1); // Default to first page if input is null } - if (pageOrder.matches("\\d+")) { - // Convert the single number string to an integer and return it in a list - return Collections.singletonList(Integer.parseInt(pageOrder)); + try { + return parsePageList(pages.split(","), totalPages, oneBased); + } catch (NumberFormatException e) { + return List.of(1); // Default to first page if input is invalid } - return parsePageList(pageOrder.split(","), totalPages, isOneBased); } - public static List parsePageList(String[] pageOrderArr, int totalPages) { - return parsePageList(pageOrderArr, totalPages, false); + public static List parsePageList(String[] pages, int totalPages) { + return parsePageList(pages, totalPages, false); } - public static List parsePageList( - String[] pageOrderArr, int totalPages, boolean isOneBased) { - List newPageOrder = new ArrayList<>(); + public static List parsePageList(String[] pages, int totalPages, boolean oneBased) { + List result = new ArrayList<>(); + int offset = oneBased ? 1 : 0; + for (String page : pages) { + if ("all".equalsIgnoreCase(page)) { - int adjustmentFactor = isOneBased ? 1 : 0; - - // loop through the page order array - for (String element : pageOrderArr) { - if ("all".equalsIgnoreCase(element)) { for (int i = 0; i < totalPages; i++) { - newPageOrder.add(i + adjustmentFactor); + result.add(i + offset); } - // As all pages are already added, no need to check further - break; - } else if (element.matches("\\d*n\\+?-?\\d*|\\d*\\+?n")) { - // Handle page order as a function - int coefficient = 0; - int constant = 0; - boolean coefficientExists = false; - boolean constantExists = false; - - if (element.contains("n")) { - String[] parts = element.split("n"); - if (!"".equals(parts[0]) && parts[0] != null) { - coefficient = Integer.parseInt(parts[0]); - coefficientExists = true; - } - if (parts.length > 1 && !"".equals(parts[1]) && parts[1] != null) { - constant = Integer.parseInt(parts[1]); - constantExists = true; - } - } else if (element.contains("+")) { - constant = Integer.parseInt(element.replace("+", "")); - constantExists = true; - } - - for (int i = 1; i <= totalPages; i++) { - int pageNum = coefficientExists ? coefficient * i : i; - pageNum += constantExists ? constant : 0; - - if (pageNum <= totalPages && pageNum > 0) { - newPageOrder.add(pageNum - adjustmentFactor); - } - } - } else if (element.contains("-")) { - // split the range into start and end page - String[] range = element.split("-"); - int start = Integer.parseInt(range[0]); - int end = Integer.parseInt(range[1]); - // check if the end page is greater than total pages - if (end > totalPages) { - end = totalPages; - } - // loop through the range of pages - for (int j = start; j <= end; j++) { - // print the current index - newPageOrder.add(j - adjustmentFactor); + } else if (page.contains(",")) { + // Split the string into parts, could be single pages or ranges + String[] parts = page.split(","); + for (String part : parts) { + result.addAll(handlePart(part, totalPages, offset)); } } else { - // if the element is a single page - newPageOrder.add(Integer.parseInt(element) - adjustmentFactor); + result.addAll(handlePart(page, totalPages, offset)); } } + return new ArrayList<>( + new java.util.LinkedHashSet<>(result)); // Remove duplicates and maintain order + } - return newPageOrder; + public static List evaluateNFunc(String expression, int maxValue) { + List results = new ArrayList<>(); + DoubleEvaluator evaluator = new DoubleEvaluator(); + + // Validate the expression + if (!expression.matches("[0-9n+\\-*/() ]+")) { + throw new IllegalArgumentException("Invalid expression"); + } + + int n = 0; + while (true) { + // Replace 'n' with the current value of n, correctly handling numbers before 'n' + String sanitizedExpression = insertMultiplicationBeforeN(expression, n); + Double result = evaluator.evaluate(sanitizedExpression); + + // Check if the result is null or not within bounds + if (result == null || result <= 0 || result.intValue() > maxValue) { + if (n != 0) break; + } else { + results.add(result.intValue()); + } + n++; + } + + return results; + } + + private static String insertMultiplicationBeforeN(String expression, int nValue) { + // Insert multiplication between a number and 'n' (e.g., "4n" becomes "4*n") + String withMultiplication = expression.replaceAll("(\\d)n", "$1*n"); + // Now replace 'n' with its current value + return withMultiplication.replace("n", String.valueOf(nValue)); + } + + private static List handlePart(String part, int totalPages, int offset) { + List partResult = new ArrayList<>(); + + // First check for n-syntax because it should not be processed as a range + if (part.contains("n")) { + partResult = evaluateNFunc(part, totalPages); + // Adjust the results according to the offset + for (int i = 0; i < partResult.size(); i++) { + int adjustedValue = partResult.get(i) - 1 + offset; + partResult.set(i, adjustedValue); + } + } else if (part.contains("-")) { + // Process ranges only if it's not n-syntax + String[] rangeParts = part.split("-"); + try { + int start = Integer.parseInt(rangeParts[0]); + int end = Integer.parseInt(rangeParts[1]); + for (int i = start; i <= end; i++) { + if (i >= 1 && i <= totalPages) { + partResult.add(i - 1 + offset); + } + } + } catch (NumberFormatException e) { + // Range is invalid, ignore this part + } + } else { + // This is a single page number + try { + int pageNum = Integer.parseInt(part.trim()); + if (pageNum >= 1 && pageNum <= totalPages) { + partResult.add(pageNum - 1 + offset); + } + } catch (NumberFormatException ignored) { + // Ignore invalid numbers + } + } + return partResult; } public static boolean createDir(String path) { diff --git a/src/main/java/stirling/software/SPDF/utils/PDFToFile.java b/src/main/java/stirling/software/SPDF/utils/PDFToFile.java index 43683be8..e52344bf 100644 --- a/src/main/java/stirling/software/SPDF/utils/PDFToFile.java +++ b/src/main/java/stirling/software/SPDF/utils/PDFToFile.java @@ -25,6 +25,71 @@ import io.github.pixee.security.Filenames; import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult; public class PDFToFile { + + public ResponseEntity processPdfToHtml(MultipartFile inputFile) + throws IOException, InterruptedException { + if (!"application/pdf".equals(inputFile.getContentType())) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + // Get the original PDF file name without the extension + String originalPdfFileName = Filenames.toSimpleFileName(inputFile.getOriginalFilename()); + String pdfBaseName = originalPdfFileName.substring(0, originalPdfFileName.lastIndexOf('.')); + + Path tempInputFile = null; + Path tempOutputDir = null; + byte[] fileBytes; + String fileName = "temp.file"; + + try { + // Save the uploaded file to a temporary location + tempInputFile = Files.createTempFile("input_", ".pdf"); + Files.copy( + inputFile.getInputStream(), tempInputFile, StandardCopyOption.REPLACE_EXISTING); + + // Prepare the output directory + tempOutputDir = Files.createTempDirectory("output_"); + + // Run the pdftohtml command with complex output + List command = + new ArrayList<>( + Arrays.asList( + "pdftohtml", "-c", tempInputFile.toString(), pdfBaseName)); + + ProcessExecutorResult returnCode = + ProcessExecutor.getInstance(ProcessExecutor.Processes.PDFTOHTML) + .runCommandWithOutputHandling(command, tempOutputDir.toFile()); + + // Get output files + List outputFiles = Arrays.asList(tempOutputDir.toFile().listFiles()); + + // Return output files in a ZIP archive + fileName = pdfBaseName + "ToHtml.zip"; + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream); + + for (File outputFile : outputFiles) { + ZipEntry entry = new ZipEntry(outputFile.getName()); + zipOutputStream.putNextEntry(entry); + FileInputStream fis = new FileInputStream(outputFile); + IOUtils.copy(fis, zipOutputStream); + fis.close(); + zipOutputStream.closeEntry(); + } + + zipOutputStream.close(); + fileBytes = byteArrayOutputStream.toByteArray(); + + } finally { + // Clean up the temporary files + if (tempInputFile != null) Files.delete(tempInputFile); + if (tempOutputDir != null) FileUtils.deleteDirectory(tempOutputDir.toFile()); + } + + return WebResponseUtils.bytesToWebResponse( + fileBytes, fileName, MediaType.APPLICATION_OCTET_STREAM); + } + public ResponseEntity processPdfToOfficeFormat( MultipartFile inputFile, String outputFormat, String libreOfficeFilter) throws IOException, InterruptedException { @@ -39,17 +104,7 @@ public class PDFToFile { // Validate output format List allowedFormats = - Arrays.asList( - "doc", - "docx", - "odt", - "ppt", - "pptx", - "odp", - "rtf", - "html", - "xml", - "txt:Text"); + Arrays.asList("doc", "docx", "odt", "ppt", "pptx", "odp", "rtf", "xml", "txt:Text"); if (!allowedFormats.contains(outputFormat)) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } diff --git a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java index 77814340..225a2c12 100644 --- a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java @@ -6,6 +6,7 @@ import java.awt.image.RenderedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Calendar; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -19,11 +20,8 @@ import javax.imageio.stream.ImageOutputStream; import org.apache.pdfbox.Loader; import org.apache.pdfbox.cos.COSName; -import org.apache.pdfbox.pdmodel.PDDocument; -import org.apache.pdfbox.pdmodel.PDPage; -import org.apache.pdfbox.pdmodel.PDPageContentStream; +import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode; -import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.PDXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; @@ -39,6 +37,8 @@ import org.springframework.web.multipart.MultipartFile; import io.github.pixee.security.Filenames; +import stirling.software.SPDF.model.PdfMetadata; + public class PdfUtils { private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class); @@ -214,6 +214,7 @@ public class PdfUtils { throws IOException, Exception { try (PDDocument document = Loader.loadPDF(inputStream)) { PDFRenderer pdfRenderer = new PDFRenderer(document); + pdfRenderer.setSubsamplingAllowed(true); int pageCount = document.getNumberOfPages(); // Create a ByteArrayOutputStream to save the image(s) to @@ -335,14 +336,12 @@ public class PdfUtils { } } - private static void addImageToDocument( + public static void addImageToDocument( PDDocument doc, PDImageXObject image, String fitOption, boolean autoRotate) throws IOException { boolean imageIsLandscape = image.getWidth() > image.getHeight(); PDRectangle pageSize = PDRectangle.A4; - System.out.println(fitOption); - if (autoRotate && imageIsLandscape) { pageSize = new PDRectangle(pageSize.getHeight(), pageSize.getWidth()); } @@ -420,4 +419,28 @@ public class PdfUtils { logger.info("PDF successfully saved to byte array"); return baos.toByteArray(); } + + public static PdfMetadata extractMetadataFromPdf(PDDocument pdf) { + return PdfMetadata.builder() + .author(pdf.getDocumentInformation().getAuthor()) + .producer(pdf.getDocumentInformation().getProducer()) + .title(pdf.getDocumentInformation().getTitle()) + .creator(pdf.getDocumentInformation().getCreator()) + .subject(pdf.getDocumentInformation().getSubject()) + .keywords(pdf.getDocumentInformation().getKeywords()) + .creationDate(pdf.getDocumentInformation().getCreationDate()) + .modificationDate(pdf.getDocumentInformation().getModificationDate()) + .build(); + } + + public static void setMetadataToPdf(PDDocument pdf, PdfMetadata pdfMetadata) { + pdf.getDocumentInformation().setAuthor(pdfMetadata.getAuthor()); + pdf.getDocumentInformation().setProducer(pdfMetadata.getProducer()); + pdf.getDocumentInformation().setTitle(pdfMetadata.getTitle()); + pdf.getDocumentInformation().setCreator(pdfMetadata.getCreator()); + pdf.getDocumentInformation().setSubject(pdfMetadata.getSubject()); + pdf.getDocumentInformation().setKeywords(pdfMetadata.getKeywords()); + pdf.getDocumentInformation().setCreationDate(pdfMetadata.getCreationDate()); + pdf.getDocumentInformation().setModificationDate(Calendar.getInstance()); + } } diff --git a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java index 2c81dce5..93fdbd54 100644 --- a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java +++ b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java @@ -24,6 +24,7 @@ public class ProcessExecutor { public enum Processes { LIBRE_OFFICE, + PDFTOHTML, OCR_MY_PDF, PYTHON_OPENCV, GHOSTSCRIPT, @@ -45,6 +46,7 @@ public class ProcessExecutor { int semaphoreLimit = switch (key) { case LIBRE_OFFICE -> 1; + case PDFTOHTML -> 1; case OCR_MY_PDF -> 2; case PYTHON_OPENCV -> 8; case GHOSTSCRIPT -> 16; @@ -56,9 +58,10 @@ public class ProcessExecutor { long timeoutMinutes = switch (key) { case LIBRE_OFFICE -> 30; + case PDFTOHTML -> 20; case OCR_MY_PDF -> 30; case PYTHON_OPENCV -> 30; - case GHOSTSCRIPT -> 5; + case GHOSTSCRIPT -> 30; case WEASYPRINT -> 30; case INSTALL_APP -> 60; case CALIBRE -> 30; @@ -169,27 +172,35 @@ public class ProcessExecutor { errorReaderThread.join(); outputReaderThread.join(); - if (!liveUpdates) { - if (outputLines.size() > 0) { - String outputMessage = String.join("\n", outputLines); - messages += outputMessage; + if (outputLines.size() > 0) { + String outputMessage = String.join("\n", outputLines); + messages += outputMessage; + if (!liveUpdates) { logger.info("Command output:\n" + outputMessage); } + } - if (errorLines.size() > 0) { - String errorMessage = String.join("\n", errorLines); - messages += errorMessage; + if (errorLines.size() > 0) { + String errorMessage = String.join("\n", errorLines); + messages += errorMessage; + if (!liveUpdates) { logger.warn("Command error output:\n" + errorMessage); - if (exitCode != 0) { - throw new IOException( - "Command process failed with exit code " - + exitCode - + ". Error message: " - + errorMessage); - } } - } else if (exitCode != 0) { - throw new IOException("Command process failed with exit code " + exitCode); + if (exitCode != 0) { + throw new IOException( + "Command process failed with exit code " + + exitCode + + ". Error message: " + + errorMessage); + } + } + + if (exitCode != 0) { + throw new IOException( + "Command process failed with exit code " + + exitCode + + "\nLogs: " + + messages); } } finally { semaphore.release(); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1a5d1438..a5f12e64 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -24,8 +24,8 @@ spring.devtools.livereload.enabled=true spring.thymeleaf.encoding=UTF-8 -server.connection-timeout=${SYSTEM_CONNECTIONTIMEOUTMINUTES:5m} -spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:300000} +server.connection-timeout=${SYSTEM_CONNECTIONTIMEOUTMINUTES:20m} +spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000} spring.resources.static-locations=file:customFiles/static/ #spring.thymeleaf.prefix=file:/customFiles/templates/,classpath:/templates/ diff --git a/src/main/resources/messages.properties b/src/main/resources/messages.properties index 579e649c..e69de29b 100644 --- a/src/main/resources/messages.properties +++ b/src/main/resources/messages.properties @@ -1,11 +0,0 @@ -fileToPDF.fileTypesList=Microsoft Word: (DOC, DOCX, DOT, DOTX)
\ -Microsoft Excel: (CSV, XLS, XLSX, XLT, XLTX, SLK, DIF)
\ -Microsoft PowerPoint: (PPT, PPTX)
\ -OpenDocument Formats: (ODT, OTT, ODS, OTS, ODP, OTP, ODG, OTG)
\ -Plain Text: (TXT, TEXT, XML)
\ -Rich Text Format: (RTF)
\ -Images: (BMP, GIF, JPEG, PNG, TIF, PBM, PGM, PPM, RAS, XBM, XPM, SVG, SVM, WMF)
\ -HTML: (HTML)
\ -Lotus Word Pro: (LWP)
\ -StarOffice formats: (SDA, SDC, SDD, SDW, STC, STD, STI, STW, SXD, SXG, SXI, SXW)
\ -Other formats: (DBF, FODS, VSD, VOR, VOR3, VOR4, UOP, PCT, PS, PDF) diff --git a/src/main/resources/messages_ar_AR.properties b/src/main/resources/messages_ar_AR.properties index bdd14ecd..c6119110 100644 --- a/src/main/resources/messages_ar_AR.properties +++ b/src/main/resources/messages_ar_AR.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -13,15 +13,17 @@ processTimeWarning=تحذير: يمكن أن تستغرق هذه العملية pageOrderPrompt=ترتيب الصفحات (أدخل قائمة بأرقام الصفحات مفصولة بفواصل): pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : goToPage=اذهب -true=\u0635\u062D\u064A\u062D -false=\u062E\u0637\u0623 -unknown=\u063A\u064A\u0631 \u0645\u0639\u0631\u0648\u0641 -save=\u062D\u0641\u0638 -close=\u0625\u063A\u0644\u0627\u0642 +true=صحيح +false=خطأ +unknown=غير معروف +save=حفظ +saveToBrowser=Save to Browser +close=إغلاق filesSelected=الملفات المحددة noFavourites=لم تتم إضافة أي مفضلات +downloadComplete=Download Complete bored=الانتظار بالملل؟ -alphabet=\u0627\u0644\u0623\u0628\u062C\u062F\u064A\u0629 +alphabet=الأبجدية downloadPdf=تنزيل PDF text=نص font=الخط @@ -52,16 +54,34 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=لا يمكن خفض دور المستخدم الحالي +downgradeCurrentUserLongMessage=لا يمكن تخفيض دور المستخدم الحالي. وبالتالي، لن يظهر المستخدم الحالي. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -87,27 +107,29 @@ navbar.security=الأمان navbar.other=أخرى navbar.darkmode=الوضع الداكن navbar.pageOps=عمليات الصفحة -navbar.settings=\u0625\u0639\u062F\u0627\u062F\u0627\u062A +navbar.settings=إعدادات ############# # SETTINGS # ############# -settings.title=\u0627\u0644\u0625\u0639\u062F\u0627\u062F\u0627\u062A -settings.update=\u0627\u0644\u062A\u062D\u062F\u064A\u062B \u0645\u062A\u0627\u062D -settings.appVersion=\u0625\u0635\u062F\u0627\u0631 \u0627\u0644\u062A\u0637\u0628\u064A\u0642: -settings.downloadOption.title=\u062A\u062D\u062F\u064A\u062F \u062E\u064A\u0627\u0631 \u0627\u0644\u062A\u0646\u0632\u064A\u0644 (\u0644\u0644\u062A\u0646\u0632\u064A\u0644\u0627\u062A \u0630\u0627\u062A \u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0648\u0627\u062D\u062F \u063A\u064A\u0631 \u0627\u0644\u0645\u0636\u063A\u0648\u0637): -settings.downloadOption.1=\u0641\u062A\u062D \u0641\u064A \u0646\u0641\u0633 \u0627\u0644\u0646\u0627\u0641\u0630\u0629 -settings.downloadOption.2=\u0641\u062A\u062D \u0641\u064A \u0646\u0627\u0641\u0630\u0629 \u062C\u062F\u064A\u062F\u0629 -settings.downloadOption.3=\u062A\u0646\u0632\u064A\u0644 \u0627\u0644\u0645\u0644\u0641 -settings.zipThreshold=\u0645\u0644\u0641\u0627\u062A \u0645\u0636\u063A\u0648\u0637\u0629 \u0639\u0646\u062F \u062A\u062C\u0627\u0648\u0632 \u0639\u062F\u062F \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0645 \u062A\u0646\u0632\u064A\u0644\u0647\u0627 +settings.title=الإعدادات +settings.update=التحديث متاح +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. +settings.appVersion=إصدار التطبيق: +settings.downloadOption.title=تحديد خيار التنزيل (للتنزيلات ذات الملف الواحد غير المضغوط): +settings.downloadOption.1=فتح في نفس النافذة +settings.downloadOption.2=فتح في نافذة جديدة +settings.downloadOption.3=تنزيل الملف +settings.zipThreshold=ملفات مضغوطة عند تجاوز عدد الملفات التي تم تنزيلها settings.signOut=Sign Out settings.accountSettings=Account Settings - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Change Credentials changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=New Username changeCreds.oldPassword=Current Password changeCreds.newPassword=New Password @@ -142,14 +164,18 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions adminUserSettings.apiUser=Limited API User +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Web Only User adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Force user to change username/password on login +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User +adminUserSettings.changeUserRole=تغيير دور المستخدم ############# # HOME-PAGE # @@ -222,25 +248,25 @@ home.compressPdfs.desc=ضغط ملفات PDF لتقليل حجم الملف. compressPdfs.tags=squish,small,tiny -home.changeMetadata.title=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629 -home.changeMetadata.desc=\u062A\u063A\u064A\u064A\u0631 / \u0625\u0632\u0627\u0644\u0629 / \u0625\u0636\u0627\u0641\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF +home.changeMetadata.title=تغيير البيانات الوصفية +home.changeMetadata.desc=تغيير / إزالة / إضافة بيانات أولية من مستند PDF changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats -home.fileToPDF.title=\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0645\u0644\u0641 \u0625\u0644\u0649 PDF -home.fileToPDF.desc=\u062A\u062D\u0648\u064A\u0644 \u0623\u064A \u0645\u0644\u0641 \u062A\u0642\u0631\u064A\u0628\u0627 \u0625\u0644\u0649 PDF (DOCX \u0648PNG \u0648XLS \u0648PPT \u0648TXT \u0648\u0627\u0644\u0645\u0632\u064A\u062F) +home.fileToPDF.title=تحويل الملف إلى PDF +home.fileToPDF.desc=تحويل أي ملف تقريبا إلى PDF (DOCX وPNG وXLS وPPT وTXT والمزيد) fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint -home.ocr.title=\u062A\u0634\u063A\u064A\u0644 OCR \u0639\u0644\u0649 PDF \u0648 / \u0623\u0648 \u0645\u0633\u062D \u0636\u0648\u0626\u064A -home.ocr.desc=\u064A\u0642\u0648\u0645 \u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0628\u0645\u0633\u062D \u0648\u0627\u0643\u062A\u0634\u0627\u0641 \u0627\u0644\u0646\u0635 \u0645\u0646 \u0627\u0644\u0635\u0648\u0631 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF \u0648\u064A\u0639\u064A\u062F \u0625\u0636\u0627\u0641\u062A\u0647 \u0643\u0646\u0635 +home.ocr.title=تشغيل OCR على PDF و / أو مسح ضوئي +home.ocr.desc=يقوم برنامج التنظيف بمسح واكتشاف النص من الصور داخل ملف PDF ويعيد إضافته كنص ocr.tags=recognition,text,image,scan,read,identify,detection,editable -home.extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631 -home.extractImages.desc=\u064A\u0633\u062A\u062E\u0631\u062C \u062C\u0645\u064A\u0639 \u0627\u0644\u0635\u0648\u0631 \u0645\u0646 \u0645\u0644\u0641 PDF \u0648\u064A\u062D\u0641\u0638\u0647\u0627 \u0641\u064A \u0627\u0644\u0631\u0645\u0632 \u0627\u0644\u0628\u0631\u064A\u062F\u064A +home.extractImages.title=استخراج الصور +home.extractImages.desc=يستخرج جميع الصور من ملف PDF ويحفظها في الرمز البريدي extractImages.tags=picture,photo,save,archive,zip,capture,grab -home.pdfToPDFA.title=\u062A\u062D\u0648\u064A\u0644 \u0645\u0644\u0641\u0627\u062A PDF \u0625\u0644\u0649 PDF / A -home.pdfToPDFA.desc=\u062A\u062D\u0648\u064A\u0644 PDF \u0625\u0644\u0649 PDF / A \u0644\u0644\u062A\u062E\u0632\u064A\u0646 \u0637\u0648\u064A\u0644 \u0627\u0644\u0645\u062F\u0649 +home.pdfToPDFA.title=تحويل ملفات PDF إلى PDF / A +home.pdfToPDFA.desc=تحويل PDF إلى PDF / A للتخزين طويل المدى pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation home.PDFToWord.title=تحويل PDF إلى Word @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Sign in +login.header=Sign in login.signin=Sign in login.rememberme=Remember me login.invalid=Invalid username or password. login.locked=Your account has been locked. login.signinTitle=Please sign in +login.ssoSignIn=تسجيل الدخول عبر تسجيل الدخول الأحادي +login.oauth2AutoCreateDisabled=تم تعطيل مستخدم الإنشاء التلقائي لـ OAuth2 #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Convert To Single Page pageExtracter.title=Extract Pages pageExtracter.header=Extract Pages pageExtracter.submit=Extract +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=المستند 1 compare.document.2=المستند 2 compare.submit=يقارن +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=تسجيل الدخول @@ -643,6 +694,7 @@ repair.submit=الإصلاح #flatten flatten.title=تسطيح flatten.header=تسوية ملفات PDF +flatten.flattenOnlyForms=Flatten only forms flatten.submit=تسطيح @@ -660,38 +712,38 @@ ScannerImageSplit.selectText.10=يضبط حجم الحدود المضافة وا #OCR -ocr.title=\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 / \u062A\u0646\u0638\u064A\u0641 \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A -ocr.header=\u0645\u0633\u062D \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A / \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 (\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641) -ocr.selectText.1=\u062D\u062F\u062F \u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u062A\u064A \u0633\u064A\u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF (\u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u0645\u062F\u0631\u062C\u0629 \u0647\u064A \u062A\u0644\u0643 \u0627\u0644\u062A\u064A \u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062D\u0627\u0644\u064A\u064B\u0627): -ocr.selectText.2=\u0625\u0646\u062A\u0627\u062C \u0645\u0644\u0641 \u0646\u0635\u064A \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 OCR \u0628\u062C\u0627\u0646\u0628 \u0645\u0644\u0641 PDF \u0627\u0644\u0630\u064A \u062A\u0645 \u0625\u0639\u062F\u0627\u062F\u0647 \u0628\u0648\u0627\u0633\u0637\u0629 OCR -ocr.selectText.3=\u062A\u0645 \u0645\u0633\u062D \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u0635\u062D\u064A\u062D\u0629 \u0636\u0648\u0626\u064A\u064B\u0627 \u0628\u0632\u0627\u0648\u064A\u0629 \u0645\u0646\u062D\u0631\u0641\u0629 \u0639\u0646 \u0637\u0631\u064A\u0642 \u062A\u062F\u0648\u064A\u0631\u0647\u0627 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0641\u064A \u0645\u0643\u0627\u0646\u0647\u0627 -ocr.selectText.4=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629. (\u0644\u0627 \u064A\u0648\u062C\u062F \u062A\u063A\u064A\u064A\u0631 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C) -ocr.selectText.5=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u060C \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629 \u060C \u0648\u064A\u062D\u0627\u0641\u0638 \u0639\u0644\u0649 \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C. -ocr.selectText.6=\u064A\u062A\u062C\u0627\u0647\u0644 \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 \u062A\u0641\u0627\u0639\u0644\u064A \u060C \u0641\u0642\u0637 \u0635\u0641\u062D\u0627\u062A OCRs \u0627\u0644\u062A\u064A \u0647\u064A \u0635\u0648\u0631 -ocr.selectText.7=\u0641\u0631\u0636 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u060C \u0633\u064A\u0624\u062F\u064A \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u0639\u0644\u0649 \u0643\u0644 \u0635\u0641\u062D\u0629 \u0625\u0644\u0649 \u0625\u0632\u0627\u0644\u0629 \u062C\u0645\u064A\u0639 \u0639\u0646\u0627\u0635\u0631 \u0627\u0644\u0646\u0635 \u0627\u0644\u0623\u0635\u0644\u064A -ocr.selectText.8=\u0639\u0627\u062F\u064A (\u062E\u0637\u0623 \u0625\u0630\u0627 \u0643\u0627\u0646 PDF \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635) -ocr.selectText.9=\u0625\u0639\u062F\u0627\u062F\u0627\u062A \u0625\u0636\u0627\u0641\u064A\u0629 -ocr.selectText.10=\u0648\u0636\u0639 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 +ocr.title=التعرف الضوئي على الحروف / تنظيف المسح الضوئي +ocr.header=مسح المسح الضوئي / التعرف الضوئي على الحروف (التعرف الضوئي على الحروف) +ocr.selectText.1=حدد اللغات التي سيتم اكتشافها داخل ملف PDF (اللغات المدرجة هي تلك التي تم اكتشافها حاليًا): +ocr.selectText.2=إنتاج ملف نصي يحتوي على نص OCR بجانب ملف PDF الذي تم إعداده بواسطة OCR +ocr.selectText.3=تم مسح الصفحات الصحيحة ضوئيًا بزاوية منحرفة عن طريق تدويرها مرة أخرى في مكانها +ocr.selectText.4=صفحة نظيفة لذلك من غير المحتمل أن يجد OCR نصًا في ضوضاء الخلفية. (لا يوجد تغيير في الإخراج) +ocr.selectText.5=صفحة نظيفة ، لذلك من غير المحتمل أن يجد OCR نصًا في ضوضاء الخلفية ، ويحافظ على التنظيف في الإخراج. +ocr.selectText.6=يتجاهل الصفحات التي تحتوي على نص تفاعلي ، فقط صفحات OCRs التي هي صور +ocr.selectText.7=فرض التعرف الضوئي على الحروف ، سيؤدي التعرف الضوئي على الحروف على كل صفحة إلى إزالة جميع عناصر النص الأصلي +ocr.selectText.8=عادي (خطأ إذا كان PDF يحتوي على نص) +ocr.selectText.9=إعدادات إضافية +ocr.selectText.10=وضع التعرف الضوئي على الحروف ocr.selectText.11=إزالة الصور بعد التعرف الضوئي على الحروف (يزيل كل الصور ، يكون مفيدًا فقط إذا كان جزءًا من خطوة التحويل) ocr.selectText.12=نوع العرض (متقدم) -ocr.help=\u064A\u0631\u062C\u0649 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0648\u062B\u0627\u0626\u0642 \u062D\u0648\u0644 \u0643\u064A\u0641\u064A\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0647\u0630\u0627 \u0644\u0644\u063A\u0627\u062A \u0623\u062E\u0631\u0649 \u0648 / \u0623\u0648 \u0627\u0644\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0644\u064A\u0633 \u0641\u064A \u0639\u0627\u0645\u0644 \u0627\u0644\u0625\u0631\u0633\u0627\u0621 -ocr.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0648 Tesseract \u0644 OCR. -ocr.submit=\u0645\u0639\u0627\u0644\u062C\u0629 PDF \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 OCR +ocr.help=يرجى قراءة هذه الوثائق حول كيفية استخدام هذا للغات أخرى و / أو الاستخدام ليس في عامل الإرساء +ocr.credit=تستخدم هذه الخدمة OCRmyPDF و Tesseract ل OCR. +ocr.submit=معالجة PDF باستخدام OCR #extractImages -extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631 -extractImages.header=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631 -extractImages.selectText=\u062D\u062F\u062F \u062A\u0646\u0633\u064A\u0642 \u0627\u0644\u0635\u0648\u0631\u0629 \u0644\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0635\u0648\u0631 \u0627\u0644\u0645\u0633\u062A\u062E\u0631\u062C\u0629 \u0625\u0644\u0649 -extractImages.submit=\u0627\u0633\u062A\u062E\u0631\u0627\u062C +extractImages.title=استخراج الصور +extractImages.header=استخراج الصور +extractImages.selectText=حدد تنسيق الصورة لتحويل الصور المستخرجة إلى +extractImages.submit=استخراج #File to PDF -fileToPDF.title=\u0645\u0644\u0641 \u0625\u0644\u0649 PDF -fileToPDF.header=\u062A\u062D\u0648\u064A\u0644 \u0623\u064A \u0645\u0644\u0641 \u0625\u0644\u0649 PDF -fileToPDF.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 \u0644\u064A\u0628\u0631 \u0623\u0648\u0641\u064A\u0633 \u0648\u0623\u0648\u0646\u0648\u0643\u0648\u0646\u0641 \u0644\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0645\u0644\u0641\u0627\u062A. -fileToPDF.supportedFileTypes=\u064A\u062C\u0628 \u0623\u0646 \u062A\u062A\u0636\u0645\u0646 \u0623\u0646\u0648\u0627\u0639 \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u0645\u062F\u0639\u0648\u0645\u0629 \u0645\u0627 \u064A\u0644\u064A \u0648\u0644\u0643\u0646 \u0644\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0642\u0627\u0626\u0645\u0629 \u0645\u062D\u062F\u062B\u0629 \u0643\u0627\u0645\u0644\u0629 \u0628\u0627\u0644\u062A\u0646\u0633\u064A\u0642\u0627\u062A \u0627\u0644\u0645\u062F\u0639\u0648\u0645\u0629 \u060C \u064A\u0631\u062C\u0649 \u0627\u0644\u0631\u062C\u0648\u0639 \u0625\u0644\u0649 \u0648\u062B\u0627\u0626\u0642 LibreOffice -fileToPDF.submit=\u062A\u062D\u0648\u064A\u0644 \u0625\u0644\u0649 PDF +fileToPDF.title=ملف إلى PDF +fileToPDF.header=تحويل أي ملف إلى PDF +fileToPDF.credit=تستخدم هذه الخدمة ليبر أوفيس وأونوكونف لتحويل الملفات. +fileToPDF.supportedFileTypes=يجب أن تتضمن أنواع الملفات المدعومة ما يلي ولكن للحصول على قائمة محدثة كاملة بالتنسيقات المدعومة ، يرجى الرجوع إلى وثائق LibreOffice +fileToPDF.submit=تحويل إلى PDF #compress @@ -726,11 +778,23 @@ merge.submit=دمج pdfOrganiser.title=منظم الصفحة pdfOrganiser.header=منظم صفحات PDF pdfOrganiser.submit=إعادة ترتيب الصفحات +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=أداة متعددة PDF multiTool.header=أداة متعددة PDF +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=مزيل الصفحة pageRemover.header=مزيل صفحة PDF pageRemover.pagesToDelete=الصفحات المراد حذفها (أدخل قائمة بأرقام الصفحات مفصولة بفواصل): pageRemover.submit=حذف الصفحات +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=حدد زاوية الدوران (بمضاعفات 90 درج rotate.submit=استدارة -#merge +#split-pdfs split.title=انقسام PDF split.header=تقسيم PDF split.desc.1=الأرقام التي تحددها هي رقم الصفحة التي تريد تقسيمها -split.desc.2=على هذا النحو ، سيؤدي تحديد 1،3،7-8 إلى تقسيم مستند من 10 صفحات إلى 6 PDFS منفصلة مع: +split.desc.2=على هذا النحو ، سيؤدي تحديد 1،3،7-9 إلى تقسيم مستند من 10 صفحات إلى 6 PDFS منفصلة مع: split.desc.3=المستند رقم 1: الصفحة 1 split.desc.4=المستند رقم 2: الصفحتان 2 و 3 -split.desc.5=المستند رقم 3: الصفحة 4 و 5 و 6 -split.desc.6=المستند رقم 4: الصفحة 7 -split.desc.7=المستند رقم 5: الصفحة 8 -split.desc.8=المستند رقم 6: الصفحتان 9 و 10 +split.desc.5=المستند رقم 3: الصفحة 4 و 5 و 6 و 7 +split.desc.6=المستند رقم 4: الصفحة 8 +split.desc.7=المستند رقم 5: الصفحة 9 +split.desc.8=المستند رقم 6: الصفحة 10 split.splitPages=أدخل الصفحات المراد تقسيمها: split.submit=Split @@ -773,23 +838,23 @@ imageToPDF.selectLabel=Image Fit Options imageToPDF.fillPage=Fill Page imageToPDF.fitDocumentToImage=Fit Page to Image imageToPDF.maintainAspectRatio=Maintain Aspect Ratios -imageToPDF.selectText.2=\u062F\u0648\u0631\u0627\u0646 PDF \u062A\u0644\u0642\u0627\u0626\u064A\u064B\u0627 -imageToPDF.selectText.3=\u0627\u0644\u0645\u0646\u0637\u0642 \u0627\u0644\u0645\u062A\u0639\u062F\u062F \u0644\u0644\u0645\u0644\u0641\u0627\u062A (\u0645\u0641\u0639\u0651\u0644 \u0641\u0642\u0637 \u0625\u0630\u0627 \u0643\u0646\u062A \u062A\u0639\u0645\u0644 \u0645\u0639 \u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629) -imageToPDF.selectText.4=\u062F\u0645\u062C \u0641\u064A \u0645\u0644\u0641 PDF \u0648\u0627\u062D\u062F -imageToPDF.selectText.5=\u062A\u062D\u0648\u064A\u0644 \u0625\u0644\u0649 \u0645\u0644\u0641\u0627\u062A PDF \u0645\u0646\u0641\u0635\u0644\u0629 +imageToPDF.selectText.2=دوران PDF تلقائيًا +imageToPDF.selectText.3=المنطق المتعدد للملفات (مفعّل فقط إذا كنت تعمل مع صور متعددة) +imageToPDF.selectText.4=دمج في ملف PDF واحد +imageToPDF.selectText.5=تحويل إلى ملفات PDF منفصلة #pdfToImage pdfToImage.title=تحويل PDF إلى صورة pdfToImage.header=تحويل PDF إلى صورة pdfToImage.selectText=تنسيق الصورة -pdfToImage.singleOrMultiple=\u0646\u0648\u0639 \u0646\u062A\u064A\u062C\u0629 \u0627\u0644\u0635\u0648\u0631\u0629 -pdfToImage.single=\u0635\u0648\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0643\u0628\u064A\u0631\u0629 -pdfToImage.multi=\u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629 -pdfToImage.colorType=\u0646\u0648\u0639 \u0627\u0644\u0644\u0648\u0646 -pdfToImage.color=\u0627\u0644\u0644\u0648\u0646 -pdfToImage.grey=\u062A\u062F\u0631\u062C \u0627\u0644\u0631\u0645\u0627\u062F\u064A -pdfToImage.blackwhite=\u0623\u0628\u064A\u0636 \u0648\u0623\u0633\u0648\u062F (\u0642\u062F \u064A\u0641\u0642\u062F \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A!) +pdfToImage.singleOrMultiple=نوع نتيجة الصورة +pdfToImage.single=صورة واحدة كبيرة +pdfToImage.multi=صور متعددة +pdfToImage.colorType=نوع اللون +pdfToImage.color=اللون +pdfToImage.grey=تدرج الرمادي +pdfToImage.blackwhite=أبيض وأسود (قد يفقد البيانات!) pdfToImage.submit=تحول @@ -824,10 +889,12 @@ watermark.selectText.3=حجم الخط: watermark.selectText.4=دوران (0-360): watermark.selectText.5=widthSpacer (مسافة بين كل علامة مائية أفقيًا): watermark.selectText.6=heightSpacer (مسافة بين كل علامة مائية عموديًا): -watermark.selectText.7=\u0627\u0644\u062A\u0639\u062A\u064A\u0645 (0\u066A - 100\u066A): +watermark.selectText.7=التعتيم (0٪ - 100٪): watermark.selectText.8=Watermark Type: watermark.selectText.9=Watermark Image: watermark.submit=إضافة علامة مائية +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -856,29 +923,31 @@ removePassword.submit=إزالة #changeMetadata -changeMetadata.title=\u0627\u0644\u0639\u0646\u0648\u0627\u0646: -changeMetadata.header=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629 -changeMetadata.selectText.1=\u064A\u0631\u062C\u0649 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u062A\u063A\u064A\u0631\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u063A\u064A\u064A\u0631\u0647\u0627 -changeMetadata.selectText.2=\u062D\u0630\u0641 \u0643\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 -changeMetadata.selectText.3=\u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629: -changeMetadata.author=\u0627\u0644\u0645\u0624\u0644\u0641: -changeMetadata.creationDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0625\u0646\u0634\u0627\u0621 (yyyy / MM / dd HH: mm: ss): -changeMetadata.creator=\u0627\u0644\u0645\u0646\u0634\u0626: -changeMetadata.keywords=\u0627\u0644\u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629: -changeMetadata.modDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0639\u062F\u064A\u0644 (yyyy / MM / dd HH: mm: ss): -changeMetadata.producer=\u0627\u0644\u0645\u0646\u062A\u062C: -changeMetadata.subject=\u0627\u0644\u0645\u0648\u0636\u0648\u0639: -changeMetadata.trapped=\u0645\u062D\u0627\u0635\u0631: -changeMetadata.selectText.4=\u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0635\u0641\u064A\u0629 \u0623\u062E\u0631\u0649: -changeMetadata.selectText.5=\u0625\u0636\u0627\u0641\u0629 \u0625\u062F\u062E\u0627\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u062E\u0635\u0635 -changeMetadata.submit=\u062A\u063A\u064A\u064A\u0631 +changeMetadata.title=العنوان: +changeMetadata.header=تغيير البيانات الوصفية +changeMetadata.selectText.1=يرجى تعديل المتغيرات التي ترغب في تغييرها +changeMetadata.selectText.2=حذف كل البيانات الأولية +changeMetadata.selectText.3=إظهار البيانات الأولية المخصصة: +changeMetadata.author=المؤلف: +changeMetadata.creationDate=تاريخ الإنشاء (yyyy / MM / dd HH: mm: ss): +changeMetadata.creator=المنشئ: +changeMetadata.keywords=الكلمات الرئيسية: +changeMetadata.modDate=تاريخ التعديل (yyyy / MM / dd HH: mm: ss): +changeMetadata.producer=المنتج: +changeMetadata.subject=الموضوع: +changeMetadata.trapped=محاصر: +changeMetadata.selectText.4=بيانات وصفية أخرى: +changeMetadata.selectText.5=إضافة إدخال بيانات أولية مخصص +changeMetadata.submit=تغيير #pdfToPDFA -pdfToPDFA.title=PDF \u0625\u0644\u0649 PDF / A -pdfToPDFA.header=PDF \u0625\u0644\u0649 PDF / A -pdfToPDFA.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0644\u062A\u062D\u0648\u064A\u0644 PDF / A. -pdfToPDFA.submit=\u062A\u062D\u0648\u064A\u0644 +pdfToPDFA.title=PDF إلى PDF / A +pdfToPDFA.header=PDF إلى PDF / A +pdfToPDFA.credit=تستخدم هذه الخدمة OCRmyPDF لتحويل PDF / A. +pdfToPDFA.submit=تحويل +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=تحويل #PDFToHTML PDFToHTML.title=PDF إلى HTML PDFToHTML.header=PDF إلى HTML -PDFToHTML.credit=تستخدم هذه الخدمة LibreOffice لتحويل الملفات. +PDFToHTML.credit=تستخدم هذه الخدمة pdftohtml لتحويل الملفات. PDFToHTML.submit=تحويل @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=?????? #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_bg_BG.properties b/src/main/resources/messages_bg_BG.properties index 9bef411d..ca196110 100644 --- a/src/main/resources/messages_bg_BG.properties +++ b/src/main/resources/messages_bg_BG.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=Вярно false=Невярно unknown=Непознат save=Съхранете +saveToBrowser=Save to Browser close=Затворете filesSelected=избрани файлове noFavourites=Няма добавени любими +downloadComplete=Download Complete bored=Отекчени сте да чакате? alphabet=Азбука downloadPdf=Изтеглете PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=Потребителят не е автентикира userNotFoundMessage=Потребителят не е намерен incorrectPasswordMessage=Текущата парола е неправилна. usernameExistsMessage=Новият потребител вече съществува. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Не може да се понижи ролята на текущия потребител +downgradeCurrentUserLongMessage=Не може да се понижи ролята на текущия потребител. Следователно текущият потребител няма да бъде показан. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Настройки ############# settings.title=Настройки settings.update=Налична актуализация +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Версия на приложението: settings.downloadOption.title=Изберете опция за изтегляне (за изтегляния на един файл без да е архивиран): settings.downloadOption.1=Отваряне в същия прозорец @@ -102,12 +123,13 @@ settings.downloadOption.3=Изтегли файл settings.zipThreshold=Архивирайте файловете, когато броят на изтеглените файлове надвишава settings.signOut=Изход settings.accountSettings=Настройки на акаунта - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Промяна на идентификационните данни changeCreds.header=Актуализирайте данните за акаунта си -changeCreds.changeUserAndPassword=Използвате идентификационни данни за вход по подразбиране. Моля, въведете нова парола (и потребителско име, ако искате) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=Ново потребителско име changeCreds.oldPassword=Текуща парола changeCreds.newPassword=Нова парола @@ -142,14 +164,18 @@ adminUserSettings.header=Настройки за администраторск adminUserSettings.admin=Администратор adminUserSettings.user=Потребител adminUserSettings.addUser=Добавяне на нов потребител +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Роли adminUserSettings.role=Роля adminUserSettings.actions=Действия adminUserSettings.apiUser=Ограничен API потребител +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Само за уеб-потребител adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Принудете потребителя да промени потребителското име/парола при влизане adminUserSettings.submit=Съхранете потребителя +adminUserSettings.changeUserRole=Промяна на ролята на потребителя ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Вход +login.header=Вход login.signin=Впишете се login.rememberme=Запомни ме login.invalid=Невалидно потребителско име или парола. login.locked=Вашият акаунт е заключен. login.signinTitle=Моля впишете се +login.ssoSignIn=Влизане чрез еднократно влизане +login.oauth2AutoCreateDisabled=OAUTH2 Автоматично създаване на потребител е деактивирано #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Преобразуване към единична стр pageExtracter.title=Extract Pages pageExtracter.header=Extract Pages pageExtracter.submit=Extract +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Документ 1 compare.document.2=Документ 2 compare.submit=Сравнявай +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Подпишете @@ -643,6 +694,7 @@ repair.submit=Поправи #flatten flatten.title=Изравнете flatten.header=Изравнете PDF-и +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Изравнете @@ -726,11 +778,23 @@ merge.submit=Обединяване pdfOrganiser.title=Организатор на страници pdfOrganiser.header=Организатор на PDF страници pdfOrganiser.submit=Пренареждане на страниците +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF Мулти инструмент multiTool.header=PDF Мулти инструмент +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=Премахване на страници pageRemover.header=Премахване на PDF страници pageRemover.pagesToDelete=Страници за изтриване (Въведете списък с номера на страници, разделени със запетая) : pageRemover.submit=Изтриване на страници +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Изберете ъгъл на въртене (кратно rotate.submit=Завъртане -#merge +#split-pdfs split.title=Разделяне на PDF split.header=Разделяне на PDF split.desc.1=Числата, които избирате, са номера на страницата, на която искате да направите разделяне -split.desc.2=Така че избирането на 1,3,7-8 ще раздели документ от 10 страници на 6 отделни PDF файла с: +split.desc.2=Така че избирането на 1,3,7-9 ще раздели документ от 10 страници на 6 отделни PDF файла с: split.desc.3=Документ #1: Страница 1 split.desc.4=Документ #2: Страница 2 и 3 -split.desc.5=Документ #3: Страница 4, 5 и 6 -split.desc.6=Документ #4: Страница 7 -split.desc.7=Документ #5: Страница 8 -split.desc.8=Документ #6: Страница 9 и 10 +split.desc.5=Документ #3: Страница 4, 5, 6 и 7 +split.desc.6=Документ #4: Страница 8 +split.desc.7=Документ #5: Страница 9 +split.desc.8=Документ #6: Страница 10 split.splitPages=Въведете страници за разделяне: split.submit=Разделяне @@ -828,6 +893,8 @@ watermark.selectText.7=Непрозрачност (0% - 100%): watermark.selectText.8=Тип воден знак: watermark.selectText.9=Изображение за воден знак: watermark.submit=Добавяне на воден знак +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF към PDF/A pdfToPDFA.header=PDF към PDF/A pdfToPDFA.credit=Тази услуга използва OCRmyPDF за PDF/A преобразуване. pdfToPDFA.submit=Преобразуване +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Преобразуване #PDFToHTML PDFToHTML.title=PDF към HTML PDFToHTML.header=PDF към HTML -PDFToHTML.credit=Тази услуга използва LibreOffice за преобразуване на файлове. +PDFToHTML.credit=Тази услуга използва pdftohtml за преобразуване на файлове. PDFToHTML.submit=Преобразуване @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=???????? #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_ca_CA.properties b/src/main/resources/messages_ca_CA.properties index e47451b6..41f4f662 100644 --- a/src/main/resources/messages_ca_CA.properties +++ b/src/main/resources/messages_ca_CA.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=Verdader false=Fals unknown=Desconegut save=Desa +saveToBrowser=Save to Browser close=Tanca filesSelected=fitxers seleccionats noFavourites=No s'ha afegit cap favorit +downloadComplete=Download Complete bored=Avorrit esperant? alphabet=Alfabet downloadPdf=Descarregueu PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=No es pot reduir la funció de l'usuari actual +downgradeCurrentUserLongMessage=No es pot baixar la funció de l'usuari actual. Per tant, no es mostrarà l'usuari actual. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Opcions ############# settings.title=Opcions settings.update=Actualització Disponible +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Versió App: settings.downloadOption.title=Trieu l'opció de descàrrega (per a descàrregues d'un sol fitxer no zip): settings.downloadOption.1=Obre mateixa finestra @@ -102,12 +123,13 @@ settings.downloadOption.3=Descarrega Arxiu settings.zipThreshold=Comprimiu els fitxers quan el nombre de fitxers baixats superi settings.signOut=Sortir settings.accountSettings=Account Settings - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Change Credentials changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=New Username changeCreds.oldPassword=Current Password changeCreds.newPassword=New Password @@ -142,14 +164,18 @@ adminUserSettings.header=Usuari Admin Opcions Control adminUserSettings.admin=Admin adminUserSettings.user=Usuari adminUserSettings.addUser=Afegir Usuari +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Rols adminUserSettings.role=Rol adminUserSettings.actions=Accions adminUserSettings.apiUser=Usuari amb API limitada +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Usuari només WEB adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Force user to change username/password on login +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Desar Usuari +adminUserSettings.changeUserRole=Canvia el rol de l'usuari ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Accedir +login.header=Accedir login.signin=Accedir login.rememberme=Recordar login.invalid=Nom usuari / password no vàlid login.locked=Compte bloquejat login.signinTitle=Autenticat +login.ssoSignIn=Inicia sessió mitjançant l'inici de sessió ún +login.oauth2AutoCreateDisabled=L'usuari de creació automàtica OAUTH2 està desactivat #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Convert To Single Page pageExtracter.title=Extract Pages pageExtracter.header=Extract Pages pageExtracter.submit=Extract +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Document 1 compare.document.2=Document 2 compare.submit=Comparar +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Sign @@ -643,6 +694,7 @@ repair.submit=Reparar #flatten flatten.title=Aplanar flatten.header=Aplana els PDF +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Aplanar @@ -726,11 +778,23 @@ merge.submit=Fusiona pdfOrganiser.title=Organitzador de pàgines pdfOrganiser.header=Organitzador de pàgines PDF pdfOrganiser.submit=Reorganitza Pàgines +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF Multi Tool multiTool.header=PDF Multi Tool +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=Eliminació Pàgines pageRemover.header=Eliminació Pàgines PDF pageRemover.pagesToDelete=Pàgines a esborrar (Números de pàgina) : pageRemover.submit=Esborra Pàgines +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Selecciona l'angle de gir (en múltiples de 90 graus): rotate.submit=Rota -#merge +#split-pdfs split.title=Divideix PDF split.header=Divideix PDF split.desc.1=Els números seleccionats són el número de pàgina en què voleu fer la divisió -split.desc.2=Per tant, seleccionant 1,3,7-8 dividiria un document de 10 pàgines en 6 PDFS separats amb: +split.desc.2=Per tant, seleccionant 1,3,7-9 dividiria un document de 10 pàgines en 6 PDFS separats amb: split.desc.3=Document #1: Pàgina 1 split.desc.4=Document #2: Pàgina 2 i 3 -split.desc.5=Document #3: Pàgina 4, 5 i 6 -split.desc.6=Document #4: Pàgina 7 -split.desc.7=Document #5: Pàgina 8 -split.desc.8=Document #6: Pàgina 9 i 10 +split.desc.5=Document #3: Pàgina 4, 5, 6 i 7 +split.desc.6=Document #4: Pàgina 8 +split.desc.7=Document #5: Pàgina 9 +split.desc.8=Document #6: Pàgina 10 split.splitPages=Introdueix pàgines per dividir-les: split.submit=Divideix @@ -828,6 +893,8 @@ watermark.selectText.7=Opacitat (0% - 100%): watermark.selectText.8=Watermark Type: watermark.selectText.9=Watermark Image: watermark.submit=Afegir Marca d'Aigua +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF a PDF/A pdfToPDFA.header=PDF a PDF/A pdfToPDFA.credit=Utilitza OCRmyPDF per la conversió a PDF/A pdfToPDFA.submit=Converteix +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Converteix #PDFToHTML PDFToHTML.title=PDF a HTML PDFToHTML.header=PDF a HTML -PDFToHTML.credit=Utilitza LibreOffice per a la conversió d'Arxius. +PDFToHTML.credit=Utilitza pdftohtml per a la conversió d'Arxius. PDFToHTML.submit=Converteix @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=Extracte #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_de_DE.properties b/src/main/resources/messages_de_DE.properties index 2f043a7c..6cbd3a29 100644 --- a/src/main/resources/messages_de_DE.properties +++ b/src/main/resources/messages_de_DE.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -8,7 +8,7 @@ pdfPrompt=PDF auswählen multiPdfPrompt=PDFs auswählen(2+) multiPdfDropPrompt=Wählen Sie alle gewünschten PDFs aus (oder ziehen Sie sie per Drag & Drop hierhin) imgPrompt=Wählen Sie ein Bild -genericSubmit=Einreichen +genericSubmit=Absenden processTimeWarning=Achtung: Abhängig von der Dateigröße kann dieser Prozess bis zu einer Minute dauern pageOrderPrompt=Seitenreihenfolge (Geben Sie eine durch Komma getrennte Liste von Seitenzahlen ein): pageSelectionPrompt=Benutzerdefinierte Seitenauswahl (Geben Sie eine durch Kommas getrennte Liste von Seitenzahlen 1,5,6 oder Funktionen wie 2n+1 ein): @@ -17,9 +17,11 @@ true=Wahr false=Falsch unknown=Unbekannt save=Speichern +saveToBrowser=Im Browser speichern close=Schließen filesSelected=Dateien ausgewählt noFavourites=Keine Favoriten hinzugefügt +downloadComplete=Download abgeschlossen bored=Langeweile beim Warten? alphabet=Alphabet downloadPdf=PDF herunterladen @@ -44,7 +46,7 @@ green=Grün blue=Blau custom=benutzerdefiniert... WorkInProgess=In Arbeit, funktioniert möglicherweise nicht oder ist fehlerhaft. Bitte melden Sie alle Probleme! -poweredBy=Powered by +poweredBy=Unterstützt von yes=Ja no=Nein changedCredsMessage=Anmeldedaten geändert! @@ -52,28 +54,46 @@ notAuthenticatedMessage=Benutzer nicht authentifiziert. userNotFoundMessage=Benutzer nicht gefunden. incorrectPasswordMessage=Das Passwort ist falsch. usernameExistsMessage=Neuer Benutzername existiert bereits. +invalidUsernameMessage=Ungültiger Benutzername. Der Benutzername darf nur Buchstaben und Zahlen enthalten. +deleteCurrentUserMessage=Der aktuell angemeldete Benutzer kann nicht gelöscht werden. +deleteUsernameExistsMessage=Der Benutzername existiert nicht und kann nicht gelöscht werden. +downgradeCurrentUserMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden +downgradeCurrentUserLongMessage=Die Rolle des aktuellen Benutzers kann nicht herabgestuft werden. Daher wird der aktuelle Benutzer nicht angezeigt. +error=Fehler +oops=Hoppla! +help=Hilfe +goHomepage=Zur Startseite gehen +joinDiscord=Unserem Discord-Server beitreten +seeDockerHub=Docker Hub ansehen +visitGithub=GitHub-Repository besuchen +donate=Spenden +color=Farbe +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline-Menü (Alpha) +pipeline.header=Pipeline-Menü (Beta) pipeline.uploadButton=Benutzerdefinierter Upload pipeline.configureButton=Konfigurieren pipeline.defaultOption=Benutzerdefiniert pipeline.submitButton=Speichern +pipeline.help=Hilfe für Pipeline +pipeline.scanHelp=Hilfe zum Ordnerscan ###################### # Pipeline Options # ###################### pipelineOptions.header=Pipeline-Konfiguration pipelineOptions.pipelineNameLabel=Pipeline-Name -pipelineOptions.saveSettings=Save Operation Settings +pipelineOptions.saveSettings=Operations-Einstellungen speichern pipelineOptions.pipelineNamePrompt=Geben Sie hier den Namen der Pipeline ein pipelineOptions.selectOperation=Vorgang auswählen pipelineOptions.addOperationButton=Vorgang hinzufügen pipelineOptions.pipelineHeader=Pipeline: -pipelineOptions.saveButton=Downloaden +pipelineOptions.saveButton=Herunterladen pipelineOptions.validateButton=Validieren @@ -85,7 +105,7 @@ pipelineOptions.validateButton=Validieren navbar.convert=Konvertieren navbar.security=Sicherheit navbar.other=Anderes -navbar.darkmode=Dark Mode +navbar.darkmode=Dunkler Modus navbar.pageOps=Seitenoperationen navbar.settings=Einstellungen @@ -94,6 +114,7 @@ navbar.settings=Einstellungen ############# settings.title=Einstellungen settings.update=Update verfügbar +settings.updateAvailable={0} ist die aktuelle installierte Version. Eine neue Version ({1}) ist verfügbar. settings.appVersion=App-Version: settings.downloadOption.title=Download-Option wählen (für einzelne Dateien, die keine Zip-Downloads sind): settings.downloadOption.1=Im selben Fenster öffnen @@ -102,12 +123,13 @@ settings.downloadOption.3=Datei herunterladen settings.zipThreshold=Dateien komprimieren, wenn die Anzahl der heruntergeladenen Dateien überschritten wird settings.signOut=Abmelden settings.accountSettings=Kontoeinstellungen - - +settings.bored.help=Aktiviert das Easter-Egg-Spiel +settings.cacheInputs.name=Formulareingaben speichern +settings.cacheInputs.help=Aktivieren, um zuvor verwendete Eingaben für zukünftige Durchläufe zu speichern changeCreds.title=Anmeldeinformationen ändern changeCreds.header=Aktualisieren Sie Ihre Kontodaten -changeCreds.changeUserAndPassword=Sie verwenden Standard-Anmeldeinformationen. Bitte geben Sie ein neues Passwort (und ggf. einen Benutzernamen) ein. +changeCreds.changePassword=Sie verwenden die Standard-Zugangsdaten. Bitte geben Sie ein neues Passwort ein. changeCreds.newUsername=Neuer Benutzername changeCreds.oldPassword=Aktuelles Passwort changeCreds.newPassword=Neues Passwort @@ -128,7 +150,7 @@ account.newPassword=Neues Passwort account.changePassword=Passwort ändern account.confirmNewPassword=Neues Passwort bestätigen account.signOut=Abmelden -account.yourApiKey=Dein API Schlüssel +account.yourApiKey=Dein API-Schlüssel account.syncTitle=Browsereinstellungen mit Konto synchronisieren account.settingsCompare=Einstellungen vergleichen: account.property=Eigenschaft @@ -139,17 +161,21 @@ account.syncToAccount=Synchronisiere Konto <- Browser adminUserSettings.title=Benutzerkontrolle adminUserSettings.header=Administrator-Benutzerkontrolle -adminUserSettings.admin=Admin +adminUserSettings.admin=Administrator adminUserSettings.user=Benutzer adminUserSettings.addUser=Neuen Benutzer hinzufügen +adminUserSettings.usernameInfo=Der Benutzername darf nur Buchstaben und Zahlen enthalten, keine Leerzeichen oder Sonderzeichen. adminUserSettings.roles=Rollen adminUserSettings.role=Rolle adminUserSettings.actions=Aktion adminUserSettings.apiUser=Eingeschränkter API-Benutzer +adminUserSettings.extraApiUser=Zusätzlicher eingeschränkter API-Benutzer adminUserSettings.webOnlyUser=Nur Web-Benutzer adminUserSettings.demoUser=Demo-Benutzer (Keine benutzerdefinierten Einstellungen) +adminUserSettings.internalApiUser=Interner API-Benutzer adminUserSettings.forceChange=Benutzer dazu zwingen, Benutzernamen/Passwort bei der Anmeldung zu ändern adminUserSettings.submit=Benutzer speichern +adminUserSettings.changeUserRole=Benutzerrolle ändern ############# # HOME-PAGE # @@ -160,96 +186,96 @@ home.searchBar=Suche nach Funktionen... home.viewPdf.title=PDF anzeigen home.viewPdf.desc=Anzeigen, Kommentieren, Text oder Bilder hinzufügen -viewPdf.tags=view,read,annotate,text,image +viewPdf.tags=anzeigen,lesen,kommentieren,text,bild home.multiTool.title=PDF-Multitool home.multiTool.desc=Seiten zusammenführen, drehen, neu anordnen und entfernen multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side home.merge.title=Zusammenführen -home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen. -merge.tags=merge,Page operations,Back end,server side +home.merge.desc=Mehrere PDF-Dateien zu einer einzigen zusammenführen +merge.tags=zusammenführen,seitenvorgänge,back end,serverseite home.split.title=Aufteilen -home.split.desc=PDFs in mehrere Dokumente aufteilen. -split.tags=Page operations,divide,Multi Page,cut,server side +home.split.desc=PDFs in mehrere Dokumente aufteilen +split.tags=seitenoperationen,teilen,mehrseitig,ausschneiden,serverseitig home.rotate.title=Drehen -home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach. -rotate.tags=server side +home.rotate.desc=Drehen Sie Ihre PDFs ganz einfach +rotate.tags=serverseitig home.imageToPdf.title=Bild zu PDF -home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF. -imageToPdf.tags=conversion,img,jpg,picture,photo +home.imageToPdf.desc=Konvertieren Sie ein Bild (PNG, JPEG, GIF) in ein PDF +imageToPdf.tags=konvertierung,img,jpg,bild,foto home.pdfToImage.title=PDF zu Bild -home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF). -pdfToImage.tags=conversion,img,jpg,picture,photo +home.pdfToImage.desc=Konvertieren Sie ein PDF in ein Bild (PNG, JPEG, GIF) +pdfToImage.tags=konvertierung,img,jpg,bild,foto home.pdfOrganiser.title=Organisieren -home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern. -pdfOrganiser.tags=duplex,even,odd,sort,move +home.pdfOrganiser.desc=Seiten entfernen und Seitenreihenfolge ändern +pdfOrganiser.tags=duplex,gerade,ungerade,sortieren,verschieben home.addImage.title=Bild einfügen -home.addImage.desc=Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit). -addImage.tags=img,jpg,picture,photo +home.addImage.desc=Fügt ein Bild an eine bestimmte Stelle im PDF ein (in Arbeit) +addImage.tags=img,jpg,bild,foto home.watermark.title=Wasserzeichen hinzufügen -home.watermark.desc=Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu. -watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo +home.watermark.desc=Fügen Sie ein eigenes Wasserzeichen zu Ihrem PDF hinzu +watermark.tags=text,wiederholend,beschriftung,besitzen,urheberrecht,marke,img,jpg,bild,foto home.permissions.title=Berechtigungen ändern -home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern. -permissions.tags=read,write,edit,print +home.permissions.desc=Die Berechtigungen für Ihr PDF-Dokument verändern +permissions.tags=lesen,schreiben,bearbeiten,drucken home.removePages.title=Entfernen -home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen. -removePages.tags=Remove pages,delete pages +home.removePages.desc=Ungewollte Seiten aus dem PDF entfernen +removePages.tags=seiten entfernen,seiten löschen home.addPassword.title=Passwort hinzufügen -home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln. -addPassword.tags=secure,security +home.addPassword.desc=Das PDF mit einem Passwort verschlüsseln +addPassword.tags=sicher,sicherheit home.removePassword.title=Passwort entfernen -home.removePassword.desc=Den Passwortschutz eines PDFs entfernen. -removePassword.tags=secure,Decrypt,security,unpassword,delete password +home.removePassword.desc=Den Passwortschutz eines PDFs entfernen +removePassword.tags=sichern,entschlüsseln,sicherheit,passwort aufheben,passwort löschen home.compressPdfs.title=Komprimieren -home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren. -compressPdfs.tags=squish,small,tiny +home.compressPdfs.desc=PDF komprimieren um die Dateigröße zu reduzieren +compressPdfs.tags=komprimieren,verkleinern,minimieren home.changeMetadata.title=Metadaten ändern home.changeMetadata.desc=Ändern/Entfernen/Hinzufügen von Metadaten aus einem PDF-Dokument -changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats +changeMetadata.tags==titel,autor,datum,erstellung,uhrzeit,herausgeber,produzent,statistiken home.fileToPDF.title=Datei in PDF konvertieren home.fileToPDF.desc=Konvertieren Sie nahezu jede Datei in PDF (DOCX, PNG, XLS, PPT, TXT und mehr) -fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint +fileToPDF.tags=transformation,format,dokument,bild,folie,text,konvertierung,büro,dokumente,word,excel,powerpoint home.ocr.title=Führe OCR/Cleanup-Scans aus -home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu. -ocr.tags=recognition,text,image,scan,read,identify,detection,editable +home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fügt ihn erneut als Text hinzu +ocr.tags=erkennung,text,bild,scannen,lesen,identifizieren,erkennung,bearbeitbar home.extractImages.title=Bilder extrahieren home.extractImages.desc=Extrahiert alle Bilder aus einer PDF-Datei und speichert sie als Zip-Archiv -extractImages.tags=picture,photo,save,archive,zip,capture,grab +extractImages.tags=bild,foto,speichern,archivieren,zippen,erfassen,greifen home.pdfToPDFA.title=PDF zu PDF/A konvertieren home.pdfToPDFA.desc=PDF zu PDF/A für Langzeitarchivierung konvertieren -pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation +pdfToPDFA.tags=archiv,langfristig,standard,konvertierung,speicherung,aufbewahrung home.PDFToWord.title=PDF zu Word home.PDFToWord.desc=PDF in Word-Formate konvertieren (DOC, DOCX und ODT) -PDFToWord.tags=doc,docx,odt,word,transformation,format,conversion,office,microsoft,docfile +PDFToWord.tags=doc,docx,odt,word,transformation,format,konvertierung,office,microsoft,docfile home.PDFToPresentation.title=PDF zu Präsentation home.PDFToPresentation.desc=PDF in Präsentationsformate konvertieren (PPT, PPTX und ODP) -PDFToPresentation.tags=slides,show,office,microsoft +PDFToPresentation.tags=folien,show,büro,microsoft home.PDFToText.title=PDF in Text/RTF home.PDFToText.desc=PDF in Text- oder RTF-Format konvertieren @@ -257,88 +283,88 @@ PDFToText.tags=richformat,richtextformat,rich text format home.PDFToHTML.title=PDF in HTML home.PDFToHTML.desc=PDF in HTML-Format konvertieren -PDFToHTML.tags=web content,browser friendly +PDFToHTML.tags=webinhalte,browserfreundlich home.PDFToXML.title=PDF in XML home.PDFToXML.desc=PDF in XML-Format konvertieren -PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert +PDFToXML.tags=datenextraktion,strukturierter inhalt,interop,transformation,konvertierung home.ScannerImageSplit.title=Gescannte Fotos erkennen/aufteilen home.ScannerImageSplit.desc=Teilt mehrere Fotos innerhalb eines Fotos/PDF -ScannerImageSplit.tags=separate,auto-detect,scans,multi-photo,organize +ScannerImageSplit.tags=separat,automatische erkennung,scans,mehrere fotos,organisieren home.sign.title=Signieren home.sign.desc=Fügt PDF-Signaturen durch Zeichnung, Text oder Bild hinzu -sign.tags=authorize,initials,drawn-signature,text-sign,image-signature +sign.tags=autorisieren,initialen,gezeichnete signatur,textzeichen,bildsignatur home.flatten.title=Abflachen home.flatten.desc=Alle interaktiven Elemente und Formulare aus einem PDF entfernen -flatten.tags=static,deactivate,non-interactive,streamline +flatten.tags=statisch,deaktivieren,nicht interaktiv,optimieren home.repair.title=Reparatur home.repair.desc=Versucht, ein beschädigtes/kaputtes PDF zu reparieren -repair.tags=fix,restore,correction,recover +repair.tags=reparieren,wiederherstellen,korrigieren,wiederherstellen home.removeBlanks.title=Leere Seiten entfernen home.removeBlanks.desc=Erkennt und entfernt leere Seiten aus einem Dokument -removeBlanks.tags=cleanup,streamline,non-content,organize +removeBlanks.tags=aufräumen,rationalisieren,nicht inhaltsreich,organisieren home.removeAnnotations.title=Anmerkungen entfernen home.removeAnnotations.desc=Entfernt alle Kommentare/Anmerkungen aus einem PDF -removeAnnotations.tags=comments,highlight,notes,markup,remove +removeAnnotations.tags=kommentare,hervorheben,notizen,markieren,entfernen home.compare.title=Vergleichen home.compare.desc=Vergleicht und zeigt die Unterschiede zwischen zwei PDF-Dokumenten an -compare.tags=differentiate,contrast,changes,analysis +compare.tags=differenzieren,kontrastieren,verändern,analysieren home.certSign.title=Mit Zertifikat signieren home.certSign.desc=Ein PDF mit einem Zertifikat/Schlüssel (PEM/P12) signieren -certSign.tags=authenticate,PEM,P12,official,encrypt +certSign.tags=authentifizieren,pem,p12,offiziell,verschlüsseln home.pageLayout.title=Mehrseitiges Layout home.pageLayout.desc=Mehrere Seiten eines PDF zu einer Seite zusammenführen -pageLayout.tags=merge,composite,single-view,organize +pageLayout.tags=zusammenführen,zusammensetzen,einzelansicht,organisieren home.scalePages.title=Seitengröße/Skalierung anpassen home.scalePages.desc=Größe/Skalierung der Seite und/oder des Inhalts ändern -scalePages.tags=resize,modify,dimension,adapt +scalePages.tags=größe ändern,ändern,dimensionieren,anpassen home.pipeline.title=Pipeline (Fortgeschritten) home.pipeline.desc=Mehrere Aktionen auf ein PDF anwenden, definiert durch ein Pipeline Skript -pipeline.tags=automate,sequence,scripted,batch-process +pipeline.tags=automatisieren,sequenzieren,skriptgesteuert,batch prozess home.add-page-numbers.title=Seitenzahlen hinzufügen home.add-page-numbers.desc=Hinzufügen von Seitenzahlen an einer bestimmten Stelle -add-page-numbers.tags=paginate,label,organize,index +add-page-numbers.tags=paginieren,beschriften,organisieren,indizieren home.auto-rename.title=PDF automatisch umbenennen home.auto-rename.desc=PDF-Datei anhand von erkannten Kopfzeilen umbenennen -auto-rename.tags=auto-detect,header-based,organize,relabel +auto-rename.tags=automatisch erkennen,header basiert,organisieren,neu kennzeichnen home.adjust-contrast.title=Farben/Kontrast anpassen home.adjust-contrast.desc=Kontrast, Sättigung und Helligkeit einer PDF anpassen -adjust-contrast.tags=color-correction,tune,modify,enhance +adjust-contrast.tags=farbkorrektur,abstimmung,änderung,verbesserung home.crop.title=PDF zuschneiden home.crop.desc=PDF zuschneiden um die Größe zu verändern (Text bleibt erhalten!) -crop.tags=trim,shrink,edit,shape +crop.tags=trimmen,verkleinern,bearbeiten,formen home.autoSplitPDF.title=PDF automatisch teilen home.autoSplitPDF.desc=Physisch gescannte PDF anhand von Splitter-Seiten und QR-Codes aufteilen -autoSplitPDF.tags=QR-based,separate,scan-segment,organize +autoSplitPDF.tags=qr basiert,trennen,segment scannen,organisieren home.sanitizePdf.title=PDF Bereinigen home.sanitizePdf.desc=Entfernen von Skripten und anderen Elementen aus PDF-Dateien -sanitizePdf.tags=clean,secure,safe,remove-threats +sanitizePdf.tags=sauber,sicher,sicher,bedrohungen entfernen home.URLToPDF.title=URL/Website zu PDF home.URLToPDF.desc=Konvertiert jede http(s)URL zu PDF -URLToPDF.tags=web-capture,save-page,web-to-doc,archive +URLToPDF.tags=web capture,seite speichern,web to doc,archiv home.HTMLToPDF.title=HTML zu PDF home.HTMLToPDF.desc=Konvertiert jede HTML-Datei oder Zip-Archiv zu PDF -HTMLToPDF.tags=markup,web-content,transformation,convert +HTMLToPDF.tags=markup,webinhalt,transformation,konvertierung home.MarkdownToPDF.title=Markdown zu PDF @@ -353,17 +379,17 @@ getPdfInfo.tags=infomation,daten,statistik home.extractPage.title=Seite(n) extrahieren home.extractPage.desc=Extrahiert ausgewählte Seiten aus einer PDF -extractPage.tags=extrahieren +extractPage.tags=extrahieren,seite home.PdfToSinglePage.title=PDF zu einer Seite zusammenfassen home.PdfToSinglePage.desc=Fügt alle PDF-Seiten zu einer einzigen großen Seite zusammen -PdfToSinglePage.tags=einzelseite +PdfToSinglePage.tags=einzelseite,zusammenfassen home.showJS.title=Javascript anzeigen home.showJS.desc=Alle Javascript Funktionen in einer PDF anzeigen -showJS.tags=JS +showJS.tags=js,javascript home.autoRedact.title=Automatisch zensieren/schwärzen home.autoRedact.desc=Automatisches Zensieren (Schwärzen) von Text in einer PDF-Datei basierend auf dem eingegebenen Text @@ -371,7 +397,7 @@ autoRedact.tags=zensieren,schwärzen home.tableExtraxt.title=Tabelle extrahieren home.tableExtraxt.desc=Tabelle aus PDF in CSV extrahieren -tableExtraxt.tags=CSV +tableExtraxt.tags=CSV,tabelle,extrahieren home.autoSizeSplitPDF.title=Teilen nach Größe/Anzahl @@ -389,7 +415,16 @@ split-by-sections.tags=abschnitte,teilen,bearbeiten home.AddStampRequest.title=Stempel zu PDF hinzufügen home.AddStampRequest.desc=Fügen Sie an festgelegten Stellen Text oder Bildstempel hinzu -AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +AddStampRequest.tags=stempeln,bild hinzufügen,bild zentrieren,wasserzeichen,pdf,einbetten,anpassen + + +home.PDFToBook.title=PDF zum Buch +home.PDFToBook.desc=Konvertiert PDF mit Calibre in Buch-/Comic-Formate +PDFToBook.tags=buch,comic,calibre,convert,manga,amazon,kindle + +home.BookToPDF.title=Buch als PDF +home.BookToPDF.desc=Konvertiert Buch-/Comic-Formate mithilfe von Calibre in PDF +BookToPDF.tags=buch,comic,calibre,convert,manga,amazon,kindle ########################### @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Anmelden +login.header=Anmelden login.signin=Anmelden login.rememberme=Angemeldet bleiben -login.invalid=Ungültiger Benutzername oder Passwort. +login.invalid=Benutzername oder Passwort ungültig. login.locked=Ihr Konto wurde gesperrt. -login.signinTitle=Bitte melden Sie sich an +login.signinTitle=Bitte melden Sie sich an. +login.ssoSignIn=Anmeldung per Single Sign-On +login.oauth2AutoCreateDisabled=OAUTH2 Benutzer automatisch erstellen deaktiviert #auto-redact @@ -414,7 +452,7 @@ autoRedact.textsToRedactLabel=Zu zensierender Text (einer pro Zeile) autoRedact.textsToRedactPlaceholder=z.B. \nVertraulich \nStreng geheim autoRedact.useRegexLabel=Regex verwenden autoRedact.wholeWordSearchLabel=Ganzes Wort suchen -autoRedact.customPaddingLabel=Benutzerdefinierte Extra-Padding +autoRedact.customPaddingLabel=Zensierten Bereich vergrößern autoRedact.convertPDFToImageLabel=PDF in PDF-Bild konvertieren (zum Entfernen von Text hinter dem Kasten) autoRedact.submitButton=Zensieren @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Zusammenfassen pageExtracter.title=Seiten extrahieren pageExtracter.header=Seiten extrahieren pageExtracter.submit=Extrahieren +pageExtracter.placeholder=(z.B. 1,2,8 oder 4,7,12-16 oder 2n-1) #getPdfInfo @@ -467,16 +506,16 @@ HTMLToPDF.header=HTML zu PDF HTMLToPDF.help=Akzeptiert HTML-Dateien und ZIPs mit html/css/images etc. HTMLToPDF.submit=Konvertieren HTMLToPDF.credit=Verwendet WeasyPrint -HTMLToPDF.zoom=Zoomstufe zur Darstellung der Website. -HTMLToPDF.pageWidth=Breite der Seite in Zentimetern. (Leer auf Standard) -HTMLToPDF.pageHeight=Höhe der Seite in Zentimetern. (Leer auf Standard) -HTMLToPDF.marginTop=Oberer Rand der Seite in Millimetern. (Leer auf Standard) -HTMLToPDF.marginBottom=Unterer Rand der Seite in Millimetern. (Leer auf Standard) -HTMLToPDF.marginLeft=Linker Rand der Seite in Millimetern. (Leer auf Standard) -HTMLToPDF.marginRight=Linker Rand der Seite in Millimetern. (Leer auf Standard) -HTMLToPDF.printBackground=Den Hintergrund der Website rendern. +HTMLToPDF.zoom=Zoomstufe zur Darstellung der Website +HTMLToPDF.pageWidth=Breite der Seite in Zentimetern (Leer auf Standard) +HTMLToPDF.pageHeight=Höhe der Seite in Zentimetern (Leer auf Standard) +HTMLToPDF.marginTop=Oberer Rand der Seite in Millimetern (Leer auf Standard) +HTMLToPDF.marginBottom=Unterer Rand der Seite in Millimetern (Leer auf Standard) +HTMLToPDF.marginLeft=Linker Rand der Seite in Millimetern (Leer auf Standard) +HTMLToPDF.marginRight=Linker Rand der Seite in Millimetern (Leer auf Standard) +HTMLToPDF.printBackground=Den Hintergrund der Website rendern HTMLToPDF.defaultHeader=Standardkopfzeile aktivieren (Name und Seitenzahl) -HTMLToPDF.cssMediaType=CSS-Medientyp der Seite ändern. +HTMLToPDF.cssMediaType=CSS-Medientyp der Seite ändern HTMLToPDF.none=Keine HTMLToPDF.print=Drucken HTMLToPDF.screen=Bildschirm @@ -490,7 +529,7 @@ AddStampRequest.stampText=Stempeltext AddStampRequest.stampImage=Stampelbild AddStampRequest.alphabet=Alphabet AddStampRequest.fontSize=Schriftart/Bildgröße -AddStampRequest.rotation=Rotation +AddStampRequest.rotation=Drehung AddStampRequest.opacity=Deckkraft AddStampRequest.position=Position AddStampRequest.overrideX=X-Koordinate überschreiben @@ -577,8 +616,8 @@ pageLayout.submit=Abschicken #scalePages scalePages.title=Seitengröße anpassen scalePages.header=Seitengröße anpassen -scalePages.pageSize=Format der Seiten des Dokuments. -scalePages.scaleFactor=Zoomstufe (Ausschnitt) einer Seite. +scalePages.pageSize=Format der Seiten des Dokuments +scalePages.scaleFactor=Zoomstufe (Ausschnitt) einer Seite scalePages.submit=Abschicken @@ -623,6 +662,18 @@ compare.document.1=Dokument 1 compare.document.2=Dokument 2 compare.submit=Vergleichen +#BookToPDF +BookToPDF.title=Bücher und Comics zu PDF +BookToPDF.header=Buch zu PDF +BookToPDF.credit=Verwendet Calibre +BookToPDF.submit=Konvertieren + +#PDFToBook +PDFToBook.title=PDF zu Buch +PDFToBook.header=PDF zu Buch +PDFToBook.selectText.1=Format +PDFToBook.credit=Verwendet Calibre +PDFToBook.submit=Konvertieren #sign sign.title=Signieren @@ -643,6 +694,7 @@ repair.submit=Reparieren #flatten flatten.title=Abflachen flatten.header=PDFs reduzieren +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Abflachen @@ -709,7 +761,7 @@ compress.submit=Komprimieren #Add image addImage.title=Bild hinzufügen addImage.header=Ein Bild einfügen -addImage.everyPage=Jede Seite? +addImage.everyPage=In jede Seite einfügen? addImage.upload=Bild hinzufügen addImage.submit=Bild hinzufügen @@ -726,11 +778,23 @@ merge.submit=Zusammenführen pdfOrganiser.title=Seiten anordnen pdfOrganiser.header=PDF Seitenorganisation pdfOrganiser.submit=Seiten anordnen +pdfOrganiser.mode=Modus +pdfOrganiser.mode.1=Benutzerdefinierte Seitenreihenfolge +pdfOrganiser.mode.2=Umgekehrte Reihenfolge +pdfOrganiser.mode.3=Duplex-Sortierung +pdfOrganiser.mode.4=Heftsortierung +pdfOrganiser.mode.5=Seitenheftungs-Heftsortierung +pdfOrganiser.mode.6=Ungerade-Gerade-Teilung +pdfOrganiser.mode.7=Erste entfernen +pdfOrganiser.mode.8=Letzte entfernen +pdfOrganiser.mode.9=Erste und letzte entfernen +pdfOrganiser.placeholder=(z.B. 1,3,2 oder 4-8,2,10-12 oder 2n-1) #multiTool multiTool.title=PDF-Multitool multiTool.header=PDF-Multitool +multiTool.uploadPrompts=Bitte PDF hochladen #view pdf viewPdf.title=PDF anzeigen @@ -741,26 +805,27 @@ pageRemover.title=Seiten entfernen pageRemover.header=PDF Seiten entfernen pageRemover.pagesToDelete=Seiten zu entfernen (geben Sie eine Kommagetrennte Liste der Seitenzahlen an): pageRemover.submit=Seiten löschen +pageRemover.placeholder=(z.B. 1,2,6 oder 1-10,15-30) #rotate rotate.title=PDF drehen rotate.header=PDF drehen rotate.selectAngle=Wählen Sie den Winkel (in Vielfachen von 90 Grad): -rotate.submit=Drehen +rotate.submit=Herunterladen -#merge +#split-pdfs split.title=PDF aufteilen split.header=PDF aufteilen split.desc.1=Die Nummern, die Sie auswählen, sind die Seitenzahlen, an denen Sie aufteilen möchten. -split.desc.2=So würde die Auswahl von 1,3,7-8 ein 10-seitiges Dokument in 6 separate PDFs aufteilen, mit: +split.desc.2=So würde die Auswahl von 1,3,7-9 ein 10-seitiges Dokument in 6 separate PDFs aufteilen, mit: split.desc.3=Dokument #1: Seite 1 split.desc.4=Dokument #2: Seite 2 und 3 -split.desc.5=Dokument #3: Seite 4, 5 und 6 -split.desc.6=Dokument #4: Seite 7 -split.desc.7=Dokument #5: Seite 8 -split.desc.8=Dokument #6: Seite 9 und 10 +split.desc.5=Dokument #3: Seite 4, 5, 6 und 7 +split.desc.6=Dokument #4: Seite 8 +split.desc.7=Dokument #5: Seite 9 +split.desc.8=Dokument #6: Seite 10 split.splitPages=Geben Sie die Seiten an, an denen aufgeteilt werden soll: split.submit=Aufteilen @@ -828,6 +893,8 @@ watermark.selectText.7=Deckkraft (0% - 100 %): watermark.selectText.8=Wasserzeichen Typ: watermark.selectText.9=Wasserzeichen-Bild: watermark.submit=Wasserzeichen hinzufügen +watermark.type.1=Text +watermark.type.2=Bild #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF zu PDF/A pdfToPDFA.header=PDF zu PDF/A pdfToPDFA.credit=Dieser Dienst verwendet OCRmyPDF für die PDF/A-Konvertierung pdfToPDFA.submit=Konvertieren +pdfToPDFA.tip=Dieser Dienst kann nur einzelne Eingangsdateien verarbeiten. +pdfToPDFA.outputFormat=Ausgabeformat #PDFToWord @@ -906,15 +975,15 @@ PDFToText.submit=Konvertieren #PDFToHTML -PDFToHTML.title=PDF in HTML -PDFToHTML.header=PDF in HTML -PDFToHTML.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung. +PDFToHTML.title=PDF zu HTML +PDFToHTML.header=PDF zu HTML +PDFToHTML.credit=Dieser Dienst verwendet pdftohtml für die Dateikonvertierung. PDFToHTML.submit=Konvertieren #PDFToXML -PDFToXML.title=PDF in XML -PDFToXML.header=PDF in XML +PDFToXML.title=PDF zu XML +PDFToXML.header=PDF zu XML PDFToXML.credit=Dieser Dienst verwendet LibreOffice für die Dateikonvertierung. PDFToXML.submit=Konvertieren @@ -925,6 +994,7 @@ PDFToCSV.prompt=Seite mit der zu extrahierenden Tabelle wählen PDFToCSV.submit=Extrahieren #split-by-size-or-count +split-by-size-or-count.title=PDF nach Größe oder Anzahl teilen split-by-size-or-count.header=PDF nach Größe oder Anzahl teilen split-by-size-or-count.type.label=Teil-Modus wählen split-by-size-or-count.type.size=Nach Größe @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertikale Teiler split-by-sections.horizontal.placeholder=Anzahl horizontaler Teiler eingeben split-by-sections.vertical.placeholder=Anzahl vertikaler Teiler eingeben split-by-sections.submit=PDF teilen +split-by-sections.merge=In eine PDF zusammenfügen + + +#printFile +printFile.title=Datei drucken +printFile.header=Datei an Drucker senden +printFile.selectText.1=Wähle die auszudruckende Datei +printFile.selectText.2=Druckernamen eingeben +printFile.submit=Drucken #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=Lizenz +# error +error.sorry=Entschuldigung für das Problem! +error.needHelp=Brauchst du Hilfe / Ein Problem gefunden? +error.contactTip=Wenn du weiterhin Probleme hast, zögere nicht, uns um Hilfe zu bitten. Du kannst ein Ticket auf unserer GitHub-Seite einreichen oder uns über Discord kontaktieren: +error.404.head=404 - Seite nicht gefunden | Ups, wir sind im Code gestolpert! +error.404.1=Wir können die gesuchte Seite nicht finden. +error.404.2=Etwas ist schiefgelaufen +error.github=Ein Ticket auf GitHub einreichen +error.showStack=Stack-Trace anzeigen +error.copyStack=Stack-Trace kopieren +error.githubSubmit=GitHub - Ein Ticket einreichen +error.discordSubmit=Discord - Unterstützungsbeitrag einreichen + diff --git a/src/main/resources/messages_el_GR.properties b/src/main/resources/messages_el_GR.properties index fa3c4b65..e4d2537c 100644 --- a/src/main/resources/messages_el_GR.properties +++ b/src/main/resources/messages_el_GR.properties @@ -1,80 +1,100 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) language.direction=ltr -pdfPrompt=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE PDF(s) -multiPdfPrompt=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE PDFs (2+) -multiPdfDropPrompt=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE (\u03AE \u03C4\u03C1\u03AC\u03B2\u03B7\u03B3\u03BC\u03B1 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 \u03BA\u03B1\u03B9 \u03B1\u03C0\u03CC\u03B8\u03B5\u03C3\u03B7) \u03CC\u03BB\u03C9\u03BD \u03C4\u03C9\u03BD PDF \u03C0\u03BF\u03C5 \u03C7\u03C1\u03B5\u03B9\u03AC\u03B6\u03B5\u03C3\u03C4\u03B5 -imgPrompt=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u0395\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2(\u0395\u03B9\u03BA\u03CC\u03BD\u03C9\u03BD) -genericSubmit=\u03A5\u03C0\u03BF\u03B2\u03BF\u03BB\u03AE -processTimeWarning=\u03A0\u03C1\u03BF\u03C3\u03BF\u03C7\u03AE: \u0391\u03C5\u03C4\u03AE \u03B7 \u03B4\u03B9\u03B1\u03B4\u03B9\u03BA\u03B1\u03C3\u03AF\u03B1 \u03BC\u03C0\u03BF\u03C1\u03B5\u03AF \u03BD\u03B1 \u03B4\u03B9\u03B1\u03C1\u03BA\u03AD\u03C3\u03B5\u03B9 \u03AD\u03C9\u03C2 \u03BA\u03B1\u03B9 \u03AD\u03BD\u03B1 \u03BB\u03B5\u03C0\u03C4\u03CC \u03B1\u03BD\u03AC\u03BB\u03BF\u03B3\u03B1 \u03BC\u03B5 \u03C4\u03BF \u03BC\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 \u03C4\u03BF\u03C5 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 -pageOrderPrompt=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03C3\u03BC\u03AD\u03BD\u03B7 \u03A3\u03B5\u03B9\u03C1\u03AC \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 (\u03A0\u03C1\u03BF\u03C3\u03B8\u03AD\u03C3\u03C4\u03B5 \u03BC\u03AF\u03B1 \u03BB\u03AF\u03C3\u03C4\u03B5 \u03B1\u03C0\u03BF \u03B1\u03C1\u03B9\u03B8\u03BC\u03BF\u03CD\u03C2 \u03C3\u03B5\u03BB\u03B9\u03B4\u03CE\u03BD, \u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03AD\u03BD\u03B5\u03C2 \u03BC\u03B5 \u03BA\u03CC\u03BC\u03BC\u03B1 \u03AE \u03C3\u03C5\u03BD\u03B1\u03C1\u03C4\u03AE\u03C3\u03B5\u03B9\u03C2 \u03CC\u03C0\u03C9\u03C2 2n+1) : -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : -goToPage=Go -true=\u0391\u03BB\u03B7\u03B8\u03AD\u03C2 -false=\u039B\u03B1\u03BD\u03B8\u03B1\u03C3\u03BC\u03AD\u03BD\u03BF -unknown=\u0386\u03B3\u03BD\u03C9\u03C3\u03C4\u03BF -save=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 -close=\u039A\u03BB\u03B5\u03AF\u03C3\u03B9\u03BC\u03BF -filesSelected=\u03B1\u03C1\u03C7\u03B5\u03AF\u03B1 \u03C0\u03BF\u03C5 \u03B5\u03C0\u03B9\u03BB\u03AD\u03C7\u03B8\u03B7\u03BA\u03B1\u03BD -noFavourites=\u039A\u03B1\u03BD\u03AD\u03BD\u03B1 \u03B1\u03B3\u03B1\u03C0\u03AE\u03BC\u03B5\u03BD\u03BF \u03B4\u03B5\u03BD \u03AD\u03C7\u03B5\u03B9 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B5\u03B8\u03B5\u03AF -bored=\u0392\u03B1\u03C1\u03B9\u03AD\u03C3\u03C4\u03B5 \u03BD\u03B1 \u03C0\u03B5\u03C1\u03B9\u03BC\u03AD\u03BD\u03B5\u03C4\u03B5; -alphabet=\u0391\u03BB\u03C6\u03AC\u03B2\u03B7\u03C4\u03BF -downloadPdf=\u039A\u03B1\u03C4\u03AD\u03B2\u03B1\u03C3\u03BC\u03B1 \u03C4\u03BF\u03C5 PDF -text=\u039A\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF -font=\u0393\u03C1\u03B1\u03BC\u03BC\u03B1\u03C4\u03BF\u03C3\u03B5\u03B9\u03C1\u03AC -selectFillter=-- \u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE -- -pageNum=\u0391\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 -sizes.small=\u039C\u03B9\u03BA\u03C1\u03CC -sizes.medium=\u039C\u03B5\u03C3\u03B1\u03AF\u03BF -sizes.large=\u039C\u03B5\u03B3\u03AC\u03BB\u03BF -sizes.x-large=\u03A0\u03BF\u03BB\u03CD \u039C\u03B5\u03B3\u03AC\u03BB\u03BF -error.pdfPassword=\u03A4\u03BF PDF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF \u03B5\u03AF\u03BD\u03B1\u03B9 \u03BA\u03BB\u03B5\u03B9\u03B4\u03C9\u03BC\u03AD\u03BD\u03BF \u03BC\u03B5 \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC \u03BA\u03B1\u03B9 \u03B5\u03AF\u03C4\u03B5 \u03B4\u03B5\u03BD \u03AD\u03C7\u03B5\u03C4\u03B5 \u03B5\u03B9\u03C3\u03AC\u03B3\u03B5\u03B9 \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC, \u03B5\u03AF\u03C4\u03B5 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03BB\u03B1\u03BD\u03B8\u03B1\u03C3\u03BC\u03AD\u03BD\u03BF\u03C2 -delete=\u0394\u03B9\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE -username=\u038C\u03BD\u03BF\u03BC\u03B1 \u03A7\u03C1\u03AE\u03C3\u03C4\u03B7 -password=\u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 -welcome=\u039A\u03B1\u03BB\u03C9\u03C2 \u0389\u03BB\u03B8\u03B1\u03C4\u03B5 +pdfPrompt=Επιλογή PDF(s) +multiPdfPrompt=Επιλογή PDFs (2+) +multiPdfDropPrompt=Επιλογή (ή τράβηγμα αρχείου και απόθεση) όλων των PDF που χρειάζεστε +imgPrompt=Επιλογή Εικόνας(Εικόνων) +genericSubmit=Υποβολή +processTimeWarning=Προσοχή: Αυτή η διαδικασία μπορεί να διαρκέσει έως και ένα λεπτό ανάλογα με το μέγεθος του αρχείου +pageOrderPrompt=Προσαρμοσμένη Σειρά Σελίδας (Προσθέστε μία λίστα απο αριθμούς σελιδών, χωρισμένες με κόμμα ή συναρτήσεις όπως 2n+1) : +pageSelectionPrompt=Προσαρμοσμένη Επιλογή Σελίδας (Προσθέστε μία λίστα απο αριθμούς σελιδών, χωρισμένες με κόμμα 1,5,6 ή συναρτήσεις όπως 2n+1): +goToPage=Πήγαινε +true=Αληθές +false=Λανθασμένο +unknown=Άγνωστο +save=Αποθήκευση +saveToBrowser=Αποθήκευση στο Browser +close=Κλείσιμο +filesSelected=αρχεία που επιλέχθηκαν +noFavourites=Κανένα αγαπήμενο δεν έχει προστεθεί +downloadComplete=Η Λήψη Ολοκληρώθηκε +bored=Βαριέστε να περιμένετε; +alphabet=Αλφάβητο +downloadPdf=Κατέβασμα του PDF +text=Κείμενο +font=Γραμματοσειρά +selectFillter=-- Επιλογή -- +pageNum=Αριθμός Σελίδας +sizes.small=Μικρό +sizes.medium=Μεσαίο +sizes.large=Μεγάλο +sizes.x-large=Πολύ Μεγάλο +error.pdfPassword=Το PDF αρχείο είναι κλειδωμένο με κωδικό και είτε δεν έχετε εισάγει κωδικό, είτε είναι λανθασμένος +delete=Διαγραφή +username=Όνομα Χρήστη +password=Κωδικός +welcome=Καλως Ήλθατε property=Property -black=\u039C\u03B1\u03CD\u03C1\u03BF -white=\u0386\u03C3\u03C0\u03C1\u03BF -red=\u039A\u03CC\u03BA\u03BA\u03B9\u03BD\u03BF -green=\u03A0\u03C1\u03AC\u03C3\u03B9\u03BD\u03BF -blue=\u039C\u03C0\u03BB\u03AD -custom=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE... -WorkInProgess=Work in progress, May not work or be buggy, Please report any problems! -poweredBy=Powered by -yes=Yes -no=No -changedCredsMessage=\u03A4\u03B1 \u03B4\u03B9\u03B1\u03C0\u03B9\u03C3\u03C4\u03B5\u03C5\u03C4\u03AE\u03C1\u03B9\u03B1 \u03AD\u03C7\u03BF\u03C5\u03BD \u03B1\u03BB\u03BB\u03AC\u03BE\u03B5\u03B9! -notAuthenticatedMessage=\u039F \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7\u03C2 \u03B4\u03B5\u03BD \u03AD\u03C7\u03B5\u03B9 \u03B1\u03C5\u03B8\u03B5\u03BD\u03C4\u03B9\u03BA\u03BF\u03C0\u03BF\u03B9\u03B7\u03B8\u03B5\u03AF. -userNotFoundMessage=\u039F \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7\u03C2 \u03B4\u03B5\u03BD \u03B2\u03C1\u03AD\u03B8\u03B7\u03BA\u03B5. -incorrectPasswordMessage=\u039F \u03C4\u03C1\u03AD\u03C7\u03C9\u03BD \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03BB\u03B1\u03BD\u03B8\u03B1\u03C3\u03BC\u03AD\u03BD\u03BF\u03C2. -usernameExistsMessage=\u03A4\u03BF \u03BD\u03AD\u03BF \u03CC\u03BD\u03BF\u03BC\u03B1 \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7 \u03C5\u03C0\u03AC\u03C1\u03C7\u03B5\u03B9 \u03AE\u03B4\u03B7. +black=Μαύρο +white=Άσπρο +red=Κόκκινο +green=Πράσινο +blue=Μπλέ +custom=Προσαρμογή... +WorkInProgess=Εργασία σε εξέλιξη, Ενδέχεται να μην λειτουργεί ή να έχει λάθη, Παρακαλώ αναφέρετε τυχόν προβλήματα! +poweredBy=Τροφοδοτείται από +yes=Ναι +no=Όχι +changedCredsMessage=Τα διαπιστευτήρια έχουν αλλάξει! +notAuthenticatedMessage=Ο χρήστης δεν έχει αυθεντικοποιηθεί. +userNotFoundMessage=Ο χρήστης δεν βρέθηκε. +incorrectPasswordMessage=Ο τρέχων κωδικός πρόσβασης είναι λανθασμένος. +usernameExistsMessage=Το νέο όνομα χρήστη υπάρχει ήδη. +invalidUsernameMessage=Μη έγκυρο όνομα χρήστη, το όνομα χρήστη πρέπει να περιέχει μόνο αλφαβητικούς χαρακτήρες και αριθμούς. +deleteCurrentUserMessage=Δεν είναι δυνατή η διαγραφή του τρέχοντος συνδεδεμένου χρήστη. +deleteUsernameExistsMessage=Το όνομα χρήστη δεν υπάρχει και δεν μπορεί να διαγραφεί. +downgradeCurrentUserMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη +downgradeCurrentUserLongMessage=Δεν είναι δυνατή η υποβάθμιση του ρόλου του τρέχοντος χρήστη. Ως εκ τούτου, ο τρέχων χρήστης δεν θα εμφανίζεται. +error=Σφάλμα +oops=Ωχ! +help=Βοήθεια +goHomepage=Πήγαινε στην Αρχική Σελίδα +joinDiscord=Εγγραφείτε στο Server του Discord μας +seeDockerHub=Βλέπε το Docker Hub +visitGithub=Επισκεφθείτε το Αποθετήριο του Github +donate=Δωρισε +color=Χρώμα +sponsor=Yποστηρικτής + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Μενού Pipeline (Beta) pipeline.uploadButton=Upload Custom -pipeline.configureButton=Configure +pipeline.configureButton=Διαμόρφωσε pipeline.defaultOption=Custom -pipeline.submitButton=Submit +pipeline.submitButton=Υποβολή +pipeline.help=Βοήθεια για το Pipeline +pipeline.scanHelp=Βοήθεια για Σάρωση Φακέλων ###################### # Pipeline Options # ###################### -pipelineOptions.header=Pipeline Configuration -pipelineOptions.pipelineNameLabel=Pipeline Name -pipelineOptions.saveSettings=Save Operation Settings -pipelineOptions.pipelineNamePrompt=Enter pipeline name here -pipelineOptions.selectOperation=Select Operation -pipelineOptions.addOperationButton=Add operation +pipelineOptions.header=Διαμόρφωση Pipeline +pipelineOptions.pipelineNameLabel=Όνομα Pipeline +pipelineOptions.saveSettings=Αποθήκευση Ρυθμίσεων Λειτουργίας +pipelineOptions.pipelineNamePrompt=Εισαγάγετε το όνομα του pipeline εδώ +pipelineOptions.selectOperation=Επιλέξτε Λειτουργία +pipelineOptions.addOperationButton=Προσθήκη λειτουργίας pipelineOptions.pipelineHeader=Pipeline: -pipelineOptions.saveButton=Download -pipelineOptions.validateButton=Validate +pipelineOptions.saveButton=Λήψη +pipelineOptions.validateButton=Επικυρώνω @@ -82,484 +102,503 @@ pipelineOptions.validateButton=Validate ############# # NAVBAR # ############# -navbar.convert=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE -navbar.security=\u0391\u03C3\u03C6\u03AC\u03BB\u03B5\u03B9\u03B1 -navbar.other=\u0394\u03B9\u03AC\u03C6\u03BF\u03C1\u03B1 -navbar.darkmode=\u039C\u03B1\u03CD\u03C1\u03BF \u0398\u03AD\u03BC\u03B1 -navbar.pageOps=\u039B\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03AF\u03B5\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 -navbar.settings=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 +navbar.convert=Μετατροπή +navbar.security=Ασφάλεια +navbar.other=Διάφορα +navbar.darkmode=Μαύρο Θέμα +navbar.pageOps=Λειτουργίες σελίδας +navbar.settings=Ρυθμίσεις ############# # SETTINGS # ############# -settings.title=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 -settings.update=\u03A5\u03C0\u03AC\u03C1\u03C7\u03B5\u03B9 \u03B4\u03B9\u03B1\u03B8\u03AD\u03C3\u03B9\u03BC\u03B7 \u03B5\u03BD\u03B7\u03BC\u03AD\u03C1\u03C9\u03C3\u03B7 -settings.appVersion=\u0388\u03BA\u03B4\u03BF\u03C3\u03B7 \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE\u03C2: App Version: -settings.downloadOption.title=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03B5\u03C4\u03B5 \u03C4\u03B7\u03BD \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03BB\u03AE\u03C8\u03B7\u03C2 (\u0393\u03B9\u03B1 \u03BB\u03AE\u03C8\u03B5\u03B9\u03C2 \u03BC\u03B5\u03BC\u03BF\u03BD\u03C9\u03BC\u03AD\u03BD\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD \u03C7\u03C9\u03C1\u03AF\u03C2 zip): -settings.downloadOption.1=\u0386\u03BD\u03BF\u03B9\u03B3\u03BC\u03B1 \u03C3\u03C4\u03BF \u03AF\u03B4\u03B9\u03BF \u03C0\u03B1\u03C1\u03AC\u03B8\u03C5\u03C1\u03BF -settings.downloadOption.2=\u0386\u03BD\u03BF\u03B9\u03B3\u03BC\u03B1 \u03C3\u03B5 \u03BD\u03AD\u03BF \u03C0\u03B1\u03C1\u03AC\u03B8\u03C5\u03C1\u03BF -settings.downloadOption.3=\u039B\u03AE\u03C8\u03B7 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 -settings.zipThreshold=Zip \u03B1\u03C1\u03C7\u03B5\u03AF\u03B1 \u03CC\u03C4\u03B1\u03BD \u03BF \u03B1\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03C4\u03C9\u03BD \u03BB\u03B7\u03C6\u03B8\u03AD\u03BD\u03C4\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD \u03B5\u03AF\u03BD\u03B1\u03B9 \u03C0\u03BF\u03BB\u03CD \u03BC\u03B5\u03B3\u03AC\u03BB\u03BF\u03C2 -settings.signOut=\u0391\u03C0\u03BF\u03C3\u03CD\u03BD\u03B4\u03B5\u03C3\u03B7 -settings.accountSettings=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 \u039B\u03BF\u03B3\u03B1\u03C1\u03B9\u03B1\u03C3\u03BC\u03BF\u03CD +settings.title=Ρυθμίσεις +settings.update=Υπάρχει διαθέσιμη ενημέρωση +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. +settings.appVersion=Έκδοση εφαρμογής: +settings.downloadOption.title=Επιλέξετε την επιλογή λήψης (Για λήψεις μεμονωμένων αρχείων χωρίς zip): +settings.downloadOption.1=Άνοιγμα στο ίδιο παράθυρο +settings.downloadOption.2=Άνοιγμα σε νέο παράθυρο +settings.downloadOption.3=Λήψη αρχείου +settings.zipThreshold=Αρχεία Zip όταν ο αριθμός των ληφθέντων αρχείων είναι πολύ μεγάλος +settings.signOut=Αποσύνδεση +settings.accountSettings=Ρυθμίσεις Λογαριασμού +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs + +changeCreds.title=Αλλαγή Διαπιστευτηρίων +changeCreds.header=Ενημέρωση των λεπτομερειών του Λογαριασμού σας +changeCreds.changePassword=Χρησιμοποιείτε προεπιλεγμένα διαπιστευτήρια σύνδεσης. Εισαγάγετε έναν νέο κωδικό πρόσβασης +changeCreds.newUsername=Νέο Όνομα Χρήστη +changeCreds.oldPassword=Τρέχων Κωδικός Πρόσβασης +changeCreds.newPassword=Νέος Κωδικός Πρόσβασης +changeCreds.confirmNewPassword=Επιβεβαίωση Νέου Κωδικού Πρόσβασης +changeCreds.submit=Υποβολή Αλλαγών -changeCreds.title=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u0394\u03B9\u03B1\u03C0\u03B9\u03C3\u03C4\u03B5\u03C5\u03C4\u03B7\u03C1\u03AF\u03C9\u03BD -changeCreds.header=\u0395\u03BD\u03B7\u03BC\u03AD\u03C1\u03C9\u03C3\u03B7 \u03C4\u03C9\u03BD \u03BB\u03B5\u03C0\u03C4\u03BF\u03BC\u03B5\u03C1\u03B5\u03B9\u03CE\u03BD \u03C4\u03BF\u03C5 \u039B\u03BF\u03B3\u03B1\u03C1\u03B9\u03B1\u03C3\u03BC\u03BF\u03CD \u03C3\u03B1\u03C2 -changeCreds.changeUserAndPassword=\u03A7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF\u03C4\u03B5 \u03C4\u03B1 \u03C0\u03C1\u03BF\u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03B1 \u03B4\u03B9\u03B1\u03C0\u03B9\u03C3\u03C4\u03B5\u03C5\u03C4\u03AE\u03C1\u03B9\u03B1 \u03C3\u03CD\u03BD\u03B4\u03B5\u03C3\u03B7\u03C2. \u03A0\u03B1\u03C1\u03B1\u03BA\u03B1\u03BB\u03CE \u03B5\u03B9\u03C3\u03AC\u03B3\u03B5\u03C4\u03B5 \u03BD\u03AD\u03BF \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 (\u03BA\u03B1\u03B9 \u03CC\u03BD\u03BF\u03BC\u03B1 \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7 \u03B1\u03BD \u03C4\u03BF \u03B5\u03C0\u03B9\u03B8\u03C5\u03BC\u03B5\u03AF\u03C4\u03B5) -changeCreds.newUsername=\u039D\u03AD\u03BF \u038C\u03BD\u03BF\u03BC\u03B1 \u03A7\u03C1\u03AE\u03C3\u03C4\u03B7 -changeCreds.oldPassword=\u03A4\u03C1\u03AD\u03C7\u03C9\u03BD \u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 \u03A0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 -changeCreds.newPassword=\u039D\u03AD\u03BF\u03C2 \u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 \u03A0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 -changeCreds.confirmNewPassword=\u0395\u03C0\u03B9\u03B2\u03B5\u03B2\u03B1\u03AF\u03C9\u03C3\u03B7 \u039D\u03AD\u03BF\u03C5 \u039A\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD \u03A0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 -changeCreds.submit=\u03A5\u03C0\u03BF\u03B2\u03BF\u03BB\u03AE \u0391\u03BB\u03BB\u03B1\u03B3\u03CE\u03BD - - - -account.title=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 \u039B\u03BF\u03B3\u03B1\u03C1\u03B9\u03B1\u03C3\u03BC\u03BF\u03CD -account.accountSettings=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 \u039B\u03BF\u03B3\u03B1\u03C1\u03B9\u03B1\u03C3\u03BC\u03BF\u03CD -account.adminSettings=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 \u0394\u03B9\u03B1\u03C7\u03B5\u03B9\u03C1\u03B9\u03C3\u03C4\u03AE - \u03A0\u03C1\u03BF\u03B2\u03BF\u03BB\u03AE \u03BA\u03B1\u03B9 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C7\u03C1\u03B7\u03C3\u03C4\u03CE\u03BD -account.userControlSettings=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 \u03A7\u03B5\u03B9\u03C1\u03B9\u03C3\u03BC\u03BF\u03CD \u03A7\u03C1\u03B7\u03C3\u03C4\u03CE\u03BD -account.changeUsername=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u039F\u03BD\u03CC\u03BC\u03B1\u03C4\u03BF\u03C2 \u03A7\u03C1\u03AE\u03C3\u03C4\u03B7 -account.newUsername=\u039d\u03ad\u03bf \u038c\u03bd\u03bf\u03bc\u03b1 \u03a7\u03c1\u03ae\u03c3\u03c4\u03b7 -account.password=\u0395\u03C0\u03B9\u03B2\u03B5\u03B2\u03B1\u03AF\u03C9\u03C3\u03B7 \u039A\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD \u03A0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 -account.oldPassword=\u03A0\u03B1\u03BB\u03B9\u03CC\u03C2 \u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 \u03A0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 -account.newPassword=\u039D\u03AD\u03BF\u03C2 \u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 \u03A0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 -account.changePassword=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u039A\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD \u03A0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 -account.confirmNewPassword=\u0395\u03C0\u03B9\u03B2\u03B5\u03B2\u03B1\u03AF\u03C9\u03C3\u03B7 \u039D\u03AD\u03BF\u03C5 \u039A\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD -account.signOut=\u0391\u03C0\u03BF\u03C3\u03CD\u03BD\u03B4\u03B5\u03C3\u03B7 -account.yourApiKey=\u03A4\u03BF \u03BA\u03BB\u03B5\u03B9\u03B4\u03AF \u03C3\u03B1\u03C2 \u03B3\u03B9\u03B1 \u03C4\u03B7 \u03B4\u03B9\u03B5\u03C0\u03B1\u03C6\u03AE \u03C0\u03C1\u03BF\u03B3\u03C1\u03B1\u03BC\u03BC\u03B1\u03C4\u03B9\u03C3\u03BC\u03BF\u03CD \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03CE\u03BD (API key) -account.syncTitle=\u03A3\u03C5\u03B3\u03C7\u03C1\u03BF\u03BD\u03B9\u03C3\u03BC\u03CC\u03C2 \u03C4\u03C9\u03BD \u03C1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03C9\u03BD \u03C4\u03BF\u03C5 \u03C6\u03C5\u03BB\u03BB\u03BF\u03BC\u03B5\u03C4\u03C1\u03B7\u03C4\u03AE (Web Browser) \u03BC\u03B5 \u03C4\u03BF\u03BD \u03BB\u03BF\u03B3\u03B1\u03C1\u03B9\u03B1\u03C3\u03BC\u03CC -account.settingsCompare=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7 \u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03C9\u03BD: +account.title=Ρυθμίσεις Λογαριασμού +account.accountSettings=Ρυθμίσεις Λογαριασμού +account.adminSettings=Ρυθμίσεις Διαχειριστή - Προβολή και προσθήκη χρηστών +account.userControlSettings=Ρυθμίσεις Χειρισμού Χρηστών +account.changeUsername=Αλλαγή Ονόματος Χρήστη +account.newUsername=Νέο Όνομα Χρήστη +account.password=Επιβεβαίωση Κωδικού Πρόσβασης +account.oldPassword=Παλιός Κωδικός Πρόσβασης +account.newPassword=Νέος Κωδικός Πρόσβασης +account.changePassword=Αλλαγή Κωδικού Πρόσβασης +account.confirmNewPassword=Επιβεβαίωση Νέου Κωδικού +account.signOut=Αποσύνδεση +account.yourApiKey=Το κλειδί σας για τη διεπαφή προγραμματισμού εφαρμογών (API key) +account.syncTitle=Συγχρονισμός των ρυθμίσεων του φυλλομετρητή (Web Browser) με τον λογαριασμό +account.settingsCompare=Σύγκριση Ρυθμίσεων: account.property=Property -account.webBrowserSettings=\u03A1\u03CD\u03B8\u03BC\u03B9\u03C3\u03B7 \u03C6\u03C5\u03BB\u03BB\u03BF\u03BC\u03B5\u03C4\u03C1\u03B7\u03C4\u03AE (Web Browser) -account.syncToBrowser=\u03A3\u03C5\u03B3\u03C7\u03C1\u03BF\u03BD\u03B9\u03C3\u03BC\u03CC\u03C2 \u039B\u03BF\u03B3\u03B1\u03C1\u03B9\u03B1\u03C3\u03BC\u03BF\u03CD -> \u03A6\u03C5\u03BB\u03BB\u03BF\u03BC\u03B5\u03C4\u03C1\u03B7\u03C4\u03AE (Web Browser) -account.syncToAccount=\u03A3\u03C5\u03B3\u03C7\u03C1\u03BF\u03BD\u03B9\u03C3\u03BC\u03CC\u03C2 \u039B\u03BF\u03B3\u03B1\u03C1\u03B9\u03B1\u03C3\u03BC\u03BF\u03CD <- \u03A6\u03C5\u03BB\u03BB\u03BF\u03BC\u03B5\u03C4\u03C1\u03B7\u03C4\u03AE (Web Browser) +account.webBrowserSettings=Ρύθμιση φυλλομετρητή (Web Browser) +account.syncToBrowser=Συγχρονισμός Λογαριασμού -> Φυλλομετρητή (Web Browser) +account.syncToAccount=Συγχρονισμός Λογαριασμού <- Φυλλομετρητή (Web Browser) -adminUserSettings.title=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 \u03B5\u03BB\u03AD\u03B3\u03C7\u03BF\u03C5 \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7 -adminUserSettings.header=\u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 \u03B5\u03BB\u03AD\u03B3\u03C7\u03BF\u03C5 \u0394\u03B9\u03B1\u03C7\u03B5\u03B9\u03C1\u03B9\u03C3\u03C4\u03AE -adminUserSettings.admin=\u0394\u03B9\u03B1\u03C7\u03B5\u03B9\u03C1\u03B9\u03C3\u03C4\u03AE\u03C2 -adminUserSettings.user=\u03A7\u03C1\u03AE\u03C3\u03C4\u03B7\u03C2 -adminUserSettings.addUser=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03BD\u03AD\u03BF\u03C5 \u03A7\u03C1\u03AE\u03C3\u03C4\u03B7 -adminUserSettings.roles=\u03A1\u03CC\u03BB\u03BF\u03B9 -adminUserSettings.role=\u03A1\u03CC\u03BB\u03BF\u03C2 -adminUserSettings.actions=\u0395\u03BD\u03AD\u03C1\u03B3\u03B5\u03B9\u03B5\u03C2 -adminUserSettings.apiUser=\u03A0\u03B5\u03C1\u03B9\u03BF\u03C1\u03B9\u03C3\u03BC\u03AD\u03BD\u03BF\u03C2 \u03A7\u03C1\u03AE\u03C3\u03C4\u03B7\u03C2 \u03B3\u03B9\u03B1 \u03B4\u03B9\u03B5\u03C0\u03B1\u03C6\u03AE \u03C0\u03C1\u03BF\u03B3\u03C1\u03B1\u03BC\u03BC\u03B1\u03C4\u03B9\u03C3\u03BC\u03BF\u03CD \u03B5\u03C6\u03B1\u03C1\u03BC\u03BF\u03B3\u03CE\u03BD (API User) -adminUserSettings.webOnlyUser=\u03A7\u03C1\u03AE\u03C3\u03C4\u03B7\u03C2 \u03BC\u03CC\u03BD\u03BF \u0399\u03C3\u03C4\u03BF\u03CD -adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=\u0391\u03BD\u03B1\u03B3\u03BA\u03AC\u03C3\u03C4\u03B5 \u03C4\u03BF\u03BD \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7 \u03BD\u03B1 \u03B1\u03BB\u03BB\u03AC\u03BE\u03B5\u03B9 \u03C4\u03BF \u03CC\u03BD\u03BF\u03BC\u03B1 \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7/\u03BA\u03C9\u03B4\u03B9\u03BA\u03CC \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03BA\u03B1\u03C4\u03AC \u03C4\u03B7 \u03C3\u03CD\u03BD\u03B4\u03B5\u03C3\u03B7 -adminUserSettings.submit=\u0391\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03A7\u03C1\u03AE\u03C3\u03C4\u03B7 +adminUserSettings.title=Ρυθμίσεις ελέγχου χρήστη +adminUserSettings.header=Ρυθμίσεις ελέγχου Διαχειριστή +adminUserSettings.admin=Διαχειριστής +adminUserSettings.user=Χρήστης +adminUserSettings.addUser=Προσθήκη νέου Χρήστη +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.roles=Ρόλοι +adminUserSettings.role=Ρόλος +adminUserSettings.actions=Ενέργειες +adminUserSettings.apiUser=Περιορισμένος Χρήστης για διεπαφή προγραμματισμού εφαρμογών (API User) +adminUserSettings.extraApiUser=Πρόσθετος Περιορισμένος Χρήστης για Διεπαφή Προγραμματισμού Εφαρμογών (API User) +adminUserSettings.webOnlyUser=Χρήστης μόνο Ιστού +adminUserSettings.demoUser=Demo χρήστης (Χωρίς προσαρμοσμένες ρυθμίσεις) +adminUserSettings.internalApiUser=Εσωτερικός API χρήστης +adminUserSettings.forceChange=Αναγκάστε τον χρήστη να αλλάξει το όνομα χρήστη/κωδικό πρόσβασης κατά τη σύνδεση +adminUserSettings.submit=Αποθήκευση Χρήστη +adminUserSettings.changeUserRole=Αλλαγή ρόλου χρήστη ############# # HOME-PAGE # ############# -home.desc=\u0397 \u03C4\u03BF\u03C0\u03B9\u03BA\u03AC \u03C6\u03B9\u03BB\u03BF\u03BE\u03B5\u03BD\u03BF\u03CD\u03BC\u03B5\u03BD\u03B7 one-stop-shop \u03C3\u03B1\u03C2 \u03B3\u03B9\u03B1 \u03CC\u03BB\u03B5\u03C2 \u03C4\u03B9\u03C2 \u03B1\u03BD\u03AC\u03B3\u03BA\u03B5\u03C2 \u03C3\u03B1\u03C2 \u03C3\u03B5 PDF. -home.searchBar=Search for features... +home.desc=Η τοπικά φιλοξενούμενη one-stop-shop σας για όλες τις ανάγκες σας σε PDF. +home.searchBar=Αναζήτηση για χαρακτηριστικά... -home.viewPdf.title=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 PDF -home.viewPdf.desc=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7, \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C3\u03C7\u03B5\u03B4\u03AF\u03BF\u03C5, \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03BA\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5 \u03AE \u03B5\u03B9\u03BA\u03CC\u03BD\u03C9\u03BD +home.viewPdf.title=Εμφάνιση PDF +home.viewPdf.desc=Εμφάνιση, προσθήκη σχεδίου, προσθήκη κειμένου ή εικόνων viewPdf.tags=view,read,annotate,text,image -home.multiTool.title=PDF \u03A0\u03BF\u03BB\u03C5\u03B5\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03BF -home.multiTool.desc=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7, \u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE, \u0391\u03BD\u03B1\u03B4\u03B9\u03AC\u03C4\u03B1\u03BE\u03B7 \u03BA\u03B1\u03B9 \u039A\u03B1\u03C4\u03AC\u03C1\u03B3\u03B7\u03C3\u03B7 \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD +home.multiTool.title=PDF Πολυεργαλείο +home.multiTool.desc=Συγχώνευση, Περιστροφή, Αναδιάταξη και Κατάργηση σελίδων multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side,interactive,intractable,move -home.merge.title=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 -home.merge.desc=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 \u03C0\u03BF\u03BB\u03BB\u03CE\u03BD PDF \u03C3\u03B5 \u03AD\u03BD\u03B1 \u03BC\u03B5 \u03B5\u03CD\u03BA\u03BF\u03BB\u03BF \u03C4\u03C1\u03CC\u03C0\u03BF. +home.merge.title=Συγχώνευση +home.merge.desc=Συγχώνευση πολλών PDF σε ένα με εύκολο τρόπο. merge.tags=merge,Page operations,Back end,server side -home.split.title=\u0394\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2 -home.split.desc=\u0394\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2 \u03C4\u03C9\u03BD PDF \u03C3\u03B5 \u03C0\u03BF\u03BB\u03BB\u03AC \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03B1. +home.split.title=Διαχωρισμός +home.split.desc=Διαχωρισμός των PDF σε πολλά έγγραφα. split.tags=Page operations,divide,Multi Page,cut,server side -home.rotate.title=\u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE -home.rotate.desc=\u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE \u03C4\u03C9\u03BD PDF \u03C3\u03B1\u03C2 \u03BC\u03B5 \u03B5\u03CD\u03BA\u03BF\u03BB\u03BF \u03C4\u03C1\u03CC\u03C0\u03BF. -rotate.tags=server side +home.rotate.title=Περιστροφή +home.rotate.desc=Περιστροφή των PDF σας με εύκολο τρόπο. +rotate.tags=από την πλευρά του server -home.imageToPdf.title=\u0395\u03B9\u03BA\u03CC\u03BD\u03B1 \u03C3\u03B5 PDF -home.imageToPdf.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 (PNG, JPEG, GIF) \u03C3\u03B5 PDF. +home.imageToPdf.title=Εικόνα σε PDF +home.imageToPdf.desc=Μετατροπή εικόνας (PNG, JPEG, GIF) σε PDF. imageToPdf.tags=conversion,img,jpg,picture,photo -home.pdfToImage.title=PDF \u03C3\u03B5 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1 -home.pdfToImage.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BD\u03CC\u03C2 PDF \u03C3\u03B5 \u03BC\u03AF\u03B1 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1. (PNG, JPEG, GIF) +home.pdfToImage.title=PDF σε εικόνα +home.pdfToImage.desc=Μετατροπή ενός PDF σε μία εικόνα. (PNG, JPEG, GIF) pdfToImage.tags=conversion,img,jpg,picture,photo -home.pdfOrganiser.title=\u039F\u03C1\u03B3\u03AC\u03BD\u03C9\u03C3\u03B7 -home.pdfOrganiser.desc=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7/\u0391\u03BD\u03B1\u03B4\u03B9\u03AC\u03C4\u03B1\u03BE\u03B7 \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD \u03BC\u03B5 \u03BF\u03C0\u03BF\u03B9\u03B1\u03B4\u03AE\u03C0\u03BF\u03C4\u03B5 \u03C3\u03B5\u03B9\u03C1\u03AC +home.pdfOrganiser.title=Οργάνωση +home.pdfOrganiser.desc=Αφαίρεση/Αναδιάταξη σελίδων με οποιαδήποτε σειρά pdfOrganiser.tags=duplex,even,odd,sort,move -home.addImage.title=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u0395\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 -home.addImage.desc=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03BC\u03B9\u03B1\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 \u03C3\u03B5 \u03BC\u03B9\u03B1 \u03BA\u03B1\u03B8\u03BF\u03C1\u03B9\u03C3\u03BC\u03AD\u03BD\u03B7 \u03B8\u03AD\u03C3\u03B7 \u03C3\u03C4\u03BF PDF +home.addImage.title=Προσθήκη Εικόνας +home.addImage.desc=Προσθήκη μιας εικόνας σε μια καθορισμένη θέση στο PDF addImage.tags=img,jpg,picture,photo -home.watermark.title=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03A5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 -home.watermark.desc=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B5\u03BD\u03CC\u03C2 \u03C0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03C3\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03AC\u03C6\u03B7\u03BC\u03B1\u03C4\u03BF\u03C2 \u03C3\u03C4\u03BF \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03CC PDF. +home.watermark.title=Προσθήκη Υδατογραφήματος +home.watermark.desc=Προσθήκη ενός προσαρμοσμένου υδατογράφηματος στο έγγραφό PDF. watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo -home.permissions.title=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u0394\u03B9\u03BA\u03B1\u03B9\u03C9\u03BC\u03AC\u03C4\u03C9\u03BD -home.permissions.desc=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u03C4\u03C9\u03BD \u0394\u03B9\u03BA\u03B1\u03B9\u03C9\u03BC\u03AC\u03C4\u03C9\u03BD \u03C3\u03C4\u03BF \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF PDF +home.permissions.title=Αλλαγή Δικαιωμάτων +home.permissions.desc=Αλλαγή των Δικαιωμάτων στο έγγραφο PDF permissions.tags=read,write,edit,print -home.removePages.title=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 -home.removePages.desc=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03BC\u03AE \u03B5\u03C0\u03B9\u03B8\u03C5\u03BC\u03B7\u03C4\u03CE\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD \u03B1\u03C0\u03BF \u03C4\u03BF \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF PDF. +home.removePages.title=Αφαίρεση +home.removePages.desc=Αφαίρεση μή επιθυμητών σελίδων απο το έγγραφο PDF. removePages.tags=Remove pages,delete pages -home.addPassword.title=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03BA\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD -home.addPassword.desc=\u039A\u03C1\u03C5\u03C0\u03C4\u03BF\u03B3\u03C1\u03AC\u03C6\u03B7\u03C3\u03B7 - \u03BA\u03BB\u03B5\u03AF\u03B4\u03C9\u03BC\u03B1 \u03C4\u03BF\u03C5 PDF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 \u03BC\u03B5 \u03AD\u03BD\u03B1\u03BD \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC. +home.addPassword.title=Προσθήκη κωδικού +home.addPassword.desc=Κρυπτογράφηση - κλείδωμα του PDF αρχείου με έναν κωδικό. addPassword.tags=secure,security -home.removePassword.title=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u039A\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD -home.removePassword.desc=\u039A\u03B1\u03C4\u03AC\u03C1\u03B3\u03AE\u03C3\u03B7 \u03C4\u03B7\u03C2 \u03C0\u03C1\u03BF\u03C3\u03C4\u03B1\u03C3\u03AF\u03B1\u03C2 \u03BC\u03B5 \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03B1\u03C0\u03CC \u03C4\u03BF \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF PDF. +home.removePassword.title=Αφαίρεση Κωδικού +home.removePassword.desc=Κατάργήση της προστασίας με κωδικό πρόσβασης από το έγγραφο PDF. removePassword.tags=secure,Decrypt,security,unpassword,delete password -home.compressPdfs.title=\u03A3\u03C5\u03BC\u03C0\u03AF\u03B5\u03C3\u03B7 -home.compressPdfs.desc=\u03A3\u03C5\u03BC\u03C0\u03AF\u03B5\u03C3\u03B7 \u03C4\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD PDF \u03B3\u03B9\u03B1 \u03C4\u03B7\u03BD \u03BC\u03B5\u03AF\u03C9\u03C3\u03B7 \u03C4\u03BF\u03C5 \u03BC\u03B5\u03B3\u03AD\u03B8\u03BF\u03C5\u03C2 \u03C4\u03BF\u03C5\u03C2. +home.compressPdfs.title=Συμπίεση +home.compressPdfs.desc=Συμπίεση των αρχείων PDF για την μείωση του μεγέθους τους. compressPdfs.tags=squish,small,tiny -home.changeMetadata.title=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u039C\u03B5\u03C4\u03B1\u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03C9\u03BD -home.changeMetadata.desc=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE/\u039A\u03B1\u03C4\u03AC\u03C1\u03B3\u03B7\u03C3\u03B7/\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03BC\u03B5\u03C4\u03B1\u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03C9\u03BD \u03B1\u03C0\u03CC \u03AD\u03BD\u03B1 \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF PDF. +home.changeMetadata.title=Αλλαγή Μεταδεδομένων +home.changeMetadata.desc=Αλλαγή/Κατάργηση/Προσθήκη μεταδεδομένων από ένα έγγραφο PDF. changeMetadata.tags==Title,author,date,creation,time,publisher,producer,stats -home.fileToPDF.title=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BD\u03CC\u03C2 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 \u03C3\u03B5 PDF -home.fileToPDF.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C3\u03C7\u03B5\u03B4\u03CC\u03BD \u03BF\u03C0\u03BF\u03B9\u03BF\u03C5\u03B4\u03AE\u03C0\u03BF\u03C4\u03B5 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 \u03C3\u03B5 PDF (DOCX, PNG, XLS, PPT, TXT and more) +home.fileToPDF.title=Μετατροπή ενός αρχείου σε PDF +home.fileToPDF.desc=Μετατροπή σχεδόν οποιουδήποτε αρχείου σε PDF (DOCX, PNG, XLS, PPT, TXT and more) fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint -home.ocr.title=\u03BF\u03C0\u03C4\u03B9\u03BA\u03AE \u03B1\u03BD\u03B1\u03B3\u03BD\u03CE\u03C1\u03B9\u03C3\u03B7 \u03C7\u03B1\u03C1\u03B1\u03BA\u03C4\u03AE\u03C1\u03C9\u03BD (OCR) / \u03A3\u03B1\u03C1\u03CE\u03C3\u03B5\u03B9\u03C2 Cleanup -home.ocr.desc=\u03A4\u03BF Cleanup \u03C3\u03B1\u03C1\u03CE\u03BD\u03B5\u03B9 \u03BA\u03B1\u03B9 \u03B1\u03BD\u03B9\u03C7\u03BD\u03B5\u03CD\u03B5\u03B9 \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF \u03B1\u03C0\u03CC \u03B5\u03B9\u03BA\u03CC\u03BD\u03B5\u03C2 \u03BC\u03AD\u03C3\u03B1 \u03C3\u03B5 \u03AD\u03BD\u03B1 PDF \u03BA\u03B1\u03B9 \u03C4\u03BF \u03C0\u03C1\u03BF\u03C3\u03B8\u03AD\u03C4\u03B5\u03B9 \u03BE\u03B1\u03BD\u03AC \u03C9\u03C2 \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF +home.ocr.title=οπτική αναγνώριση χαρακτήρων (OCR) / Σαρώσεις Cleanup +home.ocr.desc=Το Cleanup σαρώνει και ανιχνεύει κείμενο από εικόνες μέσα σε ένα PDF και το προσθέτει ξανά ως κείμενο ocr.tags=recognition,text,image,scan,read,identify,detection,editable -home.extractImages.title=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u03B5\u03B9\u03BA\u03CC\u03BD\u03C9\u03BD -home.extractImages.desc=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u03CC\u03BB\u03C9\u03BD \u03C4\u03C9\u03BD \u03B5\u03B9\u03BA\u03CC\u03BD\u03C9\u03BD \u03B1\u03C0\u03BF \u03AD\u03BD\u03B1 PDF \u03BA\u03B1\u03B9 \u03B1\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 \u03B1\u03C5\u03C4\u03CE\u03BD \u03C3\u03B5 zip +home.extractImages.title=Εξαγωγή εικόνων +home.extractImages.desc=Εξαγωγή όλων των εικόνων απο ένα PDF και αποθήκευση αυτών σε zip extractImages.tags=picture,photo,save,archive,zip,capture,grab -home.pdfToPDFA.title=PDF \u03C3\u03B5 PDF/A -home.pdfToPDFA.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE PDF \u03C3\u03B5 PDF/A \u03B3\u03B9\u03B1 \u03BC\u03B1\u03BA\u03C1\u03BF\u03C7\u03C1\u03CC\u03BD\u03B9\u03B1 \u03B1\u03C0\u03BF\u03B8\u03AE\u03BA\u03B5\u03C5\u03C3\u03B7 +home.pdfToPDFA.title=PDF σε PDF/A +home.pdfToPDFA.desc=Μετατροπή PDF σε PDF/A για μακροχρόνια αποθήκευση pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation -home.PDFToWord.title=PDF \u03C3\u03B5 Word -home.PDFToWord.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03BF\u03C5 PDF \u03C3\u03B5 Word \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF (DOC, DOCX and ODT) +home.PDFToWord.title=PDF σε Word +home.PDFToWord.desc=Μετατροπή του PDF σε Word αρχείο (DOC, DOCX and ODT) PDFToWord.tags=doc,docx,odt,word,transformation,format,conversion,office,microsoft,docfile -home.PDFToPresentation.title=PDF \u03C3\u03B5 Powerpoint -home.PDFToPresentation.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03BF\u03C5 PDF \u03C3\u03B5 Powerpoint \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF (PPT, PPTX and ODP) +home.PDFToPresentation.title=PDF σε Powerpoint +home.PDFToPresentation.desc=Μετατροπή του PDF σε Powerpoint αρχείο (PPT, PPTX and ODP) PDFToPresentation.tags=slides,show,office,microsoft -home.PDFToText.title=PDF \u03C3\u03B5 RTF (\u039A\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF) -home.PDFToText.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03BF\u03C5 PDF \u03C3\u03B5 \u039A\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF \u03AE \u03C3\u03B5 \u03BC\u03BF\u03C1\u03C6\u03AE RTF +home.PDFToText.title=PDF σε RTF (Κείμενο) +home.PDFToText.desc=Μετατροπή του PDF σε Κείμενο ή σε μορφή RTF PDFToText.tags=richformat,richtextformat,rich text format -home.PDFToHTML.title=PDF \u03C3\u03B5 HTML -home.PDFToHTML.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03BF\u03C5 PDF \u03C3\u03B5 \u03BC\u03BF\u03C1\u03C6\u03AE HTML +home.PDFToHTML.title=PDF σε HTML +home.PDFToHTML.desc=Μετατροπή του PDF σε μορφή HTML PDFToHTML.tags=web content,browser friendly -home.PDFToXML.title=PDF \u03C3\u03B5 XML -home.PDFToXML.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03BF\u03C5 PDF \u03C3\u03B5 \u03BC\u03BF\u03C1\u03C6\u03AE XML +home.PDFToXML.title=PDF σε XML +home.PDFToXML.desc=Μετατροπή του PDF σε μορφή XML PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert -home.ScannerImageSplit.title=\u0391\u03BD\u03AF\u03C7\u03BD\u03B5\u03C5\u03C3\u03B7/\u0394\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03C3\u03B1\u03C1\u03C9\u03BC\u03AD\u03BD\u03C9\u03BD \u03C6\u03C9\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03B9\u03CE\u03BD -home.ScannerImageSplit.desc=\u0394\u03B9\u03B1\u03C7\u03C9\u03C1\u03AF\u03C3\u03BC\u03CC\u03C2 \u03C0\u03BF\u03BB\u03BB\u03CE\u03BD \u03C6\u03C9\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AF\u03CE\u03BD \u03BC\u03AD\u03C3\u03B1 \u03B1\u03C0\u03CC \u03BC\u03B9\u03B1 \u03C6\u03C9\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AF\u03B1/PDF +home.ScannerImageSplit.title=Ανίχνευση/Διαίρεση σαρωμένων φωτογραφιών +home.ScannerImageSplit.desc=Διαχωρίσμός πολλών φωτογραφίών μέσα από μια φωτογραφία/PDF ScannerImageSplit.tags=separate,auto-detect,scans,multi-photo,organize -home.sign.title=\u03A5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE -home.sign.desc=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03C2 \u03C3\u03C4\u03BF PDF \u03BC\u03B5 \u03C3\u03C7\u03AD\u03B4\u03B9\u03BF, \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF \u03AE \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1. +home.sign.title=Υπογραφή +home.sign.desc=Προσθήκη υπογραφής στο PDF με σχέδιο, κείμενο ή εικόνα. sign.tags=authorize,initials,drawn-signature,text-sign,image-signature home.flatten.title=Flatten -home.flatten.desc=\u039A\u03B1\u03C4\u03AC\u03C1\u03B3\u03B7\u03C3\u03B7 \u03CC\u03BB\u03C9\u03BD \u03C4\u03C9\u03BD \u03B4\u03B9\u03B1\u03B4\u03C1\u03B1\u03C3\u03C4\u03B9\u03BA\u03CE\u03BD \u03C3\u03C4\u03BF\u03B9\u03C7\u03B5\u03AF\u03C9\u03BD \u03BA\u03B1\u03B9 \u03C6\u03BF\u03C1\u03BC\u03CE\u03BD \u03B1\u03C0\u03CC \u03AD\u03BD\u03B1 PDF +home.flatten.desc=Κατάργηση όλων των διαδραστικών στοιχείων και φορμών από ένα PDF flatten.tags=static,deactivate,non-interactive,streamline -home.repair.title=\u0395\u03C0\u03B9\u03B4\u03B9\u03CC\u03C1\u03B8\u03C9\u03C3\u03B7 -home.repair.desc=\u03A0\u03C1\u03BF\u03C3\u03C0\u03AC\u03B8\u03B5\u03B9\u03B1 \u03B5\u03C0\u03B9\u03B4\u03B9\u03CC\u03C1\u03B8\u03C9\u03C3\u03B7\u03C2 \u03B5\u03BD\u03CC\u03C2 \u03BA\u03B1\u03C4\u03B5\u03C3\u03C4\u03C1\u03B1\u03BC\u03BC\u03AD\u03BD\u03BF\u03C5 PDF +home.repair.title=Επιδιόρθωση +home.repair.desc=Προσπάθεια επιδιόρθωσης ενός κατεστραμμένου PDF repair.tags=fix,restore,correction,recover -home.removeBlanks.title=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03BA\u03B5\u03BD\u03CE\u03BD \u03A3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -home.removeBlanks.desc=\u0391\u03BD\u03AF\u03C7\u03B5\u03C5\u03C3\u03B7 \u03BA\u03B1\u03B9 \u03B1\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03BA\u03B5\u03BD\u03CE\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD \u03B1\u03C0\u03CC \u03AD\u03BD\u03B1 \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF +home.removeBlanks.title=Αφαίρεση κενών Σελίδων +home.removeBlanks.desc=Ανίχευση και αφαίρεση κενών σελίδων από ένα έγγραφο removeBlanks.tags=cleanup,streamline,non-content,organize home.removeAnnotations.title=Remove Annotations home.removeAnnotations.desc=Removes all comments/annotations from a PDF removeAnnotations.tags=comments,highlight,notes,markup,remove -home.compare.title=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7 -home.compare.desc=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7 \u03BA\u03B1\u03B9 \u03B5\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 \u03C4\u03C9\u03BD \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03CE\u03BD \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD \u03B4\u03CD\u03BF PDF \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD +home.compare.title=Σύγκριση +home.compare.desc=Σύγκριση και εμφάνιση των διαφορών μεταξύ δύο PDF αρχείων compare.tags=differentiate,contrast,changes,analysis -home.certSign.title=\u03A5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE \u03BC\u03B5 \u03A0\u03B9\u03C3\u03C4\u03BF\u03C0\u03BF\u03B9\u03B7\u03C4\u03B9\u03BA\u03CC -home.certSign.desc=\u03A5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE \u03B5\u03BD\u03CC\u03C2 PDF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 \u03BC\u03B5 \u03AD\u03BD\u03B1 \u03A0\u03B9\u03C3\u03C4\u03BF\u03C0\u03BF\u03B9\u03B7\u03C4\u03B9\u03BA\u03CC/\u039A\u03BB\u03B5\u03B9\u03B4\u03AF (PEM/P12) +home.certSign.title=Υπογραφή με Πιστοποιητικό +home.certSign.desc=Υπογραφή ενός PDF αρχείου με ένα Πιστοποιητικό/Κλειδί (PEM/P12) certSign.tags=authenticate,PEM,P12,official,encrypt -home.pageLayout.title=\u0394\u03B9\u03AC\u03C4\u03B1\u03BE\u03B7 \u03C0\u03BF\u03BB\u03BB\u03CE\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -home.pageLayout.desc=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 \u03C0\u03BF\u03BB\u03BB\u03B1\u03C0\u03BB\u03CE\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD \u03B5\u03BD\u03CC\u03C2 \u03B5\u03B3\u03B3\u03C1\u03AC\u03C6\u03BF\u03C5 PDF \u03C3\u03B5 \u03BC\u03AF\u03B1 \u03BC\u03CC\u03BD\u03BF \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1 +home.pageLayout.title=Διάταξη πολλών σελίδων +home.pageLayout.desc=Συγχώνευση πολλαπλών σελίδων ενός εγγράφου PDF σε μία μόνο σελίδα pageLayout.tags=merge,composite,single-view,organize -home.scalePages.title=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C4\u03BF\u03C5 \u03BC\u03B5\u03B3\u03AD\u03B8\u03BF\u03C5\u03C2/\u03BA\u03BB\u03AF\u03BC\u03B1\u03BA\u03B1\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 -home.scalePages.desc=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u03C4\u03BF\u03C5 \u03BC\u03B5\u03B3\u03AD\u03B8\u03BF\u03C5\u03C2/\u03BA\u03BB\u03AF\u03BC\u03B1\u03BA\u03B1\u03C2 \u03BC\u03AF\u03B1\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 \u03BA\u03B1\u03B9/\u03B7 \u03C4\u03BF\u03C5 \u03C0\u03B5\u03C1\u03B9\u03B5\u03C7\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C4\u03B7\u03C2. +home.scalePages.title=Προσαρμογή του μεγέθους/κλίμακας σελίδας +home.scalePages.desc=Αλλαγή του μεγέθους/κλίμακας μίας σελίδας και/η του περιεχομένου της. scalePages.tags=resize,modify,dimension,adapt -home.pipeline.title=Pipeline (\u0393\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C7\u03C9\u03C1\u03B7\u03BC\u03AD\u03BD\u03BF\u03C5\u03C2) -home.pipeline.desc=\u0395\u03BA\u03C4\u03AD\u03BB\u03B5\u03C3\u03B7 \u03C0\u03BF\u03BB\u03BB\u03B1\u03C0\u03BB\u03CE\u03BD \u03B5\u03BD\u03B5\u03C1\u03B3\u03B5\u03B9\u03CE\u03BD \u03C3\u03B5 \u03B1\u03C1\u03C7\u03B5\u03AF\u03B1 PDF \u03BF\u03C1\u03AF\u03B6\u03BF\u03BD\u03C4\u03B1\u03C2 pipeline scripts +home.pipeline.title=Pipeline (Για προχωρημένους) +home.pipeline.desc=Εκτέλεση πολλαπλών ενεργειών σε αρχεία PDF ορίζοντας pipeline scripts pipeline.tags=automate,sequence,scripted,batch-process -home.add-page-numbers.title=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B1\u03C1\u03B9\u03B8\u03BC\u03CE\u03BD \u03C3\u03B5 \u03A3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 -home.add-page-numbers.desc=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B1\u03C1\u03B9\u03B8\u03BC\u03CE\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD \u03C3\u03B5 \u03AD\u03BD\u03B1 \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF \u03C3\u03B5 \u03BC\u03B9\u03B1 \u03BA\u03B1\u03B8\u03BF\u03C1\u03B9\u03C3\u03BC\u03AD\u03BD\u03B7 \u03B8\u03AD\u03C3\u03B7 +home.add-page-numbers.title=Προσθήκη αριθμών σε Σελίδες +home.add-page-numbers.desc=Προσθήκη αριθμών σελίδων σε ένα έγγραφο σε μια καθορισμένη θέση add-page-numbers.tags=paginate,label,organize,index -home.auto-rename.title=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u03BC\u03B5\u03C4\u03BF\u03BD\u03BF\u03BC\u03B1\u03C3\u03AF\u03B1 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 PDF -home.auto-rename.desc=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u03BC\u03B5\u03C4\u03BF\u03BD\u03BF\u03BC\u03B1\u03C3\u03AF\u03B1 \u03B5\u03BD\u03CC\u03C2 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 PDF \u03BC\u03B5 \u03B2\u03AC\u03C3\u03B7 \u03C4\u03B7\u03BD \u03BA\u03B5\u03C6\u03B1\u03BB\u03AF\u03B4\u03B1 \u03C0\u03BF\u03C5 \u03AD\u03C7\u03B5\u03B9 \u03B5\u03BD\u03C4\u03BF\u03C0\u03B9\u03C3\u03C4\u03B5\u03AF +home.auto-rename.title=Αυτόματη μετονομασία αρχείου PDF +home.auto-rename.desc=Αυτόματη μετονομασία ενός αρχείου PDF με βάση την κεφαλίδα που έχει εντοπιστεί auto-rename.tags=auto-detect,header-based,organize,relabel -home.adjust-contrast.title=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C7\u03C1\u03C9\u03BC\u03AC\u03C4\u03C9\u03BD/\u0391\u03BD\u03C4\u03AF\u03B8\u03B5\u03C3\u03B7 -home.adjust-contrast.desc=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C4\u03B7\u03C2 \u03B1\u03BD\u03C4\u03AF\u03B8\u03B5\u03C3\u03B7\u03C2, \u03C4\u03BF\u03C5 \u03BA\u03BF\u03C1\u03B5\u03C3\u03BC\u03BF\u03CD \u03BA\u03B1\u03B9 \u03C4\u03B7\u03C2 \u03C6\u03C9\u03C4\u03B5\u03B9\u03BD\u03CC\u03C4\u03B7\u03C4\u03B1\u03C2 \u03B5\u03BD\u03CC\u03C2 PDF +home.adjust-contrast.title=Προσαρμογή χρωμάτων/Αντίθεση +home.adjust-contrast.desc=Προσαρμογή της αντίθεσης, του κορεσμού και της φωτεινότητας ενός PDF adjust-contrast.tags=color-correction,tune,modify,enhance -home.crop.title=\u03A0\u03B5\u03C1\u03B9\u03BA\u03BF\u03C0\u03AE PDF -home.crop.desc=\u03A0\u03B5\u03C1\u03B9\u03BA\u03BF\u03C0\u03AE \u03B5\u03BD\u03CC\u03C2 PDF \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03BC\u03B5\u03B9\u03C9\u03B8\u03B5\u03AF \u03C4\u03BF \u03BC\u03AD\u03B3\u03B5\u03B8\u03CC\u03C2 \u03C4\u03BF\u03C5 (\u03B4\u03B9\u03B1\u03C4\u03B7\u03C1\u03B5\u03AF \u03C4\u03BF \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF!) +home.crop.title=Περικοπή PDF +home.crop.desc=Περικοπή ενός PDF για να μειωθεί το μέγεθός του (διατηρεί το κείμενο!) crop.tags=trim,shrink,edit,shape -home.autoSplitPDF.title=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03BF\u03C2 \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -home.autoSplitPDF.desc=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03BF\u03C2 \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2 \u03C3\u03B1\u03C1\u03C9\u03BC\u03AD\u03BD\u03BF\u03C5 PDF \u03BC\u03B5 \u03C6\u03C5\u03C3\u03B9\u03BA\u03CC \u03C3\u03B1\u03C1\u03C9\u03BC\u03AD\u03BD\u03BF \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03C4\u03AE \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD QR Code +home.autoSplitPDF.title=Αυτόματος διαχωρισμός σελίδων +home.autoSplitPDF.desc=Αυτόματος διαχωρισμός σαρωμένου PDF με φυσικό σαρωμένο διαχωριστή σελίδων QR Code autoSplitPDF.tags=QR-based,separate,scan-segment,organize -home.sanitizePdf.title=\u0391\u03C0\u03BF\u03BB\u03CD\u03BC\u03B1\u03BD\u03C3\u03B7 -home.sanitizePdf.desc=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03C3\u03B5\u03BD\u03B1\u03C1\u03AF\u03C9\u03BD \u03BA\u03B1\u03B9 \u03AC\u03BB\u03BB\u03C9\u03BD \u03C3\u03C4\u03BF\u03B9\u03C7\u03B5\u03AF\u03C9\u03BD \u03B1\u03C0\u03CC \u03B1\u03C1\u03C7\u03B5\u03AF\u03B1 PDF +home.sanitizePdf.title=Απολύμανση +home.sanitizePdf.desc=Αφαίρεση σεναρίων και άλλων στοιχείων από αρχεία PDF sanitizePdf.tags=clean,secure,safe,remove-threats -home.URLToPDF.title=URL/\u0399\u03C3\u03C4\u03CC\u03C4\u03BF\u03C0\u03BF\u03C2 \u03C3\u03B5 PDF -home.URLToPDF.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03BF\u03C0\u03BF\u03B9\u03B1\u03B4\u03AE\u03C0\u03BF\u03C4\u03B5 \u03B4\u03B9\u03B5\u03CD\u03B8\u03C5\u03BD\u03C3\u03B7\u03C2 URL http(s) \u03C3\u03B5 PDF +home.URLToPDF.title=URL/Ιστότοπος σε PDF +home.URLToPDF.desc=Μετατροπή οποιαδήποτε διεύθυνσης URL http(s) σε PDF URLToPDF.tags=web-capture,save-page,web-to-doc,archive -home.HTMLToPDF.title=HTML \u03C3\u03B5 PDF -home.HTMLToPDF.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03BF\u03C0\u03BF\u03B9\u03BF\u03C5\u03B4\u03AE\u03C0\u03BF\u03C4\u03B5 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 HTML \u03AE zip \u03C3\u03B5 PDF +home.HTMLToPDF.title=HTML σε PDF +home.HTMLToPDF.desc=Μετατροπή οποιουδήποτε αρχείου HTML ή zip σε PDF HTMLToPDF.tags=markup,web-content,transformation,convert -home.MarkdownToPDF.title=Markdown \u03C3\u03B5 PDF -home.MarkdownToPDF.desc=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03BF\u03C0\u03BF\u03B9\u03BF\u03C5\u03B4\u03AE\u03C0\u03BF\u03C4\u03B5 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 Markdown \u03C3\u03B5 PDF +home.MarkdownToPDF.title=Markdown σε PDF +home.MarkdownToPDF.desc=Μετατροπή οποιουδήποτε αρχείου Markdown σε PDF MarkdownToPDF.tags=markup,web-content,transformation,convert -home.getPdfInfo.title=\u039B\u03AE\u03C8\u03B7 \u03CC\u03BB\u03C9\u03BD \u03C4\u03C9\u03BD \u03C0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03B9\u03CE\u03BD \u03B1\u03C0\u03CC \u03C4\u03BF PDF -home.getPdfInfo.desc=\u039B\u03AE\u03C8\u03B7 \u03CC\u03BB\u03C9\u03BD \u03C4\u03C9\u03BD \u03C0\u03B9\u03B8\u03B1\u03BD\u03CE\u03BD \u03C0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03B9\u03CE\u03BD \u03B1\u03C0\u03CC \u03C4\u03B1 \u03B1\u03C1\u03C7\u03B5\u03AF\u03B1 PDF +home.getPdfInfo.title=Λήψη όλων των πληροφοριών από το PDF +home.getPdfInfo.desc=Λήψη όλων των πιθανών πληροφοριών από τα αρχεία PDF getPdfInfo.tags=infomation,data,stats,statistics -home.extractPage.title=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -home.extractPage.desc=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u03C4\u03C9\u03BD \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03C9\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD \u03B1\u03C0\u03CC \u03AD\u03BD\u03B1 PDF +home.extractPage.title=Εξαγωγή σελίδων +home.extractPage.desc=Εξαγωγή των επιλεγμένων σελίδων από ένα PDF extractPage.tags=extract -home.PdfToSinglePage.title=PDF \u03C3\u03B5 \u03BC\u03AF\u03B1 \u03BC\u03B5\u03B3\u03AC\u03BB\u03B7 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1 -home.PdfToSinglePage.desc=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 \u03CC\u03BB\u03C9\u03BD \u03C4\u03C9\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD PDF \u03C3\u03B5 \u03BC\u03B9\u03B1 \u03BC\u03B5\u03B3\u03AC\u03BB\u03B7 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1 +home.PdfToSinglePage.title=PDF σε μία μεγάλη σελίδα +home.PdfToSinglePage.desc=Συγχώνευση όλων των σελίδων PDF σε μια μεγάλη σελίδα PdfToSinglePage.tags=single page -home.showJS.title=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 Javascript -home.showJS.desc=\u0391\u03BD\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7 \u03BA\u03B1\u03B9 \u03B5\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 \u03BA\u03CE\u03B4\u03B9\u03BA\u03B1 Javascript \u03C0\u03BF\u03C5 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03B5\u03BD\u03C3\u03C9\u03BC\u03B1\u03C4\u03C9\u03BC\u03AD\u03BD\u03BF \u03BC\u03AD\u03C3\u03B1 \u03C3\u03B5 \u03AD\u03BD\u03B1 PDF +home.showJS.title=Εμφάνιση Javascript +home.showJS.desc=Ανζήτηση και εμφάνιση κώδικα Javascript που είναι ενσωματωμένο μέσα σε ένα PDF showJS.tags=JS -home.autoRedact.title=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03BF \u039C\u03B1\u03CD\u03C1\u03B9\u03C3\u03BC\u03B1 \u039A\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5 -home.autoRedact.desc=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u03B5\u03C0\u03B5\u03BE\u03B5\u03C1\u03B3\u03B1\u03C3\u03AF\u03B1 (\u039C\u03B1\u03CD\u03C1\u03B9\u03C3\u03BC\u03B1) \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF\u03C5 \u03C3\u03B5 PDF \u03BC\u03B5 \u03B2\u03AC\u03C3\u03B7 \u03C4\u03BF \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF \u03B5\u03B9\u03C3\u03B1\u03B3\u03C9\u03B3\u03AE\u03C2 +home.autoRedact.title=Αυτόματο Μαύρισμα Κειμένου +home.autoRedact.desc=Αυτόματη επεξεργασία (Μαύρισμα) κείμενου σε PDF με βάση το κείμενο εισαγωγής autoRedact.tags=Redact,Hide,black out,black,marker,hidden -home.tableExtraxt.title=PDF to CSV -home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV +home.tableExtraxt.title=PDF σε CSV +home.tableExtraxt.desc=Εξάγει πίνακες από PDF μετατρέποντάς το σε CSV tableExtraxt.tags=CSV,Table Extraction,extract,convert -home.autoSizeSplitPDF.title=Auto Split by Size/Count -home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count +home.autoSizeSplitPDF.title=Αυτόματη διαίρεση κατά μέγεθος/πλήθος +home.autoSizeSplitPDF.desc=Διαχωρίστε ένα μόνο PDF σε πολλά έγγραφα με βάση το μέγεθος, τον αριθμό σελίδων ή τον αριθμό εγγράφων autoSizeSplitPDF.tags=pdf,split,document,organization -home.overlay-pdfs.title=Overlay PDFs -home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF +home.overlay-pdfs.title=Επικάλυψη PDFs +home.overlay-pdfs.desc=Επικαλύπτει αρχεία PDF πάνω σε άλλο PDF overlay-pdfs.tags=Overlay -home.split-by-sections.title=Split PDF by Sections -home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections +home.split-by-sections.title=Διαχωρισμός PDF ανά ενότητες +home.split-by-sections.desc=Διαχωρίστε κάθε σελίδα ενός PDF σε μικρότερες οριζόντιες και κάθετες ενότητες split-by-sections.tags=Section Split, Divide, Customize -home.AddStampRequest.title=Add Stamp to PDF -home.AddStampRequest.desc=Add text or add image stamps at set locations +home.AddStampRequest.title=Προσθήκη σφραγίδας σε PDF +home.AddStampRequest.desc=Προσθέστε κείμενο ή προσθέστε σφραγίδες εικόνας σε καθορισμένες τοποθεσίες AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF σε Book +home.PDFToBook.desc=Μετατρέπει τις μορφές PDF σε Book/Comic χρησιμοποιώντας calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book σε PDF +home.BookToPDF.desc=Μετατρέπει τις μορφές Books/Comics σε PDF χρησιμοποιώντας calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # # # ########################### #login -login.title=\u0395\u03AF\u03C3\u03BF\u03B4\u03BF\u03C2 -login.signin=\u0395\u03AF\u03C3\u03BF\u03B4\u03BF\u03C2 -login.rememberme=\u039D\u03B1 \u039C\u03B5 \u0398\u03C5\u03BC\u03AC\u03C3\u03B1\u03B9 -login.invalid=\u039B\u03AC\u03B8\u03BF\u03C2 \u03CC\u03BD\u03BF\u03BC\u03B1 \u03C7\u03C1\u03AE\u03C3\u03C4\u03B7 \u03AE \u03BA\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2. -login.locked=\u039F \u03BB\u03BF\u03B3\u03B1\u03C1\u03B9\u03B1\u03C3\u03BC\u03CC\u03C2 \u03C3\u03B1\u03C2 \u03AD\u03C7\u03B5\u03B9 \u03BA\u03BB\u03B5\u03B9\u03B4\u03C9\u03B8\u03B5\u03AF. -login.signinTitle=\u03A0\u03B1\u03C1\u03B1\u03BA\u03B1\u03BB\u03CE, \u03C3\u03C5\u03BD\u03B4\u03B5\u03B8\u03B5\u03AF\u03C4\u03B5 +login.title=Είσοδος +login.header=Είσοδος +login.signin=Είσοδος +login.rememberme=Να Με Θυμάσαι +login.invalid=Λάθος όνομα χρήστη ή κωδικού πρόσβασης. +login.locked=Ο λογαριασμός σας έχει κλειδωθεί. +login.signinTitle=Παρακαλώ, συνδεθείτε +login.ssoSignIn=Σύνδεση μέσω μοναδικής σύνδεσης +login.oauth2AutoCreateDisabled=Απενεργοποιήθηκε ο χρήστης αυτόματης δημιουργίας OAUTH2 #auto-redact -autoRedact.title=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03BF \u039C\u03B1\u03CD\u03C1\u03B9\u03C3\u03BC\u03B1 \u039A\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5 -autoRedact.header=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03BF \u039C\u03B1\u03CD\u03C1\u03B9\u03C3\u03BC\u03B1 \u039A\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5 -autoRedact.colorLabel=\u03A7\u03C1\u03CE\u03BC\u03B1 -autoRedact.textsToRedactLabel=\u039A\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF \u03B3\u03B9\u03B1 \u03BC\u03B1\u03CD\u03C1\u03B9\u03C3\u03BC\u03B1 (\u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03AD\u03BD\u03BF \u03C3\u03B5 \u03B3\u03C1\u03B1\u03BC\u03BC\u03AD\u03C2) -autoRedact.textsToRedactPlaceholder=\u03C0.\u03C7. \n\u0395\u03BC\u03C0\u03B9\u03C3\u03C4\u03B5\u03C5\u03C4\u03B9\u03BA\u03CC \n\u0391\u03BA\u03C1\u03CE\u03C2 \u03B1\u03C0\u03CC\u03C1\u03C1\u03B7\u03C4\u03BF -autoRedact.useRegexLabel=\u03A7\u03C1\u03AE\u03C3\u03B7 Regex -autoRedact.wholeWordSearchLabel=\u0391\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7 \u03BF\u03BB\u03CC\u03BA\u03BB\u03B7\u03C1\u03B7\u03C2 \u03C4\u03B7\u03C2 \u03BB\u03AD\u03BE\u03B7\u03C2 +autoRedact.title=Αυτόματο Μαύρισμα Κειμένου +autoRedact.header=Αυτόματο Μαύρισμα Κειμένου +autoRedact.colorLabel=Χρώμα +autoRedact.textsToRedactLabel=Κείμενο για μαύρισμα (διαχωρισμένο σε γραμμές) +autoRedact.textsToRedactPlaceholder=π.χ. \nΕμπιστευτικό \nΑκρώς απόρρητο +autoRedact.useRegexLabel=Χρήση Regex +autoRedact.wholeWordSearchLabel=Αναζήτηση ολόκληρης της λέξης autoRedact.customPaddingLabel=Custom Extra Padding -autoRedact.convertPDFToImageLabel=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE PDF \u03C3\u03B5 PDF-\u0395\u03B9\u03BA\u03CC\u03BD\u03B1 (\u03A7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF\u03C4\u03B1\u03B9 \u03B3\u03B9\u03B1 \u03C4\u03B7\u03BD \u03B1\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03BA\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C0\u03AF\u03C3\u03C9 \u03B1\u03C0\u03CC \u03C4\u03BF \u03C0\u03BB\u03B1\u03AF\u03C3\u03B9\u03BF) -autoRedact.submitButton=\u03A5\u03C0\u03BF\u03B2\u03BF\u03BB\u03AE +autoRedact.convertPDFToImageLabel=Μετατροπή PDF σε PDF-Εικόνα (Χρησιμοποιείται για την αφαίρεση κειμένου πίσω από το πλαίσιο) +autoRedact.submitButton=Υποβολή #showJS -showJS.title=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 Javascript -showJS.header=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 Javascript -showJS.downloadJS=\u039B\u03AE\u03C8\u03B7 Javascript -showJS.submit=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 +showJS.title=Εμφάνιση Javascript +showJS.header=Εμφάνιση Javascript +showJS.downloadJS=Λήψη Javascript +showJS.submit=Εμφάνιση #pdfToSinglePage -pdfToSinglePage.title=PDF \u03C3\u03B5 \u039C\u03BF\u03BD\u03AE \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 -pdfToSinglePage.header=PDF \u03C3\u03B5 \u039C\u03BF\u03BD\u03AE \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 -pdfToSinglePage.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C3\u03B5 \u039C\u03BF\u03BD\u03AE \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 +pdfToSinglePage.title=PDF σε Μονή Σελίδα +pdfToSinglePage.header=PDF σε Μονή Σελίδα +pdfToSinglePage.submit=Μετατροπή σε Μονή Σελίδα #pageExtracter -pageExtracter.title=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u03A3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -pageExtracter.header=E\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u03A3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -pageExtracter.submit=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE +pageExtracter.title=Εξαγωγή Σελίδων +pageExtracter.header=EΕξαγωγή Σελίδων +pageExtracter.submit=Εξαγωγή +pageExtracter.placeholder=(π.χ. 1,2,8 ή 4,7,12-16 ή 2n-1) #getPdfInfo -getPdfInfo.title=\u0391\u03BD\u03AC\u03BA\u03C4\u03B7\u03C3\u03B7 \u03C0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03B9\u03CE\u03BD \u03B1\u03C0\u03CC \u03C4\u03BF PDF -getPdfInfo.header=\u0391\u03BD\u03AC\u03BA\u03C4\u03B7\u03C3\u03B7 \u03C0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03B9\u03CE\u03BD \u03B1\u03C0\u03CC \u03C4\u03BF PDF -getPdfInfo.submit=\u0391\u03BD\u03AC\u03BA\u03C4\u03B7\u03C3\u03B7 \u03C0\u03BB\u03B7\u03C1\u03BF\u03C6\u03BF\u03C1\u03B9\u03CE\u03BD -getPdfInfo.downloadJson=\u039B\u03AE\u03C8\u03B7 JSON +getPdfInfo.title=Ανάκτηση πληροφοριών από το PDF +getPdfInfo.header=Ανάκτηση πληροφοριών από το PDF +getPdfInfo.submit=Ανάκτηση πληροφοριών +getPdfInfo.downloadJson=Λήψη JSON #markdown-to-pdf -MarkdownToPDF.title=Markdown \u03C3\u03B5 PDF -MarkdownToPDF.header=Markdown \u03C3\u03B5 PDF -MarkdownToPDF.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE -MarkdownToPDF.help=\u0395\u03C1\u03B3\u03B1\u03C3\u03AF\u03B1 \u03C3\u03B5 \u03B5\u03BE\u03AD\u03BB\u03B9\u03BE\u03B7 -MarkdownToPDF.credit=\u03A7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF WeasyPrint +MarkdownToPDF.title=Markdown σε PDF +MarkdownToPDF.header=Markdown σε PDF +MarkdownToPDF.submit=Μετατροπή +MarkdownToPDF.help=Εργασία σε εξέλιξη +MarkdownToPDF.credit=Χρησιμοποιεί WeasyPrint #url-to-pdf -URLToPDF.title=URL \u03C3\u03B5 PDF -URLToPDF.header=URL \u03C3\u03B5 PDF -URLToPDF.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE -URLToPDF.credit=\u03A7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF WeasyPrint +URLToPDF.title=URL σε PDF +URLToPDF.header=URL σε PDF +URLToPDF.submit=Μετατροπή +URLToPDF.credit=Χρησιμοποιεί WeasyPrint #html-to-pdf -HTMLToPDF.title=HTML \u03C3\u03B5 PDF -HTMLToPDF.header=HTML \u03C3\u03B5 PDF -HTMLToPDF.help=\u0394\u03AD\u03C7\u03B5\u03C4\u03B1\u03B9 \u03B1\u03C1\u03C7\u03B5\u03AF\u03B1 \u03C4\u03CD\u03C0\u03BF\u03C5 HTML \u03BA\u03B1\u03B9 \u03C4\u03CD\u03C0\u03BF\u03C5 ZIP \u03C0\u03BF\u03C5 \u03C0\u03B5\u03C1\u03B9\u03AD\u03C7\u03BF\u03C5\u03BD html/css/\u03B5\u03B9\u03BA\u03CC\u03BD\u03B5\u03C2 \u03BA.\u03BB\u03C0. \u03C0\u03BF\u03C5 \u03B1\u03C0\u03B1\u03B9\u03C4\u03BF\u03CD\u03BD\u03C4\u03B1\u03B9 -HTMLToPDF.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE -HTMLToPDF.credit=\u03A7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF WeasyPrint -HTMLToPDF.zoom=Zoom level for displaying the website. -HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default) -HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default) -HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default) -HTMLToPDF.printBackground=Render the background of websites. -HTMLToPDF.defaultHeader=Enable Default Header (Name and page number) -HTMLToPDF.cssMediaType=Change the CSS media type of the page. -HTMLToPDF.none=None -HTMLToPDF.print=Print +HTMLToPDF.title=HTML σε PDF +HTMLToPDF.header=HTML σε PDF +HTMLToPDF.help=Δέχεται αρχεία τύπου HTML και τύπου ZIP που περιέχουν html/css/εικόνες κ.λπ. που απαιτούνται +HTMLToPDF.submit=Μετατροπή +HTMLToPDF.credit=Χρησιμοποιεί WeasyPrint +HTMLToPDF.zoom=Επίπεδο ζουμ για την εμφάνιση του ιστότοπου. +HTMLToPDF.pageWidth=Το πλάτος της σελίδας σε εκατοστά. (Κενό έως προεπιλογή) +HTMLToPDF.pageHeight=Το ύψος της σελίδας σε εκατοστά. (Κενό έως προεπιλογή) +HTMLToPDF.marginTop=Το επάνω περιθώριο της σελίδας σε χιλιοστά. (Κενό έως προεπιλογή) +HTMLToPDF.marginBottom=Κάτω περιθώριο σελίδας σε χιλιοστά. (Κενό έως προεπιλογή) +HTMLToPDF.marginLeft=Το αριστερό περιθώριο της σελίδας σε χιλιοστά. (Κενό έως προεπιλογή) +HTMLToPDF.marginRight=Το δεξί περιθώριο της σελίδας σε χιλιοστά. (Κενό έως προεπιλογή) +HTMLToPDF.printBackground=Αποδώστε το φόντο των ιστοσελίδων. +HTMLToPDF.defaultHeader=Ενεργοποίηση προεπιλεγμένης κεφαλίδας (Όνομα και αριθμός σελίδας) +HTMLToPDF.cssMediaType=Αλλάξτε τον τύπο μέσων (media type )CSS της σελίδας. +HTMLToPDF.none=Κανένα +HTMLToPDF.print=Εκτύπωσε HTMLToPDF.screen=Screen #AddStampRequest -AddStampRequest.header=Stamp PDF -AddStampRequest.title=Stamp PDF -AddStampRequest.stampType=Stamp Type -AddStampRequest.stampText=Stamp Text -AddStampRequest.stampImage=Stamp Image -AddStampRequest.alphabet=Alphabet -AddStampRequest.fontSize=Font/Image Size -AddStampRequest.rotation=Rotation -AddStampRequest.opacity=Opacity -AddStampRequest.position=Position -AddStampRequest.overrideX=Override X Coordinate -AddStampRequest.overrideY=Override Y Coordinate -AddStampRequest.customMargin=Custom Margin -AddStampRequest.customColor=Custom Text Color -AddStampRequest.submit=Submit +AddStampRequest.header=Σφραγίδα PDF +AddStampRequest.title=Σφραγίδα PDF +AddStampRequest.stampType=Τύπος σφραγίδας +AddStampRequest.stampText=Κείμενο σφραγίδας +AddStampRequest.stampImage=Εικόνα σφραγίδας +AddStampRequest.alphabet=Αλφάβητο +AddStampRequest.fontSize=Μέγεθος γραμματοσειράς/εικόνας +AddStampRequest.rotation=Περιστροφή +AddStampRequest.opacity=Αδιαφάνεια +AddStampRequest.position=Θέση +AddStampRequest.overrideX=Override Συντεταγμένης X +AddStampRequest.overrideY=Override Συντεταγμένης Ψ +AddStampRequest.customMargin=Προσαρμοσμένο Περιθώριο +AddStampRequest.customColor=Προσαρμοσμένο χρώμα κειμένου +AddStampRequest.submit=Υποβολή #sanitizePDF -sanitizePDF.title=\u0391\u03C0\u03BF\u03BB\u03CD\u03BC\u03B1\u03BD\u03C3\u03B7 PDF -sanitizePDF.header=\u0391\u03C0\u03BF\u03BB\u03CD\u03BC\u03B1\u03BD\u03C3\u03B7 \u03B5\u03BD\u03CC\u03C2 PDF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 -sanitizePDF.selectText.1=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 JavaScript -sanitizePDF.selectText.2=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03B5\u03BC\u03C3\u03C9\u03BC\u03B1\u03C4\u03C9\u03BC\u03AD\u03BD\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD -sanitizePDF.selectText.3=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03BC\u03B5\u03C4\u03B1\u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03C9\u03BD -sanitizePDF.selectText.4=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03C3\u03C5\u03BD\u03B4\u03AD\u03C3\u03BC\u03C9\u03BD (links) -sanitizePDF.selectText.5=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03B3\u03C1\u03B1\u03BC\u03BC\u03B1\u03C4\u03BF\u03C3\u03B5\u03B9\u03C1\u03CE\u03BD -sanitizePDF.submit=\u0391\u03C0\u03BF\u03BB\u03CD\u03BC\u03B1\u03BD\u03C3\u03B7 PDF +sanitizePDF.title=Απολύμανση PDF +sanitizePDF.header=Απολύμανση ενός PDF αρχείου +sanitizePDF.selectText.1=Αφαίρεση JavaScript +sanitizePDF.selectText.2=Αφαίρεση εμσωματωμένων αρχείων +sanitizePDF.selectText.3=Αφαίρεση μεταδεδομένων +sanitizePDF.selectText.4=Αφαίρεση συνδέσμων (links) +sanitizePDF.selectText.5=Αφαίρεση γραμματοσειρών +sanitizePDF.submit=Απολύμανση PDF #addPageNumbers -addPageNumbers.title=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B1\u03C1\u03B9\u03B8\u03BC\u03CE\u03BD \u03C3\u03B5 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 -addPageNumbers.header=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B1\u03C1\u03B9\u03B8\u03BC\u03CE\u03BD \u03C3\u03B5 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 -addPageNumbers.selectText.1=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE PDF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5: -addPageNumbers.selectText.2=\u039C\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 \u03C0\u03B5\u03C1\u03B9\u03B8\u03C9\u03C1\u03AF\u03BF\u03C5 -addPageNumbers.selectText.3=\u0398\u03AD\u03C3\u03B7 -addPageNumbers.selectText.4=\u0391\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03C0\u03BF\u03C5 \u03BE\u03B5\u03BA\u03B9\u03BD\u03AC \u03B7 \u03B1\u03C1\u03AF\u03B8\u03BC\u03B7\u03C3\u03B7 -addPageNumbers.selectText.5=\u03A3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 \u03C3\u03B5 \u03B1\u03C1\u03B9\u03B8\u03BC\u03CC -addPageNumbers.selectText.6=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03C3\u03BC\u03AD\u03BD\u03BF \u039A\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF -addPageNumbers.customTextDesc=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03C3\u03BC\u03AD\u03BD\u03BF \u039A\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF -addPageNumbers.numberPagesDesc=\u03A0\u03BF\u03B9\u03AD\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 \u03BD\u03B1 \u03B1\u03C1\u03B9\u03B8\u03BC\u03B7\u03B8\u03BF\u03CD\u03BD, \u03C0\u03C1\u03BF\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE 'all' (\u03CC\u03BB\u03B5\u03C2), \u03B5\u03C0\u03AF\u03C3\u03B7\u03C2 \u03B4\u03AD\u03C7\u03B5\u03C4\u03B1\u03B9 1-5 \u03AE 2,5,9 \u03BA.\u03BB.\u03C0 -addPageNumbers.customNumberDesc=\u03A0\u03C1\u03BF\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03C3\u03B5 {n}, \u03B4\u03AD\u03C7\u03B5\u03C4\u03B1\u03B9 \u03B5\u03C0\u03AF\u03C3\u03B7\u03C2 "\u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 {n} \u03B1\u03C0\u03CC {total}", "Text-{n}", "{filename}-{n} -addPageNumbers.submit=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B1\u03C1\u03B9\u03B8\u03BC\u03CE\u03BD \u03C3\u03B5 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 +addPageNumbers.title=Προσθήκη αριθμών σε σελίδες +addPageNumbers.header=Προσθήκη αριθμών σε σελίδες +addPageNumbers.selectText.1=Επιλογή PDF αρχείου: +addPageNumbers.selectText.2=Μέγεθος περιθωρίου +addPageNumbers.selectText.3=Θέση +addPageNumbers.selectText.4=Αριθμός που ξεκινά η αρίθμηση +addPageNumbers.selectText.5=Σελίδες σε αριθμό +addPageNumbers.selectText.6=Προσαρμοσμένο Κείμενο +addPageNumbers.customTextDesc=Προσαρμοσμένο Κείμενο +addPageNumbers.numberPagesDesc=Ποιές σελίδες να αριθμηθούν, προεπιλογή 'all' (όλες), επίσης δέχεται 1-5 ή 2,5,9 κ.λ.π +addPageNumbers.customNumberDesc=Προεπιλογή σε {n}, δέχεται επίσης "Σελίδα {n} από {total}", "Text-{n}", "{filename}-{n} +addPageNumbers.submit=Προσθήκη αριθμών σε σελίδες #auto-rename -auto-rename.title=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u03BC\u03B5\u03C4\u03BF\u03BD\u03BF\u03BC\u03B1\u03C3\u03AF\u03B1 -auto-rename.header=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u03BC\u03B5\u03C4\u03BF\u03BD\u03BF\u03BC\u03B1\u03C3\u03AF\u03B1 PDF -auto-rename.submit=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u03BC\u03B5\u03C4\u03BF\u03BD\u03BF\u03BC\u03B1\u03C3\u03AF\u03B1 +auto-rename.title=Αυτόματη μετονομασία +auto-rename.header=Αυτόματη μετονομασία PDF +auto-rename.submit=Αυτόματη μετονομασία #adjustContrast -adjustContrast.title=\u03A0\u03C1\u03BF\u03C3\u03C0\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C4\u03B7\u03C2 \u0391\u03BD\u03C4\u03AF\u03B8\u03B5\u03C3\u03B7\u03C2 -adjustContrast.header=\u03A0\u03C1\u03BF\u03C3\u03C0\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C4\u03B7\u03C2 \u0391\u03BD\u03C4\u03AF\u03B8\u03B5\u03C3\u03B7\u03C2 -adjustContrast.contrast=\u0391\u03BD\u03C4\u03AF\u03B8\u03B5\u03C3\u03B7: -adjustContrast.brightness=\u03A6\u03C9\u03C4\u03B5\u03B9\u03BD\u03CC\u03C4\u03B7\u03C4\u03B1: -adjustContrast.saturation=\u039A\u03BF\u03C1\u03B5\u03C3\u03BC\u03CC\u03C2: -adjustContrast.download=\u039B\u03AE\u03C8\u03B7 +adjustContrast.title=Προσπαρμογή της Αντίθεσης +adjustContrast.header=Προσπαρμογή της Αντίθεσης +adjustContrast.contrast=Αντίθεση: +adjustContrast.brightness=Φωτεινότητα: +adjustContrast.saturation=Κορεσμός: +adjustContrast.download=Λήψη #crop -crop.title=\u039A\u03BF\u03C0\u03AE -crop.header=\u039A\u03BF\u03C0\u03AE \u0395\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 -crop.submit=\u03A5\u03C0\u03BF\u03B2\u03BF\u03BB\u03AE +crop.title=Κοπή +crop.header=Κοπή Εικόνας +crop.submit=Υποβολή #autoSplitPDF -autoSplitPDF.title=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u03B4\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 PDF -autoSplitPDF.header=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u03B4\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 PDF -autoSplitPDF.description=\u0395\u03BA\u03C4\u03C5\u03C0\u03CE\u03C3\u03C4\u03B5, \u03B5\u03B9\u03C3\u03AC\u03B3\u03B5\u03C4\u03B5, \u03C3\u03B1\u03C1\u03CE\u03C3\u03C4\u03B5, \u03B1\u03BD\u03B5\u03B2\u03AC\u03C3\u03C4\u03B5 \u03BA\u03B1\u03B9 \u03B1\u03C6\u03AE\u03C3\u03C4\u03B5 \u03BC\u03B1\u03C2 \u03BD\u03B1 \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03AF\u03C3\u03BF\u03C5\u03BC\u03B5 \u03B1\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B1 \u03C4\u03B1 \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03AC \u03C3\u03B1\u03C2. \u0394\u03B5\u03BD \u03B1\u03C0\u03B1\u03B9\u03C4\u03B5\u03AF\u03C4\u03B1\u03B9 \u03C7\u03B5\u03B9\u03C1\u03BF\u03BA\u03AF\u03BD\u03B7\u03C4\u03B7 \u03C4\u03B1\u03BE\u03B9\u03BD\u03CC\u03BC\u03B7\u03C3\u03B7. -autoSplitPDF.selectText.1=\u0395\u03BA\u03C4\u03C5\u03C0\u03CE\u03C3\u03C4\u03B5 \u03BC\u03B5\u03C1\u03B9\u03BA\u03AC \u03C6\u03CD\u03BB\u03BB\u03B1 \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03BF\u03CD \u03B1\u03C0\u03CC \u03BA\u03AC\u03C4\u03C9 (\u03C4\u03BF \u03B1\u03C3\u03C0\u03C1\u03CC\u03BC\u03B1\u03C5\u03C1\u03BF \u03B5\u03AF\u03BD\u03B1\u03B9 \u03BC\u03B9\u03B1 \u03C7\u03B1\u03C1\u03AC). -autoSplitPDF.selectText.2=\u03A3\u03B1\u03C1\u03CE\u03C3\u03C4\u03B5 \u03CC\u03BB\u03B1 \u03C4\u03B1 \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03AC \u03C3\u03B1\u03C2 \u03C4\u03B1\u03C5\u03C4\u03CC\u03C7\u03C1\u03BF\u03BD\u03B1, \u03BC\u03B5 \u03C4\u03BF \u03BD\u03B1 \u03B5\u03B9\u03C3\u03AC\u03B3\u03B5\u03C4\u03B5 \u03C4\u03BF \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03C4\u03B9\u03BA\u03CC \u03C6\u03CD\u03BB\u03BB\u03BF \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD \u03C4\u03BF\u03C5\u03C2. -autoSplitPDF.selectText.3=\u0391\u03BD\u03B5\u03B2\u03AC\u03C3\u03C4\u03B5 \u03AD\u03BD\u03B1 \u03BC\u03B5\u03B3\u03AC\u03BB\u03BF \u03C3\u03B1\u03C1\u03C9\u03BC\u03AD\u03BD\u03BF PDF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF \u03BA\u03B1\u03B9 \u03B1\u03C6\u03AE\u03C3\u03C4\u03B5 \u03C4\u03BF Stirling PDF \u03BD\u03B1 \u03C7\u03B5\u03B9\u03C1\u03B9\u03C3\u03C4\u03B5\u03AF \u03C4\u03B1 \u03C5\u03C0\u03CC\u03BB\u03BF\u03B9\u03C0\u03B1 -autoSplitPDF.selectText.4=\u039F\u03B9 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03BF\u03CD \u03B5\u03BD\u03C4\u03BF\u03C0\u03AF\u03B6\u03BF\u03BD\u03C4\u03B1\u03B9 \u03BA\u03B1\u03B9 \u03B1\u03C6\u03B1\u03B9\u03C1\u03BF\u03CD\u03BD\u03C4\u03B1\u03B9 \u03B1\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B1, \u03B4\u03B9\u03B1\u03C3\u03C6\u03B1\u03BB\u03AF\u03B6\u03BF\u03BD\u03C4\u03B1\u03C2 \u03AD\u03BD\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF \u03C4\u03B5\u03BB\u03B9\u03BA\u03CC \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF. -autoSplitPDF.formPrompt=\u03A5\u03C0\u03BF\u03B2\u03BF\u03BB\u03AE PDF \u03C0\u03BF\u03C5 \u03C0\u03B5\u03C1\u03B9\u03AD\u03C7\u03B5\u03B9 \u03B4\u03B9\u03B1\u03B9\u03C1\u03AD\u03C4\u03B5\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD Stirling-PDF: -autoSplitPDF.duplexMode=\u039B\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 \u03B4\u03B9\u03C0\u03BB\u03AE\u03C2 \u03CC\u03C8\u03B7\u03C2 (\u03A3\u03AC\u03C1\u03C9\u03C3\u03B7 \u03BC\u03C0\u03C1\u03BF\u03C3\u03C4\u03AC \u03BA\u03B1\u03B9 \u03C0\u03AF\u03C3\u03C9) -autoSplitPDF.dividerDownload1=\u039B\u03AE\u03C8\u03B7 'Auto Splitter Divider (minimal).pdf' -autoSplitPDF.dividerDownload2=\u039B\u03AE\u03C8\u03B7 'Auto Splitter Divider (with instructions).pdf' -autoSplitPDF.submit=\u03A5\u03C0\u03BF\u03B2\u03BF\u03BB\u03AE +autoSplitPDF.title=Αυτόματη διαίρεση PDF +autoSplitPDF.header=Αυτόματη διαίρεση PDF +autoSplitPDF.description=Εκτυπώστε, εισάγετε, σαρώστε, ανεβάστε και αφήστε μας να διαχωρίσουμε αυτόματα τα έγγραφά σας. Δεν απαιτείται χειροκίνητη ταξινόμηση. +autoSplitPDF.selectText.1=Εκτυπώστε μερικά φύλλα διαχωρισμού από κάτω (το ασπρόμαυρο είναι μια χαρά). +autoSplitPDF.selectText.2=Σαρώστε όλα τα έγγραφά σας ταυτόχρονα, με το να εισάγετε το διαχωριστικό φύλλο μεταξύ τους. +autoSplitPDF.selectText.3=Ανεβάστε ένα μεγάλο σαρωμένο PDF αρχείο και αφήστε το Stirling PDF να χειριστεί τα υπόλοιπα +autoSplitPDF.selectText.4=Οι σελίδες διαχωρισμού εντοπίζονται και αφαιρούνται αυτόματα, διασφαλίζοντας ένα προσεγμένο τελικό έγγραφο. +autoSplitPDF.formPrompt=Υποβολή PDF που περιέχει διαιρέτες σελίδων Stirling-PDF: +autoSplitPDF.duplexMode=Λειτουργία διπλής όψης (Σάρωση μπροστά και πίσω) +autoSplitPDF.dividerDownload1=Λήψη 'Auto Splitter Divider (minimal).pdf' +autoSplitPDF.dividerDownload2=Λήψη 'Auto Splitter Divider (with instructions).pdf' +autoSplitPDF.submit=Υποβολή #pipeline @@ -567,406 +606,459 @@ pipeline.title=Pipeline #pageLayout -pageLayout.title=\u0394\u03B9\u03AC\u03C4\u03B1\u03BE\u03B7 \u03C0\u03BF\u03BB\u03BB\u03CE\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -pageLayout.header=\u0394\u03B9\u03AC\u03C4\u03B1\u03BE\u03B7 \u03C0\u03BF\u03BB\u03BB\u03CE\u03BD \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -pageLayout.pagesPerSheet=\u03A3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 \u03B1\u03BD\u03AC \u03C6\u03CD\u03BB\u03BB\u03BF: -pageLayout.addBorder=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C0\u03B5\u03C1\u03B9\u03B3\u03C1\u03B1\u03BC\u03BC\u03AC\u03C4\u03C9\u03BD -pageLayout.submit=\u03A5\u03C0\u03BF\u03B2\u03BF\u03BB\u03AE +pageLayout.title=Διάταξη πολλών σελίδων +pageLayout.header=Διάταξη πολλών σελίδων +pageLayout.pagesPerSheet=Σελίδες ανά φύλλο: +pageLayout.addBorder=Προσθήκη περιγραμμάτων +pageLayout.submit=Υποβολή #scalePages -scalePages.title=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03BA\u03BB\u03AF\u03BC\u03B1\u03BA\u03B1\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 -scalePages.header=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03BA\u03BB\u03AF\u03BC\u03B1\u03BA\u03B1\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 -scalePages.pageSize=\u039C\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 \u03BC\u03B9\u03B1\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 \u03C4\u03BF\u03C5 \u03B5\u03B3\u03B3\u03C1\u03AC\u03C6\u03BF\u03C5. -scalePages.scaleFactor=\u0395\u03C0\u03AF\u03C0\u03B5\u03B4\u03BF \u03B6\u03BF\u03C5\u03BC (\u03C0\u03B5\u03C1\u03B9\u03BA\u03BF\u03C0\u03AE) \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2. -scalePages.submit=\u03A5\u03C0\u03BF\u03B2\u03BF\u03BB\u03AE +scalePages.title=Προσαρμογή κλίμακας σελίδας +scalePages.header=Προσαρμογή κλίμακας σελίδας +scalePages.pageSize=Μέγεθος μιας σελίδας του εγγράφου. +scalePages.scaleFactor=Επίπεδο ζουμ (περικοπή) σελίδας. +scalePages.submit=Υποβολή #certSign -certSign.title=\u03A5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE \u03A0\u03B9\u03C3\u03C4\u03BF\u03C0\u03BF\u03B9\u03B7\u03C4\u03B9\u03BA\u03BF\u03CD -certSign.header=\u03A5\u03C0\u03BF\u03B3\u03C1\u03AC\u03C8\u03C4\u03B5 \u03AD\u03BD\u03B1 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF PDF \u03BC\u03B5 \u03C4\u03BF \u03C0\u03B9\u03C3\u03C4\u03BF\u03C0\u03BF\u03B9\u03B7\u03C4\u03B9\u03BA\u03CC \u03C3\u03B1\u03C2 (\u0395\u03C1\u03B3\u03B1\u03C3\u03AF\u03B1 \u03C3\u03B5 \u03B5\u03BE\u03AD\u03BB\u03B9\u03BE\u03B7) -certSign.selectPDF=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 PDF \u03B3\u03B9\u03B1 \u03C5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE: -certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below. -certSign.selectKey=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF \u03C4\u03BF\u03C5 \u03B9\u03B4\u03B9\u03C9\u03C4\u03B9\u03BA\u03BF\u03CD \u03BA\u03BB\u03B5\u03B9\u03B4\u03B9\u03BF\u03CD \u03C3\u03B1\u03C2 (\u03BC\u03BF\u03C1\u03C6\u03AE PKCS#8, \u03BC\u03C0\u03BF\u03C1\u03B5\u03AF \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 .pem \u03AE .der): -certSign.selectCert=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF \u03C0\u03B9\u03C3\u03C4\u03BF\u03C0\u03BF\u03B9\u03B7\u03C4\u03B9\u03BA\u03BF\u03CD \u03C3\u03B1\u03C2 (\u03BC\u03BF\u03C1\u03C6\u03AE X.509, \u03BC\u03C0\u03BF\u03C1\u03B5\u03AF \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 .pem \u03AE .der): -certSign.selectP12=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03C4\u03BF \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF PKCS#12 Keystore (.p12 \u03AE .pfx) (\u03A0\u03C1\u03BF\u03B1\u03B9\u03C1\u03B5\u03C4\u03B9\u03BA\u03CC, \u03B5\u03AC\u03BD \u03C0\u03B1\u03C1\u03AD\u03C7\u03B5\u03C4\u03B1\u03B9, \u03B8\u03B1 \u03C0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03C0\u03B5\u03C1\u03B9\u03AD\u03C7\u03B5\u03B9 \u03C4\u03BF \u03B9\u03B4\u03B9\u03C9\u03C4\u03B9\u03BA\u03CC \u03BA\u03BB\u03B5\u03B9\u03B4\u03AF \u03BA\u03B1\u03B9 \u03C4\u03BF \u03C0\u03B9\u03C3\u03C4\u03BF\u03C0\u03BF\u03B9\u03B7\u03C4\u03B9\u03BA\u03CC \u03C3\u03B1\u03C2): -certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore): -certSign.certType=\u03A4\u03CD\u03C0\u03BF\u03C2 \u03A0\u03B9\u03C3\u03C4\u03BF\u03C0\u03BF\u03B9\u03B7\u03C4\u03B9\u03BA\u03BF\u03CD -certSign.password=\u0395\u03B9\u03C3\u03B1\u03B3\u03AC\u03B3\u03B5\u03C4\u03B5 \u03C4\u03BF\u03BD \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC \u03C4\u03BF\u03C5 Keystore \u03AE \u03C4\u03BF\u03C5 \u0399\u03B4\u03B9\u03C9\u03C4\u03B9\u03BA\u03BF\u03CD \u039A\u03BB\u03B5\u03B9\u03B4\u03B9\u03BF\u03CD (\u03B5\u03AC\u03BD \u03C5\u03C0\u03AC\u03C1\u03C7\u03B5\u03B9): -certSign.showSig=\u0395\u03BC\u03C6\u03AC\u03BD\u03B9\u03C3\u03B7 \u03A5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03C2 -certSign.reason=\u0391\u03B9\u03C4\u03AF\u03B1 -certSign.location=\u03A4\u03BF\u03C0\u03BF\u03B8\u03B5\u03C3\u03AF\u03B1 -certSign.name=\u038C\u03BD\u03BF\u03BC\u03B1 -certSign.submit=\u03A5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE PDF +certSign.title=Υπογραφή Πιστοποιητικού +certSign.header=Υπογράψτε ένα αρχείο PDF με το πιστοποιητικό σας (Εργασία σε εξέλιξη) +certSign.selectPDF=Επιλογή αρχείου PDF για υπογραφή: +certSign.jksNote=Σημείωση: Εάν ο τύπος πιστοποιητικού σας δεν αναφέρεται παρακάτω, μετατρέψτε το σε αρχείο Java Keystore (.jks) χρησιμοποιώντας το εργαλείο γραμμής εντολών keytool. Στη συνέχεια, επιλέξτε την επιλογή αρχείου .jks παρακάτω. +certSign.selectKey=Επιλέξτε το αρχείο του ιδιωτικού κλειδιού σας (μορφή PKCS#8, μπορεί να είναι .pem ή .der): +certSign.selectCert=Επιλέξτε το αρχείο πιστοποιητικού σας (μορφή X.509, μπορεί να είναι .pem ή .der): +certSign.selectP12=Επιλέξτε το αρχείο PKCS#12 Keystore (.p12 ή .pfx) (Προαιρετικό, εάν παρέχεται, θα πρέπει να περιέχει το ιδιωτικό κλειδί και το πιστοποιητικό σας): +certSign.selectJKS=Επιλέξτε το αρχείο Java Keystore (.jks ή .keystore): +certSign.certType=Τύπος Πιστοποιητικού +certSign.password=Εισαγάγετε τον κωδικό του Keystore ή του Ιδιωτικού Κλειδιού (εάν υπάρχει): +certSign.showSig=Εμφάνιση Υπογραφής +certSign.reason=Αιτία +certSign.location=Τοποθεσία +certSign.name=Όνομα +certSign.submit=Υπογραφή PDF #removeBlanks -removeBlanks.title=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u039A\u03B5\u03BD\u03CE\u03BD -removeBlanks.header=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u039A\u03B5\u03BD\u03CE\u03BD \u03A3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -removeBlanks.threshold=\u038C\u03C1\u03B9\u03BF \u03BB\u03B5\u03C5\u03BA\u03CC\u03C4\u03B7\u03C4\u03B1\u03C2 pixel: -removeBlanks.thresholdDesc=\u038C\u03C1\u03B9\u03BF \u03B3\u03B9\u03B1 \u03C4\u03BF\u03BD \u03C0\u03C1\u03BF\u03C3\u03B4\u03B9\u03BF\u03C1\u03B9\u03C3\u03BC\u03CC \u03C4\u03BF\u03C5 \u03C0\u03CC\u03C3\u03BF \u03BB\u03B5\u03C5\u03BA\u03CC \u03C0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03AD\u03BD\u03B1 \u03BB\u03B5\u03C5\u03BA\u03CC \u03B5\u03B9\u03BA\u03BF\u03BD\u03BF\u03C3\u03C4\u03BF\u03B9\u03C7\u03B5\u03AF\u03BF (pixel) \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03C4\u03B1\u03BE\u03B9\u03BD\u03BF\u03BC\u03B7\u03B8\u03B5\u03AF \u03C9\u03C2 "\u039B\u03B5\u03C5\u03BA\u03CC". 0 = \u039C\u03B1\u03CD\u03C1\u03BF, 255 \u03BA\u03B1\u03B8\u03B1\u03C1\u03CC \u03BB\u03B5\u03C5\u03BA\u03CC. -removeBlanks.whitePercent=\u03A0\u03BF\u03C3\u03BF\u03C3\u03C4\u03CC \u039B\u03B5\u03C5\u03BA\u03BF\u03CD (%): -removeBlanks.whitePercentDesc=\u03A4\u03BF \u03C0\u03BF\u03C3\u03BF\u03C3\u03C4\u03CC \u03C4\u03B7\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 \u03C0\u03BF\u03C5 \u03C0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 "\u03BB\u03B5\u03C5\u03BA\u03AC" pixel \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03B1\u03C6\u03B1\u03B9\u03C1\u03B5\u03B8\u03B5\u03AF -removeBlanks.submit=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u039A\u03B5\u03BD\u03CE\u03BD +removeBlanks.title=Αφαίρεση Κενών +removeBlanks.header=Αφαίρεση Κενών Σελίδων +removeBlanks.threshold=Όριο λευκότητας pixel: +removeBlanks.thresholdDesc=Όριο για τον προσδιορισμό του πόσο λευκό πρέπει να είναι ένα λευκό εικονοστοιχείο (pixel) για να ταξινομηθεί ως "Λευκό". 0 = Μαύρο, 255 καθαρό λευκό. +removeBlanks.whitePercent=Ποσοστό Λευκού (%): +removeBlanks.whitePercentDesc=Το ποσοστό της σελίδας που πρέπει να είναι "λευκά" pixel για να αφαιρεθεί +removeBlanks.submit=Αφαίρεση Κενών #removeAnnotations -removeAnnotations.title=Remove Annotations -removeAnnotations.header=Remove Annotations -removeAnnotations.submit=Remove +removeAnnotations.title=Κατάργηση σχολιασμών +removeAnnotations.header=Κατάργηση σχολιασμών +removeAnnotations.submit=Κατάργηση #compare -compare.title=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7 -compare.header=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7 PDFs -compare.document.1=\u0388\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF 1 -compare.document.2=\u0388\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF 2 -compare.submit=\u03A3\u03CD\u03B3\u03BA\u03C1\u03B9\u03C3\u03B7 +compare.title=Σύγκριση +compare.header=Σύγκριση PDFs +compare.document.1=Έγγραφο 1 +compare.document.2=Έγγραφο 2 +compare.submit=Σύγκριση +#BookToPDF +BookToPDF.title=Books και Comics σε PDF +BookToPDF.header=Book σε PDF +BookToPDF.credit=Χρησιμοποιεί Caliber +BookToPDF.submit=Μετατροπή + +#PDFToBook +PDFToBook.title=PDF σε Book +PDFToBook.header=PDF σε Book +PDFToBook.selectText.1=Μορφή +PDFToBook.credit=Χρησιμοποιεί Caliber +PDFToBook.submit=Μετατροπή #sign -sign.title=\u03A5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE -sign.header=\u03A5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE PDFs -sign.upload=\u0391\u03BD\u03AD\u03B2\u03B1\u03C3\u03BC\u03B1 \u0395\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 -sign.draw=\u03A3\u03C7\u03B5\u03B4\u03AF\u03B1\u03C3\u03B7 \u03C5\u03C0\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03C2 -sign.text=\u0395\u03B9\u03C3\u03B1\u03B3\u03C9\u03B3\u03AE \u03BA\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5 -sign.clear=\u039A\u03B1\u03B8\u03AC\u03C1\u03B9\u03C3\u03BC\u03B1 -sign.add=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 +sign.title=Υπογραφή +sign.header=Υπογραφή PDFs +sign.upload=Ανέβασμα Εικόνας +sign.draw=Σχεδίαση υπογραφής +sign.text=Εισαγωγή κειμένου +sign.clear=Καθάρισμα +sign.add=Προσθήκη #repair -repair.title=\u0395\u03C0\u03B9\u03B4\u03B9\u03CC\u03C1\u03B8\u03C9\u03C3\u03B7 -repair.header=\u0395\u03C0\u03B9\u03B4\u03B9\u03CC\u03C1\u03B8\u03C9\u03C3\u03B7 PDFs -repair.submit=\u0395\u03C0\u03B9\u03B4\u03B9\u03CC\u03C1\u03B8\u03C9\u03C3\u03B7 +repair.title=Επιδιόρθωση +repair.header=Επιδιόρθωση PDFs +repair.submit=Επιδιόρθωση #flatten flatten.title=Flatten flatten.header=Flatten PDFs +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Flatten #ScannerImageSplit -ScannerImageSplit.selectText.1=\u038C\u03C1\u03B9\u03BF \u03B3\u03C9\u03BD\u03AF\u03B1\u03C2: -ScannerImageSplit.selectText.2=\u039F\u03C1\u03AF\u03B6\u03B5\u03B9 \u03C4\u03B7\u03BD \u03B5\u03BB\u03AC\u03C7\u03B9\u03C3\u03C4\u03B7 \u03B1\u03C0\u03CC\u03BB\u03C5\u03C4\u03B7 \u03B3\u03C9\u03BD\u03AF\u03B1 \u03C0\u03BF\u03C5 \u03B1\u03C0\u03B1\u03B9\u03C4\u03B5\u03AF\u03C4\u03B1\u03B9 \u03B3\u03B9\u03B1 \u03C4\u03B7\u03BD \u03C0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE \u03C4\u03B7\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 (\u03C0\u03C1\u03BF\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE: 10). -ScannerImageSplit.selectText.3=\u0391\u03BD\u03B5\u03BA\u03C4\u03B9\u03BA\u03CC\u03C4\u03B7\u03C4\u03B1 (Tolerance): -ScannerImageSplit.selectText.4=\u039A\u03B1\u03B8\u03BF\u03C1\u03AF\u03B6\u03B5\u03B9 \u03C4\u03BF \u03B5\u03CD\u03C1\u03BF\u03C2 \u03C4\u03B7\u03C2 \u03C7\u03C1\u03C9\u03BC\u03B1\u03C4\u03B9\u03BA\u03AE\u03C2 \u03B4\u03B9\u03B1\u03BA\u03CD\u03BC\u03B1\u03BD\u03C3\u03B7\u03C2 \u03B3\u03CD\u03C1\u03C9 \u03B1\u03C0\u03CC \u03C4\u03BF \u03B5\u03BA\u03C4\u03B9\u03BC\u03CE\u03BC\u03B5\u03BD\u03BF \u03C7\u03C1\u03CE\u03BC\u03B1 \u03C6\u03CC\u03BD\u03C4\u03BF\u03C5 (\u03C0\u03C1\u03BF\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE: 30). -ScannerImageSplit.selectText.5=\u0395\u03BB\u03AC\u03C7\u03B9\u03C3\u03C4\u03B7 \u03A0\u03B5\u03C1\u03B9\u03BF\u03C7\u03AE: -ScannerImageSplit.selectText.6=\u039F\u03C1\u03AF\u03B6\u03B5\u03B9 \u03C4\u03BF \u03B5\u03BB\u03AC\u03C7\u03B9\u03C3\u03C4\u03BF \u03CC\u03C1\u03B9\u03BF \u03B5\u03C0\u03B9\u03C6\u03AC\u03BD\u03B5\u03B9\u03B1\u03C2 \u03B3\u03B9\u03B1 \u03BC\u03B9\u03B1 \u03C6\u03C9\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AF\u03B1 (\u03C0\u03C1\u03BF\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE: 10000). -ScannerImageSplit.selectText.7=\u0395\u03BB\u03AC\u03C7\u03B9\u03C3\u03C4\u03B7 \u03B5\u03C0\u03B9\u03C6\u03AC\u03BD\u03B5\u03B9\u03B1 \u03C0\u03B5\u03C1\u03B9\u03B3\u03C1\u03AC\u03BC\u03BC\u03B1\u03C4\u03BF\u03C2: -ScannerImageSplit.selectText.8=\u03A1\u03C5\u03B8\u03BC\u03AF\u03B6\u03B5\u03B9 \u03C4\u03BF \u03B5\u03BB\u03AC\u03C7\u03B9\u03C3\u03C4\u03BF \u03CC\u03C1\u03B9\u03BF \u03C0\u03B5\u03C1\u03B9\u03B3\u03C1\u03AC\u03BC\u03BC\u03B1\u03C4\u03BF\u03C2 \u03B3\u03B9\u03B1 \u03BC\u03B9\u03B1 \u03C6\u03C9\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AF\u03B1 -ScannerImageSplit.selectText.9=\u039C\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 \u03C0\u03B5\u03C1\u03B9\u03B3\u03C1\u03AC\u03BC\u03BC\u03B1\u03C4\u03BF\u03C2: -ScannerImageSplit.selectText.10=\u039F\u03C1\u03AF\u03B6\u03B5\u03B9 \u03C4\u03BF \u03BC\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 \u03C4\u03BF\u03C5 \u03C0\u03B5\u03C1\u03B9\u03B3\u03C1\u03AC\u03BC\u03BC\u03B1\u03C4\u03BF\u03C2 \u03C0\u03BF\u03C5 \u03C0\u03C1\u03BF\u03C3\u03C4\u03AF\u03B8\u03B5\u03C4\u03B1\u03B9 \u03BA\u03B1\u03B9 \u03B1\u03C6\u03B1\u03B9\u03C1\u03B5\u03AF\u03C4\u03B1\u03B9 \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03B1\u03C0\u03BF\u03C4\u03C1\u03AD\u03C0\u03BF\u03BD\u03C4\u03B1\u03B9 \u03BB\u03B5\u03C5\u03BA\u03AC \u03C0\u03B5\u03C1\u03B9\u03B3\u03C1\u03AC\u03BC\u03BC\u03B1\u03C4\u03B1 \u03C3\u03C4\u03B7\u03BD \u03AD\u03BE\u03BF\u03B4\u03BF (\u03C0\u03C1\u03BF\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE: 1). +ScannerImageSplit.selectText.1=Όριο γωνίας: +ScannerImageSplit.selectText.2=Ορίζει την ελάχιστη απόλυτη γωνία που απαιτείται για την περιστροφή της εικόνας (προεπιλογή: 10). +ScannerImageSplit.selectText.3=Ανεκτικότητα (Tolerance): +ScannerImageSplit.selectText.4=Καθορίζει το εύρος της χρωματικής διακύμανσης γύρω από το εκτιμώμενο χρώμα φόντου (προεπιλογή: 30). +ScannerImageSplit.selectText.5=Ελάχιστη Περιοχή: +ScannerImageSplit.selectText.6=Ορίζει το ελάχιστο όριο επιφάνειας για μια φωτογραφία (προεπιλογή: 10000). +ScannerImageSplit.selectText.7=Ελάχιστη επιφάνεια περιγράμματος: +ScannerImageSplit.selectText.8=Ρυθμίζει το ελάχιστο όριο περιγράμματος για μια φωτογραφία +ScannerImageSplit.selectText.9=Μέγεθος περιγράμματος: +ScannerImageSplit.selectText.10=Ορίζει το μέγεθος του περιγράμματος που προστίθεται και αφαιρείται για να αποτρέπονται λευκά περιγράμματα στην έξοδο (προεπιλογή: 1). #OCR -ocr.title=\u039F\u03C0\u03C4\u03B9\u03BA\u03AE \u03B1\u03BD\u03B1\u03B3\u03BD\u03CE\u03C1\u03B9\u03C3\u03B7 \u03C7\u03B1\u03C1\u03B1\u03BA\u03C4\u03AE\u03C1\u03C9\u03BD (OCR) / \u03A3\u03B1\u03C1\u03CE\u03C3\u03B5\u03B9\u03C2 Cleanup -ocr.header=\u03A3\u03B1\u03C1\u03CE\u03C3\u03B5\u03B9\u03C2 Cleanup / OCR (Optical Character Recognition - \u039F\u03C0\u03C4\u03B9\u03BA\u03AE \u03B1\u03BD\u03B1\u03B3\u03BD\u03CE\u03C1\u03B9\u03C3\u03B7 \u03C7\u03B1\u03C1\u03B1\u03BA\u03C4\u03AE\u03C1\u03C9\u03BD) -ocr.selectText.1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03B3\u03BB\u03CE\u03C3\u03C3\u03B5\u03C2 \u03C0\u03BF\u03C5 \u03C0\u03C1\u03CC\u03BA\u03B5\u03B9\u03C4\u03B1\u03B9 \u03BD\u03B1 \u03B5\u03BD\u03C4\u03BF\u03C0\u03B9\u03C3\u03C4\u03BF\u03CD\u03BD \u03C3\u03C4\u03BF PDF (\u039F\u03B9 \u03C0\u03BF\u03C5 \u03B1\u03BD\u03B1\u03C6\u03AD\u03C1\u03BF\u03BD\u03C4\u03B1\u03B9 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03B1\u03C5\u03C4\u03AD\u03C2 \u03C0\u03BF\u03C5 \u03B1\u03BD\u03B9\u03C7\u03BD\u03B5\u03CD\u03BF\u03BD\u03C4\u03B1\u03B9 \u03B1\u03C5\u03C4\u03AE\u03BD \u03C4\u03B7 \u03C3\u03C4\u03B9\u03B3\u03BC\u03AE): -ocr.selectText.2=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AE\u03C3\u03C4\u03B5 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF \u03BA\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5 \u03C0\u03BF\u03C5 \u03C0\u03B5\u03C1\u03B9\u03AD\u03C7\u03B5\u03B9 \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF OCR \u03BC\u03B1\u03B6\u03AF \u03BC\u03B5 \u03C4\u03BF PDF \u03C0\u03BF\u03C5 \u03AD\u03C7\u03B5\u03B9 \u03C5\u03C0\u03BF\u03B2\u03BB\u03B7\u03B8\u03B5\u03AF \u03C3\u03B5 OCR -ocr.selectText.3=\u039F\u03B9 \u03C3\u03C9\u03C3\u03C4\u03AD\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 \u03C3\u03B1\u03C1\u03CE\u03B8\u03B7\u03BA\u03B1\u03BD \u03BC\u03B5 \u03BB\u03BF\u03BE\u03AE \u03B3\u03C9\u03BD\u03AF\u03B1 \u03C0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03AD\u03C6\u03BF\u03BD\u03C4\u03AC\u03C2 \u03C4\u03B5\u03C2 \u03C3\u03C4\u03B7 \u03B8\u03AD\u03C3\u03B7 \u03C4\u03BF\u03C5\u03C2 -ocr.selectText.4=C\u039A\u03B1\u03B8\u03B1\u03C1\u03AF\u03C3\u03C4\u03B5 \u03C4\u03B7 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1, \u03CE\u03C3\u03C4\u03B5 \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03BB\u03B9\u03B3\u03CC\u03C4\u03B5\u03C1\u03BF \u03C0\u03B9\u03B8\u03B1\u03BD\u03CC \u03C4\u03BF OCR \u03BD\u03B1 \u03B2\u03C1\u03B5\u03B9 \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF \u03C3\u03C4\u03BF \u03B8\u03CC\u03C1\u03C5\u03B2\u03BF \u03C6\u03CC\u03BD\u03C4\u03BF\u03C5 (background noise). (\u039A\u03B1\u03BC\u03AF\u03B1 \u03B1\u03BB\u03BB\u03B1\u03B3\u03AE \u03C3\u03C4\u03BF \u03B5\u03BE\u03B1\u03B3\u03CC\u03BC\u03B5\u03BD\u03BF) -ocr.selectText.5=\u039A\u03B1\u03B8\u03B1\u03C1\u03AF\u03C3\u03C4\u03B5 \u03C4\u03B7 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1, \u03CE\u03C3\u03C4\u03B5 \u03BD\u03B1 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03BB\u03B9\u03B3\u03CC\u03C4\u03B5\u03C1\u03BF \u03C0\u03B9\u03B8\u03B1\u03BD\u03CC \u03C4\u03BF OCR \u03BD\u03B1 \u03B2\u03C1\u03B5\u03B9 \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF \u03C3\u03C4\u03BF \u03B8\u03CC\u03C1\u03C5\u03B2\u03BF \u03C6\u03CC\u03BD\u03C4\u03BF\u03C5, \u03B4\u03B9\u03B1\u03C4\u03B7\u03C1\u03B5\u03AF \u03C4\u03B7\u03BD \u03B5\u03BA\u03BA\u03B1\u03B8\u03AC\u03C1\u03B9\u03C3\u03B7 \u03C3\u03C4\u03BF \u03C0\u03B1\u03C1\u03B1\u03B3\u03CC\u03BC\u03B5\u03BD\u03BF. -ocr.selectText.6=\u0391\u03B3\u03BD\u03BF\u03B5\u03AF \u03C4\u03B9\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 \u03C0\u03BF\u03C5 \u03AD\u03C7\u03BF\u03C5\u03BD \u03B4\u03B9\u03B1\u03B4\u03C1\u03B1\u03C3\u03C4\u03B9\u03BA\u03CC \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF, \u03BC\u03CC\u03BD\u03BF \u03C4\u03B9\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 OCR \u03C0\u03BF\u03C5 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B5\u03C2 -ocr.selectText.7=\u0395\u03C0\u03B9\u03B2\u03BF\u03BB\u03AE OCR, \u03B8\u03B1 \u03C0\u03C1\u03B1\u03B3\u03BC\u03B1\u03C4\u03BF\u03C0\u03BF\u03B9\u03AE\u03C3\u03B5\u03B9 \u039F\u03C0\u03C4\u03B9\u03BA\u03AE \u03B1\u03BD\u03B1\u03B3\u03BD\u03CE\u03C1\u03B9\u03C3\u03B7 \u03C7\u03B1\u03C1\u03B1\u03BA\u03C4\u03AE\u03C1\u03C9\u03BD \u03C3\u03B5 \u03BA\u03AC\u03B8\u03B5 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1 \u03B1\u03C6\u03B1\u03B9\u03C1\u03CE\u03BD\u03C4\u03B1\u03C2 \u03CC\u03BB\u03BF \u03C4\u03B1 \u03C3\u03C4\u03BF\u03B9\u03C7\u03B5\u03AF\u03B1 \u03C4\u03BF\u03C5 \u03B1\u03C1\u03C7\u03B9\u03BA\u03BF\u03CD \u03BA\u03B5\u03AF\u03BC\u03AD\u03BD\u03BF\u03C5 -ocr.selectText.8=\u039A\u03B1\u03BD\u03BF\u03BD\u03B9\u03BA\u03CC (\u0398\u03B1 \u03C0\u03B1\u03C1\u03AC\u03BE\u03B5\u03B9 \u03C3\u03C6\u03AC\u03BB\u03BC\u03B1 \u03B1\u03BD \u03C4\u03BF PDF \u03C0\u03B5\u03C1\u03B9\u03AD\u03C7\u03B5\u03B9 \u03BA\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF) -ocr.selectText.9=\u0395\u03C0\u03B9\u03C0\u03C1\u03CC\u03C3\u03B8\u03B5\u03C4\u03B5\u03C2 \u03A1\u03C5\u03B8\u03BC\u03AF\u03C3\u03B5\u03B9\u03C2 -ocr.selectText.10=\u039B\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 OCR -ocr.selectText.11=\u039A\u03B1\u03C4\u03AC\u03C1\u03B3\u03B7\u03C3\u03B7 \u03B5\u03B9\u03BA\u03CC\u03BD\u03C9\u03BD \u03BC\u03B5\u03C4\u03AC \u03C4\u03BF OCR (\u039A\u03B1\u03C4\u03B1\u03C1\u03B3\u03B5\u03AF \u039F\u039B\u0395\u03A3 \u03C4\u03B9\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B5\u03C2, \u03B5\u03AF\u03BD\u03B1\u03B9 \u03C7\u03C1\u03AE\u03C3\u03B9\u03BC\u03BF \u03BC\u03CC\u03BD\u03BF \u03B1\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03B5\u03AF \u03BC\u03AD\u03C1\u03BF\u03C2 \u03C4\u03BF\u03C5 \u03B2\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03BC\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE\u03C2) -ocr.selectText.12=\u03A4\u03CD\u03C0\u03BF\u03C2 \u03B1\u03C0\u03CC\u03B4\u03BF\u03C3\u03B7\u03C2 (\u0393\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C7\u03C9\u03C1\u03B7\u03BC\u03AD\u03BD\u03BF\u03C5\u03C2) -ocr.help=\u0394\u03B9\u03B1\u03B2\u03AC\u03C3\u03C4\u03B5 \u03B1\u03C5\u03C4\u03AE\u03BD \u03C4\u03B7\u03BD \u03C4\u03B5\u03BA\u03BC\u03B7\u03C1\u03AF\u03C9\u03C3\u03B7 \u03C3\u03C7\u03B5\u03C4\u03B9\u03BA\u03AC \u03BC\u03B5 \u03C4\u03BF\u03BD \u03C4\u03C1\u03CC\u03C0\u03BF \u03C7\u03C1\u03AE\u03C3\u03B7\u03C2 \u03B1\u03C5\u03C4\u03AE\u03C2 \u03B3\u03B9\u03B1 \u03AC\u03BB\u03BB\u03B5\u03C2 \u03B3\u03BB\u03CE\u03C3\u03C3\u03B5\u03C2 \u03AE/\u03BA\u03B1\u03B9 \u03BC\u03B7 \u03C7\u03C1\u03AE\u03C3\u03B7\u03C2 \u03C3\u03B5 docker -ocr.credit=\u0391\u03C5\u03C4\u03AE \u03B7 \u03C5\u03C0\u03B7\u03C1\u03B5\u03C3\u03AF\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF OCRmyPDF \u03BA\u03B1\u03B9 Tesseract \u03B3\u03B9\u03B1 OCR. -ocr.submit=\u0395\u03C0\u03B5\u03BE\u03B5\u03C1\u03B3\u03B1\u03C3\u03AF\u03B1 PDF \u03BC\u03B5 OCR +ocr.title=Οπτική αναγνώριση χαρακτήρων (OCR) / Σαρώσεις Cleanup +ocr.header=Σαρώσεις Cleanup / OCR (Optical Character Recognition - Οπτική αναγνώριση χαρακτήρων) +ocr.selectText.1=Επιλέξτε γλώσσες που πρόκειται να εντοπιστούν στο PDF (Οι που αναφέρονται είναι αυτές που ανιχνεύονται αυτήν τη στιγμή): +ocr.selectText.2=Δημιουργήστε αρχείο κειμένου που περιέχει κείμενο OCR μαζί με το PDF που έχει υποβληθεί σε OCR +ocr.selectText.3=Οι σωστές σελίδες σαρώθηκαν με λοξή γωνία περιστρέφοντάς τες στη θέση τους +ocr.selectText.4=CΚαθαρίστε τη σελίδα, ώστε να είναι λιγότερο πιθανό το OCR να βρει κείμενο στο θόρυβο φόντου (background noise). (Καμία αλλαγή στο εξαγόμενο) +ocr.selectText.5=Καθαρίστε τη σελίδα, ώστε να είναι λιγότερο πιθανό το OCR να βρει κείμενο στο θόρυβο φόντου, διατηρεί την εκκαθάριση στο παραγόμενο. +ocr.selectText.6=Αγνοεί τις σελίδες που έχουν διαδραστικό κείμενο, μόνο τις σελίδες OCR που είναι εικόνες +ocr.selectText.7=Επιβολή OCR, θα πραγματοποιήσει Οπτική αναγνώριση χαρακτήρων σε κάθε σελίδα αφαιρώντας όλο τα στοιχεία του αρχικού κείμένου +ocr.selectText.8=Κανονικό (Θα παράξει σφάλμα αν το PDF περιέχει κείμενο) +ocr.selectText.9=Επιπρόσθετες Ρυθμίσεις +ocr.selectText.10=Λειτουργία OCR +ocr.selectText.11=Κατάργηση εικόνων μετά το OCR (Καταργεί ΟΛΕΣ τις εικόνες, είναι χρήσιμο μόνο αν αποτελεί μέρος του βήματος μετατροπής) +ocr.selectText.12=Τύπος απόδοσης (Για προχωρημένους) +ocr.help=Διαβάστε αυτήν την τεκμηρίωση σχετικά με τον τρόπο χρήσης αυτής για άλλες γλώσσες ή/και μη χρήσης σε docker +ocr.credit=Αυτή η υπηρεσία χρησιμοποιεί OCRmyPDF και Tesseract για OCR. +ocr.submit=Επεξεργασία PDF με OCR #extractImages -extractImages.title=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u0395\u03B9\u03BA\u03CC\u03BD\u03C9\u03BD -extractImages.header=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE \u0395\u03B9\u03BA\u03CC\u03BD\u03C9\u03BD -extractImages.selectText=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 \u03BC\u03BF\u03C1\u03C6\u03AE \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03BC\u03B5\u03C4\u03B1\u03C4\u03C1\u03AD\u03C8\u03B5\u03C4\u03B5 \u03C4\u03B9\u03C2 \u03B5\u03BE\u03B1\u03B3\u03CC\u03BC\u03B5\u03BD\u03B5\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B5\u03C2 -extractImages.submit=\u0395\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE +extractImages.title=Εξαγωγή Εικόνων +extractImages.header=Εξαγωγή Εικόνων +extractImages.selectText=Επιλέξτε μορφή εικόνας για να μετατρέψετε τις εξαγόμενες εικόνες +extractImages.submit=Εξαγωγή #File to PDF -fileToPDF.title=\u0391\u03C1\u03C7\u03B5\u03AF\u03BF \u03C3\u03B5 PDF -fileToPDF.header=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03BF\u03C0\u03BF\u03B9\u03BF\u03C5\u03B4\u03AE\u03C0\u03BF\u03C4\u03B5 \u03B1\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 \u03C3\u03B5 PDF -fileToPDF.credit=\u0391\u03C5\u03C4\u03AE \u03B7 \u03C5\u03C0\u03B7\u03C1\u03B5\u03C3\u03AF\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF LibreOffice \u03BA\u03B1\u03B9 Unoconv \u03B3\u03B9\u03B1 \u03C4\u03B7\u03BD \u03BC\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD. -fileToPDF.supportedFileTypes=\u039F\u03B9 \u03C5\u03C0\u03BF\u03C3\u03C4\u03B7\u03C1\u03B9\u03B6\u03CC\u03BC\u03B5\u03BD\u03BF\u03B9 \u03C4\u03CD\u03C0\u03BF\u03B9 \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD \u03B8\u03B1 \u03C0\u03C1\u03AD\u03C0\u03B5\u03B9 \u03BD\u03B1 \u03C0\u03B5\u03C1\u03B9\u03BB\u03B1\u03BC\u03B2\u03AC\u03BD\u03BF\u03C5\u03BD \u03C4\u03B1 \u03C0\u03B1\u03C1\u03B1\u03BA\u03AC\u03C4\u03C9, \u03C9\u03C3\u03C4\u03CC\u03C3\u03BF, \u03B3\u03B9\u03B1 \u03BC\u03B9\u03B1 \u03C0\u03BB\u03AE\u03C1\u03B7 \u03B5\u03BD\u03B7\u03BC\u03B5\u03C1\u03C9\u03BC\u03AD\u03BD\u03B7 \u03BB\u03AF\u03C3\u03C4\u03B1 \u03BC\u03B5 \u03C4\u03B9\u03C2 \u03C5\u03C0\u03BF\u03C3\u03C4\u03B7\u03C1\u03B9\u03B6\u03CC\u03BC\u03B5\u03BD\u03B5\u03C2 \u03BC\u03BF\u03C1\u03C6\u03AD\u03C2, \u03B1\u03BD\u03B1\u03C4\u03C1\u03AD\u03BE\u03C4\u03B5 \u03C3\u03C4\u03B7\u03BD \u03C4\u03B5\u03BA\u03BC\u03B7\u03C1\u03AF\u03C9\u03C3\u03B7 \u03C4\u03BF\u03C5 LibreOffice -fileToPDF.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C3\u03B5 PDF +fileToPDF.title=Αρχείο σε PDF +fileToPDF.header=Μετατροπή οποιουδήποτε αρχείου σε PDF +fileToPDF.credit=Αυτή η υπηρεσία χρησιμοποιεί LibreOffice και Unoconv για την μετατροπή των αρχείων. +fileToPDF.supportedFileTypes=Οι υποστηριζόμενοι τύποι αρχείων θα πρέπει να περιλαμβάνουν τα παρακάτω, ωστόσο, για μια πλήρη ενημερωμένη λίστα με τις υποστηριζόμενες μορφές, ανατρέξτε στην τεκμηρίωση του LibreOffice +fileToPDF.submit=Μετατροπή σε PDF #compress -compress.title=\u03A3\u03C5\u03BC\u03C0\u03AF\u03B5\u03C3\u03B7 -compress.header=\u03A3\u03C5\u03BC\u03C0\u03AF\u03B5\u03C3\u03B7 PDF -compress.credit=\u0391\u03C5\u03C4\u03AE \u03B7 \u03C5\u03C0\u03B7\u03C1\u03B5\u03C3\u03AF\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF Ghostscript \u03B3\u03B9\u03B1 PDF \u03A3\u03C5\u03BC\u03C0\u03AF\u03B5\u03C3\u03B7/\u0392\u03B5\u03BB\u03C4\u03B9\u03C3\u03C4\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7. -compress.selectText.1=\u03A7\u03B5\u03B9\u03C1\u03BF\u03BA\u03AF\u03BD\u03B7\u03C4\u03B7 \u039B\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 - \u0391\u03C0\u03CC 1 \u03AD\u03C9\u03C2 4 -compress.selectText.2=\u0395\u03C0\u03AF\u03C0\u03B5\u03B4\u03BF \u0392\u03B5\u03BB\u03C4\u03B9\u03C3\u03C4\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7\u03C2: -compress.selectText.3=4 (\u03A0\u03BF\u03BB\u03CD \u03BA\u03B1\u03BA\u03CC \u03B3\u03B9\u03B1 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B5\u03C2 \u03BA\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5) -compress.selectText.4=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u039B\u03B5\u03B9\u03C4\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1 - Auto mode - \u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03CC\u03B6\u03B5\u03B9 \u03B1\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B1 \u03C4\u03B7\u03BD \u03C0\u03BF\u03B9\u03CC\u03C4\u03B7\u03C4\u03B1 \u03B3\u03B9\u03B1 \u03BB\u03AE\u03C8\u03B7 PDF \u03C3\u03C4\u03BF \u03B1\u03BA\u03C1\u03B9\u03B2\u03AD\u03C2 \u03BC\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 -compress.selectText.5=\u0391\u03BD\u03B1\u03BC\u03B5\u03BD\u03CC\u03BC\u03B5\u03BD\u03BF \u039C\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 PDF (\u03C0.\u03C7 25MB, 10.8MB, 25KB) -compress.submit=\u03A3\u03C5\u03BC\u03C0\u03AF\u03B5\u03C3\u03B7 +compress.title=Συμπίεση +compress.header=Συμπίεση PDF +compress.credit=Αυτή η υπηρεσία χρησιμοποιεί Ghostscript για PDF Συμπίεση/Βελτιστοποίηση. +compress.selectText.1=Χειροκίνητη Λειτουργία - Από 1 έως 4 +compress.selectText.2=Επίπεδο Βελτιστοποίησης: +compress.selectText.3=4 (Πολύ κακό για εικόνες κειμένου) +compress.selectText.4=Αυτόματη Λειτουργία - Auto mode - Προσαρμόζει αυτόματα την ποιότητα για λήψη PDF στο ακριβές μέγεθος +compress.selectText.5=Αναμενόμενο Μέγεθος PDF (π.χ 25MB, 10.8MB, 25KB) +compress.submit=Συμπίεση #Add image -addImage.title=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u0395\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 -addImage.header=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u0395\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 \u03C3\u03B5 PDF -addImage.everyPage=\u039A\u03AC\u03B8\u03B5 \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1? -addImage.upload=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u0395\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 -addImage.submit=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u0395\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 +addImage.title=Προσθήκη Εικόνας +addImage.header=Προσθήκη Εικόνας σε PDF +addImage.everyPage=Κάθε Σελίδα; +addImage.upload=Προσθήκη Εικόνας +addImage.submit=Προσθήκη Εικόνας #merge -merge.title=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 -merge.header=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 \u03C0\u03BF\u03BB\u03BB\u03B1\u03C0\u03BB\u03CE\u03BD PDFs (2+) -merge.sortByName=\u03A4\u03B1\u03BE\u03B9\u03BD\u03CC\u03BC\u03B7\u03C3\u03B7 \u03BC\u03B5 \u03B2\u03AC\u03C3\u03B7 \u03C4\u03BF \u038C\u03BD\u03BF\u03BC\u03B1 -merge.sortByDate=\u03A4\u03B1\u03BE\u03B9\u03BD\u03CC\u03BC\u03B7\u03C3\u03B7 \u03BC\u03B5 \u03B2\u03AC\u03C3\u03B7 \u03C4\u03B7\u03BD \u0397\u03BC\u03B5\u03C1\u03BF\u03BC\u03B7\u03BD\u03AF\u03B1 -merge.submit=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 +merge.title=Συγχώνευση +merge.header=Συγχώνευση πολλαπλών PDFs (2+) +merge.sortByName=Ταξινόμηση με βάση το Όνομα +merge.sortByDate=Ταξινόμηση με βάση την Ημερομηνία +merge.submit=Συγχώνευση #pdfOrganiser -pdfOrganiser.title=\u0394\u03B9\u03BF\u03C1\u03B3\u03B1\u03BD\u03C9\u03C4\u03AE\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 -pdfOrganiser.header=\u0394\u03B9\u03BF\u03C1\u03B3\u03B1\u03BD\u03C9\u03C4\u03AE\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 PDF -pdfOrganiser.submit=\u0391\u03BD\u03B1\u03B4\u03B9\u03AC\u03C4\u03B1\u03BE\u03B7 \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD +pdfOrganiser.title=Διοργανωτής σελίδας +pdfOrganiser.header=Διοργανωτής σελίδας PDF +pdfOrganiser.submit=Αναδιάταξη σελίδων +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Προσαρμοσμένη Σειρά Σελίδας +pdfOrganiser.mode.2=Αντίστροφη Σειρά +pdfOrganiser.mode.3=Ταξινόμηση Διπλής Όψης (Duplex Sort) +pdfOrganiser.mode.4=Ταξινόμηση Φυλλαδίου (Booklet Sort) +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Διαίρεση Μονού-Ζυγού +pdfOrganiser.mode.7=Αφαίρεση Πρώτου +pdfOrganiser.mode.8=Αφαίρεση Τελευταίου +pdfOrganiser.mode.9=Αφαίρεση Πρώτου και Τελευταίου +pdfOrganiser.placeholder=(π.χ. 1,3,2 ή 4-8,2,10-12 ή 2n-1) #multiTool -multiTool.title=PDF \u03A0\u03BF\u03BB\u03C5\u03B5\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03BF -multiTool.header=PDF \u03A0\u03BF\u03BB\u03C5\u03B5\u03C1\u03B3\u03B1\u03BB\u03B5\u03AF\u03BF +multiTool.title=PDF Πολυεργαλείο +multiTool.header=PDF Πολυεργαλείο +multiTool.uploadPrompts=Ανεβάστε το PDF #view pdf -viewPdf.title=View PDF -viewPdf.header=View PDF +viewPdf.title=Προβολή PDF +viewPdf.header=Προβολή PDF #pageRemover -pageRemover.title=\u0391\u03C6\u03B1\u03B9\u03C1\u03B5\u03C4\u03AE\u03C2 \u03A3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD -pageRemover.header=\u0391\u03C6\u03B1\u03B9\u03C1\u03B5\u03C4\u03AE\u03C2 \u03A3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD PDF -pageRemover.pagesToDelete=\u03A3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 \u03C0\u03C1\u03BF\u03C2 \u03B4\u03B9\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE (\u0395\u03B9\u03C3\u03B1\u03B3\u03AC\u03B3\u03B5\u03C4\u03B5 \u03BC\u03B9\u03B1 \u03BB\u03AF\u03C3\u03C4\u03B1 \u03BC\u03B5 \u03B1\u03C1\u03B9\u03B8\u03BC\u03BF\u03CD\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03AD\u03BD\u03C9\u03BD \u03BC\u03B5 \u03BA\u03CC\u03BC\u03BC\u03B1\u03C4\u03B1): -pageRemover.submit=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u03A3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD +pageRemover.title=Αφαιρετής Σελίδων +pageRemover.header=Αφαιρετής Σελίδων PDF +pageRemover.pagesToDelete=Σελίδες προς διαγραφή (Εισαγάγετε μια λίστα με αριθμούς σελίδων διαχωρισμένων με κόμματα): +pageRemover.submit=Αφαίρεση Σελίδων +pageRemover.placeholder=(π.χ. 1,2,6 ή 1-10,15-30) #rotate -rotate.title=\u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE PDF -rotate.header=\u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE PDF -rotate.selectAngle=Select rotation angle (in multiples of 90 degrees): -rotate.submit=Rotate +rotate.title=Περιστροφή PDF +rotate.header=Περιστροφή PDF +rotate.selectAngle=Επιλέξτε γωνία περιστροφής (σε πολλαπλάσια των 90 μοιρών): +rotate.submit=Περιστροφή + + +#split-pdfs +split.title=Διαίρεση PDF +split.header=Διαίρεση PDF +split.desc.1=Οι αριθμοί που επιλέγετε είναι ο αριθμός σελίδας στον οποίο θέλετε να κάνετε διαχωρισμό +split.desc.2=Ως εκ τούτου, η επιλογή 1,3,7-9 θα χωρίσει ένα έγγραφο 10 σελίδων σε 6 ξεχωριστά αρχεία PDF με: +split.desc.3=Έγγραφο #1: Σελίδα 1 +split.desc.4=Έγγραφο #2: Σελίδα 2 και 3 +split.desc.5=Έγγραφο #3: Σελίδα 4, 5, 6 και 7 +split.desc.6=Έγγραφο #4: Σελίδα 8 +split.desc.7=Έγγραφο #5: Σελίδα 9 +split.desc.8=Έγγραφο #6: Σελίδα 10 +split.splitPages=Εισαγάγετε σελίδες για διαχωρισμό: +split.submit=Διαίρεση #merge -split.title=\u0394\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 PDF -split.header=\u0394\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 PDF -split.desc.1=\u039F\u03B9 \u03B1\u03C1\u03B9\u03B8\u03BC\u03BF\u03AF \u03C0\u03BF\u03C5 \u03B5\u03C0\u03B9\u03BB\u03AD\u03B3\u03B5\u03C4\u03B5 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03BF \u03B1\u03C1\u03B9\u03B8\u03BC\u03CC\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 \u03C3\u03C4\u03BF\u03BD \u03BF\u03C0\u03BF\u03AF\u03BF \u03B8\u03AD\u03BB\u03B5\u03C4\u03B5 \u03BD\u03B1 \u03BA\u03AC\u03BD\u03B5\u03C4\u03B5 \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03CC -split.desc.2=\u03A9\u03C2 \u03B5\u03BA \u03C4\u03BF\u03CD\u03C4\u03BF\u03C5, \u03B7 \u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE 1,3,7-8 \u03B8\u03B1 \u03C7\u03C9\u03C1\u03AF\u03C3\u03B5\u03B9 \u03AD\u03BD\u03B1 \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF 10 \u03C3\u03B5\u03BB\u03AF\u03B4\u03C9\u03BD \u03C3\u03B5 6 \u03BE\u03B5\u03C7\u03C9\u03C1\u03B9\u03C3\u03C4\u03AC \u03B1\u03C1\u03C7\u03B5\u03AF\u03B1 PDF \u03BC\u03B5: -split.desc.3=\u0388\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF #1: \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 1 -split.desc.4=\u0388\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF #2: \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 2 \u03BA\u03B1\u03B9 3 -split.desc.5=\u0388\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF #3: \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 4, 5 \u03BA\u03C3\u03B9 6 -split.desc.6=\u0388\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF #4: \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 7 -split.desc.7=\u0388\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF #5: \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 8 -split.desc.8=\u0388\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF #6: \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1 9 \u03BA\u03B1\u03B9 10 -split.splitPages=\u0395\u03B9\u03C3\u03B1\u03B3\u03AC\u03B3\u03B5\u03C4\u03B5 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 \u03B3\u03B9\u03B1 \u03B4\u03B9\u03B1\u03C7\u03C9\u03C1\u03B9\u03C3\u03BC\u03CC: -split.submit=\u0394\u03B9\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 - - -#merge -imageToPDF.title=\u0395\u03B9\u03BA\u03CC\u03BD\u03B1 \u03C3\u03B5 PDF -imageToPDF.header=\u0395\u03B9\u03BA\u03CC\u03BD\u03B1 \u03C3\u03B5 PDF -imageToPDF.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE -imageToPDF.selectLabel=\u0395\u03C0\u03B9\u03BB\u03BF\u03B3\u03AD\u03C2 \u03C0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 -imageToPDF.fillPage=\u0393\u03AD\u03BC\u03B9\u03C3\u03BC\u03B1 \u03A3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 -imageToPDF.fitDocumentToImage=\u03A0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03B3\u03AE \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 \u03C3\u03B5 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1 -imageToPDF.maintainAspectRatio=\u0394\u03B9\u03B1\u03C4\u03AE\u03C1\u03B7\u03C3\u03B7 \u03B1\u03BD\u03B1\u03BB\u03BF\u03B3\u03B9\u03CE\u03BD \u03B4\u03B9\u03B1\u03C3\u03C4\u03AC\u03C3\u03B5\u03C9\u03BD -imageToPDF.selectText.2=\u0391\u03C5\u03C4\u03CC\u03BC\u03B1\u03C4\u03B7 \u03C0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE PDF -imageToPDF.selectText.3=\u039B\u03BF\u03B3\u03B9\u03BA\u03AE \u03C0\u03BF\u03BB\u03BB\u03CE\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD (\u0395\u03BD\u03B5\u03C1\u03B3\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF\u03C4\u03B1\u03B9 \u03BC\u03CC\u03BD\u03BF \u03B5\u03AC\u03BD \u03B5\u03C1\u03B3\u03AC\u03B6\u03B5\u03C3\u03C4\u03B5 \u03BC\u03B5 \u03C0\u03BF\u03BB\u03BB\u03AD\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B5\u03C2) -imageToPDF.selectText.4=\u03A3\u03C5\u03B3\u03C7\u03CE\u03BD\u03B5\u03C5\u03C3\u03B7 \u03C3\u03B5 \u03AD\u03BD\u03B1 PDF -imageToPDF.selectText.5=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C3\u03B5 \u03BE\u03B5\u03C7\u03C9\u03C1\u03B9\u03C3\u03C4\u03AC \u03B1\u03C1\u03C7\u03B5\u03AF\u03B1 PDF +imageToPDF.title=Εικόνα σε PDF +imageToPDF.header=Εικόνα σε PDF +imageToPDF.submit=Μετατροπή +imageToPDF.selectLabel=Επιλογές προσαρμογής εικόνας +imageToPDF.fillPage=Γέμισμα Σελίδας +imageToPDF.fitDocumentToImage=Προσαρμογή σελίδας σε εικόνα +imageToPDF.maintainAspectRatio=Διατήρηση αναλογιών διαστάσεων +imageToPDF.selectText.2=Αυτόματη περιστροφή PDF +imageToPDF.selectText.3=Λογική πολλών αρχείων (Ενεργοποιείται μόνο εάν εργάζεστε με πολλές εικόνες) +imageToPDF.selectText.4=Συγχώνευση σε ένα PDF +imageToPDF.selectText.5=Μετατροπή σε ξεχωριστά αρχεία PDF #pdfToImage -pdfToImage.title=PDF \u03C3\u03B5 \u0395\u03B9\u03BA\u03CC\u03BD\u03B1 -pdfToImage.header=PDF \u03C3\u03B5 \u0395\u03B9\u03BA\u03CC\u03BD\u03B1 -pdfToImage.selectText=\u039C\u03BF\u03C1\u03C6\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1\u03C2 -pdfToImage.singleOrMultiple=\u03A4\u03CD\u03C0\u03BF\u03C2 \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03BF\u03C2 \u03B1\u03C0\u03CC \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1 \u03C3\u03B5 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1 -pdfToImage.single=\u0395\u03BD\u03B9\u03B1\u03AF\u03B1 \u03BC\u03B5\u03B3\u03AC\u03BB\u03B7 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1 \u03C0\u03BF\u03C5 \u03C3\u03C5\u03BD\u03B4\u03C5\u03AC\u03B6\u03B5\u03B9 \u03CC\u03BB\u03B5\u03C2 \u03C4\u03B9\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B5\u03C2 -pdfToImage.multi=\u03A0\u03BF\u03BB\u03BB\u03B1\u03C0\u03BB\u03AD\u03C2 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B5\u03C2, \u03BC\u03AF\u03B1 \u03B5\u03B9\u03BA\u03CC\u03BD\u03B1 \u03B1\u03BD\u03AC \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1 -pdfToImage.colorType=\u03A4\u03CD\u03C0\u03BF\u03C2 \u03A7\u03C1\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 -pdfToImage.color=\u03A7\u03C1\u03CE\u03BC\u03B1 -pdfToImage.grey=\u039A\u03BB\u03AF\u03BC\u03B1\u03BA\u03B1 \u03C4\u03BF\u03C5 \u03B3\u03BA\u03C1\u03B9 -pdfToImage.blackwhite=\u0391\u03C3\u03C0\u03C1\u03CC\u03BC\u03B1\u03C5\u03C1\u03BF (\u039C\u03C0\u03BF\u03C1\u03B5\u03AF \u03BD\u03B1 \u03C7\u03B1\u03B8\u03BF\u03CD\u03BD \u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03B1!) -pdfToImage.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE +pdfToImage.title=PDF σε Εικόνα +pdfToImage.header=PDF σε Εικόνα +pdfToImage.selectText=Μορφοποίηση εικόνας +pdfToImage.singleOrMultiple=Τύπος αποτελέσματος από σελίδα σε εικόνα +pdfToImage.single=Ενιαία μεγάλη εικόνα που συνδυάζει όλες τις σελίδες +pdfToImage.multi=Πολλαπλές εικόνες, μία εικόνα ανά σελίδα +pdfToImage.colorType=Τύπος Χρήματος +pdfToImage.color=Χρώμα +pdfToImage.grey=Κλίμακα του γκρι +pdfToImage.blackwhite=Ασπρόμαυρο (Μπορεί να χαθούν δεδομένα!) +pdfToImage.submit=Μετατροπή #addPassword -addPassword.title=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u039A\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD -addPassword.header=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u039A\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD (\u039A\u03C1\u03C5\u03C0\u03C4\u03BF\u03B3\u03C1\u03AC\u03C6\u03B7\u03C3\u03B7) -addPassword.selectText.1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 PDF \u03B3\u03B9\u03B1 \u03BA\u03C1\u03C5\u03C0\u03C4\u03BF\u03B3\u03C1\u03AC\u03C6\u03B7\u03C3\u03B7 -addPassword.selectText.2=\u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 \u03A7\u03C1\u03AE\u03C3\u03C4\u03B7 -addPassword.selectText.3=\u039C\u03AE\u03BA\u03BF\u03C2 \u039A\u03BB\u03B5\u03B9\u03B4\u03B9\u03BF\u03CD \u039A\u03C1\u03C5\u03C0\u03C4\u03BF\u03B3\u03C1\u03AC\u03C6\u03B7\u03C3\u03B7\u03C2 -addPassword.selectText.4=\u039F\u03B9 \u03C5\u03C8\u03B7\u03BB\u03CC\u03C4\u03B5\u03C1\u03B5\u03C2 \u03C4\u03B9\u03BC\u03AD\u03C2 \u03B5\u03AF\u03BD\u03B1\u03B9 \u03B9\u03C3\u03C7\u03C5\u03C1\u03CC\u03C4\u03B5\u03C1\u03B5\u03C2, \u03B1\u03BB\u03BB\u03AC \u03BF\u03B9 \u03C7\u03B1\u03BC\u03B7\u03BB\u03CC\u03C4\u03B5\u03C1\u03B5\u03C2 \u03C4\u03B9\u03BC\u03AD\u03C2 \u03AD\u03C7\u03BF\u03C5\u03BD \u03BA\u03B1\u03BB\u03CD\u03C4\u03B5\u03C1\u03B7 \u03C3\u03C5\u03BC\u03B2\u03B1\u03C4\u03CC\u03C4\u03B7\u03C4\u03B1. -addPassword.selectText.5=\u0394\u03B9\u03BA\u03B1\u03B9\u03CE\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 \u03C1\u03CD\u03B8\u03BC\u03B9\u03C3\u03B7 (\u03A3\u03C5\u03BD\u03B9\u03C3\u03C4\u03AC\u03C4\u03B1\u03B9 \u03BD\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF\u03C4\u03B1\u03B9 \u03BC\u03B1\u03B6\u03AF \u03BC\u03B5 \u03C4\u03BF\u03BD \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03BA\u03B1\u03C4\u03CC\u03C7\u03BF\u03C5 (Owner password) -addPassword.selectText.6=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03C3\u03C5\u03BD\u03B1\u03C1\u03BC\u03BF\u03BB\u03CC\u03B3\u03B7\u03C3\u03B7\u03C2 \u03B5\u03B3\u03B3\u03C1\u03AC\u03C6\u03BF\u03C5 -addPassword.selectText.7=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE\u03C2 \u03C0\u03B5\u03C1\u03B9\u03B5\u03C7\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 -addPassword.selectText.8=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE\u03C2 \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B2\u03B1\u03C3\u03B9\u03BC\u03CC\u03C4\u03B7\u03C4\u03B1 -addPassword.selectText.9=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03C3\u03C5\u03BC\u03C0\u03BB\u03AE\u03C1\u03C9\u03C3\u03B7\u03C2 \u03C6\u03CC\u03C1\u03BC\u03B1\u03C2 -addPassword.selectText.10=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7\u03C2 -addPassword.selectText.11=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7\u03C2 annotation -addPassword.selectText.12=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BA\u03C4\u03CD\u03C0\u03C9\u03C3\u03B7\u03C2 -addPassword.selectText.13=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BA\u03C4\u03CD\u03C0\u03C9\u03C3\u03B7\u03C2 \u03C3\u03B5 \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03B5\u03C4\u03B9\u03BA\u03BF\u03CD\u03C2 \u03C4\u03CD\u03C0\u03BF\u03C5\u03C2 \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD -addPassword.selectText.14=\u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03BA\u03B1\u03C4\u03CC\u03C7\u03BF\u03C5 -addPassword.selectText.15=\u03A0\u03B5\u03C1\u03B9\u03BF\u03C1\u03AF\u03B6\u03B5\u03B9 \u03CC,\u03C4\u03B9 \u03BC\u03C0\u03BF\u03C1\u03B5\u03AF \u03BD\u03B1 \u03B3\u03AF\u03BD\u03B5\u03B9 \u03BC\u03B5 \u03C4\u03BF \u03AD\u03B3\u03B3\u03C1\u03B1\u03C6\u03BF \u03BC\u03B5\u03C4\u03AC \u03C4\u03BF \u03AC\u03BD\u03BF\u03B9\u03B3\u03BC\u03B1 (\u0394\u03B5\u03BD \u03C5\u03C0\u03BF\u03C3\u03C4\u03B7\u03C1\u03AF\u03B6\u03B5\u03C4\u03B1\u03B9 \u03B1\u03C0\u03CC \u03CC\u03BB\u03BF\u03C5\u03C2 \u03C4\u03BF\u03C5\u03C2 \u03B1\u03BD\u03B1\u03B3\u03BD\u03CE\u03C3\u03C4\u03B5\u03C2 PDF) -addPassword.selectText.16=\u03A0\u03B5\u03C1\u03B9\u03BF\u03C1\u03AF\u03B6\u03B5\u03B9 \u03C4\u03BF \u03AC\u03BD\u03BF\u03B9\u03B3\u03BC\u03B1 \u03C4\u03BF\u03C5 \u03AF\u03B4\u03B9\u03BF\u03C5 \u03C4\u03BF\u03C5 \u03B5\u03B3\u03B3\u03C1\u03AC\u03C6\u03BF\u03C5 -addPassword.submit=\u039A\u03C1\u03C5\u03C0\u03C4\u03BF\u03B3\u03C1\u03AC\u03C6\u03B7\u03C3\u03B7 +addPassword.title=Προσθήκη Κωδικού +addPassword.header=Προσθήκη Κωδικού (Κρυπτογράφηση) +addPassword.selectText.1=Επιλέξτε PDF για κρυπτογράφηση +addPassword.selectText.2=Κωδικός Χρήστη +addPassword.selectText.3=Μήκος Κλειδιού Κρυπτογράφησης +addPassword.selectText.4=Οι υψηλότερες τιμές είναι ισχυρότερες, αλλά οι χαμηλότερες τιμές έχουν καλύτερη συμβατότητα. +addPassword.selectText.5=Δικαιώματα για ρύθμιση (Συνιστάται να χρησιμοποιείται μαζί με τον κωδικό πρόσβασης κατόχου (Owner password) +addPassword.selectText.6=Αποτροπή συναρμολόγησης εγγράφου +addPassword.selectText.7=Αποτροπή εξαγωγής περιεχομένου +addPassword.selectText.8=Αποτροπή εξαγωγής για προσβασιμότητα +addPassword.selectText.9=Αποτροπή συμπλήρωσης φόρμας +addPassword.selectText.10=Αποτροπή τροποποίησης +addPassword.selectText.11=Αποτροπή τροποποίησης annotation +addPassword.selectText.12=Αποτροπή εκτύπωσης +addPassword.selectText.13=Αποτροπή εκτύπωσης σε διαφορετικούς τύπους αρχείων +addPassword.selectText.14=Κωδικός πρόσβασης κατόχου +addPassword.selectText.15=Περιορίζει ό,τι μπορεί να γίνει με το έγγραφο μετά το άνοιγμα (Δεν υποστηρίζεται από όλους τους αναγνώστες PDF) +addPassword.selectText.16=Περιορίζει το άνοιγμα του ίδιου του εγγράφου +addPassword.submit=Κρυπτογράφηση #watermark -watermark.title=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03A5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 -watermark.header=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03A5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 -watermark.selectText.1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 PDF \u03B3\u03B9\u03B1 \u03C4\u03B7\u03BD \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C4\u03BF\u03C5 \u03C5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2: -watermark.selectText.2=\u039A\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF \u03A5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2: -watermark.selectText.3=\u039C\u03AD\u03B3\u03B5\u03B8\u03BF\u03C2 \u039A\u03B5\u03B9\u03BC\u03AD\u03BD\u03BF\u03C5: -watermark.selectText.4=\u03A0\u03B5\u03C1\u03B9\u03C3\u03C4\u03C1\u03BF\u03C6\u03AE (0-360): -watermark.selectText.5=widthSpacer (\u039A\u03B5\u03BD\u03CC \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD \u03BA\u03AC\u03B8\u03B5 \u03C5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03BF\u03C1\u03B9\u03B6\u03CC\u03BD\u03C4\u03B9\u03B1): -watermark.selectText.6=heightSpacer (\u039A\u03B5\u03BD\u03CC \u03BC\u03B5\u03C4\u03B1\u03BE\u03CD \u03BA\u03AC\u03B8\u03B5 \u03C5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 \u03BA\u03AC\u03B8\u03B5\u03C4\u03B1): -watermark.selectText.7=\u0391\u03B4\u03B9\u03B1\u03C6\u03AC\u03BD\u03B5\u03B9\u03B1 (Opacity) (0% - 100%): -watermark.selectText.8=\u03A4\u03CD\u03C0\u03BF\u03C2 \u03A5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2: -watermark.selectText.9=\u0395\u03B9\u03BA\u03CC\u03BD\u03B1 \u03A5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2: -watermark.submit=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03A5\u03B4\u03B1\u03C4\u03BF\u03B3\u03C1\u03B1\u03C6\u03AE\u03BC\u03B1\u03C4\u03BF\u03C2 +watermark.title=Προσθήκη Υδατογραφήματος +watermark.header=Προσθήκη Υδατογραφήματος +watermark.selectText.1=Επιλέξτε PDF για την προσθήκη του υδατογραφήματος: +watermark.selectText.2=Κείμενο Υδατογραφήματος: +watermark.selectText.3=Μέγεθος Κειμένου: +watermark.selectText.4=Περιστροφή (0-360): +watermark.selectText.5=widthSpacer (Κενό μεταξύ κάθε υδατογραφήματος οριζόντια): +watermark.selectText.6=heightSpacer (Κενό μεταξύ κάθε υδατογραφήματος κάθετα): +watermark.selectText.7=Αδιαφάνεια (Opacity) (0% - 100%): +watermark.selectText.8=Τύπος Υδατογραφήματος: +watermark.selectText.9=Εικόνα Υδατογραφήματος: +watermark.submit=Προσθήκη Υδατογραφήματος +watermark.type.1=Κείμενο +watermark.type.2=Εικόνα #Change permissions -permissions.title=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u0394\u03B9\u03BA\u03B1\u03B9\u03C9\u03BC\u03AC\u03C4\u03C9\u03BD -permissions.header=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u0394\u03B9\u03BA\u03B1\u03B9\u03C9\u03BC\u03AC\u03C4\u03C9\u03BD -permissions.warning=\u03A0\u03C1\u03BF\u03B5\u03B9\u03B4\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7 \u03CC\u03C4\u03B9 \u03B1\u03C5\u03C4\u03AC \u03C4\u03B1 \u03B4\u03B9\u03BA\u03B1\u03B9\u03CE\u03BC\u03B1\u03C4\u03B1 \u03B4\u03B5\u03BD \u03B1\u03BB\u03BB\u03AC\u03B6\u03BF\u03C5\u03BD, \u03C3\u03C5\u03BD\u03B9\u03C3\u03C4\u03AC\u03C4\u03B1\u03B9 \u03BD\u03B1 \u03C4\u03B1 \u03BF\u03C1\u03AF\u03C3\u03B5\u03C4\u03B5 \u03BC\u03B5 \u03BA\u03C9\u03B4\u03B9\u03BA\u03CC \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 \u03BC\u03AD\u03C3\u03C9 \u03C4\u03B7\u03C2 \u03C3\u03B5\u03BB\u03AF\u03B4\u03B1\u03C2 \u03C0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7\u03C2 \u03BA\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD \u03C0\u03C1\u03CC\u03C3\u03B2\u03B1\u03C3\u03B7\u03C2 -permissions.selectText.1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 PDF \u03B3\u03B9\u03B1 \u03BD\u03B1 \u03B1\u03BB\u03BB\u03AC\u03BE\u03B5\u03C4\u03B5 \u03C4\u03B1 \u03B4\u03B9\u03BA\u03B1\u03B9\u03CE\u03BC\u03B1\u03C4\u03B1 -permissions.selectText.2=\u0394\u03B9\u03BA\u03B1\u03B9\u03CE\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 \u03C1\u03CD\u03B8\u03BC\u03B9\u03C3\u03B7 -permissions.selectText.3=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03C3\u03C5\u03BD\u03B1\u03C1\u03BC\u03BF\u03BB\u03CC\u03B3\u03B7\u03C3\u03B7\u03C2 \u03B5\u03B3\u03B3\u03C1\u03AC\u03C6\u03BF\u03C5 -permissions.selectText.4=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE\u03C2 \u03C0\u03B5\u03C1\u03B9\u03B5\u03C7\u03BF\u03BC\u03AD\u03BD\u03BF\u03C5 -permissions.selectText.5=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BE\u03B1\u03B3\u03C9\u03B3\u03AE\u03C2 \u03B3\u03B9\u03B1 \u03C0\u03C1\u03BF\u03C3\u03B2\u03B1\u03C3\u03B9\u03BC\u03CC\u03C4\u03B7\u03C4\u03B1 -permissions.selectText.6=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03C3\u03C5\u03BC\u03C0\u03BB\u03AE\u03C1\u03C9\u03C3\u03B7\u03C2 \u03C6\u03CC\u03C1\u03BC\u03B1\u03C2 -permissions.selectText.7=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7\u03C2 -permissions.selectText.8=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7\u03C2 annotation -permissions.selectText.9=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BA\u03C4\u03CD\u03C0\u03C9\u03C3\u03B7\u03C2 -permissions.selectText.10=\u0391\u03C0\u03BF\u03C4\u03C1\u03BF\u03C0\u03AE \u03B5\u03BA\u03C4\u03CD\u03C0\u03C9\u03C3\u03B7\u03C2 \u03C3\u03B5 \u03B4\u03B9\u03B1\u03C6\u03BF\u03C1\u03B5\u03C4\u03B9\u03BA\u03BF\u03CD\u03C2 \u03C4\u03CD\u03C0\u03BF\u03C5\u03C2 \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD -permissions.submit=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE +permissions.title=Αλλαγή Δικαιωμάτων +permissions.header=Αλλαγή Δικαιωμάτων +permissions.warning=Προειδοποίηση ότι αυτά τα δικαιώματα δεν αλλάζουν, συνιστάται να τα ορίσετε με κωδικό πρόσβασης μέσω της σελίδας προσθήκης κωδικού πρόσβασης +permissions.selectText.1=Επιλέξτε PDF για να αλλάξετε τα δικαιώματα +permissions.selectText.2=Δικαιώματα για ρύθμιση +permissions.selectText.3=Αποτροπή συναρμολόγησης εγγράφου +permissions.selectText.4=Αποτροπή εξαγωγής περιεχομένου +permissions.selectText.5=Αποτροπή εξαγωγής για προσβασιμότητα +permissions.selectText.6=Αποτροπή συμπλήρωσης φόρμας +permissions.selectText.7=Αποτροπή τροποποίησης +permissions.selectText.8=Αποτροπή τροποποίησης annotation +permissions.selectText.9=Αποτροπή εκτύπωσης +permissions.selectText.10=Αποτροπή εκτύπωσης σε διαφορετικούς τύπους αρχείων +permissions.submit=Αλλαγή #remove password -removePassword.title=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u039A\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD -removePassword.header=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 \u039A\u03C9\u03B4\u03B9\u03BA\u03BF\u03CD (\u0391\u03C0\u03BF\u03BA\u03C1\u03C5\u03C0\u03C4\u03BF\u03B3\u03C1\u03AC\u03C6\u03B7\u03C3\u03B7) -removePassword.selectText.1=\u0395\u03C0\u03B9\u03BB\u03AD\u03BE\u03C4\u03B5 PDF \u03B3\u03B9\u03B1 \u03B1\u03C0\u03BF\u03BA\u03C1\u03C5\u03C0\u03C4\u03BF\u03B3\u03C1\u03AC\u03C6\u03B7\u03C3\u03B7 -removePassword.selectText.2=\u039A\u03C9\u03B4\u03B9\u03BA\u03CC\u03C2 -removePassword.submit=\u0391\u03C6\u03B1\u03AF\u03C1\u03B5\u03C3\u03B7 +removePassword.title=Αφαίρεση Κωδικού +removePassword.header=Αφαίρεση Κωδικού (Αποκρυπτογράφηση) +removePassword.selectText.1=Επιλέξτε PDF για αποκρυπτογράφηση +removePassword.selectText.2=Κωδικός +removePassword.submit=Αφαίρεση #changeMetadata -changeMetadata.title=\u03A4\u03AF\u03C4\u03BB\u03BF\u03C2: -changeMetadata.header=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE \u039C\u03B5\u03C4\u03B1\u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03C9\u03BD -changeMetadata.selectText.1=\u03A0\u03B1\u03C1\u03B1\u03BA\u03B1\u03BB\u03BF\u03CD\u03BC\u03B5 \u03B5\u03C0\u03B5\u03BE\u03B5\u03C1\u03B3\u03B1\u03C3\u03C4\u03B5\u03AF\u03C4\u03B5 \u03C4\u03B9\u03C2 \u03BC\u03B5\u03C4\u03B1\u03B2\u03BB\u03B7\u03C4\u03AD\u03C2 \u03C0\u03BF\u03C5 \u03B8\u03AD\u03BB\u03B5\u03C4\u03B5 \u03BD\u03B1 \u03B1\u03BB\u03BB\u03AC\u03BE\u03B5\u03C4\u03B5 -changeMetadata.selectText.2=\u0394\u03B9\u03B1\u03B3\u03C1\u03B1\u03C6\u03AE \u03CC\u03BB\u03C9\u03BD \u03C4\u03C9\u03BD \u03BC\u03B5\u03C4\u03B1\u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03C9\u03BD -changeMetadata.selectText.3=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03C0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03C3\u03BC\u03AD\u03BD\u03C9\u03BD \u03BC\u03B5\u03C4\u03B1\u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03C9\u03BD: -changeMetadata.author=\u03A3\u03C5\u03BD\u03C4\u03AC\u03BA\u03C4\u03B7\u03C2: -changeMetadata.creationDate=\u0397\u03BC\u03B5\u03C1\u03BF\u03BC\u03B7\u03BD\u03AF\u03B1 \u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03AF\u03B1\u03C2 (yyyy/MM/dd HH:mm:ss): -changeMetadata.creator=\u0394\u03B7\u03BC\u03B9\u03BF\u03C5\u03C1\u03B3\u03CC\u03C2: -changeMetadata.keywords=\u039B\u03AD\u03BE\u03B5\u03B9\u03C2-\u03BA\u03BB\u03B5\u03B9\u03B4\u03B9\u03AC: -changeMetadata.modDate=\u0397\u03BC\u03B5\u03C1\u03BF\u03BC\u03B7\u03BD\u03AF\u03B1 \u03A4\u03C1\u03BF\u03C0\u03BF\u03C0\u03BF\u03AF\u03B7\u03C3\u03B7\u03C2 (yyyy/MM/dd HH:mm:ss): -changeMetadata.producer=\u03A0\u03B1\u03C1\u03B1\u03B3\u03C9\u03B3\u03CC\u03C2: -changeMetadata.subject=\u0398\u03AD\u03BC\u03B1: +changeMetadata.title=Τίτλος: +changeMetadata.header=Αλλαγή Μεταδεδομένων +changeMetadata.selectText.1=Παρακαλούμε επεξεργαστείτε τις μεταβλητές που θέλετε να αλλάξετε +changeMetadata.selectText.2=Διαγραφή όλων των μεταδεδομένων +changeMetadata.selectText.3=Προσθήκη προσαρμοσμένων μεταδεδομένων: +changeMetadata.author=Συντάκτης: +changeMetadata.creationDate=Ημερομηνία Δημιουργίας (yyyy/MM/dd HH:mm:ss): +changeMetadata.creator=Δημιουργός: +changeMetadata.keywords=Λέξεις-κλειδιά: +changeMetadata.modDate=Ημερομηνία Τροποποίησης (yyyy/MM/dd HH:mm:ss): +changeMetadata.producer=Παραγωγός: +changeMetadata.subject=Θέμα: changeMetadata.trapped=Trapped: -changeMetadata.selectText.4=\u0386\u03BB\u03BB\u03B1 \u03BC\u03B5\u03C4\u03B1\u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03B1: -changeMetadata.selectText.5=\u03A0\u03C1\u03BF\u03C3\u03B8\u03AE\u03BA\u03B7 \u03B5\u03B3\u03B3\u03C1\u03B1\u03C6\u03AE\u03C2 \u03C0\u03C1\u03BF\u03C3\u03B1\u03C1\u03BC\u03BF\u03C3\u03BC\u03AD\u03BD\u03C9\u03BD \u03BC\u03B5\u03C4\u03B1\u03B4\u03B5\u03B4\u03BF\u03BC\u03AD\u03BD\u03C9\u03BD -changeMetadata.submit=\u0391\u03BB\u03BB\u03B1\u03B3\u03AE +changeMetadata.selectText.4=Άλλα μεταδεδομένα: +changeMetadata.selectText.5=Προσθήκη εγγραφής προσαρμοσμένων μεταδεδομένων +changeMetadata.submit=Αλλαγή #pdfToPDFA -pdfToPDFA.title=PDF \u03C3\u03B5 PDF/A -pdfToPDFA.header=PDF \u03C3\u03B5 PDF/A -pdfToPDFA.credit=\u0391\u03C5\u03C4\u03AE \u03B7 \u03C5\u03C0\u03B7\u03C1\u03B5\u03C3\u03AF\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF OCRmyPDF \u03B3\u03B9\u03B1 PDF/A \u03BC\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE -pdfToPDFA.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE +pdfToPDFA.title=PDF σε PDF/A +pdfToPDFA.header=PDF σε PDF/A +pdfToPDFA.credit=Αυτή η υπηρεσία χρησιμοποιεί OCRmyPDF για PDF/A μετατροπή +pdfToPDFA.submit=Μετατροπή +pdfToPDFA.tip=Προς το παρόν δεν λειτουργεί για πολλαπλές εισόδους ταυτόχρονα +pdfToPDFA.outputFormat=Output format #PDFToWord -PDFToWord.title=PDF \u03C3\u03B5 Word -PDFToWord.header=PDF \u03C3\u03B5 Word -PDFToWord.selectText.1=\u03A4\u03CD\u03C0\u03BF\u03C2 \u0391\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 \u0395\u03BE\u03B1\u03B3\u03CC\u03BC\u03B5\u03BD\u03BF\u03C5 -PDFToWord.credit=\u0391\u03C5\u03C4\u03AE \u03B7 \u03C5\u03C0\u03B7\u03C1\u03B5\u03C3\u03AF\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF LibreOffice \u03B3\u03B9\u03B1 \u03C4\u03B7 \u03BC\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD. -PDFToWord.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE +PDFToWord.title=PDF σε Word +PDFToWord.header=PDF σε Word +PDFToWord.selectText.1=Τύπος Αρχείου Εξαγόμενου +PDFToWord.credit=Αυτή η υπηρεσία χρησιμοποιεί LibreOffice για τη μετατροπή των αρχείων. +PDFToWord.submit=Μετατροπή #PDFToPresentation -PDFToPresentation.title=PDF \u03C3\u03B5 Powerpoint -PDFToPresentation.header=PDF \u03C3\u03B5 Powerpoint -PDFToPresentation.selectText.1=\u03A4\u03CD\u03C0\u03BF\u03C2 \u0391\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 \u0395\u03BE\u03B1\u03B3\u03CC\u03BC\u03B5\u03BD\u03BF\u03C5 -PDFToPresentation.credit=\u0391\u03C5\u03C4\u03AE \u03B7 \u03C5\u03C0\u03B7\u03C1\u03B5\u03C3\u03AF\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF LibreOffice \u03B3\u03B9\u03B1 \u03C4\u03B7 \u03BC\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD. -PDFToPresentation.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE +PDFToPresentation.title=PDF σε Powerpoint +PDFToPresentation.header=PDF σε Powerpoint +PDFToPresentation.selectText.1=Τύπος Αρχείου Εξαγόμενου +PDFToPresentation.credit=Αυτή η υπηρεσία χρησιμοποιεί LibreOffice για τη μετατροπή των αρχείων. +PDFToPresentation.submit=Μετατροπή #PDFToText -PDFToText.title=PDF \u03C3\u03B5 RTF (\u039A\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF) -PDFToText.header=PDF \u03C3\u03B5 RTF (\u039A\u03B5\u03AF\u03BC\u03B5\u03BD\u03BF) -PDFToText.selectText.1=\u03A4\u03CD\u03C0\u03BF\u03C2 \u0391\u03C1\u03C7\u03B5\u03AF\u03BF\u03C5 \u0395\u03BE\u03B1\u03B3\u03CC\u03BC\u03B5\u03BD\u03BF\u03C5 -PDFToText.credit=\u0391\u03C5\u03C4\u03AE \u03B7 \u03C5\u03C0\u03B7\u03C1\u03B5\u03C3\u03AF\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF LibreOffice \u03B3\u03B9\u03B1 \u03C4\u03B7 \u03BC\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD. -PDFToText.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE +PDFToText.title=PDF σε RTF (Κείμενο) +PDFToText.header=PDF σε RTF (Κείμενο) +PDFToText.selectText.1=Τύπος Αρχείου Εξαγόμενου +PDFToText.credit=Αυτή η υπηρεσία χρησιμοποιεί LibreOffice για τη μετατροπή των αρχείων. +PDFToText.submit=Μετατροπή #PDFToHTML -PDFToHTML.title=PDF \u03C3\u03B5 HTML -PDFToHTML.header=PDF \u03C3\u03B5 HTML -PDFToHTML.credit=\u0391\u03C5\u03C4\u03AE \u03B7 \u03C5\u03C0\u03B7\u03C1\u03B5\u03C3\u03AF\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF LibreOffice \u03B3\u03B9\u03B1 \u03C4\u03B7 \u03BC\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD. -PDFToHTML.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE +PDFToHTML.title=PDF σε HTML +PDFToHTML.header=PDF σε HTML +PDFToHTML.credit=Αυτή η υπηρεσία χρησιμοποιεί pdftohtml για τη μετατροπή των αρχείων. +PDFToHTML.submit=Μετατροπή #PDFToXML -PDFToXML.title=PDF \u03C3\u03B5 XML -PDFToXML.header=PDF \u03C3\u03B5 XML -PDFToXML.credit=\u0391\u03C5\u03C4\u03AE \u03B7 \u03C5\u03C0\u03B7\u03C1\u03B5\u03C3\u03AF\u03B1 \u03C7\u03C1\u03B7\u03C3\u03B9\u03BC\u03BF\u03C0\u03BF\u03B9\u03B5\u03AF LibreOffice \u03B3\u03B9\u03B1 \u03C4\u03B7 \u03BC\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE \u03C4\u03C9\u03BD \u03B1\u03C1\u03C7\u03B5\u03AF\u03C9\u03BD. -PDFToXML.submit=\u039C\u03B5\u03C4\u03B1\u03C4\u03C1\u03BF\u03C0\u03AE +PDFToXML.title=PDF σε XML +PDFToXML.header=PDF σε XML +PDFToXML.credit=Αυτή η υπηρεσία χρησιμοποιεί LibreOffice για τη μετατροπή των αρχείων. +PDFToXML.submit=Μετατροπή #PDFToCSV -PDFToCSV.title=PDF ?? CSV -PDFToCSV.header=PDF ?? CSV -PDFToCSV.prompt=Choose page to extract table -PDFToCSV.submit=????????? +PDFToCSV.title=PDF σε CSV +PDFToCSV.header=PDF σε CSV +PDFToCSV.prompt=Επιλέξτε σελίδα για εξαγωγή πίνακα +PDFToCSV.submit=Εξαξωγή #split-by-size-or-count -split-by-size-or-count.header=Split PDF by Size or Count -split-by-size-or-count.type.label=Select Split Type -split-by-size-or-count.type.size=By Size -split-by-size-or-count.type.pageCount=By Page Count -split-by-size-or-count.type.docCount=By Document Count -split-by-size-or-count.value.label=Enter Value -split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5) -split-by-size-or-count.submit=Submit +split-by-size-or-count.title=Διαχωρίστε το PDF Κατά Μέγεθος ή Μέτρηση +split-by-size-or-count.header=Διαχωρίστε το PDF Κατά Μέγεθος ή Μέτρηση +split-by-size-or-count.type.label=Επιλέξτε Τύπος Διαχωρισμού +split-by-size-or-count.type.size=Ανά Μέγεθος +split-by-size-or-count.type.pageCount=Ανά αριθμό σελίδων +split-by-size-or-count.type.docCount=Ανά αριθμό εγγράφων +split-by-size-or-count.value.label=Εισαγάγετε τιμή +split-by-size-or-count.value.placeholder=Εισαγάγετε μέγεθος (π.χ. 2MB ή 3KB) ή μέτρηση (π.χ. 5) +split-by-size-or-count.submit=Υποβολή #overlay-pdfs -overlay-pdfs.header=Overlay PDF Files -overlay-pdfs.baseFile.label=Select Base PDF File -overlay-pdfs.overlayFiles.label=Select Overlay PDF Files -overlay-pdfs.mode.label=Select Overlay Mode -overlay-pdfs.mode.sequential=Sequential Overlay -overlay-pdfs.mode.interleaved=Interleaved Overlay -overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay -overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode) -overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1) -overlay-pdfs.position.label=Select Overlay Position +overlay-pdfs.header=Επικάλυψη αρχείων PDF +overlay-pdfs.baseFile.label=Επιλέξτε Βασικό αρχείο PDF +overlay-pdfs.overlayFiles.label=Επιλέξτε Επικάλυψη αρχείων PDF +overlay-pdfs.mode.label=Επιλέξτε Λειτουργία Επικάλυψης +overlay-pdfs.mode.sequential=Διαδοχική Επικάλυψη +overlay-pdfs.mode.interleaved=Επικάλυψη με Παρεμβολή +overlay-pdfs.mode.fixedRepeat=Διορθώθηκε η Επικάλυψη Επανάληψης +overlay-pdfs.counts.label=Μετρήσεις επικάλυψης (για σταθερή λειτουργία επανάληψης) +overlay-pdfs.counts.placeholder=Εισαγάγετε μετρήσεις διαχωρισμένες με κόμματα (π.χ. 2,3,1) +overlay-pdfs.position.label=Επιλέξτε Θέση Επικάλυψης overlay-pdfs.position.foreground=Foreground overlay-pdfs.position.background=Background -overlay-pdfs.submit=Submit +overlay-pdfs.submit=Υποβολή #split-by-sections -split-by-sections.title=Split PDF by Sections -split-by-sections.header=Split PDF into Sections -split-by-sections.horizontal.label=Horizontal Divisions -split-by-sections.vertical.label=Vertical Divisions -split-by-sections.horizontal.placeholder=Enter number of horizontal divisions -split-by-sections.vertical.placeholder=Enter number of vertical divisions -split-by-sections.submit=Split PDF +split-by-sections.title=Διαχωρισμός PDF ανά ενότητες +split-by-sections.header=Διαχωρίστε το PDF σε Ενότητες +split-by-sections.horizontal.label=Οριζόντιες Διαιρέσεις +split-by-sections.vertical.label=Κάθετες Διαιρέσεις +split-by-sections.horizontal.placeholder=Εισαγάγετε τον αριθμό των οριζόντιων διαιρέσεων +split-by-sections.vertical.placeholder=Εισαγάγετε τον αριθμό των κάθετων διαιρέσεων +split-by-sections.submit=Διαχωρισμός PDF +split-by-sections.merge=Συγχώνευση σε ένα PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses -licenses.nav=Licenses -licenses.title=3rd Party Licenses -licenses.header=3rd Party Licenses +licenses.nav=Άδειες +licenses.title=3rd Party Άδειες +licenses.header=3rd Party Άδειες licenses.module=Module -licenses.version=Version -licenses.license=License +licenses.version=Εκδοχή +licenses.license=Άδεια +# error +error.sorry=Συγγνώμη για το ζήτημα! +error.needHelp=Χρειάζεστε βοήθεια / Βρήκατε πρόβλημα; +error.contactTip=Εάν εξακολουθείτε να αντιμετωπίζετε προβλήματα, μη διστάσετε να επικοινωνήσετε μαζί μας για βοήθεια. Μπορείτε να υποβάλετε ένα ticket στη σελίδα μας στο GitHub ή να επικοινωνήσετε μαζί μας μέσω του Discord: +error.404.head=404 - Η σελίδα δεν βρέθηκε | Oops, we tripped in the code! +error.404.1=Δεν μπορούμε να βρούμε τη σελίδα που ψάχνετε. +error.404.2=Κάτι πήγε στραβά +error.github=Υποβάλετε ένα ticket στο GitHub +error.showStack=Εμφάνιση Stack Trace +error.copyStack=Αντιγραφή Stack Trace +error.githubSubmit=GitHub - Υποβάλετε ένα ticket +error.discordSubmit=Discord - Υποβάλετε ένα Support post + diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index 1dd5262d..6189f56b 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr = left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=True false=False unknown=Unknown save=Save +saveToBrowser=Save to Browser close=Close filesSelected=files selected noFavourites=No favourites added +downloadComplete=Download Complete bored=Bored Waiting? alphabet=Alphabet downloadPdf=Download PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Cannot downgrade current user's role +downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Settings ############# settings.title=Settings settings.update=Update available +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=App Version: settings.downloadOption.title=Choose download option (For single file non zip downloads): settings.downloadOption.1=Open in same window @@ -102,12 +123,13 @@ settings.downloadOption.3=Download file settings.zipThreshold=Zip files when the number of downloaded files exceeds settings.signOut=Sign Out settings.accountSettings=Account Settings - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Change Credentials changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=New Username changeCreds.oldPassword=Current Password changeCreds.newPassword=New Password @@ -142,14 +164,18 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions adminUserSettings.apiUser=Limited API User +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Web Only User adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange = Force user to change username/password on login +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User +adminUserSettings.changeUserRole=Change User's Role ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Sign in +login.header=Sign in login.signin=Sign in login.rememberme=Remember me login.invalid=Invalid username or password. login.locked=Your account has been locked. login.signinTitle=Please sign in +login.ssoSignIn=Login via Single Sign-on +login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Convert To Single Page pageExtracter.title=Extract Pages pageExtracter.header=Extract Pages pageExtracter.submit=Extract +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Document 1 compare.document.2=Document 2 compare.submit=Compare +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Sign @@ -642,7 +693,8 @@ repair.submit=Repair #flatten flatten.title=Flatten -flatten.header=Flatten PDFs +flatten.header=Flatten PDF +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Flatten @@ -726,11 +778,23 @@ merge.submit=Merge pdfOrganiser.title=Page Organiser pdfOrganiser.header=PDF Page Organiser pdfOrganiser.submit=Rearrange Pages +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF Multi Tool multiTool.header=PDF Multi Tool +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=Page Remover pageRemover.header=PDF Page remover pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) : pageRemover.submit=Delete Pages +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Select rotation angle (in multiples of 90 degrees): rotate.submit=Rotate -#merge +#split-pdfs split.title=Split PDF split.header=Split PDF split.desc.1=The numbers you select are the page number you wish to do a split on -split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with: +split.desc.2=As such selecting 1,3,7-9 would split a 10 page document into 6 separate PDFS with: split.desc.3=Document #1: Page 1 split.desc.4=Document #2: Page 2 and 3 -split.desc.5=Document #3: Page 4, 5 and 6 -split.desc.6=Document #4: Page 7 -split.desc.7=Document #5: Page 8 -split.desc.8=Document #6: Page 9 and 10 +split.desc.5=Document #3: Page 4, 5, 6 and 7 +split.desc.6=Document #4: Page 8 +split.desc.7=Document #5: Page 9 +split.desc.8=Document #6: Page 10 split.splitPages=Enter pages to split on: split.submit=Split @@ -828,6 +893,8 @@ watermark.selectText.7=Opacity (0% - 100%): watermark.selectText.8=Watermark Type: watermark.selectText.9=Watermark Image: watermark.submit=Add Watermark +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF To PDF/A pdfToPDFA.header=PDF To PDF/A pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion pdfToPDFA.submit=Convert +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Convert #PDFToHTML PDFToHTML.title=PDF to HTML PDFToHTML.header=PDF to HTML -PDFToHTML.credit=This service uses LibreOffice for file conversion. +PDFToHTML.credit=This service uses pdftohtml for file conversion. PDFToHTML.submit=Convert @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=Extract #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_en_US.properties b/src/main/resources/messages_en_US.properties index 120ace75..2ecaa2e8 100644 --- a/src/main/resources/messages_en_US.properties +++ b/src/main/resources/messages_en_US.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=True false=False unknown=Unknown save=Save +saveToBrowser=Save to Browser close=Close filesSelected=files selected noFavourites=No favorites added +downloadComplete=Download Complete bored=Bored Waiting? alphabet=Alphabet downloadPdf=Download PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Cannot downgrade current user's role +downgradeCurrentUserLongMessage=Cannot downgrade current user's role. Hence, current user will not be shown. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Settings ############# settings.title=Settings settings.update=Update available +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=App Version: settings.downloadOption.title=Choose download option (For single file non zip downloads): settings.downloadOption.1=Open in same window @@ -102,12 +123,13 @@ settings.downloadOption.3=Download file settings.zipThreshold=Zip files when the number of downloaded files exceeds settings.signOut=Sign Out settings.accountSettings=Account Settings - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Change Credentials changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=New Username changeCreds.oldPassword=Current Password changeCreds.newPassword=New Password @@ -142,14 +164,18 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions adminUserSettings.apiUser=Limited API User +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Web Only User adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Force user to change username/password on login +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User +adminUserSettings.changeUserRole=Change User's Role ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Sign in +login.header=Sign in login.signin=Sign in login.rememberme=Remember me login.invalid=Invalid username or password. login.locked=Your account has been locked. login.signinTitle=Please sign in +login.ssoSignIn=Login via Single Sign-on +login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User Disabled #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Convert To Single Page pageExtracter.title=Extract Pages pageExtracter.header=Extract Pages pageExtracter.submit=Extract +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Document 1 compare.document.2=Document 2 compare.submit=Compare +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Sign @@ -643,6 +694,7 @@ repair.submit=Repair #flatten flatten.title=Flatten flatten.header=Flatten PDFs +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Flatten @@ -726,11 +778,23 @@ merge.submit=Merge pdfOrganiser.title=Page Organizer pdfOrganiser.header=PDF Page Organizer pdfOrganiser.submit=Rearrange Pages +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF Multi Tool multiTool.header=PDF Multi Tool +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=Page Remover pageRemover.header=PDF Page remover pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) : pageRemover.submit=Delete Pages +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Select rotation angle (in multiples of 90 degrees): rotate.submit=Rotate -#merge +#split-pdfs split.title=Split PDF split.header=Split PDF split.desc.1=The numbers you select are the page number you wish to do a split on -split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with: +split.desc.2=As such selecting 1,3,7-9 would split a 10 page document into 6 separate PDFS with: split.desc.3=Document #1: Page 1 split.desc.4=Document #2: Page 2 and 3 -split.desc.5=Document #3: Page 4, 5 and 6 -split.desc.6=Document #4: Page 7 -split.desc.7=Document #5: Page 8 -split.desc.8=Document #6: Page 9 and 10 +split.desc.5=Document #3: Page 4, 5, 6, 7 +split.desc.6=Document #4: Page 8 +split.desc.7=Document #5: Page 9 +split.desc.8=Document #6: Page 10 split.splitPages=Enter pages to split on: split.submit=Split @@ -828,6 +893,8 @@ watermark.selectText.7=Opacity (0% - 100%): watermark.selectText.8=Watermark Type: watermark.selectText.9=Watermark Image: watermark.submit=Add Watermark +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF To PDF/A pdfToPDFA.header=PDF To PDF/A pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion pdfToPDFA.submit=Convert +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Convert #PDFToHTML PDFToHTML.title=PDF to HTML PDFToHTML.header=PDF to HTML -PDFToHTML.credit=This service uses LibreOffice for file conversion. +PDFToHTML.credit=This service uses pdftohtml for file conversion. PDFToHTML.submit=Convert @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=Extract #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_es_ES.properties b/src/main/resources/messages_es_ES.properties index 43061780..275bf1d1 100644 --- a/src/main/resources/messages_es_ES.properties +++ b/src/main/resources/messages_es_ES.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -11,15 +11,17 @@ imgPrompt=Seleccionar Imagen(es) genericSubmit=Enviar processTimeWarning=Advertencia: este proceso puede tardar hasta un minuto dependiendo del tamaño del archivo pageOrderPrompt=Orden de páginas (Introduzca una lista de números de página separados por coma): -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : +pageSelectionPrompt=Selección de página personalizada (Intruduzca una lista de números de página separados por comas 1,5,6 o funciones como 2n+1) : goToPage=Ir a true=Verdadero false=Falso unknown=Desconocido save=Guardar +saveToBrowser=Guardar en el Navegador close=Cerrar filesSelected=archivos seleccionados noFavourites=No se agregaron favoritos +downloadComplete=Descarga finalizada bored=¿Cansado de esperar? alphabet=Alfabeto downloadPdf=Descargar PDF @@ -52,27 +54,45 @@ notAuthenticatedMessage=Usuario no autentificado. userNotFoundMessage=Usuario no encontrado. incorrectPasswordMessage=La contraseña actual no es correcta. usernameExistsMessage=El nuevo nombre de usuario está en uso. +invalidUsernameMessage=Nombre de usuario no válido, El nombre de ususario debe contener únicamente números y caracteres alfabéticos. +deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actualmente en uso. +deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse. +downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual +downgradeCurrentUserLongMessage=No se puede degradar el rol del usuario actual. Por lo tanto, el usuario actual no se mostrará. +error=Error +oops=Ups! +help=Help +goHomepage=Ir a la página principal +joinDiscord=Únase a nuestro servidor Discord +seeDockerHub=Ver Docker Hub +visitGithub=Visitar Repositorio de Github +donate=Donar +color=Color +sponsor=Patrocinador + ############### # Pipeline # ############### -pipeline.header=Menu Pipeline (Alfa) +pipeline.header=Menú de canalización (Alfa) pipeline.uploadButton=Cargar personalización pipeline.configureButton=Configurar pipeline.defaultOption=Personalizar pipeline.submitButton=Enviar +pipeline.help=Ayuda de Canalización +pipeline.scanHelp=Ayuda de escaneado de carpetas ###################### # Pipeline Options # ###################### -pipelineOptions.header=Configuración Pipeline -pipelineOptions.pipelineNameLabel=Nombre del Pipeline -pipelineOptions.saveSettings=Guardar configuración de la oiperación -pipelineOptions.pipelineNamePrompt=Introduzca aquí el nombre del pipeline +pipelineOptions.header=Configuración de la canalización +pipelineOptions.pipelineNameLabel=Nombre de la canalización +pipelineOptions.saveSettings=Guardar configuración de la canalización +pipelineOptions.pipelineNamePrompt=Introduzca aquí el nombre de la canalización pipelineOptions.selectOperation=Seleccione la operación pipelineOptions.addOperationButton=Añadir operación -pipelineOptions.pipelineHeader=Pipeline: +pipelineOptions.pipelineHeader=Canalización: pipelineOptions.saveButton=Descargar pipelineOptions.validateButton=Validar @@ -94,6 +114,7 @@ navbar.settings=Configuración ############# settings.title=Configuración settings.update=Actualización disponible +settings.updateAvailable={0} es la versión instalada. Hay disponible una versión nueva ({1}). settings.appVersion=Versión de la aplicación: settings.downloadOption.title=Elegir la opción de descarga (para descargas de un solo archivo sin ZIP): settings.downloadOption.1=Abrir en la misma ventana @@ -102,12 +123,13 @@ settings.downloadOption.3=Descargar el archivo settings.zipThreshold=Archivos ZIP cuando excede el número de archivos descargados settings.signOut=Desconectar settings.accountSettings=Configuración de la cuenta - - +settings.bored.help=Habilita el juego del huevo de pascua +settings.cacheInputs.name=Guardar entradas del formulario +settings.cacheInputs.help=Habilitar guardar entradas previamente utilizadas para futuras acciones changeCreds.title=Cambiar Credenciales changeCreds.header=Actualice los detalles de su cuenta -changeCreds.changeUserAndPassword=Está usando las credenciales por defecto. Por favor, introduzca una nueva contraseña (y usuario si lo desea) +changeCreds.changePassword=Está usando las credenciales de inicio de sesión por defecto. Por favor, introduzca una contraseña nueva changeCreds.newUsername=Nuevo usuario changeCreds.oldPassword=Contraseña actual changeCreds.newPassword=Nueva contraseña @@ -142,14 +164,18 @@ adminUserSettings.header=Configuración de control de usuario administrador adminUserSettings.admin=Administrador adminUserSettings.user=Usuario adminUserSettings.addUser=Añadir Nuevo Usuario +adminUserSettings.usernameInfo=El nombrede usuario debe contener únicamente letras y números, no espacios ni caracteres especiales. adminUserSettings.roles=Roles adminUserSettings.role=Rol adminUserSettings.actions=Acciones adminUserSettings.apiUser=Usuario limitado de API +adminUserSettings.extraApiUser=Otro usuario limitado de API adminUserSettings.webOnlyUser=Usuario solo web -adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.demoUser=Usuario Demo (Sin ajustes personalizados) +adminUserSettings.internalApiUser=Usuario interno de API adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso adminUserSettings.submit=Guardar Usuario +adminUserSettings.changeUserRole=Cambiar rol de usuario ############# # HOME-PAGE # @@ -224,7 +250,7 @@ compressPdfs.tags=aplastar,pequeño,diminuto home.changeMetadata.title=Cambiar metadatos home.changeMetadata.desc=Cambiar/Eliminar/Añadir metadatos al documento PDF -changeMetadata.tags==Título,autor,fecha,creación,hora,editorial,productor,estadísticas +changeMetadata.tags=título,autor,fecha,creación,hora,editorial,productor,estadísticas home.fileToPDF.title=Convertir archivo a PDF home.fileToPDF.desc=Convertir casi cualquier archivo a PDF (DOCX, PNG, XLS, PPT, TXT y más) @@ -367,7 +393,7 @@ showJS.tags=JS home.autoRedact.title=Auto Redactar home.autoRedact.desc=Redactar automáticamente (ocultar) texto en un PDF según el texto introducido -autoRedact.tags=Redact,Hide,black out,black,marker,hidden +autoRedact.tags=Redactar,Ocultar,ocultar,negro,subrayador,oculto home.tableExtraxt.title=PDF a CSV home.tableExtraxt.desc=Extraer Tablas de un PDF convirtiéndolas a CSV @@ -387,9 +413,18 @@ home.split-by-sections.title=Dividir PDF por Secciones home.split-by-sections.desc=Dividir cada página de un PDF en secciones verticales y horizontales más pequeñas split-by-sections.tags=Dividir sección, Dividir, Personalizar -home.AddStampRequest.title=Add Stamp to PDF -home.AddStampRequest.desc=Add text or add image stamps at set locations -AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.AddStampRequest.title=Añadir Sello a PDF +home.AddStampRequest.desc=Añadir texto o sello de imagen en ubicaciones específicas +AddStampRequest.tags=Sello, Añadir imagen, centrar imagen, Marca de agua, PDF, Incrustar, Personalizar + + +home.PDFToBook.title=PDF a Libro +home.PDFToBook.desc=Convierte PDF a formatos de Libro/Cómic usando Calibre +PDFToBook.tags=Libro,Cómic,Calibre,Convertir,Manga,Amazon,Kindle + +home.BookToPDF.title=Libro a PDF +home.BookToPDF.desc=Convierte formatos de Libro/Cómic a PDF usando Calibre +BookToPDF.tags=Libro,Cómic,Calibre,Convertir,manga,Amazon,Kindle ########################### @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Iniciar sesión +login.header=Iniciar sesión login.signin=Iniciar sesión login.rememberme=Recordarme login.invalid=Nombre de usuario o contraseña erróneos. login.locked=Su cuenta se ha bloqueado. login.signinTitle=Por favor, inicie sesión +login.ssoSignIn=Iniciar sesión a través del inicio de sesión único +login.oauth2AutoCreateDisabled=Usuario DE creación automática de OAUTH2 DESACTIVADO #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Convertir a página única pageExtracter.title=Extraer Páginas pageExtracter.header=Extraer Páginas pageExtracter.submit=Extraer +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -563,7 +602,7 @@ autoSplitPDF.submit=Entregar #pipeline -pipeline.title=Pipeline +pipeline.title=Canalización #pageLayout @@ -623,6 +662,18 @@ compare.document.1=Documento 1 compare.document.2=Documento 2 compare.submit=Comparar +#BookToPDF +BookToPDF.title=Libros y Cómics a PDF +BookToPDF.header=Libro a PDF +BookToPDF.credit=Usa Calibre +BookToPDF.submit=Convertir + +#PDFToBook +PDFToBook.title=PDF a Libro +PDFToBook.header=PDF a Libro +PDFToBook.selectText.1=Formato +PDFToBook.credit=Utiliza Calibre +PDFToBook.submit=Convertir #sign sign.title=Firmar @@ -643,6 +694,7 @@ repair.submit=Reparar #flatten flatten.title=Aplanar flatten.header=Acoplar archivos PDF +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Aplanar @@ -726,11 +778,23 @@ merge.submit=Unir pdfOrganiser.title=Organizador de páginas pdfOrganiser.header=Organizador de páginas PDF pdfOrganiser.submit=Organizar páginas +pdfOrganiser.mode=Modo +pdfOrganiser.mode.1=Orden de páginas personalizado +pdfOrganiser.mode.2=Orden inverso +pdfOrganiser.mode.3=Ordenar dúplex +pdfOrganiser.mode.4=Ordenar folleto +pdfOrganiser.mode.5=Orden de folleto de encuadernado lateral +pdfOrganiser.mode.6=División par-impar +pdfOrganiser.mode.7=Quitar primera +pdfOrganiser.mode.8=Quitar última +pdfOrganiser.mode.9=Quitar primera y última +pdfOrganiser.placeholder=(por ej., 1,3,2 o 4-8,2,10-12 o 2n-1) #multiTool multiTool.title=Multi-herramienta PDF multiTool.header=Multi-herramienta PDF +multiTool.uploadPrompts=Por favor, cargue PDF #view pdf viewPdf.title=Ver PDF @@ -741,6 +805,7 @@ pageRemover.title=Eliminador de páginas pageRemover.header=Eliminador de páginas PDF pageRemover.pagesToDelete=Páginas a eliminar (introducir una lista de números de página separados por coma): pageRemover.submit=Eliminar Páginas +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Seleccionar ángulo de rotación (en múltiplos de 90 grados) rotate.submit=Rotar -#merge +#split-pdfs split.title=Dividir PDF split.header=Dividir PDF split.desc.1=Los números que seleccione son el número de página en el que desea hacer una división -split.desc.2=Como tal, seleccionar 1,3,7-8 dividiría un documento de 10 páginas en 6 archivos PDF separados con: +split.desc.2=Como tal, seleccionar 1,3,7-9 dividiría un documento de 10 páginas en 6 archivos PDF separados con: split.desc.3=Documento #1: Página 1 split.desc.4=Documento #2: Páginas 2 y 3 -split.desc.5=Documento #3: Páginas 4, 5 y 6 -split.desc.6=Documento #4: Página 7 -split.desc.7=Documento #5: Página 8 -split.desc.8=Documento #6: Páginas 9 y 10 +split.desc.5=Documento #3: Páginas 4, 5, 6 y 7 +split.desc.6=Documento #4: Página 8 +split.desc.7=Documento #5: Página 9 +split.desc.8=Documento #6: Páginas 10 split.splitPages=Introducir las páginas para dividir: split.submit=Dividir @@ -828,6 +893,8 @@ watermark.selectText.7=Opacidad (0% - 100%): watermark.selectText.8=Tipo de marca de agua: watermark.selectText.9=Imagen de marca de agua: watermark.submit=Añadir marca de agua +watermark.type.1=Texto +watermark.type.2=Imagen #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF a PDF/A pdfToPDFA.header=PDF a PDF/A pdfToPDFA.credit=Este servicio usa OCRmyPDF para la conversión a PDF/A pdfToPDFA.submit=Convertir +pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Convertir #PDFToHTML PDFToHTML.title=PDF a HTML PDFToHTML.header=PDF a HTML -PDFToHTML.credit=Este servicio utiliza LibreOffice para la conversión de archivos +PDFToHTML.credit=Este servicio utiliza pdftohtml para la conversión de archivos PDFToHTML.submit=Convertir @@ -925,6 +994,7 @@ PDFToCSV.prompt=Elija una página para extraer la tabla PDFToCSV.submit=Extraer #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Dividir PDF por tamaño o número split-by-size-or-count.type.label=Seleccionar tipo de división split-by-size-or-count.type.size=Por tamaño @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Divisiones Verticales split-by-sections.horizontal.placeholder=Introduzca el número de divisiones horizontales split-by-sections.vertical.placeholder=Introduzca el número de divisiones verticales split-by-sections.submit=Dividir PDF +split-by-sections.merge=Unir en Un PDF + + +#printFile +printFile.title=Imprimir archivo +printFile.header=Imprimir archivo en la impresora +printFile.selectText.1=Seleccionar archivo para imprimir +printFile.selectText.2=Introducir nombre de la impresora +printFile.submit=Imprimir #licenses @@ -970,3 +1049,16 @@ licenses.version=Versión licenses.license=Licencia +# error +error.sorry=¡Perdón por el fallo! +error.needHelp=Necesita ayuda / Encontró un fallo? +error.contactTip=Si sigue experimentando errores, no dude en contactarnos para solicitar soporte. Puede enviarnos un ticket en la página de GitHub o contactarnos mediante Discord: +error.404.head=404 - Página no encontrada | Ups, tropezamos con el código! +error.404.1=Parece que no podemos encontrar la página que está buscando. +error.404.2=Algo salió mal +error.github=Envíe un ticket en GitHub +error.showStack=Mostrar seguimiento de pila +error.copyStack=Mostrar seguimiento de pila +error.githubSubmit=GitHub - Enviar un ticket +error.discordSubmit=Discord - Enviar mensaje de soporte + diff --git a/src/main/resources/messages_eu_ES.properties b/src/main/resources/messages_eu_ES.properties index 21767b94..c9337e03 100644 --- a/src/main/resources/messages_eu_ES.properties +++ b/src/main/resources/messages_eu_ES.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=Egiazkoa false=Faltsua unknown=Ezezaguna save=Gorde +saveToBrowser=Save to Browser close=Itxi filesSelected=Hautatutako fitxategiak noFavourites=Ez dira gogokoak gehitu +downloadComplete=Download Complete bored=Itxaroten aspertuta? alphabet=Alfabetoa downloadPdf=PDFa deskargatu @@ -52,16 +54,34 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Ezin da uneko erabiltzailearen rola jaitsi +downgradeCurrentUserLongMessage=Ezin da uneko erabiltzailearen rola jaitsi. Beraz, oraingo erabiltzailea ez da erakutsiko. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Ezarpenak ############# settings.title=Ezarpenak settings.update=Eguneratze eskuragarria +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Aplikazioaren bertsioa: settings.downloadOption.title=Hautatu deskargatzeko aukera (fitxategi bakarra deskargatzeko ZIP gabe): settings.downloadOption.1=Ireki leiho berean @@ -102,12 +123,13 @@ settings.downloadOption.3=Deskargatu fitxategia settings.zipThreshold=ZIP fitxategiak deskargatutako fitxategi kopurua gainditzen denean settings.signOut=Saioa itxi settings.accountSettings=Kontuaren ezarpenak - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Change Credentials changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=New Username changeCreds.oldPassword=Current Password changeCreds.newPassword=New Password @@ -142,14 +164,18 @@ adminUserSettings.header=Admin Erabiltzailearen Ezarpenen Kontrolak adminUserSettings.admin=Admin adminUserSettings.user=Erabiltzaile adminUserSettings.addUser=Erabiltzaile berria +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Rolak adminUserSettings.role=Rol adminUserSettings.actions=Ekintzak adminUserSettings.apiUser=APIren erabiltzaile mugatua +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Web-erabiltzailea bakarrik adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Force user to change username/password on login +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Gorde Erabiltzailea +adminUserSettings.changeUserRole=Erabiltzailearen rola aldatu ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Saioa hasi +login.header=Saioa hasi login.signin=Saioa hasi login.rememberme=Oroitu nazazu login.invalid=Okerreko erabiltzaile izena edo pasahitza. login.locked=Zure kontua blokeatu egin da. login.signinTitle=Mesedez, hasi saioa +login.ssoSignIn=Hasi saioa Saioa hasteko modu bakarraren bidez +login.oauth2AutoCreateDisabled=OAUTH2 Sortu automatikoki erabiltzailea desgaituta dago #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Orrialde bakarrera bihurtu pageExtracter.title=Atera orriak pageExtracter.header=Atera orriak pageExtracter.submit=Atera +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=1. dokumentua compare.document.2=2. dokumentua compare.submit=Konparatu +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Sinatu @@ -643,6 +694,7 @@ repair.submit=Konpondu #flatten flatten.title=Lautu flatten.header=Akoplatu PDF fitxategiak +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Lautu @@ -726,11 +778,23 @@ merge.submit=Elkartu pdfOrganiser.title=Orrialdeen antolatzailea pdfOrganiser.header=PDF orrialdeen antolatzailea pdfOrganiser.submit=Antolatu orrialdeak +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF erabilera anitzeko tresna multiTool.header=PDF erabilera anitzeko tresna +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=Orrialdeen ezabatzailea pageRemover.header=PDF orrialdeen ezabatzailea pageRemover.pagesToDelete=Ezabatu beharreko orrialdeak (sartu komaz bereizitako orrialde-zenbakien zerrenda): pageRemover.submit=Ezabatu orrialdeak +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Hautatu errotazio-angelua (90 graduko multiploetan): rotate.submit=Biratu -#merge +#split-pdfs split.title=Zatitu PDFa split.header=Zatitu PDFa split.desc.1=Hautatzen dituzun zenbakiak zatiketa egin nahi duzun orrialde-zenbakiak dira -split.desc.2=Beraz, 1,3,7-8 hautatzean 10 orrialdeko dokumentua zatituko luke 6 PDF fitxategi bereizituetan +split.desc.2=Beraz, 1,3,7-9 hautatzean 10 orrialdeko dokumentua zatituko luke 6 PDF fitxategi bereizituetan split.desc.3=#1 Dokumentua: 1. orrialdea split.desc.4=#2 Dokumentua: 2. eta 3. orrialdeak -split.desc.5=#3 Dokumentua: 4., 5. eta 6. orrialdeak -split.desc.6=#4 Dokumentua: 7. orrialdea -split.desc.7=#5 Dokumentua: 8. orrialdea -split.desc.8=#6 Dokumentua: 9. eta 10. orrialdeak +split.desc.5=#3 Dokumentua: 4., 5., 6. eta 7. orrialdeak +split.desc.6=#4 Dokumentua: 8. orrialdea +split.desc.7=#5 Dokumentua: 9. orrialdea +split.desc.8=#6 Dokumentua: 10. orrialdeak split.splitPages=Sartu orrialdeak zatitzeko: split.submit=Zatitu @@ -828,6 +893,8 @@ watermark.selectText.7=Opakutasuna (0% - 100%): watermark.selectText.8=Watermark Type: watermark.selectText.9=Watermark Image: watermark.submit=Gehitu ur-marka +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDFa PDF/A bihurtu pdfToPDFA.header=PDFa PDF/A bihurtu pdfToPDFA.credit=Zerbitzu honek OCRmyPDF erabiltzen du PDFak PDF/A bihurtzeko pdfToPDFA.submit=Bihurtu +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Bihurtu #PDFToHTML PDFToHTML.title=PDFa HTML bihurtu PDFToHTML.header=PDFa HTML bihurtu -PDFToHTML.credit=Zerbitzu honek LibreOffice erabiltzen du fitxategiak bihurtzeko +PDFToHTML.credit=Zerbitzu honek pdftohtml erabiltzen du fitxategiak bihurtzeko PDFToHTML.submit=Bihurtu @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=Extracto #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_fr_FR.properties b/src/main/resources/messages_fr_FR.properties index 7bda2156..06068ffc 100644 --- a/src/main/resources/messages_fr_FR.properties +++ b/src/main/resources/messages_fr_FR.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -9,18 +9,20 @@ multiPdfPrompt=Sélectionnez les PDF multiPdfDropPrompt=Sélectionnez (ou glissez-déposez) tous les PDF dont vous avez besoin imgPrompt=Choisir une image genericSubmit=Envoyer -processTimeWarning=Attention, ce processus peut prendre jusqu\u2019à une minute en fonction de la taille du fichier. -pageOrderPrompt=Ordre des pages (entrez une liste de numéros de page séparés par des virgules ou des fonctions telles que 2n+1)\u00a0: -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : +processTimeWarning=Attention, ce processus peut prendre jusqu’à une minute en fonction de la taille du fichier. +pageOrderPrompt=Ordre des pages (entrez une liste de numéros de page séparés par des virgules ou des fonctions telles que 2n+1) : +pageSelectionPrompt=Sélection des pages (entrez une liste de numéros de page séparés par des virgules ou des fonctions telles que 2n+1) : goToPage=Aller true=Vrai false=Faux unknown=Inconnu save=Enregistrer +saveToBrowser=Save to Browser close=Fermer filesSelected=fichiers sélectionnés noFavourites=Aucun favori ajouté -bored=Ennuyé d\u2019attendre\u00a0? +downloadComplete=Téléchargement terminé +bored=Ennuyé d’attendre ? alphabet=Alphabet downloadPdf=Télécharger le PDF text=Texte @@ -31,9 +33,9 @@ sizes.small=Petit sizes.medium=Moyen sizes.large=Grand sizes.x-large=Très grand -error.pdfPassword=Le document PDF est protégé par un mot de passe et le mot de passe n\u2019a pas été fourni ou était incorrect +error.pdfPassword=Le document PDF est protégé par un mot de passe et le mot de passe n’a pas été fourni ou était incorrect delete=Supprimer -username=Nom d\u2019utilisateur +username=Nom d’utilisateur password=Mot de passe welcome=Bienvenue property=Propriété @@ -42,39 +44,57 @@ white=Blanc red=Rouge green=Vert blue=Bleu -custom=Personnalisé\u2026 -WorkInProgess=Work in progress, May not work or be buggy, Please report any problems! -poweredBy=Powered by -yes=Yes -no=No -changedCredsMessage=Les identifiants ont été mis à jour\u00a0! +custom=Personnalisé… +WorkInProgess=En cours de développement, merci de nous remonter les problèmes que vous pourriez constater! +poweredBy=Propulsé par +yes=Oui +no=Non +changedCredsMessage=Les identifiants ont été mis à jour ! notAuthenticatedMessage=Utilisateur non authentifié. userNotFoundMessage=Utilisateur non trouvé. incorrectPasswordMessage=Le mot de passe actuel est incorrect. -usernameExistsMessage=Le nouveau nom d\u2019utilisateur existe déjà. +usernameExistsMessage=Le nouveau nom d’utilisateur existe déjà. +invalidUsernameMessage=Nom d’utilisateur invalide, le nom d’utilisateur ne peut contenir que des chiffres et des lettres. +deleteCurrentUserMessage=Impossible de supprimer l’utilisateur actuellement connecté. +deleteUsernameExistsMessage=Le nom d’utilisateur n’existe pas et ne peut pas être supprimé. +downgradeCurrentUserMessage=Impossible de rétrograder le rôle de l'utilisateur actuel +downgradeCurrentUserLongMessage=Impossible de rétrograder le rôle de l'utilisateur actuel. Par conséquent, l'utilisateur actuel ne sera pas affiché. +error=Erreur +oops=Oups ! +help=Aide +goHomepage=Aller à la page d’accueil +joinDiscord=Rejoignez notre serveur Discord +seeDockerHub=Consulter le Docker Hub +visitGithub=Visiter le dépôt Github +donate=Faire un don +color=Couleur +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) -pipeline.uploadButton=Upload Custom -pipeline.configureButton=Configure -pipeline.defaultOption=Custom -pipeline.submitButton=Submit +pipeline.header=Menu Pipeline (Alpha) +pipeline.uploadButton=Charger une personnalisation +pipeline.configureButton=Configurer +pipeline.defaultOption=Personnaliser +pipeline.submitButton=Soumettre +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # ###################### -pipelineOptions.header=Pipeline Configuration -pipelineOptions.pipelineNameLabel=Pipeline Name -pipelineOptions.saveSettings=Save Operation Settings -pipelineOptions.pipelineNamePrompt=Enter pipeline name here -pipelineOptions.selectOperation=Select Operation -pipelineOptions.addOperationButton=Add operation +pipelineOptions.header=Configuration du pipeline +pipelineOptions.pipelineNameLabel=Nom du pipeline +pipelineOptions.saveSettings=Sauvegarder la configuration +pipelineOptions.pipelineNamePrompt=Entrez ici le nom du pipeline +pipelineOptions.selectOperation=Sélectionner une opération +pipelineOptions.addOperationButton=Ajouter une opération pipelineOptions.pipelineHeader=Pipeline: -pipelineOptions.saveButton=Download -pipelineOptions.validateButton=Validate +pipelineOptions.saveButton=Télécharger +pipelineOptions.validateButton=Valider @@ -94,21 +114,23 @@ navbar.settings=Paramètres ############# settings.title=Paramètres settings.update=Mise à jour disponible -settings.appVersion=Version de l\u2019application\u00a0: -settings.downloadOption.title=Choisissez l\u2019option de téléchargement (pour les téléchargements à fichier unique non ZIP)\u00a0: +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. +settings.appVersion=Version de l’application : +settings.downloadOption.title=Choisissez l’option de téléchargement (pour les téléchargements à fichier unique non ZIP) : settings.downloadOption.1=Ouvrir dans la même fenêtre settings.downloadOption.2=Ouvrir dans une nouvelle fenêtre settings.downloadOption.3=Télécharger le fichier settings.zipThreshold=Compresser les fichiers en ZIP lorsque le nombre de fichiers téléchargés dépasse settings.signOut=Déconnexion settings.accountSettings=Paramètres du compte - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Modifiez vos identifiants changeCreds.header=Mettez à jour vos identifiants de connexion -changeCreds.changeUserAndPassword=Vous utilisez les identifiants de connexion par défaut. Veuillez entrer un nouveau mot de passe (et nom d\u2019utilisateur si vous le souhaitez) -changeCreds.newUsername=Nouveau nom d\u2019utilisateur +changeCreds.changePassword=Vous utilisez les identifiants de connexion par défaut. Veuillez saisir un nouveau mot de passe +changeCreds.newUsername=Nouveau nom d’utilisateur changeCreds.oldPassword=Mot de passe actuel changeCreds.newPassword=Nouveau mot de passe changeCreds.confirmNewPassword=Confirmer le nouveau mot de passe @@ -118,10 +140,10 @@ changeCreds.submit=Soumettre les modifications account.title=Paramètres du compte account.accountSettings=Paramètres du compte -account.adminSettings=Paramètres d\u2019administration \u2013 Voir et ajouter des utilisateurs +account.adminSettings=Paramètres d’administration – Voir et ajouter des utilisateurs account.userControlSettings=Contrôle des paramètres des utilisateurs -account.changeUsername=Modifier le nom d\u2019utilisateur -account.newUsername=Nouveau nom d\u2019utilisateur +account.changeUsername=Modifier le nom d’utilisateur +account.newUsername=Nouveau nom d’utilisateur account.password=Mot de passe de confirmation account.oldPassword=Ancien mot de passe account.newPassword=Nouveau mot de passe @@ -133,8 +155,8 @@ account.syncTitle=Synchroniser les paramètres du navigateur avec le compte account.settingsCompare=Comparaison des paramètres account.property=Propriété account.webBrowserSettings=Paramètres du navigateur -account.syncToBrowser=Synchroniser\u00a0: Compte → Navigateur -account.syncToAccount=Synchroniser\u00a0: Compte ← Navigateur +account.syncToBrowser=Synchroniser : Compte → Navigateur +account.syncToAccount=Synchroniser : Compte ← Navigateur adminUserSettings.title=Administration des paramètres des utilisateurs @@ -142,14 +164,18 @@ adminUserSettings.header=Administration des paramètres des utilisateurs adminUserSettings.admin=Administateur adminUserSettings.user=Utilisateur adminUserSettings.addUser=Ajouter un utilisateur +adminUserSettings.usernameInfo=Le nom d’utilisateur ne doit contenir que des lettres et des chiffres, sans espaces ni caractères spéciaux. adminUserSettings.roles=Rôles adminUserSettings.role=Rôle adminUserSettings.actions=Actions adminUserSettings.apiUser=Utilisateur API limité +adminUserSettings.extraApiUser=Utilisateur limité supplémentaire de l’API adminUserSettings.webOnlyUser=Utilisateur Web uniquement -adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Forcer l\u2019utilisateur à changer son nom d\u2019utilisateur/mot de passe lors de la connexion +adminUserSettings.demoUser=Demo User (Paramètres par défaut) +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Forcer l’utilisateur à changer son nom d’utilisateur/mot de passe lors de la connexion adminUserSettings.submit=Ajouter +adminUserSettings.changeUserRole=Changer le rôle de l'utilisateur ############# # HOME-PAGE # @@ -159,7 +185,7 @@ home.searchBar=Rechercher des fonctionnalités... home.viewPdf.title=Visionner le PDF -home.viewPdf.desc=Visionner, annoter, ajouter du texte ou des images +home.viewPdf.desc=Visionner, annoter, ajouter du texte ou des images. viewPdf.tags=visualiser,lire,annoter,texte,image home.multiTool.title=Outil multifonction PDF @@ -168,7 +194,7 @@ multiTool.tags=outil multifonction,opération multifonction,interface utilisateu home.merge.title=Fusionner home.merge.desc=Fusionnez facilement plusieurs PDF en un seul. -merge.tags=fusionner,opérations sur les pages,backeend,server side,merge +merge.tags=fusionner,opérations sur les pages,backend,server side,merge home.split.title=Diviser home.split.desc=Divisez un PDF en plusieurs documents. @@ -188,7 +214,7 @@ home.pdfToImage.desc=Convertissez un PDF en image (PNG, JPEG, GIF). pdfToImage.tags=conversion,img,jpg,image,photo home.pdfOrganiser.title=Organiser -home.pdfOrganiser.desc=Supprimez ou réorganisez les pages dans n\u2019importe quel ordre. +home.pdfOrganiser.desc=Supprimez ou réorganisez les pages dans n’importe quel ordre. pdfOrganiser.tags=organiser,recto-verso,duplex,even,odd,sort,move @@ -198,7 +224,7 @@ addImage.tags=img,jpg,image,photo home.watermark.title=Ajouter un filigrane home.watermark.desc=Ajoutez un filigrane personnalisé à votre PDF. -watermark.tags=texte,filigrane,label,propriété,droit d\u2019auteur,marque déposée,img,jpg,image,photo,copyright,trademark +watermark.tags=texte,filigrane,label,propriété,droit d’auteur,marque déposée,img,jpg,image,photo,copyright,trademark home.permissions.title=Modifier les permissions home.permissions.desc=Modifiez les permissions de votre PDF. @@ -227,16 +253,16 @@ home.changeMetadata.desc=Modifiez, supprimez ou ajoutez des métadonnées à un changeMetadata.tags=métadonnées,titre,auteur,date,création,heure,éditeur,statistiques,title,author,date,creation,time,publisher,producer,stats,metadata home.fileToPDF.title=Fichier en PDF -home.fileToPDF.desc=Convertissez presque n\u2019importe quel fichiers en PDF (DOCX, PNG, XLS, PPT, TXT et plus). +home.fileToPDF.desc=Convertissez presque n’importe quel fichiers en PDF (DOCX, PNG, XLS, PPT, TXT et plus). fileToPDF.tags=convertion,transformation,format,document,image,slide,texte,conversion,office,docs,word,excel,powerpoint home.ocr.title=OCR / Nettoyage des numérisations -home.ocr.desc=Utilisez l\u2019OCR pour analyser et détecter le texte des images d\u2019un PDF et le rajouter en temps que tel. +home.ocr.desc=Utilisez l’OCR pour analyser et détecter le texte des images d’un PDF et le rajouter en temps que tel. ocr.tags=ocr,reconnaissance,texte,image,numérisation,scan,read,identify,detection,editable home.extractImages.title=Extraire les images -home.extractImages.desc=Extrayez toutes les images d\u2019un PDF et enregistrez-les dans un ZIP. +home.extractImages.desc=Extrayez toutes les images d’un PDF et enregistrez-les dans un ZIP. extractImages.tags=image,photo,save,archive,zip,capture,grab home.pdfToPDFA.title=PDF en PDF/A @@ -265,7 +291,7 @@ home.PDFToXML.desc=Convertissez un PDF au format XML. PDFToXML.tags=xml,extraction de données,contenu structuré,interopérabilité,data-extraction,structured-content,interop,transformation,convert home.ScannerImageSplit.title=Diviser les photos numérisées -home.ScannerImageSplit.desc=Divisez plusieurs photos à partir d\u2019une photo ou d\u2019un PDF. +home.ScannerImageSplit.desc=Divisez plusieurs photos à partir d’une photo ou d’un PDF. ScannerImageSplit.tags=diviser,détecter automatiquement,numériser,separate,auto-detect,scans,multi-photo,organize home.sign.title=Signer @@ -273,7 +299,7 @@ home.sign.desc=Ajoutez une signature au PDF avec un dessin, du texte ou une imag sign.tags=signer,authorize,initials,drawn-signature,text-sign,image-signature home.flatten.title=Rendre inerte -home.flatten.desc=Supprimez tous les éléments et formulaires interactifs d\u2019un PDF. +home.flatten.desc=Supprimez tous les éléments et formulaires interactifs d’un PDF. flatten.tags=inerte,static,deactivate,non-interactive,streamline home.repair.title=Réparer @@ -281,12 +307,12 @@ home.repair.desc=Essayez de réparer un PDF corrompu ou cassé. repair.tags=réparer,restaurer,corriger,récupérer,fix,restore,correction,recover home.removeBlanks.title=Supprimer les pages vierges -home.removeBlanks.desc=Détectez et supprimez les pages vierges d\u2019un PDF. +home.removeBlanks.desc=Détectez et supprimez les pages vierges d’un PDF. removeBlanks.tags=pages vierges,supprimer,nettoyer,cleanup,streamline,non-content,organize -home.removeAnnotations.title=Remove Annotations -home.removeAnnotations.desc=Removes all comments/annotations from a PDF -removeAnnotations.tags=comments,highlight,notes,markup,remove +home.removeAnnotations.title=Supprimer les annotations +home.removeAnnotations.desc=Supprimer tous les commentaires/annotations d’un PDF. +removeAnnotations.tags=commentaires,supprimer,annotations,highlight,notes,markup,remove home.compare.title=Comparer home.compare.desc=Comparez et visualisez les différences entre deux PDF. @@ -297,11 +323,11 @@ home.certSign.desc=Signez un PDF avec un certificat ou une clé (PEM/P12). certSign.tags=signer,chiffrer,certificat,authenticate,PEM,P12,official,encrypt home.pageLayout.title=Fusionner des pages -home.pageLayout.desc=Fusionnez plusieurs pages d\u2019un PDF en une seule. +home.pageLayout.desc=Fusionnez plusieurs pages d’un PDF en une seule. pageLayout.tags=fusionner,merge,composite,single-view,organize -home.scalePages.title=Ajuster l\u2019échelle ou la taille -home.scalePages.desc=Modifiez la taille ou l\u2019échelle d\u2019une page et/ou de son contenu. +home.scalePages.title=Ajuster l’échelle ou la taille +home.scalePages.desc=Modifiez la taille ou l’échelle d’une page et/ou de son contenu. scalePages.tags=ajuster,redimensionner,resize,modify,dimension,adapt home.pipeline.title=Pipeline (avancé) @@ -317,11 +343,11 @@ home.auto-rename.desc=Renommez automatiquement un fichier PDF en fonction de son auto-rename.tags=renommer,détection automatique,réétiqueter,auto-detect,header-based,organize,relabel home.adjust-contrast.title=Ajuster les couleurs -home.adjust-contrast.desc=Ajustez le contraste, la saturation et la luminosité d\u2019un PDF. +home.adjust-contrast.desc=Ajustez le contraste, la saturation et la luminosité d’un PDF. adjust-contrast.tags=ajuster,couleurs,amélioration,color-correction,tune,modify,enhance home.crop.title=Redimensionner -home.crop.desc=Redimmensionnez un PDF pour réduire sa taille (en conservant le texte\u00a0!). +home.crop.desc=Redimmensionnez un PDF pour réduire sa taille (en conservant le texte !). crop.tags=redimensionner,trim,shrink,edit,shape home.autoSplitPDF.title=Séparer automatiquement les pages @@ -333,16 +359,16 @@ home.sanitizePdf.desc=Supprimez les scripts et autres éléments des PDF. sanitizePdf.tags=assainir,sécurisé,clean,secure,safe,remove-threats home.URLToPDF.title=URL en PDF -home.URLToPDF.desc=Convertissez n\u2019importe quelle URL http(s) en PDF. +home.URLToPDF.desc=Convertissez n’importe quelle URL http(s) en PDF. URLToPDF.tags=pdf,contenu Web,save-page,web-to-doc,archive home.HTMLToPDF.title=HTML en PDF -home.HTMLToPDF.desc=Convertissez n\u2019importe quel fichier HTML ou ZIP en PDF. +home.HTMLToPDF.desc=Convertissez n’importe quel fichier HTML ou ZIP en PDF. HTMLToPDF.tags=html,markup,contenu Web,transformation,convert home.MarkdownToPDF.title=Markdown en PDF -home.MarkdownToPDF.desc=Convertissez n\u2019importe quel fichier Markdown en PDF. +home.MarkdownToPDF.desc=Convertissez n’importe quel fichier Markdown en PDF. MarkdownToPDF.tags=markdown,markup,contenu Web,transformation,convert @@ -366,11 +392,11 @@ home.showJS.desc=Recherche et affiche tout JavaScript injecté dans un PDF. showJS.tags=JS home.autoRedact.title=Caviarder automatiquement -home.autoRedact.desc=Caviardez automatiquement les informations sensibles d\u2019un PDF. +home.autoRedact.desc=Caviardez automatiquement les informations sensibles d’un PDF. autoRedact.tags=caviarder,redact,auto home.tableExtraxt.title=PDF en CSV -home.tableExtraxt.desc=Extrait les tableaux d\u2019un PDF et les transforme en CSV +home.tableExtraxt.desc=Extrait les tableaux d’un PDF et les transforme en CSV. tableExtraxt.tags=CSV,Table Extraction,extract,convert @@ -380,16 +406,25 @@ autoSizeSplitPDF.tags=pdf,split,document,organization home.overlay-pdfs.title=Incrustation de PDF -home.overlay-pdfs.desc=Incrustation d\u2019un PDF sur un autre PDF -overlay-pdfs.tags=Overlay +home.overlay-pdfs.desc=Incrustation d’un PDF sur un autre PDF. +overlay-pdfs.tags=Overlay,incrustation -home.split-by-sections.title=Split PDF by Sections -home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections -split-by-sections.tags=Section Split, Divide, Customize +home.split-by-sections.title=Séparer un PDF en sections +home.split-by-sections.desc=Diviser chaque page d’un PDF en sections horizontales/verticales plus petites. +split-by-sections.tags=Sections,Diviser,Section Split, Divide, Customize -home.AddStampRequest.title=Add Stamp to PDF -home.AddStampRequest.desc=Add text or add image stamps at set locations -AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.AddStampRequest.title=Ajouter un tampon sur un PDF +home.AddStampRequest.desc=Ajouter un texte ou l’image d’un tampon à un emplacement défini. +AddStampRequest.tags=Tampon,Ajouter,Stamp,Add image,center image,Watermark,PDF,Embed,Customize + + +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle ########################### @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Connexion +login.header=Connexion login.signin=Connexion login.rememberme=Se souvenir de moi -login.invalid=Nom d\u2019utilisateur ou mot de passe invalide. +login.invalid=Nom d’utilisateur ou mot de passe invalide. login.locked=Votre compte a été verrouillé. login.signinTitle=Veuillez vous connecter +login.ssoSignIn=Se connecter via l'authentification unique +login.oauth2AutoCreateDisabled=OAUTH2 Création automatique d'utilisateur désactivée #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Convertir en une seule page pageExtracter.title=Extraire des pages pageExtracter.header=Extraire des pages pageExtracter.submit=Extraire +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -467,37 +506,37 @@ HTMLToPDF.header=HTML en PDF HTMLToPDF.help=Accepte les fichiers HTML et les ZIP contenant du HTML, du CSS, des images, etc. (requis). HTMLToPDF.submit=Convertir HTMLToPDF.credit=Utilise WeasyPrint. -HTMLToPDF.zoom=Zoom level for displaying the website. -HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default) -HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default) -HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default) -HTMLToPDF.printBackground=Render the background of websites. -HTMLToPDF.defaultHeader=Enable Default Header (Name and page number) -HTMLToPDF.cssMediaType=Change the CSS media type of the page. -HTMLToPDF.none=None -HTMLToPDF.print=Print -HTMLToPDF.screen=Screen +HTMLToPDF.zoom=Niveau de zoom pour l’affichage du site web. +HTMLToPDF.pageWidth=Largeur de la page en centimètres. (Vide par défaut) +HTMLToPDF.pageHeight=Hauteur de la page en centimètres. (Vide par défaut) +HTMLToPDF.marginTop=Marge supérieure de la page en millimètres. (Vide par défaut) +HTMLToPDF.marginBottom=Marge inférieure de la page en millimètres. (Vide par défaut) +HTMLToPDF.marginLeft=Marge gauche de la page en millimètres. (Vide par défaut) +HTMLToPDF.marginRight=Marge droite de la page en millimètres. (Vide par défaut) +HTMLToPDF.printBackground=Restituer l’image de fond des sites web. +HTMLToPDF.defaultHeader=Activer l’entête par défaut (Nom et numéro de page) +HTMLToPDF.cssMediaType=Modifier le type de média CSS de la page. +HTMLToPDF.none=Aucun +HTMLToPDF.print=Imprimer +HTMLToPDF.screen=Écran #AddStampRequest -AddStampRequest.header=Stamp PDF -AddStampRequest.title=Stamp PDF -AddStampRequest.stampType=Stamp Type -AddStampRequest.stampText=Stamp Text -AddStampRequest.stampImage=Stamp Image +AddStampRequest.header=Tampon PDF +AddStampRequest.title=Tampon PDF +AddStampRequest.stampType=Type de tampon +AddStampRequest.stampText=Tampon texte +AddStampRequest.stampImage=Tampon image AddStampRequest.alphabet=Alphabet -AddStampRequest.fontSize=Font/Image Size +AddStampRequest.fontSize=Taille de fonte/image AddStampRequest.rotation=Rotation -AddStampRequest.opacity=Opacity +AddStampRequest.opacity=Opacité AddStampRequest.position=Position -AddStampRequest.overrideX=Override X Coordinate -AddStampRequest.overrideY=Override Y Coordinate -AddStampRequest.customMargin=Custom Margin -AddStampRequest.customColor=Custom Text Color -AddStampRequest.submit=Submit +AddStampRequest.overrideX=Définir coordonnées X +AddStampRequest.overrideY=Définir coordonnées Y +AddStampRequest.customMargin=Marge personnalisée +AddStampRequest.customColor=Couleur de texte personnalisée +AddStampRequest.submit=Soumettre #sanitizePDF @@ -553,9 +592,9 @@ autoSplitPDF.header=Séparer automatiquement les pages autoSplitPDF.description=Imprimez, insérez, numérisez, téléchargez et laissez-nous séparer automatiquement vos documents. Aucun travail de tri manuel nécessaire. autoSplitPDF.selectText.1=Imprimez des feuilles de séparation ci-dessous (le mode noir et blanc convient). autoSplitPDF.selectText.2=Numérisez tous vos documents en une seule fois en insérant les feuilles intercalaires entre eux. -autoSplitPDF.selectText.3=Téléchargez le fichier PDF numérisé et laissez Stirling PDF s\u2019occuper du reste. +autoSplitPDF.selectText.3=Téléchargez le fichier PDF numérisé et laissez Stirling PDF s’occuper du reste. autoSplitPDF.selectText.4=Les feuilles de séparation sont automatiquement détectées et supprimées, garantissant un document final soigné. -autoSplitPDF.formPrompt=PDF contenant des feuilles de séparation de Stirling PDF\u00a0: +autoSplitPDF.formPrompt=PDF contenant des feuilles de séparation de Stirling PDF : autoSplitPDF.duplexMode=Mode recto-verso autoSplitPDF.dividerDownload1=Auto Splitter Divider (minimal).pdf autoSplitPDF.dividerDownload2=Auto Splitter Divider (with instructions).pdf @@ -575,10 +614,10 @@ pageLayout.submit=Fusionner #scalePages -scalePages.title=Ajuster la taille ou l\u2019échelle -scalePages.header=Ajuster la taille ou l\u2019échelle -scalePages.pageSize=Taille d\u2019une page du document -scalePages.scaleFactor=Niveau de zoom (recadrage) d\u2019une page +scalePages.title=Ajuster la taille ou l’échelle +scalePages.header=Ajuster la taille ou l’échelle +scalePages.pageSize=Taille d’une page du document +scalePages.scaleFactor=Niveau de zoom (recadrage) d’une page scalePages.submit=Ajuster @@ -586,11 +625,11 @@ scalePages.submit=Ajuster certSign.title=Signer avec un certificat certSign.header=Signer avec un certificat (Travail en cours) certSign.selectPDF=PDF à signer -certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below. +certSign.jksNote=Note: Si votre type de certificat n’est pas listé ci-dessous, merci de le convertir en fichier Java Keystore (.jks) en utilisant l’outil en ligne de commande keytool. Puis choisissez l’option Fichier .jks ci-dessous. certSign.selectKey=Fichier de clé privée (format PKCS#8, peut être .pem ou .der) certSign.selectCert=Fichier de certificat (format X.509, peut être .pem ou .der) -certSign.selectP12=Fichier keystore de clés PKCS#12 (.p12 ou .pfx) (facultatif, s\u2019il n\u2019est fourni, il doit contenir votre clé privée et votre certificat) -certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore): +certSign.selectP12=Fichier keystore de clés PKCS#12 (.p12 ou .pfx) (facultatif, s’il n’est fourni, il doit contenir votre clé privée et votre certificat) +certSign.selectJKS=Sélectionner votre fichier Java Keystore File (.jks or .keystore): certSign.certType=Type de certificat certSign.password=Mot de passe keystore ou clé privée le cas échéant certSign.showSig=Afficher la signature @@ -604,16 +643,16 @@ certSign.submit=Signer removeBlanks.title=Supprimer les pages vierges removeBlanks.header=Supprimer les pages vierges removeBlanks.threshold=Seuil de blancheur des pixels -removeBlanks.thresholdDesc=Seuil pour déterminer à quel point un pixel blanc doit être blanc pour être classé comme «\u00a0blanc\u00a0» (0 = noir, 255 = blanc pur). +removeBlanks.thresholdDesc=Seuil pour déterminer à quel point un pixel blanc doit être blanc pour être classé comme « blanc » (0 = noir, 255 = blanc pur). removeBlanks.whitePercent=Pourcentage de blanc removeBlanks.whitePercentDesc=Pourcentage de la page qui doit contenir des pixels « blancs » à supprimer. removeBlanks.submit=Supprimer les pages vierges #removeAnnotations -removeAnnotations.title=Remove Annotations -removeAnnotations.header=Remove Annotations -removeAnnotations.submit=Remove +removeAnnotations.title=Supprimer les annotations +removeAnnotations.header=Supprimer les annotations +removeAnnotations.submit=Supprimer #compare @@ -623,6 +662,18 @@ compare.document.1=Document 1 compare.document.2=Document 2 compare.submit=Comparer +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Signer @@ -643,20 +694,21 @@ repair.submit=Réparer #flatten flatten.title=Rendre inerte flatten.header=Rendre inerte +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Rendre inerte #ScannerImageSplit ScannerImageSplit.selectText.1=Seuil de rotation -ScannerImageSplit.selectText.2=Définit l\u2019angle absolu minimum requis pour la rotation de l\u2019image (par défaut\u00a0: 10). +ScannerImageSplit.selectText.2=Définit l’angle absolu minimum requis pour la rotation de l’image (par défaut : 10). ScannerImageSplit.selectText.3=Tolérance -ScannerImageSplit.selectText.4=Détermine la plage de variation de couleur autour de la couleur d\u2019arrière-plan estimée (par défaut\u00a0: 20). +ScannerImageSplit.selectText.4=Détermine la plage de variation de couleur autour de la couleur d’arrière-plan estimée (par défaut : 20). ScannerImageSplit.selectText.5=Surface minimale -ScannerImageSplit.selectText.6=Définit la surface minimale pour une photo (par défaut\u00a0: 8\u202f000). +ScannerImageSplit.selectText.6=Définit la surface minimale pour une photo (par défaut : 8 000). ScannerImageSplit.selectText.7=Surface de contour minimale -ScannerImageSplit.selectText.8=Définit la surface de contour minimale pour une photo (par défaut\u00a0: 500). +ScannerImageSplit.selectText.8=Définit la surface de contour minimale pour une photo (par défaut : 500). ScannerImageSplit.selectText.9=Taille de la bordure -ScannerImageSplit.selectText.10=Définit la taille de la bordure ajoutée et supprimée pour éviter les bordures blanches dans la sortie (par défaut\u00a0: 1). +ScannerImageSplit.selectText.10=Définit la taille de la bordure ajoutée et supprimée pour éviter les bordures blanches dans la sortie (par défaut : 1). #OCR @@ -665,24 +717,24 @@ ocr.header=OCR (Reconnaissance optique de caractères) / Nettoyage des numérisa ocr.selectText.1=Langues à détecter dans le PDF (celles listées sont celles actuellement détectées) ocr.selectText.2=Produire un fichier texte contenant le texte détecté à côté du PDF ocr.selectText.3=Corriger les pages qui ont été numérisées à un angle oblique en les remettant en place -ocr.selectText.4=Nettoyer la page afin qu\u2019il soit moins probable que l\u2019OCR trouve du texte dans le bruit de fond, sans modifier la sortie -ocr.selectText.5=Nettoyer la page afin qu\u2019il soit moins probable que l\u2019OCR trouve du texte dans le bruit de fond, en modifiant la sortie -ocr.selectText.6=Ignorer les pages contenant du texte interactif, n\u2019analyser que les pages qui sont des images -ocr.selectText.7=Forcer l\u2019OCR, analyser chaque page et supprimer tous les éléments de texte d\u2019origine +ocr.selectText.4=Nettoyer la page afin qu’il soit moins probable que l’OCR trouve du texte dans le bruit de fond, sans modifier la sortie +ocr.selectText.5=Nettoyer la page afin qu’il soit moins probable que l’OCR trouve du texte dans le bruit de fond, en modifiant la sortie +ocr.selectText.6=Ignorer les pages contenant du texte interactif, n’analyser que les pages qui sont des images +ocr.selectText.7=Forcer l’OCR, analyser chaque page et supprimer tous les éléments de texte d’origine ocr.selectText.8=Normal (génère une erreur si le PDF contient du texte) ocr.selectText.9=Paramètres additionnels ocr.selectText.10=Mode OCR -ocr.selectText.11=Supprimer les images après l\u2019OCR (Supprime TOUTES les images, utile uniquement si elles font partie de l\u2019étape de conversion) +ocr.selectText.11=Supprimer les images après l’OCR (Supprime TOUTES les images, utile uniquement si elles font partie de l’étape de conversion) ocr.selectText.12=Type de rendu (avancé) -ocr.help=Veuillez lire cette documentation pour savoir comment utiliser l\u2019OCR pour d\u2019autres langues ou une utilisation hors Docker\u00a0: -ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour l\u2019OCR. +ocr.help=Veuillez lire cette documentation pour savoir comment utiliser l’OCR pour d’autres langues ou une utilisation hors Docker : +ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour l’OCR. ocr.submit=Traiter #extractImages extractImages.title=Extraire les images extractImages.header=Extraire les images -extractImages.selectText=Format d\u2019image dans lequel convertir les images extraites +extractImages.selectText=Format d’image dans lequel convertir les images extraites extractImages.submit=Extraire @@ -695,21 +747,21 @@ fileToPDF.submit=Convertir #compress -compress.title=Compresser -compress.header=Compresser -compress.credit=Ce service utilise Ghostscript pour la compression et l\u2019optimisation des PDF. -compress.selectText.1=Mode manuel \u2013 de 1 à 4 -compress.selectText.2=Niveau d\u2019optimisation +compress.title=Compresser un PDF +compress.header=Compresser un PDF (lorsque c’est possible!) +compress.credit=Ce service utilise Ghostscript pour la compression et l’optimisation des PDF. +compress.selectText.1=Mode manuel – de 1 à 4 +compress.selectText.2=Niveau d’optimisation compress.selectText.3=4 (terrible pour les images textuelles) -compress.selectText.4=Mode automatique \u2013 ajuste automatiquement la qualité pour obtenir le PDF à la taille exacte -compress.selectText.5=Taille PDF attendue (par exemple, 25\u202fMo, 10,8\u202fMo, 25\u202fKo) +compress.selectText.4=Mode automatique – ajuste automatiquement la qualité pour obtenir le PDF à la taille exacte +compress.selectText.5=Taille PDF attendue (par exemple, 25 MB, 10,8 MB, 25 KB) compress.submit=Compresser #Add image addImage.title=Ajouter une image addImage.header=Ajouter une image -addImage.everyPage=Toutes les pages\u00a0? +addImage.everyPage=Toutes les pages ? addImage.upload=Télécharger une image addImage.submit=Ajouter une image @@ -726,41 +778,54 @@ merge.submit=Fusionner pdfOrganiser.title=Organiser pdfOrganiser.header=Organiser les pages pdfOrganiser.submit=Organiser +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Outil multifonction PDF multiTool.header=Outil multifonction PDF +multiTool.uploadPrompts=Please Upload PDF #view pdf -viewPdf.title=View PDF -viewPdf.header=View PDF +viewPdf.title=Visualiser un PDF +viewPdf.header=Visualiser un PDF #pageRemover pageRemover.title=Supprimer des pages pageRemover.header=Supprimer des pages -pageRemover.pagesToDelete=Pages à supprimer (entrez une liste de numéros de pages séparés par des virgules)\u00a0: +pageRemover.pagesToDelete=Pages à supprimer (entrez une liste de numéros de pages séparés par des virgules) : pageRemover.submit=Supprimer les pages +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate rotate.title=Pivoter rotate.header=Pivoter -rotate.selectAngle=Angle de rotation (par multiples de 90\u202fdegrés) +rotate.selectAngle=Angle de rotation (par multiples de 90 degrés) rotate.submit=Pivoter -#merge +#split-pdfs split.title=Diviser split.header=Diviser split.desc.1=Les numéros que vous sélectionnez sont le numéro de page sur lequel vous souhaitez faire une division -split.desc.2=Ainsi, la sélection de 1,3,7-8 diviserait un document de 10 pages en 6 PDF distincts avec\u00a0: +split.desc.2=Ainsi, la sélection de 1,3,7-9 diviserait un document de 10 pages en 6 PDF distincts avec : split.desc.3=Document #1: Page 1 split.desc.4=Document #2: Page 2 et 3 -split.desc.5=Document #3: Page 4, 5 et 6 -split.desc.6=Document #4: Page 7 -split.desc.7=Document #5: Page 8 -split.desc.8=Document #6: Page 9 et 10 +split.desc.5=Document #3: Page 4, 5, 6 et 7 +split.desc.6=Document #4: Page 8 +split.desc.7=Document #5: Page 9 +split.desc.8=Document #6: Page 10 split.splitPages=Pages sur lesquelles diviser split.submit=Diviser @@ -769,9 +834,9 @@ split.submit=Diviser imageToPDF.title=Image en PDF imageToPDF.header=Image en PDF imageToPDF.submit=Convertir -imageToPDF.selectLabel=Options d\u2019ajustement de l\u2019image +imageToPDF.selectLabel=Options d’ajustement de l’image imageToPDF.fillPage=Remplir la page -imageToPDF.fitDocumentToImage=Ajuster la page à l\u2019image +imageToPDF.fitDocumentToImage=Ajuster la page à l’image imageToPDF.maintainAspectRatio=Maintenir les proportions imageToPDF.selectText.2=Rotation automatique du PDF imageToPDF.selectText.3=Logique multi-fichiers (uniquement activée si vous travaillez avec plusieurs images) @@ -782,14 +847,14 @@ imageToPDF.selectText.5=Convertir en PDF séparés #pdfToImage pdfToImage.title=Image en PDF pdfToImage.header=Image en PDF -pdfToImage.selectText=Format d\u2019image +pdfToImage.selectText=Format d’image pdfToImage.singleOrMultiple=Type de résultat pdfToImage.single=Une seule grande image pdfToImage.multi=Plusieurs images -pdfToImage.colorType=Type d\u2019impression +pdfToImage.colorType=Type d’impression pdfToImage.color=Couleur pdfToImage.grey=Niveaux de gris -pdfToImage.blackwhite=Noir et blanc (peut engendre une perde de données\u00a0!) +pdfToImage.blackwhite=Noir et blanc (peut engendrer une perte de données !) pdfToImage.submit=Convertir @@ -797,21 +862,21 @@ pdfToImage.submit=Convertir addPassword.title=Ajouter un mot de passe addPassword.header=Ajouter un mot de passe addPassword.selectText.1=PDF à chiffrer -addPassword.selectText.2=Mot de passe de l\u2019utilisateur +addPassword.selectText.2=Mot de passe de l’utilisateur addPassword.selectText.3=Longueur de la clé de chiffrement addPassword.selectText.4=Les valeurs plus élevées sont plus fortes, mais les valeurs plus faibles ont une meilleure compatibilité. addPassword.selectText.5=Autorisations à définir (utilisation recommandée avec le mot de passe du propriétaire) -addPassword.selectText.6=Empêcher l\u2019assemblage du document -addPassword.selectText.7=Empêcher l\u2019extraction de contenu -addPassword.selectText.8=Empêcher l\u2019extraction pour l\u2019accessibilité +addPassword.selectText.6=Empêcher l’assemblage du document +addPassword.selectText.7=Empêcher l’extraction de contenu +addPassword.selectText.8=Empêcher l’extraction pour l’accessibilité addPassword.selectText.9=Empêcher de remplir les formulaires addPassword.selectText.10=Empêcher la modification addPassword.selectText.11=Empêcher la modification des annotations -addPassword.selectText.12=Empêcher l\u2019impression -addPassword.selectText.13=Empêcher l\u2019impression des différents formats +addPassword.selectText.12=Empêcher l’impression +addPassword.selectText.13=Empêcher l’impression des différents formats addPassword.selectText.14=Mot de passe du propriétaire -addPassword.selectText.15=Restreint ce qui peut être fait avec le document une fois qu\u2019il est ouvert (non pris en charge par tous les lecteurs). -addPassword.selectText.16=Restreint l\u2019ouverture du document lui-même. +addPassword.selectText.15=Restreint ce qui peut être fait avec le document une fois qu’il est ouvert (non pris en charge par tous les lecteurs). +addPassword.selectText.16=Restreint l’ouverture du document lui-même. addPassword.submit=Chiffrer @@ -828,6 +893,8 @@ watermark.selectText.7=Opacité (de 0% à 100%) watermark.selectText.8=Type de filigrane watermark.selectText.9=Image du filigrane watermark.submit=Ajouter un filigrane +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -836,14 +903,14 @@ permissions.header=Modifier les permissions permissions.warning=Attention, pour que ces permissions soient immuables il est recommandé de les paramétrer avec un mot de passe via la page Ajouter un mot de passe. permissions.selectText.1=Sélectionnez le PDF permissions.selectText.2=Permissions à définir -permissions.selectText.3=Empêcher l\u2019assemblage du document -permissions.selectText.4=Empêcher l\u2019extraction de contenu -permissions.selectText.5=Empêcher l\u2019extraction pour l\u2019accessibilité +permissions.selectText.3=Empêcher l’assemblage du document +permissions.selectText.4=Empêcher l’extraction de contenu +permissions.selectText.5=Empêcher l’extraction pour l’accessibilité permissions.selectText.6=Empêcher de remplir les formulaires permissions.selectText.7=Empêcher la modification permissions.selectText.8=Empêcher la modification des annotations -permissions.selectText.9=Empêcher l\u2019impression -permissions.selectText.10=Empêcher l\u2019impression des différents formats +permissions.selectText.9=Empêcher l’impression +permissions.selectText.10=Empêcher l’impression des différents formats permissions.submit=Modifier @@ -868,7 +935,7 @@ changeMetadata.keywords=Mots clés changeMetadata.modDate=Date de modification (yyyy/MM/dd HH:mm:ss) changeMetadata.producer=Producteur changeMetadata.subject=Sujet -changeMetadata.trapped=Défoncé (technique d’impression) +changeMetadata.trapped=Recouvrement (technique d’impression) changeMetadata.selectText.4=Autres métadonnées changeMetadata.selectText.5=Ajouter une entrée de métadonnées personnalisée changeMetadata.submit=Modifier @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF en PDF/A pdfToPDFA.header=PDF en PDF/A pdfToPDFA.credit=Ce service utilise OCRmyPDF pour la conversion en PDF/A. pdfToPDFA.submit=Convertir +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Convertir #PDFToHTML PDFToHTML.title=PDF en HTML PDFToHTML.header=PDF en HTML -PDFToHTML.credit=Ce service utilise LibreOffice pour la conversion de fichiers. +PDFToHTML.credit=Ce service utilise pdftohtml pour la conversion de fichiers. PDFToHTML.submit=Convertir @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choisir la page pour en extraire le tableau PDFToCSV.submit=Extrait #split-by-size-or-count +split-by-size-or-count.title=Séparer le PDF par taille ou par nombre split-by-size-or-count.header=Séparer le PDF par taille ou par nombre split-by-size-or-count.type.label=Sélectionner le type de division split-by-size-or-count.type.size=Par taille @@ -936,19 +1006,19 @@ split-by-size-or-count.submit=Séparer #overlay-pdfs -overlay-pdfs.header=Overlay PDF Files +overlay-pdfs.header=Incrustation de PDF overlay-pdfs.baseFile.label=Sélectionner le fichier PDF de base overlay-pdfs.overlayFiles.label=Sélectionner les fichiers PDF à superposer -overlay-pdfs.mode.label=Select Overlay Mode -overlay-pdfs.mode.sequential=Sequential Overlay -overlay-pdfs.mode.interleaved=Interleaved Overlay +overlay-pdfs.mode.label=Sélectionner le mode d’incrustation +overlay-pdfs.mode.sequential=Superposition séquentielle +overlay-pdfs.mode.interleaved=Superposition entrelacée overlay-pdfs.mode.fixedRepeat=Superposition à répétition fixe overlay-pdfs.counts.label=Nombre de superpositions (pour le mode de répétition fixe) -overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1) -overlay-pdfs.position.label=Select Overlay Position +overlay-pdfs.counts.placeholder=Compteurs (séparés par des virgules, exemple : 2,3,1) +overlay-pdfs.position.label=Définir la position de l’incrustation overlay-pdfs.position.foreground=Premier plan overlay-pdfs.position.background=Arrière-plan -overlay-pdfs.submit=Submit +overlay-pdfs.submit=Soumettre #split-by-sections @@ -959,14 +1029,36 @@ split-by-sections.vertical.label=Divisions verticales split-by-sections.horizontal.placeholder=Saisir le nombre de divisions horizontales split-by-sections.vertical.placeholder=Entrer le nombre de divisions verticales split-by-sections.submit=Diviser le PDF +split-by-sections.merge=Fusionner en un seul PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses -licenses.nav=Licenses -licenses.title=3rd Party Licenses -licenses.header=3rd Party Licenses +licenses.nav=Licences +licenses.title=Licences tierces +licenses.header=Licences tierces licenses.module=Module licenses.version=Version -licenses.license=License +licenses.license=Licence +# error +error.sorry=Désolé pour ce problème ! +error.needHelp=Besoin d’aide / Vous avez trouvé un problème ? +error.contactTip=Si vous avez encore des problèmes, n’hésitez pas à nous contacter pour obtenir de l’aide. Vous pouvez soumettre un ticket sur notre page GitHub ou nous contacter via Discord : +error.404.head=404 - Page non trouvée | oups on s’est foiré ! +error.404.1=Nous ne parvenons pas à trouver la page que vous recherchez. +error.404.2=Quelque chose n’a pas fonctionné +error.github=Créer un ticket sur GitHub +error.showStack=Afficher la Stack Trace +error.copyStack=Copier la Stack Trace +error.githubSubmit=GitHub - Créer un ticket +error.discordSubmit=Discord - Poster un message de demande d’assistance + diff --git a/src/main/resources/messages_hi_IN.properties b/src/main/resources/messages_hi_IN.properties index b17af690..9bcddca1 100644 --- a/src/main/resources/messages_hi_IN.properties +++ b/src/main/resources/messages_hi_IN.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=सही false=गलत unknown=अज्ञात save=सहेजें +saveToBrowser=Save to Browser close=बंद करें filesSelected=फ़ाइलें चयनित हैं noFavourites=कोई पसंदीदा जोड़ा नहीं गया है +downloadComplete=Download Complete bored=बोर हो रहे हैं? alphabet=वर्णमाला downloadPdf=पीडीएफ़ डाउनलोड करें @@ -52,16 +54,34 @@ notAuthenticatedMessage=उपयोगकर्ता प्रमाणित userNotFoundMessage=उपयोगकर्ता नहीं मिला। incorrectPasswordMessage=वर्तमान पासवर्ड गलत है। usernameExistsMessage=नया उपयोगकर्ता नाम पहले से मौजूद है। +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता +downgradeCurrentUserLongMessage=मौजूदा यूज़र की भूमिका को डाउनग्रेड नहीं किया जा सकता। इसलिए, वर्तमान उपयोगकर्ता को नहीं दिखाया जाएगा। +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=सेटिंग्स ############# settings.title=सेटिंग्स settings.update=अपडेट उपलब्ध है +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=ऐप संस्करण: settings.downloadOption.title=डाउनलोड विकल्प चुनें (एकल फ़ाइल गैर-ज़िप डाउनलोड के लिए): settings.downloadOption.1=एक ही विंडो में खोलें @@ -102,12 +123,13 @@ settings.downloadOption.3=फ़ाइल डाउनलोड करें settings.zipThreshold=जब डाउनलोड की गई फ़ाइलों की संख्या सीमा से अधिक हो settings.signOut=साइन आउट settings.accountSettings=खाता सेटिंग्स - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=क्रेडेंशियल बदलें changeCreds.header=अपना खाता विवरण अपडेट करें -changeCreds.changeUserAndPassword=आप डिफ़ॉल्ट लॉगिन क्रेडेंशियल का उपयोग कर रहे हैं। कृपया एक नया पासवर्ड दर्ज करें (और यदि चाहें तो उपयोगकर्ता नाम) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=नया उपयोगकर्ता नाम changeCreds.oldPassword=वर्तमान पासवर्ड changeCreds.newPassword=नया पासवर्ड @@ -142,14 +164,18 @@ adminUserSettings.header=व्यवस्थापक उपयोगकर् adminUserSettings.admin=व्यवस्थापक adminUserSettings.user=उपयोगकर्ता adminUserSettings.addUser=नया उपयोगकर्ता जोड़ें +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=रोल्स adminUserSettings.role=रोल adminUserSettings.actions=क्रियाएँ adminUserSettings.apiUser=सीमित API उपयोगकर्ता +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=केवल वेब उपयोगकर्ता adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=उपयोगकर्ता को लॉगिन पर उपयोगकर्ता नाम/पासवर्ड बदलने के लिए मजबूर करें adminUserSettings.submit=उपयोगकर्ता को सहेजें +adminUserSettings.changeUserRole=यूज़र की भूमिका बदलें ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=साइन इन करें +login.header=साइन इन करें login.signin=साइन इन करें login.rememberme=मुझे याद रखें login.invalid=अमान्य उपयोगकर्ता नाम या पासवर्ड। login.locked=आपका खाता लॉक कर दिया गया है। login.signinTitle=कृपया साइन इन करें +login.ssoSignIn=सिंगल साइन - ऑन के ज़रिए लॉग इन करें +login.oauth2AutoCreateDisabled=OAUTH2 ऑटो - क्रिएट यूज़र अक्षम किया गया #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=एकल पृष्ठ में परिवर्त pageExtracter.title=पृष्ठों को निकालें pageExtracter.header=पृष्ठों को निकालें pageExtracter.submit=निकालें +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=दस्तावेज़ 1 compare.document.2=दस्तावेज़ 2 compare.submit=तुलना करें +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=हस्ताक्षर @@ -643,6 +694,7 @@ repair.submit=मरम्मत #flatten flatten.title=समतल करें flatten.header=पीडीएफ़ समतल करें +flatten.flattenOnlyForms=Flatten only forms flatten.submit=समतल करें @@ -726,11 +778,23 @@ merge.submit=मर्ज करें pdfOrganiser.title=पेज व्यवस्थापक pdfOrganiser.header=PDF पेज व्यवस्थापक pdfOrganiser.submit=पृष्ठों को पुनः व्यवस्थित करें +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=पीडीएफ मल्टी टूल multiTool.header=पीडीएफ मल्टी टूल +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=पीडीएफ देखें @@ -741,6 +805,7 @@ pageRemover.title=पेज हटाने वाला pageRemover.header=पीडीएफ पेज हटाने वाला pageRemover.pagesToDelete=हटाने के पेज (पृष्ठ संख्याओं की व्यवस्था के लिए एक कॉमा से अलग संख्याओं की सूची दर्ज करें): pageRemover.submit=पेज हटाएं +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=चुनें घुमाने का कोण (90 ड rotate.submit=घुमाएं -#merge +#split-pdfs split.title=पीडीएफ को विभाजित करें split.header=पीडीएफ को विभाजित करें split.desc.1=जिन नंबरों को आप चुनते हैं, वे पृष्ठ संख्या होती हैं जिन पर आप विभाजन करना चाहते हैं। -split.desc.2=इसलिए, 1,3,7-8 का चयन करना एक 10 पृष्ठों के दस्तावेज़ को 6 अलग-अलग पीडीएफ में विभाजित करेगा जैसे: +split.desc.2=इसलिए, 1,3,7-9 का चयन करना एक 10 पृष्ठों के दस्तावेज़ को 6 अलग-अलग पीडीएफ में विभाजित करेगा जैसे: split.desc.3=दस्तावेज़ #1: पृष्ठ 1 split.desc.4=दस्तावेज़ #2: पृष्ठ 2 और 3 -split.desc.5=दस्तावेज़ #3: पृष्ठ 4, 5 और 6 +split.desc.5=दस्तावेज़ #3: पृष्ठ 4, 5, 6 और 7 split.desc.6=दस्तावेज़ #4: पृष्ठ 7 split.desc.7=दस्तावेज़ #5: पृष्ठ 8 -split.desc.8=दस्तावेज़ #6: पृष्ठ 9 और 10 +split.desc.8=दस्तावेज़ #6: पृष्ठ 10 split.splitPages=विभाजन करने के लिए पृष्ठ दर्ज करें: split.submit=विभाजित करें @@ -828,6 +893,8 @@ watermark.selectText.7=अपारदर्शिता (0% - 100%): watermark.selectText.8=वॉटरमार्क प्रकार: watermark.selectText.9=वॉटरमार्क छवि: watermark.submit=वॉटरमार्क जोड़ें +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF से PDF/A में pdfToPDFA.header=PDF से PDF/A में pdfToPDFA.credit=इस सेवा में PDF/A परिवर्तन के लिए OCRmyPDF का उपयोग किया जाता है। pdfToPDFA.submit=परिवर्तित करें +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=परिवर्तित करें #PDFToHTML PDFToHTML.title=PDF से HTML PDFToHTML.header=PDF से HTML -PDFToHTML.credit=यह सेवा फ़ाइल परिवर्तन के लिए LibreOffice का उपयोग करती है। +PDFToHTML.credit=यह सेवा फ़ाइल परिवर्तन के लिए pdftohtml का उपयोग करती है। PDFToHTML.submit=परिवर्तित करें @@ -925,6 +994,7 @@ PDFToCSV.prompt=टेबल निकालने के लिए पृष् PDFToCSV.submit=निकालें #split-by-size-or-count +split-by-size-or-count.title=आकार या गणना द्वारा PDF को विभाजित करें split-by-size-or-count.header=आकार या गणना द्वारा PDF को विभाजित करें split-by-size-or-count.type.label=स्प्लिट प्रकार चुनें split-by-size-or-count.type.size=आकार द्वारा @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=लंबवत विभाजन split-by-sections.horizontal.placeholder=क्षैतिज विभाजन की संख्या दर्ज करें split-by-sections.vertical.placeholder=लंबवत विभाजन की संख्या दर्ज करें split-by-sections.submit=PDF को विभाजित करें +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_hu_HU.properties b/src/main/resources/messages_hu_HU.properties index 0fab7e30..625e8b23 100644 --- a/src/main/resources/messages_hu_HU.properties +++ b/src/main/resources/messages_hu_HU.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=Igaz false=Hamis unknown=Ismeretlen save=Mentés +saveToBrowser=Save to Browser close=Bezárás filesSelected=kiválasztott fájlok noFavourites=Nincs hozzáadva kedvenc +downloadComplete=Download Complete bored=Unatkozol? alphabet=Ábécé downloadPdf=PDF letöltése @@ -52,16 +54,34 @@ notAuthenticatedMessage=Felhasználó nincs hitelesítve. userNotFoundMessage=A felhasználó nem található. incorrectPasswordMessage=A jelenlegi jelszó helytelen. usernameExistsMessage=Az új felhasználónév már létezik. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=A jelenlegi felhasználó szerepkörét nem lehet visszaminősíteni +downgradeCurrentUserLongMessage=Az aktuális felhasználó szerepkörét nem lehet visszaminősíteni. Ezért az aktuális felhasználó nem jelenik meg. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Beállítások ############# settings.title=Beállítások settings.update=Frisítés elérhető +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=App Verzió: settings.downloadOption.title=Válassza ki a letöltési lehetőséget (Egyetlen fájl esetén a nem tömörített letöltésekhez): settings.downloadOption.1=Nyissa meg ugyanabban az ablakban @@ -102,12 +123,13 @@ settings.downloadOption.3=Töltse le a fájlt settings.zipThreshold=Fájlok tömörítése, ha a letöltött fájlok száma meghaladja settings.signOut=Kijelentkezés settings.accountSettings=Fiókbeállítások - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Hitelesítés megváltoztatása changeCreds.header=Frissítse fiókadatait -changeCreds.changeUserAndPassword=Alapértelmezett bejelentkezési adatokat használ. Adjon meg egy új jelszót (és felhasználónevet, ha szeretné) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=Új felhasználónév changeCreds.oldPassword=Jelenlegi jelszó changeCreds.newPassword=Új jelszó @@ -142,14 +164,18 @@ adminUserSettings.header=Adminisztrátori Felhasználói Vezérlési Beállítá adminUserSettings.admin=Adminisztrátor adminUserSettings.user=Felhasználó adminUserSettings.addUser=Új felhasználó hozzáadása +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Szerepek adminUserSettings.role=Szerep adminUserSettings.actions=Műveletek adminUserSettings.apiUser=Korlátozott API-felhasználó +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Csak webes felhasználó adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Kényszerítse a felhasználót a felhasználónév/jelszó megváltoztatására bejelentkezéskor adminUserSettings.submit=Felhasználó mentése +adminUserSettings.changeUserRole=Felhasználó szerepkörének módosítása ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Bejelentkezés +login.header=Bejelentkezés login.signin=Bejelentkezés login.rememberme=Emlékezz rám login.invalid=Érvénytelen felhasználónév vagy jelszó! login.locked=A fiókja zárolva lett! login.signinTitle=Kérjük, jelentkezzen be! +login.ssoSignIn=Bejelentkezés egyszeri bejelentkezéssel +login.oauth2AutoCreateDisabled=OAUTH2 Felhasználó automatikus létrehozása letiltva #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Átalakítás egyetlen oldallá pageExtracter.title=Oldalak kinyerése pageExtracter.header=Oldalak kinyerése pageExtracter.submit=Kinyerés +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Dokumentum 1 compare.document.2=Dokumentum 2 compare.submit=Összehasonlítás +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Aláírás @@ -643,6 +694,7 @@ repair.submit=Javítás #flatten flatten.title=Kiegyenlítés flatten.header=PDF-ek kiegyenlítése +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Kiegyenlítés @@ -726,11 +778,23 @@ merge.submit=Összevonás pdfOrganiser.title=Oldalszervező pdfOrganiser.header=PDF Oldalszervező pdfOrganiser.submit=Oldalak átrendezése +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF többfunkciós eszköz multiTool.header=PDF többfunkciós eszköz +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=PDF megtekintése @@ -741,6 +805,7 @@ pageRemover.title=Oldaltörlő pageRemover.header=PDF oldaltörlő pageRemover.pagesToDelete=Törlendő oldalak (adja meg az oldalszámok vesszővel elválasztott listáját): pageRemover.submit=Oldalak törlése +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Válassza ki a forgatási szöget (90 fok egész számú töb rotate.submit=Forgatás -#merge +#split-pdfs split.title=PDF szétválasztás split.header=PDF szétválasztás split.desc.1=A kiválasztott számok a szétválasztani kívánt oldalszámok -split.desc.2=Például az 1,3,7-8 kiválasztása egy 10 oldalas dokumentumot 6 különálló PDF-fé szétválaszt +split.desc.2=Például az 1,3,7-9 kiválasztása egy 10 oldalas dokumentumot 6 különálló PDF-fé szétválaszt split.desc.3=Dokumentum #1: Oldal 1 split.desc.4=Dokumentum #2: Oldal 2 és 3 -split.desc.5=Dokumentum #3: Oldal 4, 5 és 6 -split.desc.6=Dokumentum #4: Oldal 7 -split.desc.7=Dokumentum #5: Oldal 8 -split.desc.8=Dokumentum #6: Oldal 9 és 10 +split.desc.5=Dokumentum #3: Oldal 4, 5, 6 és 7 +split.desc.6=Dokumentum #4: Oldal 8 +split.desc.7=Dokumentum #5: Oldal 9 +split.desc.8=Dokumentum #6: Oldal 10 split.splitPages=Adja meg az oldalakat, amelyekre szét akarja választani: split.submit=Szétválasztás @@ -828,6 +893,8 @@ watermark.selectText.7=Átlátszóság (0% - 100%): watermark.selectText.8=Vízjel típusa: watermark.selectText.9=Vízjel képe: watermark.submit=Vízjel hozzáadása +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF >> PDF/A pdfToPDFA.header=PDF >> PDF/A pdfToPDFA.credit=Ez a szolgáltatás az OCRmyPDF-t használja a PDF/A konverzióhoz pdfToPDFA.submit=Konvertálás +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Konvertálás #PDFToHTML PDFToHTML.title=PDF >> HTML PDFToHTML.header=PDF >> HTML -PDFToHTML.credit=Ez a szolgáltatás a LibreOffice-t használja a fájlkonverzióhoz. +PDFToHTML.credit=Ez a szolgáltatás a pdftohtml-t használja a fájlkonverzióhoz. PDFToHTML.submit=Konvertálás @@ -925,6 +994,7 @@ PDFToCSV.prompt=Válassza ki az oldalt a táblázat kinyeréséhez PDFToCSV.submit=Kinyerés #split-by-size-or-count +split-by-size-or-count.title=PDF felosztása méret vagy oldalszám alapján split-by-size-or-count.header=PDF felosztása méret vagy oldalszám alapján split-by-size-or-count.type.label=Válassza ki a felosztás típusát split-by-size-or-count.type.size=Méret alapján @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vízszintes szakaszok split-by-sections.horizontal.placeholder=Adja meg a vízszintes szakaszok számát split-by-sections.vertical.placeholder=Adja meg a függőleges szakaszok számát split-by-sections.submit=Felosztás +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_id_ID.properties b/src/main/resources/messages_id_ID.properties index 4bbe247b..fd5c3e31 100644 --- a/src/main/resources/messages_id_ID.properties +++ b/src/main/resources/messages_id_ID.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl=right to left) @@ -17,9 +17,11 @@ true=Benar false=Salah unknown=Tidak diketahui save=Simpan +saveToBrowser=Save to Browser close=Tutup filesSelected=berkas dipilih noFavourites=Tidak ada favorit yang ditambahkan +downloadComplete=Download Complete bored=Bosan Menunggu? alphabet=Abjad downloadPdf=Unduh PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=Pengguna tidak ter-autentikasi. userNotFoundMessage=Pengguna tidak ditemukan. incorrectPasswordMessage=Kata sandi saat ini salah. usernameExistsMessage=Nama pengguna baru sudah ada. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Tidak dapat menurunkan peran pengguna saat ini +downgradeCurrentUserLongMessage=Tidak dapat menurunkan peran pengguna saat ini. Oleh karena itu, pengguna saat ini tidak akan ditampilkan. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Pengaturan ############# settings.title=Pengaturan settings.update=Pembaruan tersedia +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Versi Aplikasi: settings.downloadOption.title=Pilih opsi unduhan (Untuk unduhan berkas tunggal non zip): settings.downloadOption.1=Buka di jendela yang sama @@ -102,12 +123,13 @@ settings.downloadOption.3=Unduh berkas settings.zipThreshold=Berkas zip ketika jumlah berkas yang diunduh melebihi settings.signOut=Keluar settings.accountSettings=Pengaturan Akun - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Ubah Kredensial changeCreds.header=Perbarui Detail Akun Anda -changeCreds.changeUserAndPassword=Anda menggunakan kredensial masuk default. Masukkan kata sandi baru (dan nama pengguna jika diinginkan) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=Nama Pengguna Baru changeCreds.oldPassword=Kata Sandi Saat Ini changeCreds.newPassword=Kata Sandi Baru @@ -142,14 +164,18 @@ adminUserSettings.header=Pengaturan Kontrol Admin adminUserSettings.admin=Admin adminUserSettings.user=Pengguna adminUserSettings.addUser=Tambahkan Pengguna Baru +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Peran adminUserSettings.role=Peran adminUserSettings.actions=Tindakan adminUserSettings.apiUser=Pengguna API Terbatas +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Pengguna Khusus Web adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Memaksa pengguna untuk mengubah nama pengguna/kata sandi saat masuk adminUserSettings.submit=Simpan Pengguna +adminUserSettings.changeUserRole=Ubah Peran Pengguna ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Masuk +login.header=Masuk login.signin=Masuk login.rememberme=Ingat saya login.invalid=Nama pengguna atau kata sandi tidak valid. login.locked=Akun Anda telah dikunci. login.signinTitle=Silakan masuk +login.ssoSignIn=Masuk melalui Single Sign - on +login.oauth2AutoCreateDisabled=OAUTH2 Buat Otomatis Pengguna Dinonaktifkan #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Konversi ke Halaman Tunggal pageExtracter.title=Ekstrak Halaman pageExtracter.header=Ekstrak Halaman pageExtracter.submit=Ekstrak +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Dokumen 1 compare.document.2=Dokumen 2 compare.submit=Bandingkan +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Tanda @@ -643,6 +694,7 @@ repair.submit=Perbaiki #flatten flatten.title=Ratakan flatten.header=Ratakan PDF +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Ratakan @@ -726,11 +778,23 @@ merge.submit=Gabungkan pdfOrganiser.title=Pengaturan Halaman pdfOrganiser.header=Pengaturan Halaman PDF pdfOrganiser.submit=Susun ulang halaman +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Alat Multi PDF multiTool.header=Alat Multi PDF +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=Lihat PDF @@ -741,6 +805,7 @@ pageRemover.title=Penghapus Halaman pageRemover.header=Penghapus Halaman PDF pageRemover.pagesToDelete=Halaman yang akan dihapus (Masukkan daftar nomor halaman yang dipisahkan dengan koma) : pageRemover.submit=Hapus Halaman +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Pilih sudut rotasi (dalam kelipatan 90 derajat): rotate.submit=Rotasi -#merge +#split-pdfs split.title=Membagi PDF split.header=Membagi PDF split.desc.1=Angka yang Anda pilih adalah nomor halaman yang ingin Anda pisahkan -split.desc.2=Dengan demikian, memilih 1,3,7-8 akan membagi dokumen 10 halaman menjadi 6 PDF terpisah: +split.desc.2=Dengan demikian, memilih 1,3,7-9 akan membagi dokumen 10 halaman menjadi 6 PDF terpisah: split.desc.3=Dokumen #1: Halaman 1 split.desc.4=Dokumen #2: Halaman 2 dan 3 -split.desc.5=Dokumen #3: Halaman 4, 5 dan 6 -split.desc.6=Dokumen #4: Halaman 7 -split.desc.7=Dokumen #5: Halaman 8 -split.desc.8=Dokumen #6: Halaman 9 dan 10 +split.desc.5=Dokumen #3: Halaman 4, 5, 6 dan 7 +split.desc.6=Dokumen #4: Halaman 8 +split.desc.7=Dokumen #5: Halaman 9 +split.desc.8=Dokumen #6: Halaman 10 split.splitPages=Masukkan halaman yang akan dipisah: split.submit=Pisahkan @@ -828,6 +893,8 @@ watermark.selectText.7=Opacity (0% - 100%): watermark.selectText.8=Tipe Watermark: watermark.selectText.9=Gambar Watermark: watermark.submit=Tambahkan Watermark +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF Ke PDF/A pdfToPDFA.header=PDF ke PDF/A pdfToPDFA.credit=Layanan ini menggunakan OCRmyPDF untuk konversi PDF/A. pdfToPDFA.submit=Konversi +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Konversi #PDFToHTML PDFToHTML.title=PDF Ke HTML PDFToHTML.header=PDF ke HTML -PDFToHTML.credit=Layanan ini menggunakan LibreOffice untuk konversi berkas. +PDFToHTML.credit=Layanan ini menggunakan pdftohtml untuk konversi berkas. PDFToHTML.submit=Konversi @@ -925,6 +994,7 @@ PDFToCSV.prompt=Pilih halaman untuk mengambil tabel PDFToCSV.submit=Ektraksi #split-by-size-or-count +split-by-size-or-count.title=Pisahkan PDF berdasarkan ukuran atau jumlah split-by-size-or-count.header=Pisahkan PDF berdasarkan ukuran atau jumlah split-by-size-or-count.type.label=Pilih Tipe Split split-by-size-or-count.type.size=Berdasarkan Ukuran @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Pembagian Vertikal split-by-sections.horizontal.placeholder=Input angka untuk pembagian horizontal split-by-sections.vertical.placeholder=Input angka untuk pembagian vertikal split-by-sections.submit=Pisahkan PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_it_IT.properties b/src/main/resources/messages_it_IT.properties index 70793c70..e22addfe 100644 --- a/src/main/resources/messages_it_IT.properties +++ b/src/main/resources/messages_it_IT.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -11,15 +11,17 @@ imgPrompt=Scegli immagine/i genericSubmit=Invia processTimeWarning=Nota: Questo processo potrebbe richiedere fino a un minuto in base alla dimensione dei file pageOrderPrompt=Ordine delle pagine (inserisci una lista di numeri separati da virgola): -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : +pageSelectionPrompt=Selezione pagina personalizzata (inserisci un elenco separato da virgole di numeri di pagina 1,5,6 o funzioni come 2n+1) : goToPage=Vai true=Vero false=Falso unknown=Sconosciuto save=Salva +saveToBrowser=Salva nel browser close=Chiudi filesSelected=file selezionati noFavourites=Nessun preferito +downloadComplete=Download completo bored=Stanco di aspettare? alphabet=Alfabeto downloadPdf=Scarica PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=Utente non autenticato. userNotFoundMessage=Utente non trovato. incorrectPasswordMessage=La password attuale non è corretta. usernameExistsMessage=Il nuovo nome utente esiste già. +invalidUsernameMessage=Nome utente non valido, il nome utente deve contenere solo caratteri alfabetici e numeri. +deleteCurrentUserMessage=Impossibile eliminare l'utente attualmente connesso. +deleteUsernameExistsMessage=Il nome utente non esiste e non può essere eliminato. +downgradeCurrentUserMessage=Impossibile declassare il ruolo dell'utente corrente +downgradeCurrentUserLongMessage=Impossibile declassare il ruolo dell'utente corrente. Pertanto, l'utente corrente non verrà visualizzato. +error=Errore +oops=Oops! +help=Aiuto +goHomepage=Vai alla Homepage +joinDiscord=Unisciti al nostro server Discord +seeDockerHub=Vedi DockerHub +visitGithub=Visita il repository Github +donate=Donazione +color=Colore +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Menù pipeline (Beta) pipeline.uploadButton=Caricamento personalizzato pipeline.configureButton=Configura pipeline.defaultOption=Personalizzato pipeline.submitButton=Invia +pipeline.help=Aiuto sulla pipeline +pipeline.scanHelp=Aiuto per la scansione delle cartelle ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Impostazioni ############# settings.title=Impostazioni settings.update=Aggiornamento disponibile +settings.updateAvailable={0} è la versione attualmente installata. Una nuova versione ({1}) è disponibile. settings.appVersion=Versione App: settings.downloadOption.title=Scegli opzione di download (Per file singoli non compressi): settings.downloadOption.1=Apri in questa finestra @@ -102,16 +123,17 @@ settings.downloadOption.3=Scarica file settings.zipThreshold=Comprimi file in .zip quando il numero di download supera settings.signOut=Logout settings.accountSettings=Impostazioni Account - - +settings.bored.help=Abilita easter egg game +settings.cacheInputs.name=Salva gli input del modulo +settings.cacheInputs.help=Abilitare per memorizzare gli input utilizzati in precedenza per esecuzioni future changeCreds.title=Cambia credenziali changeCreds.header=Aggiorna i dettagli del tuo account -changeCreds.changeUserAndPassword=Stai utilizzando le credenziali di accesso predefinite. Inserisci una nuova password (e un nome utente se lo desideri) +changeCreds.changePassword=Stai utilizzando le credenziali di accesso predefinite. Inserisci una nuova password changeCreds.newUsername=Nuovo nome utente changeCreds.oldPassword=Password attuale changeCreds.newPassword=Nuova Password -changeCreds.confirmNewPassword=Conferma Nuova Password +changeCreds.confirmNewPassword=Conferma nuova Password changeCreds.submit=Invia modifiche @@ -142,14 +164,18 @@ adminUserSettings.header=Impostazioni di controllo utente amministratore adminUserSettings.admin=Amministratore adminUserSettings.user=Utente adminUserSettings.addUser=Aggiungi un nuovo Utente +adminUserSettings.usernameInfo=Il nome utente deve contenere solo lettere e numeri, senza spazi o caratteri speciali. adminUserSettings.roles=Ruoli adminUserSettings.role=Ruolo adminUserSettings.actions=Azioni adminUserSettings.apiUser=Utente API limitato +adminUserSettings.extraApiUser=API utente limitato aggiuntivo adminUserSettings.webOnlyUser=Utente solo Web adminUserSettings.demoUser=Utente demo (nessuna impostazione personalizzata) +adminUserSettings.internalApiUser=API utente interna adminUserSettings.forceChange=Forza l'utente a cambiare nome username/password all'accesso adminUserSettings.submit=Salva utente +adminUserSettings.changeUserRole=Cambia il ruolo dell'utente ############# # HOME-PAGE # @@ -168,7 +194,7 @@ multiTool.tags=Strumento multiplo,operazione multipla,interfaccia utente,trascin home.merge.title=Unisci home.merge.desc=Unisci facilmente più PDF in uno. -merge.tags=unione, operazioni sulla pagina, back end, lato server +merge.tags=unione,operazioni sulla pagina,back-end,lato server home.split.title=Dividi home.split.desc=Dividi un singolo PDF in più documenti. @@ -237,7 +263,7 @@ ocr.tags=riconoscimento,testo,immagine,scansione,lettura,identificazione,rilevam home.extractImages.title=Estrai immagini home.extractImages.desc=Estrai tutte le immagini da un PDF e salvale come zip. -extractImages.tags=immagine,photo,save,archive,zip,capture,grab +extractImages.tags=immagine,foto,salva,archivio,zip,catturare,prendere home.pdfToPDFA.title=Converti in PDF/A home.pdfToPDFA.desc=Converti un PDF nel formato PDF/A per archiviazione a lungo termine. @@ -367,7 +393,7 @@ showJS.tags=JS home.autoRedact.title=Redazione automatica home.autoRedact.desc=Redige automaticamente (oscura) il testo in un PDF in base al testo immesso -autoRedact.tags=Redact,Hide,black out,black,marker,hidden +autoRedact.tags=Redigere,nascondere,oscurare,nero,pennarello,nascosto home.tableExtraxt.title=Da PDF a CSV home.tableExtraxt.desc=Estrae tabelle da un PDF convertendolo in CSV @@ -385,11 +411,20 @@ overlay-pdfs.tags=Svrapponi home.split-by-sections.title=Dividi PDF per sezioni home.split-by-sections.desc=Dividi ciascuna pagina di un PDF in sezioni orizzontali e verticali più piccole -split-by-sections.tags=Dividi sezione, dividi, personalizza +split-by-sections.tags=Dividi sezione,dividi,personalizza home.AddStampRequest.title=Aggiungi timbro al PDF home.AddStampRequest.desc=Aggiungi testo o aggiungi timbri immagine nelle posizioni prestabilite -AddStampRequest.tags=Timbro, Aggiungi immagine, Centra immagine, Filigrana, PDF, Incorpora, Personalizza +AddStampRequest.tags=Timbro,Aggiungi immagine,Centra immagine,Filigrana,PDF,Incorpora,Personalizza + + +home.PDFToBook.title=PDF in libro +home.PDFToBook.desc=Converte PDF in formati libro/fumetto utilizzando Calibre +PDFToBook.tags=Libro,fumetto,calibre,conversione,manga,amazon,kindle + +home.BookToPDF.title=Libro in PDF +home.BookToPDF.desc=Converte i formati di libri/fumetti in PDF utilizzando Calibre +BookToPDF.tags=Libro,fumetto,calibre,conversione,manga,amazon,kindle ########################### @@ -399,11 +434,14 @@ AddStampRequest.tags=Timbro, Aggiungi immagine, Centra immagine, Filigrana, PDF, ########################### #login login.title=Accedi +login.header=Accedi login.signin=Accedi login.rememberme=Ricordami login.invalid=Nome utente o password errati. login.locked=Il tuo account è stato bloccato. login.signinTitle=Per favore accedi +login.ssoSignIn=Accedi tramite Single Sign-on +login.oauth2AutoCreateDisabled=Creazione automatica utente OAUTH2 DISABILITATA #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Converti in pagina singola pageExtracter.title=Estrai pagine pageExtracter.header=Estrai pagine pageExtracter.submit=Estrai +pageExtracter.placeholder=(es. 1,2,8 o 4,7,12-16 o 2n-1) #getPdfInfo @@ -612,7 +651,7 @@ removeBlanks.submit=Rimuovi #removeAnnotations removeAnnotations.title=Rimuovi Annotazioni -removeAnnotations.header=Remuovi Annotazioni +removeAnnotations.header=Rimuovi Annotazioni removeAnnotations.submit=Rimuovi @@ -623,6 +662,18 @@ compare.document.1=Documento 1 compare.document.2=Documento 2 compare.submit=Compara +#BookToPDF +BookToPDF.title=Libri e fumetti in PDF +BookToPDF.header=Libro in PDF +BookToPDF.credit=Utilizza Calibre +BookToPDF.submit=Converti + +#PDFToBook +PDFToBook.title=PDF in libro +PDFToBook.header=PDF in libro +PDFToBook.selectText.1=Formato +PDFToBook.credit=Utilizzo Calibre +PDFToBook.submit=Converti #sign sign.title=Firma @@ -641,8 +692,9 @@ repair.submit=Ripara #flatten -flatten.title=Appiattisci +flatten.title=Appiattire flatten.header=Appiattisci PDF +flatten.flattenOnlyForms=Appiattisci solo i moduli flatten.submit=Appiattisci @@ -726,11 +778,23 @@ merge.submit=Unisci pdfOrganiser.title=Organizza pagine pdfOrganiser.header=Organizza le pagine di un PDF pdfOrganiser.submit=Riordina pagine +pdfOrganiser.mode=Modalità +pdfOrganiser.mode.1=Ordine delle pagine personalizzato +pdfOrganiser.mode.2=Ordine inverso +pdfOrganiser.mode.3=Ordinamento fronte-retro +pdfOrganiser.mode.4=Ordinamento a libretto +pdfOrganiser.mode.5=Ordinamento libretto con cucitura laterale +pdfOrganiser.mode.6=Divisione pari-dispari +pdfOrganiser.mode.7=Rimuovi prima +pdfOrganiser.mode.8=Rimuovi ultima +pdfOrganiser.mode.9=Rimuovi la prima e l'ultima +pdfOrganiser.placeholder=(ad es. 1,3,2 o 4-8,2,10-12 o 2n-1) #multiTool multiTool.title=Multifunzione PDF multiTool.header=Multifunzione PDF +multiTool.uploadPrompts=Caricare il PDF #view pdf viewPdf.title=Visualizza PDF @@ -741,6 +805,7 @@ pageRemover.title=Rimuovi pagine pageRemover.header=Rimuovi pagine da un PDF pageRemover.pagesToDelete=Pagine da eliminare (inserisci una lista di numeri separati da virgola): pageRemover.submit=Rimuovi pagine +pageRemover.placeholder=(es. 1,2,6 o 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Scegli angolo di rotazione (in multipli di 90 gradi): rotate.submit=Ruota -#merge +#split-pdfs split.title=Dividi PDF split.header=Dividi PDF split.desc.1=I numeri che scegli sono le pagine a cui desideri dividere il documento -split.desc.2=Per esempio inserendo 1,3,7-8 separeresti un documento di 10 pagine in 6 diversi PDF con: +split.desc.2=Per esempio inserendo 1,3,7-9 separeresti un documento di 10 pagine in 6 diversi PDF con: split.desc.3=Documento #1: Pagina 1 split.desc.4=Documento #2: Pagine 2 e 3 -split.desc.5=Documento #3: Pagine 4, 5 e 6 -split.desc.6=Documento #4: Pagina 7 -split.desc.7=Documento #5: Pagina 8 -split.desc.8=Documento #6: Pagine 9 e 10 +split.desc.5=Documento #3: Pagine 4, 5, 6 e 7 +split.desc.6=Documento #4: Pagina 8 +split.desc.7=Documento #5: Pagina 9 +split.desc.8=Documento #6: Pagine 10 split.splitPages=Inserisci pagine a cui dividere: split.submit=Dividi @@ -828,6 +893,8 @@ watermark.selectText.7=Opacità (0% - 100%): watermark.selectText.8=Tipo di filigrana: watermark.selectText.9=Immagine filigrana: watermark.submit=Aggiungi Filigrana +watermark.type.1=Testo +watermark.type.2=Immagine #Change permissions @@ -868,10 +935,10 @@ changeMetadata.keywords=Parole chiave: changeMetadata.modDate=Data di modifica (yyyy/MM/dd HH:mm:ss): changeMetadata.producer=Produttore: changeMetadata.subject=Oggetto: -changeMetadata.trapped=Trapped: +changeMetadata.trapped=Recuperato: changeMetadata.selectText.4=Altre proprietà: changeMetadata.selectText.5=Aggiungi proprietà personalizzata: -changeMetadata.submit=Cambia Proprietà +changeMetadata.submit=Cambia proprietà #pdfToPDFA @@ -879,6 +946,8 @@ pdfToPDFA.title=Da PDF a PDF/A pdfToPDFA.header=Da PDF a PDF/A pdfToPDFA.credit=Questo servizio utilizza OCRmyPDF per la conversione in PDF/A. pdfToPDFA.submit=Converti +pdfToPDFA.tip=Attualmente non funziona per più input contemporaneamente +pdfToPDFA.outputFormat=Formato di output #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Converti #PDFToHTML PDFToHTML.title=Da PDF a HTML PDFToHTML.header=Da PDF a HTML -PDFToHTML.credit=Questo servizio utilizza LibreOffice per la conversione. +PDFToHTML.credit=Questo servizio utilizza pdftohtml per la conversione. PDFToHTML.submit=Converti @@ -925,6 +994,7 @@ PDFToCSV.prompt=Scegli la pagina per estrarre la tabella PDFToCSV.submit=Estrai #split-by-size-or-count +split-by-size-or-count.title=Dividi il PDF per dimensione o numero split-by-size-or-count.header=Dividi il PDF per dimensione o numero split-by-size-or-count.type.label=Seleziona il tipo di divisione split-by-size-or-count.type.size=Per dimensione @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Divisioni verticali split-by-sections.horizontal.placeholder=Inserire il numero di divisioni orizzontali split-by-sections.vertical.placeholder=Inserire il numero di divisioni verticali split-by-sections.submit=Dividi PDF +split-by-sections.merge=Unisci in un unico PDF + + +#printFile +printFile.title=Stampa file +printFile.header=Stampa file su stampante +printFile.selectText.1=Seleziona file da stampare +printFile.selectText.2=Inserire il nome della stampante +printFile.submit=Stampare #licenses @@ -970,3 +1049,16 @@ licenses.version=Versione licenses.license=Licenza +# error +error.sorry=Ci scusiamo per il problema! +error.needHelp=Hai bisogno di aiuto / trovato un problema? +error.contactTip=Se i problemi persistono, non esitare a contattarci per chiedere aiuto. Puoi inviare un ticket sulla nostra pagina GitHub o contattarci tramite Discord: +error.404.head=404 - Pagina non trovata | Spiacenti, siamo inciampati nel codice! +error.404.1=Non riusciamo a trovare la pagina che stai cercando. +error.404.2=Qualcosa è andato storto +error.github=Invia un ticket su GitHub +error.showStack=Mostra traccia dello stack +error.copyStack=Copia traccia dello stack +error.githubSubmit=GitHub: invia un ticket +error.discordSubmit=Discord: invia post di supporto + diff --git a/src/main/resources/messages_ja_JP.properties b/src/main/resources/messages_ja_JP.properties index fb794c81..9f981333 100644 --- a/src/main/resources/messages_ja_JP.properties +++ b/src/main/resources/messages_ja_JP.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -11,17 +11,19 @@ imgPrompt=画像を選択 genericSubmit=送信 processTimeWarning=警告:この処理はファイルサイズによって1分程度かかることがあります pageOrderPrompt=ページ順序 (ページ番号をカンマ区切り又は2n+1のような関数で入力): -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : +pageSelectionPrompt=カスタムページ選択(ページ番号1、5、6または2n + 1などの関数のコンマ区切りリストを入力します): goToPage=移動 -true=True -false=False +true=真 +false=偽 unknown=不明 save=保存 +saveToBrowser=ブラウザへ保存 close=閉じる filesSelected=選択されたファイル noFavourites=お気に入りはありません +downloadComplete=ダウンロード完了 bored=待ち時間が退屈 -alphabet=\u30A2\u30EB\u30D5\u30A1\u30D9\u30C3\u30C8 +alphabet=アルファベット downloadPdf=PDFをダウンロード text=テキスト font=フォント @@ -43,25 +45,43 @@ red=赤 green=緑 blue=青 custom=カスタム... -WorkInProgess=Work in progress, May not work or be buggy, Please report any problems! +WorkInProgess=作業中です。動作しないまたはバグがある可能性があります。問題があれば報告してください! poweredBy=Powered by -yes=Yes -no=No +yes=はい +no=いいえ changedCredsMessage=資格情報が変更されました! notAuthenticatedMessage=ユーザーが認証されていません。 userNotFoundMessage=ユーザーが見つかりません。 incorrectPasswordMessage=現在のパスワードが正しくありません。 usernameExistsMessage=新しいユーザー名はすでに存在します。 +invalidUsernameMessage=ユーザー名が無効です。ユーザー名にはアルファベットと数字のみを使用してください。 +deleteCurrentUserMessage=現在ログインしているユーザーは削除できません。 +deleteUsernameExistsMessage=そのユーザー名は存在しないため削除できません。 +downgradeCurrentUserMessage=現在のユーザーの役割をダウングレードできません +downgradeCurrentUserLongMessage=現在のユーザーの役割をダウングレードできません。したがって、現在のユーザーは表示されません。 +error=エラー +oops=おっと! +help=ヘルプ +goHomepage=ホームページへ移動 +joinDiscord=Discordサーバーに参加する +seeDockerHub=Docker Hubを見る +visitGithub=Githubリポジトリを訪問する +donate=寄付する +color=色 +sponsor=スポンサー + ############### # Pipeline # ############### pipeline.header=パイプラインメニュー (Alpha) -pipeline.uploadButton=Upload Custom +pipeline.uploadButton=カスタムのアップロード pipeline.configureButton=設定 pipeline.defaultOption=カスタム pipeline.submitButton=送信 +pipeline.help=パイプラインのヘルプ +pipeline.scanHelp=フォルダ スキャンのヘルプ ###################### # Pipeline Options # @@ -70,7 +90,7 @@ pipelineOptions.header=パイプライン設定 pipelineOptions.pipelineNameLabel=パイプライン名 pipelineOptions.saveSettings=動作設定の保存 pipelineOptions.pipelineNamePrompt=ここにパイプライン名を入力 -pipelineOptions.selectOperation=Select Operation +pipelineOptions.selectOperation=動作の選択 pipelineOptions.addOperationButton=動作の追加 pipelineOptions.pipelineHeader=パイプライン: pipelineOptions.saveButton=ダウンロード @@ -94,6 +114,7 @@ navbar.settings=設定 ############# settings.title=設定 settings.update=利用可能なアップデート +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Appバージョン: settings.downloadOption.title=ダウンロードオプション (zip以外の単一ファイル): settings.downloadOption.1=同じウィンドウで開く @@ -102,12 +123,13 @@ settings.downloadOption.3=ファイルをダウンロード settings.zipThreshold=このファイル数を超えたときにファイルを圧縮する settings.signOut=サインアウト settings.accountSettings=アカウント設定 - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=資格情報の変更 changeCreds.header=アカウントの詳細を更新する -changeCreds.changeUserAndPassword=デフォルトのログイン認証情報を使用しています。新しいパスワード (必要に応じてユーザー名も) を入力してください +changeCreds.changePassword=デフォルトのログイン認証情報を使用しています。新しいパスワードを入力してください changeCreds.newUsername=新しいユーザー名 changeCreds.oldPassword=現在のパスワード changeCreds.newPassword=新しいパスワード @@ -142,14 +164,18 @@ adminUserSettings.header=管理者ユーザー制御設定 adminUserSettings.admin=管理者 adminUserSettings.user=ユーザー adminUserSettings.addUser=新しいユーザを追加 +adminUserSettings.usernameInfo=ユーザー名には文字と数字のみが使用でき、スペースや特殊文字は使用できません。 adminUserSettings.roles=役割 adminUserSettings.role=役割 adminUserSettings.actions=アクション adminUserSettings.apiUser=限定されたAPIユーザー +adminUserSettings.extraApiUser=追加の制限付きAPIユーザー adminUserSettings.webOnlyUser=ウェブ専用ユーザー -adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.demoUser=デモユーザー (カスタム設定なし) +adminUserSettings.internalApiUser=内部APIユーザー adminUserSettings.forceChange=ログイン時にユーザー名/パスワードを強制的に変更する adminUserSettings.submit=ユーザーの保存 +adminUserSettings.changeUserRole=ユーザーの役割を変更する ############# # HOME-PAGE # @@ -158,7 +184,7 @@ home.desc=PDFのあらゆるニーズに対応するローカルホスティン home.searchBar=機能検索... -home.viewPdf.title=View PDF +home.viewPdf.title=PDFを表示 home.viewPdf.desc=表示、注釈、テキストや画像の追加 viewPdf.tags=view,read,annotate,text,image @@ -387,11 +413,20 @@ home.split-by-sections.title=PDFをセクションで分割 home.split-by-sections.desc=PDFの各ページを縦横に分割します。 split-by-sections.tags=Section Split, Divide, Customize -home.AddStampRequest.title=Add Stamp to PDF -home.AddStampRequest.desc=Add text or add image stamps at set locations +home.AddStampRequest.title=PDFにスタンプを追加 +home.AddStampRequest.desc=設定した位置にテキストや画像のスタンプを追加できます AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDFを書籍に変換 +home.PDFToBook.desc=calibreを使用してPDFを書籍/コミック形式に変換します +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=PDFを書籍に変換 +home.BookToPDF.desc=calibreを使用してPDFを書籍/コミック形式に変換します +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=サインイン +login.header=サインイン login.signin=サインイン login.rememberme=サインイン状態を記憶する login.invalid=ユーザー名かパスワードが無効です。 login.locked=あなたのアカウントはロックされています。 login.signinTitle=サインインしてください +login.ssoSignIn=シングルサインオンでログイン +login.oauth2AutoCreateDisabled=OAuth 2自動作成ユーザーが無効 #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=単一ページに変換 pageExtracter.title=ページの抽出 pageExtracter.header=ページの抽出 pageExtracter.submit=抽出 +pageExtracter.placeholder=(例:1,2,8、4,7,12-16、2n-1) #getPdfInfo @@ -467,37 +506,37 @@ HTMLToPDF.header=HTMLをPDFに変換 HTMLToPDF.help=HTMLファイルと必要なhtml/css/画像などを含むZIPを受け入れます HTMLToPDF.submit=変換 HTMLToPDF.credit=WeasyPrintを使用 -HTMLToPDF.zoom=Zoom level for displaying the website. -HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default) -HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default) -HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default) -HTMLToPDF.printBackground=Render the background of websites. -HTMLToPDF.defaultHeader=Enable Default Header (Name and page number) -HTMLToPDF.cssMediaType=Change the CSS media type of the page. -HTMLToPDF.none=None -HTMLToPDF.print=Print -HTMLToPDF.screen=Screen +HTMLToPDF.zoom=Webサイトを表示するためのズームレベル。 +HTMLToPDF.pageWidth=ページ幅 (cm)。 (デフォルトでは空白) +HTMLToPDF.pageHeight=ページ高さ (cm)。 (デフォルトでは空白) +HTMLToPDF.marginTop=ページ上の余白 (mm)。 (デフォルトでは空白) +HTMLToPDF.marginBottom=ページ下の余白 (mm)。 (デフォルトでは空白) +HTMLToPDF.marginLeft=ページ左の余白 (mm)。 (デフォルトでは空白) +HTMLToPDF.marginRight=ページ右の余白 (mm)。 (デフォルトでは空白) +HTMLToPDF.printBackground=Webサイトの背景をレンダリングします。 +HTMLToPDF.defaultHeader=デフォルトのヘッダー (名前とページ番号) を有効にする +HTMLToPDF.cssMediaType=ページのCSSメディアタイプを変更します。 +HTMLToPDF.none=なし +HTMLToPDF.print=印刷 +HTMLToPDF.screen=画面 #AddStampRequest -AddStampRequest.header=Stamp PDF -AddStampRequest.title=Stamp PDF -AddStampRequest.stampType=Stamp Type -AddStampRequest.stampText=Stamp Text -AddStampRequest.stampImage=Stamp Image -AddStampRequest.alphabet=Alphabet -AddStampRequest.fontSize=Font/Image Size -AddStampRequest.rotation=Rotation -AddStampRequest.opacity=Opacity -AddStampRequest.position=Position -AddStampRequest.overrideX=Override X Coordinate -AddStampRequest.overrideY=Override Y Coordinate -AddStampRequest.customMargin=Custom Margin -AddStampRequest.customColor=Custom Text Color -AddStampRequest.submit=Submit +AddStampRequest.header=PDFにスタンプを押す +AddStampRequest.title=PDFにスタンプを押す +AddStampRequest.stampType=スタンプの種類 +AddStampRequest.stampText=スタンプする文章 +AddStampRequest.stampImage=スタンプする画像 +AddStampRequest.alphabet=文字 +AddStampRequest.fontSize=フォント/画像 サイズ +AddStampRequest.rotation=回転 +AddStampRequest.opacity=不透明度 +AddStampRequest.position=位置 +AddStampRequest.overrideX=X座標のオーバーライド +AddStampRequest.overrideY=Y座標のオーバーライド +AddStampRequest.customMargin=余白のカスタム +AddStampRequest.customColor=文字色のカスタム +AddStampRequest.submit=送信 #sanitizePDF @@ -522,7 +561,7 @@ addPageNumbers.selectText.5=番号をつけるページ addPageNumbers.selectText.6=カスタムテキスト addPageNumbers.customTextDesc=カスタムテキスト addPageNumbers.numberPagesDesc=番号をつけるページ、デフォルトは'all'、 1-5 や 2,5,9 など -addPageNumbers.customNumberDesc=デフォルトは{n}、'{n} / {total} ページ'、'テキスト-{n}'、'{filename}-{n}など +addPageNumbers.customNumberDesc=デフォルトは{n}、'{n} / {total} ページ'、'テキスト-{n}'、'{filename}-{n}など' addPageNumbers.submit=ページ番号の追加 @@ -570,7 +609,7 @@ pipeline.title=パイプライン pageLayout.title=マルチページレイアウト pageLayout.header=マルチページレイアウト pageLayout.pagesPerSheet=1枚あたりのページ数: -pageLayout.addBorder=Add Borders +pageLayout.addBorder=境界線を追加 pageLayout.submit=送信 @@ -586,11 +625,11 @@ scalePages.submit=送信 certSign.title=証明書による署名 certSign.header=証明書を使用してPDFに署名します。 (制作中) certSign.selectPDF=署名するPDFファイルを選択: -certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below. +certSign.jksNote=注: 証明書のタイプが以下にリストされていない場合は、keytoolコマンドラインツールを使用して証明書をJavaキーストア(.jks)ファイルに変換してください。次に以下の.jksファイル オプションを選択します。 certSign.selectKey=秘密キーファイルを選択 (PKCS#8形式、.pemまたは.der) : certSign.selectCert=証明書ファイルを選択 (X.509形式、.pemまたは.der) : certSign.selectP12=PKCS#12キーストアファイルを選択 (.p12または.pfx) (オプション。指定する場合は秘密キーと証明書が含まれている必要があります。): -certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore): +certSign.selectJKS=Javaキーストアファイルを選択 (.jks or .keystore): certSign.certType=証明書の種類 certSign.password=キーストアまたは秘密キーのパスワードを入力 (ある場合) : certSign.showSig=署名を表示 @@ -613,7 +652,7 @@ removeBlanks.submit=空白ページの削除 #removeAnnotations removeAnnotations.title=注釈の削除 removeAnnotations.header=注釈の削除 -removeAnnotations.submit=Remove +removeAnnotations.submit=削除 #compare @@ -623,6 +662,18 @@ compare.document.1=ドキュメント 1 compare.document.2=ドキュメント 2 compare.submit=比較 +#BookToPDF +BookToPDF.title=書籍やコミックをPDFに変換 +BookToPDF.header=書籍をPDFに変換 +BookToPDF.credit=calibreを使用 +BookToPDF.submit=変換 + +#PDFToBook +PDFToBook.title=書籍をPDFに変換 +PDFToBook.header=書籍をPDFに変換 +PDFToBook.selectText.1=フォーマット +PDFToBook.credit=calibreを使用 +PDFToBook.submit=変換 #sign sign.title=署名 @@ -643,6 +694,7 @@ repair.submit=修復 #flatten flatten.title=平坦化 flatten.header=PDFを平坦化する +flatten.flattenOnlyForms=Flatten only forms flatten.submit=平坦化 @@ -726,21 +778,34 @@ merge.submit=結合 pdfOrganiser.title=整理 pdfOrganiser.header=PDFページの整理 pdfOrganiser.submit=ページの整理 +pdfOrganiser.mode=モード +pdfOrganiser.mode.1=カスタムページ順序 +pdfOrganiser.mode.2=逆順 +pdfOrganiser.mode.3=デュプレックスソート +pdfOrganiser.mode.4=小冊子ソート +pdfOrganiser.mode.5=サイドステッチ小冊子ソート +pdfOrganiser.mode.6=奇数-偶数分割 +pdfOrganiser.mode.7=最初に削除 +pdfOrganiser.mode.8=最後を削除 +pdfOrganiser.mode.9=最初と最後を削除 +pdfOrganiser.placeholder=(例:1,3,2または4-8,2,10-12または2n-1) #multiTool multiTool.title=PDFマルチツール multiTool.header=PDFマルチツール +multiTool.uploadPrompts=Please Upload PDF #view pdf -viewPdf.title=View PDF -viewPdf.header=View PDF +viewPdf.title=PDFを表示 +viewPdf.header=PDFを表示 #pageRemover pageRemover.title=ページ削除 pageRemover.header=PDFページ削除 pageRemover.pagesToDelete=削除するページ (ページ番号のカンマ区切りリストを入力してください): pageRemover.submit=ページ削除 +pageRemover.placeholder=(例:1,2,6または1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=回転角度を選択 (90度の倍数): rotate.submit=回転 -#merge +#split-pdfs split.title=PDFの分割 split.header=PDFの分割 split.desc.1=選択する番号は分割するページ番号です。 -split.desc.2=したがって、1,3,7-8を選択すると、10ページのドキュメントが以下のように6つのPDFに分割されることになります。 +split.desc.2=したがって、1,3,7-9を選択すると、10ページのドキュメントが以下のように6つのPDFに分割されることになります。 split.desc.3=ドキュメント #1: ページ 1 split.desc.4=ドキュメント #2: ページ 2, 3 -split.desc.5=ドキュメント #3: ページ 4, 5, 6 -split.desc.6=ドキュメント #4: ページ 7 -split.desc.7=ドキュメント #5: ページ 8 -split.desc.8=ドキュメント #6: ページ 9, 10 +split.desc.5=ドキュメント #3: ページ 4, 5, 6, 7 +split.desc.6=ドキュメント #4: ページ 8 +split.desc.7=ドキュメント #5: ページ 9 +split.desc.8=ドキュメント #6: ページ 10 split.splitPages=分割するページ番号を入力: split.submit=分割 @@ -825,9 +890,11 @@ watermark.selectText.4=回転 (0-360): watermark.selectText.5=幅スペース (各透かし間の水平方向のスペース): watermark.selectText.6=高さスペース (各透かし間の垂直方向のスペース): watermark.selectText.7=不透明度 (0% - 100%): -watermark.selectText.8=Watermark Type: -watermark.selectText.9=Watermark Image: +watermark.selectText.8=透かしの種類: +watermark.selectText.9=透かしの画像: watermark.submit=透かしを追加 +watermark.type.1=テキスト +watermark.type.2=画像 #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDFをPDF/Aに変換 pdfToPDFA.header=PDFをPDF/Aに変換 pdfToPDFA.credit=本サービスはPDF/Aの変換にOCRmyPDFを使用しています。 pdfToPDFA.submit=変換 +pdfToPDFA.tip=現在、一度に複数の入力に対して機能しません +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=変換 #PDFToHTML PDFToHTML.title=PDFをHTMLに変換 PDFToHTML.header=PDFをHTMLに変換 -PDFToHTML.credit=本サービスはファイル変換にLibreOfficeを使用しています。 +PDFToHTML.credit=本サービスはファイル変換にpdftohtmlを使用しています。 PDFToHTML.submit=変換 @@ -925,6 +994,7 @@ PDFToCSV.prompt=表を抽出するページを選択 PDFToCSV.submit=変換 #split-by-size-or-count +split-by-size-or-count.title=サイズまたは数で分割 split-by-size-or-count.header=サイズまたは数で分割 split-by-size-or-count.type.label=分割タイプの選択 split-by-size-or-count.type.size=サイズ @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=垂直方向 split-by-sections.horizontal.placeholder=水平方向の分割数を選択 split-by-sections.vertical.placeholder=垂直方向の分割数を選択 split-by-sections.submit=分割 +split-by-sections.merge=1 つの PDF に結合するかどうか + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -969,3 +1048,17 @@ licenses.module=モジュール licenses.version=バージョン licenses.license=ライセンス + +# error +error.sorry=問題が発生したことをお詫び申し上げます! +error.needHelp=助けが必要/問題が見つかりましたか? +error.contactTip=まだ問題が解決していない場合は、お手数ですが、GitHubページでチケットを提出するか、Discordで私たちに連絡してください: +error.404.head=404 - ページが見つかりません | おっと、コードでつまずきました! +error.404.1=あなたが探しているページが見つかりません。 +error.404.2=何か問題が発生しました +error.github=GitHubでチケットを提出 +error.showStack=スタックトレースを表示 +error.copyStack=スタックトレースをコピー +error.githubSubmit=GitHub - チケットを提出 +error.discordSubmit=Discord - サポート投稿を提出 + diff --git a/src/main/resources/messages_ko_KR.properties b/src/main/resources/messages_ko_KR.properties index a016e6b3..da011ec6 100644 --- a/src/main/resources/messages_ko_KR.properties +++ b/src/main/resources/messages_ko_KR.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -11,70 +11,90 @@ imgPrompt=이미지 선택 genericSubmit=확인 processTimeWarning=경고: 파일 크기에 따라 1분 정도 소요될 수 있습니다 pageOrderPrompt=페이지 순서(쉼표로 구분된 페이지 번호 목록 입력): -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : +pageSelectionPrompt=사용자 지정 페이지 선택(페이지 번호 1,5,6 또는 2n+1과 같은 기능을 쉼표로 구분하여 목록 입력): goToPage=이동 true=참 false=거짓 unknown=알 수 없음 save=저장 +saveToBrowser=브라우저에 저장 close=닫기 filesSelected=개 파일 선택됨 noFavourites=즐겨찾기 없음 +downloadComplete=다운로드 완료 bored=기다리는 게 지루하신가요? -alphabet=\uC54C\uD30C\uBCB3 +alphabet=알파벳 downloadPdf=PDF 다운로드 text=텍스트 font=폰트 selectFillter=-- 선택 -- pageNum=페이지 번호 -sizes.small=Small -sizes.medium=Medium -sizes.large=Large -sizes.x-large=X-Large +sizes.small=작은 크기 +sizes.medium=중간 사이즈 +sizes.large=큰 사이즈 +sizes.x-large=초대형 사이즈 error.pdfPassword=이 PDF는 비밀번호로 보호되어 있으며, 비밀번호를 입력하지 않았거나, 입력된 비밀번호가 올바르지 않습니다. delete=삭제 username=사용자명 password=비밀번호 welcome=환영합니다. -property=Property -black=Black -white=White -red=Red -green=Green -blue=Blue -custom=Custom... -WorkInProgess=Work in progress, May not work or be buggy, Please report any problems! +property=재산 +black=검정 +white=하얀 +red=빨강 +green=녹색 +blue=파랑 +custom=관습... +WorkInProgess=작업 진행 중, 작동하지 않거나 버그가 있을 수 있음, 문제가 있으면 보고하십시오! poweredBy=Powered by -yes=Yes -no=No +yes=예 +no=아니요 changedCredsMessage=계정 정보 변경 성공! -notAuthenticatedMessage=User not authenticated. +notAuthenticatedMessage=사용자가 인증되지 않았습니다. userNotFoundMessage=사용자를 찾을 수 없습니다. incorrectPasswordMessage=현재 비밀번호가 틀립니다. usernameExistsMessage=새 사용자명이 이미 존재합니다. +invalidUsernameMessage=사용자 이름이 잘못되었습니다. 사용자 이름에는 알파벳 문자와 숫자만 포함되어야 합니다. +deleteCurrentUserMessage=현재 로그인한 사용자를 삭제할 수 없습니다. +deleteUsernameExistsMessage=사용자 이름이 존재하지 않으며 삭제할 수 없습니다. +downgradeCurrentUserMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다 +downgradeCurrentUserLongMessage=현재 사용자의 역할을 다운그레이드할 수 없습니다. 따라서 현재 사용자는 표시되지 않습니다. +error=오류 +oops=어머나! +help=도움말 +goHomepage=홈페이지로 이동 +joinDiscord=Discord 서버에 참여하기 +seeDockerHub=Docker Hub에서 확인하기 +visitGithub=GitHub 저장소 방문하기 +donate=기부하기 +color=색상 +sponsor=스폰서 + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) -pipeline.uploadButton=Upload Custom -pipeline.configureButton=Configure -pipeline.defaultOption=Custom -pipeline.submitButton=Submit +pipeline.header=파이프라인 메뉴 (Beta) +pipeline.uploadButton=사용자 지정 업로드 +pipeline.configureButton=구성 +pipeline.defaultOption=관습 +pipeline.submitButton=전송 +pipeline.help=파이프라인 도움말 +pipeline.scanHelp=폴더 스캔 도움말 ###################### # Pipeline Options # ###################### -pipelineOptions.header=Pipeline Configuration -pipelineOptions.pipelineNameLabel=Pipeline Name -pipelineOptions.saveSettings=Save Operation Settings -pipelineOptions.pipelineNamePrompt=Enter pipeline name here -pipelineOptions.selectOperation=Select Operation -pipelineOptions.addOperationButton=Add operation -pipelineOptions.pipelineHeader=Pipeline: -pipelineOptions.saveButton=Download -pipelineOptions.validateButton=Validate +pipelineOptions.header=파이프라인 구성 +pipelineOptions.pipelineNameLabel=파이프라인 이름 +pipelineOptions.saveSettings=작업 설정 저장 +pipelineOptions.pipelineNamePrompt=여기에 파이프라인 이름을 입력합니다. +pipelineOptions.selectOperation=작업 선택 +pipelineOptions.addOperationButton=작업 추가 +pipelineOptions.pipelineHeader=파이프라인: +pipelineOptions.saveButton=다운로드 +pipelineOptions.validateButton=확인 @@ -94,6 +114,7 @@ navbar.settings=설정 ############# settings.title=설정 settings.update=업데이트 가능 +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=앱 버전: settings.downloadOption.title=다운로드 옵션 선택 (zip 파일이 아닌 단일 파일 다운로드 시): settings.downloadOption.1=현재 창에서 열기 @@ -102,12 +123,13 @@ settings.downloadOption.3=다운로드 settings.zipThreshold=다운로드한 파일 수가 초과된 경우 파일 압축하기 settings.signOut=로그아웃 settings.accountSettings=계정 설정 - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=계정 정보 변경 changeCreds.header=계정 정보 업데이트 -changeCreds.changeUserAndPassword=기본 제공된 로그인 정보를 사용하고 있습니다. 새 비밀번호를 입력합니다. (필요하다면 사용자명을 변경할 수 있습니다.) +changeCreds.changePassword=기본 로그인 자격 증명을 사용하고 있습니다. 새 비밀번호를 입력해 주세요. changeCreds.newUsername=새 사용자명 changeCreds.oldPassword=현재 비밀번호 changeCreds.newPassword=새 비밀번호 @@ -119,20 +141,20 @@ changeCreds.submit=변경 account.title=계정 설정 account.accountSettings=계정 설정 account.adminSettings=관리자 설정 - 사용자 추가 및 확인 -account.userControlSettings=User Control Settings +account.userControlSettings=사용자 컨트롤 설정 account.changeUsername=사용자명 변경 account.newUsername=새 사용자 이름 -account.password=Confirmation Password +account.password=확인 비밀번호 account.oldPassword=이전 비밀번호 account.newPassword=새 비밀번호 account.changePassword=비밀번호 변경 account.confirmNewPassword=새 비밀번호 확인 account.signOut=로그아웃 account.yourApiKey=API 키 -account.syncTitle=Sync browser settings with Account -account.settingsCompare=Settings Comparison: -account.property=Property -account.webBrowserSettings=Web Browser Setting +account.syncTitle=브라우저 설정을 계정과 동기화 +account.settingsCompare=설정 비교: +account.property=재산 +account.webBrowserSettings=웹 브라우저 설정 account.syncToBrowser=계정 -> 브라우저로 동기화 account.syncToAccount=브라우저 -> 계정으로 동기화 @@ -142,14 +164,18 @@ adminUserSettings.header=사용자 관리 adminUserSettings.admin=관리자 adminUserSettings.user=사용자 adminUserSettings.addUser=새 사용자 추가 +adminUserSettings.usernameInfo=사용자 이름은 문자와 숫자만 포함해야 하며 공백이나 특수 문자는 포함할 수 없습니다. adminUserSettings.roles=역할 adminUserSettings.role=역할 adminUserSettings.actions=동작 adminUserSettings.apiUser=제한된 API 사용 +adminUserSettings.extraApiUser=제한된 API 사용자 추가 adminUserSettings.webOnlyUser=웹 사용만 허용 -adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.demoUser=데모 사용자(사용자 지정 설정 없음) +adminUserSettings.internalApiUser=내부 API 사용자 adminUserSettings.forceChange=다음 로그인 때 사용자명과 비밀번호를 변경하도록 강제 adminUserSettings.submit=사용자 저장 +adminUserSettings.changeUserRole=사용자의 역할 변경 ############# # HOME-PAGE # @@ -179,11 +205,11 @@ home.rotate.desc=PDF 페이지를 회전합니다. rotate.tags=server side -home.imageToPdf.title=Image to PDF +home.imageToPdf.title=이미지를 PDF로 home.imageToPdf.desc=이미지(PNG, JPEG, GIF)를 PDF 문서로 변환합니다. imageToPdf.tags=conversion,img,jpg,picture,photo -home.pdfToImage.title=PDF to Image +home.pdfToImage.title=PDF를 이미지로 home.pdfToImage.desc=PDF 문서을 이미지(PNG, JPEG, GIF)로 변환합니다. pdfToImage.tags=conversion,img,jpg,picture,photo @@ -239,15 +265,15 @@ home.extractImages.title=이미지 추출 home.extractImages.desc=PDF에서 모든 이미지를 추출하여 zip으로 저장합니다. extractImages.tags=picture,photo,save,archive,zip,capture,grab -home.pdfToPDFA.title=PDF to PDF/A +home.pdfToPDFA.title=PDF를 PDF/A로 변환 home.pdfToPDFA.desc=장기 보관을 위해 PDF 문서를 PDF/A 문서로 변환합니다. pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation -home.PDFToWord.title=PDF to Word +home.PDFToWord.title=PDF를 Word로 home.PDFToWord.desc=PDF 문서를 Word 형식으로 변환합니다. (DOC, DOCX, ODT) PDFToWord.tags=doc,docx,odt,word,transformation,format,conversion,office,microsoft,docfile -home.PDFToPresentation.title=PDF to Presentation +home.PDFToPresentation.title=PDF를 프리젠테이션으로 home.PDFToPresentation.desc=PDF 문서를 프리젠테이션 형식으로 변환합니다. (PPT, PPTX, ODP) PDFToPresentation.tags=slides,show,office,microsoft @@ -255,12 +281,12 @@ home.PDFToText.title=PDF to 텍스트/RTF home.PDFToText.desc=PDF 문서를 텍스트 또는 RTF 형식으로 변환합니다. PDFToText.tags=richformat,richtextformat,rich text format -home.PDFToHTML.title=PDF to HTML +home.PDFToHTML.title=PDF를 HTML로 home.PDFToHTML.desc=PDF 문서를 HTML 형식으로 변환합니다. PDFToHTML.tags=web content,browser friendly -home.PDFToXML.title=PDF to XML +home.PDFToXML.title=PDF를 XML로 변환 home.PDFToXML.desc=PDF 문서를 XML 형식으로 변환합니다. PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert @@ -284,8 +310,8 @@ home.removeBlanks.title=빈 페이지 제거 home.removeBlanks.desc=PDF 문서에서 빈 페이지를 감지하고 제거합니다. removeBlanks.tags=cleanup,streamline,non-content,organize -home.removeAnnotations.title=Remove Annotations -home.removeAnnotations.desc=Removes all comments/annotations from a PDF +home.removeAnnotations.title=주석 제거 +home.removeAnnotations.desc=PDF에서 모든 주석/주석을 제거합니다. removeAnnotations.tags=comments,highlight,notes,markup,remove home.compare.title=비교 @@ -332,16 +358,16 @@ home.sanitizePdf.title=정제 home.sanitizePdf.desc=PDF 문서에서 스크립트와 같은 요소들을 제거합니다. sanitizePdf.tags=clean,secure,safe,remove-threats -home.URLToPDF.title=URL/Website To PDF +home.URLToPDF.title=URL/웹사이트를 PDF로 home.URLToPDF.desc=http(s) 웹사이트를 PDF 문서로 변환합니다. URLToPDF.tags=web-capture,save-page,web-to-doc,archive -home.HTMLToPDF.title=HTML to PDF +home.HTMLToPDF.title=HTML에서 PDF로 home.HTMLToPDF.desc=HTML 파일, 또는 ZIP 파일을 PDF로 변환합니다. HTMLToPDF.tags=markup,web-content,transformation,convert -home.MarkdownToPDF.title=Markdown to PDF +home.MarkdownToPDF.title=Markdown에서 PDF로 home.MarkdownToPDF.desc=마크다운 파일을 PDF 문서로 변환합니다. MarkdownToPDF.tags=markup,web-content,transformation,convert @@ -369,29 +395,38 @@ home.autoRedact.title=자동 검열 home.autoRedact.desc=PDF 문서에서 입력된 텍스트들을 자동으로 검열(모자이크)합니다. autoRedact.tags=Redact,Hide,black out,black,marker,hidden -home.tableExtraxt.title=PDF to CSV -home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV +home.tableExtraxt.title=PDF에서 CSV로 +home.tableExtraxt.desc=PDF에서 표를 추출하여 CSV로 변환 tableExtraxt.tags=CSV,Table Extraction,extract,convert -home.autoSizeSplitPDF.title=Auto Split by Size/Count -home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count +home.autoSizeSplitPDF.title=크기/개수로 자동 분할 +home.autoSizeSplitPDF.desc=단일 PDF를 크기, 페이지 수 또는 문서 수에 따라 여러 문서로 분할 autoSizeSplitPDF.tags=pdf,split,document,organization -home.overlay-pdfs.title=Overlay PDFs -home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF +home.overlay-pdfs.title=PDF 오버레이 +home.overlay-pdfs.desc=PDF를 다른 PDF 위에 오버레이 overlay-pdfs.tags=Overlay -home.split-by-sections.title=Split PDF by Sections -home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections +home.split-by-sections.title=섹션별로 PDF 분할 +home.split-by-sections.desc=PDF의 각 페이지를 더 작은 가로와 세로 구역으로 나눕니다 split-by-sections.tags=Section Split, Divide, Customize -home.AddStampRequest.title=Add Stamp to PDF -home.AddStampRequest.desc=Add text or add image stamps at set locations +home.AddStampRequest.title=PDF에 스탬프 추가 +home.AddStampRequest.desc=설정된 위치에 텍스트 추가 또는 이미지 스탬프 추가 AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF를 책으로 +home.PDFToBook.desc=구경을 사용하여 PDF를 책/만화 형식으로 변환 +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=책을 PDF로 +home.BookToPDF.desc=구경을 사용하여 책/만화 형식을 PDF로 변환 +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=로그인 +login.header=로그인 login.signin=로그인 login.rememberme=로그인 유지 login.invalid=사용자 이름이나 비밀번호가 틀립니다. login.locked=계정이 잠겼습니다. login.signinTitle=로그인해 주세요. +login.ssoSignIn=싱글사인온을 통한 로그인 +login.oauth2AutoCreateDisabled=OAUTH2 사용자 자동 생성 비활성화됨 #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=단일 페이지로 통합 pageExtracter.title=페이지 추출 pageExtracter.header=페이지 추출 pageExtracter.submit=추출 +pageExtracter.placeholder=(예: 1,2,8 또는 4,7,12-16 또는 2n-1) #getPdfInfo @@ -446,7 +485,7 @@ getPdfInfo.downloadJson=JSON으로 다운로드 #markdown-to-pdf -MarkdownToPDF.title=Markdown To PDF +MarkdownToPDF.title=Markdown에서 PDF로 MarkdownToPDF.header=Markdown 문서를 PDF 문서로 변환 MarkdownToPDF.submit=변환 MarkdownToPDF.help=변환중 @@ -455,49 +494,49 @@ MarkdownToPDF.credit=이 기능은 WeasyPrint를 사용합니다. #url-to-pdf -URLToPDF.title=URL To PDF +URLToPDF.title=URL을 PDF로 URLToPDF.header=URL을 PDF 문서로 변환 URLToPDF.submit=변환 URLToPDF.credit=이 기능은 WeasyPrint를 사용합니다. #html-to-pdf -HTMLToPDF.title=HTML To PDF +HTMLToPDF.title=HTML을 PDF로 HTMLToPDF.header=HTML 파일을 PDF 문서로 변환 HTMLToPDF.help=HTML 파일, 또는 html/css/이미지 등을 포함한 ZIP 파일을 받습니다. HTMLToPDF.submit=변환 HTMLToPDF.credit=이 기능은 WeasyPrint를 사용합니다. -HTMLToPDF.zoom=Zoom level for displaying the website. -HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default) -HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default) -HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default) -HTMLToPDF.printBackground=Render the background of websites. -HTMLToPDF.defaultHeader=Enable Default Header (Name and page number) -HTMLToPDF.cssMediaType=Change the CSS media type of the page. -HTMLToPDF.none=None -HTMLToPDF.print=Print -HTMLToPDF.screen=Screen +HTMLToPDF.zoom=웹 사이트를 표시하기 위한 확대/축소 수준입니다. +HTMLToPDF.pageWidth=센티미터 단위의 페이지 너비입니다. (기본값은 비어 있음) +HTMLToPDF.pageHeight=페이지 높이(센티미터)입니다. (기본값은 비어 있음) +HTMLToPDF.marginTop=페이지의 위쪽 여백(밀리미터)입니다. (기본값은 비어 있음) +HTMLToPDF.marginBottom=페이지의 아래쪽 여백(밀리미터)입니다. (기본값은 비어 있음) +HTMLToPDF.marginLeft=페이지의 왼쪽 여백(밀리미터)입니다. (기본값은 비어 있음) +HTMLToPDF.marginRight=페이지의 오른쪽 여백(밀리미터)입니다. (기본값은 비어 있음) +HTMLToPDF.printBackground=웹 사이트의 배경을 렌더링합니다. +HTMLToPDF.defaultHeader=기본 헤더 활성화(이름 및 페이지 번호) +HTMLToPDF.cssMediaType=페이지의 CSS 미디어 유형을 변경합니다. +HTMLToPDF.none=없음 +HTMLToPDF.print=인쇄하다 +HTMLToPDF.screen=화면 #AddStampRequest -AddStampRequest.header=Stamp PDF -AddStampRequest.title=Stamp PDF -AddStampRequest.stampType=Stamp Type -AddStampRequest.stampText=Stamp Text -AddStampRequest.stampImage=Stamp Image -AddStampRequest.alphabet=Alphabet -AddStampRequest.fontSize=Font/Image Size -AddStampRequest.rotation=Rotation -AddStampRequest.opacity=Opacity -AddStampRequest.position=Position -AddStampRequest.overrideX=Override X Coordinate -AddStampRequest.overrideY=Override Y Coordinate -AddStampRequest.customMargin=Custom Margin -AddStampRequest.customColor=Custom Text Color -AddStampRequest.submit=Submit +AddStampRequest.header=스탬프 PDF +AddStampRequest.title=스탬프 PDF +AddStampRequest.stampType=스탬프 유형 +AddStampRequest.stampText=스탬프 텍스트 +AddStampRequest.stampImage=스탬프 이미지 +AddStampRequest.alphabet=알파벳 +AddStampRequest.fontSize=글꼴/이미지 크기 +AddStampRequest.rotation=회전 +AddStampRequest.opacity=불투명도 +AddStampRequest.position=위치 +AddStampRequest.overrideX=X 좌표 재정의 +AddStampRequest.overrideY=Y 좌표 재정의 +AddStampRequest.customMargin=맞춤 마진 +AddStampRequest.customColor=사용자 정의 텍스트 색상 +AddStampRequest.submit=전송 #sanitizePDF @@ -586,11 +625,11 @@ scalePages.submit=제출 certSign.title=인증서로 서명 certSign.header=인증서로 PDF 문서에 서명 (개발 중) certSign.selectPDF=서명할 PDF 문서를 선택합니다: -certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below. +certSign.jksNote=참고: 인증서 유형이 아래에 나열되지 않은 경우 keytool 명령줄 도구를 사용하여 Java 키 저장소(.jks) 파일로 변환하십시오. 그런 다음 아래의 .jks 파일 옵션을 선택합니다. certSign.selectKey=개인 키 파일을 선택합니다 (PKCS#8 형식, .pem 또는 .der): certSign.selectCert=인증서 파일을 선택합니다 (X.509 형식, .pem 또는 .der): certSign.selectP12=PKCS#12 키 저장소 파일을 선택합니다 (.p12 or .pfx) (선택 사항, 선택할 경우, 개인 키와 인증서를 포함하고 있어야 합니다): -certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore): +certSign.selectJKS=Java 키 저장소 파일(.jks 또는 .keystore)을 선택합니다. certSign.certType=인증서 유형 certSign.password=키 저장소 또는 개인 키 비밀번호를 입력합니다 (있는 경우): certSign.showSig=서명 보기 @@ -611,9 +650,9 @@ removeBlanks.submit=빈 페이지 제거 #removeAnnotations -removeAnnotations.title=Remove Annotations -removeAnnotations.header=Remove Annotations -removeAnnotations.submit=Remove +removeAnnotations.title=주석 제거 +removeAnnotations.header=주석 제거 +removeAnnotations.submit=제거하다 #compare @@ -623,6 +662,18 @@ compare.document.1=문서 1 compare.document.2=문서 2 compare.submit=비교 +#BookToPDF +BookToPDF.title=책과 만화를 PDF로 +BookToPDF.header=책을 PDF로 +BookToPDF.credit=이 서비스는 파일 변환을 위해 Calibre 사용합니다. +BookToPDF.submit=변환 + +#PDFToBook +PDFToBook.title=PDF를 책으로 +PDFToBook.header=PDF를 책으로 +PDFToBook.selectText.1=판 +PDFToBook.credit=이 서비스는 파일 변환을 위해 Calibre 사용합니다. +PDFToBook.submit=변환 #sign sign.title=서명 @@ -643,6 +694,7 @@ repair.submit=복구 #flatten flatten.title=평탄화 flatten.header=PDF 문서의 레이어 평탄화 +flatten.flattenOnlyForms=Flatten only forms flatten.submit=평탄화 @@ -717,8 +769,8 @@ addImage.submit=이미지 추가 #merge merge.title=병합 merge.header=여러 개의 PDF 병합 (2개 이상) -merge.sortByName=Sort by name -merge.sortByDate=Sort by date +merge.sortByName=이름순 정렬 +merge.sortByDate=날짜순 정렬 merge.submit=병합 @@ -726,11 +778,23 @@ merge.submit=병합 pdfOrganiser.title=페이지 정렬 pdfOrganiser.header=PDF 페이지 정렬 pdfOrganiser.submit=페이지 재정렬 +pdfOrganiser.mode=모드 +pdfOrganiser.mode.1=사용자 지정 페이지 순서 +pdfOrganiser.mode.2=역순 +pdfOrganiser.mode.3=양면 정렬(Duplex Sort) +pdfOrganiser.mode.4=소책자 정렬 +pdfOrganiser.mode.5=사이드 스티치 소책자 정렬 +pdfOrganiser.mode.6=홀수-짝수 분할 +pdfOrganiser.mode.7=첫 번째 항목 삭제 +pdfOrganiser.mode.8=마지막 항목 제거 +pdfOrganiser.mode.9=첫 번째와 마지막 제거 +pdfOrganiser.placeholder=(예: 1,3,2 또는 4-8,2,10-12 또는 2n-1) #multiTool multiTool.title=PDF 멀티툴 multiTool.header=PDF 멀티툴 +multiTool.uploadPrompts=PDF를 업로드하십시오 #view pdf viewPdf.title=PDF 뷰어 @@ -741,6 +805,7 @@ pageRemover.title=페이지 제거 pageRemover.header=PDF 페이지 제거 pageRemover.pagesToDelete=제거할 페이지 (쉼표로 구분된 페이지 번호 입력): pageRemover.submit=페이지 제거 +pageRemover.placeholder=(예: 1,2,6 또는 1-10,15-30) #rotate @@ -750,23 +815,23 @@ rotate.selectAngle=회전 각도 선택 (90도의 배수로): rotate.submit=회전 -#merge +#split-pdfs split.title=PDF 분할 split.header=PDF 분할 split.desc.1=입력한 번호는 분할할 페이지의 번호입니다. -split.desc.2=예를 들어, 1,3,7-8을 입력하면 10페이지 문서를 아래와 같이 6개의 별도의 PDF 문서로 분할하게 됩니다. +split.desc.2=예를 들어, 1,3,7-9을 입력하면 10페이지 문서를 아래와 같이 6개의 별도의 PDF 문서로 분할하게 됩니다. split.desc.3=문서 #1: 페이지 1 split.desc.4=문서 #2: 페이지 2, 3 -split.desc.5=문서 #3: 페이지 4, 5, 6 -split.desc.6=문서 #4: 페이지 7 -split.desc.7=문서 #5: 페이지 8 -split.desc.8=문서 #6: 페이지 9, 10 +split.desc.5=문서 #3: 페이지 4, 5, 6, 7 +split.desc.6=문서 #4: 페이지 8 +split.desc.7=문서 #5: 페이지 9 +split.desc.8=문서 #6: 페이지 10 split.splitPages=분할할 페이지 입력: split.submit=분할 #merge -imageToPDF.title=Image to PDF +imageToPDF.title=이미지를 PDF로 imageToPDF.header=이미지를 PDF로 변환 imageToPDF.submit=변환 imageToPDF.selectLabel=이미지 맞춤 방법 @@ -780,7 +845,7 @@ imageToPDF.selectText.5=별도의 PDF로 변환 #pdfToImage -pdfToImage.title=PDF to Image +pdfToImage.title=PDF를 이미지로 pdfToImage.header=PDF 문서를 이미지로 변환 pdfToImage.selectText=이미지 형식 pdfToImage.singleOrMultiple=이미지 결과 유형 @@ -828,6 +893,8 @@ watermark.selectText.7=투명도 (0% - 100%): watermark.selectText.8=워터마크 유형: watermark.selectText.9=워터마크 이미지: watermark.submit=워터마크 추가 +watermark.type.1=텍스트 +watermark.type.2=이미지 #Change permissions @@ -875,14 +942,16 @@ changeMetadata.submit=변경 #pdfToPDFA -pdfToPDFA.title=PDF To PDF/A +pdfToPDFA.title=PDF를 PDF/A로 pdfToPDFA.header=PDF 문서를 PDF/A로 변환 pdfToPDFA.credit=이 서비스는 PDF/A 변환을 위해 OCRmyPDF 문서를 사용합니다. pdfToPDFA.submit=변환 +pdfToPDFA.tip=현재 한 번에 여러 입력에 대해 작동하지 않습니다. +pdfToPDFA.outputFormat=Output format #PDFToWord -PDFToWord.title=PDF to Word +PDFToWord.title=PDF를 Word로 PDFToWord.header=PDF 문서를 Word 문서로 변환 PDFToWord.selectText.1=출력 파일 형식 PDFToWord.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다. @@ -890,7 +959,7 @@ PDFToWord.submit=변환 #PDFToPresentation -PDFToPresentation.title=PDF to Presentation +PDFToPresentation.title=PDF를 프리젠테이션으로 PDFToPresentation.header=PDF 문서를 프레젠테이션으로 변환 PDFToPresentation.selectText.1=출력 파일 형식 PDFToPresentation.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다. @@ -898,7 +967,7 @@ PDFToPresentation.submit=변환 #PDFToText -PDFToText.title=PDF to RTF +PDFToText.title=PDF에서 RTF로 PDFToText.header=PDF 문서를 RTF(서식 있는 텍스트 문서)로 변환 PDFToText.selectText.1=출력 파일 형식 PDFToText.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다. @@ -906,67 +975,90 @@ PDFToText.submit=변환 #PDFToHTML -PDFToHTML.title=PDF to HTML +PDFToHTML.title=PDF를 HTML로 PDFToHTML.header=PDF 문서를 HTML로 변환 -PDFToHTML.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다. +PDFToHTML.credit=이 서비스는 파일 변환을 위해 pdftohtml를 사용합니다. PDFToHTML.submit=변환 #PDFToXML -PDFToXML.title=PDF to XML +PDFToXML.title=PDF를 XML로 변환 PDFToXML.header=PDF 문서를 XML로 변환 PDFToXML.credit=이 서비스는 파일 변환을 위해 LibreOffice를 사용합니다. PDFToXML.submit=변환 #PDFToCSV -PDFToCSV.title=PDF? CSV? -PDFToCSV.header=PDF? CSV? -PDFToCSV.prompt=Choose page to extract table -PDFToCSV.submit=?? +PDFToCSV.title=PDF에서 CSV로 +PDFToCSV.header=PDF에서 CSV로 +PDFToCSV.prompt=테이블을 추출할 페이지 선택 +PDFToCSV.submit=추출물 #split-by-size-or-count -split-by-size-or-count.header=Split PDF by Size or Count -split-by-size-or-count.type.label=Select Split Type -split-by-size-or-count.type.size=By Size -split-by-size-or-count.type.pageCount=By Page Count -split-by-size-or-count.type.docCount=By Document Count -split-by-size-or-count.value.label=Enter Value -split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5) -split-by-size-or-count.submit=Submit +split-by-size-or-count.title=크기 또는 개수로 PDF 분할 +split-by-size-or-count.header=크기 또는 개수로 PDF 분할 +split-by-size-or-count.type.label=분할 유형 선택 +split-by-size-or-count.type.size=크기별 +split-by-size-or-count.type.pageCount=페이지 수별 +split-by-size-or-count.type.docCount=문서 수 기준 +split-by-size-or-count.value.label=값 입력 +split-by-size-or-count.value.placeholder=크기(예: 2MB 또는 3KB) 또는 개수(예: 5)를 입력합니다. +split-by-size-or-count.submit=전송 #overlay-pdfs -overlay-pdfs.header=Overlay PDF Files -overlay-pdfs.baseFile.label=Select Base PDF File -overlay-pdfs.overlayFiles.label=Select Overlay PDF Files -overlay-pdfs.mode.label=Select Overlay Mode -overlay-pdfs.mode.sequential=Sequential Overlay -overlay-pdfs.mode.interleaved=Interleaved Overlay -overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay -overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode) -overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1) -overlay-pdfs.position.label=Select Overlay Position -overlay-pdfs.position.foreground=Foreground -overlay-pdfs.position.background=Background -overlay-pdfs.submit=Submit +overlay-pdfs.header=PDF 파일 오버레이 +overlay-pdfs.baseFile.label=기본 PDF 파일 선택 +overlay-pdfs.overlayFiles.label=오버레이 PDF 파일 선택 +overlay-pdfs.mode.label=오버레이 모드 선택 +overlay-pdfs.mode.sequential=순차 오버레이 +overlay-pdfs.mode.interleaved=인터리브 오버레이 +overlay-pdfs.mode.fixedRepeat=고정 반복 오버레이 +overlay-pdfs.counts.label=오버레이 카운트(고정 반복 모드의 경우) +overlay-pdfs.counts.placeholder=쉼표로 구분된 개수를 입력합니다(예: 2,3,1). +overlay-pdfs.position.label=오버레이 위치 선택 +overlay-pdfs.position.foreground=전경 +overlay-pdfs.position.background=배경 +overlay-pdfs.submit=전송 #split-by-sections -split-by-sections.title=Split PDF by Sections -split-by-sections.header=Split PDF into Sections -split-by-sections.horizontal.label=Horizontal Divisions -split-by-sections.vertical.label=Vertical Divisions -split-by-sections.horizontal.placeholder=Enter number of horizontal divisions -split-by-sections.vertical.placeholder=Enter number of vertical divisions -split-by-sections.submit=Split PDF +split-by-sections.title=섹션별로 PDF 분할 +split-by-sections.header=PDF를 섹션으로 분할 +split-by-sections.horizontal.label=수평 분할 +split-by-sections.vertical.label=수직 분할 +split-by-sections.horizontal.placeholder=수평 분할 수를 입력합니다 +split-by-sections.vertical.placeholder=수직 분할 수를 입력합니다 +split-by-sections.submit=PDF 분할 +split-by-sections.merge=하나의 PDF로 병합 + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses -licenses.nav=Licenses -licenses.title=3rd Party Licenses -licenses.header=3rd Party Licenses -licenses.module=Module -licenses.version=Version -licenses.license=License +licenses.nav=라이센스 +licenses.title=제3자 라이선스 +licenses.header=제3자 라이선스 +licenses.module=모듈 +licenses.version=버전 +licenses.license=라이센스 +# error +error.sorry=문제를 끼친 점 죄송합니다! +error.needHelp=도움이 필요하신가요 / 문제가 있으신가요? +error.contactTip=여전히 문제가 해결되지 않는다면 망설이지 마시고 도움을 요청하십시오. GitHub 페이지에서 티켓을 제출하거나 Discord를 통해 우리에게 연락하실 수 있습니다: +error.404.head=404 - 페이지를 찾을 수 없습니다 | 이런, 코드에 걸려 넘어졌어요! +error.404.1=원하시는 페이지를 찾을 수가 없네요. +error.404.2=문제가 발생했습니다 +error.github=GitHub에서 티켓 제출 +error.showStack=스택 추적 보기 +error.copyStack=스택 추적 복사 +error.githubSubmit=GitHub - 티켓 제출 +error.discordSubmit=Discord - 문의 게시 + diff --git a/src/main/resources/messages_nl_NL.properties b/src/main/resources/messages_nl_NL.properties index fed34234..cbbf7b43 100644 --- a/src/main/resources/messages_nl_NL.properties +++ b/src/main/resources/messages_nl_NL.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=Waar false=Onwaar unknown=Onbekend save=Opslaan +saveToBrowser=Save to Browser close=Sluiten filesSelected=Bestanden geselecteerd noFavourites=Geen favorieten toegevoegd +downloadComplete=Download Complete bored=Verveeld met wachten? alphabet=Alfabet downloadPdf=Download PDF @@ -52,6 +54,22 @@ notAuthenticatedMessage=Gebruiker niet ingelogd. userNotFoundMessage=Gebruiker niet gevonden. incorrectPasswordMessage=Huidige wachtwoord is onjuist. usernameExistsMessage=Nieuwe gebruikersnaam bestaat al. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Kan de rol van de huidige gebruiker niet downgraden +downgradeCurrentUserLongMessage=Kan de rol van de huidige gebruiker niet downgraden. Huidige gebruiker wordt dus niet weergegeven. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### @@ -62,6 +80,8 @@ pipeline.uploadButton=Aangepast uploaden pipeline.configureButton=Configureren pipeline.defaultOption=Aangepast pipeline.submitButton=Opslaan +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Instellingen ############# settings.title=Instellingen settings.update=Update beschikbaar +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=App versie: settings.downloadOption.title=Kies download optie (Voor enkelvoudige bestanddownloads zonder zip): settings.downloadOption.1=Open in hetzelfde venster @@ -102,12 +123,13 @@ settings.downloadOption.3=Download bestand settings.zipThreshold=Bestanden zippen wanneer het aantal gedownloade bestanden meer is dan settings.signOut=Uitloggen settings.accountSettings=Account instellingen - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Inloggegevens wijzigen changeCreds.header=Werk je accountgegevens bij -changeCreds.changeUserAndPassword=Je gebruikt de standaard inloggegevens. Voer een nieuw wachtwoord in (en eventueel een gebruikersnaam) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=Nieuwe gebruikersnaam changeCreds.oldPassword=Huidige wachtwoord changeCreds.newPassword=Nieuw wachtwoord @@ -142,14 +164,18 @@ adminUserSettings.header=Beheer gebruikers adminUserSettings.admin=Beheerder adminUserSettings.user=Gebruiker adminUserSettings.addUser=Voeg nieuwe gebruiker toe +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Rollen adminUserSettings.role=Rol adminUserSettings.actions=Acties adminUserSettings.apiUser=Beperkte API gebruiker +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Alleen web gebruiker adminUserSettings.demoUser=Demogebruiker (geen aangepaste instellingen) +adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Forceer gebruiker om gebruikersnaam/wachtwoord te wijzigen bij inloggen adminUserSettings.submit=Gebruiker opslaan +adminUserSettings.changeUserRole=De rol van de gebruiker wijzigen ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Voeg tekst of afbeeldingsstempels toe op vaste locatie AddStampRequest.tags=Stempel, Afbeelding toevoegen, afbeelding centreren, watermerk, PDF, Insluiten, Aanpassen +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stempel, Afbeelding toevoegen, afbeelding centreren, waterm ########################### #login login.title=Inloggen +login.header=Inloggen login.signin=Inloggen login.rememberme=Onthoud mij login.invalid=Ongeldige gebruikersnaam of wachtwoord. login.locked=Je account is geblokkeerd. login.signinTitle=Gelieve in te loggen +login.ssoSignIn=Inloggen via Single Sign-on +login.oauth2AutoCreateDisabled=OAUTH2 Automatisch aanmaken gebruiker uitgeschakeld #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Converteren naar enkele pagina pageExtracter.title=Pagina's extraheren pageExtracter.header=Pagina's extraheren pageExtracter.submit=Extraheren +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Document 1 compare.document.2=Document 2 compare.submit=Vergelijken +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Ondertekenen @@ -643,6 +694,7 @@ repair.submit=Repareren #flatten flatten.title=Afvlakken flatten.header=PDF's afvlakken +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Afvlakken @@ -726,11 +778,23 @@ merge.submit=Samenvoegen pdfOrganiser.title=Pagina organisator pdfOrganiser.header=PDF pagina organisator pdfOrganiser.submit=Pagina's herschikken +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF Multitool multiTool.header=PDF Multitool +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=PDF bekijken @@ -741,6 +805,7 @@ pageRemover.title=Pagina verwijderaar pageRemover.header=PDF pagina verwijderaar pageRemover.pagesToDelete=Te verwijderen pagina's (Voer een door komma's gescheiden lijst met paginanummers in): pageRemover.submit=Pagina's verwijderen +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Selecteer rotatiehoek (in veelvouden van 90 graden): rotate.submit=Roteren -#merge +#split-pdfs split.title=PDF splitsen split.header=PDF splitsen split.desc.1=De nummers die je kiest zijn de paginanummers waarop je een splitsing wilt uitvoeren -split.desc.2=Als zodanig selecteren van 1,3,7-8 zou een 10 pagina's tellend document splitsen in 6 aparte PDF's met: +split.desc.2=Als zodanig selecteren van 1,3,7-9 zou een 10 pagina's tellend document splitsen in 6 aparte PDF's met: split.desc.3=Document #1: Pagina 1 split.desc.4=Document #2: Pagina 2 en 3 -split.desc.5=Document #3: Pagina 4, 5 en 6 -split.desc.6=Document #4: Pagina 7 -split.desc.7=Document #5: Pagina 8 -split.desc.8=Document #6: Pagina 9 en 10 +split.desc.5=Document #3: Pagina 4, 5, 6 en 7 +split.desc.6=Document #4: Pagina 8 +split.desc.7=Document #5: Pagina 9 +split.desc.8=Document #6: Pagina 10 split.splitPages=Voer pagina's in om op te splitsen: split.submit=Splitsen @@ -828,6 +893,8 @@ watermark.selectText.7=Transparantie (0% - 100%): watermark.selectText.8=Type watermerk: watermark.selectText.9=Watermerk afbeelding: watermark.submit=Watermerk toevoegen +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF naar PDF/A pdfToPDFA.header=PDF naar PDF/A pdfToPDFA.credit=Deze service gebruikt OCRmyPDF voor PDF/A-conversie pdfToPDFA.submit=Converteren +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Converteren #PDFToHTML PDFToHTML.title=PDF naar HTML PDFToHTML.header=PDF naar HTML -PDFToHTML.credit=Deze service gebruikt LibreOffice voor bestandsconversie. +PDFToHTML.credit=Deze service gebruikt pdftohtml voor bestandsconversie. PDFToHTML.submit=Converteren @@ -925,6 +994,7 @@ PDFToCSV.prompt=Kies pagina om tabel te extraheren PDFToCSV.submit=Extraheren #split-by-size-or-count +split-by-size-or-count.title=PDF splitsen op grootte of aantal split-by-size-or-count.header=PDF splitsen op grootte of aantal split-by-size-or-count.type.label=Selecteer splits type split-by-size-or-count.type.size=Op grootte @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Verticale secties split-by-sections.horizontal.placeholder=Voer het aantal horizontale secties in split-by-sections.vertical.placeholder=Voer het aantal verticale secties in split-by-sections.submit=PDF splitsen +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Versie licenses.license=Licentie +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_pl_PL.properties b/src/main/resources/messages_pl_PL.properties index c385a13f..757647ee 100644 --- a/src/main/resources/messages_pl_PL.properties +++ b/src/main/resources/messages_pl_PL.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=Tak false=Nie unknown=Nieznany save=Zapisz +saveToBrowser=Save to Browser close=Zamknij filesSelected=wybrane pliki noFavourites=Nie dodano ulubionych +downloadComplete=Download Complete bored=Znudzony czekaniem? alphabet=Alfabet downloadPdf=Pobierz PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Nie można obniżyć roli bieżącego użytkownika +downgradeCurrentUserLongMessage=Nie można obniżyć roli bieżącego użytkownika. W związku z tym bieżący użytkownik nie zostanie wyświetlony. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Ustawienia ############# settings.title=Ustawienia settings.update=Dostępna aktualizacja +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Wersia aplikacji: settings.downloadOption.title=Wybierz opcję pobierania (w przypadku pobierania pojedynczych plików innych niż ZIP): settings.downloadOption.1=Otwórz w tym samym oknie @@ -102,12 +123,13 @@ settings.downloadOption.3=Pobierz plik settings.zipThreshold=Spakuj pliki, gdy liczba pobranych plików przekroczy settings.signOut=Sign Out settings.accountSettings=Account Settings - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Change Credentials changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=New Username changeCreds.oldPassword=Current Password changeCreds.newPassword=New Password @@ -142,14 +164,18 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions adminUserSettings.apiUser=Limited API User +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Web Only User adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Force user to change username/password on login +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User +adminUserSettings.changeUserRole=Zmień rolę użytkownika ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Sign in +login.header=Sign in login.signin=Sign in login.rememberme=Remember me login.invalid=Invalid username or password. login.locked=Your account has been locked. login.signinTitle=Please sign in +login.ssoSignIn=Zaloguj się za pomocą logowania jednokrotnego +login.oauth2AutoCreateDisabled=Wyłączono automatyczne tworzenie użytkownika OAUTH2 #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Convert To Single Page pageExtracter.title=Extract Pages pageExtracter.header=Extract Pages pageExtracter.submit=Extract +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Dokument 1 compare.document.2=Dokument 2 compare.submit=Porównaj +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Podpis @@ -643,6 +694,7 @@ repair.submit=Napraw #flatten flatten.title=Spłaszcz flatten.header=Spłaszcz dokument(y) PDF +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Spłaszcz @@ -726,11 +778,23 @@ merge.submit=Połącz pdfOrganiser.title=Kolejność stron pdfOrganiser.header=Kolejność stron PDF pdfOrganiser.submit=Zmień kolejność stron +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Multi narzędzie PDF multiTool.header=Multi narzędzie PDF +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=Narzędzie do usuwania stron pageRemover.header=Narzędzie do usuwania stron w dokumentach PDF pageRemover.pagesToDelete=Strony do usunięcia (wprowadź listę numerów stron oddzielonych przecinkami): pageRemover.submit=Usuń strony +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Wybierz kąt obrotu (domyślnie 90 stopni): rotate.submit=Obróć -#merge +#split-pdfs split.title=Podziel dokument PDF split.header=Podziel dokument PDF split.desc.1=Wybrane numery to numery stron, na których chcesz dokonać podziału -split.desc.2=Np. taki wybór 1,3,7-8 podzieliłby 10-stronicowy dokument na 6 oddzielnych plików PDF z: +split.desc.2=Np. taki wybór 1,3,7-9 podzieliłby 10-stronicowy dokument na 6 oddzielnych plików PDF z: split.desc.3=Dokument #1: Strona 1 split.desc.4=Dokument #2: Strona 2 i 3 -split.desc.5=Dokument #3: Strona 4, 5 i 6 -split.desc.6=Dokument #4: Strona 7 -split.desc.7=Dokument #5: Strona 8 -split.desc.8=Dokument #6: Strona 9 i 10 +split.desc.5=Dokument #3: Strona 4, 5, 6 i 7 +split.desc.6=Dokument #4: Strona 8 +split.desc.7=Dokument #5: Strona 9 +split.desc.8=Dokument #6: Strona 10 split.splitPages=Wprowadź strony do podziału na: split.submit=Podziel @@ -828,6 +893,8 @@ watermark.selectText.7=Nieprzezroczystość (0% - 100%): watermark.selectText.8=Watermark Type: watermark.selectText.9=Watermark Image: watermark.submit=Dodaj znak wodny +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF na PDF/A pdfToPDFA.header=PDF na PDF/A pdfToPDFA.credit=Ta usługa używa OCRmyPDF do konwersji PDF/A pdfToPDFA.submit=Konwertuj +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Konwertuj #PDFToHTML PDFToHTML.title=PDF na HTML PDFToHTML.header=PDF na HTML -PDFToHTML.credit=Ta usługa używa LibreOffice do konwersji plików. +PDFToHTML.credit=Ta usługa używa pdftohtml do konwersji plików. PDFToHTML.submit=Konwertuj @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=Wyci?g #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_pt_BR.properties b/src/main/resources/messages_pt_BR.properties index 778f1df5..97ac151e 100644 --- a/src/main/resources/messages_pt_BR.properties +++ b/src/main/resources/messages_pt_BR.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=Verdadeiro false=Falso unknown=Desconhecido save=Salvar +saveToBrowser=Save to Browser close=Fechar filesSelected=arquivos selecionados noFavourites=Nenhum favorito adicionado +downloadComplete=Download Complete bored=Entediado esperando? alphabet=Alfabeto downloadPdf=baixar PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Não é possível fazer downgrade da função do usuário atual +downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do usuário atual. Portanto, o usuário atual não será mostrado. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Configurações ############# settings.title=Configurações settings.update=Atualização disponível +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Versão do aplicativo: settings.downloadOption.title=Escolha a opção de download (para downloads não compactados de arquivo único): settings.downloadOption.1=Abrir na mesma janela @@ -102,12 +123,13 @@ settings.downloadOption.3=⇬ Fazer download do arquivo settings.zipThreshold=Compactar arquivos quando o número de arquivos baixados exceder settings.signOut=Sign Out settings.accountSettings=Account Settings - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Change Credentials changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=New Username changeCreds.oldPassword=Current Password changeCreds.newPassword=New Password @@ -142,14 +164,18 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions adminUserSettings.apiUser=Limited API User +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Web Only User adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Force user to change username/password on login +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User +adminUserSettings.changeUserRole=Alterar Função de Usuário ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Sign in +login.header=Sign in login.signin=Sign in login.rememberme=Remember me login.invalid=Invalid username or password. login.locked=Your account has been locked. login.signinTitle=Please sign in +login.ssoSignIn=Iniciar sessão através de início de sessão único +login.oauth2AutoCreateDisabled=OAUTH2 Auto-Criar Usuário Desativado #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Converter para Página Única pageExtracter.title=Extrair Páginas pageExtracter.header=Extrair Páginas pageExtracter.submit=Extrair +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Documento 1 compare.document.2=Documento 2 compare.submit=Comparar +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Assinar @@ -643,6 +694,7 @@ repair.submit=Reparar #flatten flatten.title=Achatar flatten.header=Achatar PDFs +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Achatar @@ -726,11 +778,23 @@ merge.submit=Mesclar pdfOrganiser.title=Organizador de Páginas pdfOrganiser.header=Organizador de Páginas PDF pdfOrganiser.submit=Reorganizar Páginas +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Multiferramenta de PDF multiTool.header=Multiferramenta de PDF +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=Remover Página pageRemover.header=Remover Páginas do PDF pageRemover.pagesToDelete=Páginas a serem excluídas (insira uma lista separada por vírgulas de números de página): pageRemover.submit=Excluir Páginas +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Selecione o ângulo de rotação (múltiplos de 90 graus): rotate.submit=Girar -#merge +#split-pdfs split.title=Dividir PDF split.header=Dividir PDF split.desc.1=Os números selecionados correspondem às páginas onde você deseja fazer a divisão. -split.desc.2=Por exemplo, selecionar 1,3,7-8 dividirá um documento de 10 páginas em 6 PDFs separados da seguinte forma: +split.desc.2=Por exemplo, selecionar 1,3,7-9 dividirá um documento de 10 páginas em 6 PDFs separados da seguinte forma: split.desc.3=Documento Nº1: Página 1 split.desc.4=Documento Nº2: Páginas 2 e 3 -split.desc.5=Documento Nº3: Páginas 4, 5 e 6 -split.desc.6=Documento Nº4: Página 7 -split.desc.7=Documento Nº5: Página 8 -split.desc.8=Documento Nº6: Páginas 9 e 10 +split.desc.5=Documento Nº3: Páginas 4, 5, 6 e 7 +split.desc.6=Documento Nº4: Página 8 +split.desc.7=Documento Nº5: Página 9 +split.desc.8=Documento Nº6: Páginas 10 split.splitPages=Digite as páginas para a divisão: split.submit=Dividir @@ -828,6 +893,8 @@ watermark.selectText.7=Opacidade (0% - 100%) watermark.selectText.8=Tipo de Marca d'Água watermark.selectText.9=Imagem da Marca d'Água watermark.submit=Adicionar Marca d'Água +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF para PDF/A pdfToPDFA.header=PDF para PDF/A pdfToPDFA.credit=Este serviço usa OCRmyPDF para Conversão de PDF/A pdfToPDFA.submit=Converter +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Converter #PDFToHTML PDFToHTML.title=PDF para HTML PDFToHTML.header=PDF para HTML -PDFToHTML.credit=Este serviço usa o LibreOffice para Conversão de Arquivos. +PDFToHTML.credit=Este serviço usa o pdftohtml para Conversão de Arquivos. PDFToHTML.submit=Converter @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=Eztenna #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_pt_PT.properties b/src/main/resources/messages_pt_PT.properties new file mode 100644 index 00000000..fcde52ad --- /dev/null +++ b/src/main/resources/messages_pt_PT.properties @@ -0,0 +1,1064 @@ +########### +# Generic # +########### +# the direction that the language is written (ltr=left to right, rtl = right to left) +language.direction=ltr + +pdfPrompt=Selecione PDF(s) +multiPdfPrompt=Selecione PDFs (2+) +multiPdfDropPrompt=Selecione (ou arraste e solte) todos os PDFs necessários +imgPrompt=Selecione a(s) imagem(ns) +genericSubmit=Enviar +processTimeWarning=Aviso: esse processo pode levar até um minuto, dependendo do tamanho do ficheiro +pageOrderPrompt=Ordem das páginas (digite uma lista separada por vírgulas de números de página): +pageSelectionPrompt=Seleção de página personalizada (Insira uma lista separada por vírgulas de números de página 1,5,6 ou funções como 2n+1): +goToPage=Ir +true=Verdadeiro +false=Falso +unknown=Desconhecido +save=Salvar +saveToBrowser=Save to Browser +close=Fechar +filesSelected=Ficheiros Selecionados +noFavourites=Nenhum favorito adicionado +downloadComplete=Download Complete +bored=Entediado esperando? +alphabet=Alfabeto +downloadPdf=Descarregar PDF +text=Texto +font=Fonte +selectFillter=-- Selecione -- +pageNum=Número de página +sizes.small=Pequeno +sizes.medium=Médio +sizes.large=Grande +sizes.x-large=Muito grande +error.pdfPassword=O documento PDF está protegido por senha e a senha não foi fornecida ou está incorreta +delete=Apagar +username=Utilizador +password=Senha +welcome=Bem-vindo +property=Propriedade +black=Preto +white=Branco +red=Vermelho +green=Verde +blue=Azul +custom=Personalizar... +WorkInProgess=Trabalho em progresso, pode não funcionar ou apresentar erros. Por favor, relate quaisquer problemas! +poweredBy=Distribuído por +yes=Sim +no=Não +changedCredsMessage=Dados alterados! +notAuthenticatedMessage=Utilizador não autenticado. +userNotFoundMessage=Utilizador inexistente. +incorrectPasswordMessage=Senha incorreta. +usernameExistsMessage=Esse utilizador já existe. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Não é possível fazer downgrade da função do utilizador atual +downgradeCurrentUserLongMessage=Não é possível fazer downgrade da função do utilizador atual. Portanto, o utilizador atual não será mostrado. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + + + +############### +# Pipeline # +############### +pipeline.header=Pipeline Menu (Beta) +pipeline.uploadButton=Carregar personalizado +pipeline.configureButton=Configurar +pipeline.defaultOption=Personalizar +pipeline.submitButton=Submeter +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help + +###################### +# Pipeline Options # +###################### +pipelineOptions.header=Pipeline Configuração +pipelineOptions.pipelineNameLabel=Pipeline Nome +pipelineOptions.saveSettings=Guardar configuração da operação +pipelineOptions.pipelineNamePrompt=Introduza o nome da pipeline aqui +pipelineOptions.selectOperation=Escolher acção +pipelineOptions.addOperationButton=Adicionar acção +pipelineOptions.pipelineHeader=Pipeline: +pipelineOptions.saveButton=Descarregar +pipelineOptions.validateButton=Validar + + + + +############# +# NAVBAR # +############# +navbar.convert=Converter +navbar.security=Segurança +navbar.other=Outro +navbar.darkmode=Modo Escuro +navbar.pageOps=Operações de página +navbar.settings=Configurações + +############# +# SETTINGS # +############# +settings.title=Configurações +settings.update=Atualização disponível +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. +settings.appVersion=Versão da aplicação: +settings.downloadOption.title=Escolha a opção de download (para downloads não compactados de ficheiro único): +settings.downloadOption.1=Abrir na mesma janela +settings.downloadOption.2=Abrir em nova janela +settings.downloadOption.3=⇬ Fazer download do ficheiro +settings.zipThreshold=Compactar ficheiros quando o número de ficheiros baixados exceder +settings.signOut=Terminar Sessão +settings.accountSettings=Configuração de Conta +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs + +changeCreds.title=Alterar senha +changeCreds.header=Alterar dados da sua conta +changeCreds.changePassword=You are using default login credentials. Please enter a new password +changeCreds.newUsername=Novo Utilizador +changeCreds.oldPassword=Senha Atual +changeCreds.newPassword=Nova Senha +changeCreds.confirmNewPassword=Confirmar Nova Senha +changeCreds.submit=Submeter Alterações + + + +account.title=Account Settings +account.accountSettings=Account Settings +account.adminSettings=Admin Settings - View and Add Users +account.userControlSettings=User Control Settings +account.changeUsername=Change Username +account.newUsername=New Username +account.password=Confirmation Password +account.oldPassword=Old password +account.newPassword=New Password +account.changePassword=Change Password +account.confirmNewPassword=Confirm New Password +account.signOut=Sign Out +account.yourApiKey=Your API Key +account.syncTitle=Sync browser settings with Account +account.settingsCompare=Settings Comparison: +account.property=Property +account.webBrowserSettings=Web Browser Setting +account.syncToBrowser=Sync Account -> Browser +account.syncToAccount=Sync Account <- Browser + + +adminUserSettings.title=User Control Settings +adminUserSettings.header=Admin User Control Settings +adminUserSettings.admin=Admin +adminUserSettings.user=User +adminUserSettings.addUser=Add New User +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. +adminUserSettings.roles=Roles +adminUserSettings.role=Role +adminUserSettings.actions=Actions +adminUserSettings.apiUser=Limited API User +adminUserSettings.extraApiUser=Additional Limited API User +adminUserSettings.webOnlyUser=Web Only User +adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login +adminUserSettings.submit=Save User +adminUserSettings.changeUserRole=Alterar usuário + +############# +# HOME-PAGE # +############# +home.desc=Seu melhor utilitário para suas necessidades de PDF. +home.searchBar=Procurar ferramentas... + + +home.viewPdf.title=Visualizar PDF +home.viewPdf.desc=Visualizar, adicionar notas, texto ou imagens +viewPdf.tags=visualizat,ler,notas,texto,imagens + +home.multiTool.title=Multiferramenta de PDF +home.multiTool.desc=Juntar, girar, reorganizar e remover páginas +multiTool.tags=Multi Ferramenta, Operação Múltipla, Interface do Usuário, Clique e Arraste, Front-end, Lado do Cliente + +home.merge.title=Juntar +home.merge.desc=Juntar facilmente vários PDFs num só. +merge.tags=juntar, Operações de Página, Lado do Servidor + +home.split.title=Dividir +home.split.desc=Dividir PDFs em vários documentos +split.tags=Operações de Página, dividir, Múltiplas Páginas, cortar, Lado do Servidor + +home.rotate.title=Girar +home.rotate.desc=Girar facilmente seus PDFs. +rotate.tags=Lado do Servidor + + +home.imageToPdf.title=Imagem para PDF +home.imageToPdf.desc=Converter uma imagem (PNG, JPEG, GIF) em PDF. +imageToPdf.tags=conversão, img, jpg, imagem, foto + +home.pdfToImage.title=PDF para Imagem +home.pdfToImage.desc=Converter um PDF em uma imagem. (PNG, JPG, GIF) +pdfToImage.tags=conversão, img, jpg, imagem, foto + +home.pdfOrganiser.title=Organizar +home.pdfOrganiser.desc=Remover/reorganizar as páginas em qualquer ordem. +pdfOrganiser.tags=duplex, par, ímpar, ordenar, mover + + +home.addImage.title=Adicionar Imagem +home.addImage.desc=Adicionar uma imagem em um local definido no PDF (trabalho em andamento) +addImage.tags=img, jpg, imagem, foto + +home.watermark.title=Adicionar Marca d'água +home.watermark.desc=Adicionar uma marca d'água personalizada ao seu documento PDF. +watermark.tags=Texto, repetindo, rótulo, próprio, direitos autorais, marca registrada, img, jpg, imagem, foto + +home.permissions.title=Alterar Permissões +home.permissions.desc=Alterar as permissões do seu documento PDF. +permissions.tags=leitura, escrita, edição, impressão + + +home.removePages.title=Remover +home.removePages.desc=Excluir as páginas indesejadas do seu documento PDF. +removePages.tags=Remover páginas, excluir páginas + +home.addPassword.title=Adicionar Senha +home.addPassword.desc=Proteger seu documento PDF com uma senha. +addPassword.tags=seguro, segurança + +home.removePassword.title=Remover Senha +home.removePassword.desc=Remover a proteção por senha do seu documento PDF. +removePassword.tags=seguro, Descriptografar, segurança, remover senha + +home.compressPdfs.title=Comprimir +home.compressPdfs.desc=Comprimir PDFs para reduzir o tamanho do ficheiro. +compressPdfs.tags=compactar, pequeno, mínimo + + +home.changeMetadata.title=Alterar Metadados +home.changeMetadata.desc=Alterar/remover/adicionar metadados de um documento PDF. +changeMetadata.tags=Título, autor, data, criação, hora, editor, produtor, estatísticas + +home.fileToPDF.title=Converter ficheiro para PDF +home.fileToPDF.desc=Converter praticamente qualquer ficheiro em PDF (DOCX, PNG, XLS, PPT, TXT e mais) +fileToPDF.tags=transformação, formato, documento, imagem, slide, texto, conversão, escritório, documentos, word, excel, powerpoint + +home.ocr.title=OCR / Limpeza de Digitalizações +home.ocr.desc=Verifica e detecta texto não seleccionável de um PDF. +ocr.tags=reconhecimento, texto, imagem, digitalização, leitura, identificação, detecção, editável + + +home.extractImages.title=Extrair Imagens +home.extractImages.desc=Extrair todas as imagens de um PDF e salvá-las em um ficheiro zip. +extractImages.tags=imagem, foto, salvar, ficheiro, zip, captura, coleta + +home.pdfToPDFA.title=PDF para PDF/A +home.pdfToPDFA.desc=Converter PDF para o formato PDF/A para armazenamento a longo prazo. +pdfToPDFA.tags=ficheiro, longo prazo, padrão, conversão, armazenamento, preservação + +home.PDFToWord.title=PDF para Word +home.PDFToWord.desc=Converter PDF para formatos Word (DOC, DOCX e ODT) +PDFToWord.tags=doc, docx, odt, word, transformação, formato, conversão, escritório, microsoft, ficheiro doc + +home.PDFToPresentation.title=PDF para Powerpoint +home.PDFToPresentation.desc=Converter PDF para formatos de apresentação (PPT, PPTX e ODP) +PDFToPresentation.tags=slides, apresentação, escritório, microsoft + +home.PDFToText.title=PDF para Texto/RTF +home.PDFToText.desc=Converter PDF em formato de texto ou RTF +PDFToText.tags=formato rico, formato de texto enriquecido, formato de texto rico + +home.PDFToHTML.title=PDF para HTML +home.PDFToHTML.desc=Converter PDF para o formato HTML +PDFToHTML.tags=conteúdo web, compatível com navegador + + +home.PDFToXML.title=PDF para XML +home.PDFToXML.desc=Converter PDF para o formato XML +PDFToXML.tags=extração-de-dados,conteúdo-estruturado,interoperabilidade,transformação,converter + +home.ScannerImageSplit.title=Detectar/Dividir Fotos Digitalizadas +home.ScannerImageSplit.desc=Divide várias fotos de dentro de uma imagem/PDF digitalizado +ScannerImageSplit.tags=separar,detecção-automática,digitalizações,foto-múltipla,organizar + +home.sign.title=Assinar +home.sign.desc=Adicionar assinatura ao PDF por desenho, texto ou imagem +sign.tags=autorizar,iniciais,assinatura-desenhada,assinatura-de-texto,assinatura-de-imagem + +home.flatten.title=Achatar +home.flatten.desc=Remover todos os elementos e formulários interativos de um PDF +flatten.tags=estático,desativar,não-interativo,otimizar + +home.repair.title=Reparar +home.repair.desc=Tentar reparar um PDF corrompido/quebrado +repair.tags=corrigir,restaurar,correção,recuperar + +home.removeBlanks.title=Remover Páginas em Branco +home.removeBlanks.desc=Detectar e remover páginas em branco de um documento +removeBlanks.tags=limpeza,otimização,sem-conteúdo,organizar + +home.removeAnnotations.title=Removee Notas +home.removeAnnotations.desc=Remove todas as notas ou comentário de um PDF. +removeAnnotations.tags=comments,highlight,notes,markup,remove + +home.compare.title=Comparar +home.compare.desc=Comparar e mostrar as diferenças entre 2 documentos PDF +compare.tags=diferenciar,contraste,mudanças,análise + +home.certSign.title=Assinar com Certificado +home.certSign.desc=Assinar um PDF com um Certificado/Chave (PEM/P12) +certSign.tags=autenticar,PEM,P12,oficial,criptografar + +home.pageLayout.title=Layout de Múltiplas Páginas +home.pageLayout.desc=Juntar várias páginas de um documento PDF em uma única página +pageLayout.tags=juntar,composto,vista-única,organizar + +home.scalePages.title=Ajustar Tamanho/Escala de Página +home.scalePages.desc=Alterar o tamanho/escala da página e/ou seu conteúdo. +scalePages.tags=redimensionar,modificar,dimensão,adaptar + +home.pipeline.title=Pipeline (Avançado) +home.pipeline.desc=Executar várias ações em PDFs definindo scripts de pipeline +pipeline.tags=automatizar,sequência,scriptado,processo-em-lote + +home.add-page-numbers.title=Adicionar Números de Página +home.add-page-numbers.desc=Adicionar números de página em todo o documento em um local definido +add-page-numbers.tags=paginar,rotular,organizar,índice + +home.auto-rename.title=Renomear Automaticamente o ficheiro PDF +home.auto-rename.desc=Renomeia automaticamente um ficheiro PDF com base no cabeçalho detectado +auto-rename.tags=detecção-automática,baseado-em-cabeçalho,organizar,relabel + +home.adjust-contrast.title=Ajustar Cores/Contraste +home.adjust-contrast.desc=Ajustar Contraste, Saturação e Brilho de um PDF +adjust-contrast.tags=correção-de-cor,ajustar,modificar,realçar + +home.crop.title=Cortar PDF +home.crop.desc=Cortar um PDF para reduzir o tamanho (mantém o texto!) +crop.tags=aparar,encolher,editar,formato + +home.autoSplitPDF.title=Divisão Automática de Páginas +home.autoSplitPDF.desc=Dividir automaticamente um PDF digitalizado com separador de páginas físicas QR Code +autoSplitPDF.tags=baseado-em-QR,separar,segmento-de-digitalização,organizar + +home.sanitizePdf.title=Sanitizar +home.sanitizePdf.desc=Remover scripts e outros elementos de ficheiros PDF +sanitizePdf.tags=limpar,seguro,protegido,remover-ameaças + +home.URLToPDF.title=Converter Site para PDF +home.URLToPDF.desc=Converte qualquer página da internet para um ficheiro PDF +URLToPDF.tags=captura-de-web,salvar-página,web-para-doc,arquivar + +home.HTMLToPDF.title=HTML para PDF +home.HTMLToPDF.desc=Converte qualquer ficheiro HTML ou zip para PDF +HTMLToPDF.tags=marcação,conteúdo-web,transformação,converter + + +home.MarkdownToPDF.title=Markdown para PDF +home.MarkdownToPDF.desc=Converte qualquer ficheiro Markdown para PDF +MarkdownToPDF.tags=marcação,conteúdo-web,transformação,converter + + +home.getPdfInfo.title=Obter TODAS as Informações de um PDF +home.getPdfInfo.desc=Obtém todas as informações possíveis de um PDF +getPdfInfo.tags=informações,dados,estatísticas + + +home.extractPage.title=Extrair Página(s) +home.extractPage.desc=Extrai páginas selecionadas de um PDF +extractPage.tags=extrair + + +home.PdfToSinglePage.title=PDF para Página Única Grande +home.PdfToSinglePage.desc=Combina todas as páginas de um PDF em uma única página grande +PdfToSinglePage.tags=página única + + +home.showJS.title=Mostrar Javascript +home.showJS.desc=Procura e exibe qualquer JavaScript injetado em um PDF +showJS.tags=JavaScript + +home.autoRedact.title=Edição automática +home.autoRedact.desc=Edição automática (marca a preto) baseada numa expressão indicada de um PDF. +autoRedact.tags=Redact,Hide,black out,black,marker,hidden + +home.tableExtraxt.title=PDF para CSV +home.tableExtraxt.desc=Extrai tabelas de um PDF convertendo em um CSV +tableExtraxt.tags=CSV,Tabelas Extracção,extracção,conversão + + +home.autoSizeSplitPDF.title=Dividir automaticamente por Tamanho/Páginas +home.autoSizeSplitPDF.desc=Divide um PDF em diversos documentos com base no tamanho ou número de páginas +autoSizeSplitPDF.tags=pdf,dividir,documento,organização + + +home.overlay-pdfs.title=Sobrepor PDFs +home.overlay-pdfs.desc=Sobrepor um PDF em cima de outro PDF +overlay-pdfs.tags=Sobrepor + +home.split-by-sections.title=Dividir PDF por Secções +home.split-by-sections.desc=Divide cada páginas de um PDF em secções horizontais ou verticais mais pequenas +split-by-sections.tags=Dividir secções, Dividir, Personalizar + +home.AddStampRequest.title=Adicionar carimbo em um PDF +home.AddStampRequest.desc=Adicionar um carimbo de texto ou imagem +AddStampRequest.tags=Carimbo, Adicionar imagem, imagem central, Marca d'água, PDF, Embebido, Personalizado + + +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + +########################### +# # +# WEB PAGES # +# # +########################### +#login +login.title=Aceder +login.header=Aceder +login.signin=Aceder +login.rememberme=Lembrar dados +login.invalid=Utilizador ou senha inválidos. +login.locked=A sua conta foi bloqueada. +login.signinTitle=Introduza os seus dados de acesso +login.ssoSignIn=Iniciar sessão através de início de sessão único +login.oauth2AutoCreateDisabled=OAUTH2 Criação Automática de Utilizador Desativada + + +#auto-redact +autoRedact.title=Edição Automática +autoRedact.header=Edição Automática +autoRedact.colorLabel=Cor +autoRedact.textsToRedactLabel=Texto para editar (separado por linhas) +autoRedact.textsToRedactPlaceholder=e.g. \nConfidencial \nTop-Secret +autoRedact.useRegexLabel=Usar Regex +autoRedact.wholeWordSearchLabel=Pesquisa de palavras inteiras +autoRedact.customPaddingLabel=Preenchimento extra personalizado +autoRedact.convertPDFToImageLabel=Converter PDF em imagem (usado para remover texto atrás de caixas) +autoRedact.submitButton=Submeter + + +#showJS +showJS.title=Exibir JavaScript +showJS.header=Exibir JavaScript +showJS.downloadJS=Download do JavaScript +showJS.submit=Exibir + + +#pdfToSinglePage +pdfToSinglePage.title=PDF para Página Única +pdfToSinglePage.header=PDF para Página Única +pdfToSinglePage.submit=Converter para Página Única + + +#pageExtracter +pageExtracter.title=Extrair Páginas +pageExtracter.header=Extrair Páginas +pageExtracter.submit=Extrair +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) + + +#getPdfInfo +getPdfInfo.title=Obter Informações do PDF +getPdfInfo.header=Obter Informações do PDF +getPdfInfo.submit=Obter Informações +getPdfInfo.downloadJson=Download JSON + + +#markdown-to-pdf +MarkdownToPDF.title=Markdown para PDF +MarkdownToPDF.header=Markdown para PDF +MarkdownToPDF.submit=Converter +MarkdownToPDF.help=Trabalho em andamento +MarkdownToPDF.credit=Usa o WeasyPrint + + + +#url-to-pdf +URLToPDF.title=URL para PDF +URLToPDF.header=URL para PDF +URLToPDF.submit=Converter +URLToPDF.credit=Usa o WeasyPrint + + +#html-to-pdf +HTMLToPDF.title=HTML para PDF +HTMLToPDF.header=HTML para PDF +HTMLToPDF.help=Aceita ficheiros HTML e ZIPs contendo html/css/imagens etc necessários +HTMLToPDF.submit=Converter +HTMLToPDF.credit=Usa o WeasyPrint +HTMLToPDF.zoom=Nível de zoom para exibição do site. +HTMLToPDF.pageWidth=Largura da página em centímetros. (Vazio para padrão) +HTMLToPDF.pageHeight=Altura da página em centímetros. (BVazio para padrão) +HTMLToPDF.marginTop=Margem superior da página em milímetros. (Vazio para padrão) +HTMLToPDF.marginBottom=Margem inferior da página em milímetros. (Vazio para padrão) +HTMLToPDF.marginLeft=Margem esquerda da página em milímetros. (Vazio para padrão) +HTMLToPDF.marginRight=Margem direita da página em milímetros. (Vazio para padrão) +HTMLToPDF.printBackground=Renderize o plano de fundo dos wesites. +HTMLToPDF.defaultHeader=Habilitar cabeçalho padrão (Nome e número de página) +HTMLToPDF.cssMediaType=Alterar o CSS da página. +HTMLToPDF.none=Nenhum +HTMLToPDF.print=Imprimir +HTMLToPDF.screen=Ecrã + + +#AddStampRequest +AddStampRequest.header=Carimbo PDF +AddStampRequest.title=Carimbo PDF +AddStampRequest.stampType=Tipo do Carimbo +AddStampRequest.stampText=Texto do Carimbo +AddStampRequest.stampImage=Imagem do Carimbo +AddStampRequest.alphabet=Alfabeto +AddStampRequest.fontSize=Tamanho do(a) Tipo de Letra/Imagem +AddStampRequest.rotation=Rotação +AddStampRequest.opacity=Opacidade +AddStampRequest.position=Posição +AddStampRequest.overrideX=Substituir a Coordenada X +AddStampRequest.overrideY=Substituir a Coordenada Y +AddStampRequest.customMargin=Personalizar a Margem +AddStampRequest.customColor=Personalizar a cor do texto +AddStampRequest.submit=Submeter + + +#sanitizePDF +sanitizePDF.title=Sanitizar PDF +sanitizePDF.header=Sanitizar um ficheiro PDF +sanitizePDF.selectText.1=Remover acções de JavaScript +sanitizePDF.selectText.2=Remover ficheiros embutidos +sanitizePDF.selectText.3=Remover metadados +sanitizePDF.selectText.4=Remover links +sanitizePDF.selectText.5=Remover fontes +sanitizePDF.submit=Sanitizar PDF + + +#addPageNumbers +addPageNumbers.title=Adicionar Números de Página +addPageNumbers.header=Adicionar Números de Página +addPageNumbers.selectText.1=Seleccionar ficheiro PDF: +addPageNumbers.selectText.2=Tamanho da Margem +addPageNumbers.selectText.3=Posição +addPageNumbers.selectText.4=Número Inicial +addPageNumbers.selectText.5=Páginas a Numerar +addPageNumbers.selectText.6=Texto Personalizado +addPageNumbers.customTextDesc=Texto personalizado +addPageNumbers.numberPagesDesc=Quais as páginas a numerar. (padrão 'todas', ex: 1-5 ou 2,5,9 etc) +addPageNumbers.customNumberDesc=O padrão é {n}, também aceita 'Pagina {n} de {total}', 'Texto-{n}', '{filename}-{n} +addPageNumbers.submit=Adicionar Números de Página + + +#auto-rename +auto-rename.title=Renomear Automático +auto-rename.header=Renomear Automático de PDF +auto-rename.submit=Renomear Automático + + +#adjustContrast +adjustContrast.title=Ajustar Contraste +adjustContrast.header=Ajustar Contraste +adjustContrast.contrast=Contraste: +adjustContrast.brightness=Brilho: +adjustContrast.saturation=Saturação: +adjustContrast.download=Download + + +#crop +crop.title=Cortar +crop.header=Cortar Imagem +crop.submit=Enviar + + +#autoSplitPDF +autoSplitPDF.title=Divisão Automática de PDF +autoSplitPDF.header=Divisão Automática de PDF +autoSplitPDF.description=Imprima, insira, digitalize, faça o upload e deixe que o sistema divida seus documentos automaticamente. Nenhuma classificação manual necessária. +autoSplitPDF.selectText.1=Imprima algumas folhas divisórias abaixo. +autoSplitPDF.selectText.2=Digitalize todos os seus documentos de uma vez, inserindo a folha divisória entre eles. +autoSplitPDF.selectText.3=Faça o upload do único ficheiro PDF grande digitalizado e os sistema faz o restante trabalho. +autoSplitPDF.selectText.4=As páginas divisórias são detectadas e removidas automaticamente, garantindo um documento final organizado. +autoSplitPDF.formPrompt=Enviar PDF contendo folhas divisórias: +autoSplitPDF.duplexMode=Modo Duplex (Digitalização frente e verso) +autoSplitPDF.dividerDownload1=Download 'Folha Divisória Automática (mínima).pdf' +autoSplitPDF.dividerDownload2=Download 'Folha Divisória Automática (com instruções).pdf' +autoSplitPDF.submit=Enviar + + +#pipeline +pipeline.title=Pipeline + + +#pageLayout +pageLayout.title=Layout de Múltiplas Páginas +pageLayout.header=Layout de Múltiplas Páginas +pageLayout.pagesPerSheet=Páginas por folha: +pageLayout.addBorder=Adicionar Contorno +pageLayout.submit=Enviar + + +#scalePages +scalePages.title=Ajustar Tamanho/Escala da Página +scalePages.header=Ajustar Tamanho/Escala da Página +scalePages.pageSize=Tamanho de uma página do documento. +scalePages.scaleFactor=Fator de zoom (corte) de uma página. +scalePages.submit=Enviar + + +#certSign +certSign.title=Assinatura com Certificado +certSign.header=Assine um PDF com o seu certificado (Em desenvolvimento) +certSign.selectPDF=Selecione um ficheiro PDF para assinatura: +certSign.jksNote=Nota: Se o seu tipo de certificado não estiver listado abaixo, converta-o em um arquivo Java Keystore (.jks) usando a ferramenta de linha de comando keytool. Em seguida, escolha a opção de arquivo .jks abaixo. +certSign.selectKey=Selecione o seu ficheiro de chave privada (formato PKCS#8, pode ser .pem ou .der): +certSign.selectCert=Selecione o seu ficheiro de certificado (formato X.509, pode ser .pem ou .der): +certSign.selectP12=Selecione o seu ficheiro de armazenamento de chave PKCS#12 (.p12 ou .pfx) (opcional, se fornecido, deve conter a sua chave privada e certificado): +certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore): +certSign.certType=Tipo de Certificado +certSign.password=Digite a senha do seu armazenamento de chave ou chave privada (se aplicável): +certSign.showSig=Mostrar Assinatura +certSign.reason=Razão +certSign.location=Localização +certSign.name=Nome +certSign.submit=Assinar PDF + + +#removeBlanks +removeBlanks.title=Remover Páginas em Branco +removeBlanks.header=Remover Páginas em Branco +removeBlanks.threshold=Limiar: +removeBlanks.thresholdDesc=Limiar para determinar o quão branco um pixel branco deve ser +removeBlanks.whitePercent=Porcentagem de Branco (%): +removeBlanks.whitePercentDesc=Porcentagem da página que deve ser branca para ser removida +removeBlanks.submit=Remover Páginas em Branco + + +#removeAnnotations +removeAnnotations.title=Remover Notas +removeAnnotations.header=Remover Notas +removeAnnotations.submit=Remover + + +#compare +compare.title=Comparar +compare.header=Comparar PDFs +compare.document.1=Documento 1 +compare.document.2=Documento 2 +compare.submit=Comparar + +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert + +#sign +sign.title=Assinar +sign.header=Assinar PDFs +sign.upload=Enviar Imagem +sign.draw=Desenhar Assinatura +sign.text=Inserir Texto +sign.clear=Limpar +sign.add=Adicionar + + +#repair +repair.title=Reparar +repair.header=Reparar PDFs +repair.submit=Reparar + + +#flatten +flatten.title=Achatar +flatten.header=Achatar PDFs +flatten.flattenOnlyForms=Flatten only forms +flatten.submit=Achatar + + +#ScannerImageSplit +ScannerImageSplit.selectText.1=Limite de Ângulo: +ScannerImageSplit.selectText.2=Define o ângulo absoluto mínimo necessário para que a imagem seja girada (padrão: 10). +ScannerImageSplit.selectText.3=Tolerância: +ScannerImageSplit.selectText.4=Determina o intervalo de variação de cor em torno da cor de fundo estimada (padrão: 30). +ScannerImageSplit.selectText.5=Área Mínima: +ScannerImageSplit.selectText.6=Define o limite mínimo de área para uma foto (padrão: 10000). +ScannerImageSplit.selectText.7=Área mínima de contorno: +ScannerImageSplit.selectText.8=Define o limite mínimo da área de contorno para uma foto +ScannerImageSplit.selectText.9=Tamanho do contorno: +ScannerImageSplit.selectText.10=Define o tamanho do contorno adicionado e removido para evitar contornos brancos na saída (padrão: 1). + + +#OCR +ocr.title=OCR / Limpeza de Digitalização +ocr.header=OCR / Limpeza de Digitalização (Reconhecimento Óptico de Caracteres) +ocr.selectText.1=Selecione os idiomas a serem detectados no PDF (os listados são os atualmente detectados): +ocr.selectText.2=Criar um ficheiro de texto contendo o texto OCR ao lado do PDF com OCR +ocr.selectText.3=Páginas corretamente digitalizadas em um ângulo inclinado, gire-as de volta à posição original +ocr.selectText.4=Limpar a página para reduzir a probabilidade de o OCR encontrar texto no ruído de fundo (sem alteração na saída) +ocr.selectText.5=Limpar a página para reduzir a probabilidade de o OCR encontrar texto no ruído de fundo, mantendo a limpeza na saída. +ocr.selectText.6=Ignorar páginas com texto interativo, processar apenas as páginas de OCR que são imagens +ocr.selectText.7=Forçar OCR, executar OCR em todas as páginas, removendo todos os elementos de texto originais +ocr.selectText.8=Normal (gerará um erro se o PDF já contiver texto) +ocr.selectText.9=Configurações adicionais +ocr.selectText.10=Modo OCR +ocr.selectText.11=Remover imagens após o OCR (remove TODAS as imagens, útil apenas como parte do processo de conversão) +ocr.selectText.12=Tipo de renderização (avançado) +ocr.help=Por favor, leia a documentação sobre como usar isso para outros idiomas e/ou fora do ambiente Docker +ocr.credit=Este serviço usa OCRmyPDF e Tesseract para OCR. +ocr.submit=Processar PDF com OCR + + +#extractImages +extractImages.title=Extrair Imagens +extractImages.header=Extrair Imagens +extractImages.selectText=Selecione o formato de imagem para converter as imagens extraídas +extractImages.submit=Extrair + + +#File to PDF +fileToPDF.title=Ficheiro para PDF +fileToPDF.header=Converter Qualquer ficheiro para PDF +fileToPDF.credit=Este serviço usa o LibreOffice e o Unoconv para conversão de ficheiros. +fileToPDF.supportedFileTypes=Os tipos de ficheiro suportados devem incluir os listados abaixo. No entanto, para obter uma lista atualizada completa dos formatos suportados, consulte a documentação do LibreOffice. +fileToPDF.submit=Converter para PDF + + +#compress +compress.title=Comprimir +compress.header=Comprimir PDF +compress.credit=Este serviço usa o Ghostscript para compressão/otimização de PDF. +compress.selectText.1=Modo Manual - De 1 a 4 +compress.selectText.2=Nível de Otimização: +compress.selectText.3=4 (Pior para imagens de texto) +compress.selectText.4=Modo Automático - Ajusta automaticamente a qualidade para atingir o tamanho exato do PDF +compress.selectText.5=Tamanho Esperado do PDF (por exemplo, 25 MB, 10,8 MB, 25 KB) +compress.submit=Comprimir + + +#Add image +addImage.title=Adicionar Imagem +addImage.header=Adicionar Imagem ao PDF +addImage.everyPage=Para cada página? +addImage.upload=Enviar Imagem +addImage.submit=Adicionar Imagem + + +#merge +merge.title=Juntar +merge.header=Juntar Vários PDFs (2+) +merge.sortByName=Ordenar por nome +merge.sortByDate=Ordenar por data +merge.submit=Juntar + + +#pdfOrganiser +pdfOrganiser.title=Organizador de Páginas +pdfOrganiser.header=Organizador de Páginas PDF +pdfOrganiser.submit=Reorganizar Páginas +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) + + +#multiTool +multiTool.title=Multiferramenta de PDF +multiTool.header=Multiferramenta de PDF +multiTool.uploadPrompts=Please Upload PDF + +#view pdf +viewPdf.title=View PDF +viewPdf.header=View PDF + +#pageRemover +pageRemover.title=Remover Página +pageRemover.header=Remover Páginas do PDF +pageRemover.pagesToDelete=Páginas a serem excluídas (insira uma lista separada por vírgulas de números de página): +pageRemover.submit=Excluir Páginas +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) + + +#rotate +rotate.title=Girar PDF +rotate.header=Girar PDF +rotate.selectAngle=Seleccione o ângulo de rotação (múltiplos de 90 graus): +rotate.submit=Girar + + +#split-pdfs +split.title=Dividir PDF +split.header=Dividir PDF +split.desc.1=Os números seleccionados correspondem às páginas onde você deseja fazer a divisão. +split.desc.2=Por exemplo, seleccionar 1,3,7-9 dividirá um documento de 10 páginas em 6 PDFs separados da seguinte forma: +split.desc.3=Documento Nº1: Página 1 +split.desc.4=Documento Nº2: Páginas 2 e 3 +split.desc.5=Documento Nº3: Páginas 4, 5, 6 e 7 +split.desc.6=Documento Nº4: Página 8 +split.desc.7=Documento Nº5: Página 9 +split.desc.8=Documento Nº6: Páginas 10 +split.splitPages=Digite as páginas para a divisão: +split.submit=Dividir + + +#merge +imageToPDF.title=Imagem para PDF +imageToPDF.header=Converter Imagem para PDF +imageToPDF.submit=Converter +imageToPDF.selectLabel=Opções de ajuste de imagem +imageToPDF.fillPage=Preencher página +imageToPDF.fitDocumentToImage=Ajustar página à imagem +imageToPDF.maintainAspectRatio=Manter proporções +imageToPDF.selectText.2=Girar Automaticamente +imageToPDF.selectText.3=Lógica de Vários ficheiros (Ativada apenas ao trabalhar com várias imagens) +imageToPDF.selectText.4=Juntar em um Único PDF +imageToPDF.selectText.5=Converter em PDFs Separados + + +#pdfToImage +pdfToImage.title=PDF para Imagem +pdfToImage.header=Converter PDF para Imagem +pdfToImage.selectText=Formato de Imagem +pdfToImage.singleOrMultiple=Tipo de Resultado de Imagem +pdfToImage.single=Única Imagem Grande +pdfToImage.multi=Múltiplas Imagens +pdfToImage.colorType=Tipo de Cor +pdfToImage.color=Colorida +pdfToImage.grey=Escala de Cinza +pdfToImage.blackwhite=Preto e Branco (pode resultar em perda de dados!) +pdfToImage.submit=Converter + + +#addPassword +addPassword.title=Adicionar Senha +addPassword.header=Adicionar Senha (Proteger) +addPassword.selectText.1=Selecione o PDF para Proteger +addPassword.selectText.2=Senha +addPassword.selectText.3=Tamanho da Chave de Criptografia +addPassword.selectText.4=Valores mais altos são mais seguros, mas valores mais baixos são mais compatíveis. +addPassword.selectText.5=Permissões para Definir +addPassword.selectText.6=Impedir Montagem do Documento +addPassword.selectText.7=Impedir Extracção de Conteúdo +addPassword.selectText.8=Impedir Extracção para Acessibilidade +addPassword.selectText.9=Impedir Preenchimento de Formulário +addPassword.selectText.10=Impedir Modificação +addPassword.selectText.11=Impedir Modificação de Anotação +addPassword.selectText.12=Impedir Impressão +addPassword.selectText.13=Impedir Impressão de Formatos Diferentes +addPassword.selectText.14=Senha do Proprietário +addPassword.selectText.15=Restringe o que pode ser feito com o documento após a abertura (nem todos os leitores dão suporte a isso) +addPassword.selectText.16=Restringe a abertura do próprio documento +addPassword.submit=Proteger + + +#watermark +watermark.title=Adicionar Marca d'Água +watermark.header=Adicionar Marca d'Água +watermark.selectText.1=Seleccione o PDF para Adicionar a Marca d'Água +watermark.selectText.2=Texto da Marca d'Água +watermark.selectText.3=Tamanho da Fonte +watermark.selectText.4=Rotação (0-360) +watermark.selectText.5=Espaçamento Horizontal (widthSpacer) +watermark.selectText.6=Espaçamento Vertical (heightSpacer) +watermark.selectText.7=Opacidade (0% - 100%) +watermark.selectText.8=Tipo de Marca d'Água +watermark.selectText.9=Imagem da Marca d'Água +watermark.submit=Adicionar Marca d'Água +watermark.type.1=Text +watermark.type.2=Image + + +#Change permissions +permissions.title=Alterar Permissões +permissions.header=Alterar Permissões +permissions.warning=Nota: Para tornar essas permissões inalteráveis, é recomendável defini-las com uma senha através da página "Adicionar Senha". +permissions.selectText.1=Seleccione o PDF para Alterar as Permissões +permissions.selectText.2=Permissões para Definir +permissions.selectText.3=Impedir Montagem do Documento +permissions.selectText.4=Impedir Extração de Conteúdo +permissions.selectText.5=Impedir Extração para Acessibilidade +permissions.selectText.6=Impedir Preenchimento de Formulário +permissions.selectText.7=Impedir Modificações +permissions.selectText.8=Impedir Modificação de Anotação +permissions.selectText.9=Impedir Impressão +permissions.selectText.10=Impedir Impressão de Formatos Diferentes +permissions.submit=Alterar + + +#remove password +removePassword.title=Remover Senha +removePassword.header=Remover Senha (Desproteger) +removePassword.selectText.1=Selecione o PDF para Desproteger +removePassword.selectText.2=Senha +removePassword.submit=Remover + + +#changeMetadata +changeMetadata.title=Título: +changeMetadata.header=Alterar Metadados +changeMetadata.selectText.1=Edite as Variáveis que Deseja Alterar +changeMetadata.selectText.2=Excluir Todos os Metadados +changeMetadata.selectText.3=Mostrar Metadados Personalizados +changeMetadata.author=Autor: +changeMetadata.creationDate=Data de Criação (aaaa/MM/dd HH:mm:ss): +changeMetadata.creator=Criador: +changeMetadata.keywords=Palavras-chave: +changeMetadata.modDate=Data de Modificação (aaaa/MM/dd HH:mm:ss): +changeMetadata.producer=Produtor: +changeMetadata.subject=Assunto: +changeMetadata.trapped=Trapped: +changeMetadata.selectText.4=Outros Metadados +changeMetadata.selectText.5=Adicionar Entrada de Metadados Personalizados +changeMetadata.submit=Mudar + + +#pdfToPDFA +pdfToPDFA.title=PDF para PDF/A +pdfToPDFA.header=PDF para PDF/A +pdfToPDFA.credit=Este serviço usa OCRmyPDF para Conversão de PDF/A +pdfToPDFA.submit=Converter +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format + + +#PDFToWord +PDFToWord.title=PDF para Word +PDFToWord.header=PDF para Word +PDFToWord.selectText.1=Formato do ficheiro de Saída +PDFToWord.credit=Este serviço usa o LibreOffice para Conversão de ficheiros. +PDFToWord.submit=Converter + + +#PDFToPresentation +PDFToPresentation.title=PDF para Apresentação +PDFToPresentation.header=PDF para Apresentação +PDFToPresentation.selectText.1=Formato do ficheiro de Saída +PDFToPresentation.credit=Este serviço usa o LibreOffice para Conversão de ficheiros. +PDFToPresentation.submit=Converter + + +#PDFToText +PDFToText.title=PDF para Texto/RTF +PDFToText.header=PDF para Texto/RTF +PDFToText.selectText.1=Formato do ficheiro de Saída +PDFToText.credit=Este serviço usa o LibreOffice para Conversão de ficheiros. +PDFToText.submit=Converter + + +#PDFToHTML +PDFToHTML.title=PDF para HTML +PDFToHTML.header=PDF para HTML +PDFToHTML.credit=Este serviço usa o pdftohtml para Conversão de ficheiros. +PDFToHTML.submit=Converter + + +#PDFToXML +PDFToXML.title=PDF para XML +PDFToXML.header=PDF para XML +PDFToXML.credit=Este serviço usa o LibreOffice para Conversão de ficheiros. +PDFToXML.submit=Converter + +#PDFToCSV +PDFToCSV.title=PDF para CSV +PDFToCSV.header=PDF para CSV +PDFToCSV.prompt=Escolha a página para extrair a tabela +PDFToCSV.submit=Eztenna + +#split-by-size-or-count +split-by-size-or-count.title=Dividir o PDF por tamanho, número de páginas ou número de documentos +split-by-size-or-count.header=Dividir o PDF por tamanho, número de páginas ou número de documentos +split-by-size-or-count.type.label=Seleccione o tipo de divisão +split-by-size-or-count.type.size=Por Tamanho +split-by-size-or-count.type.pageCount=Por Número de Páginas +split-by-size-or-count.type.docCount=Por Número de Documentos +split-by-size-or-count.value.label=Introduzir valor +split-by-size-or-count.value.placeholder=Introduzir tamanho (ex: 2MB or 3KB) or página (ex: 5) +split-by-size-or-count.submit=Submeter + + +#overlay-pdfs +overlay-pdfs.header=Sobrepor arquivos PDF +overlay-pdfs.baseFile.label=Selecione o arquivo PDF base +overlay-pdfs.overlayFiles.label=Selecione o arquivo PDF para sobrepor +overlay-pdfs.mode.label=Selecione o modo de sobreposição +overlay-pdfs.mode.sequential=Sobreposição sequencial +overlay-pdfs.mode.interleaved=Sobreposição intercalada +overlay-pdfs.mode.fixedRepeat=Sobreposição de repetição fixa +overlay-pdfs.counts.label=Contagens de sobreposição (para modo de repetição fixa) +overlay-pdfs.counts.placeholder=Insira páginas separadas por vírgula (ex: 2,3,1) +overlay-pdfs.position.label=Selecione a posição de sobreposição +overlay-pdfs.position.foreground=Primeiro plano +overlay-pdfs.position.background=Plano de fundo +overlay-pdfs.submit=Submeter + + +#split-by-sections +split-by-sections.title=Dividir PDF por Secções +split-by-sections.header=Divida o PDF em Secções +split-by-sections.horizontal.label=Divisões Horizontais +split-by-sections.vertical.label=Divisões Verticais +split-by-sections.horizontal.placeholder=Introduza o número de divisões horizontais +split-by-sections.vertical.placeholder=Introduza o número de divisões verticais +split-by-sections.submit=Dividir PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print + + +#licenses +licenses.nav=Licenças +licenses.title=Licenças de terceiros +licenses.header=Licenças de terceiros +licenses.module=Modulos +licenses.version=Versão +licenses.license=Licença + + +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_ro_RO.properties b/src/main/resources/messages_ro_RO.properties index 4e34c6e3..1f3daca5 100644 --- a/src/main/resources/messages_ro_RO.properties +++ b/src/main/resources/messages_ro_RO.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=Adevărat false=Fals unknown=Necunoscut save=Salvează +saveToBrowser=Save to Browser close=Închide filesSelected=fișiere selectate noFavourites=Niciun favorit adăugat +downloadComplete=Download Complete bored=Plictisit așteptând? alphabet=Alfabet downloadPdf=Descarcă PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Rolul utilizatorului curent nu poate fi retrogradat +downgradeCurrentUserLongMessage=Rolul utilizatorului curent nu poate fi retrogradat. Prin urmare, utilizatorul curent nu va fi afișat. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Setări ############# settings.title=Setări settings.update=Actualizare disponibilă +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Versiune aplicație: settings.downloadOption.title=Alege opțiunea de descărcare (pentru descărcarea unui singur fișier non-zip): settings.downloadOption.1=Deschide în aceeași fereastră @@ -102,12 +123,13 @@ settings.downloadOption.3=Descarcă fișierul settings.zipThreshold=Împachetează fișierele când numărul de fișiere descărcate depășește settings.signOut=Sign Out settings.accountSettings=Account Settings - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Change Credentials changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=New Username changeCreds.oldPassword=Current Password changeCreds.newPassword=New Password @@ -142,14 +164,18 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions adminUserSettings.apiUser=Limited API User +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Web Only User adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Force user to change username/password on login +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User +adminUserSettings.changeUserRole=Schimbați rolul utilizatorului ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Sign in +login.header=Sign in login.signin=Sign in login.rememberme=Remember me login.invalid=Invalid username or password. login.locked=Your account has been locked. login.signinTitle=Please sign in +login.ssoSignIn=Conectare prin conectare unică +login.oauth2AutoCreateDisabled=OAUTH2 Creare automată utilizator dezactivată #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Convert To Single Page pageExtracter.title=Extract Pages pageExtracter.header=Extract Pages pageExtracter.submit=Extract +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Document 1 compare.document.2=Document 2 compare.submit=Compară +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Semnează @@ -643,6 +694,7 @@ repair.submit=Repară #flatten flatten.title=Nivelare flatten.header=Nivelează documente PDF +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Nivelează @@ -726,11 +778,23 @@ merge.submit=Unire pdfOrganiser.title=Organizator de pagini pdfOrganiser.header=Organizator de pagini PDF pdfOrganiser.submit=Rearanjați paginile +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=Instrument PDF multiplu multiTool.header=Instrument PDF multiplu +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=Înlăturare pagini pageRemover.header=Înlăturare pagini din PDF pageRemover.pagesToDelete=Pagini de șters (Introduceți o listă de numere de pagini separate prin virgulă): pageRemover.submit=Ștergere pagini +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Selectați un unghi de rotație (în multiplicate de 90 de gr rotate.submit=Rotește -#merge +#split-pdfs split.title=Împarte PDF split.header=Împarte PDF split.desc.1=Numerele pe care le selectați reprezintă numărul paginii pe care doriți să o împărțiți -split.desc.2=Prin urmare, selectând 1,3,7-8, un document cu 10 pagini va fi împărțit în 6 PDF-uri separate, astfel: +split.desc.2=Prin urmare, selectând 1,3,7-9, un document cu 10 pagini va fi împărțit în 6 PDF-uri separate, astfel: split.desc.3=Documentul #1: Pagina 1 split.desc.4=Documentul #2: Paginile 2 și 3 -split.desc.5=Documentul #3: Paginile 4, 5 și 6 -split.desc.6=Documentul #4: Pagina 7 -split.desc.7=Documentul #5: Pagina 8 -split.desc.8=Documentul #6: Paginile 9 și 10 +split.desc.5=Documentul #3: Paginile 4, 5, 6 și 7 +split.desc.6=Documentul #4: Pagina 8 +split.desc.7=Documentul #5: Pagina 9 +split.desc.8=Documentul #6: Pagina 10 split.splitPages=Introduceți paginile pe care să le împărțiți: split.submit=Împarte @@ -828,6 +893,8 @@ watermark.selectText.7=Opacitate (0% - 100%): watermark.selectText.8=Watermark Type: watermark.selectText.9=Watermark Image: watermark.submit=Adăugați Filigran +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF către PDF/A pdfToPDFA.header=PDF către PDF/A pdfToPDFA.credit=Acest serviciu utilizează OCRmyPDF pentru conversia în PDF/A pdfToPDFA.submit=Convert +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Convert #PDFToHTML PDFToHTML.title=PDF către HTML PDFToHTML.header=PDF către HTML -PDFToHTML.credit=Acest serviciu utilizează LibreOffice pentru conversia fișierului. +PDFToHTML.credit=Acest serviciu utilizează pdftohtml pentru conversia fișierului. PDFToHTML.submit=Convert @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=Extrage #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_ru_RU.properties b/src/main/resources/messages_ru_RU.properties index d386167b..4517e100 100644 --- a/src/main/resources/messages_ru_RU.properties +++ b/src/main/resources/messages_ru_RU.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -11,15 +11,17 @@ imgPrompt=Выберите картинку(и) genericSubmit=Отправить processTimeWarning=Внимание: Этот процесс может занять до минуты в зависимости от размера файла. pageOrderPrompt=Порядок страниц (введите список номеров страниц через запятую): -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : +pageSelectionPrompt=Пользовательский выбор страницы (введите разделенный запятыми список номеров страниц 1,5,6 или функции типа 2n+1) : goToPage=Вперед true=Истина false=Ложь unknown=Неизвестно save=Сохранить +saveToBrowser=Сохранить в браузере close=Закрыть filesSelected=файлов выбрано noFavourites=Нет избранного +downloadComplete=Загрузка завершена bored=Скучно ждать? alphabet=Алфавит downloadPdf=Скачать PDF @@ -27,54 +29,72 @@ text=Текст font=Шрифт selectFillter=-- Выбрать -- pageNum=номер страницы -sizes.small=Small -sizes.medium=Medium -sizes.large=Large -sizes.x-large=X-Large -error.pdfPassword=The PDF Document is passworded and either the password was not provided or was incorrect -delete=Delete -username=Username -password=Password -welcome=Welcome -property=Property -black=Black -white=White -red=Red -green=Green -blue=Blue -custom=Custom... -WorkInProgess=Work in progress, May not work or be buggy, Please report any problems! +sizes.small=Маленький +sizes.medium=Середина +sizes.large=Большой +sizes.x-large=очень большой +error.pdfPassword=Документ PDF имеет пароль, и пароль не был предоставлен или был неверным +delete=Удалить +username=Имя пользователя +password=Пароль +welcome=Добро пожаловать +property=Свойство +black=Чёрный +white=Белый +red=Красный +green=Зеленый +blue=Синий +custom=Обычай... +WorkInProgess=Работа продолжается, может не работать или глючить, пожалуйста, сообщайте о любых проблемах! poweredBy=Powered by -yes=Yes -no=No -changedCredsMessage=Credentials changed! -notAuthenticatedMessage=User not authenticated. -userNotFoundMessage=User not found. -incorrectPasswordMessage=Current password is incorrect. -usernameExistsMessage=New Username already exists. +yes=Да +no=Нет +changedCredsMessage=Учетные данные изменены! +notAuthenticatedMessage=Пользователь не прошел проверку подлинности. +userNotFoundMessage=Пользователь не найден. +incorrectPasswordMessage=Текущий пароль неверен. +usernameExistsMessage=Новое имя пользователя уже существует. +invalidUsernameMessage=Недопустимое имя пользователя, Имя пользователя должно содержать только буквы алфавита и цифры. +deleteCurrentUserMessage=Невозможно удалить пользователя, вошедшего в систему. +deleteUsernameExistsMessage=Имя пользователя не существует и не может быть удалено. +downgradeCurrentUserMessage=Невозможно понизить роль текущего пользователя +downgradeCurrentUserLongMessage=Невозможно понизить роль текущего пользователя. Следовательно, текущий пользователь не будет отображаться. +error=Ошибка +oops=Ой! +help=Помощь +goHomepage=Перейти на главную страницу +joinDiscord=Присоединиться к нашему серверу Discord +seeDockerHub=Посмотреть в Docker Hub +visitGithub=Посетить репозиторий на GitHub +donate=Пожертвовать +color=Цвет +sponsor=Спонсор + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) -pipeline.uploadButton=Upload Custom -pipeline.configureButton=Configure -pipeline.defaultOption=Custom -pipeline.submitButton=Submit +pipeline.header=Меню конвейерной обработки (Бета) +pipeline.uploadButton=Загрузить Пользовательский +pipeline.configureButton=Настройка +pipeline.defaultOption=Пользовательский +pipeline.submitButton=Отправить +pipeline.help=Справка по конвейерной обработке +pipeline.scanHelp=Справка по сканированию папок ###################### # Pipeline Options # ###################### -pipelineOptions.header=Pipeline Configuration -pipelineOptions.pipelineNameLabel=Pipeline Name -pipelineOptions.saveSettings=Save Operation Settings -pipelineOptions.pipelineNamePrompt=Enter pipeline name here -pipelineOptions.selectOperation=Select Operation -pipelineOptions.addOperationButton=Add operation -pipelineOptions.pipelineHeader=Pipeline: -pipelineOptions.saveButton=Download -pipelineOptions.validateButton=Validate +pipelineOptions.header=Настройка конвейерной обработки +pipelineOptions.pipelineNameLabel=Название конвейера +pipelineOptions.saveSettings=Сохранить настройки операции +pipelineOptions.pipelineNamePrompt=Введите имя конвейера здесь +pipelineOptions.selectOperation=Выбрать операцию +pipelineOptions.addOperationButton=Добавить операцию +pipelineOptions.pipelineHeader=Конвейер: +pipelineOptions.saveButton=Скачать +pipelineOptions.validateButton=Проверить @@ -94,72 +114,78 @@ navbar.settings=Настройки ############# settings.title=Настройки settings.update=Доступно обновление +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Версия приложения: settings.downloadOption.title=Выберите вариант загрузки (для загрузки одного файла без zip): settings.downloadOption.1=Открыть в том же окне settings.downloadOption.2=Открыть в новом окне settings.downloadOption.3=Загрузить файл settings.zipThreshold=Zip-файлы, когда количество загруженных файлов превышает -settings.signOut=Sign Out -settings.accountSettings=Account Settings +settings.signOut=Выйти +settings.accountSettings=Настройки аккаунта +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs + +changeCreds.title=Изменить учетные данные +changeCreds.header=Обновите данные вашей учетной записи +changeCreds.changePassword=Вы используете предустановленные учетные данные для входа. Пожалуйста, введите новый пароль +changeCreds.newUsername=Новое имя пользователя +changeCreds.oldPassword=Текущий пароль +changeCreds.newPassword=Новый пароль +changeCreds.confirmNewPassword=Подтвердите новый пароль +changeCreds.submit=Отправить изменения -changeCreds.title=Change Credentials -changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) -changeCreds.newUsername=New Username -changeCreds.oldPassword=Current Password -changeCreds.newPassword=New Password -changeCreds.confirmNewPassword=Confirm New Password -changeCreds.submit=Submit Changes +account.title=Настройки аккаунта +account.accountSettings=Настройки аккаунта +account.adminSettings=Настройки администратора - Просмотр и добавление пользователей +account.userControlSettings=Настройки контроля пользователя +account.changeUsername=Изменить имя пользователя +account.newUsername=Новое имя пользователя +account.password=Подтверждение пароля +account.oldPassword=Старый пароль +account.newPassword=Новый пароль +account.changePassword=Изменить пароль +account.confirmNewPassword=Подтвердите новый пароль +account.signOut=Выйти +account.yourApiKey=Ваш API-ключ +account.syncTitle=Синхронизировать настройки браузера с учетной записью +account.settingsCompare=Сравнение настроек: +account.property=Свойство +account.webBrowserSettings=Настройка веб-браузера +account.syncToBrowser=Синхронизировать учетную запись -> Браузер +account.syncToAccount=Синхронизировать учетную запись <- Браузер - -account.title=Account Settings -account.accountSettings=Account Settings -account.adminSettings=Admin Settings - View and Add Users -account.userControlSettings=User Control Settings -account.changeUsername=Change Username -account.newUsername=New Username -account.password=Confirmation Password -account.oldPassword=Old password -account.newPassword=New Password -account.changePassword=Change Password -account.confirmNewPassword=Confirm New Password -account.signOut=Sign Out -account.yourApiKey=Your API Key -account.syncTitle=Sync browser settings with Account -account.settingsCompare=Settings Comparison: -account.property=Property -account.webBrowserSettings=Web Browser Setting -account.syncToBrowser=Sync Account -> Browser -account.syncToAccount=Sync Account <- Browser - - -adminUserSettings.title=User Control Settings -adminUserSettings.header=Admin User Control Settings -adminUserSettings.admin=Admin -adminUserSettings.user=User -adminUserSettings.addUser=Add New User -adminUserSettings.roles=Roles -adminUserSettings.role=Role -adminUserSettings.actions=Actions -adminUserSettings.apiUser=Limited API User -adminUserSettings.webOnlyUser=Web Only User -adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Force user to change username/password on login -adminUserSettings.submit=Save User +adminUserSettings.title=Настройки контроля пользователя +adminUserSettings.header=Настройки контроля пользователя администратора +adminUserSettings.admin=Администратор +adminUserSettings.user=Пользователь +adminUserSettings.addUser=Добавить нового пользователя +adminUserSettings.usernameInfo=Имя пользователя должно содержать только буквы и цифры, без пробелов и специальных символов. +adminUserSettings.roles=Роли +adminUserSettings.role=Роль +adminUserSettings.actions=Действия +adminUserSettings.apiUser=Ограниченный пользователь API +adminUserSettings.extraApiUser=Дополнительный ограниченный пользователь API +adminUserSettings.webOnlyUser=Только веб-пользователь +adminUserSettings.demoUser=Демо-пользователь (без настраиваемых параметров) +adminUserSettings.internalApiUser=Внутренний пользователь API +adminUserSettings.forceChange=Просить пользователя изменить пароль при входе в систему +adminUserSettings.submit=Сохранить пользователя +adminUserSettings.changeUserRole=Изменить роль пользователя ############# # HOME-PAGE # ############# home.desc=Ваш локальный универсальный магазин для всех ваших потребностей в PDF. -home.searchBar=Search for features... +home.searchBar=Поиск функций... -home.viewPdf.title=View PDF -home.viewPdf.desc=View, annotate, add text or images +home.viewPdf.title=Просмотр PDF +home.viewPdf.desc=Просмотр, аннотация, добавление текста или изображений viewPdf.tags=view,read,annotate,text,image home.multiTool.title=Мультиинструмент PDF @@ -365,58 +391,70 @@ home.showJS.title=Показать Javascript home.showJS.desc=Ищет и отображает любой JS, внедренный в PDF-файл. showJS.tags=JS -home.autoRedact.title=Auto Redact -home.autoRedact.desc=Auto Redacts(Blacks out) text in a PDF based on input text +home.autoRedact.title=Автоматическое редактирование +home.autoRedact.desc=Автоматическое затемнение (чернение) текста в PDF на основе входного текста autoRedact.tags=Redact,Hide,black out,black,marker,hidden -home.tableExtraxt.title=PDF to CSV -home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV +home.tableExtraxt.title=PDF в CSV +home.tableExtraxt.desc=Извлекает таблицы из PDF и преобразует их в CSV tableExtraxt.tags=CSV,Table Extraction,extract,convert -home.autoSizeSplitPDF.title=Auto Split by Size/Count -home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count +home.autoSizeSplitPDF.title=Автоматическое разделение по размеру/количеству +home.autoSizeSplitPDF.desc=Разделяет один PDF на несколько документов на основе размера, количества страниц или количества документов autoSizeSplitPDF.tags=pdf,split,document,organization -home.overlay-pdfs.title=Overlay PDFs -home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF +home.overlay-pdfs.title=Наложение PDF +home.overlay-pdfs.desc=Наложение одного PDF поверх другого PDF overlay-pdfs.tags=Overlay -home.split-by-sections.title=Split PDF by Sections -home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections +home.split-by-sections.title=Разделение PDF по секциям +home.split-by-sections.desc=Разделение каждой страницы PDF на более мелкие горизонтальные и вертикальные секции split-by-sections.tags=Section Split, Divide, Customize -home.AddStampRequest.title=Add Stamp to PDF -home.AddStampRequest.desc=Add text or add image stamps at set locations +home.AddStampRequest.title=Добавить печать на PDF +home.AddStampRequest.desc=Добавление текстовой или изображенческой печати в заданные места AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF в книгу/комикс +home.PDFToBook.desc=Конвертирует PDF в формат книги/комикса с помощью calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Книга в PDF +home.BookToPDF.desc=Конвертирует форматы книги/комикса в PDF с помощью calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # # # ########################### #login -login.title=Sign in -login.signin=Sign in -login.rememberme=Remember me -login.invalid=Invalid username or password. -login.locked=Your account has been locked. -login.signinTitle=Please sign in +login.title=Вход +login.header=Вход +login.signin=Войти +login.rememberme=Запомнить меня +login.invalid=Недействительное имя пользователя или пароль. +login.locked=Ваша учетная запись заблокирована. +login.signinTitle=Пожалуйста, войдите +login.ssoSignIn=Вход через единый вход +login.oauth2AutoCreateDisabled=OAUTH2 Автоматическое создание пользователя отключено #auto-redact -autoRedact.title=Auto Redact -autoRedact.header=Auto Redact -autoRedact.colorLabel=Colour -autoRedact.textsToRedactLabel=Text to Redact (line-separated) -autoRedact.textsToRedactPlaceholder=e.g. \nConfidential \nTop-Secret -autoRedact.useRegexLabel=Use Regex -autoRedact.wholeWordSearchLabel=Whole Word Search -autoRedact.customPaddingLabel=Custom Extra Padding -autoRedact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box) -autoRedact.submitButton=Submit +autoRedact.title=Автоматическое редактирование +autoRedact.header=Автоматическое редактирование +autoRedact.colorLabel=Цвет +autoRedact.textsToRedactLabel=Текст для сокрытия (каждая строка отдельно) +autoRedact.textsToRedactPlaceholder=например \nКонфиденциально \nСовершенно секретно +autoRedact.useRegexLabel=Использовать регулярные выражения +autoRedact.wholeWordSearchLabel=Поиск целых слов +autoRedact.customPaddingLabel=Дополнительное отступление по настраиваемому значению +autoRedact.convertPDFToImageLabel=Преобразовать PDF в изображение PDF (используется для удаления текста за рамкой) +autoRedact.submitButton=Отправить #showJS @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Преобразовать в одну страницу pageExtracter.title=Извлечь страницы pageExtracter.header=Извлечь страницы pageExtracter.submit=Извлечь +pageExtracter.placeholder=(например 1,2,8 или 4,7,12-16 или 2n-1) #getPdfInfo @@ -467,37 +506,37 @@ HTMLToPDF.header=HTML в PDF HTMLToPDF.help=Принимает файлы HTML и ZIP-файлы, содержащие html/css/изображения и т. д. HTMLToPDF.submit=Конвертировать HTMLToPDF.credit=Использует WeasyPrint -HTMLToPDF.zoom=Zoom level for displaying the website. -HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default) -HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default) -HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default) -HTMLToPDF.printBackground=Render the background of websites. -HTMLToPDF.defaultHeader=Enable Default Header (Name and page number) -HTMLToPDF.cssMediaType=Change the CSS media type of the page. -HTMLToPDF.none=None -HTMLToPDF.print=Print -HTMLToPDF.screen=Screen +HTMLToPDF.zoom=Уровень масштабирования для отображения веб-сайта. +HTMLToPDF.pageWidth=Ширина страницы в сантиметрах. (Пусто - по умолчанию) +HTMLToPDF.pageHeight=Высота страницы в сантиметрах. (Пусто - по умолчанию) +HTMLToPDF.marginTop=Верхний отступ страницы в миллиметрах. (Пусто - по умолчанию) +HTMLToPDF.marginBottom=Нижний отступ страницы в миллиметрах. (Пусто - по умолчанию) +HTMLToPDF.marginLeft=Левый отступ страницы в миллиметрах. (Пусто - по умолчанию) +HTMLToPDF.marginRight=Правый отступ страницы в миллиметрах. (Пусто - по умолчанию) +HTMLToPDF.printBackground=Визуализировать фон веб-сайтов. +HTMLToPDF.defaultHeader=Включить заголовок по умолчанию (Имя и номер страницы) +HTMLToPDF.cssMediaType=Изменить тип медиа CSS страницы. +HTMLToPDF.none=Нет +HTMLToPDF.print=Печать +HTMLToPDF.screen=Экран #AddStampRequest -AddStampRequest.header=Stamp PDF -AddStampRequest.title=Stamp PDF -AddStampRequest.stampType=Stamp Type -AddStampRequest.stampText=Stamp Text -AddStampRequest.stampImage=Stamp Image -AddStampRequest.alphabet=Alphabet -AddStampRequest.fontSize=Font/Image Size -AddStampRequest.rotation=Rotation -AddStampRequest.opacity=Opacity -AddStampRequest.position=Position -AddStampRequest.overrideX=Override X Coordinate -AddStampRequest.overrideY=Override Y Coordinate -AddStampRequest.customMargin=Custom Margin -AddStampRequest.customColor=Custom Text Color -AddStampRequest.submit=Submit +AddStampRequest.header=Штамповать PDF +AddStampRequest.title=Штамповать PDF +AddStampRequest.stampType=Тип штампа +AddStampRequest.stampText=Текст штампа +AddStampRequest.stampImage=Изображение штампа +AddStampRequest.alphabet=Алфавит +AddStampRequest.fontSize=Размер шрифта/изображения +AddStampRequest.rotation=Поворот +AddStampRequest.opacity=Прозрачность +AddStampRequest.position=Позиция +AddStampRequest.overrideX=Переопределить координату X +AddStampRequest.overrideY=Переопределить координату Y +AddStampRequest.customMargin=Настроенный отступ +AddStampRequest.customColor=Настроенный цвет текста +AddStampRequest.submit=Отправить #sanitizePDF @@ -520,9 +559,9 @@ addPageNumbers.selectText.3=Позиция addPageNumbers.selectText.4=Стартовый номер addPageNumbers.selectText.5=Страницы для нумерации addPageNumbers.selectText.6=Свой текст -addPageNumbers.customTextDesc=Custom Text -addPageNumbers.numberPagesDesc=Which pages to number, default 'all', also accepts 1-5 or 2,5,9 etc -addPageNumbers.customNumberDesc=Defaults to {n}, also accepts 'Page {n} of {total}', 'Text-{n}', '{filename}-{n} +addPageNumbers.customTextDesc=Пользовательский текст +addPageNumbers.numberPagesDesc=Какие страницы нумеровать, по умолчанию 'все', также принимает 1-5 или 2,5,9 и т.д. +addPageNumbers.customNumberDesc=По умолчанию {n}, также можно использовать 'Страница {n} из {total}', 'Текст-{n}', '{filename}-{n}' addPageNumbers.submit=Добавить номера страниц @@ -563,14 +602,14 @@ autoSplitPDF.submit=Отравить #pipeline -pipeline.title=Pipeline +pipeline.title=Пайплайн #pageLayout pageLayout.title=Многостраничный макет pageLayout.header=Многостраничный макет pageLayout.pagesPerSheet=Страниц на одном листе: -pageLayout.addBorder=Add Borders +pageLayout.addBorder=Добавить границы pageLayout.submit=Отправить @@ -586,11 +625,11 @@ scalePages.submit=Отправить certSign.title=Подписание сертификата certSign.header=Подпишите PDF своим сертификатом (работа в процессе) certSign.selectPDF=Выберите файл PDF для подписи: -certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below. +certSign.jksNote=Примечание: Если ваш тип сертификата не указан ниже, пожалуйста, преобразуйте его в файл хранилища Java Keystore (.jks), используя утилиту командной строки keytool. Затем выберите опцию .jks файла ниже. certSign.selectKey=Выберите файл закрытого ключа (формат PKCS#8, может быть .pem или .der): certSign.selectCert=Выберите файл сертификата (формат X.509, может быть .pem или .der): certSign.selectP12=Выберите файл хранилища ключей PKCS#12 (.p12 или .pfx) (необязательно, если он предоставлен, он должен содержать ваш закрытый ключ и сертификат): -certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore): +certSign.selectJKS=Выберите файл хранилища Java Keystore (.jks или .keystore): certSign.certType=Тип сертификата certSign.password=Введите пароль от хранилища ключей или личного ключа (если есть): certSign.showSig=Показать подпись @@ -611,9 +650,9 @@ removeBlanks.submit=Удалить Пустые #removeAnnotations -removeAnnotations.title=Remove Annotations -removeAnnotations.header=Remove Annotations -removeAnnotations.submit=Remove +removeAnnotations.title=Удалить аннотации +removeAnnotations.header=Удалить аннотации +removeAnnotations.submit=Удалить #compare @@ -623,6 +662,18 @@ compare.document.1=Документ 1 compare.document.2=Документ 2 compare.submit=Сравнить +#BookToPDF +BookToPDF.title=Книги и комиксы в PDF +BookToPDF.header=Конвертировать книгу в PDF +BookToPDF.credit=Используется Calibre +BookToPDF.submit=Конвертировать + +#PDFToBook +PDFToBook.title=PDF в книгу +PDFToBook.header=PDF в книгу +PDFToBook.selectText.1=Формат +PDFToBook.credit=Используется Calibre +PDFToBook.submit=Конвертировать #sign sign.title=Подпись @@ -643,6 +694,7 @@ repair.submit=Ремонт #flatten flatten.title=Сглаживание flatten.header=Сглаживание PDF ов +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Сгладить @@ -717,8 +769,8 @@ addImage.submit=Добавить изображение #merge merge.title=Объединить merge.header=Объединение нескольких PDF-файлов (2+) -merge.sortByName=Sort by name -merge.sortByDate=Sort by date +merge.sortByName=Сортировка по имени +merge.sortByDate=Сортировка по дате merge.submit=Объединить @@ -726,21 +778,34 @@ merge.submit=Объединить pdfOrganiser.title=Организатор страниц pdfOrganiser.header=Организатор PDF-страниц pdfOrganiser.submit=Переупорядочить страницы +pdfOrganiser.mode=Режим +pdfOrganiser.mode.1=Пользовательский порядок страниц +pdfOrganiser.mode.2=Обратный порядок +pdfOrganiser.mode.3=Сортировка дуплекса +pdfOrganiser.mode.4=Сортировка брошюры +pdfOrganiser.mode.5=Сортировка брошюры со стежкой по боковой стороне +pdfOrganiser.mode.6=Разделение на чётные и нечётные страницы +pdfOrganiser.mode.7=Удалить первую +pdfOrganiser.mode.8=Удалить последнюю +pdfOrganiser.mode.9=Удалить первую и последнюю +pdfOrganiser.placeholder=(например, 1,3,2 или 4-8,2,10-12 или 2n-1) #multiTool multiTool.title=Мультиинструмент PDF multiTool.header=Мультиинструмент PDF +multiTool.uploadPrompts=Пожалуйста, загрузите PDF #view pdf -viewPdf.title=View PDF -viewPdf.header=View PDF +viewPdf.title=Просмотреть PDF +viewPdf.header=Просмотреть PDF #pageRemover pageRemover.title=Удаление страниц pageRemover.header=Удаление PDF-страниц pageRemover.pagesToDelete=Страницы для удаления (введите список номеров страниц через запятую): pageRemover.submit=Удалить страницы +pageRemover.placeholder=(например, 1,2,6 или 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Выберите угол поворота (кратный 90 rotate.submit=Повернуть -#merge +#split-pdfs split.title=Разделить PDF split.header=Разделить PDF split.desc.1=Выбранные вами числа — это номера страниц, на которых вы хотите сделать разделение. -split.desc.2=Таким образом, выбор 1,3,7-8 разделит 10-страничный документ на 6 отдельных PDF-файлов с: -split.desc.3=Документ #1: Page 1 -split.desc.4=Документ #2: Page 2 and 3 -split.desc.5=Документ #3: Page 4, 5 and 6 -split.desc.6=Документ #4: Page 7 -split.desc.7=Документ #5: Page 8 -split.desc.8=Документ #6: Page 9 and 10 +split.desc.2=Таким образом, выбор 1,3,7-9 разделит 10-страничный документ на 6 отдельных PDF-файлов с: +split.desc.3=Документ #1: Страница 1 +split.desc.4=Документ #2: Страница 2 и 3 +split.desc.5=Документ #3: Страница 4, 5, 6 aиnd 7 +split.desc.6=Документ #4: Страница 8 +split.desc.7=Документ #5: Страница 9 +split.desc.8=Документ #6: Страница 10 split.splitPages=Введите страницы для разделения: split.submit=Разделить @@ -769,10 +834,10 @@ split.submit=Разделить imageToPDF.title=Изображение в PDF imageToPDF.header=Изображение в PDF imageToPDF.submit=Конвертировать -imageToPDF.selectLabel=Image Fit Options -imageToPDF.fillPage=Fill Page -imageToPDF.fitDocumentToImage=Fit Page to Image -imageToPDF.maintainAspectRatio=Maintain Aspect Ratios +imageToPDF.selectLabel=Выберите режим отображения изображения +imageToPDF.fillPage=Заполнение страницы +imageToPDF.fitDocumentToImage=Подогнать документ под изображение +imageToPDF.maintainAspectRatio=Сохранить пропорции imageToPDF.selectText.2=Автоматический поворот PDF imageToPDF.selectText.3=Многофайловая логика (включена только при работе с несколькими изображениями) imageToPDF.selectText.4=Объединить в один PDF @@ -809,9 +874,9 @@ addPassword.selectText.10=Предотвратить модификацию addPassword.selectText.11=Запретить модификацию аннотаций addPassword.selectText.12=Запретить печать addPassword.selectText.13=Запретить печать разных форматов -addPassword.selectText.14=Owner Password -addPassword.selectText.15=Restricts what can be done with the document once it is opened (Not supported by all readers) -addPassword.selectText.16=Restricts the opening of the document itself +addPassword.selectText.14=Владельческий пароль +addPassword.selectText.15=Ограничивает то, что можно сделать с документом после его открытия (не поддерживается всеми программами чтения) +addPassword.selectText.16=Ограничивает открытие самого документа addPassword.submit=Шифровать @@ -825,9 +890,11 @@ watermark.selectText.4=Поворот (0-360): watermark.selectText.5=widthSpacer (пробел между каждым водяным знаком по горизонтали): watermark.selectText.6=heightSpacer (пробел между каждым водяным знаком по вертикали): watermark.selectText.7=Непрозрачность (0% - 100%): -watermark.selectText.8=Watermark Type: -watermark.selectText.9=Watermark Image: +watermark.selectText.8=Тип водяного знака: +watermark.selectText.9=Изображение водяного знака: watermark.submit=Добавить водяной знак +watermark.type.1=Текст +watermark.type.2=Изображение #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF в PDF/A pdfToPDFA.header=PDF в PDF/A pdfToPDFA.credit=Этот сервис использует OCRmyPDF для преобразования PDF/A pdfToPDFA.submit=Конвертировать +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Конвертировать #PDFToHTML PDFToHTML.title=PDF в HTML PDFToHTML.header=PDF в HTML -PDFToHTML.credit=Этот сервис использует LibreOffice для преобразования файлов. +PDFToHTML.credit=Этот сервис использует pdftohtml для преобразования файлов. PDFToHTML.submit=Конвертировать @@ -919,54 +988,77 @@ PDFToXML.credit=Этот сервис использует LibreOffice для п PDFToXML.submit=Конвертировать #PDFToCSV -PDFToCSV.title=PDF ? CSV -PDFToCSV.header=PDF ? CSV -PDFToCSV.prompt=Choose page to extract table -PDFToCSV.submit=??????? +PDFToCSV.title=PDF в CSV +PDFToCSV.header=PDF в CSV +PDFToCSV.prompt=Выберите страницу для извлечения таблицы +PDFToCSV.submit=Извлечь #split-by-size-or-count -split-by-size-or-count.header=Split PDF by Size or Count -split-by-size-or-count.type.label=Select Split Type -split-by-size-or-count.type.size=By Size -split-by-size-or-count.type.pageCount=By Page Count -split-by-size-or-count.type.docCount=By Document Count -split-by-size-or-count.value.label=Enter Value -split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5) -split-by-size-or-count.submit=Submit +split-by-size-or-count.title=Разделить PDF по размеру или количеству +split-by-size-or-count.header=Разделить PDF по размеру или количеству +split-by-size-or-count.type.label=Выберите тип разделения +split-by-size-or-count.type.size=По размеру +split-by-size-or-count.type.pageCount=По количеству страниц +split-by-size-or-count.type.docCount=По количеству документов +split-by-size-or-count.value.label=Введите значение +split-by-size-or-count.value.placeholder=Введите размер (например, 2MB или 3KB) или количество (например, 5) +split-by-size-or-count.submit=Отправить #overlay-pdfs -overlay-pdfs.header=Overlay PDF Files -overlay-pdfs.baseFile.label=Select Base PDF File -overlay-pdfs.overlayFiles.label=Select Overlay PDF Files -overlay-pdfs.mode.label=Select Overlay Mode -overlay-pdfs.mode.sequential=Sequential Overlay -overlay-pdfs.mode.interleaved=Interleaved Overlay -overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay -overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode) -overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1) -overlay-pdfs.position.label=Select Overlay Position -overlay-pdfs.position.foreground=Foreground -overlay-pdfs.position.background=Background -overlay-pdfs.submit=Submit +overlay-pdfs.header=Наложение файлов PDF +overlay-pdfs.baseFile.label=Выберите основной файл PDF +overlay-pdfs.overlayFiles.label=Выберите налагаемые файлы PDF +overlay-pdfs.mode.label=Выберите режим наложения +overlay-pdfs.mode.sequential=Последовательное наложение +overlay-pdfs.mode.interleaved=Перекрестное наложение +overlay-pdfs.mode.fixedRepeat=Наложение с фиксированным повторением +overlay-pdfs.counts.label=Количество наложений (для режима с фиксированным повторением) +overlay-pdfs.counts.placeholder=Введите через запятую количество повторений (например, 2,3,1) +overlay-pdfs.position.label=Выберите позицию наложения +overlay-pdfs.position.foreground=Поверх основного +overlay-pdfs.position.background=Позади основного +overlay-pdfs.submit=Отправить #split-by-sections -split-by-sections.title=Split PDF by Sections -split-by-sections.header=Split PDF into Sections -split-by-sections.horizontal.label=Horizontal Divisions -split-by-sections.vertical.label=Vertical Divisions -split-by-sections.horizontal.placeholder=Enter number of horizontal divisions -split-by-sections.vertical.placeholder=Enter number of vertical divisions -split-by-sections.submit=Split PDF +split-by-sections.title=Разделить PDF по разделам +split-by-sections.header=Разделить PDF на секции +split-by-sections.horizontal.label=Горизонтальное деление +split-by-sections.vertical.label=Вертикальное деление +split-by-sections.horizontal.placeholder=Введите количество горизонтальных разделов +split-by-sections.vertical.placeholder=Введите количество вертикальных разделов +split-by-sections.submit=Разделить PDF +split-by-sections.merge=Объединить в один PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses -licenses.nav=Licenses -licenses.title=3rd Party Licenses -licenses.header=3rd Party Licenses -licenses.module=Module -licenses.version=Version -licenses.license=License +licenses.nav=Лицензии +licenses.title=Лицензии от третьих сторон +licenses.header=Лицензии от третьих сторон +licenses.module=Модуль +licenses.version=Версия +licenses.license=Лицензия +# error +error.sorry=Извините за проблему! +error.needHelp=Нужна помощь / Нашли проблему? +error.contactTip=Если у вас все еще есть проблемы, не стесняйтесь обращаться к нам за помощью. Вы можете отправить заявку на нашей странице GitHub или связаться с нами через Discord: +error.404.head=404 - Страница не найдена | Ой, мы запутались в коде! +error.404.1=Мы не можем найти страницу, которую вы ищете. +error.404.2=Произошла ошибка +error.github=Отправить заявку на GitHub +error.showStack=Показать стек вызовов +error.copyStack=Скопировать стек вызовов +error.githubSubmit=GitHub - Отправить заявку +error.discordSubmit=Discord - Отправить запрос в поддержку + diff --git a/src/main/resources/messages_sr-Latn-RS.properties b/src/main/resources/messages_sr_LATN_RS.properties similarity index 89% rename from src/main/resources/messages_sr-Latn-RS.properties rename to src/main/resources/messages_sr_LATN_RS.properties index 1cd0eec3..708dc2d8 100644 --- a/src/main/resources/messages_sr-Latn-RS.properties +++ b/src/main/resources/messages_sr_LATN_RS.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=Tačno false=Netačno unknown=Nepoznato save=Sačuvaj +saveToBrowser=Save to Browser close=Zatvori filesSelected=odabrani fajlovi noFavourites=Nema dodatih favorita +downloadComplete=Download Complete bored=Da li ti je dosadno dok čekaš? alphabet=Alfabet downloadPdf=Skini PDF @@ -52,6 +54,22 @@ notAuthenticatedMessage=Korisnik nije autentifikovan. userNotFoundMessage=Korisnik nije pronađen. incorrectPasswordMessage=Trenutna šifra je netačna. usernameExistsMessage=Novi korisnik već postoji +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Nije moguće degradirati ulogu trenutnog korisnika +downgradeCurrentUserLongMessage=Nije moguće unazaditi ulogu trenutnog korisnika. Dakle, trenutni korisnik neće biti prikazan. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### @@ -62,6 +80,8 @@ pipeline.uploadButton=Postavi prilagođeno pipeline.configureButton=Konfiguriši pipeline.defaultOption=Prilagođeno pipeline.submitButton=Pošalji +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Podešavanja ############# settings.title=Podešavanja settings.update=Dostupno ažuriranje +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Verzija aplikacije: settings.downloadOption.title=Odaberite opciju preuzimanja (Za preuzimanje pojedinačnih fajlova bez zip formata): settings.downloadOption.1=Otvori u istom prozoru @@ -102,12 +123,13 @@ settings.downloadOption.3=Preuzmi fajl settings.zipThreshold=Zipuj fajlove kada pređe broj preuzetih fajlova settings.signOut=Odjava settings.accountSettings=Podešavanja naloga - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Promeni pristupne podatke changeCreds.header=Ažurirajte detalje svog naloga -changeCreds.changeUserAndPassword=Koristite podrazumevane prijavne podatke. Unesite novu lozinku (i korisničko ime ako želite) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=Novo korisničko ime changeCreds.oldPassword=Trenutna lozinka changeCreds.newPassword=Nova lozinka @@ -142,14 +164,18 @@ adminUserSettings.header=Podešavanja kontrole korisnika za administratora adminUserSettings.admin=Administrator adminUserSettings.user=Korisnik adminUserSettings.addUser=Dodaj novog korisnika +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Uloge adminUserSettings.role=Uloga adminUserSettings.actions=Akcije adminUserSettings.apiUser=Korisnik s ograničenim API pristupom +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Korisnik samo za web adminUserSettings.demoUser=Demo korisnik (Bez prilagođenih podešavanja) +adminUserSettings.internalApiUser=Internal API User adminUserSettings.forceChange=Prisili korisnika da promeni korisničko ime/lozinku pri prijavi adminUserSettings.submit=Sačuvaj korisnika +adminUserSettings.changeUserRole=Promenite ulogu korisnika ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Prijavite se +login.header=Prijavite se login.signin=Prijavite se login.rememberme=Zapamti me login.invalid=Neispravno korisničko ime ili lozinka. login.locked=Vaš nalog je zaključan. login.signinTitle=Molimo vas da se prijavite +login.ssoSignIn=Prijavite se putem jedinstvene prijave +login.oauth2AutoCreateDisabled=OAUTH2 automatsko kreiranje korisnika je onemogućeno #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Konvertuj u Jednu Stranicu pageExtracter.title=Izdvajanje stranica pageExtracter.header=Izdvajanje stranica pageExtracter.submit=Izdvoji +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Dokument 1 compare.document.2=Dokument 2 compare.submit=Uporedi +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Potpiši @@ -643,6 +694,7 @@ repair.submit=Popravi #flatten flatten.title=Ravnanje flatten.header=Ravnanje PDF fajlova +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Ravnanje @@ -726,11 +778,23 @@ merge.submit=Spajanje pdfOrganiser.title=Organizator stranica pdfOrganiser.header=Organizator stranica u PDF-u pdfOrganiser.submit=Preuredi stranice +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF Multi Alatka multiTool.header=PDF Multi Alatka +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=Prikaz @@ -741,6 +805,7 @@ pageRemover.title=Uklanjanje stranica pageRemover.header=Uklanjanje stranica iz PDF-a pageRemover.pagesToDelete=Stranice za brisanje (Unesite listu brojeva stranica odvojenih zarezima) : pageRemover.submit=Obriši stranice +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Izaberite ugao rotacije (u višestrukim od 90 stepeni): rotate.submit=Rotiraj -#merge +#split-pdfs split.title=Razdvajanje PDF-a split.header=Razdvajanje PDF-a split.desc.1=Brojevi koje izaberete predstavljaju brojeve stranica na kojima želite napraviti razdvajanje -split.desc.2=Na primer, izbor 1,3,7-8 bi razdvojio dokument od 10 stranica u 6 odvojenih PDF-a sa: +split.desc.2=Na primer, izbor 1,3,7-9 bi razdvojio dokument od 10 stranica u 6 odvojenih PDF-a sa: split.desc.3=Dokument #1: Stranica 1 split.desc.4=Dokument #2: Stranice 2 i 3 -split.desc.5=Dokument #3: Stranice 4, 5 i 6 -split.desc.6=Dokument #4: Stranica 7 -split.desc.7=Dokument #5: Stranica 8 -split.desc.8=Dokument #6: Stranice 9 i 10 +split.desc.5=Dokument #3: Stranice 4, 5, 6 i 7 +split.desc.6=Dokument #4: Stranica 8 +split.desc.7=Dokument #5: Stranica 9 +split.desc.8=Dokument #6: Stranice 10 split.splitPages=Unesite stranice za razdvajanje: split.submit=Razdvoji @@ -828,6 +893,8 @@ watermark.selectText.7=Opačitost (0% - 100%): watermark.selectText.8=Tip vodenog žiga: watermark.selectText.9=Slika vodenog žiga: watermark.submit=Dodaj vodeni žig +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF u PDF/A pdfToPDFA.header=PDF u PDF/A pdfToPDFA.credit=Ova usluga koristi OCRmyPDF za konverziju u PDF/A format pdfToPDFA.submit=Konvertuj +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Konvertuj #PDFToHTML PDFToHTML.title=PDF u HTML PDFToHTML.header=PDF u HTML -PDFToHTML.credit=Ova usluga koristi LibreOffice za konverziju fajlova. +PDFToHTML.credit=Ova usluga koristi pdftohtml za konverziju fajlova. PDFToHTML.submit=Konvertuj @@ -925,6 +994,7 @@ PDFToCSV.prompt=Izaberite stranicu za ekstrakciju tabele PDFToCSV.submit=Izvuci #split-by-size-or-count +split-by-size-or-count.title=Razdvoji PDF po veličini ili broju split-by-size-or-count.header=Razdvoji PDF po veličini ili broju split-by-size-or-count.type.label=Izaberite tip razdvajanja split-by-size-or-count.type.size=Po veličini @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertikalne podele split-by-sections.horizontal.placeholder=Unesite broj horizontalnih podele split-by-sections.vertical.placeholder=Unesite broj vertikalnih podele split-by-sections.submit=Razdvoji PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_sv_SE.properties b/src/main/resources/messages_sv_SE.properties index 5908b41c..f621c73c 100644 --- a/src/main/resources/messages_sv_SE.properties +++ b/src/main/resources/messages_sv_SE.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -17,9 +17,11 @@ true=True false=Falskt unknown=Okänt save=Spara +saveToBrowser=Save to Browser close=Stäng filesSelected=filer valda noFavourites=Inga favoriter har lagts till +downloadComplete=Download Complete bored=Utråkad att vänta? alphabet=Alfabet downloadPdf=Ladda ner PDF @@ -52,16 +54,34 @@ notAuthenticatedMessage=User not authenticated. userNotFoundMessage=User not found. incorrectPasswordMessage=Current password is incorrect. usernameExistsMessage=New Username already exists. +invalidUsernameMessage=Invalid username, Username must only contain alphabet characters and numbers. +deleteCurrentUserMessage=Cannot delete currently logged in user. +deleteUsernameExistsMessage=The username does not exist and cannot be deleted. +downgradeCurrentUserMessage=Kan inte nedgradera nuvarande användares roll +downgradeCurrentUserLongMessage=Kan inte nedgradera nuvarande användares roll. Därför kommer den aktuella användaren inte att visas. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) +pipeline.header=Pipeline Menu (Beta) pipeline.uploadButton=Upload Custom pipeline.configureButton=Configure pipeline.defaultOption=Custom pipeline.submitButton=Submit +pipeline.help=Pipeline Help +pipeline.scanHelp=Folder Scanning Help ###################### # Pipeline Options # @@ -94,6 +114,7 @@ navbar.settings=Inställningar ############# settings.title=Inställningar settings.update=Uppdatering tillgänglig +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=Appversion: settings.downloadOption.title=Välj nedladdningsalternativ (för nedladdning av en fil utan zip): settings.downloadOption.1=Öppnas i samma fönster @@ -102,12 +123,13 @@ settings.downloadOption.3=Ladda ner fil settings.zipThreshold=Zip-filer när antalet nedladdade filer överskrider settings.signOut=Sign Out settings.accountSettings=Account Settings - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=Change Credentials changeCreds.header=Update Your Account Details -changeCreds.changeUserAndPassword=You are using default login credentials. Please enter a new password (and username if wanted) +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=New Username changeCreds.oldPassword=Current Password changeCreds.newPassword=New Password @@ -142,14 +164,18 @@ adminUserSettings.header=Admin User Control Settings adminUserSettings.admin=Admin adminUserSettings.user=User adminUserSettings.addUser=Add New User +adminUserSettings.usernameInfo=Username must only contain letters and numbers, no spaces or special characters. adminUserSettings.roles=Roles adminUserSettings.role=Role adminUserSettings.actions=Actions adminUserSettings.apiUser=Limited API User +adminUserSettings.extraApiUser=Additional Limited API User adminUserSettings.webOnlyUser=Web Only User adminUserSettings.demoUser=Demo User (No custom settings) -adminUserSettings.forceChange=Force user to change username/password on login +adminUserSettings.internalApiUser=Internal API User +adminUserSettings.forceChange=Force user to change password on login adminUserSettings.submit=Save User +adminUserSettings.changeUserRole=Ändra användarens roll ############# # HOME-PAGE # @@ -392,6 +418,15 @@ home.AddStampRequest.desc=Add text or add image stamps at set locations AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.PDFToBook.title=PDF to Book +home.PDFToBook.desc=Converts PDF to Book/Comic formats using calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Book to PDF +home.BookToPDF.desc=Converts Books/Comics formats to PDF using calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + ########################### # # # WEB PAGES # @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Sign in +login.header=Sign in login.signin=Sign in login.rememberme=Remember me login.invalid=Invalid username or password. login.locked=Your account has been locked. login.signinTitle=Please sign in +login.ssoSignIn=Logga in via enkel inloggning +login.oauth2AutoCreateDisabled=OAUTH2 Auto-Create User inaktiverad #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Convert To Single Page pageExtracter.title=Extract Pages pageExtracter.header=Extract Pages pageExtracter.submit=Extract +pageExtracter.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) #getPdfInfo @@ -623,6 +662,18 @@ compare.document.1=Dokument 1 compare.document.2=Dokument 2 compare.submit=Jämför +#BookToPDF +BookToPDF.title=Books and Comics to PDF +BookToPDF.header=Book to PDF +BookToPDF.credit=Uses Calibre +BookToPDF.submit=Convert + +#PDFToBook +PDFToBook.title=PDF to Book +PDFToBook.header=PDF to Book +PDFToBook.selectText.1=Format +PDFToBook.credit=Uses Calibre +PDFToBook.submit=Convert #sign sign.title=Signera @@ -643,6 +694,7 @@ repair.submit=Reparera #flatten flatten.title=Platta till flatten.header=Placera PDF-filer +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Platta till @@ -726,11 +778,23 @@ merge.submit=Slå samman pdfOrganiser.title=Sidorganisatör pdfOrganiser.header=PDF-sidorganisatör pdfOrganiser.submit=Ordna om sidor +pdfOrganiser.mode=Mode +pdfOrganiser.mode.1=Custom Page Order +pdfOrganiser.mode.2=Reverse Order +pdfOrganiser.mode.3=Duplex Sort +pdfOrganiser.mode.4=Booklet Sort +pdfOrganiser.mode.5=Side Stitch Booklet Sort +pdfOrganiser.mode.6=Odd-Even Split +pdfOrganiser.mode.7=Remove First +pdfOrganiser.mode.8=Remove Last +pdfOrganiser.mode.9=Remove First and Last +pdfOrganiser.placeholder=(e.g. 1,3,2 or 4-8,2,10-12 or 2n-1) #multiTool multiTool.title=PDF-multiverktyg multiTool.header=PDF Multi-verktyg +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=View PDF @@ -741,6 +805,7 @@ pageRemover.title=Sidborttagare pageRemover.header=PDF Sidborttagning pageRemover.pagesToDelete=Sidor att radera (Ange en kommaseparerad lista med sidnummer) : pageRemover.submit=Ta bort sidor +pageRemover.placeholder=(e.g. 1,2,6 or 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Välj rotationsvinkel (i multipler av 90 grader): rotate.submit=Rotera -#merge +#split-pdfs split.title=Dela upp PDF split.header=Dela upp PDF split.desc.1=Siffrorna du väljer är sidnumret du vill göra en split på -split.desc.2=Som sådan skulle ett val av 1,3,7-8 dela upp ett 10-sidigt dokument i 6 separata PDF-filer med: +split.desc.2=Som sådan skulle ett val av 1,3,7-9 dela upp ett 10-sidigt dokument i 6 separata PDF-filer med: split.desc.3=Dokument #1: Sida 1 split.desc.4=Dokument #2: Sida 2 och 3 -split.desc.5=Dokument #3: Sida 4, 5 och 6 -split.desc.6=Dokument #4: Sida 7 -split.desc.7=Dokument #5: Sida 8 -split.desc.8=Dokument #6: Sida 9 och 10 +split.desc.5=Dokument #3: Sida 4, 5, 6 och 7 +split.desc.6=Dokument #4: Sida 8 +split.desc.7=Dokument #5: Sida 9 +split.desc.8=Dokument #6: Sida 10 split.splitPages=Ange sidor att dela på: split.submit=Dela @@ -828,6 +893,8 @@ watermark.selectText.7=Opacitet (0% - 100%): watermark.selectText.8=Watermark Type: watermark.selectText.9=Watermark Image: watermark.submit=Lägg till vattenstämpel +watermark.type.1=Text +watermark.type.2=Image #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF till PDF/A pdfToPDFA.header=PDF till PDF/A pdfToPDFA.credit=Denna tjänst använder OCRmyPDF för PDF/A-konvertering pdfToPDFA.submit=Konvertera +pdfToPDFA.tip=Currently does not work for multiple inputs at once +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Konvertera #PDFToHTML PDFToHTML.title=PDF till HTML PDFToHTML.header=PDF till HTML -PDFToHTML.credit=Denna tjänst använder LibreOffice för filkonvertering. +PDFToHTML.credit=Denna tjänst använder pdftohtml för filkonvertering. PDFToHTML.submit=Konvertera @@ -925,6 +994,7 @@ PDFToCSV.prompt=Choose page to extract table PDFToCSV.submit=Navvit #split-by-size-or-count +split-by-size-or-count.title=Split PDF by Size or Count split-by-size-or-count.header=Split PDF by Size or Count split-by-size-or-count.type.label=Select Split Type split-by-size-or-count.type.size=By Size @@ -959,6 +1029,15 @@ split-by-sections.vertical.label=Vertical Divisions split-by-sections.horizontal.placeholder=Enter number of horizontal divisions split-by-sections.vertical.placeholder=Enter number of vertical divisions split-by-sections.submit=Split PDF +split-by-sections.merge=Merge Into One PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses @@ -970,3 +1049,16 @@ licenses.version=Version licenses.license=License +# error +error.sorry=Sorry for the issue! +error.needHelp=Need help / Found an issue? +error.contactTip=If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord: +error.404.head=404 - Page Not Found | Oops, we tripped in the code! +error.404.1=We can't seem to find the page you're looking for. +error.404.2=Something went wrong +error.github=Submit a ticket on GitHub +error.showStack=Show Stack Trace +error.copyStack=Copy Stack Trace +error.githubSubmit=GitHub - Submit a ticket +error.discordSubmit=Discord - Submit Support post + diff --git a/src/main/resources/messages_tr_TR.properties b/src/main/resources/messages_tr_TR.properties index 618e967e..8484e555 100644 --- a/src/main/resources/messages_tr_TR.properties +++ b/src/main/resources/messages_tr_TR.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -11,15 +11,17 @@ imgPrompt=Resim(leri) seçin genericSubmit=Gönder processTimeWarning=Uyarı: Bu işlem, dosya boyutuna bağlı olarak bir dakikaya kadar sürebilir. pageOrderPrompt=Özel Sayfa Sırası (Virgülle ayrılmış sayfa numaraları veya 2n+1 gibi bir fonksiyon girin) : -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : +pageSelectionPrompt=Özel Sayfa Seçimi (1,5,6 sayfa numaralarının virgülle ayrılmış bir listesini veya 2n+1 gibi bir fonksiyon girin) : goToPage=Git true=Doğru false=Yanlış unknown=Bilinmeyen save=Kaydet +saveToBrowser=Tarayıcıya Kaydet close=Kapat filesSelected=dosya seçildi noFavourites=Favori eklenmedi +downloadComplete=İndirme Tamamlandı bored=Sıkıldınız mı? alphabet=Alfabe downloadPdf=PDF İndir @@ -43,38 +45,56 @@ red=Kırmızı green=Yeşil blue=Mavi custom=Özel -WorkInProgess=Work in progress, May not work or be buggy, Please report any problems! -poweredBy=Powered by -yes=Yes -no=No +WorkInProgess=Çalışmalar devam ediyor, Çalışmayabilir veya hatalı olabilir, Lütfen herhangi bir sorunu bildirin! +poweredBy=Tarafından desteklenmektedir +yes=Evet +no=Hayır changedCredsMessage=Bilgiler değiştirildi! notAuthenticatedMessage=Kullanıcı doğrulanmadı. userNotFoundMessage=Kullanıcı bulunamadı. incorrectPasswordMessage=Mevcut şifre yanlış. usernameExistsMessage=Yeni Kullanıcı Adı zaten var. +invalidUsernameMessage=Geçersiz kullanıcı adı, Kullanıcı adı yalnızca alfabe karakterleri ve sayılar içermelidir. +deleteCurrentUserMessage=Şu anda oturum açmış olan kullanıcı silinemiyor. +deleteUsernameExistsMessage=Kullanıcı adı mevcut değil ve silinemez. +downgradeCurrentUserMessage=Mevcut kullanıcının rolü düşürülemiyor +downgradeCurrentUserLongMessage=Mevcut kullanıcının rolü düşürülemiyor. Bu nedenle, mevcut kullanıcı gösterilmeyecektir. +error=Hata +oops=Tüh! +help=Yardım +goHomepage=Anasayfa'ya git +joinDiscord=Discord sunucumuza katılın +seeDockerHub=Docker Hub'a bakın +visitGithub=Github Deposunu Ziyaret Edin +donate=Bağış Yapın +color=Renk +sponsor=Bağış + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) -pipeline.uploadButton=Upload Custom -pipeline.configureButton=Configure -pipeline.defaultOption=Custom -pipeline.submitButton=Submit +pipeline.header=Çoklu İşlemler Menü (Beta) +pipeline.uploadButton=Upload edin +pipeline.configureButton=Yapılandır +pipeline.defaultOption=Özel +pipeline.submitButton=Gönder +pipeline.help=Çoklu İşlemler Yardım +pipeline.scanHelp=Klasör Tarama Yardımı ###################### # Pipeline Options # ###################### -pipelineOptions.header=Pipeline Configuration -pipelineOptions.pipelineNameLabel=Pipeline Name -pipelineOptions.saveSettings=Save Operation Settings -pipelineOptions.pipelineNamePrompt=Enter pipeline name here -pipelineOptions.selectOperation=Select Operation -pipelineOptions.addOperationButton=Add operation -pipelineOptions.pipelineHeader=Pipeline: -pipelineOptions.saveButton=Download -pipelineOptions.validateButton=Validate +pipelineOptions.header=Çoklu İşlemler Yapılandırma +pipelineOptions.pipelineNameLabel=Çoklu İşlemler İsim +pipelineOptions.saveSettings=Ayarları Kaydet +pipelineOptions.pipelineNamePrompt=Buraya isim girin +pipelineOptions.selectOperation=İşlem Seçin +pipelineOptions.addOperationButton=İşlem ekle +pipelineOptions.pipelineHeader=Çoklu İşlemler: +pipelineOptions.saveButton=İndir +pipelineOptions.validateButton=Doğrula @@ -94,6 +114,7 @@ navbar.settings=Ayarlar ############# settings.title=Ayarlar settings.update=Güncelleme mevcut +settings.updateAvailable={0} mevcut kurulu sürümdür. Yeni bir sürüm ({1}) mevcuttur. settings.appVersion=Uygulama Sürümü: settings.downloadOption.title=İndirme seçeneği seçin (Zip olmayan tek dosya indirmeler için): settings.downloadOption.1=Aynı pencerede aç @@ -102,12 +123,13 @@ settings.downloadOption.3=Dosyayı indir settings.zipThreshold=İndirilen dosya sayısı şu değeri aştığında zip dosyası oluştur: settings.signOut=Oturumu Kapat settings.accountSettings=Hesap Ayarları - - +settings.bored.help=Paskalya yumurtası oyunu etkinleştirir +settings.cacheInputs.name=Form girdilerini kaydet +settings.cacheInputs.help=Gelecekteki çalıştırmalar için önceden kullanılan girdileri saklamayı etkinleştirin changeCreds.title=Giriş Bilgilerini Değiştir changeCreds.header=Hesap Detaylarınızı Güncelleyin -changeCreds.changeUserAndPassword=Varsayılan giriş bilgilerini kullanıyorsunuz. Lütfen yeni bir şifre (ve istenirse kullanıcı adı) girin +changeCreds.changePassword=You are using default login credentials. Please enter a new password changeCreds.newUsername=Yeni Kullanıcı Adı changeCreds.oldPassword=Mevcut Şifre changeCreds.newPassword=Yeni Şifre @@ -142,25 +164,29 @@ adminUserSettings.header=Yönetici Kullanıcı Kontrol Ayarları adminUserSettings.admin=Yönetici adminUserSettings.user=Kullanıcı adminUserSettings.addUser=Yeni Kullanıcı Ekle +adminUserSettings.usernameInfo=Kullanıcı adı yalnızca harf ve rakamlardan oluşmalı, boşluk veya özel karakter içermemelidir. adminUserSettings.roles=Roller adminUserSettings.role=Rol adminUserSettings.actions=Eylemler adminUserSettings.apiUser=Sınırlı API Kullanıcısı +adminUserSettings.extraApiUser=Ek Sınırlı API Kullanıcısı adminUserSettings.webOnlyUser=Sadece Web Kullanıcısı -adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.demoUser=Demo Kullanıcısı (Özel ayar yok) +adminUserSettings.internalApiUser=Dahili API Kullanıcısı adminUserSettings.forceChange=Kullanıcının girişte kullanıcı adı/şifre değiştirmesini zorla adminUserSettings.submit=Kullanıcıyı Kaydet +adminUserSettings.changeUserRole=Kullanıcı rolünü değiştir ############# # HOME-PAGE # ############# home.desc=Yerel olarak barındırılan tüm PDF ihtiyaçlarınız için tek durak noktanız. -home.searchBar=Search for features... +home.searchBar=Özellikleri arayın... -home.viewPdf.title=View PDF -home.viewPdf.desc=View, annotate, add text or images -viewPdf.tags=view,read,annotate,text,image +home.viewPdf.title=PDF Görüntüle +home.viewPdf.desc=Görüntüleyin, açıklama ekleyin, metin veya resim ekleyin +viewPdf.tags=görüntüle,oku,açıklama ekle,metin,görüntü home.multiTool.title=PDF Çoklu Araç home.multiTool.desc=Birleştir, Döndür, Yeniden Düzenle ve Sayfaları Kaldır @@ -284,9 +310,9 @@ home.removeBlanks.title=Boş Sayfaları Kaldır home.removeBlanks.desc=Bir belgeden boş sayfaları tespit eder ve kaldırır removeBlanks.tags=temizle,sadeleştir,içeriksiz,düzenle -home.removeAnnotations.title=Remove Annotations -home.removeAnnotations.desc=Removes all comments/annotations from a PDF -removeAnnotations.tags=comments,highlight,notes,markup,remove +home.removeAnnotations.title=Ek Açıklamaları Kaldır +home.removeAnnotations.desc=PDF'deki tüm yorumları/açıklamaları kaldırır +removeAnnotations.tags=yorumlar,vurgulama,notlar,işaretleme,kaldırma home.compare.title=Karşılaştır home.compare.desc=2 PDF Belgesi arasındaki farkları karşılaştırır ve gösterir @@ -304,8 +330,8 @@ home.scalePages.title=Sayfa boyutunu/ölçeğini ayarla home.scalePages.desc=Bir sayfanın ve/veya içeriğinin boyutunu/ölçeğini değiştirir scalePages.tags=boyutlandır,değiştir,boyut,uyarla -home.pipeline.title=Hattı (İleri Seviye) -home.pipeline.desc=Hattı betikleri tanımlayarak PDF'lere birden fazla işlemi çalıştır +home.pipeline.title=Çoklu İşlemler (İleri Seviye) +home.pipeline.desc=Çoklu İşlemler tanımlayarak PDF'lere birden fazla işlemi çalıştır pipeline.tags=otomatikleştir,sıralı,betikli,toplu-işlem home.add-page-numbers.title=Sayfa Numaraları Ekle @@ -369,27 +395,36 @@ home.autoRedact.title=Otomatik Karartma home.autoRedact.desc=Giriş metnine dayanarak bir PDF'teki metni Otomatik Karartır (Redakte) autoRedact.tags=Karart,Gizle,karartma,siyah,markör,gizli -home.tableExtraxt.title=PDF to CSV -home.tableExtraxt.desc=Extracts Tables from a PDF converting it to CSV -tableExtraxt.tags=CSV,Table Extraction,extract,convert +home.tableExtraxt.title=PDF'den CSV'ye +home.tableExtraxt.desc=PDF'den Tabloları çıkarır ve CSV'ye dönüştürür +tableExtraxt.tags=CSV, Tablo Çıkarma, ayıklama, dönüştürme -home.autoSizeSplitPDF.title=Auto Split by Size/Count -home.autoSizeSplitPDF.desc=Split a single PDF into multiple documents based on size, page count, or document count -autoSizeSplitPDF.tags=pdf,split,document,organization +home.autoSizeSplitPDF.title=Boyut/Sayıya Göre Otomatik Bölme +home.autoSizeSplitPDF.desc=Tek bir PDF'yi boyut, sayfa sayısı veya belge sayısına göre birden fazla belgeye bölün +autoSizeSplitPDF.tags=pdf,bölme,belge,organizasyon -home.overlay-pdfs.title=Overlay PDFs -home.overlay-pdfs.desc=Overlays PDFs on-top of another PDF -overlay-pdfs.tags=Overlay +home.overlay-pdfs.title=PDF'leri Bindirme +home.overlay-pdfs.desc=PDF'leri başka bir PDF'nin üzerine bindirir +overlay-pdfs.tags=Bindirme -home.split-by-sections.title=Split PDF by Sections -home.split-by-sections.desc=Divide each page of a PDF into smaller horizontal and vertical sections -split-by-sections.tags=Section Split, Divide, Customize +home.split-by-sections.title=PDF'yi Bölümlere Ayırma +home.split-by-sections.desc=PDF'nin her sayfasını daha küçük yatay ve dikey bölümlere ayırın +split-by-sections.tags=Bölümlere Ayırma, Bölme, Özelleştirme -home.AddStampRequest.title=Add Stamp to PDF -home.AddStampRequest.desc=Add text or add image stamps at set locations -AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.AddStampRequest.title=PDF'ye Damga Ekleme +home.AddStampRequest.desc=Belirlenen konumlara metin veya resim damgaları ekleyin +AddStampRequest.tags=Damga, Görüntü ekle, Görüntüyü ortala, Filigran, PDF, Göm, Özelleştir + + +home.PDFToBook.title=PDF'den Kitaba +home.PDFToBook.desc=Calibre kullanarak PDF'yi Kitap/Komik formatlarına dönüştürür +PDFToBook.tags=Kitap,Çizgi Roman,Calibre,Dönüştür,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf + +home.BookToPDF.title=Kitaptan PDF'ye +home.BookToPDF.desc=calibre kullanarak Kitap/Karikatür formatlarını PDF'ye dönüştürür +BookToPDF.tags=Kitap,Çizgi Roman,Calibre,Dönüştür,manga,amazon,kindle,epub,mobi,azw3,docx,rtf,txt,html,lit,fb2,pdb,lrf ########################### @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=Giriş Yap +login.header=Giriş Yap login.signin=Giriş Yap login.rememberme=Beni hatırla login.invalid=Geçersiz kullanıcı adı veya şifre. login.locked=Hesabınız kilitlendi. login.signinTitle=Lütfen giriş yapınız. +login.ssoSignIn=Tek Oturum Açma ile Giriş Yap +login.oauth2AutoCreateDisabled=OAUTH2 Otomatik Oluşturma Kullanıcı Devre Dışı Bırakıldı #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=Tek Sayfaya Dönüştür pageExtracter.title=Sayfaları Çıkar pageExtracter.header=Sayfaları Çıkar pageExtracter.submit=Çıkar +pageExtracter.placeholder=(örneğin 1,2,8 veya 4,7,12-16 ya da 2n-1) #getPdfInfo @@ -467,37 +506,37 @@ HTMLToPDF.header=HTML'den PDF'e HTMLToPDF.help=HTML dosyalarını ve html/css/görsel vb. içeren ZIP'leri kabul eder HTMLToPDF.submit=Dönüştür HTMLToPDF.credit=WeasyPrint Kullanıyor -HTMLToPDF.zoom=Zoom level for displaying the website. -HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default) -HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default) -HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default) -HTMLToPDF.printBackground=Render the background of websites. -HTMLToPDF.defaultHeader=Enable Default Header (Name and page number) -HTMLToPDF.cssMediaType=Change the CSS media type of the page. -HTMLToPDF.none=None -HTMLToPDF.print=Print -HTMLToPDF.screen=Screen +HTMLToPDF.zoom=Web sitesini görüntülemek için yakınlaştırma düzeyi. +HTMLToPDF.pageWidth=Sayfanın santimetre cinsinden genişliği. (Varsayılan olarak boş) +HTMLToPDF.pageHeight=Sayfanın santimetre cinsinden yüksekliği. (Varsayılan olarak boş) +HTMLToPDF.marginTop=Sayfanın milimetre cinsinden üst kenar boşluğu. (Varsayılan olarak boş) +HTMLToPDF.marginBottom=Sayfanın milimetre cinsinden alt kenar boşluğu. (Varsayılan olarak boş) +HTMLToPDF.marginLeft=Sayfanın milimetre cinsinden sol kenar boşluğu. (Varsayılan olarak boş) +HTMLToPDF.marginRight=Sayfanın milimetre cinsinden sağ kenar boşluğu. (Varsayılan olarak boş) +HTMLToPDF.printBackground=Web sitelerinin arka planını oluşturun. +HTMLToPDF.defaultHeader=Varsayılan Üstbilgiyi Etkinleştir (Ad ve sayfa numarası) +HTMLToPDF.cssMediaType=Sayfanın CSS ortam türünü değiştirin. +HTMLToPDF.none=Hiçbiri +HTMLToPDF.print=Yazdır +HTMLToPDF.screen=Ekran #AddStampRequest -AddStampRequest.header=Stamp PDF -AddStampRequest.title=Stamp PDF -AddStampRequest.stampType=Stamp Type -AddStampRequest.stampText=Stamp Text -AddStampRequest.stampImage=Stamp Image -AddStampRequest.alphabet=Alphabet -AddStampRequest.fontSize=Font/Image Size -AddStampRequest.rotation=Rotation -AddStampRequest.opacity=Opacity -AddStampRequest.position=Position -AddStampRequest.overrideX=Override X Coordinate -AddStampRequest.overrideY=Override Y Coordinate -AddStampRequest.customMargin=Custom Margin -AddStampRequest.customColor=Custom Text Color -AddStampRequest.submit=Submit +AddStampRequest.header=Damga PDF +AddStampRequest.title=Damga PDF +AddStampRequest.stampType=Damga Türü +AddStampRequest.stampText=Damga Metni +AddStampRequest.stampImage=Damga Resmi +AddStampRequest.alphabet=Alfabe +AddStampRequest.fontSize=Yazı Tipi/Görüntü Boyutu +AddStampRequest.rotation=Döndürme +AddStampRequest.opacity=Opaklık +AddStampRequest.position=Konum +AddStampRequest.overrideX=X Koordinatını geçersiz kıl +AddStampRequest.overrideY=Y Koordinatını Geçersiz Kıl +AddStampRequest.customMargin=Özel Kenar Boşluğu +AddStampRequest.customColor=Özel Metin Rengi +AddStampRequest.submit=Gönder #sanitizePDF @@ -563,7 +602,7 @@ autoSplitPDF.submit=Gönder #pipeline -pipeline.title=Pipeline +pipeline.title=Çoklu İşlemler #pageLayout @@ -586,11 +625,11 @@ scalePages.submit=Gönder certSign.title=Sertifika İmzalama certSign.header=Sertifikanızla bir PDF imzalayın (Devam eden iş) certSign.selectPDF=İmzalamak için bir PDF Dosyası seçin: -certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below. +certSign.jksNote=Note: Sertifika türünüz aşağıda listelenmemişse, lütfen keytool komut satırı aracını kullanarak sertifikanızı bir Java Keystore (.jks) dosyasına dönüştürün. Ardından, aşağıdaki .jks dosyası seçeneğini seçin. certSign.selectKey=Özel Anahtar Dosyanızı Seçin (PKCS#8 formatında, .pem veya .der olabilir): certSign.selectCert=Sertifika Dosyanızı Seçin (X.509 formatında, .pem veya .der olabilir): certSign.selectP12=PKCS#12 Anahtar Deposu Dosyanızı Seçin (.p12 veya .pfx) (İsteğe bağlı, sağlanırsa, özel anahtarınızı ve sertifikanızı içermelidir): -certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore): +certSign.selectJKS=Java Keystore Dosyanızı (.jks veya .keystore) seçin: certSign.certType=Sertifika Türü certSign.password=Anahtar Deposu veya Özel Anahtar Şifrenizi Girin (Varsa): certSign.showSig=İmzayı Göster @@ -611,9 +650,9 @@ removeBlanks.submit=Boşları Kaldır #removeAnnotations -removeAnnotations.title=Remove Annotations -removeAnnotations.header=Remove Annotations -removeAnnotations.submit=Remove +removeAnnotations.title=Ek Açıklamaları Kaldır +removeAnnotations.header=Ek Açıklamaları Kaldır +removeAnnotations.submit=Kaldır #compare @@ -623,6 +662,18 @@ compare.document.1=Belge 1 compare.document.2=Belge 2 compare.submit=Karşılaştır +#BookToPDF +BookToPDF.title=Kitapları ve Çizgi Romanları PDF'e Dönüştürme +BookToPDF.header=Kitaptan PDF'ye +BookToPDF.credit=Kalibre Kullanır +BookToPDF.submit=Dönüştür + +#PDFToBook +PDFToBook.title=PDF'den Kitaba +PDFToBook.header=PDF'den Kitaba +PDFToBook.selectText.1=Format biçimi +PDFToBook.credit=Kalibre Kullanır +PDFToBook.submit=Dönüştür #sign sign.title=İmzala @@ -643,6 +694,7 @@ repair.submit=Onar #flatten flatten.title=Düzleştir flatten.header=PDF'leri Düzleştir +flatten.flattenOnlyForms=Flatten only forms flatten.submit=Düzleştir @@ -726,21 +778,34 @@ merge.submit=Birleştir pdfOrganiser.title=Sayfa Organizatörü pdfOrganiser.header=PDF Sayfa Organizatörü pdfOrganiser.submit=Sayfaları Yeniden Düzenle +pdfOrganiser.mode=Mod +pdfOrganiser.mode.1=Özel Sayfa Düzeni +pdfOrganiser.mode.2=Ters Sıralama +pdfOrganiser.mode.3=Çift Taraflı Sıralama +pdfOrganiser.mode.4=Kitapçık Sıralama +pdfOrganiser.mode.5=Yandan Dikişli Kitapçık Sıralama +pdfOrganiser.mode.6=Tek-Çift Ayrımı +pdfOrganiser.mode.7=İlk Önce Kaldır +pdfOrganiser.mode.8=Sonuncuyu Kaldır +pdfOrganiser.mode.9=İlk ve Sonu Kaldır +pdfOrganiser.placeholder=(örn. 1,3,2 veya 4-8,2,10-12 veya 2n-1) #multiTool multiTool.title=PDF Çoklu Araç multiTool.header=PDF Çoklu Araç +multiTool.uploadPrompts=Lütfen PDF Yükleyin #view pdf -viewPdf.title=View PDF -viewPdf.header=View PDF +viewPdf.title=PDF Görüntüle +viewPdf.header=PDF Görüntüle #pageRemover pageRemover.title=Sayfa Silici pageRemover.header=PDF Sayfa silici pageRemover.pagesToDelete=Silinmesi gereken sayfalar (Virgülle ayrılmış sayfa numaraları listesi girin): pageRemover.submit=Sayfaları Sil +pageRemover.placeholder=(örn. 1,2,6 veya 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=Döndürme açısını seçin (90 derecenin katları olarak): rotate.submit=Döndür -#merge +#split-pdfs split.title=PDF Ayır split.header=PDF Ayır split.desc.1=Seçtiğiniz numaralar, bir ayrım yapmak istediğiniz sayfa numarasıdır -split.desc.2=Bu nedenle, 1,3,7-8 seçmek 10 sayfalı bir belgeyi şunlarla 6 ayrı PDF'e böler: +split.desc.2=Bu nedenle, 1,3,7-9 seçmek 10 sayfalı bir belgeyi şunlarla 6 ayrı PDF'e böler: split.desc.3=Belge #1: Sayfa 1 split.desc.4=Belge #2: Sayfa 2 ve 3 -split.desc.5=Belge #3: Sayfa 4, 5 ve 6 -split.desc.6=Belge #4: Sayfa 7 -split.desc.7=Belge #5: Sayfa 8 -split.desc.8=Belge #6: Sayfa 9 ve 10 +split.desc.5=Belge #3: Sayfa 4, 5, 6 ve 7 +split.desc.6=Belge #4: Sayfa 8 +split.desc.7=Belge #5: Sayfa 9 +split.desc.8=Belge #6: Sayfa 10 split.splitPages=Ayrılacak sayfaları girin: split.submit=Ayır @@ -828,6 +893,8 @@ watermark.selectText.7=Opaklık (0% - 100%): watermark.selectText.8=Filigran Türü: watermark.selectText.9=Filigran Resmi: watermark.submit=Filigran Ekle +watermark.type.1=Metin +watermark.type.2=Resim #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF'den PDF/A'ya pdfToPDFA.header=PDF'den PDF/A'ya pdfToPDFA.credit=Bu hizmet PDF/A dönüşümü için OCRmyPDF kullanır pdfToPDFA.submit=Dönüştür +pdfToPDFA.tip=Şu anda aynı anda birden fazla giriş için çalışmıyor +pdfToPDFA.outputFormat=Çıkış formatı #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=Dönüştür #PDFToHTML PDFToHTML.title=PDF'den HTML'e PDFToHTML.header=PDF'den HTML'e -PDFToHTML.credit=Bu hizmet dosya dönüşümü için LibreOffice kullanır. +PDFToHTML.credit=Bu hizmet dosya dönüşümü için pdftohtml kullanır. PDFToHTML.submit=Dönüştür @@ -919,54 +988,77 @@ PDFToXML.credit=Bu hizmet dosya dönüşümü için LibreOffice kullanır. PDFToXML.submit=Dönüştür #PDFToCSV -PDFToCSV.title=PDF to CSV -PDFToCSV.header=PDF to CSV -PDFToCSV.prompt=Choose page to extract table -PDFToCSV.submit=Extract +PDFToCSV.title=PDF'den CSV'ye +PDFToCSV.header=PDF'den CSV'ye +PDFToCSV.prompt=Tabloyu çıkarmak için sayfa seçin +PDFToCSV.submit=Çıkart #split-by-size-or-count -split-by-size-or-count.header=Split PDF by Size or Count -split-by-size-or-count.type.label=Select Split Type -split-by-size-or-count.type.size=By Size -split-by-size-or-count.type.pageCount=By Page Count -split-by-size-or-count.type.docCount=By Document Count -split-by-size-or-count.value.label=Enter Value -split-by-size-or-count.value.placeholder=Enter size (e.g., 2MB or 3KB) or count (e.g., 5) -split-by-size-or-count.submit=Submit +split-by-size-or-count.title=PDF'yi Boyuta veya Sayıya Göre Bölme +split-by-size-or-count.header=PDF'yi Boyuta veya Sayıya Göre Bölme +split-by-size-or-count.type.label=Bölme Türünü Seçin +split-by-size-or-count.type.size=Boyuta Göre +split-by-size-or-count.type.pageCount=Sayfa Sayısına Göre +split-by-size-or-count.type.docCount=Belge Sayısına Göre +split-by-size-or-count.value.label=Değer Girin +split-by-size-or-count.value.placeholder=Boyutu (örn. 2MB veya 3KB) veya sayıyı (örn. 5) girin +split-by-size-or-count.submit=Gönder #overlay-pdfs -overlay-pdfs.header=Overlay PDF Files -overlay-pdfs.baseFile.label=Select Base PDF File -overlay-pdfs.overlayFiles.label=Select Overlay PDF Files -overlay-pdfs.mode.label=Select Overlay Mode -overlay-pdfs.mode.sequential=Sequential Overlay -overlay-pdfs.mode.interleaved=Interleaved Overlay -overlay-pdfs.mode.fixedRepeat=Fixed Repeat Overlay -overlay-pdfs.counts.label=Overlay Counts (for Fixed Repeat Mode) -overlay-pdfs.counts.placeholder=Enter comma-separated counts (e.g., 2,3,1) -overlay-pdfs.position.label=Select Overlay Position -overlay-pdfs.position.foreground=Foreground -overlay-pdfs.position.background=Background -overlay-pdfs.submit=Submit +overlay-pdfs.header=PDF Dosyalarını Bindirme +overlay-pdfs.baseFile.label=Temel PDF Dosyasını Seçin +overlay-pdfs.overlayFiles.label=İkinci PDF Dosyalarını Seçin +overlay-pdfs.mode.label=Bindirme Modunu Seçin +overlay-pdfs.mode.sequential=Sıralı Bindirme +overlay-pdfs.mode.interleaved=Serpiştirilmiş Bindirme +overlay-pdfs.mode.fixedRepeat=Sabit Tekrar Bindirme +overlay-pdfs.counts.label=Bindirme Sayıları (Sabit Tekrar Modu için) +overlay-pdfs.counts.placeholder=Virgülle ayrılmış sayıları girin (örn. 2,3,1) +overlay-pdfs.position.label=Bindirme Konumunu Seçin +overlay-pdfs.position.foreground=Ön plan +overlay-pdfs.position.background=Arka plan +overlay-pdfs.submit=Gönder #split-by-sections -split-by-sections.title=Split PDF by Sections -split-by-sections.header=Split PDF into Sections -split-by-sections.horizontal.label=Horizontal Divisions -split-by-sections.vertical.label=Vertical Divisions -split-by-sections.horizontal.placeholder=Enter number of horizontal divisions -split-by-sections.vertical.placeholder=Enter number of vertical divisions -split-by-sections.submit=Split PDF +split-by-sections.title=PDF'yi Bölümlere Ayırma +split-by-sections.header=PDF'yi Bölümlere Ayırma +split-by-sections.horizontal.label=Yatay Bölümler +split-by-sections.vertical.label=Dikey Bölümler +split-by-sections.horizontal.placeholder=Yatay bölme sayısını girin +split-by-sections.vertical.placeholder=Dikey bölme sayısını girin +split-by-sections.submit=PDF'yi Böl +split-by-sections.merge=Bir PDF'de Birleştirin + + +#printFile +printFile.title=Dosya Yazdır +printFile.header=Dosyayı Yazıcıya Yazdır +printFile.selectText.1=Yazdırılacak Dosyayı Seçin +printFile.selectText.2=Yazıcı Adını Girin +printFile.submit=Yazdır #licenses -licenses.nav=Licenses -licenses.title=3rd Party Licenses -licenses.header=3rd Party Licenses -licenses.module=Module -licenses.version=Version -licenses.license=License +licenses.nav=Lisanslar +licenses.title=3. Taraf Lisansları +licenses.header=3. Taraf Lisansları +licenses.module=Modül +licenses.version=Versiyon +licenses.license=Lisans +# error +error.sorry=Sorun için özür dileriz! +error.needHelp=Yardıma mı ihtiyacınız var / Bir sorun mu buldunuz? +error.contactTip=Hala sorun yaşıyorsanız, yardım için bize ulaşmaktan çekinmeyin. GitHub sayfamızdan bir bilet gönderebilir veya Discord üzerinden bizimle iletişime geçebilirsiniz: +error.404.head=404 - Sayfa Bulunamadı | Tüh, kodda takıldık! +error.404.1=Aradığınız sayfayı bulamıyoruz. +error.404.2=Bir şeyler ters gitti +error.github=GitHub üzerinden bir hata bildirin +error.showStack=Yığın İzlemesini Göster +error.copyStack=Yığın İzini Kopyala +error.githubSubmit=GitHub - Hata gönderin +error.discordSubmit=Discord - Destek gönderisi gönderin + diff --git a/src/main/resources/messages_uk_UA.properties b/src/main/resources/messages_uk_UA.properties new file mode 100644 index 00000000..1ace290c --- /dev/null +++ b/src/main/resources/messages_uk_UA.properties @@ -0,0 +1,1064 @@ +########### +# Generic # +########### +# the direction that the language is written (ltr=left to right, rtl = right to left) +language.direction=ltr + +pdfPrompt=Оберіть PDF(и) +multiPdfPrompt=Оберіть PDFи (2+) +multiPdfDropPrompt=Оберіть (або перетягніть) всі необхідні PDFи +imgPrompt=Оберіть зображення(я) +genericSubmit=Надіслати +processTimeWarning=Увага: Цей процес може тривати до хвилини в залежності від розміру файлу. +pageOrderPrompt=Порядок сторінок (введіть список номерів сторінок через кому): +pageSelectionPrompt=Користувацький вибір сторінки (введіть список номерів сторінок через кому 1,5,6 або функції типу 2n+1) : +goToPage=Вперед +true=Правда +false=Брехня +unknown=Невідомо +save=Зберегти +saveToBrowser=Зберегти в браузері +close=Закрити +filesSelected=файлів обрано +noFavourites=Немає вибраного +downloadComplete=Завантаження завершено +bored=Нудно чекати? +alphabet=Алфавіт +downloadPdf=Завантажити PDF +text=Текст +font=Шрифт +selectFillter=-- Вибрати -- +pageNum=номер сторінки +sizes.small=Малий +sizes.medium=Середній +sizes.large=Великий +sizes.x-large=Дуже великий +error.pdfPassword=Документ PDF захищено паролем, і пароль не був наданий або був невірним +delete=Видалити +username=Ім'я користувача +password=Пароль +welcome=Ласкаво просимо +property=Властивість +black=Чорний +white=Білий +red=Червоний +green=Зелений +blue=Синій +custom=Звичай... +WorkInProgess=Робота триває, може не працювати або глючити, будь ласка, повідомляйте про будь-які проблеми! +poweredBy=Працює на +yes=Так +no=Ні +changedCredsMessage=Облікові дані змінено! +notAuthenticatedMessage=Користувач не пройшов перевірку автентичності. +userNotFoundMessage=Користувача не знайдено. +incorrectPasswordMessage=Поточний пароль невірний. +usernameExistsMessage=Нове ім'я користувача вже існує. +invalidUsernameMessage=Недійсне ім'я користувача, Ім'я користувача повинно містити тільки літери алфавіту та цифри. +deleteCurrentUserMessage=Неможливо видалити користувача, який увійшов в систему. +deleteUsernameExistsMessage=Ім'я користувача не існує і не може бути видалено. +downgradeCurrentUserMessage=Неможливо понизити роль поточного користувача +downgradeCurrentUserLongMessage=Неможливо понизити роль поточного користувача. Отже, поточний користувач не відображатиметься. +error=Error +oops=Oops! +help=Help +goHomepage=Go to Homepage +joinDiscord=Join our Discord server +seeDockerHub=See Docker Hub +visitGithub=Visit Github Repository +donate=Donate +color=Color +sponsor=Sponsor + + + +############### +# Pipeline # +############### +pipeline.header=Меню конвеєрної обробки (Бета) +pipeline.uploadButton=Завантажити Користувацький +pipeline.configureButton=Налаштування +pipeline.defaultOption=Користувацький +pipeline.submitButton=Надіслати +pipeline.help=Довідка з конвеєрної обробки +pipeline.scanHelp=Довідка зі сканування папок + +###################### +# Pipeline Options # +###################### +pipelineOptions.header=Налаштування конвеєрної обробки +pipelineOptions.pipelineNameLabel=Назва конвеєра +pipelineOptions.saveSettings=Зберегти налаштування операції +pipelineOptions.pipelineNamePrompt=Введіть назву конвеєра тут +pipelineOptions.selectOperation=Вибрати операцію +pipelineOptions.addOperationButton=Додати операцію +pipelineOptions.pipelineHeader=Конвеєр: +pipelineOptions.saveButton=Завантажити +pipelineOptions.validateButton=Перевірити + + + + +############# +# NAVBAR # +############# +navbar.convert=Конвертувати +navbar.security=Безпека +navbar.other=Інше +navbar.darkmode=Темний режим +navbar.pageOps=Операції зі сторінкою +navbar.settings=Налаштування + +############# +# SETTINGS # +############# +settings.title=Налаштування +settings.update=Доступне оновлення +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. +settings.appVersion=Версія додатку: +settings.downloadOption.title=Виберіть варіант завантаження (для завантаження одного файлу без zip): +settings.downloadOption.1=Відкрити в тому ж вікні +settings.downloadOption.2=Відкрити в новому вікні +settings.downloadOption.3=Завантажити файл +settings.zipThreshold=Zip-файли, коли кількість завантажених файлів перевищує +settings.signOut=Вийти +settings.accountSettings=Налаштування акаунта +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs + +changeCreds.title=Змінити облікові дані +changeCreds.header=Оновіть дані вашого облікового запису +changeCreds.changePassword=Ви використовуєте заводські облікові дані для входу. Будь ласка, введіть новий пароль +changeCreds.newUsername=Нове ім'я користувача +changeCreds.oldPassword=Поточний пароль +changeCreds.newPassword=Новий пароль +changeCreds.confirmNewPassword=Підтвердіть новий пароль +changeCreds.submit=Надіслати зміни + + + +account.title=Налаштування акаунта +account.accountSettings=Налаштування акаунта +account.adminSettings=Налаштування адміністратора - Перегляд і додавання користувачів +account.userControlSettings=Налаштування контролю користувача +account.changeUsername=Змінити ім'я користувача +account.newUsername=Нове ім'я користувача +account.password=Підтвердження пароля +account.oldPassword=Старий пароль +account.newPassword=Новий пароль +account.changePassword=Змінити пароль +account.confirmNewPassword=Підтвердіть новий пароль +account.signOut=Вийти +account.yourApiKey=Ваш API-ключ +account.syncTitle=Синхронізувати налаштування браузера з обліковим записом +account.settingsCompare=Порівняння налаштувань: +account.property=Властивість +account.webBrowserSettings=Налаштування веб-браузера +account.syncToBrowser=Синхронізувати обліковий запис -> Браузер +account.syncToAccount=Синхронізувати обліковий запис <- Браузер + + +adminUserSettings.title=Налаштування контролю користувача +adminUserSettings.header=Налаштування контролю користувача адміністратора +adminUserSettings.admin=Адміністратор +adminUserSettings.user=Користувач +adminUserSettings.addUser=Додати нового користувача +adminUserSettings.usernameInfo=Ім'я користувача має містити тільки літери та цифри, без пробілів та спеціальних символів. +adminUserSettings.roles=Ролі +adminUserSettings.role=Роль +adminUserSettings.actions=Дії +adminUserSettings.apiUser=Обмежений користувач API +adminUserSettings.extraApiUser=Додатковий обмежений користувач API +adminUserSettings.webOnlyUser=Тільки веб-користувач +adminUserSettings.demoUser=Демо-користувач (без налаштованих параметрів) +adminUserSettings.internalApiUser=Внутрішній користувач API +adminUserSettings.forceChange=Примусити користувача змінити пароль при вході в систему +adminUserSettings.submit=Зберегти користувача +adminUserSettings.changeUserRole=Змінити роль користувача + +############# +# HOME-PAGE # +############# +home.desc=Ваш локальний універсальний магазин для всіх ваших потреб у PDF. +home.searchBar=Пошук функцій... + + +home.viewPdf.title=Перегляд PDF +home.viewPdf.desc=Перегляд, анотація, додавання тексту або зображень +viewPdf.tags=view,read,annotate,text,image + +home.multiTool.title=Мультіінструмент PDF +home.multiTool.desc=Об'єднання, поворот, зміна порядку та видалення сторінок +multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side + +home.merge.title=Об'єднати +home.merge.desc=Легко об'єднуйте кілька PDF-файлів у один. +merge.tags=merge,Page operations,Back end,server side + +home.split.title=Розділити +home.split.desc=Розділіть PDF-файли на кілька документів +split.tags=Page operations,divide,Multi Page,cut,server side + +home.rotate.title=Повернути +home.rotate.desc=Легко повертайте ваші PDF-файли. +rotate.tags=server side + + +home.imageToPdf.title=Зображення в PDF +home.imageToPdf.desc=Перетворення зображення (PNG, JPEG, GIF) в PDF. +imageToPdf.tags=conversion,img,jpg,picture,photo + +home.pdfToImage.title=PDF в зображення +home.pdfToImage.desc=Перетворення PDF в зображення. (PNG, JPEG, GIF) +pdfToImage.tags=conversion,img,jpg,picture,photo + +home.pdfOrganiser.title=Реорганізація +home.pdfOrganiser.desc=Видалення/перестановка сторінок у будь-якому порядку +pdfOrganiser.tags=duplex,even,odd,sort,move + + +home.addImage.title=Додати зображення +home.addImage.desc=Додає зображення у вказане місце в PDF (в розробці) +addImage.tags=img,jpg,picture,photo + +home.watermark.title=Додати водяний знак +home.watermark.desc=Додайте свій водяний знак до документа PDF. +watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo + +home.permissions.title=Змінити дозволи +home.permissions.desc=Змініть дозволи вашого документа PDF +permissions.tags=read,write,edit,print + + +home.removePages.title=Видалення +home.removePages.desc=Видаліть непотрібні сторінки з документа PDF. +removePages.tags=Remove pages,delete pages + +home.addPassword.title=Додати пароль +home.addPassword.desc=Зашифруйте документ PDF паролем. +addPassword.tags=secure,security + +home.removePassword.title=Видалити пароль +home.removePassword.desc=Зніміть захист паролем з вашого документа PDF. +removePassword.tags=secure,Decrypt,security,unpassword,delete password + +home.compressPdfs.title=Стиснути +home.compressPdfs.desc=Стискайте PDF-файли, щоб зменшити їх розмір. +compressPdfs.tags=squish,small,tiny + + +home.changeMetadata.title=Змінити метадані +home.changeMetadata.desc=Змінити/видалити/додати метадані з документа PDF +changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats + +home.fileToPDF.title=Конвертувати файл в PDF +home.fileToPDF.desc=Конвертуйте майже будь-який файл в PDF (DOCX, PNG, XLS, PPT, TXT та інші) +fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint + +home.ocr.title=OCR/Очищення сканування +home.ocr.desc=Очищення сканування та виявлення тексту на зображеннях у файлі PDF та повторне додавання його як текст. +ocr.tags=recognition,text,image,scan,read,identify,detection,editable + + +home.extractImages.title=Витягнути зображення +home.extractImages.desc=Витягує всі зображення з PDF і зберігає їх у zip +extractImages.tags=picture,photo,save,archive,zip,capture,grab + +home.pdfToPDFA.title=PDF в PDF/A +home.pdfToPDFA.desc=Перетворення PDF в PDF/A для довготривалого зберігання +pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation + +home.PDFToWord.title=PDF в Word +home.PDFToWord.desc=Перетворення PDF в формати Word (DOC, DOCX та ODT) +PDFToWord.tags=doc,docx,odt,word,transformation,format,conversion,office,microsoft,docfile + +home.PDFToPresentation.title=PDF в презентацію +home.PDFToPresentation.desc=Перетворення PDF в формати презентацій (PPT, PPTX та ODP) +PDFToPresentation.tags=slides,show,office,microsoft + +home.PDFToText.title=PDF в Text/RTF +home.PDFToText.desc=Перетворення PDF в текстовий або RTF формат +PDFToText.tags=richformat,richtextformat,rich text format + +home.PDFToHTML.title=PDF в HTML +home.PDFToHTML.desc=Перетворення PDF в формат HTML +PDFToHTML.tags=web content,browser friendly + + +home.PDFToXML.title=PDF в XML +home.PDFToXML.desc=Перетворення PDF в формат XML +PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert + +home.ScannerImageSplit.title=Виявлення/розділення відсканованих фотографій +home.ScannerImageSplit.desc=Розділяє кілька фотографій з фото/PDF +ScannerImageSplit.tags=separate,auto-detect,scans,multi-photo,organize + +home.sign.title=Підпис +home.sign.desc=Додає підпис до PDF за допомогою малюнка, тексту або зображення +sign.tags=authorize,initials,drawn-signature,text-sign,image-signature + +home.flatten.title=Згладжування +home.flatten.desc=Видалення всіх інтерактивних елементів та форм з PDF +flatten.tags=static,deactivate,non-interactive,streamline + +home.repair.title=Ремонт +home.repair.desc=Намагається відновити пошкоджений/зламаний PDF +repair.tags=fix,restore,correction,recover + +home.removeBlanks.title=Видалити порожні сторінки +home.removeBlanks.desc=Виявляє та видаляє порожні сторінки з документа +removeBlanks.tags=cleanup,streamline,non-content,organize + +home.removeAnnotations.title=Видалити анотації +home.removeAnnotations.desc=Видаляє всі коментарі/анотації з PDF +removeAnnotations.tags=comments,highlight,notes,markup,remove + +home.compare.title=Порівняння +home.compare.desc=Порівнює та показує різницю між двома PDF-документами +compare.tags=differentiate,contrast,changes,analysis + +home.certSign.title=Підписати сертифікатом +home.certSign.desc=Підписати PDF сертифікатом/ключем (PEM/P12) +certSign.tags=authenticate,PEM,P12,official,encrypt + +home.pageLayout.title=Об'єднати сторінки +home.pageLayout.desc=Об'єднання кількох сторінок документа PDF в одну сторінку +pageLayout.tags=merge,composite,single-view,organize + +home.scalePages.title=Змінити розмір/масштаб сторінки +home.scalePages.desc=Змінити розмір/масштаб сторінки та/або її вмісту. +scalePages.tags=resize,modify,dimension,adapt + +home.pipeline.title=Конвеєр (розширений) +home.pipeline.desc=Виконуйте кілька дій з PDF-файлами, визначаючи сценарії конвеєрної обробки. +pipeline.tags=automate,sequence,scripted,batch-process + +home.add-page-numbers.title=Додати номера сторінок +home.add-page-numbers.desc=Додає номера сторінок по всьому документу в заданому місці +add-page-numbers.tags=paginate,label,organize,index + +home.auto-rename.title=Автоматичне перейменування PDF-файлу +home.auto-rename.desc=Автоматичне перейменування файлу PDF на основі його виявленого заголовку +auto-rename.tags=auto-detect,header-based,organize,relabel + +home.adjust-contrast.title=Налаштування кольорів/контрастності +home.adjust-contrast.desc=Налаштування контрастності, насиченості та яскравості файлу PDF +adjust-contrast.tags=color-correction,tune,modify,enhance + +home.crop.title=Обрізати PDF-файл +home.crop.desc=Обрізати PDF-файл, щоб зменшити його розмір (текст залишається!) +crop.tags=trim,shrink,edit,shape + +home.autoSplitPDF.title=Автоматичне розділення сторінок +home.autoSplitPDF.desc=Автоматичне розділення відсканованого PDF-файлу за допомогою фізичного роздільника відсканованих сторінок QR-коду +autoSplitPDF.tags=QR-based,separate,scan-segment,organize + +home.sanitizePdf.title=Санітарна обробка +home.sanitizePdf.desc=Видалення скриптів та інших елементів з PDF-файлів +sanitizePdf.tags=clean,secure,safe,remove-threats + +home.URLToPDF.title=URL/сайт у PDF +home.URLToPDF.desc=Конвертує будь-який http(s)URL у PDF +URLToPDF.tags=web-capture,save-page,web-to-doc,archive + +home.HTMLToPDF.title=HTML у PDF +home.HTMLToPDF.desc=Конвертує будь-який HTML-файл або zip-файл у PDF. +HTMLToPDF.tags=markup,web-content,transformation,convert + + +home.MarkdownToPDF.title=Markdown у PDF +home.MarkdownToPDF.desc=Конвертує будь-який файл Markdown у PDF +MarkdownToPDF.tags=markup,web-content,transformation,convert + + +home.getPdfInfo.title=Отримати ВСЮ інформацію у форматі PDF +home.getPdfInfo.desc=Збирає будь-яку можливу інформацію у PDF-файлах. +getPdfInfo.tags=infomation,data,stats,statistics + + +home.extractPage.title=Видобути сторінку(и) +home.extractPage.desc=Видобуває обрані сторінки з PDF +extractPage.tags=extract + + +home.PdfToSinglePage.title=PDF на одну велику сторінку +home.PdfToSinglePage.desc=Об'єднує всі сторінки PDF в одну велику сторінку. +PdfToSinglePage.tags=single page + + +home.showJS.title=Показати Javascript +home.showJS.desc=Шукає та відображає будь-який JS, вбудований у PDF-файл. +showJS.tags=JS + +home.autoRedact.title=Автоматичне редагування +home.autoRedact.desc=Автоматичне затемнення (чорніння) тексту в PDF на основі вхідного тексту +autoRedact.tags=Redact,Hide,black out,black,marker,hidden + +home.tableExtraxt.title=PDF у CSV +home.tableExtraxt.desc=Видобуває таблиці з PDF та перетворює їх у CSV +tableExtraxt.tags=CSV,Table Extraction,extract,convert + + +home.autoSizeSplitPDF.title=Автоматичне розділення за розміром/кількістю +home.autoSizeSplitPDF.desc=Розділяє один PDF на кілька документів на основі розміру, кількості сторінок або кількості документів +autoSizeSplitPDF.tags=pdf,split,document,organization + + +home.overlay-pdfs.title=Накладення PDF +home.overlay-pdfs.desc=Накладення одного PDF поверх іншого PDF +overlay-pdfs.tags=Overlay + +home.split-by-sections.title=Розділення PDF за секціями +home.split-by-sections.desc=Розділення кожної сторінки PDF на менші горизонтальні та вертикальні секції +split-by-sections.tags=Section Split, Divide, Customize + +home.AddStampRequest.title=Додати печатку на PDF +home.AddStampRequest.desc=Додавання текстової або зображення печатки у вказані місця +AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize + + +home.PDFToBook.title=PDF у книгу/комікс +home.PDFToBook.desc=Конвертує PDF у формат книги/комікса за допомогою calibre +PDFToBook.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + +home.BookToPDF.title=Книга у PDF +home.BookToPDF.desc=Конвертує формати книги/комікса у PDF за допомогою calibre +BookToPDF.tags=Book,Comic,Calibre,Convert,manga,amazon,kindle + + +########################### +# # +# WEB PAGES # +# # +########################### +#login +login.title=Вхід +login.header=Вхід +login.signin=Увійти +login.rememberme=Запам'ятати мене +login.invalid=Недійсне ім'я користувача або пароль. +login.locked=Ваш обліковий запис заблоковано. +login.signinTitle=Будь ласка, увійдіть +login.ssoSignIn=Увійти через єдиний вхід +login.oauth2AutoCreateDisabled=Автоматичне створення користувача OAUTH2 ВИМКНЕНО + + +#auto-redact +autoRedact.title=Автоматичне редагування +autoRedact.header=Автоматичне редагування +autoRedact.colorLabel=Колір +autoRedact.textsToRedactLabel=Текст для приховування (кожен рядок окремо) +autoRedact.textsToRedactPlaceholder=наприклад \nКонфіденційно \nЦілком таємно +autoRedact.useRegexLabel=Використовувати регулярні вирази +autoRedact.wholeWordSearchLabel=Пошук цілих слів +autoRedact.customPaddingLabel=Додаткове заповнення за користувацьким значенням +autoRedact.convertPDFToImageLabel=Перетворити PDF в зображення PDF (використовується для видалення тексту поза межами) +autoRedact.submitButton=Надіслати + + +#showJS +showJS.title=Показати Javascript +showJS.header=Показати Javascript +showJS.downloadJS=Завантажити Javascript +showJS.submit=Показати + + +#pdfToSinglePage +pdfToSinglePage.title=PDF на одну сторінку +pdfToSinglePage.header=PDF на одну сторінку +pdfToSinglePage.submit=Перетворити на одну сторінку + + +#pageExtracter +pageExtracter.title=Видобути сторінки +pageExtracter.header=Видобути сторінки +pageExtracter.submit=Видобути +pageExtracter.placeholder=(наприклад 1,2,8 або 4,7,12-16 або 2n-1) + + +#getPdfInfo +getPdfInfo.title=Отримати інформацію в PDF +getPdfInfo.header=Отримати інформацію в PDF +getPdfInfo.submit=Отримати інформацію +getPdfInfo.downloadJson=Завантажити JSON + + +#markdown-to-pdf +MarkdownToPDF.title=Markdown у PDF +MarkdownToPDF.header=Markdown у PDF +MarkdownToPDF.submit=Конвертувати +MarkdownToPDF.help=Робота в процесі +MarkdownToPDF.credit=Використовує WeasyPrint + + + +#url-to-pdf +URLToPDF.title=URL у PDF +URLToPDF.header=URL у PDF +URLToPDF.submit=Конвертувати +URLToPDF.credit=Використовує WeasyPrint + + +#html-to-pdf +HTMLToPDF.title=HTML у PDF +HTMLToPDF.header=HTML у PDF +HTMLToPDF.help=Приймає файли HTML та ZIP-файли, що містять html/css/зображення тощо. +HTMLToPDF.submit=Конвертувати +HTMLToPDF.credit=Використовує WeasyPrint +HTMLToPDF.zoom=Рівень масштабування для відображення веб-сайту. +HTMLToPDF.pageWidth=Ширина сторінки в сантиметрах. (Порожньо - за замовчуванням) +HTMLToPDF.pageHeight=Висота сторінки в сантиметрах. (Порожньо - за замовчуванням) +HTMLToPDF.marginTop=Верхній відступ сторінки в міліметрах. (Порожньо - за замовчуванням) +HTMLToPDF.marginBottom=Нижній відступ сторінки в міліметрах. (Порожньо - за замовчуванням) +HTMLToPDF.marginLeft=Лівий відступ сторінки в міліметрах. (Порожньо - за замовчуванням) +HTMLToPDF.marginRight=Правий відступ сторінки в міліметрах. (Порожньо - за замовчуванням) +HTMLToPDF.printBackground=Відтворити фон веб-сайтів. +HTMLToPDF.defaultHeader=Включити заголовок за замовчуванням (Ім'я та номер сторінки) +HTMLToPDF.cssMediaType=Змінити тип медіа CSS сторінки. +HTMLToPDF.none=Немає +HTMLToPDF.print=Друк +HTMLToPDF.screen=Екран + + +#AddStampRequest +AddStampRequest.header=Поставити печатку на PDF +AddStampRequest.title=Поставити печатку на PDF +AddStampRequest.stampType=Тип печатки +AddStampRequest.stampText=Текст печатки +AddStampRequest.stampImage=Зображення печатки +AddStampRequest.alphabet=Алфавіт +AddStampRequest.fontSize=Розмір шрифту/зображення +AddStampRequest.rotation=Обертання +AddStampRequest.opacity=Прозорість +AddStampRequest.position=Позиція +AddStampRequest.overrideX=Перевизначити координату X +AddStampRequest.overrideY=Перевизначити координату Y +AddStampRequest.customMargin=Користувацький відступ +AddStampRequest.customColor=Користувацький колір тексту +AddStampRequest.submit=Надіслати + + +#sanitizePDF +sanitizePDF.title=Дезінфекція PDF +sanitizePDF.header=Дезінфекція PDF файлу +sanitizePDF.selectText.1=Видалити JavaScript +sanitizePDF.selectText.2=Видалити вбудовані файли +sanitizePDF.selectText.3=Видалити метадані +sanitizePDF.selectText.4=Видалити посилання +sanitizePDF.selectText.5=Видалити шрифти +sanitizePDF.submit=Дезінфекція + + +#addPageNumbers +addPageNumbers.title=Додати номери сторінок +addPageNumbers.header=Додати номери сторінок +addPageNumbers.selectText.1=Виберіть PDF-файл: +addPageNumbers.selectText.2=Розмір поля +addPageNumbers.selectText.3=Позиція +addPageNumbers.selectText.4=Стартовий номер +addPageNumbers.selectText.5=Сторінки для нумерації +addPageNumbers.selectText.6=Свій текст +addPageNumbers.customTextDesc=Користувацький текст +addPageNumbers.numberPagesDesc=Які сторінки нумерувати, за замовчуванням 'всі', також приймає 1-5 або 2,5,9 тощо. +addPageNumbers.customNumberDesc=За замовчуванням {n}, також можна використовувати 'Сторінка {n} з {total}', 'Текст-{n}', '{filename}-{n}' +addPageNumbers.submit=Додати номери сторінок + + +#auto-rename +auto-rename.title=Автоматичне перейменування +auto-rename.header=Автоматичне перейменування PDF +auto-rename.submit=Автоматичне перейменування + + +#adjustContrast +adjustContrast.title=Налаштування контрастності +adjustContrast.header=Налаштування контрастності +adjustContrast.contrast=Контраст: +adjustContrast.brightness=Яскравість: +adjustContrast.saturation=Насиченість: +adjustContrast.download=Завантажити + + +#crop +crop.title=Обрізати +crop.header=Обрізати зображення +crop.submit=Надіслати + + +#autoSplitPDF +autoSplitPDF.title=Автоматичне розділення PDF +autoSplitPDF.header=Автоматичне розділення PDF +autoSplitPDF.description=Друк, вставка, сканування, завантаження і дозвольте нам автоматично розділити ваші документи. Не потребує ручного сортування. +autoSplitPDF.selectText.1=Друк кількох окремих аркушів (підійде чорно-білий варіант). +autoSplitPDF.selectText.2=Скануйте всі документи одночасно, вставляючи між ними роздільний аркуш. +autoSplitPDF.selectText.3=Завантажте один великий відсканований PDF-файл, і нехай Stirling PDF зробить все інше. +autoSplitPDF.selectText.4=Роздільні сторінки автоматично виявляються і видаляються, забезпечуючи акуратний кінцевий документ. +autoSplitPDF.formPrompt=Надіслати PDF-файл, що містить роздільні сторінки Stirling-PDF: +autoSplitPDF.duplexMode=Дуплексний режим (сканування спереду і ззаду) +autoSplitPDF.dividerDownload1=Завантажити 'Auto Splitter Divider (minimal).pdf' +autoSplitPDF.dividerDownload2=Завантажити 'Auto Splitter Divider (with instructions).pdf' +autoSplitPDF.submit=Надіслати + + +#pipeline +pipeline.title=Пайплайн + + +#pageLayout +pageLayout.title=Многосторінковий макет +pageLayout.header=Многосторінковий макет +pageLayout.pagesPerSheet=Сторінок на одному аркуші: +pageLayout.addBorder=Додати рамки +pageLayout.submit=Відправити + + +#scalePages +scalePages.title=Відрегулювати масштаб сторінки +scalePages.header=Відрегулювати масштаб сторінки +scalePages.pageSize=Розмір сторінки документа. +scalePages.scaleFactor=Рівень масштабування (обрізки) сторінки. +scalePages.submit=Відправити + + +#certSign +certSign.title=Підпис сертифікатом +certSign.header=Підпишіть PDF своїм сертифікатом (робота в процесі) +certSign.selectPDF=Виберіть файл PDF для підпису: +certSign.jksNote=Примітка: Якщо ваш тип сертифіката не зазначений нижче, будь ласка, конвертуйте його в файл сховища Java Keystore (.jks), використовуючи утиліту командного рядка keytool. Потім виберіть опцію файлу .jks нижче. +certSign.selectKey=Виберіть файл закритого ключа (формат PKCS#8, може бути .pem або .der): +certSign.selectCert=Виберіть файл сертифіката (формат X.509, може бути .pem або .der): +certSign.selectP12=Виберіть файл сховища ключів PKCS#12 (.p12 або .pfx) (необов'язково, якщо він наданий, він повинен містити ваш закритий ключ і сертифікат): +certSign.selectJKS=Виберіть файл сховища Java Keystore (.jks або .keystore): +certSign.certType=Тип сертифіката +certSign.password=Введіть пароль до сховища ключів або особистого ключа (якщо є): +certSign.showSig=Показати підпис +certSign.reason=Причина +certSign.location=Місцезнаходження +certSign.name=Ім'я +certSign.submit=Підписати PDF + + +#removeBlanks +removeBlanks.title=Видалити порожні +removeBlanks.header=Видалити порожні сторінки +removeBlanks.threshold=Поріг: +removeBlanks.thresholdDesc=Поріг для визначення того, наскільки білим має бути білий піксель +removeBlanks.whitePercent=Відсоток білого (%): +removeBlanks.whitePercentDesc=Загальний відсоток білого на сторінці, для видалення +removeBlanks.submit=Видалити порожні + + +#removeAnnotations +removeAnnotations.title=Видалити анотації +removeAnnotations.header=Видалити анотації +removeAnnotations.submit=Видалити + + +#compare +compare.title=Порівняння +compare.header=Порівняння PDF +compare.document.1=Документ 1 +compare.document.2=Документ 2 +compare.submit=Порівняти + +#BookToPDF +BookToPDF.title=Книги та комікси в PDF +BookToPDF.header=Конвертувати книгу в PDF +BookToPDF.credit=Використовується Calibre +BookToPDF.submit=Конвертувати + +#PDFToBook +PDFToBook.title=PDF в книгу +PDFToBook.header=PDF в книгу +PDFToBook.selectText.1=Формат +PDFToBook.credit=Використовується Calibre +PDFToBook.submit=Конвертувати + +#sign +sign.title=Підпис +sign.header=Підписати PDF +sign.upload=Завантажити зображення +sign.draw=Намалювати підпис +sign.text=Ввід тексту +sign.clear=Очистити +sign.add=Додати + + +#repair +repair.title=Ремонт +repair.header=Ремонт PDF +repair.submit=Ремонтувати + + +#flatten +flatten.title=Згладжування +flatten.header=Згладжування PDF +flatten.flattenOnlyForms=Flatten only forms +flatten.submit=Згладити + + +#ScannerImageSplit +ScannerImageSplit.selectText.1=Пороговий кут: +ScannerImageSplit.selectText.2=Встановлює мінімальний абсолютний кут, необхідний для повороту зображення (за замовчуванням: 10). +ScannerImageSplit.selectText.3=Толерантність: +ScannerImageSplit.selectText.4=Визначає діапазон зміни кольору навколо передбачуваного кольору фону (за замовчуванням: 30). +ScannerImageSplit.selectText.5=Мінімальна площа: +ScannerImageSplit.selectText.6=Встановлює мінімальний поріг площі для фотографії (за замовчуванням: 10000). +ScannerImageSplit.selectText.7=Мінімальна площа контуру: +ScannerImageSplit.selectText.8=Встановлює мінімальний поріг площі контуру для фотографії +ScannerImageSplit.selectText.9=Розмір рамки: +ScannerImageSplit.selectText.10=Встановлює розмір додаваної та видаляної рамки, щоб запобігти появі білих рамок на виході (за замовчуванням: 1). + + +#OCR +ocr.title=OCR/Очищення сканування +ocr.header=Очищення сканування / OCR (Optical Character Recognition) Розпізнавання тексту +ocr.selectText.1=Виберіть мови, які повинні бути виявлені у PDF-файлі (перелічені ті, які виявлені на даний момент): +ocr.selectText.2=Створіть текстовий файл, що містить текст OCR, разом із PDF-файлом, обробленим OCR. +ocr.selectText.3=Правильні сторінки були відскановані під перекошеним кутом шляхом повороту їх на місце +ocr.selectText.4=Очистіть сторінку, щоб зменшити шанси, що OCR знайде текст на фоновому шумі. (без зміни виходу) +ocr.selectText.5=Очистіть сторінку, щоб зменшити шанси, що OCR знайде текст на фоновому шумі, підтримує очищення виводу. +ocr.selectText.6=Ігнорує сторінки з інтерактивним текстом, розпізнає лише сторінки з зображеннями +ocr.selectText.7=Примусове розпізнавання символів, буде розпізнавати кожну сторінку, видаляючи всі елементи початкового тексту +ocr.selectText.8=Звичайний (буде помилка, якщо PDF містить текст) +ocr.selectText.9=Додаткові налаштування +ocr.selectText.10=Режим OCR +ocr.selectText.11=Видалити зображення після OCR (видаляє ВСІ зображення, корисно лише в тому випадку, якщо вони є частиною етапу перетворення) +ocr.selectText.12=Тип рендеру (розширений) +ocr.help=Прочитайте цю документацію про те, як використовувати це для інших мов і/або використовувати не в докері. +ocr.credit=Цей сервіс використовує OCRmyPDF та Tesseract для OCR. +ocr.submit=Обробка PDF з OCR + + +#extractImages +extractImages.title=Витягнути зображення +extractImages.header=Витягнути зображення +extractImages.selectText=Виберіть формат зображення для перетворення витягнутих зображень у +extractImages.submit=Витягнути + + +#File to PDF +fileToPDF.title=Файл у PDF +fileToPDF.header=Конвертувати будь-який файл у PDF +fileToPDF.credit=Цей сервіс використовує LibreOffice та Unoconv для перетворення файлів. +fileToPDF.supportedFileTypes=Підтримувані типи файлів повинні включати нижченаведені, однак повний оновлений список підтримуваних форматів дивіться у документації LibreOffice. +fileToPDF.submit=Перетворити у PDF + + +#compress +compress.title=Стиснути +compress.header=Стиснути PDF +compress.credit=Ця служба використовує Ghostscript для стиснення/оптимізації PDF. +compress.selectText.1=Ручний режим - від 1 до 4 +compress.selectText.2=Рівень оптимізації: +compress.selectText.3=4 (Жахливо для текстових зображень) +compress.selectText.4=Автоматичний режим - автоматично налаштовує якість для отримання PDF точного розміру +compress.selectText.5=Очікуваний розмір PDF (наприклад, 25 МБ, 10,8 МБ, 25 КБ) +compress.submit=Стиснути + + +#Add image +addImage.title=Додати зображення +addImage.header=Додати зображення в PDF +addImage.everyPage=Кожна сторінка? +addImage.upload=Додати зображення +addImage.submit=Додати зображення + + +#merge +merge.title=Об'єднати +merge.header=Об'єднання кількох PDF-файлів (2+) +merge.sortByName=Сортування за ім'ям +merge.sortByDate=Сортування за датою +merge.submit=Об'єднати + + +#pdfOrganiser +pdfOrganiser.title=Організатор сторінок +pdfOrganiser.header=Організатор PDF-сторінок +pdfOrganiser.submit=Переупорядкувати сторінки +pdfOrganiser.mode=Режим +pdfOrganiser.mode.1=Користувацький порядок сторінок +pdfOrganiser.mode.2=Зворотній порядок +pdfOrganiser.mode.3=Сортування дуплексом +pdfOrganiser.mode.4=Сортування брошурою +pdfOrganiser.mode.5=Сортування брошурою зі степлером з боку +pdfOrganiser.mode.6=Розділення на парні та непарні сторінки +pdfOrganiser.mode.7=Видалити першу +pdfOrganiser.mode.8=Видалити останню +pdfOrganiser.mode.9=Видалити першу та останню +pdfOrganiser.placeholder=(наприклад, 1,3,2 або 4-8,2,10-12 або 2n-1) + + +#multiTool +multiTool.title=Мультіінструмент PDF +multiTool.header=Мультіінструмент PDF +multiTool.uploadPrompts=Будь ласка, завантажте PDF + +#view pdf +viewPdf.title=Переглянути PDF +viewPdf.header=Переглянути PDF + +#pageRemover +pageRemover.title=Видалення сторінок +pageRemover.header=Видалення сторінок PDF +pageRemover.pagesToDelete=Сторінки для видалення (введіть список номерів сторінок через кому): +pageRemover.submit=Видалити сторінки +pageRemover.placeholder=(наприклад, 1,2,6 або 1-10,15-30) + + +#rotate +rotate.title=Повернути PDF +rotate.header=Повернути PDF +rotate.selectAngle=Виберіть кут повороту (кратний 90 градусам): +rotate.submit=Повернути + + +#split-pdfs +split.title=Розділити PDF +split.header=Розділити PDF +split.desc.1=Числа, які ви вибрали, це номери сторінок, на яких ви хочете зробити розділ. +split.desc.2=Таким чином, вибір 1,3,7-8 розділить 10-сторінковий документ на 6 окремих PDF-файлів з: +split.desc.3=Документ #1: Сторінка 1 +split.desc.4=Документ #2: Сторінки 2 і 3 +split.desc.5=Документ #3: Сторінки 4, 5 і 6 +split.desc.6=Документ #4: Сторінка 7 +split.desc.7=Документ #5: Сторінка 8 +split.desc.8=Документ #6: Сторінки 9 і 10 +split.splitPages=Введіть сторінки для розділення: +split.submit=Розділити + + +#merge +imageToPDF.title=Зображення в PDF +imageToPDF.header=Зображення в PDF +imageToPDF.submit=Конвертувати +imageToPDF.selectLabel=Виберіть режим відображення зображення +imageToPDF.fillPage=Заповнення сторінки +imageToPDF.fitDocumentToImage=Підігнати документ під зображення +imageToPDF.maintainAspectRatio=Зберегти пропорції +imageToPDF.selectText.2=Автоматичний поворот PDF +imageToPDF.selectText.3=Логіка для кількох файлів (активується лише при роботі з декількома зображеннями) +imageToPDF.selectText.4=Об'єднати в один PDF +imageToPDF.selectText.5=Перетворення в окремі PDF-файли + + +#pdfToImage +pdfToImage.title=PDF в зображення +pdfToImage.header=PDF в зображення +pdfToImage.selectText=Формат зображення +pdfToImage.singleOrMultiple=Тип результату зображення +pdfToImage.single=Одне велике зображення +pdfToImage.multi=Декілька зображень +pdfToImage.colorType=Тип кольору +pdfToImage.color=Колір +pdfToImage.grey=Відтінки сірого +pdfToImage.blackwhite=Чорно-білий (може втратити дані!) +pdfToImage.submit=Конвертувати + + +#addPassword +addPassword.title=Додати пароль +addPassword.header=Додати пароль (зашифрувати) +addPassword.selectText.1=Оберіть PDF для шифрування +addPassword.selectText.2=Пароль +addPassword.selectText.3=Довжина ключа шифрування +addPassword.selectText.4=Вищі значення сильніші, але нижчі значення мають кращу сумісність. +addPassword.selectText.5=Дозволи на встановлення +addPassword.selectText.6=Запобігти збірці документа +addPassword.selectText.7=Запобігти вилученню контенту +addPassword.selectText.8=Запобігти вилученню для доступності +addPassword.selectText.9=Заборонити заповнення форм +addPassword.selectText.10=Запобігти модифікації +addPassword.selectText.11=Заборонити модифікацію анотацій +addPassword.selectText.12=Заборонити друк +addPassword.selectText.13=Заборонити друк різних форматів +addPassword.selectText.14=Власницький пароль +addPassword.selectText.15=Обмежує, що можна робити з документом після його відкриття (не підтримується всіма програмами читання) +addPassword.selectText.16=Обмежує відкриття самого документа +addPassword.submit=Шифрувати + + +#watermark +watermark.title=Додати водяний знак +watermark.header=Додати водяний знак +watermark.selectText.1=Виберіть PDF, щоб додати водяний знак: +watermark.selectText.2=Текст водяного знаку: +watermark.selectText.3=Розмір шрифту: +watermark.selectText.4=Обертання (0-360): +watermark.selectText.5=widthSpacer (проміжок між кожним водяним знаком по горизонталі): +watermark.selectText.6=heightSpacer (проміжок між кожним водяним знаком по вертикалі): +watermark.selectText.7=Непрозорість (0% - 100%): +watermark.selectText.8=Тип водяного знаку: +watermark.selectText.9=Зображення водяного знаку: +watermark.submit=Додати водяний знак +watermark.type.1=Текст +watermark.type.2=Зображення + + +#Change permissions +permissions.title=Змінити дозволи +permissions.header=Змінити дозволи +permissions.warning=Попередження про те, що ці дозволи не можна змінити, рекомендується встановити їх за допомогою пароля на сторінці додавання пароля. +permissions.selectText.1=Виберіть PDF, щоб змінити дозволи +permissions.selectText.2=Дозволи на встановлення +permissions.selectText.3=Запобігти збірці документа +permissions.selectText.4=Запобігти вилученню контенту +permissions.selectText.5=Запобігти вилученню для доступності +permissions.selectText.6=Заборонити заповнення форм +permissions.selectText.7=Запобігти модифікації +permissions.selectText.8=Заборонити модифікацію анотацій +permissions.selectText.9=Заборонити друк +permissions.selectText.10=Заборонити друк різних форматів +permissions.submit=Змінити + + +#remove password +removePassword.title=Видалити пароль +removePassword.header=Видалити пароль (Розшифрувати) +removePassword.selectText.1=Виберіть PDF для розшифрування +removePassword.selectText.2=Пароль +removePassword.submit=Видалити + + +#changeMetadata +changeMetadata.title=Заголовок: +changeMetadata.header=Змінити метадані +changeMetadata.selectText.1=Будь ласка, відредагуйте змінні, які ви хочете змінити +changeMetadata.selectText.2=Видалити всі метадані +changeMetadata.selectText.3=Показати користувацькі метадані: +changeMetadata.author=Автор: +changeMetadata.creationDate=Дата створення (yyyy/MM/dd HH:mm:ss): +changeMetadata.creator=Створювач: +changeMetadata.keywords=Ключові слова: +changeMetadata.modDate=Дата зміни (yyyy/MM/dd HH:mm:ss): +changeMetadata.producer=Виробник: +changeMetadata.subject=Тема: +changeMetadata.trapped=Пастка: +changeMetadata.selectText.4=Інші метадані: +changeMetadata.selectText.5=Додати користувацький запис метаданих +changeMetadata.submit=Змінити + + +#pdfToPDFA +pdfToPDFA.title=PDF в PDF/A +pdfToPDFA.header=PDF в PDF/A +pdfToPDFA.credit=Цей сервіс використовує OCRmyPDF для перетворення у формат PDF/A +pdfToPDFA.submit=Конвертувати +pdfToPDFA.tip=Наразі не працює для кількох вхідних файлів одночасно +pdfToPDFA.outputFormat=Output format + + +#PDFToWord +PDFToWord.title=PDF в Word +PDFToWord.header=PDF в Word +PDFToWord.selectText.1=Формат вихідного файлу +PDFToWord.credit=Цей сервіс використовує LibreOffice для перетворення файлів. +PDFToWord.submit=Конвертувати + + +#PDFToPresentation +PDFToPresentation.title=PDF в Презентацію +PDFToPresentation.header=PDF в Презентацію +PDFToPresentation.selectText.1=Формат вихідного файлу +PDFToPresentation.credit=Цей сервіс використовує LibreOffice для перетворення файлів. +PDFToPresentation.submit=Конвертувати + + +#PDFToText +PDFToText.title=PDF в Text/RTF +PDFToText.header=PDF в Text/RTF +PDFToText.selectText.1=Формат вихідного файлу +PDFToText.credit=Цей сервіс використовує LibreOffice для перетворення файлів. +PDFToText.submit=Конвертувати + + +#PDFToHTML +PDFToHTML.title=PDF в HTML +PDFToHTML.header=PDF в HTML +PDFToHTML.credit=Цей сервіс використовує pdftohtml для перетворення файлів. +PDFToHTML.submit=Конвертувати + + +#PDFToXML +PDFToXML.title=PDF в XML +PDFToXML.header=PDF в XML +PDFToXML.credit=Цей сервіс використовує LibreOffice для перетворення файлів. +PDFToXML.submit=Конвертувати + +#PDFToCSV +PDFToCSV.title=PDF в CSV +PDFToCSV.header=PDF в CSV +PDFToCSV.prompt=Виберіть сторінку для витягу таблиці +PDFToCSV.submit=Конвертувати + +#split-by-size-or-count +split-by-size-or-count.title=Розділити PDF за розміром або кількістю +split-by-size-or-count.header=Розділити PDF за розміром або кількістю +split-by-size-or-count.type.label=Виберіть тип розділення +split-by-size-or-count.type.size=За розміром +split-by-size-or-count.type.pageCount=За кількістю сторінок +split-by-size-or-count.type.docCount=За кількістю документів +split-by-size-or-count.value.label=Введіть значення +split-by-size-or-count.value.placeholder=Введіть розмір (наприклад, 2MB або 3KB) або кількість (наприклад, 5) +split-by-size-or-count.submit=Надіслати + + +#overlay-pdfs +overlay-pdfs.header=Накладення файлів PDF +overlay-pdfs.baseFile.label=Виберіть основний файл PDF +overlay-pdfs.overlayFiles.label=Виберіть файл(и) для накладення +overlay-pdfs.mode.label=Виберіть режим накладення +overlay-pdfs.mode.sequential=Послідовне накладення +overlay-pdfs.mode.interleaved=Перехресне накладення +overlay-pdfs.mode.fixedRepeat=Накладення з фіксованим повторенням +overlay-pdfs.counts.label=Кількість накладень (для режиму з фіксованим повторенням) +overlay-pdfs.counts.placeholder=Введіть через кому кількість повторень (наприклад, 2,3,1) +overlay-pdfs.position.label=Виберіть позицію накладення +overlay-pdfs.position.foreground=Над основним +overlay-pdfs.position.background=За основним +overlay-pdfs.submit=Надіслати + + +#split-by-sections +split-by-sections.title=Розділити PDF за розділами +split-by-sections.header=Розділити PDF на секції +split-by-sections.horizontal.label=Горизонтальні розділи +split-by-sections.vertical.label=Вертикальні розділи +split-by-sections.horizontal.placeholder=Введіть кількість горизонтальних розділів +split-by-sections.vertical.placeholder=Введіть кількість вертикальних розділів +split-by-sections.submit=Розділити PDF +split-by-sections.merge=Об'єднати в один PDF + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print + + +#licenses +licenses.nav=Ліцензії +licenses.title=Ліцензії від третіх сторін +licenses.header=Ліцензії від третіх сторін +licenses.module=Модуль +licenses.version=Версія +licenses.license=Ліцензія + + +# error +error.sorry=Вибачте за незручності! +error.needHelp=Потрібна допомога / Знайшли проблему? +error.contactTip=Якщо у вас досі виникають проблеми, не соромтеся звертатися до нас за допомогою. Ви можете надіслати запит на нашій сторінці GitHub або зв'язатися з нами через Discord: +error.404.head=404 - Сторінку не знайдено | Ой, ми заплуталися в коді! +error.404.1=Ми не можемо знайти сторінку, яку ви шукаєте. +error.404.2=Щось пішло не так +error.github=Надіслати запит на GitHub +error.showStack=Показати стек викликів +error.copyStack=Скопіювати стек викликів +error.githubSubmit=GitHub - Надіслати запит +error.discordSubmit=Discord - Надіслати повідомлення підтримки + diff --git a/src/main/resources/messages_zh_CN.properties b/src/main/resources/messages_zh_CN.properties index ea994fbb..ca04fc36 100644 --- a/src/main/resources/messages_zh_CN.properties +++ b/src/main/resources/messages_zh_CN.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -11,16 +11,18 @@ imgPrompt=选择图像 genericSubmit=提交 processTimeWarning=警告:此过程可能需要多达一分钟,具体时间取决于文件大小 pageOrderPrompt=页面顺序(输入逗号分隔的页码列表): -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : +pageSelectionPrompt=自定义页面选择(输入以逗号分隔的页码列表 1,5,6 或 2n+1 等函数): goToPage=到 true=对 false=错 unknown=未知 save=保存 +saveToBrowser=保存到浏览器 close=关闭 filesSelected=选中的文件 noFavourites=没有添加收藏夹 -bored=无聊等待吗? +downloadComplete=下载完成 +bored=等待时觉得无聊? alphabet=字母表 downloadPdf=下载PDF text=文本 @@ -30,51 +32,69 @@ pageNum=页码 sizes.small=小型尺寸 sizes.medium=中型尺寸 sizes.large=大型尺寸 -sizes.x-large=稍大型尺寸 +sizes.x-large=超大型尺寸 error.pdfPassword=PDF 文档有密码,未提供密码或密码不正确 delete=删除 username=用户名 password=密码 welcome=欢迎 property=资产 -black=Black -white=White -red=Red -green=Green -blue=Blue -custom=Custom... -WorkInProgess=Work in progress, May not work or be buggy, Please report any problems! +black=黑色 +white=白色 +red=红色 +green=绿色 +blue=蓝色 +custom=自定义... +WorkInProgess=工作正在进行中,可能无法工作或有错误,请报告任何问题! poweredBy=Powered by -yes=Yes -no=No +yes=是 +no=否 changedCredsMessage=凭证已更改! notAuthenticatedMessage=用户未经过身份验证。 userNotFoundMessage=未找到用户。 incorrectPasswordMessage=当前密码不正确。 usernameExistsMessage=新用户名已存在。 +invalidUsernameMessage=用户名无效,用户名只能由字母字符和数字组成。 +deleteCurrentUserMessage=无法删除当前登录的用户。 +deleteUsernameExistsMessage=用户名不存在,无法删除。 +downgradeCurrentUserMessage=无法降级当前用户的角色 +downgradeCurrentUserLongMessage=无法降级当前用户的角色。因此,当前用户将不会显示。 +error=错误 +oops=哎呀! +help=帮助 +goHomepage=返回主页 +joinDiscord=加入我们的Discord服务器 +seeDockerHub=查看Docker Hub +visitGithub=访问Github仓库 +donate=捐款 +color=颜色 +sponsor=赞助 + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) -pipeline.uploadButton=Upload Custom -pipeline.configureButton=Configure -pipeline.defaultOption=Custom -pipeline.submitButton=Submit +pipeline.header=流水线菜单 (Beta) +pipeline.uploadButton=上传自定义流水线 +pipeline.configureButton=配置 +pipeline.defaultOption=自定义 +pipeline.submitButton=提交 +pipeline.help=工作流帮助 +pipeline.scanHelp=文件夹扫描帮助 ###################### # Pipeline Options # ###################### -pipelineOptions.header=Pipeline Configuration -pipelineOptions.pipelineNameLabel=Pipeline Name -pipelineOptions.saveSettings=Save Operation Settings -pipelineOptions.pipelineNamePrompt=Enter pipeline name here -pipelineOptions.selectOperation=Select Operation -pipelineOptions.addOperationButton=Add operation -pipelineOptions.pipelineHeader=Pipeline: -pipelineOptions.saveButton=Download -pipelineOptions.validateButton=Validate +pipelineOptions.header=流水线配置 +pipelineOptions.pipelineNameLabel=流水线名称 +pipelineOptions.saveSettings=保存设置 +pipelineOptions.pipelineNamePrompt=请输入流水线名称 +pipelineOptions.selectOperation=选择操作 +pipelineOptions.addOperationButton=添加操作 +pipelineOptions.pipelineHeader=流水线: +pipelineOptions.saveButton=下载 +pipelineOptions.validateButton=验证 @@ -94,6 +114,7 @@ navbar.settings=设置 ############# settings.title=设置 settings.update=可更新 +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=应用程序版本: settings.downloadOption.title=选择下载选项(单个文件非压缩文件): settings.downloadOption.1=在同一窗口打开 @@ -102,12 +123,13 @@ settings.downloadOption.3=下载文件 settings.zipThreshold=当下载的文件数量超过限制时,将文件压缩。 settings.signOut=登出 settings.accountSettings=帐号设定 - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=更改凭证 changeCreds.header=更新您的账户详情 -changeCreds.changeUserAndPassword=您正在使用默认登录凭据。请输入新密码(如果需要,还可以输入新用户名) +changeCreds.changePassword=您正在使用默认登录凭证,请输入新密码 changeCreds.newUsername=新用户名 changeCreds.oldPassword=当前密码 changeCreds.newPassword=新密码 @@ -142,19 +164,23 @@ adminUserSettings.header=管理员用户控制设置 adminUserSettings.admin=管理员 adminUserSettings.user=用户 adminUserSettings.addUser=添加新用户 +adminUserSettings.usernameInfo=用户名只能由字母和数字组成,不能包含空格或特殊字符。 adminUserSettings.roles=角色 adminUserSettings.role=角色 adminUserSettings.actions=操作 -adminUserSettings.apiUser=有限 API 用户 +adminUserSettings.apiUser=受限制的 API 用户 +adminUserSettings.extraApiUser=额外受限制的 API 用户 adminUserSettings.webOnlyUser=仅限 Web 用户 -adminUserSettings.demoUser=Demo User (No custom settings) +adminUserSettings.demoUser=演示用户(无自定义设置) +adminUserSettings.internalApiUser=内部API用户 adminUserSettings.forceChange=强制用户在登录时更改用户名/密码 adminUserSettings.submit=保存用户 +adminUserSettings.changeUserRole=更改用户角色 ############# # HOME-PAGE # ############# -home.desc=CZL一站式服务,满足您的所有PDF需求。 +home.desc=本地部署的一站式服务,满足您的所有PDF需求。 home.searchBar=搜索您需要的功能... @@ -228,7 +254,7 @@ changeMetadata.tags=标题、作者、日期、创建、时间、发布者、制 home.fileToPDF.title=将文件转换为PDF文件 home.fileToPDF.desc=将几乎所有文件转换为PDF(DOCX、PNG、XLS、PPT、TXT等)。 -fileToPDF.tags=转换、格式、文档、图片、幻灯片、文本、转换、办公室、文档、Word、Excel、PowerPoint +fileToPDF.tags=转换、格式、文档、图片、幻灯片、文本、转换、Office、Docs、Word、Excel、PowerPoint home.ocr.title=运行OCR/清理扫描 home.ocr.desc=清理和识别PDF中的图像文本,并将其转换为可编辑文本。 @@ -239,17 +265,17 @@ home.extractImages.title=提取图像 home.extractImages.desc=从PDF中提取所有图像并保存到压缩包中。 extractImages.tags=图片、照片、保存、归档、压缩包、截取、抓取 -home.pdfToPDFA.title=PDF To PDF/A +home.pdfToPDFA.title=PDF转PDF/A home.pdfToPDFA.desc=将PDF转换为PDF/A以进行长期保存。 pdfToPDFA.tags=归档、长期、标准、转换、存储、保存 home.PDFToWord.title=PDF转Word home.PDFToWord.desc=将PDF转换为Word格式(DOC、DOCX和ODT)。 -PDFToWord.tags=doc、docx、odt、word、转换、格式、办公、Microsoft、文档 +PDFToWord.tags=doc、docx、odt、word、转换、格式、Office、Microsoft、文档 home.PDFToPresentation.title=PDF转演示文稿 home.PDFToPresentation.desc=将PDF转换为演示文稿格式(PPT、PPTX和ODP)。 -PDFToPresentation.tags=幻灯片、展示、办公、Microsoft +PDFToPresentation.tags=幻灯片、展示、Office、Microsoft home.PDFToText.title=PDF转RTF(文本) home.PDFToText.desc=将PDF转换为文本或RTF格式。 @@ -268,7 +294,7 @@ home.ScannerImageSplit.title=检测/分割扫描图像 home.ScannerImageSplit.desc=从一张照片或PDF中分割出多张照片。 ScannerImageSplit.tags=分离、自动检测、扫描、多张照片、整理 -home.sign.title=标志 +home.sign.title=签名 home.sign.desc=通过绘图、文字或图像向PDF添加签名 sign.tags=授权、缩写、手绘签名、文本签名、图像签名 @@ -304,8 +330,8 @@ home.scalePages.title=调整页面尺寸/缩放 home.scalePages.desc=调整页面及/或其内容的尺寸/缩放 scalePages.tags=调整大小、修改、尺寸、适应 -home.pipeline.title=管道(高级版) -home.pipeline.desc=通过定义管道脚本在PDF上运行多个操作 +home.pipeline.title=流水线(高级版) +home.pipeline.desc=通过定义流水线脚本在PDF上运行多个操作 pipeline.tags=自动化、顺序、脚本化、批处理 home.add-page-numbers.title=添加页码 @@ -367,9 +393,9 @@ showJS.tags=JavaScript home.autoRedact.title=自动删除 home.autoRedact.desc=根据输入文本自动删除(覆盖)PDF中的文本 -autoRedact.tags=Redact,Hide,black out,black,marker,hidden +autoRedact.tags=脱敏、隐藏、涂黑、标记、不可见 -home.tableExtraxt.title=PDF to CSV +home.tableExtraxt.title=PDF转CSV home.tableExtraxt.desc=从PDF中提取表格并将其转换为CSV tableExtraxt.tags=CSV、表格提取、提取、转换 @@ -387,9 +413,18 @@ home.split-by-sections.title=拆分PDF成小块 home.split-by-sections.desc=将PDF的每一页分割成更小的水平和垂直的部分 split-by-sections.tags=章节拆分、分割、自定义 -home.AddStampRequest.title=Add Stamp to PDF -home.AddStampRequest.desc=Add text or add image stamps at set locations -AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.AddStampRequest.title=添加图章 +home.AddStampRequest.desc=在指定位置添加文本或图片图章 +AddStampRequest.tags=图章、添加图片、图片居中、水印、PDF、嵌入、自定义 + + +home.PDFToBook.title=PDF转电子书 +home.PDFToBook.desc=使用Calibre将PDF转换成电子书/漫画 +PDFToBook.tags=电子书、漫画、Calibre、转换、日本漫画、亚马逊、kindle + +home.BookToPDF.title=电子书转PDF +home.BookToPDF.desc=使用Calibre将电子书/漫画转换成PDF +BookToPDF.tags=电子书、漫画、Calibre、转换、日本漫画、亚马逊、kindle ########################### @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=登录 +login.header=登录 login.signin=登录 login.rememberme=记住我 login.invalid=用户名或密码无效。 login.locked=您的账户已被锁定。 login.signinTitle=请登录 +login.ssoSignIn=通过单点登录登录 +login.oauth2AutoCreateDisabled=OAUTH2自动创建用户已禁用 #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=转为单页 pageExtracter.title=提取页面 pageExtracter.header=提取页面 pageExtracter.submit=提取 +pageExtracter.placeholder=(例如 1,2,8 或 4,7,12-16 或 2n-1) #getPdfInfo @@ -450,7 +489,7 @@ MarkdownToPDF.title=Markdown转PDF MarkdownToPDF.header=Markdown转PDF MarkdownToPDF.submit=转换 MarkdownToPDF.help=正在努力中 -MarkdownToPDF.credit=使用WeasyPrint +MarkdownToPDF.credit=此服务使用WeasyPrint进行文件转换。 @@ -458,7 +497,7 @@ MarkdownToPDF.credit=使用WeasyPrint URLToPDF.title=URL转PDF URLToPDF.header=URL转PDF URLToPDF.submit=转换 -URLToPDF.credit=使用WeasyPrint +URLToPDF.credit=此服务使用WeasyPrint进行文件转换。 #html-to-pdf @@ -466,38 +505,38 @@ HTMLToPDF.title=HTML转PDF HTMLToPDF.header=HTML转PDF HTMLToPDF.help=接受HTML文件和包含所需的html/css/images等的ZIP文件 HTMLToPDF.submit=转换 -HTMLToPDF.credit=使用WeasyPrint -HTMLToPDF.zoom=Zoom level for displaying the website. -HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default) -HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default) -HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default) -HTMLToPDF.printBackground=Render the background of websites. -HTMLToPDF.defaultHeader=Enable Default Header (Name and page number) -HTMLToPDF.cssMediaType=Change the CSS media type of the page. -HTMLToPDF.none=None -HTMLToPDF.print=Print -HTMLToPDF.screen=Screen +HTMLToPDF.credit=此服务使用WeasyPrint进行文件转换。 +HTMLToPDF.zoom=网站显示缩放级别 +HTMLToPDF.pageWidth=页面宽度-以厘米为单位(填空则使用默认值) +HTMLToPDF.pageHeight=页面高度-以厘米为单位(填空则使用默认值) +HTMLToPDF.marginTop=页面上边距-以毫米为单位(填空则使用默认值) +HTMLToPDF.marginBottom=页面下边距-以毫米为单位(填空则使用默认值) +HTMLToPDF.marginLeft=页面左上边距-以毫米为单位(填空则使用默认值) +HTMLToPDF.marginRight=页面右边距-以毫米为单位(填空则使用默认值) +HTMLToPDF.printBackground=页面背景渲染 +HTMLToPDF.defaultHeader=启用默认页头(文件名称和页码) +HTMLToPDF.cssMediaType=更换页面的CSS media type. +HTMLToPDF.none=无 +HTMLToPDF.print=打印 +HTMLToPDF.screen=屏幕 #AddStampRequest -AddStampRequest.header=Stamp PDF -AddStampRequest.title=Stamp PDF -AddStampRequest.stampType=Stamp Type -AddStampRequest.stampText=Stamp Text -AddStampRequest.stampImage=Stamp Image -AddStampRequest.alphabet=Alphabet -AddStampRequest.fontSize=Font/Image Size -AddStampRequest.rotation=Rotation -AddStampRequest.opacity=Opacity -AddStampRequest.position=Position -AddStampRequest.overrideX=Override X Coordinate -AddStampRequest.overrideY=Override Y Coordinate -AddStampRequest.customMargin=Custom Margin -AddStampRequest.customColor=Custom Text Color -AddStampRequest.submit=Submit +AddStampRequest.header=添加图章 +AddStampRequest.title=添加图章 +AddStampRequest.stampType=图章类型 +AddStampRequest.stampText=图章文字 +AddStampRequest.stampImage=图章图片 +AddStampRequest.alphabet=字母表 +AddStampRequest.fontSize=字体/图片大小 +AddStampRequest.rotation=旋转角度 +AddStampRequest.opacity=透明度 +AddStampRequest.position=定位 +AddStampRequest.overrideX=覆盖 X 坐标 +AddStampRequest.overrideY=覆盖 Y 坐标 +AddStampRequest.customMargin=自定义外边距 +AddStampRequest.customColor=自定义文本颜色 +AddStampRequest.submit=提交 #sanitizePDF @@ -586,11 +625,11 @@ scalePages.submit=提交 certSign.title=证书签名 certSign.header=使用您的证书签署 PDF(进行中) certSign.selectPDF=选择要签名的 PDF 文件: -certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below. +certSign.jksNote=注意:如果您的证书类型未在下面列出,请使用 keytool 命令行工具将其转换为 Java Keystore (.jks) 文件。 然后,选择下面的 .jks 文件选项。 certSign.selectKey=选择您的私钥文件(PKCS#8 格式,可以是 .pem 或 .der): certSign.selectCert=选择您的证书文件(X.509 格式,可以是 .pem 或 .der): certSign.selectP12=选择您的 PKCS#12 密钥库文件(.p12 或 .pfx)(可选,如果提供,它应该包含您的私钥和证书): -certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore): +certSign.selectJKS=选择你的 Java Keystore 文件 (.jks 或 .keystore): certSign.certType=证书类型 certSign.password=输入您的密钥库或私钥密码(如果有): certSign.showSig=显示签名 @@ -611,9 +650,9 @@ removeBlanks.submit=删除空白 #removeAnnotations -removeAnnotations.title=Remove Annotations -removeAnnotations.header=Remove Annotations -removeAnnotations.submit=Remove +removeAnnotations.title=删除标注 +removeAnnotations.header=删除标注 +removeAnnotations.submit=删除 #compare @@ -623,6 +662,18 @@ compare.document.1=文档 1 compare.document.2=文档 2 compare.submit=比较 +#BookToPDF +BookToPDF.title=电子书和漫画转换成PDF +BookToPDF.header=电子书转PDF +BookToPDF.credit=此服务使用Calibre进行文件转换。 +BookToPDF.submit=转换 + +#PDFToBook +PDFToBook.title=PDF转电子书 +PDFToBook.header=PDF转电子书 +PDFToBook.selectText.1=格式 +PDFToBook.credit=此服务使用Calibre进行文件转换。 +PDFToBook.submit=转换 #sign sign.title=签名 @@ -643,6 +694,7 @@ repair.submit=修复 #flatten flatten.title=展平 flatten.header=展平 PDF +flatten.flattenOnlyForms=Flatten only forms flatten.submit=展平 @@ -689,7 +741,7 @@ extractImages.submit=提取 #File to PDF fileToPDF.title=文件转换为PDF fileToPDF.header=将任何文件转换为PDF。 -fileToPDF.credit=本服务使用LibreOffice和Unoconv进行文件转换。 +fileToPDF.credit=此服务使用LibreOffice和Unoconv进行文件转换。 fileToPDF.supportedFileTypes=支持的文件类型应该包括以下几种,但是,对于支持的格式的完整更新列表,请参考LibreOffice文档。 fileToPDF.submit=转换为 PDF @@ -708,7 +760,7 @@ compress.submit=压缩 #Add image addImage.title=添加图像 -addImage.header=添加图片到PDF(正在进行中) +addImage.header=添加图片到PDF addImage.everyPage=每一页? addImage.upload=添加图片 addImage.submit=添加图片 @@ -726,21 +778,34 @@ merge.submit=合并 pdfOrganiser.title=页面排序 pdfOrganiser.header=PDF页面排序 pdfOrganiser.submit=重新排列页面 +pdfOrganiser.mode=模式 +pdfOrganiser.mode.1=自定义页面顺序 +pdfOrganiser.mode.2=反向顺序 +pdfOrganiser.mode.3=双面排序 +pdfOrganiser.mode.4=小册子排序 +pdfOrganiser.mode.5=侧装订小册子排序 +pdfOrganiser.mode.6=奇偶拆分 +pdfOrganiser.mode.7=删除第一页 +pdfOrganiser.mode.8=删除最后一页 +pdfOrganiser.mode.9=删除第一页和最后一页 +pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1) #multiTool multiTool.title=PDF多功能工具 multiTool.header=PDF多功能工具 +multiTool.uploadPrompts=上传PDF #view pdf -viewPdf.title=View PDF -viewPdf.header=View PDF +viewPdf.title=浏览PDF +viewPdf.header=浏览PDF #pageRemover pageRemover.title=删除页面 pageRemover.header=PDF页面移除器 pageRemover.pagesToDelete=要删除的页面(输入一个用逗号分隔的页码列表): pageRemover.submit=删除页面 +pageRemover.placeholder=(例如 1,2,6 或 1-10,15-30) #rotate @@ -750,14 +815,14 @@ rotate.selectAngle=选择旋转角度(以90度的倍数): rotate.submit=旋转 -#merge +#split-pdfs split.title=拆分PDF split.header=拆分PDF split.desc.1=选择希望进行分割的页数 -split.desc.2=如选择1,3,7-8将把一个10页的文件分割成6个独立的PDF: +split.desc.2=如选择1,3,7-9将把一个10页的文件分割成6个独立的PDF: split.desc.3=文档 #1:第1页 split.desc.4=文档 #2:第2页和第3页 -split.desc.5=文档 #3:第4页、第5页和第6页 +split.desc.5=文档 #3:第4页、第5页、第6页和第7页 split.desc.6=文档 #4:第7页 split.desc.7=文档 #5:第8页 split.desc.8=文档 #6:第9页和第10页 @@ -780,7 +845,7 @@ imageToPDF.selectText.5=转换为独立的PDF文件 #pdfToImage -pdfToImage.title=PDF to Image +pdfToImage.title=PDF转图片 pdfToImage.header=PDF转图片 pdfToImage.selectText=图像格式 pdfToImage.singleOrMultiple=图像结果类型 @@ -828,6 +893,8 @@ watermark.selectText.7=透明度(0% - 100%): watermark.selectText.8=水印类型: watermark.selectText.9=水印图片: watermark.submit=添加水印 +watermark.type.1=文字 +watermark.type.2=图片 #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=将PDF转换为PDF/A pdfToPDFA.header=PDF转换为PDF/A pdfToPDFA.credit=此服务使用OCRmyPDF进行PDF/A转换 pdfToPDFA.submit=转换 +pdfToPDFA.tip=目前不支持上传多个 +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -893,7 +962,7 @@ PDFToWord.submit=转换 PDFToPresentation.title=PDF转换为演示文稿 PDFToPresentation.header=将PDF转为演示文稿 PDFToPresentation.selectText.1=输出文件格式 -PDFToPresentation.credit=该服务使用LibreOffice进行文件转换。 +PDFToPresentation.credit=此服务使用LibreOffice进行文件转换。 PDFToPresentation.submit=转换 @@ -901,14 +970,14 @@ PDFToPresentation.submit=转换 PDFToText.title=PDF to RTF (Text) PDFToText.header=将PDF转换成文本/RTF PDFToText.selectText.1=输出文件格式 -PDFToText.credit=该服务使用LibreOffice进行文件转换。 +PDFToText.credit=此服务使用LibreOffice进行文件转换。 PDFToText.submit=转换 #PDFToHTML PDFToHTML.title=PDF To HTML PDFToHTML.header=将PDF转换成HTML -PDFToHTML.credit=此服务使用LibreOffice进行文件转换。 +PDFToHTML.credit=此服务使用pdftohtml进行文件转换。 PDFToHTML.submit=转换 @@ -925,6 +994,7 @@ PDFToCSV.prompt=选择需要提取表格的页面 PDFToCSV.submit=提取 #split-by-size-or-count +split-by-size-or-count.title=按照大小或数目拆分PDF split-by-size-or-count.header=按照大小或数目拆分PDF split-by-size-or-count.type.label=选择拆分类型 split-by-size-or-count.type.size=按照大小 @@ -959,14 +1029,36 @@ split-by-sections.vertical.label=垂直分割 split-by-sections.horizontal.placeholder=输入水平分割数 split-by-sections.vertical.placeholder=输入垂直分割数 split-by-sections.submit=分割PDF +split-by-sections.merge=是否合并为一个pdf + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses -licenses.nav=Licenses -licenses.title=3rd Party Licenses -licenses.header=3rd Party Licenses -licenses.module=Module -licenses.version=Version -licenses.license=License +licenses.nav=许可证 +licenses.title=第三方许可证 +licenses.header=第三方许可证 +licenses.module=模块 +licenses.version=版本 +licenses.license=许可证 +# error +error.sorry=对此问题感到抱歉! +error.needHelp=需要帮助 / 发现问题? +error.contactTip=如果你仍然遇到问题,不要犹豫,向我们寻求帮助。你可以在我们的GitHub页面上提交工单,或者通过Discord与我们联系: +error.404.head=404 - 页面未找到 | 哎呀,我们在代码中触发了错误! +error.404.1=我们似乎找不到你寻找的页面。 +error.404.2=出了些问题 +error.github=在GitHub上提交工单 +error.showStack=显示堆栈跟踪 +error.copyStack=复制堆栈跟踪 +error.githubSubmit=GitHub - 提交工单 +error.discordSubmit=Discord - 提交支持帖子 + diff --git a/src/main/resources/messages_zh_TW.properties b/src/main/resources/messages_zh_TW.properties index 2122d9f0..8391da55 100644 --- a/src/main/resources/messages_zh_TW.properties +++ b/src/main/resources/messages_zh_TW.properties @@ -1,4 +1,4 @@ -########### +########### # Generic # ########### # the direction that the language is written (ltr=left to right, rtl = right to left) @@ -11,15 +11,17 @@ imgPrompt=選擇圖片 genericSubmit=送出 processTimeWarning=警告:此過程可能需要長達一分鐘,具體取決於檔案大小 pageOrderPrompt=自訂頁面順序(輸入以逗號分隔的頁碼或函式,如 2n+1): -pageSelectionPrompt=Custom Page Selection (Enter a comma-separated list of page numbers 1,5,6 or Functions like 2n+1) : +pageSelectionPrompt=自訂頁面選擇(輸入以逗號分隔的頁碼 1、5、6 或 2n+1 等函數的清單): goToPage=前往 true=是 false=否 unknown=未知 save=儲存 +saveToBrowser=儲存到瀏覽器 close=關閉 filesSelected=已選擇的檔案 noFavourites=未新增收藏 +downloadComplete=下載完成 bored=等待時覺得無聊? alphabet=字母表 downloadPdf=下載 PDF @@ -43,38 +45,56 @@ red=紅色 green=綠色 blue=藍色 custom=自訂... -WorkInProgess=Work in progress, May not work or be buggy, Please report any problems! +WorkInProgess=工作正在進行中,可能無法工作或有問題,請報告任何問題! poweredBy=Powered by -yes=Yes -no=No +yes=是 +no=否 changedCredsMessage=憑證已變更! notAuthenticatedMessage=使用者未認證。 userNotFoundMessage=找不到使用者。 incorrectPasswordMessage=目前密碼不正確。 usernameExistsMessage=新使用者名稱已存在。 +invalidUsernameMessage=使用者名無效,使用者名只能包含字母字元和數位。 +deleteCurrentUserMessage=無法刪除目前登錄的使用者。 +deleteUsernameExistsMessage=使用者名不存在,無法刪除。 +downgradeCurrentUserMessage=無法降級目前使用者的角色 +downgradeCurrentUserLongMessage=無法降級目前使用者的角色。因此,不會顯示目前的使用者。 +error=錯誤 +oops=哎呀! +help=幫助 +goHomepage=前往首頁 +joinDiscord=加入我們的Discord服務器 +seeDockerHub=查看Docker Hub +visitGithub=訪問Github存儲庫 +donate=捐贈 +color=顏色 +sponsor=贊助 + ############### # Pipeline # ############### -pipeline.header=Pipeline Menu (Alpha) -pipeline.uploadButton=Upload Custom -pipeline.configureButton=Configure -pipeline.defaultOption=Custom -pipeline.submitButton=Submit +pipeline.header=管道選單(測試版) +pipeline.uploadButton=上傳自定義 +pipeline.configureButton=配置 +pipeline.defaultOption=自訂 +pipeline.submitButton=送出 +pipeline.help=管道説明 +pipeline.scanHelp=資料夾掃描説明 ###################### # Pipeline Options # ###################### -pipelineOptions.header=Pipeline Configuration -pipelineOptions.pipelineNameLabel=Pipeline Name -pipelineOptions.saveSettings=Save Operation Settings -pipelineOptions.pipelineNamePrompt=Enter pipeline name here -pipelineOptions.selectOperation=Select Operation -pipelineOptions.addOperationButton=Add operation -pipelineOptions.pipelineHeader=Pipeline: -pipelineOptions.saveButton=Download -pipelineOptions.validateButton=Validate +pipelineOptions.header=管道配置 +pipelineOptions.pipelineNameLabel=管道名稱 +pipelineOptions.saveSettings=保存操作設置 +pipelineOptions.pipelineNamePrompt=在此處輸入管道名稱 +pipelineOptions.selectOperation=選擇操作 +pipelineOptions.addOperationButton=添加操作 +pipelineOptions.pipelineHeader=管道: +pipelineOptions.saveButton=下載 +pipelineOptions.validateButton=驗證 @@ -94,6 +114,7 @@ navbar.settings=設定 ############# settings.title=設定 settings.update=有更新可用 +settings.updateAvailable={0} is the current installed version. A new version ({1}) is available. settings.appVersion=應用版本: settings.downloadOption.title=選擇下載選項(對於單一檔案非壓縮下載): settings.downloadOption.1=在同一視窗中開啟 @@ -102,12 +123,13 @@ settings.downloadOption.3=下載檔案 settings.zipThreshold=當下載的檔案數量超過時,壓縮檔案 settings.signOut=登出 settings.accountSettings=帳戶設定 - - +settings.bored.help=Enables easter egg game +settings.cacheInputs.name=Save form inputs +settings.cacheInputs.help=Enable to store previously used inputs for future runs changeCreds.title=變更憑證 changeCreds.header=更新您的帳戶詳細資訊 -changeCreds.changeUserAndPassword=您正在使用預設的登入憑證。請輸入新的密碼(如果需要,也可以輸入使用者名稱) +changeCreds.changePassword=您使用的是預設登錄認證。請輸入新密碼 changeCreds.newUsername=新使用者名稱 changeCreds.oldPassword=目前密碼 changeCreds.newPassword=新密碼 @@ -142,14 +164,18 @@ adminUserSettings.header=管理使用者控制設定 adminUserSettings.admin=管理員 adminUserSettings.user=使用者 adminUserSettings.addUser=新增使用者 +adminUserSettings.usernameInfo=使用者名只能包含字母和數位,不能包含空格或特殊字元。 adminUserSettings.roles=角色 adminUserSettings.role=角色 adminUserSettings.actions=操作 adminUserSettings.apiUser=受限制的 API 使用者 +adminUserSettings.extraApiUser=其他受限 API 使用者 adminUserSettings.webOnlyUser=僅使用網頁的使用者 adminUserSettings.demoUser=示範用途的使用者(無自訂設定) +adminUserSettings.internalApiUser=內部 API 使用者 adminUserSettings.forceChange=強制使用者在登入時修改使用者名稱/密碼 adminUserSettings.submit=儲存 +adminUserSettings.changeUserRole=更改使用者身份 ############# # HOME-PAGE # @@ -387,9 +413,18 @@ home.split-by-sections.title=依區段分割 PDF home.split-by-sections.desc=將 PDF 的每一頁分割為較小的水平和垂直區段 split-by-sections.tags=區段分割, 劃分, 自訂 -home.AddStampRequest.title=Add Stamp to PDF -home.AddStampRequest.desc=Add text or add image stamps at set locations -AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +home.AddStampRequest.title=將圖章添加到 PDF +home.AddStampRequest.desc=在設置位置添加文字或添加圖像圖章 +AddStampRequest.tags=圖章,添加圖片,中心圖像,浮水印,PDF,嵌入,自訂 + + +home.PDFToBook.title=PDF 轉電子書 +home.PDFToBook.desc=使用 calibre 將 PDF 轉換為書籍/漫畫格式 +PDFToBook.tags=電子書,漫畫,Calibre,轉換,日本漫畫,亞馬遜,kindle + +home.BookToPDF.title=電子書轉 PDF +home.BookToPDF.desc=使用 calibre 將書籍/漫畫格式轉換為 PDF +BookToPDF.tags=電子書,漫畫,Calibre,轉換,日本漫畫,亞馬遜,kindle ########################### @@ -399,11 +434,14 @@ AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Cust ########################### #login login.title=登入 +login.header=登入 login.signin=登入 login.rememberme=記住我 login.invalid=使用者名稱或密碼無效。 login.locked=您的帳戶已被鎖定。 login.signinTitle=請登入 +login.ssoSignIn=透過織網單一簽入 +login.oauth2AutoCreateDisabled=OAUTH2自動建立使用者已停用 #auto-redact @@ -436,6 +474,7 @@ pdfToSinglePage.submit=轉換為單一頁面 pageExtracter.title=提取頁面 pageExtracter.header=提取頁面 pageExtracter.submit=提取 +pageExtracter.placeholder=(例如 1,2,8 或 4,7,12-16 或 2n-1) #getPdfInfo @@ -450,7 +489,7 @@ MarkdownToPDF.title=Markdown 轉 PDF MarkdownToPDF.header=Markdown 轉 PDF MarkdownToPDF.submit=轉換 MarkdownToPDF.help=正在進行中 -MarkdownToPDF.credit=使用 WeasyPrint +MarkdownToPDF.credit=此服務使用 WeasyPrint 進行轉換 @@ -458,7 +497,7 @@ MarkdownToPDF.credit=使用 WeasyPrint URLToPDF.title=URL 轉 PDF URLToPDF.header=URL 轉 PDF URLToPDF.submit=轉換 -URLToPDF.credit=使用 WeasyPrint +URLToPDF.credit=此服務使用 WeasyPrint 進行轉換 #html-to-pdf @@ -466,38 +505,38 @@ HTMLToPDF.title=HTML 轉 PDF HTMLToPDF.header=HTML 轉 PDF HTMLToPDF.help=接受 HTML 文件和包含所需 html/css/images 等的 ZIP HTMLToPDF.submit=轉換 -HTMLToPDF.credit=使用 WeasyPrint -HTMLToPDF.zoom=Zoom level for displaying the website. -HTMLToPDF.pageWidth=Width of the page in centimeters. (Blank to default) -HTMLToPDF.pageHeight=Height of the page in centimeters. (Blank to default) -HTMLToPDF.marginTop=Top margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginBottom=Bottom margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginLeft=Left margin of the page in millimeters. (Blank to default) -HTMLToPDF.marginRight=Right margin of the page in millimeters. (Blank to default) -HTMLToPDF.printBackground=Render the background of websites. -HTMLToPDF.defaultHeader=Enable Default Header (Name and page number) -HTMLToPDF.cssMediaType=Change the CSS media type of the page. -HTMLToPDF.none=None -HTMLToPDF.print=Print -HTMLToPDF.screen=Screen +HTMLToPDF.credit=此服務使用 WeasyPrint 進行轉換 +HTMLToPDF.zoom=用於顯示網站的縮放級別。 +HTMLToPDF.pageWidth=頁面寬度-以釐米為單位(填空則使用預設值) +HTMLToPDF.pageHeight=頁面高度-以釐米為單位(填空則使用預設值) +HTMLToPDF.marginTop=頁面的上邊距-以毫米為單位(填空則使用預設值) +HTMLToPDF.marginBottom=頁面的下邊距-以毫米為單位(填空則使用預設值) +HTMLToPDF.marginLeft=頁面的左邊距-以毫米為單位(填空則使用預設值) +HTMLToPDF.marginRight=頁面的右邊距-以毫米為單位(填空則使用預設值) +HTMLToPDF.printBackground=渲染網站的背景。 +HTMLToPDF.defaultHeader=啟用預設標頭(名稱和頁碼) +HTMLToPDF.cssMediaType=更改頁面的 CSS 媒體類型。 +HTMLToPDF.none=無 +HTMLToPDF.print=列印 +HTMLToPDF.screen=螢幕 #AddStampRequest -AddStampRequest.header=Stamp PDF -AddStampRequest.title=Stamp PDF -AddStampRequest.stampType=Stamp Type -AddStampRequest.stampText=Stamp Text -AddStampRequest.stampImage=Stamp Image -AddStampRequest.alphabet=Alphabet -AddStampRequest.fontSize=Font/Image Size -AddStampRequest.rotation=Rotation -AddStampRequest.opacity=Opacity -AddStampRequest.position=Position -AddStampRequest.overrideX=Override X Coordinate -AddStampRequest.overrideY=Override Y Coordinate -AddStampRequest.customMargin=Custom Margin -AddStampRequest.customColor=Custom Text Color -AddStampRequest.submit=Submit +AddStampRequest.header=圖章 PDF +AddStampRequest.title=圖章 PDF +AddStampRequest.stampType=圖章類型 +AddStampRequest.stampText=圖章文字 +AddStampRequest.stampImage=圖章圖片 +AddStampRequest.alphabet=字母表 +AddStampRequest.fontSize=字體/圖像大小 +AddStampRequest.rotation=旋轉 +AddStampRequest.opacity=透明度 +AddStampRequest.position=位置 +AddStampRequest.overrideX=覆蓋 X 座標 +AddStampRequest.overrideY=覆蓋 Y 座標 +AddStampRequest.customMargin=自訂邊緣 +AddStampRequest.customColor=自訂文字顏色 +AddStampRequest.submit=送出 #sanitizePDF @@ -522,7 +561,7 @@ addPageNumbers.selectText.5=要編號的頁面 addPageNumbers.selectText.6=自訂文字 addPageNumbers.customTextDesc=自訂文字 addPageNumbers.numberPagesDesc=要編號的頁面,預設為 '全部',也可使用 1-5 或 2,5,9 等格式 -addPageNumbers.customNumberDesc=預設為 {n},也接受 '頁面 {n} 共 {total}','文字-{n}','{filename}-{n} +addPageNumbers.customNumberDesc=預設為 {n},也接受 '頁面 {n} 共 {total}','文字-{n}','{filename}-{n}' addPageNumbers.submit=新增頁碼 @@ -586,11 +625,11 @@ scalePages.submit=送出 certSign.title=憑證簽章 certSign.header=使用您的憑證簽章(進行中) certSign.selectPDF=選擇要簽章的 PDF 檔案: -certSign.jksNote=Note: If your certificate type is not listed below, please convert it to a Java Keystore (.jks) file using the keytool command line tool. Then, choose the .jks file option below. +certSign.jksNote=注意:如果您的證書類型未在下面列出,請使用keytool命令行工具將其轉換為Java Keystore (.jks) 檔。 然後,選擇下面的 .jks 文件選項。 certSign.selectKey=選擇您的私鑰文件(PKCS#8 格式,可能是 .pem 或 .der): certSign.selectCert=選擇您的憑證文件(X.509 格式,可能是 .pem 或 .der): certSign.selectP12=選擇您的 PKCS#12 金鑰庫文件(.p12 或 .pfx)(可選,如果提供,它應包含您的私鑰和憑證): -certSign.selectJKS=Select Your Java Keystore File (.jks or .keystore): +certSign.selectJKS=選擇你的 Java Keystore 檔 (.jks 或 .keystore): certSign.certType=憑證類型 certSign.password=輸入您的金鑰庫或私鑰密碼(如果有): certSign.showSig=顯示簽章 @@ -623,6 +662,18 @@ compare.document.1=文件 1 compare.document.2=文件 2 compare.submit=比較 +#BookToPDF +BookToPDF.title=電子書和漫畫轉 PDF +BookToPDF.header=電子書轉 PDF +BookToPDF.credit=此服務使用 Calibre 進行轉換 +BookToPDF.submit=轉換 + +#PDFToBook +PDFToBook.title=PDF 轉電子書 +PDFToBook.header=PDF 轉電子書 +PDFToBook.selectText.1=格式 +PDFToBook.credit=此服務使用 Calibre 進行轉換 +PDFToBook.submit=轉換 #sign sign.title=簽章 @@ -643,6 +694,7 @@ repair.submit=修復 #flatten flatten.title=平坦化 flatten.header=PDF 平坦化 +flatten.flattenOnlyForms=Flatten only forms flatten.submit=平坦化 @@ -726,11 +778,23 @@ merge.submit=合併 pdfOrganiser.title=頁面整理 pdfOrganiser.header=PDF 頁面整理 pdfOrganiser.submit=重新排列頁面 +pdfOrganiser.mode=模式 +pdfOrganiser.mode.1=自定義頁面順序 +pdfOrganiser.mode.2=反向順序 +pdfOrganiser.mode.3=雙工排序 +pdfOrganiser.mode.4=摺頁冊排序 +pdfOrganiser.mode.5=側裝訂摺頁冊排序 +pdfOrganiser.mode.6=奇偶拆分 +pdfOrganiser.mode.7=刪除第一頁 +pdfOrganiser.mode.8=刪除最後一頁 +pdfOrganiser.mode.9=刪除第一頁和最後一頁 +pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1) #multiTool multiTool.title=PDF 多工具 multiTool.header=PDF 多工具 +multiTool.uploadPrompts=Please Upload PDF #view pdf viewPdf.title=檢視 PDF @@ -741,6 +805,7 @@ pageRemover.title=頁面移除 pageRemover.header=PDF 頁面移除 pageRemover.pagesToDelete=要刪除的頁面(輸入以逗號分隔的頁碼): pageRemover.submit=刪除頁面 +pageRemover.placeholder=(例如 1,2,6 或 1-10,15-30) #rotate @@ -750,17 +815,17 @@ rotate.selectAngle=選擇旋轉角度(以 90 度的倍數): rotate.submit=旋轉 -#merge +#split-pdfs split.title=分割 PDF split.header=分割 PDF split.desc.1=您選擇的數字是您希望進行分割的頁碼 -split.desc.2=因此,選擇 1,3,7-8 將會將一個 10 頁的文件分割為 6 個單獨的 PDF,包括: +split.desc.2=因此,選擇 1,3,7-9 將會將一個 10 頁的文件分割為 6 個單獨的 PDF,包括: split.desc.3=文件 #1:頁面 1 split.desc.4=文件 #2:頁面 2 和 3 -split.desc.5=文件 #3:頁面 4、5 和 6 -split.desc.6=文件 #4:頁面 7 -split.desc.7=文件 #5:頁面 8 -split.desc.8=文件 #6:頁面 9 和 10 +split.desc.5=文件 #3:頁面 4、5、6 和 7 +split.desc.6=文件 #4:頁面 8 +split.desc.7=文件 #5:頁面 9 +split.desc.8=文件 #6:頁面 10 split.splitPages=輸入要分割的頁面: split.submit=分割 @@ -828,6 +893,8 @@ watermark.selectText.7=不透明度(0% - 100%): watermark.selectText.8=浮水印類型: watermark.selectText.9=浮水印影像: watermark.submit=新增浮水印 +watermark.type.1=文字 +watermark.type.2=圖片 #Change permissions @@ -879,6 +946,8 @@ pdfToPDFA.title=PDF 轉 PDF/A pdfToPDFA.header=PDF 轉 PDF/A pdfToPDFA.credit=此服務使用 OCRmyPDF 進行 PDF/A 轉換 pdfToPDFA.submit=轉換 +pdfToPDFA.tip=目前不支援上傳多個 +pdfToPDFA.outputFormat=Output format #PDFToWord @@ -908,7 +977,7 @@ PDFToText.submit=轉換 #PDFToHTML PDFToHTML.title=PDF 轉 HTML PDFToHTML.header=PDF 轉 HTML -PDFToHTML.credit=此服務使用 LibreOffice 進行檔案轉換。 +PDFToHTML.credit=此服務使用 pdftohtml 進行檔案轉換。 PDFToHTML.submit=轉換 @@ -925,6 +994,7 @@ PDFToCSV.prompt=選擇要提取表格的頁面 PDFToCSV.submit=提取 #split-by-size-or-count +split-by-size-or-count.title=依大小或數量分割 PDF split-by-size-or-count.header=依大小或數量分割 PDF split-by-size-or-count.type.label=選擇分割類型 split-by-size-or-count.type.size=依大小 @@ -959,14 +1029,36 @@ split-by-sections.vertical.label=垂直劃分 split-by-sections.horizontal.placeholder=輸入水平劃分的數量 split-by-sections.vertical.placeholder=輸入垂直劃分的數量 split-by-sections.submit=分割 PDF +split-by-sections.merge=是否合併為一個pdf + + +#printFile +printFile.title=Print File +printFile.header=Print File to Printer +printFile.selectText.1=Select File to Print +printFile.selectText.2=Enter Printer Name +printFile.submit=Print #licenses -licenses.nav=Licenses -licenses.title=3rd Party Licenses -licenses.header=3rd Party Licenses -licenses.module=Module -licenses.version=Version -licenses.license=License +licenses.nav=許可證 +licenses.title=第三方許可證 +licenses.header=第三方許可證 +licenses.module=模組 +licenses.version=版本 +licenses.license=許可證 +# error +error.sorry=對於這個問題,我們感到抱歉! +error.needHelp=需要幫助/發現了一個問題? +error.contactTip=如果你仍然遇到問題,請不要猶豫,隨時向我們尋求幫助。你可以在我們的GitHub頁面提交工單,或通過Discord與我們聯繋: +error.404.head=404 - 找不到頁面 | 哎呀,我們在代碼中走錯了路! +error.404.1=我們好像找不到你正在尋找的頁面。 +error.404.2=出了點錯誤 +error.github=在GitHub上提交工單 +error.showStack=顯示堆疊追蹤 +error.copyStack=複製堆疊追蹤 +error.githubSubmit=GitHub - 提交工單 +error.discordSubmit=Discord - 提交支援帖子 + diff --git a/src/main/resources/settings.yml.template b/src/main/resources/settings.yml.template index 5869bc4e..45f1762d 100644 --- a/src/main/resources/settings.yml.template +++ b/src/main/resources/settings.yml.template @@ -7,19 +7,24 @@ security: csrfDisabled: true loginAttemptCount: 5 # lock user account after 5 tries loginResetTimeMinutes : 120 # lock account for 2 hours after x attempts + #oauth2: + # enabled: false # set to 'true' to enable login (Note: enableLogin must also be 'true' for this to work) + # issuer: "" # set to any provider that supports OpenID Connect Discovery (/.well-known/openid-configuration) end-point + # clientId: "" # Client ID from your provider + # clientSecret: "" # Client Secret from your provider + # autoCreateUser: false # set to 'true' to allow auto-creation of non-existing users system: - defaultLocale: 'en-US' # Set the default language (e.g. 'de-DE', 'fr-FR', etc) googlevisibility: false # 'true' to allow Google visibility (via robots.txt), 'false' to disallow enableAlphaFunctionality: false # Set to enable functionality which might need more testing before it fully goes live (This feature might make no changes) - customApplications: - bookAndHtmlFormatsInstalled: false # Installs Calibre for book format conversion (For non docker it must be manually downloaded but will need to be true to show in UI) - -#ui: -# appName: exampleAppName # Application's visible name -# homeDescription: I am a description # Short description or tagline shown on homepage. -# appNameNavbar: navbarName # Name displayed on the navigation bar + showUpdate: true # see when a new update is available + showUpdateOnlyAdmin: false # Only admins can see when a new update is available, depending on showUpdate it must be set to 'true' + +ui: + appName: null # Application's visible name + homeDescription: null # Short description or tagline shown on homepage. + appNameNavbar: null # Name displayed on the navigation bar endpoints: toRemove: [] # List endpoints to disable (e.g. ['img-to-pdf', 'remove-pages']) @@ -27,3 +32,7 @@ endpoints: metrics: enabled: true # 'true' to enable Info APIs (`/api/*`) endpoints, 'false' to disable + +# Automatically Generated Settings (Do Not Edit Directly) +AutomaticallyGenerated: + key: example \ No newline at end of file diff --git a/src/main/resources/static/3rdPartyLicenses.json b/src/main/resources/static/3rdPartyLicenses.json index c03dd84c..97025001 100644 --- a/src/main/resources/static/3rdPartyLicenses.json +++ b/src/main/resources/static/3rdPartyLicenses.json @@ -3,69 +3,69 @@ { "moduleName": "ch.qos.logback:logback-classic", "moduleUrl": "http://www.qos.ch", - "moduleVersion": "1.4.14", + "moduleVersion": "1.5.3", "moduleLicense": "GNU Lesser General Public License", "moduleLicenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" }, { "moduleName": "ch.qos.logback:logback-core", "moduleUrl": "http://www.qos.ch", - "moduleVersion": "1.4.14", + "moduleVersion": "1.5.3", "moduleLicense": "GNU Lesser General Public License", "moduleLicenseUrl": "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html" }, { "moduleName": "com.fasterxml.jackson.core:jackson-annotations", "moduleUrl": "https://github.com/FasterXML/jackson", - "moduleVersion": "2.15.3", + "moduleVersion": "2.15.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "com.fasterxml.jackson.core:jackson-core", "moduleUrl": "https://github.com/FasterXML/jackson-core", - "moduleVersion": "2.15.3", + "moduleVersion": "2.15.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "com.fasterxml.jackson.core:jackson-databind", "moduleUrl": "https://github.com/FasterXML/jackson", - "moduleVersion": "2.15.3", + "moduleVersion": "2.15.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml", "moduleUrl": "https://github.com/FasterXML/jackson-dataformats-text", - "moduleVersion": "2.15.3", + "moduleVersion": "2.15.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jdk8", "moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8", - "moduleVersion": "2.15.3", + "moduleVersion": "2.15.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "com.fasterxml.jackson.datatype:jackson-datatype-jsr310", "moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jsr310", - "moduleVersion": "2.15.3", + "moduleVersion": "2.15.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "com.fasterxml.jackson.module:jackson-module-parameter-names", "moduleUrl": "https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names", - "moduleVersion": "2.15.3", + "moduleVersion": "2.15.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "com.fasterxml.jackson:jackson-bom", - "moduleVersion": "2.15.3" + "moduleVersion": "2.15.4" }, { "moduleName": "com.fasterxml:classmate", @@ -74,6 +74,20 @@ "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, + { + "moduleName": "com.fathzer:javaluator", + "moduleUrl": "http://javaluator.fathzer.com", + "moduleVersion": "3.0.3", + "moduleLicense": "GNU Lesser General Public License v3 (LGPL-v3)", + "moduleLicenseUrl": "http://www.gnu.org/licenses/lgpl-3.0.html" + }, + { + "moduleName": "com.github.stephenc.jcip:jcip-annotations", + "moduleUrl": "http://stephenc.github.com/jcip-annotations", + "moduleVersion": "1.0-1", + "moduleLicense": "Apache License, Version 2.0", + "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" + }, { "moduleName": "com.github.vladimir-bukhtoyarov:bucket4j-core", "moduleUrl": "http://github.com/vladimir-bukhtoyarov/bucket4j/bucket4j-core", @@ -84,7 +98,7 @@ { "moduleName": "com.google.zxing:core", "moduleUrl": "https://github.com/zxing/zxing/core", - "moduleVersion": "3.5.2", + "moduleVersion": "3.5.3", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, @@ -102,6 +116,34 @@ "moduleLicense": "LGPL", "moduleLicenseUrl": "http://www.martiansoftware.com/jsap/license.html" }, + { + "moduleName": "com.nimbusds:content-type", + "moduleUrl": "https://connect2id.com", + "moduleVersion": "2.2", + "moduleLicense": "The Apache Software License, Version 2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" + }, + { + "moduleName": "com.nimbusds:lang-tag", + "moduleUrl": "https://connect2id.com/", + "moduleVersion": "1.7", + "moduleLicense": "The Apache Software License, Version 2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" + }, + { + "moduleName": "com.nimbusds:nimbus-jose-jwt", + "moduleUrl": "https://connect2id.com", + "moduleVersion": "9.24.4", + "moduleLicense": "The Apache Software License, Version 2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" + }, + { + "moduleName": "com.nimbusds:oauth2-oidc-sdk", + "moduleUrl": "https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions", + "moduleVersion": "9.43.3", + "moduleLicense": "Apache License, version 2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.html" + }, { "moduleName": "com.opencsv:opencsv", "moduleUrl": "http://opencsv.sf.net", @@ -214,35 +256,35 @@ { "moduleName": "io.github.pixee:java-security-toolkit", "moduleUrl": "https://github.com/pixee/java-security-toolkit", - "moduleVersion": "1.1.2", + "moduleVersion": "1.1.3", "moduleLicense": "MIT License", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" }, { "moduleName": "io.micrometer:micrometer-commons", "moduleUrl": "https://github.com/micrometer-metrics/micrometer", - "moduleVersion": "1.12.2", + "moduleVersion": "1.12.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "io.micrometer:micrometer-core", "moduleUrl": "https://github.com/micrometer-metrics/micrometer", - "moduleVersion": "1.12.2", + "moduleVersion": "1.12.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "io.micrometer:micrometer-jakarta9", "moduleUrl": "https://github.com/micrometer-metrics/micrometer", - "moduleVersion": "1.12.2", + "moduleVersion": "1.12.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "io.micrometer:micrometer-observation", "moduleUrl": "https://github.com/micrometer-metrics/micrometer", - "moduleVersion": "1.12.2", + "moduleVersion": "1.12.4", "moduleLicense": "The Apache Software License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, @@ -276,7 +318,7 @@ { "moduleName": "jakarta.activation:jakarta.activation-api", "moduleUrl": "https://www.eclipse.org", - "moduleVersion": "2.1.2", + "moduleVersion": "2.1.3", "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, @@ -318,16 +360,30 @@ { "moduleName": "jakarta.xml.bind:jakarta.xml.bind-api", "moduleUrl": "https://www.eclipse.org", - "moduleVersion": "4.0.1", + "moduleVersion": "4.0.2", "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleName": "net.bytebuddy:byte-buddy", - "moduleVersion": "1.14.11", + "moduleVersion": "1.14.12", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, + { + "moduleName": "net.minidev:accessors-smart", + "moduleUrl": "https://urielch.github.io/", + "moduleVersion": "2.5.0", + "moduleLicense": "The Apache Software License, Version 2.0", + "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" + }, + { + "moduleName": "net.minidev:json-smart", + "moduleUrl": "https://urielch.github.io/", + "moduleVersion": "2.5.0", + "moduleLicense": "The Apache Software License, Version 2.0", + "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" + }, { "moduleName": "org.antlr:antlr4-runtime", "moduleUrl": "https://www.antlr.org/", @@ -371,49 +427,49 @@ { "moduleName": "org.apache.pdfbox:fontbox", "moduleUrl": "https://pdfbox.apache.org", - "moduleVersion": "3.0.1", + "moduleVersion": "3.0.2", "moduleLicense": "Apache-2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "org.apache.pdfbox:pdfbox", "moduleUrl": "https://pdfbox.apache.org", - "moduleVersion": "3.0.1", + "moduleVersion": "3.0.2", "moduleLicense": "Apache-2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "org.apache.pdfbox:pdfbox-io", "moduleUrl": "https://pdfbox.apache.org", - "moduleVersion": "3.0.1", + "moduleVersion": "3.0.2", "moduleLicense": "Apache-2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "org.apache.pdfbox:xmpbox", "moduleUrl": "https://pdfbox.apache.org", - "moduleVersion": "3.0.1", + "moduleVersion": "3.0.2", "moduleLicense": "Apache-2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "org.apache.tomcat.embed:tomcat-embed-core", "moduleUrl": "https://tomcat.apache.org/", - "moduleVersion": "10.1.18", + "moduleVersion": "10.1.19", "moduleLicense": "Eclipse Public License - v 2.0", "moduleLicenseUrl": "https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt" }, { "moduleName": "org.apache.tomcat.embed:tomcat-embed-el", "moduleUrl": "https://tomcat.apache.org/", - "moduleVersion": "10.1.18", + "moduleVersion": "10.1.19", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "org.apache.tomcat.embed:tomcat-embed-websocket", "moduleUrl": "https://tomcat.apache.org/", - "moduleVersion": "10.1.18", + "moduleVersion": "10.1.19", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, @@ -467,41 +523,41 @@ }, { "moduleName": "org.commonmark:commonmark", - "moduleVersion": "0.21.0", + "moduleVersion": "0.22.0", "moduleLicense": "BSD 2-Clause License", "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause" }, { "moduleName": "org.commonmark:commonmark-ext-gfm-tables", - "moduleVersion": "0.21.0", + "moduleVersion": "0.22.0", "moduleLicense": "BSD 2-Clause License", "moduleLicenseUrl": "https://opensource.org/licenses/BSD-2-Clause" }, { "moduleName": "org.eclipse.angus:angus-activation", "moduleUrl": "https://www.eclipse.org", - "moduleVersion": "2.0.1", + "moduleVersion": "2.0.2", "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleName": "org.glassfish.jaxb:jaxb-core", "moduleUrl": "https://www.eclipse.org", - "moduleVersion": "4.0.4", + "moduleVersion": "4.0.5", "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleName": "org.glassfish.jaxb:jaxb-runtime", "moduleUrl": "https://www.eclipse.org", - "moduleVersion": "4.0.4", + "moduleVersion": "4.0.5", "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, { "moduleName": "org.glassfish.jaxb:txw2", "moduleUrl": "https://eclipse-ee4j.github.io/jaxb-ri/", - "moduleVersion": "4.0.4", + "moduleVersion": "4.0.5", "moduleLicense": "GNU General Public License, version 2 with the GNU Classpath Exception", "moduleLicenseUrl": "https://www.gnu.org/software/classpath/license.html" }, @@ -522,7 +578,7 @@ { "moduleName": "org.hibernate.orm:hibernate-core", "moduleUrl": "https://www.hibernate.org/orm/6.4", - "moduleVersion": "6.4.1.Final", + "moduleVersion": "6.4.4.Final", "moduleLicense": "GNU Library General Public License v2.1 or later", "moduleLicenseUrl": "https://www.opensource.org/licenses/LGPL-2.1" }, @@ -533,52 +589,6 @@ "moduleLicense": "Public Domain", "moduleLicenseUrl": "http://repository.jboss.org/licenses/cc0-1.0.txt" }, - { - "moduleName": "org.junit.jupiter:junit-jupiter", - "moduleUrl": "https://junit.org/junit5/", - "moduleVersion": "5.10.1", - "moduleLicense": "Eclipse Public License v2.0", - "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" - }, - { - "moduleName": "org.junit.jupiter:junit-jupiter-api", - "moduleUrl": "https://junit.org/junit5/", - "moduleVersion": "5.10.1", - "moduleLicense": "Eclipse Public License v2.0", - "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" - }, - { - "moduleName": "org.junit.jupiter:junit-jupiter-engine", - "moduleUrl": "https://junit.org/junit5/", - "moduleVersion": "5.10.1", - "moduleLicense": "Eclipse Public License v2.0", - "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" - }, - { - "moduleName": "org.junit.jupiter:junit-jupiter-params", - "moduleUrl": "https://junit.org/junit5/", - "moduleVersion": "5.10.1", - "moduleLicense": "Eclipse Public License v2.0", - "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" - }, - { - "moduleName": "org.junit.platform:junit-platform-commons", - "moduleUrl": "https://junit.org/junit5/", - "moduleVersion": "1.10.1", - "moduleLicense": "Eclipse Public License v2.0", - "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" - }, - { - "moduleName": "org.junit.platform:junit-platform-engine", - "moduleUrl": "https://junit.org/junit5/", - "moduleVersion": "1.10.1", - "moduleLicense": "Eclipse Public License v2.0", - "moduleLicenseUrl": "https://www.eclipse.org/legal/epl-v20.html" - }, - { - "moduleName": "org.junit:junit-bom", - "moduleVersion": "5.10.1" - }, { "moduleName": "org.latencyutils:LatencyUtils", "moduleUrl": "http://latencyutils.github.io/LatencyUtils/", @@ -587,23 +597,23 @@ "moduleLicenseUrl": "http://creativecommons.org/publicdomain/zero/1.0/" }, { - "moduleName": "org.opentest4j:opentest4j", - "moduleUrl": "https://github.com/ota4j-team/opentest4j", - "moduleVersion": "1.3.0", - "moduleLicense": "The Apache License, Version 2.0", - "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0.txt" + "moduleName": "org.ow2.asm:asm", + "moduleUrl": "http://asm.ow2.org", + "moduleVersion": "9.3", + "moduleLicense": "The Apache Software License, Version 2.0", + "moduleLicenseUrl": "http://www.apache.org/licenses/LICENSE-2.0.txt" }, { "moduleName": "org.slf4j:jul-to-slf4j", "moduleUrl": "http://www.slf4j.org", - "moduleVersion": "2.0.11", + "moduleVersion": "2.0.12", "moduleLicense": "MIT License", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" }, { "moduleName": "org.slf4j:slf4j-api", "moduleUrl": "http://www.slf4j.org", - "moduleVersion": "2.0.11", + "moduleVersion": "2.0.12", "moduleLicense": "MIT License", "moduleLicenseUrl": "http://www.opensource.org/licenses/mit-license.php" }, @@ -628,238 +638,266 @@ { "moduleName": "org.springframework.boot:spring-boot", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-actuator", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-actuator-autoconfigure", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-autoconfigure", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-devtools", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-actuator", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-aop", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-data-jpa", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-jdbc", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-json", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-logging", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", + "moduleLicense": "Apache License, Version 2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" + }, + { + "moduleName": "org.springframework.boot:spring-boot-starter-oauth2-client", + "moduleUrl": "https://spring.io/projects/spring-boot", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-security", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-thymeleaf", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-tomcat", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.boot:spring-boot-starter-web", "moduleUrl": "https://spring.io/projects/spring-boot", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.data:spring-data-commons", "moduleUrl": "https://spring.io/projects/spring-data", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.data:spring-data-jpa", "moduleUrl": "https://projects.spring.io/spring-data-jpa", - "moduleVersion": "3.2.2", + "moduleVersion": "3.2.4", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.security:spring-security-config", "moduleUrl": "https://spring.io/projects/spring-security", - "moduleVersion": "6.2.1", + "moduleVersion": "6.2.3", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.security:spring-security-core", "moduleUrl": "https://spring.io/projects/spring-security", - "moduleVersion": "6.2.1", + "moduleVersion": "6.2.3", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.security:spring-security-crypto", "moduleUrl": "https://spring.io/projects/spring-security", - "moduleVersion": "6.2.1", + "moduleVersion": "6.2.3", + "moduleLicense": "Apache License, Version 2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" + }, + { + "moduleName": "org.springframework.security:spring-security-oauth2-client", + "moduleUrl": "https://spring.io/projects/spring-security", + "moduleVersion": "6.2.3", + "moduleLicense": "Apache License, Version 2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" + }, + { + "moduleName": "org.springframework.security:spring-security-oauth2-core", + "moduleUrl": "https://spring.io/projects/spring-security", + "moduleVersion": "6.2.3", + "moduleLicense": "Apache License, Version 2.0", + "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" + }, + { + "moduleName": "org.springframework.security:spring-security-oauth2-jose", + "moduleUrl": "https://spring.io/projects/spring-security", + "moduleVersion": "6.2.3", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework.security:spring-security-web", "moduleUrl": "https://spring.io/projects/spring-security", - "moduleVersion": "6.2.1", + "moduleVersion": "6.2.3", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-aop", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-aspects", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-beans", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-context", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-core", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-expression", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-jcl", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-jdbc", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-orm", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-tx", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-web", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.3", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, { "moduleName": "org.springframework:spring-webmvc", "moduleUrl": "https://github.com/spring-projects/spring-framework", - "moduleVersion": "6.1.2", + "moduleVersion": "6.1.5", "moduleLicense": "Apache License, Version 2.0", "moduleLicenseUrl": "https://www.apache.org/licenses/LICENSE-2.0" }, diff --git a/src/main/resources/static/css/account.css b/src/main/resources/static/css/account.css new file mode 100644 index 00000000..957fcd9f --- /dev/null +++ b/src/main/resources/static/css/account.css @@ -0,0 +1,4 @@ +.buttons-container { + margin-top: 20px; + text-align: center; +} diff --git a/src/main/resources/static/css/add-image.css b/src/main/resources/static/css/add-image.css new file mode 100644 index 00000000..5a735b42 --- /dev/null +++ b/src/main/resources/static/css/add-image.css @@ -0,0 +1,28 @@ +#box-drag-container { + position: relative; + margin: 20px 0; +} +#pdf-canvas { + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384); + width: 100%; +} +.draggable-buttons-box { + position: absolute; + top: 0; + padding: 10px; + width: 100%; + display: flex; + gap: 5px; +} +.draggable-buttons-box > button { + z-index: 10; + background-color: rgba(13, 110, 253, 0.1); +} +.draggable-canvas { + border: 1px solid red; + position: absolute; + touch-action: none; + user-select: none; + top: 0px; + left: 0; +} diff --git a/src/main/resources/static/css/dark-mode.css b/src/main/resources/static/css/dark-mode.css index 6f914fc3..b874f410 100644 --- a/src/main/resources/static/css/dark-mode.css +++ b/src/main/resources/static/css/dark-mode.css @@ -1,9 +1,11 @@ /* Dark Mode Styles */ -body, select, textarea { - --body-background-color: 51, 51, 51; - --base-font-color: 255, 255, 255; - background-color: rgb(var(--body-background-color)) !important; - color: rgb(var(--base-font-color)) !important; +body, +select, +textarea { + --body-background-color: 51, 51, 51; + --base-font-color: 255, 255, 255; + background-color: rgb(var(--body-background-color)) !important; + color: rgb(var(--base-font-color)) !important; } .card { background-color: rgb(var(--body-background-color)) !important; @@ -11,11 +13,11 @@ body, select, textarea { color: rgb(var(--base-font-color)) !important; } - a { - color: #add8e6; - } - a:hover { - color: #87ceeb; /* Slightly brighter blue on hover for accessibility */ +a { + color: #add8e6; +} +a:hover { + color: #87ceeb; /* Slightly brighter blue on hover for accessibility */ } .dark-card { @@ -36,7 +38,7 @@ body, select, textarea { color: rgb(var(--base-font-color)) !important; } #support-section { - background-color: #444 !important; + background-color: #444 !important; } #pages-container-wrapper { @@ -47,89 +49,93 @@ body, select, textarea { } .favorite-icon img { - filter: brightness(0) invert(1) !important; + filter: brightness(0) invert(1) !important; } table thead { - background-color: #333 !important; - border: 1px solid #444; + background-color: #333 !important; + border: 1px solid #444; } -table th, table td { - border: 1px solid #444 !important; - color: white; +table th, +table td { + border: 1px solid #444 !important; + color: white; } .btn { - background-color: #444 !important; - border: none; - color: #fff !important; + background-color: #444 !important; + border: none; + color: #fff !important; } .btn-primary { - background-color: #007bff !important; - border: none; - color: #fff !important; + background-color: #007bff !important; + border: none; + color: #fff !important; } .btn-secondary { - background-color: #6c757d !important; - border: none; - color: #fff !important; + background-color: #6c757d !important; + border: none; + color: #fff !important; } .btn-info { - background-color: #17a2b8 !important; - border: none; - color: #fff !important; + background-color: #17a2b8 !important; + border: none; + color: #fff !important; } .btn-danger { - background-color: #dc3545 !important; - border: none; - color: #fff !important; + background-color: #dc3545 !important; + border: none; + color: #fff !important; } .btn-warning { - background-color: #ffc107 !important; - border: none; - color: #000 !important; + background-color: #ffc107 !important; + border: none; + color: #000 !important; } .btn-outline-secondary { - color: #fff !important; - border-color: #fff; + color: #fff !important; + border-color: #fff; } .btn-outline-secondary:hover { - background-color: #444 !important; - color: #007bff !important; - border-color: #007bff; + background-color: #444 !important; + color: #007bff !important; + border-color: #007bff; } .blackwhite-icon { - filter: brightness(0) invert(1); + filter: brightness(0) invert(1); } hr { - border-color: rgba(255, 255, 255, 0.6); /* semi-transparent white */ - background-color: rgba(255, 255, 255, 0.6); /* for some browsers that might use background instead of border for
*/ + border-color: rgba(255, 255, 255, 0.6); /* semi-transparent white */ + background-color: rgba(255, 255, 255, 0.6); /* for some browsers that might use background instead of border for
*/ } .modal-content { - color: #fff !important; - border-color: #fff; + color: #fff !important; + border-color: #fff; } -#global-buttons-container input { - background-color: #323948; - caret-color: #ffffff; - color: #ffffff; +.global-buttons-container input { + background-color: #323948; + caret-color: #ffffff; + color: #ffffff; } -#global-buttons-container input::placeholder { - color: #ffffff; +.global-buttons-container input::placeholder { + color: #ffffff; } -#global-buttons-container input:disabled::-webkit-input-placeholder { /* WebKit browsers */ - color: #6E6865; +.global-buttons-container input:disabled::-webkit-input-placeholder { + /* WebKit browsers */ + color: #6e6865; } -#global-buttons-container input:disabled:-moz-placeholder { /* Mozilla Firefox 4 to 18 */ - color: #6E6865; +.global-buttons-container input:disabled:-moz-placeholder { + /* Mozilla Firefox 4 to 18 */ + color: #6e6865; } -#global-buttons-container input:disabled::-moz-placeholder { /* Mozilla Firefox 19+ */ - color: #6E6865; +.global-buttons-container input:disabled::-moz-placeholder { + /* Mozilla Firefox 19+ */ + color: #6e6865; } -#global-buttons-container input:disabled:-ms-input-placeholder { /* Internet Explorer 10+ */ - color: #6E6865; +.global-buttons-container input:disabled:-ms-input-placeholder { + /* Internet Explorer 10+ */ + color: #6e6865; } - diff --git a/src/main/resources/static/css/dragdrop.css b/src/main/resources/static/css/dragdrop.css index e75a0d06..7758bdd9 100644 --- a/src/main/resources/static/css/dragdrop.css +++ b/src/main/resources/static/css/dragdrop.css @@ -1,78 +1,78 @@ #drag-container { - position: fixed; - display:flex; - inset: 0; - pointer-events: none; - z-index: 10000; - visibility: hidden; + position: fixed; + display: flex; + inset: 0; + pointer-events: none; + z-index: 10000; + visibility: hidden; } #drag-container:not(:empty) { - visibility: visible; + visibility: visible; } #drag-container .dragged-img { - position: fixed; - max-width: 200px; - max-height: 200px; - box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.58); - transform-origin: top left; + position: fixed; + max-width: 200px; + max-height: 200px; + box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.58); + transform-origin: top left; } .drag-manager_dragging { - width: 0px; - visibility: hidden; + width: 0px; + visibility: hidden; } .drag-manager_draghover { - width: 375px !important; + width: 375px !important; } .drag-manager_draghover .insert-file-button-container { - display: none !important; + display: none !important; } .drag-manager_draghover .button-container { - visibility: hidden !important; + visibility: hidden !important; } -html[lang-direction=ltr] .drag-manager_draghover img { - left: calc(50% + 62.5px) !important; +html[dir="ltr"] .drag-manager_draghover img { + left: calc(50% + 62.5px) !important; } -html[lang-direction=rtl] .drag-manager_draghover img { - left: 125px +html[dir="rtl"] .drag-manager_draghover img { + left: 125px; } .drag-manager_dragging-container .hide-on-drag { - display: none !important; + display: none !important; } .drag-manager_endpoint { - width: 80px; - height: 100%; - background-color: #FFFFFF10; - transition: width 0.1s; - animation: end-drop-expand .3s ease; - display: flex; - align-items: center; - justify-content: center; + width: 80px; + height: 100%; + background-color: #ffffff10; + transition: width 0.1s; + animation: end-drop-expand 0.3s ease; + display: flex; + align-items: center; + justify-content: center; } .drag-manager_endpoint svg { - width: 50px; - height: 50px; + width: 50px; + height: 50px; } .drag-manager_endpoint.drag-manager_draghover { - width: 150px !important; + width: 150px !important; } @keyframes end-drop-expand { - from { - width: 0; - } - to { - width: 80px; - } -} \ No newline at end of file + from { + width: 0; + } + to { + width: 80px; + } +} diff --git a/src/main/resources/static/css/error.css b/src/main/resources/static/css/error.css new file mode 100644 index 00000000..5cc37b61 --- /dev/null +++ b/src/main/resources/static/css/error.css @@ -0,0 +1,88 @@ +h1 { + text-align: center; + margin-top: 10%; +} + +p { + text-align: center; + margin-top: 2em; +} + +.button:hover { + background-color: #005b7f; +} + +.features-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr)); + gap: 25px 30px; +} + +.feature-card { + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; + padding: 1.25rem; + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.feature-card .card-text { + flex: 1; +} + +#support-section { + background-color: #f9f9f9; + padding: 4rem; + margin-top: 1rem; + text-align: center; +} + +#support-section h1 { + margin-top: 0; +} + +#support-section p { + margin-top: 0; +} + +#button-group { + display: flex; + justify-content: center; + flex-wrap: wrap; +} + +#github-button, +#discord-button { + display: inline-block; + padding: 1rem 2rem; + margin: 1rem; + background-color: #008cba; + color: #fff; + font-size: 1.2rem; + text-align: center; + text-decoration: none; + border-radius: 3rem; + transition: all 0.3s ease-in-out; +} + +#github-button:hover, +#discord-button:hover, +.home-button:hover { + background-color: #005b7f; +} + +.home-button { + display: block; + width: 200px; + height: 50px; + margin: 2em auto; + background-color: #008cba; + color: white; + text-align: center; + line-height: 50px; + text-decoration: none; + font-weight: bold; + border-radius: 25px; + transition: all 0.3s ease-in-out; +} diff --git a/src/main/resources/static/css/errorBanner.css b/src/main/resources/static/css/errorBanner.css index 69a940b3..75618731 100644 --- a/src/main/resources/static/css/errorBanner.css +++ b/src/main/resources/static/css/errorBanner.css @@ -1,94 +1,97 @@ #errorContainer { - margin: 20px; /* adjust this value as needed */ + margin: 20px; /* adjust this value as needed */ } #helpModalDialog { - width: 90%; - max-width: 800px; + width: 90%; + max-width: 800px; } #helpModal h1 { - text-align: center; - margin-top: 10%; + text-align: center; + margin-top: 10%; } #helpModal p { - text-align: center; - margin-top: 2em; + text-align: center; + margin-top: 2em; } #helpModal .button:hover { - background-color: #005b7f; + background-color: #005b7f; } #helpModal .features-container { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr)); - gap: 25px 30px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(21rem, 3fr)); + gap: 25px 30px; } #helpModal .feature-card { - border: 1px solid rgba(0, 0, 0, .125); - border-radius: 0.25rem; - padding: 1.25rem; - display: flex; - flex-direction: column; - align-items: flex-start; + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; + padding: 1.25rem; + display: flex; + flex-direction: column; + align-items: flex-start; } #helpModal .feature-card .card-text { - flex: 1; + flex: 1; } #support-section { - background-color: #f9f9f9; - padding: 4rem; - margin-top: 1rem; - text-align: center; + background-color: #f9f9f9; + padding: 4rem; + margin-top: 1rem; + text-align: center; } #support-section h1 { - margin-top: 0; + margin-top: 0; } #support-section p { - margin-top: 0; + margin-top: 0; } #button-group { - display: flex; - justify-content: center; - flex-wrap: wrap; + display: flex; + justify-content: center; + flex-wrap: wrap; } -#github-button, #discord-button { - display: inline-block; - padding: 1rem 2rem; - margin: 1rem; - background-color: #008CBA; - color: #fff; - font-size: 1.2rem; - text-align: center; - text-decoration: none; - border-radius: 3rem; - transition: all 0.3s ease-in-out; +#github-button, +#discord-button { + display: inline-block; + padding: 1rem 2rem; + margin: 1rem; + background-color: #008cba; + color: #fff; + font-size: 1.2rem; + text-align: center; + text-decoration: none; + border-radius: 3rem; + transition: all 0.3s ease-in-out; } -#github-button:hover, #discord-button:hover, #home-button:hover { - background-color: #005b7f; +#github-button:hover, +#discord-button:hover, +.home-button:hover { + background-color: #005b7f; } -#home-button { - display: block; - width: 200px; - height: 50px; - margin: 2em auto; - background-color: #008CBA; - color: white; - text-align: center; - line-height: 50px; - text-decoration: none; - font-weight: bold; - border-radius: 25px; - transition: all 0.3s ease-in-out; -} \ No newline at end of file +.home-button { + display: block; + width: 200px; + height: 50px; + margin: 2em auto; + background-color: #008cba; + color: white; + text-align: center; + line-height: 50px; + text-decoration: none; + font-weight: bold; + border-radius: 25px; + transition: all 0.3s ease-in-out; +} diff --git a/src/main/resources/static/css/fileSelect.css b/src/main/resources/static/css/fileSelect.css index 2cd2c682..e8f12979 100644 --- a/src/main/resources/static/css/fileSelect.css +++ b/src/main/resources/static/css/fileSelect.css @@ -1,10 +1,10 @@ .custom-file-label { - padding-right: 90px; + padding-right: 90px; } .selected-files { - margin-top: 10px; - max-height: 150px; - overflow-y: auto; - white-space: pre-wrap; -} \ No newline at end of file + margin-top: 10px; + max-height: 150px; + overflow-y: auto; + white-space: pre-wrap; +} diff --git a/src/main/resources/static/css/footer.css b/src/main/resources/static/css/footer.css new file mode 100644 index 00000000..f6cf093d --- /dev/null +++ b/src/main/resources/static/css/footer.css @@ -0,0 +1,20 @@ +#footer { + display: flex; + flex-direction: column; /* Stack children vertically */ + justify-content: center; + align-items: center; + width: 100%; +} + +.footer-center { + display: flex; + align-items: center; /* Center children horizontally */ + flex-grow: 1; +} + +.footer-powered-by { + margin-top: auto; /* Pushes the text to the bottom */ + color: grey; + text-align: center; /* Centers the text inside the div */ + width: 100%; /* Full width to center the text properly */ +} diff --git a/src/main/resources/static/css/game.css b/src/main/resources/static/css/game.css index 01b93e94..fc2f7262 100644 --- a/src/main/resources/static/css/game.css +++ b/src/main/resources/static/css/game.css @@ -1,49 +1,54 @@ #game-container { - position: relative; - width: 100vh; - height: 0; - padding-bottom: 75%; /* 4:3 aspect ratio */ - background-color: transparent; - margin: auto; - overflow: hidden; - border: 2px solid black; /* Add border */ + position: relative; + width: 100vh; + height: 0; + padding-bottom: 75%; /* 4:3 aspect ratio */ + background-color: transparent; + margin: auto; + overflow: hidden; + border: 2px solid black; /* Add border */ } -.pdf, .player, .projectile { - position: absolute; +.pdf, +.player, +.projectile { + position: absolute; } .pdf { - width: 50px; - height: 50px; + width: 50px; + height: 50px; } .player { - width: 50px; - height: 50px; + width: 50px; + height: 50px; } .projectile { - background-color: black !important; - width: 5px; - height: 10px; + background-color: black !important; + width: 5px; + height: 10px; } -#score, #level, #lives, #high-score { - color: black; - font-family: sans-serif; - position: absolute; - font-size: calc(14px + 0.25vw); /* Reduced font size */ +#score, +#level, +#lives, +#high-score { + color: black; + font-family: sans-serif; + position: absolute; + font-size: calc(14px + 0.25vw); /* Reduced font size */ } #score { - top: 10px; - left: 10px; + top: 10px; + left: 10px; } #lives { - top: 10px; - left: calc(7vw); /* Adjusted position */ + top: 10px; + left: calc(9vw); /* Adjusted position */ } #high-score { - top: 10px; - left: calc(14vw); /* Adjusted position */ + top: 10px; + left: calc(14vw); /* Adjusted position */ } #level { - top: 10px; - right: 10px; -} \ No newline at end of file + top: 10px; + right: 10px; +} diff --git a/src/main/resources/static/css/general.css b/src/main/resources/static/css/general.css index 934074ca..72edf184 100644 --- a/src/main/resources/static/css/general.css +++ b/src/main/resources/static/css/general.css @@ -1,20 +1,20 @@ #page-container { - min-height: 100vh; - display: flex; - flex-direction: column; + min-height: 100vh; + display: flex; + flex-direction: column; } #content-wrap { - flex: 1; + flex: 1; } #footer { - bottom: 0; - width: 100%; + bottom: 0; + width: 100%; } .navbar { - height: auto; /* Adjusts height automatically based on content */ - white-space: nowrap; /* Prevents wrapping of navbar contents */ + height: auto; /* Adjusts height automatically based on content */ + white-space: nowrap; /* Prevents wrapping of navbar contents */ } /* TODO enable later .navbar .container { @@ -25,70 +25,70 @@ margin-right: auto; }*/ -html[lang-direction=ltr] * { - direction: ltr; +html[dir="ltr"] * { + direction: ltr; } -html[lang-direction=rtl] * { - direction: rtl; - text-align: right; +html[dir="rtl"] * { + direction: rtl; + text-align: right; } .ignore-rtl { - direction: ltr !important; - text-align: left !important; + direction: ltr !important; + text-align: left !important; } .align-top { - position: absolute; - top: 0; + position: absolute; + top: 0; } .align-center-right { - position: absolute; - right: 0; - top: 50%; + position: absolute; + right: 0; + top: 50%; } .align-center-left { - position: absolute; - left: 0; - top: 50%; + position: absolute; + left: 0; + top: 50%; } .align-bottom { - position: absolute; - bottom: 0; + position: absolute; + bottom: 0; } .btn-group > label:first-of-type { - border-top-left-radius: 0.25rem !important; - border-bottom-left-radius: 0.25rem !important; + border-top-left-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; } -html[lang-direction="rtl"] input.form-check-input { - position: relative; - margin-left: 0px; +html[dir="rtl"] input.form-check-input { + position: relative; + margin-left: 0px; } -html[lang-direction="rtl"] label.form-check-label { - display: inline; +html[dir="rtl"] label.form-check-label { + display: inline; } .margin-auto-parent { - width: 100%; - display: flex; + width: 100%; + display: flex; } .margin-center { - margin: 0 auto; + margin: 0 auto; } #pdf-canvas { - box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384); - width: 100%; + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384); + width: 100%; } .fixed-shadow-canvas { - box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384); - width: 100%; + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384); + width: 100%; } .shadow-canvas { - box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384); + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384); } .hidden { - display: none; -} \ No newline at end of file + display: none; +} diff --git a/src/main/resources/static/css/home.css b/src/main/resources/static/css/home.css index fe184637..ff8d1cf7 100644 --- a/src/main/resources/static/css/home.css +++ b/src/main/resources/static/css/home.css @@ -1,64 +1,62 @@ #searchBar { - background-image: url('../images/search.svg'); - background-position: 16px 16px; - background-repeat: no-repeat; - width: 100%; - font-size: 16px; - margin-bottom: 12px; - padding: 12px 20px 12px 40px; - border: 1px solid #ddd; - - + background-image: url("../images/search.svg"); + background-position: 16px 16px; + background-repeat: no-repeat; + width: 100%; + font-size: 16px; + margin-bottom: 12px; + padding: 12px 20px 12px 40px; + border: 1px solid #ddd; } .dark-mode-search { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' hei… 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z'/%3E%3C/svg%3E") !important; - color: #f8f9fa !important; - background-color: #212529 !important; - border-color: #343a40 !important; - } - - + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' hei… 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z'/%3E%3C/svg%3E") !important; + color: #f8f9fa !important; + background-color: #212529 !important; + border-color: #343a40 !important; +} .features-container { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(15rem, 3fr)); - gap: 25px 30px; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(15rem, 3fr)); + gap: 25px 30px; } .feature-card { - border: 2px solid rgba(0, 0, 0, .25); - border-radius: 0.25rem; - padding: 1.25rem; - display: flex; - flex-direction: column; - align-items: flex-start; - background: rgba(13, 110, 253, 0.05); - transition: transform 0.3s, border 0.3s; - transform-origin: center center; - outline: 2px solid transparent; + border: 2px solid rgba(0, 0, 0, 0.25); + border-radius: 0.25rem; + padding: 1.25rem; + display: flex; + flex-direction: column; + align-items: flex-start; + background: rgba(13, 110, 253, 0.05); + transition: + transform 0.3s, + border 0.3s; + transform-origin: center center; + outline: 2px solid transparent; } .feature-card a { - text-decoration: none; - color: inherit; - display: flex; - flex-direction: column; - width: 100%; - height: 100%; + text-decoration: none; + color: inherit; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; } .feature-card .card-text { - flex: 1; + flex: 1; } .feature-card:hover { - outline: 1px solid rgba(0, 0, 0, .5); - cursor: pointer; - transform: scale(1.1); + outline: 1px solid rgba(0, 0, 0, 0.5); + cursor: pointer; + transform: scale(1.1); } .feature-card:hover .card-title { - text-decoration: underline; + text-decoration: underline; } .card-title.text-primary { color: #000; /* Replace with your desired shade of blue */ @@ -67,27 +65,62 @@ .home-card-icon { width: 30px; height: 30px; - transform: translateY(-5px); + transform: translateY(-5px); } .home-card-icon-colour { -filter: invert(0.2) sepia(2) saturate(50) hue-rotate(190deg); + filter: invert(0.2) sepia(2) saturate(50) hue-rotate(190deg); } .favorite-icon { - display: none; - position: absolute; - top: 10px; - right: 10px; + display: none; + position: absolute; + top: 10px; + right: 10px; } /* Only show the favorite icons when the parent card is being hovered over */ .feature-card:hover .favorite-icon { - display: block; + display: block; } .favorite-icon img { - filter: brightness(0); + filter: brightness(0); } .jumbotron { - padding: 3rem 3rem; /* Reduce vertical padding */ + padding: 3rem 3rem; /* Reduce vertical padding */ +} + +.lookatme { + opacity: 1; + position: relative; + display: inline-block; +} + +.lookatme::after { + color: #e33100; + text-shadow: 0 0 5px #e33100; + /* in the html, the data-lookatme-text attribute must */ + /* contain the same text as the .lookatme element */ + content: attr(data-lookatme-text); + padding: inherit; + position: absolute; + inset: 0 0 0 0; + z-index: 1; + /* 20 steps / 2 seconds = 10fps */ + -webkit-animation: 2s infinite Pulse steps(20); + animation: 2s infinite Pulse steps(20); +} + +@keyframes Pulse { + from { + opacity: 0; + } + + 50% { + opacity: 1; + } + + to { + opacity: 0; + } } diff --git a/src/main/resources/static/css/imageHighlighter.css b/src/main/resources/static/css/imageHighlighter.css index 231895d6..397c0c54 100644 --- a/src/main/resources/static/css/imageHighlighter.css +++ b/src/main/resources/static/css/imageHighlighter.css @@ -1,40 +1,43 @@ - #image-highlighter { - position: fixed; - display:flex; - inset: 0; - z-index: 10000; - background-color: rgba(0, 0, 0, 0); - visibility: hidden; - align-items: center; - justify-content: center; - transition: visbility 0.1s linear, background-color 0.1s linear; + position: fixed; + display: flex; + inset: 0; + z-index: 10000; + background-color: rgba(0, 0, 0, 0); + visibility: hidden; + align-items: center; + justify-content: center; + transition: + visbility 0.1s linear, + background-color 0.1s linear; } #image-highlighter > * { - max-width: 80vw; - max-height: 80vh; - animation: image-highlight .1s linear; - transition: transform .1s linear, opacity .1s linear; + max-width: 80vw; + max-height: 80vh; + animation: image-highlight 0.1s linear; + transition: + transform 0.1s linear, + opacity 0.1s linear; } #image-highlighter > *.remove { - transform: scale(0.8) !important; - opacity: 0 !important; + transform: scale(0.8) !important; + opacity: 0 !important; } #image-highlighter:not(:empty) { - background-color: rgba(0, 0, 0, 0.37); - visibility: visible; + background-color: rgba(0, 0, 0, 0.37); + visibility: visible; } @keyframes image-highlight { - from { - transform: scale(0.8); - opacity: 0; - } - to { - transform: scale(1); - opacity: 1; - } -} \ No newline at end of file + from { + transform: scale(0.8); + opacity: 0; + } + to { + transform: scale(1); + opacity: 1; + } +} diff --git a/src/main/resources/static/css/licenses.css b/src/main/resources/static/css/licenses.css new file mode 100644 index 00000000..79abdc34 --- /dev/null +++ b/src/main/resources/static/css/licenses.css @@ -0,0 +1,9 @@ +td a { + text-decoration: none; +} + +td a:hover, +td a:focus { + text-decoration: underline; + /* Adds underline on hover/focus for clarity */ +} diff --git a/src/main/resources/static/css/light-mode.css b/src/main/resources/static/css/light-mode.css index 08efbf4c..f7e79b90 100644 --- a/src/main/resources/static/css/light-mode.css +++ b/src/main/resources/static/css/light-mode.css @@ -1,24 +1,23 @@ /* Dark Mode Styles */ body { - --body-background-color: 255, 255, 255; - --base-font-color: 33, 37, 41; + --body-background-color: 255, 255, 255; + --base-font-color: 33, 37, 41; } - -#global-buttons-container input { - background-color: #ffffff; - /*caret-color: #ffffff;*/ - /*color: #ffffff;*/ +.global-buttons-container input { + background-color: #ffffff; + /*caret-color: #ffffff;*/ + /*color: #ffffff;*/ } -/*#global-buttons-container input:disabled::-webkit-input-placeholder { !* WebKit browsers *!*/ +/*.global-buttons-container input:disabled::-webkit-input-placeholder { !* WebKit browsers *!*/ /* color: #98A0AB;*/ /*}*/ -/*#global-buttons-container input:disabled:-moz-placeholder { !* Mozilla Firefox 4 to 18 *!*/ +/*.global-buttons-container input:disabled:-moz-placeholder { !* Mozilla Firefox 4 to 18 *!*/ /* color: #98A0AB;*/ /*}*/ -/*#global-buttons-container input:disabled::-moz-placeholder { !* Mozilla Firefox 19+ *!*/ +/*.global-buttons-container input:disabled::-moz-placeholder { !* Mozilla Firefox 19+ *!*/ /* color: #98A0AB;*/ /*}*/ -/*#global-buttons-container input:disabled:-ms-input-placeholder { !* Internet Explorer 10+ *!*/ +/*.global-buttons-container input:disabled:-ms-input-placeholder { !* Internet Explorer 10+ *!*/ /* color: #98A0AB;*/ /*}*/ diff --git a/src/main/resources/static/css/login.css b/src/main/resources/static/css/login.css new file mode 100644 index 00000000..743ee606 --- /dev/null +++ b/src/main/resources/static/css/login.css @@ -0,0 +1,111 @@ +html, +body { + height: 100%; +} + +body { + display: flex; + align-items: center; + padding-top: 40px; + padding-bottom: 40px; + background-color: #f5f5f5; +} + +.form-signin { + width: 100%; + max-width: 330px; + padding: 15px; + margin: auto; +} + +.form-signin .checkbox { + font-weight: 400; +} + +.form-signin .form-floating:focus-within { + z-index: 2; +} + +.form-signin input[type="text"] { + margin-bottom: -1px; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.form-signin input[type="password"] { + margin-bottom: 10px; + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.container-flex { + display: flex; + flex-direction: column; + min-height: 100vh; + width: 100%; /* Set width to 100% */ + align-items: center; /* Center its children horizontally */ +} +.footer-bottom { + margin-top: auto; +} +body.light-mode input:-webkit-autofill, +body.light-mode input:-webkit-autofill:hover, +body.light-mode input:-webkit-autofill:focus, +body.light-mode input:-webkit-autofill:active { + -webkit-text-fill-color: #212529; /* Dark font color */ + -webkit-box-shadow: 0 0 0 1000px #f8f9fa inset; /* Light background color */ +} + +/* Dark Mode */ +body.dark-mode input:-webkit-autofill, +body.dark-mode input:-webkit-autofill:hover, +body.dark-mode input:-webkit-autofill:focus, +body.dark-mode input:-webkit-autofill:active { + -webkit-text-fill-color: #f8f9fa; /* Light font color */ + -webkit-box-shadow: 0 0 0 1000px #212529 inset; /* Dark background color */ +} +/* Light Mode */ +body.light-mode .form-floating > input:focus + label { + color: #212529 !important; /* Dark text for light background */ +} + +/* Dark Mode */ +body.dark-mode .form-floating > input:focus + label { + color: #fff !important; /* Light text for dark background */ +} + +body.light-mode .form-floating > label { + color: #212529 !important; /* Dark text for light background */ +} + +body.dark-mode .form-floating > label { + color: #fff !important; /* Light text for dark background */ +} + +/* Removing default styles for ul and li */ +ul { + list-style: none; + padding: 0; + margin: 0; +} + +li { + list-style: none; +} + +/* Positioning the container of these elements to the top right */ +.your-container-class { + position: absolute; + top: 0; + right: 0; + display: flex; +} + +/* Styling for the dropdown */ +.dropdown-menu { + min-width: 200px; /* or whatever width you prefer */ +} + +.navbar-icon { + width: 40px; + height: 40px; +} diff --git a/src/main/resources/static/css/merge.css b/src/main/resources/static/css/merge.css index 866e2e8c..dc3c7657 100644 --- a/src/main/resources/static/css/merge.css +++ b/src/main/resources/static/css/merge.css @@ -1,4 +1,4 @@ - .list-group-item { +.list-group-item { display: flex; justify-content: space-between; align-items: center; @@ -25,5 +25,4 @@ .move-down span { font-weight: bold; font-size: 1.2em; - } diff --git a/src/main/resources/static/css/multi-tool.css b/src/main/resources/static/css/multi-tool.css new file mode 100644 index 00000000..50e038ce --- /dev/null +++ b/src/main/resources/static/css/multi-tool.css @@ -0,0 +1,119 @@ +.multi-tool-container { + max-width: 95vw; + margin: 0 auto; +} + +.global-buttons-container { + display: flex; + gap: 10px; + align-items: start; + + background-color: rgba(13, 110, 253, 0.1); + border: 1px solid rgba(0, 0, 0, 0.25); + backdrop-filter: blur(2px); + + top: 10px; + z-index: 10; + padding: 10px; + border-radius: 8px; +} +.global-buttons-container > * { + padding: 0.6rem 0.75rem; +} + +.global-buttons-container svg { + width: 20px; + height: 20px; +} +#export-button { + margin-left: auto; +} + +#pages-container-wrapper { + --background-color: rgba(0, 0, 0, 0.025); + --scroll-bar-color: #f1f1f1; + --scroll-bar-thumb: #888; + --scroll-bar-thumb-hover: #555; + background-color: var(--background-color); + width: 100%; + display: flex; + flex-direction: column; + padding: 10px 25px; + border-radius: 10px; + overflow-y: hidden; + overflow-x: auto; + min-height: 275px; + margin: 0 0 30px 0; +} + +#pages-container { + margin: auto; + gap: 0px; + display: flex; + align-items: center; + justify-content: center; +} + +/* width */ +#pages-container-wrapper::-webkit-scrollbar { + width: 10px; + height: 10px; +} + +/* Track */ +#pages-container-wrapper::-webkit-scrollbar-track { + background: var(--scroll-bar-color); +} + +/* Handle */ +#pages-container-wrapper::-webkit-scrollbar-thumb { + border-radius: 10px; + background: var(--scroll-bar-thumb); +} + +/* Handle on hover */ +#pages-container-wrapper::-webkit-scrollbar-thumb:hover { + background: var(--scroll-bar-thumb-hover); +} + +.page-container { + height: 250px; + display: flex; + align-items: center; + flex-direction: column-reverse; + aspect-ratio: 1; + text-align: center; + position: relative; + user-select: none; + transition: width 1s linear; +} + +.page-container img { + /* max-width: calc(100% - 15px); */ + max-height: calc(100% - 15px); + max-width: 237px; + display: block; + position: absolute; + left: 50%; + top: 50%; + translate: -50% -50%; + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.384); + border-radius: 4px; + transition: rotate 0.3s; +} + +#add-pdf-button { + margin: 0 auto; +} + +.page-number { + position: absolute; + top: 5px; + right: 5px; + color: white; + background-color: #007bff; /* Primary blue color */ + padding: 3px 6px; + border-radius: 4px; + font-size: 12px; + z-index: 2; +} diff --git a/src/main/resources/static/css/navbar.css b/src/main/resources/static/css/navbar.css index 5bb99a5e..f46c44a6 100644 --- a/src/main/resources/static/css/navbar.css +++ b/src/main/resources/static/css/navbar.css @@ -1,80 +1,116 @@ - - #navbarSearch { - top: 100%; - right: 0; + top: 100%; + right: 0; + transition: all 0.3s; + max-height: 0; + overflow: hidden; +} + +#navbarSearch.show { + max-height: 300px; /* Adjust this to your desired max height */ } #searchForm { - width: 200px; /* Adjust this value as needed */ + width: 200px; /* Adjust this value as needed */ } /* Style the search results to match the navbar */ #searchResults { - max-height: 200px; /* Adjust this value as needed */ - overflow-y: auto; - width: 100%; + max-height: 200px; /* Adjust this value as needed */ + overflow-y: auto; + width: 100%; + max-width: 300px; /* Adjust to your preferred width */ + transition: height 0.3s ease; /* Smooth height transition */ } #searchResults .dropdown-item { - display: flex; - align-items: center; - white-space: nowrap; - height: 50px; /* Fixed height */ - overflow: hidden; /* Hide overflow */ + display: flex; + align-items: center; + white-space: nowrap; + height: 50px; /* Fixed height */ + overflow: hidden; /* Hide overflow */ } #searchResults .icon { - margin-right: 10px; + margin-right: 10px; } #searchResults .icon-text { - display: inline; - overflow: hidden; /* Hide overflow */ - text-overflow: ellipsis; /* Add ellipsis for long text */ + display: inline; + overflow: hidden; /* Hide overflow */ + text-overflow: ellipsis; /* Add ellipsis for long text */ } +#search-icon i { + font-size: 24px; /* Adjust this to your desired size */ + transition: color 0.3s; +} +#search-icon:hover i { + color: #666; /* Adjust this to your hover color */ +} + +.search-input { + transition: + border 0.3s, + box-shadow 0.3s; +} + +.search-input:focus { + border-color: #666; /* Adjust this to your focus color */ + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); /* Adjust this to your desired shadow */ +} + +/* Set a fixed height and styling for each search result item */ +.search-results a { + display: flex; + align-items: center; + gap: 10px; /* space between icon and text */ + height: 40px; /* Adjust based on your design */ + overflow: hidden; /* Prevent content from overflowing */ + white-space: nowrap; /* Prevent text from wrapping to next line */ + text-overflow: ellipsis; /* Truncate text if it's too long */ +} .main-icon { - width: 36px; - height: 36px; - vertical-align: middle; - transform: translateY(-2px); + width: 36px; + height: 36px; + vertical-align: middle; + transform: translateY(-2px); } .icon { - width: 16px; - height: 16px; - vertical-align: middle; - transform: translateY(-2px); + width: 16px; + height: 16px; + vertical-align: middle; + transform: translateY(-2px); } -.icon+.icon { - margin-left: -4px; +.icon + .icon { + margin-left: -4px; } .icon-text { - margin-left: 4px; + margin-left: 4px; } .nav-item-separator { - position: relative; - margin: 0 4px; /* Adjust the margin as needed */ + position: relative; + margin: 0 4px; /* Adjust the margin as needed */ } .nav-item-separator::before { - content: ''; - position: absolute; - left: 0; - top: 10%; /* Adjust the top and bottom margins as needed */ - bottom: 10%; - width: 1px; - background-color: #ccc; /* Adjust the color as needed */ + content: ""; + position: absolute; + left: 0; + top: 10%; /* Adjust the top and bottom margins as needed */ + bottom: 10%; + width: 1px; + background-color: #ccc; /* Adjust the color as needed */ } .navbar-icon { - width: 20px; - height: 20px; - transform: translateY(-2px); -} \ No newline at end of file + width: 20px; + height: 20px; + transform: translateY(-2px); +} diff --git a/src/main/resources/static/css/pdfActions.css b/src/main/resources/static/css/pdfActions.css index 98c29dbe..455de860 100644 --- a/src/main/resources/static/css/pdfActions.css +++ b/src/main/resources/static/css/pdfActions.css @@ -1,87 +1,85 @@ - .pdf-actions_button-container { - z-index: 2; - display:flex; - opacity: 0; - transition: opacity 0.1s linear; + z-index: 2; + display: flex; + opacity: 0; + transition: opacity 0.1s linear; } .pdf-actions_container:hover .pdf-actions_button-container { - opacity: 1; + opacity: 1; } .pdf-actions_button-container > * { - padding: 0.25rem 0.5rem; - margin: 3px; - display: block; + padding: 0.25rem 0.5rem; + margin: 3px; + display: block; } .pdf-actions_container svg { - width: 16px; - height: 16px; + width: 16px; + height: 16px; } .pdf-actions_container:nth-child(1) .pdf-actions_move-left-button { - display: none; + display: none; } .pdf-actions_container:last-child .pdf-actions_move-right-button { - display: none; + display: none; } /* "insert pdf" buttons that appear on the right when hover */ .pdf-actions_insert-file-button-container { - translate: 0 -50%; - width: 80px; - height: 100%; + translate: 0 -50%; + width: 80px; + height: 100%; - z-index: 1; - opacity: 0; - transition: opacity 0.2s; + z-index: 1; + opacity: 0; + transition: opacity 0.2s; } .pdf-actions_insert-file-button-container.left { - left: -20px; + left: -20px; } .pdf-actions_insert-file-button-container.right { - right: -20px; + right: -20px; } -html[lang-direction=ltr] .pdf-actions_insert-file-button-container.right { - display:none; +html[dir="ltr"] .pdf-actions_insert-file-button-container.right { + display: none; } -html[lang-direction=rtl] .pdf-actions_insert-file-button-container.left { - display:none; +html[dir="rtl"] .pdf-actions_insert-file-button-container.left { + display: none; } .pdf-actions_insert-file-button-container.left .pdf-actions_insert-file-button { - left: 0; - translate: 0 -50%; + left: 0; + translate: 0 -50%; } .pdf-actions_insert-file-button-container.right .pdf-actions_insert-file-button { - right: 0; - translate: 0 -50%; + right: 0; + translate: 0 -50%; } -html[lang-direction=ltr] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.right { - display: block; +html[dir="ltr"] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.right { + display: block; } - -html[lang-direction=rtl] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.left { - display: block; +html[dir="rtl"] .pdf-actions_container:last-child > .pdf-actions_insert-file-button-container.left { + display: block; } .pdf-actions_insert-file-button-container:hover { - opacity: 1; - transition: opacity 0.05s; + opacity: 1; + transition: opacity 0.05s; } .pdf-actions_insert-file-button { - position: absolute; - top: 50%; - right: 50%; - translate: 50% -50%; - aspect-ratio: 1; - border-radius: 100px; -} \ No newline at end of file + position: absolute; + top: 50%; + right: 50%; + translate: 50% -50%; + aspect-ratio: 1; + border-radius: 100px; +} diff --git a/src/main/resources/static/css/pipeline.css b/src/main/resources/static/css/pipeline.css new file mode 100644 index 00000000..9468333b --- /dev/null +++ b/src/main/resources/static/css/pipeline.css @@ -0,0 +1,21 @@ +.btn-margin { + margin-right: 2px; +} + +.bordered-box { + border: 1px solid #ddd; + padding: 20px; + margin: 20px; + width: 70%; +} + +.center-element { + width: 80%; + text-align: center; + margin: auto; +} + +.element-margin { + margin: 10px 0; + /* Adjust this value to increase/decrease the margin as needed */ +} diff --git a/src/main/resources/static/css/rainbow-mode.css b/src/main/resources/static/css/rainbow-mode.css index 2c58adf5..921423a5 100644 --- a/src/main/resources/static/css/rainbow-mode.css +++ b/src/main/resources/static/css/rainbow-mode.css @@ -1,37 +1,113 @@ /* Rainbow Mode Styles */ body { - background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%); - color: #fff !important; - --body-background-color: 255, 255, 255; - --base-font-color: 33, 37, 41; + background: linear-gradient( + 90deg, + rgba(255, 0, 0, 1) 0%, + rgba(255, 154, 0, 1) 10%, + rgba(208, 222, 33, 1) 20%, + rgba(79, 220, 74, 1) 30%, + rgba(63, 218, 216, 1) 40%, + rgba(47, 201, 226, 1) 50%, + rgba(28, 127, 238, 1) 60%, + rgba(95, 21, 242, 1) 70%, + rgba(186, 12, 248, 1) 80%, + rgba(251, 7, 217, 1) 90%, + rgba(255, 0, 0, 1) 100% + ); + color: #fff !important; + --body-background-color: 255, 255, 255; + --base-font-color: 33, 37, 41; } .dark-card { - background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important; - color: white !important; + background: linear-gradient( + 90deg, + rgba(255, 0, 0, 1) 0%, + rgba(255, 154, 0, 1) 10%, + rgba(208, 222, 33, 1) 20%, + rgba(79, 220, 74, 1) 30%, + rgba(63, 218, 216, 1) 40%, + rgba(47, 201, 226, 1) 50%, + rgba(28, 127, 238, 1) 60%, + rgba(95, 21, 242, 1) 70%, + rgba(186, 12, 248, 1) 80%, + rgba(251, 7, 217, 1) 90%, + rgba(255, 0, 0, 1) 100% + ) !important; + color: white !important; } .jumbotron { - background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%); - color: #fff !important; + background: linear-gradient( + 90deg, + rgba(255, 0, 0, 1) 0%, + rgba(255, 154, 0, 1) 10%, + rgba(208, 222, 33, 1) 20%, + rgba(79, 220, 74, 1) 30%, + rgba(63, 218, 216, 1) 40%, + rgba(47, 201, 226, 1) 50%, + rgba(28, 127, 238, 1) 60%, + rgba(95, 21, 242, 1) 70%, + rgba(186, 12, 248, 1) 80%, + rgba(251, 7, 217, 1) 90%, + rgba(255, 0, 0, 1) 100% + ); + color: #fff !important; } .list-group { - background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important; - color: fff !important; + background: linear-gradient( + 90deg, + rgba(255, 0, 0, 1) 0%, + rgba(255, 154, 0, 1) 10%, + rgba(208, 222, 33, 1) 20%, + rgba(79, 220, 74, 1) 30%, + rgba(63, 218, 216, 1) 40%, + rgba(47, 201, 226, 1) 50%, + rgba(28, 127, 238, 1) 60%, + rgba(95, 21, 242, 1) 70%, + rgba(186, 12, 248, 1) 80%, + rgba(251, 7, 217, 1) 90%, + rgba(255, 0, 0, 1) 100% + ) !important; + color: fff !important; } .list-group-item { - background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important; - color: fff !important; + background: linear-gradient( + 90deg, + rgba(255, 0, 0, 1) 0%, + rgba(255, 154, 0, 1) 10%, + rgba(208, 222, 33, 1) 20%, + rgba(79, 220, 74, 1) 30%, + rgba(63, 218, 216, 1) 40%, + rgba(47, 201, 226, 1) 50%, + rgba(28, 127, 238, 1) 60%, + rgba(95, 21, 242, 1) 70%, + rgba(186, 12, 248, 1) 80%, + rgba(251, 7, 217, 1) 90%, + rgba(255, 0, 0, 1) 100% + ) !important; + color: fff !important; } #support-section { - background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(255,154,0,1) 10%, rgba(208,222,33,1) 20%, rgba(79,220,74,1) 30%, rgba(63,218,216,1) 40%, rgba(47,201,226,1) 50%, rgba(28,127,238,1) 60%, rgba(95,21,242,1) 70%, rgba(186,12,248,1) 80%, rgba(251,7,217,1) 90%, rgba(255,0,0,1) 100%) !important; + background: linear-gradient( + 90deg, + rgba(255, 0, 0, 1) 0%, + rgba(255, 154, 0, 1) 10%, + rgba(208, 222, 33, 1) 20%, + rgba(79, 220, 74, 1) 30%, + rgba(63, 218, 216, 1) 40%, + rgba(47, 201, 226, 1) 50%, + rgba(28, 127, 238, 1) 60%, + rgba(95, 21, 242, 1) 70%, + rgba(186, 12, 248, 1) 80%, + rgba(251, 7, 217, 1) 90%, + rgba(255, 0, 0, 1) 100% + ) !important; } - #pages-container-wrapper { --background-color: rgba(255, 255, 255, 0.046) !important; --scroll-bar-color: #4c4c4c !important; --scroll-bar-thumb: #d3d3d3 !important; --scroll-bar-thumb-hover: #ffffff !important; } - diff --git a/src/main/resources/static/css/rotate-pdf.css b/src/main/resources/static/css/rotate-pdf.css new file mode 100644 index 00000000..57a30024 --- /dev/null +++ b/src/main/resources/static/css/rotate-pdf.css @@ -0,0 +1,29 @@ +#pdf-preview { + margin: 0 auto; + display: block; + max-width: calc(100% - 30px); + max-height: calc(100% - 30px); + box-shadow: 0 0 4px rgba(100, 100, 100, 0.25); + transition: rotate 0.3s; + position: absolute; + top: 50%; + left: 50%; + translate: -50% -50%; +} + +#previewContainer { + aspect-ratio: 1; + width: 100%; + border: 1px solid rgba(0, 0, 0, 0.125); + border-radius: 0.25rem; + margin: 1rem 0; + padding: 15px; + display: block; + overflow: hidden; + position: relative; +} + +.buttonContainer { + display: flex; + justify-content: space-around; +} diff --git a/src/main/resources/static/css/sign.css b/src/main/resources/static/css/sign.css new file mode 100644 index 00000000..c6ae3374 --- /dev/null +++ b/src/main/resources/static/css/sign.css @@ -0,0 +1,39 @@ +select#font-select, +select#font-select option { + height: 60px; /* Adjust as needed */ + font-size: 30px; /* Adjust as needed */ +} + +.drawing-pad-container { + text-align: center; +} + +#drawing-pad-canvas { + background: rgba(125, 125, 125, 0.2); + width: 100%; + height: 300px; +} +#box-drag-container { + position: relative; + margin: 20px 0; +} +.draggable-buttons-box { + position: absolute; + top: 0; + padding: 10px; + width: 100%; + display: flex; + gap: 5px; +} +.draggable-buttons-box > button { + z-index: 10; + background-color: rgba(13, 110, 253, 0.1); +} +.draggable-canvas { + border: 1px solid red; + position: absolute; + touch-action: none; + user-select: none; + top: 0px; + left: 0; +} diff --git a/src/main/resources/static/css/split-pdf-by-sections.css b/src/main/resources/static/css/split-pdf-by-sections.css new file mode 100644 index 00000000..7520c10e --- /dev/null +++ b/src/main/resources/static/css/split-pdf-by-sections.css @@ -0,0 +1,10 @@ +.pdf-visual-aid { + width: 150px; /* Adjust as needed */ + height: 200px; /* Adjust as needed */ + border: 1px solid black; /* Represents the PDF page */ + position: relative; +} +.line { + position: absolute; + background-color: red; /* Line color */ +} diff --git a/src/main/resources/static/css/stamp.css b/src/main/resources/static/css/stamp.css new file mode 100644 index 00000000..9fed6178 --- /dev/null +++ b/src/main/resources/static/css/stamp.css @@ -0,0 +1,41 @@ +.a4container { + position: relative; + width: 50%; + aspect-ratio: 0.707; + border: 1px solid #ddd; + box-sizing: border-box; + background-color: white; +} + +.pageNumber { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + font-size: 1em; + color: #333; + cursor: pointer; + background-color: #ccc; + width: 15%; + height: 15%; + transform: translate(-50%, -50%); +} + +.pageNumber:hover { + background-color: #eee; +} + +#myForm { + display: flex; + justify-content: center; + align-items: center; + margin-top: 20px; +} + +.selectedPosition { + background-color: #0a0; +} + +.selectedPosition.selectedHovered { + background-color: #006600; +} diff --git a/src/main/resources/static/css/tab-container.css b/src/main/resources/static/css/tab-container.css index f6609de4..cf048650 100644 --- a/src/main/resources/static/css/tab-container.css +++ b/src/main/resources/static/css/tab-container.css @@ -1,26 +1,24 @@ - .tab-group { - } .tab-container { - display: none; + display: none; } .tab-container.active { - display: block; - border: 1px solid rgba(var(--base-font-color), 0.25); - padding: 15px; + display: block; + border: 1px solid rgba(var(--base-font-color), 0.25); + padding: 15px; } .tab-buttons > button { - margin-bottom: -1px; - background: 0 0; - border: 1px solid transparent; - color: rgb(var(--base-font-color)); + margin-bottom: -1px; + background: 0 0; + border: 1px solid transparent; + color: rgb(var(--base-font-color)); - border-top-left-radius: 0.25rem; - border-top-right-radius: 0.25rem; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; } .tab-buttons > button.active { - background-color: rgb(var(--body-background-color)); - border-color: rgba(var(--base-font-color), 0.25) rgba(var(--base-font-color), 0.25) rgb(var(--body-background-color)); -} \ No newline at end of file + background-color: rgb(var(--body-background-color)); + border-color: rgba(var(--base-font-color), 0.25) rgba(var(--base-font-color), 0.25) rgb(var(--body-background-color)); +} diff --git a/src/main/resources/static/images/flags/pt_pt.svg b/src/main/resources/static/images/flags/pt_pt.svg new file mode 100644 index 00000000..445cf7f5 --- /dev/null +++ b/src/main/resources/static/images/flags/pt_pt.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/static/images/flags/ua.svg b/src/main/resources/static/images/flags/ua.svg new file mode 100644 index 00000000..a339eb1b --- /dev/null +++ b/src/main/resources/static/images/flags/ua.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/main/resources/static/images/update.svg b/src/main/resources/static/images/update.svg new file mode 100644 index 00000000..3edc4c67 --- /dev/null +++ b/src/main/resources/static/images/update.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/resources/static/js/cacheFormInputs.js b/src/main/resources/static/js/cacheFormInputs.js new file mode 100644 index 00000000..e61b4449 --- /dev/null +++ b/src/main/resources/static/js/cacheFormInputs.js @@ -0,0 +1,82 @@ +document.addEventListener("DOMContentLoaded", function() { + + var cacheInputs = localStorage.getItem("cacheInputs") || "disabled"; + if (cacheInputs !== "enabled") { + return; // Stop execution if caching is not enabled + } + + // Function to generate a key based on the form's action attribute + function generateStorageKey(form) { + const action = form.getAttribute('action'); + if (!action || action.length < 3) { + return null; // Not a valid action, return null to skip processing + } + return 'formData_' + encodeURIComponent(action); + } + + // Function to save form data to localStorage + function saveFormData(form) { + const formKey = generateStorageKey(form); + if (!formKey) return; // Skip if no valid key + + const formData = {}; + const elements = form.elements; + for (let i = 0; i < elements.length; i++) { + const element = elements[i]; + // Skip elements without names, passwords, files, hidden fields, and submit/reset buttons + if (!element.name || + element.type === 'password' || + element.type === 'file' || + //element.type === 'hidden' || + element.type === 'submit' || + element.type === 'reset') { + continue; + } + // Handle checkboxes: store only if checked + if (element.type === 'checkbox') { + if (element.checked) { + formData[element.name] = element.value; + } else { + continue; // Skip unchecked boxes + } + } else { + // Skip saving empty values + if (element.value === "" || element.value == null) { + continue; + } + formData[element.name] = element.value; + } + } + localStorage.setItem(formKey, JSON.stringify(formData)); + } + + // Function to load form data from localStorage + function loadFormData(form) { + const formKey = generateStorageKey(form); + if (!formKey) return; // Skip if no valid key + + const savedData = localStorage.getItem(formKey); + if (savedData) { + const formData = JSON.parse(savedData); + for (const key in formData) { + if (formData.hasOwnProperty(key) && form.elements[key]) { + const element = form.elements[key]; + if (element.type === 'checkbox') { + element.checked = true; + } else { + element.value = formData[key]; + } + } + } + } + } + + // Attach event listeners and load data for all forms + const forms = document.querySelectorAll('form'); + forms.forEach(form => { + form.addEventListener('submit', function(event) { + saveFormData(form); + }); + loadFormData(form); + }); +}); diff --git a/src/main/resources/static/js/darkmode.js b/src/main/resources/static/js/darkmode.js index d3c62266..cb119a8a 100644 --- a/src/main/resources/static/js/darkmode.js +++ b/src/main/resources/static/js/darkmode.js @@ -1,5 +1,5 @@ -var toggleCount = 0 -var lastToggleTime = Date.now() +var toggleCount = 0; +var lastToggleTime = Date.now(); var elements = { lightModeStyles: null, @@ -11,18 +11,18 @@ var elements = { navbar: null, navIcons: null, navDropdownMenus: null, -} +}; function getElements() { - elements.lightModeStyles = document.getElementById("light-mode-styles") - elements.darkModeStyles = document.getElementById("dark-mode-styles") - elements.rainbowModeStyles = document.getElementById("rainbow-mode-styles") - elements.darkModeIcon = document.getElementById("dark-mode-icon") - elements.searchBar = document.getElementById("searchBar") - elements.formControls = document.querySelectorAll(".form-control") - elements.navbar = document.querySelectorAll("nav.navbar") - elements.navIcons = document.querySelectorAll("nav .icon, .navbar-icon") - elements.navDropdownMenus = document.querySelectorAll("nav .dropdown-menu") + elements.lightModeStyles = document.getElementById("light-mode-styles"); + elements.darkModeStyles = document.getElementById("dark-mode-styles"); + elements.rainbowModeStyles = document.getElementById("rainbow-mode-styles"); + elements.darkModeIcon = document.getElementById("dark-mode-icon"); + elements.searchBar = document.getElementById("searchBar"); + elements.formControls = document.querySelectorAll(".form-control"); + elements.navbar = document.querySelectorAll("nav.navbar"); + elements.navIcons = document.querySelectorAll("nav .icon, .navbar-icon"); + elements.navDropdownMenus = document.querySelectorAll(".dropdown-menu"); } function setMode(mode) { var event = new CustomEvent("modeChanged", { detail: mode }); @@ -48,22 +48,22 @@ function setMode(mode) { elements.searchBar.classList.add("dark-mode-search"); } if (elements && elements.formControls) { - elements.formControls.forEach(input => input.classList.add("bg-dark", "text-white")); + elements.formControls.forEach((input) => input.classList.add("bg-dark", "text-white")); } if (elements && elements.navbar) { - elements.navbar.forEach(navElement => { + elements.navbar.forEach((navElement) => { navElement.classList.remove("navbar-light", "bg-light"); navElement.classList.add("navbar-dark", "bg-dark"); }); } if (elements && elements.navDropdownMenus) { - elements.navDropdownMenus.forEach(menu => menu.classList.add("dropdown-menu-dark")); + elements.navDropdownMenus.forEach((menu) => menu.classList.add("dropdown-menu-dark")); } if (elements && elements.navIcons) { - elements.navIcons.forEach(icon => (icon.style.filter = "invert(1)")); + elements.navIcons.forEach((icon) => (icon.style.filter = "invert(1)")); } var tables = document.querySelectorAll(".table"); - tables.forEach(table => { + tables.forEach((table) => { table.classList.add("table-dark"); }); if (jumbotron) { @@ -78,22 +78,22 @@ function setMode(mode) { elements.searchBar.classList.remove("dark-mode-search"); } if (elements && elements.formControls) { - elements.formControls.forEach(input => input.classList.remove("bg-dark", "text-white")); + elements.formControls.forEach((input) => input.classList.remove("bg-dark", "text-white")); } if (elements && elements.navbar) { - elements.navbar.forEach(navElement => { + elements.navbar.forEach((navElement) => { navElement.classList.remove("navbar-dark", "bg-dark"); navElement.classList.add("navbar-light", "bg-light"); }); } if (elements && elements.navDropdownMenus) { - elements.navDropdownMenus.forEach(menu => menu.classList.remove("dropdown-menu-dark")); + elements.navDropdownMenus.forEach((menu) => menu.classList.remove("dropdown-menu-dark")); } if (elements && elements.navIcons) { - elements.navIcons.forEach(icon => (icon.style.filter = "none")); + elements.navIcons.forEach((icon) => (icon.style.filter = "none")); } var tables = document.querySelectorAll(".table-dark"); - tables.forEach(table => { + tables.forEach((table) => { table.classList.remove("table-dark"); }); if (jumbotron) { @@ -108,43 +108,43 @@ function setMode(mode) { } function toggleDarkMode() { - var currentTime = Date.now() + var currentTime = Date.now(); if (currentTime - lastToggleTime < 1000) { - toggleCount++ + toggleCount++; } else { - toggleCount = 1 + toggleCount = 1; } - lastToggleTime = currentTime + lastToggleTime = currentTime; if (toggleCount >= 18) { - localStorage.setItem("dark-mode", "rainbow") - setMode("rainbow") + localStorage.setItem("dark-mode", "rainbow"); + setMode("rainbow"); } else if (localStorage.getItem("dark-mode") == "on") { - localStorage.setItem("dark-mode", "off") - setMode("off") + localStorage.setItem("dark-mode", "off"); + setMode("off"); } else { - localStorage.setItem("dark-mode", "on") - setMode("on") + localStorage.setItem("dark-mode", "on"); + setMode("on"); } } document.addEventListener("DOMContentLoaded", function () { - getElements() + getElements(); - var currentMode = localStorage.getItem("dark-mode") + var currentMode = localStorage.getItem("dark-mode"); if (currentMode === "on" || currentMode === "off" || currentMode === "rainbow") { - setMode(currentMode) + setMode(currentMode); } else if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { - setMode("on") + setMode("on"); } else { - setMode("off") + setMode("off"); } var darkModeToggle = document.getElementById("dark-mode-toggle"); if (darkModeToggle !== null) { - darkModeToggle.addEventListener("click", function (event) { - event.preventDefault(); - toggleDarkMode(); - }); - } -}) + darkModeToggle.addEventListener("click", function (event) { + event.preventDefault(); + toggleDarkMode(); + }); + } +}); diff --git a/src/main/resources/static/js/downloader.js b/src/main/resources/static/js/downloader.js index 8971f98e..c955bb1b 100644 --- a/src/main/resources/static/js/downloader.js +++ b/src/main/resources/static/js/downloader.js @@ -1,242 +1,265 @@ function showErrorBanner(message, stackTrace) { - const errorContainer = document.getElementById("errorContainer"); - errorContainer.style.display = "block"; // Display the banner - document.querySelector("#errorContainer .alert-heading").textContent = "Error"; - document.querySelector("#errorContainer p").textContent = message; - document.querySelector("#traceContent").textContent = stackTrace; + const errorContainer = document.getElementById("errorContainer"); + errorContainer.style.display = "block"; // Display the banner + document.querySelector("#errorContainer .alert-heading").textContent = "Error"; + document.querySelector("#errorContainer p").textContent = message; + document.querySelector("#traceContent").textContent = stackTrace; } let firstErrorOccurred = false; -$(document).ready(function() { - $('form').submit(async function(event) { - event.preventDefault(); - firstErrorOccurred = false; - const url = this.action; - const files = $('#fileInput-input')[0].files; - const formData = new FormData(this); +$(document).ready(function () { + $("form").submit(async function (event) { + event.preventDefault(); + firstErrorOccurred = false; + const url = this.action; + const files = $("#fileInput-input")[0].files; + const formData = new FormData(this); - // Remove empty file entries - for (let [key, value] of formData.entries()) { - if (value instanceof File && !value.name) { - formData.delete(key); - } + // Remove empty file entries + for (let [key, value] of formData.entries()) { + if (value instanceof File && !value.name) { + formData.delete(key); + } + } + const override = $("#override").val() || ""; + const originalButtonText = $("#submitBtn").text(); + $("#submitBtn").text("Processing..."); + console.log(override); + + // Set a timeout to show the game button if operation takes more than 5 seconds + const timeoutId = setTimeout(() => { + var boredWaiting = localStorage.getItem("boredWaiting") || "disabled"; + const showGameBtn = document.getElementById("show-game-btn"); + if (boredWaiting === "enabled" && showGameBtn) { + showGameBtn.style.display = "block"; + } + }, 5000); + + try { + if (remoteCall === true) { + if (override === "multi" || (!multiple && files.length > 1 && override !== "single")) { + await submitMultiPdfForm(url, files); + } else { + await handleSingleDownload(url, formData); } - const override = $('#override').val() || ''; - const originalButtonText = $('#submitBtn').text(); - $('#submitBtn').text('Processing...'); - console.log(override); - try { - if(remoteCall === true) { - if (override === 'multi' || (!multiple && files.length > 1) && override !== 'single' ) { - await submitMultiPdfForm(url, files); - } else { - await handleSingleDownload(url, formData); - } - } - $('#submitBtn').text(originalButtonText); - } catch (error) { - handleDownloadError(error); - $('#submitBtn').text(originalButtonText); - console.error(error); - } - }); + } + clearTimeout(timeoutId); + $("#submitBtn").text(originalButtonText); + + // After process finishes, check for boredWaiting and gameDialog open status + const boredWaiting = localStorage.getItem("boredWaiting") || "disabled"; + const gameDialog = document.getElementById('game-container-wrapper'); + if (boredWaiting === "enabled" && gameDialog && gameDialog.open) { + // Display a green banner at the bottom of the screen saying "Download complete" + let downloadCompleteText = "Download Complete"; + if(window.downloadCompleteText){ + downloadCompleteText = window.downloadCompleteText; + } + $("body").append('
'+ downloadCompleteText + '
'); + setTimeout(function() { + $("#download-complete-banner").fadeOut("slow", function() { + $(this).remove(); // Remove the banner after fading out + }); + }, 5000); // Banner will fade out after 5 seconds + } + + } catch (error) { + clearTimeout(timeoutId); + handleDownloadError(error); + $("#submitBtn").text(originalButtonText); + console.error(error); + } + }); }); +async function handleSingleDownload(url, formData, isMulti = false, isZip = false) { + try { + const response = await fetch(url, { method: "POST", body: formData }); + const contentType = response.headers.get("content-type"); + if (!response.ok) { + if (contentType && contentType.includes("application/json")) { + console.error("Throwing error banner, response was not okay"); + return handleJsonResponse(response); + } + throw new Error(`HTTP error! status: ${response.status}`); + } -async function handleSingleDownload(url, formData, isMulti = false , isZip = false) { - try { - const response = await fetch(url, { method: 'POST', body: formData }); - const contentType = response.headers.get('content-type'); + const contentDisposition = response.headers.get("Content-Disposition"); + let filename = getFilenameFromContentDisposition(contentDisposition); - if (!response.ok) { - if (contentType && contentType.includes('application/json')) { - return handleJsonResponse(response); - console.error('Throwing error banner, response was not okay'); - } - throw new Error(`HTTP error! status: ${response.status}`); - } - - const contentDisposition = response.headers.get('Content-Disposition'); - let filename = getFilenameFromContentDisposition(contentDisposition); - - const blob = await response.blob(); - if (contentType.includes('application/pdf') || contentType.includes('image/')) { - return handleResponse(blob, filename, !isMulti, isZip); - } else { - return handleResponse(blob, filename, false, isZip); - } - } catch (error) { - console.error('Error in handleSingleDownload:', error); - throw error; // Re-throw the error if you want it to be handled higher up. - } + const blob = await response.blob(); + if (contentType.includes("application/pdf") || contentType.includes("image/")) { + return handleResponse(blob, filename, !isMulti, isZip); + } else { + return handleResponse(blob, filename, false, isZip); + } + } catch (error) { + console.error("Error in handleSingleDownload:", error); + throw error; // Re-throw the error if you want it to be handled higher up. + } } function getFilenameFromContentDisposition(contentDisposition) { - let filename; + let filename; - if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) { - filename = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, '')).trim(); - } else { - // If the Content-Disposition header is not present or does not contain the filename, use a default filename - filename = 'download'; - } + if (contentDisposition && contentDisposition.indexOf("attachment") !== -1) { + filename = decodeURIComponent(contentDisposition.split("filename=")[1].replace(/"/g, "")).trim(); + } else { + // If the Content-Disposition header is not present or does not contain the filename, use a default filename + filename = "download"; + } - return filename; + return filename; } - - async function handleJsonResponse(response) { - const json = await response.json(); - const errorMessage = JSON.stringify(json, null, 2); - if (errorMessage.toLowerCase().includes('the password is incorrect') || errorMessage.toLowerCase().includes('Password is not provided') || errorMessage.toLowerCase().includes('PDF contains an encryption dictionary')) { - if (!firstErrorOccurred) { - firstErrorOccurred = true; - alert(pdfPasswordPrompt); - } - } else { - showErrorBanner(json.error + ':' + json.message, json.trace); - } + const json = await response.json(); + const errorMessage = JSON.stringify(json, null, 2); + if ( + errorMessage.toLowerCase().includes("the password is incorrect") || + errorMessage.toLowerCase().includes("Password is not provided") || + errorMessage.toLowerCase().includes("PDF contains an encryption dictionary") + ) { + if (!firstErrorOccurred) { + firstErrorOccurred = true; + alert(pdfPasswordPrompt); + } + } else { + showErrorBanner(json.error + ":" + json.message, json.trace); + } } - async function handleResponse(blob, filename, considerViewOptions = false, isZip = false) { - if (!blob) return; - const downloadOption = localStorage.getItem('downloadOption'); - if (considerViewOptions) { - if (downloadOption === 'sameWindow') { - const url = URL.createObjectURL(blob); - window.location.href = url; - return; - } else if (downloadOption === 'newWindow') { - const url = URL.createObjectURL(blob); - window.open(url, '_blank'); - return; - } - } - if(!isZip){ - downloadFile(blob, filename); - } - return { filename, blob }; + if (!blob) return; + const downloadOption = localStorage.getItem("downloadOption"); + if (considerViewOptions) { + if (downloadOption === "sameWindow") { + const url = URL.createObjectURL(blob); + window.location.href = url; + return; + } else if (downloadOption === "newWindow") { + const url = URL.createObjectURL(blob); + window.open(url, "_blank"); + return; + } + } + if (!isZip) { + downloadFile(blob, filename); + } + return { filename, blob }; } function handleDownloadError(error) { - const errorMessage = error.message; - showErrorBanner(errorMessage); + const errorMessage = error.message; + showErrorBanner(errorMessage); } let urls = []; // An array to hold all the URLs function downloadFile(blob, filename) { - if (!(blob instanceof Blob)) { - console.error('Invalid blob passed to downloadFile function'); - return; - } - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = filename; - a.click(); - urls.push(url); // Store the URL so it doesn't get garbage collected too soon + if (!(blob instanceof Blob)) { + console.error("Invalid blob passed to downloadFile function"); + return; + } + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + a.click(); + urls.push(url); // Store the URL so it doesn't get garbage collected too soon - return { filename, blob }; + return { filename, blob }; } - - async function submitMultiPdfForm(url, files) { - const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4; - const zipFiles = files.length > zipThreshold; - let jszip = null; - // Show the progress bar - $('#progressBarContainer').show(); - // Initialize the progress bar + const zipThreshold = parseInt(localStorage.getItem("zipThreshold"), 10) || 4; + const zipFiles = files.length > zipThreshold; + let jszip = null; + // Show the progress bar + $(".progressBarContainer").show(); + // Initialize the progress bar - let progressBar = $('#progressBar'); - progressBar.css('width', '0%'); - progressBar.attr('aria-valuenow', 0); - progressBar.attr('aria-valuemax', files.length); + let progressBar = $(".progressBar"); + progressBar.css("width", "0%"); + progressBar.attr("aria-valuenow", 0); + progressBar.attr("aria-valuemax", files.length); - if (zipFiles) { - jszip = new JSZip(); - } + if (zipFiles) { + jszip = new JSZip(); + } + // Get the form with the method attribute set to POST + let postForm = document.querySelector('form[method="POST"]'); - // Get the form with the method attribute set to POST - let postForm = document.querySelector('form[method="POST"]'); - - // Get existing form data - let formData; - if (postForm) { - formData = new FormData($(postForm)[0]); // Convert the form to a jQuery object and get the raw DOM element - } else { - console.log("No form with POST method found."); - } - //Remove file to reuse parameters for other runs - formData.delete('fileInput'); - // Remove empty file entries - for (let [key, value] of formData.entries()) { - if (value instanceof File && !value.name) { - formData.delete(key); - } + // Get existing form data + let formData; + if (postForm) { + formData = new FormData($(postForm)[0]); // Convert the form to a jQuery object and get the raw DOM element + } else { + console.log("No form with POST method found."); + } + //Remove file to reuse parameters for other runs + formData.delete("fileInput"); + // Remove empty file entries + for (let [key, value] of formData.entries()) { + if (value instanceof File && !value.name) { + formData.delete(key); } - const CONCURRENCY_LIMIT = 8; - const chunks = []; - for (let i = 0; i < Array.from(files).length; i += CONCURRENCY_LIMIT) { - chunks.push(Array.from(files).slice(i, i + CONCURRENCY_LIMIT)); - } + } + const CONCURRENCY_LIMIT = 8; + const chunks = []; + for (let i = 0; i < Array.from(files).length; i += CONCURRENCY_LIMIT) { + chunks.push(Array.from(files).slice(i, i + CONCURRENCY_LIMIT)); + } - for (const chunk of chunks) { - const promises = chunk.map(async file => { - let fileFormData = new FormData(); - fileFormData.append('fileInput', file); - console.log(fileFormData); - // Add other form data - for (let pair of formData.entries()) { - fileFormData.append(pair[0], pair[1]); - console.log(pair[0]+ ', ' + pair[1]); - } + for (const chunk of chunks) { + const promises = chunk.map(async (file) => { + let fileFormData = new FormData(); + fileFormData.append("fileInput", file); + console.log(fileFormData); + // Add other form data + for (let pair of formData.entries()) { + fileFormData.append(pair[0], pair[1]); + console.log(pair[0] + ", " + pair[1]); + } - try { - const downloadDetails = await handleSingleDownload(url, fileFormData, true, zipFiles); - console.log(downloadDetails); - if (zipFiles) { - jszip.file(downloadDetails.filename, downloadDetails.blob); - } else { - //downloadFile(downloadDetails.blob, downloadDetails.filename); - } - updateProgressBar(progressBar, Array.from(files).length); - } catch (error) { - handleDownloadError(error); - console.error(error); - } - }); - await Promise.all(promises); + try { + const downloadDetails = await handleSingleDownload(url, fileFormData, true, zipFiles); + console.log(downloadDetails); + if (zipFiles) { + jszip.file(downloadDetails.filename, downloadDetails.blob); + } else { + //downloadFile(downloadDetails.blob, downloadDetails.filename); + } + updateProgressBar(progressBar, Array.from(files).length); + } catch (error) { + handleDownloadError(error); + console.error(error); + } + }); + await Promise.all(promises); + } - } - - if (zipFiles) { - try { - const content = await jszip.generateAsync({ type: "blob" }); - downloadFile(content, "files.zip"); - } catch (error) { - console.error('Error generating ZIP file: ' + error); - } - } - progressBar.css('width', '100%'); - progressBar.attr('aria-valuenow', Array.from(files).length); + if (zipFiles) { + try { + const content = await jszip.generateAsync({ type: "blob" }); + downloadFile(content, "files.zip"); + } catch (error) { + console.error("Error generating ZIP file: " + error); + } + } + progressBar.css("width", "100%"); + progressBar.attr("aria-valuenow", Array.from(files).length); } - - function updateProgressBar(progressBar, files) { - let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length); - progressBar.css('width', progress + '%'); - progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1); + let progress = (progressBar.attr("aria-valuenow") / files.length) * 100 + 100 / files.length; + progressBar.css("width", progress + "%"); + progressBar.attr("aria-valuenow", parseInt(progressBar.attr("aria-valuenow")) + 1); } -window.addEventListener('unload', () => { - for (const url of urls) { - URL.revokeObjectURL(url); - } +window.addEventListener("unload", () => { + for (const url of urls) { + URL.revokeObjectURL(url); + } }); diff --git a/src/main/resources/static/js/draggable-utils.js b/src/main/resources/static/js/draggable-utils.js index 2263e540..6064e398 100644 --- a/src/main/resources/static/js/draggable-utils.js +++ b/src/main/resources/static/js/draggable-utils.js @@ -1,282 +1,357 @@ const DraggableUtils = { + boxDragContainer: document.getElementById("box-drag-container"), + pdfCanvas: document.getElementById("pdf-canvas"), + nextId: 0, + pdfDoc: null, + pageIndex: 0, + documentsMap: new Map(), + lastInteracted: null, - boxDragContainer: document.getElementById('box-drag-container'), - pdfCanvas: document.getElementById('pdf-canvas'), - nextId: 0, - pdfDoc: null, - pageIndex: 0, - documentsMap: new Map(), - - init() { - interact('.draggable-canvas') - .draggable({ - listeners: { - move: (event) => { - 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; - - target.style.transform = `translate(${x}px, ${y}px)`; - target.setAttribute('data-bs-x', x); - target.setAttribute('data-bs-y', y); - - this.onInteraction(target); - }, - }, - }) - .resizable({ - edges: { left: true, right: true, bottom: true, top: true }, - listeners: { + init() { + interact(".draggable-canvas") + .draggable({ + listeners: { move: (event) => { - var target = event.target - var x = (parseFloat(target.getAttribute('data-bs-x')) || 0) - var y = (parseFloat(target.getAttribute('data-bs-y')) || 0) + 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; - // check if control key is pressed - if (event.ctrlKey) { - const aspectRatio = target.offsetWidth / target.offsetHeight; - // preserve aspect ratio - let width = event.rect.width; - let height = event.rect.height; + target.style.transform = `translate(${x}px, ${y}px)`; + target.setAttribute("data-bs-x", x); + target.setAttribute("data-bs-y", y); - if (Math.abs(event.deltaRect.width) >= Math.abs(event.deltaRect.height)) { - height = width / aspectRatio; - } else { - width = height * aspectRatio; - } - - event.rect.width = width; - event.rect.height = height; - } - - target.style.width = event.rect.width + 'px' - target.style.height = event.rect.height + 'px' - - // translate when resizing from top or left edges - x += event.deltaRect.left - y += event.deltaRect.top - - target.style.transform = 'translate(' + x + 'px,' + y + 'px)' - - target.setAttribute('data-bs-x', x) - target.setAttribute('data-bs-y', y) - target.textContent = Math.round(event.rect.width) + '\u00D7' + Math.round(event.rect.height) - - this.onInteraction(target); + this.onInteraction(target); + //update the last interacted element + this.lastInteracted = event.target; }, - }, + }, + }) + .resizable({ + edges: { left: true, right: true, bottom: true, top: true }, + listeners: { + move: (event) => { + var target = event.target; + var x = parseFloat(target.getAttribute("data-bs-x")) || 0; + var y = parseFloat(target.getAttribute("data-bs-y")) || 0; - modifiers: [ - interact.modifiers.restrictSize({ - min: { width: 5, height: 5 }, - }), - ], - inertia: true, - }); - }, - onInteraction(target) { - this.boxDragContainer.appendChild(target); - }, + // check if control key is pressed + if (event.ctrlKey) { + const aspectRatio = target.offsetWidth / target.offsetHeight; + // preserve aspect ratio + let width = event.rect.width; + let height = event.rect.height; - 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); - - createdCanvas.onclick = e => this.onInteraction(e.target); - - this.boxDragContainer.appendChild(createdCanvas); - return createdCanvas; - }, - createDraggableCanvasFromUrl(dataUrl) { - return new Promise((resolve) => { - var myImage = new Image(); - myImage.src = dataUrl; - myImage.onload = () => { - var createdCanvas = this.createDraggableCanvas(); - - createdCanvas.width = myImage.width; - createdCanvas.height = myImage.height; - - const imgAspect = myImage.width / myImage.height; - const pdfAspect = this.boxDragContainer.offsetWidth / this.boxDragContainer.offsetHeight; - - var scaleMultiplier; - if (imgAspect > pdfAspect) { - scaleMultiplier = this.boxDragContainer.offsetWidth / myImage.width; - } else { - scaleMultiplier = this.boxDragContainer.offsetHeight / myImage.height; - } - - var newWidth = createdCanvas.width; - var newHeight = createdCanvas.height; - if (scaleMultiplier < 1) { - newWidth = newWidth * scaleMultiplier; - newHeight = newHeight * scaleMultiplier; - } - - createdCanvas.style.width = newWidth+"px"; - createdCanvas.style.height = newHeight+"px"; - - var myContext = createdCanvas.getContext("2d"); - myContext.drawImage(myImage,0,0); - resolve(createdCanvas); + if (Math.abs(event.deltaRect.width) >= Math.abs( + event.deltaRect.height)) { + height = width / aspectRatio; + } else { + width = height * aspectRatio; } - }) - }, - deleteAllDraggableCanvases() { - this.boxDragContainer.querySelectorAll(".draggable-canvas").forEach(el => el.remove()); - }, - deleteDraggableCanvas(element) { - if (element) { - element.remove(); - } - }, - getLastInteracted() { - return this.boxDragContainer.querySelector(".draggable-canvas:last-of-type"); - }, - storePageContents() { - var pagesMap = this.documentsMap.get(this.pdfDoc); - if (!pagesMap) { - pagesMap = {}; + event.rect.width = width; + event.rect.height = height; + } + + target.style.width = event.rect.width + "px"; + target.style.height = event.rect.height + "px"; + + // translate when resizing from top or left edges + x += event.deltaRect.left; + y += event.deltaRect.top; + + target.style.transform = "translate(" + x + "px," + y + "px)"; + + target.setAttribute("data-bs-x", x); + target.setAttribute("data-bs-y", y); + target.textContent = Math.round(event.rect.width) + "\u00D7" + + Math.round(event.rect.height); + + this.onInteraction(target); + }, + }, + + modifiers: [ + interact.modifiers.restrictSize({ + min: {width: 5, height: 5}, + }), + ], + inertia: true, + }); + //Arrow key Support for Add-Image and Sign pages + if(window.location.pathname.endsWith('sign') || window.location.pathname.endsWith('add-image')) { + window.addEventListener('keydown', (event) => { + //Check for last interacted element + if (!this.lastInteracted){ + return; + } + // Get the currently selected element + const target = this.lastInteracted; + + // Step size relatively to the elements size + const stepX = target.offsetWidth * 0.05; + const stepY = target.offsetHeight * 0.05; + + // Get the current x and y coordinates + let x = (parseFloat(target.getAttribute('data-bs-x')) || 0); + let y = (parseFloat(target.getAttribute('data-bs-y')) || 0); + + // Check which key was pressed and update the coordinates accordingly + switch (event.key) { + case 'ArrowUp': + y -= stepY; + event.preventDefault(); // Prevent the default action + break; + case 'ArrowDown': + y += stepY; + event.preventDefault(); + break; + case 'ArrowLeft': + x -= stepX; + event.preventDefault(); + break; + case 'ArrowRight': + x += stepX; + event.preventDefault(); + break; + default: + return; // Listen only to arrow keys } - const elements = [...this.boxDragContainer.querySelectorAll(".draggable-canvas")]; - const draggablesData = elements.map(el => {return{element:el, offsetWidth:el.offsetWidth, offsetHeight:el.offsetHeight}}); - elements.forEach(el => this.boxDragContainer.removeChild(el)); + // Update position + target.style.transform = `translate(${x}px, ${y}px)`; + target.setAttribute('data-bs-x', x); + target.setAttribute('data-bs-y', y); - pagesMap[this.pageIndex] = draggablesData; - pagesMap[this.pageIndex+"-offsetWidth"] = this.pdfCanvas.offsetWidth; - pagesMap[this.pageIndex+"-offsetHeight"] = this.pdfCanvas.offsetHeight; + DraggableUtils.onInteraction(target); + }); + } + }, - this.documentsMap.set(this.pdfDoc, pagesMap); - }, - loadPageContents() { - var pagesMap = this.documentsMap.get(this.pdfDoc); - this.deleteAllDraggableCanvases(); - if (!pagesMap) { - return; - } + onInteraction(target) { + this.boxDragContainer.appendChild(target); + }, - const draggablesData = pagesMap[this.pageIndex]; - if (draggablesData) { - draggablesData.forEach(draggableData => this.boxDragContainer.appendChild(draggableData.element)); - } + createDraggableCanvas() { + const createdCanvas = document.createElement("canvas"); + createdCanvas.id = `draggable-canvas-${this.nextId++}`; + createdCanvas.classList.add("draggable-canvas"); - this.documentsMap.set(this.pdfDoc, pagesMap); - }, + 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); - async renderPage(pdfDocument, pageIdx) { - this.pdfDoc = pdfDocument ? pdfDocument : this.pdfDoc; - this.pageIndex = pageIdx; + //Click element in order to enable arrow keys + createdCanvas.addEventListener('click', () => { + this.lastInteracted = createdCanvas; + }); - // persist - const page = await this.pdfDoc.getPage(this.pageIndex+1); + createdCanvas.onclick = (e) => this.onInteraction(e.target); - // set the canvas size to the size of the page - if (page.rotate == 90 || page.rotate == 270) { - this.pdfCanvas.width = page.view[3]; - this.pdfCanvas.height = page.view[2]; + this.boxDragContainer.appendChild(createdCanvas); + + //Enable Arrow keys directly after the element is created + this.lastInteracted = createdCanvas; + + return createdCanvas; + }, + createDraggableCanvasFromUrl(dataUrl) { + return new Promise((resolve) => { + var myImage = new Image(); + myImage.src = dataUrl; + myImage.onload = () => { + var createdCanvas = this.createDraggableCanvas(); + + createdCanvas.width = myImage.width; + createdCanvas.height = myImage.height; + + const imgAspect = myImage.width / myImage.height; + const pdfAspect = this.boxDragContainer.offsetWidth / this.boxDragContainer.offsetHeight; + + var scaleMultiplier; + if (imgAspect > pdfAspect) { + scaleMultiplier = this.boxDragContainer.offsetWidth / myImage.width; } else { - this.pdfCanvas.width = page.view[2]; - this.pdfCanvas.height = page.view[3]; + scaleMultiplier = this.boxDragContainer.offsetHeight / myImage.height; } - // render the page onto the canvas - var renderContext = { - canvasContext: this.pdfCanvas.getContext("2d"), - viewport: page.getViewport({ scale: 1 }) + var newWidth = createdCanvas.width; + var newHeight = createdCanvas.height; + if (scaleMultiplier < 1) { + newWidth = newWidth * scaleMultiplier; + newHeight = newHeight * scaleMultiplier; + } + + createdCanvas.style.width = newWidth + "px"; + createdCanvas.style.height = newHeight + "px"; + + var myContext = createdCanvas.getContext("2d"); + myContext.drawImage(myImage, 0, 0); + resolve(createdCanvas); + }; + }); + }, + deleteAllDraggableCanvases() { + this.boxDragContainer.querySelectorAll(".draggable-canvas").forEach((el) => el.remove()); + }, + deleteDraggableCanvas(element) { + if (element) { + //Check if deleted element is the last interacted + if (this.lastInteracted === element) { + // If it is, set lastInteracted to null + this.lastInteracted = null; + } + element.remove(); + } + }, + getLastInteracted() { + return this.boxDragContainer.querySelector(".draggable-canvas:last-of-type"); + }, + + storePageContents() { + var pagesMap = this.documentsMap.get(this.pdfDoc); + if (!pagesMap) { + pagesMap = {}; + } + + const elements = [...this.boxDragContainer.querySelectorAll(".draggable-canvas")]; + const draggablesData = elements.map((el) => { + return { + element: el, + offsetWidth: el.offsetWidth, + offsetHeight: el.offsetHeight, + }; + }); + elements.forEach((el) => this.boxDragContainer.removeChild(el)); + + pagesMap[this.pageIndex] = draggablesData; + pagesMap[this.pageIndex + "-offsetWidth"] = this.pdfCanvas.offsetWidth; + pagesMap[this.pageIndex + "-offsetHeight"] = this.pdfCanvas.offsetHeight; + + this.documentsMap.set(this.pdfDoc, pagesMap); + }, + loadPageContents() { + var pagesMap = this.documentsMap.get(this.pdfDoc); + this.deleteAllDraggableCanvases(); + if (!pagesMap) { + return; + } + + const draggablesData = pagesMap[this.pageIndex]; + if (draggablesData) { + draggablesData.forEach((draggableData) => this.boxDragContainer.appendChild(draggableData.element)); + } + + this.documentsMap.set(this.pdfDoc, pagesMap); + }, + + async renderPage(pdfDocument, pageIdx) { + this.pdfDoc = pdfDocument ? pdfDocument : this.pdfDoc; + this.pageIndex = pageIdx; + + // persist + const page = await this.pdfDoc.getPage(this.pageIndex + 1); + + // set the canvas size to the size of the page + if (page.rotate == 90 || page.rotate == 270) { + this.pdfCanvas.width = page.view[3]; + this.pdfCanvas.height = page.view[2]; + } else { + this.pdfCanvas.width = page.view[2]; + this.pdfCanvas.height = page.view[3]; + } + + // render the page onto the canvas + var renderContext = { + canvasContext: this.pdfCanvas.getContext("2d"), + viewport: page.getViewport({ scale: 1 }), + }; + await page.render(renderContext).promise; + + //return pdfCanvas.toDataURL(); + }, + async incrementPage() { + if (this.pageIndex < this.pdfDoc.numPages - 1) { + this.storePageContents(); + await this.renderPage(this.pdfDoc, this.pageIndex + 1); + this.loadPageContents(); + } + }, + async decrementPage() { + if (this.pageIndex > 0) { + this.storePageContents(); + await this.renderPage(this.pdfDoc, this.pageIndex - 1); + this.loadPageContents(); + } + }, + + parseTransform(element) {}, + async getOverlayedPdfDocument() { + const pdfBytes = await this.pdfDoc.getData(); + const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, { + ignoreEncryption: true, + }); + this.storePageContents(); + + const pagesMap = this.documentsMap.get(this.pdfDoc); + for (let pageIdx in pagesMap) { + if (pageIdx.includes("offset")) { + continue; + } + console.log(typeof pageIdx); + + const page = pdfDocModified.getPage(parseInt(pageIdx)); + const draggablesData = pagesMap[pageIdx]; + const offsetWidth = pagesMap[pageIdx + "-offsetWidth"]; + const offsetHeight = pagesMap[pageIdx + "-offsetHeight"]; + + for (const draggableData of draggablesData) { + // embed the draggable canvas + const draggableElement = draggableData.element; + const response = await fetch(draggableElement.toDataURL()); + const draggableImgBytes = await response.arrayBuffer(); + const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes); + + // calculate the position in the pdf document + const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, ""); + const transformComponents = tansform.split(","); + const draggablePositionPixels = { + x: parseFloat(transformComponents[0]), + y: parseFloat(transformComponents[1]), + width: draggableData.offsetWidth, + height: draggableData.offsetHeight, + }; + const draggablePositionRelative = { + x: draggablePositionPixels.x / offsetWidth, + y: draggablePositionPixels.y / offsetHeight, + width: draggablePositionPixels.width / offsetWidth, + height: draggablePositionPixels.height / offsetHeight, + }; + const draggablePositionPdf = { + x: draggablePositionRelative.x * page.getWidth(), + y: draggablePositionRelative.y * page.getHeight(), + width: draggablePositionRelative.width * page.getWidth(), + height: draggablePositionRelative.height * page.getHeight(), }; - await page.render(renderContext).promise; - //return pdfCanvas.toDataURL(); - }, - async incrementPage() { - if (this.pageIndex < this.pdfDoc.numPages-1) { - this.storePageContents(); - await this.renderPage(this.pdfDoc, this.pageIndex+1); - this.loadPageContents(); - } - }, - async decrementPage() { - if (this.pageIndex > 0) { - this.storePageContents(); - await this.renderPage(this.pdfDoc, this.pageIndex-1); - this.loadPageContents(); - } - }, + // draw the image + page.drawImage(pdfImageObject, { + x: draggablePositionPdf.x, + y: page.getHeight() - draggablePositionPdf.y - draggablePositionPdf.height, + width: draggablePositionPdf.width, + height: draggablePositionPdf.height, + }); + } + } - parseTransform(element) { - - }, - async getOverlayedPdfDocument() { - const pdfBytes = await this.pdfDoc.getData(); - const pdfDocModified = await PDFLib.PDFDocument.load(pdfBytes, { ignoreEncryption: true }); - this.storePageContents(); - - const pagesMap = this.documentsMap.get(this.pdfDoc); - for (let pageIdx in pagesMap) { - if (pageIdx.includes("offset")) { - continue; - } - console.log(typeof pageIdx); - - const page = pdfDocModified.getPage(parseInt(pageIdx)); - const draggablesData = pagesMap[pageIdx]; - const offsetWidth = pagesMap[pageIdx+"-offsetWidth"]; - const offsetHeight = pagesMap[pageIdx+"-offsetHeight"]; - - for (const draggableData of draggablesData) { - // embed the draggable canvas - const draggableElement = draggableData.element; - const response = await fetch(draggableElement.toDataURL()); - const draggableImgBytes = await response.arrayBuffer(); - const pdfImageObject = await pdfDocModified.embedPng(draggableImgBytes); - - // calculate the position in the pdf document - const tansform = draggableElement.style.transform.replace(/[^.,-\d]/g, ''); - const transformComponents = tansform.split(","); - const draggablePositionPixels = { - x: parseFloat(transformComponents[0]), - y: parseFloat(transformComponents[1]), - width: draggableData.offsetWidth, - height: draggableData.offsetHeight, - }; - const draggablePositionRelative = { - x: draggablePositionPixels.x / offsetWidth, - y: draggablePositionPixels.y / offsetHeight, - width: draggablePositionPixels.width / offsetWidth, - height: draggablePositionPixels.height / offsetHeight, - } - const draggablePositionPdf = { - x: draggablePositionRelative.x * page.getWidth(), - y: draggablePositionRelative.y * page.getHeight(), - width: draggablePositionRelative.width * page.getWidth(), - height: draggablePositionRelative.height * page.getHeight(), - } - - // draw the image - page.drawImage(pdfImageObject, { - x: draggablePositionPdf.x, - y: page.getHeight() - draggablePositionPdf.y - draggablePositionPdf.height, - width: draggablePositionPdf.width, - height: draggablePositionPdf.height, - }); - } - } - - this.loadPageContents(); - return pdfDocModified; - }, -} + this.loadPageContents(); + return pdfDocModified; + }, +}; document.addEventListener("DOMContentLoaded", () => { - DraggableUtils.init(); + DraggableUtils.init(); }); diff --git a/src/main/resources/static/js/errorBanner.js b/src/main/resources/static/js/errorBanner.js index 9d151407..727a854f 100644 --- a/src/main/resources/static/js/errorBanner.js +++ b/src/main/resources/static/js/errorBanner.js @@ -1,50 +1,50 @@ var traceVisible = false; function toggletrace() { - var traceDiv = document.getElementById("trace"); - if (!traceVisible) { - traceDiv.style.maxHeight = "500px"; - traceVisible = true; - } else { - traceDiv.style.maxHeight = "0px"; - traceVisible = false; - } - adjustContainerHeight(); + var traceDiv = document.getElementById("trace"); + if (!traceVisible) { + traceDiv.style.maxHeight = "500px"; + traceVisible = true; + } else { + traceDiv.style.maxHeight = "0px"; + traceVisible = false; + } + adjustContainerHeight(); } function copytrace() { - var flip = false - if (!traceVisible) { - toggletrace() - flip = true - } - var traceContent = document.getElementById("traceContent"); - var range = document.createRange(); - range.selectNode(traceContent); - window.getSelection().removeAllRanges(); - window.getSelection().addRange(range); - document.execCommand("copy"); - window.getSelection().removeAllRanges(); - if (flip) { - toggletrace() - } + var flip = false; + if (!traceVisible) { + toggletrace(); + flip = true; + } + var traceContent = document.getElementById("traceContent"); + var range = document.createRange(); + range.selectNode(traceContent); + window.getSelection().removeAllRanges(); + window.getSelection().addRange(range); + document.execCommand("copy"); + window.getSelection().removeAllRanges(); + if (flip) { + toggletrace(); + } } function dismissError() { - var errorContainer = document.getElementById("errorContainer"); - errorContainer.style.display = "none"; - errorContainer.style.height = "0"; + var errorContainer = document.getElementById("errorContainer"); + errorContainer.style.display = "none"; + errorContainer.style.height = "0"; } function adjustContainerHeight() { - var errorContainer = document.getElementById("errorContainer"); - var traceDiv = document.getElementById("trace"); - if (traceVisible) { - errorContainer.style.height = errorContainer.scrollHeight - traceDiv.scrollHeight + traceDiv.offsetHeight + "px"; - } else { - errorContainer.style.height = "auto"; - } + var errorContainer = document.getElementById("errorContainer"); + var traceDiv = document.getElementById("trace"); + if (traceVisible) { + errorContainer.style.height = errorContainer.scrollHeight - traceDiv.scrollHeight + traceDiv.offsetHeight + "px"; + } else { + errorContainer.style.height = "auto"; + } } function showHelp() { - $('#helpModal').modal('show'); -} \ No newline at end of file + $("#helpModal").modal("show"); +} diff --git a/src/main/resources/static/js/favourites.js b/src/main/resources/static/js/favourites.js index 08c6f183..dbecd013 100644 --- a/src/main/resources/static/js/favourites.js +++ b/src/main/resources/static/js/favourites.js @@ -1,45 +1,45 @@ function updateFavoritesDropdown() { - var dropdown = document.querySelector('#favoritesDropdown'); + var dropdown = document.querySelector("#favoritesDropdown"); - // Check if dropdown exists - if (!dropdown) { - console.error('Dropdown element with ID "favoritesDropdown" not found!'); - return; // Exit the function + // Check if dropdown exists + if (!dropdown) { + console.error('Dropdown element with ID "favoritesDropdown" not found!'); + return; // Exit the function + } + dropdown.innerHTML = ""; // Clear the current favorites + + var hasFavorites = false; + + for (var i = 0; i < localStorage.length; i++) { + var key = localStorage.key(i); + if (localStorage.getItem(key) === "favorite") { + // Find the corresponding navbar entry + var navbarEntry = document.querySelector(`a[href='${key}']`); + if (navbarEntry) { + // Create a new dropdown entry + var dropdownItem = document.createElement("a"); + dropdownItem.className = "dropdown-item"; + dropdownItem.href = navbarEntry.href; + dropdownItem.innerHTML = navbarEntry.innerHTML; + dropdown.appendChild(dropdownItem); + hasFavorites = true; + } else { + console.warn(`Navbar entry not found for key: ${key}`); + } } - dropdown.innerHTML = ''; // Clear the current favorites + } - var hasFavorites = false; - - for (var i = 0; i < localStorage.length; i++) { - var key = localStorage.key(i); - if (localStorage.getItem(key) === 'favorite') { - // Find the corresponding navbar entry - var navbarEntry = document.querySelector(`a[href='${key}']`); - if (navbarEntry) { - // Create a new dropdown entry - var dropdownItem = document.createElement('a'); - dropdownItem.className = 'dropdown-item'; - dropdownItem.href = navbarEntry.href; - dropdownItem.innerHTML = navbarEntry.innerHTML; - dropdown.appendChild(dropdownItem); - hasFavorites = true; - } else { - console.warn(`Navbar entry not found for key: ${key}`); - } - } - } - - // Show or hide the default item based on whether there are any favorites - if (!hasFavorites) { - var defaultItem = document.createElement('a'); - defaultItem.className = 'dropdown-item'; - defaultItem.textContent = noFavourites; - dropdown.appendChild(defaultItem); - } + // Show or hide the default item based on whether there are any favorites + if (!hasFavorites) { + var defaultItem = document.createElement("a"); + defaultItem.className = "dropdown-item"; + defaultItem.textContent = noFavourites; + dropdown.appendChild(defaultItem); + } } // Ensure that the DOM content has been fully loaded before calling the function -document.addEventListener('DOMContentLoaded', function() { - console.log('DOMContentLoaded event fired'); - updateFavoritesDropdown(); +document.addEventListener("DOMContentLoaded", function () { + console.log("DOMContentLoaded event fired"); + updateFavoritesDropdown(); }); diff --git a/src/main/resources/static/js/fileInput.js b/src/main/resources/static/js/fileInput.js index 610ae723..001c8f24 100644 --- a/src/main/resources/static/js/fileInput.js +++ b/src/main/resources/static/js/fileInput.js @@ -1,104 +1,112 @@ -document.addEventListener('DOMContentLoaded', function() { - document.querySelectorAll('.custom-file-chooser').forEach(setupFileInput); +document.addEventListener("DOMContentLoaded", function () { + document.querySelectorAll(".custom-file-chooser").forEach(setupFileInput); }); function setupFileInput(chooser) { - const elementId = chooser.getAttribute('data-bs-element-id'); - const filesSelected = chooser.getAttribute('data-bs-files-selected'); - const pdfPrompt = chooser.getAttribute('data-bs-pdf-prompt'); + const elementId = chooser.getAttribute("data-bs-element-id"); + const filesSelected = chooser.getAttribute("data-bs-files-selected"); + const pdfPrompt = chooser.getAttribute("data-bs-pdf-prompt"); - let allFiles = []; - let overlay; - let dragCounter = 0; + let allFiles = []; + let overlay; + let dragCounter = 0; - const dragenterListener = function() { - dragCounter++; - if (!overlay) { - overlay = document.createElement('div'); - overlay.style.position = 'fixed'; - overlay.style.top = 0; - overlay.style.left = 0; - overlay.style.width = '100%'; - overlay.style.height = '100%'; - overlay.style.background = 'rgba(0, 0, 0, 0.5)'; - overlay.style.color = '#fff'; - overlay.style.zIndex = '1000'; - overlay.style.display = 'flex'; - overlay.style.alignItems = 'center'; - overlay.style.justifyContent = 'center'; - overlay.style.pointerEvents = 'none'; - overlay.innerHTML = '

Drop files anywhere to upload

'; - document.getElementById('content-wrap').appendChild(overlay); - } - }; + const dragenterListener = function () { + dragCounter++; + if (!overlay) { + overlay = document.createElement("div"); + overlay.style.position = "fixed"; + overlay.style.top = 0; + overlay.style.left = 0; + overlay.style.width = "100%"; + overlay.style.height = "100%"; + overlay.style.background = "rgba(0, 0, 0, 0.5)"; + overlay.style.color = "#fff"; + overlay.style.zIndex = "1000"; + overlay.style.display = "flex"; + overlay.style.alignItems = "center"; + overlay.style.justifyContent = "center"; + overlay.style.pointerEvents = "none"; + overlay.innerHTML = "

Drop files anywhere to upload

"; + document.getElementById("content-wrap").appendChild(overlay); + } + }; - const dragleaveListener = function() { - dragCounter--; - if (dragCounter === 0) { - if (overlay) { - overlay.remove(); - overlay = null; - } - } - }; + const dragleaveListener = function () { + dragCounter--; + if (dragCounter === 0) { + if (overlay) { + overlay.remove(); + overlay = null; + } + } + }; - const dropListener = function(e) { - e.preventDefault(); - const dt = e.dataTransfer; - const files = dt.files; + const dropListener = function (e) { + e.preventDefault(); + const dt = e.dataTransfer; + const files = dt.files; - for (let i = 0; i < files.length; i++) { - allFiles.push(files[i]); - } + for (let i = 0; i < files.length; i++) { + allFiles.push(files[i]); + } - const dataTransfer = new DataTransfer(); - allFiles.forEach(file => dataTransfer.items.add(file)); + const dataTransfer = new DataTransfer(); + allFiles.forEach((file) => dataTransfer.items.add(file)); - const fileInput = document.getElementById(elementId); - fileInput.files = dataTransfer.files; + const fileInput = document.getElementById(elementId); + fileInput.files = dataTransfer.files; - if (overlay) { - overlay.remove(); - overlay = null; - } + if (overlay) { + overlay.remove(); + overlay = null; + } - dragCounter = 0; + dragCounter = 0; - fileInput.dispatchEvent(new Event('change', { bubbles: true })); - }; + fileInput.dispatchEvent(new Event("change", { bubbles: true })); + }; - ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { - document.body.addEventListener(eventName, preventDefaults, false); + ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => { + document.body.addEventListener(eventName, preventDefaults, false); + }); + + function preventDefaults(e) { + e.preventDefault(); + e.stopPropagation(); + } + + document.body.addEventListener("dragenter", dragenterListener); + document.body.addEventListener("dragleave", dragleaveListener); + document.body.addEventListener("drop", dropListener); + + $("#" + elementId).on("change", function (e) { + allFiles = Array.from(e.target.files); + handleFileInputChange(this); + }); + + function handleFileInputChange(inputElement) { + const files = allFiles; + const fileNames = files.map((f) => f.name); + const selectedFilesContainer = $(inputElement).siblings(".selected-files"); + selectedFilesContainer.empty(); + fileNames.forEach((fileName) => { + selectedFilesContainer.append("
" + fileName + "
"); }); - - function preventDefaults(e) { - e.preventDefault(); - e.stopPropagation(); - } - - document.body.addEventListener('dragenter', dragenterListener); - document.body.addEventListener('dragleave', dragleaveListener); - document.body.addEventListener('drop', dropListener); - - $("#" + elementId).on("change", function(e) { - allFiles = Array.from(e.target.files); - handleFileInputChange(this); - }); - - function handleFileInputChange(inputElement) { - const files = allFiles; - const fileNames = files.map(f => f.name); - const selectedFilesContainer = $(inputElement).siblings(".selected-files"); - selectedFilesContainer.empty(); - fileNames.forEach(fileName => { - selectedFilesContainer.append("
" + fileName + "
"); - }); - if (fileNames.length === 1) { - $(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]); - } else if (fileNames.length > 1) { - $(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames.length + " " + filesSelected); - } else { - $(inputElement).siblings(".custom-file-label").addClass("selected").html(pdfPrompt); - } + if (fileNames.length === 1) { + $(inputElement).siblings(".custom-file-label").addClass("selected").html(fileNames[0]); + } else if (fileNames.length > 1) { + $(inputElement) + .siblings(".custom-file-label") + .addClass("selected") + .html(fileNames.length + " " + filesSelected); + } else { + $(inputElement).siblings(".custom-file-label").addClass("selected").html(pdfPrompt); } + } + //Listen for event of file being removed and the filter it out of the allFiles array + document.addEventListener("fileRemoved", function (e) { + const fileName = e.detail; + allFiles = allFiles.filter(file => file.name !== fileName); + }); } diff --git a/src/main/resources/static/js/game.js b/src/main/resources/static/js/game.js index 1bfb9374..3746ef63 100644 --- a/src/main/resources/static/js/game.js +++ b/src/main/resources/static/js/game.js @@ -1,292 +1,276 @@ function initializeGame() { - const gameContainer = document.getElementById('game-container'); - const player = document.getElementById('player'); + const gameContainer = document.getElementById("game-container"); + const player = document.getElementById("player"); - let playerSize = gameContainer.clientWidth * 0.0625; // 5% of container width - player.style.width = playerSize + 'px'; - player.style.height = playerSize + 'px'; + let playerSize = gameContainer.clientWidth * 0.0625; // 5% of container width + player.style.width = playerSize + "px"; + player.style.height = playerSize + "px"; - let playerX = gameContainer.clientWidth / 2 - playerSize / 2; - let playerY = gameContainer.clientHeight * 0.1; - const scoreElement = document.getElementById('score'); - const levelElement = document.getElementById('level'); - const livesElement = document.getElementById('lives'); - const highScoreElement = document.getElementById('high-score'); + let playerX = gameContainer.clientWidth / 2 - playerSize / 2; + let playerY = gameContainer.clientHeight * 0.1; + const scoreElement = document.getElementById("score"); + const levelElement = document.getElementById("level"); + const livesElement = document.getElementById("lives"); + const highScoreElement = document.getElementById("high-score"); - let pdfSize = gameContainer.clientWidth * 0.0625; // 5% of container width - let projectileWidth = gameContainer.clientWidth * 0.00625;// 0.00625; // 0.5% of container width - let projectileHeight = gameContainer.clientHeight * 0.01667; // 1% of container height + let pdfSize = gameContainer.clientWidth * 0.0625; // 5% of container width + let projectileWidth = gameContainer.clientWidth * 0.00625; // 0.00625; // 0.5% of container width + let projectileHeight = gameContainer.clientHeight * 0.01667; // 1% of container height - let paused = false; + let paused = false; - const fireRate = 200; // Time between shots in milliseconds - let lastProjectileTime = 0; - let lives = 3; + const fireRate = 200; // Time between shots in milliseconds + let lastProjectileTime = 0; + let lives = 3; + let highScore = localStorage.getItem("highScore") ? parseInt(localStorage.getItem("highScore")) : 0; + updateHighScore(); - let highScore = localStorage.getItem('highScore') ? parseInt(localStorage.getItem('highScore')) : 0; - updateHighScore(); + const PLAYER_MOVE_SPEED = 5; + const BASE_PDF_SPEED = 1; + const LEVEL_INCREASE_PDF_SPEED = 0.2; + const BASE_SPAWN_INTERVAL_MS = 1250; // milliseconds before a new enemy spawns + const LEVEL_INCREASE_FACTOR_MS = 25; // milliseconds to decrease the spawn interval per level + const MAX_SPAWN_RATE_REDUCTION_MS = 800; // Max milliseconds from the base spawn interval + + + let keysPressed = {}; + const pdfs = []; + const projectiles = []; + let score = 0; + let level = 1; + let pdfSpeed = BASE_PDF_SPEED; + let gameOver = false; - - - const keysPressed = {}; - const pdfs = []; - const projectiles = []; - let score = 0; - let level = 1; - let pdfSpeed = 0.5; - let gameOver = false; - - function handleKeys() { - if (keysPressed['ArrowLeft']) { - playerX -= 10; - } - if (keysPressed['ArrowRight']) { - playerX += 10; - } - if (keysPressed[' '] && !gameOver) { - const currentTime = new Date().getTime(); - if (currentTime - lastProjectileTime >= fireRate) { - shootProjectile(); - lastProjectileTime = currentTime; - } - } - updatePlayerPosition(); - } - - - - - document.addEventListener('keydown', (event) => { - if (event.key === ' ') { - event.preventDefault(); - } - keysPressed[event.key] = true; - handleKeys(); - }); - - document.addEventListener('keyup', (event) => { - keysPressed[event.key] = false; - }); - - - function updatePlayerPosition() { - player.style.left = playerX + 'px'; - player.style.bottom = playerY + 'px'; + + function handleKeys() { + if (keysPressed["ArrowLeft"]) { + playerX -= PLAYER_MOVE_SPEED; + playerX = Math.max(0, playerX) } - - function updateLives() { - livesElement.textContent = 'Lives: ' + lives; + if (keysPressed["ArrowRight"]) { + playerX += PLAYER_MOVE_SPEED; + playerX = Math.min(gameContainer.clientWidth - playerSize, playerX); } - - function updateHighScore() { - highScoreElement.textContent = 'High Score: ' + highScore; + if (keysPressed[" "] && !gameOver) { + const currentTime = new Date().getTime(); + if (currentTime - lastProjectileTime >= fireRate) { + shootProjectile(); + lastProjectileTime = currentTime; + } } - - - function shootProjectile() { - const projectile = document.createElement('div'); - projectile.classList.add('projectile'); - projectile.style.backgroundColor = 'black'; - projectile.style.width = projectileWidth + 'px'; - projectile.style.height = projectileHeight + 'px'; - projectile.style.left = (playerX + playerSize / 2 - projectileWidth / 2) + 'px'; - projectile.style.top = (gameContainer.clientHeight - playerY - playerSize) + 'px'; - gameContainer.appendChild(projectile); - projectiles.push(projectile); - } - - - - function spawnPdf() { - const pdf = document.createElement('img'); - pdf.src = 'images/file-earmark-pdf.svg'; - pdf.classList.add('pdf'); - pdf.style.width = pdfSize + 'px'; - pdf.style.height = pdfSize + 'px'; - pdf.style.left = Math.floor(Math.random() * (gameContainer.clientWidth - pdfSize)) + 'px'; - pdf.style.top = '0px'; - gameContainer.appendChild(pdf); - pdfs.push(pdf); - } - - - function resetEnemies() { - pdfs.forEach((pdf) => gameContainer.removeChild(pdf)); - pdfs.length = 0; - } - - - function updateGame() { - if (gameOver || paused) return; - - for (let pdfIndex = 0; pdfIndex < pdfs.length; pdfIndex++) { - const pdf = pdfs[pdfIndex]; - const pdfY = parseFloat(pdf.style.top) + pdfSpeed; - if (pdfY + 50 > gameContainer.clientHeight) { - gameContainer.removeChild(pdf); - pdfs.splice(pdfIndex, 1); - - // Deduct 2 points when a PDF gets past the player - score -= 0; - updateScore(); - - // Decrease lives and check if game over - lives--; - updateLives(); - if (lives <= 0) { - endGame(); - return; - } - - } else { - pdf.style.top = pdfY + 'px'; - - // Check for collision with player - if (collisionDetected(player, pdf)) { - lives--; - updateLives(); - resetEnemies(); - if (lives <= 0) { - endGame(); - return; - } - } - } - }; - - projectiles.forEach((projectile, projectileIndex) => { - const projectileY = parseInt(projectile.style.top) - 10; - if (projectileY < 0) { - gameContainer.removeChild(projectile); - projectiles.splice(projectileIndex, 1); - } else { - projectile.style.top = projectileY + 'px'; - } - - for (let pdfIndex = 0; pdfIndex < pdfs.length; pdfIndex++) { - const pdf = pdfs[pdfIndex]; - if (collisionDetected(projectile, pdf)) { - gameContainer.removeChild(pdf); - gameContainer.removeChild(projectile); - pdfs.splice(pdfIndex, 1); - projectiles.splice(projectileIndex, 1); - score = score + 10; - updateScore(); - break; - } - } - }); - - setTimeout(updateGame, 1000 / 60); - } - - function resetGame() { - playerX = gameContainer.clientWidth / 2; - playerY = 50; - updatePlayerPosition(); - - pdfs.forEach((pdf) => gameContainer.removeChild(pdf)); - projectiles.forEach((projectile) => gameContainer.removeChild(projectile)); - - pdfs.length = 0; - projectiles.length = 0; - - score = 0; - level = 1; - lives = 3; - - gameOver = false; - - updateScore(); - updateLives(); - levelElement.textContent = 'Level: ' + level; - pdfSpeed = 1; - clearTimeout(spawnPdfTimeout); // Clear the existing spawnPdfTimeout - setTimeout(updateGame, 1000 / 60); - spawnPdfInterval(); - } - - - - function updateScore() { - scoreElement.textContent = 'Score: ' + score; - checkLevelUp(); - } - - - - function checkLevelUp() { - const newLevel = Math.floor(score / 100) + 1; - if (newLevel > level) { - level = newLevel; - levelElement.textContent = 'Level: ' + level; - pdfSpeed += 0.2; - } - } - - function collisionDetected(a, b) { - const rectA = a.getBoundingClientRect(); - const rectB = b.getBoundingClientRect(); - return ( - rectA.left < rectB.right && - rectA.right > rectB.left && - rectA.top < rectB.bottom && - rectA.bottom > rectB.top - ); - } - - function endGame() { - gameOver = true; - if (score > highScore) { - highScore = score; - localStorage.setItem('highScore', highScore); - updateHighScore(); - } - alert('Game Over! Your final score is: ' + score); - document.getElementById('game-container-wrapper').close(); - } - - - - - let spawnPdfTimeout; - - const BASE_SPAWN_INTERVAL_MS = 1250; // milliseconds before a new enemy spawns - const LEVEL_INCREASE_FACTOR_MS = 0; // milliseconds to decrease the spawn interval per level - const MAX_SPAWN_RATE_REDUCTION_MS = 800; // Max milliseconds from the base spawn interval - - function spawnPdfInterval() { - console.log("spawnPdfInterval"); - if (gameOver || paused) { - console.log("spawnPdfInterval 2"); - clearTimeout(spawnPdfTimeout); - return; - } - console.log("spawnPdfInterval 3"); - spawnPdf(); - let spawnRateReduction = Math.min(level * LEVEL_INCREASE_FACTOR_MS, MAX_SPAWN_RATE_REDUCTION_MS); - let spawnRate = BASE_SPAWN_INTERVAL_MS - spawnRateReduction; - spawnPdfTimeout = setTimeout(spawnPdfInterval, spawnRate); - } - updatePlayerPosition(); - updateGame(); - spawnPdfInterval(); + } + function onKeydown(event) { + if (event.key === " ") { + event.preventDefault(); + } + keysPressed[event.key] = true; + handleKeys(); + } + function onKeyUp(event) { + keysPressed[event.key] = false; + } + + document.removeEventListener("keydown", onKeydown); + document.removeEventListener("keyup", onKeyUp); + document.addEventListener("keydown", onKeydown); + document.addEventListener("keyup", onKeyUp); - document.addEventListener('visibilitychange', function() { - if (document.hidden) { - paused = true; - } else { - paused = false; - updateGame(); - spawnPdfInterval(); + function updatePlayerPosition() { + player.style.left = playerX + "px"; + player.style.bottom = playerY + "px"; + } + + function updateLives() { + livesElement.textContent = "Lives: " + lives; + } + + function updateHighScore() { + highScoreElement.textContent = "High Score: " + highScore; + } + + function shootProjectile() { + const projectile = document.createElement("div"); + projectile.classList.add("projectile"); + projectile.style.backgroundColor = "black"; + projectile.style.width = projectileWidth + "px"; + projectile.style.height = projectileHeight + "px"; + projectile.style.left = playerX + playerSize / 2 - projectileWidth / 2 + "px"; + projectile.style.top = gameContainer.clientHeight - playerY - playerSize + "px"; + gameContainer.appendChild(projectile); + projectiles.push(projectile); + } + + function spawnPdf() { + const pdf = document.createElement("img"); + pdf.src = "images/file-earmark-pdf.svg"; + pdf.classList.add("pdf"); + pdf.style.width = pdfSize + "px"; + pdf.style.height = pdfSize + "px"; + pdf.style.left = Math.floor(Math.random() * (gameContainer.clientWidth - (2*pdfSize))) + pdfSize + "px"; + pdf.style.top = "0px"; + gameContainer.appendChild(pdf); + pdfs.push(pdf); + } + + function resetEnemies() { + pdfs.forEach((pdf) => gameContainer.removeChild(pdf)); + pdfs.length = 0; + } + + function updateGame() { + if (gameOver || paused) return; + + handleKeys(); + for (let pdfIndex = 0; pdfIndex < pdfs.length; pdfIndex++) { + const pdf = pdfs[pdfIndex]; + const pdfY = parseFloat(pdf.style.top) + pdfSpeed; + if (pdfY + 50 > gameContainer.clientHeight) { + gameContainer.removeChild(pdf); + pdfs.splice(pdfIndex, 1); + + // Deduct 2 points when a PDF gets past the player + score -= 0; + updateScore(); + + // Decrease lives and check if game over + lives--; + updateLives(); + if (lives <= 0) { + endGame(); + return; } + } else { + pdf.style.top = pdfY + "px"; + // Check for collision with player + if (collisionDetected(player, pdf)) { + lives--; + updateLives(); + resetEnemies(); + if (lives <= 0) { + endGame(); + return; + } + } + } + } + + projectiles.forEach((projectile, projectileIndex) => { + const projectileY = parseInt(projectile.style.top) - 10; + if (projectileY < 0) { + gameContainer.removeChild(projectile); + projectiles.splice(projectileIndex, 1); + } else { + projectile.style.top = projectileY + "px"; + } + + for (let pdfIndex = 0; pdfIndex < pdfs.length; pdfIndex++) { + const pdf = pdfs[pdfIndex]; + if (collisionDetected(projectile, pdf)) { + gameContainer.removeChild(pdf); + gameContainer.removeChild(projectile); + pdfs.splice(pdfIndex, 1); + projectiles.splice(projectileIndex, 1); + score = score + 10; + updateScore(); + break; + } + } }); - window.resetGame = resetGame; + setTimeout(updateGame, 1000 / 60); + } + + function resetGame() { + playerX = gameContainer.clientWidth / 2; + playerY = 50; + updatePlayerPosition(); + + pdfs.forEach((pdf) => gameContainer.removeChild(pdf)); + projectiles.forEach((projectile) => gameContainer.removeChild(projectile)); + + pdfs.length = 0; + projectiles.length = 0; + + score = 0; + level = 1; + lives = 3; + + gameOver = false; + + updateScore(); + updateLives(); + levelElement.textContent = "Level: " + level; + pdfSpeed = BASE_PDF_SPEED; + clearTimeout(spawnPdfTimeout); // Clear the existing spawnPdfTimeout + setTimeout(updateGame, 1000 / 60); + spawnPdfInterval(); + } + + function updateScore() { + scoreElement.textContent = "Score: " + score; + checkLevelUp(); + } + + function checkLevelUp() { + const newLevel = Math.floor(score / 100) + 1; + if (newLevel > level) { + level = newLevel; + levelElement.textContent = "Level: " + level; + pdfSpeed += LEVEL_INCREASE_PDF_SPEED; + } + } + + function collisionDetected(a, b) { + const rectA = a.getBoundingClientRect(); + const rectB = b.getBoundingClientRect(); + return rectA.left < rectB.right && rectA.right > rectB.left && rectA.top < rectB.bottom && rectA.bottom > rectB.top; + } + + function endGame() { + gameOver = true; + if (score > highScore) { + highScore = score; + localStorage.setItem("highScore", highScore); + updateHighScore(); + } + alert("Game Over! Your final score is: " + score); + document.getElementById("game-container-wrapper").close(); + } + + let spawnPdfTimeout; + + + + function spawnPdfInterval() { + if (gameOver || paused) { + clearTimeout(spawnPdfTimeout); + return; + } + spawnPdf(); + let spawnRateReduction = Math.min(level * LEVEL_INCREASE_FACTOR_MS, MAX_SPAWN_RATE_REDUCTION_MS); + let spawnRate = BASE_SPAWN_INTERVAL_MS - spawnRateReduction; + spawnPdfTimeout = setTimeout(spawnPdfInterval, spawnRate); + } + + updatePlayerPosition(); + updateGame(); + spawnPdfInterval(); + + document.addEventListener("visibilitychange", function () { + if (document.hidden) { + paused = true; + } else { + paused = false; + updateGame(); + spawnPdfInterval(); + } + }); + + window.resetGame = resetGame; } window.initializeGame = initializeGame; diff --git a/src/main/resources/static/js/githubVersion.js b/src/main/resources/static/js/githubVersion.js index 6400d5cf..b312fd85 100644 --- a/src/main/resources/static/js/githubVersion.js +++ b/src/main/resources/static/js/githubVersion.js @@ -1,55 +1,72 @@ function compareVersions(version1, version2) { - const v1 = version1.split('.'); - const v2 = version2.split('.'); + const v1 = version1.split("."); + const v2 = version2.split("."); - for (let i = 0; i < v1.length || i < v2.length; i++) { - const n1 = parseInt(v1[i]) || 0; - const n2 = parseInt(v2[i]) || 0; + for (let i = 0; i < v1.length || i < v2.length; i++) { + const n1 = parseInt(v1[i]) || 0; + const n2 = parseInt(v2[i]) || 0; - if (n1 > n2) { - return 1; - } else if (n1 < n2) { - return -1; - } - } + if (n1 > n2) { + return 1; + } else if (n1 < n2) { + return -1; + } + } - return 0; + return 0; } - async function getLatestReleaseVersion() { - const url = "https://api.github.com/repos/Stirling-Tools/Stirling-PDF/releases/latest"; - try { - const response = await fetch(url); - const data = await response.json(); - return data.tag_name ? data.tag_name.substring(1) : ""; - } catch (error) { - console.error("Failed to fetch latest version:", error); - return ""; // Return an empty string if the fetch fails - } + const url = "https://api.github.com/repos/Stirling-Tools/Stirling-PDF/releases/latest"; + try { + const response = await fetch(url); + const data = await response.json(); + return data.tag_name ? data.tag_name.substring(1) : ""; + } catch (error) { + console.error("Failed to fetch latest version:", error); + return ""; // Return an empty string if the fetch fails + } } async function checkForUpdate() { - // Initialize the update button as hidden - var updateBtn = document.getElementById("update-btn"); - if (updateBtn !== null) { - updateBtn.style.display = "none"; - } - - - const latestVersion = await getLatestReleaseVersion(); - console.log("latestVersion=" + latestVersion) - console.log("currentVersion=" + currentVersion) - console.log("compareVersions(latestVersion, currentVersion) > 0)=" + compareVersions(latestVersion, currentVersion)) - if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) { - document.getElementById("update-btn").style.display = "block"; - console.log("visible") - } else { - console.log("hidden") + // Initialize the update button as hidden + var updateBtn = document.getElementById("update-btn") || null; + var updateLink = document.getElementById("update-link") || null; + if (updateBtn !== null) { + updateBtn.style.display = "none"; + } + if (updateLink !== null) { + console.log("hidden!"); + if (!updateLink.classList.contains("visually-hidden")) { + updateLink.classList.add("visually-hidden"); } + } + + const latestVersion = await getLatestReleaseVersion(); + console.log("latestVersion=" + latestVersion); + console.log("currentVersion=" + currentVersion); + console.log("compareVersions(latestVersion, currentVersion) > 0)=" + compareVersions(latestVersion, currentVersion)); + if (latestVersion && compareVersions(latestVersion, currentVersion) > 0) { + if (updateBtn != null) { + document.getElementById("update-btn").style.display = "block"; + } + if (updateLink !== null) { + document.getElementById("app-update").innerHTML = updateAvailable.replace("{0}", '' + currentVersion + '').replace("{1}", '' + latestVersion + ''); + if (updateLink.classList.contains("visually-hidden")) { + updateLink.classList.remove("visually-hidden"); + } + } + console.log("visible"); + } else { + if (updateLink !== null) { + if (!updateLink.classList.contains("visually-hidden")) { + updateLink.classList.add("visually-hidden"); + } + } + console.log("hidden"); + } } - -document.addEventListener('DOMContentLoaded', (event) => { - checkForUpdate(); +document.addEventListener("DOMContentLoaded", (event) => { + checkForUpdate(); }); diff --git a/src/main/resources/static/js/homecard.js b/src/main/resources/static/js/homecard.js index 6fb43387..c461af3c 100644 --- a/src/main/resources/static/js/homecard.js +++ b/src/main/resources/static/js/homecard.js @@ -1,78 +1,82 @@ function filterCards() { - var input = document.getElementById('searchBar'); - var filter = input.value.toUpperCase(); - var cards = document.querySelectorAll('.feature-card'); + var input = document.getElementById("searchBar"); + var filter = input.value.toUpperCase(); + var cards = document.querySelectorAll(".feature-card"); - for (var i = 0; i < cards.length; i++) { - var card = cards[i]; - var title = card.querySelector('h5.card-title').innerText; - var text = card.querySelector('p.card-text').innerText; + for (var i = 0; i < cards.length; i++) { + var card = cards[i]; + var title = card.querySelector("h5.card-title").innerText; + var text = card.querySelector("p.card-text").innerText; - // Get the navbar tags associated with the card - var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`); - var navbarTags = navbarItem ? navbarItem.getAttribute('data-bs-tags') : ''; + // Get the navbar tags associated with the card + var navbarItem = document.querySelector(`a.dropdown-item[href="${card.id}"]`); + var navbarTags = navbarItem ? navbarItem.getAttribute("data-bs-tags") : ""; - var content = title + ' ' + text + ' ' + navbarTags; + var content = title + " " + text + " " + navbarTags; - if (content.toUpperCase().indexOf(filter) > -1) { - card.style.display = ""; - } else { - card.style.display = "none"; - } + if (content.toUpperCase().indexOf(filter) > -1) { + card.style.display = ""; + } else { + card.style.display = "none"; } + } } - - function toggleFavorite(element) { - var img = element.querySelector('img'); - var card = element.closest('.feature-card'); - var cardId = card.id; - if (img.src.endsWith('star.svg')) { - img.src = 'images/star-fill.svg'; - card.classList.add('favorite'); - localStorage.setItem(cardId, 'favorite'); - } else { - img.src = 'images/star.svg'; - card.classList.remove('favorite'); - localStorage.removeItem(cardId); - } - reorderCards(); - updateFavoritesDropdown(); - filterCards(); + var img = element.querySelector("img"); + var card = element.closest(".feature-card"); + var cardId = card.id; + if (img.src.endsWith("star.svg")) { + img.src = "images/star-fill.svg"; + card.classList.add("favorite"); + localStorage.setItem(cardId, "favorite"); + } else { + img.src = "images/star.svg"; + card.classList.remove("favorite"); + localStorage.removeItem(cardId); + } + reorderCards(); + updateFavoritesDropdown(); + filterCards(); } function reorderCards() { - var container = document.querySelector('.features-container'); - var cards = Array.from(container.getElementsByClassName('feature-card')); - cards.sort(function(a, b) { - var aIsFavorite = localStorage.getItem(a.id) === 'favorite'; - var bIsFavorite = localStorage.getItem(b.id) === 'favorite'; - if (aIsFavorite && !bIsFavorite) { - return -1; - } - if (!aIsFavorite && bIsFavorite) { - return 1; - } - return 0; - }); - cards.forEach(function(card) { - container.appendChild(card); - }); + var container = document.querySelector(".features-container"); + var cards = Array.from(container.getElementsByClassName("feature-card")); + cards.sort(function (a, b) { + var aIsFavorite = localStorage.getItem(a.id) === "favorite"; + var bIsFavorite = localStorage.getItem(b.id) === "favorite"; + if (a.id === "update-link") { + return -1; + } + if (b.id === "update-link") { + return 1; + } + if (aIsFavorite && !bIsFavorite) { + return -1; + } + if (!aIsFavorite && bIsFavorite) { + return 1; + } + return 0; + }); + cards.forEach(function (card) { + container.appendChild(card); + }); } function initializeCards() { - var cards = document.querySelectorAll('.feature-card'); - cards.forEach(function(card) { - var cardId = card.id; - var img = card.querySelector('.favorite-icon img'); - if (localStorage.getItem(cardId) === 'favorite') { - img.src = 'images/star-fill.svg'; - card.classList.add('favorite'); - } - }); - reorderCards(); - updateFavoritesDropdown(); - filterCards(); + var cards = document.querySelectorAll(".feature-card"); + cards.forEach(function (card) { + var cardId = card.id; + var img = card.querySelector(".favorite-icon img"); + if (localStorage.getItem(cardId) === "favorite") { + img.src = "images/star-fill.svg"; + card.classList.add("favorite"); + } + }); + reorderCards(); + updateFavoritesDropdown(); + filterCards(); } -window.onload = initializeCards; \ No newline at end of file +window.onload = initializeCards; diff --git a/src/main/resources/static/js/languageSelection.js b/src/main/resources/static/js/languageSelection.js index 4cc18f12..7af113d0 100644 --- a/src/main/resources/static/js/languageSelection.js +++ b/src/main/resources/static/js/languageSelection.js @@ -1,80 +1,90 @@ -document.addEventListener('DOMContentLoaded', function() { - setLanguageForDropdown('.lang_dropdown-item'); +document.addEventListener("DOMContentLoaded", function () { + setLanguageForDropdown(".lang_dropdown-item"); - // Detect the browser's preferred language - let browserLang = navigator.language || navigator.userLanguage; - // Convert to a format consistent with your language codes (e.g., en-GB, fr-FR) - browserLang = browserLang.replace('-', '_'); + // Detect the browser's preferred language + let browserLang = navigator.language || navigator.userLanguage; + // Convert to a format consistent with your language codes (e.g., en-GB, fr-FR) + browserLang = browserLang.replace("-", "_"); - // Check if the dropdown contains the browser's language - const dropdownLangExists = document.querySelector(`.lang_dropdown-item[data-language-code="${browserLang}"]`); + // Check if the dropdown contains the browser's language + const dropdownLangExists = document.querySelector(`.lang_dropdown-item[data-language-code="${browserLang}"]`); - // Set the default language to browser's language or 'en_GB' if not found in the dropdown - const defaultLocale = dropdownLangExists ? browserLang : 'en_GB'; - const storedLocale = localStorage.getItem('languageCode') || defaultLocale; + // Set the default language to browser's language or 'en_GB' if not found in the dropdown + const defaultLocale = dropdownLangExists ? browserLang : "en_GB"; + const storedLocale = localStorage.getItem("languageCode") || defaultLocale; + const dropdownItems = document.querySelectorAll(".lang_dropdown-item"); - - const dropdownItems = document.querySelectorAll('.lang_dropdown-item'); - - for (let i = 0; i < dropdownItems.length; i++) { - const item = dropdownItems[i]; - item.classList.remove('active'); - if (item.dataset.languageCode === storedLocale) { - item.classList.add('active'); - } - item.addEventListener('click', handleDropdownItemClick); - } + for (let i = 0; i < dropdownItems.length; i++) { + const item = dropdownItems[i]; + item.classList.remove("active"); + if (item.dataset.languageCode === storedLocale) { + item.classList.add("active"); + } + item.addEventListener("click", handleDropdownItemClick); + } }); function setLanguageForDropdown(dropdownClass) { - const defaultLocale = document.documentElement.lang || 'en_GB'; - const storedLocale = localStorage.getItem('languageCode') || defaultLocale; - const dropdownItems = document.querySelectorAll(dropdownClass); + const defaultLocale = document.documentElement.getAttribute("data-language") || "en_GB"; + const storedLocale = localStorage.getItem("languageCode") || defaultLocale; + const dropdownItems = document.querySelectorAll(dropdownClass); - for (let i = 0; i < dropdownItems.length; i++) { - const item = dropdownItems[i]; - item.classList.remove('active'); - if (item.dataset.languageCode === storedLocale) { - item.classList.add('active'); - } - item.addEventListener('click', handleDropdownItemClick); + for (let i = 0; i < dropdownItems.length; i++) { + const item = dropdownItems[i]; + item.classList.remove("active"); + if (item.dataset.languageCode === storedLocale) { + item.classList.add("active"); } + item.addEventListener("click", handleDropdownItemClick); + } } function handleDropdownItemClick(event) { - event.preventDefault(); - const languageCode = event.currentTarget.dataset.bsLanguageCode; // change this to event.currentTarget - if (languageCode) { - localStorage.setItem('languageCode', languageCode); + event.preventDefault(); + const languageCode = event.currentTarget.dataset.bsLanguageCode; // change this to event.currentTarget + if (languageCode) { + localStorage.setItem("languageCode", languageCode); - const currentUrl = window.location.href; - if (currentUrl.indexOf('?lang=') === -1) { - window.location.href = currentUrl + '?lang=' + languageCode; - } else { - window.location.href = currentUrl.replace(/\?lang=\w{2,}/, '?lang=' + languageCode); - } + const currentUrl = window.location.href; + if (currentUrl.indexOf("?lang=") === -1 && currentUrl.indexOf("&lang=") === -1) { + window.location.href = currentUrl + "?lang=" + languageCode; + } else if (currentUrl.indexOf("&lang=") !== -1 && currentUrl.indexOf("?lang=") === -1) { + window.location.href = currentUrl.replace(/&lang=\w{2,}/, "&lang=" + languageCode); } else { - console.error("Language code is not set for this item."); // for debugging + window.location.href = currentUrl.replace(/\?lang=\w{2,}/, "?lang=" + languageCode); } + } else { + console.error("Language code is not set for this item."); // for debugging + } } +document.addEventListener("DOMContentLoaded", function () { + document.querySelectorAll(".nav-item.dropdown").forEach((element) => { + const dropdownMenu = element.querySelector(".dropdown-menu"); + if ( + dropdownMenu.id !== "favoritesDropdown" && + dropdownMenu.children.length <= 2 && + dropdownMenu.querySelectorAll("hr.dropdown-divider").length === dropdownMenu.children.length + ) { + if ( + element.previousElementSibling && + element.previousElementSibling.classList.contains("nav-item") && + element.previousElementSibling.classList.contains("nav-item-separator") + ) { + element.previousElementSibling.remove(); + } + element.remove(); + } + }); -document.addEventListener('DOMContentLoaded', function() { - document.querySelectorAll('.nav-item.dropdown').forEach((element) => { - const dropdownMenu = element.querySelector(".dropdown-menu"); - if (dropdownMenu.id !== 'favoritesDropdown' && dropdownMenu.children.length <= 2 && dropdownMenu.querySelectorAll("hr.dropdown-divider").length === dropdownMenu.children.length) { - if (element.previousElementSibling && element.previousElementSibling.classList.contains('nav-item') && element.previousElementSibling.classList.contains('nav-item-separator')) { - element.previousElementSibling.remove(); - } - element.remove(); - } - }); - - //Sort languages by alphabet - const list = Array.from(document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').children).filter(child => child.matches('a')); - list.sort(function(a, b) { - return a.textContent.toUpperCase().localeCompare(b.textContent.toUpperCase()); - }).forEach(node => document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').appendChild(node)); - -}); \ No newline at end of file + //Sort languages by alphabet + const list = Array.from(document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').children).filter( + (child) => child.matches("a"), + ); + list + .sort(function (a, b) { + return a.textContent.toUpperCase().localeCompare(b.textContent.toUpperCase()); + }) + .forEach((node) => document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').appendChild(node)); +}); diff --git a/src/main/resources/static/js/local-pdf-input-download.js b/src/main/resources/static/js/local-pdf-input-download.js index 37090390..44793884 100644 --- a/src/main/resources/static/js/local-pdf-input-download.js +++ b/src/main/resources/static/js/local-pdf-input-download.js @@ -1,47 +1,47 @@ async function downloadFilesWithCallback(processFileCallback) { - const fileInput = document.querySelector('input[type="file"]'); - const files = fileInput.files; + const fileInput = document.querySelector('input[type="file"]'); + const files = fileInput.files; - const zipThreshold = 4; - const zipFiles = files.length > zipThreshold; + const zipThreshold = 4; + const zipFiles = files.length > zipThreshold; - let jszip = null; - if (zipFiles) { - jszip = new JSZip(); - } + let jszip = null; + if (zipFiles) { + jszip = new JSZip(); + } - const promises = Array.from(files).map(async file => { - const { processedData, fileName } = await processFileCallback(file); - - if (zipFiles) { - jszip.file(fileName, processedData); - } else { - const url = URL.createObjectURL(processedData); - const downloadOption = localStorage.getItem('downloadOption'); - - if (downloadOption === 'sameWindow') { - window.location.href = url; - } else if (downloadOption === 'newWindow') { - window.open(url, '_blank'); - } else { - const downloadLink = document.createElement('a'); - downloadLink.href = url; - downloadLink.download = fileName; - downloadLink.click(); - } - } - }); - - await Promise.all(promises); + const promises = Array.from(files).map(async (file) => { + const { processedData, fileName } = await processFileCallback(file); if (zipFiles) { - const content = await jszip.generateAsync({ type: "blob" }); - const url = URL.createObjectURL(content); - const a = document.createElement('a'); - a.href = url; - a.download = "files.zip"; - document.body.appendChild(a); - a.click(); - a.remove(); + jszip.file(fileName, processedData); + } else { + const url = URL.createObjectURL(processedData); + const downloadOption = localStorage.getItem("downloadOption"); + + if (downloadOption === "sameWindow") { + window.location.href = url; + } else if (downloadOption === "newWindow") { + window.open(url, "_blank"); + } else { + const downloadLink = document.createElement("a"); + downloadLink.href = url; + downloadLink.download = fileName; + downloadLink.click(); + } } + }); + + await Promise.all(promises); + + if (zipFiles) { + const content = await jszip.generateAsync({ type: "blob" }); + const url = URL.createObjectURL(content); + const a = document.createElement("a"); + a.href = url; + a.download = "files.zip"; + document.body.appendChild(a); + a.click(); + a.remove(); + } } diff --git a/src/main/resources/static/js/merge.js b/src/main/resources/static/js/merge.js index e8e60d4f..55575727 100644 --- a/src/main/resources/static/js/merge.js +++ b/src/main/resources/static/js/merge.js @@ -1,27 +1,27 @@ let currentSort = { - field: null, - descending: false + field: null, + descending: false, }; -document.getElementById("fileInput-input").addEventListener("change", function() { - var files = this.files; - displayFiles(files); +document.getElementById("fileInput-input").addEventListener("change", function () { + var files = this.files; + displayFiles(files); }); /** * @param {FileList} files */ function displayFiles(files) { - const list = document.getElementById("selectedFiles"); + const list = document.getElementById("selectedFiles"); - while (list.firstChild) { - list.removeChild(list.firstChild); - } + while (list.firstChild) { + list.removeChild(list.firstChild); + } - for (let i = 0; i < files.length; i++) { - const item = document.createElement("li"); - item.className = "list-group-item"; - item.innerHTML = ` + for (let i = 0; i < files.length; i++) { + const item = document.createElement("li"); + item.className = "list-group-item"; + item.innerHTML = `
${files[i].name}
@@ -31,100 +31,105 @@ function displayFiles(files) {
`; - list.appendChild(item); - } + list.appendChild(item); + } - attachMoveButtons(); + attachMoveButtons(); } function attachMoveButtons() { - var moveUpButtons = document.querySelectorAll(".move-up"); - for (var i = 0; i < moveUpButtons.length; i++) { - moveUpButtons[i].addEventListener("click", function(event) { - event.preventDefault(); - var parent = this.closest(".list-group-item"); - var grandParent = parent.parentNode; - if (parent.previousElementSibling) { - grandParent.insertBefore(parent, parent.previousElementSibling); - updateFiles(); - } - }); - } + var moveUpButtons = document.querySelectorAll(".move-up"); + for (var i = 0; i < moveUpButtons.length; i++) { + moveUpButtons[i].addEventListener("click", function (event) { + event.preventDefault(); + var parent = this.closest(".list-group-item"); + var grandParent = parent.parentNode; + if (parent.previousElementSibling) { + grandParent.insertBefore(parent, parent.previousElementSibling); + updateFiles(); + } + }); + } - var moveDownButtons = document.querySelectorAll(".move-down"); - for (var i = 0; i < moveDownButtons.length; i++) { - moveDownButtons[i].addEventListener("click", function(event) { - event.preventDefault(); - var parent = this.closest(".list-group-item"); - var grandParent = parent.parentNode; - if (parent.nextElementSibling) { - grandParent.insertBefore(parent.nextElementSibling, parent); - updateFiles(); - } - }); - } + var moveDownButtons = document.querySelectorAll(".move-down"); + for (var i = 0; i < moveDownButtons.length; i++) { + moveDownButtons[i].addEventListener("click", function (event) { + event.preventDefault(); + var parent = this.closest(".list-group-item"); + var grandParent = parent.parentNode; + if (parent.nextElementSibling) { + grandParent.insertBefore(parent.nextElementSibling, parent); + updateFiles(); + } + }); + } - var removeButtons = document.querySelectorAll(".remove-file"); - for (var i = 0; i < removeButtons.length; i++) { - removeButtons[i].addEventListener("click", function (event) { - event.preventDefault(); - var parent = this.closest(".list-group-item"); - parent.remove(); - updateFiles(); - }); - } + var removeButtons = document.querySelectorAll(".remove-file"); + for (var i = 0; i < removeButtons.length; i++) { + removeButtons[i].addEventListener("click", function (event) { + event.preventDefault(); + var parent = this.closest(".list-group-item"); + //Get name of removed file + var fileName = parent.querySelector(".filename").innerText; + parent.remove(); + updateFiles(); + //Dispatch a custom event with the name of the removed file + var event = new CustomEvent("fileRemoved", { detail: fileName }); + document.dispatchEvent(event); + }); + } } -document.getElementById("sortByNameBtn").addEventListener("click", function() { - if (currentSort.field === "name" && !currentSort.descending) { - currentSort.descending = true; - sortFiles((a, b) => b.name.localeCompare(a.name)); - } else { - currentSort.field = "name"; - currentSort.descending = false; - sortFiles((a, b) => a.name.localeCompare(b.name)); - } +document.getElementById("sortByNameBtn").addEventListener("click", function () { + if (currentSort.field === "name" && !currentSort.descending) { + currentSort.descending = true; + sortFiles((a, b) => b.name.localeCompare(a.name)); + } else { + currentSort.field = "name"; + currentSort.descending = false; + sortFiles((a, b) => a.name.localeCompare(b.name)); + } }); -document.getElementById("sortByDateBtn").addEventListener("click", function() { - if (currentSort.field === "lastModified" && !currentSort.descending) { - currentSort.descending = true; - sortFiles((a, b) => b.lastModified - a.lastModified); - } else { - currentSort.field = "lastModified"; - currentSort.descending = false; - sortFiles((a, b) => a.lastModified - b.lastModified); - } +document.getElementById("sortByDateBtn").addEventListener("click", function () { + if (currentSort.field === "lastModified" && !currentSort.descending) { + currentSort.descending = true; + sortFiles((a, b) => b.lastModified - a.lastModified); + } else { + currentSort.field = "lastModified"; + currentSort.descending = false; + sortFiles((a, b) => a.lastModified - b.lastModified); + } }); function sortFiles(comparator) { - // Convert FileList to array and sort - const sortedFilesArray = Array.from(document.getElementById("fileInput-input").files).sort(comparator); + // Convert FileList to array and sort + const sortedFilesArray = Array.from(document.getElementById("fileInput-input").files).sort(comparator); - // Refresh displayed list - displayFiles(sortedFilesArray); + // Refresh displayed list + displayFiles(sortedFilesArray); - // Update the files property - const dataTransfer = new DataTransfer(); - sortedFilesArray.forEach(file => dataTransfer.items.add(file)); - document.getElementById("fileInput-input").files = dataTransfer.files; + // Update the files property + const dataTransfer = new DataTransfer(); + sortedFilesArray.forEach((file) => dataTransfer.items.add(file)); + document.getElementById("fileInput-input").files = dataTransfer.files; } function updateFiles() { - var dataTransfer = new DataTransfer(); - var liElements = document.querySelectorAll("#selectedFiles li"); - const files = document.getElementById("fileInput-input").files; + var dataTransfer = new DataTransfer(); + var liElements = document.querySelectorAll("#selectedFiles li"); + const files = document.getElementById("fileInput-input").files; - for (var i = 0; i < liElements.length; i++) { - var fileNameFromList = liElements[i].querySelector(".filename").innerText; - var fileFromFiles; - for (var j = 0; j < files.length; j++) { - var file = files[j]; - if (file.name === fileNameFromList) { - dataTransfer.items.add(file); - break; - } - } + for (var i = 0; i < liElements.length; i++) { + var fileNameFromList = liElements[i].querySelector(".filename").innerText; + var fileFromFiles; + for (var j = 0; j < files.length; j++) { + var file = files[j]; + if (file.name === fileNameFromList) { + dataTransfer.items.add(file); + break; + } } - document.getElementById("fileInput-input").files = dataTransfer.files; + } + document.getElementById("fileInput-input").files = dataTransfer.files; } diff --git a/src/main/resources/static/js/multitool/DragDropManager.js b/src/main/resources/static/js/multitool/DragDropManager.js index 2481adf1..eac92a8c 100644 --- a/src/main/resources/static/js/multitool/DragDropManager.js +++ b/src/main/resources/static/js/multitool/DragDropManager.js @@ -1,125 +1,123 @@ class DragDropManager { - dragContainer; - wrapper; - pageDirection; - movePageTo; - pageDragging; - draggelEl; - draggedImageEl; - hoveredEl; - endInsertionElement; + dragContainer; + wrapper; + pageDirection; + movePageTo; + pageDragging; + draggelEl; + draggedImageEl; + hoveredEl; + endInsertionElement; - constructor(id, wrapperId) { - this.dragContainer = document.getElementById(id); - this.pageDirection = document.documentElement.getAttribute("lang-direction"); - this.wrapper = document.getElementById(wrapperId); - this.pageDragging = false; - this.hoveredEl = undefined; - this.draggelEl = undefined - this.draggedImageEl = undefined; + constructor(id, wrapperId) { + this.dragContainer = document.getElementById(id); + this.pageDirection = document.documentElement.getAttribute("dir"); + this.wrapper = document.getElementById(wrapperId); + this.pageDragging = false; + this.hoveredEl = undefined; + this.draggelEl = undefined; + this.draggedImageEl = undefined; - var styleElement = document.createElement('link'); - styleElement.rel = 'stylesheet'; - styleElement.href = 'css/dragdrop.css' + var styleElement = document.createElement("link"); + styleElement.rel = "stylesheet"; + styleElement.href = "css/dragdrop.css"; - document.head.appendChild(styleElement); + document.head.appendChild(styleElement); - const div = document.createElement('div'); - div.classList.add('drag-manager_endpoint'); - div.innerHTML = ` + const div = document.createElement("div"); + div.classList.add("drag-manager_endpoint"); + div.innerHTML = ` - ` - this.endInsertionElement = div; + `; + this.endInsertionElement = div; - this.startDraggingPage = this.startDraggingPage.bind(this); - this.onDragEl = this.onDragEl.bind(this); - this.stopDraggingPage = this.stopDraggingPage.bind(this); + this.startDraggingPage = this.startDraggingPage.bind(this); + this.onDragEl = this.onDragEl.bind(this); + this.stopDraggingPage = this.stopDraggingPage.bind(this); - this.adapt(div); + this.adapt(div); + } + + startDraggingPage(div) { + this.pageDragging = true; + this.draggedEl = div; + const img = div.querySelector("img"); + div.classList.add("drag-manager_dragging"); + const imageSrc = img.src; + + const imgEl = document.createElement("img"); + imgEl.classList.add("dragged-img"); + imgEl.src = imageSrc; + this.draggedImageEl = imgEl; + imgEl.style.visibility = "hidden"; + imgEl.style.transform = `rotate(${img.style.rotate === "" ? "0deg" : img.style.rotate}) translate(-50%, -50%)`; + this.dragContainer.appendChild(imgEl); + + window.addEventListener("mouseup", this.stopDraggingPage); + window.addEventListener("mousemove", this.onDragEl); + this.wrapper.classList.add("drag-manager_dragging-container"); + this.wrapper.appendChild(this.endInsertionElement); + } + + onDragEl(mouseEvent) { + const { clientX, clientY } = mouseEvent; + if (this.draggedImageEl) { + this.draggedImageEl.style.visibility = "visible"; + this.draggedImageEl.style.left = `${clientX}px`; + this.draggedImageEl.style.top = `${clientY}px`; } + } - startDraggingPage(div,) { - this.pageDragging = true; - this.draggedEl = div; - const img = div.querySelector('img'); - div.classList.add('drag-manager_dragging'); - const imageSrc = img.src; - - const imgEl = document.createElement('img'); - imgEl.classList.add('dragged-img'); - imgEl.src = imageSrc; - this.draggedImageEl = imgEl; - imgEl.style.visibility = 'hidden'; - imgEl.style.transform = `rotate(${img.style.rotate === '' ? '0deg' : img.style.rotate}) translate(-50%, -50%)`; - this.dragContainer.appendChild(imgEl); - - window.addEventListener('mouseup', this.stopDraggingPage) - window.addEventListener('mousemove', this.onDragEl) - this.wrapper.classList.add('drag-manager_dragging-container'); - this.wrapper.appendChild(this.endInsertionElement); + stopDraggingPage() { + window.removeEventListener("mousemove", this.onDragEl); + this.wrapper.classList.remove("drag-manager_dragging-container"); + this.wrapper.removeChild(this.endInsertionElement); + window.removeEventListener("mouseup", this.stopDraggingPage); + this.draggedImageEl = undefined; + this.pageDragging = false; + this.draggedEl.classList.remove("drag-manager_dragging"); + this.hoveredEl?.classList.remove("drag-manager_draghover"); + this.dragContainer.childNodes.forEach((dragChild) => { + this.dragContainer.removeChild(dragChild); + }); + if (!this.hoveredEl) { + return; } - - onDragEl(mouseEvent) { - const { clientX, clientY } = mouseEvent; - if(this.draggedImageEl) { - this.draggedImageEl.style.visibility = 'visible'; - this.draggedImageEl.style.left = `${clientX}px`; - this.draggedImageEl.style.top = `${clientY}px`; - } + if (this.hoveredEl === this.endInsertionElement) { + this.movePageTo(this.draggedEl); + return; } + this.movePageTo(this.draggedEl, this.hoveredEl); + } + setActions({ movePageTo }) { + this.movePageTo = movePageTo; + } - stopDraggingPage() { - window.removeEventListener('mousemove', this.onDragEl); - this.wrapper.classList.remove('drag-manager_dragging-container'); - this.wrapper.removeChild(this.endInsertionElement); - window.removeEventListener('mouseup', this.stopDraggingPage) - this.draggedImageEl = undefined; - this.pageDragging = false; - this.draggedEl.classList.remove('drag-manager_dragging'); - this.hoveredEl?.classList.remove('drag-manager_draghover'); - this.dragContainer.childNodes.forEach((dragChild) => { - this.dragContainer.removeChild(dragChild); - }) - if(!this.hoveredEl) { - return; - } - if(this.hoveredEl === this.endInsertionElement) { - this.movePageTo(this.draggedEl); - return; - } - this.movePageTo(this.draggedEl, this.hoveredEl); - } + adapt(div) { + const onDragStart = () => { + this.startDraggingPage(div); + }; - setActions({ movePageTo }) { - this.movePageTo = movePageTo; - } + const onMouseEnter = () => { + if (this.pageDragging) { + this.hoveredEl = div; + div.classList.add("drag-manager_draghover"); + } + }; + const onMouseLeave = () => { + this.hoveredEl = undefined; + div.classList.remove("drag-manager_draghover"); + }; - adapt(div) { - const onDragStart = () => { - this.startDraggingPage(div); - } + div.addEventListener("dragstart", onDragStart); + div.addEventListener("mouseenter", onMouseEnter); + div.addEventListener("mouseleave", onMouseLeave); - const onMouseEnter = () => { - if (this.pageDragging) { - this.hoveredEl = div; - div.classList.add('drag-manager_draghover'); - } - } - - const onMouseLeave = () => { - this.hoveredEl = undefined - div.classList.remove('drag-manager_draghover'); - } - - div.addEventListener('dragstart', onDragStart); - div.addEventListener('mouseenter', onMouseEnter); - div.addEventListener('mouseleave', onMouseLeave); - - return div; - } + return div; + } } export default DragDropManager; diff --git a/src/main/resources/static/js/multitool/ImageHighlighter.js b/src/main/resources/static/js/multitool/ImageHighlighter.js index 6f7cd22e..b72df3bb 100644 --- a/src/main/resources/static/js/multitool/ImageHighlighter.js +++ b/src/main/resources/static/js/multitool/ImageHighlighter.js @@ -1,46 +1,46 @@ class ImageHiglighter { - imageHighlighter; - constructor(id) { - this.imageHighlighter = document.getElementById(id); - this.imageHighlightCallback = this.imageHighlightCallback.bind(this); + imageHighlighter; + constructor(id) { + this.imageHighlighter = document.getElementById(id); + this.imageHighlightCallback = this.imageHighlightCallback.bind(this); - var styleElement = document.createElement('link'); - styleElement.rel = 'stylesheet'; - styleElement.href = 'css/imageHighlighter.css' + var styleElement = document.createElement("link"); + styleElement.rel = "stylesheet"; + styleElement.href = "css/imageHighlighter.css"; - document.head.appendChild(styleElement); + document.head.appendChild(styleElement); - this.imageHighlighter.onclick = () => { - this.imageHighlighter.childNodes.forEach((child) => { - child.classList.add('remove'); - setTimeout(() => { - this.imageHighlighter.removeChild(child); - }, 100) - }) - } - } - - imageHighlightCallback(highlightEvent) { - var bigImg = document.createElement('img'); - bigImg.onclick = (imageClickEvent) => { - // This prevents the highlighter's onClick from closing the image when clicking - // on the image instead of next to it. - imageClickEvent.preventDefault(); - imageClickEvent.stopPropagation(); - }; - bigImg.src = highlightEvent.target.src; - this.imageHighlighter.appendChild(bigImg); + this.imageHighlighter.onclick = () => { + this.imageHighlighter.childNodes.forEach((child) => { + child.classList.add("remove"); + setTimeout(() => { + this.imageHighlighter.removeChild(child); + }, 100); + }); }; + } - setActions() { - // not needed in this case - } + imageHighlightCallback(highlightEvent) { + var bigImg = document.createElement("img"); + bigImg.onclick = (imageClickEvent) => { + // This prevents the highlighter's onClick from closing the image when clicking + // on the image instead of next to it. + imageClickEvent.preventDefault(); + imageClickEvent.stopPropagation(); + }; + bigImg.src = highlightEvent.target.src; + this.imageHighlighter.appendChild(bigImg); + } - adapt(div) { - const img = div.querySelector('.page-image'); - img.addEventListener('click', this.imageHighlightCallback) - return div; - } + setActions() { + // not needed in this case + } + + adapt(div) { + const img = div.querySelector(".page-image"); + img.addEventListener("click", this.imageHighlightCallback); + return div; + } } -export default ImageHiglighter; \ No newline at end of file +export default ImageHiglighter; diff --git a/src/main/resources/static/js/multitool/PdfActionsManager.js b/src/main/resources/static/js/multitool/PdfActionsManager.js index b77d2121..163dff3a 100644 --- a/src/main/resources/static/js/multitool/PdfActionsManager.js +++ b/src/main/resources/static/js/multitool/PdfActionsManager.js @@ -1,198 +1,200 @@ class PdfActionsManager { - pageDirection; - pagesContainer; + pageDirection; + pagesContainer; - constructor(id) { - this.pagesContainer = document.getElementById(id); - this.pageDirection = document.documentElement.getAttribute("lang-direction"); + constructor(id) { + this.pagesContainer = document.getElementById(id); + this.pageDirection = document.documentElement.getAttribute("dir"); - var styleElement = document.createElement('link'); - styleElement.rel = 'stylesheet'; - styleElement.href = 'css/pdfActions.css' + var styleElement = document.createElement("link"); + styleElement.rel = "stylesheet"; + styleElement.href = "css/pdfActions.css"; - document.head.appendChild(styleElement); + document.head.appendChild(styleElement); + } + + getPageContainer(element) { + var container = element; + while (!container.classList.contains("page-container")) { + container = container.parentNode; } + return container; + } - getPageContainer(element) { - var container = element - while (!container.classList.contains('page-container')) { - container = container.parentNode; - } - return container; + moveUpButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + + const sibling = imgContainer.previousSibling; + if (sibling) { + this.movePageTo(imgContainer, sibling, true); } + } - moveUpButtonCallback(e) { - var imgContainer = this.getPageContainer(e.target); - - const sibling = imgContainer.previousSibling; - if (sibling) { - this.movePageTo(imgContainer, sibling, true); - } + moveDownButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + const sibling = imgContainer.nextSibling; + if (sibling) { + this.movePageTo(imgContainer, sibling.nextSibling, true); } + } - moveDownButtonCallback(e) { - var imgContainer = this.getPageContainer(e.target); - const sibling = imgContainer.nextSibling; - if (sibling) { - this.movePageTo(imgContainer, sibling.nextSibling, true); - } - }; + rotateCCWButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + const img = imgContainer.querySelector("img"); - rotateCCWButtonCallback(e) { - var imgContainer = this.getPageContainer(e.target); - const img = imgContainer.querySelector("img"); + this.rotateElement(img, -90); + } - this.rotateElement(img, -90) - }; + rotateCWButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + const img = imgContainer.querySelector("img"); - rotateCWButtonCallback(e) { - var imgContainer = this.getPageContainer(e.target); - const img = imgContainer.querySelector("img"); + this.rotateElement(img, 90); + } - this.rotateElement(img, 90) - }; + deletePageButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + this.pagesContainer.removeChild(imgContainer); + if (this.pagesContainer.childElementCount === 0) { + const filenameInput = document.getElementById("filename-input"); + const filenameParagraph = document.getElementById("filename"); + const downloadBtn = document.getElementById("export-button"); - deletePageButtonCallback(e) { - var imgContainer = this.getPageContainer(e.target); - this.pagesContainer.removeChild(imgContainer); - if (this.pagesContainer.childElementCount === 0) { - const filenameInput = document.getElementById('filename-input'); - const filenameParagraph = document.getElementById('filename'); - const downloadBtn = document.getElementById('export-button'); + filenameInput.disabled = true; + filenameInput.value = ""; + filenameParagraph.innerText = ""; - filenameInput.disabled = true; - filenameInput.value = ""; - filenameParagraph.innerText = ""; - - downloadBtn.disabled = true; - } - }; - - insertFileButtonCallback(e) { - var imgContainer = this.getPageContainer(e.target); - this.addPdfs(imgContainer) - }; - - setActions({ movePageTo, addPdfs, rotateElement }) { - this.movePageTo = movePageTo; - this.addPdfs = addPdfs; - this.rotateElement = rotateElement; - - this.moveUpButtonCallback = this.moveUpButtonCallback.bind(this); - this.moveDownButtonCallback = this.moveDownButtonCallback.bind(this); - this.rotateCCWButtonCallback = this.rotateCCWButtonCallback.bind(this); - this.rotateCWButtonCallback = this.rotateCWButtonCallback.bind(this); - this.deletePageButtonCallback = this.deletePageButtonCallback.bind(this); - this.insertFileButtonCallback = this.insertFileButtonCallback.bind(this); + downloadBtn.disabled = true; } + } - adapt(div) { - div.classList.add('pdf-actions_container'); - const leftDirection = this.pageDirection === 'rtl' ? 'right' : 'left' - const rightDirection = this.pageDirection === 'rtl' ? 'left' : 'right' - const buttonContainer = document.createElement('div'); + insertFileButtonCallback(e) { + var imgContainer = this.getPageContainer(e.target); + this.addPdfs(imgContainer); + } - buttonContainer.classList.add("pdf-actions_button-container", "hide-on-drag"); + setActions({ movePageTo, addPdfs, rotateElement }) { + this.movePageTo = movePageTo; + this.addPdfs = addPdfs; + this.rotateElement = rotateElement; - const moveUp = document.createElement('button'); - moveUp.classList.add("pdf-actions_move-left-button","btn", "btn-secondary"); - moveUp.innerHTML = ``; - moveUp.onclick = this.moveUpButtonCallback; - buttonContainer.appendChild(moveUp); + this.moveUpButtonCallback = this.moveUpButtonCallback.bind(this); + this.moveDownButtonCallback = this.moveDownButtonCallback.bind(this); + this.rotateCCWButtonCallback = this.rotateCCWButtonCallback.bind(this); + this.rotateCWButtonCallback = this.rotateCWButtonCallback.bind(this); + this.deletePageButtonCallback = this.deletePageButtonCallback.bind(this); + this.insertFileButtonCallback = this.insertFileButtonCallback.bind(this); + } - const moveDown = document.createElement('button'); - moveDown.classList.add("pdf-actions_move-right-button","btn", "btn-secondary"); - moveDown.innerHTML = ``; - moveDown.onclick = this.moveDownButtonCallback; - buttonContainer.appendChild(moveDown); + adapt(div) { + div.classList.add("pdf-actions_container"); + const leftDirection = this.pageDirection === "rtl" ? "right" : "left"; + const rightDirection = this.pageDirection === "rtl" ? "left" : "right"; + const buttonContainer = document.createElement("div"); - const rotateCCW = document.createElement('button'); - rotateCCW.classList.add("btn", "btn-secondary"); - rotateCCW.innerHTML = ` + buttonContainer.classList.add("pdf-actions_button-container", "hide-on-drag"); + + const moveUp = document.createElement("button"); + moveUp.classList.add("pdf-actions_move-left-button", "btn", "btn-secondary"); + moveUp.innerHTML = ``; + moveUp.onclick = this.moveUpButtonCallback; + buttonContainer.appendChild(moveUp); + + const moveDown = document.createElement("button"); + moveDown.classList.add("pdf-actions_move-right-button", "btn", "btn-secondary"); + moveDown.innerHTML = ``; + moveDown.onclick = this.moveDownButtonCallback; + buttonContainer.appendChild(moveDown); + + const rotateCCW = document.createElement("button"); + rotateCCW.classList.add("btn", "btn-secondary"); + rotateCCW.innerHTML = ` `; - rotateCCW.onclick = this.rotateCCWButtonCallback; - buttonContainer.appendChild(rotateCCW); + rotateCCW.onclick = this.rotateCCWButtonCallback; + buttonContainer.appendChild(rotateCCW); - const rotateCW = document.createElement('button'); - rotateCW.classList.add("btn", "btn-secondary"); - rotateCW.innerHTML = ` + const rotateCW = document.createElement("button"); + rotateCW.classList.add("btn", "btn-secondary"); + rotateCW.innerHTML = ` `; - rotateCW.onclick = this.rotateCWButtonCallback; - buttonContainer.appendChild(rotateCW); + rotateCW.onclick = this.rotateCWButtonCallback; + buttonContainer.appendChild(rotateCW); - const deletePage = document.createElement('button'); - deletePage.classList.add("btn", "btn-danger"); - deletePage.innerHTML = ` + const deletePage = document.createElement("button"); + deletePage.classList.add("btn", "btn-danger"); + deletePage.innerHTML = ` `; - deletePage.onclick = this.deletePageButtonCallback; - buttonContainer.appendChild(deletePage); + deletePage.onclick = this.deletePageButtonCallback; + buttonContainer.appendChild(deletePage); - div.appendChild(buttonContainer); + div.appendChild(buttonContainer); - const insertFileButtonContainer = document.createElement('div'); + const insertFileButtonContainer = document.createElement("div"); - insertFileButtonContainer.classList.add( - "pdf-actions_insert-file-button-container", - leftDirection, - `align-center-${leftDirection}`); + insertFileButtonContainer.classList.add( + "pdf-actions_insert-file-button-container", + leftDirection, + `align-center-${leftDirection}`, + ); - const insertFileButton = document.createElement('button'); - insertFileButton.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button"); - insertFileButton.innerHTML = ` + const insertFileButton = document.createElement("button"); + insertFileButton.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button"); + insertFileButton.innerHTML = ` `; - insertFileButton.onclick = this.insertFileButtonCallback; - insertFileButtonContainer.appendChild(insertFileButton); + insertFileButton.onclick = this.insertFileButtonCallback; + insertFileButtonContainer.appendChild(insertFileButton); - div.appendChild(insertFileButtonContainer); + div.appendChild(insertFileButtonContainer); - // add this button to every element, but only show it on the last one :D - const insertFileButtonRightContainer = document.createElement('div'); - insertFileButtonRightContainer.classList.add( - "pdf-actions_insert-file-button-container", - rightDirection, - `align-center-${rightDirection}`); + // add this button to every element, but only show it on the last one :D + const insertFileButtonRightContainer = document.createElement("div"); + insertFileButtonRightContainer.classList.add( + "pdf-actions_insert-file-button-container", + rightDirection, + `align-center-${rightDirection}`, + ); - const insertFileButtonRight = document.createElement('button'); - insertFileButtonRight.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button"); - insertFileButtonRight.innerHTML = ` + const insertFileButtonRight = document.createElement("button"); + insertFileButtonRight.classList.add("btn", "btn-primary", "pdf-actions_insert-file-button"); + insertFileButtonRight.innerHTML = ` insertFileButtonRight`; - insertFileButtonRight.onclick = () => addPdfs(); - insertFileButtonRightContainer.appendChild(insertFileButtonRight); + insertFileButtonRight.onclick = () => addPdfs(); + insertFileButtonRightContainer.appendChild(insertFileButtonRight); - div.appendChild(insertFileButtonRightContainer); + div.appendChild(insertFileButtonRightContainer); - const adaptPageNumber = (pageNumber, div) => { - const pageNumberElement = document.createElement('span'); - pageNumberElement.classList.add('page-number'); - pageNumberElement.textContent = pageNumber; + const adaptPageNumber = (pageNumber, div) => { + const pageNumberElement = document.createElement("span"); + pageNumberElement.classList.add("page-number"); + pageNumberElement.textContent = pageNumber; - div.insertBefore(pageNumberElement, div.firstChild); - }; + div.insertBefore(pageNumberElement, div.firstChild); + }; - div.addEventListener('mouseenter', () => { - const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1; - adaptPageNumber(pageNumber, div); - }); + div.addEventListener("mouseenter", () => { + const pageNumber = Array.from(div.parentNode.children).indexOf(div) + 1; + adaptPageNumber(pageNumber, div); + }); - div.addEventListener('mouseleave', () => { - const pageNumberElement = div.querySelector('.page-number'); - if (pageNumberElement) { - div.removeChild(pageNumberElement); - } - }); + div.addEventListener("mouseleave", () => { + const pageNumberElement = div.querySelector(".page-number"); + if (pageNumberElement) { + div.removeChild(pageNumberElement); + } + }); - return div; - } + return div; + } } -export default PdfActionsManager; \ No newline at end of file +export default PdfActionsManager; diff --git a/src/main/resources/static/js/multitool/PdfContainer.js b/src/main/resources/static/js/multitool/PdfContainer.js index 1823cff4..bb76d409 100644 --- a/src/main/resources/static/js/multitool/PdfContainer.js +++ b/src/main/resources/static/js/multitool/PdfContainer.js @@ -1,285 +1,284 @@ class PdfContainer { - fileName; - pagesContainer; - pagesContainerWrapper; - pdfAdapters; - downloadLink; + fileName; + pagesContainer; + pagesContainerWrapper; + pdfAdapters; + downloadLink; - constructor(id, wrapperId, pdfAdapters) { - this.pagesContainer = document.getElementById(id) - this.pagesContainerWrapper = document.getElementById(wrapperId); - this.downloadLink = null; - this.movePageTo = this.movePageTo.bind(this); - this.addPdfs = this.addPdfs.bind(this); - this.addPdfsFromFiles = this.addPdfsFromFiles.bind(this); - this.rotateElement = this.rotateElement.bind(this); - this.rotateAll = this.rotateAll.bind(this); - this.exportPdf = this.exportPdf.bind(this); - this.updateFilename = this.updateFilename.bind(this); - this.setDownloadAttribute = this.setDownloadAttribute.bind(this); - this.preventIllegalChars = this.preventIllegalChars.bind(this); + constructor(id, wrapperId, pdfAdapters) { + this.pagesContainer = document.getElementById(id); + this.pagesContainerWrapper = document.getElementById(wrapperId); + this.downloadLink = null; + this.movePageTo = this.movePageTo.bind(this); + this.addPdfs = this.addPdfs.bind(this); + this.addPdfsFromFiles = this.addPdfsFromFiles.bind(this); + this.rotateElement = this.rotateElement.bind(this); + this.rotateAll = this.rotateAll.bind(this); + this.exportPdf = this.exportPdf.bind(this); + this.updateFilename = this.updateFilename.bind(this); + this.setDownloadAttribute = this.setDownloadAttribute.bind(this); + this.preventIllegalChars = this.preventIllegalChars.bind(this); - this.pdfAdapters = pdfAdapters; + this.pdfAdapters = pdfAdapters; - this.pdfAdapters.forEach(adapter => { - adapter.setActions({ - movePageTo: this.movePageTo, - addPdfs: this.addPdfs, - rotateElement: this.rotateElement, - updateFilename: this.updateFilename - }) - }) + this.pdfAdapters.forEach((adapter) => { + adapter.setActions({ + movePageTo: this.movePageTo, + addPdfs: this.addPdfs, + rotateElement: this.rotateElement, + updateFilename: this.updateFilename, + }); + }); - window.addPdfs = this.addPdfs; - window.exportPdf = this.exportPdf; - window.rotateAll = this.rotateAll; + window.addPdfs = this.addPdfs; + window.exportPdf = this.exportPdf; + window.rotateAll = this.rotateAll; - const filenameInput = document.getElementById('filename-input'); - const downloadBtn = document.getElementById('export-button'); + const filenameInput = document.getElementById("filename-input"); + const downloadBtn = document.getElementById("export-button"); - filenameInput.onkeyup = this.updateFilename; - filenameInput.onkeydown = this.preventIllegalChars; - filenameInput.disabled = false; - filenameInput.innerText = ""; - downloadBtn.disabled = true; + filenameInput.onkeyup = this.updateFilename; + filenameInput.onkeydown = this.preventIllegalChars; + filenameInput.disabled = false; + filenameInput.innerText = ""; + downloadBtn.disabled = true; + } + + movePageTo(startElement, endElement, scrollTo = false) { + const childArray = Array.from(this.pagesContainer.childNodes); + const startIndex = childArray.indexOf(startElement); + const endIndex = childArray.indexOf(endElement); + this.pagesContainer.removeChild(startElement); + if (!endElement) { + this.pagesContainer.append(startElement); + } else { + this.pagesContainer.insertBefore(startElement, endElement); } - movePageTo(startElement, endElement, scrollTo = false) { - const childArray = Array.from(this.pagesContainer.childNodes); - const startIndex = childArray.indexOf(startElement); - const endIndex = childArray.indexOf(endElement); - this.pagesContainer.removeChild(startElement); - if(!endElement) { - this.pagesContainer.append(startElement); + if (scrollTo) { + const { width } = startElement.getBoundingClientRect(); + const vector = endIndex !== -1 && startIndex > endIndex ? 0 - width : width; + + this.pagesContainerWrapper.scroll({ + left: this.pagesContainerWrapper.scrollLeft + vector, + }); + } + } + + addPdfs(nextSiblingElement) { + var input = document.createElement("input"); + input.type = "file"; + input.multiple = true; + input.setAttribute("accept", "application/pdf"); + input.onchange = async (e) => { + const files = e.target.files; + + this.addPdfsFromFiles(files, nextSiblingElement); + this.updateFilename(files ? files[0].name : ""); + }; + + input.click(); + } + + async addPdfsFromFiles(files, nextSiblingElement) { + this.fileName = files[0].name; + for (var i = 0; i < files.length; i++) { + await this.addPdfFile(files[i], nextSiblingElement); + } + + document.querySelectorAll(".enable-on-file").forEach((element) => { + element.disabled = false; + }); + } + + rotateElement(element, deg) { + var lastTransform = element.style.rotate; + if (!lastTransform) { + lastTransform = "0"; + } + const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, "")); + const newAngle = lastAngle + deg; + + element.style.rotate = newAngle + "deg"; + } + + async addPdfFile(file, nextSiblingElement) { + const { renderer, pdfDocument } = await this.loadFile(file); + + for (var i = 0; i < renderer.pageCount; i++) { + const div = document.createElement("div"); + + div.classList.add("page-container"); + + var img = document.createElement("img"); + img.classList.add("page-image"); + const imageSrc = await renderer.renderPage(i); + img.src = imageSrc; + img.pageIdx = i; + img.rend = renderer; + img.doc = pdfDocument; + div.appendChild(img); + + this.pdfAdapters.forEach((adapter) => { + adapter.adapt?.(div); + }); + if (nextSiblingElement) { + this.pagesContainer.insertBefore(div, nextSiblingElement); + } else { + this.pagesContainer.appendChild(div); + } + } + } + + async loadFile(file) { + var objectUrl = URL.createObjectURL(file); + var pdfDocument = await this.toPdfLib(objectUrl); + var renderer = await this.toRenderer(objectUrl); + return { renderer, pdfDocument }; + } + + async toRenderer(objectUrl) { + pdfjsLib.GlobalWorkerOptions.workerSrc = "pdfjs/pdf.worker.js"; + const pdf = await pdfjsLib.getDocument(objectUrl).promise; + return { + document: pdf, + pageCount: pdf.numPages, + renderPage: async function (pageIdx) { + const page = await this.document.getPage(pageIdx + 1); + + const canvas = document.createElement("canvas"); + + // set the canvas size to the size of the page + if (page.rotate == 90 || page.rotate == 270) { + canvas.width = page.view[3]; + canvas.height = page.view[2]; } else { - this.pagesContainer.insertBefore(startElement, endElement); + canvas.width = page.view[2]; + canvas.height = page.view[3]; } - if(scrollTo) { - const { width } = startElement.getBoundingClientRect(); - const vector = (endIndex !== -1 && startIndex > endIndex) - ? 0-width - : width; - - this.pagesContainerWrapper.scroll({ - left: this.pagesContainerWrapper.scrollLeft + vector, - }) - } - } - - addPdfs(nextSiblingElement) { - var input = document.createElement('input'); - input.type = 'file'; - input.multiple = true; - input.setAttribute("accept", "application/pdf"); - input.onchange = async(e) => { - const files = e.target.files; - - this.addPdfsFromFiles(files, nextSiblingElement); - this.updateFilename(files ? files[0].name : ""); - } - - input.click(); - } - - async addPdfsFromFiles(files, nextSiblingElement) { - this.fileName = files[0].name; - for (var i=0; i < files.length; i++) { - await this.addPdfFile(files[i], nextSiblingElement); - } - - document.querySelectorAll(".enable-on-file").forEach(element => { - element.disabled = false; - }); - } - - rotateElement(element, deg) { - var lastTransform = element.style.rotate; - if (!lastTransform) { - lastTransform = "0"; - } - const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, '')); - const newAngle = lastAngle + deg; - - element.style.rotate = newAngle + "deg"; - } - - async addPdfFile(file, nextSiblingElement) { - const { renderer, pdfDocument } = await this.loadFile(file); - - for (var i=0; i < renderer.pageCount; i++) { - const div = document.createElement('div'); - - div.classList.add("page-container"); - - var img = document.createElement('img'); - img.classList.add('page-image') - const imageSrc = await renderer.renderPage(i) - img.src = imageSrc; - img.pageIdx = i; - img.rend = renderer; - img.doc = pdfDocument; - div.appendChild(img); - - this.pdfAdapters.forEach((adapter) => { - adapter.adapt?.(div) - }) - if (nextSiblingElement) { - this.pagesContainer.insertBefore(div, nextSiblingElement); - } else { - this.pagesContainer.appendChild(div); - } - } - } - - async loadFile(file) { - var objectUrl = URL.createObjectURL(file); - var pdfDocument = await this.toPdfLib(objectUrl); - var renderer = await this.toRenderer(objectUrl); - return { renderer, pdfDocument }; - } - - async toRenderer(objectUrl) { - pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdfjs/pdf.worker.js' - const pdf = await pdfjsLib.getDocument(objectUrl).promise; - return { - document: pdf, - pageCount: pdf.numPages, - renderPage: async function(pageIdx) { - const page = await this.document.getPage(pageIdx+1); - - const canvas = document.createElement("canvas"); - - // set the canvas size to the size of the page - if (page.rotate == 90 || page.rotate == 270) { - canvas.width = page.view[3]; - canvas.height = page.view[2]; - } else { - canvas.width = page.view[2]; - canvas.height = page.view[3]; - } - - // render the page onto the canvas - var renderContext = { - canvasContext: canvas.getContext("2d"), - viewport: page.getViewport({ scale: 1 }) - }; - - await page.render(renderContext).promise; - return canvas.toDataURL(); - } + // render the page onto the canvas + var renderContext = { + canvasContext: canvas.getContext("2d"), + viewport: page.getViewport({ scale: 1 }), }; + + await page.render(renderContext).promise; + return canvas.toDataURL(); + }, + }; + } + + async toPdfLib(objectUrl) { + const existingPdfBytes = await fetch(objectUrl).then((res) => res.arrayBuffer()); + const pdfDoc = await PDFLib.PDFDocument.load(existingPdfBytes, { + ignoreEncryption: true, + }); + return pdfDoc; + } + + rotateAll(deg) { + for (var i = 0; i < this.pagesContainer.childNodes.length; i++) { + const child = this.pagesContainer.children[i]; + if (!child) continue; + const img = child.querySelector("img"); + if (!img) continue; + this.rotateElement(img, deg); + } + } + + async exportPdf() { + const pdfDoc = await PDFLib.PDFDocument.create(); + const pageContainers = this.pagesContainer.querySelectorAll(".page-container"); // Select all .page-container elements + for (var i = 0; i < pageContainers.length; i++) { + const img = pageContainers[i].querySelector("img"); // Find the img element within each .page-container + if (!img) continue; + const pages = await pdfDoc.copyPages(img.doc, [img.pageIdx]); + const page = pages[0]; + + const rotation = img.style.rotate; + if (rotation) { + const rotationAngle = parseInt(rotation.replace(/[^\d-]/g, "")); + page.setRotation(PDFLib.degrees(page.getRotation().angle + rotationAngle)); + } + + pdfDoc.addPage(page); + } + const pdfBytes = await pdfDoc.save(); + const pdfBlob = new Blob([pdfBytes], { type: "application/pdf" }); + const url = URL.createObjectURL(pdfBlob); + const downloadOption = localStorage.getItem("downloadOption"); + + const filenameInput = document.getElementById("filename-input"); + + let inputArr = filenameInput.value.split("."); + + if (inputArr !== null && inputArr !== undefined && inputArr.length > 0) { + inputArr = inputArr.filter((n) => n); // remove all empty strings, nulls or undefined + + if (inputArr.length > 1) { + inputArr.pop(); // remove right part after last dot + } + + filenameInput.value = inputArr.join(""); + this.fileName = filenameInput.value; } - async toPdfLib(objectUrl) { - const existingPdfBytes = await fetch(objectUrl).then(res => res.arrayBuffer()); - const pdfDoc = await PDFLib.PDFDocument.load(existingPdfBytes, { ignoreEncryption: true }); - return pdfDoc; + if (!filenameInput.value.includes(".pdf")) { + filenameInput.value = filenameInput.value + ".pdf"; + this.fileName = filenameInput.value; } + if (downloadOption === "sameWindow") { + // Open the file in the same window + window.location.href = url; + } else if (downloadOption === "newWindow") { + // Open the file in a new window + window.open(url, "_blank"); + } else { + // Download the file + this.downloadLink = document.createElement("a"); + this.downloadLink.id = "download-link"; + this.downloadLink.href = url; + // downloadLink.download = this.fileName ? this.fileName : 'managed.pdf'; + // downloadLink.download = this.fileName; + this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf"); + this.downloadLink.setAttribute("target", "_blank"); + this.downloadLink.onclick = this.setDownloadAttribute; + this.downloadLink.click(); + } + } + setDownloadAttribute() { + this.downloadLink.setAttribute("download", this.fileName ? this.fileName : "managed.pdf"); + } - rotateAll(deg) { - for (var i=0; i 0) { - - inputArr = inputArr.filter(n => n); // remove all empty strings, nulls or undefined - - if (inputArr.length > 1) { - inputArr.pop(); // remove right part after last dot - } - - filenameInput.value = inputArr.join(''); - this.fileName = filenameInput.value; - } - - if (!filenameInput.value.includes('.pdf')) { - filenameInput.value = filenameInput.value + '.pdf'; - this.fileName = filenameInput.value; - } - - if (downloadOption === 'sameWindow') { - // Open the file in the same window - window.location.href = url; - } else if (downloadOption === 'newWindow') { - // Open the file in a new window - window.open(url, '_blank'); - } else { - // Download the file - this.downloadLink = document.createElement('a'); - this.downloadLink.id = 'download-link'; - this.downloadLink.href = url; - // downloadLink.download = this.fileName ? this.fileName : 'managed.pdf'; - // downloadLink.download = this.fileName; - this.downloadLink.setAttribute('download', this.fileName ? this.fileName : 'managed.pdf'); - this.downloadLink.setAttribute('target', '_blank'); - this.downloadLink.onclick = this.setDownloadAttribute; - this.downloadLink.click(); - } + if (!filenameInput.value) { + filenameInput.value = this.fileName; } + } - setDownloadAttribute() { - this.downloadLink.setAttribute("download", this.fileName ? this.fileName : 'managed.pdf'); - } - - updateFilename(fileName = "") { - const filenameInput = document.getElementById('filename-input'); - const pagesContainer = document.getElementById('pages-container'); - const downloadBtn = document.getElementById('export-button'); - - downloadBtn.disabled = pagesContainer.childElementCount === 0 - - if (!this.fileName) { - this.fileName = fileName; - } - - if (!filenameInput.value) { - filenameInput.value = this.fileName; - } - } - - preventIllegalChars(e) { - // const filenameInput = document.getElementById('filename-input'); - // - // filenameInput.value = filenameInput.value.replace('.pdf', ''); - // - // // prevent . - // if (filenameInput.value.includes('.')) { - // filenameInput.value.replace('.',''); - // } - } + preventIllegalChars(e) { + // const filenameInput = document.getElementById('filename-input'); + // + // filenameInput.value = filenameInput.value.replace('.pdf', ''); + // + // // prevent . + // if (filenameInput.value.includes('.')) { + // filenameInput.value.replace('.',''); + // } + } } export default PdfContainer; diff --git a/src/main/resources/static/js/multitool/fileInput.js b/src/main/resources/static/js/multitool/fileInput.js index ecaf64ed..77455c06 100644 --- a/src/main/resources/static/js/multitool/fileInput.js +++ b/src/main/resources/static/js/multitool/fileInput.js @@ -1,94 +1,95 @@ class FileDragManager { - overlay; - dragCounter; - updateFilename; + overlay; + dragCounter; + updateFilename; - constructor(cb = null) { - this.dragCounter = 0; - this.setCallback(cb); + constructor(cb = null) { + this.dragCounter = 0; + this.setCallback(cb); - // Prevent default behavior for drag events - ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { - document.body.addEventListener(eventName, preventDefaults, false); - }); + // Prevent default behavior for drag events + ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => { + document.body.addEventListener(eventName, preventDefaults, false); + }); - function preventDefaults(e) { - e.preventDefault(); - e.stopPropagation(); - } - - this.dragenterListener = this.dragenterListener.bind(this); - this.dragleaveListener = this.dragleaveListener.bind(this); - this.dropListener = this.dropListener.bind(this); - - document.body.addEventListener('dragenter', this.dragenterListener); - document.body.addEventListener('dragleave', this.dragleaveListener); - // Add drop event listener - document.body.addEventListener('drop', this.dropListener); + function preventDefaults(e) { + e.preventDefault(); + e.stopPropagation(); } - setActions({ updateFilename }) { - this.updateFilename = updateFilename; + this.dragenterListener = this.dragenterListener.bind(this); + this.dragleaveListener = this.dragleaveListener.bind(this); + this.dropListener = this.dropListener.bind(this); + + document.body.addEventListener("dragenter", this.dragenterListener); + document.body.addEventListener("dragleave", this.dragleaveListener); + // Add drop event listener + document.body.addEventListener("drop", this.dropListener); + } + + setActions({ updateFilename }) { + this.updateFilename = updateFilename; + } + + setCallback(cb) { + if (cb) { + this.callback = cb; + } else { + this.callback = (files) => console.warn("FileDragManager not set"); } + } - setCallback(cb) { - if (cb) { - this.callback = cb; - } else { - this.callback = (files) => console.warn("FileDragManager not set"); - } + dragenterListener() { + this.dragCounter++; + if (!this.overlay) { + // Create and show the overlay + this.overlay = document.createElement("div"); + this.overlay.style.position = "fixed"; + this.overlay.style.top = 0; + this.overlay.style.left = 0; + this.overlay.style.width = "100%"; + this.overlay.style.height = "100%"; + this.overlay.style.background = "rgba(0, 0, 0, 0.5)"; + this.overlay.style.color = "#fff"; + this.overlay.style.zIndex = "1000"; + this.overlay.style.display = "flex"; + this.overlay.style.alignItems = "center"; + this.overlay.style.justifyContent = "center"; + this.overlay.style.pointerEvents = "none"; + this.overlay.innerHTML = "

Drop files anywhere to upload

"; + document.getElementById("content-wrap").appendChild(this.overlay); } + } - dragenterListener() { - this.dragCounter++; - if (!this.overlay) { - // Create and show the overlay - this.overlay = document.createElement('div'); - this.overlay.style.position = 'fixed'; - this.overlay.style.top = 0; - this.overlay.style.left = 0; - this.overlay.style.width = '100%'; - this.overlay.style.height = '100%'; - this.overlay.style.background = 'rgba(0, 0, 0, 0.5)'; - this.overlay.style.color = '#fff'; - this.overlay.style.zIndex = '1000'; - this.overlay.style.display = 'flex'; - this.overlay.style.alignItems = 'center'; - this.overlay.style.justifyContent = 'center'; - this.overlay.style.pointerEvents = 'none'; - this.overlay.innerHTML = '

Drop files anywhere to upload

'; - document.getElementById('content-wrap').appendChild(this.overlay); + dragleaveListener() { + this.dragCounter--; + if (this.dragCounter === 0) { + // Hide and remove the overlay + if (this.overlay) { + this.overlay.remove(); + this.overlay = null; + } + } + } + + dropListener(e) { + const dt = e.dataTransfer; + const files = dt.files; + this.callback(files) + .catch((err) => { + console.error(err); + //maybe + }) + .finally(() => { + // Hide and remove the overlay + if (this.overlay) { + this.overlay.remove(); + this.overlay = null; } - }; - dragleaveListener() { - this.dragCounter--; - if (this.dragCounter === 0) { - // Hide and remove the overlay - if (this.overlay) { - this.overlay.remove(); - this.overlay = null; - } - } - }; - - dropListener(e) { - - const dt = e.dataTransfer; - const files = dt.files; - this.callback(files).catch((err) => { - console.error(err); - //maybe - }).finally(() => { - // Hide and remove the overlay - if (this.overlay) { - this.overlay.remove(); - this.overlay = null; - } - - this.updateFilename(files ? files[0].name : ""); - }); - }; + this.updateFilename(files ? files[0].name : ""); + }); + } } export default FileDragManager; diff --git a/src/main/resources/static/js/multitool/horizontalScroll.js b/src/main/resources/static/js/multitool/horizontalScroll.js index 1bbcfed0..2d20fd7b 100644 --- a/src/main/resources/static/js/multitool/horizontalScroll.js +++ b/src/main/resources/static/js/multitool/horizontalScroll.js @@ -1,35 +1,34 @@ const scrollDivHorizontally = (id) => { - var scrollDelta = 0; // variable to store the accumulated scroll delta - var isScrolling = false; // variable to track if scroll is already in progress - const divToScrollHorizontally = document.getElementById(id) - function scrollLoop() { - // Scroll the div horizontally by a fraction of the accumulated scroll delta - divToScrollHorizontally.scrollLeft += scrollDelta * 0.1; + var scrollDelta = 0; // variable to store the accumulated scroll delta + var isScrolling = false; // variable to track if scroll is already in progress + const divToScrollHorizontally = document.getElementById(id); + function scrollLoop() { + // Scroll the div horizontally by a fraction of the accumulated scroll delta + divToScrollHorizontally.scrollLeft += scrollDelta * 0.1; - // Reduce the accumulated scroll delta by a fraction - scrollDelta *= 0.9; + // Reduce the accumulated scroll delta by a fraction + scrollDelta *= 0.9; - // If scroll delta is still significant, continue the scroll loop - if (Math.abs(scrollDelta) > 0.1) { - requestAnimationFrame(scrollLoop); - } else { - isScrolling = false; // Reset scroll in progress flag - } + // If scroll delta is still significant, continue the scroll loop + if (Math.abs(scrollDelta) > 0.1) { + requestAnimationFrame(scrollLoop); + } else { + isScrolling = false; // Reset scroll in progress flag } + } + divToScrollHorizontally.addEventListener("wheel", function (e) { + e.preventDefault(); // prevent default mousewheel behavior - divToScrollHorizontally.addEventListener("wheel", function(e) { - e.preventDefault(); // prevent default mousewheel behavior + // Accumulate the horizontal scroll delta + scrollDelta -= e.deltaX || e.wheelDeltaX || -e.deltaY || -e.wheelDeltaY; - // Accumulate the horizontal scroll delta - scrollDelta -= e.deltaX || e.wheelDeltaX || -e.deltaY || -e.wheelDeltaY; - - // If scroll is not already in progress, start the scroll loop - if (!isScrolling) { - isScrolling = true; - requestAnimationFrame(scrollLoop); - } - }); -} + // If scroll is not already in progress, start the scroll loop + if (!isScrolling) { + isScrolling = true; + requestAnimationFrame(scrollLoop); + } + }); +}; export default scrollDivHorizontally; diff --git a/src/main/resources/static/js/pipeline.js b/src/main/resources/static/js/pipeline.js index e77c4d01..afe1d4d7 100644 --- a/src/main/resources/static/js/pipeline.js +++ b/src/main/resources/static/js/pipeline.js @@ -1,671 +1,681 @@ -document.getElementById('validateButton').addEventListener('click', function(event) { - event.preventDefault(); - validatePipeline(); +document.getElementById("validateButton").addEventListener("click", function (event) { + event.preventDefault(); + validatePipeline(); }); function validatePipeline() { - let pipelineListItems = document.getElementById('pipelineList').children; - let isValid = true; - let containsAddPassword = false; - for (let i = 0; i < pipelineListItems.length - 1; i++) { - let currentOperation = pipelineListItems[i].querySelector('.operationName').textContent; - let nextOperation = pipelineListItems[i + 1].querySelector('.operationName').textContent; - if (currentOperation === '/add-password') { - containsAddPassword = true; - } + let pipelineListItems = document.getElementById("pipelineList").children; + let isValid = true; + let containsAddPassword = false; + for (let i = 0; i < pipelineListItems.length - 1; i++) { + let currentOperation = pipelineListItems[i].querySelector(".operationName").textContent; + let nextOperation = pipelineListItems[i + 1].querySelector(".operationName").textContent; + if (currentOperation === "/add-password") { + containsAddPassword = true; + } - let currentOperationDescription = apiDocs[currentOperation]?.post?.description || ""; - let nextOperationDescription = apiDocs[nextOperation]?.post?.description || ""; + let currentOperationDescription = apiDocs[currentOperation]?.post?.description || ""; + let nextOperationDescription = apiDocs[nextOperation]?.post?.description || ""; - // Strip off 'ZIP-' prefix - currentOperationDescription = currentOperationDescription.replace("ZIP-", ''); - nextOperationDescription = nextOperationDescription.replace("ZIP-", ''); + // Strip off 'ZIP-' prefix + currentOperationDescription = currentOperationDescription.replace("ZIP-", ""); + nextOperationDescription = nextOperationDescription.replace("ZIP-", ""); - let currentOperationOutput = currentOperationDescription.match(/Output:([A-Z\/]*)/)?.[1] || ""; - let nextOperationInput = nextOperationDescription.match(/Input:([A-Z\/]*)/)?.[1] || ""; + let currentOperationOutput = currentOperationDescription.match(/Output:([A-Z\/]*)/)?.[1] || ""; + let nextOperationInput = nextOperationDescription.match(/Input:([A-Z\/]*)/)?.[1] || ""; - // Splitting in case of multiple possible output/input - let currentOperationOutputArr = currentOperationOutput.split('/'); - let nextOperationInputArr = nextOperationInput.split('/'); + // Splitting in case of multiple possible output/input + let currentOperationOutputArr = currentOperationOutput.split("/"); + let nextOperationInputArr = nextOperationInput.split("/"); - if (currentOperationOutput !== 'ANY' && nextOperationInput !== 'ANY') { - let intersection = currentOperationOutputArr.filter(value => nextOperationInputArr.includes(value)); - console.log(`Intersection: ${intersection}`); + if (currentOperationOutput !== "ANY" && nextOperationInput !== "ANY") { + let intersection = currentOperationOutputArr.filter((value) => nextOperationInputArr.includes(value)); + console.log(`Intersection: ${intersection}`); - if (intersection.length === 0) { - updateValidateButton(false); - isValid = false; - console.log(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`); - alert(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`); - break; - } - } - } - if (containsAddPassword && pipelineListItems[pipelineListItems.length - 1].querySelector('.operationName').textContent !== '/add-password') { - updateValidateButton(false); - alert('The "add-password" operation should be at the end of the operations sequence. Please adjust the operations order.'); - return false; - } - if (isValid) { - console.log('Pipeline is valid'); - // Continue with the pipeline operation - } else { - console.error('Pipeline is not valid'); - // Stop operation, maybe display an error to the user - } - updateValidateButton(isValid); - return isValid; + if (intersection.length === 0) { + updateValidateButton(false); + isValid = false; + console.log( + `Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`, + ); + alert( + `Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`, + ); + break; + } + } + } + if ( + containsAddPassword && + pipelineListItems[pipelineListItems.length - 1].querySelector(".operationName").textContent !== "/add-password" + ) { + updateValidateButton(false); + alert('The "add-password" operation should be at the end of the operations sequence. Please adjust the operations order.'); + return false; + } + if (isValid) { + console.log("Pipeline is valid"); + // Continue with the pipeline operation + } else { + console.error("Pipeline is not valid"); + // Stop operation, maybe display an error to the user + } + updateValidateButton(isValid); + return isValid; } function updateValidateButton(isValid) { - var validateButton = document.getElementById('validateButton'); - if (isValid) { - validateButton.classList.remove('btn-danger'); - validateButton.classList.add('btn-success'); - } else { - validateButton.classList.remove('btn-success'); - validateButton.classList.add('btn-danger'); - } + var validateButton = document.getElementById("validateButton"); + if (isValid) { + validateButton.classList.remove("btn-danger"); + validateButton.classList.add("btn-success"); + } else { + validateButton.classList.remove("btn-success"); + validateButton.classList.add("btn-danger"); + } } +document.getElementById("submitConfigBtn").addEventListener("click", function () { + if (validatePipeline() === false) { + return; + } + let selectedOperation = document.getElementById("operationsDropdown").value; + var pipelineName = document.getElementById("pipelineName").value; + let pipelineList = document.getElementById("pipelineList").children; + let pipelineConfig = { + name: pipelineName, + pipeline: [], + _examples: { + outputDir: "{outputFolder}/{folderName}", + outputFileName: "{filename}-{pipelineName}-{date}-{time}", + }, + outputDir: "httpWebRequest", + outputFileName: "{filename}", + }; + for (let i = 0; i < pipelineList.length; i++) { + let operationName = pipelineList[i].querySelector(".operationName").textContent; + let parameters = operationSettings[operationName] || {}; -document.getElementById('submitConfigBtn').addEventListener('click', function() { + pipelineConfig.pipeline.push({ + operation: operationName, + parameters: parameters, + }); + } - if (validatePipeline() === false) { - return; - } - let selectedOperation = document.getElementById('operationsDropdown').value; + let pipelineConfigJson = JSON.stringify(pipelineConfig, null, 2); + let formData = new FormData(); + let fileInput = document.getElementById("fileInput-input"); + let files = fileInput.files; - var pipelineName = document.getElementById('pipelineName').value; - let pipelineList = document.getElementById('pipelineList').children; - let pipelineConfig = { - "name": pipelineName, - "pipeline": [], - "_examples": { - "outputDir": "{outputFolder}/{folderName}", - "outputFileName": "{filename}-{pipelineName}-{date}-{time}" - }, - "outputDir": "httpWebRequest", - "outputFileName": "{filename}" - }; + for (let i = 0; i < files.length; i++) { + console.log("files[i]", files[i].name); + formData.append("fileInput", files[i], files[i].name); + } - for (let i = 0; i < pipelineList.length; i++) { - let operationName = pipelineList[i].querySelector('.operationName').textContent; - let parameters = operationSettings[operationName] || {}; + console.log("pipelineConfigJson", pipelineConfigJson); + formData.append("json", pipelineConfigJson); + console.log("formData", formData); - pipelineConfig.pipeline.push({ - "operation": operationName, - "parameters": parameters - }); - } + fetch("api/v1/pipeline/handleData", { + method: "POST", + body: formData, + }) + .then((response) => { + // Save the response to use it later + const responseToUseLater = response; + return response.blob().then((blob) => { + let url = window.URL.createObjectURL(blob); + let a = document.createElement("a"); + a.href = url; + // Use responseToUseLater instead of response + const contentDisposition = responseToUseLater.headers.get("Content-Disposition"); + let filename = "download"; + if (contentDisposition && contentDisposition.indexOf("attachment") !== -1) { + filename = decodeURIComponent(contentDisposition.split("filename=")[1].replace(/"/g, "")).trim(); + } + a.download = filename; - - - - - - - - - - - let pipelineConfigJson = JSON.stringify(pipelineConfig, null, 2); - - let formData = new FormData(); - - let fileInput = document.getElementById('fileInput-input'); - let files = fileInput.files; - - for (let i = 0; i < files.length; i++) { - console.log("files[i]", files[i].name); - formData.append('fileInput', files[i], files[i].name); - } - - console.log("pipelineConfigJson", pipelineConfigJson); - formData.append('json', pipelineConfigJson); - console.log("formData", formData); - - fetch('api/v1/pipeline/handleData', { - method: 'POST', - body: formData - }) - .then(response => { - // Save the response to use it later - const responseToUseLater = response; - - return response.blob().then(blob => { - let url = window.URL.createObjectURL(blob); - let a = document.createElement('a'); - a.href = url; - - // Use responseToUseLater instead of response - const contentDisposition = responseToUseLater.headers.get('Content-Disposition'); - let filename = 'download'; - if (contentDisposition && contentDisposition.indexOf('attachment') !== -1) { - filename = decodeURIComponent(contentDisposition.split('filename=')[1].replace(/"/g, '')).trim(); - } - a.download = filename; - - document.body.appendChild(a); - a.click(); - a.remove(); - }); - }) - .catch((error) => { - console.error('Error:', error); - }); - + document.body.appendChild(a); + a.click(); + a.remove(); + }); + }) + .catch((error) => { + console.error("Error:", error); + }); }); let apiDocs = {}; let apiSchemas = {}; let operationSettings = {}; -fetch('v1/api-docs') - .then(response => response.json()) - .then(data => { +fetch("v1/api-docs") + .then((response) => response.json()) + .then((data) => { + apiDocs = data.paths; + apiSchemas = data.components.schemas; + let operationsDropdown = document.getElementById("operationsDropdown"); + const ignoreOperations = ["/api/v1/pipeline/handleData", "/api/v1/pipeline/operationToIgnore"]; // Add the operations you want to ignore here - apiDocs = data.paths; - apiSchemas = data.components.schemas; - let operationsDropdown = document.getElementById('operationsDropdown'); - const ignoreOperations = ["/api/v1/pipeline/handleData", "/api/v1/pipeline/operationToIgnore"]; // Add the operations you want to ignore here + operationsDropdown.innerHTML = ""; - operationsDropdown.innerHTML = ''; + let operationsByTag = {}; - let operationsByTag = {}; + // Group operations by tags + Object.keys(data.paths).forEach((operationPath) => { + let operation = data.paths[operationPath].post; + if (!operation || !operation.description) { + console.log(operationPath); + } + //!operation.description.includes("Type:MISO") + if (operation && !ignoreOperations.includes(operationPath)) { + let operationTag = operation.tags[0]; // This assumes each operation has exactly one tag + if (!operationsByTag[operationTag]) { + operationsByTag[operationTag] = []; + } + operationsByTag[operationTag].push(operationPath); + } + }); - // Group operations by tags - Object.keys(data.paths).forEach(operationPath => { - let operation = data.paths[operationPath].post; - if (!operation || !operation.description) { - console.log(operationPath); - } - //!operation.description.includes("Type:MISO") - if (operation && !ignoreOperations.includes(operationPath)) { - let operationTag = operation.tags[0]; // This assumes each operation has exactly one tag - if (!operationsByTag[operationTag]) { - operationsByTag[operationTag] = []; - } - operationsByTag[operationTag].push(operationPath); - } - }); + // Sort operations within each tag alphabetically + Object.keys(operationsByTag).forEach((tag) => { + operationsByTag[tag].sort(); + }); - // Sort operations within each tag alphabetically - Object.keys(operationsByTag).forEach(tag => { - operationsByTag[tag].sort(); - }); + // Specify the order of tags + let tagOrder = ["General", "Security", "Convert", "Misc", "Filter"]; - // Specify the order of tags - let tagOrder = ["General", "Security", "Convert", "Misc", "Filter"]; + // Create dropdown options + tagOrder.forEach((tag) => { + if (operationsByTag[tag]) { + let group = document.createElement("optgroup"); + group.label = tag; - // Create dropdown options - tagOrder.forEach(tag => { - if (operationsByTag[tag]) { - let group = document.createElement('optgroup'); - group.label = tag; + operationsByTag[tag].forEach((operationPath) => { + let option = document.createElement("option"); - operationsByTag[tag].forEach(operationPath => { - let option = document.createElement('option'); + let operationPathDisplay = operationPath; + operationPathDisplay = operationPath.replace(new RegExp("api/v1/" + tag.toLowerCase() + "/", "i"), ""); - let operationPathDisplay = operationPath - operationPathDisplay = operationPath.replace(new RegExp("api/v1/" + tag.toLowerCase() + "/", 'i'), ""); + if (operationPath.includes("/convert")) { + operationPathDisplay = operationPathDisplay.replace(/^\//, "").replaceAll("/", " to "); + } else { + operationPathDisplay = operationPathDisplay.replace(/\//g, ""); // Remove slashes + } + operationPathDisplay = operationPathDisplay.replaceAll(" ", "-"); + option.textContent = operationPathDisplay; + option.value = operationPath; // Keep the value with slashes for querying + group.appendChild(option); + }); + operationsDropdown.appendChild(group); + } + }); + }); - if (operationPath.includes("/convert")) { - operationPathDisplay = operationPathDisplay.replace(/^\//, '').replaceAll("/", " to "); - } else { - operationPathDisplay = operationPathDisplay.replace(/\//g, ''); // Remove slashes - } - operationPathDisplay = operationPathDisplay.replaceAll(" ", "-"); - option.textContent = operationPathDisplay; - option.value = operationPath; // Keep the value with slashes for querying - group.appendChild(option); - }); +document.getElementById("addOperationBtn").addEventListener("click", function () { + let selectedOperation = document.getElementById("operationsDropdown").value; + let pipelineList = document.getElementById("pipelineList"); - operationsDropdown.appendChild(group); - } - }); - }); + let listItem = document.createElement("li"); + listItem.className = "list-group-item"; + let hasSettings = false; + if (apiDocs[selectedOperation] && apiDocs[selectedOperation].post) { + const postMethod = apiDocs[selectedOperation].post; + // Check if parameters exist + if (postMethod.parameters && postMethod.parameters.length > 0) { + hasSettings = true; + } else if (postMethod.requestBody && postMethod.requestBody.content["multipart/form-data"]) { + // Extract the reference key + const refKey = postMethod.requestBody.content["multipart/form-data"].schema["$ref"].split("/").pop(); + // Check if the referenced schema exists and has properties more than just its input file + if (apiSchemas[refKey]) { + const properties = apiSchemas[refKey].properties; + const propertyKeys = Object.keys(properties); -document.getElementById('addOperationBtn').addEventListener('click', function() { - let selectedOperation = document.getElementById('operationsDropdown').value; - let pipelineList = document.getElementById('pipelineList'); + // Check if there's more than one property or if there's exactly one property and its format is not 'binary' + if (propertyKeys.length > 1 || (propertyKeys.length === 1 && properties[propertyKeys[0]].format !== "binary")) { + hasSettings = true; + } + } + } + } - let listItem = document.createElement('li'); - listItem.className = "list-group-item"; - let hasSettings = false; - if (apiDocs[selectedOperation] && apiDocs[selectedOperation].post) { - const postMethod = apiDocs[selectedOperation].post; + listItem.innerHTML = ` +
+
${selectedOperation}
+
+ + + + +
+
+ `; - // Check if parameters exist - if (postMethod.parameters && postMethod.parameters.length > 0) { - hasSettings = true; - } else if (postMethod.requestBody && postMethod.requestBody.content['multipart/form-data']) { - // Extract the reference key - const refKey = postMethod.requestBody.content['multipart/form-data'].schema['$ref'].split('/').pop(); - // Check if the referenced schema exists and has properties more than just its input file - if (apiSchemas[refKey]) { - const properties = apiSchemas[refKey].properties; - const propertyKeys = Object.keys(properties); + pipelineList.appendChild(listItem); - // Check if there's more than one property or if there's exactly one property and its format is not 'binary' - if (propertyKeys.length > 1 || (propertyKeys.length === 1 && properties[propertyKeys[0]].format !== 'binary')) { - hasSettings = true; - } - } - } - } + listItem.querySelector(".move-up").addEventListener("click", function (event) { + event.preventDefault(); + if (listItem.previousElementSibling) { + pipelineList.insertBefore(listItem, listItem.previousElementSibling); + updateConfigInDropdown(); + } + }); + listItem.querySelector(".move-down").addEventListener("click", function (event) { + event.preventDefault(); + if (listItem.nextElementSibling) { + pipelineList.insertBefore(listItem.nextElementSibling, listItem); + updateConfigInDropdown(); + } + }); + listItem.querySelector(".remove").addEventListener("click", function (event) { + event.preventDefault(); + pipelineList.removeChild(listItem); + hideOrShowPipelineHeader(); + updateConfigInDropdown(); + }); + listItem.querySelector(".pipelineSettings").addEventListener("click", function (event) { + event.preventDefault(); + showpipelineSettingsModal(selectedOperation); + hideOrShowPipelineHeader(); + }); - listItem.innerHTML = ` -
-
${selectedOperation}
-
- - - - -
-
- `; + function showpipelineSettingsModal(operation) { + let pipelineSettingsModal = document.getElementById("pipelineSettingsModal"); + let pipelineSettingsContent = document.getElementById("pipelineSettingsContent"); + let operationData = apiDocs[operation].post.parameters || []; + // Resolve the $ref reference to get actual schema properties + let refKey = apiDocs[operation].post.requestBody.content["multipart/form-data"].schema["$ref"].split("/").pop(); + let requestBodyData = apiSchemas[refKey].properties || {}; - pipelineList.appendChild(listItem); + // Combine operationData and requestBodyData into a single array + operationData = operationData.concat( + Object.keys(requestBodyData).map((key) => ({ + name: key, + schema: requestBodyData[key], + })), + ); - listItem.querySelector('.move-up').addEventListener('click', function(event) { - event.preventDefault(); - if (listItem.previousElementSibling) { - pipelineList.insertBefore(listItem, listItem.previousElementSibling); - updateConfigInDropdown(); - } - }); + pipelineSettingsContent.innerHTML = ""; - listItem.querySelector('.move-down').addEventListener('click', function(event) { - event.preventDefault(); - if (listItem.nextElementSibling) { - pipelineList.insertBefore(listItem.nextElementSibling, listItem); - updateConfigInDropdown(); - } + operationData.forEach((parameter) => { + // If the parameter name is 'fileInput', return early to skip the rest of this iteration + if (parameter.name === "fileInput") return; - }); + let parameterDiv = document.createElement("div"); + parameterDiv.className = "mb-3"; - listItem.querySelector('.remove').addEventListener('click', function(event) { - event.preventDefault(); - pipelineList.removeChild(listItem); - hideOrShowPipelineHeader(); - updateConfigInDropdown(); - }); + let parameterLabel = document.createElement("label"); + parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `; + parameterLabel.title = parameter.schema.description; + parameterLabel.setAttribute("for", parameter.name); + parameterDiv.appendChild(parameterLabel); - listItem.querySelector('.pipelineSettings').addEventListener('click', function(event) { - event.preventDefault(); - showpipelineSettingsModal(selectedOperation); - hideOrShowPipelineHeader(); - }); + let defaultValue = parameter.schema.example; + if (defaultValue === undefined) defaultValue = parameter.schema.default; - function showpipelineSettingsModal(operation) { - let pipelineSettingsModal = document.getElementById('pipelineSettingsModal'); - let pipelineSettingsContent = document.getElementById('pipelineSettingsContent'); - let operationData = apiDocs[operation].post.parameters || []; + let parameterInput; - // Resolve the $ref reference to get actual schema properties - let refKey = apiDocs[operation].post.requestBody.content['multipart/form-data'].schema['$ref'].split('/').pop(); - let requestBodyData = apiSchemas[refKey].properties || {}; + // check if enum exists in schema + if (parameter.schema.enum) { + // if enum exists, create a select element + parameterInput = document.createElement("select"); + parameterInput.className = "form-control"; - // Combine operationData and requestBodyData into a single array - operationData = operationData.concat(Object.keys(requestBodyData).map(key => ({ - name: key, - schema: requestBodyData[key] - }))); + // iterate over each enum value and create an option for it + parameter.schema.enum.forEach((value) => { + let option = document.createElement("option"); + option.value = value; + option.text = value; + parameterInput.appendChild(option); + }); + } else { + // switch-case statement for handling non-enum types + switch (parameter.schema.type) { + case "string": + if (parameter.schema.format === "binary") { + // This is a file input - pipelineSettingsContent.innerHTML = ''; - - operationData.forEach(parameter => { - // If the parameter name is 'fileInput', return early to skip the rest of this iteration - if (parameter.name === 'fileInput') return; - - let parameterDiv = document.createElement('div'); - parameterDiv.className = "mb-3"; - - let parameterLabel = document.createElement('label'); - parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `; - parameterLabel.title = parameter.schema.description; - parameterLabel.setAttribute('for', parameter.name); - parameterDiv.appendChild(parameterLabel); - - let defaultValue = parameter.schema.example; - if (defaultValue === undefined) defaultValue = parameter.schema.default; - - let parameterInput; - - // check if enum exists in schema - if (parameter.schema.enum) { - // if enum exists, create a select element - parameterInput = document.createElement('select'); - parameterInput.className = "form-control"; - - // iterate over each enum value and create an option for it - parameter.schema.enum.forEach(value => { - let option = document.createElement('option'); - option.value = value; - option.text = value; - parameterInput.appendChild(option); - }); - } else { - // switch-case statement for handling non-enum types - switch (parameter.schema.type) { - case 'string': - if (parameter.schema.format === 'binary') { - // This is a file input - - //parameterInput = document.createElement('input'); - //parameterInput.type = 'file'; - //parameterInput.className = "form-control"; - - parameterInput = document.createElement('input'); - parameterInput.type = 'text'; - parameterInput.className = "form-control"; - parameterInput.value = "FileInputPathToBeInputtedManuallyForOffline"; - } else { - parameterInput = document.createElement('input'); - parameterInput.type = 'text'; - parameterInput.className = "form-control"; - if (defaultValue !== undefined) parameterInput.value = defaultValue; - } - break; - case 'number': - case 'integer': - parameterInput = document.createElement('input'); - parameterInput.type = 'number'; - parameterInput.className = "form-control"; - if (defaultValue !== undefined) parameterInput.value = defaultValue; - break; - case 'boolean': - parameterInput = document.createElement('input'); - parameterInput.type = 'checkbox'; - if (defaultValue === true) parameterInput.checked = true; - break; - case 'array': - case 'object': - //TODO compare to doc and check if fileInput array? parameter.schema.format === 'binary' - parameterInput = document.createElement('textarea'); - parameterInput.placeholder = `Enter a JSON formatted ${parameter.schema.type}, If this is a fileInput, it is not currently supported`; - parameterInput.className = "form-control"; - break; - default: - parameterInput = document.createElement('input'); - parameterInput.type = 'text'; - parameterInput.className = "form-control"; - if (defaultValue !== undefined) parameterInput.value = defaultValue; - } - } - parameterInput.id = parameter.name; - - console.log("defaultValue", defaultValue); - console.log("parameterInput", parameterInput); - if (operationSettings[operation] && operationSettings[operation][parameter.name] !== undefined) { - let savedValue = operationSettings[operation][parameter.name]; - - switch (parameter.schema.type) { - case 'number': - case 'integer': - parameterInput.value = savedValue.toString(); - break; - case 'boolean': - parameterInput.checked = savedValue; - break; - case 'array': - case 'object': - parameterInput.value = JSON.stringify(savedValue); - break; - default: - parameterInput.value = savedValue; - } - } - console.log("parameterInput2", parameterInput); - parameterDiv.appendChild(parameterInput); - - pipelineSettingsContent.appendChild(parameterDiv); - }); - - if(hasSettings) { - let saveButton = document.createElement('button'); - saveButton.textContent = saveSettings; - saveButton.className = "btn btn-primary"; - saveButton.addEventListener('click', function(event) { - event.preventDefault(); - let settings = {}; - operationData.forEach(parameter => { - if (parameter.name !== "fileInput") { - let value = document.getElementById(parameter.name).value; - switch (parameter.schema.type) { - case 'number': - case 'integer': - settings[parameter.name] = Number(value); - break; - case 'boolean': - settings[parameter.name] = document.getElementById(parameter.name).checked; - break; - case 'array': - case 'object': - if (value === null || value === '') { - settings[parameter.name] = ''; - } else { - try { - settings[parameter.name] = JSON.parse(value); - } catch (err) { - console.error(`Invalid JSON format for ${parameter.name}`); - } - } - break; - default: - settings[parameter.name] = value; - } - } - }); - operationSettings[operation] = settings; - //pipelineSettingsModal.style.display = "none"; - }); - pipelineSettingsContent.appendChild(saveButton); - saveButton.click(); - } - //pipelineSettingsModal.style.display = "block"; - - //pipelineSettingsModal.getElementsByClassName("close")[0].onclick = function() { - // pipelineSettingsModal.style.display = "none"; - //} - - //window.onclick = function(event) { - // if (event.target == pipelineSettingsModal) { - // pipelineSettingsModal.style.display = "none"; - // } - //} - } - showpipelineSettingsModal(selectedOperation); - updateConfigInDropdown(); - hideOrShowPipelineHeader(); + //parameterInput = document.createElement('input'); + //parameterInput.type = 'file'; + //parameterInput.className = "form-control"; + parameterInput = document.createElement("input"); + parameterInput.type = "text"; + parameterInput.className = "form-control"; + parameterInput.value = "FileInputPathToBeInputtedManuallyForOffline"; + } else { + parameterInput = document.createElement("input"); + parameterInput.type = "text"; + parameterInput.className = "form-control"; + if (defaultValue !== undefined) parameterInput.value = defaultValue; + } + break; + case "number": + case "integer": + parameterInput = document.createElement("input"); + parameterInput.type = "number"; + parameterInput.className = "form-control"; + if (defaultValue !== undefined) parameterInput.value = defaultValue; + break; + case "boolean": + parameterInput = document.createElement("input"); + parameterInput.type = "checkbox"; + if (defaultValue === true) parameterInput.checked = true; + break; + case "array": + case "object": + //TODO compare to doc and check if fileInput array? parameter.schema.format === 'binary' + parameterInput = document.createElement("textarea"); + parameterInput.placeholder = `Enter a JSON formatted ${parameter.schema.type}, If this is a fileInput, it is not currently supported`; + parameterInput.className = "form-control"; + break; + default: + parameterInput = document.createElement("input"); + parameterInput.type = "text"; + parameterInput.className = "form-control"; + if (defaultValue !== undefined) parameterInput.value = defaultValue; + } + } + parameterInput.id = parameter.name; + console.log("defaultValue", defaultValue); + console.log("parameterInput", parameterInput); + if (operationSettings[operation] && operationSettings[operation][parameter.name] !== undefined) { + let savedValue = operationSettings[operation][parameter.name]; + switch (parameter.schema.type) { + case "number": + case "integer": + parameterInput.value = savedValue.toString(); + break; + case "boolean": + parameterInput.checked = savedValue; + break; + case "array": + case "object": + parameterInput.value = JSON.stringify(savedValue); + break; + default: + parameterInput.value = savedValue; + } + } + console.log("parameterInput2", parameterInput); + parameterDiv.appendChild(parameterInput); + pipelineSettingsContent.appendChild(parameterDiv); + }); + if (hasSettings) { + let saveButton = document.createElement("button"); + saveButton.textContent = saveSettings; + saveButton.className = "btn btn-primary"; + saveButton.addEventListener("click", function (event) { + event.preventDefault(); + let settings = {}; + operationData.forEach((parameter) => { + if (parameter.name !== "fileInput") { + let value = document.getElementById(parameter.name).value; + switch (parameter.schema.type) { + case "number": + case "integer": + settings[parameter.name] = Number(value); + break; + case "boolean": + settings[parameter.name] = document.getElementById(parameter.name).checked; + break; + case "array": + case "object": + if (value === null || value === "") { + settings[parameter.name] = ""; + } else { + try { + settings[parameter.name] = JSON.parse(value); + } catch (err) { + console.error(`Invalid JSON format for ${parameter.name}`); + } + } + break; + default: + settings[parameter.name] = value; + } + } + }); + operationSettings[operation] = settings; + //pipelineSettingsModal.style.display = "none"; + }); + pipelineSettingsContent.appendChild(saveButton); + saveButton.click(); + } + //pipelineSettingsModal.style.display = "block"; + //pipelineSettingsModal.getElementsByClassName("close")[0].onclick = function() { + // pipelineSettingsModal.style.display = "none"; + //} + //window.onclick = function(event) { + // if (event.target == pipelineSettingsModal) { + // pipelineSettingsModal.style.display = "none"; + // } + //} + } + showpipelineSettingsModal(selectedOperation); + updateConfigInDropdown(); + hideOrShowPipelineHeader(); }); +function loadBrowserPipelinesIntoDropdown() { + let pipelineSelect = document.getElementById("pipelineSelect"); + + // Retrieve the current set of option values for comparison + let existingOptions = new Set(); + for (let option of pipelineSelect.options) { + existingOptions.add(option.value); + } + + // Iterate over all items in localStorage + for (let i = 0; i < localStorage.length; i++) { + let key = localStorage.key(i); + if (key.startsWith("#Pipeline-")) { + let pipelineData = localStorage.getItem(key); + // Check if the data is already in the dropdown + if (!existingOptions.has(pipelineData)) { + let pipelineName = key.replace("#Pipeline-", ""); // Extract pipeline name from the key + let option = new Option(pipelineName, pipelineData); // Use pipeline data as the option value + pipelineSelect.appendChild(option); + } + } + } +} +loadBrowserPipelinesIntoDropdown(); + function updateConfigInDropdown() { - let pipelineSelect = document.getElementById('pipelineSelect'); - let selectedOption = pipelineSelect.options[pipelineSelect.selectedIndex]; + let pipelineSelect = document.getElementById("pipelineSelect"); + let selectedOption = pipelineSelect.options[pipelineSelect.selectedIndex]; - // Get the current configuration as JSON - let pipelineConfigJson = configToJson(); - console.log("pipelineConfigJson", pipelineConfigJson); - if (!pipelineConfigJson) { - console.error("Failed to update configuration: Invalid configuration"); - return; - } - - // Update the value of the selected option with the new configuration - selectedOption.value = pipelineConfigJson; + // Get the current configuration as JSON + let pipelineConfigJson = configToJson(); + console.log("pipelineConfigJson", pipelineConfigJson); + if (!pipelineConfigJson) { + console.error("Failed to update configuration: Invalid configuration"); + return; + } + // Update the value of the selected option with the new configuration + selectedOption.value = pipelineConfigJson; } -var saveBtn = document.getElementById('savePipelineBtn'); - +var saveBtn = document.getElementById("savePipelineBtn"); +var saveBrowserBtn = document.getElementById("saveBrowserPipelineBtn"); // Remove any existing event listeners -saveBtn.removeEventListener('click', savePipeline); - +saveBtn.removeEventListener("click", savePipeline); +saveBrowserBtn.removeEventListener("click", savePipelineToBrowser); // Add the event listener -saveBtn.addEventListener('click', savePipeline); -console.log("saveBtn", saveBtn) +saveBtn.addEventListener("click", savePipeline); +saveBrowserBtn.addEventListener("click", savePipelineToBrowser); function configToJson() { - if (!validatePipeline()) { - return null; // Return null if validation fails - } + if (!validatePipeline()) { + return null; // Return null if validation fails + } - var pipelineName = document.getElementById('pipelineName').value; - let pipelineList = document.getElementById('pipelineList').children; - let pipelineConfig = { - "name": pipelineName, - "pipeline": [], - "_examples": { - "outputDir": "{outputFolder}/{folderName}", - "outputFileName": "{filename}-{pipelineName}-{date}-{time}" - }, - "outputDir": "{outputFolder}", - "outputFileName": "{filename}" - }; + var pipelineName = document.getElementById("pipelineName").value; + let pipelineList = document.getElementById("pipelineList").children; + let pipelineConfig = { + name: pipelineName, + pipeline: [], + _examples: { + outputDir: "{outputFolder}/{folderName}", + outputFileName: "{filename}-{pipelineName}-{date}-{time}", + }, + outputDir: "{outputFolder}", + outputFileName: "{filename}", + }; - for (let i = 0; i < pipelineList.length; i++) { - let operationName = pipelineList[i].querySelector('.operationName').textContent; - let parameters = operationSettings[operationName] || {}; + for (let i = 0; i < pipelineList.length; i++) { + let operationName = pipelineList[i].querySelector(".operationName").textContent; + let parameters = operationSettings[operationName] || {}; - parameters['fileInput'] = 'automated'; + parameters["fileInput"] = "automated"; - pipelineConfig.pipeline.push({ - "operation": operationName, - "parameters": parameters - }); - } + pipelineConfig.pipeline.push({ + operation: operationName, + parameters: parameters, + }); + } - return JSON.stringify(pipelineConfig, null, 2); + return JSON.stringify(pipelineConfig, null, 2); } +function savePipelineToBrowser() { + let pipelineConfigJson = configToJson(); + if (!pipelineConfigJson) { + console.error("Failed to save pipeline: Invalid configuration"); + return; + } - + let pipelineName = document.getElementById("pipelineName").value; + if (!pipelineName) { + console.error("Failed to save pipeline: Pipeline name is required"); + return; + } + localStorage.setItem("#Pipeline-" +pipelineName, pipelineConfigJson); + console.log("Pipeline configuration saved to localStorage"); +} function savePipeline() { - let pipelineConfigJson = configToJson(); - if (!pipelineConfigJson) { - console.error("Failed to save pipeline: Invalid configuration"); - return; - } + let pipelineConfigJson = configToJson(); + if (!pipelineConfigJson) { + console.error("Failed to save pipeline: Invalid configuration"); + return; + } - let pipelineName = document.getElementById('pipelineName').value; - console.log("Downloading..."); - let a = document.createElement('a'); - a.href = URL.createObjectURL(new Blob([pipelineConfigJson], { type: 'application/json' })); - a.download = pipelineName + '.json'; - a.style.display = 'none'; + let pipelineName = document.getElementById("pipelineName").value; + console.log("Downloading..."); + let a = document.createElement("a"); + a.href = URL.createObjectURL(new Blob([pipelineConfigJson], { type: "application/json" })); + a.download = pipelineName + ".json"; + a.style.display = "none"; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); } - async function processPipelineConfig(configString) { - console.log("configString", configString); - let pipelineConfig = JSON.parse(configString); - let pipelineList = document.getElementById('pipelineList'); + console.log("configString", configString); + let pipelineConfig = JSON.parse(configString); + let pipelineList = document.getElementById("pipelineList"); - while (pipelineList.firstChild) { - pipelineList.removeChild(pipelineList.firstChild); - } - document.getElementById('pipelineName').value = pipelineConfig.name - for (const operationConfig of pipelineConfig.pipeline) { - let operationsDropdown = document.getElementById('operationsDropdown'); - operationsDropdown.value = operationConfig.operation; - operationSettings[operationConfig.operation] = operationConfig.parameters; + while (pipelineList.firstChild) { + pipelineList.removeChild(pipelineList.firstChild); + } + document.getElementById("pipelineName").value = pipelineConfig.name; + for (const operationConfig of pipelineConfig.pipeline) { + let operationsDropdown = document.getElementById("operationsDropdown"); + operationsDropdown.value = operationConfig.operation; + operationSettings[operationConfig.operation] = operationConfig.parameters; - // assuming addOperation is async - await new Promise((resolve) => { - document.getElementById('addOperationBtn').addEventListener('click', resolve, { once: true }); - document.getElementById('addOperationBtn').click(); - }); + // assuming addOperation is async + await new Promise((resolve) => { + document.getElementById("addOperationBtn").addEventListener("click", resolve, { once: true }); + document.getElementById("addOperationBtn").click(); + }); - let lastOperation = pipelineList.lastChild; + let lastOperation = pipelineList.lastChild; - Object.keys(operationConfig.parameters).forEach(parameterName => { - let input = document.getElementById(parameterName); - if (input) { - switch (input.type) { - case 'checkbox': - input.checked = operationConfig.parameters[parameterName]; - break; - case 'number': - input.value = operationConfig.parameters[parameterName].toString(); - break; - case 'file': - if (parameterName !== 'fileInput') { - // Create a new file input element - let newInput = document.createElement('input'); - newInput.type = 'file'; - newInput.id = parameterName; + Object.keys(operationConfig.parameters).forEach((parameterName) => { + let input = document.getElementById(parameterName); + if (input) { + switch (input.type) { + case "checkbox": + input.checked = operationConfig.parameters[parameterName]; + break; + case "number": + input.value = operationConfig.parameters[parameterName].toString(); + break; + case "file": + if (parameterName !== "fileInput") { + // Create a new file input element + let newInput = document.createElement("input"); + newInput.type = "file"; + newInput.id = parameterName; - // Add the new file input to the main page (change the selector according to your needs) - document.querySelector('#main').appendChild(newInput); - } - break; - case 'text': - case 'textarea': - default: - input.value = JSON.stringify(operationConfig.parameters[parameterName]); - } - } - }); - - } + // Add the new file input to the main page (change the selector according to your needs) + document.querySelector("#main").appendChild(newInput); + } + break; + case "text": + case "textarea": + default: + input.value = JSON.stringify(operationConfig.parameters[parameterName]); + } + } + }); + } } - -document.getElementById('uploadPipelineBtn').addEventListener('click', function() { - document.getElementById('uploadPipelineInput').click(); +document.getElementById("uploadPipelineBtn").addEventListener("click", function () { + document.getElementById("uploadPipelineInput").click(); }); -document.getElementById('uploadPipelineInput').addEventListener('change', function(e) { - let reader = new FileReader(); - reader.onload = function(event) { - processPipelineConfig(event.target.result); - }; - reader.readAsText(e.target.files[0]); - hideOrShowPipelineHeader(); +document.getElementById("uploadPipelineInput").addEventListener("change", function (e) { + let reader = new FileReader(); + reader.onload = function (event) { + processPipelineConfig(event.target.result); + }; + reader.readAsText(e.target.files[0]); + hideOrShowPipelineHeader(); }); -document.getElementById('pipelineSelect').addEventListener('change', function(e) { - let selectedPipelineJson = e.target.value; // assuming the selected value is the JSON string of the pipeline config - processPipelineConfig(selectedPipelineJson); +document.getElementById("pipelineSelect").addEventListener("change", function (e) { + let selectedPipelineJson = e.target.value; // assuming the selected value is the JSON string of the pipeline config + processPipelineConfig(selectedPipelineJson); }); - function hideOrShowPipelineHeader() { - var pipelineHeader = document.getElementById('pipelineHeader'); - var pipelineList = document.getElementById('pipelineList'); + var pipelineHeader = document.getElementById("pipelineHeader"); + var pipelineList = document.getElementById("pipelineList"); - if (pipelineList.children.length === 0) { - // Hide the pipeline header if there are no items in the pipeline list - pipelineHeader.style.display = 'none'; - } else { - // Show the pipeline header if there are items in the pipeline list - pipelineHeader.style.display = 'block'; - } + if (pipelineList.children.length === 0) { + // Hide the pipeline header if there are no items in the pipeline list + pipelineHeader.style.display = "none"; + } else { + // Show the pipeline header if there are items in the pipeline list + pipelineHeader.style.display = "block"; + } } diff --git a/src/main/resources/static/js/search.js b/src/main/resources/static/js/search.js index 674b54b7..2329f998 100644 --- a/src/main/resources/static/js/search.js +++ b/src/main/resources/static/js/search.js @@ -1,75 +1,76 @@ // Toggle search bar when the search icon is clicked -document.querySelector('#search-icon').addEventListener('click', function(e) { - e.preventDefault(); - var searchBar = document.querySelector('#navbarSearch'); - searchBar.classList.toggle('show'); +document.querySelector("#search-icon").addEventListener("click", function (e) { + e.preventDefault(); + var searchBar = document.querySelector("#navbarSearch"); + searchBar.classList.toggle("show"); }); -window.onload = function() { - var items = document.querySelectorAll('.dropdown-item, .nav-link'); - var dummyContainer = document.createElement('div'); - dummyContainer.style.position = 'absolute'; - dummyContainer.style.visibility = 'hidden'; - dummyContainer.style.whiteSpace = 'nowrap'; // Ensure we measure full width - document.body.appendChild(dummyContainer); +window.onload = function () { + var items = document.querySelectorAll(".dropdown-item, .nav-link"); + var dummyContainer = document.createElement("div"); + dummyContainer.style.position = "absolute"; + dummyContainer.style.visibility = "hidden"; + dummyContainer.style.whiteSpace = "nowrap"; // Ensure we measure full width + document.body.appendChild(dummyContainer); - var maxWidth = 0; + var maxWidth = 0; - items.forEach(function(item) { - var clone = item.cloneNode(true); - dummyContainer.appendChild(clone); - var width = clone.offsetWidth; - if (width > maxWidth) { - maxWidth = width; - } - dummyContainer.removeChild(clone); - }); + items.forEach(function (item) { + var clone = item.cloneNode(true); + dummyContainer.appendChild(clone); + var width = clone.offsetWidth; + if (width > maxWidth) { + maxWidth = width; + } + dummyContainer.removeChild(clone); + }); - document.body.removeChild(dummyContainer); + document.body.removeChild(dummyContainer); - // Store max width for later use - window.navItemMaxWidth = maxWidth; + // Store max width for later use + window.navItemMaxWidth = maxWidth; }; // Show search results as user types in search box -document.querySelector('#navbarSearchInput').addEventListener('input', function(e) { - var searchText = e.target.value.toLowerCase(); - var items = document.querySelectorAll('.dropdown-item, .nav-link'); - var resultsBox = document.querySelector('#searchResults'); +document.querySelector("#navbarSearchInput").addEventListener("input", function (e) { + var searchText = e.target.value.toLowerCase(); + var items = document.querySelectorAll(".dropdown-item, .nav-link"); + var resultsBox = document.querySelector("#searchResults"); - // Clear any previous results - resultsBox.innerHTML = ''; + // Clear any previous results + resultsBox.innerHTML = ""; - items.forEach(function(item) { - var titleElement = item.querySelector('.icon-text'); - var iconElement = item.querySelector('.icon'); - var itemHref = item.getAttribute('href'); - var tags = item.getAttribute('data-bs-tags') || ""; // If no tags, default to empty string + items.forEach(function (item) { + var titleElement = item.querySelector(".icon-text"); + var iconElement = item.querySelector(".icon"); + var itemHref = item.getAttribute("href"); + var tags = item.getAttribute("data-bs-tags") || ""; // If no tags, default to empty string - if (titleElement && iconElement && itemHref !== '#') { - var title = titleElement.innerText; - if ((title.toLowerCase().indexOf(searchText) !== -1 || tags.toLowerCase().indexOf(searchText) !== -1) && !resultsBox.querySelector(`a[href="${item.getAttribute('href')}"]`)) { - var result = document.createElement('a'); - result.href = itemHref; - result.classList.add('dropdown-item'); + if (titleElement && iconElement && itemHref !== "#") { + var title = titleElement.innerText; + if ( + (title.toLowerCase().indexOf(searchText) !== -1 || tags.toLowerCase().indexOf(searchText) !== -1) && + !resultsBox.querySelector(`a[href="${item.getAttribute("href")}"]`) + ) { + var result = document.createElement("a"); + result.href = itemHref; + result.classList.add("dropdown-item"); - var resultIcon = document.createElement('img'); - resultIcon.src = iconElement.src; - resultIcon.alt = 'icon'; - resultIcon.classList.add('icon'); - result.appendChild(resultIcon); + var resultIcon = document.createElement("img"); + resultIcon.src = iconElement.src; + resultIcon.alt = "icon"; + resultIcon.classList.add("icon"); + result.appendChild(resultIcon); - var resultText = document.createElement('span'); - resultText.textContent = title; - resultText.classList.add('icon-text'); - result.appendChild(resultText); + var resultText = document.createElement("span"); + resultText.textContent = title; + resultText.classList.add("icon-text"); + result.appendChild(resultText); - resultsBox.appendChild(result); - } - } - }); + resultsBox.appendChild(result); + } + } + }); - // Set the width of the search results box to the maximum width - resultsBox.style.width = window.navItemMaxWidth + 'px'; + // Set the width of the search results box to the maximum width + resultsBox.style.width = window.navItemMaxWidth + "px"; }); - - diff --git a/src/main/resources/static/js/settings.js b/src/main/resources/static/js/settings.js index 272b555b..8f6289af 100644 --- a/src/main/resources/static/js/settings.js +++ b/src/main/resources/static/js/settings.js @@ -1,42 +1,42 @@ // Get the download option from local storage, or set it to 'sameWindow' if it doesn't exist -var downloadOption = localStorage.getItem('downloadOption') - || 'sameWindow'; +var downloadOption = localStorage.getItem("downloadOption") || "sameWindow"; // Set the selected option in the dropdown -document.getElementById('downloadOption').value = downloadOption; - +document.getElementById("downloadOption").value = downloadOption; // Save the selected option to local storage when the dropdown value changes -document.getElementById('downloadOption').addEventListener( - 'change', - function() { - downloadOption = this.value; - localStorage.setItem('downloadOption', - downloadOption); - }); - - -// Get the zipThreshold value from local storage, or set it to 0 if it doesn't exist -var zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4; - -// Set the value of the slider and the display span -document.getElementById('zipThreshold').value = zipThreshold; -document.getElementById('zipThresholdValue').textContent = zipThreshold; - - - -// Save the selected value to local storage when the slider value changes -document.getElementById('zipThreshold').addEventListener('input', function() { - zipThreshold = this.value; - document.getElementById('zipThresholdValue').textContent = zipThreshold; - localStorage.setItem('zipThreshold', zipThreshold); +document.getElementById("downloadOption").addEventListener("change", function () { + downloadOption = this.value; + localStorage.setItem("downloadOption", downloadOption); }); +// Get the zipThreshold value from local storage, or set it to 0 if it doesn't exist +var zipThreshold = parseInt(localStorage.getItem("zipThreshold"), 10) || 4; -var boredWaiting = localStorage.getItem('boredWaiting') || 'disabled'; -document.getElementById('boredWaiting').checked = boredWaiting === 'enabled'; +// Set the value of the slider and the display span +document.getElementById("zipThreshold").value = zipThreshold; +document.getElementById("zipThresholdValue").textContent = zipThreshold; + +// Save the selected value to local storage when the slider value changes +document.getElementById("zipThreshold").addEventListener("input", function () { + zipThreshold = this.value; + document.getElementById("zipThresholdValue").textContent = zipThreshold; + localStorage.setItem("zipThreshold", zipThreshold); +}); + +var boredWaiting = localStorage.getItem("boredWaiting") || "disabled"; +document.getElementById("boredWaiting").checked = boredWaiting === "enabled"; + +document.getElementById("boredWaiting").addEventListener("change", function () { + boredWaiting = this.checked ? "enabled" : "disabled"; + localStorage.setItem("boredWaiting", boredWaiting); +}); + +var cacheInputs = localStorage.getItem("cacheInputs") || "disabled"; +document.getElementById("cacheInputs").checked = cacheInputs === "enabled"; + +document.getElementById("cacheInputs").addEventListener("change", function () { + cacheInputs = this.checked ? "enabled" : "disabled"; + localStorage.setItem("cacheInputs", cacheInputs); +}); -document.getElementById('boredWaiting').addEventListener('change', function() { - boredWaiting = this.checked ? 'enabled' : 'disabled'; - localStorage.setItem('boredWaiting', boredWaiting); -}); \ No newline at end of file diff --git a/src/main/resources/static/js/tab-container.js b/src/main/resources/static/js/tab-container.js index bd97d2b6..2aa85b32 100644 --- a/src/main/resources/static/js/tab-container.js +++ b/src/main/resources/static/js/tab-container.js @@ -1,39 +1,38 @@ - TabContainer = { - initTabGroups() { - const groups = document.querySelectorAll(".tab-group"); - const unloadedGroups = [...groups].filter(g => !g.initialised); - unloadedGroups.forEach(group => { - const containers = group.querySelectorAll(".tab-container"); - const tabTitles = [...containers].map(c => c.getAttribute("title")); + initTabGroups() { + const groups = document.querySelectorAll(".tab-group"); + const unloadedGroups = [...groups].filter((g) => !g.initialised); + unloadedGroups.forEach((group) => { + const containers = group.querySelectorAll(".tab-container"); + const tabTitles = [...containers].map((c) => c.getAttribute("title")); - const tabList = document.createElement("div"); - tabList.classList.add("tab-buttons"); - tabTitles.forEach(title => { - const tabButton = document.createElement("button"); - tabButton.innerHTML = title; - tabButton.onclick = e => { - this.setActiveTab(e.target); - } - tabList.appendChild(tabButton); - }); - group.prepend(tabList); + const tabList = document.createElement("div"); + tabList.classList.add("tab-buttons"); + tabTitles.forEach((title) => { + const tabButton = document.createElement("button"); + tabButton.innerHTML = title; + tabButton.onclick = (e) => { + this.setActiveTab(e.target); + }; + tabList.appendChild(tabButton); + }); + group.prepend(tabList); - this.setActiveTab(tabList.firstChild); + this.setActiveTab(tabList.firstChild); - group.initialised = true; - }); - }, - setActiveTab(tabButton) { - const group = tabButton.closest(".tab-group") + group.initialised = true; + }); + }, + setActiveTab(tabButton) { + const group = tabButton.closest(".tab-group"); - group.querySelectorAll(".active").forEach(el => el.classList.remove("active")); + group.querySelectorAll(".active").forEach((el) => el.classList.remove("active")); - tabButton.classList.add("active"); - group.querySelector(`[title="${tabButton.innerHTML}"]`).classList.add("active"); - }, -} + tabButton.classList.add("active"); + group.querySelector(`[title="${tabButton.innerHTML}"]`).classList.add("active"); + }, +}; document.addEventListener("DOMContentLoaded", () => { - TabContainer.initTabGroups(); -}) \ No newline at end of file + TabContainer.initTabGroups(); +}); diff --git a/src/main/resources/static/pdfjs/css/viewer.css b/src/main/resources/static/pdfjs/css/viewer.css index 14a8aff0..ab0ad406 100644 --- a/src/main/resources/static/pdfjs/css/viewer.css +++ b/src/main/resources/static/pdfjs/css/viewer.css @@ -116,7 +116,6 @@ top: 0; } - :root { --annotation-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,"); --input-focus-border-color: Highlight; @@ -139,9 +138,7 @@ .annotationLayer .textWidgetAnnotation :is(input, textarea):required, .annotationLayer .choiceWidgetAnnotation select:required, - .annotationLayer - .buttonWidgetAnnotation:is(.checkBox, .radioButton) - input:required { + .annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required { outline: 1.5px solid selectedItem; } @@ -228,9 +225,7 @@ height: 100%; } -.annotationLayer -:is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder) -> a:hover { +.annotationLayer :is(.linkAnnotation, .buttonWidgetAnnotation.pushButton):not(.hasBorder) > a:hover { opacity: 0.2; background-color: rgba(255, 255, 0, 1); box-shadow: 0 2px 10px rgba(255, 255, 0, 1); @@ -268,9 +263,7 @@ .annotationLayer .textWidgetAnnotation :is(input, textarea):required, .annotationLayer .choiceWidgetAnnotation select:required, -.annotationLayer -.buttonWidgetAnnotation:is(.checkBox, .radioButton) -input:required { +.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:required { outline: 1.5px solid red; } @@ -288,9 +281,7 @@ input:required { .annotationLayer .textWidgetAnnotation :is(input, textarea)[disabled], .annotationLayer .choiceWidgetAnnotation select[disabled], -.annotationLayer -.buttonWidgetAnnotation:is(.checkBox, .radioButton) -input[disabled] { +.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input[disabled] { background: none; border: 2px solid var(--input-disabled-border-color); cursor: not-allowed; @@ -298,9 +289,7 @@ input[disabled] { .annotationLayer .textWidgetAnnotation :is(input, textarea):hover, .annotationLayer .choiceWidgetAnnotation select:hover, -.annotationLayer -.buttonWidgetAnnotation:is(.checkBox, .radioButton) -input:hover { +.annotationLayer .buttonWidgetAnnotation:is(.checkBox, .radioButton) input:hover { border: 2px solid var(--input-hover-border-color); } @@ -489,7 +478,6 @@ input:hover { z-index: -1; } - :root { --xfa-unfocused-field-background: url("data:image/svg+xml;charset=UTF-8,"); --xfa-focus-outline: auto; @@ -827,10 +815,7 @@ input:hover { --freetext-padding: 2px; --resizer-bg-color: var(--outline-color); --resizer-size: 6px; - --resizer-shift: calc( - 0px - (var(--outline-width) + var(--resizer-size)) / 2 - - var(--outline-around-width) - ); + --resizer-shift: calc(0px - (var(--outline-width) + var(--resizer-size)) / 2 - var(--outline-around-width)); --editorFreeText-editing-cursor: text; --editorInk-editing-cursor: url(../images/cursor-editorInk.svg) 0 16, pointer; @@ -853,8 +838,7 @@ input:hover { @media (-webkit-min-device-pixel-ratio: 1.1), (min-resolution: 1.1dppx) { :root { - --editorFreeText-editing-cursor: url(../images/cursor-editorFreeText.svg) 0 16, - text; + --editorFreeText-editing-cursor: url(../images/cursor-editorFreeText.svg) 0 16, text; } } @@ -1109,216 +1093,354 @@ input:hover { } .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topLeft, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.topLeft, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topLeft, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.topLeft, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topLeft, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.topLeft, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topLeft, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.topLeft, .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomRight, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.bottomRight, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomRight, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.bottomRight, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomRight, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.bottomRight, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomRight { + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.bottomRight { cursor: nwse-resize; } .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topMiddle, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.topMiddle, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topMiddle, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.topMiddle, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topMiddle, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.topMiddle, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topMiddle, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.topMiddle, .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomMiddle, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.bottomMiddle, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomMiddle, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.bottomMiddle, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomMiddle, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.bottomMiddle, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomMiddle { + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.bottomMiddle { cursor: ns-resize; } .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topRight, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.topRight, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topRight, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.topRight, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topRight, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.topRight, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topRight, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.topRight, .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomLeft, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.bottomLeft, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomLeft, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.bottomLeft, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomLeft, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.bottomLeft, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomLeft { + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.bottomLeft { cursor: nesw-resize; } .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleRight, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.middleRight, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleRight, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.middleRight, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleRight, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.middleRight, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleRight, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.middleRight, .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleLeft, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.middleLeft, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleLeft, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.middleLeft, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleLeft, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.middleLeft, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleLeft { + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.middleLeft { cursor: ew-resize; } .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topLeft, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.topLeft, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topLeft, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.topLeft, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topLeft, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.topLeft, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topLeft, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.topLeft, .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomRight, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.bottomRight, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomRight, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.bottomRight, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomRight, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.bottomRight, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomRight { + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.bottomRight { cursor: nesw-resize; } .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topMiddle, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.topMiddle, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topMiddle, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.topMiddle, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topMiddle, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.topMiddle, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topMiddle, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.topMiddle, .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomMiddle, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.bottomMiddle, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomMiddle, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.bottomMiddle, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomMiddle, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.bottomMiddle, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomMiddle { + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.bottomMiddle { cursor: ew-resize; } .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.topRight, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.topRight, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.topRight, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.topRight, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.topRight, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.topRight, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.topRight, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.topRight, .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.bottomLeft, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.bottomLeft, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.bottomLeft, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.bottomLeft, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.bottomLeft, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.bottomLeft, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.bottomLeft { + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.bottomLeft { cursor: nwse-resize; } .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleRight, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.middleRight, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleRight, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.middleRight, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleRight, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.middleRight, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleRight, + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.middleRight, .annotationEditorLayer[data-main-rotation="0"] -:is([data-editor-rotation="90"], [data-editor-rotation="270"]) > .resizers > .resizer.middleLeft, + :is([data-editor-rotation="90"], [data-editor-rotation="270"]) + > .resizers + > .resizer.middleLeft, .annotationEditorLayer[data-main-rotation="90"] -:is([data-editor-rotation="0"], [data-editor-rotation="180"]) > .resizers > .resizer.middleLeft, + :is([data-editor-rotation="0"], [data-editor-rotation="180"]) + > .resizers + > .resizer.middleLeft, .annotationEditorLayer[data-main-rotation="180"] -:is([data-editor-rotation="270"], [data-editor-rotation="90"]) > .resizers > .resizer.middleLeft, + :is([data-editor-rotation="270"], [data-editor-rotation="90"]) + > .resizers + > .resizer.middleLeft, .annotationEditorLayer[data-main-rotation="270"] -:is([data-editor-rotation="180"], [data-editor-rotation="0"]) > .resizers > .resizer.middleLeft { + :is([data-editor-rotation="180"], [data-editor-rotation="0"]) + > .resizers + > .resizer.middleLeft { cursor: ns-resize; } .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="90"], - [data-main-rotation="90"] [data-editor-rotation="0"], - [data-main-rotation="180"] [data-editor-rotation="270"], - [data-main-rotation="270"] [data-editor-rotation="180"] - ) .altText { + :is( + [data-main-rotation="0"] [data-editor-rotation="90"], + [data-main-rotation="90"] [data-editor-rotation="0"], + [data-main-rotation="180"] [data-editor-rotation="270"], + [data-main-rotation="270"] [data-editor-rotation="180"] + ) + .altText { rotate: 270deg; } -[dir="ltr"] .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="90"], - [data-main-rotation="90"] [data-editor-rotation="0"], - [data-main-rotation="180"] [data-editor-rotation="270"], - [data-main-rotation="270"] [data-editor-rotation="180"] - ) .altText { +[dir="ltr"] + .annotationEditorLayer + :is( + [data-main-rotation="0"] [data-editor-rotation="90"], + [data-main-rotation="90"] [data-editor-rotation="0"], + [data-main-rotation="180"] [data-editor-rotation="270"], + [data-main-rotation="270"] [data-editor-rotation="180"] + ) + .altText { inset-inline-start: calc(100% - 8px); } -[dir="ltr"] .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="90"], - [data-main-rotation="90"] [data-editor-rotation="0"], - [data-main-rotation="180"] [data-editor-rotation="270"], - [data-main-rotation="270"] [data-editor-rotation="180"] - ) .altText.small { +[dir="ltr"] + .annotationEditorLayer + :is( + [data-main-rotation="0"] [data-editor-rotation="90"], + [data-main-rotation="90"] [data-editor-rotation="0"], + [data-main-rotation="180"] [data-editor-rotation="270"], + [data-main-rotation="270"] [data-editor-rotation="180"] + ) + .altText.small { inset-inline-start: calc(100% + 8px); inset-block-start: 100%; } -[dir="rtl"] .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="90"], - [data-main-rotation="90"] [data-editor-rotation="0"], - [data-main-rotation="180"] [data-editor-rotation="270"], - [data-main-rotation="270"] [data-editor-rotation="180"] - ) .altText { +[dir="rtl"] + .annotationEditorLayer + :is( + [data-main-rotation="0"] [data-editor-rotation="90"], + [data-main-rotation="90"] [data-editor-rotation="0"], + [data-main-rotation="180"] [data-editor-rotation="270"], + [data-main-rotation="270"] [data-editor-rotation="180"] + ) + .altText { inset-block-end: calc(100% - 8px); } -[dir="rtl"] .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="90"], - [data-main-rotation="90"] [data-editor-rotation="0"], - [data-main-rotation="180"] [data-editor-rotation="270"], - [data-main-rotation="270"] [data-editor-rotation="180"] - ) .altText.small { +[dir="rtl"] + .annotationEditorLayer + :is( + [data-main-rotation="0"] [data-editor-rotation="90"], + [data-main-rotation="90"] [data-editor-rotation="0"], + [data-main-rotation="180"] [data-editor-rotation="270"], + [data-main-rotation="270"] [data-editor-rotation="180"] + ) + .altText.small { inset-inline-start: -8px; inset-block-start: 0; } .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="180"], - [data-main-rotation="90"] [data-editor-rotation="90"], - [data-main-rotation="180"] [data-editor-rotation="0"], - [data-main-rotation="270"] [data-editor-rotation="270"] - ) .altText { + :is( + [data-main-rotation="0"] [data-editor-rotation="180"], + [data-main-rotation="90"] [data-editor-rotation="90"], + [data-main-rotation="180"] [data-editor-rotation="0"], + [data-main-rotation="270"] [data-editor-rotation="270"] + ) + .altText { rotate: 180deg; inset-block-end: calc(100% - 8px); @@ -1326,64 +1448,74 @@ input:hover { } .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="180"], - [data-main-rotation="90"] [data-editor-rotation="90"], - [data-main-rotation="180"] [data-editor-rotation="0"], - [data-main-rotation="270"] [data-editor-rotation="270"] - ) .altText.small { + :is( + [data-main-rotation="0"] [data-editor-rotation="180"], + [data-main-rotation="90"] [data-editor-rotation="90"], + [data-main-rotation="180"] [data-editor-rotation="0"], + [data-main-rotation="270"] [data-editor-rotation="270"] + ) + .altText.small { inset-inline-start: 100%; inset-block-start: -8px; } .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="270"], - [data-main-rotation="90"] [data-editor-rotation="180"], - [data-main-rotation="180"] [data-editor-rotation="90"], - [data-main-rotation="270"] [data-editor-rotation="0"] - ) .altText { + :is( + [data-main-rotation="0"] [data-editor-rotation="270"], + [data-main-rotation="90"] [data-editor-rotation="180"], + [data-main-rotation="180"] [data-editor-rotation="90"], + [data-main-rotation="270"] [data-editor-rotation="0"] + ) + .altText { rotate: 90deg; } -[dir="ltr"] .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="270"], - [data-main-rotation="90"] [data-editor-rotation="180"], - [data-main-rotation="180"] [data-editor-rotation="90"], - [data-main-rotation="270"] [data-editor-rotation="0"] - ) .altText { +[dir="ltr"] + .annotationEditorLayer + :is( + [data-main-rotation="0"] [data-editor-rotation="270"], + [data-main-rotation="90"] [data-editor-rotation="180"], + [data-main-rotation="180"] [data-editor-rotation="90"], + [data-main-rotation="270"] [data-editor-rotation="0"] + ) + .altText { inset-block-end: calc(100% - 8px); } -[dir="ltr"] .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="270"], - [data-main-rotation="90"] [data-editor-rotation="180"], - [data-main-rotation="180"] [data-editor-rotation="90"], - [data-main-rotation="270"] [data-editor-rotation="0"] - ) .altText.small { +[dir="ltr"] + .annotationEditorLayer + :is( + [data-main-rotation="0"] [data-editor-rotation="270"], + [data-main-rotation="90"] [data-editor-rotation="180"], + [data-main-rotation="180"] [data-editor-rotation="90"], + [data-main-rotation="270"] [data-editor-rotation="0"] + ) + .altText.small { inset-inline-start: -8px; inset-block-start: 0; } -[dir="rtl"] .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="270"], - [data-main-rotation="90"] [data-editor-rotation="180"], - [data-main-rotation="180"] [data-editor-rotation="90"], - [data-main-rotation="270"] [data-editor-rotation="0"] - ) .altText { +[dir="rtl"] + .annotationEditorLayer + :is( + [data-main-rotation="0"] [data-editor-rotation="270"], + [data-main-rotation="90"] [data-editor-rotation="180"], + [data-main-rotation="180"] [data-editor-rotation="90"], + [data-main-rotation="270"] [data-editor-rotation="0"] + ) + .altText { inset-inline-start: calc(100% - 8px); } -[dir="rtl"] .annotationEditorLayer -:is( - [data-main-rotation="0"] [data-editor-rotation="270"], - [data-main-rotation="90"] [data-editor-rotation="180"], - [data-main-rotation="180"] [data-editor-rotation="90"], - [data-main-rotation="270"] [data-editor-rotation="0"] - ) .altText.small { +[dir="rtl"] + .annotationEditorLayer + :is( + [data-main-rotation="0"] [data-editor-rotation="270"], + [data-main-rotation="90"] [data-editor-rotation="180"], + [data-main-rotation="180"] [data-editor-rotation="90"], + [data-main-rotation="270"] [data-editor-rotation="0"] + ) + .altText.small { inset-inline-start: calc(100% + 8px); inset-block-start: 100%; } @@ -1421,7 +1553,6 @@ input:hover { } .altText.small { - inset-block-end: unset; inset-inline-start: 0; inset-block-start: calc(100% + 8px); @@ -1515,7 +1646,6 @@ input:hover { } @media (prefers-color-scheme: dark) { - .altText .tooltip.show { --alt-text-tooltip-bg: #1c1b22; --alt-text-tooltip-fg: #fbfbfe; @@ -1524,7 +1654,6 @@ input:hover { } @media screen and (forced-colors: active) { - .altText .tooltip.show { --alt-text-tooltip-bg: Canvas; --alt-text-tooltip-fg: CanvasText; @@ -1581,7 +1710,6 @@ input:hover { } @media (prefers-color-scheme: dark) { - #altTextDialog { --dialog-bg-color: #1c1b22; --dialog-border-color: #1c1b22; @@ -1604,7 +1732,6 @@ input:hover { } @media screen and (forced-colors: active) { - #altTextDialog { --dialog-bg-color: Canvas; --dialog-border-color: CanvasText; @@ -2022,8 +2149,8 @@ input:hover { --toolbar-border-color: rgba(184, 184, 184, 1); --toolbar-box-shadow: 0 1px 0 var(--toolbar-border-color); --toolbar-border-bottom: none; - --toolbarSidebar-box-shadow: inset calc(-1px * var(--dir-factor)) 0 0 rgba(0, 0, 0, 0.25), - 0 1px 0 rgba(0, 0, 0, 0.15), 0 0 1px rgba(0, 0, 0, 0.1); + --toolbarSidebar-box-shadow: inset calc(-1px * var(--dir-factor)) 0 0 rgba(0, 0, 0, 0.25), 0 1px 0 rgba(0, 0, 0, 0.15), + 0 0 1px rgba(0, 0, 0, 0.1); --toolbarSidebar-border-bottom: none; --button-hover-color: rgba(221, 222, 223, 1); --toggled-btn-color: rgba(0, 0, 0, 1); @@ -2319,8 +2446,7 @@ body { font: message-box; } -:is(.toolbar, .editorParamsToolbar, .findbar, #sidebarContainer) -:is(input, button, select), +:is(.toolbar, .editorParamsToolbar, .findbar, #sidebarContainer) :is(input, button, select), .secondaryToolbar :is(input, button, a, select) { outline: none; font: message-box; @@ -2417,19 +2543,18 @@ body { height: 100%; width: calc(100% + 150px); background: repeating-linear-gradient( - 135deg, - var(--progressBar-blend-color) 0, - var(--progressBar-bg-color) 5px, - var(--progressBar-bg-color) 45px, - var(--progressBar-color) 55px, - var(--progressBar-color) 95px, - var(--progressBar-blend-color) 100px + 135deg, + var(--progressBar-blend-color) 0, + var(--progressBar-bg-color) 5px, + var(--progressBar-bg-color) 45px, + var(--progressBar-color) 55px, + var(--progressBar-color) 95px, + var(--progressBar-blend-color) 100px ); animation: progressIndeterminate 1s linear infinite; } -#outerContainer.sidebarResizing -:is(#sidebarContainer, #viewerContainer, #loadingBar) { +#outerContainer.sidebarResizing :is(#sidebarContainer, #viewerContainer, #loadingBar) { /* Improve responsiveness and avoid visual glitches when the sidebar is resized. */ transition-duration: 0s; } @@ -2600,8 +2725,9 @@ body { .doorHanger, .doorHangerRight { border-radius: 2px; - box-shadow: 0 1px 5px var(--doorhanger-border-color), - 0 0 0 1px var(--doorhanger-border-color); + box-shadow: + 0 1px 5px var(--doorhanger-border-color), + 0 0 0 1px var(--doorhanger-border-color); border: var(--doorhanger-border-color-whcm); } @@ -3455,8 +3581,7 @@ dialog :link { cursor: grab !important; } -.grab-to-pan-grab -*:not(input):not(textarea):not(button):not(select):not(:link) { +.grab-to-pan-grab *:not(input):not(textarea):not(button):not(select):not(:link) { cursor: inherit !important; } diff --git a/src/main/resources/static/pdfjs/js/viewer.js b/src/main/resources/static/pdfjs/js/viewer.js index f533f24b..3ed440f6 100644 --- a/src/main/resources/static/pdfjs/js/viewer.js +++ b/src/main/resources/static/pdfjs/js/viewer.js @@ -3323,13 +3323,13 @@ kind: OptionKind.WORKER }, workerSrc: { - value: "/pdfjs/pdf.worker.js", + value: "./pdfjs/pdf.worker.js", kind: OptionKind.WORKER } }; { defaultOptions.defaultUrl = { - value: "/pdfjs/example/Welcome.pdf", + value: "./pdfjs/example/Welcome.pdf", kind: OptionKind.VIEWER }; defaultOptions.disablePreferences = { diff --git a/src/main/resources/templates/about.html b/src/main/resources/templates/about.html index 1ce5726f..2870fb41 100644 --- a/src/main/resources/templates/about.html +++ b/src/main/resources/templates/about.html @@ -1,22 +1,21 @@ - - - - - -
-
-
-

-
-
-
+ + + + + +
+
+ +

+
+
+
+
+
-
-
- - - + + \ No newline at end of file diff --git a/src/main/resources/templates/account.html b/src/main/resources/templates/account.html index b39da2f1..e8ed623a 100644 --- a/src/main/resources/templates/account.html +++ b/src/main/resources/templates/account.html @@ -1,326 +1,283 @@ - - + + + + + - - - +
-
-
-

-
-
-
+
+ +

+
+
+
- -

User Settings

-
-
- Default message if not found -
-
- Default message if not found -
-
- Default message if not found -
-
- Default message if not found -
- - - - - - - -

User!

- - - -

-
-
- - -
-
- - -
-
- -
-
- -
- - -

Change Password?

-
-
- - -
-
- - -
-
- - -
-
- -
-
- -
- -
-
- -
-
-
- -
- - - - - -
-
-
-
- - - - -
- -

Sync browser settings with Account

-
-

Settings Comparison:

- - - - - - - - - - - -
PropertyAccount SettingWeb Browser Setting
- -
- - -
-
- - - - - - - - - - - - - - - - - -
+ +

User Settings

+
+ +
+ Default message if not found +
+
+ Default message if not found +
+
+ Default message if not found +
+
+ Default message if not found +
+
+ Default message if not found +
+
+ +

User!

+ + + + +
+
+ +
-
+
+ + +
+
+ +
+ +
+ + +

Change Password?

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+ +
+
+
+
+ +
+ + + +
+
+
+
+ + + +
+ +

Sync browser settings with Account

+
+

Settings Comparison:

+ + + + + + + + + + + +
PropertyAccount SettingWeb Browser Setting
+ +
+ + +
+
+ + + +
+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/addUsers.html b/src/main/resources/templates/addUsers.html index ff3eb559..d5fb14ee 100644 --- a/src/main/resources/templates/addUsers.html +++ b/src/main/resources/templates/addUsers.html @@ -1,84 +1,111 @@ - + + + + - - - +
-
-
-

-
-
-
+
+ +

+
+
+
- -

Admin User Control Settings

+ +

Admin User Control Settings

+
+ Cannot delete currently logged in user. + The username does not exist and cannot be deleted. +
+ + + + + + + + + + + + + + + +
UsernameRolesActions
+
+ +
+
- - - - - - - - - - - - - - - - - - -
UsernameRolesActions
-
- -
-
- - - -

Add New User

-
- Default message if not found -
-
-
- - -
-
- - -
-
- - -
-
- - -
- - - -
-
+

Add New User

+
+ Default message if not found + Default message if not found +
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
-
+ + + +
+

Change User's Role

+ +
+ Username not found + Cannot downgrade current user's role +
+
+
+ + +
+
+ + +
+ + + +
+
+
-
+
+ +
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/auto-split-pdf.html b/src/main/resources/templates/auto-split-pdf.html index 304216b9..914b9d60 100644 --- a/src/main/resources/templates/auto-split-pdf.html +++ b/src/main/resources/templates/auto-split-pdf.html @@ -1,44 +1,43 @@ - + + + + - - - - +
-
-
-

-
-
-
-

- -

-
    -
  • -
  • -
  • -
  • -
-
-

-
-
- - -
-

-

- -
-
+
+ +

+
+
+
+

+ +

+
    +
  • +
  • +
  • +
  • +
+
+

+
+
+ +
+

+

+ +
- +
-
+
+
- + \ No newline at end of file diff --git a/src/main/resources/templates/change-creds.html b/src/main/resources/templates/change-creds.html index f5b86b67..08a3ade7 100644 --- a/src/main/resources/templates/change-creds.html +++ b/src/main/resources/templates/change-creds.html @@ -1,90 +1,78 @@ - - + + + + + - - - +
-
-
-

-
-
-
- - -

User Settings

-
- -
- Default message if not found -
-
- Default message if not found -
-
- Default message if not found -
-
- Default message if not found -
- - -

User!

- - - -

-

Change Username and password

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
- - -
- - - +
+ +

+
+
+
+ +

User Settings

+
+ +
+ Default message if not found +
+
+ Default message if not found +
+
+ Default message if not found +
+
+ Default message if not found +
+
+ +

User!

+ +

Change password

+
+
+ +
-
+
+ + +
+
+ + +
+
+ +
+ + +
+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/book-to-pdf.html b/src/main/resources/templates/convert/book-to-pdf.html index 67facbb3..acd40b21 100644 --- a/src/main/resources/templates/convert/book-to-pdf.html +++ b/src/main/resources/templates/convert/book-to-pdf.html @@ -1,29 +1,29 @@ - + + + + - - +
-
-
-

-
-
-
-

-
-
-
- -
-

-

-
-
+
+ +

+
+
+
+

+
+
+ +
+

+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/file-to-pdf.html b/src/main/resources/templates/convert/file-to-pdf.html index 774c2dc8..d8c8b969 100644 --- a/src/main/resources/templates/convert/file-to-pdf.html +++ b/src/main/resources/templates/convert/file-to-pdf.html @@ -1,37 +1,43 @@ - + + + + - - - - +
-
-
-

-
- +
+ +

+
+
+
+

+

+
+
+ +
+

+

+

Microsoft Word: (DOC, DOCX, DOT, DOTX)

+

Microsoft Excel: (CSV, XLS, XLSX, XLT, XLTX, SLK, DIF)

+

Microsoft PowerPoint: (PPT, PPTX)

+

OpenDocument Formats: (ODT, OTT, ODS, OTS, ODP, OTP, ODG, OTG)

+

Plain Text: (TXT, TEXT, XML)

+

Rich Text Format: (RTF)

+

Images: (BMP, GIF, JPEG, PNG, TIF, PBM, PGM, PPM, RAS, XBM, XPM, SVG, SVM, WMF)

+

HTML: (HTML)

+

Lotus Word Pro: (LWP)

+

StarOffice: (SDA, SDC, SDD, SDW, STC, STD, STI, STW, SXD, SXG, SXI, SXW)

+

Other: (DBF, FODS, VSD, VOR, VOR3, VOR4, UOP, PCT, PS, PDF)

+ https://help.libreoffice.org/latest/en-US/text/shared/guide/supported_formats.html
- +
-
+
+
- + \ No newline at end of file diff --git a/src/main/resources/templates/convert/html-to-pdf.html b/src/main/resources/templates/convert/html-to-pdf.html index f0d17e6e..3c426917 100644 --- a/src/main/resources/templates/convert/html-to-pdf.html +++ b/src/main/resources/templates/convert/html-to-pdf.html @@ -1,36 +1,35 @@ - + + + + - - +
-
-
-

-
-
-
-

-
-
- -
- - -
- -
- - -
-

-

-
+
+ +

+
+
+
+

+
+
+
+ +
+
+ +
+

+

+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/img-to-pdf.html b/src/main/resources/templates/convert/img-to-pdf.html index 60478409..a8633ad1 100644 --- a/src/main/resources/templates/convert/img-to-pdf.html +++ b/src/main/resources/templates/convert/img-to-pdf.html @@ -1,88 +1,82 @@ - + + + + - - - +
-
-
-

-
-
-
-

- -
- -
- -
- - -
- -
- - -
-
- - -
-
- -
- - -
- -

- - - -
-
+
+ +

+
+
+
+

+
+
+
+ +
-
+
+ + +
+
+ + +
+
+ +
+ + +
+

+ + + +
+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/markdown-to-pdf.html b/src/main/resources/templates/convert/markdown-to-pdf.html index 4606d2b5..7a51a29c 100644 --- a/src/main/resources/templates/convert/markdown-to-pdf.html +++ b/src/main/resources/templates/convert/markdown-to-pdf.html @@ -1,30 +1,30 @@ - + + + + - - +
-
-
-

-
-
-
-

-
-
-
- - -
-

-

-
-
+
+ +

+
+
+
+

+
+
+ +
+

+

+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-book.html b/src/main/resources/templates/convert/pdf-to-book.html index a136c5cf..33fbd2e1 100644 --- a/src/main/resources/templates/convert/pdf-to-book.html +++ b/src/main/resources/templates/convert/pdf-to-book.html @@ -1,55 +1,45 @@ - + + + + - - - - -
-
-
-

-
-
-
-

-
-
- -
- - -
-
- - -
-

-

-
-
-
-
-
-
- - + + +
+
+ +

+
+
+
+

+
+
+
+ + +
+ +
+

+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-csv.html b/src/main/resources/templates/convert/pdf-to-csv.html index 463e1a9c..9fba9430 100644 --- a/src/main/resources/templates/convert/pdf-to-csv.html +++ b/src/main/resources/templates/convert/pdf-to-csv.html @@ -1,159 +1,143 @@ - + + + + - - - - -
-
-
-

+ +
+
+ +

-
-
-

-
- -
- -
- - -
-
- - - - -
- -
- - +
+
+

+
+ +
+ +
+ +
+
+ +
+ +
+
+
+
+
-
-
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-html.html b/src/main/resources/templates/convert/pdf-to-html.html index 7360e631..c9256805 100644 --- a/src/main/resources/templates/convert/pdf-to-html.html +++ b/src/main/resources/templates/convert/pdf-to-html.html @@ -1,29 +1,30 @@ - + + + + - - +
-
-
-

-
-
-
-

-
-
-
- - -
-

-
-
+
+ +

+
+
+
+

+
+
+
+ +
+

+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-img.html b/src/main/resources/templates/convert/pdf-to-img.html index 52bf2e66..039c52e0 100644 --- a/src/main/resources/templates/convert/pdf-to-img.html +++ b/src/main/resources/templates/convert/pdf-to-img.html @@ -1,61 +1,58 @@ - + + + + - - - - +
-
-
- -

-
-
-
-

-

-
-
-
- - -
-
- - -
-
- - -
-
- - -
- -
- -
+
+ +

+
+
+
+

+

+
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+ +
- +
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-pdfa.html b/src/main/resources/templates/convert/pdf-to-pdfa.html index 5b0d5cf1..6f558a1e 100644 --- a/src/main/resources/templates/convert/pdf-to-pdfa.html +++ b/src/main/resources/templates/convert/pdf-to-pdfa.html @@ -1,32 +1,38 @@ - + + + + - - - - +
-
-
-

-
-
-
-

-

Currently doesn't work for multiple inputs at once

-
-
-
- -
-

-
-
+
+ +

+
+
+
+

+

+
+
+
+ + +
+
+ +
+

- +
-
+
+
- - \ No newline at end of file + + diff --git a/src/main/resources/templates/convert/pdf-to-presentation.html b/src/main/resources/templates/convert/pdf-to-presentation.html index a08c202b..48d27d24 100644 --- a/src/main/resources/templates/convert/pdf-to-presentation.html +++ b/src/main/resources/templates/convert/pdf-to-presentation.html @@ -1,38 +1,38 @@ - + + + + - - +
-
-
-

-
-
-
-

-
-
- -
- - -
-
- - -
-

-
+
+ +

+
+
+
+

+
+
+
+ +
+
+ +
+

+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-text.html b/src/main/resources/templates/convert/pdf-to-text.html index 8ad6e94a..52d461e0 100644 --- a/src/main/resources/templates/convert/pdf-to-text.html +++ b/src/main/resources/templates/convert/pdf-to-text.html @@ -1,35 +1,36 @@ - + + + + - - +
-
-
-

-
-
-
-

-
-
- -
- - -
-
- - -
-

-
+
+ +

+
+
+
+

+
+
+
+ +
+ +
+

+
+
+
+
-
-
+ + \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-word.html b/src/main/resources/templates/convert/pdf-to-word.html index 8ed0d9a8..fbbbf45d 100644 --- a/src/main/resources/templates/convert/pdf-to-word.html +++ b/src/main/resources/templates/convert/pdf-to-word.html @@ -1,41 +1,38 @@ - + + + + - - +
-
-
-

-
-
-
-

-
-
- -
- - -
-
- - -
-

-
+
+ +

+
+
+
+

+
+
+
+ +
+
+ +
+

+
-
+
+
- - - - - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-xml.html b/src/main/resources/templates/convert/pdf-to-xml.html index b26a97fe..cad43da2 100644 --- a/src/main/resources/templates/convert/pdf-to-xml.html +++ b/src/main/resources/templates/convert/pdf-to-xml.html @@ -1,29 +1,30 @@ - + + + + - - +
-
-
-

-
-
-
-

-
-
-
- - -
-

-
-
+
+ +

+
+
+
+

+
+
+
+ +
+

+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/convert/url-to-pdf.html b/src/main/resources/templates/convert/url-to-pdf.html index c74bd5d5..714bd1c4 100644 --- a/src/main/resources/templates/convert/url-to-pdf.html +++ b/src/main/resources/templates/convert/url-to-pdf.html @@ -1,29 +1,30 @@ - + + + + - - +
-
-
-

-
-
-
-

-
- -
- - -
-

-
-
+
+ +

+
+
+
+

+
+ +
+ +
+

+
-
+
+
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/crop.html b/src/main/resources/templates/crop.html index f0d2186f..f2049572 100644 --- a/src/main/resources/templates/crop.html +++ b/src/main/resources/templates/crop.html @@ -1,33 +1,33 @@ - - - - - - -
-
-
-

-
-
-
-

-
-
- - - - - -
-
- - -
- - - -
+
+
-
-
- - + + \ No newline at end of file diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html index 90e654f7..d896ecbc 100644 --- a/src/main/resources/templates/error.html +++ b/src/main/resources/templates/error.html @@ -1,131 +1,31 @@ - - + - Error! :( - - + -
-
- -
-
-
- -
-

Oops!

-

We can't seem to find the page you're looking for.

-

Something went wrong

- -
-

Need help / Found a issue?

-

If you're still having trouble, don't hesitate to reach out to us for help. You can submit a ticket on our GitHub page or contact us through Discord:

-
- Submit a ticket on GitHub - Join our Discord server +
+
+ +
+
+
+

+

+

+
+

+

+
+ + +
+
- Go back to homepage
+
-
-
- - - - + \ No newline at end of file diff --git a/src/main/resources/templates/extract-page.html b/src/main/resources/templates/extract-page.html index 77cc7496..9d97b3b4 100644 --- a/src/main/resources/templates/extract-page.html +++ b/src/main/resources/templates/extract-page.html @@ -1,33 +1,33 @@ - + + + + - - - - +
-
-
-

-
-
-
-

-
-
- -
- - -
- - -
-
+
+ +

+
+
+
+

+
+
+ +
+ +
+ + +
+
-
+
+
- + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/card.html b/src/main/resources/templates/fragments/card.html index 1d8193cd..3f5517d6 100644 --- a/src/main/resources/templates/fragments/card.html +++ b/src/main/resources/templates/fragments/card.html @@ -1,12 +1,12 @@ -
- -
- Icon -
-
-

-
-
- Favorite -
-
+
+ +
+ Icon +
+
+

+
+
+ Favorite +
+
diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html index 633810ef..9e879c4c 100644 --- a/src/main/resources/templates/fragments/common.html +++ b/src/main/resources/templates/fragments/common.html @@ -1,137 +1,162 @@ - + + + - - + + + + + + - - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + - - - + + + + - - - - + + - - + + - - + + - - + + + + + + + - - - - - - - + + + + + + + + + - + + + + + + - -
-
Lives: 3
-
Score: 0
-
High Score: 0
-
Level: 1
- -
- + +
+
Lives: 3
+
Score: 0
+
High Score: 0
+
Level: 1
+ favicon +
+
- - - + + + -
-
- -
-
-
+
+
+ +
+
+
- - - - - - - -
\ No newline at end of file + + +
diff --git a/src/main/resources/templates/fragments/errorBanner.html b/src/main/resources/templates/fragments/errorBanner.html index 1bc79031..a5070422 100644 --- a/src/main/resources/templates/fragments/errorBanner.html +++ b/src/main/resources/templates/fragments/errorBanner.html @@ -1,29 +1,11 @@ -
+ + diff --git a/src/main/resources/templates/fragments/footer.html b/src/main/resources/templates/fragments/footer.html index 219786ce..a7ed547f 100644 --- a/src/main/resources/templates/fragments/footer.html +++ b/src/main/resources/templates/fragments/footer.html @@ -1,58 +1,19 @@ -
-
- - + + Licenses + diff --git a/src/main/resources/templates/fragments/languages.html b/src/main/resources/templates/fragments/languages.html index 3ca0092f..57f00e8e 100644 --- a/src/main/resources/templates/fragments/languages.html +++ b/src/main/resources/templates/fragments/languages.html @@ -1,107 +1,30 @@ - icon Български - - icon العربية - - icon Català - - icon 简体中文 - - icon 正體中文 - - icon Deutsch - - icon English (GB) - - icon English (US) - - icon Euskara - - icon Español - - icon Français - - icon Indonesia - - icon Italiano - - icon Nederlands - - icon Polski - - icon Português (BR) - - icon Romanian - - icon Svenska - - icon Türkçe - - icon Русский - - icon 한국어 - - icon 日本語 - - icon Ελληνικά - - icon Hungarian - - icon हिन्दी - - icon Srpski - - + icon Български + icon العربية + icon Català + icon 简体中文 + icon 繁體中文 + icon Deutsch + icon English (GB) + icon English (US) + icon Euskara + icon Español + icon Français + icon Indonesia + icon Italiano + icon Nederlands + icon Polski + icon Português (BR) + icon Português (PT) + icon Romanian + icon Svenska + icon Türkçe + icon Русский + icon 한국어 + icon 日本語 + icon Ελληνικά + icon Hungarian + icon हिन्दी + icon Srpski + icon Українська diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html index d75745da..8b412a6b 100644 --- a/src/main/resources/templates/fragments/navbar.html +++ b/src/main/resources/templates/fragments/navbar.html @@ -1,315 +1,231 @@
- - - - - - - - - -
- + +
\ No newline at end of file diff --git a/src/main/resources/templates/fragments/navbarEntry.html b/src/main/resources/templates/fragments/navbarEntry.html index 1cd33be6..f0a55c16 100644 --- a/src/main/resources/templates/fragments/navbarEntry.html +++ b/src/main/resources/templates/fragments/navbarEntry.html @@ -1,6 +1,6 @@ - + + + icon + + + diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 04426900..83528d4f 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -1,115 +1,113 @@ - - + + + + + - - - - - - - -
-
-
+ +
+
+
-
-

-

-
+
+

+

+

-
- -
- - -
-
- -
-
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
- -
-
-
- - -
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
- - - - +
+ +
-
-
- +
- +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/licenses.html b/src/main/resources/templates/licenses.html index fc28ad1a..f9d6c641 100644 --- a/src/main/resources/templates/licenses.html +++ b/src/main/resources/templates/licenses.html @@ -1,56 +1,39 @@ - + + + + - - - - - -
-
-
-

- -
-
-
-

3rd Party licenses

- - - - - - - - - - - - - - - -
ModuleVersionLicense
- -
-
-
-
-
-
- - + +
+
+ +

+
+
+
+

3rd Party licenses

+ + + + + + + + + + + + + + + +
ModuleVersionLicense
+
+
+
+
+ +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 3d549ac7..8cd47550 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -1,313 +1,186 @@ - - + + + + + + - - - - -
-
-
-
- - - -
+ const dropdown = document.getElementById('languageDropdown'); + const dropdownItems = document.querySelectorAll('.lang_dropdown-item'); -
- Default message if not found -
-
- -

Stirling-PDF

-

Please sign in

+ let activeItem; + for (let i = 0; i < dropdownItems.length; i++) { + const item = dropdownItems[i]; + item.classList.remove('active'); + if (item.dataset.bsLanguageCode === storedLocale) { + item.classList.add('active'); + activeItem = item; + } + item.addEventListener('click', handleDropdownItemClick); + } -
- -
-
- -
+ if (activeItem) { + dropdown.innerHTML = activeItem.innerHTML; // This will set the dropdown button's content to the active language's flag and name + } -
- -
- -
-
- -
+ // Additional functionality that was in your provided code: + document.querySelectorAll('.nav-item.dropdown').forEach((element) => { + const dropdownMenu = element.querySelector(".dropdown-menu"); + if (dropdownMenu.id !== 'favoritesDropdown' && dropdownMenu.children.length <= 2 && dropdownMenu.querySelectorAll("hr.dropdown-divider").length === dropdownMenu.children.length) { + if (element.previousElementSibling && element.previousElementSibling.classList.contains('nav-item') && element.previousElementSibling.classList.contains('nav-item-separator')) { + element.previousElementSibling.remove(); + } + element.remove(); + } + }); + // Sort languages by alphabet + const list = Array.from(document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').children).filter(child => child.matches('a')); + list.sort(function(a, b) { + var A = a.textContent.toUpperCase(); + var B = b.textContent.toUpperCase(); + return (A < B) ? -1 : (A > B) ? 1 : 0; + }).forEach(node => document.querySelector('.dropdown-menu[aria-labelledby="languageDropdown"]').appendChild(node)); + }); -
-
Invalid username or - password.
-
Your account has been locked. -
-
+ function handleDropdownItemClick(event) { + event.preventDefault(); + const languageCode = event.currentTarget.dataset.bsLanguageCode; + const dropdown = document.getElementById('languageDropdown'); -
+ if (languageCode) { + localStorage.setItem('languageCode', languageCode); + const currentLang = document.documentElement.getAttribute('language'); + if (currentLang !== languageCode) { + console.log("currentLang", currentLang) + console.log("languageCode", languageCode) + const currentUrl = window.location.href; + if (currentUrl.indexOf("?lang=") === -1 && currentUrl.indexOf("&lang=") === -1) { + window.location.href = currentUrl + "?lang=" + languageCode; + } else if (currentUrl.indexOf("&lang=") !== -1 && currentUrl.indexOf("?lang=") === -1) { + window.location.href = currentUrl.replace(/&lang=\w{2,}/, "&lang=" + languageCode); + } else { + window.location.href = currentUrl.replace(/\?lang=\w{2,}/, "?lang=" + languageCode); + } + } + dropdown.innerHTML = event.currentTarget.innerHTML; // Update the dropdown button's content + } else { + console.error("Language code is not set for this item."); + } + } + +
+
+ Default message if not found +
+
+ +

Stirling-PDF

+
+ Login Via SSO +
+
OAUTH2 Auto-Create User Disabled.
+
+
+
+

Please sign in

+
+ + +
+
+ + +
-
-
- - - - +
+ + +
+ + +
+ +
+
+
Invalid username or password.
+
Your account has been locked.
+
+ + +
+ + \ No newline at end of file diff --git a/src/main/resources/templates/merge-pdfs.html b/src/main/resources/templates/merge-pdfs.html index d97d88d9..10a0bb06 100644 --- a/src/main/resources/templates/merge-pdfs.html +++ b/src/main/resources/templates/merge-pdfs.html @@ -1,40 +1,39 @@ - + + + + + - - - - +
-
-
-

-
-
-
-

-
-
- -
- -
-
-
    -
    -
    - - - -
    -
    - - -
    +
    + +

    +
    +
    +
    +

    +
    +
    + +
    +
    +
      +
      +
      + + + +
      +
      +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/add-image.html b/src/main/resources/templates/misc/add-image.html index ae531a8f..86bc6bab 100644 --- a/src/main/resources/templates/misc/add-image.html +++ b/src/main/resources/templates/misc/add-image.html @@ -1,141 +1,111 @@ - - - - + + + + + - - +
      -
      -
      -

      -
      -
      -
      -

      +
      + +

      +
      +
      +
      +

      - -
      - + document.querySelectorAll(".show-on-file-selected").forEach(el => { + el.style.cssText = ''; + }); + } + }); + document.addEventListener("DOMContentLoaded", () => { + document.querySelectorAll(".show-on-file-selected").forEach(el => { + el.style.cssText = "display:none !important"; + }) + }); + -
      -
      -
      - -
      -
      - - -
      - - -
      - - - -
      - -
      - - -
      - -
      - -
      +
      +
      +
      +
      +
      + + +
      + + +
      + + + +
      +
      + + +
      + +
      +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/add-page-numbers.html b/src/main/resources/templates/misc/add-page-numbers.html index e484a72d..0d02d456 100644 --- a/src/main/resources/templates/misc/add-page-numbers.html +++ b/src/main/resources/templates/misc/add-page-numbers.html @@ -1,154 +1,137 @@ - + + + + + -#myForm { - display: flex; - justify-content: center; - align-items: center; - margin-top: 20px; -} + + +
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      +
      + + +
      +
      + +
      +
      1
      +
      2
      +
      3
      +
      4
      +
      5
      +
      6
      +
      7
      +
      8
      +
      9
      +
      +
      + +
      + + +
      +
      + + +
      +
      + + +
      + +
      +
      +
      +
      + - -
      -
      -
      - - + cell.addEventListener('mouseleave', function(e) { + if(e.target.classList.contains('selectedPosition')) { + e.target.classList.remove('selectedHovered'); + } + }); + }); + +
      + +
      + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/adjust-contrast.html b/src/main/resources/templates/misc/adjust-contrast.html index 23effdd0..8fc3b661 100644 --- a/src/main/resources/templates/misc/adjust-contrast.html +++ b/src/main/resources/templates/misc/adjust-contrast.html @@ -1,310 +1,292 @@ - + + + + + - + +
      +
      + +

      +
      +
      +
      +
      +
      + +
      +
      +
      + +
      + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/auto-crop.html b/src/main/resources/templates/misc/auto-crop.html index da6e6c22..06e7874f 100644 --- a/src/main/resources/templates/misc/auto-crop.html +++ b/src/main/resources/templates/misc/auto-crop.html @@ -1,31 +1,30 @@ - + + + + - - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      - -
      -

      -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + +
      +

      - +
      -
      +
      +
      - + \ No newline at end of file diff --git a/src/main/resources/templates/misc/auto-rename.html b/src/main/resources/templates/misc/auto-rename.html index 0295acac..42876df3 100644 --- a/src/main/resources/templates/misc/auto-rename.html +++ b/src/main/resources/templates/misc/auto-rename.html @@ -1,30 +1,29 @@ - + + + + - - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      - -
      -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + +
      - +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/change-metadata.html b/src/main/resources/templates/misc/change-metadata.html index 6affa6bf..b620755c 100644 --- a/src/main/resources/templates/misc/change-metadata.html +++ b/src/main/resources/templates/misc/change-metadata.html @@ -1,261 +1,227 @@ - + + + + - - - +
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      -

      - -
      - - -
      - -
      - - -
      - -
      - - -
      - -
      - - -
      - -
      - - -
      - -
      - - -
      - -
      - - -
      - -
      - - -
      - -
      - - -
      - -
      - - -
      - -
      - - -
      - -
      - -
      -
      - - -
      - -
      +
      + +

      +
      +
      +
      +

      +
      +
      +

      +
      + +
      +
      + + +
      +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + +
      +
      + + +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/compare.html b/src/main/resources/templates/misc/compare.html index ffe08a31..fd9ff958 100644 --- a/src/main/resources/templates/misc/compare.html +++ b/src/main/resources/templates/misc/compare.html @@ -1,190 +1,187 @@ - + + + + + - - - - - +
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      - - - -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      - - -
      +
      + +

      +
      +
      +
      +

      +
      +
      + +
      +
      +

      +
      +
      +

      +
      +
      +
      +
      +
      -
      +
      +
      + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/compress-pdf.html b/src/main/resources/templates/misc/compress-pdf.html index c991a6c8..3638e3af 100644 --- a/src/main/resources/templates/misc/compress-pdf.html +++ b/src/main/resources/templates/misc/compress-pdf.html @@ -1,50 +1,47 @@ - + + + + - - - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      -
      -

      - - -
      -
      - -
      -
      -

      - - -
      -
      - -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      +
      +

      + + +
      +
      +
      +

      + + +
      +
      + +
      +
      -
      +
      +
      - - + \ No newline at end of file diff --git a/src/main/resources/templates/misc/extract-image-scans.html b/src/main/resources/templates/misc/extract-image-scans.html index 82f7313c..90623b22 100644 --- a/src/main/resources/templates/misc/extract-image-scans.html +++ b/src/main/resources/templates/misc/extract-image-scans.html @@ -1,54 +1,54 @@ - + + + + - - - - -
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      -
      - - - -
      -
      - - - -
      -
      - - - -
      -
      - - - -
      -
      - - - -
      - -
      + + +
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + + + +
      +
      + + + +
      +
      + + + +
      +
      + + + +
      +
      + + + +
      + +
      +
      +
      -
      -
      - + \ No newline at end of file diff --git a/src/main/resources/templates/misc/extract-images.html b/src/main/resources/templates/misc/extract-images.html index e813e680..12d2f64e 100644 --- a/src/main/resources/templates/misc/extract-images.html +++ b/src/main/resources/templates/misc/extract-images.html @@ -1,36 +1,36 @@ - + + + + - - - - -
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      -
      - - - + +
      + +
      - -
      +
      -
      -
      - + \ No newline at end of file diff --git a/src/main/resources/templates/misc/fake-scan.html b/src/main/resources/templates/misc/fake-scan.html new file mode 100644 index 00000000..97d681a1 --- /dev/null +++ b/src/main/resources/templates/misc/fake-scan.html @@ -0,0 +1,29 @@ + + + + + + + + +
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/flatten.html b/src/main/resources/templates/misc/flatten.html index 430e343b..bbc06f58 100644 --- a/src/main/resources/templates/misc/flatten.html +++ b/src/main/resources/templates/misc/flatten.html @@ -1,57 +1,32 @@ - + + + + - - - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      -
      - - - - -
      -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + + +
      +
      + +
      +
      -
      +
      +
      - - + \ No newline at end of file diff --git a/src/main/resources/templates/misc/ocr-pdf.html b/src/main/resources/templates/misc/ocr-pdf.html index 9aabdec4..a5374a22 100644 --- a/src/main/resources/templates/misc/ocr-pdf.html +++ b/src/main/resources/templates/misc/ocr-pdf.html @@ -1,254 +1,249 @@ - - - - - + + + - + - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      -
      -
      - - -
      -
      - -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      - - -
      - - -
      -
      - -
      - -

      -

      - https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md +
      + +

      +
      +
      +
      +

      +
      +
      +
      + +
      +
      +
      + +
      +
      +
      +
      + + +
      +
      + +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + +
      + +

      +

      + https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR.md
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/print-file.html b/src/main/resources/templates/misc/print-file.html new file mode 100644 index 00000000..056d7328 --- /dev/null +++ b/src/main/resources/templates/misc/print-file.html @@ -0,0 +1,35 @@ + + + + + + + + +
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      +
      +

      Select Printer

      + + +
      +
      + +
      +
      +
      +
      +
      + +
      + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/remove-annotations.html b/src/main/resources/templates/misc/remove-annotations.html index cbd46be4..a9973b79 100644 --- a/src/main/resources/templates/misc/remove-annotations.html +++ b/src/main/resources/templates/misc/remove-annotations.html @@ -1,64 +1,65 @@ - - + + + + - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      -
      - -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + +
      +
      -
      +
      +
      - - - + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/remove-blanks.html b/src/main/resources/templates/misc/remove-blanks.html index b2c8e358..6ab25907 100644 --- a/src/main/resources/templates/misc/remove-blanks.html +++ b/src/main/resources/templates/misc/remove-blanks.html @@ -1,39 +1,37 @@ - + + + + - - - - -
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      -
      - - - -
      -
      - - - -
      - -
      + +
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + + + +
      +
      + + + +
      + +
      +
      +
      -
      -
      - - - + + \ No newline at end of file diff --git a/src/main/resources/templates/misc/repair.html b/src/main/resources/templates/misc/repair.html index 10d5d148..204bce14 100644 --- a/src/main/resources/templates/misc/repair.html +++ b/src/main/resources/templates/misc/repair.html @@ -1,29 +1,27 @@ - + + + + - - - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      - -
      -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      + +
      +
      -
      +
      +
      - - + \ No newline at end of file diff --git a/src/main/resources/templates/misc/show-javascript.html b/src/main/resources/templates/misc/show-javascript.html index 0614a2c2..ba60ec00 100644 --- a/src/main/resources/templates/misc/show-javascript.html +++ b/src/main/resources/templates/misc/show-javascript.html @@ -1,100 +1,93 @@ - - - - - -
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      - + + + + + + + -
      -
      - -
      - -
      + +
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + +
      +
      + +
      + +
      - - -
      - - -
      -
      -
      - -
      -
      -
      - + // Set the URL as the href of the download button and provide a download name + let downloadButton = document.querySelector('#downloadJS'); + downloadButton.href = url; + downloadButton.download = 'extracted.js'; + downloadButton.style.display = 'block'; + }) + .catch(error => { + console.error('Error:', error); + }); + }); + +
      +
      +
      +
      + +
      + \ No newline at end of file diff --git a/src/main/resources/templates/misc/stamp.html b/src/main/resources/templates/misc/stamp.html index 1578de36..89e689b1 100644 --- a/src/main/resources/templates/misc/stamp.html +++ b/src/main/resources/templates/misc/stamp.html @@ -1,222 +1,177 @@ - + + + + + - + + +
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      +
      + + +
      + +
      + + +
      + +
      + +
      +
      1
      +
      2
      +
      3
      +
      4
      +
      5
      +
      6
      +
      7
      +
      8
      +
      9
      +
      +
      + + + +
      + + +
      + +
      + + +
      + + + +
      + + +
      + +
      + + +
      + +
      + + +
      + +
      + + +
      - - -
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      +
      + + +
      -
      - - -
      +
      + + +
      -
      - - -
      - - - -
      - -
      -
      1
      -
      2
      -
      3
      -
      4
      -
      5
      -
      6
      -
      7
      -
      8
      -
      9
      -
      -
      - - - - - -
      - - + + +
      +
      + -
      -
      -
      - - + if (stampType === 'text') { + stampTextGroup.style.display = 'block'; + stampImageGroup.style.display = 'none'; + alphabetGroup.style.display = 'block'; + } else if (stampType === 'image') { + stampTextGroup.style.display = 'none'; + stampImageGroup.style.display = 'block'; + alphabetGroup.style.display = 'none'; + } + } + +
      + +
      + + \ No newline at end of file diff --git a/src/main/resources/templates/multi-page-layout.html b/src/main/resources/templates/multi-page-layout.html index 646f7dc0..0e51b8fa 100644 --- a/src/main/resources/templates/multi-page-layout.html +++ b/src/main/resources/templates/multi-page-layout.html @@ -1,43 +1,41 @@ - + + + + - - - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      - - -
      -
      - - -
      - -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + +
      +
      + + +
      + +
      +
      -
      +
      +
      - - + \ No newline at end of file diff --git a/src/main/resources/templates/multi-tool.html b/src/main/resources/templates/multi-tool.html index d49b7f83..170ba7f3 100644 --- a/src/main/resources/templates/multi-tool.html +++ b/src/main/resources/templates/multi-tool.html @@ -1,233 +1,104 @@ - + + + + - - - +
      -
      -
      -

      -
      -
      -

      -
      -
      -
      -
      - -
      - PDF Page -
      -
      -
      -
      -
      -
      -
      -
      -
      -
      - - -
      -
      -
      - - - - - - - - - - -
      -
      +
      + +

      +
      +
      +

      +
      +
      +
      +
      + +
      + PDF Page
      +
      +
      -
      +
      +
      +
      +
      +
      + + +
      +
      +
      + + + + +
      +
      +
      +
      +
      +
      +
      -
      - - - - - + + \ No newline at end of file diff --git a/src/main/resources/templates/overlay-pdf.html b/src/main/resources/templates/overlay-pdf.html index cab73486..10cd43d9 100644 --- a/src/main/resources/templates/overlay-pdf.html +++ b/src/main/resources/templates/overlay-pdf.html @@ -1,102 +1,96 @@ - + + + + - - - - +
      -
      -
      -

      -
      -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      - -

      - -
      -
      - - - -
      - - -
      - - - -
      - - - - - -
      + + +
      + + +
      + -
      + + + +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/pdf-organizer.html b/src/main/resources/templates/pdf-organizer.html index 4c033e35..4466fa23 100644 --- a/src/main/resources/templates/pdf-organizer.html +++ b/src/main/resources/templates/pdf-organizer.html @@ -1,57 +1,56 @@ - + + + + - - - - +
      -
      -
      -

      -
      -
      -
      -

      +
      + +

      +
      +
      +
      +

      -
      -
      -
      - - -
      -
      - - -
      - - -
      - -
      +
      +
      +
      + +
      +
      + + +
      + +
      +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/pdf-to-single-page.html b/src/main/resources/templates/pdf-to-single-page.html index 6c26ace5..0dc8761c 100644 --- a/src/main/resources/templates/pdf-to-single-page.html +++ b/src/main/resources/templates/pdf-to-single-page.html @@ -1,28 +1,27 @@ - + + + + - - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      - -
      -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      + +
      - +
      -
      +
      +
      - + \ No newline at end of file diff --git a/src/main/resources/templates/pipeline.html b/src/main/resources/templates/pipeline.html index d70c52ff..b59d68c0 100644 --- a/src/main/resources/templates/pipeline.html +++ b/src/main/resources/templates/pipeline.html @@ -1,254 +1,107 @@ - - - - - - + - -
      -
      -
      + +
      +
      + +

      +
      +
      -

      -
      -
      +
      +

      + icon +
      + +
      +
      + + +
      -

      -

      -
      -
      - - +
      +
      + +
      +
      +
      +
      +
      + +
      + Pipeline Help +
      + Folder Scanning Help +
      +
      -
      + + + + -
      -
      - -
      -
      - - - +
      + +
      +
      +
      +
      +
      + +
      +
      +
      + +
      + + \ No newline at end of file diff --git a/src/main/resources/templates/remove-pages.html b/src/main/resources/templates/remove-pages.html index 1449fc2e..cd9e57d5 100644 --- a/src/main/resources/templates/remove-pages.html +++ b/src/main/resources/templates/remove-pages.html @@ -1,38 +1,37 @@ - + + + + - + +
      +
      + +

      +
      +
      +
      +

      - - - -
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      -
      - - -
      - -
      +
      +
      +
      + + +
      + +
      +
      +
      -
      -
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/rotate-pdf.html b/src/main/resources/templates/rotate-pdf.html index 72cb280d..5446ff92 100644 --- a/src/main/resources/templates/rotate-pdf.html +++ b/src/main/resources/templates/rotate-pdf.html @@ -1,132 +1,102 @@ - + + + + - - - - - +
      -
      -
      -

      -
      -
      -
      -

      +
      + +

      +
      +
      +
      +

      -
      -
      - + +
      + -
      +
      + + + +
      +
      +
      -
      +
      +
      - - - - - + function rotate(deg) { + const preview = document.getElementById("pdf-preview"); + var lastTransform = preview.style.rotate; + if (!lastTransform) { + lastTransform = "0"; + } + const lastAngle = parseInt(lastTransform.replace(/[^\d-]/g, '')); + const newAngle = lastAngle + deg; + preview.style.rotate = newAngle + "deg"; + angleInput.value = newAngle; + } + + + \ No newline at end of file diff --git a/src/main/resources/templates/scale-pages.html b/src/main/resources/templates/scale-pages.html index 1165f862..ede08025 100644 --- a/src/main/resources/templates/scale-pages.html +++ b/src/main/resources/templates/scale-pages.html @@ -1,47 +1,45 @@ - + + + + - - - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      - - -
      -
      - - -
      - -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + +
      +
      + + +
      + +
      +
      -
      +
      +
      - - + \ No newline at end of file diff --git a/src/main/resources/templates/security/add-password.html b/src/main/resources/templates/security/add-password.html index 5c09720f..015cee5c 100644 --- a/src/main/resources/templates/security/add-password.html +++ b/src/main/resources/templates/security/add-password.html @@ -1,86 +1,85 @@ - + + + + - - - +
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      - -
      -
      -
      - - -
      -
      - - -
      - -
      - -
      -
      - -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      - -
      -
      -
      - -
      - -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      + +
      +
      + + +
      +
      + + +
      + +
      + + +
      +
      + +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      +
      +
      + +
      +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/security/add-watermark.html b/src/main/resources/templates/security/add-watermark.html index f61dcabf..2840946e 100644 --- a/src/main/resources/templates/security/add-watermark.html +++ b/src/main/resources/templates/security/add-watermark.html @@ -1,144 +1,142 @@ - + + + + - - - +
      -
      -
      -

      -
      -
      -
      -

      +
      + +

      +
      +
      +
      +

      -
      -
      - -
      - -
      -
      - -
      - - -
      -
      - - -
      -
      - - -
      - - - -
      - - -
      -
      - - - -
      - - - - -
      - - -
      -
      - - -
      -
      - - -
      -
      - -
      -
      - - - -
      +
      +
      + +
      + +
      + +
      + + +
      +
      + + +
      +
      + + +
      + + + +
      + + +
      +
      + + + +
      + + +
      + + +
      +
      + + +
      +
      + + +
      +
      + +
      +
      + +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/security/auto-redact.html b/src/main/resources/templates/security/auto-redact.html index 791b5da0..d13fcb88 100644 --- a/src/main/resources/templates/security/auto-redact.html +++ b/src/main/resources/templates/security/auto-redact.html @@ -1,84 +1,83 @@ - + + + + - - -
      -
      -
      -

      -
      -
      -
      -

      -
      -
      - -
      + +
      +
      + +

      +
      +
      +
      +

      + +
      + +
      -
      - - -
      +
      + + +
      -
      - - -
      +
      + + +
      - - + + - + +
      + + +
      +
      + + +
      +
      + + +
      -
      - - -
      +
      + + +
      -
      - - -
      - -
      - - -
      - -
      - - -
      - - - + + +
      +
      -
      -
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/security/cert-sign.html b/src/main/resources/templates/security/cert-sign.html index 20148355..0f369c27 100644 --- a/src/main/resources/templates/security/cert-sign.html +++ b/src/main/resources/templates/security/cert-sign.html @@ -1,113 +1,109 @@ - - + + + + - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      - -
      -
      - -
      - -
      -
      - -
      - - - -
      - -
      -
      - -
      - -
      - -
      -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      + +
      + +
      + +
      +
      + + +
      + + + +
      + + +
      +
      + + +
      + +
      + +
      +
      +
      -
      +
      +
      - - - - + + \ No newline at end of file diff --git a/src/main/resources/templates/security/change-permissions.html b/src/main/resources/templates/security/change-permissions.html index cf0cd3b0..58a5350c 100644 --- a/src/main/resources/templates/security/change-permissions.html +++ b/src/main/resources/templates/security/change-permissions.html @@ -1,71 +1,69 @@ - + + + + - - - - +
      -
      -
      -

      -
      -
      -
      -

      -

      -
      -
      - -
      -
      -
      - -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      - -
      -
      -
      - -
      - -
      -
      +
      + +

      +
      +
      +
      +

      +

      +
      +
      + +
      +
      + +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      +
      +
      + +
      +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/security/get-info-on-pdf.html b/src/main/resources/templates/security/get-info-on-pdf.html index 317c4e5e..ff7106ed 100644 --- a/src/main/resources/templates/security/get-info-on-pdf.html +++ b/src/main/resources/templates/security/get-info-on-pdf.html @@ -1,165 +1,152 @@ - - - -
      -
      -
      -

      -
      -
      -
      -

      -
      -
      -
      - + + + + -
      -
      - -
      - -
      + +
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      + +
      +
      + +
      + +
      - - -
      - -
      -
      -
      - -
      -
      -
      - + card.appendChild(content); + return card; + } + function createButtonElement(key, safeKey, depth) { + const buttonElem = document.createElement('button'); + buttonElem.className = 'btn btn-link'; + buttonElem.type = 'button'; + buttonElem.dataset.bsToggle = "collapse"; + buttonElem.dataset.bsTarget = `#${safeKey}-content-${depth}`; + buttonElem.setAttribute('aria-expanded', 'true'); + buttonElem.setAttribute('aria-controls', `${safeKey}-content-${depth}`); + buttonElem.textContent = key; + return buttonElem; + } + const collapsibleElems = document.querySelectorAll('[data-bs-toggle="collapse"]'); + collapsibleElems.forEach(elem => { + new bootstrap.Collapse(elem); + }); + +
      +
      +
      +
      + +
      + \ No newline at end of file diff --git a/src/main/resources/templates/security/remove-password.html b/src/main/resources/templates/security/remove-password.html index fe0c4a3c..b77f0690 100644 --- a/src/main/resources/templates/security/remove-password.html +++ b/src/main/resources/templates/security/remove-password.html @@ -1,37 +1,37 @@ - + + + + - - - +
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      - -
      -
      -
      - - -
      -
      -
      - -
      -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      + +
      +
      + + +
      +
      +
      + +
      +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/security/remove-watermark.html b/src/main/resources/templates/security/remove-watermark.html index a87a1bd7..01d89be5 100644 --- a/src/main/resources/templates/security/remove-watermark.html +++ b/src/main/resources/templates/security/remove-watermark.html @@ -1,36 +1,36 @@ - + + + + - - - +
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      - -
      -
      -
      - - -
      -
      - -
      -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      + +
      +
      + + +
      +
      + +
      +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/security/sanitize-pdf.html b/src/main/resources/templates/security/sanitize-pdf.html index 8d393cba..da1dc3c4 100644 --- a/src/main/resources/templates/security/sanitize-pdf.html +++ b/src/main/resources/templates/security/sanitize-pdf.html @@ -1,53 +1,52 @@ - + + + + - - - +
      -
      -
      -

      -
      -
      -
      -

      - -
      -
      -
      -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      - - -
      -
      -
      - -
      - -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      + + +
      +
      +
      + +
      +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/sign.html b/src/main/resources/templates/sign.html index 48d39e41..259d5e97 100644 --- a/src/main/resources/templates/sign.html +++ b/src/main/resources/templates/sign.html @@ -1,333 +1,273 @@ - + + + + + + + + - - - - - - - - - +
      -
      -
      -

      -
      -
      -
      -

      +
      + +

      +
      +
      +
      +

      - -
      - + document.querySelectorAll(".show-on-file-selected").forEach(el => { + el.style.cssText = ''; + }) + } + }); + document.addEventListener("DOMContentLoaded", () => { + document.querySelectorAll(".show-on-file-selected").forEach(el => { + el.style.cssText = "display:none !important"; + }) + }); + +
      +
      +
      + +
      +
      + +
      + + + -
      -
      - -
      - - - - -
      -
      - - - - -
      - -
      - - - - - - - - - -
      -
      - - -
      - - -
      - - - -
      - -
      - - -
      - -
      - - + // This library does not listen for canvas changes, so after the canvas is automatically + // cleared by the browser, SignaturePad#isEmpty might still return false, even though the + // canvas looks empty, because the internal data of this library wasn't cleared. To make sure + // that the state of this library is consistent with visual state of the canvas, you + // have to clear it manually. + signaturePad.clear(); + } + new IntersectionObserver((entries, observer) => { + if (entries.some(entry => entry.intersectionRatio > 0)) { + resizeCanvas(); + } + }).observe(signaturePadCanvas); + new ResizeObserver(resizeCanvas).observe(signaturePadCanvas); + +
      +
      + + + + +
      +
      + +
      +
      + + +
      + + +
      + + + +
      +
      + + +
      + +
      + +
      +
      -
      +
      +
      - + \ No newline at end of file diff --git a/src/main/resources/templates/split-by-size-or-count.html b/src/main/resources/templates/split-by-size-or-count.html index 0b6f9b52..a6bf13e0 100644 --- a/src/main/resources/templates/split-by-size-or-count.html +++ b/src/main/resources/templates/split-by-size-or-count.html @@ -1,43 +1,38 @@ - + + + + - - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      - - - -
      - - - -
      - - -
      - -
      -
      +
      + +

      +
      +
      +
      +

      +
      +
      + + +
      + + +
      + +
      - +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/split-pdf-by-sections.html b/src/main/resources/templates/split-pdf-by-sections.html index 94900977..9fb0eae4 100644 --- a/src/main/resources/templates/split-pdf-by-sections.html +++ b/src/main/resources/templates/split-pdf-by-sections.html @@ -1,95 +1,83 @@ - + + + + + - - - +
      -
      -
      -

      -
      -
      -
      -

      -
      -
      +
      + +

      +
      +
      +
      +

      + +
      + + +
      + + +
      + + +
      +
      + -
      - - - -
      -
      + // Initial draw + updateVisualAid(); + +
      + +
      - +
      -
      +
      +
      - + \ No newline at end of file diff --git a/src/main/resources/templates/split-pdfs.html b/src/main/resources/templates/split-pdfs.html index 872dc323..7ed934dd 100644 --- a/src/main/resources/templates/split-pdfs.html +++ b/src/main/resources/templates/split-pdfs.html @@ -1,43 +1,42 @@ - - - - - - + + + + +
      -
      -
      -

      -
      -
      -
      -

      -

      -

      -

      -

      -

      -

      -

      -

      +
      + +

      +
      +
      +
      +

      +

      +

      +

      +

      +

      +

      +

      +

      -
      -
      + +
      -
      - - -
      -
      - -
      -
      +
      + +
      +
      + +
      +
      -
      +
      +
      - - + + \ No newline at end of file diff --git a/src/main/resources/templates/view-pdf.html b/src/main/resources/templates/view-pdf.html index 8aca4542..5d08d597 100644 --- a/src/main/resources/templates/view-pdf.html +++ b/src/main/resources/templates/view-pdf.html @@ -32,11 +32,11 @@ See https://github.com/adobe-type-tools/cmap-resources - - + + - - + + diff --git a/src/test/java/stirling/software/SPDF/utils/GeneralUtilsTest.java b/src/test/java/stirling/software/SPDF/utils/GeneralUtilsTest.java new file mode 100644 index 00000000..14f14c82 --- /dev/null +++ b/src/test/java/stirling/software/SPDF/utils/GeneralUtilsTest.java @@ -0,0 +1,105 @@ +package stirling.software.SPDF.utils; + +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import java.util.List; + + +public class GeneralUtilsTest { + + + + @Test + void testParsePageListWithAll() { + List result = GeneralUtils.parsePageList(new String[]{"all"}, 5, false); + assertEquals(List.of(0, 1, 2, 3, 4), result, "'All' keyword should return all pages."); + } + + @Test + void testParsePageListWithAllOneBased() { + List result = GeneralUtils.parsePageList(new String[]{"all"}, 5, true); + assertEquals(List.of(1, 2, 3, 4, 5), result, "'All' keyword should return all pages."); + } + + @Test + void nFunc() { + List result = GeneralUtils.parsePageList(new String[]{"n"}, 5, true); + assertEquals(List.of(1, 2, 3, 4, 5), result, "'n' keyword should return all pages."); + } + + @Test + void nFuncAdvanced() { + List result = GeneralUtils.parsePageList(new String[]{"4n"}, 9, true); + //skip 0 as not valid + assertEquals(List.of(4,8), result, "'All' keyword should return all pages."); + } + + @Test + void nFuncAdvancedZero() { + List result = GeneralUtils.parsePageList(new String[]{"4n"}, 9, false); + //skip 0 as not valid + assertEquals(List.of(3,7), result, "'All' keyword should return all pages."); + } + + @Test + void nFuncAdvanced2() { + List result = GeneralUtils.parsePageList(new String[]{"4n-1"}, 9, true); + // skip -1 as not valid + assertEquals(List.of(3,7), result, "4n-1 should do (0-1), (4-1), (8-1)"); + } + + @Test + void nFuncAdvanced3() { + List result = GeneralUtils.parsePageList(new String[]{"4n+1"}, 9, true); + assertEquals(List.of(1,5,9), result, "'All' keyword should return all pages."); + } + + + @Test + void nFuncAdvanced4() { + List result = GeneralUtils.parsePageList(new String[]{"3+2n"}, 9, true); + assertEquals(List.of(3,5,7,9), result, "'All' keyword should return all pages."); + } + + @Test + void nFuncAdvancedZerobased() { + List result = GeneralUtils.parsePageList(new String[]{"4n"}, 9, false); + assertEquals(List.of(3,7), result, "'All' keyword should return all pages."); + } + + @Test + void nFuncAdvanced2Zerobased() { + List result = GeneralUtils.parsePageList(new String[]{"4n-1"}, 9, false); + assertEquals(List.of(2,6), result, "'All' keyword should return all pages."); + } + @Test + void testParsePageListWithRangeOneBasedOutput() { + List result = GeneralUtils.parsePageList(new String[]{"1-3"}, 5, true); + assertEquals(List.of(1, 2, 3), result, "Range should be parsed correctly."); + } + + + @Test + void testParsePageListWithRangeZeroBaseOutput() { + List result = GeneralUtils.parsePageList(new String[]{"1-3"}, 5, false); + assertEquals(List.of(0, 1, 2), result, "Range should be parsed correctly."); + } + + + @Test + void testParsePageListWithRangeOneBasedOutputFull() { + List result = GeneralUtils.parsePageList(new String[]{"1,3,7-8"}, 8, true); + assertEquals(List.of(1, 3, 7,8), result, "Range should be parsed correctly."); + } + + @Test + void testParsePageListWithRangeOneBasedOutputFullOutOfRange() { + List result = GeneralUtils.parsePageList(new String[]{"1,3,7-8"}, 5, true); + assertEquals(List.of(1, 3), result, "Range should be parsed correctly."); + } + @Test + void testParsePageListWithRangeZeroBaseOutputFull() { + List result = GeneralUtils.parsePageList(new String[]{"1,3,7-8"}, 8, false); + assertEquals(List.of(0, 2, 6,7), result, "Range should be parsed correctly."); + } +} diff --git a/test.sh b/test.sh index f17b0651..e771a9b8 100644 --- a/test.sh +++ b/test.sh @@ -18,7 +18,8 @@ check_health() { fi done echo -e "\n$service_name is healthy!" - + echo "Printing logs for $service_name:" + docker logs "$service_name" return 0 } @@ -77,13 +78,11 @@ main() { # Building Docker images - docker build --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest -f ./Dockerfile . - docker build --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest-lite -f ./Dockerfile-lite . - docker build --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest-ultra-lite -f ./Dockerfile-ultra-lite . + docker build --no-cache --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest -f ./Dockerfile . + docker build --no-cache --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest-ultra-lite -f ./Dockerfile-ultra-lite . # Test each configuration run_tests "Stirling-PDF-Ultra-Lite" "./exampleYmlFiles/docker-compose-latest-ultra-lite.yml" - run_tests "Stirling-PDF-Lite" "./exampleYmlFiles/docker-compose-latest-lite.yml" run_tests "Stirling-PDF" "./exampleYmlFiles/docker-compose-latest.yml" export DOCKER_ENABLE_SECURITY=true @@ -95,13 +94,11 @@ main() { # Building Docker images with security enabled - docker build --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest -f ./Dockerfile . - docker build --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest-lite -f ./Dockerfile-lite . - docker build --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest-ultra-lite -f ./Dockerfile-ultra-lite . + docker build --no-cache --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest -f ./Dockerfile . + docker build --no-cache --build-arg VERSION_TAG=alpha -t frooodle/s-pdf:latest-ultra-lite -f ./Dockerfile-ultra-lite . # Test each configuration with security run_tests "Stirling-PDF-Ultra-Lite-Security" "./exampleYmlFiles/docker-compose-latest-ultra-lite-security.yml" - run_tests "Stirling-PDF-Lite-Security" "./exampleYmlFiles/docker-compose-latest-lite-security.yml" run_tests "Stirling-PDF-Security" "./exampleYmlFiles/docker-compose-latest-security.yml" # Report results diff --git a/test2.sh b/test2.sh new file mode 100644 index 00000000..84effd12 --- /dev/null +++ b/test2.sh @@ -0,0 +1,123 @@ +#!/bin/bash + +# Default values +build_type="full" +enable_security="false" +run_compose="true" + +# Function to parse command line arguments +parse_args() { + case "$1" in + ""|-lite|-ultra-lite) build_type="$1";; + esac + + case "$2" in + true|false) enable_security="$2";; + esac + + case "$3" in + true|false) run_compose="$3";; + esac +} + +# Function to check the health of the service with a timeout of 80 seconds +check_health() { + local service_name=$1 + local compose_file=$2 + local end=$((SECONDS+80)) # Fixed the timeout to be 80 seconds as per the function comment + + echo -n "Waiting for $service_name to become healthy..." + until [ "$(docker inspect --format='{{json .State.Health.Status}}' "$service_name")" == '"healthy"' ] || [ $SECONDS -ge $end ]; do + sleep 3 + echo -n "." + if [ $SECONDS -ge $end ]; then + echo -e "\n$service_name health check timed out after 80 seconds." + echo "Printing logs for $service_name:" + docker logs "$service_name" + return 1 + fi + done + echo -e "\n$service_name is healthy!" + echo "Printing logs for $service_name:" + docker logs "$service_name" + return 0 +} + +# Function to build and test a Docker Compose configuration +# Function to build and test a Docker Compose configuration +# Function to build and test a Docker Compose configuration +build_and_test() { + local version_tag="alpha" + local dockerfile_name="./Dockerfile" + local image_base="frooodle/s-pdf" + local security_suffix="" + local docker_compose_base="./exampleYmlFiles/docker-compose-latest" + local compose_suffix=".yml" + local service_name_base="Stirling-PDF" + + if [ "$enable_security" == "true" ]; then + security_suffix="-Security" + docker_compose_base+="-security" # Append to base name for Docker Compose files with security + fi + + case "$build_type" in + full) + dockerfile_name="./Dockerfile" + ;; + ultra-lite) + dockerfile_name="./Dockerfile-ultra-lite" + ;; + esac + + # Dynamic image tag and service name based on build type and security + local image_tag="${image_base}:latest${build_type}${security_suffix}" + local service_name="${service_name_base}${build_type^}${security_suffix}" + local compose_file="${docker_compose_base}${build_type}${compose_suffix}" + + # Gradle build with or without security + echo "Running ./gradlew clean build with security=$enable_security..." + ./gradlew clean build + + if [ $? -ne 0 ]; then + echo "Gradle build failed, exiting script." + exit 1 + fi + + # Building Docker image + echo "Building Docker image $image_tag with Dockerfile $dockerfile_name..." + docker build --build-arg VERSION_TAG=$version_tag -t $image_tag -f $dockerfile_name . + + if [ "$run_compose" == "true" ]; then + echo "Running Docker Compose for $build_type with security=$enable_security..." + docker-compose -f "$compose_file" up -d + + # Health check using the dynamic service name + if ! check_health "$service_name" "$compose_file"; then + echo "$service_name health check failed." + docker-compose -f "$compose_file" down + exit 1 + else + # If the health check passes, prompt the user to press any key to tear down the service + read -n 1 -s -r -p "Health check passed. Press any key to tear down the service." + echo "" # Move to a new line + + # Tear down the service + docker-compose -f "$compose_file" down + fi + + # Tear down the service after the health check passes + #docker-compose -f "$compose_file" down + fi +} + + + +# Main function +main() { + SECONDS=0 + parse_args "$@" + build_and_test + echo "All operations completed in $SECONDS seconds." +} + +main "$@"