You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2016/11/10 23:04:26 UTC

svn commit: r1769226 - in /poi: site/src/documentation/content/xdocs/ trunk/src/java/org/apache/poi/sl/draw/ trunk/src/scratchpad/src/org/apache/poi/hslf/blip/ trunk/test-data/slideshow/

Author: kiwiwings
Date: Thu Nov 10 23:04:25 2016
New Revision: 1769226

URL: http://svn.apache.org/viewvc?rev=1769226&view=rev
Log:
Bug 60345 - Handle corrupt PICT streams

Added:
    poi/trunk/test-data/slideshow/bug60345_Jankovic_final_Retreat_2002.ppt   (with props)
    poi/trunk/test-data/slideshow/bug60345_paperfigures.ppt   (with props)
    poi/trunk/test-data/slideshow/bug60345_suba.ppt   (with props)
Modified:
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/Metafile.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1769226&r1=1769225&r2=1769226&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Thu Nov 10 23:04:25 2016
@@ -64,7 +64,8 @@
         <summary-item>Examples to encrypt temp files in SXSSF</summary-item>
       </summary>
       <actions>
-        <action dev="PD" type="add" fixes-bug="59273" module="XSLF,OOXML">Unable to create pptx file by potx file using Apache POI</action>
+        <action dev="PD" type="add" fixes-bug="60345" module="HSLF">Handle corrupt PICT streams</action>
+        <action dev="PD" type="fix" fixes-bug="59273" module="XSLF,OOXML">Unable to create pptx file by potx file using Apache POI</action>
         <action dev="PD" type="add" fixes-bug="github-42" module="SS Common">Add setFormattingRanges() to interface ConditionalFormatting</action>
         <action dev="PD" type="add" fixes-bug="60321" module="SXSSF examples">Examples: encrypt temp files created when unzipping or zipping an SXSSF workbook</action>
         <action dev="PD" type="fix" fixes-bug="60337" module="XWPF">Table row isRepeatingHeader and isCantSplit throw NPE</action>

Modified: poi/trunk/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java?rev=1769226&r1=1769225&r2=1769226&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java (original)
+++ poi/trunk/src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java Thu Nov 10 23:04:25 2016
@@ -17,6 +17,7 @@
 
 package org.apache.poi.sl.draw;
 
+import java.awt.AlphaComposite;
 import java.awt.Dimension;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
@@ -62,7 +63,7 @@ public class BitmapImageRenderer impleme
     
     /**
      * 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 contentType the content type
@@ -72,6 +73,10 @@ public class BitmapImageRenderer impleme
     private static BufferedImage readImage(InputStream data, String contentType) throws IOException {
         IOException lastException = null;
         BufferedImage img = null;
+        if (data.markSupported()) {
+            data.mark(data.available());
+        }
+        
         // currently don't use FileCacheImageInputStream,
         // because of the risk of filling the file handles (see #59166)
         ImageInputStream iis = new MemoryCacheImageInputStream(data);
@@ -84,31 +89,93 @@ public class BitmapImageRenderer impleme
                 ImageReader reader = iter.next();
                 ImageReadParam param = reader.getDefaultReadParam();
                 // 0:default mode, 1:fallback mode
-                for (int mode=0; img==null && mode<2; mode++) {
-                    iis.reset();
+                for (int mode=0; img==null && mode<3; mode++) {
+                    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();
 
-                    if (mode == 1) {
-                        // 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);
+                    try {
+                    
+                        switch (mode) {
+                            case 0:
+                                reader.setInput(iis, false, true);
+                                img = reader.read(0, param);
+                                break;
+                            case 1: {
+                                // 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;
                             }
                         }
-                    }
                     
-                    try {
-                        reader.setInput(iis, false, true);
-                        img = reader.read(0, param);
                     } catch (IOException e) {
-                        lastException = e;
+                        if (mode < 2) {
+                            lastException = 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();
@@ -140,6 +207,21 @@ public class BitmapImageRenderer impleme
         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
     public BufferedImage getImage() {
         return img;

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/Metafile.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/Metafile.java?rev=1769226&r1=1769225&r2=1769226&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/Metafile.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/Metafile.java Thu Nov 10 23:04:25 2016
@@ -116,6 +116,10 @@ public abstract class Metafile extends H
         public int getSize(){
             return 34;
         }
+        
+        public int getWmfSize() {
+            return wmfsize;
+        }
     }
 
     protected static byte[] compress(byte[] bytes, int offset, int length) throws IOException {

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java?rev=1769226&r1=1769225&r2=1769226&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java Thu Nov 10 23:04:25 2016
@@ -25,12 +25,16 @@ import java.io.IOException;
 import java.util.zip.InflaterInputStream;
 
 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;
 
 /**
  * Represents Macintosh PICT picture data.
  */
 public final class PICT extends Metafile {
+    private static POILogger LOG = POILogFactory.getLogger(PICT.class);
+    
     public static class NativeHeader {
         /**
          * 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 {
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
         ByteArrayInputStream bis = new ByteArrayInputStream(data);
         Header header = new Header();
         header.read(data, pos);
         bis.skip(pos + header.getSize());
-        InflaterInputStream inflater = new InflaterInputStream( bis );
         byte[] chunk = new byte[4096];
-        int count;
-        while ((count = inflater.read(chunk)) >=0 ) {
-            out.write(chunk,0,count);
+        ByteArrayOutputStream out = new ByteArrayOutputStream(header.getWmfSize());
+        InflaterInputStream inflater = new InflaterInputStream( bis );
+        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();
     }
 
@@ -192,4 +215,22 @@ public final class PICT extends Metafile
                 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);
+        }
+    }
 }

Added: poi/trunk/test-data/slideshow/bug60345_Jankovic_final_Retreat_2002.ppt
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/slideshow/bug60345_Jankovic_final_Retreat_2002.ppt?rev=1769226&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/slideshow/bug60345_Jankovic_final_Retreat_2002.ppt
------------------------------------------------------------------------------
    svn:mime-type = application/vnd.ms-powerpoint

Added: poi/trunk/test-data/slideshow/bug60345_paperfigures.ppt
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/slideshow/bug60345_paperfigures.ppt?rev=1769226&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/slideshow/bug60345_paperfigures.ppt
------------------------------------------------------------------------------
    svn:mime-type = application/vnd.ms-powerpoint

Added: poi/trunk/test-data/slideshow/bug60345_suba.ppt
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/slideshow/bug60345_suba.ppt?rev=1769226&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/slideshow/bug60345_suba.ppt
------------------------------------------------------------------------------
    svn:mime-type = application/vnd.ms-powerpoint



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org