correctly process PICT blips (see bug #44886)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@652288 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2008-04-30 06:19:38 +00:00
parent 25a1ddde16
commit c5ce487ede
4 changed files with 222 additions and 17 deletions

View File

@ -41,9 +41,20 @@ public class EscherMetafileBlip
public static final short RECORD_ID_WMF = (short) 0xF018 + 3; public static final short RECORD_ID_WMF = (short) 0xF018 + 3;
public static final short RECORD_ID_PICT = (short) 0xF018 + 4; 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 static final int HEADER_SIZE = 8;
private byte[] field_1_UID; private byte[] field_1_UID;
/**
* The primary UID is only saved to disk if (blip_instance ^ blip_signature == 1)
*/
private byte[] field_2_UID;
private int field_2_cb; private int field_2_cb;
private int field_3_rcBounds_x1; private int field_3_rcBounds_x1;
private int field_3_rcBounds_y1; private int field_3_rcBounds_y1;
@ -72,6 +83,12 @@ public class EscherMetafileBlip
field_1_UID = new byte[16]; field_1_UID = new byte[16];
System.arraycopy( data, pos, field_1_UID, 0, 16 ); pos += 16; System.arraycopy( data, pos, field_1_UID, 0, 16 ); pos += 16;
if((getOptions() ^ getSignature()) == 0x10){
field_2_UID = new byte[16];
System.arraycopy( data, pos, field_2_UID, 0, 16 ); pos += 16;
}
field_2_cb = LittleEndian.getInt( data, pos ); pos += 4; field_2_cb = LittleEndian.getInt( data, pos ); pos += 4;
field_3_rcBounds_x1 = LittleEndian.getInt( data, pos ); pos += 4; field_3_rcBounds_x1 = LittleEndian.getInt( data, pos ); pos += 4;
field_3_rcBounds_y1 = LittleEndian.getInt( data, pos ); pos += 4; field_3_rcBounds_y1 = LittleEndian.getInt( data, pos ); pos += 4;
@ -83,11 +100,8 @@ public class EscherMetafileBlip
field_6_fCompression = data[pos]; pos++; field_6_fCompression = data[pos]; pos++;
field_7_fFilter = data[pos]; pos++; field_7_fFilter = data[pos]; pos++;
// Bit of a snag - trusting field_5_cbSave results in inconsistent raw_pictureData = new byte[field_5_cbSave];
// record size in some cases. So, just check the data left System.arraycopy( data, pos, raw_pictureData, 0, field_5_cbSave );
int remainingBytes = bytesAfterHeader - 50;
raw_pictureData = new byte[remainingBytes];
System.arraycopy( data, pos, raw_pictureData, 0, remainingBytes );
// 0 means DEFLATE compression // 0 means DEFLATE compression
// 0xFE means no compression // 0xFE means no compression
@ -121,9 +135,12 @@ public class EscherMetafileBlip
int pos = offset; int pos = offset;
LittleEndian.putShort( data, pos, getOptions() ); pos += 2; LittleEndian.putShort( data, pos, getOptions() ); pos += 2;
LittleEndian.putShort( data, pos, getRecordId() ); pos += 2; LittleEndian.putShort( data, pos, getRecordId() ); pos += 2;
LittleEndian.putInt( data, getRecordSize() - HEADER_SIZE ); pos += 4; LittleEndian.putInt( data, pos, getRecordSize() - HEADER_SIZE ); pos += 4;
System.arraycopy( field_1_UID, 0, data, pos, 16 ); pos += 16; System.arraycopy( field_1_UID, 0, data, pos, field_1_UID.length ); pos += field_1_UID.length;
if((getOptions() ^ getSignature()) == 0x10){
System.arraycopy( field_2_UID, 0, data, pos, field_2_UID.length ); pos += field_2_UID.length;
}
LittleEndian.putInt( data, pos, field_2_cb ); pos += 4; LittleEndian.putInt( data, pos, field_2_cb ); pos += 4;
LittleEndian.putInt( data, pos, field_3_rcBounds_x1 ); pos += 4; LittleEndian.putInt( data, pos, field_3_rcBounds_x1 ); pos += 4;
LittleEndian.putInt( data, pos, field_3_rcBounds_y1 ); pos += 4; LittleEndian.putInt( data, pos, field_3_rcBounds_y1 ); pos += 4;
@ -138,7 +155,7 @@ public class EscherMetafileBlip
System.arraycopy( raw_pictureData, 0, data, pos, raw_pictureData.length ); System.arraycopy( raw_pictureData, 0, data, pos, raw_pictureData.length );
listener.afterRecordSerialize(offset + getRecordSize(), getRecordId(), getRecordSize(), this); listener.afterRecordSerialize(offset + getRecordSize(), getRecordId(), getRecordSize(), this);
return HEADER_SIZE + 16 + 1 + raw_pictureData.length; return getRecordSize();
} }
/** /**
@ -164,7 +181,7 @@ public class EscherMetafileBlip
} }
catch ( IOException e ) catch ( IOException e )
{ {
log.log(POILogger.INFO, "Possibly corrupt compression or non-compressed data", e); log.log(POILogger.WARN, "Possibly corrupt compression or non-compressed data", e);
return data; return data;
} }
} }
@ -176,7 +193,11 @@ public class EscherMetafileBlip
*/ */
public int getRecordSize() public int getRecordSize()
{ {
return 8 + 50 + raw_pictureData.length; int size = 8 + 50 + raw_pictureData.length;
if((getOptions() ^ getSignature()) == 0x10){
size += field_2_UID.length;
}
return size;
} }
public byte[] getUID() public byte[] getUID()
@ -189,6 +210,16 @@ public class EscherMetafileBlip
this.field_1_UID = field_1_UID; this.field_1_UID = field_1_UID;
} }
public byte[] getPrimaryUID()
{
return field_2_UID;
}
public void setPrimaryUID( byte[] field_2_UID )
{
this.field_2_UID = field_2_UID;
}
public int getUncompressedSize() public int getUncompressedSize()
{ {
return field_2_cb; return field_2_cb;
@ -267,6 +298,7 @@ public class EscherMetafileBlip
" RecordId: 0x" + HexDump.toHex( getRecordId() ) + nl + " RecordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
" Options: 0x" + HexDump.toHex( getOptions() ) + nl + " Options: 0x" + HexDump.toHex( getOptions() ) + nl +
" UID: 0x" + HexDump.toHex( field_1_UID ) + nl + " UID: 0x" + HexDump.toHex( field_1_UID ) + nl +
(field_2_UID == null ? "" : (" UID2: 0x" + HexDump.toHex( field_2_UID ) + nl)) +
" Uncompressed Size: " + HexDump.toHex( field_2_cb ) + nl + " Uncompressed Size: " + HexDump.toHex( field_2_cb ) + nl +
" Bounds: " + getBounds() + nl + " Bounds: " + getBounds() + nl +
" Size in EMU: " + getSizeEMU() + nl + " Size in EMU: " + getSizeEMU() + nl +
@ -276,4 +308,19 @@ public class EscherMetafileBlip
" Extra Data:" + nl + extraData; " Extra Data:" + nl + extraData;
} }
/**
* Return the blip signature
*
* @return the blip signature
*/
public short getSignature(){
short sig = 0;
switch(getRecordId()){
case RECORD_ID_EMF: sig = SIGNATURE_EMF; break;
case RECORD_ID_WMF: sig = SIGNATURE_WMF; break;
case RECORD_ID_PICT: sig = SIGNATURE_PICT; break;
default: log.log(POILogger.WARN, "Unknown metafile: " + getRecordId()); break;
}
return sig;
}
} }

View File

@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel;
import org.apache.poi.ddf.EscherBitmapBlip; import org.apache.poi.ddf.EscherBitmapBlip;
import org.apache.poi.ddf.EscherBlipRecord; import org.apache.poi.ddf.EscherBlipRecord;
import org.apache.poi.ddf.EscherMetafileBlip;
/** /**
* Represents binary data stored in the file. Eg. A GIF, JPEG etc... * Represents binary data stored in the file. Eg. A GIF, JPEG etc...
@ -69,19 +70,19 @@ public class HSSFPictureData
*/ */
public String suggestFileExtension() public String suggestFileExtension()
{ {
switch (blip.getOptions() & FORMAT_MASK) switch (blip.getRecordId())
{ {
case MSOBI_WMF: case EscherMetafileBlip.RECORD_ID_WMF:
return "wmf"; return "wmf";
case MSOBI_EMF: case EscherMetafileBlip.RECORD_ID_EMF:
return "emf"; return "emf";
case MSOBI_PICT: case EscherMetafileBlip.RECORD_ID_PICT:
return "pict"; return "pict";
case MSOBI_PNG: case EscherBitmapBlip.RECORD_ID_PNG:
return "png"; return "png";
case MSOBI_JPEG: case EscherBitmapBlip.RECORD_ID_JPEG:
return "jpeg"; return "jpeg";
case MSOBI_DIB: case EscherBitmapBlip.RECORD_ID_DIB:
return "dib"; return "dib";
default: default:
return ""; return "";

View File

@ -42,6 +42,7 @@ public final class AllPOIDDFTests {
result.addTestSuite(TestEscherSplitMenuColorsRecord.class); result.addTestSuite(TestEscherSplitMenuColorsRecord.class);
result.addTestSuite(TestEscherSpRecord.class); result.addTestSuite(TestEscherSpRecord.class);
result.addTestSuite(TestUnknownEscherRecord.class); result.addTestSuite(TestUnknownEscherRecord.class);
result.addTestSuite(TestEscherBlipRecord.class);
return result; return result;
} }
} }

View File

@ -0,0 +1,156 @@
/* ====================================================================
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.ddf;
import junit.framework.TestCase;
import org.apache.poi.util.HexRead;
import org.apache.poi.util.HexDump;
import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.util.Iterator;
import java.util.Arrays;
/**
* Test read/serialize of escher blip records
*
* @author Yegor Kozlov
*/
public class TestEscherBlipRecord extends TestCase
{
protected String cwd = System.getProperty("DDF.testdata.path");
//test reading/serializing of a PNG blip
public void testReadPNG() throws IOException {
//provided in bug-44886
byte[] data = read(new File(cwd, "Container.dat"));
EscherContainerRecord record = new EscherContainerRecord();
record.fillFields(data, 0, new DefaultEscherRecordFactory());
EscherContainerRecord bstore = (EscherContainerRecord)record.getChildRecords().get(1);
EscherBSERecord bse1 = (EscherBSERecord)bstore.getChildRecords().get(0);
assertEquals(EscherBSERecord.BT_PNG, bse1.getBlipTypeWin32());
assertEquals(EscherBSERecord.BT_PNG, bse1.getBlipTypeMacOS());
assertTrue(Arrays.equals(new byte[]{
0x65, 0x07, 0x4A, (byte)0x8D, 0x3E, 0x42, (byte)0x8B, (byte)0xAC,
0x1D, (byte)0x89, 0x35, 0x4F, 0x48, (byte)0xFA, 0x37, (byte)0xC2
}, bse1.getUid()));
assertEquals(255, bse1.getTag());
assertEquals(32308, bse1.getSize());
EscherBitmapBlip blip1 = (EscherBitmapBlip)bse1.getBlipRecord();
assertEquals(0x6E00, blip1.getOptions());
assertEquals(EscherBitmapBlip.RECORD_ID_PNG, blip1.getRecordId());
assertTrue(Arrays.equals(new byte[]{
0x65, 0x07, 0x4A, (byte)0x8D, 0x3E, 0x42, (byte)0x8B, (byte)0xAC,
0x1D, (byte)0x89, 0x35, 0x4F, 0x48, (byte)0xFA, 0x37, (byte)0xC2
}, blip1.getUID()));
//serialize and read again
byte[] ser = bse1.serialize();
EscherBSERecord bse2 = new EscherBSERecord();
bse2.fillFields(ser, 0, new DefaultEscherRecordFactory());
assertEquals(bse1.getRecordId(), bse2.getRecordId());
assertEquals(bse1.getBlipTypeWin32(), bse2.getBlipTypeWin32());
assertEquals(bse1.getBlipTypeMacOS(), bse2.getBlipTypeMacOS());
assertTrue(Arrays.equals(bse1.getUid(), bse2.getUid()));
assertEquals(bse1.getTag(), bse2.getTag());
assertEquals(bse1.getSize(), bse2.getSize());
EscherBitmapBlip blip2 = (EscherBitmapBlip)bse1.getBlipRecord();
assertEquals(blip1.getOptions(), blip2.getOptions());
assertEquals(blip1.getRecordId(), blip2.getRecordId());
assertEquals(blip1.getUID(), blip2.getUID());
assertTrue(Arrays.equals(blip1.getPicturedata(), blip1.getPicturedata()));
}
//test reading/serializing of a PICT metafile
public void testReadPICT() throws IOException {
//provided in bug-44886
byte[] data = read(new File(cwd, "Container.dat"));
EscherContainerRecord record = new EscherContainerRecord();
record.fillFields(data, 0, new DefaultEscherRecordFactory());
EscherContainerRecord bstore = (EscherContainerRecord)record.getChildRecords().get(1);
EscherBSERecord bse1 = (EscherBSERecord)bstore.getChildRecords().get(1);
//System.out.println(bse1);
assertEquals(EscherBSERecord.BT_WMF, bse1.getBlipTypeWin32());
assertEquals(EscherBSERecord.BT_PICT, bse1.getBlipTypeMacOS());
assertTrue(Arrays.equals(new byte[]{
(byte)0xC7, 0x15, 0x69, 0x2D, (byte)0xE5, (byte)0x89, (byte)0xA3, 0x6F,
0x66, 0x03, (byte)0xD6, 0x24, (byte)0xF7, (byte)0xDB, 0x1D, 0x13
}, bse1.getUid()));
assertEquals(255, bse1.getTag());
assertEquals(1133, bse1.getSize());
EscherMetafileBlip blip1 = (EscherMetafileBlip)bse1.getBlipRecord();
assertEquals(0x5430, blip1.getOptions());
assertEquals(EscherMetafileBlip.RECORD_ID_PICT, blip1.getRecordId());
assertTrue(Arrays.equals(new byte[]{
0x57, 0x32, 0x7B, (byte)0x91, 0x23, 0x5D, (byte)0xDB, 0x36,
0x7A, (byte)0xDB, (byte)0xFF, 0x17, (byte)0xFE, (byte)0xF3, (byte)0xA7, 0x05
}, blip1.getUID()));
assertTrue(Arrays.equals(new byte[]{
(byte)0xC7, 0x15, 0x69, 0x2D, (byte)0xE5, (byte)0x89, (byte)0xA3, 0x6F,
0x66, 0x03, (byte)0xD6, 0x24, (byte)0xF7, (byte)0xDB, 0x1D, 0x13
}, blip1.getPrimaryUID()));
//serialize and read again
byte[] ser = bse1.serialize();
EscherBSERecord bse2 = new EscherBSERecord();
bse2.fillFields(ser, 0, new DefaultEscherRecordFactory());
assertEquals(bse1.getRecordId(), bse2.getRecordId());
assertEquals(bse1.getOptions(), bse2.getOptions());
assertEquals(bse1.getBlipTypeWin32(), bse2.getBlipTypeWin32());
assertEquals(bse1.getBlipTypeMacOS(), bse2.getBlipTypeMacOS());
assertTrue(Arrays.equals(bse1.getUid(), bse2.getUid()));
assertEquals(bse1.getTag(), bse2.getTag());
assertEquals(bse1.getSize(), bse2.getSize());
EscherMetafileBlip blip2 = (EscherMetafileBlip)bse1.getBlipRecord();
assertEquals(blip1.getOptions(), blip2.getOptions());
assertEquals(blip1.getRecordId(), blip2.getRecordId());
assertEquals(blip1.getUID(), blip2.getUID());
assertEquals(blip1.getPrimaryUID(), blip2.getPrimaryUID());
assertTrue(Arrays.equals(blip1.getPicturedata(), blip1.getPicturedata()));
}
//integral test: check that the read-write-read round trip is consistent
public void testContainer() throws IOException {
byte[] data = read(new File(cwd, "Container.dat"));
EscherContainerRecord record = new EscherContainerRecord();
record.fillFields(data, 0, new DefaultEscherRecordFactory());
byte[] ser = record.serialize();
assertTrue(Arrays.equals(data, ser));
}
private byte[] read(File file) throws IOException {
byte[] data = new byte[(int)file.length()];
FileInputStream is = new FileInputStream(file);
is.read(data);
is.close();
return data;
}
}