You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by vi...@apache.org on 2015/10/19 07:52:37 UTC

svn commit: r1709354 [2/3] - in /poi/trunk/src: ooxml/java/org/apache/poi/xdgf/ ooxml/java/org/apache/poi/xdgf/exceptions/ ooxml/java/org/apache/poi/xdgf/geom/ ooxml/java/org/apache/poi/xdgf/usermodel/ ooxml/java/org/apache/poi/xdgf/usermodel/section/ ...

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFShape.java Mon Oct 19 05:52:35 2015
@@ -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<XDGFShape> _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<XDGFShape>();
+			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 + ": <Shape ID=\"" + getID() + "\">";
+		else
+			return "<Shape ID=\"" + getID() + "\">";
+	}
+	
+	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<XDGFShape> 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<String, XDGFSection> section: _sections.entrySet()) {
+				XDGFSection master = _masterShape.getSection(section.getKey());
+				if (master != null)
+					section.getValue().setupMaster(master);
+			}
+			
+			for (Entry<Long, GeometrySection> 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<XDGFShape> 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<GeometrySection> 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);
+		}
+	}
+	
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFSheet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFSheet.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFSheet.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFSheet.java Mon Oct 19 05:52:35 2015
@@ -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<String, XDGFCell> _cells = new HashMap<>();
+	
+	// sections
+	protected Map<String, XDGFSection> _sections = new HashMap<>();
+	
+	// special: geometry sections (key: index, value: section)
+	protected SortedMap<Long, GeometrySection> _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;
+	}	
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFStyleSheet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFStyleSheet.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFStyleSheet.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFStyleSheet.java Mon Oct 19 05:52:35 2015
@@ -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;
+	}
+	
+	
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XDGFText.java Mon Oct 19 05:52:35 2015
@@ -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);
+	}
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XmlVisioDocument.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XmlVisioDocument.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XmlVisioDocument.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/XmlVisioDocument.java Mon Oct 19 05:52:35 2015
@@ -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<PackagePart> getAllEmbedds() throws OpenXML4JException {
+		throw new UnsupportedOperationException("Not implemented");
+	}
+	
+	//
+	// Useful public API goes here
+	//
+
+	public Collection<XDGFPage> getPages() {
+		return _pages.getPageList();
+	}
+	
+
+	public XDGFStyleSheet getStyleById(long id) {
+		return _document.getStyleById(id);
+	}
+	
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CharacterSection.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CharacterSection.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CharacterSection.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CharacterSection.java Mon Oct 19 05:52:35 2015
@@ -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<String, XDGFCell> _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) {
+		
+	}
+	
+}
+

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CombinedIterable.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CombinedIterable.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CombinedIterable.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/CombinedIterable.java Mon Oct 19 05:52:35 2015
@@ -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<T> implements Iterable<T> {
+
+	final SortedMap<Long, T> _baseItems;
+	final SortedMap<Long, T> _masterItems;
+	
+	public CombinedIterable(SortedMap<Long, T> baseItems, 
+							SortedMap<Long, T> masterItems) {
+		_baseItems = baseItems;
+		_masterItems = masterItems;
+	}
+	
+	@Override
+	public Iterator<T> iterator() {
+		
+		final Iterator<Entry<Long, T>> vmasterI;
+		
+		if (_masterItems != null)
+			vmasterI = _masterItems.entrySet().iterator();
+		else
+			vmasterI = Collections.emptyIterator();
+		
+		return new Iterator<T>() {
+
+			Long lastI = Long.MIN_VALUE;
+			
+			Entry<Long, T> currentBase = null;
+			Entry<Long, T> currentMaster = null;
+			
+			// grab the iterator for both
+			Iterator<Entry<Long, T>> baseI = _baseItems.entrySet().iterator();
+			Iterator<Entry<Long, T>> 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();
+			}
+		};
+	}
+
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GenericSection.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GenericSection.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GenericSection.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GenericSection.java Mon Oct 19 05:52:35 2015
@@ -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) {
+	}
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GeometrySection.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GeometrySection.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GeometrySection.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/GeometrySection.java Mon Oct 19 05:52:35 2015
@@ -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<Long, GeometryRow> _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<Long, GeometryRow> 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<GeometryRow> getCombinedRows() {
+		return new CombinedIterable<>(_rows,
+									  _master == null ? null : _master._rows);
+	}
+
+	public Path2D.Double getPath(XDGFShape parent) {
+		
+		Iterator<GeometryRow> 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;
+		}
+	}
+	
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/XDGFSection.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/XDGFSection.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/XDGFSection.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/XDGFSection.java Mon Oct 19 05:52:35 2015
@@ -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<XDGFSection, SectionType> _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<String, XDGFCell> _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 "<Section type=" + _section.getN() + " from " + _containingSheet + ">";
+	}
+
+	public abstract void setupMaster(XDGFSection section);
+	
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/ArcTo.java Mon Oct 19 05:52:35 2015
@@ -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);
+	}
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/Ellipse.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/Ellipse.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/Ellipse.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/Ellipse.java Mon Oct 19 05:52:35 2015
@@ -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");
+	}
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/EllipticalArcTo.java Mon Oct 19 05:52:35 2015
@@ -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;
+	}
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRow.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRow.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRow.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRow.java Mon Oct 19 05:52:35 2015
@@ -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);
+	
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRowFactory.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRowFactory.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRowFactory.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/GeometryRowFactory.java Mon Oct 19 05:52:35 2015
@@ -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<GeometryRow, RowType> _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);
+	}
+	
+}

Added: poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/InfiniteLine.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/InfiniteLine.java?rev=1709354&view=auto
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/InfiniteLine.java (added)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xdgf/usermodel/section/geometry/InfiniteLine.java Mon Oct 19 05:52:35 2015
@@ -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;
+	}
+}




---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org