View | Details | Raw Unified | Return to bug 60345
Collapse All | Expand All

(-)src/java/org/apache/poi/sl/draw/BitmapImageRenderer.java (-18 / +100 lines)
Lines 17-22 Link Here
17
17
18
package org.apache.poi.sl.draw;
18
package org.apache.poi.sl.draw;
19
19
20
import java.awt.AlphaComposite;
20
import java.awt.Dimension;
21
import java.awt.Dimension;
21
import java.awt.Graphics;
22
import java.awt.Graphics;
22
import java.awt.Graphics2D;
23
import java.awt.Graphics2D;
Lines 62-68 Link Here
62
    
63
    
63
    /**
64
    /**
64
     * Read the image data via ImageIO and optionally try to workaround metadata errors.
65
     * Read the image data via ImageIO and optionally try to workaround metadata errors.
65
     * The resulting image is of image image type {@link BufferedImage#TYPE_INT_ARGB}
66
     * The resulting image is of image type {@link BufferedImage#TYPE_INT_ARGB}
66
     *
67
     *
67
     * @param data the data stream
68
     * @param data the data stream
68
     * @param contentType the content type
69
     * @param contentType the content type
Lines 72-77 Link Here
72
    private static BufferedImage readImage(InputStream data, String contentType) throws IOException {
73
    private static BufferedImage readImage(InputStream data, String contentType) throws IOException {
73
        IOException lastException = null;
74
        IOException lastException = null;
74
        BufferedImage img = null;
75
        BufferedImage img = null;
76
        if (data.markSupported()) {
77
            data.mark(data.available());
78
        }
79
        
75
        // currently don't use FileCacheImageInputStream,
80
        // currently don't use FileCacheImageInputStream,
76
        // because of the risk of filling the file handles (see #59166)
81
        // because of the risk of filling the file handles (see #59166)
77
        ImageInputStream iis = new MemoryCacheImageInputStream(data);
82
        ImageInputStream iis = new MemoryCacheImageInputStream(data);
Lines 84-114 Link Here
84
                ImageReader reader = iter.next();
89
                ImageReader reader = iter.next();
85
                ImageReadParam param = reader.getDefaultReadParam();
90
                ImageReadParam param = reader.getDefaultReadParam();
86
                // 0:default mode, 1:fallback mode
91
                // 0:default mode, 1:fallback mode
87
                for (int mode=0; img==null && mode<2; mode++) {
92
                for (int mode=0; img==null && mode<3; mode++) {
88
                    iis.reset();
93
                    lastException = null;
94
                    try {
95
                        iis.reset();
96
                    } catch (IOException e) {
97
                        if (data.markSupported()) {
98
                            data.reset();
99
                            data.mark(data.available());
100
                            iis.close();
101
                            iis = new MemoryCacheImageInputStream(data);
102
                        } else {
103
                            // can't restore the input stream, so we need to stop processing here
104
                            lastException = e;
105
                            break;
106
                        }
107
                    }
89
                    iis.mark();
108
                    iis.mark();
90
109
91
                    if (mode == 1) {
110
                    try {
92
                        // fallback mode for invalid image band metadata
111
                    
93
                        // see http://stackoverflow.com/questions/10416378
112
                        switch (mode) {
94
                        Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);
113
                            case 0:
95
                        while (imageTypes.hasNext()) {
114
                                reader.setInput(iis, false, true);
96
                            ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
115
                                img = reader.read(0, param);
97
                            int bufferedImageType = imageTypeSpecifier.getBufferedImageType();
98
                            if (bufferedImageType == BufferedImage.TYPE_BYTE_GRAY) {
99
                                param.setDestinationType(imageTypeSpecifier);
100
                                break;
116
                                break;
117
                            case 1: {
118
                                // try to load picture in gray scale mode
119
                                // fallback mode for invalid image band metadata
120
                                // see http://stackoverflow.com/questions/10416378
121
                                Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);
122
                                while (imageTypes.hasNext()) {
123
                                    ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
124
                                    int bufferedImageType = imageTypeSpecifier.getBufferedImageType();
125
                                    if (bufferedImageType == BufferedImage.TYPE_BYTE_GRAY) {
126
                                        param.setDestinationType(imageTypeSpecifier);
127
                                        break;
128
                                    }
129
                                }
130
                                reader.setInput(iis, false, true);
131
                                img = reader.read(0, param);
132
                                break;
101
                            }
133
                            }
134
                            case 2: {
135
                                // try to load truncated pictures by supplying a BufferedImage
136
                                // and use the processed data up till the point of error
137
                                reader.setInput(iis, false, true);
138
                                int height = reader.getHeight(0);
139
                                int width = reader.getWidth(0);
140
                                
141
                                Iterator<ImageTypeSpecifier> imageTypes = reader.getImageTypes(0);
142
                                if (imageTypes.hasNext()) {
143
                                    ImageTypeSpecifier imageTypeSpecifier = imageTypes.next();
144
                                    img = imageTypeSpecifier.createBufferedImage(width, height);
145
                                    param.setDestination(img);
146
                                } else {
147
                                    lastException = new IOException("unable to load even a truncated version of the image.");
148
                                    break;
149
                                }
150
151
                                try {
152
                                    reader.read(0, param);
153
                                } finally {
154
                                    if (img.getType() != BufferedImage.TYPE_INT_ARGB) {
155
                                        int y = findTruncatedBlackBox(img, width, height);
156
                                        if (y < height) {
157
                                            BufferedImage argbImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
158
                                            Graphics2D g = argbImg.createGraphics();
159
                                            g.clipRect(0, 0, width, y);
160
                                            g.drawImage(img, 0, 0, null);
161
                                            g.dispose();
162
                                            img.flush();
163
                                            img = argbImg;
164
                                        }
165
                                    }
166
                                }                                
167
                                break;
168
                            }
102
                        }
169
                        }
103
                    }
104
                    
170
                    
105
                    try {
106
                        reader.setInput(iis, false, true);
107
                        img = reader.read(0, param);
108
                    } catch (IOException e) {
171
                    } catch (IOException e) {
109
                        lastException = e;
172
                        if (mode < 2) {
173
                            lastException = e;
174
                        }
110
                    } catch (RuntimeException e) {
175
                    } catch (RuntimeException e) {
111
                        lastException = new IOException("ImageIO runtime exception - "+(mode==0 ? "normal" : "fallback"), e);
176
                        if (mode < 2) {
177
                            lastException = new IOException("ImageIO runtime exception - "+(mode==0 ? "normal" : "fallback"), e);
178
                        }
112
                    }
179
                    }
113
                }
180
                }
114
                reader.dispose();
181
                reader.dispose();
Lines 140-145 Link Here
140
        return img;
207
        return img;
141
    }
208
    }
142
209
210
    private static int findTruncatedBlackBox(BufferedImage img, int width, int height) {
211
        // scan through the image to find the black box after the truncated data
212
        int h = height-1;
213
        for (; h > 0; h--) {
214
            for (int w = width-1; w > 0; w-=width/10) {
215
                int p = img.getRGB(w, h);
216
                if (p != 0xff000000) {
217
                    return h+1;
218
                }
219
            }
220
        }
221
        return 0;
222
    }
223
    
224
    
143
    @Override
225
    @Override
144
    public BufferedImage getImage() {
226
    public BufferedImage getImage() {
145
        return img;
227
        return img;
(-)src/scratchpad/src/org/apache/poi/hslf/blip/Metafile.java (+4 lines)
Lines 116-121 Link Here
116
        public int getSize(){
116
        public int getSize(){
117
            return 34;
117
            return 34;
118
        }
118
        }
119
        
120
        public int getWmfSize() {
121
            return wmfsize;
122
        }
119
    }
123
    }
120
124
121
    protected static byte[] compress(byte[] bytes, int offset, int length) throws IOException {
125
    protected static byte[] compress(byte[] bytes, int offset, int length) throws IOException {
(-)src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java (-6 / +47 lines)
Lines 25-30 Link Here
25
import java.util.zip.InflaterInputStream;
25
import java.util.zip.InflaterInputStream;
26
26
27
import org.apache.poi.hslf.exceptions.HSLFException;
27
import org.apache.poi.hslf.exceptions.HSLFException;
28
import org.apache.poi.util.POILogFactory;
29
import org.apache.poi.util.POILogger;
28
import org.apache.poi.util.Units;
30
import org.apache.poi.util.Units;
29
31
30
/**
32
/**
Lines 31-36 Link Here
31
 * Represents Macintosh PICT picture data.
33
 * Represents Macintosh PICT picture data.
32
 */
34
 */
33
public final class PICT extends Metafile {
35
public final class PICT extends Metafile {
36
    private static POILogger LOG = POILogFactory.getLogger(PICT.class);
37
    
34
    public static class NativeHeader {
38
    public static class NativeHeader {
35
        /**
39
        /**
36
         * skip the first 512 bytes - they are MAC specific crap
40
         * skip the first 512 bytes - they are MAC specific crap
Lines 122-139 Link Here
122
    }
126
    }
123
127
124
    private byte[] read(byte[] data, int pos) throws IOException {
128
    private byte[] read(byte[] data, int pos) throws IOException {
125
        ByteArrayOutputStream out = new ByteArrayOutputStream();
126
        ByteArrayInputStream bis = new ByteArrayInputStream(data);
129
        ByteArrayInputStream bis = new ByteArrayInputStream(data);
127
        Header header = new Header();
130
        Header header = new Header();
128
        header.read(data, pos);
131
        header.read(data, pos);
129
        bis.skip(pos + header.getSize());
132
        bis.skip(pos + header.getSize());
133
        byte[] chunk = new byte[4096];
134
        ByteArrayOutputStream out = new ByteArrayOutputStream(header.getWmfSize());
130
        InflaterInputStream inflater = new InflaterInputStream( bis );
135
        InflaterInputStream inflater = new InflaterInputStream( bis );
131
        byte[] chunk = new byte[4096];
136
        try {
132
        int count;
137
            int count;
133
        while ((count = inflater.read(chunk)) >=0 ) {
138
            while ((count = inflater.read(chunk)) >=0 ) {
134
            out.write(chunk,0,count);
139
                out.write(chunk,0,count);
140
                // PICT zip-stream can be erroneous, so we clear the array to determine
141
                // the maximum of read bytes, after the inflater crashed
142
                bytefill(chunk, (byte)0);
143
            }
144
        } catch (Exception e) {
145
            int lastLen;
146
            for (lastLen=chunk.length-1; lastLen>=0 && chunk[lastLen] == 0; lastLen--);
147
            if (++lastLen > 0) {
148
                if (header.getWmfSize() > out.size()) {
149
                    // sometimes the wmfsize is smaller than the amount of already successfully read bytes
150
                    // in this case we take the lastLen as-is, otherwise we truncate it to the given size
151
                    lastLen = Math.min(lastLen, header.getWmfSize() - out.size());
152
                }
153
                out.write(chunk,0,lastLen);
154
            }
155
            // End of picture marker for PICT is 0x00 0xFF
156
            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);
157
        } finally {
158
            inflater.close();
135
        }
159
        }
136
        inflater.close();
137
        return out.toByteArray();
160
        return out.toByteArray();
138
    }
161
    }
139
162
Lines 192-195 Link Here
192
                throw new IllegalArgumentException(signature+" is not a valid instance/signature value for PICT");
215
                throw new IllegalArgumentException(signature+" is not a valid instance/signature value for PICT");
193
        }        
216
        }        
194
    }
217
    }
218
    
219
    
220
    /*
221
     * initialize a smaller piece of the array and use the System.arraycopy 
222
     * call to fill in the rest of the array in an expanding binary fashion
223
     */
224
    private static void bytefill(byte[] array, byte value) {
225
        // http://stackoverflow.com/questions/9128737/fastest-way-to-set-all-values-of-an-array
226
        int len = array.length;
227
228
        if (len > 0){
229
            array[0] = value;
230
        }
231
232
        for (int i = 1; i < len; i += i) {
233
            System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i) : i);
234
        }
235
    }
195
}
236
}

Return to bug 60345