|
|
|
/****************************************************************************
|
|
|
|
** 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"
|
|
|
|
#include "xlsxsharedstrings_p.h"
|
|
|
|
#include "xlsxxmlwriter_p.h"
|
|
|
|
#include "xlsxxmlreader_p.h"
|
|
|
|
#include "xlsxutility_p.h"
|
|
|
|
#include "xlsxformat_p.h"
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFile>
|
|
|
|
#include <QRegularExpression>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QBuffer>
|
|
|
|
|
|
|
|
namespace QXlsx {
|
|
|
|
|
|
|
|
SharedStrings::SharedStrings()
|
|
|
|
{
|
|
|
|
m_stringCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SharedStrings::count() const
|
|
|
|
{
|
|
|
|
return m_stringCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
int SharedStrings::addSharedString(const QString &string)
|
|
|
|
{
|
|
|
|
return addSharedString(RichString(string));
|
|
|
|
}
|
|
|
|
|
|
|
|
int SharedStrings::addSharedString(const RichString &string)
|
|
|
|
{
|
|
|
|
m_stringCount += 1;
|
|
|
|
|
|
|
|
if (m_stringTable.contains(string)) {
|
|
|
|
XlsxSharedStringInfo &item = m_stringTable[string];
|
|
|
|
item.count += 1;
|
|
|
|
return item.index;
|
|
|
|
}
|
|
|
|
|
|
|
|
int index = m_stringTable.size();
|
|
|
|
m_stringTable[string] = XlsxSharedStringInfo(index);
|
|
|
|
m_stringList.append(string);
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SharedStrings::incRefByStringIndex(int idx)
|
|
|
|
{
|
|
|
|
if (idx <0 || idx >= m_stringList.size()) {
|
|
|
|
qDebug("SharedStrings: invlid index");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
addSharedString(m_stringList[idx]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SharedStrings::removeSharedString(const QString &string)
|
|
|
|
{
|
|
|
|
removeSharedString(RichString(string));
|
|
|
|
}
|
|
|
|
|
|
|
|
void SharedStrings::removeSharedString(const RichString &string)
|
|
|
|
{
|
|
|
|
if (!m_stringTable.contains(string))
|
|
|
|
return;
|
|
|
|
|
|
|
|
m_stringCount -= 1;
|
|
|
|
|
|
|
|
XlsxSharedStringInfo &item = m_stringTable[string];
|
|
|
|
item.count -= 1;
|
|
|
|
|
|
|
|
if (item.count <= 0) {
|
|
|
|
for (int i=item.index+1; i<m_stringList.size(); ++i)
|
|
|
|
m_stringTable[m_stringList[i]].index -= 1;
|
|
|
|
|
|
|
|
m_stringList.removeAt(item.index);
|
|
|
|
m_stringTable.remove(string);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int SharedStrings::getSharedStringIndex(const QString &string) const
|
|
|
|
{
|
|
|
|
return getSharedStringIndex(RichString(string));
|
|
|
|
}
|
|
|
|
|
|
|
|
int SharedStrings::getSharedStringIndex(const RichString &string) const
|
|
|
|
{
|
|
|
|
if (m_stringTable.contains(string))
|
|
|
|
return m_stringTable[string].index;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
RichString SharedStrings::getSharedString(int index) const
|
|
|
|
{
|
|
|
|
if (index < m_stringList.count() && index >= 0)
|
|
|
|
return m_stringList[index];
|
|
|
|
return RichString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<RichString> SharedStrings::getSharedStrings() const
|
|
|
|
{
|
|
|
|
return m_stringList;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SharedStrings::writeRichStringPart_rPr(XmlStreamWriter &writer, Format *format) const
|
|
|
|
{
|
|
|
|
if (!format)
|
|
|
|
return;
|
|
|
|
|
|
|
|
XlsxFormatFontData *font = &format->d_ptr->fontData;
|
|
|
|
|
|
|
|
if (font->bold)
|
|
|
|
writer.writeEmptyElement(QStringLiteral("b"));
|
|
|
|
if (font->italic)
|
|
|
|
writer.writeEmptyElement(QStringLiteral("i"));
|
|
|
|
if (font->strikeOut)
|
|
|
|
writer.writeEmptyElement(QStringLiteral("strike"));
|
|
|
|
if (font->outline)
|
|
|
|
writer.writeEmptyElement(QStringLiteral("outline"));
|
|
|
|
if (font->shadow)
|
|
|
|
writer.writeEmptyElement(QStringLiteral("shadow"));
|
|
|
|
if (font->underline != Format::FontUnderlineNone) {
|
|
|
|
writer.writeEmptyElement(QStringLiteral("u"));
|
|
|
|
if (font->underline == Format::FontUnderlineDouble)
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double"));
|
|
|
|
else if (font->underline == Format::FontUnderlineSingleAccounting)
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting"));
|
|
|
|
else if (font->underline == Format::FontUnderlineDoubleAccounting)
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting"));
|
|
|
|
}
|
|
|
|
if (font->scirpt != Format::FontScriptNormal) {
|
|
|
|
writer.writeEmptyElement(QStringLiteral("vertAlign"));
|
|
|
|
if (font->scirpt == Format::FontScriptSuper)
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript"));
|
|
|
|
else
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript"));
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.writeEmptyElement(QStringLiteral("sz"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(font->size));
|
|
|
|
|
|
|
|
if (font->color.isValid()) {
|
|
|
|
writer.writeEmptyElement(QStringLiteral("color"));
|
|
|
|
QString color = font->color.name();
|
|
|
|
writer.writeAttribute(QStringLiteral("rgb"), QStringLiteral("FF")+color.mid(1));//remove #
|
|
|
|
} else if (!font->themeColor.isEmpty()) {
|
|
|
|
writer.writeEmptyElement(QStringLiteral("color"));
|
|
|
|
QStringList themes = font->themeColor.split(QLatin1Char(':'));
|
|
|
|
writer.writeAttribute(QStringLiteral("theme"), themes[0]);
|
|
|
|
if (!themes[1].isEmpty())
|
|
|
|
writer.writeAttribute(QStringLiteral("tint"), themes[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.writeEmptyElement(QStringLiteral("rFont"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), font->name);
|
|
|
|
writer.writeEmptyElement(QStringLiteral("family"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(font->family));
|
|
|
|
if (font->name == QLatin1String("Calibri")) {
|
|
|
|
writer.writeEmptyElement(QStringLiteral("scheme"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), font->scheme);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SharedStrings::saveToXmlFile(QIODevice *device) const
|
|
|
|
{
|
|
|
|
XmlStreamWriter writer(device);
|
|
|
|
|
|
|
|
writer.writeStartDocument(QStringLiteral("1.0"), true);
|
|
|
|
writer.writeStartElement(QStringLiteral("sst"));
|
|
|
|
writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
|
|
|
|
writer.writeAttribute(QStringLiteral("count"), QString::number(m_stringCount));
|
|
|
|
writer.writeAttribute(QStringLiteral("uniqueCount"), QString::number(m_stringTable.size()));
|
|
|
|
|
|
|
|
foreach (RichString string, m_stringList) {
|
|
|
|
writer.writeStartElement(QStringLiteral("si"));
|
|
|
|
if (string.isRichString()) {
|
|
|
|
//Rich text string
|
|
|
|
for (int i=0; i<string.fragmentCount(); ++i) {
|
|
|
|
writer.writeStartElement(QStringLiteral("r"));
|
|
|
|
if (string.fragmentFormat(i)) {
|
|
|
|
writer.writeStartElement(QStringLiteral("rPr"));
|
|
|
|
writeRichStringPart_rPr(writer, string.fragmentFormat(i));
|
|
|
|
writer.writeEndElement();// rPr
|
|
|
|
}
|
|
|
|
writer.writeStartElement(QStringLiteral("t"));
|
|
|
|
if (string.fragmentText(i).contains(QRegularExpression(QStringLiteral("^\\s")))
|
|
|
|
|| string.fragmentText(i).contains(QRegularExpression(QStringLiteral("\\s$")))) {
|
|
|
|
writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
|
|
|
|
}
|
|
|
|
writer.writeCharacters(string.fragmentText(i));
|
|
|
|
writer.writeEndElement();// t
|
|
|
|
|
|
|
|
writer.writeEndElement(); //r
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
writer.writeStartElement(QStringLiteral("t"));
|
|
|
|
QString pString = string.toPlainString();
|
|
|
|
if (pString.contains(QRegularExpression(QStringLiteral("^\\s")))
|
|
|
|
|| pString.contains(QRegularExpression(QStringLiteral("\\s$")))) {
|
|
|
|
writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
|
|
|
|
}
|
|
|
|
writer.writeCharacters(pString);
|
|
|
|
writer.writeEndElement();//t
|
|
|
|
}
|
|
|
|
writer.writeEndElement();//si
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.writeEndElement(); //sst
|
|
|
|
writer.writeEndDocument();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray SharedStrings::saveToXmlData() const
|
|
|
|
{
|
|
|
|
QByteArray data;
|
|
|
|
QBuffer buffer(&data);
|
|
|
|
buffer.open(QIODevice::WriteOnly);
|
|
|
|
saveToXmlFile(&buffer);
|
|
|
|
|
|
|
|
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")) {
|
|
|
|
format = readRichStringPart_rPr(reader, richString);
|
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
Format *SharedStrings::readRichStringPart_rPr(XmlStreamReader &reader, RichString &richString)
|
|
|
|
{
|
|
|
|
Q_ASSERT(reader.name() == QLatin1String("rPr"));
|
|
|
|
Format *format = richString.createFormat();
|
|
|
|
while (!(reader.name() == QLatin1String("rPr") && reader.tokenType() == QXmlStreamReader::EndElement)) {
|
|
|
|
reader.readNextStartElement();
|
|
|
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
|
|
|
QXmlStreamAttributes attributes = reader.attributes();
|
|
|
|
if (reader.name() == QLatin1String("rFont")) {
|
|
|
|
format->setFontName(attributes.value(QLatin1String("val")).toString());
|
|
|
|
} else if (reader.name() == QLatin1String("charset")) {
|
|
|
|
format->d_ptr->fontData.charset = attributes.value(QLatin1String("val")).toString().toInt();
|
|
|
|
} else if (reader.name() == QLatin1String("family")) {
|
|
|
|
format->d_ptr->fontData.family = attributes.value(QLatin1String("val")).toString().toInt();
|
|
|
|
} else if (reader.name() == QLatin1String("b")) {
|
|
|
|
format->setFontBold(true);
|
|
|
|
} else if (reader.name() == QLatin1String("i")) {
|
|
|
|
format->setFontItalic(true);
|
|
|
|
} else if (reader.name() == QLatin1String("strike")) {
|
|
|
|
format->setFontStrikeOut(true);
|
|
|
|
} else if (reader.name() == QLatin1String("outline")) {
|
|
|
|
format->setFontOutline(true);
|
|
|
|
} else if (reader.name() == QLatin1String("shadow")) {
|
|
|
|
format->d_ptr->fontData.shadow = true;
|
|
|
|
} else if (reader.name() == QLatin1String("condense")) {
|
|
|
|
format->d_ptr->fontData.condense = attributes.value(QLatin1String("val")).toString().toInt();
|
|
|
|
} else if (reader.name() == QLatin1String("extend")) {
|
|
|
|
format->d_ptr->fontData.extend = attributes.value(QLatin1String("val")).toString().toInt();
|
|
|
|
} else if (reader.name() == QLatin1String("color")) {
|
|
|
|
if (attributes.hasAttribute(QLatin1String("rgb"))) {
|
|
|
|
QString colorString = attributes.value(QLatin1String("rgb")).toString();
|
|
|
|
format->setFontColor(fromARGBString(colorString));
|
|
|
|
} else if (attributes.hasAttribute(QLatin1String("indexed"))) {
|
|
|
|
// color = getColorByIndex(attributes.value(QLatin1String("indexed")).toString().toInt());
|
|
|
|
} else if (attributes.hasAttribute(QLatin1String("theme"))) {
|
|
|
|
QString theme = attributes.value(QLatin1String("theme")).toString();
|
|
|
|
QString tint = attributes.value(QLatin1String("tint")).toString();
|
|
|
|
format->d_ptr->fontData.themeColor = theme + QLatin1Char(':') + tint;
|
|
|
|
}
|
|
|
|
} else if (reader.name() == QLatin1String("sz")) {
|
|
|
|
format->setFontSize(attributes.value(QLatin1String("val")).toString().toInt());
|
|
|
|
} else if (reader.name() == QLatin1String("u")) {
|
|
|
|
QString value = attributes.value(QLatin1String("val")).toString();
|
|
|
|
if (value == QLatin1String("double"))
|
|
|
|
format->setFontUnderline(Format::FontUnderlineDouble);
|
|
|
|
else if (value == QLatin1String("doubleAccounting"))
|
|
|
|
format->setFontUnderline(Format::FontUnderlineDoubleAccounting);
|
|
|
|
else if (value == QLatin1String("singleAccounting"))
|
|
|
|
format->setFontUnderline(Format::FontUnderlineSingleAccounting);
|
|
|
|
else
|
|
|
|
format->setFontUnderline(Format::FontUnderlineSingle);
|
|
|
|
} else if (reader.name() == QLatin1String("vertAlign")) {
|
|
|
|
QString value = attributes.value(QLatin1String("val")).toString();
|
|
|
|
if (value == QLatin1String("superscript"))
|
|
|
|
format->setFontScript(Format::FontScriptSuper);
|
|
|
|
else if (value == QLatin1String("subscript"))
|
|
|
|
format->setFontScript(Format::FontScriptSub);
|
|
|
|
} else if (reader.name() == QLatin1String("scheme")) {
|
|
|
|
format->d_ptr->fontData.scheme = attributes.value(QLatin1String("val")).toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return format;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SharedStrings::loadFromXmlFile(QIODevice *device)
|
|
|
|
{
|
|
|
|
XmlStreamReader reader(device);
|
|
|
|
int count = 0;
|
|
|
|
while(!reader.atEnd()) {
|
|
|
|
QXmlStreamReader::TokenType token = reader.readNext();
|
|
|
|
if (token == QXmlStreamReader::StartElement) {
|
|
|
|
if (reader.name() == QLatin1String("sst")) {
|
|
|
|
QXmlStreamAttributes attributes = reader.attributes();
|
|
|
|
count = attributes.value(QLatin1String("uniqueCount")).toString().toInt();
|
|
|
|
} else if (reader.name() == QLatin1String("si")) {
|
|
|
|
readString(reader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_stringTable.size() != count) {
|
|
|
|
qDebug("Error: Shared string count");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SharedStrings::loadFromXmlData(const QByteArray &data)
|
|
|
|
{
|
|
|
|
QBuffer buffer;
|
|
|
|
buffer.setData(data);
|
|
|
|
buffer.open(QIODevice::ReadOnly);
|
|
|
|
|
|
|
|
return loadFromXmlFile(&buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
} //namespace
|