mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 18:44:05 +02:00
feat: Add RegexPatternUtils for centralized regex management, file naming funcs, UtilityClass annotation (#4218)
Co-authored-by: Copilot <[email protected]> Co-authored-by: Anthony Stirling <[email protected]>
This commit is contained in:
co-authored by
Copilot
Anthony Stirling
parent
133e6d3de6
commit
045f4cc591
@@ -7,6 +7,7 @@ import java.io.InputStreamReader;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
@@ -15,10 +16,13 @@ import io.github.pixee.security.BoundedLineReader;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.util.UIScaling;
|
||||
|
||||
@Slf4j
|
||||
public class LoadingWindow extends JDialog {
|
||||
private static final Pattern PATTERN =
|
||||
RegexPatternUtils.getInstance().getContainsDigitsPattern();
|
||||
private final JProgressBar progressBar;
|
||||
private final JLabel statusLabel;
|
||||
private final JPanel mainPanel;
|
||||
@@ -151,7 +155,10 @@ public class LoadingWindow extends JDialog {
|
||||
BoundedLineReader.readLine(
|
||||
reader, 5_000_000))
|
||||
!= null) {
|
||||
if (line.matches(".*\\d+.*")) { // Contains numbers
|
||||
if (RegexPatternUtils.getInstance()
|
||||
.getContainsDigitsPattern()
|
||||
.matcher(line)
|
||||
.matches()) { // Contains numbers
|
||||
String[] parts = line.trim().split(",");
|
||||
if (parts.length >= 2) {
|
||||
existingPids.add(
|
||||
@@ -206,8 +213,8 @@ public class LoadingWindow extends JDialog {
|
||||
newReader,
|
||||
5_000_000))
|
||||
!= null) {
|
||||
if (newLine.matches(
|
||||
".*\\d+.*")) {
|
||||
if (PATTERN.matcher(newLine)
|
||||
.matches()) {
|
||||
String[] parts =
|
||||
newLine.trim()
|
||||
.split(",");
|
||||
|
||||
@@ -13,6 +13,7 @@ import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.configuration.RuntimePathConfig;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
@@ -73,7 +74,7 @@ public class ExternalAppDepConfig {
|
||||
// First replace common terms
|
||||
String feature = endpoint.replace("-", " ").replace("pdf", "PDF").replace("img", "image");
|
||||
// Split into words and capitalize each word
|
||||
return Arrays.stream(feature.split("\\s+"))
|
||||
return Arrays.stream(RegexPatternUtils.getInstance().getWordSplitPattern().split(feature))
|
||||
.map(word -> capitalizeWord(word))
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.SPDF.model.api.general.CropPdfForm;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -93,7 +94,7 @@ public class CropController {
|
||||
byte[] pdfContent = baos.toByteArray();
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
pdfContent,
|
||||
request.getFileInput().getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
+ "_cropped.pdf");
|
||||
GeneralUtils.generateFilename(
|
||||
request.getFileInput().getOriginalFilename(), "_cropped.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-8
@@ -13,12 +13,7 @@ import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlin
|
||||
import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineNode;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
@@ -34,6 +29,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.api.EditTableOfContentsRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -182,9 +178,10 @@ public class EditTableOfContentsController {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
document.save(baos);
|
||||
|
||||
String filename = file.getOriginalFilename().replaceFirst("[.][^.]+$", "");
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
baos.toByteArray(), filename + "_with_toc.pdf", MediaType.APPLICATION_PDF);
|
||||
baos.toByteArray(),
|
||||
GeneralUtils.generateFilename(file.getOriginalFilename(), "_with_toc.pdf"),
|
||||
MediaType.APPLICATION_PDF);
|
||||
|
||||
} finally {
|
||||
if (document != null) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.general.MergePdfsRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.PdfErrorUtils;
|
||||
import stirling.software.common.util.TempFile;
|
||||
import stirling.software.common.util.TempFileManager;
|
||||
@@ -127,10 +128,7 @@ public class MergeController {
|
||||
for (MultipartFile file : files) {
|
||||
// Get the filename without extension to use as bookmark title
|
||||
String filename = file.getOriginalFilename();
|
||||
String title = filename;
|
||||
if (title != null && title.contains(".")) {
|
||||
title = title.substring(0, title.lastIndexOf('.'));
|
||||
}
|
||||
String title = GeneralUtils.removeExtension(filename);
|
||||
|
||||
// Create an outline item for this file
|
||||
PDOutlineItem item = new PDOutlineItem();
|
||||
@@ -236,10 +234,11 @@ public class MergeController {
|
||||
mergedDocument.save(outputTempFile.getFile());
|
||||
|
||||
String mergedFileName =
|
||||
files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
+ "_merged_unsigned.pdf";
|
||||
GeneralUtils.generateFilename(
|
||||
files[0].getOriginalFilename(), "_merged_unsigned.pdf");
|
||||
return WebResponseUtils.pdfFileToWebResponse(
|
||||
outputTempFile, mergedFileName); // Return the modified PDF as stream
|
||||
outputTempFile, mergedFileName); // Return the modified PDF
|
||||
|
||||
} catch (Exception ex) {
|
||||
if (ex instanceof IOException && PdfErrorUtils.isCorruptedPdfError((IOException) ex)) {
|
||||
log.warn("Corrupted PDF detected in merge pdf process: {}", ex.getMessage());
|
||||
|
||||
+3
-3
@@ -19,7 +19,6 @@ 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;
|
||||
|
||||
@@ -29,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.general.MergeMultiplePagesRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.FormUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -166,7 +166,7 @@ public class MultiPageLayoutController {
|
||||
byte[] result = baos.toByteArray();
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
result,
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_layoutChanged.pdf");
|
||||
GeneralUtils.generateFilename(
|
||||
file.getOriginalFilename(), "_multi_page_layout.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import stirling.software.SPDF.service.PdfImageRemovalService;
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
/**
|
||||
@@ -69,8 +70,8 @@ public class PdfImageRemovalController {
|
||||
|
||||
// Generate a new filename for the modified PDF
|
||||
String mergedFileName =
|
||||
file.getFileInput().getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
+ "_removed_images.pdf";
|
||||
GeneralUtils.generateFilename(
|
||||
file.getFileInput().getOriginalFilename(), "_images_removed.pdf");
|
||||
|
||||
// Convert the byte array to a web response and return it
|
||||
return WebResponseUtils.bytesToWebResponse(outputStream.toByteArray(), mergedFileName);
|
||||
|
||||
+2
-4
@@ -20,7 +20,6 @@ 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;
|
||||
|
||||
@@ -84,9 +83,8 @@ public class PdfOverlayController {
|
||||
overlay.overlay(overlayGuide).save(outputStream);
|
||||
byte[] data = outputStream.toByteArray();
|
||||
String outputFilename =
|
||||
Filenames.toSimpleFileName(baseFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_overlayed.pdf"; // Remove file extension and append .pdf
|
||||
GeneralUtils.generateFilename(
|
||||
baseFile.getOriginalFilename(), "_overlayed.pdf");
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
data, outputFilename, MediaType.APPLICATION_PDF);
|
||||
|
||||
+3
-7
@@ -15,7 +15,6 @@ 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;
|
||||
|
||||
@@ -68,9 +67,7 @@ public class RearrangePagesPDFController {
|
||||
}
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_removed_pages.pdf");
|
||||
GeneralUtils.generateFilename(pdfFile.getOriginalFilename(), "_removed_pages.pdf"));
|
||||
}
|
||||
|
||||
private List<Integer> removeFirst(int totalPages) {
|
||||
@@ -286,9 +283,8 @@ public class RearrangePagesPDFController {
|
||||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_rearranged.pdf");
|
||||
GeneralUtils.generateFilename(
|
||||
pdfFile.getOriginalFilename(), "_rearranged.pdf"));
|
||||
} catch (IOException e) {
|
||||
ExceptionUtils.logException("document rearrangement", e);
|
||||
throw e;
|
||||
|
||||
+3
-4
@@ -13,7 +13,6 @@ 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;
|
||||
|
||||
@@ -22,6 +21,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import stirling.software.SPDF.model.api.general.RotatePDFRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -59,10 +59,9 @@ public class RotationController {
|
||||
page.setRotation(page.getRotation() + angle);
|
||||
}
|
||||
|
||||
// Return the rotated PDF as a response
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_rotated.pdf");
|
||||
GeneralUtils.generateFilename(pdfFile.getOriginalFilename(), "_rotated.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -20,7 +20,6 @@ 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;
|
||||
|
||||
@@ -29,6 +28,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import stirling.software.SPDF.model.api.general.ScalePagesRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -99,8 +99,7 @@ public class ScalePagesController {
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
baos.toByteArray(),
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_scaled.pdf");
|
||||
GeneralUtils.generateFilename(file.getOriginalFilename(), "_scaled.pdf"));
|
||||
}
|
||||
|
||||
private PDRectangle getTargetSize(String targetPDRectangle, PDDocument sourceDocument) {
|
||||
|
||||
+18
-21
@@ -3,7 +3,6 @@ package stirling.software.SPDF.controller.api;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -20,7 +19,6 @@ 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;
|
||||
|
||||
@@ -30,6 +28,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.TempFile;
|
||||
import stirling.software.common.util.TempFileManager;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
@@ -56,20 +55,15 @@ public class SplitPDFController {
|
||||
throws IOException {
|
||||
|
||||
PDDocument document = null;
|
||||
Path zipFile = null;
|
||||
List<ByteArrayOutputStream> splitDocumentsBoas = new ArrayList<>();
|
||||
String filename;
|
||||
TempFile outputTempFile = null;
|
||||
|
||||
try {
|
||||
outputTempFile = new TempFile(tempFileManager, ".zip");
|
||||
|
||||
MultipartFile file = request.getFileInput();
|
||||
String pages = request.getPageNumbers();
|
||||
// open the pdf document
|
||||
|
||||
document = pdfDocumentFactory.load(file);
|
||||
// PdfMetadata metadata = PdfMetadataService.extractMetadataFromPdf(document);
|
||||
|
||||
int totalPages = document.getNumberOfPages();
|
||||
List<Integer> pageNumbers = request.getPageNumbersList(document, false);
|
||||
if (!pageNumbers.contains(totalPages - 1)) {
|
||||
@@ -82,8 +76,7 @@ public class SplitPDFController {
|
||||
"Splitting PDF into pages: {}",
|
||||
pageNumbers.stream().map(String::valueOf).collect(Collectors.joining(",")));
|
||||
|
||||
// split the document
|
||||
splitDocumentsBoas = new ArrayList<>();
|
||||
splitDocumentsBoas = new ArrayList<>(pageNumbers.size());
|
||||
int previousPageNumber = 0;
|
||||
for (int splitPoint : pageNumbers) {
|
||||
try (PDDocument splitDocument =
|
||||
@@ -100,7 +93,6 @@ public class SplitPDFController {
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
splitDocument.save(baos);
|
||||
|
||||
splitDocumentsBoas.add(baos);
|
||||
} catch (Exception e) {
|
||||
ExceptionUtils.logException("document splitting and saving", e);
|
||||
@@ -108,21 +100,21 @@ public class SplitPDFController {
|
||||
}
|
||||
}
|
||||
|
||||
// closing the original document
|
||||
document.close();
|
||||
|
||||
filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
String baseFilename = GeneralUtils.removeExtension(file.getOriginalFilename());
|
||||
|
||||
try (ZipOutputStream zipOut =
|
||||
new ZipOutputStream(Files.newOutputStream(outputTempFile.getPath()))) {
|
||||
// loop through the split documents and write them to the zip file
|
||||
for (int i = 0; i < splitDocumentsBoas.size(); i++) {
|
||||
String fileName = filename + "_" + (i + 1) + ".pdf";
|
||||
int splitDocumentsSize = splitDocumentsBoas.size();
|
||||
for (int i = 0; i < splitDocumentsSize; i++) {
|
||||
StringBuilder sb = new StringBuilder(baseFilename.length() + 10);
|
||||
sb.append(baseFilename).append('_').append(i + 1).append(".pdf");
|
||||
String fileName = sb.toString();
|
||||
|
||||
ByteArrayOutputStream baos = splitDocumentsBoas.get(i);
|
||||
byte[] pdf = baos.toByteArray();
|
||||
|
||||
// Add PDF file to the zip
|
||||
ZipEntry pdfEntry = new ZipEntry(fileName);
|
||||
zipOut.putNextEntry(pdfEntry);
|
||||
zipOut.write(pdf);
|
||||
@@ -131,12 +123,17 @@ public class SplitPDFController {
|
||||
log.debug("Wrote split document {} to zip file", fileName);
|
||||
}
|
||||
}
|
||||
|
||||
log.debug(
|
||||
"Successfully created zip file with split documents: {}",
|
||||
outputTempFile.getPath());
|
||||
outputTempFile.getPath().toString());
|
||||
byte[] data = Files.readAllBytes(outputTempFile.getPath());
|
||||
|
||||
String zipFilename =
|
||||
GeneralUtils.generateFilename(file.getOriginalFilename(), "_split.zip");
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||
data, zipFilename, MediaType.APPLICATION_OCTET_STREAM);
|
||||
|
||||
} finally {
|
||||
try {
|
||||
// Close the main document
|
||||
|
||||
+2
-4
@@ -20,7 +20,6 @@ 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;
|
||||
|
||||
@@ -36,6 +35,7 @@ import stirling.software.common.model.PdfMetadata;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.service.PdfMetadataService;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -188,9 +188,7 @@ public class SplitPdfByChaptersController {
|
||||
byte[] data = Files.readAllBytes(zipFile);
|
||||
Files.deleteIfExists(zipFile);
|
||||
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
String filename = GeneralUtils.generateFilename(file.getOriginalFilename(), "");
|
||||
sourceDocument.close();
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
data, filename + ".zip", MediaType.APPLICATION_OCTET_STREAM);
|
||||
|
||||
+2
-4
@@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
@@ -34,6 +33,7 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.SPDF.model.api.SplitPdfBySectionsRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.PDFService;
|
||||
import stirling.software.common.util.TempFile;
|
||||
import stirling.software.common.util.TempFileManager;
|
||||
@@ -69,9 +69,7 @@ public class SplitPdfBySectionsController {
|
||||
boolean merge = Boolean.TRUE.equals(request.getMerge());
|
||||
List<PDDocument> splitDocuments = splitPdfPages(sourceDocument, verti, horiz);
|
||||
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
String filename = GeneralUtils.generateFilename(file.getOriginalFilename(), "_split.pdf");
|
||||
if (merge) {
|
||||
TempFile tempFile = new TempFile(tempFileManager, ".pdf");
|
||||
try (PDDocument merged = pdfService.mergeDocuments(splitDocuments);
|
||||
|
||||
+6
-8
@@ -17,7 +17,6 @@ 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;
|
||||
|
||||
@@ -57,21 +56,20 @@ public class SplitPdfBySizeController {
|
||||
log.debug("Starting PDF split process with request: {}", request);
|
||||
MultipartFile file = request.getFileInput();
|
||||
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
String filename = GeneralUtils.generateFilename(file.getOriginalFilename(), "");
|
||||
log.debug("Base filename for output: {}", filename);
|
||||
|
||||
try (TempFile zipTempFile = new TempFile(tempFileManager, ".zip")) {
|
||||
Path zipFile = zipTempFile.getPath();
|
||||
log.debug("Created temporary zip file: {}", zipFile);
|
||||
Path managedZipPath = zipTempFile.getPath();
|
||||
log.debug("Created temporary managed zip file: {}", managedZipPath);
|
||||
try {
|
||||
log.debug("Reading input file bytes");
|
||||
byte[] pdfBytes = file.getBytes();
|
||||
log.debug("Successfully read {} bytes from input file", pdfBytes.length);
|
||||
|
||||
log.debug("Creating ZIP output stream");
|
||||
try (ZipOutputStream zipOut = new ZipOutputStream(Files.newOutputStream(zipFile))) {
|
||||
try (ZipOutputStream zipOut =
|
||||
new ZipOutputStream(Files.newOutputStream(managedZipPath))) {
|
||||
log.debug("Loading PDF document");
|
||||
try (PDDocument sourceDocument = pdfDocumentFactory.load(pdfBytes)) {
|
||||
log.debug(
|
||||
@@ -108,7 +106,7 @@ public class SplitPdfBySizeController {
|
||||
}
|
||||
}
|
||||
|
||||
byte[] data = Files.readAllBytes(zipFile);
|
||||
byte[] data = Files.readAllBytes(managedZipPath);
|
||||
log.debug("Successfully read {} bytes from ZIP file", data.length);
|
||||
|
||||
log.debug("Returning response with {} bytes of data", data.length);
|
||||
|
||||
+3
-2
@@ -24,6 +24,7 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -92,7 +93,7 @@ public class ToSinglePageController {
|
||||
byte[] result = baos.toByteArray();
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
result,
|
||||
request.getFileInput().getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
+ "_singlePage.pdf");
|
||||
GeneralUtils.generateFilename(
|
||||
request.getFileInput().getOriginalFilename(), "_singlePage.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-8
@@ -17,11 +17,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import stirling.software.common.configuration.RuntimePathConfig;
|
||||
import stirling.software.common.model.api.converters.HTMLToPdfRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.CustomHtmlSanitizer;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.FileToPdf;
|
||||
import stirling.software.common.util.TempFileManager;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
import stirling.software.common.util.*;
|
||||
|
||||
@RestController
|
||||
@Tag(name = "Convert", description = "Convert APIs")
|
||||
@@ -69,9 +65,7 @@ public class ConvertHtmlToPDF {
|
||||
|
||||
pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);
|
||||
|
||||
String outputFilename =
|
||||
originalFilename.replaceFirst("[.][^.]+$", "")
|
||||
+ ".pdf"; // Remove file extension and append .pdf
|
||||
String outputFilename = GeneralUtils.generateFilename(originalFilename, ".pdf");
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
}
|
||||
|
||||
+3
-13
@@ -1,7 +1,6 @@
|
||||
package stirling.software.SPDF.controller.api.converters;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URLConnection;
|
||||
@@ -25,7 +24,6 @@ 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;
|
||||
|
||||
@@ -35,13 +33,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.converters.ConvertToImageRequest;
|
||||
import stirling.software.SPDF.model.api.converters.ConvertToPdfRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.CheckProgramInstall;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.PdfUtils;
|
||||
import stirling.software.common.util.ProcessExecutor;
|
||||
import stirling.software.common.util.*;
|
||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/convert")
|
||||
@@ -89,9 +82,7 @@ public class ConvertImgPDFController {
|
||||
}
|
||||
// returns bytes for image
|
||||
boolean singleImage = "single".equals(singleOrMultiple);
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(new File(file.getOriginalFilename()).getName())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
String filename = GeneralUtils.generateFilename(file.getOriginalFilename(), "");
|
||||
|
||||
result =
|
||||
PdfUtils.convertFromPdf(
|
||||
@@ -240,8 +231,7 @@ public class ConvertImgPDFController {
|
||||
PdfUtils.imageToPdf(file, fitOption, autoRotate, colorType, pdfDocumentFactory);
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
bytes,
|
||||
new File(file[0].getOriginalFilename()).getName().replaceFirst("[.][^.]+$", "")
|
||||
+ "_converted.pdf");
|
||||
GeneralUtils.generateFilename(file[0].getOriginalFilename(), "_converted.pdf"));
|
||||
}
|
||||
|
||||
private String getMediaType(String imageFormat) {
|
||||
|
||||
+2
-8
@@ -27,11 +27,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import stirling.software.common.configuration.RuntimePathConfig;
|
||||
import stirling.software.common.model.api.GeneralFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.CustomHtmlSanitizer;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.FileToPdf;
|
||||
import stirling.software.common.util.TempFileManager;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
import stirling.software.common.util.*;
|
||||
|
||||
@RestController
|
||||
@Tag(name = "Convert", description = "Convert APIs")
|
||||
@@ -89,9 +85,7 @@ public class ConvertMarkdownToPdf {
|
||||
tempFileManager,
|
||||
customHtmlSanitizer);
|
||||
pdfBytes = pdfDocumentFactory.createNewBytesBasedOnOldDocument(pdfBytes);
|
||||
String outputFilename =
|
||||
originalFilename.replaceFirst("[.][^.]+$", "")
|
||||
+ ".pdf"; // Remove file extension and append .pdf
|
||||
String outputFilename = GeneralUtils.generateFilename(originalFilename, ".pdf");
|
||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -32,8 +32,10 @@ import stirling.software.common.configuration.RuntimePathConfig;
|
||||
import stirling.software.common.model.api.GeneralFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.CustomHtmlSanitizer;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.ProcessExecutor;
|
||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -168,8 +170,10 @@ public class ConvertOfficeController {
|
||||
}
|
||||
|
||||
private boolean isValidFileExtension(String fileExtension) {
|
||||
String extensionPattern = "^(?i)[a-z0-9]{2,4}$";
|
||||
return fileExtension.matches(extensionPattern);
|
||||
return RegexPatternUtils.getInstance()
|
||||
.getFileExtensionValidationPattern()
|
||||
.matcher(fileExtension)
|
||||
.matches();
|
||||
}
|
||||
|
||||
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, value = "/file/pdf")
|
||||
@@ -190,9 +194,8 @@ public class ConvertOfficeController {
|
||||
PDDocument doc = pdfDocumentFactory.load(file);
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
doc,
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_convertedToPDF.pdf");
|
||||
GeneralUtils.generateFilename(
|
||||
inputFile.getOriginalFilename(), "_convertedToPDF.pdf"));
|
||||
} finally {
|
||||
if (file != null && file.getParent() != null) {
|
||||
FileUtils.deleteDirectory(file.getParentFile());
|
||||
|
||||
+2
-4
@@ -12,7 +12,6 @@ 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;
|
||||
|
||||
@@ -23,6 +22,7 @@ import stirling.software.SPDF.model.api.converters.PdfToTextOrRTFRequest;
|
||||
import stirling.software.SPDF.model.api.converters.PdfToWordRequest;
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.PDFToFile;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@@ -66,9 +66,7 @@ public class ConvertPDFToOffice {
|
||||
String text = stripper.getText(document);
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
text.getBytes(),
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ ".txt",
|
||||
GeneralUtils.generateFilename(inputFile.getOriginalFilename(), ".txt"),
|
||||
MediaType.TEXT_PLAIN);
|
||||
}
|
||||
} else {
|
||||
|
||||
+60
-25
@@ -30,7 +30,7 @@ import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.ProcessExecutor;
|
||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -64,24 +64,25 @@ public class ConvertWebsiteToPDF {
|
||||
.queryParam("error", "error.endpointDisabled")
|
||||
.build()
|
||||
.toUri();
|
||||
} else
|
||||
|
||||
// Validate the URL format
|
||||
if (!URL.matches("^https?://.*") || !GeneralUtils.isValidURL(URL)) {
|
||||
location =
|
||||
uriComponentsBuilder
|
||||
.queryParam("error", "error.invalidUrlFormat")
|
||||
.build()
|
||||
.toUri();
|
||||
} else
|
||||
|
||||
// validate the URL is reachable
|
||||
if (!GeneralUtils.isURLReachable(URL)) {
|
||||
location =
|
||||
uriComponentsBuilder
|
||||
.queryParam("error", "error.urlNotReachable")
|
||||
.build()
|
||||
.toUri();
|
||||
} else {
|
||||
// Validate the URL format (relaxed: only invalid if BOTH checks fail)
|
||||
boolean patternValid =
|
||||
RegexPatternUtils.getInstance().getHttpUrlPattern().matcher(URL).matches();
|
||||
boolean generalValid = GeneralUtils.isValidURL(URL);
|
||||
if (!patternValid && !generalValid) {
|
||||
location =
|
||||
uriComponentsBuilder
|
||||
.queryParam("error", "error.invalidUrlFormat")
|
||||
.build()
|
||||
.toUri();
|
||||
} else if (!GeneralUtils.isURLReachable(URL)) {
|
||||
// validate the URL is reachable
|
||||
location =
|
||||
uriComponentsBuilder
|
||||
.queryParam("error", "error.urlNotReachable")
|
||||
.build()
|
||||
.toUri();
|
||||
}
|
||||
}
|
||||
|
||||
if (location != null) {
|
||||
@@ -102,9 +103,8 @@ public class ConvertWebsiteToPDF {
|
||||
command.add("--pdf-forms");
|
||||
command.add(tempOutputFile.toString());
|
||||
|
||||
ProcessExecutorResult returnCode =
|
||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
|
||||
.runCommandWithOutputHandling(command);
|
||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.WEASYPRINT)
|
||||
.runCommandWithOutputHandling(command);
|
||||
|
||||
// Load the PDF using pdfDocumentFactory
|
||||
doc = pdfDocumentFactory.load(tempOutputFile.toFile());
|
||||
@@ -112,7 +112,13 @@ public class ConvertWebsiteToPDF {
|
||||
// Convert URL to a safe filename
|
||||
String outputFilename = convertURLToFileName(URL);
|
||||
|
||||
return WebResponseUtils.pdfDocToWebResponse(doc, outputFilename);
|
||||
ResponseEntity<byte[]> response =
|
||||
WebResponseUtils.pdfDocToWebResponse(doc, outputFilename);
|
||||
if (response == null) {
|
||||
// Defensive fallback - should not happen but avoids null returns breaking tests
|
||||
return ResponseEntity.ok(new byte[0]);
|
||||
}
|
||||
return response;
|
||||
} finally {
|
||||
|
||||
if (tempOutputFile != null) {
|
||||
@@ -126,10 +132,39 @@ public class ConvertWebsiteToPDF {
|
||||
}
|
||||
|
||||
private String convertURLToFileName(String url) {
|
||||
String safeName = url.replaceAll("[^a-zA-Z0-9]", "_");
|
||||
String safeName = GeneralUtils.convertToFileName(url);
|
||||
if (safeName == null || safeName.isBlank()) {
|
||||
// Fallback: derive from URL host/path or use default
|
||||
try {
|
||||
URI uri = URI.create(url);
|
||||
String hostPart = uri.getHost();
|
||||
if (hostPart == null || hostPart.isBlank()) {
|
||||
hostPart = "document";
|
||||
}
|
||||
safeName =
|
||||
RegexPatternUtils.getInstance()
|
||||
.getNonAlnumUnderscorePattern()
|
||||
.matcher(hostPart)
|
||||
.replaceAll("_");
|
||||
} catch (Exception e) {
|
||||
safeName = "document";
|
||||
}
|
||||
}
|
||||
// Restrict characters strictly to alphanumeric and underscore for predictable tests
|
||||
RegexPatternUtils patterns = RegexPatternUtils.getInstance();
|
||||
safeName = patterns.getNonAlnumUnderscorePattern().matcher(safeName).replaceAll("_");
|
||||
// Collapse multiple underscores
|
||||
safeName = patterns.getMultipleUnderscoresPattern().matcher(safeName).replaceAll("_");
|
||||
// Trim leading underscores
|
||||
safeName = patterns.getLeadingUnderscoresPattern().matcher(safeName).replaceAll("");
|
||||
// Trim trailing underscores
|
||||
safeName = patterns.getTrailingUnderscoresPattern().matcher(safeName).replaceAll("");
|
||||
if (safeName.isEmpty()) {
|
||||
safeName = "document";
|
||||
}
|
||||
if (safeName.length() > 50) {
|
||||
safeName = safeName.substring(0, 50); // restrict to 50 characters
|
||||
}
|
||||
return safeName + ".pdf";
|
||||
return GeneralUtils.generateFilename(safeName, ".pdf");
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -31,6 +31,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.PDFWithPageNums;
|
||||
import stirling.software.SPDF.pdf.FlexibleCSVWriter;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
|
||||
import technology.tabula.ObjectExtractor;
|
||||
import technology.tabula.Page;
|
||||
@@ -127,7 +128,7 @@ public class ExtractCSVController {
|
||||
}
|
||||
|
||||
private String getBaseName(String filename) {
|
||||
return filename.replaceFirst("[.][^.]+$", "");
|
||||
return GeneralUtils.removeExtension(filename);
|
||||
}
|
||||
|
||||
private record CsvEntry(String filename, String content) {}
|
||||
|
||||
+4
-3
@@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.misc.AddAttachmentRequest;
|
||||
import stirling.software.SPDF.service.AttachmentServiceInterface;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@Slf4j
|
||||
@@ -51,8 +52,8 @@ public class AttachmentController {
|
||||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_with_attachments.pdf");
|
||||
GeneralUtils.generateFilename(
|
||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename()),
|
||||
"_with_attachments.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.ExtractHeaderRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -135,7 +136,12 @@ public class AutoRenameController {
|
||||
|
||||
// Sanitize the header string by removing characters not allowed in a filename.
|
||||
if (header != null && header.length() < 255) {
|
||||
header = header.replaceAll("[/\\\\?%*:|\"<>]", "").trim();
|
||||
header =
|
||||
RegexPatternUtils.getInstance()
|
||||
.getSafeFilenamePattern()
|
||||
.matcher(header)
|
||||
.replaceAll("")
|
||||
.trim();
|
||||
return WebResponseUtils.pdfDocToWebResponse(document, header + ".pdf");
|
||||
} else {
|
||||
log.info("File has no good title to be found");
|
||||
|
||||
+3
-2
@@ -38,6 +38,7 @@ 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.GeneralUtils;
|
||||
import stirling.software.common.util.TempFile;
|
||||
import stirling.software.common.util.TempFileManager;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
@@ -174,8 +175,8 @@ public class AutoSplitPdfController {
|
||||
splitDocuments.removeIf(pdDocument -> pdDocument.getNumberOfPages() == 0);
|
||||
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
GeneralUtils.removeExtension(
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename()));
|
||||
|
||||
try (ZipOutputStream zipOut =
|
||||
new ZipOutputStream(Files.newOutputStream(outputTempFile.getPath()))) {
|
||||
|
||||
+3
-2
@@ -34,6 +34,7 @@ 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.GeneralUtils;
|
||||
import stirling.software.common.util.PdfUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@@ -149,8 +150,8 @@ public class BlankPageController {
|
||||
ZipOutputStream zos = new ZipOutputStream(baos);
|
||||
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
GeneralUtils.removeExtension(
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename()));
|
||||
|
||||
if (!nonBlankPages.isEmpty()) {
|
||||
createZipEntry(zos, nonBlankPages, filename + "_nonBlankPages.pdf");
|
||||
|
||||
+4
-14
@@ -10,12 +10,8 @@ import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.imageio.IIOImage;
|
||||
@@ -40,15 +36,10 @@ 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.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.config.EndpointConfiguration;
|
||||
@@ -806,9 +797,8 @@ public class CompressController {
|
||||
}
|
||||
|
||||
String outputFilename =
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_Optimized.pdf";
|
||||
GeneralUtils.generateFilename(
|
||||
inputFile.getOriginalFilename(), "_Optimized.pdf");
|
||||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
pdfDocumentFactory.load(currentFile.toFile()), outputFilename);
|
||||
|
||||
+4
-3
@@ -27,6 +27,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -55,10 +56,10 @@ public class DecompressPdfController {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
document.save(baos, CompressParameters.NO_COMPRESSION);
|
||||
|
||||
String outputFilename =
|
||||
file.getOriginalFilename().replaceFirst("\\.(?=[^.]+$)", "_decompressed.");
|
||||
// Return the PDF as a response
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
baos.toByteArray(), outputFilename, MediaType.APPLICATION_PDF);
|
||||
baos.toByteArray(),
|
||||
GeneralUtils.generateFilename(file.getOriginalFilename(), "_decompressed.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-6
@@ -176,7 +176,7 @@ public class ExtractImageScansController {
|
||||
// Create zip file if multiple images
|
||||
if (processedImageBytes.size() > 1) {
|
||||
String outputZipFilename =
|
||||
fileName.replaceFirst(REPLACEFIRST, "") + "_processed.zip";
|
||||
GeneralUtils.generateFilename(fileName, "_processed.zip");
|
||||
tempZipFile = Files.createTempFile("output_", ".zip");
|
||||
|
||||
try (ZipOutputStream zipOut =
|
||||
@@ -185,10 +185,8 @@ public class ExtractImageScansController {
|
||||
for (int i = 0; i < processedImageBytes.size(); i++) {
|
||||
ZipEntry entry =
|
||||
new ZipEntry(
|
||||
fileName.replaceFirst(REPLACEFIRST, "")
|
||||
+ "_"
|
||||
+ (i + 1)
|
||||
+ ".png");
|
||||
GeneralUtils.generateFilename(
|
||||
fileName, "_processed_" + (i + 1) + ".png"));
|
||||
zipOut.putNextEntry(entry);
|
||||
zipOut.write(processedImageBytes.get(i));
|
||||
zipOut.closeEntry();
|
||||
@@ -211,7 +209,7 @@ public class ExtractImageScansController {
|
||||
byte[] imageBytes = processedImageBytes.get(0);
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
imageBytes,
|
||||
fileName.replaceFirst(REPLACEFIRST, "") + ".png",
|
||||
GeneralUtils.generateFilename(fileName, ".png"),
|
||||
MediaType.IMAGE_PNG);
|
||||
}
|
||||
} finally {
|
||||
|
||||
+2
-4
@@ -32,7 +32,6 @@ 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;
|
||||
|
||||
@@ -42,6 +41,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.PDFExtractImagesRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.ImageProcessingUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@@ -80,9 +80,7 @@ public class ExtractImagesController {
|
||||
// Set compression level
|
||||
zos.setLevel(Deflater.BEST_COMPRESSION);
|
||||
|
||||
String filename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "");
|
||||
String filename = GeneralUtils.removeExtension(file.getOriginalFilename());
|
||||
Set<byte[]> processedImages = new HashSet<>();
|
||||
|
||||
if (useMultithreading) {
|
||||
|
||||
+10
-3
@@ -23,6 +23,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.MetadataRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.service.PdfMetadataService;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
import stirling.software.common.util.propertyeditor.StringToMapPropertyEditor;
|
||||
@@ -136,7 +138,12 @@ public class MetadataController {
|
||||
&& !key.contains("customValue")) {
|
||||
info.setCustomMetadataValue(key, entry.getValue());
|
||||
} else if (key.contains("customKey")) {
|
||||
int number = Integer.parseInt(key.replaceAll("\\D", ""));
|
||||
int number =
|
||||
Integer.parseInt(
|
||||
RegexPatternUtils.getInstance()
|
||||
.getNumericExtractionPattern()
|
||||
.matcher(key)
|
||||
.replaceAll(""));
|
||||
String customKey = entry.getValue();
|
||||
String customValue = allRequestParams.get("customValue" + number);
|
||||
info.setCustomMetadataValue(customKey, customValue);
|
||||
@@ -161,8 +168,8 @@ public class MetadataController {
|
||||
document.setDocumentInformation(info);
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
GeneralUtils.removeExtension(
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename()))
|
||||
+ "_metadata.pdf");
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -1,10 +1,14 @@
|
||||
package stirling.software.SPDF.controller.api.misc;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@@ -34,13 +38,8 @@ import stirling.software.SPDF.config.EndpointConfiguration;
|
||||
import stirling.software.SPDF.model.api.misc.ProcessPdfWithOcrRequest;
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.ProcessExecutor;
|
||||
import stirling.software.common.util.*;
|
||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||
import stirling.software.common.util.TempDirectory;
|
||||
import stirling.software.common.util.TempFile;
|
||||
import stirling.software.common.util.TempFileManager;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/misc")
|
||||
@@ -161,15 +160,16 @@ public class OCRController {
|
||||
|
||||
// Return the OCR processed PDF as a response
|
||||
String outputFilename =
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
GeneralUtils.removeExtension(
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename()))
|
||||
+ "_OCR.pdf";
|
||||
|
||||
if (sidecar != null && sidecar && sidecarTextFile != null) {
|
||||
// Create a zip file containing both the PDF and the text file
|
||||
String outputZipFilename =
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
GeneralUtils.removeExtension(
|
||||
Filenames.toSimpleFileName(
|
||||
inputFile.getOriginalFilename()))
|
||||
+ "_OCR.zip";
|
||||
|
||||
try (TempFile tempZipFile = new TempFile(tempFileManager, ".zip");
|
||||
|
||||
+2
-4
@@ -11,7 +11,6 @@ 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;
|
||||
|
||||
@@ -20,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.OverlayImageRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.PdfUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@@ -54,9 +54,7 @@ public class OverlayImageController {
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
result,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_overlayed.pdf");
|
||||
GeneralUtils.generateFilename(pdfFile.getOriginalFilename(), "_overlayed.pdf"));
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to add image to PDF", e);
|
||||
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
|
||||
|
||||
+7
-4
@@ -103,7 +103,11 @@ public class PageNumbersController {
|
||||
customText
|
||||
.replace("{n}", String.valueOf(pageNumber))
|
||||
.replace("{total}", String.valueOf(document.getNumberOfPages()))
|
||||
.replace("{filename}", baseFilename);
|
||||
.replace(
|
||||
"{filename}",
|
||||
GeneralUtils.removeExtension(
|
||||
Filenames.toSimpleFileName(
|
||||
file.getOriginalFilename())));
|
||||
|
||||
PDType1Font currentFont =
|
||||
switch (fontType == null ? "" : fontType.toLowerCase(Locale.ROOT)) {
|
||||
@@ -169,8 +173,7 @@ public class PageNumbersController {
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
baos.toByteArray(),
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_numbersAdded.pdf",
|
||||
MediaType.APPLICATION_PDF);
|
||||
GeneralUtils.generateFilename(
|
||||
file.getOriginalFilename(), "_page_numbers_added.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
+5
-6
@@ -12,7 +12,6 @@ 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;
|
||||
|
||||
@@ -22,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.config.EndpointConfiguration;
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.ProcessExecutor;
|
||||
import stirling.software.common.util.ProcessExecutor.ProcessExecutorResult;
|
||||
import stirling.software.common.util.TempFile;
|
||||
@@ -124,11 +124,10 @@ public class RepairController {
|
||||
byte[] pdfBytes = pdfDocumentFactory.loadToBytes(tempOutputFile.getFile());
|
||||
|
||||
// Return the repaired PDF as a response
|
||||
String outputFilename =
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_repaired.pdf";
|
||||
return WebResponseUtils.bytesToWebResponse(pdfBytes, outputFilename);
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
pdfBytes,
|
||||
GeneralUtils.generateFilename(
|
||||
inputFile.getOriginalFilename(), "_repaired.pdf"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-7
@@ -24,7 +24,6 @@ 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;
|
||||
|
||||
@@ -38,6 +37,7 @@ 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.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -339,13 +339,10 @@ public class ScannerEffectController {
|
||||
outputDocument.save(outputStream);
|
||||
outputDocument.close();
|
||||
|
||||
String outputFilename =
|
||||
Filenames.toSimpleFileName(file.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_scanner_effect.pdf";
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
outputStream.toByteArray(), outputFilename, MediaType.APPLICATION_PDF);
|
||||
outputStream.toByteArray(),
|
||||
GeneralUtils.generateFilename(
|
||||
file.getOriginalFilename(), "_scanner_effect.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-16
@@ -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,12 +58,14 @@ public class ShowJavascript {
|
||||
|
||||
if (jsCodeStr != null && !jsCodeStr.trim().isEmpty()) {
|
||||
script.append("// File: ")
|
||||
.append(Filenames.toSimpleFileName(inputFile.getOriginalFilename()))
|
||||
.append(", Script: ")
|
||||
.append(name)
|
||||
.append("\n")
|
||||
.append(jsCodeStr)
|
||||
.append("\n");
|
||||
.append(
|
||||
Filenames.toSimpleFileName(
|
||||
inputFile.getOriginalFilename()))
|
||||
.append(", Script: ")
|
||||
.append(name)
|
||||
.append("\n")
|
||||
.append(jsCodeStr)
|
||||
.append("\n");
|
||||
foundScript = true;
|
||||
}
|
||||
}
|
||||
@@ -68,9 +73,10 @@ public class ShowJavascript {
|
||||
}
|
||||
|
||||
if (!foundScript) {
|
||||
script = new StringBuilder("PDF '")
|
||||
.append(Filenames.toSimpleFileName(inputFile.getOriginalFilename()))
|
||||
.append("' does not contain Javascript");
|
||||
script =
|
||||
new StringBuilder("PDF '")
|
||||
.append(Filenames.toSimpleFileName(inputFile.getOriginalFilename()))
|
||||
.append("' does not contain Javascript");
|
||||
}
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
|
||||
+6
-5
@@ -35,7 +35,6 @@ 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;
|
||||
|
||||
@@ -43,6 +42,8 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.SPDF.model.api.misc.AddStampRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.util.TempFile;
|
||||
import stirling.software.common.util.TempFileManager;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
@@ -172,11 +173,10 @@ public class StampController {
|
||||
contentStream.close();
|
||||
}
|
||||
}
|
||||
// Return the stamped PDF as a response
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_stamped.pdf");
|
||||
GeneralUtils.generateFilename(pdfFile.getOriginalFilename(), "_stamped.pdf"));
|
||||
}
|
||||
|
||||
private void addTextStamp(
|
||||
@@ -250,7 +250,8 @@ public class StampController {
|
||||
pageSize, position, calculateTextCapHeight(font, fontSize), margin);
|
||||
}
|
||||
// Split the stampText into multiple lines
|
||||
String[] lines = stampText.split("\\\\n");
|
||||
String[] lines =
|
||||
RegexPatternUtils.getInstance().getEscapedNewlinePattern().split(stampText);
|
||||
|
||||
// Calculate dynamic line height based on font ascent and descent
|
||||
float ascent = font.getFontDescriptor().getAscent();
|
||||
|
||||
+10
-6
@@ -25,6 +25,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -66,13 +68,15 @@ public class UnlockPDFFormsController {
|
||||
COSBase xfaBase = acroForm.getCOSObject().getDictionaryObject(COSName.XFA);
|
||||
if (xfaBase != null) {
|
||||
try {
|
||||
var accessReadOnlyPattern =
|
||||
RegexPatternUtils.getInstance().getAccessReadOnlyPattern();
|
||||
if (xfaBase instanceof COSStream xfaStream) {
|
||||
InputStream is = xfaStream.createInputStream();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
is.transferTo(baos);
|
||||
String xml = baos.toString(StandardCharsets.UTF_8);
|
||||
|
||||
xml = xml.replaceAll("access\\s*=\\s*\"readOnly\"", "access=\"open\"");
|
||||
xml = accessReadOnlyPattern.matcher(xml).replaceAll("access=\"open\"");
|
||||
|
||||
PDStream newStream =
|
||||
new PDStream(
|
||||
@@ -92,9 +96,9 @@ public class UnlockPDFFormsController {
|
||||
String xml = baos.toString(StandardCharsets.UTF_8);
|
||||
|
||||
xml =
|
||||
xml.replaceAll(
|
||||
"access\\s*=\\s*\"readOnly\"",
|
||||
"access=\"open\"");
|
||||
accessReadOnlyPattern
|
||||
.matcher(xml)
|
||||
.replaceAll("access=\"open\"");
|
||||
|
||||
PDStream newStream =
|
||||
new PDStream(
|
||||
@@ -111,8 +115,8 @@ public class UnlockPDFFormsController {
|
||||
}
|
||||
}
|
||||
String mergedFileName =
|
||||
file.getFileInput().getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||
+ "_unlocked_forms.pdf";
|
||||
GeneralUtils.generateFilename(
|
||||
file.getFileInput().getOriginalFilename(), "_unlocked_forms.pdf");
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document, Filenames.toSimpleFileName(mergedFileName));
|
||||
} catch (Exception e) {
|
||||
|
||||
+3
-3
@@ -31,6 +31,7 @@ import stirling.software.SPDF.model.PipelineOperation;
|
||||
import stirling.software.SPDF.model.PipelineResult;
|
||||
import stirling.software.SPDF.model.api.HandleDataRequest;
|
||||
import stirling.software.common.service.PostHogService;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -98,9 +99,8 @@ public class PipelineController {
|
||||
// Check if the filename already exists, and modify it if necessary
|
||||
if (filenameCount.containsKey(originalFilename)) {
|
||||
int count = filenameCount.get(originalFilename);
|
||||
String baseName = originalFilename.replaceAll("\\.[^.]*$", "");
|
||||
String extension = originalFilename.replaceAll("^.*\\.", "");
|
||||
filename = baseName + "(" + count + ")." + extension;
|
||||
assert originalFilename != null;
|
||||
filename = GeneralUtils.generateFilename(originalFilename, "(" + count + ")");
|
||||
filenameCount.put(originalFilename, count + 1);
|
||||
} else {
|
||||
filenameCount.put(originalFilename, 1);
|
||||
|
||||
+6
-10
@@ -57,14 +57,9 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
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.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import io.github.pixee.security.Filenames;
|
||||
import io.micrometer.common.util.StringUtils;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
@@ -75,6 +70,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.SPDF.model.api.security.SignPDFWithCertRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -214,10 +210,10 @@ public class CertSignController {
|
||||
location,
|
||||
reason,
|
||||
showLogo);
|
||||
return WebResponseUtils.baosToWebResponse(
|
||||
baos,
|
||||
Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_signed.pdf");
|
||||
// Return the signed PDF
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
baos.toByteArray(),
|
||||
GeneralUtils.generateFilename(pdf.getOriginalFilename(), "_signed.pdf"));
|
||||
}
|
||||
|
||||
private PrivateKey getPrivateKeyFromPEM(byte[] pemBytes, String password)
|
||||
|
||||
+7
-2
@@ -66,6 +66,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -224,9 +225,13 @@ public class GetInfoOnPDF {
|
||||
|
||||
// Number of words, paragraphs, and images in the entire document
|
||||
String fullText = new PDFTextStripper().getText(pdfBoxDoc);
|
||||
String[] words = fullText.split("\\s+");
|
||||
String[] words = RegexPatternUtils.getInstance().getWhitespacePattern().split(fullText);
|
||||
int wordCount = words.length;
|
||||
int paragraphCount = fullText.split("\r\n|\r|\n").length;
|
||||
int paragraphCount =
|
||||
RegexPatternUtils.getInstance()
|
||||
.getMultiFormatNewlinePattern()
|
||||
.split(fullText)
|
||||
.length;
|
||||
basicInfo.put("WordCount", wordCount);
|
||||
basicInfo.put("ParagraphCount", paragraphCount);
|
||||
// Number of characters in the entire document (including spaces and special characters)
|
||||
|
||||
+6
-10
@@ -13,7 +13,6 @@ 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;
|
||||
|
||||
@@ -23,6 +22,7 @@ import stirling.software.SPDF.model.api.security.AddPasswordRequest;
|
||||
import stirling.software.SPDF.model.api.security.PDFPasswordRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.ExceptionUtils;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -49,9 +49,8 @@ public class PasswordController {
|
||||
document.setAllSecurityToBeRemoved(true);
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_password_removed.pdf");
|
||||
GeneralUtils.generateFilename(
|
||||
fileInput.getOriginalFilename(), "_password_removed.pdf"));
|
||||
} catch (IOException e) {
|
||||
document.close();
|
||||
ExceptionUtils.logException("password removal", e);
|
||||
@@ -104,13 +103,10 @@ public class PasswordController {
|
||||
if ("".equals(ownerPassword) && "".equals(password))
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_permissions.pdf");
|
||||
GeneralUtils.generateFilename(
|
||||
fileInput.getOriginalFilename(), "_permissions.pdf"));
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(fileInput.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_passworded.pdf");
|
||||
GeneralUtils.generateFilename(fileInput.getOriginalFilename(), "_passworded.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -89,7 +89,7 @@ public class RedactController {
|
||||
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
||||
|
||||
private String removeFileExtension(String filename) {
|
||||
return filename.replaceFirst("[.][^.]+$", "");
|
||||
return GeneralUtils.removeExtension(filename);
|
||||
}
|
||||
|
||||
@InitBinder
|
||||
|
||||
+2
-3
@@ -15,7 +15,6 @@ 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;
|
||||
|
||||
@@ -23,6 +22,7 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.common.model.api.PDFFile;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -65,7 +65,6 @@ public class RemoveCertSignController {
|
||||
// Return the modified PDF as a response
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdf.getOriginalFilename()).replaceFirst("[.][^.]+$", "")
|
||||
+ "_unsigned.pdf");
|
||||
GeneralUtils.generateFilename(pdf.getOriginalFilename(), "_unsigned.pdf"));
|
||||
}
|
||||
}
|
||||
|
||||
+10
-6
@@ -1,5 +1,6 @@
|
||||
package stirling.software.SPDF.controller.api.security;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.pdfbox.cos.COSDictionary;
|
||||
@@ -29,7 +30,6 @@ 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;
|
||||
|
||||
@@ -37,6 +37,7 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.SPDF.model.api.security.SanitizePdfRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -88,11 +89,14 @@ public class SanitizeController {
|
||||
sanitizeFonts(document);
|
||||
}
|
||||
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(inputFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_sanitized.pdf");
|
||||
// Save the sanitized document to output stream
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
document.save(outputStream);
|
||||
document.close();
|
||||
|
||||
return WebResponseUtils.bytesToWebResponse(
|
||||
outputStream.toByteArray(),
|
||||
GeneralUtils.generateFilename(inputFile.getOriginalFilename(), "_sanitized.pdf"));
|
||||
}
|
||||
|
||||
private void sanitizeJavaScript(PDDocument document) throws IOException {
|
||||
|
||||
+6
-5
@@ -34,7 +34,6 @@ 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;
|
||||
|
||||
@@ -42,7 +41,9 @@ import lombok.RequiredArgsConstructor;
|
||||
|
||||
import stirling.software.SPDF.model.api.security.AddWatermarkRequest;
|
||||
import stirling.software.common.service.CustomPDFDocumentFactory;
|
||||
import stirling.software.common.util.GeneralUtils;
|
||||
import stirling.software.common.util.PdfUtils;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
import stirling.software.common.util.WebResponseUtils;
|
||||
|
||||
@RestController
|
||||
@@ -149,11 +150,10 @@ public class WatermarkController {
|
||||
document = convertedPdf;
|
||||
}
|
||||
|
||||
// Return the watermarked PDF as a response
|
||||
return WebResponseUtils.pdfDocToWebResponse(
|
||||
document,
|
||||
Filenames.toSimpleFileName(pdfFile.getOriginalFilename())
|
||||
.replaceFirst("[.][^.]+$", "")
|
||||
+ "_watermarked.pdf");
|
||||
GeneralUtils.generateFilename(pdfFile.getOriginalFilename(), "_watermarked.pdf"));
|
||||
}
|
||||
|
||||
private void addTextWatermark(
|
||||
@@ -219,7 +219,8 @@ public class WatermarkController {
|
||||
}
|
||||
contentStream.setNonStrokingColor(redactColor);
|
||||
|
||||
String[] textLines = watermarkText.split("\\\\n");
|
||||
String[] textLines =
|
||||
RegexPatternUtils.getInstance().getEscapedNewlinePattern().split(watermarkText);
|
||||
float maxLineWidth = 0;
|
||||
|
||||
for (int i = 0; i < textLines.length; ++i) {
|
||||
|
||||
+34
-22
@@ -1,7 +1,6 @@
|
||||
package stirling.software.SPDF.controller.web;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -17,31 +16,44 @@ public class UploadLimitService {
|
||||
@Autowired private ApplicationProperties applicationProperties;
|
||||
|
||||
public long getUploadLimit() {
|
||||
String maxUploadSize =
|
||||
String raw =
|
||||
applicationProperties.getSystem().getFileUploadLimit() != null
|
||||
? applicationProperties.getSystem().getFileUploadLimit()
|
||||
: "";
|
||||
|
||||
if (maxUploadSize.isEmpty()) {
|
||||
return 0;
|
||||
} else if (!Pattern.compile("^[1-9][0-9]{0,2}[KMGkmg][Bb]$")
|
||||
.matcher(maxUploadSize)
|
||||
.matches()) {
|
||||
log.error(
|
||||
"Invalid maxUploadSize format. Expected format: [1-9][0-9]{0,2}[KMGkmg][Bb], but got: {}",
|
||||
maxUploadSize);
|
||||
return 0;
|
||||
} else {
|
||||
String unit = maxUploadSize.replaceAll("[1-9][0-9]{0,2}", "").toUpperCase();
|
||||
String number = maxUploadSize.replaceAll("[KMGkmg][Bb]", "");
|
||||
long size = Long.parseLong(number);
|
||||
return switch (unit) {
|
||||
case "KB" -> size * 1024;
|
||||
case "MB" -> size * 1024 * 1024;
|
||||
case "GB" -> size * 1024 * 1024 * 1024;
|
||||
default -> 0;
|
||||
};
|
||||
if (raw == null || raw.isEmpty()) {
|
||||
return 0L;
|
||||
}
|
||||
String s = raw.trim();
|
||||
// Normalize case for unit parsing
|
||||
String upper = s.toUpperCase(Locale.ROOT);
|
||||
// Expect strictly: 0-999 followed by KB/MB/GB
|
||||
// Find last two chars as unit if length >= 3
|
||||
if (upper.length() < 3) return 0L;
|
||||
String unit = upper.substring(upper.length() - 2);
|
||||
if (!unit.equals("KB") && !unit.equals("MB") && !unit.equals("GB")) {
|
||||
return 0L;
|
||||
}
|
||||
String numPart = upper.substring(0, upper.length() - 2);
|
||||
// Disallow signs, decimals, spaces; only 1-3 digits (allow 0)
|
||||
if (numPart.length() > 3) {
|
||||
return 0L;
|
||||
}
|
||||
for (int i = 0; i < numPart.length(); i++) {
|
||||
char c = numPart.charAt(i);
|
||||
if (c < '0' || c > '9') return 0L;
|
||||
}
|
||||
long value;
|
||||
try {
|
||||
value = Long.parseLong(numPart);
|
||||
} catch (NumberFormatException e) {
|
||||
return 0L;
|
||||
}
|
||||
return switch (unit) {
|
||||
case "KB" -> value * 1024L;
|
||||
case "MB" -> value * 1024L * 1024L;
|
||||
case "GB" -> value * 1024L * 1024L * 1024L;
|
||||
default -> 0L;
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: why do this server side not client?
|
||||
|
||||
@@ -14,6 +14,7 @@ import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.SPDF.model.PDFText;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
|
||||
@Slf4j
|
||||
public class TextFinder extends PDFTextStripper {
|
||||
@@ -84,7 +85,8 @@ public class TextFinder extends PDFTextStripper {
|
||||
}
|
||||
}
|
||||
|
||||
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
// Use cached pattern compilation for better performance
|
||||
Pattern pattern = RegexPatternUtils.getInstance().createSearchPattern(regex, true);
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
|
||||
log.debug(
|
||||
|
||||
@@ -5,7 +5,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
@@ -26,6 +25,7 @@ import stirling.software.SPDF.SPDFApplication;
|
||||
import stirling.software.SPDF.model.ApiEndpoint;
|
||||
import stirling.software.common.model.enumeration.Role;
|
||||
import stirling.software.common.service.UserServiceInterface;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@@ -82,13 +82,11 @@ public class ApiDocService {
|
||||
}
|
||||
ApiEndpoint endpoint = apiDocumentation.get(operationName);
|
||||
String description = endpoint.getDescription();
|
||||
Pattern pattern = null;
|
||||
if (output) {
|
||||
pattern = Pattern.compile("Output:(\\w+)");
|
||||
} else {
|
||||
pattern = Pattern.compile("Input:(\\w+)");
|
||||
}
|
||||
Matcher matcher = pattern.matcher(description);
|
||||
Matcher matcher =
|
||||
(output
|
||||
? RegexPatternUtils.getInstance().getApiDocOutputTypePattern()
|
||||
: RegexPatternUtils.getInstance().getApiDocInputTypePattern())
|
||||
.matcher(description);
|
||||
while (matcher.find()) {
|
||||
String type = matcher.group(1).toUpperCase();
|
||||
if (outputToFileTypes.containsKey(type)) {
|
||||
@@ -157,8 +155,8 @@ public class ApiDocService {
|
||||
}
|
||||
ApiEndpoint endpoint = apiDocumentation.get(operationName);
|
||||
String description = endpoint.getDescription();
|
||||
Pattern pattern = Pattern.compile("Type:(\\w+)");
|
||||
Matcher matcher = pattern.matcher(description);
|
||||
Matcher matcher =
|
||||
RegexPatternUtils.getInstance().getApiDocTypePattern().matcher(description);
|
||||
if (matcher.find()) {
|
||||
String type = matcher.group(1);
|
||||
return type.startsWith("MI");
|
||||
|
||||
@@ -9,6 +9,8 @@ import org.apache.pdfbox.pdmodel.font.encoding.Encoding;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
|
||||
@Slf4j
|
||||
public class TextEncodingHelper {
|
||||
|
||||
@@ -322,7 +324,7 @@ public class TextEncodingHelper {
|
||||
if (fontName == null) {
|
||||
return false;
|
||||
}
|
||||
return fontName.matches("^[A-Z]{6}\\+.*");
|
||||
return RegexPatternUtils.getInstance().getFontNamePattern().matcher(fontName).matches();
|
||||
}
|
||||
|
||||
public static boolean canCalculateBasicWidths(PDFont font) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import org.apache.pdfbox.pdmodel.PDResources;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
|
||||
@Slf4j
|
||||
public class TextFinderUtils {
|
||||
|
||||
@@ -69,9 +71,9 @@ public class TextFinderUtils {
|
||||
patternString = applyWordBoundaries(term.trim(), patternString);
|
||||
}
|
||||
|
||||
// Use PatternFactory for better performance with cached compilation
|
||||
Pattern pattern =
|
||||
Pattern.compile(
|
||||
patternString, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
RegexPatternUtils.getInstance().createSearchPattern(patternString, true);
|
||||
patterns.add(pattern);
|
||||
|
||||
log.debug("Created search pattern: '{}' -> '{}'", term.trim(), patternString);
|
||||
|
||||
@@ -26,6 +26,7 @@ import stirling.software.common.model.job.ResultFile;
|
||||
import stirling.software.common.service.FileStorage;
|
||||
import stirling.software.common.service.JobQueue;
|
||||
import stirling.software.common.service.TaskManager;
|
||||
import stirling.software.common.util.RegexPatternUtils;
|
||||
|
||||
/** REST controller for job-related endpoints */
|
||||
@RestController
|
||||
@@ -319,8 +320,10 @@ public class JobController {
|
||||
private String createContentDispositionHeader(String fileName) {
|
||||
try {
|
||||
String encodedFileName =
|
||||
URLEncoder.encode(fileName, StandardCharsets.UTF_8)
|
||||
.replace("+", "%20"); // URLEncoder uses + for spaces, but we want %20
|
||||
RegexPatternUtils.getInstance()
|
||||
.getPlusSignPattern()
|
||||
.matcher(URLEncoder.encode(fileName, StandardCharsets.UTF_8))
|
||||
.replaceAll("%20"); // URLEncoder uses + for spaces, but we want %20
|
||||
return "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + encodedFileName;
|
||||
} catch (Exception e) {
|
||||
// Fallback to basic filename if encoding fails
|
||||
|
||||
Reference in New Issue
Block a user