Compare commits

...

5 Commits

Author SHA1 Message Date
stirlingbot[bot]
c6b974eac5
📁 pre-commit
Signed-off-by: stirlingbot[bot] <stirlingbot[bot]@users.noreply.github.com>
2025-09-18 12:47:40 +00:00
Peter Dave Hello
a35bd09b5e
Update and improve Croatian (hrvatski) (hr_HR) locale (#4202) 2025-09-18 13:44:03 +01:00
Peter Dave Hello
8eb03c44c3
Update and improve the zh-TW Traditional Chinese translation (#4400) 2025-09-18 13:43:48 +01:00
Balázs Szücs
c684a51cf9
feat: custom error handling when calling renderImageWithDPI, controllers to respect global DPI (#4407) 2025-09-18 13:43:21 +01:00
Balázs Szücs
a438a15105
Update hungarian translation for editTableOfContents strings (#4165) 2025-09-06 20:27:19 +01:00
14 changed files with 1474 additions and 1237 deletions

View File

@ -324,4 +324,63 @@ public class ExceptionUtils {
return createIllegalArgumentException(
"error.argumentRequired", "{0} must not be null", argumentName);
}
/**
* Create a RuntimeException for memory/image size errors when rendering PDF images with DPI.
* Handles OutOfMemoryError and related conditions (e.g., NegativeArraySizeException) that
* result from images exceeding Java's array/memory limits.
*
* @param pageNumber the page number that caused the error
* @param dpi the DPI value used
* @param cause the original error/exception (e.g., OutOfMemoryError,
* NegativeArraySizeException)
* @return RuntimeException with user-friendly message
*/
public static RuntimeException createOutOfMemoryDpiException(
int pageNumber, int dpi, Throwable cause) {
String message =
MessageFormat.format(
"Out of memory or image-too-large error while rendering PDF page {0} at {1} DPI. "
+ "This can occur when the resulting image exceeds Java's array/memory limits (e.g., NegativeArraySizeException). "
+ "Please use a lower DPI value (recommended: 150 or less) or process the document in smaller chunks.",
pageNumber, dpi);
return new RuntimeException(message, cause);
}
/**
* Create a RuntimeException for OutOfMemoryError when rendering PDF images with DPI.
*
* @param pageNumber the page number that caused the error
* @param dpi the DPI value used
* @param cause the original OutOfMemoryError
* @return RuntimeException with user-friendly message
*/
public static RuntimeException createOutOfMemoryDpiException(
int pageNumber, int dpi, OutOfMemoryError cause) {
return createOutOfMemoryDpiException(pageNumber, dpi, (Throwable) cause);
}
/**
* Create a RuntimeException for memory/image size errors when rendering PDF images with DPI.
* Handles OutOfMemoryError and related conditions (e.g., NegativeArraySizeException) that
* result from images exceeding Java's array/memory limits.
*
* @param dpi the DPI value used
* @param cause the original error/exception (e.g., OutOfMemoryError,
* NegativeArraySizeException)
* @return RuntimeException with user-friendly message
*/
public static RuntimeException createOutOfMemoryDpiException(int dpi, Throwable cause) {
String message =
MessageFormat.format(
"Out of memory or image-too-large error while rendering PDF at {0} DPI. "
+ "This can occur when the resulting image exceeds Java's array/memory limits (e.g., NegativeArraySizeException). "
+ "Please use a lower DPI value (recommended: 150 or less) or process the document in smaller chunks.",
dpi);
return new RuntimeException(message, cause);
}
public static RuntimeException createOutOfMemoryDpiException(int dpi, OutOfMemoryError cause) {
return createOutOfMemoryDpiException(dpi, (Throwable) cause);
}
}

View File

@ -205,6 +205,10 @@ public class PdfUtils {
DPI);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
}
writer.writeToSequence(new IIOImage(image, null, null), param);
}
@ -253,6 +257,10 @@ public class PdfUtils {
DPI);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
}
pdfSizeImageIndex = i;
dimension =
@ -296,6 +304,10 @@ public class PdfUtils {
DPI);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
}
}
@ -330,6 +342,10 @@ public class PdfUtils {
DPI);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, DPI, e);
}
try (ByteArrayOutputStream baosImage = new ByteArrayOutputStream()) {
ImageIO.write(image, imageType, baosImage);
@ -369,8 +385,17 @@ public class PdfUtils {
pdfRenderer.setSubsamplingAllowed(true);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim;
// Use global maximum DPI setting, fallback to 300 if not set
int renderDpi = 300; // Default fallback
ApplicationProperties properties =
ApplicationContextProvider.getBean(ApplicationProperties.class);
if (properties != null && properties.getSystem() != null) {
renderDpi = properties.getSystem().getMaxDPI();
}
try {
bim = pdfRenderer.renderImageWithDPI(page, 300, ImageType.RGB);
bim = pdfRenderer.renderImageWithDPI(page, renderDpi, ImageType.RGB);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null
&& e.getMessage().contains("Maximum size of image exceeded")) {
@ -382,6 +407,10 @@ public class PdfUtils {
page + 1);
}
throw e;
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, 300, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, 300, e);
}
PDPage originalPage = document.getPage(page);

View File

@ -19,7 +19,10 @@ import org.apache.pdfbox.rendering.PDFRenderer;
import org.springframework.core.io.InputStreamResource;
import org.springframework.web.multipart.MultipartFile;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.model.api.misc.ReplaceAndInvert;
import stirling.software.common.util.ApplicationContextProvider;
import stirling.software.common.util.ExceptionUtils;
public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
@ -44,8 +47,25 @@ public class InvertFullColorStrategy extends ReplaceAndInvertColorStrategy {
// Render each page and invert colors
PDFRenderer pdfRenderer = new PDFRenderer(document);
for (int page = 0; page < document.getNumberOfPages(); page++) {
BufferedImage image =
pdfRenderer.renderImageWithDPI(page, 300); // Render page at 300 DPI
BufferedImage image;
// Use global maximum DPI setting, fallback to 300 if not set
int renderDpi = 300; // Default fallback
ApplicationProperties properties =
ApplicationContextProvider.getBean(ApplicationProperties.class);
if (properties != null && properties.getSystem() != null) {
renderDpi = properties.getSystem().getMaxDPI();
}
try {
image =
pdfRenderer.renderImageWithDPI(
page, renderDpi); // Render page with global DPI setting
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, renderDpi, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, renderDpi, e);
}
// Invert the colors
invertImageColors(image);

View File

@ -34,7 +34,10 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.AutoSplitPdfRequest;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ApplicationContextProvider;
import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.TempFile;
import stirling.software.common.util.TempFileManager;
import stirling.software.common.util.WebResponseUtils;
@ -128,7 +131,23 @@ public class AutoSplitPdfController {
pdfRenderer.setSubsamplingAllowed(true);
for (int page = 0; page < document.getNumberOfPages(); ++page) {
BufferedImage bim = pdfRenderer.renderImageWithDPI(page, 150);
BufferedImage bim;
// Use global maximum DPI setting, fallback to 300 if not set
int renderDpi = 150; // Default fallback
ApplicationProperties properties =
ApplicationContextProvider.getBean(ApplicationProperties.class);
if (properties != null && properties.getSystem() != null) {
renderDpi = properties.getSystem().getMaxDPI();
}
try {
bim = pdfRenderer.renderImageWithDPI(page, renderDpi);
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, renderDpi, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(page + 1, renderDpi, e);
}
String result = decodeQRCode(bim);
boolean isValidQrCode = VALID_QR_CONTENTS.contains(result);

View File

@ -30,7 +30,10 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.RemoveBlankPagesRequest;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ApplicationContextProvider;
import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.PdfUtils;
import stirling.software.common.util.WebResponseUtils;
@ -108,7 +111,25 @@ public class BlankPageController {
if (hasImages) {
log.info("page {} has image, running blank detection", pageIndex);
// Render image and save as temp file
BufferedImage image = pdfRenderer.renderImageWithDPI(pageIndex, 30);
BufferedImage image;
// Use global maximum DPI setting
int renderDpi = 30; // Default fallback
ApplicationProperties properties =
ApplicationContextProvider.getBean(ApplicationProperties.class);
if (properties != null && properties.getSystem() != null) {
renderDpi = properties.getSystem().getMaxDPI();
}
try {
image = pdfRenderer.renderImageWithDPI(pageIndex, renderDpi);
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(
pageIndex + 1, renderDpi, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(
pageIndex + 1, renderDpi, e);
}
blank = isBlankImage(image, threshold, whitePercent, threshold);
}
}

View File

@ -32,7 +32,9 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.ExtractImageScansRequest;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ApplicationContextProvider;
import stirling.software.common.util.CheckProgramInstall;
import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.GeneralUtils;
@ -97,7 +99,23 @@ public class ExtractImageScansController {
Path tempFile = Files.createTempFile("image_", ".png");
// Render image and save as temp file
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300);
BufferedImage image;
// Use global maximum DPI setting, fallback to 300 if not set
int renderDpi = 300; // Default fallback
ApplicationProperties properties =
ApplicationContextProvider.getBean(ApplicationProperties.class);
if (properties != null && properties.getSystem() != null) {
renderDpi = properties.getSystem().getMaxDPI();
}
try {
image = pdfRenderer.renderImageWithDPI(i, renderDpi);
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, renderDpi, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, renderDpi, e);
}
ImageIO.write(image, "png", tempFile.toFile());
// Add temp file path to images list

View File

@ -27,7 +27,10 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.FlattenRequest;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ApplicationContextProvider;
import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.WebResponseUtils;
@RestController
@ -67,7 +70,23 @@ public class FlattenController {
int numPages = document.getNumberOfPages();
for (int i = 0; i < numPages; i++) {
try {
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300, ImageType.RGB);
BufferedImage image;
// Use global maximum DPI setting, fallback to 300 if not set
int renderDpi = 300; // Default fallback
ApplicationProperties properties =
ApplicationContextProvider.getBean(ApplicationProperties.class);
if (properties != null && properties.getSystem() != null) {
renderDpi = properties.getSystem().getMaxDPI();
}
try {
image = pdfRenderer.renderImageWithDPI(i, renderDpi, ImageType.RGB);
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, renderDpi, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, renderDpi, e);
}
PDPage page = new PDPage();
page.setMediaBox(document.getPage(i).getMediaBox());
newDocument.addPage(page);

View File

@ -359,7 +359,24 @@ public class OCRController {
if (shouldOcr) {
// Convert page to image
BufferedImage image = pdfRenderer.renderImageWithDPI(pageNum, 300);
BufferedImage image;
// Use global maximum DPI setting, fallback to 300 if not set
int renderDpi = 300; // Default fallback
if (applicationProperties != null
&& applicationProperties.getSystem() != null) {
renderDpi = applicationProperties.getSystem().getMaxDPI();
}
try {
image = pdfRenderer.renderImageWithDPI(pageNum, renderDpi);
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(
pageNum + 1, renderDpi, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(
pageNum + 1, renderDpi, e);
}
File imagePath =
new File(tempImagesDir, String.format("page_%d.png", pageNum));
ImageIO.write(image, "png", imagePath);

View File

@ -34,7 +34,10 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.model.api.misc.ScannerEffectRequest;
import stirling.software.common.model.ApplicationProperties;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.ApplicationContextProvider;
import stirling.software.common.util.ExceptionUtils;
import stirling.software.common.util.WebResponseUtils;
@RestController
@ -82,6 +85,22 @@ public class ScannerEffectController {
int resolution = request.getResolution();
ScannerEffectRequest.Colorspace colorspace = request.getColorspace();
// Validate and limit DPI to prevent excessive memory usage (respecting global limits)
int maxSafeDpi = 500; // Default maximum safe DPI
ApplicationProperties properties =
ApplicationContextProvider.getBean(ApplicationProperties.class);
if (properties != null && properties.getSystem() != null) {
maxSafeDpi = properties.getSystem().getMaxDPI();
}
if (resolution > maxSafeDpi) {
throw ExceptionUtils.createIllegalArgumentException(
"error.dpiExceedsLimit",
"DPI value {0} exceeds maximum safe limit of {1}. High DPI values can cause"
+ " memory issues and crashes. Please use a lower DPI value.",
resolution,
maxSafeDpi);
}
try (PDDocument document = pdfDocumentFactory.load(file)) {
PDDocument outputDocument = new PDDocument();
PDFRenderer pdfRenderer = new PDFRenderer(document);
@ -118,7 +137,14 @@ public class ScannerEffectController {
}
// Render page to image with safe resolution
BufferedImage image = pdfRenderer.renderImageWithDPI(i, safeResolution);
BufferedImage image;
try {
image = pdfRenderer.renderImageWithDPI(i, safeResolution);
} catch (OutOfMemoryError e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, safeResolution, e);
} catch (NegativeArraySizeException e) {
throw ExceptionUtils.createOutOfMemoryDpiException(i + 1, safeResolution, e);
}
log.debug(
"Processing page {} with dimensions {}x{} ({} pixels) at {}dpi",

View File

@ -1,9 +1,8 @@
package stirling.software.SPDF.controller.api.misc;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.common.PDNameTreeNode;
import org.apache.pdfbox.pdmodel.interactive.action.PDActionJavaScript;
@ -14,13 +13,17 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import io.github.pixee.security.Filenames;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import stirling.software.common.model.api.PDFFile;
import stirling.software.common.service.CustomPDFDocumentFactory;
import stirling.software.common.util.WebResponseUtils;
import java.nio.charset.StandardCharsets;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/misc")
@Tag(name = "Misc", description = "Miscellaneous APIs")
@ -55,7 +58,9 @@ public class ShowJavascript {
if (jsCodeStr != null && !jsCodeStr.trim().isEmpty()) {
script.append("// File: ")
.append(Filenames.toSimpleFileName(inputFile.getOriginalFilename()))
.append(
Filenames.toSimpleFileName(
inputFile.getOriginalFilename()))
.append(", Script: ")
.append(name)
.append("\n")
@ -68,7 +73,8 @@ public class ShowJavascript {
}
if (!foundScript) {
script = new StringBuilder("PDF '")
script =
new StringBuilder("PDF '")
.append(Filenames.toSimpleFileName(inputFile.getOriginalFilename()))
.append("' does not contain Javascript");
}

File diff suppressed because it is too large Load Diff

View File

@ -1896,12 +1896,12 @@ editTableOfContents.replaceExisting=Meglévő könyvjelzők cseréje (törölje
editTableOfContents.editorTitle=Könyvjelző szerkesztő
editTableOfContents.editorDesc=Könyvjelzők hozzáadása és rendezése lent. Kattintson a + gombra gyermek könyvjelzők hozzáadásához.
editTableOfContents.addBookmark=Új könyvjelző hozzáadása
editTableOfContents.importBookmarksDefault=Import
editTableOfContents.importBookmarksFromJsonFile=Upload JSON file
editTableOfContents.importBookmarksFromClipboard=Paste from clipboard
editTableOfContents.exportBookmarksDefault=Export
editTableOfContents.exportBookmarksAsJson=Download as JSON
editTableOfContents.exportBookmarksAsText=Copy as text
editTableOfContents.importBookmarksDefault=Importálás
editTableOfContents.importBookmarksFromJsonFile=JSON fájl feltöltése
editTableOfContents.importBookmarksFromClipboard=Beillesztés vágólapról
editTableOfContents.exportBookmarksDefault=Exportálás
editTableOfContents.exportBookmarksAsJson=Letöltés JSON formátumban
editTableOfContents.exportBookmarksAsText=Másolás szövegként
editTableOfContents.desc.1=Ez az eszköz lehetővé teszi a tartalomjegyzék (könyvjelzők) hozzáadását vagy szerkesztését egy PDF dokumentumban.
editTableOfContents.desc.2=Hierarchikus struktúrákat hozhat létre, ha gyermek könyvjelzőket ad a szülő könyvjelzőkhöz.
editTableOfContents.desc.3=Minden könyvjelzőhöz szükséges egy cím és egy céloldalszám.

View File

@ -137,15 +137,15 @@ lang.yor=約魯巴語
addPageNumbers.fontSize=字型大小
addPageNumbers.fontName=字型名稱
addPageNumbers.fontColor=Font Colour
addPageNumbers.fontColor=字型顏色
pdfPrompt=選擇 PDF 檔案
multiPdfPrompt=選擇多個 PDF 檔案
multiPdfDropPrompt=選擇(或拖放)所有需要的 PDF 檔案
imgPrompt=選擇圖片
genericSubmit=送出
uploadLimit=檔案大小上限:
uploadLimitExceededSingular=太大。允許的最大檔案大小為
uploadLimitExceededPlural=太大。允許的最大檔案大小為
uploadLimitExceededSingular=檔案太大。允許的最大檔案大小為
uploadLimitExceededPlural=檔案太大。允許的最大檔案大小為
processTimeWarning=警告:此過程可能長達一分鐘,具體取決於檔案大小
pageOrderPrompt=自訂頁面順序(輸入以逗號分隔的頁碼或函式,如 2n+1
pageSelectionPrompt=自訂頁面選擇(輸入以逗號分隔的頁碼 1、5、6 或 2n+1 等函式的清單):
@ -194,7 +194,7 @@ error.fileFormatRequired=檔案必須為 {0} 格式
error.invalidFormat=無效的 {0} 格式:{1}
error.endpointDisabled=此端點已被管理員停用
error.urlNotReachable=無法連線至 URL請提供有效的 URL
error.invalidUrlFormat=Invalid URL format provided. The provided format is invalid.
error.invalidUrlFormat=提供了無效的 URL 格式。
# DPI and image rendering messages - used by frontend for dynamic translation
# Backend sends: [TRANSLATE:messageKey:arg1,arg2] English message
@ -580,9 +580,9 @@ home.setFavorites=設定我的最愛
home.hideFavorites=隱藏我的最愛
home.showFavorites=顯示我的最愛
home.legacyHomepage=舊版首頁
home.newHomePage=嘗試使用全新首頁!
home.newHomePage=試試全新的首頁!
home.alphabetical=按照字母排序
home.globalPopularity=熱門
home.globalPopularity=熱門
home.sortBy=排序方式:
home.multiTool.title=PDF 複合工具
@ -607,11 +607,11 @@ home.imageToPdf.desc=將圖片PNG、JPEG、GIF轉換為 PDF。
imageToPdf.tags=轉換,img,jpg,圖片,照片
home.pdfToImage.title=PDF 轉圖片
home.pdfToImage.desc=將 PDF 轉換為圖片PNG、JPEG、GIF
home.pdfToImage.desc=將 PDF 轉換為圖片PNG、JPEG、GIF
pdfToImage.tags=轉換,img,jpg,圖片,照片
home.pdfOrganiser.title=整理
home.pdfOrganiser.desc=以任何順序移除/重新排列頁面
home.pdfOrganiser.desc=移除或重新排列頁面順序
pdfOrganiser.tags=雙面,偶數,奇數,排序,移動
@ -701,8 +701,8 @@ home.sign.title=簽章
home.sign.desc=透過繪圖、文字或影像新增簽章到 PDF
sign.tags=授權,縮寫,繪製簽章,文字,影像簽章
home.flatten.title=
home.flatten.desc=從 PDF 中移除所有互動元素和表單
home.flatten.title=平化
home.flatten.desc=移除 PDF 中的所有互動元素和表單
flatten.tags=靜態,停用,非互動,簡化
home.repair.title=修復
@ -714,8 +714,8 @@ home.removeBlanks.desc=偵測並從文件中移除空白頁面
removeBlanks.tags=清理,簡化,非內容,組織
home.removeAnnotations.title=移除註釋
home.removeAnnotations.desc=從 PDF 中移除所有註釋/註解
removeAnnotations.tags=註釋,突出,註解,標記,移除
home.removeAnnotations.desc=移除 PDF 中的所有註釋
removeAnnotations.tags=註釋,醒目提示,備註,標記,移除
home.compare.title=比較
home.compare.desc=比較並顯示 2 個 PDF 檔案的差異
@ -731,7 +731,7 @@ removeCertSign.tags=驗證,PEM,P12,官方,解密
home.pageLayout.title=多頁面版面配置
home.pageLayout.desc=將 PDF 檔案的多個頁面合併到單一頁面
pageLayout.tags=合併,複合,單一檢視,組織
pageLayout.tags=合併,複合,單一檢視,整理
home.scalePages.title=調整頁面大小/比例
home.scalePages.desc=修改頁面及其內容的大小/比例。
@ -742,8 +742,8 @@ home.pipeline.desc=透過定義管道指令碼在 PDF 上執行多個操作
pipeline.tags=自動化,序列,指令碼,批次處理
home.add-page-numbers.title=新增頁碼
home.add-page-numbers.desc=在文件的定位置新增頁碼
add-page-numbers.tags=分頁,標籤,組織,索引
home.add-page-numbers.desc=在文件的定位置新增頁碼
add-page-numbers.tags=分頁,標籤,整理,索引
home.auto-rename.title=自動重新命名 PDF 檔案
home.auto-rename.desc=根據其偵測到的標頭自動重新命名 PDF 檔案
@ -758,8 +758,8 @@ home.crop.desc=裁剪 PDF 以減少其大小(保持文字!)
crop.tags=修剪,縮小,編輯,形狀
home.autoSplitPDF.title=自動分割頁面
home.autoSplitPDF.desc=自動分割掃描的 PDF使用實體掃描頁面分割器 QR Code
autoSplitPDF.tags=基於 QR Code,分離,掃描區段,組織
home.autoSplitPDF.desc=使用實體掃描頁面分割器 QR Code 自動分割掃描的 PDF
autoSplitPDF.tags=基於 QR Code,分離,掃描區段,整理
home.sanitizePdf.title=清理
home.sanitizePdf.desc=從 PDF 檔案中移除指令碼和其他元素
@ -862,8 +862,8 @@ home.validateSignature.desc=驗證 PDF 文件中的數位簽章與憑證
validateSignature.tags=簽章,驗證,確認,pdf,憑證,數位簽章,驗證簽章,驗證憑證
#replace-invert-color
replace-color.title=取代-反轉顏色
replace-color.header=取代-反轉 PDF 顏色
replace-color.title=取代反轉顏色
replace-color.header=取代反轉 PDF 顏色
home.replaceColorPdf.title=取代與反轉顏色
home.replaceColorPdf.desc=取代 PDF 中文字和背景的顏色,並反轉整個 PDF 的顏色以減少檔案大小
replaceColorPdf.tags=取代顏色,頁面操作,後端,伺服器端
@ -910,7 +910,7 @@ login.alreadyLoggedIn=您已經登入了
login.alreadyLoggedIn2=部裝置。請先從這些裝置登出後再試一次。
login.toManySessions=您有太多使用中的工作階段
login.logoutMessage=您已登出。
login.invalidInResponseTo=The requested SAML response is invalid or has expired. Please contact the administrator.
login.invalidInResponseTo=所要求的 SAML 回應無效或已過期,請聯絡系統管理員。
#auto-redact
autoRedact.title=自動塗黑
@ -1051,10 +1051,10 @@ AddStampRequest.stampImage=圖章圖片
AddStampRequest.alphabet=字母表
AddStampRequest.fontSize=字型/影像大小
AddStampRequest.rotation=旋轉
AddStampRequest.opacity=透明度
AddStampRequest.opacity=透明度
AddStampRequest.position=位置
AddStampRequest.overrideX= X 座標
AddStampRequest.overrideY= Y 座標
AddStampRequest.overrideX= X 座標
AddStampRequest.overrideY= Y 座標
AddStampRequest.customMargin=自訂邊緣
AddStampRequest.customColor=自訂文字顏色
AddStampRequest.submit=送出
@ -1217,7 +1217,7 @@ sign.last=最後一頁
sign.next=下一頁
sign.previous=上一頁
sign.maintainRatio=切換維持長寬比
sign.undo=撤銷
sign.undo=復原
sign.redo=重做
#repair
@ -1227,17 +1227,17 @@ repair.submit=修復
#flatten
flatten.title=
flatten.header=PDF
flatten.flattenOnlyForms=僅將表單
flatten.submit=
flatten.title=平化
flatten.header=PDF 平化
flatten.flattenOnlyForms=僅將表單平化
flatten.submit=平化
#ScannerImageSplit
ScannerImageSplit.selectText.1=角度閾值:
ScannerImageSplit.selectText.2=設定影像旋轉所需的最小絕對角度預設10
ScannerImageSplit.selectText.3=容忍度:
ScannerImageSplit.selectText.4=定圍繞估計的背景顏色的顏色變化範圍預設30
ScannerImageSplit.selectText.4=定圍繞估計的背景顏色的顏色變化範圍預設30
ScannerImageSplit.selectText.5=最小區域:
ScannerImageSplit.selectText.6=設定照片的最小區域閾值預設10000
ScannerImageSplit.selectText.7=最小輪廓區域:
@ -1248,21 +1248,21 @@ ScannerImageSplit.info=尚未安裝 Python。需要安裝 Python 才能執行。
#OCR
ocr.title=OCR / 掃描清理
ocr.header=清理掃描 / OCR光學字元識別
ocr.selectText.1=選擇要在 PDF 中偵測的語言(列出的是目前可偵測的語言):
ocr.selectText.2=產生包含 OCR 文字的文字文件,並與 OCR 的 PDF 一起
ocr.selectText.3=修正掃描的頁面傾斜角度,將它們旋轉回原位
ocr.selectText.4=清理頁面以降低 OCR 在背景雜訊中文字的機率。(無輸出變化)
ocr.selectText.5=清理頁面以降低 OCR 在背景雜訊中文字的機率,保持乾淨的輸出。
ocr.selectText.6=忽略具有互動文字的頁面,對影像頁面進行 OCR
ocr.selectText.7=強制 OCR將對每一頁進行 OCR移除所有原始文字元素
ocr.selectText.8=正常(如果 PDF 包含文字將出錯)
ocr.title=OCR 掃描清理
ocr.header=清理掃描與 OCR光學字元辨識
ocr.selectText.1=選擇要在 PDF 中偵測的語言(列出的是目前可偵測的語言):
ocr.selectText.2=產生包含 OCR 文字的文字檔,並與 OCR 後的 PDF 一起提供
ocr.selectText.3=修正掃描頁面的傾斜角度,將其旋轉回正
ocr.selectText.4=清理頁面以降低 OCR 在背景雜訊中識文字的機率。(無輸出變化)
ocr.selectText.5=清理頁面以降低 OCR 在背景雜訊中識文字的機率,保持乾淨的輸出。
ocr.selectText.6=忽略具有互動文字的頁面,對影像頁面進行 OCR
ocr.selectText.7=強制 OCR將對每一頁進行 OCR移除所有原始文字元素
ocr.selectText.8=正常(若 PDF 包含文字將會出錯)
ocr.selectText.9=額外設定
ocr.selectText.10=OCR 模式
ocr.selectText.11=移除 OCR 後的影像(移除所有影像,只有在轉換步驟中才有用)
ocr.selectText.11=移除 OCR 後的圖片(移除所有圖片,只有在轉換步驟中有用)
ocr.selectText.12=渲染類型(進階)
ocr.help=請閱讀此文件,了解如何使用其他語言和/或在 Docker 中使用
ocr.help=請閱讀此文件,了解如何使用其他語言或在 Docker 中使用
ocr.credit=此服務使用 qpdf 和 Tesseract 進行 OCR。
ocr.submit=使用 OCR 處理 PDF
@ -1280,26 +1280,26 @@ fileToPDF.title=檔案轉 PDF
fileToPDF.header=將任何檔案轉換為 PDF
fileToPDF.credit=此服務使用 LibreOffice 和 Unoconv 進行檔案轉換。
fileToPDF.supportedFileTypesInfo=支援的檔案類型
fileToPDF.supportedFileTypes=支援的檔案類型應包以下內容,但要獲得完整的更新支援格式列表,請參閱 LibreOffice 的文件
fileToPDF.supportedFileTypes=支援的檔案類型應包以下內容,但要獲得完整的更新支援格式列表,請參閱 LibreOffice 的文件
fileToPDF.submit=轉換為 PDF
#compress
compress.title=壓縮
compress.header=壓縮 PDF
compress.credit=此服務使用 qpdf 進行 PDF 壓縮/最佳化。
compress.credit=此服務使用 qpdf 進行 PDF 壓縮最佳化。
compress.grayscale.label=套用灰階進行壓縮
compress.selectText.1=壓縮設定
compress.selectText.1.1=1-3 為一般 PDF 壓縮,</br> 4-6 為輕度圖片壓縮,</br> 7-9 為高強度圖片壓縮,將大幅降低圖片品質
compress.selectText.2=最佳化等級:
compress.selectText.4=自動模式 - 自動調整品質使 PDF 達到指定的檔案大小
compress.selectText.5=指定的 PDF 檔案大小(例如 25MB, 10.8MB, 25KB
compress.selectText.5=指定的 PDF 檔案大小(例如25MB、10.8MB、25KB
compress.submit=壓縮
#Add image
addImage.title=新增圖片
addImage.header=新增圖片 PDF
addImage.header=新增圖片 PDF
addImage.everyPage=每一頁?
addImage.upload=新增圖片
addImage.submit=新增圖片
@ -1326,12 +1326,12 @@ pdfOrganiser.title=頁面整理
pdfOrganiser.header=PDF 頁面整理
pdfOrganiser.submit=重新排列頁面
pdfOrganiser.mode=模式
pdfOrganiser.mode.1=定義頁面順序
pdfOrganiser.mode.1=頁面順序
pdfOrganiser.mode.2=反向順序
pdfOrganiser.mode.3=排序
pdfOrganiser.mode.3=排序
pdfOrganiser.mode.4=摺頁冊排序
pdfOrganiser.mode.5=裝訂摺頁冊排序
pdfOrganiser.mode.6=奇偶
pdfOrganiser.mode.5=裝訂摺頁冊排序
pdfOrganiser.mode.6=奇偶
pdfOrganiser.mode.7=刪除第一頁
pdfOrganiser.mode.8=刪除最後一頁
pdfOrganiser.mode.9=刪除第一頁和最後一頁
@ -1341,14 +1341,14 @@ pdfOrganiser.placeholder=(例如 1,3,2 或 4-8,2,10-12 或 2n-1
#multiTool
multiTool.title=PDF 複合工具
multiTool.header=PDF 複合工具
multiTool.title=PDF 複合工具
multiTool.header=PDF 複合工具
multiTool.uploadPrompts=檔名
multiTool.selectAll=全選
multiTool.deselectAll=取消全選
multiTool.selectPages=選取頁面
multiTool.selectedPages=已選取的頁面
multiTool.page=
multiTool.page=
multiTool.deleteSelected=刪除已選取的項目
multiTool.downloadAll=匯出
multiTool.downloadSelected=匯出已選取的項目
@ -1376,7 +1376,7 @@ decrypt.serverError=解密時發生伺服器錯誤:{0}
decrypt.success=檔案已成功解密。
#multiTool-advert
multiTool-advert.message=此功能也可以在我們的<a href="{0}">複合工具頁面</a>中使用。前往查看並體驗更強大的逐頁操作介面及其他進階功能!
multiTool-advert.message=此功能也可以在我們的<a href="{0}">複合工具頁面</a>中使用。前往查看並體驗更強大的逐頁操作介面及其他進階功能!
#view pdf
viewPdf.title=檢視/編輯 PDF
@ -1437,11 +1437,11 @@ pdfToImage.colorType=顏色類型
pdfToImage.color=顏色
pdfToImage.grey=灰度
pdfToImage.blackwhite=黑白(可能會遺失資料!)
pdfToImage.dpi=DPI (The server limit is {0} dpi)
pdfToImage.dpi=DPI (伺服器的 dpi 限制為 {0} dpi)
pdfToImage.submit=轉換
pdfToImage.info=尚未安裝 Python。需要安裝 Python 才能進行 WebP 轉換。
pdfToImage.placeholder=(例如 1,2,8 或 4,7,12-16 或 2n-1
pdfToImage.includeAnnotations=Include annotations (comments, highlights etc.)
pdfToImage.includeAnnotations=包含註釋(評論、醒目提示等)
#addPassword
@ -1450,18 +1450,18 @@ addPassword.header=新增密碼(加密)
addPassword.selectText.1=選擇要加密的 PDF
addPassword.selectText.2=使用者密碼
addPassword.selectText.3=加密金鑰長度
addPassword.selectText.4=較高的值更,但較低的值具有更好的相容性。
addPassword.selectText.4=較高的值更安全,但較低的值具有更好的相容性。
addPassword.selectText.5=要設定的權限(建議與擁有者密碼一起使用)
addPassword.selectText.6=防止文件組裝
addPassword.selectText.7=防止內容
addPassword.selectText.8=防止為了無障礙使用而提取資料
addPassword.selectText.7=防止內容
addPassword.selectText.8=防止為了無障礙使用而擷取內容
addPassword.selectText.9=防止填寫表單
addPassword.selectText.10=防止修改
addPassword.selectText.11=防止註釋修改
addPassword.selectText.12=防止列印
addPassword.selectText.13=防止列印不同格式
addPassword.selectText.14=擁有者密碼
addPassword.selectText.15=限制一旦開啟文件可以做什麼(並非所有軟體都支援)
addPassword.selectText.15=限制開啟文件後的操作(並非所有軟體都支援)
addPassword.selectText.16=限制開啟文件本身
addPassword.submit=加密
@ -1488,12 +1488,12 @@ watermark.type.2=圖片
#Change permissions
permissions.title=變更權限
permissions.header=變更權限
permissions.warning=警告,要使這些權限不可變更,建議透過新增密碼頁面使用密碼設定這些權限
permissions.selectText.1=選擇要變更權限的 PDF
permissions.warning=警告:為確保權限無法被輕易變更,建議透過「新增密碼」頁面使用密碼來設定這些權限。
permissions.selectText.1=選擇要變更權限的 PDF
permissions.selectText.2=要設定的權限
permissions.selectText.3=防止文件組裝
permissions.selectText.4=防止內容
permissions.selectText.5=防止為了無障礙使用而提取資料
permissions.selectText.4=防止內容
permissions.selectText.5=防止為了無障礙而擷取內容
permissions.selectText.6=防止填寫表單
permissions.selectText.7=防止修改
permissions.selectText.8=防止註釋修改
@ -1513,17 +1513,17 @@ removePassword.submit=移除
#changeMetadata
changeMetadata.title=標題:
changeMetadata.header=變更中繼資料
changeMetadata.selectText.1=請編輯希望變更的變數
changeMetadata.selectText.1=請編輯希望變更的變數
changeMetadata.selectText.2=刪除所有中繼資料
changeMetadata.selectText.3=顯示自訂中繼資料:
changeMetadata.author=作者:
changeMetadata.creationDate=建立日期yyyy/MM/dd HH:mm:ss
changeMetadata.creationDate=建立日期 (yyyy/MM/dd HH:mm:ss)
changeMetadata.creator=建立者:
changeMetadata.keywords=關鍵字:
changeMetadata.modDate=修改日期yyyy/MM/dd HH:mm:ss
changeMetadata.modDate=修改日期 (yyyy/MM/dd HH:mm:ss)
changeMetadata.producer=製作人:
changeMetadata.subject=
changeMetadata.trapped=陷阱
changeMetadata.subject=
changeMetadata.trapped=補漏白
changeMetadata.selectText.4=其他中繼資料:
changeMetadata.selectText.5=新增自訂中繼資料項目
changeMetadata.submit=變更
@ -1536,11 +1536,11 @@ unlockPDFForms.submit=移除
#pdfToPDFA
pdfToPDFA.title=PDF 轉 PDF/A
pdfToPDFA.header=PDF 轉 PDF/A
pdfToPDFA.credit=此服務使用 libreoffice 進行 PDF/A 轉換
pdfToPDFA.credit=此服務使用 LibreOffice 進行 PDF/A 轉換
pdfToPDFA.submit=轉換
pdfToPDFA.tip=目前不支援上傳多個
pdfToPDFA.tip=目前不支援上傳多個檔案
pdfToPDFA.outputFormat=輸出格式
pdfToPDFA.pdfWithDigitalSignature=該 PDF 的憑證簽章將會在下一步被移除
pdfToPDFA.pdfWithDigitalSignature=此 PDF 的憑證簽章將在下一步被移除
#PDFToWord
@ -1617,18 +1617,18 @@ overlay-pdfs.submit=送出
#split-by-sections
split-by-sections.title=依區段分割 PDF
split-by-sections.header=將 PDF 分割成區段
split-by-sections.horizontal.label=水平
split-by-sections.vertical.label=垂直
split-by-sections.horizontal.placeholder=輸入水平分的數量
split-by-sections.vertical.placeholder=輸入垂直分的數量
split-by-sections.horizontal.label=水平
split-by-sections.vertical.label=垂直
split-by-sections.horizontal.placeholder=輸入水平的數量
split-by-sections.vertical.placeholder=輸入垂直的數量
split-by-sections.submit=分割 PDF
split-by-sections.merge=是否合併為一個 PDF
#printFile
printFile.title=列印檔案
printFile.header=使用印表機檔案
printFile.selectText.1=選擇要印的檔案
printFile.header=使用印表機印檔案
printFile.selectText.1=選擇要印的檔案
printFile.selectText.2=輸入印表機名稱
printFile.submit=列印
@ -1643,16 +1643,16 @@ licenses.license=授權條款
#survey
survey.nav=問卷調查
survey.title=Stirling-PDF 問卷調查
survey.description=Stirling-PDF 沒有追蹤功能,因此我們希望聽取使用者的意見來改進 Stirling-PDF
survey.changes=Stirling-PDF 自上次調查以來已有所改變!欲了解更多資訊,請查看我們的部落格文章:
survey.title=Stirling PDF 問卷調查
survey.description=Stirling PDF 沒有追蹤功能,因此我們希望聽取您的意見以改善產品
survey.changes=Stirling PDF 自上次調查以來已有所改變!欲了解更多資訊,請查看我們的部落格文章:
survey.changes2=隨著這些變更,我們正在獲得付費的商業支援和資金
survey.please=請考慮參與我們的問卷調查!
survey.disabled=(問卷調查彈出視窗將在後續更新中停用,但仍可在頁尾使用
survey.disabled=(問卷調查彈出視窗將在後續更新中停用,但仍可在頁尾存取
survey.button=參與問卷調查
survey.dontShowAgain=不要再顯示
survey.meeting.1=如果您在工作中使用 Stirling PDF我們很希望能與您交流。我們將提供技術支援諮詢以換取 15 分鐘的使用者體驗回饋交流
survey.meeting.2=這是一個機會讓您:
survey.dontShowAgain=不要再顯示
survey.meeting.1=如果您在工作中使用 Stirling PDF我們很希望能與您交流。我們將提供技術支援諮詢以換取 15 分鐘的使用者體驗回饋
survey.meeting.2=這是一個機會讓您:
survey.meeting.3=獲得關於部署、整合或疑難排解方面的協助
survey.meeting.4=針對效能、特殊案例和缺少的功能提供直接意見回饋
survey.meeting.5=協助我們改良 Stirling PDF 以符合實際企業使用需求
@ -1688,9 +1688,9 @@ 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.desc.2=書籤層級:選擇用於分割的書籤層級0 表示最上層1 表示第二層,依此類推)。
splitByChapters.desc.3=包含中繼資料:勾選,原始 PDF 的中繼資料將包含在每個分割後的 PDF 中。
splitByChapters.desc.4=允許重複:勾選,允許同一頁面上的多個書籤建立獨立的 PDF。
splitByChapters.submit=分割 PDF
#File Chooser

View File

@ -75,8 +75,11 @@ public class CustomUserDetailsService implements UserDetailsService {
*/
private AuthenticationType determinePreferredSSOType() {
// Check what SSO types are enabled and prefer in order: OAUTH2 > SAML2 > fallback to OAUTH2
boolean oauth2Enabled = securityProperties.getOauth2() != null && securityProperties.getOauth2().getEnabled();
boolean saml2Enabled = securityProperties.getSaml2() != null && securityProperties.getSaml2().getEnabled();
boolean oauth2Enabled =
securityProperties.getOauth2() != null
&& securityProperties.getOauth2().getEnabled();
boolean saml2Enabled =
securityProperties.getSaml2() != null && securityProperties.getSaml2().getEnabled();
if (oauth2Enabled) {
return AuthenticationType.OAUTH2;