Can read .xlsx files with font styles now
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
#include "xlsxdocument.h"
|
#include "xlsxdocument.h"
|
||||||
|
#include "xlsxformat.h"
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
@@ -8,7 +9,10 @@ int main()
|
|||||||
xlsx.setDocumentProperty("title", "This is an example spreadsheet");
|
xlsx.setDocumentProperty("title", "This is an example spreadsheet");
|
||||||
xlsx.setDocumentProperty("creator", "Qt Xlsx Library");
|
xlsx.setDocumentProperty("creator", "Qt Xlsx Library");
|
||||||
xlsx.setSheetName("First Sheet");
|
xlsx.setSheetName("First Sheet");
|
||||||
xlsx.write("A1", "Hello Qt!");
|
QXlsx::Format *format = xlsx.createFormat();
|
||||||
|
format->setFontColor(QColor(Qt::blue));
|
||||||
|
format->setFontSize(15);
|
||||||
|
xlsx.write("A1", "Hello Qt!", format);
|
||||||
xlsx.write("A2", 500);
|
xlsx.write("A2", 500);
|
||||||
xlsx.saveAs("first.xlsx");
|
xlsx.saveAs("first.xlsx");
|
||||||
//![0]
|
//![0]
|
||||||
|
|||||||
@@ -147,7 +147,11 @@ bool Package::parsePackage(QIODevice *packageDevice)
|
|||||||
QList<XlsxRelationship> rels_styles = xlworkbook_Rels.documentRelationships(QStringLiteral("/styles"));
|
QList<XlsxRelationship> rels_styles = xlworkbook_Rels.documentRelationships(QStringLiteral("/styles"));
|
||||||
if (!rels_styles.isEmpty()) {
|
if (!rels_styles.isEmpty()) {
|
||||||
//In normal case this should be styles.xml which in xl
|
//In normal case this should be styles.xml which in xl
|
||||||
//:Todo
|
QString name = rels_styles[0].target;
|
||||||
|
QString path = xlworkbook_Dir + QLatin1String("/") + name;
|
||||||
|
QSharedPointer<Styles> styles (new Styles(true));
|
||||||
|
styles->loadFromXmlData(zipReader.fileData(path));
|
||||||
|
m_document->workbook()->d_ptr->styles = styles;
|
||||||
}
|
}
|
||||||
|
|
||||||
//load sharedStrings
|
//load sharedStrings
|
||||||
|
|||||||
+191
-9
@@ -24,6 +24,7 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
#include "xlsxstyles_p.h"
|
#include "xlsxstyles_p.h"
|
||||||
#include "xlsxxmlwriter_p.h"
|
#include "xlsxxmlwriter_p.h"
|
||||||
|
#include "xlsxxmlreader_p.h"
|
||||||
#include "xlsxformat_p.h"
|
#include "xlsxformat_p.h"
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
@@ -34,8 +35,9 @@
|
|||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
|
|
||||||
Styles::Styles()
|
Styles::Styles(bool createEmpty)
|
||||||
{
|
{
|
||||||
|
if (!createEmpty) {
|
||||||
//Add default Format
|
//Add default Format
|
||||||
addFormat(createFormat());
|
addFormat(createFormat());
|
||||||
//Add another fill format
|
//Add another fill format
|
||||||
@@ -43,6 +45,7 @@ Styles::Styles()
|
|||||||
fill->pattern = Format::PatternGray125;
|
fill->pattern = Format::PatternGray125;
|
||||||
m_fillsList.append(fill);
|
m_fillsList.append(fill);
|
||||||
m_fillsHash[fill->key()] = fill;
|
m_fillsHash[fill->key()] = fill;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Styles::~Styles()
|
Styles::~Styles()
|
||||||
@@ -57,6 +60,14 @@ Format *Styles::createFormat()
|
|||||||
return format;
|
return format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Format *Styles::xfFormat(int idx) const
|
||||||
|
{
|
||||||
|
if (idx <0 || idx > m_xf_formatsList.size())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_xf_formatsList[idx];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Assign index to Font/Fill/Border and Format
|
Assign index to Font/Fill/Border and Format
|
||||||
*/
|
*/
|
||||||
@@ -111,12 +122,14 @@ void Styles::addFormat(Format *format)
|
|||||||
if (m_builtinNumFmtsHash.contains(str)) {
|
if (m_builtinNumFmtsHash.contains(str)) {
|
||||||
format->setNumFmt(m_builtinNumFmtsHash[str], str);
|
format->setNumFmt(m_builtinNumFmtsHash[str], str);
|
||||||
} else if (m_customNumFmtsHash.contains(str)) {
|
} else if (m_customNumFmtsHash.contains(str)) {
|
||||||
format->setNumFmt(m_customNumFmtsHash[str], str);
|
format->setNumFmt(m_customNumFmtsHash[str]->formatIndex, str);
|
||||||
} else {
|
} else {
|
||||||
int idx = 164 + m_customNumFmts.size();
|
int idx = 164 + m_customNumFmts.size();
|
||||||
m_customNumFmts.append(str);
|
|
||||||
m_customNumFmtsHash.insert(str, idx);
|
|
||||||
format->setNumFmt(idx, str);
|
format->setNumFmt(idx, str);
|
||||||
|
|
||||||
|
QSharedPointer<NumberData> fmt(new NumberData(format->d_func()->numberData));
|
||||||
|
m_customNumFmts.append(fmt);
|
||||||
|
m_customNumFmtsHash.insert(str, fmt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +256,7 @@ void Styles::writeNumFmts(XmlStreamWriter &writer)
|
|||||||
for (int i=0; i<m_customNumFmts.size(); ++i) {
|
for (int i=0; i<m_customNumFmts.size(); ++i) {
|
||||||
writer.writeEmptyElement(QStringLiteral("numFmt"));
|
writer.writeEmptyElement(QStringLiteral("numFmt"));
|
||||||
writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(164 + i));
|
writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(164 + i));
|
||||||
writer.writeAttribute(QStringLiteral("formatCode"), m_customNumFmts[i]);
|
writer.writeAttribute(QStringLiteral("formatCode"), m_customNumFmts[i]->formatString);
|
||||||
}
|
}
|
||||||
writer.writeEndElement();//numFmts
|
writer.writeEndElement();//numFmts
|
||||||
}
|
}
|
||||||
@@ -518,13 +531,182 @@ void Styles::writeDxfs(XmlStreamWriter &writer)
|
|||||||
writer.writeEndElement(); //dxfs
|
writer.writeEndElement(); //dxfs
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Styles> Styles::loadFromXmlFile(QIODevice *device)
|
bool Styles::readNumFmts(XmlStreamReader &reader)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(reader.name() == QLatin1String("numFmts"));
|
||||||
return QSharedPointer<Styles>(new Styles);
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
int count = attributes.value(QLatin1String("count")).toInt();
|
||||||
|
for (int i=0; i<count; ++i) {
|
||||||
|
reader.readNextStartElement();
|
||||||
|
if (reader.name() != QLatin1String("numFmt"))
|
||||||
|
return false;
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
QSharedPointer<NumberData> fmt (new NumberData);
|
||||||
|
fmt->formatIndex = attributes.value(QLatin1String("numFmtId")).toInt();
|
||||||
|
fmt->formatString = attributes.value(QLatin1String("formatCode")).toString();
|
||||||
|
m_customNumFmts.append(fmt);
|
||||||
|
m_customNumFmtsHash.insert(fmt->formatString, fmt);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Styles> Styles::loadFromXmlData(const QByteArray &data)
|
bool Styles::readFonts(XmlStreamReader &reader)
|
||||||
|
{
|
||||||
|
Q_ASSERT(reader.name() == QLatin1String("fonts"));
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
int count = attributes.value(QLatin1String("count")).toInt();
|
||||||
|
for (int i=0; i<count; ++i) {
|
||||||
|
reader.readNextStartElement();
|
||||||
|
if (reader.name() != QLatin1String("font"))
|
||||||
|
return false;
|
||||||
|
QSharedPointer<FontData> font(new FontData);
|
||||||
|
while(reader.readNextStartElement()) {
|
||||||
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||||
|
if (reader.name() == QLatin1String("b")) {
|
||||||
|
font->bold = true;
|
||||||
|
} else if (reader.name() == QLatin1String("i")) {
|
||||||
|
font->italic = true;
|
||||||
|
} else if (reader.name() == QLatin1String("strike")) {
|
||||||
|
font->strikeOut = true;
|
||||||
|
} else if (reader.name() == QLatin1String("outline")) {
|
||||||
|
font->outline = true;
|
||||||
|
} else if (reader.name() == QLatin1String("shadow")) {
|
||||||
|
font->shadow = true;
|
||||||
|
} else if (reader.name() == QLatin1String("u")) {
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
QString value = attributes.value(QLatin1String("val")).toString();
|
||||||
|
if (value == QLatin1String("double"))
|
||||||
|
font->underline = Format::FontUnderlineDouble;
|
||||||
|
else if (value == QLatin1String("doubleAccounting"))
|
||||||
|
font->underline = Format::FontUnderlineDoubleAccounting;
|
||||||
|
else if (value == QLatin1String("singleAccounting"))
|
||||||
|
font->underline = Format::FontUnderlineSingleAccounting;
|
||||||
|
else
|
||||||
|
font->underline = Format::FontUnderlineSingle;
|
||||||
|
} else if (reader.name() == QLatin1String("vertAligh")) {
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
QString value = attributes.value(QLatin1String("val")).toString();
|
||||||
|
if (value == QLatin1String("superscript"))
|
||||||
|
font->scirpt = Format::FontScriptSuper;
|
||||||
|
else
|
||||||
|
font->scirpt = Format::FontScriptSub;
|
||||||
|
} else if (reader.name() == QLatin1String("sz")) {
|
||||||
|
font->size = reader.attributes().value(QLatin1String("val")).toInt();
|
||||||
|
} else if (reader.name() == QLatin1String("color")) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
} else if (attributes.hasAttribute(QLatin1String("indexed"))) {
|
||||||
|
|
||||||
|
} else if (attributes.hasAttribute(QLatin1String("theme"))) {
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if (reader.name() == QLatin1String("name")) {
|
||||||
|
font->name = reader.attributes().value(QLatin1String("val")).toString();
|
||||||
|
} else if (reader.name() == QLatin1String("family")) {
|
||||||
|
font->family = reader.attributes().value(QLatin1String("val")).toInt();
|
||||||
|
} else if (reader.name() == QLatin1String("scheme")) {
|
||||||
|
font->scheme = reader.attributes().value(QLatin1String("val")).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("font"))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_fontsList.append(font);
|
||||||
|
m_fontsHash.insert(font->key(), font);
|
||||||
|
font->setIndex(m_fontsList.size()-1);//first call key(), then setIndex()
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Styles::readFills(XmlStreamReader &reader)
|
||||||
|
{
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Styles::readBorders(XmlStreamReader &reader)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Styles::readCellXfs(XmlStreamReader &reader)
|
||||||
|
{
|
||||||
|
Q_ASSERT(reader.name() == QLatin1String("cellXfs"));
|
||||||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
int count = attributes.value(QLatin1String("count")).toInt();
|
||||||
|
for (int i=0; i<count; ++i) {
|
||||||
|
reader.readNextStartElement();
|
||||||
|
if (reader.name() != QLatin1String("xf"))
|
||||||
|
return false;
|
||||||
|
Format *format = createFormat();
|
||||||
|
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("applyNumberFormat"))) {
|
||||||
|
int numFmtIndex = xfAttrs.value(QLatin1String("numFmtId")).toInt();
|
||||||
|
if (numFmtIndex < 164) {
|
||||||
|
format->setNumberFormatIndex(numFmtIndex);
|
||||||
|
} else {
|
||||||
|
format->d_func()->numberData = *m_customNumFmts[numFmtIndex-164];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xfAttrs.hasAttribute(QLatin1String("applyFont"))) {
|
||||||
|
int fontIndex = xfAttrs.value(QLatin1String("fontId")).toInt();
|
||||||
|
format->d_func()->fontData = *m_fontsList[fontIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
addFormat(format);
|
||||||
|
|
||||||
|
//Find the endElement of xf
|
||||||
|
while (!(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("xf")))
|
||||||
|
reader.readNextStartElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Styles::loadFromXmlFile(QIODevice *device)
|
||||||
|
{
|
||||||
|
XmlStreamReader 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")) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.hasError()) {
|
||||||
|
qDebug()<<"Error when read style file: "<<reader.errorString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Styles::loadFromXmlData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
QBuffer buffer;
|
QBuffer buffer;
|
||||||
buffer.setData(data);
|
buffer.setData(data);
|
||||||
|
|||||||
+14
-5
@@ -36,23 +36,26 @@ class QIODevice;
|
|||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
class Format;
|
class Format;
|
||||||
|
struct NumberData;
|
||||||
struct FontData;
|
struct FontData;
|
||||||
struct FillData;
|
struct FillData;
|
||||||
struct BorderData;
|
struct BorderData;
|
||||||
class XmlStreamWriter;
|
class XmlStreamWriter;
|
||||||
|
class XmlStreamReader;
|
||||||
|
|
||||||
class XLSX_AUTOTEST_EXPORT Styles
|
class XLSX_AUTOTEST_EXPORT Styles
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Styles();
|
Styles(bool createEmpty=false);
|
||||||
~Styles();
|
~Styles();
|
||||||
Format *createFormat();
|
Format *createFormat();
|
||||||
void addFormat(Format *format);
|
void addFormat(Format *format);
|
||||||
|
Format *xfFormat(int idx) const;
|
||||||
|
|
||||||
QByteArray saveToXmlData();
|
QByteArray saveToXmlData();
|
||||||
void saveToXmlFile(QIODevice *device);
|
void saveToXmlFile(QIODevice *device);
|
||||||
static QSharedPointer<Styles> loadFromXmlFile(QIODevice *device);
|
bool loadFromXmlFile(QIODevice *device);
|
||||||
static QSharedPointer<Styles> loadFromXmlData(const QByteArray &data);
|
bool loadFromXmlData(const QByteArray &data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Format;
|
friend class Format;
|
||||||
@@ -66,9 +69,15 @@ private:
|
|||||||
void writeCellXfs(XmlStreamWriter &writer);
|
void writeCellXfs(XmlStreamWriter &writer);
|
||||||
void writeDxfs(XmlStreamWriter &writer);
|
void writeDxfs(XmlStreamWriter &writer);
|
||||||
|
|
||||||
|
bool readNumFmts(XmlStreamReader &reader);
|
||||||
|
bool readFonts(XmlStreamReader &reader);
|
||||||
|
bool readFills(XmlStreamReader &reader);
|
||||||
|
bool readBorders(XmlStreamReader &reader);
|
||||||
|
bool readCellXfs(XmlStreamReader &reader);
|
||||||
|
|
||||||
QHash<QString, int> m_builtinNumFmtsHash;
|
QHash<QString, int> m_builtinNumFmtsHash;
|
||||||
QStringList m_customNumFmts;
|
QList<QSharedPointer<NumberData> > m_customNumFmts;
|
||||||
QHash<QString, int> m_customNumFmtsHash;
|
QHash<QString, QSharedPointer<NumberData> > m_customNumFmtsHash;
|
||||||
QList<QSharedPointer<FontData> > m_fontsList; //Keep a copy of unique fonts
|
QList<QSharedPointer<FontData> > m_fontsList; //Keep a copy of unique fonts
|
||||||
QList<QSharedPointer<FillData> > m_fillsList; //Keep a copy of unique fills
|
QList<QSharedPointer<FillData> > m_fillsList; //Keep a copy of unique fills
|
||||||
QList<QSharedPointer<BorderData> > m_bordersList; //Keep a copy of unique borders
|
QList<QSharedPointer<BorderData> > m_bordersList; //Keep a copy of unique borders
|
||||||
|
|||||||
@@ -1104,6 +1104,12 @@ bool Worksheet::loadFromXmlFile(QIODevice *device)
|
|||||||
QString r = attributes.value(QLatin1String("r")).toString();
|
QString r = attributes.value(QLatin1String("r")).toString();
|
||||||
QPoint pos = xl_cell_to_rowcol(r);
|
QPoint pos = xl_cell_to_rowcol(r);
|
||||||
|
|
||||||
|
Format *format = 0;
|
||||||
|
if (attributes.hasAttribute(QLatin1String("s"))) {
|
||||||
|
int idx = attributes.value(QLatin1String("s")).toInt();
|
||||||
|
format = d->workbook->styles()->xfFormat(idx);
|
||||||
|
}
|
||||||
|
|
||||||
if (attributes.hasAttribute(QLatin1String("t"))) {
|
if (attributes.hasAttribute(QLatin1String("t"))) {
|
||||||
QString type = attributes.value(QLatin1String("t")).toString();
|
QString type = attributes.value(QLatin1String("t")).toString();
|
||||||
if (type == QLatin1String("s")) {
|
if (type == QLatin1String("s")) {
|
||||||
@@ -1112,7 +1118,7 @@ bool Worksheet::loadFromXmlFile(QIODevice *device)
|
|||||||
if (reader.name() == QLatin1String("v")) {
|
if (reader.name() == QLatin1String("v")) {
|
||||||
QString value = reader.readElementText();
|
QString value = reader.readElementText();
|
||||||
d->workbook->sharedStrings()->incRefByStringIndex(value.toInt());
|
d->workbook->sharedStrings()->incRefByStringIndex(value.toInt());
|
||||||
XlsxCellData *data = new XlsxCellData(value ,XlsxCellData::String);
|
XlsxCellData *data = new XlsxCellData(value ,XlsxCellData::String, format);
|
||||||
d->cellTable[pos.x()][pos.y()] = QSharedPointer<XlsxCellData>(data);
|
d->cellTable[pos.x()][pos.y()] = QSharedPointer<XlsxCellData>(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1121,7 +1127,7 @@ bool Worksheet::loadFromXmlFile(QIODevice *device)
|
|||||||
reader.readNextStartElement();
|
reader.readNextStartElement();
|
||||||
if (reader.name() == QLatin1String("v")) {
|
if (reader.name() == QLatin1String("v")) {
|
||||||
QString value = reader.readElementText();
|
QString value = reader.readElementText();
|
||||||
XlsxCellData *data = new XlsxCellData(value ,XlsxCellData::Number);
|
XlsxCellData *data = new XlsxCellData(value ,XlsxCellData::Number, format);
|
||||||
d->cellTable[pos.x()][pos.y()] = QSharedPointer<XlsxCellData>(data);
|
d->cellTable[pos.x()][pos.y()] = QSharedPointer<XlsxCellData>(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user