]> source.dussan.org Git - poi.git/commitdiff
#60427 - Creating pictures in PowerPoint slides requires scratchpad-jar for adding...
authorAndreas Beeker <kiwiwings@apache.org>
Mon, 28 Nov 2016 20:51:02 +0000 (20:51 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Mon, 28 Nov 2016 20:51:02 +0000 (20:51 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1771812 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/sl/image/ImageHeaderBitmap.java [new file with mode: 0644]
src/java/org/apache/poi/sl/image/ImageHeaderEMF.java [new file with mode: 0644]
src/java/org/apache/poi/sl/image/ImageHeaderPICT.java [new file with mode: 0644]
src/java/org/apache/poi/sl/image/ImageHeaderWMF.java [new file with mode: 0644]
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java
src/scratchpad/src/org/apache/poi/hslf/blip/EMF.java
src/scratchpad/src/org/apache/poi/hslf/blip/PICT.java
src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java
src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java

diff --git a/src/java/org/apache/poi/sl/image/ImageHeaderBitmap.java b/src/java/org/apache/poi/sl/image/ImageHeaderBitmap.java
new file mode 100644 (file)
index 0000000..3ecc6e5
--- /dev/null
@@ -0,0 +1,57 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.image;
+
+import java.awt.Dimension;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.Units;
+
+@Internal
+public class ImageHeaderBitmap {
+    private static final POILogger LOG = POILogFactory.getLogger(ImageHeaderBitmap.class);
+    
+    private final Dimension size;
+    
+    public ImageHeaderBitmap(byte data[], int offset) {
+        BufferedImage img = null;
+        try {
+            img = ImageIO.read(new ByteArrayInputStream(data, offset, data.length-offset));
+        } catch (IOException e) {
+            LOG.log(POILogger.WARN, "Can't determine image dimensions", e);
+        }
+        // set dummy size, in case of dummy dimension can't be set
+        size = (img == null)
+            ? new Dimension(200,200)
+            : new Dimension(
+                (int)Units.pixelToPoints(img.getWidth()),
+                (int)Units.pixelToPoints(img.getHeight())
+            );
+    }
+
+    public Dimension getSize() {
+        return size;
+    }
+}
diff --git a/src/java/org/apache/poi/sl/image/ImageHeaderEMF.java b/src/java/org/apache/poi/sl/image/ImageHeaderEMF.java
new file mode 100644 (file)
index 0000000..7845c0f
--- /dev/null
@@ -0,0 +1,68 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.image;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LocaleUtil;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+
+@Internal
+public class ImageHeaderEMF {
+    private static POILogger LOG = POILogFactory.getLogger(ImageHeaderEMF.class);
+
+    private final static String EMF_SIGNATURE = " EMF"; // 0x464D4520 (LE)
+    
+    // rectangular inclusive-inclusive bounds, in device units, of the smallest 
+    // rectangle that can be drawn around the image stored in the metafile.
+    private final Rectangle deviceBounds;
+
+    public ImageHeaderEMF(byte data[], int offset) {
+        int type = (int)LittleEndian.getUInt(data, offset); offset += 4;
+        if (type != 1) {
+            LOG.log(POILogger.WARN, "Invalid EMF picture - invalid type");
+            deviceBounds = new Rectangle(0,0,200,200);
+            return;
+        }
+        // ignore header size
+        offset += 4;
+        int left = LittleEndian.getInt(data, offset); offset += 4;
+        int top = LittleEndian.getInt(data, offset); offset += 4;
+        int right = LittleEndian.getInt(data, offset); offset += 4;
+        int bottom = LittleEndian.getInt(data, offset); offset += 4;
+        deviceBounds = new Rectangle(left, top, right-left, bottom-top);
+        // ignore frame bounds
+        offset += 16;
+        String signature = new String(data, offset, EMF_SIGNATURE.length(), LocaleUtil.CHARSET_1252);
+        if (!EMF_SIGNATURE.equals(signature)) {
+            LOG.log(POILogger.WARN, "Invalid EMF picture - invalid signature");
+        }
+    }
+
+    public Dimension getSize() {
+        return deviceBounds.getSize();
+    }
+    
+    public Rectangle getBounds() {
+        return deviceBounds;
+    }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/poi/sl/image/ImageHeaderPICT.java b/src/java/org/apache/poi/sl/image/ImageHeaderPICT.java
new file mode 100644 (file)
index 0000000..d994fb8
--- /dev/null
@@ -0,0 +1,103 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.image;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.Units;
+
+@Internal
+public class ImageHeaderPICT {
+    /**
+     * skip the first 512 bytes - they are MAC specific crap
+     */
+    public static final int PICT_HEADER_OFFSET = 512;
+    
+    public static final double DEFAULT_RESOLUTION = Units.POINT_DPI;
+    
+    private static final byte V2_HEADER[] = {
+        0x00, 0x11,       // v2 version opcode
+        0x02, (byte)0xFF, // version number of new picture
+        0x0C, 0x00,        // header opcode
+        (byte)0xFF, (byte)0xFE, 0x00, 0x00 // pic size dummy
+    };
+
+    private final Rectangle bounds;
+    private final double hRes, vRes;
+    
+    public ImageHeaderPICT(byte data[], int offset) {
+        // http://mirrors.apple2.org.za/apple.cabi.net/Graphics/PICT.and_QT.INFO/PICT.file.format.TI.txt
+
+        // low order 16 bits of picture size - can be ignored
+        offset += 2;
+        // rectangular bounding box of picture, at 72 dpi
+        // rect : 8 bytes (top, left, bottom, right: integer)
+        int y1 = readUnsignedShort(data, offset); offset += 2;
+        int x1 = readUnsignedShort(data, offset); offset += 2;
+        int y2 = readUnsignedShort(data, offset); offset += 2;
+        int x2 = readUnsignedShort(data, offset); offset += 2;
+
+        // check for version 2 ... otherwise we don't read any further
+        boolean isV2 = true;
+        for (byte b : V2_HEADER) {
+            if (b != data[offset++]) {
+                isV2 = false;
+                break;
+            }
+        }
+        
+        if (isV2) {
+            // 4 bytes - fixed, horizontal resolution (dpi) of source data
+            hRes = readFixedPoint(data, offset); offset += 4;
+            // 4 bytes - fixed, vertical resolution (dpi) of source data
+            vRes = readFixedPoint(data, offset); offset += 4;
+        } else {
+            hRes = DEFAULT_RESOLUTION;
+            vRes = DEFAULT_RESOLUTION;
+        }
+        
+        bounds = new Rectangle(x1,y1,x2-x1,y2-y1);
+    }
+
+    public Dimension getSize() {
+        int height = (int)Math.round(bounds.height*DEFAULT_RESOLUTION/vRes);
+        int width = (int)Math.round(bounds.width*DEFAULT_RESOLUTION/hRes);
+        return new Dimension(width, height);
+    }
+
+    public Rectangle getBounds() {
+        return bounds;
+    }
+    
+    private static int readUnsignedShort(byte data[], int offset) {
+        int b0 = data[offset] & 0xFF;
+        int b1 = data[offset+1] & 0xFF;
+        return b0 << 8 | b1;
+    }
+
+    private static double readFixedPoint(byte data[], int offset) {
+        int b0 = data[offset] & 0xFF;
+        int b1 = data[offset+1] & 0xFF;
+        int b2 = data[offset+2] & 0xFF;
+        int b3 = data[offset+3] & 0xFF;
+        int i = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
+        return i / (double)0x10000;
+    }
+}
diff --git a/src/java/org/apache/poi/sl/image/ImageHeaderWMF.java b/src/java/org/apache/poi/sl/image/ImageHeaderWMF.java
new file mode 100644 (file)
index 0000000..b57b4d0
--- /dev/null
@@ -0,0 +1,154 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.sl.image;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.Units;
+
+/**
+ * Aldus Placeable Metafile header - 22 byte structure before WMF data.
+ * <ul>
+ *  <li>int Key;               Magic number (always 9AC6CDD7h)
+ *  <li>short  Handle;         Metafile HANDLE number (always 0)
+ *  <li>short Left;            Left coordinate in metafile units
+ *  <li>short Top;             Top coordinate in metafile units
+ *  <li>short Right;           Right coordinate in metafile units
+ *  <li>short Bottom;          Bottom coordinate in metafile units
+ *  <li>short  Inch;           Number of metafile units per inch
+ *  <li>int Reserved;          Reserved (always 0)
+ *  <li>short  Checksum;       Checksum value for previous 10 shorts
+ * </ul>
+ */
+@Internal
+public class ImageHeaderWMF {
+
+    public static final int APMHEADER_KEY = 0x9AC6CDD7;
+    private static POILogger LOG = POILogFactory.getLogger(ImageHeaderWMF.class);
+
+    @SuppressWarnings("unused")
+    private final int handle;
+    private final int left, top, right, bottom;
+
+    /**
+     * The number of logical units per inch used to represent the image.
+     * This value can be used to scale an image. By convention, an image is
+     * considered to be recorded at 1440 logical units (twips) per inch. 
+     * Thus, a value of 720 specifies that the image SHOULD be rendered at
+     * twice its normal size, and a value of 2880 specifies that the image
+     * SHOULD be rendered at half its normal size.
+     */
+    private final int inch; 
+    @SuppressWarnings("unused")
+    private final int reserved;
+    private int checksum;
+    
+    public ImageHeaderWMF(Rectangle dim) {
+        handle = 0;
+        left = dim.x;
+        top = dim.y;
+        right = dim.x + dim.width;
+        bottom = dim.y + dim.height;
+        inch = Units.POINT_DPI; //default resolution is 72 dpi
+        reserved = 0;
+    }
+
+    public ImageHeaderWMF(byte[] data, int pos) {
+        int key = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE; //header key
+        if (key != APMHEADER_KEY) {
+            LOG.log(POILogger.WARN, "WMF file doesn't contain a placeable header - ignore parsing");
+            handle = 0;
+            left = 0;
+            top = 0;
+            right = 200;
+            bottom = 200;
+            inch = Units.POINT_DPI; //default resolution is 72 dpi
+            reserved = 0;
+            return;
+        }
+
+        handle = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;
+        left = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
+        top = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
+        right = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
+        bottom = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
+
+        inch = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;
+        reserved = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;
+
+        checksum = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
+        if (checksum != getChecksum()){
+            LOG.log(POILogger.WARN, "WMF checksum does not match the header data");
+        }
+    }
+
+    /**
+     * Returns a checksum value for the previous 10 shorts in the header.
+     * The checksum is calculated by XORing each short value to an initial value of 0:
+     */
+    public int getChecksum(){
+        int cs = 0;
+        cs ^=  (APMHEADER_KEY & 0x0000FFFF);
+        cs ^= ((APMHEADER_KEY & 0xFFFF0000) >> 16);
+        cs ^= left;
+        cs ^= top;
+        cs ^= right;
+        cs ^= bottom;
+        cs ^= inch;
+        return cs;
+    }
+
+    public void write(OutputStream out) throws IOException {
+        byte[] header = new byte[22];
+        int pos = 0;
+        LittleEndian.putInt(header, pos, APMHEADER_KEY); pos += LittleEndian.INT_SIZE; //header key
+        LittleEndian.putUShort(header, pos, 0); pos += LittleEndian.SHORT_SIZE; //hmf
+        LittleEndian.putUShort(header, pos, left); pos += LittleEndian.SHORT_SIZE; //left
+        LittleEndian.putUShort(header, pos, top); pos += LittleEndian.SHORT_SIZE; //top
+        LittleEndian.putUShort(header, pos, right); pos += LittleEndian.SHORT_SIZE; //right
+        LittleEndian.putUShort(header, pos, bottom); pos += LittleEndian.SHORT_SIZE; //bottom
+        LittleEndian.putUShort(header, pos, inch); pos += LittleEndian.SHORT_SIZE; //inch
+        LittleEndian.putInt(header, pos, 0); pos += LittleEndian.INT_SIZE;  //reserved
+
+        checksum = getChecksum();
+        LittleEndian.putUShort(header, pos, checksum);
+
+        out.write(header);
+    }
+
+    public Dimension getSize() {
+        //coefficient to translate from WMF dpi to 72dpi
+        double coeff = ((double)Units.POINT_DPI)/inch;
+        return new Dimension((int)Math.round((right-left)*coeff), (int)Math.round((bottom-top)*coeff));
+    }
+
+    public Rectangle getBounds() {
+        return new Rectangle(left, top, right-left, bottom-top);
+    }
+    
+    public int getLength(){
+        return 22;
+    }
+}
index 9f705e0e04daba5bfaa4b2d57021f275e30343e5..3405ef3725e48c5f5c489fb017af2097465ef57d 100644 (file)
 package org.apache.poi.xslf.usermodel;
 
 import java.awt.Dimension;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
-import javax.imageio.ImageIO;
-
 import org.apache.poi.POIXMLDocumentPart;
 import org.apache.poi.POIXMLException;
-import org.apache.poi.hslf.blip.EMF;
-import org.apache.poi.hslf.blip.PICT;
-import org.apache.poi.hslf.blip.WMF;
 import org.apache.poi.openxml4j.opc.PackagePart;
 import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.sl.image.ImageHeaderBitmap;
+import org.apache.poi.sl.image.ImageHeaderEMF;
+import org.apache.poi.sl.image.ImageHeaderPICT;
+import org.apache.poi.sl.image.ImageHeaderWMF;
 import org.apache.poi.sl.usermodel.PictureData;
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianConsts;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
 import org.apache.poi.util.Units;
 
 /**
@@ -49,8 +44,6 @@ import org.apache.poi.util.Units;
  */
 @Beta
 public final class XSLFPictureData extends POIXMLDocumentPart implements PictureData {
-    private static final POILogger logger = POILogFactory.getLogger(XSLFPictureData.class);
-    
     private Long checksum = null;
 
     // original image dimensions (for formats supported by BufferedImage)
@@ -171,30 +164,18 @@ public final class XSLFPictureData extends POIXMLDocumentPart implements Picture
             
             switch (pt) {
             case EMF:
-                origSize = new EMF.NativeHeader(data, 0).getSize();
+                origSize = new ImageHeaderEMF(data, 0).getSize();
                 break;
             case WMF:
                 // wmf files in pptx usually have their placeable header 
                 // stripped away, so this returns only the dummy size
-                origSize = new WMF.NativeHeader(data, 0).getSize();
+                origSize = new ImageHeaderWMF(data, 0).getSize();
                 break;
             case PICT:
-                origSize = new PICT.NativeHeader(data, 0).getSize();
+                origSize = new ImageHeaderPICT(data, 0).getSize();
                 break;
             default:
-                BufferedImage img = null;
-                try {
-                    img = ImageIO.read(new ByteArrayInputStream(data));
-                } catch (IOException e) {
-                    logger.log(POILogger.WARN, "Can't determine image dimensions", e);
-                }
-                // set dummy size, in case of dummy dimension can't be set
-                origSize = (img == null)
-                    ? new Dimension(200,200)
-                    : new Dimension(
-                        (int)Units.pixelToPoints(img.getWidth()),
-                        (int)Units.pixelToPoints(img.getHeight())
-                    );
+                origSize = new ImageHeaderBitmap(data, 0).getSize();
                 break;
             }
         }
index 4d7de5f75d213fba65a80471678f3dbc9e03c590..7eb285dfb7ffc4e3544e0b3eb9d26caa4dd0c1ec 100644 (file)
@@ -18,7 +18,6 @@
 package org.apache.poi.hslf.blip;
 
 import java.awt.Dimension;
-import java.awt.Rectangle;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -26,8 +25,7 @@ import java.io.InputStream;
 import java.util.zip.InflaterInputStream;
 
 import org.apache.poi.hslf.exceptions.HSLFException;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LocaleUtil;
+import org.apache.poi.sl.image.ImageHeaderEMF;
 import org.apache.poi.util.Units;
 
 /**
@@ -35,38 +33,6 @@ import org.apache.poi.util.Units;
  */
 public final class EMF extends Metafile {
 
-    public static class NativeHeader {
-        // rectangular inclusive-inclusive bounds, in device units, of the smallest 
-        // rectangle that can be drawn around the image stored in the metafile.
-        private final Rectangle deviceBounds;
-
-        private final static String EMF_SIGNATURE = " EMF"; // 0x464D4520 (LE)
-        
-        public NativeHeader(byte data[], int offset) {
-            int type = (int)LittleEndian.getUInt(data, offset); offset += 4;
-            if (type != 1) {
-                throw new HSLFException("Invalid EMF picture");
-            }
-            // ignore header size
-            offset += 4;
-            int left = LittleEndian.getInt(data, offset); offset += 4;
-            int top = LittleEndian.getInt(data, offset); offset += 4;
-            int right = LittleEndian.getInt(data, offset); offset += 4;
-            int bottom = LittleEndian.getInt(data, offset); offset += 4;
-            deviceBounds = new Rectangle(left, top, right-left, bottom-top);
-            // ignore frame bounds
-            offset += 16;
-            String signature = new String(data, offset, EMF_SIGNATURE.length(), LocaleUtil.CHARSET_1252);
-            if (!EMF_SIGNATURE.equals(signature)) {
-                throw new HSLFException("Invalid EMF picture");
-            }
-        }
-
-        public Dimension getSize() {
-            return deviceBounds.getSize();
-        }
-    }
-    
     @Override
     public byte[] getData(){
         try {
@@ -96,11 +62,11 @@ public final class EMF extends Metafile {
     public void setData(byte[] data) throws IOException {
         byte[] compressed = compress(data, 0, data.length);
 
-        NativeHeader nHeader = new NativeHeader(data, 0);
+        ImageHeaderEMF nHeader = new ImageHeaderEMF(data, 0);
         
         Header header = new Header();
         header.wmfsize = data.length;
-        header.bounds = nHeader.deviceBounds;
+        header.bounds = nHeader.getBounds();
         Dimension nDim = nHeader.getSize();
         header.size = new Dimension(Units.toEMU(nDim.getWidth()), Units.toEMU(nDim.getHeight()));
         header.zipsize = compressed.length;
index d68b4f70165ef48606600b60b72eea17f3d08b0f..c79bade3643fcccc94cd77eee861e1748d382b82 100644 (file)
 package org.apache.poi.hslf.blip;
 
 import java.awt.Dimension;
-import java.awt.Rectangle;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.zip.InflaterInputStream;
 
 import org.apache.poi.hslf.exceptions.HSLFException;
+import org.apache.poi.sl.image.ImageHeaderPICT;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 import org.apache.poi.util.Units;
@@ -35,79 +35,6 @@ import org.apache.poi.util.Units;
 public final class PICT extends Metafile {
     private static final POILogger LOG = POILogFactory.getLogger(PICT.class);
     
-    public static class NativeHeader {
-        /**
-         * skip the first 512 bytes - they are MAC specific crap
-         */
-        public static final int PICT_HEADER_OFFSET = 512;
-        
-        public static final double DEFAULT_RESOLUTION = Units.POINT_DPI;
-        
-        private static final byte V2_HEADER[] = {
-            0x00, 0x11,       // v2 version opcode
-            0x02, (byte)0xFF, // version number of new picture
-            0x0C, 0x00,        // header opcode
-            (byte)0xFF, (byte)0xFE, 0x00, 0x00 // pic size dummy
-        };
-
-        public final Rectangle bounds;
-        public final double hRes, vRes;
-        
-        public NativeHeader(byte data[], int offset) {
-            // http://mirrors.apple2.org.za/apple.cabi.net/Graphics/PICT.and_QT.INFO/PICT.file.format.TI.txt
-
-            // low order 16 bits of picture size - can be ignored
-            offset += 2;
-            // rectangular bounding box of picture, at 72 dpi
-            // rect : 8 bytes (top, left, bottom, right: integer)
-            int y1 = readUnsignedShort(data, offset); offset += 2;
-            int x1 = readUnsignedShort(data, offset); offset += 2;
-            int y2 = readUnsignedShort(data, offset); offset += 2;
-            int x2 = readUnsignedShort(data, offset); offset += 2;
-
-            // check for version 2 ... otherwise we don't read any further
-            boolean isV2 = true;
-            for (byte b : V2_HEADER) {
-                if (b != data[offset++]) {
-                    isV2 = false;
-                    break;
-                }
-            }
-            
-            if (isV2) {
-                // 4 bytes - fixed, horizontal resolution (dpi) of source data
-                hRes = readFixedPoint(data, offset); offset += 4;
-                // 4 bytes - fixed, vertical resolution (dpi) of source data
-                vRes = readFixedPoint(data, offset); offset += 4;
-            } else {
-                hRes = DEFAULT_RESOLUTION;
-                vRes = DEFAULT_RESOLUTION;
-            }
-            
-            bounds = new Rectangle(x1,y1,x2-x1,y2-y1);
-        }
-
-        public Dimension getSize() {
-            int height = (int)Math.round(bounds.height*DEFAULT_RESOLUTION/vRes);
-            int width = (int)Math.round(bounds.width*DEFAULT_RESOLUTION/hRes);
-            return new Dimension(width, height);
-        }
-
-        private static int readUnsignedShort(byte data[], int offset) {
-            int b0 = data[offset] & 0xFF;
-            int b1 = data[offset+1] & 0xFF;
-            return b0 << 8 | b1;
-        }
-
-        private static double readFixedPoint(byte data[], int offset) {
-            int b0 = data[offset] & 0xFF;
-            int b1 = data[offset+1] & 0xFF;
-            int b2 = data[offset+2] & 0xFF;
-            int b3 = data[offset+3] & 0xFF;
-            int i = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
-            return i / (double)0x10000;
-        }
-    }
     
     @Override
     public byte[] getData(){
@@ -163,14 +90,14 @@ public final class PICT extends Metafile {
     @Override
     public void setData(byte[] data) throws IOException {
         // skip the first 512 bytes - they are MAC specific crap
-        final int nOffset = NativeHeader.PICT_HEADER_OFFSET;
-        NativeHeader nHeader = new NativeHeader(data, nOffset);
+        final int nOffset = ImageHeaderPICT.PICT_HEADER_OFFSET;
+        ImageHeaderPICT nHeader = new ImageHeaderPICT(data, nOffset);
         
         Header header = new Header();
         header.wmfsize = data.length - nOffset;
         byte[] compressed = compress(data, nOffset, header.wmfsize);
         header.zipsize = compressed.length;
-        header.bounds = nHeader.bounds;
+        header.bounds = nHeader.getBounds();
         Dimension nDim = nHeader.getSize();
         header.size = new Dimension(Units.toEMU(nDim.getWidth()), Units.toEMU(nDim.getHeight()));
 
index 9dbc7f3bc320f357800201640f9a44e051c05a4d..78aa9f364f473bb848639d611f7bf6ccb13c428c 100644 (file)
 package org.apache.poi.hslf.blip;
 
 import java.awt.Dimension;
-import java.awt.Rectangle;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.util.zip.InflaterInputStream;
 
 import org.apache.poi.hslf.exceptions.HSLFException;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
+import org.apache.poi.sl.image.ImageHeaderWMF;
 import org.apache.poi.util.Units;
 
 /**
@@ -49,7 +45,7 @@ public final class WMF extends Metafile {
             long len = is.skip(header.getSize() + CHECKSUM_SIZE*uidInstanceCount);
             assert(len == header.getSize() + CHECKSUM_SIZE*uidInstanceCount);
 
-            NativeHeader aldus = new NativeHeader(header.bounds);
+            ImageHeaderWMF aldus = new ImageHeaderWMF(header.bounds);
             aldus.write(out);
 
             InflaterInputStream inflater = new InflaterInputStream( is );
@@ -68,14 +64,14 @@ public final class WMF extends Metafile {
     @Override
     public void setData(byte[] data) throws IOException {
         int pos = 0;
-        NativeHeader nHeader = new NativeHeader(data, pos);
+        ImageHeaderWMF nHeader = new ImageHeaderWMF(data, pos);
         pos += nHeader.getLength();
 
         byte[] compressed = compress(data, pos, data.length-pos);
 
         Header header = new Header();
         header.wmfsize = data.length - nHeader.getLength();
-        header.bounds = new Rectangle((short)nHeader.left, (short)nHeader.top, (short)nHeader.right-(short)nHeader.left, (short)nHeader.bottom-(short)nHeader.top);
+        header.bounds = nHeader.getBounds();
         Dimension nDim = nHeader.getSize();
         header.size = new Dimension(Units.toEMU(nDim.getWidth()), Units.toEMU(nDim.getHeight()));
         header.zipsize = compressed.length;
@@ -118,122 +114,4 @@ public final class WMF extends Metafile {
                 throw new IllegalArgumentException(signature+" is not a valid instance/signature value for WMF");
         }
     }
-
-    /**
-     * Aldus Placeable Metafile header - 22 byte structure before WMF data.
-     * <ul>
-     *  <li>int Key;               Magic number (always 9AC6CDD7h)
-     *  <li>short  Handle;         Metafile HANDLE number (always 0)
-     *  <li>short Left;            Left coordinate in metafile units
-     *  <li>short Top;             Top coordinate in metafile units
-     *  <li>short Right;           Right coordinate in metafile units
-     *  <li>short Bottom;          Bottom coordinate in metafile units
-     *  <li>short  Inch;           Number of metafile units per inch
-     *  <li>int Reserved;          Reserved (always 0)
-     *  <li>short  Checksum;       Checksum value for previous 10 shorts
-     * </ul>
-     */
-    @SuppressWarnings("unused")
-    public static class NativeHeader {
-        public static final int APMHEADER_KEY = 0x9AC6CDD7;
-        private static POILogger logger = POILogFactory.getLogger(NativeHeader.class);
-
-        private final int handle;
-        private final int left, top, right, bottom;
-
-        /**
-         * The number of logical units per inch used to represent the image.
-         * This value can be used to scale an image. By convention, an image is
-         * considered to be recorded at 1440 logical units (twips) per inch. 
-         * Thus, a value of 720 specifies that the image SHOULD be rendered at
-         * twice its normal size, and a value of 2880 specifies that the image
-         * SHOULD be rendered at half its normal size.
-         */
-        private final int inch; 
-        private final int reserved;
-        private int checksum;
-        
-        public NativeHeader(Rectangle dim) {
-            handle = 0;
-            left = dim.x;
-            top = dim.y;
-            right = dim.x + dim.width;
-            bottom = dim.y + dim.height;
-            inch = Units.POINT_DPI; //default resolution is 72 dpi
-            reserved = 0;
-        }
-
-        public NativeHeader(byte[] data, int pos) {
-            int key = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE; //header key
-            if (key != APMHEADER_KEY) {
-                logger.log(POILogger.WARN, "WMF file doesn't contain a placeable header - ignore parsing");
-                handle = 0;
-                left = 0;
-                top = 0;
-                right = 200;
-                bottom = 200;
-                inch = Units.POINT_DPI; //default resolution is 72 dpi
-                reserved = 0;
-                return;
-            }
-
-            handle = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;
-            left = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
-            top = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
-            right = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
-            bottom = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
-
-            inch = LittleEndian.getUShort(data, pos); pos += LittleEndian.SHORT_SIZE;
-            reserved = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE;
-
-            checksum = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE;
-            if (checksum != getChecksum()){
-                logger.log(POILogger.WARN, "WMF checksum does not match the header data");
-            }
-        }
-
-        /**
-         * Returns a checksum value for the previous 10 shorts in the header.
-         * The checksum is calculated by XORing each short value to an initial value of 0:
-         */
-        public int getChecksum(){
-            int cs = 0;
-            cs ^=  (APMHEADER_KEY & 0x0000FFFF);
-            cs ^= ((APMHEADER_KEY & 0xFFFF0000) >> 16);
-            cs ^= left;
-            cs ^= top;
-            cs ^= right;
-            cs ^= bottom;
-            cs ^= inch;
-            return cs;
-        }
-
-        public void write(OutputStream out) throws IOException {
-            byte[] header = new byte[22];
-            int pos = 0;
-            LittleEndian.putInt(header, pos, APMHEADER_KEY); pos += LittleEndian.INT_SIZE; //header key
-            LittleEndian.putUShort(header, pos, 0); pos += LittleEndian.SHORT_SIZE; //hmf
-            LittleEndian.putUShort(header, pos, left); pos += LittleEndian.SHORT_SIZE; //left
-            LittleEndian.putUShort(header, pos, top); pos += LittleEndian.SHORT_SIZE; //top
-            LittleEndian.putUShort(header, pos, right); pos += LittleEndian.SHORT_SIZE; //right
-            LittleEndian.putUShort(header, pos, bottom); pos += LittleEndian.SHORT_SIZE; //bottom
-            LittleEndian.putUShort(header, pos, inch); pos += LittleEndian.SHORT_SIZE; //inch
-            LittleEndian.putInt(header, pos, 0); pos += LittleEndian.INT_SIZE;  //reserved
-
-            checksum = getChecksum();
-            LittleEndian.putUShort(header, pos, checksum);
-
-            out.write(header);
-        }
-
-        public Dimension getSize() {
-            //coefficient to translate from WMF dpi to 72dpi
-            double coeff = ((double)Units.POINT_DPI)/inch;
-            return new Dimension((int)Math.round((right-left)*coeff), (int)Math.round((bottom-top)*coeff));
-        }
-        
-        public int getLength(){
-            return 22;
-        }
-    }
 }
index 3afcfe5962cc253736f4d7a70e7bc9e72cb241d4..398603cb78f510b47f91d9b9c68f367f0e9cea8e 100644 (file)
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue;
 import java.awt.Dimension;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.net.URL;
 import java.util.List;
 
@@ -34,6 +35,9 @@ import org.apache.poi.hslf.blip.JPEG;
 import org.apache.poi.hslf.blip.PICT;
 import org.apache.poi.hslf.blip.PNG;
 import org.apache.poi.hslf.blip.WMF;
+import org.apache.poi.sl.image.ImageHeaderEMF;
+import org.apache.poi.sl.image.ImageHeaderPICT;
+import org.apache.poi.sl.image.ImageHeaderWMF;
 import org.apache.poi.sl.usermodel.PictureData.PictureType;
 import org.apache.poi.util.Units;
 import org.junit.Ignore;
@@ -51,13 +55,13 @@ public final class TestPictures {
      * Test read/write Macintosh PICT
      */
     @Test
-    public void testPICT() throws Exception {
+    public void testPICT() throws IOException {
         HSLFSlideShow ppt = new HSLFSlideShow();
 
         HSLFSlide slide = ppt.createSlide();
         byte[] src_bytes = slTests.readFile("cow.pict");
         HSLFPictureData data = ppt.addPicture(src_bytes, PictureType.PICT);
-        PICT.NativeHeader nHeader = new PICT.NativeHeader(src_bytes, 512);
+        ImageHeaderPICT nHeader = new ImageHeaderPICT(src_bytes, 512);
         final int expWidth = 197, expHeight = 137;
         Dimension nDim = nHeader.getSize();
         assertEquals(expWidth, nDim.getWidth(), 0);
@@ -114,13 +118,13 @@ public final class TestPictures {
      * Test read/write WMF
      */
     @Test
-    public void testWMF() throws Exception {
+    public void testWMF() throws IOException {
         HSLFSlideShow ppt = new HSLFSlideShow();
 
         HSLFSlide slide = ppt.createSlide();
         byte[] src_bytes = slTests.readFile("santa.wmf");
         HSLFPictureData data = ppt.addPicture(src_bytes, PictureType.WMF);
-        WMF.NativeHeader nHeader = new WMF.NativeHeader(src_bytes, 0);
+        ImageHeaderWMF nHeader = new ImageHeaderWMF(src_bytes, 0);
         final int expWidth = 136, expHeight = 146;
         Dimension nDim = nHeader.getSize();
         assertEquals(expWidth, nDim.getWidth(), 0);
@@ -176,13 +180,13 @@ public final class TestPictures {
      * Test read/write EMF
      */
     @Test
-    public void testEMF() throws Exception {
+    public void testEMF() throws IOException {
         HSLFSlideShow ppt = new HSLFSlideShow();
 
         HSLFSlide slide = ppt.createSlide();
         byte[] src_bytes = slTests.readFile("wrench.emf");
         HSLFPictureData data = ppt.addPicture(src_bytes, PictureType.EMF);
-        EMF.NativeHeader nHeader = new EMF.NativeHeader(src_bytes, 0);
+        ImageHeaderEMF nHeader = new ImageHeaderEMF(src_bytes, 0);
         final int expWidth = 190, expHeight = 115;
         Dimension nDim = nHeader.getSize();
         assertEquals(expWidth, nDim.getWidth(), 0);
@@ -233,7 +237,7 @@ public final class TestPictures {
      * Test read/write PNG
      */
     @Test
-    public void testPNG() throws Exception {
+    public void testPNG() throws IOException {
         HSLFSlideShow ppt = new HSLFSlideShow();
 
         HSLFSlide slide = ppt.createSlide();
@@ -273,7 +277,7 @@ public final class TestPictures {
      * Test read/write JPEG
      */
     @Test
-    public void testJPEG() throws Exception {
+    public void testJPEG() throws IOException {
         HSLFSlideShow ppt = new HSLFSlideShow();
 
         HSLFSlide slide = ppt.createSlide();
@@ -314,7 +318,7 @@ public final class TestPictures {
      * Test read/write DIB
      */
     @Test
-    public void testDIB() throws Exception {
+    public void testDIB() throws IOException {
         HSLFSlideShow ppt = new HSLFSlideShow();
 
         HSLFSlide slide = ppt.createSlide();
@@ -354,7 +358,7 @@ public final class TestPictures {
      * Read pictures in different formats from a reference slide show
      */
     @Test
-    public void testReadPictures() throws Exception {
+    public void testReadPictures() throws IOException {
 
         byte[] src_bytes, ppt_bytes, b1, b2;
         HSLFPictureShape pict;
@@ -417,6 +421,7 @@ public final class TestPictures {
         ppt_bytes = slTests.readFile("wrench.emf");
         assertArrayEquals(src_bytes, ppt_bytes);
 
+        ppt.close();
     }
 
        /**
@@ -424,7 +429,7 @@ public final class TestPictures {
         *  crazy pictures of type 0, we do our best.
         */
     @Test
-       public void testZeroPictureType() throws Exception {
+       public void testZeroPictureType() throws IOException {
                HSLFSlideShowImpl hslf = new HSLFSlideShowImpl(slTests.openResourceAsStream("PictureTypeZero.ppt"));
 
                // Should still have 2 real pictures
@@ -452,6 +457,8 @@ public final class TestPictures {
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof WMF);
         assertEquals(PictureType.WMF, pdata.getType());
+        
+        ppt.close();
        }
 
     /**
@@ -460,7 +467,7 @@ public final class TestPictures {
      */
     @Test
     @Ignore
-       public void testZeroPictureLength() throws Exception {
+       public void testZeroPictureLength() throws IOException {
         // take the data from www instead of test directory
         URL url = new URL("http://www.cs.sfu.ca/~anoop/courses/CMPT-882-Fall-2002/chris.ppt");
                HSLFSlideShowImpl hslf = new HSLFSlideShowImpl(url.openStream());
@@ -504,19 +511,21 @@ public final class TestPictures {
         assertEquals(streamSize, offset);
         assertEquals(3, ppt.getPictureData().size());
 
+        ppt.close();
     }
 
     @Test
-    public void testGetPictureName() throws Exception {
+    public void testGetPictureName() throws IOException {
         HSLFSlideShow ppt = new HSLFSlideShow(slTests.openResourceAsStream("ppt_with_png.ppt"));
         HSLFSlide slide = ppt.getSlides().get(0);
 
         HSLFPictureShape p = (HSLFPictureShape)slide.getShapes().get(0); //the first slide contains JPEG
         assertEquals("test", p.getPictureName());
+        ppt.close();
     }
 
     @Test
-    public void testSetPictureName() throws Exception {
+    public void testSetPictureName() throws IOException {
         HSLFSlideShow ppt = new HSLFSlideShow();
 
         HSLFSlide slide = ppt.createSlide();