diff --git a/ClosedXML/Excel/Drawings/IXLPicture.cs b/ClosedXML/Excel/Drawings/IXLPicture.cs index 2c2f750..cf167d2 100644 --- a/ClosedXML/Excel/Drawings/IXLPicture.cs +++ b/ClosedXML/Excel/Drawings/IXLPicture.cs @@ -16,6 +16,8 @@ Int32 Height { get; set; } + Int32 Id { get; } + MemoryStream ImageStream { get; } Int32 Left { get; set; } diff --git a/ClosedXML/Excel/Drawings/XLPicture.cs b/ClosedXML/Excel/Drawings/XLPicture.cs index 422336d..9266215 100644 --- a/ClosedXML/Excel/Drawings/XLPicture.cs +++ b/ClosedXML/Excel/Drawings/XLPicture.cs @@ -16,6 +16,7 @@ private static IDictionary FormatMap; private readonly IXLWorksheet _worksheet; private Int32 height; + private Int32 id; private String name = string.Empty; private Int32 width; @@ -32,7 +33,7 @@ } internal XLPicture(IXLWorksheet worksheet, Stream stream) - : this(worksheet) + : this(worksheet) { if (stream == null) throw new ArgumentNullException(nameof(stream)); @@ -67,11 +68,8 @@ using (var bitmap = new Bitmap(ImageStream)) { - if (FormatMap.ContainsKey(this.Format)) - { - if (FormatMap[this.Format].Guid != bitmap.RawFormat.Guid) - throw new ArgumentException("The picture format in the stream and the parameter don't match"); - } + if (FormatMap.ContainsKey(this.Format) && FormatMap[this.Format].Guid != bitmap.RawFormat.Guid) + throw new ArgumentException("The picture format in the stream and the parameter don't match"); DeduceDimensionsFromBitmap(bitmap); } @@ -105,6 +103,13 @@ [XLMarkerPosition.TopLeft] = null, [XLMarkerPosition.BottomRight] = null }; + + // Calculate default picture ID + var allPictures = worksheet.Workbook.Worksheets.SelectMany(ws => ws.Pictures); + if (allPictures.Any()) + this.id = allPictures.Max(p => p.Id) + 1; + else + this.id = 1; } public IXLAddress BottomRightCellAddress @@ -135,6 +140,16 @@ } } + public Int32 Id + { + get { return id; } + internal set + { + if ((_worksheet.Pictures.FirstOrDefault(p => p.Id.Equals(value)) ?? this) != this) + throw new ArgumentException($"The picture ID '{value}' already exists."); + } + } + public MemoryStream ImageStream { get; private set; } public Int32 Left @@ -156,19 +171,10 @@ { if (name == value) return; - if (value.IndexOfAny(InvalidNameChars.ToCharArray()) != -1) - throw new ArgumentException($"Picture names cannot contain any of the following characters: {InvalidNameChars}"); - - if (String.IsNullOrWhiteSpace(value)) - throw new ArgumentException("Picture names cannot be empty"); - - if (value.Length > 31) - throw new ArgumentException("Picture names cannot be more than 31 characters"); - if ((_worksheet.Pictures.FirstOrDefault(p => p.Name.Equals(value, StringComparison.OrdinalIgnoreCase)) ?? this) != this) throw new ArgumentException($"The picture name '{value}' already exists."); - name = value; + SetName(value); } } @@ -323,6 +329,20 @@ return this; } + internal void SetName(string value) + { + if (value.IndexOfAny(InvalidNameChars.ToCharArray()) != -1) + throw new ArgumentException($"Picture names cannot contain any of the following characters: {InvalidNameChars}"); + + if (String.IsNullOrWhiteSpace(value)) + throw new ArgumentException("Picture names cannot be empty"); + + if (value.Length > 31) + throw new ArgumentException("Picture names cannot be more than 31 characters"); + + name = value; + } + private static ImageFormat FromMimeType(string mimeType) { var guid = ImageCodecInfo.GetImageDecoders().FirstOrDefault(c => c.MimeType.Equals(mimeType, StringComparison.OrdinalIgnoreCase))?.FormatID; diff --git a/ClosedXML/Excel/Drawings/XLPictures.cs b/ClosedXML/Excel/Drawings/XLPictures.cs index 3381309..6a102dd 100644 --- a/ClosedXML/Excel/Drawings/XLPictures.cs +++ b/ClosedXML/Excel/Drawings/XLPictures.cs @@ -39,7 +39,7 @@ return picture; } - public Drawings.IXLPicture Add(Stream stream, XLPictureFormat format) + public IXLPicture Add(Stream stream, XLPictureFormat format) { var picture = new XLPicture(_worksheet, stream, format); _pictures.Add(picture); @@ -127,13 +127,21 @@ var matches = _pictures.Where(p => p.Name.Equals(pictureName, StringComparison.OrdinalIgnoreCase)); if (matches.Any()) { - picture = matches.Single(); + picture = matches.First(); return true; } picture = null; return false; } + internal IXLPicture Add(Stream stream, string name, int Id) + { + var picture = Add(stream) as XLPicture; + picture.SetName(name); + picture.Id = Id; + return picture; + } + private String GetNextPictureName() { var pictureNumber = this.Count; diff --git a/ClosedXML/Excel/XLWorkbook_ImageHandling.cs b/ClosedXML/Excel/XLWorkbook_ImageHandling.cs index 37e85da..9dbaf3e 100644 --- a/ClosedXML/Excel/XLWorkbook_ImageHandling.cs +++ b/ClosedXML/Excel/XLWorkbook_ImageHandling.cs @@ -43,7 +43,13 @@ if (!IsAllowedAnchor(anchor)) return null; - return anchor + var picture = anchor + .Descendants() + .FirstOrDefault(); + + if (picture == null) return null; + + return picture .Descendants() .FirstOrDefault(); } diff --git a/ClosedXML/Excel/XLWorkbook_Load.cs b/ClosedXML/Excel/XLWorkbook_Load.cs index 4490d5a..0cdd0f9 100644 --- a/ClosedXML/Excel/XLWorkbook_Load.cs +++ b/ClosedXML/Excel/XLWorkbook_Load.cs @@ -658,7 +658,7 @@ { var vsdp = GetPropertiesFromAnchor(anchor); - var picture = ws.AddPicture(stream, vsdp.Name) as XLPicture; + var picture = (ws as XLWorksheet).AddPicture(stream, vsdp.Name, Convert.ToInt32(vsdp.Id.Value)) as XLPicture; picture.RelId = imgId; Xdr.ShapeProperties spPr = anchor.Descendants().First(); diff --git a/ClosedXML/Excel/XLWorksheet.cs b/ClosedXML/Excel/XLWorksheet.cs index 453e566..d0b30b0 100644 --- a/ClosedXML/Excel/XLWorksheet.cs +++ b/ClosedXML/Excel/XLWorksheet.cs @@ -1547,7 +1547,12 @@ return Pictures.Add(stream, name); } - public Drawings.IXLPicture AddPicture(Stream stream, XLPictureFormat format) + internal IXLPicture AddPicture(Stream stream, string name, int Id) + { + return (Pictures as XLPictures).Add(stream, name, Id); + } + + public IXLPicture AddPicture(Stream stream, XLPictureFormat format) { return Pictures.Add(stream, format); } diff --git a/ClosedXML_Tests/ClosedXML_Tests.csproj b/ClosedXML_Tests/ClosedXML_Tests.csproj index c9b60d4..8059cad 100644 --- a/ClosedXML_Tests/ClosedXML_Tests.csproj +++ b/ClosedXML_Tests/ClosedXML_Tests.csproj @@ -288,6 +288,7 @@ + diff --git a/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs b/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs index b0ba0d6..a06902e 100644 --- a/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs +++ b/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs @@ -145,6 +145,35 @@ } [Test] + public void TestDefaultIds() + { + using (var wb = new XLWorkbook()) + { + var ws = wb.AddWorksheet("Sheet1"); + + using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClosedXML_Tests.Resource.Images.ImageHandling.png")) + { + ws.AddPicture(stream, XLPictureFormat.Png); + stream.Position = 0; + + ws.AddPicture(stream, XLPictureFormat.Png); + stream.Position = 0; + + ws.AddPicture(stream, XLPictureFormat.Png).Name = "Picture 4"; + stream.Position = 0; + + ws.AddPicture(stream, XLPictureFormat.Png); + stream.Position = 0; + } + + Assert.AreEqual(1, ws.Pictures.Skip(0).First().Id); + Assert.AreEqual(2, ws.Pictures.Skip(1).First().Id); + Assert.AreEqual(3, ws.Pictures.Skip(2).First().Id); + Assert.AreEqual(4, ws.Pictures.Skip(3).First().Id); + } + } + + [Test] public void XLMarkerTests() { IXLWorksheet ws = new XLWorkbook().Worksheets.Add("Sheet1"); diff --git a/ClosedXML_Tests/Excel/Loading/LoadingTests.cs b/ClosedXML_Tests/Excel/Loading/LoadingTests.cs index a20cf0f..5b1fc42 100644 --- a/ClosedXML_Tests/Excel/Loading/LoadingTests.cs +++ b/ClosedXML_Tests/Excel/Loading/LoadingTests.cs @@ -31,7 +31,8 @@ @"Misc\EmptyCellValue.xlsx", @"Misc\AllShapes.xlsx", @"Misc\TableHeadersWithLineBreaks.xlsx", - @"Misc\TableWithNameNull.xlsx" + @"Misc\TableWithNameNull.xlsx", + @"Misc\DuplicateImageNames.xlsx" }; foreach (var file in files) diff --git a/ClosedXML_Tests/Resource/Misc/DuplicateImageNames.xlsx b/ClosedXML_Tests/Resource/Misc/DuplicateImageNames.xlsx new file mode 100644 index 0000000..29df50b --- /dev/null +++ b/ClosedXML_Tests/Resource/Misc/DuplicateImageNames.xlsx Binary files differ