blob: 6bb75ff1905bfe3ff7d8a95bb559e03b571397b3 [file] [log] [blame]
/*******************************************************************************
* 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.Collections.Immutable;
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;
protected override ImmutableArray<string> SchemaNodeOrder { get; } = [
"numFmts",
"fonts",
"fills",
"borders",
"cellStyleXfs",
"cellXfs",
"cellStyles",
"dxfs",
];
internal ExcelStyles(XmlNamespaceManager nameSpaceManager, XmlDocument xml, ExcelWorkbook wb)
: base(nameSpaceManager, xml) {
_styleXml = xml;
_wb = wb;
_nameSpaceManager = nameSpaceManager;
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
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) {
var xfs = isNamedStyle ? style.CellStyleXfs[styleId] : 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;
}
}