2023-01-27 19:23:40 +01:00
|
|
|
package stirling.software.SPDF.utils;
|
|
|
|
|
2023-02-11 15:27:15 +01:00
|
|
|
import java.awt.Graphics;
|
2023-01-27 19:23:40 +01:00
|
|
|
import java.awt.image.BufferedImage;
|
2023-05-10 23:51:01 +02:00
|
|
|
import java.awt.image.BufferedImageOp;
|
|
|
|
import java.awt.image.ColorConvertOp;
|
2023-01-27 19:23:40 +01:00
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.InputStream;
|
2023-04-26 14:18:24 +02:00
|
|
|
import java.net.URLEncoder;
|
|
|
|
import java.nio.charset.StandardCharsets;
|
2023-03-25 23:16:26 +01:00
|
|
|
import java.nio.file.Files;
|
2023-04-22 13:51:01 +02:00
|
|
|
import java.security.KeyPair;
|
|
|
|
import java.security.KeyStore;
|
|
|
|
import java.security.PrivateKey;
|
|
|
|
import java.security.PublicKey;
|
|
|
|
import java.security.cert.Certificate;
|
|
|
|
import java.security.cert.X509Certificate;
|
2023-02-11 15:27:15 +01:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.List;
|
2023-05-20 00:58:54 +02:00
|
|
|
import java.util.Locale;
|
2023-02-11 15:27:15 +01:00
|
|
|
import java.util.zip.ZipEntry;
|
|
|
|
import java.util.zip.ZipOutputStream;
|
2023-01-27 19:23:40 +01:00
|
|
|
|
|
|
|
import javax.imageio.ImageIO;
|
2023-05-10 23:51:01 +02:00
|
|
|
import javax.imageio.ImageReader;
|
2023-01-27 19:23:40 +01:00
|
|
|
|
|
|
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
|
|
|
import org.apache.pdfbox.pdmodel.PDPage;
|
|
|
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
2023-05-10 23:16:02 +02:00
|
|
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
2023-05-10 23:51:01 +02:00
|
|
|
import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory;
|
2023-01-27 19:23:40 +01:00
|
|
|
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
|
|
|
|
import org.apache.pdfbox.rendering.ImageType;
|
|
|
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
2023-02-03 21:26:35 +01:00
|
|
|
import org.springframework.http.HttpHeaders;
|
|
|
|
import org.springframework.http.HttpStatus;
|
|
|
|
import org.springframework.http.MediaType;
|
|
|
|
import org.springframework.http.ResponseEntity;
|
2023-03-25 23:16:26 +01:00
|
|
|
import org.springframework.web.multipart.MultipartFile;
|
2023-02-03 21:26:35 +01:00
|
|
|
|
2023-01-27 19:23:40 +01:00
|
|
|
public class PdfUtils {
|
|
|
|
|
2023-02-11 15:27:15 +01:00
|
|
|
private static final Logger logger = LoggerFactory.getLogger(PdfUtils.class);
|
|
|
|
|
2023-04-22 13:51:01 +02:00
|
|
|
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName) throws IOException {
|
|
|
|
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName);
|
|
|
|
}
|
|
|
|
|
2023-04-26 14:18:24 +02:00
|
|
|
public static ResponseEntity<byte[]> boasToWebResponse(ByteArrayOutputStream baos, String docName, MediaType mediaType) throws IOException {
|
2023-04-29 00:18:10 +02:00
|
|
|
return PdfUtils.bytesToWebResponse(baos.toByteArray(), docName, mediaType);
|
2023-04-26 14:18:24 +02:00
|
|
|
}
|
2023-04-29 00:18:10 +02:00
|
|
|
|
|
|
|
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName, MediaType mediaType) throws IOException {
|
2023-04-22 13:51:01 +02:00
|
|
|
|
|
|
|
// Return the PDF as a response
|
|
|
|
HttpHeaders headers = new HttpHeaders();
|
2023-04-26 14:18:24 +02:00
|
|
|
headers.setContentType(mediaType);
|
2023-04-22 13:51:01 +02:00
|
|
|
headers.setContentLength(bytes.length);
|
2023-04-26 14:18:24 +02:00
|
|
|
String encodedDocName = URLEncoder.encode(docName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
|
|
|
|
headers.setContentDispositionFormData("attachment", encodedDocName);
|
2023-04-22 13:51:01 +02:00
|
|
|
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
|
|
|
|
}
|
2023-04-29 00:18:10 +02:00
|
|
|
|
2023-04-26 14:18:24 +02:00
|
|
|
public static ResponseEntity<byte[]> bytesToWebResponse(byte[] bytes, String docName) throws IOException {
|
|
|
|
return bytesToWebResponse(bytes, docName, MediaType.APPLICATION_PDF);
|
|
|
|
}
|
2023-04-22 13:51:01 +02:00
|
|
|
|
|
|
|
public static byte[] convertFromPdf(byte[] inputStream, String imageType, ImageType colorType, boolean singleImage, int DPI) throws IOException, Exception {
|
|
|
|
try (PDDocument document = PDDocument.load(new ByteArrayInputStream(inputStream))) {
|
|
|
|
PDFRenderer pdfRenderer = new PDFRenderer(document);
|
|
|
|
int pageCount = document.getNumberOfPages();
|
|
|
|
List<BufferedImage> images = new ArrayList<>();
|
|
|
|
|
|
|
|
// Create images of all pages
|
|
|
|
for (int i = 0; i < pageCount; i++) {
|
|
|
|
images.add(pdfRenderer.renderImageWithDPI(i, 300, colorType));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (singleImage) {
|
|
|
|
// Combine all images into a single big image
|
|
|
|
BufferedImage combined = new BufferedImage(images.get(0).getWidth(), images.get(0).getHeight() * pageCount, BufferedImage.TYPE_INT_RGB);
|
|
|
|
Graphics g = combined.getGraphics();
|
|
|
|
for (int i = 0; i < images.size(); i++) {
|
|
|
|
g.drawImage(images.get(i), 0, i * images.get(0).getHeight(), null);
|
|
|
|
}
|
|
|
|
images = Arrays.asList(combined);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a ByteArrayOutputStream to save the image(s) to
|
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
if (singleImage) {
|
|
|
|
// Write the image to the output stream
|
|
|
|
ImageIO.write(images.get(0), imageType, baos);
|
|
|
|
|
|
|
|
// Log that the image was successfully written to the byte array
|
|
|
|
logger.info("Image successfully written to byte array");
|
|
|
|
} else {
|
|
|
|
// Zip the images and return as byte array
|
|
|
|
try (ZipOutputStream zos = new ZipOutputStream(baos)) {
|
|
|
|
for (int i = 0; i < images.size(); i++) {
|
|
|
|
BufferedImage image = images.get(i);
|
|
|
|
try (ByteArrayOutputStream baosImage = new ByteArrayOutputStream()) {
|
|
|
|
ImageIO.write(image, imageType, baosImage);
|
|
|
|
|
|
|
|
// Add the image to the zip file
|
|
|
|
zos.putNextEntry(new ZipEntry(String.format("page_%d.%s", i + 1, imageType.toLowerCase())));
|
|
|
|
zos.write(baosImage.toByteArray());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Log that the images were successfully written to the byte array
|
|
|
|
logger.info("Images successfully written to byte array as a zip");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return baos.toByteArray();
|
|
|
|
} catch (IOException e) {
|
|
|
|
// Log an error message if there is an issue converting the PDF to an image
|
|
|
|
logger.error("Error converting PDF to image", e);
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
2023-05-10 23:51:01 +02:00
|
|
|
public static byte[] imageToPdf(MultipartFile[] files, boolean stretchToFit, boolean autoRotate, String colorType) throws IOException {
|
2023-03-25 23:16:26 +01:00
|
|
|
try (PDDocument doc = new PDDocument()) {
|
|
|
|
for (MultipartFile file : files) {
|
2023-05-10 23:51:01 +02:00
|
|
|
String originalFilename = file.getOriginalFilename();
|
|
|
|
if (originalFilename != null && (originalFilename.toLowerCase().endsWith(".tiff") || originalFilename.toLowerCase().endsWith(".tif")) ) {
|
|
|
|
ImageReader reader = ImageIO.getImageReadersByFormatName("tiff").next();
|
|
|
|
reader.setInput(ImageIO.createImageInputStream(file.getInputStream()));
|
|
|
|
int numPages = reader.getNumImages(true);
|
|
|
|
for (int i = 0; i < numPages; i++) {
|
|
|
|
BufferedImage pageImage = reader.read(i);
|
|
|
|
BufferedImage convertedImage = convertColorType(pageImage, colorType);
|
|
|
|
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
|
|
|
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
|
2023-03-25 23:16:26 +01:00
|
|
|
}
|
2023-05-10 23:51:01 +02:00
|
|
|
} else {
|
|
|
|
File imageFile = Files.createTempFile("image", ".png").toFile();
|
|
|
|
try (FileOutputStream fos = new FileOutputStream(imageFile); InputStream input = file.getInputStream()) {
|
|
|
|
byte[] buffer = new byte[1024];
|
|
|
|
int len;
|
|
|
|
while ((len = input.read(buffer)) != -1) {
|
|
|
|
fos.write(buffer, 0, len);
|
2023-03-25 23:16:26 +01:00
|
|
|
}
|
2023-05-10 23:51:01 +02:00
|
|
|
BufferedImage image = ImageIO.read(imageFile);
|
|
|
|
BufferedImage convertedImage = convertColorType(image, colorType);
|
|
|
|
PDImageXObject pdImage = LosslessFactory.createFromImage(doc, convertedImage);
|
|
|
|
addImageToDocument(doc, pdImage, stretchToFit, autoRotate);
|
2023-05-10 23:16:02 +02:00
|
|
|
} catch (IOException e) {
|
2023-05-10 23:51:01 +02:00
|
|
|
logger.error("Error writing image to file: {}", imageFile.getAbsolutePath(), e);
|
2023-05-10 23:16:02 +02:00
|
|
|
throw e;
|
2023-05-10 23:51:01 +02:00
|
|
|
} finally {
|
|
|
|
imageFile.delete();
|
2023-05-10 23:16:02 +02:00
|
|
|
}
|
|
|
|
}
|
2023-05-10 23:51:01 +02:00
|
|
|
}
|
2023-02-11 15:27:15 +01:00
|
|
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
|
|
doc.save(byteArrayOutputStream);
|
|
|
|
logger.info("PDF successfully saved to byte array");
|
|
|
|
return byteArrayOutputStream.toByteArray();
|
|
|
|
}
|
2023-05-10 23:51:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static BufferedImage convertColorType(BufferedImage sourceImage, String colorType) {
|
|
|
|
BufferedImage convertedImage;
|
|
|
|
switch (colorType) {
|
|
|
|
case "greyscale":
|
|
|
|
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
|
|
|
|
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
|
|
|
|
break;
|
|
|
|
case "blackwhite":
|
|
|
|
convertedImage = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), BufferedImage.TYPE_BYTE_BINARY);
|
|
|
|
convertedImage.getGraphics().drawImage(sourceImage, 0, 0, null);
|
|
|
|
break;
|
|
|
|
default: // full color
|
|
|
|
convertedImage = sourceImage;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return convertedImage;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void addImageToDocument(PDDocument doc, PDImageXObject image, boolean stretchToFit, boolean autoRotate) throws IOException {
|
|
|
|
boolean imageIsLandscape = image.getWidth() > image.getHeight();
|
|
|
|
PDRectangle pageSize = PDRectangle.A4;
|
|
|
|
if (autoRotate && imageIsLandscape) {
|
|
|
|
pageSize = new PDRectangle(pageSize.getHeight(), pageSize.getWidth());
|
|
|
|
}
|
|
|
|
PDPage page = new PDPage(pageSize);
|
|
|
|
doc.addPage(page);
|
|
|
|
|
|
|
|
float pageWidth = page.getMediaBox().getWidth();
|
|
|
|
float pageHeight = page.getMediaBox().getHeight();
|
|
|
|
|
|
|
|
try (PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
|
|
|
|
if (stretchToFit) {
|
|
|
|
contentStream.drawImage(image, 0, 0, pageWidth, pageHeight);
|
|
|
|
} else {
|
|
|
|
float imageAspectRatio = (float) image.getWidth() / (float) image.getHeight();
|
|
|
|
float pageAspectRatio = pageWidth / pageHeight;
|
|
|
|
|
|
|
|
float scaleFactor = 1.0f;
|
|
|
|
if (imageAspectRatio > pageAspectRatio) {
|
|
|
|
scaleFactor = pageWidth / image.getWidth();
|
|
|
|
} else {
|
|
|
|
scaleFactor = pageHeight / image.getHeight();
|
|
|
|
}
|
2023-03-25 23:16:26 +01:00
|
|
|
|
2023-05-10 23:51:01 +02:00
|
|
|
float xPos = (pageWidth - (image.getWidth() * scaleFactor)) / 2;
|
|
|
|
float yPos = (pageHeight - (image.getHeight() * scaleFactor)) / 2;
|
|
|
|
contentStream.drawImage(image, xPos, yPos, image.getWidth() * scaleFactor, image.getHeight() * scaleFactor);
|
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
logger.error("Error adding image to PDF", e);
|
|
|
|
throw e;
|
|
|
|
}
|
2023-02-11 15:27:15 +01:00
|
|
|
}
|
|
|
|
|
2023-04-22 13:51:01 +02:00
|
|
|
public static X509Certificate[] loadCertificateChainFromKeystore(InputStream keystoreInputStream, String keystorePassword) throws Exception {
|
|
|
|
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
|
|
|
keystore.load(keystoreInputStream, keystorePassword.toCharArray());
|
2023-03-25 23:16:26 +01:00
|
|
|
|
2023-04-22 13:51:01 +02:00
|
|
|
String alias = keystore.aliases().nextElement();
|
|
|
|
Certificate[] certChain = keystore.getCertificateChain(alias);
|
|
|
|
X509Certificate[] x509CertChain = new X509Certificate[certChain.length];
|
2023-02-11 15:27:15 +01:00
|
|
|
|
2023-04-22 13:51:01 +02:00
|
|
|
for (int i = 0; i < certChain.length; i++) {
|
|
|
|
x509CertChain[i] = (X509Certificate) certChain[i];
|
|
|
|
}
|
2023-02-11 15:27:15 +01:00
|
|
|
|
2023-04-22 13:51:01 +02:00
|
|
|
return x509CertChain;
|
|
|
|
}
|
2023-02-11 15:27:15 +01:00
|
|
|
|
2023-04-22 13:51:01 +02:00
|
|
|
public static KeyPair loadKeyPairFromKeystore(InputStream keystoreInputStream, String keystorePassword) throws Exception {
|
|
|
|
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
|
|
|
|
keystore.load(keystoreInputStream, keystorePassword.toCharArray());
|
2023-02-11 15:27:15 +01:00
|
|
|
|
2023-04-22 13:51:01 +02:00
|
|
|
String alias = keystore.aliases().nextElement();
|
|
|
|
PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, keystorePassword.toCharArray());
|
|
|
|
Certificate cert = keystore.getCertificate(alias);
|
|
|
|
PublicKey publicKey = cert.getPublicKey();
|
|
|
|
|
|
|
|
return new KeyPair(publicKey, privateKey);
|
2023-02-11 15:27:15 +01:00
|
|
|
}
|
|
|
|
|
2023-04-11 22:22:59 +02:00
|
|
|
public static byte[] overlayImage(byte[] pdfBytes, byte[] imageBytes, float x, float y, boolean everyPage) throws IOException {
|
|
|
|
|
2023-04-22 13:51:01 +02:00
|
|
|
PDDocument document = PDDocument.load(new ByteArrayInputStream(pdfBytes));
|
|
|
|
|
|
|
|
// Get the first page of the PDF
|
|
|
|
int pages = document.getNumberOfPages();
|
|
|
|
for (int i = 0; i < pages; i++) {
|
|
|
|
PDPage page = document.getPage(i);
|
|
|
|
try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true)) {
|
|
|
|
// Create an image object from the image bytes
|
|
|
|
PDImageXObject image = PDImageXObject.createFromByteArray(document, imageBytes, "");
|
|
|
|
// Draw the image onto the page at the specified x and y coordinates
|
|
|
|
contentStream.drawImage(image, x, y);
|
|
|
|
logger.info("Image successfully overlayed onto PDF");
|
|
|
|
if (!everyPage && i == 0) {
|
|
|
|
break;
|
2023-04-11 22:22:59 +02:00
|
|
|
}
|
2023-04-22 13:51:01 +02:00
|
|
|
} catch (IOException e) {
|
|
|
|
// Log an error message if there is an issue overlaying the image onto the PDF
|
|
|
|
logger.error("Error overlaying image onto PDF", e);
|
|
|
|
throw e;
|
2023-02-11 15:27:15 +01:00
|
|
|
}
|
2023-04-22 13:51:01 +02:00
|
|
|
|
|
|
|
}
|
2023-04-11 22:22:59 +02:00
|
|
|
// Create a ByteArrayOutputStream to save the PDF to
|
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
document.save(baos);
|
|
|
|
logger.info("PDF successfully saved to byte array");
|
|
|
|
return baos.toByteArray();
|
2023-04-22 13:51:01 +02:00
|
|
|
}
|
2023-02-11 15:27:15 +01:00
|
|
|
|
|
|
|
public static ResponseEntity<byte[]> pdfDocToWebResponse(PDDocument document, String docName) throws IOException {
|
|
|
|
|
|
|
|
// Open Byte Array and save document to it
|
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
document.save(baos);
|
|
|
|
// Close the document
|
|
|
|
document.close();
|
|
|
|
|
|
|
|
return PdfUtils.boasToWebResponse(baos, docName);
|
|
|
|
}
|
2023-05-20 00:58:54 +02:00
|
|
|
|
|
|
|
public static Long convertSizeToBytes(String sizeStr) {
|
|
|
|
if (sizeStr == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
sizeStr = sizeStr.trim().toUpperCase();
|
|
|
|
try {
|
|
|
|
if (sizeStr.endsWith("KB")) {
|
|
|
|
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 2)) * 1024;
|
|
|
|
} else if (sizeStr.endsWith("MB")) {
|
|
|
|
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024;
|
|
|
|
} else if (sizeStr.endsWith("GB")) {
|
|
|
|
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 2)) * 1024 * 1024 * 1024;
|
|
|
|
} else if (sizeStr.endsWith("B")) {
|
|
|
|
return Long.parseLong(sizeStr.substring(0, sizeStr.length() - 1));
|
|
|
|
} else {
|
|
|
|
// Input string does not have a valid format, handle this case
|
|
|
|
}
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
// The numeric part of the input string cannot be parsed, handle this case
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-01-27 19:23:40 +01:00
|
|
|
}
|