Compare commits

..

1 Commits

Author SHA1 Message Date
Anthony Stirling
f5f84f3fc9 Update build.gradle 2024-07-20 12:25:20 +01:00
66 changed files with 272 additions and 3938 deletions

View File

@@ -1,49 +1,20 @@
Translation: translation:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/resources/messages_*_*.properties' - any-glob-to-any-file: 'src/main/resources/messages_*_*.properties'
- any-glob-to-any-file: 'scripts/ignore_translation.toml'
Front End: Front End:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/resources/templates/**/*' - any-glob-to-any-file: 'src/main/resources/templates/**'
- any-glob-to-any-file: 'src/main/resources/static/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/**'
Java: java:
- changed-files: - changed-files:
- any-glob-to-any-file: 'src/main/java/**/*.java' - any-glob-to-any-file: 'src/main/java/**/*.java'
Back End: documentation:
- changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/security/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/model/provider/**/*'
- any-glob-to-any-file: 'src/main/resources/settings.yml.template'
- any-glob-to-any-file: 'src/main/resources/banner.txt'
Security:
- changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/security/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/model/provider/**/*'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/config/model/AuthenticationType.java'
API:
- changed-files:
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/web/MetricsController.java'
- any-glob-to-any-file: 'src/main/java/stirling/software/SPDF/controller/api/**/*'
Documentation:
- changed-files: - changed-files:
- any-glob-to-any-file: '**/*.md' - any-glob-to-any-file: '**/*.md'
- any-glob-to-any-file: 'scripts/counter_translation.py'
- any-glob-to-any-file: 'scripts/ignore_translation.toml'
Docker: docker:
- changed-files: - changed-files:
- any-glob-to-any-file: 'Dockerfile' - any-glob-to-any-file: 'Dockerfile'
- any-glob-to-any-file: 'Dockerfile-*' - any-glob-to-any-file: 'Dockerfile-*'
- any-glob-to-any-file: 'exampleYmlFiles/*.yml'
Test:
- changed-files:
- any-glob-to-any-file: 'cucumber/**/*'
- any-glob-to-any-file: 'test*'

2
.github/labels.yml vendored
View File

@@ -89,5 +89,3 @@
- name: "Test" - name: "Test"
color: "FF9E1F" color: "FF9E1F"
description: "Testing-related issues or pull requests" description: "Testing-related issues or pull requests"
- name: "Stale"
color: "000000"

View File

@@ -1,5 +1,4 @@
"""check_tabulator.py""" """check_tabulator.py"""
import argparse import argparse
import sys import sys

View File

@@ -51,7 +51,6 @@ jobs:
[1]: https://github.com/peter-evans/create-pull-request [1]: https://github.com/peter-evans/create-pull-request
draft: false draft: false
delete-branch: true delete-branch: true
labels: github-actions
sync-readme: sync-readme:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -89,4 +88,3 @@ jobs:
[1]: https://github.com/peter-evans/create-pull-request [1]: https://github.com/peter-evans/create-pull-request
draft: false draft: false
delete-branch: true delete-branch: true
labels: Documentation,Translation,github-actions

View File

@@ -29,8 +29,8 @@ jobs:
- name: Install Docker Compose - name: Install Docker Compose
run: | run: |
sudo curl -SL "https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose 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 # sudo chmod +x /usr/local/bin/docker-compose
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v4 uses: actions/setup-python@v4

45
.gitignore vendored
View File

@@ -1,3 +1,5 @@
### Eclipse ### ### Eclipse ###
.metadata .metadata
bin/ bin/
@@ -20,6 +22,7 @@ customFiles/
configs/ configs/
watchedFolders/ watchedFolders/
# Gradle # Gradle
.gradle .gradle
.lock .lock
@@ -116,48 +119,12 @@ watchedFolders/
*.db *.db
/build /build
# Byte-compiled / optimized / DLL files /.vscode
__pycache__/ /.idea
*.py[cod]
*.pyo
# Virtual environments
.env*
.venv*
env*/
venv*/
ENV/
env.bak/
venv.bak/
# VS Code
/.vscode/**/*
!/.vscode/settings.json
# IntelliJ IDEA
.idea/
*.iml
out/
# Ignore Mac DS_Store files # Ignore Mac DS_Store files
.DS_Store .DS_Store
**/.DS_Store **/.DS_Store
# cucumber #cucumber
/cucumber/reports/** /cucumber/reports/**
# Certs
*.p12
*.pem
*.crt
*.cer
*.der
*.key
*.csr
# cache
.ruff_cache
.mypy_cache
.pytest_cache
.ipynb_checkpoints

53
.vscode/settings.json vendored
View File

@@ -1,53 +0,0 @@
{
"java.compile.nullAnalysis.mode": "automatic",
"files.eol": "auto",
"java.configuration.updateBuildConfiguration": "interactive",
"black-formatter.args": ["--line-length", "127"],
"flake8.args": ["--max-line-length", "127"],
"pylint.args": ["max-line-length", "127"],
"[java]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.rulers": [127]
},
"[python]": {
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.rulers": [127]
},
"[gradle-build]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.rulers": [127]
},
"[gradle]": {
"editor.tabSize": 4,
"editor.detectIndentation": false,
"editor.rulers": [127]
},
"[html]": {
"editor.tabSize": 2,
"editor.rulers": [127],
"files.trimFinalNewlines": false,
"files.insertFinalNewline": false
},
"[javascript]": {
"editor.tabSize": 2,
"editor.rulers": [127]
},
"[yaml]": {
"files.trimFinalNewlines": false,
"files.insertFinalNewline": false
},
"diffEditor.maxComputationTime": 0,
"editor.wordSegmenterLocales": null,
"editor.guides.bracketPairs": "active",
"editor.guides.bracketPairsHorizontal": "active",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"files.trimTrailingWhitespace": true,
"editor.indentSize": "tabSize",
"editor.stickyScroll.enabled": false,
"editor.minimap.enabled": false,
"editor.formatOnSave": true
}

View File

@@ -165,46 +165,43 @@ Please view https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToUseOCR
## Supported Languages ## Supported Languages
Stirling PDF currently supports 38! Stirling PDF currently supports 33!
| Language | Progress | | Language | Progress |
| ------------------------------------------- | -------------------------------------- | | ------------------------------------------- | -------------------------------------- |
| Arabic (العربية) (ar_AR) | ![45%](https://geps.dev/progress/45) |
| Basque (Euskara) (eu_ES) | ![62%](https://geps.dev/progress/62) |
| Bulgarian (Български) (bg_BG) | ![94%](https://geps.dev/progress/94) |
| Catalan (Català) (ca_CA) | ![48%](https://geps.dev/progress/48) |
| Croatian (Hrvatski) (hr_HR) | ![95%](https://geps.dev/progress/95) |
| Czech (Česky) (cs_CZ) | ![90%](https://geps.dev/progress/90) |
| Danish (Dansk) (da_DK) | ![10%](https://geps.dev/progress/10) |
| Dutch (Nederlands) (nl_NL) | ![96%](https://geps.dev/progress/96) |
| English (English) (en_GB) | ![100%](https://geps.dev/progress/100) | | English (English) (en_GB) | ![100%](https://geps.dev/progress/100) |
| English (US) (en_US) | ![100%](https://geps.dev/progress/100) | | English (US) (en_US) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![94%](https://geps.dev/progress/94) | | Arabic (العربية) (ar_AR) | ![45%](https://geps.dev/progress/45) |
| German (Deutsch) (de_DE) | ![100%](https://geps.dev/progress/100) | | German (Deutsch) (de_DE) | ![100%](https://geps.dev/progress/100) |
| French (Français) (fr_FR) | ![94%](https://geps.dev/progress/94) |
| Spanish (Español) (es_ES) | ![92%](https://geps.dev/progress/92) |
| Simplified Chinese (简体中文) (zh_CN) | ![98%](https://geps.dev/progress/98) |
| Traditional Chinese (繁體中文) (zh_TW) | ![96%](https://geps.dev/progress/96) |
| Catalan (Català) (ca_CA) | ![48%](https://geps.dev/progress/48) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Swedish (Svenska) (sv_SE) | ![39%](https://geps.dev/progress/39) |
| Polish (Polski) (pl_PL) | ![90%](https://geps.dev/progress/90) |
| Romanian (Română) (ro_RO) | ![39%](https://geps.dev/progress/39) |
| Korean (한국어) (ko_KR) | ![84%](https://geps.dev/progress/84) |
| Portuguese Brazilian (Português) (pt_BR) | ![60%](https://geps.dev/progress/60) |
| Portuguese (Português) (pt_PT) | ![78%](https://geps.dev/progress/78) |
| Russian (Русский) (ru_RU) | ![84%](https://geps.dev/progress/84) |
| Basque (Euskara) (eu_ES) | ![62%](https://geps.dev/progress/62) |
| Japanese (日本語) (ja_JP) | ![90%](https://geps.dev/progress/90) |
| Dutch (Nederlands) (nl_NL) | ![96%](https://geps.dev/progress/96) |
| Greek (Ελληνικά) (el_GR) | ![82%](https://geps.dev/progress/82) | | Greek (Ελληνικά) (el_GR) | ![82%](https://geps.dev/progress/82) |
| Turkish (Türkçe) (tr_TR) | ![99%](https://geps.dev/progress/99) |
| Indonesia (Bahasa Indonesia) (id_ID) | ![76%](https://geps.dev/progress/76) |
| Hindi (हिंदी) (hi_IN) | ![77%](https://geps.dev/progress/77) | | Hindi (हिंदी) (hi_IN) | ![77%](https://geps.dev/progress/77) |
| Hungarian (Magyar) (hu_HU) | ![76%](https://geps.dev/progress/76) | | Hungarian (Magyar) (hu_HU) | ![76%](https://geps.dev/progress/76) |
| Indonesia (Bahasa Indonesia) (id_ID) | ![76%](https://geps.dev/progress/76) | | Bulgarian (Български) (bg_BG) | ![94%](https://geps.dev/progress/94) |
| Irish (Gaeilge) (ga_IE) | ![99%](https://geps.dev/progress/99) |
| Italian (Italiano) (it_IT) | ![99%](https://geps.dev/progress/99) |
| Japanese (日本語) (ja_JP) | ![90%](https://geps.dev/progress/90) |
| Korean (한국어) (ko_KR) | ![84%](https://geps.dev/progress/84) |
| Norwegian (Norsk) (no_NB) | ![96%](https://geps.dev/progress/96) |
| Polish (Polski) (pl_PL) | ![90%](https://geps.dev/progress/90) |
| Portuguese (Português) (pt_PT) | ![78%](https://geps.dev/progress/78) |
| Portuguese Brazilian (Português) (pt_BR) | ![60%](https://geps.dev/progress/60) |
| Romanian (Română) (ro_RO) | ![39%](https://geps.dev/progress/39) |
| Russian (Русский) (ru_RU) | ![84%](https://geps.dev/progress/84) |
| Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![78%](https://geps.dev/progress/78) | | Sebian Latin alphabet (Srpski) (sr_LATN_RS) | ![78%](https://geps.dev/progress/78) |
| Simplified Chinese (简体中文) (zh_CN) | ![99%](https://geps.dev/progress/99) |
| Slovakian (Slovensky) (sk_SK) | ![92%](https://geps.dev/progress/92) |
| Spanish (Español) (es_ES) | ![98%](https://geps.dev/progress/98) |
| Swedish (Svenska) (sv_SE) | ![39%](https://geps.dev/progress/39) |
| Thai (ไทย) (th_TH) | ![100%](https://geps.dev/progress/100) |
| Traditional Chinese (繁體中文) (zh_TW) | ![98%](https://geps.dev/progress/98) |
| Turkish (Türkçe) (tr_TR) | ![99%](https://geps.dev/progress/99) |
| Ukrainian (Українська) (uk_UA) | ![90%](https://geps.dev/progress/90) | | Ukrainian (Українська) (uk_UA) | ![90%](https://geps.dev/progress/90) |
| Vietnamese (Tiếng Việt) (vi_VN) | ![99%](https://geps.dev/progress/99) | | Slovakian (Slovensky) (sk_SK) | ![92%](https://geps.dev/progress/92) |
| Czech (Česky) (cs_CZ) | ![90%](https://geps.dev/progress/90) |
| Croatian (Hrvatski) (hr_HR) | ![95%](https://geps.dev/progress/95) |
| Norwegian (Norsk) (no_NB) | ![96%](https://geps.dev/progress/96) |
| Thai (ไทย) (th_TH) | ![100%](https://geps.dev/progress/100) |
## Contributing (creating issues, translations, fixing bugs, etc.) ## Contributing (creating issues, translations, fixing bugs, etc.)

View File

@@ -1,7 +1,7 @@
plugins { plugins {
id "java" id "java"
id "org.springframework.boot" version "3.3.0" id "org.springframework.boot" version "3.3.0"
id "io.spring.dependency-management" version "1.1.6" id "io.spring.dependency-management" version "1.1.5"
id "org.springdoc.openapi-gradle-plugin" version "1.8.0" id "org.springdoc.openapi-gradle-plugin" version "1.8.0"
id "io.swagger.swaggerhub" version "1.3.2" id "io.swagger.swaggerhub" version "1.3.2"
id "edu.sc.seis.launch4j" version "3.0.5" id "edu.sc.seis.launch4j" version "3.0.5"
@@ -16,7 +16,7 @@ ext {
} }
group = "stirling.software" group = "stirling.software"
version = "0.26.2" version = "0.27.0"
// 17 is lowest but we support and recommend 21 // 17 is lowest but we support and recommend 21
sourceCompatibility = "17" sourceCompatibility = "17"
@@ -40,7 +40,6 @@ sourceSets {
exclude "stirling/software/SPDF/controller/web/AccountWebController.java" exclude "stirling/software/SPDF/controller/web/AccountWebController.java"
exclude "stirling/software/SPDF/controller/web/DatabaseWebController.java" exclude "stirling/software/SPDF/controller/web/DatabaseWebController.java"
exclude "stirling/software/SPDF/model/ApiKeyAuthenticationToken.java" exclude "stirling/software/SPDF/model/ApiKeyAuthenticationToken.java"
exclude "stirling/software/SPDF/model/AttemptCounter.java"
exclude "stirling/software/SPDF/model/Authority.java" exclude "stirling/software/SPDF/model/Authority.java"
exclude "stirling/software/SPDF/model/PersistentLogin.java" exclude "stirling/software/SPDF/model/PersistentLogin.java"
exclude "stirling/software/SPDF/model/User.java" exclude "stirling/software/SPDF/model/User.java"

View File

@@ -62,10 +62,8 @@ spec:
imagePullPolicy: {{ .Values.image.pullPolicy }} imagePullPolicy: {{ .Values.image.pullPolicy }}
securityContext: securityContext:
{{- toYaml .Values.containerSecurityContext | nindent 10 }} {{- toYaml .Values.containerSecurityContext | nindent 10 }}
env:
- name: SYSTEM_ROOTURIPATH
value: {{ .Values.rootPath}}
{{- if .Values.envs }} {{- if .Values.envs }}
env:
{{ toYaml .Values.envs | indent 8 }} {{ toYaml .Values.envs | indent 8 }}
{{- end }} {{- end }}
{{- if .Values.extraArgs }} {{- if .Values.extraArgs }}
@@ -77,13 +75,13 @@ spec:
containerPort: 8080 containerPort: 8080
livenessProbe: livenessProbe:
httpGet: httpGet:
path: {{ .Values.rootPath}} path: /
port: http port: http
{{ toYaml .Values.probes.livenessHttpGetConfig | indent 12 }} {{ toYaml .Values.probes.livenessHttpGetConfig | indent 12 }}
{{ toYaml .Values.probes.liveness | indent 10 }} {{ toYaml .Values.probes.liveness | indent 10 }}
readinessProbe: readinessProbe:
httpGet: httpGet:
path: {{ .Values.rootPath}} path: /
port: http port: http
{{ toYaml .Values.probes.readinessHttpGetConfig | indent 12 }} {{ toYaml .Values.probes.readinessHttpGetConfig | indent 12 }}
{{ toYaml .Values.probes.readiness | indent 10 }} {{ toYaml .Values.probes.readiness | indent 10 }}

View File

@@ -15,9 +15,6 @@ secret:
commonLabels: {} commonLabels: {}
# team_name: dev # team_name: dev
# rootpath for the application
rootPath: /
envs: [] envs: []
# - name: UI_APP_NAME # - name: UI_APP_NAME
# value: "Stirling PDF" # value: "Stirling PDF"
@@ -27,6 +24,8 @@ envs: []
# value: "Stirling PDF" # value: "Stirling PDF"
# - name: ALLOW_GOOGLE_VISIBILITY # - name: ALLOW_GOOGLE_VISIBILITY
# value: "true" # value: "true"
# - name: APP_ROOT_PATH
# value: "/"
# - name: APP_LOCALE # - name: APP_LOCALE
# value: "en_GB" # value: "en_GB"

View File

@@ -92,10 +92,10 @@ Feature: API Validation
| threshold | 90 | | threshold | 90 |
| whitePercent | 99.9 | | whitePercent | 99.9 |
When I send the API request to the endpoint "/api/v1/misc/remove-blanks" When I send the API request to the endpoint "/api/v1/misc/remove-blanks"
Then the response content type should be "application/octet-stream" Then the response content type should be "application/pdf"
And the response file should have extension ".zip"
And the response ZIP should contain 2 files
And the response file should have size greater than 0 And the response file should have size greater than 0
And the response PDF should contain 0 pages
And the response status code should be 200
@positive @flatten @positive @flatten
Scenario: Flatten PDF Scenario: Flatten PDF

View File

@@ -25,11 +25,6 @@ ignore = [
'text', 'text',
] ]
[da_DK]
ignore = [
'language.direction',
]
[de_DE] [de_DE]
ignore = [ ignore = [
'AddStampRequest.alphabet', 'AddStampRequest.alphabet',
@@ -92,11 +87,6 @@ ignore = [
'watermark.type.2', 'watermark.type.2',
] ]
[ga_IE]
ignore = [
'language.direction',
]
[hi_IN] [hi_IN]
ignore = [ ignore = [
'language.direction', 'language.direction',
@@ -240,14 +230,6 @@ ignore = [
'language.direction', 'language.direction',
] ]
[vi_VN]
ignore = [
'language.direction',
'pipeline.title',
'pipelineOptions.pipelineHeader',
'showJS.tags',
]
[zh_CN] [zh_CN]
ignore = [ ignore = [
'language.direction', 'language.direction',

View File

@@ -45,6 +45,7 @@ public class SPdfApplication {
// Check if the BROWSER_OPEN environment variable is set to true // Check if the BROWSER_OPEN environment variable is set to true
String browserOpenEnv = env.getProperty("BROWSER_OPEN"); String browserOpenEnv = env.getProperty("BROWSER_OPEN");
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv); boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
if (browserOpen) { if (browserOpen) {
try { try {
String url = "http://localhost:" + getNonStaticPort(); String url = "http://localhost:" + getNonStaticPort();
@@ -78,13 +79,13 @@ public class SPdfApplication {
// custom javs settings file // custom javs settings file
if (Files.exists(Paths.get("configs/custom_settings.yml"))) { if (Files.exists(Paths.get("configs/custom_settings.yml"))) {
String existingLocation = propertyFiles.getOrDefault("spring.config.additional-location", ""); String existing = propertyFiles.getOrDefault("spring.config.additional-location", "");
if (!existingLocation.isEmpty()) { if (!existing.isEmpty()) {
existingLocation += ","; existing += ",";
} }
propertyFiles.put( propertyFiles.put(
"spring.config.additional-location", "spring.config.additional-location",
existingLocation + "file:configs/custom_settings.yml"); existing + "file:configs/custom_settings.yml");
} else { } else {
logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist."); logger.warn("Custom configuration file 'configs/custom_settings.yml' does not exist.");
} }

View File

@@ -32,25 +32,25 @@ public class CleanUrlInterceptor implements HandlerInterceptor {
String queryString = request.getQueryString(); String queryString = request.getQueryString();
if (queryString != null && !queryString.isEmpty()) { if (queryString != null && !queryString.isEmpty()) {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
Map<String, String> allowedParameters = new HashMap<>(); Map<String, String> parameters = new HashMap<>();
// Keep only the allowed parameters // Keep only the allowed parameters
String[] queryParameters = queryString.split("&"); String[] queryParameters = queryString.split("&");
for (String param : queryParameters) { for (String param : queryParameters) {
String[] keyValuePair = param.split("="); String[] keyValue = param.split("=");
if (keyValuePair.length != 2) { if (keyValue.length != 2) {
continue; continue;
} }
if (ALLOWED_PARAMS.contains(keyValuePair[0])) { if (ALLOWED_PARAMS.contains(keyValue[0])) {
allowedParameters.put(keyValuePair[0], keyValuePair[1]); parameters.put(keyValue[0], keyValue[1]);
} }
} }
// If there are any parameters that are not allowed // If there are any parameters that are not allowed
if (allowedParameters.size() != queryParameters.length) { if (parameters.size() != queryParameters.length) {
// Construct new query string // Construct new query string
StringBuilder newQueryString = new StringBuilder(); StringBuilder newQueryString = new StringBuilder();
for (Map.Entry<String, String> entry : allowedParameters.entrySet()) { for (Map.Entry<String, String> entry : parameters.entrySet()) {
if (newQueryString.length() > 0) { if (newQueryString.length() > 0) {
newQueryString.append("&"); newQueryString.append("&");
} }

View File

@@ -179,12 +179,11 @@ public class DatabaseBackupHelper implements DatabaseBackupInterface {
} }
private boolean executeDatabaseScript(Path scriptPath) { private boolean executeDatabaseScript(Path scriptPath) {
String query = "RUNSCRIPT from ?;";
try (Connection conn = DriverManager.getConnection(url, "sa", ""); try (Connection conn = DriverManager.getConnection(url, "sa", "");
PreparedStatement stmt = conn.prepareStatement(query)) { Statement stmt = conn.createStatement()) {
stmt.setString(1, scriptPath.toString());
stmt.execute(); String query = "RUNSCRIPT from '" + scriptPath.toString() + "';";
stmt.execute(query);
log.info("Database import completed: {}", scriptPath); log.info("Database import completed: {}", scriptPath);
return true; return true;
} catch (SQLException e) { } catch (SQLException e) {

View File

@@ -1,12 +1,12 @@
package stirling.software.SPDF.controller.api.misc; package stirling.software.SPDF.controller.api.misc;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.zip.ZipEntry; import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream; import java.util.stream.IntStream;
import org.apache.pdfbox.Loader; import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
@@ -17,7 +17,6 @@ import org.apache.pdfbox.text.PDFTextStripper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -51,31 +50,31 @@ public class BlankPageController {
int threshold = request.getThreshold(); int threshold = request.getThreshold();
float whitePercent = request.getWhitePercent(); float whitePercent = request.getWhitePercent();
try (PDDocument document = Loader.loadPDF(inputFile.getBytes())) { PDDocument document = null;
try {
document = Loader.loadPDF(inputFile.getBytes());
PDPageTree pages = document.getDocumentCatalog().getPages(); PDPageTree pages = document.getDocumentCatalog().getPages();
PDFTextStripper textStripper = new PDFTextStripper(); PDFTextStripper textStripper = new PDFTextStripper();
List<PDPage> nonBlankPages = new ArrayList<>(); List<Integer> pagesToKeepIndex = new ArrayList<>();
List<PDPage> blankPages = new ArrayList<>();
int pageIndex = 0; int pageIndex = 0;
PDFRenderer pdfRenderer = new PDFRenderer(document); PDFRenderer pdfRenderer = new PDFRenderer(document);
pdfRenderer.setSubsamplingAllowed(true); pdfRenderer.setSubsamplingAllowed(true);
for (PDPage page : pages) { for (PDPage page : pages) {
logger.info("checking page {}", pageIndex); logger.info("checking page " + pageIndex);
textStripper.setStartPage(pageIndex + 1); textStripper.setStartPage(pageIndex + 1);
textStripper.setEndPage(pageIndex + 1); textStripper.setEndPage(pageIndex + 1);
String pageText = textStripper.getText(document); String pageText = textStripper.getText(document);
boolean hasText = !pageText.trim().isEmpty(); boolean hasText = !pageText.trim().isEmpty();
boolean blank = true; Boolean blank = true;
if (hasText) { if (hasText) {
logger.info("page {} has text, not blank", pageIndex); logger.info("page " + pageIndex + " has text, not blank");
blank = false; blank = false;
} else { } else {
boolean hasImages = PdfUtils.hasImagesOnPage(page); boolean hasImages = PdfUtils.hasImagesOnPage(page);
if (hasImages) { if (hasImages) {
logger.info("page {} has image, running blank detection", pageIndex); logger.info("page " + pageIndex + " has image, running blank detection");
// Render image and save as temp file // Render image and save as temp file
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 30); BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 30);
blank = isBlankImage(image, threshold, whitePercent, threshold); blank = isBlankImage(image, threshold, whitePercent, threshold);
@@ -83,57 +82,34 @@ public class BlankPageController {
} }
if (blank) { if (blank) {
logger.info("Skipping, Image was blank for page #{}", pageIndex); logger.info("Skipping, Image was blank for page #" + pageIndex);
blankPages.add(page);
} else { } else {
logger.info("page {} has image which is not blank", pageIndex); logger.info("page " + pageIndex + " has image which is not blank");
nonBlankPages.add(page); pagesToKeepIndex.add(pageIndex);
} }
pageIndex++; pageIndex++;
} }
// Remove pages not present in pagesToKeepIndex
List<Integer> pageIndices =
IntStream.range(0, pages.getCount()).boxed().collect(Collectors.toList());
Collections.reverse(pageIndices); // Reverse to prevent index shifting during removal
for (Integer i : pageIndices) {
if (!pagesToKeepIndex.contains(i)) {
pages.remove(i);
}
}
ByteArrayOutputStream baos = new ByteArrayOutputStream(); return WebResponseUtils.pdfDocToWebResponse(
ZipOutputStream zos = new ZipOutputStream(baos); document,
String filename =
Filenames.toSimpleFileName(inputFile.getOriginalFilename()) Filenames.toSimpleFileName(inputFile.getOriginalFilename())
.replaceFirst("[.][^.]+$", ""); .replaceFirst("[.][^.]+$", "")
+ "_blanksRemoved.pdf");
if (!nonBlankPages.isEmpty()) {
createZipEntry(zos, nonBlankPages, filename + "_nonBlankPages.pdf");
} else {
createZipEntry(zos, blankPages, filename + "_allBlankPages.pdf");
}
if (!nonBlankPages.isEmpty() && !blankPages.isEmpty()) {
createZipEntry(zos, blankPages, filename + "_blankPages.pdf");
}
zos.close();
logger.info("Returning ZIP file: {}", filename + "_processed.zip");
return WebResponseUtils.boasToWebResponse(
baos, filename + "_processed.zip", MediaType.APPLICATION_OCTET_STREAM);
} catch (IOException e) { } catch (IOException e) {
logger.error("exception", e); logger.error("exception", e);
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
} } finally {
} if (document != null) document.close();
public void createZipEntry(ZipOutputStream zos, List<PDPage> pages, String entryName)
throws IOException {
try (PDDocument document = new PDDocument()) {
for (PDPage page : pages) {
document.addPage(page);
}
ZipEntry zipEntry = new ZipEntry(entryName);
zos.putNextEntry(zipEntry);
document.save(zos);
zos.closeEntry();
} }
} }

View File

@@ -360,8 +360,6 @@ public class ApplicationProperties {
+ useAsUsername + useAsUsername
+ ", provider=" + ", provider="
+ provider + provider
+ ", client="
+ client
+ ", scopes=" + ", scopes="
+ scopes + scopes
+ "]"; + "]";

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Покажи проследяване на стека
error.copyStack=Копиране на проследяване на стека error.copyStack=Копиране на проследяване на стека
error.githubSubmit=GitHub - Изпратете запитване error.githubSubmit=GitHub - Изпратете запитване
error.discordSubmit=Discord - Изпратете запитване за поддръжка error.discordSubmit=Discord - Изпратете запитване за поддръжка

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Zobrazit stopu zásobníku
error.copyStack=Kopírovat stopu zásobníku error.copyStack=Kopírovat stopu zásobníku
error.githubSubmit=GitHub - Odeslat požadavek error.githubSubmit=GitHub - Odeslat požadavek
error.discordSubmit=Discord - Odeslat příspěvek podpory error.discordSubmit=Discord - Odeslat příspěvek podpory

File diff suppressed because it is too large Load Diff

View File

@@ -1124,3 +1124,4 @@ error.showStack=Stack-Trace anzeigen
error.copyStack=Stack-Trace kopieren error.copyStack=Stack-Trace kopieren
error.githubSubmit=GitHub - Ein Ticket einreichen error.githubSubmit=GitHub - Ein Ticket einreichen
error.discordSubmit=Discord - Unterstützungsbeitrag einreichen error.discordSubmit=Discord - Unterstützungsbeitrag einreichen

View File

@@ -1124,3 +1124,4 @@ error.showStack=Εμφάνιση Stack Trace
error.copyStack=Αντιγραφή Stack Trace error.copyStack=Αντιγραφή Stack Trace
error.githubSubmit=GitHub - Υποβάλετε ένα ticket error.githubSubmit=GitHub - Υποβάλετε ένα ticket
error.discordSubmit=Discord - Υποβάλετε ένα Support post error.discordSubmit=Discord - Υποβάλετε ένα Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -60,11 +60,11 @@ deleteCurrentUserMessage=No puede eliminar el usuario que tiene la sesión actua
deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse. deleteUsernameExistsMessage=El usuario no existe y no puede eliminarse.
downgradeCurrentUserMessage=No se puede degradar el rol del usuario actual 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á. downgradeCurrentUserLongMessage=No se puede degradar el rol del usuario actual. Por lo tanto, el usuario actual no se mostrará.
userAlreadyExistsOAuthMessage=La usuario ya existe como usuario de OAuth2. userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user.
userAlreadyExistsWebMessage=El usuario ya existe como usuario web. userAlreadyExistsWebMessage=The user already exists as an web user.
error=Error error=Error
oops=Ups! oops=Ups!
help=Ayuda help=Help
goHomepage=Ir a la página principal goHomepage=Ir a la página principal
joinDiscord=Únase a nuestro servidor Discord joinDiscord=Únase a nuestro servidor Discord
seeDockerHub=Ver Docker Hub seeDockerHub=Ver Docker Hub
@@ -86,7 +86,7 @@ pipeline.defaultOption=Personalizar
pipeline.submitButton=Enviar pipeline.submitButton=Enviar
pipeline.help=Ayuda de Canalización pipeline.help=Ayuda de Canalización
pipeline.scanHelp=Ayuda de escaneado de carpetas pipeline.scanHelp=Ayuda de escaneado de carpetas
pipeline.deletePrompt=¿Estás segura de que quieres eliminar la canalización? pipeline.deletePrompt=Are you sure you want to delete pipeline
###################### ######################
# Pipeline Options # # Pipeline Options #
@@ -107,18 +107,18 @@ pipelineOptions.validateButton=Validar
############# #############
# NAVBAR # # NAVBAR #
############# #############
navbar.favorite=Favoritos navbar.favorite=Favorites
navbar.darkmode=Modo oscuro navbar.darkmode=Modo oscuro
navbar.language=Idiomas navbar.language=Languages
navbar.settings=Configuración navbar.settings=Configuración
navbar.allTools=Herramientas navbar.allTools=Tools
navbar.multiTool=Multi herramientas navbar.multiTool=Multi Tools
navbar.sections.organize=Organize navbar.sections.organize=Organize
navbar.sections.convertTo=Convertir a PDF navbar.sections.convertTo=Convert to PDF
navbar.sections.convertFrom=Convertir desde PDF navbar.sections.convertFrom=Convert from PDF
navbar.sections.security=Señalización y seguridad navbar.sections.security=Sign & Security
navbar.sections.advance=Avanzado navbar.sections.advance=Advanced
navbar.sections.edit=Ver y Editar navbar.sections.edit=View & Edit
############# #############
# SETTINGS # # SETTINGS #
@@ -175,8 +175,8 @@ adminUserSettings.header=Configuración de control de usuario administrador
adminUserSettings.admin=Administrador adminUserSettings.admin=Administrador
adminUserSettings.user=Usuario adminUserSettings.user=Usuario
adminUserSettings.addUser=Añadir Nuevo Usuario adminUserSettings.addUser=Añadir Nuevo Usuario
adminUserSettings.deleteUser=Eliminar Usuario adminUserSettings.deleteUser=Delete User
adminUserSettings.confirmDeleteUser=¿Se debe eliminar al usuario? adminUserSettings.confirmDeleteUser=Should the user be deleted?
adminUserSettings.usernameInfo=El nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida. adminUserSettings.usernameInfo=El nombre de usuario solo puede contener letras, números y los siguientes caracteres especiales @._+- o debe ser una dirección de correo electrónico válida.
adminUserSettings.roles=Roles adminUserSettings.roles=Roles
adminUserSettings.role=Rol adminUserSettings.role=Rol
@@ -189,24 +189,24 @@ adminUserSettings.internalApiUser=Usuario interno de API
adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso adminUserSettings.forceChange=Forzar usuario a cambiar usuario/contraseña en el acceso
adminUserSettings.submit=Guardar Usuario adminUserSettings.submit=Guardar Usuario
adminUserSettings.changeUserRole=Cambiar rol de usuario adminUserSettings.changeUserRole=Cambiar rol de usuario
adminUserSettings.authenticated=Autenticado adminUserSettings.authenticated=Authenticated
database.title=Base de Datos Importar/Exportar database.title=Database Import/Export
database.header=Base de Datos Importar/Exportar database.header=Database Import/Export
database.fileName=Nombre de Archivo database.fileName=File Name
database.creationDate=Fecha de creación database.creationDate=Creation Date
database.fileSize=Tamaño de archivo database.fileSize=File Size
database.deleteBackupFile=Eliminar archivo de copia de seguridad database.deleteBackupFile=Delete Backup File
database.importBackupFile=Importar archivo de copia de seguridad database.importBackupFile=Import Backup File
database.downloadBackupFile=Descargar archivo de copia de seguridad database.downloadBackupFile=Download Backup File
database.info_1=Al importar datos, es fundamental garantizar la estructura correcta. Si no está seguro de lo que está haciendo, busque consejo y apoyo de un profesional. Un error en la estructura puede causar un mal funcionamiento de la aplicación, incluyendo la imposibilidad total de ejecutar la aplicación. database.info_1=When importing data, it is crucial to ensure the correct structure. If you are unsure of what you are doing, seek advice and support from a professional. An error in the structure can cause application malfunctions, up to and including the complete inability to run the application.
database.info_2=El nombre del archivo no importa al cargarlo. Posteriormente se le cambiará el nombre para que siga el formato backup_user_yyyyMMddHHmm.sql, lo que garantiza una convención de nomenclatura coherente. database.info_2=The file name does not matter when uploading. It will be renamed afterward to follow the format backup_user_yyyyMMddHHmm.sql, ensuring a consistent naming convention.
database.submit=Importar Backup database.submit=Import Backup
database.importIntoDatabaseSuccessed=Importación a la base de datos ha sido exitosa database.importIntoDatabaseSuccessed=Import into database successed
database.fileNotFound=Archivo no encontrado database.fileNotFound=File not Found
database.fileNullOrEmpty=El archivo no debe ser nulo o vacío. database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=Archivo de importación fallido database.failedImportFile=Failed Import File
############# #############
# HOME-PAGE # # HOME-PAGE #
@@ -353,8 +353,8 @@ home.certSign.title=Firmar con certificado
home.certSign.desc=Firmar un PDF con un Certificado/Clave (PEM/P12) home.certSign.desc=Firmar un PDF con un Certificado/Clave (PEM/P12)
certSign.tags=autentificar,PEM,P12,oficial,encriptar certSign.tags=autentificar,PEM,P12,oficial,encriptar
home.removeCertSign.title=Quitar signo de certificado home.removeCertSign.title=Remove Certificate Sign
home.removeCertSign.desc=Eliminar firma de certificado de PDF home.removeCertSign.desc=Remove certificate signature from PDF
removeCertSign.tags=authenticate,PEM,P12,official,decrypt removeCertSign.tags=authenticate,PEM,P12,official,decrypt
home.pageLayout.title=Diseño de varias páginas home.pageLayout.title=Diseño de varias páginas
@@ -476,13 +476,13 @@ login.invalid=Nombre de usuario o contraseña erróneos.
login.locked=Su cuenta se ha bloqueado. login.locked=Su cuenta se ha bloqueado.
login.signinTitle=Por favor, inicie sesión login.signinTitle=Por favor, inicie sesión
login.ssoSignIn=Iniciar sesión a través del inicio de sesión único 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 login.oauth2AutoCreateDisabled=Usuario DE creación automática de OAUTH2 DESACTIVADO
login.oauth2RequestNotFound=Solicitud de autorización no encontrada login.oauth2RequestNotFound=Authorization request not found
login.oauth2InvalidUserInfoResponse=Respuesta de información de usuario no válida login.oauth2InvalidUserInfoResponse=Invalid User Info Response
login.oauth2invalidRequest=Invalid Request login.oauth2invalidRequest=Invalid Request
login.oauth2AccessDenied=Access Denied login.oauth2AccessDenied=Access Denied
login.oauth2InvalidTokenResponse=Respuesta de token no válida login.oauth2InvalidTokenResponse=Invalid Token Response
login.oauth2InvalidIdToken=Token de identificación no válido login.oauth2InvalidIdToken=Invalid Id Token
#auto-redact #auto-redact
@@ -681,10 +681,10 @@ certSign.submit=Firmar PDF
#removeCertSign #removeCertSign
removeCertSign.title=Eliminar firma del certificado removeCertSign.title=Remove Certificate Signature
removeCertSign.header=Quitar el certificado digital del PDF removeCertSign.header=Remove the digital certificate from the PDF
removeCertSign.selectPDF=Seleccione un archivo PDF: removeCertSign.selectPDF=Select a PDF file:
removeCertSign.submit=Eliminar firma removeCertSign.submit=Remove Signature
#removeBlanks #removeBlanks
@@ -706,8 +706,8 @@ removeAnnotations.submit=Eliminar
#compare #compare
compare.title=Comparar compare.title=Comparar
compare.header=Comparar archivos PDF compare.header=Comparar archivos PDF
compare.highlightColor.1=Color resaltado 1: compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Color resaltado 2: compare.highlightColor.2=Highlight Color 2:
compare.document.1=Documento 1 compare.document.1=Documento 1
compare.document.2=Documento 2 compare.document.2=Documento 2
compare.submit=Comparar compare.submit=Comparar
@@ -744,7 +744,7 @@ repair.submit=Reparar
#flatten #flatten
flatten.title=Aplanar flatten.title=Aplanar
flatten.header=Acoplar archivos PDF flatten.header=Acoplar archivos PDF
flatten.flattenOnlyForms=Aplanar sólo formularios flatten.flattenOnlyForms=Flatten only forms
flatten.submit=Aplanar flatten.submit=Aplanar
@@ -792,7 +792,7 @@ extractImages.submit=Extraer
fileToPDF.title=Archivo a PDF fileToPDF.title=Archivo a PDF
fileToPDF.header=Convertir cualquier archivo a PDF fileToPDF.header=Convertir cualquier archivo a PDF
fileToPDF.credit=Este servicio usa LibreOffice y Unoconv para la conversión de archivos fileToPDF.credit=Este servicio usa LibreOffice y Unoconv para la conversión de archivos
fileToPDF.supportedFileTypesInfo=Tipos de archivos admitidos fileToPDF.supportedFileTypesInfo=Supported File types
fileToPDF.supportedFileTypes=Los tipos de archivo soportados deben incluir los indicados a continuación; sin embargo, para una completa y acutualizada lista de formatos soportados, por favor consulte la documentación de LibreOffice fileToPDF.supportedFileTypes=Los tipos de archivo soportados deben incluir los indicados a continuación; sin embargo, para una completa y acutualizada lista de formatos soportados, por favor consulte la documentación de LibreOffice
fileToPDF.submit=Convertir a PDF fileToPDF.submit=Convertir a PDF
@@ -1000,8 +1000,8 @@ pdfToPDFA.header=PDF a PDF/A
pdfToPDFA.credit=Este servicio usa OCRmyPDF para la conversión a PDF/A pdfToPDFA.credit=Este servicio usa OCRmyPDF para la conversión a PDF/A
pdfToPDFA.submit=Convertir pdfToPDFA.submit=Convertir
pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez pdfToPDFA.tip=Actualmente no funciona para múltiples entrada a la vez
pdfToPDFA.outputFormat=Formato de salida pdfToPDFA.outputFormat=Output format
pdfToPDFA.pdfWithDigitalSignature=El PDF contiene una firma digital. Esto se eliminará en el siguiente paso. pdfToPDFA.pdfWithDigitalSignature=The PDF contains a digital signature. This will be removed in the next step.
#PDFToWord #PDFToWord
@@ -1103,13 +1103,13 @@ licenses.version=Versión
licenses.license=Licencia licenses.license=Licencia
#survey #survey
survey.nav=Encuesta survey.nav=Survey
survey.title=Encuesta Stirling-PDF survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF no tiene seguimiento, por lo que queremos escuchar a nuestros usuarios para mejorar Stirling-PDF. survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
survey.please=¡Considere realizar nuestra encuesta! survey.please=Please consider taking our survey!
survey.disabled=(La ventana emergente de la encuesta se desactivará en las siguientes actualizaciones, pero estará disponible al pie de la página.) survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=Realizar encuesta survey.button=Take Survey
survey.dontShowAgain=No volver a mostrar survey.dontShowAgain=Don't show again
#error #error
@@ -1124,3 +1124,4 @@ error.showStack=Mostrar seguimiento de pila
error.copyStack=Mostrar seguimiento de pila error.copyStack=Mostrar seguimiento de pila
error.githubSubmit=GitHub - Enviar un ticket error.githubSubmit=GitHub - Enviar un ticket
error.discordSubmit=Discord - Enviar mensaje de soporte error.discordSubmit=Discord - Enviar mensaje de soporte

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Afficher la Stack Trace
error.copyStack=Copier la Stack Trace error.copyStack=Copier la Stack Trace
error.githubSubmit=GitHub - Créer un ticket error.githubSubmit=GitHub - Créer un ticket
error.discordSubmit=Discord - Poster un message de demande dassistance error.discordSubmit=Discord - Poster un message de demande dassistance

File diff suppressed because it is too large Load Diff

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Prikaži Stack Trace
error.copyStack=Kopiraj Stack Trace error.copyStack=Kopiraj Stack Trace
error.githubSubmit=GitHub - Pošaljite ticket error.githubSubmit=GitHub - Pošaljite ticket
error.discordSubmit=Discord - Pošalji objavu podrške error.discordSubmit=Discord - Pošalji objavu podrške

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -706,8 +706,8 @@ removeAnnotations.submit=Rimuovi
#compare #compare
compare.title=Compara compare.title=Compara
compare.header=Compara PDF compare.header=Compara PDF
compare.highlightColor.1=Evidenzia colore 1: compare.highlightColor.1=Highlight Color 1:
compare.highlightColor.2=Evidenzia colore 2: compare.highlightColor.2=Highlight Color 2:
compare.document.1=Documento 1 compare.document.1=Documento 1
compare.document.2=Documento 2 compare.document.2=Documento 2
compare.submit=Compara compare.submit=Compara
@@ -1124,3 +1124,4 @@ error.showStack=Mostra traccia dello stack
error.copyStack=Copia traccia dello stack error.copyStack=Copia traccia dello stack
error.githubSubmit=GitHub: invia un ticket error.githubSubmit=GitHub: invia un ticket
error.discordSubmit=Discord: invia post di supporto error.discordSubmit=Discord: invia post di supporto

View File

@@ -1124,3 +1124,4 @@ error.showStack=スタックトレースを表示
error.copyStack=スタックトレースをコピー error.copyStack=スタックトレースをコピー
error.githubSubmit=GitHub - チケットを提出 error.githubSubmit=GitHub - チケットを提出
error.discordSubmit=Discord - サポート投稿を提出 error.discordSubmit=Discord - サポート投稿を提出

View File

@@ -1124,3 +1124,4 @@ error.showStack=스택 추적 보기
error.copyStack=스택 추적 복사 error.copyStack=스택 추적 복사
error.githubSubmit=GitHub - 티켓 제출 error.githubSubmit=GitHub - 티켓 제출
error.discordSubmit=Discord - 문의 게시 error.discordSubmit=Discord - 문의 게시

View File

@@ -1124,3 +1124,4 @@ error.showStack=Geeft tracering weer
error.copyStack=Kopieer tracering error.copyStack=Kopieer tracering
error.githubSubmit=GitHub - Dien een ticket in error.githubSubmit=GitHub - Dien een ticket in
error.discordSubmit=Discord - Maak een support post error.discordSubmit=Discord - Maak een support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Vis stakksporing
error.copyStack=Kopier stakksporing error.copyStack=Kopier stakksporing
error.githubSubmit=GitHub - Send inn en billett error.githubSubmit=GitHub - Send inn en billett
error.discordSubmit=Discord - Send inn støtteinnlegg error.discordSubmit=Discord - Send inn støtteinnlegg

View File

@@ -1124,3 +1124,4 @@ error.showStack=Pokaż Stack Trace
error.copyStack=Kopiuj Stack Trace error.copyStack=Kopiuj Stack Trace
error.githubSubmit=GitHub - wyślij zgłoszenie error.githubSubmit=GitHub - wyślij zgłoszenie
error.discordSubmit=Discord - wyślij posta z prośbą o pomoc error.discordSubmit=Discord - wyślij posta z prośbą o pomoc

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Показать стек вызовов
error.copyStack=Скопировать стек вызовов error.copyStack=Скопировать стек вызовов
error.githubSubmit=GitHub - Отправить заявку error.githubSubmit=GitHub - Отправить заявку
error.discordSubmit=Discord - Отправить запрос в поддержку error.discordSubmit=Discord - Отправить запрос в поддержку

View File

@@ -1124,3 +1124,4 @@ error.showStack=Zobraziť sledovanie zásobníka
error.copyStack=Kopírovať sledovanie zásobníka error.copyStack=Kopírovať sledovanie zásobníka
error.githubSubmit=GitHub - Podajte tiket error.githubSubmit=GitHub - Podajte tiket
error.discordSubmit=Discord - Podajte príspevok na podporu error.discordSubmit=Discord - Podajte príspevok na podporu

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=Show Stack Trace
error.copyStack=Copy Stack Trace error.copyStack=Copy Stack Trace
error.githubSubmit=GitHub - Submit a ticket error.githubSubmit=GitHub - Submit a ticket
error.discordSubmit=Discord - Submit Support post error.discordSubmit=Discord - Submit Support post

View File

@@ -1124,3 +1124,4 @@ error.showStack=แสดง Stack Trace
error.copyStack=คัดลอก Stack Trace error.copyStack=คัดลอก Stack Trace
error.githubSubmit=GitHub - ส่งตั๋ว error.githubSubmit=GitHub - ส่งตั๋ว
error.discordSubmit=Discord - ส่งโพสต์การสนับสนุน error.discordSubmit=Discord - ส่งโพสต์การสนับสนุน

View File

@@ -1124,3 +1124,4 @@ error.showStack=Yığın İzlemesini Göster
error.copyStack=Yığın İzini Kopyala error.copyStack=Yığın İzini Kopyala
error.githubSubmit=GitHub - Hata gönderin error.githubSubmit=GitHub - Hata gönderin
error.discordSubmit=Discord - Destek gönderisi gönderin error.discordSubmit=Discord - Destek gönderisi gönderin

View File

@@ -1124,3 +1124,4 @@ error.showStack=Показати стек викликів
error.copyStack=Скопіювати стек викликів error.copyStack=Скопіювати стек викликів
error.githubSubmit=GitHub - Надіслати запит error.githubSubmit=GitHub - Надіслати запит
error.discordSubmit=Discord - Надіслати повідомлення підтримки error.discordSubmit=Discord - Надіслати повідомлення підтримки

File diff suppressed because it is too large Load Diff

View File

@@ -192,21 +192,21 @@ adminUserSettings.changeUserRole=更改用户角色
adminUserSettings.authenticated=已验证 adminUserSettings.authenticated=已验证
database.title=数据库 导入/导出 database.title=Database Import/Export
database.header=数据库 导入/导出 database.header=Database Import/Export
database.fileName=文件名 database.fileName=File Name
database.creationDate=创建时间 database.creationDate=Creation Date
database.fileSize=文件大小 database.fileSize=File Size
database.deleteBackupFile=删除备份文件 database.deleteBackupFile=Delete Backup File
database.importBackupFile=导入备份文件 database.importBackupFile=Import Backup File
database.downloadBackupFile=下载备份文件 database.downloadBackupFile=Download Backup File
database.info_1=导入数据时,确保结构正确至关重要。如果您不确定自己在做什么,请寻求专业人士的建议和支持。结构错误会导致应用程序故障,甚至完全无法运行应用程序。 database.info_1=When importing data, it is crucial to ensure the correct structure. If you are unsure of what you are doing, seek advice and support from a professional. An error in the structure can cause application malfunctions, up to and including the complete inability to run the application.
database.info_2=上传文件时,文件名并不重要。上传后,文件名将重命名为 backup_user_yyyyMMddHHmm.sql以确保命名规范的一致性。 database.info_2=The file name does not matter when uploading. It will be renamed afterward to follow the format backup_user_yyyyMMddHHmm.sql, ensuring a consistent naming convention.
database.submit=导入备份 database.submit=Import Backup
database.importIntoDatabaseSuccessed=导入数据库成功 database.importIntoDatabaseSuccessed=Import into database successed
database.fileNotFound=未找到文件 database.fileNotFound=File not Found
database.fileNullOrEmpty=文件不能为空 database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=导入文件失败 database.failedImportFile=Failed Import File
############# #############
# HOME-PAGE # # HOME-PAGE #
@@ -1124,3 +1124,4 @@ error.showStack=显示堆栈跟踪
error.copyStack=复制堆栈跟踪 error.copyStack=复制堆栈跟踪
error.githubSubmit=GitHub - 提交工单 error.githubSubmit=GitHub - 提交工单
error.discordSubmit=Discord - 提交支持帖子 error.discordSubmit=Discord - 提交支持帖子

View File

@@ -9,7 +9,7 @@ multiPdfPrompt=選擇多個 PDF 檔案
multiPdfDropPrompt=選擇(或拖放)所有需要的 PDF 檔案 multiPdfDropPrompt=選擇(或拖放)所有需要的 PDF 檔案
imgPrompt=選擇圖片 imgPrompt=選擇圖片
genericSubmit=送出 genericSubmit=送出
processTimeWarning=警告:此過程可能長達一分鐘,具體取決於檔案大小 processTimeWarning=警告:此過程可能需要長達一分鐘,具體取決於檔案大小
pageOrderPrompt=自訂頁面順序(輸入以逗號分隔的頁碼或函式,如 2n+1 pageOrderPrompt=自訂頁面順序(輸入以逗號分隔的頁碼或函式,如 2n+1
pageSelectionPrompt=自訂頁面選擇(輸入以逗號分隔的頁碼 1、5、6 或 2n+1 等函數的清單): pageSelectionPrompt=自訂頁面選擇(輸入以逗號分隔的頁碼 1、5、6 或 2n+1 等函數的清單):
goToPage=前往 goToPage=前往
@@ -66,7 +66,7 @@ error=錯誤
oops=哎呀! oops=哎呀!
help=幫助 help=幫助
goHomepage=前往首頁 goHomepage=前往首頁
joinDiscord=加入我們的 Discord 服器 joinDiscord=加入我們的 Discord 服
seeDockerHub=查看 Docker Hub seeDockerHub=查看 Docker Hub
visitGithub=訪問 GitHub 存儲庫 visitGithub=訪問 GitHub 存儲庫
donate=捐贈 donate=捐贈
@@ -108,7 +108,7 @@ pipelineOptions.validateButton=驗證
# NAVBAR # # NAVBAR #
############# #############
navbar.favorite=我的最愛 navbar.favorite=我的最愛
navbar.darkmode=深色模式 navbar.darkmode=暗黑模式
navbar.language=語言 navbar.language=語言
navbar.settings=設定 navbar.settings=設定
navbar.allTools=工具 navbar.allTools=工具
@@ -125,7 +125,7 @@ navbar.sections.edit=檢視與編輯
############# #############
settings.title=設定 settings.title=設定
settings.update=有更新可用 settings.update=有更新可用
settings.updateAvailable=當前版本為 {0}。歡迎更新至最新版 ({1})。。 settings.updateAvailable=當前版本為 {0}。歡迎更新至最新版 ({1})。。
settings.appVersion=應用版本: settings.appVersion=應用版本:
settings.downloadOption.title=選擇下載選項(對於單一檔案非壓縮下載): settings.downloadOption.title=選擇下載選項(對於單一檔案非壓縮下載):
settings.downloadOption.1=在同一視窗中開啟 settings.downloadOption.1=在同一視窗中開啟
@@ -139,8 +139,8 @@ settings.cacheInputs.name=輸入檔案下載
settings.cacheInputs.help=開啟記住先前的輸入,做為日後使用 settings.cacheInputs.help=開啟記住先前的輸入,做為日後使用
changeCreds.title=變更憑證 changeCreds.title=變更憑證
changeCreds.header=更新的帳戶詳細資訊 changeCreds.header=更新的帳戶詳細資訊
changeCreds.changePassword=使用的是預設登錄認證。請輸入新密碼 changeCreds.changePassword=使用的是預設登錄認證。請輸入新密碼
changeCreds.newUsername=新使用者名稱 changeCreds.newUsername=新使用者名稱
changeCreds.oldPassword=目前密碼 changeCreds.oldPassword=目前密碼
changeCreds.newPassword=新密碼 changeCreds.newPassword=新密碼
@@ -161,7 +161,7 @@ account.newPassword=新密碼
account.changePassword=修改密碼 account.changePassword=修改密碼
account.confirmNewPassword=確認新密碼 account.confirmNewPassword=確認新密碼
account.signOut=登出 account.signOut=登出
account.yourApiKey=的 API 金鑰 account.yourApiKey=的 API 金鑰
account.syncTitle=將瀏覽器設定與帳戶同步 account.syncTitle=將瀏覽器設定與帳戶同步
account.settingsCompare=設定比較: account.settingsCompare=設定比較:
account.property=屬性 account.property=屬性
@@ -192,26 +192,26 @@ adminUserSettings.changeUserRole=更改使用者身份
adminUserSettings.authenticated=已驗證 adminUserSettings.authenticated=已驗證
database.title=資料庫匯入/匯出 database.title=Database Import/Export
database.header=資料庫匯入/匯出 database.header=Database Import/Export
database.fileName=檔案名稱 database.fileName=File Name
database.creationDate=建立日期 database.creationDate=Creation Date
database.fileSize=檔案大小 database.fileSize=File Size
database.deleteBackupFile=刪除備份檔案 database.deleteBackupFile=Delete Backup File
database.importBackupFile=匯入備份檔案 database.importBackupFile=Import Backup File
database.downloadBackupFile=下載備份檔案 database.downloadBackupFile=Download Backup File
database.info_1=在匯入資料時,確保正確的結構是至關重要的。如果你不確定自己在做什麼,請尋求專業人士的建議和支持。結構錯誤可能會導致應用程式故障,甚至完全無法運行。 database.info_1=When importing data, it is crucial to ensure the correct structure. If you are unsure of what you are doing, seek advice and support from a professional. An error in the structure can cause application malfunctions, up to and including the complete inability to run the application.
database.info_2=上傳時檔案名稱無關緊要。上傳後將重新命名為 backup_user_yyyyMMddHHmm.sql 格式,以確保命名規範一致。 database.info_2=The file name does not matter when uploading. It will be renamed afterward to follow the format backup_user_yyyyMMddHHmm.sql, ensuring a consistent naming convention.
database.submit=匯入備份 database.submit=Import Backup
database.importIntoDatabaseSuccessed=成功匯入資料庫 database.importIntoDatabaseSuccessed=Import into database successed
database.fileNotFound=檔案未找到 database.fileNotFound=File not Found
database.fileNullOrEmpty=檔案不能為空或空白 database.fileNullOrEmpty=File must not be null or empty
database.failedImportFile=匯入檔案失敗 database.failedImportFile=Failed Import File
############# #############
# HOME-PAGE # # HOME-PAGE #
############# #############
home.desc=的本地主機一站式 PDF 需求解決方案。 home.desc=的本地主機一站式 PDF 需求解決方案。
home.searchBar=搜尋功能... home.searchBar=搜尋功能...
@@ -232,7 +232,7 @@ home.split.desc=將 PDF 分割為多個文件
split.tags=頁面操作,劃分,多頁,剪下,伺服器端 split.tags=頁面操作,劃分,多頁,剪下,伺服器端
home.rotate.title=旋轉 home.rotate.title=旋轉
home.rotate.desc=輕鬆旋轉的 PDF。 home.rotate.desc=輕鬆旋轉的 PDF。
rotate.tags=伺服器端 rotate.tags=伺服器端
@@ -254,24 +254,24 @@ home.addImage.desc=在 PDF 的指定位置新增圖片
addImage.tags=img,jpg,圖片,照片 addImage.tags=img,jpg,圖片,照片
home.watermark.title=新增浮水印 home.watermark.title=新增浮水印
home.watermark.desc=的 PDF 檔案中新增自訂浮水印。 home.watermark.desc=的 PDF 檔案中新增自訂浮水印。
watermark.tags=文字,重複,標籤,自有,版權,商標,img,jpg,圖片,照片 watermark.tags=文字,重複,標籤,自有,版權,商標,img,jpg,圖片,照片
home.permissions.title=修改權限 home.permissions.title=修改權限
home.permissions.desc=修改的 PDF 檔案權限 home.permissions.desc=修改的 PDF 檔案權限
permissions.tags=讀取,寫入,編輯,列印 permissions.tags=讀取,寫入,編輯,列印
home.removePages.title=移除 home.removePages.title=移除
home.removePages.desc=的 PDF 檔案中刪除不需要的頁面。 home.removePages.desc=的 PDF 檔案中刪除不需要的頁面。
removePages.tags=移除頁面,刪除頁面 removePages.tags=移除頁面,刪除頁面
home.addPassword.title=新增密碼 home.addPassword.title=新增密碼
home.addPassword.desc=用密碼加密的 PDF 檔案。 home.addPassword.desc=用密碼加密的 PDF 檔案。
addPassword.tags=安全,安全性 addPassword.tags=安全,安全性
home.removePassword.title=移除密碼 home.removePassword.title=移除密碼
home.removePassword.desc=的 PDF 檔案中移除密碼保護。 home.removePassword.desc=的 PDF 檔案中移除密碼保護。
removePassword.tags=安全,解密,安全性,取消密碼,刪除密碼 removePassword.tags=安全,解密,安全性,取消密碼,刪除密碼
home.compressPdfs.title=壓縮 home.compressPdfs.title=壓縮
@@ -473,7 +473,7 @@ login.header=登入
login.signin=登入 login.signin=登入
login.rememberme=記住我 login.rememberme=記住我
login.invalid=使用者名稱或密碼無效。 login.invalid=使用者名稱或密碼無效。
login.locked=的帳戶已被鎖定。 login.locked=的帳戶已被鎖定。
login.signinTitle=請登入 login.signinTitle=請登入
login.ssoSignIn=透過織網單一簽入 login.ssoSignIn=透過織網單一簽入
login.oauth2AutoCreateDisabled=OAuth 2.0 自動建立使用者已停用 login.oauth2AutoCreateDisabled=OAuth 2.0 自動建立使用者已停用
@@ -664,15 +664,15 @@ scalePages.submit=送出
#certSign #certSign
certSign.title=憑證簽章 certSign.title=憑證簽章
certSign.header=使用的憑證簽章(進行中) certSign.header=使用的憑證簽章(進行中)
certSign.selectPDF=選擇要簽章的 PDF 檔案: certSign.selectPDF=選擇要簽章的 PDF 檔案:
certSign.jksNote=注意:如果的證書類型未在下面列出,請使用 keytool 命令行工具將其轉換為 Java Keystore .jks 檔。 然後,選擇下面的 .jks 文件選項。 certSign.jksNote=注意:如果的證書類型未在下面列出,請使用 keytool 命令行工具將其轉換為 Java Keystore .jks 檔。 然後,選擇下面的 .jks 文件選項。
certSign.selectKey=選擇的私鑰文件PKCS#8 格式,可能是 .pem 或 .der certSign.selectKey=選擇的私鑰文件PKCS#8 格式,可能是 .pem 或 .der
certSign.selectCert=選擇的憑證文件X.509 格式,可能是 .pem 或 .der certSign.selectCert=選擇的憑證文件X.509 格式,可能是 .pem 或 .der
certSign.selectP12=選擇的 PKCS#12 金鑰庫文件(.p12 或 .pfx可選如果提供它應包含的私鑰和憑證): certSign.selectP12=選擇的 PKCS#12 金鑰庫文件(.p12 或 .pfx可選如果提供它應包含的私鑰和憑證):
certSign.selectJKS=選擇你的 Java Keystore 檔 .jks 或 .keystore certSign.selectJKS=選擇你的 Java Keystore 檔 .jks 或 .keystore
certSign.certType=憑證類型 certSign.certType=憑證類型
certSign.password=輸入的金鑰庫或私鑰密碼(如果有): certSign.password=輸入的金鑰庫或私鑰密碼(如果有):
certSign.showSig=顯示簽章 certSign.showSig=顯示簽章
certSign.reason=原因 certSign.reason=原因
certSign.location=位置 certSign.location=位置
@@ -871,7 +871,7 @@ rotate.submit=旋轉
#split-pdfs #split-pdfs
split.title=分割 PDF split.title=分割 PDF
split.header=分割 PDF split.header=分割 PDF
split.desc.1=選擇的數字是希望進行分割的頁碼 split.desc.1=選擇的數字是希望進行分割的頁碼
split.desc.2=因此,選擇 1,3,7-9 將會將一個 10 頁的文件分割為 6 個單獨的 PDF包括 split.desc.2=因此,選擇 1,3,7-9 將會將一個 10 頁的文件分割為 6 個單獨的 PDF包括
split.desc.3=文件 #1頁面 1 split.desc.3=文件 #1頁面 1
split.desc.4=文件 #2頁面 2 和 3 split.desc.4=文件 #2頁面 2 和 3
@@ -978,7 +978,7 @@ removePassword.submit=移除
#changeMetadata #changeMetadata
changeMetadata.title=標題: changeMetadata.title=標題:
changeMetadata.header=變更中繼資料 changeMetadata.header=變更中繼資料
changeMetadata.selectText.1=請編輯希望變更的變數 changeMetadata.selectText.1=請編輯希望變更的變數
changeMetadata.selectText.2=刪除所有中繼資料 changeMetadata.selectText.2=刪除所有中繼資料
changeMetadata.selectText.3=顯示自訂中繼資料: changeMetadata.selectText.3=顯示自訂中繼資料:
changeMetadata.author=作者: changeMetadata.author=作者:
@@ -1103,13 +1103,13 @@ licenses.version=版本
licenses.license=許可證 licenses.license=許可證
#survey #survey
survey.nav=問卷調查 survey.nav=Survey
survey.title=Stirling-PDF 問卷調查 survey.title=Stirling-PDF Survey
survey.description=Stirling-PDF 沒有追蹤功能,所以我們希望聽取用戶的意見來改進 Stirling-PDF survey.description=Stirling-PDF has no tracking so we want to hear from our users to improve Stirling-PDF!
survey.please=請考慮參加我們的問卷調查! survey.please=Please consider taking our survey!
survey.disabled=(問卷調查彈出窗口將在後續更新中停用,但仍可在頁腳處使用) survey.disabled=(Survey popup will be disabled in following updates but available at foot of page)
survey.button=參加問卷調查 survey.button=Take Survey
survey.dontShowAgain=不要再次顯示 survey.dontShowAgain=Don't show again
#error #error
@@ -1124,3 +1124,4 @@ error.showStack=顯示堆疊追蹤
error.copyStack=複製堆疊追蹤 error.copyStack=複製堆疊追蹤
error.githubSubmit=GitHub - 提交工單 error.githubSubmit=GitHub - 提交工單
error.discordSubmit=Discord - 提交支援帖子 error.discordSubmit=Discord - 提交支援帖子

View File

@@ -25,22 +25,4 @@
user-select: none; user-select: none;
top: 0px; top: 0px;
left: 0; left: 0;
transform-origin: center center;
}
.rotate-handle {
width: 10px;
height: 10px;
background: blue;
position: absolute;
top: -20px;
left: 50%;
transform: translateX(-50%);
cursor: pointer;
}
.rotate-button {
background-color: rgba(13, 110, 253, 0.1);
border: none;
cursor: pointer;
padding: 5px;
margin: 2px;
} }

View File

@@ -36,22 +36,4 @@ select#font-select option {
user-select: none; user-select: none;
top: 0px; top: 0px;
left: 0; left: 0;
transform-origin: center center;
}
.rotate-handle {
width: 10px;
height: 10px;
background: blue;
position: absolute;
top: -20px;
left: 50%;
transform: translateX(-50%);
cursor: pointer;
}
.rotate-button {
background-color: rgba(13, 110, 253, 0.1);
border: none;
cursor: pointer;
padding: 5px;
margin: 2px;
} }

View File

@@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-dk" viewBox="0 0 640 480">
<path fill="#c8102e" d="M0 0h640.1v480H0z"/>
<path fill="#fff" d="M205.7 0h68.6v480h-68.6z"/>
<path fill="#fff" d="M0 205.7h640.1v68.6H0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 236 B

View File

@@ -1,7 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-ie" viewBox="0 0 640 480">
<g fill-rule="evenodd" stroke-width="1pt">
<path fill="#fff" d="M0 0h640v480H0z"/>
<path fill="#009A49" d="M0 0h213.3v480H0z"/>
<path fill="#FF7900" d="M426.7 0H640v480H426.7z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 289 B

View File

@@ -1,11 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-vn" viewBox="0 0 640 480">
<defs>
<clipPath id="vn-a">
<path fill-opacity=".7" d="M-85.3 0h682.6v512H-85.3z"/>
</clipPath>
</defs>
<g fill-rule="evenodd" clip-path="url(#vn-a)" transform="translate(80)scale(.9375)">
<path fill="#da251d" d="M-128 0h768v512h-768z"/>
<path fill="#ff0" d="M349.6 381 260 314.3l-89 67.3L204 272l-89-67.7 110.1-1 34.2-109.4L294 203l110.1.1-88.5 68.4 33.9 109.6z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 490 B

View File

@@ -18,7 +18,7 @@ const DraggableUtils = {
const y = (parseFloat(target.getAttribute("data-bs-y")) || 0) const y = (parseFloat(target.getAttribute("data-bs-y")) || 0)
+ event.dy; + event.dy;
target.style.transform = `translate(${x}px, ${y}px) rotate(${target.getAttribute("data-rotation") || 0}deg)`; target.style.transform = `translate(${x}px, ${y}px)`;
target.setAttribute("data-bs-x", x); target.setAttribute("data-bs-x", x);
target.setAttribute("data-bs-y", y); target.setAttribute("data-bs-y", y);
@@ -61,7 +61,7 @@ const DraggableUtils = {
x += event.deltaRect.left; x += event.deltaRect.left;
y += event.deltaRect.top; y += event.deltaRect.top;
target.style.transform = `translate(${x}px, ${y}px) rotate(${target.getAttribute("data-rotation") || 0}deg)`; target.style.transform = "translate(" + x + "px," + y + "px)";
target.setAttribute("data-bs-x", x); target.setAttribute("data-bs-x", x);
target.setAttribute("data-bs-y", y); target.setAttribute("data-bs-y", y);
@@ -78,17 +78,6 @@ const DraggableUtils = {
}), }),
], ],
inertia: true, inertia: true,
})
.gesturable({
listeners: {
move: (event) => {
const target = event.target;
const rotation = (parseFloat(target.getAttribute("data-rotation")) || 0) + event.da;
target.style.transform = `translate(${parseFloat(target.getAttribute("data-bs-x")) || 0}px, ${parseFloat(target.getAttribute("data-bs-y")) || 0}px) rotate(${rotation}deg)`;
target.setAttribute("data-rotation", rotation);
this.onInteraction(target);
}
}
}); });
//Arrow key Support for Add-Image and Sign pages //Arrow key Support for Add-Image and Sign pages
if(window.location.pathname.endsWith('sign') || window.location.pathname.endsWith('add-image')) { if(window.location.pathname.endsWith('sign') || window.location.pathname.endsWith('add-image')) {
@@ -131,7 +120,7 @@ const DraggableUtils = {
} }
// Update position // Update position
target.style.transform = `translate(${x}px, ${y}px) rotate(${target.getAttribute("data-rotation") || 0}deg)`; target.style.transform = `translate(${x}px, ${y}px)`;
target.setAttribute('data-bs-x', x); target.setAttribute('data-bs-x', x);
target.setAttribute('data-bs-y', y); target.setAttribute('data-bs-y', y);
@@ -151,10 +140,9 @@ const DraggableUtils = {
const x = 0; const x = 0;
const y = 20; const y = 20;
createdCanvas.style.transform = `translate(${x}px, ${y}px) rotate(0deg)`; createdCanvas.style.transform = `translate(${x}px, ${y}px)`;
createdCanvas.setAttribute("data-bs-x", x); createdCanvas.setAttribute("data-bs-x", x);
createdCanvas.setAttribute("data-bs-y", y); createdCanvas.setAttribute("data-bs-y", y);
createdCanvas.setAttribute("data-rotation", 0);
//Click element in order to enable arrow keys //Click element in order to enable arrow keys
createdCanvas.addEventListener('click', () => { createdCanvas.addEventListener('click', () => {
@@ -235,7 +223,6 @@ const DraggableUtils = {
element: el, element: el,
offsetWidth: el.offsetWidth, offsetWidth: el.offsetWidth,
offsetHeight: el.offsetHeight, offsetHeight: el.offsetHeight,
rotation: el.getAttribute("data-rotation") || 0,
}; };
}); });
elements.forEach((el) => this.boxDragContainer.removeChild(el)); elements.forEach((el) => this.boxDragContainer.removeChild(el));
@@ -255,11 +242,7 @@ const DraggableUtils = {
const draggablesData = pagesMap[this.pageIndex]; const draggablesData = pagesMap[this.pageIndex];
if (draggablesData) { if (draggablesData) {
draggablesData.forEach((draggableData) => { draggablesData.forEach((draggableData) => this.boxDragContainer.appendChild(draggableData.element));
const el = draggableData.element;
el.style.transform = `translate(${parseFloat(el.getAttribute("data-bs-x")) || 0}px, ${parseFloat(el.getAttribute("data-bs-y")) || 0}px) rotate(${draggableData.rotation}deg)`;
this.boxDragContainer.appendChild(el);
});
} }
this.documentsMap.set(this.pdfDoc, pagesMap); this.documentsMap.set(this.pdfDoc, pagesMap);
@@ -340,7 +323,6 @@ const DraggableUtils = {
y: parseFloat(transformComponents[1]), y: parseFloat(transformComponents[1]),
width: draggableData.offsetWidth, width: draggableData.offsetWidth,
height: draggableData.offsetHeight, height: draggableData.offsetHeight,
rotation: parseFloat(draggableData.rotation),
}; };
const draggablePositionRelative = { const draggablePositionRelative = {
x: draggablePositionPixels.x / offsetWidth, x: draggablePositionPixels.x / offsetWidth,
@@ -353,7 +335,6 @@ const DraggableUtils = {
y: draggablePositionRelative.y * page.getHeight(), y: draggablePositionRelative.y * page.getHeight(),
width: draggablePositionRelative.width * page.getWidth(), width: draggablePositionRelative.width * page.getWidth(),
height: draggablePositionRelative.height * page.getHeight(), height: draggablePositionRelative.height * page.getHeight(),
rotation: draggablePositionPixels.rotation,
}; };
// draw the image // draw the image
@@ -362,7 +343,6 @@ const DraggableUtils = {
y: page.getHeight() - draggablePositionPdf.y - draggablePositionPdf.height, y: page.getHeight() - draggablePositionPdf.y - draggablePositionPdf.height,
width: draggablePositionPdf.width, width: draggablePositionPdf.width,
height: draggablePositionPdf.height, height: draggablePositionPdf.height,
rotate: PDFLib.degrees(draggablePositionPdf.rotation),
}); });
} }
} }
@@ -370,13 +350,6 @@ const DraggableUtils = {
this.loadPageContents(); this.loadPageContents();
return pdfDocModified; return pdfDocModified;
}, },
rotateElement(element, angle) {
const currentRotation = parseFloat(element.getAttribute("data-rotation")) || 0;
const newRotation = currentRotation + angle;
element.style.transform = `translate(${parseFloat(element.getAttribute("data-bs-x")) || 0}px, ${parseFloat(element.getAttribute("data-bs-y")) || 0}px) rotate(${newRotation}deg)`;
element.setAttribute("data-rotation", newRotation);
},
}; };
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {

View File

@@ -4,7 +4,6 @@
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ca_CA"> <img th:src="@{'/images/flags/es-ct.svg'}" alt="icon" width="20" height="15"> Català</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="ca_CA"> <img th:src="@{'/images/flags/es-ct.svg'}" alt="icon" width="20" height="15"> Català</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_CN"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> 简体中文</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_CN"> <img th:src="@{'/images/flags/cn.svg'}" alt="icon" width="20" height="15"> 简体中文</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_TW"> <img th:src="@{'/images/flags/tw.svg'}" alt="icon" width="20" height="15"> 繁體中文</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="zh_TW"> <img th:src="@{'/images/flags/tw.svg'}" alt="icon" width="20" height="15"> 繁體中文</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="da_DK"> <img th:src="@{'/images/flags/dk.svg'}" alt="icon" width="20" height="15"> Dansk</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="de_DE"> <img th:src="@{'/images/flags/de.svg'}" alt="icon" width="20" height="15"> Deutsch</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="de_DE"> <img th:src="@{'/images/flags/de.svg'}" alt="icon" width="20" height="15"> Deutsch</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_GB"> <img th:src="@{'/images/flags/gb.svg'}" alt="icon" width="20" height="15"> English (GB)</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_GB"> <img th:src="@{'/images/flags/gb.svg'}" alt="icon" width="20" height="15"> English (GB)</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_US"> <img th:src="@{'/images/flags/us.svg'}" alt="icon" width="20" height="15"> English (US)</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="en_US"> <img th:src="@{'/images/flags/us.svg'}" alt="icon" width="20" height="15"> English (US)</a>
@@ -12,7 +11,6 @@
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES"> <img th:src="@{'/images/flags/es.svg'}" alt="icon" width="20" height="15"> Español</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="es_ES"> <img th:src="@{'/images/flags/es.svg'}" alt="icon" width="20" height="15"> Español</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fr_FR"> <img th:src="@{'/images/flags/fr.svg'}" alt="icon" width="20" height="15"> Français</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="fr_FR"> <img th:src="@{'/images/flags/fr.svg'}" alt="icon" width="20" height="15"> Français</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img th:src="@{'/images/flags/id.svg'}" alt="icon" width="20" height="15"> Indonesia</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img th:src="@{'/images/flags/id.svg'}" alt="icon" width="20" height="15"> Indonesia</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="id_ID"> <img th:src="@{'/images/flags/ie.svg'}" alt="icon" width="20" height="15"> Irish</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img th:src="@{'/images/flags/it.svg'}" alt="icon" width="20" height="15"> Italiano</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="it_IT"> <img th:src="@{'/images/flags/it.svg'}" alt="icon" width="20" height="15"> Italiano</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img th:src="@{'/images/flags/nl.svg'}" alt="icon" width="20" height="15"> Nederlands</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="nl_NL"> <img th:src="@{'/images/flags/nl.svg'}" alt="icon" width="20" height="15"> Nederlands</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="pl_PL"> <img th:src="@{'/images/flags/pl.svg'}" alt="icon" width="20" height="15"> Polski</a>
@@ -34,5 +32,4 @@
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hr_HR"> <img th:src="@{'/images/flags/hr.svg'}" alt="icon" width="20" height="15"> Hrvatski</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="hr_HR"> <img th:src="@{'/images/flags/hr.svg'}" alt="icon" width="20" height="15"> Hrvatski</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="no_NB"> <img th:src="@{'/images/flags/no.svg'}" alt="icon" width="20" height="15"> Norsk</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="no_NB"> <img th:src="@{'/images/flags/no.svg'}" alt="icon" width="20" height="15"> Norsk</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="th_TH"> <img th:src="@{'/images/flags/th.svg'}" alt="icon" width="20" height="15"> ไทย</a> <a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="th_TH"> <img th:src="@{'/images/flags/th.svg'}" alt="icon" width="20" height="15"> ไทย</a>
<a class="dropdown-item lang_dropdown-item" href="" data-bs-language-code="vi_VN"> <img th:src="@{'/images/flags/vn.svg'}" alt="icon" width="20" height="15"> Tiếng Việt</a>
</th:block> </th:block>

View File

@@ -83,21 +83,7 @@
</button> </button>
<button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.decrementPage() : DraggableUtils.incrementPage()"> <button class="btn btn-outline-secondary" onclick="document.documentElement.getAttribute('dir')==='rtl' ? DraggableUtils.decrementPage() : DraggableUtils.incrementPage()">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-right" viewBox="0 0 16 16"> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-right" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6-6a.5.5 0 0 1 0-.708z"/> <path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"/>
</svg>
</button>
<button class="btn btn-outline-secondary" onclick="DraggableUtils.rotateElement(DraggableUtils.getLastInteracted(), -90)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.418A6 6 0 1 1 8 2v1z"/>
<path d="M8 1a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0v-4A.5.5 0 0 1 8 1z"/>
<path d="M8 4.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z"/>
</svg>
</button>
<button class="btn btn-outline-secondary" onclick="DraggableUtils.rotateElement(DraggableUtils.getLastInteracted(), 90)">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.418A6 6 0 1 0 8 2v1z"/>
<path d="M8 1a.5.5 0 0 0-.5.5v4a.5.5 0 0 0 1 0v-4A.5.5 0 0 0 8 1z"/>
<path d="M8 4.5a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 .5-.5z"/>
</svg> </svg>
</button> </button>
</div> </div>
@@ -117,7 +103,6 @@
link.download = originalFileName + '_addedImage.pdf'; link.download = originalFileName + '_addedImage.pdf';
link.click(); link.click();
}); });
DraggableUtils.init();
</script> </script>
</div> </div>
</div> </div>

View File

@@ -247,20 +247,6 @@
<path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"/> <path fill-rule="evenodd" d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"/>
</svg> </svg>
</button> </button>
<button class="btn btn-outline-secondary" onclick="DraggableUtils.rotateLeft()">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-counterclockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.418A6 6 0 1 1 8 2v1z"/>
<path d="M8 1a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0v-4A.5.5 0 0 1 8 1z"/>
<path d="M8 4.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5z"/>
</svg>
</button>
<button class="btn btn-outline-secondary" onclick="DraggableUtils.rotateRight()">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 1-4.546 2.914.5.5 0 0 0-.908-.418A6 6 0 1 0 8 2v1z"/>
<path d="M8 1a.5.5 0 0 0-.5.5v4a.5.5 0 0 0 1 0v-4A.5.5 0 0 0 8 1z"/>
<path d="M8 4.5a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 0 0 1h4a.5.5 0 0 0 .5-.5z"/>
</svg>
</button>
</div> </div>
</div> </div>
@@ -279,7 +265,6 @@
link.download = originalFileName + '_signed.pdf'; link.download = originalFileName + '_signed.pdf';
link.click(); link.click();
}); });
DraggableUtils.init();
</script> </script>
</div> </div>
</div> </div>

View File

@@ -13,7 +13,7 @@ public class FileToPdfTest {
@Test @Test
public void testConvertHtmlToPdf() { public void testConvertHtmlToPdf() {
HTMLToPdfRequest request = new HTMLToPdfRequest(); HTMLToPdfRequest request = new HTMLToPdfRequest();
byte[] fileBytes = new byte[0]; // Sample file bytes byte[] fileBytes = new byte[10]; // Sample file bytes
String fileName = "test.html"; // Sample file name String fileName = "test.html"; // Sample file name
boolean htmlFormatsInstalled = true; // Sample boolean value boolean htmlFormatsInstalled = true; // Sample boolean value

View File

@@ -1,11 +1,10 @@
package stirling.software.SPDF; package stirling.software.SPDF.utils;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -15,6 +14,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import stirling.software.SPDF.SPdfApplication;
import stirling.software.SPDF.model.ApplicationProperties; import stirling.software.SPDF.model.ApplicationProperties;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@@ -43,43 +43,12 @@ public class SPdfApplicationTest {
@Test @Test
public void testMainApplicationStartup() throws IOException, InterruptedException { public void testMainApplicationStartup() throws IOException, InterruptedException {
// Setup mock environment for the main method
Path configPath = Path.of("test/configs");
Path settingsPath = Paths.get("test/configs/settings.yml");
Path customSettingsPath = Paths.get("test/configs/custom_settings.yml");
Path staticPath = Path.of("test/customFiles/static/");
Path templatesPath = Path.of("test/customFiles/templates/");
// Ensure the files do not exist for the test
if (Files.exists(settingsPath)) {
Files.delete(settingsPath);
}
if (Files.exists(customSettingsPath)) {
Files.delete(customSettingsPath);
}
if (Files.exists(staticPath)) {
Files.delete(staticPath);
}
if (Files.exists(templatesPath)) {
Files.delete(templatesPath);
}
// Ensure the directories are created for testing
Files.createDirectories(configPath);
Files.createDirectories(staticPath);
Files.createDirectories(templatesPath);
Files.createFile(settingsPath);
Files.createFile(customSettingsPath);
// Run the main method // Run the main method
SPdfApplication.main(new String[]{}); SPdfApplication.main(new String[]{});
// Verify that the directories were created // Verify that the directories were created
assertTrue(Files.exists(settingsPath)); assertTrue(Files.exists(Path.of("customFiles/static/")));
assertTrue(Files.exists(customSettingsPath)); assertTrue(Files.exists(Path.of("customFiles/templates/")));
assertTrue(Files.exists(staticPath));
assertTrue(Files.exists(templatesPath));
} }
@Test @Test