From 489b0b5bdbccbf76ce69c48a15b769b2d6e00afc Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Sun, 13 Oct 2013 07:20:36 +0000 Subject: [PATCH] Bugzilla 49658 - Support embedding EMF/WMF pictures in HSSF git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1531622 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/poi/ddf/EscherMetafileBlip.java | 41 +++++++--- .../poi/hssf/usermodel/HSSFWorkbook.java | 49 +++++++++--- .../poi/hssf/usermodel/TestHSSFPicture.java | 74 +++++++++++++++++-- 3 files changed, 137 insertions(+), 27 deletions(-) diff --git a/src/java/org/apache/poi/ddf/EscherMetafileBlip.java b/src/java/org/apache/poi/ddf/EscherMetafileBlip.java index 37f405469..428d11e4e 100644 --- a/src/java/org/apache/poi/ddf/EscherMetafileBlip.java +++ b/src/java/org/apache/poi/ddf/EscherMetafileBlip.java @@ -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; + } } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index cfbfdc43a..9e42beab9 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -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 ); } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java index dcc611c05..de8ffabf8 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPicture.java @@ -17,17 +17,23 @@ package org.apache.poi.hssf.usermodel; -import org.apache.poi.ddf.EscherBSERecord; -import org.apache.poi.hssf.HSSFTestDataSamples; -import org.apache.poi.hssf.HSSFITestDataProvider; -import org.apache.poi.hssf.model.InternalSheet; -import org.apache.poi.ss.usermodel.BaseTestPicture; -import org.apache.poi.ss.usermodel.PictureData; -import org.apache.poi.ss.usermodel.Workbook; - +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.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; + /** * Test HSSFPicture. * @@ -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(); + } + }