diff --git a/examples/xlsx/numberformat/main.cpp b/examples/xlsx/numberformat/main.cpp new file mode 100644 index 0000000..967ceb3 --- /dev/null +++ b/examples/xlsx/numberformat/main.cpp @@ -0,0 +1,52 @@ +#include +#include "xlsxdocument.h" +#include "xlsxformat.h" + +int main(int argc, char** argv) +{ + QGuiApplication(argc, argv); + + QXlsx::Document xlsx; + xlsx.setColumn(0, 4, 20.0); + + QXlsx::Format *header = xlsx.createFormat(); + header->setFontBold(true); + header->setFontSize(20); + + //Custom number formats + QStringList numFormats; + numFormats<<"Qt #" + <<"yyyy-mmm-dd" + <<"$ #,##0.00" + <<"[red]0.00"; + xlsx.write(0, 0, "Raw data", header); + xlsx.write(0, 1, "Format", header); + xlsx.write(0, 2, "Shown value", header); + for (int i=0; isetNumberFormat(numFormats[i]); + xlsx.write(row, 2, 100.0, format); + } + + //Builtin number formats + xlsx.addWorksheet(); + xlsx.setColumn(0, 4, 20.0); + xlsx.write(0, 0, "Raw data", header); + xlsx.write(0, 1, "Builtin Format", header); + xlsx.write(0, 2, "Shown value", header); + for (int i=0; i<50; ++i) { + int row = i+1; + int numFmt = i; + xlsx.write(row, 0, 100.0); + xlsx.write(row, 1, numFmt); + QXlsx::Format *format = xlsx.createFormat(); + format->setNumberFormatIndex(numFmt); + xlsx.write(row, 2, 100.0, format); + } + + xlsx.save(); + return 0; +} diff --git a/examples/xlsx/numberformat/numberformat.pro b/examples/xlsx/numberformat/numberformat.pro new file mode 100644 index 0000000..eb3863f --- /dev/null +++ b/examples/xlsx/numberformat/numberformat.pro @@ -0,0 +1,12 @@ +TARGET = mergecells + +#include(../../../src/xlsx/qtxlsx.pri) +QT += xlsx + +TARGET = numberformat +CONFIG += console +CONFIG -= app_bundle + +TEMPLATE = app + +SOURCES += main.cpp diff --git a/examples/xlsx/style/main.cpp b/examples/xlsx/style/main.cpp index 7ce890a..ba1bee1 100644 --- a/examples/xlsx/style/main.cpp +++ b/examples/xlsx/style/main.cpp @@ -43,7 +43,7 @@ int main() xlsx.setColumn(8, 15, 5.0, format4); QXlsx::Format *format5 = xlsx.createFormat(); - format5->setNumberFormat(22); + format5->setNumberFormatIndex(22); xlsx.write("A5", QDate(2013, 8, 29), format5); QXlsx::Format *format6 = xlsx.createFormat(); diff --git a/examples/xlsx/xlsx.pro b/examples/xlsx/xlsx.pro index 05c328b..db7d416 100644 --- a/examples/xlsx/xlsx.pro +++ b/examples/xlsx/xlsx.pro @@ -3,5 +3,6 @@ SUBDIRS = hello style \ documentproperty \ image \ mergecells \ - rowcolumn + rowcolumn \ + numberformat diff --git a/src/xlsx/xlsxformat.cpp b/src/xlsx/xlsxformat.cpp index e19483e..37c9449 100755 --- a/src/xlsx/xlsxformat.cpp +++ b/src/xlsx/xlsxformat.cpp @@ -68,19 +68,50 @@ Format::~Format() delete d_ptr; } -int Format::numberFormat() const +int Format::numberFormatIndex() const { Q_D(const Format); return d->numberData.formatIndex; } -void Format::setNumberFormat(int format) +void Format::setNumberFormatIndex(int format) { Q_D(Format); d->dirty = true; d->numberData.formatIndex = format; } +QString Format::numberFormat() const +{ + Q_D(const Format); + return d->numberData.formatString; +} + +void Format::setNumberFormat(const QString &format) +{ + Q_D(Format); + d->dirty = true; + d->numberData.formatString = format; + d->numberData._valid = false; //formatIndex must be re-generated +} + +bool Format::numFmtIndexValid() const +{ + Q_D(const Format); + return d->numberData._valid; +} + +/*! + * \internal + */ +void Format::setNumFmt(int index, const QString &string) +{ + Q_D(Format); + d->numberData.formatIndex = index; + d->numberData.formatString = string; + d->numberData._valid = true; +} + int Format::fontSize() const { Q_D(const Format); diff --git a/src/xlsx/xlsxformat.h b/src/xlsx/xlsxformat.h index 948701f..548e2c6 100755 --- a/src/xlsx/xlsxformat.h +++ b/src/xlsx/xlsxformat.h @@ -129,8 +129,10 @@ public: ~Format(); - int numberFormat() const; - void setNumberFormat(int format); + int numberFormatIndex() const; + void setNumberFormatIndex(int format); + QString numberFormat() const; + void setNumberFormat(const QString &format); int fontSize() const; void setFontSize(int size); @@ -210,6 +212,9 @@ private: friend class WorksheetPrivate; Format(); + bool numFmtIndexValid() const; + void setNumFmt(int index, const QString &string); + bool fontIndexValid() const; int fontIndex() const; void setFontIndex(int index); diff --git a/src/xlsx/xlsxformat_p.h b/src/xlsx/xlsxformat_p.h index 3adb919..9418a98 100644 --- a/src/xlsx/xlsxformat_p.h +++ b/src/xlsx/xlsxformat_p.h @@ -30,9 +30,12 @@ namespace QXlsx { struct NumberData { - NumberData() : formatIndex(0) {} + NumberData() : formatIndex(0), _valid(true) {} int formatIndex; + QString formatString; + + bool _valid; }; struct FontData diff --git a/src/xlsx/xlsxstyles.cpp b/src/xlsx/xlsxstyles.cpp index 0c57e88..490ce02 100755 --- a/src/xlsx/xlsxstyles.cpp +++ b/src/xlsx/xlsxstyles.cpp @@ -65,6 +65,61 @@ void Styles::addFormat(Format *format) if (!format) return; + //numFmt + if (!format->numFmtIndexValid()) { + 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); + 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(); + //Assign proper number format index + if (m_builtinNumFmtsHash.contains(str)) { + format->setNumFmt(m_builtinNumFmtsHash[str], str); + } else if (m_customNumFmtsHash.contains(str)) { + format->setNumFmt(m_customNumFmtsHash[str], str); + } else { + int idx = 164 + m_customNumFmts.size(); + m_customNumFmts.append(str); + m_customNumFmtsHash.insert(str, idx); + format->setNumFmt(idx, str); + } + } + //Font if (!format->fontIndexValid()) { if (!m_fontsHash.contains(format->fontKey())) { @@ -140,9 +195,7 @@ void Styles::saveToXmlFile(QIODevice *device) writer.writeStartElement(QStringLiteral("styleSheet")); writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main")); -// writer.writeStartElement(QStringLiteral("numFmts")); -// writer.writeEndElement();//numFmts - + writeNumFmts(writer); writeFonts(writer); writeFills(writer); writeBorders(writer); @@ -180,6 +233,17 @@ void Styles::saveToXmlFile(QIODevice *device) writer.writeEndDocument(); } +void Styles::writeNumFmts(XmlStreamWriter &writer) +{ + writer.writeStartElement(QStringLiteral("numFmts")); + for (int i=0; inumberFormat(); + int num_fmt_id = format->numberFormatIndex(); int font_id = format->fontIndex(); int fill_id = format->fillIndex(); int border_id = format->borderIndex(); @@ -405,7 +469,7 @@ void Styles::writeCellXfs(XmlStreamWriter &writer) writer.writeAttribute(QStringLiteral("fillId"), QString::number(fill_id)); writer.writeAttribute(QStringLiteral("borderId"), QString::number(border_id)); writer.writeAttribute(QStringLiteral("xfId"), QString::number(xf_id)); - if (format->numberFormat() > 0) + if (format->numberFormatIndex() > 0) writer.writeAttribute(QStringLiteral("applyNumberFormat"), QStringLiteral("1")); if (format->fontIndex() > 0) writer.writeAttribute(QStringLiteral("applyFont"), QStringLiteral("1")); diff --git a/src/xlsx/xlsxstyles_p.h b/src/xlsx/xlsxstyles_p.h index 49b9d10..91faa67 100755 --- a/src/xlsx/xlsxstyles_p.h +++ b/src/xlsx/xlsxstyles_p.h @@ -29,6 +29,7 @@ #include #include #include +#include class QIODevice; @@ -56,6 +57,7 @@ public: private: friend class Format; + void writeNumFmts(XmlStreamWriter &writer); void writeFonts(XmlStreamWriter &writer); void writeFills(XmlStreamWriter &writer); void writeFill(XmlStreamWriter &writer, FillData *fill); @@ -64,6 +66,9 @@ private: void writeCellXfs(XmlStreamWriter &writer); void writeDxfs(XmlStreamWriter &writer); + QHash m_builtinNumFmtsHash; + QStringList m_customNumFmts; + QHash m_customNumFmtsHash; QList > m_fontsList; //Keep a copy of unique fonts QList > m_fillsList; //Keep a copy of unique fills QList > m_bordersList; //Keep a copy of unique borders diff --git a/tests/auto/styles/tst_stylestest.cpp b/tests/auto/styles/tst_stylestest.cpp index c806753..cd003bc 100644 --- a/tests/auto/styles/tst_stylestest.cpp +++ b/tests/auto/styles/tst_stylestest.cpp @@ -13,6 +13,7 @@ public: private Q_SLOTS: void testEmptyStyle(); void testAddFormat(); + void testAddFormat2(); void testSolidFillBackgroundColor(); }; @@ -42,6 +43,23 @@ void StylesTest::testAddFormat() QVERIFY2(xmlData.contains(""), ""); //Note we have a default one } +void StylesTest::testAddFormat2() +{ + QXlsx::Styles styles; + + QXlsx::Format *format = styles.createFormat(); + format->setNumberFormat("h:mm:ss AM/PM"); //builtin 19 + styles.addFormat(format); + + QCOMPARE(format->numberFormatIndex(), 19); + + QXlsx::Format *format2 = styles.createFormat(); + format2->setNumberFormat("aaaaa h:mm:ss AM/PM"); //custom + styles.addFormat(format2); + + QCOMPARE(format2->numberFormatIndex(), 164); +} + // For a solid fill, Excel reverses the role of foreground and background colours void StylesTest::testSolidFillBackgroundColor() {