import java.io.ByteArrayInputStream;\r
import java.io.IOException;\r
import java.io.InputStream;\r
+import java.util.Iterator;\r
\r
import javax.imageio.ImageIO;\r
+import javax.imageio.ImageReadParam;\r
+import javax.imageio.ImageReader;\r
+import javax.imageio.ImageTypeSpecifier;\r
+import javax.imageio.stream.ImageInputStream;\r
+import javax.imageio.stream.MemoryCacheImageInputStream;\r
\r
import org.apache.poi.util.POILogFactory;\r
import org.apache.poi.util.POILogger;\r
\r
@Override\r
public void loadImage(InputStream data, String contentType) throws IOException {\r
- img = convertBufferedImage(ImageIO.read(data), contentType);\r
+ img = readImage(data, contentType);\r
}\r
\r
@Override\r
public void loadImage(byte data[], String contentType) throws IOException {\r
- img = convertBufferedImage(ImageIO.read(new ByteArrayInputStream(data)), contentType);\r
+ img = readImage(new ByteArrayInputStream(data), contentType);\r
}\r
-\r
+ \r
/**\r
- * Add alpha channel to buffered image\r
+ * Read the image data via ImageIO and optionally try to workaround metadata errors.\r
+ * The resulting image is of image image type {@link BufferedImage#TYPE_INT_ARGB}\r
+ *\r
+ * @param data the data stream\r
+ * @param contentType the content type\r
+ * @return the bufferedImage or null, if there was no image reader for this content type\r
+ * @throws IOException thrown if there was an error while processing the image\r
*/\r
- private static BufferedImage convertBufferedImage(BufferedImage img, String contentType) {\r
+ private static BufferedImage readImage(InputStream data, String contentType) throws IOException {\r
+ IOException lastException = null;\r
+ BufferedImage img = null;\r
+ // currently don't use FileCacheImageInputStream,\r
+ // because of the risk of filling the file handles (see #59166)\r
+ ImageInputStream iis = new MemoryCacheImageInputStream(data);\r
+ try {\r
+ iis = new MemoryCacheImageInputStream(data);\r
+ iis.mark();\r
+ \r
+ Iterator<ImageReader> iter = ImageIO.getImageReaders(iis);\r
+ while (img==null && iter.hasNext()) {\r
+ ImageReader reader = iter.next();\r
+ ImageReadParam param = reader.getDefaultReadParam();\r
+ // 0:default mode, 1:fallback mode\r
+ for (int mode=0; img==null && mode<2; mode++) {\r
+ iis.reset();\r
+ iis.mark();\r
+\r
+ if (mode == 1) {\r
+ // fallback mode for invalid image band metadata\r
+ // see http://stackoverflow.com/questions/10416378\r
+ Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);\r
+ while (imageTypes.hasNext()) {\r
+ ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();\r
+ int bufferedImageType = imageTypeSpecifier.getBufferedImageType();\r
+ if (bufferedImageType == BufferedImage.TYPE_BYTE_GRAY) {\r
+ param.setDestinationType(imageTypeSpecifier);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ \r
+ try {\r
+ reader.setInput(iis, false, true);\r
+ img = reader.read(0, param);\r
+ } catch (IOException e) {\r
+ lastException = e;\r
+ } catch (RuntimeException e) {\r
+ lastException = new IOException("ImageIO runtime exception - "+(mode==0 ? "normal" : "fallback"), e);\r
+ }\r
+ }\r
+ reader.dispose();\r
+ }\r
+ } finally {\r
+ iis.close();\r
+ }\r
+ \r
+ // If you don't have an image at the end of all readers\r
if (img == null) {\r
+ if (lastException != null) {\r
+ // rethrow exception - be aware that the exception source can be in\r
+ // multiple locations above ...\r
+ throw lastException;\r
+ }\r
LOG.log(POILogger.WARN, "Content-type: "+contentType+" is not support. Image ignored.");\r
return null;\r
}\r
\r
- BufferedImage bi = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);\r
- Graphics g = bi.getGraphics();\r
- g.drawImage(img, 0, 0, null);\r
- g.dispose();\r
- return bi;\r
+ // add alpha channel\r
+ if (img.getType() != BufferedImage.TYPE_INT_ARGB) {\r
+ BufferedImage argbImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);\r
+ Graphics g = argbImg.getGraphics();\r
+ g.drawImage(img, 0, 0, null);\r
+ g.dispose();\r
+ return argbImg;\r
+ }\r
+ \r
+ return img;\r
}\r
\r
@Override\r
// See how long it is. If it's under 28 bytes long, we can't
// read it
if(_contents.length < 28) {
- if(_contents.length >= 4) {
- // PPT95 has 4 byte size, then data
+ boolean isPP95 = dir.hasEntry("PP40");
+ // PPT95 has 4 byte size, then data
+ if (!isPP95 && _contents.length >= 4) {
int size = LittleEndian.getInt(_contents);
- //System.err.println(size);
- if(size + 4 == _contents.length) {
- throw new OldPowerPointFormatException("Based on the Current User stream, you seem to have supplied a PowerPoint95 file, which isn't supported");
- }
+ isPP95 = (size + 4 == _contents.length);
+ }
+
+ if (isPP95) {
+ throw new OldPowerPointFormatException("Based on the Current User stream, you seem to have supplied a PowerPoint95 file, which isn't supported");
+ } else {
+ throw new CorruptPowerPointFileException("The Current User stream must be at least 28 bytes long, but was only " + _contents.length);
}
- throw new CorruptPowerPointFileException("The Current User stream must be at least 28 bytes long, but was only " + _contents.length);
}
// Set everything up