andre@1: /**************************************************************************** andre@1: ** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> andre@1: ** All right reserved. andre@1: ** andre@1: ** Permission is hereby granted, free of charge, to any person obtaining andre@1: ** a copy of this software and associated documentation files (the andre@1: ** "Software"), to deal in the Software without restriction, including andre@1: ** without limitation the rights to use, copy, modify, merge, publish, andre@1: ** distribute, sublicense, and/or sell copies of the Software, and to andre@1: ** permit persons to whom the Software is furnished to do so, subject to andre@1: ** the following conditions: andre@1: ** andre@1: ** The above copyright notice and this permission notice shall be andre@1: ** included in all copies or substantial portions of the Software. andre@1: ** andre@1: ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, andre@1: ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF andre@1: ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND andre@1: ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE andre@1: ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION andre@1: ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION andre@1: ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. andre@1: ** andre@1: ****************************************************************************/ andre@1: #include "xlsxrichstring.h" andre@1: #include "xlsxsharedstrings_p.h" andre@1: #include "xlsxutility_p.h" andre@1: #include "xlsxformat_p.h" andre@1: #include "xlsxcolor_p.h" andre@1: #include <QXmlStreamWriter> andre@1: #include <QXmlStreamReader> andre@1: #include <QDir> andre@1: #include <QFile> andre@1: #include <QDebug> andre@1: #include <QBuffer> andre@1: andre@1: namespace QXlsx { andre@1: andre@1: /* andre@1: * Note that, when we open an existing .xlsx file (broken file?), andre@1: * duplicated string items may exist in the shared string table. andre@1: * andre@1: * In such case, the size of stringList will larger than stringTable. andre@1: * Duplicated items can be removed once we loaded all the worksheets. andre@1: */ andre@1: andre@1: SharedStrings::SharedStrings(CreateFlag flag) andre@1: :AbstractOOXmlFile(flag) andre@1: { andre@1: m_stringCount = 0; andre@1: } andre@1: andre@1: int SharedStrings::count() const andre@1: { andre@1: return m_stringCount; andre@1: } andre@1: andre@1: bool SharedStrings::isEmpty() const andre@1: { andre@1: return m_stringList.isEmpty(); andre@1: } andre@1: andre@1: int SharedStrings::addSharedString(const QString &string) andre@1: { andre@1: return addSharedString(RichString(string)); andre@1: } andre@1: andre@1: int SharedStrings::addSharedString(const RichString &string) andre@1: { andre@1: m_stringCount += 1; andre@1: andre@1: if (m_stringTable.contains(string)) { andre@1: XlsxSharedStringInfo &item = m_stringTable[string]; andre@1: item.count += 1; andre@1: return item.index; andre@1: } andre@1: andre@1: int index = m_stringList.size(); andre@1: m_stringTable[string] = XlsxSharedStringInfo(index); andre@1: m_stringList.append(string); andre@1: return index; andre@1: } andre@1: andre@1: void SharedStrings::incRefByStringIndex(int idx) andre@1: { andre@1: if (idx <0 || idx >= m_stringList.size()) { andre@1: qDebug("SharedStrings: invlid index"); andre@1: return; andre@1: } andre@1: andre@1: addSharedString(m_stringList[idx]); andre@1: } andre@1: andre@1: /* andre@1: * Broken, don't use. andre@1: */ andre@1: void SharedStrings::removeSharedString(const QString &string) andre@1: { andre@1: removeSharedString(RichString(string)); andre@1: } andre@1: andre@1: /* andre@1: * Broken, don't use. andre@1: */ andre@1: void SharedStrings::removeSharedString(const RichString &string) andre@1: { andre@1: if (!m_stringTable.contains(string)) andre@1: return; andre@1: andre@1: m_stringCount -= 1; andre@1: andre@1: XlsxSharedStringInfo &item = m_stringTable[string]; andre@1: item.count -= 1; andre@1: andre@1: if (item.count <= 0) { andre@1: for (int i=item.index+1; i<m_stringList.size(); ++i) andre@1: m_stringTable[m_stringList[i]].index -= 1; andre@1: andre@1: m_stringList.removeAt(item.index); andre@1: m_stringTable.remove(string); andre@1: } andre@1: } andre@1: andre@1: int SharedStrings::getSharedStringIndex(const QString &string) const andre@1: { andre@1: return getSharedStringIndex(RichString(string)); andre@1: } andre@1: andre@1: int SharedStrings::getSharedStringIndex(const RichString &string) const andre@1: { andre@1: if (m_stringTable.contains(string)) andre@1: return m_stringTable[string].index; andre@1: return -1; andre@1: } andre@1: andre@1: RichString SharedStrings::getSharedString(int index) const andre@1: { andre@1: if (index < m_stringList.count() && index >= 0) andre@1: return m_stringList[index]; andre@1: return RichString(); andre@1: } andre@1: andre@1: QList<RichString> SharedStrings::getSharedStrings() const andre@1: { andre@1: return m_stringList; andre@1: } andre@1: andre@1: void SharedStrings::writeRichStringPart_rPr(QXmlStreamWriter &writer, const Format &format) const andre@1: { andre@1: if (!format.hasFontData()) andre@1: return; andre@1: andre@1: if (format.fontBold()) andre@1: writer.writeEmptyElement(QStringLiteral("b")); andre@1: if (format.fontItalic()) andre@1: writer.writeEmptyElement(QStringLiteral("i")); andre@1: if (format.fontStrikeOut()) andre@1: writer.writeEmptyElement(QStringLiteral("strike")); andre@1: if (format.fontOutline()) andre@1: writer.writeEmptyElement(QStringLiteral("outline")); andre@1: if (format.boolProperty(FormatPrivate::P_Font_Shadow)) andre@1: writer.writeEmptyElement(QStringLiteral("shadow")); andre@1: if (format.hasProperty(FormatPrivate::P_Font_Underline)) { andre@1: Format::FontUnderline u = format.fontUnderline(); andre@1: if (u != Format::FontUnderlineNone) { andre@1: writer.writeEmptyElement(QStringLiteral("u")); andre@1: if (u== Format::FontUnderlineDouble) andre@1: writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double")); andre@1: else if (u == Format::FontUnderlineSingleAccounting) andre@1: writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting")); andre@1: else if (u == Format::FontUnderlineDoubleAccounting) andre@1: writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting")); andre@1: } andre@1: } andre@1: if (format.hasProperty(FormatPrivate::P_Font_Script)) { andre@1: Format::FontScript s = format.fontScript(); andre@1: if (s != Format::FontScriptNormal) { andre@1: writer.writeEmptyElement(QStringLiteral("vertAlign")); andre@1: if (s == Format::FontScriptSuper) andre@1: writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript")); andre@1: else andre@1: writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript")); andre@1: } andre@1: } andre@1: andre@1: if (format.hasProperty(FormatPrivate::P_Font_Size)) { andre@1: writer.writeEmptyElement(QStringLiteral("sz")); andre@1: writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize())); andre@1: } andre@1: andre@1: if (format.hasProperty(FormatPrivate::P_Font_Color)) { andre@1: XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>(); andre@1: color.saveToXml(writer); andre@1: } andre@1: andre@1: if (!format.fontName().isEmpty()) { andre@1: writer.writeEmptyElement(QStringLiteral("rFont")); andre@1: writer.writeAttribute(QStringLiteral("val"), format.fontName()); andre@1: } andre@1: if (format.hasProperty(FormatPrivate::P_Font_Family)) { andre@1: writer.writeEmptyElement(QStringLiteral("family")); andre@1: writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family))); andre@1: } andre@1: andre@1: if (format.hasProperty(FormatPrivate::P_Font_Scheme)) { andre@1: writer.writeEmptyElement(QStringLiteral("scheme")); andre@1: writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme)); andre@1: } andre@1: } andre@1: andre@1: void SharedStrings::saveToXmlFile(QIODevice *device) const andre@1: { andre@1: QXmlStreamWriter writer(device); andre@1: andre@1: if (m_stringList.size() != m_stringTable.size()) { andre@1: //Duplicated string items exist in m_stringList andre@1: //Clean up can not be done here, as the indices andre@1: //have been used when we save the worksheets part. andre@1: } andre@1: andre@1: writer.writeStartDocument(QStringLiteral("1.0"), true); andre@1: writer.writeStartElement(QStringLiteral("sst")); andre@1: writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main")); andre@1: writer.writeAttribute(QStringLiteral("count"), QString::number(m_stringCount)); andre@1: writer.writeAttribute(QStringLiteral("uniqueCount"), QString::number(m_stringList.size())); andre@1: andre@1: foreach (RichString string, m_stringList) { andre@1: writer.writeStartElement(QStringLiteral("si")); andre@1: if (string.isRichString()) { andre@1: //Rich text string andre@1: for (int i=0; i<string.fragmentCount(); ++i) { andre@1: writer.writeStartElement(QStringLiteral("r")); andre@1: if (string.fragmentFormat(i).hasFontData()) { andre@1: writer.writeStartElement(QStringLiteral("rPr")); andre@1: writeRichStringPart_rPr(writer, string.fragmentFormat(i)); andre@1: writer.writeEndElement();// rPr andre@1: } andre@1: writer.writeStartElement(QStringLiteral("t")); andre@1: if (isSpaceReserveNeeded(string.fragmentText(i))) andre@1: writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve")); andre@1: writer.writeCharacters(string.fragmentText(i)); andre@1: writer.writeEndElement();// t andre@1: andre@1: writer.writeEndElement(); //r andre@1: } andre@1: } else { andre@1: writer.writeStartElement(QStringLiteral("t")); andre@1: QString pString = string.toPlainString(); andre@1: if (isSpaceReserveNeeded(pString)) andre@1: writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve")); andre@1: writer.writeCharacters(pString); andre@1: writer.writeEndElement();//t andre@1: } andre@1: writer.writeEndElement();//si andre@1: } andre@1: andre@1: writer.writeEndElement(); //sst andre@1: writer.writeEndDocument(); andre@1: } andre@1: andre@1: void SharedStrings::readString(QXmlStreamReader &reader) andre@1: { andre@1: Q_ASSERT(reader.name() == QLatin1String("si")); andre@1: andre@1: RichString richString; andre@1: andre@1: while (!reader.atEnd() && !(reader.name() == QLatin1String("si") && reader.tokenType() == QXmlStreamReader::EndElement)) { andre@1: reader.readNextStartElement(); andre@1: if (reader.tokenType() == QXmlStreamReader::StartElement) { andre@1: if (reader.name() == QLatin1String("r")) andre@1: readRichStringPart(reader, richString); andre@1: else if (reader.name() == QLatin1String("t")) andre@1: readPlainStringPart(reader, richString); andre@1: } andre@1: } andre@1: andre@1: int idx = m_stringList.size(); andre@1: m_stringTable[richString] = XlsxSharedStringInfo(idx, 0); andre@1: m_stringList.append(richString); andre@1: } andre@1: andre@1: void SharedStrings::readRichStringPart(QXmlStreamReader &reader, RichString &richString) andre@1: { andre@1: Q_ASSERT(reader.name() == QLatin1String("r")); andre@1: andre@1: QString text; andre@1: Format format; andre@1: while (!reader.atEnd() && !(reader.name() == QLatin1String("r") && reader.tokenType() == QXmlStreamReader::EndElement)) { andre@1: reader.readNextStartElement(); andre@1: if (reader.tokenType() == QXmlStreamReader::StartElement) { andre@1: if (reader.name() == QLatin1String("rPr")) { andre@1: format = readRichStringPart_rPr(reader); andre@1: } else if (reader.name() == QLatin1String("t")) { andre@1: text = reader.readElementText(); andre@1: } andre@1: } andre@1: } andre@1: richString.addFragment(text, format); andre@1: } andre@1: andre@1: void SharedStrings::readPlainStringPart(QXmlStreamReader &reader, RichString &richString) andre@1: { andre@1: Q_ASSERT(reader.name() == QLatin1String("t")); andre@1: andre@1: //QXmlStreamAttributes attributes = reader.attributes(); andre@1: andre@1: QString text = reader.readElementText(); andre@1: richString.addFragment(text, Format()); andre@1: } andre@1: andre@1: Format SharedStrings::readRichStringPart_rPr(QXmlStreamReader &reader) andre@1: { andre@1: Q_ASSERT(reader.name() == QLatin1String("rPr")); andre@1: Format format; andre@1: while (!reader.atEnd() && !(reader.name() == QLatin1String("rPr") && reader.tokenType() == QXmlStreamReader::EndElement)) { andre@1: reader.readNextStartElement(); andre@1: if (reader.tokenType() == QXmlStreamReader::StartElement) { andre@1: QXmlStreamAttributes attributes = reader.attributes(); andre@1: if (reader.name() == QLatin1String("rFont")) { andre@1: format.setFontName(attributes.value(QLatin1String("val")).toString()); andre@1: } else if (reader.name() == QLatin1String("charset")) { andre@1: format.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toString().toInt()); andre@1: } else if (reader.name() == QLatin1String("family")) { andre@1: format.setProperty(FormatPrivate::P_Font_Family, attributes.value(QLatin1String("val")).toString().toInt()); andre@1: } else if (reader.name() == QLatin1String("b")) { andre@1: format.setFontBold(true); andre@1: } else if (reader.name() == QLatin1String("i")) { andre@1: format.setFontItalic(true); andre@1: } else if (reader.name() == QLatin1String("strike")) { andre@1: format.setFontStrikeOut(true); andre@1: } else if (reader.name() == QLatin1String("outline")) { andre@1: format.setFontOutline(true); andre@1: } else if (reader.name() == QLatin1String("shadow")) { andre@1: format.setProperty(FormatPrivate::P_Font_Shadow, true); andre@1: } else if (reader.name() == QLatin1String("condense")) { andre@1: format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toString().toInt()); andre@1: } else if (reader.name() == QLatin1String("extend")) { andre@1: format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toString().toInt()); andre@1: } else if (reader.name() == QLatin1String("color")) { andre@1: XlsxColor color; andre@1: color.loadFromXml(reader); andre@1: format.setProperty(FormatPrivate::P_Font_Color, color); andre@1: } else if (reader.name() == QLatin1String("sz")) { andre@1: format.setFontSize(attributes.value(QLatin1String("val")).toString().toInt()); andre@1: } else if (reader.name() == QLatin1String("u")) { andre@1: QString value = attributes.value(QLatin1String("val")).toString(); andre@1: if (value == QLatin1String("double")) andre@1: format.setFontUnderline(Format::FontUnderlineDouble); andre@1: else if (value == QLatin1String("doubleAccounting")) andre@1: format.setFontUnderline(Format::FontUnderlineDoubleAccounting); andre@1: else if (value == QLatin1String("singleAccounting")) andre@1: format.setFontUnderline(Format::FontUnderlineSingleAccounting); andre@1: else andre@1: format.setFontUnderline(Format::FontUnderlineSingle); andre@1: } else if (reader.name() == QLatin1String("vertAlign")) { andre@1: QString value = attributes.value(QLatin1String("val")).toString(); andre@1: if (value == QLatin1String("superscript")) andre@1: format.setFontScript(Format::FontScriptSuper); andre@1: else if (value == QLatin1String("subscript")) andre@1: format.setFontScript(Format::FontScriptSub); andre@1: } else if (reader.name() == QLatin1String("scheme")) { andre@1: format.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString()); andre@1: } andre@1: } andre@1: } andre@1: return format; andre@1: } andre@1: andre@1: bool SharedStrings::loadFromXmlFile(QIODevice *device) andre@1: { andre@1: QXmlStreamReader reader(device); andre@1: int count = 0; andre@1: bool hasUniqueCountAttr=true; andre@1: while (!reader.atEnd()) { andre@1: QXmlStreamReader::TokenType token = reader.readNext(); andre@1: if (token == QXmlStreamReader::StartElement) { andre@1: if (reader.name() == QLatin1String("sst")) { andre@1: QXmlStreamAttributes attributes = reader.attributes(); andre@1: if ((hasUniqueCountAttr = attributes.hasAttribute(QLatin1String("uniqueCount")))) andre@1: count = attributes.value(QLatin1String("uniqueCount")).toString().toInt(); andre@1: } else if (reader.name() == QLatin1String("si")) { andre@1: readString(reader); andre@1: } andre@1: } andre@1: } andre@1: andre@1: if (hasUniqueCountAttr && m_stringList.size() != count) { andre@1: qDebug("Error: Shared string count"); andre@1: return false; andre@1: } andre@1: andre@1: if (m_stringList.size() != m_stringTable.size()) { andre@1: //qDebug("Warning: Duplicated items exist in shared string table."); andre@1: //Nothing we can do here, as indices of the strings will be used when loading sheets. andre@1: } andre@1: andre@1: return true; andre@1: } andre@1: andre@1: } //namespace