| /******************************************************************************* | 
 |  * 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.Collections.Generic; | 
 | using System.Linq; | 
 | using System.Xml; | 
 | using OfficeOpenXml.ConditionalFormatting; | 
 | using OfficeOpenXml.Style; | 
 | using OfficeOpenXml.Style.Dxf; | 
 | using OfficeOpenXml.Style.XmlAccess; | 
 |  | 
 | namespace OfficeOpenXml; | 
 |  | 
 | /// <summary> | 
 | /// Containts all shared cell styles for a workbook | 
 | /// </summary> | 
 | public sealed class ExcelStyles : XmlHelper { | 
 |   private const string _numberFormatsPath = "d:styleSheet/d:numFmts"; | 
 |   private const string _fontsPath = "d:styleSheet/d:fonts"; | 
 |   private const string _fillsPath = "d:styleSheet/d:fills"; | 
 |   private const string _bordersPath = "d:styleSheet/d:borders"; | 
 |   private const string _cellStyleXfsPath = "d:styleSheet/d:cellStyleXfs"; | 
 |   private const string _cellXfsPath = "d:styleSheet/d:cellXfs"; | 
 |   private const string _cellStylesPath = "d:styleSheet/d:cellStyles"; | 
 |   private const string _dxfsPath = "d:styleSheet/d:dxfs"; | 
 |  | 
 |   //internal Dictionary<int, ExcelXfs> Styles = new Dictionary<int, ExcelXfs>(); | 
 |   private readonly XmlDocument _styleXml; | 
 |   private readonly ExcelWorkbook _wb; | 
 |   private readonly 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[] { | 
 |       "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(_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(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, 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, | 
 |       StyleChangeEventArgs e, | 
 |       ExcelAddressBase address, | 
 |       ExcelWorksheet ws, | 
 |       ref Dictionary<int, int> styleCashe) { | 
 |     if (address.Start.Column == 0 || address.Start.Row == 0) { | 
 |       throw (new("error address")); | 
 |     } | 
 |     //Columns | 
 |     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; | 
 |         } | 
 |         column = (ws._values.GetValue(0, col) as ExcelColumn); | 
 |       } | 
 |  | 
 |       if (column._columnMax < address.End.Column) { | 
 |         var newCol = ws.Column(column._columnMax + 1); | 
 |         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(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); | 
 |         } | 
 |         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(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; | 
 |     } | 
 |     if (ws._styles.Exists( | 
 |         row, | 
 |         0, | 
 |         ref v)) //First Row | 
 |     { | 
 |       return v; | 
 |     } // then column | 
 |     if (ws._styles.Exists(0, col, ref v)) { | 
 |       return v; | 
 |     } | 
 |     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); | 
 |       } | 
 |       return 0; | 
 |     } | 
 |     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, 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(); | 
 |   public ExcelStyleCollection<ExcelFontXml> Fonts = new(); | 
 |   public ExcelStyleCollection<ExcelFillXml> Fills = new(); | 
 |   public ExcelStyleCollection<ExcelBorderXml> Borders = new(); | 
 |   public ExcelStyleCollection<ExcelXfs> CellStyleXfs = new(); | 
 |   public ExcelStyleCollection<ExcelXfs> CellXfs = new(); | 
 |   public ExcelStyleCollection<ExcelNamedStyleXml> NamedStyles = new(); | 
 |   public ExcelStyleCollection<ExcelDxfStyleConditionalFormatting> Dxfs = new(); | 
 |  | 
 |   internal string Id => ""; | 
 |  | 
 |   public ExcelNamedStyleXml CreateNamedStyle(string name) { | 
 |     return CreateNamedStyle(name, null); | 
 |   } | 
 |  | 
 |   public ExcelNamedStyleXml CreateNamedStyle(string name, ExcelStyle template) { | 
 |     if (_wb.Styles.NamedStyles.ExistsKey(name)) { | 
 |       throw new(string.Format("Key {0} already exists in collection", name)); | 
 |     } | 
 |  | 
 |     ExcelNamedStyleXml style; | 
 |     style = new(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(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; | 
 |     } | 
 |     return 0; | 
 |     //throw(new Exception("Named style does not exist")); | 
 |   } | 
 |  | 
 |   private string GetXmlNode(XmlNode node) { | 
 |     if (node == null) { | 
 |       return ""; | 
 |     } | 
 |     if (node.Value != null) { | 
 |       return node.Value; | 
 |     } | 
 |     return ""; | 
 |   } | 
 |  | 
 |   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; | 
 |     } | 
 |   } | 
 | } |