From 7ee8fe59a40fbedee77c8f6e54347b2dee5a1849 Mon Sep 17 00:00:00 2001 From: mino-alpha Date: Thu, 11 May 2023 15:47:59 +0900 Subject: [PATCH 1/8] Added connector and ShapeGroup to ShapeGroup --- OpenXmlFormats/Drawing/spreadsheetShape.cs | 38 ++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/OpenXmlFormats/Drawing/spreadsheetShape.cs b/OpenXmlFormats/Drawing/spreadsheetShape.cs index 232e54b68..bd2d9e899 100644 --- a/OpenXmlFormats/Drawing/spreadsheetShape.cs +++ b/OpenXmlFormats/Drawing/spreadsheetShape.cs @@ -595,14 +595,17 @@ public class CT_GroupShape { CT_GroupShapeProperties grpSpPrField; CT_GroupShapeNonVisual nvGrpSpPrField; - CT_Connector connectorField = null; + List connectors = null; List pictures = null; List shapes = null; + List groups = null; public CT_GroupShape() { + this.connectors = new List(); this.pictures = new List(); this.shapes = new List(); + this.groups = new List(); } public void Set(CT_GroupShape groupShape) @@ -623,7 +626,8 @@ public CT_GroupShapeNonVisual AddNewNvGrpSpPr() } public CT_Connector AddNewCxnSp() { - connectorField = new CT_Connector(); + var connectorField = new CT_Connector(); + connectors.Add(connectorField); return connectorField; } public CT_Shape AddNewSp() @@ -638,6 +642,12 @@ public CT_Picture AddNewPic() pictures.Add(pic); return pic; } + public CT_GroupShape AddNewGroup() + { + var group = new CT_GroupShape(); + groups.Add(group); + return group; + } public CT_GroupShapeNonVisual nvGrpSpPr { @@ -671,6 +681,16 @@ public static CT_GroupShape Parse(XmlNode node, XmlNamespaceManager namespaceMan var shape = CT_Shape.Parse(childNode, namespaceManager); ctObj.shapes.Add(shape); } + else if (childNode.LocalName == "cxnSp") + { + var connector = CT_Connector.Parse(childNode, namespaceManager); + ctObj.connectors.Add(connector); + } + else if (childNode.LocalName == "grpSp") + { + var group = CT_GroupShape.Parse(childNode, namespaceManager); + ctObj.groups.Add(group); + } } return ctObj; } @@ -699,6 +719,20 @@ internal void Write(StreamWriter sw, string nodeName) pic.Write(sw, "pic"); } } + if (this.connectors.Count > 0) + { + foreach(var con in this.connectors) + { + con.Write(sw, "cxnSp"); + } + } + if (this.groups.Count > 0) + { + foreach(var group in this.groups) + { + group.Write(sw, "grpSp"); + } + } sw.Write(string.Format("", nodeName)); } From 85059e99f9893a0effdca95cde80da719e3db561 Mon Sep 17 00:00:00 2001 From: mino-alpha Date: Thu, 11 May 2023 15:49:10 +0900 Subject: [PATCH 2/8] Write the initial value (0) of the attribute --- OpenXmlFormats/Drawing/Text.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OpenXmlFormats/Drawing/Text.cs b/OpenXmlFormats/Drawing/Text.cs index fb4138a5d..c80916c82 100644 --- a/OpenXmlFormats/Drawing/Text.cs +++ b/OpenXmlFormats/Drawing/Text.cs @@ -565,12 +565,12 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "horzOverflow", this.horzOverflowField.ToString()); if(this.vertFieldSpecified) XmlHelper.WriteAttribute(sw, "vert", this.vert.ToString()); - if(this.wrapFieldSpecified && this.wrap!= ST_TextWrappingType.none) + if(this.wrapFieldSpecified && this.wrap == ST_TextWrappingType.none) XmlHelper.WriteAttribute(sw, "wrap", this.wrap.ToString()); - XmlHelper.WriteAttribute(sw, "lIns", this.lIns); - XmlHelper.WriteAttribute(sw, "tIns", this.tIns); - XmlHelper.WriteAttribute(sw, "rIns", this.rIns); - XmlHelper.WriteAttribute(sw, "bIns", this.bIns); + XmlHelper.WriteAttribute(sw, "lIns", this.lIns, true); + XmlHelper.WriteAttribute(sw, "tIns", this.tIns, true); + XmlHelper.WriteAttribute(sw, "rIns", this.rIns, true); + XmlHelper.WriteAttribute(sw, "bIns", this.bIns, true); XmlHelper.WriteAttribute(sw, "numCol", this.numCol); XmlHelper.WriteAttribute(sw, "spcCol", this.spcCol); XmlHelper.WriteAttribute(sw, "rtlCol", this.rtlCol); From 4d418ebcea5e5d9ebdfba694442939c70a17a8b0 Mon Sep 17 00:00:00 2001 From: mino-alpha Date: Thu, 11 May 2023 15:49:40 +0900 Subject: [PATCH 3/8] implementation of some methods --- OpenXmlFormats/Drawing/ShapeGeometry.cs | 143 +++++++++++++++++------- 1 file changed, 103 insertions(+), 40 deletions(-) diff --git a/OpenXmlFormats/Drawing/ShapeGeometry.cs b/OpenXmlFormats/Drawing/ShapeGeometry.cs index c2827c8db..e2aca451a 100644 --- a/OpenXmlFormats/Drawing/ShapeGeometry.cs +++ b/OpenXmlFormats/Drawing/ShapeGeometry.cs @@ -711,7 +711,7 @@ public enum ST_TextShapeType [Serializable] - [DebuggerStepThrough] + //[DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -772,33 +772,56 @@ public string fmla [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] public class CT_Path2DCubicBezierTo { - private CT_AdjPoint2D[] ptField; + private List pts = null; + public static CT_Path2DCubicBezierTo Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + CT_Path2DCubicBezierTo obj = new CT_Path2DCubicBezierTo(); + foreach(XmlNode childNode in node.ChildNodes) { + if(childNode.LocalName == "pt") { + obj.pts.Add(CT_AdjPoint2D.Parse(childNode, namespaceManager)); + } + } + return obj; + } - [XmlElement("pt", Order = 0)] - public CT_AdjPoint2D[] pt + internal void Write(StreamWriter sw, string nodeName) { + + sw.Write("", nodeName); + foreach(var cub in pts) { + cub.Write(sw, "pt"); + } + sw.Write("", nodeName); + } + + public CT_Path2DCubicBezierTo() { + pts = new List(); + } + + [XmlElement("pt", Order = 0)] + public List pt { get { - return this.ptField; + return this.pts; } set { - this.ptField = value; + this.pts = value; } } } [Serializable] - [DebuggerStepThrough] + //[DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -826,8 +849,8 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(string.Format(""); - sw.Write(string.Format("", nodeName)); + sw.Write("/>"); + } [XmlAttribute] @@ -860,7 +883,7 @@ public string y [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -886,7 +909,7 @@ public CT_AdjPoint2D[] pt [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -948,7 +971,7 @@ internal void Write(StreamWriter sw, string nodeName) [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1045,7 +1068,7 @@ public string b [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1166,7 +1189,7 @@ public string maxY [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1287,7 +1310,7 @@ public string maxAng [Serializable] - [DebuggerStepThrough] + //[DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1353,7 +1376,7 @@ public string ang [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1380,7 +1403,7 @@ public object[] Items [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1433,7 +1456,7 @@ internal void Write(StreamWriter sw, string nodeName) [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1494,19 +1517,41 @@ public uint idx [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] public class CT_Path2DMoveTo { - private CT_AdjPoint2D ptField; + private CT_AdjPoint2D ptField = null; public CT_Path2DMoveTo() { this.ptField = new CT_AdjPoint2D(); } - [XmlElement(Order = 0)] + + public static CT_Path2DMoveTo Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + CT_Path2DMoveTo obj = new CT_Path2DMoveTo(); + foreach(XmlNode childNode in node.ChildNodes) { + if(childNode.LocalName == "pt") { + obj.ptField = CT_AdjPoint2D.Parse(childNode, namespaceManager); + } + } + return obj; + } + + internal void Write(StreamWriter sw, string nodeName) { + + sw.Write("", nodeName); + + if(this.ptField != null) { + this.ptField.Write(sw, "pt"); + } + sw.Write("", nodeName); + } + + [XmlElement(Order = 0)] public CT_AdjPoint2D pt { get @@ -1522,7 +1567,7 @@ public CT_AdjPoint2D pt [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1552,7 +1597,7 @@ public CT_AdjPoint2D pt [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1626,7 +1671,7 @@ public string swAng [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1661,7 +1706,7 @@ public enum ST_PathFillMode [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1681,7 +1726,12 @@ public class CT_Path2D private bool strokeField; private bool extrusionOkField; - public static CT_Path2D Parse(XmlNode node, XmlNamespaceManager namespaceManager) + + private CT_Path2DMoveTo moveToFeild = null; + + private List cubicBezTo = null; + + public static CT_Path2D Parse(XmlNode node, XmlNamespaceManager namespaceManager) { if (node == null) return null; @@ -1690,14 +1740,18 @@ public static CT_Path2D Parse(XmlNode node, XmlNamespaceManager namespaceManager ctObj.h = XmlHelper.ReadLong(node.Attributes["h"]); if (node.Attributes["fill"] != null) ctObj.fill = (ST_PathFillMode)Enum.Parse(typeof(ST_PathFillMode), node.Attributes["fill"].Value); - ctObj.stroke = XmlHelper.ReadBool(node.Attributes["stroke"]); + ctObj.stroke = XmlHelper.ReadBool(node.Attributes["stroke"], true); ctObj.extrusionOk = XmlHelper.ReadBool(node.Attributes["extrusionOk"]); - //foreach(XmlNode childNode in node.ChildNodes) - //{ + foreach(XmlNode childNode in node.ChildNodes) + { // if(childNode.LocalName == "ItemsElementName") // ctObj.ItemsElementName = ItemsChoiceType[].Parse(childNode, namespaceManager); - //} - return ctObj; + if(childNode.LocalName == "moveTo") + ctObj.moveToFeild = CT_Path2DMoveTo.Parse(childNode, namespaceManager); + if(childNode.LocalName == "cubicBezTo") + ctObj.cubicBezTo.Add( CT_Path2DCubicBezierTo.Parse(childNode, namespaceManager)); + } + return ctObj; } @@ -1711,9 +1765,16 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "stroke", this.stroke); XmlHelper.WriteAttribute(sw, "extrusionOk", this.extrusionOk); sw.Write(">"); - //if (this.ItemsElementName != null) - // this.ItemsElementName.Write(sw, "ItemsElementName"); - sw.Write(string.Format("", nodeName)); + //if (this.ItemsElementName != null) + // this.ItemsElementName.Write(sw, "ItemsElementName"); + if(this.moveToFeild != null) + moveToFeild.Write(sw, "moveTo"); + if(cubicBezTo.Count > 0) { + foreach(CT_Path2DCubicBezierTo cub in cubicBezTo) { + cub.Write(sw, "cubicBezTo"); + } + } + sw.Write(string.Format("", nodeName)); } public CT_Path2D() @@ -1723,7 +1784,9 @@ public CT_Path2D() this.fillField = ST_PathFillMode.norm; this.strokeField = true; this.extrusionOkField = true; - } + + this.cubicBezTo = new List(); + } //[XmlElement("arcTo", typeof(CT_Path2DArcTo))] @@ -1863,7 +1926,7 @@ public enum ItemsChoiceType [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -1917,7 +1980,7 @@ internal void Write(StreamWriter sw, string nodeName) [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -2000,7 +2063,7 @@ public ST_ShapeType prst [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] @@ -2074,7 +2137,7 @@ public ST_TextShapeType prst [Serializable] - [System.Diagnostics.DebuggerStepThrough] + //[System.Diagnostics.DebuggerStepThrough] [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main")] [XmlRoot(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/main", IsNullable = true)] From af1657d8eff1ee8aa5ff9c6ff0feb1ff254d32e8 Mon Sep 17 00:00:00 2001 From: mino-alpha Date: Mon, 5 Jun 2023 13:08:37 +0900 Subject: [PATCH 4/8] add properties shape id, name fix shape id numbering --- main/SS/UserModel/Shape.cs | 3 ++ ooxml/XSSF/UserModel/XSSFConnector.cs | 11 ++++ ooxml/XSSF/UserModel/XSSFDrawing.cs | 4 ++ ooxml/XSSF/UserModel/XSSFGraphicFrame.cs | 14 +++++- ooxml/XSSF/UserModel/XSSFPicture.cs | 23 +++++++++ ooxml/XSSF/UserModel/XSSFShape.cs | 11 ++++ ooxml/XSSF/UserModel/XSSFShapeGroup.cs | 28 +++++++++++ ooxml/XSSF/UserModel/XSSFSimpleShape.cs | 25 ++++++++++ .../ooxml/XSSF/UserModel/TestXSSFShapeName.cs | 50 +++++++++++++++++++ 9 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 testcases/ooxml/XSSF/UserModel/TestXSSFShapeName.cs diff --git a/main/SS/UserModel/Shape.cs b/main/SS/UserModel/Shape.cs index b1750b378..0e113259f 100644 --- a/main/SS/UserModel/Shape.cs +++ b/main/SS/UserModel/Shape.cs @@ -8,6 +8,9 @@ public interface IShape { IShape Parent { get; } + uint ID { get; } + string Name { get; set; } + void SetLineStyleColor(int lineStyleColor); void SetLineStyleColor(int red, int green, int blue); void SetFillColor(int red, int green, int blue); diff --git a/ooxml/XSSF/UserModel/XSSFConnector.cs b/ooxml/XSSF/UserModel/XSSFConnector.cs index 65077c1a5..7dc40b9dc 100644 --- a/ooxml/XSSF/UserModel/XSSFConnector.cs +++ b/ooxml/XSSF/UserModel/XSSFConnector.cs @@ -118,6 +118,17 @@ public ST_ShapeType ShapeType ctShape.spPr.prstGeom.prst = value; } } + + public override uint ID + { + get => ctShape.nvCxnSpPr.cNvPr.id; + } + + public override string Name + { + get => ctShape.nvCxnSpPr.cNvPr.name; + set => ctShape.nvCxnSpPr.cNvPr.name = value; + } protected internal override NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_ShapeProperties GetShapeProperties() { return ctShape.spPr; diff --git a/ooxml/XSSF/UserModel/XSSFDrawing.cs b/ooxml/XSSF/UserModel/XSSFDrawing.cs index b0912c1dd..85dd50757 100644 --- a/ooxml/XSSF/UserModel/XSSFDrawing.cs +++ b/ooxml/XSSF/UserModel/XSSFDrawing.cs @@ -282,9 +282,11 @@ public XSSFSimpleShape CreateSimpleShape(XSSFClientAnchor anchor) */ public XSSFConnector CreateConnector(XSSFClientAnchor anchor) { + long shapeId = newShapeId(); CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor(anchor); CT_Connector ctShape = ctAnchor.AddNewCxnSp(); ctShape.Set(XSSFConnector.Prototype()); + ctShape.nvCxnSpPr.cNvPr.id = (uint)(shapeId); XSSFConnector shape = new XSSFConnector(this, ctShape); shape.anchor = anchor; @@ -301,9 +303,11 @@ public XSSFConnector CreateConnector(XSSFClientAnchor anchor) */ public XSSFShapeGroup CreateGroup(XSSFClientAnchor anchor) { + long shapeId = newShapeId(); CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor(anchor); CT_GroupShape ctGroup = ctAnchor.AddNewGrpSp(); ctGroup.Set(XSSFShapeGroup.Prototype()); + ctGroup.nvGrpSpPr.cNvPr.id = (uint)(shapeId); XSSFShapeGroup shape = new XSSFShapeGroup(this, ctGroup); shape.anchor = anchor; diff --git a/ooxml/XSSF/UserModel/XSSFGraphicFrame.cs b/ooxml/XSSF/UserModel/XSSFGraphicFrame.cs index 872a1cd3f..94e36e54b 100644 --- a/ooxml/XSSF/UserModel/XSSFGraphicFrame.cs +++ b/ooxml/XSSF/UserModel/XSSFGraphicFrame.cs @@ -97,11 +97,23 @@ public void SetMacro(String macro) graphicFrame.macro = (macro); } + /** + * Returns the frame id. + * @return id of the frame + */ + public override uint ID + { + get + { + return GetNonVisualProperties().id; + } + } + /** * Returns the frame name. * @return name of the frame */ - public String Name + public override String Name { get { diff --git a/ooxml/XSSF/UserModel/XSSFPicture.cs b/ooxml/XSSF/UserModel/XSSFPicture.cs index 0902b6095..3c226eb40 100644 --- a/ooxml/XSSF/UserModel/XSSFPicture.cs +++ b/ooxml/XSSF/UserModel/XSSFPicture.cs @@ -118,6 +118,29 @@ internal static CT_Picture Prototype() return prototype; } + /** + * Returns the picture id. + * @return id of the picture + */ + public override uint ID { + get { + return ctPicture.nvPicPr.cNvPr.id; + } + } + + /** + * Returns the picture name. + * @return name of the picture + */ + public override String Name { + get { + return ctPicture.nvPicPr.cNvPr.name; + } + set { + ctPicture.nvPicPr.cNvPr.name = value; + } + } + /** * Link this shape with the picture data * diff --git a/ooxml/XSSF/UserModel/XSSFShape.cs b/ooxml/XSSF/UserModel/XSSFShape.cs index bd63414c0..086d576b6 100644 --- a/ooxml/XSSF/UserModel/XSSFShape.cs +++ b/ooxml/XSSF/UserModel/XSSFShape.cs @@ -85,6 +85,17 @@ public XSSFAnchor GetAnchor() */ protected internal abstract NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_ShapeProperties GetShapeProperties(); + public abstract uint ID + { + get; + } + + public abstract string Name + { + get; + set; + } + /** * Whether this shape is not Filled with a color * diff --git a/ooxml/XSSF/UserModel/XSSFShapeGroup.cs b/ooxml/XSSF/UserModel/XSSFShapeGroup.cs index c6f510c36..183eb638a 100644 --- a/ooxml/XSSF/UserModel/XSSFShapeGroup.cs +++ b/ooxml/XSSF/UserModel/XSSFShapeGroup.cs @@ -81,6 +81,34 @@ internal static CT_GroupShape Prototype() return prototype; } + /** + * Returns the shape group id. + * @return id of the shape group + */ + public override uint ID + { + get + { + return ctGroup.nvGrpSpPr.cNvPr.id; + } + } + + /** + * Returns the shape group name. + * @return name of the shape group + */ + public override String Name + { + get + { + return ctGroup.nvGrpSpPr.cNvPr.name; + } + set + { + ctGroup.nvGrpSpPr.cNvPr.name = value; + } + } + /** * Constructs a textbox. * diff --git a/ooxml/XSSF/UserModel/XSSFSimpleShape.cs b/ooxml/XSSF/UserModel/XSSFSimpleShape.cs index 35450dc57..885cce477 100644 --- a/ooxml/XSSF/UserModel/XSSFSimpleShape.cs +++ b/ooxml/XSSF/UserModel/XSSFSimpleShape.cs @@ -125,6 +125,31 @@ public CT_Shape GetCTShape() return ctShape; } + /** + * Returns the simple shape id. + * @return id of the simple shape + */ + public override uint ID { + get + { + return ctShape.nvSpPr.cNvPr.id; + } + } + + /** + * Returns the simple shape name. + * @return name of the simple shape + */ + public override String Name { + get + { + return ctShape.nvSpPr.cNvPr.name; + } + set + { + ctShape.nvSpPr.cNvPr.name = value; + } + } public IEnumerator GetEnumerator() { diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFShapeName.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFShapeName.cs new file mode 100644 index 000000000..a2a19574b --- /dev/null +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFShapeName.cs @@ -0,0 +1,50 @@ +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; +using NUnit.Framework; +using System.Text; + +namespace TestCases.XSSF.UserModel +{ + [TestFixture] + internal class TestXSSFShapeName + { + [Test] + public void TestNameAndID() + { + int id = 0; + + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet)wb.CreateSheet(); + XSSFDrawing drawing = (XSSFDrawing)sheet.CreateDrawingPatriarch(); + + XSSFClientAnchor anchor = new XSSFClientAnchor(0,0,0,0, 1,1,2,2); + + XSSFConnector cxn = drawing.CreateConnector(anchor); + cxn.Name = "cxn1"; + Assert.AreEqual("cxn1", cxn.Name); + Assert.AreEqual(++id, cxn.ID); + + //XSSFGraphicFrame gf = drawing.CreateGraphicFrame(anchor); + //gf.Name = "graphic frame 1"; + //Assert.AreEqual("graphic frame 1", gf.Name); + //Assert.AreEqual(++id, gf.ID); + + XSSFShapeGroup sg = drawing.CreateGroup(anchor); + sg.Name = "shape group 1"; + Assert.AreEqual("shape group 1", sg.Name); + Assert.AreEqual(++id, sg.ID); + + byte[] jpegData = Encoding.UTF8.GetBytes("test jpeg data"); + int jpegIdx = wb.AddPicture(jpegData, PictureType.JPEG); + XSSFPicture shape = (XSSFPicture)drawing.CreatePicture(anchor, jpegIdx); + shape.Name = "name test pic 1"; + Assert.AreEqual("name test pic 1", shape.Name, "ID={0}", shape.ID); + Assert.AreEqual(++id, shape.ID); + + XSSFSimpleShape ss = drawing.CreateSimpleShape(anchor); + ss.Name = "simple shape 1"; + Assert.AreEqual("simple shape 1", ss.Name); + Assert.AreEqual(++id, ss.ID); + } + } +} From 607204e7562d561cb3807e6573768fe755ce6877 Mon Sep 17 00:00:00 2001 From: mino-alpha Date: Mon, 5 Jun 2023 16:14:46 +0900 Subject: [PATCH 5/8] I added properties for CompoundLineType and LineEndingCapType for the shape --- main/SS/UserModel/LineStyle.cs | 20 ++++++ main/SS/UserModel/Shape.cs | 2 + ooxml/XSSF/UserModel/XSSFShape.cs | 63 ++++++++++++++-- .../ooxml/XSSF/UserModel/TestXSSFShape.cs | 71 +++++++++++++++++++ 4 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs diff --git a/main/SS/UserModel/LineStyle.cs b/main/SS/UserModel/LineStyle.cs index 82c5891be..024950c4e 100644 --- a/main/SS/UserModel/LineStyle.cs +++ b/main/SS/UserModel/LineStyle.cs @@ -19,4 +19,24 @@ public enum LineStyle:int LongDashDotGel = 9, // long dash short dash LongDashDotDotGel = 10, // long dash short dash short dash } + + // End Line Cap + public enum LineEndingCapType : int + { + None, + Round, // Rounded ends. Semi-circle protrudes by half line width. + Square, // Square protrudes by half line width. + Flat, // Line ends at end point. + } + + // Compound Line Type + public enum CompoundLineType : int + { + None, + SingleLine, // Single line: one normal width + DoubleLines, // Double lines of equal width + ThickThin, // Double lines: one thick, one thin + ThinThick, // Double lines: one thin, one thick + TripleLines // Three lines: thin, thick, thin + } } \ No newline at end of file diff --git a/main/SS/UserModel/Shape.cs b/main/SS/UserModel/Shape.cs index 0e113259f..a7a0891a4 100644 --- a/main/SS/UserModel/Shape.cs +++ b/main/SS/UserModel/Shape.cs @@ -20,6 +20,8 @@ public interface IShape int FillColor { get; set; } double LineWidth { get; set; } LineStyle LineStyle { get; set; } + LineEndingCapType LineEndingCapType{ get; set; } + CompoundLineType CompoundLineType { get; set; } bool IsNoFill { get; set; } int CountOfAllChildren { get; } } diff --git a/ooxml/XSSF/UserModel/XSSFShape.cs b/ooxml/XSSF/UserModel/XSSFShape.cs index 086d576b6..e5c3a3629 100644 --- a/ooxml/XSSF/UserModel/XSSFShape.cs +++ b/ooxml/XSSF/UserModel/XSSFShape.cs @@ -159,7 +159,9 @@ public int FillColor } set { - throw new System.NotImplementedException(); + SetFillColor(value >> 16 & 0xff + , value >> 8 & 0xff + , value & 0xff); } } @@ -172,16 +174,29 @@ public virtual LineStyle LineStyle set { NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_ShapeProperties props = GetShapeProperties(); - CT_LineProperties ln = props.IsSetLn() ? props.ln : props.AddNewLn(); - CT_PresetLineDashProperties dashStyle = new CT_PresetLineDashProperties(); - dashStyle.val = (ST_PresetLineDashVal)(value + 1); - ln.prstDash = dashStyle; + + if(value == LineStyle.None) { + props.ln = null; + } else { + + CT_LineProperties ln = props.IsSetLn() ? props.ln : props.AddNewLn(); + CT_PresetLineDashProperties dashStyle = new CT_PresetLineDashProperties(); + dashStyle.val = (ST_PresetLineDashVal)value; + ln.prstDash = dashStyle; + props.ln = ln; + } } } public virtual int LineStyleColor { get { throw new System.NotImplementedException(); } + set + { + SetLineStyleColor(value >> 16 & 0xff + , value >> 8 & 0xff + , value & 0xff); + } } public virtual double LineWidth @@ -210,6 +225,44 @@ public void SetLineStyleColor(int lineStyleColor) { throw new System.NotImplementedException(); } + + public virtual LineEndingCapType LineEndingCapType + { + get + { + NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_ShapeProperties props = GetShapeProperties(); + return props.IsSetLn() ? (LineEndingCapType)props.ln.cap : LineEndingCapType.None; + } + set + { + if(value == LineEndingCapType.None) + { + throw new System.ArgumentException(); + } + NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_ShapeProperties props = GetShapeProperties(); + CT_LineProperties ln = props.IsSetLn() ? props.ln : props.AddNewLn(); + ln.cap = (ST_LineCap) value; + } + } + + public virtual CompoundLineType CompoundLineType + { + get + { + NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_ShapeProperties props = GetShapeProperties(); + return props.IsSetLn() ? (CompoundLineType)props.ln.cmpd : CompoundLineType.None; + } + set + { + if(value == CompoundLineType.None) + { + throw new System.ArgumentException(); + } + NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_ShapeProperties props = GetShapeProperties(); + CT_LineProperties ln = props.IsSetLn() ? props.ln : props.AddNewLn(); + ln.cmpd = (ST_CompoundLine)value; + } + } } } diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs new file mode 100644 index 000000000..d010aa8a5 --- /dev/null +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs @@ -0,0 +1,71 @@ +using NPOI.XSSF; +using NPOI.XSSF.UserModel; +using NUnit.Framework; +using System.Collections.Generic; + +namespace TestCases.XSSF.UserModel +{ + [TestFixture] + internal class TestXSSFShape + { + + [Test] + public void TestShapeLineEndingCapType() + { + + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet)wb.CreateSheet(); + XSSFDrawing drawing = (XSSFDrawing)sheet.CreateDrawingPatriarch(); + + XSSFClientAnchor anchor = new XSSFClientAnchor(0,0,0,0, 1,1,2,2); + + XSSFConnector cxn = drawing.CreateConnector(anchor); + cxn.Name = "sp1"; + cxn.LineEndingCapType = NPOI.SS.UserModel.LineEndingCapType.Round; + + XSSFWorkbook rbwb = (XSSFWorkbook)XSSFITestDataProvider.instance.WriteOutAndReadBack(wb); + + XSSFSheet rb_sht = (XSSFSheet)rbwb.GetSheetAt(0); + XSSFDrawing dr = (XSSFDrawing)rb_sht.GetDrawingPatriarch(); + List lstShp = dr.GetShapes(); + foreach(var sp in lstShp) + { + if(sp.Name == "sp1") + { + Assert.AreEqual(NPOI.SS.UserModel.LineEndingCapType.Round, sp.LineEndingCapType); + return; + } + } + Assert.True(false); + } + [Test] + public void TestShapeCompoundLineType() + { + + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet)wb.CreateSheet(); + XSSFDrawing drawing = (XSSFDrawing)sheet.CreateDrawingPatriarch(); + + XSSFClientAnchor anchor = new XSSFClientAnchor(0,0,0,0, 1,1,2,2); + + XSSFConnector cxn = drawing.CreateConnector(anchor); + cxn.Name = "sp2"; + cxn.CompoundLineType = NPOI.SS.UserModel.CompoundLineType.DoubleLines; + + XSSFWorkbook rbwb = (XSSFWorkbook)XSSFITestDataProvider.instance.WriteOutAndReadBack(wb); + + XSSFSheet rb_sht = (XSSFSheet)rbwb.GetSheetAt(0); + XSSFDrawing dr = (XSSFDrawing)rb_sht.GetDrawingPatriarch(); + List lstShp = dr.GetShapes(); + foreach(var sp in lstShp) + { + if(sp.Name == "sp2") + { + Assert.AreEqual(NPOI.SS.UserModel.CompoundLineType.DoubleLines, sp.CompoundLineType); + return; + } + } + Assert.True(false); + } + } +} From fdbab24418fd84f997d94b218b436944bc4e9b89 Mon Sep 17 00:00:00 2001 From: mino-alpha Date: Thu, 8 Jun 2023 08:37:22 +0900 Subject: [PATCH 6/8] add Anchor coordinate, modify default value, add GetShape --- OpenXmlFormats/Drawing/SpreadsheetDrawing.cs | 12 +- OpenXmlFormats/Drawing/spreadsheetShape.cs | 33 +++++ OpenXmlFormats/Spreadsheet/Sheet.cs | 2 +- ooxml/XSSF/UserModel/XSSFChildAnchor.cs | 12 +- ooxml/XSSF/UserModel/XSSFChildGroupAnchor.cs | 109 ++++++++++++++ ooxml/XSSF/UserModel/XSSFClientAnchor.cs | 34 ++++- ooxml/XSSF/UserModel/XSSFDrawing.cs | 68 ++++++++- ooxml/XSSF/UserModel/XSSFShape.cs | 22 +++ ooxml/XSSF/UserModel/XSSFShapeGroup.cs | 30 ++++ .../ooxml/XSSF/UserModel/TestXSSFShape.cs | 138 +++++++++++++++++- .../ooxml/XSSF/UserModel/TestXSSFSheet.cs | 43 +++++- .../test-data/spreadsheet/TestGetShapes.xlsx | Bin 0 -> 5116 bytes .../test-data/spreadsheet/coordinate.xlsm | Bin 0 -> 15154 bytes 13 files changed, 485 insertions(+), 18 deletions(-) create mode 100644 ooxml/XSSF/UserModel/XSSFChildGroupAnchor.cs create mode 100644 testcases/test-data/spreadsheet/TestGetShapes.xlsx create mode 100644 testcases/test-data/spreadsheet/coordinate.xlsm diff --git a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs index 2ff68226c..7beeec7b5 100644 --- a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs +++ b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs @@ -1259,8 +1259,8 @@ public static CT_AnchorClientData Parse(XmlNode node, XmlNamespaceManager namesp if (node == null) return null; CT_AnchorClientData ctObj = new CT_AnchorClientData(); - ctObj.fLocksWithSheet = XmlHelper.ReadBool(node.Attributes["fLocksWithSheet"]); - ctObj.fPrintsWithSheet = XmlHelper.ReadBool(node.Attributes["fPrintsWithSheet"]); + ctObj.fLocksWithSheet = XmlHelper.ReadBool(node.Attributes["fLocksWithSheet"], true); + ctObj.fPrintsWithSheet = XmlHelper.ReadBool(node.Attributes["fPrintsWithSheet"], true); return ctObj; } @@ -1269,12 +1269,12 @@ public static CT_AnchorClientData Parse(XmlNode node, XmlNamespaceManager namesp internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format(""); } - bool _fLocksWithSheet; - bool _fPrintsWithSheet; + bool _fLocksWithSheet = true; + bool _fPrintsWithSheet = true; [XmlAttribute] public bool fLocksWithSheet diff --git a/OpenXmlFormats/Drawing/spreadsheetShape.cs b/OpenXmlFormats/Drawing/spreadsheetShape.cs index bd2d9e899..32a8ca5d4 100644 --- a/OpenXmlFormats/Drawing/spreadsheetShape.cs +++ b/OpenXmlFormats/Drawing/spreadsheetShape.cs @@ -660,6 +660,39 @@ public CT_GroupShapeProperties grpSpPr set { grpSpPrField = value; } } + public void GetShapes(List aShapes) // a:Argument + { + aShapes.Add(this); + if(connectors != null) + { + foreach(var c in connectors) + { + aShapes.Add(c); + } + } + if(pictures != null) + { + foreach(var p in pictures) + { + aShapes.Add(p); + } + } + if(shapes != null) + { + foreach(var s in shapes) + { + aShapes.Add(s); + } + } + if(groups != null) + { + foreach(var shp in groups) + { + shp.GetShapes(aShapes); + } + } + } + public static CT_GroupShape Parse(XmlNode node, XmlNamespaceManager namespaceManager) { if (node == null) diff --git a/OpenXmlFormats/Spreadsheet/Sheet.cs b/OpenXmlFormats/Spreadsheet/Sheet.cs index 8e4b8f0b8..2d9fcb026 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet.cs @@ -2599,7 +2599,7 @@ public static CT_SheetFormatPr Parse(XmlNode node, XmlNamespaceManager namespace if (node == null) return null; CT_SheetFormatPr ctObj = new CT_SheetFormatPr(); - ctObj.baseColWidth = XmlHelper.ReadUInt(node.Attributes["baseColWidth"]); + ctObj.baseColWidth = XmlHelper.ReadUInt(node.Attributes["baseColWidth"],8); ctObj.defaultColWidth = XmlHelper.ReadDouble(node.Attributes["defaultColWidth"]); ctObj.defaultRowHeight = XmlHelper.ReadDouble(node.Attributes["defaultRowHeight"]); ctObj.customHeight = XmlHelper.ReadBool(node.Attributes["customHeight"]); diff --git a/ooxml/XSSF/UserModel/XSSFChildAnchor.cs b/ooxml/XSSF/UserModel/XSSFChildAnchor.cs index 36b280ce4..97e75aab3 100644 --- a/ooxml/XSSF/UserModel/XSSFChildAnchor.cs +++ b/ooxml/XSSF/UserModel/XSSFChildAnchor.cs @@ -30,12 +30,12 @@ public XSSFChildAnchor(int x, int y, int cx, int cy) CT_Point2D off = t2d.AddNewOff(); CT_PositiveSize2D ext = t2d.AddNewExt(); - off.x = (x); - off.y = (y); - ext.cx = (Math.Abs(cx - x)); - ext.cy = (Math.Abs(cy - y)); - if (x > cx) t2d.flipH = (true); - if (y > cy) t2d.flipV = (true); + off.x = Math.Min(x, cx); + off.y = Math.Min(y, cy); + ext.cx = Math.Abs(cx - x); + ext.cy = Math.Abs(cy - y); + if (x > cx) t2d.flipH = true; + if (y > cy) t2d.flipV = true; } public XSSFChildAnchor(CT_Transform2D t2d) diff --git a/ooxml/XSSF/UserModel/XSSFChildGroupAnchor.cs b/ooxml/XSSF/UserModel/XSSFChildGroupAnchor.cs new file mode 100644 index 000000000..4b0a2a4cc --- /dev/null +++ b/ooxml/XSSF/UserModel/XSSFChildGroupAnchor.cs @@ -0,0 +1,109 @@ +/* ==================================================================== + 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. +==================================================================== */ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NPOI.OpenXmlFormats.Dml; + +namespace NPOI.XSSF.UserModel { + public class XSSFChildGroupAnchor : XSSFAnchor + { + private CT_GroupTransform2D gt2d; + public XSSFChildGroupAnchor(int x, int y, int cx, int cy) + { + gt2d = new CT_GroupTransform2D(); + CT_Point2D off = gt2d.AddNewOff(); + CT_PositiveSize2D ext = gt2d.AddNewExt(); + CT_Point2D chOff = gt2d.AddNewChOff(); + CT_PositiveSize2D chExt = gt2d.AddNewChExt(); + + off.x = Math.Min(x, cx); + off.y = Math.Min(y, cy); + ext.cx = Math.Abs(cx - x); + ext.cy = Math.Abs(cy - y); + if (x > cx) gt2d.flipH = true; + if (y > cy) gt2d.flipV = true; + + chOff.x = off.x; + chOff.y = off.y; + chExt.cx = ext.cx; + chExt.cy = ext.cy; + } + + public XSSFChildGroupAnchor(CT_GroupTransform2D gt2d) + { + this.gt2d = gt2d; + } + + + public CT_GroupTransform2D GetCTTransform2D() + { + return gt2d; + } + + public override int Dx1 + { + get + { + return (int)gt2d.off.x; + + } + set + { + gt2d.off.y = (value); + } + } + + public override int Dy1 + { + get + { + return (int)gt2d.off.y; + } + set + { + gt2d.off.y = (value); + } + } + + public override int Dy2 + { + get + { + return (int)(Dy1 + gt2d.ext.cy); + } + set + { + gt2d.ext.cy = (value - Dy1); + } + } + + public override int Dx2 + { + get + { + return (int)(Dx1 + gt2d.ext.cx); + } + set + { + gt2d.ext.cx = (value - Dx1); + } + } + } +} diff --git a/ooxml/XSSF/UserModel/XSSFClientAnchor.cs b/ooxml/XSSF/UserModel/XSSFClientAnchor.cs index 6f6a21c8d..599c42369 100644 --- a/ooxml/XSSF/UserModel/XSSFClientAnchor.cs +++ b/ooxml/XSSF/UserModel/XSSFClientAnchor.cs @@ -41,6 +41,23 @@ public class XSSFClientAnchor : XSSFAnchor, IClientAnchor */ private CT_Marker cell2; + public int left + { + get; + } + public int top + { + get; + } + public int width + { + get; + } + public int height + { + get; + } + /** * Creates a new client anchor and defaults all the anchor positions to 0. */ @@ -97,9 +114,22 @@ internal XSSFClientAnchor(CT_Marker cell1, CT_Marker cell2) this.cell2 = cell2; } - - + /** + * Create XSSFClientAnchor from existing xml beans + * + * @param cell1 starting anchor point + * @param cell2 ending anchor point + */ + internal XSSFClientAnchor(CT_Marker cell1, CT_Marker cell2, int left, int top, int right, int bottom) + { + this.cell1 = cell1; + this.cell2 = cell2; + this.left = left; + this.top = top; + this.width = Math.Abs(right- left); + this.height = Math.Abs(bottom - top); + } public override bool Equals(Object o) { diff --git a/ooxml/XSSF/UserModel/XSSFDrawing.cs b/ooxml/XSSF/UserModel/XSSFDrawing.cs index 85dd50757..9f649faeb 100644 --- a/ooxml/XSSF/UserModel/XSSFDrawing.cs +++ b/ooxml/XSSF/UserModel/XSSFDrawing.cs @@ -127,6 +127,10 @@ public XSSFTextBox CreateTextbox(IClientAnchor anchor) CT_Shape ctShape = ctAnchor.AddNewSp(); ctShape.Set(XSSFSimpleShape.Prototype()); ctShape.nvSpPr.cNvPr.id=(uint)shapeId; + ctShape.spPr.xfrm.off.x = ((XSSFClientAnchor)(anchor)).left; + ctShape.spPr.xfrm.off.y = ((XSSFClientAnchor)(anchor)).top; + ctShape.spPr.xfrm.ext.cx = ((XSSFClientAnchor)(anchor)).width; + ctShape.spPr.xfrm.ext.cy = ((XSSFClientAnchor)(anchor)).height; XSSFTextBox shape = new XSSFTextBox(this, ctShape); shape.anchor = (XSSFClientAnchor)anchor; return shape; @@ -152,6 +156,10 @@ public IPicture CreatePicture(XSSFClientAnchor anchor, int pictureIndex) ctShape.Set(XSSFPicture.Prototype()); ctShape.nvPicPr.cNvPr.id = (uint)shapeId; + ctShape.spPr.xfrm.off.x = anchor.left; + ctShape.spPr.xfrm.off.y = anchor.top; + ctShape.spPr.xfrm.ext.cx = anchor.width; + ctShape.spPr.xfrm.ext.cy = anchor.height; ctShape.nvPicPr.cNvPr.name = "Picture " + shapeId; XSSFPicture shape = new XSSFPicture(this, ctShape); @@ -267,8 +275,14 @@ public XSSFSimpleShape CreateSimpleShape(XSSFClientAnchor anchor) CT_Shape ctShape = ctAnchor.AddNewSp(); ctShape.Set(XSSFSimpleShape.Prototype()); ctShape.nvSpPr.cNvPr.id=(uint)(shapeId); + ctShape.spPr.xfrm.off.x = anchor.left; + ctShape.spPr.xfrm.off.y = anchor.top; + ctShape.spPr.xfrm.ext.cx = anchor.width; + ctShape.spPr.xfrm.ext.cy = anchor.height; XSSFSimpleShape shape = new XSSFSimpleShape(this, ctShape); shape.anchor = anchor; + shape.cttwocellanchor = ctAnchor; + return shape; } @@ -287,9 +301,15 @@ public XSSFConnector CreateConnector(XSSFClientAnchor anchor) CT_Connector ctShape = ctAnchor.AddNewCxnSp(); ctShape.Set(XSSFConnector.Prototype()); ctShape.nvCxnSpPr.cNvPr.id = (uint)(shapeId); + ctShape.spPr.xfrm.off.x = anchor.left; + ctShape.spPr.xfrm.off.y = anchor.top; + ctShape.spPr.xfrm.ext.cx = anchor.width; + ctShape.spPr.xfrm.ext.cy = anchor.height; XSSFConnector shape = new XSSFConnector(this, ctShape); shape.anchor = anchor; + shape.cttwocellanchor = ctAnchor; + return shape; } @@ -308,9 +328,19 @@ public XSSFShapeGroup CreateGroup(XSSFClientAnchor anchor) CT_GroupShape ctGroup = ctAnchor.AddNewGrpSp(); ctGroup.Set(XSSFShapeGroup.Prototype()); ctGroup.nvGrpSpPr.cNvPr.id = (uint)(shapeId); + ctGroup.grpSpPr.xfrm.off.x = anchor.left; + ctGroup.grpSpPr.xfrm.off.y = anchor.top; + ctGroup.grpSpPr.xfrm.ext.cx = anchor.width; + ctGroup.grpSpPr.xfrm.ext.cy = anchor.height; + ctGroup.grpSpPr.xfrm.chOff.x = ctGroup.grpSpPr.xfrm.off.x; + ctGroup.grpSpPr.xfrm.chOff.y = ctGroup.grpSpPr.xfrm.off.y; + ctGroup.grpSpPr.xfrm.chExt.cx =ctGroup.grpSpPr.xfrm.ext.cx; + ctGroup.grpSpPr.xfrm.chExt.cy =ctGroup.grpSpPr.xfrm.ext.cy; XSSFShapeGroup shape = new XSSFShapeGroup(this, ctGroup); shape.anchor = anchor; + shape.cttwocellanchor = ctAnchor; + return shape; } @@ -371,6 +401,8 @@ private XSSFGraphicFrame CreateGraphicFrame(XSSFClientAnchor anchor) graphicFrame.Anchor = anchor; graphicFrame.Id = frameId; graphicFrame.Name = "Diagramm" + frameId; + graphicFrame.cttwocellanchor = ctAnchor; + return graphicFrame; } @@ -456,7 +488,39 @@ public List GetShapes() } else if (anchor.groupShape != null) { - shape = new XSSFShapeGroup(this, anchor.groupShape); + List lstCtShapes = new List(); + anchor.groupShape.GetShapes(lstCtShapes); + + foreach(var s in lstCtShapes) + { + XSSFShape gShape = null; + if(s is CT_Connector) + { + gShape = new XSSFConnector(this, (CT_Connector)s); + } + else if(s is CT_Picture) + { + gShape = new XSSFPicture(this, (CT_Picture)s); + } + else if(s is CT_Shape) + { + gShape = new XSSFSimpleShape(this, (CT_Shape)s); + } + else if(s is CT_GroupShape) + { + gShape = new XSSFShapeGroup(this, (CT_GroupShape)s); + } + else if(s is CT_GraphicalObjectFrame) + { + gShape = new XSSFGraphicFrame(this, (CT_GraphicalObjectFrame)s); + } + if(gShape != null) + { + gShape.anchor = GetAnchorFromIEGAnchor(anchor); + gShape.cttwocellanchor = (CT_TwoCellAnchor)anchor; + lst.Add(gShape); + } + } } else if (anchor.graphicFrame != null) { @@ -469,6 +533,7 @@ public List GetShapes() if (shape != null) { shape.anchor = GetAnchorFromIEGAnchor(anchor); + shape.cttwocellanchor = (CT_TwoCellAnchor)anchor; lst.Add(shape); } } @@ -497,6 +562,7 @@ public List GetShapes() //} return lst; } + private XSSFAnchor GetAnchorFromIEGAnchor(IEG_Anchor ctAnchor) { CT_Marker ctFrom=null, ctTo=null; diff --git a/ooxml/XSSF/UserModel/XSSFShape.cs b/ooxml/XSSF/UserModel/XSSFShape.cs index e5c3a3629..e2eb01b29 100644 --- a/ooxml/XSSF/UserModel/XSSFShape.cs +++ b/ooxml/XSSF/UserModel/XSSFShape.cs @@ -39,6 +39,7 @@ public abstract class XSSFShape:IShape */ protected XSSFDrawing drawing; + protected CT_TwoCellAnchor _twocellanchor; /** * The parent shape, always not-null for shapes in groups */ @@ -78,6 +79,27 @@ public XSSFAnchor GetAnchor() return anchor; } + public CT_TwoCellAnchor cttwocellanchor + { + get + { + if(_twocellanchor == null) { + return parent.cttwocellanchor; + } + return _twocellanchor; + } + set + { + if(parent == null) + { + _twocellanchor = value; + } + else + { + parent.cttwocellanchor = value; + } + } + } /** * Returns xml bean with shape properties. * diff --git a/ooxml/XSSF/UserModel/XSSFShapeGroup.cs b/ooxml/XSSF/UserModel/XSSFShapeGroup.cs index 183eb638a..0f0cee419 100644 --- a/ooxml/XSSF/UserModel/XSSFShapeGroup.cs +++ b/ooxml/XSSF/UserModel/XSSFShapeGroup.cs @@ -120,6 +120,10 @@ public XSSFTextBox CreateTextbox(XSSFChildAnchor anchor) { CT_Shape ctShape = ctGroup.AddNewSp(); ctShape.Set(XSSFSimpleShape.Prototype()); + ctShape.spPr.xfrm.off.x = anchor.Dx1; + ctShape.spPr.xfrm.off.y = anchor.Dy1; + ctShape.spPr.xfrm.ext.cx = anchor.Dx2; + ctShape.spPr.xfrm.ext.cy = anchor.Dy2; XSSFTextBox shape = new XSSFTextBox(GetDrawing(), ctShape); shape.parent = this; @@ -140,6 +144,10 @@ public XSSFSimpleShape CreateSimpleShape(XSSFChildAnchor anchor) { CT_Shape ctShape = ctGroup.AddNewSp(); ctShape.Set(XSSFSimpleShape.Prototype()); + ctShape.spPr.xfrm.off.x = anchor.Dx1; + ctShape.spPr.xfrm.off.y = anchor.Dy1; + ctShape.spPr.xfrm.ext.cx = anchor.Dx2; + ctShape.spPr.xfrm.ext.cy = anchor.Dy2; XSSFSimpleShape shape = new XSSFSimpleShape(GetDrawing(), ctShape); shape.parent = (this); @@ -160,6 +168,10 @@ public XSSFConnector CreateConnector(XSSFChildAnchor anchor) { CT_Connector ctShape = ctGroup.AddNewCxnSp(); ctShape.Set(XSSFConnector.Prototype()); + ctShape.spPr.xfrm.off.x = anchor.Dx1; + ctShape.spPr.xfrm.off.y = anchor.Dy1; + ctShape.spPr.xfrm.ext.cx = anchor.Dx2; + ctShape.spPr.xfrm.ext.cy = anchor.Dy2; XSSFConnector shape = new XSSFConnector(GetDrawing(), ctShape); shape.parent = this; @@ -182,6 +194,10 @@ public XSSFPicture CreatePicture(XSSFClientAnchor anchor, int pictureIndex) CT_Picture ctShape = ctGroup.AddNewPic(); ctShape.Set(XSSFPicture.Prototype()); + ctShape.spPr.xfrm.off.x = anchor.Dx1; + ctShape.spPr.xfrm.off.y = anchor.Dy1; + ctShape.spPr.xfrm.ext.cx = anchor.Dx2; + ctShape.spPr.xfrm.ext.cy = anchor.Dy2; XSSFPicture shape = new XSSFPicture(GetDrawing(), ctShape); shape.parent = this; @@ -190,6 +206,20 @@ public XSSFPicture CreatePicture(XSSFClientAnchor anchor, int pictureIndex) return shape; } + public XSSFShapeGroup CreateGroup(XSSFChildGroupAnchor anchor) + { + CT_GroupShape ctShape = ctGroup.AddNewGroup(); + ctShape.Set(XSSFShapeGroup.Prototype()); + + XSSFShapeGroup group = new XSSFShapeGroup(GetDrawing(), ctShape) + { + parent = this, + anchor = anchor + }; + group.GetCTGroupShape().grpSpPr.xfrm = anchor.GetCTTransform2D(); + + return group; + } public CT_GroupShape GetCTGroupShape() { diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs index d010aa8a5..ae5c69090 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs @@ -1,7 +1,11 @@ -using NPOI.XSSF; +using NPOI.SS.UserModel; +using NPOI.Util; +using NPOI.XSSF; using NPOI.XSSF.UserModel; using NUnit.Framework; using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; namespace TestCases.XSSF.UserModel { @@ -67,5 +71,137 @@ public void TestShapeCompoundLineType() } Assert.True(false); } + + [Test] + public void TestGetShapes() + { + XSSFWorkbook wb = XSSFTestDataSamples.OpenSampleWorkbook("TestGetShapes.xlsx"); + XSSFSheet sheet = (XSSFSheet)wb.GetSheet("Sheet0"); + + XSSFDrawing drawing = sheet.GetDrawingPatriarch(); + + List lstShp = drawing.GetShapes(); + foreach(var sp in lstShp) + { + Debug.WriteLine($"name:[{sp.Name}]"); + } + } + + [Test] + public void TestLockWithSheet() + { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet)wb.CreateSheet(); + XSSFDrawing drawing = (XSSFDrawing)sheet.CreateDrawingPatriarch(); + + // simple shape + XSSFClientAnchor ca0; + ca0 = sheet.CreateClientAnchor(Units.ToEMU(100), Units.ToEMU(100), Units.ToEMU(200), Units.ToEMU(200)); + XSSFSimpleShape shp0 = drawing.CreateSimpleShape(ca0); + shp0.Name = "S00"; + shp0.cttwocellanchor.clientData.fLocksWithSheet = true; + + // connector shape + XSSFClientAnchor ca1; + ca1 = sheet.CreateClientAnchor(Units.ToEMU(250), Units.ToEMU(250), Units.ToEMU(350), Units.ToEMU(350)); + XSSFConnector shp1 = drawing.CreateConnector(ca1); + shp1.Name = "L00"; + shp1.cttwocellanchor.clientData.fLocksWithSheet = true; + + XSSFTestDataSamples.WriteOut(wb, "TestLockWithSheet1-"); + + XSSFWorkbook rbwb = (XSSFWorkbook)XSSFITestDataProvider.instance.WriteOutAndReadBack(wb); + + XSSFSheet rb_sht = (XSSFSheet)rbwb.GetSheetAt(0); + XSSFDrawing dr = (XSSFDrawing)rb_sht.GetDrawingPatriarch(); + List lstShp = dr.GetShapes(); + foreach(var sp in lstShp) + { + switch(sp.Name) + { + case "S00": + case "L00": + Assert.AreEqual(sp.cttwocellanchor.clientData.fLocksWithSheet, true, "shape name:[{0}]", new object[] { sp.Name }); + break; + default: + break; + } + } + + shp0.cttwocellanchor.clientData.fLocksWithSheet = false; + shp1.cttwocellanchor.clientData.fLocksWithSheet = false; + + XSSFTestDataSamples.WriteOut(wb, "TestLockWithSheet2-"); + + XSSFWorkbook rbwb1 = (XSSFWorkbook)XSSFITestDataProvider.instance.WriteOutAndReadBack(wb); + XSSFSheet rb_sht1 = (XSSFSheet)rbwb1.GetSheetAt(0); + XSSFDrawing dr1 = (XSSFDrawing)rb_sht1.GetDrawingPatriarch(); + List lstShp1 = dr1.GetShapes(); + + foreach(var sp in lstShp1) + { + switch(sp.Name) + { + case "S00": + case "L00": + Assert.AreEqual(sp.cttwocellanchor.clientData.fLocksWithSheet, false, "shape name:[{0}]", new object[] { sp.Name }); + break; + default: + break; + } + } + } + + [Test] + public void TestGroupLockWithSheet() + { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet)wb.CreateSheet(); + XSSFDrawing drawing = (XSSFDrawing)sheet.CreateDrawingPatriarch(); + + //----- first group + XSSFClientAnchor ganchor1; + ganchor1 = sheet.CreateClientAnchor(Units.ToEMU(100), Units.ToEMU(200), Units.ToEMU(200), Units.ToEMU(100)); + ganchor1.AnchorType = AnchorType.DontMoveAndResize; + XSSFShapeGroup grp1= drawing.CreateGroup( ganchor1 ); + grp1.Name = "first"; + + // connector shape + XSSFChildAnchor ca1; + ca1 = new XSSFChildAnchor(Units.ToEMU(100), Units.ToEMU(200), Units.ToEMU(200), Units.ToEMU(100)); + XSSFConnector shp1 = grp1.CreateConnector( ca1 ); + shp1.Name = "L01"; + shp1.cttwocellanchor.clientData.fLocksWithSheet = true; + + //----- second group + XSSFChildGroupAnchor ganchor2; + ganchor2 = new XSSFChildGroupAnchor(Units.ToEMU(110), Units.ToEMU(110), Units.ToEMU(190), Units.ToEMU(190)); + XSSFShapeGroup grp2 = grp1.CreateGroup( ganchor2 ); + grp2.Name = "second"; + + // connector shape + XSSFChildAnchor ca2; + ca2 = new XSSFChildAnchor(Units.ToEMU(110), Units.ToEMU(110), Units.ToEMU(190), Units.ToEMU(190)); + XSSFConnector shp2 = grp2.CreateConnector(ca2); + shp2.Name = "L02"; + shp2.cttwocellanchor.clientData.fLocksWithSheet = true; + + //----- third group + XSSFChildGroupAnchor ganchor3; + ganchor3 = new XSSFChildGroupAnchor(Units.ToEMU(120), Units.ToEMU(120), Units.ToEMU(180), Units.ToEMU(180)); + XSSFShapeGroup grp3 = grp2.CreateGroup( ganchor3 ); + grp3.Name = "third"; + + // connector shape + XSSFChildAnchor ca3; + ca3 = new XSSFChildAnchor(Units.ToEMU(120), Units.ToEMU(150), Units.ToEMU(180), Units.ToEMU(150)); + XSSFConnector shp3 = grp3.CreateConnector(ca3); + shp3.Name = "L03"; + shp3.cttwocellanchor.clientData.fLocksWithSheet = false; + + Assert.AreEqual(shp1.cttwocellanchor.clientData.fLocksWithSheet, false); + + XSSFTestDataSamples.WriteOut(wb, "TestGroupLockWithSheet"); + } } } diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFSheet.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFSheet.cs index dc86bbc74..f5303a948 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFSheet.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFSheet.cs @@ -21,6 +21,7 @@ limitations under the License. using NPOI.SS; using NPOI.SS.UserModel; using NPOI.SS.Util; +using NPOI.Util; using NPOI.XSSF; using NPOI.XSSF.Model; using NPOI.XSSF.Streaming; @@ -31,6 +32,7 @@ limitations under the License. using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Xml.Linq; using TestCases.HSSF; using TestCases.SS.UserModel; @@ -2072,6 +2074,45 @@ private void AddComments(ICreationHelper helper, ISheet sheet) cell.CellComment = comment; } } - + [Test] + public void TestCoordinate() + { + XSSFWorkbook wb = XSSFTestDataSamples.OpenSampleWorkbook("coordinate.xlsm"); + XSSFSheet sheet = (XSSFSheet)wb.GetSheet("Sheet1"); + + XSSFDrawing drawing = sheet.GetDrawingPatriarch(); + + List shapes = drawing.GetShapes(); + XSSFClientAnchor anchor; + + foreach(var shape in shapes) { + XSSFClientAnchor sa = (XSSFClientAnchor)shape.anchor; + switch(shape.Name) { + case "cxn1": + anchor = sheet.CreateClientAnchor(Units.ToEMU(50), Units.ToEMU(75), Units.ToEMU(125), Units.ToEMU(150)); + break; + case "cxn2": + anchor = sheet.CreateClientAnchor(Units.ToEMU(75), Units.ToEMU(75), Units.ToEMU(150), Units.ToEMU(150)); + break; + case "cxn3": + anchor = sheet.CreateClientAnchor(Units.ToEMU(150), Units.ToEMU(75), Units.ToEMU(225), Units.ToEMU(150)); + break; + case "cxn4": + anchor = sheet.CreateClientAnchor(Units.ToEMU(225), Units.ToEMU(75), Units.ToEMU(300), Units.ToEMU(150)); + break; + default: + Assert.True(false, "Unexpected shape {0}", new object[] { shape.Name }); + return; + } + Assert.IsTrue(sa.From.col == anchor.From.col, /**/"From.col [{0}]({1}={2})", new object[] { shape.Name, sa.From.col, anchor.From.col }); + Assert.IsTrue(sa.From.colOff== anchor.From.colOff, /**/"From.colOff[{0}]({1}={2})", new object[] { shape.Name, sa.From.colOff, anchor.From.colOff }); + Assert.IsTrue(sa.From.row == anchor.From.row, /**/"From.row [{0}]({1}={2})", new object[] { shape.Name, sa.From.row, anchor.From.row }); + Assert.IsTrue(sa.From.rowOff == anchor.From.rowOff, /**/"From.rowOff[{0}]({1}={2})", new object[] { shape.Name, sa.From.rowOff, anchor.From.rowOff }); + Assert.IsTrue(sa.To.col == anchor.To.col, /**/"To.col [{0}]({1}={2})", new object[] { shape.Name, sa.To.col, anchor.To.col }); + Assert.IsTrue(sa.To.colOff == anchor.To.colOff, /**/"To.colOff [{0}]({1}={2})", new object[] { shape.Name, sa.To.colOff, anchor.To.colOff }); + Assert.IsTrue(sa.To.row == anchor.To.row, /**/"To.row [{0}]({1}={2})", new object[] { shape.Name, sa.To.row, anchor.To.row }); + Assert.IsTrue(sa.To.rowOff == anchor.To.rowOff, /**/"To.rowOff [{0}]({1}={2})", new object[] { shape.Name, sa.To.rowOff, anchor.To.rowOff }); + } + } } } \ No newline at end of file diff --git a/testcases/test-data/spreadsheet/TestGetShapes.xlsx b/testcases/test-data/spreadsheet/TestGetShapes.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b0bb66dae9b640e45735293d974e4f0ea35cd2b2 GIT binary patch literal 5116 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 literal 0 HcmV?d00001 From 3ad0b6f8ea56ede7ff72a90b643c5068a8f818b8 Mon Sep 17 00:00:00 2001 From: mino-alpha Date: Mon, 12 Jun 2023 09:16:31 +0900 Subject: [PATCH 7/8] test code in commit 4a1d4518d24833cc35e8f5f45369eb269a663a29,e1c806f8ee25add107a1999ac8bb2535937624b4 --- OpenXmlFormats/Drawing/Text.cs | 24 +-- ooxml/XSSF/UserModel/XSSFDrawing.cs | 12 +- ooxml/XSSF/UserModel/XSSFShape.cs | 14 +- ooxml/XSSF/UserModel/XSSFSimpleShape.cs | 38 +++- openxml4Net/Util/XmlHelper.cs | 17 +- .../ooxml/XSSF/UserModel/TestXSSFShape.cs | 186 ++++++++++++++++-- .../spreadsheet/TestShapeTextWrap.xlsx | Bin 0 -> 9767 bytes 7 files changed, 241 insertions(+), 50 deletions(-) create mode 100644 testcases/test-data/spreadsheet/TestShapeTextWrap.xlsx diff --git a/OpenXmlFormats/Drawing/Text.cs b/OpenXmlFormats/Drawing/Text.cs index c80916c82..640764117 100644 --- a/OpenXmlFormats/Drawing/Text.cs +++ b/OpenXmlFormats/Drawing/Text.cs @@ -513,10 +513,10 @@ public static CT_TextBodyProperties Parse(XmlNode node, XmlNamespaceManager name ctObj.wrapFieldSpecified = node.Attributes["wrap"] != null; if (node.Attributes["wrap"] != null) ctObj.wrapField = (ST_TextWrappingType)Enum.Parse(typeof(ST_TextWrappingType), node.Attributes["wrap"].Value); - ctObj.lIns = XmlHelper.ReadInt(node.Attributes["lIns"]); - ctObj.tIns = XmlHelper.ReadInt(node.Attributes["tIns"]); - ctObj.rIns = XmlHelper.ReadInt(node.Attributes["rIns"]); - ctObj.bIns = XmlHelper.ReadInt(node.Attributes["bIns"]); + ctObj.lIns = XmlHelper.ReadInt(node.Attributes["lIns"], 91440); + ctObj.tIns = XmlHelper.ReadInt(node.Attributes["tIns"], 45720); + ctObj.rIns = XmlHelper.ReadInt(node.Attributes["rIns"], 91440); + ctObj.bIns = XmlHelper.ReadInt(node.Attributes["bIns"], 45720); ctObj.numCol = XmlHelper.ReadInt(node.Attributes["numCol"]); ctObj.spcCol = XmlHelper.ReadInt(node.Attributes["spcCol"]); ctObj.rtlCol = XmlHelper.ReadBool(node.Attributes["rtlCol"]); @@ -567,10 +567,10 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "vert", this.vert.ToString()); if(this.wrapFieldSpecified && this.wrap == ST_TextWrappingType.none) XmlHelper.WriteAttribute(sw, "wrap", this.wrap.ToString()); - XmlHelper.WriteAttribute(sw, "lIns", this.lIns, true); - XmlHelper.WriteAttribute(sw, "tIns", this.tIns, true); - XmlHelper.WriteAttribute(sw, "rIns", this.rIns, true); - XmlHelper.WriteAttribute(sw, "bIns", this.bIns, true); + XmlHelper.WriteAttribute(sw, "lIns", this.lIns, 91440); + XmlHelper.WriteAttribute(sw, "tIns", this.tIns, 45720); + XmlHelper.WriteAttribute(sw, "rIns", this.rIns, 91440); + XmlHelper.WriteAttribute(sw, "bIns", this.bIns, 45720); XmlHelper.WriteAttribute(sw, "numCol", this.numCol); XmlHelper.WriteAttribute(sw, "spcCol", this.spcCol); XmlHelper.WriteAttribute(sw, "rtlCol", this.rtlCol); @@ -887,7 +887,7 @@ public int lIns set { this.lInsField = value; - this.lInsFieldSpecified = true; + this.lInsFieldSpecified = value == 91440 ? false : true; } } @@ -916,7 +916,7 @@ public int tIns set { this.tInsField = value; - this.tInsFieldSpecified = true; + this.tInsFieldSpecified = value == 45720 ? false : true; } } @@ -945,7 +945,7 @@ public int rIns set { this.rInsField = value; - this.rInsFieldSpecified = true; + this.rInsFieldSpecified = value == 91440 ? false : true; } } @@ -974,7 +974,7 @@ public int bIns set { this.bInsField = value; - this.bInsFieldSpecified = true; + this.bInsFieldSpecified = value == 45720 ? false : true; } } diff --git a/ooxml/XSSF/UserModel/XSSFDrawing.cs b/ooxml/XSSF/UserModel/XSSFDrawing.cs index 9f649faeb..d634ff7cb 100644 --- a/ooxml/XSSF/UserModel/XSSFDrawing.cs +++ b/ooxml/XSSF/UserModel/XSSFDrawing.cs @@ -281,7 +281,7 @@ public XSSFSimpleShape CreateSimpleShape(XSSFClientAnchor anchor) ctShape.spPr.xfrm.ext.cy = anchor.height; XSSFSimpleShape shape = new XSSFSimpleShape(this, ctShape); shape.anchor = anchor; - shape.cttwocellanchor = ctAnchor; + shape.cellanchor = ctAnchor; return shape; } @@ -308,7 +308,7 @@ public XSSFConnector CreateConnector(XSSFClientAnchor anchor) XSSFConnector shape = new XSSFConnector(this, ctShape); shape.anchor = anchor; - shape.cttwocellanchor = ctAnchor; + shape.cellanchor = ctAnchor; return shape; } @@ -339,7 +339,7 @@ public XSSFShapeGroup CreateGroup(XSSFClientAnchor anchor) XSSFShapeGroup shape = new XSSFShapeGroup(this, ctGroup); shape.anchor = anchor; - shape.cttwocellanchor = ctAnchor; + shape.cellanchor = ctAnchor; return shape; } @@ -401,7 +401,7 @@ private XSSFGraphicFrame CreateGraphicFrame(XSSFClientAnchor anchor) graphicFrame.Anchor = anchor; graphicFrame.Id = frameId; graphicFrame.Name = "Diagramm" + frameId; - graphicFrame.cttwocellanchor = ctAnchor; + graphicFrame.cellanchor = ctAnchor; return graphicFrame; } @@ -517,7 +517,7 @@ public List GetShapes() if(gShape != null) { gShape.anchor = GetAnchorFromIEGAnchor(anchor); - gShape.cttwocellanchor = (CT_TwoCellAnchor)anchor; + gShape.cellanchor = anchor; lst.Add(gShape); } } @@ -533,7 +533,7 @@ public List GetShapes() if (shape != null) { shape.anchor = GetAnchorFromIEGAnchor(anchor); - shape.cttwocellanchor = (CT_TwoCellAnchor)anchor; + shape.cellanchor = anchor; lst.Add(shape); } } diff --git a/ooxml/XSSF/UserModel/XSSFShape.cs b/ooxml/XSSF/UserModel/XSSFShape.cs index e2eb01b29..4b6180947 100644 --- a/ooxml/XSSF/UserModel/XSSFShape.cs +++ b/ooxml/XSSF/UserModel/XSSFShape.cs @@ -39,7 +39,7 @@ public abstract class XSSFShape:IShape */ protected XSSFDrawing drawing; - protected CT_TwoCellAnchor _twocellanchor; + protected IEG_Anchor IEGanchor; /** * The parent shape, always not-null for shapes in groups */ @@ -79,24 +79,24 @@ public XSSFAnchor GetAnchor() return anchor; } - public CT_TwoCellAnchor cttwocellanchor + public IEG_Anchor cellanchor { get { - if(_twocellanchor == null) { - return parent.cttwocellanchor; + if(IEGanchor == null) { + return parent.cellanchor; } - return _twocellanchor; + return IEGanchor; } set { if(parent == null) { - _twocellanchor = value; + IEGanchor = value; } else { - parent.cttwocellanchor = value; + parent.cellanchor = value; } } } diff --git a/ooxml/XSSF/UserModel/XSSFSimpleShape.cs b/ooxml/XSSF/UserModel/XSSFSimpleShape.cs index 885cce477..82a981f60 100644 --- a/ooxml/XSSF/UserModel/XSSFSimpleShape.cs +++ b/ooxml/XSSF/UserModel/XSSFSimpleShape.cs @@ -112,6 +112,11 @@ protected internal static CT_Shape Prototype() CT_SolidColorFillProperties scfpr = endPr.AddNewSolidFill(); scfpr.AddNewSrgbClr().val = (/*setter*/new byte[] { 0, 0, 0 }); + bodypr.lIns = 91440; //default value + bodypr.tIns = 45720; //default value + bodypr.rIns = 91440; //default value + bodypr.bIns = 45720; //default value + body.AddNewLstStyle(); prototype = shape; @@ -466,7 +471,21 @@ public void SetText(XSSFRichTextString str) CT_RegularTextRun r = p.AddNewR(); CT_TextCharacterProperties rPr = r.AddNewRPr(); rPr.lang = (/*setter*/"en-US"); - + if(ltPr.vertAlign != null) + { + switch(ltPr.vertAlign.val) + { + case ST_VerticalAlignRun.subscript: + rPr.baseline = -25000; + break; + case ST_VerticalAlignRun.superscript: + rPr.baseline = 30000; + break; + default: + rPr.baseline = 0; + break; + } + } ApplyAttributes(ltPr, rPr); r.t = (/*setter*/lt.t); @@ -733,7 +752,7 @@ public double BottomInset CT_TextBodyProperties bodyPr = ctShape.txBody.bodyPr; if (bodyPr != null) { - if (value == -1) + if (value == -1 || value == 3.6) { if (bodyPr.IsSetBIns()) bodyPr.UnsetBIns(); } @@ -761,15 +780,16 @@ public double LeftInset return Units.ToPoints(bodyPr.lIns); } } - // If this attribute is omitted, then a value of 0.05 inches is implied - return 3.6; + // If this attribute is omitted, then a value of 0.1 inches is implied + return 7.2; + ; } set { CT_TextBodyProperties bodyPr = ctShape.txBody.bodyPr; if (bodyPr != null) { - if (value == -1) + if (value == -1 || value == 7.2) { if (bodyPr.IsSetLIns()) bodyPr.UnsetLIns(); } @@ -797,15 +817,15 @@ public double RightInset return Units.ToPoints(bodyPr.rIns); } } - // If this attribute is omitted, then a value of 0.05 inches is implied - return 3.6; + // If this attribute is omitted, then a value of 0.1 inches is implied + return 7.2; } set { CT_TextBodyProperties bodyPr = ctShape.txBody.bodyPr; if (bodyPr != null) { - if (value == -1) + if (value == -1 || value == 7.2) { if (bodyPr.IsSetRIns()) bodyPr.UnsetRIns(); } @@ -840,7 +860,7 @@ public double TopInset CT_TextBodyProperties bodyPr = ctShape.txBody.bodyPr; if (bodyPr != null) { - if (value == -1) + if (value == -1 || value == 3.6) { if (bodyPr.IsSetTIns()) bodyPr.UnsetTIns(); } diff --git a/openxml4Net/Util/XmlHelper.cs b/openxml4Net/Util/XmlHelper.cs index c18f12cc8..a2ca3ac4d 100644 --- a/openxml4Net/Util/XmlHelper.cs +++ b/openxml4Net/Util/XmlHelper.cs @@ -63,10 +63,16 @@ public static T GetEnumValueFromString(string value) throw new ArgumentException("No XmlEnumAttribute code exists for type " + typeof(T).ToString() + " corresponding to value of " + value); } - public static int ReadInt(XmlAttribute attr) + public static int ReadInt(XmlAttribute attr, int? defaultValue = null) { - if (attr == null) + if(attr == null) + { + if(defaultValue != null) + { + return (int)defaultValue; + } return 0; + } int i; if (int.TryParse(attr.Value, out i)) { @@ -335,6 +341,13 @@ public static void WriteAttribute(StreamWriter sw, string attributeName, int val WriteAttribute(sw, attributeName, value.ToString(CultureInfo.InvariantCulture)); } + public static void WriteAttribute(StreamWriter sw, string attributeName, int value, int defaultValue) + { + if(value == defaultValue) + return; + + WriteAttribute(sw, attributeName, value.ToString(CultureInfo.InvariantCulture)); + } public static void WriteAttribute(StreamWriter sw, string attributeName, int value) { WriteAttribute(sw, attributeName, value, false); diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs index ae5c69090..e71ab7cfa 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFShape.cs @@ -1,4 +1,5 @@ -using NPOI.SS.UserModel; +using NPOI.OpenXmlFormats.Dml.Spreadsheet; +using NPOI.SS.UserModel; using NPOI.Util; using NPOI.XSSF; using NPOI.XSSF.UserModel; @@ -10,9 +11,8 @@ namespace TestCases.XSSF.UserModel { [TestFixture] - internal class TestXSSFShape + internal class TestXSSFShapes { - [Test] public void TestShapeLineEndingCapType() { @@ -84,9 +84,105 @@ public void TestGetShapes() foreach(var sp in lstShp) { Debug.WriteLine($"name:[{sp.Name}]"); + switch(sp.Name) + { + case "first": + case "second": + case "third": + case "L01": + case "L02": + case "L03": + break; + default: + Assert.Fail($"name is invalid [{sp.Name}]"); + break; + } } } + [Test] + public void TestShapeTextWrap() + { + XSSFWorkbook wb = XSSFTestDataSamples.OpenSampleWorkbook("TestShapeTextWrap.xlsx"); + XSSFSheet sheet = (XSSFSheet)wb.GetSheet("Sheet1"); + XSSFDrawing drawing = sheet.GetDrawingPatriarch(); + + List lstShp = drawing.GetShapes(); + foreach(var sp in lstShp) + { + if(sp.Name == "shape1") + { + Assert.AreEqual(true, ((XSSFSimpleShape)sp).WordWrap); + XSSFTestDataSamples.WriteOut(wb, "TestShapeTextWrap-1-"); + ((XSSFSimpleShape)sp).WordWrap = false; + XSSFWorkbook rbwb = (XSSFWorkbook)XSSFITestDataProvider.instance.WriteOutAndReadBack(wb); + Assert.AreEqual(false, ((XSSFSimpleShape)sp).WordWrap); + XSSFTestDataSamples.WriteOut(wb, "TestShapeTextWrap-2-"); + } + } + } + + [Test] + public void TestShapeInset() + { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sht0 = (XSSFSheet)wb.CreateSheet(); + XSSFDrawing drawing = (XSSFDrawing)sht0.CreateDrawingPatriarch(); + + //----- create + var ca0 = sht0.CreateClientAnchor(Units.ToEMU(100), Units.ToEMU(100), Units.ToEMU(200), Units.ToEMU(200)); + var sp0 = drawing.CreateSimpleShape(ca0); + sp0.LineStyle = LineStyle.Solid; + sp0.LineStyleColor = 0xff00000; + Assert.AreEqual(7.2, sp0.LeftInset); + Assert.AreEqual(false, sp0.GetCTShape().txBody.bodyPr.IsSetLIns()); + Assert.AreEqual(3.6, sp0.TopInset); + Assert.AreEqual(false, sp0.GetCTShape().txBody.bodyPr.IsSetTIns()); + Assert.AreEqual(7.2, sp0.RightInset); + Assert.AreEqual(false, sp0.GetCTShape().txBody.bodyPr.IsSetRIns()); + Assert.AreEqual(3.6, sp0.BottomInset); + Assert.AreEqual(false, sp0.GetCTShape().txBody.bodyPr.IsSetBIns()); + XSSFTestDataSamples.WriteOut(wb, "TestShapeTextWrap-1-"); + + //----- zero + sp0.LeftInset = 0; + sp0.TopInset = 0; + sp0.RightInset = 0; + sp0.BottomInset = 0; + XSSFWorkbook rbwb1 = (XSSFWorkbook)XSSFITestDataProvider.instance.WriteOutAndReadBack(wb); + var sht1 = rbwb1.GetSheet("sheet0"); + var dw1 = ((XSSFSheet)sht1).GetDrawingPatriarch(); + XSSFSimpleShape sp1 = (XSSFSimpleShape)dw1.GetShapes()[0]; + Assert.AreEqual(0, sp1.LeftInset); + Assert.AreEqual(true, sp1.GetCTShape().txBody.bodyPr.IsSetLIns()); + Assert.AreEqual(0, sp1.TopInset); + Assert.AreEqual(true, sp1.GetCTShape().txBody.bodyPr.IsSetTIns()); + Assert.AreEqual(0, sp1.RightInset); + Assert.AreEqual(true, sp1.GetCTShape().txBody.bodyPr.IsSetRIns()); + Assert.AreEqual(0, sp1.BottomInset); + Assert.AreEqual(true, sp1.GetCTShape().txBody.bodyPr.IsSetBIns()); + XSSFTestDataSamples.WriteOut(rbwb1, "TestShapeTextWrap-2-"); + + //----- others + sp1.LeftInset = 3.6; + sp1.TopInset = 1.8; + sp1.RightInset = 3.6; + sp1.BottomInset = 1.8; + XSSFWorkbook rbwb2 = (XSSFWorkbook)XSSFITestDataProvider.instance.WriteOutAndReadBack(rbwb1); + var sht2 = rbwb2.GetSheet("sheet0"); + var dw2 = ((XSSFSheet)sht2).GetDrawingPatriarch(); + XSSFSimpleShape sp2 = (XSSFSimpleShape)dw2.GetShapes()[0]; + Assert.AreEqual(3.6, sp2.LeftInset); + Assert.AreEqual(true, sp2.GetCTShape().txBody.bodyPr.IsSetLIns()); + Assert.AreEqual(1.8, sp2.TopInset); + Assert.AreEqual(true, sp2.GetCTShape().txBody.bodyPr.IsSetTIns()); + Assert.AreEqual(3.6, sp2.RightInset); + Assert.AreEqual(true, sp2.GetCTShape().txBody.bodyPr.IsSetRIns()); + Assert.AreEqual(1.8, sp2.BottomInset); + Assert.AreEqual(true, sp2.GetCTShape().txBody.bodyPr.IsSetBIns()); + XSSFTestDataSamples.WriteOut(rbwb2, "TestShapeTextWrap-3-"); + } + [Test] public void TestLockWithSheet() { @@ -98,15 +194,15 @@ public void TestLockWithSheet() XSSFClientAnchor ca0; ca0 = sheet.CreateClientAnchor(Units.ToEMU(100), Units.ToEMU(100), Units.ToEMU(200), Units.ToEMU(200)); XSSFSimpleShape shp0 = drawing.CreateSimpleShape(ca0); - shp0.Name = "S00"; - shp0.cttwocellanchor.clientData.fLocksWithSheet = true; + shp0.Name = "S00"; + shp0.cellanchor.clientData.fLocksWithSheet = true; // connector shape XSSFClientAnchor ca1; ca1 = sheet.CreateClientAnchor(Units.ToEMU(250), Units.ToEMU(250), Units.ToEMU(350), Units.ToEMU(350)); XSSFConnector shp1 = drawing.CreateConnector(ca1); shp1.Name = "L00"; - shp1.cttwocellanchor.clientData.fLocksWithSheet = true; + shp1.cellanchor.clientData.fLocksWithSheet = true; XSSFTestDataSamples.WriteOut(wb, "TestLockWithSheet1-"); @@ -121,15 +217,15 @@ public void TestLockWithSheet() { case "S00": case "L00": - Assert.AreEqual(sp.cttwocellanchor.clientData.fLocksWithSheet, true, "shape name:[{0}]", new object[] { sp.Name }); + Assert.AreEqual(sp.cellanchor.clientData.fLocksWithSheet, true, "shape name:[{0}]", new object[] { sp.Name }); break; default: break; } } - shp0.cttwocellanchor.clientData.fLocksWithSheet = false; - shp1.cttwocellanchor.clientData.fLocksWithSheet = false; + shp0.cellanchor.clientData.fLocksWithSheet = false; + shp1.cellanchor.clientData.fLocksWithSheet = false; XSSFTestDataSamples.WriteOut(wb, "TestLockWithSheet2-"); @@ -144,7 +240,7 @@ public void TestLockWithSheet() { case "S00": case "L00": - Assert.AreEqual(sp.cttwocellanchor.clientData.fLocksWithSheet, false, "shape name:[{0}]", new object[] { sp.Name }); + Assert.AreEqual(sp.cellanchor.clientData.fLocksWithSheet, false, "shape name:[{0}]", new object[] { sp.Name }); break; default: break; @@ -171,7 +267,7 @@ public void TestGroupLockWithSheet() ca1 = new XSSFChildAnchor(Units.ToEMU(100), Units.ToEMU(200), Units.ToEMU(200), Units.ToEMU(100)); XSSFConnector shp1 = grp1.CreateConnector( ca1 ); shp1.Name = "L01"; - shp1.cttwocellanchor.clientData.fLocksWithSheet = true; + shp1.cellanchor.clientData.fLocksWithSheet = true; //----- second group XSSFChildGroupAnchor ganchor2; @@ -184,7 +280,7 @@ public void TestGroupLockWithSheet() ca2 = new XSSFChildAnchor(Units.ToEMU(110), Units.ToEMU(110), Units.ToEMU(190), Units.ToEMU(190)); XSSFConnector shp2 = grp2.CreateConnector(ca2); shp2.Name = "L02"; - shp2.cttwocellanchor.clientData.fLocksWithSheet = true; + shp2.cellanchor.clientData.fLocksWithSheet = true; //----- third group XSSFChildGroupAnchor ganchor3; @@ -197,11 +293,73 @@ public void TestGroupLockWithSheet() ca3 = new XSSFChildAnchor(Units.ToEMU(120), Units.ToEMU(150), Units.ToEMU(180), Units.ToEMU(150)); XSSFConnector shp3 = grp3.CreateConnector(ca3); shp3.Name = "L03"; - shp3.cttwocellanchor.clientData.fLocksWithSheet = false; + shp3.cellanchor.clientData.fLocksWithSheet = false; - Assert.AreEqual(shp1.cttwocellanchor.clientData.fLocksWithSheet, false); + Assert.AreEqual(shp1.cellanchor.clientData.fLocksWithSheet, false); XSSFTestDataSamples.WriteOut(wb, "TestGroupLockWithSheet"); } + + [Test] + public void TestRecursiveGroup() + { + XSSFWorkbook wb = new XSSFWorkbook(); + XSSFSheet sheet = (XSSFSheet)wb.CreateSheet(); + XSSFDrawing drawing = (XSSFDrawing)sheet.CreateDrawingPatriarch(); + + //----- first group + XSSFClientAnchor ganchor1; + ganchor1 = sheet.CreateClientAnchor(Units.ToEMU(100), Units.ToEMU(400), Units.ToEMU(400), Units.ToEMU(100)); + ganchor1.AnchorType = AnchorType.DontMoveAndResize; + XSSFShapeGroup grp1= drawing.CreateGroup( ganchor1 ); + grp1.Name = "G00"; + + // simple shape + XSSFChildAnchor ca1; + ca1 = new XSSFChildAnchor(Units.ToEMU(100), Units.ToEMU(200), Units.ToEMU(200), Units.ToEMU(100)); + XSSFSimpleShape shp1 = grp1.CreateSimpleShape( ca1 ); + shp1.Name = "S00"; + + // connector shape + XSSFChildAnchor ca2; + ca2 = new XSSFChildAnchor(Units.ToEMU(100), Units.ToEMU(200), Units.ToEMU(200), Units.ToEMU(100)); + XSSFConnector shp2 = grp1.CreateConnector( ca2 ); + shp2.Name = "L00"; + shp2.cellanchor.clientData.fLocksWithSheet = true; + + int x = 300; + int y = 300; + int cx = 310; + int cy = 310; + XSSFShapeGroup grp = grp1; + for(var ct = 1; ct < 5; ct++) + { + //----- second group + XSSFChildGroupAnchor ganchor; + ganchor = new XSSFChildGroupAnchor(Units.ToEMU(x), Units.ToEMU(y), Units.ToEMU(cx), Units.ToEMU(cy)); + grp = grp.CreateGroup( ganchor ); + grp.Name = $"G{ct}"; + + // simple shape + XSSFChildAnchor ca; + ca = new XSSFChildAnchor(Units.ToEMU(x), Units.ToEMU(x), Units.ToEMU(cx), Units.ToEMU(cy)); + XSSFSimpleShape Sshp = grp.CreateSimpleShape( ca ); + Sshp.Name = $"S{ct:00}"; + Sshp.LineStyle = LineStyle.Solid; + Sshp.LineStyleColor = 0x00FF00; + + // connector shape + ca = new XSSFChildAnchor(Units.ToEMU(x), Units.ToEMU(y), Units.ToEMU(cx), Units.ToEMU(cy)); + XSSFConnector Cshp = grp.CreateConnector( ca ); + Cshp.Name = $"L{ct:00}"; + Cshp.cellanchor.clientData.fLocksWithSheet = true; + + x += 25; + y += 25; + cx += 25; + cy += 25; + } + XSSFTestDataSamples.WriteOut(wb, "TestRecursiveGroup"); + } } } diff --git a/testcases/test-data/spreadsheet/TestShapeTextWrap.xlsx b/testcases/test-data/spreadsheet/TestShapeTextWrap.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..15deeec6c4fb074580804641f586e1e444b4375e GIT binary patch literal 9767 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 YRg#5+gv^g~Cdhy(h$!Iyp@{(h56-6bvj6}9 literal 0 HcmV?d00001 From 3417e43c5d3b21ea545ef2fb00d3c43e9f2e02ec Mon Sep 17 00:00:00 2001 From: mino-alpha Date: Mon, 12 Jun 2023 12:48:42 +0900 Subject: [PATCH 8/8] fix: Drawing shape (68e6d02) --- .../ooxml/XSSF/UserModel/TestXSSFSimpleShape.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFSimpleShape.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFSimpleShape.cs index 47dcbfaf1..63718388c 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFSimpleShape.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFSimpleShape.cs @@ -121,21 +121,21 @@ public void TestXSSFTextParagraph() shape.BottomInset = (/*setter*/-1); Assert.AreEqual(3.6, shape.BottomInset, 0.01); - Assert.AreEqual(3.6, shape.LeftInset, 0.01); + Assert.AreEqual(7.2, shape.LeftInset, 0.01); shape.LeftInset = (/*setter*/12.31); Assert.AreEqual(12.31, shape.LeftInset, 0.01); shape.LeftInset = (/*setter*/-1); - Assert.AreEqual(3.6, shape.LeftInset, 0.01); + Assert.AreEqual(7.2, shape.LeftInset, 0.01); shape.LeftInset = (/*setter*/-1); - Assert.AreEqual(3.6, shape.LeftInset, 0.01); + Assert.AreEqual(7.2, shape.LeftInset, 0.01); - Assert.AreEqual(3.6, shape.RightInset, 0.01); + Assert.AreEqual(7.2, shape.RightInset, 0.01); shape.RightInset = (/*setter*/13.31); Assert.AreEqual(13.31, shape.RightInset, 0.01); shape.RightInset = (/*setter*/-1); - Assert.AreEqual(3.6, shape.RightInset, 0.01); + Assert.AreEqual(7.2, shape.RightInset, 0.01); shape.RightInset = (/*setter*/-1); - Assert.AreEqual(3.6, shape.RightInset, 0.01); + Assert.AreEqual(7.2, shape.RightInset, 0.01); Assert.AreEqual(3.6, shape.TopInset, 0.01); shape.TopInset = (/*setter*/23.31);