diff src/xlsx/xlsxchart.cpp @ 1:93d3106bb9a4

Add qt xlsx library
author Andre Heinecke <andre.heinecke@intevation.de>
date Tue, 22 Mar 2016 10:38:08 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/xlsx/xlsxchart.cpp	Tue Mar 22 10:38:08 2016 +0100
@@ -0,0 +1,645 @@
+/****************************************************************************
+** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
+** All right reserved.
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+**
+****************************************************************************/
+
+#include "xlsxchart_p.h"
+#include "xlsxworksheet.h"
+#include "xlsxcellrange.h"
+#include "xlsxutility_p.h"
+
+#include <QIODevice>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+#include <QDebug>
+
+QT_BEGIN_NAMESPACE_XLSX
+
+ChartPrivate::ChartPrivate(Chart *q, Chart::CreateFlag flag)
+    :AbstractOOXmlFilePrivate(q, flag), chartType(static_cast<Chart::ChartType>(0))
+{
+
+}
+
+ChartPrivate::~ChartPrivate()
+{
+
+}
+
+/*!
+ * \class Chart
+ * \inmodule QtXlsx
+ * \brief Main class for the charts.
+ */
+
+/*!
+  \enum Chart::ChartType
+
+  \value CT_Area
+  \value CT_Area3D,
+  \value CT_Line,
+  \value CT_Line3D,
+  \value CT_Scatter,
+  \value CT_Pie,
+  \value CT_Pie3D,
+  \value CT_Doughnut,
+  \value CT_Bar,
+  \value CT_Bar3D,
+
+  \omitvalue CT_Stock,
+  \omitvalue CT_Radar,
+  \omitvalue CT_OfPie,
+  \omitvalue CT_Surface,
+  \omitvalue CT_Surface3D,
+  \omitvalue CT_Bubble
+*/
+
+/*!
+ * \internal
+ */
+Chart::Chart(AbstractSheet *parent, CreateFlag flag)
+    :AbstractOOXmlFile(new ChartPrivate(this, flag))
+{
+    d_func()->sheet = parent;
+}
+
+/*!
+ * Destroys the chart.
+ */
+Chart::~Chart()
+{
+}
+
+/*!
+ * Add the data series which is in the range \a range of the \a sheet.
+ */
+void Chart::addSeries(const CellRange &range, AbstractSheet *sheet)
+{
+    Q_D(Chart);
+    if (!range.isValid())
+        return;
+    if (sheet && sheet->sheetType() != AbstractSheet::ST_WorkSheet)
+        return;
+    if (!sheet && d->sheet->sheetType() != AbstractSheet::ST_WorkSheet)
+        return;
+
+    QString sheetName = sheet ? sheet->sheetName() : d->sheet->sheetName();
+    //In case sheetName contains space or '
+    sheetName = escapeSheetName(sheetName);
+
+    if (range.columnCount() == 1 || range.rowCount() == 1) {
+        QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
+        series->numberDataSource_numRef = sheetName + QLatin1String("!") + range.toString(true, true);
+        d->seriesList.append(series);
+    } else if (range.columnCount() < range.rowCount()) {
+        //Column based series
+        int firstDataColumn = range.firstColumn();
+        QString axDataSouruce_numRef;
+        if (d->chartType == CT_Scatter || d->chartType == CT_Bubble) {
+            firstDataColumn += 1;
+            CellRange subRange(range.firstRow(), range.firstColumn(), range.lastRow(), range.firstColumn());
+            axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
+        }
+
+        for (int col=firstDataColumn; col<=range.lastColumn(); ++col) {
+            CellRange subRange(range.firstRow(), col, range.lastRow(), col);
+            QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
+            series->axDataSource_numRef = axDataSouruce_numRef;
+            series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
+            d->seriesList.append(series);
+        }
+
+    } else {
+        //Row based series
+        int firstDataRow = range.firstRow();
+        QString axDataSouruce_numRef;
+        if (d->chartType == CT_Scatter || d->chartType == CT_Bubble) {
+            firstDataRow += 1;
+            CellRange subRange(range.firstRow(), range.firstColumn(), range.firstRow(), range.lastColumn());
+            axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
+        }
+
+        for (int row=firstDataRow; row<=range.lastRow(); ++row) {
+            CellRange subRange(row, range.firstColumn(), row, range.lastColumn());
+            QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
+            series->axDataSource_numRef = axDataSouruce_numRef;
+            series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
+            d->seriesList.append(series);
+        }
+    }
+}
+
+/*!
+ * Set the type of the chart to \a type
+ */
+void Chart::setChartType(ChartType type)
+{
+    Q_D(Chart);
+    d->chartType = type;
+}
+
+/*!
+ * \internal
+ *
+ */
+void Chart::setChartStyle(int id)
+{
+    Q_UNUSED(id)
+    //!Todo
+}
+
+/*!
+ * \internal
+ */
+void Chart::saveToXmlFile(QIODevice *device) const
+{
+    Q_D(const Chart);
+
+    QXmlStreamWriter writer(device);
+
+    writer.writeStartDocument(QStringLiteral("1.0"), true);
+    writer.writeStartElement(QStringLiteral("c:chartSpace"));
+    writer.writeAttribute(QStringLiteral("xmlns:c"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart"));
+    writer.writeAttribute(QStringLiteral("xmlns:a"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main"));
+    writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
+
+    d->saveXmlChart(writer);
+
+    writer.writeEndElement();//c:chartSpace
+    writer.writeEndDocument();
+}
+
+/*!
+ * \internal
+ */
+bool Chart::loadFromXmlFile(QIODevice *device)
+{
+    Q_D(Chart);
+
+    QXmlStreamReader reader(device);
+    while (!reader.atEnd()) {
+        reader.readNextStartElement();
+        if (reader.tokenType() == QXmlStreamReader::StartElement) {
+            if (reader.name() == QLatin1String("chart")) {
+                if (!d->loadXmlChart(reader))
+                    return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool ChartPrivate::loadXmlChart(QXmlStreamReader &reader)
+{
+    Q_ASSERT(reader.name() == QLatin1String("chart"));
+
+    while (!reader.atEnd()) {
+        reader.readNextStartElement();
+        if (reader.tokenType() == QXmlStreamReader::StartElement) {
+            if (reader.name() == QLatin1String("plotArea")) {
+                if (!loadXmlPlotArea(reader))
+                    return false;
+            } else if (reader.name() == QLatin1String("legend")) {
+                //!Todo
+            }
+        } else if (reader.tokenType() == QXmlStreamReader::EndElement &&
+                   reader.name() == QLatin1String("chart")) {
+            break;
+        }
+    }
+    return true;
+}
+
+bool ChartPrivate::loadXmlPlotArea(QXmlStreamReader &reader)
+{
+    Q_ASSERT(reader.name() == QLatin1String("plotArea"));
+
+    while (!reader.atEnd()) {
+        reader.readNextStartElement();
+        if (reader.tokenType() == QXmlStreamReader::StartElement) {
+            if (reader.name() == QLatin1String("layout")) {
+                //!ToDo
+            } else if (reader.name().endsWith(QLatin1String("Chart"))) {
+                //For pieChart, barChart, ...
+                loadXmlXxxChart(reader);
+            } else if (reader.name().endsWith(QLatin1String("Ax"))) {
+                //For valAx, catAx, serAx, dateAx
+                loadXmlAxis(reader);
+            }
+
+        } else if (reader.tokenType() == QXmlStreamReader::EndElement &&
+                   reader.name() == QLatin1String("plotArea")) {
+            break;
+        }
+    }
+    return true;
+}
+
+bool ChartPrivate::loadXmlXxxChart(QXmlStreamReader &reader)
+{
+    QStringRef name = reader.name();
+    if (name == QLatin1String("pieChart")) chartType = Chart::CT_Pie;
+    else if (name == QLatin1String("pie3DChart")) chartType = Chart::CT_Pie3D;
+    else if (name == QLatin1String("barChart")) chartType = Chart::CT_Bar;
+    else if (name == QLatin1String("bar3DChart")) chartType = Chart::CT_Bar3D;
+    else if (name == QLatin1String("lineChart")) chartType = Chart::CT_Line;
+    else if (name == QLatin1String("line3DChart")) chartType = Chart::CT_Line3D;
+    else if (name == QLatin1String("scatterChart")) chartType = Chart::CT_Scatter;
+    else if (name == QLatin1String("areaChart")) chartType = Chart::CT_Area;
+    else if (name == QLatin1String("area3DChart")) chartType = Chart::CT_Area3D;
+    else if (name == QLatin1String("doughnutChart")) chartType = Chart::CT_Doughnut;
+    else qDebug()<<"Cann't load chart: "<<name;
+
+    while (!reader.atEnd()) {
+        reader.readNextStartElement();
+        if (reader.tokenType() == QXmlStreamReader::StartElement) {
+            if (reader.name() == QLatin1String("ser")) {
+                loadXmlSer(reader);
+            } else if (reader.name() == QLatin1String("axId")) {
+
+            }
+        } else if (reader.tokenType() == QXmlStreamReader::EndElement
+                   && reader.name() == name) {
+            break;
+        }
+    }
+    return true;
+}
+
+bool ChartPrivate::loadXmlSer(QXmlStreamReader &reader)
+{
+    Q_ASSERT(reader.name() == QLatin1String("ser"));
+
+    QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
+    seriesList.append(series);
+
+    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
+                                && reader.name() == QLatin1String("ser"))) {
+        if (reader.readNextStartElement()) {
+            QStringRef name = reader.name();
+            if (name == QLatin1String("cat") || name == QLatin1String("xVal")) {
+                while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
+                                            && reader.name() == name)) {
+                    if (reader.readNextStartElement()) {
+                        if (reader.name() == QLatin1String("numRef"))
+                            series->axDataSource_numRef = loadXmlNumRef(reader);
+                    }
+                }
+            } else if (name == QLatin1String("val") || name == QLatin1String("yVal")) {
+                while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
+                                            && reader.name() == name)) {
+                    if (reader.readNextStartElement()) {
+                        if (reader.name() == QLatin1String("numRef"))
+                            series->numberDataSource_numRef = loadXmlNumRef(reader);
+                    }
+                }
+            } else if (name == QLatin1String("extLst")) {
+                while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
+                                            && reader.name() == name)) {
+                    reader.readNextStartElement();
+                }
+            }
+        }
+    }
+
+    return true;
+}
+
+
+QString ChartPrivate::loadXmlNumRef(QXmlStreamReader &reader)
+{
+    Q_ASSERT(reader.name() == QLatin1String("numRef"));
+
+    while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement
+                                && reader.name() == QLatin1String("numRef"))) {
+        if (reader.readNextStartElement()) {
+            if (reader.name() == QLatin1String("f"))
+                return reader.readElementText();
+        }
+    }
+
+    return QString();
+}
+
+void ChartPrivate::saveXmlChart(QXmlStreamWriter &writer) const
+{
+    writer.writeStartElement(QStringLiteral("c:chart"));
+    writer.writeStartElement(QStringLiteral("c:plotArea"));
+    switch (chartType) {
+    case Chart::CT_Pie:
+    case Chart::CT_Pie3D:
+        saveXmlPieChart(writer);
+        break;
+    case Chart::CT_Bar:
+    case Chart::CT_Bar3D:
+        saveXmlBarChart(writer);
+        break;
+    case Chart::CT_Line:
+    case Chart::CT_Line3D:
+        saveXmlLineChart(writer);
+        break;
+    case Chart::CT_Scatter:
+        saveXmlScatterChart(writer);
+        break;
+    case Chart::CT_Area:
+    case Chart::CT_Area3D:
+        saveXmlAreaChart(writer);
+        break;
+    case Chart::CT_Doughnut:
+        saveXmlDoughnutChart(writer);
+        break;
+    default:
+        break;
+    }
+    saveXmlAxes(writer);
+    writer.writeEndElement(); //plotArea
+
+//    saveXmlLegend(writer);
+
+    writer.writeEndElement(); //chart
+}
+
+void ChartPrivate::saveXmlPieChart(QXmlStreamWriter &writer) const
+{
+    QString name = chartType==Chart::CT_Pie ? QStringLiteral("c:pieChart") : QStringLiteral("c:pie3DChart");
+
+    writer.writeStartElement(name);
+
+    //Do the same behavior as Excel, Pie prefer varyColors
+    writer.writeEmptyElement(QStringLiteral("c:varyColors"));
+    writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
+
+    for (int i=0; i<seriesList.size(); ++i)
+        saveXmlSer(writer, seriesList[i].data(), i);
+
+    writer.writeEndElement(); //pieChart, pie3DChart
+}
+
+void ChartPrivate::saveXmlBarChart(QXmlStreamWriter &writer) const
+{
+    QString name = chartType==Chart::CT_Bar ? QStringLiteral("c:barChart") : QStringLiteral("c:bar3DChart");
+
+    writer.writeStartElement(name);
+
+    writer.writeEmptyElement(QStringLiteral("c:barDir"));
+    writer.writeAttribute(QStringLiteral("val"), QStringLiteral("col"));
+
+    for (int i=0; i<seriesList.size(); ++i)
+        saveXmlSer(writer, seriesList[i].data(), i);
+
+    if (axisList.isEmpty()) {
+        //The order the axes??
+        const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)));
+        const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
+    }
+
+    //Note: Bar3D have 2~3 axes
+    Q_ASSERT(axisList.size()==2 || (axisList.size()==3 && chartType==Chart::CT_Bar3D));
+
+    for (int i=0; i<axisList.size(); ++i) {
+        writer.writeEmptyElement(QStringLiteral("c:axId"));
+        writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
+    }
+
+    writer.writeEndElement(); //barChart, bar3DChart
+}
+
+void ChartPrivate::saveXmlLineChart(QXmlStreamWriter &writer) const
+{
+    QString name = chartType==Chart::CT_Line ? QStringLiteral("c:lineChart") : QStringLiteral("c:line3DChart");
+
+    writer.writeStartElement(name);
+
+    writer.writeEmptyElement(QStringLiteral("grouping"));
+
+    for (int i=0; i<seriesList.size(); ++i)
+        saveXmlSer(writer, seriesList[i].data(), i);
+
+    if (axisList.isEmpty()) {
+        const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)));
+        const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
+        if (chartType==Chart::CT_Line3D)
+            const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Ser, XlsxAxis::Bottom, 2, 0)));
+    }
+
+    Q_ASSERT((axisList.size()==2||chartType==Chart::CT_Line)|| (axisList.size()==3 && chartType==Chart::CT_Line3D));
+
+    for (int i=0; i<axisList.size(); ++i) {
+        writer.writeEmptyElement(QStringLiteral("c:axId"));
+        writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
+    }
+
+    writer.writeEndElement(); //lineChart, line3DChart
+}
+
+void ChartPrivate::saveXmlScatterChart(QXmlStreamWriter &writer) const
+{
+    const QString name = QStringLiteral("c:scatterChart");
+
+    writer.writeStartElement(name);
+
+    writer.writeEmptyElement(QStringLiteral("c:scatterStyle"));
+
+    for (int i=0; i<seriesList.size(); ++i)
+        saveXmlSer(writer, seriesList[i].data(), i);
+
+    if (axisList.isEmpty()) {
+        const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Bottom, 0, 1)));
+        const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
+    }
+
+    Q_ASSERT(axisList.size()==2);
+
+    for (int i=0; i<axisList.size(); ++i) {
+        writer.writeEmptyElement(QStringLiteral("c:axId"));
+        writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
+    }
+
+    writer.writeEndElement(); //c:scatterChart
+}
+
+void ChartPrivate::saveXmlAreaChart(QXmlStreamWriter &writer) const
+{
+    QString name = chartType==Chart::CT_Area ? QStringLiteral("c:areaChart") : QStringLiteral("c:area3DChart");
+
+    writer.writeStartElement(name);
+
+    writer.writeEmptyElement(QStringLiteral("grouping"));
+
+    for (int i=0; i<seriesList.size(); ++i)
+        saveXmlSer(writer, seriesList[i].data(), i);
+
+    if (axisList.isEmpty()) {
+        const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)));
+        const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
+    }
+
+    //Note: Area3D have 2~3 axes
+    Q_ASSERT(axisList.size()==2 || (axisList.size()==3 && chartType==Chart::CT_Area3D));
+
+    for (int i=0; i<axisList.size(); ++i) {
+        writer.writeEmptyElement(QStringLiteral("c:axId"));
+        writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
+    }
+
+    writer.writeEndElement(); //lineChart, line3DChart
+}
+
+void ChartPrivate::saveXmlDoughnutChart(QXmlStreamWriter &writer) const
+{
+    QString name = QStringLiteral("c:doughnutChart");
+
+    writer.writeStartElement(name);
+
+    writer.writeEmptyElement(QStringLiteral("c:varyColors"));
+    writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
+
+    for (int i=0; i<seriesList.size(); ++i)
+        saveXmlSer(writer, seriesList[i].data(), i);
+
+    writer.writeStartElement(QStringLiteral("c:holeSize"));
+    writer.writeAttribute(QStringLiteral("val"), QString::number(50));
+
+    writer.writeEndElement();
+}
+
+void ChartPrivate::saveXmlSer(QXmlStreamWriter &writer, XlsxSeries *ser, int id) const
+{
+    writer.writeStartElement(QStringLiteral("c:ser"));
+    writer.writeEmptyElement(QStringLiteral("c:idx"));
+    writer.writeAttribute(QStringLiteral("val"), QString::number(id));
+    writer.writeEmptyElement(QStringLiteral("c:order"));
+    writer.writeAttribute(QStringLiteral("val"), QString::number(id));
+
+    if (!ser->axDataSource_numRef.isEmpty()) {
+        if (chartType == Chart::CT_Scatter || chartType == Chart::CT_Bubble)
+            writer.writeStartElement(QStringLiteral("c:xVal"));
+        else
+            writer.writeStartElement(QStringLiteral("c:cat"));
+        writer.writeStartElement(QStringLiteral("c:numRef"));
+        writer.writeTextElement(QStringLiteral("c:f"), ser->axDataSource_numRef);
+        writer.writeEndElement();//c:numRef
+        writer.writeEndElement();//c:cat or c:xVal
+    }
+
+    if (!ser->numberDataSource_numRef.isEmpty()) {
+        if (chartType == Chart::CT_Scatter || chartType == Chart::CT_Bubble)
+            writer.writeStartElement(QStringLiteral("c:yVal"));
+        else
+            writer.writeStartElement(QStringLiteral("c:val"));
+        writer.writeStartElement(QStringLiteral("c:numRef"));
+        writer.writeTextElement(QStringLiteral("c:f"), ser->numberDataSource_numRef);
+        writer.writeEndElement();//c:numRef
+        writer.writeEndElement();//c:val or c:yVal
+    }
+
+    writer.writeEndElement();//c:ser
+}
+
+bool ChartPrivate::loadXmlAxis(QXmlStreamReader &reader)
+{
+    Q_ASSERT(reader.name().endsWith(QLatin1String("Ax")));
+    QString name = reader.name().toString();
+
+    XlsxAxis *axis = new XlsxAxis;
+    if (name == QLatin1String("valAx"))
+        axis->type = XlsxAxis::T_Val;
+    else if (name == QLatin1String("catAx"))
+        axis->type = XlsxAxis::T_Cat;
+    else if (name == QLatin1String("serAx"))
+        axis->type = XlsxAxis::T_Ser;
+    else
+        axis->type = XlsxAxis::T_Date;
+
+    axisList.append(QSharedPointer<XlsxAxis>(axis));
+
+    while (!reader.atEnd()) {
+        reader.readNextStartElement();
+        if (reader.tokenType() == QXmlStreamReader::StartElement) {
+            if (reader.name() == QLatin1String("axPos")) {
+                QXmlStreamAttributes attrs = reader.attributes();
+                QStringRef pos = attrs.value(QLatin1String("val"));
+                if (pos==QLatin1String("l"))
+                    axis->axisPos = XlsxAxis::Left;
+                else if (pos==QLatin1String("r"))
+                    axis->axisPos = XlsxAxis::Right;
+                else if (pos==QLatin1String("b"))
+                    axis->axisPos = XlsxAxis::Bottom;
+                else
+                    axis->axisPos = XlsxAxis::Top;
+            } else if (reader.name() == QLatin1String("axId")) {
+                axis->axisId = reader.attributes().value(QLatin1String("val")).toString().toInt();
+            } else if (reader.name() == QLatin1String("crossAx")) {
+                axis->crossAx = reader.attributes().value(QLatin1String("val")).toString().toInt();
+            }
+        } else if (reader.tokenType() == QXmlStreamReader::EndElement
+                   && reader.name() == name) {
+            break;
+        }
+    }
+
+    return true;
+}
+
+void ChartPrivate::saveXmlAxes(QXmlStreamWriter &writer) const
+{
+    for (int i=0; i<axisList.size(); ++i) {
+        XlsxAxis *axis = axisList[i].data();
+        QString name;
+        switch (axis->type) {
+        case XlsxAxis::T_Cat: name = QStringLiteral("c:catAx"); break;
+        case XlsxAxis::T_Val: name = QStringLiteral("c:valAx"); break;
+        case XlsxAxis::T_Ser: name = QStringLiteral("c:serAx"); break;
+        case XlsxAxis::T_Date: name = QStringLiteral("c:dateAx"); break;
+        default: break;
+        }
+
+        QString pos;
+        switch (axis->axisPos) {
+        case XlsxAxis::Top: pos = QStringLiteral("t"); break;
+        case XlsxAxis::Bottom: pos = QStringLiteral("b"); break;
+        case XlsxAxis::Left: pos = QStringLiteral("l"); break;
+        case XlsxAxis::Right: pos = QStringLiteral("r"); break;
+        default: break;
+        }
+
+        writer.writeStartElement(name);
+        writer.writeEmptyElement(QStringLiteral("c:axId"));
+        writer.writeAttribute(QStringLiteral("val"), QString::number(axis->axisId));
+
+        writer.writeStartElement(QStringLiteral("c:scaling"));
+        writer.writeEmptyElement(QStringLiteral("c:orientation"));
+        writer.writeAttribute(QStringLiteral("val"), QStringLiteral("minMax"));
+        writer.writeEndElement();//c:scaling
+
+        writer.writeEmptyElement(QStringLiteral("c:axPos"));
+        writer.writeAttribute(QStringLiteral("val"), pos);
+
+        writer.writeEmptyElement(QStringLiteral("c:crossAx"));
+        writer.writeAttribute(QStringLiteral("val"), QString::number(axis->crossAx));
+
+        writer.writeEndElement();//name
+    }
+}
+
+QT_END_NAMESPACE_XLSX
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)