xlsxchart.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645
  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 "xlsxchart_p.h"
  26. #include "xlsxworksheet.h"
  27. #include "xlsxcellrange.h"
  28. #include "xlsxutility_p.h"
  29. #include <QIODevice>
  30. #include <QXmlStreamReader>
  31. #include <QXmlStreamWriter>
  32. #include <QDebug>
  33. QT_BEGIN_NAMESPACE_XLSX
  34. ChartPrivate::ChartPrivate(Chart *q, Chart::CreateFlag flag)
  35. :AbstractOOXmlFilePrivate(q, flag), chartType(static_cast<Chart::ChartType>(0))
  36. {
  37. }
  38. ChartPrivate::~ChartPrivate()
  39. {
  40. }
  41. /*!
  42. * \class Chart
  43. * \inmodule QtXlsx
  44. * \brief Main class for the charts.
  45. */
  46. /*!
  47. \enum Chart::ChartType
  48. \value CT_Area
  49. \value CT_Area3D,
  50. \value CT_Line,
  51. \value CT_Line3D,
  52. \value CT_Scatter,
  53. \value CT_Pie,
  54. \value CT_Pie3D,
  55. \value CT_Doughnut,
  56. \value CT_Bar,
  57. \value CT_Bar3D,
  58. \omitvalue CT_Stock,
  59. \omitvalue CT_Radar,
  60. \omitvalue CT_OfPie,
  61. \omitvalue CT_Surface,
  62. \omitvalue CT_Surface3D,
  63. \omitvalue CT_Bubble
  64. */
  65. /*!
  66. * \internal
  67. */
  68. Chart::Chart(AbstractSheet *parent, CreateFlag flag)
  69. :AbstractOOXmlFile(new ChartPrivate(this, flag))
  70. {
  71. d_func()->sheet = parent;
  72. }
  73. /*!
  74. * Destroys the chart.
  75. */
  76. Chart::~Chart()
  77. {
  78. }
  79. /*!
  80. * Add the data series which is in the range \a range of the \a sheet.
  81. */
  82. void Chart::addSeries(const CellRange &range, AbstractSheet *sheet)
  83. {
  84. Q_D(Chart);
  85. if (!range.isValid())
  86. return;
  87. if (sheet && sheet->sheetType() != AbstractSheet::ST_WorkSheet)
  88. return;
  89. if (!sheet && d->sheet->sheetType() != AbstractSheet::ST_WorkSheet)
  90. return;
  91. QString sheetName = sheet ? sheet->sheetName() : d->sheet->sheetName();
  92. //In case sheetName contains space or '
  93. sheetName = escapeSheetName(sheetName);
  94. if (range.columnCount() == 1 || range.rowCount() == 1) {
  95. QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
  96. series->numberDataSource_numRef = sheetName + QLatin1String("!") + range.toString(true, true);
  97. d->seriesList.append(series);
  98. } else if (range.columnCount() < range.rowCount()) {
  99. //Column based series
  100. int firstDataColumn = range.firstColumn();
  101. QString axDataSouruce_numRef;
  102. if (d->chartType == CT_Scatter || d->chartType == CT_Bubble) {
  103. firstDataColumn += 1;
  104. CellRange subRange(range.firstRow(), range.firstColumn(), range.lastRow(), range.firstColumn());
  105. axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
  106. }
  107. for (int col=firstDataColumn; col<=range.lastColumn(); ++col) {
  108. CellRange subRange(range.firstRow(), col, range.lastRow(), col);
  109. QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
  110. series->axDataSource_numRef = axDataSouruce_numRef;
  111. series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
  112. d->seriesList.append(series);
  113. }
  114. } else {
  115. //Row based series
  116. int firstDataRow = range.firstRow();
  117. QString axDataSouruce_numRef;
  118. if (d->chartType == CT_Scatter || d->chartType == CT_Bubble) {
  119. firstDataRow += 1;
  120. CellRange subRange(range.firstRow(), range.firstColumn(), range.firstRow(), range.lastColumn());
  121. axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
  122. }
  123. for (int row=firstDataRow; row<=range.lastRow(); ++row) {
  124. CellRange subRange(row, range.firstColumn(), row, range.lastColumn());
  125. QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
  126. series->axDataSource_numRef = axDataSouruce_numRef;
  127. series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
  128. d->seriesList.append(series);
  129. }
  130. }
  131. }
  132. /*!
  133. * Set the type of the chart to \a type
  134. */
  135. void Chart::setChartType(ChartType type)
  136. {
  137. Q_D(Chart);
  138. d->chartType = type;
  139. }
  140. /*!
  141. * \internal
  142. *
  143. */
  144. void Chart::setChartStyle(int id)
  145. {
  146. Q_UNUSED(id)
  147. //!Todo
  148. }
  149. /*!
  150. * \internal
  151. */
  152. void Chart::saveToXmlFile(QIODevice *device) const
  153. {
  154. Q_D(const Chart);
  155. QXmlStreamWriter writer(device);
  156. writer.writeStartDocument(QStringLiteral("1.0"), true);
  157. writer.writeStartElement(QStringLiteral("c:chartSpace"));
  158. writer.writeAttribute(QStringLiteral("xmlns:c"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart"));
  159. writer.writeAttribute(QStringLiteral("xmlns:a"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main"));
  160. writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
  161. d->saveXmlChart(writer);
  162. writer.writeEndElement();//c:chartSpace
  163. writer.writeEndDocument();
  164. }
  165. /*!
  166. * \internal
  167. */
  168. bool Chart::loadFromXmlFile(QIODevice *device)
  169. {
  170. Q_D(Chart);
  171. QXmlStreamReader reader(device);
  172. while (!reader.atEnd()) {
  173. reader.readNextStartElement();
  174. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  175. if (reader.name() == QLatin1String("chart")) {
  176. if (!d->loadXmlChart(reader))
  177. return false;
  178. }
  179. }
  180. }
  181. return true;
  182. }
  183. bool ChartPrivate::loadXmlChart(QXmlStreamReader &reader)
  184. {
  185. Q_ASSERT(reader.name() == QLatin1String("chart"));
  186. while (!reader.atEnd()) {
  187. reader.readNextStartElement();
  188. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  189. if (reader.name() == QLatin1String("plotArea")) {
  190. if (!loadXmlPlotArea(reader))
  191. return false;
  192. } else if (reader.name() == QLatin1String("legend")) {
  193. //!Todo
  194. }
  195. } else if (reader.tokenType() == QXmlStreamReader::EndElement &&
  196. reader.name() == QLatin1String("chart")) {
  197. break;
  198. }
  199. }
  200. return true;
  201. }
  202. bool ChartPrivate::loadXmlPlotArea(QXmlStreamReader &reader)
  203. {
  204. Q_ASSERT(reader.name() == QLatin1String("plotArea"));
  205. while (!reader.atEnd()) {
  206. reader.readNextStartElement();
  207. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  208. if (reader.name() == QLatin1String("layout")) {
  209. //!ToDo
  210. } else if (reader.name().endsWith(QLatin1String("Chart"))) {
  211. //For pieChart, barChart, ...
  212. loadXmlXxxChart(reader);
  213. } else if (reader.name().endsWith(QLatin1String("Ax"))) {
  214. //For valAx, catAx, serAx, dateAx
  215. loadXmlAxis(reader);
  216. }
  217. } else if (reader.tokenType() == QXmlStreamReader::EndElement &&
  218. reader.name() == QLatin1String("plotArea")) {
  219. break;
  220. }
  221. }
  222. return true;
  223. }
  224. bool ChartPrivate::loadXmlXxxChart(QXmlStreamReader &reader)
  225. {
  226. QStringRef name = reader.name();
  227. if (name == QLatin1String("pieChart")) chartType = Chart::CT_Pie;
  228. else if (name == QLatin1String("pie3DChart")) chartType = Chart::CT_Pie3D;
  229. else if (name == QLatin1String("barChart")) chartType = Chart::CT_Bar;
  230. else if (name == QLatin1String("bar3DChart")) chartType = Chart::CT_Bar3D;
  231. else if (name == QLatin1String("lineChart")) chartType = Chart::CT_Line;
  232. else if (name == QLatin1String("line3DChart")) chartType = Chart::CT_Line3D;
  233. else if (name == QLatin1String("scatterChart")) chartType = Chart::CT_Scatter;
  234. else if (name == QLatin1String("areaChart")) chartType = Chart::CT_Area;
  235. else if (name == QLatin1String("area3DChart")) chartType = Chart::CT_Area3D;
  236. else if (name == QLatin1String("doughnutChart")) chartType = Chart::CT_Doughnut;
  237. else qDebug()<<"Cann't load chart: "<<name;
  238. while (!reader.atEnd()) {
  239. reader.readNextStartElement();
  240. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  241. if (reader.name() == QLatin1String("ser")) {
  242. loadXmlSer(reader);
  243. } else if (reader.name() == QLatin1String("axId")) {
  244. }
  245. } else if (reader.tokenType() == QXmlStreamReader::EndElement
  246. && reader.name() == name) {
  247. break;
  248. }
  249. }
  250. return true;
  251. }
  252. bool ChartPrivate::loadXmlSer(QXmlStreamReader &reader)
  253. {
  254. Q_ASSERT(reader.name() == QLatin1String("ser"));
  255. QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
  256. seriesList.append(series);
  257. while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
  258. && reader.name() == QLatin1String("ser"))) {
  259. if (reader.readNextStartElement()) {
  260. QStringRef name = reader.name();
  261. if (name == QLatin1String("cat") || name == QLatin1String("xVal")) {
  262. while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
  263. && reader.name() == name)) {
  264. if (reader.readNextStartElement()) {
  265. if (reader.name() == QLatin1String("numRef"))
  266. series->axDataSource_numRef = loadXmlNumRef(reader);
  267. }
  268. }
  269. } else if (name == QLatin1String("val") || name == QLatin1String("yVal")) {
  270. while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
  271. && reader.name() == name)) {
  272. if (reader.readNextStartElement()) {
  273. if (reader.name() == QLatin1String("numRef"))
  274. series->numberDataSource_numRef = loadXmlNumRef(reader);
  275. }
  276. }
  277. } else if (name == QLatin1String("extLst")) {
  278. while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
  279. && reader.name() == name)) {
  280. reader.readNextStartElement();
  281. }
  282. }
  283. }
  284. }
  285. return true;
  286. }
  287. QString ChartPrivate::loadXmlNumRef(QXmlStreamReader &reader)
  288. {
  289. Q_ASSERT(reader.name() == QLatin1String("numRef"));
  290. while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
  291. && reader.name() == QLatin1String("numRef"))) {
  292. if (reader.readNextStartElement()) {
  293. if (reader.name() == QLatin1String("f"))
  294. return reader.readElementText();
  295. }
  296. }
  297. return QString();
  298. }
  299. void ChartPrivate::saveXmlChart(QXmlStreamWriter &writer) const
  300. {
  301. writer.writeStartElement(QStringLiteral("c:chart"));
  302. writer.writeStartElement(QStringLiteral("c:plotArea"));
  303. switch (chartType) {
  304. case Chart::CT_Pie:
  305. case Chart::CT_Pie3D:
  306. saveXmlPieChart(writer);
  307. break;
  308. case Chart::CT_Bar:
  309. case Chart::CT_Bar3D:
  310. saveXmlBarChart(writer);
  311. break;
  312. case Chart::CT_Line:
  313. case Chart::CT_Line3D:
  314. saveXmlLineChart(writer);
  315. break;
  316. case Chart::CT_Scatter:
  317. saveXmlScatterChart(writer);
  318. break;
  319. case Chart::CT_Area:
  320. case Chart::CT_Area3D:
  321. saveXmlAreaChart(writer);
  322. break;
  323. case Chart::CT_Doughnut:
  324. saveXmlDoughnutChart(writer);
  325. break;
  326. default:
  327. break;
  328. }
  329. saveXmlAxes(writer);
  330. writer.writeEndElement(); //plotArea
  331. // saveXmlLegend(writer);
  332. writer.writeEndElement(); //chart
  333. }
  334. void ChartPrivate::saveXmlPieChart(QXmlStreamWriter &writer) const
  335. {
  336. QString name = chartType==Chart::CT_Pie ? QStringLiteral("c:pieChart") : QStringLiteral("c:pie3DChart");
  337. writer.writeStartElement(name);
  338. //Do the same behavior as Excel, Pie prefer varyColors
  339. writer.writeEmptyElement(QStringLiteral("c:varyColors"));
  340. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
  341. for (int i=0; i<seriesList.size(); ++i)
  342. saveXmlSer(writer, seriesList[i].data(), i);
  343. writer.writeEndElement(); //pieChart, pie3DChart
  344. }
  345. void ChartPrivate::saveXmlBarChart(QXmlStreamWriter &writer) const
  346. {
  347. QString name = chartType==Chart::CT_Bar ? QStringLiteral("c:barChart") : QStringLiteral("c:bar3DChart");
  348. writer.writeStartElement(name);
  349. writer.writeEmptyElement(QStringLiteral("c:barDir"));
  350. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("col"));
  351. for (int i=0; i<seriesList.size(); ++i)
  352. saveXmlSer(writer, seriesList[i].data(), i);
  353. if (axisList.isEmpty()) {
  354. //The order the axes??
  355. const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)));
  356. const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
  357. }
  358. //Note: Bar3D have 2~3 axes
  359. Q_ASSERT(axisList.size()==2 || (axisList.size()==3 && chartType==Chart::CT_Bar3D));
  360. for (int i=0; i<axisList.size(); ++i) {
  361. writer.writeEmptyElement(QStringLiteral("c:axId"));
  362. writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
  363. }
  364. writer.writeEndElement(); //barChart, bar3DChart
  365. }
  366. void ChartPrivate::saveXmlLineChart(QXmlStreamWriter &writer) const
  367. {
  368. QString name = chartType==Chart::CT_Line ? QStringLiteral("c:lineChart") : QStringLiteral("c:line3DChart");
  369. writer.writeStartElement(name);
  370. writer.writeEmptyElement(QStringLiteral("grouping"));
  371. for (int i=0; i<seriesList.size(); ++i)
  372. saveXmlSer(writer, seriesList[i].data(), i);
  373. if (axisList.isEmpty()) {
  374. const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)));
  375. const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
  376. if (chartType==Chart::CT_Line3D)
  377. const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Ser, XlsxAxis::Bottom, 2, 0)));
  378. }
  379. Q_ASSERT((axisList.size()==2||chartType==Chart::CT_Line)|| (axisList.size()==3 && chartType==Chart::CT_Line3D));
  380. for (int i=0; i<axisList.size(); ++i) {
  381. writer.writeEmptyElement(QStringLiteral("c:axId"));
  382. writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
  383. }
  384. writer.writeEndElement(); //lineChart, line3DChart
  385. }
  386. void ChartPrivate::saveXmlScatterChart(QXmlStreamWriter &writer) const
  387. {
  388. const QString name = QStringLiteral("c:scatterChart");
  389. writer.writeStartElement(name);
  390. writer.writeEmptyElement(QStringLiteral("c:scatterStyle"));
  391. for (int i=0; i<seriesList.size(); ++i)
  392. saveXmlSer(writer, seriesList[i].data(), i);
  393. if (axisList.isEmpty()) {
  394. const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Bottom, 0, 1)));
  395. const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
  396. }
  397. Q_ASSERT(axisList.size()==2);
  398. for (int i=0; i<axisList.size(); ++i) {
  399. writer.writeEmptyElement(QStringLiteral("c:axId"));
  400. writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
  401. }
  402. writer.writeEndElement(); //c:scatterChart
  403. }
  404. void ChartPrivate::saveXmlAreaChart(QXmlStreamWriter &writer) const
  405. {
  406. QString name = chartType==Chart::CT_Area ? QStringLiteral("c:areaChart") : QStringLiteral("c:area3DChart");
  407. writer.writeStartElement(name);
  408. writer.writeEmptyElement(QStringLiteral("grouping"));
  409. for (int i=0; i<seriesList.size(); ++i)
  410. saveXmlSer(writer, seriesList[i].data(), i);
  411. if (axisList.isEmpty()) {
  412. const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)));
  413. const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
  414. }
  415. //Note: Area3D have 2~3 axes
  416. Q_ASSERT(axisList.size()==2 || (axisList.size()==3 && chartType==Chart::CT_Area3D));
  417. for (int i=0; i<axisList.size(); ++i) {
  418. writer.writeEmptyElement(QStringLiteral("c:axId"));
  419. writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
  420. }
  421. writer.writeEndElement(); //lineChart, line3DChart
  422. }
  423. void ChartPrivate::saveXmlDoughnutChart(QXmlStreamWriter &writer) const
  424. {
  425. QString name = QStringLiteral("c:doughnutChart");
  426. writer.writeStartElement(name);
  427. writer.writeEmptyElement(QStringLiteral("c:varyColors"));
  428. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
  429. for (int i=0; i<seriesList.size(); ++i)
  430. saveXmlSer(writer, seriesList[i].data(), i);
  431. writer.writeStartElement(QStringLiteral("c:holeSize"));
  432. writer.writeAttribute(QStringLiteral("val"), QString::number(50));
  433. writer.writeEndElement();
  434. }
  435. void ChartPrivate::saveXmlSer(QXmlStreamWriter &writer, XlsxSeries *ser, int id) const
  436. {
  437. writer.writeStartElement(QStringLiteral("c:ser"));
  438. writer.writeEmptyElement(QStringLiteral("c:idx"));
  439. writer.writeAttribute(QStringLiteral("val"), QString::number(id));
  440. writer.writeEmptyElement(QStringLiteral("c:order"));
  441. writer.writeAttribute(QStringLiteral("val"), QString::number(id));
  442. if (!ser->axDataSource_numRef.isEmpty()) {
  443. if (chartType == Chart::CT_Scatter || chartType == Chart::CT_Bubble)
  444. writer.writeStartElement(QStringLiteral("c:xVal"));
  445. else
  446. writer.writeStartElement(QStringLiteral("c:cat"));
  447. writer.writeStartElement(QStringLiteral("c:numRef"));
  448. writer.writeTextElement(QStringLiteral("c:f"), ser->axDataSource_numRef);
  449. writer.writeEndElement();//c:numRef
  450. writer.writeEndElement();//c:cat or c:xVal
  451. }
  452. if (!ser->numberDataSource_numRef.isEmpty()) {
  453. if (chartType == Chart::CT_Scatter || chartType == Chart::CT_Bubble)
  454. writer.writeStartElement(QStringLiteral("c:yVal"));
  455. else
  456. writer.writeStartElement(QStringLiteral("c:val"));
  457. writer.writeStartElement(QStringLiteral("c:numRef"));
  458. writer.writeTextElement(QStringLiteral("c:f"), ser->numberDataSource_numRef);
  459. writer.writeEndElement();//c:numRef
  460. writer.writeEndElement();//c:val or c:yVal
  461. }
  462. writer.writeEndElement();//c:ser
  463. }
  464. bool ChartPrivate::loadXmlAxis(QXmlStreamReader &reader)
  465. {
  466. Q_ASSERT(reader.name().endsWith(QLatin1String("Ax")));
  467. QString name = reader.name().toString();
  468. XlsxAxis *axis = new XlsxAxis;
  469. if (name == QLatin1String("valAx"))
  470. axis->type = XlsxAxis::T_Val;
  471. else if (name == QLatin1String("catAx"))
  472. axis->type = XlsxAxis::T_Cat;
  473. else if (name == QLatin1String("serAx"))
  474. axis->type = XlsxAxis::T_Ser;
  475. else
  476. axis->type = XlsxAxis::T_Date;
  477. axisList.append(QSharedPointer<XlsxAxis>(axis));
  478. while (!reader.atEnd()) {
  479. reader.readNextStartElement();
  480. if (reader.tokenType() == QXmlStreamReader::StartElement) {
  481. if (reader.name() == QLatin1String("axPos")) {
  482. QXmlStreamAttributes attrs = reader.attributes();
  483. QStringRef pos = attrs.value(QLatin1String("val"));
  484. if (pos==QLatin1String("l"))
  485. axis->axisPos = XlsxAxis::Left;
  486. else if (pos==QLatin1String("r"))
  487. axis->axisPos = XlsxAxis::Right;
  488. else if (pos==QLatin1String("b"))
  489. axis->axisPos = XlsxAxis::Bottom;
  490. else
  491. axis->axisPos = XlsxAxis::Top;
  492. } else if (reader.name() == QLatin1String("axId")) {
  493. axis->axisId = reader.attributes().value(QLatin1String("val")).toString().toInt();
  494. } else if (reader.name() == QLatin1String("crossAx")) {
  495. axis->crossAx = reader.attributes().value(QLatin1String("val")).toString().toInt();
  496. }
  497. } else if (reader.tokenType() == QXmlStreamReader::EndElement
  498. && reader.name() == name) {
  499. break;
  500. }
  501. }
  502. return true;
  503. }
  504. void ChartPrivate::saveXmlAxes(QXmlStreamWriter &writer) const
  505. {
  506. for (int i=0; i<axisList.size(); ++i) {
  507. XlsxAxis *axis = axisList[i].data();
  508. QString name;
  509. switch (axis->type) {
  510. case XlsxAxis::T_Cat: name = QStringLiteral("c:catAx"); break;
  511. case XlsxAxis::T_Val: name = QStringLiteral("c:valAx"); break;
  512. case XlsxAxis::T_Ser: name = QStringLiteral("c:serAx"); break;
  513. case XlsxAxis::T_Date: name = QStringLiteral("c:dateAx"); break;
  514. default: break;
  515. }
  516. QString pos;
  517. switch (axis->axisPos) {
  518. case XlsxAxis::Top: pos = QStringLiteral("t"); break;
  519. case XlsxAxis::Bottom: pos = QStringLiteral("b"); break;
  520. case XlsxAxis::Left: pos = QStringLiteral("l"); break;
  521. case XlsxAxis::Right: pos = QStringLiteral("r"); break;
  522. default: break;
  523. }
  524. writer.writeStartElement(name);
  525. writer.writeEmptyElement(QStringLiteral("c:axId"));
  526. writer.writeAttribute(QStringLiteral("val"), QString::number(axis->axisId));
  527. writer.writeStartElement(QStringLiteral("c:scaling"));
  528. writer.writeEmptyElement(QStringLiteral("c:orientation"));
  529. writer.writeAttribute(QStringLiteral("val"), QStringLiteral("minMax"));
  530. writer.writeEndElement();//c:scaling
  531. writer.writeEmptyElement(QStringLiteral("c:axPos"));
  532. writer.writeAttribute(QStringLiteral("val"), pos);
  533. writer.writeEmptyElement(QStringLiteral("c:crossAx"));
  534. writer.writeAttribute(QStringLiteral("val"), QString::number(axis->crossAx));
  535. writer.writeEndElement();//name
  536. }
  537. }
  538. QT_END_NAMESPACE_XLSX