diff --git a/ClosedXML/ClosedXML.csproj b/ClosedXML/ClosedXML.csproj index 26cb10b..d935052 100644 --- a/ClosedXML/ClosedXML.csproj +++ b/ClosedXML/ClosedXML.csproj @@ -25,10 +25,6 @@ $(DefineConstants);_NETFRAMEWORK_;_NET461_ - - - - @@ -44,4 +40,8 @@ + + + + diff --git a/ClosedXML/Excel/Drawings/PictureEnums.cs b/ClosedXML/Excel/Drawings/PictureEnums.cs index 3d76e73..f00f85b 100644 --- a/ClosedXML/Excel/Drawings/PictureEnums.cs +++ b/ClosedXML/Excel/Drawings/PictureEnums.cs @@ -14,9 +14,9 @@ Tiff = 3, Icon = 4, Pcx = 5, - Jpeg = 6, - Emf = 7, - Wmf = 8 + Jpeg = 6 + //Emf = 7, + //Wmf = 8 } public enum XLPicturePlacement diff --git a/ClosedXML/Excel/Drawings/XLPicture.cs b/ClosedXML/Excel/Drawings/XLPicture.cs index 4427664..7364606 100644 --- a/ClosedXML/Excel/Drawings/XLPicture.cs +++ b/ClosedXML/Excel/Drawings/XLPicture.cs @@ -1,15 +1,18 @@ +using MetadataExtractor; +using MetadataExtractor.Formats.Bmp; +using MetadataExtractor.Formats.Gif; +using MetadataExtractor.Formats.Ico; +using MetadataExtractor.Formats.Jpeg; +using MetadataExtractor.Formats.Pcx; +using MetadataExtractor.Formats.Png; +using MetadataExtractor.Formats.Tiff; +using MetadataExtractor.Util; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; - -#if _NETFRAMEWORK_ -using System.Drawing.Imaging; -#endif - using System.IO; using System.Linq; -using System.Reflection; namespace ClosedXML.Excel.Drawings { @@ -17,26 +20,23 @@ internal class XLPicture : IXLPicture { private const String InvalidNameChars = @":\/?*[]"; + + private static IDictionary FormatMap = new Dictionary() + { + [FileType.Bmp] = XLPictureFormat.Bmp, + [FileType.Gif] = XLPictureFormat.Gif, + [FileType.Png] = XLPictureFormat.Png, + [FileType.Tiff] = XLPictureFormat.Tiff, + [FileType.Ico] = XLPictureFormat.Icon, + [FileType.Pcx] = XLPictureFormat.Pcx, + [FileType.Jpeg] = XLPictureFormat.Jpeg + }; + private readonly IXLWorksheet _worksheet; private Int32 height; private String name = string.Empty; private Int32 width; -#if _NETFRAMEWORK_ - private static IDictionary FormatMap; - static XLPicture() - { - var properties = typeof(ImageFormat).GetProperties(BindingFlags.Static | BindingFlags.Public); - FormatMap = Enum.GetValues(typeof(XLPictureFormat)) - .Cast() - .Where(pf => properties.Any(pi => pi.Name.Equals(pf.ToString(), StringComparison.OrdinalIgnoreCase))) - .ToDictionary( - pf => pf, - pf => properties.Single(pi => pi.Name.Equals(pf.ToString(), StringComparison.OrdinalIgnoreCase)).GetValue(null, null) as ImageFormat - ); - } -#endif - internal XLPicture(IXLWorksheet worksheet, Stream stream) : this(worksheet) { @@ -44,21 +44,14 @@ this.ImageStream = new MemoryStream(); { - stream.Position = 0; + stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(ImageStream); ImageStream.Seek(0, SeekOrigin.Begin); -#if _NETFRAMEWORK_ - using (var bitmap = new Bitmap(ImageStream)) - { - if (FormatMap.Values.Select(f => f.Guid).Contains(bitmap.RawFormat.Guid)) - this.Format = FormatMap.Single(f => f.Value.Guid.Equals(bitmap.RawFormat.Guid)).Key; + DeduceImageFormat(ImageStream); + DeduceDimensions(ImageStream); - DeduceDimensionsFromBitmap(bitmap); - } ImageStream.Seek(0, SeekOrigin.Begin); -#endif - } } @@ -70,23 +63,10 @@ this.ImageStream = new MemoryStream(); { - stream.Position = 0; + stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(ImageStream); - ImageStream.Seek(0, SeekOrigin.Begin); - -#if _NETFRAMEWORK_ - 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"); - } - - DeduceDimensionsFromBitmap(bitmap); - } - ImageStream.Seek(0, SeekOrigin.Begin); -#endif + DeduceImageFormat(ImageStream, format); + DeduceDimensions(ImageStream); } } @@ -98,13 +78,8 @@ this.ImageStream = new MemoryStream(); bitmap.Save(ImageStream, bitmap.RawFormat); ImageStream.Seek(0, SeekOrigin.Begin); - DeduceDimensionsFromBitmap(bitmap); - - var formats = FormatMap.Where(f => f.Value.Guid.Equals(bitmap.RawFormat.Guid)); - if (!formats.Any() || formats.Count() > 1) - throw new ArgumentException("Unsupported or unknown image format in bitmap"); - - this.Format = formats.Single().Key; + DeduceImageFormat(ImageStream); + DeduceDimensions(ImageStream); } #endif @@ -336,37 +311,76 @@ return this; } -#if _NETFRAMEWORK_ - private static ImageFormat FromMimeType(string mimeType) + private void DeduceDimensions(Stream stream) { - var guid = ImageCodecInfo.GetImageDecoders().FirstOrDefault(c => c.MimeType.Equals(mimeType, StringComparison.OrdinalIgnoreCase))?.FormatID; - if (!guid.HasValue) return null; - var property = typeof(System.Drawing.Imaging.ImageFormat).GetProperties(BindingFlags.Public | BindingFlags.Static) - .FirstOrDefault(pi => (pi.GetValue(null, null) as ImageFormat).Guid.Equals(guid.Value)); - - if (property == null) return null; - return (property.GetValue(null, null) as ImageFormat); - } - - private static string GetMimeType(Image i) - { - var imgguid = i.RawFormat.Guid; - foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageDecoders()) + stream.Seek(0, SeekOrigin.Begin); + MetadataExtractor.Directory d; + switch (this.Format) { - if (codec.FormatID == imgguid) - return codec.MimeType; + case XLPictureFormat.Bmp: + d = BmpMetadataReader.ReadMetadata(stream); + this.width = d.GetInt32(BmpHeaderDirectory.TagImageWidth); + this.height = d.GetInt32(BmpHeaderDirectory.TagImageHeight); + break; + + case XLPictureFormat.Gif: + d = GifMetadataReader.ReadMetadata(stream).OfType().First(); + this.width = d.GetInt32(GifHeaderDirectory.TagImageWidth); + this.height = d.GetInt32(GifHeaderDirectory.TagImageHeight); + break; + + case XLPictureFormat.Png: + d = PngMetadataReader.ReadMetadata(stream).OfType().First(); + this.width = d.GetInt32(PngDirectory.TagImageWidth); + this.height = d.GetInt32(PngDirectory.TagImageHeight); + break; + + case XLPictureFormat.Tiff: + d = TiffMetadataReader.ReadMetadata(stream).First(); + throw new NotImplementedException(); + //this.width = d.GetInt32(tiff.TagImageWidth); + //this.height = d.GetInt32(PngDirectory.TagImageHeight); + break; + + case XLPictureFormat.Icon: + d = IcoMetadataReader.ReadMetadata(stream).OfType().First(); + this.width = d.GetInt32(IcoDirectory.TagImageWidth); + this.height = d.GetInt32(IcoDirectory.TagImageHeight); + break; + + case XLPictureFormat.Pcx: + d = PcxMetadataReader.ReadMetadata(stream); + this.width = d.GetInt32(PcxDirectory.TagXMax); + this.height = d.GetInt32(PcxDirectory.TagYMax); + break; + + case XLPictureFormat.Jpeg: + d = JpegMetadataReader.ReadMetadata(stream).OfType().First(); + this.width = d.GetInt32(JpegDirectory.TagImageWidth); + this.height = d.GetInt32(JpegDirectory.TagImageHeight); + break; } - return "image/unknown"; + this.OriginalWidth = this.width; + this.OriginalHeight = this.height; } - private void DeduceDimensionsFromBitmap(Bitmap bitmap) + private void DeduceImageFormat(Stream stream, XLPictureFormat format) { - this.OriginalWidth = bitmap.Width; - this.OriginalHeight = bitmap.Height; - - this.width = bitmap.Width; - this.height = bitmap.Height; + DeduceImageFormat(stream); + if (this.Format != format) + throw new ArgumentException(nameof(format)); } -#endif + + private void DeduceImageFormat(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); + var fileType = FileTypeDetector.DetectFileType(stream); + if (fileType == FileType.Unknown) + throw new NotImplementedException(); + else if (FormatMap.ContainsKey(fileType)) + this.Format = FormatMap[fileType]; + else + throw new NotImplementedException(); + } } } diff --git a/ClosedXML/Excel/Drawings/XLPictures.cs b/ClosedXML/Excel/Drawings/XLPictures.cs index aa62fee..aa72c5e 100644 --- a/ClosedXML/Excel/Drawings/XLPictures.cs +++ b/ClosedXML/Excel/Drawings/XLPictures.cs @@ -73,18 +73,13 @@ public IXLPicture Add(string imageFile) { -#if _NETFRAMEWORK_ - - using (var bitmap = Image.FromFile(imageFile) as Bitmap) + using (var fs = File.Open(imageFile, FileMode.Open)) { - var picture = new XLPicture(_worksheet, bitmap); + var picture = new XLPicture(_worksheet, fs); _pictures.Add(picture); picture.Name = GetNextPictureName(); return picture; } -#else - throw new NotImplementedException("System.Drawing.Image is not supported in .NET Standard"); -#endif } public IXLPicture Add(string imageFile, string name) diff --git a/ClosedXML/Excel/XLWorkbook_Load.cs b/ClosedXML/Excel/XLWorkbook_Load.cs index d8ebf53..035f52c 100644 --- a/ClosedXML/Excel/XLWorkbook_Load.cs +++ b/ClosedXML/Excel/XLWorkbook_Load.cs @@ -632,10 +632,12 @@ var imagePart = drawingsPart.GetPartById(imgId); using (var stream = imagePart.GetStream()) + using (var ms = new MemoryStream()) { + stream.CopyTo(ms); var vsdp = GetPropertiesFromAnchor(anchor); - var picture = ws.AddPicture(stream, vsdp.Name) as XLPicture; + var picture = ws.AddPicture(ms, vsdp.Name) as XLPicture; picture.RelId = imgId; Xdr.ShapeProperties spPr = anchor.Descendants().First(); @@ -2398,4 +2400,4 @@ return false; } } -} \ No newline at end of file +} diff --git a/ClosedXML_Tests/Examples/ImageHandlingTests.cs b/ClosedXML_Tests/Examples/ImageHandlingTests.cs index 02a87dd..cbbbd94 100644 --- a/ClosedXML_Tests/Examples/ImageHandlingTests.cs +++ b/ClosedXML_Tests/Examples/ImageHandlingTests.cs @@ -1,4 +1,3 @@ -#if _NETFRAMEWORK_ using ClosedXML_Examples; using NUnit.Framework; @@ -20,4 +19,3 @@ } } } -#endif \ No newline at end of file diff --git a/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs b/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs index 6f544fa..6539785 100644 --- a/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs +++ b/ClosedXML_Tests/Excel/ImageHandling/PictureTests.cs @@ -7,12 +7,12 @@ using System.Linq; using System.Reflection; -#if _NETFRAMEWORK_ namespace ClosedXML_Tests { [TestFixture] public class PictureTests { +#if _NETFRAMEWORK_ [Test] public void CanAddPictureFromBitmap() { @@ -34,6 +34,28 @@ } } } +#endif + + [Test] + public void CanAddPictureFromStream() + { + using (var wb = new XLWorkbook()) + { + var ws = wb.AddWorksheet("Sheet1"); + + using (var resourceStream = Assembly.GetAssembly(typeof(ClosedXML_Examples.BasicTable)).GetManifestResourceStream("ClosedXML_Examples.Resources.SampleImage.jpg")) + { + var picture = ws.AddPicture(resourceStream, "MyPicture") + .WithPlacement(XLPicturePlacement.FreeFloating) + .MoveTo(50, 50) + .WithSize(200, 200); + + Assert.AreEqual(XLPictureFormat.Jpeg, picture.Format); + Assert.AreEqual(200, picture.Width); + Assert.AreEqual(200, picture.Height); + } + } + } [Test] public void CanAddPictureFromFile() @@ -78,9 +100,8 @@ var ws = wb.AddWorksheet("Sheet1"); using (var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ClosedXML_Tests.Resource.Images.ImageHandling.png")) - using (var bitmap = Bitmap.FromStream(resourceStream) as Bitmap) { - var pic = ws.AddPicture(bitmap, "MyPicture") + var pic = ws.AddPicture(resourceStream, "MyPicture") .WithPlacement(XLPicturePlacement.FreeFloating) .MoveTo(50, 50); @@ -234,4 +255,3 @@ } } } -#endif \ No newline at end of file