diff --git a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java index a2f325bd8..bd6ea152e 100644 --- a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java +++ b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -48,6 +48,22 @@ public class EndpointConfiguration { return endpointStatuses.getOrDefault(endpoint, true); } + public boolean isGroupEnabled(String group) { + Set endpoints = endpointGroups.get(group); + if (endpoints == null || endpoints.isEmpty()) { + log.debug("Group '{}' does not exist or has no endpoints", group); + return false; + } + + for (String endpoint : endpoints) { + if (!isEndpointEnabled(endpoint)) { + return false; + } + } + + return true; + } + public void addEndpointToGroup(String group, String endpoint) { endpointGroups.computeIfAbsent(group, k -> new HashSet<>()).add(endpoint); } diff --git a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java index 7d1985cec..4b2f410e7 100644 --- a/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java +++ b/src/main/java/stirling/software/SPDF/controller/api/misc/CompressController.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -46,7 +47,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; - +import stirling.software.SPDF.config.EndpointConfiguration; import stirling.software.SPDF.model.api.misc.OptimizePdfRequest; import stirling.software.SPDF.service.CustomPDFDocumentFactory; import stirling.software.SPDF.utils.GeneralUtils; @@ -62,12 +63,14 @@ import stirling.software.SPDF.utils.WebResponseUtils; public class CompressController { private final CustomPDFDocumentFactory pdfDocumentFactory; - - @Autowired - public CompressController(CustomPDFDocumentFactory pdfDocumentFactory) { + private final boolean qpdfEnabled; + + public CompressController(CustomPDFDocumentFactory pdfDocumentFactory, EndpointConfiguration endpointConfiguration) { this.pdfDocumentFactory = pdfDocumentFactory; + this.qpdfEnabled = endpointConfiguration.isGroupEnabled("qpdf"); } + @Data @AllArgsConstructor @NoArgsConstructor @@ -478,17 +481,17 @@ public class CompressController { } // Create initial input file - Path originalFile = Files.createTempFile("input_", ".pdf"); + Path originalFile = Files.createTempFile("original_", ".pdf"); inputFile.transferTo(originalFile.toFile()); long inputFileSize = Files.size(originalFile); - // Start with original as current working file - Path currentFile = originalFile; + Path currentFile = Files.createTempFile("working_", ".pdf"); + Files.copy(originalFile, currentFile, StandardCopyOption.REPLACE_EXISTING); // Keep track of all temporary files for cleanup List tempFiles = new ArrayList<>(); tempFiles.add(originalFile); - + tempFiles.add(currentFile); try { if (autoMode) { double sizeReductionRatio = expectedOutputSize / (double) inputFileSize; @@ -498,7 +501,10 @@ public class CompressController { boolean sizeMet = false; boolean imageCompressionApplied = false; boolean qpdfCompressionApplied = false; - + + if(qpdfEnabled && optimizeLevel <= 3) { + optimizeLevel = 4; + } while (!sizeMet && optimizeLevel <= 9) { // Apply image compression for levels 4-9 if ((optimizeLevel >= 4 || Boolean.TRUE.equals(convertToGrayscale)) @@ -520,7 +526,7 @@ public class CompressController { } // Apply QPDF compression for all levels - if (!qpdfCompressionApplied) { + if (!qpdfCompressionApplied && qpdfEnabled) { long preQpdfSize = Files.size(currentFile); log.info("Pre-QPDF file size: {}", GeneralUtils.formatBytes(preQpdfSize)); @@ -572,6 +578,12 @@ public class CompressController { // If QPDF fails, keep using the current file log.warn("QPDF compression failed, continuing with current file"); } + } else if (!qpdfCompressionApplied) { + // If QPDF is disabled, mark as applied and log + if (!qpdfEnabled) { + log.info("Skipping QPDF compression as QPDF group is disabled"); + } + qpdfCompressionApplied = true; } // Check if file size is within expected size or not auto mode diff --git a/src/main/java/stirling/software/SPDF/service/CustomPDFDocumentFactory.java b/src/main/java/stirling/software/SPDF/service/CustomPDFDocumentFactory.java index 354324744..9f04ab70d 100644 --- a/src/main/java/stirling/software/SPDF/service/CustomPDFDocumentFactory.java +++ b/src/main/java/stirling/software/SPDF/service/CustomPDFDocumentFactory.java @@ -77,7 +77,7 @@ public class CustomPDFDocumentFactory { } long fileSize = file.length(); - log.info("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024)); + log.debug("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024)); return loadAdaptively(file, fileSize); } @@ -92,7 +92,7 @@ public class CustomPDFDocumentFactory { } long fileSize = Files.size(path); - log.info("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024)); + log.debug("Loading PDF from file, size: {}MB", fileSize / (1024 * 1024)); return loadAdaptively(path.toFile(), fileSize); } @@ -104,7 +104,7 @@ public class CustomPDFDocumentFactory { } long dataSize = input.length; - log.info("Loading PDF from byte array, size: {}MB", dataSize / (1024 * 1024)); + log.debug("Loading PDF from byte array, size: {}MB", dataSize / (1024 * 1024)); return loadAdaptively(input, dataSize); } @@ -150,7 +150,7 @@ public class CustomPDFDocumentFactory { long actualFreeMemory = maxMemory - usedMemory; // Log memory status - log.info( + log.debug( "Memory status - Free: {}MB ({}%), Used: {}MB, Max: {}MB", actualFreeMemory / (1024 * 1024), String.format("%.2f", freeMemoryPercent), @@ -160,21 +160,21 @@ public class CustomPDFDocumentFactory { // If free memory is critically low, always use file-based caching if (freeMemoryPercent < MIN_FREE_MEMORY_PERCENTAGE || actualFreeMemory < MIN_FREE_MEMORY_BYTES) { - log.info( + log.debug( "Low memory detected ({}%), forcing file-based cache", String.format("%.2f", freeMemoryPercent)); return createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly()); } else if (contentSize < SMALL_FILE_THRESHOLD) { - log.info("Using memory-only cache for small document ({}KB)", contentSize / 1024); + log.debug("Using memory-only cache for small document ({}KB)", contentSize / 1024); return IOUtils.createMemoryOnlyStreamCache(); } else if (contentSize < LARGE_FILE_THRESHOLD) { // For medium files (10-50MB), use a mixed approach - log.info( + log.debug( "Using mixed memory/file cache for medium document ({}MB)", contentSize / (1024 * 1024)); return createScratchFileCacheFunction(MemoryUsageSetting.setupMixed(LARGE_FILE_USAGE)); } else { - log.info("Using file-based cache for large document"); + log.debug("Using file-based cache for large document"); return createScratchFileCacheFunction(MemoryUsageSetting.setupTempFileOnly()); } } @@ -237,7 +237,7 @@ public class CustomPDFDocumentFactory { byte[] bytes, long size, StreamCacheCreateFunction cache, String password) throws IOException { if (size >= SMALL_FILE_THRESHOLD) { - log.info("Writing large byte array to temp file for password-protected PDF"); + log.debug("Writing large byte array to temp file for password-protected PDF"); Path tempFile = createTempFile("pdf-bytes-"); Files.write(tempFile, bytes); @@ -270,7 +270,7 @@ public class CustomPDFDocumentFactory { private PDDocument loadFromBytes(byte[] bytes, long size, StreamCacheCreateFunction cache) throws IOException { if (size >= SMALL_FILE_THRESHOLD) { - log.info("Writing large byte array to temp file"); + log.debug("Writing large byte array to temp file"); Path tempFile = createTempFile("pdf-bytes-"); Files.write(tempFile, bytes); @@ -318,7 +318,7 @@ public class CustomPDFDocumentFactory { // Temp file handling with enhanced logging private Path createTempFile(String prefix) throws IOException { Path file = Files.createTempFile(prefix + tempCounter.incrementAndGet() + "-", ".tmp"); - log.info("Created temp file: {}", file); + log.debug("Created temp file: {}", file); return file; }