diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ImageHandler.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ImageHandler.java new file mode 100644 index 0000000..e3af199 --- /dev/null +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ImageHandler.java @@ -0,0 +1,123 @@ +package marketing.heyday.hartmann.fotodocumentation.core.utils; + +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.drew.imaging.ImageMetadataReader; +import com.drew.imaging.ImageProcessingException; +import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataException; +import com.drew.metadata.exif.ExifIFD0Directory; + +/** + * + *

Copyright: Copyright (c) 2024

+ *

Company: heyday Marketing GmbH

+ * @author Patrick Verboom + * @version 1.0 + * + * created: 6 Feb 2026 + */ + +public interface ImageHandler { + static final Log LOG = LogFactory.getLog(ImageHandler.class); + + /** + * Reads image bytes and returns a BufferedImage with correct EXIF orientation applied. + */ + default BufferedImage readImageWithCorrectOrientation(byte[] imageBytes) throws IOException { + int orientation = getExifOrientation(imageBytes); + BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageBytes)); + if (image == null) { + throw new IOException("Failed to read image from byte array"); + } + return applyOrientation(image, orientation); + } + + default int getExifOrientation(byte[] imageBytes) { + try { + Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(imageBytes)); + ExifIFD0Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); + if (directory != null && directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) { + return directory.getInt(ExifIFD0Directory.TAG_ORIENTATION); + } + } catch (ImageProcessingException | IOException | MetadataException e) { + LOG.debug("Could not read EXIF orientation: " + e.getMessage()); + } + return 1; + } + + default BufferedImage applyOrientation(BufferedImage image, int orientation) { + int width = image.getWidth(); + int height = image.getHeight(); + + AffineTransform transform = new AffineTransform(); + int newWidth = width; + int newHeight = height; + + switch (orientation) { + case 1 -> { + return image; + } + case 2 -> { + transform.scale(-1.0, 1.0); + transform.translate(-width, 0); + } + case 3 -> { + transform.translate(width, height); + transform.rotate(Math.PI); + } + case 4 -> { + transform.scale(1.0, -1.0); + transform.translate(0, -height); + } + case 5 -> { + newWidth = height; + newHeight = width; + transform.rotate(-Math.PI / 2); + transform.scale(-1.0, 1.0); + } + case 6 -> { + newWidth = height; + newHeight = width; + transform.translate(height, 0); + transform.rotate(Math.PI / 2); + } + case 7 -> { + newWidth = height; + newHeight = width; + transform.scale(-1.0, 1.0); + transform.translate(-height, 0); + transform.translate(0, width); + transform.rotate(3 * Math.PI / 2); + } + case 8 -> { + newWidth = height; + newHeight = width; + transform.translate(0, width); + transform.rotate(-Math.PI / 2); + } + default -> { + return image; + } + } + + BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = rotated.createGraphics(); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2d.drawImage(image, transform, null); + g2d.dispose(); + + return rotated; + } +} diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ImageUtil.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ImageUtil.java index 7725ca8..49f2c68 100644 --- a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ImageUtil.java +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/ImageUtil.java @@ -2,7 +2,6 @@ package marketing.heyday.hartmann.fotodocumentation.core.utils; import java.awt.Graphics2D; import java.awt.RenderingHints; -import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -18,12 +17,6 @@ import javax.imageio.stream.ImageOutputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import com.drew.imaging.ImageMetadataReader; -import com.drew.imaging.ImageProcessingException; -import com.drew.metadata.Metadata; -import com.drew.metadata.MetadataException; -import com.drew.metadata.exif.ExifIFD0Directory; - /** * *

Copyright: Copyright (c) 2024

@@ -34,7 +27,7 @@ import com.drew.metadata.exif.ExifIFD0Directory; * created: 2 Feb 2026 */ -public class ImageUtil { +public class ImageUtil implements ImageHandler { private static final Log LOG = LogFactory.getLog(ImageUtil.class); private static final int NORMAL_MAX_WIDTH = 1200; @@ -130,84 +123,6 @@ public class ImageUtil { } } - private int getExifOrientation(byte[] imageBytes) { - try { - Metadata metadata = ImageMetadataReader.readMetadata(new ByteArrayInputStream(imageBytes)); - ExifIFD0Directory directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); - if (directory != null && directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) { - return directory.getInt(ExifIFD0Directory.TAG_ORIENTATION); - } - } catch (ImageProcessingException | IOException | MetadataException e) { - LOG.debug("Could not read EXIF orientation: " + e.getMessage()); - } - return 1; - } - - private BufferedImage applyOrientation(BufferedImage image, int orientation) { - int width = image.getWidth(); - int height = image.getHeight(); - - AffineTransform transform = new AffineTransform(); - int newWidth = width; - int newHeight = height; - - switch (orientation) { - case 1 -> { - return image; - } - case 2 -> { - transform.scale(-1.0, 1.0); - transform.translate(-width, 0); - } - case 3 -> { - transform.translate(width, height); - transform.rotate(Math.PI); - } - case 4 -> { - transform.scale(1.0, -1.0); - transform.translate(0, -height); - } - case 5 -> { - newWidth = height; - newHeight = width; - transform.rotate(-Math.PI / 2); - transform.scale(-1.0, 1.0); - } - case 6 -> { - newWidth = height; - newHeight = width; - transform.translate(height, 0); - transform.rotate(Math.PI / 2); - } - case 7 -> { - newWidth = height; - newHeight = width; - transform.scale(-1.0, 1.0); - transform.translate(-height, 0); - transform.translate(0, width); - transform.rotate(3 * Math.PI / 2); - } - case 8 -> { - newWidth = height; - newHeight = width; - transform.translate(0, width); - transform.rotate(-Math.PI / 2); - } - default -> { - return image; - } - } - - BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB); - Graphics2D g2d = rotated.createGraphics(); - g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - g2d.drawImage(image, transform, null); - g2d.dispose(); - - return rotated; - } - private byte[] writeJpeg(BufferedImage image, float quality) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next(); diff --git a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtils.java b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtils.java index 39a0237..9cde273 100644 --- a/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtils.java +++ b/hartmann-foto-documentation-app/src/main/java/marketing/heyday/hartmann/fotodocumentation/core/utils/PdfUtils.java @@ -1,6 +1,7 @@ package marketing.heyday.hartmann.fotodocumentation.core.utils; import java.awt.Color; +import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -19,6 +20,7 @@ import org.apache.pdfbox.pdmodel.font.PDFont; import org.apache.pdfbox.pdmodel.font.PDType0Font; import org.apache.pdfbox.pdmodel.font.PDType1Font; import org.apache.pdfbox.pdmodel.font.Standard14Fonts; +import org.apache.pdfbox.pdmodel.graphics.image.LosslessFactory; import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject; import marketing.heyday.hartmann.fotodocumentation.core.model.Customer; @@ -34,7 +36,7 @@ import marketing.heyday.hartmann.fotodocumentation.core.model.Picture; * created: 2 Feb 2026 */ @SuppressWarnings({ "java:S818", "squid:S818", "squid:S109" }) -public class PdfUtils { +public class PdfUtils implements ImageHandler { private static final Log LOG = LogFactory.getLog(PdfUtils.class); private static final String FONT_PANTON_REGULAR = "fonts/Panton-Regular.ttf"; @@ -97,7 +99,8 @@ public class PdfUtils { if (picture.getImage() != null) { try { byte[] imageBytes = Base64.getDecoder().decode(picture.getImage()); - PDImageXObject pdImage = PDImageXObject.createFromByteArray(document, imageBytes, "picture"); + BufferedImage correctedImage = readImageWithCorrectOrientation(imageBytes); + PDImageXObject pdImage = LosslessFactory.createFromImage(document, correctedImage); float scale = Math.min(imageMaxWidth / pdImage.getWidth(), imageMaxHeight / pdImage.getHeight()); float drawWidth = pdImage.getWidth() * scale; @@ -286,20 +289,7 @@ public class PdfUtils { cs.fill(); } } - - private void drawRoundedRect(PDPageContentStream cs, float x, float y, float w, float h, float r) throws IOException { - cs.moveTo(x + r, y); - cs.lineTo(x + w - r, y); - cs.curveTo(x + w, y, x + w, y, x + w, y + r); - cs.lineTo(x + w, y + h - r); - cs.curveTo(x + w, y + h, x + w, y + h, x + w - r, y + h); - cs.lineTo(x + r, y + h); - cs.curveTo(x, y + h, x, y + h, x, y + h - r); - cs.lineTo(x, y + r); - cs.curveTo(x, y, x, y, x + r, y); - cs.closePath(); - } - + private void drawCircle(PDPageContentStream cs, float cx, float cy, float r) throws IOException { float k = 0.5523f; // Bezier approximation for circle cs.moveTo(cx - r, cy);