mirror of https://github.com/apache/poi.git
Bugzilla 55578 - Support embedding OLE1.0 packages in HSSF
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1531623 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1ffc66625a
commit
f6cb7f14ac
|
@ -30,7 +30,12 @@ import org.apache.poi.util.HexDump;
|
||||||
*/
|
*/
|
||||||
public class ClassID
|
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,
|
* <p>The bytes making out the class ID in correct order,
|
||||||
* i.e. big-endian.</p>
|
* i.e. big-endian.</p>
|
||||||
|
@ -64,6 +69,20 @@ public class ClassID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <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
|
/** <p>The number of bytes occupied by this object in the byte
|
||||||
* stream.</p> */
|
* stream.</p> */
|
||||||
|
|
|
@ -64,7 +64,7 @@ public final class EmbeddedObjectRefSubRecord extends SubRecord {
|
||||||
|
|
||||||
|
|
||||||
// currently for testing only - needs review
|
// 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_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_6_unknown = EMPTY_BYTE_ARRAY;
|
||||||
field_4_ole_classname = null;
|
field_4_ole_classname = null;
|
||||||
|
@ -334,4 +334,16 @@ public final class EmbeddedObjectRefSubRecord extends SubRecord {
|
||||||
sb.append("[/ftPictFmla]");
|
sb.append("[/ftPictFmla]");
|
||||||
return sb.toString();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,6 +59,10 @@ public abstract class SubRecord {
|
||||||
return new LbsDataSubRecord(in, secondUShort, cmoOt);
|
return new LbsDataSubRecord(in, secondUShort, cmoOt);
|
||||||
case FtCblsSubRecord.sid:
|
case FtCblsSubRecord.sid:
|
||||||
return new FtCblsSubRecord(in, secondUShort);
|
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);
|
return new UnknownSubRecord(in, sid, secondUShort);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,27 +17,35 @@
|
||||||
|
|
||||||
package org.apache.poi.hssf.usermodel;
|
package org.apache.poi.hssf.usermodel;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.apache.poi.ddf.*;
|
import org.apache.poi.ddf.*;
|
||||||
import org.apache.poi.hssf.model.DrawingManager2;
|
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.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.NoteRecord;
|
||||||
|
import org.apache.poi.hssf.record.ObjRecord;
|
||||||
import org.apache.poi.hssf.util.CellReference;
|
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.ss.usermodel.Chart;
|
||||||
import org.apache.poi.util.POILogFactory;
|
|
||||||
import org.apache.poi.util.POILogger;
|
|
||||||
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;
|
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The patriarch is the toplevel container for shapes in a sheet. It does
|
* The patriarch is the toplevel container for shapes in a sheet. It does
|
||||||
* little other than act as a container for other shapes and groups.
|
* little other than act as a container for other shapes and groups.
|
||||||
*/
|
*/
|
||||||
public final class HSSFPatriarch implements HSSFShapeContainer, Drawing {
|
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 List<HSSFShape> _shapes = new ArrayList<HSSFShape>();
|
||||||
|
|
||||||
private final EscherSpgrRecord _spgrRecord;
|
private final EscherSpgrRecord _spgrRecord;
|
||||||
|
@ -193,6 +201,87 @@ public final class HSSFPatriarch implements HSSFShapeContainer, Drawing {
|
||||||
return createPicture((HSSFClientAnchor) anchor, pictureIndex);
|
return createPicture((HSSFClientAnchor) anchor, pictureIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Creates a polygon
|
||||||
*
|
*
|
||||||
|
|
|
@ -18,15 +18,19 @@
|
||||||
package org.apache.poi.hssf.usermodel;
|
package org.apache.poi.hssf.usermodel;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
@ -36,6 +40,7 @@ import org.apache.poi.ddf.EscherBitmapBlip;
|
||||||
import org.apache.poi.ddf.EscherBlipRecord;
|
import org.apache.poi.ddf.EscherBlipRecord;
|
||||||
import org.apache.poi.ddf.EscherMetafileBlip;
|
import org.apache.poi.ddf.EscherMetafileBlip;
|
||||||
import org.apache.poi.ddf.EscherRecord;
|
import org.apache.poi.ddf.EscherRecord;
|
||||||
|
import org.apache.poi.hpsf.ClassID;
|
||||||
import org.apache.poi.hssf.OldExcelFormatException;
|
import org.apache.poi.hssf.OldExcelFormatException;
|
||||||
import org.apache.poi.hssf.model.DrawingManager2;
|
import org.apache.poi.hssf.model.DrawingManager2;
|
||||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||||
|
@ -46,7 +51,11 @@ import org.apache.poi.hssf.record.*;
|
||||||
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
|
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
|
||||||
import org.apache.poi.hssf.record.common.UnicodeString;
|
import org.apache.poi.hssf.record.common.UnicodeString;
|
||||||
import org.apache.poi.hssf.util.CellReference;
|
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.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.poifs.filesystem.POIFSFileSystem;
|
||||||
import org.apache.poi.ss.formula.FormulaShifter;
|
import org.apache.poi.ss.formula.FormulaShifter;
|
||||||
import org.apache.poi.ss.formula.FormulaType;
|
import org.apache.poi.ss.formula.FormulaType;
|
||||||
|
@ -57,10 +66,7 @@ import org.apache.poi.ss.formula.udf.UDFFinder;
|
||||||
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
|
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
|
||||||
import org.apache.poi.ss.util.CellRangeAddress;
|
import org.apache.poi.ss.util.CellRangeAddress;
|
||||||
import org.apache.poi.ss.util.WorkbookUtil;
|
import org.apache.poi.ss.util.WorkbookUtil;
|
||||||
import org.apache.poi.util.Configurator;
|
import org.apache.poi.util.*;
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
import org.apache.poi.util.POILogFactory;
|
|
||||||
import org.apache.poi.util.POILogger;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1190,15 +1196,15 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
||||||
|
|
||||||
if (preserveNodes) {
|
if (preserveNodes) {
|
||||||
// Don't write out the old Workbook, we'll be doing our new one
|
// 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,
|
// 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
|
// don't write the old one as we'll use the correct name shortly
|
||||||
for (String wrongName : WORKBOOK_DIR_ENTRY_NAMES) {
|
excepts.addAll(Arrays.asList(WORKBOOK_DIR_ENTRY_NAMES));
|
||||||
excepts.add(wrongName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy over all the other nodes to our new poifs
|
// 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,
|
// YK: preserve StorageClsid, it is important for embedded workbooks,
|
||||||
// see Bugzilla 47920
|
// see Bugzilla 47920
|
||||||
|
@ -1623,7 +1629,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
blipRecord.setRecordId( (short) ( EscherBitmapBlip.RECORD_ID_START + format ) );
|
blipRecord.setRecordId((short) (EscherBitmapBlip.RECORD_ID_START + format));
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case PICTURE_TYPE_EMF:
|
case PICTURE_TYPE_EMF:
|
||||||
|
@ -1713,6 +1719,65 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)?
|
* Is the workbook protected with a password (not encrypted)?
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -41,8 +41,10 @@ public class EntryUtils
|
||||||
DirectoryEntry newTarget = null;
|
DirectoryEntry newTarget = null;
|
||||||
if ( entry.isDirectoryEntry() )
|
if ( entry.isDirectoryEntry() )
|
||||||
{
|
{
|
||||||
|
DirectoryEntry dirEntry = (DirectoryEntry)entry;
|
||||||
newTarget = target.createDirectory( entry.getName() );
|
newTarget = target.createDirectory( entry.getName() );
|
||||||
Iterator<Entry> entries = ( (DirectoryEntry) entry ).getEntries();
|
newTarget.setStorageClsid( dirEntry.getStorageClsid() );
|
||||||
|
Iterator<Entry> entries = dirEntry.getEntries();
|
||||||
|
|
||||||
while ( entries.hasNext() )
|
while ( entries.hasNext() )
|
||||||
{
|
{
|
||||||
|
|
|
@ -14,14 +14,16 @@
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
==================================================================== */
|
==================================================================== */
|
||||||
|
|
||||||
package org.apache.poi.poifs.filesystem;
|
package org.apache.poi.poifs.filesystem;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
import org.apache.poi.util.HexDump;
|
import java.io.OutputStream;
|
||||||
import org.apache.poi.util.LittleEndian;
|
|
||||||
|
import org.apache.poi.util.HexDump;
|
||||||
|
import org.apache.poi.util.LittleEndian;
|
||||||
import org.apache.poi.util.LittleEndianConsts;
|
import org.apache.poi.util.LittleEndianConsts;
|
||||||
import org.apache.poi.util.StringUtil;
|
import org.apache.poi.util.StringUtil;
|
||||||
|
|
||||||
|
@ -29,27 +31,27 @@ import org.apache.poi.util.StringUtil;
|
||||||
* Represents an Ole10Native record which is wrapped around certain binary
|
* Represents an Ole10Native record which is wrapped around certain binary
|
||||||
* files being embedded in OLE2 documents.
|
* files being embedded in OLE2 documents.
|
||||||
*
|
*
|
||||||
* @author Rainer Schwarze
|
* @author Rainer Schwarze
|
||||||
*/
|
*/
|
||||||
public class Ole10Native {
|
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
|
public static final String OLE10_NATIVE = "\u0001Ole10Native";
|
||||||
private short flags1; // 2 bytes, unknown, mostly [02 00]
|
protected static final String ISO1 = "ISO-8859-1";
|
||||||
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
|
// (the fields as they appear in the raw record:)
|
||||||
private short flags2; // 2 bytes, unknown, mostly [00 00]
|
private int totalSize; // 4 bytes, total size of record not including this field
|
||||||
// private byte unknown1Length; // 1 byte, specifying the length of the following byte array (unknown1)
|
private short flags1 = 2; // 2 bytes, unknown, mostly [02 00]
|
||||||
private byte[] unknown1; // see below
|
private String label; // ASCIIZ, stored in this field without the terminating zero
|
||||||
private byte[] unknown2; // 3 bytes, unknown, mostly [00 00 00]
|
private String fileName; // ASCIIZ, stored in this field without the terminating zero
|
||||||
private final String command; // ASCIIZ, stored in this field without the terminating zero
|
private short flags2 = 0; // 2 bytes, unknown, mostly [00 00]
|
||||||
private final int dataSize; // 4 bytes (if space), size of following buffer
|
private short unknown1 = 3; // see below
|
||||||
private final byte[] dataBuffer; // varying size, the actual native data
|
private String command; // ASCIIZ, stored in this field without the terminating zero
|
||||||
private short flags3; // some final flags? or zero terminators?, sometimes not there
|
private byte[] dataBuffer; // varying size, the actual native data
|
||||||
public static final String OLE10_NATIVE = "\u0001Ole10Native";
|
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
|
* 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
|
* to include a stream "{01}Ole10Native" which contains the actual
|
||||||
* data relevant for this class.
|
* data relevant for this class.
|
||||||
*
|
*
|
||||||
* @param poifs POI Filesystem object
|
* @param poifs POI Filesystem object
|
||||||
|
@ -87,12 +89,22 @@ public class Ole10Native {
|
||||||
directory.createDocumentInputStream(nativeEntry).read(data);
|
directory.createDocumentInputStream(nativeEntry).read(data);
|
||||||
|
|
||||||
return new Ole10Native(data, 0, plain);
|
return new Ole10Native(data, 0, plain);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance and fills the fields based on the data in the given buffer.
|
* Creates an instance and fills the fields based on ... the fields
|
||||||
*
|
*/
|
||||||
* @param data The buffer containing the Ole10Native record
|
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
|
||||||
* @param offset The start offset of the record in the buffer
|
* @param offset The start offset of the record in the buffer
|
||||||
* @throws Ole10NativeException on invalid or unexcepted data format
|
* @throws Ole10NativeException on invalid or unexcepted data format
|
||||||
*/
|
*/
|
||||||
|
@ -117,61 +129,57 @@ public class Ole10Native {
|
||||||
totalSize = LittleEndian.getInt(data, ofs);
|
totalSize = LittleEndian.getInt(data, ofs);
|
||||||
ofs += LittleEndianConsts.INT_SIZE;
|
ofs += LittleEndianConsts.INT_SIZE;
|
||||||
|
|
||||||
if (plain) {
|
if (plain) {
|
||||||
dataBuffer = new byte[totalSize-4];
|
dataBuffer = new byte[totalSize-4];
|
||||||
System.arraycopy(data, 4, dataBuffer, 0, dataBuffer.length);
|
System.arraycopy(data, 4, dataBuffer, 0, dataBuffer.length);
|
||||||
dataSize = totalSize - 4;
|
// int dataSize = totalSize - 4;
|
||||||
|
|
||||||
byte[] oleLabel = new byte[8];
|
byte[] oleLabel = new byte[8];
|
||||||
System.arraycopy(dataBuffer, 0, oleLabel, 0, Math.min(dataBuffer.length, 8));
|
System.arraycopy(dataBuffer, 0, oleLabel, 0, Math.min(dataBuffer.length, 8));
|
||||||
label = "ole-"+ HexDump.toHex(oleLabel);
|
label = "ole-"+ HexDump.toHex(oleLabel);
|
||||||
fileName = label;
|
fileName = label;
|
||||||
command = label;
|
command = label;
|
||||||
} else {
|
} else {
|
||||||
flags1 = LittleEndian.getShort(data, ofs);
|
flags1 = LittleEndian.getShort(data, ofs);
|
||||||
ofs += LittleEndianConsts.SHORT_SIZE;
|
ofs += LittleEndianConsts.SHORT_SIZE;
|
||||||
int len = getStringLength(data, ofs);
|
|
||||||
label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
int len = getStringLength(data, ofs);
|
||||||
ofs += len;
|
label = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
||||||
len = getStringLength(data, ofs);
|
ofs += len;
|
||||||
fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
|
||||||
ofs += len;
|
len = getStringLength(data, ofs);
|
||||||
flags2 = LittleEndian.getShort(data, ofs);
|
fileName = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
||||||
ofs += LittleEndianConsts.SHORT_SIZE;
|
ofs += len;
|
||||||
len = LittleEndian.getUByte(data, ofs);
|
|
||||||
unknown1 = new byte[len];
|
flags2 = LittleEndian.getShort(data, ofs);
|
||||||
ofs += len;
|
ofs += LittleEndianConsts.SHORT_SIZE;
|
||||||
len = 3;
|
|
||||||
unknown2 = new byte[len];
|
unknown1 = LittleEndian.getShort(data, ofs);
|
||||||
ofs += len;
|
ofs += LittleEndianConsts.SHORT_SIZE;
|
||||||
len = getStringLength(data, ofs);
|
|
||||||
command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
len = LittleEndian.getInt(data, ofs);
|
||||||
ofs += len;
|
ofs += LittleEndianConsts.INT_SIZE;
|
||||||
|
|
||||||
if (totalSize + LittleEndianConsts.INT_SIZE - ofs > LittleEndianConsts.INT_SIZE) {
|
command = StringUtil.getFromCompressedUnicode(data, ofs, len - 1);
|
||||||
dataSize = LittleEndian.getInt(data, ofs);
|
ofs += len;
|
||||||
ofs += LittleEndianConsts.INT_SIZE;
|
|
||||||
|
if (totalSize < ofs) {
|
||||||
if (dataSize > totalSize || dataSize<0) {
|
throw new Ole10NativeException("Invalid Ole10Native");
|
||||||
throw new Ole10NativeException("Invalid Ole10Native");
|
}
|
||||||
}
|
|
||||||
|
int dataSize = LittleEndian.getInt(data, ofs);
|
||||||
dataBuffer = new byte[dataSize];
|
ofs += LittleEndianConsts.INT_SIZE;
|
||||||
System.arraycopy(data, ofs, dataBuffer, 0, dataSize);
|
|
||||||
ofs += dataSize;
|
if (dataSize < 0 || totalSize - (ofs - LittleEndianConsts.INT_SIZE) < dataSize) {
|
||||||
|
throw new Ole10NativeException("Invalid Ole10Native");
|
||||||
if (unknown1.length > 0) {
|
}
|
||||||
flags3 = LittleEndian.getShort(data, ofs);
|
|
||||||
ofs += LittleEndianConsts.SHORT_SIZE;
|
dataBuffer = new byte[dataSize];
|
||||||
} else {
|
System.arraycopy(data, ofs, dataBuffer, 0, dataSize);
|
||||||
flags3 = 0;
|
ofs += dataSize;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
throw new Ole10NativeException("Invalid Ole10Native");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper - determine length of zero terminated string (ASCIIZ).
|
* Helper - determine length of zero terminated string (ASCIIZ).
|
||||||
*/
|
*/
|
||||||
|
@ -234,26 +242,17 @@ public class Ole10Native {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns unknown1 field - currently unknown.
|
* Returns unknown1 field - currently unknown.
|
||||||
*
|
*
|
||||||
* @return the unknown1
|
* @return the unknown1
|
||||||
*/
|
*/
|
||||||
public byte[] getUnknown1() {
|
public short getUnknown1() {
|
||||||
return unknown1;
|
return unknown1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the unknown2 field - currently being a byte[3] - mostly {0, 0, 0}.
|
* 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.
|
||||||
* @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.
|
|
||||||
*
|
|
||||||
* @return the command
|
* @return the command
|
||||||
*/
|
*/
|
||||||
public String getCommand() {
|
public String getCommand() {
|
||||||
|
@ -265,13 +264,13 @@ public class Ole10Native {
|
||||||
* embedded. To be sure, that no data has been embedded, check whether
|
* embedded. To be sure, that no data has been embedded, check whether
|
||||||
* {@link #getDataBuffer()} returns <code>null</code>.
|
* {@link #getDataBuffer()} returns <code>null</code>.
|
||||||
*
|
*
|
||||||
* @return the dataSize
|
* @return the dataSize
|
||||||
*/
|
*/
|
||||||
public int getDataSize() {
|
public int getDataSize() {
|
||||||
return dataSize;
|
return dataBuffer.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the buffer containing the embedded file's data, or <code>null</code>
|
* Returns the buffer containing the embedded file's data, or <code>null</code>
|
||||||
* if no data was embedded. Note that an embedding may provide information about
|
* if no data was embedded. Note that an embedding may provide information about
|
||||||
* the data, but the actual data is not included. (So label, filename etc. are
|
* the data, but the actual data is not included. (So label, filename etc. are
|
||||||
|
@ -288,7 +287,89 @@ public class Ole10Native {
|
||||||
*
|
*
|
||||||
* @return the flags3
|
* @return the flags3
|
||||||
*/
|
*/
|
||||||
public short getFlags3() {
|
public short getFlags3() {
|
||||||
return flags3;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -262,10 +262,7 @@ public final class TestHSSFPicture extends BaseTestPicture {
|
||||||
System.arraycopy(pictureDataWmf, 22, wmfNoHeader, 0, pictureDataWmf.length-22);
|
System.arraycopy(pictureDataWmf, 22, wmfNoHeader, 0, pictureDataWmf.length-22);
|
||||||
pictureDataOut = wb.getAllPictures().get(2).getData();
|
pictureDataOut = wb.getAllPictures().get(2).getData();
|
||||||
assertTrue(Arrays.equals(wmfNoHeader, pictureDataOut));
|
assertTrue(Arrays.equals(wmfNoHeader, pictureDataOut));
|
||||||
|
|
||||||
FileOutputStream fos = new FileOutputStream("vect.xls");
|
|
||||||
wb.write(fos);
|
|
||||||
fos.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,11 +17,23 @@
|
||||||
|
|
||||||
package org.apache.poi.hssf.usermodel;
|
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 java.util.List;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.apache.poi.POIDataSamples;
|
||||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
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 final class TestOLE2Embeding extends TestCase {
|
||||||
public void testEmbeddedObjects() throws Exception {
|
public void testEmbeddedObjects() throws Exception {
|
||||||
HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook("ole2-embedding.xls");
|
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 number of objects", 2, objects.size());
|
||||||
assertEquals("Wrong name for first object", "MBD06CAB431",
|
assertEquals("Wrong name for first object", "MBD06CAB431",
|
||||||
((HSSFObjectData)
|
((HSSFObjectData)
|
||||||
|
@ -48,5 +60,101 @@ public final class TestOLE2Embeding extends TestCase {
|
||||||
((HSSFObjectData)
|
((HSSFObjectData)
|
||||||
objects.get(1)).getDirectory().getName());
|
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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue