using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Reflection;
namespace ClosedXML.Excel.Drawings
{
[DebuggerDisplay("{Name}")]
internal class XLPicture : IXLPicture
{
private readonly IXLWorksheet _worksheet;
private Int32 height;
private Int32 width;
internal XLPicture(IXLWorksheet worksheet, Stream stream, XLPictureFormat format)
: this(worksheet)
{
if (stream == null) throw new ArgumentNullException(nameof(stream));
this.Format = format;
this.ImageStream = new MemoryStream();
{
stream.Position = 0;
stream.CopyTo(ImageStream);
ImageStream.Seek(0, SeekOrigin.Begin);
using (var bitmap = new Bitmap(ImageStream))
{
var expectedFormat = typeof(System.Drawing.Imaging.ImageFormat).GetProperty(this.Format.ToString()).GetValue(null, null) as System.Drawing.Imaging.ImageFormat;
if (expectedFormat.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);
}
}
internal XLPicture(IXLWorksheet worksheet, Bitmap bitmap)
: this(worksheet)
{
if (bitmap == null) throw new ArgumentNullException(nameof(bitmap));
this.ImageStream = new MemoryStream();
bitmap.Save(ImageStream, bitmap.RawFormat);
ImageStream.Seek(0, SeekOrigin.Begin);
DeduceDimensionsFromBitmap(bitmap);
var formats = typeof(ImageFormat).GetProperties(BindingFlags.Static | BindingFlags.Public)
.Where(pi => (pi.GetValue(null, null) as ImageFormat).Guid.Equals(bitmap.RawFormat.Guid));
if (!formats.Any() || formats.Count() > 1)
throw new ArgumentException("Unsupported or unknown image format in bitmap");
this.Format = Enum.Parse(typeof(XLPictureFormat), formats.Single().Name, true).CastTo<XLPictureFormat>();
}
private XLPicture(IXLWorksheet worksheet)
{
if (worksheet == null) throw new ArgumentNullException(nameof(worksheet));
this._worksheet = worksheet;
this.Placement = XLPicturePlacement.MoveAndSize;
this.Markers = new Dictionary<XLMarkerPosition, IXLMarker>()
{
[XLMarkerPosition.TopLeft] = null,
[XLMarkerPosition.BottomRight] = null
};
}
public IXLAddress BottomRightCellAddress
{
get
{
return Markers[XLMarkerPosition.BottomRight].Address;
}
private set
{
if (!value.Worksheet.Equals(this._worksheet))
throw new ArgumentOutOfRangeException(nameof(value.Worksheet));
this.Markers[XLMarkerPosition.BottomRight] = new XLMarker(value);
}
}
public XLPictureFormat Format { get; private set; }
public Int32 Height
{
get { return height; }
set
{
if (this.Placement == XLPicturePlacement.MoveAndSize)
throw new ArgumentException("To set the height, the placement should be FreeFloating or Move");
height = value;
}
}
public MemoryStream ImageStream { get; private set; }
public Int32 Left
{
get { return Markers[XLMarkerPosition.TopLeft]?.Offset.X ?? 0; }
set
{
if (this.Placement != XLPicturePlacement.FreeFloating)
throw new ArgumentException("To set the left-hand offset, the placement should be FreeFloating");
Markers[XLMarkerPosition.TopLeft] = new XLMarker(_worksheet.Cell(1, 1).Address, new Point(value, this.Top));
}
}
public String Name { get; set; }
public Int32 OriginalHeight { get; private set; }
public Int32 OriginalWidth { get; private set; }
public XLPicturePlacement Placement { get; set; }
public Int32 Top
{
get { return Markers[XLMarkerPosition.TopLeft]?.Offset.Y ?? 0; }
set
{
if (this.Placement != XLPicturePlacement.FreeFloating)
throw new ArgumentException("To set the top offset, the placement should be FreeFloating");
Markers[XLMarkerPosition.TopLeft] = new XLMarker(_worksheet.Cell(1, 1).Address, new Point(this.Left, value));
}
}
public IXLAddress TopLeftCellAddress
{
get
{
return Markers[XLMarkerPosition.TopLeft].Address;
}
private set
{
if (!value.Worksheet.Equals(this._worksheet))
throw new ArgumentOutOfRangeException(nameof(value.Worksheet));
this.Markers[XLMarkerPosition.TopLeft] = new XLMarker(value);
}
}
public Int32 Width
{
get { return width; }
set
{
if (this.Placement == XLPicturePlacement.MoveAndSize)
throw new ArgumentException("To set the width, the placement should be FreeFloating or Move");
width = value;
}
}
public IXLWorksheet Worksheet
{
get { return _worksheet; }
}
internal IDictionary<XLMarkerPosition, IXLMarker> Markers { get; private set; }
public void Dispose()
{
this.ImageStream.Dispose();
}
public Point GetOffset(XLMarkerPosition position)
{
return Markers[position].Offset;
}
public IXLPicture MoveTo(Int32 left, Int32 top)
{
this.Placement = XLPicturePlacement.FreeFloating;
this.Left = left;
this.Top = top;
return this;
}
public IXLPicture MoveTo(IXLAddress cell)
{
return MoveTo(cell, 0, 0);
}
public IXLPicture MoveTo(IXLAddress cell, Int32 xOffset, Int32 yOffset)
{
return MoveTo(cell, new Point(xOffset, yOffset));
}
public IXLPicture MoveTo(IXLAddress cell, Point offset)
{
if (cell == null) throw new ArgumentNullException(nameof(cell));
this.Placement = XLPicturePlacement.Move;
this.TopLeftCellAddress = cell;
this.Markers[XLMarkerPosition.TopLeft].Offset = offset;
return this;
}
public IXLPicture MoveTo(IXLAddress fromCell, IXLAddress toCell)
{
return MoveTo(fromCell, 0, 0, toCell, 0, 0);
}
public IXLPicture MoveTo(IXLAddress fromCell, Int32 fromCellXOffset, Int32 fromCellYOffset, IXLAddress toCell, Int32 toCellXOffset, Int32 toCellYOffset)
{
return MoveTo(fromCell, new Point(fromCellXOffset, fromCellYOffset), toCell, new Point(toCellXOffset, toCellYOffset));
}
public IXLPicture MoveTo(IXLAddress fromCell, Point fromOffset, IXLAddress toCell, Point toOffset)
{
if (fromCell == null) throw new ArgumentNullException(nameof(fromCell));
if (toCell == null) throw new ArgumentNullException(nameof(toCell));
this.Placement = XLPicturePlacement.MoveAndSize;
this.TopLeftCellAddress = fromCell;
this.Markers[XLMarkerPosition.TopLeft].Offset = fromOffset;
this.BottomRightCellAddress = toCell;
this.Markers[XLMarkerPosition.BottomRight].Offset = toOffset;
return this;
}
public IXLPicture Scale(Double factor, Boolean relativeToOriginal = false)
{
return this.ScaleHeight(factor, relativeToOriginal).ScaleWidth(factor, relativeToOriginal);
}
public IXLPicture ScaleHeight(Double factor, Boolean relativeToOriginal = false)
{
this.Height = Convert.ToInt32((relativeToOriginal ? this.OriginalHeight : this.Height) * factor);
return this;
}
public IXLPicture ScaleWidth(Double factor, Boolean relativeToOriginal = false)
{
this.Width = Convert.ToInt32((relativeToOriginal ? this.OriginalWidth : this.Width) * factor);
return this;
}
public IXLPicture WithPlacement(XLPicturePlacement value)
{
this.Placement = value;
return this;
}
public IXLPicture WithSize(Int32 width, Int32 height)
{
this.Width = width;
this.Height = height;
return this;
}
private void DeduceDimensionsFromBitmap(Bitmap bitmap)
{
this.OriginalWidth = bitmap.Width;
this.OriginalHeight = bitmap.Height;
this.width = bitmap.Width;
this.height = bitmap.Height;
}
}
}