diff --git a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java index 225a2c12..91cbb5cd 100644 --- a/src/main/java/stirling/software/SPDF/utils/PdfUtils.java +++ b/src/main/java/stirling/software/SPDF/utils/PdfUtils.java @@ -1,27 +1,27 @@ package stirling.software.SPDF.utils; -import java.awt.Graphics; +import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; +import java.util.HashMap; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import javax.imageio.IIOImage; -import javax.imageio.ImageIO; -import javax.imageio.ImageReader; -import javax.imageio.ImageWriteParam; -import javax.imageio.ImageWriter; +import javax.imageio.*; import javax.imageio.stream.ImageOutputStream; import org.apache.pdfbox.Loader; import org.apache.pdfbox.cos.COSName; -import org.apache.pdfbox.pdmodel.*; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.PDPage; +import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDPageContentStream.AppendMode; +import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.graphics.PDXObject; import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; @@ -245,19 +245,64 @@ public class PdfUtils { writer.dispose(); } else { // Combine all images into a single big image - BufferedImage image = pdfRenderer.renderImageWithDPI(0, DPI, colorType); + + // Calculate the combined image dimensions + int maxWidth = 0; + int totalHeight = 0; + + BufferedImage pdfSizeImage = null; + int pdfSizeImageIndex = -1; + + // Using a map to store the rendered dimensions of each page size + // to avoid rendering the same page sizes multiple times + HashMap pageSizes = + new HashMap<>(); + for (int i = 0; i < pageCount; ++i) { + PDPage page = document.getPage(i); + PDRectangle mediaBox = page.getMediaBox(); + int rotation = page.getRotation(); + PdfRenderSettingsKey settings = + new PdfRenderSettingsKey( + mediaBox.getWidth(), mediaBox.getHeight(), rotation); + PdfImageDimensionValue dimension = pageSizes.get(settings); + if (dimension == null) { + // Render the image to get the dimensions + pdfSizeImage = pdfRenderer.renderImageWithDPI(i, DPI, colorType); + pdfSizeImageIndex = i; + dimension = + new PdfImageDimensionValue( + pdfSizeImage.getWidth(), pdfSizeImage.getHeight()); + pageSizes.put(settings, dimension); + if (pdfSizeImage.getWidth() > maxWidth) { + maxWidth = pdfSizeImage.getWidth(); + } + } + totalHeight += dimension.height(); + } + + // Create a new BufferedImage to store the combined images BufferedImage combined = - new BufferedImage( - image.getWidth(), - image.getHeight() * pageCount, - BufferedImage.TYPE_INT_RGB); + prepareImageForPdfToImage(maxWidth, totalHeight, imageType); Graphics g = combined.getGraphics(); + int currentHeight = 0; + BufferedImage pageImage; + + // Check if the first image is the last rendered image + boolean firstImageAlreadyRendered = pdfSizeImageIndex == 0; + for (int i = 0; i < pageCount; ++i) { - if (i != 0) { - image = pdfRenderer.renderImageWithDPI(i, DPI, colorType); + if (firstImageAlreadyRendered && i == 0) { + pageImage = pdfSizeImage; + } else { + pageImage = pdfRenderer.renderImageWithDPI(i, DPI, colorType); } - g.drawImage(image, 0, i * image.getHeight(), null); + + // Calculate the x-coordinate to center the image + int x = (maxWidth - pageImage.getWidth()) / 2; + + g.drawImage(pageImage, x, currentHeight, null); + currentHeight += pageImage.getHeight(); } // Write the image to the output stream @@ -296,6 +341,23 @@ public class PdfUtils { } } + private static BufferedImage prepareImageForPdfToImage( + int maxWidth, int height, String imageType) { + BufferedImage combined; + if ("png".equalsIgnoreCase(imageType)) { + combined = new BufferedImage(maxWidth, height, BufferedImage.TYPE_INT_ARGB); + } else { + combined = new BufferedImage(maxWidth, height, BufferedImage.TYPE_INT_RGB); + } + if (!"png".equalsIgnoreCase(imageType)) { + Graphics g = combined.getGraphics(); + g.setColor(Color.WHITE); + g.fillRect(0, 0, combined.getWidth(), combined.getHeight()); + g.dispose(); + } + return combined; + } + public static byte[] imageToPdf( MultipartFile[] files, String fitOption, boolean autoRotate, String colorType) throws IOException { @@ -443,4 +505,10 @@ public class PdfUtils { pdf.getDocumentInformation().setCreationDate(pdfMetadata.getCreationDate()); pdf.getDocumentInformation().setModificationDate(Calendar.getInstance()); } + + /** Key for storing the dimensions of a rendered image in a map. */ + private record PdfRenderSettingsKey(float mediaBoxWidth, float mediaBoxHeight, int rotation) {} + + /** Value for storing the dimensions of a rendered image in a map. */ + private record PdfImageDimensionValue(int width, int height) {} }