Can read .xlsx files with fill styles now
This commit is contained in:
@@ -12,6 +12,7 @@ int main()
|
|||||||
QXlsx::Format *format = xlsx.createFormat();
|
QXlsx::Format *format = xlsx.createFormat();
|
||||||
format->setFontColor(QColor(Qt::blue));
|
format->setFontColor(QColor(Qt::blue));
|
||||||
format->setFontSize(15);
|
format->setFontSize(15);
|
||||||
|
format->setPatternBackgroundColor(QColor(Qt::gray));
|
||||||
xlsx.write("A1", "Hello Qt!", format);
|
xlsx.write("A1", "Hello Qt!", format);
|
||||||
xlsx.write("A2", 500);
|
xlsx.write("A2", 500);
|
||||||
xlsx.saveAs("first.xlsx");
|
xlsx.saveAs("first.xlsx");
|
||||||
|
|||||||
+90
-6
@@ -26,6 +26,7 @@
|
|||||||
#include "xlsxxmlwriter_p.h"
|
#include "xlsxxmlwriter_p.h"
|
||||||
#include "xlsxxmlreader_p.h"
|
#include "xlsxxmlreader_p.h"
|
||||||
#include "xlsxformat_p.h"
|
#include "xlsxformat_p.h"
|
||||||
|
#include "xlsxutility_p.h"
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
@@ -560,7 +561,7 @@ bool Styles::readFonts(XmlStreamReader &reader)
|
|||||||
if (reader.name() != QLatin1String("font"))
|
if (reader.name() != QLatin1String("font"))
|
||||||
return false;
|
return false;
|
||||||
QSharedPointer<FontData> font(new FontData);
|
QSharedPointer<FontData> font(new FontData);
|
||||||
while(reader.readNextStartElement()) {
|
while((reader.readNextStartElement(),true)) { //read until font endelement.
|
||||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||||
if (reader.name() == QLatin1String("b")) {
|
if (reader.name() == QLatin1String("b")) {
|
||||||
font->bold = true;
|
font->bold = true;
|
||||||
@@ -596,11 +597,7 @@ bool Styles::readFonts(XmlStreamReader &reader)
|
|||||||
QXmlStreamAttributes attributes = reader.attributes();
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
if (attributes.hasAttribute(QLatin1String("rgb"))) {
|
if (attributes.hasAttribute(QLatin1String("rgb"))) {
|
||||||
QString colorString = attributes.value(QLatin1String("rgb")).toString();
|
QString colorString = attributes.value(QLatin1String("rgb")).toString();
|
||||||
if (colorString.length() == 8) {
|
font->color = fromARGBString(colorString);
|
||||||
font->color.setRed(colorString.mid(2,2).toInt(0, 16));
|
|
||||||
font->color.setGreen(colorString.mid(4,2).toInt(0, 16));
|
|
||||||
font->color.setBlue(colorString.mid(6,2).toInt(0, 16));
|
|
||||||
}
|
|
||||||
} else if (attributes.hasAttribute(QLatin1String("indexed"))) {
|
} else if (attributes.hasAttribute(QLatin1String("indexed"))) {
|
||||||
|
|
||||||
} else if (attributes.hasAttribute(QLatin1String("theme"))) {
|
} else if (attributes.hasAttribute(QLatin1String("theme"))) {
|
||||||
@@ -627,8 +624,82 @@ bool Styles::readFonts(XmlStreamReader &reader)
|
|||||||
|
|
||||||
bool Styles::readFills(XmlStreamReader &reader)
|
bool Styles::readFills(XmlStreamReader &reader)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(reader.name() == QLatin1String("fills"));
|
||||||
|
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
int count = attributes.value(QLatin1String("count")).toInt();
|
||||||
|
for (int i=0; i<count; ++i) {
|
||||||
|
reader.readNextStartElement();
|
||||||
|
if (reader.name() != QLatin1String("fill") || reader.tokenType() != QXmlStreamReader::StartElement)
|
||||||
return false;
|
return false;
|
||||||
|
readFill(reader);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Styles::readFill(XmlStreamReader &reader)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<FillData> fill(new FillData);
|
||||||
|
while((reader.readNextStartElement(), true)) { //read until fill endelement
|
||||||
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||||
|
if (reader.name() == QLatin1String("patternFill")) {
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
QString pattern = attributes.value(QLatin1String("patternType")).toString();
|
||||||
|
fill->pattern = patternValues.contains(pattern) ? patternValues[pattern] : Format::PatternNone;
|
||||||
|
} else if (reader.name() == QLatin1String("fgColor")) {
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
if (attributes.hasAttribute(QLatin1String("rgb"))) {
|
||||||
|
QColor c = fromARGBString(attributes.value(QLatin1String("rgb")).toString());
|
||||||
|
if (fill->pattern == Format::PatternSolid)
|
||||||
|
fill->bgColor = c;
|
||||||
|
else
|
||||||
|
fill->fgColor = c;
|
||||||
|
}
|
||||||
|
} else if (reader.name() == QLatin1String("bgColor")) {
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
if (attributes.hasAttribute(QLatin1String("rgb"))) {
|
||||||
|
QColor c = fromARGBString(attributes.value(QLatin1String("rgb")).toString());
|
||||||
|
if (fill->pattern == Format::PatternSolid)
|
||||||
|
fill->fgColor = c;
|
||||||
|
else
|
||||||
|
fill->bgColor = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("fill"))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_fillsList.append(fill);
|
||||||
|
m_fillsHash.insert(fill->key(), fill);
|
||||||
|
fill->setIndex(m_fillsList.size()-1);//first call key(), then setIndex()
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Styles::readBorders(XmlStreamReader &reader)
|
bool Styles::readBorders(XmlStreamReader &reader)
|
||||||
@@ -663,8 +734,21 @@ bool Styles::readCellXfs(XmlStreamReader &reader)
|
|||||||
|
|
||||||
if (xfAttrs.hasAttribute(QLatin1String("applyFont"))) {
|
if (xfAttrs.hasAttribute(QLatin1String("applyFont"))) {
|
||||||
int fontIndex = xfAttrs.value(QLatin1String("fontId")).toInt();
|
int fontIndex = xfAttrs.value(QLatin1String("fontId")).toInt();
|
||||||
|
if (fontIndex >= m_fontsList.size()) {
|
||||||
|
qDebug("Error read styles.xml, cellXfs fontId");
|
||||||
|
} else {
|
||||||
format->d_func()->fontData = *m_fontsList[fontIndex];
|
format->d_func()->fontData = *m_fontsList[fontIndex];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xfAttrs.hasAttribute(QLatin1String("applyFill"))) {
|
||||||
|
int id = xfAttrs.value(QLatin1String("fillId")).toInt();
|
||||||
|
if (id >= m_fillsList.size()) {
|
||||||
|
qDebug("Error read styles.xml, cellXfs fillId");
|
||||||
|
} else {
|
||||||
|
format->d_func()->fillData = *m_fillsList[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addFormat(format);
|
addFormat(format);
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
|
class StylesTest;
|
||||||
|
|
||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Format;
|
friend class Format;
|
||||||
|
friend class StylesTest;
|
||||||
|
|
||||||
void writeNumFmts(XmlStreamWriter &writer);
|
void writeNumFmts(XmlStreamWriter &writer);
|
||||||
void writeFonts(XmlStreamWriter &writer);
|
void writeFonts(XmlStreamWriter &writer);
|
||||||
@@ -72,6 +74,7 @@ private:
|
|||||||
bool readNumFmts(XmlStreamReader &reader);
|
bool readNumFmts(XmlStreamReader &reader);
|
||||||
bool readFonts(XmlStreamReader &reader);
|
bool readFonts(XmlStreamReader &reader);
|
||||||
bool readFills(XmlStreamReader &reader);
|
bool readFills(XmlStreamReader &reader);
|
||||||
|
bool readFill(XmlStreamReader &reader);
|
||||||
bool readBorders(XmlStreamReader &reader);
|
bool readBorders(XmlStreamReader &reader);
|
||||||
bool readCellXfs(XmlStreamReader &reader);
|
bool readCellXfs(XmlStreamReader &reader);
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QColor>
|
||||||
|
|
||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
@@ -51,6 +52,16 @@ QStringList splitPath(const QString &path)
|
|||||||
return QStringList()<<path.left(idx)<<path.mid(idx+1);
|
return QStringList()<<path.left(idx)<<path.mid(idx+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QColor fromARGBString(const QString &c)
|
||||||
|
{
|
||||||
|
Q_ASSERT(c.length() == 8);
|
||||||
|
QColor color;
|
||||||
|
color.setRed(c.mid(2, 2).toInt(0, 16));
|
||||||
|
color.setGreen(c.mid(4, 2).toInt(0, 16));
|
||||||
|
color.setBlue(c.mid(6, 2).toInt(0, 16));
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
QPoint xl_cell_to_rowcol(const QString &cell_str)
|
QPoint xl_cell_to_rowcol(const QString &cell_str)
|
||||||
{
|
{
|
||||||
if (cell_str.isEmpty())
|
if (cell_str.isEmpty())
|
||||||
|
|||||||
@@ -29,11 +29,13 @@
|
|||||||
class QPoint;
|
class QPoint;
|
||||||
class QString;
|
class QString;
|
||||||
class QStringList;
|
class QStringList;
|
||||||
|
class QColor;
|
||||||
|
|
||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
XLSX_AUTOTEST_EXPORT int intPow(int x, int p);
|
XLSX_AUTOTEST_EXPORT int intPow(int x, int p);
|
||||||
XLSX_AUTOTEST_EXPORT QStringList splitPath(const QString &path);
|
XLSX_AUTOTEST_EXPORT QStringList splitPath(const QString &path);
|
||||||
|
XLSX_AUTOTEST_EXPORT QColor fromARGBString(const QString &c);
|
||||||
|
|
||||||
XLSX_AUTOTEST_EXPORT QPoint xl_cell_to_rowcol(const QString &cell_str);
|
XLSX_AUTOTEST_EXPORT QPoint xl_cell_to_rowcol(const QString &cell_str);
|
||||||
XLSX_AUTOTEST_EXPORT QString xl_col_to_name(int col_num);
|
XLSX_AUTOTEST_EXPORT QString xl_col_to_name(int col_num);
|
||||||
|
|||||||
@@ -32,4 +32,9 @@ XmlStreamReader::XmlStreamReader(QIODevice *device) :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XmlStreamReader::XmlStreamReader(const QByteArray &data) :
|
||||||
|
QXmlStreamReader(data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QXlsx
|
} // namespace QXlsx
|
||||||
|
|||||||
@@ -25,15 +25,16 @@
|
|||||||
|
|
||||||
#ifndef QXLSX_XLSXXMLREADER_H
|
#ifndef QXLSX_XLSXXMLREADER_H
|
||||||
#define QXLSX_XLSXXMLREADER_H
|
#define QXLSX_XLSXXMLREADER_H
|
||||||
|
#include "xlsxglobal.h"
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
class XmlStreamReader : public QXmlStreamReader
|
class XLSX_AUTOTEST_EXPORT XmlStreamReader : public QXmlStreamReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
XmlStreamReader(QIODevice *device);
|
explicit XmlStreamReader(QIODevice *device);
|
||||||
|
explicit XmlStreamReader(const QByteArray &data);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace QXlsx
|
} // namespace QXlsx
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "private/xlsxstyles_p.h"
|
#include "private/xlsxstyles_p.h"
|
||||||
|
#include "private/xlsxxmlreader_p.h"
|
||||||
#include "xlsxformat.h"
|
#include "xlsxformat.h"
|
||||||
|
#include "private/xlsxformat_p.h"
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
|
|
||||||
@@ -15,6 +17,10 @@ private Q_SLOTS:
|
|||||||
void testAddFormat();
|
void testAddFormat();
|
||||||
void testAddFormat2();
|
void testAddFormat2();
|
||||||
void testSolidFillBackgroundColor();
|
void testSolidFillBackgroundColor();
|
||||||
|
|
||||||
|
void testReadFonts();
|
||||||
|
void testReadFills();
|
||||||
|
void testReaderBorders();
|
||||||
};
|
};
|
||||||
|
|
||||||
StylesTest::StylesTest()
|
StylesTest::StylesTest()
|
||||||
@@ -73,6 +79,45 @@ void StylesTest::testSolidFillBackgroundColor()
|
|||||||
QVERIFY(xmlData.contains("<patternFill patternType=\"solid\"><fgColor rgb=\"FFff0000\"/>"));
|
QVERIFY(xmlData.contains("<patternFill patternType=\"solid\"><fgColor rgb=\"FFff0000\"/>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void StylesTest::testReadFonts()
|
||||||
|
{
|
||||||
|
QByteArray xmlData = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
|
||||||
|
"<fonts count=\"3\">"
|
||||||
|
"<font><sz val=\"11\"/><name val=\"Calibri\"/><family val=\"2\"/><scheme val=\"minor\"/></font>"
|
||||||
|
"<font><sz val=\"15\"/><color rgb=\"FFff0000\"/><name val=\"Calibri\"/><family val=\"2\"/><scheme val=\"minor\"/></font>"
|
||||||
|
"<font><b/><u val=\"double\"/><sz val=\"11\"/><name val=\"Calibri\"/><family val=\"2\"/><scheme val=\"minor\"/></font>"
|
||||||
|
"</fonts>";
|
||||||
|
QXlsx::Styles styles(true);
|
||||||
|
QXlsx::XmlStreamReader reader(xmlData);
|
||||||
|
reader.readNextStartElement();//So current node is fonts
|
||||||
|
styles.readFonts(reader);
|
||||||
|
|
||||||
|
QCOMPARE(styles.m_fontsList.size(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylesTest::testReadFills()
|
||||||
|
{
|
||||||
|
QByteArray xmlData = "<fills count=\"4\">"
|
||||||
|
"<fill><patternFill patternType=\"none\"/></fill>"
|
||||||
|
"<fill><patternFill patternType=\"gray125\"/></fill>"
|
||||||
|
"<fill><patternFill patternType=\"lightUp\"/></fill>"
|
||||||
|
"<fill><patternFill patternType=\"solid\"><fgColor rgb=\"FFa0a0a4\"/></patternFill></fill>"
|
||||||
|
"</fills>";
|
||||||
|
QXlsx::Styles styles(true);
|
||||||
|
QXlsx::XmlStreamReader reader(xmlData);
|
||||||
|
reader.readNextStartElement();//So current node is fonts
|
||||||
|
styles.readFills(reader);
|
||||||
|
|
||||||
|
QCOMPARE(styles.m_fillsList.size(), 4);
|
||||||
|
QCOMPARE(styles.m_fillsList[3]->pattern, QXlsx::Format::PatternSolid);
|
||||||
|
QCOMPARE(styles.m_fillsList[3]->bgColor, QColor(Qt::gray));//for solid pattern, bg vs. fg color!
|
||||||
|
}
|
||||||
|
|
||||||
|
void StylesTest::testReaderBorders()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_APPLESS_MAIN(StylesTest)
|
QTEST_APPLESS_MAIN(StylesTest)
|
||||||
|
|
||||||
#include "tst_stylestest.moc"
|
#include "tst_stylestest.moc"
|
||||||
|
|||||||
Reference in New Issue
Block a user