From: Yegor Kozlov Date: Fri, 10 Oct 2008 14:54:32 +0000 (+0000) Subject: Initial support for SpreadsheetML drawings,implemented XSSFPicture, added ability... X-Git-Tag: ooxml_20081107~31 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b4e74b306c947a57fc97eac555e3a9b3fa85e650;p=poi.git 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 --- 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 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 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 0000000000..862cd374d5 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/WithDrawing.xlsx differ