From 980e8fe473fbb23bf56f7111fd7822fba31de090 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Wed, 2 May 2012 14:00:46 +0000 Subject: [PATCH] Bugzilla 53058 - Utility for representing drawings contained in a binary Excel file as a XML tree git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1333050 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/status.xml | 1 + .../poi/ddf/AbstractEscherOptRecord.java | 20 +- .../apache/poi/ddf/EscherArrayProperty.java | 12 ++ .../org/apache/poi/ddf/EscherBSERecord.java | 19 ++ .../org/apache/poi/ddf/EscherBitmapBlip.java | 22 ++ .../org/apache/poi/ddf/EscherBlipRecord.java | 10 + .../apache/poi/ddf/EscherBlipWMFRecord.java | 31 +++ .../apache/poi/ddf/EscherBoolProperty.java | 9 + .../poi/ddf/EscherChildAnchorRecord.java | 12 ++ .../poi/ddf/EscherClientAnchorRecord.java | 32 +++ .../poi/ddf/EscherClientDataRecord.java | 24 +++ .../apache/poi/ddf/EscherComplexProperty.java | 10 + .../apache/poi/ddf/EscherContainerRecord.java | 17 ++ .../org/apache/poi/ddf/EscherDgRecord.java | 9 + .../org/apache/poi/ddf/EscherDggRecord.java | 12 ++ .../apache/poi/ddf/EscherMetafileBlip.java | 31 ++- .../org/apache/poi/ddf/EscherOptRecord.java | 12 ++ .../org/apache/poi/ddf/EscherPictBlip.java | 25 ++- .../org/apache/poi/ddf/EscherProperty.java | 7 + .../org/apache/poi/ddf/EscherRGBProperty.java | 9 + src/java/org/apache/poi/ddf/EscherRecord.java | 29 ++- .../apache/poi/ddf/EscherSimpleProperty.java | 8 + .../org/apache/poi/ddf/EscherSpRecord.java | 11 + .../org/apache/poi/ddf/EscherSpgrRecord.java | 12 ++ .../poi/ddf/EscherSplitMenuColorsRecord.java | 12 ++ .../apache/poi/ddf/EscherTextboxRecord.java | 22 ++ .../apache/poi/ddf/UnknownEscherRecord.java | 19 ++ .../apache/poi/hssf/dev/BiffDrawingToXml.java | 192 ++++++++++++++++++ .../poi/hssf/record/EscherAggregate.java | 12 ++ 29 files changed, 623 insertions(+), 18 deletions(-) create mode 100644 src/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index 7b085461f6..1eae1dcdce 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 53058 - Utility for representing drawings contained in a binary Excel file as a XML tree 53165 - HWPF support for fetching the description (alt text) of a picture 48528 - support negative arguments to the DATE() function 53092 - allow specifying of a TimeZone to DateUtil.getJavaDate(), for when it is known that a file comes from a different (known) timezone to the current machine diff --git a/src/java/org/apache/poi/ddf/AbstractEscherOptRecord.java b/src/java/org/apache/poi/ddf/AbstractEscherOptRecord.java index 5ff11dea0c..9606fd7603 100644 --- a/src/java/org/apache/poi/ddf/AbstractEscherOptRecord.java +++ b/src/java/org/apache/poi/ddf/AbstractEscherOptRecord.java @@ -173,7 +173,19 @@ public abstract class AbstractEscherOptRecord extends EscherRecord stringBuilder.append( " " + property.toString() + nl ); } - return stringBuilder.toString(); - } - -} + return stringBuilder.toString(); + } + + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(getChildRecords().size()).append("\n") + .append(tab).append("\t").append("").append(isContainerRecord()).append("\n"); + for (EscherProperty property: getEscherProperties()){ + builder.append(property.toXml(tab+"\t")); + } + builder.append(tab).append("\n"); + return builder.toString(); + } +} diff --git a/src/java/org/apache/poi/ddf/EscherArrayProperty.java b/src/java/org/apache/poi/ddf/EscherArrayProperty.java index e3ad88a071..6faec52321 100644 --- a/src/java/org/apache/poi/ddf/EscherArrayProperty.java +++ b/src/java/org/apache/poi/ddf/EscherArrayProperty.java @@ -135,6 +135,18 @@ public final class EscherArrayProperty extends EscherComplexProperty { + ", data: " + '\n' + results.toString(); } + public String toXml(String tab){ + StringBuilder builder = new StringBuilder(); + builder.append(tab).append("<").append(getClass().getSimpleName()).append(" id=\"0x").append(HexDump.toHex(getId())) + .append("\" name=\"").append(getName()).append("\" blipId=\"") + .append(isBlipId()).append("\">\n"); + for (int i = 0; i < getNumberOfElementsInArray(); i++) { + builder.append("\t").append(tab).append("").append(HexDump.toHex(getElement(i))).append("\n"); + } + builder.append(tab).append("\n"); + return builder.toString(); + } + /** * We have this method because the way in which arrays in escher works * is screwed for seemly arbitary reasons. While most properties are diff --git a/src/java/org/apache/poi/ddf/EscherBSERecord.java b/src/java/org/apache/poi/ddf/EscherBSERecord.java index 78420683a2..556dcdd7e2 100644 --- a/src/java/org/apache/poi/ddf/EscherBSERecord.java +++ b/src/java/org/apache/poi/ddf/EscherBSERecord.java @@ -330,6 +330,25 @@ public final class EscherBSERecord extends EscherRecord { " Extra Data:" + '\n' + extraData; } + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(field_1_blipTypeWin32).append("\n") + .append(tab).append("\t").append("").append(field_2_blipTypeMacOS).append("\n") + .append(tab).append("\t").append("").append(field_3_uid == null ? "" : HexDump.toHex(field_3_uid)).append("\n") + .append(tab).append("\t").append("").append(field_4_tag).append("\n") + .append(tab).append("\t").append("").append(field_5_size).append("\n") + .append(tab).append("\t").append("").append(field_6_ref).append("\n") + .append(tab).append("\t").append("").append(field_7_offset).append("\n") + .append(tab).append("\t").append("").append(field_8_usage).append("\n") + .append(tab).append("\t").append("").append(field_9_name).append("\n") + .append(tab).append("\t").append("").append(field_10_unused2).append("\n") + .append(tab).append("\t").append("").append(field_11_unused3).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + /** * Retrieve the string representation given a blip id. */ diff --git a/src/java/org/apache/poi/ddf/EscherBitmapBlip.java b/src/java/org/apache/poi/ddf/EscherBitmapBlip.java index bec7635ebf..d81eebdcc9 100644 --- a/src/java/org/apache/poi/ddf/EscherBitmapBlip.java +++ b/src/java/org/apache/poi/ddf/EscherBitmapBlip.java @@ -114,4 +114,26 @@ public class EscherBitmapBlip extends EscherBlipRecord { " Marker: 0x" + HexDump.toHex( field_2_marker ) + nl + " Extra Data:" + nl + extraData; } + + @Override + public String toXml(String tab) { + String extraData; + ByteArrayOutputStream b = new ByteArrayOutputStream(); + try + { + HexDump.dump( this.field_pictureData, 0, b, 0 ); + extraData = b.toString(); + } + catch ( Exception e ) + { + extraData = e.toString(); + } + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("0x").append(HexDump.toHex(field_1_UID)).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex(field_2_marker)).append("\n") + .append(tab).append("\t").append("").append(extraData).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } } diff --git a/src/java/org/apache/poi/ddf/EscherBlipRecord.java b/src/java/org/apache/poi/ddf/EscherBlipRecord.java index f10781f602..b6bb8a46b0 100644 --- a/src/java/org/apache/poi/ddf/EscherBlipRecord.java +++ b/src/java/org/apache/poi/ddf/EscherBlipRecord.java @@ -81,4 +81,14 @@ public class EscherBlipRecord extends EscherRecord { // TODO - instantiable supe " Instance: 0x" + HexDump.toHex( getInstance() ) + '\n' + " Extra Data:" + '\n' + extraData; } + + @Override + public String toXml(String tab) { + String extraData = HexDump.toHex(field_pictureData, 32); + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(extraData).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } } diff --git a/src/java/org/apache/poi/ddf/EscherBlipWMFRecord.java b/src/java/org/apache/poi/ddf/EscherBlipWMFRecord.java index 7881754af5..a3659c28bf 100644 --- a/src/java/org/apache/poi/ddf/EscherBlipWMFRecord.java +++ b/src/java/org/apache/poi/ddf/EscherBlipWMFRecord.java @@ -350,6 +350,37 @@ public class EscherBlipWMFRecord " Data:" + nl + extraData; } + @Override + public String toXml(String tab) { + String extraData; + ByteArrayOutputStream b = new ByteArrayOutputStream(); + try + { + HexDump.dump( this.field_12_data, 0, b, 0 ); + extraData = b.toString(); + } + catch ( Exception e ) + { + extraData = e.toString(); + } + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("0x").append(HexDump.toHex(field_1_secondaryUID)).append("\n") + .append(tab).append("\t").append("").append(field_2_cacheOfSize).append("\n") + .append(tab).append("\t").append("").append(field_3_boundaryTop).append("\n") + .append(tab).append("\t").append("").append(field_4_boundaryLeft).append("\n") + .append(tab).append("\t").append("").append(field_5_boundaryWidth).append("\n") + .append(tab).append("\t").append("").append(field_6_boundaryHeight).append("\n") + .append(tab).append("\t").append("").append(field_7_width).append("\n") + .append(tab).append("\t").append("").append(field_8_height).append("\n") + .append(tab).append("\t").append("").append(field_9_cacheOfSavedSize).append("\n") + .append(tab).append("\t").append("").append(field_10_compressionFlag).append("\n") + .append(tab).append("\t").append("").append(field_11_filter).append("\n") + .append(tab).append("\t").append("").append(extraData).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + /** * Compress the contents of the provided array * diff --git a/src/java/org/apache/poi/ddf/EscherBoolProperty.java b/src/java/org/apache/poi/ddf/EscherBoolProperty.java index cf69299f66..da9499af38 100644 --- a/src/java/org/apache/poi/ddf/EscherBoolProperty.java +++ b/src/java/org/apache/poi/ddf/EscherBoolProperty.java @@ -18,6 +18,8 @@ package org.apache.poi.ddf; +import org.apache.poi.util.HexDump; + /** * Represents a boolean property. The actual utility of this property is in doubt because many * of the properties marked as boolean seem to actually contain special values. In other words @@ -65,4 +67,11 @@ public class EscherBoolProperty // + ", value: " + (getValue() != 0); // } + public String toXml(String tab){ + StringBuilder builder = new StringBuilder(); + builder.append(tab).append("<").append(getClass().getSimpleName()).append(" id=\"0x").append(HexDump.toHex(getId())) + .append("\" name=\"").append(getName()).append("\" blipId=\"") + .append(isBlipId()).append("\" value=\"").append(isTrue()).append("\"").append("/>\n"); + return builder.toString(); + } } diff --git a/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java b/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java index 00c61c6e8e..6aa0efde93 100644 --- a/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java +++ b/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java @@ -97,6 +97,18 @@ public class EscherChildAnchorRecord } + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(field_1_dx1).append("\n") + .append(tab).append("\t").append("").append(field_2_dy1).append("\n") + .append(tab).append("\t").append("").append(field_3_dx2).append("\n") + .append(tab).append("\t").append("").append(field_4_dy2).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + /** * Retrieves offset within the parent coordinate space for the top left point. */ diff --git a/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java b/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java index f79569ac6c..fbca0fb87d 100644 --- a/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java +++ b/src/java/org/apache/poi/ddf/EscherClientAnchorRecord.java @@ -160,6 +160,38 @@ public class EscherClientAnchorRecord } + @Override + public String toXml(String tab) { + String extraData; + ByteArrayOutputStream b = new ByteArrayOutputStream(); + try + { + HexDump.dump(this.remainingData, 0, b, 0); + extraData = b.toString(); + } + catch ( Exception e ) + { + extraData = "error\n"; + } + if (extraData.contains("No Data")){ + extraData = "No Data"; + } + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(field_1_flag).append("\n") + .append(tab).append("\t").append("").append(field_2_col1).append("\n") + .append(tab).append("\t").append("").append(field_3_dx1).append("\n") + .append(tab).append("\t").append("").append(field_4_row1).append("\n") + .append(tab).append("\t").append("").append(field_5_dy1).append("\n") + .append(tab).append("\t").append("").append(field_6_col2).append("\n") + .append(tab).append("\t").append("").append(field_7_dx2).append("\n") + .append(tab).append("\t").append("").append(field_8_row2).append("\n") + .append(tab).append("\t").append("").append(field_9_dy2).append("\n") + .append(tab).append("\t").append("").append(extraData).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + /** * 0 = Move and size with Cells, 2 = Move but don't size with cells, 3 = Don't move or size with cells. */ diff --git a/src/java/org/apache/poi/ddf/EscherClientDataRecord.java b/src/java/org/apache/poi/ddf/EscherClientDataRecord.java index a94a022f0a..4c78e6760e 100644 --- a/src/java/org/apache/poi/ddf/EscherClientDataRecord.java +++ b/src/java/org/apache/poi/ddf/EscherClientDataRecord.java @@ -99,6 +99,30 @@ public class EscherClientDataRecord } + @Override + public String toXml(String tab) { + String extraData; + ByteArrayOutputStream b = new ByteArrayOutputStream(); + try + { + HexDump.dump(this.remainingData, 0, b, 0); + extraData = b.toString(); + } + catch ( Exception e ) + { + extraData = "error"; + } + if (extraData.contains("No Data")){ + extraData = "No Data"; + } + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), + HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(extraData).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + /** * Any data recording this record. */ diff --git a/src/java/org/apache/poi/ddf/EscherComplexProperty.java b/src/java/org/apache/poi/ddf/EscherComplexProperty.java index fa822f7f49..d3db3469a4 100644 --- a/src/java/org/apache/poi/ddf/EscherComplexProperty.java +++ b/src/java/org/apache/poi/ddf/EscherComplexProperty.java @@ -134,4 +134,14 @@ public class EscherComplexProperty extends EscherProperty { + ", data: " + System.getProperty("line.separator") + dataStr; } + public String toXml(String tab){ + String dataStr = HexDump.toHex( _complexData, 32); + StringBuilder builder = new StringBuilder(); + builder.append(tab).append("<").append(getClass().getSimpleName()).append(" id=\"0x").append(HexDump.toHex(getId())) + .append("\" name=\"").append(getName()).append("\" blipId=\"") + .append(isBlipId()).append("\">\n"); + builder.append("\t").append(tab).append(dataStr); + builder.append(tab).append("\n"); + return builder.toString(); + } } diff --git a/src/java/org/apache/poi/ddf/EscherContainerRecord.java b/src/java/org/apache/poi/ddf/EscherContainerRecord.java index 70232eb1d8..bed9619d11 100644 --- a/src/java/org/apache/poi/ddf/EscherContainerRecord.java +++ b/src/java/org/apache/poi/ddf/EscherContainerRecord.java @@ -255,6 +255,23 @@ public final class EscherContainerRecord extends EscherRecord { + children.toString(); } + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(getRecordName()).append("\n") + .append(tab).append("\t").append("").append(isContainerRecord()).append("\n") + .append(tab).append("\t").append("").append(HexDump.toHex(_childRecords.size())).append("\n"); + for ( Iterator iterator = _childRecords.iterator(); iterator + .hasNext(); ) + { + EscherRecord record = iterator.next(); + builder.append(record.toXml(tab+"\t")); + } + builder.append(tab).append("\n"); + return builder.toString(); + } + public T getChildById( short recordId ) { for ( EscherRecord childRecord : _childRecords ) diff --git a/src/java/org/apache/poi/ddf/EscherDgRecord.java b/src/java/org/apache/poi/ddf/EscherDgRecord.java index 2f251f17d1..a4447dc016 100644 --- a/src/java/org/apache/poi/ddf/EscherDgRecord.java +++ b/src/java/org/apache/poi/ddf/EscherDgRecord.java @@ -92,7 +92,16 @@ public class EscherDgRecord " Instance: 0x" + HexDump.toHex(getInstance()) + '\n' + " NumShapes: " + field_1_numShapes + '\n' + " LastMSOSPID: " + field_2_lastMSOSPID + '\n'; + } + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(field_1_numShapes).append("\n") + .append(tab).append("\t").append("").append(field_2_lastMSOSPID).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); } /** diff --git a/src/java/org/apache/poi/ddf/EscherDggRecord.java b/src/java/org/apache/poi/ddf/EscherDggRecord.java index c5e4551d4d..7e8d07cb86 100644 --- a/src/java/org/apache/poi/ddf/EscherDggRecord.java +++ b/src/java/org/apache/poi/ddf/EscherDggRecord.java @@ -142,6 +142,18 @@ public final class EscherDggRecord extends EscherRecord { } + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(field_1_shapeIdMax).append("\n") + .append(tab).append("\t").append("").append(getNumIdClusters()).append("\n") + .append(tab).append("\t").append("").append(field_3_numShapesSaved).append("\n") + .append(tab).append("\t").append("").append(field_4_drawingsSaved).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + public int getShapeIdMax() { return field_1_shapeIdMax; } diff --git a/src/java/org/apache/poi/ddf/EscherMetafileBlip.java b/src/java/org/apache/poi/ddf/EscherMetafileBlip.java index d9a1347615..37f4054694 100644 --- a/src/java/org/apache/poi/ddf/EscherMetafileBlip.java +++ b/src/java/org/apache/poi/ddf/EscherMetafileBlip.java @@ -259,12 +259,31 @@ public final class EscherMetafileBlip extends EscherBlipRecord { " Filter: " + HexDump.toHex( field_7_fFilter ) + '\n' + " Extra Data:" + '\n' + extraData + (remainingData == null ? null : ("\n" + - " Remaining Data: " + HexDump.toHex(remainingData, 32))); - } - - /** - * Return the blip signature - * + " Remaining Data: " + HexDump.toHex(remainingData, 32))); + } + + @Override + public String toXml(String tab) { + String extraData = ""; + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_1_UID ) + '\n' + + (field_2_UID == null ? "" : (" UID2: 0x" + HexDump.toHex( field_2_UID ) + '\n'))).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_2_cb )).append("\n") + .append(tab).append("\t").append("").append(getBounds()).append("\n") + .append(tab).append("\t").append("").append(getSizeEMU()).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_5_cbSave )).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_6_fCompression )).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_7_fFilter )).append("\n") + .append(tab).append("\t").append("").append(extraData).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex(remainingData, 32)).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + + /** + * Return the blip signature + * * @return the blip signature */ public short getSignature() { diff --git a/src/java/org/apache/poi/ddf/EscherOptRecord.java b/src/java/org/apache/poi/ddf/EscherOptRecord.java index e83b324adf..39c20b5b3b 100644 --- a/src/java/org/apache/poi/ddf/EscherOptRecord.java +++ b/src/java/org/apache/poi/ddf/EscherOptRecord.java @@ -16,6 +16,7 @@ ==================================================================== */ package org.apache.poi.ddf; +import org.apache.poi.util.HexDump; import org.apache.poi.util.Internal; /** @@ -71,4 +72,15 @@ public class EscherOptRecord extends AbstractEscherOptRecord super.setVersion( value ); } + + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))); + for (EscherProperty property: getEscherProperties()){ + builder.append(property.toXml(tab+"\t")); + } + builder.append(tab).append("\n"); + return builder.toString(); + } } diff --git a/src/java/org/apache/poi/ddf/EscherPictBlip.java b/src/java/org/apache/poi/ddf/EscherPictBlip.java index 9b93b08b9d..c450d4f18c 100644 --- a/src/java/org/apache/poi/ddf/EscherPictBlip.java +++ b/src/java/org/apache/poi/ddf/EscherPictBlip.java @@ -210,7 +210,24 @@ public final class EscherPictBlip extends EscherBlipRecord { " Size in EMU: " + getSizeEMU() + '\n' + " Compressed Size: " + HexDump.toHex( field_5_cbSave ) + '\n' + " Compression: " + HexDump.toHex( field_6_fCompression ) + '\n' + - " Filter: " + HexDump.toHex( field_7_fFilter ) + '\n' + - " Extra Data:" + '\n' + extraData; - } -} + " Filter: " + HexDump.toHex( field_7_fFilter ) + '\n' + + " Extra Data:" + '\n' + extraData; + } + + @Override + public String toXml(String tab) { + String extraData = ""; + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_1_UID )).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_2_cb )).append("\n") + .append(tab).append("\t").append("").append(getBounds()).append("\n") + .append(tab).append("\t").append("").append(getSizeEMU()).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_5_cbSave )).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_6_fCompression )).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex( field_7_fFilter )).append("\n") + .append(tab).append("\t").append("").append(extraData).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } +} diff --git a/src/java/org/apache/poi/ddf/EscherProperty.java b/src/java/org/apache/poi/ddf/EscherProperty.java index e06fff1bfc..0d4efce975 100644 --- a/src/java/org/apache/poi/ddf/EscherProperty.java +++ b/src/java/org/apache/poi/ddf/EscherProperty.java @@ -72,6 +72,13 @@ public abstract class EscherProperty { public int getPropertySize() { return 6; } + + public String toXml(String tab){ + StringBuilder builder = new StringBuilder(); + builder.append(tab).append("<").append(getClass().getSimpleName()).append(" id=\"").append(getId()).append("\" name=\"").append(getName()).append("\" blipId=\"") + .append(isBlipId()).append("\"/>\n"); + return builder.toString(); + } /** * Escher properties consist of a simple fixed length part and a complex variable length part. diff --git a/src/java/org/apache/poi/ddf/EscherRGBProperty.java b/src/java/org/apache/poi/ddf/EscherRGBProperty.java index 2e1ef02618..5d23addfb5 100644 --- a/src/java/org/apache/poi/ddf/EscherRGBProperty.java +++ b/src/java/org/apache/poi/ddf/EscherRGBProperty.java @@ -17,6 +17,8 @@ package org.apache.poi.ddf; +import org.apache.poi.util.HexDump; + /** * A color property. * @@ -51,4 +53,11 @@ public class EscherRGBProperty return (byte) ( (propertyValue >> 16) & 0xFF ); } + public String toXml(String tab){ + StringBuilder builder = new StringBuilder(); + builder.append(tab).append("<").append(getClass().getSimpleName()).append(" id=\"0x").append(HexDump.toHex(getId())) + .append("\" name=\"").append(getName()).append("\" blipId=\"") + .append(isBlipId()).append("\" value=\"0x").append(HexDump.toHex(propertyValue)).append("\"/>\n"); + return builder.toString(); + } } diff --git a/src/java/org/apache/poi/ddf/EscherRecord.java b/src/java/org/apache/poi/ddf/EscherRecord.java index 0eb5a31d49..cad6be171d 100644 --- a/src/java/org/apache/poi/ddf/EscherRecord.java +++ b/src/java/org/apache/poi/ddf/EscherRecord.java @@ -22,10 +22,7 @@ import java.io.PrintWriter; import java.util.Collections; import java.util.List; -import org.apache.poi.util.BitField; -import org.apache.poi.util.BitFieldFactory; -import org.apache.poi.util.Internal; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.*; /** * The base abstract record from which all escher records are defined. Subclasses will need @@ -297,4 +294,28 @@ public abstract class EscherRecord { { _options = fVersion.setShortValue( _options, value ); } + + /** + * @param tab - each children must be a right of his parent + * @return + */ + public String toXml(String tab){ + StringBuilder builder = new StringBuilder(); + builder.append(tab).append("<").append(getClass().getSimpleName()).append(">\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex(_recordId)).append("\n") + .append(tab).append("\t").append("").append(_options).append("\n") + .append(tab).append("\n"); + return builder.toString(); + } + + protected String formatXmlRecordHeader(String className, String recordId, String version, String instance){ + StringBuilder builder = new StringBuilder(); + builder.append("<").append(className).append(" recordId=\"0x").append(recordId).append("\" version=\"0x") + .append(version).append("\" instance=\"0x").append(instance).append("\">\n"); + return builder.toString(); + } + + public String toXml(){ + return toXml(""); + } } diff --git a/src/java/org/apache/poi/ddf/EscherSimpleProperty.java b/src/java/org/apache/poi/ddf/EscherSimpleProperty.java index a1adae84d6..78fb642034 100644 --- a/src/java/org/apache/poi/ddf/EscherSimpleProperty.java +++ b/src/java/org/apache/poi/ddf/EscherSimpleProperty.java @@ -118,4 +118,12 @@ public class EscherSimpleProperty extends EscherProperty + ", value: " + propertyValue + " (0x" + HexDump.toHex(propertyValue) + ")"; } + public String toXml(String tab){ + StringBuilder builder = new StringBuilder(); + builder.append(tab).append("<").append(getClass().getSimpleName()).append(" id=\"0x").append(HexDump.toHex(getId())) + .append("\" name=\"").append(getName()).append("\" blipId=\"") + .append(isBlipId()).append("\" complex=\"").append(isComplex()).append("\" value=\"").append("0x") + .append(HexDump.toHex(propertyValue)).append("\"/>\n"); + return builder.toString(); + } } diff --git a/src/java/org/apache/poi/ddf/EscherSpRecord.java b/src/java/org/apache/poi/ddf/EscherSpRecord.java index cd51c91929..11a893f466 100644 --- a/src/java/org/apache/poi/ddf/EscherSpRecord.java +++ b/src/java/org/apache/poi/ddf/EscherSpRecord.java @@ -115,6 +115,17 @@ public class EscherSpRecord } + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(HexDump.toHex(getShapeType())).append("\n") + .append(tab).append("\t").append("").append(field_1_shapeId).append("\n") + .append(tab).append("\t").append("").append(decodeFlags(field_2_flags) + " (0x" + HexDump.toHex(field_2_flags) + ")").append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + /** * Converts the shape flags into a more descriptive name. */ diff --git a/src/java/org/apache/poi/ddf/EscherSpgrRecord.java b/src/java/org/apache/poi/ddf/EscherSpgrRecord.java index 43197322df..de279a5a50 100644 --- a/src/java/org/apache/poi/ddf/EscherSpgrRecord.java +++ b/src/java/org/apache/poi/ddf/EscherSpgrRecord.java @@ -97,6 +97,18 @@ public class EscherSpgrRecord " RectHeight: " + field_4_rectY2 + '\n'; } + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(field_1_rectX1).append("\n") + .append(tab).append("\t").append("").append(field_2_rectY1).append("\n") + .append(tab).append("\t").append("").append(field_3_rectX2).append("\n") + .append(tab).append("\t").append("").append(field_4_rectY2).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + /** * The starting top-left coordinate of child records. */ diff --git a/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java b/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java index 2f85ef24ad..443c003784 100644 --- a/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java +++ b/src/java/org/apache/poi/ddf/EscherSplitMenuColorsRecord.java @@ -99,6 +99,18 @@ public class EscherSplitMenuColorsRecord ""; } + @Override + public String toXml(String tab) { + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("0x").append(HexDump.toHex(field_1_color1)).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex(field_2_color2)).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex(field_3_color3)).append("\n") + .append(tab).append("\t").append("0x").append(HexDump.toHex(field_4_color4)).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + public int getColor1() { return field_1_color1; diff --git a/src/java/org/apache/poi/ddf/EscherTextboxRecord.java b/src/java/org/apache/poi/ddf/EscherTextboxRecord.java index 5e726d523e..12a0dcccd8 100644 --- a/src/java/org/apache/poi/ddf/EscherTextboxRecord.java +++ b/src/java/org/apache/poi/ddf/EscherTextboxRecord.java @@ -139,6 +139,28 @@ public class EscherTextboxRecord extends EscherRecord theDumpHex; } + @Override + public String toXml(String tab) { + String theDumpHex = ""; + try + { + if (thedata.length != 0) + { + theDumpHex += HexDump.dump(thedata, 0, 0); + } + } + catch ( Exception e ) + { + theDumpHex = "Error!!"; + } + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(isContainerRecord()).append("\n") + .append(tab).append("\t").append("").append(getChildRecords().size()).append("\n") + .append(tab).append("\t").append("").append(theDumpHex).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } } diff --git a/src/java/org/apache/poi/ddf/UnknownEscherRecord.java b/src/java/org/apache/poi/ddf/UnknownEscherRecord.java index 563ef6d6e4..e60703ae3a 100644 --- a/src/java/org/apache/poi/ddf/UnknownEscherRecord.java +++ b/src/java/org/apache/poi/ddf/UnknownEscherRecord.java @@ -18,6 +18,7 @@ package org.apache.poi.ddf; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.apache.poi.util.HexDump; @@ -143,6 +144,24 @@ public final class UnknownEscherRecord extends EscherRecord { children.toString(); } + @Override + public String toXml(String tab) { + String theDumpHex = HexDump.toHex(thedata, 32); + StringBuilder builder = new StringBuilder(); + builder.append(tab).append(formatXmlRecordHeader(getClass().getSimpleName(), HexDump.toHex(getRecordId()), HexDump.toHex(getVersion()), HexDump.toHex(getInstance()))) + .append(tab).append("\t").append("").append(isContainerRecord()).append("\n") + .append(tab).append("\t").append("").append(HexDump.toHex(_childRecords.size())).append("\n"); + for ( Iterator iterator = _childRecords.iterator(); iterator + .hasNext(); ) + { + EscherRecord record = iterator.next(); + builder.append(record.toXml(tab+"\t")); + } + builder.append(theDumpHex).append("\n"); + builder.append(tab).append("\n"); + return builder.toString(); + } + public void addChildRecord(EscherRecord childRecord) { getChildRecords().add( childRecord ); } diff --git a/src/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java b/src/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java new file mode 100644 index 0000000000..b20670c89f --- /dev/null +++ b/src/java/org/apache/poi/hssf/dev/BiffDrawingToXml.java @@ -0,0 +1,192 @@ +/* + * ==================================================================== + * 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.dev; + +import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.hssf.model.InternalWorkbook; +import org.apache.poi.hssf.record.*; +import org.apache.poi.hssf.usermodel.HSSFPatriarch; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +import java.io.*; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility for representing drawings contained in a binary Excel file as a XML tree + * + * @author Evgeniy Berlog + * date: 10.04.12 + */ +public class BiffDrawingToXml { + + private static final String SHEET_NAME_PARAM = "-sheet-name"; + private static final String SHEET_INDEXES_PARAM = "-sheet-indexes"; + private static final String EXCLUDE_WORKBOOK_RECORDS = "-exclude-workbook"; + + private static int getAttributeIndex(String attribute, String[] params) { + for (int i = 0; i < params.length; i++) { + String param = params[i]; + if (attribute.equals(param)) { + return i; + } + } + return -1; + } + + private static boolean isExcludeWorkbookRecords(String[] params) { + return -1 != getAttributeIndex(EXCLUDE_WORKBOOK_RECORDS, params); + } + + private static List getIndexesByName(String[] params, HSSFWorkbook workbook) { + List list = new ArrayList(); + int pos = getAttributeIndex(SHEET_NAME_PARAM, params); + if (-1 != pos) { + if (pos >= params.length) { + throw new IllegalArgumentException("sheet name param value was not specified"); + } + String sheetName = params[pos + 1]; + int sheetPos = workbook.getSheetIndex(sheetName); + if (-1 == sheetPos){ + throw new IllegalArgumentException("specified sheet name has not been found in xls file"); + } + list.add(sheetPos); + } + return list; + } + + private static List getIndexesByIdArray(String[] params) { + List list = new ArrayList(); + int pos = getAttributeIndex(SHEET_INDEXES_PARAM, params); + if (-1 != pos) { + if (pos >= params.length) { + throw new IllegalArgumentException("sheet list value was not specified"); + } + String sheetParam = params[pos + 1]; + String[] sheets = sheetParam.split(","); + for (String sheet : sheets) { + list.add(Integer.parseInt(sheet)); + } + } + return list; + } + + private static List getSheetsIndexes(String[] params, HSSFWorkbook workbook) { + List list = new ArrayList(); + list.addAll(getIndexesByIdArray(params)); + list.addAll(getIndexesByName(params, workbook)); + if (0 == list.size()) { + int size = workbook.getNumberOfSheets(); + for (int i = 0; i < size; i++) { + list.add(i); + } + } + return list; + } + + private static String getInputFileName(String[] params) { + return params[params.length - 1]; + } + + private static String getOutputFileName(String input) { + if (input.contains("xls")) { + return input.replace(".xls", ".xml"); + } + return input + ".xml"; + } + + public static void main(String[] params) throws IOException { + if (0 == params.length) { + System.out.println("Usage: BiffDrawingToXml [options] inputWorkbook"); + System.out.println("Options:"); + System.out.println(" -exclude-workbook exclude workbook-level records"); + System.out.println(" -sheet-indexes output sheets with specified indexes"); + System.out.println(" -sheet-namek output sheets with specified name"); + return; + } + String input = getInputFileName(params); + FileInputStream inp = new FileInputStream(input); + String output = getOutputFileName(input); + FileOutputStream outputStream = new FileOutputStream(output); + writeToFile(outputStream, inp, isExcludeWorkbookRecords(params), params); + inp.close(); + outputStream.close(); + } + + public static void writeToFile(FileOutputStream fos, InputStream xlsWorkbook, boolean excludeWorkbookRecords, String[] params) throws IOException { + POIFSFileSystem fs = new POIFSFileSystem(xlsWorkbook); + HSSFWorkbook workbook = new HSSFWorkbook(fs); + InternalWorkbook internalWorkbook = getInternalWorkbook(workbook); + DrawingGroupRecord r = (DrawingGroupRecord) internalWorkbook.findFirstRecordBySid(DrawingGroupRecord.sid); + r.decode(); + + StringBuilder builder = new StringBuilder(); + builder.append("\n"); + String tab = "\t"; + if (!excludeWorkbookRecords) { + List escherRecords = r.getEscherRecords(); + for (EscherRecord record : escherRecords) { + builder.append(record.toXml(tab)); + } + } + List sheets = getSheetsIndexes(params, workbook); + for (Integer i : sheets) { + HSSFPatriarch p = workbook.getSheetAt(i).getDrawingPatriarch(); + if(p != null ) { + builder.append(tab).append("\n"); + builder.append(getHSSFPatriarchBoundAggregate(p).toXml(tab + "\t")); + builder.append(tab).append("\n"); + } + } + builder.append("\n"); + fos.write(builder.toString().getBytes()); + fos.close(); + } + + private static EscherAggregate getHSSFPatriarchBoundAggregate(HSSFPatriarch patriarch) { + Field boundAggregateField = null; + try { + boundAggregateField = patriarch.getClass().getDeclaredField("_boundAggregate"); + boundAggregateField.setAccessible(true); + return (EscherAggregate) boundAggregateField.get(patriarch); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + private static InternalWorkbook getInternalWorkbook(HSSFWorkbook workbook) { + Field internalSheetField = null; + try { + internalSheetField = workbook.getClass().getDeclaredField("workbook"); + internalSheetField.setAccessible(true); + return (InternalWorkbook) internalSheetField.get(workbook); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/src/java/org/apache/poi/hssf/record/EscherAggregate.java b/src/java/org/apache/poi/hssf/record/EscherAggregate.java index abd67e72cb..6cbec15016 100644 --- a/src/java/org/apache/poi/hssf/record/EscherAggregate.java +++ b/src/java/org/apache/poi/hssf/record/EscherAggregate.java @@ -329,6 +329,18 @@ public final class EscherAggregate extends AbstractEscherHolderRecord { return result.toString(); } + + public String toXml(String tab){ + StringBuilder builder = new StringBuilder(); + builder.append(tab).append("<").append(getRecordName()).append(">\n"); + for ( Iterator iterator = getEscherRecords().iterator(); iterator.hasNext(); ) + { + EscherRecord escherRecord = (EscherRecord) iterator.next(); + builder.append( escherRecord.toXml(tab+"\t") ); + } + builder.append(tab).append("\n"); + return builder.toString(); + } /** * Collapses the drawing records into an aggregate. -- 2.39.5