xlsxsharedstrings.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. /****************************************************************************
  2. ** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
  3. ** All right reserved.
  4. **
  5. ** Permission is hereby granted, free of charge, to any person obtaining
  6. ** a copy of this software and associated documentation files (the
  7. ** "Software"), to deal in the Software without restriction, including
  8. ** without limitation the rights to use, copy, modify, merge, publish,
  9. ** distribute, sublicense, and/or sell copies of the Software, and to
  10. ** permit persons to whom the Software is furnished to do so, subject to
  11. ** the following conditions:
  12. **
  13. ** The above copyright notice and this permission notice shall be
  14. ** included in all copies or substantial portions of the Software.
  15. **
  16. ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  17. ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  18. ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  19. ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  20. ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  21. ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  22. ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. **
  24. ****************************************************************************/
  25. #include "xlsxrichstring.h"
  26. #include "xlsxsharedstrings_p.h"
  27. #include "xlsxutility_p.h"
  28. #include "xlsxformat_p.h"
  29. #include "xlsxcolor_p.h"
  30. #include <QXmlStreamWriter>
  31. #include <QXmlStreamReader>
  32. #include <QDir>
  33. #include <QFile>
  34. #include <QDebug>
  35. #include <QBuffer>
  36. namespace QXlsx {
  37. /*
  38. * Note that, when we open an existing .xlsx file (broken file?),
  39. * duplicated string items may exist in the shared string table.
  40. *
  41. * In such case, the size of stringList will larger than stringTable.
  42. * Duplicated items can be removed once we loaded all the worksheets.
  43. */
  44. SharedStrings::SharedStrings(CreateFlag flag)
  45. :AbstractOOXmlFile(flag)
  46. {
  47. m_stringCount = 0;
  48. }
  49. int SharedStrings::count() const
  50. {
  51. return m_stringCount;
  52. }
  53. bool SharedStrings::isEmpty() const
  54. {
  55. return m_stringList.isEmpty();
  56. }
  57. int SharedStrings::addSharedString(const QString &string)
  58. {
  59. return addSharedString(RichString(string));
  60. }
  61. int SharedStrings::addSharedString(const RichString &string)
  62. {
  63. m_stringCount += 1;
  64. if (m_stringTable.contains(string)) {
  65. XlsxSharedStringInfo &item = m_stringTable[string];
  66. item.count += 1;
  67. return item.index;
  68. }
  69. int index = m_stringList.size();
  70. m_stringTable[string] = XlsxSharedStringInfo(index);
  71. m_stringList.append(string);
  72. return index;
  73. }
  74. void SharedStrings::incRefByStringIndex(int idx)
  75. {
  76. if (idx <0 || idx >= m_stringList.size()) {
  77. qDebug("SharedStrings: invlid index");
  78. return;
  79. }
  80. addSharedString(m_stringList[idx]);
  81. }
  82. /*
  83. * Broken, don't use.
  84. */
  85. void SharedStrings::removeSharedString(const QString &string)
  86. {
  87. removeSharedString(RichString(string));
  88. }
  89. /*
  90. * Broken, don't use.
  91. */
  92. void SharedStrings::removeSharedString(const RichString &string)
  93. {
  94. if (!m_stringTable.contains(string))
  95. return;
  96. m_stringCount -= 1;
  97. XlsxSharedStringInfo &item = m_stringTable[string];
  98. item.count -= 1;
  99. if (item.count <= 0) {
  100. for (int i=item.index+1; i<m_stringList.size(); ++i)
  101. m_stringTable[m_stringList[i]].index -= 1;
  102. m_stringList.removeAt(item.index);
  103. m_stringTable.remove(string);
  104. }
  105. }
  106. int SharedStrings::getSharedStringIndex(const QString &string) const
  107. {
  108. return getSharedStringIndex(RichString(string));
  109. }
  110. int SharedStrings::getSharedStringIndex(const RichString &string) const
  111. {
  112. if (m_stringTable.contains(string))
  113. return m_stringTable[string].index;
  114. return -1;
  115. }
  116. RichString SharedStrings::getSharedString(int index) const
  117. {
  118. if (index < m_stringList.count() && index >= 0)
  119. return m_stringList[index];
  120. return RichString();
  121. }
  122. QList<RichString> SharedStrings::getSharedStrings() const
  123. {
  124. return m_stringList;
  125. }
  126. void SharedStrings::writeRichStringPart_rPr(QXmlStreamWriter &writer, const Format &format) const
  127. {
  128. if (!format.hasFontData())
  129. return;
  130. if (format.fontBold())
  131. writer.writeEmptyElement(QStringLiteral("b"));
  132. if (format.fontItalic())
  133. writer.writeEmptyElement(QStringLiteral("i"));
  134. if (format.fontStrikeOut())
  135. writer.writeEmptyElement(QStringLiteral("strike"));
  136. if (format.fontOutline())
  137. writer.writeEmptyElement(QStringLiteral("outline"));
  138. if (format.boolProperty(FormatPrivate::P_Font_Shadow))
  139. writer.writeEmptyElement(QStringLiteral("shadow"));
  140. if (format.hasProperty(FormatPrivate::P_Font_Underline)) {
  141. Format::FontUnderline u = format.fontUnderline();
  142. if (u != Format::FontUnderlineNone) {
  143. writer.writeEmptyElement(QStringLiteral("u"));
  144. if (u== Format::FontUnderlineDouble)
  145. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double"));
  146. else if (u == Format::FontUnderlineSingleAccounting)
  147. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting"));
  148. else if (u == Format::FontUnderlineDoubleAccounting)
  149. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting"));
  150. }
  151. }
  152. if (format.hasProperty(FormatPrivate::P_Font_Script)) {
  153. Format::FontScript s = format.fontScript();
  154. if (s != Format::FontScriptNormal) {
  155. writer.writeEmptyElement(QStringLiteral("vertAlign"));
  156. if (s == Format::FontScriptSuper)
  157. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript"));
  158. else
  159. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript"));
  160. }
  161. }
  162. if (format.hasProperty(FormatPrivate::P_Font_Size)) {
  163. writer.writeEmptyElement(QStringLiteral("sz"));
  164. writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize()));
  165. }
  166. if (format.hasProperty(FormatPrivate::P_Font_Color)) {
  167. XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>();
  168. color.saveToXml(writer);
  169. }
  170. if (!format.fontName().isEmpty()) {
  171. writer.writeEmptyElement(QStringLiteral("rFont"));
  172. writer.writeAttribute(QStringLiteral("val"), format.fontName());
  173. }
  174. if (format.hasProperty(FormatPrivate::P_Font_Family)) {
  175. writer.writeEmptyElement(QStringLiteral("family"));
  176. writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family)));
  177. }
  178. if (format.hasProperty(FormatPrivate::P_Font_Scheme)) {
  179. writer.writeEmptyElement(QStringLiteral("scheme"));
  180. writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme));
  181. }
  182. }
  183. void SharedStrings::saveToXmlFile(QIODevice *device) const
  184. {
  185. QXmlStreamWriter writer(device);
  186. if (m_stringList.size() != m_stringTable.size()) {
  187. //Duplicated string items exist in m_stringList
  188. //Clean up can not be done here, as the indices
  189. //have been used when we save the worksheets part.
  190. }
  191. writer.writeStartDocument(QStringLiteral("1.0"), true);
  192. writer.writeStartElement(QStringLiteral("sst"));
  193. writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
  194. writer.writeAttribute(QStringLiteral("count"), QString::number(m_stringCount));
  195. writer.writeAttribute(QStringLiteral("uniqueCount"), QString::number(m_stringList.size()));
  196. foreach (RichString string, m_stringList) {
  197. writer.writeStartElement(QStringLiteral("si"));
  198. if (string.isRichString()) {
  199. //Rich text string
  200. for (int i=0; i<string.fragmentCount(); ++i) {
  201. writer.writeStartElement(QStringLiteral("r"));
  202. if (string.fragmentFormat(i).hasFontData()) {
  203. writer.writeStartElement(QStringLiteral("rPr"));
  204. writeRichStringPart_rPr(writer, string.fragmentFormat(i));
  205. writer.writeEndElement();// rPr
  206. }
  207. writer.writeStartElement(QStringLiteral("t"));
  208. if (isSpaceReserveNeeded(string.fragmentText(i)))
  209. writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
  210. writer.writeCharacters(string.fragmentText(i));
  211. writer.writeEndElement();// t
  212. writer.writeEndElement(); //r
  213. }
  214. } else {
  215. writer.writeStartElement(QStringLiteral("t"));
  216. QString pString = string.toPlainString();
  217. if (isSpaceReserveNeeded(pString))
  218. writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
  219. writer.writeCharacters(pString);
  220. writer.writeEndElement();//t
  221. }
  222. writer.writeEndElement();//si
  223. }
  224. writer.writeEndElement(); //sst
  225. writer.writeEndDocument();
  226. }
  227. void SharedStrings::readString(QXmlStreamReader &reader)
  228. {
  229. Q_ASSERT(reader.name() == QLatin1String("si"));
  230. RichString richString;
  231. while (!reader.atEnd() && !(reader.name() == QLatin1String("si") && reader.tokenType() == QXmlStreamReader::EndElement)) {
  232. reader.readNextStartElement();
  233. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  234. if (reader.name() == QLatin1String("r"))
  235. readRichStringPart(reader, richString);
  236. else if (reader.name() == QLatin1String("t"))
  237. readPlainStringPart(reader, richString);
  238. }
  239. }
  240. int idx = m_stringList.size();
  241. m_stringTable[richString] = XlsxSharedStringInfo(idx, 0);
  242. m_stringList.append(richString);
  243. }
  244. void SharedStrings::readRichStringPart(QXmlStreamReader &reader, RichString &richString)
  245. {
  246. Q_ASSERT(reader.name() == QLatin1String("r"));
  247. QString text;
  248. Format format;
  249. while (!reader.atEnd() && !(reader.name() == QLatin1String("r") && reader.tokenType() == QXmlStreamReader::EndElement)) {
  250. reader.readNextStartElement();
  251. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  252. if (reader.name() == QLatin1String("rPr")) {
  253. format = readRichStringPart_rPr(reader);
  254. } else if (reader.name() == QLatin1String("t")) {
  255. text = reader.readElementText();
  256. }
  257. }
  258. }
  259. richString.addFragment(text, format);
  260. }
  261. void SharedStrings::readPlainStringPart(QXmlStreamReader &reader, RichString &richString)
  262. {
  263. Q_ASSERT(reader.name() == QLatin1String("t"));
  264. //QXmlStreamAttributes attributes = reader.attributes();
  265. QString text = reader.readElementText();
  266. richString.addFragment(text, Format());
  267. }
  268. Format SharedStrings::readRichStringPart_rPr(QXmlStreamReader &reader)
  269. {
  270. Q_ASSERT(reader.name() == QLatin1String("rPr"));
  271. Format format;
  272. while (!reader.atEnd() && !(reader.name() == QLatin1String("rPr") && reader.tokenType() == QXmlStreamReader::EndElement)) {
  273. reader.readNextStartElement();
  274. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  275. QXmlStreamAttributes attributes = reader.attributes();
  276. if (reader.name() == QLatin1String("rFont")) {
  277. format.setFontName(attributes.value(QLatin1String("val")).toString());
  278. } else if (reader.name() == QLatin1String("charset")) {
  279. format.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toString().toInt());
  280. } else if (reader.name() == QLatin1String("family")) {
  281. format.setProperty(FormatPrivate::P_Font_Family, attributes.value(QLatin1String("val")).toString().toInt());
  282. } else if (reader.name() == QLatin1String("b")) {
  283. format.setFontBold(true);
  284. } else if (reader.name() == QLatin1String("i")) {
  285. format.setFontItalic(true);
  286. } else if (reader.name() == QLatin1String("strike")) {
  287. format.setFontStrikeOut(true);
  288. } else if (reader.name() == QLatin1String("outline")) {
  289. format.setFontOutline(true);
  290. } else if (reader.name() == QLatin1String("shadow")) {
  291. format.setProperty(FormatPrivate::P_Font_Shadow, true);
  292. } else if (reader.name() == QLatin1String("condense")) {
  293. format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toString().toInt());
  294. } else if (reader.name() == QLatin1String("extend")) {
  295. format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toString().toInt());
  296. } else if (reader.name() == QLatin1String("color")) {
  297. XlsxColor color;
  298. color.loadFromXml(reader);
  299. format.setProperty(FormatPrivate::P_Font_Color, color);
  300. } else if (reader.name() == QLatin1String("sz")) {
  301. format.setFontSize(attributes.value(QLatin1String("val")).toString().toInt());
  302. } else if (reader.name() == QLatin1String("u")) {
  303. QString value = attributes.value(QLatin1String("val")).toString();
  304. if (value == QLatin1String("double"))
  305. format.setFontUnderline(Format::FontUnderlineDouble);
  306. else if (value == QLatin1String("doubleAccounting"))
  307. format.setFontUnderline(Format::FontUnderlineDoubleAccounting);
  308. else if (value == QLatin1String("singleAccounting"))
  309. format.setFontUnderline(Format::FontUnderlineSingleAccounting);
  310. else
  311. format.setFontUnderline(Format::FontUnderlineSingle);
  312. } else if (reader.name() == QLatin1String("vertAlign")) {
  313. QString value = attributes.value(QLatin1String("val")).toString();
  314. if (value == QLatin1String("superscript"))
  315. format.setFontScript(Format::FontScriptSuper);
  316. else if (value == QLatin1String("subscript"))
  317. format.setFontScript(Format::FontScriptSub);
  318. } else if (reader.name() == QLatin1String("scheme")) {
  319. format.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString());
  320. }
  321. }
  322. }
  323. return format;
  324. }
  325. bool SharedStrings::loadFromXmlFile(QIODevice *device)
  326. {
  327. QXmlStreamReader reader(device);
  328. int count = 0;
  329. bool hasUniqueCountAttr=true;
  330. while (!reader.atEnd()) {
  331. QXmlStreamReader::TokenType token = reader.readNext();
  332. if (token == QXmlStreamReader::StartElement) {
  333. if (reader.name() == QLatin1String("sst")) {
  334. QXmlStreamAttributes attributes = reader.attributes();
  335. if ((hasUniqueCountAttr = attributes.hasAttribute(QLatin1String("uniqueCount"))))
  336. count = attributes.value(QLatin1String("uniqueCount")).toString().toInt();
  337. } else if (reader.name() == QLatin1String("si")) {
  338. readString(reader);
  339. }
  340. }
  341. }
  342. if (hasUniqueCountAttr && m_stringList.size() != count) {
  343. qDebug("Error: Shared string count");
  344. return false;
  345. }
  346. if (m_stringList.size() != m_stringTable.size()) {
  347. //qDebug("Warning: Duplicated items exist in shared string table.");
  348. //Nothing we can do here, as indices of the strings will be used when loading sheets.
  349. }
  350. return true;
  351. }
  352. } //namespace