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