9using NanoXLSX.Exceptions;
11using NanoXLSX.Registry;
12using NanoXLSX.Registry.Attributes;
15using NanoXLSX.Utils.Xml;
16using System.Collections.Generic;
22using static
NanoXLSX.Styles.NumberFormat;
29 [NanoXlsxPlugIn(PlugInUUID = PlugInUUID.StyleWriter)]
30 internal class StyleWriter : IPluginWriter
33 private StyleManager styles;
34 private XmlElement styleSheet;
35 private IColorWriter colorWriter;
41 public Workbook Workbook {
get;
set; }
46 public XmlElement XmlElement {
get => styleSheet; }
53 internal StyleWriter()
63 public void Init(IBaseWriter baseWriter)
65 this.styles = baseWriter.Styles;
66 this.Workbook = baseWriter.Workbook;
67 this.colorWriter = PlugInLoader.GetPlugIn<IColorWriter>(PlugInUUID.ColorWriter,
new ColorWriter());
75 styleSheet = XmlElement.CreateElement(
"styleSheet");
76 styleSheet.AddDefaultXmlNameSpace(
"http://schemas.openxmlformats.org/spreadsheetml/2006/main");
77 styleSheet.AddNameSpaceAttribute(
"mc",
"xmlns",
"http://schemas.openxmlformats.org/markup-compatibility/2006");
78 styleSheet.AddNameSpaceAttribute(
"x14ac",
"xmlns",
"http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
79 styleSheet.AddAttribute(
"mc:Ignorable",
"x14ac");
80 int numFormatCount = styles.GetNumberFormatStyleNumber();
81 int fontCount = styles.GetFontStyleNumber();
82 int fillCount = styles.GetFillStyleNumber();
83 int borderCount = styles.GetBorderStyleNumber();
84 int styleCount = styles.GetStyleNumber();
85 if (numFormatCount > 0)
87 XmlElement numFmts = XmlElement.CreateElementWithAttribute(
"numFmts",
"count", ParserUtils.ToString(numFormatCount));
88 numFmts.AddChildElements(GetNumberFormatElements());
89 styleSheet.AddChildElement(numFmts);
91 XmlElement fonts = XmlElement.CreateElementWithAttribute(
"fonts",
"count", ParserUtils.ToString(fontCount));
92 fonts.AddAttribute(
"knownFonts",
"1",
"x14ac");
93 fonts.AddChildElements(GetFontElements());
94 styleSheet.AddChildElement(fonts);
95 XmlElement fills = XmlElement.CreateElementWithAttribute(
"fills",
"count", ParserUtils.ToString(fillCount));
96 fills.AddChildElements(GetFillElements());
97 styleSheet.AddChildElement(fills);
98 XmlElement borders = XmlElement.CreateElementWithAttribute(
"borders",
"count", ParserUtils.ToString(borderCount));
99 borders.AddChildElements(GetBorderElements());
100 styleSheet.AddChildElement(borders);
101 XmlElement cellXfs = XmlElement.CreateElementWithAttribute(
"cellXfs",
"count", ParserUtils.ToString(styleCount));
102 cellXfs.AddChildElements(GetCellXfElements());
103 styleSheet.AddChildElement(cellXfs);
104 if (Workbook.WorkbookMetadata !=
null)
106 XmlElement mruElement = GetMruElement();
107 if (mruElement !=
null)
109 XmlElement colors = styleSheet.AddChildElement(
"colors");
110 colors.AddChildElement(mruElement);
114 WriterPlugInHandler.HandleInlineQueuePlugins(ref styleSheet, Workbook, PlugInUUID.StyleInlineWriter);
121 private List<XmlElement> GetBorderElements()
123 Border[] borderStyles = styles.GetBorders();
124 List<XmlElement> borders =
new List<XmlElement>(borderStyles.Length);
125 foreach (Border item
in borderStyles)
127 XmlElement border = XmlElement.CreateElement(
"border");
128 if (item.DiagonalDown && !item.DiagonalUp) { border.AddAttribute(
"diagonalDown",
"1"); }
129 else if (!item.DiagonalDown && item.DiagonalUp) { border.AddAttribute(
"diagonalUp",
"1"); }
130 else if (item.DiagonalDown && item.DiagonalUp)
132 border.AddAttribute(
"diagonalDown",
"1");
133 border.AddAttribute(
"diagonalUp",
"1");
135 if (item.LeftStyle != StyleValue.None)
137 XmlElement left = border.AddChildElementWithAttribute(
"left",
"style", Border.GetStyleName(item.LeftStyle));
138 if (!
string.IsNullOrEmpty(item.LeftColor)) { left.AddChildElementWithAttribute(
"color",
"rgb", item.LeftColor); }
139 else { left.AddChildElementWithAttribute(
"color",
"auto",
"1"); }
143 border.AddChildElement(
"left");
145 if (item.RightStyle != StyleValue.None)
147 XmlElement right = border.AddChildElementWithAttribute(
"right",
"style", Border.GetStyleName(item.RightStyle));
148 if (!
string.IsNullOrEmpty(item.RightColor)) { right.AddChildElementWithAttribute(
"color",
"rgb", item.RightColor); }
149 else { right.AddChildElementWithAttribute(
"color",
"auto",
"1"); }
153 border.AddChildElement(
"right");
155 if (item.TopStyle != StyleValue.None)
157 XmlElement top = border.AddChildElementWithAttribute(
"top",
"style", Border.GetStyleName(item.TopStyle));
158 if (!
string.IsNullOrEmpty(item.TopColor)) { top.AddChildElementWithAttribute(
"color",
"rgb", item.TopColor); }
159 else { top.AddChildElementWithAttribute(
"color",
"auto",
"1"); }
163 border.AddChildElement(
"top");
165 if (item.BottomStyle != StyleValue.None)
167 XmlElement bottom = border.AddChildElementWithAttribute(
"bottom",
"style", Border.GetStyleName(item.BottomStyle));
168 if (!
string.IsNullOrEmpty(item.BottomColor)) { bottom.AddChildElementWithAttribute(
"color",
"rgb", item.BottomColor); }
169 else { bottom.AddChildElementWithAttribute(
"color",
"auto",
"1"); }
173 border.AddChildElement(
"bottom");
175 if (item.DiagonalStyle != StyleValue.None)
177 XmlElement diagonal = border.AddChildElementWithAttribute(
"diagonal",
"style", Border.GetStyleName(item.DiagonalStyle));
178 if (!
string.IsNullOrEmpty(item.DiagonalColor)) { diagonal.AddChildElementWithAttribute(
"color",
"rgb", item.DiagonalColor); }
179 else { diagonal.AddChildElementWithAttribute(
"color",
"auto",
"1"); }
183 border.AddChildElement(
"diagonal");
194 private List<XmlElement> GetFontElements()
196 Font[] fontStyles = styles.GetFonts();
197 List<XmlElement> fonts =
new List<XmlElement>(fontStyles.Length);
198 foreach (Font item
in fontStyles)
200 XmlElement font = XmlElement.CreateElement(
"font");
201 if (item.Bold) { font.AddChildElement(
"b"); }
202 if (item.Italic) { font.AddChildElement(
"i"); }
203 if (item.Strike) { font.AddChildElement(
"strike"); }
204 if (item.Outline) { font.AddChildElement(
"outline"); }
205 if (item.Shadow) { font.AddChildElement(
"shadow"); }
206 if (item.Condense) { font.AddChildElement(
"condense"); }
207 if (item.Extend) { font.AddChildElement(
"extend"); }
208 if (item.Underline != UnderlineValue.None && item.Underline != UnderlineValue.Single)
210 font.AddChildElementWithAttribute(
"u",
"val", Font.GetUnderlineName(item.Underline));
212 else if (item.Underline == UnderlineValue.Single)
214 font.AddChildElement(
"u");
216 if (item.VerticalAlign != VerticalTextAlignValue.None)
218 font.AddChildElementWithAttribute(
"vertAlign",
"val", Font.GetVerticalTextAlignName(item.VerticalAlign));
220 font.AddChildElementWithAttribute(
"sz",
"val", ParserUtils.ToString(item.Size));
221 if (item.ColorValue !=
null && item.ColorValue.IsDefined)
223 XmlElement color = XmlElement.CreateElement(
"color");
224 color.AddAttributes(colorWriter.GetAttributes(item.ColorValue));
225 font.AddChildElement(color);
227 font.AddChildElementWithAttribute(
"name",
"val", item.Name);
228 font.AddChildElementWithAttribute(
"family",
"val", ParserUtils.ToString((
int)item.Family));
229 if (item.Scheme != SchemeValue.None)
231 if (item.Scheme == SchemeValue.Major)
232 { font.AddChildElementWithAttribute(
"scheme",
"val",
"major"); }
233 else if (item.Scheme == SchemeValue.Minor)
234 { font.AddChildElementWithAttribute(
"scheme",
"val",
"minor"); }
236 font.AddChildElementWithAttribute(
"charset",
"val", ParserUtils.ToString((
int)item.Charset));
246 private List<XmlElement> GetFillElements()
248 Fill[] fillStyles = styles.GetFills();
249 List<XmlElement> fills =
new List<XmlElement>(fillStyles.Length);
250 foreach (Fill fill
in fillStyles)
252 XmlElement fillElement = XmlElement.CreateElement(
"fill");
253 XmlElement patternFillElement = fillElement.AddChildElement(
"patternFill");
254 patternFillElement.AddAttribute(
"patternType", Fill.GetPatternName(fill.PatternFill));
255 if (fill.PatternFill == PatternValue.Solid)
257 XmlElement fgColor = XmlElement.CreateElement(
"fgColor");
258 fgColor.AddAttributes(colorWriter.GetAttributes(fill.ForegroundColor));
259 patternFillElement.AddChildElement(fgColor);
262 if (fill.BackgroundColor.IsDefined)
264 XmlElement bgColor = XmlElement.CreateElement(
"bgColor");
265 bgColor.AddAttributes(colorWriter.GetAttributes(fill.BackgroundColor));
266 patternFillElement.AddChildElement(bgColor);
268 else if (fill.ForegroundColor.Type == Color.ColorType.Rgb
269 || fill.ForegroundColor.Type == Color.ColorType.Theme)
272 XmlElement bgColor = XmlElement.CreateElement(
"bgColor");
273 bgColor.AddAttributes(
274 colorWriter.GetAttributes(
275 Color.CreateIndexed(IndexedColor.DefaultIndexedColor)
277 patternFillElement.AddChildElement(bgColor);
280 else if (fill.PatternFill != PatternValue.None)
282 XmlElement fgColor = XmlElement.CreateElement(
"fgColor");
283 fgColor.AddAttributes(colorWriter.GetAttributes(fill.ForegroundColor));
284 patternFillElement.AddChildElement(fgColor);
285 XmlElement bgColor = XmlElement.CreateElement(
"bgColor");
286 bgColor.AddAttributes(colorWriter.GetAttributes(fill.BackgroundColor));
287 patternFillElement.AddChildElement(bgColor);
289 fills.Add(fillElement);
298 private List<XmlElement> GetNumberFormatElements()
300 NumberFormat[] numberFormatStyles = styles.GetNumberFormats();
301 List<XmlElement> elements =
new List<XmlElement>(numberFormatStyles.Length);
302 foreach (NumberFormat item
in numberFormatStyles)
304 if (item.IsCustomFormat)
306 if (
string.IsNullOrEmpty(item.CustomFormatCode))
308 throw new FormatException(
"The number format style component with the ID " + ParserUtils.ToString(item.CustomFormatID) +
" cannot be null or empty");
312 XmlElement element = XmlElement.CreateElementWithAttribute(
"numFmt",
"formatCode", XmlUtils.SanitizeXmlValue(item.CustomFormatCode));
313 element.AddAttribute(
"numFmtId", ParserUtils.ToString(item.CustomFormatID));
314 elements.Add(element);
324 private List<XmlElement> GetCellXfElements()
326 Style[] styleItems = this.styles.GetStyles();
327 List<XmlElement> xfs =
new List<XmlElement>(styleItems.Length);
328 foreach (Style style
in styleItems)
330 int textRotation = style.CurrentCellXf.CalculateInternalRotation();
331 XmlElement alignment =
null;
332 XmlElement protection =
null;
333 if (style.CurrentCellXf.HorizontalAlign != HorizontalAlignValue.None || style.CurrentCellXf.VerticalAlign != VerticalAlignValue.None || style.CurrentCellXf.Alignment != TextBreakValue.None || textRotation != 0)
335 alignment = XmlElement.CreateElement(
"alignment");
336 if (style.CurrentCellXf.HorizontalAlign != HorizontalAlignValue.None)
338 string alignValue = CellXf.GetHorizontalAlignName(style.CurrentCellXf.HorizontalAlign);
339 alignment.AddAttribute(
"horizontal", alignValue);
341 if (style.CurrentCellXf.VerticalAlign != VerticalAlignValue.None)
343 string alignValue = CellXf.GetVerticalAlignName(style.CurrentCellXf.VerticalAlign);
344 alignment.AddAttribute(
"vertical", alignValue);
346 if (style.CurrentCellXf.Indent > 0 &&
347 (style.CurrentCellXf.HorizontalAlign == HorizontalAlignValue.Left
348 || style.CurrentCellXf.HorizontalAlign == HorizontalAlignValue.Right
349 || style.CurrentCellXf.HorizontalAlign == HorizontalAlignValue.Distributed))
351 alignment.AddAttribute(
"indent", ParserUtils.ToString(style.CurrentCellXf.Indent));
353 if (style.CurrentCellXf.Alignment != TextBreakValue.None)
355 if (style.CurrentCellXf.Alignment == TextBreakValue.ShrinkToFit) { alignment.AddAttribute(
"shrinkToFit",
"1"); }
356 else { alignment.AddAttribute(
"wrapText",
"1"); }
358 if (textRotation != 0)
360 alignment.AddAttribute(
"textRotation", ParserUtils.ToString(textRotation));
363 if (!style.CurrentCellXf.Locked || style.CurrentCellXf.Hidden)
365 protection = XmlElement.CreateElement(
"protection");
366 if (style.CurrentCellXf.Hidden && style.CurrentCellXf.Locked)
368 protection.AddAttribute(
"hidden",
"1");
370 else if (style.CurrentCellXf.Hidden && !style.CurrentCellXf.Locked)
372 protection.AddAttribute(
"hidden",
"1");
373 protection.AddAttribute(
"locked",
"0");
375 else if (!style.CurrentCellXf.Hidden && !style.CurrentCellXf.Locked)
377 protection.AddAttribute(
"locked",
"0");
385 XmlElement xf = XmlElement.CreateElement(
"xf");
386 if (style.CurrentNumberFormat.IsCustomFormat)
388 xf.AddAttribute(
"numFmtId", ParserUtils.ToString(style.CurrentNumberFormat.CustomFormatID));
392 int formatNumber = (int)style.CurrentNumberFormat.Number;
393 xf.AddAttribute(
"numFmtId", ParserUtils.ToString(formatNumber));
395 xf.AddAttribute(
"borderId", ParserUtils.ToString(style.CurrentBorder.InternalID.Value));
396 xf.AddAttribute(
"fillId", ParserUtils.ToString(style.CurrentFill.InternalID.Value));
397 xf.AddAttribute(
"fontId", ParserUtils.ToString(style.CurrentFont.InternalID.Value));
398 if (!style.CurrentFont.IsDefaultFont)
400 xf.AddAttribute(
"applyFont",
"1");
402 if (style.CurrentFill.PatternFill != PatternValue.None)
404 xf.AddAttribute(
"applyFill",
"1");
406 if (!style.CurrentBorder.IsEmpty())
408 xf.AddAttribute(
"applyBorder",
"1");
410 if (alignment !=
null || style.CurrentCellXf.ForceApplyAlignment)
412 xf.AddAttribute(
"applyAlignment",
"1");
414 if (protection !=
null)
416 xf.AddAttribute(
"applyProtection",
"1");
418 if (style.CurrentNumberFormat.Number != FormatNumber.None)
420 xf.AddAttribute(
"applyNumberFormat",
"1");
422 if (alignment !=
null || protection !=
null)
424 xf.AddChildElement(alignment);
425 xf.AddChildElement(protection);
436 private XmlElement GetMruElement()
438 XmlElement mruColors =
null;
439 List<SrgbColor> tempColors =
new List<SrgbColor>();
440 foreach (Color color
in Workbook.GetMruColors())
442 if (!color.IsDefined || (color.Type != Color.ColorType.Rgb && color.Type != Color.ColorType.Indexed))
446 SrgbColor tempColor =
null;
447 if (color.Type == Color.ColorType.Rgb)
449 tempColor = color.RgbColor;
453 tempColor = color.IndexedColor.GetSrgbColor();
455 if (tempColor.StringValue == SrgbColor.DefaultSrgbColor)
459 if (!tempColors.Any(c => c.StringValue == tempColor.StringValue))
461 tempColors.Add(tempColor);
464 if (tempColors.Count > 0)
466 mruColors = XmlElement.CreateElement(
"mruColors");
467 foreach (SrgbColor item
in tempColors)
469 mruColors.AddChildElementWithAttribute(
"color",
"rgb", item.ColorValue);