diff --git a/README.md b/README.md index af95826ec..d8164c136 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ Stirling-PDF currently supports 36 languages! | Spanish (Español) (es_ES) |  | | Swedish (Svenska) (sv_SE) |  | | Thai (ไทย) (th_TH) |  | -| Traditional Chinese (繁體中文) (zh_TW) |  | +| Traditional Chinese (繁體中文) (zh_TW) |  | | Turkish (Türkçe) (tr_TR) |  | | Ukrainian (Українська) (uk_UA) |  | | Vietnamese (Tiếng Việt) (vi_VN) |  | diff --git a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java index 16397965a..83df30ae0 100644 --- a/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java +++ b/src/main/java/stirling/software/SPDF/model/ApplicationProperties.java @@ -47,6 +47,7 @@ public class ApplicationProperties { private AutomaticallyGenerated automaticallyGenerated = new AutomaticallyGenerated(); private EnterpriseEdition enterpriseEdition = new EnterpriseEdition(); private AutoPipeline autoPipeline = new AutoPipeline(); + private ProcessExecutor processExecutor = new ProcessExecutor(); @Data public static class AutoPipeline { @@ -309,4 +310,98 @@ public class ApplicationProperties { } } } + + @Data + public static class ProcessExecutor { + private SessionLimit sessionLimit = new SessionLimit(); + private TimeoutMinutes timeoutMinutes = new TimeoutMinutes(); + + @Data + public static class SessionLimit { + private int libreOfficeSessionLimit; + private int pdfToHtmlSessionLimit; + private int ocrMyPdfSessionLimit; + private int pythonOpenCvSessionLimit; + private int ghostScriptSessionLimit; + private int weasyPrintSessionLimit; + private int installAppSessionLimit; + private int calibreSessionLimit; + + public int getLibreOfficeSessionLimit() { + return libreOfficeSessionLimit > 0 ? libreOfficeSessionLimit : 1; + } + + public int getPdfToHtmlSessionLimit() { + return pdfToHtmlSessionLimit > 0 ? pdfToHtmlSessionLimit : 1; + } + + public int getOcrMyPdfSessionLimit() { + return ocrMyPdfSessionLimit > 0 ? ocrMyPdfSessionLimit : 2; + } + + public int getPythonOpenCvSessionLimit() { + return pythonOpenCvSessionLimit > 0 ? pythonOpenCvSessionLimit : 8; + } + + public int getGhostScriptSessionLimit() { + return ghostScriptSessionLimit > 0 ? ghostScriptSessionLimit : 16; + } + + public int getWeasyPrintSessionLimit() { + return weasyPrintSessionLimit > 0 ? weasyPrintSessionLimit : 16; + } + + public int getInstallAppSessionLimit() { + return installAppSessionLimit > 0 ? installAppSessionLimit : 1; + } + + public int getCalibreSessionLimit() { + return calibreSessionLimit > 0 ? calibreSessionLimit : 1; + } + } + + @Data + public static class TimeoutMinutes { + private long libreOfficeTimeoutMinutes; + private long pdfToHtmlTimeoutMinutes; + private long ocrMyPdfTimeoutMinutes; + private long pythonOpenCvTimeoutMinutes; + private long ghostScriptTimeoutMinutes; + private long weasyPrintTimeoutMinutes; + private long installAppTimeoutMinutes; + private long calibreTimeoutMinutes; + + public long getLibreOfficeTimeoutMinutes() { + return libreOfficeTimeoutMinutes > 0 ? libreOfficeTimeoutMinutes : 30; + } + + public long getPdfToHtmlTimeoutMinutes() { + return pdfToHtmlTimeoutMinutes > 0 ? pdfToHtmlTimeoutMinutes : 20; + } + + public long getOcrMyPdfTimeoutMinutes() { + return ocrMyPdfTimeoutMinutes > 0 ? ocrMyPdfTimeoutMinutes : 30; + } + + public long getPythonOpenCvTimeoutMinutes() { + return pythonOpenCvTimeoutMinutes > 0 ? pythonOpenCvTimeoutMinutes : 30; + } + + public long getGhostScriptTimeoutMinutes() { + return ghostScriptTimeoutMinutes > 0 ? ghostScriptTimeoutMinutes : 30; + } + + public long getWeasyPrintTimeoutMinutes() { + return weasyPrintTimeoutMinutes > 0 ? weasyPrintTimeoutMinutes : 30; + } + + public long getInstallAppTimeoutMinutes() { + return installAppTimeoutMinutes > 0 ? installAppTimeoutMinutes : 60; + } + + public long getCalibreTimeoutMinutes() { + return calibreTimeoutMinutes > 0 ? calibreTimeoutMinutes : 30; + } + } + } } diff --git a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java index 5055c14c7..0947714fd 100644 --- a/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java +++ b/src/main/java/stirling/software/SPDF/utils/ProcessExecutor.java @@ -18,10 +18,14 @@ import org.slf4j.LoggerFactory; import io.github.pixee.security.BoundedLineReader; +import stirling.software.SPDF.model.ApplicationProperties; + public class ProcessExecutor { private static final Logger logger = LoggerFactory.getLogger(ProcessExecutor.class); + private static ApplicationProperties applicationProperties = new ApplicationProperties(); + public enum Processes { LIBRE_OFFICE, PDFTOHTML, @@ -45,26 +49,90 @@ public class ProcessExecutor { key -> { int semaphoreLimit = switch (key) { - case LIBRE_OFFICE -> 1; - case PDFTOHTML -> 1; - case OCR_MY_PDF -> 2; - case PYTHON_OPENCV -> 8; - case GHOSTSCRIPT -> 16; - case WEASYPRINT -> 16; - case INSTALL_APP -> 1; - case CALIBRE -> 1; + case LIBRE_OFFICE -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getLibreOfficeSessionLimit(); + case PDFTOHTML -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getPdfToHtmlSessionLimit(); + case OCR_MY_PDF -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getOcrMyPdfSessionLimit(); + case PYTHON_OPENCV -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getPythonOpenCvSessionLimit(); + case GHOSTSCRIPT -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getGhostScriptSessionLimit(); + case WEASYPRINT -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getWeasyPrintSessionLimit(); + case INSTALL_APP -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getInstallAppSessionLimit(); + case CALIBRE -> + applicationProperties + .getProcessExecutor() + .getSessionLimit() + .getCalibreSessionLimit(); }; long timeoutMinutes = switch (key) { - case LIBRE_OFFICE -> 30; - case PDFTOHTML -> 20; - case OCR_MY_PDF -> 30; - case PYTHON_OPENCV -> 30; - case GHOSTSCRIPT -> 30; - case WEASYPRINT -> 30; - case INSTALL_APP -> 60; - case CALIBRE -> 30; + case LIBRE_OFFICE -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getLibreOfficeTimeoutMinutes(); + case PDFTOHTML -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getPdfToHtmlTimeoutMinutes(); + case OCR_MY_PDF -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getOcrMyPdfTimeoutMinutes(); + case PYTHON_OPENCV -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getPythonOpenCvTimeoutMinutes(); + case GHOSTSCRIPT -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getGhostScriptTimeoutMinutes(); + case WEASYPRINT -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getWeasyPrintTimeoutMinutes(); + case INSTALL_APP -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getInstallAppTimeoutMinutes(); + case CALIBRE -> + applicationProperties + .getProcessExecutor() + .getTimeoutMinutes() + .getCalibreTimeoutMinutes(); }; return new ProcessExecutor(semaphoreLimit, liveUpdates, timeoutMinutes); }); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 0f957c0a9..fdfb603c1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -50,4 +50,4 @@ springdoc.swagger-ui.url=/v1/api-docs posthog.api.key=phc_fiR65u5j6qmXTYL56MNrLZSWqLaDW74OrZH0Insd2xq -posthog.host=https://eu.i.posthog.com \ No newline at end of file +posthog.host=https://eu.i.posthog.com diff --git a/src/main/resources/messages_zh_TW.properties b/src/main/resources/messages_zh_TW.properties index 98d729026..55a049ed8 100644 --- a/src/main/resources/messages_zh_TW.properties +++ b/src/main/resources/messages_zh_TW.properties @@ -79,8 +79,8 @@ info=資訊 pro=專業版 page=頁面 pages=頁面 -loading=Loading... -addToDoc=Add to Document +loading=載入中... +addToDoc=新增至文件 legal.privacy=隱私權政策 legal.terms=使用條款 @@ -140,7 +140,7 @@ navbar.darkmode=深色模式 navbar.language=語言 navbar.settings=設定 navbar.allTools=工具 -navbar.multiTool=多功能工具 +navbar.multiTool=複合工具 navbar.sections.organize=整理 navbar.sections.convertTo=轉換為 PDF navbar.sections.convertFrom=從 PDF 轉換 @@ -246,7 +246,7 @@ database.fileNullOrEmpty=檔案不得為空或空白 database.failedImportFile=匯入檔案失敗 session.expired=您的工作階段已過期。請重新整理頁面並再試一次。 -session.refreshPage=Refresh Page +session.refreshPage=重新整理頁面 ############# # HOME-PAGE # @@ -751,7 +751,7 @@ certSign.showSig=顯示簽章 certSign.reason=原因 certSign.location=位置 certSign.name=名稱 -certSign.showLogo=Show Logo +certSign.showLogo=顯示 Logo certSign.submit=簽章 PDF @@ -786,9 +786,9 @@ 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 進行比較 #BookToPDF BookToPDF.title=電子書和漫畫轉 PDF @@ -805,17 +805,17 @@ PDFToBook.submit=轉換 #sign sign.title=簽章 -sign.header=簽章 PDF +sign.header=簽署 PDF sign.upload=上傳影像 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.saved=已儲存的簽章 +sign.save=儲存簽章 +sign.personalSigs=個人簽章 +sign.sharedSigs=共用簽章 +sign.noSavedSigs=尚未儲存任何簽章 #repair diff --git a/src/main/resources/settings.yml.template b/src/main/resources/settings.yml.template index 84f628ba6..d9971d0c8 100644 --- a/src/main/resources/settings.yml.template +++ b/src/main/resources/settings.yml.template @@ -102,3 +102,22 @@ metrics: AutomaticallyGenerated: key: example UUID: example + +processExecutor: + sessionLimit: # Process executor instances limits + libreOfficeSessionLimit: 1 + pdfToHtmlSessionLimit: 1 + ocrMyPdfSessionLimit: 2 + pythonOpenCvSessionLimit: 8 + ghostScriptSessionLimit: 16 + weasyPrintSessionLimit: 16 + installAppSessionLimit: 1 + calibreSessionLimit: 1 + timeoutMinutes: # Process executor timeout in minutes + libreOfficetimeoutMinutes: 30 + pdfToHtmltimeoutMinutes: 20 + pythonOpenCvtimeoutMinutes: 30 + ghostScripttimeoutMinutes: 30 + weasyPrinttimeoutMinutes: 30 + installApptimeoutMinutes: 60 + calibretimeoutMinutes: 30 diff --git a/src/main/resources/static/js/merge.js b/src/main/resources/static/js/merge.js index 41d1fe1eb..2c9a39397 100644 --- a/src/main/resources/static/js/merge.js +++ b/src/main/resources/static/js/merge.js @@ -21,27 +21,55 @@ async function displayFiles(files) { for (let i = 0; i < files.length; i++) { const pageCount = await getPDFPageCount(files[i]); const pageLabel = pageCount === 1 ? pageTranslation : pagesTranslation; + + // Create list item const item = document.createElement("li"); item.className = "list-group-item"; - item.innerHTML = ` -