/*******************************************************************************
* You may amend and distribute as you like, but don't remove this header!
*
* EPPlus provides server-side generation of Excel 2007/2010 spreadsheets.
* See http://www.codeplex.com/EPPlus for details.
*
* Copyright (C) 2011 Jan Källman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The GNU Lesser General Public License can be viewed at http://www.opensource.org/licenses/lgpl-license.php
* If you unfamiliar with this license or have questions about it, here is an http://www.gnu.org/licenses/gpl-faq.html
*
* All code and executables are provided "as is" with no warranty either express or implied.
* The author accepts no liability for any damage or loss of business that this product may cause.
*
* Code change notes:
*
* Author Change Date
* ******************************************************************************
* Jan Källman Initial Release 2009-12-22
* Jan Källman License changed GPL-->LGPL 2011-12-16
*******************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Packaging;
using System.Xml;
using System.Collections;
using System.IO;
using System.Drawing;
using System.Linq;
using OfficeOpenXml.Drawing.Chart;
using OfficeOpenXml.Table.PivotTable;
namespace OfficeOpenXml.Drawing
{
///
/// Collection for Drawing objects.
///
public class ExcelDrawings : IEnumerable
{
private XmlDocument _drawingsXml=new XmlDocument();
private Dictionary _drawingNames;
private List _drawings;
internal class ImageCompare
{
internal byte[] image { get; set; }
internal string relID { get; set; }
internal bool Comparer(byte[] compareImg)
{
if (compareImg.Length != image.Length)
{
return false;
}
for (int i = 0; i < image.Length; i++)
{
if (image[i] != compareImg[i])
{
return false;
}
}
return true; //Equal
}
}
//internal List _pics = new List();
internal Dictionary _hashes = new Dictionary();
internal ExcelPackage _package;
internal PackageRelationship _drawingRelation=null;
internal ExcelDrawings(ExcelPackage xlPackage, ExcelWorksheet sheet)
{
_drawingsXml = new XmlDocument();
_drawingsXml.PreserveWhitespace = false;
_drawings = new List();
_drawingNames = new Dictionary();
_package = xlPackage;
Worksheet = sheet;
XmlNode node = sheet.WorksheetXml.SelectSingleNode("//d:drawing", sheet.NameSpaceManager);
CreateNSM();
if (node != null)
{
_drawingRelation = sheet.Part.GetRelationship(node.Attributes["r:id"].Value);
_uriDrawing = PackUriHelper.ResolvePartUri(sheet.WorksheetUri, _drawingRelation.TargetUri);
_part = xlPackage.Package.GetPart(_uriDrawing);
XmlHelper.LoadXmlSafe(_drawingsXml, _part.GetStream());
AddDrawings();
}
}
internal ExcelWorksheet Worksheet { get; set; }
///
/// A reference to the drawing xml document
///
public XmlDocument DrawingXml
{
get
{
return _drawingsXml;
}
}
private void AddDrawings()
{
XmlNodeList list = _drawingsXml.SelectNodes("//xdr:twoCellAnchor", NameSpaceManager);
foreach (XmlNode node in list)
{
ExcelDrawing dr = ExcelDrawing.GetDrawing(this, node);
_drawings.Add(dr);
if (!_drawingNames.ContainsKey(dr.Name.ToLower()))
{
_drawingNames.Add(dr.Name.ToLower(), _drawings.Count - 1);
}
}
}
#region NamespaceManager
///
/// Creates the NamespaceManager.
///
private void CreateNSM()
{
NameTable nt = new NameTable();
_nsManager = new XmlNamespaceManager(nt);
_nsManager.AddNamespace("a", ExcelPackage.schemaDrawings);
_nsManager.AddNamespace("xdr", ExcelPackage.schemaSheetDrawings);
_nsManager.AddNamespace("c", ExcelPackage.schemaChart);
_nsManager.AddNamespace("r", ExcelPackage.schemaRelationships);
}
///
/// Provides access to a namespace manager instance to allow XPath searching
///
XmlNamespaceManager _nsManager=null;
public XmlNamespaceManager NameSpaceManager
{
get
{
return _nsManager;
}
}
#endregion
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return (_drawings.GetEnumerator());
}
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return (_drawings.GetEnumerator());
}
#endregion
///
/// Returns the drawing at the specified position.
///
/// The position of the drawing. 0-base
///
public ExcelDrawing this[int PositionID]
{
get
{
return (_drawings[PositionID]);
}
}
///
/// Returns the drawing matching the specified name
///
/// The name of the worksheet
///
public ExcelDrawing this[string Name]
{
get
{
if (_drawingNames.ContainsKey(Name.ToLower()))
{
return _drawings[_drawingNames[Name.ToLower()]];
}
else
{
return null;
}
}
}
public int Count
{
get
{
if (_drawings == null)
{
return 0;
}
else
{
return _drawings.Count;
}
}
}
PackagePart _part=null;
internal PackagePart Part
{
get
{
return _part;
}
}
Uri _uriDrawing=null;
public Uri UriDrawing
{
get
{
return _uriDrawing;
}
}
#endregion
#region Add functions
///
/// Add a new chart to the worksheet.
/// Do not support Bubble-, Radar-, Stock- or Surface charts.
///
///
/// Type of chart
/// The pivottable source for a pivotchart
/// The chart
public ExcelChart AddChart(string Name, eChartType ChartType, ExcelPivotTable PivotTableSource)
{
if(_drawingNames.ContainsKey(Name.ToLower()))
{
throw new Exception("Name already exists in the drawings collection");
}
if (ChartType == eChartType.Bubble ||
ChartType == eChartType.Bubble3DEffect ||
ChartType == eChartType.Radar ||
ChartType == eChartType.RadarFilled ||
ChartType == eChartType.RadarMarkers ||
ChartType == eChartType.StockHLC ||
ChartType == eChartType.StockOHLC ||
ChartType == eChartType.StockVOHLC ||
ChartType == eChartType.Surface ||
ChartType == eChartType.SurfaceTopView ||
ChartType == eChartType.SurfaceTopViewWireframe ||
ChartType == eChartType.SurfaceWireframe)
{
throw(new NotImplementedException("Chart type is not supported in the current version"));
}
XmlElement drawNode = CreateDrawingXml();
ExcelChart chart = ExcelChart.GetNewChart(this, drawNode, ChartType, null, PivotTableSource);
chart.Name = Name;
_drawings.Add(chart);
_drawingNames.Add(Name.ToLower(), _drawings.Count - 1);
return chart;
}
///
/// Add a new chart to the worksheet.
/// Do not support Bubble-, Radar-, Stock- or Surface charts.
///
///
/// Type of chart
/// The chart
public ExcelChart AddChart(string Name, eChartType ChartType)
{
return AddChart(Name, ChartType, null);
}
///
/// Add a picure to the worksheet
///
///
/// An image. Allways saved in then JPeg format
///
public ExcelPicture AddPicture(string Name, Image image)
{
return AddPicture(Name, image, null);
}
///
/// Add a picure to the worksheet
///
///
/// An image. Allways saved in then JPeg format
/// Picture Hyperlink
///
public ExcelPicture AddPicture(string Name, Image image, Uri Hyperlink)
{
if (image != null)
{
if (_drawingNames.ContainsKey(Name.ToLower()))
{
throw new Exception("Name already exists in the drawings collection");
}
XmlElement drawNode = CreateDrawingXml();
drawNode.SetAttribute("editAs", "oneCell");
ExcelPicture pic = new ExcelPicture(this, drawNode, image, Hyperlink);
pic.Name = Name;
_drawings.Add(pic);
_drawingNames.Add(Name.ToLower(), _drawings.Count - 1);
return pic;
}
throw (new Exception("AddPicture: Image can't be null"));
}
///
/// Add a picure to the worksheet
///
///
/// The image file
///
public ExcelPicture AddPicture(string Name, FileInfo ImageFile)
{
return AddPicture(Name, ImageFile, null);
}
///
/// Add a picure to the worksheet
///
///
/// The image file
/// Picture Hyperlink
///
public ExcelPicture AddPicture(string Name, FileInfo ImageFile, Uri Hyperlink)
{
if (ImageFile != null)
{
if (_drawingNames.ContainsKey(Name.ToLower()))
{
throw new Exception("Name already exists in the drawings collection");
}
XmlElement drawNode = CreateDrawingXml();
drawNode.SetAttribute("editAs", "oneCell");
ExcelPicture pic = new ExcelPicture(this, drawNode, ImageFile, Hyperlink);
pic.Name = Name;
_drawings.Add(pic);
_drawingNames.Add(Name.ToLower(), _drawings.Count - 1);
return pic;
}
throw (new Exception("AddPicture: ImageFile can't be null"));
}
///
/// Add a new shape to the worksheet
///
/// Name
/// Shape style
/// The shape object
public ExcelShape AddShape(string Name, eShapeStyle Style)
{
if (_drawingNames.ContainsKey(Name.ToLower()))
{
throw new Exception("Name already exists in the drawings collection");
}
XmlElement drawNode = CreateDrawingXml();
ExcelShape shape = new ExcelShape(this, drawNode, Style);
shape.Name = Name;
shape.Style = Style;
_drawings.Add(shape);
_drawingNames.Add(Name.ToLower(), _drawings.Count - 1);
return shape;
}
private XmlElement CreateDrawingXml()
{
if (DrawingXml.OuterXml == "")
{
DrawingXml.LoadXml(string.Format("", ExcelPackage.schemaSheetDrawings, ExcelPackage.schemaDrawings));
_uriDrawing = new Uri(string.Format("/xl/drawings/drawing{0}.xml", Worksheet.SheetID),UriKind.Relative);
Package package = Worksheet._package.Package;
_part = package.CreatePart(_uriDrawing, "application/vnd.openxmlformats-officedocument.drawing+xml", _package.Compression);
StreamWriter streamChart = new StreamWriter(_part.GetStream(FileMode.Create, FileAccess.Write));
DrawingXml.Save(streamChart);
streamChart.Close();
package.Flush();
_drawingRelation = Worksheet.Part.CreateRelationship(PackUriHelper.GetRelativeUri(Worksheet.WorksheetUri, _uriDrawing), TargetMode.Internal, ExcelPackage.schemaRelationships + "/drawing");
XmlElement e = Worksheet.WorksheetXml.CreateElement("drawing", ExcelPackage.schemaMain);
e.SetAttribute("id",ExcelPackage.schemaRelationships, _drawingRelation.Id);
Worksheet.WorksheetXml.DocumentElement.AppendChild(e);
package.Flush();
}
XmlNode colNode = _drawingsXml.SelectSingleNode("//xdr:wsDr", NameSpaceManager);
XmlElement drawNode = _drawingsXml.CreateElement("xdr", "twoCellAnchor", ExcelPackage.schemaSheetDrawings);
colNode.AppendChild(drawNode);
//Add from position Element;
XmlElement fromNode = _drawingsXml.CreateElement("xdr","from", ExcelPackage.schemaSheetDrawings);
drawNode.AppendChild(fromNode);
fromNode.InnerXml = "0000";
//Add to position Element;
XmlElement toNode = _drawingsXml.CreateElement("xdr", "to", ExcelPackage.schemaSheetDrawings);
drawNode.AppendChild(toNode);
toNode.InnerXml = "100100";
return drawNode;
}
#endregion
#region Remove methods
///
/// Removes a drawing.
///
/// The index of the drawing
public void Remove(int Index)
{
var draw=_drawings[Index];
draw.DeleteMe();
for (int i = Index + 1; i < _drawings.Count; i++)
{
_drawingNames[_drawings[i].Name.ToLower()]--;
}
_drawingNames.Remove(draw.Name.ToLower());
_drawings.Remove(draw);
}
///
/// Removes a drawing.
///
/// The drawing
public void Remove(ExcelDrawing Drawing)
{
Remove(_drawingNames[Drawing.Name.ToLower()]);
}
///
/// Removes a drawing.
///
/// The name of the drawing
public void Remove(string Name)
{
Remove(_drawingNames[Name.ToLower()]);
}
///
/// Removes all drawings from the collection
///
public void Clear()
{
while (Count > 0)
{
Remove(0);
}
}
#endregion
internal void AdjustWidth(int[,] pos)
{
var ix = 0;
//Now set the size for all drawings depending on the editAs property.
foreach (OfficeOpenXml.Drawing.ExcelDrawing d in this)
{
if (d.EditAs != Drawing.eEditAs.TwoCell)
{
if (d.EditAs == Drawing.eEditAs.Absolute)
{
d.SetPixelLeft(pos[ix, 0]);
}
d.SetPixelWidth(pos[ix, 1]);
}
ix++;
}
}
internal void AdjustHeight(int[,] pos)
{
var ix = 0;
//Now set the size for all drawings depending on the editAs property.
foreach (OfficeOpenXml.Drawing.ExcelDrawing d in this)
{
if (d.EditAs != Drawing.eEditAs.TwoCell)
{
if (d.EditAs == Drawing.eEditAs.Absolute)
{
d.SetPixelTop(pos[ix, 0]);
}
d.SetPixelHeight(pos[ix, 1]);
}
ix++;
}
}
internal int[,] GetDrawingWidths()
{
int[,] pos = new int[Count, 2];
int ix = 0;
//Save the size for all drawings
foreach (ExcelDrawing d in this)
{
pos[ix, 0] = d.GetPixelLeft();
pos[ix++, 1] = d.GetPixelWidth();
}
return pos;
}
internal int[,] GetDrawingHeight()
{
int[,] pos = new int[Count, 2];
int ix = 0;
//Save the size for all drawings
foreach (ExcelDrawing d in this)
{
pos[ix, 0] = d.GetPixelTop();
pos[ix++, 1] = d.GetPixelHeight();
}
return pos;
}
}
}