From b4e74b306c947a57fc97eac555e3a9b3fa85e650 Mon Sep 17 00:00:00 2001
From: Yegor Kozlov
Date: Fri, 10 Oct 2008 14:54:32 +0000
Subject: [PATCH] Initial support for SpreadsheetML drawings,implemented
XSSFPicture, added ability to add pictures to workbook, refactored
XSSFPictureData to be a subclass of POIXMLDocumentPart. Also refactored misc
odds and ends in order to produce xml better compatible with what MS Office
produces
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@703490 13f79535-47bb-0310-9956-ffa450edef68
---
.../org/apache/poi/ss/usermodel/Sheet.java | 2 +-
.../org/apache/poi/POIXMLDocumentPart.java | 19 +-
.../java/org/apache/poi/POIXMLRelation.java | 50 ++-
.../org/apache/poi/xssf/dev/XSSFSave.java | 1 -
.../org/apache/poi/xssf/model/Drawing.java | 19 +-
.../poi/xssf/model/SharedStringsTable.java | 4 +-
.../apache/poi/xssf/model/StylesTable.java | 8 +-
.../apache/poi/xssf/usermodel/ShapeTypes.java | 212 ++++++++++++
.../poi/xssf/usermodel/XSSFClientAnchor.java | 187 ++++++++++
.../poi/xssf/usermodel/XSSFDrawing.java | 155 +++++++++
.../poi/xssf/usermodel/XSSFFactory.java | 2 +
.../poi/xssf/usermodel/XSSFPicture.java | 325 ++++++++++++++++++
.../poi/xssf/usermodel/XSSFPictureData.java | 114 ++++--
.../poi/xssf/usermodel/XSSFRelation.java | 7 +-
.../apache/poi/xssf/usermodel/XSSFShape.java | 93 +++++
.../apache/poi/xssf/usermodel/XSSFSheet.java | 74 ++--
.../poi/xssf/usermodel/XSSFSimpleShape.java | 178 ++++++++++
.../poi/xssf/usermodel/XSSFWorkbook.java | 81 +++--
.../apache/poi/xssf/io/TestLoadSaveXSSF.java | 7 +-
.../poi/xssf/usermodel/TestXSSFDrawing.java | 70 ++++
.../poi/xssf/usermodel/TestXSSFPicture.java | 55 +++
.../xssf/usermodel/TestXSSFPictureData.java | 104 ++++++
.../poi/xssf/usermodel/TestXSSFSheet.java | 126 +++----
.../org/apache/poi/hssf/data/WithDrawing.xlsx | Bin 0 -> 101696 bytes
24 files changed, 1712 insertions(+), 181 deletions(-)
create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/ShapeTypes.java
create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFClientAnchor.java
create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java
create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShape.java
create mode 100755 src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSimpleShape.java
create mode 100755 src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java
create mode 100755 src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPicture.java
create mode 100755 src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPictureData.java
create mode 100755 src/testcases/org/apache/poi/hssf/data/WithDrawing.xlsx
diff --git a/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Sheet.java b/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Sheet.java
index ae35bc00a8..ce4f92ceb5 100644
--- a/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Sheet.java
+++ b/src/ooxml/interfaces-jdk15/org/apache/poi/ss/usermodel/Sheet.java
@@ -672,7 +672,7 @@ public interface Sheet extends Iterable {
*
* @return The new patriarch.
*/
- Patriarch createDrawingPatriarch();
+ //Patriarch createDrawingPatriarch();
/**
* Expands or collapses a column group.
diff --git a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java
index ed3e59fd00..979caeea30 100755
--- a/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java
+++ b/src/ooxml/java/org/apache/poi/POIXMLDocumentPart.java
@@ -43,6 +43,7 @@ public class POIXMLDocumentPart {
DEFAULT_XML_OPTIONS = new XmlOptions();
DEFAULT_XML_OPTIONS.setSaveOuter();
DEFAULT_XML_OPTIONS.setUseDefaultNamespace();
+ DEFAULT_XML_OPTIONS.setSaveAggressiveNamespaces();
}
protected PackagePart packagePart;
@@ -158,16 +159,30 @@ public class POIXMLDocumentPart {
* @return the created child POIXMLDocumentPart
*/
protected POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, Class extends POIXMLDocumentPart> cls, int idx){
+ return createRelationship(descriptor, cls, idx, false);
+ }
+
+ /**
+ * Create a new child POIXMLDocumentPart
+ *
+ * @param descriptor the part descriptor
+ * @param cls the Class object identifying the type of instance to create
+ * @param idx part number
+ * @param norel if true, then no relationship is added.
+ * @return the created child POIXMLDocumentPart
+ */
+ protected POIXMLDocumentPart createRelationship(POIXMLRelation descriptor, Class extends POIXMLDocumentPart> cls, int idx, boolean norel){
try {
PackagePartName ppName = PackagingURIHelper.createPartName(descriptor.getFileName(idx));
- PackageRelationship rel =
- packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation());
+ PackageRelationship rel = null;
+ if(!norel) rel = packagePart.addRelationship(ppName, TargetMode.INTERNAL, descriptor.getRelation());
PackagePart part = packagePart.getPackage().createPart(ppName, descriptor.getContentType());
POIXMLDocumentPart doc = cls.newInstance();
doc.packageRel = rel;
doc.packagePart = part;
+ doc.parent = this;
addRelation(doc);
return doc;
} catch (Exception e){
diff --git a/src/ooxml/java/org/apache/poi/POIXMLRelation.java b/src/ooxml/java/org/apache/poi/POIXMLRelation.java
index a786c02d47..fb001efdcc 100755
--- a/src/ooxml/java/org/apache/poi/POIXMLRelation.java
+++ b/src/ooxml/java/org/apache/poi/POIXMLRelation.java
@@ -23,22 +23,64 @@ package org.apache.poi;
*/
public class POIXMLRelation {
+ /**
+ * Describes the content stored in a part.
+ */
protected String _type;
+
+ /**
+ * The kind of connection between a source part and a target part in a package.
+ */
protected String _relation;
+
+ /**
+ * The path component of a pack URI.
+ */
protected String _defaultName;
/**
* Instantiates a POIXMLRelation.
+ *
+ * @param type content type
+ * @param rel relationship
+ * @param defaultName default item name
*/
- protected POIXMLRelation(String type, String rel, String defaultName) {
+ public POIXMLRelation(String type, String rel, String defaultName) {
_type = type;
_relation = rel;
_defaultName = defaultName;
}
- public String getContentType() { return _type; }
- public String getRelation() { return _relation; }
- public String getDefaultFileName() { return _defaultName; }
+ /**
+ * Return the content type. Content types define a media type, a subtype, and an
+ * optional set of parameters, as defined in RFC 2616.
+ *
+ * @return the content type
+ */
+ public String getContentType() {
+ return _type;
+ }
+
+ /**
+ * Return the relationship, the kind of connection between a source part and a target part in a package.
+ * Relationships make the connections between parts directly discoverable without looking at the content
+ * in the parts, and without altering the parts themselves.
+ *
+ * @return the relationship
+ */
+ public String getRelation() {
+ return _relation;
+ }
+
+ /**
+ * Return the default part name. Part names are used to refer to a part in the context of a
+ * package, typically as part of a URI.
+ *
+ * @return the default part name
+ */
+ public String getDefaultFileName() {
+ return _defaultName;
+ }
/**
* Returns the filename for the nth one of these,
diff --git a/src/ooxml/java/org/apache/poi/xssf/dev/XSSFSave.java b/src/ooxml/java/org/apache/poi/xssf/dev/XSSFSave.java
index 7ca4a3d691..148a02fcb2 100755
--- a/src/ooxml/java/org/apache/poi/xssf/dev/XSSFSave.java
+++ b/src/ooxml/java/org/apache/poi/xssf/dev/XSSFSave.java
@@ -31,7 +31,6 @@ public class XSSFSave {
for (int i = 0; i < args.length; i++) {
XSSFWorkbook wb = new XSSFWorkbook(args[i]);
- System.out.println("wb.getNumberOfSheets(): " + wb.getNumberOfSheets());
int sep = args[i].lastIndexOf('.');
String outfile = args[i].substring(0, sep) + "-save.xlsx";
FileOutputStream out = new FileOutputStream(outfile);
diff --git a/src/ooxml/java/org/apache/poi/xssf/model/Drawing.java b/src/ooxml/java/org/apache/poi/xssf/model/Drawing.java
index 58dc805708..c151841798 100644
--- a/src/ooxml/java/org/apache/poi/xssf/model/Drawing.java
+++ b/src/ooxml/java/org/apache/poi/xssf/model/Drawing.java
@@ -16,7 +16,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDrawing;
* A drawing object in XSSF. May well have raw pictures
* attached to it as children.
*/
-public class Drawing implements XSSFChildContainingModel {
+public class Drawing implements XSSFModel {
private CTDrawing drawing;
private String originalId;
@@ -77,20 +77,11 @@ public class Drawing implements XSSFChildContainingModel {
* Generates and adds XSSFActiveXData children
*/
public void generateChild(PackagePart childPart, String childRelId) {
- XSSFPictureData pd = new XSSFPictureData(childPart, childRelId);
- pictures.add(pd);
- }
+ //XSSFPictureData pd = new XSSFPictureData(childPart, childRelId);
+ //pictures.add(pd);
+ throw new RuntimeException("deprecated");
+ }
- public WritableChild getChildForWriting(int index) {
- if(index >= pictures.size()) {
- throw new IllegalArgumentException("Can't get child at " + index + " when size is " + getNumberOfChildren());
- }
- return new WritableChild(
- pictures.get(index),
- XSSFRelation.IMAGES
- );
- }
-
public ArrayList getPictures()
{
return this.pictures;
diff --git a/src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java b/src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java
index c004905412..e854d19d02 100644
--- a/src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java
+++ b/src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java
@@ -198,9 +198,7 @@ public class SharedStringsTable extends POIXMLDocumentPart implements XSSFModel,
* @throws IOException if an error occurs while writing.
*/
public void writeTo(OutputStream out) throws IOException {
- XmlOptions options = new XmlOptions();
- options.setSaveOuter();
- options.setUseDefaultNamespace();
+ XmlOptions options = new XmlOptions(DEFAULT_XML_OPTIONS);
//re-create the sst table every time saving a workbook
SstDocument doc = SstDocument.Factory.newInstance();
diff --git a/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java b/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java
index c3dd808392..568d56c157 100644
--- a/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java
+++ b/src/ooxml/java/org/apache/poi/xssf/model/StylesTable.java
@@ -330,13 +330,7 @@ public class StylesTable extends POIXMLDocumentPart implements StylesSource, XSS
* @throws IOException if an error occurs while writing.
*/
public void writeTo(OutputStream out) throws IOException {
- XmlOptions options = new XmlOptions();
- options.setSaveOuter();
- options.setUseDefaultNamespace();
-
- // Requests use of whitespace for easier reading
- options.setSavePrettyPrint();
-
+ XmlOptions options = new XmlOptions(DEFAULT_XML_OPTIONS);
// Work on the current one
// Need to do this, as we don't handle
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/ShapeTypes.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/ShapeTypes.java
new file mode 100755
index 0000000000..4b2cdf8c4d
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/ShapeTypes.java
@@ -0,0 +1,212 @@
+/* ====================================================================
+ 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.xssf.usermodel;
+
+/**
+ * All know type of automatic shapes in DrawingML
+ *
+ * @author Yegor Kozlov
+ */
+public class ShapeTypes {
+ public static final int LINE = 1;
+ public static final int LINE_INV = 2;
+ public static final int TRIANGLE = 3;
+ public static final int RT_TRIANGLE = 4;
+ public static final int RECT = 5;
+ public static final int DIAMOND = 6;
+ public static final int PARALLELOGRAM = 7;
+ public static final int TRAPEZOID = 8;
+ public static final int NON_ISOSCELES_TRAPEZOID = 9;
+ public static final int PENTAGON = 10;
+ public static final int HEXAGON = 11;
+ public static final int HEPTAGON = 12;
+ public static final int OCTAGON = 13;
+ public static final int DECAGON = 14;
+ public static final int DODECAGON = 15;
+ public static final int STAR_4 = 16;
+ public static final int STAR_5 = 17;
+ public static final int STAR_6 = 18;
+ public static final int STAR_7 = 19;
+ public static final int STAR_8 = 20;
+ public static final int STAR_10 = 21;
+ public static final int STAR_12 = 22;
+ public static final int STAR_16 = 23;
+ public static final int STAR_24 = 24;
+ public static final int STAR_32 = 25;
+ public static final int ROUND_RECT = 26;
+ public static final int ROUND_1_RECT = 27;
+ public static final int ROUND_2_SAME_RECT = 28;
+ public static final int ROUND_2_DIAG_RECT = 29;
+ public static final int SNIP_ROUND_RECT = 30;
+ public static final int SNIP_1_RECT = 31;
+ public static final int SNIP_2_SAME_RECT = 32;
+ public static final int SNIP_2_DIAG_RECT = 33;
+ public static final int PLAQUE = 34;
+ public static final int ELLIPSE = 35;
+ public static final int TEARDROP = 36;
+ public static final int HOME_PLATE = 37;
+ public static final int CHEVRON = 38;
+ public static final int PIE_WEDGE = 39;
+ public static final int PIE = 40;
+ public static final int BLOCK_ARC = 41;
+ public static final int DONUT = 42;
+ public static final int NO_SMOKING = 43;
+ public static final int RIGHT_ARROW = 44;
+ public static final int LEFT_ARROW = 45;
+ public static final int UP_ARROW = 46;
+ public static final int DOWN_ARROW = 47;
+ public static final int STRIPED_RIGHT_ARROW = 48;
+ public static final int NOTCHED_RIGHT_ARROW = 49;
+ public static final int BENT_UP_ARROW = 50;
+ public static final int LEFT_RIGHT_ARROW = 51;
+ public static final int UP_DOWN_ARROW = 52;
+ public static final int LEFT_UP_ARROW = 53;
+ public static final int LEFT_RIGHT_UP_ARROW = 54;
+ public static final int QUAD_ARROW = 55;
+ public static final int LEFT_ARROW_CALLOUT = 56;
+ public static final int RIGHT_ARROW_CALLOUT = 57;
+ public static final int UP_ARROW_CALLOUT = 58;
+ public static final int DOWN_ARROW_CALLOUT = 59;
+ public static final int LEFT_RIGHT_ARROW_CALLOUT = 60;
+ public static final int UP_DOWN_ARROW_CALLOUT = 61;
+ public static final int QUAD_ARROW_CALLOUT = 62;
+ public static final int BENT_ARROW = 63;
+ public static final int UTURN_ARROW = 64;
+ public static final int CIRCULAR_ARROW = 65;
+ public static final int LEFT_CIRCULAR_ARROW = 66;
+ public static final int LEFT_RIGHT_CIRCULAR_ARROW = 67;
+ public static final int CURVED_RIGHT_ARROW = 68;
+ public static final int CURVED_LEFT_ARROW = 69;
+ public static final int CURVED_UP_ARROW = 70;
+ public static final int CURVED_DOWN_ARROW = 71;
+ public static final int SWOOSH_ARROW = 72;
+ public static final int CUBE = 73;
+ public static final int CAN = 74;
+ public static final int LIGHTNING_BOLT = 75;
+ public static final int HEART = 76;
+ public static final int SUN = 77;
+ public static final int MOON = 78;
+ public static final int SMILEY_FACE = 79;
+ public static final int IRREGULAR_SEAL_1 = 80;
+ public static final int IRREGULAR_SEAL_2 = 81;
+ public static final int FOLDED_CORNER = 82;
+ public static final int BEVEL = 83;
+ public static final int FRAME = 84;
+ public static final int HALF_FRAME = 85;
+ public static final int CORNER = 86;
+ public static final int DIAG_STRIPE = 87;
+ public static final int CHORD = 88;
+ public static final int ARC = 89;
+ public static final int LEFT_BRACKET = 90;
+ public static final int RIGHT_BRACKET = 91;
+ public static final int LEFT_BRACE = 92;
+ public static final int RIGHT_BRACE = 93;
+ public static final int BRACKET_PAIR = 94;
+ public static final int BRACE_PAIR = 95;
+ public static final int STRAIGHT_CONNECTOR_1 = 96;
+ public static final int BENT_CONNECTOR_2 = 97;
+ public static final int BENT_CONNECTOR_3 = 98;
+ public static final int BENT_CONNECTOR_4 = 99;
+ public static final int BENT_CONNECTOR_5 = 100;
+ public static final int CURVED_CONNECTOR_2 = 101;
+ public static final int CURVED_CONNECTOR_3 = 102;
+ public static final int CURVED_CONNECTOR_4 = 103;
+ public static final int CURVED_CONNECTOR_5 = 104;
+ public static final int CALLOUT_1 = 105;
+ public static final int CALLOUT_2 = 106;
+ public static final int CALLOUT_3 = 107;
+ public static final int ACCENT_CALLOUT_1 = 108;
+ public static final int ACCENT_CALLOUT_2 = 109;
+ public static final int ACCENT_CALLOUT_3 = 110;
+ public static final int BORDER_CALLOUT_1 = 111;
+ public static final int BORDER_CALLOUT_2 = 112;
+ public static final int BORDER_CALLOUT_3 = 113;
+ public static final int ACCENT_BORDER_CALLOUT_1 = 114;
+ public static final int ACCENT_BORDER_CALLOUT_2 = 115;
+ public static final int ACCENT_BORDER_CALLOUT_3 = 116;
+ public static final int WEDGE_RECT_CALLOUT = 117;
+ public static final int WEDGE_ROUND_RECT_CALLOUT = 118;
+ public static final int WEDGE_ELLIPSE_CALLOUT = 119;
+ public static final int CLOUD_CALLOUT = 120;
+ public static final int CLOUD = 121;
+ public static final int RIBBON = 122;
+ public static final int RIBBON_2 = 123;
+ public static final int ELLIPSE_RIBBON = 124;
+ public static final int ELLIPSE_RIBBON_2 = 125;
+ public static final int LEFT_RIGHT_RIBBON = 126;
+ public static final int VERTICAL_SCROLL = 127;
+ public static final int HORIZONTAL_SCROLL = 128;
+ public static final int WAVE = 129;
+ public static final int DOUBLE_WAVE = 130;
+ public static final int PLUS = 131;
+ public static final int FLOW_CHART_PROCESS = 132;
+ public static final int FLOW_CHART_DECISION = 133;
+ public static final int FLOW_CHART_INPUT_OUTPUT = 134;
+ public static final int FLOW_CHART_PREDEFINED_PROCESS = 135;
+ public static final int FLOW_CHART_INTERNAL_STORAGE = 136;
+ public static final int FLOW_CHART_DOCUMENT = 137;
+ public static final int FLOW_CHART_MULTIDOCUMENT = 138;
+ public static final int FLOW_CHART_TERMINATOR = 139;
+ public static final int FLOW_CHART_PREPARATION = 140;
+ public static final int FLOW_CHART_MANUAL_INPUT = 141;
+ public static final int FLOW_CHART_MANUAL_OPERATION = 142;
+ public static final int FLOW_CHART_CONNECTOR = 143;
+ public static final int FLOW_CHART_PUNCHED_CARD = 144;
+ public static final int FLOW_CHART_PUNCHED_TAPE = 145;
+ public static final int FLOW_CHART_SUMMING_JUNCTION = 146;
+ public static final int FLOW_CHART_OR = 147;
+ public static final int FLOW_CHART_COLLATE = 148;
+ public static final int FLOW_CHART_SORT = 149;
+ public static final int FLOW_CHART_EXTRACT = 150;
+ public static final int FLOW_CHART_MERGE = 151;
+ public static final int FLOW_CHART_OFFLINE_STORAGE = 152;
+ public static final int FLOW_CHART_ONLINE_STORAGE = 153;
+ public static final int FLOW_CHART_MAGNETIC_TAPE = 154;
+ public static final int FLOW_CHART_MAGNETIC_DISK = 155;
+ public static final int FLOW_CHART_MAGNETIC_DRUM = 156;
+ public static final int FLOW_CHART_DISPLAY = 157;
+ public static final int FLOW_CHART_DELAY = 158;
+ public static final int FLOW_CHART_ALTERNATE_PROCESS = 159;
+ public static final int FLOW_CHART_OFFPAGE_CONNECTOR = 160;
+ public static final int ACTION_BUTTON_BLANK = 161;
+ public static final int ACTION_BUTTON_HOME = 162;
+ public static final int ACTION_BUTTON_HELP = 163;
+ public static final int ACTION_BUTTON_INFORMATION = 164;
+ public static final int ACTION_BUTTON_FORWARD_NEXT = 165;
+ public static final int ACTION_BUTTON_BACK_PREVIOUS = 166;
+ public static final int ACTION_BUTTON_END = 167;
+ public static final int ACTION_BUTTON_BEGINNING = 168;
+ public static final int ACTION_BUTTON_RETURN = 169;
+ public static final int ACTION_BUTTON_DOCUMENT = 170;
+ public static final int ACTION_BUTTON_SOUND = 171;
+ public static final int ACTION_BUTTON_MOVIE = 172;
+ public static final int GEAR_6 = 173;
+ public static final int GEAR_9 = 174;
+ public static final int FUNNEL = 175;
+ public static final int MATH_PLUS = 176;
+ public static final int MATH_MINUS = 177;
+ public static final int MATH_MULTIPLY = 178;
+ public static final int MATH_DIVIDE = 179;
+ public static final int MATH_EQUAL = 180;
+ public static final int MATH_NOT_EQUAL = 181;
+ public static final int CORNER_TABS = 182;
+ public static final int SQUARE_TABS = 183;
+ public static final int PLAQUE_TABS = 184;
+ public static final int CHART_X = 185;
+ public static final int CHART_STAR = 186;
+ public static final int CHART_PLUS = 187;
+}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFClientAnchor.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFClientAnchor.java
new file mode 100755
index 0000000000..0a6129e432
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFClientAnchor.java
@@ -0,0 +1,187 @@
+/* ====================================================================
+ 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.xssf.usermodel;
+
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
+
+/**
+ * A client anchor is attached to an excel worksheet. It anchors against
+ * top-left and buttom-right cells.
+ *
+ * @author Yegor Kozlov
+ */
+public class XSSFClientAnchor {
+
+ /**
+ * Starting anchor point
+ */
+ private CTMarker cell1;
+
+ /**
+ * Ending anchor point
+ */
+ private CTMarker cell2;
+
+ /**
+ * Creates a new client anchor and defaults all the anchor positions to 0.
+ */
+ public XSSFClientAnchor() {
+ cell1 = CTMarker.Factory.newInstance();
+ cell1.setCol(0);
+ cell1.setColOff(0);
+ cell1.setRow(0);
+ cell1.setRowOff(0);
+ cell2 = CTMarker.Factory.newInstance();
+ cell2.setCol(0);
+ cell2.setColOff(0);
+ cell2.setRow(0);
+ cell2.setRowOff(0);
+ }
+
+ /**
+ * Creates a new client anchor and sets the top-left and bottom-right
+ * coordinates of the anchor.
+ *
+ * @param dx1 the x coordinate within the first cell.
+ * @param dy1 the y coordinate within the first cell.
+ * @param dx2 the x coordinate within the second cell.
+ * @param dy2 the y coordinate within the second cell.
+ * @param col1 the column (0 based) of the first cell.
+ * @param row1 the row (0 based) of the first cell.
+ * @param col2 the column (0 based) of the second cell.
+ * @param row2 the row (0 based) of the second cell.
+ */
+ public XSSFClientAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2) {
+ this();
+ cell1.setCol(col1);
+ cell1.setColOff(dx1);
+ cell1.setRow(row1);
+ cell1.setRowOff(dy1);
+ cell2.setCol(col2);
+ cell2.setColOff(dx2);
+ cell2.setRow(row2);
+ cell2.setRowOff(dy2);
+ }
+
+ /**
+ * Create XSSFClientAnchor from existing xml beans
+ *
+ * @param cell1 starting anchor point
+ * @param cell2 ending anchor point
+ */
+ protected XSSFClientAnchor(CTMarker cell1, CTMarker cell2) {
+ this.cell1 = cell1;
+ this.cell2 = cell2;
+ }
+
+ public int getCol1() {
+ return cell1.getCol();
+ }
+
+ public void setCol1(short col1) {
+ cell1.setCol(col1);
+ }
+
+ public int getCol2() {
+ return cell2.getCol();
+ }
+
+ public void setCol2(short col2) {
+ cell2.setCol(col2);
+ }
+
+ public int getRow1() {
+ return cell1.getRow();
+ }
+
+ public void setRow1(int row1) {
+ cell1.setRow(row1);
+ }
+
+ public int getRow2() {
+ return cell2.getRow();
+ }
+
+ public void setRow2(int row2) {
+ cell2.setRow(row2);
+ }
+
+ public int getDx1() {
+ return (int)cell1.getColOff();
+ }
+
+ public void setDx1(int dx1) {
+ cell1.setColOff(dx1);
+ }
+
+ public int getDy1() {
+ return (int)cell1.getRowOff();
+ }
+
+ public void setDy1(int dy1) {
+ cell1.setRowOff(dy1);
+ }
+
+ public int getDy2() {
+ return (int)cell2.getRowOff();
+ }
+
+ public void setDy2(int dy2) {
+ cell2.setRowOff(dy2);
+ }
+
+ public int getDx2() {
+ return (int)cell2.getColOff();
+ }
+
+ public void setDx2(int dx2) {
+ cell2.setColOff(dx2);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof XSSFClientAnchor)) return false;
+
+ XSSFClientAnchor anchor = (XSSFClientAnchor) o;
+ return cell1.toString().equals(anchor.getFrom().toString()) &&
+ cell2.toString().equals(anchor.getTo().toString()) ;
+
+ }
+
+ @Override
+ public String toString(){
+ return "from : " + cell1.toString() + "; to: " + cell2.toString();
+ }
+
+ /**
+ * Return starting anchor point
+ *
+ * @return starting anchor point
+ */
+ public CTMarker getFrom(){
+ return cell1;
+ }
+
+ /**
+ * Return ending anchor point
+ *
+ * @return ending anchor point
+ */
+ public CTMarker getTo(){
+ return cell2;
+ }
+}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java
new file mode 100755
index 0000000000..b451b4ef3d
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java
@@ -0,0 +1,155 @@
+/* ====================================================================
+ 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.xssf.usermodel;
+
+import org.apache.poi.POIXMLDocumentPart;
+import org.apache.xmlbeans.XmlException;
+import org.apache.xmlbeans.XmlOptions;
+import org.openxml4j.opc.*;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.STEditAs;
+import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
+
+import javax.xml.namespace.QName;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Represents a SpreadsheetML drawing
+ *
+ * @author Yegor Kozlov
+ */
+public class XSSFDrawing extends POIXMLDocumentPart {
+ /**
+ * Root element of the SpreadsheetML Drawing part
+ */
+ private CTDrawing drawing;
+
+ /**
+ * Create a new SpreadsheetML drawing
+ *
+ * @see org.apache.poi.xssf.usermodel.XSSFWorksheet#createDrawingPatriarch()
+ */
+ public XSSFDrawing() {
+ super(null, null);
+ drawing = newDrawing();
+ }
+
+ /**
+ * Construct a SpreadsheetML drawing from a package part
+ *
+ * @param part the package part holding the drawing data,
+ * the content type must be application/vnd.openxmlformats-officedocument.drawing+xml
+ * @param rel the package relationship holding this drawing,
+ * the relationship type must be http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing
+ */
+ public XSSFDrawing(PackagePart part, PackageRelationship rel) throws IOException, XmlException {
+ super(part, rel);
+ drawing = CTDrawing.Factory.parse(part.getInputStream());
+ }
+
+ /**
+ * Construct a new CTDrawing bean. By default, it's just an empty placeholder for drawing objects
+ *
+ * @return a new CTDrawing bean
+ */
+ private static CTDrawing newDrawing(){
+ return CTDrawing.Factory.newInstance();
+ }
+
+ /**
+ * Return the underlying CTDrawing bean, the root element of the SpreadsheetML Drawing part.
+ *
+ * @return the underlying CTDrawing bean
+ */
+ public CTDrawing getCTDrawing(){
+ return drawing;
+ }
+
+ @Override
+ protected void commit() throws IOException {
+ XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
+
+ /*
+ Saved drawings must have the following namespaces set:
+
+ */
+ xmlOptions.setSaveSyntheticDocumentElement(new QName(CTDrawing.type.getName().getNamespaceURI(), "wsDr", "xdr"));
+ Map map = new HashMap();
+ map.put("http://schemas.openxmlformats.org/drawingml/2006/main", "a");
+ map.put(STRelationshipId.type.getName().getNamespaceURI(), "r");
+ xmlOptions.setSaveSuggestedPrefixes(map);
+
+ PackagePart part = getPackagePart();
+ OutputStream out = part.getOutputStream();
+ drawing.save(out, xmlOptions);
+ out.close();
+ }
+
+ /**
+ * Creates a picture.
+ *
+ * @param anchor the client anchor describes how this picture is attached to the sheet.
+ * @param pictureIndex the index of the picture in the workbook collection of pictures,
+ * {@link org.apache.poi.xssf.usermodel.XSSFWorkbook#getAllPictures()} .
+ *
+ * @return the newly created picture shape.
+ */
+ public XSSFPicture createPicture(XSSFClientAnchor anchor, int pictureIndex)
+ {
+ XSSFWorkbook wb = (XSSFWorkbook)getParent().getParent();
+ XSSFPictureData data = wb.getAllPictures().get(pictureIndex);
+ PackagePartName ppName = data.getPackagePart().getPartName();
+ PackageRelationship rel = packagePart.addRelationship(ppName, TargetMode.INTERNAL, XSSFRelation.IMAGES.getRelation());
+ addRelation(new XSSFPictureData(data.getPackagePart(), rel));
+ CTTwoCellAnchor ctAnchor = createTwoCellAnchor(anchor);
+ return new XSSFPicture(this, rel, ctAnchor);
+ }
+
+ /**
+ * Creates a simple shape. This includes such shapes as lines, rectangles,
+ * and ovals.
+ *
+ * @param anchor the client anchor describes how this group is attached
+ * to the sheet.
+ * @return the newly created shape.
+ */
+ public XSSFSimpleShape createSimpleShape(XSSFClientAnchor anchor)
+ {
+ CTTwoCellAnchor ctAnchor = createTwoCellAnchor(anchor);
+ return new XSSFSimpleShape(this, ctAnchor);
+ }
+
+ /**
+ * Create and initialize a CTTwoCellAnchor that anchors a shape against top-left and bottom-right cells.
+ *
+ * @return a new CTTwoCellAnchor
+ */
+ private CTTwoCellAnchor createTwoCellAnchor(XSSFClientAnchor anchor){
+ CTTwoCellAnchor ctAnchor = drawing.addNewTwoCellAnchor();
+ ctAnchor.setEditAs(STEditAs.ONE_CELL);
+ ctAnchor.setFrom(anchor.getFrom());
+ ctAnchor.setTo(anchor.getTo());
+ ctAnchor.addNewClientData();
+ return ctAnchor;
+ }
+}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java
index 44f8054b3c..52b8d63f9a 100755
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFFactory.java
@@ -41,6 +41,8 @@ public class XSSFFactory extends POIXMLFactory {
parts.put(XSSFRelation.SHARED_STRINGS.getRelation(), SharedStringsTable.class);
parts.put(XSSFRelation.STYLES.getRelation(), StylesTable.class);
parts.put(XSSFRelation.SHEET_COMMENTS.getRelation(), CommentsTable.class);
+ parts.put(XSSFRelation.DRAWINGS.getRelation(), XSSFDrawing.class);
+ parts.put(XSSFRelation.IMAGES.getRelation(), XSSFPictureData.class);
}
public POIXMLDocumentPart create(PackageRelationship rel, PackagePart p){
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
new file mode 100755
index 0000000000..93a7caa6e6
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPicture.java
@@ -0,0 +1,325 @@
+/* ====================================================================
+ 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.xssf.usermodel;
+
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.*;
+import org.openxmlformats.schemas.drawingml.x2006.main.*;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.POIXMLDocumentPart;
+import org.openxml4j.opc.PackageRelationship;
+import org.openxml4j.opc.PackagePart;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Element;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageReader;
+import javax.imageio.stream.ImageInputStream;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.Iterator;
+
+/**
+ * Represents a picture shape in a SpreadsheetML drawing.
+ *
+ * @author Yegor Kozlov
+ */
+public class XSSFPicture extends XSSFShape {
+ private static final POILogger logger = POILogFactory.getLogger(XSSFPicture.class);
+
+ /**
+ * width of 1px in columns with default width
+ */
+ private static final float PX_DEFAULT = 0.125f;
+ /**
+ * width of 1px in columns with overridden width
+ */
+ private static final float PX_MODIFIED = 0.143f;
+
+ /**
+ * Height of 1px of a row
+ */
+ private static final int PX_ROW = 15;
+
+ /**
+ * This object specifies a picture object and all its properties
+ */
+ private CTPicture ctPicture;
+
+ /**
+ * Construct a new XSSFPicture object. This constructor is called from
+ * {@link XSSFDrawing#createPicture(XSSFClientAnchor, int)}
+ *
+ * @param parent the XSSFDrawing that owns this picture
+ * @param rel the relationship to the picture data
+ * @param anchor the two cell anchor placeholder for this picture,
+ * this object encloses the CTPicture bean that holds all the picture properties
+ */
+ protected XSSFPicture(XSSFDrawing parent, PackageRelationship rel, CTTwoCellAnchor anchor){
+ super(parent, anchor);
+ //Create a new picture and attach it to the specified two-cell anchor
+ ctPicture = newPicture(rel);
+ anchor.setPic(ctPicture);
+ }
+
+ /**
+ * Create a new CTPicture bean and initialize its required attributes
+ *
+ * @param rel the relationship to the picture data
+ * @return a new CTPicture bean
+ */
+ private static CTPicture newPicture(PackageRelationship rel){
+ CTPicture pic = CTPicture.Factory.newInstance();
+
+ CTPictureNonVisual nvpr = pic.addNewNvPicPr();
+ CTNonVisualDrawingProps nvProps = nvpr.addNewCNvPr();
+ //YK: TODO shape IDs must be unique across workbook
+ int shapeId = 1;
+ nvProps.setId(shapeId);
+ nvProps.setName("Picture " + shapeId);
+ nvProps.setDescr(rel.getTargetURI().toString());
+ CTNonVisualPictureProperties nvPicProps = nvpr.addNewCNvPicPr();
+ nvPicProps.addNewPicLocks().setNoChangeAspect(true);
+
+ CTBlipFillProperties blip = pic.addNewBlipFill();
+ blip.addNewBlip().setEmbed(rel.getId());
+ blip.addNewStretch().addNewFillRect();
+
+ CTShapeProperties sppr = pic.addNewSpPr();
+ CTTransform2D t2d = sppr.addNewXfrm();
+ CTPositiveSize2D ext = t2d.addNewExt();
+ //should be original picture width and height expressed in EMUs
+ ext.setCx(0);
+ ext.setCy(0);
+
+ CTPoint2D off = t2d.addNewOff();
+ off.setX(0);
+ off.setY(0);
+
+ CTPresetGeometry2D prstGeom = sppr.addNewPrstGeom();
+ prstGeom.setPrst(STShapeType.RECT);
+ prstGeom.addNewAvLst();
+ return pic;
+ }
+
+ /**
+ * Return the underlying CTPicture bean that holds all properties for this picture
+ *
+ * @return the underlying CTPicture bean
+ */
+ public CTPicture getCTPicture(){
+ return ctPicture;
+ }
+
+ /**
+ * Reset the image to the original size.
+ */
+ public void resize(){
+ XSSFClientAnchor anchor = getAnchor();
+
+ XSSFClientAnchor pref = getPreferredSize();
+
+ int row2 = anchor.getRow1() + (pref.getRow2() - pref.getRow1());
+ int col2 = anchor.getCol1() + (pref.getCol2() - pref.getCol1());
+
+ anchor.setCol2((short)col2);
+ anchor.setDx1(0);
+ anchor.setDx2(pref.getDx2());
+
+ anchor.setRow2(row2);
+ anchor.setDy1(0);
+ anchor.setDy2(pref.getDy2());
+ }
+
+ /**
+ * Calculate the preferred size for this picture.
+ *
+ * @return XSSFClientAnchor with the preferred size for this image
+ */
+ public XSSFClientAnchor getPreferredSize(){
+ XSSFClientAnchor anchor = getAnchor();
+
+ XSSFPictureData data = getPictureData();
+ Dimension size = getImageDimension(data.getPackagePart(), data.getPictureType());
+
+ float w = 0;
+
+ //space in the leftmost cell
+ w += anchor.getDx1()/EMU_PER_POINT;
+ short col2 = (short)(anchor.getCol1() + 1);
+ int dx2 = 0;
+
+ while(w < size.width){
+ w += getColumnWidthInPixels(col2++);
+ }
+
+ if(w > size.width) {
+ //calculate dx2, offset in the rightmost cell
+ col2--;
+ float cw = getColumnWidthInPixels(col2);
+ float delta = w - size.width;
+ dx2 = (int)(EMU_PER_POINT*(cw-delta));
+ }
+ anchor.setCol2(col2);
+ anchor.setDx2(dx2);
+
+ float h = 0;
+ h += (1 - anchor.getDy1()/256)* getRowHeightInPixels(anchor.getRow1());
+ int row2 = anchor.getRow1() + 1;
+ int dy2 = 0;
+
+ while(h < size.height){
+ h += getRowHeightInPixels(row2++);
+ }
+ if(h > size.height) {
+ row2--;
+ float ch = getRowHeightInPixels(row2);
+ float delta = h - size.height;
+ dy2 = (int)((ch-delta)/ch*256);
+ }
+ anchor.setRow2(row2);
+ anchor.setDy2(dy2);
+
+ return anchor;
+ }
+
+ private float getColumnWidthInPixels(int column){
+ XSSFSheet sheet = (XSSFSheet)getDrawing().getParent();
+ int cw = sheet.getColumnWidth(column);
+ float px = getPixelWidth(column);
+
+ return cw/px;
+ }
+
+ private float getRowHeightInPixels(int i){
+ XSSFSheet sheet = (XSSFSheet)getDrawing().getParent();
+
+ XSSFRow row = sheet.getRow(i);
+ float height;
+ if(row != null) height = row.getHeight();
+ else height = sheet.getDefaultRowHeight();
+
+ return height/PX_ROW;
+ }
+
+ private float getPixelWidth(int column){
+ XSSFSheet sheet = (XSSFSheet)getDrawing().getParent();
+
+ int def = sheet.getDefaultColumnWidth();
+ int cw = sheet.getColumnWidth(column);
+
+ return cw == def ? PX_DEFAULT : PX_MODIFIED;
+ }
+
+ /**
+ * Return the dimension of this image
+ *
+ * @param part the package part holding raw picture data
+ * @param type type of the picture: {@link Workbook#PICTURE_TYPE_JPEG, Workbook#PICTURE_TYPE_PNG or Workbook#PICTURE_TYPE_DIB)
+ *
+ * @return image dimension in pixels
+ */
+ protected static Dimension getImageDimension(PackagePart part, int type){
+ Dimension size = new Dimension();
+
+ switch (type){
+ //we can calculate the preferred size only for JPEG and PNG
+ //other formats like WMF, EMF and PICT are not supported in Java
+ case Workbook.PICTURE_TYPE_JPEG:
+ case Workbook.PICTURE_TYPE_PNG:
+ case Workbook.PICTURE_TYPE_DIB:
+ try {
+ //read the image using javax.imageio.*
+ ImageInputStream iis = ImageIO.createImageInputStream( part.getInputStream() );
+ Iterator i = ImageIO.getImageReaders( iis );
+ ImageReader r = (ImageReader) i.next();
+ r.setInput( iis );
+ BufferedImage img = r.read(0);
+
+ int[] dpi = getResolution(r);
+
+ //if DPI is zero then assume standard 96 DPI
+ //since cannot divide by zero
+ if (dpi[0] == 0) dpi[0] = 96;
+ if (dpi[1] == 0) dpi[1] = 96;
+
+ size.width = img.getWidth()*96/dpi[0];
+ size.height = img.getHeight()*96/dpi[1];
+
+ } catch (IOException e){
+ //silently return if ImageIO failed to read the image
+ logger.log(POILogger.WARN, e);
+ }
+
+ break;
+ default:
+ logger.log(POILogger.WARN, "Only JPEG, PNG and DIB pictures can be automatically sized");
+ }
+ return size;
+ }
+
+ /**
+ * The metadata of PNG and JPEG can contain the width of a pixel in millimeters.
+ * Return the the "effective" dpi calculated as 25.4/HorizontalPixelSize
+ * and 25.4/VerticalPixelSize
. Where 25.4 is the number of mm in inch.
+ *
+ * @return array of two elements: {horisontalPdi, verticalDpi}
.
+ * {96, 96} is the default.
+ */
+ protected static int[] getResolution(ImageReader r) throws IOException {
+ int hdpi=96, vdpi=96;
+ double mm2inch = 25.4;
+
+ NodeList lst;
+ Element node = (Element)r.getImageMetadata(0).getAsTree("javax_imageio_1.0");
+ lst = node.getElementsByTagName("HorizontalPixelSize");
+ if(lst != null && lst.getLength() == 1) hdpi = (int)(mm2inch/Float.parseFloat(((Element)lst.item(0)).getAttribute("value")));
+
+ lst = node.getElementsByTagName("VerticalPixelSize");
+ if(lst != null && lst.getLength() == 1) vdpi = (int)(mm2inch/Float.parseFloat(((Element)lst.item(0)).getAttribute("value")));
+
+ return new int[]{hdpi, vdpi};
+ }
+
+ /**
+ * return the anchor that is used by this shape.
+ *
+ * @return the anchor that is used by this shape.
+ */
+ public XSSFClientAnchor getAnchor(){
+ CTTwoCellAnchor ctAnchor = (CTTwoCellAnchor)getShapeContainer();
+ return new XSSFClientAnchor(ctAnchor.getFrom(), ctAnchor.getTo());
+ }
+
+ /**
+ * Return picture data for this shape
+ *
+ * @return picture data for this shape
+ */
+ public XSSFPictureData getPictureData() {
+ String blipId = ctPicture.getBlipFill().getBlip().getEmbed();
+ for (POIXMLDocumentPart part : getDrawing().getRelations()) {
+ if(part.getPackageRelationship().getId().equals(blipId)){
+ return (XSSFPictureData)part;
+ }
+ }
+ logger.log(POILogger.WARN, "Picture data was not found for blipId=" + blipId);
+ return null;
+ }
+
+}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java
index f7fb5b27ad..fdc49fcaca 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFPictureData.java
@@ -18,51 +18,105 @@
package org.apache.poi.xssf.usermodel;
import java.io.IOException;
-import java.io.OutputStream;
-
import org.apache.poi.ss.usermodel.PictureData;
+import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.IOUtils;
-import org.apache.poi.xssf.model.XSSFWritableModel;
+import org.apache.poi.POIXMLDocumentPart;
+import org.apache.poi.POIXMLException;
+import org.apache.poi.POIXMLRelation;
import org.openxml4j.opc.PackagePart;
+import org.openxml4j.opc.PackageRelationship;
/**
- * Raw picture data, normally attached to a
- * vmlDrawing
+ * Raw picture data, normally attached to a SpreadsheetML Drawing.
+ * As a rule, pictures are stored in the /xl/media/ part of a SpreadsheetML package.
*/
-public class XSSFPictureData implements PictureData, XSSFWritableModel {
- private PackagePart packagePart;
- private String originalId;
+public class XSSFPictureData extends POIXMLDocumentPart implements PictureData {
- public XSSFPictureData(PackagePart packagePart, String originalId) {
- this(packagePart);
- this.originalId = originalId;
- }
-
- public XSSFPictureData(PackagePart packagePart) {
- this.packagePart = packagePart;
+ /**
+ * Relationships for each known picture type
+ */
+ protected static final POIXMLRelation[] RELATIONS;
+ static {
+ RELATIONS = new POIXMLRelation[8];
+ RELATIONS[Workbook.PICTURE_TYPE_EMF] = new POIXMLRelation("image/x-emf", XSSFRelation.IMAGES.getRelation(), "/xl/media/image#.emf");
+ RELATIONS[Workbook.PICTURE_TYPE_WMF] = new POIXMLRelation("image/x-wmf", XSSFRelation.IMAGES.getRelation(), "/xl/media/image#.wmf");
+ RELATIONS[Workbook.PICTURE_TYPE_PICT] = new POIXMLRelation("image/pict", XSSFRelation.IMAGES.getRelation(), "/xl/media/image#.pict");
+ RELATIONS[Workbook.PICTURE_TYPE_JPEG] = new POIXMLRelation("image/jpeg", XSSFRelation.IMAGES.getRelation(), "/xl/media/image#.jpeg");
+ RELATIONS[Workbook.PICTURE_TYPE_PNG] = new POIXMLRelation("image/png", XSSFRelation.IMAGES.getRelation(), "/xl/media/image#.png");
+ RELATIONS[Workbook.PICTURE_TYPE_DIB] = new POIXMLRelation("image/dib", XSSFRelation.IMAGES.getRelation(), "/xl/media/image#.dib");
}
- public String getOriginalId() {
- return originalId;
+ /**
+ * Create a new XSSFPictureData node
+ *
+ * @see org.apache.poi.xssf.usermodel.XSSFWorkbook#addPicture(byte[], int)
+ */
+ public XSSFPictureData() {
+ super(null, null);
}
-
- protected PackagePart getPart() {
- return packagePart;
+
+ /**
+ * Construct XSSFPictureData from a package part
+ *
+ * @param part the package part holding the drawing data,
+ * @param rel the package relationship holding this drawing,
+ * the relationship type must be http://schemas.openxmlformats.org/officeDocument/2006/relationships/image
+ */
+ public XSSFPictureData(PackagePart part, PackageRelationship rel) {
+ super(part, rel);
}
-
- public void writeTo(OutputStream out) throws IOException {
- IOUtils.copy(packagePart.getInputStream(), out);
- }
+ /**
+ * Gets the picture data as a byte array.
+ *
+ * Note, that this call might be expensive since all the picture data is copied into a temporary byte array.
+ * You can grab the picture data directly from the underlying package part as follows:
+ *
+ *
+ * InputStream is = getPackagePart().getInputStream();
+ *
+ *
+ *
+ * @return the picture data.
+ */
public byte[] getData() {
- try {
- return IOUtils.toByteArray(packagePart.getInputStream());
- } catch(IOException e) {
- throw new RuntimeException(e);
- }
+ try {
+ return IOUtils.toByteArray(getPackagePart().getInputStream());
+ } catch(IOException e) {
+ throw new POIXMLException(e);
+ }
}
+ /**
+ * Suggests a file extension for this image.
+ *
+ * @return the file extension.
+ */
public String suggestFileExtension() {
- return packagePart.getPartName().getExtension();
+ return getPackagePart().getPartName().getExtension();
+ }
+
+ /**
+ * Return an integer constant that specifies type of this picture
+ *
+ * @return an integer constant that specifies type of this picture
+ * @see Workbook#PICTURE_TYPE_EMF
+ * @see Workbook#PICTURE_TYPE_WMF
+ * @see Workbook#PICTURE_TYPE_PICT
+ * @see Workbook#PICTURE_TYPE_JPEG
+ * @see Workbook#PICTURE_TYPE_PNG
+ * @see Workbook#PICTURE_TYPE_DIB
+ */
+ public int getPictureType(){
+ String contentType = getPackagePart().getContentType();
+ for (int i = 0; i < RELATIONS.length; i++) {
+ if(RELATIONS[i] == null) continue;
+
+ if(RELATIONS[i].getContentType().equals(contentType)){
+ return i;
+ }
+ }
+ return 0;
}
}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java
index 42781a5926..53197200b1 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java
@@ -83,7 +83,7 @@ public final class XSSFRelation extends POIXMLRelation {
StylesTable.class
);
public static final XSSFRelation DRAWINGS = new XSSFRelation(
- "application/vnd.openxmlformats-officedocument.drawingml.chart+xml",
+ "application/vnd.openxmlformats-officedocument.drawing+xml",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
"/xl/drawings/drawing#.xml",
null
@@ -95,9 +95,10 @@ public final class XSSFRelation extends POIXMLRelation {
Drawing.class
);
public static final XSSFRelation IMAGES = new XSSFRelation(
- "image/x-emf", // TODO
+ //client will substitute $type and $ext with the appropriate values depending on the passed data
+ "image/$type",
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
- "/xl/media/image#.emf",
+ "/xl/media/image#.$ext",
null
);
public static final XSSFRelation SHEET_COMMENTS = create(
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShape.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShape.java
new file mode 100755
index 0000000000..861c96b525
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFShape.java
@@ -0,0 +1,93 @@
+/* ====================================================================
+ 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.xssf.usermodel;
+
+import org.apache.xmlbeans.XmlObject;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTAbsoluteAnchor;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTOneCellAnchor;
+import org.openxmlformats.schemas.drawingml.x2006.chartDrawing.CTGroupShape;
+
+/**
+ * Represents a shape in a SpreadsheetML drawing.
+ *
+ * @author Yegor Kozlov
+ */
+public abstract class XSSFShape {
+ public static final int EMU_PER_POINT = 12700;
+
+
+ /**
+ * Shape container. Can be CTTwoCellAnchor, CTOneCellAnchor, CTAbsoluteAnchor or CTGroupShape
+ */
+ private XmlObject spContainer;
+
+ /**
+ * Parent drawing
+ */
+ private XSSFDrawing drawing;
+
+ /**
+ * The parent shape, always not-null for shapes in groups
+ */
+ private XSSFShape parent;
+
+ /**
+ * Construct a new XSSFSimpleShape object.
+ *
+ * @param parent the XSSFDrawing that owns this shape
+ * @param anchor an object that encloses the shape bean,
+ * can be CTTwoCellAnchor, CTOneCellAnchor, CTAbsoluteAnchor or CTGroupShape
+ */
+ protected XSSFShape(XSSFDrawing parent, XmlObject anchor){
+ drawing = parent;
+ if(!(anchor instanceof CTTwoCellAnchor) && !(anchor instanceof CTOneCellAnchor) &&
+ !(anchor instanceof CTAbsoluteAnchor) && !(anchor instanceof CTGroupShape)) {
+ throw new IllegalArgumentException("anchor must be one of the following types: " +
+ "CTTwoCellAnchor, CTOneCellAnchor, CTAbsoluteAnchor or CTGroupShape");
+ }
+ spContainer = anchor;
+ }
+
+ /**
+ * Return the anchor bean that encloses this shape.
+ * Can be CTTwoCellAnchor, CTOneCellAnchor, CTAbsoluteAnchor or CTGroupShape.
+ *
+ * @return the anchor bean that encloses this shape
+ */
+ public XmlObject getShapeContainer(){
+ return spContainer;
+ }
+
+ /**
+ * Return the drawing that owns this shape
+ *
+ * @return the parent drawing that owns this shape
+ */
+ public XSSFDrawing getDrawing(){
+ return drawing;
+ }
+
+ /**
+ * Gets the parent shape.
+ */
+ public XSSFShape getParent()
+ {
+ return parent;
+ }
+
+}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
index 638fea7f14..4db48fe2c7 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
@@ -19,11 +19,7 @@ package org.apache.poi.xssf.usermodel;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
import javax.xml.namespace.QName;
import org.apache.poi.hssf.util.PaneInformation;
@@ -31,7 +27,6 @@ import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CommentsSource;
import org.apache.poi.ss.usermodel.Footer;
import org.apache.poi.ss.usermodel.Header;
-import org.apache.poi.ss.usermodel.Patriarch;
import org.apache.poi.ss.usermodel.PrintSetup;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
@@ -40,16 +35,18 @@ import org.apache.poi.ss.util.CellReference;
import org.apache.poi.ss.util.Region;
import org.apache.poi.xssf.model.CommentsTable;
import org.apache.poi.xssf.model.Control;
-import org.apache.poi.xssf.model.Drawing;
import org.apache.poi.xssf.usermodel.helpers.ColumnHelper;
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLException;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.POILogFactory;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.XmlException;
import org.openxml4j.opc.PackagePart;
import org.openxml4j.opc.PackageRelationship;
import org.openxml4j.opc.PackageRelationshipCollection;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
+import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
/**
@@ -62,6 +59,8 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
*
*/
public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
+ private static POILogger logger = POILogFactory.getLogger(XSSFSheet.class);
+
protected CTSheet sheet;
protected CTWorksheet worksheet;
protected CTDialogsheet dialogsheet;
@@ -72,7 +71,6 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
protected CTMergeCells ctMergeCells;
- protected List drawings;
protected List controls;
@@ -149,7 +147,6 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
ctFormat.setDefaultRowHeight(15.0);
CTSheetView ctView = worksheet.addNewSheetViews().addNewSheetView();
- ctView.setTabSelected(true);
ctView.setWorkbookViewId(0);
worksheet.addNewDimension().setRef("A1");
@@ -167,11 +164,6 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
return worksheet;
}
- public List getDrawings()
- {
- return drawings;
- }
-
public List getControls()
{
return controls;
@@ -263,9 +255,42 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
columnHelper.setColBestFit(column, true);
}
- public Patriarch createDrawingPatriarch() {
- // TODO Auto-generated method stub
- return null;
+ /**
+ * Create a new SpreadsheetML drawing. If this sheet already contains a drawing - return that.
+ *
+ * @return a SpreadsheetML drawing
+ */
+ public XSSFDrawing createDrawingPatriarch() {
+ XSSFDrawing drawing = null;
+ CTDrawing ctDrawing = worksheet.getDrawing();
+ if(ctDrawing == null) {
+ //drawingNumber = #drawings.size() + 1
+ int drawingNumber = getPackagePart().getPackage().getPartsByRelationshipType(XSSFRelation.DRAWINGS.getRelation()).size() + 1;
+ drawing = (XSSFDrawing)createRelationship(XSSFRelation.DRAWINGS, XSSFDrawing.class, drawingNumber);
+ String relId = drawing.getPackageRelationship().getId();
+
+ //add CT_Drawing element which indicates that this sheet contains drawing components built on the drawingML platform.
+ //The relationship Id references the part containing the drawingML definitions.
+ ctDrawing = worksheet.addNewDrawing();
+ ctDrawing.setId(relId);
+ } else {
+ //search the referenced drawing in the list of the sheet's relations
+ for(POIXMLDocumentPart p : getRelations()){
+ if(p instanceof XSSFDrawing) {
+ XSSFDrawing dr = (XSSFDrawing)p;
+ String drId = dr.getPackageRelationship().getId();
+ if(drId.equals(ctDrawing.getId())){
+ drawing = dr;
+ break;
+ }
+ break;
+ }
+ }
+ if(drawing == null){
+ logger.log(POILogger.ERROR, "Can't find drawing with id=" + ctDrawing.getId() + " in the list of the sheet's relationships");
+ }
+ }
+ return drawing;
}
/**
@@ -406,14 +431,16 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
}
public int getColumnWidth(int columnIndex) {
- return (int) columnHelper.getColumn(columnIndex, false).getWidth();
+ CTCol col = columnHelper.getColumn(columnIndex, false);
+ return col == null ? getDefaultColumnWidth() : (int)col.getWidth();
}
public short getColumnWidth(short column) {
return (short) getColumnWidth(column & 0xFFFF);
}
public int getDefaultColumnWidth() {
- return (int)getSheetTypeSheetFormatPr().getDefaultColWidth();
+ CTSheetFormatPr pr = getSheetTypeSheetFormatPr();
+ return pr.isSetDefaultColWidth() ? (int)pr.getDefaultColWidth() : (int)pr.getBaseColWidth();
}
public short getDefaultRowHeight() {
@@ -1570,14 +1597,15 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
xmlOptions.setSaveSyntheticDocumentElement(new QName(CTWorksheet.type.getName().getNamespaceURI(), "worksheet"));
+
+ Map map = new HashMap();
+ map.put(STRelationshipId.type.getName().getNamespaceURI(), "r");
+ xmlOptions.setSaveSuggestedPrefixes(map);
+
PackagePart part = getPackagePart();
OutputStream out = part.getOutputStream();
worksheet.save(out, xmlOptions);
out.close();
}
- protected void setParent(POIXMLDocumentPart p){
- this.parent = p;
- }
-
}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSimpleShape.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSimpleShape.java
new file mode 100755
index 0000000000..a564013d13
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSimpleShape.java
@@ -0,0 +1,178 @@
+/* ====================================================================
+ 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.xssf.usermodel;
+
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTTwoCellAnchor;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShape;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTShapeNonVisual;
+import org.openxmlformats.schemas.drawingml.x2006.main.*;
+
+/**
+ * Represents an auto-shape in a SpreadsheetML drawing.
+ *
+ * @author Yegor Kozlov
+ */
+public class XSSFSimpleShape extends XSSFShape {
+
+ private CTShape ctShape;
+
+ /**
+ * Construct a new XSSFSimpleShape object.
+ *
+ * @param parent the XSSFDrawing that owns this shape
+ * @param anchor the two cell anchor placeholder for this shape,
+ * this object encloses the shape bean that holds all the shape properties
+ */
+ protected XSSFSimpleShape(XSSFDrawing parent, CTTwoCellAnchor anchor) {
+ super(parent, anchor);
+ ctShape = anchor.addNewSp();
+ newShape(ctShape);
+ }
+
+ /**
+ * Initialize default structure of a new auto-shape
+ *
+ * @param shape newly created shape to initialize
+ */
+ private static void newShape(CTShape shape) {
+ CTShapeNonVisual nv = shape.addNewNvSpPr();
+ CTNonVisualDrawingProps nvp = nv.addNewCNvPr();
+ int shapeId = 1;
+ nvp.setId(shapeId);
+ nvp.setName("Shape " + shapeId);
+ nv.addNewCNvSpPr();
+
+ CTShapeProperties sp = shape.addNewSpPr();
+ CTTransform2D t2d = sp.addNewXfrm();
+ CTPositiveSize2D p1 = t2d.addNewExt();
+ p1.setCx(0);
+ p1.setCy(0);
+ CTPoint2D p2 = t2d.addNewOff();
+ p2.setX(0);
+ p2.setY(0);
+
+ CTPresetGeometry2D geom = sp.addNewPrstGeom();
+ geom.setPrst(STShapeType.RECT);
+ geom.addNewAvLst();
+
+ CTShapeStyle style = shape.addNewStyle();
+ CTSchemeColor scheme = style.addNewLnRef().addNewSchemeClr();
+ scheme.setVal(STSchemeColorVal.ACCENT_1);
+ scheme.addNewShade().setVal(50000);
+ style.getLnRef().setIdx(2);
+
+ CTStyleMatrixReference fillref = style.addNewFillRef();
+ fillref.setIdx(1);
+ fillref.addNewSchemeClr().setVal(STSchemeColorVal.ACCENT_1);
+
+ CTStyleMatrixReference effectRef = style.addNewEffectRef();
+ effectRef.setIdx(0);
+ effectRef.addNewSchemeClr().setVal(STSchemeColorVal.ACCENT_1);
+
+ CTFontReference fontRef = style.addNewFontRef();
+ fontRef.setIdx(STFontCollectionIndex.MINOR);
+ fontRef.addNewSchemeClr().setVal(STSchemeColorVal.LT_1);
+
+ CTTextBody body = shape.addNewTxBody();
+ CTTextBodyProperties bodypr = body.addNewBodyPr();
+ bodypr.setAnchor(STTextAnchoringType.CTR);
+ bodypr.setRtlCol(false);
+ CTTextParagraph p = body.addNewP();
+ p.addNewPPr().setAlgn(STTextAlignType.CTR);
+
+ body.addNewLstStyle();
+ }
+
+ /**
+ * Gets the shape type, one of the constants defined in {@link ShapeTypes}.
+ *
+ * @return the shape type
+ * @see ShapeTypes
+ */
+ public int getShapeType() {
+ return ctShape.getSpPr().getPrstGeom().getPrst().intValue();
+ }
+
+ /**
+ * Sets the shape types.
+ *
+ * @param type the shape type, one of the constants defined in {@link ShapeTypes}.
+ * @see ShapeTypes
+ */
+ public void setShapeType(int type) {
+ ctShape.getSpPr().getPrstGeom().setPrst(STShapeType.Enum.forInt(type));
+ }
+
+
+ /**
+ * Whether this shape is not filled with a color
+ *
+ * @return true if this shape is not filled with a color.
+ */
+ public boolean isNoFill() {
+ return ctShape.getSpPr().isSetNoFill();
+ }
+
+ /**
+ * Sets whether this shape is filled or transparent.
+ *
+ * @param noFill if true then no fill will be applied to the shape element.
+ */
+ public void setNoFill(boolean noFill) {
+ CTShapeProperties props = ctShape.getSpPr();
+ //unset solid and pattern fills if they are set
+ if (props.isSetPattFill()) props.unsetPattFill();
+ if (props.isSetSolidFill()) props.unsetSolidFill();
+
+ props.setNoFill(CTNoFillProperties.Factory.newInstance());
+ }
+
+ /**
+ * Sets the color used to fill this shape using the solid fill pattern.
+ */
+ public void setFillColor(int red, int green, int blue) {
+ CTShapeProperties props = ctShape.getSpPr();
+ CTSolidColorFillProperties fill = props.isSetSolidFill() ? props.getSolidFill() : props.addNewSolidFill();
+ CTSRgbColor rgb = CTSRgbColor.Factory.newInstance();
+ rgb.setVal(new byte[]{(byte)red, (byte)green, (byte)blue});
+ fill.setSrgbClr(rgb);
+ }
+
+ /**
+ * The color applied to the lines of this shape.
+ */
+ public void setLineStyleColor( int red, int green, int blue ) {
+ CTShapeProperties props = ctShape.getSpPr();
+ CTLineProperties ln = props.isSetLn() ? props.getLn() : props.addNewLn();
+ CTSolidColorFillProperties fill = ln.isSetSolidFill() ? ln.getSolidFill() : ln.addNewSolidFill();
+ CTSRgbColor rgb = CTSRgbColor.Factory.newInstance();
+ rgb.setVal(new byte[]{(byte)red, (byte)green, (byte)blue});
+ fill.setSrgbClr(rgb);
+ }
+
+ /**
+ * Specifies the width to be used for the underline stroke.
+ *
+ * @param lineWidth width in points
+ */
+ public void setLineWidth( double lineWidth ) {
+ CTShapeProperties props = ctShape.getSpPr();
+ CTLineProperties ln = props.isSetLn() ? props.getLn() : props.addNewLn();
+ ln.setW((int)(lineWidth*EMU_PER_POINT));
+ }
+
+}
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
index 674a422440..bcc61660f6 100644
--- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
+++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
@@ -19,10 +19,9 @@ package org.apache.poi.xssf.usermodel;
import java.io.IOException;
import java.io.OutputStream;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.HashMap;
-import java.util.Iterator;
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.util.*;
import javax.xml.namespace.QName;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.POIXMLDocumentPart;
@@ -35,6 +34,7 @@ import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.PackageHelper;
+import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.model.*;
import org.apache.poi.POIXMLException;
import org.apache.xmlbeans.XmlObject;
@@ -44,6 +44,7 @@ import org.openxml4j.exceptions.OpenXML4JException;
import org.openxml4j.opc.*;
import org.openxml4j.opc.Package;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
+import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId;
/**
* High level representation of a SpreadsheetML workbook. This is the first object most users
@@ -93,6 +94,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable pictures;
+
private static POILogger log = POILogFactory.getLogger(XSSFWorkbook.class);
/**
@@ -250,9 +256,33 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable getAllPictures() {
- // In OOXML pictures are referred to in sheets
- List pictures = new LinkedList();
- for(POIXMLDocumentPart p : getRelations()){
- if (p instanceof XSSFSheet) {
- PackagePart sheetPart = p.getPackagePart();
- try {
- PackageRelationshipCollection prc = sheetPart.getRelationshipsByType(XSSFRelation.DRAWINGS.getRelation());
- for (PackageRelationship rel : prc) {
- PackagePart drawingPart = getTargetPart(rel);
- PackageRelationshipCollection prc2 = drawingPart.getRelationshipsByType(XSSFRelation.IMAGES.getRelation());
- for (PackageRelationship rel2 : prc2) {
- PackagePart imagePart = getTargetPart(rel2);
- XSSFPictureData pd = new XSSFPictureData(imagePart);
- pictures.add(pd);
+ public List getAllPictures() {
+ if(pictures == null) {
+ //In OOXML pictures are referred to in sheets,
+ //dive into sheet's relations, select drawings and their images
+ pictures = new ArrayList();
+ for(XSSFSheet sh : sheets){
+ for(POIXMLDocumentPart dr : sh.getRelations()){
+ if(dr instanceof XSSFDrawing){
+ for(POIXMLDocumentPart img : dr.getRelations()){
+ if(img instanceof XSSFPictureData){
+ pictures.add((XSSFPictureData)img);
+ }
}
}
- } catch (InvalidFormatException e) {
- throw new POIXMLException(e.getMessage(), e);
}
-
}
}
return pictures;
@@ -705,7 +728,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable pictures = workbook.getAllPictures();
+ List pictures = workbook.getAllPictures();
assertEquals(1, pictures.size());
}
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java
new file mode 100755
index 0000000000..c2ff6acad3
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFDrawing.java
@@ -0,0 +1,70 @@
+/* ====================================================================
+ 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.xssf.usermodel;
+
+import junit.framework.TestCase;
+import org.apache.poi.xssf.XSSFTestDataSamples;
+import org.apache.poi.POIXMLDocumentPart;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing;
+
+import java.util.List;
+import java.io.IOException;
+
+/**
+ * @author Yegor Kozlov
+ */
+public class TestXSSFDrawing extends TestCase {
+ public void testRead(){
+ XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("WithDrawing.xlsx");
+ XSSFSheet sheet = wb.getSheetAt(0);
+ //the sheet has one relationship and it is XSSFDrawing
+ List rels = sheet.getRelations();
+ assertEquals(1, rels.size());
+ assertTrue(rels.get(0) instanceof XSSFDrawing);
+
+ XSSFDrawing drawing = (XSSFDrawing)rels.get(0);
+ //sheet.createDrawingPatriarch() should return the same instance of XSSFDrawing
+ assertSame(drawing, sheet.createDrawingPatriarch());
+ String drawingId = drawing.getPackageRelationship().getId();
+
+ //there should be a relation to this drawing in the worksheet
+ assertTrue(sheet.getWorksheet().isSetDrawing());
+ assertEquals(drawingId, sheet.getWorksheet().getDrawing().getId());
+
+ }
+
+ public void testNew(){
+ XSSFWorkbook wb = new XSSFWorkbook();
+ XSSFSheet sheet = wb.createSheet();
+ //multiple calls of createDrawingPatriarch should return the same instance of XSSFDrawing
+ XSSFDrawing dr1 = sheet.createDrawingPatriarch();
+ XSSFDrawing dr2 = sheet.createDrawingPatriarch();
+ assertSame(dr1, dr2);
+
+ List rels = sheet.getRelations();
+ assertEquals(1, rels.size());
+ assertTrue(rels.get(0) instanceof XSSFDrawing);
+
+ XSSFDrawing drawing = (XSSFDrawing)rels.get(0);
+ String drawingId = drawing.getPackageRelationship().getId();
+
+ //there should be a relation to this drawing in the worksheet
+ assertTrue(sheet.getWorksheet().isSetDrawing());
+ assertEquals(drawingId, sheet.getWorksheet().getDrawing().getId());
+
+ }
+}
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPicture.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPicture.java
new file mode 100755
index 0000000000..eb405f1e12
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPicture.java
@@ -0,0 +1,55 @@
+/* ====================================================================
+ 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.xssf.usermodel;
+
+import junit.framework.TestCase;
+import org.apache.poi.xssf.XSSFTestDataSamples;
+import org.apache.poi.POIXMLDocumentPart;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing;
+
+import java.util.List;
+import java.util.Arrays;
+import java.io.IOException;
+
+/**
+ * @author Yegor Kozlov
+ */
+public class TestXSSFPicture extends TestCase {
+
+ public void testCreate(){
+ XSSFWorkbook wb = new XSSFWorkbook();
+ XSSFSheet sheet = wb.createSheet();
+ XSSFDrawing drawing = sheet.createDrawingPatriarch();
+
+ byte[] jpegData = "test jpeg data".getBytes();
+
+ List pictures = wb.getAllPictures();
+ assertEquals(0, pictures.size());
+
+ int jpegIdx = wb.addPicture(jpegData, XSSFWorkbook.PICTURE_TYPE_JPEG);
+ assertEquals(1, pictures.size());
+ assertEquals("jpeg", pictures.get(jpegIdx).suggestFileExtension());
+ assertTrue(Arrays.equals(jpegData, pictures.get(jpegIdx).getData()));
+
+ XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, 1, 1, 10, 30);
+ XSSFPicture shape = drawing.createPicture(anchor, jpegIdx);
+ assertTrue(anchor.equals(shape.getAnchor()));
+ assertNotNull(shape.getPictureData());
+ assertTrue(Arrays.equals(jpegData, shape.getPictureData().getData()));
+
+ }
+}
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPictureData.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPictureData.java
new file mode 100755
index 0000000000..50b90e746e
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFPictureData.java
@@ -0,0 +1,104 @@
+/* ====================================================================
+ 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.xssf.usermodel;
+
+import junit.framework.TestCase;
+import org.apache.poi.xssf.XSSFTestDataSamples;
+import org.apache.poi.POIXMLDocumentPart;
+import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTDrawing;
+
+import java.util.List;
+import java.util.Arrays;
+import java.io.IOException;
+
+/**
+ * @author Yegor Kozlov
+ */
+public class TestXSSFPictureData extends TestCase {
+ public void testRead(){
+ XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("WithDrawing.xlsx");
+ List pictures = wb.getAllPictures();
+ //wb.getAllPictures() should return the same instance across multiple calls
+ assertSame(pictures, wb.getAllPictures());
+
+ assertEquals(5, pictures.size());
+ String[] ext = {"jpeg", "emf", "png", "emf", "wmf"};
+ for (int i = 0; i < pictures.size(); i++) {
+ assertEquals(ext[i], pictures.get(i).suggestFileExtension());
+ }
+
+ int num = pictures.size();
+
+ byte[] pictureData = {0xA, 0xB, 0XC, 0xD, 0xE, 0xF};
+
+ int idx = wb.addPicture(pictureData, XSSFWorkbook.PICTURE_TYPE_JPEG);
+ assertEquals(num + 1, pictures.size());
+ //idx is 0-based index in the #pictures array
+ assertEquals(pictures.size() - 1, idx);
+ XSSFPictureData pict = pictures.get(idx);
+ assertEquals("jpeg", pict.suggestFileExtension());
+ assertTrue(Arrays.equals(pictureData, pict.getData()));
+ }
+
+ public void testNew(){
+ XSSFWorkbook wb = new XSSFWorkbook();
+ XSSFSheet sheet = wb.createSheet();
+ XSSFDrawing drawing = sheet.createDrawingPatriarch();
+
+ byte[] jpegData = "test jpeg data".getBytes();
+ byte[] wmfData = "test wmf data".getBytes();
+ byte[] pngData = "test png data".getBytes();
+
+ List pictures = wb.getAllPictures();
+ assertEquals(0, pictures.size());
+
+ int jpegIdx = wb.addPicture(jpegData, XSSFWorkbook.PICTURE_TYPE_JPEG);
+ assertEquals(1, pictures.size());
+ assertEquals("jpeg", pictures.get(jpegIdx).suggestFileExtension());
+ assertTrue(Arrays.equals(jpegData, pictures.get(jpegIdx).getData()));
+
+ int wmfIdx = wb.addPicture(wmfData, XSSFWorkbook.PICTURE_TYPE_WMF);
+ assertEquals(2, pictures.size());
+ assertEquals("wmf", pictures.get(wmfIdx).suggestFileExtension());
+ assertTrue(Arrays.equals(wmfData, pictures.get(wmfIdx).getData()));
+
+ int pngIdx = wb.addPicture(pngData, XSSFWorkbook.PICTURE_TYPE_PNG);
+ assertEquals(3, pictures.size());
+ assertEquals("png", pictures.get(pngIdx).suggestFileExtension());
+ assertTrue(Arrays.equals(pngData, pictures.get(pngIdx).getData()));
+
+ //TODO finish usermodel API for XSSFPicture
+ XSSFPicture p1 = drawing.createPicture(new XSSFClientAnchor(), jpegIdx);
+ XSSFPicture p2 = drawing.createPicture(new XSSFClientAnchor(), wmfIdx);
+ XSSFPicture p3 = drawing.createPicture(new XSSFClientAnchor(), pngIdx);
+
+ //check that the added pictures are accessible after write
+ wb = XSSFTestDataSamples.writeOutAndReadBack(wb);
+ List pictures2 = wb.getAllPictures();
+ assertEquals(3, pictures2.size());
+
+ assertEquals("jpeg", pictures2.get(jpegIdx).suggestFileExtension());
+ assertTrue(Arrays.equals(jpegData, pictures2.get(jpegIdx).getData()));
+
+ assertEquals("wmf", pictures2.get(wmfIdx).suggestFileExtension());
+ assertTrue(Arrays.equals(wmfData, pictures2.get(wmfIdx).getData()));
+
+ assertEquals("png", pictures2.get(pngIdx).suggestFileExtension());
+ assertTrue(Arrays.equals(pngData, pictures2.get(pngIdx).getData()));
+
+ }
+}
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java
index 402725aca5..0400c5b4d7 100644
--- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java
+++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java
@@ -41,7 +41,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPane;
public class TestXSSFSheet extends TestCase {
-
+
public void testRowIterator() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -55,7 +55,7 @@ public class TestXSSFSheet extends TestCase {
assertEquals(row2, it.next());
assertFalse(it.hasNext());
}
-
+
public void testGetRow() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -63,17 +63,17 @@ public class TestXSSFSheet extends TestCase {
Cell cell = row1.createCell((short) 0);
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
cell.setCellValue((double) 1000);
-
+
// Test getting a row and check its cell's value
Row row_got = sheet.getRow(0);
Cell cell_got = row_got.getCell((short) 0);
assertEquals((double) 1000, cell_got.getNumericCellValue());
}
-
+
public void testCreateRow() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
-
+
// Test row creation with consecutive indexes
Row row1 = sheet.createRow(0);
Row row2 = sheet.createRow(1);
@@ -84,11 +84,11 @@ public class TestXSSFSheet extends TestCase {
assertEquals(row1, it.next());
assertTrue(it.hasNext());
assertEquals(row2, it.next());
-
+
// Test row creation with non consecutive index
Row row101 = sheet.createRow(100);
assertNotNull(row101);
-
+
// Test overwriting an existing row
Row row2_ovrewritten = sheet.createRow(1);
Cell cell = row2_ovrewritten.createCell((short) 0);
@@ -102,7 +102,7 @@ public class TestXSSFSheet extends TestCase {
assertEquals(row2_ovrewritten, row2_overwritten_copy);
assertEquals(row2_overwritten_copy.getCell((short) 0).getNumericCellValue(), (double) 100);
}
-
+
public void testRemoveRow() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -114,7 +114,7 @@ public class TestXSSFSheet extends TestCase {
assertNull(sheet.getRow(2));
assertNotNull(sheet.getRow(1));
}
-
+
public void testGetSetDefaultRowHeight() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -131,17 +131,17 @@ public class TestXSSFSheet extends TestCase {
sheet.setDefaultRowHeightInPoints((short) 17);
assertEquals((short) 340, sheet.getDefaultRowHeight());
}
-
+
public void testGetSetDefaultColumnWidth() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
// Test that default column width set by the constructor
- assertEquals((short) 0, sheet.getDefaultColumnWidth());
+ assertEquals((short) 8, sheet.getDefaultColumnWidth());
// Set a new default column width and get its value
sheet.setDefaultColumnWidth((short) 14);
assertEquals((short) 14, sheet.getDefaultColumnWidth());
}
-
+
public void testGetFirstLastRowNum() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -149,9 +149,9 @@ public class TestXSSFSheet extends TestCase {
Row row1 = sheet.createRow(0);
Row row2 = sheet.createRow(1);
assertEquals(0, sheet.getFirstRowNum());
- assertEquals(9, sheet.getLastRowNum());
+ assertEquals(9, sheet.getLastRowNum());
}
-
+
public void testGetPhysicalNumberOfRows() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -160,7 +160,7 @@ public class TestXSSFSheet extends TestCase {
Row row2 = sheet.createRow(1);
assertEquals(3, sheet.getPhysicalNumberOfRows());
}
-
+
public void testGetSetRowBreaks() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -173,7 +173,7 @@ public class TestXSSFSheet extends TestCase {
sheet.setRowBreak(1);
assertEquals(2, sheet.getRowBreaks().length);
}
-
+
public void testRemoveRowBreak() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -184,7 +184,7 @@ public class TestXSSFSheet extends TestCase {
sheet.removeRowBreak(1);
assertEquals(1, sheet.getRowBreaks().length);
}
-
+
public void testGetSetColumnBreaks() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -195,7 +195,7 @@ public class TestXSSFSheet extends TestCase {
sheet.setColumnBreak((short) 11223);
assertEquals(2, sheet.getColumnBreaks().length);
}
-
+
public void testRemoveColumnBreak() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -209,7 +209,7 @@ public class TestXSSFSheet extends TestCase {
sheet.removeColumnBreak((short) 15);
assertEquals(1, sheet.getColumnBreaks().length);
}
-
+
public void testIsRowColumnBroken() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -220,7 +220,7 @@ public class TestXSSFSheet extends TestCase {
sheet.setColumnBreak((short) 3);
assertTrue(sheet.isColumnBroken((short) 3));
}
-
+
public void testGetSetAutoBreaks() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -228,7 +228,7 @@ public class TestXSSFSheet extends TestCase {
sheet.setAutobreaks(false);
assertFalse(sheet.getAutobreaks());
}
-
+
public void testIsSetFitToPage() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -238,7 +238,7 @@ public class TestXSSFSheet extends TestCase {
sheet.setFitToPage(false);
assertFalse(sheet.getFitToPage());
}
-
+
public void testGetSetMargin() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
@@ -270,7 +270,7 @@ public class TestXSSFSheet extends TestCase {
assertEquals((double) 14, sheet.getMargin((short) 5));
sheet.setMargin((short) 5, 15);
assertEquals((double) 15, sheet.getMargin((short) 5));
-
+
// Test that nothing happens if another margin constant is given (E.G. 65)
sheet.setMargin((short) 65, 15);
assertEquals((double) 10, sheet.getMargin((short) 0));
@@ -280,83 +280,83 @@ public class TestXSSFSheet extends TestCase {
assertEquals((double) 14, sheet.getMargin((short) 4));
assertEquals((double) 15, sheet.getMargin((short) 5));
}
-
+
public void testGetFooter() {
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = (XSSFSheet)workbook.createSheet("Sheet 1");
assertNotNull(sheet.getFooter());
sheet.getFooter().setCenter("test center footer");
assertEquals("test center footer", sheet.getFooter().getCenter());
-
+
// Default is odd footer
assertNotNull(sheet.getOddFooter());
assertEquals("test center footer", sheet.getOddFooter().getCenter());
}
-
+
public void testExistingHeaderFooter() throws Exception {
File xml = new File(
System.getProperty("HSSF.testdata.path") +
File.separator + "45540_classic_Header.xlsx"
);
assertTrue(xml.exists());
-
+
XSSFWorkbook workbook = new XSSFWorkbook(xml.toString());
XSSFOddHeader hdr;
XSSFOddFooter ftr;
-
+
// Sheet 1 has a header with center and right text
XSSFSheet s1 = (XSSFSheet)workbook.getSheetAt(0);
assertNotNull(s1.getHeader());
assertNotNull(s1.getFooter());
- hdr = (XSSFOddHeader)s1.getHeader();
- ftr = (XSSFOddFooter)s1.getFooter();
-
+ hdr = (XSSFOddHeader)s1.getHeader();
+ ftr = (XSSFOddFooter)s1.getFooter();
+
assertEquals("&Ctestdoc&Rtest phrase", hdr.getText());
assertEquals(null, ftr.getText());
-
+
assertEquals("", hdr.getLeft());
assertEquals("testdoc", hdr.getCenter());
assertEquals("test phrase", hdr.getRight());
-
+
assertEquals("", ftr.getLeft());
assertEquals("", ftr.getCenter());
assertEquals("", ftr.getRight());
-
-
+
+
// Sheet 2 has a footer, but it's empty
XSSFSheet s2 = (XSSFSheet)workbook.getSheetAt(1);
assertNotNull(s2.getHeader());
assertNotNull(s2.getFooter());
- hdr = (XSSFOddHeader)s2.getHeader();
- ftr = (XSSFOddFooter)s2.getFooter();
-
+ hdr = (XSSFOddHeader)s2.getHeader();
+ ftr = (XSSFOddFooter)s2.getFooter();
+
assertEquals(null, hdr.getText());
assertEquals("&L&F", ftr.getText());
-
+
assertEquals("", hdr.getLeft());
assertEquals("", hdr.getCenter());
assertEquals("", hdr.getRight());
-
+
assertEquals("&F", ftr.getLeft());
assertEquals("", ftr.getCenter());
assertEquals("", ftr.getRight());
-
-
+
+
// Save and reload
XSSFWorkbook wb = XSSFTestDataSamples.writeOutAndReadBack(workbook);
-
+
hdr = (XSSFOddHeader)wb.getSheetAt(0).getHeader();
- ftr = (XSSFOddFooter)wb.getSheetAt(0).getFooter();
-
+ ftr = (XSSFOddFooter)wb.getSheetAt(0).getFooter();
+
assertEquals("", hdr.getLeft());
assertEquals("testdoc", hdr.getCenter());
assertEquals("test phrase", hdr.getRight());
-
+
assertEquals("", ftr.getLeft());
assertEquals("", ftr.getCenter());
assertEquals("", ftr.getRight());
}
-
+
public void testGetAllHeadersFooters() {
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = (XSSFSheet) workbook.createSheet("Sheet 1");
@@ -366,27 +366,27 @@ public class TestXSSFSheet extends TestCase {
assertNotNull(sheet.getOddHeader());
assertNotNull(sheet.getEvenHeader());
assertNotNull(sheet.getFirstHeader());
-
+
assertEquals("", sheet.getOddFooter().getLeft());
sheet.getOddFooter().setLeft("odd footer left");
assertEquals("odd footer left", sheet.getOddFooter().getLeft());
-
+
assertEquals("", sheet.getEvenFooter().getLeft());
sheet.getEvenFooter().setLeft("even footer left");
assertEquals("even footer left", sheet.getEvenFooter().getLeft());
-
+
assertEquals("", sheet.getFirstFooter().getLeft());
sheet.getFirstFooter().setLeft("first footer left");
assertEquals("first footer left", sheet.getFirstFooter().getLeft());
-
+
assertEquals("", sheet.getOddHeader().getLeft());
sheet.getOddHeader().setLeft("odd header left");
assertEquals("odd header left", sheet.getOddHeader().getLeft());
-
+
assertEquals("", sheet.getOddHeader().getRight());
sheet.getOddHeader().setRight("odd header right");
assertEquals("odd header right", sheet.getOddHeader().getRight());
-
+
assertEquals("", sheet.getOddHeader().getCenter());
sheet.getOddHeader().setCenter("odd header center");
assertEquals("odd header center", sheet.getOddHeader().getCenter());
@@ -395,49 +395,49 @@ public class TestXSSFSheet extends TestCase {
assertEquals("odd footer left", sheet.getFooter().getLeft());
assertEquals("odd header center", sheet.getHeader().getCenter());
}
-
+
public void testGetSetColumnWidth() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
sheet.setColumnWidth((short) 1,(short) 22);
assertEquals(22, sheet.getColumnWidth((short) 1));
-
+
// Now check the low level stuff, and check that's all
// been set correctly
XSSFSheet xs = (XSSFSheet)sheet;
CTWorksheet cts = xs.getWorksheet();
-
+
CTCols[] cols_s = cts.getColsArray();
assertEquals(1, cols_s.length);
CTCols cols = cols_s[0];
assertEquals(1, cols.sizeOfColArray());
CTCol col = cols.getColArray(0);
-
+
// XML is 1 based, POI is 0 based
assertEquals(2, col.getMin());
assertEquals(2, col.getMax());
assertEquals(22.0, col.getWidth());
-
-
+
+
// Now set another
sheet.setColumnWidth((short) 3,(short) 33);
-
+
cols_s = cts.getColsArray();
assertEquals(1, cols_s.length);
cols = cols_s[0];
assertEquals(2, cols.sizeOfColArray());
-
+
col = cols.getColArray(0);
assertEquals(2, col.getMin()); // POI 1
assertEquals(2, col.getMax());
assertEquals(22.0, col.getWidth());
-
+
col = cols.getColArray(1);
assertEquals(4, col.getMin()); // POI 3
assertEquals(4, col.getMax());
assertEquals(33.0, col.getWidth());
}
-
+
public void testGetSetColumnHidden() {
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sheet 1");
diff --git a/src/testcases/org/apache/poi/hssf/data/WithDrawing.xlsx b/src/testcases/org/apache/poi/hssf/data/WithDrawing.xlsx
new file mode 100755
index 0000000000000000000000000000000000000000..862cd374d546de39a91163f2e2cf5720e43a8017
GIT binary patch
literal 101696
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