1 // xlsxstyles.cpp
2
3 #include <QtGlobal>
4 #include <QXmlStreamWriter>
5 #include <QXmlStreamReader>
6 #include <QFile>
7 #include <QMap>
8 #include <QDataStream>
9 #include <QDebug>
10 #include <QBuffer>
11
12 #include "xlsxglobal.h"
13 #include "xlsxstyles_p.h"
14 #include "xlsxformat_p.h"
15 #include "xlsxutility_p.h"
16 #include "xlsxcolor_p.h"
17
18 QT_BEGIN_NAMESPACE_XLSX
19
20 /*
21 When loading from existing .xlsx file. we should create a clean styles object.
22 otherwise, default formats should be added.
23
24 */
Styles(CreateFlag flag)25 Styles::Styles(CreateFlag flag)
26 : AbstractOOXmlFile(flag), m_nextCustomNumFmtId(176), m_isIndexedColorsDefault(true)
27 , m_emptyFormatAdded(false)
28 {
29 //!Fix me. Should the custom num fmt Id starts with 164 or 176 or others??
30
31 //!Fix me! Where should we put these register code?
32
33 // issue #172, #89
34 #if QT_VERSION >= 0x060000 // Qt 6.0 or over
35 if (QMetaType::fromName("XlsxColor").isRegistered())
36 #else
37 #if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 ) // Qt 5 or higher
38 if (QMetaType::type("XlsxColor") == QMetaType::UnknownType)
39 #else
40 if (QMetaType::type("XlsxColor") == 0
41 || !QMetaType::isRegistered(QMetaType::type("XlsxColor")))
42 #endif
43 #endif
44 {
45 qRegisterMetaType<XlsxColor>("XlsxColor");
46
47 #if QT_VERSION >= 0x060000
48 // Qt 6
49
50 ///TODO:
51
52 #else
53 // Qt 5
54
55 qRegisterMetaTypeStreamOperators<XlsxColor>("XlsxColor");
56
57 #if QT_VERSION >= 0x050200 // 5.2 or higher
58 QMetaType::registerDebugStreamOperator<XlsxColor>();
59 #endif
60
61 #endif
62 }
63
64 if (flag == F_NewFromScratch) {
65 //Add default Format
66 Format defaultFmt;
67 addXfFormat(defaultFmt);
68
69 //Add another fill format
70 Format fillFmt;
71 fillFmt.setFillPattern(Format::PatternGray125);
72 m_fillsList.append(fillFmt);
73 m_fillsHash.insert(fillFmt.fillKey(), fillFmt);
74 }
75 }
76
~Styles()77 Styles::~Styles()
78 {
79 }
80
xfFormat(int idx) const81 Format Styles::xfFormat(int idx) const
82 {
83 if (idx <0 || idx >= m_xf_formatsList.size())
84 return Format();
85
86 return m_xf_formatsList[idx];
87 }
88
dxfFormat(int idx) const89 Format Styles::dxfFormat(int idx) const
90 {
91 if (idx <0 || idx >= m_dxf_formatsList.size())
92 return Format();
93
94 return m_dxf_formatsList[idx];
95 }
96
97 // dev74 issue#57
fixNumFmt(const Format & format)98 void Styles::fixNumFmt(const Format &format)
99 {
100 if (!format.hasNumFmtData())
101 return;
102
103 if (format.hasProperty(FormatPrivate::P_NumFmt_Id)
104 && !format.stringProperty(FormatPrivate::P_NumFmt_FormatCode).isEmpty())
105 {
106 return;
107 }
108
109 if ( m_builtinNumFmtsHash.isEmpty() )
110 {
111 m_builtinNumFmtsHash.insert(QStringLiteral("General"), 0);
112 m_builtinNumFmtsHash.insert(QStringLiteral("0"), 1);
113 m_builtinNumFmtsHash.insert(QStringLiteral("0.00"), 2);
114 m_builtinNumFmtsHash.insert(QStringLiteral("#,##0"), 3);
115 m_builtinNumFmtsHash.insert(QStringLiteral("#,##0.00"), 4);
116 // m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0_);($#,##0)"), 5);
117 // m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0_);[Red]($#,##0)"), 6);
118 // m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0.00_);($#,##0.00)"), 7);
119 // m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0.00_);[Red]($#,##0.00)"), 8);
120 m_builtinNumFmtsHash.insert(QStringLiteral("0%"), 9);
121 m_builtinNumFmtsHash.insert(QStringLiteral("0.00%"), 10);
122 m_builtinNumFmtsHash.insert(QStringLiteral("0.00E+00"), 11);
123 m_builtinNumFmtsHash.insert(QStringLiteral("# ?/?"), 12);
124 m_builtinNumFmtsHash.insert(QStringLiteral("# ?\?/??"), 13);// Note: "??/" is a c++ trigraph, so escape one "?"
125 m_builtinNumFmtsHash.insert(QStringLiteral("m/d/yy"), 14);
126 m_builtinNumFmtsHash.insert(QStringLiteral("d-mmm-yy"), 15);
127 m_builtinNumFmtsHash.insert(QStringLiteral("d-mmm"), 16);
128 m_builtinNumFmtsHash.insert(QStringLiteral("mmm-yy"), 17);
129 m_builtinNumFmtsHash.insert(QStringLiteral("h:mm AM/PM"), 18);
130 m_builtinNumFmtsHash.insert(QStringLiteral("h:mm:ss AM/PM"), 19);
131 m_builtinNumFmtsHash.insert(QStringLiteral("h:mm"), 20);
132 m_builtinNumFmtsHash.insert(QStringLiteral("h:mm:ss"), 21);
133 m_builtinNumFmtsHash.insert(QStringLiteral("m/d/yy h:mm"), 22);
134
135 m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0_);(#,##0)"), 37);
136 m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0_);[Red](#,##0)"), 38);
137 m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0.00_);(#,##0.00)"), 39);
138 m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0.00_);[Red](#,##0.00)"), 40);
139 // m_builtinNumFmtsHash.insert(QStringLiteral("_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(_)"), 41);
140 // m_builtinNumFmtsHash.insert(QStringLiteral("_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(_)"), 42);
141 // m_builtinNumFmtsHash.insert(QStringLiteral("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(_)"), 43);
142 // m_builtinNumFmtsHash.insert(QStringLiteral("_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(_)"), 44);
143 m_builtinNumFmtsHash.insert(QStringLiteral("mm:ss"), 45);
144 m_builtinNumFmtsHash.insert(QStringLiteral("[h]:mm:ss"), 46);
145 m_builtinNumFmtsHash.insert(QStringLiteral("mm:ss.0"), 47);
146 m_builtinNumFmtsHash.insert(QStringLiteral("##0.0E+0"), 48);
147 m_builtinNumFmtsHash.insert(QStringLiteral("@"), 49);
148
149 // dev74
150 // m_builtinNumFmtsHash.insert(QStringLiteral("0.####"), 176);
151
152 }
153
154 const auto& str = format.numberFormat();
155 if (!str.isEmpty())
156 {
157 QHash<QString, QSharedPointer<XlsxFormatNumberData> >::ConstIterator cIt;
158 //Assign proper number format index
159 const auto& it = m_builtinNumFmtsHash.constFind(str);
160 if (it != m_builtinNumFmtsHash.constEnd())
161 {
162 const_cast<Format *>(&format)->fixNumberFormat(it.value(), str);
163 }
164 else if ((cIt = m_customNumFmtsHash.constFind(str)) != m_customNumFmtsHash.constEnd())
165 {
166 const_cast<Format *>(&format)->fixNumberFormat(cIt.value()->formatIndex, str);
167 }
168 else
169 {
170 //Assign a new fmt Id.
171 const_cast<Format *>(&format)->fixNumberFormat(m_nextCustomNumFmtId, str);
172
173 QSharedPointer<XlsxFormatNumberData> fmt(new XlsxFormatNumberData);
174 fmt->formatIndex = m_nextCustomNumFmtId;
175 fmt->formatString = str;
176 m_customNumFmtIdMap.insert(m_nextCustomNumFmtId, fmt);
177 m_customNumFmtsHash.insert(str, fmt);
178
179 m_nextCustomNumFmtId += 1;
180 }
181 }
182 else
183 {
184 const auto id = format.numberFormatIndex();
185 //Assign proper format code, this is needed by dxf format
186 const auto& it = m_customNumFmtIdMap.constFind(id);
187 if (it != m_customNumFmtIdMap.constEnd())
188 {
189 const_cast<Format *>(&format)->fixNumberFormat(id, it.value()->formatString);
190 }
191 else
192 {
193 bool found = false;
194 for ( auto&& it = m_builtinNumFmtsHash.constBegin() ; it != m_builtinNumFmtsHash.constEnd() ; ++it )
195 {
196 if (it.value() == id)
197 {
198 const_cast<Format *>(&format)->fixNumberFormat(id, it.key());
199 found = true;
200 break;
201 }
202 }
203
204 if (!found)
205 {
206 //Wrong numFmt
207 const_cast<Format *>(&format)->fixNumberFormat(id, QStringLiteral("General"));
208 }
209 }
210 }
211 }
212
213 /*
214 Assign index to Font/Fill/Border and Format
215
216 When \a force is true, add the format to the format list, even other format has
217 the same key have been in.
218 This is useful when reading existing .xlsx files which may contains duplicated formats.
219 */
addXfFormat(const Format & format,bool force)220 void Styles::addXfFormat(const Format &format, bool force)
221 {
222 if (format.isEmpty())
223 {
224 //Try do something for empty Format.
225 if (m_emptyFormatAdded && !force)
226 return;
227
228 m_emptyFormatAdded = true;
229 }
230
231 //numFmt
232 if (format.hasNumFmtData() &&
233 !format.hasProperty(FormatPrivate::P_NumFmt_Id))
234 {
235 fixNumFmt(format);
236 }
237
238 //Font
239 const auto& fontIt = m_fontsHash.constFind(format.fontKey());
240 if (format.hasFontData() && !format.fontIndexValid())
241 {
242 //Assign proper font index, if has font data.
243 if (fontIt == m_fontsHash.constEnd())
244 const_cast<Format *>(&format)->setFontIndex(m_fontsList.size());
245 else
246 const_cast<Format *>(&format)->setFontIndex(fontIt->fontIndex());
247 }
248 if (fontIt == m_fontsHash.constEnd())
249 {
250 //Still a valid font if the format has no fontData. (All font properties are default)
251 m_fontsList.append(format);
252 m_fontsHash[format.fontKey()] = format;
253 }
254
255 //Fill
256 const auto& fillIt = m_fillsHash.constFind(format.fillKey());
257 if (format.hasFillData() && !format.fillIndexValid()) {
258 //Assign proper fill index, if has fill data.
259 if (fillIt == m_fillsHash.constEnd())
260 const_cast<Format *>(&format)->setFillIndex(m_fillsList.size());
261 else
262 const_cast<Format *>(&format)->setFillIndex(fillIt->fillIndex());
263 }
264 if (fillIt == m_fillsHash.constEnd()) {
265 //Still a valid fill if the format has no fillData. (All fill properties are default)
266 m_fillsList.append(format);
267 m_fillsHash[format.fillKey()] = format;
268 }
269
270 //Border
271 const auto& borderIt = m_bordersHash.constFind(format.borderKey());
272 if (format.hasBorderData() && !format.borderIndexValid()) {
273 //Assign proper border index, if has border data.
274 if (borderIt == m_bordersHash.constEnd())
275 const_cast<Format *>(&format)->setBorderIndex(m_bordersList.size());
276 else
277 const_cast<Format *>(&format)->setBorderIndex(borderIt->borderIndex());
278 }
279 if (borderIt == m_bordersHash.constEnd()) {
280 //Still a valid border if the format has no borderData. (All border properties are default)
281 m_bordersList.append(format);
282 m_bordersHash[format.borderKey()] = format;
283 }
284
285 //Format
286 const auto& formatIt = m_xf_formatsHash.constFind(format.formatKey());
287 if (!format.isEmpty() && !format.xfIndexValid())
288 {
289 if (formatIt == m_xf_formatsHash.constEnd())
290 const_cast<Format *>(&format)->setXfIndex(m_xf_formatsList.size());
291 else
292 const_cast<Format *>(&format)->setXfIndex(formatIt->xfIndex());
293 }
294
295 if (formatIt == m_xf_formatsHash.constEnd() ||
296 force)
297 {
298 m_xf_formatsList.append(format);
299 m_xf_formatsHash[format.formatKey()] = format;
300 }
301 }
302
addDxfFormat(const Format & format,bool force)303 void Styles::addDxfFormat(const Format &format, bool force)
304 {
305 //numFmt
306 if ( format.hasNumFmtData() )
307 {
308 fixNumFmt(format);
309 }
310
311 const auto& formatIt = m_dxf_formatsHash.constFind(format.formatKey());
312 if ( !format.isEmpty() &&
313 !format.dxfIndexValid() )
314 {
315 if (formatIt == m_dxf_formatsHash.constEnd() ) // m_xf_formatsHash.constEnd()) // issue #108
316 {
317 const_cast<Format *>(&format)->setDxfIndex( m_dxf_formatsList.size() );
318 }
319 else
320 {
321 const_cast<Format *>(&format)->setDxfIndex( formatIt->dxfIndex() );
322 }
323 }
324
325 if (formatIt == m_xf_formatsHash.constEnd() ||
326 force )
327 {
328 m_dxf_formatsList.append(format);
329 m_dxf_formatsHash[ format.formatKey() ] = format;
330 }
331 }
332
saveToXmlFile(QIODevice * device) const333 void Styles::saveToXmlFile(QIODevice *device) const
334 {
335 QXmlStreamWriter writer(device);
336
337 writer.writeStartDocument(QStringLiteral("1.0"), true);
338 writer.writeStartElement(QStringLiteral("styleSheet"));
339 writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
340
341 writeNumFmts(writer);
342 writeFonts(writer);
343 writeFills(writer);
344 writeBorders(writer);
345
346 writer.writeStartElement(QStringLiteral("cellStyleXfs"));
347 writer.writeAttribute(QStringLiteral("count"), QStringLiteral("1"));
348 writer.writeStartElement(QStringLiteral("xf"));
349 writer.writeAttribute(QStringLiteral("numFmtId"), QStringLiteral("0"));
350 writer.writeAttribute(QStringLiteral("fontId"), QStringLiteral("0"));
351 writer.writeAttribute(QStringLiteral("fillId"), QStringLiteral("0"));
352 writer.writeAttribute(QStringLiteral("borderId"), QStringLiteral("0"));
353 writer.writeEndElement();//xf
354 writer.writeEndElement();//cellStyleXfs
355
356 writeCellXfs(writer);
357
358 writer.writeStartElement(QStringLiteral("cellStyles"));
359 writer.writeAttribute(QStringLiteral("count"), QStringLiteral("1"));
360 writer.writeStartElement(QStringLiteral("cellStyle"));
361 writer.writeAttribute(QStringLiteral("name"), QStringLiteral("Normal"));
362 writer.writeAttribute(QStringLiteral("xfId"), QStringLiteral("0"));
363 writer.writeAttribute(QStringLiteral("builtinId"), QStringLiteral("0"));
364 writer.writeEndElement();//cellStyle
365 writer.writeEndElement();//cellStyles
366
367 writeDxfs(writer);
368
369 writer.writeStartElement(QStringLiteral("tableStyles"));
370 writer.writeAttribute(QStringLiteral("count"), QStringLiteral("0"));
371 writer.writeAttribute(QStringLiteral("defaultTableStyle"), QStringLiteral("TableStyleMedium9"));
372 writer.writeAttribute(QStringLiteral("defaultPivotStyle"), QStringLiteral("PivotStyleLight16"));
373 writer.writeEndElement();//tableStyles
374
375 writeColors(writer);
376
377 writer.writeEndElement();//styleSheet
378 writer.writeEndDocument();
379 }
380
writeNumFmts(QXmlStreamWriter & writer) const381 void Styles::writeNumFmts(QXmlStreamWriter &writer) const
382 {
383 if (m_customNumFmtIdMap.size() == 0)
384 return;
385
386 writer.writeStartElement(QStringLiteral("numFmts"));
387 writer.writeAttribute(QStringLiteral("count"), QString::number(m_customNumFmtIdMap.count()));
388
389 QMapIterator<int, QSharedPointer<XlsxFormatNumberData> > it(m_customNumFmtIdMap);
390 while (it.hasNext()) {
391 it.next();
392 writer.writeEmptyElement(QStringLiteral("numFmt"));
393 writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(it.value()->formatIndex));
394 writer.writeAttribute(QStringLiteral("formatCode"), it.value()->formatString);
395 }
396 writer.writeEndElement();//numFmts
397 }
398
399 /*
400 */
writeFonts(QXmlStreamWriter & writer) const401 void Styles::writeFonts(QXmlStreamWriter &writer) const
402 {
403 writer.writeStartElement(QStringLiteral("fonts"));
404 writer.writeAttribute(QStringLiteral("count"), QString::number(m_fontsList.count()));
405 for (int i=0; i<m_fontsList.size(); ++i)
406 writeFont(writer, m_fontsList[i], false);
407 writer.writeEndElement();//fonts
408 }
409
writeFont(QXmlStreamWriter & writer,const Format & format,bool isDxf) const410 void Styles::writeFont(QXmlStreamWriter &writer, const Format &format, bool isDxf) const
411 {
412 writer.writeStartElement(QStringLiteral("font"));
413
414 //The condense and extend elements are mainly used in dxf format
415 if (format.hasProperty(FormatPrivate::P_Font_Condense)
416 && !format.boolProperty(FormatPrivate::P_Font_Condense)) {
417 writer.writeEmptyElement(QStringLiteral("condense"));
418 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
419 }
420 if (format.hasProperty(FormatPrivate::P_Font_Extend)
421 && !format.boolProperty(FormatPrivate::P_Font_Extend)) {
422 writer.writeEmptyElement(QStringLiteral("extend"));
423 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
424 }
425
426 if (format.fontBold())
427 writer.writeEmptyElement(QStringLiteral("b"));
428 if (format.fontItalic())
429 writer.writeEmptyElement(QStringLiteral("i"));
430 if (format.fontStrikeOut())
431 writer.writeEmptyElement(QStringLiteral("strike"));
432 if (format.fontOutline())
433 writer.writeEmptyElement(QStringLiteral("outline"));
434 if (format.boolProperty(FormatPrivate::P_Font_Shadow))
435 writer.writeEmptyElement(QStringLiteral("shadow"));
436 if (format.hasProperty(FormatPrivate::P_Font_Underline)) {
437 Format::FontUnderline u = format.fontUnderline();
438 if (u != Format::FontUnderlineNone) {
439 writer.writeEmptyElement(QStringLiteral("u"));
440 if (u== Format::FontUnderlineDouble)
441 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double"));
442 else if (u == Format::FontUnderlineSingleAccounting)
443 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting"));
444 else if (u == Format::FontUnderlineDoubleAccounting)
445 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting"));
446 }
447 }
448 if (format.hasProperty(FormatPrivate::P_Font_Script)) {
449 Format::FontScript s = format.fontScript();
450 if (s != Format::FontScriptNormal) {
451 writer.writeEmptyElement(QStringLiteral("vertAlign"));
452 if (s == Format::FontScriptSuper)
453 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript"));
454 else
455 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript"));
456 }
457 }
458
459 if (!isDxf && format.hasProperty(FormatPrivate::P_Font_Size)) {
460 writer.writeEmptyElement(QStringLiteral("sz"));
461 writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize()));
462 }
463
464 if (format.hasProperty(FormatPrivate::P_Font_Color)) {
465 XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>();
466 color.saveToXml(writer);
467 }
468
469 if (!isDxf) {
470 if (!format.fontName().isEmpty()) {
471 writer.writeEmptyElement(QStringLiteral("name"));
472 writer.writeAttribute(QStringLiteral("val"), format.fontName());
473 }
474 if (format.hasProperty(FormatPrivate::P_Font_Charset)) {
475 writer.writeEmptyElement(QStringLiteral("charset"));
476 writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Charset)));
477 }
478 if (format.hasProperty(FormatPrivate::P_Font_Family)) {
479 writer.writeEmptyElement(QStringLiteral("family"));
480 writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family)));
481 }
482
483 if (format.hasProperty(FormatPrivate::P_Font_Scheme)) {
484 writer.writeEmptyElement(QStringLiteral("scheme"));
485 writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme));
486 }
487 }
488 writer.writeEndElement(); //font
489 }
490
writeFills(QXmlStreamWriter & writer) const491 void Styles::writeFills(QXmlStreamWriter &writer) const
492 {
493 writer.writeStartElement(QStringLiteral("fills"));
494 writer.writeAttribute(QStringLiteral("count"), QString::number(m_fillsList.size()));
495
496 for (int i=0; i<m_fillsList.size(); ++i)
497 writeFill(writer, m_fillsList[i]);
498
499 writer.writeEndElement(); //fills
500 }
501
writeFill(QXmlStreamWriter & writer,const Format & fill,bool isDxf) const502 void Styles::writeFill(QXmlStreamWriter &writer, const Format &fill, bool isDxf) const
503 {
504 static const QMap<int, QString> patternStrings = {
505 {Format::PatternNone, QStringLiteral("none")},
506 {Format::PatternSolid, QStringLiteral("solid")},
507 {Format::PatternMediumGray, QStringLiteral("mediumGray")},
508 {Format::PatternDarkGray, QStringLiteral("darkGray")},
509 {Format::PatternLightGray, QStringLiteral("lightGray")},
510 {Format::PatternDarkHorizontal, QStringLiteral("darkHorizontal")},
511 {Format::PatternDarkVertical, QStringLiteral("darkVertical")},
512 {Format::PatternDarkDown, QStringLiteral("darkDown")},
513 {Format::PatternDarkUp, QStringLiteral("darkUp")},
514 {Format::PatternDarkGrid, QStringLiteral("darkGrid")},
515 {Format::PatternDarkTrellis, QStringLiteral("darkTrellis")},
516 {Format::PatternLightHorizontal, QStringLiteral("lightHorizontal")},
517 {Format::PatternLightVertical, QStringLiteral("lightVertical")},
518 {Format::PatternLightDown, QStringLiteral("lightDown")},
519 {Format::PatternLightUp, QStringLiteral("lightUp")},
520 {Format::PatternLightTrellis, QStringLiteral("lightTrellis")},
521 {Format::PatternGray125, QStringLiteral("gray125")},
522 {Format::PatternGray0625, QStringLiteral("gray0625")},
523 {Format::PatternLightGrid, QStringLiteral("lightGrid")}
524 };
525
526 writer.writeStartElement(QStringLiteral("fill"));
527 writer.writeStartElement(QStringLiteral("patternFill"));
528 Format::FillPattern pattern = fill.fillPattern();
529 // For normal fill formats, Excel prefer to outputing the default "none" attribute
530 // But for dxf, Excel prefer to omiting the default "none"
531 // Though not make any difference, but it make easier to compare origin files with generate files during debug
532 if (!(pattern == Format::PatternNone && isDxf))
533 writer.writeAttribute(QStringLiteral("patternType"), patternStrings[pattern]);
534 // For a solid fill, Excel reverses the role of foreground and background colours
535 if (fill.fillPattern() == Format::PatternSolid) {
536 if (fill.hasProperty(FormatPrivate::P_Fill_BgColor))
537 fill.property(FormatPrivate::P_Fill_BgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("fgColor"));
538 if (fill.hasProperty(FormatPrivate::P_Fill_FgColor))
539 fill.property(FormatPrivate::P_Fill_FgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("bgColor"));
540 } else {
541 if (fill.hasProperty(FormatPrivate::P_Fill_FgColor))
542 fill.property(FormatPrivate::P_Fill_FgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("fgColor"));
543 if (fill.hasProperty(FormatPrivate::P_Fill_BgColor))
544 fill.property(FormatPrivate::P_Fill_BgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("bgColor"));
545 }
546 writer.writeEndElement();//patternFill
547 writer.writeEndElement();//fill
548 }
549
writeBorders(QXmlStreamWriter & writer) const550 void Styles::writeBorders(QXmlStreamWriter &writer) const
551 {
552 writer.writeStartElement(QStringLiteral("borders"));
553 writer.writeAttribute(QStringLiteral("count"), QString::number(m_bordersList.count()));
554 for (int i=0; i<m_bordersList.size(); ++i)
555 writeBorder(writer, m_bordersList[i]);
556 writer.writeEndElement();//borders
557 }
558
writeBorder(QXmlStreamWriter & writer,const Format & border,bool isDxf) const559 void Styles::writeBorder(QXmlStreamWriter &writer, const Format &border, bool isDxf) const
560 {
561 writer.writeStartElement(QStringLiteral("border"));
562 if (border.hasProperty(FormatPrivate::P_Border_DiagonalType)) {
563 Format::DiagonalBorderType t = border.diagonalBorderType();
564 if (t == Format::DiagonalBorderUp) {
565 writer.writeAttribute(QStringLiteral("diagonalUp"), QStringLiteral("1"));
566 } else if (t == Format::DiagonalBorderDown) {
567 writer.writeAttribute(QStringLiteral("diagonalDown"), QStringLiteral("1"));
568 } else if (t == Format::DiagnoalBorderBoth) {
569 writer.writeAttribute(QStringLiteral("diagonalUp"), QStringLiteral("1"));
570 writer.writeAttribute(QStringLiteral("diagonalDown"), QStringLiteral("1"));
571 }
572 }
573
574 writeSubBorder(writer, QStringLiteral("left"), border.leftBorderStyle(), border.property(FormatPrivate::P_Border_LeftColor).value<XlsxColor>());
575 writeSubBorder(writer, QStringLiteral("right"), border.rightBorderStyle(), border.property(FormatPrivate::P_Border_RightColor).value<XlsxColor>());
576 writeSubBorder(writer, QStringLiteral("top"), border.topBorderStyle(), border.property(FormatPrivate::P_Border_TopColor).value<XlsxColor>());
577 writeSubBorder(writer, QStringLiteral("bottom"), border.bottomBorderStyle(), border.property(FormatPrivate::P_Border_BottomColor).value<XlsxColor>());
578
579 //Condition DXF formats don't allow diagonal style
580 if (!isDxf)
581 writeSubBorder(writer, QStringLiteral("diagonal"), border.diagonalBorderStyle(), border.property(FormatPrivate::P_Border_DiagonalColor).value<XlsxColor>());
582
583 if (isDxf) {
584 // writeSubBorder(wirter, QStringLiteral("vertical"), );
585 // writeSubBorder(writer, QStringLiteral("horizontal"), );
586 }
587
588 writer.writeEndElement();//border
589 }
590
writeSubBorder(QXmlStreamWriter & writer,const QString & type,int style,const XlsxColor & color) const591 void Styles::writeSubBorder(QXmlStreamWriter &writer, const QString &type, int style, const XlsxColor &color) const
592 {
593 if (style == Format::BorderNone) {
594 writer.writeEmptyElement(type);
595 return;
596 }
597
598 static const QMap<int, QString> stylesString = {
599 {Format::BorderNone, QStringLiteral("none")},
600 {Format::BorderThin, QStringLiteral("thin")},
601 {Format::BorderMedium, QStringLiteral("medium")},
602 {Format::BorderDashed, QStringLiteral("dashed")},
603 {Format::BorderDotted, QStringLiteral("dotted")},
604 {Format::BorderThick, QStringLiteral("thick")},
605 {Format::BorderDouble, QStringLiteral("double")},
606 {Format::BorderHair, QStringLiteral("hair")},
607 {Format::BorderMediumDashed, QStringLiteral("mediumDashed")},
608 {Format::BorderDashDot, QStringLiteral("dashDot")},
609 {Format::BorderMediumDashDot, QStringLiteral("mediumDashDot")},
610 {Format::BorderDashDotDot, QStringLiteral("dashDotDot")},
611 {Format::BorderMediumDashDotDot, QStringLiteral("mediumDashDotDot")},
612 {Format::BorderSlantDashDot, QStringLiteral("slantDashDot")}
613 };
614
615 writer.writeStartElement(type);
616 writer.writeAttribute(QStringLiteral("style"), stylesString[style]);
617 color.saveToXml(writer); //write color element
618
619 writer.writeEndElement();//type
620 }
621
writeCellXfs(QXmlStreamWriter & writer) const622 void Styles::writeCellXfs(QXmlStreamWriter &writer) const
623 {
624 writer.writeStartElement(QStringLiteral("cellXfs"));
625 writer.writeAttribute(QStringLiteral("count"), QString::number(m_xf_formatsList.size()));
626 for (const Format &format : m_xf_formatsList) {
627 int xf_id = 0;
628 writer.writeStartElement(QStringLiteral("xf"));
629 writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(format.numberFormatIndex()));
630 writer.writeAttribute(QStringLiteral("fontId"), QString::number(format.fontIndex()));
631 writer.writeAttribute(QStringLiteral("fillId"), QString::number(format.fillIndex()));
632 writer.writeAttribute(QStringLiteral("borderId"), QString::number(format.borderIndex()));
633 writer.writeAttribute(QStringLiteral("xfId"), QString::number(xf_id));
634 if (format.hasNumFmtData())
635 writer.writeAttribute(QStringLiteral("applyNumberFormat"), QStringLiteral("1"));
636 if (format.hasFontData())
637 writer.writeAttribute(QStringLiteral("applyFont"), QStringLiteral("1"));
638 if (format.hasFillData())
639 writer.writeAttribute(QStringLiteral("applyFill"), QStringLiteral("1"));
640 if (format.hasBorderData())
641 writer.writeAttribute(QStringLiteral("applyBorder"), QStringLiteral("1"));
642 if (format.hasAlignmentData())
643 writer.writeAttribute(QStringLiteral("applyAlignment"), QStringLiteral("1"));
644
645 if (format.hasAlignmentData()) {
646 writer.writeEmptyElement(QStringLiteral("alignment"));
647 if (format.hasProperty(FormatPrivate::P_Alignment_AlignH)) {
648 switch (format.horizontalAlignment()) {
649 case Format::AlignLeft:
650 writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("left"));
651 break;
652 case Format::AlignHCenter:
653 writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("center"));
654 break;
655 case Format::AlignRight:
656 writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("right"));
657 break;
658 case Format::AlignHFill:
659 writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("fill"));
660 break;
661 case Format::AlignHJustify:
662 writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("justify"));
663 break;
664 case Format::AlignHMerge:
665 writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("centerContinuous"));
666 break;
667 case Format::AlignHDistributed:
668 writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("distributed"));
669 break;
670 default:
671 break;
672 }
673 }
674
675 if (format.hasProperty(FormatPrivate::P_Alignment_AlignV)) {
676 switch (format.verticalAlignment()) {
677 case Format::AlignTop:
678 writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("top"));
679 break;
680 case Format::AlignVCenter:
681 writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("center"));
682 break;
683 case Format::AlignVJustify:
684 writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("justify"));
685 break;
686 case Format::AlignVDistributed:
687 writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("distributed"));
688 break;
689 default:
690 break;
691 }
692 }
693 if (format.hasProperty(FormatPrivate::P_Alignment_Indent))
694 writer.writeAttribute(QStringLiteral("indent"), QString::number(format.indent()));
695 if (format.hasProperty(FormatPrivate::P_Alignment_Wrap) && format.textWrap())
696 writer.writeAttribute(QStringLiteral("wrapText"), QStringLiteral("1"));
697 if (format.hasProperty(FormatPrivate::P_Alignment_ShinkToFit) && format.shrinkToFit())
698 writer.writeAttribute(QStringLiteral("shrinkToFit"), QStringLiteral("1"));
699 if (format.hasProperty(FormatPrivate::P_Alignment_Rotation))
700 writer.writeAttribute(QStringLiteral("textRotation"), QString::number(format.rotation()));
701 }
702
703 writer.writeEndElement();//xf
704 }
705 writer.writeEndElement();//cellXfs
706 }
707
writeDxfs(QXmlStreamWriter & writer) const708 void Styles::writeDxfs(QXmlStreamWriter &writer) const
709 {
710 writer.writeStartElement(QStringLiteral("dxfs"));
711 writer.writeAttribute(QStringLiteral("count"), QString::number(m_dxf_formatsList.size()));
712 for (const Format &format : m_dxf_formatsList)
713 writeDxf(writer, format);
714 writer.writeEndElement(); //dxfs
715 }
716
writeDxf(QXmlStreamWriter & writer,const Format & format) const717 void Styles::writeDxf(QXmlStreamWriter &writer, const Format &format) const
718 {
719 writer.writeStartElement(QStringLiteral("dxf"));
720
721 if (format.hasFontData())
722 writeFont(writer, format, true);
723
724 if (format.hasNumFmtData()) {
725 writer.writeEmptyElement(QStringLiteral("numFmt"));
726 writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(format.numberFormatIndex()));
727 writer.writeAttribute(QStringLiteral("formatCode"), format.numberFormat());
728 }
729
730 if (format.hasFillData())
731 writeFill(writer, format, true);
732
733 if (format.hasBorderData())
734 writeBorder(writer, format, true);
735
736 writer.writeEndElement();//dxf
737 }
738
writeColors(QXmlStreamWriter & writer) const739 void Styles::writeColors(QXmlStreamWriter &writer) const
740 {
741 if (m_isIndexedColorsDefault) //Don't output the default indexdeColors
742 return;
743
744 writer.writeStartElement(QStringLiteral("colors"));
745
746 writer.writeStartElement(QStringLiteral("indexedColors"));
747 for (const QColor &color : m_indexedColors) {
748 writer.writeEmptyElement(QStringLiteral("rgbColor"));
749 writer.writeAttribute(QStringLiteral("rgb"), XlsxColor::toARGBString(color));
750 }
751
752 writer.writeEndElement();//indexedColors
753
754 writer.writeEndElement();//colors
755 }
756
readNumFmts(QXmlStreamReader & reader)757 bool Styles::readNumFmts(QXmlStreamReader &reader)
758 {
759 Q_ASSERT(reader.name() == QLatin1String("numFmts"));
760 const auto& attributes = reader.attributes();
761 const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
762 const auto count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
763
764 //Read utill we find the numFmts end tag or ....
765 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
766 && reader.name() == QLatin1String("numFmts"))) {
767 reader.readNextStartElement();
768 if (reader.tokenType() == QXmlStreamReader::StartElement) {
769 if (reader.name() == QLatin1String("numFmt")) {
770 const auto& attributes = reader.attributes();
771 QSharedPointer<XlsxFormatNumberData> fmt (new XlsxFormatNumberData);
772 fmt->formatIndex = attributes.value(QLatin1String("numFmtId")).toString().toInt();
773 fmt->formatString = attributes.value(QLatin1String("formatCode")).toString();
774 if (fmt->formatIndex >= m_nextCustomNumFmtId)
775 m_nextCustomNumFmtId = fmt->formatIndex + 1;
776 m_customNumFmtIdMap.insert(fmt->formatIndex, fmt);
777 m_customNumFmtsHash.insert(fmt->formatString, fmt);
778 }
779 }
780 }
781
782 if (reader.hasError())
783 qWarning()<<reader.errorString();
784
785 if (hasCount && (count != m_customNumFmtIdMap.size()))
786 qWarning("error read custom numFmts");
787
788 return true;
789 }
790
readFonts(QXmlStreamReader & reader)791 bool Styles::readFonts(QXmlStreamReader &reader)
792 {
793 Q_ASSERT(reader.name() == QLatin1String("fonts"));
794 const auto& attributes = reader.attributes();
795 const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
796 const auto count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
797 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
798 && reader.name() == QLatin1String("fonts"))) {
799 reader.readNextStartElement();
800 if (reader.tokenType() == QXmlStreamReader::StartElement) {
801 if (reader.name() == QLatin1String("font")) {
802 Format format;
803 readFont(reader, format);
804 m_fontsList.append(format);
805 m_fontsHash.insert(format.fontKey(), format);
806 if (format.isValid())
807 format.setFontIndex(m_fontsList.size()-1);
808 }
809 }
810 }
811 if (reader.hasError())
812 qWarning()<<reader.errorString();
813
814 if (hasCount && (count != m_fontsList.size()))
815 qWarning("error read fonts");
816 return true;
817 }
818
readFont(QXmlStreamReader & reader,Format & format)819 bool Styles::readFont(QXmlStreamReader &reader, Format &format)
820 {
821 Q_ASSERT(reader.name() == QLatin1String("font"));
822 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
823 && reader.name() == QLatin1String("font"))) {
824 reader.readNextStartElement();
825 if (reader.tokenType() == QXmlStreamReader::StartElement) {
826 const auto& attributes = reader.attributes();
827 if (reader.name() == QLatin1String("name")) {
828 format.setFontName(attributes.value(QLatin1String("val")).toString());
829 } else if (reader.name() == QLatin1String("charset")) {
830 format.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toString().toInt());
831 } else if (reader.name() == QLatin1String("family")) {
832 format.setProperty(FormatPrivate::P_Font_Family, attributes.value(QLatin1String("val")).toString().toInt());
833 } else if (reader.name() == QLatin1String("b")) {
834 format.setFontBold(true);
835 } else if (reader.name() == QLatin1String("i")) {
836 format.setFontItalic(true);
837 } else if (reader.name() == QLatin1String("strike")) {
838 format.setFontStrikeOut(true);
839 } else if (reader.name() == QLatin1String("outline")) {
840 format.setFontOutline(true);
841 } else if (reader.name() == QLatin1String("shadow")) {
842 format.setProperty(FormatPrivate::P_Font_Shadow, true);
843 } else if (reader.name() == QLatin1String("condense")) {
844 format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toString().toInt());
845 } else if (reader.name() == QLatin1String("extend")) {
846 format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toString().toInt());
847 } else if (reader.name() == QLatin1String("color")) {
848 XlsxColor color;
849 color.loadFromXml(reader);
850 format.setProperty(FormatPrivate::P_Font_Color, color);
851 } else if (reader.name() == QLatin1String("sz")) {
852 const auto sz = attributes.value(QLatin1String("val")).toString().toInt();
853 format.setFontSize(sz);
854 } else if (reader.name() == QLatin1String("u")) {
855 QString value = attributes.value(QLatin1String("val")).toString();
856 if (value == QLatin1String("double"))
857 format.setFontUnderline(Format::FontUnderlineDouble);
858 else if (value == QLatin1String("doubleAccounting"))
859 format.setFontUnderline(Format::FontUnderlineDoubleAccounting);
860 else if (value == QLatin1String("singleAccounting"))
861 format.setFontUnderline(Format::FontUnderlineSingleAccounting);
862 else
863 format.setFontUnderline(Format::FontUnderlineSingle);
864 } else if (reader.name() == QLatin1String("vertAlign")) {
865 QString value = attributes.value(QLatin1String("val")).toString();
866 if (value == QLatin1String("superscript"))
867 format.setFontScript(Format::FontScriptSuper);
868 else if (value == QLatin1String("subscript"))
869 format.setFontScript(Format::FontScriptSub);
870 } else if (reader.name() == QLatin1String("scheme")) {
871 format.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString());
872 }
873 }
874 }
875 return true;
876 }
877
readFills(QXmlStreamReader & reader)878 bool Styles::readFills(QXmlStreamReader &reader)
879 {
880 Q_ASSERT(reader.name() == QLatin1String("fills"));
881
882 const auto& attributes = reader.attributes();
883 const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
884 const auto count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
885 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
886 && reader.name() == QLatin1String("fills"))) {
887 reader.readNextStartElement();
888 if (reader.tokenType() == QXmlStreamReader::StartElement) {
889 if (reader.name() == QLatin1String("fill")) {
890 Format fill;
891 readFill(reader, fill);
892 m_fillsList.append(fill);
893 m_fillsHash.insert(fill.fillKey(), fill);
894 if (fill.isValid())
895 fill.setFillIndex(m_fillsList.size()-1);
896 }
897 }
898 }
899 if (reader.hasError())
900 qWarning()<<reader.errorString();
901
902 if (hasCount && (count != m_fillsList.size()))
903 qWarning("error read fills");
904 return true;
905 }
906
readFill(QXmlStreamReader & reader,Format & fill)907 bool Styles::readFill(QXmlStreamReader &reader, Format &fill)
908 {
909 Q_ASSERT(reader.name() == QLatin1String("fill"));
910
911 static const QMap<QString, Format::FillPattern> patternValues = {
912 {QStringLiteral("none"), Format::PatternNone},
913 {QStringLiteral("solid"), Format::PatternSolid},
914 {QStringLiteral("mediumGray"), Format::PatternMediumGray},
915 {QStringLiteral("darkGray"), Format::PatternDarkGray},
916 {QStringLiteral("lightGray"), Format::PatternLightGray},
917 {QStringLiteral("darkHorizontal"), Format::PatternDarkHorizontal},
918 {QStringLiteral("darkVertical"), Format::PatternDarkVertical},
919 {QStringLiteral("darkDown"), Format::PatternDarkDown},
920 {QStringLiteral("darkUp"), Format::PatternDarkUp},
921 {QStringLiteral("darkGrid"), Format::PatternDarkGrid},
922 {QStringLiteral("darkTrellis"), Format::PatternDarkTrellis},
923 {QStringLiteral("lightHorizontal"), Format::PatternLightHorizontal},
924 {QStringLiteral("lightVertical"), Format::PatternLightVertical},
925 {QStringLiteral("lightDown"), Format::PatternLightDown},
926 {QStringLiteral("lightUp"), Format::PatternLightUp},
927 {QStringLiteral("lightTrellis"), Format::PatternLightTrellis},
928 {QStringLiteral("gray125"), Format::PatternGray125},
929 {QStringLiteral("gray0625"), Format::PatternGray0625},
930 {QStringLiteral("lightGrid"), Format::PatternLightGrid}
931 };
932
933 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("fill"))) {
934 reader.readNextStartElement();
935 if (reader.tokenType() == QXmlStreamReader::StartElement) {
936 if (reader.name() == QLatin1String("patternFill")) {
937 const auto& attributes = reader.attributes();
938 if (attributes.hasAttribute(QLatin1String("patternType"))) {
939 const auto& it = patternValues.constFind(attributes.value(QLatin1String("patternType")).toString());
940 fill.setFillPattern(it != patternValues.constEnd() ? it.value() : Format::PatternNone);
941
942 //parse foreground and background colors if they exist
943 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("patternFill"))) {
944 reader.readNextStartElement();
945 if (reader.tokenType() == QXmlStreamReader::StartElement) {
946 if (reader.name() == QLatin1String("fgColor")) {
947 XlsxColor c;
948 if ( c.loadFromXml(reader) )
949 {
950 if (fill.fillPattern() == Format::PatternSolid)
951 fill.setProperty(FormatPrivate::P_Fill_BgColor, c);
952 else
953 fill.setProperty(FormatPrivate::P_Fill_FgColor, c);
954 }
955 } else if (reader.name() == QLatin1String("bgColor")) {
956 XlsxColor c;
957 if ( c.loadFromXml(reader) )
958 {
959 if (fill.fillPattern() == Format::PatternSolid)
960 fill.setProperty(FormatPrivate::P_Fill_FgColor, c);
961 else
962 fill.setProperty(FormatPrivate::P_Fill_BgColor, c);
963 }
964 }
965 }
966 }
967 }
968 }
969 }
970 }
971
972 return true;
973 }
974
readBorders(QXmlStreamReader & reader)975 bool Styles::readBorders(QXmlStreamReader &reader)
976 {
977 Q_ASSERT(reader.name() == QLatin1String("borders"));
978
979 const auto& attributes = reader.attributes();
980 const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
981 const auto count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
982 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
983 && reader.name() == QLatin1String("borders"))) {
984 reader.readNextStartElement();
985 if (reader.tokenType() == QXmlStreamReader::StartElement) {
986 if (reader.name() == QLatin1String("border")) {
987 Format border;
988 readBorder(reader, border);
989 m_bordersList.append(border);
990 m_bordersHash.insert(border.borderKey(), border);
991 if (border.isValid())
992 border.setBorderIndex(m_bordersList.size()-1);
993 }
994 }
995 }
996
997 if (reader.hasError())
998 qWarning()<<reader.errorString();
999
1000 if (hasCount && (count != m_bordersList.size()))
1001 qWarning("error read borders");
1002
1003 return true;
1004 }
1005
readBorder(QXmlStreamReader & reader,Format & border)1006 bool Styles::readBorder(QXmlStreamReader &reader, Format &border)
1007 {
1008 Q_ASSERT(reader.name() == QLatin1String("border"));
1009
1010 const auto& attributes = reader.attributes();
1011 const auto isUp = attributes.hasAttribute(QLatin1String("diagonalUp"));
1012 const auto isDown = attributes.hasAttribute(QLatin1String("diagonalUp"));
1013 if (isUp && isDown)
1014 border.setDiagonalBorderType(Format::DiagnoalBorderBoth);
1015 else if (isUp)
1016 border.setDiagonalBorderType(Format::DiagonalBorderUp);
1017 else if (isDown)
1018 border.setDiagonalBorderType(Format::DiagonalBorderDown);
1019
1020 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("border"))) {
1021 reader.readNextStartElement();
1022 if (reader.tokenType() == QXmlStreamReader::StartElement) {
1023 if (reader.name() == QLatin1String("left") || reader.name() == QLatin1String("right")
1024 || reader.name() == QLatin1String("top") || reader.name() == QLatin1String("bottom")
1025 || reader.name() == QLatin1String("diagonal") ) {
1026 Format::BorderStyle style(Format::BorderNone);
1027 XlsxColor color;
1028 readSubBorder(reader, reader.name().toString(), style, color);
1029
1030 if (reader.name() == QLatin1String("left")) {
1031 border.setLeftBorderStyle(style);
1032 if (!color.isInvalid())
1033 border.setProperty(FormatPrivate::P_Border_LeftColor, color);
1034 } else if (reader.name() == QLatin1String("right")) {
1035 border.setRightBorderStyle(style);
1036 if (!color.isInvalid())
1037 border.setProperty(FormatPrivate::P_Border_RightColor, color);
1038 } else if (reader.name() == QLatin1String("top")) {
1039 border.setTopBorderStyle(style);
1040 if (!color.isInvalid())
1041 border.setProperty(FormatPrivate::P_Border_TopColor, color);
1042 } else if (reader.name() == QLatin1String("bottom")) {
1043 border.setBottomBorderStyle(style);
1044 if (!color.isInvalid())
1045 border.setProperty(FormatPrivate::P_Border_BottomColor, color);
1046 } else if (reader.name() == QLatin1String("diagonal")) {
1047 border.setDiagonalBorderStyle(style);
1048 if (!color.isInvalid())
1049 border.setProperty(FormatPrivate::P_Border_DiagonalColor, color);
1050 }
1051 }
1052 }
1053
1054 if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("border"))
1055 break;
1056 }
1057
1058 return true;
1059 }
1060
readCellStyleXfs(QXmlStreamReader & reader)1061 bool Styles::readCellStyleXfs(QXmlStreamReader &reader)
1062 {
1063 Q_UNUSED(reader);
1064 return true;
1065 }
1066
readSubBorder(QXmlStreamReader & reader,const QString & name,Format::BorderStyle & style,XlsxColor & color)1067 bool Styles::readSubBorder(QXmlStreamReader &reader, const QString &name, Format::BorderStyle &style, XlsxColor &color)
1068 {
1069 Q_ASSERT(reader.name() == name);
1070
1071 static const QMap<QString, Format::BorderStyle> stylesStringsMap = {
1072 {QStringLiteral("none"), Format::BorderNone},
1073 {QStringLiteral("thin"), Format::BorderThin},
1074 {QStringLiteral("medium"), Format::BorderMedium},
1075 {QStringLiteral("dashed"), Format::BorderDashed},
1076 {QStringLiteral("dotted"), Format::BorderDotted},
1077 {QStringLiteral("thick"), Format::BorderThick},
1078 {QStringLiteral("double"), Format::BorderDouble},
1079 {QStringLiteral("hair"), Format::BorderHair},
1080 {QStringLiteral("mediumDashed"), Format::BorderMediumDashed},
1081 {QStringLiteral("dashDot"), Format::BorderDashDot},
1082 {QStringLiteral("mediumDashDot"), Format::BorderMediumDashDot},
1083 {QStringLiteral("dashDotDot"), Format::BorderDashDotDot},
1084 {QStringLiteral("mediumDashDotDot"), Format::BorderMediumDashDotDot},
1085 {QStringLiteral("slantDashDot"), Format::BorderSlantDashDot}
1086 };
1087
1088 const auto& attributes = reader.attributes();
1089 if (attributes.hasAttribute(QLatin1String("style"))) {
1090 QString styleString = attributes.value(QLatin1String("style")).toString();
1091 const auto& it = stylesStringsMap.constFind(styleString);
1092 if (it != stylesStringsMap.constEnd()) {
1093 //get style
1094 style = it.value();
1095 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == name)) {
1096 reader.readNextStartElement();
1097 if (reader.tokenType() == QXmlStreamReader::StartElement) {
1098 if (reader.name() == QLatin1String("color"))
1099 color.loadFromXml(reader);
1100 }
1101 }
1102 }
1103 }
1104
1105 return true;
1106 }
1107
readCellXfs(QXmlStreamReader & reader)1108 bool Styles::readCellXfs(QXmlStreamReader &reader)
1109 {
1110 Q_ASSERT(reader.name() == QLatin1String("cellXfs"));
1111 const auto& attributes = reader.attributes();
1112 const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
1113 const auto count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
1114 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
1115 && reader.name() == QLatin1String("cellXfs"))) {
1116 reader.readNextStartElement();
1117 if (reader.tokenType() == QXmlStreamReader::StartElement) {
1118 if (reader.name() == QLatin1String("xf")) {
1119
1120 Format format;
1121 const auto& xfAttrs = reader.attributes();
1122
1123 // qDebug()<<reader.name()<<reader.tokenString()<<" .........";
1124 // for (int i=0; i<xfAttrs.size(); ++i)
1125 // qDebug()<<"... "<<i<<" "<<xfAttrs[i].name()<<xfAttrs[i].value();
1126
1127 if (xfAttrs.hasAttribute(QLatin1String("numFmtId"))) {
1128 const auto numFmtIndex = xfAttrs.value(QLatin1String("numFmtId")).toString().toInt();
1129 const auto apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyNumberFormat")).toString());
1130 if(apply) {
1131 const auto& it = m_customNumFmtIdMap.constFind(numFmtIndex);
1132 if (it == m_customNumFmtIdMap.constEnd())
1133 format.setNumberFormatIndex(numFmtIndex);
1134 else
1135 format.setNumberFormat(numFmtIndex, it.value()->formatString);
1136 }
1137 }
1138
1139 if (xfAttrs.hasAttribute(QLatin1String("fontId"))) {
1140 const auto fontIndex = xfAttrs.value(QLatin1String("fontId")).toString().toInt();
1141 if (fontIndex >= m_fontsList.size()) {
1142 qDebug("Error read styles.xml, cellXfs fontId");
1143 } else {
1144 const auto apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyFont")).toString());
1145 if(apply) {
1146 Format fontFormat = m_fontsList[fontIndex];
1147 for (int i=FormatPrivate::P_Font_STARTID; i<FormatPrivate::P_Font_ENDID; ++i) {
1148 if (fontFormat.hasProperty(i))
1149 format.setProperty(i, fontFormat.property(i));
1150 }
1151 }
1152 }
1153 }
1154
1155 if (xfAttrs.hasAttribute(QLatin1String("fillId"))) {
1156 const auto id = xfAttrs.value(QLatin1String("fillId")).toString().toInt();
1157 if (id >= m_fillsList.size()) {
1158 qDebug("Error read styles.xml, cellXfs fillId");
1159 } else {
1160
1161 // dev20 branch
1162 // NOTE: MIcrosoft Excel does not have 'applyFill' tag.
1163 //
1164
1165 // bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyFill")).toString());
1166 // if (apply)
1167
1168 {
1169 Format fillFormat = m_fillsList[id];
1170 for (int i=FormatPrivate::P_Fill_STARTID; i<FormatPrivate::P_Fill_ENDID; ++i)
1171 {
1172 if (fillFormat.hasProperty(i))
1173 format.setProperty(i, fillFormat.property(i));
1174 }
1175 }
1176
1177 }
1178 }
1179
1180 if (xfAttrs.hasAttribute(QLatin1String("borderId"))) {
1181 const auto id = xfAttrs.value(QLatin1String("borderId")).toString().toInt();
1182 if (id >= m_bordersList.size()) {
1183 qDebug("Error read styles.xml, cellXfs borderId");
1184 } else {
1185 const auto apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyBorder")).toString());
1186 if(apply) {
1187 Format borderFormat = m_bordersList[id];
1188 for (int i=FormatPrivate::P_Border_STARTID; i<FormatPrivate::P_Border_ENDID; ++i) {
1189 if (borderFormat.hasProperty(i))
1190 format.setProperty(i, borderFormat.property(i));
1191 }
1192 }
1193 }
1194 }
1195
1196 const auto apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyAlignment")).toString());
1197 if(apply) {
1198 reader.readNextStartElement();
1199 if (reader.name() == QLatin1String("alignment")) {
1200 const auto& alignAttrs = reader.attributes();
1201
1202 if (alignAttrs.hasAttribute(QLatin1String("horizontal"))) {
1203 static const QMap<QString, Format::HorizontalAlignment> alignStringMap = {
1204 {QStringLiteral("left"), Format::AlignLeft},
1205 {QStringLiteral("center"), Format::AlignHCenter},
1206 {QStringLiteral("right"), Format::AlignRight},
1207 {QStringLiteral("justify"), Format::AlignHJustify},
1208 {QStringLiteral("centerContinuous"), Format::AlignHMerge},
1209 {QStringLiteral("distributed"), Format::AlignHDistributed}
1210 };
1211
1212 const auto& it = alignStringMap.constFind(alignAttrs.value(QLatin1String("horizontal")).toString());
1213 if (it != alignStringMap.constEnd())
1214 format.setHorizontalAlignment(it.value());
1215 }
1216
1217 if (alignAttrs.hasAttribute(QLatin1String("vertical"))) {
1218 static const QMap<QString, Format::VerticalAlignment> alignStringMap = {
1219 {QStringLiteral("top"), Format::AlignTop},
1220 {QStringLiteral("center"), Format::AlignVCenter},
1221 {QStringLiteral("justify"), Format::AlignVJustify},
1222 {QStringLiteral("distributed"), Format::AlignVDistributed}
1223 };
1224
1225 const auto& it = alignStringMap.constFind(alignAttrs.value(QLatin1String("vertical")).toString());
1226 if (it != alignStringMap.constEnd())
1227 format.setVerticalAlignment(it.value());
1228 }
1229
1230 if (alignAttrs.hasAttribute(QLatin1String("indent"))) {
1231 const auto indent = alignAttrs.value(QLatin1String("indent")).toString().toInt();
1232 format.setIndent(indent);
1233 }
1234
1235 if (alignAttrs.hasAttribute(QLatin1String("textRotation"))) {
1236 const auto rotation = alignAttrs.value(QLatin1String("textRotation")).toString().toInt();
1237 format.setRotation(rotation);
1238 }
1239
1240 if (alignAttrs.hasAttribute(QLatin1String("wrapText")))
1241 format.setTextWrap(true);
1242
1243 if (alignAttrs.hasAttribute(QLatin1String("shrinkToFit")))
1244 format.setShrinkToFit(true);
1245
1246 }
1247 }
1248
1249 addXfFormat(format, true);
1250 }
1251 }
1252 }
1253
1254 if (reader.hasError())
1255 qWarning()<<reader.errorString();
1256
1257 if (hasCount && (count != m_xf_formatsList.size()))
1258 qWarning("error read CellXfs");
1259
1260 return true;
1261 }
1262
readDxfs(QXmlStreamReader & reader)1263 bool Styles::readDxfs(QXmlStreamReader &reader)
1264 {
1265 Q_ASSERT(reader.name() == QLatin1String("dxfs"));
1266 const auto& attributes = reader.attributes();
1267 const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
1268 const auto count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
1269 while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
1270 && reader.name() == QLatin1String("dxfs"))) {
1271 reader.readNextStartElement();
1272 if (reader.tokenType() == QXmlStreamReader::StartElement) {
1273 if (reader.name() == QLatin1String("dxf"))
1274 readDxf(reader);
1275 }
1276 }
1277 if (reader.hasError())
1278 qWarning()<<reader.errorString();
1279
1280 if (hasCount && (count != m_dxf_formatsList.size()))
1281 qWarning("error read dxfs");
1282
1283 return true;
1284 }
1285
readDxf(QXmlStreamReader & reader)1286 bool Styles::readDxf(QXmlStreamReader &reader)
1287 {
1288 Q_ASSERT(reader.name() == QLatin1String("dxf"));
1289 Format format;
1290 while (!reader.atEnd() && !(reader.name() == QLatin1String("dxf") && reader.tokenType() == QXmlStreamReader::EndElement)) {
1291 reader.readNextStartElement();
1292 if (reader.tokenType() == QXmlStreamReader::StartElement) {
1293 if (reader.name() == QLatin1String("numFmt")) {
1294 const auto& attributes = reader.attributes();
1295 const auto id = attributes.value(QLatin1String("numFmtId")).toString().toInt();
1296 QString code = attributes.value(QLatin1String("formatCode")).toString();
1297 format.setNumberFormat(id, code);
1298 } else if (reader.name() == QLatin1String("font")) {
1299 readFont(reader, format);
1300 } else if (reader.name() == QLatin1String("fill")) {
1301 readFill(reader, format);
1302 } else if (reader.name() == QLatin1String("border")) {
1303 readBorder(reader, format);
1304 }
1305 }
1306 }
1307 addDxfFormat(format, true);
1308 return true;
1309 }
1310
readColors(QXmlStreamReader & reader)1311 bool Styles::readColors(QXmlStreamReader &reader)
1312 {
1313 Q_ASSERT(reader.name() == QLatin1String("colors"));
1314 while (!reader.atEnd() && !(reader.name() == QLatin1String("colors") && reader.tokenType() == QXmlStreamReader::EndElement)) {
1315 reader.readNextStartElement();
1316 if (reader.tokenType() == QXmlStreamReader::StartElement) {
1317 if (reader.name() == QLatin1String("indexedColors")) {
1318 readIndexedColors(reader);
1319 } else if (reader.name() == QLatin1String("mruColors")) {
1320
1321 }
1322 }
1323 }
1324 return true;
1325 }
1326
readIndexedColors(QXmlStreamReader & reader)1327 bool Styles::readIndexedColors(QXmlStreamReader &reader)
1328 {
1329 Q_ASSERT(reader.name() == QLatin1String("indexedColors"));
1330 m_indexedColors.clear();
1331 while (!reader.atEnd() && !(reader.name() == QLatin1String("indexedColors") && reader.tokenType() == QXmlStreamReader::EndElement)) {
1332 reader.readNextStartElement();
1333 if (reader.tokenType() == QXmlStreamReader::StartElement) {
1334 if (reader.name() == QLatin1String("rgbColor")) {
1335 const auto& color = reader.attributes().value(QLatin1String("rgb")).toString();
1336 m_indexedColors.append(XlsxColor::fromARGBString(color));
1337 }
1338 }
1339 }
1340 if (!m_indexedColors.isEmpty())
1341 m_isIndexedColorsDefault = false;
1342 return true;
1343 }
1344
loadFromXmlFile(QIODevice * device)1345 bool Styles::loadFromXmlFile(QIODevice *device)
1346 {
1347 QXmlStreamReader reader(device);
1348 while (!reader.atEnd()) {
1349 QXmlStreamReader::TokenType token = reader.readNext();
1350 if (token == QXmlStreamReader::StartElement) {
1351 if (reader.name() == QLatin1String("numFmts")) {
1352 readNumFmts(reader);
1353 } else if (reader.name() == QLatin1String("fonts")) {
1354 readFonts(reader);
1355 } else if (reader.name() == QLatin1String("fills")) {
1356 readFills(reader);
1357 } else if (reader.name() == QLatin1String("borders")) {
1358 readBorders(reader);
1359 } else if (reader.name() == QLatin1String("cellStyleXfs")) {
1360
1361 readCellStyleXfs(reader);
1362
1363 } else if (reader.name() == QLatin1String("cellXfs")) {
1364 readCellXfs(reader);
1365 } else if (reader.name() == QLatin1String("cellStyles")) {
1366
1367 // cellStyles
1368
1369 } else if (reader.name() == QLatin1String("dxfs")) {
1370 readDxfs(reader);
1371 } else if (reader.name() == QLatin1String("colors")) {
1372 readColors(reader);
1373 }
1374 }
1375
1376 if (reader.hasError()) {
1377 qDebug()<<"Error when read style file: "<<reader.errorString();
1378 }
1379 }
1380 return true;
1381 }
1382
1383 #if QT_VERSION >= 0x050600
getColorByIndex(int idx)1384 QColor Styles::getColorByIndex(int idx)
1385 {
1386 // #if QT_VERSION >= 0x050600
1387
1388 if (m_indexedColors.isEmpty()) {
1389 m_indexedColors = {
1390 QColor(QRgba64::fromArgb32(0x000000)), QColor(QRgba64::fromArgb32(0xFFFFFF)), QColor(QRgba64::fromArgb32(0xFF0000)), QColor(QRgba64::fromArgb32(0x00FF00)),
1391 QColor(QRgba64::fromArgb32(0x0000FF)), QColor(QRgba64::fromArgb32(0xFFFF00)), QColor(QRgba64::fromArgb32(0xFF00FF)), QColor(QRgba64::fromArgb32(0x00FFFF)),
1392 QColor(QRgba64::fromArgb32(0x000000)), QColor(QRgba64::fromArgb32(0xFFFFFF)), QColor(QRgba64::fromArgb32(0xFF0000)), QColor(QRgba64::fromArgb32(0x00FF00)),
1393 QColor(QRgba64::fromArgb32(0x0000FF)), QColor(QRgba64::fromArgb32(0xFFFF00)), QColor(QRgba64::fromArgb32(0xFF00FF)), QColor(QRgba64::fromArgb32(0x00FFFF)),
1394 QColor(QRgba64::fromArgb32(0x800000)), QColor(QRgba64::fromArgb32(0x008000)), QColor(QRgba64::fromArgb32(0x000080)), QColor(QRgba64::fromArgb32(0x808000)),
1395 QColor(QRgba64::fromArgb32(0x800080)), QColor(QRgba64::fromArgb32(0x008080)), QColor(QRgba64::fromArgb32(0xC0C0C0)), QColor(QRgba64::fromArgb32(0x808080)),
1396 QColor(QRgba64::fromArgb32(0x9999FF)), QColor(QRgba64::fromArgb32(0x993366)), QColor(QRgba64::fromArgb32(0xFFFFCC)), QColor(QRgba64::fromArgb32(0xCCFFFF)),
1397 QColor(QRgba64::fromArgb32(0x660066)), QColor(QRgba64::fromArgb32(0xFF8080)), QColor(QRgba64::fromArgb32(0x0066CC)), QColor(QRgba64::fromArgb32(0xCCCCFF)),
1398 QColor(QRgba64::fromArgb32(0x000080)), QColor(QRgba64::fromArgb32(0xFF00FF)), QColor(QRgba64::fromArgb32(0xFFFF00)), QColor(QRgba64::fromArgb32(0x00FFFF)),
1399 QColor(QRgba64::fromArgb32(0x800080)), QColor(QRgba64::fromArgb32(0x800000)), QColor(QRgba64::fromArgb32(0x008080)), QColor(QRgba64::fromArgb32(0x0000FF)),
1400 QColor(QRgba64::fromArgb32(0x00CCFF)), QColor(QRgba64::fromArgb32(0xCCFFFF)), QColor(QRgba64::fromArgb32(0xCCFFCC)), QColor(QRgba64::fromArgb32(0xFFFF99)),
1401 QColor(QRgba64::fromArgb32(0x99CCFF)), QColor(QRgba64::fromArgb32(0xFF99CC)), QColor(QRgba64::fromArgb32(0xCC99FF)), QColor(QRgba64::fromArgb32(0xFFCC99)),
1402 QColor(QRgba64::fromArgb32(0x3366FF)), QColor(QRgba64::fromArgb32(0x33CCCC)), QColor(QRgba64::fromArgb32(0x99CC00)), QColor(QRgba64::fromArgb32(0xFFCC00)),
1403 QColor(QRgba64::fromArgb32(0xFF9900)), QColor(QRgba64::fromArgb32(0xFF6600)), QColor(QRgba64::fromArgb32(0x666699)), QColor(QRgba64::fromArgb32(0x969696)),
1404 QColor(QRgba64::fromArgb32(0x003366)), QColor(QRgba64::fromArgb32(0x339966)), QColor(QRgba64::fromArgb32(0x003300)), QColor(QRgba64::fromArgb32(0x333300)),
1405 QColor(QRgba64::fromArgb32(0x993300)), QColor(QRgba64::fromArgb32(0x993366)), QColor(QRgba64::fromArgb32(0x333399)), QColor(QRgba64::fromArgb32(0x333333)),
1406 };
1407 m_isIndexedColorsDefault = true;
1408 }
1409 if (idx < 0 || idx >= m_indexedColors.size())
1410 return QColor();
1411 return m_indexedColors[idx];
1412 }
1413 #endif
1414
1415 QT_END_NAMESPACE_XLSX
1416