xlsxsharedstrings.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. // xlsxsharedstrings.cpp
  2. #include <QtGlobal>
  3. #include <QXmlStreamWriter>
  4. #include <QXmlStreamReader>
  5. #include <QDir>
  6. #include <QFile>
  7. #include <QDebug>
  8. #include <QBuffer>
  9. #include "xlsxrichstring.h"
  10. #include "xlsxsharedstrings_p.h"
  11. #include "xlsxutility_p.h"
  12. #include "xlsxformat_p.h"
  13. #include "xlsxcolor_p.h"
  14. QT_BEGIN_NAMESPACE_XLSX
  15. /*
  16. * Note that, when we open an existing .xlsx file (broken file?),
  17. * duplicated string items may exist in the shared string table.
  18. *
  19. * In such case, the size of stringList will larger than stringTable.
  20. * Duplicated items can be removed once we loaded all the worksheets.
  21. */
  22. SharedStrings::SharedStrings(CreateFlag flag)
  23. :AbstractOOXmlFile(flag)
  24. {
  25. m_stringCount = 0;
  26. }
  27. int SharedStrings::count() const
  28. {
  29. return m_stringCount;
  30. }
  31. bool SharedStrings::isEmpty() const
  32. {
  33. return m_stringList.isEmpty();
  34. }
  35. int SharedStrings::addSharedString(const QString &string)
  36. {
  37. return addSharedString(RichString(string));
  38. }
  39. int SharedStrings::addSharedString(const RichString &string)
  40. {
  41. m_stringCount += 1;
  42. auto it = m_stringTable.find(string);
  43. if (it != m_stringTable.end()) {
  44. it->count += 1;
  45. return it->index;
  46. }
  47. int index = m_stringList.size();
  48. m_stringTable[string] = XlsxSharedStringInfo(index);
  49. m_stringList.append(string);
  50. return index;
  51. }
  52. void SharedStrings::incRefByStringIndex(int idx)
  53. {
  54. if (idx <0 || idx >= m_stringList.size()) {
  55. qDebug("SharedStrings: invlid index");
  56. return;
  57. }
  58. addSharedString(m_stringList[idx]);
  59. }
  60. /*
  61. * Broken, don't use.
  62. */
  63. void SharedStrings::removeSharedString(const QString &string)
  64. {
  65. removeSharedString(RichString(string));
  66. }
  67. /*
  68. * Broken, don't use.
  69. */
  70. void SharedStrings::removeSharedString(const RichString &string)
  71. {
  72. auto it = m_stringTable.find(string);
  73. if (it == m_stringTable.end())
  74. return;
  75. m_stringCount -= 1;
  76. it->count -= 1;
  77. if (it->count <= 0) {
  78. for (int i=it->index+1; i<m_stringList.size(); ++i)
  79. m_stringTable[m_stringList[i]].index -= 1;
  80. m_stringList.removeAt(it->index);
  81. m_stringTable.remove(string);
  82. }
  83. }
  84. int SharedStrings::getSharedStringIndex(const QString &string) const
  85. {
  86. return getSharedStringIndex(RichString(string));
  87. }
  88. int SharedStrings::getSharedStringIndex(const RichString &string) const
  89. {
  90. auto it = m_stringTable.constFind(string);
  91. if (it != m_stringTable.constEnd())
  92. return it->index;
  93. return -1;
  94. }
  95. RichString SharedStrings::getSharedString(int index) const
  96. {
  97. if (index < m_stringList.count() && index >= 0)
  98. return m_stringList[index];
  99. return RichString();
  100. }
  101. QList<RichString> SharedStrings::getSharedStrings() const
  102. {
  103. return m_stringList;
  104. }
  105. void SharedStrings::writeRichStringPart_rPr(QXmlStreamWriter &writer, const Format &format) const
  106. {
  107. if (!format.hasFontData())
  108. return;
  109. if (format.fontBold())
  110. writer.writeEmptyElement(QStringLiteral("b"));
  111. if (format.fontItalic())
  112. writer.writeEmptyElement(QStringLiteral("i"));
  113. if (format.fontStrikeOut())
  114. writer.writeEmptyElement(QStringLiteral("strike"));
  115. if (format.fontOutline())
  116. writer.writeEmptyElement(QStringLiteral("outline"));
  117. if (format.boolProperty(FormatPrivate::P_Font_Shadow))
  118. writer.writeEmptyElement(QStringLiteral("shadow"));
  119. if (format.hasProperty(FormatPrivate::P_Font_Underline)) {
  120. Format::FontUnderline u = format.fontUnderline();
  121. if (u != Format::FontUnderlineNone) {
  122. writer.writeEmptyElement(QStringLiteral("u"));
  123. if (u== Format::FontUnderlineDouble)
  124. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double"));
  125. else if (u == Format::FontUnderlineSingleAccounting)
  126. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting"));
  127. else if (u == Format::FontUnderlineDoubleAccounting)
  128. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting"));
  129. }
  130. }
  131. if (format.hasProperty(FormatPrivate::P_Font_Script)) {
  132. Format::FontScript s = format.fontScript();
  133. if (s != Format::FontScriptNormal) {
  134. writer.writeEmptyElement(QStringLiteral("vertAlign"));
  135. if (s == Format::FontScriptSuper)
  136. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript"));
  137. else
  138. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript"));
  139. }
  140. }
  141. if (format.hasProperty(FormatPrivate::P_Font_Size)) {
  142. writer.writeEmptyElement(QStringLiteral("sz"));
  143. writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize()));
  144. }
  145. if (format.hasProperty(FormatPrivate::P_Font_Color)) {
  146. XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>();
  147. color.saveToXml(writer);
  148. }
  149. if (!format.fontName().isEmpty()) {
  150. writer.writeEmptyElement(QStringLiteral("rFont"));
  151. writer.writeAttribute(QStringLiteral("val"), format.fontName());
  152. }
  153. if (format.hasProperty(FormatPrivate::P_Font_Family)) {
  154. writer.writeEmptyElement(QStringLiteral("family"));
  155. writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family)));
  156. }
  157. if (format.hasProperty(FormatPrivate::P_Font_Scheme)) {
  158. writer.writeEmptyElement(QStringLiteral("scheme"));
  159. writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme));
  160. }
  161. }
  162. void SharedStrings::saveToXmlFile(QIODevice *device) const
  163. {
  164. QXmlStreamWriter writer(device);
  165. if (m_stringList.size() != m_stringTable.size()) {
  166. //Duplicated string items exist in m_stringList
  167. //Clean up can not be done here, as the indices
  168. //have been used when we save the worksheets part.
  169. }
  170. writer.writeStartDocument(QStringLiteral("1.0"), true);
  171. writer.writeStartElement(QStringLiteral("sst"));
  172. writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
  173. writer.writeAttribute(QStringLiteral("count"), QString::number(m_stringCount));
  174. writer.writeAttribute(QStringLiteral("uniqueCount"), QString::number(m_stringList.size()));
  175. for (const RichString &string : m_stringList) {
  176. writer.writeStartElement(QStringLiteral("si"));
  177. if (string.isRichString()) {
  178. //Rich text string
  179. for (int i=0; i<string.fragmentCount(); ++i) {
  180. writer.writeStartElement(QStringLiteral("r"));
  181. if (string.fragmentFormat(i).hasFontData()) {
  182. writer.writeStartElement(QStringLiteral("rPr"));
  183. writeRichStringPart_rPr(writer, string.fragmentFormat(i));
  184. writer.writeEndElement();// rPr
  185. }
  186. writer.writeStartElement(QStringLiteral("t"));
  187. if (isSpaceReserveNeeded(string.fragmentText(i)))
  188. writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
  189. writer.writeCharacters(string.fragmentText(i));
  190. writer.writeEndElement();// t
  191. writer.writeEndElement(); //r
  192. }
  193. } else {
  194. writer.writeStartElement(QStringLiteral("t"));
  195. QString pString = string.toPlainString();
  196. if (isSpaceReserveNeeded(pString))
  197. writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
  198. writer.writeCharacters(pString);
  199. writer.writeEndElement();//t
  200. }
  201. writer.writeEndElement();//si
  202. }
  203. writer.writeEndElement(); //sst
  204. writer.writeEndDocument();
  205. }
  206. void SharedStrings::readString(QXmlStreamReader &reader)
  207. {
  208. Q_ASSERT(reader.name() == QLatin1String("si"));
  209. RichString richString;
  210. while (!reader.atEnd() && !(reader.name() == QLatin1String("si") && reader.tokenType() == QXmlStreamReader::EndElement)) {
  211. reader.readNextStartElement();
  212. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  213. if (reader.name() == QLatin1String("r"))
  214. readRichStringPart(reader, richString);
  215. else if (reader.name() == QLatin1String("t"))
  216. readPlainStringPart(reader, richString);
  217. }
  218. }
  219. int idx = m_stringList.size();
  220. m_stringTable[richString] = XlsxSharedStringInfo(idx, 0);
  221. m_stringList.append(richString);
  222. }
  223. void SharedStrings::readRichStringPart(QXmlStreamReader &reader, RichString &richString)
  224. {
  225. Q_ASSERT(reader.name() == QLatin1String("r"));
  226. QString text;
  227. Format format;
  228. while (!reader.atEnd() && !(reader.name() == QLatin1String("r") && reader.tokenType() == QXmlStreamReader::EndElement)) {
  229. reader.readNextStartElement();
  230. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  231. if (reader.name() == QLatin1String("rPr")) {
  232. format = readRichStringPart_rPr(reader);
  233. } else if (reader.name() == QLatin1String("t")) {
  234. text = reader.readElementText();
  235. }
  236. }
  237. }
  238. richString.addFragment(text, format);
  239. }
  240. void SharedStrings::readPlainStringPart(QXmlStreamReader &reader, RichString &richString)
  241. {
  242. Q_ASSERT(reader.name() == QLatin1String("t"));
  243. //QXmlStreamAttributes attributes = reader.attributes();
  244. // NOTICE: CHECK POINT
  245. QString text = reader.readElementText();
  246. richString.addFragment(text, Format());
  247. }
  248. Format SharedStrings::readRichStringPart_rPr(QXmlStreamReader &reader)
  249. {
  250. Q_ASSERT(reader.name() == QLatin1String("rPr"));
  251. Format format;
  252. while (!reader.atEnd() && !(reader.name() == QLatin1String("rPr") && reader.tokenType() == QXmlStreamReader::EndElement)) {
  253. reader.readNextStartElement();
  254. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  255. QXmlStreamAttributes attributes = reader.attributes();
  256. if (reader.name() == QLatin1String("rFont")) {
  257. format.setFontName(attributes.value(QLatin1String("val")).toString());
  258. } else if (reader.name() == QLatin1String("charset")) {
  259. format.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toString().toInt());
  260. } else if (reader.name() == QLatin1String("family")) {
  261. format.setProperty(FormatPrivate::P_Font_Family, attributes.value(QLatin1String("val")).toString().toInt());
  262. } else if (reader.name() == QLatin1String("b")) {
  263. format.setFontBold(true);
  264. } else if (reader.name() == QLatin1String("i")) {
  265. format.setFontItalic(true);
  266. } else if (reader.name() == QLatin1String("strike")) {
  267. format.setFontStrikeOut(true);
  268. } else if (reader.name() == QLatin1String("outline")) {
  269. format.setFontOutline(true);
  270. } else if (reader.name() == QLatin1String("shadow")) {
  271. format.setProperty(FormatPrivate::P_Font_Shadow, true);
  272. } else if (reader.name() == QLatin1String("condense")) {
  273. format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toString().toInt());
  274. } else if (reader.name() == QLatin1String("extend")) {
  275. format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toString().toInt());
  276. } else if (reader.name() == QLatin1String("color")) {
  277. XlsxColor color;
  278. color.loadFromXml(reader);
  279. format.setProperty(FormatPrivate::P_Font_Color, color);
  280. } else if (reader.name() == QLatin1String("sz")) {
  281. format.setFontSize(attributes.value(QLatin1String("val")).toString().toInt());
  282. } else if (reader.name() == QLatin1String("u")) {
  283. QString value = attributes.value(QLatin1String("val")).toString();
  284. if (value == QLatin1String("double"))
  285. format.setFontUnderline(Format::FontUnderlineDouble);
  286. else if (value == QLatin1String("doubleAccounting"))
  287. format.setFontUnderline(Format::FontUnderlineDoubleAccounting);
  288. else if (value == QLatin1String("singleAccounting"))
  289. format.setFontUnderline(Format::FontUnderlineSingleAccounting);
  290. else
  291. format.setFontUnderline(Format::FontUnderlineSingle);
  292. } else if (reader.name() == QLatin1String("vertAlign")) {
  293. QString value = attributes.value(QLatin1String("val")).toString();
  294. if (value == QLatin1String("superscript"))
  295. format.setFontScript(Format::FontScriptSuper);
  296. else if (value == QLatin1String("subscript"))
  297. format.setFontScript(Format::FontScriptSub);
  298. } else if (reader.name() == QLatin1String("scheme")) {
  299. format.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString());
  300. }
  301. }
  302. }
  303. return format;
  304. }
  305. bool SharedStrings::loadFromXmlFile(QIODevice *device)
  306. {
  307. QXmlStreamReader reader(device);
  308. int count = 0;
  309. bool hasUniqueCountAttr=true;
  310. while (!reader.atEnd()) {
  311. QXmlStreamReader::TokenType token = reader.readNext();
  312. if (token == QXmlStreamReader::StartElement) {
  313. if (reader.name() == QLatin1String("sst")) {
  314. QXmlStreamAttributes attributes = reader.attributes();
  315. if ((hasUniqueCountAttr = attributes.hasAttribute(QLatin1String("uniqueCount"))))
  316. count = attributes.value(QLatin1String("uniqueCount")).toString().toInt();
  317. } else if (reader.name() == QLatin1String("si")) {
  318. readString(reader);
  319. }
  320. }
  321. }
  322. if (hasUniqueCountAttr && m_stringList.size() != count) {
  323. qDebug("Error: Shared string count");
  324. return false;
  325. }
  326. if (m_stringList.size() != m_stringTable.size()) {
  327. //qDebug("Warning: Duplicated items exist in shared string table.");
  328. //Nothing we can do here, as indices of the strings will be used when loading sheets.
  329. }
  330. return true;
  331. }
  332. QT_END_NAMESPACE_XLSX