From 2390462418facd6c323143c06cc30c1ba6051c97 Mon Sep 17 00:00:00 2001 From: Tobias Kellner Date: Tue, 15 Nov 2016 16:06:18 +0100 Subject: Honor EXIF orientation metadata for JPEG logos --- .../composites/SimpleConfigurationComposite.java | 13 +- pdf-over-signator/pom.xml | 4 + .../pdfover/signator/CachedFileNameEmblem.java | 189 ++++++++++++++++----- pom.xml | 5 + 4 files changed, 167 insertions(+), 44 deletions(-) diff --git a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/SimpleConfigurationComposite.java b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/SimpleConfigurationComposite.java index 4c02c044..e54b4a59 100644 --- a/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/SimpleConfigurationComposite.java +++ b/pdf-over-gui/src/main/java/at/asit/pdfover/gui/composites/SimpleConfigurationComposite.java @@ -17,8 +17,11 @@ package at.asit.pdfover.gui.composites; // Imports import java.io.File; +import java.io.IOException; import java.util.Locale; +import javax.imageio.ImageIO; + import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTarget; @@ -64,6 +67,7 @@ import at.asit.pdfover.gui.controls.ErrorDialog; import at.asit.pdfover.gui.controls.ErrorMarker; import at.asit.pdfover.gui.exceptions.InvalidEmblemFile; import at.asit.pdfover.gui.exceptions.InvalidNumberException; +import at.asit.pdfover.gui.utils.ImageConverter; import at.asit.pdfover.gui.utils.Messages; import at.asit.pdfover.gui.utils.SignaturePlaceholderCache; import at.asit.pdfover.gui.workflow.config.ConfigManipulator; @@ -635,7 +639,14 @@ public class SimpleConfigurationComposite extends BaseConfigurationComposite { } if (logo != null) { - this.logo = new Image(this.getDisplay(), logo); + try { + File imgFile = new File(image); + this.logo = new Image(this.getDisplay(), + ImageConverter.convertToSWT(CachedFileNameEmblem.fixImage( + ImageIO.read(imgFile), imgFile))); + } catch (IOException e) { + log.error("Error reading image", e); //$NON-NLS-1$ + } } else { this.logo = null; } diff --git a/pdf-over-signator/pom.xml b/pdf-over-signator/pom.xml index f66f9aad..bd829225 100644 --- a/pdf-over-signator/pom.xml +++ b/pdf-over-signator/pom.xml @@ -20,5 +20,9 @@ commons-codec commons-codec + + com.drewnoakes + metadata-extractor + diff --git a/pdf-over-signator/src/main/java/at/asit/pdfover/signator/CachedFileNameEmblem.java b/pdf-over-signator/src/main/java/at/asit/pdfover/signator/CachedFileNameEmblem.java index fe08abb6..73f25837 100644 --- a/pdf-over-signator/src/main/java/at/asit/pdfover/signator/CachedFileNameEmblem.java +++ b/pdf-over-signator/src/main/java/at/asit/pdfover/signator/CachedFileNameEmblem.java @@ -26,14 +26,24 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Iterator; import java.util.Properties; import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; import org.apache.commons.codec.digest.DigestUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +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.ExifDirectoryBase; +import com.drew.metadata.exif.ExifIFD0Directory; + /** * */ @@ -44,6 +54,16 @@ public class CachedFileNameEmblem implements Emblem { private static final Logger log = LoggerFactory .getLogger(CachedFileNameEmblem.class); + private final String fileDir = System.getProperty("user.home") + File.separator + ".pdf-over"; //$NON-NLS-1$ //$NON-NLS-2$ + private final String imgFileName = ".emblem"; //$NON-NLS-1$ + private final String imgFileExt = "png"; //$NON-NLS-1$ + private final String propFileName = ".emblem.properties"; //$NON-NLS-1$ + + private final String imgProp = "IMG"; //$NON-NLS-1$ + private final String hshProp = "HSH"; //$NON-NLS-1$ + private final int maxWidth = 480; + private final int maxHeight = 600; + private String fileName = null; /** @@ -59,24 +79,126 @@ public class CachedFileNameEmblem implements Emblem { return DigestUtils.md5Hex(is); } + /** + * Correctly rotate JPEG image by EXIF header + * @param img the image + * @param imgFile the image file + * @return the fixed image + */ + public static BufferedImage fixImage(BufferedImage img, File imgFile) { + int oheight = img.getHeight(); + int owidth = img.getWidth(); + + // Read EXIF metadata for jpeg + ImageInputStream iis = null; + try { + iis = ImageIO.createImageInputStream(imgFile); + Iterator imageReaders = ImageIO.getImageReaders(iis); + + while (imageReaders.hasNext()) { + ImageReader reader = imageReaders.next(); + log.debug(reader.getFormatName()); + if (reader.getFormatName().equals("JPEG")) { + try { + Metadata metadata = ImageMetadataReader.readMetadata(imgFile); + int orientation = metadata.getFirstDirectoryOfType( + ExifIFD0Directory.class).getInt( + ExifDirectoryBase.TAG_ORIENTATION); + if (orientation > 2) { + // rotate + double rotation = 0; + int height = owidth; + int width = oheight; + switch ((orientation + 1) / 2) { + case 2: + rotation = Math.PI; + height = oheight; + width = owidth; + break; + case 3: + rotation = Math.PI/2; + break; + case 4: + rotation = 3*Math.PI/2; + break; + } + log.debug("EXIF orientation " + orientation + ", rotating " + rotation + "rad"); + BufferedImage result = new BufferedImage(width, height, img.getType()); + Graphics2D g = result.createGraphics(); + g.translate((width - owidth) / 2, (height - oheight) / 2); + g.rotate(rotation, owidth / 2, oheight / 2); + g.drawRenderedImage(img, null); + g.dispose(); + img = result; + owidth = width; + oheight = height; + } + if (((orientation < 5) && (orientation % 2 == 0)) || + (orientation > 5) && (orientation % 2 == 1)) { + // flip + log.debug("flipping image"); + BufferedImage result = new BufferedImage(owidth, oheight, img.getType()); + Graphics2D g = result.createGraphics(); + g.drawImage(img, owidth, 0, -owidth, oheight, null); + g.dispose(); + img = result; + } + } catch (ImageProcessingException e) { + log.error("Error reading emblem metadata", e); + } catch (MetadataException e) { + log.error("Error reading emblem metadata", e); + } + } + } + } catch (IOException e) { + log.error("Error reading image" , e); + } finally { + try { + if (iis != null) + iis.close(); + } catch (IOException e) { + log.debug("Error closing stream", e); + } + } + return img; + } + + private static BufferedImage scaleImage(BufferedImage img, int maxWidth, int maxHeight) { + int oheight = img.getHeight(); + int owidth = img.getWidth(); + + double ratio = (double)owidth/(double)oheight; + + int height = oheight; + int width = owidth; + if (height > maxHeight) { + height = maxHeight; + width = (int) (maxHeight * ratio); + } + if (width > maxWidth) { + width = maxWidth; + height = (int) (maxWidth / ratio); + } + BufferedImage result = img; + if (width != owidth || height == oheight) { + //scale image + log.debug("Scaling emblem: " + owidth + "x" + oheight + " to " + width + "x" + height); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + result = new BufferedImage(width, height, img.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : img.getType()); + Graphics2D g = result.createGraphics(); + g.drawImage(img, 0, 0, width, height, null); + g.dispose(); + } + return result; + } + /* (non-Javadoc) * @see at.asit.pdfover.signator.Emblem#getFileName() */ @Override public String getFileName() { - final String fileDir = System.getProperty("user.home") + File.separator + ".pdf-over"; //$NON-NLS-1$ //$NON-NLS-2$ - final String imgFileName = ".emblem"; //$NON-NLS-1$ - final String imgFileExt = "png"; //$NON-NLS-1$ - final String propFileName = ".emblem.properties"; //$NON-NLS-1$ - - final String imgProp = "IMG"; //$NON-NLS-1$ - final String hshProp = "HSH"; //$NON-NLS-1$ - final int maxWidth = 480; - final int maxHeight = 600; - String emblemImg = this.fileName; String emblemHsh = null; - String cachedEmblemFileName = fileDir + File.separator + imgFileName + "." + imgFileExt; //$NON-NLS-1$ + String cachedEmblemFileName = this.fileDir + File.separator + this.imgFileName + "." + this.imgFileExt; //$NON-NLS-1$ if (emblemImg == null || !(new File(emblemImg).exists())) return null; @@ -84,11 +206,11 @@ public class CachedFileNameEmblem implements Emblem { Properties emblemProps = new Properties(); // compare cache, try to load if match try { - InputStream in = new FileInputStream(new File(fileDir, propFileName)); + InputStream in = new FileInputStream(new File(this.fileDir, this.propFileName)); emblemProps.load(in); - if (emblemImg.equals(emblemProps.getProperty(imgProp))) { + if (emblemImg.equals(emblemProps.getProperty(this.imgProp))) { emblemHsh = getFileHash(emblemImg); - if (emblemHsh.equals(emblemProps.getProperty(hshProp))) { + if (emblemHsh.equals(emblemProps.getProperty(this.hshProp))) { log.debug("Emblem cache hit: " + cachedEmblemFileName); //$NON-NLS-1$ return cachedEmblemFileName; //$NON-NLS-1$ } @@ -102,36 +224,17 @@ public class CachedFileNameEmblem implements Emblem { // create new cache if (emblemHsh == null) emblemHsh = getFileHash(emblemImg); - emblemProps.setProperty(imgProp, emblemImg); - emblemProps.setProperty(hshProp, emblemHsh); - BufferedImage img = ImageIO.read(new File(emblemImg)); - int oheight = img.getHeight(); - int owidth = img.getWidth(); - double ratio = (double)owidth/(double)oheight; - - int height = oheight; - int width = owidth; - if (height > maxHeight) { - height = maxHeight; - width = (int) (maxHeight * ratio); - } - if (width > maxWidth) { - width = maxWidth; - height = (int) (maxWidth / ratio); - } - BufferedImage cacheImg = img; - if (width != owidth || height == oheight) { - //scale image - log.debug("Scaling emblem: " + owidth + "x" + oheight + " to " + width + "x" + height); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - cacheImg = new BufferedImage(width, height, img.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : img.getType()); - Graphics2D g = cacheImg.createGraphics(); - g.drawImage(img, 0, 0, width, height, null); - g.dispose(); - } + emblemProps.setProperty(this.imgProp, emblemImg); + emblemProps.setProperty(this.hshProp, emblemHsh); + File imgFile = new File(emblemImg); + + BufferedImage img = ImageIO.read(imgFile); + img = fixImage(img, imgFile); + img = scaleImage(img, this.maxWidth, this.maxHeight); - File file = new File(fileDir, imgFileName + "." + imgFileExt); //$NON-NLS-1$ - ImageIO.write(cacheImg, imgFileExt, file); // ignore returned boolean - OutputStream out = new FileOutputStream(new File(fileDir, propFileName)); + File file = new File(this.fileDir, this.imgFileName + "." + this.imgFileExt); //$NON-NLS-1$ + ImageIO.write(img, this.imgFileExt, file); // ignore returned boolean + OutputStream out = new FileOutputStream(new File(this.fileDir, this.propFileName)); emblemProps.store(out, null); } catch (IOException e) { log.error("Can't save emblem cache", e); //$NON-NLS-1$ diff --git a/pom.xml b/pom.xml index b8004cf9..6ca8564f 100644 --- a/pom.xml +++ b/pom.xml @@ -171,6 +171,11 @@ jbig2 1.0 + + com.drewnoakes + metadata-extractor + 2.9.1 + -- cgit v1.2.3