diff --git a/Dockerfile b/Dockerfile index 90807823..880d89bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,6 +36,7 @@ RUN apt-get update && \ python3-pip \ unoconv \ pngquant \ + unpaper \ ocrmypdf && \ pip install --user --upgrade ocrmypdf @@ -49,10 +50,10 @@ COPY build/libs/*.jar app.jar EXPOSE 8080 # Set environment variables -ENV LOG_LEVEL=INFO +ENV APP_NAME="Stirling PDF" # Run the application -ENTRYPOINT ["java","-jar","/app.jar","-Dlogging.level=${LOG_LEVEL}"] +ENTRYPOINT java -jar /app.jar diff --git a/build.gradle b/build.gradle index 4f5920e5..b2243347 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,10 @@ dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.20.0' + // https://mvnrepository.com/artifact/org.apache.pdfbox/jbig2-imageio + implementation group: 'org.apache.pdfbox', name: 'jbig2-imageio', version: '3.0.4' + + //general PDF implementation 'org.apache.pdfbox:pdfbox:2.0.27' diff --git a/src/main/java/stirling/software/SPDF/SPdfApplication.java b/src/main/java/stirling/software/SPDF/SPdfApplication.java index c217625a..7a0fe145 100644 --- a/src/main/java/stirling/software/SPDF/SPdfApplication.java +++ b/src/main/java/stirling/software/SPDF/SPdfApplication.java @@ -5,9 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SPdfApplication { - public static void main(String[] args) { - SpringApplication.run(SPdfApplication.class, args); + SpringApplication.run(SPdfApplication.class, args); } - -} +} \ No newline at end of file diff --git a/src/main/java/stirling/software/SPDF/config/AppConfig.java b/src/main/java/stirling/software/SPDF/config/AppConfig.java index bdea6904..431148d8 100644 --- a/src/main/java/stirling/software/SPDF/config/AppConfig.java +++ b/src/main/java/stirling/software/SPDF/config/AppConfig.java @@ -11,4 +11,12 @@ public class AppConfig { String version = getClass().getPackage().getImplementationVersion(); return (version != null) ? version : "0.3.3"; } + + @Bean(name = "appName") + public String appName() { + String appName = System.getProperty("AppName"); + if(appName == null) + appName = System.getenv("APP_NAME"); + return (appName != null) ? appName : "Stirling PDF"; + } } \ No newline at end of file diff --git a/src/main/java/stirling/software/SPDF/config/Beans.java b/src/main/java/stirling/software/SPDF/config/Beans.java index ae064351..e9cfa48c 100644 --- a/src/main/java/stirling/software/SPDF/config/Beans.java +++ b/src/main/java/stirling/software/SPDF/config/Beans.java @@ -16,7 +16,7 @@ public class Beans implements WebMvcConfigurer { @Bean public LocaleResolver localeResolver() { SessionLocaleResolver slr = new SessionLocaleResolver(); - slr.setDefaultLocale(Locale.US); + slr.setDefaultLocale(Locale.UK); return slr; } diff --git a/src/main/java/stirling/software/SPDF/controller/CompressController.java b/src/main/java/stirling/software/SPDF/controller/CompressController.java index d8028a75..b14e0721 100644 --- a/src/main/java/stirling/software/SPDF/controller/CompressController.java +++ b/src/main/java/stirling/software/SPDF/controller/CompressController.java @@ -54,6 +54,9 @@ public class CompressController { command.add("--tesseract-timeout=0"); command.add("--optimize"); command.add(String.valueOf(optimizeLevel)); + command.add("--output-type"); + command.add("pdf"); + if (fastWebView != null && fastWebView) { long fileSize = inputFile.getSize(); @@ -69,7 +72,7 @@ public class CompressController { command.add(tempInputFile.toString()); command.add(tempOutputFile.toString()); - int returnCode = ProcessExecutor.runCommandWithOutputHandling(command); + int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command); // Read the optimized PDF file byte[] pdfBytes = Files.readAllBytes(tempOutputFile); diff --git a/src/main/java/stirling/software/SPDF/controller/OCRController.java b/src/main/java/stirling/software/SPDF/controller/OCRController.java index 93e0f6eb..0e2bd04b 100644 --- a/src/main/java/stirling/software/SPDF/controller/OCRController.java +++ b/src/main/java/stirling/software/SPDF/controller/OCRController.java @@ -28,6 +28,7 @@ import org.springframework.web.servlet.ModelAndView; import stirling.software.SPDF.utils.ProcessExecutor; //import com.spire.pdf.*; +import java.util.concurrent.Semaphore; @Controller public class OCRController { @@ -41,11 +42,18 @@ public class OCRController { return modelAndView; } + private final Semaphore semaphore = new Semaphore(2); + @PostMapping("/ocr-pdf") public ResponseEntity processPdfWithOCR(@RequestParam("fileInput") MultipartFile inputFile, @RequestParam("languages") List selectedLanguages, - @RequestParam(name = "sidecar", required = false) Boolean sidecar) throws IOException, InterruptedException { + @RequestParam(name = "sidecar", required = false) Boolean sidecar, + @RequestParam(name = "deskew", required = false) Boolean deskew, + @RequestParam(name = "clean", required = false) Boolean clean, + @RequestParam(name = "clean-final", required = false) Boolean cleanFinal, + @RequestParam(name = "ocrType", required = false) String ocrType) throws IOException, InterruptedException { + //--output-type pdfa if (selectedLanguages == null || selectedLanguages.size() < 1) { throw new IOException("Please select at least one language."); @@ -58,20 +66,50 @@ public class OCRController { // Prepare the output file path Path tempOutputFile = Files.createTempFile("output_", ".pdf"); + // Prepare the output file path + Path sidecarTextPath = null; + // Run OCR Command String languageOption = String.join("+", selectedLanguages); - List command = new ArrayList<>(Arrays.asList("ocrmypdf","--verbose", "2", "--language", languageOption, - tempInputFile.toString(), tempOutputFile.toString())); - String sidecarFile = tempOutputFile.toString().replace(".pdf", ".txt"); + + List command = new ArrayList<>(Arrays.asList("ocrmypdf","--verbose", "2", "--output-type", "pdf")); + + if (sidecar != null && sidecar) { + sidecarTextPath = Files.createTempFile("sidecar", ".txt"); command.add("--sidecar"); - command.add(sidecarFile); + command.add(sidecarTextPath.toString()); } - int returnCode = ProcessExecutor.runCommandWithOutputHandling(command); + + if (deskew != null && deskew) { + command.add("--deskew"); + } + if (clean != null && clean) { + command.add("--clean"); + } + if (cleanFinal != null && cleanFinal) { + command.add("--clean-final"); + } + if (ocrType != null && !ocrType.equals("")) { + if("skip-text".equals(ocrType)) { + command.add("--skip-text"); + } else if("force-ocr".equals(ocrType)) { + command.add("--force-ocr"); + } else if("Normal".equals(ocrType)) { + + } + } + command.addAll(Arrays.asList("--language", languageOption, + tempInputFile.toString(), tempOutputFile.toString())); + + //Run CLI command + int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command); + // Read the OCR processed PDF file byte[] pdfBytes = Files.readAllBytes(tempOutputFile); - + + // Clean up the temporary files Files.delete(tempInputFile); // Return the OCR processed PDF as a response @@ -92,9 +130,9 @@ public class OCRController { zipOut.closeEntry(); // Add text file to the zip - ZipEntry txtEntry = new ZipEntry(sidecarFile); + ZipEntry txtEntry = new ZipEntry(outputFilename.replace(".pdf", ".txt")); zipOut.putNextEntry(txtEntry); - Files.copy(Paths.get(sidecarFile), zipOut); + Files.copy(sidecarTextPath, zipOut); zipOut.closeEntry(); } @@ -103,7 +141,7 @@ public class OCRController { // Clean up the temporary zip file Files.delete(tempZipFile); Files.delete(tempOutputFile); - Files.delete(Paths.get(sidecarFile)); + Files.delete(sidecarTextPath); // Return the zip file containing both the PDF and the text file headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java index 8d0d43b2..5a57cc7d 100644 --- a/src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertImgPDFController.java @@ -73,7 +73,6 @@ public class ConvertImgPDFController { if (singleImage) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.parseMediaType(getMediaType(imageFormat))); - headers.setCacheControl("must-revalidate, post-check=0, pre-check=0"); ResponseEntity response = new ResponseEntity<>(new ByteArrayResource(result), headers, HttpStatus.OK); return response; } else { diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertOfficeController.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertOfficeController.java index 708f3df2..71c8b461 100644 --- a/src/main/java/stirling/software/SPDF/controller/converters/ConvertOfficeController.java +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertOfficeController.java @@ -53,7 +53,7 @@ public byte[] convertToPdf(MultipartFile inputFile) throws IOException, Interrup "-o", tempOutputFile.toString(), tempInputFile.toString())); - int returnCode = ProcessExecutor.runCommandWithOutputHandling(command); + int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.LIBRE_OFFICE).runCommandWithOutputHandling(command); // Read the converted PDF file byte[] pdfBytes = Files.readAllBytes(tempOutputFile); diff --git a/src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToPDFA.java b/src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToPDFA.java new file mode 100644 index 00000000..9f648d4c --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/converters/ConvertPDFToPDFA.java @@ -0,0 +1,75 @@ +package stirling.software.SPDF.controller.converters; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import com.itextpdf.xmp.XMPException; + +import stirling.software.SPDF.utils.PdfUtils; +import stirling.software.SPDF.utils.ProcessExecutor; +@Controller +public class ConvertPDFToPDFA { + + @GetMapping("/pdf-to-pdfa") + public String pdfToPdfAForm(Model model) { + model.addAttribute("currentPage", "pdf-to-pdfa"); + return "convert/pdf-to-pdfa"; + } + + + @PostMapping("/pdf-to-pdfa") + public ResponseEntity pdfToPdfA( + @RequestParam("fileInput") MultipartFile inputFile) throws IOException, InterruptedException { + + + // Save the uploaded file to a temporary location + Path tempInputFile = Files.createTempFile("input_", ".pdf"); + inputFile.transferTo(tempInputFile.toFile()); + + // Prepare the output file path + Path tempOutputFile = Files.createTempFile("output_", ".pdf"); + + // Prepare the OCRmyPDF command + List command = new ArrayList<>(); + command.add("ocrmypdf"); + command.add("--skip-text"); + command.add("--tesseract-timeout=0"); + command.add("--output-type"); + command.add("pdfa"); + command.add(tempInputFile.toString()); + command.add(tempOutputFile.toString()); + + int returnCode = ProcessExecutor.getInstance(ProcessExecutor.Processes.OCR_MY_PDF).runCommandWithOutputHandling(command); + + // Read the optimized PDF file + byte[] pdfBytes = Files.readAllBytes(tempOutputFile); + + // Clean up the temporary files + Files.delete(tempInputFile); + Files.delete(tempOutputFile); + + // Return the optimized PDF as a response + String outputFilename = inputFile.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_PDFA.pdf"; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_PDF); + headers.setContentDispositionFormData("attachment", outputFilename); + return ResponseEntity.ok().headers(headers).body(pdfBytes); +} + + +} diff --git a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java index 73d597dc..70a018fa 100644 --- a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java +++ b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java @@ -1,65 +1,100 @@ package stirling.software.SPDF.utils; import java.io.BufferedReader; +import java.util.concurrent.ConcurrentHashMap; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.Semaphore; public class ProcessExecutor { - public static int runCommandWithOutputHandling(List command) throws IOException, InterruptedException { - ProcessBuilder processBuilder = new ProcessBuilder(command); - Process process = processBuilder.start(); + + public enum Processes { + LIBRE_OFFICE, + OCR_MY_PDF + } - // Read the error stream and standard output stream concurrently - List errorLines = new ArrayList<>(); - List outputLines = new ArrayList<>(); + private static final Map instances = new ConcurrentHashMap<>(); - Thread errorReaderThread = new Thread(() -> { - try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8))) { - String line; - while ((line = errorReader.readLine()) != null) { - errorLines.add(line); - } - } catch (IOException e) { - e.printStackTrace(); - } - }); - Thread outputReaderThread = new Thread(() -> { - try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { - String line; - while ((line = outputReader.readLine()) != null) { - outputLines.add(line); - } - } catch (IOException e) { - e.printStackTrace(); - } - }); + private final Semaphore semaphore; - errorReaderThread.start(); - outputReaderThread.start(); - - // Wait for the conversion process to complete - int exitCode = process.waitFor(); - - // Wait for the reader threads to finish - errorReaderThread.join(); - outputReaderThread.join(); - - if (outputLines.size() > 0) { - String outputMessage = String.join("\n", outputLines); - System.out.println("Command output:\n" + outputMessage); - } - - if (errorLines.size() > 0) { - String errorMessage = String.join("\n", errorLines); - System.out.println("Command error output:\n" + errorMessage); - if (exitCode != 0) { - throw new IOException("Command process failed with exit code " + exitCode + ". Error message: " + errorMessage); - } - } + private ProcessExecutor(int semaphoreLimit) { + this.semaphore = new Semaphore(semaphoreLimit); + } + public static ProcessExecutor getInstance(Processes processType) { + return instances.computeIfAbsent(processType, key -> { + int semaphoreLimit = switch (key) { + case LIBRE_OFFICE -> 1; + case OCR_MY_PDF -> 2; + }; + return new ProcessExecutor(semaphoreLimit); + }); + } + + public int runCommandWithOutputHandling(List command) throws IOException, InterruptedException { + int exitCode = 1; + semaphore.acquire(); + try { + + System.out.print("Running command: " + String.join(" ", command)); + ProcessBuilder processBuilder = new ProcessBuilder(command); + Process process = processBuilder.start(); + + // Read the error stream and standard output stream concurrently + List errorLines = new ArrayList<>(); + List outputLines = new ArrayList<>(); + + Thread errorReaderThread = new Thread(() -> { + try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = errorReader.readLine()) != null) { + errorLines.add(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + + Thread outputReaderThread = new Thread(() -> { + try (BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = outputReader.readLine()) != null) { + outputLines.add(line); + } + } catch (IOException e) { + e.printStackTrace(); + } + }); + + errorReaderThread.start(); + outputReaderThread.start(); + + // Wait for the conversion process to complete + exitCode = process.waitFor(); + + // Wait for the reader threads to finish + errorReaderThread.join(); + outputReaderThread.join(); + + if (outputLines.size() > 0) { + String outputMessage = String.join("\n", outputLines); + System.out.println("Command output:\n" + outputMessage); + } + + if (errorLines.size() > 0) { + String errorMessage = String.join("\n", errorLines); + System.out.println("Command error output:\n" + errorMessage); + if (exitCode != 0) { + throw new IOException("Command process failed with exit code " + exitCode + ". Error message: " + errorMessage); + } + } + } finally { + semaphore.release(); + } return exitCode; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 266a0f35..d374846f 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,12 +1,12 @@ -spring.http.multipart.max-file-size=1GB -spring.http.multipart.max-request-size=1GB +spring.http.multipart.max-file-size=2GB +spring.http.multipart.max-request-size=2GB multipart.enabled=true -multipart.max-file-size=1000MB -multipart.max-request-size=1000MB +multipart.max-file-size=2000MB +multipart.max-request-size=2000MB -spring.servlet.multipart.max-file-size=1000MB -spring.servlet.multipart.max-request-size=1000MB +spring.servlet.multipart.max-file-size=2000MB +spring.servlet.multipart.max-request-size=2000MB server.forward-headers-strategy=NATIVE diff --git a/src/main/resources/messages_ar_AR.properties b/src/main/resources/messages_ar_AR.properties index f5a38183..6c1d37fd 100644 --- a/src/main/resources/messages_ar_AR.properties +++ b/src/main/resources/messages_ar_AR.properties @@ -5,7 +5,7 @@ ########### # Generic # ########### -# the direction that the language is written (ltr = left to right, rtl = right to left) +# the direction that the language is written (ltr=left to right, rtl=right to left) language.direction=rtl pdfPrompt=اختر PDF @@ -56,8 +56,8 @@ home.addImage.desc=إضافة صورة إلى موقع معين في PDF (الع home.watermark.title=إضافة علامة مائية home.watermark.desc=أضف علامة مائية مخصصة إلى مستند PDF الخاص بك. -home.remove-watermark.title = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 -home.remove-watermark.desc = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0627\u062A \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF \u0627\u0644\u062E\u0627\u0635 \u0628\u0643. +home.remove-watermark.title=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 +home.remove-watermark.desc=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0627\u062A \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF \u0627\u0644\u062E\u0627\u0635 \u0628\u0643. home.permissions.title=تغيير الأذونات home.permissions.desc=قم بتغيير أذونات مستند PDF الخاص بك @@ -74,23 +74,23 @@ home.removePassword.desc=إزالة الحماية بكلمة مرور من مس home.compressPdfs.title=ضغط ملفات PDF home.compressPdfs.desc=ضغط ملفات PDF لتقليل حجم الملف. -home.changeMetadata.title = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629 -home.changeMetadata.desc = \u062A\u063A\u064A\u064A\u0631 / \u0625\u0632\u0627\u0644\u0629 / \u0625\u0636\u0627\u0641\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF +home.changeMetadata.title=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629 +home.changeMetadata.desc=\u062A\u063A\u064A\u064A\u0631 / \u0625\u0632\u0627\u0644\u0629 / \u0625\u0636\u0627\u0641\u0629 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u0646 \u0645\u0633\u062A\u0646\u062F PDF home.fileToPDF.title=\u062A\u062D\u0648\u064A\u0644 \u0627\u0644\u0645\u0644\u0641 \u0625\u0644\u0649 PDF home.fileToPDF.desc=\u062A\u062D\u0648\u064A\u0644 \u0623\u064A \u0645\u0644\u0641 \u062A\u0642\u0631\u064A\u0628\u0627 \u0625\u0644\u0649 PDF (DOCX \u0648PNG \u0648XLS \u0648PPT \u0648TXT \u0648\u0627\u0644\u0645\u0632\u064A\u062F) -home.ocr.title=\u062A\u0634\u063A\u064A\u0644 OCR \u0639\u0644\u0649 PDF -home.ocr.desc=\u0645\u0633\u062D \u0648\u0627\u0643\u062A\u0634\u0627\u0641 \u0627\u0644\u0646\u0635 \u0645\u0646 \u0627\u0644\u0635\u0648\u0631 \u062F\u0627\u062E\u0644 PDF \u0648\u0625\u0639\u0627\u062F\u0629 \u0625\u0636\u0627\u0641\u062A\u0647 \u0643\u0646\u0635. +home.ocr.title=\u062A\u0634\u063A\u064A\u0644 OCR \u0639\u0644\u0649 PDF \u0648 / \u0623\u0648 \u0645\u0633\u062D \u0636\u0648\u0626\u064A +home.ocr.desc=\u064A\u0642\u0648\u0645 \u0628\u0631\u0646\u0627\u0645\u062C \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0628\u0645\u0633\u062D \u0648\u0627\u0643\u062A\u0634\u0627\u0641 \u0627\u0644\u0646\u0635 \u0645\u0646 \u0627\u0644\u0635\u0648\u0631 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF \u0648\u064A\u0639\u064A\u062F \u0625\u0636\u0627\u0641\u062A\u0647 \u0643\u0646\u0635 home.extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631 home.extractImages.desc=\u064A\u0633\u062A\u062E\u0631\u062C \u062C\u0645\u064A\u0639 \u0627\u0644\u0635\u0648\u0631 \u0645\u0646 \u0645\u0644\u0641 PDF \u0648\u064A\u062D\u0641\u0638\u0647\u0627 \u0641\u064A \u0627\u0644\u0631\u0645\u0632 \u0627\u0644\u0628\u0631\u064A\u062F\u064A -navbar.settings = \u0625\u0639\u062F\u0627\u062F\u0627\u062A +navbar.settings=\u0625\u0639\u062F\u0627\u062F\u0627\u062A settings.title=\u0627\u0644\u0625\u0639\u062F\u0627\u062F\u0627\u062A -settings.update = \u0627\u0644\u062A\u062D\u062F\u064A\u062B \u0645\u062A\u0627\u062D -settings.appVersion = \u0625\u0635\u062F\u0627\u0631 \u0627\u0644\u062A\u0637\u0628\u064A\u0642: +settings.update=\u0627\u0644\u062A\u062D\u062F\u064A\u062B \u0645\u062A\u0627\u062D +settings.appVersion=\u0625\u0635\u062F\u0627\u0631 \u0627\u0644\u062A\u0637\u0628\u064A\u0642: settings.downloadOption.title=\u062A\u062D\u062F\u064A\u062F \u062E\u064A\u0627\u0631 \u0627\u0644\u062A\u0646\u0632\u064A\u0644 (\u0644\u0644\u062A\u0646\u0632\u064A\u0644\u0627\u062A \u0630\u0627\u062A \u0627\u0644\u0645\u0644\u0641 \u0627\u0644\u0648\u0627\u062D\u062F \u063A\u064A\u0631 \u0627\u0644\u0645\u0636\u063A\u0648\u0637): settings.downloadOption.1=\u0641\u062A\u062D \u0641\u064A \u0646\u0641\u0633 \u0627\u0644\u0646\u0627\u0641\u0630\u0629 settings.downloadOption.2=\u0641\u062A\u062D \u0641\u064A \u0646\u0627\u0641\u0630\u0629 \u062C\u062F\u064A\u062F\u0629 @@ -98,13 +98,21 @@ settings.downloadOption.3=\u062A\u0646\u0632\u064A\u0644 \u0627\u0644\u0645\u064 settings.zipThreshold=\u0645\u0644\u0641\u0627\u062A \u0645\u0636\u063A\u0648\u0637\u0629 \u0639\u0646\u062F \u062A\u062C\u0627\u0648\u0632 \u0639\u062F\u062F \u0627\u0644\u0645\u0644\u0641\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0645 \u062A\u0646\u0632\u064A\u0644\u0647\u0627 #OCR -OCR.title = OCR -ocr.header=OCR (\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u0623\u062D\u0631\u0641) -ocr.selectText.1=\u062A\u062D\u062F\u064A\u062F \u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u062A\u064A \u0633\u064A\u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062F\u0627\u062E\u0644 PDF (\u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u0645\u062F\u0631\u062C\u0629 \u0647\u064A \u062A\u0644\u0643 \u0627\u0644\u0645\u0643\u062A\u0634\u0641\u0629 \u062D\u0627\u0644\u064A\u0627): -ocr.selectText.2=\u0625\u0646\u062A\u0627\u062C \u0645\u0644\u0641 \u0646\u0635\u064A \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 OCR \u062C\u0646\u0628\u0627 \u0625\u0644\u0649 \u062C\u0646\u0628 \u0645\u0639 \u0645\u0644\u0641 PDF OCR'ed +ocr.title=\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 / \u062A\u0646\u0638\u064A\u0641 \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A +ocr.header=\u0645\u0633\u062D \u0627\u0644\u0645\u0633\u062D \u0627\u0644\u0636\u0648\u0626\u064A / \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 (\u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641) +ocr.selectText.1=\u062D\u062F\u062F \u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u062A\u064A \u0633\u064A\u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062F\u0627\u062E\u0644 \u0645\u0644\u0641 PDF (\u0627\u0644\u0644\u063A\u0627\u062A \u0627\u0644\u0645\u062F\u0631\u062C\u0629 \u0647\u064A \u062A\u0644\u0643 \u0627\u0644\u062A\u064A \u062A\u0645 \u0627\u0643\u062A\u0634\u0627\u0641\u0647\u0627 \u062D\u0627\u0644\u064A\u064B\u0627): +ocr.selectText.2=\u0625\u0646\u062A\u0627\u062C \u0645\u0644\u0641 \u0646\u0635\u064A \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 OCR \u0628\u062C\u0627\u0646\u0628 \u0645\u0644\u0641 PDF \u0627\u0644\u0630\u064A \u062A\u0645 \u0625\u0639\u062F\u0627\u062F\u0647 \u0628\u0648\u0627\u0633\u0637\u0629 OCR +ocr.selectText.3=\u062A\u0645 \u0645\u0633\u062D \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u0635\u062D\u064A\u062D\u0629 \u0636\u0648\u0626\u064A\u064B\u0627 \u0628\u0632\u0627\u0648\u064A\u0629 \u0645\u0646\u062D\u0631\u0641\u0629 \u0639\u0646 \u0637\u0631\u064A\u0642 \u062A\u062F\u0648\u064A\u0631\u0647\u0627 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0641\u064A \u0645\u0643\u0627\u0646\u0647\u0627 +ocr.selectText.4=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629. (\u0644\u0627 \u064A\u0648\u062C\u062F \u062A\u063A\u064A\u064A\u0631 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C) +ocr.selectText.5=\u0635\u0641\u062D\u0629 \u0646\u0638\u064A\u0641\u0629 \u060C \u0644\u0630\u0644\u0643 \u0645\u0646 \u063A\u064A\u0631 \u0627\u0644\u0645\u062D\u062A\u0645\u0644 \u0623\u0646 \u064A\u062C\u062F OCR \u0646\u0635\u064B\u0627 \u0641\u064A \u0636\u0648\u0636\u0627\u0621 \u0627\u0644\u062E\u0644\u0641\u064A\u0629 \u060C \u0648\u064A\u062D\u0627\u0641\u0638 \u0639\u0644\u0649 \u0627\u0644\u062A\u0646\u0638\u064A\u0641 \u0641\u064A \u0627\u0644\u0625\u062E\u0631\u0627\u062C. +ocr.selectText.6=\u064A\u062A\u062C\u0627\u0647\u0644 \u0627\u0644\u0635\u0641\u062D\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635 \u062A\u0641\u0627\u0639\u0644\u064A \u060C \u0641\u0642\u0637 \u0635\u0641\u062D\u0627\u062A OCRs \u0627\u0644\u062A\u064A \u0647\u064A \u0635\u0648\u0631 +ocr.selectText.7=\u0641\u0631\u0636 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u060C \u0633\u064A\u0624\u062F\u064A \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 \u0639\u0644\u0649 \u0643\u0644 \u0635\u0641\u062D\u0629 \u0625\u0644\u0649 \u0625\u0632\u0627\u0644\u0629 \u062C\u0645\u064A\u0639 \u0639\u0646\u0627\u0635\u0631 \u0627\u0644\u0646\u0635 \u0627\u0644\u0623\u0635\u0644\u064A +ocr.selectText.8=\u0639\u0627\u062F\u064A (\u062E\u0637\u0623 \u0625\u0630\u0627 \u0643\u0627\u0646 PDF \u064A\u062D\u062A\u0648\u064A \u0639\u0644\u0649 \u0646\u0635) +ocr.selectText.9=\u0625\u0639\u062F\u0627\u062F\u0627\u062A \u0625\u0636\u0627\u0641\u064A\u0629 +ocr.selectText.10=\u0648\u0636\u0639 \u0627\u0644\u062A\u0639\u0631\u0641 \u0627\u0644\u0636\u0648\u0626\u064A \u0639\u0644\u0649 \u0627\u0644\u062D\u0631\u0648\u0641 ocr.help=\u064A\u0631\u062C\u0649 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0648\u062B\u0627\u0626\u0642 \u062D\u0648\u0644 \u0643\u064A\u0641\u064A\u0629 \u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0647\u0630\u0627 \u0644\u0644\u063A\u0627\u062A \u0623\u062E\u0631\u0649 \u0648 / \u0623\u0648 \u0627\u0644\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0644\u064A\u0633 \u0641\u064A \u0639\u0627\u0645\u0644 \u0627\u0644\u0625\u0631\u0633\u0627\u0621 ocr.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0648 Tesseract \u0644 OCR. -ocr.submit = \u0645\u0639\u0627\u0644\u062C\u0629 PDF \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 OCR +ocr.submit=\u0645\u0639\u0627\u0644\u062C\u0629 PDF \u0628\u0627\u0633\u062A\u062E\u062F\u0627\u0645 OCR extractImages.title=\u0627\u0633\u062A\u062E\u0631\u0627\u062C \u0627\u0644\u0635\u0648\u0631 @@ -129,7 +137,7 @@ addImage.submit=إضافة صورة compress.title=ضغط compress.header=\u0636\u063A\u0637 PDF compress.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0644\u0636\u063A\u0637 / \u062A\u062D\u0633\u064A\u0646 PDF. -compress.selectText.1 = \u0645\u0633\u062A\u0648\u0649 \u0627\u0644\u062A\u062D\u0633\u064A\u0646: +compress.selectText.1=\u0645\u0633\u062A\u0648\u0649 \u0627\u0644\u062A\u062D\u0633\u064A\u0646: compress.selectText.2=0 (\u0628\u062F\u0648\u0646 \u062A\u062D\u0633\u064A\u0646) compress.selectText.3=1 (\u0627\u0641\u062A\u0631\u0627\u0636\u064A\u060C \u062A\u062D\u0633\u064A\u0646 \u0628\u062F\u0648\u0646 \u0641\u0642\u062F\u0627\u0646) compress.selectText.4=2 (\u062A\u062D\u0633\u064A\u0646 \u0636\u064A\u0627\u0639) @@ -194,13 +202,13 @@ imageToPDF.selectText.5=\u062A\u062D\u0648\u064A\u0644 \u0625\u0644\u0649 \u0645 pdfToImage.title=تحويل PDF إلى صورة pdfToImage.header=تحويل PDF إلى صورة pdfToImage.selectText=تنسيق الصورة -pdfToImage.singleOrMultiple = \u0646\u0648\u0639 \u0646\u062A\u064A\u062C\u0629 \u0627\u0644\u0635\u0648\u0631\u0629 -pdfToImage.single = \u0635\u0648\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0643\u0628\u064A\u0631\u0629 -pdfToImage.multi = \u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629 -pdfToImage.colorType = \u0646\u0648\u0639 \u0627\u0644\u0644\u0648\u0646 -pdfToImage.color = \u0627\u0644\u0644\u0648\u0646 -pdfToImage.grey = \u062A\u062F\u0631\u062C \u0627\u0644\u0631\u0645\u0627\u062F\u064A -pdfToImage.blackwhite = \u0623\u0628\u064A\u0636 \u0648\u0623\u0633\u0648\u062F (\u0642\u062F \u064A\u0641\u0642\u062F \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A!) +pdfToImage.singleOrMultiple=\u0646\u0648\u0639 \u0646\u062A\u064A\u062C\u0629 \u0627\u0644\u0635\u0648\u0631\u0629 +pdfToImage.single=\u0635\u0648\u0631\u0629 \u0648\u0627\u062D\u062F\u0629 \u0643\u0628\u064A\u0631\u0629 +pdfToImage.multi=\u0635\u0648\u0631 \u0645\u062A\u0639\u062F\u062F\u0629 +pdfToImage.colorType=\u0646\u0648\u0639 \u0627\u0644\u0644\u0648\u0646 +pdfToImage.color=\u0627\u0644\u0644\u0648\u0646 +pdfToImage.grey=\u062A\u062F\u0631\u062C \u0627\u0644\u0631\u0645\u0627\u062F\u064A +pdfToImage.blackwhite=\u0623\u0628\u064A\u0636 \u0648\u0623\u0633\u0648\u062F (\u0642\u062F \u064A\u0641\u0642\u062F \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A!) pdfToImage.submit=تحول #addPassword @@ -234,11 +242,11 @@ watermark.selectText.7=\u0627\u0644\u062A\u0639\u062A\u064A\u0645 (0\u066A - 100 watermark.submit=إضافة علامة مائية #remove-watermark -remove-watermark.title = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 -remove-watermark.header = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 -remove-watermark.selectText.1 = \u062D\u062F\u062F PDF \u0644\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646: -remove-watermark.selectText.2 = \u0646\u0635 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629: -remove-watermark.submit = \u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 +remove-watermark.title=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 +remove-watermark.header=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 +remove-watermark.selectText.1=\u062D\u062F\u062F PDF \u0644\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 \u0645\u0646: +remove-watermark.selectText.2=\u0646\u0635 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629: +remove-watermark.submit=\u0625\u0632\u0627\u0644\u0629 \u0627\u0644\u0639\u0644\u0627\u0645\u0629 \u0627\u0644\u0645\u0627\u0626\u064A\u0629 #Change permissions permissions.title=تغيير الأذونات @@ -263,27 +271,32 @@ removePassword.selectText.1=حدد PDF لفك التشفير removePassword.selectText.2=كلمة المرور removePassword.submit=إزالة -changeMetadata.title = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629 -changeMetadata.header = \u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629 -changeMetadata.selectText.1 = \u064A\u0631\u062C\u0649 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u062A\u063A\u064A\u0631\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u063A\u064A\u064A\u0631\u0647\u0627 -changeMetadata.selectText.2 = \u062D\u0630\u0641 \u0643\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 -changeMetadata.selectText.3 = \u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629: -changeMetadata.author = \u0627\u0644\u0645\u0624\u0644\u0641: -changeMetadata.creationDate = \u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0625\u0646\u0634\u0627\u0621 (yyyy / MM / dd HH: mm: ss): -changeMetadata.creator = \u0627\u0644\u0645\u0646\u0634\u0626: -changeMetadata.keywords = \u0627\u0644\u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629: -changeMetadata.modDate = \u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0639\u062F\u064A\u0644 (yyyy / MM / dd HH: mm: ss): -changeMetadata.producer = \u0627\u0644\u0645\u0646\u062A\u062C: -changeMetadata.subject = \u0627\u0644\u0645\u0648\u0636\u0648\u0639: -changeMetadata.title = \u0627\u0644\u0639\u0646\u0648\u0627\u0646: -changeMetadata.trapped = \u0645\u062D\u0627\u0635\u0631: -changeMetadata.selectText.4 = \u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0635\u0641\u064A\u0629 \u0623\u062E\u0631\u0649: -changeMetadata.selectText.5 = \u0625\u0636\u0627\u0641\u0629 \u0625\u062F\u062E\u0627\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u062E\u0635\u0635 -changeMetadata.submit = \u062A\u063A\u064A\u064A\u0631 +changeMetadata.title=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629 +changeMetadata.header=\u062A\u063A\u064A\u064A\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0648\u0635\u0641\u064A\u0629 +changeMetadata.selectText.1=\u064A\u0631\u062C\u0649 \u062A\u0639\u062F\u064A\u0644 \u0627\u0644\u0645\u062A\u063A\u064A\u0631\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u0631\u063A\u0628 \u0641\u064A \u062A\u063A\u064A\u064A\u0631\u0647\u0627 +changeMetadata.selectText.2=\u062D\u0630\u0641 \u0643\u0644 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 +changeMetadata.selectText.3=\u0625\u0638\u0647\u0627\u0631 \u0627\u0644\u0628\u064A\u0627\u0646\u0627\u062A \u0627\u0644\u0623\u0648\u0644\u064A\u0629 \u0627\u0644\u0645\u062E\u0635\u0635\u0629: +changeMetadata.author=\u0627\u0644\u0645\u0624\u0644\u0641: +changeMetadata.creationDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u0625\u0646\u0634\u0627\u0621 (yyyy / MM / dd HH: mm: ss): +changeMetadata.creator=\u0627\u0644\u0645\u0646\u0634\u0626: +changeMetadata.keywords=\u0627\u0644\u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0631\u0626\u064A\u0633\u064A\u0629: +changeMetadata.modDate=\u062A\u0627\u0631\u064A\u062E \u0627\u0644\u062A\u0639\u062F\u064A\u0644 (yyyy / MM / dd HH: mm: ss): +changeMetadata.producer=\u0627\u0644\u0645\u0646\u062A\u062C: +changeMetadata.subject=\u0627\u0644\u0645\u0648\u0636\u0648\u0639: +changeMetadata.title=\u0627\u0644\u0639\u0646\u0648\u0627\u0646: +changeMetadata.trapped=\u0645\u062D\u0627\u0635\u0631: +changeMetadata.selectText.4=\u0628\u064A\u0627\u0646\u0627\u062A \u0648\u0635\u0641\u064A\u0629 \u0623\u062E\u0631\u0649: +changeMetadata.selectText.5=\u0625\u0636\u0627\u0641\u0629 \u0625\u062F\u062E\u0627\u0644 \u0628\u064A\u0627\u0646\u0627\u062A \u0623\u0648\u0644\u064A\u0629 \u0645\u062E\u0635\u0635 +changeMetadata.submit=\u062A\u063A\u064A\u064A\u0631 -xlsToPdf.title = \u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF -xlsToPdf.header = \u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF -xlsToPdf.selectText.1 = \u062D\u062F\u062F \u0648\u0631\u0642\u0629 \u0625\u0643\u0633\u0644 XLS \u0623\u0648 XLSX \u0644\u0644\u062A\u062D\u0648\u064A\u0644 -xlsToPdf.convert = \u062A\u062D\u0648\u064A\u0644 \ No newline at end of file +xlsToPdf.title=\u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF +xlsToPdf.header=\u062A\u062D\u0648\u064A\u0644 Excel \u0625\u0644\u0649 PDF +xlsToPdf.selectText.1=\u062D\u062F\u062F \u0648\u0631\u0642\u0629 \u0625\u0643\u0633\u0644 XLS \u0623\u0648 XLSX \u0644\u0644\u062A\u062D\u0648\u064A\u0644 +xlsToPdf.convert=\u062A\u062D\u0648\u064A\u0644 + +pdfToPDFA.title=PDF \u0625\u0644\u0649 PDF / A +pdfToPDFA.header=PDF \u0625\u0644\u0649 PDF / A +pdfToPDFA.credit=\u062A\u0633\u062A\u062E\u062F\u0645 \u0647\u0630\u0647 \u0627\u0644\u062E\u062F\u0645\u0629 OCRmyPDF \u0644\u062A\u062D\u0648\u064A\u0644 PDF / A. +pdfToPDFA.submit=\u062A\u062D\u0648\u064A\u0644 diff --git a/src/main/resources/messages_de_DE.properties b/src/main/resources/messages_de_DE.properties index f653822e..8ad7d790 100644 --- a/src/main/resources/messages_de_DE.properties +++ b/src/main/resources/messages_de_DE.properties @@ -1,7 +1,7 @@ ########### # Generic # ########### -# the direction that the language is written (ltr = left to right, rtl = right to left) +# the direction that the language is written (ltr=left to right, rtl=right to left) language.direction=ltr pdfPrompt=PDF auswählen @@ -76,8 +76,8 @@ home.changeMetadata.desc= home.fileToPDF.title=Datei in PDF konvertieren home.fileToPDF.desc=Konvertieren Sie nahezu jede Datei in PDF (DOCX, PNG, XLS, PPT, TXT und mehr) -home.ocr.title=OCR auf PDF ausfhren -home.ocr.desc=Scannt und erkennt Text aus Bildern in einer PDF-Datei und fgt ihn erneut als Text hinzu. +home.ocr.title=Fhre OCR auf PDF- und/oder Cleanup-Scans aus +home.ocr.desc=Cleanup scannt und erkennt Text aus Bildern in einer PDF-Datei und fgt ihn erneut als Text hinzu. home.extractImages.title=Bilder extrahieren home.extractImages.desc=Extrahiert alle Bilder aus einer PDF-Datei und speichert sie als Zip-Datei @@ -93,10 +93,18 @@ settings.downloadOption.3=Datei herunterladen settings.zipThreshold=Dateien komprimieren, wenn die Anzahl der heruntergeladenen Dateien berschritten wird #OCR -ocr.title=OCR -ocr.header=OCR (Optische Zeichenerkennung) -ocr.selectText.1=Whlen Sie die Sprachen aus, die in der PDF-Datei erkannt werden sollen (die aufgelisteten sind die aktuell erkannten): -ocr.selectText.2=Textdatei mit OCR-Text neben der OCR-PDF-Datei erstellen +ocr.title=OCR / Scan-Bereinigung +ocr.header=Scans bereinigen / OCR (Optical Character Recognition) +ocr.selectText.1=Sprachen auswhlen, die im PDF erkannt werden sollen (die aufgelisteten sind die aktuell erkannten): +ocr.selectText.2=Textdatei erzeugen, die OCR-Text neben dem OCR-bearbeiteten PDF enthlt +ocr.selectText.3=Korrekte Seiten wurden in einem schiefen Winkel gescannt, indem sie wieder an ihren Platz gedreht wurden +ocr.selectText.4=Seite subern, daher ist es weniger wahrscheinlich, dass OCR Text im Hintergrundrauschen findet. (Keine Ausgangsnderung) +ocr.selectText.5=Seite subern, sodass es weniger wahrscheinlich ist, dass OCR Text im Hintergrundrauschen findet, Bereinigung der Ausgabe wird beibehalten. +ocr.selectText.6=Ignoriert Seiten mit interaktivem Text, nur OCR-Seiten, die Bilder sind +ocr.selectText.7=OCR erzwingen, OCR wird jede Seite entfernen und alle ursprnglichen Textelemente entfernen +ocr.selectText.8=Normal (Fehler, wenn PDF Text enthlt) +ocr.selectText.9=Zustzliche Einstellungen +ocr.selectText.10=OCR-Modus ocr.help=Bitte lesen Sie diese Dokumentation, um zu erfahren, wie Sie dies fr andere Sprachen verwenden und/oder nicht in Docker verwenden knnen ocr.credit=Dieser Dienst verwendet OCRmyPDF und Tesseract fr OCR. ocr.submit=PDF mit OCR verarbeiten @@ -289,7 +297,10 @@ xlsToPdf.selectText.1=XLS- oder XLSX-Excel-Tabelle zum Konvertieren ausw xlsToPdf.convert=konvertieren - +pdfToPDFA.title=PDF zu PDF/A +pdfToPDFA.header=PDF zu PDF/A +pdfToPDFA.credit=Dieser Dienst verwendet OCRmyPDF fr die PDF/A-Konvertierung +pdfToPDFA.submit=Konvertieren diff --git a/src/main/resources/messages_en_GB.properties b/src/main/resources/messages_en_GB.properties index 8a171bab..6bd28e6b 100644 --- a/src/main/resources/messages_en_GB.properties +++ b/src/main/resources/messages_en_GB.properties @@ -76,12 +76,15 @@ home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document home.fileToPDF.title=Convert file to PDF home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more) -home.ocr.title=Run OCR on PDF -home.ocr.desc=Scans and detects text from images within a PDF and re-adds it as text. +home.ocr.title=Run OCR on PDF and/or Cleanup scans +home.ocr.desc=Cleanup scans and detects text from images within a PDF and re-adds it as text. home.extractImages.title=Extract Images home.extractImages.desc=Extracts all images from a PDF and saves them to zip +home.pdfToPDFA.title=Convert PDF to PDF/A +home.pdfToPDFA.desc=Convert PDF to PDF/A for long-term storage + navbar.settings=Settings settings.title=Settings @@ -93,15 +96,28 @@ settings.downloadOption.2=Open in new window settings.downloadOption.3=Download file settings.zipThreshold=Zip files when the number of downloaded files exceeds + + + #OCR -ocr.title=OCR -ocr.header=OCR (Optical Character Recognition) +ocr.title=OCR / Scan Cleanup +ocr.header=Cleanup Scans / OCR (Optical Character Recognition) ocr.selectText.1=Select languages that are to be detected within the PDF (Ones listed are the ones currently detected): ocr.selectText.2=Produce text file containing OCR text alongside the OCR'ed PDF +ocr.selectText.3=Correct pages were scanned at a skewed angle by rotating them back into place +ocr.selectText.4=Clean page so its less likely that OCR will find text in background noise. (No output change) +ocr.selectText.5=Clean page so its less likely that OCR will find text in background noise, maintains cleanup in output. +ocr.selectText.6=Ignores pages that have interacive text on them, only OCRs pages that are images +ocr.selectText.7=Force OCR, will OCR Every page removing all original text elements +ocr.selectText.8=Normal (Will error if PDF contains text) +ocr.selectText.9=Additional Settings +ocr.selectText.10=OCR Mode ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker ocr.credit=This service uses OCRmyPDF and Tesseract for OCR. ocr.submit=Process PDF with OCR + + extractImages.title=Extract Images extractImages.header=Extract Images extractImages.selectText=Select image format to convert extracted images to @@ -286,6 +302,10 @@ xlsToPdf.convert=convert +pdfToPDFA.title=PDF To PDF/A +pdfToPDFA.header=PDF To PDF/A +pdfToPDFA.credit=This service uses OCRmyPDF for PDF/A conversion +pdfToPDFA.submit=Convert diff --git a/src/main/resources/messages_en_US.properties b/src/main/resources/messages_en_US.properties deleted file mode 100644 index 2532ca8e..00000000 --- a/src/main/resources/messages_en_US.properties +++ /dev/null @@ -1,298 +0,0 @@ -########### -# Generic # -########### -# the direction that the language is written (ltr = left to right, rtl = right to left) -language.direction=ltr - -pdfPrompt=Select PDF(s) -multiPdfPrompt=Select PDFs (2+) -multiPdfDropPrompt=Select (or drag & drop) all PDFs you require -imgPrompt=Select Image(s) -genericSubmit=Submit -processTimeWarning=Warning: This process can take up to a minute depending on file-size -pageOrderPrompt=Page Order (Enter a comma-separated list of page numbers) : -goToPage=Go -true=True -false=False -unknown=Unknown -save=Save -close=Close - -############# -# HOME-PAGE # -############# -home.desc=Your locally hosted one-stop-shop for all your PDF needs. - -navbar.convert=Convert -navbar.security=Security -navbar.other=Other -navbar.darkmode=Dark Mode - -home.merge.title=Merge PDFs -home.merge.desc=Easily merge multiple PDFs into one. - -home.split.title=Split PDFs -home.split.desc=Split PDFs into multiple documents - -home.rotate.title=Rotate PDFs -home.rotate.desc=Easily rotate your PDFs. - -home.imageToPdf.title=Image to PDF -home.imageToPdf.desc=Convert a images (PNG, JPEG, GIF) to PDF. - -home.pdfToImage.title=PDF to Image -home.pdfToImage.desc=Convert a PDF to a image. (PNG, JPEG, GIF) - -home.pdfOrganiser.title=PDF Organizer -home.pdfOrganiser.desc=Remove/Rearrange pages in any order - -home.addImage.title=Add image onto PDF -home.addImage.desc=Adds a image onto a set location on the PDF (Work in progress) - -home.watermark.title=Add Watermark -home.watermark.desc=Add a custom watermark to your PDF document. - -home.remove-watermark.title=Remove Watermark -home.remove-watermark.desc=Remove watermarks from your PDF document. - -home.permissions.title=Change Permissions -home.permissions.desc=Change the permissions of your PDF document - -home.removePages.title=Remove Pages -home.removePages.desc=Delete unwanted pages from your PDF document. - -home.addPassword.title=Add Password -home.addPassword.desc=Encrypt your PDF document with a password. - -home.removePassword.title=Remove Password -home.removePassword.desc=Remove password protection from your PDF document. - -home.compressPdfs.title=Compress PDFs -home.compressPdfs.desc=Compress PDFs to reduce their file size. - -home.changeMetadata.title=Change Metadata -home.changeMetadata.desc=Change/Remove/Add metadata from a PDF document - -home.fileToPDF.title=Convert file to PDF -home.fileToPDF.desc=Convert nearly any file to PDF (DOCX, PNG, XLS, PPT, TXT and more) - -home.ocr.title=Run OCR on PDF -home.ocr.desc=Scans and detects text from images within a PDF and re-adds it as text. - -home.extractImages.title=Extract Images -home.extractImages.desc=Extracts all images from a PDF and saves them to zip - - -navbar.settings=Settings -settings.title=Settings -settings.update=Update available -settings.appVersion=App Version: -settings.downloadOption.title=Choose download option (For single file non zip downloads): -settings.downloadOption.1=Open in same window -settings.downloadOption.2=Open in new window -settings.downloadOption.3=Download file -settings.zipThreshold=Zip files when the number of downloaded files exceeds - -#OCR -ocr.title=OCR -ocr.header=OCR (Optical Character Recognition) -ocr.selectText.1=Select languages that are to be detected within the PDF (Ones listed are the ones currently detected): -ocr.selectText.2=Produce text file containing OCR text alongside the OCR'ed PDF -ocr.help=Please read this documentation on how to use this for other languages and/or use not in docker -ocr.credit=This service uses OCRmyPDF and Tesseract for OCR. -ocr.submit=Process PDF with OCR - -extractImages.title=Extract Images -extractImages.header=Extract Images -extractImages.selectText=Select image format to convert extracted images to -extractImages.submit=Extract - - -#File to PDF -fileToPDF.title=File to PDF -fileToPDF.header=Convert any file to PDF -fileToPDF.credit=This service uses LibreOffice and Unoconv for file conversion. -fileToPDF.supportedFileTypes=Supported file types should include the below however for a full updated list of supported formats, please refer to the LibreOffice documentation -fileToPDF.submit=Convert to PDF - - -#Add image -addImage.title=Add Image -addImage.header=Add image to PDF (Work in progress) -addImage.submit=Add image - -#compress -compress.title=Compress -compress.header=Compress PDF -compress.credit=This service uses OCRmyPDF for PDF Compress/Optimisation. -compress.selectText.1=Optimization level: -compress.selectText.2=0 (No optimization) -compress.selectText.3=1 (Default, lossless optimization) -compress.selectText.4=2 (Lossy optimization) -compress.selectText.5=3 (Lossy optimization, more aggressive) -compress.selectText.6=Enable fast web view (linearize PDF) -compress.selectText.7=Enable lossy JBIG2 encoding -compress.submit=Compress - - -#merge -merge.title=Merge -merge.header=Merge multiple PDFs (2+) -merge.submit=Merge - -#pdfOrganiser -pdfOrganiser.title=Page Organizer -pdfOrganiser.header=PDF Page Organizer -pdfOrganiser.submit=Rearrange Pages - - -#pageRemover -pageRemover.title=Page Remover -pageRemover.header=PDF Page remover -pageRemover.pagesToDelete=Pages to delete (Enter a comma-separated list of page numbers) : -pageRemover.submit=Delete Pages - -#rotate -rotate.title=Rotate PDF -rotate.header=Rotate PDF -rotate.selectAngle=Select rotation angle (in multiples of 90 degrees): -rotate.submit=Rotate - - - - -#merge -split.title=Split PDF -split.header=Split PDF -split.desc.1=The numbers you select are the page number you wish to do a split on -split.desc.2=As such selecting 1,3,7-8 would split a 10 page document into 6 separate PDFS with: -split.desc.3=Document #1: Page 1 -split.desc.4=Document #2: Page 2 and 3 -split.desc.5=Document #3: Page 4, 5 and 6 -split.desc.6=Document #4: Page 7 -split.desc.7=Document #5: Page 8 -split.desc.8=Document #6: Page 9 and 10 -split.splitPages=Enter pages to split on: -split.submit=Split - - -#merge -imageToPDF.title=Image to PDF -imageToPDF.header=Image to PDF -imageToPDF.submit=Convert -imageToPDF.selectText.1=Stretch to fit -imageToPDF.selectText.2=Auto rotate PDF -imageToPDF.selectText.3=Multi file logic (Only enabled if working with multiple images) -imageToPDF.selectText.4=Merge into single PDF -imageToPDF.selectText.5=Convert to separate PDFs - -#pdfToImage -pdfToImage.title=PDF to Image -pdfToImage.header=PDF to Image -pdfToImage.selectText=Image Format -pdfToImage.singleOrMultiple=Image result type -pdfToImage.single=Single Big Image -pdfToImage.multi=Multiple Images -pdfToImage.colorType=Color type -pdfToImage.color=Color -pdfToImage.grey=Grayscale -pdfToImage.blackwhite=Black and White (May lose data!) -pdfToImage.submit=Convert - - -#addPassword -addPassword.title=Add Password -addPassword.header=Add password (Encrypt) -addPassword.selectText.1=Select PDF to encrypt -addPassword.selectText.2=Password -addPassword.selectText.3=Encryption Key Length -addPassword.selectText.4=Higher values are stronger, but lower values have better compatibility. -addPassword.selectText.5=Permissions to set -addPassword.selectText.6=Prevent assembly of document -addPassword.selectText.7=Prevent content extraction -addPassword.selectText.8=Prevent extraction for accessibility -addPassword.selectText.9=Prevent filling in form -addPassword.selectText.10=Prevent modification -addPassword.selectText.11=Prevent annotation modification -addPassword.selectText.12=Prevent printing -addPassword.selectText.13=Prevent printing different formats -addPassword.submit=Encrypt - -#watermark -watermark.title=Add Watermark -watermark.header=Add Watermark -watermark.selectText.1=Select PDF to add watermark to: -watermark.selectText.2=Watermark Text: -watermark.selectText.3=Font Size: -watermark.selectText.4=Rotation (0-360): -watermark.selectText.5=widthSpacer (Space between each watermark horizontally): -watermark.selectText.6=heightSpacer (Space between each watermark vertically): -watermark.selectText.7=Opacity (0% - 100%): -watermark.submit=Add Watermark - -#remove-watermark -remove-watermark.title=Remove Watermark -remove-watermark.header=Remove Watermark -remove-watermark.selectText.1=Select PDF to remove watermark from: -remove-watermark.selectText.2=Watermark Text: -remove-watermark.submit=Remove Watermark - -#Change permissions -permissions.title=Change Permissions -permissions.header=Change Permissions -permissions.warning=Warning to have these permissions be unchangeable it is recommended to set them with a password via the add-password page -permissions.selectText.1=Select PDF to change permissions -permissions.selectText.2=Permissions to set -permissions.selectText.3=Prevent assembly of document -permissions.selectText.4=Prevent content extraction -permissions.selectText.5=Prevent extraction for accessibility -permissions.selectText.6=Prevent filling in form -permissions.selectText.7=Prevent modification -permissions.selectText.8=Prevent annotation modification -permissions.selectText.9=Prevent printing -permissions.selectText.10=Prevent printing different formats -permissions.submit=Change - -#remove password -removePassword.title=Remove password -removePassword.header=Remove password (Decrypt) -removePassword.selectText.1=Select PDF to Decrypt -removePassword.selectText.2=Password -removePassword.submit=Remove - -changeMetadata.title=Change Metadata -changeMetadata.header=Change Metadata -changeMetadata.selectText.1=Please edit the variables you wish to change -changeMetadata.selectText.2=Delete all metadata -changeMetadata.selectText.3=Show Custom Metadata: -changeMetadata.author=Author: -changeMetadata.creationDate=Creation Date (yyyy/MM/dd HH:mm:ss): -changeMetadata.creator=Creator: -changeMetadata.keywords=Keywords: -changeMetadata.modDate=Modification Date (yyyy/MM/dd HH:mm:ss): -changeMetadata.producer=Producer: -changeMetadata.subject=Subject: -changeMetadata.title=Title: -changeMetadata.trapped=Trapped: -changeMetadata.selectText.4=Other Metadata: -changeMetadata.selectText.5=Add Custom Metadata Entry -changeMetadata.submit=Change - - -fileToPDF.credit=This service uses LibreOffice and Unoconv for file conversion. -fileToPDF.supportedFileTypes=Supported file types should include the below however for a full updated list of supported formats, please refer to the LibreOffice documentation - - - - - - - - - - - - - - - diff --git a/src/main/resources/messages_fr_FR.properties b/src/main/resources/messages_fr_FR.properties index 1b3e7652..7ecb0fd1 100644 --- a/src/main/resources/messages_fr_FR.properties +++ b/src/main/resources/messages_fr_FR.properties @@ -5,7 +5,7 @@ ########### # Generic # ########### -# the direction that the language is written (ltr = left to right, rtl = right to left) +# the direction that the language is written (ltr=left to right, rtl=right to left) language.direction=ltr pdfPrompt=Choisir PDF @@ -81,8 +81,8 @@ home.changeMetadata.desc=Modifier/Supprimer/Ajouter des m home.fileToPDF.title=Convertir un fichier en PDF home.fileToPDF.desc=Convertissez presque n\u2019importe quel fichier en PDF (DOCX, PNG, XLS, PPT, TXT et plus) -home.ocr.title=Excuter OCR sur PDF -home.ocr.desc=Analyse et dtecte le texte des images d\u2019un fichier PDF et le rajoute en tant que texte. +home.ocr.title=Excuter l'OCR sur les scans PDF et/ou de nettoyage +home.ocr.desc=Le nettoyage analyse et dtecte le texte des images dans un PDF et le rajoute en tant que texte. home.extractImages.title=Extraire les images home.extractImages.desc=Extrait toutes les images d\u2019un PDF et les enregistre au format zip @@ -100,10 +100,18 @@ settings.zipThreshold=Zip les fichiers lorsque le nombre de fichiers t #OCR -ocr.title=OCR -ocr.header=OCR (reconnaissance optique de caractres) -ocr.selectText.1=Slectionnez les langues dtecter dans le fichier PDF (celles rpertories sont celles actuellement dtectes) : -ocr.selectText.2=Produire un fichier texte contenant du texte OCR ct du PDF OCR +ocr.title=OCR / Nettoyage de numrisation +ocr.header=Nettoyage des scans / OCR (reconnaissance optique des caractres) +ocr.selectText.1=Slectionnez les langues dtecter dans le PDF (celles rpertories sont celles actuellement dtectes): +ocr.selectText.2=Produire un fichier texte contenant du texte OCR avec le PDF OCR +ocr.selectText.3=Les pages correctes ont t numrises un angle oblique en les remettant en place +ocr.selectText.4=Nettoyer la page pour qu'il soit moins probable que l'OCR trouve du texte dans le bruit de fond. (Pas de changement de sortie) +ocr.selectText.5=Nettoyer la page afin qu'il soit moins probable que l'OCR trouve du texte dans le bruit de fond, maintient le nettoyage dans la sortie. +ocr.selectText.6=Ignore les pages contenant du texte interactif, seulement les pages OCR qui sont des images +ocr.selectText.7=Forcer l'OCR, OCR chaque page supprimera tous les lments de texte d'origine +ocr.selectText.8=Normal (Erreur si le PDF contient du texte) +ocr.selectText.9=Paramtres supplmentaires +ocr.selectText.10=Mode ROC ocr.help=Veuillez lire cette documentation pour savoir comment l\u2019utiliser pour d\u2019autres langues et/ou une utilisation non dans docker ocr.credit=Ce service utilise OCRmyPDF et Tesseract pour l\u2019OCR. ocr.submit=Traiter PDF avec OCR @@ -292,3 +300,7 @@ xlsToPdf.header=Excel en PDF xlsToPdf.selectText.1=Slectionnez une feuille Excel XLS ou XLSX convertir xlsToPdf.convert=convertir +pdfToPDFA.title=PDF vers PDF/A +pdfToPDFA.header=PDF vers PDF/A +pdfToPDFA.credit=Ce service utilise OCRmyPDF pour la conversion PDF/A +pdfToPDFA.submit=Convertir \ No newline at end of file diff --git a/src/main/resources/templates/convert/pdf-to-pdfa.html b/src/main/resources/templates/convert/pdf-to-pdfa.html new file mode 100644 index 00000000..4f2c03b8 --- /dev/null +++ b/src/main/resources/templates/convert/pdf-to-pdfa.html @@ -0,0 +1,31 @@ + + + + + + + +
+
+
+

+
+
+
+

+

Currently doesn't work for multiple inputs at once

+
+
+
+ +
+

+
+
+
+ +
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/common.html b/src/main/resources/templates/fragments/common.html index 3279469e..ab7dc2ed 100644 --- a/src/main/resources/templates/fragments/common.html +++ b/src/main/resources/templates/fragments/common.html @@ -3,7 +3,7 @@ - + @@ -194,98 +194,106 @@ function toggleDarkMode() { } }); - async function submitMultiPdfForm(event,url) { + async function submitMultiPdfForm(event, url) { // Get the selected PDF files - var files = $('#fileInput-input')[0].files; + let files = $('#fileInput-input')[0].files; // Get the existing form data - var formData = new FormData($('form')[0]); + let formData = new FormData($('form')[0]); formData.delete('fileInput'); - + // Show the progress bar $('#progressBarContainer').show(); // Initialize the progress bar - var progressBar = $('#progressBar'); + let progressBar = $('#progressBar'); progressBar.css('width', '0%'); progressBar.attr('aria-valuenow', 0); progressBar.attr('aria-valuemax', files.length); - - // Check the flag in localStorage, default to 4 + + // Check the flag in localStorage, default to 4 const zipThreshold = parseInt(localStorage.getItem('zipThreshold'), 10) || 4; const zipFiles = files.length > zipThreshold; // Initialize JSZip instance if needed let jszip = null; if (zipFiles) { - jszip = new JSZip(); + jszip = new JSZip(); } - - + // Submit each PDF file in parallel - var promises = []; - for (var i = 0; i < files.length; i++) { - var promise = new Promise(function(resolve, reject) { - var fileFormData = new FormData(); + let promises = []; + for (let i = 0; i < files.length; i++) { + let promise = new Promise(async function(resolve, reject) { + let fileFormData = new FormData(); fileFormData.append('fileInput', files[i]); - for (var pair of formData.entries()) { + for (let pair of formData.entries()) { fileFormData.append(pair[0], pair[1]); } console.log(fileFormData); - fetch(url, { - method: 'POST', - body: fileFormData - }).then(function(response) { + try { + let response = await fetch(url, { + method: 'POST', + body: fileFormData + }); + if (!response) { throw new Error('Received null response for file ' + i); } + + if (!response.ok) { + throw new Error(`Error submitting request for file ${i}: ${response.status} ${response.statusText}`); + } + + let contentDisposition = response.headers.get('content-disposition'); + let fileName = "file.pdf" + if (!contentDisposition) { + //throw new Error('Content-Disposition header not found for file ' + i); + } else { + fileName = contentDisposition.split('filename=')[1].replace(/"/g, ''); + } console.log('Received response for file ' + i + ': ' + response); - var contentDisposition = response.headers.get('content-disposition'); - var fileName = contentDisposition.split('filename=')[1].replace(/"/g, ''); + - response.blob().then(function (blob) { - if (zipFiles) { - // Add the file to the ZIP archive - jszip.file(fileName, blob); - resolve(); - } else { - // Download the file directly - var url = window.URL.createObjectURL(blob); - var a = document.createElement('a'); - a.href = url; - a.download = fileName; - document.body.appendChild(a); - a.click(); - a.remove(); - resolve(); - } - }); - - }).catch(function(error) { + let blob = await response.blob(); + if (zipFiles) { + // Add the file to the ZIP archive + jszip.file(fileName, blob); + resolve(); + } else { + // Download the file directly + let url = window.URL.createObjectURL(blob); + let a = document.createElement('a'); + a.href = url; + a.download = fileName; + document.body.appendChild(a); + a.click(); + a.remove(); + resolve(); + } + } catch (error) { console.error('Error submitting request for file ' + i + ': ' + error); - + // Set default values or fallbacks for error properties - var status = error && error.status || 500; - var statusText = error && error.statusText || 'Internal Server Error'; - var message = error && error.message || 'An error occurred while processing your request.'; - - // Reject the Promise to signal that the request has failed + let status = error && error.status || 500; + let statusText = error && error.statusText || 'Internal Server Error'; + let message = error && error.message || 'An error occurred while processing your request.'; + + // Reject the Promise to signal that the request has failed reject(); - // Redirect to error page with Spring Boot error parameters - var url = '/error?status=' + status + '&error=' + encodeURIComponent(statusText) + '&message=' + encodeURIComponent(message); + // Redirect to error page with Spring Boot error parameters + let url = '/error?status=' + status + '&error=' + encodeURIComponent(statusText) + '&message=' + encodeURIComponent(message); window.location.href = url; - }); + } }); - + // Update the progress bar as each request finishes promise.then(function() { - var progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length); - progressBar.css('width', progress + '%'); - progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1); + updateProgressBar(progressBar, files); }); - + promises.push(promise); } @@ -295,24 +303,33 @@ function toggleDarkMode() { } catch (error) { console.error('Error while uploading files: ' + error); } - + // Update the progress bar progressBar.css('width', '100%'); progressBar.attr('aria-valuenow', files.length); - - // After all requests are finished, download the ZIP file if needed + + // After all requests are finished, download the ZIP file if needed if (zipFiles) { - jszip.generateAsync({ type: "blob" }).then(function (content) { - var url = window.URL.createObjectURL(content); - var a = document.createElement('a'); - a.href = url; - a.download = "files.zip"; - document.body.appendChild(a); - a.click(); - a.remove(); - }); + try { + let content = await jszip.generateAsync({ type: "blob" }); + let url = window.URL.createObjectURL(content); + let a = document.createElement('a'); + a.href = url; + a.download = "files.zip"; + document.body.appendChild(a); + a.click(); + a.remove(); + } catch (error) { + console.error('Error generating ZIP file: ' + error); + } } } + function updateProgressBar(progressBar, files) { + let progress = ((progressBar.attr('aria-valuenow') / files.length) * 100) + (100 / files.length); + progressBar.css('width', progress + '%'); + progressBar.attr('aria-valuenow', parseInt(progressBar.attr('aria-valuenow')) + 1); + } + diff --git a/src/main/resources/templates/fragments/footer.html b/src/main/resources/templates/fragments/footer.html index 35ecf894..d2d03228 100644 --- a/src/main/resources/templates/fragments/footer.html +++ b/src/main/resources/templates/fragments/footer.html @@ -1,7 +1,8 @@
-
- - - -
+
+ + + +
Powered by Stirling PDF
+
\ No newline at end of file diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html index c6f95372..b4794c69 100644 --- a/src/main/resources/templates/fragments/navbar.html +++ b/src/main/resources/templates/fragments/navbar.html @@ -109,7 +109,7 @@ function compareVersions(version1, version2) {