]> source.dussan.org Git - poi.git/commitdiff
Bugzilla 49658 - Support embedding EMF/WMF pictures in HSSF
authorYegor Kozlov <yegor@apache.org>
Sun, 13 Oct 2013 07:20:36 +0000 (07:20 +0000)
committerYegor Kozlov <yegor@apache.org>
Sun, 13 Oct 2013 07:20:36 +0000 (07:20 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1531622 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/ddf/EscherMetafileBlip.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java

index 37f405469436516ed0406f341577fbdeee3abe33..428d11e4e93972534014cf82fb19180d9fe24ab0 100644 (file)
@@ -21,6 +21,7 @@ import org.apache.poi.util.HexDump;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
+import org.apache.poi.hssf.usermodel.HSSFPictureData;
 
 import java.awt.Dimension;
 import java.awt.Rectangle;
@@ -28,6 +29,7 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.zip.InflaterInputStream;
+import java.util.zip.DeflaterOutputStream;
 
 /**
  * @author Daniel Noll
@@ -39,13 +41,6 @@ public final class EscherMetafileBlip extends EscherBlipRecord {
     public static final short RECORD_ID_WMF = (short) 0xF018 + 3;
     public static final short RECORD_ID_PICT = (short) 0xF018 + 4;
 
-    /**
-     * BLIP signatures as defined in the escher spec
-     */
-    public static final short SIGNATURE_EMF  = 0x3D40;
-    public static final short SIGNATURE_WMF  = 0x2160;
-    public static final short SIGNATURE_PICT = 0x5420;
-
     private static final int HEADER_SIZE = 8;
 
     private byte[] field_1_UID;
@@ -288,11 +283,37 @@ public final class EscherMetafileBlip extends EscherBlipRecord {
      */
     public short getSignature() {
         switch (getRecordId()) {
-            case RECORD_ID_EMF:  return SIGNATURE_EMF;
-            case RECORD_ID_WMF:  return SIGNATURE_WMF;
-            case RECORD_ID_PICT: return  SIGNATURE_PICT;
+            case RECORD_ID_EMF:  return HSSFPictureData.MSOBI_EMF;
+            case RECORD_ID_WMF:  return HSSFPictureData.MSOBI_WMF;
+            case RECORD_ID_PICT: return HSSFPictureData.MSOBI_PICT;
         }
         log.log(POILogger.WARN, "Unknown metafile: " + getRecordId());
         return 0;
     }
+
+    public void setPictureData(byte[] pictureData) {
+       super.setPictureData(pictureData);
+        setUncompressedSize(pictureData.length);
+
+        // info of chicago project:
+        // "... LZ compression algorithm in the format used by GNU Zip deflate/inflate with a 32k window ..."
+        // not sure what to do, when lookup tables exceed 32k ...
+
+        try {
+               ByteArrayOutputStream bos = new ByteArrayOutputStream();
+               DeflaterOutputStream dos = new DeflaterOutputStream(bos);
+               dos.write(pictureData);
+               dos.close();
+               raw_pictureData = bos.toByteArray();
+        } catch (IOException e) {
+               throw new RuntimeException("Can't compress metafile picture data", e);
+        }
+        
+        setCompressedSize(raw_pictureData.length);
+        setCompressed(true);
+    }
+
+    public void setFilter(byte filter) {
+       field_7_fFilter = filter;
+    }
 }
index cfbfdc43a268b99e9b6a6a5ec055cf0645c6c54e..9e42beab98b013ff35abccbd9c357333d12031cf 100644 (file)
@@ -34,6 +34,7 @@ import org.apache.poi.POIDocument;
 import org.apache.poi.ddf.EscherBSERecord;
 import org.apache.poi.ddf.EscherBitmapBlip;
 import org.apache.poi.ddf.EscherBlipRecord;
+import org.apache.poi.ddf.EscherMetafileBlip;
 import org.apache.poi.ddf.EscherRecord;
 import org.apache.poi.hssf.OldExcelFormatException;
 import org.apache.poi.hssf.model.DrawingManager2;
@@ -57,6 +58,7 @@ import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.WorkbookUtil;
 import org.apache.poi.util.Configurator;
+import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
 
@@ -1587,7 +1589,40 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
         initDrawings();
 
         byte[] uid = DigestUtils.md5(pictureData);
-        EscherBitmapBlip blipRecord = new EscherBitmapBlip();
+        EscherBlipRecord blipRecord;
+        int blipSize;
+        short escherTag;
+        switch (format) {
+            case PICTURE_TYPE_WMF:
+                // remove first 22 bytes if file starts with magic bytes D7-CD-C6-9A
+                // see also http://de.wikipedia.org/wiki/Windows_Metafile#Hinweise_zur_WMF-Spezifikation
+                if (LittleEndian.getInt(pictureData) == 0x9AC6CDD7) {
+                    byte picDataNoHeader[] = new byte[pictureData.length-22];
+                    System.arraycopy(pictureData, 22, picDataNoHeader, 0, pictureData.length-22);
+                    pictureData = picDataNoHeader;
+                }
+                // fall through
+            case PICTURE_TYPE_EMF:
+                EscherMetafileBlip blipRecordMeta = new EscherMetafileBlip();
+                blipRecord = blipRecordMeta;
+                blipRecordMeta.setUID(uid);
+                blipRecordMeta.setPictureData(pictureData);
+                // taken from libre office export, it won't open, if this is left to 0
+                blipRecordMeta.setFilter((byte)-2);
+                blipSize = blipRecordMeta.getCompressedSize() + 58;
+                escherTag = 0;
+                break;
+            default:
+                EscherBitmapBlip blipRecordBitmap = new EscherBitmapBlip();
+                blipRecord = blipRecordBitmap;
+                blipRecordBitmap.setUID( uid );
+                blipRecordBitmap.setMarker( (byte) 0xFF );
+                blipRecordBitmap.setPictureData( pictureData );
+                blipSize = pictureData.length + 25;
+                escherTag = (short) 0xFF;
+               break;
+        }
+
         blipRecord.setRecordId( (short) ( EscherBitmapBlip.RECORD_ID_START + format ) );
         switch (format)
         {
@@ -1610,23 +1645,19 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
                 blipRecord.setOptions(HSSFPictureData.MSOBI_DIB);
                 break;
         }
-
-        blipRecord.setUID( uid );
-        blipRecord.setMarker( (byte) 0xFF );
-        blipRecord.setPictureData( pictureData );
-
+        
         EscherBSERecord r = new EscherBSERecord();
         r.setRecordId( EscherBSERecord.RECORD_ID );
         r.setOptions( (short) ( 0x0002 | ( format << 4 ) ) );
         r.setBlipTypeMacOS( (byte) format );
         r.setBlipTypeWin32( (byte) format );
         r.setUid( uid );
-        r.setTag( (short) 0xFF );
-        r.setSize( pictureData.length + 25 );
+        r.setTag( escherTag );
+        r.setSize( blipSize );
         r.setRef( 0 );
         r.setOffset( 0 );
         r.setBlipRecord( blipRecord );
-
+        
         return workbook.addBSERecord( r );
     }
 
index dcc611c05ab30233fcf71268be18f233bf59b258..de8ffabf8bc42eff6f3ce25baf1e938643acae48 100644 (file)
 
 package org.apache.poi.hssf.usermodel;
 
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.poi.POIDataSamples;
 import org.apache.poi.ddf.EscherBSERecord;
-import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.HSSFITestDataProvider;
+import org.apache.poi.hssf.HSSFTestDataSamples;
 import org.apache.poi.hssf.model.InternalSheet;
 import org.apache.poi.ss.usermodel.BaseTestPicture;
+import org.apache.poi.ss.usermodel.ClientAnchor;
+import org.apache.poi.ss.usermodel.CreationHelper;
 import org.apache.poi.ss.usermodel.PictureData;
 import org.apache.poi.ss.usermodel.Workbook;
 
-import java.util.Arrays;
-import java.util.List;
-
 /**
  * Test <code>HSSFPicture</code>.
  *
@@ -210,4 +216,56 @@ public final class TestHSSFPicture extends BaseTestPicture {
         p1 = (HSSFPicture) dr.getChildren().get(0);
         assertEquals(p1.getFileName(), "aaa");
     }
+
+    public void test49658() throws IOException {
+       // test if inserted EscherMetafileBlip will be read again
+        HSSFWorkbook wb = new HSSFWorkbook();
+
+        byte pictureDataEmf[] = POIDataSamples.getDocumentInstance().readFile("vector_image.emf");
+        int indexEmf = wb.addPicture(pictureDataEmf, HSSFWorkbook.PICTURE_TYPE_EMF);
+        byte pictureDataPng[] = POIDataSamples.getSpreadSheetInstance().readFile("logoKarmokar4.png");
+        int indexPng = wb.addPicture(pictureDataPng, HSSFWorkbook.PICTURE_TYPE_PNG);
+        byte pictureDataWmf[] = POIDataSamples.getSlideShowInstance().readFile("santa.wmf");
+        int indexWmf = wb.addPicture(pictureDataWmf, HSSFWorkbook.PICTURE_TYPE_WMF);
+
+        HSSFSheet sheet = wb.createSheet();
+        HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
+        CreationHelper ch = wb.getCreationHelper();
+
+        ClientAnchor anchor = ch.createClientAnchor();
+        anchor.setCol1(2);
+        anchor.setCol2(5);
+        anchor.setRow1(1);
+        anchor.setRow2(6);
+        patriarch.createPicture(anchor, indexEmf);
+        
+        anchor = ch.createClientAnchor();
+        anchor.setCol1(2);
+        anchor.setCol2(5);
+        anchor.setRow1(10);
+        anchor.setRow2(16);
+        patriarch.createPicture(anchor, indexPng);
+
+        anchor = ch.createClientAnchor();
+        anchor.setCol1(6);
+        anchor.setCol2(9);
+        anchor.setRow1(1);
+        anchor.setRow2(6);
+        patriarch.createPicture(anchor, indexWmf);
+        
+        
+        wb = HSSFTestDataSamples.writeOutAndReadBack(wb);
+        byte pictureDataOut[] = wb.getAllPictures().get(0).getData();
+        assertTrue(Arrays.equals(pictureDataEmf, pictureDataOut));
+
+        byte wmfNoHeader[] = new byte[pictureDataWmf.length-22];
+        System.arraycopy(pictureDataWmf, 22, wmfNoHeader, 0, pictureDataWmf.length-22);
+        pictureDataOut = wb.getAllPictures().get(2).getData();
+        assertTrue(Arrays.equals(wmfNoHeader, pictureDataOut));
+        
+        FileOutputStream fos = new FileOutputStream("vect.xls");
+        wb.write(fos);
+        fos.close();
+    }
+
 }