/******************************************************************************* * 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; } } }