Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow duplicate picture names when loading a file #445

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions ClosedXML/Excel/Drawings/IXLPicture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public interface IXLPicture : IDisposable

Int32 Height { get; set; }

Int32 Id { get; }

MemoryStream ImageStream { get; }

Int32 Left { get; set; }
Expand Down
52 changes: 36 additions & 16 deletions ClosedXML/Excel/Drawings/XLPicture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal class XLPicture : IXLPicture
private static IDictionary<XLPictureFormat, ImageFormat> FormatMap;
private readonly IXLWorksheet _worksheet;
private Int32 height;
private Int32 id;
private String name = string.Empty;
private Int32 width;

Expand All @@ -32,7 +33,7 @@ static XLPicture()
}

internal XLPicture(IXLWorksheet worksheet, Stream stream)
: this(worksheet)
: this(worksheet)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));

Expand Down Expand Up @@ -67,11 +68,8 @@ internal XLPicture(IXLWorksheet worksheet, Stream stream, XLPictureFormat format

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);
}
Expand Down Expand Up @@ -105,6 +103,13 @@ private XLPicture(IXLWorksheet worksheet)
[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
Expand Down Expand Up @@ -135,6 +140,16 @@ public Int32 Height
}
}

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
Expand All @@ -156,19 +171,10 @@ public String Name
{
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);
}
}

Expand Down Expand Up @@ -323,6 +329,20 @@ public IXLPicture WithSize(Int32 width, Int32 height)
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;
Expand Down
12 changes: 10 additions & 2 deletions ClosedXML/Excel/Drawings/XLPictures.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public IXLPicture Add(Stream stream, string name)
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);
Expand Down Expand Up @@ -127,13 +127,21 @@ public bool TryGetPicture(string pictureName, out IXLPicture picture)
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;
Expand Down
8 changes: 7 additions & 1 deletion ClosedXML/Excel/XLWorkbook_ImageHandling.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ public static NonVisualDrawingProperties GetPropertiesFromAnchor(OpenXmlElement
if (!IsAllowedAnchor(anchor))
return null;

return anchor
var picture = anchor
.Descendants<Xdr.Picture>()
.FirstOrDefault();

if (picture == null) return null;

return picture
.Descendants<Xdr.NonVisualDrawingProperties>()
.FirstOrDefault();
}
Expand Down
2 changes: 1 addition & 1 deletion ClosedXML/Excel/XLWorkbook_Load.cs
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ private void LoadDrawings(WorksheetPart wsPart, IXLWorksheet ws)
{
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<Xdr.ShapeProperties>().First();
Expand Down
7 changes: 6 additions & 1 deletion ClosedXML/Excel/XLWorksheet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,12 @@ public IXLPicture AddPicture(Stream stream, string name)
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);
}
Expand Down
1 change: 1 addition & 0 deletions ClosedXML_Tests/ClosedXML_Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@
<EmbeddedResource Include="Resource\Misc\ExcelProducedWorkbookWithImages.xlsx" />
<EmbeddedResource Include="Resource\Misc\EmptyCellValue.xlsx" />
<EmbeddedResource Include="Resource\Misc\AllShapes.xlsx" />
<EmbeddedResource Include="Resource\Misc\DuplicateImageNames.xlsx" />
<EmbeddedResource Include="Resource\Misc\TableHeadersWithLineBreaks.xlsx" />
<EmbeddedResource Include="Resource\Misc\TableWithNameNull.xlsx" />
</ItemGroup>
Expand Down
29 changes: 29 additions & 0 deletions ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,35 @@ public void TestDefaultPictureNames()
}
}

[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()
{
Expand Down
3 changes: 2 additions & 1 deletion ClosedXML_Tests/Excel/Loading/LoadingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public void CanSuccessfullyLoadFiles()
@"Misc\EmptyCellValue.xlsx",
@"Misc\AllShapes.xlsx",
@"Misc\TableHeadersWithLineBreaks.xlsx",
@"Misc\TableWithNameNull.xlsx"
@"Misc\TableWithNameNull.xlsx",
@"Misc\DuplicateImageNames.xlsx"
};

foreach (var file in files)
Expand Down
Binary file not shown.