File indexing completed on 2024-05-26 03:50:52

0001 // xlsxsharedstrings.cpp
0002 
0003 #include <QtGlobal>
0004 #include <QXmlStreamWriter>
0005 #include <QXmlStreamReader>
0006 #include <QDir>
0007 #include <QFile>
0008 #include <QDebug>
0009 #include <QBuffer>
0010 
0011 #include "xlsxrichstring.h"
0012 #include "xlsxsharedstrings_p.h"
0013 #include "xlsxutility_p.h"
0014 #include "xlsxformat_p.h"
0015 #include "xlsxcolor_p.h"
0016 
0017 QT_BEGIN_NAMESPACE_XLSX
0018 
0019 /*
0020  * Note that, when we open an existing .xlsx file (broken file?),
0021  * duplicated string items may exist in the shared string table.
0022  *
0023  * In such case, the size of stringList will larger than stringTable.
0024  * Duplicated items can be removed once we loaded all the worksheets.
0025  */
0026 
0027 SharedStrings::SharedStrings(CreateFlag flag)
0028     :AbstractOOXmlFile(flag)
0029 {
0030     m_stringCount = 0;
0031 }
0032 
0033 int SharedStrings::count() const
0034 {
0035     return m_stringCount;
0036 }
0037 
0038 bool SharedStrings::isEmpty() const
0039 {
0040     return m_stringList.isEmpty();
0041 }
0042 
0043 int SharedStrings::addSharedString(const QString &string)
0044 {
0045     return addSharedString(RichString(string));
0046 }
0047 
0048 int SharedStrings::addSharedString(const RichString &string)
0049 {
0050     m_stringCount += 1;
0051 
0052     auto it = m_stringTable.find(string);
0053     if (it != m_stringTable.end()) {
0054         it->count += 1;
0055         return it->index;
0056     }
0057 
0058     int index = m_stringList.size();
0059     m_stringTable[string] = XlsxSharedStringInfo(index);
0060     m_stringList.append(string);
0061     return index;
0062 }
0063 
0064 void SharedStrings::incRefByStringIndex(int idx)
0065 {
0066     if (idx <0 || idx >= m_stringList.size()) {
0067         qDebug("SharedStrings: invlid index");
0068         return;
0069     }
0070 
0071     addSharedString(m_stringList[idx]);
0072 }
0073 
0074 /*
0075  * Broken, don't use.
0076  */
0077 void SharedStrings::removeSharedString(const QString &string)
0078 {
0079     removeSharedString(RichString(string));
0080 }
0081 
0082 /*
0083  * Broken, don't use.
0084  */
0085 void SharedStrings::removeSharedString(const RichString &string)
0086 {
0087     auto it = m_stringTable.find(string);
0088     if (it == m_stringTable.end())
0089         return;
0090 
0091     m_stringCount -= 1;
0092 
0093     it->count -= 1;
0094 
0095     if (it->count <= 0) {
0096         for (int i=it->index+1; i<m_stringList.size(); ++i)
0097             m_stringTable[m_stringList[i]].index -= 1;
0098 
0099         m_stringList.removeAt(it->index);
0100         m_stringTable.remove(string);
0101     }
0102 }
0103 
0104 int SharedStrings::getSharedStringIndex(const QString &string) const
0105 {
0106     return getSharedStringIndex(RichString(string));
0107 }
0108 
0109 int SharedStrings::getSharedStringIndex(const RichString &string) const
0110 {
0111     auto it = m_stringTable.constFind(string);
0112     if (it != m_stringTable.constEnd())
0113         return it->index;
0114     return -1;
0115 }
0116 
0117 RichString SharedStrings::getSharedString(int index) const
0118 {
0119     if (index < m_stringList.count() && index >= 0)
0120         return m_stringList[index];
0121     return RichString();
0122 }
0123 
0124 QList<RichString> SharedStrings::getSharedStrings() const
0125 {
0126     return m_stringList;
0127 }
0128 
0129 void SharedStrings::writeRichStringPart_rPr(QXmlStreamWriter &writer, const Format &format) const
0130 {
0131     if (!format.hasFontData())
0132         return;
0133 
0134     if (format.fontBold())
0135         writer.writeEmptyElement(QStringLiteral("b"));
0136     if (format.fontItalic())
0137         writer.writeEmptyElement(QStringLiteral("i"));
0138     if (format.fontStrikeOut())
0139         writer.writeEmptyElement(QStringLiteral("strike"));
0140     if (format.fontOutline())
0141         writer.writeEmptyElement(QStringLiteral("outline"));
0142     if (format.boolProperty(FormatPrivate::P_Font_Shadow))
0143         writer.writeEmptyElement(QStringLiteral("shadow"));
0144     if (format.hasProperty(FormatPrivate::P_Font_Underline)) {
0145         Format::FontUnderline u = format.fontUnderline();
0146         if (u != Format::FontUnderlineNone) {
0147             writer.writeEmptyElement(QStringLiteral("u"));
0148             if (u== Format::FontUnderlineDouble)
0149                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double"));
0150             else if (u == Format::FontUnderlineSingleAccounting)
0151                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting"));
0152             else if (u == Format::FontUnderlineDoubleAccounting)
0153                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting"));
0154         }
0155     }
0156     if (format.hasProperty(FormatPrivate::P_Font_Script)) {
0157         Format::FontScript s = format.fontScript();
0158         if (s != Format::FontScriptNormal) {
0159             writer.writeEmptyElement(QStringLiteral("vertAlign"));
0160             if (s == Format::FontScriptSuper)
0161                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript"));
0162             else
0163                 writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript"));
0164         }
0165     }
0166 
0167     if (format.hasProperty(FormatPrivate::P_Font_Size)) {
0168         writer.writeEmptyElement(QStringLiteral("sz"));
0169         writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize()));
0170     }
0171 
0172     if (format.hasProperty(FormatPrivate::P_Font_Color)) {
0173         XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>();
0174         color.saveToXml(writer);
0175     }
0176 
0177     if (!format.fontName().isEmpty()) {
0178         writer.writeEmptyElement(QStringLiteral("rFont"));
0179         writer.writeAttribute(QStringLiteral("val"), format.fontName());
0180     }
0181     if (format.hasProperty(FormatPrivate::P_Font_Family)) {
0182         writer.writeEmptyElement(QStringLiteral("family"));
0183         writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family)));
0184     }
0185 
0186     if (format.hasProperty(FormatPrivate::P_Font_Scheme)) {
0187         writer.writeEmptyElement(QStringLiteral("scheme"));
0188         writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme));
0189     }
0190 }
0191 
0192 void SharedStrings::saveToXmlFile(QIODevice *device) const
0193 {
0194     QXmlStreamWriter writer(device);
0195 
0196     if (m_stringList.size() != m_stringTable.size()) {
0197         //Duplicated string items exist in m_stringList
0198         //Clean up can not be done here, as the indices
0199         //have been used when we save the worksheets part.
0200     }
0201 
0202     writer.writeStartDocument(QStringLiteral("1.0"), true);
0203     writer.writeStartElement(QStringLiteral("sst"));
0204     writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
0205     writer.writeAttribute(QStringLiteral("count"), QString::number(m_stringCount));
0206     writer.writeAttribute(QStringLiteral("uniqueCount"), QString::number(m_stringList.size()));
0207 
0208     for (const RichString &string : m_stringList) {
0209         writer.writeStartElement(QStringLiteral("si"));
0210         if (string.isRichString()) {
0211             //Rich text string
0212             for (int i=0; i<string.fragmentCount(); ++i) {
0213                 writer.writeStartElement(QStringLiteral("r"));
0214                 if (string.fragmentFormat(i).hasFontData()) {
0215                     writer.writeStartElement(QStringLiteral("rPr"));
0216                     writeRichStringPart_rPr(writer, string.fragmentFormat(i));
0217                     writer.writeEndElement();// rPr
0218                 }
0219                 writer.writeStartElement(QStringLiteral("t"));
0220                 if (isSpaceReserveNeeded(string.fragmentText(i)))
0221                     writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
0222                 writer.writeCharacters(string.fragmentText(i));
0223                 writer.writeEndElement();// t
0224 
0225                 writer.writeEndElement(); //r
0226             }
0227         } else {
0228             writer.writeStartElement(QStringLiteral("t"));
0229             QString pString = string.toPlainString();
0230             if (isSpaceReserveNeeded(pString))
0231                 writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
0232             writer.writeCharacters(pString);
0233             writer.writeEndElement();//t
0234         }
0235         writer.writeEndElement();//si
0236     }
0237 
0238     writer.writeEndElement(); //sst
0239     writer.writeEndDocument();
0240 }
0241 
0242 void SharedStrings::readString(QXmlStreamReader &reader)
0243 {
0244     Q_ASSERT(reader.name() == QLatin1String("si"));
0245 
0246     RichString richString;
0247 
0248     while (!reader.atEnd() && !(reader.name() == QLatin1String("si") && reader.tokenType() == QXmlStreamReader::EndElement)) {
0249         reader.readNextStartElement();
0250         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0251             if (reader.name() == QLatin1String("r"))
0252                 readRichStringPart(reader, richString);
0253             else if (reader.name() == QLatin1String("t"))
0254                 readPlainStringPart(reader, richString);
0255         }
0256     }
0257 
0258     int idx = m_stringList.size();
0259     m_stringTable[richString] = XlsxSharedStringInfo(idx, 0);
0260     m_stringList.append(richString);
0261 }
0262 
0263 void SharedStrings::readRichStringPart(QXmlStreamReader &reader, RichString &richString)
0264 {
0265     Q_ASSERT(reader.name() == QLatin1String("r"));
0266 
0267     QString text;
0268     Format format;
0269     while (!reader.atEnd() && !(reader.name() == QLatin1String("r") && reader.tokenType() == QXmlStreamReader::EndElement)) {
0270         reader.readNextStartElement();
0271         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0272             if (reader.name() == QLatin1String("rPr")) {
0273                 format = readRichStringPart_rPr(reader);
0274             } else if (reader.name() == QLatin1String("t")) {
0275                 text = reader.readElementText();
0276             }
0277         }
0278     }
0279     richString.addFragment(text, format);
0280 }
0281 
0282 void SharedStrings::readPlainStringPart(QXmlStreamReader &reader, RichString &richString)
0283 {
0284     Q_ASSERT(reader.name() == QLatin1String("t"));
0285 
0286     //QXmlStreamAttributes attributes = reader.attributes();
0287 
0288     // NOTICE: CHECK POINT
0289     QString text = reader.readElementText();
0290     richString.addFragment(text, Format());
0291 }
0292 
0293 Format SharedStrings::readRichStringPart_rPr(QXmlStreamReader &reader)
0294 {
0295     Q_ASSERT(reader.name() == QLatin1String("rPr"));
0296     Format format;
0297     while (!reader.atEnd() && !(reader.name() == QLatin1String("rPr") && reader.tokenType() == QXmlStreamReader::EndElement)) {
0298         reader.readNextStartElement();
0299         if (reader.tokenType() == QXmlStreamReader::StartElement) {
0300             QXmlStreamAttributes attributes = reader.attributes();
0301             if (reader.name() == QLatin1String("rFont")) {
0302                 format.setFontName(attributes.value(QLatin1String("val")).toString());
0303             } else if (reader.name() == QLatin1String("charset")) {
0304                 format.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toInt());
0305             } else if (reader.name() == QLatin1String("family")) {
0306                 format.setProperty(FormatPrivate::P_Font_Family, attributes.value(QLatin1String("val")).toInt());
0307             } else if (reader.name() == QLatin1String("b")) {
0308                 format.setFontBold(true);
0309             } else if (reader.name() == QLatin1String("i")) {
0310                 format.setFontItalic(true);
0311             } else if (reader.name() == QLatin1String("strike")) {
0312                 format.setFontStrikeOut(true);
0313             } else if (reader.name() == QLatin1String("outline")) {
0314                 format.setFontOutline(true);
0315             } else if (reader.name() == QLatin1String("shadow")) {
0316                 format.setProperty(FormatPrivate::P_Font_Shadow, true);
0317             } else if (reader.name() == QLatin1String("condense")) {
0318                 format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toInt());
0319             } else if (reader.name() == QLatin1String("extend")) {
0320                 format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toInt());
0321             } else if (reader.name() == QLatin1String("color")) {
0322                 XlsxColor color;
0323                 color.loadFromXml(reader);
0324                 format.setProperty(FormatPrivate::P_Font_Color, color);
0325             } else if (reader.name() == QLatin1String("sz")) {
0326                 format.setFontSize(attributes.value(QLatin1String("val")).toInt());
0327             } else if (reader.name() == QLatin1String("u")) {
0328                 QString value = attributes.value(QLatin1String("val")).toString();
0329                 if (value == QLatin1String("double"))
0330                     format.setFontUnderline(Format::FontUnderlineDouble);
0331                 else if (value == QLatin1String("doubleAccounting"))
0332                     format.setFontUnderline(Format::FontUnderlineDoubleAccounting);
0333                 else if (value == QLatin1String("singleAccounting"))
0334                     format.setFontUnderline(Format::FontUnderlineSingleAccounting);
0335                 else
0336                     format.setFontUnderline(Format::FontUnderlineSingle);
0337             } else if (reader.name() == QLatin1String("vertAlign")) {
0338                 QString value = attributes.value(QLatin1String("val")).toString();
0339                 if (value == QLatin1String("superscript"))
0340                     format.setFontScript(Format::FontScriptSuper);
0341                 else if (value == QLatin1String("subscript"))
0342                     format.setFontScript(Format::FontScriptSub);
0343             } else if (reader.name() == QLatin1String("scheme")) {
0344                 format.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString());
0345             }
0346         }
0347     }
0348     return format;
0349 }
0350 
0351 bool SharedStrings::loadFromXmlFile(QIODevice *device)
0352 {
0353     QXmlStreamReader reader(device);
0354     int count = 0;
0355     bool hasUniqueCountAttr=true;
0356     while (!reader.atEnd()) {
0357          QXmlStreamReader::TokenType token = reader.readNext();
0358          if (token == QXmlStreamReader::StartElement) {
0359              if (reader.name() == QLatin1String("sst")) {
0360                  QXmlStreamAttributes attributes = reader.attributes();
0361                  if ((hasUniqueCountAttr = attributes.hasAttribute(QLatin1String("uniqueCount"))))
0362                      count = attributes.value(QLatin1String("uniqueCount")).toInt();
0363              } else if (reader.name() == QLatin1String("si")) {
0364                  readString(reader);
0365              }
0366          }
0367     }
0368 
0369     if (hasUniqueCountAttr && m_stringList.size() != count) {
0370         qDebug("Error: Shared string count");
0371         return false;
0372     }
0373 
0374     if (m_stringList.size() != m_stringTable.size()) {
0375         //qDebug("Warning: Duplicated items exist in shared string table.");
0376         //Nothing we can do here, as indices of the strings will be used when loading sheets.
0377     }
0378 
0379     return true;
0380 }
0381 
0382 QT_END_NAMESPACE_XLSX