123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- /* ====================================================================
- 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 static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.math.BigInteger;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
-
- import javax.xml.namespace.QName;
-
- import com.microsoft.schemas.office.excel.CTClientData;
- import com.microsoft.schemas.office.excel.STObjectType;
- import com.microsoft.schemas.office.office.CTIdMap;
- import com.microsoft.schemas.office.office.CTShapeLayout;
- import com.microsoft.schemas.office.office.STConnectType;
- import com.microsoft.schemas.office.office.STInsetMode;
- import com.microsoft.schemas.office.office.ShapelayoutDocument;
- import com.microsoft.schemas.vml.CTGroup;
- import com.microsoft.schemas.vml.CTPath;
- import com.microsoft.schemas.vml.CTShadow;
- import com.microsoft.schemas.vml.CTShape;
- import com.microsoft.schemas.vml.CTShapetype;
- import com.microsoft.schemas.vml.STExt;
- import com.microsoft.schemas.vml.STStrokeJoinStyle;
- import com.microsoft.schemas.vml.STTrueFalse;
- import org.apache.poi.ooxml.POIXMLDocumentPart;
- import org.apache.poi.ooxml.util.DocumentHelper;
- import org.apache.poi.openxml4j.opc.PackagePart;
- import org.apache.poi.schemas.vmldrawing.XmlDocument;
- import org.apache.poi.util.ReplacingInputStream;
- import org.apache.xmlbeans.XmlCursor;
- import org.apache.xmlbeans.XmlException;
- import org.apache.xmlbeans.XmlObject;
- import org.apache.xmlbeans.XmlOptions;
- import org.w3c.dom.Document;
- import org.xml.sax.SAXException;
-
- /**
- * Represents a SpreadsheetML VML drawing.
- *
- * <p>
- * In Excel 2007 VML drawings are used to describe properties of cell comments,
- * although the spec says that VML is deprecated:
- * </p>
- * <p>
- * The VML format is a legacy format originally introduced with Office 2000 and is included and fully defined
- * in this Standard for backwards compatibility reasons. The DrawingML format is a newer and richer format
- * created with the goal of eventually replacing any uses of VML in the Office Open XML formats. VML should be
- * considered a deprecated format included in Office Open XML for legacy reasons only and new applications that
- * need a file format for drawings are strongly encouraged to use preferentially DrawingML
- * </p>
- *
- * <p>
- * Warning - Excel is known to put invalid XML into these files!
- * For example, >br< without being closed or escaped crops up.
- * </p>
- *
- * See 6.4 VML - SpreadsheetML Drawing in Office Open XML Part 4 - Markup Language Reference.pdf
- */
- public final class XSSFVMLDrawing extends POIXMLDocumentPart {
- // this ID value seems to have significance to Excel >= 2010;
- // see https://issues.apache.org/bugzilla/show_bug.cgi?id=55409
- private static final String COMMENT_SHAPE_TYPE_ID = "_x0000_t202";
-
- /**
- * to actually process the namespace-less vmldrawing, we've introduced a proxy namespace.
- * this namespace is active in-memory, but will be removed on saving to the file
- */
- public static final QName QNAME_VMLDRAWING = new QName("urn:schemas-poi-apache-org:vmldrawing", "xml");
-
- /**
- * regexp to parse shape ids, in VML they have weird form of id="_x0000_s1026"
- */
- private static final Pattern ptrn_shapeId = Pattern.compile("_x0000_s(\\d+)");
-
- private XmlDocument root;
- private String _shapeTypeId;
- private int _shapeId = 1024;
-
- /**
- * Create a new SpreadsheetML drawing
- *
- * @see XSSFSheet#createDrawingPatriarch()
- */
- protected XSSFVMLDrawing() {
- super();
- newDrawing();
- }
-
- /**
- * Construct a SpreadsheetML drawing from a package part
- *
- * @param part the package part holding the drawing data,
- * the content type must be <code>application/vnd.openxmlformats-officedocument.drawing+xml</code>
- *
- * @since POI 3.14-Beta1
- */
- protected XSSFVMLDrawing(PackagePart part) throws IOException, XmlException {
- super(part);
- read(getPackagePart().getInputStream());
- }
-
- public XmlDocument getDocument() {
- return root;
- }
-
-
- protected void read(InputStream is) throws IOException, XmlException {
- Document doc;
- try {
- /*
- * This is a seriously sick fix for the fact that some .xlsx files contain raw bits
- * of HTML, without being escaped or properly turned into XML.
- * The result is that they contain things like >br<, which breaks the XML parsing.
- * This very sick InputStream wrapper attempts to spot these go past, and fix them.
- */
- doc = DocumentHelper.readDocument(new ReplacingInputStream(is, "<br>", "<br/>"));
- } catch (SAXException e) {
- throw new XmlException(e.getMessage(), e);
- }
-
- XmlOptions xopt = new XmlOptions(DEFAULT_XML_OPTIONS);
- xopt.setLoadSubstituteNamespaces(Collections.singletonMap("", QNAME_VMLDRAWING.getNamespaceURI()));
-
- root = XmlDocument.Factory.parse(doc, xopt);
- XmlCursor cur = root.getXml().newCursor();
-
- try {
- for (boolean found = cur.toFirstChild(); found; found = cur.toNextSibling()) {
- XmlObject xo = cur.getObject();
- if (xo instanceof CTShapetype) {
- _shapeTypeId = ((CTShapetype)xo).getId();
- } else if (xo instanceof CTShape) {
- CTShape shape = (CTShape)xo;
- String id = shape.getId();
- if(id != null) {
- Matcher m = ptrn_shapeId.matcher(id);
- if(m.find()) {
- _shapeId = Math.max(_shapeId, Integer.parseInt(m.group(1)));
- }
- }
- }
- }
- } finally {
- cur.dispose();
- }
- }
-
- protected List<XmlObject> getItems(){
- List<XmlObject> items = new ArrayList<>();
-
- XmlCursor cur = root.getXml().newCursor();
- try {
- for (boolean found = cur.toFirstChild(); found; found = cur.toNextSibling()) {
- items.add(cur.getObject());
- }
- } finally {
- cur.dispose();
- }
-
- return items;
- }
-
- protected void write(OutputStream out) throws IOException {
- XmlOptions xopt = new XmlOptions(DEFAULT_XML_OPTIONS);
- xopt.setSaveImplicitNamespaces(Collections.singletonMap("", QNAME_VMLDRAWING.getNamespaceURI()));
- root.save(out, xopt);
- }
-
- @Override
- protected void commit() throws IOException {
- PackagePart part = getPackagePart();
- try (OutputStream out = part.getOutputStream()) {
- write(out);
- }
- }
-
- /**
- * Initialize a new Speadsheet VML drawing
- */
- private void newDrawing(){
- root = XmlDocument.Factory.newInstance();
- XmlCursor xml = root.addNewXml().newCursor();
-
- ShapelayoutDocument layDoc = ShapelayoutDocument.Factory.newInstance();
- CTShapeLayout layout = layDoc.addNewShapelayout();
- layout.setExt(STExt.EDIT);
- CTIdMap idmap = layout.addNewIdmap();
- idmap.setExt(STExt.EDIT);
- idmap.setData("1");
-
- xml.toEndToken();
- XmlCursor layCur = layDoc.newCursor();
- layCur.copyXmlContents(xml);
- layCur.dispose();
-
- CTGroup grp = CTGroup.Factory.newInstance();
- CTShapetype shapetype = grp.addNewShapetype();
- _shapeTypeId = COMMENT_SHAPE_TYPE_ID;
- shapetype.setId(_shapeTypeId);
- shapetype.setCoordsize("21600,21600");
- shapetype.setSpt(202);
- shapetype.setPath2("m,l,21600r21600,l21600,xe");
- shapetype.addNewStroke().setJoinstyle(STStrokeJoinStyle.MITER);
- CTPath path = shapetype.addNewPath();
- path.setGradientshapeok(STTrueFalse.T);
- path.setConnecttype(STConnectType.RECT);
-
- xml.toEndToken();
- XmlCursor grpCur = grp.newCursor();
- grpCur.copyXmlContents(xml);
- grpCur.dispose();
- }
-
- protected CTShape newCommentShape(){
- CTGroup grp = CTGroup.Factory.newInstance();
-
- CTShape shape = grp.addNewShape();
- shape.setId("_x0000_s" + (++_shapeId));
- shape.setType("#" + _shapeTypeId);
- shape.setStyle("position:absolute; visibility:hidden");
- shape.setFillcolor("#ffffe1");
- shape.setInsetmode(STInsetMode.AUTO);
- shape.addNewFill().setColor("#ffffe1");
- CTShadow shadow = shape.addNewShadow();
- shadow.setOn(STTrueFalse.T);
- shadow.setColor("black");
- shadow.setObscured(STTrueFalse.T);
- shape.addNewPath().setConnecttype(STConnectType.NONE);
- shape.addNewTextbox().setStyle("mso-direction-alt:auto");
- CTClientData cldata = shape.addNewClientData();
- cldata.setObjectType(STObjectType.NOTE);
- cldata.addNewMoveWithCells();
- cldata.addNewSizeWithCells();
- cldata.addNewAnchor().setStringValue("1, 15, 0, 2, 3, 15, 3, 16");
- cldata.addNewAutoFill().setStringValue("False");
- cldata.addNewRow().setBigIntegerValue(BigInteger.valueOf(0));
- cldata.addNewColumn().setBigIntegerValue(BigInteger.valueOf(0));
-
- XmlCursor xml = root.getXml().newCursor();
- xml.toEndToken();
- XmlCursor grpCur = grp.newCursor();
- grpCur.copyXmlContents(xml);
- xml.toPrevSibling();
- shape = (CTShape)xml.getObject();
- grpCur.dispose();
- xml.dispose();
-
- return shape;
- }
-
- /**
- * Find a shape with ClientData of type "NOTE" and the specified row and column
- *
- * @return the comment shape or <code>null</code>
- */
- public CTShape findCommentShape(int row, int col){
- XmlCursor cur = root.getXml().newCursor();
- for (boolean found = cur.toFirstChild(); found; found = cur.toNextSibling()) {
- XmlObject itm = cur.getObject();
- if (matchCommentShape(itm, row, col)) {
- return (CTShape)itm;
- }
- }
- return null;
- }
-
- private boolean matchCommentShape(XmlObject itm, int row, int col) {
- if (!(itm instanceof CTShape)) {
- return false;
- }
-
- CTShape sh = (CTShape)itm;
- if (sh.sizeOfClientDataArray() == 0) {
- return false;
- }
-
- CTClientData cldata = sh.getClientDataArray(0);
- if(cldata.getObjectType() != STObjectType.NOTE) {
- return false;
- }
-
- int crow = cldata.getRowArray(0).intValue();
- int ccol = cldata.getColumnArray(0).intValue();
- return (crow == row && ccol == col);
- }
-
- protected boolean removeCommentShape(int row, int col){
- XmlCursor cur = root.getXml().newCursor();
- for (boolean found = cur.toFirstChild(); found; found = cur.toNextSibling()) {
- XmlObject itm = cur.getObject();
- if (matchCommentShape(itm, row, col)) {
- cur.removeXml();
- return true;
- }
- }
- return false;
- }
- }
|