diff --git a/OpenXmlFormats/Drawing/BaseTypes.cs b/OpenXmlFormats/Drawing/BaseTypes.cs index 2531d1c97..f1adcbdf3 100644 --- a/OpenXmlFormats/Drawing/BaseTypes.cs +++ b/OpenXmlFormats/Drawing/BaseTypes.cs @@ -48,6 +48,7 @@ public string uri set { this.uriField = value; + this.uriSpecifiedField = !string.IsNullOrEmpty(this.uriField); } } @@ -2114,6 +2115,12 @@ public List ext this.extField = value; } } + + public CT_OfficeArtExtension AddNewExt() + { + extField.Add(new CT_OfficeArtExtension()); + return extField[extField.Count - 1]; + } } [Serializable] diff --git a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs index c8940ee92..e7abea485 100644 --- a/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs +++ b/OpenXmlFormats/Drawing/SpreadsheetDrawing.cs @@ -173,6 +173,12 @@ public bool hiddenSpecified { get { return (null != hiddenField); } } + + public CT_OfficeArtExtensionList AddNewExtLst() + { + this.extLstField = new CT_OfficeArtExtensionList(); + return this.extLst; + } } [Serializable] [System.ComponentModel.DesignerCategoryAttribute("code")] @@ -627,6 +633,13 @@ public CT_SolidColorFillProperties AddNewSolidFill() this.solidFillField = new CT_SolidColorFillProperties(); return this.solidFillField; } + + public CT_BlipFillProperties AddNewBlipFill() + { + this.blipFillField = new CT_BlipFillProperties(); + return this.blipFillField; + } + public CT_CustomGeometry2D AddNewCustGeom() { this.custGeomField = new CT_CustomGeometry2D(); @@ -1317,7 +1330,7 @@ public class CT_Drawing { private List cellAnchors = new List(); //private List absoluteCellAnchors = new List(); - + private bool inAlternateContent = false; public CT_TwoCellAnchor AddNewTwoCellAnchor() { CT_TwoCellAnchor anchor = new CT_TwoCellAnchor(); @@ -1343,10 +1356,21 @@ public void Save(Stream stream) { sw.Write(""); sw.Write(""); + if(inAlternateContent) + { + sw.Write(""); + sw.Write(""); + } foreach (IEG_Anchor anchor in this.cellAnchors) { anchor.Write(sw); } + if(inAlternateContent) + { + sw.Write(""); + sw.Write(""); + sw.Write(""); + } sw.Write(""); } } @@ -1399,11 +1423,18 @@ public static CT_Drawing Parse(XmlDocument xmldoc, XmlNamespaceManager namespace { return new CT_Drawing(); } - XmlNodeList cellanchorNodes = root.SelectNodes("descendant::xdr:oneCellAnchor|descendant::xdr:twoCellAnchor|descendant::xdr:absCellAnchor", namespaceManager); + //XmlNodeList childNodes = root.SelectNodes("descendant::xdr:oneCellAnchor|descendant::xdr:twoCellAnchor|descendant::xdr:absCellAnchor", namespaceManager); + XmlNodeList childNodes = xmldoc.SelectNodes("/xdr:wsDr/*", namespaceManager); CT_Drawing ctDrawing = new CT_Drawing(); - foreach (XmlNode node in cellanchorNodes) + // handle with-/out AlternateContent wrappers + foreach (XmlNode node in childNodes) { - if (node.LocalName == "twoCellAnchor") + if(node.LocalName == "AlternateContent") + { + ctDrawing.inAlternateContent = true; + ctDrawing.cellAnchors.Add(ParseAlternateContent(node, namespaceManager)); + } + else if (node.LocalName == "twoCellAnchor") { CT_TwoCellAnchor twoCellAnchor = CT_TwoCellAnchor.Parse(node, namespaceManager); ctDrawing.cellAnchors.Add(twoCellAnchor); @@ -1421,6 +1452,40 @@ public static CT_Drawing Parse(XmlDocument xmldoc, XmlNamespaceManager namespace } return ctDrawing; } + + private static IEG_Anchor ParseAlternateContent(XmlNode node, XmlNamespaceManager namespaceManager) + { + IEG_Anchor anchor = null; + if(node.ChildNodes.Count == 0) + return null; + if(node.ChildNodes[0].LocalName == "Choice") + { + foreach(XmlNode cnode in node.ChildNodes[0].ChildNodes) + { + if(cnode.LocalName == "twoCellAnchor") + { + anchor = CT_TwoCellAnchor.Parse(cnode, namespaceManager); + } + else if(cnode.LocalName == "oneCellAnchor") + { + anchor = CT_OneCellAnchor.Parse(cnode, namespaceManager); + } + else if(cnode.LocalName == "absCellAnchor") + { + anchor = CT_AbsoluteCellAnchor.Parse(cnode, namespaceManager); + } + else + { + throw new InvalidOperationException($"invalid localname {cnode.LocalName}"); + } + } + } + else + { + throw new NotImplementedException($"invalid localname {node.ChildNodes[0].LocalName}"); + } + return anchor; + } } [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")] @@ -1950,7 +2015,7 @@ internal static CT_TwoCellAnchor Parse(XmlNode node, XmlNamespaceManager namespa [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")] - public class CT_Connector // empty interface: EG_ObjectChoices + public class CT_Connector: XmlObject // empty interface: EG_ObjectChoices { string macroField; bool fPublishedField; @@ -1978,6 +2043,7 @@ public static CT_Connector Parse(XmlNode node, XmlNamespaceManager namespaceMana else if (childNode.LocalName == "style") ctObj.style = CT_ShapeStyle.Parse(childNode, namespaceManager); } + ctObj.Node = node; return ctObj; } diff --git a/OpenXmlFormats/Drawing/SpreadsheetPicture.cs b/OpenXmlFormats/Drawing/SpreadsheetPicture.cs index 9c204d89f..442d24fec 100644 --- a/OpenXmlFormats/Drawing/SpreadsheetPicture.cs +++ b/OpenXmlFormats/Drawing/SpreadsheetPicture.cs @@ -10,7 +10,7 @@ namespace NPOI.OpenXmlFormats.Dml.Spreadsheet [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")] - public class CT_Picture // empty interface: EG_ObjectChoices + public class CT_Picture : XmlObject // empty interface: EG_ObjectChoices { private CT_PictureNonVisual nvPicPrField = new CT_PictureNonVisual(); // draw-ssdraw: 1..1 private CT_BlipFillProperties blipFillField = new CT_BlipFillProperties(); // draw-ssdraw: 1..1 @@ -37,6 +37,7 @@ public static CT_Picture Parse(XmlNode node, XmlNamespaceManager namespaceManage else if (childNode.LocalName == "style") ctObj.style = CT_ShapeStyle.Parse(childNode, namespaceManager); } + ctObj.Node = node; return ctObj; } @@ -167,7 +168,7 @@ public void Set(CT_Picture pict) // see same class in different name space in Picture.cs [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")] - public class CT_PictureNonVisual + public class CT_PictureNonVisual: XmlObject { private CT_NonVisualDrawingProps cNvPrField = new CT_NonVisualDrawingProps(); // 1..1 @@ -185,6 +186,7 @@ public static CT_PictureNonVisual Parse(XmlNode node, XmlNamespaceManager namesp else if (childNode.LocalName == "cNvPicPr") ctObj.cNvPicPr = CT_NonVisualPictureProperties.Parse(childNode, namespaceManager); } + ctObj.Node = node; return ctObj; } diff --git a/OpenXmlFormats/Drawing/spreadsheetShape.cs b/OpenXmlFormats/Drawing/spreadsheetShape.cs index b0cb9cbc7..74aabfc77 100644 --- a/OpenXmlFormats/Drawing/spreadsheetShape.cs +++ b/OpenXmlFormats/Drawing/spreadsheetShape.cs @@ -11,9 +11,16 @@ namespace NPOI.OpenXmlFormats.Dml.Spreadsheet { + /// + /// Hold xml data + /// + public class XmlObject + { + public XmlNode Node { get; set; } + } [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")] - public class CT_Shape // empty interface: EG_ObjectChoices + public class CT_Shape : XmlObject // empty interface: EG_ObjectChoices { private CT_ShapeNonVisual nvSpPrField; private CT_ShapeProperties spPrField; @@ -57,6 +64,7 @@ public static CT_Shape Parse(XmlNode node, XmlNamespaceManager namespaceManager) else if (childNode.LocalName == "style") ctObj.style = CT_ShapeStyle.Parse(childNode, namespaceManager); } + ctObj.Node = node; return ctObj; } @@ -179,7 +187,7 @@ public bool fPublished [System.ComponentModel.DesignerCategory("code")] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")] - public class CT_TextBody + public class CT_TextBody : XmlObject { private CT_TextBodyProperties bodyPrField; @@ -203,6 +211,7 @@ public static CT_TextBody Parse(XmlNode node, XmlNamespaceManager namespaceManag else if (childNode.LocalName == "p") ctObj.p.Add(CT_TextParagraph.Parse(childNode, namespaceManager)); } + ctObj.Node = node; return ctObj; } @@ -441,7 +450,7 @@ public CT_FontReference fontRef [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")] - public class CT_ShapeNonVisual + public class CT_ShapeNonVisual : XmlObject { private CT_NonVisualDrawingProps cNvPrField; private CT_NonVisualDrawingShapeProps cNvSpPrField; @@ -491,6 +500,7 @@ public static CT_ShapeNonVisual Parse(XmlNode node, XmlNamespaceManager namespac else if (childNode.LocalName == "cNvSpPr") ctObj.cNvSpPr = CT_NonVisualDrawingShapeProps.Parse(childNode, namespaceManager); } + ctObj.Node = node; return ctObj; } @@ -595,7 +605,7 @@ public bool txBox } [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")] - public class CT_GroupShape + public class CT_GroupShape : XmlObject { CT_GroupShapeProperties grpSpPrField; CT_GroupShapeNonVisual nvGrpSpPrField; @@ -646,7 +656,7 @@ public CT_Picture AddNewPic() pictures.Add(pic); return pic; } - public CT_GroupShape AddNewGroup() + public CT_GroupShape AddNewGroupShape() { var group = new CT_GroupShape(); groups.Add(group); @@ -734,6 +744,7 @@ public static CT_GroupShape Parse(XmlNode node, XmlNamespaceManager namespaceMan ctObj.groups.Add(group); } } + ctObj.Node = node; return ctObj; } @@ -782,7 +793,7 @@ internal void Write(StreamWriter sw, string nodeName) [Serializable] [XmlType(Namespace = "http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")] - public class CT_GroupShapeNonVisual + public class CT_GroupShapeNonVisual : XmlObject { CT_NonVisualDrawingProps cNvPrField; CT_NonVisualGroupDrawingShapeProps cNvGrpSpPrField; @@ -798,6 +809,7 @@ public static CT_GroupShapeNonVisual Parse(XmlNode node, XmlNamespaceManager nam else if (childNode.LocalName == "cNvGrpSpPr") ctObj.cNvGrpSpPr = CT_NonVisualGroupDrawingShapeProps.Parse(childNode, namespaceManager); } + ctObj.Node = node; return ctObj; } diff --git a/OpenXmlFormats/Spreadsheet/Sheet.cs b/OpenXmlFormats/Spreadsheet/Sheet.cs index b51974cbf..eb49c38bd 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet.cs @@ -17,6 +17,7 @@ namespace NPOI.OpenXmlFormats.Spreadsheet using System.Xml; using NPOI.OpenXml4Net.Util; using NPOI.OpenXml4Net.OPC; + using CT_Marker = NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_Marker; public enum ST_SmartTagShow { @@ -8278,6 +8279,7 @@ internal void Write(StreamWriter sw, string nodeName) [XmlType(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main")] public class CT_OleObject { + private CT_ObjectPr objectPrField; private string progIdField; @@ -8294,13 +8296,28 @@ public class CT_OleObject private uint shapeIdField; private string idField; + // TODO: Parse CT_ObjectPr xml node + //private CT_ObjectPr objectPr; public CT_OleObject() { + this.objectPrField = new CT_ObjectPr(); this.dvAspectField = ST_DvAspect.DVASPECT_CONTENT; this.autoLoadField = false; } + public CT_ObjectPr objectPr + { + get + { + return this.objectPrField; + } + set + { + this.objectPrField = value; + } + } + public string progId { get @@ -8401,6 +8418,18 @@ public string id this.idField = value; } } + + public bool IsSetId() + { + return !string.IsNullOrEmpty(this.idField); + } + + public string Any + { + get; + set; + } + public static CT_OleObject Parse(XmlNode node, XmlNamespaceManager namespaceManager) { if (node == null) @@ -8415,9 +8444,22 @@ public static CT_OleObject Parse(XmlNode node, XmlNamespaceManager namespaceMana ctObj.autoLoad = XmlHelper.ReadBool(node.Attributes["autoLoad"]); ctObj.shapeId = XmlHelper.ReadUInt(node.Attributes["shapeId"]); ctObj.id = XmlHelper.ReadString(node.Attributes["id", PackageNamespaces.SCHEMA_RELATIONSHIPS]); + ctObj.Any = node.InnerXml; + foreach(XmlNode childNode in node.ChildNodes) + { + if(childNode.LocalName == "objectPr") + ctObj.objectPrField = CT_ObjectPr.Parse(childNode, namespaceManager); + } return ctObj; } + internal void WriteFallback(StreamWriter sw) + { + sw.Write(""); + sw.Write($""); + sw.Write(""); + } + internal void Write(StreamWriter sw, string nodeName) { sw.Write(string.Format("<{0}", nodeName)); @@ -8428,9 +8470,283 @@ internal void Write(StreamWriter sw, string nodeName) XmlHelper.WriteAttribute(sw, "autoLoad", this.autoLoad); XmlHelper.WriteAttribute(sw, "shapeId", this.shapeId); XmlHelper.WriteAttribute(sw, "r:id", this.id); - sw.Write("/>"); + sw.Write(">"); + if(objectPrField!=null) + { + this.objectPrField.Write(sw, "objectPr"); + } + sw.Write("", nodeName); + } + + } + [Serializable] + [XmlType(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main")] + public class CT_ObjectPr + { + + private CT_ObjectAnchor anchorField; + + private bool lockedField; + + private bool defaultSizeField; + + private bool printField; + + private bool disabledField; + + private bool uiObjectField; + + private bool autoFillField; + + private bool autoLineField; + + private bool autoPictField; + + private string macroField; + + private string altTextField; + + private bool ddeField; + + private string idField; + + public static CT_ObjectPr Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + if(node == null) + return null; + CT_ObjectPr ctObj = new CT_ObjectPr(); + ctObj.lockedField = XmlHelper.ReadBool(node.Attributes["locked"], true); + ctObj.defaultSizeField = XmlHelper.ReadBool(node.Attributes["defaultSize"], true); + ctObj.printField = XmlHelper.ReadBool(node.Attributes["print"], true); + ctObj.disabledField = XmlHelper.ReadBool(node.Attributes["disabled"], false); + ctObj.uiObjectField = XmlHelper.ReadBool(node.Attributes["uiObject"], false); + ctObj.autoFillField = XmlHelper.ReadBool(node.Attributes["autoFill"], true); + ctObj.autoLineField = XmlHelper.ReadBool(node.Attributes["autoLine"], true); + ctObj.autoPictField = XmlHelper.ReadBool(node.Attributes["autoPict"], true); + ctObj.ddeField = XmlHelper.ReadBool(node.Attributes["dde"], false); + ctObj.altTextField = XmlHelper.ReadString(node.Attributes["altText"]); + ctObj.macroField = XmlHelper.ReadString(node.Attributes["macro"]); + ctObj.id = XmlHelper.ReadString(node.Attributes["id", PackageNamespaces.SCHEMA_RELATIONSHIPS]); + + foreach(XmlNode childNode in node.ChildNodes) + { + if(childNode.LocalName == "anchor") + ctObj.anchorField = CT_ObjectAnchor.Parse(childNode, namespaceManager); + } + return ctObj; + } + + internal void Write(StreamWriter sw, string nodeName) + { + sw.Write(string.Format("<{0}", nodeName)); + XmlHelper.WriteAttribute(sw, "locked", this.locked, false, true); + XmlHelper.WriteAttribute(sw, "defaultSize", this.defaultSize, false, true); + XmlHelper.WriteAttribute(sw, "print", this.print, false, true); + XmlHelper.WriteAttribute(sw, "disabled", this.disabled, false, false); + XmlHelper.WriteAttribute(sw, "uiObject", this.uiObject, false, false); + XmlHelper.WriteAttribute(sw, "autoFill", this.autoFill, false, true); + XmlHelper.WriteAttribute(sw, "autoLine", this.autoLine, false, true); + XmlHelper.WriteAttribute(sw, "autoPict", this.autoPict, false, true); + XmlHelper.WriteAttribute(sw, "dde", this.dde, false, false); + XmlHelper.WriteAttribute(sw, "altText", this.altText); + XmlHelper.WriteAttribute(sw, "macro", this.macro); + XmlHelper.WriteAttribute(sw, "r:id", this.id); + sw.Write(">"); + if(anchorField!=null) + { + this.anchorField.Write(sw, "anchor"); + } + sw.Write("", nodeName); + } + + public CT_ObjectPr() + { + this.anchorField = new CT_ObjectAnchor(); + this.lockedField = true; + this.defaultSizeField = true; + this.printField = true; + this.disabledField = false; + this.uiObjectField = false; + this.autoFillField = true; + this.autoLineField = true; + this.autoPictField = true; + this.ddeField = false; + } + + public CT_ObjectAnchor anchor + { + get + { + return this.anchorField; + } + set + { + this.anchorField = value; + } + } + + [DefaultValue(true)] + public bool locked + { + get + { + return this.lockedField; + } + set + { + this.lockedField = value; + } + } + + [DefaultValue(true)] + public bool defaultSize + { + get + { + return this.defaultSizeField; + } + set + { + this.defaultSizeField = value; + } + } + + [DefaultValue(true)] + public bool print + { + get + { + return this.printField; + } + set + { + this.printField = value; + } + } + + [DefaultValue(false)] + public bool disabled + { + get + { + return this.disabledField; + } + set + { + this.disabledField = value; + } + } + + [DefaultValue(false)] + public bool uiObject + { + get + { + return this.uiObjectField; + } + set + { + this.uiObjectField = value; + } + } + + [DefaultValue(true)] + public bool autoFill + { + get + { + return this.autoFillField; + } + set + { + this.autoFillField = value; + } + } + + [DefaultValue(true)] + public bool autoLine + { + get + { + return this.autoLineField; + } + set + { + this.autoLineField = value; + } + } + + [DefaultValue(true)] + public bool autoPict + { + get + { + return this.autoPictField; + } + set + { + this.autoPictField = value; + } + } + + public string macro + { + get + { + return this.macroField; + } + set + { + this.macroField = value; + } + } + + public string altText + { + get + { + return this.altTextField; + } + set + { + this.altTextField = value; + } } + [DefaultValue(false)] + public bool dde + { + get + { + return this.ddeField; + } + set + { + this.ddeField = value; + } + } + + public string id + { + get + { + return this.idField; + } + set + { + this.idField = value; + } + } + + #region Clone method + /// + /// Create a clone of this CT_ObjectPr object + /// + public virtual CT_ObjectPr Clone() + { + return ((CT_ObjectPr) (this.MemberwiseClone())); + } + #endregion } public enum ST_DvAspect @@ -10966,6 +11282,7 @@ public List customPr [XmlType(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main")] public class CT_OleObjects { + private bool inAlternateContent = false; public static CT_OleObjects Parse(XmlNode node, XmlNamespaceManager namespaceManager) { if (node == null) @@ -10974,7 +11291,13 @@ public static CT_OleObjects Parse(XmlNode node, XmlNamespaceManager namespaceMan ctObj.oleObject = new List(); foreach (XmlNode childNode in node.ChildNodes) { - if (childNode.LocalName == "oleObject") + ctObj.inAlternateContent = true; + if(childNode.LocalName == "AlternateContent") + { + ctObj.inAlternateContent = true; + ctObj.oleObject.Add(CT_OleObject.Parse(childNode.ChildNodes[0].ChildNodes[0], namespaceManager)); + } + else if (childNode.LocalName == "oleObject") ctObj.oleObject.Add(CT_OleObject.Parse(childNode, namespaceManager)); } if (ctObj.oleObject.Count == 0) @@ -10988,9 +11311,26 @@ internal void Write(StreamWriter sw, string nodeName) sw.Write(">"); if (this.oleObject != null) { + if(inAlternateContent) + { + sw.Write(""); + } foreach (CT_OleObject x in this.oleObject) { + if(inAlternateContent) + { + sw.Write(""); + } x.Write(sw, "oleObject"); + if(inAlternateContent) + { + sw.Write(""); + x.WriteFallback(sw); + } + } + if(inAlternateContent) + { + sw.Write(""); } } sw.Write(string.Format("", nodeName)); @@ -11014,6 +11354,17 @@ public List oleObject this.oleObjectField = value; } } + + public CT_OleObject AddNewOleObject() + { + if(this.oleObjectField==null) + { + this.oleObjectField = new List(); + } + CT_OleObject obj = new CT_OleObject(); + this.oleObjectField.Add(obj); + return obj; + } } [Serializable] @@ -11067,4 +11418,119 @@ public List control } } } + [Serializable] + [XmlType(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main")] + [XmlRoot(Namespace = "http://schemas.openxmlformats.org/spreadsheetml/2006/main", IsNullable = true)] + public partial class CT_ObjectAnchor + { + + private CT_Marker fromField; + + private CT_Marker toField; + + private bool moveWithCellsField; + + private bool sizeWithCellsField; + + public static CT_ObjectAnchor Parse(XmlNode node, XmlNamespaceManager namespaceManager) + { + if(node == null) + return null; + CT_ObjectAnchor ctObj = new CT_ObjectAnchor(); + ctObj.moveWithCellsField = XmlHelper.ReadBool(node.Attributes["moveWithCells"], false); + ctObj.sizeWithCellsField = XmlHelper.ReadBool(node.Attributes["sizeWithCells"], false); + + foreach(XmlNode childNode in node.ChildNodes) + { + if(childNode.LocalName == "from") + ctObj.fromField = CT_Marker.Parse(childNode, namespaceManager); + else if(childNode.LocalName == "to") + ctObj.toField = CT_Marker.Parse(childNode, namespaceManager); + } + return ctObj; + } + + internal void Write(StreamWriter sw, string nodeName) + { + sw.Write(string.Format("<{0}", nodeName)); + XmlHelper.WriteAttribute(sw, "moveWithCells", moveWithCells, false, false); + XmlHelper.WriteAttribute(sw, "sizeWithCells", sizeWithCells, false, false); + sw.Write(">"); + if(this.fromField != null) + { + this.fromField.Write(sw, "from"); + } + if(this.toField != null) + { + this.toField.Write(sw, "to"); + } + sw.Write(string.Format("", nodeName)); + } + + public CT_ObjectAnchor() + { + this.toField = new CT_Marker(); + this.fromField = new CT_Marker(); + this.moveWithCellsField = false; + this.sizeWithCellsField = false; + } + + public CT_Marker from + { + get + { + return this.fromField; + } + set + { + this.fromField = value; + } + } + + public CT_Marker to + { + get + { + return this.toField; + } + set + { + this.toField = value; + } + } + + public bool moveWithCells + { + get + { + return this.moveWithCellsField; + } + set + { + this.moveWithCellsField = value; + } + } + + public bool sizeWithCells + { + get + { + return this.sizeWithCellsField; + } + set + { + this.sizeWithCellsField = value; + } + } + + #region Clone method + /// + /// Create a clone of this CT_ObjectAnchor object + /// + public virtual CT_ObjectAnchor Clone() + { + return ((CT_ObjectAnchor) (this.MemberwiseClone())); + } + #endregion + } } diff --git a/OpenXmlFormats/Spreadsheet/Sheet/CT_Worksheet.cs b/OpenXmlFormats/Spreadsheet/Sheet/CT_Worksheet.cs index 007175d33..685b25697 100644 --- a/OpenXmlFormats/Spreadsheet/Sheet/CT_Worksheet.cs +++ b/OpenXmlFormats/Spreadsheet/Sheet/CT_Worksheet.cs @@ -197,6 +197,7 @@ internal void Write(Stream stream, bool leaveOpen) { sw.Write(""); sw.Write(""); @@ -1032,6 +1033,15 @@ public CT_IgnoredErrors AddNewIgnoredErrors() return this.ignoredErrorsField; } + public bool IsSetOleObjects() + { + return this.oleObjectsField!=null; + } + public CT_OleObjects AddNewOleObjects() + { + this.oleObjectsField = new CT_OleObjects(); + return this.oleObjectsField; + } } } diff --git a/main/HSSF/UserModel/HSSFObjectData.cs b/main/HSSF/UserModel/HSSFObjectData.cs index 267171290..3f539b620 100644 --- a/main/HSSF/UserModel/HSSFObjectData.cs +++ b/main/HSSF/UserModel/HSSFObjectData.cs @@ -25,6 +25,7 @@ namespace NPOI.HSSF.UserModel using NPOI.Util; using NPOI.POIFS.FileSystem; using NPOI.DDF; + using NPOI.SS.UserModel; /** @@ -32,7 +33,7 @@ namespace NPOI.HSSF.UserModel * * @author Daniel Noll */ - public class HSSFObjectData : HSSFPicture + public class HSSFObjectData : HSSFPicture, IObjectData { /** @@ -64,21 +65,24 @@ public String OLE2ClassName * @return the object data as an OLE2 directory. * @ if there was an error Reading the data. */ - public DirectoryEntry GetDirectory() + public DirectoryEntry Directory { - EmbeddedObjectRefSubRecord subRecord = FindObjectRecord(); + get + { + EmbeddedObjectRefSubRecord subRecord = FindObjectRecord(); - int? streamId = ((EmbeddedObjectRefSubRecord)subRecord).StreamId; - String streamName = "MBD" + HexDump.ToHex((int)streamId); + int? streamId = ((EmbeddedObjectRefSubRecord)subRecord).StreamId; + String streamName = "MBD" + HexDump.ToHex((int)streamId); - Entry entry = _root.GetEntry(streamName); - if (entry is DirectoryEntry directoryEntry) - { - return directoryEntry; - } - else - { - throw new IOException("Stream " + streamName + " was not an OLE2 directory"); + Entry entry = _root.GetEntry(streamName); + if (entry is DirectoryEntry) + { + return (DirectoryEntry)entry; + } + else + { + throw new IOException("Stream " + streamName + " was not an OLE2 directory"); + } } } @@ -87,9 +91,12 @@ public DirectoryEntry GetDirectory() * that doesn't have an associated POIFS Directory * Entry */ - public byte[] GetObjectData() + public byte[] ObjectData { - return FindObjectRecord().ObjectData; + get + { + return FindObjectRecord().ObjectData; + } } /** diff --git a/main/HSSF/UserModel/HSSFPatriarch.cs b/main/HSSF/UserModel/HSSFPatriarch.cs index 5f60948b9..16be5dab8 100644 --- a/main/HSSF/UserModel/HSSFPatriarch.cs +++ b/main/HSSF/UserModel/HSSFPatriarch.cs @@ -34,7 +34,7 @@ namespace NPOI.HSSF.UserModel /// little other than act as a container for other shapes and Groups. /// @author Glen Stampoultzis (glens at apache.org) /// - public class HSSFPatriarch : HSSFShapeContainer, IDrawing, IDrawing + public class HSSFPatriarch : HSSFShapeContainer, IDrawing { //private static POILogger log = POILogFactory.GetLogger(typeof(HSSFPatriarch)); readonly List _shapes = new List(); @@ -200,18 +200,8 @@ public IPicture CreatePicture(IClientAnchor anchor, int pictureIndex) return CreatePicture((HSSFClientAnchor)anchor, pictureIndex); } - /** - * Adds a new OLE Package Shape - * - * @param anchor the client anchor describes how this picture is - * attached to the sheet. - * @param storageId the storageId returned by {@Link HSSFWorkbook.AddOlePackage} - * @param pictureIndex the index of the picture (used as preview image) in the - * workbook collection of pictures. - * - * @return newly Created shape - */ - public HSSFObjectData CreateObjectData(HSSFClientAnchor anchor, int storageId, int pictureIndex) + + public IObjectData CreateObjectData(IClientAnchor anchor, int storageId, int pictureIndex) { ObjRecord obj = new ObjRecord(); @@ -274,7 +264,7 @@ public HSSFObjectData CreateObjectData(HSSFClientAnchor anchor, int storageId, i } // create picture shape, which need to be minimal modified for oleshapes - HSSFPicture shape = new HSSFPicture(null, anchor); + HSSFPicture shape = new HSSFPicture(null, (HSSFClientAnchor)anchor); shape.PictureIndex = (/*setter*/pictureIndex); EscherContainerRecord spContainer = shape.GetEscherContainer(); EscherSpRecord spRecord = spContainer.GetChildById(EscherSpRecord.RECORD_ID) as EscherSpRecord; diff --git a/main/HSSF/UserModel/HSSFPicture.cs b/main/HSSF/UserModel/HSSFPicture.cs index bd260bd44..d27ceee17 100644 --- a/main/HSSF/UserModel/HSSFPicture.cs +++ b/main/HSSF/UserModel/HSSFPicture.cs @@ -223,6 +223,10 @@ public IPictureData PictureData { get { + if(PictureIndex == -1) + { + return null; + } HSSFPatriarch patriarch = Patriarch; HSSFShape parent = Parent as HSSFShape; while(patriarch == null && parent != null) @@ -264,7 +268,7 @@ public String FileName { EscherComplexProperty propFile = (EscherComplexProperty)GetOptRecord().Lookup( EscherProperties.BLIP__BLIPFILENAME); - return (null == propFile) ? "" : Trim(StringUtil.GetFromUnicodeLE(propFile.ComplexData)); + return (null == propFile) ? "" : StringUtil.Trim(StringUtil.GetFromUnicodeLE(propFile.ComplexData)); } set { @@ -275,24 +279,6 @@ public String FileName } } - private static String Trim(string value) - { - int end = value.Length; - int st = 0; - //int off = offset; /* avoid getfield opcode */ - char[] val = value.ToCharArray(); /* avoid getfield opcode */ - - while((st < end) && (val[st] <= ' ')) - { - st++; - } - while((st < end) && (val[end - 1] <= ' ')) - { - end--; - } - return ((st > 0) || (end < value.Length)) ? value.Substring(st, end - st) : value; - } - public override int ShapeType { get { return base.ShapeType; } diff --git a/main/HSSF/UserModel/HSSFShape.cs b/main/HSSF/UserModel/HSSFShape.cs index 7331e3adc..7b7a1a4c4 100644 --- a/main/HSSF/UserModel/HSSFShape.cs +++ b/main/HSSF/UserModel/HSSFShape.cs @@ -459,7 +459,7 @@ public String ShapeName } EscherProperty ep = eor.Lookup(EscherProperties.GROUPSHAPE__SHAPENAME); if (ep is EscherComplexProperty property) { - return StringUtil.GetFromUnicodeLE(property.ComplexData); + return StringUtil.Trim(StringUtil.GetFromUnicodeLE(property.ComplexData)); } return null; } diff --git a/main/HSSF/UserModel/HSSFSheet.cs b/main/HSSF/UserModel/HSSFSheet.cs index 7b0ce62e6..893eae0f0 100644 --- a/main/HSSF/UserModel/HSSFSheet.cs +++ b/main/HSSF/UserModel/HSSFSheet.cs @@ -117,7 +117,7 @@ public HSSFSheet(HSSFWorkbook workbook, InternalSheet sheet) /// the cloned sheet public ISheet CloneSheet(HSSFWorkbook workbook) { - IDrawing iDrawing = this.DrawingPatriarch;/*Aggregate drawing records*/ + IDrawing iDrawing = this.DrawingPatriarch;/*Aggregate drawing records*/ HSSFSheet sheet = new HSSFSheet(workbook, _sheet.CloneSheet()); int pos = sheet._sheet.FindFirstRecordLocBySid(DrawingRecord.sid); DrawingRecord dr = (DrawingRecord)sheet._sheet.FindFirstRecordBySid(DrawingRecord.sid); @@ -2084,7 +2084,7 @@ public EscherAggregate DrawingEscherAggregate * * @return the top-level drawing patriarch, if there is one, else returns null */ - public IDrawing DrawingPatriarch + public IDrawing DrawingPatriarch { get { @@ -2101,7 +2101,7 @@ public IDrawing DrawingPatriarch * * @return The new patriarch. */ - public IDrawing CreateDrawingPatriarch() + public IDrawing CreateDrawingPatriarch() { _patriarch = GetPatriarch(true); return _patriarch; diff --git a/main/HSSF/UserModel/HSSFWorkbook.cs b/main/HSSF/UserModel/HSSFWorkbook.cs index 75fcebbff..d50420830 100644 --- a/main/HSSF/UserModel/HSSFWorkbook.cs +++ b/main/HSSF/UserModel/HSSFWorkbook.cs @@ -1861,7 +1861,7 @@ internal void InitDrawings() { foreach (HSSFSheet sh in _sheets) { - IDrawing tmp = sh.DrawingPatriarch; + IDrawing _ = sh.DrawingPatriarch; } } else @@ -2056,6 +2056,15 @@ public int AddOlePackage(POIFSFileSystem poiData, String label, String fileName, } } + /// + /// Adds an OLE package manager object with the given content to the sheet + /// + /// the payload + /// the label of the payload + /// the original filename + /// the command to open the payload + /// the index of the added ole object, i.e. the storage id + /// if the object can't be embedded public int AddOlePackage(byte[] oleData, String label, String fileName, String command) { // check if we were Created by POIFS otherwise create a new dummy POIFS for storing the package data diff --git a/main/SS/UserModel/Drawing.cs b/main/SS/UserModel/Drawing.cs index 1924152bb..abe3da639 100644 --- a/main/SS/UserModel/Drawing.cs +++ b/main/SS/UserModel/Drawing.cs @@ -14,60 +14,70 @@ the License. You may obtain a copy of the License at See the License for the specific language governing permissions and limitations under the License. ==================================================================== */ + namespace NPOI.SS.UserModel { - - /** - * @author Yegor Kozlov - */ - public interface IDrawing + /// + /// High level representation of spreadsheet Drawing. + /// + public interface IDrawing : IShapeContainer where T : class, IShape { - /** - * Creates a picture. - * @param anchor the client anchor describes how this picture is - * attached to the sheet. - * @param pictureIndex the index of the picture in the workbook collection - * of pictures. - * - * @return the newly created picture. - */ + /// + /// Creates a picture. + /// + /// the client anchor describes how this picture is + /// attached to the sheet. + /// + /// the index of the picture in the workbook collection + /// of pictures. + /// + /// the newly created picture. IPicture CreatePicture(IClientAnchor anchor, int pictureIndex); - /** - * Creates a comment. - * @param anchor the client anchor describes how this comment is attached - * to the sheet. - * @return the newly created comment. - */ + /// + /// Creates a comment. + /// + /// the client anchor describes how this comment is attached + /// to the sheet. + /// + /// the newly created comment. IComment CreateCellComment(IClientAnchor anchor); - /** - * Creates a chart. - * @param anchor the client anchor describes how this chart is attached to - * the sheet. - * @return the newly created chart - */ + /// + /// Creates a chart. + /// + /// the client anchor describes how this chart is attached to + /// the sheet. + /// + /// the newly created chart IChart CreateChart(IClientAnchor anchor); - /** - * Creates a new client anchor and sets the top-left and bottom-right - * coordinates of the anchor. - * - * @param dx1 the x coordinate in EMU within the first cell. - * @param dy1 the y coordinate in EMU within the first cell. - * @param dx2 the x coordinate in EMU within the second cell. - * @param dy2 the y coordinate in EMU within the second cell. - * @param col1 the column (0 based) of the first cell. - * @param row1 the row (0 based) of the first cell. - * @param col2 the column (0 based) of the second cell. - * @param row2 the row (0 based) of the second cell. - * @return the newly created client anchor - */ + /// + /// Creates a new client anchor and Sets the top-left and bottom-right + /// coordinates of the anchor. + /// + /// the x coordinate in EMU within the first cell. + /// the y coordinate in EMU within the first cell. + /// the x coordinate in EMU within the second cell. + /// the y coordinate in EMU within the second cell. + /// the column (0 based) of the first cell. + /// the row (0 based) of the first cell. + /// the column (0 based) of the second cell. + /// the row (0 based) of the second cell. + /// the newly created client anchor IClientAnchor CreateAnchor(int dx1, int dy1, int dx2, int dy2, int col1, int row1, int col2, int row2); - } - - public interface IDrawing : IShapeContainer where T : class, IShape - { + /// + /// Adds a new OLE Package Shape + /// + /// the client anchor describes how this picture is + /// attached to the sheet. + /// + /// the storageId returned by + /// the index of the picture (used as preview image) in the + /// workbook collection of pictures. + /// + /// newly created shape + IObjectData CreateObjectData(IClientAnchor anchor, int storageId, int pictureIndex); } } \ No newline at end of file diff --git a/main/SS/UserModel/IObjectData.cs b/main/SS/UserModel/IObjectData.cs new file mode 100644 index 000000000..5713d72ce --- /dev/null +++ b/main/SS/UserModel/IObjectData.cs @@ -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. +==================================================================== */ + +namespace NPOI.SS.UserModel +{ + using NPOI.POIFS.FileSystem; + + /// + /// + /// Common interface for OLE shapes, i.e. shapes linked to embedded documents + /// + /// + /// + /// + /// @since POI 3.16-beta2 + public interface IObjectData : ISimpleShape + { + /// + /// data portion, for an ObjectData that doesn't have an associated POIFS Directory Entry + /// + byte[] ObjectData { get; } + + + /// does this ObjectData have an associated POIFS Directory Entry? + ///(Not all do, those that don't have a data portion) + /// + bool HasDirectoryEntry(); + + /// + /// + /// Gets the object data. Only call for ones that have + /// data though. See {@link #hasDirectoryEntry()}. + /// The caller has to close the corresponding POIFSFileSystem + /// + /// + /// object data as an OLE2 directory./// + /// if there was an error Reading the data. + DirectoryEntry Directory { get; } + + + /// + /// + /// the OLE2 Class Name of the object/// + string OLE2ClassName { get; } + + /// + /// + /// a filename suggestion - inspecting/interpreting the Directory object probably gives a better result + string FileName { get; } + + /// + /// + /// the preview picture + /// + IPictureData PictureData { get; } + } +} diff --git a/main/SS/UserModel/IShapeContainer.cs b/main/SS/UserModel/IShapeContainer.cs index 5f89de4da..a4bb85ef7 100644 --- a/main/SS/UserModel/IShapeContainer.cs +++ b/main/SS/UserModel/IShapeContainer.cs @@ -22,7 +22,7 @@ namespace NPOI.SS.UserModel /// A common interface for shape groups. /// /// - public interface IShapeContainer : IEnumerable where T : class, IShape + public interface IShapeContainer : IEnumerable where T : class, IShape { } } diff --git a/main/SS/UserModel/Sheet.cs b/main/SS/UserModel/Sheet.cs index 1e2ad7e1e..d183fa5c8 100644 --- a/main/SS/UserModel/Sheet.cs +++ b/main/SS/UserModel/Sheet.cs @@ -449,7 +449,7 @@ bool Autobreaks /// start from scratch! /// /// The drawing patriarch. - IDrawing DrawingPatriarch { get; } + IDrawing DrawingPatriarch { get; } /** * Window zoom magnification for current view representing percent values. @@ -763,7 +763,7 @@ bool Autobreaks /// /// Creates the top-level drawing patriarch. /// - IDrawing CreateDrawingPatriarch(); + IDrawing CreateDrawingPatriarch(); /// /// Gets the parent workbook. diff --git a/main/SS/UserModel/Workbook.cs b/main/SS/UserModel/Workbook.cs index b49beeecf..64b1f1554 100644 --- a/main/SS/UserModel/Workbook.cs +++ b/main/SS/UserModel/Workbook.cs @@ -434,5 +434,16 @@ public interface IWorkbook : ICloseable, IDisposable /// Returns the spreadsheet version (EXCLE97) of this workbook /// SpreadsheetVersion SpreadsheetVersion { get; } + + /// + /// Adds an OLE package manager object with the given content to the sheet + /// + /// the payload + /// the label of the payload + /// the original filename + /// the command to open the payload + /// the index of the added ole object, i.e. the storage id + /// if the object can't be embedded + int AddOlePackage(byte[] oleData, String label, String fileName, String command); } } diff --git a/main/SS/Util/ImageUtils.cs b/main/SS/Util/ImageUtils.cs index 1afac0674..121d5b21f 100644 --- a/main/SS/Util/ImageUtils.cs +++ b/main/SS/Util/ImageUtils.cs @@ -310,7 +310,7 @@ public static Size GetDimensionFromAnchor(IPicture picture) } - private static double GetRowHeightInPixels(ISheet sheet, int rowNum) + public static double GetRowHeightInPixels(ISheet sheet, int rowNum) { IRow r = sheet.GetRow(rowNum); double points = (r == null) ? sheet.DefaultRowHeightInPoints : r.HeightInPoints; diff --git a/main/Util/StringUtil.cs b/main/Util/StringUtil.cs index a4f07360d..9dcadf53e 100644 --- a/main/Util/StringUtil.cs +++ b/main/Util/StringUtil.cs @@ -895,5 +895,23 @@ public static int CodePointAt(this string text, int index) throw new Exception("String was not well-formed UTF-16."); } } + + public static String Trim(string value) + { + int end = value.Length; + int st = 0; + //int off = offset; /* avoid getfield opcode */ + char[] val = value.ToCharArray(); /* avoid getfield opcode */ + + while((st < end) && (val[st] <= ' ')) + { + st++; + } + while((st < end) && (val[end - 1] <= ' ')) + { + end--; + } + return ((st > 0) || (end < value.Length)) ? value.Substring(st, end - st) : value; + } } } diff --git a/ooxml/XSSF/Extractor/EmbeddedData.cs b/ooxml/XSSF/Extractor/EmbeddedData.cs new file mode 100644 index 000000000..6caa8a800 --- /dev/null +++ b/ooxml/XSSF/Extractor/EmbeddedData.cs @@ -0,0 +1,93 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for Additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +namespace NPOI.SS.Extractor +{ + using NPOI.SS.UserModel; + using System; + using System.Text.RegularExpressions; + + /// + /// A collection of embedded object informations and content + /// + public class EmbeddedData + { + private string _filename; + private byte[] _embeddedData; + private IShape _shape; + private String _contentType = "binary/octet-stream"; + + public EmbeddedData(String filename, byte[] embeddedData, String contentType) + { + Filename = filename; + SetEmbeddedData(embeddedData); + _contentType = contentType; + } + + private readonly Regex regFile = new Regex("[^/\\\\]*[/\\\\]", RegexOptions.Compiled); + /// + /// + /// filename + public string Filename + { + get { return _filename; } + set + { + if (value == null) + { + this._filename = "unknown.bin"; + } + else + { + this._filename = regFile.Replace(value, "").Trim(); + } + } + } + + /// + /// + /// embedded object byte array + public byte[] GetEmbeddedData() + { + return _embeddedData; + } + + /// + /// Sets the embedded object as byte array + /// + /// the embedded object byte array + public void SetEmbeddedData(byte[] embeddedData) + { + this._embeddedData = (embeddedData == null) ? null : (byte[])embeddedData.Clone(); + } + + public IShape Shape + { + get { return _shape; } + set { this._shape = value; } + } + /// + /// content-/mime-type of the embedded object, the default (if unknown) is {@code binary/octet-stream} + /// + public string ContentType + { + get { return _contentType; } + set { _contentType = value; } + } + } +} + diff --git a/ooxml/XSSF/Extractor/EmbeddedExtractor.cs b/ooxml/XSSF/Extractor/EmbeddedExtractor.cs new file mode 100644 index 000000000..ac42b7a22 --- /dev/null +++ b/ooxml/XSSF/Extractor/EmbeddedExtractor.cs @@ -0,0 +1,499 @@ +/* ==================================================================== + 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. +==================================================================== */ + +namespace NPOI.SS.Extractor +{ + using NPOI.HPSF; + using NPOI.POIFS.FileSystem; + using NPOI.SS.UserModel; + using NPOI.Util; + using NPOI.HSSF.UserModel; + using System.Collections.Generic; + using System; + using System.IO; + using System.Text; + using System.Collections; + using NPOI.XSSF.UserModel; + + /// + /// This extractor class tries to identify various embedded documents within Excel files + /// and provide them via a common interface, i.e. the EmbeddedData instances + /// + public class EmbeddedExtractor : IEnumerable + { + private static POILogger LOG = POILogFactory.GetLogger(typeof(EmbeddedExtractor)); + + // contentType + private static String CONTENT_TYPE_BYTES = "binary/octet-stream"; + private static String CONTENT_TYPE_PDF = "application/pdf"; + private static String CONTENT_TYPE_DOC = "application/msword"; + private static String CONTENT_TYPE_XLS = "application/vnd.ms-excel"; + + /// + /// + /// list of known extractors, if you provide custom extractors, override this method/// + public IEnumerator GetEnumerator() + { + EmbeddedExtractor[] ee = { + new Ole10Extractor(), new PdfExtractor(), new BiffExtractor(), new OOXMLExtractor(), new FsExtractor() + }; + return Arrays.AsList(ee).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + public virtual EmbeddedData ExtractOne(DirectoryNode src) + { + + foreach (EmbeddedExtractor ee in this) + { + if (ee.CanExtract(src)) + { + return ee.Extract(src); + } + } + return null; + } + + public virtual EmbeddedData ExtractOne(IPicture src) + { + + foreach (EmbeddedExtractor ee in this) + { + if (ee.CanExtract(src)) + { + return ee.Extract(src); + } + } + return null; + } + + public virtual List ExtractAll(ISheet sheet) + { + + IDrawing patriarch = sheet.DrawingPatriarch; + if (null == patriarch) + { + return new List(); + } + List embeddings = new List(); + ExtractAll(patriarch, embeddings); + return embeddings; + } + + protected virtual void ExtractAll(IShapeContainer parent, List embeddings) where T : class, IShape + { + foreach (IShape shape in parent) + { + EmbeddedData data = null; + if (shape is IObjectData) + { + IObjectData od = (IObjectData)shape; + try + { + if (od.HasDirectoryEntry()) + { + data = ExtractOne((DirectoryNode)od.Directory); + } + else + { + String contentType = CONTENT_TYPE_BYTES; + if (od is XSSFObjectData) + { + contentType = ((XSSFObjectData)od).GetObjectPart().ContentType; + } + data = new EmbeddedData(od.FileName, od.ObjectData, contentType); + } + } + catch (Exception e) + { + LOG.Log(POILogger.WARN, "Entry not found / Readable - ignoring OLE embedding", e); + } + } + else if (shape is IPicture) + { + data = ExtractOne((IPicture)shape); + } + else if (shape is IShapeContainer) + { + ExtractAll((IShapeContainer)shape, embeddings); + } + + if (data == null) + { + continue; + } + + data.Shape = (/*setter*/shape); + String filename = data.Filename; + String extension = (filename == null || filename.LastIndexOf('.') == -1) ? ".bin" : filename.Substring(filename.LastIndexOf('.')); + + // try to find an alternative name + if (filename == null || "".Equals(filename) || filename.StartsWith("MBD") || filename.StartsWith("Root Entry")) + { + filename = shape.ShapeName; + if (filename != null) + { + filename += extension; + } + } + // default to dummy name + if (filename == null || "".Equals(filename)) + { + filename = "picture_" + embeddings.Count + extension; + } + filename = filename.Trim(); + data.Filename = (/*setter*/filename); + + embeddings.Add(data); + } + } + + + public virtual bool CanExtract(DirectoryNode source) + { + return false; + } + + public virtual bool CanExtract(IPicture source) + { + return false; + } + + public virtual EmbeddedData Extract(DirectoryNode dn) + { + + //assert(canExtract(dn)); + POIFSFileSystem dest = new POIFSFileSystem(); + copyNodes(dn, dest.Root); + // start with a reasonable big size + MemoryStream bos = new MemoryStream(20000); + dest.WriteFileSystem(bos); + dest.Close(); + + return new EmbeddedData(dn.Name, bos.ToArray(), CONTENT_TYPE_BYTES); + } + + public virtual EmbeddedData Extract(IPicture source) + { + + return null; + } + + public class Ole10Extractor : EmbeddedExtractor + { + public override bool CanExtract(DirectoryNode dn) + { + ClassID clsId = dn.StorageClsid; + return ClassID.OLE10_PACKAGE.Equals(clsId); + } + public override EmbeddedData Extract(DirectoryNode dn) + { + try + { + // TODO: inspect the CompObj record for more details, i.e. the content type + Ole10Native ole10 = Ole10Native.CreateFromEmbeddedOleObject(dn); + return new EmbeddedData(ole10.FileName, ole10.DataBuffer, CONTENT_TYPE_BYTES); + } + catch (Ole10NativeException e) + { + throw new IOException("", e); + } + } + } + + class PdfExtractor : EmbeddedExtractor + { + static ClassID PdfClassID = new ClassID("{B801CA65-A1FC-11D0-85AD-444553540000}"); + public override bool CanExtract(DirectoryNode dn) + { + ClassID clsId = dn.StorageClsid; + return (PdfClassID.Equals(clsId) + || dn.HasEntry("CONTENTS")); + } + + public override EmbeddedData Extract(DirectoryNode dn) + { + MemoryStream bos = new MemoryStream(); + DocumentInputStream is1 = dn.CreateDocumentInputStream("CONTENTS"); + IOUtils.Copy(is1, bos); + is1.Close(); + return new EmbeddedData(dn.Name + ".pdf", bos.ToArray(), CONTENT_TYPE_PDF); + } + public override bool CanExtract(IPicture source) + { + IPictureData pd = source.PictureData; + return (pd != null && pd.PictureType == PictureType.EMF); + } + + /// + /// + /// Mac Office encodes embedded objects inside the picture, e.g. PDF is part of an EMF. + /// If an embedded stream is inside an EMF picture, this method extracts the payload. + /// + /// + /// + /// + /// embedded data in an EMF picture or null if none is found/// + public override EmbeddedData Extract(IPicture source) + { + + // check for emf+ embedded pdf (poor mans style :( ) + // Mac Excel 2011 embeds pdf files with this method. + IPictureData pd = source.PictureData; + if (pd == null || pd.PictureType != PictureType.EMF) + { + return null; + } + + // TODO: investigate if this is just an EMF-hack or if other formats are also embedded in EMF + byte[] pictureBytes = pd.Data; + int idxStart = IndexOf(pictureBytes, 0, Encoding.ASCII.GetBytes("%PDF-")); //.GetBytes(LocaleUtil.CHARSET_1252) + if (idxStart == -1) + { + return null; + } + + int idxEnd = IndexOf(pictureBytes, idxStart, Encoding.ASCII.GetBytes("%%EOF")); // .GetBytes(LocaleUtil.CHARSET_1252) + if (idxEnd == -1) + { + return null; + } + + int pictureBytesLen = idxEnd - idxStart + 6; + byte[] pdfBytes = new byte[pictureBytesLen]; + System.Array.Copy(pictureBytes, idxStart, pdfBytes, 0, pictureBytesLen); + String filename = source.ShapeName.Trim(); + if (!filename.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase)) + { + filename += ".pdf"; + } + return new EmbeddedData(filename, pdfBytes, CONTENT_TYPE_PDF); + } + + + } + + class OOXMLExtractor : EmbeddedExtractor + { + public override bool CanExtract(DirectoryNode dn) + { + return dn.HasEntry("package"); + } + public override EmbeddedData Extract(DirectoryNode dn) + { + + + ClassID clsId = dn.StorageClsid; + + String contentType, ext; + if (ClassID.WORD2007.Equals(clsId)) + { + ext = ".docx"; + contentType = "application/vnd.Openxmlformats-officedocument.wordProcessingml.document"; + } + else if (ClassID.WORD2007_MACRO.Equals(clsId)) + { + ext = ".docm"; + contentType = "application/vnd.ms-word.document.macroEnabled.12"; + } + else if (ClassID.EXCEL2007.Equals(clsId) || ClassID.EXCEL2003.Equals(clsId) || ClassID.EXCEL2010.Equals(clsId)) + { + ext = ".xlsx"; + contentType = "application/vnd.Openxmlformats-officedocument.spreadsheetml.sheet"; + } + else if (ClassID.EXCEL2007_MACRO.Equals(clsId)) + { + ext = ".xlsm"; + contentType = "application/vnd.ms-excel.sheet.macroEnabled.12"; + } + else if (ClassID.EXCEL2007_XLSB.Equals(clsId)) + { + ext = ".xlsb"; + contentType = "application/vnd.ms-excel.sheet.binary.macroEnabled.12"; + } + else if (ClassID.POWERPOINT2007.Equals(clsId)) + { + ext = ".pptx"; + contentType = "application/vnd.Openxmlformats-officedocument.presentationml.presentation"; + } + else if (ClassID.POWERPOINT2007_MACRO.Equals(clsId)) + { + ext = ".ppsm"; + contentType = "application/vnd.ms-powerpoint.slideShow.macroEnabled.12"; + } + else + { + ext = ".zip"; + contentType = "application/zip"; + } + + DocumentInputStream dis = dn.CreateDocumentInputStream("package"); + byte[] data = IOUtils.ToByteArray(dis); + dis.Close(); + + return new EmbeddedData(dn.Name + ext, data, contentType); + } + } + + class BiffExtractor : EmbeddedExtractor + { + public override bool CanExtract(DirectoryNode dn) + { + return CanExtractExcel(dn) || CanExtractWord(dn); + } + + protected static bool CanExtractExcel(DirectoryNode dn) + { + ClassID clsId = dn.StorageClsid; + return (ClassID.EXCEL95.Equals(clsId) + || ClassID.EXCEL97.Equals(clsId) + || dn.HasEntry("Workbook") /*...*/); + } + + protected static bool CanExtractWord(DirectoryNode dn) + { + ClassID clsId = dn.StorageClsid; + return (ClassID.WORD95.Equals(clsId) + || ClassID.WORD97.Equals(clsId) + || dn.HasEntry("WordDocument")); + } + public override EmbeddedData Extract(DirectoryNode dn) + { + + EmbeddedData ed = base.Extract(dn); + if (CanExtractExcel(dn)) + { + ed.Filename = (/*setter*/dn.Name + ".xls"); + ed.ContentType = (/*setter*/CONTENT_TYPE_XLS); + } + else if (CanExtractWord(dn)) + { + ed.Filename = (/*setter*/dn.Name + ".doc"); + ed.ContentType = (/*setter*/CONTENT_TYPE_DOC); + } + + return ed; + } + } + + class FsExtractor : EmbeddedExtractor + { + public override bool CanExtract(DirectoryNode dn) + { + return true; + } + public override EmbeddedData Extract(DirectoryNode dn) + { + + EmbeddedData ed = base.Extract(dn); + ed.Filename = (/*setter*/dn.Name + ".ole"); + // TODO: read the content type from CombObj stream + return ed; + } + } + + protected static void copyNodes(DirectoryNode src, DirectoryNode dest) + { + + foreach (Entry e in src) + { + if (e is DirectoryNode) + { + DirectoryNode srcDir = (DirectoryNode)e; + DirectoryNode destDir = (DirectoryNode)dest.CreateDirectory(srcDir.Name); + destDir.StorageClsid = (/*setter*/srcDir.StorageClsid); + copyNodes(srcDir, destDir); + } + else + { + DocumentInputStream is1 = src.CreateDocumentInputStream(e); + try + { + dest.CreateDocument(e.Name, is1); + } + finally + { + is1.Close(); + } + } + } + } + + + + /// + /// Knuth-Morris-Pratt Algorithm for Pattern Matching + /// Finds the first occurrence of the pattern in the text. + /// + private static int IndexOf(byte[] data, int offset, byte[] pattern) + { + int[] failure = computeFailure(pattern); + + int j = 0; + if (data.Length == 0) + { + return -1; + } + + for (int i = offset; i < data.Length; i++) + { + while (j > 0 && pattern[j] != data[i]) + { + j = failure[j - 1]; + } + if (pattern[j] == data[i]) { j++; } + if (j == pattern.Length) + { + return i - pattern.Length + 1; + } + } + return -1; + } + + /// + /// Computes the failure function using a boot-strapping Process, + /// where the pattern is matched against itself. + /// + private static int[] computeFailure(byte[] pattern) + { + int[] failure = new int[pattern.Length]; + + int j = 0; + for (int i = 1; i < pattern.Length; i++) + { + while (j > 0 && pattern[j] != pattern[i]) + { + j = failure[j - 1]; + } + if (pattern[j] == pattern[i]) + { + j++; + } + failure[i] = j; + } + + return failure; + } + + + } +} + diff --git a/ooxml/XSSF/Streaming/SXSSFDrawing.cs b/ooxml/XSSF/Streaming/SXSSFDrawing.cs index 9a9fd3a8b..587274dfa 100644 --- a/ooxml/XSSF/Streaming/SXSSFDrawing.cs +++ b/ooxml/XSSF/Streaming/SXSSFDrawing.cs @@ -27,7 +27,7 @@ namespace NPOI.XSSF.Streaming /// Delegates most tasks to the non-streaming XSSF code. /// TODO: Potentially, Comment and Chart need a similar streaming wrapper like Picture. /// - public class SXSSFDrawing : IDrawing, IDrawing + public class SXSSFDrawing : IDrawing { private readonly SXSSFWorkbook _wb; private readonly XSSFDrawing _drawing; @@ -64,10 +64,10 @@ IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } - //public ObjectData CreateObjectData(IClientAnchor anchor, int storageId, int pictureIndex) - //{ - // return _drawing.CreateObjectData(anchor, storageId, pictureIndex); - //} + public IObjectData CreateObjectData(IClientAnchor anchor, int storageId, int pictureIndex) + { + return _drawing.CreateObjectData(anchor, storageId, pictureIndex); + } //public Iterator iterator() //{ // return _drawing.Shapes.Iterator(); diff --git a/ooxml/XSSF/Streaming/SXSSFSheet.cs b/ooxml/XSSF/Streaming/SXSSFSheet.cs index c51eace23..2d4fedca3 100644 --- a/ooxml/XSSF/Streaming/SXSSFSheet.cs +++ b/ooxml/XSSF/Streaming/SXSSFSheet.cs @@ -182,7 +182,7 @@ public bool DisplayZeros } } - public IDrawing DrawingPatriarch + public IDrawing DrawingPatriarch { get { @@ -658,7 +658,7 @@ public List GetHyperlinkList() return _sh.GetHyperlinkList(); } - public IDrawing CreateDrawingPatriarch() + public IDrawing CreateDrawingPatriarch() { return new SXSSFDrawing((SXSSFWorkbook)Workbook, (XSSFDrawing)_sh.CreateDrawingPatriarch()); } diff --git a/ooxml/XSSF/Streaming/SXSSFWorkbook.cs b/ooxml/XSSF/Streaming/SXSSFWorkbook.cs index 6cfc07ace..5a8996f62 100644 --- a/ooxml/XSSF/Streaming/SXSSFWorkbook.cs +++ b/ooxml/XSSF/Streaming/SXSSFWorkbook.cs @@ -981,6 +981,11 @@ public SpreadsheetVersion SpreadsheetVersion } } + public int AddOlePackage(byte[] oleData, String label, String fileName, String command) + { + return _wb.AddOlePackage(oleData, label, fileName, command); + } + /// /// Gets a bool value that indicates whether the date systems used in the workbook starts in 1904. /// The default value is false, meaning that the workbook uses the 1900 date system, diff --git a/ooxml/XSSF/UserModel/XSSFDialogsheet.cs b/ooxml/XSSF/UserModel/XSSFDialogsheet.cs index 29ecbdd5a..cd14aa779 100644 --- a/ooxml/XSSF/UserModel/XSSFDialogsheet.cs +++ b/ooxml/XSSF/UserModel/XSSFDialogsheet.cs @@ -407,7 +407,7 @@ short ISheet.TabColorIndex } } - IDrawing ISheet.DrawingPatriarch + public IDrawing DrawingPatriarch { get { throw new System.NotImplementedException(); } } @@ -609,7 +609,7 @@ void ISheet.AutoSizeColumn(int column, bool useMergedCells) throw new System.NotImplementedException(); } - IDrawing ISheet.CreateDrawingPatriarch() + IDrawing ISheet.CreateDrawingPatriarch() { throw new System.NotImplementedException(); } diff --git a/ooxml/XSSF/UserModel/XSSFDrawing.cs b/ooxml/XSSF/UserModel/XSSFDrawing.cs index da7658219..f6a652c94 100644 --- a/ooxml/XSSF/UserModel/XSSFDrawing.cs +++ b/ooxml/XSSF/UserModel/XSSFDrawing.cs @@ -28,6 +28,8 @@ limitations under the License. using NPOI.Util; using System.Text.RegularExpressions; using System.Linq; +using System.Collections; +using NPOI.OpenXml4Net.Exceptions; namespace NPOI.XSSF.UserModel { @@ -37,7 +39,7 @@ namespace NPOI.XSSF.UserModel * * @author Yegor Kozlov */ - public class XSSFDrawing : POIXMLDocumentPart, IDrawing + public class XSSFDrawing : POIXMLDocumentPart, IDrawing { public static String NAMESPACE_A = XSSFRelation.NS_DRAWINGML; public static String NAMESPACE_C = XSSFRelation.NS_CHART; @@ -124,7 +126,7 @@ public IClientAnchor CreateAnchor(int dx1, int dy1, int dx2, int dy2, */ public XSSFTextBox CreateTextbox(IClientAnchor anchor) { - long shapeId = newShapeId(); + long shapeId = NewShapeId(); CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor(anchor); CT_Shape ctShape = ctAnchor.AddNewSp(); ctShape.Set(XSSFSimpleShape.Prototype()); @@ -152,7 +154,7 @@ public IPicture CreatePicture(XSSFClientAnchor anchor, int pictureIndex) { PackageRelationship rel = AddPictureReference(pictureIndex); - long shapeId = newShapeId(); + long shapeId = NewShapeId(); CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor(anchor); CT_Picture ctShape = ctAnchor.AddNewPic(); ctShape.Set(XSSFPicture.Prototype()); @@ -272,7 +274,7 @@ internal PackageRelationship AddPictureReference(int pictureIndex) */ public XSSFSimpleShape CreateSimpleShape(XSSFClientAnchor anchor) { - long shapeId = newShapeId(); + long shapeId = NewShapeId(); CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor(anchor); CT_Shape ctShape = ctAnchor.AddNewSp(); ctShape.Set(XSSFSimpleShape.Prototype()); @@ -298,7 +300,7 @@ public XSSFSimpleShape CreateSimpleShape(XSSFClientAnchor anchor) */ public XSSFConnector CreateConnector(XSSFClientAnchor anchor) { - long shapeId = newShapeId(); + long shapeId = NewShapeId(); CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor(anchor); CT_Connector ctShape = ctAnchor.AddNewCxnSp(); ctShape.Set(XSSFConnector.Prototype()); @@ -328,7 +330,7 @@ SS.UserModel.ISheet Sheet ) { var anchor = new XSSFClientAnchor(Sheet, (int)BFF.Left, (int)BFF.Top , (int)BFF.Rigth, (int)BFF.Bottom); - long shapeId = newShapeId(); + long shapeId = NewShapeId(); CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor(anchor); CT_Shape ctShape = ctAnchor.AddNewSp(); ctShape.Set(XSSFFreeform.Prototype()); @@ -352,7 +354,7 @@ SS.UserModel.ISheet Sheet */ public XSSFShapeGroup CreateGroup(XSSFClientAnchor anchor) { - long shapeId = newShapeId(); + long shapeId = NewShapeId(); CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor(anchor); CT_GroupShape ctGroup = ctAnchor.AddNewGrpSp(); ctGroup.Set(XSSFShapeGroup.Prototype()); @@ -424,6 +426,7 @@ private XSSFGraphicFrame CreateGraphicFrame(XSSFClientAnchor anchor) CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor(anchor); CT_GraphicalObjectFrame ctGraphicFrame = ctAnchor.AddNewGraphicFrame(); ctGraphicFrame.Set(XSSFGraphicFrame.Prototype()); + ctGraphicFrame.xfrm = CreateXfrm(anchor); long frameId = numOfGraphicFrames++; XSSFGraphicFrame graphicFrame = new XSSFGraphicFrame(this, ctGraphicFrame); @@ -435,6 +438,114 @@ private XSSFGraphicFrame CreateGraphicFrame(XSSFClientAnchor anchor) return graphicFrame; } + public IObjectData CreateObjectData(IClientAnchor anchor, int storageId, int pictureIndex) + { + XSSFSheet sh = Sheet; + PackagePart sheetPart = sh.GetPackagePart(); + + /* + * The shape id of the ole object seems to be a legacy shape id. + * + * see 5.3.2.1 legacyDrawing (Legacy Drawing Object): + * Legacy Shape ID that is unique throughout the entire document. + * Legacy shape IDs should be assigned based on which portion of the document the + * drawing resides on. The assignment of these ids is broken down into clusters of + * 1024 values. The first cluster is 1-1024, the second 1025-2048 and so on. + * + * Ole shapes seem to start with 1025 on the first sheet ... + * and not sure, if the ids need to be reindexed when sheets are removed + * or more than 1024 shapes are on a given sheet (see #51332 for a similar issue) + */ + XSSFSheet sheet = Sheet; + XSSFWorkbook wb = sheet.Workbook as XSSFWorkbook; + int sheetIndex = wb.GetSheetIndex(sheet); + long shapeId = (sheetIndex+1)*1024 + NewShapeId(); + + // add reference to OLE part + PackagePartName olePN; + try + { + olePN = PackagingUriHelper.CreatePartName("/xl/embeddings/oleObject"+storageId+".bin"); + } + catch(InvalidFormatException e) + { + throw new POIXMLException(e); + } + PackageRelationship olePR = sheetPart.AddRelationship( olePN, TargetMode.Internal, POIXMLDocument.OLE_OBJECT_REL_TYPE ); + + // add reference to image part + XSSFPictureData imgPD = sh.Workbook.GetAllPictures()[pictureIndex] as XSSFPictureData; + PackagePartName imgPN = imgPD.GetPackagePart().PartName; + PackageRelationship imgSheetPR = sheetPart.AddRelationship( imgPN, TargetMode.Internal, PackageRelationshipTypes.IMAGE_PART ); + PackageRelationship imgDrawPR = GetPackagePart().AddRelationship( imgPN, TargetMode.Internal, PackageRelationshipTypes.IMAGE_PART ); + + + // add OLE part metadata to sheet + NPOI.OpenXmlFormats.Spreadsheet.CT_Worksheet cwb = sh.GetCTWorksheet(); + NPOI.OpenXmlFormats.Spreadsheet.CT_OleObjects oo = cwb.IsSetOleObjects() ? cwb.oleObjects : cwb.AddNewOleObjects(); + + NPOI.OpenXmlFormats.Spreadsheet.CT_OleObject ole1 = oo.AddNewOleObject(); + ole1.progId = "Package"; + ole1.shapeId = (uint) shapeId; + ole1.id = olePR.Id; + + //XmlCursor cur1 = ole1.newCursor(); + //cur1.toEndToken(); + //cur1.beginElement("objectPr", XSSFRelation.NS_SPREADSHEETML); + //cur1.insertAttributeWithValue("id", PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS, imgSheetPR.Id); + //cur1.insertAttributeWithValue("defaultSize", "0"); + //cur1.beginElement("anchor", XSSFRelation.NS_SPREADSHEETML); + //cur1.insertAttributeWithValue("moveWithCells", "1"); + + ole1.objectPr.id = imgSheetPR.Id; + ole1.objectPr.defaultSize = false; + ole1.objectPr.anchor.moveWithCells = true; + + CT_TwoCellAnchor ctAnchor = CreateTwoCellAnchor((XSSFClientAnchor)anchor); + + //XmlCursor cur2 = ctAnchor.newCursor(); + //cur2.copyXmlContents(cur1); + //cur2.dispose(); + + //cur1.toParent(); + //cur1.toFirstChild(); + //cur1.setName(new QName(XSSFRelation.NS_SPREADSHEETML, "from")); + //cur1.toNextSibling(); + //cur1.setName(new QName(XSSFRelation.NS_SPREADSHEETML, "to")); + + //cur1.dispose(); + + // add a new shape and link OLE & image part + CT_Shape ctShape = ctAnchor.AddNewSp(); + ctShape.Set(XSSFObjectData.Prototype()); + ctShape.spPr.xfrm = (CreateXfrm((XSSFClientAnchor) anchor)); + + // workaround for not having the vmlDrawing filled + NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_BlipFillProperties blipFill = ctShape.spPr.AddNewBlipFill(); + blipFill.AddNewBlip().embed = imgDrawPR.Id; + blipFill.AddNewStretch().AddNewFillRect(); + + NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_NonVisualDrawingProps cNvPr = ctShape.nvSpPr.cNvPr; + cNvPr.id = (uint)shapeId; + cNvPr.name = "Object "+shapeId; + + //XmlCursor extCur = cNvPr.getExtLst().getExtArray(0).newCursor(); + //extCur.toFirstChild(); + //extCur.setAttributeText(new QName("spid"), "_x0000_s"+shapeId); + //extCur.dispose(); + var ext = cNvPr.extLst.ext[0]; + XmlDocument doc = new XmlDocument(); + doc.LoadXml(ext.Any); + doc.FirstChild.Attributes["spid"].Value = "_x0000_s"+shapeId; + + ext.Any = doc.InnerXml; + + XSSFObjectData shape = new XSSFObjectData(this, ctShape); + shape.anchor = (XSSFClientAnchor) anchor; + + return shape; + } + /** * Returns all charts in this Drawing. */ @@ -483,7 +594,34 @@ private CT_TwoCellAnchor CreateTwoCellAnchor(IClientAnchor anchor) return ctAnchor; } - private long newShapeId() + private CT_Transform2D CreateXfrm(XSSFClientAnchor anchor) + { + CT_Transform2D xfrm = new CT_Transform2D(); + CT_Point2D off = xfrm.AddNewOff(); + off.x = anchor.Dx1; + off.y = anchor.Dy1; + XSSFSheet sheet = Sheet; + double widthPx = 0; + for(int col = anchor.Col1; col + /// get shapes in this shape group + /// + /// + /// list of shapes in this shape group + public List GetShapes(XSSFShapeGroup groupshape) + { + List lst = new List(); + var gs = groupshape.GetCTGroupShape(); + AddShapes(gs, lst); + return lst; + } + + private void AddShapes(CT_GroupShape gs, List lst) + { + XSSFShape shape = null; + foreach(var sp in gs.Shapes) + { + shape = HasOleLink(sp) + ? new XSSFObjectData(this, sp) + : new XSSFSimpleShape(this, sp); + shape.anchor = GetAnchorFromParent(sp.Node); + lst.Add(shape); + } + foreach(var p in gs.Pictures) + { + shape = new XSSFPicture(this, p); + shape.anchor = GetAnchorFromParent(p.Node); + lst.Add(shape); + } + foreach (var c in gs.Connectors) + { + shape = new XSSFConnector(this, c); + shape.anchor = GetAnchorFromParent(c.Node); + lst.Add(shape); + } + foreach(var g in gs.Groups) + { + shape = new XSSFShapeGroup(this, g); + shape.anchor = GetAnchorFromParent(g.Node); + lst.Add(shape); + } + } /** - * - * @return list of shapes in this drawing - */ + * + * @return list of shapes in this drawing + */ public List GetShapes() { List lst = new List(); @@ -515,41 +696,48 @@ public List GetShapes() { shape = new XSSFConnector(this, anchor.connector); } + else if(anchor.sp != null) + { + shape = XSSFDrawing.HasOleLink(anchor.sp) + ? new XSSFObjectData(this, anchor.sp) + : new XSSFSimpleShape(this, anchor.sp); + } else if (anchor.groupShape != null) { - List lstCtShapes = new List(); - anchor.groupShape.GetShapes(lstCtShapes); - - foreach(var s in lstCtShapes) - { - XSSFShape gShape = null; - if(s is CT_Connector connector) - { - gShape = new XSSFConnector(this, connector); - } - else if(s is CT_Picture picture) - { - gShape = new XSSFPicture(this, picture); - } - else if(s is CT_Shape ctShape) - { - gShape = new XSSFSimpleShape(this, ctShape); - } - else if(s is CT_GroupShape groupShape) - { - gShape = new XSSFShapeGroup(this, groupShape); - } - else if(s is CT_GraphicalObjectFrame frame) - { - gShape = new XSSFGraphicFrame(this, frame); - } - if(gShape != null) - { - gShape.anchor = GetAnchorFromIEGAnchor(anchor); - gShape.cellanchor = anchor; - lst.Add(gShape); - } - } + 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.cellanchor = anchor; + // lst.Add(gShape); + // } + //} } else if (anchor.graphicFrame != null) { @@ -566,31 +754,26 @@ public List GetShapes() lst.Add(shape); } } - //foreach (XmlNode obj in xmldoc.SelectNodes("./*/*/*")) - //{ - // XSSFShape shape = null; - // if (obj.LocalName == "sp") - // { - // shape = new XSSFSimpleShape(this, obj); - // } - // else if (obj.LocalName == "pic") - // { - // shape = new XSSFPicture(this, obj); - // } - // else if (obj.LocalName == "cxnSp") - // { - // shape = new XSSFConnector(this, obj); - // } - // // else if (obj is CT_GraphicalObjectFrame) shape = new XSSFGraphicFrame(this, (CT_GraphicalObjectFrame)obj); - // // else if (obj is CT_GroupShape) shape = new XSSFShapeGroup(this, (CT_GroupShape)obj); - // if (shape != null) - // { - // shape.anchor = GetAnchorFromParent(obj); - // lst.Add(shape); - // } - //} + return lst; } + + private static bool HasOleLink(CT_Shape shape) + { + try + { + string uri = shape.nvSpPr.cNvPr.extLst.ext[0].uri; + if("{63B3BB69-23CF-44E3-9099-C40C66FF867C}".Equals(uri)) + { + return true; + } + } + catch + { + return false; + } + return false; + } private XSSFAnchor GetAnchorFromIEGAnchor(IEG_Anchor ctAnchor) { @@ -625,6 +808,13 @@ private static XSSFAnchor GetAnchorFromParent(XmlNode obj) { XSSFAnchor anchor = null; XmlNode parentNode = obj.ParentNode; + while (parentNode != null) + { + if(parentNode.LocalName=="twoCellAnchor"||parentNode.LocalName=="oneCellAnchor" || + parentNode.LocalName=="absoluteAnchor") + break; + parentNode = parentNode.ParentNode; + } XmlNode fromNode = parentNode.SelectSingleNode("xdr:from", POIXMLDocumentPart.NamespaceManager); if(fromNode==null) throw new InvalidDataException("xdr:from node is missing"); @@ -638,6 +828,16 @@ private static XSSFAnchor GetAnchorFromParent(XmlNode obj) anchor = new XSSFClientAnchor(ctFrom, ctTo); return anchor; } + + public IEnumerator GetEnumerator() + { + return GetShapes().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetShapes().GetEnumerator(); + } } } diff --git a/ooxml/XSSF/UserModel/XSSFObjectData.cs b/ooxml/XSSF/UserModel/XSSFObjectData.cs new file mode 100644 index 000000000..363c67cb7 --- /dev/null +++ b/ooxml/XSSF/UserModel/XSSFObjectData.cs @@ -0,0 +1,247 @@ +/* ==================================================================== + 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. +==================================================================== */ + +namespace NPOI.XSSF.UserModel +{ + using NPOI; + using NPOI.OpenXml4Net.OPC; + using NPOI.OpenXmlFormats.Dml; + using NPOI.OpenXmlFormats.Dml.Spreadsheet; + using NPOI.OpenXmlFormats.Spreadsheet; + using NPOI.POIFS.FileSystem; + using NPOI.SS.UserModel; + using NPOI.Util; + using System; + using System.IO; + + /// + /// Represents binary object (i.e. OLE) data stored in the file. Eg. A GIF, JPEG etc... + /// + public class XSSFObjectData : XSSFSimpleShape, IObjectData + { + private static POILogger LOG = POILogFactory.GetLogger(typeof(XSSFObjectData)); + + /// + /// A default instance of CTShape used for creating new shapes. + /// + private static CT_Shape prototype = null; + + private CT_OleObject oleObject; + + public XSSFObjectData(XSSFDrawing drawing, CT_Shape ctShape) + : base(drawing, ctShape) + { + ; + } + + /// + /// Prototype with the default structure of a new auto-shape. + /// + /// + /// Prototype with the default structure of a new auto-shape. + /// + public new static CT_Shape Prototype() + { + //String drawNS = "http://schemas.microsoft.com/office/drawing/2010/main"; + + if (prototype == null) + { + CT_Shape shape = new CT_Shape(); + + CT_ShapeNonVisual nv = shape.AddNewNvSpPr(); + OpenXmlFormats.Dml.Spreadsheet.CT_NonVisualDrawingProps nvp = nv.AddNewCNvPr(); + nvp.id = (/*setter*/1); + nvp.name = (/*setter*/"Shape 1"); + // nvp.Hidden=(/*setter*/true); + CT_OfficeArtExtensionList extLst = nvp.AddNewExtLst(); + // https://msdn.microsoft.com/en-us/library/dd911027(v=office.12).aspx + CT_OfficeArtExtension ext = extLst.AddNewExt(); + ext.uri = (/*setter*/"{63B3BB69-23CF-44E3-9099-C40C66FF867C}"); + //XmlCursor cur = ext.NewCursor(); + //cur.ToEndToken(); + //cur.BeginElement(new QName(drawNS, "compatExt", "a14")); + //cur.InsertNamespace("a14", drawNS); + //cur.InsertAttributeWithValue("spid", "_x0000_s1"); + //cur.Dispose(); + ext.Any = ""; + + nv.AddNewCNvSpPr(); + + OpenXmlFormats.Dml.Spreadsheet.CT_ShapeProperties sp = shape.AddNewSpPr(); + CT_Transform2D t2d = sp.AddNewXfrm(); + CT_PositiveSize2D p1 = t2d.AddNewExt(); + p1.cx = (/*setter*/0); + p1.cy = (/*setter*/0); + CT_Point2D p2 = t2d.AddNewOff(); + p2.x = (/*setter*/0); + p2.y = (/*setter*/0); + + CT_PresetGeometry2D geom = sp.AddNewPrstGeom(); + geom.prst = (/*setter*/ST_ShapeType.rect); + geom.AddNewAvLst(); + + prototype = shape; + } + return prototype; + } + public String OLE2ClassName + { + get + { + return GetOleObject().progId; + } + + } + + /// + /// + /// CTOleObject associated with the shape/// + public CT_OleObject GetOleObject() + { + if (oleObject == null) + { + long shapeId = GetCTShape().nvSpPr.cNvPr.id; + oleObject = GetSheet().ReadOleObject(shapeId); + if (oleObject == null) + { + throw new POIXMLException("Ole object not found in sheet Container - it's probably a control element"); + } + } + return oleObject; + } + public byte[] ObjectData + { + get + { + Stream is1 = GetObjectPart().GetInputStream(); + MemoryStream bos = new MemoryStream(); + IOUtils.Copy(is1, bos); + is1.Close(); + return bos.ToArray(); + } + } + + /// + /// + /// package part of the object data/// + public PackagePart GetObjectPart() + { + if (!GetOleObject().IsSetId()) + { + throw new POIXMLException("Invalid ole object found in sheet Container"); + } + POIXMLDocumentPart pdp = GetSheet().GetRelationById(GetOleObject().id); + return (pdp == null) ? null : pdp.GetPackagePart(); + } + public bool HasDirectoryEntry() + { + Stream is1 = null; + try + { + is1 = GetObjectPart().GetInputStream();// as InputStream; + + // If Clearly doesn't do mark/reset, wrap up + //if (!is1.MarkSupported()) + //{ + // is1 = new PushbackInputStream(is1, 8); + //} + + // Ensure that there is at least some data there + byte[] header8 = IOUtils.PeekFirstNBytes(is1, 8); + + // Try to create + //return NPOIFSFileSystem.HasPOIFSHeader(header8); + return FileMagicContainer.ValueOf(header8) == FileMagic.OLE2; + } + catch (IOException e) + { + LOG.Log(POILogger.WARN, "can't determine if directory entry exists", e); + return false; + } + finally + { + IOUtils.CloseQuietly(is1); + } + } + + public DirectoryEntry Directory + { + get + { + Stream is1 = null; + try + { + is1 = GetObjectPart().GetInputStream(); + return new POIFSFileSystem(is1).Root; + } + finally + { + IOUtils.CloseQuietly(is1); + } + } + } + + /// + /// The filename of the embedded image + /// + public String FileName + { + get + { + return GetObjectPart().PartName.Name; + } + } + + protected XSSFSheet GetSheet() + { + return (XSSFSheet)GetDrawing().GetParent(); + } + public IPictureData PictureData + { + get + { + var oleObj = GetOleObject(); + if(oleObj.objectPr!=null && !string.IsNullOrEmpty(oleObj.objectPr.id)) + { + return (XSSFPictureData) GetSheet().GetRelationById(oleObj.objectPr.id); + } + else + return null; + + //XmlCursor cur = GetOleObject().newCursor(); + //try + //{ + // if (cur.ToChild(XSSFRelation.NS_SPREADSHEETML, "objectPr")) + // { + // String blipId = cur.GetAttributeText(new QName(PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS, "id")); + // return (XSSFPictureData)getSheet().GetRelationById(blipId); + // } + // return null; + //} + //finally + //{ + // cur.Dispose(); + //} + } + } + [Obsolete("PictureData2 is obsolete. Use the PictureData property instead.")] + [Removal(Version = "4.0.0")] + public XSSFPictureData PictureData2 => (XSSFPictureData)PictureData; + + } +} + diff --git a/ooxml/XSSF/UserModel/XSSFShapeGroup.cs b/ooxml/XSSF/UserModel/XSSFShapeGroup.cs index c113e8add..a6c25a9a8 100644 --- a/ooxml/XSSF/UserModel/XSSFShapeGroup.cs +++ b/ooxml/XSSF/UserModel/XSSFShapeGroup.cs @@ -15,13 +15,14 @@ the License. You may obtain a copy of the License at limitations under the License. ==================================================================== */ -using System; -using System.Collections.Generic; using NPOI.OpenXml4Net.OPC; using NPOI.OpenXmlFormats.Dml; using NPOI.OpenXmlFormats.Dml.Spreadsheet; using NPOI.SS.UserModel; using NPOI.Util; +using System; +using System.Collections; +using System.Collections.Generic; namespace NPOI.XSSF.UserModel { @@ -33,7 +34,7 @@ namespace NPOI.XSSF.UserModel * * @author Yegor Kozlov */ - public class XSSFShapeGroup : XSSFShape + public class XSSFShapeGroup : XSSFShape, IShapeContainer { private static CT_GroupShape prototype = null; @@ -223,7 +224,7 @@ BuildFreeForm BFF public XSSFShapeGroup CreateGroup(XSSFChildGroupAnchor anchor) { - CT_GroupShape ctShape = ctGroup.AddNewGroup(); + CT_GroupShape ctShape = ctGroup.AddNewGroupShape(); ctShape.Set(XSSFShapeGroup.Prototype()); XSSFShapeGroup group = new XSSFShapeGroup(GetDrawing(), ctShape) @@ -236,6 +237,29 @@ public XSSFShapeGroup CreateGroup(XSSFChildGroupAnchor anchor) return group; } + public XSSFShapeGroup CreateGroup(XSSFChildAnchor anchor) + { + CT_GroupShape ctShape = ctGroup.AddNewGroupShape(); + ctShape.Set(Prototype()); + + XSSFShapeGroup shape = new XSSFShapeGroup(GetDrawing(), ctShape); + shape.parent = this; + shape.anchor = anchor; + + // TODO: calculate bounding rectangle on anchor and set off/ext correctly + + CT_GroupTransform2D xfrm = shape.GetCTGroupShape().grpSpPr.xfrm; + CT_Transform2D t2 = anchor.GetCTTransform2D(); + xfrm.off = t2.off; + xfrm.ext = t2.ext; + // child offset is left to 0,0 + xfrm.chExt = t2.ext; + xfrm.flipH = t2.flipH; + xfrm.flipV = t2.flipV; + + return shape; + } + public CT_GroupShape GetCTGroupShape() { return ctGroup; @@ -268,7 +292,8 @@ protected internal override NPOI.OpenXmlFormats.Dml.Spreadsheet.CT_ShapeProperti throw new InvalidOperationException("Not supported for shape group"); } - public void AutoFit(SS.UserModel.ISheet Sheet) { + public void AutoFit(SS.UserModel.ISheet Sheet) + { var min = new Coords(long.MaxValue, long.MaxValue); var max = new Coords(long.MinValue, long.MinValue); @@ -337,5 +362,22 @@ private static void AutoFit(CT_GroupShape CtGroup, Coords Min, Coords Max) Max.Max(max); } } + + public IEnumerator GetEnumerator() + { + return GetDrawing().GetShapes(this).GetEnumerator(); + } + IEnumerator IEnumerable.GetEnumerator() + { + return GetDrawing().GetShapes(this).GetEnumerator(); + } + + public override string ShapeName + { + get + { + return ctGroup.nvGrpSpPr.cNvPr.name; + } + } } } diff --git a/ooxml/XSSF/UserModel/XSSFSheet.cs b/ooxml/XSSF/UserModel/XSSFSheet.cs index 267f86118..1a4963088 100644 --- a/ooxml/XSSF/UserModel/XSSFSheet.cs +++ b/ooxml/XSSF/UserModel/XSSFSheet.cs @@ -1884,7 +1884,7 @@ public XSSFDrawing GetDrawingPatriarch() /// Contains a Drawing - return that. /// /// a SpreadsheetML Drawing - public IDrawing CreateDrawingPatriarch() + public IDrawing CreateDrawingPatriarch() { OpenXmlFormats.Spreadsheet.CT_Drawing ctDrawing = GetCTDrawing(); if(ctDrawing != null) @@ -3746,7 +3746,7 @@ public List GetTables() #region ISheet Members - public IDrawing DrawingPatriarch + public IDrawing DrawingPatriarch { get { @@ -5931,7 +5931,7 @@ private void CopySheetImages(XSSFWorkbook destWorkbook, XSSFSheet destSheet) XSSFDrawing sheetDrawing = GetDrawingPatriarch(); if(sheetDrawing != null) { - IDrawing destDraw = destSheet.CreateDrawingPatriarch(); + IDrawing destDraw = destSheet.CreateDrawingPatriarch(); IList sheetPictures = sheetDrawing.GetRelations(); Dictionary pictureIdMapping = new Dictionary(); foreach(IEG_Anchor anchor in sheetDrawing.GetCTDrawing().CellAnchors) @@ -6272,6 +6272,97 @@ private static HashSet GetErrorTypes(CT_IgnoredError err) return result; } + /** + * Determine the OleObject which links shapes with embedded resources + * + * @param shapeId the shape id + * @return the CTOleObject of the shape + */ + public CT_OleObject ReadOleObject(long shapeId) + { + if (!GetCTWorksheet().IsSetOleObjects()) + { + return null; + } + + CT_OleObjects objs = GetCTWorksheet().oleObjects; + CT_OleObject coo = null; + foreach(var obj in objs.oleObject) + { + if((long) obj.shapeId!=shapeId) + { + continue; + } + coo = obj; + if(coo.objectPr!=null) + { + break; + } + } + + return coo; + // we use a XmlCursor here to handle oleObject with-/out AlternateContent wrappers + //String xquery = "declare namespace p='" + XSSFRelation.NS_SPREADSHEETML + "' .//p:oleObject"; + //XmlCursor cur = GetCTWorksheet().oleObjects.newCursor(); + //try + //{ + // cur.selectPath(xquery); + // CT_OleObject coo = null; + // while (cur.toNextSelection()) + // { + // String sId = cur.getAttributeText(new QName(null, "shapeId")); + // if (sId == null || long.Parse(sId) != shapeId) + // { + // continue; + // } + + // XmlObject xObj = cur.getObject(); + // if (xObj is CT_OleObject) { + // // the unusual case ... + // coo = (CT_OleObject)xObj; + // } else + // { + // XMLStreamReader reader = cur.newXMLStreamReader(); + // try + // { + // CT_OleObjects coos = CTOleObjects.Factory.parse(reader); + // if (coos.SizeOfOleObjectArray() == 0) + // { + // continue; + // } + // coo = coos.GetOleObjectArray(0); + // } + // catch (XmlException e) + // { + // logger.log(POILogger.INFO, "can't parse CTOleObjects", e); + // } + // finally + // { + // try + // { + // reader.close(); + // } + // catch (XMLStreamException e) + // { + // logger.Log(POILogger.INFO, "can't close reader", e); + // } + // } + // } + + // // there are choice and fallback OleObject ... we prefer the one having the objectPr element, + // // which is in the choice element + // if (cur.toChild(XSSFRelation.NS_SPREADSHEETML, "objectPr")) + // { + // break; + // } + // } + // return (coo == null) ? null : coo; + //} + //finally + //{ + // cur.dispose(); + //} + } #endregion #region Helper classes diff --git a/ooxml/XSSF/UserModel/XSSFWorkbook.cs b/ooxml/XSSF/UserModel/XSSFWorkbook.cs index 60b280a5c..add3f0466 100644 --- a/ooxml/XSSF/UserModel/XSSFWorkbook.cs +++ b/ooxml/XSSF/UserModel/XSSFWorkbook.cs @@ -37,6 +37,7 @@ limitations under the License. using NPOI.SS; using System.Globalization; using System.Linq; +using NPOI.POIFS.FileSystem; namespace NPOI.XSSF.UserModel { @@ -2587,6 +2588,54 @@ public void SetVBAProject(XSSFWorkbook macroWorkbook) } } + /// + /// Adds an OLE package manager object with the given content to the sheet + /// + /// the payload + /// the label of the payload + /// the original filename + /// the command to open the payload + /// the index of the added ole object, i.e. the storage id + /// if the object can't be embedded + public int AddOlePackage(byte[] oleData, String label, String fileName, String command) + { + // find an unused part name + OPCPackage opc = Package; + PackagePartName pnOLE; + int oleId=0; + do + { + try + { + pnOLE = PackagingUriHelper.CreatePartName("/xl/embeddings/oleObject"+(++oleId)+".bin"); + } + catch(InvalidFormatException e) + { + throw new IOException("ole object name not recognized", e); + } + } while(opc.ContainPart(pnOLE)); + + PackagePart pp = opc.CreatePart( pnOLE, "application/vnd.openxmlformats-officedocument.oleObject" ); + + Ole10Native ole10 = new Ole10Native(label, fileName, command, oleData); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(oleData.Length+500); + ole10.WriteOut(bos); + + POIFSFileSystem poifs = new POIFSFileSystem(); + DirectoryNode root = poifs.Root; + root.CreateDocument(Ole10Native.OLE10_NATIVE, new ByteArrayInputStream(bos.ToByteArray())); + root.StorageClsid = (HPSF.ClassID.OLE10_PACKAGE); + + // TODO: generate CombObj stream + + Stream os = pp.GetOutputStream(); + poifs.WriteFileSystem(os); + os.Close(); + poifs.Close(); + + return oleId; + } #endregion #region IList Members diff --git a/openxml4Net/OPC/PackageRelationshipTypes.cs b/openxml4Net/OPC/PackageRelationshipTypes.cs index 9f90ce781..eeb62fb83 100644 --- a/openxml4Net/OPC/PackageRelationshipTypes.cs +++ b/openxml4Net/OPC/PackageRelationshipTypes.cs @@ -32,6 +32,11 @@ public static class PackageRelationshipTypes */ public const string CORE_PROPERTIES_ECMA376 = "http://schemas.openxmlformats.org/officedocument/2006/relationships/metadata/core-properties"; + /** + * Namespace of Core properties relationship type as defiend in ECMA 376 + */ + public const string CORE_PROPERTIES_ECMA376_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; + /** * Digital signature relationship type. */ diff --git a/testcases/main/HSSF/Model/TestEscherRecordFactory.cs b/testcases/main/HSSF/Model/TestEscherRecordFactory.cs index 0517f01a4..4b1f6ab1a 100644 --- a/testcases/main/HSSF/Model/TestEscherRecordFactory.cs +++ b/testcases/main/HSSF/Model/TestEscherRecordFactory.cs @@ -91,7 +91,7 @@ public void TestDgContainerMustBeRootOfHSSFSheetEscherRecords() // records to be aggregated List dgRecords = records.GetRange(19, 23 - 19); byte[] dgBytes = toByteArray(dgRecords); - IDrawing d = sh.DrawingPatriarch; + IDrawing d = sh.DrawingPatriarch; EscherAggregate agg = (EscherAggregate)ish.FindFirstRecordBySid(EscherAggregate.sid); ClassicAssert.AreEqual(true, agg.EscherRecords[0] is EscherContainerRecord); ClassicAssert.AreEqual(EscherContainerRecord.DG_CONTAINER, agg.EscherRecords[0].RecordId); diff --git a/testcases/main/HSSF/UserModel/TestBugs.cs b/testcases/main/HSSF/UserModel/TestBugs.cs index 4fee45b17..ca2a09167 100644 --- a/testcases/main/HSSF/UserModel/TestBugs.cs +++ b/testcases/main/HSSF/UserModel/TestBugs.cs @@ -16,7 +16,6 @@ limitations under the License. ==================================================================== */ using NPOI; -using NPOI.HSSF.Record.Aggregates; namespace TestCases.HSSF.UserModel { @@ -24,15 +23,12 @@ namespace TestCases.HSSF.UserModel using System.IO; using System.Text; using System.Collections; - using System.Linq; using NUnit.Framework;using NUnit.Framework.Legacy; using TestCases.HSSF; using NPOI.HSSF.UserModel; - //using NPOI.HSSF.Model; using NPOI.HSSF.Record; - using NPOI.SS.Formula; using NPOI.HSSF.Record.Aggregates; using NPOI.SS.Util; using NPOI.Util; @@ -1147,13 +1143,13 @@ public void Test44840() // Doesn't have a directory ClassicAssert.IsFalse(obj.HasDirectoryEntry()); - ClassicAssert.IsNotNull(obj.GetObjectData()); - ClassicAssert.AreEqual(12, obj.GetObjectData().Length); + ClassicAssert.IsNotNull(obj.ObjectData); + ClassicAssert.AreEqual(12, obj.ObjectData.Length); ClassicAssert.AreEqual("Forms.CheckBox.1", obj.OLE2ClassName); try { - obj.GetDirectory(); + var _ = obj.Directory; Assert.Fail(); } catch (FileNotFoundException) diff --git a/testcases/main/HSSF/UserModel/TestEmbeddedObjects.cs b/testcases/main/HSSF/UserModel/TestEmbeddedObjects.cs index 85aafe1ce..657b01942 100644 --- a/testcases/main/HSSF/UserModel/TestEmbeddedObjects.cs +++ b/testcases/main/HSSF/UserModel/TestEmbeddedObjects.cs @@ -36,8 +36,8 @@ public void TestReadExistingObject() IList list = wb.GetAllEmbeddedObjects(); ClassicAssert.AreEqual(list.Count, 1); HSSFObjectData obj = list[0]; - ClassicAssert.IsNotNull(obj.GetObjectData()); - ClassicAssert.IsNotNull(obj.GetDirectory()); + ClassicAssert.IsNotNull(obj.ObjectData); + ClassicAssert.IsNotNull(obj.Directory); ClassicAssert.IsNotNull(obj.OLE2ClassName); } @@ -52,7 +52,7 @@ public void TestReadNestedObject() IList list = wb.GetAllEmbeddedObjects(); ClassicAssert.AreEqual(list.Count, 1); HSSFObjectData obj = list[0]; - ClassicAssert.IsNotNull(obj.GetObjectData()); + ClassicAssert.IsNotNull(obj.ObjectData); ClassicAssert.IsNotNull(obj.OLE2ClassName); } diff --git a/testcases/main/HSSF/UserModel/TestHSSFComment.cs b/testcases/main/HSSF/UserModel/TestHSSFComment.cs index 497a9e393..4c3a684a1 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFComment.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFComment.cs @@ -92,7 +92,7 @@ public void TestBug56380InsertComments() { HSSFWorkbook workbook = new HSSFWorkbook(); ISheet sheet = workbook.CreateSheet(); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); int noOfRows = 1025; String comment = "c"; @@ -132,7 +132,7 @@ public void TestBug56380InsertTooManyComments() try { ISheet sheet = workbook.CreateSheet(); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); String comment = "c"; for (int rowNum = 0; rowNum < 258; rowNum++) @@ -181,7 +181,7 @@ private void CheckComments(ISheet sheet, int noOfRows, String comment) } } - private IComment insertComment(IDrawing Drawing, ICell cell, String message) + private IComment insertComment(IDrawing Drawing, ICell cell, String message) { ICreationHelper factory = cell.Sheet.Workbook.GetCreationHelper(); diff --git a/testcases/main/HSSF/UserModel/TestHSSFPatriarch.cs b/testcases/main/HSSF/UserModel/TestHSSFPatriarch.cs index d1353cc78..9eca56808 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFPatriarch.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFPatriarch.cs @@ -36,7 +36,7 @@ public void TestBasic() HSSFWorkbook wb = new HSSFWorkbook(); NPOI.SS.UserModel.ISheet sheet = wb.CreateSheet(); - IDrawing patr = sheet.CreateDrawingPatriarch(); + IDrawing patr = sheet.CreateDrawingPatriarch(); ClassicAssert.IsNotNull(patr); @@ -52,10 +52,10 @@ public void Test44916() NPOI.SS.UserModel.ISheet sheet = wb.CreateSheet(); // 1. Create drawing patriarch - IDrawing patr = sheet.CreateDrawingPatriarch(); + IDrawing patr = sheet.CreateDrawingPatriarch(); // 2. Try to re-get the patriarch - IDrawing existingPatr; + IDrawing existingPatr; try { existingPatr = sheet.DrawingPatriarch; diff --git a/testcases/main/HSSF/UserModel/TestHSSFPicture.cs b/testcases/main/HSSF/UserModel/TestHSSFPicture.cs index e6aade582..a94eceb70 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFPicture.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFPicture.cs @@ -63,7 +63,7 @@ public void Bug45829() { HSSFWorkbook wb = new HSSFWorkbook(); NPOI.SS.UserModel.ISheet sh1 = wb.CreateSheet(); - IDrawing p1 = sh1.CreateDrawingPatriarch(); + IDrawing p1 = sh1.CreateDrawingPatriarch(); byte[] pictureData = HSSFTestDataSamples.GetTestDataFileContent("45829.png"); int idx1 = wb.AddPicture(pictureData, PictureType.PNG); @@ -78,7 +78,7 @@ public void AddPictures() IWorkbook wb = new HSSFWorkbook(); ISheet sh = wb.CreateSheet("Pictures"); - IDrawing dr = sh.CreateDrawingPatriarch(); + IDrawing dr = sh.CreateDrawingPatriarch(); ClassicAssert.AreEqual(0, ((HSSFPatriarch)dr).Children.Count); IClientAnchor anchor = wb.GetCreationHelper().CreateClientAnchor(); diff --git a/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs b/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs index 3a9b01f7c..3d33498b8 100644 --- a/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs +++ b/testcases/main/HSSF/UserModel/TestHSSFWorkbook.cs @@ -677,7 +677,7 @@ public void WordDocEmbeddedInXls() { if (embeddedObject.HasDirectoryEntry()) { - DirectoryEntry dir = embeddedObject.GetDirectory(); + DirectoryEntry dir = embeddedObject.Directory; if (dir is DirectoryNode) { DirectoryNode dNode = (DirectoryNode)dir; diff --git a/testcases/main/HSSF/UserModel/TestOLE2Embeding.cs b/testcases/main/HSSF/UserModel/TestOLE2Embeding.cs index b6c46e68c..9c0512b97 100644 --- a/testcases/main/HSSF/UserModel/TestOLE2Embeding.cs +++ b/testcases/main/HSSF/UserModel/TestOLE2Embeding.cs @@ -60,9 +60,9 @@ public void TestEmbeddedObjects() IList objects = workbook.GetAllEmbeddedObjects(); ClassicAssert.AreEqual(2, objects.Count, "Wrong number of objects"); - ClassicAssert.AreEqual("MBD06CAB431", objects[0].GetDirectory().Name, + ClassicAssert.AreEqual("MBD06CAB431", objects[0].Directory.Name, "Wrong name for first object"); - ClassicAssert.AreEqual("MBD06CAC85A", objects[1].GetDirectory().Name, "Wrong name for second object"); + ClassicAssert.AreEqual("MBD06CAC85A", objects[1].Directory.Name, "Wrong name for second object"); workbook.Close(); } @@ -126,19 +126,19 @@ public void TestReallyEmbedSomething() MemoryStream bos = new MemoryStream(); HSSFObjectData od = wb2.GetAllEmbeddedObjects()[0]; - Ole10Native ole10 = Ole10Native.CreateFromEmbeddedOleObject((DirectoryNode)od.GetDirectory()); + Ole10Native ole10 = Ole10Native.CreateFromEmbeddedOleObject((DirectoryNode)od.Directory); bos = new MemoryStream(); pptPoifs.WriteFileSystem(bos); ClassicAssert.IsTrue(Arrays.Equals(ole10.DataBuffer, bos.ToArray())); od = wb2.GetAllEmbeddedObjects()[1]; - ole10 = Ole10Native.CreateFromEmbeddedOleObject((DirectoryNode)od.GetDirectory()); + ole10 = Ole10Native.CreateFromEmbeddedOleObject((DirectoryNode)od.Directory); bos = new MemoryStream(); xlsPoifs.WriteFileSystem(bos); ClassicAssert.IsTrue(Arrays.Equals(ole10.DataBuffer, bos.ToArray())); od = wb2.GetAllEmbeddedObjects()[2]; - ole10 = Ole10Native.CreateFromEmbeddedOleObject((DirectoryNode)od.GetDirectory()); + ole10 = Ole10Native.CreateFromEmbeddedOleObject((DirectoryNode)od.Directory); ClassicAssert.IsTrue(Arrays.Equals(ole10.DataBuffer, GetSampleTXT())); xlsPoifs.Close(); diff --git a/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs b/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs index 350e09e4f..641a11c82 100644 --- a/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs +++ b/testcases/main/SS/UserModel/BaseTestBugzillaIssues.cs @@ -1085,7 +1085,7 @@ public void Test57973() ICreationHelper factory = wb.GetCreationHelper(); ISheet sheet = wb.CreateSheet(); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = factory.CreateClientAnchor(); ICell cell0 = sheet.CreateRow(0).CreateCell(0); @@ -1517,7 +1517,7 @@ public void Bug59393_commentsCanHaveSameAnchor() ICreationHelper helper = wb.GetCreationHelper(); IClientAnchor anchor = helper.CreateClientAnchor(); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); IRow row = sheet.CreateRow(0); diff --git a/testcases/main/SS/UserModel/BaseTestCell.cs b/testcases/main/SS/UserModel/BaseTestCell.cs index d18f48b6e..0f67cafec 100644 --- a/testcases/main/SS/UserModel/BaseTestCell.cs +++ b/testcases/main/SS/UserModel/BaseTestCell.cs @@ -987,7 +987,7 @@ public void GetCellComment() anchor.Col2 = cell.ColumnIndex + 1; anchor.Row1 = row.RowNum; anchor.Row2 = row.RowNum + 3; - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); IComment comment = drawing.CreateCellComment(anchor); IRichTextString str = factory.CreateRichTextString("Hello, World!"); comment.String = str; diff --git a/testcases/main/SS/UserModel/BaseTestCellComment.cs b/testcases/main/SS/UserModel/BaseTestCellComment.cs index 51ef559e3..18f74ff63 100644 --- a/testcases/main/SS/UserModel/BaseTestCellComment.cs +++ b/testcases/main/SS/UserModel/BaseTestCellComment.cs @@ -73,7 +73,7 @@ public void Create() ClassicAssert.IsNull(cell.CellComment); ClassicAssert.IsNull(sheet.GetCellComment(new CellAddress(cellRow, cellColumn))); - IDrawing patr = sheet.CreateDrawingPatriarch(); + IDrawing patr = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = factory.CreateClientAnchor(); anchor.Col1=(2); anchor.Col2=(5); @@ -264,7 +264,7 @@ public void QuickGuide() ICell cell = sheet.CreateRow(3).CreateCell(5); cell.SetCellValue("F4"); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = factory.CreateClientAnchor(); IComment comment = drawing.CreateCellComment(anchor); @@ -299,7 +299,7 @@ public void GetClientAnchor() ICell cell = row.CreateCell(5); ICreationHelper factory = wb.GetCreationHelper(); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); double r_mul, c_mul; if (sheet is HSSFSheet) @@ -376,7 +376,7 @@ public void AttemptToSave2CommentsWithSameCoordinates() IWorkbook wb = _testDataProvider.CreateWorkbook(); ISheet sh = wb.CreateSheet(); ICreationHelper factory = wb.GetCreationHelper(); - IDrawing patriarch = sh.CreateDrawingPatriarch(); + IDrawing patriarch = sh.CreateDrawingPatriarch(); patriarch.CreateCellComment(factory.CreateClientAnchor()); try @@ -407,7 +407,7 @@ public void GetAddress() IWorkbook wb = _testDataProvider.CreateWorkbook(); ISheet sh = wb.CreateSheet(); ICreationHelper factory = wb.GetCreationHelper(); - IDrawing patriarch = sh.CreateDrawingPatriarch(); + IDrawing patriarch = sh.CreateDrawingPatriarch(); IComment comment = patriarch.CreateCellComment(factory.CreateClientAnchor()); ClassicAssert.AreEqual(CellAddress.A1, comment.Address); @@ -422,7 +422,7 @@ public void SetAddress() IWorkbook wb = _testDataProvider.CreateWorkbook(); ISheet sh = wb.CreateSheet(); ICreationHelper factory = wb.GetCreationHelper(); - IDrawing patriarch = sh.CreateDrawingPatriarch(); + IDrawing patriarch = sh.CreateDrawingPatriarch(); IComment comment = patriarch.CreateCellComment(factory.CreateClientAnchor()); ClassicAssert.AreEqual(CellAddress.A1, comment.Address); diff --git a/testcases/main/SS/UserModel/BaseTestPicture.cs b/testcases/main/SS/UserModel/BaseTestPicture.cs index 74e10a506..161d5212a 100644 --- a/testcases/main/SS/UserModel/BaseTestPicture.cs +++ b/testcases/main/SS/UserModel/BaseTestPicture.cs @@ -80,11 +80,11 @@ public void TestResizeNoColumns() IRow row = sheet.CreateRow(0); - handleResize(wb, sheet, row); + HandleResize(wb, sheet, row); } finally { - //wb.Close(); + wb.Close(); } } @@ -99,7 +99,7 @@ public void TestResizeWithColumns() IRow row = sheet.CreateRow(0); row.CreateCell(0); - handleResize(wb, sheet, row); + HandleResize(wb, sheet, row); } finally { @@ -108,10 +108,10 @@ public void TestResizeWithColumns() } - private void handleResize(IWorkbook wb, ISheet sheet, IRow row) + private void HandleResize(IWorkbook wb, ISheet sheet, IRow row) { - IDrawing Drawing = sheet.CreateDrawingPatriarch(); - ICreationHelper CreateHelper = wb.GetCreationHelper(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); + ICreationHelper createHelper = wb.GetCreationHelper(); byte[] bytes = HSSFITestDataProvider.Instance.GetTestDataFileContent("logoKarmokar4.png"); @@ -120,13 +120,13 @@ private void handleResize(IWorkbook wb, ISheet sheet, IRow row) int pictureIdx = wb.AddPicture(bytes, PictureType.PNG); //add a picture shape - IClientAnchor anchor = CreateHelper.CreateClientAnchor(); + IClientAnchor anchor = createHelper.CreateClientAnchor(); //set top-left corner of the picture, //subsequent call of Picture#resize() will operate relative to it anchor.Col1 = (/*setter*/0); anchor.Row1 = (/*setter*/0); - IPicture pict = Drawing.CreatePicture(anchor, pictureIdx); + IPicture pict = drawing.CreatePicture(anchor, pictureIdx); //auto-size picture relative to its top-left corner pict.Resize(); diff --git a/testcases/main/SS/UserModel/BaseTestSheet.cs b/testcases/main/SS/UserModel/BaseTestSheet.cs index 31bc6bf9f..d921c55c9 100644 --- a/testcases/main/SS/UserModel/BaseTestSheet.cs +++ b/testcases/main/SS/UserModel/BaseTestSheet.cs @@ -1228,7 +1228,7 @@ public virtual void GetCellComment() { IWorkbook workbook = _testDataProvider.CreateWorkbook(); ISheet sheet = workbook.CreateSheet(); - IDrawing dg = sheet.CreateDrawingPatriarch(); + IDrawing dg = sheet.CreateDrawingPatriarch(); IComment comment = dg.CreateCellComment(workbook.GetCreationHelper().CreateClientAnchor()); ICell cell = sheet.CreateRow(9).CreateCell(2); comment.Author = (/*setter*/"test C10 author"); @@ -1253,7 +1253,7 @@ public void GetCellComments() //assertEquals(Collections.emptyMap(), sheet.getCellComments()); ClassicAssert.AreEqual(0, sheet.GetCellComments().Count); - IDrawing dg = sheet.CreateDrawingPatriarch(); + IDrawing dg = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = workbook.GetCreationHelper().CreateClientAnchor(); int nRows = 5; diff --git a/testcases/main/SS/UserModel/BaseTestWorkbook.cs b/testcases/main/SS/UserModel/BaseTestWorkbook.cs index edef10c5b..d0c523377 100644 --- a/testcases/main/SS/UserModel/BaseTestWorkbook.cs +++ b/testcases/main/SS/UserModel/BaseTestWorkbook.cs @@ -961,7 +961,7 @@ public virtual void CreateDrawing() byte[] pictureData = _testDataProvider.GetTestDataFileContent("logoKarmokar4.png"); int handle = wb.AddPicture(pictureData, PictureType.PNG); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); ICreationHelper helper = wb.GetCreationHelper(); IClientAnchor anchor = helper.CreateClientAnchor(); anchor.AnchorType = (/*setter*/AnchorType.DontMoveAndResize); diff --git a/testcases/ooxml/SS/Extractor/TestEmbeddedExtractor.cs b/testcases/ooxml/SS/Extractor/TestEmbeddedExtractor.cs new file mode 100644 index 000000000..d84eecd8c --- /dev/null +++ b/testcases/ooxml/SS/Extractor/TestEmbeddedExtractor.cs @@ -0,0 +1,132 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + +using System; +using System.Collections.Generic; +using System.IO; + +namespace TestCases.SS.Extractor +{ + using NPOI.HSSF.UserModel; + using NPOI.SS.Extractor; + using NPOI.SS.UserModel; + using NUnit.Framework; + using NUnit.Framework.Legacy; + using System.Security.Cryptography; + using TestCases.HSSF; + + [TestFixture] + public class TestEmbeddedExtractor + { + private static POIDataSamples samples = POIDataSamples.GetSpreadSheetInstance(); + + [Test] + public void ExtractPDFfromEMF() + { + Stream fis = samples.OpenResourceAsStream("Basic_Expense_Template_2011.xls"); + IWorkbook wb = WorkbookFactory.Create(fis); + fis.Close(); + + EmbeddedExtractor ee = new EmbeddedExtractor(); + List edList = new List(); + foreach (ISheet s in wb) { + edList.AddRange(ee.ExtractAll(s)); + } + wb.Close(); + + ClassicAssert.AreEqual(2, edList.Count); + + string filename1 = "Sample.pdf"; + EmbeddedData ed0 = edList[0]; + ClassicAssert.AreEqual(filename1, ed0.Filename); + ClassicAssert.AreEqual(filename1, ed0.Shape.ShapeName.Trim()); + ClassicAssert.AreEqual("uNplB1QpYug+LWappiTh0w==", md5hash(ed0.GetEmbeddedData())); + + string filename2 = "kalastuslupa_jiyjhnj_yuiyuiyuio_uyte_sldfsdfsdf_sfsdfsdf_sfsssfsf_sdfsdfsdfsdf_sdfsdfsdf.pdf"; + EmbeddedData ed1 = edList[1]; + ClassicAssert.AreEqual(filename2, ed1.Filename); + ClassicAssert.AreEqual(filename2, ed1.Shape.ShapeName.Trim()); + ClassicAssert.AreEqual("QjLuAZ+cd7KbhVz4sj+QdA==", md5hash(ed1.GetEmbeddedData())); + } + + [Test] + public void ExtractFromXSSF() + { + Stream fis = samples.OpenResourceAsStream("58325_db.xlsx"); + IWorkbook wb = WorkbookFactory.Create(fis); + fis.Close(); + + EmbeddedExtractor ee = new EmbeddedExtractor(); + List edList = new List(); + foreach (ISheet s in wb) { + edList.AddRange(ee.ExtractAll(s)); + } + wb.Close(); + + ClassicAssert.AreEqual(4, edList.Count); + EmbeddedData ed0 = edList[0]; + ClassicAssert.AreEqual("Object 1.pdf", ed0.Filename); + ClassicAssert.AreEqual("Object 1", ed0.Shape.ShapeName.Trim()); + ClassicAssert.AreEqual("Oyys6UtQU1gbHYBYqA4NFA==", md5hash(ed0.GetEmbeddedData())); + + EmbeddedData ed1 = edList[1]; + ClassicAssert.AreEqual("Object 2.pdf", ed1.Filename); + ClassicAssert.AreEqual("Object 2", ed1.Shape.ShapeName.Trim()); + ClassicAssert.AreEqual("xLScPUS0XH+5CTZ2A3neNw==", md5hash(ed1.GetEmbeddedData())); + + EmbeddedData ed2 = edList[2]; + ClassicAssert.AreEqual("Object 3.pdf", ed2.Filename); + ClassicAssert.AreEqual("Object 3", ed2.Shape.ShapeName.Trim()); + ClassicAssert.AreEqual("rX4klZqJAeM5npb54Gi2+Q==", md5hash(ed2.GetEmbeddedData())); + + EmbeddedData ed3 = edList[3]; + ClassicAssert.AreEqual("Microsoft_Excel_Worksheet1.xlsx", ed3.Filename); + ClassicAssert.AreEqual("Object 1", ed3.Shape.ShapeName.Trim()); + ClassicAssert.AreEqual("4m4N8ji2tjpEGPQuw2YwGA==", md5hash(ed3.GetEmbeddedData())); + } + + public static string md5hash(byte[] input) { + + MD5 md5 = MD5.Create(); + + byte[] hash = md5.ComputeHash(input); + return Convert.ToBase64String(hash); + } + + + [Test] + public void TestNPE() + { + HSSFWorkbook wb = HSSFTestDataSamples.OpenSampleWorkbook("angelo.edu_content_files_19555-nsse-2011-multiyear-benchmark.xls"); + EmbeddedExtractor ee = new EmbeddedExtractor(); + + foreach (ISheet s in wb) + { + foreach (EmbeddedData ed in ee.ExtractAll(s)) + { + ClassicAssert.IsNotNull(ed.Filename); + ClassicAssert.IsNotNull(ed.GetEmbeddedData()); + ClassicAssert.IsNotNull(ed.Shape); + } + } + + } + } +} + + diff --git a/testcases/ooxml/SS/UserModel/TestEmbedOLEPackage.cs b/testcases/ooxml/SS/UserModel/TestEmbedOLEPackage.cs new file mode 100644 index 000000000..1d42ce910 --- /dev/null +++ b/testcases/ooxml/SS/UserModel/TestEmbedOLEPackage.cs @@ -0,0 +1,135 @@ +/* ==================================================================== + 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; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace TestCases.SS.UserModel +{ + using NPOI.HSSF; + using NPOI.HSSF.UserModel; + using NPOI.SS.Extractor; + using NPOI.SS.UserModel; + using NPOI.Util; + using NPOI.XSSF; + using NPOI.XSSF.UserModel; + using NUnit.Framework; + using NUnit.Framework.Legacy; + + [TestFixture] + public class TestEmbedOLEPackage + { + [Test] + public void EmbedXSSF() + { + + IWorkbook wb1 = new XSSFWorkbook(); + ISheet sh = wb1.CreateSheet(); + int picIdx = wb1.AddPicture(GetSamplePng(), PictureType.PNG); + byte[] samplePPTX = GetSamplePPT(true); + int oleIdx = wb1.AddOlePackage(samplePPTX, "dummy.pptx", "dummy.pptx", "dummy.pptx"); + + IDrawing pat = sh.CreateDrawingPatriarch(); + IClientAnchor anchor = pat.CreateAnchor(0, 0, 0, 0, 1, 1, 3, 6); + pat.CreateObjectData(anchor, oleIdx, picIdx); + + IWorkbook wb2 = XSSFTestDataSamples.WriteOutAndReadBack(wb1); + + pat = wb2.GetSheetAt(0).DrawingPatriarch; + var enu = pat.GetEnumerator(); + enu.MoveNext(); + ClassicAssert.IsTrue(enu.Current is IObjectData); + + EmbeddedExtractor ee = new EmbeddedExtractor(); + EmbeddedData ed = ee.ExtractAll(wb2.GetSheetAt(0))[0]; + + CollectionAssert.AreEqual(samplePPTX, ed.GetEmbeddedData()); + + wb2.Close(); + wb1.Close(); + } + + [Test] + public void EmbedHSSF() + { + + //try + //{ + // Class.forName("NPOI.HSLF.UserModel.HSLFSlideShow"); + //} + //catch(Exception e) + //{ + // assumeTrue(false); + //} + + IWorkbook wb1 = new HSSFWorkbook(); + ISheet sh = wb1.CreateSheet(); + int picIdx = wb1.AddPicture(GetSamplePng(), PictureType.PNG); + byte[] samplePPT = GetSamplePPT(false); + int oleIdx = wb1.AddOlePackage(samplePPT, "dummy.ppt", "dummy.ppt", "dummy.ppt"); + + IDrawing pat = sh.CreateDrawingPatriarch(); + IClientAnchor anchor = pat.CreateAnchor(0, 0, 0, 0, 1, 1, 3, 6); + pat.CreateObjectData(anchor, oleIdx, picIdx); + + IWorkbook wb2 = HSSF.HSSFTestDataSamples.WriteOutAndReadBack((HSSFWorkbook)wb1); + + pat = wb2.GetSheetAt(0).DrawingPatriarch; + var enu = pat.GetEnumerator(); + enu.MoveNext(); + ClassicAssert.IsTrue(enu.Current is IObjectData); + + EmbeddedExtractor ee = new EmbeddedExtractor(); + EmbeddedData ed = ee.ExtractAll(wb2.GetSheetAt(0))[0]; + CollectionAssert.AreEqual(samplePPT, ed.GetEmbeddedData()); + + wb2.Close(); + wb1.Close(); + } + + static byte[] GetSamplePng() + { + var provider = XSSFITestDataProvider.instance; + return provider.GetTestDataFileContent("logoKarmokar4.png"); + } + + static byte[] GetSamplePPT(bool ooxml) + { + var provider = POIDataSamples.GetSlideShowInstance(); + string filename = ooxml ? "49386-null_dates.pptx":"41071.ppt"; + return provider.ReadFile(filename); + //SlideShow ppt = (ooxml) ? new XMLSlideShow() : new NPOI.HSLF.UserModel.HSLFSlideShow(); + //Slide slide = ppt.CreateSlide(); + + //AutoShape sh1 = slide.CreateAutoShape(); + //sh1.SetShapeType(ShapeType.STAR_32); + //sh1.SetAnchor(new java.awt.Rectangle(50, 50, 100, 200)); + //sh1.SetFillColor(java.awt.Color.red); + + //MemoryStream bos = new MemoryStream(); + //ppt.write(bos); + //ppt.Close(); + + //return bos.ToArray(); + } + } +} + diff --git a/testcases/ooxml/XSSF/Model/TestCommentsTable.cs b/testcases/ooxml/XSSF/Model/TestCommentsTable.cs index bde3999bb..4c1fbb864 100644 --- a/testcases/ooxml/XSSF/Model/TestCommentsTable.cs +++ b/testcases/ooxml/XSSF/Model/TestCommentsTable.cs @@ -126,7 +126,7 @@ public void WriteRead() ICell c1r2s2 = r2s2.CreateCell(1); ClassicAssert.IsNull(c1r2s2.CellComment); - IDrawing dg = sheet2.CreateDrawingPatriarch(); + IDrawing dg = sheet2.CreateDrawingPatriarch(); IComment cc2 = dg.CreateCellComment(new XSSFClientAnchor()); cc2.Author = ("Also POI"); cc2.String = (new XSSFRichTextString("A new comment")); @@ -238,7 +238,7 @@ public void Bug54920() // NOTE - only occurs if a comment is placed in A1 first ICell A1 = GetCell(sheet, 0, 0); //Cell A1 = getCell(sheet, 2, 2); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); setComment(sheet, A1, drawing, "for A1", helper, anchor); // find comment in A1 before we set the comment in B2 @@ -262,7 +262,7 @@ public void Bug54920() // Set the comment on a sheet // - private static void setComment(ISheet sheet, ICell cell, IDrawing drawing, String commentText, ICreationHelper helper, IClientAnchor anchor) + private static void setComment(ISheet sheet, ICell cell, IDrawing drawing, String commentText, ICreationHelper helper, IClientAnchor anchor) { //System.out.println("Setting col: " + cell.getColumnIndex() + " and row " + cell.getRowIndex()); anchor.Col1 = (cell.ColumnIndex); diff --git a/testcases/ooxml/XSSF/SXSSFITestDataProvider.cs b/testcases/ooxml/XSSF/SXSSFITestDataProvider.cs index 0a26257ac..6ed4d99c9 100644 --- a/testcases/ooxml/XSSF/SXSSFITestDataProvider.cs +++ b/testcases/ooxml/XSSF/SXSSFITestDataProvider.cs @@ -26,14 +26,12 @@ namespace NPOI.XSSF using NPOI.SS.UserModel; using NPOI.XSSF.Streaming; using NPOI.XSSF.UserModel; - using NUnit.Framework;using NUnit.Framework.Legacy; using TestCases; using TestCases.SS; /** * @author Yegor Kozlov */ - [TestFixture] public class SXSSFITestDataProvider : ITestDataProvider { public static SXSSFITestDataProvider instance = new SXSSFITestDataProvider(); diff --git a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFBarChartData.cs b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFBarChartData.cs index e051f7b5c..1297187a3 100644 --- a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFBarChartData.cs +++ b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFBarChartData.cs @@ -25,7 +25,7 @@ public void TestSettingBarGrouping(BarGrouping barGrouping, ST_BarGrouping expec { using IWorkbook wb = new XSSFWorkbook(); ISheet sheet = new SheetBuilder(wb, plotData).Build(); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = drawing.CreateChart(anchor); @@ -54,7 +54,7 @@ public void TestBarGroupingBeClusteredWhenNoBarGroupingIsSet() { using IWorkbook wb = new XSSFWorkbook(); ISheet sheet = new SheetBuilder(wb, plotData).Build(); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = drawing.CreateChart(anchor); diff --git a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartAxis.cs b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartAxis.cs index 82f7f0c29..d9c2ced41 100644 --- a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartAxis.cs +++ b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartAxis.cs @@ -34,7 +34,7 @@ public TestXSSFChartAxis() { IWorkbook wb = new XSSFWorkbook(); ISheet sheet = wb.CreateSheet(); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = Drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = Drawing.CreateChart(anchor); axis = chart.ChartAxisFactory.CreateValueAxis(AxisPosition.Bottom); diff --git a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartLegend.cs b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartLegend.cs index 40217352b..f0dbd4aed 100644 --- a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartLegend.cs +++ b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartLegend.cs @@ -33,7 +33,7 @@ public void TestLegendPositionAccessMethods() { IWorkbook wb = new XSSFWorkbook(); ISheet sheet = wb.CreateSheet(); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = Drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = Drawing.CreateChart(anchor); IChartLegend legend = chart.GetOrCreateLegend(); @@ -50,7 +50,7 @@ public void Test_setOverlay_defaultChartLegend_expectOverlayInitialValueSetToFal // Arrange IWorkbook wb = new XSSFWorkbook(); ISheet sheet = wb.CreateSheet(); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = Drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = Drawing.CreateChart(anchor); IChartLegend legend = chart.GetOrCreateLegend(); @@ -69,7 +69,7 @@ public void Test_setOverlay_chartLegendSetToTrue_expectOverlayInitialValueSetToT // Arrange IWorkbook wb = new XSSFWorkbook(); ISheet sheet = wb.CreateSheet(); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = Drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = Drawing.CreateChart(anchor); IChartLegend legend = chart.GetOrCreateLegend(); diff --git a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartTitle.cs b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartTitle.cs index 23c06723a..e1a44d375 100644 --- a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartTitle.cs +++ b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFChartTitle.cs @@ -52,7 +52,7 @@ private IWorkbook CreateWorkbookWithChart() } } - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = Drawing.CreateAnchor(0, 0, 0, 0, 0, 5, 10, 15); IChart chart = Drawing.CreateChart(anchor); diff --git a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFColumnChartData.cs b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFColumnChartData.cs index 2cafb4237..56d7469f7 100644 --- a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFColumnChartData.cs +++ b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFColumnChartData.cs @@ -21,7 +21,7 @@ public void TestSettingBarGrouping(BarGrouping barGrouping, ST_BarGrouping expec { using IWorkbook wb = new XSSFWorkbook(); ISheet sheet = new SheetBuilder(wb, plotData).Build(); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = drawing.CreateChart(anchor); @@ -50,7 +50,7 @@ public void TestBarGroupingBeClusteredWhenNoBarGroupingIsSet() { using IWorkbook wb = new XSSFWorkbook(); ISheet sheet = new SheetBuilder(wb, plotData).Build(); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = drawing.CreateChart(anchor); diff --git a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFLineChartData.cs b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFLineChartData.cs index 56d6e32c0..af2601aff 100644 --- a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFLineChartData.cs +++ b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFLineChartData.cs @@ -25,7 +25,7 @@ public void TestOneSeriePlot() { IWorkbook wb = new XSSFWorkbook(); ISheet sheet = new SheetBuilder(wb, plotData).Build(); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = Drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = Drawing.CreateChart(anchor); diff --git a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFManualLayout.cs b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFManualLayout.cs index 0e31594b1..c93810ef7 100644 --- a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFManualLayout.cs +++ b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFManualLayout.cs @@ -33,7 +33,7 @@ public void CreateEmptyLayout() { wb = new XSSFWorkbook(); ISheet sheet = wb.CreateSheet(); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = drawing.CreateChart(anchor); IChartLegend legend = chart.GetOrCreateLegend(); diff --git a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFScatterChartData.cs b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFScatterChartData.cs index 68960aac7..8d0a82758 100644 --- a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFScatterChartData.cs +++ b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFScatterChartData.cs @@ -41,7 +41,7 @@ public void TestOneSeriePlot() { IWorkbook wb = new XSSFWorkbook(); ISheet sheet = new SheetBuilder(wb, plotData).Build(); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = Drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = Drawing.CreateChart(anchor); diff --git a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFValueAxis.cs b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFValueAxis.cs index b0391de80..8e36b8469 100644 --- a/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFValueAxis.cs +++ b/testcases/ooxml/XSSF/UserModel/Charts/TestXSSFValueAxis.cs @@ -30,7 +30,7 @@ public void TestAccessMethods() { IWorkbook wb = new XSSFWorkbook(); ISheet sheet = wb.CreateSheet(); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); IClientAnchor anchor = Drawing.CreateAnchor(0, 0, 0, 0, 1, 1, 10, 30); IChart chart = Drawing.CreateChart(anchor); IValueAxis axis = chart.ChartAxisFactory.CreateValueAxis(AxisPosition.Bottom); diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs index 4bef56af4..1497dcd1d 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFBugs.cs @@ -1422,7 +1422,7 @@ public void Test51850() // Try to add comments to Sheet 1 ICreationHelper factory = wb1.GetCreationHelper(); - IDrawing Drawing = sh1.CreateDrawingPatriarch(); + IDrawing Drawing = sh1.CreateDrawingPatriarch(); IClientAnchor anchor = factory.CreateClientAnchor(); anchor.Col1 = (0); @@ -2578,7 +2578,7 @@ public void Test56467() ClassicAssert.IsNotNull(orig); ISheet sheet = wb.CloneSheet(0); - IDrawing Drawing = sheet.CreateDrawingPatriarch(); + IDrawing Drawing = sheet.CreateDrawingPatriarch(); foreach (XSSFShape shape in ((XSSFDrawing)Drawing).GetShapes()) { if (shape is XSSFPicture) diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFComment.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFComment.cs index 548648621..a14c6d42b 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFComment.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFComment.cs @@ -261,7 +261,7 @@ public void TestBug58175a() IRow row = sheet.CreateRow(1); ICell cell = row.CreateCell(3); cell.SetCellValue("F4"); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); ICreationHelper factory = wb.GetCreationHelper(); // When the comment box is visible, have it show in a 1x3 space IClientAnchor anchor = factory.CreateClientAnchor(); @@ -327,7 +327,7 @@ public void TestRemoveXSSFCellComment() ICell cell = row.CreateCell(0); cell.SetCellValue("test"); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); ICreationHelper factory = wb.GetCreationHelper(); // When the comment box is visible, have it show in a 1x3 space IClientAnchor anchor = factory.CreateClientAnchor(); @@ -379,7 +379,7 @@ public void TestRemoveSXSSFCellComment() ICell cell = row.CreateCell(0); cell.SetCellValue("test"); - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); ICreationHelper factory = wb.GetCreationHelper(); // When the comment box is visible, have it show in a 1x3 space IClientAnchor anchor = factory.CreateClientAnchor(); diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFDrawing.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFDrawing.cs index 0d4e35442..23bb954d1 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFDrawing.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFDrawing.cs @@ -156,7 +156,6 @@ public void TestNew() wb2.Close(); } [Test] - [Ignore("TODO FIX CI TESTS")] public void TestMultipleDrawings() { XSSFWorkbook wb = new XSSFWorkbook(); @@ -174,7 +173,7 @@ public void TestMultipleDrawings() } finally { - pkg.Close(); + } wb.Close(); } @@ -813,6 +812,67 @@ public void TestBug56835CellComment() wb.Close(); } } + + [Test] + public void TestGroupShape() + { + + XSSFWorkbook wb1 = new XSSFWorkbook(); + XSSFSheet sheet = wb1.CreateSheet() as XSSFSheet; + XSSFDrawing drawing = sheet.CreateDrawingPatriarch() as XSSFDrawing; + + XSSFSimpleShape s0 = drawing.CreateSimpleShape((XSSFClientAnchor)drawing.CreateAnchor(0, 0, Units.PixelToEMU(30), Units.PixelToEMU(30), 1, 1, 10, 10)); + s0.ShapeType = (int)ShapeTypes.Rectangle; + s0.SetLineStyleColor(100, 0, 0); + + XSSFShapeGroup g1 = drawing.CreateGroup((XSSFClientAnchor)drawing.CreateAnchor(0, 0, 300, 300, 1, 1, 10, 10)); + CT_GroupTransform2D xfrmG1 = g1.GetCTGroupShape().grpSpPr.xfrm; + + XSSFSimpleShape s1 = g1.CreateSimpleShape(new XSSFChildAnchor( + (int)(xfrmG1.chExt.cx*0.1), + (int)(xfrmG1.chExt.cy*0.1), + (int)(xfrmG1.chExt.cx*0.9), + (int)(xfrmG1.chExt.cy*0.9) + )); + s1.ShapeType = (int) ShapeTypes.Rectangle; + s1.SetLineStyleColor(0, 100, 0); + + XSSFShapeGroup g2 = g1.CreateGroup(new XSSFChildAnchor( + (int)(xfrmG1.chExt.cx*0.2), + (int)(xfrmG1.chExt.cy*0.2), + (int)(xfrmG1.chExt.cx*0.8), + (int)(xfrmG1.chExt.cy*0.8) + )); + CT_GroupTransform2D xfrmG2 = g2.GetCTGroupShape().grpSpPr.xfrm; + + XSSFSimpleShape s2 = g2.CreateSimpleShape(new XSSFChildAnchor( + (int)(xfrmG2.chExt.cx*0.1), + (int)(xfrmG2.chExt.cy*0.1), + (int)(xfrmG2.chExt.cx*0.9), + (int)(xfrmG2.chExt.cy*0.9) + )); + s2.ShapeType = (int) ShapeTypes.Rectangle; + s2.SetLineStyleColor(0, 0, 100); + + XSSFWorkbook wb2 = XSSFTestDataSamples.WriteOutAndReadBack(wb1); + wb1.Close(); + + XSSFDrawing draw = wb2.GetSheetAt(0).DrawingPatriarch as XSSFDrawing; + List shapes = draw.GetShapes(); + ClassicAssert.AreEqual(2, shapes.Count); + ClassicAssert.IsTrue(shapes[0] is XSSFSimpleShape); + ClassicAssert.IsTrue(shapes[1] is XSSFShapeGroup); + shapes = draw.GetShapes((XSSFShapeGroup) shapes[1]); + ClassicAssert.AreEqual(2, shapes.Count); + ClassicAssert.IsTrue(shapes[0] is XSSFSimpleShape); + ClassicAssert.IsTrue(shapes[1] is XSSFShapeGroup); + shapes = draw.GetShapes((XSSFShapeGroup) shapes[1]); + ClassicAssert.AreEqual(1, shapes.Count); + ClassicAssert.IsTrue(shapes[0] is XSSFSimpleShape); + + wb2.Close(); + } + private static void checkRewrite(XSSFWorkbook wb) { XSSFWorkbook wb2 = XSSFTestDataSamples.WriteOutAndReadBack(wb); diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFSheet.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFSheet.cs index e3f02d329..24485db83 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFSheet.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFSheet.cs @@ -475,7 +475,7 @@ public void TestSetCellComment() XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = (XSSFSheet)workbook.CreateSheet(); - IDrawing dg = sheet.CreateDrawingPatriarch(); + IDrawing dg = sheet.CreateDrawingPatriarch(); IComment comment = dg.CreateCellComment(new XSSFClientAnchor()); ICell cell = sheet.CreateRow(0).CreateCell(0); @@ -2957,7 +2957,7 @@ public void TestInsertCommentsToClonedSheet() private void AddComments(ICreationHelper helper, ISheet sheet) { - IDrawing drawing = sheet.CreateDrawingPatriarch(); + IDrawing drawing = sheet.CreateDrawingPatriarch(); for (int i = 0; i < 2; i++) { IClientAnchor anchor = helper.CreateClientAnchor(); diff --git a/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs b/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs index 054f0ab77..c5534be61 100644 --- a/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs +++ b/testcases/ooxml/XSSF/UserModel/TestXSSFWorkbook.cs @@ -1251,7 +1251,7 @@ public void TestSavingXlsxTwiceWithBmpPictures() private static void InsertPicture(IWorkbook wb, byte[] data, PictureType picType) { ISheet sheet = wb.GetSheetAt(0); - IDrawing patriarch = sheet.DrawingPatriarch; + IDrawing patriarch = sheet.DrawingPatriarch; XSSFClientAnchor anchor = new(500, 200, 0, 0, 2, 2, 4, 7) { AnchorType = AnchorType.MoveDontResize }; diff --git a/testcases/test-data/spreadsheet/Basic_Expense_Template_2011.xls b/testcases/test-data/spreadsheet/Basic_Expense_Template_2011.xls new file mode 100644 index 000000000..5df7ae3c9 Binary files /dev/null and b/testcases/test-data/spreadsheet/Basic_Expense_Template_2011.xls differ diff --git a/testcases/test-data/spreadsheet/angelo.edu_content_files_19555-nsse-2011-multiyear-benchmark.xls b/testcases/test-data/spreadsheet/angelo.edu_content_files_19555-nsse-2011-multiyear-benchmark.xls new file mode 100644 index 000000000..bff2dea56 Binary files /dev/null and b/testcases/test-data/spreadsheet/angelo.edu_content_files_19555-nsse-2011-multiyear-benchmark.xls differ diff --git a/testcases/test-data/spreadsheet/external_image.xls b/testcases/test-data/spreadsheet/external_image.xls new file mode 100644 index 000000000..d7d2e0e4e Binary files /dev/null and b/testcases/test-data/spreadsheet/external_image.xls differ