/*******************************************************************************
* 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 2010-06-01
* Jan Källman License changed GPL-->LGPL 2011-12-16
*******************************************************************************/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Xml;
using OfficeOpenXml.Drawing.Chart;
namespace OfficeOpenXml.Drawing
{
///
/// Text anchoring
///
public enum eTextAnchoringType
{
Bottom,
Center,
Distributed,
Justify,
Top
}
///
/// Vertical text type
///
public enum eTextVerticalType
{
EastAsianVertical,
Horizontal,
MongolianVertical,
Vertical,
Vertical270,
WordArtVertical,
WordArtVerticalRightToLeft
}
///
/// How the drawing will be resized.
///
public enum eEditAs
{
///
/// Specifies that the current start and end positions shall
/// be maintained with respect to the distances from the
/// absolute start point of the worksheet.
///
Absolute,
///
/// Specifies that the current drawing shall move with its
///row and column (i.e. the object is anchored to the
/// actual from row and column), but that the size shall
///remain absolute.
///
OneCell,
///
/// Specifies that the current drawing shall move and
/// resize to maintain its row and column anchors (i.e. the
/// object is anchored to the actual from and to row and column).
///
TwoCell
}
///
/// Base class for twoanchored drawings.
/// Drawings are Charts, shapes and Pictures.
///
public class ExcelDrawing : XmlHelper, IDisposable
{
///
/// Position of the a drawing.
///
public class ExcelPosition : XmlHelper
{
XmlNode _node;
XmlNamespaceManager _ns;
internal ExcelPosition(XmlNamespaceManager ns, XmlNode node) :
base (ns,node)
{
_node = node;
_ns = ns;
}
const string colPath="xdr:col";
public int Column
{
get
{
return GetXmlNodeInt(colPath);
}
set
{
SetXmlNodeString(colPath, value.ToString());
}
}
const string rowPath="xdr:row";
public int Row
{
get
{
return GetXmlNodeInt(rowPath);
}
set
{
SetXmlNodeString(rowPath, value.ToString());
}
}
const string colOffPath = "xdr:colOff";
///
/// Column Offset
///
/// EMU units 1cm = 1/360000
/// 1US inch = 1/914400
/// 1pixel = 1/9525
///
public int ColumnOff
{
get
{
return GetXmlNodeInt(colOffPath);
}
set
{
SetXmlNodeString(colOffPath, value.ToString());
}
}
const string rowOffPath = "xdr:rowOff";
///
/// Row Offset
///
/// EMU units 1cm = 1/360000
/// 1US inch = 1/914400
/// 1pixel = 1/9525
///
public int RowOff
{
get
{
return GetXmlNodeInt(rowOffPath);
}
set
{
SetXmlNodeString(rowOffPath, value.ToString());
}
}
}
protected ExcelDrawings _drawings;
protected XmlNode _topNode;
string _nameXPath;
protected internal int _id;
const float STANDARD_DPI = 96;
public const int EMU_PER_PIXEL = 9525;
internal ExcelDrawing(ExcelDrawings drawings, XmlNode node, string nameXPath) :
base(drawings.NameSpaceManager, node)
{
_drawings = drawings;
_topNode = node;
_id = drawings.Worksheet.Workbook._nextDrawingID++;
XmlNode posNode = node.SelectSingleNode("xdr:from", drawings.NameSpaceManager);
if (node != null)
{
From = new ExcelPosition(drawings.NameSpaceManager, posNode);
}
posNode = node.SelectSingleNode("xdr:to", drawings.NameSpaceManager);
if (node != null)
{
To = new ExcelPosition(drawings.NameSpaceManager, posNode);
}
else
{
To = null;
}
_nameXPath = nameXPath;
SchemaNodeOrder = new string[] { "from", "to", "graphicFrame", "sp", "clientData" };
}
///
/// The name of the drawing object
///
public string Name
{
get
{
try
{
if (_nameXPath == "") return "";
return GetXmlNodeString(_nameXPath);
}
catch
{
return "";
}
}
set
{
try
{
if (_nameXPath == "") throw new NotImplementedException();
SetXmlNodeString(_nameXPath, value);
}
catch
{
throw new NotImplementedException();
}
}
}
///
/// How Excel resize drawings when the column width is changed within Excel.
/// The width of drawings are currently NOT resized in EPPLus when the column width changes
///
public eEditAs EditAs
{
get
{
try
{
string s = GetXmlNodeString("@editAs");
if (s == "")
{
return eEditAs.TwoCell;
}
else
{
return (eEditAs)Enum.Parse(typeof(eEditAs), s,true);
}
}
catch
{
return eEditAs.TwoCell;
}
}
set
{
string s=value.ToString();
SetXmlNodeString("@editAs", s.Substring(0,1).ToLower(CultureInfo.InvariantCulture)+s.Substring(1,s.Length-1));
}
}
const string lockedPath="xdr:clientData/@fLocksWithSheet";
///
/// Lock drawing
///
public bool Locked
{
get
{
return GetXmlNodeBool(lockedPath, true);
}
set
{
SetXmlNodeBool(lockedPath, value);
}
}
const string printPath = "xdr:clientData/@fPrintsWithSheet";
///
/// Print drawing with sheet
///
public bool Print
{
get
{
return GetXmlNodeBool(printPath, true);
}
set
{
SetXmlNodeBool(printPath, value);
}
} ///
/// Top Left position
///
public ExcelPosition From { get; set; }
///
/// Bottom right position
///
public ExcelPosition To
{
get;
set;
}
///
/// Add new Drawing types here
///
/// The drawing collection
/// Xml top node
/// The Drawing object
internal static ExcelDrawing GetDrawing(ExcelDrawings drawings, XmlNode node)
{
if (node.SelectSingleNode("xdr:sp", drawings.NameSpaceManager) != null)
{
return new ExcelShape(drawings, node);
}
else if (node.SelectSingleNode("xdr:pic", drawings.NameSpaceManager) != null)
{
return new ExcelPicture(drawings, node);
}
else if (node.SelectSingleNode("xdr:graphicFrame", drawings.NameSpaceManager) != null)
{
return ExcelChart.GetChart(drawings, node);
}
else
{
return new ExcelDrawing(drawings, node, "");
}
}
internal string Id
{
get { return _id.ToString(); }
}
internal static string GetTextAchoringText(eTextAnchoringType value)
{
switch (value)
{
case eTextAnchoringType.Bottom:
return "b";
case eTextAnchoringType.Center:
return "ctr";
case eTextAnchoringType.Distributed:
return "dist";
case eTextAnchoringType.Justify:
return "just";
default:
return "t";
}
}
internal static eTextAnchoringType GetTextAchoringEnum(string text)
{
switch (text)
{
case "b":
return eTextAnchoringType.Bottom;
case "ctr":
return eTextAnchoringType.Center;
case "dist":
return eTextAnchoringType.Distributed;
case "just":
return eTextAnchoringType.Justify;
default:
return eTextAnchoringType.Top;
}
}
internal static string GetTextVerticalText(eTextVerticalType value)
{
switch (value)
{
case eTextVerticalType.EastAsianVertical:
return "eaVert";
case eTextVerticalType.MongolianVertical:
return "mongolianVert";
case eTextVerticalType.Vertical:
return "vert";
case eTextVerticalType.Vertical270:
return "vert270";
case eTextVerticalType.WordArtVertical:
return "wordArtVert";
case eTextVerticalType.WordArtVerticalRightToLeft:
return "wordArtVertRtl";
default:
return "horz";
}
}
internal static eTextVerticalType GetTextVerticalEnum(string text)
{
switch (text)
{
case "eaVert":
return eTextVerticalType.EastAsianVertical;
case "mongolianVert":
return eTextVerticalType.MongolianVertical;
case "vert":
return eTextVerticalType.Vertical;
case "vert270":
return eTextVerticalType.Vertical270;
case "wordArtVert":
return eTextVerticalType.WordArtVertical;
case "wordArtVertRtl":
return eTextVerticalType.WordArtVerticalRightToLeft;
default:
return eTextVerticalType.Horizontal;
}
}
#region "Internal sizing functions"
internal int GetPixelLeft()
{
ExcelWorksheet ws = _drawings.Worksheet;
decimal mdw = ws.Workbook.MaxFontWidth;
int pix = 0;
for (int col = 0; col < From.Column; col++)
{
pix += (int)decimal.Truncate(((256 * GetColumnWidth(col + 1) + decimal.Truncate(128 / (decimal)mdw)) / 256) * mdw);
}
pix += From.ColumnOff / EMU_PER_PIXEL;
return pix;
}
internal int GetPixelTop()
{
ExcelWorksheet ws = _drawings.Worksheet;
int pix = 0;
for (int row = 0; row < From.Row; row++)
{
pix += (int)(GetRowWidth(row + 1) / 0.75);
}
pix += From.RowOff / EMU_PER_PIXEL;
return pix;
}
internal int GetPixelWidth()
{
ExcelWorksheet ws = _drawings.Worksheet;
decimal mdw = ws.Workbook.MaxFontWidth;
int pix = -From.ColumnOff / EMU_PER_PIXEL;
for (int col = From.Column + 1; col <= To.Column; col++)
{
pix += (int)decimal.Truncate(((256 * GetColumnWidth(col) + decimal.Truncate(128 / (decimal)mdw)) / 256) * mdw);
}
pix += To.ColumnOff / EMU_PER_PIXEL;
return pix;
}
internal int GetPixelHeight()
{
ExcelWorksheet ws = _drawings.Worksheet;
int pix = -(From.RowOff / EMU_PER_PIXEL);
for (int row = From.Row + 1; row <= To.Row; row++)
{
pix += (int)(GetRowWidth(row) / 0.75);
}
pix += To.RowOff / EMU_PER_PIXEL;
return pix;
}
private decimal GetColumnWidth(int col)
{
ExcelWorksheet ws = _drawings.Worksheet;
var column = ws._values.GetValue(0, col) as ExcelColumn;
if (column == null) //Check that the column exists
{
return (decimal)ws.DefaultColWidth;
}
else
{
return (decimal)ws.Column(col).VisualWidth;
}
}
private double GetRowWidth(int row)
{
ExcelWorksheet ws = _drawings.Worksheet;
object o = null;
if (ws._values.Exists(row, 0, ref o) && o != null) //Check that the row exists
{
var internalRow = (RowInternal)o;
if (internalRow.Height >= 0)
{
return internalRow.Height;
}
}
return (double)ws.DefaultRowHeight;
}
internal void SetPixelTop(int pixels)
{
ExcelWorksheet ws = _drawings.Worksheet;
decimal mdw = ws.Workbook.MaxFontWidth;
int prevPix = 0;
int pix = (int)(GetRowWidth(1) / 0.75);
int row = 2;
while (pix < pixels)
{
prevPix = pix;
pix += (int)(GetRowWidth(row++) / 0.75);
}
if (pix == pixels)
{
From.Row = row - 1;
From.RowOff = 0;
}
else
{
From.Row = row - 2;
From.RowOff = (pixels - prevPix) * EMU_PER_PIXEL;
}
}
internal void SetPixelLeft(int pixels)
{
ExcelWorksheet ws = _drawings.Worksheet;
decimal mdw = ws.Workbook.MaxFontWidth;
int prevPix = 0;
int pix = (int)decimal.Truncate(((256 * GetColumnWidth(1) + decimal.Truncate(128 / (decimal)mdw)) / 256) * mdw);
int col = 2;
while (pix < pixels)
{
prevPix = pix;
pix += (int)decimal.Truncate(((256 * GetColumnWidth(col++) + decimal.Truncate(128 / (decimal)mdw)) / 256) * mdw);
}
if (pix == pixels)
{
From.Column = col - 1;
From.ColumnOff = 0;
}
else
{
From.Column = col - 2;
From.ColumnOff = (pixels - prevPix) * EMU_PER_PIXEL;
}
}
internal void SetPixelHeight(int pixels)
{
SetPixelHeight(pixels, STANDARD_DPI);
}
internal void SetPixelHeight(int pixels, float dpi)
{
ExcelWorksheet ws = _drawings.Worksheet;
//decimal mdw = ws.Workbook.MaxFontWidth;
pixels = (int)(pixels / (dpi / STANDARD_DPI) + .5);
int pixOff = pixels - ((int)(ws.Row(From.Row + 1).Height / 0.75) - (int)(From.RowOff / EMU_PER_PIXEL));
int prevPixOff = pixels;
int row = From.Row + 1;
while (pixOff >= 0)
{
prevPixOff = pixOff;
pixOff -= (int)(GetRowWidth(++row) / 0.75);
}
To.Row = row - 1;
if (From.Row == To.Row)
{
To.RowOff = From.RowOff + (pixels) * EMU_PER_PIXEL;
}
else
{
To.RowOff = prevPixOff * EMU_PER_PIXEL;
}
}
internal void SetPixelWidth(int pixels)
{
SetPixelWidth(pixels, STANDARD_DPI);
}
internal void SetPixelWidth(int pixels, float dpi)
{
ExcelWorksheet ws = _drawings.Worksheet;
decimal mdw = ws.Workbook.MaxFontWidth;
pixels = (int)(pixels / (dpi / STANDARD_DPI) + .5);
int pixOff = (int)pixels - ((int)decimal.Truncate(((256 * GetColumnWidth(From.Column + 1) + decimal.Truncate(128 / (decimal)mdw)) / 256) * mdw) - From.ColumnOff / EMU_PER_PIXEL);
int prevPixOff = From.ColumnOff / EMU_PER_PIXEL + (int)pixels;
int col = From.Column + 2;
while (pixOff >= 0)
{
prevPixOff = pixOff;
pixOff -= (int)decimal.Truncate(((256 * GetColumnWidth(col++) + decimal.Truncate(128 / (decimal)mdw)) / 256) * mdw);
}
To.Column = col - 2;
To.ColumnOff = prevPixOff * EMU_PER_PIXEL;
}
#endregion
#region "Public sizing functions"
///
/// Set the top left corner of a drawing.
/// Note that resizing columns / rows after using this function will effect the position of the drawing
///
/// Top pixel
/// Left pixel
public void SetPosition(int PixelTop, int PixelLeft)
{
int width = GetPixelWidth();
int height = GetPixelHeight();
SetPixelTop(PixelTop);
SetPixelLeft(PixelLeft);
SetPixelWidth(width);
SetPixelHeight(height);
}
///
/// Set the top left corner of a drawing.
/// Note that resizing columns / rows after using this function will effect the position of the drawing
///
/// Start row
/// Offset in pixels
/// Start Column
/// Offset in pixels
public void SetPosition(int Row, int RowOffsetPixels, int Column, int ColumnOffsetPixels)
{
int width = GetPixelWidth();
int height = GetPixelHeight();
From.Row = Row;
From.RowOff = RowOffsetPixels * EMU_PER_PIXEL;
From.Column = Column;
From.ColumnOff = ColumnOffsetPixels * EMU_PER_PIXEL;
SetPixelWidth(width);
SetPixelHeight(height);
}
///
/// Set size in Percent
/// Note that resizing columns / rows after using this function will effect the size of the drawing
///
///
public virtual void SetSize(int Percent)
{
int width = GetPixelWidth();
int height = GetPixelHeight();
width = (int)(width * ((decimal)Percent / 100));
height = (int)(height * ((decimal)Percent / 100));
SetPixelWidth(width, 96);
SetPixelHeight(height, 96);
}
///
/// Set size in pixels
/// Note that resizing columns / rows after using this function will effect the size of the drawing
///
/// Width in pixels
/// Height in pixels
public void SetSize(int PixelWidth, int PixelHeight)
{
SetPixelWidth(PixelWidth);
SetPixelHeight(PixelHeight);
}
#endregion
internal virtual void DeleteMe()
{
TopNode.ParentNode.RemoveChild(TopNode);
}
public virtual void Dispose()
{
_topNode = null;
}
}
}