diff --git a/ClosedXML/ClosedXML.csproj b/ClosedXML/ClosedXML.csproj index d5ddc65..0539fcd 100644 --- a/ClosedXML/ClosedXML.csproj +++ b/ClosedXML/ClosedXML.csproj @@ -72,6 +72,10 @@ + + + + @@ -343,4 +347,4 @@ --> - \ No newline at end of file + diff --git a/ClosedXML/Excel/Drawings/IXLMarker.cs b/ClosedXML/Excel/Drawings/IXLMarker.cs new file mode 100644 index 0000000..048020b --- /dev/null +++ b/ClosedXML/Excel/Drawings/IXLMarker.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ClosedXML.Excel.Drawings +{ + public interface IXLMarker + { + Int32 ColumnId { get; set; } + Int32 RowId { get; set; } + Double ColumnOffset { get; set; } + Double RowOffset { get; set; } + } +} diff --git a/ClosedXML/Excel/Drawings/IXLPicture.cs b/ClosedXML/Excel/Drawings/IXLPicture.cs new file mode 100644 index 0000000..96a9eb6 --- /dev/null +++ b/ClosedXML/Excel/Drawings/IXLPicture.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace ClosedXML.Excel.Drawings +{ + public interface IXLPicture + { + Stream ImageStream { get; set; } + + List GetMarkers(); + void AddMarker(IXLMarker marker); + + long MaxHeight { get; set; } + long MaxWidth { get; set; } + long Width { get; set; } + long Height { get; set; } + long PaddingX { get; set; } + long PaddingY { get; set; } + long EMUOffsetX { get; set; } + long EMUOffsetY { get; set; } + + String Name { get; set; } + } +} diff --git a/ClosedXML/Excel/Drawings/XLMarker.cs b/ClosedXML/Excel/Drawings/XLMarker.cs new file mode 100644 index 0000000..a523677 --- /dev/null +++ b/ClosedXML/Excel/Drawings/XLMarker.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace ClosedXML.Excel.Drawings +{ + public class XLMarker : IXLMarker + { + private Int32 colId; + private Int32 rowId; + private Double colOffset; + private Double rowOffset; + + public Int32 ColumnId + { + set + { + this.colId = value; + } + get + { + return this.colId - 1; + } + } + + public Int32 RowId + { + set + { + this.rowId = value; + } + get + { + return this.rowId - 1; + } + } + + public Double ColumnOffset + { + set + { + this.colOffset = value; + } + get + { + return this.colOffset; + } + } + + public Double RowOffset + { + set + { + this.rowOffset = value; + } + get + { + return this.rowOffset; + } + } + } +} diff --git a/ClosedXML/Excel/Drawings/XLPicture.cs b/ClosedXML/Excel/Drawings/XLPicture.cs new file mode 100644 index 0000000..2419568 --- /dev/null +++ b/ClosedXML/Excel/Drawings/XLPicture.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace ClosedXML.Excel.Drawings +{ + public class XLPicture : IXLPicture + { + private MemoryStream imgStream; + private List Markers; + private String name; + public bool NoChangeAspect; + public bool NoMove; + public bool NoResize; + + private long iMaxWidth = 500; + private long iMaxHeight = 500; + + private long iWidth; + private long iHeight; + + private long iOffsetX; + private long iOffsetY; + + private float iVerticalResolution; + private float iHorizontalResolution; + + private bool isResized = false; + + private void Resize() + { + if (iWidth > iMaxHeight || iHeight > iMaxWidth) + { + var scaleX = (double)iWidth / (double)iMaxWidth; + var scaleY = (double)iHeight / (double)iMaxHeight; + var scale = Math.Max(scaleX, scaleY); + iWidth = (int)((double)iWidth / scale); + iHeight = (int)((double)iHeight / scale); + } + isResized = true; + } + + public long MaxWidth + { + get + { + return ConvertToEmu(iMaxWidth, iHorizontalResolution); + } + set + { + iMaxWidth = value; + isResized = false; + } + } + + + public long MaxHeight + { + get + { + return ConvertToEmu(iMaxHeight, iVerticalResolution); + } + set + { + iMaxHeight = value; + isResized = false; + } + } + + public long Width + { + get + { + if (!isResized) + { + Resize(); + } + return ConvertToEmu(iWidth, iHorizontalResolution); + } + set { } + } + + public long Height + { + get + { + if (!isResized) + { + Resize(); + } + return ConvertToEmu(iHeight, iVerticalResolution); + } + set { } + } + + public long RawHeight + { + get { return (long)iHeight; } + } + public long RawWidth + { + get { return (long)iWidth; } + } + + public long PaddingX + { + get { return ConvertToEmu(iOffsetX, iHorizontalResolution); } + set { iOffsetX = value; } + } + public long PaddingY + { + get { return ConvertToEmu(iOffsetY, iVerticalResolution); } + set { iOffsetY = value; } + } + + public long EMUOffsetX + { + get + { + return iOffsetX; + } + set + { + iOffsetX = value; + } + } + + public long EMUOffsetY + { + get + { + return iOffsetY; + } + set + { + iOffsetY = value; + } + } + + private long ConvertToEmu(long pixels, float resolution) + { + return (long)(914400 * pixels / resolution); + } + + public Stream ImageStream + { + get + { + return imgStream; + } + set + { + if (imgStream == null) + { + imgStream = new MemoryStream(); + } + else + { + imgStream.Dispose(); + imgStream = new MemoryStream(); + } + value.CopyTo(imgStream); + imgStream.Seek(0, SeekOrigin.Begin); + + using (var bitmap = new System.Drawing.Bitmap(imgStream)) + { + iWidth = (long)bitmap.Width; + iHeight = (long)bitmap.Height; + iHorizontalResolution = bitmap.HorizontalResolution; + iVerticalResolution = bitmap.VerticalResolution; + } + imgStream.Seek(0, SeekOrigin.Begin); + } + } + + public List GetMarkers() + { + return Markers; + } + public void AddMarker(IXLMarker marker) + { + if (Markers == null) + { + Markers = new List(); + } + Markers.Add(marker); + } + + public String Name + { + get + { + return name; + } + set + { + name = value; + } + } + } +} diff --git a/ClosedXML/Excel/IXLWorksheet.cs b/ClosedXML/Excel/IXLWorksheet.cs index 0d9dcd6..897e694 100644 --- a/ClosedXML/Excel/IXLWorksheet.cs +++ b/ClosedXML/Excel/IXLWorksheet.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace ClosedXML.Excel { @@ -285,7 +286,7 @@ IXLTables Tables { get; } /// - /// Copies the + /// Copies the /// /// /// @@ -368,5 +369,9 @@ Object Evaluate(String expression); String Author { get; set; } + + List Pictures(); + + void AddPicture(Drawings.XLPicture pic); } } diff --git a/ClosedXML/Excel/XLWorkbook_Save.cs b/ClosedXML/Excel/XLWorkbook_Save.cs index 9a99f83..457a474 100644 --- a/ClosedXML/Excel/XLWorkbook_Save.cs +++ b/ClosedXML/Excel/XLWorkbook_Save.cs @@ -14,6 +14,8 @@ using DocumentFormat.OpenXml.Vml.Office; using DocumentFormat.OpenXml.Vml.Spreadsheet; using Vml = DocumentFormat.OpenXml.Vml; +using A = DocumentFormat.OpenXml.Drawing; +using Xdr = DocumentFormat.OpenXml.Drawing.Spreadsheet; using BackgroundColor = DocumentFormat.OpenXml.Spreadsheet.BackgroundColor; using BottomBorder = DocumentFormat.OpenXml.Spreadsheet.BottomBorder; using Break = DocumentFormat.OpenXml.Spreadsheet.Break; @@ -2501,6 +2503,99 @@ return stroke; } + private static void AddPictureAnchor(WorksheetPart worksheetPart, Drawings.IXLPicture picture) + { + var drawingsPart = worksheetPart.DrawingsPart ?? worksheetPart.AddNewPart(); + + if (drawingsPart.WorksheetDrawing == null) + { + drawingsPart.WorksheetDrawing = new Xdr.WorksheetDrawing(); + } + + var worksheetDrawing = drawingsPart.WorksheetDrawing; + + var imagePart = drawingsPart.AddImagePart(ImagePartType.Jpeg); + + using (Stream stream = new MemoryStream()) + { + picture.ImageStream.CopyTo(stream); + stream.Seek(0, SeekOrigin.Begin); + imagePart.FeedData(stream); + } + + var extentsCx = picture.Width; + var extentsCy = picture.Height; + + var nvps = worksheetDrawing.Descendants(); + var nvpId = nvps.Count() > 0 ? + (UInt32Value)worksheetDrawing.Descendants().Max(p => p.Id.Value) + 1 : + 1U; + var markers = picture.GetMarkers(); + Xdr.TwoCellAnchor twoCellAnchor; + Xdr.FromMarker fMark; + Xdr.ToMarker tMark; + + if (markers.Count == 2) + { + fMark = new Xdr.FromMarker + { + ColumnId = new Xdr.ColumnId(markers[0].ColumnId.ToString()), + RowId = new Xdr.RowId(markers[0].RowId.ToString()), + ColumnOffset = new Xdr.ColumnOffset((markers[0].ColumnOffset + picture.PaddingX).ToString()), + RowOffset = new Xdr.RowOffset((markers[0].RowOffset + picture.PaddingY).ToString()) + }; + tMark = new Xdr.ToMarker + { + ColumnId = new Xdr.ColumnId(markers[1].ColumnId.ToString()), + RowId = new Xdr.RowId(markers[1].RowId.ToString()), + ColumnOffset = new Xdr.ColumnOffset((markers[1].ColumnOffset + picture.PaddingX).ToString()), + RowOffset = new Xdr.RowOffset((markers[1].RowOffset + picture.PaddingY).ToString()) + }; + } + else + { + fMark = new Xdr.FromMarker + { + ColumnId = new Xdr.ColumnId(markers[0].ColumnId.ToString()), + RowId = new Xdr.RowId(markers[0].RowId.ToString()), + ColumnOffset = new Xdr.ColumnOffset((markers[0].ColumnOffset + picture.PaddingX).ToString()), + RowOffset = new Xdr.RowOffset((markers[0].RowOffset + picture.PaddingY).ToString()) + }; + + tMark = new Xdr.ToMarker + { + ColumnId = new Xdr.ColumnId(markers[0].ColumnId.ToString()), + RowId = new Xdr.RowId(markers[0].RowId.ToString()), + ColumnOffset = new Xdr.ColumnOffset((markers[0].ColumnOffset + extentsCx + picture.PaddingX).ToString()), + RowOffset = new Xdr.RowOffset((markers[0].RowOffset + extentsCy + picture.PaddingY).ToString()) + }; + } + + twoCellAnchor = new Xdr.TwoCellAnchor( + fMark, tMark, + new Xdr.Picture( + new Xdr.NonVisualPictureProperties( + new Xdr.NonVisualDrawingProperties { Id = nvpId, Name = picture.Name }, + new Xdr.NonVisualPictureDrawingProperties(new A.PictureLocks { NoChangeAspect = true, NoMove = true, NoResize = true }) + ), + new Xdr.BlipFill( + new A.Blip { Embed = drawingsPart.GetIdOfPart(imagePart), CompressionState = A.BlipCompressionValues.Print }, + new A.Stretch(new A.FillRectangle()) + ), + new Xdr.ShapeProperties( + new A.Transform2D( + new A.Offset { X = 0, Y = 0 }, + new A.Extents { Cx = extentsCx, Cy = extentsCy } + ), + new A.PresetGeometry { Preset = A.ShapeTypeValues.Rectangle } + ) + ), + new Xdr.ClientData() + ); + + worksheetDrawing.Append(twoCellAnchor); + } + private static Vml.TextBox GetTextBox(IXLDrawingStyle ds) { var sb = new StringBuilder(); @@ -4564,19 +4659,6 @@ #endregion - #region Drawings - - //worksheetPart.Worksheet.RemoveAllChildren(); - //{ - // OpenXmlElement previousElement = cm.GetPreviousElementFor(XLWSContentManager.XLWSContents.Drawing); - // worksheetPart.Worksheet.InsertAfter(new Drawing() { Id = String.Format("rId{0}", 1) }, previousElement); - //} - - //Drawing drawing = worksheetPart.Worksheet.Elements().First(); - //cm.SetElement(XLWSContentManager.XLWSContents.Drawing, drawing); - - #endregion - #region Tables worksheetPart.Worksheet.RemoveAllChildren(); @@ -4596,6 +4678,36 @@ #endregion + #region Drawings + + //worksheetPart.Worksheet.RemoveAllChildren(); + //{ + // OpenXmlElement previousElement = cm.GetPreviousElementFor(XLWSContentManager.XLWSContents.Drawing); + // worksheetPart.Worksheet.InsertAfter(new Drawing() { Id = String.Format("rId{0}", 1) }, previousElement); + //} + + //Drawing drawing = worksheetPart.Worksheet.Elements().First(); + //cm.SetElement(XLWSContentManager.XLWSContents.Drawing, drawing); + + var pics = xlWorksheet.Pictures(); + if (pics != null) + { + foreach (Drawings.IXLPicture pic in pics) + { + AddPictureAnchor(worksheetPart, pic); + } + } + + if (xlWorksheet.Pictures().Count > 0) + { + Drawing worksheetDrawing = new Drawing { Id = worksheetPart.GetIdOfPart(worksheetPart.DrawingsPart) }; + worksheetDrawing.AddNamespaceDeclaration("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships"); + worksheetPart.Worksheet.InsertBefore(worksheetDrawing, tableParts); + //worksheetPart.Worksheet.Append(worksheetDrawing); + } + + #endregion + #region LegacyDrawing if (xlWorksheet.LegacyDrawingIsNew) diff --git a/ClosedXML/Excel/XLWorksheet.cs b/ClosedXML/Excel/XLWorksheet.cs index 227ee49..12cce27 100644 --- a/ClosedXML/Excel/XLWorksheet.cs +++ b/ClosedXML/Excel/XLWorksheet.cs @@ -96,6 +96,7 @@ public Boolean LegacyDrawingIsNew; private Double _columnWidth; public XLWorksheetInternals Internals { get; private set; } + private List pictures; public override IEnumerable Styles { @@ -1491,5 +1492,19 @@ } public String Author { get; set; } + + public List Pictures() + { + return pictures; + } + + public void AddPicture(Drawings.XLPicture pic) + { + if (pictures == null) + { + pictures = new List(); + } + pictures.Add(pic); + } } }