You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ja...@apache.org on 2014/03/10 06:36:00 UTC

svn commit: r1575831 - in /pdfbox/trunk: pdfbox/src/main/java/org/apache/pdfbox/rendering/ pdfbox/src/main/java/org/apache/pdfbox/util/ pdfbox/src/test/java/org/apache/pdfbox/util/ tools/src/main/java/org/apache/pdfbox/tools/

Author: jahewson
Date: Mon Mar 10 05:36:00 2014
New Revision: 1575831

URL: http://svn.apache.org/r1575831
Log:
PDFBOX-1963: restore binary TIFF writing and clean up ImageIOUtil

Added:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/ImageType.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/TIFFUtil.java
Modified:
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java
    pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/ImageIOUtil.java
    pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestImageIOUtils.java
    pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/ImageType.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/ImageType.java?rev=1575831&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/ImageType.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/ImageType.java Mon Mar 10 05:36:00 2014
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.rendering;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * Image type for rendering.
+ */
+public enum ImageType
+{
+    /** Black or white. */
+    BINARY {
+        @Override
+        int toBufferedImageType()
+        {
+            return BufferedImage.TYPE_BYTE_BINARY;
+        }
+    },
+
+    /** Shades of gray */
+    GRAY {
+        @Override
+        int toBufferedImageType()
+        {
+            return BufferedImage.TYPE_BYTE_GRAY;
+        }
+    },
+
+    /** Red, Green, Blue */
+    RGB {
+        @Override
+        int toBufferedImageType()
+        {
+            return BufferedImage.TYPE_INT_RGB;
+        }
+    },
+
+    /** Alpha, Red, Green, Blue */
+    ARGB {
+        @Override
+        int toBufferedImageType()
+        {
+            return BufferedImage.TYPE_INT_ARGB;
+        }
+    };
+
+    abstract int toBufferedImageType();
+}

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java?rev=1575831&r1=1575830&r2=1575831&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/rendering/PDFRenderer.java Mon Mar 10 05:36:00 2014
@@ -67,7 +67,7 @@ public class PDFRenderer
      */
     public BufferedImage renderImage(int pageIndex, float scale) throws IOException
     {
-        return renderImage(pageIndex, scale, false);
+        return renderImage(pageIndex, scale, ImageType.RGB);
     }
 
     /**
@@ -77,33 +77,34 @@ public class PDFRenderer
      * @return the rendered page image
      * @throws IOException if the PDF cannot be read
      */
-    public BufferedImage renderImageWithDPI(int pageIndex, int dpi) throws IOException
+    public BufferedImage renderImageWithDPI(int pageIndex, float dpi) throws IOException
     {
-        return renderImage(pageIndex, dpi / 72f, false);
+        return renderImage(pageIndex, dpi / 72f, ImageType.RGB);
     }
 
     /**
      * Returns the given page as an RGB image at the given DPI.
      * @param pageIndex the zero-based index of the page to be converted
      * @param dpi the DPI (dots per inch) to render at
+     * @param imageType the type of image to return
      * @return the rendered page image
      * @throws IOException if the PDF cannot be read
      */
-    public BufferedImage renderImageWithDPI(int pageIndex, int dpi, boolean useAlpha)
+    public BufferedImage renderImageWithDPI(int pageIndex, float dpi, ImageType imageType)
             throws IOException
     {
-        return renderImage(pageIndex, dpi / 72f, useAlpha);
+        return renderImage(pageIndex, dpi / 72f, imageType);
     }
 
     /**
      * Returns the given page as an RGB or ARGB image at the given scale.
      * @param pageIndex the zero-based index of the page to be converted
      * @param scale the scaling factor, where 1 = 72 DPI
-     * @param useAlpha true if an ARGB image is to be returned
+     * @param imageType the type of image to return
      * @return the rendered page image
      * @throws IOException if the PDF cannot be read
      */
-    public BufferedImage renderImage(int pageIndex, float scale, boolean useAlpha)
+    public BufferedImage renderImage(int pageIndex, float scale, ImageType imageType)
             throws IOException
     {
         PDPage page = document.getPage(pageIndex);
@@ -125,24 +126,20 @@ public class PDFRenderer
             rotationAngle -= 360;
         }
 
-        int imageType = useAlpha ?
-                BufferedImage.TYPE_INT_ARGB :
-                BufferedImage.TYPE_INT_RGB;
-
         // swap width and height
         BufferedImage image;
         if (rotationAngle == 90 || rotationAngle == 270)
         {
-            image = new BufferedImage(heightPx, widthPx, imageType);
+            image = new BufferedImage(heightPx, widthPx, imageType.toBufferedImageType());
         }
         else
         {
-            image = new BufferedImage(widthPx, heightPx, imageType);
+            image = new BufferedImage(widthPx, heightPx, imageType.toBufferedImageType());
         }
 
         // use a transparent background if the imageType supports alpha
         Graphics2D g = image.createGraphics();
-        if (!useAlpha)
+        if (imageType != ImageType.ARGB)
         {
             g.setBackground(Color.WHITE);
         }

Modified: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/ImageIOUtil.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/ImageIOUtil.java?rev=1575831&r1=1575830&r2=1575831&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/ImageIOUtil.java (original)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/ImageIOUtil.java Mon Mar 10 05:36:00 2014
@@ -17,13 +17,11 @@
 package org.apache.pdfbox.util;
 
 import java.awt.image.BufferedImage;
-import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.io.StringWriter;
 import java.util.Iterator;
 
-import javax.imageio.IIOException;
 import javax.imageio.IIOImage;
 import javax.imageio.ImageIO;
 import javax.imageio.ImageTypeSpecifier;
@@ -33,436 +31,175 @@ import javax.imageio.metadata.IIOInvalid
 import javax.imageio.metadata.IIOMetadata;
 import javax.imageio.metadata.IIOMetadataNode;
 import javax.imageio.stream.ImageOutputStream;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.TransformerFactoryConfigurationError;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
+import org.apache.pdfbox.filter.MissingImageReaderException;
+import org.w3c.dom.Element;
 
 /**
- * This class handles some ImageIO operations.
- *
- * @version $Revision$
- * 
+ * Handles some ImageIO operations.
  */
 public class ImageIOUtil
-{   
-    /**
-     * Log instance.
-     */
-    private static final Log LOG = LogFactory.getLog(ImageIOUtil.class);
-
-    /**
-     * Default screen resolution: 72dpi.
-     */
-    public static final int DEFAULT_SCREEN_RESOLUTION = 72;
-    /**
-     * Default compression quality: 1.0f.
-     */
-    public static final float DEFAULT_COMPRESSION_QUALITY = 1.0f;
-    
+{
     private ImageIOUtil()
     {
-        // Default constructor
     }
 
     /**
      * Writes a buffered image to a file using the given image format.
-     * 
      * @param image the image to be written
-     * @param imageFormat the target format (ex. "png")
+     * @param formatName the target format (ex. "png")
      * @param filename used to construct the filename for the individual images
-     * @param resolution the resolution in dpi (dots per inch)
-     * 
+     * @param dpi the resolution in dpi (dots per inch)
      * @return true if the images were produced, false if there was an error
      * @throws IOException if an I/O error occurs
      */
-    public static boolean writeImage(BufferedImage image, String imageFormat, String filename,
-                                     int resolution) throws IOException
+    public static boolean writeImage(BufferedImage image, String formatName, String filename,
+                                     int dpi) throws IOException
     {
-        String fileName = filename + "." + imageFormat;
-        File file = new File(fileName);
-        return writeImage(image, imageFormat, file, resolution);
+        OutputStream output = new FileOutputStream(filename + "." + formatName);
+        try
+        {
+            return writeImage(image, formatName, output, dpi);
+        }
+        finally
+        {
+            output.close();
+        }
     }
 
     /**
      * Writes a buffered image to a file using the given image format.
-     * 
      * @param image the image to be written
-     * @param imageFormat the target format (ex. "png")
-     * @param outputStream the output stream to be used for writing
-     * 
+     * @param formatName the target format (ex. "png")
+     * @param output the output stream to be used for writing
      * @return true if the images were produced, false if there was an error
      * @throws IOException if an I/O error occurs
      */
-    public static boolean writeImage(BufferedImage image, String imageFormat,
-                                     OutputStream outputStream) throws IOException
+    public static boolean writeImage(BufferedImage image, String formatName, OutputStream output)
+            throws IOException
     {
-        return writeImage(image, imageFormat, outputStream, DEFAULT_SCREEN_RESOLUTION);
+        return writeImage(image, formatName, output, 72);
     }
 
     /**
      * Writes a buffered image to a file using the given image format.
-     * 
      * @param image the image to be written
-     * @param imageFormat the target format (ex. "png")
-     * @param outputStream the output stream to be used for writing
-     * @param resolution resolution to be used when writing the image
-     * 
+     * @param formatName the target format (ex. "png")
+     * @param output the output stream to be used for writing
+     * @param dpi resolution to be used when writing the image
      * @return true if the images were produced, false if there was an error
      * @throws IOException if an I/O error occurs
      */
-    public static boolean writeImage(BufferedImage image, String imageFormat, Object outputStream,
-                                     int resolution) throws IOException
+    public static boolean writeImage(BufferedImage image, String formatName, OutputStream output,
+                                     int dpi) throws IOException
     {
-        return writeImage(image, imageFormat, outputStream, resolution, DEFAULT_COMPRESSION_QUALITY);
+        return writeImage(image, formatName, output, dpi, 1.0f);
     }
     
     /**
      * Writes a buffered image to a file using the given image format.
-     * 
      * @param image the image to be written
-     * @param imageFormat the target format (ex. "png")
-     * @param outputStream the output stream to be used for writing
-     * @param resolution resolution to be used when writing the image
-     * @param quality quality to be used when compressing the image (0 <
-     * quality < 1.0f)
-     * 
+     * @param formatName the target format (ex. "png")
+     * @param output the output stream to be used for writing
+     * @param dpi resolution to be used when writing the image
+     * @param quality quality to be used when compressing the image (0 < quality < 1.0f)
      * @return true if the images were produced, false if there was an error
      * @throws IOException if an I/O error occurs
      */
-    public static boolean writeImage(BufferedImage image, String imageFormat, Object outputStream,
-                                     int resolution, float quality) throws IOException
+    public static boolean writeImage(BufferedImage image, String formatName, OutputStream output,
+                                     int dpi, float quality) throws IOException
     {
-        boolean bSuccess = true;
-        ImageOutputStream output = null;
-        ImageWriter imageWriter = null;
+        ImageOutputStream imageOutput = null;
+        ImageWriter writer = null;
         try
         {
-            output = ImageIO.createImageOutputStream(outputStream);
-    
-            boolean foundWriter = false;
-            Iterator<ImageWriter> writerIter = ImageIO.getImageWritersByFormatName(imageFormat);
-            while (writerIter.hasNext() && !foundWriter)
-            {
-                try
+            // find suitable image writer
+            Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(formatName);
+            ImageWriteParam param = null;
+            IIOMetadata metadata = null;
+            while (writers.hasNext())
+            {
+                writer = writers.next();
+                param = writer.getDefaultWriteParam();
+                metadata = writer.getDefaultImageMetadata(new ImageTypeSpecifier(image), param);
+                if (metadata.isReadOnly() || !metadata.isStandardMetadataFormatSupported())
                 {
-                    imageWriter = (ImageWriter) writerIter.next();
-                    ImageWriteParam writerParams = imageWriter.getDefaultWriteParam();
-                    if (writerParams.canWriteCompressed())
-                    {
-                        writerParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
-                        // reset the compression type if overwritten by setCompressionMode
-
-                        if (writerParams.getCompressionType() == null)
-                        {
-                            if (imageFormat.toLowerCase().startsWith("tif"))
-                            {
-                                // avoid error: first compression type is RLE, not optimal and incorrect for color images
-                                //TODO? another writeImage() call with extra compression param so user can decide
-                                if (image.getType() == BufferedImage.TYPE_BYTE_BINARY && image.getColorModel().getPixelSize() == 1)
-                                {
-                                    writerParams.setCompressionType("CCITT T.6");
-                                }
-                                else
-                                {
-                                    writerParams.setCompressionType("LZW");
-                                }
-                            }
-                            else
-                            {
-                                writerParams.setCompressionType(writerParams.getCompressionTypes()[0]);
-                            }
-                        }
-                        writerParams.setCompressionQuality(quality);
-                    }
-                    IIOMetadata meta = createMetadata(image, imageWriter, writerParams, resolution);
-                    if (meta != null)
-                    {
-                        imageWriter.setOutput(output);
-                        imageWriter.write(null, new IIOImage(image, null, meta), writerParams);
-                    	foundWriter = true;
-                    }
+                    writer = null;
                 }
-                catch (IIOException io)
+            }
+
+            if (writer == null)
+            {
+                return false;
+            }
+
+            // compression
+            if (param.canWriteCompressed())
+            {
+                if (formatName.toLowerCase().startsWith("tif"))
                 {
-                    LOG.error("IIOException in writeImage()", io);
-                    throw new IOException(io.getMessage());
+                    // TIFF compression
+                    TIFFUtil.setCompressionType(param, image);
                 }
-                finally
+                else
                 {
-                    if (imageWriter != null)
-                    {
-                        imageWriter.dispose();
-                    }
+                    // JPEG, PNG compression
+                    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+                    param.setCompressionType(param.getCompressionTypes()[0]);
+                    param.setCompressionQuality(quality);
                 }
             }
-            if (!foundWriter)
+
+            // metadata
+            setDPI(metadata, dpi);
+
+            // TIFF metadata
+            if (formatName.toLowerCase().startsWith("tif"))
             {
-                LOG.error("No writer found for format '" + imageFormat + "'");
-                bSuccess = false;
+                TIFFUtil.updateMetadata(metadata, image, dpi);
             }
+
+            // write
+            imageOutput = ImageIO.createImageOutputStream(output);
+            writer.setOutput(imageOutput);
+            writer.write(null, new IIOImage(image, null, metadata), param);
         }
         finally
         {
-            if (output != null)
+            if (writer != null)
             {
-                output.flush();
-                output.close();
+                writer.dispose();
             }
-        }
-        return bSuccess;
-    }
-
-    private static IIOMetadata createMetadata(BufferedImage image, ImageWriter imageWriter,
-            ImageWriteParam writerParams, int resolution)
-    {
-        ImageTypeSpecifier type;
-        if (writerParams.getDestinationType() != null)
-        {
-            type = writerParams.getDestinationType();
-        }
-        else
-        {
-            type = ImageTypeSpecifier.createFromRenderedImage( image );
-        }
-        IIOMetadata meta = imageWriter.getDefaultImageMetadata(type, writerParams);
-        logMeta(meta, STANDARD_METADATA_FORMAT);
-        if (imageWriter.getClass().getName().toUpperCase().contains("TIFF"))
-        {
-            updateMetadata(image, meta, resolution);
-        }
-        else
-        {
-            if (!addResolution(meta, resolution))
+            if (imageOutput != null)
             {
-                meta = null;
+                imageOutput.close();
             }
         }
-        logMeta(meta, STANDARD_METADATA_FORMAT);
-        return meta;
+        return true;
     }
 
-    /**
-     * log the meta data as an XML tree if debug is enabled.
-     * 
-     * @param meta meta data.
-     * @param format the XML format to be used.
-     */
-    private static void logMeta(IIOMetadata meta, String format)
+    // sets the DPI metadata
+    private static void setDPI(IIOMetadata metadata, int dpi)
     {
-        if (!LOG.isDebugEnabled())
-        {
-            return;
-        }
-        if (meta == null)
-        {
-            LOG.debug("meta is null");
-            return;
-        }
-        IIOMetadataNode root = (IIOMetadataNode) meta.getAsTree(format);
-        // http://download.java.net/jdk8/docs/api/javax/imageio/metadata/doc-files/standard_metadata.html
-        // http://www.java-forum.org/java-basics-anfaenger-themen/96982-aufloesung-dpi-tiff-png-bildern-auslesen.html#post617178
-
-        try
-        {
-            StringWriter xmlStringWriter = new StringWriter();
-            StreamResult streamResult = new StreamResult(xmlStringWriter);
-            Transformer transformer = TransformerFactory.newInstance().newTransformer();
-            transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // http://stackoverflow.com/a/1264872/535646
-            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
-            DOMSource domSource = new DOMSource(root);
-            transformer.transform(domSource, streamResult);
-            LOG.debug("\n" + xmlStringWriter);
-        }
-        catch (TransformerFactoryConfigurationError ex)
-        {
-            LOG.error(ex, ex);
-        }
-        catch (IllegalArgumentException ex)
-        {
-            LOG.error(ex, ex);
-        }
-        catch (TransformerException ex)
-        {
-            LOG.error(ex, ex);
-        }
-    }
+        IIOMetadataNode root = new IIOMetadataNode("javax_imageio_1.0");
+        Element dimension = new IIOMetadataNode("Dimension");
 
-    private static final String STANDARD_METADATA_FORMAT = "javax_imageio_1.0";
-    private static final String SUN_TIFF_NATIVE_FORMAT
-            = "com_sun_media_imageio_plugins_tiff_image_1.0";
+        Element h = new IIOMetadataNode("HorizontalPixelSize");
+        h.setAttribute("value", Double.toString(dpi / 25.4));
+        dimension.appendChild(h);
 
-    private static boolean addResolution(IIOMetadata meta, int resolution)
-    {
-        if (meta != null && !meta.isReadOnly() && meta.isStandardMetadataFormatSupported())
-        {
-            IIOMetadataNode root = (IIOMetadataNode)meta.getAsTree(STANDARD_METADATA_FORMAT);
-            IIOMetadataNode dim = getChildNode(root, "Dimension");
-            if (dim == null)
-            {
-                dim = new IIOMetadataNode("Dimension");
-                root.appendChild(dim);
-            }
-            IIOMetadataNode child;
-            child = getChildNode(dim, "HorizontalPixelSize");
-            if (child == null)
-            {
-                child = new IIOMetadataNode("HorizontalPixelSize");
-                dim.appendChild(child);
-            }
-            child.setAttribute("value",
-                    Double.toString(resolution / 25.4));
-            child = getChildNode(dim, "VerticalPixelSize");
-            if (child == null)
-            {
-                child = new IIOMetadataNode("VerticalPixelSize");
-                dim.appendChild(child);
-            }
-            child.setAttribute("value",
-                    Double.toString(resolution / 25.4));
-            try
-            {
-                meta.mergeTree(STANDARD_METADATA_FORMAT, root);
-            }
-            catch (IIOInvalidTreeException e)
-            {
-                throw new RuntimeException("Cannot update image metadata: "
-                        + e.getMessage());
-            }
-            return true;
-        }
-        return false;
-    }
+        Element v = new IIOMetadataNode("HorizontalPixelSize");
+        v.setAttribute("value", Double.toString(dpi / 25.4));
+        dimension.appendChild(v);
 
-    private static IIOMetadataNode getChildNode(Node n, String name)
-    {
-        NodeList nodes = n.getChildNodes();
-        for (int i = 0; i < nodes.getLength(); i++)
+        try
         {
-            Node child = nodes.item(i);
-            if (name.equals(child.getNodeName()))
-            {
-                return (IIOMetadataNode)child;
-            }
+            metadata.mergeTree("javax_imageio_1.0", root);
         }
-        return null;
-    }
-    
-    /**
-     * Sets the resolution in a TIFF image, the resolution unit (Inches), rows
-     * per strip to the height to get smaller files, and the name of the
-     * software to "PDFBOX".
-     *
-     * @param tiffImage the TIFF Image
-     * @param meta the meta data that is to be set
-     * @param resolution in dots per inch
-     */
-    protected static void updateMetadata(BufferedImage tiffImage, IIOMetadata meta, int resolution)
-    {
-        // Code inspired by Apache XML graphics
-        // https://svn.apache.org/repos/asf/xmlgraphics/commons/tags/commons-1_3_1/src/java/org/apache/xmlgraphics/image/writer/imageio/ImageIOTIFFImageWriter.java
-        // DTD:
-        // http://download.java.net/media/jai-imageio/javadoc/1.0_01/com/sun/media/imageio/plugins/tiff/package-summary.html
-        // TIFF6 Spec:
-        // http://partners.adobe.com/public/developer/tiff/index.html
-        // We set the resolution manually using the native format since it appears that
-        // it doesn't work properly through the standard metadata. Haven't figured out why
-        // that happens.
-        if (SUN_TIFF_NATIVE_FORMAT.equals(meta.getNativeMetadataFormatName()))
+        catch (IIOInvalidTreeException e)
         {
-            IIOMetadataNode root = new IIOMetadataNode(SUN_TIFF_NATIVE_FORMAT);
-            IIOMetadataNode ifd = getChildNode(root, "TIFFIFD");
-            if (ifd == null)
-            {
-                ifd = new IIOMetadataNode("TIFFIFD");
-                ifd.setAttribute("tagSets",
-                        "com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet");
-                root.appendChild(ifd);
-            }
-
-            ifd.appendChild(createRationalField(282, "XResolution", resolution, 1));
-            ifd.appendChild(createRationalField(283, "YResolution", resolution, 1));
-            ifd.appendChild(createShortField(296, "ResolutionUnit", 2)); // Inch
-
-            ifd.appendChild(createLongField(278, "RowsPerStrip", tiffImage.getHeight()));
-
-            ifd.appendChild(createAsciiField(305, "Software", "PDFBOX"));
-
-            try
-            {
-                meta.mergeTree(SUN_TIFF_NATIVE_FORMAT, root);
-            }
-            catch (IIOInvalidTreeException e)
-            {
-                throw new RuntimeException("Cannot update image metadata: "
-                        + e.getMessage(), e);
-            }
+            // should never happen
+            throw new RuntimeException(e);
         }
     }
-
-    private static IIOMetadataNode createShortField(int tiffTagNumber, String name, int val)
-    {
-        IIOMetadataNode field, arrayNode, valueNode;
-        field = new IIOMetadataNode("TIFFField");
-        field.setAttribute("number", Integer.toString(tiffTagNumber));
-        field.setAttribute("name", name);
-        arrayNode = new IIOMetadataNode("TIFFShorts");
-        field.appendChild(arrayNode);
-        valueNode = new IIOMetadataNode("TIFFShort");
-        arrayNode.appendChild(valueNode);
-        valueNode.setAttribute("value", Integer.toString(val));
-        return field;
-    }
-
-    private static IIOMetadataNode createAsciiField(int number, String name, String val)
-    {
-        IIOMetadataNode field, arrayNode, valueNode;
-        field = new IIOMetadataNode("TIFFField");
-        field.setAttribute("number", Integer.toString(number));
-        field.setAttribute("name", name);
-        arrayNode = new IIOMetadataNode("TIFFAsciis");
-        field.appendChild(arrayNode);
-        valueNode = new IIOMetadataNode("TIFFAscii");
-        arrayNode.appendChild(valueNode);
-        valueNode.setAttribute("value", val);
-        return field;
-    }
-
-    private static IIOMetadataNode createLongField(int number, String name, long val)
-    {
-        IIOMetadataNode field, arrayNode, valueNode;
-        field = new IIOMetadataNode("TIFFField");
-        field.setAttribute("number", Integer.toString(number));
-        field.setAttribute("name", name);
-        arrayNode = new IIOMetadataNode("TIFFLongs");
-        field.appendChild(arrayNode);
-        valueNode = new IIOMetadataNode("TIFFLong");
-        arrayNode.appendChild(valueNode);
-        valueNode.setAttribute("value", Long.toString(val));
-        return field;
-    }
-
-    private static IIOMetadataNode createRationalField(int number, String name, int numerator, int denominator)
-    {
-        IIOMetadataNode field, arrayNode, valueNode;
-        field = new IIOMetadataNode("TIFFField");
-        field.setAttribute("number", Integer.toString(number));
-        field.setAttribute("name", name);
-        arrayNode = new IIOMetadataNode("TIFFRationals");
-        field.appendChild(arrayNode);
-        valueNode = new IIOMetadataNode("TIFFRational");
-        arrayNode.appendChild(valueNode);
-        valueNode.setAttribute("value", numerator + "/" + denominator);
-        return field;
-    }
-
 }

Added: pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/TIFFUtil.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/TIFFUtil.java?rev=1575831&view=auto
==============================================================================
--- pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/TIFFUtil.java (added)
+++ pdfbox/trunk/pdfbox/src/main/java/org/apache/pdfbox/util/TIFFUtil.java Mon Mar 10 05:36:00 2014
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pdfbox.util;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.imageio.ImageWriteParam;
+import javax.imageio.metadata.IIOInvalidTreeException;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataNode;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.awt.image.BufferedImage;
+import java.io.StringWriter;
+
+/**
+ * Used by ImageIOUtil to write TIFF files.
+ * @author Tilman Hausherr
+ */
+class TIFFUtil
+{
+    private static final Log LOG = LogFactory.getLog(TIFFUtil.class);
+    private static final String SUN_TIFF_FORMAT = "com_sun_media_imageio_plugins_tiff_image_1.0";
+
+    /**
+     * Sets the ImageIO parameter compression type based on the given image.
+     * @param image buffered image used to decide compression type
+     * @param param ImageIO write parameter to update
+     */
+    public static void setCompressionType(ImageWriteParam param, BufferedImage image)
+    {
+        // avoid error: first compression type is RLE, not optimal and incorrect for color images
+        // TODO expose this choice to the user?
+        if (image.getType() == BufferedImage.TYPE_BYTE_BINARY &&
+            image.getColorModel().getPixelSize() == 1)
+        {
+            param.setCompressionType("CCITT T.6");
+        }
+        else
+        {
+            param.setCompressionType("LZW");
+        }
+    }
+
+    /**
+     * Updates the given ImageIO metadata with Sun's custom TIFF tags.
+     * {@see https://svn.apache.org/repos/asf/xmlgraphics/commons/tags/commons-1_3_1/src/java/org/
+     *       apache/xmlgraphics/image/writer/imageio/ImageIOTIFFImageWriter.java}
+     * {@see http://download.java.net/media/jai-imageio/javadoc/1.0_01/com/sun/media/imageio/
+     *       plugins/tiff/package-summary.html}
+     * {@see http://partners.adobe.com/public/developer/tiff/index.html}
+     * @param image buffered image which will be written
+     * @param metadata ImageIO metadata
+     * @param dpi image dots per inch
+     */
+    public static void updateMetadata(IIOMetadata metadata, BufferedImage image, int dpi)
+    {
+        debugLogMetadata(metadata);
+
+        if (!SUN_TIFF_FORMAT.equals(metadata.getNativeMetadataFormatName()))
+        {
+            LOG.debug("Using unknown TIFF image writer: " + metadata.getNativeMetadataFormatName());
+            return;
+        }
+
+        IIOMetadataNode root = new IIOMetadataNode(SUN_TIFF_FORMAT);
+        IIOMetadataNode ifd;
+        if (root.getElementsByTagName("TIFFIFD").getLength() == 0)
+        {
+            ifd = (IIOMetadataNode)root.getElementsByTagName("TIFFIFD").item(0);
+        }
+        else
+        {
+            ifd = new IIOMetadataNode("TIFFIFD");
+            ifd.setAttribute("tagSets",
+                             "com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet");
+            root.appendChild(ifd);
+        }
+
+        // standard metadata does not work, so we set the DPI manually
+        ifd.appendChild(createRationalField(282, "XResolution", dpi, 1));
+        ifd.appendChild(createRationalField(283, "YResolution", dpi, 1));
+        ifd.appendChild(createShortField(296, "ResolutionUnit", 2)); // Inch
+
+        ifd.appendChild(createLongField(278, "RowsPerStrip", image.getHeight()));
+        ifd.appendChild(createAsciiField(305, "Software", "PDFBOX"));
+
+        try
+        {
+            metadata.mergeTree(SUN_TIFF_FORMAT, root);
+        }
+        catch (IIOInvalidTreeException e)
+        {
+            // should never happen
+            throw new RuntimeException(e);
+        }
+
+        debugLogMetadata(metadata);
+    }
+
+    private static IIOMetadataNode createShortField(int tiffTagNumber, String name, int val)
+    {
+        IIOMetadataNode field, arrayNode, valueNode;
+        field = new IIOMetadataNode("TIFFField");
+        field.setAttribute("number", Integer.toString(tiffTagNumber));
+        field.setAttribute("name", name);
+        arrayNode = new IIOMetadataNode("TIFFShorts");
+        field.appendChild(arrayNode);
+        valueNode = new IIOMetadataNode("TIFFShort");
+        arrayNode.appendChild(valueNode);
+        valueNode.setAttribute("value", Integer.toString(val));
+        return field;
+    }
+
+    private static IIOMetadataNode createAsciiField(int number, String name, String val)
+    {
+        IIOMetadataNode field, arrayNode, valueNode;
+        field = new IIOMetadataNode("TIFFField");
+        field.setAttribute("number", Integer.toString(number));
+        field.setAttribute("name", name);
+        arrayNode = new IIOMetadataNode("TIFFAsciis");
+        field.appendChild(arrayNode);
+        valueNode = new IIOMetadataNode("TIFFAscii");
+        arrayNode.appendChild(valueNode);
+        valueNode.setAttribute("value", val);
+        return field;
+    }
+
+    private static IIOMetadataNode createLongField(int number, String name, long val)
+    {
+        IIOMetadataNode field, arrayNode, valueNode;
+        field = new IIOMetadataNode("TIFFField");
+        field.setAttribute("number", Integer.toString(number));
+        field.setAttribute("name", name);
+        arrayNode = new IIOMetadataNode("TIFFLongs");
+        field.appendChild(arrayNode);
+        valueNode = new IIOMetadataNode("TIFFLong");
+        arrayNode.appendChild(valueNode);
+        valueNode.setAttribute("value", Long.toString(val));
+        return field;
+    }
+
+    private static IIOMetadataNode createRationalField(int number, String name, int numerator,
+                                                       int denominator)
+    {
+        IIOMetadataNode field, arrayNode, valueNode;
+        field = new IIOMetadataNode("TIFFField");
+        field.setAttribute("number", Integer.toString(number));
+        field.setAttribute("name", name);
+        arrayNode = new IIOMetadataNode("TIFFRationals");
+        field.appendChild(arrayNode);
+        valueNode = new IIOMetadataNode("TIFFRational");
+        arrayNode.appendChild(valueNode);
+        valueNode.setAttribute("value", numerator + "/" + denominator);
+        return field;
+    }
+
+    // logs metadata as an XML tree if debug is enabled
+
+    private static void debugLogMetadata(IIOMetadata metadata)
+    {
+        if (LOG.isDebugEnabled())
+        {
+            return;
+        }
+
+        // see http://docs.oracle.com/javase/7/docs/api/javax/imageio/
+        //     metadata/doc-files/standard_metadata.html
+        IIOMetadataNode root = (IIOMetadataNode) metadata.getAsTree("javax_imageio_1.0");
+        try
+        {
+            StringWriter xmlStringWriter = new StringWriter();
+            StreamResult streamResult = new StreamResult(xmlStringWriter);
+            Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            // see http://stackoverflow.com/a/1264872/535646
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
+            DOMSource domSource = new DOMSource(root);
+            transformer.transform(domSource, streamResult);
+            LOG.debug("\n" + xmlStringWriter);
+        }
+        catch (Exception ex)
+        {
+            LOG.error(ex, ex);
+        }
+    }
+}

Modified: pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestImageIOUtils.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestImageIOUtils.java?rev=1575831&r1=1575830&r2=1575831&view=diff
==============================================================================
--- pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestImageIOUtils.java (original)
+++ pdfbox/trunk/pdfbox/src/test/java/org/apache/pdfbox/util/TestImageIOUtils.java Mon Mar 10 05:36:00 2014
@@ -26,6 +26,7 @@ import junit.framework.TestCase;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.rendering.ImageType;
 import org.apache.pdfbox.rendering.PDFRenderer;
 
 /**
@@ -52,20 +53,20 @@ public class TestImageIOUtils extends Te
             float dpi = 120;
             document = PDDocument.load(file);
             // testing PNG
-            writeImage(document, imageType, outDir + file.getName() + "-", dpi);
+            writeImage(document, imageType, outDir + file.getName() + "-", ImageType.RGB, dpi);
             // testing JPG/JPEG
             imageType = "jpg";
-            writeImage(document, imageType, outDir + file.getName() + "-", dpi);
+            writeImage(document, imageType, outDir + file.getName() + "-", ImageType.RGB, dpi);
             // testing BMP
             imageType = "bmp";
-            writeImage(document, imageType, outDir + file.getName() + "-", dpi);
+            writeImage(document, imageType, outDir + file.getName() + "-", ImageType.RGB, dpi);
             // testing WBMP
             imageType = "wbmp";
-            writeImage(document, imageType, outDir + file.getName() + "-", dpi);
+            writeImage(document, imageType, outDir + file.getName() + "-", ImageType.RGB, dpi);
             // testing TIFF
             imageType = "tif";
-            writeImage(document, imageType, outDir + file.getName() + "-bw-", dpi);
-            writeImage(document, imageType, outDir + file.getName() + "-co-", dpi);
+            writeImage(document, imageType, outDir + file.getName() + "-bw-", ImageType.BINARY, dpi);
+            writeImage(document, imageType, outDir + file.getName() + "-co-", ImageType.RGB, dpi);
         }
         finally
         {
@@ -76,11 +77,12 @@ public class TestImageIOUtils extends Te
         }
     }
 
-    private void writeImage(PDDocument document, String imageFormat, String outputPrefix, float dpi)
+    private void writeImage(PDDocument document, String imageFormat, String outputPrefix,
+                            ImageType imageType, float dpi)
             throws IOException
     {
         PDFRenderer renderer = new PDFRenderer(document);
-        BufferedImage image = renderer.renderImage(0, dpi / 72f);
+        BufferedImage image = renderer.renderImageWithDPI(0, dpi, imageType);
         String fileName = outputPrefix + 1;
         LOG.info("Writing: " + fileName + "." + imageFormat);
         ImageIOUtil.writeImage(image, imageFormat, fileName,  Math.round(dpi));

Modified: pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java
URL: http://svn.apache.org/viewvc/pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java?rev=1575831&r1=1575830&r2=1575831&view=diff
==============================================================================
--- pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java (original)
+++ pdfbox/trunk/tools/src/main/java/org/apache/pdfbox/tools/PDFReader.java Mon Mar 10 05:36:00 2014
@@ -356,8 +356,7 @@ public class PDFReader extends JFrame
                 imageFilename = imageFilename.substring(0, imageFilename.length() - 4);
             }
             imageFilename += "_" + (currentPage + 1);
-            ImageIOUtil.writeImage(pageAsImage, "png", imageFilename,
-                                   BufferedImage.TYPE_USHORT_565_RGB, 300);
+            ImageIOUtil.writeImage(pageAsImage, "png", imageFilename, 300);
         }
         catch (IOException exception)
         {