From 1ec8112efc4580403e323ca5b4f9e4a8401c1775 Mon Sep 17 00:00:00 2001 From: Dario Ghunney Ware Date: Mon, 10 Mar 2025 19:04:30 +0000 Subject: [PATCH 01/10] New Claim Attributes `mail` & `uid` (#3154) # Description of Changes Added new claims to `UsernameAttributes`: - `mail` - `uid` Closes #3115 --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [x] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [x] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [x] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- .../java/stirling/software/SPDF/model/UsernameAttribute.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/stirling/software/SPDF/model/UsernameAttribute.java b/src/main/java/stirling/software/SPDF/model/UsernameAttribute.java index 23e098a49..23159ade1 100644 --- a/src/main/java/stirling/software/SPDF/model/UsernameAttribute.java +++ b/src/main/java/stirling/software/SPDF/model/UsernameAttribute.java @@ -4,10 +4,12 @@ import lombok.Getter; @Getter public enum UsernameAttribute { + MAIL("mail"), EMAIL("email"), LOGIN("login"), PROFILE("profile"), NAME("name"), + UID("uid"), USERNAME("username"), NICKNAME("nickname"), GIVEN_NAME("given_name"), From d0a541657018b69c2e6fa02cb0a6459528e846d5 Mon Sep 17 00:00:00 2001 From: Ludy Date: Mon, 10 Mar 2025 20:04:50 +0100 Subject: [PATCH 02/10] Fix: wrong credentials for Dockerfile.dev (#3155) # Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- .github/labeler-config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/labeler-config.yml b/.github/labeler-config.yml index c64ec320c..d72607386 100644 --- a/.github/labeler-config.yml +++ b/.github/labeler-config.yml @@ -55,7 +55,7 @@ Docker: - any-glob-to-any-file: '.github/workflows/push-docker.yml' - any-glob-to-any-file: 'Dockerfile' - any-glob-to-any-file: 'Dockerfile.*' - - any-glob-to-any-file: '!Dockerfile.dev' + - all-globs-to-all-files: '!Dockerfile.dev' - any-glob-to-any-file: 'exampleYmlFiles/*.yml' - any-glob-to-any-file: 'scripts/download-security-jar.sh' - any-glob-to-any-file: 'scripts/init.sh' @@ -67,7 +67,7 @@ Docker: Devtools: - changed-files: - any-glob-to-any-file: '.devcontainer/**/*' - - any-glob-to-any-file: '!Dockerfile.dev' + - any-glob-to-any-file: 'Dockerfile.dev' Test: - changed-files: From a61749d5003e2a2f3bc483154b9f59bbd0c99f44 Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Mon, 10 Mar 2025 20:17:45 +0000 Subject: [PATCH 03/10] removal of all getByte loads (#3153) # Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: a --- .gitignore | 2 +- Dockerfile.fat | 10 +- build.gradle | 2 +- .../util/DeletingRandomAccessFile.java | 39 +++ .../controller/api/AnalysisController.java | 16 +- .../SPDF/controller/api/CropController.java | 2 +- .../SPDF/controller/api/MergeController.java | 4 +- .../api/MultiPageLayoutController.java | 2 +- .../api/RearrangePagesPDFController.java | 2 +- .../controller/api/ScalePagesController.java | 2 +- .../controller/api/SplitPDFController.java | 2 +- .../api/SplitPdfByChaptersController.java | 2 +- .../api/SplitPdfBySectionsController.java | 2 +- .../api/ToSinglePageController.java | 2 +- .../converters/ConvertImgPDFController.java | 7 +- .../api/converters/ConvertPDFToOffice.java | 2 +- .../api/converters/ExtractCSVController.java | 2 +- .../api/filters/FilterController.java | 10 +- .../api/misc/AutoRenameController.java | 2 +- .../api/misc/BlankPageController.java | 2 +- .../api/misc/DecompressPdfController.java | 2 +- .../api/misc/ExtractImageScansController.java | 3 +- .../api/misc/ExtractImagesController.java | 2 +- .../api/misc/FlattenController.java | 2 +- .../api/misc/MetadataController.java | 2 +- .../api/misc/PageNumbersController.java | 3 +- .../controller/api/misc/ShowJavascript.java | 2 +- .../api/security/CertSignController.java | 4 +- .../controller/api/security/GetInfoOnPDF.java | 2 +- .../SPDF/model/api/PDFWithPageNums.java | 14 -- .../SPDF/service/CustomPDDocumentFactory.java | 147 ++++++----- .../software/SPDF/utils/GeneralUtils.java | 11 +- testing/test.sh | 175 ++++++++++++- testing/test_webpages.sh | 235 +++++++++++------- 34 files changed, 504 insertions(+), 214 deletions(-) create mode 100644 src/main/java/org/apache/pdfbox/examples/util/DeletingRandomAccessFile.java diff --git a/.gitignore b/.gitignore index 55ae9bdb7..e5d8ad209 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,7 @@ clientWebUI/ !cucumber/exampleFiles/ !cucumber/exampleFiles/example_html.zip exampleYmlFiles/stirling/ - +/testing/file_snapshots # Gradle .gradle .lock diff --git a/Dockerfile.fat b/Dockerfile.fat index b521b95cc..2cc2d2133 100644 --- a/Dockerfile.fat +++ b/Dockerfile.fat @@ -1,5 +1,11 @@ # Build the application -FROM gradle:8.12-jdk17 AS build +FROM gradle:8.12-jdk21 AS build + +COPY build.gradle . +COPY settings.gradle . +COPY gradlew . +COPY gradle gradle/ +RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0 # Set the working directory WORKDIR /app @@ -10,7 +16,7 @@ COPY . . # Build the application with DOCKER_ENABLE_SECURITY=false RUN DOCKER_ENABLE_SECURITY=true \ STIRLING_PDF_DESKTOP_UI=false \ - ./gradlew clean build + ./gradlew clean build -x spotlessApply -x spotlessCheck -x test -x sonarqube # Main stage FROM alpine:3.21.3@sha256:a8560b36e8b8210634f77d9f7f9efd7ffa463e380b75e2e74aff4511df3ef88c diff --git a/build.gradle b/build.gradle index 11d89d071..0e1ccb47b 100644 --- a/build.gradle +++ b/build.gradle @@ -25,7 +25,7 @@ ext { } group = "stirling.software" -version = "0.44.0" +version = "0.44.1" java { // 17 is lowest but we support and recommend 21 diff --git a/src/main/java/org/apache/pdfbox/examples/util/DeletingRandomAccessFile.java b/src/main/java/org/apache/pdfbox/examples/util/DeletingRandomAccessFile.java new file mode 100644 index 000000000..2c0341e19 --- /dev/null +++ b/src/main/java/org/apache/pdfbox/examples/util/DeletingRandomAccessFile.java @@ -0,0 +1,39 @@ +package org.apache.pdfbox.examples.util; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.apache.pdfbox.io.RandomAccessReadBufferedFile; + +import lombok.extern.slf4j.Slf4j; + +/** A custom RandomAccessRead implementation that deletes the file when closed */ +@Slf4j +public class DeletingRandomAccessFile extends RandomAccessReadBufferedFile { + private final Path tempFilePath; + + public DeletingRandomAccessFile(File file) throws IOException { + super(file); + this.tempFilePath = file.toPath(); + } + + @Override + public void close() throws IOException { + try { + super.close(); + } finally { + try { + boolean deleted = Files.deleteIfExists(tempFilePath); + if (deleted) { + log.info("Successfully deleted temp file: {}", tempFilePath); + } else { + log.warn("Failed to delete temp file (may not exist): {}", tempFilePath); + } + } catch (IOException e) { + log.error("Error deleting temp file: {}", tempFilePath, e); + } + } + } +} diff --git a/src/main/java/stirling/software/SPDF/controller/api/AnalysisController.java b/src/main/java/stirling/software/SPDF/controller/api/AnalysisController.java index 8c97605b0..37941017e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/AnalysisController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/AnalysisController.java @@ -37,7 +37,7 @@ public class AnalysisController { summary = "Get PDF page count", description = "Returns total number of pages in PDF. Input:PDF Output:JSON Type:SISO") public Map getPageCount(@ModelAttribute PDFFile file) throws IOException { - try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) { return Map.of("pageCount", document.getNumberOfPages()); } } @@ -47,7 +47,7 @@ public class AnalysisController { summary = "Get basic PDF information", description = "Returns page count, version, file size. Input:PDF Output:JSON Type:SISO") public Map getBasicInfo(@ModelAttribute PDFFile file) throws IOException { - try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) { Map info = new HashMap<>(); info.put("pageCount", document.getNumberOfPages()); info.put("pdfVersion", document.getVersion()); @@ -62,7 +62,7 @@ public class AnalysisController { description = "Returns title, author, subject, etc. Input:PDF Output:JSON Type:SISO") public Map getDocumentProperties(@ModelAttribute PDFFile file) throws IOException { - try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) { PDDocumentInformation info = document.getDocumentInformation(); Map properties = new HashMap<>(); properties.put("title", info.getTitle()); @@ -83,7 +83,7 @@ public class AnalysisController { description = "Returns width and height of each page. Input:PDF Output:JSON Type:SISO") public List> getPageDimensions(@ModelAttribute PDFFile file) throws IOException { - try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) { List> dimensions = new ArrayList<>(); PDPageTree pages = document.getPages(); @@ -103,7 +103,7 @@ public class AnalysisController { description = "Returns count and details of form fields. Input:PDF Output:JSON Type:SISO") public Map getFormFields(@ModelAttribute PDFFile file) throws IOException { - try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) { Map formInfo = new HashMap<>(); PDAcroForm form = document.getDocumentCatalog().getAcroForm(); @@ -125,7 +125,7 @@ public class AnalysisController { summary = "Get annotation information", description = "Returns count and types of annotations. Input:PDF Output:JSON Type:SISO") public Map getAnnotationInfo(@ModelAttribute PDFFile file) throws IOException { - try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) { Map annotInfo = new HashMap<>(); int totalAnnotations = 0; Map annotationTypes = new HashMap<>(); @@ -150,7 +150,7 @@ public class AnalysisController { description = "Returns list of fonts used in the document. Input:PDF Output:JSON Type:SISO") public Map getFontInfo(@ModelAttribute PDFFile file) throws IOException { - try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) { Map fontInfo = new HashMap<>(); Set fontNames = new HashSet<>(); @@ -172,7 +172,7 @@ public class AnalysisController { description = "Returns encryption and permission details. Input:PDF Output:JSON Type:SISO") public Map getSecurityInfo(@ModelAttribute PDFFile file) throws IOException { - try (PDDocument document = pdfDocumentFactory.load(file.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(file.getFileInput())) { Map securityInfo = new HashMap<>(); PDEncryption encryption = document.getEncryption(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/CropController.java b/src/main/java/stirling/software/SPDF/controller/api/CropController.java index d3e4933f5..68d252a47 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/CropController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/CropController.java @@ -42,7 +42,7 @@ public class CropController { description = "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO") public ResponseEntity cropPdf(@ModelAttribute CropPdfForm form) throws IOException { - PDDocument sourceDocument = pdfDocumentFactory.load(form.getFileInput().getBytes()); + PDDocument sourceDocument = pdfDocumentFactory.load(form); PDDocument newDocument = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); 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 416546f4a..8c63f817e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MergeController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MergeController.java @@ -100,8 +100,8 @@ public class MergeController { }; case "byPDFTitle": return (file1, file2) -> { - try (PDDocument doc1 = pdfDocumentFactory.load(file1.getBytes()); - PDDocument doc2 = pdfDocumentFactory.load(file2.getBytes())) { + try (PDDocument doc1 = pdfDocumentFactory.load(file1); + PDDocument doc2 = pdfDocumentFactory.load(file2)) { String title1 = doc1.getDocumentInformation().getTitle(); String title2 = doc2.getDocumentInformation().getTitle(); return title1.compareTo(title2); diff --git a/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java b/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java index 76ad5e75a..56d02a686 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/MultiPageLayoutController.java @@ -63,7 +63,7 @@ public class MultiPageLayoutController { : (int) Math.sqrt(pagesPerSheet); int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet); - PDDocument sourceDocument = pdfDocumentFactory.load(file.getBytes()); + PDDocument sourceDocument = pdfDocumentFactory.load(file); PDDocument newDocument = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); PDPage newPage = new PDPage(PDRectangle.A4); 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 58e69b720..09f8afe9c 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/RearrangePagesPDFController.java @@ -250,7 +250,7 @@ public class RearrangePagesPDFController { String sortType = request.getCustomMode(); try { // Load the input PDF - PDDocument document = pdfDocumentFactory.load(pdfFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(pdfFile); // Split the page order string into an array of page numbers or range of numbers String[] pageOrderArr = pageOrder != null ? pageOrder.split(",") : new String[0]; diff --git a/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java b/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java index c1715347d..5fb62cafc 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/ScalePagesController.java @@ -51,7 +51,7 @@ public class ScalePagesController { String targetPDRectangle = request.getPageSize(); float scaleFactor = request.getScaleFactor(); - PDDocument sourceDocument = pdfDocumentFactory.load(file.getBytes()); + PDDocument sourceDocument = pdfDocumentFactory.load(file); PDDocument outputDocument = pdfDocumentFactory.createNewDocumentBasedOnOldDocument(sourceDocument); 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 573bfb7da..d0df4ced5 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPDFController.java @@ -62,7 +62,7 @@ public class SplitPDFController { String pages = request.getPageNumbers(); // open the pdf document - document = pdfDocumentFactory.load(file.getBytes()); + document = pdfDocumentFactory.load(file); // PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document); int totalPages = document.getNumberOfPages(); List pageNumbers = request.getPageNumbersList(document, false); diff --git a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java index 195dbd0dd..b774d3ced 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfByChaptersController.java @@ -139,7 +139,7 @@ public class SplitPdfByChaptersController { if (bookmarkLevel < 0) { return ResponseEntity.badRequest().body("Invalid bookmark level".getBytes()); } - sourceDocument = pdfDocumentFactory.load(file.getBytes()); + sourceDocument = pdfDocumentFactory.load(file); PDDocumentOutline outline = sourceDocument.getDocumentCatalog().getDocumentOutline(); 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 1a3842e05..2a692cbca 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/SplitPdfBySectionsController.java @@ -56,7 +56,7 @@ public class SplitPdfBySectionsController { List splitDocumentsBoas = new ArrayList<>(); MultipartFile file = request.getFileInput(); - PDDocument sourceDocument = pdfDocumentFactory.load(file.getBytes()); + PDDocument sourceDocument = pdfDocumentFactory.load(file); // Process the PDF based on split parameters int horiz = request.getHorizontalDivisions() + 1; diff --git a/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java b/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java index 4e851a482..00bdf827e 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/ToSinglePageController.java @@ -45,7 +45,7 @@ public class ToSinglePageController { throws IOException { // Load the source document - PDDocument sourceDocument = pdfDocumentFactory.load(request.getFileInput().getBytes()); + PDDocument sourceDocument = pdfDocumentFactory.load(request); // Calculate total height and max width float totalHeight = 0; 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 3e277ab16..103f6e1c8 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 @@ -74,7 +74,7 @@ public class ConvertImgPDFController { ; try { // Load the input PDF - byte[] newPdfBytes = rearrangePdfPages(file.getBytes(), pageOrderArr); + byte[] newPdfBytes = rearrangePdfPages(file, pageOrderArr); ImageType colorTypeResult = ImageType.RGB; if ("greyscale".equals(colorType)) { @@ -243,9 +243,10 @@ public class ConvertImgPDFController { * @return A byte array of the rearranged PDF. * @throws IOException If an error occurs while processing the PDF. */ - private byte[] rearrangePdfPages(byte[] pdfBytes, String[] pageOrderArr) throws IOException { + private byte[] rearrangePdfPages(MultipartFile pdfFile, String[] pageOrderArr) + throws IOException { // Load the input PDF - PDDocument document = pdfDocumentFactory.load(pdfBytes); + PDDocument document = pdfDocumentFactory.load(pdfFile); int totalPages = document.getNumberOfPages(); List newPageOrder = GeneralUtils.parsePageList(pageOrderArr, totalPages, false); 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 58b6fd7fe..39c808096 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 @@ -62,7 +62,7 @@ public class ConvertPDFToOffice { MultipartFile inputFile = request.getFileInput(); String outputFormat = request.getOutputFormat(); if ("txt".equals(request.getOutputFormat())) { - try (PDDocument document = pdfDocumentFactory.load(inputFile.getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(inputFile)) { PDFTextStripper stripper = new PDFTextStripper(); String text = stripper.getText(document); return WebResponseUtils.bytesToWebResponse( diff --git a/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java b/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java index 54620113c..359d353d7 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/converters/ExtractCSVController.java @@ -59,7 +59,7 @@ public class ExtractCSVController { String baseName = getBaseName(form.getFileInput().getOriginalFilename()); List csvEntries = new ArrayList<>(); - try (PDDocument document = pdfDocumentFactory.load(form.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(form)) { List pages = form.getPageNumbersList(document, true); SpreadsheetExtractionAlgorithm sea = new SpreadsheetExtractionAlgorithm(); CSVFormat format = diff --git a/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java b/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java index 7ddded2a6..66ac4cb8b 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/filters/FilterController.java @@ -49,7 +49,7 @@ public class FilterController { String text = request.getText(); String pageNumber = request.getPageNumbers(); - PDDocument pdfDocument = pdfDocumentFactory.load(inputFile.getBytes()); + PDDocument pdfDocument = pdfDocumentFactory.load(inputFile); if (PdfUtils.hasText(pdfDocument, pageNumber, text)) return WebResponseUtils.pdfDocToWebResponse( pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename())); @@ -66,7 +66,7 @@ public class FilterController { MultipartFile inputFile = request.getFileInput(); String pageNumber = request.getPageNumbers(); - PDDocument pdfDocument = pdfDocumentFactory.load(inputFile.getBytes()); + PDDocument pdfDocument = pdfDocumentFactory.load(inputFile); if (PdfUtils.hasImages(pdfDocument, pageNumber)) return WebResponseUtils.pdfDocToWebResponse( pdfDocument, Filenames.toSimpleFileName(inputFile.getOriginalFilename())); @@ -83,7 +83,7 @@ public class FilterController { String pageCount = request.getPageCount(); String comparator = request.getComparator(); // Load the PDF - PDDocument document = pdfDocumentFactory.load(inputFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(inputFile); int actualPageCount = document.getNumberOfPages(); boolean valid = false; @@ -117,7 +117,7 @@ public class FilterController { String comparator = request.getComparator(); // Load the PDF - PDDocument document = pdfDocumentFactory.load(inputFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(inputFile); PDPage firstPage = document.getPage(0); PDRectangle actualPageSize = firstPage.getMediaBox(); @@ -193,7 +193,7 @@ public class FilterController { String comparator = request.getComparator(); // Load the PDF - PDDocument document = pdfDocumentFactory.load(inputFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(inputFile); // Get the rotation of the first page PDPage firstPage = document.getPage(0); 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 0bbe7e6be..d3d2e91cc 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 @@ -52,7 +52,7 @@ public class AutoRenameController { MultipartFile file = request.getFileInput(); Boolean useFirstTextAsFallback = request.isUseFirstTextAsFallback(); - PDDocument document = pdfDocumentFactory.load(file.getBytes()); + PDDocument document = pdfDocumentFactory.load(file); PDFTextStripper reader = new PDFTextStripper() { List lineInfos = 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 7fee8e2ab..0195382ea 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 @@ -84,7 +84,7 @@ public class BlankPageController { int threshold = request.getThreshold(); float whitePercent = request.getWhitePercent(); - try (PDDocument document = pdfDocumentFactory.load(inputFile.getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(inputFile)) { PDPageTree pages = document.getDocumentCatalog().getPages(); PDFTextStripper textStripper = new PDFTextStripper(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/DecompressPdfController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/DecompressPdfController.java index 626f3568c..45767fc82 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/DecompressPdfController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/DecompressPdfController.java @@ -50,7 +50,7 @@ public class DecompressPdfController { MultipartFile file = request.getFileInput(); - try (PDDocument document = pdfDocumentFactory.load(file.getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(file)) { // Process all objects in document processAllObjects(document); 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 3769bc235..7c4d9137f 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 @@ -95,8 +95,7 @@ public class ExtractImageScansController { // Check if input file is a PDF if ("pdf".equalsIgnoreCase(extension)) { // Load PDF document - try (PDDocument document = - pdfDocumentFactory.load(form.getFileInput().getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(form.getFileInput())) { PDFRenderer pdfRenderer = new PDFRenderer(document); pdfRenderer.setSubsamplingAllowed(true); int pageCount = document.getNumberOfPages(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java index 4c5c60e34..3010c1be6 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/ExtractImagesController.java @@ -67,7 +67,7 @@ public class ExtractImagesController { MultipartFile file = request.getFileInput(); String format = request.getFormat(); boolean allowDuplicates = request.isAllowDuplicates(); - PDDocument document = pdfDocumentFactory.load(file.getBytes()); + PDDocument document = pdfDocumentFactory.load(file); // Determine if multithreading should be used based on PDF size or number of pages boolean useMultithreading = shouldUseMultithreading(file, document); 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 index 9b9126e17..39991a1f6 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/FlattenController.java @@ -50,7 +50,7 @@ public class FlattenController { public ResponseEntity flatten(@ModelAttribute FlattenRequest request) throws Exception { MultipartFile file = request.getFileInput(); - PDDocument document = pdfDocumentFactory.load(file.getBytes()); + PDDocument document = pdfDocumentFactory.load(file); Boolean flattenOnlyForms = request.getFlattenOnlyForms(); if (Boolean.TRUE.equals(flattenOnlyForms)) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/MetadataController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/MetadataController.java index 69553e423..66c1c8b66 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/MetadataController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/MetadataController.java @@ -84,7 +84,7 @@ public class MetadataController { allRequestParams = new java.util.HashMap(); } // Load the PDF file into a PDDocument - PDDocument document = pdfDocumentFactory.load(pdfFile.getBytes()); + PDDocument document = pdfDocumentFactory.load(pdfFile); // Get the document information from the PDF PDDocumentInformation info = document.getDocumentInformation(); diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java index c3bbb721a..17d1bbaa9 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/PageNumbersController.java @@ -55,8 +55,7 @@ public class PageNumbersController { String pagesToNumber = request.getPagesToNumber(); String customText = request.getCustomText(); int pageNumber = startingNumber; - byte[] fileBytes = file.getBytes(); - PDDocument document = pdfDocumentFactory.load(fileBytes); + PDDocument document = pdfDocumentFactory.load(file); float font_size = request.getFontSize(); String font_type = request.getFontType(); float marginFactor; diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/ShowJavascript.java b/src/main/java/stirling/software/SPDF/controller/api/misc/ShowJavascript.java index a3b9dbdca..e38657cfe 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/ShowJavascript.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/ShowJavascript.java @@ -43,7 +43,7 @@ public class ShowJavascript { MultipartFile inputFile = request.getFileInput(); String script = ""; - try (PDDocument document = pdfDocumentFactory.load(inputFile.getBytes())) { + try (PDDocument document = pdfDocumentFactory.load(inputFile)) { if (document.getDocumentCatalog() != null && document.getDocumentCatalog().getNames() != null) { diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java b/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java index 3dc190982..cad762062 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/CertSignController.java @@ -90,7 +90,7 @@ public class CertSignController { private static void sign( CustomPDDocumentFactory pdfDocumentFactory, - byte[] input, + MultipartFile input, OutputStream output, CreateSignature instance, Boolean showSignature, @@ -179,7 +179,7 @@ public class CertSignController { ByteArrayOutputStream baos = new ByteArrayOutputStream(); sign( pdfDocumentFactory, - pdf.getBytes(), + pdf, baos, createSignature, showSignature, diff --git a/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java b/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java index a6387adb1..026fd38a0 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java +++ b/src/main/java/stirling/software/SPDF/controller/api/security/GetInfoOnPDF.java @@ -126,7 +126,7 @@ public class GetInfoOnPDF { @Operation(summary = "Summary here", description = "desc. Input:PDF Output:JSON Type:SISO") public ResponseEntity getPdfInfo(@ModelAttribute PDFFile request) throws IOException { MultipartFile inputFile = request.getFileInput(); - try (PDDocument pdfBoxDoc = pdfDocumentFactory.load(inputFile.getBytes()); ) { + try (PDDocument pdfBoxDoc = pdfDocumentFactory.load(inputFile); ) { ObjectMapper objectMapper = new ObjectMapper(); ObjectNode jsonOutput = objectMapper.createObjectNode(); 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 3da8b7ce9..0148b7a72 100644 --- a/src/main/java/stirling/software/SPDF/model/api/PDFWithPageNums.java +++ b/src/main/java/stirling/software/SPDF/model/api/PDFWithPageNums.java @@ -1,9 +1,7 @@ package stirling.software.SPDF.model.api; -import java.io.IOException; import java.util.List; -import org.apache.pdfbox.Loader; import org.apache.pdfbox.pdmodel.PDDocument; import io.swagger.v3.oas.annotations.Hidden; @@ -32,18 +30,6 @@ public class PDFWithPageNums extends PDFFile { requiredMode = RequiredMode.NOT_REQUIRED) private String pageNumbers; - @Hidden - public List getPageNumbersList(boolean zeroCount) { - int pageCount = 0; - try { - pageCount = Loader.loadPDF(getFileInput().getBytes()).getNumberOfPages(); - } catch (IOException e) { - // TODO Auto-generated catch block - log.error("exception", e); - } - return GeneralUtils.parsePageList(pageNumbers, pageCount, zeroCount); - } - @Hidden public List getPageNumbersList(PDDocument doc, boolean oneBased) { int pageCount = 0; diff --git a/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java b/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java index e9bc3b1a4..6963b522c 100644 --- a/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java +++ b/src/main/java/stirling/software/SPDF/service/CustomPDDocumentFactory.java @@ -10,9 +10,9 @@ import java.nio.file.StandardCopyOption; import java.util.concurrent.atomic.AtomicLong; import org.apache.pdfbox.Loader; +import org.apache.pdfbox.examples.util.DeletingRandomAccessFile; import org.apache.pdfbox.io.IOUtils; import org.apache.pdfbox.io.MemoryUsageSetting; -import org.apache.pdfbox.io.RandomAccessReadBufferedFile; import org.apache.pdfbox.io.RandomAccessStreamCache.StreamCacheCreateFunction; import org.apache.pdfbox.io.ScratchFile; import org.apache.pdfbox.pdmodel.PDDocument; @@ -102,16 +102,29 @@ public class CustomPDDocumentFactory { // Since we don't know the size upfront, buffer to a temp file Path tempFile = createTempFile("pdf-stream-"); - try { - Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING); - return loadAdaptively(tempFile.toFile(), Files.size(tempFile)); - } catch (IOException e) { - cleanupFile(tempFile); - throw e; - } + + Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING); + return loadAdaptively(tempFile.toFile(), Files.size(tempFile)); } - private PDDocument loadAdaptively(Object source, long contentSize) throws IOException { + /** Load with password from InputStream */ + public PDDocument load(InputStream input, String password) throws IOException { + if (input == null) { + throw new IllegalArgumentException("InputStream cannot be null"); + } + + // Since we don't know the size upfront, buffer to a temp file + Path tempFile = createTempFile("pdf-stream-"); + + Files.copy(input, tempFile, StandardCopyOption.REPLACE_EXISTING); + return loadAdaptivelyWithPassword(tempFile.toFile(), Files.size(tempFile), password); + } + + /** + * Determine the appropriate caching strategy based on file size and available memory. This + * common method is used by both password and non-password loading paths. + */ + private StreamCacheCreateFunction getStreamCacheFunction(long contentSize) { long maxMemory = Runtime.getRuntime().maxMemory(); long freeMemory = Runtime.getRuntime().freeMemory(); long totalMemory = Runtime.getRuntime().totalMemory(); @@ -129,32 +142,38 @@ public class CustomPDDocumentFactory { usedMemory / (1024 * 1024), maxMemory / (1024 * 1024)); - // Determine caching strategy based on both file size and available memory - StreamCacheCreateFunction cacheFunction; - // If free memory is critically low, always use file-based caching - // In loadAdaptively method, replace current caching strategy decision with: if (freeMemoryPercent < MIN_FREE_MEMORY_PERCENTAGE || actualFreeMemory < MIN_FREE_MEMORY_BYTES) { log.info( "Low memory detected ({}%), forcing file-based cache", String.format("%.2f", freeMemoryPercent)); - cacheFunction = createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly()); + return createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly()); } else if (contentSize < SMALL_FILE_THRESHOLD) { log.info("Using memory-only cache for small document ({}KB)", contentSize / 1024); - cacheFunction = IOUtils.createMemoryOnlyStreamCache(); + return IOUtils.createMemoryOnlyStreamCache(); } else if (contentSize < LARGE_FILE_THRESHOLD) { // For medium files (10-50MB), use a mixed approach log.info( "Using mixed memory/file cache for medium document ({}MB)", contentSize / (1024 * 1024)); - cacheFunction = - createScratchFileCacheFunction(MemoryUsageSetting.setupMixed(LARGE_FILE_USAGE)); + return createScratchFileCacheFunction(MemoryUsageSetting.setupMixed(LARGE_FILE_USAGE)); } else { log.info("Using file-based cache for large document"); - cacheFunction = createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly()); + return createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly()); } + } + /** Update the existing loadAdaptively method to use the common function */ + private PDDocument loadAdaptively(Object source, long contentSize) throws IOException { + // Get the appropriate caching strategy + StreamCacheCreateFunction cacheFunction = getStreamCacheFunction(contentSize); + + //If small handle as bytes and remove original file + if (contentSize <= SMALL_FILE_THRESHOLD && source instanceof File file) { + source = Files.readAllBytes(file.toPath()); + file.delete(); + } PDDocument document; if (source instanceof File file) { document = loadFromFile(file, contentSize, cacheFunction); @@ -168,6 +187,50 @@ public class CustomPDDocumentFactory { return document; } + /** Load a PDF with password protection using adaptive loading strategies */ + private PDDocument loadAdaptivelyWithPassword(Object source, long contentSize, String password) + throws IOException { + // Get the appropriate caching strategy + StreamCacheCreateFunction cacheFunction = getStreamCacheFunction(contentSize); + //If small handle as bytes and remove original file + if (contentSize <= SMALL_FILE_THRESHOLD && source instanceof File file) { + source = Files.readAllBytes(file.toPath()); + file.delete(); + } + PDDocument document; + if (source instanceof File file) { + document = loadFromFileWithPassword(file, contentSize, cacheFunction, password); + } else if (source instanceof byte[] bytes) { + document = loadFromBytesWithPassword(bytes, contentSize, cacheFunction, password); + } else { + throw new IllegalArgumentException("Unsupported source type: " + source.getClass()); + } + + postProcessDocument(document); + return document; + } + + /** Load a file with password */ + private PDDocument loadFromFileWithPassword( + File file, long size, StreamCacheCreateFunction cache, String password) + throws IOException { + return Loader.loadPDF(new DeletingRandomAccessFile(file), password, null, null, cache); + } + + /** Load bytes with password */ + private PDDocument loadFromBytesWithPassword( + byte[] bytes, long size, StreamCacheCreateFunction cache, String password) + throws IOException { + if (size >= SMALL_FILE_THRESHOLD) { + log.info("Writing large byte array to temp file for password-protected PDF"); + Path tempFile = createTempFile("pdf-bytes-"); + + Files.write(tempFile, bytes); + return Loader.loadPDF(tempFile.toFile(), password, null, null, cache); + } + return Loader.loadPDF(bytes, password, null, null, cache); + } + private StreamCacheCreateFunction createScratchFileCacheFunction(MemoryUsageSetting settings) { return () -> { try { @@ -185,11 +248,7 @@ public class CustomPDDocumentFactory { private PDDocument loadFromFile(File file, long size, StreamCacheCreateFunction cache) throws IOException { - if (size >= EXTREMELY_LARGE_THRESHOLD) { - log.info("Loading extremely large file via buffered access"); - return Loader.loadPDF(new RandomAccessReadBufferedFile(file), "", null, null, cache); - } - return Loader.loadPDF(file, "", null, null, cache); + return Loader.loadPDF(new DeletingRandomAccessFile(file), "", null, null, cache); } private PDDocument loadFromBytes(byte[] bytes, long size, StreamCacheCreateFunction cache) @@ -197,12 +256,9 @@ public class CustomPDDocumentFactory { if (size >= SMALL_FILE_THRESHOLD) { log.info("Writing large byte array to temp file"); Path tempFile = createTempFile("pdf-bytes-"); - try { - Files.write(tempFile, bytes); - return Loader.loadPDF(tempFile.toFile(), "", null, null, cache); - } finally { - cleanupFile(tempFile); - } + + Files.write(tempFile, bytes); + return loadFromFile(tempFile.toFile(), size, cache); } return Loader.loadPDF(bytes, "", null, null, cache); } @@ -225,12 +281,9 @@ public class CustomPDDocumentFactory { } } else { Path tempFile = createTempFile("pdf-save-"); - try { - document.save(tempFile.toFile()); - return Files.readAllBytes(tempFile); - } finally { - cleanupFile(tempFile); - } + + document.save(tempFile.toFile()); + return Files.readAllBytes(tempFile); } } @@ -258,17 +311,6 @@ public class CustomPDDocumentFactory { return Files.createTempDirectory(prefix + tempCounter.incrementAndGet() + "-"); } - /** Clean up a temporary file */ - private void cleanupFile(Path file) { - try { - if (Files.deleteIfExists(file)) { - log.info("Deleted temp file: {}", file); - } - } catch (IOException e) { - log.info("Error deleting temp file {}", file, e); - } - } - /** Create new document bytes based on an existing document */ public byte[] createNewBytesBasedOnOldDocument(byte[] oldDocument) throws IOException { try (PDDocument document = load(oldDocument)) { @@ -339,20 +381,11 @@ public class CustomPDDocumentFactory { /** Load from a MultipartFile */ public PDDocument load(MultipartFile pdfFile) throws IOException { - return load(pdfFile.getBytes()); + return load(pdfFile.getInputStream()); } /** Load with password from MultipartFile */ public PDDocument load(MultipartFile fileInput, String password) throws IOException { - return load(fileInput.getBytes(), password); - } - - /** Load with password from byte array */ - private PDDocument load(byte[] bytes, String password) throws IOException { - // Since we don't have direct password support in the adaptive loader, - // we'll need to use PDFBox's Loader directly - PDDocument document = Loader.loadPDF(bytes, password); - pdfMetadataService.setDefaultMetadata(document); - return document; + return load(fileInput.getInputStream(), password); } } diff --git a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java index 96f0aead4..d2615935f 100644 --- a/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/GeneralUtils.java @@ -32,8 +32,15 @@ public class GeneralUtils { public static File convertMultipartFileToFile(MultipartFile multipartFile) throws IOException { File tempFile = Files.createTempFile("temp", null).toFile(); - try (FileOutputStream os = new FileOutputStream(tempFile)) { - os.write(multipartFile.getBytes()); + try (InputStream inputStream = multipartFile.getInputStream(); + FileOutputStream outputStream = new FileOutputStream(tempFile)) { + + byte[] buffer = new byte[8192]; + int bytesRead; + + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } } return tempFile; } diff --git a/testing/test.sh b/testing/test.sh index 7c8681580..480b4fb01 100644 --- a/testing/test.sh +++ b/testing/test.sh @@ -39,6 +39,136 @@ check_health() { return 0 } +# Function to capture file list from a Docker container +capture_file_list() { + local container_name=$1 + local output_file=$2 + + echo "Capturing file list from $container_name..." + # Get all files in one command, output directly from Docker to avoid path issues + # Skip proc, sys, dev, and the specified LibreOffice config directory + # Also skip PDFBox and LibreOffice temporary files + docker exec $container_name sh -c "find / -type f \ + -not -path '*/proc/*' \ + -not -path '*/sys/*' \ + -not -path '*/dev/*' \ + -not -path '/config/*' \ + -not -path '/logs/*' \ + -not -path '*/home/stirlingpdfuser/.config/libreoffice/*' \ + -not -path '*/tmp/PDFBox*' \ + -not -path '*/tmp/hsperfdata_stirlingpdfuser/*' \ + -not -path '*/tmp/lu*' \ + -not -path '*/tmp/tmp*' \ + 2>/dev/null | xargs -I{} sh -c 'stat -c \"%n %s %Y\" \"{}\" 2>/dev/null || true' | sort" > "$output_file" + + # Check if the output file has content + if [ ! -s "$output_file" ]; then + echo "WARNING: Failed to capture file list or container returned empty list" + echo "Trying alternative approach..." + + # Alternative simpler approach - just get paths as a fallback + docker exec $container_name sh -c "find / -type f \ + -not -path '*/proc/*' \ + -not -path '*/sys/*' \ + -not -path '*/dev/*' \ + -not -path '/config/*' \ + -not -path '/logs/*' \ + -not -path '*/home/stirlingpdfuser/.config/libreoffice/*' \ + -not -path '*/tmp/PDFBox*' \ + -not -path '*/tmp/hsperfdata_stirlingpdfuser/*' \ + -not -path '*/tmp/lu*' \ + -not -path '*/tmp/tmp*' \ + 2>/dev/null | sort" > "$output_file" + + if [ ! -s "$output_file" ]; then + echo "ERROR: All attempts to capture file list failed" + # Create a dummy entry to prevent diff errors + echo "NO_FILES_FOUND 0 0" > "$output_file" + fi + fi + + echo "File list captured to $output_file" +} + +# Function to compare before and after file lists +compare_file_lists() { + local before_file=$1 + local after_file=$2 + local diff_file=$3 + local container_name=$4 # Added container_name parameter + + echo "Comparing file lists..." + + # Check if files exist and have content + if [ ! -s "$before_file" ] || [ ! -s "$after_file" ]; then + echo "WARNING: One or both file lists are empty." + + if [ ! -s "$before_file" ]; then + echo "Before file is empty: $before_file" + fi + + if [ ! -s "$after_file" ]; then + echo "After file is empty: $after_file" + fi + + # Create empty diff file + > "$diff_file" + + # Check if we at least have the after file to look for temp files + if [ -s "$after_file" ]; then + echo "Checking for temp files in the after snapshot..." + grep -i "tmp\|temp" "$after_file" > "${diff_file}.tmp" + if [ -s "${diff_file}.tmp" ]; then + echo "WARNING: Temporary files found:" + cat "${diff_file}.tmp" + echo "Printing docker logs due to temporary file detection:" + docker logs "$container_name" # Print logs when temp files are found + return 1 + else + echo "No temporary files found in the after snapshot." + fi + fi + + return 0 + fi + + # Both files exist and have content, proceed with diff + diff "$before_file" "$after_file" > "$diff_file" + + if [ -s "$diff_file" ]; then + echo "Detected changes in files:" + cat "$diff_file" + + # Extract only added files (lines starting with ">") + grep "^>" "$diff_file" > "${diff_file}.added" || true + if [ -s "${diff_file}.added" ]; then + echo "New files created during test:" + cat "${diff_file}.added" | sed 's/^> //' + + # Check for tmp files + grep -i "tmp\|temp" "${diff_file}.added" > "${diff_file}.tmp" || true + if [ -s "${diff_file}.tmp" ]; then + echo "WARNING: Temporary files detected:" + cat "${diff_file}.tmp" + echo "Printing docker logs due to temporary file detection:" + docker logs "$container_name" # Print logs when temp files are found + return 1 + fi + fi + + # Extract only removed files (lines starting with "<") + grep "^<" "$diff_file" > "${diff_file}.removed" || true + if [ -s "${diff_file}.removed" ]; then + echo "Files removed during test:" + cat "${diff_file}.removed" | sed 's/^< //' + fi + else + echo "No file changes detected during test." + fi + + return 0 +} + # Function to test a Docker Compose configuration test_compose() { local compose_file=$1 @@ -91,7 +221,7 @@ main() { # Building Docker images # docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest -f ./Dockerfile . - docker build --no-cache --pull --build-arg VERSION_TAG=alpha -t stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite . + docker build --build-arg VERSION_TAG=alpha -t docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest-ultra-lite -f ./Dockerfile.ultra-lite . # Test each configuration run_tests "Stirling-PDF-Ultra-Lite" "./exampleYmlFiles/docker-compose-latest-ultra-lite.yml" @@ -147,16 +277,55 @@ main() { run_tests "Stirling-PDF-Security-Fat-with-login" "./exampleYmlFiles/test_cicd.yml" if [ $? -eq 0 ]; then + # Create directory for file snapshots if it doesn't exist + SNAPSHOT_DIR="$PROJECT_ROOT/testing/file_snapshots" + mkdir -p "$SNAPSHOT_DIR" + + # Capture file list before running behave tests + BEFORE_FILE="$SNAPSHOT_DIR/files_before_behave.txt" + AFTER_FILE="$SNAPSHOT_DIR/files_after_behave.txt" + DIFF_FILE="$SNAPSHOT_DIR/files_diff.txt" + + # Define container name variable for consistency + CONTAINER_NAME="Stirling-PDF-Security-Fat-with-login" + + capture_file_list "$CONTAINER_NAME" "$BEFORE_FILE" + cd "testing/cucumber" if python -m behave; then + # Wait 10 seconds before capturing the file list after tests + echo "Waiting 5 seconds for any file operations to complete..." + sleep 5 + + # Capture file list after running behave tests + cd "$PROJECT_ROOT" + capture_file_list "$CONTAINER_NAME" "$AFTER_FILE" + + # Compare file lists + if compare_file_lists "$BEFORE_FILE" "$AFTER_FILE" "$DIFF_FILE" "$CONTAINER_NAME"; then + echo "No unexpected temporary files found." + passed_tests+=("Stirling-PDF-Regression") + else + echo "WARNING: Unexpected temporary files detected after behave tests!" + failed_tests+=("Stirling-PDF-Regression-Temp-Files") + fi + passed_tests+=("Stirling-PDF-Regression") else failed_tests+=("Stirling-PDF-Regression") echo "Printing docker logs of failed regression" - docker logs "Stirling-PDF-Security-Fat-with-login" + docker logs "$CONTAINER_NAME" echo "Printed docker logs of failed regression" + + # Still capture file list after failure for analysis + # Wait 10 seconds before capturing the file list + echo "Waiting 5 seconds before capturing file list..." + sleep 10 + + cd "$PROJECT_ROOT" + capture_file_list "$CONTAINER_NAME" "$AFTER_FILE" + compare_file_lists "$BEFORE_FILE" "$AFTER_FILE" "$DIFF_FILE" "$CONTAINER_NAME" fi - cd "$PROJECT_ROOT" fi docker-compose -f "./exampleYmlFiles/test_cicd.yml" down diff --git a/testing/test_webpages.sh b/testing/test_webpages.sh index e1207c2eb..2091995af 100644 --- a/testing/test_webpages.sh +++ b/testing/test_webpages.sh @@ -2,122 +2,173 @@ # Function to check a single webpage check_webpage() { - local url=$(echo "$1" | tr -d '\r') # Remove carriage returns - local base_url=$(echo "$2" | tr -d '\r') - local full_url="${base_url}${url}" - local timeout=10 - echo -n "Testing $full_url ... " - - # Use curl to fetch the page with timeout - response=$(curl -s -w "\n%{http_code}" --max-time $timeout "$full_url") - if [ $? -ne 0 ]; then - echo "FAILED - Connection error or timeout $full_url " - return 1 - fi + local url=$(echo "$1" | tr -d '\r') # Remove carriage returns + local base_url=$(echo "$2" | tr -d '\r') + local full_url="${base_url}${url}" + local timeout=10 + local result_file="$3" - # Split response into body and status code - HTTP_STATUS=$(echo "$response" | tail -n1) - BODY=$(echo "$response" | sed '$d') + # Use curl to fetch the page with timeout + response=$(curl -s -w "\n%{http_code}" --max-time $timeout "$full_url") + if [ $? -ne 0 ]; then + echo "FAILED - Connection error or timeout $full_url" >> "$result_file" + return 1 + fi - # Check HTTP status - if [ "$HTTP_STATUS" != "200" ]; then - echo "FAILED - HTTP Status: $HTTP_STATUS" - return 1 - fi + # Split response into body and status code + HTTP_STATUS=$(echo "$response" | tail -n1) + BODY=$(echo "$response" | sed '$d') - # Check if response contains HTML - if ! printf '%s' "$BODY" | grep -q "\|> "$result_file" + return 1 + fi - echo "OK" - return 0 + # Check if response contains HTML + if ! printf '%s' "$BODY" | grep -q "\|> "$result_file" + return 1 + fi + + echo "OK - $full_url" >> "$result_file" + return 0 } -# Main function to test all URLs from the list +# Function to test a URL and update counters +test_url() { + local url="$1" + local base_url="$2" + local tmp_dir="$3" + local url_index="$4" + local result_file="${tmp_dir}/result_${url_index}.txt" + + if ! check_webpage "$url" "$base_url" "$result_file"; then + echo "1" > "${tmp_dir}/failed_${url_index}" + else + echo "0" > "${tmp_dir}/failed_${url_index}" + fi +} + +# Main function to test all URLs from the list in parallel test_all_urls() { - local url_file=$1 - local base_url=${2:-"http://localhost:8080"} - local failed_count=0 - local total_count=0 - local start_time=$(date +%s) + local url_file="$1" + local base_url="${2:-"http://localhost:8080"}" + local max_parallel="${3:-10}" # Default to 10 parallel processes + local failed_count=0 + local total_count=0 + local start_time=$(date +%s) + local tmp_dir=$(mktemp -d) + local active_jobs=0 + local url_index=0 - echo "Starting webpage tests..." - echo "Base URL: $base_url" - echo "Number of lines: $(wc -l < "$url_file")" - echo "----------------------------------------" - - while IFS= read -r url || [ -n "$url" ]; do - # Skip empty lines and comments - [[ -z "$url" || "$url" =~ ^#.*$ ]] && continue - - ((total_count++)) - if ! check_webpage "$url" "$base_url"; then - ((failed_count++)) - fi - done < "$url_file" + echo "Starting webpage tests..." + echo "Base URL: $base_url" + echo "Number of lines: $(wc -l < "$url_file")" + echo "Max parallel jobs: $max_parallel" + echo "----------------------------------------" - local end_time=$(date +%s) - local duration=$((end_time - start_time)) + # Process each URL + while IFS= read -r url || [ -n "$url" ]; do + # Skip empty lines and comments + [[ -z "$url" || "$url" =~ ^#.*$ ]] && continue + + ((total_count++)) + ((url_index++)) - echo "----------------------------------------" - echo "Test Summary:" - echo "Total tests: $total_count" - echo "Failed tests: $failed_count" - echo "Passed tests: $((total_count - failed_count))" - echo "Duration: ${duration} seconds" + # Run the check in background + test_url "$url" "$base_url" "$tmp_dir" "$url_index" & + + # Track the job + ((active_jobs++)) + + # If we've reached max_parallel, wait for a job to finish + if [ $active_jobs -ge $max_parallel ]; then + wait -n # Wait for any child process to exit + ((active_jobs--)) + fi + done < "$url_file" - return $failed_count + # Wait for remaining jobs to finish + wait + + # Print results in order and count failures + for i in $(seq 1 $url_index); do + if [ -f "${tmp_dir}/result_${i}.txt" ]; then + cat "${tmp_dir}/result_${i}.txt" + fi + + if [ -f "${tmp_dir}/failed_${i}" ]; then + failed_count=$((failed_count + $(cat "${tmp_dir}/failed_${i}"))) + fi + done + + # Clean up + rm -rf "$tmp_dir" + + local end_time=$(date +%s) + local duration=$((end_time - start_time)) + + echo "----------------------------------------" + echo "Test Summary:" + echo "Total tests: $total_count" + echo "Failed tests: $failed_count" + echo "Passed tests: $((total_count - failed_count))" + echo "Duration: ${duration} seconds" + + return $failed_count } # Print usage information usage() { - echo "Usage: $0 [-f url_file] [-b base_url]" - echo "Options:" - echo " -f url_file Path to file containing URLs to test (required)" - echo " -b base_url Base URL to prepend to test URLs (default: http://localhost:8080)" - exit 1 + echo "Usage: $0 [-f url_file] [-b base_url] [-p max_parallel]" + echo "Options:" + echo " -f url_file Path to file containing URLs to test (required)" + echo " -b base_url Base URL to prepend to test URLs (default: http://localhost:8080)" + echo " -p max_parallel Maximum number of parallel requests (default: 10)" + exit 1 } # Main execution main() { - local url_file="" - local base_url="http://localhost:8080" + local url_file="" + local base_url="http://localhost:8080" + local max_parallel=10 - # Parse command line options - while getopts ":f:b:h" opt; do - case $opt in - f) url_file="$OPTARG" ;; - b) base_url="$OPTARG" ;; - h) usage ;; - \?) echo "Invalid option -$OPTARG" >&2; usage ;; - esac - done + # Parse command line options + while getopts ":f:b:p:h" opt; do + case $opt in + f) url_file="$OPTARG" ;; + b) base_url="$OPTARG" ;; + p) max_parallel="$OPTARG" ;; + h) usage ;; + \?) echo "Invalid option -$OPTARG" >&2; usage ;; + esac + done - # Check if URL file is provided - if [ -z "$url_file" ]; then - echo "Error: URL file is required" - usage - fi + # Check if URL file is provided + if [ -z "$url_file" ]; then + echo "Error: URL file is required" + usage + fi - # Check if URL file exists - if [ ! -f "$url_file" ]; then - echo "Error: URL list file not found: $url_file" - exit 1 - fi - - # Run tests using the URL list - if test_all_urls "$url_file" "$base_url"; then - echo "All webpage tests passed!" - exit 0 - else - echo "Some webpage tests failed!" - exit 1 - fi + # Check if URL file exists + if [ ! -f "$url_file" ]; then + echo "Error: URL list file not found: $url_file" + exit 1 + fi + + # Run tests using the URL list + if test_all_urls "$url_file" "$base_url" "$max_parallel"; then + echo "All webpage tests passed!" + exit 0 + else + echo "Some webpage tests failed!" + exit 1 + fi } # Run main if script is executed directly if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - main "$@" + main "$@" fi \ No newline at end of file From 6b76f0d31ae04439e50b9c3bd2375779a637acb0 Mon Sep 17 00:00:00 2001 From: "stirlingbot[bot]" <195170888+stirlingbot[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 20:27:00 +0000 Subject: [PATCH 04/10] :globe_with_meridians: Sync Translations + Update README Progress Table (#3149) ### Description of Changes This Pull Request was automatically generated to synchronize updates to translation files and documentation. Below are the details of the changes made: #### **1. Synchronization of Translation Files** - Updated translation files (`messages_*.properties`) to reflect changes in the reference file `messages_en_GB.properties`. - Ensured consistency and synchronization across all supported language files. - Highlighted any missing or incomplete translations. #### **2. Update README.md** - Generated the translation progress table in `README.md`. - Added a summary of the current translation status for all supported languages. - Included up-to-date statistics on translation coverage. #### **Why these changes are necessary** - Keeps translation files aligned with the latest reference updates. - Ensures the documentation reflects the current translation progress. --- Auto-generated by [create-pull-request][1]. [1]: https://github.com/peter-evans/create-pull-request Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com> --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ecec65dc1..d6a82cd2e 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Stirling-PDF currently supports 39 languages! | Azerbaijani (Azərbaycan Dili) (az_AZ) | ![87%](https://geps.dev/progress/87) | | Basque (Euskara) (eu_ES) | ![51%](https://geps.dev/progress/51) | | Bulgarian (Български) (bg_BG) | ![98%](https://geps.dev/progress/98) | -| Catalan (Català) (ca_CA) | ![80%](https://geps.dev/progress/80) | +| Catalan (Català) (ca_CA) | ![79%](https://geps.dev/progress/79) | | Croatian (Hrvatski) (hr_HR) | ![85%](https://geps.dev/progress/85) | | Czech (Česky) (cs_CZ) | ![96%](https://geps.dev/progress/96) | | Danish (Dansk) (da_DK) | ![84%](https://geps.dev/progress/84) | @@ -128,7 +128,7 @@ Stirling-PDF currently supports 39 languages! | English (English) (en_GB) | ![100%](https://geps.dev/progress/100) | | English (US) (en_US) | ![100%](https://geps.dev/progress/100) | | French (Français) (fr_FR) | ![96%](https://geps.dev/progress/96) | -| German (Deutsch) (de_DE) | ![98%](https://geps.dev/progress/98) | +| German (Deutsch) (de_DE) | ![99%](https://geps.dev/progress/99) | | Greek (Ελληνικά) (el_GR) | ![96%](https://geps.dev/progress/96) | | Hindi (हिंदी) (hi_IN) | ![97%](https://geps.dev/progress/97) | | Hungarian (Magyar) (hu_HU) | ![94%](https://geps.dev/progress/94) | @@ -148,11 +148,11 @@ Stirling-PDF currently supports 39 languages! | Simplified Chinese (简体中文) (zh_CN) | ![98%](https://geps.dev/progress/98) | | Slovakian (Slovensky) (sk_SK) | ![73%](https://geps.dev/progress/73) | | Slovenian (Slovenščina) (sl_SI) | ![95%](https://geps.dev/progress/95) | -| Spanish (Español) (es_ES) | ![97%](https://geps.dev/progress/97) | +| Spanish (Español) (es_ES) | ![98%](https://geps.dev/progress/98) | | Swedish (Svenska) (sv_SE) | ![92%](https://geps.dev/progress/92) | | Thai (ไทย) (th_TH) | ![84%](https://geps.dev/progress/84) | | Tibetan (བོད་ཡིག་) (zh_BO) | ![93%](https://geps.dev/progress/93) | -| Traditional Chinese (繁體中文) (zh_TW) | ![98%](https://geps.dev/progress/98) | +| Traditional Chinese (繁體中文) (zh_TW) | ![99%](https://geps.dev/progress/99) | | Turkish (Türkçe) (tr_TR) | ![81%](https://geps.dev/progress/81) | | Ukrainian (Українська) (uk_UA) | ![71%](https://geps.dev/progress/71) | | Vietnamese (Tiếng Việt) (vi_VN) | ![78%](https://geps.dev/progress/78) | From 7329a7357c2eb5d435d4064adba368cf6f72de74 Mon Sep 17 00:00:00 2001 From: Ludy Date: Mon, 10 Mar 2025 21:27:22 +0100 Subject: [PATCH 05/10] Change Labeler configuration: Replace Dockerfile.* with Dockerfile.fat and Dockerfile.ultra-lite (#3156) # Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- .github/labeler-config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/labeler-config.yml b/.github/labeler-config.yml index d72607386..73f92aaa3 100644 --- a/.github/labeler-config.yml +++ b/.github/labeler-config.yml @@ -54,8 +54,8 @@ Docker: - any-glob-to-any-file: '.github/workflows/build.yml' - any-glob-to-any-file: '.github/workflows/push-docker.yml' - any-glob-to-any-file: 'Dockerfile' - - any-glob-to-any-file: 'Dockerfile.*' - - all-globs-to-all-files: '!Dockerfile.dev' + - any-glob-to-any-file: 'Dockerfile.fat' + - any-glob-to-any-file: 'Dockerfile.ultra-lite' - any-glob-to-any-file: 'exampleYmlFiles/*.yml' - any-glob-to-any-file: 'scripts/download-security-jar.sh' - any-glob-to-any-file: 'scripts/init.sh' From 45b3a14da497d3760ba7772ea7198f282936ed7c Mon Sep 17 00:00:00 2001 From: Valerio Cislaghi Date: Mon, 10 Mar 2025 21:35:04 +0100 Subject: [PATCH 06/10] Add unit test for FileInfo.getFormattedFileSize (#3132) # Description of Changes Add Unit Test for FileInfo.getFormattedFileSize Closes #3089 --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [x] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [x] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [x] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [x] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- .../software/SPDF/utils/FileInfo.java | 7 ++-- .../software/SPDF/utils/FileInfoTest.java | 32 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 src/test/java/stirling/software/SPDF/utils/FileInfoTest.java diff --git a/src/main/java/stirling/software/SPDF/utils/FileInfo.java b/src/main/java/stirling/software/SPDF/utils/FileInfo.java index 64e21f8c1..c817b8768 100644 --- a/src/main/java/stirling/software/SPDF/utils/FileInfo.java +++ b/src/main/java/stirling/software/SPDF/utils/FileInfo.java @@ -4,6 +4,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Locale; import lombok.AllArgsConstructor; import lombok.Data; @@ -27,11 +28,11 @@ public class FileInfo { // Formats the file size into a human-readable string. public String getFormattedFileSize() { if (fileSize >= 1024 * 1024 * 1024) { - return String.format("%.2f GB", fileSize / (1024.0 * 1024 * 1024)); + return String.format(Locale.US, "%.2f GB", fileSize / (1024.0 * 1024 * 1024)); } else if (fileSize >= 1024 * 1024) { - return String.format("%.2f MB", fileSize / (1024.0 * 1024)); + return String.format(Locale.US, "%.2f MB", fileSize / (1024.0 * 1024)); } else if (fileSize >= 1024) { - return String.format("%.2f KB", fileSize / 1024.0); + return String.format(Locale.US, "%.2f KB", fileSize / 1024.0); } else { return String.format("%d Bytes", fileSize); } diff --git a/src/test/java/stirling/software/SPDF/utils/FileInfoTest.java b/src/test/java/stirling/software/SPDF/utils/FileInfoTest.java new file mode 100644 index 000000000..e83a062f8 --- /dev/null +++ b/src/test/java/stirling/software/SPDF/utils/FileInfoTest.java @@ -0,0 +1,32 @@ +package stirling.software.SPDF.utils; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FileInfoTest { + + @ParameterizedTest(name = "{index}: fileSize={0}") + @CsvSource({ + "0, '0 Bytes'", + "1023, '1023 Bytes'", + "1024, '1.00 KB'", + "1048575, '1024.00 KB'", // Do we really want this as result? + "1048576, '1.00 MB'", + "1073741823, '1024.00 MB'", // Do we really want this as result? + "1073741824, '1.00 GB'" + }) + void testGetFormattedFileSize(long fileSize, String expectedFormattedSize) { + FileInfo fileInfo = new FileInfo( + "example.txt", + "/path/to/example.txt", + LocalDateTime.now(), + fileSize, + LocalDateTime.now().minusDays(1)); + + assertEquals(expectedFormattedSize, fileInfo.getFormattedFileSize()); + } +} From 6b35049894f2873ba85a5155eb3093cc37d70b76 Mon Sep 17 00:00:00 2001 From: leo-jmateo <128976497+leo-jmateo@users.noreply.github.com> Date: Mon, 10 Mar 2025 21:35:28 +0100 Subject: [PATCH 07/10] Update Catalan translations (#3134) # Update Catalan translations - Translated and revised various entries for PDF validation, organization, and conversion features. - Improved consistency in technical terminology and language usage. --- ## Checklist ### General - [x] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [x] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --------- Signed-off-by: GitHub Action Co-authored-by: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Co-authored-by: github-actions[bot] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/main/resources/messages_ca_CA.properties | 394 +++++++++---------- 1 file changed, 197 insertions(+), 197 deletions(-) diff --git a/src/main/resources/messages_ca_CA.properties b/src/main/resources/messages_ca_CA.properties index a356a7856..a7da336de 100644 --- a/src/main/resources/messages_ca_CA.properties +++ b/src/main/resources/messages_ca_CA.properties @@ -138,7 +138,7 @@ analytics.settings=Pots canviar la configuració de les analítiques al fitxer c # NAVBAR # ############# navbar.favorite=Favorits -navbar.recent=New and recently updated +navbar.recent=Nou i recentment actualitzat navbar.darkmode=Mode Fosc navbar.language=Idiomes navbar.settings=Opcions @@ -264,221 +264,221 @@ home.searchBar=Cerca funcions... home.viewPdf.title=View/Edit PDF home.viewPdf.desc=Visualitza, anota, afegeix text o imatges -viewPdf.tags=view,read,annotate,text,image +viewPdf.tags=veure,llegir,anotar,text,imatge -home.setFavorites=Set Favourites -home.hideFavorites=Hide Favourites -home.showFavorites=Show Favourites -home.legacyHomepage=Old homepage -home.newHomePage=Try our new homepage! -home.alphabetical=Alphabetical -home.globalPopularity=Global Popularity -home.sortBy=Sort by: +home.setFavorites=Configura els favorits +home.hideFavorites=Amaga els favorits +home.showFavorites=Mostra els favorits +home.legacyHomepage=Pàgina d'inici antiga +home.newHomePage=Prova la nostra nova pàgina d'inici! +home.alphabetical=Alfabètic +home.globalPopularity=Popularitat global +home.sortBy=Ordena per: home.multiTool.title=Eina Multifunció de PDF home.multiTool.desc=Fusiona, Rota, Reorganitza i Esborra pàgines -multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side +multiTool.tags=Eina Multifunció,operacions múltiples,UI,clic i arrossega,costat frontal,client home.merge.title=Fusiona -home.merge.desc=Fusiona fàcilment pàgines en una. -merge.tags=merge,Page operations,Back end,server side +home.merge.desc=Fusiona fàcilment pàgines en una sola. +merge.tags=fusió,operacions de pàgina,backend,servidor home.split.title=Divideix home.split.desc=Divideix PDFs en múltiples documents -split.tags=Page operations,divide,Multi Page,cut,server side +split.tags=operacions de pàgina,divideix,Multi-Pàgina,talla,servidor home.rotate.title=Rota home.rotate.desc=Rota els PDFs. -rotate.tags=server side +rotate.tags=servidor home.imageToPdf.title=Imatge a PDF home.imageToPdf.desc=Converteix imatge (PNG, JPEG, GIF) a PDF. -imageToPdf.tags=conversion,img,jpg,picture,photo +imageToPdf.tags=conversió,img,jpg,imatge,foto home.pdfToImage.title=PDF a Imatge -home.pdfToImage.desc=Converteix PDF a imatge. (PNG, JPEG, GIF) -pdfToImage.tags=conversion,img,jpg,picture,photo +home.pdfToImage.desc=Converteix PDF a imatge (PNG, JPEG, GIF) +pdfToImage.tags=conversió,img,jpg,imatge,foto home.pdfOrganiser.title=Organitza -home.pdfOrganiser.desc=Elimina/Reorganitza pàgines en qualsevol ordre -pdfOrganiser.tags=duplex,even,odd,sort,move +home.pdfOrganiser.desc=Elimina/reorganitza pàgines en qualsevol ordre +pdfOrganiser.tags=duplex,parells,senars,ordenar,moure home.addImage.title=Afegir imatge a PDF -home.addImage.desc=Afegeix imatge en un PDF (En progrés) -addImage.tags=img,jpg,picture,photo +home.addImage.desc=Afegeix una imatge en un PDF (en progrés) +addImage.tags=img,jpg,imatge,foto home.watermark.title=Afegir Marca d'aigua -home.watermark.desc=Afegir Marca d'aigua personalitzada en un PDF -watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo +home.watermark.desc=Afegir una marca d'aigua personalitzada en un PDF +watermark.tags=text,repetició,etiqueta,propia,copyright,marca registrada,img,jpg,imatge,foto -home.permissions.title=Canvia permissos -home.permissions.desc=Canvia permisos del document PDF -permissions.tags=read,write,edit,print +home.permissions.title=Canvia permisos +home.permissions.desc=Canvia els permisos del document PDF +permissions.tags=lectura,escriptura,editar,imprimir home.removePages.title=Elimina home.removePages.desc=Elimina pàgines del document PDF. -removePages.tags=Remove pages,delete pages +removePages.tags=eliminar pàgines,suprimir pàgines home.addPassword.title=Afegir Contrasenya -home.addPassword.desc=Xifra document PDF amb contrasenya. -addPassword.tags=secure,security +home.addPassword.desc=Xifra el document PDF amb contrasenya. +addPassword.tags=segur,seguretat home.removePassword.title=Elimina Contrasenya -home.removePassword.desc=Elimia contrasenya de document PDF. -removePassword.tags=secure,Decrypt,security,unpassword,delete password +home.removePassword.desc=Elimina la contrasenya del document PDF. +removePassword.tags=segur,desencripta,seguretat,eliminar contrasenya,suprimir contrasenya home.compressPdfs.title=Comprimeix -home.compressPdfs.desc=Comprimeix PDFs per reduir la mida. -compressPdfs.tags=squish,small,tiny +home.compressPdfs.desc=Comprimeix PDFs per reduir-ne la mida. +compressPdfs.tags=estrènyer,petit,minúscul home.changeMetadata.title=Canvia Metadades -home.changeMetadata.desc=Canvia/Treu/Afegeix matadades al document PDF. -changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats +home.changeMetadata.desc=Canvia/Treu/Afegeix metadades al document PDF. +changeMetadata.tags=Títol,autor,data,creació,hora,editor,productor,estadístiques home.fileToPDF.title=Converteix arxiu a PDF home.fileToPDF.desc=Converteix qualsevol arxiu a PDF (DOCX, PNG, XLS, PPT, TXT i més) -fileToPDF.tags=transformation,format,document,picture,slide,text,conversion,office,docs,word,excel,powerpoint +fileToPDF.tags=transformació,format,document,imatge,diapositiva,text,conversió,oficina,docs,word,excel,powerpoint -home.ocr.title=Executa exploracions OCR i/o neteja escanejos -home.ocr.desc=Neteja escanejats i detecta text d'imatges dins d'un PDF i el torna a afegir com a text. -ocr.tags=recognition,text,image,scan,read,identify,detection,editable +home.ocr.title=Executa OCR i neteja escaneigs +home.ocr.desc=Neteja escanejats i detecta text d'imatges dins d'un PDF, tornant-lo a afegir com a text. +ocr.tags=reconeixement,text,imatge,escaneig,lectura,identificació,detecció,editable home.extractImages.title=Extreu Imatges -home.extractImages.desc=Extreu les Imatges del PDF i les desa a zip -extractImages.tags=picture,photo,save,archive,zip,capture,grab +home.extractImages.desc=Extreu les imatges del PDF i desa-les en un arxiu zip +extractImages.tags=imatge,foto,desa,arxiva,zip,captura,agafa home.pdfToPDFA.title=PDF a PDF/A -home.pdfToPDFA.desc=Converteix PDF a PDF/A per desar a llarg termini. -pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation +home.pdfToPDFA.desc=Converteix PDF a PDF/A per a l'emmagatzematge a llarg termini. +pdfToPDFA.tags=arxiu,llarg termini,estàndard,conversió,emmagatzematge,preservació home.PDFToWord.title=PDF a Word -home.PDFToWord.desc=Converteix PDF a formats de Word (DOC, DOCX and ODT) -PDFToWord.tags=doc,docx,odt,word,transformation,format,conversion,office,microsoft,docfile +home.PDFToWord.desc=Converteix PDF a formats de Word (DOC, DOCX i ODT) +PDFToWord.tags=doc,docx,odt,word,transformació,format,conversió,oficina,microsoft,fitxer doc home.PDFToPresentation.title=PDF a Presentació -home.PDFToPresentation.desc=Converteix PDF a formats de Presentació (PPT, PPTX i ODP) -PDFToPresentation.tags=slides,show,office,microsoft +home.PDFToPresentation.desc=Converteix PDF a formats de presentació (PPT, PPTX i ODP) +PDFToPresentation.tags=diapositives,presentació,oficina,microsoft home.PDFToText.title=PDF a Text/RTF -home.PDFToText.desc=Converteix PDF a Text o format RTF -PDFToText.tags=richformat,richtextformat,rich text format +home.PDFToText.desc=Converteix PDF a text o format RTF +PDFToText.tags=format ric,format de text ric,format de text enriquit home.PDFToHTML.title=PDF a HTML home.PDFToHTML.desc=Converteix PDF a format HTML -PDFToHTML.tags=web content,browser friendly +PDFToHTML.tags=contingut web,compatible amb navegadors home.PDFToXML.title=PDF a XML home.PDFToXML.desc=Converteix PDF a format XML -PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert +PDFToXML.tags=extracció de dades,contingut estructurat,interop,transformació,convertir home.ScannerImageSplit.title=Detecta/Divideix fotos escanejades home.ScannerImageSplit.desc=Divideix múltiples fotos dins del PDF/foto -ScannerImageSplit.tags=separate,auto-detect,scans,multi-photo,organize +ScannerImageSplit.tags=separa,detecció automàtica,escaneigs,multifoto,organitzar home.sign.title=Signa home.sign.desc=Afegeix signatura al PDF mitjançant dibuix, text o imatge -sign.tags=authorize,initials,drawn-signature,text-sign,image-signature +sign.tags=autoritza,inicis,signatura dibuixada,signatura de text,signatura amb imatge home.flatten.title=Aplanar -home.flatten.desc=Elimineu tots els elements i formularis interactius d'un PDF -flatten.tags=static,deactivate,non-interactive,streamline +home.flatten.desc=Elimina tots els elements i formularis interactius d'un PDF +flatten.tags=estàtic,desactivar,no interactiu,simplifica home.repair.title=Reparar home.repair.desc=Intenta reparar un PDF danyat o trencat -repair.tags=fix,restore,correction,recover +repair.tags=repara,restaura,correcció,recupera home.removeBlanks.title=Elimina les pàgines en blanc home.removeBlanks.desc=Detecta i elimina les pàgines en blanc d'un document -removeBlanks.tags=cleanup,streamline,non-content,organize +removeBlanks.tags=neteja,simplifica,sense contingut,organitza home.removeAnnotations.title=Elimina Anotacions home.removeAnnotations.desc=Elimina tots els comentaris/anotacions d'un PDF -removeAnnotations.tags=comments,highlight,notes,markup,remove +removeAnnotations.tags=comentaris,ressalta,notes,marcatge,elimina home.compare.title=Compara home.compare.desc=Compara i mostra les diferències entre 2 documents PDF -compare.tags=differentiate,contrast,changes,analysis +compare.tags=diferencia,contrasta,canvis,anàlisi home.certSign.title=Signa amb Certificat -home.certSign.desc=Signa PDF amb Certificat/Clau (PEM/P12) -certSign.tags=authentica,PEM,P12,official,encripta +home.certSign.desc=Signa un PDF amb Certificat/Clau (PEM/P12) +certSign.tags=autentica,PEM,P12,oficial,encripta home.removeCertSign.title=Elimina Signatura de Certificat home.removeCertSign.desc=Elimina la signatura de certificat d'un PDF -removeCertSign.tags=authenticate,PEM,P12,official,decrypt +removeCertSign.tags=autentica,PEM,P12,oficial,desencripta home.pageLayout.title=Disposició Multi-Pàgina home.pageLayout.desc=Fusiona diverses pàgines d'un document PDF en una sola pàgina -pageLayout.tags=merge,composite,single-view,organize +pageLayout.tags=fusió,composició,vista única,organitzar home.scalePages.title=Ajusta la mida/escala de la pàgina home.scalePages.desc=Canvia la mida/escala de la pàgina i/o del seu contingut. -scalePages.tags=resize,modify,dimension,adapt +scalePages.tags=redimensionar,modificar,dimensió,adaptar home.pipeline.title=Procés home.pipeline.desc=Executa múltiples accions en PDFs definint scripts de procés -pipeline.tags=automate,sequence,scripted,batch-process +pipeline.tags=automatitzar,seqüència,scriptat,procés per lots home.add-page-numbers.title=Afegir Números de Pàgina -home.add-page-numbers.desc=Afegir Números de Pàgina en una localització -add-page-numbers.tags=pagina,etiqueta,organitza,indexa +home.add-page-numbers.desc=Afegir números de pàgina en una localització +add-page-numbers.tags=pàgina,etiqueta,organitza,indexa home.auto-rename.title=Canvia Automàticament el Nom del Fitxer PDF home.auto-rename.desc=Canvia automàticament el nom d'un fitxer PDF en funció de la capçalera detectada -auto-rename.tags=auto-detect,header-based,organize,relabel +auto-rename.tags=autodetect,basat en capçalera,organitzar,reetiquetar home.adjust-contrast.title=Ajusta Colors/Contrast -home.adjust-contrast.desc=Ajusta Colors/Contrast, Saturació i Brillantor -adjust-contrast.tags=color-correction,tune,modify,enhance +home.adjust-contrast.desc=Ajusta colors/contrast, saturació i brillantor +adjust-contrast.tags=correcció de color,ajustar,modificar,millorar home.crop.title=Talla PDF -home.crop.desc=Talla PDF per reduïr la mida (manté text!) -crop.tags=trim,shrink,edit,shape +home.crop.desc=Talla PDF per reduir la mida (manté el text!) +crop.tags=retallar,reduir,editar,donar forma home.autoSplitPDF.title=Divisió Automàtica de Pàgines home.autoSplitPDF.desc=Divideix automàticament un PDF escanejat amb un codi QR de separació de pàgines escanejades -autoSplitPDF.tags=QR-based,separate,scan-segment,organize +autoSplitPDF.tags=basat en QR,separar,segmentació d'escaneig,organitzar home.sanitizePdf.title=Neteja home.sanitizePdf.desc=Elimina scripts i altres elements dels fitxers PDF -sanitizePdf.tags=clean,secure,safe,remove-threats +sanitizePdf.tags=netejar,segur,sense riscos,eliminar amenaces home.URLToPDF.title=URL/Lloc Web a PDF home.URLToPDF.desc=Converteix qualsevol URL http(s) a PDF -URLToPDF.tags=web-capture,save-page,web-to-doc,archive +URLToPDF.tags=captura web,desa pàgina,web a document,arxiu home.HTMLToPDF.title=HTML a PDF home.HTMLToPDF.desc=Converteix qualsevol fitxer HTML o arxiu comprimit a PDF -HTMLToPDF.tags=markup,web-content,transformation,convert +HTMLToPDF.tags=marcatge,contingut web,transformació,convertir home.MarkdownToPDF.title=Markdown a PDF home.MarkdownToPDF.desc=Converteix qualsevol fitxer Markdown a PDF -MarkdownToPDF.tags=markup,web-content,transformation,convert +MarkdownToPDF.tags=marcatge,contingut web,transformació,convertir home.PDFToMarkdown.title=PDF to Markdown home.PDFToMarkdown.desc=Converts any PDF to Markdown -PDFToMarkdown.tags=markup,web-content,transformation,convert,md +PDFToMarkdown.tags=marcatge,contingut web,transformació,convertir,md home.getPdfInfo.title=Obteniu Tota la Informació sobre el PDF home.getPdfInfo.desc=Recupera tota la informació possible sobre els PDFs -getPdfInfo.tags=infomation,data,stats,statistics +getPdfInfo.tags=informació,dades,estadístiques,estadístiques home.extractPage.title=Extreu pàgina(es) home.extractPage.desc=Extreu pàgines seleccionades d'un PDF -extractPage.tags=extract +extractPage.tags=extreure home.PdfToSinglePage.title=PDF a Una Sola Pàgina Gran home.PdfToSinglePage.desc=Fusiona totes les pàgines d'un PDF en una sola pàgina gran -PdfToSinglePage.tags=single page +PdfToSinglePage.tags=pàgina única home.showJS.title=Mostra Javascript @@ -487,47 +487,47 @@ showJS.tags=JS home.autoRedact.title=Redacció Automàtica home.autoRedact.desc=Redacta automàticament (enfosqueix) text en un PDF basat en el text introduït -autoRedact.tags=Redact,Hide,black out,black,marker,hidden +autoRedact.tags=Redactar,Amagar,ressaltar en negre,negre,marcador,ocult -home.redact.title=Manual Redaction -home.redact.desc=Redacts a PDF based on selected text, drawn shapes and/or selected page(s) -redact.tags=Redact,Hide,black out,black,marker,hidden,manual +home.redact.title=Redacció manual +home.redact.desc=Redacta un PDF segons el text seleccionat, les formes dibuixades i/o les pàgines seleccionades +redact.tags=Redactar,Amagar,ressaltar en negre,negre,marcador,ocult,manual home.tableExtraxt.title=PDF a CSV home.tableExtraxt.desc=Extreu taules d'un PDF convertint-les a CSV -tableExtraxt.tags=CSV,Table Extraction,extract,convert +tableExtraxt.tags=CSV,Extracció de taules,extreure,convertir home.autoSizeSplitPDF.title=Divisió Automàtica per Mida/Quantitat home.autoSizeSplitPDF.desc=Divideix un únic PDF en múltiples documents basant-se en la mida, el nombre de pàgines o el nombre de documents -autoSizeSplitPDF.tags=pdf,split,document,organization +autoSizeSplitPDF.tags=pdf,dividir,document,organització home.overlay-pdfs.title=Superposar PDFs home.overlay-pdfs.desc=Superposa PDFs sobre un altre PDF -overlay-pdfs.tags=Overlay +overlay-pdfs.tags=Superposició home.split-by-sections.title=Divideix PDF per Seccions home.split-by-sections.desc=Divideix cada pàgina d'un PDF en seccions horitzontals i verticals més petites -split-by-sections.tags=Section Split, Divide, Customize +split-by-sections.tags=Divisió per seccions,Divideix,Personalitza -home.AddStampRequest.title=Afegeix Segell al PDF +home.AddStampRequest.title=Afegeix segell al PDF home.AddStampRequest.desc=Afegeix segells de text o imatge en ubicacions establertes -AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +AddStampRequest.tags=Segell, Afegeix imatge, Centra imatge, Marca d'aigua, PDF, Insereix, Personalitza home.removeImagePdf.title=Elimina imatge home.removeImagePdf.desc=Elimina imatges d'un PDF per reduir la mida del fitxer -removeImagePdf.tags=Remove Image,Page operations,Back end,server side +removeImagePdf.tags=Elimina imatge,Operacions de pàgina,Back-end,Servidor home.splitPdfByChapters.title=Divideix PDF per Capítols home.splitPdfByChapters.desc=Divideix un PDF en múltiples fitxers segons la seva estructura de capítols. -splitPdfByChapters.tags=split,chapters,bookmarks,organize +splitPdfByChapters.tags=dividir,capítols,marcadors,organitza -home.validateSignature.title=Validate PDF Signature -home.validateSignature.desc=Verify digital signatures and certificates in PDF documents -validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate +home.validateSignature.title=Validar Signatura PDF +home.validateSignature.desc=Verifica les signatures digitals i els certificats en documents PDF +validateSignature.tags=signatura,verifica,valida,pdf,certificat,signatura digital,Validar signatura,Validar certificat #replace-invert-color replace-color.title=Reemplaça-Inverteix-Color @@ -591,31 +591,31 @@ autoRedact.convertPDFToImageLabel=Converteix PDF a Imatge PDF (S'utilitza per el autoRedact.submitButton=Envia #redact -redact.title=Manual Redaction -redact.header=Manual Redaction -redact.submit=Redact -redact.textBasedRedaction=Text based Redaction -redact.pageBasedRedaction=Page-based Redaction -redact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box) -redact.pageRedactionNumbers.title=Pages -redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) -redact.redactionColor.title=Redaction Color -redact.export=Export -redact.upload=Upload -redact.boxRedaction=Box draw redaction +redact.title=Redacció manual +redact.header=Redacció manual +redact.submit=Redacta +redact.textBasedRedaction=Redacció basada en text +redact.pageBasedRedaction=Redacció basada en pàgines +redact.convertPDFToImageLabel=Converteix PDF a PDF-imatge (utilitzat per eliminar text darrere del quadre) +redact.pageRedactionNumbers.title=Pàgines +redact.pageRedactionNumbers.placeholder=(p. ex. 1,2,8 o 4,7,12-16 o 2n-1) +redact.redactionColor.title=Color de redacció +redact.export=Exporta +redact.upload=Puja +redact.boxRedaction=Redacció amb dibuix de quadre redact.zoom=Zoom -redact.zoomIn=Zoom in -redact.zoomOut=Zoom out -redact.nextPage=Next Page -redact.previousPage=Previous Page -redact.toggleSidebar=Toggle Sidebar -redact.showThumbnails=Show Thumbnails -redact.showDocumentOutline=Show Document Outline (double-click to expand/collapse all items) -redact.showAttatchments=Show Attachments -redact.showLayers=Show Layers (double-click to reset all layers to the default state) -redact.colourPicker=Colour Picker -redact.findCurrentOutlineItem=Find current outline item -redact.applyChanges=Apply Changes +redact.zoomIn=Apropa +redact.zoomOut=Allunya +redact.nextPage=Pàgina següent +redact.previousPage=Pàgina anterior +redact.toggleSidebar=Commuta la barra lateral +redact.showThumbnails=Mostra miniatures +redact.showDocumentOutline=Mostra l'esquema del document (doble clic per expandir/contraure tots els elements) +redact.showAttatchments=Mostra els adjunts +redact.showLayers=Mostra les capes (doble clic per restablir totes les capes a l'estat per defecte) +redact.colourPicker=Selector de colors +redact.findCurrentOutlineItem=Troba l'element actual de l'esquema +redact.applyChanges=Aplica els canvis #showJS showJS.title=Mostra Javascript @@ -647,9 +647,9 @@ getPdfInfo.downloadJson=Descarrega JSON #markdown-to-pdf MarkdownToPDF.title=Markdown To PDF MarkdownToPDF.header=Markdown To PDF -MarkdownToPDF.submit=Convert -MarkdownToPDF.help=Work in progress -MarkdownToPDF.credit=Uses WeasyPrint +MarkdownToPDF.submit=Converteix +MarkdownToPDF.help=Treball en curs +MarkdownToPDF.credit=Utilitza WeasyPrint #pdf-to-markdown @@ -976,42 +976,42 @@ pdfOrganiser.placeholder=(p. ex. 1,3,2 o 4-8,2,10-12 o 2n-1) #multiTool -multiTool.title=Eina Multifunció de PDF -multiTool.header=Eina Multifunció de PDF +multiTool.title=Eina multifunció de PDF +multiTool.header=Eina multifunció de PDF multiTool.uploadPrompts=Nom del fitxer -multiTool.selectAll=Select All -multiTool.deselectAll=Deselect All -multiTool.selectPages=Page Select -multiTool.selectedPages=Selected Pages -multiTool.page=Page -multiTool.deleteSelected=Delete Selected -multiTool.downloadAll=Export -multiTool.downloadSelected=Export Selected +multiTool.selectAll=Selecciona-ho tot +multiTool.deselectAll=Desselecciona-ho tot +multiTool.selectPages=Selecció de pàgines +multiTool.selectedPages=Pàgines seleccionades +multiTool.page=Pàgina +multiTool.deleteSelected=Suprimeix seleccionades +multiTool.downloadAll=Exporta +multiTool.downloadSelected=Exporta seleccionades -multiTool.insertPageBreak=Insert Page Break -multiTool.addFile=Add File -multiTool.rotateLeft=Rotate Left -multiTool.rotateRight=Rotate Right -multiTool.split=Split -multiTool.moveLeft=Move Left -multiTool.moveRight=Move Right -multiTool.delete=Delete -multiTool.dragDropMessage=Page(s) Selected -multiTool.undo=Undo -multiTool.redo=Redo +multiTool.insertPageBreak=Insereix un salt de pàgina +multiTool.addFile=Afegeix fitxer +multiTool.rotateLeft=Gira a l'esquerra +multiTool.rotateRight=Gira a la dreta +multiTool.split=Divideix +multiTool.moveLeft=Mou a l'esquerra +multiTool.moveRight=Mou a la dreta +multiTool.delete=Suprimeix +multiTool.dragDropMessage=Pàgina(es) seleccionada(es) +multiTool.undo=Desfés +multiTool.redo=Refés #decrypt -decrypt.passwordPrompt=This file is password-protected. Please enter the password: -decrypt.cancelled=Operation cancelled for PDF: {0} -decrypt.noPassword=No password provided for encrypted PDF: {0} -decrypt.invalidPassword=Please try again with the correct password. -decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0} -decrypt.unexpectedError=There was an error processing the file. Please try again. -decrypt.serverError=Server error while decrypting: {0} -decrypt.success=File decrypted successfully. +decrypt.passwordPrompt=Aquest fitxer està protegit amb contrasenya. Si us plau, introdueix la contrasenya: +decrypt.cancelled=Operació cancel·lada per al PDF: {0} +decrypt.noPassword=No s'ha proporcionat cap contrasenya per al PDF xifrat: {0} +decrypt.invalidPassword=Si us plau, torna-ho a intentar amb la contrasenya correcta. +decrypt.invalidPasswordHeader=Contrasenya incorrecta o xifratge no compatible per al PDF: {0} +decrypt.unexpectedError=S'ha produït un error en processar el fitxer. Si us plau, torna-ho a intentar. +decrypt.serverError=Error del servidor en desxifrar: {0} +decrypt.success=Fitxer desxifrat correctament. #multiTool-advert -multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features! +multiTool-advert.message=Aquesta funcionalitat també està disponible a la nostra pàgina d'eines múltiples. Fes-hi una ullada per obtenir una interfície millorada per pàgina i funcions addicionals! #view pdf viewPdf.title=View/Edit PDF @@ -1323,52 +1323,52 @@ splitByChapters.desc.4=Permetre Duplicats: Si està marcat, permet diversos marc splitByChapters.submit=Divideix PDF #File Chooser -fileChooser.click=Click -fileChooser.or=or -fileChooser.dragAndDrop=Drag & Drop -fileChooser.dragAndDropPDF=Drag & Drop PDF file -fileChooser.dragAndDropImage=Drag & Drop Image file -fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here +fileChooser.click=Clica +fileChooser.or=o +fileChooser.dragAndDrop=Arrossega i deixa anar +fileChooser.dragAndDropPDF=Arrossega i deixa anar un fitxer PDF +fileChooser.dragAndDropImage=Arrossega i deixa anar un fitxer d'imatge +fileChooser.hoveredDragAndDrop=Arrossega i deixa anar fitxer(s) aquí fileChooser.extractPDF=Extracting... #release notes -releases.footer=Releases -releases.title=Release Notes -releases.header=Release Notes -releases.current.version=Current Release -releases.note=Release notes are only available in English +releases.footer=Llançaments +releases.title=Notes de Llançament +releases.header=Notes de Llançament +releases.current.version=Llançament Actual +releases.note=Les notes de llançament només estan disponibles en anglès #Validate Signature -validateSignature.title=Validate PDF Signatures -validateSignature.header=Validate Digital Signatures -validateSignature.selectPDF=Select signed PDF file -validateSignature.submit=Validate Signatures -validateSignature.results=Validation Results -validateSignature.status=Status -validateSignature.signer=Signer -validateSignature.date=Date -validateSignature.reason=Reason -validateSignature.location=Location -validateSignature.noSignatures=No digital signatures found in this document -validateSignature.status.valid=Valid -validateSignature.status.invalid=Invalid -validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity -validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified -validateSignature.cert.expired=Certificate has expired -validateSignature.cert.revoked=Certificate has been revoked -validateSignature.signature.info=Signature Information -validateSignature.signature=Signature -validateSignature.signature.mathValid=Signature is mathematically valid BUT: -validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional) -validateSignature.cert.info=Certificate Details -validateSignature.cert.issuer=Issuer -validateSignature.cert.subject=Subject -validateSignature.cert.serialNumber=Serial Number -validateSignature.cert.validFrom=Valid From -validateSignature.cert.validUntil=Valid Until -validateSignature.cert.algorithm=Algorithm -validateSignature.cert.keySize=Key Size -validateSignature.cert.version=Version -validateSignature.cert.keyUsage=Key Usage -validateSignature.cert.selfSigned=Self-Signed +validateSignature.title=Validar Signatures PDF +validateSignature.header=Validar Signatures Digitals +validateSignature.selectPDF=Selecciona fitxer PDF signat +validateSignature.submit=Validar Signatures +validateSignature.results=Resultats de Validació +validateSignature.status=Estat +validateSignature.signer=Signant +validateSignature.date=Data +validateSignature.reason=Motiu +validateSignature.location=Ubicació +validateSignature.noSignatures=No s'han trobat signatures digitals en aquest document +validateSignature.status.valid=Vàlid +validateSignature.status.invalid=Invàlid +validateSignature.chain.invalid=La validació de la cadena de certificats ha fallat - no es pot verificar la identitat del signant +validateSignature.trust.invalid=El certificat no es troba en el magatzem de confiança - no es pot verificar la font +validateSignature.cert.expired=El certificat ha expirat +validateSignature.cert.revoked=El certificat ha estat revocat +validateSignature.signature.info=Informació de la Signatura +validateSignature.signature=Signatura +validateSignature.signature.mathValid=La signatura és matemàticament vàlida, PERÒ: +validateSignature.selectCustomCert=Fitxer de Certificat Personalitzat X.509 (Opcional) +validateSignature.cert.info=Detalls del Certificat +validateSignature.cert.issuer=Emissor +validateSignature.cert.subject=Assumpte +validateSignature.cert.serialNumber=Número de sèrie +validateSignature.cert.validFrom=Vàlid des de +validateSignature.cert.validUntil=Vàlid fins a +validateSignature.cert.algorithm=Algorisme +validateSignature.cert.keySize=Mida de la clau +validateSignature.cert.version=Versió +validateSignature.cert.keyUsage=Ús de la clau +validateSignature.cert.selfSigned=Autofirmat validateSignature.cert.bits=bits From aee8b8e5c6ca9338b4c5a5a7f6cf2a7553903061 Mon Sep 17 00:00:00 2001 From: daenur Date: Tue, 11 Mar 2025 14:08:23 +0200 Subject: [PATCH 08/10] Ukrainian translation has been improved (#3161) Ukrainian translation has been improved messages_uk_UA.properties --- src/main/resources/messages_uk_UA.properties | 612 +++++++++---------- 1 file changed, 306 insertions(+), 306 deletions(-) diff --git a/src/main/resources/messages_uk_UA.properties b/src/main/resources/messages_uk_UA.properties index 8547463fe..f17f8a774 100644 --- a/src/main/resources/messages_uk_UA.properties +++ b/src/main/resources/messages_uk_UA.properties @@ -3,8 +3,8 @@ ########### # the direction that the language is written (ltr = left to right, rtl = right to left) language.direction=ltr -addPageNumbers.fontSize=Font Size -addPageNumbers.fontName=Font Name +addPageNumbers.fontSize=Розмір шрифту +addPageNumbers.fontName=Назва шрифту pdfPrompt=Оберіть PDF(и) multiPdfPrompt=Оберіть PDFи (2+) multiPdfDropPrompt=Оберіть (або перетягніть) всі необхідні PDFи @@ -56,15 +56,15 @@ userNotFoundMessage=Користувача не знайдено. incorrectPasswordMessage=Поточний пароль невірний. usernameExistsMessage=Нове ім'я користувача вже існує. invalidUsernameMessage=Недійсне ім’я користувача, ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою. -invalidPasswordMessage=The password must not be empty and must not have spaces at the beginning or end. -confirmPasswordErrorMessage=New Password and Confirm New Password must match. +invalidPasswordMessage=Пароль не повинен бути порожнім і не повинен мати пробілів на початку або в кінці. +confirmPasswordErrorMessage=Новий пароль і підтвердження нового пароля мають збігатися. deleteCurrentUserMessage=Неможливо видалити користувача, який увійшов в систему. deleteUsernameExistsMessage=Ім'я користувача не існує і не може бути видалено. downgradeCurrentUserMessage=Неможливо понизити роль поточного користувача -disabledCurrentUserMessage=The current user cannot be disabled +disabledCurrentUserMessage=Поточного користувача неможливо вимкнути downgradeCurrentUserLongMessage=Неможливо понизити роль поточного користувача. Отже, поточний користувач не відображатиметься. -userAlreadyExistsOAuthMessage=The user already exists as an OAuth2 user. -userAlreadyExistsWebMessage=The user already exists as an web user. +userAlreadyExistsOAuthMessage=Користувач уже існує як користувач OAuth2. +userAlreadyExistsWebMessage=Користувач уже існує як веб-користувач. error=Помилка oops=Упс! help=Допомога @@ -77,18 +77,18 @@ color=Колір sponsor=Спонсор info=Інформація pro=Pro -page=Page -pages=Pages -loading=Loading... -addToDoc=Add to Document -reset=Reset -apply=Apply +page=Сторінка +pages=Сторінки +loading=Завантаження... +addToDoc=Додати до документу +reset=Скинути +apply=Застосувати -legal.privacy=Privacy Policy -legal.terms=Terms and Conditions -legal.accessibility=Accessibility -legal.cookie=Cookie Policy -legal.impressum=Impressum +legal.privacy=Політика конфіденційності +legal.terms=Правила та умови +legal.accessibility=Доступність +legal.cookie=Політика використання файлів cookie +legal.impressum=Вихідні дані ############### # Pipeline # @@ -100,7 +100,7 @@ pipeline.defaultOption=Користувацький pipeline.submitButton=Надіслати pipeline.help=Довідка з конвеєрної обробки pipeline.scanHelp=Довідка зі сканування папок -pipeline.deletePrompt=Are you sure you want to delete pipeline +pipeline.deletePrompt=Ви впевнені, що хочете видалити конвеєр? ###################### # Pipeline Options # @@ -118,27 +118,27 @@ pipelineOptions.validateButton=Перевірити ######################## # ENTERPRISE EDITION # ######################## -enterpriseEdition.button=Upgrade to Pro -enterpriseEdition.warning=This feature is only available to Pro users. -enterpriseEdition.yamlAdvert=Stirling PDF Pro supports YAML configuration files and other SSO features. -enterpriseEdition.ssoAdvert=Looking for more user management features? Check out Stirling PDF Pro +enterpriseEdition.button=Оновлення до Pro +enterpriseEdition.warning=Ця функція доступна лише для користувачів Pro. +enterpriseEdition.yamlAdvert=Stirling PDF Pro підтримує конфігураційні файли YAML та інші функції SSO. +enterpriseEdition.ssoAdvert=Шукаєте більше функцій керування користувачами? Перегляньте Stirling PDF Pro ################# # Analytics # ################# -analytics.title=Do you want make Stirling PDF better? -analytics.paragraph1=Stirling PDF has opt in analytics to help us improve the product. We do not track any personal information or file contents. -analytics.paragraph2=Please consider enabling analytics to help Stirling-PDF grow and to allow us to understand our users better. -analytics.enable=Enable analytics -analytics.disable=Disable analytics -analytics.settings=You can change the settings for analytics in the config/settings.yml file +analytics.title=Бажаєте покращити Stirling PDF? +analytics.paragraph1=Stirling PDF увімкнув аналітику, щоб допомогти нам покращити продукт. Ми не відстежуємо жодну особисту інформацію чи вміст файлів. +analytics.paragraph2=Увімкніть аналітику, щоб допомогти Stirling-PDF розвиватися та дозволити нам краще розуміти наших користувачів. +analytics.enable=Увімкнути аналітику +analytics.disable=Вимкнути аналітику +analytics.settings=Ви можете змінити параметри аналітики у файлі config/settings.yml ############# # NAVBAR # ############# navbar.favorite=Обране -navbar.recent=New and recently updated +navbar.recent=Новий і нещодавно оновлений navbar.darkmode=Темний режим navbar.language=Мови navbar.settings=Налаштування @@ -151,7 +151,7 @@ navbar.sections.convertFrom=Конвертувати з PDF navbar.sections.security=Підпис та Безпека navbar.sections.advance=Додаткове navbar.sections.edit=Перегляд та Редагування -navbar.sections.popular=Popular +navbar.sections.popular=Популярне ############# # SETTINGS # @@ -167,9 +167,9 @@ 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 +settings.bored.help=Вмикає гру «пасхальне яйце». +settings.cacheInputs.name=Зберігати дані форм +settings.cacheInputs.help=Увімкнути для збереження раніше використаних вхідних даних для майбутніх прогонів changeCreds.title=Змінити облікові дані changeCreds.header=Оновіть дані вашого облікового запису @@ -210,7 +210,7 @@ adminUserSettings.user=Користувач adminUserSettings.addUser=Додати нового користувача adminUserSettings.deleteUser=Видалити користувача adminUserSettings.confirmDeleteUser=Видалити цього користувача? -adminUserSettings.confirmChangeUserStatus=Should the user be disabled/enabled? +adminUserSettings.confirmChangeUserStatus=Чи потрібно вимкнути/ввімкнути користувача? adminUserSettings.usernameInfo=Ім’я користувача може містити лише літери, цифри та наступні спеціальні символи @._+- або має бути дійсною електронною адресою. adminUserSettings.roles=Ролі adminUserSettings.role=Роль @@ -224,36 +224,36 @@ adminUserSettings.forceChange=Примусити користувача змін adminUserSettings.submit=Зберегти користувача adminUserSettings.changeUserRole=Змінити роль користувача adminUserSettings.authenticated=Автентифіковано -adminUserSettings.editOwnProfil=Edit own profile -adminUserSettings.enabledUser=enabled user -adminUserSettings.disabledUser=disabled user -adminUserSettings.activeUsers=Active Users: -adminUserSettings.disabledUsers=Disabled Users: -adminUserSettings.totalUsers=Total Users: -adminUserSettings.lastRequest=Last Request +adminUserSettings.editOwnProfil=Редагувати власний профіль +adminUserSettings.enabledUser=активний користувач +adminUserSettings.disabledUser=заблокований користувач +adminUserSettings.activeUsers=Активні користувачі: +adminUserSettings.disabledUsers=Заблоковані користувачі: +adminUserSettings.totalUsers=Всього користувачів: +adminUserSettings.lastRequest=Останній запит -database.title=Database Import/Export -database.header=Database Import/Export -database.fileName=File Name -database.creationDate=Creation Date -database.fileSize=File Size -database.deleteBackupFile=Delete Backup File -database.importBackupFile=Import Backup File -database.createBackupFile=Create Backup File -database.downloadBackupFile=Download Backup File -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=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=Import Backup -database.importIntoDatabaseSuccessed=Import into database successed -database.backupCreated=Database backup successful -database.fileNotFound=File not Found -database.fileNullOrEmpty=File must not be null or empty -database.failedImportFile=Failed Import File -database.notSupported=This function is not available for your database connection. +database.title=Імпорт/експорт бази даних +database.header=Імпорт/експорт бази даних +database.fileName=Ім'я файлу +database.creationDate=Дата створення +database.fileSize=Розмір файлу +database.deleteBackupFile=Видалити файл резервної копії +database.importBackupFile=Імпортувати файл резервної копії +database.createBackupFile=Створити файл резервної копії +database.downloadBackupFile=Завантажте файл резервної копії +database.info_1=При імпорті даних важливо забезпечити правильну структуру. Якщо ви не впевнені у своїх діях, зверніться за професійною допомогою. Помилка в структурі може призвести до збоїв у роботі програми та призвести до повної непрацездатності. +database.info_2=Ім'я файлу під час завантаження не має значення. Воно буде перейменовано на формат backup_user_yyyyMMddHHmm.sql для забезпечення одноманітності найменувань. +database.submit=Імпорт резервної копії +database.importIntoDatabaseSuccessed=Імпорт до бази даних виконано вдало +database.backupCreated=Резервне копіювання бази даних успішно +database.fileNotFound=Файл не знайдено +database.fileNullOrEmpty=Файл не має бути пустим +database.failedImportFile=Не вдалося імпортувати файл +database.notSupported=Ця функція недоступна для вашого підключення до бази даних. -session.expired=Your session has expired. Please refresh the page and try again. -session.refreshPage=Refresh Page +session.expired=Ваш сеанс закінчився. Будь ласка, оновіть сторінку та повторіть спробу. +session.refreshPage=Оновити сторінку ############# # HOME-PAGE # @@ -262,128 +262,128 @@ home.desc=Ваш локальний універсальний магазин д home.searchBar=Пошук функцій... -home.viewPdf.title=View/Edit PDF +home.viewPdf.title=Перегляд/редагування PDF home.viewPdf.desc=Перегляд, анотація, додавання тексту або зображень -viewPdf.tags=view,read,annotate,text,image +viewPdf.tags=перегляд,читання,анотації,текст,зображення -home.setFavorites=Set Favourites -home.hideFavorites=Hide Favourites -home.showFavorites=Show Favourites -home.legacyHomepage=Old homepage -home.newHomePage=Try our new homepage! -home.alphabetical=Alphabetical -home.globalPopularity=Global Popularity -home.sortBy=Sort by: +home.setFavorites=Налаштувати обрані +home.hideFavorites=Приховати обрані +home.showFavorites=Показати обрані +home.legacyHomepage=Стара сторінка +home.newHomePage=Спробуйте нову сторінку! +home.alphabetical=Абеткою +home.globalPopularity=Глобальною поулярністю +home.sortBy=Сортувати за: home.multiTool.title=Мультіінструмент PDF home.multiTool.desc=Об'єднання, поворот, зміна порядку та видалення сторінок -multiTool.tags=Multi Tool,Multi operation,UI,click drag,front end,client side +multiTool.tags=мультиінструмент,багатоопераційний,інтерфейс,перетягування,клієнтська частина,інтерактивний home.merge.title=Об'єднати home.merge.desc=Легко об'єднуйте кілька PDF-файлів у один. -merge.tags=merge,Page operations,Back end,server side +merge.tags=об'єднання,операції зі сторінками,серверна частина home.split.title=Розділити home.split.desc=Розділіть PDF-файли на кілька документів -split.tags=Page operations,divide,Multi Page,cut,server side +split.tags=операції зі сторінками,розділення,багатосторінковий,вирізання,серверна частина home.rotate.title=Повернути home.rotate.desc=Легко повертайте ваші PDF-файли. -rotate.tags=server side +rotate.tags=серверна частина home.imageToPdf.title=Зображення в PDF home.imageToPdf.desc=Перетворення зображення (PNG, JPEG, GIF) в PDF. -imageToPdf.tags=conversion,img,jpg,picture,photo +imageToPdf.tags=конвертація,зображення,jpg,картинка,фото home.pdfToImage.title=PDF в зображення home.pdfToImage.desc=Перетворення PDF в зображення. (PNG, JPEG, GIF) -pdfToImage.tags=conversion,img,jpg,picture,photo +pdfToImage.tags=конвертація,зображення,jpg,картинка,фото home.pdfOrganiser.title=Реорганізація home.pdfOrganiser.desc=Видалення/перестановка сторінок у будь-якому порядку -pdfOrganiser.tags=duplex,even,odd,sort,move +pdfOrganiser.tags=двосторонній друк,парні,непарні,сортування,переміщення home.addImage.title=Додати зображення home.addImage.desc=Додає зображення у вказане місце в PDF (в розробці) -addImage.tags=img,jpg,picture,photo +addImage.tags=зображення,jpg,картинка,фото home.watermark.title=Додати водяний знак home.watermark.desc=Додайте свій водяний знак до документа PDF. -watermark.tags=Text,repeating,label,own,copyright,trademark,img,jpg,picture,photo +watermark.tags=текст,повторний,мітка,власний,авторське право,торговельна марка,зображення,jpg,картинка,фото home.permissions.title=Змінити дозволи home.permissions.desc=Змініть дозволи вашого документа PDF -permissions.tags=read,write,edit,print +permissions.tags=читання,запис,редагування,друк home.removePages.title=Видалення home.removePages.desc=Видаліть непотрібні сторінки з документа PDF. -removePages.tags=Remove pages,delete pages +removePages.tags=видалити сторінки,видалення сторінок home.addPassword.title=Додати пароль home.addPassword.desc=Зашифруйте документ PDF паролем. -addPassword.tags=secure,security +addPassword.tags=безпека,захист home.removePassword.title=Видалити пароль home.removePassword.desc=Зніміть захист паролем з вашого документа PDF. -removePassword.tags=secure,Decrypt,security,unpassword,delete password +removePassword.tags=безпека,розшифровка,захист,видалення пароля home.compressPdfs.title=Стиснути home.compressPdfs.desc=Стискайте PDF-файли, щоб зменшити їх розмір. -compressPdfs.tags=squish,small,tiny +compressPdfs.tags=стиск,маленький,крихітний home.changeMetadata.title=Змінити метадані home.changeMetadata.desc=Змінити/видалити/додати метадані з документа PDF -changeMetadata.tags=Title,author,date,creation,time,publisher,producer,stats +changeMetadata.tags=заголовок,автор,дата,створення,час,видавець,виробник,статистика 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 +fileToPDF.tags=перетворення,формат,документ,картинка,презентація,текст,конвертація,офіс,документи,word,excel,powerpoint home.ocr.title=OCR/Очищення сканування home.ocr.desc=Очищення сканування та виявлення тексту на зображеннях у файлі PDF та повторне додавання його як текст. -ocr.tags=recognition,text,image,scan,read,identify,detection,editable +ocr.tags=розпізнавання,текст,зображення,сканування,читання,ідентифікація,виявлення,редагований home.extractImages.title=Витягнути зображення home.extractImages.desc=Витягує всі зображення з PDF і зберігає їх у zip -extractImages.tags=picture,photo,save,archive,zip,capture,grab +extractImages.tags=зображення,фото,збереження,архів,zip,захоплення,захоплення home.pdfToPDFA.title=PDF в PDF/A home.pdfToPDFA.desc=Перетворення PDF в PDF/A для довготривалого зберігання -pdfToPDFA.tags=archive,long-term,standard,conversion,storage,preservation +pdfToPDFA.tags=архів,довгостроковий,стандартний,конверсія,зберігання,консервація 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 +PDFToWord.tags=doc,docx,odt,word,перетворення,формат,перетворення,офіс,microsoft,docfile home.PDFToPresentation.title=PDF в презентацію home.PDFToPresentation.desc=Перетворення PDF в формати презентацій (PPT, PPTX та ODP) -PDFToPresentation.tags=slides,show,office,microsoft +PDFToPresentation.tags=слайди,шоу,офіс,майкрософт home.PDFToText.title=PDF в Text/RTF home.PDFToText.desc=Перетворення PDF в текстовий або RTF формат -PDFToText.tags=richformat,richtextformat,rich text format +PDFToText.tags=richformat,richtextformat,формат rich text,rtf home.PDFToHTML.title=PDF в HTML home.PDFToHTML.desc=Перетворення PDF в формат HTML -PDFToHTML.tags=web content,browser friendly +PDFToHTML.tags=веб-контент,зручний для перегляду home.PDFToXML.title=PDF в XML home.PDFToXML.desc=Перетворення PDF в формат XML -PDFToXML.tags=data-extraction,structured-content,interop,transformation,convert +PDFToXML.tags=вилучення даних,структурований вміст,взаємодія,перетворення,перетворення home.ScannerImageSplit.title=Виявлення/розділення відсканованих фотографій home.ScannerImageSplit.desc=Розділяє кілька фотографій з фото/PDF -ScannerImageSplit.tags=separate,auto-detect,scans,multi-photo,organize +ScannerImageSplit.tags=окремий,автоматичне визначення,сканування,кілька фотографій,упорядкування home.sign.title=Підпис home.sign.desc=Додає підпис до PDF за допомогою малюнка, тексту або зображення -sign.tags=authorize,initials,drawn-signature,text-sign,image-signature +sign.tags=авторизувати,ініціали,намальований-підпис,текстовий-підпис,зображення-підпис home.flatten.title=Згладжування home.flatten.desc=Видалення всіх інтерактивних елементів та форм з PDF @@ -391,162 +391,162 @@ flatten.tags=static,deactivate,non-interactive,streamline home.repair.title=Ремонт home.repair.desc=Намагається відновити пошкоджений/зламаний PDF -repair.tags=fix,restore,correction,recover +repair.tags=виправити,відновити,виправити,відновити home.removeBlanks.title=Видалити порожні сторінки home.removeBlanks.desc=Виявляє та видаляє порожні сторінки з документа -removeBlanks.tags=cleanup,streamline,non-content,organize +removeBlanks.tags=очищення,упорядкування,без вмісту,упорядкування home.removeAnnotations.title=Видалити анотації home.removeAnnotations.desc=Видаляє всі коментарі/анотації з PDF -removeAnnotations.tags=comments,highlight,notes,markup,remove +removeAnnotations.tags=коментарі,виділення,примітки,розмітка,видалення home.compare.title=Порівняння home.compare.desc=Порівнює та показує різницю між двома PDF-документами -compare.tags=differentiate,contrast,changes,analysis +compare.tags=диференціація,контраст,зміни,аналіз home.certSign.title=Підписати сертифікатом home.certSign.desc=Підписати PDF сертифікатом/ключем (PEM/P12) -certSign.tags=authenticate,PEM,P12,official,encrypt +certSign.tags=автентифікація,pem,p12,офіційний,шифрування home.removeCertSign.title=Видалити підпис сертифікатом home.removeCertSign.desc=Видалити підпис сертифікатом з PDF-документу -removeCertSign.tags=authenticate,PEM,P12,official,decrypt +removeCertSign.tags=автентифікація,pem,p12,офіційний,розшифрувати home.pageLayout.title=Об'єднати сторінки home.pageLayout.desc=Об'єднання кількох сторінок документа PDF в одну сторінку -pageLayout.tags=merge,composite,single-view,organize +pageLayout.tags=об'єднати,скласти,єдиний перегляд,упорядкувати home.scalePages.title=Змінити розмір/масштаб сторінки home.scalePages.desc=Змінити розмір/масштаб сторінки та/або її вмісту. -scalePages.tags=resize,modify,dimension,adapt +scalePages.tags=змінити розмір,змінити,розмір,адаптувати home.pipeline.title=Конвеєр (розширений) home.pipeline.desc=Виконуйте кілька дій з PDF-файлами, визначаючи сценарії конвеєрної обробки. -pipeline.tags=automate,sequence,scripted,batch-process +pipeline.tags=автоматизація,послідовність,сценарій,scripted,batch-process home.add-page-numbers.title=Додати номера сторінок home.add-page-numbers.desc=Додає номера сторінок по всьому документу в заданому місці -add-page-numbers.tags=paginate,label,organize,index +add-page-numbers.tags=розбити на сторінки,позначити,упорядкувати,індексувати home.auto-rename.title=Автоматичне перейменування PDF-файлу home.auto-rename.desc=Автоматичне перейменування файлу PDF на основі його виявленого заголовку -auto-rename.tags=auto-detect,header-based,organize,relabel +auto-rename.tags=автоматичне визначення,на основі заголовка,організація,зміна міток home.adjust-contrast.title=Налаштування кольорів/контрастності home.adjust-contrast.desc=Налаштування контрастності, насиченості та яскравості файлу PDF -adjust-contrast.tags=color-correction,tune,modify,enhance +adjust-contrast.tags=корекція кольору,налаштування,зміна,покращення home.crop.title=Обрізати PDF-файл home.crop.desc=Обрізати PDF-файл, щоб зменшити його розмір (текст залишається!) -crop.tags=trim,shrink,edit,shape +crop.tags=обрізати,зменшувати,редагувати,формувати home.autoSplitPDF.title=Автоматичне розділення сторінок home.autoSplitPDF.desc=Автоматичне розділення відсканованого PDF-файлу за допомогою фізичного роздільника відсканованих сторінок QR-коду -autoSplitPDF.tags=QR-based,separate,scan-segment,organize +autoSplitPDF.tags=на основі qr,відокремити,сканувати сегмент,упорядкувати home.sanitizePdf.title=Санітарна обробка home.sanitizePdf.desc=Видалення скриптів та інших елементів з PDF-файлів -sanitizePdf.tags=clean,secure,safe,remove-threats +sanitizePdf.tags=чистка,безпека,безпечні,віддалення загроз home.URLToPDF.title=URL/сайт у PDF home.URLToPDF.desc=Конвертує будь-який http(s)URL у PDF -URLToPDF.tags=web-capture,save-page,web-to-doc,archive +URLToPDF.tags=веб-захоплення,збереження сторінки,веб-документ,архів home.HTMLToPDF.title=HTML у PDF home.HTMLToPDF.desc=Конвертує будь-який HTML-файл або zip-файл у PDF. -HTMLToPDF.tags=markup,web-content,transformation,convert +HTMLToPDF.tags=розмітка,веб-контент,перетворення,конвертація home.MarkdownToPDF.title=Markdown у PDF home.MarkdownToPDF.desc=Конвертує будь-який файл Markdown у PDF -MarkdownToPDF.tags=markup,web-content,transformation,convert +MarkdownToPDF.tags=розмітка,веб-контент,перетворення,конвертація -home.PDFToMarkdown.title=PDF to Markdown -home.PDFToMarkdown.desc=Converts any PDF to Markdown -PDFToMarkdown.tags=markup,web-content,transformation,convert,md +home.PDFToMarkdown.title=PDF у Markdown +home.PDFToMarkdown.desc=Конвертує будь-який файл PDF у Markdown +PDFToMarkdown.tags=розмітка,веб-вміст,трансформація,перетворення,md home.getPdfInfo.title=Отримати ВСЮ інформацію у форматі PDF home.getPdfInfo.desc=Збирає будь-яку можливу інформацію у PDF-файлах. -getPdfInfo.tags=infomation,data,stats,statistics +getPdfInfo.tags=інформація,дані,статистика,статистика home.extractPage.title=Видобути сторінку(и) home.extractPage.desc=Видобуває обрані сторінки з PDF -extractPage.tags=extract +extractPage.tags=екстракт home.PdfToSinglePage.title=PDF на одну велику сторінку home.PdfToSinglePage.desc=Об'єднує всі сторінки PDF в одну велику сторінку. -PdfToSinglePage.tags=single page +PdfToSinglePage.tags=одну сторінку home.showJS.title=Показати JavaScript home.showJS.desc=Шукає та відображає будь-який JS, вбудований у PDF-файл. -showJS.tags=JS +showJS.tags=js home.autoRedact.title=Автоматичне редагування home.autoRedact.desc=Автоматичне затемнення (чорніння) тексту в PDF на основі вхідного тексту -autoRedact.tags=Redact,Hide,black out,black,marker,hidden +autoRedact.tags=редагувати,приховати,затемнити,чорний,маркер,приховано -home.redact.title=Manual Redaction -home.redact.desc=Redacts a PDF based on selected text, drawn shapes and/or selected page(s) -redact.tags=Redact,Hide,black out,black,marker,hidden,manual +home.redact.title=Ручне редагування +home.redact.desc=Редагує PDF-файл на основі виділеного тексту, намальованих форм і/або вибраних сторінок +redact.tags=редагувати,приховати,затемнити,чорний,маркер,приховано,вручну home.tableExtraxt.title=PDF у CSV home.tableExtraxt.desc=Видобуває таблиці з PDF та перетворює їх у CSV -tableExtraxt.tags=CSV,Table Extraction,extract,convert +tableExtraxt.tags=csv,видобуток таблиці,вилучення,конвертація home.autoSizeSplitPDF.title=Автоматичне розділення за розміром/кількістю home.autoSizeSplitPDF.desc=Розділяє один PDF на кілька документів на основі розміру, кількості сторінок або кількості документів -autoSizeSplitPDF.tags=pdf,split,document,organization +autoSizeSplitPDF.tags=pdf,розділити,документ,організація home.overlay-pdfs.title=Накладення PDF home.overlay-pdfs.desc=Накладення одного PDF поверх іншого PDF -overlay-pdfs.tags=Overlay +overlay-pdfs.tags=накладання home.split-by-sections.title=Розділення PDF за секціями home.split-by-sections.desc=Розділення кожної сторінки PDF на менші горизонтальні та вертикальні секції -split-by-sections.tags=Section Split, Divide, Customize +split-by-sections.tags=розділ розділу,розділення,налаштування home.AddStampRequest.title=Додати печатку на PDF home.AddStampRequest.desc=Додавання текстової або зображення печатки у вказані місця -AddStampRequest.tags=Stamp, Add image, center image, Watermark, PDF, Embed, Customize +AddStampRequest.tags=штамп,додати зображення,центральне зображення,водяний знак,pdf,вставити,налаштувати -home.removeImagePdf.title=Remove image -home.removeImagePdf.desc=Remove image from PDF to reduce file size -removeImagePdf.tags=Remove Image,Page operations,Back end,server side +home.removeImagePdf.title=Видалити зображення +home.removeImagePdf.desc=Видаляє зображення з PDF для зменшення розміру файлу +removeImagePdf.tags=видалення зображення,операції зі сторінками,серверна частина -home.splitPdfByChapters.title=Split PDF by Chapters -home.splitPdfByChapters.desc=Split a PDF into multiple files based on its chapter structure. -splitPdfByChapters.tags=split,chapters,bookmarks,organize +home.splitPdfByChapters.title=Розділити PDF за розділами +home.splitPdfByChapters.desc=Розділяє PDF на кілька файлів на основі структури його розділів +splitPdfByChapters.tags=поділ,глави,закладки,організація -home.validateSignature.title=Validate PDF Signature -home.validateSignature.desc=Verify digital signatures and certificates in PDF documents -validateSignature.tags=signature,verify,validate,pdf,certificate,digital signature,Validate Signature,Validate certificate +home.validateSignature.title=Перевірка підпису PDF +home.validateSignature.desc=Перевірка цифрових підписів та сертифікатів у PDF-документах +validateSignature.tags=підпис,перевірка,валідація,pdf,сертифікат,цифровий підпис,перевірка підпису,перевірка сертифіката #replace-invert-color -replace-color.title=Replace-Invert-Color -replace-color.header=Replace-Invert Color PDF -home.replaceColorPdf.title=Replace and Invert Color -home.replaceColorPdf.desc=Replace color for text and background in PDF and invert full color of pdf to reduce file size -replaceColorPdf.tags=Replace Color,Page operations,Back end,server side -replace-color.selectText.1=Replace or Invert color Options -replace-color.selectText.2=Default(Default high contrast colors) -replace-color.selectText.3=Custom(Customized colors) -replace-color.selectText.4=Full-Invert(Invert all colors) -replace-color.selectText.5=High contrast color options -replace-color.selectText.6=white text on black background -replace-color.selectText.7=Black text on white background -replace-color.selectText.8=Yellow text on black background -replace-color.selectText.9=Green text on black background -replace-color.selectText.10=Choose text Color -replace-color.selectText.11=Choose background Color -replace-color.submit=Replace +replace-color.title=Заміна-інверсія кольору +replace-color.header=Заміна-інверсія кольору PDF +home.replaceColorPdf.title=Заміна та інверсія кольору +home.replaceColorPdf.desc=Замінює колір тексту та фону у PDF та інвертує всі кольори PDF для зменшення розміру файлу +replaceColorPdf.tags=Заміна кольору, операції зі сторінками, Серверна частина +replace-color.selectText.1=Параметри заміни або інверсії кольору +replace-color.selectText.2=За замовчуванням (кольори високого розмаїття) +replace-color.selectText.3=Користувальницькі (настроювані кольори) +replace-color.selectText.4=Повна інверсія (інвертувати всі кольори) +replace-color.selectText.5=Параметри високого розмаїття +replace-color.selectText.6=білий текст на чорному тлі +replace-color.selectText.7=чорний текст на білому тлі +replace-color.selectText.8=жовтий текст на чорному тлі +replace-color.selectText.9=зелений текст на чорному тлі +replace-color.selectText.10=Вибрати колір тексту +replace-color.selectText.11=Вибрати колір тла +replace-color.submit=Замінити @@ -571,12 +571,12 @@ login.oauth2InvalidUserInfoResponse=Недійсна відповідь з ін login.oauth2invalidRequest=Недійсний запит login.oauth2AccessDenied=Доступ заблоковано login.oauth2InvalidTokenResponse=Недійсна відповідь з токеном -login.oauth2InvalidIdToken=Недійсний Id токен -login.relyingPartyRegistrationNotFound=No relying party registration found -login.userIsDisabled=User is deactivated, login is currently blocked with this username. Please contact the administrator. -login.alreadyLoggedIn=You are already logged in to -login.alreadyLoggedIn2=devices. Please log out of the devices and try again. -login.toManySessions=You have too many active sessions +login.oauth2InvalidIdToken=Недійсний ідентифікаційний токен +login.relyingPartyRegistrationNotFound=Реєстрацію довіряючої сторони не знайдено +login.userIsDisabled=Користувач деактивовано, вхід з цим ім'ям користувача заблоковано. Зверніться до адміністратора. +login.alreadyLoggedIn=Ви вже увійшли до +login.alreadyLoggedIn2=пристроїв (а). Будь ласка, вийдіть із цих пристроїв і спробуйте знову. +login.toManySessions=У вас дуже багато активних сесій #auto-redact autoRedact.title=Автоматичне редагування @@ -591,31 +591,31 @@ autoRedact.convertPDFToImageLabel=Перетворити PDF в зображен autoRedact.submitButton=Надіслати #redact -redact.title=Manual Redaction -redact.header=Manual Redaction -redact.submit=Redact -redact.textBasedRedaction=Text based Redaction -redact.pageBasedRedaction=Page-based Redaction -redact.convertPDFToImageLabel=Convert PDF to PDF-Image (Used to remove text behind the box) -redact.pageRedactionNumbers.title=Pages -redact.pageRedactionNumbers.placeholder=(e.g. 1,2,8 or 4,7,12-16 or 2n-1) -redact.redactionColor.title=Redaction Color -redact.export=Export -redact.upload=Upload -redact.boxRedaction=Box draw redaction -redact.zoom=Zoom -redact.zoomIn=Zoom in -redact.zoomOut=Zoom out -redact.nextPage=Next Page -redact.previousPage=Previous Page -redact.toggleSidebar=Toggle Sidebar -redact.showThumbnails=Show Thumbnails -redact.showDocumentOutline=Show Document Outline (double-click to expand/collapse all items) -redact.showAttatchments=Show Attachments -redact.showLayers=Show Layers (double-click to reset all layers to the default state) -redact.colourPicker=Colour Picker -redact.findCurrentOutlineItem=Find current outline item -redact.applyChanges=Apply Changes +redact.title=Ручне редагування +redact.header=Ручне редагування +redact.submit=Редагувати +redact.textBasedRedaction=Редагування на основі тексту +redact.pageBasedRedaction=Редагування на основі сторінок +redact.convertPDFToImageLabel=Перетворити PDF на PDF-зображення (використовується для видалення тексту за рамкою) +redact.pageRedactionNumbers.title=Сторінки +redact.pageRedactionNumbers.placeholder=(наприклад, 1,2,8 або 4,7,12-16 або 2n-1) +redact.redactionColor.title=Колір редагування +redact.export=Експорт +redact.upload=Завантажити +redact.boxRedaction=Редагування малюванням рамки +redact.zoom=Масштаб +redact.zoomIn=Збільшити +redact.zoomOut=Зменшити +redact.nextPage=Наступна сторінка +redact.previousPage=Попередня сторінка +redact.toggleSidebar=Перемикати бічну панель +redact.showThumbnails=Показати мініатюри +redact.showDocumentOutline=Показати структуру документа (подвійне клацання для розгортання/згортання всіх елементів) +redact.showAttatchments=Показати вкладення +redact.showLayers=Показати шари (подвійне клацання для скидання всіх шарів до стану за умовчанням) +redact.colourPicker=Вибір кольору +redact.findCurrentOutlineItem=Знайти поточний елемент структури +redact.applyChanges=Застосувати зміни #showJS showJS.title=Показати JavaScript @@ -831,14 +831,14 @@ removeAnnotations.submit=Видалити #compare compare.title=Порівняння compare.header=Порівняння PDF -compare.highlightColor.1=Highlight Color 1: -compare.highlightColor.2=Highlight Color 2: +compare.highlightColor.1=Колір виділення 1: +compare.highlightColor.2=Колір виділення 2: compare.document.1=Документ 1 compare.document.2=Документ 2 compare.submit=Порівняти -compare.complex.message=One or both of the provided documents are large files, accuracy of comparison may be reduced -compare.large.file.message=One or Both of the provided documents are too large to process -compare.no.text.message=One or both of the selected PDFs have no text content. Please choose PDFs with text for comparison. +compare.complex.message=Один або обидва надані документи є великими файлами, точність порівняння може бути знижена +compare.large.file.message=Один або обидва надані документи занадто великі для обробки +compare.no.text.message=Вибрані PDF-файли не містять текстового вмісту. Будь ласка, виберіть PDF-файли з текстом для порівняння. #sign sign.title=Підпис @@ -848,18 +848,18 @@ sign.draw=Намалювати підпис sign.text=Ввід тексту sign.clear=Очистити sign.add=Додати -sign.saved=Saved Signatures -sign.save=Save Signature -sign.personalSigs=Personal Signatures -sign.sharedSigs=Shared Signatures -sign.noSavedSigs=No saved signatures found -sign.addToAll=Add to all pages -sign.delete=Delete -sign.first=First page -sign.last=Last page -sign.next=Next page -sign.previous=Previous page -sign.maintainRatio=Toggle maintain aspect ratio +sign.saved=Збережені підписи +sign.save=Зберегти підпис +sign.personalSigs=Особисті підписи +sign.sharedSigs=Загальні підписи +sign.noSavedSigs=Збережені підписи не знайдено +sign.addToAll=Додати на всі сторінки +sign.delete=Видалити +sign.first=Перша сторінка +sign.last=Остання сторінка +sign.next=Наступна сторінка +sign.previous=Попередня сторінка +sign.maintainRatio=Переключити збереження пропорцій #repair @@ -886,7 +886,7 @@ ScannerImageSplit.selectText.7=Мінімальна площа контуру: ScannerImageSplit.selectText.8=Встановлює мінімальний поріг площі контуру для фотографії ScannerImageSplit.selectText.9=Розмір рамки: ScannerImageSplit.selectText.10=Встановлює розмір додаваної та видаляної рамки, щоб запобігти появі білих рамок на виході (за замовчуванням: 1). -ScannerImageSplit.info=Python is not installed. It is required to run. +ScannerImageSplit.info=Python не встановлено. Він необхідний роботи. #OCR @@ -976,45 +976,45 @@ pdfOrganiser.placeholder=(наприклад, 1,3,2 або 4-8,2,10-12 або 2n #multiTool -multiTool.title=Мультіінструмент PDF -multiTool.header=Мультіінструмент PDF +multiTool.title=Мультиінструмент PDF +multiTool.header=Мультиінструмент PDF multiTool.uploadPrompts=Ім'я файлу -multiTool.selectAll=Select All -multiTool.deselectAll=Deselect All -multiTool.selectPages=Page Select -multiTool.selectedPages=Selected Pages -multiTool.page=Page -multiTool.deleteSelected=Delete Selected -multiTool.downloadAll=Export -multiTool.downloadSelected=Export Selected +multiTool.selectAll=Вибрати все +multiTool.deselectAll=Скасувати вибір усіх +multiTool.selectPages=Вибір сторінки +multiTool.selectedPages=Вибрані сторінки +multiTool.page=Сторінка +multiTool.deleteSelected=Видалити вибрані +multiTool.downloadAll=Експорт +multiTool.downloadSelected=Експорт вибраних -multiTool.insertPageBreak=Insert Page Break -multiTool.addFile=Add File -multiTool.rotateLeft=Rotate Left -multiTool.rotateRight=Rotate Right -multiTool.split=Split -multiTool.moveLeft=Move Left -multiTool.moveRight=Move Right -multiTool.delete=Delete -multiTool.dragDropMessage=Page(s) Selected -multiTool.undo=Undo -multiTool.redo=Redo +multiTool.insertPageBreak=Вставити розрив сторінки +multiTool.addFile=Додати файл +multiTool.rotateLeft=Повернути вліво +multiTool.rotateRight=Повернути праворуч +multiTool.split=Розділити +multiTool.moveLeft=Перемістити вліво +multiTool.moveRight=Перемістити праворуч +multiTool.delete=Видалити +multiTool.dragDropMessage=Вибрано сторінок +multiTool.undo=Скасувати +multiTool.redo=Повторити #decrypt -decrypt.passwordPrompt=This file is password-protected. Please enter the password: -decrypt.cancelled=Operation cancelled for PDF: {0} -decrypt.noPassword=No password provided for encrypted PDF: {0} -decrypt.invalidPassword=Please try again with the correct password. -decrypt.invalidPasswordHeader=Incorrect password or unsupported encryption for PDF: {0} -decrypt.unexpectedError=There was an error processing the file. Please try again. -decrypt.serverError=Server error while decrypting: {0} -decrypt.success=File decrypted successfully. +decrypt.passwordPrompt=Цей файл захищений паролем. Будь ласка, введіть пароль: +decrypt.cancelled=Операцію скасовано для PDF: {0} +decrypt.noPassword=Не надано пароль для зашифрованого PDF: {0} +decrypt.invalidPassword=Будь ласка, спробуйте ще раз з правильним паролем. +decrypt.invalidPasswordHeader=Неправильний пароль або непідтримуване шифрування для PDF: {0} +decrypt.unexpectedError=Виникла помилка при обробці файлу. Будь ласка, спробуйте ще раз. +decrypt.serverError=Помилка сервера під час розшифровки: {0} +decrypt.success=Файл успішно розшифровано. #multiTool-advert -multiTool-advert.message=This feature is also available in our multi-tool page. Check it out for enhanced page-by-page UI and additional features! +multiTool-advert.message=Ця функція також доступна на нашій сторінці мультиінструменту. Спробуйте її для покращеного посторінкового інтерфейсу та додаткових можливостей! #view pdf -viewPdf.title=View/Edit PDF +viewPdf.title=Перегляд/редагування PDF viewPdf.header=Переглянути PDF #pageRemover @@ -1280,15 +1280,15 @@ survey.please=Будь-ласка, пройдіть опитування! survey.disabled=(Вікно з опитування буде відключено у наступних оновленнях, але буде доступне внизу сторінки) survey.button=Пройти опитування survey.dontShowAgain=Не показувати це вікно -survey.meeting.1=If you're using Stirling PDF at work, we'd love to speak to you. We're offering technical support sessions in exchange for a 15 minute user discovery session. -survey.meeting.2=This is a chance to: -survey.meeting.3=Get help with deployment, integrations, or troubleshooting -survey.meeting.4=Provide direct feedback on performance, edge cases, and feature gaps -survey.meeting.5=Help us refine Stirling PDF for real-world enterprise use -survey.meeting.6=If you're interested, you can book time with our team directly. (English speaking only) -survey.meeting.7=Looking forward to digging into your use cases and making Stirling PDF even better! -survey.meeting.notInterested=Not a business and/or interested in a meeting? -survey.meeting.button=Book meeting +survey.meeting.1=Якщо ви використовуєте Stirling PDF на роботі, ми будемо раді поговорити з вами. Ми пропонуємо сеанси технічної підтримки в обмін на 15-хвилинний сеанс пошуку користувачів. +survey.meeting.2=Це можливість: +survey.meeting.3=Отримайте допомогу щодо розгортання, інтеграції або усунення несправностей +survey.meeting.4=Надайте прямий відгук про продуктивність, крайні випадки та недоліки функцій +survey.meeting.5=Допоможіть нам удосконалити Stirling PDF для реального корпоративного використання +survey.meeting.6=Якщо ви зацікавлені, ви можете забронювати час безпосередньо з нашою командою. (тільки англомовний) +survey.meeting.7=З нетерпінням чекаємо на можливість розібратися у ваших сценаріях використання та зробити Stirling PDF ще кращим! +survey.meeting.notInterested=Не бізнес і/або зацікавлені у зустрічі? +survey.meeting.button=Зустріч #error error.sorry=Вибачте за незручності! @@ -1305,70 +1305,70 @@ error.discordSubmit=Discord - Надіслати повідомлення під #remove-image -removeImage.title=Remove image -removeImage.header=Remove image -removeImage.removeImage=Remove image -removeImage.submit=Remove image +removeImage.title=Видалити зображення +removeImage.header=Видалити зображення +removeImage.removeImage=Видалити зображення +removeImage.submit=Видалити зображення -splitByChapters.title=Split PDF by Chapters -splitByChapters.header=Split PDF by Chapters -splitByChapters.bookmarkLevel=Bookmark Level -splitByChapters.includeMetadata=Include Metadata -splitByChapters.allowDuplicates=Allow Duplicates -splitByChapters.desc.1=This tool splits a PDF file into multiple PDFs based on its chapter structure. -splitByChapters.desc.2=Bookmark Level: Choose the level of bookmarks to use for splitting (0 for top-level, 1 for second-level, etc.). -splitByChapters.desc.3=Include Metadata: If checked, the original PDF's metadata will be included in each split PDF. -splitByChapters.desc.4=Allow Duplicates: If checked, allows multiple bookmarks on the same page to create separate PDFs. -splitByChapters.submit=Split PDF +splitByChapters.title=Розділити PDF по главам +splitByChapters.header=Розділити PDF по главам +splitByChapters.bookmarkLevel=Уровень закладок +splitByChapters.includeMetadata=Включити метаданні +splitByChapters.allowDuplicates=Разрешить публикации +splitByChapters.desc.1=Цей інструмент розділяє PDF-файл на кілька PDF-файлів на основі своєї структури глави. +splitByChapters.desc.2=Уровень закладок: виберіть рівень закладок для розподілу (0 для верхнього рівня, 1 для другого рівня і т.д.). +splitByChapters.desc.3=Включити метаданні: якщо позначено, метаданні вихідного PDF будуть включені в кожен розділений PDF. +splitByChapters.desc.4=Розрішити публікації: якщо позначено, можна створити окремий PDF із кількох закладок на одній сторінці. +splitByChapters.submit=Розділити PDF #File Chooser -fileChooser.click=Click -fileChooser.or=or -fileChooser.dragAndDrop=Drag & Drop -fileChooser.dragAndDropPDF=Drag & Drop PDF file -fileChooser.dragAndDropImage=Drag & Drop Image file -fileChooser.hoveredDragAndDrop=Drag & Drop file(s) here -fileChooser.extractPDF=Extracting... +fileChooser.click=Натисніть +fileChooser.or=або +fileChooser.dragAndDrop=Перетащите +fileChooser.dragAndDropPDF=Перетащите PDF-файл +fileChooser.dragAndDropImage=Перетащите файл зображення +fileChooser.hoveredDragAndDrop=Перетащите файл(и) сюда +fileChooser.extractPDF=Видобування... #release notes -releases.footer=Releases -releases.title=Release Notes -releases.header=Release Notes -releases.current.version=Current Release -releases.note=Release notes are only available in English +releases.footer=Релізи +releases.title=Примечания к релизу +releases.header=Примечания к релизу +releases.current.version=Текущий релиз +releases.note=Примітка до релізу доступна тільки на англійській мові #Validate Signature -validateSignature.title=Validate PDF Signatures -validateSignature.header=Validate Digital Signatures -validateSignature.selectPDF=Select signed PDF file -validateSignature.submit=Validate Signatures -validateSignature.results=Validation Results -validateSignature.status=Status -validateSignature.signer=Signer -validateSignature.date=Date -validateSignature.reason=Reason -validateSignature.location=Location -validateSignature.noSignatures=No digital signatures found in this document -validateSignature.status.valid=Valid -validateSignature.status.invalid=Invalid -validateSignature.chain.invalid=Certificate chain validation failed - cannot verify signer's identity -validateSignature.trust.invalid=Certificate not in trust store - source cannot be verified -validateSignature.cert.expired=Certificate has expired -validateSignature.cert.revoked=Certificate has been revoked -validateSignature.signature.info=Signature Information -validateSignature.signature=Signature -validateSignature.signature.mathValid=Signature is mathematically valid BUT: -validateSignature.selectCustomCert=Custom Certificate File X.509 (Optional) -validateSignature.cert.info=Certificate Details -validateSignature.cert.issuer=Issuer -validateSignature.cert.subject=Subject -validateSignature.cert.serialNumber=Serial Number -validateSignature.cert.validFrom=Valid From -validateSignature.cert.validUntil=Valid Until -validateSignature.cert.algorithm=Algorithm -validateSignature.cert.keySize=Key Size -validateSignature.cert.version=Version -validateSignature.cert.keyUsage=Key Usage -validateSignature.cert.selfSigned=Self-Signed -validateSignature.cert.bits=bits +validateSignature.title=Перевірка підписів PDF +validateSignature.header=Перевірка цифрових підписів +validateSignature.selectPDF=Виберіть підписаний PDF-файл +validateSignature.submit=Перевірити підписи +validateSignature.results=Результаты проверки +validateSignature.status=Статус +validateSignature.signer=Підписант +validateSignature.date=Дата +validateSignature.reason=Причина +validateSignature.location=Местоположение +validateSignature.noSignatures=В цьому документі не знайдено цифрових підписів +validateSignature.status.valid=Дійна +validateSignature.status.invalid=Недійсна +validateSignature.chain.invalid=Перевірка цепочки сертифікатів не удалась - неможливо перевірити особистість підписанта +validateSignature.trust.invalid=Сертифікат відсутній у довіреному сховищі - джерело не може бути перевірено +validateSignature.cert.expired=Срок дії сертифіката істеку +validateSignature.cert.revoked=Сертифікат був отозван +validateSignature.signature.info=Інформація про підписи +validateSignature.signature=Подпись +validateSignature.signature.mathValid=Подпись математически корректна, НО: +validateSignature.selectCustomCert=Користувачський файл сертифіката X.509 (Необов'язково) +validateSignature.cert.info=Сведения про сертифікати +validateSignature.cert.issuer=Издатель +validateSignature.cert.subject=суб'єкт +validateSignature.cert.serialNumber=Серийний номер +validateSignature.cert.validFrom=Дійсний з +validateSignature.cert.validUntil=Дійсний до +validateSignature.cert.algorithm=Алгоритм +validateSignature.cert.keySize=Розмір ключа +validateSignature.cert.version=Версія +validateSignature.cert.keyUsage=Використання ключа +validateSignature.cert.selfSigned=Самоподписанный +validateSignature.cert.bits=біт From 57188e716175d4d1e8847badeeb8f89343eb53a9 Mon Sep 17 00:00:00 2001 From: Ludy Date: Tue, 11 Mar 2025 15:12:00 +0100 Subject: [PATCH 09/10] Add: unoserver and more (#3108) # Description of Changes Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#6-testing) for more details. --- .devcontainer/devcontainer.json | 31 +++++++++++--- .devcontainer/git-init.sh | 19 +++++++++ .devcontainer/init-setup.sh | 75 +++++++++++++++++++++++++++++++++ Dockerfile.dev | 37 ++++++++-------- scripts/init-setup.sh | 8 ---- 5 files changed, 137 insertions(+), 33 deletions(-) create mode 100644 .devcontainer/git-init.sh create mode 100644 .devcontainer/init-setup.sh delete mode 100644 scripts/init-setup.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1bfa99b0d..8cc06c753 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,11 +8,29 @@ // Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename. "dockerfile": "../Dockerfile.dev" }, + "runArgs": [ + "-e", + "GIT_EDITOR=code --wait", + "--security-opt", + "label=disable" + ], // Use 'forwardPorts' to make a list of ports inside the container available locally. - "appPort": [8080], + "forwardPorts": [8080, 2002, 2003], + "portsAttributes": { + "8080": { + "label": "Stirling-PDF Dev Port" + }, + "2002": { + "label": "unoserver Port" + }, + "2003": { + "label": "UnoConvert Port" + } + }, "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated", "mounts": [ - "source=logs-volume,target=/workspace/logs,type=volume" + "source=logs-volume,target=/workspace/logs,type=volume", + "source=build-volume,target=/workspace/build,type=volume" ], "workspaceFolder": "/workspace", // Configure tool-specific properties. @@ -97,18 +115,17 @@ "Oracle.oracle-java", // Oracle Java extension with additional features for Java development "streetsidesoftware.code-spell-checker", // Spell checker for code to avoid typos "vmware.vscode-boot-dev-pack", // Developer tools for Spring Boot by VMware - "vmware.vscode-spring-boot", // Spring Boot tools by VMware for enhanced Spring development "vscjava.vscode-java-pack", // Java Extension Pack with essential Java tools for VS Code - "vscjava.vscode-spring-boot-dashboard", // Spring Boot dashboard for managing and visualizing Spring Boot applications - "vscjava.vscode-spring-initializr", // Support for Spring Initializr to create new Spring projects "EditorConfig.EditorConfig", // EditorConfig support for maintaining consistent coding styles "ms-azuretools.vscode-docker", // Docker extension for Visual Studio Code - "charliermarsh.ruff" // Ruff extension for Ruff language support + "charliermarsh.ruff", // Ruff extension for Ruff language support + "github.vscode-github-actions" // GitHub Actions extension for Visual Studio Code ] } }, // Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root. "remoteUser": "devuser", "shutdownAction": "stopContainer", - "postStartCommand": "./scripts/init-setup.sh" + "initializeCommand": "bash ./.devcontainer/git-init.sh", + "postStartCommand": "./.devcontainer/init-setup.sh" } diff --git a/.devcontainer/git-init.sh b/.devcontainer/git-init.sh new file mode 100644 index 000000000..4d34efc11 --- /dev/null +++ b/.devcontainer/git-init.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +GIT_USER=$(git config --get user.name) +GIT_EMAIL=$(git config --get user.email) + +# Exit if GIT_USER or GIT_EMAIL is empty +if [ -z "$GIT_USER" ] || [ -z "$GIT_EMAIL" ]; then + echo "GIT_USER or GIT_EMAIL is not set. Exiting." + exit 1 +fi + +git config --local user.name "$GIT_USER" +git config --local user.email "$GIT_EMAIL" + +# This directory should contain custom Git hooks for the repository +# Set the path for Git hooks to /workspace/hooks +git config --local core.hooksPath '%(prefix)/workspace/hooks' +# Set the safe directory to the workspace path +git config --local --add safe.directory /workspace diff --git a/.devcontainer/init-setup.sh b/.devcontainer/init-setup.sh new file mode 100644 index 000000000..9a96131ee --- /dev/null +++ b/.devcontainer/init-setup.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -e + +# ============================================================================= +# Dev Container Initialization Script (init-setup.sh) +# +# This script runs when the Dev Container starts and provides guidance on +# how to interact with the project. It prints an ASCII logo, displays the +# current user, changes to the project root, and then shows helpful command +# instructions. +# +# Instructions for future developers: +# +# - To start the application, use: +# ./gradlew bootRun --no-daemon -Dspring-boot.run.fork=true -Dserver.address=0.0.0.0 +# +# - To run tests, use: +# ./gradlew test +# +# - To build the project, use: +# ./gradlew build +# +# - For running pre-commit hooks (if configured), use: +# pre-commit run --all-files +# +# Make sure you are in the project root directory after this script executes. +# ============================================================================= + +echo "Devcontainer started successfully!" + +VERSION=$(grep "^version =" build.gradle | awk -F'"' '{print $2}') +GRADLE_VERSION=$(gradle -version | grep "^Gradle " | awk '{print $2}') +GRADLE_PATH=$(which gradle) +JAVA_VERSION=$(java -version 2>&1 | awk -F '"' '/version/ {print $2}') +JAVA_PATH=$(which java) + +echo """ + ____ _____ ___ ____ _ ___ _ _ ____ ____ ____ _____ +/ ___|_ _|_ _| _ \| | |_ _| \ | |/ ___| | _ \| _ \| ___| +\___ \ | | | || |_) | | | || \| | | _ _____| |_) | | | | |_ + ___) || | | || _ <| |___ | || |\ | |_| |_____| __/| |_| | _| +|____/ |_| |___|_| \_\_____|___|_| \_|\____| |_| |____/|_| +""" +echo -e "Stirling-PDF Version: \e[32m$VERSION\e[0m" +echo -e "Gradle Version: \e[32m$GRADLE_VERSION\e[0m" +echo -e "Gradle Path: \e[32m$GRADLE_PATH\e[0m" +echo -e "Java Version: \e[32m$JAVA_VERSION\e[0m" +echo -e "Java Path: \e[32m$JAVA_PATH\e[0m" + +# Display current active user (for permission/debugging purposes) +echo -e "Current user: \e[32m$(whoami)\e[0m" + +# Change directory to the project root (parent directory of the script) +cd "$(dirname "$0")/.." +echo -e "Changed to project root: \e[32m$(pwd)\e[0m" + +# Display available commands for developers +echo "==================================================================" +echo "Available commands:" +echo "" +echo " To start unoserver: " +echo -e "\e[34m nohup /opt/venv/bin/unoserver --port 2003 --interface 0.0.0.0 > /tmp/unoserver.log 2>&1 &\e[0m" +echo +echo " To start the application: " +echo -e "\e[34m gradle bootRun\e[0m" +echo "" +echo " To run tests: " +echo -e "\e[34m gradle test\e[0m" +echo "" +echo " To build the project: " +echo -e "\e[34m gradle build\e[0m" +echo "" +echo " To run pre-commit hooks (if configured):" +echo -e "\e[34m pre-commit run --all-files -c .pre-commit-config.yaml\e[0m" +echo "==================================================================" diff --git a/Dockerfile.dev b/Dockerfile.dev index dc6f40d73..646bccaf0 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,53 +1,54 @@ # dockerfile.dev # Basisimage: Gradle mit JDK 17 (Debian-basiert) -FROM gradle:8.12-jdk17 +FROM gradle:8.13-jdk17 # Als Root-Benutzer arbeiten, um benötigte Pakete zu installieren USER root +# Set GRADLE_HOME und füge Gradle zum PATH hinzu +ENV GRADLE_HOME=/opt/gradle +ENV PATH="$GRADLE_HOME/bin:$PATH" + # Update und Installation zusätzlicher Pakete (Debian/Ubuntu-basiert) RUN apt-get update && apt-get install -y \ - wget \ - ca-certificates \ - tzdata \ - tini \ - bash \ - curl \ + sudo \ libreoffice \ poppler-utils \ qpdf \ +# settings.yml | tessdataDir: /usr/share/tesseract-ocr/5/tessdata tesseract-ocr \ tesseract-ocr-eng \ - fonts-dejavu \ - fonts-noto \ - python3 \ - python3-pip \ + fonts-terminus fonts-dejavu fonts-font-awesome fonts-noto fonts-noto-core fonts-noto-cjk fonts-noto-extra fonts-liberation fonts-linuxlibertine \ + python3-uno \ python3-venv \ +# ss -tln + iproute2 \ && apt-get clean && rm -rf /var/lib/apt/lists/* # Setze die Environment Variable für setuptools ENV SETUPTOOLS_USE_DISTUTILS=local # Installation der benötigten Python-Pakete -RUN python3 -m venv /opt/venv \ +RUN python3 -m venv --system-site-packages /opt/venv \ && . /opt/venv/bin/activate \ - && pip install --upgrade setuptools \ && pip install --no-cache-dir WeasyPrint pdf2image pillow unoserver opencv-python-headless pre-commit # Füge den venv-Pfad zur globalen PATH-Variable hinzu, damit die Tools verfügbar sind ENV PATH="/opt/venv/bin:$PATH" -# Erstelle notwendige Verzeichnisse und lege einen Nicht‑Root Benutzer an -RUN mkdir -p /home/devuser/{configs,logs,customFiles,pipeline/watchedFolders,pipeline/finishedFolders} \ - && adduser --disabled-password --gecos '' devuser \ - && chown -R devuser:devuser /home/devuser +COPY . /workspace -RUN mkdir -p /home/devuser/logs /workspace/logs /workspace/scripts /workspace/src/main/resources \ +RUN adduser --disabled-password --gecos '' devuser \ && chown -R devuser:devuser /home/devuser /workspace +RUN echo "devuser ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/devuser \ + && chmod 0440 /etc/sudoers.d/devuser # Setze das Arbeitsverzeichnis (wird später per Bind-Mount überschrieben) WORKDIR /workspace +RUN chmod +x /workspace/.devcontainer/git-init.sh +RUN sudo chmod +x /workspace/.devcontainer/init-setup.sh + # Wechsel zum Nicht‑Root Benutzer USER devuser diff --git a/scripts/init-setup.sh b/scripts/init-setup.sh deleted file mode 100644 index 6b7153cda..000000000 --- a/scripts/init-setup.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -set -e - -whoami - -cd "$(dirname "$0")/.." - -echo "Devcontainer started..." From 07f55c4fe063457944f9a6ff00921e594a0593ca Mon Sep 17 00:00:00 2001 From: "stirlingbot[bot]" <195170888+stirlingbot[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 14:17:43 +0000 Subject: [PATCH 10/10] :globe_with_meridians: Sync Translations + Update README Progress Table (#3157) ### Description of Changes This Pull Request was automatically generated to synchronize updates to translation files and documentation. Below are the details of the changes made: #### **1. Synchronization of Translation Files** - Updated translation files (`messages_*.properties`) to reflect changes in the reference file `messages_en_GB.properties`. - Ensured consistency and synchronization across all supported language files. - Highlighted any missing or incomplete translations. #### **2. Update README.md** - Generated the translation progress table in `README.md`. - Added a summary of the current translation status for all supported languages. - Included up-to-date statistics on translation coverage. #### **Why these changes are necessary** - Keeps translation files aligned with the latest reference updates. - Ensures the documentation reflects the current translation progress. --- Auto-generated by [create-pull-request][1]. [1]: https://github.com/peter-evans/create-pull-request Co-authored-by: stirlingbot[bot] <195170888+stirlingbot[bot]@users.noreply.github.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d6a82cd2e..4f07c2428 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Stirling-PDF currently supports 39 languages! | Azerbaijani (Azərbaycan Dili) (az_AZ) | ![87%](https://geps.dev/progress/87) | | Basque (Euskara) (eu_ES) | ![51%](https://geps.dev/progress/51) | | Bulgarian (Български) (bg_BG) | ![98%](https://geps.dev/progress/98) | -| Catalan (Català) (ca_CA) | ![79%](https://geps.dev/progress/79) | +| Catalan (Català) (ca_CA) | ![95%](https://geps.dev/progress/95) | | Croatian (Hrvatski) (hr_HR) | ![85%](https://geps.dev/progress/85) | | Czech (Česky) (cs_CZ) | ![96%](https://geps.dev/progress/96) | | Danish (Dansk) (da_DK) | ![84%](https://geps.dev/progress/84) | @@ -154,7 +154,7 @@ Stirling-PDF currently supports 39 languages! | Tibetan (བོད་ཡིག་) (zh_BO) | ![93%](https://geps.dev/progress/93) | | Traditional Chinese (繁體中文) (zh_TW) | ![99%](https://geps.dev/progress/99) | | Turkish (Türkçe) (tr_TR) | ![81%](https://geps.dev/progress/81) | -| Ukrainian (Українська) (uk_UA) | ![71%](https://geps.dev/progress/71) | +| Ukrainian (Українська) (uk_UA) | ![98%](https://geps.dev/progress/98) | | Vietnamese (Tiếng Việt) (vi_VN) | ![78%](https://geps.dev/progress/78) |