view src/xlsx/xlsxstyles.cpp @ 28:6f38bb40a441

Added tag 0.8 for changeset 0129971c7046
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 11 Apr 2016 13:09:28 +0200
parents 93d3106bb9a4
children
line wrap: on
line source
/****************************************************************************
** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
** All right reserved.
**
** Permission is hereby granted, free of charge, to any person obtaining
** a copy of this software and associated documentation files (the
** "Software"), to deal in the Software without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Software, and to
** permit persons to whom the Software is furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be
** included in all copies or substantial portions of the Software.
**
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**
****************************************************************************/
#include "xlsxstyles_p.h"
#include "xlsxformat_p.h"
#include "xlsxutility_p.h"
#include "xlsxcolor_p.h"
#include <QXmlStreamWriter>
#include <QXmlStreamReader>
#include <QFile>
#include <QMap>
#include <QDataStream>
#include <QDebug>
#include <QBuffer>

namespace QXlsx {

/*
  When loading from existing .xlsx file. we should create a clean styles object.
  otherwise, default formats should be added.

*/
Styles::Styles(CreateFlag flag)
    : AbstractOOXmlFile(flag), m_nextCustomNumFmtId(176), m_isIndexedColorsDefault(true)
    , m_emptyFormatAdded(false)
{
    //!Fix me. Should the custom num fmt Id starts with 164 or 176 or others??

    //!Fix me! Where should we put these register code?
    if (QMetaType::type("XlsxColor") == QMetaType::UnknownType) {
        qRegisterMetaType<XlsxColor>("XlsxColor");
        qRegisterMetaTypeStreamOperators<XlsxColor>("XlsxColor");
#if QT_VERSION >= 0x050200
        QMetaType::registerDebugStreamOperator<XlsxColor>();
#endif
    }

    if (flag == F_NewFromScratch) {
        //Add default Format
        Format defaultFmt;
        addXfFormat(defaultFmt);

        //Add another fill format
        Format fillFmt;
        fillFmt.setFillPattern(Format::PatternGray125);
        m_fillsList.append(fillFmt);
        m_fillsHash.insert(fillFmt.fillKey(), fillFmt);
    }
}

Styles::~Styles()
{
}

Format Styles::xfFormat(int idx) const
{
    if (idx <0 || idx >= m_xf_formatsList.size())
        return Format();

    return m_xf_formatsList[idx];
}

Format Styles::dxfFormat(int idx) const
{
    if (idx <0 || idx >= m_dxf_formatsList.size())
        return Format();

    return m_dxf_formatsList[idx];
}

void Styles::fixNumFmt(const Format &format)
{
    if (!format.hasNumFmtData())
        return;

    if (format.hasProperty(FormatPrivate::P_NumFmt_Id)
            && !format.stringProperty(FormatPrivate::P_NumFmt_FormatCode).isEmpty()) {
        return;
    }

    if (m_builtinNumFmtsHash.isEmpty()) {
        m_builtinNumFmtsHash.insert(QStringLiteral("General"), 0);
        m_builtinNumFmtsHash.insert(QStringLiteral("0"), 1);
        m_builtinNumFmtsHash.insert(QStringLiteral("0.00"), 2);
        m_builtinNumFmtsHash.insert(QStringLiteral("#,##0"), 3);
        m_builtinNumFmtsHash.insert(QStringLiteral("#,##0.00"), 4);
//            m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0_);($#,##0)"), 5);
//            m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0_);[Red]($#,##0)"), 6);
//            m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0.00_);($#,##0.00)"), 7);
//            m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0.00_);[Red]($#,##0.00)"), 8);
        m_builtinNumFmtsHash.insert(QStringLiteral("0%"), 9);
        m_builtinNumFmtsHash.insert(QStringLiteral("0.00%"), 10);
        m_builtinNumFmtsHash.insert(QStringLiteral("0.00E+00"), 11);
        m_builtinNumFmtsHash.insert(QStringLiteral("# ?/?"), 12);
        m_builtinNumFmtsHash.insert(QStringLiteral("# ?\?/??"), 13);// Note: "??/" is a c++ trigraph, so escape one "?"
        m_builtinNumFmtsHash.insert(QStringLiteral("m/d/yy"), 14);
        m_builtinNumFmtsHash.insert(QStringLiteral("d-mmm-yy"), 15);
        m_builtinNumFmtsHash.insert(QStringLiteral("d-mmm"), 16);
        m_builtinNumFmtsHash.insert(QStringLiteral("mmm-yy"), 17);
        m_builtinNumFmtsHash.insert(QStringLiteral("h:mm AM/PM"), 18);
        m_builtinNumFmtsHash.insert(QStringLiteral("h:mm:ss AM/PM"), 19);
        m_builtinNumFmtsHash.insert(QStringLiteral("h:mm"), 20);
        m_builtinNumFmtsHash.insert(QStringLiteral("h:mm:ss"), 21);
        m_builtinNumFmtsHash.insert(QStringLiteral("m/d/yy h:mm"), 22);

        m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0_);(#,##0)"), 37);
        m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0_);[Red](#,##0)"), 38);
        m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0.00_);(#,##0.00)"), 39);
        m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0.00_);[Red](#,##0.00)"), 40);
//            m_builtinNumFmtsHash.insert(QStringLiteral("_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(_)"), 41);
//            m_builtinNumFmtsHash.insert(QStringLiteral("_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(_)"), 42);
//            m_builtinNumFmtsHash.insert(QStringLiteral("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(_)"), 43);
//            m_builtinNumFmtsHash.insert(QStringLiteral("_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(_)"), 44);
        m_builtinNumFmtsHash.insert(QStringLiteral("mm:ss"), 45);
        m_builtinNumFmtsHash.insert(QStringLiteral("[h]:mm:ss"), 46);
        m_builtinNumFmtsHash.insert(QStringLiteral("mm:ss.0"), 47);
        m_builtinNumFmtsHash.insert(QStringLiteral("##0.0E+0"), 48);
        m_builtinNumFmtsHash.insert(QStringLiteral("@"), 49);
    }

    const QString str = format.numberFormat();
    if (!str.isEmpty()) {
        //Assign proper number format index
        if (m_builtinNumFmtsHash.contains(str)) {
            const_cast<Format *>(&format)->fixNumberFormat(m_builtinNumFmtsHash[str], str);
        } else if (m_customNumFmtsHash.contains(str)) {
            const_cast<Format *>(&format)->fixNumberFormat(m_customNumFmtsHash[str]->formatIndex, str);
        } else {
            //Assign a new fmt Id.
            const_cast<Format *>(&format)->fixNumberFormat(m_nextCustomNumFmtId, str);

            QSharedPointer<XlsxFormatNumberData> fmt(new XlsxFormatNumberData);
            fmt->formatIndex = m_nextCustomNumFmtId;
            fmt->formatString = str;
            m_customNumFmtIdMap.insert(m_nextCustomNumFmtId, fmt);
            m_customNumFmtsHash.insert(str, fmt);

            m_nextCustomNumFmtId += 1;
        }
    } else {
        int id = format.numberFormatIndex();
        //Assign proper format code, this is needed by dxf format
        if (m_customNumFmtIdMap.contains(id)) {
            const_cast<Format *>(&format)->fixNumberFormat(id, m_customNumFmtIdMap[id]->formatString);
        } else {
            QHashIterator<QString, int> it(m_builtinNumFmtsHash);
            bool find=false;
            while (it.hasNext()) {
                it.next();
                if (it.value() == id) {
                    const_cast<Format *>(&format)->fixNumberFormat(id, it.key());
                    find = true;
                    break;
                }
            }

            if (!find) {
                //Wrong numFmt
                const_cast<Format *>(&format)->fixNumberFormat(id, QStringLiteral("General"));
            }
        }
    }
}

/*
   Assign index to Font/Fill/Border and Format

   When \a force is true, add the format to the format list, even other format has
   the same key have been in.
   This is useful when reading existing .xlsx files which may contains duplicated formats.
*/
void Styles::addXfFormat(const Format &format, bool force)
{
    if (format.isEmpty()) {
        //Try do something for empty Format.
        if (m_emptyFormatAdded && !force)
            return;
        m_emptyFormatAdded = true;
    }

    //numFmt
    if (format.hasNumFmtData() && !format.hasProperty(FormatPrivate::P_NumFmt_Id))
        fixNumFmt(format);

    //Font
    if (format.hasFontData() && !format.fontIndexValid()) {
        //Assign proper font index, if has font data.
        if (!m_fontsHash.contains(format.fontKey()))
            const_cast<Format *>(&format)->setFontIndex(m_fontsList.size());
        else
            const_cast<Format *>(&format)->setFontIndex(m_fontsHash[format.fontKey()].fontIndex());
    }
    if (!m_fontsHash.contains(format.fontKey())) {
        //Still a valid font if the format has no fontData. (All font properties are default)
        m_fontsList.append(format);
        m_fontsHash[format.fontKey()] = format;
    }

    //Fill
    if (format.hasFillData() && !format.fillIndexValid()) {
        //Assign proper fill index, if has fill data.
        if (!m_fillsHash.contains(format.fillKey()))
            const_cast<Format *>(&format)->setFillIndex(m_fillsList.size());
        else
            const_cast<Format *>(&format)->setFillIndex(m_fillsHash[format.fillKey()].fillIndex());
    }
    if (!m_fillsHash.contains(format.fillKey())) {
        //Still a valid fill if the format has no fillData. (All fill properties are default)
        m_fillsList.append(format);
        m_fillsHash[format.fillKey()] = format;
    }

    //Border
    if (format.hasBorderData() && !format.borderIndexValid()) {
        //Assign proper border index, if has border data.
        if (!m_bordersHash.contains(format.borderKey()))
            const_cast<Format *>(&format)->setBorderIndex(m_bordersList.size());
        else
            const_cast<Format *>(&format)->setBorderIndex(m_bordersHash[format.borderKey()].borderIndex());
    }
    if (!m_bordersHash.contains(format.borderKey())) {
        //Still a valid border if the format has no borderData. (All border properties are default)
        m_bordersList.append(format);
        m_bordersHash[format.borderKey()] = format;
    }

    //Format
    if (!format.isEmpty() && !format.xfIndexValid()) {
        if (m_xf_formatsHash.contains(format.formatKey()))
            const_cast<Format *>(&format)->setXfIndex(m_xf_formatsHash[format.formatKey()].xfIndex());
        else
            const_cast<Format *>(&format)->setXfIndex(m_xf_formatsList.size());
    }
    if (!m_xf_formatsHash.contains(format.formatKey()) || force) {
        m_xf_formatsList.append(format);
        m_xf_formatsHash[format.formatKey()] = format;
    }
}

void Styles::addDxfFormat(const Format &format, bool force)
{
    //numFmt
    if (format.hasNumFmtData())
        fixNumFmt(format);

    if (!format.isEmpty() && !format.dxfIndexValid()) {
        if (m_dxf_formatsHash.contains(format.formatKey()))
            const_cast<Format *>(&format)->setDxfIndex(m_dxf_formatsHash[format.formatKey()].dxfIndex());
        else
            const_cast<Format *>(&format)->setDxfIndex(m_dxf_formatsList.size());
    }
    if (!m_dxf_formatsHash.contains(format.formatKey()) || force) {
        m_dxf_formatsList.append(format);
        m_dxf_formatsHash[format.formatKey()] = format;
    }
}

void Styles::saveToXmlFile(QIODevice *device) const
{
    QXmlStreamWriter writer(device);

    writer.writeStartDocument(QStringLiteral("1.0"), true);
    writer.writeStartElement(QStringLiteral("styleSheet"));
    writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));

    writeNumFmts(writer);
    writeFonts(writer);
    writeFills(writer);
    writeBorders(writer);

    writer.writeStartElement(QStringLiteral("cellStyleXfs"));
    writer.writeAttribute(QStringLiteral("count"), QStringLiteral("1"));
    writer.writeStartElement(QStringLiteral("xf"));
    writer.writeAttribute(QStringLiteral("numFmtId"), QStringLiteral("0"));
    writer.writeAttribute(QStringLiteral("fontId"), QStringLiteral("0"));
    writer.writeAttribute(QStringLiteral("fillId"), QStringLiteral("0"));
    writer.writeAttribute(QStringLiteral("borderId"), QStringLiteral("0"));
    writer.writeEndElement();//xf
    writer.writeEndElement();//cellStyleXfs

    writeCellXfs(writer);

    writer.writeStartElement(QStringLiteral("cellStyles"));
    writer.writeAttribute(QStringLiteral("count"), QStringLiteral("1"));
    writer.writeStartElement(QStringLiteral("cellStyle"));
    writer.writeAttribute(QStringLiteral("name"), QStringLiteral("Normal"));
    writer.writeAttribute(QStringLiteral("xfId"), QStringLiteral("0"));
    writer.writeAttribute(QStringLiteral("builtinId"), QStringLiteral("0"));
    writer.writeEndElement();//cellStyle
    writer.writeEndElement();//cellStyles

    writeDxfs(writer);

    writer.writeStartElement(QStringLiteral("tableStyles"));
    writer.writeAttribute(QStringLiteral("count"), QStringLiteral("0"));
    writer.writeAttribute(QStringLiteral("defaultTableStyle"), QStringLiteral("TableStyleMedium9"));
    writer.writeAttribute(QStringLiteral("defaultPivotStyle"), QStringLiteral("PivotStyleLight16"));
    writer.writeEndElement();//tableStyles

    writeColors(writer);

    writer.writeEndElement();//styleSheet
    writer.writeEndDocument();
}

void Styles::writeNumFmts(QXmlStreamWriter &writer) const
{
    if (m_customNumFmtIdMap.size() == 0)
        return;

    writer.writeStartElement(QStringLiteral("numFmts"));
    writer.writeAttribute(QStringLiteral("count"), QString::number(m_customNumFmtIdMap.count()));

    QMapIterator<int, QSharedPointer<XlsxFormatNumberData> > it(m_customNumFmtIdMap);
    while (it.hasNext()) {
        it.next();
        writer.writeEmptyElement(QStringLiteral("numFmt"));
        writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(it.value()->formatIndex));
        writer.writeAttribute(QStringLiteral("formatCode"), it.value()->formatString);
    }
    writer.writeEndElement();//numFmts
}

/*
*/
void Styles::writeFonts(QXmlStreamWriter &writer) const
{
    writer.writeStartElement(QStringLiteral("fonts"));
    writer.writeAttribute(QStringLiteral("count"), QString::number(m_fontsList.count()));
    for (int i=0; i<m_fontsList.size(); ++i)
        writeFont(writer, m_fontsList[i], false);
    writer.writeEndElement();//fonts
}

void Styles::writeFont(QXmlStreamWriter &writer, const Format &format, bool isDxf) const
{
    writer.writeStartElement(QStringLiteral("font"));

    //The condense and extend elements are mainly used in dxf format
    if (format.hasProperty(FormatPrivate::P_Font_Condense)
            && !format.boolProperty(FormatPrivate::P_Font_Condense)) {
        writer.writeEmptyElement(QStringLiteral("condense"));
        writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
    }
    if (format.hasProperty(FormatPrivate::P_Font_Extend)
            && !format.boolProperty(FormatPrivate::P_Font_Extend)) {
        writer.writeEmptyElement(QStringLiteral("extend"));
        writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0"));
    }

    if (format.fontBold())
        writer.writeEmptyElement(QStringLiteral("b"));
    if (format.fontItalic())
        writer.writeEmptyElement(QStringLiteral("i"));
    if (format.fontStrikeOut())
        writer.writeEmptyElement(QStringLiteral("strike"));
    if (format.fontOutline())
        writer.writeEmptyElement(QStringLiteral("outline"));
    if (format.boolProperty(FormatPrivate::P_Font_Shadow))
        writer.writeEmptyElement(QStringLiteral("shadow"));
    if (format.hasProperty(FormatPrivate::P_Font_Underline)) {
        Format::FontUnderline u = format.fontUnderline();
        if (u != Format::FontUnderlineNone) {
            writer.writeEmptyElement(QStringLiteral("u"));
            if (u== Format::FontUnderlineDouble)
                writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double"));
            else if (u == Format::FontUnderlineSingleAccounting)
                writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting"));
            else if (u == Format::FontUnderlineDoubleAccounting)
                writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting"));
        }
    }
    if (format.hasProperty(FormatPrivate::P_Font_Script)) {
        Format::FontScript s = format.fontScript();
        if (s != Format::FontScriptNormal) {
            writer.writeEmptyElement(QStringLiteral("vertAlign"));
            if (s == Format::FontScriptSuper)
                writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript"));
            else
                writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript"));
        }
    }

    if (!isDxf && format.hasProperty(FormatPrivate::P_Font_Size)) {
        writer.writeEmptyElement(QStringLiteral("sz"));
        writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize()));
    }

    if (format.hasProperty(FormatPrivate::P_Font_Color)) {
        XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>();
        color.saveToXml(writer);
    }

    if (!isDxf) {
        if (!format.fontName().isEmpty()) {
            writer.writeEmptyElement(QStringLiteral("name"));
            writer.writeAttribute(QStringLiteral("val"), format.fontName());
        }
        if (format.hasProperty(FormatPrivate::P_Font_Charset)) {
            writer.writeEmptyElement(QStringLiteral("charset"));
            writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Charset)));
        }
        if (format.hasProperty(FormatPrivate::P_Font_Family)) {
            writer.writeEmptyElement(QStringLiteral("family"));
            writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family)));
        }

        if (format.hasProperty(FormatPrivate::P_Font_Scheme)) {
            writer.writeEmptyElement(QStringLiteral("scheme"));
            writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme));
        }
    }
    writer.writeEndElement(); //font
}

void Styles::writeFills(QXmlStreamWriter &writer) const
{
    writer.writeStartElement(QStringLiteral("fills"));
    writer.writeAttribute(QStringLiteral("count"), QString::number(m_fillsList.size()));

    for (int i=0; i<m_fillsList.size(); ++i)
        writeFill(writer, m_fillsList[i]);

    writer.writeEndElement(); //fills
}

void Styles::writeFill(QXmlStreamWriter &writer, const Format &fill, bool isDxf) const
{
    static QMap<int, QString> patternStrings;
    if (patternStrings.isEmpty()) {
        patternStrings[Format::PatternNone] = QStringLiteral("none");
        patternStrings[Format::PatternSolid] = QStringLiteral("solid");
        patternStrings[Format::PatternMediumGray] = QStringLiteral("mediumGray");
        patternStrings[Format::PatternDarkGray] = QStringLiteral("darkGray");
        patternStrings[Format::PatternLightGray] = QStringLiteral("lightGray");
        patternStrings[Format::PatternDarkHorizontal] = QStringLiteral("darkHorizontal");
        patternStrings[Format::PatternDarkVertical] = QStringLiteral("darkVertical");
        patternStrings[Format::PatternDarkDown] = QStringLiteral("darkDown");
        patternStrings[Format::PatternDarkUp] = QStringLiteral("darkUp");
        patternStrings[Format::PatternDarkGrid] = QStringLiteral("darkGrid");
        patternStrings[Format::PatternDarkTrellis] = QStringLiteral("darkTrellis");
        patternStrings[Format::PatternLightHorizontal] = QStringLiteral("lightHorizontal");
        patternStrings[Format::PatternLightVertical] = QStringLiteral("lightVertical");
        patternStrings[Format::PatternLightDown] = QStringLiteral("lightDown");
        patternStrings[Format::PatternLightUp] = QStringLiteral("lightUp");
        patternStrings[Format::PatternLightTrellis] = QStringLiteral("lightTrellis");
        patternStrings[Format::PatternGray125] = QStringLiteral("gray125");
        patternStrings[Format::PatternGray0625] = QStringLiteral("gray0625");
        patternStrings[Format::PatternLightGrid] = QStringLiteral("lightGrid");
    }

    writer.writeStartElement(QStringLiteral("fill"));
    writer.writeStartElement(QStringLiteral("patternFill"));
    Format::FillPattern pattern = fill.fillPattern();
    // For normal fill formats, Excel prefer to outputing the default "none" attribute
    // But for dxf, Excel prefer to omiting the default "none"
    // Though not make any difference, but it make easier to compare origin files with generate files during debug
    if (!(pattern == Format::PatternNone && isDxf))
        writer.writeAttribute(QStringLiteral("patternType"), patternStrings[pattern]);
    // For a solid fill, Excel reverses the role of foreground and background colours
    if (fill.fillPattern() == Format::PatternSolid) {
        if (fill.hasProperty(FormatPrivate::P_Fill_BgColor))
            fill.property(FormatPrivate::P_Fill_BgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("fgColor"));
        if (fill.hasProperty(FormatPrivate::P_Fill_FgColor))
            fill.property(FormatPrivate::P_Fill_FgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("bgColor"));
    } else {
        if (fill.hasProperty(FormatPrivate::P_Fill_FgColor))
            fill.property(FormatPrivate::P_Fill_FgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("fgColor"));
        if (fill.hasProperty(FormatPrivate::P_Fill_BgColor))
            fill.property(FormatPrivate::P_Fill_BgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("bgColor"));
    }
    writer.writeEndElement();//patternFill
    writer.writeEndElement();//fill
}

void Styles::writeBorders(QXmlStreamWriter &writer) const
{
    writer.writeStartElement(QStringLiteral("borders"));
    writer.writeAttribute(QStringLiteral("count"), QString::number(m_bordersList.count()));
    for (int i=0; i<m_bordersList.size(); ++i)
        writeBorder(writer, m_bordersList[i]);
    writer.writeEndElement();//borders
}

void Styles::writeBorder(QXmlStreamWriter &writer, const Format &border, bool isDxf) const
{
    writer.writeStartElement(QStringLiteral("border"));
    if (border.hasProperty(FormatPrivate::P_Border_DiagonalType)) {
        Format::DiagonalBorderType t = border.diagonalBorderType();
        if (t == Format::DiagonalBorderUp) {
            writer.writeAttribute(QStringLiteral("diagonalUp"), QStringLiteral("1"));
        } else if (t == Format::DiagonalBorderDown) {
            writer.writeAttribute(QStringLiteral("diagonalDown"), QStringLiteral("1"));
        } else if (t == Format::DiagnoalBorderBoth) {
            writer.writeAttribute(QStringLiteral("diagonalUp"), QStringLiteral("1"));
            writer.writeAttribute(QStringLiteral("diagonalDown"), QStringLiteral("1"));
        }
    }

    writeSubBorder(writer, QStringLiteral("left"), border.leftBorderStyle(), border.property(FormatPrivate::P_Border_LeftColor).value<XlsxColor>());
    writeSubBorder(writer, QStringLiteral("right"), border.rightBorderStyle(), border.property(FormatPrivate::P_Border_RightColor).value<XlsxColor>());
    writeSubBorder(writer, QStringLiteral("top"), border.topBorderStyle(), border.property(FormatPrivate::P_Border_TopColor).value<XlsxColor>());
    writeSubBorder(writer, QStringLiteral("bottom"), border.bottomBorderStyle(), border.property(FormatPrivate::P_Border_BottomColor).value<XlsxColor>());

    //Condition DXF formats don't allow diagonal style
    if (!isDxf)
        writeSubBorder(writer, QStringLiteral("diagonal"), border.diagonalBorderStyle(), border.property(FormatPrivate::P_Border_DiagonalColor).value<XlsxColor>());

    if (isDxf) {
//        writeSubBorder(wirter, QStringLiteral("vertical"), );
//        writeSubBorder(writer, QStringLiteral("horizontal"), );
    }

    writer.writeEndElement();//border
}

void Styles::writeSubBorder(QXmlStreamWriter &writer, const QString &type, int style, const XlsxColor &color) const
{
    if (style == Format::BorderNone) {
        writer.writeEmptyElement(type);
        return;
    }

    static QMap<int, QString> stylesString;
    if (stylesString.isEmpty()) {
        stylesString[Format::BorderNone] = QStringLiteral("none");
        stylesString[Format::BorderThin] = QStringLiteral("thin");
        stylesString[Format::BorderMedium] = QStringLiteral("medium");
        stylesString[Format::BorderDashed] = QStringLiteral("dashed");
        stylesString[Format::BorderDotted] = QStringLiteral("dotted");
        stylesString[Format::BorderThick] = QStringLiteral("thick");
        stylesString[Format::BorderDouble] = QStringLiteral("double");
        stylesString[Format::BorderHair] = QStringLiteral("hair");
        stylesString[Format::BorderMediumDashed] = QStringLiteral("mediumDashed");
        stylesString[Format::BorderDashDot] = QStringLiteral("dashDot");
        stylesString[Format::BorderMediumDashDot] = QStringLiteral("mediumDashDot");
        stylesString[Format::BorderDashDotDot] = QStringLiteral("dashDotDot");
        stylesString[Format::BorderMediumDashDotDot] = QStringLiteral("mediumDashDotDot");
        stylesString[Format::BorderSlantDashDot] = QStringLiteral("slantDashDot");
    }

    writer.writeStartElement(type);
    writer.writeAttribute(QStringLiteral("style"), stylesString[style]);
    color.saveToXml(writer); //write color element

    writer.writeEndElement();//type
}

void Styles::writeCellXfs(QXmlStreamWriter &writer) const
{
    writer.writeStartElement(QStringLiteral("cellXfs"));
    writer.writeAttribute(QStringLiteral("count"), QString::number(m_xf_formatsList.size()));
    foreach (const Format &format, m_xf_formatsList) {
        int xf_id = 0;
        writer.writeStartElement(QStringLiteral("xf"));
        writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(format.numberFormatIndex()));
        writer.writeAttribute(QStringLiteral("fontId"), QString::number(format.fontIndex()));
        writer.writeAttribute(QStringLiteral("fillId"), QString::number(format.fillIndex()));
        writer.writeAttribute(QStringLiteral("borderId"), QString::number(format.borderIndex()));
        writer.writeAttribute(QStringLiteral("xfId"), QString::number(xf_id));
        if (format.hasNumFmtData())
            writer.writeAttribute(QStringLiteral("applyNumberFormat"), QStringLiteral("1"));
        if (format.hasFontData())
            writer.writeAttribute(QStringLiteral("applyFont"), QStringLiteral("1"));
        if (format.hasFillData())
            writer.writeAttribute(QStringLiteral("applyFill"), QStringLiteral("1"));
        if (format.hasBorderData())
            writer.writeAttribute(QStringLiteral("applyBorder"), QStringLiteral("1"));
        if (format.hasAlignmentData())
            writer.writeAttribute(QStringLiteral("applyAlignment"), QStringLiteral("1"));

        if (format.hasAlignmentData()) {
            writer.writeEmptyElement(QStringLiteral("alignment"));
            if (format.hasProperty(FormatPrivate::P_Alignment_AlignH)) {
                switch (format.horizontalAlignment()) {
                case Format::AlignLeft:
                    writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("left"));
                    break;
                case Format::AlignHCenter:
                    writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("center"));
                    break;
                case Format::AlignRight:
                    writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("right"));
                    break;
                case Format::AlignHFill:
                    writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("fill"));
                    break;
                case Format::AlignHJustify:
                    writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("justify"));
                    break;
                case Format::AlignHMerge:
                    writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("centerContinuous"));
                    break;
                case Format::AlignHDistributed:
                    writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("distributed"));
                    break;
                default:
                    break;
                }
            }

            if (format.hasProperty(FormatPrivate::P_Alignment_AlignV)) {
                switch (format.verticalAlignment()) {
                case Format::AlignTop:
                    writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("top"));
                    break;
                case Format::AlignVCenter:
                    writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("center"));
                    break;
                case Format::AlignVJustify:
                    writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("justify"));
                    break;
                case Format::AlignVDistributed:
                    writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("distributed"));
                    break;
                default:
                    break;
                }
            }
            if (format.hasProperty(FormatPrivate::P_Alignment_Indent))
                writer.writeAttribute(QStringLiteral("indent"), QString::number(format.indent()));
            if (format.hasProperty(FormatPrivate::P_Alignment_Wrap) && format.textWrap())
                writer.writeAttribute(QStringLiteral("wrapText"), QStringLiteral("1"));
            if (format.hasProperty(FormatPrivate::P_Alignment_ShinkToFit) && format.shrinkToFit())
                writer.writeAttribute(QStringLiteral("shrinkToFit"), QStringLiteral("1"));
            if (format.hasProperty(FormatPrivate::P_Alignment_Rotation))
                writer.writeAttribute(QStringLiteral("textRotation"), QString::number(format.rotation()));
        }

        writer.writeEndElement();//xf
    }
    writer.writeEndElement();//cellXfs
}

void Styles::writeDxfs(QXmlStreamWriter &writer) const
{
    writer.writeStartElement(QStringLiteral("dxfs"));
    writer.writeAttribute(QStringLiteral("count"), QString::number(m_dxf_formatsList.size()));
    foreach (const Format &format, m_dxf_formatsList)
        writeDxf(writer, format);
    writer.writeEndElement(); //dxfs
}

void Styles::writeDxf(QXmlStreamWriter &writer, const Format &format) const
{
    writer.writeStartElement(QStringLiteral("dxf"));

    if (format.hasFontData())
        writeFont(writer, format, true);

    if (format.hasNumFmtData()) {
        writer.writeEmptyElement(QStringLiteral("numFmt"));
        writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(format.numberFormatIndex()));
        writer.writeAttribute(QStringLiteral("formatCode"), format.numberFormat());
    }

    if (format.hasFillData())
        writeFill(writer, format, true);

    if (format.hasBorderData())
        writeBorder(writer, format, true);

    writer.writeEndElement();//dxf
}

void Styles::writeColors(QXmlStreamWriter &writer) const
{
    if (m_isIndexedColorsDefault) //Don't output the default indexdeColors
        return;

    writer.writeStartElement(QStringLiteral("colors"));

    writer.writeStartElement(QStringLiteral("indexedColors"));
    foreach(QColor color, m_indexedColors) {
        writer.writeEmptyElement(QStringLiteral("rgbColor"));
        writer.writeAttribute(QStringLiteral("rgb"), XlsxColor::toARGBString(color));
    }

    writer.writeEndElement();//indexedColors

    writer.writeEndElement();//colors
}

bool Styles::readNumFmts(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("numFmts"));
    QXmlStreamAttributes attributes = reader.attributes();
    bool hasCount = attributes.hasAttribute(QLatin1String("count"));
    int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;

    //Read utill we find the numFmts end tag or ....
    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
           && reader.name() == QLatin1String("numFmts"))) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("numFmt")) {
                QXmlStreamAttributes attributes = reader.attributes();
                QSharedPointer<XlsxFormatNumberData> fmt (new XlsxFormatNumberData);
                fmt->formatIndex = attributes.value(QLatin1String("numFmtId")).toString().toInt();
                fmt->formatString = attributes.value(QLatin1String("formatCode")).toString();
                if (fmt->formatIndex >= m_nextCustomNumFmtId)
                    m_nextCustomNumFmtId = fmt->formatIndex + 1;
                m_customNumFmtIdMap.insert(fmt->formatIndex, fmt);
                m_customNumFmtsHash.insert(fmt->formatString, fmt);
            }
        }
    }

    if (reader.hasError())
        qWarning()<<reader.errorString();

    if (hasCount && (count != m_customNumFmtIdMap.size()))
        qWarning("error read custom numFmts");

    return true;
}

bool Styles::readFonts(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("fonts"));
    QXmlStreamAttributes attributes = reader.attributes();
    bool hasCount = attributes.hasAttribute(QLatin1String("count"));
    int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                               && reader.name() == QLatin1String("fonts"))) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("font")) {
                Format format;
                readFont(reader, format);
                m_fontsList.append(format);
                m_fontsHash.insert(format.fontKey(), format);
                if (format.isValid())
                    format.setFontIndex(m_fontsList.size()-1);
            }
        }
    }
    if (reader.hasError())
        qWarning()<<reader.errorString();

    if (hasCount && (count != m_fontsList.size()))
        qWarning("error read fonts");
    return true;
}

bool Styles::readFont(QXmlStreamReader &reader, Format &format)
{
    Q_ASSERT(reader.name() == QLatin1String("font"));
    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                               && reader.name() == QLatin1String("font"))) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            QXmlStreamAttributes attributes = reader.attributes();
            if (reader.name() == QLatin1String("name")) {
                format.setFontName(attributes.value(QLatin1String("val")).toString());
            } else if (reader.name() == QLatin1String("charset")) {
                format.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toString().toInt());
            } else if (reader.name() == QLatin1String("family")) {
                format.setProperty(FormatPrivate::P_Font_Family, attributes.value(QLatin1String("val")).toString().toInt());
            } else if (reader.name() == QLatin1String("b")) {
                format.setFontBold(true);
            } else if (reader.name() == QLatin1String("i")) {
                format.setFontItalic(true);
            } else if (reader.name() == QLatin1String("strike")) {
                format.setFontStrikeOut(true);
            } else if (reader.name() == QLatin1String("outline")) {
                format.setFontOutline(true);
            } else if (reader.name() == QLatin1String("shadow")) {
                format.setProperty(FormatPrivate::P_Font_Shadow, true);
            } else if (reader.name() == QLatin1String("condense")) {
                format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toString().toInt());
            } else if (reader.name() == QLatin1String("extend")) {
                format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toString().toInt());
            } else if (reader.name() == QLatin1String("color")) {
                XlsxColor color;
                color.loadFromXml(reader);
                format.setProperty(FormatPrivate::P_Font_Color, color);
            } else if (reader.name() == QLatin1String("sz")) {
                int sz = attributes.value(QLatin1String("val")).toString().toInt();
                format.setFontSize(sz);
            } else if (reader.name() == QLatin1String("u")) {
                QString value = attributes.value(QLatin1String("val")).toString();
                if (value == QLatin1String("double"))
                    format.setFontUnderline(Format::FontUnderlineDouble);
                else if (value == QLatin1String("doubleAccounting"))
                    format.setFontUnderline(Format::FontUnderlineDoubleAccounting);
                else if (value == QLatin1String("singleAccounting"))
                    format.setFontUnderline(Format::FontUnderlineSingleAccounting);
                else
                    format.setFontUnderline(Format::FontUnderlineSingle);
            } else if (reader.name() == QLatin1String("vertAlign")) {
                QString value = attributes.value(QLatin1String("val")).toString();
                if (value == QLatin1String("superscript"))
                    format.setFontScript(Format::FontScriptSuper);
                else if (value == QLatin1String("subscript"))
                    format.setFontScript(Format::FontScriptSub);
            } else if (reader.name() == QLatin1String("scheme")) {
                format.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString());
            }
        }
    }
    return true;
}

bool Styles::readFills(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("fills"));

    QXmlStreamAttributes attributes = reader.attributes();
    bool hasCount = attributes.hasAttribute(QLatin1String("count"));
    int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                               && reader.name() == QLatin1String("fills"))) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("fill")) {
                Format fill;
                readFill(reader, fill);
                m_fillsList.append(fill);
                m_fillsHash.insert(fill.fillKey(), fill);
                if (fill.isValid())
                    fill.setFillIndex(m_fillsList.size()-1);
            }
        }
    }
    if (reader.hasError())
        qWarning()<<reader.errorString();

    if (hasCount && (count != m_fillsList.size()))
        qWarning("error read fills");
    return true;
}

bool Styles::readFill(QXmlStreamReader &reader, Format &fill)
{
    Q_ASSERT(reader.name() == QLatin1String("fill"));

    static QMap<QString, Format::FillPattern> patternValues;
    if (patternValues.isEmpty()) {
        patternValues[QStringLiteral("none")] = Format::PatternNone;
        patternValues[QStringLiteral("solid")] = Format::PatternSolid;
        patternValues[QStringLiteral("mediumGray")] = Format::PatternMediumGray;
        patternValues[QStringLiteral("darkGray")] = Format::PatternDarkGray;
        patternValues[QStringLiteral("lightGray")] = Format::PatternLightGray;
        patternValues[QStringLiteral("darkHorizontal")] = Format::PatternDarkHorizontal;
        patternValues[QStringLiteral("darkVertical")] = Format::PatternDarkVertical;
        patternValues[QStringLiteral("darkDown")] = Format::PatternDarkDown;
        patternValues[QStringLiteral("darkUp")] = Format::PatternDarkUp;
        patternValues[QStringLiteral("darkGrid")] = Format::PatternDarkGrid;
        patternValues[QStringLiteral("darkTrellis")] = Format::PatternDarkTrellis;
        patternValues[QStringLiteral("lightHorizontal")] = Format::PatternLightHorizontal;
        patternValues[QStringLiteral("lightVertical")] = Format::PatternLightVertical;
        patternValues[QStringLiteral("lightDown")] = Format::PatternLightDown;
        patternValues[QStringLiteral("lightUp")] = Format::PatternLightUp;
        patternValues[QStringLiteral("lightTrellis")] = Format::PatternLightTrellis;
        patternValues[QStringLiteral("gray125")] = Format::PatternGray125;
        patternValues[QStringLiteral("gray0625")] = Format::PatternGray0625;
        patternValues[QStringLiteral("lightGrid")] = Format::PatternLightGrid;
    }

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("fill"))) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("patternFill")) {
                QXmlStreamAttributes attributes = reader.attributes();
                if (attributes.hasAttribute(QLatin1String("patternType"))) {
                    QString pattern = attributes.value(QLatin1String("patternType")).toString();
                    fill.setFillPattern(patternValues.contains(pattern) ? patternValues[pattern] : Format::PatternNone);

                    //parse foreground and background colors if they exist
                    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("patternFill"))) {
                        reader.readNextStartElement();
                        if (reader.tokenType() == QXmlStreamReader::StartElement) {
                            if (reader.name() == QLatin1String("fgColor")) {
                                XlsxColor c;
                                c.loadFromXml(reader);
                                if (fill.fillPattern() == Format::PatternSolid)
                                    fill.setProperty(FormatPrivate::P_Fill_BgColor, c);
                                else
                                    fill.setProperty(FormatPrivate::P_Fill_FgColor, c);
                            } else if (reader.name() == QLatin1String("bgColor")) {
                                XlsxColor c;
                                c.loadFromXml(reader);
                                if (fill.fillPattern() == Format::PatternSolid)
                                    fill.setProperty(FormatPrivate::P_Fill_FgColor, c);
                                else
                                    fill.setProperty(FormatPrivate::P_Fill_BgColor, c);
                            }
                        }
                    }
                }
            }
        }
    }

    return true;
}

bool Styles::readBorders(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("borders"));

    QXmlStreamAttributes attributes = reader.attributes();
    bool hasCount = attributes.hasAttribute(QLatin1String("count"));
    int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                               && reader.name() == QLatin1String("borders"))) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("border")) {
                Format border;
                readBorder(reader, border);
                m_bordersList.append(border);
                m_bordersHash.insert(border.borderKey(), border);
                if (border.isValid())
                    border.setBorderIndex(m_bordersList.size()-1);
            }
        }
    }

    if (reader.hasError())
        qWarning()<<reader.errorString();

    if (hasCount && (count != m_bordersList.size()))
        qWarning("error read borders");

    return true;
}

bool Styles::readBorder(QXmlStreamReader &reader, Format &border)
{
    Q_ASSERT(reader.name() == QLatin1String("border"));

    QXmlStreamAttributes attributes = reader.attributes();
    bool isUp = attributes.hasAttribute(QLatin1String("diagonalUp"));
    bool isDown = attributes.hasAttribute(QLatin1String("diagonalUp"));
    if (isUp && isDown)
        border.setDiagonalBorderType(Format::DiagnoalBorderBoth);
    else if (isUp)
        border.setDiagonalBorderType(Format::DiagonalBorderUp);
    else if (isDown)
        border.setDiagonalBorderType(Format::DiagonalBorderDown);

    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("border"))) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("left") || reader.name() == QLatin1String("right")
                    || reader.name() == QLatin1String("top") || reader.name() == QLatin1String("bottom")
                    || reader.name() == QLatin1String("diagonal") ) {
                Format::BorderStyle style(Format::BorderNone);
                XlsxColor color;
                readSubBorder(reader, reader.name().toString(), style, color);

                if (reader.name() == QLatin1String("left")) {
                    border.setLeftBorderStyle(style);
                    if (!color.isInvalid())
                        border.setProperty(FormatPrivate::P_Border_LeftColor, color);
                } else if (reader.name() == QLatin1String("right")) {
                    border.setRightBorderStyle(style);
                    if (!color.isInvalid())
                        border.setProperty(FormatPrivate::P_Border_RightColor, color);
                } else if (reader.name() == QLatin1String("top")) {
                    border.setTopBorderStyle(style);
                    if (!color.isInvalid())
                        border.setProperty(FormatPrivate::P_Border_TopColor, color);
                } else if (reader.name() == QLatin1String("bottom")) {
                    border.setBottomBorderStyle(style);
                    if (!color.isInvalid())
                        border.setProperty(FormatPrivate::P_Border_BottomColor, color);
                } else if (reader.name() == QLatin1String("diagonal")) {
                    border.setDiagonalBorderStyle(style);
                    if (!color.isInvalid())
                        border.setProperty(FormatPrivate::P_Border_DiagonalColor, color);
                }
            }
        }

        if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("border"))
            break;
    }

    return true;
}

bool Styles::readSubBorder(QXmlStreamReader &reader, const QString &name, Format::BorderStyle &style, XlsxColor &color)
{
    Q_ASSERT(reader.name() == name);

    static QMap<QString, Format::BorderStyle> stylesStringsMap;
    if (stylesStringsMap.isEmpty()) {
        stylesStringsMap[QStringLiteral("none")] = Format::BorderNone;
        stylesStringsMap[QStringLiteral("thin")] = Format::BorderThin;
        stylesStringsMap[QStringLiteral("medium")] = Format::BorderMedium;
        stylesStringsMap[QStringLiteral("dashed")] = Format::BorderDashed;
        stylesStringsMap[QStringLiteral("dotted")] = Format::BorderDotted;
        stylesStringsMap[QStringLiteral("thick")] = Format::BorderThick;
        stylesStringsMap[QStringLiteral("double")] = Format::BorderDouble;
        stylesStringsMap[QStringLiteral("hair")] = Format::BorderHair;
        stylesStringsMap[QStringLiteral("mediumDashed")] = Format::BorderMediumDashed;
        stylesStringsMap[QStringLiteral("dashDot")] = Format::BorderDashDot;
        stylesStringsMap[QStringLiteral("mediumDashDot")] = Format::BorderMediumDashDot;
        stylesStringsMap[QStringLiteral("dashDotDot")] = Format::BorderDashDotDot;
        stylesStringsMap[QStringLiteral("mediumDashDotDot")] = Format::BorderMediumDashDotDot;
        stylesStringsMap[QStringLiteral("slantDashDot")] = Format::BorderSlantDashDot;
    }

    QXmlStreamAttributes attributes = reader.attributes();
    if (attributes.hasAttribute(QLatin1String("style"))) {
        QString styleString = attributes.value(QLatin1String("style")).toString();
        if (stylesStringsMap.contains(styleString)) {
            //get style
            style = stylesStringsMap[styleString];
            while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == name)) {
                reader.readNextStartElement();
                if (reader.tokenType() == QXmlStreamReader::StartElement) {
                    if (reader.name() == QLatin1String("color"))
                        color.loadFromXml(reader);
                }
            }
        }
    }

    return true;
}

bool Styles::readCellXfs(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("cellXfs"));
    QXmlStreamAttributes attributes = reader.attributes();
    bool hasCount = attributes.hasAttribute(QLatin1String("count"));
    int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("cellXfs"))) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("xf")) {

                Format format;
                QXmlStreamAttributes xfAttrs = reader.attributes();

                //        qDebug()<<reader.name()<<reader.tokenString()<<" .........";
                //        for (int i=0; i<xfAttrs.size(); ++i)
                //            qDebug()<<"... "<<i<<" "<<xfAttrs[i].name()<<xfAttrs[i].value();

                if (xfAttrs.hasAttribute(QLatin1String("numFmtId"))) {
                    int numFmtIndex = xfAttrs.value(QLatin1String("numFmtId")).toString().toInt();
                    bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyNumberFormat")).toString());
                    if(apply) {
                        if (!m_customNumFmtIdMap.contains(numFmtIndex))
                            format.setNumberFormatIndex(numFmtIndex);
                        else
                            format.setNumberFormat(numFmtIndex, m_customNumFmtIdMap[numFmtIndex]->formatString);
                    }
                }

                if (xfAttrs.hasAttribute(QLatin1String("fontId"))) {
                    int fontIndex = xfAttrs.value(QLatin1String("fontId")).toString().toInt();
                    if (fontIndex >= m_fontsList.size()) {
                        qDebug("Error read styles.xml, cellXfs fontId");
                    } else {
                        bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyFont")).toString());
                        if(apply) {
                            Format fontFormat = m_fontsList[fontIndex];
                            for (int i=FormatPrivate::P_Font_STARTID; i<FormatPrivate::P_Font_ENDID; ++i) {
                                if (fontFormat.hasProperty(i))
                                    format.setProperty(i, fontFormat.property(i));
                            }
                        }
                    }
                }

                if (xfAttrs.hasAttribute(QLatin1String("fillId"))) {
                    int id = xfAttrs.value(QLatin1String("fillId")).toString().toInt();
                    if (id >= m_fillsList.size()) {
                        qDebug("Error read styles.xml, cellXfs fillId");
                    } else {
                        bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyFill")).toString());
                        if(apply) {
                            Format fillFormat = m_fillsList[id];
                            for (int i=FormatPrivate::P_Fill_STARTID; i<FormatPrivate::P_Fill_ENDID; ++i) {
                                if (fillFormat.hasProperty(i))
                                    format.setProperty(i, fillFormat.property(i));
                            }
                        }
                    }
                }

                if (xfAttrs.hasAttribute(QLatin1String("borderId"))) {
                    int id = xfAttrs.value(QLatin1String("borderId")).toString().toInt();
                    if (id >= m_bordersList.size()) {
                        qDebug("Error read styles.xml, cellXfs borderId");
                    } else {
                        bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyBorder")).toString());
                        if(apply) {
                            Format borderFormat = m_bordersList[id];
                            for (int i=FormatPrivate::P_Border_STARTID; i<FormatPrivate::P_Border_ENDID; ++i) {
                                if (borderFormat.hasProperty(i))
                                    format.setProperty(i, borderFormat.property(i));
                            }
                        }
                    }
                }

                bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyAlignment")).toString());
                if(apply) {
                    reader.readNextStartElement();
                    if (reader.name() == QLatin1String("alignment")) {
                        QXmlStreamAttributes alignAttrs = reader.attributes();

                        if (alignAttrs.hasAttribute(QLatin1String("horizontal"))) {
                            static QMap<QString, Format::HorizontalAlignment> alignStringMap;
                            if (alignStringMap.isEmpty()) {
                                alignStringMap.insert(QStringLiteral("left"), Format::AlignLeft);
                                alignStringMap.insert(QStringLiteral("center"), Format::AlignHCenter);
                                alignStringMap.insert(QStringLiteral("right"), Format::AlignRight);
                                alignStringMap.insert(QStringLiteral("justify"), Format::AlignHJustify);
                                alignStringMap.insert(QStringLiteral("centerContinuous"), Format::AlignHMerge);
                                alignStringMap.insert(QStringLiteral("distributed"), Format::AlignHDistributed);
                            }
                            QString str = alignAttrs.value(QLatin1String("horizontal")).toString();
                            if (alignStringMap.contains(str))
                                format.setHorizontalAlignment(alignStringMap[str]);
                        }

                        if (alignAttrs.hasAttribute(QLatin1String("vertical"))) {
                            static QMap<QString, Format::VerticalAlignment> alignStringMap;
                            if (alignStringMap.isEmpty()) {
                                alignStringMap.insert(QStringLiteral("top"), Format::AlignTop);
                                alignStringMap.insert(QStringLiteral("center"), Format::AlignVCenter);
                                alignStringMap.insert(QStringLiteral("justify"), Format::AlignVJustify);
                                alignStringMap.insert(QStringLiteral("distributed"), Format::AlignVDistributed);
                            }
                            QString str = alignAttrs.value(QLatin1String("vertical")).toString();
                            if (alignStringMap.contains(str))
                                format.setVerticalAlignment(alignStringMap[str]);
                        }

                        if (alignAttrs.hasAttribute(QLatin1String("indent"))) {
                            int indent = alignAttrs.value(QLatin1String("indent")).toString().toInt();
                            format.setIndent(indent);
                        }

                        if (alignAttrs.hasAttribute(QLatin1String("textRotation"))) {
                            int rotation = alignAttrs.value(QLatin1String("textRotation")).toString().toInt();
                            format.setRotation(rotation);
                        }

                        if (alignAttrs.hasAttribute(QLatin1String("wrapText")))
                            format.setTextWarp(true);

                        if (alignAttrs.hasAttribute(QLatin1String("shrinkToFit")))
                            format.setShrinkToFit(true);

                    }
                }

                addXfFormat(format, true);
            }
        }
    }

    if (reader.hasError())
        qWarning()<<reader.errorString();

    if (hasCount && (count != m_xf_formatsList.size()))
        qWarning("error read CellXfs");

    return true;
}

bool Styles::readDxfs(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("dxfs"));
    QXmlStreamAttributes attributes = reader.attributes();
    bool hasCount = attributes.hasAttribute(QLatin1String("count"));
    int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1;
    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
                                && reader.name() == QLatin1String("dxfs"))) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("dxf"))
                readDxf(reader);
        }
    }
    if (reader.hasError())
        qWarning()<<reader.errorString();

    if (hasCount && (count != m_dxf_formatsList.size()))
        qWarning("error read dxfs");

    return true;
}

bool Styles::readDxf(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("dxf"));
    Format format;
    while (!reader.atEnd() && !(reader.name() == QLatin1String("dxf") && reader.tokenType() == QXmlStreamReader::EndElement)) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("numFmt")) {
                QXmlStreamAttributes attributes = reader.attributes();
                int id = attributes.value(QLatin1String("numFmtId")).toString().toInt();
                QString code = attributes.value(QLatin1String("formatCode")).toString();
                format.setNumberFormat(id, code);
            } else if (reader.name() == QLatin1String("font")) {
                readFont(reader, format);
            } else if (reader.name() == QLatin1String("fill")) {
                readFill(reader, format);
            } else if (reader.name() == QLatin1String("border")) {
                readBorder(reader, format);
            }
        }
    }
    addDxfFormat(format, true);
    return true;
}

bool Styles::readColors(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("colors"));
    while (!reader.atEnd() && !(reader.name() == QLatin1String("colors") && reader.tokenType() == QXmlStreamReader::EndElement)) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("indexedColors")) {
                readIndexedColors(reader);
            } else if (reader.name() == QLatin1String("mruColors")) {

            }
        }
    }
    return true;
}

bool Styles::readIndexedColors(QXmlStreamReader &reader)
{
    Q_ASSERT(reader.name() == QLatin1String("indexedColors"));
    m_indexedColors.clear();
    while (!reader.atEnd() && !(reader.name() == QLatin1String("indexedColors") && reader.tokenType() == QXmlStreamReader::EndElement)) {
        reader.readNextStartElement();
        if (reader.tokenType() == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("rgbColor")) {
                QString color = reader.attributes().value(QLatin1String("rgb")).toString();
                m_indexedColors.append(XlsxColor::fromARGBString(color));
            }
        }
    }
    if (!m_indexedColors.isEmpty())
        m_isIndexedColorsDefault = false;
    return true;
}

bool Styles::loadFromXmlFile(QIODevice *device)
{
    QXmlStreamReader reader(device);
    while (!reader.atEnd()) {
        QXmlStreamReader::TokenType token = reader.readNext();
        if (token == QXmlStreamReader::StartElement) {
            if (reader.name() == QLatin1String("numFmts")) {
                readNumFmts(reader);
            } else if (reader.name() == QLatin1String("fonts")) {
                readFonts(reader);
            } else if (reader.name() == QLatin1String("fills")) {
                readFills(reader);
            } else if (reader.name() == QLatin1String("borders")) {
                readBorders(reader);
            } else if (reader.name() == QLatin1String("cellStyleXfs")) {

            } else if (reader.name() == QLatin1String("cellXfs")) {
                readCellXfs(reader);
            } else if (reader.name() == QLatin1String("cellStyles")) {

            } else if (reader.name() == QLatin1String("dxfs")) {
                readDxfs(reader);
            } else if (reader.name() == QLatin1String("colors")) {
                readColors(reader);
            }
        }

        if (reader.hasError()) {
            qDebug()<<"Error when read style file: "<<reader.errorString();
        }
    }
    return true;
}

QColor Styles::getColorByIndex(int idx)
{
    if (m_indexedColors.isEmpty()) {
        m_indexedColors<<QColor("#000000") <<QColor("#FFFFFF") <<QColor("#FF0000") <<QColor("#00FF00")
                      <<QColor("#0000FF") <<QColor("#FFFF00") <<QColor("#FF00FF") <<QColor("#00FFFF")
                     <<QColor("#000000") <<QColor("#FFFFFF") <<QColor("#FF0000") <<QColor("#00FF00")
                    <<QColor("#0000FF") <<QColor("#FFFF00") <<QColor("#FF00FF") <<QColor("#00FFFF")
                   <<QColor("#800000") <<QColor("#008000") <<QColor("#000080") <<QColor("#808000")
                  <<QColor("#800080") <<QColor("#008080") <<QColor("#C0C0C0") <<QColor("#808080")
                 <<QColor("#9999FF") <<QColor("#993366") <<QColor("#FFFFCC") <<QColor("#CCFFFF")
                <<QColor("#660066") <<QColor("#FF8080") <<QColor("#0066CC") <<QColor("#CCCCFF")
               <<QColor("#000080") <<QColor("#FF00FF") <<QColor("#FFFF00") <<QColor("#00FFFF")
              <<QColor("#800080") <<QColor("#800000") <<QColor("#008080") <<QColor("#0000FF")
             <<QColor("#00CCFF") <<QColor("#CCFFFF") <<QColor("#CCFFCC") <<QColor("#FFFF99")
            <<QColor("#99CCFF") <<QColor("#FF99CC") <<QColor("#CC99FF") <<QColor("#FFCC99")
           <<QColor("#3366FF") <<QColor("#33CCCC") <<QColor("#99CC00") <<QColor("#FFCC00")
          <<QColor("#FF9900") <<QColor("#FF6600") <<QColor("#666699") <<QColor("#969696")
         <<QColor("#003366") <<QColor("#339966") <<QColor("#003300") <<QColor("#333300")
        <<QColor("#993300") <<QColor("#993366") <<QColor("#333399") <<QColor("#333333");
        m_isIndexedColorsDefault = true;
    }
    if (idx < 0 || idx >= m_indexedColors.size())
        return QColor();
    return m_indexedColors[idx];
}

} //namespace QXlsx
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)