| /******************************************************************************* | |
| * 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 | |
| { | |
| /// <summary> | |
| /// Text anchoring | |
| /// </summary> | |
| public enum eTextAnchoringType | |
| { | |
| Bottom, | |
| Center, | |
| Distributed, | |
| Justify, | |
| Top | |
| } | |
| /// <summary> | |
| /// Vertical text type | |
| /// </summary> | |
| public enum eTextVerticalType | |
| { | |
| EastAsianVertical, | |
| Horizontal, | |
| MongolianVertical, | |
| Vertical, | |
| Vertical270, | |
| WordArtVertical, | |
| WordArtVerticalRightToLeft | |
| } | |
| /// <summary> | |
| /// How the drawing will be resized. | |
| /// </summary> | |
| public enum eEditAs | |
| { | |
| /// <summary> | |
| /// Specifies that the current start and end positions shall | |
| /// be maintained with respect to the distances from the | |
| /// absolute start point of the worksheet. | |
| /// </summary> | |
| Absolute, | |
| /// <summary> | |
| /// 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. | |
| /// </summary> | |
| OneCell, | |
| /// <summary> | |
| /// 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). | |
| /// </summary> | |
| TwoCell | |
| } | |
| /// <summary> | |
| /// Base class for twoanchored drawings. | |
| /// Drawings are Charts, shapes and Pictures. | |
| /// </summary> | |
| public class ExcelDrawing : XmlHelper, IDisposable | |
| { | |
| /// <summary> | |
| /// Position of the a drawing. | |
| /// </summary> | |
| 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"; | |
| /// <summary> | |
| /// Column Offset | |
| /// | |
| /// EMU units 1cm = 1/360000 | |
| /// 1US inch = 1/914400 | |
| /// 1pixel = 1/9525 | |
| /// </summary> | |
| public int ColumnOff | |
| { | |
| get | |
| { | |
| return GetXmlNodeInt(colOffPath); | |
| } | |
| set | |
| { | |
| SetXmlNodeString(colOffPath, value.ToString()); | |
| } | |
| } | |
| const string rowOffPath = "xdr:rowOff"; | |
| /// <summary> | |
| /// Row Offset | |
| /// | |
| /// EMU units 1cm = 1/360000 | |
| /// 1US inch = 1/914400 | |
| /// 1pixel = 1/9525 | |
| /// </summary> | |
| 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" }; | |
| } | |
| /// <summary> | |
| /// The name of the drawing object | |
| /// </summary> | |
| 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(); | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// 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 | |
| /// </summary> | |
| 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"; | |
| /// <summary> | |
| /// Lock drawing | |
| /// </summary> | |
| public bool Locked | |
| { | |
| get | |
| { | |
| return GetXmlNodeBool(lockedPath, true); | |
| } | |
| set | |
| { | |
| SetXmlNodeBool(lockedPath, value); | |
| } | |
| } | |
| const string printPath = "xdr:clientData/@fPrintsWithSheet"; | |
| /// <summary> | |
| /// Print drawing with sheet | |
| /// </summary> | |
| public bool Print | |
| { | |
| get | |
| { | |
| return GetXmlNodeBool(printPath, true); | |
| } | |
| set | |
| { | |
| SetXmlNodeBool(printPath, value); | |
| } | |
| } /// <summary> | |
| /// Top Left position | |
| /// </summary> | |
| public ExcelPosition From { get; set; } | |
| /// <summary> | |
| /// Bottom right position | |
| /// </summary> | |
| public ExcelPosition To | |
| { | |
| get; | |
| set; | |
| } | |
| /// <summary> | |
| /// Add new Drawing types here | |
| /// </summary> | |
| /// <param name="drawings">The drawing collection</param> | |
| /// <param name="node">Xml top node</param> | |
| /// <returns>The Drawing object</returns> | |
| 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" | |
| /// <summary> | |
| /// Set the top left corner of a drawing. | |
| /// Note that resizing columns / rows after using this function will effect the position of the drawing | |
| /// </summary> | |
| /// <param name="PixelTop">Top pixel</param> | |
| /// <param name="PixelLeft">Left pixel</param> | |
| public void SetPosition(int PixelTop, int PixelLeft) | |
| { | |
| int width = GetPixelWidth(); | |
| int height = GetPixelHeight(); | |
| SetPixelTop(PixelTop); | |
| SetPixelLeft(PixelLeft); | |
| SetPixelWidth(width); | |
| SetPixelHeight(height); | |
| } | |
| /// <summary> | |
| /// Set the top left corner of a drawing. | |
| /// Note that resizing columns / rows after using this function will effect the position of the drawing | |
| /// </summary> | |
| /// <param name="Row">Start row</param> | |
| /// <param name="RowOffsetPixels">Offset in pixels</param> | |
| /// <param name="Column">Start Column</param> | |
| /// <param name="ColumnOffsetPixels">Offset in pixels</param> | |
| 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); | |
| } | |
| /// <summary> | |
| /// Set size in Percent | |
| /// Note that resizing columns / rows after using this function will effect the size of the drawing | |
| /// </summary> | |
| /// <param name="Percent"></param> | |
| 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); | |
| } | |
| /// <summary> | |
| /// Set size in pixels | |
| /// Note that resizing columns / rows after using this function will effect the size of the drawing | |
| /// </summary> | |
| /// <param name="PixelWidth">Width in pixels</param> | |
| /// <param name="PixelHeight">Height in pixels</param> | |
| 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; | |
| } | |
| } | |
| } |