ASF Bugzilla – Attachment 30869 Details for
Bug 55578
[PATCH] Patch for embedding OLE1.0 package
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
Patch for embedding OLE1.0 package
hssf-embed-olepackage.diff (text/plain), 39.40 KB, created by
Andreas Beeker
on 2013-09-21 12:29:29 UTC
(
hide
)
Description:
Patch for embedding OLE1.0 package
Filename:
MIME Type:
Creator:
Andreas Beeker
Created:
2013-09-21 12:29:29 UTC
Size:
39.40 KB
patch
obsolete
>Index: src/java/org/apache/poi/hpsf/ClassID.java >=================================================================== >--- src/java/org/apache/poi/hpsf/ClassID.java (revision 1523427) >+++ src/java/org/apache/poi/hpsf/ClassID.java (working copy) >@@ -30,7 +30,12 @@ > */ > public class ClassID > { >- >+ public static final ClassID OLE10_PACKAGE = new ClassID("{0003000C-0000-0000-C000-000000000046}"); >+ public static final ClassID PPT_SHOW = new ClassID("{64818D10-4F9B-11CF-86EA-00AA00B929E8}"); >+ public static final ClassID XLS_WORKBOOK = new ClassID("{00020841-0000-0000-C000-000000000046}"); >+ public static final ClassID TXT_ONLY = new ClassID("{5e941d80-bf96-11cd-b579-08002b30bfeb}"); // ??? >+ >+ > /** > * <p>The bytes making out the class ID in correct order, > * i.e. big-endian.</p> >@@ -64,6 +69,20 @@ > } > > >+ /** >+ * <p>Creates a {@link ClassID} from a human-readable representation of the Class ID in standard >+ * format <code>"{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}"</code>.</p> >+ * >+ * @param externalForm representation of the Class ID represented by this object. >+ */ >+ public ClassID(String externalForm) { >+ bytes = new byte[LENGTH]; >+ String clsStr = externalForm.replaceAll("[{}-]", ""); >+ for (int i=0; i<clsStr.length(); i+=2) { >+ bytes[i/2] = (byte)Integer.parseInt(clsStr.substring(i, i+2), 16); >+ } >+ } >+ > > /** <p>The number of bytes occupied by this object in the byte > * stream.</p> */ >Index: src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java >=================================================================== >--- src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java (revision 1523427) >+++ src/java/org/apache/poi/hssf/record/EmbeddedObjectRefSubRecord.java (working copy) >@@ -64,7 +64,7 @@ > > > // currently for testing only - needs review >- EmbeddedObjectRefSubRecord() { >+ public EmbeddedObjectRefSubRecord() { > field_2_unknownFormulaData = new byte[] { 0x02, 0x6C, 0x6A, 0x16, 0x01, }; // just some sample data. These values vary a lot > field_6_unknown = EMPTY_BYTE_ARRAY; > field_4_ole_classname = null; >@@ -334,4 +334,16 @@ > sb.append("[/ftPictFmla]"); > return sb.toString(); > } >+ >+ public void setUnknownFormulaData(byte[] formularData) { >+ field_2_unknownFormulaData = formularData; >+ } >+ >+ public void setOleClassname(String oleClassname) { >+ field_4_ole_classname = oleClassname; >+ } >+ >+ public void setStorageId(int storageId) { >+ field_5_stream_id = storageId; >+ } > } >Index: src/java/org/apache/poi/hssf/record/SubRecord.java >=================================================================== >--- src/java/org/apache/poi/hssf/record/SubRecord.java (revision 1523427) >+++ src/java/org/apache/poi/hssf/record/SubRecord.java (working copy) >@@ -59,6 +59,10 @@ > return new LbsDataSubRecord(in, secondUShort, cmoOt); > case FtCblsSubRecord.sid: > return new FtCblsSubRecord(in, secondUShort); >+ case FtPioGrbitSubRecord.sid: >+ return new FtPioGrbitSubRecord(in, secondUShort); >+ case FtCfSubRecord.sid: >+ return new FtCfSubRecord(in, secondUShort); > } > return new UnknownSubRecord(in, sid, secondUShort); > } >Index: src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java >=================================================================== >--- src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java (revision 1523427) >+++ src/java/org/apache/poi/hssf/usermodel/HSSFPatriarch.java (working copy) >@@ -17,20 +17,28 @@ > > package org.apache.poi.hssf.usermodel; > >+import java.io.FileNotFoundException; > import java.util.*; > > import org.apache.poi.ddf.*; > import org.apache.poi.hssf.model.DrawingManager2; >+import org.apache.poi.hssf.record.CommonObjectDataSubRecord; >+import org.apache.poi.hssf.record.EmbeddedObjectRefSubRecord; >+import org.apache.poi.hssf.record.EndSubRecord; > import org.apache.poi.hssf.record.EscherAggregate; >+import org.apache.poi.hssf.record.FtCfSubRecord; >+import org.apache.poi.hssf.record.FtPioGrbitSubRecord; > import org.apache.poi.hssf.record.NoteRecord; >+import org.apache.poi.hssf.record.ObjRecord; > import org.apache.poi.hssf.util.CellReference; >+import org.apache.poi.poifs.filesystem.DirectoryEntry; >+import org.apache.poi.poifs.filesystem.DirectoryNode; > import org.apache.poi.ss.usermodel.Chart; >-import org.apache.poi.util.POILogFactory; >-import org.apache.poi.util.POILogger; >+import org.apache.poi.ss.usermodel.ClientAnchor; >+import org.apache.poi.ss.usermodel.Drawing; >+import org.apache.poi.util.HexDump; >+import org.apache.poi.util.Internal; > import org.apache.poi.util.StringUtil; >-import org.apache.poi.util.Internal; >-import org.apache.poi.ss.usermodel.Drawing; >-import org.apache.poi.ss.usermodel.ClientAnchor; > > /** > * The patriarch is the toplevel container for shapes in a sheet. It does >@@ -37,7 +45,7 @@ > * little other than act as a container for other shapes and groups. > */ > public final class HSSFPatriarch implements HSSFShapeContainer, Drawing { >- private static POILogger log = POILogFactory.getLogger(HSSFPatriarch.class); >+ // private static POILogger log = POILogFactory.getLogger(HSSFPatriarch.class); > private final List<HSSFShape> _shapes = new ArrayList<HSSFShape>(); > > private final EscherSpgrRecord _spgrRecord; >@@ -194,6 +202,87 @@ > } > > /** >+ * Adds a new OLE Package Shape >+ * >+ * @param anchor the client anchor describes how this picture is >+ * attached to the sheet. >+ * @param storageId the storageId returned by {@Link HSSFWorkbook.addOlePackage} >+ * @param pictureIndex the index of the picture (used as preview image) in the >+ * workbook collection of pictures. >+ * >+ * @return newly created shape >+ */ >+ public HSSFObjectData createObjectData(HSSFClientAnchor anchor, int storageId, int pictureIndex) { >+ ObjRecord obj = new ObjRecord(); >+ >+ CommonObjectDataSubRecord ftCmo = new CommonObjectDataSubRecord(); >+ ftCmo.setObjectType(CommonObjectDataSubRecord.OBJECT_TYPE_PICTURE); >+ // ftCmo.setObjectId(oleShape.getShapeId()); ... will be set by onCreate(...) >+ ftCmo.setLocked(true); >+ ftCmo.setPrintable(true); >+ ftCmo.setAutofill(true); >+ ftCmo.setAutoline(true); >+ ftCmo.setReserved1(0); >+ ftCmo.setReserved2(0); >+ ftCmo.setReserved3(0); >+ obj.addSubRecord(ftCmo); >+ >+ // FtCf (pictFormat) >+ FtCfSubRecord ftCf = new FtCfSubRecord(); >+ HSSFPictureData pictData = getSheet().getWorkbook().getAllPictures().get(pictureIndex-1); >+ switch (pictData.getFormat()) { >+ case HSSFWorkbook.PICTURE_TYPE_WMF: >+ case HSSFWorkbook.PICTURE_TYPE_EMF: >+ // this needs patch #49658 to be applied to actually work >+ ftCf.setFlags(FtCfSubRecord.METAFILE_BIT); >+ break; >+ case HSSFWorkbook.PICTURE_TYPE_DIB: >+ case HSSFWorkbook.PICTURE_TYPE_PNG: >+ case HSSFWorkbook.PICTURE_TYPE_JPEG: >+ case HSSFWorkbook.PICTURE_TYPE_PICT: >+ ftCf.setFlags(FtCfSubRecord.BITMAP_BIT); >+ break; >+ } >+ obj.addSubRecord(ftCf); >+ // FtPioGrbit (pictFlags) >+ FtPioGrbitSubRecord ftPioGrbit = new FtPioGrbitSubRecord(); >+ ftPioGrbit.setFlagByBit(FtPioGrbitSubRecord.AUTO_PICT_BIT, true); >+ obj.addSubRecord(ftPioGrbit); >+ >+ EmbeddedObjectRefSubRecord ftPictFmla = new EmbeddedObjectRefSubRecord(); >+ ftPictFmla.setUnknownFormulaData(new byte[]{2, 0, 0, 0, 0}); >+ ftPictFmla.setOleClassname("Paket"); >+ ftPictFmla.setStorageId(storageId); >+ >+ obj.addSubRecord(ftPictFmla); >+ obj.addSubRecord(new EndSubRecord()); >+ >+ String entryName = "MBD"+HexDump.toHex(storageId); >+ DirectoryEntry oleRoot; >+ try { >+ DirectoryNode dn = _sheet.getWorkbook().getRootDirectory(); >+ if (dn == null) throw new FileNotFoundException(); >+ oleRoot = (DirectoryEntry)dn.getEntry(entryName); >+ } catch (FileNotFoundException e) { >+ throw new IllegalStateException("trying to add ole shape without actually adding data first - use HSSFWorkbook.addOlePackage first", e); >+ } >+ >+ // create picture shape, which need to be minimal modified for oleshapes >+ HSSFPicture shape = new HSSFPicture(null, anchor); >+ shape.setPictureIndex(pictureIndex); >+ EscherContainerRecord spContainer = shape.getEscherContainer(); >+ EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID); >+ spRecord.setFlags(spRecord.getFlags() | EscherSpRecord.FLAG_OLESHAPE); >+ >+ HSSFObjectData oleShape = new HSSFObjectData(spContainer, obj, oleRoot); >+ addShape(oleShape); >+ onCreate(oleShape); >+ >+ >+ return oleShape; >+ } >+ >+ /** > * Creates a polygon > * > * @param anchor the client anchor describes how this group is attached >Index: src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java >=================================================================== >--- src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java (revision 1523427) >+++ src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java (working copy) >@@ -18,6 +18,7 @@ > package org.apache.poi.hssf.usermodel; > > import java.io.ByteArrayInputStream; >+import java.io.ByteArrayOutputStream; > import java.io.FileNotFoundException; > import java.io.IOException; > import java.io.InputStream; >@@ -24,9 +25,12 @@ > import java.io.OutputStream; > import java.io.PrintWriter; > import java.util.ArrayList; >+import java.util.Arrays; >+import java.util.HashMap; > import java.util.Hashtable; > import java.util.Iterator; > import java.util.List; >+import java.util.Map; > import java.util.regex.Pattern; > > import org.apache.commons.codec.digest.DigestUtils; >@@ -35,6 +39,7 @@ > import org.apache.poi.ddf.EscherBitmapBlip; > import org.apache.poi.ddf.EscherBlipRecord; > import org.apache.poi.ddf.EscherRecord; >+import org.apache.poi.hpsf.ClassID; > import org.apache.poi.hssf.OldExcelFormatException; > import org.apache.poi.hssf.model.DrawingManager2; > import org.apache.poi.hssf.model.HSSFFormulaParser; >@@ -45,7 +50,11 @@ > import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor; > import org.apache.poi.hssf.record.common.UnicodeString; > import org.apache.poi.hssf.util.CellReference; >+import org.apache.poi.poifs.filesystem.DirectoryEntry; > import org.apache.poi.poifs.filesystem.DirectoryNode; >+import org.apache.poi.poifs.filesystem.EntryUtils; >+import org.apache.poi.poifs.filesystem.FilteringDirectoryNode; >+import org.apache.poi.poifs.filesystem.Ole10Native; > import org.apache.poi.poifs.filesystem.POIFSFileSystem; > import org.apache.poi.ss.formula.FormulaShifter; > import org.apache.poi.ss.formula.FormulaType; >@@ -57,6 +66,7 @@ > 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.HexDump; > import org.apache.poi.util.POILogFactory; > import org.apache.poi.util.POILogger; > >@@ -1188,15 +1198,15 @@ > > if (preserveNodes) { > // Don't write out the old Workbook, we'll be doing our new one >- excepts.add("Workbook"); > // If the file had an "incorrect" name for the workbook stream, > // don't write the old one as we'll use the correct name shortly >- for (String wrongName : WORKBOOK_DIR_ENTRY_NAMES) { >- excepts.add(wrongName); >- } >+ excepts.addAll(Arrays.asList(WORKBOOK_DIR_ENTRY_NAMES)); > > // Copy over all the other nodes to our new poifs >- copyNodes(this.directory, fs.getRoot(), excepts); >+ EntryUtils.copyNodes( >+ new FilteringDirectoryNode(this.directory, excepts) >+ , new FilteringDirectoryNode(fs.getRoot(), excepts) >+ ); > > // YK: preserve StorageClsid, it is important for embedded workbooks, > // see Bugzilla 47920 >@@ -1682,6 +1692,65 @@ > > } > >+ protected static Map<String,ClassID> getOleMap() { >+ Map<String,ClassID> olemap = new HashMap<String,ClassID>(); >+ olemap.put("PowerPoint Document", ClassID.PPT_SHOW); >+ for (String str : WORKBOOK_DIR_ENTRY_NAMES) { >+ olemap.put(str, ClassID.XLS_WORKBOOK); >+ } >+ // ... to be continued >+ return olemap; >+ } >+ >+ public int addOlePackage(POIFSFileSystem poiData, String label, String fileName, String command) >+ throws IOException { >+ DirectoryNode root = poiData.getRoot(); >+ Map<String,ClassID> olemap = getOleMap(); >+ for (Map.Entry<String,ClassID> entry : olemap.entrySet()) { >+ if (root.hasEntry(entry.getKey())) { >+ root.setStorageClsid(entry.getValue()); >+ break; >+ } >+ } >+ >+ ByteArrayOutputStream bos = new ByteArrayOutputStream(); >+ poiData.writeFilesystem(bos); >+ return addOlePackage(bos.toByteArray(), label, fileName, command); >+ } >+ >+ public int addOlePackage(byte[] oleData, String label, String fileName, String command) >+ throws IOException { >+ // check if we were created by POIFS otherwise create a new dummy POIFS for storing the package data >+ if (directory == null) { >+ directory = new POIFSFileSystem().getRoot(); >+ preserveNodes = true; >+ } >+ >+ // get free MBD-Node >+ int storageId = 0; >+ DirectoryEntry oleDir = null; >+ do { >+ String storageStr = "MBD"+HexDump.toHex(++storageId); >+ if (!directory.hasEntry(storageStr)) { >+ oleDir = directory.createDirectory(storageStr); >+ oleDir.setStorageClsid(ClassID.OLE10_PACKAGE); >+ } >+ } while (oleDir == null); >+ >+ // the following data was taken from an example libre office document >+ // beside this "\u0001Ole" record there were several other records, e.g. CompObj, >+ // OlePresXXX, but it seems, that they aren't neccessary >+ byte oleBytes[] = { 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; >+ oleDir.createDocument("\u0001Ole", new ByteArrayInputStream(oleBytes)); >+ >+ Ole10Native oleNative = new Ole10Native(label, fileName, command, oleData); >+ ByteArrayOutputStream bos = new ByteArrayOutputStream(); >+ oleNative.writeOut(bos); >+ oleDir.createDocument(Ole10Native.OLE10_NATIVE, new ByteArrayInputStream(bos.toByteArray())); >+ >+ return storageId; >+ } >+ > /** > * Is the workbook protected with a password (not encrypted)? > */ >Index: src/java/org/apache/poi/poifs/filesystem/EntryUtils.java >=================================================================== >--- src/java/org/apache/poi/poifs/filesystem/EntryUtils.java (revision 1523427) >+++ src/java/org/apache/poi/poifs/filesystem/EntryUtils.java (working copy) >@@ -41,8 +41,10 @@ > DirectoryEntry newTarget = null; > if ( entry.isDirectoryEntry() ) > { >+ DirectoryEntry dirEntry = (DirectoryEntry)entry; > newTarget = target.createDirectory( entry.getName() ); >- Iterator<Entry> entries = ( (DirectoryEntry) entry ).getEntries(); >+ newTarget.setStorageClsid( dirEntry.getStorageClsid() ); >+ Iterator<Entry> entries = dirEntry.getEntries(); > > while ( entries.hasNext() ) > { >Index: src/java/org/apache/poi/poifs/filesystem/Ole10Native.java >=================================================================== >--- src/java/org/apache/poi/poifs/filesystem/Ole10Native.java (revision 1523427) >+++ src/java/org/apache/poi/poifs/filesystem/Ole10Native.java (working copy) >@@ -17,8 +17,10 @@ > > package org.apache.poi.poifs.filesystem; > >+import java.io.ByteArrayOutputStream; > import java.io.FileNotFoundException; > import java.io.IOException; >+import java.io.OutputStream; > > import org.apache.poi.util.HexDump; > import org.apache.poi.util.LittleEndian; >@@ -32,21 +34,21 @@ > * @author Rainer Schwarze > */ > public class Ole10Native { >- // (the fields as they appear in the raw record:) >- private final int totalSize; // 4 bytes, total size of record not including this field >- private short flags1; // 2 bytes, unknown, mostly [02 00] >- private final String label; // ASCIIZ, stored in this field without the terminating zero >- private final String fileName; // ASCIIZ, stored in this field without the terminating zero >- private short flags2; // 2 bytes, unknown, mostly [00 00] >- // private byte unknown1Length; // 1 byte, specifying the length of the following byte array (unknown1) >- private byte[] unknown1; // see below >- private byte[] unknown2; // 3 bytes, unknown, mostly [00 00 00] >- private final String command; // ASCIIZ, stored in this field without the terminating zero >- private final int dataSize; // 4 bytes (if space), size of following buffer >- private final byte[] dataBuffer; // varying size, the actual native data >- private short flags3; // some final flags? or zero terminators?, sometimes not there >+ > public static final String OLE10_NATIVE = "\u0001Ole10Native"; >+ protected static final String ISO1 = "ISO-8859-1"; > >+ // (the fields as they appear in the raw record:) >+ private int totalSize; // 4 bytes, total size of record not including this field >+ private short flags1 = 2; // 2 bytes, unknown, mostly [02 00] >+ private String label; // ASCIIZ, stored in this field without the terminating zero >+ private String fileName; // ASCIIZ, stored in this field without the terminating zero >+ private short flags2 = 0; // 2 bytes, unknown, mostly [00 00] >+ private short unknown1 = 3; // see below >+ private String command; // ASCIIZ, stored in this field without the terminating zero >+ private byte[] dataBuffer; // varying size, the actual native data >+ private short flags3 = 0; // some final flags? or zero terminators?, sometimes not there >+ > /** > * Creates an instance of this class from an embedded OLE Object. The OLE Object is expected > * to include a stream "{01}Ole10Native" which contains the actual >@@ -90,6 +92,16 @@ > } > > /** >+ * Creates an instance and fills the fields based on ... the fields >+ */ >+ public Ole10Native(String label, String filename, String command, byte[] data) { >+ setLabel(label); >+ setFileName(filename); >+ setCommand(command); >+ setDataBuffer(data); >+ } >+ >+ /** > * Creates an instance and fills the fields based on the data in the given buffer. > * > * @param data The buffer containing the Ole10Native record >@@ -120,7 +132,7 @@ > if (plain) { > dataBuffer = new byte[totalSize-4]; > System.arraycopy(data, 4, dataBuffer, 0, dataBuffer.length); >- dataSize = totalSize - 4; >+ // int dataSize = totalSize - 4; > > byte[] oleLabel = new byte[8]; > System.arraycopy(dataBuffer, 0, oleLabel, 0, Math.min(dataBuffer.length, 8)); >@@ -130,45 +142,41 @@ > } else { > flags1 = LittleEndian.getShort(data, ofs); > ofs += LittleEndianConsts.SHORT_SIZE; >+ > int len = getStringLength(data, ofs); > label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1); > ofs += len; >+ > len = getStringLength(data, ofs); > fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1); > ofs += len; >+ > flags2 = LittleEndian.getShort(data, ofs); > ofs += LittleEndianConsts.SHORT_SIZE; >- len = LittleEndian.getUByte(data, ofs); >- unknown1 = new byte[len]; >- ofs += len; >- len = 3; >- unknown2 = new byte[len]; >- ofs += len; >- len = getStringLength(data, ofs); >+ >+ unknown1 = LittleEndian.getShort(data, ofs); >+ ofs += LittleEndianConsts.SHORT_SIZE; >+ >+ len = LittleEndian.getInt(data, ofs); >+ ofs += LittleEndianConsts.INT_SIZE; >+ > command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1); > ofs += len; >+ >+ if (totalSize < ofs) { >+ throw new Ole10NativeException("Invalid Ole10Native"); >+ } > >- if (totalSize + LittleEndianConsts.INT_SIZE - ofs > LittleEndianConsts.INT_SIZE) { >- dataSize = LittleEndian.getInt(data, ofs); >- ofs += LittleEndianConsts.INT_SIZE; >+ int dataSize = LittleEndian.getInt(data, ofs); >+ ofs += LittleEndianConsts.INT_SIZE; > >- if (dataSize > totalSize || dataSize<0) { >+ if (dataSize < 0 || totalSize - (ofs - LittleEndianConsts.INT_SIZE) < dataSize) { > throw new Ole10NativeException("Invalid Ole10Native"); >- } >- >- dataBuffer = new byte[dataSize]; >- System.arraycopy(data, ofs, dataBuffer, 0, dataSize); >- ofs += dataSize; >- >- if (unknown1.length > 0) { >- flags3 = LittleEndian.getShort(data, ofs); >- ofs += LittleEndianConsts.SHORT_SIZE; >- } else { >- flags3 = 0; >- } >- } else { >- throw new Ole10NativeException("Invalid Ole10Native"); > } >+ >+ dataBuffer = new byte[dataSize]; >+ System.arraycopy(data, ofs, dataBuffer, 0, dataSize); >+ ofs += dataSize; > } > } > >@@ -237,20 +245,11 @@ > * > * @return the unknown1 > */ >- public byte[] getUnknown1() { >+ public short getUnknown1() { > return unknown1; > } > > /** >- * Returns the unknown2 field - currently being a byte[3] - mostly {0, 0, 0}. >- * >- * @return the unknown2 >- */ >- public byte[] getUnknown2() { >- return unknown2; >- } >- >- /** > * Returns the command field - usually the name of the file being embedded > * including the full path, may be a command specified during embedding the file. > * >@@ -268,7 +267,7 @@ > * @return the dataSize > */ > public int getDataSize() { >- return dataSize; >+ return dataBuffer.length; > } > > /** >@@ -291,4 +290,86 @@ > public short getFlags3() { > return flags3; > } >+ >+ /** >+ * Have the contents printer out into an OutputStream, used when writing a >+ * file back out to disk (Normally, atom classes will keep their bytes >+ * around, but non atom classes will just request the bytes from their >+ * children, then chuck on their header and return) >+ */ >+ public void writeOut(OutputStream out) throws IOException { >+ byte intbuf[] = new byte[LittleEndianConsts.INT_SIZE]; >+ byte shortbuf[] = new byte[LittleEndianConsts.SHORT_SIZE]; >+ >+ ByteArrayOutputStream bos = new ByteArrayOutputStream(); >+ bos.write(intbuf); // total size, will be determined later .. >+ >+ LittleEndian.putShort(shortbuf, 0, getFlags1()); >+ bos.write(shortbuf); >+ >+ bos.write(getLabel().getBytes(ISO1)); >+ bos.write(0); >+ >+ bos.write(getFileName().getBytes(ISO1)); >+ bos.write(0); >+ >+ LittleEndian.putShort(shortbuf, 0, getFlags2()); >+ bos.write(shortbuf); >+ >+ LittleEndian.putShort(shortbuf, 0, getUnknown1()); >+ bos.write(shortbuf); >+ >+ LittleEndian.putInt(intbuf, 0, getCommand().length()+1); >+ bos.write(intbuf); >+ >+ bos.write(getCommand().getBytes(ISO1)); >+ bos.write(0); >+ >+ LittleEndian.putInt(intbuf, 0, getDataBuffer().length); >+ bos.write(intbuf); >+ >+ bos.write(getDataBuffer()); >+ >+ LittleEndian.putShort(shortbuf, 0, getFlags3()); >+ bos.write(shortbuf); >+ >+ // update total size - length of length-field (4 bytes) >+ byte data[] = bos.toByteArray(); >+ totalSize = data.length - LittleEndianConsts.INT_SIZE; >+ LittleEndian.putInt(data, 0, totalSize); >+ >+ out.write(data); >+ } >+ >+ public void setFlags1(short flags1) { >+ this.flags1 = flags1; >+ } >+ >+ public void setFlags2(short flags2) { >+ this.flags2 = flags2; >+ } >+ >+ public void setFlags3(short flags3) { >+ this.flags3 = flags3; >+ } >+ >+ public void setLabel(String label) { >+ this.label = label; >+ } >+ >+ public void setFileName(String fileName) { >+ this.fileName = fileName; >+ } >+ >+ public void setCommand(String command) { >+ this.command = command; >+ } >+ >+ public void setUnknown1(short unknown1) { >+ this.unknown1 = unknown1; >+ } >+ >+ public void setDataBuffer(byte dataBuffer[]) { >+ this.dataBuffer = dataBuffer; >+ } > } >Index: src/testcases/org/apache/poi/hssf/usermodel/TestOLE2Embeding.java >=================================================================== >--- src/testcases/org/apache/poi/hssf/usermodel/TestOLE2Embeding.java (revision 1523427) >+++ src/testcases/org/apache/poi/hssf/usermodel/TestOLE2Embeding.java (working copy) >@@ -17,11 +17,23 @@ > > package org.apache.poi.hssf.usermodel; > >+import java.io.ByteArrayInputStream; >+import java.io.ByteArrayOutputStream; >+import java.io.FileOutputStream; >+import java.io.IOException; >+import java.io.InputStream; >+import java.util.Arrays; > import java.util.List; > > import junit.framework.TestCase; > >+import org.apache.poi.POIDataSamples; > import org.apache.poi.hssf.HSSFTestDataSamples; >+import org.apache.poi.poifs.filesystem.DirectoryNode; >+import org.apache.poi.poifs.filesystem.Ole10Native; >+import org.apache.poi.poifs.filesystem.POIFSFileSystem; >+import org.apache.poi.ss.usermodel.ClientAnchor; >+import org.apache.poi.ss.usermodel.CreationHelper; > > /** > * >@@ -39,7 +51,7 @@ > public void testEmbeddedObjects() throws Exception { > HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook("ole2-embedding.xls"); > >- List objects = workbook.getAllEmbeddedObjects(); >+ List<HSSFObjectData> objects = workbook.getAllEmbeddedObjects(); > assertEquals("Wrong number of objects", 2, objects.size()); > assertEquals("Wrong name for first object", "MBD06CAB431", > ((HSSFObjectData) >@@ -48,5 +60,101 @@ > ((HSSFObjectData) > objects.get(1)).getDirectory().getName()); > } >-} >+ >+ public void testReallyEmbedSomething() throws Exception { >+ HSSFWorkbook wb = new HSSFWorkbook(); >+ HSSFSheet sheet = wb.createSheet(); >+ HSSFPatriarch patriarch = sheet.createDrawingPatriarch(); > >+ byte[] pictureData = HSSFTestDataSamples.getTestDataFileContent("logoKarmokar4.png"); >+ byte[] picturePPT = POIDataSamples.getSlideShowInstance().readFile("clock.jpg"); >+ int imgIdx = wb.addPicture(pictureData, HSSFWorkbook.PICTURE_TYPE_PNG); >+ POIFSFileSystem pptPoifs = getSamplePPT(); >+ int pptIdx = wb.addOlePackage(pptPoifs, "Sample-PPT", "sample.ppt", "sample.ppt"); >+ POIFSFileSystem xlsPoifs = getSampleXLS(); >+ int imgPPT = wb.addPicture(picturePPT, HSSFWorkbook.PICTURE_TYPE_JPEG); >+ int xlsIdx = wb.addOlePackage(xlsPoifs, "Sample-XLS", "sample.xls", "sample.xls"); >+ int txtIdx = wb.addOlePackage(getSampleTXT(), "Sample-TXT", "sample.txt", "sample.txt"); >+ >+ int rowoffset = 5; >+ int coloffset = 5; >+ >+ CreationHelper ch = wb.getCreationHelper(); >+ HSSFClientAnchor anchor = (HSSFClientAnchor)ch.createClientAnchor(); >+ anchor.setAnchor((short)(2+coloffset), 1+rowoffset, 0, 0, (short)(3+coloffset), 5+rowoffset, 0, 0); >+ anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE); >+ >+ patriarch.createObjectData(anchor, pptIdx, imgPPT); >+ >+ anchor = (HSSFClientAnchor)ch.createClientAnchor(); >+ anchor.setAnchor((short)(5+coloffset), 1+rowoffset, 0, 0, (short)(6+coloffset), 5+rowoffset, 0, 0); >+ anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE); >+ >+ patriarch.createObjectData(anchor, xlsIdx, imgIdx); >+ >+ anchor = (HSSFClientAnchor)ch.createClientAnchor(); >+ anchor.setAnchor((short)(3+coloffset), 10+rowoffset, 0, 0, (short)(5+coloffset), 11+rowoffset, 0, 0); >+ anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE); >+ >+ patriarch.createObjectData(anchor, txtIdx, imgIdx); >+ >+ anchor = (HSSFClientAnchor)ch.createClientAnchor(); >+ anchor.setAnchor((short)(1+coloffset), -2+rowoffset, 0, 0, (short)(7+coloffset), 14+rowoffset, 0, 0); >+ anchor.setAnchorType(ClientAnchor.DONT_MOVE_AND_RESIZE); >+ >+ HSSFSimpleShape circle = patriarch.createSimpleShape(anchor); >+ circle.setShapeType(HSSFSimpleShape.OBJECT_TYPE_OVAL); >+ circle.setNoFill(true); >+ >+ if (false) { >+ FileOutputStream fos = new FileOutputStream("embed.xls"); >+ wb.write(fos); >+ fos.close(); >+ } >+ >+ wb = HSSFTestDataSamples.writeOutAndReadBack(wb); >+ >+ ByteArrayOutputStream bos = new ByteArrayOutputStream(); >+ HSSFObjectData od = wb.getAllEmbeddedObjects().get(0); >+ Ole10Native ole10 = Ole10Native.createFromEmbeddedOleObject((DirectoryNode)od.getDirectory()); >+ bos.reset(); >+ pptPoifs.writeFilesystem(bos); >+ assertTrue(Arrays.equals(ole10.getDataBuffer(), bos.toByteArray())); >+ >+ od = wb.getAllEmbeddedObjects().get(1); >+ ole10 = Ole10Native.createFromEmbeddedOleObject((DirectoryNode)od.getDirectory()); >+ bos.reset(); >+ xlsPoifs.writeFilesystem(bos); >+ assertTrue(Arrays.equals(ole10.getDataBuffer(), bos.toByteArray())); >+ >+ od = wb.getAllEmbeddedObjects().get(2); >+ ole10 = Ole10Native.createFromEmbeddedOleObject((DirectoryNode)od.getDirectory()); >+ assertTrue(Arrays.equals(ole10.getDataBuffer(), getSampleTXT())); >+ >+ } >+ >+ static POIFSFileSystem getSamplePPT() throws IOException { >+ // scratchpad classes are not available, so we use something pre-cooked >+ InputStream is = POIDataSamples.getSlideShowInstance().openResourceAsStream("with_textbox.ppt"); >+ POIFSFileSystem poifs = new POIFSFileSystem(is); >+ is.close(); >+ >+ return poifs; >+ } >+ >+ static POIFSFileSystem getSampleXLS() throws IOException { >+ HSSFWorkbook wb = new HSSFWorkbook(); >+ HSSFSheet sheet = wb.createSheet(); >+ sheet.createRow(5).createCell(2).setCellValue("yo dawg i herd you like embeddet objekts, so we put a ole in your ole so you can save a file while you save a file"); >+ >+ ByteArrayOutputStream bos = new ByteArrayOutputStream(); >+ wb.write(bos); >+ POIFSFileSystem poifs = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray())); >+ >+ return poifs; >+ } >+ >+ static byte[] getSampleTXT() { >+ return "All your base are belong to us".getBytes(); >+ } >+} >\ No newline at end of file >Index: src/java/org/apache/poi/hssf/record/FtCfSubRecord.java >=================================================================== >--- src/java/org/apache/poi/hssf/record/FtCfSubRecord.java (revision 0) >+++ src/java/org/apache/poi/hssf/record/FtCfSubRecord.java (working copy) >@@ -0,0 +1,113 @@ >+/* ==================================================================== >+ 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.hssf.record; >+ >+import org.apache.poi.util.HexDump; >+import org.apache.poi.util.LittleEndianInput; >+import org.apache.poi.util.LittleEndianOutput; >+ >+ >+/** >+ * The FtCf structure specifies the clipboard format of the picture-type Obj record containing this FtCf. >+ */ >+public final class FtCfSubRecord extends SubRecord { >+ public final static short sid = 0x07; >+ public final static short length = 0x02; >+ >+ /** >+ * Specifies the format of the picture is an enhanced metafile. >+ */ >+ public static short METAFILE_BIT = (short)0x0002; >+ >+ /** >+ * Specifies the format of the picture is a bitmap. >+ */ >+ public static short BITMAP_BIT = (short)0x0009; >+ >+ /** >+ * Specifies the picture is in an unspecified format that is >+ * neither and enhanced metafile nor a bitmap. >+ */ >+ public static short UNSPECIFIED_BIT = (short)0xFFFF; >+ >+ private short flags = 0; >+ >+ /** >+ * Construct a new <code>FtPioGrbitSubRecord</code> and >+ * fill its data with the default values >+ */ >+ public FtCfSubRecord() { >+ } >+ >+ public FtCfSubRecord(LittleEndianInput in, int size) { >+ if (size != length) { >+ throw new RecordFormatException("Unexpected size (" + size + ")"); >+ } >+ flags = in.readShort(); >+ } >+ >+ /** >+ * Convert this record to string. >+ * Used by BiffViewer and other utilities. >+ */ >+ public String toString() { >+ StringBuffer buffer = new StringBuffer(); >+ buffer.append("[FtCf ]\n"); >+ buffer.append(" size = ").append(length).append("\n"); >+ buffer.append(" flags = ").append(HexDump.toHex(flags)).append("\n"); >+ buffer.append("[/FtCf ]\n"); >+ return buffer.toString(); >+ } >+ >+ /** >+ * Serialize the record data into the supplied array of bytes >+ * >+ * @param out the stream to serialize into >+ */ >+ public void serialize(LittleEndianOutput out) { >+ out.writeShort(sid); >+ out.writeShort(length); >+ out.writeShort(flags); >+ } >+ >+ protected int getDataSize() { >+ return length; >+ } >+ >+ /** >+ * @return id of this record. >+ */ >+ public short getSid() >+ { >+ return sid; >+ } >+ >+ public Object clone() { >+ FtCfSubRecord rec = new FtCfSubRecord(); >+ rec.flags = this.flags; >+ return rec; >+ } >+ >+ public short getFlags() { >+ return flags; >+ } >+ >+ public void setFlags(short flags) { >+ this.flags = flags; >+ } >+} >\ No newline at end of file >Index: src/java/org/apache/poi/hssf/record/FtPioGrbitSubRecord.java >=================================================================== >--- src/java/org/apache/poi/hssf/record/FtPioGrbitSubRecord.java (revision 0) >+++ src/java/org/apache/poi/hssf/record/FtPioGrbitSubRecord.java (working copy) >@@ -0,0 +1,167 @@ >+/* ==================================================================== >+ 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.hssf.record; >+ >+import org.apache.poi.util.HexDump; >+import org.apache.poi.util.LittleEndianInput; >+import org.apache.poi.util.LittleEndianOutput; >+ >+ >+/** >+ * This structure appears as part of an Obj record that represents image display properties. >+ */ >+public final class FtPioGrbitSubRecord extends SubRecord { >+ public final static short sid = 0x08; >+ public final static short length = 0x02; >+ >+ /** >+ * A bit that specifies whether the picture's aspect ratio is preserved when rendered in >+ * different views (Normal view, Page Break Preview view, Page Layout view and printing). >+ */ >+ public static int AUTO_PICT_BIT = 1 << 0; >+ >+ /** >+ * A bit that specifies whether the pictFmla field of the Obj record that contains >+ * this FtPioGrbit specifies a DDE reference. >+ */ >+ public static int DDE_BIT = 1 << 1; >+ >+ /** >+ * A bit that specifies whether this object is expected to be updated on print to >+ * reflect the values in the cell associated with the object. >+ */ >+ public static int PRINT_CALC_BIT = 1 << 2; >+ >+ /** >+ * A bit that specifies whether the picture is displayed as an icon. >+ */ >+ public static int ICON_BIT = 1 << 3; >+ >+ /** >+ * A bit that specifies whether this object is an ActiveX control. >+ * It MUST NOT be the case that both fCtl and fDde are equal to 1. >+ */ >+ public static int CTL_BIT = 1 << 4; >+ >+ /** >+ * A bit that specifies whether the object data are stored in an >+ * embedding storage (= 0) or in the controls stream (ctls) (= 1). >+ */ >+ public static int PRSTM_BIT = 1 << 5; >+ >+ /** >+ * A bit that specifies whether this is a camera picture. >+ */ >+ public static int CAMERA_BIT = 1 << 7; >+ >+ /** >+ * A bit that specifies whether this picture's size has been explicitly set. >+ * 0 = picture size has been explicitly set, 1 = has not been set >+ */ >+ public static int DEFAULT_SIZE_BIT = 1 << 8; >+ >+ /** >+ * A bit that specifies whether the OLE server for the object is called >+ * to load the object's data automatically when the parent workbook is opened. >+ */ >+ public static int AUTO_LOAD_BIT = 1 << 9; >+ >+ >+ private short flags = 0; >+ >+ /** >+ * Construct a new <code>FtPioGrbitSubRecord</code> and >+ * fill its data with the default values >+ */ >+ public FtPioGrbitSubRecord() { >+ } >+ >+ public FtPioGrbitSubRecord(LittleEndianInput in, int size) { >+ if (size != length) { >+ throw new RecordFormatException("Unexpected size (" + size + ")"); >+ } >+ flags = in.readShort(); >+ } >+ >+ /** >+ * Use one of the bitmasks MANUAL_ADVANCE_BIT ... CURSOR_VISIBLE_BIT >+ * @param bitmask >+ * @param enabled >+ */ >+ public void setFlagByBit(int bitmask, boolean enabled) { >+ if (enabled) { >+ flags |= bitmask; >+ } else { >+ flags &= (0xFFFF ^ bitmask); >+ } >+ } >+ >+ public boolean getFlagByBit(int bitmask) { >+ return ((flags & bitmask) != 0); >+ } >+ >+ /** >+ * Convert this record to string. >+ * Used by BiffViewer and other utilities. >+ */ >+ public String toString() { >+ StringBuffer buffer = new StringBuffer(); >+ buffer.append("[FtPioGrbit ]\n"); >+ buffer.append(" size = ").append(length).append("\n"); >+ buffer.append(" flags = ").append(HexDump.toHex(flags)).append("\n"); >+ buffer.append("[/FtPioGrbit ]\n"); >+ return buffer.toString(); >+ } >+ >+ /** >+ * Serialize the record data into the supplied array of bytes >+ * >+ * @param out the stream to serialize into >+ */ >+ public void serialize(LittleEndianOutput out) { >+ out.writeShort(sid); >+ out.writeShort(length); >+ out.writeShort(flags); >+ } >+ >+ protected int getDataSize() { >+ return length; >+ } >+ >+ /** >+ * @return id of this record. >+ */ >+ public short getSid() >+ { >+ return sid; >+ } >+ >+ public Object clone() { >+ FtPioGrbitSubRecord rec = new FtPioGrbitSubRecord(); >+ rec.flags = this.flags; >+ return rec; >+ } >+ >+ public short getFlags() { >+ return flags; >+ } >+ >+ public void setFlags(short flags) { >+ this.flags = flags; >+ } >+} >\ No newline at end of file
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 55578
: 30869