Can read .xlsx files with fill styles now
This commit is contained in:
@@ -12,6 +12,7 @@ int main()
|
||||
QXlsx::Format *format = xlsx.createFormat();
|
||||
format->setFontColor(QColor(Qt::blue));
|
||||
format->setFontSize(15);
|
||||
format->setPatternBackgroundColor(QColor(Qt::gray));
|
||||
xlsx.write("A1", "Hello Qt!", format);
|
||||
xlsx.write("A2", 500);
|
||||
xlsx.saveAs("first.xlsx");
|
||||
|
||||
+92
-8
@@ -26,6 +26,7 @@
|
||||
#include "xlsxxmlwriter_p.h"
|
||||
#include "xlsxxmlreader_p.h"
|
||||
#include "xlsxformat_p.h"
|
||||
#include "xlsxutility_p.h"
|
||||
#include <QFile>
|
||||
#include <QMap>
|
||||
#include <QDataStream>
|
||||
@@ -560,7 +561,7 @@ bool Styles::readFonts(XmlStreamReader &reader)
|
||||
if (reader.name() != QLatin1String("font"))
|
||||
return false;
|
||||
QSharedPointer<FontData> font(new FontData);
|
||||
while(reader.readNextStartElement()) {
|
||||
while((reader.readNextStartElement(),true)) { //read until font endelement.
|
||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||
if (reader.name() == QLatin1String("b")) {
|
||||
font->bold = true;
|
||||
@@ -596,11 +597,7 @@ bool Styles::readFonts(XmlStreamReader &reader)
|
||||
QXmlStreamAttributes attributes = reader.attributes();
|
||||
if (attributes.hasAttribute(QLatin1String("rgb"))) {
|
||||
QString colorString = attributes.value(QLatin1String("rgb")).toString();
|
||||
if (colorString.length() == 8) {
|
||||
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));
|
||||
}
|
||||
font->color = fromARGBString(colorString);
|
||||
} else if (attributes.hasAttribute(QLatin1String("indexed"))) {
|
||||
|
||||
} else if (attributes.hasAttribute(QLatin1String("theme"))) {
|
||||
@@ -627,8 +624,82 @@ bool Styles::readFonts(XmlStreamReader &reader)
|
||||
|
||||
bool Styles::readFills(XmlStreamReader &reader)
|
||||
{
|
||||
Q_ASSERT(reader.name() == QLatin1String("fills"));
|
||||
|
||||
return false;
|
||||
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;
|
||||
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)
|
||||
@@ -663,7 +734,20 @@ bool Styles::readCellXfs(XmlStreamReader &reader)
|
||||
|
||||
if (xfAttrs.hasAttribute(QLatin1String("applyFont"))) {
|
||||
int fontIndex = xfAttrs.value(QLatin1String("fontId")).toInt();
|
||||
format->d_func()->fontData = *m_fontsList[fontIndex];
|
||||
if (fontIndex >= m_fontsList.size()) {
|
||||
qDebug("Error read styles.xml, cellXfs fontId");
|
||||
} else {
|
||||
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);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <QStringList>
|
||||
|
||||
class QIODevice;
|
||||
class StylesTest;
|
||||
|
||||
namespace QXlsx {
|
||||
|
||||
@@ -59,6 +60,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class Format;
|
||||
friend class StylesTest;
|
||||
|
||||
void writeNumFmts(XmlStreamWriter &writer);
|
||||
void writeFonts(XmlStreamWriter &writer);
|
||||
@@ -72,6 +74,7 @@ private:
|
||||
bool readNumFmts(XmlStreamReader &reader);
|
||||
bool readFonts(XmlStreamReader &reader);
|
||||
bool readFills(XmlStreamReader &reader);
|
||||
bool readFill(XmlStreamReader &reader);
|
||||
bool readBorders(XmlStreamReader &reader);
|
||||
bool readCellXfs(XmlStreamReader &reader);
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <QRegularExpression>
|
||||
#include <QMap>
|
||||
#include <QStringList>
|
||||
#include <QColor>
|
||||
|
||||
namespace QXlsx {
|
||||
|
||||
@@ -51,6 +52,16 @@ QStringList splitPath(const QString &path)
|
||||
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)
|
||||
{
|
||||
if (cell_str.isEmpty())
|
||||
|
||||
@@ -29,11 +29,13 @@
|
||||
class QPoint;
|
||||
class QString;
|
||||
class QStringList;
|
||||
class QColor;
|
||||
|
||||
namespace QXlsx {
|
||||
|
||||
XLSX_AUTOTEST_EXPORT int intPow(int x, int p);
|
||||
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 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
|
||||
|
||||
@@ -25,15 +25,16 @@
|
||||
|
||||
#ifndef QXLSX_XLSXXMLREADER_H
|
||||
#define QXLSX_XLSXXMLREADER_H
|
||||
|
||||
#include "xlsxglobal.h"
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
namespace QXlsx {
|
||||
|
||||
class XmlStreamReader : public QXmlStreamReader
|
||||
class XLSX_AUTOTEST_EXPORT XmlStreamReader : public QXmlStreamReader
|
||||
{
|
||||
public:
|
||||
XmlStreamReader(QIODevice *device);
|
||||
explicit XmlStreamReader(QIODevice *device);
|
||||
explicit XmlStreamReader(const QByteArray &data);
|
||||
};
|
||||
|
||||
} // namespace QXlsx
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "private/xlsxstyles_p.h"
|
||||
#include "private/xlsxxmlreader_p.h"
|
||||
#include "xlsxformat.h"
|
||||
#include "private/xlsxformat_p.h"
|
||||
#include <QString>
|
||||
#include <QtTest>
|
||||
|
||||
@@ -15,6 +17,10 @@ private Q_SLOTS:
|
||||
void testAddFormat();
|
||||
void testAddFormat2();
|
||||
void testSolidFillBackgroundColor();
|
||||
|
||||
void testReadFonts();
|
||||
void testReadFills();
|
||||
void testReaderBorders();
|
||||
};
|
||||
|
||||
StylesTest::StylesTest()
|
||||
@@ -73,6 +79,45 @@ void StylesTest::testSolidFillBackgroundColor()
|
||||
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)
|
||||
|
||||
#include "tst_stylestest.moc"
|
||||
|
||||
Reference in New Issue
Block a user