|  | /******************************************************************************* | 
|  | * 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-10-01 | 
|  | * Jan Källman		    License changed GPL-->LGPL 2011-12-27 | 
|  | *******************************************************************************/ | 
|  | using System; | 
|  | using System.Xml; | 
|  | using System.Linq; | 
|  | using System.Collections.Generic; | 
|  | using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical; | 
|  | using draw=System.Drawing; | 
|  | using OfficeOpenXml.Style; | 
|  | using OfficeOpenXml.Style.XmlAccess; | 
|  | using OfficeOpenXml.Style.Dxf; | 
|  | using OfficeOpenXml.ConditionalFormatting; | 
|  | namespace OfficeOpenXml | 
|  | { | 
|  | /// <summary> | 
|  | /// Containts all shared cell styles for a workbook | 
|  | /// </summary> | 
|  | public sealed class ExcelStyles : XmlHelper | 
|  | { | 
|  | const string NumberFormatsPath = "d:styleSheet/d:numFmts"; | 
|  | const string FontsPath = "d:styleSheet/d:fonts"; | 
|  | const string FillsPath = "d:styleSheet/d:fills"; | 
|  | const string BordersPath = "d:styleSheet/d:borders"; | 
|  | const string CellStyleXfsPath = "d:styleSheet/d:cellStyleXfs"; | 
|  | const string CellXfsPath = "d:styleSheet/d:cellXfs"; | 
|  | const string CellStylesPath = "d:styleSheet/d:cellStyles"; | 
|  | const string dxfsPath = "d:styleSheet/d:dxfs"; | 
|  |  | 
|  | //internal Dictionary<int, ExcelXfs> Styles = new Dictionary<int, ExcelXfs>(); | 
|  | XmlDocument _styleXml; | 
|  | ExcelWorkbook _wb; | 
|  | XmlNamespaceManager _nameSpaceManager; | 
|  | internal int _nextDfxNumFmtID = 164; | 
|  | internal ExcelStyles(XmlNamespaceManager NameSpaceManager, XmlDocument xml, ExcelWorkbook wb) : | 
|  | base(NameSpaceManager, xml) | 
|  | { | 
|  | _styleXml=xml; | 
|  | _wb = wb; | 
|  | _nameSpaceManager = NameSpaceManager; | 
|  | SchemaNodeOrder = new string[] { "numFmts", "fonts", "fills", "borders", "cellStyleXfs", "cellXfs", "cellStyles", "dxfs" }; | 
|  | LoadFromDocument(); | 
|  | } | 
|  | /// <summary> | 
|  | /// Loads the style XML to memory | 
|  | /// </summary> | 
|  | private void LoadFromDocument() | 
|  | { | 
|  | //NumberFormats | 
|  | ExcelNumberFormatXml.AddBuildIn(NameSpaceManager, NumberFormats); | 
|  | XmlNode numNode = _styleXml.SelectSingleNode(NumberFormatsPath, _nameSpaceManager); | 
|  | if (numNode != null) | 
|  | { | 
|  | foreach (XmlNode n in numNode) | 
|  | { | 
|  | ExcelNumberFormatXml nf = new ExcelNumberFormatXml(_nameSpaceManager, n); | 
|  | NumberFormats.Add(nf.Id, nf); | 
|  | if (nf.NumFmtId >= NumberFormats.NextId) NumberFormats.NextId=nf.NumFmtId+1; | 
|  | } | 
|  | } | 
|  |  | 
|  | //Fonts | 
|  | XmlNode fontNode = _styleXml.SelectSingleNode(FontsPath, _nameSpaceManager); | 
|  | foreach (XmlNode n in fontNode) | 
|  | { | 
|  | ExcelFontXml f = new ExcelFontXml(_nameSpaceManager, n); | 
|  | Fonts.Add(f.Id, f); | 
|  | } | 
|  |  | 
|  | //Fills | 
|  | XmlNode fillNode = _styleXml.SelectSingleNode(FillsPath, _nameSpaceManager); | 
|  | foreach (XmlNode n in fillNode) | 
|  | { | 
|  | ExcelFillXml f; | 
|  | if (n.FirstChild != null && n.FirstChild.LocalName == "gradientFill") | 
|  | { | 
|  | f = new ExcelGradientFillXml(_nameSpaceManager, n); | 
|  | } | 
|  | else | 
|  | { | 
|  | f = new ExcelFillXml(_nameSpaceManager, n); | 
|  | } | 
|  | Fills.Add(f.Id, f); | 
|  | } | 
|  |  | 
|  | //Borders | 
|  | XmlNode borderNode = _styleXml.SelectSingleNode(BordersPath, _nameSpaceManager); | 
|  | foreach (XmlNode n in borderNode) | 
|  | { | 
|  | ExcelBorderXml b = new ExcelBorderXml(_nameSpaceManager, n); | 
|  | Borders.Add(b.Id, b); | 
|  | } | 
|  |  | 
|  | //cellStyleXfs | 
|  | XmlNode styleXfsNode = _styleXml.SelectSingleNode(CellStyleXfsPath, _nameSpaceManager); | 
|  | if (styleXfsNode != null) | 
|  | { | 
|  | foreach (XmlNode n in styleXfsNode) | 
|  | { | 
|  | ExcelXfs item = new ExcelXfs(_nameSpaceManager, n, this); | 
|  | CellStyleXfs.Add(item.Id, item); | 
|  | } | 
|  | } | 
|  |  | 
|  | XmlNode styleNode = _styleXml.SelectSingleNode(CellXfsPath, _nameSpaceManager); | 
|  | for (int i = 0; i < styleNode.ChildNodes.Count; i++) | 
|  | { | 
|  | XmlNode n = styleNode.ChildNodes[i]; | 
|  | ExcelXfs item = new ExcelXfs(_nameSpaceManager, n, this); | 
|  | CellXfs.Add(item.Id, item); | 
|  | } | 
|  |  | 
|  | //cellStyle | 
|  | XmlNode namedStyleNode = _styleXml.SelectSingleNode(CellStylesPath, _nameSpaceManager); | 
|  | if (namedStyleNode != null) | 
|  | { | 
|  | foreach (XmlNode n in namedStyleNode) | 
|  | { | 
|  | ExcelNamedStyleXml item = new ExcelNamedStyleXml(_nameSpaceManager, n, this); | 
|  | NamedStyles.Add(item.Name, item); | 
|  | } | 
|  | } | 
|  |  | 
|  | //dxfsPath | 
|  | XmlNode dxfsNode = _styleXml.SelectSingleNode(dxfsPath, _nameSpaceManager); | 
|  | if (dxfsNode != null) | 
|  | { | 
|  | foreach (XmlNode x in dxfsNode) | 
|  | { | 
|  | ExcelDxfStyleConditionalFormatting item = new ExcelDxfStyleConditionalFormatting(_nameSpaceManager, x, this); | 
|  | Dxfs.Add(item.Id, item); | 
|  | } | 
|  | } | 
|  | } | 
|  | internal ExcelStyle GetStyleObject(int Id,int PositionID, string Address) | 
|  | { | 
|  | if (Id < 0) Id = 0; | 
|  | return new ExcelStyle(this, PropertyChange, PositionID, Address, Id); | 
|  | } | 
|  | /// <summary> | 
|  | /// Handels changes of properties on the style objects | 
|  | /// </summary> | 
|  | /// <param name="sender"></param> | 
|  | /// <param name="e"></param> | 
|  | /// <returns></returns> | 
|  | internal int PropertyChange(StyleBase sender, Style.StyleChangeEventArgs e) | 
|  | { | 
|  | var address = new ExcelAddressBase(e.Address); | 
|  | var ws = _wb.Worksheets[e.PositionID]; | 
|  | Dictionary<int, int> styleCashe = new Dictionary<int, int>(); | 
|  | //Set single address | 
|  | lock (ws._styles) | 
|  | { | 
|  | SetStyleAddress(sender, e, address, ws, ref styleCashe); | 
|  | if (address.Addresses != null) | 
|  | { | 
|  | //Handle multiaddresses | 
|  | foreach (var innerAddress in address.Addresses) | 
|  | { | 
|  | SetStyleAddress(sender, e, innerAddress, ws, ref styleCashe); | 
|  | } | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | private void SetStyleAddress(StyleBase sender, Style.StyleChangeEventArgs e, ExcelAddressBase address, ExcelWorksheet ws, ref Dictionary<int, int> styleCashe) | 
|  | { | 
|  | if (address.Start.Column == 0 || address.Start.Row == 0) | 
|  | { | 
|  | throw (new Exception("error address")); | 
|  | } | 
|  | //Columns | 
|  | else if (address.Start.Row == 1 && address.End.Row == ExcelPackage.MaxRows) | 
|  | { | 
|  | ExcelColumn column; | 
|  | int col = address.Start.Column, row = 0; | 
|  | //Get the startcolumn | 
|  | if (!ws._values.Exists(0, address.Start.Column)) | 
|  | { | 
|  | column = ws.Column(address.Start.Column); | 
|  | } | 
|  | else | 
|  | { | 
|  | column = (ExcelColumn)ws._values.GetValue(0, address.Start.Column); | 
|  | } | 
|  |  | 
|  |  | 
|  | while (column.ColumnMin <= address.End.Column) | 
|  | { | 
|  | if (column.ColumnMax > address.End.Column) | 
|  | { | 
|  | var newCol = ws.CopyColumn(column, address.End.Column + 1, column.ColumnMax); | 
|  | column.ColumnMax = address.End.Column; | 
|  | } | 
|  | var s = ws._styles.GetValue(0, column.ColumnMin); | 
|  | if (styleCashe.ContainsKey(s)) | 
|  | { | 
|  | ws.SetStyle(0, column.ColumnMin, styleCashe[s]); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExcelXfs st = CellXfs[s]; | 
|  | int newId = st.GetNewID(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value); | 
|  | styleCashe.Add(s, newId); | 
|  | ws.SetStyle(0, column.ColumnMin, newId); | 
|  | } | 
|  |  | 
|  | //index++; | 
|  |  | 
|  | if (!ws._values.NextCell(ref row, ref col) || row > 0) | 
|  | { | 
|  | column._columnMax = address.End.Column; | 
|  | break; | 
|  | } | 
|  | else | 
|  | { | 
|  | column = (ws._values.GetValue(0, col) as ExcelColumn); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (column._columnMax < address.End.Column) | 
|  | { | 
|  | var newCol = ws.Column(column._columnMax + 1) as ExcelColumn; | 
|  | newCol._columnMax = address.End.Column; | 
|  |  | 
|  | var s = ws._styles.GetValue(0, column.ColumnMin); | 
|  | if (styleCashe.ContainsKey(s)) | 
|  | { | 
|  | ws.SetStyle(0, column.ColumnMin, styleCashe[s]); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExcelXfs st = CellXfs[s]; | 
|  | int newId = st.GetNewID(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value); | 
|  | styleCashe.Add(s, newId); | 
|  | ws.SetStyle(0, column.ColumnMin, newId); | 
|  | } | 
|  |  | 
|  | column._columnMax = address.End.Column; | 
|  | } | 
|  |  | 
|  | //Set for individual cells in the span. We loop all cells here since the cells are sorted with columns first. | 
|  | var cse = new CellsStoreEnumerator<int>(ws._styles, 1, address._fromCol, address._toRow, address._toCol); | 
|  | while (cse.Next()) | 
|  | { | 
|  | if (cse.Column >= address.Start.Column && | 
|  | cse.Column <= address.End.Column) | 
|  | { | 
|  | if (styleCashe.ContainsKey(cse.Value)) | 
|  | { | 
|  | ws.SetStyle(cse.Row, cse.Column, styleCashe[cse.Value]); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExcelXfs st = CellXfs[cse.Value]; | 
|  | int newId = st.GetNewID(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value); | 
|  | styleCashe.Add(cse.Value, newId); | 
|  | cse.Value = newId; | 
|  | //ws.SetStyle(cse.Row, cse.Column, newId); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //Update cells with styled columns | 
|  | cse = new CellsStoreEnumerator<int>(ws._styles, 1, 0, address._toRow, 0); | 
|  | while (cse.Next()) | 
|  | { | 
|  | for (int c = address._fromRow; c <= address._toCol; c++) | 
|  | { | 
|  | if (!ws._styles.Exists(cse.Row, c)) | 
|  | { | 
|  | if (styleCashe.ContainsKey(cse.Value)) | 
|  | { | 
|  | ws.SetStyle(cse.Row, c, styleCashe[cse.Value]); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExcelXfs st = CellXfs[cse.Value]; | 
|  | int newId = st.GetNewID(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value); | 
|  | styleCashe.Add(cse.Value, newId); | 
|  | ws.SetStyle(cse.Row, c, newId); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //Rows | 
|  | else if (address.Start.Column == 1 && address.End.Column == ExcelPackage.MaxColumns) | 
|  | { | 
|  | for (int rowNum = address.Start.Row; rowNum <= address.End.Row; rowNum++) | 
|  | { | 
|  | var s = ws._styles.GetValue(rowNum, 0); | 
|  | if (s == 0) | 
|  | { | 
|  | //iterate all columns and set the row to the style of the last column | 
|  | var cse = new CellsStoreEnumerator<int>(ws._styles, 0, 1, 0, ExcelPackage.MaxColumns); | 
|  | while (cse.Next()) | 
|  | { | 
|  | s = cse.Value; | 
|  | var c = ws._values.GetValue(cse.Row, cse.Column) as ExcelColumn; | 
|  | if (c != null && c.ColumnMax < ExcelPackage.MaxColumns) | 
|  | { | 
|  | for (int col = c.ColumnMin; col < c.ColumnMax; col++) | 
|  | { | 
|  | if (!ws._styles.Exists(rowNum, col)) | 
|  | { | 
|  | ws._styles.SetValue(rowNum, col, s); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | ws.SetStyle(rowNum, 0, s); | 
|  | cse.Dispose(); | 
|  | } | 
|  | if (styleCashe.ContainsKey(s)) | 
|  | { | 
|  | ws.SetStyle(rowNum, 0, styleCashe[s]); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExcelXfs st = CellXfs[s]; | 
|  | int newId = st.GetNewID(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value); | 
|  | styleCashe.Add(s, newId); | 
|  | ws._styles.SetValue(rowNum, 0, newId); | 
|  | ws.SetStyle(rowNum, 0, newId); | 
|  | } | 
|  | } | 
|  |  | 
|  | //Update individual cells | 
|  | var cse2 = new CellsStoreEnumerator<int>(ws._styles, address._fromRow, address._fromCol, address._toRow, address._toCol); | 
|  | while (cse2.Next()) | 
|  | { | 
|  | var s = cse2.Value; | 
|  | if (styleCashe.ContainsKey(s)) | 
|  | { | 
|  | ws.SetStyle(cse2.Row, cse2.Column, styleCashe[s]); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExcelXfs st = CellXfs[s]; | 
|  | int newId = st.GetNewID(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value); | 
|  | styleCashe.Add(s, newId); | 
|  | cse2.Value = newId; | 
|  | } | 
|  | } | 
|  |  | 
|  | //Update cells with styled rows | 
|  | cse2 = new CellsStoreEnumerator<int>(ws._styles, 0, 1, 0, address._toCol); | 
|  | while (cse2.Next()) | 
|  | { | 
|  | for (int r = address._fromRow; r <= address._toRow; r++) | 
|  | { | 
|  | if (!ws._styles.Exists(r, cse2.Column)) | 
|  | { | 
|  | var s = cse2.Value; | 
|  | if (styleCashe.ContainsKey(s)) | 
|  | { | 
|  | ws.SetStyle(r, cse2.Column, styleCashe[s]); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExcelXfs st = CellXfs[s]; | 
|  | int newId = st.GetNewID(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value); | 
|  | styleCashe.Add(s, newId); | 
|  | ws.SetStyle(r, cse2.Column, newId); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | else             //Cellrange | 
|  | { | 
|  | for (int col = address.Start.Column; col <= address.End.Column; col++) | 
|  | { | 
|  | for (int row = address.Start.Row; row <= address.End.Row; row++) | 
|  | { | 
|  | var s = GetStyleId(ws, row, col); | 
|  | if (styleCashe.ContainsKey(s)) | 
|  | { | 
|  | ws.SetStyle(row, col, styleCashe[s]); | 
|  | } | 
|  | else | 
|  | { | 
|  | ExcelXfs st = CellXfs[s]; | 
|  | int newId = st.GetNewID(CellXfs, sender, e.StyleClass, e.StyleProperty, e.Value); | 
|  | styleCashe.Add(s, newId); | 
|  | ws.SetStyle(row, col, newId); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | internal int GetStyleId(ExcelWorksheet ws, int row, int col) | 
|  | { | 
|  | int v=0; | 
|  | if (ws._styles.Exists(row, col, ref v)) | 
|  | { | 
|  | return v; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (ws._styles.Exists(row, 0, ref v)) //First Row | 
|  | { | 
|  | return v; | 
|  | } | 
|  | else // then column | 
|  | { | 
|  | if (ws._styles.Exists(0, col, ref v)) | 
|  | { | 
|  | return v; | 
|  | } | 
|  | else | 
|  | { | 
|  | int r=0,c=col; | 
|  | if(ws._values.PrevCell(ref r,ref c)) | 
|  | { | 
|  | var column=ws._values.GetValue(0,c) as ExcelColumn; | 
|  | if (column != null && column.ColumnMax >= col) //Fixes issue 15174 | 
|  | { | 
|  | return ws._styles.GetValue(0, c); | 
|  | } | 
|  | else | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  | /// <summary> | 
|  | /// Handles property changes on Named styles. | 
|  | /// </summary> | 
|  | /// <param name="sender"></param> | 
|  | /// <param name="e"></param> | 
|  | /// <returns></returns> | 
|  | internal int NamedStylePropertyChange(StyleBase sender, Style.StyleChangeEventArgs e) | 
|  | { | 
|  |  | 
|  | int index = NamedStyles.FindIndexByID(e.Address); | 
|  | if (index >= 0) | 
|  | { | 
|  | int newId = CellStyleXfs[NamedStyles[index].StyleXfId].GetNewID(CellStyleXfs, sender, e.StyleClass, e.StyleProperty, e.Value); | 
|  | int prevIx=NamedStyles[index].StyleXfId; | 
|  | NamedStyles[index].StyleXfId = newId; | 
|  | NamedStyles[index].Style.Index = newId; | 
|  |  | 
|  | NamedStyles[index].XfId = int.MinValue; | 
|  | foreach (var style in CellXfs) | 
|  | { | 
|  | if (style.XfId == prevIx) | 
|  | { | 
|  | style.XfId = newId; | 
|  | } | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  | public ExcelStyleCollection<ExcelNumberFormatXml> NumberFormats = new ExcelStyleCollection<ExcelNumberFormatXml>(); | 
|  | public ExcelStyleCollection<ExcelFontXml> Fonts = new ExcelStyleCollection<ExcelFontXml>(); | 
|  | public ExcelStyleCollection<ExcelFillXml> Fills = new ExcelStyleCollection<ExcelFillXml>(); | 
|  | public ExcelStyleCollection<ExcelBorderXml> Borders = new ExcelStyleCollection<ExcelBorderXml>(); | 
|  | public ExcelStyleCollection<ExcelXfs> CellStyleXfs = new ExcelStyleCollection<ExcelXfs>(); | 
|  | public ExcelStyleCollection<ExcelXfs> CellXfs = new ExcelStyleCollection<ExcelXfs>(); | 
|  | public ExcelStyleCollection<ExcelNamedStyleXml> NamedStyles = new ExcelStyleCollection<ExcelNamedStyleXml>(); | 
|  | public ExcelStyleCollection<ExcelDxfStyleConditionalFormatting> Dxfs = new ExcelStyleCollection<ExcelDxfStyleConditionalFormatting>(); | 
|  |  | 
|  | internal string Id | 
|  | { | 
|  | get { return ""; } | 
|  | } | 
|  |  | 
|  | public ExcelNamedStyleXml CreateNamedStyle(string name) | 
|  | { | 
|  | return CreateNamedStyle(name, null); | 
|  | } | 
|  | public ExcelNamedStyleXml CreateNamedStyle(string name, ExcelStyle Template) | 
|  | { | 
|  | if (_wb.Styles.NamedStyles.ExistsKey(name)) | 
|  | { | 
|  | throw new Exception(string.Format("Key {0} already exists in collection", name)); | 
|  | } | 
|  |  | 
|  | ExcelNamedStyleXml style; | 
|  | style = new ExcelNamedStyleXml(NameSpaceManager, this); | 
|  | int xfIdCopy, positionID; | 
|  | ExcelStyles styles; | 
|  | if (Template == null) | 
|  | { | 
|  | //                style.Style = new ExcelStyle(this, NamedStylePropertyChange, -1, name, 0); | 
|  | xfIdCopy = 0; | 
|  | positionID = -1; | 
|  | styles = this; | 
|  | } | 
|  | else | 
|  | { | 
|  | if (Template.PositionID < 0 && Template.Styles==this) | 
|  | { | 
|  | xfIdCopy = Template.Index; | 
|  | positionID=Template.PositionID; | 
|  | styles = this; | 
|  | //style.Style = new ExcelStyle(this, NamedStylePropertyChange, Template.PositionID, name, Template.Index); | 
|  | //style.StyleXfId = Template.Index; | 
|  | } | 
|  | else | 
|  | { | 
|  | xfIdCopy = Template.XfId; | 
|  | positionID = -1; | 
|  | styles = Template.Styles; | 
|  | } | 
|  | } | 
|  | //Clone namedstyle | 
|  | int styleXfId = CloneStyle(styles, xfIdCopy, true); | 
|  | //Close cells style | 
|  | CellStyleXfs[styleXfId].XfId = CellStyleXfs.Count-1; | 
|  | int xfid = CloneStyle(styles, xfIdCopy, false, true); //Always add a new style (We create a new named style here) | 
|  | CellXfs[xfid].XfId = styleXfId; | 
|  | style.Style = new ExcelStyle(this, NamedStylePropertyChange, positionID, name, styleXfId); | 
|  | style.StyleXfId = styleXfId; | 
|  |  | 
|  | style.Name = name; | 
|  | int ix =_wb.Styles.NamedStyles.Add(style.Name, style); | 
|  | style.Style.SetIndex(ix); | 
|  | //style.Style.XfId = ix; | 
|  | return style; | 
|  | } | 
|  | public void UpdateXml() | 
|  | { | 
|  | RemoveUnusedStyles(); | 
|  |  | 
|  | //NumberFormat | 
|  | XmlNode nfNode=_styleXml.SelectSingleNode(NumberFormatsPath, _nameSpaceManager); | 
|  | if (nfNode == null) | 
|  | { | 
|  | CreateNode(NumberFormatsPath, true); | 
|  | nfNode = _styleXml.SelectSingleNode(NumberFormatsPath, _nameSpaceManager); | 
|  | } | 
|  | else | 
|  | { | 
|  | nfNode.RemoveAll(); | 
|  | } | 
|  |  | 
|  | int count = 0; | 
|  | int normalIx = NamedStyles.FindIndexByID("Normal"); | 
|  | if (NamedStyles.Count > 0 && normalIx>=0 && NamedStyles[normalIx].Style.Numberformat.NumFmtID >= 164) | 
|  | { | 
|  | ExcelNumberFormatXml nf = NumberFormats[NumberFormats.FindIndexByID(NamedStyles[normalIx].Style.Numberformat.Id)]; | 
|  | nfNode.AppendChild(nf.CreateXmlNode(_styleXml.CreateElement("numFmt", ExcelPackage.schemaMain))); | 
|  | nf.newID = count++; | 
|  | } | 
|  | foreach (ExcelNumberFormatXml nf in NumberFormats) | 
|  | { | 
|  | if(!nf.BuildIn /*&& nf.newID<0*/) //Buildin formats are not updated. | 
|  | { | 
|  | nfNode.AppendChild(nf.CreateXmlNode(_styleXml.CreateElement("numFmt", ExcelPackage.schemaMain))); | 
|  | nf.newID = count; | 
|  | count++; | 
|  | } | 
|  | } | 
|  | (nfNode as XmlElement).SetAttribute("count", count.ToString()); | 
|  |  | 
|  | //Font | 
|  | count=0; | 
|  | XmlNode fntNode = _styleXml.SelectSingleNode(FontsPath, _nameSpaceManager); | 
|  | fntNode.RemoveAll(); | 
|  |  | 
|  | //Normal should be first in the collection | 
|  | if (NamedStyles.Count > 0 && normalIx >= 0 && NamedStyles[normalIx].Style.Font.Index > 0) | 
|  | { | 
|  | ExcelFontXml fnt = Fonts[NamedStyles[normalIx].Style.Font.Index]; | 
|  | fntNode.AppendChild(fnt.CreateXmlNode(_styleXml.CreateElement("font", ExcelPackage.schemaMain))); | 
|  | fnt.newID = count++; | 
|  | } | 
|  |  | 
|  | foreach (ExcelFontXml fnt in Fonts) | 
|  | { | 
|  | if (fnt.useCnt > 0/* && fnt.newID<0*/) | 
|  | { | 
|  | fntNode.AppendChild(fnt.CreateXmlNode(_styleXml.CreateElement("font", ExcelPackage.schemaMain))); | 
|  | fnt.newID = count; | 
|  | count++; | 
|  | } | 
|  | } | 
|  | (fntNode as XmlElement).SetAttribute("count", count.ToString()); | 
|  |  | 
|  |  | 
|  | //Fills | 
|  | count = 0; | 
|  | XmlNode fillsNode = _styleXml.SelectSingleNode(FillsPath, _nameSpaceManager); | 
|  | fillsNode.RemoveAll(); | 
|  | Fills[0].useCnt = 1;    //Must exist (none); | 
|  | Fills[1].useCnt = 1;    //Must exist (gray125); | 
|  | foreach (ExcelFillXml fill in Fills) | 
|  | { | 
|  | if (fill.useCnt > 0) | 
|  | { | 
|  | fillsNode.AppendChild(fill.CreateXmlNode(_styleXml.CreateElement("fill", ExcelPackage.schemaMain))); | 
|  | fill.newID = count; | 
|  | count++; | 
|  | } | 
|  | } | 
|  |  | 
|  | (fillsNode as XmlElement).SetAttribute("count", count.ToString()); | 
|  |  | 
|  | //Borders | 
|  | count = 0; | 
|  | XmlNode bordersNode = _styleXml.SelectSingleNode(BordersPath, _nameSpaceManager); | 
|  | bordersNode.RemoveAll(); | 
|  | Borders[0].useCnt = 1;    //Must exist blank; | 
|  | foreach (ExcelBorderXml border in Borders) | 
|  | { | 
|  | if (border.useCnt > 0) | 
|  | { | 
|  | bordersNode.AppendChild(border.CreateXmlNode(_styleXml.CreateElement("border", ExcelPackage.schemaMain))); | 
|  | border.newID = count; | 
|  | count++; | 
|  | } | 
|  | } | 
|  | (bordersNode as XmlElement).SetAttribute("count", count.ToString()); | 
|  |  | 
|  | XmlNode styleXfsNode = _styleXml.SelectSingleNode(CellStyleXfsPath, _nameSpaceManager); | 
|  | if (styleXfsNode == null && NamedStyles.Count > 0) | 
|  | { | 
|  | CreateNode(CellStyleXfsPath); | 
|  | styleXfsNode = _styleXml.SelectSingleNode(CellStyleXfsPath, _nameSpaceManager); | 
|  | } | 
|  | if (NamedStyles.Count > 0) | 
|  | { | 
|  | styleXfsNode.RemoveAll(); | 
|  | } | 
|  | //NamedStyles | 
|  | count = normalIx > -1 ? 1 : 0;  //If we have a normal style, we make sure it's added first. | 
|  |  | 
|  | XmlNode cellStyleNode = _styleXml.SelectSingleNode(CellStylesPath, _nameSpaceManager); | 
|  | if(cellStyleNode!=null) | 
|  | { | 
|  | cellStyleNode.RemoveAll(); | 
|  | } | 
|  | XmlNode cellXfsNode = _styleXml.SelectSingleNode(CellXfsPath, _nameSpaceManager); | 
|  | cellXfsNode.RemoveAll(); | 
|  |  | 
|  | if (NamedStyles.Count > 0 && normalIx >= 0) | 
|  | { | 
|  | NamedStyles[normalIx].newID = 0; | 
|  | AddNamedStyle(0, styleXfsNode, cellXfsNode, NamedStyles[normalIx]); | 
|  | } | 
|  | foreach (ExcelNamedStyleXml style in NamedStyles) | 
|  | { | 
|  | if (!style.Name.Equals("normal", StringComparison.InvariantCultureIgnoreCase)) | 
|  | { | 
|  | AddNamedStyle(count++, styleXfsNode, cellXfsNode, style); | 
|  | } | 
|  | else | 
|  | { | 
|  | style.newID = 0; | 
|  | } | 
|  | cellStyleNode.AppendChild(style.CreateXmlNode(_styleXml.CreateElement("cellStyle", ExcelPackage.schemaMain))); | 
|  | } | 
|  | if (cellStyleNode!=null) (cellStyleNode as XmlElement).SetAttribute("count", count.ToString()); | 
|  | if (styleXfsNode != null) (styleXfsNode as XmlElement).SetAttribute("count", count.ToString()); | 
|  |  | 
|  | //CellStyle | 
|  | int xfix = 0; | 
|  | foreach (ExcelXfs xf in CellXfs) | 
|  | { | 
|  | if (xf.useCnt > 0 && !(normalIx >= 0 && NamedStyles[normalIx].XfId == xfix)) | 
|  | { | 
|  | cellXfsNode.AppendChild(xf.CreateXmlNode(_styleXml.CreateElement("xf", ExcelPackage.schemaMain))); | 
|  | xf.newID = count; | 
|  | count++; | 
|  | } | 
|  | xfix++; | 
|  | } | 
|  | (cellXfsNode as XmlElement).SetAttribute("count", count.ToString()); | 
|  |  | 
|  | //Set dxf styling for conditional Formatting | 
|  | XmlNode dxfsNode = _styleXml.SelectSingleNode(dxfsPath, _nameSpaceManager); | 
|  | foreach (var ws in _wb.Worksheets) | 
|  | { | 
|  | if (ws is ExcelChartsheet) continue; | 
|  | foreach (var cf in ws.ConditionalFormatting) | 
|  | { | 
|  | if (cf.Style.HasValue) | 
|  | { | 
|  | int ix = Dxfs.FindIndexByID(cf.Style.Id); | 
|  | if (ix < 0) | 
|  | { | 
|  | ((ExcelConditionalFormattingRule)cf).DxfId = Dxfs.Count; | 
|  | Dxfs.Add(cf.Style.Id, cf.Style); | 
|  | var elem = ((XmlDocument)TopNode).CreateElement("d", "dxf", ExcelPackage.schemaMain); | 
|  | cf.Style.CreateNodes(new XmlHelperInstance(NameSpaceManager, elem), ""); | 
|  | dxfsNode.AppendChild(elem); | 
|  | } | 
|  | else | 
|  | { | 
|  | ((ExcelConditionalFormattingRule)cf).DxfId = ix; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if (dxfsNode != null) (dxfsNode as XmlElement).SetAttribute("count", Dxfs.Count.ToString()); | 
|  | } | 
|  |  | 
|  | private void AddNamedStyle(int id, XmlNode styleXfsNode,XmlNode cellXfsNode, ExcelNamedStyleXml style) | 
|  | { | 
|  | var styleXfs = CellStyleXfs[style.StyleXfId]; | 
|  | styleXfsNode.AppendChild(styleXfs.CreateXmlNode(_styleXml.CreateElement("xf", ExcelPackage.schemaMain), true)); | 
|  | styleXfs.newID = id; | 
|  | styleXfs.XfId = style.StyleXfId; | 
|  |  | 
|  | var ix = CellXfs.FindIndexByID(styleXfs.Id); | 
|  | if (ix < 0) | 
|  | { | 
|  | cellXfsNode.AppendChild(styleXfs.CreateXmlNode(_styleXml.CreateElement("xf", ExcelPackage.schemaMain))); | 
|  | } | 
|  | else | 
|  | { | 
|  | if(id<0) CellXfs[ix].XfId = id; | 
|  | cellXfsNode.AppendChild(CellXfs[ix].CreateXmlNode(_styleXml.CreateElement("xf", ExcelPackage.schemaMain))); | 
|  | CellXfs[ix].useCnt = 0; | 
|  | CellXfs[ix].newID = id; | 
|  | } | 
|  |  | 
|  | if (style.XfId >= 0) | 
|  | style.XfId = CellXfs[style.XfId].newID; | 
|  | else | 
|  | style.XfId = 0; | 
|  | } | 
|  |  | 
|  | private void RemoveUnusedStyles() | 
|  | { | 
|  | CellXfs[0].useCnt = 1; //First item is allways used. | 
|  | foreach (ExcelWorksheet sheet in _wb.Worksheets) | 
|  | { | 
|  | var cse = new CellsStoreEnumerator<int>(sheet._styles); | 
|  | while(cse.Next()) | 
|  | { | 
|  | var v = cse.Value; | 
|  | if (v >= 0) | 
|  | { | 
|  | CellXfs[v].useCnt++; | 
|  | } | 
|  | } | 
|  | } | 
|  | foreach (ExcelNamedStyleXml ns in NamedStyles) | 
|  | { | 
|  | CellStyleXfs[ns.StyleXfId].useCnt++; | 
|  | } | 
|  |  | 
|  | foreach (ExcelXfs xf in CellXfs) | 
|  | { | 
|  | if (xf.useCnt > 0) | 
|  | { | 
|  | if (xf.FontId >= 0) Fonts[xf.FontId].useCnt++; | 
|  | if (xf.FillId >= 0) Fills[xf.FillId].useCnt++; | 
|  | if (xf.BorderId >= 0) Borders[xf.BorderId].useCnt++; | 
|  | } | 
|  | } | 
|  | foreach (ExcelXfs xf in CellStyleXfs) | 
|  | { | 
|  | if (xf.useCnt > 0) | 
|  | { | 
|  | if (xf.FontId >= 0) Fonts[xf.FontId].useCnt++; | 
|  | if (xf.FillId >= 0) Fills[xf.FillId].useCnt++; | 
|  | if (xf.BorderId >= 0) Borders[xf.BorderId].useCnt++; | 
|  | } | 
|  | } | 
|  | } | 
|  | internal int GetStyleIdFromName(string Name) | 
|  | { | 
|  | int i = NamedStyles.FindIndexByID(Name); | 
|  | if (i >= 0) | 
|  | { | 
|  | int id = NamedStyles[i].XfId; | 
|  | if (id < 0) | 
|  | { | 
|  | int styleXfId=NamedStyles[i].StyleXfId; | 
|  | ExcelXfs newStyle = CellStyleXfs[styleXfId].Copy(); | 
|  | newStyle.XfId = styleXfId; | 
|  | id = CellXfs.FindIndexByID(newStyle.Id); | 
|  | if (id < 0) | 
|  | { | 
|  | id = CellXfs.Add(newStyle.Id, newStyle); | 
|  | } | 
|  | NamedStyles[i].XfId=id; | 
|  | } | 
|  | return id; | 
|  | } | 
|  | else | 
|  | { | 
|  | return 0; | 
|  | //throw(new Exception("Named style does not exist")); | 
|  | } | 
|  | } | 
|  | #region XmlHelpFunctions | 
|  | private int GetXmlNodeInt(XmlNode node) | 
|  | { | 
|  | int i; | 
|  | if (int.TryParse(GetXmlNode(node), out i)) | 
|  | { | 
|  | return i; | 
|  | } | 
|  | else | 
|  | { | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | private string GetXmlNode(XmlNode node) | 
|  | { | 
|  | if (node == null) | 
|  | { | 
|  | return ""; | 
|  | } | 
|  | if (node.Value != null) | 
|  | { | 
|  | return node.Value; | 
|  | } | 
|  | else | 
|  | { | 
|  | return ""; | 
|  | } | 
|  | } | 
|  |  | 
|  | #endregion | 
|  | internal int CloneStyle(ExcelStyles style, int styleID) | 
|  | { | 
|  | return CloneStyle(style, styleID, false, false); | 
|  | } | 
|  | internal int CloneStyle(ExcelStyles style, int styleID, bool isNamedStyle) | 
|  | { | 
|  | return CloneStyle(style, styleID, isNamedStyle, false); | 
|  | } | 
|  | internal int CloneStyle(ExcelStyles style, int styleID, bool isNamedStyle, bool allwaysAdd) | 
|  | { | 
|  | ExcelXfs xfs; | 
|  | lock (style) | 
|  | { | 
|  | if (isNamedStyle) | 
|  | { | 
|  | xfs = style.CellStyleXfs[styleID]; | 
|  | } | 
|  | else | 
|  | { | 
|  | xfs = style.CellXfs[styleID]; | 
|  | } | 
|  | ExcelXfs newXfs = xfs.Copy(this); | 
|  | //Numberformat | 
|  | if (xfs.NumberFormatId > 0) | 
|  | { | 
|  | //rake36: Two problems here... | 
|  | //rake36:  1. the first time through when format stays equal to String.Empty, it adds a string.empty to the list of Number Formats | 
|  | //rake36:  2. when adding a second sheet, if the numberformatid == 164, it finds the 164 added by previous sheets but was using the array index | 
|  | //rake36:      for the numberformatid | 
|  |  | 
|  | string format = string.Empty; | 
|  | foreach (var fmt in style.NumberFormats) | 
|  | { | 
|  | if (fmt.NumFmtId == xfs.NumberFormatId) | 
|  | { | 
|  | format = fmt.Format; | 
|  | break; | 
|  | } | 
|  | } | 
|  | //rake36: Don't add another format if it's blank | 
|  | if (!String.IsNullOrEmpty(format)) | 
|  | { | 
|  | int ix = NumberFormats.FindIndexByID(format); | 
|  | if (ix < 0) | 
|  | { | 
|  | var item = new ExcelNumberFormatXml(NameSpaceManager) { Format = format, NumFmtId = NumberFormats.NextId++ }; | 
|  | NumberFormats.Add(format, item); | 
|  | //rake36: Use the just added format id | 
|  | newXfs.NumberFormatId = item.NumFmtId; | 
|  | } | 
|  | else | 
|  | { | 
|  | //rake36: Use the format id defined by the index... not the index itself | 
|  | newXfs.NumberFormatId = NumberFormats[ix].NumFmtId; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | //Font | 
|  | if (xfs.FontId > -1) | 
|  | { | 
|  | int ix = Fonts.FindIndexByID(xfs.Font.Id); | 
|  | if (ix < 0) | 
|  | { | 
|  | ExcelFontXml item = style.Fonts[xfs.FontId].Copy(); | 
|  | ix = Fonts.Add(xfs.Font.Id, item); | 
|  | } | 
|  | newXfs.FontId = ix; | 
|  | } | 
|  |  | 
|  | //Border | 
|  | if (xfs.BorderId > -1) | 
|  | { | 
|  | int ix = Borders.FindIndexByID(xfs.Border.Id); | 
|  | if (ix < 0) | 
|  | { | 
|  | ExcelBorderXml item = style.Borders[xfs.BorderId].Copy(); | 
|  | ix = Borders.Add(xfs.Border.Id, item); | 
|  | } | 
|  | newXfs.BorderId = ix; | 
|  | } | 
|  |  | 
|  | //Fill | 
|  | if (xfs.FillId > -1) | 
|  | { | 
|  | int ix = Fills.FindIndexByID(xfs.Fill.Id); | 
|  | if (ix < 0) | 
|  | { | 
|  | var item = style.Fills[xfs.FillId].Copy(); | 
|  | ix = Fills.Add(xfs.Fill.Id, item); | 
|  | } | 
|  | newXfs.FillId = ix; | 
|  | } | 
|  |  | 
|  | //Named style reference | 
|  | if (xfs.XfId > 0) | 
|  | { | 
|  | var id = style.CellStyleXfs[xfs.XfId].Id; | 
|  | var newId = CellStyleXfs.FindIndexByID(id); | 
|  | if (newId >= 0) | 
|  | { | 
|  | newXfs.XfId = newId; | 
|  | } | 
|  | else if(style._wb!=_wb && allwaysAdd==false) //Not the same workbook, copy the namedstyle to the workbook or match the id | 
|  | { | 
|  | var nsFind = style.NamedStyles.ToDictionary(d => (d.StyleXfId)); | 
|  | if (nsFind.ContainsKey(xfs.XfId)) | 
|  | { | 
|  | var st = nsFind[xfs.XfId]; | 
|  | if (NamedStyles.ExistsKey(st.Name)) | 
|  | { | 
|  | newXfs.XfId = NamedStyles.FindIndexByID(st.Name); | 
|  | } | 
|  | else | 
|  | { | 
|  | var ns = CreateNamedStyle(st.Name, st.Style); | 
|  | newXfs.XfId = NamedStyles.Count - 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | int index; | 
|  | if (isNamedStyle) | 
|  | { | 
|  | index = CellStyleXfs.Add(newXfs.Id, newXfs); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (allwaysAdd) | 
|  | { | 
|  | index = CellXfs.Add(newXfs.Id, newXfs); | 
|  | } | 
|  | else | 
|  | { | 
|  | index = CellXfs.FindIndexByID(newXfs.Id); | 
|  | if (index < 0) | 
|  | { | 
|  | index = CellXfs.Add(newXfs.Id, newXfs); | 
|  | } | 
|  | } | 
|  | } | 
|  | return index; | 
|  | } | 
|  | } | 
|  | } | 
|  | } |