Fix Issue 7: Cell string misplacement when rich text exist
A private class RichString has been added. More work is needed
This commit is contained in:
+4
-2
@@ -31,7 +31,8 @@ HEADERS += $$PWD/xlsxdocpropscore_p.h \
|
|||||||
$$PWD/xlsxcell_p.h \
|
$$PWD/xlsxcell_p.h \
|
||||||
$$PWD/xlsxdatavalidation.h \
|
$$PWD/xlsxdatavalidation.h \
|
||||||
$$PWD/xlsxdatavalidation_p.h \
|
$$PWD/xlsxdatavalidation_p.h \
|
||||||
$$PWD/xlsxcellrange.h
|
$$PWD/xlsxcellrange.h \
|
||||||
|
$$PWD/xlsxrichstring_p.h
|
||||||
|
|
||||||
SOURCES += $$PWD/xlsxdocpropscore.cpp \
|
SOURCES += $$PWD/xlsxdocpropscore.cpp \
|
||||||
$$PWD/xlsxdocpropsapp.cpp \
|
$$PWD/xlsxdocpropsapp.cpp \
|
||||||
@@ -53,4 +54,5 @@ SOURCES += $$PWD/xlsxdocpropscore.cpp \
|
|||||||
$$PWD/xlsxdocument.cpp \
|
$$PWD/xlsxdocument.cpp \
|
||||||
$$PWD/xlsxcell.cpp \
|
$$PWD/xlsxcell.cpp \
|
||||||
$$PWD/xlsxdatavalidation.cpp \
|
$$PWD/xlsxdatavalidation.cpp \
|
||||||
$$PWD/xlsxcellrange.cpp
|
$$PWD/xlsxcellrange.cpp \
|
||||||
|
$$PWD/xlsxrichstring.cpp
|
||||||
|
|||||||
@@ -121,4 +121,16 @@ QDateTime Cell::dateTime() const
|
|||||||
return datetimeFromNumber(d->value.toDouble(), d->parent->workbook()->isDate1904());
|
return datetimeFromNumber(d->value.toDouble(), d->parent->workbook()->isDate1904());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns whether the cell is probably a rich string or not
|
||||||
|
*/
|
||||||
|
bool Cell::isRichString() const
|
||||||
|
{
|
||||||
|
Q_D(const Cell);
|
||||||
|
if (d->dataType != String && d->dataType != InlineString)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return d->richString.isRichString();
|
||||||
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE_XLSX
|
QT_END_NAMESPACE_XLSX
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ public:
|
|||||||
bool isDateTime() const;
|
bool isDateTime() const;
|
||||||
QDateTime dateTime() const;
|
QDateTime dateTime() const;
|
||||||
|
|
||||||
|
bool isRichString() const;
|
||||||
|
|
||||||
~Cell();
|
~Cell();
|
||||||
private:
|
private:
|
||||||
friend class Worksheet;
|
friend class Worksheet;
|
||||||
|
|||||||
@@ -28,6 +28,9 @@
|
|||||||
#include "xlsxglobal.h"
|
#include "xlsxglobal.h"
|
||||||
#include "xlsxcell.h"
|
#include "xlsxcell.h"
|
||||||
#include "xlsxcellrange.h"
|
#include "xlsxcellrange.h"
|
||||||
|
#include "xlsxrichstring_p.h"
|
||||||
|
#include <QList>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE_XLSX
|
QT_BEGIN_NAMESPACE_XLSX
|
||||||
|
|
||||||
@@ -43,6 +46,8 @@ public:
|
|||||||
Format *format;
|
Format *format;
|
||||||
CellRange range; //used for arrayFormula
|
CellRange range; //used for arrayFormula
|
||||||
|
|
||||||
|
RichString richString;
|
||||||
|
|
||||||
Worksheet *parent;
|
Worksheet *parent;
|
||||||
Cell *q_ptr;
|
Cell *q_ptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ QT_BEGIN_NAMESPACE_XLSX
|
|||||||
class Styles;
|
class Styles;
|
||||||
class Worksheet;
|
class Worksheet;
|
||||||
class WorksheetPrivate;
|
class WorksheetPrivate;
|
||||||
|
class RichString;
|
||||||
|
|
||||||
class FormatPrivate;
|
class FormatPrivate;
|
||||||
class Q_XLSX_EXPORT Format
|
class Q_XLSX_EXPORT Format
|
||||||
@@ -214,6 +215,7 @@ private:
|
|||||||
friend class Styles;
|
friend class Styles;
|
||||||
friend class Worksheet;
|
friend class Worksheet;
|
||||||
friend class WorksheetPrivate;
|
friend class WorksheetPrivate;
|
||||||
|
friend class RichString;
|
||||||
friend class ::FormatTest;
|
friend class ::FormatTest;
|
||||||
|
|
||||||
Format();
|
Format();
|
||||||
|
|||||||
@@ -160,8 +160,7 @@ bool Package::parsePackage(QIODevice *packageDevice)
|
|||||||
//In normal case this should be sharedStrings.xml which in xl
|
//In normal case this should be sharedStrings.xml which in xl
|
||||||
QString name = rels_sharedStrings[0].target;
|
QString name = rels_sharedStrings[0].target;
|
||||||
QString path = xlworkbook_Dir + QLatin1String("/") + name;
|
QString path = xlworkbook_Dir + QLatin1String("/") + name;
|
||||||
QSharedPointer<SharedStrings> sst= SharedStrings::loadFromXmlData(zipReader.fileData(path));
|
m_document->workbook()->d_ptr->sharedStrings->loadFromXmlData(zipReader.fileData(path));
|
||||||
m_document->workbook()->d_ptr->sharedStrings = sst;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//load theme
|
//load theme
|
||||||
|
|||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
** Copyright (c) 2013 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 "xlsxrichstring_p.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE_XLSX
|
||||||
|
|
||||||
|
RichString::RichString()
|
||||||
|
:m_dirty(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RichString::RichString(const QString text)
|
||||||
|
:m_dirty(true)
|
||||||
|
{
|
||||||
|
addFragment(text, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RichString::isRichString() const
|
||||||
|
{
|
||||||
|
if (fragmentCount() > 1) //Is this enough??
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RichString::isEmtpy() const
|
||||||
|
{
|
||||||
|
return m_fragmentTexts.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RichString::toPlainString() const
|
||||||
|
{
|
||||||
|
if (isEmtpy())
|
||||||
|
return QString();
|
||||||
|
if (m_fragmentTexts.size() == 1)
|
||||||
|
return m_fragmentTexts[0];
|
||||||
|
|
||||||
|
return m_fragmentTexts.join(QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
int RichString::fragmentCount() const
|
||||||
|
{
|
||||||
|
return m_fragmentTexts.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RichString::addFragment(const QString &text, Format *format)
|
||||||
|
{
|
||||||
|
m_fragmentTexts.append(text);
|
||||||
|
m_fragmentFormats.append(format);
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RichString::fragmentText(int index) const
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= fragmentCount())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
return m_fragmentTexts[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Format *RichString::fragmentFormat(int index) const
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= fragmentCount())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_fragmentFormats[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
*/
|
||||||
|
QByteArray RichString::idKey() const
|
||||||
|
{
|
||||||
|
if (m_dirty) {
|
||||||
|
RichString *rs = const_cast<RichString *>(this);
|
||||||
|
QByteArray bytes;
|
||||||
|
if (!isRichString()) {
|
||||||
|
bytes = toPlainString().toUtf8();
|
||||||
|
} else {
|
||||||
|
//Generate a hash value base on QByteArray ?
|
||||||
|
bytes.append("@@QtXlsxRichString=");
|
||||||
|
for (int i=0; i<fragmentCount(); ++i) {
|
||||||
|
bytes.append("@Text");
|
||||||
|
bytes.append(m_fragmentTexts[i].toUtf8());
|
||||||
|
bytes.append("@Format");
|
||||||
|
if (m_fragmentFormats[i])
|
||||||
|
bytes.append(m_fragmentFormats[i]->fontKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rs->m_idKey = bytes;
|
||||||
|
rs->m_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_idKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const RichString &rs1, const RichString &rs2)
|
||||||
|
{
|
||||||
|
if (rs1.fragmentCount() != rs2.fragmentCount())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return rs1.idKey() == rs2.idKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const RichString &rs1, const RichString &rs2)
|
||||||
|
{
|
||||||
|
if (rs1.fragmentCount() != rs2.fragmentCount())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return rs1.idKey() != rs2.idKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \internal
|
||||||
|
*/
|
||||||
|
bool operator<(const RichString &rs1, const RichString &rs2)
|
||||||
|
{
|
||||||
|
return rs1.idKey() < rs2.idKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator ==(const RichString &rs1, const QString &rs2)
|
||||||
|
{
|
||||||
|
if (rs1.fragmentCount() == 1 && rs1.fragmentText(0) == rs2) //format == 0
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator !=(const RichString &rs1, const QString &rs2)
|
||||||
|
{
|
||||||
|
if (rs1.fragmentCount() == 1 && rs1.fragmentText(0) == rs2) //format == 0
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator ==(const QString &rs1, const RichString &rs2)
|
||||||
|
{
|
||||||
|
return rs2 == rs1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator !=(const QString &rs1, const RichString &rs2)
|
||||||
|
{
|
||||||
|
return rs2 != rs1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const RichString &rs, uint seed) Q_DECL_NOTHROW
|
||||||
|
{
|
||||||
|
return qHash(rs.idKey(), seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE_XLSX
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
** Copyright (c) 2013 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.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#ifndef XLSXRICHSTRING_P_H
|
||||||
|
#define XLSXRICHSTRING_P_H
|
||||||
|
|
||||||
|
#include "xlsxglobal.h"
|
||||||
|
#include "xlsxformat.h"
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE_XLSX
|
||||||
|
|
||||||
|
class RichString;
|
||||||
|
// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
|
||||||
|
XLSX_AUTOTEST_EXPORT uint qHash(const RichString &rs, uint seed = 0) Q_DECL_NOTHROW;
|
||||||
|
|
||||||
|
class XLSX_AUTOTEST_EXPORT RichString
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RichString();
|
||||||
|
explicit RichString(const QString text);
|
||||||
|
bool isRichString() const;
|
||||||
|
bool isEmtpy() const;
|
||||||
|
QString toPlainString() const;
|
||||||
|
|
||||||
|
int fragmentCount() const;
|
||||||
|
void addFragment(const QString &text, Format *format);
|
||||||
|
QString fragmentText(int index) const;
|
||||||
|
Format *fragmentFormat(int index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend XLSX_AUTOTEST_EXPORT uint qHash(const RichString &rs, uint seed) Q_DECL_NOTHROW;
|
||||||
|
friend XLSX_AUTOTEST_EXPORT bool operator==(const RichString &rs1, const RichString &rs2);
|
||||||
|
friend XLSX_AUTOTEST_EXPORT bool operator!=(const RichString &rs1, const RichString &rs2);
|
||||||
|
friend XLSX_AUTOTEST_EXPORT bool operator<(const RichString &rs1, const RichString &rs2);
|
||||||
|
|
||||||
|
QByteArray idKey() const;
|
||||||
|
|
||||||
|
QStringList m_fragmentTexts;
|
||||||
|
QList<Format *> m_fragmentFormats;
|
||||||
|
QByteArray m_idKey;
|
||||||
|
bool m_dirty;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
XLSX_AUTOTEST_EXPORT bool operator==(const RichString &rs1, const RichString &rs2);
|
||||||
|
XLSX_AUTOTEST_EXPORT bool operator!=(const RichString &rs1, const RichString &rs2);
|
||||||
|
XLSX_AUTOTEST_EXPORT bool operator<(const RichString &rs1, const RichString &rs2);
|
||||||
|
XLSX_AUTOTEST_EXPORT bool operator==(const RichString &rs1, const QString &rs2);
|
||||||
|
XLSX_AUTOTEST_EXPORT bool operator==(const QString &rs1, const RichString &rs2);
|
||||||
|
XLSX_AUTOTEST_EXPORT bool operator!=(const RichString &rs1, const QString &rs2);
|
||||||
|
XLSX_AUTOTEST_EXPORT bool operator!=(const QString &rs1, const RichString &rs2);
|
||||||
|
|
||||||
|
QT_END_NAMESPACE_XLSX
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QXlsx::RichString);
|
||||||
|
|
||||||
|
#endif // XLSXRICHSTRING_P_H
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
**
|
**
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
#include "xlsxrichstring_p.h"
|
||||||
#include "xlsxsharedstrings_p.h"
|
#include "xlsxsharedstrings_p.h"
|
||||||
#include "xlsxxmlwriter_p.h"
|
#include "xlsxxmlwriter_p.h"
|
||||||
#include "xlsxxmlreader_p.h"
|
#include "xlsxxmlreader_p.h"
|
||||||
@@ -44,6 +45,11 @@ int SharedStrings::count() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
int SharedStrings::addSharedString(const QString &string)
|
int SharedStrings::addSharedString(const QString &string)
|
||||||
|
{
|
||||||
|
return addSharedString(RichString(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
int SharedStrings::addSharedString(const RichString &string)
|
||||||
{
|
{
|
||||||
m_stringCount += 1;
|
m_stringCount += 1;
|
||||||
|
|
||||||
@@ -70,6 +76,11 @@ void SharedStrings::incRefByStringIndex(int idx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SharedStrings::removeSharedString(const QString &string)
|
void SharedStrings::removeSharedString(const QString &string)
|
||||||
|
{
|
||||||
|
removeSharedString(RichString(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharedStrings::removeSharedString(const RichString &string)
|
||||||
{
|
{
|
||||||
if (!m_stringTable.contains(string))
|
if (!m_stringTable.contains(string))
|
||||||
return;
|
return;
|
||||||
@@ -89,20 +100,25 @@ void SharedStrings::removeSharedString(const QString &string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int SharedStrings::getSharedStringIndex(const QString &string) const
|
int SharedStrings::getSharedStringIndex(const QString &string) const
|
||||||
|
{
|
||||||
|
return getSharedStringIndex(RichString(string));
|
||||||
|
}
|
||||||
|
|
||||||
|
int SharedStrings::getSharedStringIndex(const RichString &string) const
|
||||||
{
|
{
|
||||||
if (m_stringTable.contains(string))
|
if (m_stringTable.contains(string))
|
||||||
return m_stringTable[string].index;
|
return m_stringTable[string].index;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SharedStrings::getSharedString(int index) const
|
RichString SharedStrings::getSharedString(int index) const
|
||||||
{
|
{
|
||||||
if (index < m_stringList.count() && index >= 0)
|
if (index < m_stringList.count() && index >= 0)
|
||||||
return m_stringList[index];
|
return m_stringList[index];
|
||||||
return QString();
|
return RichString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList SharedStrings::getSharedStrings() const
|
QList<RichString> SharedStrings::getSharedStrings() const
|
||||||
{
|
{
|
||||||
return m_stringList;
|
return m_stringList;
|
||||||
}
|
}
|
||||||
@@ -117,16 +133,29 @@ void SharedStrings::saveToXmlFile(QIODevice *device) const
|
|||||||
writer.writeAttribute(QStringLiteral("count"), QString::number(m_stringCount));
|
writer.writeAttribute(QStringLiteral("count"), QString::number(m_stringCount));
|
||||||
writer.writeAttribute(QStringLiteral("uniqueCount"), QString::number(m_stringTable.size()));
|
writer.writeAttribute(QStringLiteral("uniqueCount"), QString::number(m_stringTable.size()));
|
||||||
|
|
||||||
foreach (QString string, m_stringList) {
|
foreach (RichString string, m_stringList) {
|
||||||
writer.writeStartElement(QStringLiteral("si"));
|
writer.writeStartElement(QStringLiteral("si"));
|
||||||
if (string.contains(QRegularExpression(QStringLiteral("^<r>"))) || string.contains(QRegularExpression(QStringLiteral("</r>$")))) {
|
if (string.isRichString()) {
|
||||||
//Rich text string,
|
//Rich text string
|
||||||
// writer.writeCharacters(string);
|
for (int i=0; i<string.fragmentCount(); ++i) {
|
||||||
|
if (string.fragmentFormat(i)) {
|
||||||
|
writer.writeStartElement(QStringLiteral("rPr"));
|
||||||
|
//:Todo
|
||||||
|
writer.writeEndElement();// rPr
|
||||||
|
}
|
||||||
|
writer.writeStartElement(QStringLiteral("t"));
|
||||||
|
writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
|
||||||
|
writer.writeCharacters(string.fragmentText(i));
|
||||||
|
writer.writeEndElement();// t
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
writer.writeStartElement(QStringLiteral("t"));
|
writer.writeStartElement(QStringLiteral("t"));
|
||||||
if (string.contains(QRegularExpression(QStringLiteral("^\\s"))) || string.contains(QRegularExpression(QStringLiteral("\\s$"))))
|
QString pString = string.toPlainString();
|
||||||
|
if (pString.contains(QRegularExpression(QStringLiteral("^\\s")))
|
||||||
|
|| pString.contains(QRegularExpression(QStringLiteral("\\s$")))) {
|
||||||
writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
|
writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
|
||||||
writer.writeCharacters(string);
|
}
|
||||||
|
writer.writeCharacters(pString);
|
||||||
writer.writeEndElement();//t
|
writer.writeEndElement();//t
|
||||||
}
|
}
|
||||||
writer.writeEndElement();//si
|
writer.writeEndElement();//si
|
||||||
@@ -146,6 +175,56 @@ QByteArray SharedStrings::saveToXmlData() const
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SharedStrings::readString(XmlStreamReader &reader)
|
||||||
|
{
|
||||||
|
Q_ASSERT(reader.name() == QLatin1String("si"));
|
||||||
|
|
||||||
|
RichString richString;
|
||||||
|
|
||||||
|
while (!(reader.name() == QLatin1String("si") && reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||||||
|
reader.readNextStartElement();
|
||||||
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||||
|
if (reader.name() == QLatin1String("r"))
|
||||||
|
readRichStringPart(reader, richString);
|
||||||
|
else if (reader.name() == QLatin1String("t"))
|
||||||
|
readPlainStringPart(reader, richString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = m_stringList.size();
|
||||||
|
m_stringTable[richString] = XlsxSharedStringInfo(idx, 0);
|
||||||
|
m_stringList.append(richString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharedStrings::readRichStringPart(XmlStreamReader &reader, RichString &richString)
|
||||||
|
{
|
||||||
|
Q_ASSERT(reader.name() == QLatin1String("r"));
|
||||||
|
|
||||||
|
QString text;
|
||||||
|
Format *format=0;
|
||||||
|
while (!(reader.name() == QLatin1String("r") && reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||||||
|
reader.readNextStartElement();
|
||||||
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||||
|
if (reader.name() == QLatin1String("rPr")) {
|
||||||
|
//:Todo
|
||||||
|
} else if (reader.name() == QLatin1String("t")) {
|
||||||
|
text = reader.readElementText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
richString.addFragment(text, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SharedStrings::readPlainStringPart(XmlStreamReader &reader, RichString &richString)
|
||||||
|
{
|
||||||
|
Q_ASSERT(reader.name() == QLatin1String("t"));
|
||||||
|
|
||||||
|
//QXmlStreamAttributes attributes = reader.attributes();
|
||||||
|
|
||||||
|
QString text = reader.readElementText();
|
||||||
|
richString.addFragment(text, 0);
|
||||||
|
}
|
||||||
|
|
||||||
bool SharedStrings::loadFromXmlFile(QIODevice *device)
|
bool SharedStrings::loadFromXmlFile(QIODevice *device)
|
||||||
{
|
{
|
||||||
XmlStreamReader reader(device);
|
XmlStreamReader reader(device);
|
||||||
@@ -157,15 +236,7 @@ bool SharedStrings::loadFromXmlFile(QIODevice *device)
|
|||||||
QXmlStreamAttributes attributes = reader.attributes();
|
QXmlStreamAttributes attributes = reader.attributes();
|
||||||
count = attributes.value(QLatin1String("uniqueCount")).toString().toInt();
|
count = attributes.value(QLatin1String("uniqueCount")).toString().toInt();
|
||||||
} else if (reader.name() == QLatin1String("si")) {
|
} else if (reader.name() == QLatin1String("si")) {
|
||||||
if (reader.readNextStartElement()) {
|
readString(reader);
|
||||||
if (reader.name() == QLatin1String("t")) {
|
|
||||||
// QXmlStreamAttributes attributes = reader.attributes();
|
|
||||||
QString string = reader.readElementText();
|
|
||||||
int idx = m_stringList.size();
|
|
||||||
m_stringTable[string] = XlsxSharedStringInfo(idx, 0);
|
|
||||||
m_stringList.append(string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#define XLSXSHAREDSTRINGS_H
|
#define XLSXSHAREDSTRINGS_H
|
||||||
|
|
||||||
#include "xlsxglobal.h"
|
#include "xlsxglobal.h"
|
||||||
|
#include "xlsxrichstring_p.h"
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
@@ -34,6 +35,9 @@ class QIODevice;
|
|||||||
|
|
||||||
namespace QXlsx {
|
namespace QXlsx {
|
||||||
|
|
||||||
|
class XmlStreamReader;
|
||||||
|
class RichString;
|
||||||
|
|
||||||
class XlsxSharedStringInfo
|
class XlsxSharedStringInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -53,12 +57,15 @@ public:
|
|||||||
int count() const;
|
int count() const;
|
||||||
|
|
||||||
int addSharedString(const QString &string);
|
int addSharedString(const QString &string);
|
||||||
|
int addSharedString(const RichString &string);
|
||||||
void removeSharedString(const QString &string);
|
void removeSharedString(const QString &string);
|
||||||
|
void removeSharedString(const RichString &string);
|
||||||
void incRefByStringIndex(int idx);
|
void incRefByStringIndex(int idx);
|
||||||
|
|
||||||
int getSharedStringIndex(const QString &string) const;
|
int getSharedStringIndex(const QString &string) const;
|
||||||
QString getSharedString(int index) const;
|
int getSharedStringIndex(const RichString &string) const;
|
||||||
QStringList getSharedStrings() const;
|
RichString getSharedString(int index) const;
|
||||||
|
QList<RichString> getSharedStrings() const;
|
||||||
|
|
||||||
void saveToXmlFile(QIODevice *device) const;
|
void saveToXmlFile(QIODevice *device) const;
|
||||||
QByteArray saveToXmlData() const;
|
QByteArray saveToXmlData() const;
|
||||||
@@ -66,8 +73,12 @@ public:
|
|||||||
bool loadFromXmlData(const QByteArray &data);
|
bool loadFromXmlData(const QByteArray &data);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHash<QString, XlsxSharedStringInfo> m_stringTable; //for fast lookup
|
void readString(XmlStreamReader &reader); // <si>
|
||||||
QStringList m_stringList;
|
void readRichStringPart(XmlStreamReader &reader, RichString &rich); // <r>
|
||||||
|
void readPlainStringPart(XmlStreamReader &reader, RichString &rich); // <v>
|
||||||
|
|
||||||
|
QHash<RichString, XlsxSharedStringInfo> m_stringTable; //for fast lookup
|
||||||
|
QList<RichString> m_stringList;
|
||||||
int m_stringCount;
|
int m_stringCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
**
|
**
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
#include "xlsxrichstring_p.h"
|
||||||
#include "xlsxworksheet.h"
|
#include "xlsxworksheet.h"
|
||||||
#include "xlsxworksheet_p.h"
|
#include "xlsxworksheet_p.h"
|
||||||
#include "xlsxworkbook.h"
|
#include "xlsxworkbook.h"
|
||||||
@@ -1163,7 +1164,21 @@ void WorksheetPrivate::writeCellData(XmlStreamWriter &writer, int row, int col,
|
|||||||
} else if (cell->dataType() == Cell::InlineString) {
|
} else if (cell->dataType() == Cell::InlineString) {
|
||||||
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("inlineStr"));
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("inlineStr"));
|
||||||
writer.writeStartElement(QStringLiteral("is"));
|
writer.writeStartElement(QStringLiteral("is"));
|
||||||
writer.writeTextElement(QStringLiteral("t"), cell->value().toString());
|
if (cell->isRichString()) {
|
||||||
|
//Rich text string
|
||||||
|
RichString string = cell->d_ptr->richString;
|
||||||
|
for (int i=0; i<string.fragmentCount(); ++i) {
|
||||||
|
writer.writeStartElement(QStringLiteral("rPr"));
|
||||||
|
//:Todo
|
||||||
|
writer.writeEndElement();// rPr
|
||||||
|
writer.writeStartElement(QStringLiteral("t"));
|
||||||
|
writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
|
||||||
|
writer.writeCharacters(string.fragmentText(i));
|
||||||
|
writer.writeEndElement();// t
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
writer.writeTextElement(QStringLiteral("t"), cell->value().toString());
|
||||||
|
}
|
||||||
writer.writeEndElement();//is
|
writer.writeEndElement();//is
|
||||||
} else if (cell->dataType() == Cell::Numeric){
|
} else if (cell->dataType() == Cell::Numeric){
|
||||||
double value = cell->value().toDouble();
|
double value = cell->value().toDouble();
|
||||||
@@ -1874,8 +1889,10 @@ void WorksheetPrivate::readSheetData(XmlStreamReader &reader)
|
|||||||
if (reader.name() == QLatin1String("v")) {
|
if (reader.name() == QLatin1String("v")) {
|
||||||
int sst_idx = reader.readElementText().toInt();
|
int sst_idx = reader.readElementText().toInt();
|
||||||
sharedStrings()->incRefByStringIndex(sst_idx);
|
sharedStrings()->incRefByStringIndex(sst_idx);
|
||||||
QString value = sharedStrings()->getSharedString(sst_idx);
|
RichString rs = sharedStrings()->getSharedString(sst_idx);
|
||||||
QSharedPointer<Cell> data(new Cell(value ,Cell::String, format, q));
|
QSharedPointer<Cell> data(new Cell(rs.toPlainString() ,Cell::String, format, q));
|
||||||
|
if (rs.isRichString())
|
||||||
|
data->d_ptr->richString = rs;
|
||||||
cellTable[pos.x()][pos.y()] = QSharedPointer<Cell>(data);
|
cellTable[pos.x()][pos.y()] = QSharedPointer<Cell>(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1884,6 +1901,7 @@ void WorksheetPrivate::readSheetData(XmlStreamReader &reader)
|
|||||||
while (!(reader.name() == QLatin1String("c") && reader.tokenType() == QXmlStreamReader::EndElement)) {
|
while (!(reader.name() == QLatin1String("c") && reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||||||
reader.readNextStartElement();
|
reader.readNextStartElement();
|
||||||
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||||||
|
//:Todo, add rich text read support
|
||||||
if (reader.name() == QLatin1String("t")) {
|
if (reader.name() == QLatin1String("t")) {
|
||||||
QString value = reader.readElementText();
|
QString value = reader.readElementText();
|
||||||
QSharedPointer<Cell> data(new Cell(value, Cell::InlineString, format, q));
|
QSharedPointer<Cell> data(new Cell(value, Cell::InlineString, format, q));
|
||||||
|
|||||||
+2
-1
@@ -9,4 +9,5 @@ SUBDIRS=\
|
|||||||
document \
|
document \
|
||||||
sharedstrings \
|
sharedstrings \
|
||||||
styles \
|
styles \
|
||||||
format
|
format \
|
||||||
|
richstring
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
QT += testlib xlsx xlsx-private
|
||||||
|
CONFIG += testcase
|
||||||
|
DEFINES += XLSX_TEST
|
||||||
|
|
||||||
|
TARGET = tst_richstringtest
|
||||||
|
CONFIG += console
|
||||||
|
CONFIG -= app_bundle
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
SOURCES += tst_richstringtest.cpp
|
||||||
|
DEFINES += SRCDIR=\\\"$$PWD/\\\"
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#include "private/xlsxrichstring_p.h"
|
||||||
|
#include <QtTest>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
class RichstringTest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
RichstringTest();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void testEqual();
|
||||||
|
};
|
||||||
|
|
||||||
|
RichstringTest::RichstringTest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void RichstringTest::testEqual()
|
||||||
|
{
|
||||||
|
QXlsx::RichString rs;
|
||||||
|
rs.addFragment("Hello", 0);
|
||||||
|
rs.addFragment(" RichText", 0);
|
||||||
|
|
||||||
|
QXlsx::RichString rs2;
|
||||||
|
rs2.addFragment("Hello", 0);
|
||||||
|
rs2.addFragment(" Qt!", 0);
|
||||||
|
|
||||||
|
QXlsx::RichString rs3;
|
||||||
|
rs3.addFragment("Hello", 0);
|
||||||
|
rs3.addFragment(" Qt!", 0);
|
||||||
|
|
||||||
|
QVERIFY2(rs2 != rs, "Failure");
|
||||||
|
QVERIFY2(rs2 == rs3, "Failure");
|
||||||
|
QVERIFY2(rs2 != QStringLiteral("Hello Qt!"), "Failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_APPLESS_MAIN(RichstringTest)
|
||||||
|
|
||||||
|
#include "tst_richstringtest.moc"
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "private/xlsxsharedstrings_p.h"
|
#include "private/xlsxsharedstrings_p.h"
|
||||||
|
#include "private/xlsxrichstring_p.h"
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtTest>
|
#include <QtTest>
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
@@ -27,6 +28,19 @@ void SharedStringsTest::testAddSharedString()
|
|||||||
QXlsx::SharedStrings sst;
|
QXlsx::SharedStrings sst;
|
||||||
sst.addSharedString("Hello Qt!");
|
sst.addSharedString("Hello Qt!");
|
||||||
sst.addSharedString("Xlsx Writer");
|
sst.addSharedString("Xlsx Writer");
|
||||||
|
|
||||||
|
QXlsx::RichString rs;
|
||||||
|
rs.addFragment("Hello", 0);
|
||||||
|
rs.addFragment(" RichText", 0);
|
||||||
|
sst.addSharedString(rs);
|
||||||
|
|
||||||
|
for (int i=0; i<3; ++i) {
|
||||||
|
QXlsx::RichString rs2;
|
||||||
|
rs2.addFragment("Hello", 0);
|
||||||
|
rs2.addFragment(" Qt!", 0);
|
||||||
|
sst.addSharedString(rs2);
|
||||||
|
}
|
||||||
|
|
||||||
sst.addSharedString("Hello World");
|
sst.addSharedString("Hello World");
|
||||||
sst.addSharedString("Hello Qt!");
|
sst.addSharedString("Hello Qt!");
|
||||||
|
|
||||||
@@ -46,8 +60,8 @@ void SharedStringsTest::testAddSharedString()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QCOMPARE(count, 4);
|
QCOMPARE(count, 8);
|
||||||
QCOMPARE(uniqueCount, 3);
|
QCOMPARE(uniqueCount, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SharedStringsTest::testRemoveSharedString()
|
void SharedStringsTest::testRemoveSharedString()
|
||||||
@@ -88,6 +102,17 @@ void SharedStringsTest::testLoadXmlData()
|
|||||||
QXlsx::SharedStrings sst;
|
QXlsx::SharedStrings sst;
|
||||||
sst.addSharedString("Hello Qt!");
|
sst.addSharedString("Hello Qt!");
|
||||||
sst.addSharedString("Xlsx Writer");
|
sst.addSharedString("Xlsx Writer");
|
||||||
|
|
||||||
|
QXlsx::RichString rs;
|
||||||
|
rs.addFragment("Hello", 0);
|
||||||
|
rs.addFragment(" RichText", 0);
|
||||||
|
sst.addSharedString(rs);
|
||||||
|
for (int i=0; i<3; ++i) {
|
||||||
|
QXlsx::RichString rs2;
|
||||||
|
rs2.addFragment("Hello", 0);
|
||||||
|
rs2.addFragment(" Qt!", 0);
|
||||||
|
sst.addSharedString(rs2);
|
||||||
|
}
|
||||||
sst.addSharedString("Hello World");
|
sst.addSharedString("Hello World");
|
||||||
sst.addSharedString("Hello Qt!");
|
sst.addSharedString("Hello Qt!");
|
||||||
QByteArray xmlData = sst.saveToXmlData();
|
QByteArray xmlData = sst.saveToXmlData();
|
||||||
@@ -95,10 +120,11 @@ void SharedStringsTest::testLoadXmlData()
|
|||||||
QSharedPointer<QXlsx::SharedStrings> sst2(new QXlsx::SharedStrings);
|
QSharedPointer<QXlsx::SharedStrings> sst2(new QXlsx::SharedStrings);
|
||||||
sst2->loadFromXmlData(xmlData);
|
sst2->loadFromXmlData(xmlData);
|
||||||
|
|
||||||
QCOMPARE(sst2->getSharedString(0), QStringLiteral("Hello Qt!"));
|
QCOMPARE(sst2->getSharedString(0).toPlainString(), QStringLiteral("Hello Qt!"));
|
||||||
QCOMPARE(sst2->getSharedString(2), QStringLiteral("Hello World"));
|
QCOMPARE(sst2->getSharedString(2), rs);
|
||||||
|
QCOMPARE(sst2->getSharedString(4).toPlainString(), QStringLiteral("Hello World"));
|
||||||
QCOMPARE(sst2->getSharedStringIndex("Hello Qt!"), 0);
|
QCOMPARE(sst2->getSharedStringIndex("Hello Qt!"), 0);
|
||||||
QCOMPARE(sst2->getSharedStringIndex("Hello World"), 2);
|
QCOMPARE(sst2->getSharedStringIndex("Hello World"), 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
QTEST_APPLESS_MAIN(SharedStringsTest)
|
QTEST_APPLESS_MAIN(SharedStringsTest)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "private/xlsxworksheet_p.h"
|
#include "private/xlsxworksheet_p.h"
|
||||||
#include "private/xlsxxmlreader_p.h"
|
#include "private/xlsxxmlreader_p.h"
|
||||||
#include "private/xlsxsharedstrings_p.h"
|
#include "private/xlsxsharedstrings_p.h"
|
||||||
|
#include "private/xlsxrichstring_p.h"
|
||||||
|
|
||||||
class WorksheetTest : public QObject
|
class WorksheetTest : public QObject
|
||||||
{
|
{
|
||||||
@@ -116,7 +117,7 @@ void WorksheetTest::testWriteCells()
|
|||||||
QVERIFY2(xmldata.contains("<c r=\"A5\" t=\"str\"><f>44+33</f><v>0</v></c>"), "formula");
|
QVERIFY2(xmldata.contains("<c r=\"A5\" t=\"str\"><f>44+33</f><v>0</v></c>"), "formula");
|
||||||
QVERIFY2(xmldata.contains("<c r=\"B5\" t=\"str\"><f>44+33</f><v>77</v></c>"), "formula");
|
QVERIFY2(xmldata.contains("<c r=\"B5\" t=\"str\"><f>44+33</f><v>77</v></c>"), "formula");
|
||||||
|
|
||||||
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(0), QStringLiteral("Hello"));
|
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(0).toPlainString(), QStringLiteral("Hello"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorksheetTest::testWriteHyperlinks()
|
void WorksheetTest::testWriteHyperlinks()
|
||||||
@@ -136,11 +137,11 @@ void WorksheetTest::testWriteHyperlinks()
|
|||||||
QVERIFY2(xmldata.contains("<hyperlink ref=\"D1\" r:id=\"rId4\"/>"), "mail");
|
QVERIFY2(xmldata.contains("<hyperlink ref=\"D1\" r:id=\"rId4\"/>"), "mail");
|
||||||
QVERIFY2(xmldata.contains("<hyperlink ref=\"E1\" r:id=\"rId5\"/>"), "mail with subject");
|
QVERIFY2(xmldata.contains("<hyperlink ref=\"E1\" r:id=\"rId5\"/>"), "mail with subject");
|
||||||
|
|
||||||
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(0), QStringLiteral("http://qt-project.org"));
|
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(0).toPlainString(), QStringLiteral("http://qt-project.org"));
|
||||||
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(1), QStringLiteral("http://qt-project.org/abc"));
|
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(1).toPlainString(), QStringLiteral("http://qt-project.org/abc"));
|
||||||
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(2), QStringLiteral("http://qt-project.org/abc.html#test"));
|
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(2).toPlainString(), QStringLiteral("http://qt-project.org/abc.html#test"));
|
||||||
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(3), QStringLiteral("xyz@debao.me"));
|
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(3).toPlainString(), QStringLiteral("xyz@debao.me"));
|
||||||
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(4), QStringLiteral("xyz@debao.me?subject=Test"));
|
QCOMPARE(sheet.d_ptr->sharedStrings()->getSharedString(4).toPlainString(), QStringLiteral("xyz@debao.me?subject=Test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorksheetTest::testWriteDataValidations()
|
void WorksheetTest::testWriteDataValidations()
|
||||||
|
|||||||
Reference in New Issue
Block a user