diff --git a/ClosedXML/ClosedXML/ClosedXML/ClosedXML.csproj b/ClosedXML/ClosedXML/ClosedXML/ClosedXML.csproj
index 80bba6a..044e497 100644
--- a/ClosedXML/ClosedXML/ClosedXML/ClosedXML.csproj
+++ b/ClosedXML/ClosedXML/ClosedXML/ClosedXML.csproj
@@ -155,6 +155,7 @@
+
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Cells/XLCell.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Cells/XLCell.cs
index 5a8eed3..e52ea8d 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/Cells/XLCell.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Cells/XLCell.cs
@@ -962,7 +962,7 @@
// MS Excel uses Tahoma 8 Swiss no matter what current style font
// var style = GetStyleForRead();
var defaultFont = new XLFont() { FontName = "Tahoma", FontSize = 8, FontFamilyNumbering = XLFontFamilyNumberingValues.Swiss };
- _comment = new XLComment(defaultFont);
+ _comment = new XLComment(Worksheet, defaultFont);
}
return _comment;
@@ -1447,7 +1447,7 @@
_dataType = source._dataType;
FormulaR1C1 = source.FormulaR1C1;
_richText = source._richText == null ? null : new XLRichText(source._richText, source.Style.Font);
- _comment = source._comment == null ? null : new XLComment(source._comment, source.Style.Font);
+ _comment = source._comment == null ? null : new XLComment(source.Worksheet, source._comment, source.Style.Font);
}
public IXLCell CopyFrom(XLCell otherCell)
@@ -1455,7 +1455,7 @@
var source = otherCell;
_cellValue = source._cellValue;
_richText = source._richText == null ? null : new XLRichText(source._richText, source.Style.Font);
- _comment = source._comment == null ? null : new XLComment(source._comment, source.Style.Font);
+ _comment = source._comment == null ? null : new XLComment(source.Worksheet, source._comment, source.Style.Font);
_dataType = source._dataType;
FormulaR1C1 = source.FormulaR1C1;
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Charts/XLChart.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Charts/XLChart.cs
index 6edee88..75de711 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/Charts/XLChart.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Charts/XLChart.cs
@@ -20,7 +20,7 @@
else
zOrder = 1;
ZOrder = zOrder;
- Id = zOrder;
+ ShapeId = worksheet.Workbook.ShapeIdManager.GetNext();
RightAngleAxes = true;
}
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Comments/XLComment.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Comments/XLComment.cs
index 436cd9b..181ea72 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/Comments/XLComment.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Comments/XLComment.cs
@@ -7,26 +7,26 @@
{
internal class XLComment : XLFormattedText, IXLComment
{
-
- public XLComment(IXLFontBase defaultFont)
+ XLWorksheet _worksheet;
+ public XLComment(XLWorksheet worksheet, IXLFontBase defaultFont)
: base(defaultFont)
{
- Initialize();
+ Initialize(worksheet);
}
- public XLComment(XLFormattedText defaultComment, IXLFontBase defaultFont)
+ public XLComment(XLWorksheet worksheet, XLFormattedText defaultComment, IXLFontBase defaultFont)
: base(defaultComment, defaultFont)
{
- Initialize();
+ Initialize(worksheet);
}
- public XLComment(String text, IXLFontBase defaultFont)
+ public XLComment(XLWorksheet worksheet, String text, IXLFontBase defaultFont)
: base(text, defaultFont)
{
- Initialize();
+ Initialize(worksheet);
}
- private void Initialize()
+ private void Initialize(XLWorksheet worksheet)
{
Container = this;
Anchor = XLDrawingAnchor.MoveAndSizeWithCells;
@@ -34,6 +34,8 @@
Style.Size.Height = 4; // I think this is misused for legacy drawing
Style.Size.Width = 2;
SetVisible();
+ _worksheet = worksheet;
+ ShapeId = worksheet.Workbook.ShapeIdManager.GetNext();
}
public String Author { get; set; }
@@ -64,7 +66,7 @@
#region IXLDrawing
- public Int32 Id { get; internal set; }
+ public Int32 ShapeId { get; internal set; }
public Boolean Hidden { get; set; }
public IXLComment SetHidden()
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/IXLAddress.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/IXLAddress.cs
index dd26417..48ad531 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/IXLAddress.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/IXLAddress.cs
@@ -17,5 +17,6 @@
String ToStringFixed(XLReferenceStyle referenceStyle);
String ToStringFixed(XLReferenceStyle referenceStyle, Boolean includeSheet);
String ToString(XLReferenceStyle referenceStyle);
+ String UniqueId { get; }
}
}
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/XLAddress.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/XLAddress.cs
index 9aa3821..d19a005 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/XLAddress.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Coordinates/XLAddress.cs
@@ -373,5 +373,6 @@
return address;
}
+ public String UniqueId { get { return RowNumber.ToString("0000000") + ColumnNumber.ToString("00000"); } }
}
}
\ No newline at end of file
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Drawings/IXLDrawing.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Drawings/IXLDrawing.cs
index ee09244..5884a22 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/Drawings/IXLDrawing.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Drawings/IXLDrawing.cs
@@ -5,7 +5,7 @@
public enum XLDrawingAnchor { MoveAndSizeWithCells, MoveWithCells, Absolute}
public interface IXLDrawing
{
- Int32 Id { get; }
+ Int32 ShapeId { get; }
Boolean Hidden { get; set; }
T SetHidden();
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Drawings/XLDrawing.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Drawings/XLDrawing.cs
index 005d000..60cb1a8 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/Drawings/XLDrawing.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Drawings/XLDrawing.cs
@@ -11,7 +11,7 @@
Style = new XLDrawingStyle();
}
- public Int32 Id { get; internal set; }
+ public Int32 ShapeId { get; internal set; }
public Boolean Hidden { get; set; }
public T SetHidden()
@@ -158,5 +158,6 @@
}
public IXLDrawingStyle Style { get; private set; }
+
}
}
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/Misc/XLIdManager.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/Misc/XLIdManager.cs
new file mode 100644
index 0000000..1c8c7fd
--- /dev/null
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/Misc/XLIdManager.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace ClosedXML.Excel
+{
+ internal class XLIdManager
+ {
+ private HashSet _hash = new HashSet();
+
+
+ public Int32 GetNext()
+ {
+ if (_hash.Count == 0)
+ {
+ _hash.Add(1);
+ return 1;
+ }
+
+ Int32 id = 1;
+ while (true)
+ {
+ if (!_hash.Contains(id))
+ {
+ _hash.Add(id);
+ return id;
+ }
+ id++;
+ }
+ }
+ public void Add(Int32 value)
+ {
+ _hash.Add(value);
+ }
+ public void Add(IEnumerable values)
+ {
+ values.ForEach(v => _hash.Add(v));
+ }
+ }
+}
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook.cs
index f038660..4598934 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook.cs
@@ -441,7 +441,7 @@
}
}
- internal void CopyStream(Stream input, Stream output)
+ internal static void CopyStream(Stream input, Stream output)
{
var buffer = new byte[8 * 1024];
int len;
@@ -537,6 +537,7 @@
WorksheetsInternal = new XLWorksheets(this);
NamedRanges = new XLNamedRanges(this);
CustomProperties = new XLCustomProperties(this);
+ ShapeIdManager = new XLIdManager();
}
///
@@ -598,5 +599,7 @@
retVal.Add(r);
return retVal;
}
+
+ internal XLIdManager ShapeIdManager { get; private set; }
}
}
\ No newline at end of file
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Load.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Load.cs
index 3fa2d0e..f68fe34 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Load.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Load.cs
@@ -53,6 +53,7 @@
private void LoadSpreadsheetDocument(SpreadsheetDocument dSpreadsheet)
{
+ ShapeIdManager = new XLIdManager();
SetProperties(dSpreadsheet);
//var sharedStrings = dSpreadsheet.WorkbookPart.SharedStringTablePart.SharedStringTable.Elements();
SharedStringItem[] sharedStrings = null;
@@ -195,6 +196,8 @@
LoadRowBreaks((RowBreaks)reader.LoadCurrentElement(), ws);
else if (reader.ElementType == typeof(ColumnBreaks))
LoadColumnBreaks((ColumnBreaks)reader.LoadCurrentElement(), ws);
+ else if (reader.ElementType == typeof(LegacyDrawing))
+ ws.LegacyDrawingId = (reader.LoadCurrentElement() as LegacyDrawing).Id.Value;
}
reader.Close();
@@ -275,7 +278,11 @@
foreach (Comment c in comments) {
// find cell by reference
var cell = ws.Cell(c.Reference);
- cell.Comment.Author = authors[(int)c.AuthorId.Value].InnerText;
+ XLComment xlComment = cell.Comment as XLComment;
+ xlComment.Author = authors[(int)c.AuthorId.Value].InnerText;
+ //xlComment.ShapeId = (Int32)c.ShapeId.Value;
+ //ShapeIdManager.Add(xlComment.ShapeId);
+
var runs = c.GetFirstChild().Elements();
foreach (Run run in runs) {
var runProperties = run.RunProperties;
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.NestedTypes.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.NestedTypes.cs
index 51bf44b..174ac4e 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.NestedTypes.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.NestedTypes.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Linq;
namespace ClosedXML.Excel
{
@@ -141,5 +142,9 @@
public IXLStyle Style;
}
#endregion
+
+
+
+
}
}
\ No newline at end of file
diff --git a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.cs b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.cs
index 0a6ca14..0f1bfa8 100644
--- a/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.cs
+++ b/ClosedXML/ClosedXML/ClosedXML/Excel/XLWorkbook_Save.cs
@@ -31,6 +31,9 @@
using Text = DocumentFormat.OpenXml.Spreadsheet.Text;
using TopBorder = DocumentFormat.OpenXml.Spreadsheet.TopBorder;
using Underline = DocumentFormat.OpenXml.Spreadsheet.Underline;
+using System.Xml;
+using System.Xml.Linq;
+using System.Text;
namespace ClosedXML.Excel
@@ -144,17 +147,22 @@
context.RelIdGenerator.AddValues(worksheetPart.Parts.Select(p => p.RelationshipId).ToList(), RelType.Worksheet);
// delete comment related parts (todo: review)
- worksheetPart.DeletePart(worksheetPart.WorksheetCommentsPart);
- worksheetPart.DeleteParts(worksheetPart.GetPartsOfType());
-
+ DeleteComments(worksheetPart, worksheet, context);
+
if (worksheet.Internals.CellsCollection.GetCells(c => c.HasComment).Any())
{
WorksheetCommentsPart worksheetCommentsPart =
worksheetPart.AddNewPart(context.RelIdGenerator.GetNext(RelType.Worksheet));
GenerateWorksheetCommentsPartContent(worksheetCommentsPart, worksheet);
- worksheet.LegacyDrawingId = context.RelIdGenerator.GetNext(RelType.Worksheet);
- VmlDrawingPart vmlDrawingPart = worksheetPart.AddNewPart(worksheet.LegacyDrawingId);
+ //VmlDrawingPart vmlDrawingPart = worksheetPart.AddNewPart(worksheet.LegacyDrawingId);
+ VmlDrawingPart vmlDrawingPart = worksheetPart.VmlDrawingParts.FirstOrDefault();
+ if (vmlDrawingPart == null)
+ {
+ if (StringExtensions.IsNullOrWhiteSpace(worksheet.LegacyDrawingId))
+ worksheet.LegacyDrawingId = context.RelIdGenerator.GetNext(RelType.Worksheet);
+ vmlDrawingPart = worksheetPart.AddNewPart(worksheet.LegacyDrawingId);
+ }
GenerateVmlDrawingPartContent(vmlDrawingPart, worksheet, context);
}
@@ -196,6 +204,43 @@
SetPackageProperties(document);
}
+ private void DeleteComments(WorksheetPart worksheetPart, XLWorksheet worksheet, SaveContext context)
+ {
+ // We have the comments so we can delete the comments part
+ worksheetPart.DeletePart(worksheetPart.WorksheetCommentsPart);
+ var vmlDrawingPart = worksheetPart.VmlDrawingParts.FirstOrDefault();
+
+ // Only delete the VmlDrawingParts for comments.
+ //foreach (VmlDrawingPart vmlDrawingPart in vmlDrawingParts)
+ if (vmlDrawingPart != null)
+ {
+ XDocument xdoc = XDocument.Load(vmlDrawingPart.GetStream(FileMode.Open));
+ xdoc.Root.Elements().Where(e => e.Name.LocalName == "shapelayout").Remove();
+ xdoc.Root.Elements().Where(e=>e.Name.LocalName == "shapetype" && (string)e.Attribute("id") == @"_x0000_t202").Remove();
+ xdoc.Root.Elements().Where(e => e.Name.LocalName == "shape" && (string)e.Attribute("type") == @"#_x0000_t202").Remove();
+ var imageParts = vmlDrawingPart.ImageParts.ToList();
+ var legacyParts = vmlDrawingPart.LegacyDiagramTextParts.ToList();
+ worksheetPart.ChangeIdOfPart(vmlDrawingPart, "xxRRxx"); // Anything will do for the new relationship id
+ // we just want it alive enough to create the copy
+
+ Boolean hasShapes = xdoc.Root.Elements().Where(e => e.Name.LocalName == "shape").Any();
+ if (imageParts.Count > 0 || legacyParts.Count > 0 || hasShapes)
+ {
+ if (StringExtensions.IsNullOrWhiteSpace(worksheet.LegacyDrawingId))
+ worksheet.LegacyDrawingId = context.RelIdGenerator.GetNext(RelType.Worksheet);
+ VmlDrawingPart vmlDrawingPartNew = worksheetPart.AddNewPart(worksheet.LegacyDrawingId);
+
+ if (hasShapes)
+ xdoc.Save(vmlDrawingPartNew.GetStream(System.IO.FileMode.Create));
+
+ imageParts.ForEach(p => vmlDrawingPartNew.AddPart(p, vmlDrawingPart.GetIdOfPart(p)));
+ legacyParts.ForEach(p => vmlDrawingPartNew.AddPart(p, vmlDrawingPart.GetIdOfPart(p)));
+ }
+
+ worksheetPart.DeletePart(vmlDrawingPart);
+ }
+ }
+
private static void GenerateTables(XLWorksheet worksheet, WorksheetPart worksheetPart, SaveContext context)
{
worksheetPart.Worksheet.RemoveAllChildren();
@@ -4330,18 +4375,64 @@
//
#endregion
- System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(vmlDrawingPart.GetStream(System.IO.FileMode.Create), System.Text.Encoding.UTF8);
+ //XDocument xdoc;
+ //if (vmlDrawingPart.GetStream(FileMode.Open).Length > 0)
+ // xdoc = XDocument.Load(vmlDrawingPart.GetStream(FileMode.Open));
+ //else
+ // xdoc = new XDocument(new XElement("xml"));
+
+ ////xdoc.AddFirst(new XElement("xml"));
+ //const string shapeTypeId = "_x0000_t202"; // arbitrary, assigned by office
+ //xdoc.Root.Add(
+ // // v:shapetype
+ // new Vml.Shapetype(
+ // new Vml.Stroke() { JoinStyle = Vml.StrokeJoinStyleValues.Miter },
+ // new Vml.Path() { AllowGradientShape = true, ConnectionPointType = Vml.Office.ConnectValues.Rectangle }
+ // )
+ // {
+ // Id = shapeTypeId,
+ // CoordinateSize = "21600,21600",
+ // OptionalNumber = 202,
+ // EdgePath = "m,l,21600r21600,l21600,xe",
+ // }
+ // ) ;
+
+ //// v:shape
+ //var cellWithComments = xlWorksheet.Internals.CellsCollection.GetCells().Where(c => c.HasComment);
+
+ //foreach (XLCell c in cellWithComments)
+ //{
+ // xdoc.Root.Add(
+ // GenerateShape(c, shapeTypeId)
+ // );
+ //}
+
+ //xdoc.Save(vmlDrawingPart.GetStream(System.IO.FileMode.Create), );
+ ////////////////////////////////
+ var ms = new MemoryStream();
+ CopyStream(vmlDrawingPart.GetStream(FileMode.OpenOrCreate), ms);
+ ms.Position = 0;
+ XmlTextReader reader = new XmlTextReader(ms);
+ XmlTextWriter writer = new XmlTextWriter(vmlDrawingPart.GetStream(FileMode.Create), Encoding.UTF8);
+ //if (ms.Length == 0)
+ // writer.WriteStartElement("xml");
+ //else
+ //{
+ // //ms.Position = 0;
+ // CopyXml(reader, writer);
+ //}
+
writer.WriteStartElement("xml");
- // o:shapelayout
- new Vml.Office.ShapeLayout(
- new Vml.Office.ShapeIdMap()
- {
- Extension = Vml.ExtensionHandlingBehaviorValues.Edit,
- Data = "1"
- }
- ) { Extension = Vml.ExtensionHandlingBehaviorValues.Edit }
- .WriteTo(writer);
+ //// o:shapelayout
+ //new Vml.Office.ShapeLayout(
+ // new Vml.Office.ShapeIdMap()
+ // {
+ // Extension = Vml.ExtensionHandlingBehaviorValues.Edit,
+ // Data = "1"
+ // }
+ // ) { Extension = Vml.ExtensionHandlingBehaviorValues.Edit }
+ // .WriteTo(writer);
const string shapeTypeId = "_x0000_t202"; // arbitrary, assigned by office
@@ -4366,11 +4457,88 @@
GenerateShape(c, shapeTypeId).WriteTo(writer);
}
+ if (ms.Length > 0)
+ {
+ //ms.Position = 0;
+ //var sb = new StringBuilder();
+ //while (reader.Read())
+ // sb.Append(reader.ReadString());
+ //var t = sb.ToString();
+ CopyXml(reader, writer);
+ }
+
+ //writer.WriteEndElement();
writer.Flush();
writer.Close();
}
+ static void CopyXml(XmlTextReader xtr, XmlTextWriter xtw)
+ {
+ string docElemName = null;
+ bool b = true;
+ while (b)
+ {
+ xtr.Read();
+ switch (xtr.NodeType)
+ {
+ case XmlNodeType.Attribute:
+ xtw.WriteAttributeString(xtr.Prefix,
+ xtr.LocalName, xtr.NamespaceURI, xtr.Value);
+ break;
+ case XmlNodeType.CDATA:
+ xtw.WriteCData(xtr.Value);
+ break;
+ case XmlNodeType.Comment:
+ xtw.WriteComment(xtr.Value);
+ break;
+ case XmlNodeType.DocumentType:
+ xtw.WriteDocType(xtr.Name, null, null, null);
+ break;
+ case XmlNodeType.Element:
+ if (xtr.LocalName != "xml")
+ {
+ xtw.WriteNode(xtr, true);
+ //xtw.WriteStartElement(xtr.Prefix,
+ // xtr.LocalName, xtr.NamespaceURI);
+ //if (xtr.IsEmptyElement)
+ // xtw.WriteEndElement();
+ if (docElemName == null)
+ docElemName = xtr.Name;
+ }
+ break;
+ case XmlNodeType.EndElement:
+ if (docElemName == xtr.Name)
+ b = false;
+ else
+ xtw.WriteEndElement();
+ break;
+ case XmlNodeType.EntityReference:
+ xtw.WriteEntityRef(xtr.Name);
+ break;
+ case XmlNodeType.ProcessingInstruction:
+ xtw.WriteProcessingInstruction(xtr.Name, xtr.Value);
+ break;
+ case XmlNodeType.SignificantWhitespace:
+ xtw.WriteWhitespace(xtr.Value);
+ break;
+ case XmlNodeType.Text:
+ xtw.WriteString(xtr.Value);
+ break;
+ case XmlNodeType.Whitespace:
+ xtw.WriteWhitespace(xtr.Value);
+ break;
+ case XmlNodeType.XmlDeclaration:
+ //xtw.WriteStartDocument();
+ break;
+ default:
+ if (docElemName == xtr.Name)
+ b = false;
+ break;
+ }
+ }
+ }
+
// VML Shape for Comment
private static Vml.Shape GenerateShape(XLCell c, string shapeTypeId)
{
@@ -4406,7 +4574,8 @@
var bottomRow = topRow + c.Comment.Style.Size.Height;
var bottomOffset = rowNumber == 1 ? 2 : 9;
- var shapeId = string.Format("_x0000_s{0}", c.GetHashCode().ToString()); // Unique per cell, e.g.: "_x0000_s1026"
+
+ String shapeId = String.Format("_x0000_s{0}", c.Comment.ShapeId); // Unique per cell (workbook?), e.g.: "_x0000_s1026"
return new Vml.Shape(
new Vml.Fill { Color2 = "#" + c.Comment.Style.ColorsAndLines.FillColor.Color.ToHex().Substring(2) },