mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-12 17:52:13 +02:00
compressTest
This commit is contained in:
parent
b0836d0bd7
commit
f20ce0ed04
1
.gitignore
vendored
1
.gitignore
vendored
@ -25,6 +25,7 @@ clientWebUI/
|
|||||||
!cucumber/
|
!cucumber/
|
||||||
!cucumber/exampleFiles/
|
!cucumber/exampleFiles/
|
||||||
!cucumber/exampleFiles/example_html.zip
|
!cucumber/exampleFiles/example_html.zip
|
||||||
|
testing/file_snapshots
|
||||||
exampleYmlFiles/stirling/
|
exampleYmlFiles/stirling/
|
||||||
|
|
||||||
# Gradle
|
# Gradle
|
||||||
|
@ -3,8 +3,11 @@ package stirling.software.SPDF.controller.api.misc;
|
|||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -41,6 +44,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import stirling.software.SPDF.model.api.misc.OptimizePdfRequest;
|
import stirling.software.SPDF.model.api.misc.OptimizePdfRequest;
|
||||||
import stirling.software.SPDF.service.CustomPDDocumentFactory;
|
import stirling.software.SPDF.service.CustomPDDocumentFactory;
|
||||||
import stirling.software.SPDF.utils.GeneralUtils;
|
import stirling.software.SPDF.utils.GeneralUtils;
|
||||||
|
import stirling.software.SPDF.utils.ImageProcessingUtils;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||||
import stirling.software.SPDF.utils.WebResponseUtils;
|
import stirling.software.SPDF.utils.WebResponseUtils;
|
||||||
@ -68,8 +72,10 @@ public class CompressController {
|
|||||||
jpegQuality,
|
jpegQuality,
|
||||||
GeneralUtils.formatBytes(originalFileSize));
|
GeneralUtils.formatBytes(originalFileSize));
|
||||||
|
|
||||||
// Track processed images to avoid recompression
|
// Track both original and compressed image hashes
|
||||||
Set<String> processedImages = new HashSet<>();
|
// This prevents both reprocessing the same original image
|
||||||
|
// and further compressing an already compressed image
|
||||||
|
Set<String> processedImageHashes = new HashSet<>();
|
||||||
|
|
||||||
try (PDDocument doc = pdfDocumentFactory.load(fileBytes)) {
|
try (PDDocument doc = pdfDocumentFactory.load(fileBytes)) {
|
||||||
int totalImages = 0;
|
int totalImages = 0;
|
||||||
@ -79,8 +85,8 @@ public class CompressController {
|
|||||||
long totalCompressedBytes = 0;
|
long totalCompressedBytes = 0;
|
||||||
|
|
||||||
// Minimum dimensions to preserve reasonable quality
|
// Minimum dimensions to preserve reasonable quality
|
||||||
int MIN_WIDTH = 400; // Higher minimum
|
int MIN_WIDTH = 400;
|
||||||
int MIN_HEIGHT = 400; // Higher minimum
|
int MIN_HEIGHT = 400;
|
||||||
|
|
||||||
log.info("PDF has {} pages", doc.getNumberOfPages());
|
log.info("PDF has {} pages", doc.getNumberOfPages());
|
||||||
|
|
||||||
@ -96,13 +102,6 @@ public class CompressController {
|
|||||||
|
|
||||||
for (COSName name : res.getXObjectNames()) {
|
for (COSName name : res.getXObjectNames()) {
|
||||||
String imageName = name.getName();
|
String imageName = name.getName();
|
||||||
|
|
||||||
// Skip already processed images
|
|
||||||
if (processedImages.contains(imageName)) {
|
|
||||||
skippedImages++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
PDXObject xobj = res.getXObject(name);
|
PDXObject xobj = res.getXObject(name);
|
||||||
if (!(xobj instanceof PDImageXObject)) {
|
if (!(xobj instanceof PDImageXObject)) {
|
||||||
continue;
|
continue;
|
||||||
@ -111,6 +110,21 @@ public class CompressController {
|
|||||||
totalImages++;
|
totalImages++;
|
||||||
pageImages++;
|
pageImages++;
|
||||||
PDImageXObject image = (PDImageXObject) xobj;
|
PDImageXObject image = (PDImageXObject) xobj;
|
||||||
|
|
||||||
|
// Generate a hash for the image data
|
||||||
|
String imageHash = generateImageHash(image);
|
||||||
|
|
||||||
|
// Skip if this image has already been processed (by hash)
|
||||||
|
if (processedImageHashes.contains(imageHash)) {
|
||||||
|
skippedImages++;
|
||||||
|
log.info(
|
||||||
|
"Page {}, Image {}: Skipping - already processed (hash: {})",
|
||||||
|
pageNum + 1,
|
||||||
|
imageName,
|
||||||
|
imageHash);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
BufferedImage bufferedImage = image.getImage();
|
BufferedImage bufferedImage = image.getImage();
|
||||||
|
|
||||||
int originalWidth = bufferedImage.getWidth();
|
int originalWidth = bufferedImage.getWidth();
|
||||||
@ -130,7 +144,8 @@ public class CompressController {
|
|||||||
pageNum + 1,
|
pageNum + 1,
|
||||||
imageName);
|
imageName);
|
||||||
skippedImages++;
|
skippedImages++;
|
||||||
processedImages.add(imageName);
|
// Add to processed set so we don't try to process it again
|
||||||
|
processedImageHashes.add(imageHash);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +184,7 @@ public class CompressController {
|
|||||||
pageNum + 1,
|
pageNum + 1,
|
||||||
imageName);
|
imageName);
|
||||||
skippedImages++;
|
skippedImages++;
|
||||||
processedImages.add(imageName);
|
processedImageHashes.add(imageHash);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,7 +313,7 @@ public class CompressController {
|
|||||||
totalOriginalBytes += originalEncodedSize;
|
totalOriginalBytes += originalEncodedSize;
|
||||||
totalCompressedBytes += originalEncodedSize;
|
totalCompressedBytes += originalEncodedSize;
|
||||||
skippedImages++;
|
skippedImages++;
|
||||||
processedImages.add(imageName);
|
processedImageHashes.add(imageHash);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
log.info(
|
log.info(
|
||||||
@ -319,7 +334,21 @@ public class CompressController {
|
|||||||
totalOriginalBytes += originalEncodedSize;
|
totalOriginalBytes += originalEncodedSize;
|
||||||
totalCompressedBytes += imageBytes.length;
|
totalCompressedBytes += imageBytes.length;
|
||||||
compressedImages++;
|
compressedImages++;
|
||||||
processedImages.add(imageName);
|
|
||||||
|
// Add the hash of the original image to the processed set
|
||||||
|
processedImageHashes.add(imageHash);
|
||||||
|
|
||||||
|
// IMPORTANT: Also add the hash of the compressed image
|
||||||
|
// This prevents recompressing an already compressed image if it appears again
|
||||||
|
String compressedImageHash = generateHashFromBytes(imageBytes);
|
||||||
|
processedImageHashes.add(compressedImageHash);
|
||||||
|
|
||||||
|
log.info(
|
||||||
|
"Page {}, Image {}: Added original hash {} and compressed hash {} to tracking set",
|
||||||
|
pageNum + 1,
|
||||||
|
imageName,
|
||||||
|
imageHash,
|
||||||
|
compressedImageHash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,6 +384,53 @@ public class CompressController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String bytesToHexString(byte[] bytes) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : bytes) {
|
||||||
|
sb.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] generatMD5(byte[] data) throws IOException {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
return md.digest(data); // Get the MD5 hash of the image bytes
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException("MD5 algorithm not available", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] generateImageMD5(PDImageXObject image) throws IOException {
|
||||||
|
return generatMD5(ImageProcessingUtils.getImageData(image.getImage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateImageHash(PDImageXObject image) {
|
||||||
|
try {
|
||||||
|
// Use the existing method to generate MD5 hash
|
||||||
|
byte[] hash = generateImageMD5(image);
|
||||||
|
return bytesToHexString(hash);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error generating image hash", e);
|
||||||
|
// Return a unique string based on object identifier to ensure the image is
|
||||||
|
// still tracked
|
||||||
|
return "fallback-" + System.identityHashCode(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generates a hash string from a byte array */
|
||||||
|
private String generateHashFromBytes(byte[] data) {
|
||||||
|
try {
|
||||||
|
// Use the existing method to generate MD5 hash
|
||||||
|
byte[] hash = generatMD5(data);
|
||||||
|
return bytesToHexString(hash);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error generating hash from bytes", e);
|
||||||
|
// Return a unique string as fallback
|
||||||
|
return "fallback-" + System.identityHashCode(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Updated scale factor method for levels 4-9
|
// Updated scale factor method for levels 4-9
|
||||||
private double getScaleFactorForLevel(int optimizeLevel) {
|
private double getScaleFactorForLevel(int optimizeLevel) {
|
||||||
return switch (optimizeLevel) {
|
return switch (optimizeLevel) {
|
||||||
|
Loading…
Reference in New Issue
Block a user