From: Dustin Spicuzza Date: Mon, 19 Oct 2015 05:52:35 +0000 (+0000) Subject: Import poi-visio codebase X-Git-Tag: REL_3_14_BETA1~247 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=8a41cf7c40c05f39377e755f05dc8ce19def9f07;p=poi.git Import poi-visio codebase git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1709354 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/ooxml/java/org/apache/poi/xdgf/exceptions/XDGFException.java b/src/ooxml/java/org/apache/poi/xdgf/exceptions/XDGFException.java new file mode 100644 index 0000000000..3e173779c8 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/exceptions/XDGFException.java @@ -0,0 +1,50 @@ +/* ==================================================================== + 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.xdgf.exceptions; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; + +public class XDGFException { + + /** + * Creates an error message to be thrown + */ + public static POIXMLException error(String message, Object o) { + return new POIXMLException(o.toString() + ": " + message); + } + + public static POIXMLException error(String message, Object o, Throwable t) { + return new POIXMLException(o.toString() + ": " + message, t); + } + + // + // Use these to wrap error messages coming up so that we have at least + // some idea where the error was located + // + + public static POIXMLException wrap(POIXMLDocumentPart part, POIXMLException e) { + return new POIXMLException(part.getPackagePart().getPartName().toString() + ": " + e.getMessage(), + e.getCause() == null ? e : e.getCause()); + } + + public static POIXMLException wrap(String where, POIXMLException e) { + return new POIXMLException(where + ": " + e.getMessage(), + e.getCause() == null ? e : e.getCause()); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/geom/Dimension2dDouble.java b/src/ooxml/java/org/apache/poi/xdgf/geom/Dimension2dDouble.java new file mode 100644 index 0000000000..63a5031f75 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/geom/Dimension2dDouble.java @@ -0,0 +1,73 @@ +/* ==================================================================== + 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.xdgf.geom; + +import java.awt.geom.Dimension2D; + +public class Dimension2dDouble extends Dimension2D { + + double width; + double height; + + public Dimension2dDouble() { + width = 0d; + height = 0d; + } + + public Dimension2dDouble(double width, double height) { + this.width = width; + this.height = height; + } + + @Override + public double getWidth() { + return width; + } + + @Override + public double getHeight() { + return height; + } + + @Override + public void setSize(double width, double height) { + this.width = width; + this.height = height; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Dimension2dDouble) { + Dimension2dDouble other = (Dimension2dDouble)obj; + return width == other.width && height == other.height; + } + + return false; + } + + @Override + public int hashCode() { + double sum = width + height; + return (int)Math.ceil(sum * (sum + 1)/2 + width); + } + + @Override + public String toString() { + return "Dimension2dDouble[" + width + ", " + height + "]"; + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/geom/SplineCollector.java b/src/ooxml/java/org/apache/poi/xdgf/geom/SplineCollector.java new file mode 100644 index 0000000000..23f2636846 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/geom/SplineCollector.java @@ -0,0 +1,79 @@ +/* ==================================================================== + 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.xdgf.geom; + +import java.awt.geom.Point2D; +import java.util.ArrayList; + +import org.apache.poi.xdgf.usermodel.XDGFShape; +import org.apache.poi.xdgf.usermodel.section.geometry.SplineKnot; +import org.apache.poi.xdgf.usermodel.section.geometry.SplineStart; + +import com.graphbuilder.curve.ControlPath; +import com.graphbuilder.curve.ShapeMultiPath; +import com.graphbuilder.curve.ValueVector; +import com.graphbuilder.geom.PointFactory; + +public class SplineCollector { + + SplineStart _start; + ArrayList _knots = new ArrayList<>(); + + public SplineCollector(SplineStart start) { + _start = start; + } + + public void addKnot(SplineKnot knot) { + if (!knot.getDel()) + _knots.add(knot); + } + + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + // ok, we have the start, and all knots... do something with this + + Point2D last = path.getCurrentPoint(); + + // create a control path and knots + ControlPath controlPath = new ControlPath(); + ValueVector knots = new ValueVector(_knots.size() + 3); + + double firstKnot = _start.getB(); + double lastKnot = _start.getC(); + int degree = _start.getD(); + + // first/second knot + knots.add(firstKnot); + knots.add(_start.getA()); + + // first/second control point + controlPath.addPoint(PointFactory.create(last.getX(), last.getY())); + controlPath.addPoint(PointFactory.create(_start.getX(), _start.getY())); + + // middle knots/control points + for (SplineKnot knot: _knots) { + knots.add(knot.getA()); + controlPath.addPoint(PointFactory.create(knot.getX(), knot.getY())); + } + + // last knot + knots.add(lastKnot); + + ShapeMultiPath shape = SplineRenderer.createNurbsSpline(controlPath, knots, null, degree); + path.append(shape, true); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/geom/SplineRenderer.java b/src/ooxml/java/org/apache/poi/xdgf/geom/SplineRenderer.java new file mode 100644 index 0000000000..060555d429 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/geom/SplineRenderer.java @@ -0,0 +1,68 @@ +/* ==================================================================== + 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.xdgf.geom; + +import com.graphbuilder.curve.ControlPath; +import com.graphbuilder.curve.GroupIterator; +import com.graphbuilder.curve.NURBSpline; +import com.graphbuilder.curve.ShapeMultiPath; +import com.graphbuilder.curve.ValueVector; + +public class SplineRenderer { + + public static ShapeMultiPath createNurbsSpline(ControlPath controlPoints, + ValueVector knots, + ValueVector weights, + int degree) { + + double firstKnot = knots.get(0); + double lastKnot = knots.get(knots.size()-1); + + // scale knots to [0, 1] based on first/last knots + for (int i = 0; i < knots.size(); i++) { + knots.set((knots.get(i) - firstKnot)/lastKnot, i); + } + + // if we don't have enough knots, duplicate the last knot until we do + for (int i = knots.size(); i < controlPoints.numPoints() + degree + 1; i++) { + knots.add(1); + } + + GroupIterator gi = new GroupIterator("0:n-1", controlPoints.numPoints()); + + NURBSpline spline = new NURBSpline(controlPoints, gi); + + spline.setDegree(degree); + spline.setKnotVectorType(NURBSpline.NON_UNIFORM); + spline.setKnotVector(knots); + + if (weights == null) { + spline.setUseWeightVector(false); + } else { + spline.setWeightVector(weights); + } + + // now that this is done, add it to the path + ShapeMultiPath shape = new ShapeMultiPath(); + shape.setFlatness(0.01); + + spline.appendTo(shape); + return shape; + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFBaseContents.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFBaseContents.java new file mode 100644 index 0000000000..79edbd970c --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFBaseContents.java @@ -0,0 +1,159 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.poi.POIXMLException; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.util.Internal; +import org.apache.poi.xdgf.exceptions.XDGFException; +import org.apache.poi.xdgf.usermodel.shape.ShapeRenderer; +import org.apache.poi.xdgf.usermodel.shape.ShapeVisitor; +import org.apache.poi.xdgf.usermodel.shape.exceptions.StopVisiting; +import org.apache.poi.xdgf.xml.XDGFXMLDocumentPart; + +import com.microsoft.schemas.office.visio.x2012.main.ConnectType; +import com.microsoft.schemas.office.visio.x2012.main.PageContentsType; +import com.microsoft.schemas.office.visio.x2012.main.ShapeSheetType; + +/** + * Container of shapes for a page in a visio document + */ +public class XDGFBaseContents extends XDGFXMLDocumentPart { + + protected PageContentsType _pageContents; + + // shapes without parents + protected List _toplevelShapes = new ArrayList<>(); + protected Map _shapes = new HashMap<>(); + protected List _connections = new ArrayList<>(); + + public XDGFBaseContents(PackagePart part, PackageRelationship rel, XDGFDocument document) { + super(part, rel, document); + } + + @Internal + public PageContentsType getXmlObject() { + return _pageContents; + } + + + @Override + protected void onDocumentRead() { + + if (_pageContents.isSetShapes()) { + for (ShapeSheetType shapeSheet: _pageContents.getShapes().getShapeArray()) { + XDGFShape shape = new XDGFShape(shapeSheet, this, _document); + _toplevelShapes.add(shape); + addToShapeIndex(shape); + } + } + + if (_pageContents.isSetConnects()) { + for (ConnectType connect: _pageContents.getConnects().getConnectArray()) { + + XDGFShape from = _shapes.get(connect.getFromSheet()); + XDGFShape to = _shapes.get(connect.getToSheet()); + + if (from == null) + throw new POIXMLException(this.toString() + "; Connect; Invalid from id: " + connect.getFromSheet()); + + if (to == null) + throw new POIXMLException(this.toString() + "; Connect; Invalid to id: " + connect.getToSheet()); + + _connections.add(new XDGFConnection(connect, from, to)); + } + } + } + + protected void addToShapeIndex(XDGFShape shape) { + _shapes.put(shape.getID(), shape); + + List shapes = shape.getShapes(); + if (shapes == null) + return; + + for (XDGFShape subshape: shapes) + addToShapeIndex(subshape); + } + + // + // API + // + + /** + * + * @param graphics + */ + public void draw(Graphics2D graphics) { + visitShapes(new ShapeRenderer(graphics)); + } + + + public XDGFShape getShapeById(long id) { + return _shapes.get(id); + } + + public Map getShapesMap() { + return Collections.unmodifiableMap(_shapes); + } + + public Collection getShapes() { + return _shapes.values(); + } + + public List getTopLevelShapes() { + return Collections.unmodifiableList(_toplevelShapes); + } + + // get connections + public List getConnections() { + return Collections.unmodifiableList(_connections); + } + + @Override + public String toString() { + return getPackagePart().getPartName().toString(); + } + + + /** + * Provides iteration over the shapes using the visitor pattern, and provides + * an easy way to convert shape coordinates into global coordinates + */ + public void visitShapes(ShapeVisitor visitor) { + try { + for (XDGFShape shape: _toplevelShapes) { + shape.visitShapes(visitor, new AffineTransform(), 0); + } + } catch (StopVisiting e) { + // intentionally empty + } catch (POIXMLException e) { + throw XDGFException.wrap(this, e); + } + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFCell.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFCell.java new file mode 100644 index 0000000000..ef16a545d2 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFCell.java @@ -0,0 +1,150 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.util.Map; + +import org.apache.poi.POIXMLException; +import org.apache.poi.util.Internal; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; + +/** + * There are a lot of different cell types. Cell is really just an attribute + * of the thing that it's attached to. Will probably refactor this once I + * figure out a better way to use them + * + * The various attributes of a Cell are constrained, and are better defined + * in the XSD 1.1 visio schema + */ +public class XDGFCell { + + public static Boolean maybeGetBoolean(Map cells, String name) { + XDGFCell cell = cells.get(name); + if (cell == null) + return null; + + if (cell.getValue().equals("0")) + return false; + if (cell.getValue().equals("1")) + return true; + + throw new POIXMLException("Invalid boolean value for '" + cell.getName() + "'"); + } + + public static Double maybeGetDouble(Map cells, String name) { + XDGFCell cell = cells.get(name); + if (cell != null) + return parseDoubleValue(cell._cell); + return null; + } + + public static Integer maybeGetInteger(Map cells, String name) { + XDGFCell cell = cells.get(name); + if (cell != null) + return parseIntegerValue(cell._cell); + return null; + } + + public static String maybeGetString(Map cells, String name) { + XDGFCell cell = cells.get(name); + if (cell != null) { + String v = cell._cell.getV(); + if (v.equals("Themed")) + return null; + return v; + } + return null; + } + + public static Double parseDoubleValue(CellType cell) { + try { + return Double.parseDouble(cell.getV()); + } catch (NumberFormatException e) { + if (cell.getV().equals("Themed")) + return null; + throw new POIXMLException("Invalid float value for '" + cell.getN() + "': " + e); + } + } + + public static Integer parseIntegerValue(CellType cell) { + try { + return Integer.parseInt(cell.getV()); + } catch (NumberFormatException e) { + if (cell.getV().equals("Themed")) + return null; + throw new POIXMLException("Invalid integer value for '" + cell.getN() + "': " + e); + } + } + + // returns a length, converts it to inches? + public static Double parseVLength(CellType cell) { + try { + return Double.parseDouble(cell.getV()); + } catch (NumberFormatException e) { + if (cell.getV().equals("Themed")) + return null; + throw new POIXMLException("Invalid float value for '" + cell.getN() + "': " + e); + } + } + + + CellType _cell; + + public XDGFCell(CellType cell) { + _cell = cell; + } + + @Internal + CellType getXmlObject() { + return _cell; + } + + /** + * Represents the name of the ShapeSheet cell. + */ + public String getName() { + return _cell.getN(); + } + + /** + * Represents the value of the cell. + */ + public String getValue() { + return _cell.getV(); + } + + /** + * Represents the element’s formula. This attribute can contain one of the following strings: + * - ‘(some formula)’ if the formula exists locally + * - No Formula if the formula is locally deleted or blocked + * - Inh if the formula is inherited. + */ + public String getFormula() { + return _cell.getF(); + } + + /* + * Indicates that the formula evaluates to an error. The value of E is the + * current value (an error message string); the value of the V attribute is + * the last valid value. + */ + public String getError() { + return _cell.getE(); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFConnection.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFConnection.java new file mode 100644 index 0000000000..6e9f1085c3 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFConnection.java @@ -0,0 +1,134 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import com.microsoft.schemas.office.visio.x2012.main.ConnectType; + +public class XDGFConnection { + + // comments on frompart/topart taken from pkgVisio + + // https://msdn.microsoft.com/en-us/library/ms367611(v=office.12).aspx + + // The following constants declared by the Microsoft Office Visio type + // library show return values for the FromPart property. + // Constant Value + // visConnectFromError -1 + // visFromNone 0 + // visLeftEdge 1 + // visCenterEdge 2 + // visRightEdge 3 + // visBottomEdge 4 + // visMiddleEdge 5 + // visTopEdge 6 + // visBeginX 7 + // visBeginY 8 + // visBegin 9 + // visEndX 10 + // visEndY 11 + // visEnd 12 + // visFromAngle 13 + // visFromPin 14 + // visControlPoint 100 + zero-based row index (for example, visControlPoint = 100 if the control point is in row 0; visControlPoint = 101 if the control point is in row 1) + + public static final int visConnectFromError = -1; + public static final int visFromNone = 0; + public static final int visLeftEdge = 1; + public static final int visCenterEdge = 2; + public static final int visRightEdge = 3; + public static final int visBottomEdge = 4; + public static final int visMiddleEdge = 5; + public static final int visTopEdge = 6; + public static final int visBeginX = 7; + public static final int visBeginY = 8; + public static final int visBegin = 9; + public static final int visEndX = 10; + public static final int visEndY = 11; + public static final int visEnd = 12; + public static final int visFromAngle = 13; + public static final int visFromPin = 14; + + + // The ToPart property identifies the part of a shape to which another + // shape is glued, such as its begin point or endpoint, one of its edges, + // or a connection point. The following constants declared by the Visio type library in member VisToParts show possible return values for the ToPart property. + // Constant Value + // visConnectToError -1 + // visToNone 0 + // visGuideX 1 + // visGuideY 2 + // visWholeShape 3 + // visGuideIntersect 4 + // visToAngle 7 + // visConnectionPoint 100 + row index of connection point + + public static final int visConnectToError = -1; + public static final int visToNone = 0; + public static final int visGuideX = 1; + public static final int visGuideY = 2; + public static final int visWholeShape = 3; + public static final int visGuideIntersect = 4; + public static final int visToAngle = 7; + + private ConnectType _connect; + private XDGFShape _from; + private XDGFShape _to; + + + public XDGFConnection(ConnectType connect, XDGFShape from, XDGFShape to) { + _connect = connect; + _from = from; + _to = to; + } + + public XDGFShape getFromShape() { + return _from; + } + + public XDGFCell getFromCell() { + return _from.getCell(_connect.getFromCell()); + } + + public String getFromCellName() { + return _connect.getFromCell(); + } + + public XDGFShape getToShape() { + return _to; + } + + public String getToCellName() { + return _connect.getToCell(); + } + + // see constants above + public Integer getFromPart() { + if (_connect.isSetFromPart()) + return _connect.getFromPart(); + else + return null; + } + + // see constants above + public Integer getToPart() { + if (_connect.isSetToPart()) + return _connect.getToPart(); + else + return null; + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFDocument.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFDocument.java new file mode 100644 index 0000000000..b38c87960b --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFDocument.java @@ -0,0 +1,116 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.POIXMLException; +import org.apache.poi.util.Internal; + +import com.microsoft.schemas.office.visio.x2012.main.DocumentSettingsType; +import com.microsoft.schemas.office.visio.x2012.main.StyleSheetType; +import com.microsoft.schemas.office.visio.x2012.main.VisioDocumentType; + +/** + * Represents the root document: /visio/document.xml + */ +public class XDGFDocument { + + protected VisioDocumentType _document; + + Map _styleSheets = new HashMap<>(); + + // defaults + long _defaultFillStyle = 0; + long _defaultGuideStyle = 0; + long _defaultLineStyle = 0; + long _defaultTextStyle = 0; + + + public XDGFDocument(VisioDocumentType document) { + + _document = document; + + if (!_document.isSetDocumentSettings()) + throw new POIXMLException("Document settings not found"); + + DocumentSettingsType docSettings = _document.getDocumentSettings(); + + if (docSettings.isSetDefaultFillStyle()) + _defaultFillStyle = docSettings.getDefaultFillStyle(); + + if (docSettings.isSetDefaultGuideStyle()) + _defaultGuideStyle = docSettings.getDefaultGuideStyle(); + + if (docSettings.isSetDefaultLineStyle()) + _defaultLineStyle = docSettings.getDefaultLineStyle(); + + if (docSettings.isSetDefaultTextStyle()) + _defaultTextStyle = docSettings.getDefaultTextStyle(); + + if (_document.isSetStyleSheets()) { + + for (StyleSheetType styleSheet: _document.getStyleSheets().getStyleSheetArray()) { + _styleSheets.put(styleSheet.getID(), new XDGFStyleSheet(styleSheet, this)); + } + } + } + + + @Internal + public VisioDocumentType getXmlObject() { + return _document; + } + + + public XDGFStyleSheet getStyleById(long id) { + return _styleSheets.get(id); + } + + + public XDGFStyleSheet getDefaultFillStyle() { + XDGFStyleSheet style = getStyleById(_defaultFillStyle); + if (style == null) + throw new POIXMLException("No default fill style found!"); + return style; + } + + public XDGFStyleSheet getDefaultGuideStyle() { + XDGFStyleSheet style = getStyleById(_defaultGuideStyle); + if (style == null) + throw new POIXMLException("No default guide style found!"); + return style; + } + + public XDGFStyleSheet getDefaultLineStyle() { + XDGFStyleSheet style = getStyleById(_defaultLineStyle); + if (style == null) + throw new POIXMLException("No default line style found!"); + return style; + } + + public XDGFStyleSheet getDefaultTextStyle() { + XDGFStyleSheet style = getStyleById(_defaultTextStyle); + if (style == null) + throw new POIXMLException("No default text style found!"); + return style; + } + + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFFactory.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFFactory.java new file mode 100644 index 0000000000..80acd09dc3 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFFactory.java @@ -0,0 +1,75 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.lang.reflect.Constructor; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; +import org.apache.poi.POIXMLFactory; +import org.apache.poi.POIXMLRelation; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +public class XDGFFactory extends POIXMLFactory { + + private static final POILogger logger = POILogFactory.getLogger(XDGFFactory.class); + + private XDGFDocument _document; + + public XDGFFactory(XDGFDocument document){ + _document = document; + } + + @Override + public POIXMLDocumentPart createDocumentPart(POIXMLDocumentPart parent, + PackageRelationship rel, PackagePart part) { + POIXMLRelation descriptor = XDGFRelation.getInstance(rel.getRelationshipType()); + if(descriptor == null || descriptor.getRelationClass() == null){ + logger.log(POILogger.DEBUG, "using default POIXMLDocumentPart for " + rel.getRelationshipType()); + return new POIXMLDocumentPart(part, rel); + } + + try { + Class cls = descriptor.getRelationClass(); + try { + Constructor constructor = cls.getDeclaredConstructor(POIXMLDocumentPart.class, PackagePart.class, PackageRelationship.class, XDGFDocument.class); + return constructor.newInstance(parent, part, rel, _document); + } catch (NoSuchMethodException e) { + Constructor constructor = cls.getDeclaredConstructor(PackagePart.class, PackageRelationship.class, XDGFDocument.class); + return constructor.newInstance(part, rel, _document); + } + } catch (Exception e){ + throw new POIXMLException(e); + } + } + + @Override + public POIXMLDocumentPart newDocumentPart(POIXMLRelation descriptor) { + try { + Class cls = descriptor.getRelationClass(); + Constructor constructor = cls.getDeclaredConstructor(); + return constructor.newInstance(); + } catch (Exception e){ + throw new POIXMLException(e); + } + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFMaster.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFMaster.java new file mode 100644 index 0000000000..17736f87d9 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFMaster.java @@ -0,0 +1,68 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import org.apache.poi.util.Internal; + +import com.microsoft.schemas.office.visio.x2012.main.MasterType; + +/** + * Provides the API to work with an underlying master + */ +public class XDGFMaster { + + private MasterType _master; + private XDGFMasterContents _content; + XDGFSheet _pageSheet = null; + + public XDGFMaster(MasterType master, XDGFMasterContents content, XDGFDocument document) { + _master = master; + _content = content; + content.setMaster(this); + + if (master.isSetPageSheet()) + _pageSheet = new XDGFPageSheet(master.getPageSheet(), document); + } + + @Internal + MasterType getXmlObject() { + return _master; + } + + @Override + public String toString() { + return ""; + } + + public long getID() { + return _master.getID(); + } + + public String getName() { + return _master.getName(); + } + + public XDGFMasterContents getContent() { + return _content; + } + + public XDGFSheet getPageSheet() { + return _pageSheet; + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFMasterContents.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFMasterContents.java new file mode 100644 index 0000000000..ddeaa8cbc1 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFMasterContents.java @@ -0,0 +1,64 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.io.IOException; + +import org.apache.poi.POIXMLException; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.xdgf.exceptions.XDGFException; +import org.apache.xmlbeans.XmlException; + +import com.microsoft.schemas.office.visio.x2012.main.MasterContentsDocument; + +public class XDGFMasterContents extends XDGFBaseContents { + + private XDGFMaster _master; + + public XDGFMasterContents(PackagePart part, PackageRelationship rel, XDGFDocument document) { + super(part, rel, document); + } + + @Override + protected void onDocumentRead() { + + try { + + try { + _pageContents = MasterContentsDocument.Factory.parse(getPackagePart().getInputStream()).getMasterContents(); + } catch (XmlException | IOException e) { + throw new POIXMLException(e); + } + + super.onDocumentRead(); + + } catch (POIXMLException e) { + throw XDGFException.wrap(this, e); + } + } + + public XDGFMaster getMaster() { + return _master; + } + + protected void setMaster(XDGFMaster master) { + _master = master; + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFMasters.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFMasters.java new file mode 100644 index 0000000000..ddfca1308f --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFMasters.java @@ -0,0 +1,99 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.util.Internal; +import org.apache.poi.xdgf.exceptions.XDGFException; +import org.apache.poi.xdgf.xml.XDGFXMLDocumentPart; +import org.apache.xmlbeans.XmlException; + +import com.microsoft.schemas.office.visio.x2012.main.MasterType; +import com.microsoft.schemas.office.visio.x2012.main.MastersDocument; +import com.microsoft.schemas.office.visio.x2012.main.MastersType; + +public class XDGFMasters extends XDGFXMLDocumentPart { + + MastersType _mastersObject; + + // key: id of master + Map _masters = new HashMap<>(); + + public XDGFMasters(PackagePart part, PackageRelationship rel, XDGFDocument document) { + super(part, rel, document); + } + + @Internal + MastersType getXmlObject() { + return _mastersObject; + } + + @Override + protected void onDocumentRead() { + try { + try { + _mastersObject = MastersDocument.Factory.parse(getPackagePart().getInputStream()).getMasters(); + } catch (XmlException | IOException e) { + throw new POIXMLException(e); + } + + Map masterSettings = new HashMap<>(); + for (MasterType master: _mastersObject.getMasterArray()) { + masterSettings.put(master.getRel().getId(), master); + } + + // create the masters + for (POIXMLDocumentPart part: getRelations()) { + + String relId = part.getPackageRelationship().getId(); + MasterType settings = masterSettings.get(relId); + + if (settings == null) + throw new POIXMLException("Master relationship for " + relId + " not found"); + + if (!(part instanceof XDGFMasterContents)) + throw new POIXMLException("Unexpected masters relationship for " + relId + ": " + part); + + XDGFMasterContents contents = (XDGFMasterContents)part; + contents.onDocumentRead(); + + XDGFMaster master = new XDGFMaster(settings, contents, _document); + _masters.put(master.getID(), master); + } + } catch (POIXMLException e) { + throw XDGFException.wrap(this, e); + } + } + + public Collection getMastersList() { + return Collections.unmodifiableCollection(_masters.values()); + } + + public XDGFMaster getMasterById(long masterId) { + return _masters.get(masterId); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPage.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPage.java new file mode 100644 index 0000000000..762c9631a2 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPage.java @@ -0,0 +1,113 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.apache.poi.POIXMLException; +import org.apache.poi.util.Internal; +import org.apache.poi.xdgf.geom.Dimension2dDouble; + +import com.microsoft.schemas.office.visio.x2012.main.PageType; + +/** + * Provides the API to work with an underlying page + */ +public class XDGFPage { + + PageType _page; + XDGFPageContents _content; + XDGFPages _pages; + XDGFSheet _pageSheet = null; + + public XDGFPage(PageType page, XDGFPageContents content, XDGFDocument document, XDGFPages pages) { + _page = page; + _content = content; + _pages = pages; + content.setPage(this); + + if (page.isSetPageSheet()) + _pageSheet = new XDGFPageSheet(page.getPageSheet(), document); + } + + @Internal + PageType getXmlObject() { + return _page; + } + + public long getID() { + return _page.getID(); + } + + public String getName() { + return _page.getName(); + } + + public XDGFPageContents getContent() { + return _content; + } + + public XDGFSheet getPageSheet() { + return _pageSheet; + } + + public long getPageNumber() { + return _pages.getPageList().indexOf(this) + 1; + } + + // height/width of page + public Dimension2dDouble getPageSize() { + XDGFCell w = _pageSheet.getCell("PageWidth"); + XDGFCell h = _pageSheet.getCell("PageHeight"); + + if (w == null || h == null) + throw new POIXMLException("Cannot determine page size"); + + return new Dimension2dDouble(Double.parseDouble(w.getValue()), + Double.parseDouble(h.getValue())); + } + + // origin of coordinate system + public Point2D.Double getPageOffset() { + XDGFCell xoffcell = _pageSheet.getCell("XRulerOrigin"); + XDGFCell yoffcell = _pageSheet.getCell("YRulerOrigin"); + + double xoffset = 0; + double yoffset = 0; + + if (xoffcell != null) + xoffset = Double.parseDouble(xoffcell.getValue()); + + if (xoffcell != null) + yoffset = Double.parseDouble(yoffcell.getValue()); + + return new Point2D.Double(xoffset, yoffset); + } + + // bounding box of page + public Rectangle2D getBoundingBox() { + Dimension2dDouble sz = getPageSize(); + Point2D.Double offset = getPageOffset(); + + return new Rectangle2D.Double(-offset.getX(), + -offset.getY(), + sz.getWidth(), + sz.getHeight()); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPageContents.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPageContents.java new file mode 100644 index 0000000000..392ec48629 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPageContents.java @@ -0,0 +1,83 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.xdgf.exceptions.XDGFException; +import org.apache.xmlbeans.XmlException; + +import com.microsoft.schemas.office.visio.x2012.main.PageContentsDocument; + +public class XDGFPageContents extends XDGFBaseContents { + + Map _masters = new HashMap<>(); + XDGFPage _page; + + public XDGFPageContents(PackagePart part, PackageRelationship rel, XDGFDocument document) { + super(part, rel, document); + } + + @Override + protected void onDocumentRead() { + try { + try { + _pageContents = PageContentsDocument.Factory.parse(getPackagePart().getInputStream()).getPageContents(); + } catch (XmlException | IOException e) { + throw new POIXMLException(e); + } + + for (POIXMLDocumentPart part: getRelations()) { + if (!(part instanceof XDGFMasterContents)) + continue; + //throw new POIXMLException("Unexpected page relation: " + part); + + XDGFMaster master = ((XDGFMasterContents)part).getMaster(); + _masters.put(master.getID(), master); + } + + super.onDocumentRead(); + + for (XDGFShape shape: _shapes.values()) { + if (shape.isTopmost()) + shape.setupMaster(this, null); + } + + } catch (POIXMLException e) { + throw XDGFException.wrap(this, e); + } + } + + public XDGFPage getPage() { + return _page; + } + + protected void setPage(XDGFPage page) { + _page = page; + } + + public XDGFMaster getMasterById(long id) { + return _masters.get(id); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPageSheet.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPageSheet.java new file mode 100644 index 0000000000..2bf8324fca --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPageSheet.java @@ -0,0 +1,36 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import com.microsoft.schemas.office.visio.x2012.main.PageSheetType; + +public class XDGFPageSheet extends XDGFSheet { + + PageSheetType _pageSheet; + + public XDGFPageSheet(PageSheetType sheet, XDGFDocument document) { + super(sheet, document); + _pageSheet = sheet; + } + + @Override + PageSheetType getXmlObject() { + return _pageSheet; + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPages.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPages.java new file mode 100644 index 0000000000..c8447f57a7 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFPages.java @@ -0,0 +1,95 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.util.Internal; +import org.apache.poi.xdgf.exceptions.XDGFException; +import org.apache.poi.xdgf.xml.XDGFXMLDocumentPart; +import org.apache.xmlbeans.XmlException; + +import com.microsoft.schemas.office.visio.x2012.main.PageType; +import com.microsoft.schemas.office.visio.x2012.main.PagesDocument; +import com.microsoft.schemas.office.visio.x2012.main.PagesType; + + +/** + * Contains a list of Page objects (not page content!) + */ +public class XDGFPages extends XDGFXMLDocumentPart { + + PagesType _pagesObject; + + // ordered by page number + List _pages = new ArrayList<>(); + + public XDGFPages(PackagePart part, PackageRelationship rel, XDGFDocument document) { + super(part, rel, document); + } + + @Internal + PagesType getXmlObject() { + return _pagesObject; + } + + @Override + protected void onDocumentRead() { + try { + try { + _pagesObject = PagesDocument.Factory.parse(getPackagePart().getInputStream()).getPages(); + } catch (XmlException | IOException e) { + throw new POIXMLException(e); + } + + // this iteration is ordered by page number + for (PageType pageSettings: _pagesObject.getPageArray()) { + + String relId = pageSettings.getRel().getId(); + + POIXMLDocumentPart pageContentsPart = getRelationById(relId); + if (pageContentsPart == null) + throw new POIXMLException("PageSettings relationship for " + relId + " not found"); + + if (!(pageContentsPart instanceof XDGFPageContents)) + throw new POIXMLException("Unexpected pages relationship for " + relId + ": " + pageContentsPart); + + XDGFPageContents contents = (XDGFPageContents)pageContentsPart; + XDGFPage page = new XDGFPage(pageSettings, contents, _document, this); + + contents.onDocumentRead(); + + _pages.add(page); + } + + } catch (POIXMLException e) { + throw XDGFException.wrap(this, e); + } + } + + // ordered by page number + public List getPageList() { + return Collections.unmodifiableList(_pages); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFRelation.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFRelation.java new file mode 100644 index 0000000000..1090377fb9 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFRelation.java @@ -0,0 +1,99 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.POIXMLRelation; +import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; +import org.apache.poi.xdgf.xml.XDGFXMLDocumentPart; + +public class XDGFRelation extends POIXMLRelation { + + /** + * A map to lookup POIXMLRelation by its relation type + */ + protected static final Map _table = new HashMap(); + + public static final XDGFRelation DOCUMENT = new XDGFRelation( + "application/vnd.ms-visio.drawing.main+xml", + "http://schemas.microsoft.com/visio/2010/relationships/document", + "/visio/document.xml", + null + ); + + public static final XDGFRelation MASTERS = new XDGFRelation( + "application/vnd.ms-visio.masters+xml", + "http://schemas.microsoft.com/visio/2010/relationships/masters", + "/visio/masters/masters.xml", + XDGFMasters.class + ); + + public static final XDGFRelation MASTER = new XDGFRelation( + "application/vnd.ms-visio.master+xml", + "http://schemas.microsoft.com/visio/2010/relationships/master", + "/visio/masters/master#.xml", + XDGFMasterContents.class + ); + + public static final XDGFRelation IMAGES = new XDGFRelation( + null, + PackageRelationshipTypes.IMAGE_PART, + null, + null // XSSFPictureData.class + ); + + public static final XDGFRelation PAGES = new XDGFRelation( + "application/vnd.ms-visio.pages+xml", + "http://schemas.microsoft.com/visio/2010/relationships/pages", + "/visio/pages/pages.xml", + XDGFPages.class + ); + + public static final XDGFRelation PAGE = new XDGFRelation( + "application/vnd.ms-visio.page+xml", + "http://schemas.microsoft.com/visio/2010/relationships/page", + "/visio/pages/page#.xml", + XDGFPageContents.class + ); + + public static final XDGFRelation WINDOW = new XDGFRelation( + "application/vnd.ms-visio.windows+xml", + "http://schemas.microsoft.com/visio/2010/relationships/windows", + "/visio/windows.xml", + null + ); + + private XDGFRelation(String type, String rel, String defaultName, Class cls) { + super(type, rel, defaultName, cls); + + if (cls != null && !_table.containsKey(rel)) _table.put(rel, this); + } + + /** + * Get POIXMLRelation by relation type + * + * @param rel relation type, for example, + * http://schemas.openxmlformats.org/officeDocument/2006/relationships/image + * @return registered POIXMLRelation or null if not found + */ + public static XDGFRelation getInstance(String rel) { + return _table.get(rel); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java new file mode 100644 index 0000000000..493f049428 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java @@ -0,0 +1,943 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map.Entry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.util.Internal; +import org.apache.poi.xdgf.exceptions.XDGFException; +import org.apache.poi.xdgf.usermodel.section.CombinedIterable; +import org.apache.poi.xdgf.usermodel.section.GeometrySection; +import org.apache.poi.xdgf.usermodel.section.XDGFSection; +import org.apache.poi.xdgf.usermodel.shape.ShapeVisitor; +import org.apache.poi.xdgf.usermodel.shape.exceptions.StopVisitingThisBranch; + +import com.microsoft.schemas.office.visio.x2012.main.ShapeSheetType; +import com.microsoft.schemas.office.visio.x2012.main.TextType; + +/** + * A shape is a collection of Geometry Visualization, Format, Text, Images, + * and Shape Data in a Drawing Page. + */ +public class XDGFShape extends XDGFSheet { + + XDGFBaseContents _parentPage; + XDGFShape _parent; // only non-null if a subshape + + XDGFMaster _master = null; + XDGFShape _masterShape = null; + + XDGFText _text = null; + + // subshapes if they exist + List _shapes = null; + + // properties specific to shapes + + // center of rotation relative to origin of parent + Double _pinX = null; + Double _pinY = null; + + Double _width = null; + Double _height = null; + + // center of rotation relative to self + Double _locPinX = null; + Double _locPinY = null; + + // start x coordinate, relative to parent + // -> one dimensional shapes only + Double _beginX = null; + Double _beginY = null; + + // end x coordinate, relative to parent + // -> one dimensional shapes only + Double _endX = null; + Double _endY = null; + + Double _angle = null; + Double _rotationXAngle = null; + Double _rotationYAngle = null; + Double _rotationZAngle = null; + + // end x coordinate, relative to parent + Boolean _flipX = null; + Boolean _flipY = null; + + // center of text relative to this shape + Double _txtPinX = null; + Double _txtPinY = null; + + // center of text relative to text block + Double _txtLocPinX = null; + Double _txtLocPinY = null; + + Double _txtAngle = null; + + Double _txtWidth = null; + Double _txtHeight = null; + + public XDGFShape(ShapeSheetType shapeSheet, XDGFBaseContents parentPage, XDGFDocument document) { + this(null, shapeSheet, parentPage, document); + } + + public XDGFShape(XDGFShape parent, ShapeSheetType shapeSheet, XDGFBaseContents parentPage, XDGFDocument document) { + + super(shapeSheet, document); + + _parent = parent; + _parentPage = parentPage; + + TextType text = shapeSheet.getText(); + if (text != null) + _text = new XDGFText(text, this); + + if (shapeSheet.isSetShapes()) { + _shapes = new ArrayList(); + for (ShapeSheetType shape: shapeSheet.getShapes().getShapeArray()) + _shapes.add(new XDGFShape(this, shape, parentPage, document)); + } + + readProperties(); + } + + @Override + public String toString() { + if (_parentPage instanceof XDGFMasterContents) + return _parentPage + ": "; + else + return ""; + } + + protected void readProperties() { + + _pinX = XDGFCell.maybeGetDouble(_cells, "PinX"); + _pinY = XDGFCell.maybeGetDouble(_cells, "PinY"); + _width = XDGFCell.maybeGetDouble(_cells, "Width"); + _height = XDGFCell.maybeGetDouble(_cells, "Height"); + _locPinX = XDGFCell.maybeGetDouble(_cells, "LocPinX"); + _locPinY = XDGFCell.maybeGetDouble(_cells, "LocPinY"); + _beginX = XDGFCell.maybeGetDouble(_cells, "BeginX"); + _beginY = XDGFCell.maybeGetDouble(_cells, "BeginY"); + _endX = XDGFCell.maybeGetDouble(_cells, "EndX"); + _endY = XDGFCell.maybeGetDouble(_cells, "EndY"); + + _angle = XDGFCell.maybeGetDouble(_cells, "Angle"); + _rotationXAngle = XDGFCell.maybeGetDouble(_cells, "RotationXAngle"); + _rotationYAngle = XDGFCell.maybeGetDouble(_cells, "RotationYAngle"); + _rotationZAngle = XDGFCell.maybeGetDouble(_cells, "RotationZAngle"); + + _flipX = XDGFCell.maybeGetBoolean(_cells, "FlipX"); + _flipY = XDGFCell.maybeGetBoolean(_cells, "FlipY"); + + _txtPinX = XDGFCell.maybeGetDouble(_cells, "TxtPinX"); + _txtPinY = XDGFCell.maybeGetDouble(_cells, "TxtPinY"); + _txtLocPinX = XDGFCell.maybeGetDouble(_cells, "TxtLocPinX"); + _txtLocPinY = XDGFCell.maybeGetDouble(_cells, "TxtLocPinY"); + _txtWidth = XDGFCell.maybeGetDouble(_cells, "TxtWidth"); + _txtHeight = XDGFCell.maybeGetDouble(_cells, "TxtHeight"); + + _txtAngle = XDGFCell.maybeGetDouble(_cells, "TxtAngle"); + } + + /** + * Setup top level shapes + * + * Shapes that have a 'Master' attribute refer to a specific master in + * the page, whereas shapes with a 'MasterShape' attribute refer to a + * subshape of a Master. + * + * + */ + protected void setupMaster(XDGFPageContents pageContents, XDGFMasterContents master) { + + ShapeSheetType obj = getXmlObject(); + + if (obj.isSetMaster()) { + _master = pageContents.getMasterById(obj.getMaster()); + if (_master == null) + throw XDGFException.error("refers to non-existant master " + obj.getMaster(), + this); + + /* + * If a master has one top-level shape, a shape that inherits from + * that master inherits the descendant elements of that master + * shape. If a master has more than one master shape, a shape that + * inherits from that master inherits those master shapes as + * subshapes. + */ + + Collection masterShapes = _master.getContent().getTopLevelShapes(); + + switch (masterShapes.size()) { + case 0: + throw XDGFException.error("Could not retrieve master shape from " + _master, this); + case 1: + _masterShape = masterShapes.iterator().next(); + break; + default: + break; + } + + } else if (obj.isSetMasterShape()) { + _masterShape = master.getShapeById(obj.getMasterShape()); + if (_masterShape == null) + throw XDGFException.error("refers to non-existant master shape " + obj.getMasterShape(), + this); + + } + + setupSectionMasters(); + + if (_shapes != null) { + for (XDGFShape shape: _shapes) { + shape.setupMaster(pageContents, + _master == null ? master : _master.getContent()); + } + } + } + + protected void setupSectionMasters() { + + if (_masterShape == null) + return; + + try { + for (Entry section: _sections.entrySet()) { + XDGFSection master = _masterShape.getSection(section.getKey()); + if (master != null) + section.getValue().setupMaster(master); + } + + for (Entry section: _geometry.entrySet()) { + GeometrySection master = _masterShape.getGeometryByIdx(section.getKey()); + if (master != null) + section.getValue().setupMaster(master); + } + } catch (POIXMLException e) { + throw XDGFException.wrap(this.toString(), e); + } + } + + @Internal + public ShapeSheetType getXmlObject() { + return (ShapeSheetType)_sheet; + } + + public long getID() { + return getXmlObject().getID(); + } + + public String getType() { + return getXmlObject().getType(); + } + + public String getTextAsString() { + XDGFText text = getText(); + if (text == null) + return ""; + + return text.getTextContent(); + } + + public boolean hasText() { + return _text != null || (_masterShape != null && _masterShape._text != null); + } + + @Override + public XDGFCell getCell(String cellName) { + XDGFCell _cell = super.getCell(cellName); + + // if not found, ask the master + if (_cell == null && _masterShape != null) { + _cell = _masterShape.getCell(cellName); + } + + return _cell; + } + + public GeometrySection getGeometryByIdx(long idx) { + return _geometry.get(idx); + } + + // only available if this is a shape group + // -> May be null + public List getShapes() { + return _shapes; + } + + // unique to this shape on the page? + public String getName() { + String name = getXmlObject().getName(); + if (name == null) + return ""; + return name; + } + + // unique to this shape on the page? + public String getShapeType() { + String type = getXmlObject().getType(); + if (type == null) + return ""; + return type; + } + + // name of the symbol that this was derived from + public String getSymbolName() { + + if (_master == null) + return ""; + + String name = _master.getName(); + if (name == null) + return ""; + + return name; + } + + public XDGFShape getMasterShape() { + return _masterShape; + } + + // returns the parent shape of this shape, if its in a subshape + public XDGFShape getParentShape() { + return _parent; + } + + public XDGFShape getTopmostParentShape() { + XDGFShape top = null; + if (_parent != null) { + top = _parent.getTopmostParentShape(); + if (top == null) + top = _parent; + } + + return top; + } + + public boolean hasMaster() { + return _master != null; + } + + public boolean hasMasterShape() { + return _masterShape != null; + } + + public boolean hasParent() { + return _parent != null; + } + + public boolean hasShapes() { + return _shapes != null; + } + + public boolean isTopmost() { + return _parent == null; + } + + public boolean isShape1D() { + return getBeginX() != null; + } + + public boolean isDeleted() { + return getXmlObject().isSetDel() ? getXmlObject().getDel() : false; + } + + public XDGFText getText() { + if (_text == null && _masterShape != null) + return _masterShape.getText(); + + return _text; + } + + public Double getPinX() { + if (_pinX == null && _masterShape != null) + return _masterShape.getPinX(); + + if (_pinX == null) + throw XDGFException.error("PinX not set!", this); + + return _pinX; + } + + public Double getPinY() { + if (_pinY == null && _masterShape != null) + return _masterShape.getPinY(); + + if (_pinY == null) + throw XDGFException.error("PinY not specified!", this); + + return _pinY; + } + + public Double getWidth() { + if (_width == null && _masterShape != null) + return _masterShape.getWidth(); + + if (_width == null) + throw XDGFException.error("Width not specified!", this); + + return _width; + } + + public Double getHeight() { + if (_height == null && _masterShape != null) + return _masterShape.getHeight(); + + if (_height == null) + throw XDGFException.error("Height not specified!", this); + + return _height; + } + + public Double getLocPinX() { + if (_locPinX == null && _masterShape != null) + return _masterShape.getLocPinX(); + + if (_locPinX == null) + throw XDGFException.error("LocPinX not specified!", this); + + return _locPinX; + } + + public Double getLocPinY() { + if (_locPinY == null && _masterShape != null) + return _masterShape.getLocPinY(); + + if (_locPinY == null) + throw XDGFException.error("LocPinY not specified!", this); + + return _locPinY; + } + + public Double getBeginX() { + if (_beginX == null && _masterShape != null) + return _masterShape.getBeginX(); + + return _beginX; + } + + public Double getBeginY() { + if (_beginY == null && _masterShape != null) + return _masterShape.getBeginY(); + + return _beginY; + } + + public Double getEndX() { + if (_endX == null && _masterShape != null) + return _masterShape.getEndX(); + + return _endX; + } + + public Double getEndY() { + if (_endY == null && _masterShape != null) + return _masterShape.getEndY(); + + return _endY; + } + + public Double getAngle() { + if (_angle == null && _masterShape != null) + return _masterShape.getAngle(); + + return _angle; + } + + public Boolean getFlipX() { + if (_flipX == null && _masterShape != null) + return _masterShape.getFlipX(); + + return _flipX; + } + + public Boolean getFlipY() { + if (_flipY == null && _masterShape != null) + return _masterShape.getFlipY(); + + return _flipY; + } + + public Double getTxtPinX() { + if (_txtPinX == null && + _masterShape != null && _masterShape._txtPinX != null) + return _masterShape._txtPinX; + + if (_txtPinX == null) + return getWidth()*0.5; + + return _txtPinX; + } + + + public Double getTxtPinY() { + if (_txtLocPinY == null && + _masterShape != null && _masterShape._txtLocPinY != null) + return _masterShape._txtLocPinY; + + if (_txtPinY == null) + return getHeight()*0.5; + + return _txtPinY; + } + + + public Double getTxtLocPinX() { + if (_txtLocPinX == null && + _masterShape != null && _masterShape._txtLocPinX != null) + return _masterShape._txtLocPinX; + + if (_txtLocPinX == null) + return getTxtWidth()*0.5; + + return _txtLocPinX; + } + + + public Double getTxtLocPinY() { + if (_txtLocPinY == null && + _masterShape != null && _masterShape._txtLocPinY != null) + return _masterShape._txtLocPinY; + + if (_txtLocPinY == null) + return getTxtHeight()*0.5; + + return _txtLocPinY; + } + + public Double getTxtAngle() { + if (_txtAngle == null && _masterShape != null) + return _masterShape.getTxtAngle(); + + return _txtAngle; + } + + public Double getTxtWidth() { + if (_txtWidth == null && + _masterShape != null && _masterShape._txtWidth != null) + return _masterShape._txtWidth; + + if (_txtWidth == null) + return getWidth(); + + return _txtWidth; + } + + + public Double getTxtHeight() { + if (_txtHeight == null && + _masterShape != null && _masterShape._txtHeight != null) + return _masterShape._txtHeight; + + if (_txtHeight == null) + return getHeight(); + + return _txtHeight; + } + + @Override + public Integer getLineCap() { + + Integer lineCap = super.getLineCap(); + if (lineCap != null) + return lineCap; + + // get from master + if (_masterShape != null) { + return _masterShape.getLineCap(); + } + + // get default + XDGFStyleSheet style = _document.getDefaultLineStyle(); + if (style != null) + return style.getLineCap(); + + return null; + } + + @Override + public Color getLineColor() { + + Color lineColor = super.getLineColor(); + if (lineColor != null) + return lineColor; + + // get from master + if (_masterShape != null) { + return _masterShape.getLineColor(); + } + + // get default + XDGFStyleSheet style = _document.getDefaultLineStyle(); + if (style != null) + return style.getLineColor(); + + return null; + } + + @Override + public Integer getLinePattern() { + + Integer linePattern = super.getLinePattern(); + if (linePattern != null) + return linePattern; + + // get from master + if (_masterShape != null) { + return _masterShape.getLinePattern(); + } + + // get default + XDGFStyleSheet style = _document.getDefaultLineStyle(); + if (style != null) + return style.getLinePattern(); + + return null; + } + + @Override + public Double getLineWeight() { + + Double lineWeight = super.getLineWeight(); + if (lineWeight != null) + return lineWeight; + + // get from master + if (_masterShape != null) { + return _masterShape.getLineWeight(); + } + + // get default + XDGFStyleSheet style = _document.getDefaultLineStyle(); + if (style != null) + return style.getLineWeight(); + + return null; + } + + public Color getFontColor() { + + Color fontColor = super.getFontColor(); + if (fontColor != null) + return fontColor; + + // get from master + if (_masterShape != null) { + return _masterShape.getFontColor(); + } + + // get default + XDGFStyleSheet style = _document.getDefaultTextStyle(); + if (style != null) + return style.getFontColor(); + + return null; + } + + public Double getFontSize() { + + Double fontSize = super.getFontSize(); + if (fontSize != null) + return fontSize; + + // get from master + if (_masterShape != null) { + return _masterShape.getFontSize(); + } + + // get default + XDGFStyleSheet style = _document.getDefaultTextStyle(); + if (style != null) + return style.getFontSize(); + + return null; + } + + public Stroke getStroke() { + + float lineWeight = getLineWeight().floatValue(); + int cap; + int join = BasicStroke.JOIN_MITER; + float miterlimit = 10.0f; + + switch (getLineCap()) { + case 0: + cap = BasicStroke.CAP_ROUND; + break; + case 1: + cap = BasicStroke.CAP_SQUARE; + break; + case 2: + cap = BasicStroke.CAP_BUTT; // TODO: what does extended mean? + break; + default: + throw new POIXMLException("Invalid line cap specified"); + } + + float[] dash = null; + + // these line patterns are just approximations + switch (getLinePattern()) { + case 0: // transparent + break; + case 1: // solid + break; + case 2: + dash = new float[]{5,3}; + break; + case 3: + dash = new float[]{1,4}; + break; + case 4: + dash = new float[]{6,3,1,3}; + break; + case 5: + dash = new float[]{6,3,1,3,1,3}; + break; + case 6: + dash = new float[]{1,3,6,3,6,3}; + break; + case 7: + dash = new float[]{15,3,6,3}; + break; + case 8: + dash = new float[]{6,3,6,3}; + break; + case 9: + dash = new float[]{3,2}; + break; + case 10: + dash = new float[]{1,2}; + break; + case 11: + dash = new float[]{3,2,1,2}; + break; + case 12: + dash = new float[]{3,2,1,2,1}; + break; + case 13: + dash = new float[]{1,2,3,2,3,2}; + break; + case 14: + dash = new float[]{3,2,7,2}; + break; + case 15: + dash = new float[]{7,2,3,2,3,2}; + break; + case 16: + dash = new float[]{12,6}; + break; + case 17: + dash = new float[]{1,6}; + break; + case 18: + dash = new float[]{1,6,12,6}; + break; + case 19: + dash = new float[]{1,6,1,6,12,6}; + break; + case 20: + dash = new float[]{1,6,12,6,12,6}; + break; + case 21: + dash = new float[]{30,6,12,6}; + break; + case 22: + dash = new float[]{30,6,12,6,12,6}; + break; + case 23: + dash = new float[]{1}; + break; + case 254: + throw new POIXMLException("Unsupported line pattern value"); + default: + throw new POIXMLException("Invalid line pattern value"); + } + + // dashes are in units of line width + if (dash != null) { + for (int i = 0; i < dash.length; i++) { + dash[i] *= lineWeight; + } + } + + return new BasicStroke(lineWeight, cap, join, miterlimit, dash, 0); + } + + + // + // Geometry + // + + public Iterable getGeometrySections() { + return new CombinedIterable<>(_geometry, + _masterShape != null ? _masterShape._geometry : null); + } + + + // returns a rectangle in local coordinates + public Rectangle2D.Double getBounds() { + return new Rectangle2D.Double(0, 0, getWidth(), + getHeight()); + } + + // returns bounds as a path in local coordinates + // -> useful if you need to transform to global coordinates + // -> Don't use for 1d objects, fails for infinite line objects + public Path2D.Double getBoundsAsPath() { + + Double w = getWidth(); + Double h = getHeight(); + + Path2D.Double bounds = new Path2D.Double(); + bounds.moveTo(0, 0); + bounds.lineTo(w, 0); + bounds.lineTo(w, h); + bounds.lineTo(0, h); + bounds.lineTo(0, 0); + + return bounds; + } + + // returns the path in local coordinates + public Path2D.Double getPath() { + for (GeometrySection geoSection: getGeometrySections()) { + if (geoSection.getNoShow() == true) + continue; + + return geoSection.getPath(this); + } + + return null; + } + + /* + * Returns true if the shape has a drawable geometry associated with it + */ + public boolean hasGeometry() { + for (GeometrySection geoSection: getGeometrySections()) { + if (geoSection.getNoShow() == false) + return true; + } + return false; + } + + /** + * Returns a transform that can translate shape-local coordinates + * to the coordinates of its parent shape + */ + protected AffineTransform getParentTransform() { + // TODO: There's probably a better way to do this + AffineTransform tr = new AffineTransform(); + + Double locX = getLocPinX(); + Double locY = getLocPinY(); + Boolean flipX = getFlipX(); + Boolean flipY = getFlipY(); + Double angle = getAngle(); + + tr.translate(-locX, -locY); + + tr.translate(getPinX(), getPinY()); + + // rotate about the origin + if (angle != null && Math.abs(angle) > 0.001) { + tr.rotate(angle, locX, locY); + } + + // flip if necessary + + if (flipX != null && flipX) { + tr.scale(-1, 1); + tr.translate(-getWidth(), 0); + } + + if (flipY != null && flipY) { + tr.scale(1, -1); + tr.translate(0, -getHeight()); + } + + return tr; + } + + + + /** + * The visitor will first visit this shape, then it's children + * + * This is useful because exceptions will be marked with the shapes as it + * propagates up the shape hierarchy. + */ + public void visitShapes(ShapeVisitor visitor, AffineTransform tr, int level) { + + tr = (AffineTransform)tr.clone(); + tr.concatenate(getParentTransform()); + + try { + if (visitor.accept(this)) + visitor.visit(this, tr, level); + + if (_shapes != null) { + for (XDGFShape shape: _shapes) { + shape.visitShapes(visitor, tr, level + 1); + } + } + } catch (StopVisitingThisBranch e) { + // intentionally empty + } catch (POIXMLException e) { + throw XDGFException.wrap(this.toString(), e); + } + } + + /** + * The visitor will first visit this shape, then it's children. No transform + * is calculated for this visit + * + * This is useful because exceptions will be marked with the shapes as it + * propagates up the shape hierarchy. + */ + public void visitShapes(ShapeVisitor visitor, int level) { + + try { + if (visitor.accept(this)) + visitor.visit(this, null, level); + + if (_shapes != null) { + for (XDGFShape shape: _shapes) { + shape.visitShapes(visitor, level + 1); + } + } + } catch (StopVisitingThisBranch e) { + // intentionally empty + } catch (POIXMLException e) { + throw XDGFException.wrap(this.toString(), e); + } + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFSheet.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFSheet.java new file mode 100644 index 0000000000..f460319d2b --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFSheet.java @@ -0,0 +1,204 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.awt.Color; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.exceptions.XDGFException; +import org.apache.poi.xdgf.usermodel.section.CharacterSection; +import org.apache.poi.xdgf.usermodel.section.GeometrySection; +import org.apache.poi.xdgf.usermodel.section.XDGFSection; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.SectionType; +import com.microsoft.schemas.office.visio.x2012.main.SheetType; + +/** + * A sheet is a collection of properties that specify information for a shape, + * master, drawing page, style, or web drawing. + */ +public abstract class XDGFSheet { + + protected XDGFDocument _document; + protected SheetType _sheet; + + // cells + protected Map _cells = new HashMap<>(); + + // sections + protected Map _sections = new HashMap<>(); + + // special: geometry sections (key: index, value: section) + protected SortedMap _geometry = new TreeMap<>(); + + // special: character section + protected CharacterSection _character = null; + + public XDGFSheet(SheetType sheet, XDGFDocument document) { + try { + _sheet = sheet; + _document = document; + + for (CellType cell: sheet.getCellArray()) { + if (_cells.containsKey(cell.getN())) + throw new POIXMLException("Unexpected duplicate cell " + cell.getN()); // this shouldn't happen + + _cells.put(cell.getN(), new XDGFCell(cell)); + } + + // only geometry sections can have duplicate names + // sections can be found in the master too, if there are no attributes here! + + // no idea if I have a master in this space. go figure. + + for (SectionType section: sheet.getSectionArray()) { + String name = section.getN(); + if (name.equals("Geometry")) { + _geometry.put(section.getIX(), new GeometrySection(section, this)); + } else if (name.equals("Character")) { + _character = new CharacterSection(section, this); + } else { + _sections.put(name, XDGFSection.load(section, this)); + } + } + } catch (POIXMLException e) { + throw XDGFException.wrap(this.toString(), e); + } + } + + abstract SheetType getXmlObject(); + + public XDGFDocument getDocument() { + return _document; + } + + // A cell is really just a setting + public XDGFCell getCell(String cellName) { + return _cells.get(cellName); + } + + public XDGFSection getSection(String sectionName) { + return _sections.get(sectionName); + } + + public XDGFStyleSheet getLineStyle() { + if (!_sheet.isSetLineStyle()) + return null; + + return _document.getStyleById(_sheet.getLineStyle()); + } + + public XDGFStyleSheet getFillStyle() { + if (!_sheet.isSetFillStyle()) + return null; + + return _document.getStyleById(_sheet.getFillStyle()); + } + + public XDGFStyleSheet getTextStyle() { + if (!_sheet.isSetTextStyle()) + return null; + + return _document.getStyleById(_sheet.getTextStyle()); + } + + public Color getFontColor() { + Color fontColor = null; + + if (_character != null) { + fontColor = _character.getFontColor(); + if (fontColor != null) + return fontColor; + } + + XDGFStyleSheet style = getTextStyle(); + if (style != null) + return style.getFontColor(); + + return null; + } + + public Double getFontSize() { + Double fontSize = null; + + if (_character != null) { + fontSize = _character.getFontSize(); + if (fontSize != null) + return fontSize; + } + + XDGFStyleSheet style = getTextStyle(); + if (style != null) + return style.getFontSize(); + + return null; + } + + public Integer getLineCap() { + Integer lineCap = XDGFCell.maybeGetInteger(_cells, "LineCap"); + if (lineCap != null) + return lineCap; + + XDGFStyleSheet style = getLineStyle(); + if (style != null) + return style.getLineCap(); + + return null; + } + + public Color getLineColor() { + String lineColor = XDGFCell.maybeGetString(_cells, "LineColor"); + if (lineColor != null) + return Color.decode(lineColor); + + XDGFStyleSheet style = getLineStyle(); + if (style != null) + return style.getLineColor(); + + return null; + } + + public Integer getLinePattern() { + Integer linePattern = XDGFCell.maybeGetInteger(_cells, "LinePattern"); + if (linePattern != null) + return linePattern; + + XDGFStyleSheet style = getLineStyle(); + if (style != null) + return style.getLinePattern(); + + return null; + } + + public Double getLineWeight() { + Double lineWeight = XDGFCell.maybeGetDouble(_cells, "LineWeight"); + if (lineWeight != null) + return lineWeight; + + XDGFStyleSheet style = getLineStyle(); + if (style != null) + return style.getLineWeight(); + + return null; + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFStyleSheet.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFStyleSheet.java new file mode 100644 index 0000000000..39df4a90ac --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFStyleSheet.java @@ -0,0 +1,36 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import org.apache.poi.util.Internal; + +import com.microsoft.schemas.office.visio.x2012.main.StyleSheetType; + +public class XDGFStyleSheet extends XDGFSheet { + + public XDGFStyleSheet(StyleSheetType styleSheet, XDGFDocument document) { + super(styleSheet, document); + } + + @Internal + public StyleSheetType getXmlObject() { + return (StyleSheetType)_sheet; + } + + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java new file mode 100644 index 0000000000..a5c89fca8a --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java @@ -0,0 +1,150 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.apache.poi.util.Internal; + +import com.microsoft.schemas.office.visio.x2012.main.TextType; +import com.microsoft.schemas.office.visio.x2012.main.impl.TextTypeImpl; + +public class XDGFText { + + TextType _text; + XDGFShape _parent; + + public XDGFText(TextType text, XDGFShape parent) { + _text = text; + _parent = parent; + } + + @Internal + TextType getXmlObject() { + return _text; + } + + public String getTextContent() { + // casting here is wrong, but there's no other way of getting the value, + // as it doesn't seem to be exposed by complex types (even though this + // is a mixed type) + return ((TextTypeImpl)_text).getStringValue(); + } + + // these are in the shape coordinate system + // -> See https://msdn.microsoft.com/en-us/library/hh644132(v=office.12).aspx + public Rectangle2D.Double getTextBounds() { + + double txtPinX = _parent.getTxtPinX(); + double txtPinY = _parent.getTxtPinY(); + + double txtLocPinX = _parent.getTxtLocPinX(); + double txtLocPinY = _parent.getTxtLocPinY(); + + double txtWidth = _parent.getTxtWidth(); + double txtHeight = _parent.getTxtHeight(); + + double x = txtPinX - txtLocPinX; + double y = txtPinY - txtLocPinY; + + return new Rectangle2D.Double(x, y, txtWidth, txtHeight); + } + + // returns bounds as a path in local coordinates + // -> useful if you need to transform to global coordinates + public Path2D.Double getBoundsAsPath() { + + Rectangle2D.Double rect = getTextBounds(); + Double w = rect.getWidth(); + Double h = rect.getHeight(); + + Path2D.Double bounds = new Path2D.Double(); + bounds.moveTo(0, 0); + bounds.lineTo(w, 0); + bounds.lineTo(w, h); + bounds.lineTo(0, h); + bounds.lineTo(0, 0); + + return bounds; + } + + // center of text in local coordinates + public Point2D.Double getTextCenter() { + return new Point2D.Double(_parent.getTxtLocPinX(), _parent.getTxtLocPinY()); + } + + // assumes graphics is set properly to draw in the right style + public void draw(Graphics2D graphics) { + + String textContent = getTextContent(); + if (textContent.length() == 0) + return; + + Rectangle2D.Double bounds = getTextBounds(); + + String[] lines = textContent.trim().split("\n"); + FontRenderContext frc = graphics.getFontRenderContext(); + Font font = graphics.getFont(); + + AffineTransform oldTr = graphics.getTransform(); + + // visio is in flipped coordinates, so translate the text to be in the right place + Boolean flipX = _parent.getFlipX(); + Boolean flipY = _parent.getFlipY(); + + if (flipY == null || !_parent.getFlipY()) { + graphics.translate(bounds.x, bounds.y); + graphics.scale(1, -1); + graphics.translate(0, -bounds.height + graphics.getFontMetrics().getMaxCharBounds(graphics).getHeight()); + } + + if (flipX != null && _parent.getFlipX()) { + graphics.scale(-1, 1); + graphics.translate(-bounds.width, 0); + } + + Double txtAngle = _parent.getTxtAngle(); + if (txtAngle != null && Math.abs(txtAngle) > 0.01) + graphics.rotate(txtAngle); + + float nextY = 0; + for (String line: lines) { + + if (line.length() == 0) + continue; + + TextLayout layout = new TextLayout(line, font, frc); + + if (layout.isLeftToRight()) + layout.draw(graphics, 0, nextY); + else + layout.draw(graphics, (float)(bounds.width - layout.getAdvance()), nextY); + + nextY += layout.getAscent() + layout.getDescent() + layout.getLeading(); + } + + graphics.setTransform(oldTr); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/XmlVisioDocument.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XmlVisioDocument.java new file mode 100644 index 0000000000..83adc18a67 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/XmlVisioDocument.java @@ -0,0 +1,113 @@ +/* ==================================================================== + 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.xdgf.usermodel; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; +import java.util.List; + +import org.apache.poi.POIXMLDocument; +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.POIXMLException; +import org.apache.poi.openxml4j.exceptions.OpenXML4JException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.util.PackageHelper; +import org.apache.xmlbeans.XmlException; + +import com.microsoft.schemas.office.visio.x2012.main.VisioDocumentDocument1; +import com.microsoft.schemas.office.visio.x2012.main.VisioDocumentType; + +public class XmlVisioDocument extends POIXMLDocument { + + public static String CORE_DOCUMENT = "http://schemas.microsoft.com/visio/2010/relationships/document"; + + XDGFPages _pages; + XDGFMasters _masters; + XDGFDocument _document; + + + public XmlVisioDocument(OPCPackage pkg) throws IOException { + super(pkg, CORE_DOCUMENT); + + VisioDocumentType document; + + try { + document = VisioDocumentDocument1.Factory.parse(getPackagePart().getInputStream()).getVisioDocument(); + } catch (XmlException | IOException e) { + throw new POIXMLException(e); + } + + _document = new XDGFDocument(document); + + //build a tree of POIXMLDocumentParts, this document being the root + load(new XDGFFactory(_document)); + } + + public XmlVisioDocument(InputStream is) throws IOException { + this(PackageHelper.open(is)); + } + + + @Override + protected void onDocumentRead() throws IOException { + + // by the time this gets called, all other document parts should + // have been loaded, so it's safe to build the document structure + + // note that in other onDocumentRead(), relations/etc may not have + // loaded yet, so it's not quite safe + + for (POIXMLDocumentPart part: getRelations()) { + + // organize the document pieces + if (part instanceof XDGFPages) + _pages = (XDGFPages)part; + + else if (part instanceof XDGFMasters) + _masters = (XDGFMasters)part; + } + + if (_masters != null) + _masters.onDocumentRead(); + + _pages.onDocumentRead(); + } + + + + @Override + public List getAllEmbedds() throws OpenXML4JException { + throw new UnsupportedOperationException("Not implemented"); + } + + // + // Useful public API goes here + // + + public Collection getPages() { + return _pages.getPageList(); + } + + + public XDGFStyleSheet getStyleById(long id) { + return _document.getStyleById(id); + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CharacterSection.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CharacterSection.java new file mode 100644 index 0000000000..b5407cb521 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CharacterSection.java @@ -0,0 +1,71 @@ +/* ==================================================================== + 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.xdgf.usermodel.section; + +import java.awt.Color; +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFSheet; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; +import com.microsoft.schemas.office.visio.x2012.main.SectionType; + +public class CharacterSection extends XDGFSection { + + Double _fontSize = null; + Color _fontColor = null; + + Map _characterCells = new HashMap<>(); + + public CharacterSection(SectionType section, XDGFSheet containingSheet) { + super(section, containingSheet); + + // there aren't cells for this, just a single row + RowType row = section.getRowArray(0); + + for (CellType cell: row.getCellArray()) { + _characterCells.put(cell.getN(), new XDGFCell(cell)); + } + + if (row != null) { + _fontSize = XDGFCell.maybeGetDouble(_characterCells, "Size"); + + String tmpColor = XDGFCell.maybeGetString(_characterCells, "Color"); + if (tmpColor != null) + _fontColor = Color.decode(tmpColor); + } + } + + public Double getFontSize() { + return _fontSize; + } + + public Color getFontColor() { + return _fontColor; + } + + @Override + public void setupMaster(XDGFSection section) { + + } + +} + diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CombinedIterable.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CombinedIterable.java new file mode 100644 index 0000000000..b1fb4c652e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CombinedIterable.java @@ -0,0 +1,136 @@ +/* ==================================================================== + 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.xdgf.usermodel.section; + +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.SortedMap; +import java.util.Map.Entry; + +// iterates over the base and master +public class CombinedIterable implements Iterable { + + final SortedMap _baseItems; + final SortedMap _masterItems; + + public CombinedIterable(SortedMap baseItems, + SortedMap masterItems) { + _baseItems = baseItems; + _masterItems = masterItems; + } + + @Override + public Iterator iterator() { + + final Iterator> vmasterI; + + if (_masterItems != null) + vmasterI = _masterItems.entrySet().iterator(); + else + vmasterI = Collections.emptyIterator(); + + return new Iterator() { + + Long lastI = Long.MIN_VALUE; + + Entry currentBase = null; + Entry currentMaster = null; + + // grab the iterator for both + Iterator> baseI = _baseItems.entrySet().iterator(); + Iterator> masterI = vmasterI; + + @Override + public boolean hasNext() { + return currentBase != null || currentMaster != null || baseI.hasNext() || masterI.hasNext(); + } + + @Override + public T next() { + + // TODO: This seems far more complex than it needs to be + + long baseIdx = Long.MAX_VALUE; + long masterIdx = Long.MAX_VALUE; + + if (currentBase == null) { + while (baseI.hasNext()) { + currentBase = baseI.next(); + if (currentBase.getKey() > lastI) { + baseIdx = currentBase.getKey(); + break; + } + } + } else { + baseIdx = currentBase.getKey(); + } + + if (currentMaster == null) { + while (masterI.hasNext()) { + currentMaster = masterI.next(); + if (currentMaster.getKey() > lastI) { + masterIdx = currentMaster.getKey(); + break; + } + } + } else { + masterIdx = currentMaster.getKey(); + } + + T val; + + if (currentBase != null) { + + if (baseIdx <= masterIdx) { + lastI = baseIdx; + val = currentBase.getValue(); + + // discard master if same as base + if (masterIdx == baseIdx) { + currentMaster = null; + } + + currentBase = null; + + } else { + lastI = masterIdx; + val = currentMaster.getValue(); + currentMaster = null; + } + + } else if (currentMaster != null) { + lastI = currentMaster.getKey(); + val = currentMaster.getValue(); + + currentMaster = null; + } else { + throw new NoSuchElementException(); + } + + return val; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GenericSection.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GenericSection.java new file mode 100644 index 0000000000..0a35fc0559 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GenericSection.java @@ -0,0 +1,33 @@ +/* ==================================================================== + 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.xdgf.usermodel.section; + +import org.apache.poi.xdgf.usermodel.XDGFSheet; + +import com.microsoft.schemas.office.visio.x2012.main.SectionType; + +public class GenericSection extends XDGFSection { + + public GenericSection(SectionType section, XDGFSheet containingSheet) { + super(section, containingSheet); + } + + @Override + public void setupMaster(XDGFSection section) { + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GeometrySection.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GeometrySection.java new file mode 100644 index 0000000000..079b06e35b --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GeometrySection.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.xdgf.usermodel.section; + +import java.awt.geom.Path2D; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.geom.SplineCollector; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; +import org.apache.poi.xdgf.usermodel.XDGFSheet; +import org.apache.poi.xdgf.usermodel.section.geometry.Ellipse; +import org.apache.poi.xdgf.usermodel.section.geometry.GeometryRowFactory; +import org.apache.poi.xdgf.usermodel.section.geometry.InfiniteLine; +import org.apache.poi.xdgf.usermodel.section.geometry.GeometryRow; +import org.apache.poi.xdgf.usermodel.section.geometry.SplineKnot; +import org.apache.poi.xdgf.usermodel.section.geometry.SplineStart; + +import com.microsoft.schemas.office.visio.x2012.main.RowType; +import com.microsoft.schemas.office.visio.x2012.main.SectionType; + +public class GeometrySection extends XDGFSection { + + GeometrySection _master = null; + + // rows + SortedMap _rows = new TreeMap<>(); + + public GeometrySection(SectionType section, XDGFSheet containingSheet) { + super(section, containingSheet); + + for (RowType row: section.getRowArray()) { + if (_rows.containsKey(row.getIX())) + throw new POIXMLException("Index element '" + row.getIX() + "' already exists"); + + _rows.put(row.getIX(), GeometryRowFactory.load(row)); + } + } + + @Override + public void setupMaster(XDGFSection master) { + + _master = (GeometrySection)master; + + for (Entry entry : _rows.entrySet()) { + GeometryRow masterRow = _master._rows.get(entry.getKey()); + if (masterRow != null) { + try { + entry.getValue().setupMaster(masterRow); + } catch (ClassCastException e) { + // this can happen when a dynamic connector overrides its master's geometry + // .. probably can happen elsewhere too, I imagine. + //throw XDGFException.error("Mismatched geometry section '" + entry.getKey() + "' in master", this, e); + } + } + } + } + + // returns True if this row shouldn't be displayed + public Boolean getNoShow() { + Boolean noShow = XDGFCell.maybeGetBoolean(_cells, "NoShow"); + if (noShow == null) { + if (_master != null) + return _master.getNoShow(); + + return false; + } + + return noShow; + } + + public Iterable getCombinedRows() { + return new CombinedIterable<>(_rows, + _master == null ? null : _master._rows); + } + + public Path2D.Double getPath(XDGFShape parent) { + + Iterator rows = getCombinedRows().iterator(); + + // special cases + GeometryRow first = rows.next(); + + if (first instanceof Ellipse) { + return ((Ellipse)first).getPath(); + } else if (first instanceof InfiniteLine) { + return ((InfiniteLine)first).getPath(); + } else if (first instanceof SplineStart) { + throw new POIXMLException("SplineStart must be preceded by another type"); + } else { + + // everything else is a path + Path2D.Double path = new Path2D.Double(); + + // dealing with splines makes this more complex + SplineCollector renderer = null; + GeometryRow row; + + while (true) { + + if (first != null) { + row = first; + first = null; + } else { + if (!rows.hasNext()) + break; + row = rows.next(); + } + + if (row instanceof SplineStart) { + if (renderer != null) + throw new POIXMLException("SplineStart found multiple times!"); + renderer = new SplineCollector((SplineStart) row); + } else if (row instanceof SplineKnot) { + if (renderer == null) + throw new POIXMLException("SplineKnot found without SplineStart!"); + renderer.addKnot((SplineKnot) row); + } else { + if (renderer != null) { + renderer.addToPath(path, parent); + renderer = null; + } + + row.addToPath(path, parent); + } + } + + // just in case we end iteration + if (renderer != null) + renderer.addToPath(path, parent); + + return path; + } + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/XDGFSection.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/XDGFSection.java new file mode 100644 index 0000000000..835dade97b --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/XDGFSection.java @@ -0,0 +1,98 @@ +/* ==================================================================== + 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.xdgf.usermodel.section; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.POIXMLException; +import org.apache.poi.util.Internal; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFSheet; +import org.apache.poi.xdgf.util.ObjectFactory; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.SectionType; + +public abstract class XDGFSection { + + static final ObjectFactory _sectionTypes; + + static { + _sectionTypes = new ObjectFactory<>(); + try { + _sectionTypes.put("LineGradient", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("FillGradient", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Character", CharacterSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Paragraph", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Tabs", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Scratch", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Connection", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("ConnectionABCD", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Field", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Control", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Geometry", GeometrySection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Actions", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Layer", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("User", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Property", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Hyperlink", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Reviewer", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("Annotation", GenericSection.class, SectionType.class, XDGFSheet.class); + _sectionTypes.put("ActionTag", GenericSection.class, SectionType.class, XDGFSheet.class); + } catch (NoSuchMethodException | SecurityException e) { + throw new POIXMLException("Internal error"); + } + + } + + public static XDGFSection load(SectionType section, XDGFSheet containingSheet) { + return _sectionTypes.load(section.getN(), section, containingSheet); + } + + + protected SectionType _section; + protected XDGFSheet _containingSheet; + + protected Map _cells = new HashMap<>(); + + + public XDGFSection(SectionType section, XDGFSheet containingSheet) { + _section = section; + _containingSheet = containingSheet; + + // only store cells in the base, not rows -- because rows are handled + // specially for geometry sections + for (CellType cell: section.getCellArray()) { + _cells.put(cell.getN(), new XDGFCell(cell)); + } + } + + @Internal + public SectionType getXmlObject() { + return _section; + } + + @Override + public String toString() { + return "
"; + } + + public abstract void setupMaster(XDGFSection section); + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java new file mode 100644 index 0000000000..243b3887b8 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java @@ -0,0 +1,131 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class ArcTo implements GeometryRow { + + ArcTo _master = null; + + // The x-coordinate of the ending vertex of an arc. + Double x = null; + + // The y-coordinate of the ending vertex of an arc. + Double y = null; + + // The distance from the arc's midpoint to the midpoint of its chord. + Double a = null; + + Boolean deleted = null; + + // TODO: support formulas + + public ArcTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in ArcTo row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (ArcTo) row; + } + + @Override + public void addToPath(Path2D.Double path, XDGFShape parent) { + + if (getDel()) return; + + Point2D last = path.getCurrentPoint(); + + // intentionally shadowing variables here + double x = getX(); + double y = getY(); + double a = getA(); + + if (a == 0) { + path.lineTo(x, y); + return; + } + + double x0 = last.getX(); + double y0 = last.getY(); + + double chordLength = Math.hypot(y - y0, x - x0); + double radius = (4 * a * a + chordLength * chordLength) / (8 * Math.abs(a)); + + // center point + double cx = x0 + (x - x0) / 2.0; + double cy = y0 + (y - y0) / 2.0; + + double rotate = Math.atan2(y - cy, x - cx); + + Arc2D arc = new Arc2D.Double(x0, y0 - radius, + chordLength, 2*radius, + 180, x0 < x ? 180 : -180, + Arc2D.OPEN); + + path.append(AffineTransform.getRotateInstance(rotate, x0, y0).createTransformedShape(arc), true); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/Ellipse.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/Ellipse.java new file mode 100644 index 0000000000..0e4124e9bb --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/Ellipse.java @@ -0,0 +1,156 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.xdgf.usermodel.section.geometry; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Path2D; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class Ellipse implements GeometryRow { + + Ellipse _master = null; + + // x coordinate of center point + Double x = null; + // y coordinate of center point + Double y = null; + + // x coordinate of first point on ellipse + Double a = null; + // y coordinate of first point on ellipse + Double b = null; + + // x coordinate of second point on ellipse + Double c = null; + // y coordinate of second point on ellipse + Double d = null; + + Boolean deleted = null; + + // TODO: support formulas + + public Ellipse(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("B")) { + b = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("C")) { + c = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("D")) { + d = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in Ellipse row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + public Double getB() { + return b == null ? _master.b : b; + } + + public Double getC() { + return c == null ? _master.c : c; + } + + public Double getD() { + return d == null ? _master.d : d; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (Ellipse) row; + } + + public Path2D.Double getPath() { + + if (getDel()) return null; + + // intentionally shadowing variables here + double cx = getX(); // center + double cy = getY(); + double a = getA(); // left + double b = getB(); + double c = getC(); // top + double d = getD(); + + // compute radius + double rx = Math.hypot(a - cx, b - cy); + double ry = Math.hypot(c - cx, d - cy); + + // compute angle of ellipse + double angle = (2.0*Math.PI + (cy > b ? 1.0 : -1.0) * Math.acos((cx - a) / rx)) % (2.0*Math.PI); + + // create ellipse + Ellipse2D.Double ellipse = new Ellipse2D.Double(cx - rx, + cy - ry, + rx*2, ry*2); + + // create a path, rotate it about its center + Path2D.Double path = new Path2D.Double(ellipse); + + AffineTransform tr = new AffineTransform(); + tr.rotate(angle, cx, cy); + path.transform(tr); + + return path; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + throw new POIXMLException("Ellipse elements cannot be part of a path"); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java new file mode 100644 index 0000000000..f7b3bcadc1 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java @@ -0,0 +1,231 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import java.awt.geom.AffineTransform; +import java.awt.geom.Arc2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class EllipticalArcTo implements GeometryRow { + + EllipticalArcTo _master = null; + + // The x-coordinate of the ending vertex on an arc. + Double x = null; + + // The y-coordinate of the ending vertex on an arc. + Double y = null; + + // The x-coordinate of the arc's control point; a point on the arc. The + // control point is best located about halfway between the beginning and + // ending vertices of the arc. Otherwise, the arc may grow to an extreme + // size in order to pass through the control point, with unpredictable + // results. + Double a = null; + + // The y-coordinate of an arc's control point. + Double b = null; + + // The angle of an arc's major axis relative to the x-axis of its parent shape. + Double c = null; + + // The ratio of an arc's major axis to its minor axis. Despite the usual + // meaning of these words, the "major" axis does not have to be greater than + // the "minor" axis, so this ratio does not have to be greater than 1. + // Setting this cell to a value less than or equal to 0 or greater than 1000 + // can lead to unpredictable results. + Double d = null; + + Boolean deleted = null; + + // TODO: support formulas + + public EllipticalArcTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("B")) { + b = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("C")) { + c = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("D")) { + d = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in EllipticalArcTo row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + public Double getB() { + return b == null ? _master.b : b; + } + + public Double getC() { + return c == null ? _master.c : c; + } + + public Double getD() { + return d == null ? _master.d : d; + } + + + @Override + public void setupMaster(GeometryRow row) { + _master = (EllipticalArcTo) row; + } + + public static int draw = 0; + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + + if (getDel()) return; + + // intentionally shadowing variables here + double x = getX(); + double y = getY(); + double a = getA(); + double b = getB(); + double c = getC(); + double d = getD(); + + createEllipticalArc(x, y, a, b, c, d, path); + } + + public static void createEllipticalArc(double x, double y, double a, double b, + double c, double d, + java.awt.geom.Path2D.Double path) { + + // Formula for center of ellipse by Junichi Yoda & nashwaan: + // -> From http://visguy.com/vgforum/index.php?topic=2464.0 + // + // x1,y1 = start; x2,y2 = end; x3,y3 = control point + // + // x0 = ((x1-x2)*(x1+x2)*(y2-y3)-(x2-x3)*(x2+x3)*(y1-y2)+D^2*(y1-y2)*(y2-y3)*(y1-y3))/(2*((x1-x2)*(y2-y3)-(x2-x3)*(y1-y2))) + // y0 = ((x1-x2)*(x2-x3)*(x1-x3)/D^2+(x2-x3)*(y1-y2)*(y1+y2)-(x1-x2)*(y2-y3)*(y2+y3))/(2*((x2-x3)*(y1-y2)-(x1-x2)*(y2-y3))) + // radii along axis: a = sqrt{ (x1-x0)^2 + (y1-y0)^2 * D^2 } + // + + Point2D last = path.getCurrentPoint(); + double x0 = last.getX(); + double y0 = last.getY(); + + // translate all of the points to the same angle as the ellipse + AffineTransform at = AffineTransform.getRotateInstance(-c); + double[] pts = new double[]{x0, y0, x, y, a, b}; + at.transform(pts, 0, pts, 0, 3); + + + x0 = pts[0]; y0 = pts[1]; + x = pts[2]; y = pts[3]; + a = pts[4]; b = pts[5]; + + // nasty math time + + double d2 = d*d; + double cx = ((x0-x)*(x0+x)*(y-b)-(x-a)*(x+a)*(y0-y)+d2*(y0-y)*(y-b)*(y0-b))/(2.0*((x0-x)*(y-b)-(x-a)*(y0-y))); + double cy = ((x0-x)*(x-a)*(x0-a)/d2+(x-a)*(y0-y)*(y0+y)-(x0-x)*(y-b)*(y+b))/(2.0*((x-a)*(y0-y)-(x0-x)*(y-b))); + + // calculate radii of ellipse + double rx = Math.sqrt(Math.pow(x0-cx, 2) + Math.pow(y0-cy,2) * d2); + double ry = rx / d; + + // Arc2D requires us to draw an arc from one point to another, so we + // need to calculate the angle of the start point and end point along the ellipse + // - Derived from parametric form of ellipse: x = h + a*cos(t); y = k + b*sin(t) + + double ctrlAngle = Math.toDegrees(Math.atan2((b-cy)/ry, (a-cx)/rx)); + double startAngle = Math.toDegrees(Math.atan2((y0-cy)/ry, (x0-cx)/rx)); + double endAngle = Math.toDegrees(Math.atan2((y-cy)/ry, (x-cx)/rx)); + + double sweep = computeSweep(startAngle, endAngle, ctrlAngle); + + // Now we have enough information to go on. Create the arc. + Arc2D arc = new Arc2D.Double(cx-rx, cy-ry, + rx*2, ry*2, -startAngle, sweep, Arc2D.OPEN); + + // rotate the arc back to the original coordinate system + at.setToRotation(c); + path.append(at.createTransformedShape(arc), false); + } + + protected static double computeSweep(double startAngle, double endAngle, double ctrlAngle) { + double sweep; + + startAngle = (360.0 + startAngle) % 360.0; + endAngle = (360.0 + endAngle) % 360.0; + ctrlAngle = (360.0 + ctrlAngle) % 360.0; + + // different sweeps depending on where the control point is + + if (startAngle < endAngle) { + if (startAngle < ctrlAngle && ctrlAngle < endAngle) { + sweep = startAngle - endAngle; + } else { + sweep = 360 + (startAngle - endAngle); + } + } else { + if (endAngle < ctrlAngle && ctrlAngle < startAngle) { + sweep = startAngle - endAngle; + } else { + sweep = - (360 - (startAngle - endAngle)); + } + } + + return sweep; + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRow.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRow.java new file mode 100644 index 0000000000..fcd488f3b7 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRow.java @@ -0,0 +1,31 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import java.awt.geom.Path2D; + +import org.apache.poi.xdgf.usermodel.XDGFShape; + + +public interface GeometryRow { + + public void setupMaster(GeometryRow row); + + public void addToPath(Path2D.Double path, XDGFShape parent); + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRowFactory.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRowFactory.java new file mode 100644 index 0000000000..57e21d1907 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRowFactory.java @@ -0,0 +1,57 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.util.ObjectFactory; + +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class GeometryRowFactory { + + static final ObjectFactory _rowTypes; + + static { + _rowTypes = new ObjectFactory<>(); + try { + _rowTypes.put("ArcTo", ArcTo.class, RowType.class); + _rowTypes.put("Ellipse", Ellipse.class, RowType.class); + _rowTypes.put("EllipticalArcTo", EllipticalArcTo.class, RowType.class); + _rowTypes.put("InfiniteLine", InfiniteLine.class, RowType.class); + _rowTypes.put("LineTo", LineTo.class, RowType.class); + _rowTypes.put("MoveTo", MoveTo.class, RowType.class); + _rowTypes.put("NURBSTo", NURBSTo.class, RowType.class); + _rowTypes.put("PolyLineTo", PolyLineTo.class, RowType.class); + _rowTypes.put("RelCubBezTo", RelCubBezTo.class, RowType.class); + _rowTypes.put("RelEllipticalArcTo", RelEllipticalArcTo.class, RowType.class); + _rowTypes.put("RelLineTo", RelLineTo.class, RowType.class); + _rowTypes.put("RelMoveTo", RelMoveTo.class, RowType.class); + _rowTypes.put("RelQuadBezTo", RelQuadBezTo.class, RowType.class); + _rowTypes.put("SplineKnot", SplineKnot.class, RowType.class); + _rowTypes.put("SplineStart", SplineStart.class, RowType.class); + } catch (NoSuchMethodException | SecurityException e) { + throw new POIXMLException("Internal error", e); + } + + } + + public static GeometryRow load(RowType row) { + return _rowTypes.load(row.getT(), row); + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/InfiniteLine.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/InfiniteLine.java new file mode 100644 index 0000000000..584acb8f50 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/InfiniteLine.java @@ -0,0 +1,151 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.geom.Dimension2dDouble; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFDocument; +import org.apache.poi.xdgf.usermodel.XDGFPage; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +/** + * Contains the x- and y-coordinates of two points on an infinite line. + */ +public class InfiniteLine implements GeometryRow { + + InfiniteLine _master = null; + + // An x-coordinate of a point on the infinite line; paired with y-coordinate represented by the Y cell. + Double x = null; + + // A y-coordinate of a point on the infinite line; paired with x-coordinate represented by the X cell. + Double y = null; + + // An x-coordinate of a point on the infinite line; paired with y-coordinate represented by the B cell. + Double a = null; + + // A y-coordinate of a point on an infinite line; paired with x-coordinate represented by the A cell. + Double b = null; + + Boolean deleted = null; + + // TODO: support formulas + + public InfiniteLine(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("B")) { + b = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in InfiniteLine row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + public Double getB() { + return b == null ? _master.b : b; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (InfiniteLine) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + + if (getDel()) return; + + throw new POIXMLException("InfiniteLine elements cannot be part of a path"); + } + + // returns this object as a line that extends between the boundaries of + // the document + public Path2D.Double getPath() { + Path2D.Double path = new Path2D.Double(); + + // this is a bit of a hack, but it works + double max_val = 100000; + + // compute slope.. + double x0 = getX(); + double y0 = getY(); + double x1 = getA(); // second x + double y1 = getB(); // second y + + if (x0 == x1) { + path.moveTo(x0, -max_val); + path.lineTo(x0, max_val); + } else if (y0 == y1) { + path.moveTo(-max_val, y0); + path.lineTo(max_val, y0); + } else { + + // normal case: compute slope/intercept + double m = (y1 - y0) / (x1 - x0); + double c = y0 - m*x0; + + // y = mx + c + + path.moveTo(max_val, m*max_val + c); + path.lineTo(max_val, (max_val - c)/m); + } + + return path; + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/LineTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/LineTo.java new file mode 100644 index 0000000000..e3bfe627bc --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/LineTo.java @@ -0,0 +1,88 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class LineTo implements GeometryRow { + + LineTo _master = null; + + Double x = null; + Double y = null; + + Boolean deleted = null; + + // TODO: support formulas + + public LineTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in LineTo row"); + } + } + } + + @Override + public String toString() { + return "LineTo: x=" + getX() + "; y=" + getY(); + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (LineTo) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + if (getDel()) return; + path.lineTo(getX(), getY()); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/MoveTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/MoveTo.java new file mode 100644 index 0000000000..fa704c821e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/MoveTo.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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +/** + * Contains the x- and y-coordinates of the first vertex of a shape or the x- + * and y-coordinates of the first vertex after a break in a path, relative to + * the height and width of the shape. + */ +public class MoveTo implements GeometryRow { + + MoveTo _master = null; + + Double x = null; + Double y = null; + + Boolean deleted = null; + + // TODO: support formulas + + public MoveTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in MoveTo row"); + } + } + } + + @Override + public String toString() { + return "MoveTo: x=" + getX() + "; y=" + getY(); + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (MoveTo) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + if (getDel()) return; + path.moveTo(getX(), getY()); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/NURBSTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/NURBSTo.java new file mode 100644 index 0000000000..39165238e4 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/NURBSTo.java @@ -0,0 +1,205 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import java.awt.geom.Point2D; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.geom.SplineRenderer; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.graphbuilder.curve.ControlPath; +import com.graphbuilder.curve.ShapeMultiPath; +import com.graphbuilder.curve.ValueVector; +import com.graphbuilder.geom.PointFactory; +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class NURBSTo implements GeometryRow { + + NURBSTo _master = null; + + // The x-coordinate of the last control point of a NURBS. + Double x = null; + + // The y-coordinate of the last control point of a NURBS. + Double y = null; + + // The second to the last knot of the NURBS. + Double a = null; + + // The last weight of the NURBS. + Double b = null; + + // The first knot of the NURBS. + Double c = null; + + // The first weight of the NURBS. + Double d = null; + + // A NURBS formula. + String e = null; + + Boolean deleted = null; + + // TODO: support formulas + + public NURBSTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("B")) { + b = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("C")) { + c = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("D")) { + d = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("E")) { + e = cell.getV(); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in NURBS row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + public Double getB() { + return b == null ? _master.b : b; + } + + public Double getC() { + return c == null ? _master.c : c; + } + + public Double getD() { + return d == null ? _master.d : d; + } + + public String getE() { + return e == null ? _master.e : e; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (NURBSTo) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + if (getDel()) return; + + Point2D last = path.getCurrentPoint(); + + // A NURBS formula: knotLast, degree, xType, yType, x1, y1, knot1, weight1, .. + String formula = getE().trim(); + if (!formula.startsWith("NURBS(") || !formula.endsWith(")")) + throw new POIXMLException("Invalid NURBS formula: " + formula); + + String [] components = formula.substring(6, formula.length()-1).split(","); + + if (components.length < 8) + throw new POIXMLException("Invalid NURBS formula (not enough arguments)"); + + if ((components.length - 4) % 4 != 0) + throw new POIXMLException("Invalid NURBS formula -- need 4 + n*4 arguments, got " + components.length); + + double lastControlX = getX(); + double lastControlY = getY(); + double secondToLastKnot = getA(); + double lastWeight = getB(); + double firstKnot = getC(); + double firstWeight = getD(); + + double lastKnot = Double.parseDouble(components[0].trim()); + int degree = Integer.parseInt(components[1].trim()); + int xType = Integer.parseInt(components[2].trim()); + int yType = Integer.parseInt(components[3].trim()); + + double xScale = 1; + double yScale = 1; + + if (xType == 0) + xScale = parent.getWidth(); + if (yType == 0) + yScale = parent.getHeight(); + + // setup first knots/weights/control point + ControlPath controlPath = new ControlPath(); + ValueVector knots = new ValueVector(); + ValueVector weights = new ValueVector(); + + knots.add(firstKnot); + weights.add(firstWeight); + controlPath.addPoint(PointFactory.create(last.getX(), last.getY())); + + // iterate get knots/weights + int sets = (components.length - 4) / 4; + for (int i = 0; i < sets; i++) { + double x1 = Double.parseDouble(components[4 + i*4 + 0].trim()); + double y1 = Double.parseDouble(components[4 + i*4 + 1].trim()); + double k = Double.parseDouble(components[4 + i*4 + 2].trim()); + double w = Double.parseDouble(components[4 + i*4 + 3].trim()); + + controlPath.addPoint(PointFactory.create(x1*xScale, y1*yScale)); + knots.add(k); + weights.add(w); + } + + // last knots/weights/control point + knots.add(secondToLastKnot); + knots.add(lastKnot); + + weights.add(lastWeight); + + controlPath.addPoint(PointFactory.create(lastControlX, lastControlY)); + + ShapeMultiPath shape = SplineRenderer.createNurbsSpline(controlPath, knots, weights, degree); + path.append(shape, true); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/PolyLineTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/PolyLineTo.java new file mode 100644 index 0000000000..7ce70d5b8c --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/PolyLineTo.java @@ -0,0 +1,95 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class PolyLineTo implements GeometryRow { + + PolyLineTo _master = null; + + // The x-coordinate of the ending vertex of a polyline. + Double x = null; + + // The y-coordinate of the ending vertex of a polyline. + Double y = null; + + // The polyline formula + String a = null; + + Boolean deleted = null; + + // TODO: support formulas + + public PolyLineTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = cell.getV(); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in ArcTo row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public String getA() { + return a == null ? _master.a : a; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (PolyLineTo) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + if (getDel()) return; + throw new POIXMLException("Polyline support not implemented"); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelCubBezTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelCubBezTo.java new file mode 100644 index 0000000000..4b0ad4781a --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelCubBezTo.java @@ -0,0 +1,127 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class RelCubBezTo implements GeometryRow { + + RelCubBezTo _master = null; + + // The x-coordinate of the ending vertex of a cubic Bézier curve relative to the width of the shape. + Double x = null; + + // The y-coordinate of the ending vertex of a cubic Bézier curve relative to the height of the shape. + Double y = null; + + // The x-coordinate of the curve’s beginning control point relative to the shape’s width; a point on the arc. The control point is best located between the beginning and ending vertices of the arc. + Double a = null; + + // The y-coordinate of a curve’s beginning control point relative to the shape’s height. + Double b = null; + + // The x-coordinate of the curve’s ending control point relative to the shape’s width; a point on the arc. The control point is best located between the beginning control point and ending vertices of the arc. + Double c = null; + + // The y-coordinate of a curve's ending control point relative to the shape’s height. + Double d = null; + + Boolean deleted = null; + + // TODO: support formulas + + public RelCubBezTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("B")) { + b = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("C")) { + c = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("D")) { + d = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in RelCubBezTo row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + public Double getB() { + return b == null ? _master.b : b; + } + + public Double getC() { + return c == null ? _master.c : c; + } + + public Double getD() { + return d == null ? _master.d : d; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (RelCubBezTo) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + + if (getDel()) return; + + double w = parent.getWidth(); + double h = parent.getHeight(); + + path.curveTo(getA()*w, getB()*h, getC()*w, getD()*h, getX()*w, getY()*h); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelEllipticalArcTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelEllipticalArcTo.java new file mode 100644 index 0000000000..99ae22b3e6 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelEllipticalArcTo.java @@ -0,0 +1,141 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class RelEllipticalArcTo implements GeometryRow { + + RelEllipticalArcTo _master = null; + + // The x-coordinate of the ending vertex on an arc relative to the width of + // the shape. + Double x = null; + + // The y-coordinate of the ending vertex on an arc relative to the height of + // the shape. + Double y = null; + + // The x-coordinate of the arc's control point relative to the shape’s + // width; a point on the arc. + Double a = null; + + // The y-coordinate of an arc's control point relative to the shape’s width. + Double b = null; + + // The angle of an arc's major axis relative to the x-axis of its parent. + Double c = null; + + // The ratio of an arc's major axis to its minor axis. Despite the usual + // meaning of these words, the "major" axis does not have to be greater than + // the "minor" axis, so this ratio does not have to be greater than 1. + Double d = null; + + Boolean deleted = null; + + // TODO: support formulas + + public RelEllipticalArcTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("B")) { + b = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("C")) { + c = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("D")) { + d = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in RelEllipticalArcTo row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + public Double getB() { + return b == null ? _master.b : b; + } + + public Double getC() { + return c == null ? _master.c : c; + } + + public Double getD() { + return d == null ? _master.d : d; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (RelEllipticalArcTo) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + + if (getDel()) return; + + double w = parent.getWidth(); + double h = parent.getHeight(); + + // intentionally shadowing variables here + double x = getX()*w; + double y = getY()*h; + double a = getA()*w; + double b = getB()*h; + double c = getC(); + double d = getD(); + + EllipticalArcTo.createEllipticalArc(x, y, a, b, c, d, path); + + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelLineTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelLineTo.java new file mode 100644 index 0000000000..e2cbeb4cdb --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelLineTo.java @@ -0,0 +1,90 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +/** + * Contains x-and y-coordinates of the ending vertex of a straight line segment + * relative to a shape’s width and height. + */ +public class RelLineTo implements GeometryRow { + + RelLineTo _master = null; + + Double x = null; + Double y = null; + + Boolean deleted = null; + + // TODO: support formulas + + public RelLineTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in RelLineTo row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (RelLineTo) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + + if (getDel()) return; + + path.lineTo(getX()*parent.getWidth(), + getY()*parent.getHeight()); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelMoveTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelMoveTo.java new file mode 100644 index 0000000000..5413e374a9 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelMoveTo.java @@ -0,0 +1,86 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +public class RelMoveTo implements GeometryRow { + + RelMoveTo _master = null; + + Double x = null; + Double y = null; + + Boolean deleted = null; + + // TODO: support formulas + + public RelMoveTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in RelMoveTo row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (RelMoveTo) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + + if (getDel()) return; + + path.moveTo(getX()*parent.getWidth(), + getY()*parent.getHeight()); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelQuadBezTo.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelQuadBezTo.java new file mode 100644 index 0000000000..ba715b1294 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/RelQuadBezTo.java @@ -0,0 +1,114 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +/** + * Contains the x- and y-coordinates of the endpoint of a quadratic Bézier curve + * relative to the shape’s width and height and the x- and y-coordinates of the + * control point of the curve relative shape’s width and height. + */ +public class RelQuadBezTo implements GeometryRow { + + RelQuadBezTo _master = null; + + // The x-coordinate of the ending vertex of a quadratic Bézier curve relative to the width of the shape. + Double x = null; + + // The y-coordinate of the ending vertex of a quadratic Bézier curve relative to the height of the shape. + Double y = null; + + // The x-coordinate of the curve’s control point relative to the shape’s width; a point on the arc. The control point is best located about halfway between the beginning and ending vertices of the arc. + Double a = null; + + // The y-coordinate of a curve’s control point relative to the shape’s height. + Double b = null; + + Boolean deleted = null; + + // TODO: support formulas + + public RelQuadBezTo(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("B")) { + b = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in RelQuadBezTo row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + public Double getB() { + return b == null ? _master.b : b; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (RelQuadBezTo) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + + if (getDel()) return; + + double w = parent.getWidth(); + double h = parent.getHeight(); + + path.quadTo(getA()*w, getB()*h, getX()*w, getY()*h); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/SplineKnot.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/SplineKnot.java new file mode 100644 index 0000000000..254fae5649 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/SplineKnot.java @@ -0,0 +1,102 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +/** + * Contains x- and y-coordinates for a spline's control point and a spline's knot. + */ +public class SplineKnot implements GeometryRow { + + SplineKnot _master = null; + + // The x-coordinate of a control point. + Double x = null; + + // The y-coordinate of a control point. + Double y = null; + + // One of the spline's knots (other than the last one or the first two). + Double a = null; + + Boolean deleted = null; + + // TODO: support formulas + + public SplineKnot(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in SplineKnot row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (SplineKnot) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + throw new POIXMLException("Error: Use SplineRenderer!"); + } + + @Override + public String toString() { + return "{SplineKnot x=" + getX() + " y=" + getY() + " a=" + getA() + "}"; + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/SplineStart.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/SplineStart.java new file mode 100644 index 0000000000..b485eb7180 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/SplineStart.java @@ -0,0 +1,132 @@ +/* ==================================================================== + 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.xdgf.usermodel.section.geometry; + +import org.apache.poi.POIXMLException; +import org.apache.poi.xdgf.usermodel.XDGFCell; +import org.apache.poi.xdgf.usermodel.XDGFShape; + +import com.microsoft.schemas.office.visio.x2012.main.CellType; +import com.microsoft.schemas.office.visio.x2012.main.RowType; + +/** + * Contains x- and y-coordinates for a spline's second control point, its second + * knot, its first knot, the last knot, and the degree of the spline. + */ +public class SplineStart implements GeometryRow { + + SplineStart _master = null; + + // The x-coordinate of a spline's second control point. + Double x = null; + + // The y-coordinate of a spline's second control point. + Double y = null; + + // The second knot of the spline. + Double a = null; + + // The first knot of a spline. + Double b = null; + + // The last knot of a spline. + Double c = null; + + // The degree of a spline (an integer from 1 to 25). + Integer d = null; + + Boolean deleted = null; + + // TODO: support formulas + + public SplineStart(RowType row) { + + if (row.isSetDel()) deleted = row.getDel(); + + for (CellType cell: row.getCellArray()) { + String cellName = cell.getN(); + + if (cellName.equals("X")) { + x = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("Y")) { + y = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("A")) { + a = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("B")) { + b = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("C")) { + c = XDGFCell.parseDoubleValue(cell); + } else if (cellName.equals("D")) { + d = XDGFCell.parseIntegerValue(cell); + } else { + throw new POIXMLException("Invalid cell '" + cellName + "' in SplineStart row"); + } + } + } + + public boolean getDel() { + if (deleted != null) + return deleted; + + if (_master != null) + return _master.getDel(); + + return false; + } + + public Double getX() { + return x == null ? _master.x : x; + } + + public Double getY() { + return y == null ? _master.y : y; + } + + public Double getA() { + return a == null ? _master.a : a; + } + + public Double getB() { + return b == null ? _master.b : b; + } + + public Double getC() { + return c == null ? _master.c : c; + } + + public Integer getD() { + return d == null ? _master.d : d; + } + + @Override + public void setupMaster(GeometryRow row) { + _master = (SplineStart) row; + } + + @Override + public void addToPath(java.awt.geom.Path2D.Double path, XDGFShape parent) { + throw new POIXMLException("Error: Use SplineRenderer!"); + } + + @Override + public String toString() { + return "{SplineStart x=" + getX() + " y=" + getY() + + " a=" + getA() + " b=" + getB() + + " c=" + getC() + " d=" + getD() + + "}"; + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeDataAcceptor.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeDataAcceptor.java new file mode 100644 index 0000000000..5090699783 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeDataAcceptor.java @@ -0,0 +1,69 @@ +/* ==================================================================== + 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.xdgf.usermodel.shape; + +import org.apache.poi.xdgf.usermodel.XDGFShape; + +/** + * This acceptor only allows traversal to shapes that have useful data + * associated with them, and tries to elide details that aren't useful + * when analyzing the content of a document. + * + * Useful is subjective of course, and is defined as any of: + * + * - Has non-empty text + * - Is a 1d shape, such as a line + * - User specified shapes + * - The outline of stencil objects + * - TODO + */ +public class ShapeDataAcceptor implements ShapeVisitorAcceptor { + + @Override + public boolean accept(XDGFShape shape) { + + if (shape.isDeleted()) + return false; + + // text is interesting + if (shape.hasText() && shape.getTextAsString().length() != 0) + return true; + + // 1d shapes are interesting, they create connections + if (shape.isShape1D()) + return true; + + // User specified shapes are interesting + if (!shape.hasMaster() && !shape.hasMasterShape()) + return true; + + if (shape.hasMaster() && !shape.hasMasterShape()) + return true; + + // include stencil content, but try to elide stencil interiors + //if (shape.getXmlObject().isSetMaster()) + // return true; + + if (shape.hasMasterShape() && shape.getMasterShape().isTopmost()) + return true; + + return false; + } + + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeDebuggerRenderer.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeDebuggerRenderer.java new file mode 100644 index 0000000000..2f32337e96 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeDebuggerRenderer.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.xdgf.usermodel.shape; + +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.geom.Path2D; + +import org.apache.poi.xdgf.usermodel.XDGFShape; + +public class ShapeDebuggerRenderer extends ShapeRenderer { + + ShapeVisitorAcceptor _debugAcceptor = null; + + public ShapeDebuggerRenderer() { + super(); + } + + public ShapeDebuggerRenderer(Graphics2D g) { + super(g); + } + + public void setDebugAcceptor(ShapeVisitorAcceptor acceptor) { + _debugAcceptor = acceptor; + } + + @Override + protected Path2D drawPath(XDGFShape shape){ + + Path2D path = super.drawPath(shape); + if (_debugAcceptor == null || _debugAcceptor.accept(shape)) { + + // show numbers to associate shapes with ids.. doesn't always work + Font f = _graphics.getFont(); + _graphics.scale(1, -1); + _graphics.setFont(f.deriveFont(0.05F)); + + String shapeId = "" + shape.getID(); + float shapeOffset = -0.1F; + + if (shape.hasMasterShape()) { + shapeId += " MS:" + shape.getMasterShape().getID(); + shapeOffset -= 0.15F; + } + + + _graphics.drawString(shapeId, shapeOffset, 0); + _graphics.setFont(f); + _graphics.scale(1, -1); + } + + return path; + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeRenderer.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeRenderer.java new file mode 100644 index 0000000000..1386368da7 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeRenderer.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.xdgf.usermodel.shape; + +import java.awt.BasicStroke; +import java.awt.Font; +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Path2D; + +import org.apache.poi.xdgf.usermodel.XDGFShape; +import org.apache.poi.xdgf.usermodel.XDGFText; + +/** + * To use this to render only particular shapes, override it and provide an + * appropriate implementation of getAcceptor() or accept() + */ +public class ShapeRenderer extends ShapeVisitor { + + protected Graphics2D _graphics; + + public ShapeRenderer() { + _graphics = null; + } + + public ShapeRenderer(Graphics2D g) { + _graphics = g; + } + + public void setGraphics(Graphics2D g) { + _graphics = g; + } + + @Override + public void visit(XDGFShape shape, AffineTransform globalTransform, int level) { + + AffineTransform savedTr = _graphics.getTransform(); + _graphics.transform(globalTransform); + + drawPath(shape); + drawText(shape); + + // we're done, undo the transforms + _graphics.setTransform(savedTr); + } + + protected Path2D drawPath(XDGFShape shape) { + Path2D.Double path = shape.getPath(); + if (path != null) { + + // setup the stroke for this line + + _graphics.setColor(shape.getLineColor()); + _graphics.setStroke(shape.getStroke()); + _graphics.draw(path); + } + + return path; + } + + protected void drawText(XDGFShape shape) { + XDGFText text = shape.getText(); + if (text != null) { + + if (text.getTextContent().equals("Header")) + text.getTextBounds(); + + Font oldFont = _graphics.getFont(); + + _graphics.setFont(oldFont.deriveFont(shape.getFontSize().floatValue())); + _graphics.setColor(shape.getFontColor()); + + text.draw(_graphics); + _graphics.setFont(oldFont); + } + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeVisitor.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeVisitor.java new file mode 100644 index 0000000000..cf4eee15c5 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeVisitor.java @@ -0,0 +1,68 @@ +/* ==================================================================== + 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.xdgf.usermodel.shape; + +import java.awt.geom.AffineTransform; + +import org.apache.poi.xdgf.usermodel.XDGFShape; + +/** + * Used to iterate through shapes + * + * To change the behavior of a particular visitor, you can override either + * accept() or getAcceptor() [preferred] + * + * If accept() or visit() throw StopVisitingThisBranch, the iteration will + * not visit subshapes of the shape. + */ +public abstract class ShapeVisitor{ + + ShapeVisitorAcceptor _acceptor; + + public ShapeVisitor() { + _acceptor = getAcceptor(); + } + + // is only called on construction of the visitor, allows + // mixing visitors and acceptors + public ShapeVisitorAcceptor getAcceptor() { + return new ShapeVisitorAcceptor() { + @Override + public boolean accept(XDGFShape shape) { + return !shape.isDeleted(); + } + }; + } + + public void setAcceptor(ShapeVisitorAcceptor acceptor) { + _acceptor = acceptor; + } + + + public boolean accept(XDGFShape shape) { + return _acceptor.accept(shape); + } + + /** + * @param shape Current shape + * @param globalTransform A transform that can convert the shapes points to global coordinates + * @param level Level in the tree (0 is topmost, 1 is next level... + */ + public abstract void visit(XDGFShape shape, AffineTransform globalTransform, int level); + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeVisitorAcceptor.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeVisitorAcceptor.java new file mode 100644 index 0000000000..f9fb98271e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/ShapeVisitorAcceptor.java @@ -0,0 +1,26 @@ +/* ==================================================================== + 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.xdgf.usermodel.shape; + +import org.apache.poi.xdgf.usermodel.XDGFShape; + +public interface ShapeVisitorAcceptor { + + public boolean accept(XDGFShape shape); + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/exceptions/StopVisiting.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/exceptions/StopVisiting.java new file mode 100644 index 0000000000..9a5d377b4e --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/exceptions/StopVisiting.java @@ -0,0 +1,24 @@ +/* ==================================================================== + 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.xdgf.usermodel.shape.exceptions; + +public class StopVisiting extends RuntimeException { + + private static final long serialVersionUID = -4651207777092840750L; + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/exceptions/StopVisitingThisBranch.java b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/exceptions/StopVisitingThisBranch.java new file mode 100644 index 0000000000..576e06276c --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/usermodel/shape/exceptions/StopVisitingThisBranch.java @@ -0,0 +1,24 @@ +/* ==================================================================== + 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.xdgf.usermodel.shape.exceptions; + +public class StopVisitingThisBranch extends RuntimeException { + + private static final long serialVersionUID = 5262319077534717862L; + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/util/HierarchyPrinter.java b/src/ooxml/java/org/apache/poi/xdgf/util/HierarchyPrinter.java new file mode 100644 index 0000000000..ae48c211fd --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/util/HierarchyPrinter.java @@ -0,0 +1,84 @@ +/* ==================================================================== + 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.xdgf.util; + +import java.awt.geom.AffineTransform; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; + +import org.apache.poi.xdgf.usermodel.XDGFPage; +import org.apache.poi.xdgf.usermodel.XDGFShape; +import org.apache.poi.xdgf.usermodel.XmlVisioDocument; +import org.apache.poi.xdgf.usermodel.shape.ShapeVisitor; + +public class HierarchyPrinter { + + public static void printHierarchy(XDGFPage page, File outDir) throws FileNotFoundException { + + File pageFile = new File(outDir, "page" + page.getPageNumber() + "-" + Util.sanitizeFilename(page.getName()) + ".txt"); + + OutputStream os = new FileOutputStream(pageFile); + PrintStream pos = new PrintStream(os); + + printHierarchy(page, pos); + + pos.close(); + } + + public static void printHierarchy(XDGFPage page, final PrintStream os) { + + page.getContent().visitShapes(new ShapeVisitor() { + + @Override + public void visit(XDGFShape shape, AffineTransform globalTransform, int level) { + for (int i = 0; i < level; i++) { + os.append(" "); + } + // TODO: write text? + os.println(shape.toString() + " [" + shape.getShapeType() + ", " + shape.getSymbolName() + "] " + shape.getMasterShape() + " " + shape.getTextAsString().trim()); + } + }); + } + + public static void printHierarchy(XmlVisioDocument document, String outDirname) throws FileNotFoundException { + + File outDir = new File(outDirname); + + for (XDGFPage page: document.getPages()) { + printHierarchy(page, outDir); + } + } + + + public static void main(String [] args) throws Exception { + if (args.length != 2) { + System.err.println("Usage: in.vsdx outdir"); + System.exit(1); + } + + String inFilename = args[0]; + String outDir = args[1]; + + XmlVisioDocument doc = new XmlVisioDocument(new FileInputStream(inFilename)); + printHierarchy(doc, outDir); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/util/ObjectFactory.java b/src/ooxml/java/org/apache/poi/xdgf/util/ObjectFactory.java new file mode 100644 index 0000000000..561e0c3c50 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/util/ObjectFactory.java @@ -0,0 +1,57 @@ +/* ==================================================================== + 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.xdgf.util; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.POIXMLException; +import org.apache.xmlbeans.XmlObject; + + +public class ObjectFactory { + + Map> _types = new HashMap<>(); + + public void put(String typeName, Class cls, Class... varargs) throws NoSuchMethodException, SecurityException { + _types.put(typeName, cls.getDeclaredConstructor(varargs)); + } + + public T load(String name, Object... varargs) { + Constructor constructor = _types.get(name); + if (constructor == null) { + + @SuppressWarnings("unchecked") + X xmlObject = (X) varargs[0]; + + String typeName = xmlObject.schemaType().getName().getLocalPart(); + throw new POIXMLException("Invalid '" + typeName + "' name '" + name + "'"); + } + + try { + return constructor.newInstance(varargs); + } catch (InvocationTargetException e) { + throw new POIXMLException(e.getCause()); + } catch (Exception e) { + throw new POIXMLException(e); + } + } + +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/util/Util.java b/src/ooxml/java/org/apache/poi/xdgf/util/Util.java new file mode 100644 index 0000000000..3c6725343c --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/util/Util.java @@ -0,0 +1,37 @@ +/* ==================================================================== + 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.xdgf.util; + +public class Util { + + public static int countLines(String str) { + int lines = 1; + int pos = 0; + while ((pos = str.indexOf("\n", pos) + 1) != 0) { + lines++; + } + return lines; + } + + // this probably isn't 100% correct, so don't use it in security-sensitive + // applications! + // from: http://www.rgagnon.com/javadetails/java-0662.html + public static String sanitizeFilename(String name) { + return name.replaceAll("[:\\\\/*\"?|<>]", "_"); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java b/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java new file mode 100644 index 0000000000..1bf6cf3e44 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/util/VsdxToPng.java @@ -0,0 +1,118 @@ +/* ==================================================================== + 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.xdgf.util; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.apache.poi.xdgf.geom.Dimension2dDouble; +import org.apache.poi.xdgf.usermodel.XDGFPage; +import org.apache.poi.xdgf.usermodel.XmlVisioDocument; +import org.apache.poi.xdgf.usermodel.shape.ShapeDebuggerRenderer; +import org.apache.poi.xdgf.usermodel.shape.ShapeRenderer; + +public class VsdxToPng { + + + public static void renderToPng(XDGFPage page, String outFilename, double scale, ShapeRenderer renderer) throws IOException { + renderToPng(page, new File(outFilename), scale, renderer); + } + + public static void renderToPngDir(XDGFPage page, File outDir, double scale, ShapeRenderer renderer) throws IOException { + + File pageFile = new File(outDir, "page" + page.getPageNumber() + "-" + Util.sanitizeFilename(page.getName()) + ".png"); + System.out.println("** Writing image to " + pageFile); + + renderToPng(page, pageFile, scale, renderer); + + } + + public static void renderToPng(XDGFPage page, File outFile, double scale, ShapeRenderer renderer) throws IOException { + + Dimension2dDouble sz = page.getPageSize(); + + int width = (int)(scale * sz.getWidth()); + int height = (int)(scale * sz.getHeight()); + + BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + final Graphics2D graphics = img.createGraphics(); + + // default rendering options + graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + + graphics.setColor(Color.black); + graphics.setBackground(Color.white); + graphics.clearRect(0, 0, width, height); + + // Visio's coordinate system is flipped, so flip the image vertically + graphics.translate(0, img.getHeight()); + graphics.scale(scale, -scale); + + // toplevel shapes only + renderer.setGraphics(graphics); + page.getContent().visitShapes(renderer); + + graphics.dispose(); + + FileOutputStream out = new FileOutputStream(outFile); + ImageIO.write(img, "png", out); + out.close(); + } + + public static void renderToPng(XmlVisioDocument document, String outDirname, double scale, ShapeRenderer renderer) throws IOException { + + File outDir = new File(outDirname); + + for (XDGFPage page: document.getPages()) { + renderToPngDir(page, outDir, scale, renderer); + } + } + + public static void main(String [] args) throws Exception { + if (args.length > 2) { + System.err.println("Usage: [--debug] in.vsdx outdir"); + System.exit(1); + } + + ShapeRenderer renderer = new ShapeRenderer(); + + String inFilename = args[0]; + String pngDir = args[1]; + + if (args[0].equals("--debug")) { + inFilename = args[1]; + pngDir = args[2]; + renderer = new ShapeDebuggerRenderer(); + } + + XmlVisioDocument doc = new XmlVisioDocument(new FileInputStream(inFilename)); + renderToPng(doc, pngDir, 2000/11.0, renderer); + } +} diff --git a/src/ooxml/java/org/apache/poi/xdgf/xml/XDGFXMLDocumentPart.java b/src/ooxml/java/org/apache/poi/xdgf/xml/XDGFXMLDocumentPart.java new file mode 100644 index 0000000000..afe3489086 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xdgf/xml/XDGFXMLDocumentPart.java @@ -0,0 +1,33 @@ +/* ==================================================================== + 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.xdgf.xml; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.xdgf.usermodel.XDGFDocument; + +public class XDGFXMLDocumentPart extends POIXMLDocumentPart { + + protected XDGFDocument _document; + + public XDGFXMLDocumentPart(PackagePart part, PackageRelationship rel, XDGFDocument document) { + super(part, rel); + _document = document; + } + +} diff --git a/src/ooxml/testcases/org/apache/poi/xdgf/usermodel/section/CombinedIteratorTest.java b/src/ooxml/testcases/org/apache/poi/xdgf/usermodel/section/CombinedIteratorTest.java new file mode 100644 index 0000000000..c48ab62c8e --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xdgf/usermodel/section/CombinedIteratorTest.java @@ -0,0 +1,159 @@ +/* ==================================================================== + 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.xdgf.usermodel.section; + +import java.util.Iterator; +import java.util.SortedMap; +import java.util.TreeMap; + +import org.junit.Assert; +import org.junit.Test; + +public class CombinedIteratorTest { + + void testIteration(CombinedIterable iterable, String... expected) { + + Iterator iter = iterable.iterator(); + + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(true, iter.hasNext()); + Assert.assertEquals(expected[i], iter.next()); + } + + Assert.assertEquals(false, iter.hasNext()); + } + + @Test + public void testNullMaster() { + + SortedMap base = new TreeMap<>(); + base.put(1L, "B1"); + base.put(2L, "B2"); + base.put(3L, "B3"); + + testIteration(new CombinedIterable<>(base, null), + "B1", "B2", "B3"); + } + + @Test + public void testNoMatchesBaseFirst() { + + SortedMap base = new TreeMap<>(); + base.put(1L, "B1"); + base.put(2L, "B2"); + base.put(3L, "B3"); + + SortedMap master = new TreeMap<>(); + master.put(4L, "M4"); + master.put(5L, "M5"); + master.put(6L, "M6"); + + testIteration(new CombinedIterable<>(base, master), + "B1", "B2", "B3", "M4", "M5", "M6"); + } + + @Test + public void testNoMatchesMasterFirst() { + + SortedMap base = new TreeMap<>(); + base.put(4L, "B4"); + base.put(5L, "B5"); + base.put(6L, "B6"); + + SortedMap master = new TreeMap<>(); + master.put(1L, "M1"); + master.put(2L, "M2"); + master.put(3L, "M3"); + + testIteration(new CombinedIterable<>(base, master), + "M1", "M2", "M3", "B4", "B5", "B6"); + } + + @Test + public void testInterleaved1() { + + SortedMap base = new TreeMap<>(); + base.put(1L, "B1"); + base.put(3L, "B3"); + base.put(5L, "B5"); + + SortedMap master = new TreeMap<>(); + master.put(2L, "M2"); + master.put(4L, "M4"); + master.put(6L, "M6"); + + testIteration(new CombinedIterable<>(base, master), + "B1", "M2", "B3", "M4", "B5", "M6"); + } + + @Test + public void testInterleaved2() { + + SortedMap base = new TreeMap<>(); + base.put(1L, "B1"); + base.put(2L, "B2"); + base.put(5L, "B5"); + base.put(6L, "B6"); + + SortedMap master = new TreeMap<>(); + master.put(3L, "M3"); + master.put(4L, "M4"); + master.put(7L, "M7"); + master.put(8L, "M8"); + + testIteration(new CombinedIterable<>(base, master), + "B1", "B2", "M3", "M4", "B5", "B6", "M7", "M8"); + } + + @Test + public void testAllMatching() { + + SortedMap base = new TreeMap<>(); + base.put(1L, "B1"); + base.put(2L, "B2"); + base.put(3L, "B3"); + + SortedMap master = new TreeMap<>(); + master.put(1L, "M1"); + master.put(2L, "M2"); + master.put(3L, "M3"); + + testIteration(new CombinedIterable<>(base, master), + "B1", "B2", "B3"); + } + + @Test + public void testAllMatching2() { + + SortedMap base = new TreeMap<>(); + base.put(1L, "B1"); + base.put(2L, "B2"); + base.put(3L, "B3"); + + SortedMap master = new TreeMap<>(); + master.put(1L, "M1"); + master.put(2L, "M2"); + master.put(3L, "M3"); + master.put(4L, "M4"); + + testIteration(new CombinedIterable<>(base, master), + "B1", "B2", "B3", "M4"); + } + + +} diff --git a/src/resources/devtools/unpack_ooxml.sh b/src/resources/devtools/unpack_ooxml.sh new file mode 100755 index 0000000000..a4b8703a84 --- /dev/null +++ b/src/resources/devtools/unpack_ooxml.sh @@ -0,0 +1,45 @@ +#!/bin/bash -e +# +# 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. + +# basename it + +FILENAME="$1" +BASENAME="${FILENAME##*/}" +OUTDIR="${BASENAME%.*}" + +if [ ! -f "$FILENAME" ]; then + echo "File $FILENAME does not exist" + exit 1 +fi + +# if dir exists, fail +if [ -d "$OUTDIR" ]; then + echo "Directory '$OUTDIR' already exists!" + exit 1 +fi + +mkdir "$OUTDIR" +unzip -d "$OUTDIR" "$FILENAME" + +pushd "$OUTDIR" + +find . -type f \( -iname "*.xml" -or -iname "*.rels" \) -exec python -c "import os, sys, xml.dom.minidom +with open('{}', 'r') as fp: + s = fp.read() +with open('{}.bak', 'w') as fp: + fp.write(xml.dom.minidom.parseString(s).toprettyxml().encode('utf-8')) +os.rename('{}.bak', '{}')" \;