Mercurial > clickerconvert
diff src/xlsx/xlsxsharedstrings.cpp @ 1:93d3106bb9a4
Add qt xlsx library
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Tue, 22 Mar 2016 10:38:08 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxsharedstrings.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,400 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 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.h" +#include "xlsxsharedstrings_p.h" +#include "xlsxutility_p.h" +#include "xlsxformat_p.h" +#include "xlsxcolor_p.h" +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QDir> +#include <QFile> +#include <QDebug> +#include <QBuffer> + +namespace QXlsx { + +/* + * Note that, when we open an existing .xlsx file (broken file?), + * duplicated string items may exist in the shared string table. + * + * In such case, the size of stringList will larger than stringTable. + * Duplicated items can be removed once we loaded all the worksheets. + */ + +SharedStrings::SharedStrings(CreateFlag flag) + :AbstractOOXmlFile(flag) +{ + m_stringCount = 0; +} + +int SharedStrings::count() const +{ + return m_stringCount; +} + +bool SharedStrings::isEmpty() const +{ + return m_stringList.isEmpty(); +} + +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_stringList.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]); +} + +/* + * Broken, don't use. + */ +void SharedStrings::removeSharedString(const QString &string) +{ + removeSharedString(RichString(string)); +} + +/* + * Broken, don't use. + */ +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(QXmlStreamWriter &writer, const Format &format) const +{ + if (!format.hasFontData()) + return; + + if (format.fontBold()) + writer.writeEmptyElement(QStringLiteral("b")); + if (format.fontItalic()) + writer.writeEmptyElement(QStringLiteral("i")); + if (format.fontStrikeOut()) + writer.writeEmptyElement(QStringLiteral("strike")); + if (format.fontOutline()) + writer.writeEmptyElement(QStringLiteral("outline")); + if (format.boolProperty(FormatPrivate::P_Font_Shadow)) + writer.writeEmptyElement(QStringLiteral("shadow")); + if (format.hasProperty(FormatPrivate::P_Font_Underline)) { + Format::FontUnderline u = format.fontUnderline(); + if (u != Format::FontUnderlineNone) { + writer.writeEmptyElement(QStringLiteral("u")); + if (u== Format::FontUnderlineDouble) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double")); + else if (u == Format::FontUnderlineSingleAccounting) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting")); + else if (u == Format::FontUnderlineDoubleAccounting) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting")); + } + } + if (format.hasProperty(FormatPrivate::P_Font_Script)) { + Format::FontScript s = format.fontScript(); + if (s != Format::FontScriptNormal) { + writer.writeEmptyElement(QStringLiteral("vertAlign")); + if (s == Format::FontScriptSuper) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript")); + else + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript")); + } + } + + if (format.hasProperty(FormatPrivate::P_Font_Size)) { + writer.writeEmptyElement(QStringLiteral("sz")); + writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize())); + } + + if (format.hasProperty(FormatPrivate::P_Font_Color)) { + XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>(); + color.saveToXml(writer); + } + + if (!format.fontName().isEmpty()) { + writer.writeEmptyElement(QStringLiteral("rFont")); + writer.writeAttribute(QStringLiteral("val"), format.fontName()); + } + if (format.hasProperty(FormatPrivate::P_Font_Family)) { + writer.writeEmptyElement(QStringLiteral("family")); + writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family))); + } + + if (format.hasProperty(FormatPrivate::P_Font_Scheme)) { + writer.writeEmptyElement(QStringLiteral("scheme")); + writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme)); + } +} + +void SharedStrings::saveToXmlFile(QIODevice *device) const +{ + QXmlStreamWriter writer(device); + + if (m_stringList.size() != m_stringTable.size()) { + //Duplicated string items exist in m_stringList + //Clean up can not be done here, as the indices + //have been used when we save the worksheets part. + } + + 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_stringList.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).hasFontData()) { + writer.writeStartElement(QStringLiteral("rPr")); + writeRichStringPart_rPr(writer, string.fragmentFormat(i)); + writer.writeEndElement();// rPr + } + writer.writeStartElement(QStringLiteral("t")); + if (isSpaceReserveNeeded(string.fragmentText(i))) + 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 (isSpaceReserveNeeded(pString)) + writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve")); + writer.writeCharacters(pString); + writer.writeEndElement();//t + } + writer.writeEndElement();//si + } + + writer.writeEndElement(); //sst + writer.writeEndDocument(); +} + +void SharedStrings::readString(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("si")); + + RichString richString; + + while (!reader.atEnd() && !(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(QXmlStreamReader &reader, RichString &richString) +{ + Q_ASSERT(reader.name() == QLatin1String("r")); + + QString text; + Format format; + while (!reader.atEnd() && !(reader.name() == QLatin1String("r") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("rPr")) { + format = readRichStringPart_rPr(reader); + } else if (reader.name() == QLatin1String("t")) { + text = reader.readElementText(); + } + } + } + richString.addFragment(text, format); +} + +void SharedStrings::readPlainStringPart(QXmlStreamReader &reader, RichString &richString) +{ + Q_ASSERT(reader.name() == QLatin1String("t")); + + //QXmlStreamAttributes attributes = reader.attributes(); + + QString text = reader.readElementText(); + richString.addFragment(text, Format()); +} + +Format SharedStrings::readRichStringPart_rPr(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("rPr")); + Format format; + while (!reader.atEnd() && !(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.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("family")) { + format.setProperty(FormatPrivate::P_Font_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.setProperty(FormatPrivate::P_Font_Shadow, true); + } else if (reader.name() == QLatin1String("condense")) { + format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("extend")) { + format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("color")) { + XlsxColor color; + color.loadFromXml(reader); + format.setProperty(FormatPrivate::P_Font_Color, color); + } 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.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString()); + } + } + } + return format; +} + +bool SharedStrings::loadFromXmlFile(QIODevice *device) +{ + QXmlStreamReader reader(device); + int count = 0; + bool hasUniqueCountAttr=true; + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("sst")) { + QXmlStreamAttributes attributes = reader.attributes(); + if ((hasUniqueCountAttr = attributes.hasAttribute(QLatin1String("uniqueCount")))) + count = attributes.value(QLatin1String("uniqueCount")).toString().toInt(); + } else if (reader.name() == QLatin1String("si")) { + readString(reader); + } + } + } + + if (hasUniqueCountAttr && m_stringList.size() != count) { + qDebug("Error: Shared string count"); + return false; + } + + if (m_stringList.size() != m_stringTable.size()) { + //qDebug("Warning: Duplicated items exist in shared string table."); + //Nothing we can do here, as indices of the strings will be used when loading sheets. + } + + return true; +} + +} //namespace