File indexing completed on 2025-03-23 03:32:50
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