diff --git a/engines/misc/pom.xml b/engines/misc/pom.xml index d3ce1f47..a5cdf926 100644 --- a/engines/misc/pom.xml +++ b/engines/misc/pom.xml @@ -52,6 +52,13 @@ ${dependency.pdfbox.version} + + + org.apache.commons + commons-imaging + ${dependency.imaging.version} + + org.apache.poi diff --git a/engines/misc/src/main/java/org/alfresco/transform/misc/transformers/ImageToPdfTransformer.java b/engines/misc/src/main/java/org/alfresco/transform/misc/transformers/ImageToPdfTransformer.java index d6167cfb..64515757 100644 --- a/engines/misc/src/main/java/org/alfresco/transform/misc/transformers/ImageToPdfTransformer.java +++ b/engines/misc/src/main/java/org/alfresco/transform/misc/transformers/ImageToPdfTransformer.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * Copyright (C) 2005 - 2024 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -30,6 +30,8 @@ import static org.alfresco.transform.common.RequestParamMap.END_PAGE; import static org.alfresco.transform.common.RequestParamMap.PDF_FORMAT; import static org.alfresco.transform.common.RequestParamMap.PDF_ORIENTATION; import static org.alfresco.transform.common.RequestParamMap.START_PAGE; +import static org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants.TIFF_TAG_XRESOLUTION; +import static org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants.TIFF_TAG_YRESOLUTION; import javax.imageio.ImageIO; import javax.imageio.ImageReader; @@ -45,6 +47,12 @@ import java.util.stream.Stream; import org.alfresco.transform.base.TransformManager; import org.alfresco.transform.base.util.CustomTransformerFileAdaptor; +import org.apache.commons.imaging.Imaging; +import org.apache.commons.imaging.ImagingException; +import org.apache.commons.imaging.common.ImageMetadata; +import org.apache.commons.imaging.formats.tiff.TiffField; +import org.apache.commons.imaging.formats.tiff.TiffImageMetadata; +import org.apache.commons.imaging.formats.tiff.taginfos.TagInfo; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; @@ -75,8 +83,9 @@ public class ImageToPdfTransformer implements CustomTransformerFileAdaptor private static final String START_PAGE_GREATER_THAN_END_PAGE_ERROR_MESSAGE = "Start page number cannot be greater than end page."; private static final String INVALID_OPTION_ERROR_MESSAGE = "Parameter '%s' is invalid: \"%s\" - it must be an integer."; private static final String INVALID_IMAGE_ERROR_MESSAGE = "Image file (%s) format (%s) not supported by ImageIO."; - private static final String DEFAULT_PDF_FORMAT_STRING = "DEFAULT"; + private static final String DEFAULT_PDF_FORMAT_STRING = "DEFAULT"; // pdf format to use when no pdf format specified private static final String DEFAULT_PDF_ORIENTATION_STRING = "DEFAULT"; + private static final float PDFBOX_POINTS_PER_INCH = 72.0F; @Override public String getTransformerName() @@ -99,6 +108,7 @@ public class ImageToPdfTransformer implements CustomTransformerFileAdaptor final String pdfOrientation = parseOptionIfPresent(transformOptions, PDF_ORIENTATION, String.class).orElse(DEFAULT_PDF_ORIENTATION_STRING); verifyOptions(startPage, endPage); + final Map resolution = determineImageResolution(imageFile); final ImageReader imageReader = findImageReader(imageInputStream, imageFile.getName(), sourceMimetype); for (int i = 0; i < imageReader.getNumImages(true); i++) { @@ -111,7 +121,7 @@ public class ImageToPdfTransformer implements CustomTransformerFileAdaptor break; } - scaleAndDrawImage(pdfDocument, imageReader.read(i), pdfFormat, pdfOrientation); + scaleAndDrawImage(pdfDocument, imageReader.read(i), pdfFormat, pdfOrientation, resolution); } pdfDocument.save(pdfFile); @@ -131,11 +141,22 @@ public class ImageToPdfTransformer implements CustomTransformerFileAdaptor return imageReader; } - private void scaleAndDrawImage(final PDDocument pdfDocument, final BufferedImage bufferedImage, final String pdfFormat, final String pdfOrientation) + private void scaleAndDrawImage(final PDDocument pdfDocument, final BufferedImage bufferedImage, final String pdfFormat, final String pdfOrientation, final Map resolution) throws IOException { final PDImageXObject image = LosslessFactory.createFromImage(pdfDocument, bufferedImage); - final PDPage pdfPage = new PDPage(resolvePdfFormat(pdfFormat, pdfOrientation, image.getWidth(), image.getHeight())); + + int imageWidth = image.getWidth(); + int imageHeight = image.getHeight(); + // if the image has a resolution which differs from pdfbox then adjust size in pixels according to pdfbox ppi + if (resolution.get("X") > 0 && resolution.get("X") != PDFBOX_POINTS_PER_INCH && + resolution.get("Y") > 0 && resolution.get("Y") != PDFBOX_POINTS_PER_INCH) + { + imageWidth = (int)(((float)imageWidth / resolution.get("X")) * PDFBOX_POINTS_PER_INCH); + imageHeight = (int)(((float)imageHeight / resolution.get("Y")) * PDFBOX_POINTS_PER_INCH); + } + + final PDPage pdfPage = new PDPage(resolvePdfFormat(pdfFormat, pdfOrientation, imageWidth, imageHeight)); pdfDocument.addPage(pdfPage); try (PDPageContentStream pdfPageContent = new PDPageContentStream(pdfDocument, pdfPage)) { @@ -257,4 +278,44 @@ public class ImageToPdfTransformer implements CustomTransformerFileAdaptor throw new IllegalArgumentException(START_PAGE_GREATER_THAN_END_PAGE_ERROR_MESSAGE); } } + + private static Map determineImageResolution(File imageFile) + { + int xResolution = 0; + int yResolution = 0; + + try + { + final ImageMetadata metadata = Imaging.getMetadata(imageFile); + if (metadata instanceof TiffImageMetadata) + { + final TiffImageMetadata tiffImageMetadata = (TiffImageMetadata) metadata; + xResolution = findMetadataField(tiffImageMetadata, TIFF_TAG_XRESOLUTION); + yResolution = findMetadataField(tiffImageMetadata, TIFF_TAG_YRESOLUTION); + } + } + catch (IOException e) + { + // treat as though no resolution exists + } + return Map.of("X", xResolution, "Y", yResolution); + } + + static private int findMetadataField(TiffImageMetadata tiffImageMetadata, TagInfo tagInfo) + { + int value = 0; + try + { + TiffField field = tiffImageMetadata.findField(tagInfo); + if (field != null) + { + value = field.getIntValue(); + } + } + catch (ImagingException e) + { + // treat as though field not found + } + return value; + } } diff --git a/engines/misc/src/test/java/org/alfresco/transform/misc/transformers/ImageToPdfTransformerTest.java b/engines/misc/src/test/java/org/alfresco/transform/misc/transformers/ImageToPdfTransformerTest.java index 64c81ed3..269ec388 100644 --- a/engines/misc/src/test/java/org/alfresco/transform/misc/transformers/ImageToPdfTransformerTest.java +++ b/engines/misc/src/test/java/org/alfresco/transform/misc/transformers/ImageToPdfTransformerTest.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2005 - 2022 Alfresco Software Limited + * Copyright (C) 2005 - 2024 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -290,6 +290,41 @@ class ImageToPdfTransformerTest } } + static Stream imageFilesOfVariousSizeAndResolution() + { + return Stream.of( + Arguments.of(ImageFile.of("MNT-24205.tiff", MIMETYPE_IMAGE_TIFF), 612.0f, 792.0f), + Arguments.of(ImageFile.of("459x594-50.tif", MIMETYPE_IMAGE_TIFF), 660.0f, 855.0f), + Arguments.of(ImageFile.of("459x594-72.tif", MIMETYPE_IMAGE_TIFF), 459.0f, 594.0f), + Arguments.of(ImageFile.of("459x594-300.tif", MIMETYPE_IMAGE_TIFF), 110.0f, 142.0f), + Arguments.of(ImageFile.of("612x792-50.tif", MIMETYPE_IMAGE_TIFF), 881.0f, 1140.0f), + Arguments.of(ImageFile.of("612x792-72.tif", MIMETYPE_IMAGE_TIFF), 612.0f, 792.0f), + Arguments.of(ImageFile.of("612x792-300.tif", MIMETYPE_IMAGE_TIFF), 146.0f, 190.0f), + Arguments.of(ImageFile.of("765x990-50.tif", MIMETYPE_IMAGE_TIFF), 1101.0f, 1425.0f), + Arguments.of(ImageFile.of("765x990-72.tif", MIMETYPE_IMAGE_TIFF), 765.0f, 990.0f), + Arguments.of(ImageFile.of("765x990-300.tif", MIMETYPE_IMAGE_TIFF), 183.0f, 237.0f) + ); + } + + @ParameterizedTest + @MethodSource("imageFilesOfVariousSizeAndResolution") + void testTransformTiffToPDF_withVariousImageSizes(ImageFile imageFile, float expectedWidth, float expectedHeight) throws Exception + { + TransformOptions transformOptions = TransformOptions.of("DEFAULT"); + + File source = loadFile(imageFile.fileName); + + // when + transformer.transform(imageFile.mimetype, MIMETYPE_PDF, transformOptions.toMap(), source, targetFile, transformManager); + + try (PDDocument actualPdfDocument = PDDocument.load(targetFile)) + { + assertNotNull(actualPdfDocument); + assertEquals(expectedWidth, actualPdfDocument.getPage(0).getMediaBox().getWidth(),"Pdf width"); + assertEquals(expectedHeight,actualPdfDocument.getPage(0).getMediaBox().getHeight(),"Pdf height"); + } + } + //----------------------------------------------- Helper methods and classes ----------------------------------------------- private static BiFunction unchangedRectangle() diff --git a/engines/misc/src/test/resources/459x594-300.tif b/engines/misc/src/test/resources/459x594-300.tif new file mode 100644 index 00000000..c3b64b88 Binary files /dev/null and b/engines/misc/src/test/resources/459x594-300.tif differ diff --git a/engines/misc/src/test/resources/459x594-50.tif b/engines/misc/src/test/resources/459x594-50.tif new file mode 100644 index 00000000..29d6d02b Binary files /dev/null and b/engines/misc/src/test/resources/459x594-50.tif differ diff --git a/engines/misc/src/test/resources/459x594-72.tif b/engines/misc/src/test/resources/459x594-72.tif new file mode 100644 index 00000000..7bc05a79 Binary files /dev/null and b/engines/misc/src/test/resources/459x594-72.tif differ diff --git a/engines/misc/src/test/resources/612x792-300.tif b/engines/misc/src/test/resources/612x792-300.tif new file mode 100644 index 00000000..18fa08d9 Binary files /dev/null and b/engines/misc/src/test/resources/612x792-300.tif differ diff --git a/engines/misc/src/test/resources/612x792-50.tif b/engines/misc/src/test/resources/612x792-50.tif new file mode 100644 index 00000000..cd77e5ae Binary files /dev/null and b/engines/misc/src/test/resources/612x792-50.tif differ diff --git a/engines/misc/src/test/resources/612x792-72.tif b/engines/misc/src/test/resources/612x792-72.tif new file mode 100644 index 00000000..51aea01b Binary files /dev/null and b/engines/misc/src/test/resources/612x792-72.tif differ diff --git a/engines/misc/src/test/resources/765x990-300.tif b/engines/misc/src/test/resources/765x990-300.tif new file mode 100644 index 00000000..e449cafd Binary files /dev/null and b/engines/misc/src/test/resources/765x990-300.tif differ diff --git a/engines/misc/src/test/resources/765x990-50.tif b/engines/misc/src/test/resources/765x990-50.tif new file mode 100644 index 00000000..3f094ae5 Binary files /dev/null and b/engines/misc/src/test/resources/765x990-50.tif differ diff --git a/engines/misc/src/test/resources/765x990-72.tif b/engines/misc/src/test/resources/765x990-72.tif new file mode 100644 index 00000000..a3c85025 Binary files /dev/null and b/engines/misc/src/test/resources/765x990-72.tif differ diff --git a/engines/misc/src/test/resources/MNT-24205.tiff b/engines/misc/src/test/resources/MNT-24205.tiff new file mode 100644 index 00000000..df419d3d Binary files /dev/null and b/engines/misc/src/test/resources/MNT-24205.tiff differ diff --git a/pom.xml b/pom.xml index a9a4017d..eba30cca 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,7 @@ 2.16.1 2.9.2 5.2.5 + 1.0.0-alpha5 2.2 1.78.1 @@ -160,6 +161,12 @@ pdfbox-tools ${dependency.pdfbox.version} + + + org.apache.commons + commons-imaging + ${dependency.imaging.version} + com.fasterxml.jackson