Bug 60345 - Handle corrupt PICT streams
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1769226 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ead2cdd696
commit
3820215393
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package org.apache.poi.sl.draw;
|
package org.apache.poi.sl.draw;
|
||||||
|
|
||||||
|
import java.awt.AlphaComposite;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Graphics;
|
import java.awt.Graphics;
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
@ -62,7 +63,7 @@ public class BitmapImageRenderer implements ImageRenderer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the image data via ImageIO and optionally try to workaround metadata errors.
|
* Read the image data via ImageIO and optionally try to workaround metadata errors.
|
||||||
* The resulting image is of image image type {@link BufferedImage#TYPE_INT_ARGB}
|
* The resulting image is of image type {@link BufferedImage#TYPE_INT_ARGB}
|
||||||
*
|
*
|
||||||
* @param data the data stream
|
* @param data the data stream
|
||||||
* @param contentType the content type
|
* @param contentType the content type
|
||||||
@ -72,6 +73,10 @@ public class BitmapImageRenderer implements ImageRenderer {
|
|||||||
private static BufferedImage readImage(InputStream data, String contentType) throws IOException {
|
private static BufferedImage readImage(InputStream data, String contentType) throws IOException {
|
||||||
IOException lastException = null;
|
IOException lastException = null;
|
||||||
BufferedImage img = null;
|
BufferedImage img = null;
|
||||||
|
if (data.markSupported()) {
|
||||||
|
data.mark(data.available());
|
||||||
|
}
|
||||||
|
|
||||||
// currently don't use FileCacheImageInputStream,
|
// currently don't use FileCacheImageInputStream,
|
||||||
// because of the risk of filling the file handles (see #59166)
|
// because of the risk of filling the file handles (see #59166)
|
||||||
ImageInputStream iis = new MemoryCacheImageInputStream(data);
|
ImageInputStream iis = new MemoryCacheImageInputStream(data);
|
||||||
@ -84,31 +89,93 @@ public class BitmapImageRenderer implements ImageRenderer {
|
|||||||
ImageReader reader = iter.next();
|
ImageReader reader = iter.next();
|
||||||
ImageReadParam param = reader.getDefaultReadParam();
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
// 0:default mode, 1:fallback mode
|
// 0:default mode, 1:fallback mode
|
||||||
for (int mode=0; img==null && mode<2; mode++) {
|
for (int mode=0; img==null && mode<3; mode++) {
|
||||||
iis.reset();
|
lastException = null;
|
||||||
|
try {
|
||||||
|
iis.reset();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (data.markSupported()) {
|
||||||
|
data.reset();
|
||||||
|
data.mark(data.available());
|
||||||
|
iis.close();
|
||||||
|
iis = new MemoryCacheImageInputStream(data);
|
||||||
|
} else {
|
||||||
|
// can't restore the input stream, so we need to stop processing here
|
||||||
|
lastException = e;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
iis.mark();
|
iis.mark();
|
||||||
|
|
||||||
if (mode == 1) {
|
try {
|
||||||
// fallback mode for invalid image band metadata
|
|
||||||
// see http://stackoverflow.com/questions/10416378
|
switch (mode) {
|
||||||
Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);
|
case 0:
|
||||||
while (imageTypes.hasNext()) {
|
reader.setInput(iis, false, true);
|
||||||
ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
|
img = reader.read(0, param);
|
||||||
int bufferedImageType = imageTypeSpecifier.getBufferedImageType();
|
break;
|
||||||
if (bufferedImageType == BufferedImage.TYPE_BYTE_GRAY) {
|
case 1: {
|
||||||
param.setDestinationType(imageTypeSpecifier);
|
// try to load picture in gray scale mode
|
||||||
|
// fallback mode for invalid image band metadata
|
||||||
|
// see http://stackoverflow.com/questions/10416378
|
||||||
|
Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);
|
||||||
|
while (imageTypes.hasNext()) {
|
||||||
|
ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
|
||||||
|
int bufferedImageType = imageTypeSpecifier.getBufferedImageType();
|
||||||
|
if (bufferedImageType == BufferedImage.TYPE_BYTE_GRAY) {
|
||||||
|
param.setDestinationType(imageTypeSpecifier);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.setInput(iis, false, true);
|
||||||
|
img = reader.read(0, param);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
// try to load truncated pictures by supplying a BufferedImage
|
||||||
|
// and use the processed data up till the point of error
|
||||||
|
reader.setInput(iis, false, true);
|
||||||
|
int height = reader.getHeight(0);
|
||||||
|
int width = reader.getWidth(0);
|
||||||
|
|
||||||
|
Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);
|
||||||
|
if (imageTypes.hasNext()) {
|
||||||
|
ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
|
||||||
|
img = imageTypeSpecifier.createBufferedImage(width, height);
|
||||||
|
param.setDestination(img);
|
||||||
|
} else {
|
||||||
|
lastException = new IOException("unable to load even a truncated version of the image.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
reader.read(0, param);
|
||||||
|
} finally {
|
||||||
|
if (img.getType() != BufferedImage.TYPE_INT_ARGB) {
|
||||||
|
int y = findTruncatedBlackBox(img, width, height);
|
||||||
|
if (y < height) {
|
||||||
|
BufferedImage argbImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
|
||||||
|
Graphics2D g = argbImg.createGraphics();
|
||||||
|
g.clipRect(0, 0, width, y);
|
||||||
|
g.drawImage(img, 0, 0, null);
|
||||||
|
g.dispose();
|
||||||
|
img.flush();
|
||||||
|
img = argbImg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
reader.setInput(iis, false, true);
|
|
||||||
img = reader.read(0, param);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
lastException = e;
|
if (mode < 2) {
|
||||||
|
lastException = e;
|
||||||
|
}
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
lastException = new IOException("ImageIO runtime exception - "+(mode==0 ? "normal" : "fallback"), e);
|
if (mode < 2) {
|
||||||
|
lastException = new IOException("ImageIO runtime exception - "+(mode==0 ? "normal" : "fallback"), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reader.dispose();
|
reader.dispose();
|
||||||
@ -140,6 +207,21 @@ public class BitmapImageRenderer implements ImageRenderer {
|
|||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int findTruncatedBlackBox(BufferedImage img, int width, int height) {
|
||||||
|
// scan through the image to find the black box after the truncated data
|
||||||
|
int h = height-1;
|
||||||
|
for (; h > 0; h--) {
|
||||||
|
for (int w = width-1; w > 0; w-=width/10) {
|
||||||
|
int p = img.getRGB(w, h);
|
||||||
|
if (p != 0xff000000) {
|
||||||
|
return h+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BufferedImage getImage() {
|
public BufferedImage getImage() {
|
||||||
return img;
|
return img;
|
||||||
|
@ -116,6 +116,10 @@ public abstract class Metafile extends HSLFPictureData {
|
|||||||
public int getSize(){
|
public int getSize(){
|
||||||
return 34;
|
return 34;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getWmfSize() {
|
||||||
|
return wmfsize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static byte[] compress(byte[] bytes, int offset, int length) throws IOException {
|
protected static byte[] compress(byte[] bytes, int offset, int length) throws IOException {
|
||||||
|
@ -25,12 +25,16 @@ import java.io.IOException;
|
|||||||
import java.util.zip.InflaterInputStream;
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
import org.apache.poi.hslf.exceptions.HSLFException;
|
import org.apache.poi.hslf.exceptions.HSLFException;
|
||||||
|
import org.apache.poi.util.POILogFactory;
|
||||||
|
import org.apache.poi.util.POILogger;
|
||||||
import org.apache.poi.util.Units;
|
import org.apache.poi.util.Units;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents Macintosh PICT picture data.
|
* Represents Macintosh PICT picture data.
|
||||||
*/
|
*/
|
||||||
public final class PICT extends Metafile {
|
public final class PICT extends Metafile {
|
||||||
|
private static POILogger LOG = POILogFactory.getLogger(PICT.class);
|
||||||
|
|
||||||
public static class NativeHeader {
|
public static class NativeHeader {
|
||||||
/**
|
/**
|
||||||
* skip the first 512 bytes - they are MAC specific crap
|
* skip the first 512 bytes - they are MAC specific crap
|
||||||
@ -122,18 +126,37 @@ public final class PICT extends Metafile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private byte[] read(byte[] data, int pos) throws IOException {
|
private byte[] read(byte[] data, int pos) throws IOException {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
||||||
Header header = new Header();
|
Header header = new Header();
|
||||||
header.read(data, pos);
|
header.read(data, pos);
|
||||||
bis.skip(pos + header.getSize());
|
bis.skip(pos + header.getSize());
|
||||||
InflaterInputStream inflater = new InflaterInputStream( bis );
|
|
||||||
byte[] chunk = new byte[4096];
|
byte[] chunk = new byte[4096];
|
||||||
int count;
|
ByteArrayOutputStream out = new ByteArrayOutputStream(header.getWmfSize());
|
||||||
while ((count = inflater.read(chunk)) >=0 ) {
|
InflaterInputStream inflater = new InflaterInputStream( bis );
|
||||||
out.write(chunk,0,count);
|
try {
|
||||||
|
int count;
|
||||||
|
while ((count = inflater.read(chunk)) >=0 ) {
|
||||||
|
out.write(chunk,0,count);
|
||||||
|
// PICT zip-stream can be erroneous, so we clear the array to determine
|
||||||
|
// the maximum of read bytes, after the inflater crashed
|
||||||
|
bytefill(chunk, (byte)0);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
int lastLen;
|
||||||
|
for (lastLen=chunk.length-1; lastLen>=0 && chunk[lastLen] == 0; lastLen--);
|
||||||
|
if (++lastLen > 0) {
|
||||||
|
if (header.getWmfSize() > out.size()) {
|
||||||
|
// sometimes the wmfsize is smaller than the amount of already successfully read bytes
|
||||||
|
// in this case we take the lastLen as-is, otherwise we truncate it to the given size
|
||||||
|
lastLen = Math.min(lastLen, header.getWmfSize() - out.size());
|
||||||
|
}
|
||||||
|
out.write(chunk,0,lastLen);
|
||||||
|
}
|
||||||
|
// End of picture marker for PICT is 0x00 0xFF
|
||||||
|
LOG.log(POILogger.ERROR, "PICT zip-stream is invalid, read as much as possible. Uncompressed length of header: "+header.getWmfSize()+" / Read bytes: "+out.size(), e);
|
||||||
|
} finally {
|
||||||
|
inflater.close();
|
||||||
}
|
}
|
||||||
inflater.close();
|
|
||||||
return out.toByteArray();
|
return out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,4 +215,22 @@ public final class PICT extends Metafile {
|
|||||||
throw new IllegalArgumentException(signature+" is not a valid instance/signature value for PICT");
|
throw new IllegalArgumentException(signature+" is not a valid instance/signature value for PICT");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize a smaller piece of the array and use the System.arraycopy
|
||||||
|
* call to fill in the rest of the array in an expanding binary fashion
|
||||||
|
*/
|
||||||
|
private static void bytefill(byte[] array, byte value) {
|
||||||
|
// http://stackoverflow.com/questions/9128737/fastest-way-to-set-all-values-of-an-array
|
||||||
|
int len = array.length;
|
||||||
|
|
||||||
|
if (len > 0){
|
||||||
|
array[0] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < len; i += i) {
|
||||||
|
System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i) : i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
BIN
test-data/slideshow/bug60345_Jankovic_final_Retreat_2002.ppt
Normal file
BIN
test-data/slideshow/bug60345_Jankovic_final_Retreat_2002.ppt
Normal file
Binary file not shown.
BIN
test-data/slideshow/bug60345_paperfigures.ppt
Normal file
BIN
test-data/slideshow/bug60345_paperfigures.ppt
Normal file
Binary file not shown.
BIN
test-data/slideshow/bug60345_suba.ppt
Normal file
BIN
test-data/slideshow/bug60345_suba.ppt
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user