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