You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by ti...@apache.org on 2019/11/17 10:55:16 UTC
svn commit: r1869928 -
/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java
Author: tilman
Date: Sun Nov 17 10:55:15 2019
New Revision: 1869928
URL: http://svn.apache.org/viewvc?rev=1869928&view=rev
Log:
PDFBOX-4691: avoid decoding JPEG raster, try using metadata instead to lessen time and memory footprint
Modified:
pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java
Modified: pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java?rev=1869928&r1=1869927&r2=1869928&view=diff
==============================================================================
--- pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java (original)
+++ pdfbox/branches/2.0/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/image/JPEGFactory.java Sun Nov 17 10:55:15 2019
@@ -39,7 +39,12 @@ import javax.imageio.metadata.IIOMetadat
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.cos.COSArray;
import org.apache.pdfbox.cos.COSInteger;
import org.apache.pdfbox.cos.COSName;
@@ -58,6 +63,8 @@ import org.w3c.dom.Element;
*/
public final class JPEGFactory
{
+ private static final Log LOG = LogFactory.getLog(JPEGFactory.class);
+
private JPEGFactory()
{
}
@@ -93,12 +100,10 @@ public final class JPEGFactory
// copy stream
ByteArrayInputStream byteStream = new ByteArrayInputStream(byteArray);
- // read image
- Raster raster = readJPEGRaster(byteStream);
- byteStream.reset();
+ Dimensions meta = retrieveDimensions(byteStream);
PDColorSpace colorSpace;
- switch (raster.getNumDataElements())
+ switch (meta.numComponents)
{
case 1:
colorSpace = PDDeviceGray.INSTANCE;
@@ -111,12 +116,12 @@ public final class JPEGFactory
break;
default:
throw new UnsupportedOperationException("number of data elements not supported: " +
- raster.getNumDataElements());
+ meta.numComponents);
}
// create PDImageXObject from stream
PDImageXObject pdImage = new PDImageXObject(document, byteStream,
- COSName.DCT_DECODE, raster.getWidth(), raster.getHeight(), 8, colorSpace);
+ COSName.DCT_DECODE, meta.width, meta.height, 8, colorSpace);
if (colorSpace instanceof PDDeviceCMYK)
{
@@ -135,7 +140,12 @@ public final class JPEGFactory
return pdImage;
}
- private static Raster readJPEGRaster(InputStream stream) throws IOException
+ private static class Dimensions
+ {
+ private int width, height, numComponents;
+ }
+
+ private static Dimensions retrieveDimensions(ByteArrayInputStream stream) throws IOException
{
// find suitable image reader
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
@@ -160,19 +170,68 @@ public final class JPEGFactory
{
iis = ImageIO.createImageInputStream(stream);
reader.setInput(iis);
+
+ Dimensions meta = new Dimensions();
+ meta.width = reader.getWidth(0);
+ meta.height = reader.getHeight(0);
+ // PDFBOX-4691: get from image metadata (faster because no decoding)
+ try
+ {
+ meta.numComponents = getNumComponentsFromImageMetadata(reader);
+ if (meta.numComponents != 0)
+ {
+ return meta;
+ }
+ LOG.warn("No image metadata, will decode image and use raster size");
+ }
+ catch (IOException ex)
+ {
+ LOG.warn("Error reading image metadata, will decode image and use raster size", ex);
+ }
+
+ // Old method: get from raster (slower)
ImageIO.setUseCache(false);
- return reader.readRaster(0, null);
+ Raster raster = reader.readRaster(0, null);
+ meta.numComponents = raster.getNumDataElements();
+ return meta;
}
finally
{
- if (iis != null)
- {
- iis.close();
- }
+ IOUtils.closeQuietly(iis);
+ stream.reset();
reader.dispose();
}
}
+ private static int getNumComponentsFromImageMetadata(ImageReader reader) throws IOException
+ {
+ IIOMetadata imageMetadata = reader.getImageMetadata(0);
+ if (imageMetadata == null)
+ {
+ return 0;
+ }
+ Element root = (Element) imageMetadata.getAsTree("javax_imageio_jpeg_image_1.0");
+ if (root == null)
+ {
+ return 0;
+ }
+
+ try
+ {
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ String numScanComponents = xpath.evaluate("markerSequence/sos/@numScanComponents", root);
+ return Integer.parseInt(numScanComponents);
+ }
+ catch (NumberFormatException ex)
+ {
+ return 0;
+ }
+ catch (XPathExpressionException ex)
+ {
+ return 0;
+ }
+ }
+
/**
* Creates a new JPEG PDImageXObject from a BufferedImage.
* <p>