From 28b6f4e2a3239ea5b63eaacd4246149c6913f933 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Mon, 5 May 2008 14:20:39 +0000 Subject: [PATCH] merged with trunk r653489 git-svn-id: https://svn.apache.org/repos/asf/poi/tags/REL_3_1_BETA2@653490 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 180 +---------- src/documentation/content/xdocs/changes.xml | 12 +- src/documentation/content/xdocs/index.xml | 17 + src/documentation/content/xdocs/status.xml | 12 +- src/documentation/release-guide.txt | 59 +++- .../org/apache/poi/ddf/EscherDggRecord.java | 27 +- .../apache/poi/ddf/EscherMetafileBlip.java | 60 +++- .../apache/poi/hssf/model/FormulaParser.java | 26 +- .../org/apache/poi/hssf/model/Workbook.java | 6 +- .../poi/hssf/record/ColumnInfoRecord.java | 39 +-- .../poi/hssf/record/FileSharingRecord.java | 90 ++---- .../apache/poi/hssf/record/FormulaRecord.java | 7 +- .../poi/hssf/record/SharedFormulaRecord.java | 14 +- .../record/formula/AbstractFunctionPtg.java | 8 +- .../poi/hssf/record/formula/AreaNAPtg.java | 22 +- .../poi/hssf/record/formula/AreaNPtg.java | 14 +- .../poi/hssf/record/formula/AreaNVPtg.java | 24 +- .../poi/hssf/record/formula/AreaPtg.java | 12 +- .../poi/hssf/record/formula/FuncPtg.java | 8 +- .../poi/hssf/record/formula/FuncVarPtg.java | 4 +- .../apache/poi/hssf/record/formula/Ptg.java | 294 +++++++++--------- .../poi/hssf/record/formula/RefNAPtg.java | 22 +- .../poi/hssf/record/formula/RefNPtg.java | 21 +- .../poi/hssf/record/formula/RefNVPtg.java | 17 +- .../poi/hssf/record/formula/ReferencePtg.java | 14 +- .../formula/function/FunctionDataBuilder.java | 6 +- .../formula/function/FunctionMetadata.java | 13 +- .../function/FunctionMetadataReader.java | 68 +++- .../poi/hssf/usermodel/HSSFPictureData.java | 15 +- .../apache/poi/hssf/usermodel/HSSFSheet.java | 12 +- .../poi/hssf/usermodel/HSSFWorkbook.java | 271 ++++++++-------- .../org/apache/poi/POIXMLDocument.java | 45 --- .../org/apache/poi/POIXMLTextExtractor.java | 31 -- .../org/apache/poi/hslf/HSLFXML.java | 148 --------- .../extractor/HXFPowerPointExtractor.java | 139 --------- .../poi/hslf/usermodel/HSLFXMLSlideShow.java | 39 --- .../org/apache/poi/hssf/HSSFXML.java | 104 ------- .../poi/hssf/extractor/HXFExcelExtractor.java | 133 -------- .../poi/hssf/model/SharedStringsTable.java | 78 ----- .../poi/hssf/usermodel/HSSFXMLCell.java | 58 ---- .../poi/hssf/usermodel/HSSFXMLWorkbook.java | 43 --- .../org/apache/poi/hwpf/HWPFXML.java | 92 ------ .../poi/hwpf/extractor/HXFWordExtractor.java | 87 ------ .../poi/hwpf/usermodel/HWPFXMLDocument.java | 36 --- .../org/apache/poi/hxf/HXFDocument.java | 272 ---------------- .../org/apache/poi/hxf/dev/HXFLister.java | 133 -------- .../org/apache/poi/hslf/TestHSLFXML.java | 127 -------- .../extractor/TestHXFPowerPointExtractor.java | 109 ------- .../org/apache/poi/hssf/TestHSSFXML.java | 160 ---------- .../hssf/extractor/TestHXFExcelExtractor.java | 196 ------------ .../org/apache/poi/hwpf/TestHWPFXML.java | 110 ------- .../hwpf/extractor/TestHXFWordExtractor.java | 117 ------- .../org/apache/poi/hxf/TestDetectAsOOXML.java | 65 ---- .../org/apache/poi/hslf/model/AutoShapes.java | 7 + .../org/apache/poi/hslf/model/Freeform.java | 29 +- .../apache/poi/hslf/model/MasterSheet.java | 17 + .../org/apache/poi/hslf/model/Picture.java | 5 + .../src/org/apache/poi/hslf/model/Shape.java | 51 --- .../org/apache/poi/hslf/model/ShapeGroup.java | 18 +- .../src/org/apache/poi/hslf/model/Sheet.java | 57 +++- .../apache/poi/hslf/model/SimpleShape.java | 4 +- .../src/org/apache/poi/hslf/model/Slide.java | 41 +++ .../apache/poi/hslf/model/SlideMaster.java | 1 + .../apache/poi/hslf/model/TextPainter.java | 77 ++++- .../org/apache/poi/hslf/model/TextRun.java | 17 +- .../org/apache/poi/hslf/model/TextShape.java | 29 +- .../org/apache/poi/hslf/record/PPDrawing.java | 24 ++ .../apache/poi/hslf/record/RecordTypes.java | 2 +- .../poi/hslf/record/StyleTextPropAtom.java | 2 +- .../apache/poi/hslf/record/TextRulerAtom.java | 194 ++++++++++++ .../poi/hslf/usermodel/RichTextRun.java | 32 +- .../apache/poi/hslf/usermodel/SlideShow.java | 54 ++-- .../apache/poi/hslf/model/TestFreeform.java | 8 +- .../org/apache/poi/hslf/model/TestShapes.java | 53 +++- .../poi/hslf/record/TestTextRulerAtom.java | 76 +++++ .../poi/hslf/usermodel/TestRichTextRun.java | 8 +- .../org/apache/poi/ddf/AllPOIDDFTests.java | 1 + .../apache/poi/ddf/TestEscherBlipRecord.java | 156 ++++++++++ .../poi/ddf/TestEscherContainerRecord.java | 28 +- .../org/apache/poi/ddf/data/Container.dat | Bin 0 -> 33627 bytes .../apache/poi/hssf/HSSFTestDataSamples.java | 24 ++ .../org/apache/poi/hssf/data/44840.xls | Bin 0 -> 16896 bytes .../org/apache/poi/hssf/data/44861.xls | Bin 0 -> 61440 bytes .../org/apache/poi/hssf/data/44891.xls | Bin 0 -> 432128 bytes .../apache/poi/hssf/data/ex44921-21902.xls | Bin 0 -> 39424 bytes .../poi/hssf/model/TestFormulaParser.java | 19 +- .../hssf/record/formula/AllFormulaTests.java | 1 + .../hssf/record/formula/TestFuncVarPtg.java | 53 ++++ .../hssf/record/formula/TestReferencePtg.java | 27 +- .../poi/hssf/usermodel/AllUserModelTests.java | 3 +- .../apache/poi/hssf/usermodel/TestBugs.java | 12 +- .../usermodel/TestFormulaEvaluatorBugs.java | 28 +- .../poi/hssf/usermodel/TestHSSFPatriarch.java | 71 +++++ .../poi/hssf/usermodel/TestHSSFPicture.java | 30 +- .../poi/hssf/usermodel/TestHSSFWorkbook.java | 20 +- 95 files changed, 1775 insertions(+), 3220 deletions(-) delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/POIXMLDocument.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/POIXMLTextExtractor.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hslf/HSLFXML.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hslf/extractor/HXFPowerPointExtractor.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hslf/usermodel/HSLFXMLSlideShow.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hssf/HSSFXML.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hssf/extractor/HXFExcelExtractor.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hssf/model/SharedStringsTable.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hssf/usermodel/HSSFXMLCell.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hssf/usermodel/HSSFXMLWorkbook.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hwpf/HWPFXML.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hwpf/extractor/HXFWordExtractor.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hwpf/usermodel/HWPFXMLDocument.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hxf/HXFDocument.java delete mode 100644 src/scratchpad/ooxml-src/org/apache/poi/hxf/dev/HXFLister.java delete mode 100644 src/scratchpad/ooxml-testcases/org/apache/poi/hslf/TestHSLFXML.java delete mode 100644 src/scratchpad/ooxml-testcases/org/apache/poi/hslf/extractor/TestHXFPowerPointExtractor.java delete mode 100644 src/scratchpad/ooxml-testcases/org/apache/poi/hssf/TestHSSFXML.java delete mode 100644 src/scratchpad/ooxml-testcases/org/apache/poi/hssf/extractor/TestHXFExcelExtractor.java delete mode 100644 src/scratchpad/ooxml-testcases/org/apache/poi/hwpf/TestHWPFXML.java delete mode 100644 src/scratchpad/ooxml-testcases/org/apache/poi/hwpf/extractor/TestHXFWordExtractor.java delete mode 100644 src/scratchpad/ooxml-testcases/org/apache/poi/hxf/TestDetectAsOOXML.java create mode 100755 src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java create mode 100755 src/testcases/org/apache/poi/ddf/TestEscherBlipRecord.java create mode 100644 src/testcases/org/apache/poi/ddf/data/Container.dat create mode 100644 src/testcases/org/apache/poi/hssf/data/44840.xls create mode 100644 src/testcases/org/apache/poi/hssf/data/44861.xls create mode 100644 src/testcases/org/apache/poi/hssf/data/44891.xls create mode 100644 src/testcases/org/apache/poi/hssf/data/ex44921-21902.xls create mode 100644 src/testcases/org/apache/poi/hssf/record/formula/TestFuncVarPtg.java create mode 100644 src/testcases/org/apache/poi/hssf/usermodel/TestHSSFPatriarch.java diff --git a/build.xml b/build.xml index b97df7693..f8ef5efa3 100644 --- a/build.xml +++ b/build.xml @@ -74,7 +74,6 @@ under the License. - @@ -124,33 +123,6 @@ under the License. - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -163,7 +135,7 @@ under the License. - + @@ -195,15 +167,6 @@ under the License. - - - - - - - - - @@ -268,15 +231,12 @@ under the License. - - - @@ -311,11 +271,6 @@ under the License. - - - - - @@ -330,67 +285,8 @@ under the License. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -473,24 +369,6 @@ under the License. - - - - - - - - - - - - - - - @@ -523,6 +401,8 @@ under the License. file="${main.src.test}/org/apache/poi/hpsf/data"/> + @@ -725,43 +605,6 @@ under the License. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1148,21 +991,6 @@ FORREST_HOME environment variable! - - - - - - - - - - - - - - - @@ -1171,6 +999,7 @@ FORREST_HOME environment variable! + @@ -1193,6 +1022,7 @@ FORREST_HOME environment variable! + diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 336ac0615..150b41ff9 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -36,7 +36,17 @@ - + + 44929 - Improved error handling in HSSFWorkbook when attempting to read a BIFF5 file + 44675 - Parameter operand classes (function metadata) required to encode SUM() etc properly. Added parse validation for number of parameters + 44921 - allow Ptg.writeBytes() to be called on relative ref Ptgs (RefN* and AreaN*) + 44914 - Fix/suppress warning message "WARN. Unread n bytes of record 0xNN" + 44892 - made HSSFWorkbook.getSheet(String) case insensitive + 44886] - Correctly process PICT metafile in EscherMetafileBlip + 44893 - Take into account indentation in HSSFSheet.autoSizeColumn + + + 44857 - Avoid OOM on unknown escher records when EscherMetafileBlip is incorrect HSLF: Support for getting embedded sounds from slide show HSLF: Initial support for rendering slides into images HSLF: Support for getting OLE object data from slide show diff --git a/src/documentation/content/xdocs/index.xml b/src/documentation/content/xdocs/index.xml index 92eaaea0d..8cc7ea858 100644 --- a/src/documentation/content/xdocs/index.xml +++ b/src/documentation/content/xdocs/index.xml @@ -31,6 +31,23 @@ +
POI 3.1-BETA1 Released (2008-04028) +

+ The POI team is pleased to announce the release of 3.1 BETA1 which is one of the final steps before 3.1 FINAL. + The status of this release is a beta, meaning that we encourage users to try it out. + If you find any bugs, please report them to the POI bug database or to + the POI Developer List. +

A full list of changes is available in + the changelog, and + download + the source and binaries from your + local mirror. +

+

+ The release is also available from the central Maven repository + under Group ID "org.apache.poi" and Version "3.1-beta1". +

+
POI 3.0.2 Released

The POI team is pleased to announce POI 3.0.2, the latest release of Apache POI. There have been many important bug fixes since the 3.0.1 release and a lot of new features. A full list of changes is available in diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index c61f2b1db..e9059493b 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -33,7 +33,17 @@ - + + 44929 - Improved error handling in HSSFWorkbook when attempting to read a BIFF5 file + 44675 - Parameter operand classes (function metadata) required to encode SUM() etc properly. Added parse validation for number of parameters + 44921 - allow Ptg.writeBytes() to be called on relative ref Ptgs (RefN* and AreaN*) + 44914 - Fix/suppress warning message "WARN. Unread n bytes of record 0xNN" + 44892 - made HSSFWorkbook.getSheet(String) case insensitive + 44886] - Correctly process PICT metafile in EscherMetafileBlip + 44893 - Take into account indentation in HSSFSheet.autoSizeColumn + + + 44857 - Avoid OOM on unknown escher records when EscherMetafileBlip is incorrect HSLF: Support for getting embedded sounds from slide show HSLF: Initial support for rendering slides into images HSLF: Support for getting OLE object data from slide show diff --git a/src/documentation/release-guide.txt b/src/documentation/release-guide.txt index 8a38150ac..56ebf3fa1 100755 --- a/src/documentation/release-guide.txt +++ b/src/documentation/release-guide.txt @@ -19,7 +19,11 @@ POI Release Guide POI 3.0.2 and 3.1 were built using Ant 1.6.2 and Forrest 0.5 (II) Making release artefacts - 1. Update version id in build.xml. + 1. Update version id in build.xml +{code:xml} + +{code} + 2. Tag current version. Include the current revision number in the comment {code} @@ -46,7 +50,7 @@ https://svn.apache.org/repos/asf/poi/trunk 5. Start a new section in sites.xml and status.xml. - 6. Build as if the vote had passed. The buid date must be +7 days from current. + 6. Build as if the vote had passed. The build date must be +7 days from current. {code} ant build {code} @@ -109,27 +113,68 @@ Log-in on people.apache.org 1. Go to ~/POI-3.1-BETA1 +zap previous version first. + +{code} cd ~/POI-3.1-BETA1/main +{code} BETA and ALPHA releases: + +{code} cp *-src-* /www/www.apache.org/dist/poi/dev/src cp *-bin-* /www/www.apache.org/dist/poi/dev/bin +{code} FINAL release: +{code} cp *-src-* /www/www.apache.org/dist/poi/release/src cp *-bin-* /www/www.apache.org/dist/poi/release/bin +{code} +{code} cd ~/POI-3.1-BETA1/maven - cp -r org.apache.poi /www/people.apache.org/repo/m1-ibiblio-rsync-repository/ cp -r poi/poms /www/people.apache.org/repo/m1-ibiblio-rsync-repository/poi - +{code} 2. Make sure that the files are owned by the unix group apcvs and that they are writable by this group. 3. Wait for the distributions to appear on your favourite mirror -4. Send announcements: - - to poi-user and poi-dev lists - - send announcements to announcement@apache.org, announcements@jakarta.apache.org +4. test maven +create a simple project and make sure the release artifacts are accessible by maven: + +{code} +$ mvn archetype:create -DgroupId=org.apache.poi.scratchpad -DartifactId=maven-test +cd maven-test +{code} +edit pom.xml and add the release artefacts to the project dependencies: + +{code:xml} + + org.apache.poi + poi + 3.1-beta1 + + + org.apache.poi + poi-scratchpad + 3.1-beta1 + +{code} + +{code} +mvn compile +{code} + +You should see [INFO] BUILD SUCCESSFUL in the end. + +5. Don't forget to upload the latest version of the site and javadocs + +6. Send announcements: + - to poi-user and poi-dev lists + - to announcement@apache.org, announcements@jakarta.apache.org + +Note, announcements should be sent from your @apache.org e-mail address. diff --git a/src/java/org/apache/poi/ddf/EscherDggRecord.java b/src/java/org/apache/poi/ddf/EscherDggRecord.java index 0eff3feee..0ff00016d 100644 --- a/src/java/org/apache/poi/ddf/EscherDggRecord.java +++ b/src/java/org/apache/poi/ddf/EscherDggRecord.java @@ -38,6 +38,7 @@ public class EscherDggRecord private int field_3_numShapesSaved; private int field_4_drawingsSaved; private FileIdCluster[] field_5_fileIdClusters; + private int maxDgId; public static class FileIdCluster { @@ -87,6 +88,7 @@ public class EscherDggRecord for (int i = 0; i < field_5_fileIdClusters.length; i++) { field_5_fileIdClusters[i] = new FileIdCluster(LittleEndian.getInt( data, pos + size ), LittleEndian.getInt( data, pos + size + 4 )); + maxDgId = Math.max(maxDgId, field_5_fileIdClusters[i].getDrawingGroupId()); size += 8; } bytesRemaining -= size; @@ -229,7 +231,14 @@ public class EscherDggRecord this.field_4_drawingsSaved = field_4_drawingsSaved; } - public FileIdCluster[] getFileIdClusters() + /** + * @return The maximum drawing group ID + */ + public int getMaxDrawingGroupId(){ + return maxDgId; + } + + public FileIdCluster[] getFileIdClusters() { return field_5_fileIdClusters; } @@ -240,10 +249,23 @@ public class EscherDggRecord } public void addCluster( int dgId, int numShapedUsed ) + { + addCluster(dgId, numShapedUsed, true); + } + + /** + * Add a new cluster + * + * @param dgId id of the drawing group (stored in the record options) + * @param numShapedUsed initial value of the numShapedUsed field + * @param sort if true then sort clusters by drawing group id.( + * In Excel the clusters are sorted but in PPT they are not) + */ + public void addCluster( int dgId, int numShapedUsed, boolean sort ) { List clusters = new ArrayList(Arrays.asList(field_5_fileIdClusters)); clusters.add(new FileIdCluster(dgId, numShapedUsed)); - Collections.sort(clusters, new Comparator() + if(sort) Collections.sort(clusters, new Comparator() { public int compare( Object o1, Object o2 ) { @@ -257,6 +279,7 @@ public class EscherDggRecord return +1; } } ); + maxDgId = Math.min(maxDgId, dgId); field_5_fileIdClusters = (FileIdCluster[]) clusters.toArray( new FileIdCluster[clusters.size()] ); } } diff --git a/src/java/org/apache/poi/ddf/EscherMetafileBlip.java b/src/java/org/apache/poi/ddf/EscherMetafileBlip.java index f2dc1bb01..75c282ea5 100644 --- a/src/java/org/apache/poi/ddf/EscherMetafileBlip.java +++ b/src/java/org/apache/poi/ddf/EscherMetafileBlip.java @@ -41,9 +41,20 @@ public class EscherMetafileBlip public static final short RECORD_ID_WMF = (short) 0xF018 + 3; public static final short RECORD_ID_PICT = (short) 0xF018 + 4; + /** + * BLIP signatures as defined in the escher spec + */ + public static final short SIGNATURE_EMF = 0x3D40; + public static final short SIGNATURE_WMF = 0x2160; + public static final short SIGNATURE_PICT = 0x5420; + private static final int HEADER_SIZE = 8; private byte[] field_1_UID; + /** + * The primary UID is only saved to disk if (blip_instance ^ blip_signature == 1) + */ + private byte[] field_2_UID; private int field_2_cb; private int field_3_rcBounds_x1; private int field_3_rcBounds_y1; @@ -72,6 +83,12 @@ public class EscherMetafileBlip field_1_UID = new byte[16]; System.arraycopy( data, pos, field_1_UID, 0, 16 ); pos += 16; + + if((getOptions() ^ getSignature()) == 0x10){ + field_2_UID = new byte[16]; + System.arraycopy( data, pos, field_2_UID, 0, 16 ); pos += 16; + } + field_2_cb = LittleEndian.getInt( data, pos ); pos += 4; field_3_rcBounds_x1 = LittleEndian.getInt( data, pos ); pos += 4; field_3_rcBounds_y1 = LittleEndian.getInt( data, pos ); pos += 4; @@ -118,9 +135,12 @@ public class EscherMetafileBlip int pos = offset; LittleEndian.putShort( data, pos, getOptions() ); pos += 2; LittleEndian.putShort( data, pos, getRecordId() ); pos += 2; - LittleEndian.putInt( data, getRecordSize() - HEADER_SIZE ); pos += 4; + LittleEndian.putInt( data, pos, getRecordSize() - HEADER_SIZE ); pos += 4; - System.arraycopy( field_1_UID, 0, data, pos, 16 ); pos += 16; + System.arraycopy( field_1_UID, 0, data, pos, field_1_UID.length ); pos += field_1_UID.length; + if((getOptions() ^ getSignature()) == 0x10){ + System.arraycopy( field_2_UID, 0, data, pos, field_2_UID.length ); pos += field_2_UID.length; + } LittleEndian.putInt( data, pos, field_2_cb ); pos += 4; LittleEndian.putInt( data, pos, field_3_rcBounds_x1 ); pos += 4; LittleEndian.putInt( data, pos, field_3_rcBounds_y1 ); pos += 4; @@ -135,7 +155,7 @@ public class EscherMetafileBlip System.arraycopy( raw_pictureData, 0, data, pos, raw_pictureData.length ); listener.afterRecordSerialize(offset + getRecordSize(), getRecordId(), getRecordSize(), this); - return HEADER_SIZE + 16 + 1 + raw_pictureData.length; + return getRecordSize(); } /** @@ -161,7 +181,7 @@ public class EscherMetafileBlip } catch ( IOException e ) { - log.log(POILogger.INFO, "Possibly corrupt compression or non-compressed data", e); + log.log(POILogger.WARN, "Possibly corrupt compression or non-compressed data", e); return data; } } @@ -173,7 +193,11 @@ public class EscherMetafileBlip */ public int getRecordSize() { - return 8 + 50 + raw_pictureData.length; + int size = 8 + 50 + raw_pictureData.length; + if((getOptions() ^ getSignature()) == 0x10){ + size += field_2_UID.length; + } + return size; } public byte[] getUID() @@ -186,6 +210,16 @@ public class EscherMetafileBlip this.field_1_UID = field_1_UID; } + public byte[] getPrimaryUID() + { + return field_2_UID; + } + + public void setPrimaryUID( byte[] field_2_UID ) + { + this.field_2_UID = field_2_UID; + } + public int getUncompressedSize() { return field_2_cb; @@ -264,6 +298,7 @@ public class EscherMetafileBlip " RecordId: 0x" + HexDump.toHex( getRecordId() ) + nl + " Options: 0x" + HexDump.toHex( getOptions() ) + nl + " UID: 0x" + HexDump.toHex( field_1_UID ) + nl + + (field_2_UID == null ? "" : (" UID2: 0x" + HexDump.toHex( field_2_UID ) + nl)) + " Uncompressed Size: " + HexDump.toHex( field_2_cb ) + nl + " Bounds: " + getBounds() + nl + " Size in EMU: " + getSizeEMU() + nl + @@ -273,4 +308,19 @@ public class EscherMetafileBlip " Extra Data:" + nl + extraData; } + /** + * Return the blip signature + * + * @return the blip signature + */ + public short getSignature(){ + short sig = 0; + switch(getRecordId()){ + case RECORD_ID_EMF: sig = SIGNATURE_EMF; break; + case RECORD_ID_WMF: sig = SIGNATURE_WMF; break; + case RECORD_ID_PICT: sig = SIGNATURE_PICT; break; + default: log.log(POILogger.WARN, "Unknown metafile: " + getRecordId()); break; + } + return sig; + } } diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java index 05b4d80f4..f16ab4512 100644 --- a/src/java/org/apache/poi/hssf/model/FormulaParser.java +++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java @@ -380,12 +380,13 @@ public final class FormulaParser { } else { isVarArgs = !fm.hasFixedArgsLength(); funcIx = fm.getIndex(); + validateNumArgs(numArgs, fm); } AbstractFunctionPtg retval; if(isVarArgs) { retval = new FuncVarPtg(name, (byte)numArgs); } else { - retval = new FuncPtg(funcIx, (byte)numArgs); + retval = new FuncPtg(funcIx); } if (!name.equals(AbstractFunctionPtg.FUNCTION_NAME_IF)) { // early return for everything else besides IF() @@ -447,6 +448,29 @@ public final class FormulaParser { return retval; } + private void validateNumArgs(int numArgs, FunctionMetadata fm) { + if(numArgs < fm.getMinParams()) { + String msg = "Too few arguments to function '" + fm.getName() + "'. "; + if(fm.hasFixedArgsLength()) { + msg += "Expected " + fm.getMinParams(); + } else { + msg += "At least " + fm.getMinParams() + " were expected"; + } + msg += " but got " + numArgs + "."; + throw new FormulaParseException(msg); + } + if(numArgs > fm.getMaxParams()) { + String msg = "Too many arguments to function '" + fm.getName() + "'. "; + if(fm.hasFixedArgsLength()) { + msg += "Expected " + fm.getMaxParams(); + } else { + msg += "At most " + fm.getMaxParams() + " were expected"; + } + msg += " but got " + numArgs + "."; + throw new FormulaParseException(msg); + } + } + private static boolean isArgumentDelimiter(char ch) { return ch == ',' || ch == ')'; } diff --git a/src/java/org/apache/poi/hssf/model/Workbook.java b/src/java/org/apache/poi/hssf/model/Workbook.java index 8fa3010a4..08f226318 100644 --- a/src/java/org/apache/poi/hssf/model/Workbook.java +++ b/src/java/org/apache/poi/hssf/model/Workbook.java @@ -476,9 +476,9 @@ public class Workbook implements Model } /** - * Determines whether a workbook contains the privided sheet name. + * Determines whether a workbook contains the provided sheet name. * - * @param name the name to test + * @param name the name to test (case insensitive match) * @param excludeSheetIdx the sheet to exclude from the check or -1 to include all sheets in the check. * @return true if the sheet contains the name, false otherwise. */ @@ -487,7 +487,7 @@ public class Workbook implements Model for ( int i = 0; i < boundsheets.size(); i++ ) { BoundSheetRecord boundSheetRecord = (BoundSheetRecord) boundsheets.get( i ); - if (excludeSheetIdx != i && name.equals(boundSheetRecord.getSheetname())) + if (excludeSheetIdx != i && name.equalsIgnoreCase(boundSheetRecord.getSheetname())) return true; } return false; diff --git a/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java b/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java index 932c4547a..b77dca3e1 100644 --- a/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java +++ b/src/java/org/apache/poi/hssf/record/ColumnInfoRecord.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -15,13 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - -/* - * ColumnInfoRecord.java - * - * Created on December 8, 2001, 8:44 AM - */ package org.apache.poi.hssf.record; import org.apache.poi.util.LittleEndian; @@ -29,29 +22,28 @@ import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; /** - * Title: ColumnInfo Record

- * Description: Defines with width and formatting for a range of columns

- * REFERENCE: PG 293 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

+ * Title: COLINFO Record

+ * Description: Defines with width and formatting for a range of columns

+ * REFERENCE: PG 293 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

* @author Andrew C. Oliver (acoliver at apache dot org) * @version 2.0-pre */ - -public class ColumnInfoRecord - extends Record -{ +public final class ColumnInfoRecord extends Record { public static final short sid = 0x7d; private short field_1_first_col; private short field_2_last_col; private short field_3_col_width; private short field_4_xf_index; private short field_5_options; - static final private BitField hidden = BitFieldFactory.getInstance(0x01); - static final private BitField outlevel = BitFieldFactory.getInstance(0x0700); - static final private BitField collapsed = BitFieldFactory.getInstance(0x1000); + private static final BitField hidden = BitFieldFactory.getInstance(0x01); + private static final BitField outlevel = BitFieldFactory.getInstance(0x0700); + private static final BitField collapsed = BitFieldFactory.getInstance(0x1000); + // Excel seems write values 2, 10, and 260, even though spec says "must be zero" private short field_6_reserved; public ColumnInfoRecord() { + field_6_reserved = 2; // seems to be the most common value } /** @@ -71,7 +63,18 @@ public class ColumnInfoRecord field_3_col_width = in.readShort(); field_4_xf_index = in.readShort(); field_5_options = in.readShort(); - field_6_reserved = in.readShort(); + switch(in.remaining()) { + case 2: // usual case + field_6_reserved = in.readShort(); + break; + case 1: + // often COLINFO gets encoded 1 byte short + // shouldn't matter because this field is unused + field_6_reserved = in.readByte(); + break; + default: + throw new RuntimeException("Unusual record size remaining=(" + in.remaining() + ")"); + } } protected void validateSid(short id) diff --git a/src/java/org/apache/poi/hssf/record/FileSharingRecord.java b/src/java/org/apache/poi/hssf/record/FileSharingRecord.java index 2f56eb092..e375af365 100644 --- a/src/java/org/apache/poi/hssf/record/FileSharingRecord.java +++ b/src/java/org/apache/poi/hssf/record/FileSharingRecord.java @@ -14,31 +14,26 @@ 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.LittleEndian; -import org.apache.poi.util.POILogFactory; -import org.apache.poi.util.POILogger; import org.apache.poi.util.StringUtil; /** - * Title: FileSharing

+ * Title: FILESHARING

* Description: stores the encrypted readonly for a workbook (write protect) - * REFERENCE: PG 314 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

+ * This functionality is accessed from the options dialog box available when performing 'Save As'.

+ * REFERENCE: PG 314 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)

* @author Andrew C. Oliver (acoliver at apache dot org) */ +public final class FileSharingRecord extends Record { -public class FileSharingRecord extends Record { - private static POILogger logger = POILogFactory.getLogger(FileSharingRecord.class); - public final static short sid = 0x5b; private short field_1_readonly; private short field_2_password; - private byte field_3_username_length; - private short field_4_unknown; // not documented - private String field_5_username; + private byte field_3_username_unicode_options; + private String field_3_username_value; public FileSharingRecord() {} @@ -61,23 +56,15 @@ public class FileSharingRecord extends Record { protected void fillFields(RecordInputStream in) { field_1_readonly = in.readShort(); field_2_password = in.readShort(); - field_3_username_length = in.readByte(); - // Is this really correct? The latest docs - // seem to hint there's nothing between the - // username length and the username string - field_4_unknown = in.readShort(); + int nameLen = in.readShort(); - // Ensure we don't try to read more data than - // there actually is - if(field_3_username_length > in.remaining()) { - logger.log(POILogger.WARN, "FileSharingRecord defined a username of length " + field_3_username_length + ", but only " + in.remaining() + " bytes were left, truncating"); - field_3_username_length = (byte)in.remaining(); - } - if(field_3_username_length > 0) { - field_5_username = in.readCompressedUnicode(field_3_username_length); + if(nameLen > 0) { + // TODO - Current examples(3) from junits only have zero length username. + field_3_username_unicode_options = in.readByte(); + field_3_username_value = in.readCompressedUnicode(nameLen); } else { - field_5_username = ""; + field_3_username_value = ""; } } @@ -135,45 +122,24 @@ public class FileSharingRecord extends Record { /** * @returns byte representing the length of the username field */ - public byte getUsernameLength() { - return field_3_username_length ; - } - - /** - * @param byte representing the length of the username field - */ - public void setUsernameLength(byte length) { - this.field_3_username_length = length; + public short getUsernameLength() { + return (short) field_3_username_value.length(); } /** * @returns username of the user that created the file */ public String getUsername() { - return this.field_5_username; + return field_3_username_value; } /** * @param username of the user that created the file */ public void setUsername(String username) { - this.field_5_username = username; - this.field_3_username_length = (byte)username.length(); + field_3_username_value = username; } - /** - * @return short value of a "bonus field" in Excel that was not doc'd - */ - public short getUnknown() { - return field_4_unknown; - } - - /** - * @param unknown field value to set (bonus field that is not doc'd) - */ - public void setUnknown(short unk) { - field_4_unknown = unk; - } public String toString() { StringBuffer buffer = new StringBuffer(); @@ -183,10 +149,6 @@ public class FileSharingRecord extends Record { .append(getReadOnly() == 1 ? "true" : "false").append("\n"); buffer.append(" .password = ") .append(Integer.toHexString(getPassword())).append("\n"); - buffer.append(" .userlen = ") - .append(Integer.toHexString(getUsernameLength())).append("\n"); - buffer.append(" .unknown = ") - .append(Integer.toHexString(getUnknown())).append("\n"); buffer.append(" .username = ") .append(getUsername()).append("\n"); buffer.append("[/FILESHARING]\n"); @@ -194,18 +156,25 @@ public class FileSharingRecord extends Record { } public int serialize(int offset, byte [] data) { + // TODO - junit LittleEndian.putShort(data, 0 + offset, sid); LittleEndian.putShort(data, 2 + offset, (short)(getRecordSize()-4)); LittleEndian.putShort(data, 4 + offset, getReadOnly()); LittleEndian.putShort(data, 6 + offset, getPassword()); - data[ 8 + offset ] = getUsernameLength(); - LittleEndian.putShort(data, 9 + offset, getUnknown()); - StringUtil.putCompressedUnicode( getUsername(), data, 11 + offset ); + LittleEndian.putShort(data, 8 + offset, getUsernameLength()); + if(getUsernameLength() > 0) { + LittleEndian.putByte(data, 10 + offset, field_3_username_unicode_options); + StringUtil.putCompressedUnicode( getUsername(), data, 11 + offset ); + } return getRecordSize(); } public int getRecordSize() { - return 11+getUsernameLength(); + short nameLen = getUsernameLength(); + if (nameLen < 1) { + return 10; + } + return 11+nameLen; } public short getSid() { @@ -219,10 +188,7 @@ public class FileSharingRecord extends Record { FileSharingRecord clone = new FileSharingRecord(); clone.setReadOnly(field_1_readonly); clone.setPassword(field_2_password); - clone.setUsernameLength(field_3_username_length); - clone.setUnknown(field_4_unknown); - clone.setUsername(field_5_username); + clone.setUsername(field_3_username_value); return clone; } - } diff --git a/src/java/org/apache/poi/hssf/record/FormulaRecord.java b/src/java/org/apache/poi/hssf/record/FormulaRecord.java index c405f901b..c20f5e6d3 100644 --- a/src/java/org/apache/poi/hssf/record/FormulaRecord.java +++ b/src/java/org/apache/poi/hssf/record/FormulaRecord.java @@ -40,7 +40,7 @@ import org.apache.poi.util.LittleEndian; * @version 2.0-pre */ -public class FormulaRecord +public final class FormulaRecord extends Record implements CellValueRecordInterface, Comparable { @@ -108,6 +108,11 @@ public class FormulaRecord } catch (java.lang.UnsupportedOperationException uoe) { throw new RecordFormatException(uoe); } + if (in.remaining() == 10) { + // TODO - this seems to occur when IntersectionPtg is present + // 10 extra bytes are just 0x01 and 0x00 + // This causes POI stderr: "WARN. Unread 10 bytes of record 0x6" + } } //public void setRow(short row) diff --git a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java index a7715474a..2b0c50d12 100755 --- a/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java +++ b/src/java/org/apache/poi/hssf/record/SharedFormulaRecord.java @@ -33,7 +33,7 @@ import org.apache.poi.hssf.record.formula.*; * @author Danny Mui at apache dot org */ public final class SharedFormulaRecord extends Record { - public final static short sid = 0x4BC; + public final static short sid = 0x4BC; private int field_1_first_row; private int field_2_last_row; @@ -186,6 +186,16 @@ public final class SharedFormulaRecord extends Record { * counter part */ protected static Stack convertSharedFormulas(Stack ptgs, int formulaRow, int formulaColumn) { + if(false) { + /* + * TODO - (May-2008) Stop converting relative ref Ptgs in shared formula records. + * If/when POI writes out the workbook, this conversion makes an unnecessary diff in the BIFF records. + * Disabling this code breaks one existing junit. + * Some fix-up will be required to make Ptg.toFormulaString(HSSFWorkbook) work properly. + * That method will need 2 extra params: rowIx and colIx. + */ + return ptgs; + } Stack newPtgStack = new Stack(); if (ptgs != null) @@ -265,7 +275,7 @@ public final class SharedFormulaRecord extends Record { throw new RuntimeException("Shared Formula Conversion: Coding Error"); } } - + private static int fixupRelativeColumn(int currentcolumn, int column, boolean relative) { if(relative) { // mask out upper bits to produce 'wrapping' at column 256 ("IV") diff --git a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java index 82d85cceb..48d7d4cc5 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java @@ -147,10 +147,12 @@ public abstract class AbstractFunctionPtg extends OperationPtg { } public byte getParameterClass(int index) { - try { - return paramClass[index]; - } catch (ArrayIndexOutOfBoundsException aioobe) { + if (index >= paramClass.length) { + // For var-arg (and other?) functions, the metadata does not list all the parameter + // operand classes. In these cases, all extra parameters are assumed to have the + // same operand class as the last one specified. return paramClass[paramClass.length - 1]; } + return paramClass[index]; } } diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaNAPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaNAPtg.java index 6b0eb908a..bf3ebce87 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaNAPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaNAPtg.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -16,19 +15,9 @@ limitations under the License. ==================================================================== */ -/* - * AreaPtg.java - * - * Created on November 17, 2001, 9:30 PM - */ package org.apache.poi.hssf.record.formula; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.util.BitField; - import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.hssf.util.AreaReference; -import org.apache.poi.hssf.util.CellReference; import org.apache.poi.hssf.usermodel.HSSFWorkbook; /** @@ -36,8 +25,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook; * @author Jason Height (jheight at chariot dot net dot au) */ -public class AreaNAPtg - extends AreaPtg +public final class AreaNAPtg extends AreaPtg { public final static short sid = 0x6D; @@ -50,20 +38,16 @@ public class AreaNAPtg super(in); } - public void writeBytes(byte [] array, int offset) { - throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); - } - public String getAreaPtgName() { return "AreaNAPtg"; } public String toFormulaString(HSSFWorkbook book) { - throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); + throw notImplemented(); } public Object clone() { - throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); + throw notImplemented(); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java index 71e413fea..b5e1ca490 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaNPtg.java @@ -36,8 +36,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook; * @author Jason Height (jheight at chariot dot net dot au) */ -public class AreaNPtg - extends AreaPtg +public final class AreaNPtg extends AreaPtg { public final static short sid = 0x2D; @@ -50,23 +49,16 @@ public class AreaNPtg super(in); } - public void writeBytes(byte [] array, int offset) { - super.writeBytes(array,offset); - //this should be a warning...there doesn't seem to be any rationale to throwing an exception here... - //this excpeiton appears to break user defined named ranges... - //throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); - } - public String getAreaPtgName() { return "AreaNPtg"; } public String toFormulaString(HSSFWorkbook book) { - throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); + throw notImplemented(); } public Object clone() { - throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); + throw notImplemented(); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaNVPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaNVPtg.java index 2ebf01982..d5f0c38cc 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaNVPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaNVPtg.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -16,19 +15,9 @@ limitations under the License. ==================================================================== */ -/* - * AreaPtg.java - * - * Created on November 17, 2001, 9:30 PM - */ package org.apache.poi.hssf.record.formula; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.util.BitField; - import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.hssf.util.AreaReference; -import org.apache.poi.hssf.util.CellReference; import org.apache.poi.hssf.usermodel.HSSFWorkbook; /** @@ -36,10 +25,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook; * @author andy * @author Jason Height (jheight at chariot dot net dot au) */ - -public class AreaNVPtg - extends AreaPtg -{ +public final class AreaNVPtg extends AreaPtg { public final static short sid = 0x4D; protected AreaNVPtg() { @@ -51,20 +37,16 @@ public class AreaNVPtg super(in); } - public void writeBytes(byte [] array, int offset) { - throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); - } - public String getAreaPtgName() { return "AreaNVPtg"; } public String toFormulaString(HSSFWorkbook book) { - throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); + throw notImplemented(); } public Object clone() { - throw new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); + throw notImplemented(); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java index 27e4d1759..90be1974f 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaPtg.java @@ -15,7 +15,6 @@ limitations under the License. ==================================================================== */ - package org.apache.poi.hssf.record.formula; import org.apache.poi.util.LittleEndian; @@ -32,10 +31,15 @@ import org.apache.poi.hssf.record.RecordInputStream; * @author andy * @author Jason Height (jheight at chariot dot net dot au) */ +public class AreaPtg extends Ptg implements AreaI { + /** + * TODO - (May-2008) fix subclasses of AreaPtg 'AreaN~' which are used in shared formulas. + * see similar comment in ReferencePtg + */ + protected final RuntimeException notImplemented() { + return new RuntimeException("Coding Error: This method should never be called. This ptg should be converted"); + } -public class AreaPtg - extends Ptg implements AreaI -{ public final static short sid = 0x25; private final static int SIZE = 9; /** zero based, unsigned 16 bit */ diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java index 69edf88aa..7901fb675 100644 --- a/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/FuncPtg.java @@ -57,10 +57,12 @@ public final class FuncPtg extends AbstractFunctionPtg { } numParams = fm.getMinParams(); } - public FuncPtg(int functionIndex, int numberOfParameters) { + public FuncPtg(int functionIndex) { field_2_fnc_index = (short) functionIndex; - numParams = numberOfParameters; - paramClass = new byte[] { Ptg.CLASS_VALUE, }; // TODO + FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByIndex(functionIndex); + numParams = fm.getMinParams(); // same as max since these are not var-arg funcs + returnClass = fm.getReturnClassCode(); + paramClass = fm.getParameterClassCodes(); } public void writeBytes(byte[] array, int offset) { diff --git a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java index fb6527139..431dc5717 100644 --- a/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/FuncVarPtg.java @@ -54,8 +54,8 @@ public final class FuncVarPtg extends AbstractFunctionPtg{ returnClass = Ptg.CLASS_VALUE; paramClass = new byte[] {Ptg.CLASS_VALUE}; } else { - returnClass = Ptg.CLASS_VALUE; - paramClass = new byte[] {Ptg.CLASS_VALUE}; + returnClass = fm.getReturnClassCode(); + paramClass = fm.getParameterClassCodes(); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/Ptg.java b/src/java/org/apache/poi/hssf/record/formula/Ptg.java index 0e8c7741a..fe8702c4a 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Ptg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Ptg.java @@ -30,24 +30,23 @@ import org.apache.poi.hssf.record.RecordInputStream; * @author avik * @author Jason Height (jheight at chariot dot net dot au) */ - public abstract class Ptg { - + /* convert infix order ptg list to rpn order ptg list * @return List ptgs in RPN order * @param infixPtgs List of ptgs in infix order */ - + /* DO NOT REMOVE *we keep this method in case we wish to change the way we parse *It needs a getPrecedence in OperationsPtg - + public static List ptgsToRpn(List infixPtgs) { java.util.Stack operands = new java.util.Stack(); java.util.List retval = new java.util.Stack(); - + java.util.ListIterator i = infixPtgs.listIterator(); Object p; OperationPtg o ; @@ -61,13 +60,13 @@ public abstract class Ptg weHaveABracket = true; } else { o = (OperationPtg) operands.pop(); - while (!(o instanceof ParenthesisPtg)) { + while (!(o instanceof ParenthesisPtg)) { retval.add(o); } weHaveABracket = false; } } else { - + while (!operands.isEmpty() && ((OperationPtg) operands.peek()).getPrecedence() >= ((OperationPtg) p).getPrecedence() ) { //TODO handle ^ since it is right associative retval.add(operands.pop()); } @@ -82,12 +81,16 @@ public abstract class Ptg //throw some error } else { retval.add(operands.pop()); - } + } } return retval; } */ + /** + * Reads size bytes of the input stream, to create an array of Ptgs. + * Extra data (beyond size) may be read if and ArrayPtgs are present. + */ public static Stack createParsedExpressionTokens(short size, RecordInputStream in ) { Stack stack = new Stack(); @@ -97,22 +100,25 @@ public abstract class Ptg { Ptg ptg = Ptg.createPtg( in ); if (ptg instanceof ArrayPtg) { - if (arrayPtgs == null) - arrayPtgs = new ArrayList(5); - arrayPtgs.add(ptg); - pos += 8; + if (arrayPtgs == null) + arrayPtgs = new ArrayList(5); + arrayPtgs.add(ptg); + pos += 8; } else pos += ptg.getSize(); stack.push( ptg ); } + if(pos != size) { + throw new RuntimeException("Ptg array size mismatch"); + } if (arrayPtgs != null) { - for (int i=0;i 0x60) { retval.setClass(CLASS_ARRAY); } else if (id > 0x40) { @@ -371,35 +377,35 @@ public abstract class Ptg } return retval; - - } - - public static int serializePtgStack(Stack expression, byte[] array, int offset) { - int pos = 0; - int size = 0; - if (expression != null) - size = expression.size(); - List arrayPtgs = null; - - for (int k = 0; k < size; k++) { - Ptg ptg = ( Ptg ) expression.get(k); - - ptg.writeBytes(array, pos + offset); - if (ptg instanceof ArrayPtg) { - if (arrayPtgs == null) - arrayPtgs = new ArrayList(5); - arrayPtgs.add(ptg); - pos += 8; - } else pos += ptg.getSize(); - } - if (arrayPtgs != null) { - for (int i=0;iFunctionMetadataRegistry * @@ -36,6 +38,12 @@ final class FunctionMetadataReader { private static final String METADATA_FILE_NAME = "functionMetadata.txt"; private static final Pattern TAB_DELIM_PATTERN = Pattern.compile("\t"); + private static final Pattern SPACE_DELIM_PATTERN = Pattern.compile(" "); + private static final byte[] EMPTY_BYTE_ARRAY = { }; + + // special characters from the ooo document + private static final int CHAR_ELLIPSIS_8230 = 8230; + private static final int CHAR_NDASH_8211 = 8211; private static final String[] DIGIT_ENDING_FUNCTION_NAMES = { // Digits at the end of a function might be due to a left-over footnote marker. @@ -86,14 +94,66 @@ final class FunctionMetadataReader { String functionName = parts[1]; int minParams = parseInt(parts[2]); int maxParams = parseInt(parts[3]); - // 4 returnClass - // 5 parameterClasses + byte returnClassCode = parseReturnTypeCode(parts[4]); + byte[] parameterClassCodes = parseOperandTypeCodes(parts[5]); // 6 isVolatile boolean hasNote = parts[7].length() > 0; validateFunctionName(functionName); - // TODO - make POI use returnClass, parameterClasses, isVolatile - fdb.add(functionIndex, functionName, minParams, maxParams, hasNote); + // TODO - make POI use isVolatile + fdb.add(functionIndex, functionName, minParams, maxParams, + returnClassCode, parameterClassCodes, hasNote); + } + + + private static byte parseReturnTypeCode(String code) { + if(code.length() == 0) { + return Ptg.CLASS_REF; // happens for GETPIVOTDATA + } + return parseOperandTypeCode(code); + } + + private static byte[] parseOperandTypeCodes(String codes) { + if(codes.length() < 1) { + return EMPTY_BYTE_ARRAY; // happens for GETPIVOTDATA + } + if(isDash(codes)) { + // '-' means empty: + return EMPTY_BYTE_ARRAY; + } + String[] array = SPACE_DELIM_PATTERN.split(codes); + int nItems = array.length; + if(array[nItems-1].charAt(0) == CHAR_ELLIPSIS_8230) { + nItems --; + } + byte[] result = new byte[nItems]; + for (int i = 0; i < nItems; i++) { + result[i] = parseOperandTypeCode(array[i]); + } + return result; + } + + private static boolean isDash(String codes) { + if(codes.length() == 1) { + switch (codes.charAt(0)) { + case '-': + case CHAR_NDASH_8211: // this is what the ooo doc has + return true; + } + } + return false; + } + + private static byte parseOperandTypeCode(String code) { + if(code.length() != 1) { + throw new RuntimeException("Bad operand type code format '" + code + "' expected single char"); + } + switch(code.charAt(0)) { + case 'V': return Ptg.CLASS_VALUE; + case 'R': return Ptg.CLASS_REF; + case 'A': return Ptg.CLASS_ARRAY; + } + throw new IllegalArgumentException("Unexpected operand type code '" + code + "'"); } /** diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFPictureData.java b/src/java/org/apache/poi/hssf/usermodel/HSSFPictureData.java index 439100a53..487c277f3 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFPictureData.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFPictureData.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel; import org.apache.poi.ddf.EscherBitmapBlip; import org.apache.poi.ddf.EscherBlipRecord; +import org.apache.poi.ddf.EscherMetafileBlip; /** * Represents binary data stored in the file. Eg. A GIF, JPEG etc... @@ -69,19 +70,19 @@ public class HSSFPictureData */ public String suggestFileExtension() { - switch (blip.getOptions() & FORMAT_MASK) + switch (blip.getRecordId()) { - case MSOBI_WMF: + case EscherMetafileBlip.RECORD_ID_WMF: return "wmf"; - case MSOBI_EMF: + case EscherMetafileBlip.RECORD_ID_EMF: return "emf"; - case MSOBI_PICT: + case EscherMetafileBlip.RECORD_ID_PICT: return "pict"; - case MSOBI_PNG: + case EscherBitmapBlip.RECORD_ID_PNG: return "png"; - case MSOBI_JPEG: + case EscherBitmapBlip.RECORD_ID_JPEG: return "jpeg"; - case MSOBI_DIB: + case EscherBitmapBlip.RECORD_ID_DIB: return "dib"; default: return ""; diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java index b6d8d5723..b56122688 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java @@ -1710,6 +1710,8 @@ public final class HSSFSheet { HSSFCellStyle style = cell.getCellStyle(); HSSFFont font = wb.getFontAt(style.getFontIndex()); + //the number of spaces to indent the text in the cell + int indention = style.getIndention(); if (cell.getCellType() == HSSFCell.CELL_TYPE_STRING) { HSSFRichTextString rt = cell.getRichStringCellValue(); @@ -1742,9 +1744,9 @@ public final class HSSFSheet { trans.concatenate( AffineTransform.getScaleInstance(1, fontHeightMultiple) ); - width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth); + width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth + indention); } else { - width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth); + width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth + indention); } } } else { @@ -1787,15 +1789,15 @@ public final class HSSFSheet { trans.concatenate( AffineTransform.getScaleInstance(1, fontHeightMultiple) ); - width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth); + width = Math.max(width, layout.getOutline(trans).getBounds().getWidth() / defaultCharWidth + indention); } else { - width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth); + width = Math.max(width, layout.getBounds().getWidth() / defaultCharWidth + indention); } } } if (width != -1) { - if (width > Short.MAX_VALUE) { //width can be bigger that Short.MAX_VALUE! + if (width > Short.MAX_VALUE) { //calculated width can be greater that Short.MAX_VALUE! width = Short.MAX_VALUE; } sheet.setColumnWidth(column, (short) (width * 256)); diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java index 30776608b..78fe26013 100644 --- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java @@ -165,9 +165,45 @@ public class HSSFWorkbook extends POIDocument public HSSFWorkbook(POIFSFileSystem fs, boolean preserveNodes) throws IOException { - this(fs.getRoot(), fs, preserveNodes); + this(fs.getRoot(), fs, preserveNodes); } - + + /** + * Normally, the Workbook will be in a POIFS Stream + * called "Workbook". However, some weird XLS generators use "WORKBOOK" + */ + private static final String[] WORKBOOK_DIR_ENTRY_NAMES = { + "Workbook", // as per BIFF8 spec + "WORKBOOK", + }; + + + private static String getWorkbookDirEntryName(DirectoryNode directory) { + + String[] potentialNames = WORKBOOK_DIR_ENTRY_NAMES; + for (int i = 0; i < potentialNames.length; i++) { + String wbName = potentialNames[i]; + try { + directory.getEntry(wbName); + return wbName; + } catch (FileNotFoundException e) { + // continue - to try other options + } + } + + // check for previous version of file format + try { + directory.getEntry("Book"); + throw new IllegalArgumentException("The supplied spreadsheet seems to be Excel 5.0/7.0 (BIFF5) format. " + + "POI only supports BIFF8 format (from Excel versions 97/2000/XP/2003)"); + } catch (FileNotFoundException e) { + // fall through + } + + throw new IllegalArgumentException("The supplied POIFSFileSystem does not contain a BIFF8 'Workbook' entry. " + + "Is it really an excel file?"); + } + /** * given a POI POIFSFileSystem object, and a specific directory * within it, read in its Workbook and populate the high and @@ -185,9 +221,11 @@ public class HSSFWorkbook extends POIDocument public HSSFWorkbook(DirectoryNode directory, POIFSFileSystem fs, boolean preserveNodes) throws IOException { - super(directory, fs); + super(directory, fs); + String workbookName = getWorkbookDirEntryName(directory); + this.preserveNodes = preserveNodes; - + // If we're not preserving nodes, don't track the // POIFS any more if(! preserveNodes) { @@ -197,28 +235,9 @@ public class HSSFWorkbook extends POIDocument sheets = new ArrayList(INITIAL_CAPACITY); names = new ArrayList(INITIAL_CAPACITY); - - // Normally, the Workbook will be in a POIFS Stream - // called "Workbook". However, some wierd XLS generators - // put theirs in one called "WORKBOOK" - String workbookName = "Workbook"; - try { - directory.getEntry(workbookName); - // Is the default name - } catch(FileNotFoundException fe) { - // Try the upper case form - try { - workbookName = "WORKBOOK"; - directory.getEntry(workbookName); - } catch(FileNotFoundException wfe) { - // Doesn't contain it in either form - throw new IllegalArgumentException("The supplied POIFSFileSystem contained neither a 'Workbook' entry, nor a 'WORKBOOK' entry. Is it really an excel file?"); - } - } - // Grab the data from the workbook stream, however - // it happens to be spelt. + // it happens to be spelled. InputStream stream = directory.createDocumentInputStream(workbookName); EventRecordFactory factory = new EventRecordFactory(); @@ -231,7 +250,7 @@ public class HSSFWorkbook extends POIDocument int sheetNum = 0; // convert all LabelRecord records to LabelSSTRecord - convertLabelRecords(records, recOffset); + convertLabelRecords(records, recOffset); while (recOffset < records.size()) { Sheet sheet = Sheet.createSheet(records, sheetNum++, recOffset ); @@ -288,7 +307,7 @@ public class HSSFWorkbook extends POIDocument // none currently } - + /** * This is basically a kludge to deal with the now obsolete Label records. If * you have to read in a sheet that contains Label records, be aware that the rest @@ -304,7 +323,7 @@ public class HSSFWorkbook extends POIDocument * @see org.apache.poi.hssf.record.LabelSSTRecord * @see org.apache.poi.hssf.record.SSTRecord */ - + private void convertLabelRecords(List records, int offset) { if (log.check( POILogger.DEBUG )) @@ -332,7 +351,7 @@ public class HSSFWorkbook extends POIDocument if (log.check( POILogger.DEBUG )) log.log(POILogger.DEBUG, "convertLabelRecords exit"); } - + /** * sets the order of appearance for a given sheet. @@ -345,7 +364,7 @@ public class HSSFWorkbook extends POIDocument sheets.add(pos,sheets.remove(getSheetIndex(sheetname))); workbook.setSheetOrder(sheetname, pos); } - + /** * sets the tab whose data is actually seen when the sheet is opened. * This may be different from the "selected sheet" since excel seems to @@ -357,7 +376,7 @@ public class HSSFWorkbook extends POIDocument public void setSelectedTab(short index) { workbook.getWindowOne().setSelectedTab(index); } - + /** * gets the tab whose data is actually seen when the sheet is opened. * This may be different from the "selected sheet" since excel seems to @@ -368,7 +387,7 @@ public class HSSFWorkbook extends POIDocument public short getSelectedTab() { return workbook.getWindowOne().getSelectedTab(); } - + /** * sets the first tab that is displayed in the list of tabs * in excel. @@ -377,7 +396,7 @@ public class HSSFWorkbook extends POIDocument public void setDisplayedTab(short index) { workbook.getWindowOne().setDisplayedTab(index); } - + /** * sets the first tab that is displayed in the list of tabs * in excel. @@ -399,7 +418,7 @@ public class HSSFWorkbook extends POIDocument /** - * set the sheet name. + * set the sheet name. * Will throw IllegalArgumentException if the name is greater than 31 chars * or contains /\?*[] * @param sheet number (0 based) @@ -413,19 +432,19 @@ public class HSSFWorkbook extends POIDocument { throw new RuntimeException("Sheet out of bounds"); } - + workbook.setSheetName( sheet, name); } - + /** * set the sheet name forcing the encoding. Forcing the encoding IS A BAD IDEA!!! * @deprecated 3-Jan-2006 POI now automatically detects unicode and sets the encoding - * appropriately. Simply use setSheetName(int sheet, String encoding) + * appropriately. Simply use setSheetName(int sheet, String encoding) * @throws IllegalArgumentException if the name is greater than 31 chars * or contains /\?*[] * @param sheet number (0 based) - */ + */ public void setSheetName( int sheet, String name, short encoding ) { if (workbook.doesContainsSheetName( name, sheet )) @@ -480,7 +499,7 @@ public class HSSFWorkbook extends POIDocument /** * Hide or unhide a sheet - * + * * @param sheetnum The sheet number * @param hidden True to mark the sheet as hidden, false otherwise */ @@ -492,7 +511,7 @@ public class HSSFWorkbook extends POIDocument } workbook.setSheetHidden(sheet,hidden); } - + /* * get the sheet's index * @param name sheet name @@ -516,23 +535,23 @@ public class HSSFWorkbook extends POIDocument */ public int getSheetIndex(HSSFSheet sheet) { - for(int i=0; inull if it does not exist */ public HSSFSheet getSheet(String name) @@ -652,16 +673,16 @@ public class HSSFWorkbook extends POIDocument { String sheetname = workbook.getSheetName(k); - if (sheetname.equals(name)) + if (sheetname.equalsIgnoreCase(name)) { retval = (HSSFSheet) sheets.get(k); } } return retval; } - + public SheetReferences getSheetReferences() { - return workbook.getSheetReferences(); + return workbook.getSheetReferences(); } /** @@ -839,7 +860,7 @@ public class HSSFWorkbook extends POIDocument { return index; } - } + } index++; } @@ -989,21 +1010,21 @@ public class HSSFWorkbook extends POIDocument // For tracking what we've written out, used if we're // going to be preserving nodes List excepts = new ArrayList(1); - + // Write out the Workbook stream fs.createDocument(new ByteArrayInputStream(bytes), "Workbook"); - + // Write out our HPFS properties, if we have them writeProperties(fs, excepts); 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 WORKBOOK instead of Workbook, we'll write it - // out correctly shortly, so don't include the old one + // If the file had WORKBOOK instead of Workbook, we'll write it + // out correctly shortly, so don't include the old one excepts.add("WORKBOOK"); - // Copy over all the other nodes to our new poifs + // Copy over all the other nodes to our new poifs copyNodes(this.filesystem,fs,excepts); } fs.writeFilesystem(stream); @@ -1117,7 +1138,7 @@ public class HSSFWorkbook extends POIDocument return result; } - + /** * TODO - make this less cryptic / move elsewhere * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table @@ -1125,76 +1146,76 @@ public class HSSFWorkbook extends POIDocument * @return the string representation of the defined or external name */ public String resolveNameXText(int refIndex, int definedNameIndex) { - return workbook.resolveNameXText(refIndex, definedNameIndex); + return workbook.resolveNameXText(refIndex, definedNameIndex); } - /** - * Sets the printarea for the sheet provided - *

- * i.e. Reference = $A$1:$B$2 - * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java) - * @param reference Valid name Reference for the Print Area - */ - public void setPrintArea(int sheetIndex, String reference) - { - NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); + /** + * Sets the printarea for the sheet provided + *

+ * i.e. Reference = $A$1:$B$2 + * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java) + * @param reference Valid name Reference for the Print Area + */ + public void setPrintArea(int sheetIndex, String reference) + { + NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); - if (name == null) - name = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); + if (name == null) + name = workbook.createBuiltInName(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); //adding one here because 0 indicates a global named region; doesnt make sense for print areas - short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex); - name.setExternSheetNumber(externSheetIndex); - name.setAreaReference(reference); + short externSheetIndex = getWorkbook().checkExternSheet(sheetIndex); + name.setExternSheetNumber(externSheetIndex); + name.setAreaReference(reference); - } + } - /** - * For the Convenience of Java Programmers maintaining pointers. - * @see #setPrintArea(int, String) - * @param sheetIndex Zero-based sheet index (0 = First Sheet) - * @param startColumn Column to begin printarea - * @param endColumn Column to end the printarea - * @param startRow Row to begin the printarea - * @param endRow Row to end the printarea - */ - public void setPrintArea(int sheetIndex, int startColumn, int endColumn, - int startRow, int endRow) { + /** + * For the Convenience of Java Programmers maintaining pointers. + * @see #setPrintArea(int, String) + * @param sheetIndex Zero-based sheet index (0 = First Sheet) + * @param startColumn Column to begin printarea + * @param endColumn Column to end the printarea + * @param startRow Row to begin the printarea + * @param endRow Row to end the printarea + */ + public void setPrintArea(int sheetIndex, int startColumn, int endColumn, + int startRow, int endRow) { - //using absolute references because they don't get copied and pasted anyway - CellReference cell = new CellReference(startRow, startColumn, true, true); - String reference = cell.formatAsString(); + //using absolute references because they don't get copied and pasted anyway + CellReference cell = new CellReference(startRow, startColumn, true, true); + String reference = cell.formatAsString(); - cell = new CellReference(endRow, endColumn, true, true); - reference = reference+":"+cell.formatAsString(); + cell = new CellReference(endRow, endColumn, true, true); + reference = reference+":"+cell.formatAsString(); - setPrintArea(sheetIndex, reference); - } + setPrintArea(sheetIndex, reference); + } - /** - * Retrieves the reference for the printarea of the specified sheet, the sheet name is appended to the reference even if it was not specified. - * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java) - * @return String Null if no print area has been defined - */ - public String getPrintArea(int sheetIndex) - { - NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); - if (name == null) return null; - //adding one here because 0 indicates a global named region; doesnt make sense for print areas + /** + * Retrieves the reference for the printarea of the specified sheet, the sheet name is appended to the reference even if it was not specified. + * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java) + * @return String Null if no print area has been defined + */ + public String getPrintArea(int sheetIndex) + { + NameRecord name = workbook.getSpecificBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); + if (name == null) return null; + //adding one here because 0 indicates a global named region; doesnt make sense for print areas - return name.getAreaReference(this); - } + return name.getAreaReference(this); + } /** * Delete the printarea for the sheet specified * @param sheetIndex Zero-based sheet index (0 = First Sheet) */ public void removePrintArea(int sheetIndex) { - getWorkbook().removeBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); + getWorkbook().removeBuiltinRecord(NameRecord.BUILTIN_PRINT_AREA, sheetIndex+1); } /** creates a new named range and add it to the model @@ -1213,7 +1234,7 @@ public class HSSFWorkbook extends POIDocument /** gets the named range index by his name * Note:Excel named ranges are case-insensitive and * this method performs a case-insensitive search. - * + * * @param name named range name * @return named range index */ @@ -1250,9 +1271,9 @@ public class HSSFWorkbook extends POIDocument * @see org.apache.poi.hssf.record.Record */ public HSSFDataFormat createDataFormat() { - if (formatter == null) - formatter = new HSSFDataFormat(workbook); - return formatter; + if (formatter == null) + formatter = new HSSFDataFormat(workbook); + return formatter; } /** remove the named range by his name @@ -1431,9 +1452,9 @@ public class HSSFWorkbook extends POIDocument * Is the workbook protected with a password (not encrypted)? */ public boolean isWriteProtected() { - return this.workbook.isWriteProtected(); + return this.workbook.isWriteProtected(); } - + /** * protect a workbook with a password (not encypted, just sets writeprotect * flags and the password. diff --git a/src/scratchpad/ooxml-src/org/apache/poi/POIXMLDocument.java b/src/scratchpad/ooxml-src/org/apache/poi/POIXMLDocument.java deleted file mode 100644 index 86b0d557b..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/POIXMLDocument.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ==================================================================== - 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; - -import org.apache.poi.hxf.HXFDocument; - -/** - * Parent class of all UserModel POI XML (ooxml) - * implementations. - * Provides a similar function to {@link POIDocument}, - * for the XML based classes. - */ -public abstract class POIXMLDocument { - private HXFDocument document; - - /** - * Creates a new POI XML Document, wrapping up - * the underlying raw HXFDocument - */ - protected POIXMLDocument(HXFDocument document) { - this.document = document; - } - - /** - * Returns the underlying HXFDocument, typically - * used for unit testing - */ - public HXFDocument _getHXFDocument() { - return document; - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/POIXMLTextExtractor.java b/src/scratchpad/ooxml-src/org/apache/poi/POIXMLTextExtractor.java deleted file mode 100644 index c28eba49d..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/POIXMLTextExtractor.java +++ /dev/null @@ -1,31 +0,0 @@ -/* ==================================================================== - 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; - -public abstract class POIXMLTextExtractor extends POITextExtractor { - /** The POIXMLDocument that's open */ - protected POIXMLDocument document; - - /** - * Creates a new text extractor for the given document - */ - public POIXMLTextExtractor(POIXMLDocument document) { - super(null); - - this.document = document; - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hslf/HSLFXML.java b/src/scratchpad/ooxml-src/org/apache/poi/hslf/HSLFXML.java deleted file mode 100644 index 568cb80aa..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hslf/HSLFXML.java +++ /dev/null @@ -1,148 +0,0 @@ -/* ==================================================================== - 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.hslf; - -import java.io.IOException; - -import org.apache.poi.hxf.HXFDocument; -import org.apache.xmlbeans.XmlException; -import org.openxml4j.exceptions.InvalidFormatException; -import org.openxml4j.exceptions.OpenXML4JException; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; -import org.openxml4j.opc.PackageRelationshipCollection; -import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesSlide; -import org.openxmlformats.schemas.presentationml.x2006.main.CTPresentation; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdList; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterIdList; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterIdListEntry; -import org.openxmlformats.schemas.presentationml.x2006.main.NotesDocument; -import org.openxmlformats.schemas.presentationml.x2006.main.PresentationDocument; -import org.openxmlformats.schemas.presentationml.x2006.main.SldDocument; -import org.openxmlformats.schemas.presentationml.x2006.main.SldMasterDocument; - -/** - * Experimental class to do low level processing - * of pptx files. - * - * If you are using these low level classes, then you - * will almost certainly need to refer to the OOXML - * specifications from - * http://www.ecma-international.org/publications/standards/Ecma-376.htm - * - * WARNING - APIs expected to change rapidly - */ -public class HSLFXML extends HXFDocument { - public static final String MAIN_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"; - public static final String NOTES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"; - public static final String SLIDE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.slide+xml"; - public static final String SLIDE_LAYOUT_RELATION_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout"; - public static final String NOTES_RELATION_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide"; - - private PresentationDocument presentationDoc; - - public HSLFXML(Package container) throws OpenXML4JException, IOException, XmlException { - super(container, MAIN_CONTENT_TYPE); - - presentationDoc = - PresentationDocument.Factory.parse(basePart.getInputStream()); - } - - /** - * Returns the low level presentation base object - */ - public CTPresentation getPresentation() { - return presentationDoc.getPresentation(); - } - - /** - * Returns the references from the presentation to its - * slides. - * You'll need these to figure out the slide ordering, - * and to get at the actual slides themselves - */ - public CTSlideIdList getSlideReferences() { - return getPresentation().getSldIdLst(); - } - /** - * Returns the references from the presentation to its - * slide masters. - * You'll need these to get at the actual slide - * masters themselves - */ - public CTSlideMasterIdList getSlideMasterReferences() { - return getPresentation().getSldMasterIdLst(); - } - - /** - * Returns the low level slide master object from - * the supplied slide master reference - */ - public CTSlideMaster getSlideMaster(CTSlideMasterIdListEntry master) throws IOException, XmlException { - PackagePart masterPart = - getRelatedPackagePart(master.getId2()); - SldMasterDocument masterDoc = - SldMasterDocument.Factory.parse(masterPart.getInputStream()); - return masterDoc.getSldMaster(); - } - - /** - * Returns the low level slide object from - * the supplied slide reference - */ - public CTSlide getSlide(CTSlideIdListEntry slide) throws IOException, XmlException { - PackagePart slidePart = - getRelatedPackagePart(slide.getId2()); - SldDocument slideDoc = - SldDocument.Factory.parse(slidePart.getInputStream()); - return slideDoc.getSld(); - } - - /** - * Returns the low level notes object for the given - * slide, as found from the supplied slide reference - */ - public CTNotesSlide getNotes(CTSlideIdListEntry slide) throws IOException, XmlException { - PackagePart slidePart = - getRelatedPackagePart(slide.getId2()); - - PackageRelationshipCollection notes; - try { - notes = slidePart.getRelationshipsByType(NOTES_RELATION_TYPE); - } catch(InvalidFormatException e) { - throw new IllegalStateException(e); - } - - if(notes.size() == 0) { - // No notes for this slide - return null; - } - if(notes.size() > 1) { - throw new IllegalStateException("Expecting 0 or 1 notes for a slide, but found " + notes.size()); - } - - PackagePart notesPart = - getPackagePart(notes.getRelationship(0)); - NotesDocument notesDoc = - NotesDocument.Factory.parse(notesPart.getInputStream()); - - return notesDoc.getNotes(); - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hslf/extractor/HXFPowerPointExtractor.java b/src/scratchpad/ooxml-src/org/apache/poi/hslf/extractor/HXFPowerPointExtractor.java deleted file mode 100644 index 1d4b1a2bd..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hslf/extractor/HXFPowerPointExtractor.java +++ /dev/null @@ -1,139 +0,0 @@ -/* ==================================================================== - 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.hslf.extractor; - -import java.io.File; -import java.io.IOException; - -import org.apache.poi.POIXMLTextExtractor; -import org.apache.poi.hslf.HSLFXML; -import org.apache.poi.hslf.usermodel.HSLFXMLSlideShow; -import org.apache.poi.hxf.HXFDocument; -import org.apache.xmlbeans.XmlException; -import org.openxml4j.exceptions.OpenXML4JException; -import org.openxml4j.opc.Package; -import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; -import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; -import org.openxmlformats.schemas.presentationml.x2006.main.CTNotesSlide; -import org.openxmlformats.schemas.presentationml.x2006.main.CTShape; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry; - -public class HXFPowerPointExtractor extends POIXMLTextExtractor { - private HSLFXMLSlideShow slideshow; - private boolean slidesByDefault = true; - private boolean notesByDefault = false; - - public HXFPowerPointExtractor(Package container) throws XmlException, OpenXML4JException, IOException { - this(new HSLFXMLSlideShow( - new HSLFXML(container) - )); - } - public HXFPowerPointExtractor(HSLFXMLSlideShow slideshow) { - super(slideshow); - this.slideshow = slideshow; - } - - public static void main(String[] args) throws Exception { - if(args.length < 1) { - System.err.println("Use:"); - System.err.println(" HXFPowerPointExtractor "); - System.exit(1); - } - POIXMLTextExtractor extractor = - new HXFPowerPointExtractor(HXFDocument.openPackage( - new File(args[0]) - )); - System.out.println(extractor.getText()); - } - - /** - * Should a call to getText() return slide text? - * Default is yes - */ - public void setSlidesByDefault(boolean slidesByDefault) { - this.slidesByDefault = slidesByDefault; - } - /** - * Should a call to getText() return notes text? - * Default is no - */ - public void setNotesByDefault(boolean notesByDefault) { - this.notesByDefault = notesByDefault; - } - - /** - * Gets the slide text, but not the notes text - */ - public String getText() { - return getText(slidesByDefault, notesByDefault); - } - - /** - * Gets the requested text from the file - * @param slideText Should we retrieve text from slides? - * @param notesText Should we retrieve text from notes? - */ - public String getText(boolean slideText, boolean notesText) { - StringBuffer text = new StringBuffer(); - - CTSlideIdListEntry[] slideRefs = - slideshow._getHSLFXML().getSlideReferences().getSldIdArray(); - for (int i = 0; i < slideRefs.length; i++) { - try { - CTSlide slide = - slideshow._getHSLFXML().getSlide(slideRefs[i]); - CTNotesSlide notes = - slideshow._getHSLFXML().getNotes(slideRefs[i]); - - if(slideText) { - extractText(slide.getCSld().getSpTree(), text); - } - if(notesText && notes != null) { - extractText(notes.getCSld().getSpTree(), text); - } - } catch(Exception e) { - throw new RuntimeException(e); - } - } - - return text.toString(); - } - - private void extractText(CTGroupShape gs, StringBuffer text) { - CTShape[] shapes = gs.getSpArray(); - for (int i = 0; i < shapes.length; i++) { - CTTextBody textBody = - shapes[i].getTxBody(); - if(textBody != null) { - CTTextParagraph[] paras = - textBody.getPArray(); - for (int j = 0; j < paras.length; j++) { - CTRegularTextRun[] textRuns = - paras[j].getRArray(); - for (int k = 0; k < textRuns.length; k++) { - text.append( textRuns[k].getT() ); - } - // End each paragraph with a new line - text.append("\n"); - } - } - } - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hslf/usermodel/HSLFXMLSlideShow.java b/src/scratchpad/ooxml-src/org/apache/poi/hslf/usermodel/HSLFXMLSlideShow.java deleted file mode 100644 index b8a5fcde3..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hslf/usermodel/HSLFXMLSlideShow.java +++ /dev/null @@ -1,39 +0,0 @@ -/* ==================================================================== - 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.hslf.usermodel; - -import org.apache.poi.POIXMLDocument; -import org.apache.poi.hslf.HSLFXML; - -/** - * High level representation of a ooxml slideshow. - * This is the first object most users will construct whether - * they are reading or writing a slideshow. It is also the - * top level object for creating new slides/etc. - */ -public class HSLFXMLSlideShow extends POIXMLDocument { - private org.apache.poi.hslf.HSLFXML hslfXML; - - public HSLFXMLSlideShow(HSLFXML xml) { - super(xml); - this.hslfXML = xml; - } - - public HSLFXML _getHSLFXML() { - return hslfXML; - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hssf/HSSFXML.java b/src/scratchpad/ooxml-src/org/apache/poi/hssf/HSSFXML.java deleted file mode 100644 index 3766a046a..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hssf/HSSFXML.java +++ /dev/null @@ -1,104 +0,0 @@ -/* ==================================================================== - 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; - -import java.io.IOException; - -import org.apache.poi.hssf.model.SharedStringsTable; -import org.apache.poi.hxf.HXFDocument; -import org.apache.xmlbeans.XmlException; -import org.openxml4j.exceptions.OpenXML4JException; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheets; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument; - -/** - * Experimental class to do low level processing - * of xlsx files. - * - * If you are using these low level classes, then you - * will almost certainly need to refer to the OOXML - * specifications from - * http://www.ecma-international.org/publications/standards/Ecma-376.htm - * - * WARNING - APIs expected to change rapidly - */ -public class HSSFXML extends HXFDocument { - public static final String MAIN_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"; - public static final String SHEET_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"; - public static final String SHARED_STRINGS_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"; - public static final String SHARED_STRINGS_RELATION_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"; - - private WorkbookDocument workbookDoc; - private SharedStringsTable sharedStrings; - - public HSSFXML(Package container) throws OpenXML4JException, IOException, XmlException { - super(container, MAIN_CONTENT_TYPE); - - workbookDoc = - WorkbookDocument.Factory.parse(basePart.getInputStream()); - - PackagePart ssPart = getSinglePartByRelationType(SHARED_STRINGS_RELATION_TYPE, basePart); - if (ssPart != null) { - sharedStrings = new SharedStringsTable(ssPart); - } else { - - } - } - - /** - * Returns the low level workbook base object - */ - public CTWorkbook getWorkbook() { - return workbookDoc.getWorkbook(); - } - /** - * Returns the references from the workbook to its - * sheets. - * You'll need these to figure out the sheet ordering, - * and to get at the actual sheets themselves - */ - public CTSheets getSheetReferences() { - return getWorkbook().getSheets(); - } - /** - * Returns the low level (work)sheet object from - * the supplied sheet reference - */ - public CTWorksheet getSheet(CTSheet sheet) throws IOException, XmlException { - PackagePart sheetPart = - getRelatedPackagePart(sheet.getId()); - WorksheetDocument sheetDoc = - WorksheetDocument.Factory.parse(sheetPart.getInputStream()); - return sheetDoc.getWorksheet(); - } - - /** - * Returns the shared string at the given index - */ - public String getSharedString(int index) { - return this.sharedStrings.get(index); - } - protected SharedStringsTable _getSharedStringsTable() { - return sharedStrings; - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hssf/extractor/HXFExcelExtractor.java b/src/scratchpad/ooxml-src/org/apache/poi/hssf/extractor/HXFExcelExtractor.java deleted file mode 100644 index 34ae06800..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hssf/extractor/HXFExcelExtractor.java +++ /dev/null @@ -1,133 +0,0 @@ -/* ==================================================================== - 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.extractor; - -import java.io.File; -import java.io.IOException; - -import org.apache.poi.POIXMLTextExtractor; -import org.apache.poi.hssf.HSSFXML; -import org.apache.poi.hssf.usermodel.HSSFXMLCell; -import org.apache.poi.hssf.usermodel.HSSFXMLWorkbook; -import org.apache.poi.hxf.HXFDocument; -import org.apache.xmlbeans.XmlException; -import org.openxml4j.exceptions.OpenXML4JException; -import org.openxml4j.opc.Package; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; - -/** - * Helper class to extract text from an OOXML Excel file - */ -public class HXFExcelExtractor extends POIXMLTextExtractor { - private HSSFXMLWorkbook workbook; - private boolean includeSheetNames = true; - private boolean formulasNotResults = false; - - public HXFExcelExtractor(Package container) throws XmlException, OpenXML4JException, IOException { - this(new HSSFXMLWorkbook( - new HSSFXML(container) - )); - } - public HXFExcelExtractor(HSSFXMLWorkbook workbook) { - super(workbook); - this.workbook = workbook; - } - - public static void main(String[] args) throws Exception { - if(args.length < 1) { - System.err.println("Use:"); - System.err.println(" HXFExcelExtractor "); - System.exit(1); - } - POIXMLTextExtractor extractor = - new HXFExcelExtractor(HXFDocument.openPackage( - new File(args[0]) - )); - System.out.println(extractor.getText()); - } - - /** - * Should sheet names be included? Default is true - */ - public void setIncludeSheetNames(boolean includeSheetNames) { - this.includeSheetNames = includeSheetNames; - } - /** - * Should we return the formula itself, and not - * the result it produces? Default is false - */ - public void setFormulasNotResults(boolean formulasNotResults) { - this.formulasNotResults = formulasNotResults; - } - - /** - * Retreives the text contents of the file - */ - public String getText() { - StringBuffer text = new StringBuffer(); - - CTSheet[] sheetRefs = - workbook._getHSSFXML().getSheetReferences().getSheetArray(); - for(int i=0; i 0) { - text.append("\n"); - } - if(includeSheetNames) { - text.append(sheetRefs[i].getName() + "\n"); - } - - for(int j=0; j 0) { - text.append("\t"); - } - - boolean done = false; - - // Is it a formula one? - if(cell.getF() != null) { - if(formulasNotResults) { - text.append(cell.getF().getStringValue()); - done = true; - } - } - if(!done) { - HSSFXMLCell uCell = new HSSFXMLCell(cell, workbook); - text.append(uCell.getStringValue()); - } - } - text.append("\n"); - } - } catch(Exception e) { - throw new RuntimeException(e); - } - } - - return text.toString(); - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hssf/model/SharedStringsTable.java b/src/scratchpad/ooxml-src/org/apache/poi/hssf/model/SharedStringsTable.java deleted file mode 100644 index b3e219256..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hssf/model/SharedStringsTable.java +++ /dev/null @@ -1,78 +0,0 @@ -/* ==================================================================== - 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.model; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.LinkedList; - -import org.apache.xmlbeans.XmlException; -import org.openxml4j.opc.PackagePart; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSst; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.SstDocument; - - -public class SharedStringsTable extends LinkedList { - public static final String MAIN_SML_NS_URI = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; - - private SstDocument doc; - private PackagePart part; - - public SharedStringsTable(PackagePart part) throws IOException, XmlException { - this.part = part; - doc = SstDocument.Factory.parse( - part.getInputStream() - ); - read(); - } - - private void read() { - CTRst[] sts = doc.getSst().getSiArray(); - for (int i = 0; i < sts.length; i++) { - add(sts[i].getT()); - } - } - - /** - * Writes the current shared strings table into - * the associated OOXML PackagePart - */ - public void write() throws IOException { - CTSst sst = doc.getSst(); - - // Remove the old list - for(int i=sst.sizeOfSiArray() - 1; i>=0; i--) { - sst.removeSi(i); - } - - // Add the new one - for(String s : this) { - sst.addNewSi().setT(s); - } - - // Update the counts - sst.setCount(this.size()); - sst.setUniqueCount(this.size()); - - // Write out - OutputStream out = part.getOutputStream(); - doc.save(out); - out.close(); - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hssf/usermodel/HSSFXMLCell.java b/src/scratchpad/ooxml-src/org/apache/poi/hssf/usermodel/HSSFXMLCell.java deleted file mode 100644 index b24556cd8..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hssf/usermodel/HSSFXMLCell.java +++ /dev/null @@ -1,58 +0,0 @@ -/* ==================================================================== - 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.usermodel; - -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType; - -/** - * User facing wrapper around an underlying cell object - */ -public class HSSFXMLCell { - private CTCell cell; - - /** The workbook to which this cell belongs */ - private final HSSFXMLWorkbook workbook; - - public HSSFXMLCell(CTCell rawCell, HSSFXMLWorkbook workbook) { - this.cell = rawCell; - this.workbook = workbook; - } - - /** - * Formats the cell's contents, based on its type, - * and returns it as a string. - */ - public String getStringValue() { - - switch (cell.getT().intValue()) { - case STCellType.INT_S: - return this.workbook.getSharedString(Integer.valueOf(cell.getV())); - case STCellType.INT_INLINE_STR: - return cell.getV(); - case STCellType.INT_N: - return cell.getV(); - // TODO: support other types - default: - return "UNSUPPORTED CELL TYPE: '" + cell.getT() + "'"; - } - } - - public String toString() { - return cell.getR() + " - " + getStringValue(); - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hssf/usermodel/HSSFXMLWorkbook.java b/src/scratchpad/ooxml-src/org/apache/poi/hssf/usermodel/HSSFXMLWorkbook.java deleted file mode 100644 index 023b80f4d..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hssf/usermodel/HSSFXMLWorkbook.java +++ /dev/null @@ -1,43 +0,0 @@ -/* ==================================================================== - 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.usermodel; - -import org.apache.poi.POIXMLDocument; -import org.apache.poi.hssf.HSSFXML; - -/** - * High level representation of a ooxml workbook. - * This is the first object most users will construct whether - * they are reading or writing a workbook. It is also the - * top level object for creating new sheets/etc. - */ -public class HSSFXMLWorkbook extends POIXMLDocument { - private HSSFXML hssfXML; - - public HSSFXMLWorkbook(HSSFXML xml) { - super(xml); - this.hssfXML = xml; - } - - public HSSFXML _getHSSFXML() { - return hssfXML; - } - - public String getSharedString(int index) { - return hssfXML.getSharedString(index); - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hwpf/HWPFXML.java b/src/scratchpad/ooxml-src/org/apache/poi/hwpf/HWPFXML.java deleted file mode 100644 index 66bba7ee1..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hwpf/HWPFXML.java +++ /dev/null @@ -1,92 +0,0 @@ -/* ==================================================================== - 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.hwpf; - -import java.io.IOException; - -import org.apache.poi.hxf.HXFDocument; -import org.apache.xmlbeans.XmlException; -import org.openxml4j.exceptions.InvalidFormatException; -import org.openxml4j.exceptions.OpenXML4JException; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTDocument1; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTStyles; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.DocumentDocument; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.StylesDocument; - -/** - * Experimental class to do low level processing - * of docx files. - * - * If you are using these low level classes, then you - * will almost certainly need to refer to the OOXML - * specifications from - * http://www.ecma-international.org/publications/standards/Ecma-376.htm - * - * WARNING - APIs expected to change rapidly - */ -public class HWPFXML extends HXFDocument { - public static final String MAIN_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"; - public static final String FOOTER_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"; - public static final String HEADER_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"; - public static final String STYLES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"; - public static final String STYLES_RELATION_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"; - - private DocumentDocument wordDoc; - - public HWPFXML(Package container) throws OpenXML4JException, IOException, XmlException { - super(container, MAIN_CONTENT_TYPE); - - wordDoc = - DocumentDocument.Factory.parse(basePart.getInputStream()); - } - - /** - * Returns the low level document base object - */ - public CTDocument1 getDocument() { - return wordDoc.getDocument(); - } - - /** - * Returns the low level body of the document - */ - public CTBody getDocumentBody() { - return getDocument().getBody(); - } - - /** - * Returns the styles object used - */ - public CTStyles getStyle() throws XmlException, IOException { - PackagePart[] parts; - try { - parts = getRelatedByType(STYLES_RELATION_TYPE); - } catch(InvalidFormatException e) { - throw new IllegalStateException(e); - } - if(parts.length != 1) { - throw new IllegalStateException("Expecting one Styles document part, but found " + parts.length); - } - - StylesDocument sd = - StylesDocument.Factory.parse(parts[0].getInputStream()); - return sd.getStyles(); - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hwpf/extractor/HXFWordExtractor.java b/src/scratchpad/ooxml-src/org/apache/poi/hwpf/extractor/HXFWordExtractor.java deleted file mode 100644 index a4427e49e..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hwpf/extractor/HXFWordExtractor.java +++ /dev/null @@ -1,87 +0,0 @@ -/* ==================================================================== - 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.hwpf.extractor; - -import java.io.File; -import java.io.IOException; - -import org.apache.poi.POIXMLTextExtractor; -import org.apache.poi.hwpf.HWPFXML; -import org.apache.poi.hwpf.usermodel.HWPFXMLDocument; -import org.apache.poi.hxf.HXFDocument; -import org.apache.xmlbeans.XmlException; -import org.openxml4j.exceptions.OpenXML4JException; -import org.openxml4j.opc.Package; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBody; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTP; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTR; -import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTText; - -/** - * Helper class to extract text from an OOXML Word file - */ -public class HXFWordExtractor extends POIXMLTextExtractor { - private HWPFXMLDocument document; - - public HXFWordExtractor(Package container) throws XmlException, OpenXML4JException, IOException { - this(new HWPFXMLDocument( - new HWPFXML(container) - )); - } - public HXFWordExtractor(HWPFXMLDocument document) { - super(document); - this.document = document; - } - - public static void main(String[] args) throws Exception { - if(args.length < 1) { - System.err.println("Use:"); - System.err.println(" HXFWordExtractor "); - System.exit(1); - } - POIXMLTextExtractor extractor = - new HXFWordExtractor(HXFDocument.openPackage( - new File(args[0]) - )); - System.out.println(extractor.getText()); - } - - public String getText() { - CTBody body = document._getHWPFXML().getDocumentBody(); - StringBuffer text = new StringBuffer(); - - // Loop over paragraphs - CTP[] ps = body.getPArray(); - for (int i = 0; i < ps.length; i++) { - // Loop over ranges - CTR[] rs = ps[i].getRArray(); - for (int j = 0; j < rs.length; j++) { - // Loop over text runs - CTText[] texts = rs[j].getTArray(); - for (int k = 0; k < texts.length; k++) { - text.append( - texts[k].getStringValue() - ); - } - } - // New line after each paragraph. - text.append("\n"); - } - - return text.toString(); - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hwpf/usermodel/HWPFXMLDocument.java b/src/scratchpad/ooxml-src/org/apache/poi/hwpf/usermodel/HWPFXMLDocument.java deleted file mode 100644 index 64597e83d..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hwpf/usermodel/HWPFXMLDocument.java +++ /dev/null @@ -1,36 +0,0 @@ -/* ==================================================================== - 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.hwpf.usermodel; - -import org.apache.poi.POIXMLDocument; -import org.apache.poi.hwpf.HWPFXML; - -/** - * High level representation of a ooxml text document. - */ -public class HWPFXMLDocument extends POIXMLDocument { - private HWPFXML hwpfXML; - - public HWPFXMLDocument(HWPFXML xml) { - super(xml); - this.hwpfXML = xml; - } - - public HWPFXML _getHWPFXML() { - return hwpfXML; - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hxf/HXFDocument.java b/src/scratchpad/ooxml-src/org/apache/poi/hxf/HXFDocument.java deleted file mode 100644 index 9849a7d21..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hxf/HXFDocument.java +++ /dev/null @@ -1,272 +0,0 @@ -/* ==================================================================== - 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.hxf; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.PushbackInputStream; -import java.util.ArrayList; - -import org.apache.poi.POIXMLDocument; -import org.apache.poi.poifs.common.POIFSConstants; -import org.apache.poi.poifs.storage.HeaderBlockConstants; -import org.apache.poi.util.IOUtils; -import org.apache.poi.util.LongField; -import org.apache.xmlbeans.XmlException; -import org.dom4j.Document; -import org.dom4j.DocumentException; -import org.dom4j.io.SAXReader; -import org.openxml4j.exceptions.InvalidFormatException; -import org.openxml4j.exceptions.OpenXML4JException; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackageAccess; -import org.openxml4j.opc.PackagePart; -import org.openxml4j.opc.PackagePartName; -import org.openxml4j.opc.PackageRelationship; -import org.openxml4j.opc.PackageRelationshipCollection; -import org.openxml4j.opc.PackagingURIHelper; -import org.openxml4j.opc.internal.PackagePropertiesPart; -import org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.CTProperties; -import org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument; - -/** - * Parent class of the low level interface to - * all POI XML (OOXML) implementations. - * Normal users should probably deal with things that - * extends {@link POIXMLDocument}, unless they really - * do need to get low level access to the files. - * - * If you are using these low level classes, then you - * will almost certainly need to refer to the OOXML - * specifications from - * http://www.ecma-international.org/publications/standards/Ecma-376.htm - * - * WARNING - APIs expected to change rapidly - */ -public abstract class HXFDocument { - public static final String CORE_PROPERTIES_REL_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; - public static final String EXTENDED_PROPERTIES_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties"; - - /** - * File package/container. - */ - protected Package container; - /** - * The Package Part for our base document - */ - protected PackagePart basePart; - /** - * The base document of this instance, eg Workbook for - * xslsx - */ - protected Document baseDocument; - - protected HXFDocument(Package container, String baseContentType) throws OpenXML4JException { - this.container = container; - - // Find the base document - basePart = getSinglePartByType(baseContentType); - - // And load it up - try { - SAXReader reader = new SAXReader(); - baseDocument = reader.read(basePart.getInputStream()); - } catch (DocumentException e) { - throw new OpenXML4JException(e.getMessage()); - } catch (IOException ioe) { - throw new OpenXML4JException(ioe.getMessage()); - } - } - - /** - * Checks that the supplied InputStream (which MUST - * support mark and reset, or be a PushbackInputStream) - * has a OOXML (zip) header at the start of it. - * If your InputStream does not support mark / reset, - * then wrap it in a PushBackInputStream, then be - * sure to always use that, and not the original! - * @param inp An InputStream which supports either mark/reset, or is a PushbackInputStream - */ - public static boolean hasOOXMLHeader(InputStream inp) throws IOException { - // We want to peek at the first 4 bytes - inp.mark(4); - - byte[] header = new byte[4]; - IOUtils.readFully(inp, header); - - // Wind back those 4 bytes - if(inp instanceof PushbackInputStream) { - PushbackInputStream pin = (PushbackInputStream)inp; - pin.unread(header); - } else { - inp.reset(); - } - - // Did it match the ooxml zip signature? - return ( - header[0] == POIFSConstants.OOXML_FILE_HEADER[0] && - header[1] == POIFSConstants.OOXML_FILE_HEADER[1] && - header[2] == POIFSConstants.OOXML_FILE_HEADER[2] && - header[3] == POIFSConstants.OOXML_FILE_HEADER[3] - ); - } - - /** - * Fetches the (single) PackagePart with the supplied - * content type. - * @param contentType The content type to search for - * @throws IllegalArgumentException If we don't find a single part of that type - */ - private PackagePart getSinglePartByType(String contentType) throws IllegalArgumentException { - ArrayList parts = - container.getPartsByContentType(contentType); - if(parts.size() != 1) { - throw new IllegalArgumentException("Expecting one entry with content type of " + contentType + ", but found " + parts.size()); - } - return parts.get(0); - } - - /** - * Fetches the (single) PackagePart which is defined as - * the supplied relation content type of the specified part, - * or null if none found. - * @param relationType The relation content type to search for - * @throws IllegalArgumentException If we find more than one part of that type - * TODO: this sucks! Make Package and PackagePart implement common intf that defines getRelationshipsByType & friends - */ - protected PackagePart getSinglePartByRelationType(String relationType, PackagePart part) throws IllegalArgumentException, OpenXML4JException { - PackageRelationshipCollection rels = - part.getRelationshipsByType(relationType); - if(rels.size() == 0) { - return null; - } - if(rels.size() > 1) { - throw new IllegalArgumentException("Found " + rels.size() + " relations for the type " + relationType + ", should only ever be one!"); - } - PackageRelationship rel = rels.getRelationship(0); - return getPackagePart(rel); - } - - /** - * Fetches the (single) PackagePart which is defined as - * the supplied relation content type of the base - * container, or null if none found. - * @param relationType The relation content type to search for - * @throws IllegalArgumentException If we find more than one part of that type - */ - protected PackagePart getSinglePartByRelationType(String relationType) throws IllegalArgumentException, OpenXML4JException { - PackageRelationshipCollection rels = - container.getRelationshipsByType(relationType); - if(rels.size() == 0) { - return null; - } - if(rels.size() > 1) { - throw new IllegalArgumentException("Found " + rels.size() + " relations for the type " + relationType + ", should only ever be one!"); - } - PackageRelationship rel = rels.getRelationship(0); - return getPackagePart(rel); - } - - /** - * Retrieves the PackagePart for the given relation - * id. This will normally come from a r:id attribute - * on part of the base document. - * @param partId The r:id pointing to the other PackagePart - */ - protected PackagePart getRelatedPackagePart(String partId) { - PackageRelationship rel = - basePart.getRelationship(partId); - return getPackagePart(rel); - } - - /** - * Retrieves the PackagePart for the given Relationship - * object. Normally you'll want to go via a content type - * or r:id to get one of those. - */ - protected PackagePart getPackagePart(PackageRelationship rel) { - PackagePartName relName; - try { - relName = PackagingURIHelper.createPartName(rel.getTargetURI()); - } catch(InvalidFormatException e) { - throw new InternalError(e.getMessage()); - } - - PackagePart part = container.getPart(relName); - if(part == null) { - throw new IllegalArgumentException("No part found for rel " + rel); - } - return part; - } - - /** - * Retrieves all the PackageParts which are defined as - * relationships of the base document with the - * specified content type. - */ - protected PackagePart[] getRelatedByType(String contentType) throws InvalidFormatException { - PackageRelationshipCollection partsC = - basePart.getRelationshipsByType(contentType); - - PackagePart[] parts = new PackagePart[partsC.size()]; - int count = 0; - for (PackageRelationship rel : partsC) { - parts[count] = getPackagePart(rel); - count++; - } - return parts; - } - - /** - * Get the package container. - * @return The package associated to this document. - */ - public Package getPackage() { - return container; - } - - /** - * Get the core document properties (core ooxml properties). - */ - public PackagePropertiesPart getCoreProperties() throws OpenXML4JException, XmlException, IOException { - PackagePart propsPart = getSinglePartByRelationType(CORE_PROPERTIES_REL_TYPE); - if(propsPart == null) { - return null; - } - return (PackagePropertiesPart)propsPart; - } - - /** - * Get the extended document properties (extended ooxml properties) - */ - public CTProperties getExtendedProperties() throws OpenXML4JException, XmlException, IOException { - PackagePart propsPart = getSinglePartByRelationType(EXTENDED_PROPERTIES_REL_TYPE); - - PropertiesDocument props = PropertiesDocument.Factory.parse( - propsPart.getInputStream()); - return props.getProperties(); - } - - /** - * Returns an opened OOXML Package for the supplied File - * @param f File to open - */ - public static Package openPackage(File f) throws InvalidFormatException { - return Package.open(f.toString(), PackageAccess.READ_WRITE); - } -} diff --git a/src/scratchpad/ooxml-src/org/apache/poi/hxf/dev/HXFLister.java b/src/scratchpad/ooxml-src/org/apache/poi/hxf/dev/HXFLister.java deleted file mode 100644 index 032b74b6f..000000000 --- a/src/scratchpad/ooxml-src/org/apache/poi/hxf/dev/HXFLister.java +++ /dev/null @@ -1,133 +0,0 @@ -/* ==================================================================== - 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.hxf.dev; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; -import java.util.ArrayList; - -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackageAccess; -import org.openxml4j.opc.PackagePart; -import org.openxml4j.opc.PackageRelationship; -import org.openxml4j.opc.PackageRelationshipCollection; - -/** - * Prints out the contents of a HXF (ooxml) container. - * Useful for seeing what parts are defined, and how - * they're all related to each other. - */ -public class HXFLister { - private Package container; - private PrintStream disp; - - public HXFLister(Package container) { - this(container, System.out); - } - public HXFLister(Package container, PrintStream disp) { - this.container = container; - this.disp = disp; - } - - /** - * Figures out how big a given PackagePart is. - */ - public static long getSize(PackagePart part) throws IOException { - InputStream in = part.getInputStream(); - byte[] b = new byte[8192]; - long size = 0; - int read = 0; - - while(read > -1) { - read = in.read(b); - if(read > 0) { - size += read; - } - } - - return size; - } - - /** - * Displays information on all the different - * parts of the OOXML file container. - */ - public void displayParts() throws Exception { - ArrayList parts = container.getParts(); - for (PackagePart part : parts) { - disp.println(part.getPartName()); - disp.println("\t" + part.getContentType()); - - if(! part.getPartName().toString().equals("/docProps/core.xml")) { - disp.println("\t" + getSize(part) + " bytes"); - } - - if(! part.isRelationshipPart()) { - disp.println("\t" + part.getRelationships().size() + " relations"); - for(PackageRelationship rel : part.getRelationships()) { - displayRelation(rel, "\t "); - } - } - } - } - /** - * Displays information on all the different - * relationships between different parts - * of the OOXML file container. - */ - public void displayRelations() throws Exception { - PackageRelationshipCollection rels = - container.getRelationships(); - for (PackageRelationship rel : rels) { - displayRelation(rel, ""); - } - } - private void displayRelation(PackageRelationship rel, String indent) { - disp.println(indent+"Relationship:"); - disp.println(indent+"\tFrom: "+ rel.getSourceURI()); - disp.println(indent+"\tTo: " + rel.getTargetURI()); - disp.println(indent+"\tID: " + rel.getId()); - disp.println(indent+"\tMode: " + rel.getTargetMode()); - disp.println(indent+"\tType: " + rel.getRelationshipType()); - } - - public static void main(String[] args) throws Exception { - if(args.length == 0) { - System.err.println("Use:"); - System.err.println("\tjava HXFLister "); - System.exit(1); - } - - File f = new File(args[0]); - if(! f.exists()) { - System.err.println("Error, file not found!"); - System.err.println("\t" + f.toString()); - System.exit(2); - } - - HXFLister lister = new HXFLister( - Package.open(f.toString(), PackageAccess.READ) - ); - - lister.disp.println(f.toString() + "\n"); - lister.displayParts(); - lister.disp.println(); - lister.displayRelations(); - } -} diff --git a/src/scratchpad/ooxml-testcases/org/apache/poi/hslf/TestHSLFXML.java b/src/scratchpad/ooxml-testcases/org/apache/poi/hslf/TestHSLFXML.java deleted file mode 100644 index fd4653a85..000000000 --- a/src/scratchpad/ooxml-testcases/org/apache/poi/hslf/TestHSLFXML.java +++ /dev/null @@ -1,127 +0,0 @@ -/* ==================================================================== - 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.hslf; - -import java.io.File; - -import org.apache.poi.hxf.HXFDocument; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdListEntry; -import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterIdListEntry; - -import junit.framework.TestCase; - -public class TestHSLFXML extends TestCase { - private File sampleFile; - - protected void setUp() throws Exception { - super.setUp(); - - sampleFile = new File( - System.getProperty("HSLF.testdata.path") + - File.separator + "sample.pptx" - ); - } - - public void testContainsMainContentType() throws Exception { - Package pack = HXFDocument.openPackage(sampleFile); - - boolean found = false; - for(PackagePart part : pack.getParts()) { - if(part.getContentType().equals(HSLFXML.MAIN_CONTENT_TYPE)) { - found = true; - } - System.out.println(part); - } - assertTrue(found); - } - - public void testOpen() throws Exception { - HXFDocument.openPackage(sampleFile); - - HSLFXML xml; - - // With the finalised uri, should be fine - xml = new HSLFXML( - HXFDocument.openPackage(sampleFile) - ); - - // Check the core - assertNotNull(xml.getPresentation()); - - // Check it has some slides - assertTrue( - xml.getSlideReferences().sizeOfSldIdArray() > 0 - ); - assertTrue( - xml.getSlideMasterReferences().sizeOfSldMasterIdArray() > 0 - ); - } - - public void testSlideBasics() throws Exception { - HSLFXML xml = new HSLFXML( - HXFDocument.openPackage(sampleFile) - ); - - // Should have 1 master - assertEquals(1, xml.getSlideMasterReferences().sizeOfSldMasterIdArray()); - assertEquals(1, xml.getSlideMasterReferences().getSldMasterIdArray().length); - - // Should have three sheets - assertEquals(2, xml.getSlideReferences().sizeOfSldIdArray()); - assertEquals(2, xml.getSlideReferences().getSldIdArray().length); - - // Check they're as expected - CTSlideIdListEntry[] slides = xml.getSlideReferences().getSldIdArray(); - assertEquals(256, slides[0].getId()); - assertEquals(257, slides[1].getId()); - assertEquals("rId2", slides[0].getId2()); - assertEquals("rId3", slides[1].getId2()); - - // Now get those objects - assertNotNull(xml.getSlide(slides[0])); - assertNotNull(xml.getSlide(slides[1])); - - // And check they have notes as expected - assertNotNull(xml.getNotes(slides[0])); - assertNotNull(xml.getNotes(slides[1])); - - // And again for the master - CTSlideMasterIdListEntry[] masters = - xml.getSlideMasterReferences().getSldMasterIdArray(); - assertEquals(2147483648l, masters[0].getId()); - assertEquals("rId1", masters[0].getId2()); - assertNotNull(xml.getSlideMaster(masters[0])); - } - - public void testMetadataBasics() throws Exception { - HSLFXML xml = new HSLFXML( - HXFDocument.openPackage(sampleFile) - ); - - assertNotNull(xml.getCoreProperties()); - assertNotNull(xml.getExtendedProperties()); - - assertEquals("Microsoft Office PowerPoint", xml.getExtendedProperties().getApplication()); - assertEquals(0, xml.getExtendedProperties().getCharacters()); - assertEquals(0, xml.getExtendedProperties().getLines()); - - assertEquals(null, xml.getCoreProperties().getTitleProperty().getValue()); - assertEquals(null, xml.getCoreProperties().getSubjectProperty().getValue()); - } -} diff --git a/src/scratchpad/ooxml-testcases/org/apache/poi/hslf/extractor/TestHXFPowerPointExtractor.java b/src/scratchpad/ooxml-testcases/org/apache/poi/hslf/extractor/TestHXFPowerPointExtractor.java deleted file mode 100644 index 6a006ab5c..000000000 --- a/src/scratchpad/ooxml-testcases/org/apache/poi/hslf/extractor/TestHXFPowerPointExtractor.java +++ /dev/null @@ -1,109 +0,0 @@ -/* ==================================================================== - 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.hslf.extractor; - -import java.io.File; - -import org.apache.poi.hslf.HSLFXML; -import org.apache.poi.hslf.usermodel.HSLFXMLSlideShow; -import org.apache.poi.hxf.HXFDocument; - -import junit.framework.TestCase; - -/** - * Tests for HXFPowerPointExtractor - */ -public class TestHXFPowerPointExtractor extends TestCase { - /** - * A simple file - */ - private HSLFXML xmlA; - - protected void setUp() throws Exception { - super.setUp(); - - File fileA = new File( - System.getProperty("HSLF.testdata.path") + - File.separator + "sample.pptx" - ); - - xmlA = new HSLFXML(HXFDocument.openPackage(fileA)); - } - - /** - * Get text out of the simple file - */ - public void testGetSimpleText() throws Exception { - new HXFPowerPointExtractor(xmlA.getPackage()); - new HXFPowerPointExtractor(new HSLFXMLSlideShow(xmlA)); - - HXFPowerPointExtractor extractor = - new HXFPowerPointExtractor(xmlA.getPackage()); - extractor.getText(); - - String text = extractor.getText(); - assertTrue(text.length() > 0); - - // Check Basics - assertTrue(text.startsWith("Lorem ipsum dolor sit amet\n")); - assertTrue(text.endsWith("amet\n\n")); - - // Just slides, no notes - text = extractor.getText(true, false); - assertEquals( - "Lorem ipsum dolor sit amet\n" + - "Nunc at risus vel erat tempus posuere. Aenean non ante.\n" + - "\n" + - "Lorem ipsum dolor sit amet\n" + - "Lorem\n" + - "ipsum\n" + - "dolor\n" + - "sit\n" + - "amet\n" + - "\n", text - ); - - // Just notes, no slides - text = extractor.getText(false, true); - assertEquals( - "\n\n\n\n", text - ); - - // Both - text = extractor.getText(true, true); - assertEquals( - "Lorem ipsum dolor sit amet\n" + - "Nunc at risus vel erat tempus posuere. Aenean non ante.\n" + - "\n\n\n" + - "Lorem ipsum dolor sit amet\n" + - "Lorem\n" + - "ipsum\n" + - "dolor\n" + - "sit\n" + - "amet\n" + - "\n\n\n", text - ); - - // Via set defaults - extractor.setSlidesByDefault(false); - extractor.setNotesByDefault(true); - text = extractor.getText(); - assertEquals( - "\n\n\n\n", text - ); - } -} diff --git a/src/scratchpad/ooxml-testcases/org/apache/poi/hssf/TestHSSFXML.java b/src/scratchpad/ooxml-testcases/org/apache/poi/hssf/TestHSSFXML.java deleted file mode 100644 index 97453265c..000000000 --- a/src/scratchpad/ooxml-testcases/org/apache/poi/hssf/TestHSSFXML.java +++ /dev/null @@ -1,160 +0,0 @@ -/* ==================================================================== - 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; - -import java.io.File; - -import org.apache.poi.hssf.model.SharedStringsTable; -import org.apache.poi.hxf.HXFDocument; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheet; - -import junit.framework.TestCase; - -public class TestHSSFXML extends TestCase { - /** - * Uses the old style schemas.microsoft.com schema uri - */ - private File sampleFileBeta; - /** - * Uses the new style schemas.openxmlformats.org schema uri - */ - private File sampleFile; - - protected void setUp() throws Exception { - super.setUp(); - - sampleFile = new File( - System.getProperty("HSSF.testdata.path") + - File.separator + "sample.xlsx" - ); - sampleFileBeta = new File( - System.getProperty("HSSF.testdata.path") + - File.separator + "sample-beta.xlsx" - ); - } - - public void testContainsMainContentType() throws Exception { - Package pack = HXFDocument.openPackage(sampleFile); - - boolean found = false; - for(PackagePart part : pack.getParts()) { - if(part.getContentType().equals(HSSFXML.MAIN_CONTENT_TYPE)) { - found = true; - } - System.out.println(part); - } - assertTrue(found); - } - - public void testOpen() throws Exception { - HXFDocument.openPackage(sampleFile); - HXFDocument.openPackage(sampleFileBeta); - - HSSFXML xml; - - // With an old-style uri, as found in a file produced - // with the office 2007 beta, will fail, as we don't - // translate things - try { - xml = new HSSFXML( - HXFDocument.openPackage(sampleFileBeta) - ); - fail(); - } catch(Exception e) {} - - // With the finalised uri, should be fine - xml = new HSSFXML( - HXFDocument.openPackage(sampleFile) - ); - - // Check it has a workbook - assertNotNull(xml.getWorkbook()); - } - - public void testSheetBasics() throws Exception { - HSSFXML xml = new HSSFXML( - HXFDocument.openPackage(sampleFile) - ); - - // Should have three sheets - assertEquals(3, xml.getSheetReferences().sizeOfSheetArray()); - assertEquals(3, xml.getSheetReferences().getSheetArray().length); - - // Check they're as expected - CTSheet[] sheets = xml.getSheetReferences().getSheetArray(); - assertEquals("Sheet1", sheets[0].getName()); - assertEquals("Sheet2", sheets[1].getName()); - assertEquals("Sheet3", sheets[2].getName()); - assertEquals("rId1", sheets[0].getId()); - assertEquals("rId2", sheets[1].getId()); - assertEquals("rId3", sheets[2].getId()); - - // Now get those objects - assertNotNull(xml.getSheet(sheets[0])); - assertNotNull(xml.getSheet(sheets[1])); - assertNotNull(xml.getSheet(sheets[2])); - } - - public void testMetadataBasics() throws Exception { - HSSFXML xml = new HSSFXML( - HXFDocument.openPackage(sampleFile) - ); - assertNotNull(xml.getCoreProperties()); - assertNotNull(xml.getExtendedProperties()); - - assertEquals("Microsoft Excel", xml.getExtendedProperties().getApplication()); - assertEquals(0, xml.getExtendedProperties().getCharacters()); - assertEquals(0, xml.getExtendedProperties().getLines()); - - assertEquals(null, xml.getCoreProperties().getTitleProperty().getValue()); - assertEquals(null, xml.getCoreProperties().getSubjectProperty().getValue()); - } - - public void testSharedStringBasics() throws Exception { - HSSFXML xml = new HSSFXML( - HXFDocument.openPackage(sampleFile) - ); - assertNotNull(xml._getSharedStringsTable()); - - SharedStringsTable sst = xml._getSharedStringsTable(); - assertEquals(10, sst.size()); - - assertEquals("Lorem", sst.get(0)); - for(int i=0; i 0); - - // Check sheet names - assertTrue(text.startsWith("Sheet1")); - assertTrue(text.endsWith("Sheet3\n")); - - // Now without, will have text - extractor.setIncludeSheetNames(false); - text = extractor.getText(); - assertEquals( - "Lorem\t111\n" + - "ipsum\t222\n" + - "dolor\t333\n" + - "sit\t444\n" + - "amet\t555\n" + - "consectetuer\t666\n" + - "adipiscing\t777\n" + - "elit\t888\n" + - "Nunc\t999\n" + - "at\t4995\n" + - "\n\n", text); - - // Now get formulas not their values - extractor.setFormulasNotResults(true); - text = extractor.getText(); - assertEquals( - "Lorem\t111\n" + - "ipsum\t222\n" + - "dolor\t333\n" + - "sit\t444\n" + - "amet\t555\n" + - "consectetuer\t666\n" + - "adipiscing\t777\n" + - "elit\t888\n" + - "Nunc\t999\n" + - "at\tSUM(B1:B9)\n" + - "\n\n", text); - - // With sheet names too - extractor.setIncludeSheetNames(true); - text = extractor.getText(); - assertEquals( - "Sheet1\n" + - "Lorem\t111\n" + - "ipsum\t222\n" + - "dolor\t333\n" + - "sit\t444\n" + - "amet\t555\n" + - "consectetuer\t666\n" + - "adipiscing\t777\n" + - "elit\t888\n" + - "Nunc\t999\n" + - "at\tSUM(B1:B9)\n\n" + - "Sheet2\n\n" + - "Sheet3\n" - , text); - } - - public void testGetComplexText() throws Exception { - new HXFExcelExtractor(xmlB.getPackage()); - new HXFExcelExtractor(new HSSFXMLWorkbook(xmlB)); - - HXFExcelExtractor extractor = - new HXFExcelExtractor(xmlB.getPackage()); - extractor.getText(); - - String text = extractor.getText(); - assertTrue(text.length() > 0); - - // Might not have all formatting it should do! - // TODO decide if we should really have the "null" in there - assertTrue(text.startsWith( - "Avgtxfull\n" + - "null\t(iii) AVERAGE TAX RATES ON ANNUAL" - )); - } - - /** - * Test that we return pretty much the same as - * ExcelExtractor does, when we're both passed - * the same file, just saved as xls and xlsx - */ - public void testComparedToOLE2() throws Exception { - HXFExcelExtractor ooxmlExtractor = - new HXFExcelExtractor(simpleXLSX.getPackage()); - ExcelExtractor ole2Extractor = - new ExcelExtractor(simpleXLS); - - POITextExtractor[] extractors = - new POITextExtractor[] { ooxmlExtractor, ole2Extractor }; - for (int i = 0; i < extractors.length; i++) { - POITextExtractor extractor = extractors[i]; - - String text = extractor.getText().replaceAll("[\r\t]", ""); - //System.out.println(text.length()); - //System.out.println(text); - assertTrue(text.startsWith("First Sheet\nTest spreadsheet\n2nd row2nd row 2nd column\n")); - Pattern pattern = Pattern.compile(".*13(\\.0+)?\\s+Sheet3.*", Pattern.DOTALL); - Matcher m = pattern.matcher(text); - assertTrue(m.matches()); - } - } -} diff --git a/src/scratchpad/ooxml-testcases/org/apache/poi/hwpf/TestHWPFXML.java b/src/scratchpad/ooxml-testcases/org/apache/poi/hwpf/TestHWPFXML.java deleted file mode 100644 index 0d8e196f4..000000000 --- a/src/scratchpad/ooxml-testcases/org/apache/poi/hwpf/TestHWPFXML.java +++ /dev/null @@ -1,110 +0,0 @@ -/* ==================================================================== - 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.hwpf; - -import java.io.File; - -import org.apache.poi.hxf.HXFDocument; -import org.openxml4j.opc.Package; -import org.openxml4j.opc.PackagePart; - -import junit.framework.TestCase; - -public class TestHWPFXML extends TestCase { - private File sampleFile; - private File complexFile; - - protected void setUp() throws Exception { - super.setUp(); - - sampleFile = new File( - System.getProperty("HWPF.testdata.path") + - File.separator + "sample.docx" - ); - complexFile = new File( - System.getProperty("HWPF.testdata.path") + - File.separator + "IllustrativeCases.docx" - ); - } - - public void testContainsMainContentType() throws Exception { - Package pack = HXFDocument.openPackage(sampleFile); - - boolean found = false; - for(PackagePart part : pack.getParts()) { - if(part.getContentType().equals(HWPFXML.MAIN_CONTENT_TYPE)) { - found = true; - } - System.out.println(part); - } - assertTrue(found); - } - - public void testOpen() throws Exception { - HXFDocument.openPackage(sampleFile); - HXFDocument.openPackage(complexFile); - - HWPFXML xml; - - // Simple file - xml = new HWPFXML( - HXFDocument.openPackage(sampleFile) - ); - // Check it has key parts - assertNotNull(xml.getDocument()); - assertNotNull(xml.getDocumentBody()); - assertNotNull(xml.getStyle()); - - // Complex file - xml = new HWPFXML( - HXFDocument.openPackage(complexFile) - ); - assertNotNull(xml.getDocument()); - assertNotNull(xml.getDocumentBody()); - assertNotNull(xml.getStyle()); - } - - public void testMetadataBasics() throws Exception { - HWPFXML xml = new HWPFXML( - HXFDocument.openPackage(sampleFile) - ); - assertNotNull(xml.getCoreProperties()); - assertNotNull(xml.getExtendedProperties()); - - assertEquals("Microsoft Office Word", xml.getExtendedProperties().getApplication()); - assertEquals(1315, xml.getExtendedProperties().getCharacters()); - assertEquals(10, xml.getExtendedProperties().getLines()); - - assertEquals(null, xml.getCoreProperties().getTitleProperty().getValue()); - assertEquals(null, xml.getCoreProperties().getSubjectProperty().getValue()); - } - - public void testMetadataComplex() throws Exception { - HWPFXML xml = new HWPFXML( - HXFDocument.openPackage(complexFile) - ); - assertNotNull(xml.getCoreProperties()); - assertNotNull(xml.getExtendedProperties()); - - assertEquals("Microsoft Office Outlook", xml.getExtendedProperties().getApplication()); - assertEquals(5184, xml.getExtendedProperties().getCharacters()); - assertEquals(0, xml.getExtendedProperties().getLines()); - - assertEquals(" ", xml.getCoreProperties().getTitleProperty().getValue()); - assertEquals(" ", xml.getCoreProperties().getSubjectProperty().getValue()); - } -} diff --git a/src/scratchpad/ooxml-testcases/org/apache/poi/hwpf/extractor/TestHXFWordExtractor.java b/src/scratchpad/ooxml-testcases/org/apache/poi/hwpf/extractor/TestHXFWordExtractor.java deleted file mode 100644 index 62695b3a8..000000000 --- a/src/scratchpad/ooxml-testcases/org/apache/poi/hwpf/extractor/TestHXFWordExtractor.java +++ /dev/null @@ -1,117 +0,0 @@ -/* ==================================================================== - 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.hwpf.extractor; - -import java.io.File; - -import org.apache.poi.hwpf.HWPFXML; -import org.apache.poi.hwpf.usermodel.HWPFXMLDocument; -import org.apache.poi.hxf.HXFDocument; - -import junit.framework.TestCase; - -/** - * Tests for HXFWordExtractor - */ -public class TestHXFWordExtractor extends TestCase { - /** - * A very simple file - */ - private HWPFXML xmlA; - /** - * A fairly complex file - */ - private HWPFXML xmlB; - - protected void setUp() throws Exception { - super.setUp(); - - File fileA = new File( - System.getProperty("HWPF.testdata.path") + - File.separator + "sample.docx" - ); - File fileB = new File( - System.getProperty("HWPF.testdata.path") + - File.separator + "IllustrativeCases.docx" - ); - - xmlA = new HWPFXML(HXFDocument.openPackage(fileA)); - xmlB = new HWPFXML(HXFDocument.openPackage(fileB)); - } - - /** - * Get text out of the simple file - */ - public void testGetSimpleText() throws Exception { - new HXFWordExtractor(xmlA.getPackage()); - new HXFWordExtractor(new HWPFXMLDocument(xmlA)); - - HXFWordExtractor extractor = - new HXFWordExtractor(xmlA.getPackage()); - extractor.getText(); - - String text = extractor.getText(); - assertTrue(text.length() > 0); - - // Check contents - assertTrue(text.startsWith( - "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nunc at risus vel erat tempus posuere. Aenean non ante. Suspendisse vehicula dolor sit amet odio." - )); - assertTrue(text.endsWith( - "Phasellus ultricies mi nec leo. Sed tempus. In sit amet lorem at velit faucibus vestibulum.\n" - )); - - // Check number of paragraphs - int ps = 0; - char[] t = text.toCharArray(); - for (int i = 0; i < t.length; i++) { - if(t[i] == '\n') { ps++; } - } - assertEquals(3, ps); - } - - /** - * Tests getting the text out of a complex file - */ - public void testGetComplexText() throws Exception { - HXFWordExtractor extractor = - new HXFWordExtractor(xmlB.getPackage()); - extractor.getText(); - - String text = extractor.getText(); - assertTrue(text.length() > 0); - - char euro = '\u20ac'; - System.err.println("'"+text.substring(text.length() - 20) + "'"); - - // Check contents - assertTrue(text.startsWith( - " \n(V) ILLUSTRATIVE CASES\n\n" - )); - assertTrue(text.endsWith( - "As well as gaining "+euro+"90 from child benefit increases, he will also receive the early childhood supplement of "+euro+"250 per quarter for Vincent for the full four quarters of the year.\n\n\n\n \n\n\n" - )); - - // Check number of paragraphs - int ps = 0; - char[] t = text.toCharArray(); - for (int i = 0; i < t.length; i++) { - if(t[i] == '\n') { ps++; } - } - assertEquals(79, ps); - } -} diff --git a/src/scratchpad/ooxml-testcases/org/apache/poi/hxf/TestDetectAsOOXML.java b/src/scratchpad/ooxml-testcases/org/apache/poi/hxf/TestDetectAsOOXML.java deleted file mode 100644 index 36adb497c..000000000 --- a/src/scratchpad/ooxml-testcases/org/apache/poi/hxf/TestDetectAsOOXML.java +++ /dev/null @@ -1,65 +0,0 @@ - -/* ==================================================================== - 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.hxf; - -import junit.framework.TestCase; -import java.io.*; - -/** - * Class to test that HXF correctly detects OOXML - * documents - */ -public class TestDetectAsOOXML extends TestCase -{ - public String dirname; - - public void setUp() { - dirname = System.getProperty("HSSF.testdata.path"); - } - - public void testOpensProperly() throws Exception - { - File f = new File(dirname + "/sample.xlsx"); - - HXFDocument.openPackage(f); - } - - public void testDetectAsPOIFS() throws Exception { - InputStream in; - - // ooxml file is - in = new PushbackInputStream( - new FileInputStream(dirname + "/SampleSS.xlsx"), 10 - ); - assertTrue(HXFDocument.hasOOXMLHeader(in)); - - // xls file isn't - in = new PushbackInputStream( - new FileInputStream(dirname + "/SampleSS.xls"), 10 - ); - assertFalse(HXFDocument.hasOOXMLHeader(in)); - - // text file isn't - in = new PushbackInputStream( - new FileInputStream(dirname + "/SampleSS.txt"), 10 - ); - assertFalse(HXFDocument.hasOOXMLHeader(in)); - } -} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/AutoShapes.java b/src/scratchpad/src/org/apache/poi/hslf/model/AutoShapes.java index 5d345e6de..1fc4ec993 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/AutoShapes.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/AutoShapes.java @@ -369,5 +369,12 @@ public class AutoShapes { } }; + shapes[ShapeTypes.StraightConnector1] = new ShapeOutline(){ + public java.awt.Shape getOutline(Shape shape){ + return new Line2D.Float(0, 0, 21600, 21600); + } + }; + + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java b/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java index d31237f8d..fb3980a45 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Freeform.java @@ -19,6 +19,7 @@ package org.apache.poi.hslf.model; import org.apache.poi.ddf.*; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogger; +import org.apache.poi.util.HexDump; import java.awt.geom.*; import java.util.ArrayList; @@ -185,10 +186,6 @@ public class Freeform extends AutoShape { return null; } - Rectangle2D bounds = getAnchor2D(); - float right = (float)bounds.getX(); - float bottom = (float)bounds.getY(); - GeneralPath path = new GeneralPath(); int numPoints = verticesProp.getNumberOfElementsInArray(); int numSegments = segmentsProp.getNumberOfElementsInArray(); @@ -199,8 +196,8 @@ public class Freeform extends AutoShape { short x = LittleEndian.getShort(p, 0); short y = LittleEndian.getShort(p, 2); path.moveTo( - ((float)x*POINT_DPI/MASTER_DPI + right), - ((float)y*POINT_DPI/MASTER_DPI + bottom)); + ((float)x*POINT_DPI/MASTER_DPI), + ((float)y*POINT_DPI/MASTER_DPI)); } else if (Arrays.equals(elem, SEGMENTINFO_CUBICTO) || Arrays.equals(elem, SEGMENTINFO_CUBICTO2)){ i++; byte[] p1 = verticesProp.getElement(j++); @@ -213,9 +210,9 @@ public class Freeform extends AutoShape { short x3 = LittleEndian.getShort(p3, 0); short y3 = LittleEndian.getShort(p3, 2); path.curveTo( - ((float)x1*POINT_DPI/MASTER_DPI + right), ((float)y1*POINT_DPI/MASTER_DPI + bottom), - ((float)x2*POINT_DPI/MASTER_DPI + right), ((float)y2*POINT_DPI/MASTER_DPI + bottom), - ((float)x3*POINT_DPI/MASTER_DPI + right), ((float)y3*POINT_DPI/MASTER_DPI + bottom)); + ((float)x1*POINT_DPI/MASTER_DPI), ((float)y1*POINT_DPI/MASTER_DPI), + ((float)x2*POINT_DPI/MASTER_DPI), ((float)y2*POINT_DPI/MASTER_DPI), + ((float)x3*POINT_DPI/MASTER_DPI), ((float)y3*POINT_DPI/MASTER_DPI)); } else if (Arrays.equals(elem, SEGMENTINFO_LINETO)){ i++; @@ -226,18 +223,26 @@ public class Freeform extends AutoShape { short x = LittleEndian.getShort(p, 0); short y = LittleEndian.getShort(p, 2); path.lineTo( - ((float)x*POINT_DPI/MASTER_DPI + right), ((float)y*POINT_DPI/MASTER_DPI + bottom)); + ((float)x*POINT_DPI/MASTER_DPI), ((float)y*POINT_DPI/MASTER_DPI)); } } else if (Arrays.equals(pnext, SEGMENTINFO_CLOSE)){ path.closePath(); } } } - return path; } public java.awt.Shape getOutline(){ - return getPath(); + GeneralPath path = getPath(); + Rectangle2D anchor = getAnchor2D(); + Rectangle2D bounds = path.getBounds2D(); + AffineTransform at = new AffineTransform(); + at.translate(anchor.getX(), anchor.getY()); + at.scale( + anchor.getWidth()/bounds.getWidth(), + anchor.getHeight()/bounds.getHeight() + ); + return at.createTransformedShape(path); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java index 5b1b1016e..d01136d87 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/MasterSheet.java @@ -67,4 +67,21 @@ public abstract class MasterSheet extends Sheet { } return false; } + + /** + * Return placeholder by text type + */ + public TextShape getPlaceholder(int type){ + Shape[] shape = getShapes(); + for (int i = 0; i < shape.length; i++) { + if(shape[i] instanceof TextShape){ + TextShape tx = (TextShape)shape[i]; + TextRun run = tx.getTextRun(); + if(run != null && run.getRunType() == type){ + return tx; + } + } + } + return null; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java index e10986966..375249b51 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java @@ -28,6 +28,7 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.awt.*; import java.awt.geom.Rectangle2D; +import java.awt.geom.AffineTransform; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; @@ -244,6 +245,9 @@ public class Picture extends SimpleShape { } public void draw(Graphics2D graphics){ + AffineTransform at = graphics.getTransform(); + ShapePainter.paint(this, graphics); + PictureData data = getPictureData(); if (data instanceof Bitmap){ BufferedImage img = null; @@ -260,5 +264,6 @@ public class Picture extends SimpleShape { } else { logger.log(POILogger.WARN, "Rendering of metafiles is not yet supported. image.type: " + (data == null ? "NA" : data.getClass().getName())); } + graphics.setTransform(at); } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java index e96e34900..fbea17e88 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java @@ -341,58 +341,7 @@ public abstract class Shape { * @param sh - owning shape */ protected void afterInsert(Sheet sh){ - PPDrawing ppdrawing = sh.getPPDrawing(); - EscherContainerRecord dgContainer = (EscherContainerRecord) ppdrawing.getEscherRecords()[0]; - - EscherDgRecord dg = (EscherDgRecord) Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID); - - int id = allocateShapeId(dg); - setShapeId(id); - } - - /** - * Allocates new shape id for the new drawing group id. - * - * @param dg EscherDgRecord of the sheet that owns the shape being created - * - * @return a new shape id. - */ - protected int allocateShapeId(EscherDgRecord dg) - { - EscherDggRecord dgg = _sheet.getSlideShow().getDocumentRecord().getPPDrawingGroup().getEscherDggRecord(); - if(dgg == null){ - logger.log(POILogger.ERROR, "EscherDggRecord not found"); - return 0; - } - - dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 ); - - // Add to existing cluster if space available - for (int i = 0; i < dgg.getFileIdClusters().length; i++) - { - EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i]; - if (c.getDrawingGroupId() == dg.getDrawingGroupId() && c.getNumShapeIdsUsed() != 1024) - { - int result = c.getNumShapeIdsUsed() + (1024 * (i+1)); - c.incrementShapeId(); - dg.setNumShapes( dg.getNumShapes() + 1 ); - dg.setLastMSOSPID( result ); - if (result >= dgg.getShapeIdMax()) - dgg.setShapeIdMax( result + 1 ); - return result; - } - } - - // Create new cluster - dgg.addCluster( dg.getDrawingGroupId(), 0 ); - dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId(); - dg.setNumShapes( dg.getNumShapes() + 1 ); - int result = (1024 * dgg.getFileIdClusters().length); - dg.setLastMSOSPID( result ); - if (result >= dgg.getShapeIdMax()) - dgg.setShapeIdMax( result + 1 ); - return result; } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java index b57a6e233..cfa829671 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java @@ -196,13 +196,8 @@ public class ShapeGroup extends Shape{ Sheet sheet = getSheet(); shape.setSheet(sheet); + shape.setShapeId(sheet.allocateShapeId()); shape.afterInsert(sheet); - - if (shape instanceof TextShape) { - TextShape tbox = (TextShape) shape; - EscherTextboxWrapper txWrapper = tbox.getEscherTextboxWrapper(); - if(txWrapper != null) getSheet().getPPDrawing().addTextboxWrapper(txWrapper); - } } /** @@ -277,20 +272,9 @@ public class ShapeGroup extends Shape{ } public void draw(Graphics2D graphics){ - Rectangle2D anchor = getAnchor2D(); - Rectangle2D coords = getCoordinates(); - //transform coordinates AffineTransform at = graphics.getTransform(); - /* - if(!anchor.equals(coords)){ - graphics.scale(anchor.getWidth()/coords.getWidth(), anchor.getHeight()/coords.getHeight()); - graphics.translate( - anchor.getX()*coords.getWidth()/anchor.getWidth() - coords.getX(), - anchor.getY()*coords.getHeight()/anchor.getHeight() - coords.getY()); - } - */ Shape[] sh = getShapes(); for (int i = 0; i < sh.length; i++) { sh[i].draw(graphics); diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java index d9c8903d5..6eb84ca2e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java @@ -18,12 +18,10 @@ package org.apache.poi.hslf.model; -import org.apache.poi.ddf.EscherContainerRecord; -import org.apache.poi.ddf.EscherDgRecord; -import org.apache.poi.ddf.EscherRecord; -import org.apache.poi.ddf.EscherSpRecord; +import org.apache.poi.ddf.*; import org.apache.poi.hslf.record.*; import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.util.POILogger; import java.util.ArrayList; import java.util.Iterator; @@ -248,15 +246,47 @@ public abstract class Sheet { spgr.addChildRecord(shape.getSpContainer()); shape.setSheet(this); + shape.setShapeId(allocateShapeId()); shape.afterInsert(this); + } - // If it's a TextShape, we need to tell the PPDrawing, as it has to - // track TextboxWrappers specially - if (shape instanceof TextShape) { - TextShape tbox = (TextShape) shape; - EscherTextboxWrapper txWrapper = tbox.getEscherTextboxWrapper(); - if(txWrapper != null) ppdrawing.addTextboxWrapper(txWrapper); + /** + * Allocates new shape id for the new drawing group id. + * + * @return a new shape id. + */ + public int allocateShapeId() + { + EscherDggRecord dgg = _slideShow.getDocumentRecord().getPPDrawingGroup().getEscherDggRecord(); + EscherDgRecord dg = _container.getPPDrawing().getEscherDgRecord(); + + dgg.setNumShapesSaved( dgg.getNumShapesSaved() + 1 ); + + // Add to existing cluster if space available + for (int i = 0; i < dgg.getFileIdClusters().length; i++) + { + EscherDggRecord.FileIdCluster c = dgg.getFileIdClusters()[i]; + if (c.getDrawingGroupId() == dg.getDrawingGroupId() && c.getNumShapeIdsUsed() != 1024) + { + int result = c.getNumShapeIdsUsed() + (1024 * (i+1)); + c.incrementShapeId(); + dg.setNumShapes( dg.getNumShapes() + 1 ); + dg.setLastMSOSPID( result ); + if (result >= dgg.getShapeIdMax()) + dgg.setShapeIdMax( result + 1 ); + return result; + } } + + // Create new cluster + dgg.addCluster( dg.getDrawingGroupId(), 0, false ); + dgg.getFileIdClusters()[dgg.getFileIdClusters().length-1].incrementShapeId(); + dg.setNumShapes( dg.getNumShapes() + 1 ); + int result = (1024 * dgg.getFileIdClusters().length); + dg.setLastMSOSPID( result ); + if (result >= dgg.getShapeIdMax()) + dgg.setShapeIdMax( result + 1 ); + return result; } /** @@ -284,6 +314,13 @@ public abstract class Sheet { return lst.remove(shape.getSpContainer()); } + /** + * Called by SlideShow ater a new sheet is created + */ + public void onCreate(){ + + } + /** * Return the master sheet . */ diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java index ea0719a7f..e15454d65 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java @@ -126,8 +126,8 @@ public class SimpleShape extends Shape { EscherSimpleProperty p2 = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH); int p2val = p2 == null ? 0 : p2.getPropertyValue(); Color clr = null; - if (p1 != null && (p2val & 0x8) != 0){ - int rgb = p1.getPropertyValue(); + if ((p2val & 0x8) != 0 || (p2val & 0x10) != 0){ + int rgb = p1 == null ? 0 : p1.getPropertyValue(); if (rgb >= 0x8000000) { int idx = rgb % 0x8000000; if(getSheet() != null) { diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java b/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java index e618ae230..bb99c1bca 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Slide.java @@ -21,12 +21,17 @@ package org.apache.poi.hslf.model; import java.util.Vector; +import java.util.Iterator; import java.awt.*; import org.apache.poi.hslf.record.SlideAtom; import org.apache.poi.hslf.record.TextHeaderAtom; import org.apache.poi.hslf.record.ColorSchemeAtom; import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; +import org.apache.poi.ddf.EscherDggRecord; +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherDgRecord; +import org.apache.poi.ddf.EscherSpRecord; /** * This class represents a slide in a PowerPoint Document. It allows @@ -126,6 +131,42 @@ public class Slide extends Sheet _slideNo = newSlideNumber; } + /** + * Called by SlideShow ater a new slide is created. + *

+ * For Slide we need to do the following: + *

  • set id of the drawing group. + *
  • set shapeId for the container descriptor and background + *

    + */ + public void onCreate(){ + //initialize drawing group id + EscherDggRecord dgg = getSlideShow().getDocumentRecord().getPPDrawingGroup().getEscherDggRecord(); + EscherContainerRecord dgContainer = (EscherContainerRecord)getSheetContainer().getPPDrawing().getEscherRecords()[0]; + EscherDgRecord dg = (EscherDgRecord) Shape.getEscherChild(dgContainer, EscherDgRecord.RECORD_ID); + int dgId = dgg.getMaxDrawingGroupId() + 1; + dg.setOptions((short)(dgId << 4)); + dgg.setDrawingsSaved(dgg.getDrawingsSaved() + 1); + + for (Iterator it = dgContainer.getChildContainers().iterator(); it.hasNext(); ) { + EscherContainerRecord c = (EscherContainerRecord)it.next(); + EscherSpRecord spr = null; + switch(c.getRecordId()){ + case EscherContainerRecord.SPGR_CONTAINER: + EscherContainerRecord dc = (EscherContainerRecord)c.getChildRecords().get(0); + spr = dc.getChildById(EscherSpRecord.RECORD_ID); + break; + case EscherContainerRecord.SP_CONTAINER: + spr = c.getChildById(EscherSpRecord.RECORD_ID); + break; + } + if(spr != null) spr.setShapeId(allocateShapeId()); + } + + //PPT doen't increment the number of saved shapes for group descriptor and background + dg.setNumShapes(1); + } + /** * Create a TextBox object that represents the slide's title. * diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java b/src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java index 26870dbdb..b48edfc1a 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/SlideMaster.java @@ -92,6 +92,7 @@ public class SlideMaster extends MasterSheet { } else { switch (txtype) { case TextHeaderAtom.CENTRE_BODY_TYPE: + case TextHeaderAtom.HALF_BODY_TYPE: case TextHeaderAtom.QUARTER_BODY_TYPE: txtype = TextHeaderAtom.BODY_TYPE; break; diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java index c1eadc336..6eff54329 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java @@ -17,6 +17,7 @@ package org.apache.poi.hslf.model; import org.apache.poi.hslf.usermodel.RichTextRun; +import org.apache.poi.hslf.record.TextRulerAtom; import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogFactory; @@ -38,6 +39,13 @@ import java.util.ArrayList; public class TextPainter { protected POILogger logger = POILogFactory.getLogger(this.getClass()); + /** + * Display unicode square if a bullet char can't be displayed, + * for example, if Wingdings font is used. + * TODO: map Wingdngs and Symbol to unicode Arial + */ + protected static final char DEFAULT_BULLET_CHAR = '\u25a0'; + protected TextShape _shape; public TextPainter(TextShape shape){ @@ -49,6 +57,10 @@ public class TextPainter { */ public AttributedString getAttributedString(TextRun txrun){ String text = txrun.getText(); + //TODO: properly process tabs + text = text.replace('\t', ' '); + text = text.replace((char)160, ' '); + AttributedString at = new AttributedString(text); RichTextRun[] rt = txrun.getRichTextRuns(); for (int i = 0; i < rt.length; i++) { @@ -109,7 +121,24 @@ public class TextPainter { } float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight(); - wrappingWidth -= rt.getTextOffset(); + int bulletOffset = rt.getBulletOffset(); + int textOffset = rt.getTextOffset(); + int indent = rt.getIndentLevel(); + + TextRulerAtom ruler = run.getTextRuler(); + if(ruler != null) { + int bullet_val = ruler.getBulletOffsets()[indent]*Shape.POINT_DPI/Shape.MASTER_DPI; + int text_val = ruler.getTextOffsets()[indent]*Shape.POINT_DPI/Shape.MASTER_DPI; + if(bullet_val > text_val){ + int a = bullet_val; + bullet_val = text_val; + text_val = a; + } + if(bullet_val != 0 ) bulletOffset = bullet_val; + if(text_val != 0) textOffset = text_val; + } + + wrappingWidth -= textOffset; if (_shape.getWordWrap() == TextShape.WrapNone) { wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width; @@ -141,8 +170,9 @@ public class TextPainter { } el._align = rt.getAlignment(); - el._text = textLayout; - el._textOffset = rt.getTextOffset(); + el.advance = textLayout.getAdvance(); + el._textOffset = textOffset; + el._text = new AttributedString(it, startIndex, endIndex); if (prStart){ int sp = rt.getSpaceBefore(); @@ -182,13 +212,25 @@ public class TextPainter { Color clr = rt.getBulletColor(); if (clr != null) bat.addAttribute(TextAttribute.FOREGROUND, clr); else bat.addAttribute(TextAttribute.FOREGROUND, it.getAttribute(TextAttribute.FOREGROUND)); - bat.addAttribute(TextAttribute.FAMILY, it.getAttribute(TextAttribute.FAMILY)); - bat.addAttribute(TextAttribute.SIZE, it.getAttribute(TextAttribute.SIZE)); - TextLayout bulletLayout = new TextLayout(bat.getIterator(), graphics.getFontRenderContext()); + int fontIdx = rt.getBulletFont(); + if(fontIdx == -1) fontIdx = rt.getFontIndex(); + PPFont bulletFont = _shape.getSheet().getSlideShow().getFont(fontIdx); + bat.addAttribute(TextAttribute.FAMILY, bulletFont.getFontName()); + + int bulletSize = rt.getBulletSize(); + int fontSize = rt.getFontSize(); + if(bulletSize != -1) fontSize = Math.round(fontSize*bulletSize*0.01f); + bat.addAttribute(TextAttribute.SIZE, new Float(fontSize)); + + if(!new Font(bulletFont.getFontName(), Font.PLAIN, 1).canDisplay(rt.getBulletChar())){ + bat.addAttribute(TextAttribute.FAMILY, "Arial"); + bat = new AttributedString("" + DEFAULT_BULLET_CHAR, bat.getIterator().getAttributes()); + } + if(text.substring(startIndex, endIndex).length() > 1){ - el._bullet = bulletLayout; - el._bulletOffset = rt.getBulletOffset(); + el._bullet = bat; + el._bulletOffset = bulletOffset; } } lines.add(el); @@ -225,29 +267,32 @@ public class TextPainter { break; case TextShape.AlignCenter: pen.x = anchor.getX() + _shape.getMarginLeft() + - (anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight()) / 2; + (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2; break; case TextShape.AlignRight: pen.x = anchor.getX() + _shape.getMarginLeft() + - (anchor.getWidth() - elem._text.getAdvance() - _shape.getMarginLeft() - _shape.getMarginRight()); + (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight()); break; } if(elem._bullet != null){ - elem._bullet.draw(graphics, (float)(pen.x + elem._bulletOffset), (float)pen.y); + graphics.drawString(elem._bullet.getIterator(), (float)(pen.x + elem._bulletOffset), (float)pen.y); + } + AttributedCharacterIterator chIt = elem._text.getIterator(); + if(chIt.getEndIndex() > chIt.getBeginIndex()) { + graphics.drawString(chIt, (float)(pen.x + elem._textOffset), (float)pen.y); } - elem._text.draw(graphics, (float)(pen.x + elem._textOffset), (float)pen.y); - y0 += elem.descent; } } - static class TextElement { - public TextLayout _text; + public static class TextElement { + public AttributedString _text; public int _textOffset; - public TextLayout _bullet; + public AttributedString _bullet; public int _bulletOffset; public int _align; public float ascent, descent; + public float advance; } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java index 2f77ac5ff..5dac65025 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java @@ -535,9 +535,13 @@ public class TextRun // them to \n String text = rawText.replace('\r','\n'); - //0xB acts like cariage return in page titles - text = text.replace((char) 0x0B, '\n'); - + int type = _headerAtom == null ? 0 : _headerAtom.getTextType(); + if(type == TextHeaderAtom.TITLE_TYPE || type == TextHeaderAtom.CENTER_TITLE_TYPE){ + //0xB acts like cariage return in page titles and like blank in the others + text = text.replace((char) 0x0B, '\n'); + } else { + text = text.replace((char) 0x0B, ' '); + } return text; } @@ -655,4 +659,11 @@ public class TextRun return null; } + public TextRulerAtom getTextRuler(){ + for (int i = 0; i < _records.length; i++) { + if(_records[i] instanceof TextRulerAtom) return (TextRulerAtom)_records[i]; + } + return null; + + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java index 7249817be..53f8ef97d 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java @@ -261,17 +261,28 @@ public abstract class TextShape extends SimpleShape { public int getVerticalAlignment(){ EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT); - int valign; + int valign = TextShape.AnchorTop; if (prop == null){ + /** + * If vertical alignment was not found in the shape properties then try to + * fetch the master shape and search for the align property there. + */ int type = getTextRun().getRunType(); - switch (type){ - case TextHeaderAtom.TITLE_TYPE: - case TextHeaderAtom.CENTER_TITLE_TYPE: - valign = TextShape.AnchorMiddle; - break; - default: - valign = TextShape.AnchorTop; - break; + MasterSheet master = getSheet().getMasterSheet(); + if(master != null){ + TextShape masterShape = master.getPlaceholder(type); + if(masterShape != null) valign = masterShape.getVerticalAlignment(); + } else { + //not found in the master sheet. Use the hardcoded defaults. + switch (type){ + case TextHeaderAtom.TITLE_TYPE: + case TextHeaderAtom.CENTER_TITLE_TYPE: + valign = TextShape.AnchorMiddle; + break; + default: + valign = TextShape.AnchorTop; + break; + } } } else { valign = prop.getPropertyValue(); diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java index e42b358b8..3a1ed8dbe 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java @@ -24,11 +24,13 @@ import org.apache.poi.util.POILogger; import org.apache.poi.ddf.*; import org.apache.poi.hslf.model.ShapeTypes; +import org.apache.poi.hslf.model.Shape; import java.io.IOException; import java.io.OutputStream; import java.util.List; import java.util.Vector; +import java.util.Iterator; /** * These are actually wrappers onto Escher drawings. Make use of @@ -52,6 +54,8 @@ public class PPDrawing extends RecordAtom private EscherRecord[] childRecords; private EscherTextboxWrapper[] textboxWrappers; + //cached EscherDgRecord + private EscherDgRecord dg; /** * Get access to the underlying Escher Records @@ -296,4 +300,24 @@ public class PPDrawing extends RecordAtom tw[textboxWrappers.length] = txtbox; textboxWrappers = tw; } + + /** + * Return EscherDgRecord which keeps track of the number of shapes and shapeId in this drawing group + * + * @return EscherDgRecord + */ + public EscherDgRecord getEscherDgRecord(){ + if(dg == null){ + EscherContainerRecord dgContainer = (EscherContainerRecord)childRecords[0]; + for(Iterator it = dgContainer.getChildRecords().iterator(); it.hasNext();){ + EscherRecord r = (EscherRecord) it.next(); + if(r instanceof EscherDgRecord){ + dg = (EscherDgRecord)r; + break; + } + } + } + return dg; + } + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java index 45038f7a2..cedc99ce0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java @@ -89,7 +89,7 @@ public class RecordTypes { public static final Type TxMasterStyleAtom = new Type(4003,TxMasterStyleAtom.class); public static final Type TxCFStyleAtom = new Type(4004,null); public static final Type TxPFStyleAtom = new Type(4005,null); - public static final Type TextRulerAtom = new Type(4006,null); + public static final Type TextRulerAtom = new Type(4006,TextRulerAtom.class); public static final Type TextBookmarkAtom = new Type(4007,null); public static final Type TextBytesAtom = new Type(4008,TextBytesAtom.class); public static final Type TxSIStyleAtom = new Type(4009,null); diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java index 187dec12a..2f3b898a7 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java @@ -127,8 +127,8 @@ public class StyleTextPropAtom extends RecordAtom new ParagraphFlagsTextProp(), new TextProp(2, 0x80, "bullet.char"), new TextProp(2, 0x10, "bullet.font"), + new TextProp(2, 0x40, "bullet.size"), new TextProp(4, 0x20, "bullet.color"), - new TextProp(2, 0x40, "bullet.size"), new AlignmentTextProp(), new TextProp(2, 0x100, "text.offset"), new TextProp(2, 0x200, "para_unknown_2"), diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java new file mode 100755 index 000000000..71ded8540 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java @@ -0,0 +1,194 @@ +/* ==================================================================== + 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.hslf.record; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.InflaterInputStream; + +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogger; + +/** + * Ruler of a text as it differs from the style's ruler settings. + * + * @author Yegor Kozlov + */ +public class TextRulerAtom extends RecordAtom { + + /** + * Record header. + */ + private byte[] _header; + + /** + * Record data. + */ + private byte[] _data; + + //ruler internals + private int defaultTabSize; + private int numLevels; + private int[] tabStops; + private int[] bulletOffsets = new int[5]; + private int[] textOffsets = new int[5]; + + /** + * Constructs a new empty ruler atom. + */ + protected TextRulerAtom() { + _header = new byte[8]; + _data = new byte[0]; + + LittleEndian.putShort(_header, 2, (short)getRecordType()); + LittleEndian.putInt(_header, 4, _data.length); + } + + /** + * Constructs the ruler atom record from its + * source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected TextRulerAtom(byte[] source, int start, int len) { + // Get the header. + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Get the record data. + _data = new byte[len-8]; + System.arraycopy(source,start+8,_data,0,len-8); + + try { + read(); + } catch (Exception e){ + logger.log(POILogger.ERROR, "Failed to parse TextRulerAtom: " + e.getMessage()); + e.printStackTrace(); + } + } + + /** + * Gets the record type. + * + * @return the record type. + */ + public long getRecordType() { + return RecordTypes.TextRulerAtom.typeID; + } + + /** + * Write the contents of the record back, so it can be written + * to disk. + * + * @param out the output stream to write to. + * @throws java.io.IOException if an error occurs. + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + out.write(_data); + } + + /** + * Read the record bytes and initialize the internal variables + */ + private void read(){ + int pos = 0; + short mask = LittleEndian.getShort(_data); pos += 4; + short val; + int[] bits = {1, 0, 2, 3, 8, 4, 9, 5, 10, 6, 11, 7, 12}; + for (int i = 0; i < bits.length; i++) { + if((mask & 1 << bits[i]) != 0){ + switch (bits[i]){ + case 0: + //defaultTabSize + defaultTabSize = LittleEndian.getShort(_data, pos); pos += 2; + break; + case 1: + //numLevels + numLevels = LittleEndian.getShort(_data, pos); pos += 2; + break; + case 2: + //tabStops + val = LittleEndian.getShort(_data, pos); pos += 2; + tabStops = new int[val*2]; + for (int j = 0; j < tabStops.length; j++) { + tabStops[j] = LittleEndian.getUShort(_data, pos); pos += 2; + } + break; + case 3: + case 4: + case 5: + case 6: + case 7: + //bullet.offset + val = LittleEndian.getShort(_data, pos); pos += 2; + bulletOffsets[bits[i]-3] = val; + break; + case 8: + case 9: + case 10: + case 11: + case 12: + //text.offset + val = LittleEndian.getShort(_data, pos); pos += 2; + textOffsets[bits[i]-8] = val; + break; + } + } + } + } + + /** + * Default distance between tab stops, in master coordinates (576 dpi). + */ + public int getDefaultTabSize(){ + return defaultTabSize; + } + + /** + * Number of indent levels (maximum 5). + */ + public int getNumberOfLevels(){ + return numLevels; + } + + /** + * Default distance between tab stops, in master coordinates (576 dpi). + */ + public int[] getTabStops(){ + return tabStops; + } + + /** + * Paragraph's distance from shape's left margin, in master coordinates (576 dpi). + */ + public int[] getTextOffsets(){ + return textOffsets; + } + + /** + * First line of paragraph's distance from shape's left margin, in master coordinates (576 dpi). + */ + public int[] getBulletOffsets(){ + return bulletOffsets; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java index 2a09f2224..7458df7e6 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java @@ -32,6 +32,8 @@ import org.apache.poi.hslf.model.textproperties.ParagraphFlagsTextProp; import org.apache.poi.hslf.model.textproperties.TextProp; import org.apache.poi.hslf.model.textproperties.TextPropCollection; import org.apache.poi.hslf.record.ColorSchemeAtom; +import org.apache.poi.util.POILogger; +import org.apache.poi.util.POILogFactory; /** @@ -39,6 +41,8 @@ import org.apache.poi.hslf.record.ColorSchemeAtom; * */ public class RichTextRun { + protected POILogger logger = POILogFactory.getLogger(this.getClass()); + /** The TextRun we belong to */ private TextRun parentRun; /** The SlideShow we belong to */ @@ -199,10 +203,15 @@ public class RichTextRun { } if (prop == null){ Sheet sheet = parentRun.getSheet(); - int txtype = parentRun.getRunType(); - MasterSheet master = sheet.getMasterSheet(); - if (master != null) - prop = (BitMaskTextProp)master.getStyleAttribute(txtype, getIndentLevel(), propname, isCharacter); + if(sheet != null){ + int txtype = parentRun.getRunType(); + MasterSheet master = sheet.getMasterSheet(); + if (master != null){ + prop = (BitMaskTextProp)master.getStyleAttribute(txtype, getIndentLevel(), propname, isCharacter); + } + } else { + logger.log(POILogger.WARN, "MasterSheet is not available"); + } } return prop == null ? false : prop.getSubValue(index); @@ -213,7 +222,7 @@ public class RichTextRun { * it if required. */ private void setCharFlagsTextPropVal(int index, boolean value) { - setFlag(true, index, value); + if(getFlag(true, index) != value) setFlag(true, index, value); } public void setFlag(boolean isCharacter, int index, boolean value) { @@ -281,10 +290,14 @@ public class RichTextRun { */ private int getParaTextPropVal(String propName) { TextProp prop = null; + boolean hardAttribute = false; if (paragraphStyle != null){ prop = paragraphStyle.findByName(propName); + + BitMaskTextProp maskProp = (BitMaskTextProp)paragraphStyle.findByName(ParagraphFlagsTextProp.NAME); + hardAttribute = maskProp != null && maskProp.getValue() == 0; } - if (prop == null){ + if (prop == null && !hardAttribute){ Sheet sheet = parentRun.getSheet(); int txtype = parentRun.getRunType(); MasterSheet master = sheet.getMasterSheet(); @@ -574,6 +587,13 @@ public class RichTextRun { return getFlag(false, ParagraphFlagsTextProp.BULLET_IDX); } + /** + * Returns whether this rich text run has bullets + */ + public boolean isBulletHard() { + return getFlag(false, ParagraphFlagsTextProp.BULLET_IDX); + } + /** * Sets the bullet character */ diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java index 828255087..d969c5b88 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java @@ -24,10 +24,7 @@ import java.util.*; import java.awt.Dimension; import java.io.*; -import org.apache.poi.ddf.EscherBSERecord; -import org.apache.poi.ddf.EscherContainerRecord; -import org.apache.poi.ddf.EscherOptRecord; -import org.apache.poi.ddf.EscherRecord; +import org.apache.poi.ddf.*; import org.apache.poi.hslf.*; import org.apache.poi.hslf.model.*; import org.apache.poi.hslf.model.Notes; @@ -66,9 +63,7 @@ public class SlideShow // Lookup between the PersitPtr "sheet" IDs, and the position // in the mostRecentCoreRecords array private Hashtable _sheetIdToCoreRecordsLookup; - // Used when adding new core records - private int _highestSheetId; - + // Records that are interesting private Document _documentRecord; @@ -203,8 +198,6 @@ public class SlideShow for(int i=0; i 0); + //EscherDgg is a document-level record which keeps track of the drawing groups + EscherDggRecord dgg = ppt.getDocumentRecord().getPPDrawingGroup().getEscherDggRecord(); + EscherDgRecord dg = slide.getSheetContainer().getPPDrawing().getEscherDgRecord(); - int shapeId = shape.getShapeId(); + int dggShapesUsed = dgg.getNumShapesSaved(); //total number of shapes in the ppt + int dggMaxId = dgg.getShapeIdMax(); //max number of shapeId - shape = new Line(); - assertEquals(0, shape.getShapeId()); - slide.addShape(shape); - assertEquals(shapeId + 1, shape.getShapeId()); + int dgMaxId = dg.getLastMSOSPID(); //max shapeId in the slide + int dgShapesUsed = dg.getNumShapes(); // number of shapes in the slide + //insert 3 shapes and make sure the Ids are properly incremented + for (int i = 0; i < 3; i++) { + shape = new Line(); + assertEquals(0, shape.getShapeId()); + slide.addShape(shape); + assertTrue(shape.getShapeId() > 0); + + //check that EscherDgRecord is updated + assertEquals(shape.getShapeId(), dg.getLastMSOSPID()); + assertEquals(dgMaxId + 1, dg.getLastMSOSPID()); + assertEquals(dgShapesUsed + 1, dg.getNumShapes()); + + //check that EscherDggRecord is updated + assertEquals(shape.getShapeId() + 1, dgg.getShapeIdMax()); + assertEquals(dggMaxId + 1, dgg.getShapeIdMax()); + assertEquals(dggShapesUsed + 1, dgg.getNumShapesSaved()); + + dggShapesUsed = dgg.getNumShapesSaved(); + dggMaxId = dgg.getShapeIdMax(); + dgMaxId = dg.getLastMSOSPID(); + dgShapesUsed = dg.getNumShapes(); + } + + + //For each drawing group PPT allocates clusters with size=1024 + //if the number of shapes is greater that 1024 a new cluster is allocated + //make sure it is so + int numClusters = dgg.getNumIdClusters(); + for (int i = 0; i < 1025; i++) { + shape = new Line(); + slide.addShape(shape); + } + assertEquals(numClusters + 1, dgg.getNumIdClusters()); } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java new file mode 100755 index 000000000..0b610cb64 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java @@ -0,0 +1,76 @@ + +/* ==================================================================== + 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.hslf.record; + +import org.apache.poi.hslf.HSLFSlideShow; +import org.apache.poi.hslf.model.textproperties.CharFlagsTextProp; +import org.apache.poi.hslf.model.textproperties.TextProp; +import org.apache.poi.hslf.model.textproperties.TextPropCollection; +import org.apache.poi.hslf.record.StyleTextPropAtom.*; +import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.util.HexDump; + +import junit.framework.TestCase; +import java.io.ByteArrayOutputStream; +import java.util.LinkedList; +import java.util.Arrays; + +/** + * Tests TextRulerAtom + * + * @author Yegor Kozlov + */ +public class TestTextRulerAtom extends TestCase { + + //from a real file + private byte[] data_1 = new byte[] { + 0x00, 0x00, (byte)0xA6, 0x0F, 0x18, 0x00, 0x00, 0x00, + (byte)0xF8, 0x1F, 0x00, 0x00, 0x75, 0x00, (byte)0xE2, 0x00, 0x59, + 0x01, (byte)0xC3, 0x01, 0x1A, 0x03, (byte)0x87, 0x03, (byte)0xF8, + 0x03, 0x69, 0x04, (byte)0xF6, 0x05, (byte)0xF6, 0x05 + }; + + + public void testReadRuler() throws Exception { + TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length); + assertEquals(ruler.getNumberOfLevels(), 0); + assertEquals(ruler.getDefaultTabSize(), 0); + + int[] tabStops = ruler.getTabStops(); + assertNull(tabStops); + + int[] textOffsets = ruler.getTextOffsets(); + assertTrue(Arrays.equals(new int[]{226, 451, 903, 1129, 1526}, textOffsets)); + + int[] bulletOffsets = ruler.getBulletOffsets(); + assertTrue(Arrays.equals(new int[]{117, 345, 794, 1016, 1526}, bulletOffsets)); + + } + + public void testWriteRuler() throws Exception { + TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ruler.writeOut(out); + + byte[] result = out.toByteArray(); + assertTrue(Arrays.equals(result, data_1)); + } +} diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java index 0fecdab97..eda6589c4 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestRichTextRun.java @@ -90,9 +90,11 @@ public class TestRichTextRun extends TestCase { // Now set it to not bold rtr.setBold(false); - assertNotNull(rtr._getRawCharacterStyle()); - assertNotNull(rtr._getRawParagraphStyle()); - assertFalse(rtr.isBold()); + //setting bold=false doesn't change the internal state + assertNull(rtr._getRawCharacterStyle()); + assertNull(rtr._getRawParagraphStyle()); + + assertFalse(rtr.isBold()); // And now make it bold rtr.setBold(true); diff --git a/src/testcases/org/apache/poi/ddf/AllPOIDDFTests.java b/src/testcases/org/apache/poi/ddf/AllPOIDDFTests.java index 9b5fc0bfe..ca82ac83c 100755 --- a/src/testcases/org/apache/poi/ddf/AllPOIDDFTests.java +++ b/src/testcases/org/apache/poi/ddf/AllPOIDDFTests.java @@ -42,6 +42,7 @@ public final class AllPOIDDFTests { result.addTestSuite(TestEscherSplitMenuColorsRecord.class); result.addTestSuite(TestEscherSpRecord.class); result.addTestSuite(TestUnknownEscherRecord.class); + result.addTestSuite(TestEscherBlipRecord.class); return result; } } diff --git a/src/testcases/org/apache/poi/ddf/TestEscherBlipRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherBlipRecord.java new file mode 100755 index 000000000..f7fecb666 --- /dev/null +++ b/src/testcases/org/apache/poi/ddf/TestEscherBlipRecord.java @@ -0,0 +1,156 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.ddf; + +import junit.framework.TestCase; +import org.apache.poi.util.HexRead; +import org.apache.poi.util.HexDump; + +import java.io.IOException; +import java.io.File; +import java.io.FileInputStream; +import java.util.Iterator; +import java.util.Arrays; + +/** + * Test read/serialize of escher blip records + * + * @author Yegor Kozlov + */ +public class TestEscherBlipRecord extends TestCase +{ + protected String cwd = System.getProperty("DDF.testdata.path"); + + //test reading/serializing of a PNG blip + public void testReadPNG() throws IOException { + //provided in bug-44886 + byte[] data = read(new File(cwd, "Container.dat")); + + EscherContainerRecord record = new EscherContainerRecord(); + record.fillFields(data, 0, new DefaultEscherRecordFactory()); + EscherContainerRecord bstore = (EscherContainerRecord)record.getChildRecords().get(1); + EscherBSERecord bse1 = (EscherBSERecord)bstore.getChildRecords().get(0); + assertEquals(EscherBSERecord.BT_PNG, bse1.getBlipTypeWin32()); + assertEquals(EscherBSERecord.BT_PNG, bse1.getBlipTypeMacOS()); + assertTrue(Arrays.equals(new byte[]{ + 0x65, 0x07, 0x4A, (byte)0x8D, 0x3E, 0x42, (byte)0x8B, (byte)0xAC, + 0x1D, (byte)0x89, 0x35, 0x4F, 0x48, (byte)0xFA, 0x37, (byte)0xC2 + }, bse1.getUid())); + assertEquals(255, bse1.getTag()); + assertEquals(32308, bse1.getSize()); + + EscherBitmapBlip blip1 = (EscherBitmapBlip)bse1.getBlipRecord(); + assertEquals(0x6E00, blip1.getOptions()); + assertEquals(EscherBitmapBlip.RECORD_ID_PNG, blip1.getRecordId()); + assertTrue(Arrays.equals(new byte[]{ + 0x65, 0x07, 0x4A, (byte)0x8D, 0x3E, 0x42, (byte)0x8B, (byte)0xAC, + 0x1D, (byte)0x89, 0x35, 0x4F, 0x48, (byte)0xFA, 0x37, (byte)0xC2 + }, blip1.getUID())); + + //serialize and read again + byte[] ser = bse1.serialize(); + EscherBSERecord bse2 = new EscherBSERecord(); + bse2.fillFields(ser, 0, new DefaultEscherRecordFactory()); + assertEquals(bse1.getRecordId(), bse2.getRecordId()); + assertEquals(bse1.getBlipTypeWin32(), bse2.getBlipTypeWin32()); + assertEquals(bse1.getBlipTypeMacOS(), bse2.getBlipTypeMacOS()); + assertTrue(Arrays.equals(bse1.getUid(), bse2.getUid())); + assertEquals(bse1.getTag(), bse2.getTag()); + assertEquals(bse1.getSize(), bse2.getSize()); + + EscherBitmapBlip blip2 = (EscherBitmapBlip)bse1.getBlipRecord(); + assertEquals(blip1.getOptions(), blip2.getOptions()); + assertEquals(blip1.getRecordId(), blip2.getRecordId()); + assertEquals(blip1.getUID(), blip2.getUID()); + + assertTrue(Arrays.equals(blip1.getPicturedata(), blip1.getPicturedata())); + } + + //test reading/serializing of a PICT metafile + public void testReadPICT() throws IOException { + //provided in bug-44886 + byte[] data = read(new File(cwd, "Container.dat")); + + EscherContainerRecord record = new EscherContainerRecord(); + record.fillFields(data, 0, new DefaultEscherRecordFactory()); + EscherContainerRecord bstore = (EscherContainerRecord)record.getChildRecords().get(1); + EscherBSERecord bse1 = (EscherBSERecord)bstore.getChildRecords().get(1); + //System.out.println(bse1); + assertEquals(EscherBSERecord.BT_WMF, bse1.getBlipTypeWin32()); + assertEquals(EscherBSERecord.BT_PICT, bse1.getBlipTypeMacOS()); + assertTrue(Arrays.equals(new byte[]{ + (byte)0xC7, 0x15, 0x69, 0x2D, (byte)0xE5, (byte)0x89, (byte)0xA3, 0x6F, + 0x66, 0x03, (byte)0xD6, 0x24, (byte)0xF7, (byte)0xDB, 0x1D, 0x13 + }, bse1.getUid())); + assertEquals(255, bse1.getTag()); + assertEquals(1133, bse1.getSize()); + + EscherMetafileBlip blip1 = (EscherMetafileBlip)bse1.getBlipRecord(); + assertEquals(0x5430, blip1.getOptions()); + assertEquals(EscherMetafileBlip.RECORD_ID_PICT, blip1.getRecordId()); + assertTrue(Arrays.equals(new byte[]{ + 0x57, 0x32, 0x7B, (byte)0x91, 0x23, 0x5D, (byte)0xDB, 0x36, + 0x7A, (byte)0xDB, (byte)0xFF, 0x17, (byte)0xFE, (byte)0xF3, (byte)0xA7, 0x05 + }, blip1.getUID())); + assertTrue(Arrays.equals(new byte[]{ + (byte)0xC7, 0x15, 0x69, 0x2D, (byte)0xE5, (byte)0x89, (byte)0xA3, 0x6F, + 0x66, 0x03, (byte)0xD6, 0x24, (byte)0xF7, (byte)0xDB, 0x1D, 0x13 + }, blip1.getPrimaryUID())); + + //serialize and read again + byte[] ser = bse1.serialize(); + EscherBSERecord bse2 = new EscherBSERecord(); + bse2.fillFields(ser, 0, new DefaultEscherRecordFactory()); + assertEquals(bse1.getRecordId(), bse2.getRecordId()); + assertEquals(bse1.getOptions(), bse2.getOptions()); + assertEquals(bse1.getBlipTypeWin32(), bse2.getBlipTypeWin32()); + assertEquals(bse1.getBlipTypeMacOS(), bse2.getBlipTypeMacOS()); + assertTrue(Arrays.equals(bse1.getUid(), bse2.getUid())); + assertEquals(bse1.getTag(), bse2.getTag()); + assertEquals(bse1.getSize(), bse2.getSize()); + + EscherMetafileBlip blip2 = (EscherMetafileBlip)bse1.getBlipRecord(); + assertEquals(blip1.getOptions(), blip2.getOptions()); + assertEquals(blip1.getRecordId(), blip2.getRecordId()); + assertEquals(blip1.getUID(), blip2.getUID()); + assertEquals(blip1.getPrimaryUID(), blip2.getPrimaryUID()); + + assertTrue(Arrays.equals(blip1.getPicturedata(), blip1.getPicturedata())); + } + + //integral test: check that the read-write-read round trip is consistent + public void testContainer() throws IOException { + byte[] data = read(new File(cwd, "Container.dat")); + + EscherContainerRecord record = new EscherContainerRecord(); + record.fillFields(data, 0, new DefaultEscherRecordFactory()); + + byte[] ser = record.serialize(); + assertTrue(Arrays.equals(data, ser)); + } + + private byte[] read(File file) throws IOException { + byte[] data = new byte[(int)file.length()]; + FileInputStream is = new FileInputStream(file); + is.read(data); + is.close(); + return data; + } + +} diff --git a/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java b/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java index 7c139827b..0f1fc9c73 100644 --- a/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java +++ b/src/testcases/org/apache/poi/ddf/TestEscherContainerRecord.java @@ -18,13 +18,24 @@ package org.apache.poi.ddf; +import java.io.File; +import java.io.FileInputStream; + import junit.framework.TestCase; import org.apache.poi.util.HexRead; import org.apache.poi.util.HexDump; +import org.apache.poi.util.IOUtils; public class TestEscherContainerRecord extends TestCase { - public void testFillFields() throws Exception + private String ESCHER_DATA_PATH; + + protected void setUp() throws Exception { + super.setUp(); + ESCHER_DATA_PATH = System.getProperty("DDF.testdata.path"); + } + + public void testFillFields() throws Exception { EscherRecordFactory f = new DefaultEscherRecordFactory(); byte[] data = HexRead.readFromString( "0F 02 11 F1 00 00 00 00" ); @@ -137,4 +148,19 @@ public class TestEscherContainerRecord extends TestCase assertEquals(18, r.getRecordSize()); } + /** + * We were having problems with reading too much data on an UnknownEscherRecord, + * but hopefully we now read the correct size. + */ + public void testBug44857() throws Exception { + File f = new File(ESCHER_DATA_PATH, "Container.dat"); + assertTrue(f.exists()); + + FileInputStream finp = new FileInputStream(f); + byte[] data = IOUtils.toByteArray(finp); + + // This used to fail with an OutOfMemory + EscherContainerRecord record = new EscherContainerRecord(); + record.fillFields(data, 0, new DefaultEscherRecordFactory()); + } } diff --git a/src/testcases/org/apache/poi/ddf/data/Container.dat b/src/testcases/org/apache/poi/ddf/data/Container.dat new file mode 100644 index 0000000000000000000000000000000000000000..c1b5447771458049d805ebd5661112bbff89c728 GIT binary patch literal 33627 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 literal 0 HcmV?d00001 diff --git a/src/testcases/org/apache/poi/hssf/HSSFTestDataSamples.java b/src/testcases/org/apache/poi/hssf/HSSFTestDataSamples.java index 703551f88..0e64637f9 100644 --- a/src/testcases/org/apache/poi/hssf/HSSFTestDataSamples.java +++ b/src/testcases/org/apache/poi/hssf/HSSFTestDataSamples.java @@ -172,4 +172,28 @@ public final class HSSFTestDataSamples { throw new RuntimeException(e); } } + + /** + * @return byte array of sample file content from file found in standard hssf test data dir + */ + public static byte[] getTestDataFileContent(String fileName) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + try { + InputStream fis = HSSFTestDataSamples.openSampleFileStream(fileName); + + byte[] buf = new byte[512]; + while (true) { + int bytesRead = fis.read(buf); + if (bytesRead < 1) { + break; + } + bos.write(buf, 0, bytesRead); + } + fis.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + return bos.toByteArray(); + } } diff --git a/src/testcases/org/apache/poi/hssf/data/44840.xls b/src/testcases/org/apache/poi/hssf/data/44840.xls new file mode 100644 index 0000000000000000000000000000000000000000..66dd9185ea57a7973ab29dc8a6421c0bac9392be GIT binary patch literal 16896 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 H&h`Hf@o5l7 literal 0 HcmV?d00001 diff --git a/src/testcases/org/apache/poi/hssf/data/44861.xls b/src/testcases/org/apache/poi/hssf/data/44861.xls new file mode 100644 index 0000000000000000000000000000000000000000..bab82b3135bf6bc3e318c5def705fbc55c6a5b3f GIT binary patch literal 61440 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 ULD+%7y4vdewmZLsG4$s6e|{(_T>t<8 literal 0 HcmV?d00001 diff --git a/src/testcases/org/apache/poi/hssf/data/44891.xls b/src/testcases/org/apache/poi/hssf/data/44891.xls new file mode 100644 index 0000000000000000000000000000000000000000..30082aa8ff805b8e0fe307a770a55fff81335fb4 GIT binary patch literal 432128 zcmeF42Ygh;-u_QEh0r@O7F-b&r1z#kNCHMlf}z-zO|n2R%@hPH_U`rCd+)t>5qtM~ z?Om{Uv3L1D&pErZo6Y38y!U-Cb3gZgH$Lp1-_AK_zB9j>Gi}G`|JHr=#W!!f#!Gok z_R_sS*R}VulI-p9c}9B7(9QF%4S62^L%4P8;=xwn!=cb&(BaS#5H95*$bVn|hAHqz zEKbbU3@8&~o-?nrq4rP*s3X(~>I`*(xIwCNdP9An9B3mb7up!w1lknZ z4C)JQ4uzpDpnlMnP=9Cuv=y{9G!PmDZ37L4hCoB1VbE}B1T+#F1&xNrKx3hC(0FJ9 zG!dEvO@_9Gwu7cXQ=w^49yA@whYFxVXa+PB+8!!`c7S$-Wh+7sFf+8bH`MWAx10;+_f&_ZYtv>2*_mO%SJ)ldyo3)MmO z(7sRu)Ce^}&CpV4KWG`W9NHf`06Gvl2s#)#1UeEb!I08S zcI)*yExEo}pYZ=E%zy8nNP&NpR{TCW{o7}w6lZ06_jZ1UMcyy_Yf!7-l<}Znx4#G5 z9`H2O4f&DkitvVdP50vvNekir+offBX*f>zUc}KoX7rg?Yz=e*%t zMxp|H0%6u2a&j8paFfimJQS2F6y9o7KrOySfd9L1`+67QdQ-gn2nMxFn~w^j0To3A zg*AeE2O-uS@{dAH@#1lBeT|L%yZj@%_#Il(4NE>3sna!WF4Cn2v1~+@QiAvW@Xku6 z#*Z^&9HeFZdadI#%v%fGSbWy=noOqzLF;R79J^-xu67Fl+x56(-;7S)Wqd@=EG#K3 z%bV3Vqq}!GowA@{cyaOYUGbVct#3v*t+f_H%p1{mSQE(dKn_YJHtBt{;ingX?7Z_w5IJpBCXgln6im z-*;~jK4D+5ljp}XxK0rNZeF)m*NLUW+MQCa6U(oQTE{=&jkMqmu8(Hs&wW~?k4ev; zXZKEyPrF2VMv~LPgkKwp*@O58;r;8k2%qhzKhrHOGR0%Q1mP3*omyNcDIfbJ-#55E zuhYB5_1pXLL^Js=KD*hpP56nQzdyH3AKuqyH(bZq!qcz%EDYjvP6qvl_l>1<%-*3d z?TPDWEev?$;jak7{~l=(KHx$V|L27*uAi_kSQz9>aGef*{0FuQZ{mMdx9)!Uq;&B8 z@T%_J{rD%@Lo8pPbZ4%h_cOd>P%c<$BOkv{wOK6Vy+)tr?ow}FY<9h_cX-Qgb(VI*7mOc z^;_G6>%)$AZ176>f4YbJu8pNbtlW8c_WJhkQ|(u@VZSV}-_$ZaJv`Cv*I~NhzROa> zUlrI7OtoJT*qc)ASGHll8uoC|TlawzylM2?jbh1==NLxpRk*Ao zTD|x5=BnzZqT0Rpo`LshLqVh|QXXlH?mZHt1)aQJxLH>uCx;HaE!O|!Fi-D_zij+{ zj@~=QO4gS9H_loMNA=RsQbfWbo`D!oig0|Vo$sT)j%zu#@_>J(L(|$L|5l|Z)jMQ?wEJKdWeZS>fEgN%%Nw#E1#QM~(fBjNq8qm4~# zYI#)v*ER4&SfWG1OQQ|Qsk(4O-4aBe*W|@a=tym(AyT^}Qdt+SsE!~bkcWPPHHH%j z7G4gYsIFS*7w_+C2QmHpNYth#U${PsYt-YK;YjtO=2{f@ispt!ye$tmRn-TjA4zYn z<)<}cgn0VKhH$hx+z@SSu5Lo4klb-10>t9YT4X8m1i?(wBwe7U8QIrZAFZg0RQs8V zoBPpi;EBx9Xhl;zyWt}B)sfn&YWMZjvLWGmBqVa4(L=TZy1qFI=R}+< zkPSW{(z6cC@oLY%z5aku)aNJF@)c41vZ4ML#=)8XNNLC)48UmKZ0O-!;_4T?bV z$i$;Iu(1kmhx6dk!I?M-r#4j~X0aUo^dIH_5Z_xWqcv+9V<~B18{vm}%rOAr%1jC4 zeF^X&744ahvzqGYl_(}i5Z(dwwM(P5er@GPGRR~`H#rND%)SZdFqKd$;o7KOBB<@E z8l&IPIa|0?!jtG*$X&QhWAk!^s;g}HG7@A~130Sbj|;1+1ILdg95OH{6udOj7l{(y zAC*Up9zn^08K>bQ@S~=PpU=a>Wzh=MXRrsp zf^Ypx`XQ6NH^}@bXd?|3i>sEx%c@W@G(=d;5w7XWCjXfk_ba3@tbVloT&!f|Qt4XM z0AYA~c@jf(ud zSw%C7^6@OeW2^YVU5bkH!UK`W_06@2+#o#i5)Fp%+|s#uv+(3gG8I2t!c+3+mCY%| zY{Trjh6)ss$dYKd=F8fu8dP0T#IvCaUeEz2nyS`+7$wyt184Jgt*nU2fI)b+m6oIK zTpBB=U2&n=W%CNBBdT+XhL@BM!gHsjv=~oQJWBIs7tWehR8Sf`0f*tM{GxdUc?EcC z7ZlFUE1R2FTv#%<6t^mB$7&&4*(qvXuIjX)0o_WifBDm#@>Sz@Uera_TiU^}Ka@5%IqsEU5 zAjS+Ii}A4Hs_K0rc%S)QO>}<-89jF7xG^J^?t7~NI0O>P365T&szy=Abg(iH3 z7S6aaBSzu=v-3&{$eLGMiR2C!M^N;8!Q6nPt=%8D2+yo)0AO`x70w$ye*CBj6UHKz zv&%|nmKDw^MZ?CVB+{=ex*RpuY}5(7%ee6)M~@tfAjNsJ3Q9}RM8ci?TZ9YoT&-qm z%|T|ABlYs@@bK85lq1KD9XoQ&NMseh!?Ps6uC^jtPyb*7Hp4BLV525Zm@s}MIrB?r zm6jCc4M0pRt!I|I;E@!23T&R{Q7WDgTlLp>D-0kg2Gwh zxn+6N^L8i=7w47C%bSJg@SM5H7o1&MQ1)zINf>RQyxFr*`pf1NhV#O6<^|<=NO(5d zl(S01JIyP^sbQq|?9!4sdDGF}L_zZJP*gH!ZrQy2xd;d*zX~by!!yFgMRSVrHm4Bx z$S*A`D@3?CVMM32APC7oh4?OSPALMEAi8BmrNb`HNSlQ*YmQ@Ape7-mCFPOT>h3#Mn1ORL8CrKbs@uP2L*+yWkoX2w4sBI>8--Fi}i= zhS8)CM*ex{I;a8vE`f&Ob3C4HUptsDXh0llaTa68YgG8L;+4XfYz^+;jFEcApQQb~ zJiJ%=F=ZSp{5g+)eti02%9QgTRhX`M`$wftZGb|HX~G>AM*-) zZosGDh_T_d>SM>uInvwXAI8LM#3zWSXOQ*@Qsxjng1NvvYR2a#oD-z#K;On#2I*Dp z&xZu@=Xr5&U<{b!@e~izTc;RvyU@QvvbV&2fmcs>6@7`hxYXyQU-8@4{u}qLmV7mc z7t@XBw8g^j6CU4y&+*jf-FUwWKV1^}7xz}?9ZN(la)no59tLseC#FH2pWln{J!iXE z0+@S28DrkZ^M*Md_oRBnIg0Q>8Z#GJj(ASOKLXFL^O@*7%p;c2AZ-%9MZa%--5}5E z@f|%cjQ3jjKjX#uEnbTyAjoA-rU!nOTzfFKjA5hyFUvB+1wPC)X<3#S^Oh--nrZ`a zr7B#n#!ofgk7u;3u>yY&?#glyq**)#gLMDY#5?YnL29pmO~gE6?C4{>7fUcd2iFg3 zy4E?{h?*eo6%5@n=0Q#-*L|#MzrkICbO`Pk#u5kSA<0<=Yh!84Sg?jp&9%6n(z96C z#ABR@zpZXacnNE>pcYNg#$yurHEV-}Uy!d1@nEf#sKt~0GbjNp(}8aVDadQu%W#mF z{5MG7Am1BshsDU>r8tYe%baG-NB@dHpMvW(d0)cLdMh|i^?RlYQ<5nY50jjyiIn?C zd6!&Iwe%&%ECMenM?3@XW8Mbj@#kuC?Lz<4j;mlM8|JCXW{QW;#o4tHcJ zpl`+Rr*e-eT#5VACwLy~)0Wi9wRoz>vlKDqlG_A9zLB2KkVH%qwHi~66pVXtcHqOz z&v+S&m*1s^h2I9(4*aQg9ozcbmY!;34bnaSv}Fpg=HrzZd!`EO9(vrS;Swy2D#n7U zGCZX=LyzVBG;bD1yDBi0!yY1g@m0}=rl8keTUUdAiQngKZbXw7gLvp-)&5$8ksse$ zf!<+tBs>tk*xE+FJB)TGyXf@|RS1MWbic(#2?R=JjxE4AwN( zqhsz5A`BXWVM2_9vbT=G5Pyu37iZVJDT)ECMmD)Io>v{-9XWQR1Olb+Xv;@vsceRO7o&+?nb4LDPZFj-V>%HEj2!V+N11czk#+ zD;dTrxKHqKDeu3CC;npqRmRZT@1ds$v?H8~b;B3|z%%Y%eSBa|z^%7f9K@A)KhAtGZ`FKZ$W3ux36aOY~AX|)r%x7YtGmkTk zueX&L#on_>nz*0mV>M?P608(e+5a|aYVg2iW0yY2JS|63RH2jv4Uk|l8S_2Sp$VEw zY-%vsPlCWmu(y2v&MJ;}Fx)BRzP~3Q1KIPXBFNgwN^B>)T&s7#f z7JuG@cMXbYyoee4%a`~R_n>%NrDgvw@S<^87(77q zB;wUoq6HfC8X0p|Sp1&v$;B{HeCe5NwKSkw3tAwFDzIfSrXROmbc0@U>kMa+55@=L zy^;vrrDZxYHP}XKT{c-^G5+jBwaB9-bmp|MdX~0xkRXs+(FqMX@b5uEgc}}aD0e&_t zI5v(5a1d$@AH?|J4|KN}><{LThP0R#;y^#f`8gR@RU4boiOm&7Fd@KkvtarT&rLL2 z*qFg^AW1oQ5{}}mMQA=CSamRw6l>?i8yk^W+&N8%p;uh6Jle1ZFclb3tZE2jK(`KK ztHI1e(DdO=lPCKyB*|e~&JOtV6aHjhYM=5!Vo8dX_)NgpdVPOn~hoh~h z8yl+@)%tT~eyjn;LB0Bh=4g2&B}jw6Zn6LeiuYd8f(ZXz5H{`=n<1@hi{LX+u zY9gIQYk==ff#(P19fQvm5e&}9Is`3h3zR3;5DXZbXY>cZW3|Vyc-$TiVPZceZY}0^ z{8$B!>rd`AVje4L>IX#*GYOmuL1fr0Hx-tDYgP)FRmHq99Nx@cVvY^#0(5Gn7?k0lvlIs6Dx4&+~5Y)U)EwWBceO+<+x*GeRXsZze06^v}xq@T)#q|8H-d>ycSlW3lqWJSp9QGJvM!Tg%YTc zsyIDsR#-$E6SZp$2UTl1x_!7nQgUF)MHH7p9pqPchyr_o--Y?0K^L^apXW!-1jq2x z0hJkI8dPM#yl%_PLmaReWD%bX%aJgqO07()?c?$B6DSV79C7FDe^4WGDM4(-6CN>m zEaIt12TtT4To7~jwW&yhe1FDtr*{sN=t8XD@ZEsd4UZc)Al7W)>I^Vo z`2o`=>3Yl(_;3_uJE+wBW!hsp(NaL5tBrHU+H)B%cZ)w)}L6}(ZAe1Z2bNdyU zpYs@d@V(eH$r4MUP$&_EDanLH4eTd-HCk`}a7C@ZnyA$>kiaEl^+6k67vvOt6{U}L zWl~NDSrf}?Q!5U`hz`m;$`#6UP@6_-zpKIOAJ!)RN-NZ&e(?`;;ZIE+7e2)njL-{` zDtJ_Oz(Q_Cu(*h>;8$;KcKL-Cp%-F(0EccW0i0N@lBlx~2ft+*_ah88VG6i9K3>zJ zj%k^fsBogoumTNzebDCxxT0u4#4|~cwZ4~Q|%UN@9lxrhI`{Z zg7>kUZ;!Q_;a0IFmGCgrew%}Q3&x$A@nooCO<7wv2tTZx9SogeB_W5(hVCB(!o#7W znWI*Tkv~>-LHj&*Cobl|`oz+t^@$y^m=7xoG1<%o9GKLOtv>9H1r=`;7nYU!lgdT; zMVPOji>Y8-B4*E<&KZ2nQ^WLZNkLIQW{~5i99#!;(TURvXJ87rXnyIOu9&JX%rC_X zjDldv$p9=R@MADEZ`QnGTn^&~`7PF@6c!Zag$H15W$ebYO8GtJp$q3=;cd(N6d{tB zNiGWy06>dnmld&Pmss{OV*KzCV}_5IfRVpZql1sZx|5(2lh?cqK7rZ(MvUiRnQDaP zKHdw;74jKTiDhxuVSc7p`O&&lDc_(3`U-3sS_ z{S{vyPu)7{^FVxlaqEL#*!rNarw&T`d@^Slc?@vi*IY$yM$NY})12qE*jC63dvX!pXNiQ^3PjUPw~pUGc;^-6r7 z#kD-W6UXiNYqBm}xf$G#ONW!gw@2(R?2-uI0r5<}KT1woq+{^C7wY0)r#BPI{{eD# z2*m=g5^pi#y=nLfC2c$?@%px~@uU*uhX0C%OSjmVXQ|=9*vbaZRyMGuZJ}= z7u#}e!FFmm6zEnqdUa|z=3lCfK9Ooejn>MBa^K3ves$`(s03QsIEp9Y^uMT#O9-yZjaV zv1L3r_G=(gurAp+W)I=_2`ndMO2em3Q3H71{aA*dhEJP$A613$EZ%BK2sJ?vCJSR{ zEHMX%ct$%f>~CbS(mMr5+1?iaHsraO_F;@!Gvu(|@^g^2dys>y3;VJDLJqR-=dWV; z7jTAf#+IzjV%KPg(#CR4&d|RlCv$Ot@3G9|bZ`#v^C%th-OA4+rUq;Lt^I8=f>0Sa z3hbE}Zer~d9A&}IdM7w)=MD0=&mes|=>J{3oAC&8nO?gMp5Dx%Zt&bR90mCq&(RE% zqbt4B5P~`S@5jNC73AVdQu`LU)V@V7ar>x5S!(}pnoI4m zSx;|&N062H?G)RWT`}UF{MXK}H9Gj=*p}lHy8~LLy}`$NvXB29NIrv~gR?sNXR)=% zvpV``@gA&)_>>sx_ia{UhMK*MVczhhzu*d;{41~>$SZX6ufQW-flrp9{$@+OLgzNF zz&7p(w5oz9cNc^j;a`h;os2~79$c#na&i>vy5K%tkdq_*-;(h@T~HH_LR%;PT^7D$ zOE>tg9lm25lJV;TKgSOj(NJ26HeeTc%Rl#Q*wVi?pF?BNmJ06F6)_*{Zz09|vWGd= zAKwU$(s9I=XK<8(qp&&3#N6)|<|qqCaq6zfr*Ztk@fcOMRz6(>Ddi@7-8{WUrnXY zaC@#an(xaG zWQQ7qGkt4T-%77nhul!tWLs|bj$Y1SuLMIs{&?tO{8R*Mj_`Y&q_ItZ&^v*mQ~e65+S>JIa%IN|CKOy zVC;D~9ajs|C>_7gWs42i>ypwa9cfgFBrNsP)4gfCk?N0CeKWo3pw7v}?{mTDr+k>O4InbAmF#T>{Dt>(e+tNMn5IEC=j z9z>{~8Te;B0}6-0znx4+QYwsax=M?2;Px5>-x?vsr^ZOvHk;4O$Lv>>C zlF~#<#xrEzdfqSB%jtm&q>k(7;^)V0T)9OewF_m!^$=ke&Teqy-gp!SMUKsv-v0h> z!FTO&gfu|)u>>JIV8^z9{oiE2@UuIgD2&}(6h;nYwNV)9huz_WZTMmEb({XtB4xV! zDKj*dGTr@@8D@@{GQ-VLHjYN%i0RaQgQgQJ{zN*lgWKCX7i}Gqa{H}#I(^i#(h9zo z>C^)coi@r*i*)MYr_;z-TJ-SKX_Pr)I*m3*$?4Q%gQgRk3W;>;>8I0R)KN?bZcr9a zr_WlaQ%~(rJ=Zgxdiv=!CYBaG{d5{@j+jp4%u#YW_1vK8#KzG?f5%2PAh@+vZ~w8* zbn2CqPG7W6r(QapdaY+Vahs&xcq|7sQZGN9CYmFr6Z4Ao6F0a?l)GLVG@Up+nDD3G zE$XMz=E|=QaY_|T|aH4(`lpiOs9?fbSjLc#YTQQ%`itScQefq z(`kDgF`YKrpy|Y}VMoT%At2>zPiuemWJ!(jwPSrya}@(`iR@#B@Tl z+E1t44Vq3HQ9?0f{~3@>I%PuMzO>w)-sDg;jNjm~G5*C`k6ww!cy@4~s+o%sp<>v%Ukaq|D> zKG`{G-tcVPEyKfbLW{m7=U+C3b4>9I(5Aj)v`P$)ngnSj9fPxW+qOW9^4ITR5U;og(P_8G8Po z1115HucQW8yv@;1)A!T#Tz0XcYhLC(YfnMHrJ>(a({HKix774oI-=)uLeTd&^!+t` ze@)+C)Ax5o&nKdwA7JPQX!-$~et@PQ;E0~jenG#Lq30S1QSP_W^jm5AtsK#_l_Ti4 zHuPI-`mHtn)|!56NAzsZ3HpJCexRlwsObl4dM-wDQRlPeD(D9p`azn0kftA`=?6KY zXB%D6Z)52B8wFABx6$<5X!>m&(X%%q=m#5m{x(C<57zX9HT_^m^z3&D`XPpXh^8N+ z>4#|gA&%(T!xZ#G4gF9}KUC8X)$~Ih(X%ft=!Y5lVVZuJrssYcVr+4kBYKV+2>RiM zez>L|uIYzs`r(e~IX)ujM;Q7Mntp_)AED_-IHKoBkf0xF=tpY$k(z#_rXT5uo?~8u zew3jfrRhg$`cayGlp}hM?g{$QhJLiBAFb&}Yx>cS=sAul=*JlPF`9mirXQo}$2g+r zh^?R>Yv{*n`mvgRtfn99h@NB9f_|K#AE)WZY5H-Rew-tEj=~H2@rHi9rXR2A$7}lW zj_5hxAm}F;`U#qTf~KFK=_fd%=gf+rpJ?bOYIbQeV(Du z)AV_oK2Ou8CrQXYDEI^9_Bzrq9>(`ITM zLqAi~&(!oYHT_IS^lZ-w`t1$<_L_csO~1XS-`){DTdsn>$j}#Q`XWtVr0I(s(X)*% z=yx#mJ81eHH2n^meg{YN?2QQe9S!}Cntn%3zoVw#(GflSU4nj=p`WGcXKDIbntqle zdiF2{eX*f0*7U`izF5;2JECV_TF{pm`VviFqUlRCeTgG_jv5I1QbUjJiqyI>?9`)1 zR!cQ~sUv!hj|lqNhJLo4o@VamG#me;jQ{Ush$*U-<^^m8@+TundM5j`gf1pPci z&rSR-%hUOwWx<}O>E~(sd5-8g9U|!G8~XX0e!ixkuj%JIqUXeqpx@ch@2u%}*7Q4T z`kfuobBasQ?_%h8(e%4$`du{rE{^CqnJ4IXHT1h``du~su9|*VNA#Sw6!g0p`rS1B zZkm2KO~0EXdQQ*^`rQrv?wWpgO~1RQ-`x>Cr=l(NG;>>@J-l=A=|6j0pFKhG4v5lAJOy? zO&@VY&pJiWmmB(WO<%6*%QbzuBYM`Jg1*AgS7`bQO<$quD;&}DIU(pP4Sl7iuhjIF zn!eHzJ)ekzK5FQrnm(%OqnbYIh@Q`WLBG(@FVyr4HT^8mt-m8P$9M9((5pkHF>muUJW zntq9ZeYGQc_Amu~jiIm6 z^fj8kM$^|gqGw-P(AOIJT1{W8>1#E8ts{Dl8VLG2Ltm%q>ok3xrmu5E&+!pKUvKE^ zHGRFNuh;bTj_5fOB2lsBYKY53i@V4->m7IHGQ+DZ+1k_v1viS z)X*>0^h-7UQcb_q5j{uY1^s@8em_mWpQhhW)9>eqp7RZYewm?PrswoGlad2N?PTH2ndZ{s2vX zfFpX&3kv!J4gG1^q#W{vb_%kfuLK(;wuBo^!o|{$N9Yu%5tI#M`-#Z9MSWMDCmzg^hav?BQ^bzn*K;f^nCUU`lAf}QJVfJ zO@EZ8Kgtn3TRDRMXhVOrraxNKAFb(+c0|wioS;9(&>y4ekJ0qUX!>Iu(X-_$=#Mq@ z$7=dxHT|)g{#Zx!Y@-YM;|%?An*KOVf1IX2&JjI(BZB^TLw~%cKVH)xuj!9>M9+Sg zpg+OTpP=ba(DWy0`V$<{vxh0@Pc-x=YWfp3{fV0XL`U@OOAGpw4E;%({v=I*lBPe& z5j{r@1pQwOJ@&0pzfZ!JEw*iaxTg!dlGsWGo%M9wti}I2H@?rtUmVeMd_>TnZ0Jw6 z)6>jteNOiN_TO!NPPV%M&D_?9X|y4>^*Pz@YBY0OA4cQ9+xndBsK9aXQ53kpngaLN zwhJ8VlfSm@Yr)sn>V`hRjcxwwh@Qi&g8mdke~P9*Mbn?sb_pQ;DURqlpe*Q5HT0)y z`cpOisha*&NAw&z7xbqY`qMQ1X`22tO@EprdQK7u`qK^l>6-p@O@F$kKiv^Mr$Yq& z8HWB0O@D@_KSR@>;fS6SKZ5>DLw}~GKU346sp-#jM9(QML4TH^KTFe}rRmSo^k+Gu z=VYFsKiklst?AF!^k-}OvmMcM+EUP;W9ZM(^yg^$b2R-qj_5f-E9lQP^yg~&b2a_B zn*Llz^qh*e(9_Iqea`dV#Hau4ZGFx&IeebZ;q!D3pQm&9JV!aqg#~ToFt_zN-_W10 z>Ce~n=WF`&9no_Mg`mH{&|jeGFVOTCX!;8r(Q~ngpuf=2U#RIX)btl>`U@S?cE828(qelc#m3>Ci(nI_ z?;=zBF4Cp%B3=3}(xvYr2c?hnENO!NVnctiroULzU##gbc0|uQMbKYj=r7UqmuUJ+ zH2o!x=vjLT`b!P{rJDXyO@FDTztj;upA&-qGDClvroT+nU#96Vb41T4qM*Or&|j|U zFW2;!Yx>I_(ev3a=&vyJS7`bxH2oEt{t8F*Y~=|0D-HdXn*K^nf2F3s(h)t|bAtXV zLw}W~ze>|zrRlG7M9-G1pugJCU#;n{*7R3v`l}t$vyCq3uQBx3X!>h3{WY5Y8b|c( zjR^W{4gIy6{#s3ct){=$5k31|g8n)~f1RekPSanf>92D{&mN|rzuwSauj#J`{U7Vr ztxFXbI&--%7rk=nCl^3+86X$KaY+~#Msax%7s+tmp0n4S!{tmS=kqwL#kn2MaB!TT zqum_yp@3U!0JLp`9LP%o%A)CbCeHiB}Y zjiF7TO`*-8zR>1S7}^5r2W<)UhXz1fL0dxup+V3#&|qi?G!z;J4TnZRBcV~yXlM*H z78(bQhbBN1p-IqWXj^DIXbLnHng-=T)1iE*04juLKr^B3p(1DpXh&!kR1B3srO<3> zC#VdX1I>lzLGz)Vp93+fH^fpVaYpj>EUXcK5tXfvoUv^f-pwt)IUTSEPz z0nk>^*3dv`5VQ?67#ac%g@!@Hp%KtXXcROW8Uu}m#zEtu3D87n5;Pgw7TOM)0!@Xc zL3z-0C?6_-3ZWU$OlW(k2-*SK5t;=RLnTltG#lCpDud=gbD??Ad}wEA7id>#H)wZg z4`@$lFKBOQ0ThACp$e!Hib4yaMbKiX3R(i~164ycP%Tsk)kFJ24NxQ01T{lTq5Ys` z&~j*h=m6+I=pg7|=n!ZHbSQKfbU1VbbR={XbTo7fbS!inbUbtdbRu*T^cU!4=&#Tz z(5cXA(CN?_(3#L#(Am&A(7Dig(D~2>(1p-N(8bUt(5292(B;q-(3Q|t(ACg2(4T0h zOi717K{w#|M(8H!X6P2^R_He9cIXc1PUtS^Zs;E9Ug$pPerP510JI8v5PArD7?Rp>S7b?6P~P3SG?ZRj27 zU1&A526_*AANm0L5c&xE82SYI6#5MM9Qp$K68Z}I8d?i|1APm92YnCy0R0I41pN&C z0{sg82K^5G0sRTBgVK-PpP&70yc zEx*CDE==drY>t-S08BxDgQma15k1?zg8oKBf1{?qQPbb3>2GvI&lbO+zsb2K5Yw`uy@9MN-3L(t!D=x^8bw`=;_HT~_5=sB7r z=4EOQ%{X?4mAx-~~rhmv0J!?-v|FEHdSkphO=^xhg4?Cjgb3)KRV(1^y^p9xz zM>PE-j_CPB6!ec8`bRbWqniFvP5-DPdOrIF{bPpyF-`xNrhiP+Kjw&@tsFuBxS@Ys z(?721AJ_DcJECWMPS8JL=%3K^PiXolH2o8f=-F}=^iLZ4CpG<(n*K>m|D+>&w$TOs zQ-=O2P5+dpe@fFo<%piW5kdd7p?_M_KdtGX*7Q$1qG!KL&_847pV9QsX!>U~{WFf} z*~1j{&l>t?HT|=i{#i}`tRs5%r3L+ShWem>ass>)!?8W5)&j%z)EF{jn;tF*wt=X7#P~dUePRbxpSAX7A|b z4E9Ru!u8G3%4m3Tq&(UXZmtd2*ELi`YnviVqT!mz^16m_Bs_A&hyme&epJi_GqZC- z-he^bx!LJnzw|UOH+z7Wvx!$!yQm@B7!Bt~s;d@ORYWSPzN-xvuUS@A6G?>Z=H+C0 zL;Y96*nzR<;dES$!v)XVU4Uk8>&P_P5ZgLFZ+A7Cxve9k@!xG7pLbN?IQS?E+}}-s z`+M63j=uKywtX%5+FISv2e`4}-yP9&m{rifVCY}a^e<@o7uqfXq<_H?JqMHp{fmbF zMNR*rrhie>zvzgbL+672B}4y`rhiG(zohA3azxKb0zv<>p?_J^zpUwB*7PqsqUUso zpnt{CzoO}1(e$rq`d1v$zpCk9HT17)`d2mmtD62*NA#TH5_$icp?^)&zozM5)AX-7 zqUU6upnu)azpm+D*YvMz`qv%NbJ|kSzhUU#(DZL;`ZqNF8;0xv-#(9Okx;ZyWlz zHT~O~{%uYFwj+8jp%C=%82WcK{X3fe9Zmm^BYG|t5%ljG`gb+`yPE!8P5-U~dM?Xa z4f^DzGPJF+*xs|)-nZDe(Lw6} ze_zwTuj$`+M9=4hp#Q+of1v3<(DWZ@`VSn@^NA?vKQ#0oYWfc~{fCDOxd zwVHmdBYKVm3Homg{WqHa8%_U>rvJtfJ;%HR{kMkxTTTD1rvFycf9r^zqkDq>J464S zrvFaUf2Zlcb41T^OhNy>q5odff3NAk*Yw{zqUVUMp#QWH4RWrF@UL;stm|4q~X zrs;okM9+CaLI1m<|6SAnuIYc*^uIfz=M1Qz|HIJ#q3Qq7^nYmjKOE6>u2<0iY3Tpd z^nYslKQ;ZIj_5h7E$G)7`gNLqou*%>>DM`+=UNfawV2bVEzM#JS#0>RQLA$^EVfLG zEz4qSXR&2lZ0#+!4i;NSi>;Hz*4bj~VzG6#*t%J4-7U5r7F$n?t(V2t+hXfuvE^87 z8(D0*7Td-a+a?yIRul*$zL@WVBe)1bv#JPt){i*e~9)VS1cCEltv=IihEs zBIrYgKBVbGnm(lILyqWKdkXq=L!Ykc(=~m%rcZZ7&*y}o#}9d9;fgE5x=aj?%u zS4Z?59})E341G5{J=YM&rNR`gC(t z;5hgw3S4(n;JUY6;5anZy=`9$zP463^Z{;c)7=q0hgk)E4@2KW)A!KyJ=!h-r0?N~ zo&(B)zNew@sp)%a`ktD;rz3g}oeTP2hQ61k@1^N`Y5HD{=s8Is=zAOb-kQF*rthuk zdpn}%bcmquW9a*6`aYVzkEZYAh@KNaf_mLFpqsOPZkH!q9J_>9^4ITWI<%9MQ8*5%m2GeLqd#Pt*6) z^!*&sv-TA9TN?T;HT{;FeoIZir6YPiCj@T1fTkay z=?7@~0gmYT>=*P~8Tzd>{Z^WOD^0(ZBYL)S1pU^Aerrv?wWi-%({JsFp6xk7KhV$* z)bs;2{Xk7W&=Eabu7ZA$p&z8_2Wk32ntqTYdbZI8{WgYv8%@8Brr$=>Z{vuby%9k_ z*w7Ew^n*41U`;>R5k31|f_{jhAEN1pX!;?VeuyJ__AmwgP(wdd(+}13LpA+SNA&DV z3;JP(ewd~ors;=i`eBafIcgy2ha39gntr&ZAFk<#JEG_Kh@c-~=tpS!5t@F4rXS&m zo+Ckmex#uvsp&^*`jMJ`q$7Hcc?tSahJKW$AEoI>Y5Gx)=sCJ4=tmp+(VBj=rXQ{8 zM?0eDIHsTBl>w=X`^ppJ3=GX!;47euAc-;E0|xD}sKap`WPf zCu;hMntq}qdd?vU`bma#CI)%4qH`fVN2a|Tq*mknm$}F}y7Ta8lZJxz8-(uU@V%x=H+tp&*&0^!S$W-6h z!(!XhV%y7N+dIX^6%_P8QTnEt(lR)Ue!8ZguIZ<1`st47S$hild_$kF>GL&xzNXK2M9=4hpf51=1)9D<(-&y^ z0!Q?GA`1FKLtm)r3pIVArZ03v&u71&pJC`{X!;qNeuk!>;fS8C96>+R(9hKLGd2B8 zO+V8SJ==4FetSc|y{6w@({Hcow|7L(maCvIGW127zDUy-Y5F2Z^lYOG`W+1Y4w`-k zO}~St-@y?*dn1B=M?=4(rr%N1@2Kf_bVSd7m!O|z=x1sAS(<*9rk~}Ao;^%KUu@`$ zHGQ$BFV^(Mj_BE!7W5^CzC_cPX!;UOU*d?KqXvS$)X-z!8r!x$+}MVFMQqt(+t!DB zx&&X#eKtxR(Q|x6(9bsXv+eXWb6cO;X@B|ewm!4%E{d`CCocIy+I~)3)HT}+-erHX;vm<&=aS8fe z4E-*eeiu!@i>BYj5j`jK1pTgtepgMutES&o)9>ntp3|0sem6tEo2K7Q)9Z%x0q19~pYS^)agr8g0at=wX(u-Gaswy4Fn&|+I;u`RaPsw}o87TZ1+TeZbj zW3km*Y;_h}y~Vb##nxc4HCk*<7F)B$w$x(V&th9B}{Jxu!37M9c z(?>OZRMSTt(ev3a=ocFLg_?e$reCP(7doP6D@V{TGW3fy{US}jNYgKJM9=n|pkHk0 z7i;>(ntrjSU+jpUEmuKbW$3FkeU+xK()3l1=-EaW^h*r=5>3BE(=XBVOB~U&HzMfw zG4%Ur`h7J0KAL_XNA&D>3HoY7U#;n@HGQ?FuXaSw9;TqLG4wT>zDCp6X!;sQ^z2Iu z`dUL@tLbYseXXXibwtlm13_PB=<76nou;qT^mUHtIX)uj>kWOqrmxrZ^_srk5j{tO z1pU5-eqT+$ucqHu)9>quo?~8uzQNEpX!-_C-=OIm9MN-hPtZ3S`bJIPsOcLueWN3K zj$;b?CPUw(>6Bs2rf=5t&5r0fHZACv8v3Q0eyOHks_Bqic?*bcMU4!78ju-J~Y*p9N;j<(p2vDl8a*p9Q< zj? zAED`w(DX+*qURG)&>v~&kJR)>YWgEJ{gIC7`Ro_;M;ZE~H2qPU{wPgU+ zhW=`ja&MNt*s7O@ERjdX5?h`o9=@>|0~o z)`uJ0u&;TnZ0Jw6)6>jteNImM+kdz9Ioa+4G;>=Y zrqPDj*5_nhQ{ppVAIUOSC&oK07X!{8Y)yZ*raxQLpY4dA z)0Tq%97BJOrawp1pQGu|aYWAvT0wuVp+8sCpR4K5)%52&qUTh!g`Q?^>vLY(oA~sf zy{*rACWp_{IeebZ;q!D3pXVrtxv-#(9Okw@=NtO-HU0UT{(Mb;z9V`rp%C;J82Sq| z{RNu-0!@E`BYG|t5%d=t`U^Gvg_{0CO@E;SdM?Yl2=u8-Z!WgjF0t4ywb(AR*eI_(ev3a=&vyJS7`bx zH2oEt{t8F*Y~=|0D-HdXn*K^nf2F3s(h)t|bAtXVLw}W~ze>|zrRlG7M9-G1pugJC zU#;n{*7R3v`l}t$vyCq3uQBx3X!>h3{WY5Y8b|c(jR^W{4gIy6{#s3ct){=$5k31| zg8n)~f1RekPSanf>92D{&mN|rzuwSauj#MX^w(?p>mAXvFD>YAF!VQQ`WrO;4VwN2 zNAw&u5cD@1`WrR`kM^>O`85DO@EW7zsV6jM}h?X&4&JFO@Fhd zzgg4Y?1-LYUV{D>Lw}2=zeUsEqUmpOM9q=QyUIzs=C! zrs;3f^tWmH+Z@qz#8%MXZs>2<^tWsJ+co{|j_5fyE$Hts^ml0bJ2d?rn*I()^c;m3 z^miKiJ2m~Cn*L5rf2SjQ&Nm49yA1tZn*J_Lf0w4e%Mm?iRs{XshW>6%f48Q;Thrg| zh@Nvug8m*ue~+fWN7LV<>F;qw&)G6Tf3KmxSJU6C>F?F__d25Iyr7`J&(Pne>F?9@ z_i6h39MN+IRM6jV=UVtdMBd)i`q z#$tQcVtdYF`C(4Km%ddFN+0Q2(ggj3hWys_7rq^p85C=d)kXKW6A3)AWyN`o}c=V~*(A$`SOB8~VpJ z{o|VcaZUfYBYL*y1pO0+{s~S0grURQqw=F>7Uf}PdcJ!8(q*p zW$2&M^iOH}r!@Ujj_BDN5%fKNuv2TrSTOV$0 z!@eT6Y_VY8lJ&EC<= z8SItRh3lK6mC^9xNO`m&+*}*3uWP7?);2|!M8h?a<#i3=NOl{iv7=W@hJv zya9u)hz!|Avh^Z0qY3((AMeV$M26~eYYg}agJk5zp$z3HIN$@EG}!y|?xea3k? z!@a`Brf4`)-CR>!7sh{;QBZyv2`{W#)Z7qh2uEwf(Z;4oxV}14TUA|+I9j4%b+zYF z{RJG?ueeltb+apL;r%N ze?il~V1LxG1d#p(NAw&}7W6L~`WH3*i<2b<|lNt;{CP6$^V=CWap%L!?SU>3=jRv7CkX;Z1a}M;kR@Szom2dEuF(} zIm%%!ENCN#N&mK?e_PYPt?A#^^lv+&=MoA*|Bj)5N7KKf>EF@x?>M68Vi7_AuAzTd z)4!|f-_`W*I-uvWtks}TU3#;|VtdbGd*5RFz+(H*V*AKq``BXp#A5r@V*AWu``lvt z!eaZQIHG5r zBIw^U^zUi<_cZ-`n*KdU^sGGv{riUgeNF$qrhi}4zwd~i&j~^Qfua9E(|@4pKhX3a zIHKnhQP6*A=s(o-A8Ps!HT{Q<==tmy^dA}ek2L*9n*JkA|B)klwsHjh$AF>(e$4L+|GB3BT+@HP!L{nwiQYfb;PrvKUz zJ;z4`{aQo6R@1N5^lLT!T1WI82@>?*82WEC{WqHa8%_U>BYKW`3Homh{kNL_TTTD1 zrvKIvJxBKh{db1`J5B$crvFaUf9Hsv8tFPi=rP5+A{dd?vU`dP5-N=|J4ya zXUhcrZ-)LiP5+yw|4q~X=7^s2f`a~cL;t&`|6SAnuIYbwM9&#eLH~!L|3lONq3Qq7 z^nW;_=UlI#|I^U_sp-sj*3n|?WU+O&*t%G3T`jh57F&0Vt%t?d z(_-UN#N_onTxGyK5+3S>(!44D&%Qk5(j+gP3m3h0>pJ-FnI3*;75p&J3t4{{$X|s8 zw~@cp@j{v2)Y#v4-iG*_>_>~H@1W^BX!;JCzJnurw%7%IM?>FH z(|6SL9W{MNNA&EQ2>MQjzLTc!r0F|p`c97M*$Wf&oeh0wP2XA5ch>Zs9nrJDDd@Wx z`YxKji>B|Q>AN_hXU|&DcQy3b+eP(qx@vlCH-gT!rGFgk#?aLfJ;x9PeYY5W=d^Au z=y4WaKY0ZR5c}2QyIy}DtnWTI{+VW5nqZ%sB-{UdpPQtUv2o77exI9eD!;oRy^`}g zNUxunUh!XfuI7Qas&6RR04IcBXkz!9dZuD1o{9(&@{`y9fYi$NI!#aKMnUa8?Bg@1 zC^vhO=bQWD_oDszeUSdxPG~@Ks7?%CQkvMdC%*g7y!E_au9wpT7Z}2QhQg7?D(ufw zTTz9LciOyii{F8A!x5l68N}g`m~ip##>KnaT^s=tF3yAXc5wzyb@7DlpK$TS$s5GQ zyK5KkZg=tS$u5q>x41Y`I-ZIJ{LBro{15*k7hkXY{U5kE(4DwAM|g#c_b@Kr!|vh; zkZ^Gxthb9ZaH@+ZZ2yFdCr;iVF5W}Ccn`aa_egeeB)-MPk<#&0EZ}F~#rff16z@t(;p zj>NaPI8r*EiUs`ayEs4mi(GuY?)QJ-;y`!e;+%gIF5b(ycrUw)BS6B%d9dCt&cLZI zp0NEBE}l4fgSa@hl(cO-8q6+s!5%Btws@~(7f0e-TpTGKPsIX$_FbGG{zWdnUibSy zaB-k-a2Nm3yTt3|_ZvMA>mKk5dF}9tpVROe-XD7pd70=bv#*@yzo%no8UFE@@BE&B zJmx#U&%z&%`Ofd#;g83B=l9w8<1yd)eS7@znD6|)3;uX~OIi=S$6xF4V*FO7&GrsY z%f|N%lTcTD>Y;V$Yq1I&Wcfv?0Q1qc@h}$DDU(4EY}a;~n|=3vag9)eEiN9w&73 zveyRj!vR3Mdg*IN;A2-WW9>k^Bc5xqV4~G4E^Tn9ySj`n04zWgz=WKZKk#FmwXB4R&X(Xx;bq+ zE-|X*CH}{oy558I2GjO+OClo~<@VV~fvj%cy`7(7*82I~33*w`RK(r8<6l1|z2C3? z=7r+&*;DuS?JNA8=NX7PVD+_xR!|GO2=|N1Mw zKAyUD(&vHr{NmOJy|DE`Ur!yB^!Z}1A9A=7S78E&ydh~r(mLI-pyM42R!@z;r+Gd6 z`@QY|TjBpZ5C3-X4)QwTOwS8v(YNplb-`6UufKo5@3?e^SMCT7|CQF&x6$!BdFy=B zw3v;wol?TlPf~3xGwI$y-U!hSuE(;OYGc_+wY9f|>tG4j(Gso`Y@w9+b@pvtTEvBE zkQ%P5B^*;GHC%T~xE_{pJuTsSS;F>?x!CkKCKl%Q> z=`{u3e4IghJKxSv^qe3B{iV>`IToV52|=F;LeNtRy;((Er37EcDCe7WkB&;^SMwmh7i1aTkUw^k%@$D=|c&_hGD_VBRxd znG+pbJUPP2PYygn{>M)aoWra7PY%3$t)Co3WM#Gq{?gKts^wAt|5eKoY{$+Qt7&FR zWTV8f--PL!IOd};(j#%q=j;RB6US^AgnK29S&T01oH%A9A(C`GzX_9cJwD09N#A#b zYec#v&hLachPwsFe2lPKn2Bd(n%~snV zh&?jcKshs2z@~%u%+tqqoEywa3vu=BNWg zR-2=aII1y6op4l(BNpN;q{b}#I~ggO<`u)r*Sd9VsGu?8S5#imtH=#?#OxAYp;ak+ zD7Fi2#5PG;)E{!kzWmtsKfl`F=CLujbjVzK;IyPmqwyQ-5BitpValbmylW6MYzfI5 z;}wtDX^a`#+OgAj2@c~k7vs#g*0uWN$_&eO3A@KyN!GY_7UV z(l?lkDUZ}v*40Mvb*n4yjw@$*rGB#WpCI1~y#jEoVif%ket#2D@G~Nq`RU3anXCM- zvvRY0cu48sf66d)XnbGKEUyfgZ)YMNcZ+R<%eTC$|39AaubRQDX0FO;H70SYybkrz{z1W{psl$oX7I-+m81Qt;%ebNIT#z9gs-# z{MYAGgS(80XI*GjXw`HqUe8O%mD=Gk?d4|oN&PaBCNpuhjwa3<#o|0N$g-?eS*vo< z!9dbu8ZLIRUM;^%Txt<6)ydl_mV2vX5!)$9-WItR{C_=$1mD0LyQ0IDy()WEP6w~J zv}A7K{Gx)wQaDIPZ=^xSD&%O3i8Iz{tX0drI^68w5RXDQV&^LO4qLl=^*Fj8O@(YP zBlcYxLh-?#g+@;{{$;Z+&A~;HiCpMa+3>-C?qf8t%il99K$m$%f*+k&CE zb8wblh;zK0|Hs~)09I9;Z2&$Ai-4lID{e7vNC302swhi95D*ojHm(UI5)A|c2;vsE znr>FqT4QaswzZg6tJ1n*TdG~GqE#DPt5T`m)V7vt7qx2s_q}Iw=T7)$erKE@*hEiq z=FPd^z4y6iKPSV0WKv@zE;3^)H>aw8&XW3u-TTb*thLXLE&O`*$i7^|Ucz z&dIJ@vkT-ZwQkm1qmE1B9UGf%@k!@SNw>rBN_XZz=#!Zj4#NVrGkixN@0~ebq00uf zHrOXA$K;vWkt>wF`H$@w%QksABUEN)Z}Rd6k&`j-t7BW-#>0~x1YQ*@UeyZZ^{W+u zmzalVFu7=_En?0>i+>PuIKE@f$b4-$lE)cbo8>WnsOM4m_4!Hf-pRPJ>^J*;Wqbe| z?Q5pvoZT91;j`peAL~zfqfdZ~U1jEgm7l=a|EW)4Y~~Y~23vjtV>7ce3^Sj=SoJRg6|$p+Y?_cw7qS^b zc8rkC6tZK5>^LFgu4U)%LUy8%og`$R5VA@kJ6Xt15wcT->@*?c zm4)2*!3CM~UuEW`1sCLTwhaj`F>JJ_gv~Ewy(woaa6$H~6=6Z<8gE=?u^p)v+i}KX zJI=n?l8uXKW#$YG*~Z)0a0WzTW31WO3TN}E6^U(phV4+rHr`+x?`Gp#tTJV*@z9B4aR!2!B!xfU#$pi^7eN^26C)|oM1psa6@v{Uzs^$N63jbNNKUfo6lKn zRVy6QqgIzePRu}#Q;-u4$cb)9ZZ9h{N0@{xw=KvxO5A9U#5#+yUXa-eg!HQw0g~&# z@(kNV#a5nSD@Tj_f93d&c_Z`H=}?C2KkiagCg)}*ehn=i0j6L22oMjK)_*G23-X6p z|M}I5z$P!qlQNJzElSo&2IQn1$WHrzgyeo$WwKmBPPRerf>}#AQ^8n6vK2W00n(#Z zmqAX>Kpv$aCubn1WNh|;lyi>c&TD0Ik%BzR1}QDbc=I_6vTB7xderI?NM4pb3Xtr3 zg6C5AK#O!8XK0LL5YB_#$FEE-RdiEqbh}~p66Qh1dLCpeoX(?Gm(fiz=y)d5LB~U( z%H)%ZZfb_E!#N*ml4Q<=?IF-%mCsmjKG+JR^Q#qsj`P@518S-pipPhQ$)^?6(YASP zcL53uBx5~~u@wmAS1SV4#v2UM9c|DZ?WW^lbY*gtqMK%;8z|E;)^u!z(|OeDGP-F7 z-845H&o)#h*C@K_w#9QVfewolW4(B?6-eh-D*_$ovFQdBFX%hgV>|^@nOv`+X4vL2 zX+2hHtjAO<5X!Gs1Som&oMF(-aMSTjRb_IcqC3V$w{tVgH*+}FW;_90ncSwpjoW9F94RmkuhEJChV_mJMuQ z8H}+8V=ElYqgG_FSq9iFHyE$0R3`T*u-P`SLK%#)24gE6%%fIhu-OLKY&RG$I#nk3 zDzFM0*nTn?V-3buIG9JR$Y2!)ScMymSL7;_2Nl=}HZc52inNtvtijj{2lJ>E8SDfD z>;yL$FIQIL)~j?KcA^dJ02z$224gE6%%fIhuoDfi6Ww6EmRp%TtiVpPfgLD=G1g#g zg@bw2iVSv=0d|rbj2F5qlb$qB40BKs+?!U5Fys!LSexz8PyUw!*mDwAI+uv2Yd zMKTy;4aQbDm`AP1V5b^jr@Fy-=TT+yyaGGT28P>_rA3ml24gE6%%fIhu+t2%)7)UZ zEvqv5tpYpU1~ya%W30j03J3G36&dVw1MGA=81I=p17NxLu`#O>vT7mYU#)k3>|7zM z5wcn#J5$K!30a+xoh4*v3t7F8%@?u-Le?N;3x(_)A!`(}MMAb%$d(A%xk9#7$d(D& zc|x{a$j%qC3xw=KA-hP(F7A}^dO7F6%H(CWz@A}SV28;IEMvXEvK79-den-%z@A|& zuxHp8Sb}lktW5r-z^ZIuxD#F4Brw)sY=wh))QSvNWq?(=!MNtC#13t`TU%`d!_E2< z7-J2_RydePt;k^223WNljH~s^LR z&9#A*%3zE&7+c|B9>z1 zgPm!Bo#_VSem2gS<)vH6c{Z?-G8khG##T6(N3F>z1gDo+@mbk%q^{X;DRDqpq11p!o7;7-L!ofUhMFu<906W(W#!G{h z$q@={sSRwB48~Z4u@w&HQ7baoQUh$M8;sXYE0YNdY?%#gvJA#pgRvD3=20s$*fIlb znH!83l`E6U3hX=^*ikYVV-3buIG9JR$YAFgVCT8Pc*VUkIa+}&w}DNO!5C{Ww!*&L24gE6%%fIhunP>Z3uG|H>{RlCWOb}gC3#N_I+axR7uxKn zfmJy1!C2d~6>jfQE3*BChW&*=7G_r3b7tiYL+DOYfn8(+n+^*BjIjn|D;&(DR%Eb? z46utJ;{fB`ROmKRfn96^n<0ZS)?jRfgL%}740f>rcCj6dw>35atn=-Pmk8OVLUx&u zT`pu-2-zov>`EbP7P3zX*{6lytp1tGg$ z$i66KHwamakliR`UlOvLgzU>g_7x$!xl_h99OplD52+T|CffpgjJ&`y)(b3K;R~!s zt;h>(ld-@y*%w%Xap6SAkP7S)8`w-4jIjn|D;&(DR%EbC46sYwU|e&dOGpKFsSWH{ z8H}+8V=ElYqgG_FOAW9~-C$g;qw7Znc9{+AI2nww24gE6%%fIhu*(dv%iLhx=Any6 z1$Ma&?06ZBu?Ax+9L%FuWU$K(u*=%DzIi7*aB%p46skR!FYs*4jL8Mr)^*-$zY5%7+c|B9S|vujrOhfqljX z_6Zq`u?Ax+9L%FuWU$W|V4rb=@z@@nF)Fan+Q2GhFvc2;t#B}pT9Ls%Yk+;$4aO56 z=#)``ea;4UvJA#pgRvD3=20s$*yjwe&$+>PRtQ}&DzK|;V5i7nj5Qcr;b0!MB7ps1+IP^9I=G-C#VQh^`nF*wr?$(_}El8jP)Q zFppZ1!LByIu6Bd*WGlL2RA4J?V5iGqj5Qcr;b0!MB7?0kz*e}ycqSTMF)FZYY+z@| zV2m{wTj5|HwIYLEV}M=b2IFaZbj7H^uC;+x$zY5%7+c|B9jvXB40Oeq zm)`1loeiv724k$j*a`>ps1+IPIs@!FHyAJQpesfN_5~Z*92tzU24gE6%%fIhurC;3 zUvPu*>KD3VRAATJz~;(ej5Qcr;b0!MB7;?nu1~(WlDx)h#1=eB%J5vT@ ztijj{2lJ>E8LY(sYjK0|iaWYuRA4vSz~;$dj5Qcr;b0!MB7@y%fZgZ@E8SG{Q>}ER{?@zo1V4d$z{Hl=MDr8?1 zvabu-H-zjqA^WC~trW7`g{)P`z9nSe7P32p>`o#3j*#6YWOobMJwmoh$i6FN-xIQX zh3q~dyI;s25V8k_Y_*U*BxK(gvL6W94?AVN-H!7gx?)rd>@Bthc7eRWGS&+$Tj2|= zN3F;U>@CIudy9R6B^Vb@bj7H^zG?$&kii&hFt)FxnVt95k6sKCBv13O0sW30j03J3G36&dVn2H4l!VBF@R zD@FzObsJct48~Z4u@w&HQ7bao*A1|*yTQ0afUX!7*f(roi)1jy8jP)QFppZ1!MB%(4X|&! z!Fb{WT`?-KJ8WR*%V3N(7+c|B9}tTf!%EbyI2Netijj{2lJ>E8SHKY>~1$0&qSjuMg?|{ z4XjB9W30j03J3G36&dUv1MD6*7*ErqD@Fyj$_93c48~Z4u@w&HQ7baoDg$hl8;sX5 z&=sQs`>qY_QW=b~24gE6%%fIhu3B{cbQ`R7O{f3hV(J z*p)IEV-3buIG9JR$Y2i`U=O&#c*PxEF)FYJZD7qZ7-J2_RydePt;k>x8ek8)!FW3Y zx?)sdt8HMPlED~jFt)uT`?-KhiqVWIqghn8zFmM z$X*b#7lmw{ko{K3Uh0%_4afNpT`{T!c8zU;y-HqS8S4d>t?&ibqgLbvc8#&XuCXt$ z1mnVqt{4^Ak8EI{m%$ioFt)E z8SG&L>|r+;SL^7CQGq>T16v`3G1g#gg@bw2iVXIM0rrR+jN3eP#i+o3Yy-PS24k$j z*a`>ps1+IP#|GGs-C*1yKv#?k>?by`Yh^IT8jP)QFppZ1!G2E8SGI5>`^xu4?@rtqXPT64eX0D7-J2_RydePt;k?M zH^6@G2ICPLx?)sdkJ-R(kii&hFt)>z1gFSA5J?;kMu|2wCRA6guU^mKOj5Qcr;b0!MB7?0pz}C9Kc;W+HF)FYpY+zrK z!5C{Ww!*`4RcNjDfznV~C2 z1@;RY*q3E6#u|*Ra4?Trk->gpfc?S^#`B5jicx_*Wdr+)48~Z4u@w&HQ7baoQwG>m zZZMu~MOTap>}ea=%`zBc4aQbDm`AP1U{4!hPrJc*CK_EaDzIm4V7JI%j5Qcr;b0!M zB7;3+fIZ^|<7s+y#i+o3X#@MJ48~Z4u@w&HQ7baoFAcC?y1{r216?sHur?dmtuh#6 z4aQbDm`AP1U~L9in;VQ5c+eGNetN6pvo^4=$zY5%7+c|B9jvZ1FLcGI zz^U3QZ88{R4aQbDm`AP1V9yy~&$+>PQ5jt^DzM+!z`iMiG1g#gg@bw2 ziVXG}1MD|$FkW#-SBwhmc^lYD8H}+8V=ElYqgG_F=MAvu-C(>O0bMaFuorA#x65FR zH5gmrU>>z1gS}vYz2FApy&C9>QGvZ^18bGR7;7-L!ofUhMFxA(0DI95#v57C6{7-M zX9N3|48~Z4u@w&HQ7baoIsB$i2G~n>Fy5cI9$=mCPyC&by)0zE7qUMH*&l`M zPeS%*A$vv0UKO&}gzPUu_E#bMn~=ROWPca34MO%0A$vo}-W0Nb3faGe?B7E6A0c~7 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 BdsqMf literal 0 HcmV?d00001 diff --git a/src/testcases/org/apache/poi/hssf/data/ex44921-21902.xls b/src/testcases/org/apache/poi/hssf/data/ex44921-21902.xls new file mode 100644 index 0000000000000000000000000000000000000000..d0fbf3cab84da324786379ab95a00dea4ac707ea GIT binary patch literal 39424 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 S7QbKfz*UOYU;mya3H)Cx>#J`7 literal 0 HcmV?d00001 diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index 31b4dd180..9ce862b9b 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -592,7 +592,7 @@ public final class TestFormulaParser extends TestCase { HSSFWorkbook book = new HSSFWorkbook(); Ptg[] ptgs = { - new FuncPtg(10, 0), + new FuncPtg(10), }; assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs)); } @@ -900,4 +900,21 @@ public final class TestFormulaParser extends TestCase { assertEquals(2, ptgs.length); assertEquals(FuncPtg.class, ptgs[1].getClass()); } + + public void testWrongNumberOfFunctionArgs() { + confirmArgCountMsg("sin()", "Too few arguments to function 'SIN'. Expected 1 but got 0."); + confirmArgCountMsg("countif(1, 2, 3, 4)", "Too many arguments to function 'COUNTIF'. Expected 2 but got 4."); + confirmArgCountMsg("index(1, 2, 3, 4, 5, 6)", "Too many arguments to function 'INDEX'. At most 4 were expected but got 6."); + confirmArgCountMsg("vlookup(1, 2)", "Too few arguments to function 'VLOOKUP'. At least 3 were expected but got 2."); + } + + private static void confirmArgCountMsg(String formula, String expectedMessage) { + HSSFWorkbook book = new HSSFWorkbook(); + try { + FormulaParser.parse(formula, book); + throw new AssertionFailedError("Didn't get parse exception as expected"); + } catch (FormulaParseException e) { + assertEquals(expectedMessage, e.getMessage()); + } + } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java index 3a7045022..80f6616fa 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java @@ -43,6 +43,7 @@ public final class AllFormulaTests { result.addTestSuite(TestErrPtg.class); result.addTestSuite(TestExternalFunctionFormulas.class); result.addTestSuite(TestFuncPtg.class); + result.addTestSuite(TestFuncVarPtg.class); result.addTestSuite(TestIntersectionPtg.class); result.addTestSuite(TestPercentPtg.class); result.addTestSuite(TestRangePtg.class); diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestFuncVarPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestFuncVarPtg.java new file mode 100644 index 000000000..58ae55f26 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestFuncVarPtg.java @@ -0,0 +1,53 @@ +/* ==================================================================== + 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.formula; + +import org.apache.poi.hssf.model.FormulaParser; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; + +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; +/** + * @author Josh Micich + */ +public final class TestFuncVarPtg extends TestCase { + + /** + * The first fix for bugzilla 44675 broke the encoding of SUM formulas (and probably others). + * The operand classes of the parameters to SUM() should be coerced to 'reference' not 'value'. + * In the case of SUM, Excel evaluates the formula to '#VALUE!' if a parameter operand class is + * wrong. In other cases Excel seems to tolerate bad operand classes.

    + * This functionality is related to the setParameterRVA() methods of FormulaParser + */ + public void testOperandClass() { + HSSFWorkbook book = new HSSFWorkbook(); + Ptg[] ptgs = FormulaParser.parse("sum(A1:A2)", book); + assertEquals(2, ptgs.length); + assertEquals(AreaPtg.class, ptgs[0].getClass()); + + switch(ptgs[0].getPtgClass()) { + case Ptg.CLASS_REF: + // correct behaviour + break; + case Ptg.CLASS_VALUE: + throw new AssertionFailedError("Identified bug 44675b"); + default: + throw new RuntimeException("Unexpected operand class"); + } + } +} diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java index 226b14400..0bddecd3f 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestReferencePtg.java @@ -1,4 +1,3 @@ - /* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -18,20 +17,22 @@ package org.apache.poi.hssf.record.formula; +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import org.apache.poi.hssf.HSSFTestDataSamples; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; /** * Tests for {@link ReferencePtg}. */ -public class TestReferencePtg extends AbstractPtgTestCase -{ +public final class TestReferencePtg extends TestCase { /** * Tests reading a file containing this ptg. */ - public void testReading() throws Exception - { - HSSFWorkbook workbook = loadWorkbook("ReferencePtg.xls"); + public void testReading() { + HSSFWorkbook workbook = HSSFTestDataSamples.openSampleWorkbook("ReferencePtg.xls"); HSSFSheet sheet = workbook.getSheetAt(0); // First row @@ -72,6 +73,18 @@ public class TestReferencePtg extends AbstractPtgTestCase assertEquals("Wrong formula string for reference", "A32770", sheet.getRow(32769).getCell((short) 1).getCellFormula()); } + + public void testBug44921() { + HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ex44921-21902.xls"); + + try { + HSSFTestDataSamples.writeOutAndReadBack(wb); + } catch (RuntimeException e) { + if(e.getMessage().equals("Coding Error: This method should never be called. This ptg should be converted")) { + throw new AssertionFailedError("Identified bug 44921"); + } + throw e; + } + } } - diff --git a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java index 6d6b053a6..47c2c481e 100755 --- a/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/AllUserModelTests.java @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ - + package org.apache.poi.hssf.usermodel; import junit.framework.Test; @@ -46,6 +46,7 @@ public class AllUserModelTests { result.addTestSuite(TestHSSFHeaderFooter.class); result.addTestSuite(TestHSSFHyperlink.class); result.addTestSuite(TestHSSFPalette.class); + result.addTestSuite(TestHSSFPatriarch.class); result.addTestSuite(TestHSSFPicture.class); result.addTestSuite(TestHSSFPictureData.class); result.addTestSuite(TestHSSFRichTextString.class); diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java index 7e9aeff5f..4ba81c409 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestBugs.java @@ -882,10 +882,20 @@ public final class TestBugs extends TestCase { * Bug 28774: Excel will crash when opening xls-files with images. */ public void test28774() { - HSSFWorkbook wb = openSample("28774.xls"); assertTrue("no errors reading sample xls", true); writeOutAndReadBack(wb); assertTrue("no errors writing sample xls", true); } + + /** + * Had a problem apparently, not sure what as it + * works just fine... + */ + public void test44891() throws Exception { + HSSFWorkbook wb = openSample("44891.xls"); + assertTrue("no errors reading sample xls", true); + writeOutAndReadBack(wb); + assertTrue("no errors writing sample xls", true); + } } diff --git a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java index 19069d32b..0ef642917 100644 --- a/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java +++ b/src/testcases/org/apache/poi/hssf/usermodel/TestFormulaEvaluatorBugs.java @@ -19,6 +19,7 @@ package org.apache.poi.hssf.usermodel; import java.io.File; import java.io.FileOutputStream; +import java.util.Iterator; import java.util.List; import junit.framework.TestCase; @@ -252,4 +253,29 @@ public final class TestFormulaEvaluatorBugs extends TestCase { } assertEquals(true, cell.getBooleanCellValue()); } -} + + public void testClassCast_bug44861() throws Exception { + HSSFWorkbook wb = HSSFTestDataSamples. + openSampleWorkbook("44861.xls"); + + // Check direct + HSSFFormulaEvaluator.evaluateAllFormulaCells(wb); + + // And via calls + int numSheets = wb.getNumberOfSheets(); + for(int i=0; i