File indexing completed on 2024-06-16 03:42:28

0001 // xlsxstyles.cpp
0002 
0003 #include <QtGlobal>
0004 #include <QXmlStreamWriter>
0005 #include <QXmlStreamReader>
0006 #include <QFile>
0007 #include <QMap>
0008 #include <QDataStream>
0009 #include <QDebug>
0010 #include <QBuffer>
0011 
0012 #include "xlsxglobal.h"
0013 #include "xlsxstyles_p.h"
0014 #include "xlsxformat_p.h"
0015 #include "xlsxutility_p.h"
0016 #include "xlsxcolor_p.h"
0017 
0018 QT_BEGIN_NAMESPACE_XLSX
0019 
0020 /*
0021   When loading from existing .xlsx file. we should create a clean styles object.
0022   otherwise, default formats should be added.
0023 
0024 */
0025 Styles::Styles(CreateFlag flag)
0026     : AbstractOOXmlFile(flag), m_nextCustomNumFmtId(176), m_isIndexedColorsDefault(true)
0027     , m_emptyFormatAdded(false)
0028 {
0029     //!Fix me. Should the custom num fmt Id starts with 164 or 176 or others??
0030 
0031     //!Fix me! Where should we put these register code?
0032 
0033     // issue #172, #89
0034 #if QT_VERSION >= 0x060000 // Qt 6.0 or over
0035     if (QMetaType::fromName("XlsxColor").isRegistered())
0036 #else
0037     if (QMetaType::type("XlsxColor") == QMetaType::UnknownType)
0038 #endif
0039     {
0040         qRegisterMetaType<XlsxColor>("XlsxColor");
0041 
0042 #if QT_VERSION >= 0x060000
0043         // Qt 6
0044 
0045         ///TODO:
0046 
0047 #else
0048         // Qt 5
0049 
0050         qRegisterMetaTypeStreamOperators<XlsxColor>("XlsxColor");
0051 
0052         QMetaType::registerDebugStreamOperator<XlsxColor>();
0053 
0054 #endif
0055     }
0056 
0057     if (flag == F_NewFromScratch) {
0058         //Add default Format
0059         Format defaultFmt;
0060         addXfFormat(defaultFmt);
0061 
0062         //Add another fill format
0063         Format fillFmt;
0064         fillFmt.setFillPattern(Format::PatternGray125);
0065         m_fillsList.append(fillFmt);
0066         m_fillsHash.insert(fillFmt.fillKey(), fillFmt);
0067     }
0068 }
0069 
0070 Styles::~Styles()
0071 {
0072 }
0073 
0074 Format Styles::xfFormat(int idx) const
0075 {
0076     if (idx <0 || idx >= m_xf_formatsList.size())
0077         return Format();
0078 
0079     return m_xf_formatsList[idx];
0080 }
0081 
0082 Format Styles::dxfFormat(int idx) const
0083 {
0084     if (idx <0 || idx >= m_dxf_formatsList.size())
0085         return Format();
0086 
0087     return m_dxf_formatsList[idx];
0088 }
0089 
0090 // dev74 issue#57
0091 void Styles::fixNumFmt(const Format &format)
0092 {
0093     if (!format.hasNumFmtData())
0094         return;
0095 
0096     if (format.hasProperty(FormatPrivate::P_NumFmt_Id)
0097             && !format.stringProperty(FormatPrivate::P_NumFmt_FormatCode).isEmpty())
0098     {
0099         return;
0100     }
0101 
0102     if ( m_builtinNumFmtsHash.isEmpty() )
0103     {
0104         m_builtinNumFmtsHash.insert(QStringLiteral("General"), 0);
0105         m_builtinNumFmtsHash.insert(QStringLiteral("0"), 1);
0106         m_builtinNumFmtsHash.insert(QStringLiteral("0.00"), 2);
0107         m_builtinNumFmtsHash.insert(QStringLiteral("#,##0"), 3);
0108         m_builtinNumFmtsHash.insert(QStringLiteral("#,##0.00"), 4);
0109 //            m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0_);($#,##0)"), 5);
0110 //            m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0_);[Red]($#,##0)"), 6);
0111 //            m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0.00_);($#,##0.00)"), 7);
0112 //            m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0.00_);[Red]($#,##0.00)"), 8);
0113         m_builtinNumFmtsHash.insert(QStringLiteral("0%"), 9);
0114         m_builtinNumFmtsHash.insert(QStringLiteral("0.00%"), 10);
0115         m_builtinNumFmtsHash.insert(QStringLiteral("0.00E+00"), 11);
0116         m_builtinNumFmtsHash.insert(QStringLiteral("# ?/?"), 12);
0117         m_builtinNumFmtsHash.insert(QStringLiteral("# ?\?/??"), 13);// Note: "??/" is a c++ trigraph, so escape one "?"
0118         m_builtinNumFmtsHash.insert(QStringLiteral("m/d/yy"), 14);
0119         m_builtinNumFmtsHash.insert(QStringLiteral("d-mmm-yy"), 15);
0120         m_builtinNumFmtsHash.insert(QStringLiteral("d-mmm"), 16);
0121         m_builtinNumFmtsHash.insert(QStringLiteral("mmm-yy"), 17);
0122         m_builtinNumFmtsHash.insert(QStringLiteral("h:mm AM/PM"), 18);
0123         m_builtinNumFmtsHash.insert(QStringLiteral("h:mm:ss AM/PM"), 19);
0124         m_builtinNumFmtsHash.insert(QStringLiteral("h:mm"), 20);
0125         m_builtinNumFmtsHash.insert(QStringLiteral("h:mm:ss"), 21);
0126         m_builtinNumFmtsHash.insert(QStringLiteral("m/d/yy h:mm"), 22);
0127 
0128         m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0_);(#,##0)"), 37);
0129         m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0_);[Red](#,##0)"), 38);
0130         m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0.00_);(#,##0.00)"), 39);
0131         m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0.00_);[Red](#,##0.00)"), 40);
0132 //            m_builtinNumFmtsHash.insert(QStringLiteral("_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(_)"), 41);
0133 //            m_builtinNumFmtsHash.insert(QStringLiteral("_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(_)"), 42);
0134 //            m_builtinNumFmtsHash.insert(QStringLiteral("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(_)"), 43);
0135 //            m_builtinNumFmtsHash.insert(QStringLiteral("_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(_)"), 44);
0136         m_builtinNumFmtsHash.insert(QStringLiteral("mm:ss"), 45);
0137         m_builtinNumFmtsHash.insert(QStringLiteral("[h]:mm:ss"), 46);
0138         m_builtinNumFmtsHash.insert(QStringLiteral("mm:ss.0"), 47);
0139         m_builtinNumFmtsHash.insert(QStringLiteral("##0.0E+0"), 48);
0140         m_builtinNumFmtsHash.insert(QStringLiteral("@"), 49);
0141 
0142         // dev74
0143         // m_builtinNumFmtsHash.insert(QStringLiteral("0.####"), 176);
0144 
0145     }
0146 
0147     const auto& str = format.numberFormat();
0148     if (!str.isEmpty())
0149     {
0150         QHash<QString, QSharedPointer<XlsxFormatNumberData> >::ConstIterator cIt;
0151         //Assign proper number format index
0152         const auto& it = m_builtinNumFmtsHash.constFind(str);
0153         if (it != m_builtinNumFmtsHash.constEnd())
0154         {
0155             const_cast<Format *>(&format)->fixNumberFormat(it.value(), str);
0156         }
0157         else if ((cIt = m_customNumFmtsHash.constFind(str)) != m_customNumFmtsHash.constEnd())
0158         {
0159             const_cast<Format *>(&format)->fixNumberFormat(cIt.value()->formatIndex, str);
0160         }
0161         else
0162         {
0163             //Assign a new fmt Id.
0164             const_cast<Format *>(&format)->fixNumberFormat(m_nextCustomNumFmtId, str);
0165 
0166             QSharedPointer<XlsxFormatNumberData> fmt(new XlsxFormatNumberData);
0167             fmt->formatIndex = m_nextCustomNumFmtId;
0168             fmt->formatString = str;
0169             m_customNumFmtIdMap.insert(m_nextCustomNumFmtId, fmt);
0170             m_customNumFmtsHash.insert(str, fmt);
0171 
0172             m_nextCustomNumFmtId += 1;
0173         }
0174     }
0175     else
0176     {
0177         const auto id = format.numberFormatIndex();
0178         //Assign proper format code, this is needed by dxf format
0179         const auto& it = m_customNumFmtIdMap.constFind(id);
0180         if (it != m_customNumFmtIdMap.constEnd())
0181         {
0182             const_cast<Format *>(&format)->fixNumberFormat(id, it.value()->formatString);
0183         }
0184         else
0185         {
0186             bool found = false;
0187             for ( auto&& it = m_builtinNumFmtsHash.constBegin() ; it != m_builtinNumFmtsHash.constEnd() ; ++it )
0188             {
0189                 if (it.value() == id)
0190                 {
0191                     const_cast<Format *>(&format)->fixNumberFormat(id, it.key());
0192                     found = true;
0193                     break;
0194                 }
0195             }
0196 
0197             if (!found)
0198             {
0199                 //Wrong numFmt
0200                 const_cast<Format *>(&format)->fixNumberFormat(id, QStringLiteral("General"));
0201             }
0202         }
0203     }
0204 }
0205 
0206 /*
0207    Assign index to Font/Fill/Border and Format
0208 
0209    When \a force is true, add the format to the format list, even other format has
0210    the same key have been in.
0211    This is useful when reading existing .xlsx files which may contains duplicated formats.
0212 */
0213 void Styles::addXfFormat(const Format &format, bool force)
0214 {
0215     if (format.isEmpty())
0216     {
0217         //Try do something for empty Format.
0218         if (m_emptyFormatAdded && !force)
0219             return;
0220 
0221         m_emptyFormatAdded = true;
0222     }
0223 
0224     //numFmt
0225     if (format.hasNumFmtData() &&
0226             !format.hasProperty(FormatPrivate::P_NumFmt_Id))
0227     {
0228         fixNumFmt(format);
0229     }
0230 
0231     //Font
0232     const auto& fontIt = m_fontsHash.constFind(format.fontKey());
0233     if (format.hasFontData() && !format.fontIndexValid())
0234     {
0235         //Assign proper font index, if has font data.
0236         if (fontIt == m_fontsHash.constEnd())
0237             const_cast<Format *>(&format)->setFontIndex(m_fontsList.size());
0238         else
0239             const_cast<Format *>(&format)->setFontIndex(fontIt->fontIndex());
0240     }
0241     if (fontIt == m_fontsHash.constEnd())
0242     {
0243         //Still a valid font if the format has no fontData. (All font properties are default)
0244         m_fontsList.append(format);
0245         m_fontsHash[format.fontKey()] = format;
0246     }
0247 
0248     //Fill
0249     const auto& fillIt = m_fillsHash.constFind(format.fillKey());
0250     if (format.hasFillData() && !format.fillIndexValid()) {
0251         //Assign proper fill index, if has fill data.
0252         if (fillIt == m_fillsHash.constEnd())
0253             const_cast<Format *>(&format)->setFillIndex(m_fillsList.size());
0254         else
0255             const_cast<Format *>(&format)->setFillIndex(fillIt->fillIndex());
0256     }
0257     if (fillIt == m_fillsHash.constEnd()) {
0258         //Still a valid fill if the format has no fillData. (All fill properties are default)
0259         m_fillsList.append(format);
0260         m_fillsHash[format.fillKey()] = format;
0261     }
0262 
0263     //Border
0264     const auto& borderIt = m_bordersHash.constFind(format.borderKey());
0265     if (format.hasBorderData() && !format.borderIndexValid()) {
0266         //Assign proper border index, if has border data.
0267         if (borderIt == m_bordersHash.constEnd())
0268             const_cast<Format *>(&format)->setBorderIndex(m_bordersList.size());
0269         else
0270             const_cast<Format *>(&format)->setBorderIndex(borderIt->borderIndex());
0271     }
0272     if (borderIt == m_bordersHash.constEnd()) {
0273         //Still a valid border if the format has no borderData. (All border properties are default)
0274         m_bordersList.append(format);
0275         m_bordersHash[format.borderKey()] = format;
0276     }
0277 
0278     //Format
0279     const auto& formatIt = m_xf_formatsHash.constFind(format.formatKey());
0280     if (!format.isEmpty() && !format.xfIndexValid())
0281     {
0282         if (formatIt == m_xf_formatsHash.constEnd())
0283             const_cast<Format *>(&format)->setXfIndex(m_xf_formatsList.size());
0284         else
0285             const_cast<Format *>(&format)->setXfIndex(formatIt->xfIndex());
0286     }
0287 
0288     if (formatIt == m_xf_formatsHash.constEnd() ||
0289             force)
0290     {
0291         m_xf_formatsList.append(format);
0292         m_xf_formatsHash[format.formatKey()] = format;
0293     }
0294 }
0295 
0296 void Styles::addDxfFormat(const Format &format, bool force)
0297 {
0298     //numFmt
0299     if ( format.hasNumFmtData() )
0300     {
0301         fixNumFmt(format);
0302     }
0303 
0304     const auto& formatIt = m_dxf_formatsHash.constFind(format.formatKey());
0305     if ( !format.isEmpty() &&
0306             !format.dxfIndexValid() )
0307     {
0308         if (formatIt ==  m_dxf_formatsHash.constEnd() ) // m_xf_formatsHash.constEnd()) // issue #108
0309         {
0310             const_cast<Format *>(&format)->setDxfIndex( m_dxf_formatsList.size() );
0311         }
0312         else
0313         {
0314             const_cast<Format *>(&format)->setDxfIndex( formatIt->dxfIndex() );
0315         }
0316     }
0317 
0318     if (formatIt == m_xf_formatsHash.constEnd() ||
0319          force )
0320     {
0321         m_dxf_formatsList.append(format);
0322         m_dxf_formatsHash[ format.formatKey() ] = format;
0323     }
0324 }
0325 
0326 void Styles::saveToXmlFile(QIODevice *device) const
0327 {
0328     QXmlStreamWriter writer(device);
0329 
0330     writer.writeStartDocument(QStringLiteral("1.0"), true);
0331     writer.writeStartElement(QStringLiteral("styleSheet"));
0332     writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
0333 
0334     writeNumFmts(writer);
0335     writeFonts(writer);
0336     writeFills(writer);
0337     writeBorders(writer);
0338 
0339     writer.writeStartElement(QStringLiteral("cellStyleXfs"));
0340     writer.writeAttribute(QStringLiteral("count"), QStringLiteral("1"));
0341     writer.writeStartElement(QStringLiteral("xf"));
0342     writer.writeAttribute(QStringLiteral("numFmtId"), QStringLiteral("0"));
0343     writer.writeAttribute(QStringLiteral("fontId"), QStringLiteral("0"));
0344     writer.writeAttribute(QStringLiteral("fillId"), QStringLiteral("0"));
0345     writer.writeAttribute(QStringLiteral("borderId"), QStringLiteral("0"));
0346     writer.writeEndElement();//xf
0347     writer.writeEndElement();//cellStyleXfs
0348 
0349     writeCellXfs(writer);
0350 
0351     writer.writeStartElement(QStringLiteral("cellStyles"));
0352     writer.writeAttribute(QStringLiteral("count"), QStringLiteral("1"));
0353     writer.writeStartElement(QStringLiteral("cellStyle"));
0354     writer.writeAttribute(QStringLiteral("name"), QStringLiteral("Normal"));
0355     writer.writeAttribute(QStringLiteral("xfId"), QStringLiteral("0"));
0356     writer.writeAttribute(QStringLiteral("builtinId"), QStringLiteral("0"));
0357     writer.writeEndElement();//cellStyle
0358     writer.writeEndElement();//cellStyles
0359 
0360     writeDxfs(writer);
0361 
0362     writer.writeStartElement(QStringLiteral("tableStyles"));
0363     writer.writeAttribute(QStringLiteral("count"), QStringLiteral("0"));
0364     writer.writeAttribute(QStringLiteral("defaultTableStyle"), QStringLiteral("TableStyleMedium9"));
0365     writer.writeAttribute(QStringLiteral("defaultPivotStyle"), QStringLiteral("PivotStyleLight16"));
0366     writer.writeEndElement();//tableStyles
0367 
0368     writeColors(writer);
0369 
0370     writer.writeEndElement();//styleSheet
0371     writer.writeEndDocument();
0372 }
0373 
0374 void Styles::writeNumFmts(QXmlStreamWriter &writer) const
0375 {
0376     if (m_customNumFmtIdMap.size() == 0)
0377         return;
0378 
0379     writer.writeStartElement(QStringLiteral("numFmts"));
0380     writer.writeAttribute(QStringLiteral("count"), QString::number(m_customNumFmtIdMap.count()));
0381 
0382     QMapIterator<int, QSharedPointer<XlsxFormatNumberData> > it(m_customNumFmtIdMap);
0383     while (it.hasNext()) {
0384         it.next();
0385         writer.writeEmptyElement(QStringLiteral("numFmt"));
0386         writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(it.value()->formatIndex));
0387         writer.writeAttribute(QStringLiteral("formatCode"), it.value()->formatString);
0388     }
0389     writer.writeEndElement();//numFmts
0390 }
0391 
0392 /*
0393 */
0394 void Styles::writeFonts(QXmlStreamWriter &writer) const
0395 {
0396     writer.writeStartElement(QStringLiteral("fonts"));
0397     writer.writeAttribute(QStringLiteral("count"), QString::number(m_fontsList.count()));
0398     for (const auto &font : m_fontsList) {
0399         writeFont(writer, font, false);
0400     }
0401     writer.writeEndElement();//fonts
0402 }
0403 
0404 void Styles::writeFont(QXmlStreamWriter &writer, const Format &format, bool isDxf) const
0405 {
0406     writer.writeStartElement(QStringLiteral("font"));
0407 
0408     //The condense and extend elements are mainly used in dxf format
0409     if (format.hasProperty(FormatPrivate::P_Font_Condense)
0410             && !format.boolProperty(FormatPrivate::P_Font_Condense)) {
0411         writer.writeEmptyElement(QStringLiteral("condense"));
0412         writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
0413     }
0414     if (format.hasProperty(FormatPrivate::P_Font_Extend)
0415             && !format.boolProperty(FormatPrivate::P_Font_Extend)) {
0416         writer.writeEmptyElement(QStringLiteral("extend"));
0417         writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
0418     }
0419 
0420     if (format.fontBold())
0421         writer.writeEmptyElement(QStringLiteral("b"));
0422     if (format.fontItalic())
0423         writer.writeEmptyElement(QStringLiteral("i"));
0424     if (format.fontStrikeOut())
0425         writer.writeEmptyElement(QStringLiteral("strike"));
0426     if (format.fontOutline())
0427         writer.writeEmptyElement(QStringLiteral("outline"));
0428     if (format.boolProperty(FormatPrivate::P_Font_Shadow))
0429         writer.writeEmptyElement(QStringLiteral("shadow"));
0430     if (format.hasProperty(FormatPrivate::P_Font_Underline)) {
0431         Format::FontUnderline u = format.fontUnderline();
0432         if (u != Format::FontUnderlineNone) {
0433             writer.writeEmptyElement(QStringLiteral("u"));
0434             if (u== Format::FontUnderlineDouble)
0435                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double"));
0436             else if (u == Format::FontUnderlineSingleAccounting)
0437                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting"));
0438             else if (u == Format::FontUnderlineDoubleAccounting)
0439                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting"));
0440         }
0441     }
0442     if (format.hasProperty(FormatPrivate::P_Font_Script)) {
0443         Format::FontScript s = format.fontScript();
0444         if (s != Format::FontScriptNormal) {
0445             writer.writeEmptyElement(QStringLiteral("vertAlign"));
0446             if (s == Format::FontScriptSuper)
0447                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript"));
0448             else
0449                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript"));
0450         }
0451     }
0452 
0453     if (!isDxf && format.hasProperty(FormatPrivate::P_Font_Size)) {
0454         writer.writeEmptyElement(QStringLiteral("sz"));
0455         writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize()));
0456     }
0457 
0458     if (format.hasProperty(FormatPrivate::P_Font_Color)) {
0459         XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>();
0460         color.saveToXml(writer);
0461     }
0462 
0463     if (!isDxf) {
0464         if (!format.fontName().isEmpty()) {
0465             writer.writeEmptyElement(QStringLiteral("name"));
0466             writer.writeAttribute(QStringLiteral("val"), format.fontName());
0467         }
0468         if (format.hasProperty(FormatPrivate::P_Font_Charset)) {
0469             writer.writeEmptyElement(QStringLiteral("charset"));
0470             writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Charset)));
0471         }
0472         if (format.hasProperty(FormatPrivate::P_Font_Family)) {
0473             writer.writeEmptyElement(QStringLiteral("family"));
0474             writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family)));
0475         }
0476 
0477         if (format.hasProperty(FormatPrivate::P_Font_Scheme)) {
0478             writer.writeEmptyElement(QStringLiteral("scheme"));
0479             writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme));
0480         }
0481     }
0482     writer.writeEndElement(); //font
0483 }
0484 
0485 void Styles::writeFills(QXmlStreamWriter &writer) const
0486 {
0487     writer.writeStartElement(QStringLiteral("fills"));
0488     writer.writeAttribute(QStringLiteral("count"), QString::number(m_fillsList.size()));
0489 
0490     for (const auto &fill : m_fillsList) {
0491         writeFill(writer, fill);
0492     }
0493 
0494     writer.writeEndElement(); //fills
0495 }
0496 
0497 void Styles::writeFill(QXmlStreamWriter &writer, const Format &fill, bool isDxf) const
0498 {
0499     static const QMap<int, QString> patternStrings = {
0500         {Format::PatternNone, QStringLiteral("none")},
0501         {Format::PatternSolid, QStringLiteral("solid")},
0502         {Format::PatternMediumGray, QStringLiteral("mediumGray")},
0503         {Format::PatternDarkGray, QStringLiteral("darkGray")},
0504         {Format::PatternLightGray, QStringLiteral("lightGray")},
0505         {Format::PatternDarkHorizontal, QStringLiteral("darkHorizontal")},
0506         {Format::PatternDarkVertical, QStringLiteral("darkVertical")},
0507         {Format::PatternDarkDown, QStringLiteral("darkDown")},
0508         {Format::PatternDarkUp, QStringLiteral("darkUp")},
0509         {Format::PatternDarkGrid, QStringLiteral("darkGrid")},
0510         {Format::PatternDarkTrellis, QStringLiteral("darkTrellis")},
0511         {Format::PatternLightHorizontal, QStringLiteral("lightHorizontal")},
0512         {Format::PatternLightVertical, QStringLiteral("lightVertical")},
0513         {Format::PatternLightDown, QStringLiteral("lightDown")},
0514         {Format::PatternLightUp, QStringLiteral("lightUp")},
0515         {Format::PatternLightTrellis, QStringLiteral("lightTrellis")},
0516         {Format::PatternGray125, QStringLiteral("gray125")},
0517         {Format::PatternGray0625, QStringLiteral("gray0625")},
0518         {Format::PatternLightGrid, QStringLiteral("lightGrid")}
0519     };
0520 
0521     writer.writeStartElement(QStringLiteral("fill"));
0522     writer.writeStartElement(QStringLiteral("patternFill"));
0523     Format::FillPattern pattern = fill.fillPattern();
0524     // For normal fill formats, Excel prefer to outputing the default "none" attribute
0525     // But for dxf, Excel prefer to omiting the default "none"
0526     // Though not make any difference, but it make easier to compare origin files with generate files during debug
0527     if (!(pattern == Format::PatternNone && isDxf))
0528         writer.writeAttribute(QStringLiteral("patternType"), patternStrings[pattern]);
0529     // For a solid fill, Excel reverses the role of foreground and background colours
0530     if (fill.fillPattern() == Format::PatternSolid) {
0531         if (fill.hasProperty(FormatPrivate::P_Fill_BgColor))
0532             fill.property(FormatPrivate::P_Fill_BgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("fgColor"));
0533         if (fill.hasProperty(FormatPrivate::P_Fill_FgColor))
0534             fill.property(FormatPrivate::P_Fill_FgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("bgColor"));
0535     } else {
0536         if (fill.hasProperty(FormatPrivate::P_Fill_FgColor))
0537             fill.property(FormatPrivate::P_Fill_FgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("fgColor"));
0538         if (fill.hasProperty(FormatPrivate::P_Fill_BgColor))
0539             fill.property(FormatPrivate::P_Fill_BgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("bgColor"));
0540     }
0541     writer.writeEndElement();//patternFill
0542     writer.writeEndElement();//fill
0543 }
0544 
0545 void Styles::writeBorders(QXmlStreamWriter &writer) const
0546 {
0547     writer.writeStartElement(QStringLiteral("borders"));
0548     writer.writeAttribute(QStringLiteral("count"), QString::number(m_bordersList.count()));
0549 
0550     for (const auto &border : m_bordersList) {
0551         writeBorder(writer, border);
0552     }
0553 
0554     writer.writeEndElement();//borders
0555 }
0556 
0557 void Styles::writeBorder(QXmlStreamWriter &writer, const Format &border, bool isDxf) const
0558 {
0559     writer.writeStartElement(QStringLiteral("border"));
0560     if (border.hasProperty(FormatPrivate::P_Border_DiagonalType)) {
0561         Format::DiagonalBorderType t = border.diagonalBorderType();
0562         if (t == Format::DiagonalBorderUp) {
0563             writer.writeAttribute(QStringLiteral("diagonalUp"), QStringLiteral("1"));
0564         } else if (t == Format::DiagonalBorderDown) {
0565             writer.writeAttribute(QStringLiteral("diagonalDown"), QStringLiteral("1"));
0566         } else if (t == Format::DiagnoalBorderBoth) {
0567             writer.writeAttribute(QStringLiteral("diagonalUp"), QStringLiteral("1"));
0568             writer.writeAttribute(QStringLiteral("diagonalDown"), QStringLiteral("1"));
0569         }
0570     }
0571 
0572     writeSubBorder(writer, QStringLiteral("left"), border.leftBorderStyle(), border.property(FormatPrivate::P_Border_LeftColor).value<XlsxColor>());
0573     writeSubBorder(writer, QStringLiteral("right"), border.rightBorderStyle(), border.property(FormatPrivate::P_Border_RightColor).value<XlsxColor>());
0574     writeSubBorder(writer, QStringLiteral("top"), border.topBorderStyle(), border.property(FormatPrivate::P_Border_TopColor).value<XlsxColor>());
0575     writeSubBorder(writer, QStringLiteral("bottom"), border.bottomBorderStyle(), border.property(FormatPrivate::P_Border_BottomColor).value<XlsxColor>());
0576 
0577     //Condition DXF formats don't allow diagonal style
0578     if (!isDxf)
0579         writeSubBorder(writer, QStringLiteral("diagonal"), border.diagonalBorderStyle(), border.property(FormatPrivate::P_Border_DiagonalColor).value<XlsxColor>());
0580 
0581     if (isDxf) {
0582 //        writeSubBorder(wirter, QStringLiteral("vertical"), );
0583 //        writeSubBorder(writer, QStringLiteral("horizontal"), );
0584     }
0585 
0586     writer.writeEndElement();//border
0587 }
0588 
0589 void Styles::writeSubBorder(QXmlStreamWriter &writer, const QString &type, int style, const XlsxColor &color) const
0590 {
0591     if (style == Format::BorderNone) {
0592         writer.writeEmptyElement(type);
0593         return;
0594     }
0595 
0596     static const QMap<int, QString> stylesString = {
0597         {Format::BorderNone, QStringLiteral("none")},
0598         {Format::BorderThin, QStringLiteral("thin")},
0599         {Format::BorderMedium, QStringLiteral("medium")},
0600         {Format::BorderDashed, QStringLiteral("dashed")},
0601         {Format::BorderDotted, QStringLiteral("dotted")},
0602         {Format::BorderThick, QStringLiteral("thick")},
0603         {Format::BorderDouble, QStringLiteral("double")},
0604         {Format::BorderHair, QStringLiteral("hair")},
0605         {Format::BorderMediumDashed, QStringLiteral("mediumDashed")},
0606         {Format::BorderDashDot, QStringLiteral("dashDot")},
0607         {Format::BorderMediumDashDot, QStringLiteral("mediumDashDot")},
0608         {Format::BorderDashDotDot, QStringLiteral("dashDotDot")},
0609         {Format::BorderMediumDashDotDot, QStringLiteral("mediumDashDotDot")},
0610         {Format::BorderSlantDashDot, QStringLiteral("slantDashDot")}
0611     };
0612 
0613     writer.writeStartElement(type);
0614     writer.writeAttribute(QStringLiteral("style"), stylesString[style]);
0615     color.saveToXml(writer); //write color element
0616 
0617     writer.writeEndElement();//type
0618 }
0619 
0620 void Styles::writeCellXfs(QXmlStreamWriter &writer) const
0621 {
0622     writer.writeStartElement(QStringLiteral("cellXfs"));
0623     writer.writeAttribute(QStringLiteral("count"), QString::number(m_xf_formatsList.size()));
0624     for (const Format &format : m_xf_formatsList) {
0625         int xf_id = 0;
0626         writer.writeStartElement(QStringLiteral("xf"));
0627         writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(format.numberFormatIndex()));
0628         writer.writeAttribute(QStringLiteral("fontId"), QString::number(format.fontIndex()));
0629         writer.writeAttribute(QStringLiteral("fillId"), QString::number(format.fillIndex()));
0630         writer.writeAttribute(QStringLiteral("borderId"), QString::number(format.borderIndex()));
0631         writer.writeAttribute(QStringLiteral("xfId"), QString::number(xf_id));
0632         if (format.hasNumFmtData())
0633             writer.writeAttribute(QStringLiteral("applyNumberFormat"), QStringLiteral("1"));
0634         if (format.hasFontData())
0635             writer.writeAttribute(QStringLiteral("applyFont"), QStringLiteral("1"));
0636         if (format.hasFillData())
0637             writer.writeAttribute(QStringLiteral("applyFill"), QStringLiteral("1"));
0638         if (format.hasBorderData())
0639             writer.writeAttribute(QStringLiteral("applyBorder"), QStringLiteral("1"));
0640         if (format.hasAlignmentData())
0641             writer.writeAttribute(QStringLiteral("applyAlignment"), QStringLiteral("1"));
0642 
0643         if (format.hasAlignmentData()) {
0644             writer.writeEmptyElement(QStringLiteral("alignment"));
0645             if (format.hasProperty(FormatPrivate::P_Alignment_AlignH)) {
0646                 switch (format.horizontalAlignment()) {
0647                 case Format::AlignLeft:
0648                     writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("left"));
0649                     break;
0650                 case Format::AlignHCenter:
0651                     writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("center"));
0652                     break;
0653                 case Format::AlignRight:
0654                     writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("right"));
0655                     break;
0656                 case Format::AlignHFill:
0657                     writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("fill"));
0658                     break;
0659                 case Format::AlignHJustify:
0660                     writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("justify"));
0661                     break;
0662                 case Format::AlignHMerge:
0663                     writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("centerContinuous"));
0664                     break;
0665                 case Format::AlignHDistributed:
0666                     writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("distributed"));
0667                     break;
0668                 default:
0669                     break;
0670                 }
0671             }
0672 
0673             if (format.hasProperty(FormatPrivate::P_Alignment_AlignV)) {
0674                 switch (format.verticalAlignment()) {
0675                 case Format::AlignTop:
0676                     writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("top"));
0677                     break;
0678                 case Format::AlignVCenter:
0679                     writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("center"));
0680                     break;
0681                 case Format::AlignVJustify:
0682                     writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("justify"));
0683                     break;
0684                 case Format::AlignVDistributed:
0685                     writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("distributed"));
0686                     break;
0687                 default:
0688                     break;
0689                 }
0690             }
0691             if (format.hasProperty(FormatPrivate::P_Alignment_Indent))
0692                 writer.writeAttribute(QStringLiteral("indent"), QString::number(format.indent()));
0693             if (format.hasProperty(FormatPrivate::P_Alignment_Wrap) && format.textWrap())
0694                 writer.writeAttribute(QStringLiteral("wrapText"), QStringLiteral("1"));
0695             if (format.hasProperty(FormatPrivate::P_Alignment_ShinkToFit) && format.shrinkToFit())
0696                 writer.writeAttribute(QStringLiteral("shrinkToFit"), QStringLiteral("1"));
0697             if (format.hasProperty(FormatPrivate::P_Alignment_Rotation))
0698                 writer.writeAttribute(QStringLiteral("textRotation"), QString::number(format.rotation()));
0699         }
0700 
0701         writer.writeEndElement();//xf
0702     }
0703     writer.writeEndElement();//cellXfs
0704 }
0705 
0706 void Styles::writeDxfs(QXmlStreamWriter &writer) const
0707 {
0708     writer.writeStartElement(QStringLiteral("dxfs"));
0709     writer.writeAttribute(QStringLiteral("count"), QString::number(m_dxf_formatsList.size()));
0710     for (const Format &format : m_dxf_formatsList)
0711         writeDxf(writer, format);
0712     writer.writeEndElement(); //dxfs
0713 }
0714 
0715 void Styles::writeDxf(QXmlStreamWriter &writer, const Format &format) const
0716 {
0717     writer.writeStartElement(QStringLiteral("dxf"));
0718 
0719     if (format.hasFontData())
0720         writeFont(writer, format, true);
0721 
0722     if (format.hasNumFmtData()) {
0723         writer.writeEmptyElement(QStringLiteral("numFmt"));
0724         writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(format.numberFormatIndex()));
0725         writer.writeAttribute(QStringLiteral("formatCode"), format.numberFormat());
0726     }
0727 
0728     if (format.hasFillData())
0729         writeFill(writer, format, true);
0730 
0731     if (format.hasBorderData())
0732         writeBorder(writer, format, true);
0733 
0734     writer.writeEndElement();//dxf
0735 }
0736 
0737 void Styles::writeColors(QXmlStreamWriter &writer) const
0738 {
0739     if (m_isIndexedColorsDefault) //Don't output the default indexdeColors
0740         return;
0741 
0742     writer.writeStartElement(QStringLiteral("colors"));
0743 
0744     writer.writeStartElement(QStringLiteral("indexedColors"));
0745     for (const QColor &color : m_indexedColors) {
0746         writer.writeEmptyElement(QStringLiteral("rgbColor"));
0747         writer.writeAttribute(QStringLiteral("rgb"), XlsxColor::toARGBString(color));
0748     }
0749 
0750     writer.writeEndElement();//indexedColors
0751 
0752     writer.writeEndElement();//colors
0753 }
0754 
0755 bool Styles::readNumFmts(QXmlStreamReader &reader)
0756 {
0757     Q_ASSERT(reader.name() == QLatin1String("numFmts"));
0758     const auto& attributes = reader.attributes();
0759     const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
0760     const auto count = hasCount ? attributes.value(QLatin1String("count")).toInt() : -1;
0761 
0762     //Read utill we find the numFmts end tag or ....
0763     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0764            && reader.name() == QLatin1String("numFmts"))) {
0765         reader.readNextStartElement();
0766         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0767             if (reader.name() == QLatin1String("numFmt")) {
0768                 const auto& attributes = reader.attributes();
0769                 QSharedPointer<XlsxFormatNumberData> fmt (new XlsxFormatNumberData);
0770                 fmt->formatIndex = attributes.value(QLatin1String("numFmtId")).toInt();
0771                 fmt->formatString = attributes.value(QLatin1String("formatCode")).toString();
0772                 if (fmt->formatIndex >= m_nextCustomNumFmtId)
0773                     m_nextCustomNumFmtId = fmt->formatIndex + 1;
0774                 m_customNumFmtIdMap.insert(fmt->formatIndex, fmt);
0775                 m_customNumFmtsHash.insert(fmt->formatString, fmt);
0776             }
0777         }
0778     }
0779 
0780     if (reader.hasError())
0781         qWarning()<<reader.errorString();
0782 
0783     if (hasCount && (count != m_customNumFmtIdMap.size()))
0784         qWarning("error read custom numFmts");
0785 
0786     return true;
0787 }
0788 
0789 bool Styles::readFonts(QXmlStreamReader &reader)
0790 {
0791     Q_ASSERT(reader.name() == QLatin1String("fonts"));
0792     const auto& attributes = reader.attributes();
0793     const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
0794     const auto count = hasCount ? attributes.value(QLatin1String("count")).toInt() : -1;
0795     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0796                                && reader.name() == QLatin1String("fonts"))) {
0797         reader.readNextStartElement();
0798         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0799             if (reader.name() == QLatin1String("font")) {
0800                 Format format;
0801                 readFont(reader, format);
0802                 m_fontsList.append(format);
0803                 m_fontsHash.insert(format.fontKey(), format);
0804                 if (format.isValid())
0805                     format.setFontIndex(m_fontsList.size()-1);
0806             }
0807         }
0808     }
0809     if (reader.hasError())
0810         qWarning()<<reader.errorString();
0811 
0812     if (hasCount && (count != m_fontsList.size()))
0813         qWarning("error read fonts");
0814     return true;
0815 }
0816 
0817 bool Styles::readFont(QXmlStreamReader &reader, Format &format)
0818 {
0819     Q_ASSERT(reader.name() == QLatin1String("font"));
0820     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0821                                && reader.name() == QLatin1String("font"))) {
0822         reader.readNextStartElement();
0823         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0824             const auto& attributes = reader.attributes();
0825             if (reader.name() == QLatin1String("name")) {
0826                 format.setFontName(attributes.value(QLatin1String("val")).toString());
0827             } else if (reader.name() == QLatin1String("charset")) {
0828                 format.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toInt());
0829             } else if (reader.name() == QLatin1String("family")) {
0830                 format.setProperty(FormatPrivate::P_Font_Family, attributes.value(QLatin1String("val")).toInt());
0831             } else if (reader.name() == QLatin1String("b")) {
0832                 format.setFontBold(true);
0833             } else if (reader.name() == QLatin1String("i")) {
0834                 format.setFontItalic(true);
0835             } else if (reader.name() == QLatin1String("strike")) {
0836                 format.setFontStrikeOut(true);
0837             } else if (reader.name() == QLatin1String("outline")) {
0838                 format.setFontOutline(true);
0839             } else if (reader.name() == QLatin1String("shadow")) {
0840                 format.setProperty(FormatPrivate::P_Font_Shadow, true);
0841             } else if (reader.name() == QLatin1String("condense")) {
0842                 format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toInt());
0843             } else if (reader.name() == QLatin1String("extend")) {
0844                 format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toInt());
0845             } else if (reader.name() == QLatin1String("color")) {
0846                 XlsxColor color;
0847                 color.loadFromXml(reader);
0848                 format.setProperty(FormatPrivate::P_Font_Color, color);
0849             } else if (reader.name() == QLatin1String("sz")) {
0850                 const auto sz = attributes.value(QLatin1String("val")).toInt();
0851                 format.setFontSize(sz);
0852             } else if (reader.name() == QLatin1String("u")) {
0853                 QString value = attributes.value(QLatin1String("val")).toString();
0854                 if (value == QLatin1String("double"))
0855                     format.setFontUnderline(Format::FontUnderlineDouble);
0856                 else if (value == QLatin1String("doubleAccounting"))
0857                     format.setFontUnderline(Format::FontUnderlineDoubleAccounting);
0858                 else if (value == QLatin1String("singleAccounting"))
0859                     format.setFontUnderline(Format::FontUnderlineSingleAccounting);
0860                 else
0861                     format.setFontUnderline(Format::FontUnderlineSingle);
0862             } else if (reader.name() == QLatin1String("vertAlign")) {
0863                 QString value = attributes.value(QLatin1String("val")).toString();
0864                 if (value == QLatin1String("superscript"))
0865                     format.setFontScript(Format::FontScriptSuper);
0866                 else if (value == QLatin1String("subscript"))
0867                     format.setFontScript(Format::FontScriptSub);
0868             } else if (reader.name() == QLatin1String("scheme")) {
0869                 format.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString());
0870             }
0871         }
0872     }
0873     return true;
0874 }
0875 
0876 bool Styles::readFills(QXmlStreamReader &reader)
0877 {
0878     Q_ASSERT(reader.name() == QLatin1String("fills"));
0879 
0880     const auto& attributes = reader.attributes();
0881     const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
0882     const auto count = hasCount ? attributes.value(QLatin1String("count")).toInt() : -1;
0883     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0884                                && reader.name() == QLatin1String("fills"))) {
0885         reader.readNextStartElement();
0886         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0887             if (reader.name() == QLatin1String("fill")) {
0888                 Format fill;
0889                 readFill(reader, fill);
0890                 m_fillsList.append(fill);
0891                 m_fillsHash.insert(fill.fillKey(), fill);
0892                 if (fill.isValid())
0893                     fill.setFillIndex(m_fillsList.size()-1);
0894             }
0895         }
0896     }
0897     if (reader.hasError())
0898         qWarning()<<reader.errorString();
0899 
0900     if (hasCount && (count != m_fillsList.size()))
0901         qWarning("error read fills");
0902     return true;
0903 }
0904 
0905 bool Styles::readFill(QXmlStreamReader &reader, Format &fill)
0906 {
0907     Q_ASSERT(reader.name() == QLatin1String("fill"));
0908 
0909     static const QMap<QString, Format::FillPattern> patternValues = {
0910         {QStringLiteral("none"), Format::PatternNone},
0911         {QStringLiteral("solid"), Format::PatternSolid},
0912         {QStringLiteral("mediumGray"), Format::PatternMediumGray},
0913         {QStringLiteral("darkGray"), Format::PatternDarkGray},
0914         {QStringLiteral("lightGray"), Format::PatternLightGray},
0915         {QStringLiteral("darkHorizontal"), Format::PatternDarkHorizontal},
0916         {QStringLiteral("darkVertical"), Format::PatternDarkVertical},
0917         {QStringLiteral("darkDown"), Format::PatternDarkDown},
0918         {QStringLiteral("darkUp"), Format::PatternDarkUp},
0919         {QStringLiteral("darkGrid"), Format::PatternDarkGrid},
0920         {QStringLiteral("darkTrellis"), Format::PatternDarkTrellis},
0921         {QStringLiteral("lightHorizontal"), Format::PatternLightHorizontal},
0922         {QStringLiteral("lightVertical"), Format::PatternLightVertical},
0923         {QStringLiteral("lightDown"), Format::PatternLightDown},
0924         {QStringLiteral("lightUp"), Format::PatternLightUp},
0925         {QStringLiteral("lightTrellis"), Format::PatternLightTrellis},
0926         {QStringLiteral("gray125"), Format::PatternGray125},
0927         {QStringLiteral("gray0625"), Format::PatternGray0625},
0928         {QStringLiteral("lightGrid"), Format::PatternLightGrid}
0929     };
0930 
0931     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("fill"))) {
0932         reader.readNextStartElement();
0933         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0934             if (reader.name() == QLatin1String("patternFill")) {
0935                 const auto& attributes = reader.attributes();
0936                 if (attributes.hasAttribute(QLatin1String("patternType"))) {
0937                     const auto& it = patternValues.constFind(attributes.value(QLatin1String("patternType")).toString());
0938                     fill.setFillPattern(it != patternValues.constEnd() ? it.value() : Format::PatternNone);
0939 
0940                     //parse foreground and background colors if they exist
0941                     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("patternFill"))) {
0942                         reader.readNextStartElement();
0943                         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0944                             if (reader.name() == QLatin1String("fgColor")) {
0945                                 XlsxColor c;
0946                                 if ( c.loadFromXml(reader) )
0947                                 {
0948                                     if (fill.fillPattern() == Format::PatternSolid)
0949                                         fill.setProperty(FormatPrivate::P_Fill_BgColor, c);
0950                                     else
0951                                         fill.setProperty(FormatPrivate::P_Fill_FgColor, c);
0952                                 }
0953                             } else if (reader.name() == QLatin1String("bgColor")) {
0954                                 XlsxColor c;
0955                                 if ( c.loadFromXml(reader) )
0956                                 {
0957                                     if (fill.fillPattern() == Format::PatternSolid)
0958                                         fill.setProperty(FormatPrivate::P_Fill_FgColor, c);
0959                                     else
0960                                         fill.setProperty(FormatPrivate::P_Fill_BgColor, c);
0961                                 }
0962                             }
0963                         }
0964                     }
0965                 }
0966             }
0967         }
0968     }
0969 
0970     return true;
0971 }
0972 
0973 bool Styles::readBorders(QXmlStreamReader &reader)
0974 {
0975     Q_ASSERT(reader.name() == QLatin1String("borders"));
0976 
0977     const auto& attributes = reader.attributes();
0978     const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
0979     const auto count = hasCount ? attributes.value(QLatin1String("count")).toInt() : -1;
0980     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
0981                                && reader.name() == QLatin1String("borders"))) {
0982         reader.readNextStartElement();
0983         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0984             if (reader.name() == QLatin1String("border")) {
0985                 Format border;
0986                 readBorder(reader, border);
0987                 m_bordersList.append(border);
0988                 m_bordersHash.insert(border.borderKey(), border);
0989                 if (border.isValid())
0990                     border.setBorderIndex(m_bordersList.size()-1);
0991             }
0992         }
0993     }
0994 
0995     if (reader.hasError())
0996         qWarning()<<reader.errorString();
0997 
0998     if (hasCount && (count != m_bordersList.size()))
0999         qWarning("error read borders");
1000 
1001     return true;
1002 }
1003 
1004 bool Styles::readBorder(QXmlStreamReader &reader, Format &border)
1005 {
1006     Q_ASSERT(reader.name() == QLatin1String("border"));
1007 
1008     const auto& attributes = reader.attributes();
1009     const auto isUp = attributes.hasAttribute(QLatin1String("diagonalUp"));
1010     const auto isDown = attributes.hasAttribute(QLatin1String("diagonalDown"));
1011     if (isUp && isDown)
1012         border.setDiagonalBorderType(Format::DiagnoalBorderBoth);
1013     else if (isUp)
1014         border.setDiagonalBorderType(Format::DiagonalBorderUp);
1015     else if (isDown)
1016         border.setDiagonalBorderType(Format::DiagonalBorderDown);
1017 
1018     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("border"))) {
1019         reader.readNextStartElement();
1020         if (reader.tokenType() == QXmlStreamReader::StartElement) {
1021             if (reader.name() == QLatin1String("left") || reader.name() == QLatin1String("right")
1022                     || reader.name() == QLatin1String("top") || reader.name() == QLatin1String("bottom")
1023                     || reader.name() == QLatin1String("diagonal") ) {
1024                 Format::BorderStyle style(Format::BorderNone);
1025                 XlsxColor color;
1026                 readSubBorder(reader, reader.name().toString(), style, color);
1027 
1028                 if (reader.name() == QLatin1String("left")) {
1029                     border.setLeftBorderStyle(style);
1030                     if (!color.isInvalid())
1031                         border.setProperty(FormatPrivate::P_Border_LeftColor, color);
1032                 } else if (reader.name() == QLatin1String("right")) {
1033                     border.setRightBorderStyle(style);
1034                     if (!color.isInvalid())
1035                         border.setProperty(FormatPrivate::P_Border_RightColor, color);
1036                 } else if (reader.name() == QLatin1String("top")) {
1037                     border.setTopBorderStyle(style);
1038                     if (!color.isInvalid())
1039                         border.setProperty(FormatPrivate::P_Border_TopColor, color);
1040                 } else if (reader.name() == QLatin1String("bottom")) {
1041                     border.setBottomBorderStyle(style);
1042                     if (!color.isInvalid())
1043                         border.setProperty(FormatPrivate::P_Border_BottomColor, color);
1044                 } else if (reader.name() == QLatin1String("diagonal")) {
1045                     border.setDiagonalBorderStyle(style);
1046                     if (!color.isInvalid())
1047                         border.setProperty(FormatPrivate::P_Border_DiagonalColor, color);
1048                 }
1049             }
1050         }
1051 
1052         if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("border"))
1053             break;
1054     }
1055 
1056     return true;
1057 }
1058 
1059 bool Styles::readCellStyleXfs(QXmlStreamReader &reader)
1060 {
1061     Q_UNUSED(reader);
1062     return true;
1063 }
1064 
1065 bool Styles::readSubBorder(QXmlStreamReader &reader, const QString &name, Format::BorderStyle &style, XlsxColor &color)
1066 {
1067     Q_ASSERT(reader.name() == name);
1068 
1069     static const QMap<QString, Format::BorderStyle> stylesStringsMap = {
1070         {QStringLiteral("none"), Format::BorderNone},
1071         {QStringLiteral("thin"), Format::BorderThin},
1072         {QStringLiteral("medium"), Format::BorderMedium},
1073         {QStringLiteral("dashed"), Format::BorderDashed},
1074         {QStringLiteral("dotted"), Format::BorderDotted},
1075         {QStringLiteral("thick"), Format::BorderThick},
1076         {QStringLiteral("double"), Format::BorderDouble},
1077         {QStringLiteral("hair"), Format::BorderHair},
1078         {QStringLiteral("mediumDashed"), Format::BorderMediumDashed},
1079         {QStringLiteral("dashDot"), Format::BorderDashDot},
1080         {QStringLiteral("mediumDashDot"), Format::BorderMediumDashDot},
1081         {QStringLiteral("dashDotDot"), Format::BorderDashDotDot},
1082         {QStringLiteral("mediumDashDotDot"), Format::BorderMediumDashDotDot},
1083         {QStringLiteral("slantDashDot"), Format::BorderSlantDashDot}
1084     };
1085 
1086     const auto& attributes = reader.attributes();
1087     if (attributes.hasAttribute(QLatin1String("style"))) {
1088         QString styleString = attributes.value(QLatin1String("style")).toString();
1089         const auto& it = stylesStringsMap.constFind(styleString);
1090         if (it != stylesStringsMap.constEnd()) {
1091             //get style
1092             style = it.value();
1093             while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == name)) {
1094                 reader.readNextStartElement();
1095                 if (reader.tokenType() == QXmlStreamReader::StartElement) {
1096                     if (reader.name() == QLatin1String("color"))
1097                         color.loadFromXml(reader);
1098                 }
1099             }
1100         }
1101     }
1102 
1103     return true;
1104 }
1105 
1106 bool Styles::readCellXfs(QXmlStreamReader &reader)
1107 {
1108     Q_ASSERT(reader.name() == QLatin1String("cellXfs"));
1109     const auto& attributes = reader.attributes();
1110     const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
1111     const auto count = hasCount ? attributes.value(QLatin1String("count")).toInt() : -1;
1112     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
1113                                 && reader.name() == QLatin1String("cellXfs"))) {
1114         reader.readNextStartElement();
1115         if (reader.tokenType() == QXmlStreamReader::StartElement) {
1116             if (reader.name() == QLatin1String("xf")) {
1117 
1118                 Format format;
1119                 const auto& xfAttrs = reader.attributes();
1120 
1121                 //        qDebug()<<reader.name()<<reader.tokenString()<<" .........";
1122                 //        for (int i=0; i<xfAttrs.size(); ++i)
1123                 //            qDebug()<<"... "<<i<<" "<<xfAttrs[i].name()<<xfAttrs[i].value();
1124 
1125                 if (xfAttrs.hasAttribute(QLatin1String("numFmtId"))) {
1126                     const auto numFmtIndex = xfAttrs.value(QLatin1String("numFmtId")).toInt();
1127                     const auto apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyNumberFormat")).toString());
1128                     if(apply) {
1129                         const auto& it = m_customNumFmtIdMap.constFind(numFmtIndex);
1130                         if (it == m_customNumFmtIdMap.constEnd())
1131                             format.setNumberFormatIndex(numFmtIndex);
1132                         else
1133                             format.setNumberFormat(numFmtIndex, it.value()->formatString);
1134                     }
1135                 }
1136 
1137                 if (xfAttrs.hasAttribute(QLatin1String("fontId"))) {
1138                     const auto fontIndex = xfAttrs.value(QLatin1String("fontId")).toInt();
1139                     if (fontIndex >= m_fontsList.size()) {
1140                         qDebug("Error read styles.xml, cellXfs fontId");
1141                     } else {
1142                         const auto apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyFont")).toString());
1143                         if(apply) {
1144                             Format fontFormat = m_fontsList[fontIndex];
1145                             for (int i=FormatPrivate::P_Font_STARTID; i<FormatPrivate::P_Font_ENDID; ++i) {
1146                                 if (fontFormat.hasProperty(i))
1147                                     format.setProperty(i, fontFormat.property(i));
1148                             }
1149                         }
1150                     }
1151                 }
1152 
1153                 if (xfAttrs.hasAttribute(QLatin1String("fillId"))) {
1154                     const auto id = xfAttrs.value(QLatin1String("fillId")).toInt();
1155                     if (id >= m_fillsList.size()) {
1156                         qDebug("Error read styles.xml, cellXfs fillId");
1157                     } else {
1158 
1159                         // dev20 branch
1160                         // NOTE: MIcrosoft Excel does not have 'applyFill' tag.
1161                         //
1162 
1163                         // bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyFill")).toString());
1164                         // if (apply)
1165 
1166                         {
1167                             Format fillFormat = m_fillsList[id];
1168                             for (int i=FormatPrivate::P_Fill_STARTID; i<FormatPrivate::P_Fill_ENDID; ++i)
1169                             {
1170                                 if (fillFormat.hasProperty(i))
1171                                     format.setProperty(i, fillFormat.property(i));
1172                             }
1173                         }
1174 
1175                     }
1176                 }
1177 
1178                 if (xfAttrs.hasAttribute(QLatin1String("borderId"))) {
1179                     const auto id = xfAttrs.value(QLatin1String("borderId")).toInt();
1180                     if (id >= m_bordersList.size()) {
1181                         qDebug("Error read styles.xml, cellXfs borderId");
1182                     } else {
1183                         const auto apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyBorder")).toString());
1184                         if(apply) {
1185                             Format borderFormat = m_bordersList[id];
1186                             for (int i=FormatPrivate::P_Border_STARTID; i<FormatPrivate::P_Border_ENDID; ++i) {
1187                                 if (borderFormat.hasProperty(i))
1188                                     format.setProperty(i, borderFormat.property(i));
1189                             }
1190                         }
1191                     }
1192                 }
1193 
1194                 const auto apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyAlignment")).toString());
1195                 if(apply) {
1196                     reader.readNextStartElement();
1197                     if (reader.name() == QLatin1String("alignment")) {
1198                         const auto& alignAttrs = reader.attributes();
1199 
1200                         if (alignAttrs.hasAttribute(QLatin1String("horizontal"))) {
1201                             static const QMap<QString, Format::HorizontalAlignment> alignStringMap = {
1202                                 {QStringLiteral("left"), Format::AlignLeft},
1203                                 {QStringLiteral("center"), Format::AlignHCenter},
1204                                 {QStringLiteral("right"), Format::AlignRight},
1205                                 {QStringLiteral("justify"), Format::AlignHJustify},
1206                                 {QStringLiteral("centerContinuous"), Format::AlignHMerge},
1207                                 {QStringLiteral("distributed"), Format::AlignHDistributed}
1208                             };
1209 
1210                             const auto& it = alignStringMap.constFind(alignAttrs.value(QLatin1String("horizontal")).toString());
1211                             if (it != alignStringMap.constEnd())
1212                                 format.setHorizontalAlignment(it.value());
1213                         }
1214 
1215                         if (alignAttrs.hasAttribute(QLatin1String("vertical"))) {
1216                             static const QMap<QString, Format::VerticalAlignment> alignStringMap = {
1217                                 {QStringLiteral("top"), Format::AlignTop},
1218                                 {QStringLiteral("center"), Format::AlignVCenter},
1219                                 {QStringLiteral("justify"), Format::AlignVJustify},
1220                                 {QStringLiteral("distributed"), Format::AlignVDistributed}
1221                             };
1222 
1223                             const auto& it = alignStringMap.constFind(alignAttrs.value(QLatin1String("vertical")).toString());
1224                             if (it != alignStringMap.constEnd())
1225                                 format.setVerticalAlignment(it.value());
1226                         }
1227 
1228                         if (alignAttrs.hasAttribute(QLatin1String("indent"))) {
1229                             const auto indent = alignAttrs.value(QLatin1String("indent")).toInt();
1230                             format.setIndent(indent);
1231                         }
1232 
1233                         if (alignAttrs.hasAttribute(QLatin1String("textRotation"))) {
1234                             const auto rotation = alignAttrs.value(QLatin1String("textRotation")).toInt();
1235                             format.setRotation(rotation);
1236                         }
1237 
1238                         if (alignAttrs.hasAttribute(QLatin1String("wrapText")))
1239                             format.setTextWrap(true);
1240 
1241                         if (alignAttrs.hasAttribute(QLatin1String("shrinkToFit")))
1242                             format.setShrinkToFit(true);
1243 
1244                     }
1245                 }
1246 
1247                 addXfFormat(format, true);
1248             }
1249         }
1250     }
1251 
1252     if (reader.hasError())
1253         qWarning()<<reader.errorString();
1254 
1255     if (hasCount && (count != m_xf_formatsList.size()))
1256         qWarning("error read CellXfs");
1257 
1258     return true;
1259 }
1260 
1261 bool Styles::readDxfs(QXmlStreamReader &reader)
1262 {
1263     Q_ASSERT(reader.name() == QLatin1String("dxfs"));
1264     const auto& attributes = reader.attributes();
1265     const auto hasCount = attributes.hasAttribute(QLatin1String("count"));
1266     const auto count = hasCount ? attributes.value(QLatin1String("count")).toInt() : -1;
1267     while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
1268                                 && reader.name() == QLatin1String("dxfs"))) {
1269         reader.readNextStartElement();
1270         if (reader.tokenType() == QXmlStreamReader::StartElement) {
1271             if (reader.name() == QLatin1String("dxf"))
1272                 readDxf(reader);
1273         }
1274     }
1275     if (reader.hasError())
1276         qWarning()<<reader.errorString();
1277 
1278     if (hasCount && (count != m_dxf_formatsList.size()))
1279         qWarning("error read dxfs");
1280 
1281     return true;
1282 }
1283 
1284 bool Styles::readDxf(QXmlStreamReader &reader)
1285 {
1286     Q_ASSERT(reader.name() == QLatin1String("dxf"));
1287     Format format;
1288     while (!reader.atEnd() && !(reader.name() == QLatin1String("dxf") && reader.tokenType() == QXmlStreamReader::EndElement)) {
1289         reader.readNextStartElement();
1290         if (reader.tokenType() == QXmlStreamReader::StartElement) {
1291             if (reader.name() == QLatin1String("numFmt")) {
1292                 const auto& attributes = reader.attributes();
1293                 const auto id = attributes.value(QLatin1String("numFmtId")).toInt();
1294                 QString code = attributes.value(QLatin1String("formatCode")).toString();
1295                 format.setNumberFormat(id, code);
1296             } else if (reader.name() == QLatin1String("font")) {
1297                 readFont(reader, format);
1298             } else if (reader.name() == QLatin1String("fill")) {
1299                 readFill(reader, format);
1300             } else if (reader.name() == QLatin1String("border")) {
1301                 readBorder(reader, format);
1302             }
1303         }
1304     }
1305     addDxfFormat(format, true);
1306     return true;
1307 }
1308 
1309 bool Styles::readColors(QXmlStreamReader &reader)
1310 {
1311     Q_ASSERT(reader.name() == QLatin1String("colors"));
1312     while (!reader.atEnd() && !(reader.name() == QLatin1String("colors") && reader.tokenType() == QXmlStreamReader::EndElement)) {
1313         reader.readNextStartElement();
1314         if (reader.tokenType() == QXmlStreamReader::StartElement) {
1315             if (reader.name() == QLatin1String("indexedColors")) {
1316                 readIndexedColors(reader);
1317             } else if (reader.name() == QLatin1String("mruColors")) {
1318 
1319             }
1320         }
1321     }
1322     return true;
1323 }
1324 
1325 bool Styles::readIndexedColors(QXmlStreamReader &reader)
1326 {
1327     Q_ASSERT(reader.name() == QLatin1String("indexedColors"));
1328     m_indexedColors.clear();
1329     while (!reader.atEnd() && !(reader.name() == QLatin1String("indexedColors") && reader.tokenType() == QXmlStreamReader::EndElement)) {
1330         reader.readNextStartElement();
1331         if (reader.tokenType() == QXmlStreamReader::StartElement) {
1332             if (reader.name() == QLatin1String("rgbColor")) {
1333                 const auto& color = reader.attributes().value(QLatin1String("rgb")).toString();
1334                 m_indexedColors.append(XlsxColor::fromARGBString(color));
1335             }
1336         }
1337     }
1338     if (!m_indexedColors.isEmpty())
1339         m_isIndexedColorsDefault = false;
1340     return true;
1341 }
1342 
1343 bool Styles::loadFromXmlFile(QIODevice *device)
1344 {
1345     QXmlStreamReader reader(device);
1346     while (!reader.atEnd()) {
1347         QXmlStreamReader::TokenType token = reader.readNext();
1348         if (token == QXmlStreamReader::StartElement) {
1349             if (reader.name() == QLatin1String("numFmts")) {
1350                 readNumFmts(reader);
1351             } else if (reader.name() == QLatin1String("fonts")) {
1352                 readFonts(reader);
1353             } else if (reader.name() == QLatin1String("fills")) {
1354                 readFills(reader);
1355             } else if (reader.name() == QLatin1String("borders")) {
1356                 readBorders(reader);
1357             } else if (reader.name() == QLatin1String("cellStyleXfs")) {
1358 
1359                 readCellStyleXfs(reader);
1360 
1361             } else if (reader.name() == QLatin1String("cellXfs")) {
1362                 readCellXfs(reader);
1363             } else if (reader.name() == QLatin1String("cellStyles")) {
1364 
1365                 // cellStyles
1366 
1367             } else if (reader.name() == QLatin1String("dxfs")) {
1368                 readDxfs(reader);
1369             } else if (reader.name() == QLatin1String("colors")) {
1370                 readColors(reader);
1371             }
1372         }
1373 
1374         if (reader.hasError()) {
1375             qDebug()<<"Error when read style file: "<<reader.errorString();
1376         }
1377     }
1378     return true;
1379 }
1380 
1381 QColor Styles::getColorByIndex(int idx)
1382 {
1383     if (m_indexedColors.isEmpty()) {
1384         m_indexedColors = {
1385             QColor(QRgba64::fromArgb32(0x000000)), QColor(QRgba64::fromArgb32(0xFFFFFF)), QColor(QRgba64::fromArgb32(0xFF0000)), QColor(QRgba64::fromArgb32(0x00FF00)),
1386             QColor(QRgba64::fromArgb32(0x0000FF)), QColor(QRgba64::fromArgb32(0xFFFF00)), QColor(QRgba64::fromArgb32(0xFF00FF)), QColor(QRgba64::fromArgb32(0x00FFFF)),
1387             QColor(QRgba64::fromArgb32(0x000000)), QColor(QRgba64::fromArgb32(0xFFFFFF)), QColor(QRgba64::fromArgb32(0xFF0000)), QColor(QRgba64::fromArgb32(0x00FF00)),
1388             QColor(QRgba64::fromArgb32(0x0000FF)), QColor(QRgba64::fromArgb32(0xFFFF00)), QColor(QRgba64::fromArgb32(0xFF00FF)), QColor(QRgba64::fromArgb32(0x00FFFF)),
1389             QColor(QRgba64::fromArgb32(0x800000)), QColor(QRgba64::fromArgb32(0x008000)), QColor(QRgba64::fromArgb32(0x000080)), QColor(QRgba64::fromArgb32(0x808000)),
1390             QColor(QRgba64::fromArgb32(0x800080)), QColor(QRgba64::fromArgb32(0x008080)), QColor(QRgba64::fromArgb32(0xC0C0C0)), QColor(QRgba64::fromArgb32(0x808080)),
1391             QColor(QRgba64::fromArgb32(0x9999FF)), QColor(QRgba64::fromArgb32(0x993366)), QColor(QRgba64::fromArgb32(0xFFFFCC)), QColor(QRgba64::fromArgb32(0xCCFFFF)),
1392             QColor(QRgba64::fromArgb32(0x660066)), QColor(QRgba64::fromArgb32(0xFF8080)), QColor(QRgba64::fromArgb32(0x0066CC)), QColor(QRgba64::fromArgb32(0xCCCCFF)),
1393             QColor(QRgba64::fromArgb32(0x000080)), QColor(QRgba64::fromArgb32(0xFF00FF)), QColor(QRgba64::fromArgb32(0xFFFF00)), QColor(QRgba64::fromArgb32(0x00FFFF)),
1394             QColor(QRgba64::fromArgb32(0x800080)), QColor(QRgba64::fromArgb32(0x800000)), QColor(QRgba64::fromArgb32(0x008080)), QColor(QRgba64::fromArgb32(0x0000FF)),
1395             QColor(QRgba64::fromArgb32(0x00CCFF)), QColor(QRgba64::fromArgb32(0xCCFFFF)), QColor(QRgba64::fromArgb32(0xCCFFCC)), QColor(QRgba64::fromArgb32(0xFFFF99)),
1396             QColor(QRgba64::fromArgb32(0x99CCFF)), QColor(QRgba64::fromArgb32(0xFF99CC)), QColor(QRgba64::fromArgb32(0xCC99FF)), QColor(QRgba64::fromArgb32(0xFFCC99)),
1397             QColor(QRgba64::fromArgb32(0x3366FF)), QColor(QRgba64::fromArgb32(0x33CCCC)), QColor(QRgba64::fromArgb32(0x99CC00)), QColor(QRgba64::fromArgb32(0xFFCC00)),
1398             QColor(QRgba64::fromArgb32(0xFF9900)), QColor(QRgba64::fromArgb32(0xFF6600)), QColor(QRgba64::fromArgb32(0x666699)), QColor(QRgba64::fromArgb32(0x969696)),
1399             QColor(QRgba64::fromArgb32(0x003366)), QColor(QRgba64::fromArgb32(0x339966)), QColor(QRgba64::fromArgb32(0x003300)), QColor(QRgba64::fromArgb32(0x333300)),
1400             QColor(QRgba64::fromArgb32(0x993300)), QColor(QRgba64::fromArgb32(0x993366)), QColor(QRgba64::fromArgb32(0x333399)), QColor(QRgba64::fromArgb32(0x333333)),
1401         };
1402         m_isIndexedColorsDefault = true;
1403     }
1404     if (idx < 0 || idx >= m_indexedColors.size())
1405         return QColor();
1406     return m_indexedColors[idx];
1407 }
1408 
1409 QT_END_NAMESPACE_XLSX