commit 060e7ea0b2ba5161cff1ea07b61af9a4d1c40893 Author: nasiCurious Date: Sun Feb 12 16:52:07 2023 +0330 Refactoring Code and change it for pattern file with out value diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75c107b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pro.user diff --git a/XlsxToXmlConverter.pro b/XlsxToXmlConverter.pro new file mode 100644 index 0000000..7a4e750 --- /dev/null +++ b/XlsxToXmlConverter.pro @@ -0,0 +1,44 @@ +QT += gui +QT += xml +QT += xmlpatterns + + +TARGET = QXlsx + +QT += core +QT += gui-private + +CONFIG += c++11 console +CONFIG -= app_bundle + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + + + + + + +SOURCES += $$files(src/*.cpp, true) +SOURCES += main.cpp +HEADERS += $$files(include/QXlsx/*.h, true) +HEADERS += $$files(include/*.h, true) + +INCLUDEPATH += "$$PWD/include/" \ +INCLUDEPATH += "$$PWD/include/QXlsx" \ + $$PWD/.. + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + + diff --git a/include/QXlsx/xlsxabstractooxmlfile.h b/include/QXlsx/xlsxabstractooxmlfile.h new file mode 100644 index 0000000..adf9331 --- /dev/null +++ b/include/QXlsx/xlsxabstractooxmlfile.h @@ -0,0 +1,48 @@ +//xlsxabstractooxmlfile.h + +#ifndef QXLSX_XLSXABSTRACTOOXMLFILE_H +#define QXLSX_XLSXABSTRACTOOXMLFILE_H + +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Relationships; + +class AbstractOOXmlFilePrivate; + +class QXLSX_EXPORT AbstractOOXmlFile +{ + Q_DECLARE_PRIVATE(AbstractOOXmlFile) + +public: + enum CreateFlag + { + F_NewFromScratch, + F_LoadFromExists + }; + +public: + virtual ~AbstractOOXmlFile(); + + virtual void saveToXmlFile(QIODevice* device) const = 0; + virtual bool loadFromXmlFile(QIODevice* device) = 0; + + virtual QByteArray saveToXmlData() const; + virtual bool loadFromXmlData(const QByteArray& data); + + Relationships* relationships() const; + + void setFilePath(const QString path); + QString filePath() const; + +protected: + AbstractOOXmlFile(CreateFlag flag); + AbstractOOXmlFile(AbstractOOXmlFilePrivate* d); + + AbstractOOXmlFilePrivate* d_ptr; +}; + +QT_END_NAMESPACE_XLSX + +#endif //QXLSX_XLSXABSTRACTOOXMLFILE_H diff --git a/include/QXlsx/xlsxabstractooxmlfile_p.h b/include/QXlsx/xlsxabstractooxmlfile_p.h new file mode 100644 index 0000000..30fea98 --- /dev/null +++ b/include/QXlsx/xlsxabstractooxmlfile_p.h @@ -0,0 +1,31 @@ +// xlsxabstractooxmlfile_p.h + +#ifndef XLSXOOXMLFILE_P_H +#define XLSXOOXMLFILE_P_H + +#include "xlsxglobal.h" + +#include "xlsxabstractooxmlfile.h" +#include "xlsxrelationships_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +class AbstractOOXmlFilePrivate +{ + Q_DECLARE_PUBLIC(AbstractOOXmlFile) + +public: + AbstractOOXmlFilePrivate(AbstractOOXmlFile* q, AbstractOOXmlFile::CreateFlag flag); + virtual ~AbstractOOXmlFilePrivate(); + +public: + QString filePathInPackage; //such as "xl/worksheets/sheet1.xml" + + Relationships *relationships; + AbstractOOXmlFile::CreateFlag flag; + AbstractOOXmlFile *q_ptr; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXOOXMLFILE_P_H diff --git a/include/QXlsx/xlsxabstractsheet.h b/include/QXlsx/xlsxabstractsheet.h new file mode 100644 index 0000000..8104740 --- /dev/null +++ b/include/QXlsx/xlsxabstractsheet.h @@ -0,0 +1,49 @@ +// xlsxabstractsheet.h + +#ifndef XLSXABSTRACTSHEET_H +#define XLSXABSTRACTSHEET_H + +#include "xlsxglobal.h" +#include "xlsxabstractooxmlfile.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Workbook; +class Drawing; +class AbstractSheetPrivate; + +class QXLSX_EXPORT AbstractSheet : public AbstractOOXmlFile +{ + Q_DECLARE_PRIVATE(AbstractSheet) + +public: + Workbook *workbook() const; + +public: + // NOTE: If all Qt compiler supports C++1x, recommend to use a 'class enum'. + enum SheetType { ST_WorkSheet, ST_ChartSheet, ST_DialogSheet, ST_MacroSheet }; + enum SheetState { SS_Visible,SS_Hidden, SS_VeryHidden }; + +public: + QString sheetName() const; + SheetType sheetType() const; + SheetState sheetState() const; + void setSheetState(SheetState ss); + bool isHidden() const; + bool isVisible() const; + void setHidden(bool hidden); + void setVisible(bool visible); + +protected: + friend class Workbook; + AbstractSheet(const QString &sheetName, int sheetId, Workbook *book, AbstractSheetPrivate *d); + virtual AbstractSheet *copy(const QString &distName, int distId) const = 0; + void setSheetName(const QString &sheetName); + void setSheetType(SheetType type); + int sheetId() const; + + Drawing *drawing() const; +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXABSTRACTSHEET_H diff --git a/include/QXlsx/xlsxabstractsheet_p.h b/include/QXlsx/xlsxabstractsheet_p.h new file mode 100644 index 0000000..b95a058 --- /dev/null +++ b/include/QXlsx/xlsxabstractsheet_p.h @@ -0,0 +1,36 @@ +// xlsxabstractsheet_p/h + +#ifndef XLSXABSTRACTSHEET_P_H +#define XLSXABSTRACTSHEET_P_H + +#include + +#include + +#include "xlsxglobal.h" + +#include "xlsxabstractsheet.h" +#include "xlsxabstractooxmlfile_p.h" +#include "xlsxdrawing_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +class AbstractSheetPrivate : public AbstractOOXmlFilePrivate +{ + Q_DECLARE_PUBLIC(AbstractSheet) +public: + AbstractSheetPrivate(AbstractSheet *p, AbstractSheet::CreateFlag flag); + ~AbstractSheetPrivate(); + + Workbook *workbook; + std::shared_ptr drawing; + + QString name; + int id; + AbstractSheet::SheetState sheetState; + AbstractSheet::SheetType type; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXABSTRACTSHEET_P_H diff --git a/include/QXlsx/xlsxcell.h b/include/QXlsx/xlsxcell.h new file mode 100644 index 0000000..619d41f --- /dev/null +++ b/include/QXlsx/xlsxcell.h @@ -0,0 +1,82 @@ +// xlsxcell.h + +#ifndef QXLSX_XLSXCELL_H +#define QXLSX_XLSXCELL_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" +#include "xlsxformat.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Worksheet; +class Format; +class CellFormula; +class CellPrivate; +class WorksheetPrivate; + +class QXLSX_EXPORT Cell +{ + Q_DECLARE_PRIVATE(Cell) + +private: + friend class Worksheet; + friend class WorksheetPrivate; + +public: + enum CellType // See ECMA 376, 18.18.11. ST_CellType (Cell Type) for more information. + { + BooleanType, + DateType, + ErrorType, + InlineStringType, + NumberType, + SharedStringType, + StringType, + CustomType, // custom or un-defined cell type + }; + +public: + Cell(const QVariant &data = QVariant(), + CellType type = NumberType, + const Format &format = Format(), + Worksheet *parent = nullptr, + qint32 styleIndex = (-1) ); + Cell(const Cell * const cell); + ~Cell(); + +public: + CellPrivate * const d_ptr; // See D-pointer and Q-pointer of Qt, for more information. + +public: + CellType cellType() const; + QVariant value() const; + QVariant readValue() const; + Format format() const; + + bool hasFormula() const; + CellFormula formula() const; + + bool isDateTime() const; + QVariant dateTime() const; // QDateTime, QDate, QTime + + bool isRichString() const; + + qint32 styleNumber() const; + + static bool isDateType(CellType cellType, const Format &format); + +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXCELL_H diff --git a/include/QXlsx/xlsxcell_p.h b/include/QXlsx/xlsxcell_p.h new file mode 100644 index 0000000..0abcc42 --- /dev/null +++ b/include/QXlsx/xlsxcell_p.h @@ -0,0 +1,41 @@ +// xlsxcell_p.h + +#ifndef XLSXCELL_P_H +#define XLSXCELL_P_H + +#include +#include +#include + +#include "xlsxglobal.h" +#include "xlsxcell.h" +#include "xlsxcellrange.h" +#include "xlsxrichstring.h" +#include "xlsxcellformula.h" + +QT_BEGIN_NAMESPACE_XLSX + +class CellPrivate +{ + Q_DECLARE_PUBLIC(Cell) +public: + CellPrivate(Cell *p); + CellPrivate(const CellPrivate * const cp); +public: + Worksheet *parent; + Cell *q_ptr; +public: + Cell::CellType cellType; + QVariant value; + + CellFormula formula; + Format format; + + RichString richString; + + qint32 styleNumber; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXCELL_P_H diff --git a/include/QXlsx/xlsxcellformula.h b/include/QXlsx/xlsxcellformula.h new file mode 100644 index 0000000..f0f4de4 --- /dev/null +++ b/include/QXlsx/xlsxcellformula.h @@ -0,0 +1,55 @@ +// xlsxcellformula.h + +#ifndef QXLSX_XLSXCELLFORMULA_H +#define QXLSX_XLSXCELLFORMULA_H + +#include "xlsxglobal.h" + +#include + +class QXmlStreamWriter; +class QXmlStreamReader; + +QT_BEGIN_NAMESPACE_XLSX + +class CellFormulaPrivate; +class CellRange; +class Worksheet; +class WorksheetPrivate; + +class QXLSX_EXPORT CellFormula +{ +public: + enum FormulaType { NormalType, ArrayType, DataTableType, SharedType }; + +public: + CellFormula(); + CellFormula(const char *formula, FormulaType type=NormalType); + CellFormula(const QString &formula, FormulaType type=NormalType); + CellFormula(const QString &formula, const CellRange &ref, FormulaType type); + CellFormula(const CellFormula &other); + ~CellFormula(); + +public: + CellFormula &operator =(const CellFormula &other); + bool isValid() const; + + FormulaType formulaType() const; + QString formulaText() const; + CellRange reference() const; + int sharedIndex() const; + + bool operator == (const CellFormula &formula) const; + bool operator != (const CellFormula &formula) const; + + bool saveToXml(QXmlStreamWriter &writer) const; + bool loadFromXml(QXmlStreamReader &reader); +private: + friend class Worksheet; + friend class WorksheetPrivate; + QExplicitlySharedDataPointer d; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXCELLFORMULA_H diff --git a/include/QXlsx/xlsxcellformula_p.h b/include/QXlsx/xlsxcellformula_p.h new file mode 100644 index 0000000..4390089 --- /dev/null +++ b/include/QXlsx/xlsxcellformula_p.h @@ -0,0 +1,31 @@ +// xlsxcellformula_p.h + +#ifndef XLSXCELLFORMULA_P_H +#define XLSXCELLFORMULA_P_H + +#include "xlsxglobal.h" +#include "xlsxcellformula.h" +#include "xlsxcellrange.h" + +#include +#include + +QT_BEGIN_NAMESPACE_XLSX + +class CellFormulaPrivate : public QSharedData +{ +public: + CellFormulaPrivate(const QString &formula, const CellRange &reference, CellFormula::FormulaType type); + CellFormulaPrivate(const CellFormulaPrivate &other); + ~CellFormulaPrivate(); + + QString formula; //formula contents + CellFormula::FormulaType type; + CellRange reference; + bool ca; //Calculate Cell + int si; //Shared group index +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXCELLFORMULA_P_H diff --git a/include/QXlsx/xlsxcelllocation.h b/include/QXlsx/xlsxcelllocation.h new file mode 100644 index 0000000..ccb5cdb --- /dev/null +++ b/include/QXlsx/xlsxcelllocation.h @@ -0,0 +1,33 @@ +// xlsxcelllocation.h + +#ifndef CELL_LOCATION_H +#define CELL_LOCATION_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Cell; + +class QXLSX_EXPORT CellLocation +{ +public: + CellLocation(); + + int col; + int row; + + std::shared_ptr cell; +}; + +QT_END_NAMESPACE_XLSX +#endif diff --git a/include/QXlsx/xlsxcellrange.h b/include/QXlsx/xlsxcellrange.h new file mode 100644 index 0000000..719317c --- /dev/null +++ b/include/QXlsx/xlsxcellrange.h @@ -0,0 +1,73 @@ +// xlsxcellrange.h + +#ifndef QXLSX_XLSXCELLRANGE_H +#define QXLSX_XLSXCELLRANGE_H + +#include +#include + +#include "xlsxglobal.h" +#include "xlsxcellreference.h" + +QT_BEGIN_NAMESPACE_XLSX + +// dev57 +class QXLSX_EXPORT CellRange +{ +public: + CellRange(); + CellRange(int firstRow, int firstColumn, int lastRow, int lastColumn); + CellRange(const CellReference &topLeft, const CellReference &bottomRight); + CellRange(const QString &range); + CellRange(const char *range); + CellRange(const CellRange &other); + ~CellRange(); + + QString toString(bool row_abs=false, bool col_abs=false) const; + bool isValid() const; + inline void setFirstRow(int row) { top = row; } + inline void setLastRow(int row) { bottom = row; } + inline void setFirstColumn(int col) { left = col; } + inline void setLastColumn(int col) { right = col; } + inline int firstRow() const { return top; } + inline int lastRow() const { return bottom; } + inline int firstColumn() const { return left; } + inline int lastColumn() const { return right; } + inline int rowCount() const { return bottom - top + 1; } + inline int columnCount() const { return right - left + 1; } + inline CellReference topLeft() const { return CellReference(top, left); } + inline CellReference topRight() const { return CellReference(top, right); } + inline CellReference bottomLeft() const { return CellReference(bottom, left); } + inline CellReference bottomRight() const { return CellReference(bottom, right); } + + inline void operator =(const CellRange &other) + { + top = other.top; + bottom = other.bottom; + left = other.left; + right = other.right; + } + inline bool operator ==(const CellRange &other) const + { + return top==other.top && bottom==other.bottom + && left == other.left && right == other.right; + } + inline bool operator !=(const CellRange &other) const + { + return top!=other.top || bottom!=other.bottom + || left != other.left || right != other.right; + } +private: + void init(const QString &range); + + int top; + int left; + int bottom; + int right; +}; + +QT_END_NAMESPACE_XLSX + +Q_DECLARE_TYPEINFO(QXlsx::CellRange, Q_MOVABLE_TYPE); + +#endif // QXLSX_XLSXCELLRANGE_H diff --git a/include/QXlsx/xlsxcellreference.h b/include/QXlsx/xlsxcellreference.h new file mode 100644 index 0000000..c20abdc --- /dev/null +++ b/include/QXlsx/xlsxcellreference.h @@ -0,0 +1,47 @@ +// xlsxcellreference.h + +#ifndef QXLSX_XLSXCELLREFERENCE_H +#define QXLSX_XLSXCELLREFERENCE_H + +#include + +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class QXLSX_EXPORT CellReference +{ +public: + CellReference(); + CellReference(int row, int column); + CellReference(const QString &cell); + CellReference(const char *cell); + CellReference(const CellReference &other); + ~CellReference(); + + QString toString(bool row_abs=false, bool col_abs=false) const; + static CellReference fromString(const QString &cell); + bool isValid() const; + inline void setRow(int row) { _row = row; } + inline void setColumn(int col) { _column = col; } + inline int row() const { return _row; } + inline int column() const { return _column; } + + inline bool operator ==(const CellReference &other) const + { + return _row==other._row && _column==other._column; + } + inline bool operator !=(const CellReference &other) const + { + return _row!=other._row || _column!=other._column; + } +private: + void init(const QString &cell); + int _row, _column; +}; + +QT_END_NAMESPACE_XLSX + +Q_DECLARE_TYPEINFO(QXlsx::CellReference, Q_MOVABLE_TYPE); + +#endif // QXLSX_XLSXCELLREFERENCE_H diff --git a/include/QXlsx/xlsxchart.h b/include/QXlsx/xlsxchart.h new file mode 100644 index 0000000..b724e79 --- /dev/null +++ b/include/QXlsx/xlsxchart.h @@ -0,0 +1,58 @@ +// xlsxchart.h + +#ifndef QXLSX_CHART_H +#define QXLSX_CHART_H + +#include +#include +#include + +#include "xlsxabstractooxmlfile.h" + +QT_BEGIN_NAMESPACE_XLSX + +class AbstractSheet; +class Worksheet; +class ChartPrivate; +class CellRange; +class DrawingAnchor; + +class QXLSX_EXPORT Chart : public AbstractOOXmlFile +{ + Q_DECLARE_PRIVATE(Chart) +public: + enum ChartType { // 16 type of chart (ECMA 376) + CT_NoStatementChart = 0, // Zero is internally used for unknown types + CT_AreaChart, CT_Area3DChart, CT_LineChart, + CT_Line3DChart, CT_StockChart, CT_RadarChart, + CT_ScatterChart, CT_PieChart, CT_Pie3DChart, + CT_DoughnutChart, CT_BarChart, CT_Bar3DChart, + CT_OfPieChart, CT_SurfaceChart, CT_Surface3DChart, + CT_BubbleChart, + }; + enum ChartAxisPos { None = (-1), Left = 0, Right, Top, Bottom }; +private: + friend class AbstractSheet; + friend class Worksheet; + friend class Chartsheet; + friend class DrawingAnchor; +private: + Chart(AbstractSheet *parent, CreateFlag flag); +public: + ~Chart(); +public: + void addSeries(const CellRange &range, AbstractSheet *sheet = NULL, bool headerH = false, bool headerV = false, bool swapHeaders = false); + void setChartType(ChartType type); + void setChartStyle(int id); + void setAxisTitle(Chart::ChartAxisPos pos, QString axisTitle); + void setChartTitle(QString strchartTitle); + void setChartLegend(Chart::ChartAxisPos legendPos, bool overlap = false); + void setGridlinesEnable(bool majorGridlinesEnable = false, bool minorGridlinesEnable = false); +public: + bool loadFromXmlFile(QIODevice *device) override; + void saveToXmlFile(QIODevice *device) const override; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_CHART_H diff --git a/include/QXlsx/xlsxchart_p.h b/include/QXlsx/xlsxchart_p.h new file mode 100644 index 0000000..1d5de8c --- /dev/null +++ b/include/QXlsx/xlsxchart_p.h @@ -0,0 +1,148 @@ +// xlsxchart_p.h + +#ifndef QXLSX_CHART_P_H +#define QXLSX_CHART_P_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "xlsxabstractooxmlfile_p.h" +#include "xlsxchart.h" + +QT_BEGIN_NAMESPACE_XLSX + +class XlsxSeries +{ +public: + //At present, we care about number cell ranges only! + QString numberDataSource_numRef; // yval, val + QString axDataSource_numRef; // xval, cat + QString headerH_numRef; + QString headerV_numRef; + bool swapHeader = false; +}; + +class XlsxAxis +{ +public: + enum Type { T_None = (-1), T_Cat, T_Val, T_Date, T_Ser }; + enum AxisPos { None = (-1), Left, Right, Top, Bottom }; +public: + XlsxAxis(){} + + XlsxAxis( Type t, + XlsxAxis::AxisPos p, + int id, + int crossId, + QString axisTitle = QString()) + { + type = t; + axisPos = p; + axisId = id; + crossAx = crossId; + + if ( !axisTitle.isEmpty() ) + { + axisNames[ p ] = axisTitle; + } + } + +public: + Type type; + XlsxAxis::AxisPos axisPos; + int axisId; + int crossAx; + QMap< XlsxAxis::AxisPos, QString > axisNames; +}; + +class ChartPrivate : public AbstractOOXmlFilePrivate +{ + Q_DECLARE_PUBLIC(Chart) + +public: + ChartPrivate(Chart *q, Chart::CreateFlag flag); + ~ChartPrivate(); + +public: + bool loadXmlChart(QXmlStreamReader &reader); + bool loadXmlPlotArea(QXmlStreamReader &reader); +protected: + bool loadXmlPlotAreaElement(QXmlStreamReader &reader); +public: + bool loadXmlXxxChart(QXmlStreamReader &reader); + bool loadXmlSer(QXmlStreamReader &reader); + QString loadXmlNumRef(QXmlStreamReader &reader); + QString loadXmlStrRef(QXmlStreamReader &reader); + bool loadXmlChartTitle(QXmlStreamReader &reader); + bool loadXmlChartLegend(QXmlStreamReader &reader); +protected: + bool loadXmlChartTitleTx(QXmlStreamReader &reader); + bool loadXmlChartTitleTxRich(QXmlStreamReader &reader); + bool loadXmlChartTitleTxRichP(QXmlStreamReader &reader); + bool loadXmlChartTitleTxRichP_R(QXmlStreamReader &reader); +protected: + bool loadXmlAxisCatAx(QXmlStreamReader &reader); + bool loadXmlAxisDateAx(QXmlStreamReader &reader); + bool loadXmlAxisSerAx(QXmlStreamReader &reader); + bool loadXmlAxisValAx(QXmlStreamReader &reader); + bool loadXmlAxisEG_AxShared(QXmlStreamReader &reader, XlsxAxis* axis); + bool loadXmlAxisEG_AxShared_Scaling(QXmlStreamReader &reader, XlsxAxis* axis); + bool loadXmlAxisEG_AxShared_Title(QXmlStreamReader &reader, XlsxAxis* axis); + bool loadXmlAxisEG_AxShared_Title_Overlay(QXmlStreamReader &reader, XlsxAxis* axis); + bool loadXmlAxisEG_AxShared_Title_Tx(QXmlStreamReader &reader, XlsxAxis* axis); + bool loadXmlAxisEG_AxShared_Title_Tx_Rich(QXmlStreamReader &reader, XlsxAxis* axis); + bool loadXmlAxisEG_AxShared_Title_Tx_Rich_P(QXmlStreamReader &reader, XlsxAxis* axis); + bool loadXmlAxisEG_AxShared_Title_Tx_Rich_P_pPr(QXmlStreamReader &reader, XlsxAxis* axis); + bool loadXmlAxisEG_AxShared_Title_Tx_Rich_P_R(QXmlStreamReader &reader, XlsxAxis* axis); + + QString readSubTree(QXmlStreamReader &reader); + +public: + void saveXmlChart(QXmlStreamWriter &writer) const; + void saveXmlChartTitle(QXmlStreamWriter &writer) const; + void saveXmlPieChart(QXmlStreamWriter &writer) const; + void saveXmlBarChart(QXmlStreamWriter &writer) const; + void saveXmlLineChart(QXmlStreamWriter &writer) const; + void saveXmlScatterChart(QXmlStreamWriter &writer) const; + void saveXmlAreaChart(QXmlStreamWriter &writer) const; + void saveXmlDoughnutChart(QXmlStreamWriter &writer) const; + void saveXmlSer(QXmlStreamWriter &writer, XlsxSeries *ser, int id) const; + void saveXmlAxis(QXmlStreamWriter &writer) const; + void saveXmlChartLegend(QXmlStreamWriter &writer) const; + +protected: + void saveXmlAxisCatAx(QXmlStreamWriter &writer, XlsxAxis* axis) const; + void saveXmlAxisDateAx(QXmlStreamWriter &writer, XlsxAxis* axis) const; + void saveXmlAxisSerAx(QXmlStreamWriter &writer, XlsxAxis* axis) const; + void saveXmlAxisValAx(QXmlStreamWriter &writer, XlsxAxis* axis) const; + + void saveXmlAxisEG_AxShared(QXmlStreamWriter &writer, XlsxAxis* axis) const; + void saveXmlAxisEG_AxShared_Title(QXmlStreamWriter &writer, XlsxAxis* axis) const; + QString GetAxisPosString( XlsxAxis::AxisPos axisPos ) const; + QString GetAxisName(XlsxAxis* ptrXlsxAxis) const; + +public: + Chart::ChartType chartType; + QList< std::shared_ptr > seriesList; + QList< std::shared_ptr > axisList; + QMap< XlsxAxis::AxisPos, QString > axisNames; + QString chartTitle; + AbstractSheet* sheet; + Chart::ChartAxisPos legendPos; + bool legendOverlay; + bool majorGridlinesEnabled; + bool minorGridlinesEnabled; + + QString layout; // only for storing a readed file +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_CHART_P_H diff --git a/include/QXlsx/xlsxchartsheet.h b/include/QXlsx/xlsxchartsheet.h new file mode 100644 index 0000000..e654af1 --- /dev/null +++ b/include/QXlsx/xlsxchartsheet.h @@ -0,0 +1,38 @@ +// xlsxchartsheet.h + +#ifndef XLSXCHARTSHEET_H +#define XLSXCHARTSHEET_H + +#include +#include + +#include "xlsxabstractsheet.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Workbook; +class DocumentPrivate; +class ChartsheetPrivate; +class Chart; + +class QXLSX_EXPORT Chartsheet : public AbstractSheet +{ + Q_DECLARE_PRIVATE(Chartsheet) + +public: + ~Chartsheet(); + Chart *chart(); + +private: + friend class DocumentPrivate; + friend class Workbook; + + Chartsheet(const QString &sheetName, int sheetId, Workbook *book, CreateFlag flag); + Chartsheet *copy(const QString &distName, int distId) const override; + + void saveToXmlFile(QIODevice *device) const override; + bool loadFromXmlFile(QIODevice *device) override; +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXCHARTSHEET_H diff --git a/include/QXlsx/xlsxchartsheet_p.h b/include/QXlsx/xlsxchartsheet_p.h new file mode 100644 index 0000000..21ade91 --- /dev/null +++ b/include/QXlsx/xlsxchartsheet_p.h @@ -0,0 +1,25 @@ +// xlsxchartsheet_p.h + +#ifndef XLSXCHARTSHEET_P_H +#define XLSXCHARTSHEET_P_H + +#include + +#include "xlsxglobal.h" +#include "xlsxchartsheet.h" +#include "xlsxabstractsheet_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +class ChartsheetPrivate : public AbstractSheetPrivate +{ + Q_DECLARE_PUBLIC(Chartsheet) +public: + ChartsheetPrivate(Chartsheet *p, Chartsheet::CreateFlag flag); + ~ChartsheetPrivate(); + + Chart *chart; +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXCHARTSHEET_P_H diff --git a/include/QXlsx/xlsxcolor_p.h b/include/QXlsx/xlsxcolor_p.h new file mode 100644 index 0000000..4fafacf --- /dev/null +++ b/include/QXlsx/xlsxcolor_p.h @@ -0,0 +1,59 @@ +// xlsxcolor_p.h + +#ifndef QXLSX_XLSXCOLOR_P_H +#define QXLSX_XLSXCOLOR_P_H + +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Styles; + +class XlsxColor +{ +public: + explicit XlsxColor(const QColor &color = QColor()); + explicit XlsxColor(const QString &theme, const QString &tint=QString()); + explicit XlsxColor (int index); + + bool isThemeColor() const; + bool isIndexedColor() const; + bool isRgbColor() const; + bool isInvalid() const; + + QColor rgbColor() const; + int indexedColor() const; + QStringList themeColor() const; + + operator QVariant() const; + + static QColor fromARGBString(const QString &c); + static QString toARGBString(const QColor &c); + + bool saveToXml(QXmlStreamWriter &writer, const QString &node=QString()) const; + bool loadFromXml(QXmlStreamReader &reader); + +private: + QVariant val; +}; + +#if !defined(QT_NO_DATASTREAM) + QDataStream &operator<<(QDataStream &, const XlsxColor &); + QDataStream &operator>>(QDataStream &, XlsxColor &); +#endif + +#ifndef QT_NO_DEBUG_STREAM + QDebug operator<<(QDebug dbg, const XlsxColor &c); +#endif + +QT_END_NAMESPACE_XLSX + +Q_DECLARE_METATYPE(QXlsx::XlsxColor) + +#endif // QXLSX_XLSXCOLOR_P_H diff --git a/include/QXlsx/xlsxconditionalformatting.h b/include/QXlsx/xlsxconditionalformatting.h new file mode 100644 index 0000000..117f74a --- /dev/null +++ b/include/QXlsx/xlsxconditionalformatting.h @@ -0,0 +1,120 @@ +// xlsxconditionalformatting.h + +#ifndef QXLSX_XLSXCONDITIONALFORMATTING_H +#define QXLSX_XLSXCONDITIONALFORMATTING_H + +#include +#include +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" +#include "xlsxcellrange.h" +#include "xlsxcellreference.h" + +class ConditionalFormattingTest; + +QT_BEGIN_NAMESPACE_XLSX + +class Format; +class Worksheet; +class Styles; +class ConditionalFormattingPrivate; + +class QXLSX_EXPORT ConditionalFormatting +{ +public: + enum HighlightRuleType { + Highlight_LessThan, + Highlight_LessThanOrEqual, + Highlight_Equal, + Highlight_NotEqual, + Highlight_GreaterThanOrEqual, + Highlight_GreaterThan, + Highlight_Between, + Highlight_NotBetween, + + Highlight_ContainsText, + Highlight_NotContainsText, + Highlight_BeginsWith, + Highlight_EndsWith, + + Highlight_TimePeriod, + + Highlight_Duplicate, + Highlight_Unique, + Highlight_Blanks, + Highlight_NoBlanks, + Highlight_Errors, + Highlight_NoErrors, + + Highlight_Top, + Highlight_TopPercent, + Highlight_Bottom, + Highlight_BottomPercent, + + Highlight_AboveAverage, + Highlight_AboveOrEqualAverage, + Highlight_AboveStdDev1, + Highlight_AboveStdDev2, + Highlight_AboveStdDev3, + Highlight_BelowAverage, + Highlight_BelowOrEqualAverage, + Highlight_BelowStdDev1, + Highlight_BelowStdDev2, + Highlight_BelowStdDev3, + + Highlight_Expression + }; + + enum ValueObjectType + { + VOT_Formula, + VOT_Max, + VOT_Min, + VOT_Num, + VOT_Percent, + VOT_Percentile + }; + +public: + ConditionalFormatting(); + ConditionalFormatting(const ConditionalFormatting &other); + ~ConditionalFormatting(); + +public: + bool addHighlightCellsRule(HighlightRuleType type, const Format &format, bool stopIfTrue=false); + bool addHighlightCellsRule(HighlightRuleType type, const QString &formula1, const Format &format, bool stopIfTrue=false); + bool addHighlightCellsRule(HighlightRuleType type, const QString &formula1, const QString &formula2, const Format &format, bool stopIfTrue=false); + bool addDataBarRule(const QColor &color, bool showData=true, bool stopIfTrue=false); + bool addDataBarRule(const QColor &color, ValueObjectType type1, const QString &val1, ValueObjectType type2, const QString &val2, bool showData=true, bool stopIfTrue=false); + bool add2ColorScaleRule(const QColor &minColor, const QColor &maxColor, bool stopIfTrue=false); + bool add3ColorScaleRule(const QColor &minColor, const QColor &midColor, const QColor &maxColor, bool stopIfTrue=false); + + QList ranges() const; + + void addCell(const CellReference &cell); + void addCell(int row, int col); + void addRange(int firstRow, int firstCol, int lastRow, int lastCol); + void addRange(const CellRange &range); + + //needed by QSharedDataPointer!! + ConditionalFormatting &operator=(const ConditionalFormatting &other); + +private: + friend class Worksheet; + friend class ::ConditionalFormattingTest; + +private: + bool saveToXml(QXmlStreamWriter &writer) const; + bool loadFromXml(QXmlStreamReader &reader, Styles* styles = NULL); + + QSharedDataPointer d; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXCONDITIONALFORMATTING_H diff --git a/include/QXlsx/xlsxconditionalformatting_p.h b/include/QXlsx/xlsxconditionalformatting_p.h new file mode 100644 index 0000000..455af59 --- /dev/null +++ b/include/QXlsx/xlsxconditionalformatting_p.h @@ -0,0 +1,99 @@ +// xlsxconditionalformatting_p.h + +#ifndef XLSXCONDITIONALFORMATTING_P_H +#define XLSXCONDITIONALFORMATTING_P_H + +#include +#include + +#include + +#include "xlsxconditionalformatting.h" +#include "xlsxformat.h" +#include "xlsxcolor_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +class XlsxCfVoData +{ +public: + XlsxCfVoData() + :gte(true) + { + } + + XlsxCfVoData(ConditionalFormatting::ValueObjectType type, const QString &value, bool gte=true) + :type(type), value(value), gte(gte) + { + } + + ConditionalFormatting::ValueObjectType type; + QString value; + bool gte; +}; + +class XlsxCfRuleData +{ +public: + enum Attribute { + A_type, + A_dxfId, + //A_priority, + A_stopIfTrue, + A_aboveAverage, + A_percent, + A_bottom, + A_operator, + A_text, + A_timePeriod, + A_rank, + A_stdDev, + A_equalAverage, + + A_dxfFormat, + A_formula1, + A_formula2, + A_formula3, + A_formula1_temp, + + A_color1, + A_color2, + A_color3, + + A_cfvo1, + A_cfvo2, + A_cfvo3, + + A_hideData + }; + + XlsxCfRuleData() + :priority(1) + {} + + int priority; + Format dxfFormat; + QMap attrs; +}; + +class ConditionalFormattingPrivate : public QSharedData +{ +public: + ConditionalFormattingPrivate(); + ConditionalFormattingPrivate(const ConditionalFormattingPrivate &other); + ~ConditionalFormattingPrivate(); + + void writeCfVo(QXmlStreamWriter &writer, const XlsxCfVoData& cfvo) const; + bool readCfVo(QXmlStreamReader &reader, XlsxCfVoData& cfvo); + bool readCfRule(QXmlStreamReader &reader, XlsxCfRuleData *cfRule, Styles *styles); + bool readCfDataBar(QXmlStreamReader &reader, XlsxCfRuleData *cfRule); + bool readCfColorScale(QXmlStreamReader &reader, XlsxCfRuleData *cfRule); + + QList >cfRules; + QList ranges; +}; + +QT_END_NAMESPACE_XLSX + +Q_DECLARE_METATYPE(QXlsx::XlsxCfVoData) +#endif // XLSXCONDITIONALFORMATTING_P_H diff --git a/include/QXlsx/xlsxcontenttypes_p.h b/include/QXlsx/xlsxcontenttypes_p.h new file mode 100644 index 0000000..43c73f0 --- /dev/null +++ b/include/QXlsx/xlsxcontenttypes_p.h @@ -0,0 +1,55 @@ +// xlsxcontenttypes_p.h + +#ifndef XLSXCONTENTTYPES_H +#define XLSXCONTENTTYPES_H + +#include +#include +#include +#include +#include + +#include "xlsxabstractooxmlfile.h" + +QT_BEGIN_NAMESPACE_XLSX + +class ContentTypes : public AbstractOOXmlFile +{ +public: + ContentTypes(CreateFlag flag); + + void addDefault(const QString &key, const QString &value); + void addOverride(const QString &key, const QString &value); + + //Convenient funcation for addOverride() + void addDocPropCore(); + void addDocPropApp(); + void addStyles(); + void addTheme(); + void addWorkbook(); + void addWorksheetName(const QString &name); + void addChartsheetName(const QString &name); + void addChartName(const QString &name); + void addDrawingName(const QString &name); + void addCommentName(const QString &name); + void addTableName(const QString &name); + void addExternalLinkName(const QString &name); + void addSharedString(); + void addVmlName(); + void addCalcChain(); + void addVbaProject(); + + void clearOverrides(); + + void saveToXmlFile(QIODevice *device) const override; + bool loadFromXmlFile(QIODevice *device) override; +private: + QMap m_defaults; + QMap m_overrides; + + QString m_package_prefix; + QString m_document_prefix; +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXCONTENTTYPES_H diff --git a/include/QXlsx/xlsxdatavalidation.h b/include/QXlsx/xlsxdatavalidation.h new file mode 100644 index 0000000..ad8a1d6 --- /dev/null +++ b/include/QXlsx/xlsxdatavalidation.h @@ -0,0 +1,105 @@ +// xlsxvalidation.h + +#ifndef QXLSX_XLSXDATAVALIDATION_H +#define QXLSX_XLSXDATAVALIDATION_H + +#include +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" + +class QXmlStreamReader; +class QXmlStreamWriter; + +QT_BEGIN_NAMESPACE_XLSX + +class Worksheet; +class CellRange; +class CellReference; + +class DataValidationPrivate; +class QXLSX_EXPORT DataValidation +{ +public: + enum ValidationType + { + None, + Whole, + Decimal, + List, + Date, + Time, + TextLength, + Custom + }; + + enum ValidationOperator + { + Between, + NotBetween, + Equal, + NotEqual, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual + }; + + enum ErrorStyle + { + Stop, + Warning, + Information + }; + + DataValidation(); + DataValidation(ValidationType type, ValidationOperator op=Between, const QString &formula1=QString() + , const QString &formula2=QString(), bool allowBlank=false); + DataValidation(const DataValidation &other); + ~DataValidation(); + + ValidationType validationType() const; + ValidationOperator validationOperator() const; + ErrorStyle errorStyle() const; + QString formula1() const; + QString formula2() const; + bool allowBlank() const; + QString errorMessage() const; + QString errorMessageTitle() const; + QString promptMessage() const; + QString promptMessageTitle() const; + bool isPromptMessageVisible() const; + bool isErrorMessageVisible() const; + QList ranges() const; + + void setValidationType(ValidationType type); + void setValidationOperator(ValidationOperator op); + void setErrorStyle(ErrorStyle es); + void setFormula1(const QString &formula); + void setFormula2(const QString &formula); + void setErrorMessage(const QString &error, const QString &title=QString()); + void setPromptMessage(const QString &prompt, const QString &title=QString()); + void setAllowBlank(bool enable); + void setPromptMessageVisible(bool visible); + void setErrorMessageVisible(bool visible); + + void addCell(const CellReference &cell); + void addCell(int row, int col); + void addRange(int firstRow, int firstCol, int lastRow, int lastCol); + void addRange(const CellRange &range); + + DataValidation &operator=(const DataValidation &other); + + bool saveToXml(QXmlStreamWriter &writer) const; + static DataValidation loadFromXml(QXmlStreamReader &reader); +private: + QSharedDataPointer d; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXDATAVALIDATION_H diff --git a/include/QXlsx/xlsxdatavalidation_p.h b/include/QXlsx/xlsxdatavalidation_p.h new file mode 100644 index 0000000..727a182 --- /dev/null +++ b/include/QXlsx/xlsxdatavalidation_p.h @@ -0,0 +1,37 @@ +// xlsxdatavalidation_p.h + +#ifndef XLSXDATAVALIDATION_P_H +#define XLSXDATAVALIDATION_P_H + +#include +#include + +#include "xlsxdatavalidation.h" + +QT_BEGIN_NAMESPACE_XLSX + +class DataValidationPrivate : public QSharedData +{ +public: + DataValidationPrivate(); + DataValidationPrivate(DataValidation::ValidationType type, DataValidation::ValidationOperator op, const QString &formula1, const QString &formula2, bool allowBlank); + DataValidationPrivate(const DataValidationPrivate &other); + ~DataValidationPrivate(); + + DataValidation::ValidationType validationType; + DataValidation::ValidationOperator validationOperator; + DataValidation::ErrorStyle errorStyle; + bool allowBlank; + bool isPromptMessageVisible; + bool isErrorMessageVisible; + QString formula1; + QString formula2; + QString errorMessage; + QString errorMessageTitle; + QString promptMessage; + QString promptMessageTitle; + QList ranges; +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXDATAVALIDATION_P_H diff --git a/include/QXlsx/xlsxdatetype.h b/include/QXlsx/xlsxdatetype.h new file mode 100644 index 0000000..366d99a --- /dev/null +++ b/include/QXlsx/xlsxdatetype.h @@ -0,0 +1,48 @@ +// xlsxdatetype.h + +#ifndef QXLSX_XLSXDATETYPE_H +#define QXLSX_XLSXDATETYPE_H + +#include +#include +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class QXLSX_EXPORT DateType +{ +public: + DateType(); +/* + DateType(bool is1904 = false); + DateType(double d, bool is1904 = false); + DateType(QDateTime qdt, bool is1904 = false); + DateType(QDate qd, bool is1904 = false); + DateType(QTime qt, bool is1904 = false); +public: + enum currentDateType { DateAndTimeType, OnlyDateType, OnlyTimeType }; +public: + currentDateType getType(); + bool getValue(QDateTime* pQdt); + bool getValue(QDate* pQd); + bool getValue(QTime* pQt); + bool getValue(double* pD); + +protected: + +protected: + bool isSet; + double dValue; + bool is1904Type; + currentDateType dType; +*/ +}; + +QT_END_NAMESPACE_XLSX +#endif diff --git a/include/QXlsx/xlsxdocpropsapp_p.h b/include/QXlsx/xlsxdocpropsapp_p.h new file mode 100644 index 0000000..32b95fa --- /dev/null +++ b/include/QXlsx/xlsxdocpropsapp_p.h @@ -0,0 +1,40 @@ +// xlsxdocpropsapp_p.h + +#ifndef XLSXDOCPROPSAPP_H +#define XLSXDOCPROPSAPP_H + +#include +#include +#include + +#include "xlsxglobal.h" +#include "xlsxabstractooxmlfile.h" + +class QIODevice; + +QT_BEGIN_NAMESPACE_XLSX + +class DocPropsApp : public AbstractOOXmlFile +{ +public: + DocPropsApp(CreateFlag flag); + + void addPartTitle(const QString &title); + void addHeadingPair(const QString &name, int value); + + bool setProperty(const QString &name, const QString &value); + QString property(const QString &name) const; + QStringList propertyNames() const; + + void saveToXmlFile(QIODevice *device) const override; + bool loadFromXmlFile(QIODevice *device) override; + +private: + QStringList m_titlesOfPartsList; + QList > m_headingPairsList; + QMap m_properties; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXDOCPROPSAPP_H diff --git a/include/QXlsx/xlsxdocpropscore_p.h b/include/QXlsx/xlsxdocpropscore_p.h new file mode 100644 index 0000000..3aaace6 --- /dev/null +++ b/include/QXlsx/xlsxdocpropscore_p.h @@ -0,0 +1,34 @@ +// xlsxdocpropscore_p.h + +#ifndef XLSXDOCPROPSCORE_H +#define XLSXDOCPROPSCORE_H + +#include "xlsxglobal.h" +#include "xlsxabstractooxmlfile.h" + +#include +#include + +class QIODevice; + +QT_BEGIN_NAMESPACE_XLSX + +class DocPropsCore : public AbstractOOXmlFile +{ +public: + explicit DocPropsCore(CreateFlag flag); + + bool setProperty(const QString &name, const QString &value); + QString property(const QString &name) const; + QStringList propertyNames() const; + + void saveToXmlFile(QIODevice *device) const override; + bool loadFromXmlFile(QIODevice *device) override; + +private: + QMap m_properties; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXDOCPROPSCORE_H diff --git a/include/QXlsx/xlsxdocument.h b/include/QXlsx/xlsxdocument.h new file mode 100644 index 0000000..5d3455f --- /dev/null +++ b/include/QXlsx/xlsxdocument.h @@ -0,0 +1,143 @@ +// xlsxdocument.h + +#ifndef QXLSX_XLSXDOCUMENT_H +#define QXLSX_XLSXDOCUMENT_H + +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" +#include "xlsxformat.h" +#include "xlsxworksheet.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Workbook; +class Cell; +class CellRange; +class DataValidation; +class ConditionalFormatting; +class Chart; +class CellReference; +class DocumentPrivate; + +class QXLSX_EXPORT Document : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(Document) // D-Pointer. Qt classes have a Q_DECLARE_PRIVATE + // macro in the public class. The macro reads: qglobal.h +public: + explicit Document(QObject *parent = nullptr); + Document(const QString& xlsxName, QObject* parent = nullptr); + Document(QIODevice* device, QObject* parent = nullptr); + ~Document(); + + bool write(const CellReference &cell, const QVariant &value, const Format &format=Format()); + bool write(int row, int col, const QVariant &value, const Format &format=Format()); + + QVariant read(const CellReference &cell) const; + QVariant read(int row, int col) const; + + int insertImage(int row, int col, const QImage &image); + bool getImage(int imageIndex, QImage& img); + bool getImage(int row, int col, QImage& img); + uint getImageCount(); + + Chart *insertChart(int row, int col, const QSize &size); + + bool mergeCells(const CellRange &range, const Format &format=Format()); + bool unmergeCells(const CellRange &range); + + bool setColumnWidth(const CellRange &range, double width); + bool setColumnFormat(const CellRange &range, const Format &format); + bool setColumnHidden(const CellRange &range, bool hidden); + bool setColumnWidth(int column, double width); + bool setColumnFormat(int column, const Format &format); + bool setColumnHidden(int column, bool hidden); + bool setColumnWidth(int colFirst, int colLast, double width); + bool setColumnFormat(int colFirst, int colLast, const Format &format); + bool setColumnHidden(int colFirst, int colLast, bool hidden); + + double columnWidth(int column); + Format columnFormat(int column); + bool isColumnHidden(int column); + + bool setRowHeight(int row, double height); + bool setRowFormat(int row, const Format &format); + bool setRowHidden(int row, bool hidden); + bool setRowHeight(int rowFirst, int rowLast, double height); + bool setRowFormat(int rowFirst, int rowLast, const Format &format); + bool setRowHidden(int rowFirst, int rowLast, bool hidden); + + double rowHeight(int row); + Format rowFormat(int row); + bool isRowHidden(int row); + + bool groupRows(int rowFirst, int rowLast, bool collapsed = true); + bool groupColumns(int colFirst, int colLast, bool collapsed = true); + + bool addDataValidation(const DataValidation &validation); + bool addConditionalFormatting(const ConditionalFormatting &cf); + + Cell *cellAt(const CellReference &cell) const; + Cell *cellAt(int row, int col) const; + + bool defineName(const QString &name, const QString &formula, + const QString &comment=QString(), const QString &scope=QString()); + + CellRange dimension() const; + + QString documentProperty(const QString &name) const; + void setDocumentProperty(const QString &name, const QString &property); + QStringList documentPropertyNames() const; + + QStringList sheetNames() const; + bool addSheet(const QString &name = QString(), + AbstractSheet::SheetType type = AbstractSheet::ST_WorkSheet); + bool insertSheet(int index, const QString &name = QString(), + AbstractSheet::SheetType type = AbstractSheet::ST_WorkSheet); + bool selectSheet(const QString &name); + bool selectSheet(int index); + bool renameSheet(const QString &oldName, const QString &newName); + bool copySheet(const QString &srcName, const QString &distName = QString()); + bool moveSheet(const QString &srcName, int distIndex); + bool deleteSheet(const QString &name); + + Workbook *workbook() const; + AbstractSheet *sheet(const QString &sheetName) const; + AbstractSheet *currentSheet() const; + Worksheet *currentWorksheet() const; + + bool save() const; + bool saveAs(const QString &xlsXname) const; + bool saveAs(QIODevice *device) const; + + // copy style from one xlsx file to other + static bool copyStyle(const QString &from, const QString &to); + + bool isLoadPackage() const; + bool load() const; // equals to isLoadPackage() + + bool changeimage(int filenoinmidea,QString newfile); // add by liufeijin20181025 + + bool autosizeColumnWidth(const CellRange &range); + bool autosizeColumnWidth(int column); + bool autosizeColumnWidth(int colFirst, int colLast); + bool autosizeColumnWidth(void); + +private: + QMap getMaximalColumnWidth(int firstRow=1, int lastRow=INT_MAX); + + +private: + Q_DISABLE_COPY(Document) // Disables the use of copy constructors and + // assignment operators for the given Class. + DocumentPrivate* const d_ptr; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXDOCUMENT_H diff --git a/include/QXlsx/xlsxdocument_p.h b/include/QXlsx/xlsxdocument_p.h new file mode 100644 index 0000000..b15bdfa --- /dev/null +++ b/include/QXlsx/xlsxdocument_p.h @@ -0,0 +1,41 @@ +// xlsxdocument_p.h + +#ifndef XLSXDOCUMENT_P_H +#define XLSXDOCUMENT_P_H + +#include +#include + +#include "xlsxglobal.h" +#include "xlsxdocument.h" +#include "xlsxworkbook.h" +#include "xlsxcontenttypes_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +class DocumentPrivate +{ + Q_DECLARE_PUBLIC(Document) +public: + DocumentPrivate(Document *p); + void init(); + + bool loadPackage(QIODevice *device); + bool savePackage(QIODevice *device) const; + + // copy style from one xlsx file to other + static bool copyStyle(const QString &from, const QString &to); + + Document *q_ptr; + const QString defaultPackageName; //default name when package name not specified + QString packageName; //name of the .xlsx file + + QMap documentProperties; //core, app and custom properties + QSharedPointer workbook; + std::shared_ptr contentTypes; + bool isLoad; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXDOCUMENT_P_H diff --git a/include/QXlsx/xlsxdrawing_p.h b/include/QXlsx/xlsxdrawing_p.h new file mode 100644 index 0000000..80793cc --- /dev/null +++ b/include/QXlsx/xlsxdrawing_p.h @@ -0,0 +1,38 @@ +// xlsxdrwaing_p.h + +#ifndef QXLSX_DRAWING_H +#define QXLSX_DRAWING_H + +#include +#include +#include + +#include "xlsxrelationships_p.h" +#include "xlsxabstractooxmlfile.h" + +class QIODevice; +class QXmlStreamWriter; + +QT_BEGIN_NAMESPACE_XLSX + +class DrawingAnchor; +class Workbook; +class AbstractSheet; +class MediaFile; + +class Drawing : public AbstractOOXmlFile +{ +public: + Drawing(AbstractSheet *sheet, CreateFlag flag); + ~Drawing(); + void saveToXmlFile(QIODevice *device) const override; + bool loadFromXmlFile(QIODevice *device) override; + + AbstractSheet *sheet; + Workbook *workbook; + QList anchors; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_DRAWING_H diff --git a/include/QXlsx/xlsxdrawinganchor_p.h b/include/QXlsx/xlsxdrawinganchor_p.h new file mode 100644 index 0000000..0c8c641 --- /dev/null +++ b/include/QXlsx/xlsxdrawinganchor_p.h @@ -0,0 +1,169 @@ +// xlsxdrawinganchor_p.h + +#ifndef QXLSX_XLSXDRAWINGANCHOR_P_H +#define QXLSX_XLSXDRAWINGANCHOR_P_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Drawing; +class MediaFile; +class Chart; + +//Helper class +struct XlsxMarker +{ + XlsxMarker(){} + XlsxMarker(int row, int column, int rowOffset, int colOffset) + :cell(QPoint(row, column)), offset(rowOffset, colOffset) + { + + } + + int row() const {return cell.x();} + int col() const {return cell.y();} + int rowOff() const {return offset.width();} + int colOff() const {return offset.height();} + + QPoint cell; + QSize offset; +}; + +class DrawingAnchor +{ +public: + enum ObjectType { + GraphicFrame, + Shape, + GroupShape, + ConnectionShape, + Picture, + Unknown + }; + + DrawingAnchor(Drawing *drawing, ObjectType objectType); + virtual ~DrawingAnchor(); + + void setObjectPicture(const QImage &img); + bool getObjectPicture(QImage &img); + + void setObjectGraphicFrame(QSharedPointer chart); + + virtual bool loadFromXml(QXmlStreamReader &reader) = 0; + virtual void saveToXml(QXmlStreamWriter &writer) const = 0; + + virtual int row() const; + virtual int col() const; + +protected: + QPoint loadXmlPos(QXmlStreamReader &reader); + QSize loadXmlExt(QXmlStreamReader &reader); + XlsxMarker loadXmlMarker(QXmlStreamReader &reader, const QString &node); + void loadXmlObject(QXmlStreamReader &reader); + void loadXmlObjectShape(QXmlStreamReader &reader); + void loadXmlObjectGroupShape(QXmlStreamReader &reader); + void loadXmlObjectGraphicFrame(QXmlStreamReader &reader); + void loadXmlObjectConnectionShape(QXmlStreamReader &reader); + void loadXmlObjectPicture(QXmlStreamReader &reader); + + void saveXmlPos(QXmlStreamWriter &writer, const QPoint &pos) const; + void saveXmlExt(QXmlStreamWriter &writer, const QSize &ext) const; + void saveXmlMarker(QXmlStreamWriter &writer, const XlsxMarker &marker, const QString &node) const; + void saveXmlObject(QXmlStreamWriter &writer) const; + void saveXmlObjectShape(QXmlStreamWriter &writer) const; + void saveXmlObjectGroupShape(QXmlStreamWriter &writer) const; + void saveXmlObjectGraphicFrame(QXmlStreamWriter &writer) const; + void saveXmlObjectConnectionShape(QXmlStreamWriter &writer) const; + void saveXmlObjectPicture(QXmlStreamWriter &writer) const; + + Drawing *m_drawing; + ObjectType m_objectType; + std::shared_ptr m_pictureFile; + QSharedPointer m_chartFile; + + int m_id; +public: + int getm_id(); + +protected: + + // liufeij {{ + void setObjectShape(const QImage &img); // liufeij + + QString editASName; + // below only for twocellanchor shape + QPoint posTA; // for shape liufeij 20181024 + QSize extTA; // for shape liufeij 20181024 + int rotWithShapeTA; //// for shape liufeij 20181024 + int dpiTA; //// for shape liufeij 20181024 + QString sp_textlink,sp_macro,sp_blip_cstate,sp_blip_rembed; + + // BELOW only for cxnSp shape + QString cxnSp_filpV,cxnSp_macro; + // belwo for cxnsp and sp + QString xsp_cNvPR_name,xsp_cNvPR_id; //x measns shape and cxnSp together using + QString xbwMode; // same as above + QString xIn_algn,xIn_cmpd,xIn_cap,xIn_w; //cxnSp only need xIn_w + QString xprstGeom_prst; + QString x_headEnd_w,x_headEnd_len,x_headEnd_tyep; + QString x_tailEnd_w,x_tailEnd_len,x_tailEnd_tyep; + QString Style_inref_idx,style_fillref_idx,style_effectref_idx,style_forntref_idx; + QString Style_inref_val,style_fillref_val,style_effectref_val,style_forntref_val; + // liufeij }} +}; + +class DrawingAbsoluteAnchor : public DrawingAnchor +{ +public: + DrawingAbsoluteAnchor(Drawing *drawing, ObjectType objectType=Unknown); + + QPoint pos; + QSize ext; + + bool loadFromXml(QXmlStreamReader &reader) override; + void saveToXml(QXmlStreamWriter &writer) const override; +}; + +class DrawingOneCellAnchor : public DrawingAnchor +{ +public: + DrawingOneCellAnchor(Drawing *drawing, ObjectType objectType=Unknown); + + XlsxMarker from; + QSize ext; + + int row() const override; + int col() const override; + + bool loadFromXml(QXmlStreamReader &reader) override; + void saveToXml(QXmlStreamWriter &writer) const override; +}; + +class DrawingTwoCellAnchor : public DrawingAnchor +{ +public: + DrawingTwoCellAnchor(Drawing *drawing, ObjectType objectType=Unknown); + + XlsxMarker from; + XlsxMarker to; + + int row() const override; + int col() const override; + + bool loadFromXml(QXmlStreamReader &reader) override; + void saveToXml(QXmlStreamWriter &writer) const override; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXDRAWINGANCHOR_P_H diff --git a/include/QXlsx/xlsxformat.h b/include/QXlsx/xlsxformat.h new file mode 100644 index 0000000..8a69e15 --- /dev/null +++ b/include/QXlsx/xlsxformat.h @@ -0,0 +1,262 @@ +// xlsxformat.h + +#ifndef QXLSX_FORMAT_H +#define QXLSX_FORMAT_H + +#include +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" + +class FormatTest; + +QT_BEGIN_NAMESPACE_XLSX + +class Styles; +class Worksheet; +class WorksheetPrivate; +class RichStringPrivate; +class SharedStrings; + +class FormatPrivate; + +class QXLSX_EXPORT Format +{ +public: + enum FontScript + { + FontScriptNormal, + FontScriptSuper, + FontScriptSub + }; + + enum FontUnderline + { + FontUnderlineNone, + FontUnderlineSingle, + FontUnderlineDouble, + FontUnderlineSingleAccounting, + FontUnderlineDoubleAccounting + }; + + enum HorizontalAlignment + { + AlignHGeneral, + AlignLeft, + AlignHCenter, + AlignRight, + AlignHFill, + AlignHJustify, + AlignHMerge, + AlignHDistributed + }; + + enum VerticalAlignment + { + AlignTop, + AlignVCenter, + AlignBottom, + AlignVJustify, + AlignVDistributed + }; + + enum BorderStyle + { + BorderNone, + BorderThin, + BorderMedium, + BorderDashed, + BorderDotted, + BorderThick, + BorderDouble, + BorderHair, + BorderMediumDashed, + BorderDashDot, + BorderMediumDashDot, + BorderDashDotDot, + BorderMediumDashDotDot, + BorderSlantDashDot + }; + + enum DiagonalBorderType + { + DiagonalBorderNone, + DiagonalBorderDown, + DiagonalBorderUp, + DiagnoalBorderBoth + }; + + enum FillPattern + { + PatternNone, + PatternSolid, + PatternMediumGray, + PatternDarkGray, + PatternLightGray, + PatternDarkHorizontal, + PatternDarkVertical, + PatternDarkDown, + PatternDarkUp, + PatternDarkGrid, + PatternDarkTrellis, + PatternLightHorizontal, + PatternLightVertical, + PatternLightDown, + PatternLightUp, + PatternLightTrellis, + PatternGray125, + PatternGray0625, + PatternLightGrid + }; + + Format(); + Format(const Format &other); + Format &operator=(const Format &rhs); + ~Format(); + + int numberFormatIndex() const; + void setNumberFormatIndex(int format); + QString numberFormat() const; + void setNumberFormat(const QString &format); + void setNumberFormat(int id, const QString &format); + bool isDateTimeFormat() const; + + int fontSize() const; + void setFontSize(int size); + bool fontItalic() const; + void setFontItalic(bool italic); + bool fontStrikeOut() const; + void setFontStrikeOut(bool); + QColor fontColor() const; + void setFontColor(const QColor &); + bool fontBold() const; + void setFontBold(bool bold); + FontScript fontScript() const; + void setFontScript(FontScript); + FontUnderline fontUnderline() const; + void setFontUnderline(FontUnderline); + bool fontOutline() const; + void setFontOutline(bool outline); + QString fontName() const; + void setFontName(const QString &); + QFont font() const; + void setFont(const QFont &font); + + HorizontalAlignment horizontalAlignment() const; + void setHorizontalAlignment(HorizontalAlignment align); + VerticalAlignment verticalAlignment() const; + void setVerticalAlignment(VerticalAlignment align); + bool textWrap() const; + void setTextWrap(bool textWrap); + int rotation() const; + void setRotation(int rotation); + int indent() const; + void setIndent(int indent); + bool shrinkToFit() const; + void setShrinkToFit(bool shink); + + void setBorderStyle(BorderStyle style); + void setBorderColor(const QColor &color); + BorderStyle leftBorderStyle() const; + void setLeftBorderStyle(BorderStyle style); + QColor leftBorderColor() const; + void setLeftBorderColor(const QColor &color); + BorderStyle rightBorderStyle() const; + void setRightBorderStyle(BorderStyle style); + QColor rightBorderColor() const; + void setRightBorderColor(const QColor &color); + BorderStyle topBorderStyle() const; + void setTopBorderStyle(BorderStyle style); + QColor topBorderColor() const; + void setTopBorderColor(const QColor &color); + BorderStyle bottomBorderStyle() const; + void setBottomBorderStyle(BorderStyle style); + QColor bottomBorderColor() const; + void setBottomBorderColor(const QColor &color); + BorderStyle diagonalBorderStyle() const; + void setDiagonalBorderStyle(BorderStyle style); + DiagonalBorderType diagonalBorderType() const; + void setDiagonalBorderType(DiagonalBorderType style); + QColor diagonalBorderColor() const; + void setDiagonalBorderColor(const QColor &color); + + FillPattern fillPattern() const; + void setFillPattern(FillPattern pattern); + QColor patternForegroundColor() const; + void setPatternForegroundColor(const QColor &color); + QColor patternBackgroundColor() const; + void setPatternBackgroundColor(const QColor &color); + + bool locked() const; + void setLocked(bool locked); + bool hidden() const; + void setHidden(bool hidden); + + void mergeFormat(const Format &modifier); + bool isValid() const; + bool isEmpty() const; + + bool operator == (const Format &format) const; + bool operator != (const Format &format) const; + + QVariant property(int propertyId, const QVariant &defaultValue=QVariant()) const; + void setProperty(int propertyId, const QVariant &value, const QVariant &clearValue=QVariant(), bool detach=true); + void clearProperty(int propertyId); + bool hasProperty(int propertyId) const; + + bool boolProperty(int propertyId, bool defaultValue=false) const; + int intProperty(int propertyId, int defaultValue=0) const; + double doubleProperty(int propertyId, double defaultValue = 0.0) const; + QString stringProperty(int propertyId, const QString &defaultValue = QString()) const; + QColor colorProperty(int propertyId, const QColor &defaultValue = QColor()) const; + + bool hasNumFmtData() const; + bool hasFontData() const; + bool hasFillData() const; + bool hasBorderData() const; + bool hasAlignmentData() const; + bool hasProtectionData() const; + + bool fontIndexValid() const; + int fontIndex() const; + QByteArray fontKey() const; + bool borderIndexValid() const; + QByteArray borderKey() const; + int borderIndex() const; + bool fillIndexValid() const; + QByteArray fillKey() const; + int fillIndex() const; + + QByteArray formatKey() const; + bool xfIndexValid() const; + int xfIndex() const; + bool dxfIndexValid() const; + int dxfIndex() const; + + void fixNumberFormat(int id, const QString &format); + void setFontIndex(int index); + void setBorderIndex(int index); + void setFillIndex(int index); + void setXfIndex(int index); + void setDxfIndex(int index); +private: + friend class Styles; + friend class ::FormatTest; + friend QDebug operator<<(QDebug, const Format &f); + + int theme() const; + + QExplicitlySharedDataPointer d; +}; + +#ifndef QT_NO_DEBUG_STREAM + QDebug operator<<(QDebug dbg, const Format &f); +#endif + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_FORMAT_H diff --git a/include/QXlsx/xlsxformat_p.h b/include/QXlsx/xlsxformat_p.h new file mode 100644 index 0000000..0f3240d --- /dev/null +++ b/include/QXlsx/xlsxformat_p.h @@ -0,0 +1,130 @@ +// xlsxformat_p.h +#ifndef XLSXFORMAT_P_H +#define XLSXFORMAT_P_H + +#include +#include +#include +#include + +#include "xlsxformat.h" + +QT_BEGIN_NAMESPACE_XLSX + +class FormatPrivate : public QSharedData +{ +public: + enum FormatType + { + FT_Invalid = 0, + FT_NumFmt = 0x01, + FT_Font = 0x02, + FT_Alignment = 0x04, + FT_Border = 0x08, + FT_Fill = 0x10, + FT_Protection = 0x20 + }; + + enum Property { + P_STARTID, + + //numFmt + P_NumFmt_Id, + P_NumFmt_FormatCode, + + //font + P_Font_STARTID, + P_Font_Size = P_Font_STARTID, + P_Font_Italic, + P_Font_StrikeOut, + P_Font_Color, + P_Font_Bold, + P_Font_Script, + P_Font_Underline, + P_Font_Outline, + P_Font_Shadow, + P_Font_Name, + P_Font_Family, + P_Font_Charset, + P_Font_Scheme, + P_Font_Condense, + P_Font_Extend, + P_Font_ENDID, + + //border + P_Border_STARTID, + P_Border_LeftStyle = P_Border_STARTID, + P_Border_RightStyle, + P_Border_TopStyle, + P_Border_BottomStyle, + P_Border_DiagonalStyle, + P_Border_LeftColor, + P_Border_RightColor, + P_Border_TopColor, + P_Border_BottomColor, + P_Border_DiagonalColor, + P_Border_DiagonalType, + P_Border_ENDID, + + //fill + P_Fill_STARTID, + P_Fill_Pattern = P_Fill_STARTID, + P_Fill_BgColor, + P_Fill_FgColor, + P_Fill_ENDID, + + //alignment + P_Alignment_STARTID, + P_Alignment_AlignH = P_Alignment_STARTID, + P_Alignment_AlignV, + P_Alignment_Wrap, + P_Alignment_Rotation, + P_Alignment_Indent, + P_Alignment_ShinkToFit, + P_Alignment_ENDID, + + //protection + P_Protection_Locked, + P_Protection_Hidden, + + P_ENDID + }; + + FormatPrivate(); + FormatPrivate(const FormatPrivate &other); + ~FormatPrivate(); + + bool dirty; //The key re-generation is need. + QByteArray formatKey; + + bool font_dirty; + bool font_index_valid; + QByteArray font_key; + int font_index; + + bool fill_dirty; + bool fill_index_valid; + QByteArray fill_key; + int fill_index; + + bool border_dirty; + bool border_index_valid; + QByteArray border_key; + int border_index; + + int xf_index; + bool xf_indexValid; + + bool is_dxf_fomat; + int dxf_index; + bool dxf_indexValid; + + int theme; + + QMap properties; +}; + + +QT_END_NAMESPACE_XLSX + +#endif diff --git a/include/QXlsx/xlsxglobal.h b/include/QXlsx/xlsxglobal.h new file mode 100644 index 0000000..a176309 --- /dev/null +++ b/include/QXlsx/xlsxglobal.h @@ -0,0 +1,33 @@ +// xlsxglobal.h + +#ifndef XLSXGLOBAL_H +#define XLSXGLOBAL_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#if defined(QXlsx_SHAREDLIB) +#if defined(QXlsx_EXPORTS) +# define QXLSX_EXPORT Q_DECL_EXPORT +#else +# define QXLSX_EXPORT Q_DECL_IMPORT +#endif +#else +# define QXLSX_EXPORT +#endif + +#define QT_BEGIN_NAMESPACE_XLSX namespace QXlsx { +#define QT_END_NAMESPACE_XLSX } + +#define QXLSX_USE_NAMESPACE using namespace QXlsx; + +#endif // XLSXGLOBAL_H diff --git a/include/QXlsx/xlsxmediafile_p.h b/include/QXlsx/xlsxmediafile_p.h new file mode 100644 index 0000000..1c18578 --- /dev/null +++ b/include/QXlsx/xlsxmediafile_p.h @@ -0,0 +1,46 @@ +// xlsxmediafile_p.h + +#ifndef QXLSX_XLSXMEDIAFILE_H +#define QXLSX_XLSXMEDIAFILE_H + +#include "xlsxglobal.h" + +#include +#include + +QT_BEGIN_NAMESPACE_XLSX + +class MediaFile +{ +public: + MediaFile(const QString &fileName); + MediaFile(const QByteArray &bytes, const QString &suffix, const QString &mimeType=QString()); + +public: + void set(const QByteArray &bytes, const QString &suffix, const QString &mimeType=QString()); + QString suffix() const; + QString mimeType() const; + QByteArray contents() const; + + bool isIndexValid() const; + int index() const; + void setIndex(int idx); + QByteArray hashKey() const; + + void setFileName(const QString &name); + QString fileName() const; + +protected: + QString m_fileName; + QByteArray m_contents; + QString m_suffix; + QString m_mimeType; + + int m_index; + bool m_indexValid; + QByteArray m_hashKey; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXMEDIAFILE_H diff --git a/include/QXlsx/xlsxnumformatparser_p.h b/include/QXlsx/xlsxnumformatparser_p.h new file mode 100644 index 0000000..dfa81e9 --- /dev/null +++ b/include/QXlsx/xlsxnumformatparser_p.h @@ -0,0 +1,51 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang +** 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. +** +****************************************************************************/ +#ifndef QXLSX_NUMFORMATPARSER_H +#define QXLSX_NUMFORMATPARSER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Xlsx API. It exists for the convenience +// of the Qt Xlsx. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class NumFormatParser +{ +public: + static bool isDateTime(const QString &formatCode); +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_NUMFORMATPARSER_H diff --git a/include/QXlsx/xlsxrelationships_p.h b/include/QXlsx/xlsxrelationships_p.h new file mode 100644 index 0000000..f7259cf --- /dev/null +++ b/include/QXlsx/xlsxrelationships_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang +** 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. +** +****************************************************************************/ +#ifndef XLSXRELATIONSHIPS_H +#define XLSXRELATIONSHIPS_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Xlsx API. It exists for the convenience +// of the Qt Xlsx. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "xlsxglobal.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE_XLSX + +struct XlsxRelationship +{ + QString id; + QString type; + QString target; + QString targetMode; +}; + +class Relationships +{ +public: + Relationships(); + + QList documentRelationships(const QString &relativeType) const; + QList packageRelationships(const QString &relativeType) const; + QList msPackageRelationships(const QString &relativeType) const; + QList worksheetRelationships(const QString &relativeType) const; + + void addDocumentRelationship(const QString &relativeType, const QString &target); + void addPackageRelationship(const QString &relativeType, const QString &target); + void addMsPackageRelationship(const QString &relativeType, const QString &target); + void addWorksheetRelationship(const QString &relativeType, const QString &target, const QString &targetMode=QString()); + + void saveToXmlFile(QIODevice *device) const; + QByteArray saveToXmlData() const; + bool loadFromXmlFile(QIODevice *device); + bool loadFromXmlData(const QByteArray &data); + XlsxRelationship getRelationshipById(const QString &id) const; + + void clear(); + int count() const; + bool isEmpty() const; + +private: + QList relationships(const QString &type) const; + void addRelationship(const QString &type, const QString &target, const QString &targetMode=QString()); + + QList m_relationships; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXRELATIONSHIPS_H diff --git a/include/QXlsx/xlsxrichstring.h b/include/QXlsx/xlsxrichstring.h new file mode 100644 index 0000000..41a28db --- /dev/null +++ b/include/QXlsx/xlsxrichstring.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang +** 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. +** +****************************************************************************/ +#ifndef XLSXRICHSTRING_H +#define XLSXRICHSTRING_H + +#include "xlsxglobal.h" +#include "xlsxformat.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE_XLSX +class RichStringPrivate; +class RichString; +// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4) + uint qHash(const RichString &rs, uint seed = 0) Q_DECL_NOTHROW; + +class QXLSX_EXPORT RichString +{ +public: + RichString(); + explicit RichString(const QString& text); + RichString(const RichString &other); + ~RichString(); + + bool isRichString() const; + bool isNull() const; + bool isEmtpy() const; + QString toPlainString() const; + QString toHtml() const; + void setHtml(const QString &text); + + int fragmentCount() const; + void addFragment(const QString &text, const Format &format); + QString fragmentText(int index) const; + Format fragmentFormat(int index) const; + + operator QVariant() const; + + RichString &operator=(const RichString &other); +private: + friend uint qHash(const RichString &rs, uint seed) Q_DECL_NOTHROW; + friend bool operator==(const RichString &rs1, const RichString &rs2); + friend bool operator!=(const RichString &rs1, const RichString &rs2); + friend bool operator<(const RichString &rs1, const RichString &rs2); + friend QDebug operator<<(QDebug dbg, const RichString &rs); + + QSharedDataPointer d; +}; + + bool operator==(const RichString &rs1, const RichString &rs2); + bool operator!=(const RichString &rs1, const RichString &rs2); + bool operator<(const RichString &rs1, const RichString &rs2); + bool operator==(const RichString &rs1, const QString &rs2); + bool operator==(const QString &rs1, const RichString &rs2); + bool operator!=(const RichString &rs1, const QString &rs2); + bool operator!=(const QString &rs1, const RichString &rs2); + +#ifndef QT_NO_DEBUG_STREAM + QDebug operator<<(QDebug dbg, const RichString &rs); +#endif + +QT_END_NAMESPACE_XLSX + +Q_DECLARE_METATYPE(QXlsx::RichString) + +#endif // XLSXRICHSTRING_H diff --git a/include/QXlsx/xlsxrichstring_p.h b/include/QXlsx/xlsxrichstring_p.h new file mode 100644 index 0000000..8b00fe3 --- /dev/null +++ b/include/QXlsx/xlsxrichstring_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang +** 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. +** +****************************************************************************/ +#ifndef XLSXRICHSTRING_P_H +#define XLSXRICHSTRING_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Xlsx API. It exists for the convenience +// of the Qt Xlsx. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include "xlsxrichstring.h" + +QT_BEGIN_NAMESPACE_XLSX + +class RichStringPrivate : public QSharedData +{ +public: + RichStringPrivate(); + RichStringPrivate(const RichStringPrivate &other); + ~RichStringPrivate(); + + QByteArray idKey() const; + + QStringList fragmentTexts; + QList fragmentFormats; + QByteArray _idKey; + bool _dirty; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXRICHSTRING_P_H diff --git a/include/QXlsx/xlsxsharedstrings_p.h b/include/QXlsx/xlsxsharedstrings_p.h new file mode 100644 index 0000000..c0b3250 --- /dev/null +++ b/include/QXlsx/xlsxsharedstrings_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang +** 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. +** +****************************************************************************/ +#ifndef XLSXSHAREDSTRINGS_H +#define XLSXSHAREDSTRINGS_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Xlsx API. It exists for the convenience +// of the Qt Xlsx. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" +#include "xlsxrichstring.h" +#include "xlsxabstractooxmlfile.h" + +QT_BEGIN_NAMESPACE_XLSX + +class XlsxSharedStringInfo +{ +public: + XlsxSharedStringInfo(int index=0, int count = 1) : + index(index), count(count) + { + } + + int index; + int count; +}; + +class SharedStrings : public AbstractOOXmlFile +{ +public: + SharedStrings(CreateFlag flag); + int count() const; + bool isEmpty() const; + + int addSharedString(const QString &string); + int addSharedString(const RichString &string); + void removeSharedString(const QString &string); + void removeSharedString(const RichString &string); + void incRefByStringIndex(int idx); + + int getSharedStringIndex(const QString &string) const; + int getSharedStringIndex(const RichString &string) const; + RichString getSharedString(int index) const; + QList getSharedStrings() const; + + void saveToXmlFile(QIODevice *device) const override; + bool loadFromXmlFile(QIODevice *device) override; + +private: + void readString(QXmlStreamReader &reader); // + void readRichStringPart(QXmlStreamReader &reader, RichString &rich); // + void readPlainStringPart(QXmlStreamReader &reader, RichString &rich); // + Format readRichStringPart_rPr(QXmlStreamReader &reader); + void writeRichStringPart_rPr(QXmlStreamWriter &writer, const Format &format) const; + + QHash m_stringTable; //for fast lookup + QList m_stringList; + int m_stringCount; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXSHAREDSTRINGS_H diff --git a/include/QXlsx/xlsxsimpleooxmlfile_p.h b/include/QXlsx/xlsxsimpleooxmlfile_p.h new file mode 100644 index 0000000..ba026c9 --- /dev/null +++ b/include/QXlsx/xlsxsimpleooxmlfile_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang +** 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. +** +****************************************************************************/ +#ifndef XLSXSIMPLEOOXMLFILE_H +#define XLSXSIMPLEOOXMLFILE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Xlsx API. It exists for the convenience +// of the Qt Xlsx. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// +#include "xlsxabstractooxmlfile.h" + +#include +#include + +QT_BEGIN_NAMESPACE_XLSX + +class SimpleOOXmlFile : public AbstractOOXmlFile +{ +public: + SimpleOOXmlFile(CreateFlag flag); + + void saveToXmlFile(QIODevice *device) const override; + QByteArray saveToXmlData() const override; + bool loadFromXmlData(const QByteArray &data) override; + bool loadFromXmlFile(QIODevice *device) override; + + QByteArray xmlData; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXSIMPLEOOXMLFILE_H diff --git a/include/QXlsx/xlsxstyles_p.h b/include/QXlsx/xlsxstyles_p.h new file mode 100644 index 0000000..380afd2 --- /dev/null +++ b/include/QXlsx/xlsxstyles_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang +** 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. +** +****************************************************************************/ +#ifndef XLSXSTYLES_H +#define XLSXSTYLES_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt Xlsx API. It exists for the convenience +// of the Qt Xlsx. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// class StylesTest; + +#include "xlsxglobal.h" +#include "xlsxformat.h" +#include "xlsxabstractooxmlfile.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Format; +class XlsxColor; + +struct XlsxFormatNumberData +{ + XlsxFormatNumberData() : formatIndex(0) {} + + int formatIndex; + QString formatString; +}; + +class Styles : public AbstractOOXmlFile +{ +public: + Styles(CreateFlag flag); + ~Styles(); + void addXfFormat(const Format &format, bool force=false); + Format xfFormat(int idx) const; + void addDxfFormat(const Format &format, bool force=false); + Format dxfFormat(int idx) const; + + void saveToXmlFile(QIODevice *device) const override; + bool loadFromXmlFile(QIODevice *device) override; + + QColor getColorByIndex(int idx); + +private: + friend class Format; + // friend class ::StylesTest; + + void fixNumFmt(const Format &format); + + void writeNumFmts(QXmlStreamWriter &writer) const; + void writeFonts(QXmlStreamWriter &writer) const; + void writeFont(QXmlStreamWriter &writer, const Format &font, bool isDxf = false) const; + void writeFills(QXmlStreamWriter &writer) const; + void writeFill(QXmlStreamWriter &writer, const Format &fill, bool isDxf = false) const; + void writeBorders(QXmlStreamWriter &writer) const; + void writeBorder(QXmlStreamWriter &writer, const Format &border, bool isDxf = false) const; + void writeSubBorder(QXmlStreamWriter &writer, const QString &type, int style, const XlsxColor &color) const; + void writeCellXfs(QXmlStreamWriter &writer) const; + void writeDxfs(QXmlStreamWriter &writer) const; + void writeDxf(QXmlStreamWriter &writer, const Format &format) const; + void writeColors(QXmlStreamWriter &writer) const; + + bool readNumFmts(QXmlStreamReader &reader); + bool readFonts(QXmlStreamReader &reader); + bool readFont(QXmlStreamReader &reader, Format &format); + bool readFills(QXmlStreamReader &reader); + bool readFill(QXmlStreamReader &reader, Format &format); + bool readBorders(QXmlStreamReader &reader); + bool readBorder(QXmlStreamReader &reader, Format &format); + bool readSubBorder(QXmlStreamReader &reader, const QString &name, Format::BorderStyle &style, XlsxColor &color); + bool readCellXfs(QXmlStreamReader &reader); + bool readDxfs(QXmlStreamReader &reader); + bool readDxf(QXmlStreamReader &reader); + bool readColors(QXmlStreamReader &reader); + bool readIndexedColors(QXmlStreamReader &reader); + + bool readCellStyleXfs(QXmlStreamReader &reader); + + QHash m_builtinNumFmtsHash; + QMap > m_customNumFmtIdMap; + QHash > m_customNumFmtsHash; + int m_nextCustomNumFmtId; + QList m_fontsList; + QList m_fillsList; + QList m_bordersList; + QHash m_fontsHash; + QHash m_fillsHash; + QHash m_bordersHash; + + QVector m_indexedColors; + bool m_isIndexedColorsDefault; + + QList m_xf_formatsList; + QHash m_xf_formatsHash; + + QList m_dxf_formatsList; + QHash m_dxf_formatsHash; + + bool m_emptyFormatAdded; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXSTYLES_H diff --git a/include/QXlsx/xlsxtheme_p.h b/include/QXlsx/xlsxtheme_p.h new file mode 100644 index 0000000..a9a783b --- /dev/null +++ b/include/QXlsx/xlsxtheme_p.h @@ -0,0 +1,29 @@ +// xlsxtheme_p.h + +#ifndef XLSXTHEME_H +#define XLSXTHEME_H + +#include +#include +#include + +#include "xlsxabstractooxmlfile.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Theme : public AbstractOOXmlFile +{ +public: + Theme(CreateFlag flag); + + void saveToXmlFile(QIODevice *device) const override; + QByteArray saveToXmlData() const override; + bool loadFromXmlData(const QByteArray &data) override; + bool loadFromXmlFile(QIODevice *device) override; + + QByteArray xmlData; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXTHEME_H diff --git a/include/QXlsx/xlsxutility_p.h b/include/QXlsx/xlsxutility_p.h new file mode 100644 index 0000000..0c8e68f --- /dev/null +++ b/include/QXlsx/xlsxutility_p.h @@ -0,0 +1,42 @@ +// xlsxutility_p.h + +#ifndef XLSXUTILITY_H +#define XLSXUTILITY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class CellReference; + +bool parseXsdBoolean(const QString &value, bool defaultValue=false); + +QStringList splitPath(const QString &path); +QString getRelFilePath(const QString &filePath); + +double datetimeToNumber(const QDateTime &dt, bool is1904=false); +QVariant datetimeFromNumber(double num, bool is1904=false); +double timeToNumber(const QTime &t); + +QString createSafeSheetName(const QString &nameProposal); +QString escapeSheetName(const QString &sheetName); +QString unescapeSheetName(const QString &sheetName); + +bool isSpaceReserveNeeded(const QString &string); + +QString convertSharedFormula(const QString &rootFormula, const CellReference &rootCell, const CellReference &cell); + +QT_END_NAMESPACE_XLSX +#endif // XLSXUTILITY_H diff --git a/include/QXlsx/xlsxworkbook.h b/include/QXlsx/xlsxworkbook.h new file mode 100644 index 0000000..536ad5f --- /dev/null +++ b/include/QXlsx/xlsxworkbook.h @@ -0,0 +1,95 @@ +// xlsxworkbook.h + +#ifndef XLSXWORKBOOK_H +#define XLSXWORKBOOK_H + +#include +#include +#include +#include +#include + +#include + +#include "xlsxglobal.h" +#include "xlsxabstractooxmlfile.h" +#include "xlsxabstractsheet.h" + +QT_BEGIN_NAMESPACE_XLSX + +class SharedStrings; +class Styles; +class Drawing; +class Document; +class Theme; +class Relationships; +class DocumentPrivate; +class MediaFile; +class Chart; +class Chartsheet; +class Worksheet; +class WorkbookPrivate; + +class QXLSX_EXPORT Workbook : public AbstractOOXmlFile +{ + Q_DECLARE_PRIVATE(Workbook) +public: + ~Workbook(); + + int sheetCount() const; + AbstractSheet *sheet(int index) const; + + AbstractSheet *addSheet(const QString &name = QString(), AbstractSheet::SheetType type = AbstractSheet::ST_WorkSheet); + AbstractSheet *insertSheet(int index, const QString &name = QString(), AbstractSheet::SheetType type = AbstractSheet::ST_WorkSheet); + bool renameSheet(int index, const QString &name); + bool deleteSheet(int index); + bool copySheet(int index, const QString &newName=QString()); + bool moveSheet(int srcIndex, int distIndex); + + AbstractSheet *activeSheet() const; + bool setActiveSheet(int index); + +// void addChart(); + bool defineName(const QString &name, const QString &formula, const QString &comment=QString(), const QString &scope=QString()); + bool isDate1904() const; + void setDate1904(bool date1904); + bool isStringsToNumbersEnabled() const; + void setStringsToNumbersEnabled(bool enable=true); + bool isStringsToHyperlinksEnabled() const; + void setStringsToHyperlinksEnabled(bool enable=true); + bool isHtmlToRichStringEnabled() const; + void setHtmlToRichStringEnabled(bool enable=true); + QString defaultDateFormat() const; + void setDefaultDateFormat(const QString &format); + + //internal used member + void addMediaFile(std::shared_ptr media, bool force=false); + QList > mediaFiles() const; + void addChartFile(QSharedPointer chartFile); + QList > chartFiles() const; + +private: + friend class Worksheet; + friend class Chartsheet; + friend class WorksheetPrivate; + friend class Document; + friend class DocumentPrivate; + + Workbook(Workbook::CreateFlag flag); + + void saveToXmlFile(QIODevice *device) const override; + bool loadFromXmlFile(QIODevice *device) override; + + SharedStrings *sharedStrings() const; + Styles *styles(); + Theme *theme(); + QList images(); + QList drawings(); + QList > getSheetsByTypes(AbstractSheet::SheetType type) const; + QStringList worksheetNames() const; + AbstractSheet *addSheet(const QString &name, int sheetId, AbstractSheet::SheetType type = AbstractSheet::ST_WorkSheet); +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXWORKBOOK_H diff --git a/include/QXlsx/xlsxworkbook_p.h b/include/QXlsx/xlsxworkbook_p.h new file mode 100644 index 0000000..7124f30 --- /dev/null +++ b/include/QXlsx/xlsxworkbook_p.h @@ -0,0 +1,74 @@ +// xlsxworkbook_p.h + +#ifndef XLSXWORKBOOK_P_H +#define XLSXWORKBOOK_P_H + +#include +#include +#include + +#include "xlsxworkbook.h" +#include "xlsxabstractooxmlfile_p.h" +#include "xlsxtheme_p.h" +#include "xlsxsimpleooxmlfile_p.h" +#include "xlsxrelationships_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +struct XlsxDefineNameData +{ + XlsxDefineNameData() + :sheetId(-1) + {} + XlsxDefineNameData(const QString &name, const QString &formula, const QString &comment, int sheetId=-1) + :name(name), formula(formula), comment(comment), sheetId(sheetId) + { + + } + QString name; + QString formula; + QString comment; + //using internal sheetId, instead of the localSheetId(order in the workbook) + int sheetId; +}; + +class WorkbookPrivate : public AbstractOOXmlFilePrivate +{ + Q_DECLARE_PUBLIC(Workbook) +public: + WorkbookPrivate(Workbook *q, Workbook::CreateFlag flag); + + QSharedPointer sharedStrings; + QList > sheets; + QList > externalLinks; + QStringList sheetNames; + QSharedPointer styles; + QSharedPointer theme; + QList > mediaFiles; + QList > chartFiles; + QList definedNamesList; + + bool strings_to_numbers_enabled; + bool strings_to_hyperlinks_enabled; + bool html_to_richstring_enabled; + bool date1904; + QString defaultDateFormat; + + int x_window; + int y_window; + int window_width; + int window_height; + + int activesheetIndex; + int firstsheet; + int table_count; + + //Used to generate new sheet name and id + int last_worksheet_index; + int last_chartsheet_index; + int last_sheet_id; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXWORKBOOK_P_H diff --git a/include/QXlsx/xlsxworksheet.h b/include/QXlsx/xlsxworksheet.h new file mode 100644 index 0000000..75a08fc --- /dev/null +++ b/include/QXlsx/xlsxworksheet.h @@ -0,0 +1,164 @@ +// xlsxworksheet.h + +#ifndef XLSXWORKSHEET_H +#define XLSXWORKSHEET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xlsxabstractsheet.h" +#include "xlsxcell.h" +#include "xlsxcellrange.h" +#include "xlsxcellreference.h" +#include "xlsxcelllocation.h" + +class WorksheetTest; + +QT_BEGIN_NAMESPACE_XLSX + +class DocumentPrivate; +class Workbook; +class Format; +class Drawing; +class DataValidation; +class ConditionalFormatting; +class CellRange; +class RichString; +class Relationships; +class Chart; + +class WorksheetPrivate; +class QXLSX_EXPORT Worksheet : public AbstractSheet +{ + Q_DECLARE_PRIVATE(Worksheet) + +private: + friend class DocumentPrivate; + friend class Workbook; + friend class ::WorksheetTest; + Worksheet(const QString &sheetName, int sheetId, Workbook *book, CreateFlag flag); + Worksheet *copy(const QString &distName, int distId) const override; + +public: + ~Worksheet(); + +public: + bool write(const CellReference &row_column, const QVariant &value, const Format &format=Format()); + bool write(int row, int column, const QVariant &value, const Format &format=Format()); + + QVariant read(const CellReference &row_column) const; + QVariant read(int row, int column) const; + + bool writeString(const CellReference &row_column, const QString &value, const Format &format=Format()); + bool writeString(int row, int column, const QString &value, const Format &format=Format()); + bool writeString(const CellReference &row_column, const RichString &value, const Format &format=Format()); + bool writeString(int row, int column, const RichString &value, const Format &format=Format()); + + bool writeInlineString(const CellReference &row_column, const QString &value, const Format &format=Format()); + bool writeInlineString(int row, int column, const QString &value, const Format &format=Format()); + + bool writeNumeric(const CellReference &row_column, double value, const Format &format=Format()); + bool writeNumeric(int row, int column, double value, const Format &format=Format()); + + bool writeFormula(const CellReference &row_column, const CellFormula &formula, const Format &format=Format(), double result=0); + bool writeFormula(int row, int column, const CellFormula &formula, const Format &format=Format(), double result=0); + + bool writeBlank(const CellReference &row_column, const Format &format=Format()); + bool writeBlank(int row, int column, const Format &format=Format()); + + bool writeBool(const CellReference &row_column, bool value, const Format &format=Format()); + bool writeBool(int row, int column, bool value, const Format &format=Format()); + + bool writeDateTime(const CellReference &row_column, const QDateTime& dt, const Format &format=Format()); + bool writeDateTime(int row, int column, const QDateTime& dt, const Format &format=Format()); + + // dev67 + bool writeDate(const CellReference &row_column, const QDate& dt, const Format &format=Format()); + bool writeDate(int row, int column, const QDate& dt, const Format &format=Format()); + + bool writeTime(const CellReference &row_column, const QTime& t, const Format &format=Format()); + bool writeTime(int row, int column, const QTime& t, const Format &format=Format()); + + bool writeHyperlink(const CellReference &row_column, const QUrl &url, const Format &format=Format(), const QString &display=QString(), const QString &tip=QString()); + bool writeHyperlink(int row, int column, const QUrl &url, const Format &format=Format(), const QString &display=QString(), const QString &tip=QString()); + + bool addDataValidation(const DataValidation &validation); + bool addConditionalFormatting(const ConditionalFormatting &cf); + + Cell *cellAt(const CellReference &row_column) const; + Cell *cellAt(int row, int column) const; + + int insertImage(int row, int column, const QImage &image); + bool getImage(int imageIndex, QImage& img); + bool getImage(int row, int column, QImage& img); + uint getImageCount(); + + Chart *insertChart(int row, int column, const QSize &size); + + bool mergeCells(const CellRange &range, const Format &format=Format()); + bool unmergeCells(const CellRange &range); + QList mergedCells() const; + + bool setColumnWidth(const CellRange& range, double width); + bool setColumnFormat(const CellRange& range, const Format &format); + bool setColumnHidden(const CellRange& range, bool hidden); + bool setColumnWidth(int colFirst, int colLast, double width); + bool setColumnFormat(int colFirst, int colLast, const Format &format); + bool setColumnHidden(int colFirst, int colLast, bool hidden); + + double columnWidth(int column); + Format columnFormat(int column); + bool isColumnHidden(int column); + + bool setRowHeight(int rowFirst,int rowLast, double height); + bool setRowFormat(int rowFirst,int rowLast, const Format &format); + bool setRowHidden(int rowFirst,int rowLast, bool hidden); + + double rowHeight(int row); + Format rowFormat(int row); + bool isRowHidden(int row); + + bool groupRows(int rowFirst, int rowLast, bool collapsed = true); + bool groupColumns(int colFirst, int colLast, bool collapsed = true); + bool groupColumns(const CellRange &range, bool collapsed = true); + CellRange dimension() const; + + bool isWindowProtected() const; + void setWindowProtected(bool protect); + bool isFormulasVisible() const; + void setFormulasVisible(bool visible); + bool isGridLinesVisible() const; + void setGridLinesVisible(bool visible); + bool isRowColumnHeadersVisible() const; + void setRowColumnHeadersVisible(bool visible); + bool isZerosVisible() const; + void setZerosVisible(bool visible); + bool isRightToLeft() const; + void setRightToLeft(bool enable); + bool isSelected() const; + void setSelected(bool select); + bool isRulerVisible() const; + void setRulerVisible(bool visible); + bool isOutlineSymbolsVisible() const; + void setOutlineSymbolsVisible(bool visible); + bool isWhiteSpaceVisible() const; + void setWhiteSpaceVisible(bool visible); + bool setStartPage(int spagen); //add by liufeijin20181028 + + QVector getFullCells(int* maxRow, int* maxCol); + +private: + void saveToXmlFile(QIODevice *device) const override; + bool loadFromXmlFile(QIODevice *device) override; +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXWORKSHEET_H diff --git a/include/QXlsx/xlsxworksheet_p.h b/include/QXlsx/xlsxworksheet_p.h new file mode 100644 index 0000000..1fdd552 --- /dev/null +++ b/include/QXlsx/xlsxworksheet_p.h @@ -0,0 +1,252 @@ +// xlsxworksheet_p.h + +#ifndef XLSXWORKSHEET_P_H +#define XLSXWORKSHEET_P_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "xlsxworksheet.h" +#include "xlsxabstractsheet_p.h" +#include "xlsxcell.h" +#include "xlsxdatavalidation.h" +#include "xlsxconditionalformatting.h" +#include "xlsxcellformula.h" + +class QXmlStreamWriter; +class QXmlStreamReader; + +QT_BEGIN_NAMESPACE_XLSX + +const int XLSX_ROW_MAX = 1048576; +const int XLSX_COLUMN_MAX = 16384; +const int XLSX_STRING_MAX = 32767; + +class SharedStrings; + +struct XlsxHyperlinkData +{ + enum LinkType + { + External, + Internal + }; + + XlsxHyperlinkData(LinkType linkType=External, const QString &target=QString(), const QString &location=QString() + , const QString &display=QString(), const QString &tip=QString()) + :linkType(linkType), target(target), location(location), display(display), tooltip(tip) + { + + } + + LinkType linkType; + QString target; //For External link + QString location; + QString display; + QString tooltip; +}; + +// ECMA-376 Part1 18.3.1.81 +struct XlsxSheetFormatProps +{ + XlsxSheetFormatProps(int baseColWidth = 8, + bool customHeight = false, + double defaultColWidth = 8.430f, // https://learn.microsoft.com/en-us/office/troubleshoot/excel/determine-column-widths + double defaultRowHeight = 15, + quint8 outlineLevelCol = 0, + quint8 outlineLevelRow = 0, + bool thickBottom = false, + bool thickTop = false, + bool zeroHeight = false) : + baseColWidth(baseColWidth), + customHeight(customHeight), + defaultColWidth(defaultColWidth), + defaultRowHeight(defaultRowHeight), + outlineLevelCol(outlineLevelCol), + outlineLevelRow(outlineLevelRow), + thickBottom(thickBottom), + thickTop(thickTop), + zeroHeight(zeroHeight) { + } + + int baseColWidth; + bool customHeight; + double defaultColWidth; + double defaultRowHeight; + quint8 outlineLevelCol; + quint8 outlineLevelRow; + bool thickBottom; + bool thickTop; + bool zeroHeight; +}; + +struct XlsxRowInfo +{ + XlsxRowInfo(double height=0, const Format &format=Format(), bool hidden=false) : + customHeight(false), height(height), format(format), hidden(hidden), outlineLevel(0) + , collapsed(false) + { + + } + + bool customHeight; + double height; + Format format; + bool hidden; + int outlineLevel; + bool collapsed; +}; + +struct XlsxColumnInfo +{ + XlsxColumnInfo( int firstColumn, // = 0, + int lastColumn, // = 1, + bool isSetWidth, + double width = 0, + const Format &format = Format(), + bool hidden = false) + : width(width), + format(format), + firstColumn(firstColumn), + lastColumn(lastColumn), + outlineLevel(0), + isSetWidth(isSetWidth), + customWidth(false), + hidden(hidden), + collapsed(false) + { + + } + + double width; + Format format; + int firstColumn; + int lastColumn; + int outlineLevel; + bool isSetWidth; + bool customWidth; + bool hidden; + bool collapsed; +}; + +class WorksheetPrivate : public AbstractSheetPrivate +{ + Q_DECLARE_PUBLIC(Worksheet) + +public: + WorksheetPrivate(Worksheet *p, Worksheet::CreateFlag flag); + ~WorksheetPrivate(); + +public: + int checkDimensions(int row, int col, bool ignore_row=false, bool ignore_col=false); + Format cellFormat(int row, int col) const; + QString generateDimensionString() const; + void calculateSpans() const; + void splitColsInfo(int colFirst, int colLast); + void validateDimension(); + + void saveXmlSheetData(QXmlStreamWriter &writer) const; + void saveXmlCellData(QXmlStreamWriter &writer, int row, int col, std::shared_ptr cell) const; + void saveXmlMergeCells(QXmlStreamWriter &writer) const; + void saveXmlHyperlinks(QXmlStreamWriter &writer) const; + void saveXmlDrawings(QXmlStreamWriter &writer) const; + void saveXmlDataValidations(QXmlStreamWriter &writer) const; + + int rowPixelsSize(int row) const; + int colPixelsSize(int col) const; + + void loadXmlSheetData(QXmlStreamReader &reader); + void loadXmlColumnsInfo(QXmlStreamReader &reader); + void loadXmlMergeCells(QXmlStreamReader &reader); + void loadXmlDataValidations(QXmlStreamReader &reader); + void loadXmlSheetFormatProps(QXmlStreamReader &reader); + void loadXmlSheetViews(QXmlStreamReader &reader); + void loadXmlHyperlinks(QXmlStreamReader &reader); + + QList > getRowInfoList(int rowFirst, int rowLast); + QList > getColumnInfoList(int colFirst, int colLast); + QList getColumnIndexes(int colFirst, int colLast); + bool isColumnRangeValid(int colFirst, int colLast); + + SharedStrings *sharedStrings() const; + +public: + QMap > > cellTable; + + QMap > comments; + QMap > > urlTable; + QList merges; + QMap > rowsInfo; + QMap > colsInfo; + QMap > colsInfoHelper; + + QList dataValidationsList; + QList conditionalFormattingList; + + QMap sharedFormulaMap; // shared formula map + + CellRange dimension; + int previous_row; + + mutable QMap row_spans; + QMap row_sizes; + QMap col_sizes; + + int outline_row_level; + int outline_col_level; + + int default_row_height; + bool default_row_zeroed; + + // pagesetup and print settings add by liufeijin 20181028, liufeijin + QString PpaperSize; + QString Pscale; + QString PfirstPageNumber; + QString Porientation; + QString PuseFirstPageNumber; + QString PhorizontalDpi; + QString PverticalDpi; + QString Prid; + QString Pcopies; + + // pageMargins, liufeijin + QString PMheader; + QString PMfooter; + QString PMtop; + QString PMbotton; + QString PMleft; + QString PMright; + + // header footer, liufeijin + QString MoodFooter; + QString ModdHeader; + QString MoodalignWithMargins; // add align 20190619 + + XlsxSheetFormatProps sheetFormatProps; + + bool windowProtection; + bool showFormulas; + bool showGridLines; + bool showRowColHeaders; + bool showZeros; + bool rightToLeft; + bool tabSelected; + bool showRuler; + bool showOutlineSymbols; + bool showWhiteSpace; + + QRegularExpression urlPattern; + +private: + + static double calculateColWidth(int characters); +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXWORKSHEET_P_H diff --git a/include/QXlsx/xlsxzipreader_p.h b/include/QXlsx/xlsxzipreader_p.h new file mode 100644 index 0000000..0a18cbc --- /dev/null +++ b/include/QXlsx/xlsxzipreader_p.h @@ -0,0 +1,37 @@ +// xlsxzipreader_p.h + +#ifndef QXLSX_XLSXZIPREADER_P_H +#define QXLSX_XLSXZIPREADER_P_H + +#include +#include +#include + +#include "xlsxglobal.h" + +#include + +class QZipReader; + +QT_BEGIN_NAMESPACE_XLSX + +class ZipReader +{ +public: + explicit ZipReader(const QString &fileName); + explicit ZipReader(QIODevice *device); + ~ZipReader(); + bool exists() const; + QStringList filePaths() const; + QByteArray fileData(const QString &fileName) const; + +private: + Q_DISABLE_COPY(ZipReader) + void init(); + QScopedPointer m_reader; + QStringList m_filePaths; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXZIPREADER_P_H diff --git a/include/QXlsx/xlsxzipwriter_p.h b/include/QXlsx/xlsxzipwriter_p.h new file mode 100644 index 0000000..52ae63c --- /dev/null +++ b/include/QXlsx/xlsxzipwriter_p.h @@ -0,0 +1,34 @@ +// xlsxzipwriter_p.h + +#ifndef QXLSX_ZIPWRITER_H +#define QXLSX_ZIPWRITER_H + +#include +#include +#include + +#include "xlsxglobal.h" + +class QZipWriter; + +QT_BEGIN_NAMESPACE_XLSX + +class ZipWriter +{ +public: + explicit ZipWriter(const QString &filePath); + explicit ZipWriter(QIODevice *device); + ~ZipWriter(); + + void addFile(const QString &filePath, QIODevice *device); + void addFile(const QString &filePath, const QByteArray &data); + bool error() const; + void close(); + +private: + QZipWriter *m_writer; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_ZIPWRITER_H diff --git a/include/XlsxReader.h b/include/XlsxReader.h new file mode 100644 index 0000000..e5c37e2 --- /dev/null +++ b/include/XlsxReader.h @@ -0,0 +1,12 @@ +#ifndef XLSXREADER_H +#define XLSXREADER_H +#include + +class XlsxReader +{ +public: + XlsxReader(); + QList ReadeXlsx(QString pathOfXlsx); +}; + +#endif //XLSXREADER_H diff --git a/include/XlsxXmlConverter.h b/include/XlsxXmlConverter.h new file mode 100644 index 0000000..5fd355e --- /dev/null +++ b/include/XlsxXmlConverter.h @@ -0,0 +1,23 @@ +#ifndef XLSXXMLCONVERTER_H +#define XLSXXMLCONVERTER_H +#include +#include + +class XlsxXmlConverter +{ +private: + QString xlsxFilePath; + QString xmlPatternFilePath; + QString xmlOutPutFilePath; + QList xlsxData; + QList patternList; + +public: + XlsxXmlConverter(); + void initPath(QString xlsxPath, QString patternFilePath, QString outputFilePath); + void readXlsx(); + void readPattern(); + void wrtiteXMl(); +}; + +#endif //XLSXXMLCONVERTER_H diff --git a/include/XmlGenerator.h b/include/XmlGenerator.h new file mode 100644 index 0000000..75481da --- /dev/null +++ b/include/XmlGenerator.h @@ -0,0 +1,15 @@ +#ifndef XMLGENERATOR_H +#define XMLGENERATOR_H +#include + +class XmlGenerator +{ +public: + XmlGenerator(); + void generateXML(QList inputList, + qint16 sheetCounter, + QList patternList, + QString outputPath); +}; + +#endif //XMLGENERATOR_H diff --git a/include/XmlTemplateReader.h b/include/XmlTemplateReader.h new file mode 100644 index 0000000..8e33df3 --- /dev/null +++ b/include/XmlTemplateReader.h @@ -0,0 +1,12 @@ +#ifndef XMLTEMPLATEREADER_H +#define XMLTEMPLATEREADER_H +#include + +class XmlTemplateReader +{ +public: + XmlTemplateReader(); + QList readxmlPattern(QString pathOfPattern); +}; + +#endif //XMLTEMPLATEREADER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..bca8b13 --- /dev/null +++ b/main.cpp @@ -0,0 +1,15 @@ +#include +#include "include/XlsxXmlConverter.h" + +int main(int argc, char* argv[]) +{ + QCoreApplication a(argc, argv); + + XlsxXmlConverter convert; + convert.initPath("BPresetValue.xlsx", "Pattern.xml", ""); + convert.readXlsx(); + convert.readPattern(); + convert.wrtiteXMl(); + + return a.exec(); +} diff --git a/src/QXlsx/xlsxabstractooxmlfile.cpp b/src/QXlsx/xlsxabstractooxmlfile.cpp new file mode 100644 index 0000000..e7d0ce1 --- /dev/null +++ b/src/QXlsx/xlsxabstractooxmlfile.cpp @@ -0,0 +1,97 @@ +//xlsxabstractooxmlfile.cpp + +#include +#include +#include + +#include "include/QXlsx/xlsxabstractooxmlfile.h" +#include "xlsxabstractooxmlfile_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +AbstractOOXmlFilePrivate::AbstractOOXmlFilePrivate(AbstractOOXmlFile* q, + AbstractOOXmlFile::CreateFlag flag = AbstractOOXmlFile::F_NewFromScratch) + : relationships(new Relationships), flag(flag), q_ptr(q) +{ +} + +AbstractOOXmlFilePrivate::~AbstractOOXmlFilePrivate() +{ + if(relationships) + { + delete relationships; + } +} + +/*! + * \internal + * + * \class AbstractOOXmlFile + * + * Base class of all the ooxml part file. + */ + +AbstractOOXmlFile::AbstractOOXmlFile(CreateFlag flag) + : d_ptr(new AbstractOOXmlFilePrivate(this, flag)) +{ +} + +AbstractOOXmlFile::AbstractOOXmlFile(AbstractOOXmlFilePrivate* d) + : d_ptr(d) +{ +} + +AbstractOOXmlFile::~AbstractOOXmlFile() +{ + delete d_ptr; +} + +QByteArray AbstractOOXmlFile::saveToXmlData() const +{ + QByteArray data; + QBuffer buffer(&data); + buffer.open(QIODevice::WriteOnly); + saveToXmlFile(&buffer); + + return data; +} + +bool AbstractOOXmlFile::loadFromXmlData(const QByteArray& data) +{ + QBuffer buffer; + buffer.setData(data); + buffer.open(QIODevice::ReadOnly); + + return loadFromXmlFile(&buffer); +} + +/*! + * \internal + */ +void AbstractOOXmlFile::setFilePath(const QString path) +{ + Q_D(AbstractOOXmlFile); + d->filePathInPackage = path; +} + +/*! + * \internal + */ +QString AbstractOOXmlFile::filePath() const +{ + Q_D(const AbstractOOXmlFile); + + return d->filePathInPackage; +} + +/*! + * \internal + */ +Relationships* AbstractOOXmlFile::relationships() const +{ + Q_D(const AbstractOOXmlFile); + + return d->relationships; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxabstractsheet.cpp b/src/QXlsx/xlsxabstractsheet.cpp new file mode 100644 index 0000000..88f4db4 --- /dev/null +++ b/src/QXlsx/xlsxabstractsheet.cpp @@ -0,0 +1,186 @@ +// xlsxabstractsheet.cpp + +#include + +#include "xlsxabstractsheet.h" +#include "xlsxabstractsheet_p.h" +#include "xlsxworkbook.h" + +QT_BEGIN_NAMESPACE_XLSX + +AbstractSheetPrivate::AbstractSheetPrivate(AbstractSheet *p, AbstractSheet::CreateFlag flag) + : AbstractOOXmlFilePrivate(p, flag) +{ + type = AbstractSheet::ST_WorkSheet; + sheetState = AbstractSheet::SS_Visible; +} + +AbstractSheetPrivate::~AbstractSheetPrivate() +{ +} + +/*! + \class AbstractSheet + \inmodule QtXlsx + \brief Base class for worksheet, chartsheet, etc. +*/ + +/*! + \enum AbstractSheet::SheetType + + \value ST_WorkSheet + \value ST_ChartSheet + \omitvalue ST_DialogSheet + \omitvalue ST_MacroSheet +*/ + +/*! + \enum AbstractSheet::SheetState + + \value SS_Visible + \value SS_Hidden + \value SS_VeryHidden User cann't make a veryHidden sheet visible in normal way. +*/ + +/*! + \fn AbstractSheet::copy(const QString &distName, int distId) const + + Copies the current sheet to a sheet called \a distName with \a distId. + Returns the new sheet. + */ + +/*! + * \internal + */ +AbstractSheet::AbstractSheet(const QString &name, int id, Workbook *workbook, AbstractSheetPrivate *d) : + AbstractOOXmlFile(d) +{ + d_func()->name = name; + d_func()->id = id; + d_func()->workbook = workbook; +} + + +/*! + * Returns the name of the sheet. + */ +QString AbstractSheet::sheetName() const +{ + Q_D(const AbstractSheet); + return d->name; +} + +/*! + * \internal + */ +void AbstractSheet::setSheetName(const QString &sheetName) +{ + Q_D(AbstractSheet); + d->name = sheetName; +} + +/*! + * Returns the type of the sheet. + */ +AbstractSheet::SheetType AbstractSheet::sheetType() const +{ + Q_D(const AbstractSheet); + return d->type; +} + +/*! + * \internal + */ +void AbstractSheet::setSheetType(SheetType type) +{ + Q_D(AbstractSheet); + d->type = type; +} + +/*! + * Returns the state of the sheet. + * + * \sa isHidden(), isVisible(), setSheetState() + */ +AbstractSheet::SheetState AbstractSheet::sheetState() const +{ + Q_D(const AbstractSheet); + return d->sheetState; +} + +/*! + * Set the state of the sheet to \a state. + */ +void AbstractSheet::setSheetState(SheetState state) +{ + Q_D(AbstractSheet); + d->sheetState = state; +} + +/*! + * Returns true if the sheet is not visible, otherwise false will be returned. + * + * \sa sheetState(), setHidden() + */ +bool AbstractSheet::isHidden() const +{ + Q_D(const AbstractSheet); + return d->sheetState != SS_Visible; +} + +/*! + * Returns true if the sheet is visible. + */ +bool AbstractSheet::isVisible() const +{ + return !isHidden(); +} + +/*! + * Make the sheet hiden or visible based on \a hidden. + */ +void AbstractSheet::setHidden(bool hidden) +{ + Q_D(AbstractSheet); + if (hidden == isHidden()) + return; + + d->sheetState = hidden ? SS_Hidden : SS_Visible; +} + +/*! + * Convenience function, equivalent to setHidden(! \a visible). + */ +void AbstractSheet::setVisible(bool visible) +{ + setHidden(!visible); +} + +/*! + * \internal + */ +int AbstractSheet::sheetId() const +{ + Q_D(const AbstractSheet); + return d->id; +} + +/*! + * \internal + */ +Drawing *AbstractSheet::drawing() const +{ + Q_D(const AbstractSheet); + return d->drawing.get(); +} + +/*! + * Return the workbook + */ +Workbook *AbstractSheet::workbook() const +{ + Q_D(const AbstractSheet); + return d->workbook; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxcell.cpp b/src/QXlsx/xlsxcell.cpp new file mode 100644 index 0000000..f3fe4b6 --- /dev/null +++ b/src/QXlsx/xlsxcell.cpp @@ -0,0 +1,364 @@ +// xlsxcell.cpp + +#include + +#include +#include +#include +#include +#include + +#include "xlsxcell.h" +#include "xlsxcell_p.h" +#include "xlsxformat.h" +#include "xlsxformat_p.h" +#include "xlsxutility_p.h" +#include "xlsxworksheet.h" +#include "xlsxworkbook.h" + +QT_BEGIN_NAMESPACE_XLSX + +CellPrivate::CellPrivate(Cell *p) : + q_ptr(p) +{ + +} + +CellPrivate::CellPrivate(const CellPrivate * const cp) + : parent(cp->parent) + , cellType(cp->cellType) + , value(cp->value) + , formula(cp->formula) + , format(cp->format) + , richString(cp->richString) + , styleNumber(cp->styleNumber) +{ + +} + +/*! + \class Cell + \inmodule QtXlsx + \brief The Cell class provides a API that is used to handle the worksheet cell. + +*/ + +/*! + \enum Cell::CellType + \value BooleanType Boolean type + \value NumberType Number type, can be blank or used with forumula + \value ErrorType Error type + \value SharedStringType Shared string type + \value StringType String type, can be used with forumula + \value InlineStringType Inline string type + */ + +/*! + * \internal + * Created by Worksheet only. + */ +// qint32 styleIndex = (-1) +Cell::Cell(const QVariant &data, + CellType type, + const Format &format, + Worksheet *parent, + qint32 styleIndex ) : + d_ptr(new CellPrivate(this)) +{ + d_ptr->value = data; + d_ptr->cellType = type; + d_ptr->format = format; + d_ptr->parent = parent; + d_ptr->styleNumber = styleIndex; +} + +/*! + * \internal + */ +Cell::Cell(const Cell * const cell): + d_ptr(new CellPrivate(cell->d_ptr)) +{ + d_ptr->q_ptr = this; +} + +/*! + * Destroys the Cell and cleans up. + */ +Cell::~Cell() +{ + if ( NULL != d_ptr ) + delete d_ptr; +} + +/*! + * Return the dataType of this Cell + */ +Cell::CellType Cell::cellType() const +{ + Q_D(const Cell); + + return d->cellType; +} + +/*! + * Return the data content of this Cell + */ +QVariant Cell::value() const +{ + Q_D(const Cell); + + return d->value; +} + +/*! +* Return the data content of this Cell for reading +*/ +QVariant Cell::readValue() const +{ + Q_D(const Cell); + + QVariant ret; // return value + ret = d->value; + + Format fmt = this->format(); + + if (isDateTime()) + { + QVariant vDT = dateTime(); + if ( vDT.isNull() ) + { + return QVariant(); + } + + // https://github.com/QtExcel/QXlsx/issues/171 + // https://www.qt.io/blog/whats-new-in-qmetatype-qvariant + #if QT_VERSION >= 0x060000 // Qt 6.0 or over + if ( vDT.metaType().id() == QMetaType::QDateTime ) + { + ret = vDT; + } + else if ( vDT.metaType().id() == QMetaType::QDate ) + { + ret = vDT; + } + else if ( vDT.metaType().id() == QMetaType::QTime ) + { + ret = vDT; + } + else + { + return QVariant(); + } + #else + if ( vDT.type() == QVariant::DateTime ) + { + ret = vDT; + } + else if ( vDT.type() == QVariant::Date ) + { + ret = vDT; + } + else if ( vDT.type() == QVariant::Time ) + { + ret = vDT; + } + else + { + return QVariant(); + } + #endif + + // QDateTime dt = dateTime(); + // ret = dt; + + // QString strFormat = fmt.numberFormat(); + // if (!strFormat.isEmpty()) + // { + // // TODO: use number format + // } + + // qint32 styleNo = d->styleNumber; + + // if (styleNo == 10) + // { + // } + + // if (styleNo == 11) + // { + // QTime timeValue = dt.time(); // only time. (HH:mm:ss) + // ret = timeValue; + // return ret; + // } + + // if (styleNo == 12) + // { + // } + + // if (styleNo == 13) // (HH:mm:ss) + // { + // double dValue = d->value.toDouble(); + // int day = int(dValue); // unit is day. + // double deciamlPointValue1 = dValue - double(day); + + // double dHour = deciamlPointValue1 * (double(1.0) / double(24.0)); + // int hour = int(dHour); + + // double deciamlPointValue2 = deciamlPointValue1 - (double(hour) * (double(1.0) / double(24.0))); + // double dMin = deciamlPointValue2 * (double(1.0) / double(60.0)); + // int min = int(dMin); + + // double deciamlPointValue3 = deciamlPointValue2 - (double(min) * (double(1.0) / double(60.0))); + // double dSec = deciamlPointValue3 * (double(1.0) / double(60.0)); + // int sec = int(dSec); + + // int totalHour = hour + (day * 24); + + // QString strTime; + // strTime = QString("%1:%2:%3").arg(totalHour).arg(min).arg(sec); + // ret = strTime; + + // return ret; + // } + + // return ret; + // */ + } + + if (hasFormula()) + { + QString formulaString = this->formula().formulaText(); + ret = formulaString; + return ret; // return formula string + } + + return ret; +} + +/*! + * Return the style used by this Cell. If no style used, 0 will be returned. + */ +Format Cell::format() const +{ + Q_D(const Cell); + + return d->format; +} + +/*! + * Returns true if the cell has one formula. + */ +bool Cell::hasFormula() const +{ + Q_D(const Cell); + + return d->formula.isValid(); +} + +/*! + * Return the formula contents if the dataType is Formula + */ +CellFormula Cell::formula() const +{ + Q_D(const Cell); + + return d->formula; +} + +/*! + * Returns whether the value is probably a dateTime or not + */ +bool Cell::isDateTime() const +{ + Q_D(const Cell); + + Cell::CellType cellType = d->cellType; + double dValue = d->value.toDouble(); // number +// QString strValue = d->value.toString().toUtf8(); + bool isValidFormat = d->format.isValid(); + bool isDateTimeFormat = d->format.isDateTimeFormat(); // datetime format + + // dev67 + if ( cellType == NumberType || + cellType == DateType || + cellType == CustomType ) + { + if ( dValue >= 0 && + isValidFormat && + isDateTimeFormat ) + { + return true; + } + } + + return false; +} + +/*! + * Return the data time value. + */ +/* +QDateTime Cell::dateTime() const +{ + Q_D(const Cell); + + if (!isDateTime()) + return QDateTime(); + + return datetimeFromNumber(d->value.toDouble(), d->parent->workbook()->isDate1904()); +} +*/ +QVariant Cell::dateTime() const +{ + Q_D(const Cell); + + if (!isDateTime()) + { + return QVariant(); + } + + // dev57 + + QVariant ret; + double dValue = d->value.toDouble(); + bool isDate1904 = d->parent->workbook()->isDate1904(); + ret = datetimeFromNumber(dValue, isDate1904); + return ret; +} + +/*! + * Returns whether the cell is probably a rich string or not + */ +bool Cell::isRichString() const +{ + Q_D(const Cell); + + if ( d->cellType != SharedStringType && + d->cellType != InlineStringType && + d->cellType != StringType ) + { + return false; + } + + return d->richString.isRichString(); +} + +qint32 Cell::styleNumber() const +{ + Q_D(const Cell); + + qint32 ret = d->styleNumber; + return ret; +} + +bool Cell::isDateType(CellType cellType, const Format &format) +{ + if ( cellType == NumberType || + cellType == DateType || + cellType == CustomType ) + { + return format.isValid() && format.isDateTimeFormat(); + } + return false; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxcellformula.cpp b/src/QXlsx/xlsxcellformula.cpp new file mode 100644 index 0000000..4b1e2b5 --- /dev/null +++ b/src/QXlsx/xlsxcellformula.cpp @@ -0,0 +1,453 @@ +// xlsxcellformula.cpp + +#include +#include +#include +#include +#include +#include + +#include "xlsxcellformula.h" +#include "xlsxcellformula_p.h" +#include "xlsxutility_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +CellFormulaPrivate::CellFormulaPrivate(const QString &formula_, const CellRange &ref_, CellFormula::FormulaType type_) + :formula(formula_), type(type_), reference(ref_), ca(false), si(0) +{ + //Remove the formula '=' sign if exists + if (formula.startsWith(QLatin1String("="))) + formula.remove(0,1); + else if (formula.startsWith(QLatin1String("{=")) && formula.endsWith(QLatin1String("}"))) + formula = formula.mid(2, formula.length()-3); +} + +CellFormulaPrivate::CellFormulaPrivate(const CellFormulaPrivate &other) + : QSharedData(other) + , formula(other.formula), type(other.type), reference(other.reference) + , ca(other.ca), si(other.si) +{ + +} + +CellFormulaPrivate::~CellFormulaPrivate() +{ + +} + +/*! + \class CellFormula + \inmodule QtXlsx + \brief The CellFormula class provides a API that is used to handle the cell formula. + +*/ + +/*! + \enum CellFormula::FormulaType + \value NormalType + \value ArrayType + \value DataTableType + \value SharedType +*/ + +/*! + * Creates a new formula. + */ +CellFormula::CellFormula() +{ + //The d pointer is initialized with a null pointer +} + +/*! + * Creates a new formula with the given \a formula and \a type. + */ +CellFormula::CellFormula(const char *formula, FormulaType type) + :d(new CellFormulaPrivate(QString::fromLatin1(formula), CellRange(), type)) +{ + +} + +/*! + * Creates a new formula with the given \a formula and \a type. + */ +CellFormula::CellFormula(const QString &formula, FormulaType type) + :d(new CellFormulaPrivate(formula, CellRange(), type)) +{ + +} + +/*! + * Creates a new formula with the given \a formula, \a ref and \a type. + */ +CellFormula::CellFormula(const QString &formula, const CellRange &ref, FormulaType type) + :d(new CellFormulaPrivate(formula, ref, type)) +{ + +} + +/*! + Creates a new formula with the same attributes as the \a other formula. + */ +CellFormula::CellFormula(const CellFormula &other) + :d(other.d) +{ +} + +/*! + Assigns the \a other formula to this formula, and returns a + reference to this formula. + */ +CellFormula &CellFormula::operator =(const CellFormula &other) +{ + d = other.d; + return *this; +} + +/*! + * Destroys this formula. + */ +CellFormula::~CellFormula() +{ + +} + +/*! + * Returns the type of the formula. + */ +CellFormula::FormulaType CellFormula::formulaType() const +{ + return d ? d->type : NormalType; +} + +/*! + * Returns the contents of the formula. + */ +QString CellFormula::formulaText() const +{ + return d ? d->formula : QString(); +} + +/*! + * Returns the reference cells of the formula. For normal formula, + * this will return an invalid CellRange object. + */ +CellRange CellFormula::reference() const +{ + return d ? d->reference : CellRange(); +} + +/*! + * Returns whether the formula is valid. + */ +bool CellFormula::isValid() const +{ + return d; +} + +/*! + * Returns the shared index for shared formula. + */ +int CellFormula::sharedIndex() const +{ + return d && d->type == SharedType ? d->si : (-1); +} + +/* aca (Always Calculate Array) // not-implmented attribute + * + * Only applies to array formulas. + * + * true indicates that the entire array shall be calculated in full. + * If false the individual cells of the array shall be calculated as needed. + * + * The aca value shall be ignored unless the value of the corresponding + * t attribute is array. + * + * [Note: The primary case where an array formula must be calculated in + * part instead of in full is when some cells in the array depend on other + * cells that are semi-calculated, e.g., contains the function =(). end note] + * + * The possible values for this attribute are defined by the W3C XML Schema + * boolean datatype. + */ + +/* bx (Assigns Value to Name) // not-implmented attribute + * + * Specifies that this formula assigns a value to a name. + * + * The possible values for this attribute are defined by the W3C XML + * Schema boolean datatype. + */ + +/* del1 (Input 1 Deleted) // not-implmented attribute + * + * Whether the first input cell for data table has been deleted. + * Applies to data table formula only. Written on master cell of data table + * formula only. + * + * The possible values for this attribute are defined by the W3C XML Schema + * boolean datatype. +*/ + +/* del2 (Input 2 Deleted) // not-impplmented attribute + * + * Whether the second input cell for data table has been deleted. + * Applies to data table formula only. Written on master cell of data + * table formula only. + * + * The possible values for this attribute are defined by the W3C XML Schema + * boolean datatype. + */ + +/* dt2D (Data Table 2-D) // not-implmented attribute + * + * Data table is two-dimentional. Only applies to the data tables function. + * Written on master cell of data table formula only. + * + * The possible values for this attribute are defined by the W3C XML Schema + * boolean datatype. + */ + +/* dtr (Data Table Row) // not-implmented attribute + * + * true if one-dimentional data table is a row, otherwise it's a column. + * Only applies to the data tables function. Written on master cell of data + * table formula only. + * + * The possible values for this attribute are defined by the W3C XML Schema + * boolean datatype. + */ + +/* r1 (Data Table Cell 1) // not-implmented attribute + * + * First input cell for data table. Only applies to the data tables array + * function "TABLE()". Written on master cell of data table formula only. + * + * The possible values for this attribute are defined by the ST_CellRef + * simple type (§18.18.7). + */ + +/* r2 (Input Cell 2) // not-implmented attribute + * + * Second input cell for data table when dt2D is '1'. Only applies to the + * data tables array function "TABLE()".Written on master cell of data table + * formula only. + * + * The possible values for this attribute are defined by the ST_CellRef + * simple type (§18.18.7). + */ + +/*! + * \internal + * \remark pair with loadFromXml() + */ +bool CellFormula::saveToXml(QXmlStreamWriter &writer) const +{ + + // t (Formula Type) + // + // Type of formula. + // The possible values for this attribute are defined by the + // ST_CellFormulaType simple type (§18.18.6). + // + // 18.18.6 ST_CellFormulaType (Formula Type) + // array (Array Formula) + // dataTable (Table Formula) + // normal (Normal) + // shared (Shared Formula) + + QString t; + switch (d->type) + { + case CellFormula::ArrayType: + t = QStringLiteral("array"); + break; + case CellFormula::SharedType: + t = QStringLiteral("shared"); + break; + case CellFormula::NormalType: + t = QStringLiteral("normal"); + break; + case CellFormula::DataTableType: + t = QStringLiteral("dataTable"); + break; + default: // undefined type + return false; + break; + } + + // f (Formula) + // + // Formula for the cell. The formula expression is contained in the + // character node of this element. + writer.writeStartElement(QStringLiteral("f")); + + if (!t.isEmpty()) + { + writer.writeAttribute(QStringLiteral("t"), t); // write type(t) + } + + // ref (Range of Cells) + // + // Range of cells which the formula applies to. + // Only required for shared formula, array formula or data table. + // Only written on the master formula, + // not subsequent formulas belonging to the same shared group, array, + // or data table. + // The possible values for this attribute are defined by the ST_Ref + // simple type (§18.18.62). + + if ( d->type == CellFormula::SharedType || + d->type == CellFormula::ArrayType || + d->type == CellFormula::DataTableType ) + { + if (d->reference.isValid()) + { + writer.writeAttribute(QStringLiteral("ref"), d->reference.toString()); + } + } + + // ca (Calculate Cell) + // + // Indicates that this formula needs to be recalculated the next time + // calculation is performed. [Example: This is always set on volatile + // functions, like =(), and circular references. end example] + // The possible values for this attribute are defined by the W3C XML + // Schema boolean datatype. + // + // 3.2.2 boolean + // 3.2.2.1 Lexical representation + // An instance of a datatype that is defined as ·boolean· can have the + // following legal literals {true, false, 1, 0}. + + if (d->ca) + { + writer.writeAttribute(QStringLiteral("ca"), QStringLiteral("1")); + } + + // si (Shared Group Index) + // Optional attribute to optimize load performance by sharing formulas. + // + // When a formula is a shared formula (t value is shared) then this value + // indicates the group to which this particular cell's formula belongs. The + // first formula in a group of shared formulas is saved in the f element. + // This is considered the 'master' formula cell. Subsequent cells sharing + // this formula need not have the formula written in their f element. + // Instead, the attribute si value for a particular cell is used to figure + // what the formula expression should be based on the cell's relative + // location to the master formula cell. + + if (d->type == CellFormula::SharedType) + { + int si = d->si; + writer.writeAttribute(QStringLiteral("si"), QString::number(si)); + } + + if (!d->formula.isEmpty()) + { + QString strFormula = d->formula; + writer.writeCharacters(strFormula); // write formula + } + + writer.writeEndElement(); // f + + return true; +} + +/*! + * \internal + * \remark pair with saveToXml() + */ +bool CellFormula::loadFromXml(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("f")); + if (!d) + d = new CellFormulaPrivate(QString(), CellRange(), NormalType); + + QXmlStreamAttributes attributes = reader.attributes(); + QString typeString = attributes.value(QLatin1String("t")).toString(); + + // branch: shared-formula + // + if (typeString == QLatin1String("array")) { + d->type = ArrayType; + } + else if (typeString == QLatin1String("shared")) { + d->type = SharedType; + } + else if (typeString == QLatin1String("normal")) { + d->type = NormalType; + } + else if (typeString == QLatin1String("dataTable")) { + d->type = DataTableType; + } + else { + /* + // undefined type + // qDebug() << "Undefined type" << typeString; + return false; + // */ + + // dev40 {{ + // https://github.com/QtExcel/QXlsx/issues/38 + d->type = NormalType; // Change: normal Type is not mentioned in the xml file!!!!! + // }} + } + + // branch: shared-formula + // + // ref (Range of Cells) + // Range of cells which the formula applies to. + // Only required for shared formula, array formula or data table. + if ( d->type == CellFormula::SharedType || + d->type == CellFormula::ArrayType || + d->type == CellFormula::DataTableType ) + { + if (attributes.hasAttribute(QLatin1String("ref"))) + { + QString refString = attributes.value(QLatin1String("ref")).toString(); + d->reference = CellRange(refString); + } + } + + // branch: shared-formula + // + // si (Shared Group Index) + // Optional attribute to optimize load performance by sharing formulas. + // When a formula is a shared formula (t value is shared) then this value + // indicates the group to which this particular cell's formula belongs. + if ( d->type == CellFormula::SharedType ) + { + QString ca = attributes.value(QLatin1String("si")).toString(); + d->ca = parseXsdBoolean(ca, false); + + if (attributes.hasAttribute(QLatin1String("si"))) + { + d->si = attributes.value(QLatin1String("si")).toInt(); + } + } + + d->formula = reader.readElementText(); // read formula + + return true; +} + +/*! + * \internal + */ +bool CellFormula::operator ==(const CellFormula &formula) const +{ + return d->formula == formula.d->formula && d->type == formula.d->type + && d->si ==formula.d->si; +} + +/*! + * \internal + */ +bool CellFormula::operator !=(const CellFormula &formula) const +{ + return d->formula != formula.d->formula || d->type != formula.d->type + || d->si !=formula.d->si; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxcelllocation.cpp b/src/QXlsx/xlsxcelllocation.cpp new file mode 100644 index 0000000..c6342fd --- /dev/null +++ b/src/QXlsx/xlsxcelllocation.cpp @@ -0,0 +1,23 @@ +// xlsxcelllocation.cpp + +#include +#include +#include +#include +#include + +#include "xlsxglobal.h" +#include "xlsxcell.h" +#include "xlsxcelllocation.h" + +QT_BEGIN_NAMESPACE_XLSX + +CellLocation::CellLocation() +{ + col = -1; + row = -1; + + cell.reset(); +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxcellrange.cpp b/src/QXlsx/xlsxcellrange.cpp new file mode 100644 index 0000000..5f54483 --- /dev/null +++ b/src/QXlsx/xlsxcellrange.cpp @@ -0,0 +1,127 @@ +// xlsxcellrange.cpp + +#include +#include +#include +#include + +#include "xlsxcellrange.h" +#include "xlsxcellreference.h" + +QT_BEGIN_NAMESPACE_XLSX + +/*! + \class CellRange + \brief For a range "A1:B2" or single cell "A1" + \inmodule QtXlsx + + The CellRange class stores the top left and bottom + right rows and columns of a range in a worksheet. +*/ + +/*! + Constructs an range, i.e. a range + whose rowCount() and columnCount() are 0. +*/ +CellRange::CellRange() + : top(-1), left(-1), bottom(-2), right(-2) +{ +} + +/*! + Constructs the range from the given \a top, \a + left, \a bottom and \a right rows and columns. + + \sa topRow(), leftColumn(), bottomRow(), rightColumn() +*/ +CellRange::CellRange(int top, int left, int bottom, int right) + : top(top), left(left), bottom(bottom), right(right) +{ +} + +CellRange::CellRange(const CellReference &topLeft, const CellReference &bottomRight) + : top(topLeft.row()), left(topLeft.column()) + , bottom(bottomRight.row()), right(bottomRight.column()) +{ +} + +/*! + \overload + Constructs the range form the given \a range string. +*/ +CellRange::CellRange(const QString &range) +{ + init(range); +} + +/*! + \overload + Constructs the range form the given \a range string. +*/ +CellRange::CellRange(const char *range) +{ + init(QString::fromLatin1(range)); +} + +void CellRange::init(const QString &range) +{ + QStringList rs = range.split(QLatin1Char(':')); + if (rs.size() == 2) { + CellReference start(rs[0]); + CellReference end(rs[1]); + top = start.row(); + left = start.column(); + bottom = end.row(); + right = end.column(); + } else { + CellReference p(rs[0]); + top = p.row(); + left = p.column(); + bottom = p.row(); + right = p.column(); + } +} + +/*! + Constructs a the range by copying the given \a + other range. +*/ +CellRange::CellRange(const CellRange &other) + : top(other.top), left(other.left), bottom(other.bottom), right(other.right) +{ +} + +/*! + Destroys the range. +*/ +CellRange::~CellRange() +{ +} + +/*! + Convert the range to string notation, such as "A1:B5". +*/ +QString CellRange::toString(bool row_abs, bool col_abs) const +{ + if (!isValid()) + return QString(); + + if (left == right && top == bottom) { + //Single cell + return CellReference(top, left).toString(row_abs, col_abs); + } + + QString cell_1 = CellReference(top, left).toString(row_abs, col_abs); + QString cell_2 = CellReference(bottom, right).toString(row_abs, col_abs); + return cell_1 + QLatin1String(":") + cell_2; +} + +/*! + * Returns true if the Range is valid. + */ +bool CellRange::isValid() const +{ + return left <= right && top <= bottom; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxcellreference.cpp b/src/QXlsx/xlsxcellreference.cpp new file mode 100644 index 0000000..dc28b72 --- /dev/null +++ b/src/QXlsx/xlsxcellreference.cpp @@ -0,0 +1,154 @@ +// xlsxcellreference.cpp + +#include "xlsxcellreference.h" +#include +#include + +#include + +QT_BEGIN_NAMESPACE_XLSX + +namespace { + +int intPow(int x, int p) +{ + if (p == 0) return 1; + if (p == 1) return x; + + int tmp = intPow(x, p/2); + if (p%2 == 0) return tmp * tmp; + else return x * tmp * tmp; +} + +QString col_to_name(int col_num) +{ + static thread_local QMap col_cache; + + auto it = col_cache.find(col_num); + if (it == col_cache.end()) { + QString col_str; + int remainder; + while (col_num) { + remainder = col_num % 26; + if (remainder == 0) + remainder = 26; + col_str.prepend(QChar('A'+remainder-1)); + col_num = (col_num - 1) / 26; + } + it = col_cache.insert(col_num, col_str); + } + + return it.value(); +} + +int col_from_name(const QString &col_str) +{ + int col = 0; + int expn = 0; + for (int i=col_str.size()-1; i>-1; --i) { + col += (col_str[i].unicode() - 'A' + 1) * intPow(26, expn); + expn++; + } + + return col; +} +} //namespace + +/*! + \class CellReference + \brief For one single cell such as "A1" + \inmodule QtXlsx + + The CellReference class stores the cell location in a worksheet. +*/ + +/*! + Constructs an invalid Cell Reference +*/ +CellReference::CellReference() + : _row(-1), _column(-1) +{ +} + +/*! + Constructs the Reference from the given \a row, and \a column. +*/ +CellReference::CellReference(int row, int column) + : _row(row), _column(column) +{ +} + +/*! + \overload + Constructs the Reference form the given \a cell string. +*/ +CellReference::CellReference(const QString &cell) +{ + init(cell); +} + +/*! + \overload + Constructs the Reference form the given \a cell string. +*/ +CellReference::CellReference(const char *cell) +{ + init(QString::fromLatin1(cell)); +} + +void CellReference::init(const QString &cell_str) +{ + static thread_local QRegularExpression re(QStringLiteral("^\\$?([A-Z]{1,3})\\$?(\\d+)$")); + QRegularExpressionMatch match = re.match(cell_str); + if (match.hasMatch()) { + const QString col_str = match.captured(1); + const QString row_str = match.captured(2); + _row = row_str.toInt(); + _column = col_from_name(col_str); + } +} + +/*! + Constructs a Reference by copying the given \a + other Reference. +*/ +CellReference::CellReference(const CellReference &other) + : _row(other._row), _column(other._column) +{ +} + +/*! + Destroys the Reference. +*/ +CellReference::~CellReference() +{ +} + +/*! + Convert the Reference to string notation, such as "A1" or "$A$1". + If current object is invalid, an empty string will be returned. +*/ +QString CellReference::toString(bool row_abs, bool col_abs) const +{ + if (!isValid()) + return QString(); + + QString cell_str; + if (col_abs) + cell_str.append(QLatin1Char('$')); + cell_str.append(col_to_name(_column)); + if (row_abs) + cell_str.append(QLatin1Char('$')); + cell_str.append(QString::number(_row)); + return cell_str; +} + +/*! + * Returns true if the Reference is valid. + */ +bool CellReference::isValid() const +{ + return _row > 0 && _column > 0; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxchart.cpp b/src/QXlsx/xlsxchart.cpp new file mode 100644 index 0000000..c0a0015 --- /dev/null +++ b/src/QXlsx/xlsxchart.cpp @@ -0,0 +1,2335 @@ +// xlsxchart.cpp + +#include +#include +#include +#include +#include +#include + +#include "xlsxchart_p.h" +#include "xlsxworksheet.h" +#include "xlsxcellrange.h" +#include "xlsxutility_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +ChartPrivate::ChartPrivate(Chart *q, Chart::CreateFlag flag) + : AbstractOOXmlFilePrivate(q, flag), chartType(static_cast(0)) +{ + +} + +ChartPrivate::~ChartPrivate() +{ +} + + + +/*! + * \internal + */ +Chart::Chart(AbstractSheet *parent, CreateFlag flag) + : AbstractOOXmlFile(new ChartPrivate(this, flag)) +{ + Q_D(Chart); + + d_func()->sheet = parent; + + // d->legendPos = Chart::ChartAxisPos::None; + d->legendPos = Chart::None; + d->legendOverlay = false; + d->majorGridlinesEnabled = false; + d->minorGridlinesEnabled = false; +} + +/*! + * 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, bool headerH, bool headerV, bool swapHeaders) +{ + 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) + { + auto series = std::make_shared(); + series->numberDataSource_numRef = sheetName + QLatin1String("!") + range.toString(true, true); + d->seriesList.append(series); + } + else if ((range.columnCount() < range.rowCount()) || swapHeaders ) + { + //Column based series + int firstDataRow = range.firstRow(); + int firstDataColumn = range.firstColumn(); + + QString axDataSouruce_numRef; + if (d->chartType == CT_ScatterChart || d->chartType == CT_BubbleChart) + { + firstDataColumn += 1; + CellRange subRange(range.firstRow(), range.firstColumn(), range.lastRow(), range.firstColumn()); + axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + } + + if( headerH ) + { + firstDataRow += 1; + } + if( headerV ) + { + firstDataColumn += 1; + } + + for (int col=firstDataColumn; col<=range.lastColumn(); ++col) + { + CellRange subRange(firstDataRow, col, range.lastRow(), col); + auto series = std::make_shared(); + series->axDataSource_numRef = axDataSouruce_numRef; + series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + + if( headerH ) + { + CellRange subRange(range.firstRow(), col, range.firstRow(), col); + series->headerH_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + } + else + { + series->headerH_numRef = QString(); + } + if( headerV ) + { + CellRange subRange(firstDataRow, range.firstColumn(), range.lastRow(), range.firstColumn()); + series->headerV_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + } + else + { + series->headerV_numRef = QString(); + } + series->swapHeader = swapHeaders; + + d->seriesList.append(series); + } + + } + else + { + //Row based series + int firstDataRow = range.firstRow(); + int firstDataColumn = range.firstColumn(); + + QString axDataSouruce_numRef; + if (d->chartType == CT_ScatterChart || d->chartType == CT_BubbleChart) + { + firstDataRow += 1; + CellRange subRange(range.firstRow(), range.firstColumn(), range.firstRow(), range.lastColumn()); + axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + } + + if( headerH ) + { + firstDataRow += 1; + } + if( headerV ) + { + firstDataColumn += 1; + } + + for (int row=firstDataRow; row<=range.lastRow(); ++row) + { + CellRange subRange(row, firstDataColumn, row, range.lastColumn()); + auto series = std::make_shared(); + series->axDataSource_numRef = axDataSouruce_numRef; + series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + + if( headerH ) + { + CellRange subRange(range.firstRow(), firstDataColumn, range.firstRow(), range.lastColumn()); + series->headerH_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + } + else + { + series->headerH_numRef = QString(); + } + + if( headerV ) + { + CellRange subRange(row, range.firstColumn(), row, range.firstColumn()); + series->headerV_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + } + else + { + series->headerV_numRef = QString(); + } + series->swapHeader = swapHeaders; + + 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 +} + +void Chart::setAxisTitle(Chart::ChartAxisPos pos, QString axisTitle) +{ + Q_D(Chart); + + if ( axisTitle.isEmpty() ) + return; + + // dev24 : fixed for old compiler + if ( pos == Chart::Left ) + { + d->axisNames[ XlsxAxis::Left ] = axisTitle; + } + else if ( pos == Chart::Top ) + { + d->axisNames[ XlsxAxis::Top ] = axisTitle; + } + else if ( pos == Chart::Right ) + { + d->axisNames[ XlsxAxis::Right ] = axisTitle; + } + else if ( pos == Chart::Bottom ) + { + d->axisNames[ XlsxAxis::Bottom ] = axisTitle; + } + +} + +// dev25 +void Chart::setChartTitle(QString strchartTitle) +{ + Q_D(Chart); + + d->chartTitle = strchartTitle; +} + + +void Chart::setChartLegend(Chart::ChartAxisPos legendPos, bool overlay) +{ + Q_D(Chart); + + d->legendPos = legendPos; + d->legendOverlay = overlay; +} + + +void Chart::setGridlinesEnable(bool majorGridlinesEnable, bool minorGridlinesEnable) +{ + Q_D(Chart); + + d->majorGridlinesEnabled = majorGridlinesEnable; + d->minorGridlinesEnabled = minorGridlinesEnable; +} + + +/*! + * \internal + */ +void Chart::saveToXmlFile(QIODevice *device) const +{ + Q_D(const Chart); + + /* + + + + + + + + + ... + + + + + + ... + + + + + + */ + + QXmlStreamWriter writer(device); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + + // L.4.13.2.2 Chart + // + // chartSpace is the root node, which contains an element defining the chart, + // and an element defining the print settings for the chart. + + 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")); + + /* + * chart is the root element for the chart. If the chart is a 3D chart, + * then a view3D element is contained, which specifies the 3D view. + * It then has a plot area, which defines a layout and contains an element + * that corresponds to, and defines, the type of chart. + */ + + 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")); + +// qDebug() << "-------------- loadXmlChart"; + + while (!reader.atEnd()) + { + reader.readNextStartElement(); + +// qDebug() << "-------------1- " << reader.name(); + + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + + if (reader.name() == QLatin1String("plotArea")) + { + if (!loadXmlPlotArea(reader)) + { + return false; + } + } + else if (reader.name() == QLatin1String("title")) + { + //!Todo + + if ( loadXmlChartTitle(reader) ) + { + } + + } +// else if (reader.name() == QLatin1String("legend")) +// { +// loadXmlChartLegend(reader); +// qDebug() << "-------------- loadXmlChartLegend"; +// } + } + else if (reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("chart") ) + { + break; + } + } + return true; +} + +// TO DEBUG: loop is not work, when i looping second element. +/* +dchrt_CT_PlotArea = + element layout { dchrt_CT_Layout }?, + (element areaChart { dchrt_CT_AreaChart } + | element area3DChart { dchrt_ CT_Area3DChart } + | element lineChart { dchrt_CT_LineChart } + | element line3DChart { dchrt_CT_Line3DChart } + | element stockChart { dchrt_CT_StockChart } + | element radarChart { dchrt_CT_RadarChart } + | element scatterChart { dchrt_CT_ScatterChart } + | element pieChart { dchrt_CT_PieChart } + | element pie3DChart { dchrt_CT_Pie3DChart } + | element doughnutChart { dchrt_CT_DoughnutChart } + | element barChart { dchrt_CT_BarChart } + | element bar3DChart { dchrt_CT_Bar3DChart } + | element ofPieChart { dchrt_CT_OfPieChart } + | element surfaceChart { dchrt_CT_SurfaceChart } + | element surface3DChart { dchrt_CT_Surface3DChart } + | element bubbleChart { dchrt_CT_BubbleChart })+, + (element valAx { dchrt_CT_ValAx } + | element catAx { dchrt_CT_CatAx } + | element dateAx { dchrt_CT_DateAx } + | element serAx { dchrt_CT_SerAx })*, + element dTable { dchrt_CT_DTable }?, + element spPr { a_CT_ShapeProperties }?, + element extLst { dchrt_CT_ExtensionList }? + */ +bool ChartPrivate::loadXmlPlotArea(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("plotArea")); + + // TO DEBUG: + + reader.readNext(); + + while (!reader.atEnd()) + { +// qDebug() << "-------------2- " << reader.name(); + + if (reader.isStartElement()) + { + if (!loadXmlPlotAreaElement(reader)) + { + qDebug() << "[debug] failed to load plotarea element."; + return false; + } + else if (reader.name() == QLatin1String("legend")) // Why here? + { + loadXmlChartLegend(reader); +// qDebug() << "-------------- loadXmlChartLegend"; + } + + reader.readNext(); + } + else + { + reader.readNext(); + } + } + + return true; +} + +bool ChartPrivate::loadXmlPlotAreaElement(QXmlStreamReader &reader) +{ + if (reader.name() == QLatin1String("layout")) + { + //!ToDo extract attributes + layout = readSubTree(reader); + } + else if (reader.name().endsWith(QLatin1String("Chart"))) + { + // for pieChart, barChart, ... (choose one) + if ( !loadXmlXxxChart(reader) ) + { + qDebug() << "[debug] failed to load chart"; + return false; + } + } + else if (reader.name() == QLatin1String("catAx")) // choose one : catAx, dateAx, serAx, valAx + { + // qDebug() << "loadXmlAxisCatAx()"; + loadXmlAxisCatAx(reader); + } + else if (reader.name() == QLatin1String("dateAx")) // choose one : catAx, dateAx, serAx, valAx + { + // qDebug() << "loadXmlAxisDateAx()"; + loadXmlAxisDateAx(reader); + } + else if (reader.name() == QLatin1String("serAx")) // choose one : catAx, dateAx, serAx, valAx + { + // qDebug() << "loadXmlAxisSerAx()"; + loadXmlAxisSerAx(reader); + } + else if (reader.name() == QLatin1String("valAx")) // choose one : catAx, dateAx, serAx, valAx + { + // qDebug() << "loadXmlAxisValAx()"; + loadXmlAxisValAx(reader); + } + else if (reader.name() == QLatin1String("dTable")) + { + //!ToDo + // dTable "CT_DTable" + // reader.skipCurrentElement(); + } + else if (reader.name() == QLatin1String("spPr")) + { + //!ToDo + // spPr "a:CT_ShapeProperties" + // reader.skipCurrentElement(); + } + else if (reader.name() == QLatin1String("extLst")) + { + //!ToDo + // extLst "CT_ExtensionList" + // reader.skipCurrentElement(); + } + + return true; +} + +bool ChartPrivate::loadXmlXxxChart(QXmlStreamReader &reader) +{ + const auto& name = reader.name(); + + if (name == QLatin1String("areaChart")) + { + chartType = Chart::CT_AreaChart; + } + else if (name == QLatin1String("area3DChart")) + { + chartType = Chart::CT_Area3DChart; + } + else if (name == QLatin1String("lineChart")) + { + chartType = Chart::CT_LineChart; + } + else if (name == QLatin1String("line3DChart")) + { + chartType = Chart::CT_Line3DChart; + } + else if (name == QLatin1String("stockChart")) + { + chartType = Chart::CT_StockChart; + } + else if (name == QLatin1String("radarChart")) + { + chartType = Chart::CT_RadarChart; + } + else if (name == QLatin1String("scatterChart")) + { + chartType = Chart::CT_ScatterChart; + } + else if (name == QLatin1String("pieChart")) + { + chartType = Chart::CT_PieChart; + } + else if (name == QLatin1String("pie3DChart")) + { + chartType = Chart::CT_Pie3DChart; + } + else if (name == QLatin1String("doughnutChart")) + { + chartType = Chart::CT_DoughnutChart; + } + else if (name == QLatin1String("barChart")) + { + chartType = Chart::CT_BarChart; + } + else if (name == QLatin1String("bar3DChart")) + { + chartType = Chart::CT_Bar3DChart; + } + else if (name == QLatin1String("ofPieChart")) + { + chartType = Chart::CT_OfPieChart; + } + else if (name == QLatin1String("surfaceChart")) + { + chartType = Chart::CT_SurfaceChart; + } + else if (name == QLatin1String("surface3DChart")) + { + chartType = Chart::CT_Surface3DChart; + } + else if (name == QLatin1String("bubbleChart")) + { + chartType = Chart::CT_BubbleChart; + } + else + { + qDebug() << "[undefined chart type] " << name; + chartType = Chart::CT_NoStatementChart; + return false; + } + + while( !reader.atEnd() ) + { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + // dev57 + + if ( reader.name() == QLatin1String("ser") ) + { + loadXmlSer(reader); + } + else if (reader.name() == QLatin1String("varyColors")) + { + } + else if (reader.name() == QLatin1String("barDir")) + { + } + else if (reader.name() == QLatin1String("axId")) + { + // + + } + else if (reader.name() == QLatin1String("scatterStyle")) + { + } + else if (reader.name() == QLatin1String("holeSize")) + { + } + else + { + } + + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == name ) + { + break; + } + } + + return true; +} + +bool ChartPrivate::loadXmlSer(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("ser")); + + auto series = std::make_shared(); + seriesList.append(series); + + while ( !reader.atEnd() && + !(reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("ser")) ) + { + if (reader.readNextStartElement()) + { + //TODO beide Header noch auswerten RTR 2019.11 + const auto& name = reader.name(); + if ( name == QLatin1String("tx") ) + { + while ( !reader.atEnd() && + !(reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == name)) + { + if (reader.readNextStartElement()) + { + if (reader.name() == QLatin1String("strRef")) + series->headerV_numRef = loadXmlStrRef(reader); + } + } + } + else 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 (reader.name() == QLatin1String("strRef")) + series->headerH_numRef = loadXmlStrRef(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(); +} + + +QString ChartPrivate::loadXmlStrRef(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("strRef")); + + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("strRef"))) + { + if (reader.readNextStartElement()) + { + if (reader.name() == QLatin1String("f")) + return reader.readElementText(); + } + } + + return QString(); +} + + +void ChartPrivate::saveXmlChart(QXmlStreamWriter &writer) const +{ + //---------------------------------------------------- + // c:chart + writer.writeStartElement(QStringLiteral("c:chart")); + + //---------------------------------------------------- + // c:title + + saveXmlChartTitle(writer); // write 'chart title' + + //---------------------------------------------------- + // c:plotArea + + writer.writeStartElement(QStringLiteral("c:plotArea")); + + // a little workaround for Start- and EndElement with starting ">" and ending without ">" + writer.device()->write(">"); //layout + writer.device()->write(layout.toUtf8()); + writer.device()->write("chartTitle = textValue; + return true; + } + } + } + + return false; +} + + +// write 'chart title' +void ChartPrivate::saveXmlChartTitle(QXmlStreamWriter &writer) const +{ + if ( chartTitle.isEmpty() ) + return; + + writer.writeStartElement(QStringLiteral("c:title")); + /* + + + + + + + + + + + */ + + writer.writeStartElement(QStringLiteral("c:tx")); + /* + + + + + + + + + */ + + writer.writeStartElement(QStringLiteral("c:rich")); + /* + + + + + + + + */ + + writer.writeEmptyElement(QStringLiteral("a:bodyPr")); // + /* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */ + + writer.writeEmptyElement(QStringLiteral("a:lstStyle")); // + + writer.writeStartElement(QStringLiteral("a:p")); + /* + + + + + + + + */ + + // + writer.writeStartElement(QStringLiteral("a:pPr")); + + writer.writeAttribute(QStringLiteral("lvl"), QStringLiteral("0")); + + // + writer.writeStartElement(QStringLiteral("a:defRPr")); + + writer.writeAttribute(QStringLiteral("b"), QStringLiteral("0")); + + writer.writeEndElement(); // a:defRPr + + writer.writeEndElement(); // a:pPr + + /* + + + + + + + + */ + + writer.writeStartElement(QStringLiteral("a:r")); + /* + + + + + + + */ + + // chart name + writer.writeTextElement(QStringLiteral("a:t"), chartTitle); + + writer.writeEndElement(); // a:r + + writer.writeEndElement(); // a:p + + writer.writeEndElement(); // c:rich + + writer.writeEndElement(); // c:tx + + // + writer.writeStartElement(QStringLiteral("c:overlay")); + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0")); + writer.writeEndElement(); // c:overlay + + writer.writeEndElement(); // c:title +} +// }} + + +// write 'chart legend' +void ChartPrivate::saveXmlChartLegend(QXmlStreamWriter &writer) const +{ + if ( legendPos == Chart::None ) + return; + +// +// +// +// + + writer.writeStartElement(QStringLiteral("c:legend")); + + writer.writeStartElement(QStringLiteral("c:legendPos")); + + QString pos; + switch( legendPos ) + { + //case Chart::ChartAxisPos::Right: + case Chart::Right : + pos = QStringLiteral("r"); + break; + + // case Chart::ChartAxisPos::Left: + case Chart::Left : + pos = QStringLiteral("l"); + break; + + // case Chart::ChartAxisPos::Top: + case Chart::Top : + pos = QStringLiteral("t"); + break; + + // case Chart::ChartAxisPos::Bottom: + case Chart::Bottom : + pos = QStringLiteral("b"); + break; + + default: + pos = QStringLiteral("r"); + break; + } + + writer.writeAttribute(QStringLiteral("val"), pos); + + writer.writeEndElement(); // c:legendPos + + writer.writeStartElement(QStringLiteral("c:overlay")); + + if( legendOverlay ) + { + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1")); + } + else + { + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0")); + } + + writer.writeEndElement(); // c:overlay + + writer.writeEndElement(); // c:legend +} + + +void ChartPrivate::saveXmlPieChart(QXmlStreamWriter &writer) const +{ + QString name = chartType == Chart::CT_PieChart ? 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(this)->axisList.append( + std::make_shared( XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1, axisNames[XlsxAxis::Bottom] )); + + const_cast(this)->axisList.append( + std::make_shared( XlsxAxis::T_Val, XlsxAxis::Left, 1, 0, axisNames[XlsxAxis::Left] )); + } + + + // Note: Bar3D have 2~3 axes + // int axisListSize = axisList.size(); + // [dev62] + // Q_ASSERT( axisListSize == 2 || + // ( axisListSize == 3 && chartType == Chart::CT_Bar3DChart ) ); + + 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_LineChart ? QStringLiteral("c:lineChart") : QStringLiteral("c:line3DChart"); + + writer.writeStartElement(name); + + // writer.writeEmptyElement(QStringLiteral("grouping")); // dev22 + + for (int i=0; i(this)->axisList.append(std::make_shared(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1, axisNames[XlsxAxis::Bottom] )); + const_cast(this)->axisList.append(std::make_shared(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0, axisNames[XlsxAxis::Left] )); + if (chartType==Chart::CT_Line3DChart) + const_cast(this)->axisList.append(std::make_shared(XlsxAxis::T_Ser, XlsxAxis::Bottom, 2, 0)); + } + + Q_ASSERT((axisList.size()==2||chartType==Chart::CT_LineChart)|| (axisList.size()==3 && chartType==Chart::CT_Line3DChart)); + + for (int i=0; iaxisId)); + } + + 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(this)->axisList.append( + std::make_shared(XlsxAxis::T_Val, XlsxAxis::Bottom, 0, 1, axisNames[XlsxAxis::Bottom] )); + const_cast(this)->axisList.append( + std::make_shared(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0, axisNames[XlsxAxis::Left] )); + } + + int axisListSize = axisList.size(); + Q_ASSERT(axisListSize == 2); + + for (int i=0; iaxisId)); + } + + writer.writeEndElement(); //c:scatterChart +} + +void ChartPrivate::saveXmlAreaChart(QXmlStreamWriter &writer) const +{ + QString name = chartType==Chart::CT_AreaChart ? QStringLiteral("c:areaChart") : QStringLiteral("c:area3DChart"); + + writer.writeStartElement(name); + + // writer.writeEmptyElement(QStringLiteral("grouping")); // dev22 + + for (int i=0; i(this)->axisList.append(std::make_shared(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)); + const_cast(this)->axisList.append(std::make_shared(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)); + } + + //Note: Area3D have 2~3 axes + Q_ASSERT(axisList.size()==2 || (axisList.size()==3 && chartType==Chart::CT_Area3DChart)); + + for (int i=0; iaxisId)); + } + + 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; iswapHeader ) + { + header1 = ser->headerH_numRef; + header2 = ser->headerV_numRef; + } + else + { + header1 = ser->headerV_numRef; + header2 = ser->headerH_numRef; + } + + if( !header1.isEmpty() ) + { + writer.writeStartElement(QStringLiteral("c:tx")); + writer.writeStartElement(QStringLiteral("c:strRef")); + writer.writeTextElement(QStringLiteral("c:f"), header1); + writer.writeEndElement(); + writer.writeEndElement(); + } + if( !header2.isEmpty() ) + { + writer.writeStartElement(QStringLiteral("c:cat")); + writer.writeStartElement(QStringLiteral("c:strRef")); + writer.writeTextElement(QStringLiteral("c:f"), header2); + writer.writeEndElement(); + writer.writeEndElement(); + } + +#if 0 + if (!ser->axDataSource_numRef.isEmpty()) + { + if (chartType == Chart::CT_ScatterChart || chartType == Chart::CT_BubbleChart) + { + 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 + } +#endif + + if (!ser->numberDataSource_numRef.isEmpty()) + { + if (chartType == Chart::CT_ScatterChart || chartType == Chart::CT_BubbleChart) + 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::loadXmlAxisCatAx(QXmlStreamReader &reader) +{ + + auto axis = std::make_shared(); + axis->type = XlsxAxis::T_Cat; + axisList.append(axis); + + // load EG_AxShared + if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) ) + { + qDebug() << "failed to load EG_AxShared"; + return false; + } + + //!TODO: load element + // auto + // lblAlgn + // lblOffset + // tickLblSkip + // tickMarkSkip + // noMultiLvlLbl + // extLst + + return true; +} + +bool ChartPrivate::loadXmlAxisDateAx(QXmlStreamReader &reader) +{ + + auto axis = std::make_shared(); + axis->type = XlsxAxis::T_Date; + axisList.append(axis); + + // load EG_AxShared + if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) ) + { + qDebug() << "failed to load EG_AxShared"; + return false; + } + + //!TODO: load element + // auto + // lblOffset + // baseTimeUnit + // majorUnit + // majorTimeUnit + // minorUnit + // minorTimeUnit + // extLst + + return true; +} + +bool ChartPrivate::loadXmlAxisSerAx(QXmlStreamReader &reader) +{ + + auto axis = std::make_shared(); + axis->type = XlsxAxis::T_Ser; + axisList.append(axis); + + // load EG_AxShared + if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) ) + { + qDebug() << "failed to load EG_AxShared"; + return false; + } + + //!TODO: load element + // tickLblSkip + // tickMarkSkip + // extLst + + return true; +} + +bool ChartPrivate::loadXmlAxisValAx(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("valAx")); + + auto axis = std::make_shared(); + axis->type = XlsxAxis::T_Val; + axisList.append(axis); + + if ( ! loadXmlAxisEG_AxShared( reader, axis.get() ) ) + { + qDebug() << "failed to load EG_AxShared"; + return false; + } + + //!TODO: load element + // crossBetween + // majorUnit + // minorUnit + // dispUnits + // extLst + + return true; +} + +/* + + + (*)(M) + (*)(M) + + (*)(M) + + + (*) + + + + + + + (*)(M) + + + + + + +*/ +bool ChartPrivate::loadXmlAxisEG_AxShared(QXmlStreamReader &reader, XlsxAxis* axis) +{ + Q_ASSERT( NULL != axis ); + Q_ASSERT( reader.name().endsWith(QLatin1String("Ax")) ); + QString name = reader.name().toString(); // + + while ( !reader.atEnd() ) + { + reader.readNextStartElement(); + if ( reader.tokenType() == QXmlStreamReader::StartElement ) + { + // qDebug() << "[debug]" << QTime::currentTime() << reader.name().toString(); + + if ( reader.name() == QLatin1String("axId") ) // mandatory element + { + // dev57 + uint axId = reader.attributes().value(QStringLiteral("val")).toUInt(); // for Qt5.1 + axis->axisId = axId; + } + else if ( reader.name() == QLatin1String("scaling") ) + { + // mandatory element + + loadXmlAxisEG_AxShared_Scaling(reader, axis); + } + else if ( reader.name() == QLatin1String("delete") ) + { + //!TODO + } + else if ( reader.name() == QLatin1String("axPos") ) + { + // mandatory element + + QString axPosVal = reader.attributes().value(QLatin1String("val")).toString(); + + if ( axPosVal == QLatin1String("l") ) { axis->axisPos = XlsxAxis::Left; } + else if ( axPosVal == QLatin1String("r") ) { axis->axisPos = XlsxAxis::Right; } + else if ( axPosVal == QLatin1String("t") ) { axis->axisPos = XlsxAxis::Top; } + else if ( axPosVal == QLatin1String("b") ) { axis->axisPos = XlsxAxis::Bottom; } + } + else if ( reader.name() == QLatin1String("majorGridlines") ) + { + //!TODO anything else? + majorGridlinesEnabled = true; + } + else if ( reader.name() == QLatin1String("minorGridlines") ) + { + //!TODO anything else? + minorGridlinesEnabled = true; + } + else if ( reader.name() == QLatin1String("title") ) + { + // title + if ( !loadXmlAxisEG_AxShared_Title(reader, axis) ) + { + qDebug() << "failed to load EG_AxShared title."; + Q_ASSERT(false); + return false; + } + } + else if ( reader.name() == QLatin1String("numFmt") ) + { + //!TODO + } + else if ( reader.name() == QLatin1String("majorTickMark") ) + { + //!TODO + } + else if ( reader.name() == QLatin1String("minorTickMark") ) + { + //!TODO + } + else if ( reader.name() == QLatin1String("tickLblPos") ) + { + //!TODO + } + else if ( reader.name() == QLatin1String("spPr") ) + { + //!TODO + } + else if ( reader.name() == QLatin1String("txPr") ) + { + //!TODO + } + else if ( reader.name() == QLatin1String("crossAx") ) // mandatory element + { + // dev57 + uint crossAx = reader.attributes().value(QLatin1String("val")).toUInt(); // for Qt5.1 + axis->crossAx = crossAx; + } + else if ( reader.name() == QLatin1String("crosses") ) + { + //!TODO + } + else if ( reader.name() == QLatin1String("crossesAt") ) + { + //!TODO + } + + // reader.readNext(); + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name().toString() == name ) + { + break; + } + } + + return true; +} + +bool ChartPrivate::loadXmlAxisEG_AxShared_Scaling(QXmlStreamReader &reader, XlsxAxis* axis) +{ + Q_UNUSED(axis); + Q_ASSERT(reader.name() == QLatin1String("scaling")); + + while ( !reader.atEnd() ) + { + reader.readNextStartElement(); + if ( reader.tokenType() == QXmlStreamReader::StartElement ) + { + if ( reader.name() == QLatin1String("orientation") ) + { + } + else + { + } + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("scaling") ) + { + break; + } + } + + return true; +} + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */ +bool ChartPrivate::loadXmlAxisEG_AxShared_Title(QXmlStreamReader &reader, XlsxAxis* axis) +{ + Q_ASSERT(reader.name() == QLatin1String("title")); + + while ( !reader.atEnd() ) + { + reader.readNextStartElement(); + if ( reader.tokenType() == QXmlStreamReader::StartElement ) + { + if ( reader.name() == QLatin1String("tx") ) + { + loadXmlAxisEG_AxShared_Title_Tx(reader, axis); + } + else if ( reader.name() == QLatin1String("overlay") ) + { + //!TODO: load overlay + loadXmlAxisEG_AxShared_Title_Overlay(reader, axis); + } + else + { + } + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("title") ) + { + break; + } + } + + return true; +} + +bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Overlay(QXmlStreamReader &reader, XlsxAxis* axis) +{ + Q_UNUSED(axis); + Q_ASSERT(reader.name() == QLatin1String("overlay")); + + while ( !reader.atEnd() ) + { + reader.readNextStartElement(); + if ( reader.tokenType() == QXmlStreamReader::StartElement ) + { + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("overlay") ) + { + break; + } + } + + return true; +} + +bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx(QXmlStreamReader &reader, XlsxAxis* axis) +{ + Q_ASSERT(reader.name() == QLatin1String("tx")); + + while ( !reader.atEnd() ) + { + reader.readNextStartElement(); + if ( reader.tokenType() == QXmlStreamReader::StartElement ) + { + if ( reader.name() == QLatin1String("rich") ) + { + loadXmlAxisEG_AxShared_Title_Tx_Rich(reader, axis); + } + else + { + } + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("tx") ) + { + break; + } + } + + return true; +} + +bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich(QXmlStreamReader &reader, XlsxAxis* axis) +{ + Q_ASSERT(reader.name() == QLatin1String("rich")); + + while ( !reader.atEnd() ) + { + reader.readNextStartElement(); + if ( reader.tokenType() == QXmlStreamReader::StartElement ) + { + if ( reader.name() == QLatin1String("p") ) + { + loadXmlAxisEG_AxShared_Title_Tx_Rich_P(reader, axis); + } + else + { + } + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("rich") ) + { + break; + } + } + + return true; +} + +bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich_P(QXmlStreamReader &reader, XlsxAxis* axis) +{ + Q_ASSERT(reader.name() == QLatin1String("p")); + + while ( !reader.atEnd() ) + { + reader.readNextStartElement(); + if ( reader.tokenType() == QXmlStreamReader::StartElement ) + { + if ( reader.name() == QLatin1String("r") ) + { + loadXmlAxisEG_AxShared_Title_Tx_Rich_P_R(reader, axis); + } + else if ( reader.name() == QLatin1String("pPr") ) + { + loadXmlAxisEG_AxShared_Title_Tx_Rich_P_pPr(reader, axis); + } + else + { + + } + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("p") ) + { + break; + } + } + + return true; +} + +bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich_P_pPr(QXmlStreamReader &reader, XlsxAxis* axis) +{ + Q_UNUSED(axis); + Q_ASSERT(reader.name() == QLatin1String("pPr")); + + while ( !reader.atEnd() ) + { + reader.readNextStartElement(); + if ( reader.tokenType() == QXmlStreamReader::StartElement ) + { + if ( reader.name() == QLatin1String("defRPr") ) + { + reader.readElementText(); + } + else + { + } + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("pPr") ) + { + break; + } + } + + return true; +} + +bool ChartPrivate::loadXmlAxisEG_AxShared_Title_Tx_Rich_P_R(QXmlStreamReader &reader, XlsxAxis* axis) +{ + Q_ASSERT(reader.name() == QLatin1String("r")); + + while ( !reader.atEnd() ) + { + reader.readNextStartElement(); + if ( reader.tokenType() == QXmlStreamReader::StartElement ) + { + if ( reader.name() == QLatin1String("t") ) + { + QString strAxisName = reader.readElementText(); + XlsxAxis::AxisPos axisPos = axis->axisPos; + axis->axisNames[ axisPos ] = strAxisName; + } + else + { + } + } + else if ( reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("r") ) + { + break; + } + } + + return true; +} + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +*/ + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +*/ + +void ChartPrivate::saveXmlAxis(QXmlStreamWriter &writer) const +{ + for ( int i = 0 ; i < axisList.size() ; ++i ) + { + XlsxAxis* axis = axisList[i].get(); + if ( nullptr == axis ) + continue; + + if ( axis->type == XlsxAxis::T_Cat ) { saveXmlAxisCatAx( writer, axis ); } + if ( axis->type == XlsxAxis::T_Val ) { saveXmlAxisValAx( writer, axis ); } + if ( axis->type == XlsxAxis::T_Ser ) { saveXmlAxisSerAx( writer, axis ); } + if ( axis->type == XlsxAxis::T_Date ) { saveXmlAxisDateAx( writer, axis ); } + } + +} + +void ChartPrivate::saveXmlAxisCatAx(QXmlStreamWriter &writer, XlsxAxis* axis) const +{ +/* + + + + + + + + + + + + +*/ + + writer.writeStartElement(QStringLiteral("c:catAx")); + + saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared + + //!TODO: write element + // auto + // lblAlgn + // lblOffset + // tickLblSkip + // tickMarkSkip + // noMultiLvlLbl + // extLst + + writer.writeEndElement(); // c:catAx +} + +void ChartPrivate::saveXmlAxisDateAx(QXmlStreamWriter &writer, XlsxAxis* axis) const +{ +/* + + + + + + + + + + + + + +*/ + + writer.writeStartElement(QStringLiteral("c:dateAx")); + + saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared + + //!TODO: write element + // auto + // lblOffset + // baseTimeUnit + // majorUnit + // majorTimeUnit + // minorUnit + // minorTimeUnit + // extLst + + writer.writeEndElement(); // c:dateAx +} + +void ChartPrivate::saveXmlAxisSerAx(QXmlStreamWriter &writer, XlsxAxis* axis) const +{ +/* + + + + + + + + +*/ + + writer.writeStartElement(QStringLiteral("c:serAx")); + + saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared + + //!TODO: write element + // tickLblSkip + // tickMarkSkip + // extLst + + writer.writeEndElement(); // c:serAx +} + +void ChartPrivate::saveXmlAxisValAx(QXmlStreamWriter &writer, XlsxAxis* axis) const +{ +/* + + + + + + + + + + +*/ + + writer.writeStartElement(QStringLiteral("c:valAx")); + + saveXmlAxisEG_AxShared(writer, axis); // EG_AxShared + + //!TODO: write element + // crossBetween + // majorUnit + // minorUnit + // dispUnits + // extLst + + writer.writeEndElement(); // c:valAx +} + +void ChartPrivate::saveXmlAxisEG_AxShared(QXmlStreamWriter &writer, XlsxAxis* axis) const +{ + /* + + + (*) + (*) + + (*) + + + (***********************) + + + + + + + (*) + + + + + + + */ + + writer.writeEmptyElement(QStringLiteral("c:axId")); // 21.2.2.9. axId (Axis ID) (mandatory value) + writer.writeAttribute(QStringLiteral("val"), QString::number(axis->axisId)); + + writer.writeStartElement(QStringLiteral("c:scaling")); // CT_Scaling (mandatory value) + writer.writeEmptyElement(QStringLiteral("c:orientation")); // CT_Orientation + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("minMax")); // ST_Orientation + writer.writeEndElement(); // c:scaling + + writer.writeEmptyElement(QStringLiteral("c:axPos")); // axPos CT_AxPos (mandatory value) + QString pos = GetAxisPosString( axis->axisPos ); + if ( !pos.isEmpty() ) + { + writer.writeAttribute(QStringLiteral("val"), pos); // ST_AxPos + } + + if( majorGridlinesEnabled ) + { + writer.writeEmptyElement(QStringLiteral("c:majorGridlines")); + } + if( minorGridlinesEnabled ) + { + writer.writeEmptyElement(QStringLiteral("c:minorGridlines")); + } + + saveXmlAxisEG_AxShared_Title(writer, axis); // "c:title" CT_Title + + writer.writeEmptyElement(QStringLiteral("c:crossAx")); // crossAx (mandatory value) + writer.writeAttribute(QStringLiteral("val"), QString::number(axis->crossAx)); + +} + +void ChartPrivate::saveXmlAxisEG_AxShared_Title(QXmlStreamWriter &writer, XlsxAxis* axis) const +{ + // CT_Title + + /* + + + + + + + + + + + */ + /* + + + + + + + + + */ + /* + + + + + + + + */ + /* + + + + + + + + */ + + writer.writeStartElement(QStringLiteral("c:title")); + + // CT_Tx {{ + writer.writeStartElement(QStringLiteral("c:tx")); + + writer.writeStartElement(QStringLiteral("c:rich")); // CT_TextBody + + writer.writeEmptyElement(QStringLiteral("a:bodyPr")); // CT_TextBodyProperties + + writer.writeEmptyElement(QStringLiteral("a:lstStyle")); // CT_TextListStyle + + writer.writeStartElement(QStringLiteral("a:p")); + + writer.writeStartElement(QStringLiteral("a:pPr")); + writer.writeAttribute(QStringLiteral("lvl"), QString::number(0)); + + writer.writeStartElement(QStringLiteral("a:defRPr")); + writer.writeAttribute(QStringLiteral("b"), QString::number(0)); + writer.writeEndElement(); // a:defRPr + writer.writeEndElement(); // a:pPr + + writer.writeStartElement(QStringLiteral("a:r")); + QString strAxisName = GetAxisName(axis); + writer.writeTextElement( QStringLiteral("a:t"), strAxisName ); + writer.writeEndElement(); // a:r + + writer.writeEndElement(); // a:p + + writer.writeEndElement(); // c:rich + + writer.writeEndElement(); // c:tx + // CT_Tx }} + + writer.writeStartElement(QStringLiteral("c:overlay")); + writer.writeAttribute(QStringLiteral("val"), QString::number(0)); // CT_Boolean + writer.writeEndElement(); // c:overlay + + writer.writeEndElement(); // c:title + +} + +QString ChartPrivate::GetAxisPosString( XlsxAxis::AxisPos axisPos ) const +{ + QString pos; + switch ( 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; // ?? + } + + return pos; +} + +QString ChartPrivate::GetAxisName(XlsxAxis* axis) const +{ + QString strAxisName; + if ( NULL == axis ) + return strAxisName; + + QString pos = GetAxisPosString( axis->axisPos ); // l, t, r, b + if ( pos.isEmpty() ) + return strAxisName; + + strAxisName = axis->axisNames[ axis->axisPos ]; + return strAxisName; +} + + +/// +/// \brief ChartPrivate::readSubTree +/// \param reader +/// \return +/// +QString ChartPrivate::readSubTree(QXmlStreamReader &reader) +{ + QString treeString; + QString prefix; + const auto& treeName = reader.name(); + + while (!reader.atEnd()) + { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + prefix = reader.prefix().toString(); + + treeString += QLatin1String("<") + reader.qualifiedName().toString(); + + const QXmlStreamAttributes attributes = reader.attributes(); + for (const QXmlStreamAttribute &attr : attributes) { + treeString += QLatin1String(" ") + attr.name().toString() + QLatin1String("=\"") + attr.value().toString() + QLatin1String("\""); + } + treeString += QStringLiteral(">"); + } + else if (reader.tokenType() == QXmlStreamReader::EndElement ) + { + if( reader.name() == treeName) + { + break; + } + treeString += QLatin1String(""); + } + } + + return treeString; +} + + +/// +/// \brief ChartPrivate::loadXmlChartLegend +/// \param reader +/// \return +/// +bool ChartPrivate::loadXmlChartLegend(QXmlStreamReader &reader) +{ + + Q_ASSERT(reader.name() == QLatin1String("legend")); + + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("legend"))) + { + if (reader.readNextStartElement()) + { + if (reader.name() == QLatin1String("legendPos")) // c:legendPos + { + QString pos = reader.attributes().value(QLatin1String("val")).toString(); + if( pos.compare(QLatin1String("r"), Qt::CaseInsensitive) == 0) + { + // legendPos = Chart::ChartAxisPos::Right; + legendPos = Chart::Right; + } + else + if( pos.compare(QLatin1String("l"), Qt::CaseInsensitive) == 0) + { + // legendPos = Chart::ChartAxisPos::Left; + legendPos = Chart::Left; + } + else + if( pos.compare(QLatin1String("t"), Qt::CaseInsensitive) == 0) + { + // legendPos = Chart::ChartAxisPos::Top; + legendPos = Chart::Top; + } + else + if( pos.compare(QLatin1String("b"), Qt::CaseInsensitive) == 0) + { + // legendPos = Chart::ChartAxisPos::Bottom; + legendPos = Chart::Bottom; + } + else + { + // legendPos = Chart::ChartAxisPos::None; + legendPos = Chart::None; + } + } + else + if (reader.name() == QLatin1String("overlay")) // c:legendPos + { + QString pos = reader.attributes().value(QLatin1String("val")).toString(); + if( pos.compare(QLatin1String("1"), Qt::CaseInsensitive) == 0 ) + { + legendOverlay = true; + } + else + { + legendOverlay = false; + } + } + } + } + + return false; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxchartsheet.cpp b/src/QXlsx/xlsxchartsheet.cpp new file mode 100644 index 0000000..12fdd79 --- /dev/null +++ b/src/QXlsx/xlsxchartsheet.cpp @@ -0,0 +1,142 @@ +// xlsxchartsheet.cpp + +#include +#include +#include +#include + +#include "xlsxchartsheet.h" +#include "xlsxchartsheet_p.h" +#include "xlsxworkbook.h" +#include "xlsxutility_p.h" +#include "xlsxdrawing_p.h" +#include "xlsxdrawinganchor_p.h" +#include "xlsxchart.h" + +QT_BEGIN_NAMESPACE_XLSX + +ChartsheetPrivate::ChartsheetPrivate(Chartsheet *p, Chartsheet::CreateFlag flag) + : AbstractSheetPrivate(p, flag), chart(0) +{ + +} + +ChartsheetPrivate::~ChartsheetPrivate() +{ +} + +/*! + \class Chartsheet + \inmodule QtXlsx + \brief Represent one chartsheet in the workbook. +*/ + +/*! + * \internal + */ +Chartsheet::Chartsheet(const QString &name, int id, Workbook *workbook, CreateFlag flag) + : AbstractSheet( name, id, workbook, new ChartsheetPrivate(this, flag) ) +{ + setSheetType(ST_ChartSheet); + + if (flag == Chartsheet::F_NewFromScratch) + { + d_func()->drawing = std::make_shared(this, flag); + + DrawingAbsoluteAnchor *anchor = new DrawingAbsoluteAnchor(drawing(), DrawingAnchor::Picture); + + anchor->pos = QPoint(0, 0); + anchor->ext = QSize(9293679, 6068786); + + QSharedPointer chart = QSharedPointer(new Chart(this, flag)); + chart->setChartType(Chart::CT_BarChart); + anchor->setObjectGraphicFrame(chart); + + d_func()->chart = chart.data(); + } +} + +/*! + * \internal + * + * Make a copy of this sheet. + */ + +Chartsheet *Chartsheet::copy(const QString &distName, int distId) const +{ + //:Todo + Q_UNUSED(distName) + Q_UNUSED(distId) + return 0; +} + +/*! + * Destroys this workssheet. + */ +Chartsheet::~Chartsheet() +{ +} + +/*! + * Returns the chart object of the sheet. + */ +Chart *Chartsheet::chart() +{ + Q_D(Chartsheet); + + return d->chart; +} + +void Chartsheet::saveToXmlFile(QIODevice *device) const +{ + Q_D(const Chartsheet); + d->relationships->clear(); + + QXmlStreamWriter writer(device); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeDefaultNamespace(QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main")); + writer.writeNamespace(QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"), QStringLiteral("r")); + writer.writeStartElement(QStringLiteral("chartsheet")); + + writer.writeStartElement(QStringLiteral("sheetViews")); + writer.writeEmptyElement(QStringLiteral("sheetView")); + writer.writeAttribute(QStringLiteral("workbookViewId"), QString::number(0)); + writer.writeAttribute(QStringLiteral("zoomToFit"), QStringLiteral("1")); + writer.writeEndElement(); //sheetViews + + int idx = d->workbook->drawings().indexOf(d->drawing.get()); + d->relationships->addWorksheetRelationship(QStringLiteral("/drawing"), QStringLiteral("../drawings/drawing%1.xml").arg(idx+1)); + + writer.writeEmptyElement(QStringLiteral("drawing")); + writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(d->relationships->count())); + + writer.writeEndElement();//chartsheet + writer.writeEndDocument(); +} + +bool Chartsheet::loadFromXmlFile(QIODevice *device) +{ + Q_D(Chartsheet); + + QXmlStreamReader reader(device); + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("drawing")) { + QString rId = reader.attributes().value(QStringLiteral("r:id")).toString(); + QString name = d->relationships->getRelationshipById(rId).target; + + const auto parts = splitPath(filePath()); + QString path = QDir::cleanPath(parts.first() + QLatin1String("/") + name); + + d->drawing = std::make_shared(this, F_LoadFromExists); + d->drawing->setFilePath(path); + } + } + } + + return true; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxcolor.cpp b/src/QXlsx/xlsxcolor.cpp new file mode 100644 index 0000000..84512b6 --- /dev/null +++ b/src/QXlsx/xlsxcolor.cpp @@ -0,0 +1,196 @@ +// xlsxcolor.cpp + +#include +#include +#include +#include + +#include "xlsxcolor_p.h" +#include "xlsxstyles_p.h" +#include "xlsxutility_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +XlsxColor::XlsxColor(const QColor &color) +{ + if (color.isValid()) + val.setValue(color); +} + +XlsxColor::XlsxColor(const QString &theme, const QString &tint) + :val(QStringList()<() && val.value().isValid(); +} + +bool XlsxColor::isIndexedColor() const +{ + return val.userType() == QMetaType::Int; +} + +bool XlsxColor::isThemeColor() const +{ + return val.userType() == QMetaType::QStringList; +} + +bool XlsxColor::isInvalid() const +{ + return !val.isValid(); +} + +QColor XlsxColor::rgbColor() const +{ + return isRgbColor() ? val.value() : QColor(); +} + +int XlsxColor::indexedColor() const +{ + return isIndexedColor() ? val.toInt() : -1; +} + +QStringList XlsxColor::themeColor() const +{ + return isThemeColor() ? val.toStringList() : QStringList(); +} + +bool XlsxColor::saveToXml(QXmlStreamWriter &writer, const QString &node) const +{ + if (!node.isEmpty()) + writer.writeEmptyElement(node); //color, bgColor, fgColor + else + writer.writeEmptyElement(QStringLiteral("color")); + + if (val.userType() == qMetaTypeId()) { + writer.writeAttribute(QStringLiteral("rgb"), XlsxColor::toARGBString(val.value())); + } else if (val.userType() == QMetaType::QStringList) { + QStringList themes = val.toStringList(); + writer.writeAttribute(QStringLiteral("theme"), themes[0]); + if (!themes[1].isEmpty()) + writer.writeAttribute(QStringLiteral("tint"), themes[1]); + } else if (val.userType() == QMetaType::Int) { + writer.writeAttribute(QStringLiteral("indexed"), val.toString()); + } else { + writer.writeAttribute(QStringLiteral("auto"), QStringLiteral("1")); + } + + return true; +} + +bool XlsxColor::loadFromXml(QXmlStreamReader &reader) +{ + const auto& attributes = reader.attributes(); + + if (attributes.hasAttribute(QLatin1String("rgb"))) { + const auto& colorString = attributes.value(QLatin1String("rgb")).toString(); + val.setValue(fromARGBString(colorString)); + } else if (attributes.hasAttribute(QLatin1String("indexed"))) { + int index = attributes.value(QLatin1String("indexed")).toInt(); + val.setValue(index); + } else if (attributes.hasAttribute(QLatin1String("theme"))) { + const auto& theme = attributes.value(QLatin1String("theme")).toString(); + const auto& tint = attributes.value(QLatin1String("tint")).toString(); + val.setValue(QStringList()<= 0x060000 // Qt 6.0 or over + = QMetaType::fromType(); +#else + = qMetaTypeId() ; +#endif + return QVariant(cref, this); +} + + +QColor XlsxColor::fromARGBString(const QString &c) +{ + QColor color; + if (c.startsWith(u'#')) { + color.setNamedColor(c); + } else { + color.setNamedColor(QLatin1Char('#') + c); + } + return color; +} + +QString XlsxColor::toARGBString(const QColor &c) +{ + return QString::asprintf("%02X%02X%02X%02X", c.alpha(), c.red(), c.green(), c.blue()); +} + +#if !defined(QT_NO_DATASTREAM) +QDataStream &operator<<(QDataStream &s, const XlsxColor &color) +{ + if (color.isInvalid()) + s<<0; + else if (color.isRgbColor()) + s<<1<>(QDataStream &s, XlsxColor &color) +{ + int marker(4); + s>>marker; + if (marker == 0) { + color = XlsxColor(); + } else if (marker == 1) { + QColor c; + s>>c; + color = XlsxColor(c); + } else if (marker == 2) { + int indexed; + s>>indexed; + color = XlsxColor(indexed); + } else if (marker == 3) { + QStringList list; + s>>list; + color = XlsxColor(list[0], list[1]); + } + + return s; +} + +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const XlsxColor &c) +{ + if (c.isInvalid()) + dbg.nospace() << "XlsxColor(invalid)"; + else if (c.isRgbColor()) + dbg.nospace() << c.rgbColor(); + else if (c.isIndexedColor()) + dbg.nospace() << "XlsxColor(indexed," << c.indexedColor() << ")"; + else if (c.isThemeColor()) + dbg.nospace() << "XlsxColor(theme," << c.themeColor().join(QLatin1Char(':')) << ')'; + + return dbg.space(); +} + +#endif + + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxconditionalformatting.cpp b/src/QXlsx/xlsxconditionalformatting.cpp new file mode 100644 index 0000000..510cc2c --- /dev/null +++ b/src/QXlsx/xlsxconditionalformatting.cpp @@ -0,0 +1,751 @@ +// xlsxconditionalformatting.cpp + +#include +#include +#include +#include + +#include "xlsxconditionalformatting.h" +#include "xlsxconditionalformatting_p.h" +#include "xlsxworksheet.h" +#include "xlsxcellrange.h" +#include "xlsxstyles_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +ConditionalFormattingPrivate::ConditionalFormattingPrivate() +{ + +} + +ConditionalFormattingPrivate::ConditionalFormattingPrivate(const ConditionalFormattingPrivate &other) + :QSharedData(other) +{ + +} + +ConditionalFormattingPrivate::~ConditionalFormattingPrivate() +{ + +} + +void ConditionalFormattingPrivate::writeCfVo(QXmlStreamWriter &writer, const XlsxCfVoData &cfvo) const +{ + writer.writeEmptyElement(QStringLiteral("cfvo")); + QString type; + switch(cfvo.type) { + case ConditionalFormatting::VOT_Formula: type=QStringLiteral("formula"); break; + case ConditionalFormatting::VOT_Max: type=QStringLiteral("max"); break; + case ConditionalFormatting::VOT_Min: type=QStringLiteral("min"); break; + case ConditionalFormatting::VOT_Num: type=QStringLiteral("num"); break; + case ConditionalFormatting::VOT_Percent: type=QStringLiteral("percent"); break; + case ConditionalFormatting::VOT_Percentile: type=QStringLiteral("percentile"); break; + default: break; + } + writer.writeAttribute(QStringLiteral("type"), type); + writer.writeAttribute(QStringLiteral("val"), cfvo.value); + if (!cfvo.gte) + writer.writeAttribute(QStringLiteral("gte"), QStringLiteral("0")); +} + +/*! + * \class ConditionalFormatting + * \brief Conditional formatting for single cell or ranges + * \inmodule QtXlsx + * + * The conditional formatting can be applied to a single cell or ranges of cells. + */ + + +/*! + \enum ConditionalFormatting::HighlightRuleType + + \value Highlight_LessThan + \value Highlight_LessThanOrEqual + \value Highlight_Equal + \value Highlight_NotEqual + \value Highlight_GreaterThanOrEqual + \value Highlight_GreaterThan + \value Highlight_Between + \value Highlight_NotBetween + + \value Highlight_ContainsText + \value Highlight_NotContainsText + \value Highlight_BeginsWith + \value Highlight_EndsWith + + \value Highlight_TimePeriod + + \value Highlight_Duplicate + \value Highlight_Unique + + \value Highlight_Blanks + \value Highlight_NoBlanks + \value Highlight_Errors + \value Highlight_NoErrors + + \value Highlight_Top + \value Highlight_TopPercent + \value Highlight_Bottom + \value Highlight_BottomPercent + + \value Highlight_AboveAverage + \value Highlight_AboveOrEqualAverage + \value Highlight_BelowAverage + \value Highlight_BelowOrEqualAverage + \value Highlight_AboveStdDev1 + \value Highlight_AboveStdDev2 + \value Highlight_AboveStdDev3 + \value Highlight_BelowStdDev1 + \value Highlight_BelowStdDev2 + \value Highlight_BelowStdDev3 + + \value Highlight_Expression +*/ + +/*! + \enum ConditionalFormatting::ValueObjectType + + \value VOT_Formula + \value VOT_Max + \value VOT_Min + \value VOT_Num + \value VOT_Percent + \value VOT_Percentile +*/ + +/*! + Construct a conditional formatting object +*/ +ConditionalFormatting::ConditionalFormatting() + :d(new ConditionalFormattingPrivate()) +{ + +} + +/*! + Constructs a copy of \a other. +*/ +ConditionalFormatting::ConditionalFormatting(const ConditionalFormatting &other) + :d(other.d) +{ + +} + +/*! + Assigns \a other to this conditional formatting and returns a reference to + this conditional formatting. + */ +ConditionalFormatting &ConditionalFormatting::operator=(const ConditionalFormatting &other) +{ + this->d = other.d; + return *this; +} + + +/*! + * Destroy the object. + */ +ConditionalFormatting::~ConditionalFormatting() +{ +} + +/*! + * Add a hightlight rule with the given \a type, \a formula1, \a formula2, + * \a format and \a stopIfTrue. + * Return false if failed. + */ +bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const QString &formula1, const QString &formula2, const Format &format, bool stopIfTrue) +{ + if (format.isEmpty()) + return false; + + bool skipFormula = false; + + auto cfRule = std::make_shared(); + if (type >= Highlight_LessThan && type <= Highlight_NotBetween) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("cellIs"); + QString op; + switch (type) { + case Highlight_Between: op = QStringLiteral("between"); break; + case Highlight_Equal: op = QStringLiteral("equal"); break; + case Highlight_GreaterThan: op = QStringLiteral("greaterThan"); break; + case Highlight_GreaterThanOrEqual: op = QStringLiteral("greaterThanOrEqual"); break; + case Highlight_LessThan: op = QStringLiteral("lessThan"); break; + case Highlight_LessThanOrEqual: op = QStringLiteral("lessThanOrEqual"); break; + case Highlight_NotBetween: op = QStringLiteral("notBetween"); break; + case Highlight_NotEqual: op = QStringLiteral("notEqual"); break; + default: break; + } + cfRule->attrs[XlsxCfRuleData::A_operator] = op; + } else if (type >= Highlight_ContainsText && type <= Highlight_EndsWith) { + if (type == Highlight_ContainsText) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("containsText"); + cfRule->attrs[XlsxCfRuleData::A_operator] = QStringLiteral("containsText"); + cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("NOT(ISERROR(SEARCH(\"%1\",%2)))").arg(formula1); + } else if (type == Highlight_NotContainsText) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("notContainsText"); + cfRule->attrs[XlsxCfRuleData::A_operator] = QStringLiteral("notContains"); + cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("ISERROR(SEARCH(\"%2\",%1))").arg(formula1); + } else if (type == Highlight_BeginsWith) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("beginsWith"); + cfRule->attrs[XlsxCfRuleData::A_operator] = QStringLiteral("beginsWith"); + cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("LEFT(%2,LEN(\"%1\"))=\"%1\"").arg(formula1); + } else { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("endsWith"); + cfRule->attrs[XlsxCfRuleData::A_operator] = QStringLiteral("endsWith"); + cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("RIGHT(%2,LEN(\"%1\"))=\"%1\"").arg(formula1); + } + cfRule->attrs[XlsxCfRuleData::A_text] = formula1; + skipFormula = true; + } else if (type == Highlight_TimePeriod) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("timePeriod"); + //:Todo + return false; + } else if (type == Highlight_Duplicate) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("duplicateValues"); + } else if (type == Highlight_Unique) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("uniqueValues"); + } else if (type == Highlight_Errors) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("containsErrors"); + cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("ISERROR(%1)"); + skipFormula = true; + } else if (type == Highlight_NoErrors) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("notContainsErrors"); + cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("NOT(ISERROR(%1))"); + skipFormula = true; + } else if (type == Highlight_Blanks) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("containsBlanks"); + cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("LEN(TRIM(%1))=0"); + skipFormula = true; + } else if (type == Highlight_NoBlanks) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("notContainsBlanks"); + cfRule->attrs[XlsxCfRuleData::A_formula1_temp] = QStringLiteral("LEN(TRIM(%1))>0"); + skipFormula = true; + } else if (type >= Highlight_Top && type <= Highlight_BottomPercent) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("top10"); + if (type == Highlight_Bottom || type == Highlight_BottomPercent) + cfRule->attrs[XlsxCfRuleData::A_bottom] = QStringLiteral("1"); + if (type == Highlight_TopPercent || type == Highlight_BottomPercent) + cfRule->attrs[XlsxCfRuleData::A_percent] = QStringLiteral("1"); + cfRule->attrs[XlsxCfRuleData::A_rank] = !formula1.isEmpty() ? formula1 : QStringLiteral("10"); + skipFormula = true; + } else if (type >= Highlight_AboveAverage && type <= Highlight_BelowStdDev3) { + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("aboveAverage"); + if (type >= Highlight_BelowAverage && type <= Highlight_BelowStdDev3) + cfRule->attrs[XlsxCfRuleData::A_aboveAverage] = QStringLiteral("0"); + if (type == Highlight_AboveOrEqualAverage || type == Highlight_BelowOrEqualAverage) + cfRule->attrs[XlsxCfRuleData::A_equalAverage] = QStringLiteral("1"); + if (type == Highlight_AboveStdDev1 || type == Highlight_BelowStdDev1) + cfRule->attrs[XlsxCfRuleData::A_stdDev] = QStringLiteral("1"); + else if (type == Highlight_AboveStdDev2 || type == Highlight_BelowStdDev2) + cfRule->attrs[XlsxCfRuleData::A_stdDev] = QStringLiteral("2"); + else if (type == Highlight_AboveStdDev3 || type == Highlight_BelowStdDev3) + cfRule->attrs[XlsxCfRuleData::A_stdDev] = QStringLiteral("3"); + } else if (type == Highlight_Expression){ + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("expression"); + } else { + return false; + } + + cfRule->dxfFormat = format; + if (stopIfTrue) + cfRule->attrs[XlsxCfRuleData::A_stopIfTrue] = true; + if (!skipFormula) { + if (!formula1.isEmpty()) + cfRule->attrs[XlsxCfRuleData::A_formula1] = formula1.startsWith(QLatin1String("=")) ? formula1.mid(1) : formula1; + if (!formula2.isEmpty()) + cfRule->attrs[XlsxCfRuleData::A_formula2] = formula2.startsWith(QLatin1String("=")) ? formula2.mid(1) : formula2; + } + d->cfRules.append(cfRule); + return true; +} + +/*! + * \overload + * + * Add a hightlight rule with the given \a type \a format and \a stopIfTrue. + */ +bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const Format &format, bool stopIfTrue) +{ + if ((type >= Highlight_AboveAverage && type <= Highlight_BelowStdDev3) + || (type >= Highlight_Duplicate && type <= Highlight_NoErrors)) { + return addHighlightCellsRule(type, QString(), QString(), format, stopIfTrue); + } + + return false; +} + +/*! + * \overload + * + * Add a hightlight rule with the given \a type, \a formula, \a format and \a stopIfTrue. + * Return false if failed. + */ +bool ConditionalFormatting::addHighlightCellsRule(HighlightRuleType type, const QString &formula, const Format &format, bool stopIfTrue) +{ + if (type == Highlight_Between || type == Highlight_NotBetween) + return false; + + return addHighlightCellsRule(type, formula, QString(), format, stopIfTrue); +} + +/*! + * Add a dataBar rule with the given \a color, \a type1, \a val1 + * , \a type2, \a val2, \a showData and \a stopIfTrue. + * Return false if failed. + */ +bool ConditionalFormatting::addDataBarRule(const QColor &color, ValueObjectType type1, const QString &val1, ValueObjectType type2, const QString &val2, bool showData, bool stopIfTrue) +{ + auto cfRule = std::make_shared(); + + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("dataBar"); + cfRule->attrs[XlsxCfRuleData::A_color1] = XlsxColor(color); + if (stopIfTrue) + cfRule->attrs[XlsxCfRuleData::A_stopIfTrue] = true; + if (!showData) + cfRule->attrs[XlsxCfRuleData::A_hideData] = true; + + XlsxCfVoData cfvo1(type1, val1); + XlsxCfVoData cfvo2(type2, val2); + cfRule->attrs[XlsxCfRuleData::A_cfvo1] = QVariant::fromValue(cfvo1); + cfRule->attrs[XlsxCfRuleData::A_cfvo2] = QVariant::fromValue(cfvo2); + + d->cfRules.append(cfRule); + return true; +} + +/*! + * \overload + * Add a dataBar rule with the given \a color, \a showData and \a stopIfTrue. + */ +bool ConditionalFormatting::addDataBarRule(const QColor &color, bool showData, bool stopIfTrue) +{ + return addDataBarRule(color, VOT_Min, QStringLiteral("0"), VOT_Max, QStringLiteral("0"), showData, stopIfTrue); +} + +/*! + * Add a colorScale rule with the given \a minColor, \a maxColor and \a stopIfTrue. + * Return false if failed. + */ +bool ConditionalFormatting::add2ColorScaleRule(const QColor &minColor, const QColor &maxColor, bool stopIfTrue) +{ + ValueObjectType type1 = VOT_Min; + ValueObjectType type2 = VOT_Max; + QString val1 = QStringLiteral("0"); + QString val2 = QStringLiteral("0"); + + auto cfRule = std::make_shared(); + + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("colorScale"); + cfRule->attrs[XlsxCfRuleData::A_color1] = XlsxColor(minColor); + cfRule->attrs[XlsxCfRuleData::A_color2] = XlsxColor(maxColor); + if (stopIfTrue) + cfRule->attrs[XlsxCfRuleData::A_stopIfTrue] = true; + + XlsxCfVoData cfvo1(type1, val1); + XlsxCfVoData cfvo2(type2, val2); + cfRule->attrs[XlsxCfRuleData::A_cfvo1] = QVariant::fromValue(cfvo1); + cfRule->attrs[XlsxCfRuleData::A_cfvo2] = QVariant::fromValue(cfvo2); + + d->cfRules.append(cfRule); + return true; +} + +/*! + * Add a colorScale rule with the given \a minColor, \a midColor, \a maxColor and \a stopIfTrue. + * Return false if failed. + */ +bool ConditionalFormatting::add3ColorScaleRule(const QColor &minColor, const QColor &midColor, const QColor &maxColor, bool stopIfTrue) +{ + ValueObjectType type1 = VOT_Min; + ValueObjectType type2 = VOT_Percent; + ValueObjectType type3 = VOT_Max; + QString val1 = QStringLiteral("0"); + QString val2 = QStringLiteral("50"); + QString val3 = QStringLiteral("0"); + + auto cfRule = std::make_shared(); + + cfRule->attrs[XlsxCfRuleData::A_type] = QStringLiteral("colorScale"); + cfRule->attrs[XlsxCfRuleData::A_color1] = XlsxColor(minColor); + cfRule->attrs[XlsxCfRuleData::A_color2] = XlsxColor(midColor); + cfRule->attrs[XlsxCfRuleData::A_color3] = XlsxColor(maxColor); + + if (stopIfTrue) + cfRule->attrs[XlsxCfRuleData::A_stopIfTrue] = true; + + XlsxCfVoData cfvo1(type1, val1); + XlsxCfVoData cfvo2(type2, val2); + XlsxCfVoData cfvo3(type3, val3); + cfRule->attrs[XlsxCfRuleData::A_cfvo1] = QVariant::fromValue(cfvo1); + cfRule->attrs[XlsxCfRuleData::A_cfvo2] = QVariant::fromValue(cfvo2); + cfRule->attrs[XlsxCfRuleData::A_cfvo3] = QVariant::fromValue(cfvo3); + + d->cfRules.append(cfRule); + return true; +} + +/*! + Returns the ranges on which the validation will be applied. + */ +QList ConditionalFormatting::ranges() const +{ + return d->ranges; +} + +/*! + Add the \a cell on which the conditional formatting will apply to. + */ +void ConditionalFormatting::addCell(const CellReference &cell) +{ + d->ranges.append(CellRange(cell, cell)); +} + +/*! + \overload + Add the cell(\a row, \a col) on which the conditional formatting will apply to. + */ +void ConditionalFormatting::addCell(int row, int col) +{ + d->ranges.append(CellRange(row, col, row, col)); +} + +/*! + \overload + Add the range(\a firstRow, \a firstCol, \a lastRow, \a lastCol) on + which the conditional formatting will apply to. + */ +void ConditionalFormatting::addRange(int firstRow, int firstCol, int lastRow, int lastCol) +{ + d->ranges.append(CellRange(firstRow, firstCol, lastRow, lastCol)); +} + +/*! + Add the \a range on which the conditional formatting will apply to. + */ +void ConditionalFormatting::addRange(const CellRange &range) +{ + d->ranges.append(range); +} + +bool ConditionalFormattingPrivate::readCfRule(QXmlStreamReader &reader, XlsxCfRuleData *rule, Styles *styles) +{ + Q_ASSERT(reader.name() == QLatin1String("cfRule")); + QXmlStreamAttributes attrs = reader.attributes(); + if (attrs.hasAttribute(QLatin1String("type"))) + rule->attrs[XlsxCfRuleData::A_type] = attrs.value(QLatin1String("type")).toString(); + if (attrs.hasAttribute(QLatin1String("dxfId"))) { + int id = attrs.value(QLatin1String("dxfId")).toInt(); + if (styles) + rule->dxfFormat = styles->dxfFormat(id); + else + rule->dxfFormat.setDxfIndex(id); + } + rule->priority = attrs.value(QLatin1String("priority")).toInt(); + if (attrs.value(QLatin1String("stopIfTrue")) == QLatin1String("1")) { + //default is false + rule->attrs[XlsxCfRuleData::A_stopIfTrue] = QLatin1String("1"); + } + if (attrs.value(QLatin1String("aboveAverage")) == QLatin1String("0")) { + //default is true + rule->attrs[XlsxCfRuleData::A_aboveAverage] = QLatin1String("0"); + } + if (attrs.value(QLatin1String("percent")) == QLatin1String("1")) { + //default is false + rule->attrs[XlsxCfRuleData::A_percent] = QLatin1String("1"); + } + if (attrs.value(QLatin1String("bottom")) == QLatin1String("1")) { + //default is false + rule->attrs[XlsxCfRuleData::A_bottom] = QLatin1String("1"); + } + if (attrs.hasAttribute(QLatin1String("operator"))) + rule->attrs[XlsxCfRuleData::A_operator] = attrs.value(QLatin1String("operator")).toString(); + + if (attrs.hasAttribute(QLatin1String("text"))) + rule->attrs[XlsxCfRuleData::A_text] = attrs.value(QLatin1String("text")).toString(); + + if (attrs.hasAttribute(QLatin1String("timePeriod"))) + rule->attrs[XlsxCfRuleData::A_timePeriod] = attrs.value(QLatin1String("timePeriod")).toString(); + + if (attrs.hasAttribute(QLatin1String("rank"))) + rule->attrs[XlsxCfRuleData::A_rank] = attrs.value(QLatin1String("rank")).toString(); + + if (attrs.hasAttribute(QLatin1String("stdDev"))) + rule->attrs[XlsxCfRuleData::A_stdDev] = attrs.value(QLatin1String("stdDev")).toString(); + + if (attrs.value(QLatin1String("equalAverage")) == QLatin1String("1")) { + //default is false + rule->attrs[XlsxCfRuleData::A_equalAverage] = QLatin1String("1"); + } + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("formula")) { + const QString f = reader.readElementText(); + if (!rule->attrs.contains(XlsxCfRuleData::A_formula1)) + rule->attrs[XlsxCfRuleData::A_formula1] = f; + else if (!rule->attrs.contains(XlsxCfRuleData::A_formula2)) + rule->attrs[XlsxCfRuleData::A_formula2] = f; + else if (!rule->attrs.contains(XlsxCfRuleData::A_formula3)) + rule->attrs[XlsxCfRuleData::A_formula3] = f; + } else if (reader.name() == QLatin1String("dataBar")) { + readCfDataBar(reader, rule); + } else if (reader.name() == QLatin1String("colorScale")) { + readCfColorScale(reader, rule); + } + } + if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QStringLiteral("conditionalFormatting")) { + break; + } + } + return true; +} + +bool ConditionalFormattingPrivate::readCfDataBar(QXmlStreamReader &reader, XlsxCfRuleData *rule) +{ + Q_ASSERT(reader.name() == QLatin1String("dataBar")); + QXmlStreamAttributes attrs = reader.attributes(); + if (attrs.value(QLatin1String("showValue")) == QLatin1String("0")) + rule->attrs[XlsxCfRuleData::A_hideData] = QStringLiteral("1"); + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("cfvo")) { + XlsxCfVoData data; + readCfVo(reader, data); + if (!rule->attrs.contains(XlsxCfRuleData::A_cfvo1)) + rule->attrs[XlsxCfRuleData::A_cfvo1] = QVariant::fromValue(data); + else + rule->attrs[XlsxCfRuleData::A_cfvo2] = QVariant::fromValue(data); + } else if (reader.name() == QLatin1String("color")) { + XlsxColor color; + color.loadFromXml(reader); + rule->attrs[XlsxCfRuleData::A_color1] = color; + } + } + if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QStringLiteral("dataBar")) { + break; + } + } + + return true; +} + +bool ConditionalFormattingPrivate::readCfColorScale(QXmlStreamReader &reader, XlsxCfRuleData *rule) +{ + Q_ASSERT(reader.name() == QLatin1String("colorScale")); + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("cfvo")) { + XlsxCfVoData data; + readCfVo(reader, data); + if (!rule->attrs.contains(XlsxCfRuleData::A_cfvo1)) + rule->attrs[XlsxCfRuleData::A_cfvo1] = QVariant::fromValue(data); + else if (!rule->attrs.contains(XlsxCfRuleData::A_cfvo2)) + rule->attrs[XlsxCfRuleData::A_cfvo2] = QVariant::fromValue(data); + else + rule->attrs[XlsxCfRuleData::A_cfvo3] = QVariant::fromValue(data); + } else if (reader.name() == QLatin1String("color")) { + XlsxColor color; + color.loadFromXml(reader); + if (!rule->attrs.contains(XlsxCfRuleData::A_color1)) + rule->attrs[XlsxCfRuleData::A_color1] = color; + else if (!rule->attrs.contains(XlsxCfRuleData::A_color2)) + rule->attrs[XlsxCfRuleData::A_color2] = color; + else + rule->attrs[XlsxCfRuleData::A_color3] = color; + } + } + if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QStringLiteral("colorScale")) { + break; + } + } + + return true; +} + +bool ConditionalFormattingPrivate::readCfVo(QXmlStreamReader &reader, XlsxCfVoData &cfvo) +{ + Q_ASSERT(reader.name() == QStringLiteral("cfvo")); + + QXmlStreamAttributes attrs = reader.attributes(); + + QString type = attrs.value(QLatin1String("type")).toString(); + ConditionalFormatting::ValueObjectType t; + if (type == QLatin1String("formula")) + t = ConditionalFormatting::VOT_Formula; + else if (type == QLatin1String("max")) + t = ConditionalFormatting::VOT_Max; + else if (type == QLatin1String("min")) + t = ConditionalFormatting::VOT_Min; + else if (type == QLatin1String("num")) + t = ConditionalFormatting::VOT_Num; + else if (type == QLatin1String("percent")) + t = ConditionalFormatting::VOT_Percent; + else //if (type == QLatin1String("percentile")) + t = ConditionalFormatting::VOT_Percentile; + + cfvo.type = t; + cfvo.value = attrs.value(QLatin1String("val")).toString(); + if (attrs.value(QLatin1String("gte")) == QLatin1String("0")) { + //default is true + cfvo.gte = false; + } + return true; +} + +bool ConditionalFormatting::loadFromXml(QXmlStreamReader &reader, Styles *styles) +{ + Q_ASSERT(reader.name() == QStringLiteral("conditionalFormatting")); + + d->ranges.clear(); + d->cfRules.clear(); + QXmlStreamAttributes attrs = reader.attributes(); + const QString sqref = attrs.value(QLatin1String("sqref")).toString(); + const auto sqrefParts = sqref.split(QLatin1Char(' ')); + for (const QString &range : sqrefParts) { + this->addRange(range); + } + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("cfRule")) { + auto cfRule = std::make_shared(); + d->readCfRule(reader, cfRule.get(), styles); + d->cfRules.append(cfRule); + } + } + if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QStringLiteral("conditionalFormatting")) { + break; + } + } + + + return true; +} + +bool ConditionalFormatting::saveToXml(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("conditionalFormatting")); + QStringList sqref; + const auto rangeList = ranges(); + for (const CellRange &range : rangeList) { + sqref.append(range.toString()); + } + writer.writeAttribute(QStringLiteral("sqref"), sqref.join(QLatin1String(" "))); + + for (int i=0; icfRules.size(); ++i) { + const std::shared_ptr &rule = d->cfRules[i]; + writer.writeStartElement(QStringLiteral("cfRule")); + writer.writeAttribute(QStringLiteral("type"), rule->attrs[XlsxCfRuleData::A_type].toString()); + if (rule->dxfFormat.dxfIndexValid()) + writer.writeAttribute(QStringLiteral("dxfId"), QString::number(rule->dxfFormat.dxfIndex())); + writer.writeAttribute(QStringLiteral("priority"), QString::number(rule->priority)); + + auto it = rule->attrs.constFind(XlsxCfRuleData::A_stopIfTrue); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("stopIfTrue"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_aboveAverage); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("aboveAverage"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_percent); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("percent"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_bottom); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("bottom"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_operator); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("operator"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_text); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("text"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_timePeriod); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("timePeriod"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_rank); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("rank"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_stdDev); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("stdDev"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_equalAverage); + if (it != rule->attrs.constEnd()) + writer.writeAttribute(QStringLiteral("equalAverage"), it.value().toString()); + + if (rule->attrs[XlsxCfRuleData::A_type] == QLatin1String("dataBar")) { + writer.writeStartElement(QStringLiteral("dataBar")); + if (rule->attrs.contains(XlsxCfRuleData::A_hideData)) + writer.writeAttribute(QStringLiteral("showValue"), QStringLiteral("0")); + d->writeCfVo(writer, rule->attrs[XlsxCfRuleData::A_cfvo1].value()); + d->writeCfVo(writer, rule->attrs[XlsxCfRuleData::A_cfvo2].value()); + rule->attrs[XlsxCfRuleData::A_color1].value().saveToXml(writer); + writer.writeEndElement();//dataBar + } else if (rule->attrs[XlsxCfRuleData::A_type] == QLatin1String("colorScale")) { + writer.writeStartElement(QStringLiteral("colorScale")); + d->writeCfVo(writer, rule->attrs[XlsxCfRuleData::A_cfvo1].value()); + d->writeCfVo(writer, rule->attrs[XlsxCfRuleData::A_cfvo2].value()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_cfvo3); + if (it != rule->attrs.constEnd()) + d->writeCfVo(writer, it.value().value()); + + rule->attrs[XlsxCfRuleData::A_color1].value().saveToXml(writer); + rule->attrs[XlsxCfRuleData::A_color2].value().saveToXml(writer); + + it = rule->attrs.constFind(XlsxCfRuleData::A_color3); + if (it != rule->attrs.constEnd()) + it.value().value().saveToXml(writer); + + writer.writeEndElement();//colorScale + } + + + it = rule->attrs.constFind(XlsxCfRuleData::A_formula1_temp); + if (it != rule->attrs.constEnd()) { + const auto _ranges = ranges(); + const auto begin = _ranges.begin(); + if (begin != _ranges.end()) { + QString str = begin->toString(); + QString startCell = str.mid(0, str.indexOf(u':')); + writer.writeTextElement(QStringLiteral("formula"), it.value().toString().arg(startCell)); + } + } else if ((it = rule->attrs.constFind(XlsxCfRuleData::A_formula1)) != rule->attrs.constEnd()) { + writer.writeTextElement(QStringLiteral("formula"), it.value().toString()); + } + + it = rule->attrs.constFind(XlsxCfRuleData::A_formula2); + if (it != rule->attrs.constEnd()) + writer.writeTextElement(QStringLiteral("formula"), it.value().toString()); + + it = rule->attrs.constFind(XlsxCfRuleData::A_formula3); + if (it != rule->attrs.constEnd()) + writer.writeTextElement(QStringLiteral("formula"), it.value().toString()); + + writer.writeEndElement(); //cfRule + } + + writer.writeEndElement(); //conditionalFormatting + return true; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxcontenttypes.cpp b/src/QXlsx/xlsxcontenttypes.cpp new file mode 100644 index 0000000..e3dd167 --- /dev/null +++ b/src/QXlsx/xlsxcontenttypes.cpp @@ -0,0 +1,184 @@ +// xlsxcontenttypes.cpp + +#include +#include +#include +#include +#include +#include + +#include "xlsxcontenttypes_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +ContentTypes::ContentTypes(CreateFlag flag) + :AbstractOOXmlFile(flag) +{ + m_package_prefix = QStringLiteral("application/vnd.openxmlformats-package."); + m_document_prefix = QStringLiteral("application/vnd.openxmlformats-officedocument."); + + m_defaults.insert(QStringLiteral("rels"), m_package_prefix + QLatin1String("relationships+xml")); + m_defaults.insert(QStringLiteral("xml"), QStringLiteral("application/xml")); +} + +void ContentTypes::addDefault(const QString &key, const QString &value) +{ + m_defaults.insert(key, value); +} + +void ContentTypes::addOverride(const QString &key, const QString &value) +{ + m_overrides.insert(key, value); +} + +void ContentTypes::addDocPropApp() +{ + addOverride(QStringLiteral("/docProps/app.xml"), m_document_prefix + QLatin1String("extended-properties+xml")); +} + +void ContentTypes::addDocPropCore() +{ + addOverride(QStringLiteral("/docProps/core.xml"), m_package_prefix + QLatin1String("core-properties+xml")); +} + +void ContentTypes::addStyles() +{ + addOverride(QStringLiteral("/xl/styles.xml"), m_document_prefix + QLatin1String("spreadsheetml.styles+xml")); +} + +void ContentTypes::addTheme() +{ + addOverride(QStringLiteral("/xl/theme/theme1.xml"), m_document_prefix + QLatin1String("theme+xml")); +} + +void ContentTypes::addWorkbook() +{ + addOverride(QStringLiteral("/xl/workbook.xml"), m_document_prefix + QLatin1String("spreadsheetml.sheet.main+xml")); +} + +void ContentTypes::addWorksheetName(const QString &name) +{ + addOverride(QStringLiteral("/xl/worksheets/%1.xml").arg(name), m_document_prefix + QLatin1String("spreadsheetml.worksheet+xml")); +} + +void ContentTypes::addChartsheetName(const QString &name) +{ + addOverride(QStringLiteral("/xl/chartsheets/%1.xml").arg(name), m_document_prefix + QLatin1String("spreadsheetml.chartsheet+xml")); +} + +void ContentTypes::addDrawingName(const QString &name) +{ + addOverride(QStringLiteral("/xl/drawings/%1.xml").arg(name), m_document_prefix + QLatin1String("drawing+xml")); +} + +void ContentTypes::addChartName(const QString &name) +{ + addOverride(QStringLiteral("/xl/charts/%1.xml").arg(name), m_document_prefix + QLatin1String("drawingml.chart+xml")); +} + +void ContentTypes::addCommentName(const QString &name) +{ + addOverride(QStringLiteral("/xl/%1.xml").arg(name), m_document_prefix + QLatin1String("spreadsheetml.comments+xml")); +} + +void ContentTypes::addTableName(const QString &name) +{ + addOverride(QStringLiteral("/xl/tables/%1.xml").arg(name), m_document_prefix + QLatin1String("spreadsheetml.table+xml")); +} + +void ContentTypes::addExternalLinkName(const QString &name) +{ + addOverride(QStringLiteral("/xl/externalLinks/%1.xml").arg(name), m_document_prefix + QLatin1String("spreadsheetml.externalLink+xml")); +} + +void ContentTypes::addSharedString() +{ + addOverride(QStringLiteral("/xl/sharedStrings.xml"), m_document_prefix + QLatin1String("spreadsheetml.sharedStrings+xml")); +} + +void ContentTypes::addVmlName() +{ + addOverride(QStringLiteral("vml"), m_document_prefix + QLatin1String("vmlDrawing")); +} + +void ContentTypes::addCalcChain() +{ + addOverride(QStringLiteral("/xl/calcChain.xml"), m_document_prefix + QLatin1String("spreadsheetml.calcChain+xml")); +} + +void ContentTypes::addVbaProject() +{ + //:TODO + addOverride(QStringLiteral("bin"), QStringLiteral("application/vnd.ms-office.vbaProject")); +} + +void ContentTypes::clearOverrides() +{ + m_overrides.clear(); +} + +void ContentTypes::saveToXmlFile(QIODevice *device) const +{ + QXmlStreamWriter writer(device); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("Types")); + writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/package/2006/content-types")); + + { + QMapIterator it(m_defaults); + while (it.hasNext()) { + it.next(); + writer.writeStartElement(QStringLiteral("Default")); + writer.writeAttribute(QStringLiteral("Extension"), it.key()); + writer.writeAttribute(QStringLiteral("ContentType"), it.value()); + writer.writeEndElement();//Default + } + } + + { + QMapIterator it(m_overrides); + while (it.hasNext()) { + it.next(); + writer.writeStartElement(QStringLiteral("Override")); + writer.writeAttribute(QStringLiteral("PartName"), it.key()); + writer.writeAttribute(QStringLiteral("ContentType"), it.value()); + writer.writeEndElement(); //Override + } + } + + writer.writeEndElement();//Types + writer.writeEndDocument(); + +} + +bool ContentTypes::loadFromXmlFile(QIODevice *device) +{ + m_defaults.clear(); + m_overrides.clear(); + + QXmlStreamReader reader(device); + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("Default")) { + QXmlStreamAttributes attrs = reader.attributes(); + QString extension = attrs.value(QLatin1String("Extension")).toString(); + QString type = attrs.value(QLatin1String("ContentType")).toString(); + m_defaults.insert(extension, type); + } else if (reader.name() == QLatin1String("Override")) { + QXmlStreamAttributes attrs = reader.attributes(); + QString partName = attrs.value(QLatin1String("PartName")).toString(); + QString type = attrs.value(QLatin1String("ContentType")).toString(); + m_overrides.insert(partName, type); + } + } + + if (reader.hasError()) { + qDebug()< +#include +#include + +#include "xlsxdatavalidation.h" +#include "xlsxdatavalidation_p.h" +#include "xlsxworksheet.h" +#include "xlsxcellrange.h" + +QT_BEGIN_NAMESPACE_XLSX + +DataValidationPrivate::DataValidationPrivate() + :validationType(DataValidation::None), validationOperator(DataValidation::Between) + , errorStyle(DataValidation::Stop), allowBlank(false), isPromptMessageVisible(true) + , isErrorMessageVisible(true) +{ + +} + +DataValidationPrivate::DataValidationPrivate(DataValidation::ValidationType type, DataValidation::ValidationOperator op, const QString &formula1, const QString &formula2, bool allowBlank) + :validationType(type), validationOperator(op) + , errorStyle(DataValidation::Stop), allowBlank(allowBlank), isPromptMessageVisible(true) + , isErrorMessageVisible(true), formula1(formula1), formula2(formula2) +{ + +} + +DataValidationPrivate::DataValidationPrivate(const DataValidationPrivate &other) + :QSharedData(other) + , validationType(DataValidation::None), validationOperator(DataValidation::Between) + , errorStyle(DataValidation::Stop), allowBlank(false), isPromptMessageVisible(true) + , isErrorMessageVisible(true) +{ + +} + +DataValidationPrivate::~DataValidationPrivate() +{ + +} + +/*! + * \class DataValidation + * \brief Data validation for single cell or a range + * \inmodule QtXlsx + * + * The data validation can be applied to a single cell or a range of cells. + */ + +/*! + * \enum DataValidation::ValidationType + * + * The enum type defines the type of data that you wish to validate. + * + * \value None the type of data is unrestricted. This is the same as not applying a data validation. + * \value Whole restricts the cell to integer values. Means "Whole number"? + * \value Decimal restricts the cell to decimal values. + * \value List restricts the cell to a set of user specified values. + * \value Date restricts the cell to date values. + * \value Time restricts the cell to time values. + * \value TextLength restricts the cell data based on an integer string length. + * \value Custom restricts the cell based on an external Excel formula that returns a true/false value. + */ + +/*! + * \enum DataValidation::ValidationOperator + * + * The enum type defines the criteria by which the data in the + * cell is validated + * + * \value Between + * \value NotBetween + * \value Equal + * \value NotEqual + * \value LessThan + * \value LessThanOrEqual + * \value GreaterThan + * \value GreaterThanOrEqual + */ + +/*! + * \enum DataValidation::ErrorStyle + * + * The enum type defines the type of error dialog that + * is displayed. + * + * \value Stop + * \value Warning + * \value Information + */ + +/*! + * Construct a data validation object with the given \a type, \a op, \a formula1 + * \a formula2, and \a allowBlank. + */ +DataValidation::DataValidation(ValidationType type, ValidationOperator op, const QString &formula1, const QString &formula2, bool allowBlank) + :d(new DataValidationPrivate(type, op, formula1, formula2, allowBlank)) +{ + +} + +/*! + Construct a data validation object +*/ +DataValidation::DataValidation() + :d(new DataValidationPrivate()) +{ + +} + +/*! + Constructs a copy of \a other. +*/ +DataValidation::DataValidation(const DataValidation &other) + :d(other.d) +{ + +} + +/*! + Assigns \a other to this validation and returns a reference to this validation. + */ +DataValidation &DataValidation::operator=(const DataValidation &other) +{ + this->d = other.d; + return *this; +} + + +/*! + * Destroy the object. + */ +DataValidation::~DataValidation() +{ +} + +/*! + Returns the validation type. + */ +DataValidation::ValidationType DataValidation::validationType() const +{ + return d->validationType; +} + +/*! + Returns the validation operator. + */ +DataValidation::ValidationOperator DataValidation::validationOperator() const +{ + return d->validationOperator; +} + +/*! + Returns the validation error style. + */ +DataValidation::ErrorStyle DataValidation::errorStyle() const +{ + return d->errorStyle; +} + +/*! + Returns the formula1. + */ +QString DataValidation::formula1() const +{ + return d->formula1; +} + +/*! + Returns the formula2. + */ +QString DataValidation::formula2() const +{ + return d->formula2; +} + +/*! + Returns whether blank is allowed. + */ +bool DataValidation::allowBlank() const +{ + return d->allowBlank; +} + +/*! + Returns the error message. + */ +QString DataValidation::errorMessage() const +{ + return d->errorMessage; +} + +/*! + Returns the error message title. + */ +QString DataValidation::errorMessageTitle() const +{ + return d->errorMessageTitle; +} + +/*! + Returns the prompt message. + */ +QString DataValidation::promptMessage() const +{ + return d->promptMessage; +} + +/*! + Returns the prompt message title. + */ +QString DataValidation::promptMessageTitle() const +{ + return d->promptMessageTitle; +} + +/*! + Returns the whether prompt message is shown. + */ +bool DataValidation::isPromptMessageVisible() const +{ + return d->isPromptMessageVisible; +} + +/*! + Returns the whether error message is shown. + */ +bool DataValidation::isErrorMessageVisible() const +{ + return d->isErrorMessageVisible; +} + +/*! + Returns the ranges on which the validation will be applied. + */ +QList DataValidation::ranges() const +{ + return d->ranges; +} + +/*! + Sets the validation type to \a type. + */ +void DataValidation::setValidationType(DataValidation::ValidationType type) +{ + d->validationType = type; +} + +/*! + Sets the validation operator to \a op. + */ +void DataValidation::setValidationOperator(DataValidation::ValidationOperator op) +{ + d->validationOperator = op; +} + +/*! + Sets the error style to \a es. + */ +void DataValidation::setErrorStyle(DataValidation::ErrorStyle es) +{ + d->errorStyle = es; +} + +/*! + Sets the formula1 to \a formula. + */ +void DataValidation::setFormula1(const QString &formula) +{ + if (formula.startsWith(QLatin1Char('='))) + d->formula1 = formula.mid(1); + else + d->formula1 = formula; +} + +/*! + Sets the formulas to \a formula. + */ +void DataValidation::setFormula2(const QString &formula) +{ + if (formula.startsWith(QLatin1Char('='))) + d->formula2 = formula.mid(1); + else + d->formula2 = formula; +} + +/*! + Sets the error message to \a error with title \a title. + */ +void DataValidation::setErrorMessage(const QString &error, const QString &title) +{ + d->errorMessage = error; + d->errorMessageTitle = title; +} + +/*! + Sets the prompt message to \a prompt with title \a title. + */ +void DataValidation::setPromptMessage(const QString &prompt, const QString &title) +{ + d->promptMessage = prompt; + d->promptMessageTitle = title; +} + +/*! + Enable/disabe blank allow based on \a enable. + */ +void DataValidation::setAllowBlank(bool enable) +{ + d->allowBlank = enable; +} + +/*! + Enable/disabe prompt message visible based on \a visible. + */ +void DataValidation::setPromptMessageVisible(bool visible) +{ + d->isPromptMessageVisible = visible; +} + +/*! + Enable/disabe error message visible based on \a visible. + */ +void DataValidation::setErrorMessageVisible(bool visible) +{ + d->isErrorMessageVisible = visible; +} + +/*! + Add the \a cell on which the DataValidation will apply to. + */ +void DataValidation::addCell(const CellReference &cell) +{ + d->ranges.append(CellRange(cell, cell)); +} + +/*! + \overload + Add the cell(\a row, \a col) on which the DataValidation will apply to. + */ +void DataValidation::addCell(int row, int col) +{ + d->ranges.append(CellRange(row, col, row, col)); +} + +/*! + \overload + Add the range(\a firstRow, \a firstCol, \a lastRow, \a lastCol) on + which the DataValidation will apply to. + */ +void DataValidation::addRange(int firstRow, int firstCol, int lastRow, int lastCol) +{ + d->ranges.append(CellRange(firstRow, firstCol, lastRow, lastCol)); +} + +/*! + Add the \a range on which the DataValidation will apply to. + */ +void DataValidation::addRange(const CellRange &range) +{ + d->ranges.append(range); +} + +/*! + * \internal + */ +bool DataValidation::saveToXml(QXmlStreamWriter &writer) const +{ + static const QMap typeMap = { + {DataValidation::None, QStringLiteral("none")}, + {DataValidation::Whole, QStringLiteral("whole")}, + {DataValidation::Decimal, QStringLiteral("decimal")}, + {DataValidation::List, QStringLiteral("list")}, + {DataValidation::Date, QStringLiteral("date")}, + {DataValidation::Time, QStringLiteral("time")}, + {DataValidation::TextLength, QStringLiteral("textLength")}, + {DataValidation::Custom, QStringLiteral("custom")} + }; + + static const QMap opMap = { + {DataValidation::Between, QStringLiteral("between")}, + {DataValidation::NotBetween, QStringLiteral("notBetween")}, + {DataValidation::Equal, QStringLiteral("equal")}, + {DataValidation::NotEqual, QStringLiteral("notEqual")}, + {DataValidation::LessThan, QStringLiteral("lessThan")}, + {DataValidation::LessThanOrEqual, QStringLiteral("lessThanOrEqual")}, + {DataValidation::GreaterThan, QStringLiteral("greaterThan")}, + {DataValidation::GreaterThanOrEqual, QStringLiteral("greaterThanOrEqual")} + }; + + static const QMap esMap = { + {DataValidation::Stop, QStringLiteral("stop")}, + {DataValidation::Warning, QStringLiteral("warning")}, + {DataValidation::Information, QStringLiteral("information")} + }; + + writer.writeStartElement(QStringLiteral("dataValidation")); + if (validationType() != DataValidation::None) + writer.writeAttribute(QStringLiteral("type"), typeMap[validationType()]); + if (errorStyle() != DataValidation::Stop) + writer.writeAttribute(QStringLiteral("errorStyle"), esMap[errorStyle()]); + if (validationOperator() != DataValidation::Between) + writer.writeAttribute(QStringLiteral("operator"), opMap[validationOperator()]); + if (allowBlank()) + writer.writeAttribute(QStringLiteral("allowBlank"), QStringLiteral("1")); + // if (dropDownVisible()) + // writer.writeAttribute(QStringLiteral("showDropDown"), QStringLiteral("1")); + if (isPromptMessageVisible()) + writer.writeAttribute(QStringLiteral("showInputMessage"), QStringLiteral("1")); + if (isErrorMessageVisible()) + writer.writeAttribute(QStringLiteral("showErrorMessage"), QStringLiteral("1")); + if (!errorMessageTitle().isEmpty()) + writer.writeAttribute(QStringLiteral("errorTitle"), errorMessageTitle()); + if (!errorMessage().isEmpty()) + writer.writeAttribute(QStringLiteral("error"), errorMessage()); + if (!promptMessageTitle().isEmpty()) + writer.writeAttribute(QStringLiteral("promptTitle"), promptMessageTitle()); + if (!promptMessage().isEmpty()) + writer.writeAttribute(QStringLiteral("prompt"), promptMessage()); + + QStringList sqref; + const auto rangeList = ranges(); + for (const CellRange &range : rangeList) + sqref.append(range.toString()); + writer.writeAttribute(QStringLiteral("sqref"), sqref.join(QLatin1String(" "))); + + if (!formula1().isEmpty()) + writer.writeTextElement(QStringLiteral("formula1"), formula1()); + if (!formula2().isEmpty()) + writer.writeTextElement(QStringLiteral("formula2"), formula2()); + + writer.writeEndElement(); //dataValidation + + return true; +} + +/*! + * \internal + */ +DataValidation DataValidation::loadFromXml(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("dataValidation")); + + static const QMap typeMap = { + {QStringLiteral("none"), DataValidation::None}, + {QStringLiteral("whole"), DataValidation::Whole}, + {QStringLiteral("decimal"), DataValidation::Decimal}, + {QStringLiteral("list"), DataValidation::List}, + {QStringLiteral("date"), DataValidation::Date}, + {QStringLiteral("time"), DataValidation::Time}, + {QStringLiteral("textLength"), DataValidation::TextLength}, + {QStringLiteral("custom"), DataValidation::Custom} + }; + + static const QMap opMap = { + {QStringLiteral("between"), DataValidation::Between}, + {QStringLiteral("notBetween"), DataValidation::NotBetween}, + {QStringLiteral("equal"), DataValidation::Equal}, + {QStringLiteral("notEqual"), DataValidation::NotEqual}, + {QStringLiteral("lessThan"), DataValidation::LessThan}, + {QStringLiteral("lessThanOrEqual"), DataValidation::LessThanOrEqual}, + {QStringLiteral("greaterThan"), DataValidation::GreaterThan}, + {QStringLiteral("greaterThanOrEqual"), DataValidation::GreaterThanOrEqual} + }; + + static const QMap esMap = { + {QStringLiteral("stop"), DataValidation::Stop}, + {QStringLiteral("warning"), DataValidation::Warning}, + {QStringLiteral("information"), DataValidation::Information} + }; + + DataValidation validation; + QXmlStreamAttributes attrs = reader.attributes(); + + QString sqref = attrs.value(QLatin1String("sqref")).toString(); + const auto sqrefParts = sqref.split(QLatin1Char(' ')); + for (const QString &range : sqrefParts) + validation.addRange(range); + + if (attrs.hasAttribute(QLatin1String("type"))) { + QString t = attrs.value(QLatin1String("type")).toString(); + auto it = typeMap.constFind(t); + validation.setValidationType(it != typeMap.constEnd() ? it.value() : DataValidation::None); + } + if (attrs.hasAttribute(QLatin1String("errorStyle"))) { + QString es = attrs.value(QLatin1String("errorStyle")).toString(); + auto it = esMap.constFind(es); + validation.setErrorStyle(it != esMap.constEnd() ? it.value() : DataValidation::Stop); + } + if (attrs.hasAttribute(QLatin1String("operator"))) { + QString op = attrs.value(QLatin1String("operator")).toString(); + auto it = opMap.constFind(op); + validation.setValidationOperator(it != opMap.constEnd() ? it.value() : DataValidation::Between); + } + if (attrs.hasAttribute(QLatin1String("allowBlank"))) { + validation.setAllowBlank(true); + } else { + validation.setAllowBlank(false); + } + if (attrs.hasAttribute(QLatin1String("showInputMessage"))) { + validation.setPromptMessageVisible(true); + } else { + validation.setPromptMessageVisible(false); + } + if (attrs.hasAttribute(QLatin1String("showErrorMessage"))) { + validation.setErrorMessageVisible(true); + } else { + validation.setErrorMessageVisible(false); + } + + QString et = attrs.value(QLatin1String("errorTitle")).toString(); + QString e = attrs.value(QLatin1String("error")).toString(); + if (!e.isEmpty() || !et.isEmpty()) + validation.setErrorMessage(e, et); + + QString pt = attrs.value(QLatin1String("promptTitle")).toString(); + QString p = attrs.value(QLatin1String("prompt")).toString(); + if (!p.isEmpty() || !pt.isEmpty()) + validation.setPromptMessage(p, pt); + + //find the end + while(!(reader.name() == QLatin1String("dataValidation") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("formula1")) { + validation.setFormula1(reader.readElementText()); + } else if (reader.name() == QLatin1String("formula2")) { + validation.setFormula2(reader.readElementText()); + } + } + } + return validation; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxdatetype.cpp b/src/QXlsx/xlsxdatetype.cpp new file mode 100644 index 0000000..6486b6a --- /dev/null +++ b/src/QXlsx/xlsxdatetype.cpp @@ -0,0 +1,85 @@ +// xlsxdatetype.cpp + +#include + +#include "xlsxglobal.h" +#include "xlsxutility_p.h" +#include "xlsxdatetype.h" + +QT_BEGIN_NAMESPACE_XLSX + +DateType::DateType() +{ +} + +/* +DateType::DateType(bool is1904) +{ + isSet = false; +} + +DateType::DateType(double d, bool is1904) +{ + // TODO: check date + + // int iVaue = (int) d; + // double surplus = d - double(iVaue); + + dValue = d; + is1904Type = is1904; + isSet = true; +} + +DateType::DateType(QDateTime qdt, bool is1904) +{ + double ret = datetimeToNumber( qdt, is1904 ); + dValue = ret; + is1904Type = is1904; + isSet = true; +} + +DateType::DateType(QDate qd, bool is1904) +{ + + is1904Type = is1904; + isSet = true; +} + +DateType::DateType(QTime qt, bool is1904) +{ + double ret = timeToNumber( qt ); + dValue = ret; + is1904Type = is1904; + isSet = true; +} + +// enum currentDateType { DateAndTimeType, OnlyDateType, OnlyTimeType }; + +DateType::currentDateType DateType::getType() +{ + +} + +bool DateType::getValue(QDateTime* pQdt) +{ + +} + + +bool DateType::getValue(QDate* pQd) +{ + +} + +bool DateType::getValue(QTime* pQt) +{ + +} + +bool DateType::getValue(double* pD) +{ + +} +*/ + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxdocpropsapp.cpp b/src/QXlsx/xlsxdocpropsapp.cpp new file mode 100644 index 0000000..01d40a1 --- /dev/null +++ b/src/QXlsx/xlsxdocpropsapp.cpp @@ -0,0 +1,138 @@ +// xlsxdocpropsapp.cpp + +#include "xlsxdocpropsapp_p.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE_XLSX + +DocPropsApp::DocPropsApp(CreateFlag flag) + :AbstractOOXmlFile(flag) +{ +} + +void DocPropsApp::addPartTitle(const QString &title) +{ + m_titlesOfPartsList.append(title); +} + +void DocPropsApp::addHeadingPair(const QString &name, int value) +{ + m_headingPairsList.append({ name, value }); +} + +bool DocPropsApp::setProperty(const QString &name, const QString &value) +{ + static const QStringList validKeys = { + QStringLiteral("manager"), QStringLiteral("company") + }; + + if (!validKeys.contains(name)) + return false; + + if (value.isEmpty()) + m_properties.remove(name); + else + m_properties[name] = value; + + return true; +} + +QString DocPropsApp::property(const QString &name) const +{ + auto it = m_properties.constFind(name); + if (it != m_properties.constEnd()) + return it.value(); + + return QString(); +} + +QStringList DocPropsApp::propertyNames() const +{ + return m_properties.keys(); +} + +void DocPropsApp::saveToXmlFile(QIODevice *device) const +{ + QXmlStreamWriter writer(device); + QString vt = QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("Properties")); + writer.writeDefaultNamespace(QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/extended-properties")); + writer.writeNamespace(vt, QStringLiteral("vt")); + writer.writeTextElement(QStringLiteral("Application"), QStringLiteral("Microsoft Excel")); + writer.writeTextElement(QStringLiteral("DocSecurity"), QStringLiteral("0")); + writer.writeTextElement(QStringLiteral("ScaleCrop"), QStringLiteral("false")); + + writer.writeStartElement(QStringLiteral("HeadingPairs")); + writer.writeStartElement(vt, QStringLiteral("vector")); + writer.writeAttribute(QStringLiteral("size"), QString::number(m_headingPairsList.size()*2)); + writer.writeAttribute(QStringLiteral("baseType"), QStringLiteral("variant")); + + for (const auto &pair : m_headingPairsList) { + writer.writeStartElement(vt, QStringLiteral("variant")); + writer.writeTextElement(vt, QStringLiteral("lpstr"), pair.first); + writer.writeEndElement(); //vt:variant + writer.writeStartElement(vt, QStringLiteral("variant")); + writer.writeTextElement(vt, QStringLiteral("i4"), QString::number(pair.second)); + writer.writeEndElement(); //vt:variant + } + writer.writeEndElement();//vt:vector + writer.writeEndElement();//HeadingPairs + + writer.writeStartElement(QStringLiteral("TitlesOfParts")); + writer.writeStartElement(vt, QStringLiteral("vector")); + writer.writeAttribute(QStringLiteral("size"), QString::number(m_titlesOfPartsList.size())); + writer.writeAttribute(QStringLiteral("baseType"), QStringLiteral("lpstr")); + for (const QString &title : m_titlesOfPartsList) + writer.writeTextElement(vt, QStringLiteral("lpstr"), title); + writer.writeEndElement();//vt:vector + writer.writeEndElement();//TitlesOfParts + + auto it = m_properties.constFind(QStringLiteral("manager")); + if (it != m_properties.constEnd()) + writer.writeTextElement(QStringLiteral("Manager"), it.value()); + //Not like "manager", "company" always exists for Excel generated file. + + it = m_properties.constFind(QStringLiteral("company")); + writer.writeTextElement(QStringLiteral("Company"), it != m_properties.constEnd() ? it.value() : QString()); + writer.writeTextElement(QStringLiteral("LinksUpToDate"), QStringLiteral("false")); + writer.writeTextElement(QStringLiteral("SharedDoc"), QStringLiteral("false")); + writer.writeTextElement(QStringLiteral("HyperlinksChanged"), QStringLiteral("false")); + writer.writeTextElement(QStringLiteral("AppVersion"), QStringLiteral("12.0000")); + + writer.writeEndElement(); //Properties + writer.writeEndDocument(); +} + +bool DocPropsApp::loadFromXmlFile(QIODevice *device) +{ + QXmlStreamReader reader(device); + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("Properties")) + continue; + + if (reader.name() == QStringLiteral("Manager")) { + setProperty(QStringLiteral("manager"), reader.readElementText()); + } else if (reader.name() == QStringLiteral("Company")) { + setProperty(QStringLiteral("company"), reader.readElementText()); + } + } + + if (reader.hasError()) { + qDebug("Error when read doc props app file."); + } + } + return true; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxdocpropscore.cpp b/src/QXlsx/xlsxdocpropscore.cpp new file mode 100644 index 0000000..b11c8b6 --- /dev/null +++ b/src/QXlsx/xlsxdocpropscore.cpp @@ -0,0 +1,176 @@ +// xlsxdocpropscore.cpp + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xlsxdocpropscore_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +DocPropsCore::DocPropsCore(CreateFlag flag) + :AbstractOOXmlFile(flag) +{ +} + +bool DocPropsCore::setProperty(const QString &name, const QString &value) +{ + static const QStringList validKeys = { + QStringLiteral("title"), QStringLiteral("subject"), + QStringLiteral("keywords"), QStringLiteral("description"), + QStringLiteral("category"), QStringLiteral("status"), + QStringLiteral("created"), QStringLiteral("creator") + }; + + if (!validKeys.contains(name)) + return false; + + if (value.isEmpty()) + m_properties.remove(name); + else + m_properties[name] = value; + + return true; +} + +QString DocPropsCore::property(const QString &name) const +{ + auto it = m_properties.constFind(name); + if (it != m_properties.constEnd()) + return it.value(); + + return QString(); +} + +QStringList DocPropsCore::propertyNames() const +{ + return m_properties.keys(); +} + +void DocPropsCore::saveToXmlFile(QIODevice *device) const +{ + QXmlStreamWriter writer(device); + const QString cp = QStringLiteral("http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); + const QString dc = QStringLiteral("http://purl.org/dc/elements/1.1/"); + const QString dcterms = QStringLiteral("http://purl.org/dc/terms/"); + const QString dcmitype = QStringLiteral("http://purl.org/dc/dcmitype/"); + const QString xsi = QStringLiteral("http://www.w3.org/2001/XMLSchema-instance"); + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("cp:coreProperties")); + writer.writeNamespace(cp, QStringLiteral("cp")); + writer.writeNamespace(dc, QStringLiteral("dc")); + writer.writeNamespace(dcterms, QStringLiteral("dcterms")); + writer.writeNamespace(dcmitype, QStringLiteral("dcmitype")); + writer.writeNamespace(xsi, QStringLiteral("xsi")); + + auto it = m_properties.constFind(QStringLiteral("title")); + if (it != m_properties.constEnd()) + writer.writeTextElement(dc, QStringLiteral("title"), it.value()); + + it = m_properties.constFind(QStringLiteral("subject")); + if (it != m_properties.constEnd()) + writer.writeTextElement(dc, QStringLiteral("subject"), it.value()); + + it = m_properties.constFind(QStringLiteral("creator")); + writer.writeTextElement(dc, QStringLiteral("creator"), it != m_properties.constEnd() ? it.value() : QStringLiteral("Qt Xlsx Library")); + + it = m_properties.constFind(QStringLiteral("keywords")); + if (it != m_properties.constEnd()) + writer.writeTextElement(cp, QStringLiteral("keywords"), it.value()); + + it = m_properties.constFind(QStringLiteral("description")); + if (it != m_properties.constEnd()) + writer.writeTextElement(dc, QStringLiteral("description"), it.value()); + + it = m_properties.constFind(QStringLiteral("creator")); + writer.writeTextElement(cp, QStringLiteral("lastModifiedBy"), it != m_properties.constEnd() ? it.value() : QStringLiteral("Qt Xlsx Library")); + + writer.writeStartElement(dcterms, QStringLiteral("created")); + writer.writeAttribute(xsi, QStringLiteral("type"), QStringLiteral("dcterms:W3CDTF")); + it = m_properties.constFind(QStringLiteral("created")); + writer.writeCharacters(it != m_properties.constEnd() ? it.value() : QDateTime::currentDateTime().toString(Qt::ISODate)); + writer.writeEndElement();//dcterms:created + + writer.writeStartElement(dcterms, QStringLiteral("modified")); + writer.writeAttribute(xsi, QStringLiteral("type"), QStringLiteral("dcterms:W3CDTF")); + writer.writeCharacters(QDateTime::currentDateTime().toString(Qt::ISODate)); + writer.writeEndElement();//dcterms:created + + it = m_properties.constFind(QStringLiteral("category")); + if (it != m_properties.constEnd()) + writer.writeTextElement(cp, QStringLiteral("category"), it.value()); + + it = m_properties.constFind(QStringLiteral("status")); + if (it != m_properties.constEnd()) + writer.writeTextElement(cp, QStringLiteral("contentStatus"), it.value()); + + writer.writeEndElement(); //cp:coreProperties + writer.writeEndDocument(); +} + +bool DocPropsCore::loadFromXmlFile(QIODevice *device) +{ + QXmlStreamReader reader(device); + + const QString cp = QStringLiteral("http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); + const QString dc = QStringLiteral("http://purl.org/dc/elements/1.1/"); + const QString dcterms = QStringLiteral("http://purl.org/dc/terms/"); + + while (!reader.atEnd()) + { + QXmlStreamReader::TokenType token = reader.readNext(); + + if (token == QXmlStreamReader::StartElement) + { + + const auto& nsUri = reader.namespaceUri(); + const auto& name = reader.name(); + + if (name == QStringLiteral("subject") && nsUri == dc) + { + setProperty(QStringLiteral("subject"), reader.readElementText()); + } + else if (name == QStringLiteral("title") && nsUri == dc) + { + setProperty(QStringLiteral("title"), reader.readElementText()); + } + else if (name == QStringLiteral("creator") && nsUri == dc) + { + setProperty(QStringLiteral("creator"), reader.readElementText()); + } + else if (name == QStringLiteral("description") && nsUri == dc) + { + setProperty(QStringLiteral("description"), reader.readElementText()); + } + else if (name == QStringLiteral("keywords") && nsUri == cp) + { + setProperty(QStringLiteral("keywords"), reader.readElementText()); + } + else if (name == QStringLiteral("created") && nsUri == dcterms) + { + setProperty(QStringLiteral("created"), reader.readElementText()); + } + else if (name == QStringLiteral("category") && nsUri == cp) + { + setProperty(QStringLiteral("category"), reader.readElementText()); + } + else if (name == QStringLiteral("contentStatus") && nsUri == cp) + { + setProperty(QStringLiteral("status"), reader.readElementText()); + } + } + + if (reader.hasError()) + { + qDebug() << "Error when read doc props core file." << reader.errorString(); + } + } + return true; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxdocument.cpp b/src/QXlsx/xlsxdocument.cpp new file mode 100644 index 0000000..cf6d898 --- /dev/null +++ b/src/QXlsx/xlsxdocument.cpp @@ -0,0 +1,1454 @@ +// xlsxdocument.cpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xlsxdocument.h" +#include "xlsxdocument_p.h" +#include "xlsxworkbook.h" +#include "xlsxworksheet.h" +#include "xlsxcontenttypes_p.h" +#include "xlsxrelationships_p.h" +#include "xlsxstyles_p.h" +#include "xlsxtheme_p.h" +#include "xlsxdocpropsapp_p.h" +#include "xlsxdocpropscore_p.h" +#include "xlsxsharedstrings_p.h" +#include "xlsxutility_p.h" +#include "xlsxworkbook_p.h" +#include "xlsxdrawing_p.h" +#include "xlsxmediafile_p.h" +#include "xlsxchart.h" +#include "xlsxzipreader_p.h" +#include "xlsxzipwriter_p.h" + +/* + From Wikipedia: The Open Packaging Conventions (OPC) is a + container-file technology initially created by Microsoft to store + a combination of XML and non-XML files that together form a single + entity such as an Open XML Paper Specification (OpenXPS) + document. http://en.wikipedia.org/wiki/Open_Packaging_Conventions. + + At its simplest an Excel XLSX file contains the following elements: + + ____ [Content_Types].xml + | + |____ docProps + | |____ app.xml + | |____ core.xml + | + |____ xl + | |____ workbook.xml + | |____ worksheets + | | |____ sheet1.xml + | | + | |____ styles.xml + | | + | |____ theme + | | |____ theme1.xml + | | + | |_____rels + | |____ workbook.xml.rels + | + |_____rels + |____ .rels + + The Packager class coordinates the classes that represent the + elements of the package and writes them into the XLSX file. +*/ + +QT_BEGIN_NAMESPACE_XLSX + +namespace xlsxDocumentCpp { + std::string copyTag(const std::string &sFrom, const std::string &sTo, const std::string &tag) { + const std::string tagToFindStart = "<" + tag; + const std::string tagToFindEnd = ""; + + // search all occurrences of tag in 'sFrom' + std::string sFromData = ""; + size_t startIndex = 0; + while (true) { + std::size_t startPos = sFrom.find(tagToFindStart, startIndex); + if (startPos != std::string::npos) { + std::size_t endPos = sFrom.find(tagToFindEnd, startPos); + std::string tagEndTmp = tagEnd; + if (endPos == std::string::npos) { // second try to find the ending, maybe it is "/>" + endPos = sFrom.find("/>", startPos); + tagEndTmp = "/>"; + } + if (endPos != std::string::npos) { + sFromData += sFrom.substr(startPos, endPos - startPos) + tagEndTmp; + startIndex = endPos + strlen(tagEndTmp.c_str()); + } + else { + break; + } + } + else { + break; + } + } + + std::string sOut = sTo; // copy 'sTo' in the output string + + if (!sFromData.empty()) { // tag found in 'from'? + // search all occurrences of tag in 'sOut' and delete them + int firstPosTag = -1; + while (true) { + std::size_t startPos = sOut.find(tagToFindStart); + if (startPos != std::string::npos) { + std::size_t endPos = sOut.find(tagToFindEnd); + std::string tagEndTmp = tagEnd; + if (endPos == std::string::npos) { // second try to find the ending, maybe it is "/>" + endPos = sOut.find("/>", startPos); + tagEndTmp = "/>"; + } + if (endPos != std::string::npos) { + if (firstPosTag < 0) + firstPosTag = startPos; + std::string stringBefore = sOut.substr(0, startPos); + endPos += strlen(tagEndTmp.c_str()); + std::string stringAfter = sOut.substr(endPos, strlen(sOut.c_str()) - endPos); + sOut = stringBefore + stringAfter; + } + else { + break; + } + } + else { + break; + } + } + + if (firstPosTag == -1) { + // tag not found in 'sTo' file + // try to find a default pos using standard tags + std::vector defaultPos{ "", "" }; + for (unsigned int i = 0; i < defaultPos.size(); ++i) { + std::size_t iDefaultPos = sOut.find(defaultPos[i]); + if (iDefaultPos != std::string::npos) { + firstPosTag = iDefaultPos; + break; + } + } + } + + // add the tag extracted from 'sFrom' in 'sOut' + // add in the position of the first tag found in 'sOut' ('firstPosTag') + if (firstPosTag >= 0) { + std::string stringBefore = sOut.substr(0, firstPosTag); + std::string stringAfter = sOut.substr(firstPosTag, strlen(sOut.c_str()) - firstPosTag); + sOut = stringBefore + sFromData + stringAfter; + } + } + + return sOut; + } +} + +DocumentPrivate::DocumentPrivate(Document *p) : + q_ptr(p), defaultPackageName(QStringLiteral("Book1.xlsx")), + isLoad(false) +{ +} + +void DocumentPrivate::init() +{ + if (!contentTypes) + contentTypes = std::make_shared(ContentTypes::F_NewFromScratch); + + if (workbook.isNull()) + workbook = QSharedPointer(new Workbook(Workbook::F_NewFromScratch)); +} + +bool DocumentPrivate::loadPackage(QIODevice *device) +{ + Q_Q(Document); + ZipReader zipReader(device); + QStringList filePaths = zipReader.filePaths(); + + //Load the Content_Types file + if (!filePaths.contains(QLatin1String("[Content_Types].xml"))) + return false; + contentTypes = std::make_shared(ContentTypes::F_LoadFromExists); + contentTypes->loadFromXmlData(zipReader.fileData(QStringLiteral("[Content_Types].xml"))); + + //Load root rels file + if (!filePaths.contains(QLatin1String("_rels/.rels"))) + return false; + Relationships rootRels; + rootRels.loadFromXmlData(zipReader.fileData(QStringLiteral("_rels/.rels"))); + + //load core property + QList rels_core = rootRels.packageRelationships(QStringLiteral("/metadata/core-properties")); + if (!rels_core.isEmpty()) { + //Get the core property file name if it exists. + //In normal case, this should be "docProps/core.xml" + QString docPropsCore_Name = rels_core[0].target; + + DocPropsCore props(DocPropsCore::F_LoadFromExists); + props.loadFromXmlData(zipReader.fileData(docPropsCore_Name)); + const auto propNames = props.propertyNames(); + for (const QString &name : propNames) + q->setDocumentProperty(name, props.property(name)); + } + + //load app property + QList rels_app = rootRels.documentRelationships(QStringLiteral("/extended-properties")); + if (!rels_app.isEmpty()) { + //Get the app property file name if it exists. + //In normal case, this should be "docProps/app.xml" + QString docPropsApp_Name = rels_app[0].target; + + DocPropsApp props(DocPropsApp::F_LoadFromExists); + props.loadFromXmlData(zipReader.fileData(docPropsApp_Name)); + const auto propNames = props.propertyNames(); + for (const QString &name : propNames) + q->setDocumentProperty(name, props.property(name)); + } + + //load workbook now, Get the workbook file path from the root rels file + //In normal case, this should be "xl/workbook.xml" + workbook = QSharedPointer(new Workbook(Workbook::F_LoadFromExists)); + QList rels_xl = rootRels.documentRelationships(QStringLiteral("/officeDocument")); + if (rels_xl.isEmpty()) + return false; + const QString xlworkbook_Path = rels_xl[0].target; + const auto parts = splitPath(xlworkbook_Path); + const QString xlworkbook_Dir = parts.first(); + const QString relFilePath = getRelFilePath(xlworkbook_Path); + + workbook->relationships()->loadFromXmlData( zipReader.fileData(relFilePath) ); + workbook->setFilePath(xlworkbook_Path); + workbook->loadFromXmlData(zipReader.fileData(xlworkbook_Path)); + + //load styles + QList rels_styles = workbook->relationships()->documentRelationships(QStringLiteral("/styles")); + if (!rels_styles.isEmpty()) { + //In normal case this should be styles.xml which in xl + QString name = rels_styles[0].target; + + // dev34 + QString path; + if ( xlworkbook_Dir == QLatin1String(".") ) // root + { + path = name; + } + else + { + path = xlworkbook_Dir + QLatin1String("/") + name; + } + + QSharedPointer styles (new Styles(Styles::F_LoadFromExists)); + styles->loadFromXmlData(zipReader.fileData(path)); + workbook->d_func()->styles = styles; + } + + //load sharedStrings + QList rels_sharedStrings = workbook->relationships()->documentRelationships(QStringLiteral("/sharedStrings")); + if (!rels_sharedStrings.isEmpty()) { + //In normal case this should be sharedStrings.xml which in xl + QString name = rels_sharedStrings[0].target; + QString path = xlworkbook_Dir + QLatin1String("/") + name; + workbook->d_func()->sharedStrings->loadFromXmlData(zipReader.fileData(path)); + } + + //load theme + QList rels_theme = workbook->relationships()->documentRelationships(QStringLiteral("/theme")); + if (!rels_theme.isEmpty()) { + //In normal case this should be theme/theme1.xml which in xl + QString name = rels_theme[0].target; + QString path = xlworkbook_Dir + QLatin1String("/") + name; + workbook->theme()->loadFromXmlData(zipReader.fileData(path)); + } + + //load sheets + for (int i=0; isheetCount(); ++i) { + AbstractSheet *sheet = workbook->sheet(i); + QString strFilePath = sheet->filePath(); + QString rel_path = getRelFilePath(strFilePath); + //If the .rel file exists, load it. + if (zipReader.filePaths().contains(rel_path)) + sheet->relationships()->loadFromXmlData(zipReader.fileData(rel_path)); + sheet->loadFromXmlData(zipReader.fileData(sheet->filePath())); + } + + //load external links + for (int i=0; id_func()->externalLinks.count(); ++i) { + SimpleOOXmlFile *link = workbook->d_func()->externalLinks[i].data(); + QString rel_path = getRelFilePath(link->filePath()); + //If the .rel file exists, load it. + if (zipReader.filePaths().contains(rel_path)) + link->relationships()->loadFromXmlData(zipReader.fileData(rel_path)); + link->loadFromXmlData(zipReader.fileData(link->filePath())); + } + + //load drawings + for (int i=0; idrawings().size(); ++i) { + Drawing *drawing = workbook->drawings()[i]; + QString rel_path = getRelFilePath(drawing->filePath()); + if (zipReader.filePaths().contains(rel_path)) + drawing->relationships()->loadFromXmlData(zipReader.fileData(rel_path)); + drawing->loadFromXmlData(zipReader.fileData(drawing->filePath())); + } + + //load charts + QList > chartFileToLoad = workbook->chartFiles(); + for (int i=0; i cf = chartFileToLoad[i]; + cf->loadFromXmlData(zipReader.fileData(cf->filePath())); + } + + //load media files + const auto mediaFileToLoad = workbook->mediaFiles(); + for (const auto &mf : mediaFileToLoad) { + const QString path = mf->fileName(); + const QString suffix = path.mid(path.lastIndexOf(QLatin1Char('.'))+1); + mf->set(zipReader.fileData(path), suffix); + } + + isLoad = true; + return true; +} + +bool DocumentPrivate::savePackage(QIODevice *device) const +{ + Q_Q(const Document); + + ZipWriter zipWriter(device); + if (zipWriter.error()) + return false; + + contentTypes->clearOverrides(); + + DocPropsApp docPropsApp(DocPropsApp::F_NewFromScratch); + DocPropsCore docPropsCore(DocPropsCore::F_NewFromScratch); + + // save worksheet xml files + QList > worksheets = workbook->getSheetsByTypes(AbstractSheet::ST_WorkSheet); + if (!worksheets.isEmpty()) + docPropsApp.addHeadingPair(QStringLiteral("Worksheets"), worksheets.size()); + + for (int i = 0 ; i < worksheets.size(); ++i) + { + QSharedPointer sheet = worksheets[i]; + contentTypes->addWorksheetName(QStringLiteral("sheet%1").arg(i+1)); + docPropsApp.addPartTitle(sheet->sheetName()); + + zipWriter.addFile(QStringLiteral("xl/worksheets/sheet%1.xml").arg(i+1), sheet->saveToXmlData()); + + Relationships *rel = sheet->relationships(); + if (!rel->isEmpty()) + zipWriter.addFile(QStringLiteral("xl/worksheets/_rels/sheet%1.xml.rels").arg(i+1), rel->saveToXmlData()); + } + + //save chartsheet xml files + QList > chartsheets = workbook->getSheetsByTypes(AbstractSheet::ST_ChartSheet); + if (!chartsheets.isEmpty()) + docPropsApp.addHeadingPair(QStringLiteral("Chartsheets"), chartsheets.size()); + for (int i=0; i sheet = chartsheets[i]; + contentTypes->addWorksheetName(QStringLiteral("sheet%1").arg(i+1)); + docPropsApp.addPartTitle(sheet->sheetName()); + + zipWriter.addFile(QStringLiteral("xl/chartsheets/sheet%1.xml").arg(i+1), sheet->saveToXmlData()); + Relationships *rel = sheet->relationships(); + if (!rel->isEmpty()) + zipWriter.addFile(QStringLiteral("xl/chartsheets/_rels/sheet%1.xml.rels").arg(i+1), rel->saveToXmlData()); + } + + // save external links xml files + for (int i=0; id_func()->externalLinks.count(); ++i) + { + SimpleOOXmlFile *link = workbook->d_func()->externalLinks[i].data(); + contentTypes->addExternalLinkName(QStringLiteral("externalLink%1").arg(i+1)); + + zipWriter.addFile(QStringLiteral("xl/externalLinks/externalLink%1.xml").arg(i+1), link->saveToXmlData()); + Relationships *rel = link->relationships(); + if (!rel->isEmpty()) + zipWriter.addFile(QStringLiteral("xl/externalLinks/_rels/externalLink%1.xml.rels").arg(i+1), rel->saveToXmlData()); + } + + // save workbook xml file + contentTypes->addWorkbook(); + zipWriter.addFile(QStringLiteral("xl/workbook.xml"), workbook->saveToXmlData()); + zipWriter.addFile(QStringLiteral("xl/_rels/workbook.xml.rels"), workbook->relationships()->saveToXmlData()); + + // save drawing xml files + for (int i=0; idrawings().size(); ++i) + { + contentTypes->addDrawingName(QStringLiteral("drawing%1").arg(i+1)); + + Drawing *drawing = workbook->drawings()[i]; + zipWriter.addFile(QStringLiteral("xl/drawings/drawing%1.xml").arg(i+1), drawing->saveToXmlData()); + if (!drawing->relationships()->isEmpty()) + zipWriter.addFile(QStringLiteral("xl/drawings/_rels/drawing%1.xml.rels").arg(i+1), drawing->relationships()->saveToXmlData()); + } + + // save docProps app/core xml file + const auto docPropNames = q->documentPropertyNames(); + for (const QString &name : docPropNames) { + docPropsApp.setProperty(name, q->documentProperty(name)); + docPropsCore.setProperty(name, q->documentProperty(name)); + } + contentTypes->addDocPropApp(); + contentTypes->addDocPropCore(); + zipWriter.addFile(QStringLiteral("docProps/app.xml"), docPropsApp.saveToXmlData()); + zipWriter.addFile(QStringLiteral("docProps/core.xml"), docPropsCore.saveToXmlData()); + + // save sharedStrings xml file + if (!workbook->sharedStrings()->isEmpty()) { + contentTypes->addSharedString(); + zipWriter.addFile(QStringLiteral("xl/sharedStrings.xml"), workbook->sharedStrings()->saveToXmlData()); + } + + // save calc chain [dev16] + contentTypes->addCalcChain(); + zipWriter.addFile(QStringLiteral("xl/calcChain.xml"), workbook->styles()->saveToXmlData()); + + // save styles xml file + contentTypes->addStyles(); + zipWriter.addFile(QStringLiteral("xl/styles.xml"), workbook->styles()->saveToXmlData()); + + // save theme xml file + contentTypes->addTheme(); + zipWriter.addFile(QStringLiteral("xl/theme/theme1.xml"), workbook->theme()->saveToXmlData()); + + // save chart xml files + for (int i=0; ichartFiles().size(); ++i) + { + contentTypes->addChartName(QStringLiteral("chart%1").arg(i+1)); + QSharedPointer cf = workbook->chartFiles()[i]; + zipWriter.addFile(QStringLiteral("xl/charts/chart%1.xml").arg(i+1), cf->saveToXmlData()); + } + + // save image files + const auto mfs = workbook->mediaFiles(); + for (int i=0; i < mfs.size(); ++i) + { + auto mf = mfs[i]; + if (!mf->mimeType().isEmpty()) + contentTypes->addDefault(mf->suffix(), mf->mimeType()); + + zipWriter.addFile(QStringLiteral("xl/media/image%1.%2").arg(i+1).arg(mf->suffix()), mf->contents()); + } + + // save root .rels xml file + Relationships rootrels; + rootrels.addDocumentRelationship(QStringLiteral("/officeDocument"), QStringLiteral("xl/workbook.xml")); + rootrels.addPackageRelationship(QStringLiteral("/metadata/core-properties"), QStringLiteral("docProps/core.xml")); + rootrels.addDocumentRelationship(QStringLiteral("/extended-properties"), QStringLiteral("docProps/app.xml")); + zipWriter.addFile(QStringLiteral("_rels/.rels"), rootrels.saveToXmlData()); + + // save content types xml file + zipWriter.addFile(QStringLiteral("[Content_Types].xml"), contentTypes->saveToXmlData()); + + zipWriter.close(); + return true; +} + +bool DocumentPrivate::copyStyle(const QString &from, const QString &to) +{ + // create a temp file because the zip writer cannot modify already existing zips + QTemporaryFile tempFile; + tempFile.open(); + tempFile.close(); + QString temFilePath = QFileInfo(tempFile).absoluteFilePath(); + + ZipWriter temporalZip(temFilePath); + + ZipReader zipReader(from); + QStringList filePaths = zipReader.filePaths(); + + QSharedPointer toReader = QSharedPointer(new ZipReader(to)); + + QStringList toFilePaths = toReader->filePaths(); + + // copy all files from "to" zip except those related to style + for (int i = 0; i < toFilePaths.size(); i++) { + if (toFilePaths[i].contains(QLatin1String("xl/styles"))) { + if (filePaths.contains(toFilePaths[i])) { // style file exist in 'from' as well + // modify style file + std::string fromData = QString::fromUtf8(zipReader.fileData(toFilePaths[i])).toStdString(); + std::string toData = QString::fromUtf8(toReader->fileData(toFilePaths[i])).toStdString(); + // copy default theme style from 'from' to 'to' + toData = xlsxDocumentCpp::copyTag(fromData, toData, "dxfs"); + temporalZip.addFile(toFilePaths.at(i), QString::fromUtf8(toData.c_str()).toUtf8()); + + continue; + } + } + + if (toFilePaths[i].contains(QLatin1String("xl/workbook"))) { + if (filePaths.contains(toFilePaths[i])) { // workbook file exist in 'from' as well + // modify workbook file + std::string fromData = QString::fromUtf8(zipReader.fileData(toFilePaths[i])).toStdString(); + std::string toData = QString::fromUtf8(toReader->fileData(toFilePaths[i])).toStdString(); + // copy default theme style from 'from' to 'to' + toData = xlsxDocumentCpp::copyTag(fromData, toData, "workbookPr"); + temporalZip.addFile(toFilePaths.at(i), QString::fromUtf8(toData.c_str()).toUtf8()); + continue; + } + } + + if (toFilePaths[i].contains(QLatin1String("xl/worksheets/sheet"))) { + if (filePaths.contains(toFilePaths[i])) { // sheet file exist in 'from' as well + // modify sheet file + std::string fromData = QString::fromUtf8(zipReader.fileData(toFilePaths[i])).toStdString(); + std::string toData = QString::fromUtf8(toReader->fileData(toFilePaths[i])).toStdString(); + // copy "conditionalFormatting" from 'from' to 'to' + toData = xlsxDocumentCpp::copyTag(fromData, toData, "conditionalFormatting"); + temporalZip.addFile(toFilePaths.at(i), QString::fromUtf8(toData.c_str()).toUtf8()); + continue; + } + } + + QByteArray data = toReader->fileData(toFilePaths.at(i)); + temporalZip.addFile(toFilePaths.at(i), data); + } + + temporalZip.close(); + + toReader.clear(); + + tempFile.close(); + + QFile::remove(to); + tempFile.copy(to); + + return true; +} + +/*! + \class Document + \inmodule QtXlsx + \brief The Document class provides a API that is used to handle the contents of .xlsx files. + +*/ + +/*! + * Creates a new empty xlsx document. + * The \a parent argument is passed to QObject's constructor. + */ +Document::Document(QObject *parent) : + QObject(parent), d_ptr(new DocumentPrivate(this)) +{ + d_ptr->init(); +} + +/*! + * \overload + * Try to open an existing xlsx document named \a name. + * The \a parent argument is passed to QObject's constructor. + */ +Document::Document(const QString &name, + QObject *parent) : + QObject(parent), + d_ptr(new DocumentPrivate(this)) +{ + d_ptr->packageName = name; + + if (QFile::exists(name)) + { + QFile xlsx(name); + if (xlsx.open(QFile::ReadOnly)) + { + if (! d_ptr->loadPackage(&xlsx)) + { + // NOTICE: failed to load package + } + } + } + + d_ptr->init(); +} + +/*! + * \overload + * Try to open an existing xlsx document from \a device. + * The \a parent argument is passed to QObject's constructor. + */ +Document::Document(QIODevice *device, QObject *parent) : + QObject(parent), d_ptr(new DocumentPrivate(this)) +{ + if (device && device->isReadable()) + { + if (!d_ptr->loadPackage(device)) + { + // NOTICE: failed to load package + } + } + d_ptr->init(); +} + +/*! + \overload + + Write \a value to cell \a row_column with the given \a format. + */ +bool Document::write(const CellReference &row_column, const QVariant &value, const Format &format) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->write(row_column, value, format); + return false; +} + +/*! + * Write \a value to cell (\a row, \a col) with the \a format. + * Returns true on success. + */ +bool Document::write(int row, int col, const QVariant &value, const Format &format) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->write(row, col, value, format); + return false; +} + +/*! + \overload + Returns the contents of the cell \a cell. + + \sa cellAt() +*/ +QVariant Document::read(const CellReference &cell) const +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->read(cell); + return QVariant(); +} + +/*! + Returns the contents of the cell (\a row, \a col). + + \sa cellAt() + */ +QVariant Document::read(int row, int col) const +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->read(row, col); + return QVariant(); +} + +/*! + * Insert an \a image to current active worksheet at the position \a row, \a column + * Returns ture if success. + */ +int Document::insertImage(int row, int column, const QImage &image) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->insertImage(row, column, image); + + return 0; +} + +bool Document::getImage(int imageIndex, QImage& img) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->getImage(imageIndex, img); + + return false; +} + +bool Document::getImage(int row, int col, QImage &img) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->getImage(row, col, img); + + return false; +} + +uint Document::getImageCount() +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->getImageCount(); + + return 0; +} + + +/*! + * Creates an chart with the given \a size and insert it to the current + * active worksheet at the position \a row, \a col. + * The chart will be returned. + */ +Chart *Document::insertChart(int row, int col, const QSize &size) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->insertChart(row, col, size); + return 0; +} + +/*! + Merge a \a range of cells. The first cell should contain the data and the others should + be blank. All cells will be applied the same style if a valid \a format is given. + Returns true on success. + + \note All cells except the top-left one will be cleared. + */ +bool Document::mergeCells(const CellRange &range, const Format &format) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->mergeCells(range, format); + return false; +} + +/*! + Unmerge the cells in the \a range. + Returns true on success. +*/ +bool Document::unmergeCells(const CellRange &range) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->unmergeCells(range); + return false; +} + +/*! + Sets width in characters of columns with the given \a range and \a width. + Returns true on success. + */ +bool Document::setColumnWidth(const CellRange &range, double width) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->setColumnWidth(range, width); + return false; +} + +/*! + Sets format property of columns with the gien \a range and \a format. + Returns true on success. + */ +bool Document::setColumnFormat(const CellRange &range, const Format &format) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->setColumnFormat(range, format); + return false; +} + +/*! + Sets hidden property of columns \a range to \a hidden. Columns are 1-indexed. + Hidden columns are not visible. + Returns true on success. + */ +bool Document::setColumnHidden(const CellRange &range, bool hidden) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->setColumnWidth(range, hidden); + return false; +} + +/*! + Sets width in characters \a column to \a width. Columns are 1-indexed. + Returns true on success. + */ +bool Document::setColumnWidth(int column, double width) +{ + return setColumnWidth(column,column,width); +} + +/*! + Sets format property \a column to \a format. Columns are 1-indexed. + Returns true on success. + */ +bool Document::setColumnFormat(int column, const Format &format) +{ + return setColumnFormat(column,column,format); +} + +/*! + Sets hidden property of a \a column. Columns are 1-indexed. + Returns true on success. + */ +bool Document::setColumnHidden(int column, bool hidden) +{ + return setColumnHidden(column,column,hidden); +} + +/*! + Sets width in characters for columns [\a colFirst, \a colLast]. Columns are 1-indexed. + Returns true on success. + */ +bool Document::setColumnWidth(int colFirst, int colLast, double width) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->setColumnWidth(colFirst, colLast, width); + return false; +} + +/*! + Sets format property of columns [\a colFirst, \a colLast] to \a format. + Columns are 1-indexed. + Returns true on success. + */ +bool Document::setColumnFormat(int colFirst, int colLast, const Format &format) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->setColumnFormat(colFirst, colLast, format); + return false; +} + + +/*! + Sets hidden property of columns [\a colFirst, \a colLast] to \a hidden. + Columns are 1-indexed. + Returns true on success. + */ +bool Document::setColumnHidden(int colFirst, int colLast, bool hidden) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->setColumnHidden(colFirst, colLast, hidden); + return false; +} + +/*! + Returns width of the \a column in characters of the normal font. + Columns are 1-indexed. + Returns true on success. + */ +double Document::columnWidth(int column) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->columnWidth(column); + return 0.0; +} + +/*! + Returns formatting of the \a column. Columns are 1-indexed. + */ +Format Document::columnFormat(int column) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->columnFormat(column); + return Format(); +} + +/*! + Returns true if \a column is hidden. Columns are 1-indexed. + */ +bool Document::isColumnHidden(int column) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->isColumnHidden(column); + return false; +} + +/*! + Sets the \a format of the \a row. + Rows are 1-indexed. + + Returns true if success. +*/ +bool Document::setRowFormat(int row, const Format &format) +{ + return setRowFormat(row,row, format); +} + +/*! + Sets the \a format of the rows including and between \a rowFirst and \a rowLast. + Rows are 1-indexed. + + Returns true if success. +*/ +bool Document::setRowFormat(int rowFirst, int rowLast, const Format &format) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->setRowFormat(rowFirst, rowLast, format); + return false; +} + +/*! + Sets the \a hidden property of the row \a row. + Rows are 1-indexed. If hidden is true rows will not be visible. + + Returns true if success. +*/ +bool Document::setRowHidden(int row, bool hidden) +{ + return setRowHidden(row,row,hidden); +} + +/*! + Sets the \a hidden property of the rows including and between \a rowFirst and \a rowLast. + Rows are 1-indexed. If hidden is true rows will not be visible. + + Returns true if success. +*/ +bool Document::setRowHidden(int rowFirst, int rowLast, bool hidden) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->setRowHidden(rowFirst, rowLast, hidden); + return false; +} + +/*! + Sets the \a height of the row \a row. + Row height measured in point size. + Rows are 1-indexed. + + Returns true if success. +*/ +bool Document::setRowHeight(int row, double height) +{ + return setRowHeight(row,row,height); +} + +/*! + Sets the \a height of the rows including and between \a rowFirst and \a rowLast. + Row height measured in point size. + Rows are 1-indexed. + + Returns true if success. +*/ +bool Document::setRowHeight(int rowFirst, int rowLast, double height) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->setRowHeight(rowFirst, rowLast, height); + return false; +} + +/*! + Returns height of \a row in points. +*/ +double Document::rowHeight(int row) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->rowHeight(row); + return 0.0; +} + +/*! + Returns format of \a row. +*/ +Format Document::rowFormat(int row) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->rowFormat(row); + return Format(); +} + +/*! + Returns true if \a row is hidden. +*/ +bool Document::isRowHidden(int row) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->isRowHidden(row); + return false; +} + +/*! + Groups rows from \a rowFirst to \a rowLast with the given \a collapsed. + Returns false if error occurs. + */ +bool Document::groupRows(int rowFirst, int rowLast, bool collapsed) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->groupRows(rowFirst, rowLast, collapsed); + return false; +} + +/*! + Groups columns from \a colFirst to \a colLast with the given \a collapsed. + Returns false if error occurs. + */ +bool Document::groupColumns(int colFirst, int colLast, bool collapsed) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->groupColumns(colFirst, colLast, collapsed); + return false; +} + +/*! + * Add a data \a validation rule for current worksheet. Returns true if successful. + */ +bool Document::addDataValidation(const DataValidation &validation) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->addDataValidation(validation); + return false; +} + +/*! + * Add a conditional formatting \a cf for current worksheet. Returns true if successful. + */ +bool Document::addConditionalFormatting(const ConditionalFormatting &cf) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->addConditionalFormatting(cf); + return false; +} + +/*! + * \overload + * Returns the cell at the position \a pos. If there is no cell at + * the specified position, the function returns 0. + * + * \sa read() + */ +Cell *Document::cellAt(const CellReference &pos) const +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->cellAt(pos); + return 0; +} + +/*! + * Returns the cell at the given \a row and \a col. If there + * is no cell at the specified position, the function returns 0. + * + * \sa read() + */ +Cell *Document::cellAt(int row, int col) const +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->cellAt(row, col); + return 0; +} + +/*! + * \brief Create a defined name in the workbook with the given \a name, \a formula, \a comment + * and \a scope. + * + * \param name The defined name. + * \param formula The cell or range that the defined name refers to. + * \param scope The name of one worksheet, or empty which means golbal scope. + * \return Return false if the name invalid. + */ +bool Document::defineName(const QString &name, const QString &formula, const QString &comment, const QString &scope) +{ + Q_D(Document); + + return d->workbook->defineName(name, formula, comment, scope); +} + +/*! + Return the range that contains cell data. + */ +CellRange Document::dimension() const +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->dimension(); + return CellRange(); +} + +/*! + * Returns the value of the document's \a key property. + */ +QString Document::documentProperty(const QString &key) const +{ + Q_D(const Document); + auto it = d->documentProperties.constFind(key); + if (it != d->documentProperties.constEnd()) + return it.value(); + else + return QString(); +} + +/*! + Set the document properties such as Title, Author etc. + + The method can be used to set the document properties of the Excel + file created by Qt Xlsx. These properties are visible when you use the + Office Button -> Prepare -> Properties option in Excel and are also + available to external applications that read or index windows files. + + The \a property \a key that can be set are: + + \list + \li title + \li subject + \li creator + \li manager + \li company + \li category + \li keywords + \li description + \li status + \endlist +*/ +void Document::setDocumentProperty(const QString &key, const QString &property) +{ + Q_D(Document); + d->documentProperties[key] = property; +} + +/*! + * Returns the names of all properties that were addedusing setDocumentProperty(). + */ +QStringList Document::documentPropertyNames() const +{ + Q_D(const Document); + return d->documentProperties.keys(); +} + +/*! + * Return the internal Workbook object. + */ +Workbook *Document::workbook() const +{ + Q_D(const Document); + return d->workbook.data(); +} + +/*! + * Returns the sheet object named \a sheetName. + */ +AbstractSheet *Document::sheet(const QString &sheetName) const +{ + Q_D(const Document); + return d->workbook->sheet(sheetNames().indexOf(sheetName)); +} + +/*! + * Creates and append an sheet with the given \a name and \a type. + * Return true if success. + */ +bool Document::addSheet(const QString &name, AbstractSheet::SheetType type) +{ + Q_D(Document); + return d->workbook->addSheet(name, type); +} + +/*! + * Creates and inserts an document with the given \a name and \a type at the \a index. + * Returns false if the \a name already used. + */ +bool Document::insertSheet(int index, const QString &name, AbstractSheet::SheetType type) +{ + Q_D(Document); + return d->workbook->insertSheet(index, name, type); +} + +/*! + Rename the worksheet from \a oldName to \a newName. + Returns true if the success. + */ +bool Document::renameSheet(const QString &oldName, const QString &newName) +{ + Q_D(Document); + if (oldName == newName) + return false; + return d->workbook->renameSheet(sheetNames().indexOf(oldName), newName); +} + +/*! + Make a copy of the worksheet \a srcName with the new name \a distName. + Returns true if the success. + */ +bool Document::copySheet(const QString &srcName, const QString &distName) +{ + Q_D(Document); + if (srcName == distName) + return false; + return d->workbook->copySheet(sheetNames().indexOf(srcName), distName); +} + +/*! + Move the worksheet \a srcName to the new pos \a distIndex. + Returns true if the success. + */ +bool Document::moveSheet(const QString &srcName, int distIndex) +{ + Q_D(Document); + return d->workbook->moveSheet(sheetNames().indexOf(srcName), distIndex); +} + +/*! + Delete the worksheet \a name. + Returns true if current sheet was deleted successfully. + */ +bool Document::deleteSheet(const QString &name) +{ + Q_D(Document); + return d->workbook->deleteSheet(sheetNames().indexOf(name)); +} + +/*! + * \brief Return pointer of current sheet. + */ +AbstractSheet *Document::currentSheet() const +{ + Q_D(const Document); + + return d->workbook->activeSheet(); +} + +/*! + * \brief Return pointer of current worksheet. + * If the type of sheet is not AbstractSheet::ST_WorkSheet, then 0 will be returned. + */ +Worksheet *Document::currentWorksheet() const +{ + AbstractSheet *st = currentSheet(); + if (st && st->sheetType() == AbstractSheet::ST_WorkSheet) + return static_cast(st); + else + return 0; +} + +/*! + * \brief Set worksheet named \a name to be active sheet. + * Returns true if success. + */ +bool Document::selectSheet(const QString &name) +{ + Q_D(Document); + return d->workbook->setActiveSheet(sheetNames().indexOf(name)); +} + +/*! + * \brief Set worksheet whose index is \a index to be active sheet. + * Returns true if success. + */ +bool Document::selectSheet(int index) +{ + Q_D(Document); + return d->workbook->setActiveSheet(index); +} + +/*! + * Returns the names of worksheets contained in current document. + */ +QStringList Document::sheetNames() const +{ + Q_D(const Document); + return d->workbook->worksheetNames(); +} + +/*! + * Save current document to the filesystem. If no name specified when + * the document constructed, a default name "book1.xlsx" will be used. + * Returns true if saved successfully. + */ +bool Document::save() const +{ + Q_D(const Document); + QString name = d->packageName.isEmpty() ? d->defaultPackageName : d->packageName; + + return saveAs(name); +} + +/*! + * Saves the document to the file with the given \a name. + * Returns true if saved successfully. + */ +bool Document::saveAs(const QString &name) const +{ + QFile file(name); + if (file.open(QIODevice::WriteOnly)) + return saveAs(&file); + return false; +} + +/*! + * \overload + * This function writes a document to the given \a device. + * + * \warning The \a device will be closed when this function returned. + */ +bool Document::saveAs(QIODevice *device) const +{ + Q_D(const Document); + return d->savePackage(device); +} + +bool Document::isLoadPackage() const +{ + Q_D(const Document); + return d->isLoad; +} + +bool Document::load() const +{ + return isLoadPackage(); +} + +bool Document::copyStyle(const QString &from, const QString &to) { + return DocumentPrivate::copyStyle(from, to); +} + +/*! + * Destroys the document and cleans up. + */ +Document::~Document() +{ + delete d_ptr; +} + +// add by liufeijin 20181025 {{ +bool Document::changeimage(int filenoinmidea, QString newfile) +{ + Q_D(const Document); + + QImage newpic(newfile); + + auto mediaFileToLoad = d->workbook->mediaFiles(); + const auto mf = mediaFileToLoad[filenoinmidea]; + + const QString suffix = newfile.mid(newfile.lastIndexOf(QLatin1Char('.'))+1); + QString mimetypemy; + if(QString::compare(QLatin1String("jpg"), suffix, Qt::CaseInsensitive)==0) + mimetypemy=QStringLiteral("image/jpeg"); + if(QString::compare(QLatin1String("bmp"), suffix, Qt::CaseInsensitive)==0) + mimetypemy=QStringLiteral("image/bmp"); + if(QString::compare(QLatin1String("gif"), suffix, Qt::CaseInsensitive)==0) + mimetypemy=QStringLiteral("image/gif"); + if(QString::compare(QLatin1String("png"), suffix, Qt::CaseInsensitive)==0) + mimetypemy=QStringLiteral("image/png"); + + QByteArray ba; + QBuffer buffer(&ba); + buffer.setBuffer(&ba); + buffer.open(QIODevice::WriteOnly); + newpic.save(&buffer,suffix.toLocal8Bit().data()); + + mf->set(ba,suffix,mimetypemy); + mediaFileToLoad[filenoinmidea]=mf; + + return true; +} +// liufeijin }} + + +/*! + Returns map of columns with there maximal width + */ +QMap Document::getMaximalColumnWidth(int firstRow, int lastRow) +{ + const int defaultPixelSize = 11; //Default font pixel size of excel? + int maxRows = -1; + int maxCols = -1; + QMap colWidth; + if (!currentWorksheet()) return colWidth; + QVector cellLocation = currentWorksheet()->getFullCells(&maxRows, &maxCols); + + for(int i=0; i < cellLocation.size(); i++) + { + int col = cellLocation.at(i).col; + int row = cellLocation.at(i).row; + int fs = cellLocation.at(i).cell->format().fontSize(); + if( fs <= 0) + { + fs = defaultPixelSize; + } + +// QString str = cellLocation.at(i).cell.data()->value().toString(); + QString str = read(row, col).toString(); + + double w = str.length() * double(fs) / defaultPixelSize + 1; // width not perfect, but works reasonably well + + if( (row >= firstRow) && (row <= lastRow)) + { + if( w > colWidth.value(col)) + { + colWidth.insert(col, int(w)); + } + } + } + + return colWidth; +} + + +/*! + Auto ets width in characters of columns with the given \a range. + Returns true on success. + */ +bool Document::autosizeColumnWidth(const CellRange &range) +{ + bool erg = false; + + if( !range.isValid()) + { + return false; + } + + const QMap colWidth = getMaximalColumnWidth(range.firstRow(), range.lastRow()); + auto it = colWidth.constBegin(); + while (it != colWidth.constEnd()) { + if( (it.key() >= range.firstColumn()) && (it.key() <= range.lastColumn()) ) + { + erg |= setColumnWidth(it.key(), it.value()); + } + ++it; + } + + return erg; +} + + +/*! + Auto sets width in characters \a column . Columns are 1-indexed. + Returns true on success. + */ +bool Document::autosizeColumnWidth(int column) +{ + bool erg = false; + + const QMap colWidth = getMaximalColumnWidth(); + auto it = colWidth.constBegin(); + while (it != colWidth.constEnd()) { + if( it.key() == column) + { + erg |= setColumnWidth(it.key(), it.value()); + } + ++it; + } + + return erg; +} + + +/*! + Auto sets width in characters for columns [\a colFirst, \a colLast]. Columns are 1-indexed. + Returns true on success. + */ +bool Document::autosizeColumnWidth(int colFirst, int colLast) +{ + Q_UNUSED(colFirst) + Q_UNUSED(colLast) + bool erg = false; + + const QMap colWidth = getMaximalColumnWidth(); + auto it = colWidth.constBegin(); + while (it != colWidth.constEnd()) { + if( (it.key() >= colFirst) && (it.key() <= colLast) ) + { + erg |= setColumnWidth(it.key(), it.value()); + } + ++it; + } + + return erg; +} + + +/*! + Auto sets width in characters for all columns. + Returns true on success. + */ +bool Document::autosizeColumnWidth(void) +{ + bool erg = false; + + const QMap colWidth = getMaximalColumnWidth(); + auto it = colWidth.constBegin(); + while (it != colWidth.constEnd()) { + erg |= setColumnWidth(it.key(), it.value()); + ++it; + } + + return erg; +} + + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxdrawing.cpp b/src/QXlsx/xlsxdrawing.cpp new file mode 100644 index 0000000..2675310 --- /dev/null +++ b/src/QXlsx/xlsxdrawing.cpp @@ -0,0 +1,84 @@ +// xlsxdrawing.cpp + +#include +#include +#include +#include + +#include "xlsxdrawing_p.h" +#include "xlsxdrawinganchor_p.h" +#include "xlsxabstractsheet.h" + +QT_BEGIN_NAMESPACE_XLSX + +Drawing::Drawing(AbstractSheet *sheet, CreateFlag flag) + :AbstractOOXmlFile(flag), sheet(sheet) +{ + workbook = sheet->workbook(); +} + +Drawing::~Drawing() +{ + qDeleteAll(anchors); +} + +void Drawing::saveToXmlFile(QIODevice *device) const +{ + relationships()->clear(); + + QXmlStreamWriter writer(device); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("xdr:wsDr")); + writer.writeAttribute(QStringLiteral("xmlns:xdr"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing")); + writer.writeAttribute(QStringLiteral("xmlns:a"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main")); + + for (DrawingAnchor *anchor : anchors) + anchor->saveToXml(writer); + + writer.writeEndElement();//xdr:wsDr + writer.writeEndDocument(); +} + +// check point +bool Drawing::loadFromXmlFile(QIODevice *device) +{ + /* + + + + + + + + */ + + QXmlStreamReader reader(device); + + while (!reader.atEnd()) + { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + if (reader.name() == QLatin1String("absoluteAnchor")) // CT_AbsoluteAnchor + { + DrawingAbsoluteAnchor * anchor = new DrawingAbsoluteAnchor(this); + anchor->loadFromXml(reader); + } + else if (reader.name() == QLatin1String("oneCellAnchor")) // CT_OneCellAnchor + { + DrawingOneCellAnchor * anchor = new DrawingOneCellAnchor(this); + anchor->loadFromXml(reader); + } + else if (reader.name() == QLatin1String("twoCellAnchor")) // CT_TwoCellAnchor + { + DrawingTwoCellAnchor * anchor = new DrawingTwoCellAnchor(this); + anchor->loadFromXml(reader); + } + } + } + + return true; +} + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxdrawinganchor.cpp b/src/QXlsx/xlsxdrawinganchor.cpp new file mode 100644 index 0000000..89d27eb --- /dev/null +++ b/src/QXlsx/xlsxdrawinganchor.cpp @@ -0,0 +1,1203 @@ +// xlsxdrawinganchor.cpp + +#include +#include +#include +#include +#include +#include + +#include "xlsxdrawinganchor_p.h" +#include "xlsxdrawing_p.h" +#include "xlsxmediafile_p.h" +#include "xlsxchart.h" +#include "xlsxworkbook.h" +#include "xlsxutility_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +/* + The vertices that define the position of a graphical object + within the worksheet in pixels. + + +------------+------------+ + | A | B | + +-----+------------+------------+ + | |(x1,y1) | | + | 1 |(A1)._______|______ | + | | | | | + | | | | | + +-----+----| OBJECT |-----+ + | | | | | + | 2 | |______________. | + | | | (B2)| + | | | (x2,y2)| + +---- +------------+------------+ + + Example of an object that covers some of the area from cell A1 to B2. + + Based on the width and height of the object we need to calculate 8 vars: + + col_start, row_start, col_end, row_end, x1, y1, x2, y2. + + We also calculate the absolute x and y position of the top left vertex of + the object. This is required for images. + + The width and height of the cells that the object occupies can be + variable and have to be taken into account. +*/ + +//anchor + +DrawingAnchor::DrawingAnchor(Drawing *drawing, ObjectType objectType) + :m_drawing(drawing), m_objectType(objectType) +{ + m_drawing->anchors.append(this); + m_id = m_drawing->anchors.size();//must be unique in one drawing{x}.xml file. +} + +DrawingAnchor::~DrawingAnchor() +{ + +} + +void DrawingAnchor::setObjectPicture(const QImage &img) +{ + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + img.save(&buffer, "PNG"); + + m_pictureFile = std::make_shared(ba, QStringLiteral("png"), QStringLiteral("image/png")); + m_drawing->workbook->addMediaFile(m_pictureFile); + + m_objectType = Picture; +} + +bool DrawingAnchor::getObjectPicture(QImage &img) +{ + if ( m_pictureFile == nullptr ) + return false; + + bool ret = img.loadFromData( m_pictureFile->contents() ); + return ret; +} + +//{{ liufeijin +void DrawingAnchor::setObjectShape(const QImage &img) +{ + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + img.save(&buffer, "PNG"); + + m_pictureFile = std::make_shared(ba, QStringLiteral("png"), QStringLiteral("image/png")); + m_drawing->workbook->addMediaFile(m_pictureFile); + + m_objectType = Shape; +} +//}} + +void DrawingAnchor::setObjectGraphicFrame(QSharedPointer chart) +{ + m_chartFile = chart; + m_drawing->workbook->addChartFile(chart); + + m_objectType = GraphicFrame; +} + +int DrawingAnchor::row() const +{ + return -1; +} + +int DrawingAnchor::col() const +{ + return -1; +} + +QPoint DrawingAnchor::loadXmlPos(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("pos")); + + QPoint pos; + QXmlStreamAttributes attrs = reader.attributes(); + pos.setX(attrs.value(QLatin1String("x")).toInt()); + pos.setY(attrs.value(QLatin1String("y")).toInt()); + return pos; +} + +QSize DrawingAnchor::loadXmlExt(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("ext")); + + QSize size; + QXmlStreamAttributes attrs = reader.attributes(); + size.setWidth(attrs.value(QLatin1String("cx")).toInt()); + size.setHeight(attrs.value(QLatin1String("cy")).toInt()); + return size; +} + +XlsxMarker DrawingAnchor::loadXmlMarker(QXmlStreamReader &reader, const QString &node) +{ + Q_ASSERT(reader.name() == node); + + int col = 0; + int colOffset = 0; + int row = 0; + int rowOffset = 0; + while (!reader.atEnd()) + { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + if (reader.name() == QLatin1String("col")) + { + col = reader.readElementText().toInt(); + } + else if (reader.name() == QLatin1String("colOff")) + { + colOffset = reader.readElementText().toInt(); + } + else if (reader.name() == QLatin1String("row")) + { + row = reader.readElementText().toInt(); + } + else if (reader.name() == QLatin1String("rowOff")) + { + rowOffset = reader.readElementText().toInt(); + } + } + else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == node) + { + break; + } + } + + return XlsxMarker(row, col, rowOffset, colOffset); +} + +void DrawingAnchor::loadXmlObject(QXmlStreamReader &reader) +{ + /* + + + + + + + + + + + + + */ + + if (reader.name() == QLatin1String("sp")) // + { + // Shape + m_objectType = Shape; + + //{{ liufeijin + sp_textlink = reader.attributes().value(QLatin1String("textlink")).toString(); + sp_macro = reader.attributes().value(QLatin1String("macro")).toString(); + //}} + + // + // + // + // + + loadXmlObjectShape(reader); // CT_Shape + } + else if (reader.name() == QLatin1String("grpSp")) // + { + //Group Shape + m_objectType = GroupShape; + loadXmlObjectGroupShape(reader); + } + else if (reader.name() == QLatin1String("graphicFrame")) // + { + //Graphic Frame + m_objectType = GraphicFrame; + loadXmlObjectGraphicFrame(reader); + } + else if (reader.name() == QLatin1String("cxnSp")) // + { + //Connection Shape + m_objectType = ConnectionShape; + + // {{ liufeijin + cxnSp_macro = reader.attributes().value(QLatin1String("macro")).toString(); + // }} + + loadXmlObjectConnectionShape(reader); + } + else if (reader.name() == QLatin1String("pic")) // + { + // Picture + m_objectType = Picture; + loadXmlObjectPicture(reader); + } + else if (reader.name() == QLatin1String("contentPart")) // + { + // contentPart + /// TODO: + } +} + +void DrawingAnchor::loadXmlObjectConnectionShape(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("cxnSp")); + bool hasoffext=false; + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("cNvPr")){ + xsp_cNvPR_name= reader.attributes().value(QLatin1String("name")).toString(); + xsp_cNvPR_id= reader.attributes().value(QLatin1String("id")).toString(); + }else if(reader.name() == QLatin1String("spPr")){ + xbwMode= reader.attributes().value(QLatin1String("bwMode")).toString(); + }else if(reader.name() == QLatin1String("xfrm")){ + cxnSp_filpV= reader.attributes().value(QLatin1String("flipV")).toString(); + }else if (reader.name() == QLatin1String("off")) { + posTA = loadXmlPos(reader); + hasoffext=true; + } else if (reader.name() == QLatin1String("ext")&&hasoffext) { + extTA = loadXmlExt(reader); + hasoffext=false; + }else if(reader.name() == QLatin1String("prstGeom")){ + xprstGeom_prst= reader.attributes().value(QLatin1String("prst")).toString().trimmed(); + }else if(reader.name() == QLatin1String("ln")){ + xIn_algn= reader.attributes().value(QLatin1String("algn")).toString().trimmed(); + xIn_cmpd= reader.attributes().value(QLatin1String("cmpd")).toString().trimmed(); + xIn_cap= reader.attributes().value(QLatin1String("cap")).toString().trimmed(); + xIn_w= reader.attributes().value(QLatin1String("w")).toString().trimmed(); + }else if(reader.name() == QLatin1String("headEnd")){ + x_headEnd_w= reader.attributes().value(QLatin1String("w")).toString().trimmed(); + x_headEnd_len= reader.attributes().value(QLatin1String("len")).toString().trimmed(); + x_headEnd_tyep= reader.attributes().value(QLatin1String("type")).toString().trimmed(); + }else if(reader.name() == QLatin1String("tailEnd")){ + x_tailEnd_w= reader.attributes().value(QLatin1String("w")).toString().trimmed(); + x_tailEnd_len= reader.attributes().value(QLatin1String("len")).toString().trimmed(); + x_tailEnd_tyep= reader.attributes().value(QLatin1String("type")).toString().trimmed(); + }else if(reader.name() == QLatin1String("lnRef")){ + Style_inref_idx= reader.attributes().value(QLatin1String("idx")).toString().trimmed(); + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if(reader.name() == QLatin1String("schemeClr")){ + Style_inref_val=reader.attributes().value(QLatin1String("val")).toString().trimmed(); + } + } + }else if(reader.name() == QLatin1String("fillRef")){ + style_fillref_idx= reader.attributes().value(QLatin1String("idx")).toString().trimmed(); + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if(reader.name() == QLatin1String("schemeClr")){ + style_fillref_val=reader.attributes().value(QLatin1String("val")).toString().trimmed(); + } + } + }else if(reader.name() == QLatin1String("effectRef")){ + style_effectref_idx= reader.attributes().value(QLatin1String("idx")).toString().trimmed(); + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if(reader.name() == QLatin1String("schemeClr")){ + style_effectref_val=reader.attributes().value(QLatin1String("val")).toString().trimmed(); + } + } + }else if(reader.name() == QLatin1String("fontRef")){ + style_forntref_idx= reader.attributes().value(QLatin1String("idx")).toString().trimmed(); + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if(reader.name() == QLatin1String("schemeClr")){ + style_forntref_val=reader.attributes().value(QLatin1String("val")).toString().trimmed(); + } + } + } + + } else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("cxnSp")) { + break; + } + } + + return; + +} + +void DrawingAnchor::loadXmlObjectGraphicFrame(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("graphicFrame")); + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("chart")) { + QString rId = reader.attributes().value(QLatin1String("r:id")).toString(); + QString name = m_drawing->relationships()->getRelationshipById(rId).target; + + const auto parts = splitPath(m_drawing->filePath()); + QString path = QDir::cleanPath(parts.first() + QLatin1String("/") + name); + + bool exist = false; + QList > cfs = m_drawing->workbook->chartFiles(); + for (int i=0; ifilePath() == path) { + //already exist + exist = true; + m_chartFile = cfs[i]; + } + } + if (!exist) { + m_chartFile = QSharedPointer (new Chart(m_drawing->sheet, Chart::F_LoadFromExists)); + m_chartFile->setFilePath(path); + m_drawing->workbook->addChartFile(m_chartFile); + } + } + } else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("graphicFrame")) { + break; + } + } + + return; +} + +void DrawingAnchor::loadXmlObjectGroupShape(QXmlStreamReader &reader) +{ + Q_UNUSED(reader) +} + +void DrawingAnchor::loadXmlObjectPicture(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("pic")); + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("blip")) { + QString rId = reader.attributes().value(QLatin1String("r:embed")).toString(); + QString name = m_drawing->relationships()->getRelationshipById(rId).target; + + const auto parts = splitPath(m_drawing->filePath()); + QString path = QDir::cleanPath(parts.first() + QLatin1String("/") + name); + + bool exist = false; + const auto mfs = m_drawing->workbook->mediaFiles(); + for (const auto &mf : mfs) { + if (mf->fileName() == path) { + //already exist + exist = true; + m_pictureFile = mf; + } + } + if (!exist) { + m_pictureFile = std::make_shared(path); + m_drawing->workbook->addMediaFile(m_pictureFile, true); + } + } + } else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("pic")) { + break; + } + } + + return; +} + +void DrawingAnchor::loadXmlObjectShape(QXmlStreamReader &reader) +{ + /* + + + + + + + + + + + + + */ + /* + + + + + + + */ + + Q_ASSERT(reader.name() == QLatin1String("sp")); + + while (!reader.atEnd()) + { + reader.readNextStartElement(); + + // qDebug() << __FUNCTION__ << reader.name().toString(); + + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + if (reader.name() == QLatin1String("nvSpPr")) + { + + } + else if (reader.name() == QLatin1String("spPr")) + { + + } + else if (reader.name() == QLatin1String("style")) + { + + } + else if (reader.name() == QLatin1String("txBody")) + { + + } + } + else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("sp")) + { + break; + } + } + + /* + + bool hasoffext = false; + while (!reader.atEnd()) + { + reader.readNextStartElement(); + + // qDebug() << __FUNCTION__ << reader.name().toString(); + + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + if (reader.name() == QLatin1String("blip")) + { + QString rId; + sp_blip_rembed= reader.attributes().value(QLatin1String("r:embed")).toString(); + sp_blip_cstate=reader.attributes().value(QLatin1String("cstate")).toString(); + rId=sp_blip_rembed; + QString name = m_drawing->relationships()->getRelationshipById(rId).target; + QString path = QDir::cleanPath(splitPath(m_drawing->filePath())[0] + QLatin1String("/") + name); + bool exist = false; + QList > mfs = m_drawing->workbook->mediaFiles(); + for (int i=0; ifileName() == path) + { + //already exist + exist = true; + m_pictureFile = mfs[i]; + } + } + if (!exist) { + m_pictureFile = QSharedPointer (new MediaFile(path)); + m_drawing->workbook->addMediaFile(m_pictureFile, true); + } + } + else if (reader.name() == QLatin1String("off")) + { + posTA = loadXmlPos(reader); + hasoffext=true; + } + else if (reader.name() == QLatin1String("ext")&&hasoffext) + { + extTA = loadXmlExt(reader); + hasoffext=false; + } + else if(reader.name() == QLatin1String("blipFill")) + { + // dev24 : fixed for old Qt 5 + + rotWithShapeTA = reader.attributes().value(QLatin1String("rotWithShape")).toString().toInt(); + dpiTA = reader.attributes().value(QLatin1String("dpi")).toString().toInt(); + + // rotWithShapeTA = reader.attributes().value(QLatin1String("rotWithShape")).toInt(); + // dpiTA = reader.attributes().value(QLatin1String("dpi")).toInt(); + + }else if(reader.name() == QLatin1String("cNvPr")) + { + xsp_cNvPR_name= reader.attributes().value(QLatin1String("name")).toString(); + xsp_cNvPR_id= reader.attributes().value(QLatin1String("id")).toString(); + } + else if(reader.name() == QLatin1String("spPr")) + { + xbwMode= reader.attributes().value(QLatin1String("bwMode")).toString(); + } + else if(reader.name() == QLatin1String("prstGeom")) + { + xprstGeom_prst= reader.attributes().value(QLatin1String("prst")).toString(); + } + else if(reader.name() == QLatin1String("ln")) + { + xIn_algn= reader.attributes().value(QLatin1String("algn")).toString(); + xIn_cmpd= reader.attributes().value(QLatin1String("cmpd")).toString(); + xIn_cap= reader.attributes().value(QLatin1String("cap")).toString(); + xIn_w= reader.attributes().value(QLatin1String("w")).toString(); + } + else if(reader.name() == QLatin1String("headEnd")) + { + x_headEnd_w= reader.attributes().value(QLatin1String("w")).toString(); + x_headEnd_len= reader.attributes().value(QLatin1String("len")).toString(); + x_headEnd_tyep= reader.attributes().value(QLatin1String("type")).toString(); + } + else if(reader.name() == QLatin1String("tailEnd")) + { + x_tailEnd_w= reader.attributes().value(QLatin1String("w")).toString(); + x_tailEnd_len= reader.attributes().value(QLatin1String("len")).toString(); + x_tailEnd_tyep= reader.attributes().value(QLatin1String("type")).toString(); + } + else if(reader.name() == QLatin1String("lnRef")) + { + Style_inref_idx= reader.attributes().value(QLatin1String("idx")).toString().trimmed(); + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if(reader.name() == QLatin1String("schemeClr")){ + Style_inref_val=reader.attributes().value(QLatin1String("val")).toString().trimmed(); + } + } + } + else if(reader.name() == QLatin1String("fillRef")) + { + style_fillref_idx= reader.attributes().value(QLatin1String("idx")).toString().trimmed(); + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + if(reader.name() == QLatin1String("schemeClr")) + { + style_fillref_val=reader.attributes().value(QLatin1String("val")).toString().trimmed(); + } + } + } + else if(reader.name() == QLatin1String("effectRef")) + { + style_effectref_idx= reader.attributes().value(QLatin1String("idx")).toString().trimmed(); + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if(reader.name() == QLatin1String("schemeClr")){ + style_effectref_val=reader.attributes().value(QLatin1String("val")).toString().trimmed(); + } + } + } + else if(reader.name() == QLatin1String("fontRef")) + { + style_forntref_idx= reader.attributes().value(QLatin1String("idx")).toString().trimmed(); + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if(reader.name() == QLatin1String("schemeClr")){ + style_forntref_val=reader.attributes().value(QLatin1String("val")).toString().trimmed(); + } + } + } + + } + else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("sp")) + { + break; + } + } + + //*/ + + return; +} + +void DrawingAnchor::saveXmlPos(QXmlStreamWriter &writer, const QPoint &pos) const +{ + writer.writeEmptyElement(QStringLiteral("xdr:pos")); + writer.writeAttribute(QStringLiteral("x"), QString::number(pos.x())); + writer.writeAttribute(QStringLiteral("y"), QString::number(pos.y())); +} + +void DrawingAnchor::saveXmlExt(QXmlStreamWriter &writer, const QSize &ext) const +{ + writer.writeStartElement(QStringLiteral("xdr:ext")); + writer.writeAttribute(QStringLiteral("cx"), QString::number(ext.width())); + writer.writeAttribute(QStringLiteral("cy"), QString::number(ext.height())); + writer.writeEndElement(); //xdr:ext +} + +void DrawingAnchor::saveXmlMarker(QXmlStreamWriter &writer, const XlsxMarker &marker, const QString &node) const +{ + writer.writeStartElement(node); //xdr:from or xdr:to + writer.writeTextElement(QStringLiteral("xdr:col"), QString::number(marker.col())); + writer.writeTextElement(QStringLiteral("xdr:colOff"), QString::number(marker.colOff())); + writer.writeTextElement(QStringLiteral("xdr:row"), QString::number(marker.row())); + writer.writeTextElement(QStringLiteral("xdr:rowOff"), QString::number(marker.rowOff())); + writer.writeEndElement(); +} + +void DrawingAnchor::saveXmlObject(QXmlStreamWriter &writer) const +{ + if (m_objectType == Picture) + saveXmlObjectPicture(writer); + else if (m_objectType == ConnectionShape) + saveXmlObjectConnectionShape(writer); + else if (m_objectType == GraphicFrame) + saveXmlObjectGraphicFrame(writer); + else if (m_objectType == GroupShape) + saveXmlObjectGroupShape(writer); + else if (m_objectType == Shape) + saveXmlObjectShape(writer); +} + +void DrawingAnchor::saveXmlObjectConnectionShape(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("xdr:cxnSp")); ///? + writer.writeAttribute(QStringLiteral("macro"), cxnSp_macro); + + writer.writeStartElement(QStringLiteral("xdr:nvCxnSpPr")); + writer.writeEmptyElement(QStringLiteral("xdr:cNvPr")); + writer.writeAttribute(QStringLiteral("id"), xsp_cNvPR_id); + writer.writeAttribute(QStringLiteral("name"), xsp_cNvPR_name); + writer.writeEmptyElement(QStringLiteral("xdr:cNvCxnSpPr")); + writer.writeEndElement(); //xdr:nvCxnSpPr + + writer.writeStartElement(QStringLiteral("xdr:spPr")); + if(!xbwMode.isNull()){ + writer.writeAttribute(QStringLiteral("bwMode"), xbwMode); + } + + writer.writeStartElement(QStringLiteral("a:xfrm")); + if(!cxnSp_filpV.isEmpty()){ + writer.writeAttribute(QStringLiteral("flipV"), cxnSp_filpV);} + writer.writeEmptyElement(QStringLiteral("a:off")); + writer.writeAttribute(QStringLiteral("x"), QString::number(posTA.x())); + writer.writeAttribute(QStringLiteral("y"), QString::number(posTA.y())); + writer.writeEmptyElement(QStringLiteral("a:ext")); + writer.writeAttribute(QStringLiteral("cx"), QString::number(extTA.width())); + writer.writeAttribute(QStringLiteral("cy"), QString::number(extTA.height())); + writer.writeEndElement(); //a:xfrm + + writer.writeStartElement(QStringLiteral("a:prstGeom")); + writer.writeAttribute(QStringLiteral("prst"), xprstGeom_prst); + writer.writeEmptyElement(QStringLiteral("a:avLst")); + writer.writeEndElement(); //a:prstGeom + + + writer.writeStartElement(QStringLiteral("a:ln")); + if(!xIn_w.isEmpty()&&!xIn_cap.isEmpty()){ + if(!xIn_w.isEmpty()){ + writer.writeAttribute(QStringLiteral("w"), xIn_w); + } + if(!xIn_cap.isEmpty()){ + writer.writeAttribute(QStringLiteral("cap"), xIn_cap); + } + if(!xIn_cmpd.isEmpty()){ + writer.writeAttribute(QStringLiteral("cmpd"), xIn_cmpd); + } + if(!xIn_algn.isEmpty()){ + writer.writeAttribute(QStringLiteral("algn"), xIn_algn); + } + } + if((!x_headEnd_tyep.isEmpty())||(!x_headEnd_w.isEmpty())||(!x_headEnd_len.isEmpty())){ + writer.writeEmptyElement(QStringLiteral("a:headEnd")); + if(!x_headEnd_tyep.isEmpty()){ + writer.writeAttribute(QStringLiteral("type"),x_headEnd_tyep); + } + if(!x_headEnd_w.isEmpty()){ + writer.writeAttribute(QStringLiteral("w"),x_headEnd_w); + } + if(!x_headEnd_len.isEmpty()){ + writer.writeAttribute(QStringLiteral("len"),x_headEnd_len); + } + } + if((!x_tailEnd_tyep.isEmpty())||(!x_tailEnd_w.isEmpty())||(!x_tailEnd_len.isEmpty())){ + writer.writeEmptyElement(QStringLiteral("a:tailEnd")); + if(!x_tailEnd_tyep.isEmpty()){ + writer.writeAttribute(QStringLiteral("type"),x_tailEnd_tyep);} + if(!x_tailEnd_w.isEmpty()){ + writer.writeAttribute(QStringLiteral("w"),x_tailEnd_w);} + if(!x_tailEnd_len.isEmpty()){ + writer.writeAttribute(QStringLiteral("len"),x_tailEnd_len);} + } + + writer.writeEndElement();//a:ln + + + writer.writeEndElement(); //xdr:spPr + // writer style + + writer.writeStartElement(QStringLiteral("xdr:style"));// style + writer.writeStartElement(QStringLiteral("a:lnRef"));//lnRef + writer.writeAttribute(QStringLiteral("idx"),Style_inref_idx); + writer.writeStartElement(QStringLiteral("a:schemeClr"));//val + writer.writeAttribute(QStringLiteral("val"),Style_inref_val); + writer.writeEndElement(); // val + writer.writeEndElement(); // lnRef + writer.writeStartElement(QStringLiteral("a:fillRef"));//fillRef + writer.writeAttribute(QStringLiteral("idx"),style_fillref_idx); + writer.writeStartElement(QStringLiteral("a:schemeClr"));//val + writer.writeAttribute(QStringLiteral("val"),style_fillref_val); + writer.writeEndElement(); // val + writer.writeEndElement(); // fillRef + writer.writeStartElement(QStringLiteral("a:effectRef"));//effectRef + writer.writeAttribute(QStringLiteral("idx"),style_effectref_idx); + writer.writeStartElement(QStringLiteral("a:schemeClr"));//val + writer.writeAttribute(QStringLiteral("val"),style_effectref_val); + writer.writeEndElement(); // val + writer.writeEndElement(); // effectRef + writer.writeStartElement(QStringLiteral("a:fontRef"));//fontRef + writer.writeAttribute(QStringLiteral("idx"),style_forntref_idx); + writer.writeStartElement(QStringLiteral("a:schemeClr"));//val + writer.writeAttribute(QStringLiteral("val"),style_forntref_val); + writer.writeEndElement(); // val + writer.writeEndElement(); // fontRef + writer.writeEndElement(); // style + + writer.writeEndElement(); //xdr:sp +} + +void DrawingAnchor::saveXmlObjectGraphicFrame(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("xdr:graphicFrame")); + writer.writeAttribute(QStringLiteral("macro"), QString()); + + writer.writeStartElement(QStringLiteral("xdr:nvGraphicFramePr")); + writer.writeEmptyElement(QStringLiteral("xdr:cNvPr")); + writer.writeAttribute(QStringLiteral("id"), QString::number(m_id)); + writer.writeAttribute(QStringLiteral("name"), QStringLiteral("Chart %1").arg(m_id)); + writer.writeEmptyElement(QStringLiteral("xdr:cNvGraphicFramePr")); + writer.writeEndElement();//xdr:nvGraphicFramePr + + writer.writeStartElement(QStringLiteral("xdr:xfrm")); + writer.writeEndElement(); //xdr:xfrm + + writer.writeStartElement(QStringLiteral("a:graphic")); + writer.writeStartElement(QStringLiteral("a:graphicData")); + writer.writeAttribute(QStringLiteral("uri"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart")); + + int idx = m_drawing->workbook->chartFiles().indexOf(m_chartFile); + m_drawing->relationships()->addDocumentRelationship(QStringLiteral("/chart"), QStringLiteral("../charts/chart%1.xml").arg(idx+1)); + + writer.writeEmptyElement(QStringLiteral("c:chart")); + writer.writeAttribute(QStringLiteral("xmlns:c"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart")); + writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships")); + writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(m_drawing->relationships()->count())); + + writer.writeEndElement(); //a:graphicData + writer.writeEndElement(); //a:graphic + writer.writeEndElement(); //xdr:graphicFrame +} + +void DrawingAnchor::saveXmlObjectGroupShape(QXmlStreamWriter &writer) const +{ + Q_UNUSED(writer) +} + +void DrawingAnchor::saveXmlObjectPicture(QXmlStreamWriter &writer) const +{ + Q_ASSERT(m_objectType == Picture && m_pictureFile); + + writer.writeStartElement(QStringLiteral("xdr:pic")); + + writer.writeStartElement(QStringLiteral("xdr:nvPicPr")); + writer.writeEmptyElement(QStringLiteral("xdr:cNvPr")); + writer.writeAttribute(QStringLiteral("id"), QString::number(m_id+1)); // liufeijin + writer.writeAttribute(QStringLiteral("name"), QStringLiteral("Picture %1").arg(m_id)); + + writer.writeStartElement(QStringLiteral("xdr:cNvPicPr")); + writer.writeEmptyElement(QStringLiteral("a:picLocks")); + writer.writeAttribute(QStringLiteral("noChangeAspect"), QStringLiteral("1")); + writer.writeEndElement(); //xdr:cNvPicPr + + writer.writeEndElement(); //xdr:nvPicPr + + m_drawing->relationships()->addDocumentRelationship(QStringLiteral("/image"), QStringLiteral("../media/image%1.%2") + .arg(m_pictureFile->index()+1) + .arg(m_pictureFile->suffix())); + + writer.writeStartElement(QStringLiteral("xdr:blipFill")); + writer.writeEmptyElement(QStringLiteral("a:blip")); + writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships")); + writer.writeAttribute(QStringLiteral("r:embed"), QStringLiteral("rId%1").arg(m_drawing->relationships()->count())); + writer.writeStartElement(QStringLiteral("a:stretch")); + writer.writeEmptyElement(QStringLiteral("a:fillRect")); + writer.writeEndElement(); //a:stretch + writer.writeEndElement();//xdr:blipFill + + writer.writeStartElement(QStringLiteral("xdr:spPr")); + + writer.writeStartElement(QStringLiteral("a:prstGeom")); + writer.writeAttribute(QStringLiteral("prst"), QStringLiteral("rect")); + writer.writeEmptyElement(QStringLiteral("a:avLst")); + writer.writeEndElement(); //a:prstGeom + + writer.writeEndElement(); //xdr:spPr + + writer.writeEndElement(); //xdr:pic +} + +int DrawingAnchor::getm_id() +{ + return (this->m_id); +} + +void DrawingAnchor::saveXmlObjectShape(QXmlStreamWriter &writer) const +{ +//{{ liufeijin + writer.writeStartElement(QStringLiteral("xdr:sp")); //xdr:sp + writer.writeAttribute(QStringLiteral("macro"), sp_macro); + writer.writeAttribute(QStringLiteral("textlink"),sp_textlink); + + writer.writeStartElement(QStringLiteral("xdr:nvSpPr"));//xdr:nvSpPr + + writer.writeStartElement(QStringLiteral("xdr:cNvPr")); + writer.writeAttribute(QStringLiteral("id"), xsp_cNvPR_id); + writer.writeAttribute(QStringLiteral("name"), xsp_cNvPR_name); + writer.writeStartElement(QStringLiteral("a:extLst")); + writer.writeEndElement(); + writer.writeEndElement();//xdr:cNvPr + + writer.writeEmptyElement(QStringLiteral("xdr:cNvSpPr")); + + writer.writeEndElement(); //xdr:nvSpPr + + writer.writeStartElement(QStringLiteral("xdr:spPr")); + if(!xbwMode.isNull()){ + writer.writeAttribute(QStringLiteral("bwMode"), xbwMode); + } + writer.writeStartElement(QStringLiteral("a:xfrm")); + writer.writeEmptyElement(QStringLiteral("a:off")); + writer.writeAttribute(QStringLiteral("x"), QString::number(posTA.x())); + writer.writeAttribute(QStringLiteral("y"), QString::number(posTA.y())); + writer.writeEmptyElement(QStringLiteral("a:ext")); + writer.writeAttribute(QStringLiteral("cx"), QString::number(extTA.width())); + writer.writeAttribute(QStringLiteral("cy"), QString::number(extTA.height())); + writer.writeEndElement(); //a:xfrm + + writer.writeStartElement(QStringLiteral("a:prstGeom")); + writer.writeAttribute(QStringLiteral("prst"), xprstGeom_prst); + writer.writeEmptyElement(QStringLiteral("a:avLst")); + writer.writeEndElement(); //a:prstGeom + + if(m_pictureFile){ + m_drawing->relationships()->addDocumentRelationship(QStringLiteral("/image"), QStringLiteral("../media/image%1.%2").arg(m_pictureFile->index()+1).arg(m_pictureFile->suffix())); + writer.writeStartElement(QStringLiteral("a:blipFill")); + writer.writeAttribute(QStringLiteral("dpi"), QString::number(dpiTA)); + writer.writeAttribute(QStringLiteral("rotWithShape"),QString::number(rotWithShapeTA)); + + writer.writeStartElement(QStringLiteral("a:blip")); + writer.writeAttribute(QStringLiteral("r:embed"), QStringLiteral("rId%1").arg(m_drawing->relationships()->count())); //sp_blip_rembed QStringLiteral("rId%1").arg(m_drawing->relationships()->count()) can't made new pic + writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships")); + if(!sp_blip_cstate.isNull()){ + writer.writeAttribute(QStringLiteral("cstate"), sp_blip_cstate); + } + writer.writeEndElement();//a:blip + writer.writeEmptyElement(QStringLiteral("a:srcRect")); + writer.writeStartElement(QStringLiteral("a:stretch")); + writer.writeEmptyElement(QStringLiteral("a:fillRect")); + writer.writeEndElement(); //a:stretch + writer.writeEndElement();//a:blipFill + } + writer.writeStartElement(QStringLiteral("a:ln")); + if(!xIn_w.isEmpty()&&!xIn_cap.isEmpty()){ + if(!xIn_w.isEmpty()){ + writer.writeAttribute(QStringLiteral("w"), xIn_w); + } + if(!xIn_cap.isEmpty()){ + writer.writeAttribute(QStringLiteral("cap"), xIn_cap); + } + if(!xIn_cmpd.isEmpty()){ + writer.writeAttribute(QStringLiteral("cmpd"), xIn_cmpd); + } + if(!xIn_algn.isEmpty()){ + writer.writeAttribute(QStringLiteral("algn"), xIn_algn); + } + } + if((!x_headEnd_tyep.isEmpty())||(!x_headEnd_w.isEmpty())||(!x_headEnd_len.isEmpty())){ + writer.writeEmptyElement(QStringLiteral("a:headEnd")); + if(!x_headEnd_tyep.isEmpty()){ + writer.writeAttribute(QStringLiteral("type"),x_headEnd_tyep); + } + if(!x_headEnd_w.isEmpty()){ + writer.writeAttribute(QStringLiteral("w"),x_headEnd_w); + } + if(!x_headEnd_len.isEmpty()){ + writer.writeAttribute(QStringLiteral("len"),x_headEnd_len); + } + } + if((!x_tailEnd_tyep.isEmpty())||(!x_tailEnd_w.isEmpty())||(!x_tailEnd_len.isEmpty())){ + writer.writeEmptyElement(QStringLiteral("a:tailEnd")); + if(!x_tailEnd_tyep.isEmpty()){ + writer.writeAttribute(QStringLiteral("type"),x_tailEnd_tyep);} + if(!x_tailEnd_w.isEmpty()){ + writer.writeAttribute(QStringLiteral("w"),x_tailEnd_w);} + if(!x_tailEnd_len.isEmpty()){ + writer.writeAttribute(QStringLiteral("len"),x_tailEnd_len);} + } + + writer.writeEndElement();//a:ln + + + writer.writeEndElement(); //xdr:spPr + // writer style + + writer.writeStartElement(QStringLiteral("xdr:style"));// style + writer.writeStartElement(QStringLiteral("a:lnRef"));//lnRef + writer.writeAttribute(QStringLiteral("idx"),Style_inref_idx); + writer.writeStartElement(QStringLiteral("a:schemeClr"));//val + writer.writeAttribute(QStringLiteral("val"),Style_inref_val); + writer.writeEndElement(); // val + writer.writeEndElement(); // lnRef + writer.writeStartElement(QStringLiteral("a:fillRef"));//fillRef + writer.writeAttribute(QStringLiteral("idx"),style_fillref_idx); + writer.writeStartElement(QStringLiteral("a:schemeClr"));//val + writer.writeAttribute(QStringLiteral("val"),style_fillref_val); + writer.writeEndElement(); // val + writer.writeEndElement(); // fillRef + writer.writeStartElement(QStringLiteral("a:effectRef"));//effectRef + writer.writeAttribute(QStringLiteral("idx"),style_effectref_idx); + writer.writeStartElement(QStringLiteral("a:schemeClr"));//val + writer.writeAttribute(QStringLiteral("val"),style_effectref_val); + writer.writeEndElement(); // val + writer.writeEndElement(); // effectRef + writer.writeStartElement(QStringLiteral("a:fontRef"));//fontRef + writer.writeAttribute(QStringLiteral("idx"),style_forntref_idx); + writer.writeStartElement(QStringLiteral("a:schemeClr"));//val + writer.writeAttribute(QStringLiteral("val"),style_forntref_val); + writer.writeEndElement(); // val + writer.writeEndElement(); // fontRef + writer.writeEndElement(); // style + + writer.writeEndElement(); //xdr:sp + + //}} liufeijin +} + +//absolute anchor + +DrawingAbsoluteAnchor::DrawingAbsoluteAnchor(Drawing *drawing, ObjectType objectType) + :DrawingAnchor(drawing, objectType) +{ + +} + +// check point +bool DrawingAbsoluteAnchor::loadFromXml(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("absoluteAnchor")); + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("pos")) + { + pos = loadXmlPos(reader); + } + else if (reader.name() == QLatin1String("ext")) + { + ext = loadXmlExt(reader); + } + else + { + loadXmlObject(reader); + } + } + else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("absoluteAnchor")) + { + break; + } + } + return true; +} + +void DrawingAbsoluteAnchor::saveToXml(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("xdr:absoluteAnchor")); + saveXmlPos(writer, pos); + saveXmlExt(writer, ext); + + saveXmlObject(writer); + + writer.writeEmptyElement(QStringLiteral("xdr:clientData")); + writer.writeEndElement(); //xdr:absoluteAnchor +} + +//one cell anchor + +DrawingOneCellAnchor::DrawingOneCellAnchor(Drawing *drawing, ObjectType objectType) + :DrawingAnchor(drawing, objectType) +{ + +} + +int DrawingOneCellAnchor::row() const +{ + return from.row(); +} + +int DrawingOneCellAnchor::col() const +{ + return from.col(); +} + +// check point +bool DrawingOneCellAnchor::loadFromXml(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("oneCellAnchor")); + + while (!reader.atEnd()) + { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + if (reader.name() == QLatin1String("from")) + { + from = loadXmlMarker(reader, QLatin1String("from")); + } + else if (reader.name() == QLatin1String("ext")) + { + ext = loadXmlExt(reader); + } + else + { + loadXmlObject(reader); + } + } + else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("oneCellAnchor")) + { + break; + } + } + return true; +} + +void DrawingOneCellAnchor::saveToXml(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("xdr:oneCellAnchor")); + + saveXmlMarker(writer, from, QStringLiteral("xdr:from")); + saveXmlExt(writer, ext); + + saveXmlObject(writer); + + writer.writeEmptyElement(QStringLiteral("xdr:clientData")); + writer.writeEndElement(); //xdr:oneCellAnchor +} + +/* + Two cell anchor + + This class specifies a two cell anchor placeholder for a group + , a shape, or a drawing element. It moves with + cells and its extents are in EMU units. +*/ +DrawingTwoCellAnchor::DrawingTwoCellAnchor(Drawing *drawing, ObjectType objectType) + :DrawingAnchor(drawing, objectType) +{ + +} + +int DrawingTwoCellAnchor::row() const +{ + return from.row(); +} + +int DrawingTwoCellAnchor::col() const +{ + return from.col(); +} + +// check point +bool DrawingTwoCellAnchor::loadFromXml(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("twoCellAnchor")); + + /* + + + + + + + + + + */ + + //{{ liufeijin + QXmlStreamAttributes attrs = reader.attributes(); // for absolute twocell aadd by liufeijin 20181024 + editASName = attrs.value(QLatin1String("editAs")).toString(); + //}} + + while (!reader.atEnd()) + { + reader.readNextStartElement(); + + if (reader.tokenType() == QXmlStreamReader::StartElement) + { + if (reader.name() == QLatin1String("from")) + { + from = loadXmlMarker(reader, QLatin1String("from")); + } + else if (reader.name() == QLatin1String("to")) + { + to = loadXmlMarker(reader, QLatin1String("to")); + } + else if (reader.name() == QLatin1String("clientData")) + { + // clientData + } + else + { + /* + + + + + + + + + + + + + */ + + loadXmlObject(reader); + } + } + else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("twoCellAnchor")) + { + break; + } + } + return true; +} + + + void DrawingTwoCellAnchor::saveToXml(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("xdr:twoCellAnchor")); + + //{{ liufeijin + // writer.writeAttribute(QStringLiteral("editAs"), QStringLiteral("oneCell")); + if(!editASName.isNull()){ + writer.writeAttribute(QStringLiteral("editAs"), editASName ); //QStringLiteral("oneCell") + } + // }} + + saveXmlMarker(writer, from, QStringLiteral("xdr:from")); + saveXmlMarker(writer, to, QStringLiteral("xdr:to")); + + saveXmlObject(writer); + + writer.writeEmptyElement(QStringLiteral("xdr:clientData")); + writer.writeEndElement(); //xdr:twoCellAnchor + +} + + +QT_END_NAMESPACE_XLSX diff --git a/src/QXlsx/xlsxformat.cpp b/src/QXlsx/xlsxformat.cpp new file mode 100644 index 0000000..cc50677 --- /dev/null +++ b/src/QXlsx/xlsxformat.cpp @@ -0,0 +1,1437 @@ +// xlsxformat.cpp + +#include +#include +#include + +#include "xlsxformat.h" +#include "xlsxformat_p.h" +#include "xlsxcolor_p.h" +#include "xlsxnumformatparser_p.h" + +QT_BEGIN_NAMESPACE_XLSX + +FormatPrivate::FormatPrivate() + : dirty(true) + , font_dirty(true), font_index_valid(false), font_index(0) + , fill_dirty(true), fill_index_valid(false), fill_index(0) + , border_dirty(true), border_index_valid(false), border_index(0) + , xf_index(-1), xf_indexValid(false) + , is_dxf_fomat(false), dxf_index(-1), dxf_indexValid(false) + , theme(0) +{ +} + +FormatPrivate::FormatPrivate(const FormatPrivate &other) + : QSharedData(other) + , dirty(other.dirty), formatKey(other.formatKey) + , font_dirty(other.font_dirty), font_index_valid(other.font_index_valid), font_key(other.font_key), font_index(other.font_index) + , fill_dirty(other.fill_dirty), fill_index_valid(other.fill_index_valid), fill_key(other.fill_key), fill_index(other.fill_index) + , border_dirty(other.border_dirty), border_index_valid(other.border_index_valid), border_key(other.border_key), border_index(other.border_index) + , xf_index(other.xf_index), xf_indexValid(other.xf_indexValid) + , is_dxf_fomat(other.is_dxf_fomat), dxf_index(other.dxf_index), dxf_indexValid(other.dxf_indexValid) + , theme(other.theme) + , properties(other.properties) +{ + +} + +FormatPrivate::~FormatPrivate() +{ + +} + +/*! + * \class Format + * \inmodule QtXlsx + * \brief Providing the methods and properties that are available for formatting cells in Excel. + */ + +/*! + * \enum Format::FontScript + * + * The enum type defines the type of font script. + * + * \value FontScriptNormal normal + * \value FontScriptSuper super script + * \value FontScriptSub sub script + */ + + +/*! + * \enum Format::FontUnderline + * + * The enum type defines the type of font underline. + * + * \value FontUnderlineNone + * \value FontUnderlineSingle + * \value FontUnderlineDouble + * \value FontUnderlineSingleAccounting + * \value FontUnderlineDoubleAccounting + */ + +/*! + * \enum Format::HorizontalAlignment + * + * The enum type defines the type of horizontal alignment. + * + * \value AlignHGeneral + * \value AlignLeft + * \value AlignHCenter + * \value AlignRight + * \value AlignHFill + * \value AlignHJustify + * \value AlignHMerge + * \value AlignHDistributed + */ + +/*! + * \enum Format::VerticalAlignment + * + * The enum type defines the type of vertical alignment. + * + * \value AlignTop, + * \value AlignVCenter, + * \value AlignBottom, + * \value AlignVJustify, + * \value AlignVDistributed + */ + +/*! + * \enum Format::BorderStyle + * + * The enum type defines the type of font underline. + * + * \value BorderNone + * \value BorderThin + * \value BorderMedium + * \value BorderDashed + * \value BorderDotted + * \value BorderThick + * \value BorderDouble + * \value BorderHair + * \value BorderMediumDashed + * \value BorderDashDot + * \value BorderMediumDashDot + * \value BorderDashDotDot + * \value BorderMediumDashDotDot + * \value BorderSlantDashDot +*/ + +/*! + * \enum Format::DiagonalBorderType + * + * The enum type defines the type of diagonal border. + * + * \value DiagonalBorderNone + * \value DiagonalBorderDown + * \value DiagonalBorderUp + * \value DiagnoalBorderBoth + */ + +/*! + * \enum Format::FillPattern + * + * The enum type defines the type of fill. + * + * \value PatternNone + * \value PatternSolid + * \value PatternMediumGray + * \value PatternDarkGray + * \value PatternLightGray + * \value PatternDarkHorizontal + * \value PatternDarkVertical + * \value PatternDarkDown + * \value PatternDarkUp + * \value PatternDarkGrid + * \value PatternDarkTrellis + * \value PatternLightHorizontal + * \value PatternLightVertical + * \value PatternLightDown + * \value PatternLightUp + * \value PatternLightTrellis + * \value PatternGray125 + * \value PatternGray0625 + * \value PatternLightGrid + */ + +/*! + * Creates a new invalid format. + */ +Format::Format() +{ + //The d pointer is initialized with a null pointer +} + +/*! + Creates a new format with the same attributes as the \a other format. + */ +Format::Format(const Format &other) + :d(other.d) +{ + +} + +/*! + Assigns the \a other format to this format, and returns a + reference to this format. + */ +Format &Format::operator =(const Format &other) +{ + d = other.d; + return *this; +} + +/*! + * Destroys this format. + */ +Format::~Format() +{ +} + +/*! + * Returns the number format identifier. + */ +int Format::numberFormatIndex() const +{ + return intProperty(FormatPrivate::P_NumFmt_Id, 0); +} + +/*! + * Set the number format identifier. The \a format + * must be a valid built-in number format identifier + * or the identifier of a custom number format. + */ +void Format::setNumberFormatIndex(int format) +{ + setProperty(FormatPrivate::P_NumFmt_Id, format); + clearProperty(FormatPrivate::P_NumFmt_FormatCode); +} + +/*! + * Returns the number format string. + * \note for built-in number formats, this may + * return an empty string. + */ +QString Format::numberFormat() const +{ + return stringProperty(FormatPrivate::P_NumFmt_FormatCode); +} + +/*! + * Set number \a format. + * http://office.microsoft.com/en-001/excel-help/create-a-custom-number-format-HP010342372.aspx + */ +void Format::setNumberFormat(const QString &format) +{ + if (format.isEmpty()) + return; + setProperty(FormatPrivate::P_NumFmt_FormatCode, format); + clearProperty(FormatPrivate::P_NumFmt_Id); //numFmt id must be re-generated. +} + +/*! + * Returns whether the number format is probably a dateTime or not + */ +bool Format::isDateTimeFormat() const +{ + //NOTICE: + + if (hasProperty(FormatPrivate::P_NumFmt_FormatCode)) + { + //Custom numFmt, so + //Gauss from the number string + return NumFormatParser::isDateTime(numberFormat()); + } + else if (hasProperty(FormatPrivate::P_NumFmt_Id)) + { + //Non-custom numFmt + int idx = numberFormatIndex(); + + //Is built-in date time number id? + if ((idx >= 14 && idx <= 22) || (idx >= 45 && idx <= 47)) + return true; + + if ((idx >= 27 && idx <= 36) || (idx >= 50 && idx <= 58)) //Used in CHS\CHT\JPN\KOR + return true; + } + + return false; +} + +/*! + \internal + Set a custom num \a format with the given \a id. + */ +void Format::setNumberFormat(int id, const QString &format) +{ + setProperty(FormatPrivate::P_NumFmt_Id, id); + setProperty(FormatPrivate::P_NumFmt_FormatCode, format); +} + +/*! + \internal + Called by styles to fix the numFmt + */ +void Format::fixNumberFormat(int id, const QString &format) +{ + setProperty(FormatPrivate::P_NumFmt_Id, id, 0, false); + setProperty(FormatPrivate::P_NumFmt_FormatCode, format, QString(), false); +} + +/*! + \internal + Return true if the format has number format. + */ +bool Format::hasNumFmtData() const +{ + if (!d) + return false; + + if ( hasProperty(FormatPrivate::P_NumFmt_Id) || + hasProperty(FormatPrivate::P_NumFmt_FormatCode) ) + { + return true; + } + return false; +} + +/*! + * Return the size of the font in points. + */ +int Format::fontSize() const +{ + return intProperty(FormatPrivate::P_Font_Size); +} + +/*! + * Set the \a size of the font in points. + */ +void Format::setFontSize(int size) +{ + setProperty(FormatPrivate::P_Font_Size, size, 0); +} + +/*! + * Return whether the font is italic. + */ +bool Format::fontItalic() const +{ + return boolProperty(FormatPrivate::P_Font_Italic); +} + +/*! + * Turn on/off the italic font based on \a italic. + */ +void Format::setFontItalic(bool italic) +{ + setProperty(FormatPrivate::P_Font_Italic, italic, false); +} + +/*! + * Return whether the font is strikeout. + */ +bool Format::fontStrikeOut() const +{ + return boolProperty(FormatPrivate::P_Font_StrikeOut); +} + +/*! + * Turn on/off the strikeOut font based on \a strikeOut. + */ +void Format::setFontStrikeOut(bool strikeOut) +{ + setProperty(FormatPrivate::P_Font_StrikeOut, strikeOut, false); +} + +/*! + * Return the color of the font. + */ +QColor Format::fontColor() const +{ + if (hasProperty(FormatPrivate::P_Font_Color)) + return colorProperty(FormatPrivate::P_Font_Color); + return QColor(); +} + +/*! + * Set the \a color of the font. + */ +void Format::setFontColor(const QColor &color) +{ + setProperty(FormatPrivate::P_Font_Color, XlsxColor(color), XlsxColor()); +} + +/*! + * Return whether the font is bold. + */ +bool Format::fontBold() const +{ + return boolProperty(FormatPrivate::P_Font_Bold); +} + +/*! + * Turn on/off the bold font based on the given \a bold. + */ +void Format::setFontBold(bool bold) +{ + setProperty(FormatPrivate::P_Font_Bold, bold, false); +} + +/*! + * Return the script style of the font. + */ +Format::FontScript Format::fontScript() const +{ + return static_cast(intProperty(FormatPrivate::P_Font_Script)); +} + +/*! + * Set the script style of the font to \a script. + */ +void Format::setFontScript(FontScript script) +{ + setProperty(FormatPrivate::P_Font_Script, script, FontScriptNormal); +} + +/*! + * Return the underline style of the font. + */ +Format::FontUnderline Format::fontUnderline() const +{ + return static_cast(intProperty(FormatPrivate::P_Font_Underline)); +} + +/*! + * Set the underline style of the font to \a underline. + */ +void Format::setFontUnderline(FontUnderline underline) +{ + setProperty(FormatPrivate::P_Font_Underline, underline, FontUnderlineNone); +} + +/*! + * Return whether the font is outline. + */ +bool Format::fontOutline() const +{ + return boolProperty(FormatPrivate::P_Font_Outline); +} + +/*! + * Turn on/off the outline font based on \a outline. + */ +void Format::setFontOutline(bool outline) +{ + setProperty(FormatPrivate::P_Font_Outline, outline, false); +} + +/*! + * Return the name of the font. + */ +QString Format::fontName() const +{ + return stringProperty(FormatPrivate::P_Font_Name, QStringLiteral("Calibri")); +} + +/*! + * Set the name of the font to \a name. + */ +void Format::setFontName(const QString &name) +{ + setProperty(FormatPrivate::P_Font_Name, name, QStringLiteral("Calibri")); +} + +/*! + * Returns a QFont object based on font data contained in the format. + */ +QFont Format::font() const +{ + QFont font; + font.setFamily(fontName()); + if (fontSize() > 0) + font.setPointSize(fontSize()); + font.setBold(fontBold()); + font.setItalic(fontItalic()); + font.setUnderline(fontUnderline()!=FontUnderlineNone); + font.setStrikeOut(fontStrikeOut()); + return font; +} + +/*! + * Set the format properties from the given \a font. + */ +void Format::setFont(const QFont &font) +{ + setFontName(font.family()); + if (font.pointSize() > 0) + setFontSize(font.pointSize()); + setFontBold(font.bold()); + setFontItalic(font.italic()); + setFontUnderline(font.underline() ? FontUnderlineSingle : FontUnderlineNone); + setFontStrikeOut(font.strikeOut()); +} + +/*! + * \internal + * When the format has font data, when need to assign a valid index for it. + * The index value is depend on the order in styles.xml + */ +bool Format::fontIndexValid() const +{ + if (!hasFontData()) + return false; + return d->font_index_valid; +} + +/*! + * \internal + */ +int Format::fontIndex() const +{ + if (fontIndexValid()) + return d->font_index; + + return 0; +} + +/*! + * \internal + */ +void Format::setFontIndex(int index) +{ + d->font_index = index; + d->font_index_valid = true; +} + +/*! + * \internal + */ +QByteArray Format::fontKey() const +{ + if (isEmpty()) + return QByteArray(); + + if (d->font_dirty) { + QByteArray key; + QDataStream stream(&key, QIODevice::WriteOnly); + for (int i=FormatPrivate::P_Font_STARTID; iproperties.constFind(i); + if (it != d->properties.constEnd()) + stream << i << it.value(); + }; + + const_cast(this)->d->font_key = key; + const_cast(this)->d->font_dirty = false; + } + + return d->font_key; +} + +/*! + \internal + Return true if the format has font format, otherwise return false. + */ +bool Format::hasFontData() const +{ + if (!d) + return false; + + for (int i=FormatPrivate::P_Font_STARTID; i(intProperty(FormatPrivate::P_Alignment_AlignH, AlignHGeneral)); +} + +/*! + * Set the horizontal alignment with the given \a align. + */ +void Format::setHorizontalAlignment(HorizontalAlignment align) +{ + if (hasProperty(FormatPrivate::P_Alignment_Indent) + &&(align != AlignHGeneral && align != AlignLeft && align != AlignRight && align != AlignHDistributed)) { + clearProperty(FormatPrivate::P_Alignment_Indent); + } + + if (hasProperty(FormatPrivate::P_Alignment_ShinkToFit) + && (align == AlignHFill || align == AlignHJustify || align == AlignHDistributed)) { + clearProperty(FormatPrivate::P_Alignment_ShinkToFit); + } + + setProperty(FormatPrivate::P_Alignment_AlignH, align, AlignHGeneral); +} + +/*! + * Return the vertical alignment. + */ +Format::VerticalAlignment Format::verticalAlignment() const +{ + return static_cast(intProperty(FormatPrivate::P_Alignment_AlignV, AlignBottom)); +} + +/*! + * Set the vertical alignment with the given \a align. + */ +void Format::setVerticalAlignment(VerticalAlignment align) +{ + setProperty(FormatPrivate::P_Alignment_AlignV, align, AlignBottom); +} + +/*! + * Return whether the cell text is wrapped. + */ +bool Format::textWrap() const +{ + return boolProperty(FormatPrivate::P_Alignment_Wrap); +} + +/*! + * Enable the text wrap if \a wrap is true. + */ +void Format::setTextWrap(bool wrap) +{ + if (wrap && hasProperty(FormatPrivate::P_Alignment_ShinkToFit)) + clearProperty(FormatPrivate::P_Alignment_ShinkToFit); + + setProperty(FormatPrivate::P_Alignment_Wrap, wrap, false); +} + +/*! + * Return the text rotation. + */ +int Format::rotation() const +{ + return intProperty(FormatPrivate::P_Alignment_Rotation); +} + +/*! + * Set the text roation with the given \a rotation. Must be in the range [0, 180] or 255. + */ +void Format::setRotation(int rotation) +{ + setProperty(FormatPrivate::P_Alignment_Rotation, rotation, 0); +} + +/*! + * Return the text indentation level. + */ +int Format::indent() const +{ + return intProperty(FormatPrivate::P_Alignment_Indent); +} + +/*! + * Set the text indentation level with the given \a indent. Must be less than or equal to 15. + */ +void Format::setIndent(int indent) +{ + if (indent && hasProperty(FormatPrivate::P_Alignment_AlignH)) { + HorizontalAlignment hl = horizontalAlignment(); + + if (hl != AlignHGeneral && hl != AlignLeft && hl!= AlignRight && hl!= AlignHJustify) { + setHorizontalAlignment(AlignLeft); + } + } + + setProperty(FormatPrivate::P_Alignment_Indent, indent, 0); +} + +/*! + * Return whether the cell is shrink to fit. + */ +bool Format::shrinkToFit() const +{ + return boolProperty(FormatPrivate::P_Alignment_ShinkToFit); +} + +/*! + * Turn on/off shrink to fit base on \a shink. + */ +void Format::setShrinkToFit(bool shink) +{ + if (shink && hasProperty(FormatPrivate::P_Alignment_Wrap)) + clearProperty(FormatPrivate::P_Alignment_Wrap); + + if (shink && hasProperty(FormatPrivate::P_Alignment_AlignH)) { + HorizontalAlignment hl = horizontalAlignment(); + if (hl == AlignHFill || hl == AlignHJustify || hl == AlignHDistributed) + setHorizontalAlignment(AlignLeft); + } + + setProperty(FormatPrivate::P_Alignment_ShinkToFit, shink, false); +} + +/*! + * \internal + */ +bool Format::hasAlignmentData() const +{ + if (!d) + return false; + + for (int i=FormatPrivate::P_Alignment_STARTID; i(intProperty(FormatPrivate::P_Border_LeftStyle)); +} + +/*! + * Sets the left border style to \a style + */ +void Format::setLeftBorderStyle(BorderStyle style) +{ + setProperty(FormatPrivate::P_Border_LeftStyle, style, BorderNone); +} + +/*! + * Returns the left border color + */ +QColor Format::leftBorderColor() const +{ + return colorProperty(FormatPrivate::P_Border_LeftColor); +} + +/*! + Sets the left border color to the given \a color +*/ +void Format::setLeftBorderColor(const QColor &color) +{ + setProperty(FormatPrivate::P_Border_LeftColor, XlsxColor(color), XlsxColor()); +} + +/*! + Returns the right border style. +*/ +Format::BorderStyle Format::rightBorderStyle() const +{ + return static_cast(intProperty(FormatPrivate::P_Border_RightStyle)); +} + +/*! + Sets the right border style to the given \a style. +*/ +void Format::setRightBorderStyle(BorderStyle style) +{ + setProperty(FormatPrivate::P_Border_RightStyle, style, BorderNone); +} + +/*! + Returns the right border color. +*/ +QColor Format::rightBorderColor() const +{ + return colorProperty(FormatPrivate::P_Border_RightColor); +} + +/*! + Sets the right border color to the given \a color +*/ +void Format::setRightBorderColor(const QColor &color) +{ + setProperty(FormatPrivate::P_Border_RightColor, XlsxColor(color), XlsxColor()); +} + +/*! + Returns the top border style. +*/ +Format::BorderStyle Format::topBorderStyle() const +{ + return static_cast(intProperty(FormatPrivate::P_Border_TopStyle)); +} + +/*! + Sets the top border style to the given \a style. +*/ +void Format::setTopBorderStyle(BorderStyle style) +{ + setProperty(FormatPrivate::P_Border_TopStyle, style, BorderNone); +} + +/*! + Returns the top border color. +*/ +QColor Format::topBorderColor() const +{ + return colorProperty(FormatPrivate::P_Border_TopColor); +} + +/*! + Sets the top border color to the given \a color. +*/ +void Format::setTopBorderColor(const QColor &color) +{ + setProperty(FormatPrivate::P_Border_TopColor, XlsxColor(color), XlsxColor()); +} + +/*! + Returns the bottom border style. +*/ +Format::BorderStyle Format::bottomBorderStyle() const +{ + return static_cast(intProperty(FormatPrivate::P_Border_BottomStyle)); +} + +/*! + Sets the bottom border style to the given \a style. +*/ +void Format::setBottomBorderStyle(BorderStyle style) +{ + setProperty(FormatPrivate::P_Border_BottomStyle, style, BorderNone); +} + +/*! + Returns the bottom border color. +*/ +QColor Format::bottomBorderColor() const +{ + return colorProperty(FormatPrivate::P_Border_BottomColor); +} + +/*! + Sets the bottom border color to the given \a color. +*/ +void Format::setBottomBorderColor(const QColor &color) +{ + setProperty(FormatPrivate::P_Border_BottomColor, XlsxColor(color), XlsxColor()); +} + +/*! + Return the diagonla border style. +*/ +Format::BorderStyle Format::diagonalBorderStyle() const +{ + return static_cast(intProperty(FormatPrivate::P_Border_DiagonalStyle)); +} + +/*! + Sets the diagonal border style to the given \a style. +*/ +void Format::setDiagonalBorderStyle(BorderStyle style) +{ + setProperty(FormatPrivate::P_Border_DiagonalStyle, style, BorderNone); +} + +/*! + Returns the diagonal border type. +*/ +Format::DiagonalBorderType Format::diagonalBorderType() const +{ + return static_cast(intProperty(FormatPrivate::P_Border_DiagonalType)); +} + +/*! + Sets the diagonal border type to the given \a style +*/ +void Format::setDiagonalBorderType(DiagonalBorderType style) +{ + setProperty(FormatPrivate::P_Border_DiagonalType, style, DiagonalBorderNone); +} + +/*! + Returns the diagonal border color. +*/ +QColor Format::diagonalBorderColor() const +{ + return colorProperty(FormatPrivate::P_Border_DiagonalColor); +} + +/*! + Sets the diagonal border color to the given \a color +*/ +void Format::setDiagonalBorderColor(const QColor &color) +{ + setProperty(FormatPrivate::P_Border_DiagonalColor, XlsxColor(color), XlsxColor()); +} + +/*! + \internal + Returns whether this format has been set valid border index. +*/ +bool Format::borderIndexValid() const +{ + if (!hasBorderData()) + return false; + return d->border_index_valid; +} + +/*! + \internal + Returns the border index. +*/ +int Format::borderIndex() const +{ + if (borderIndexValid()) + return d->border_index; + return 0; +} + +/*! + * \internal + */ +void Format::setBorderIndex(int index) +{ + d->border_index = index; + d->border_index_valid = true; +} + +/*! \internal + */ +QByteArray Format::borderKey() const +{ + if (isEmpty()) + return QByteArray(); + + if (d->border_dirty) { + QByteArray key; + QDataStream stream(&key, QIODevice::WriteOnly); + for (int i=FormatPrivate::P_Border_STARTID; iproperties.constFind(i); + if (it != d->properties.constEnd()) + stream << i << it.value(); + }; + + const_cast(this)->d->border_key = key; + const_cast(this)->d->border_dirty = false; + } + + return d->border_key; +} + +/*! + \internal + Return true if the format has border format, otherwise return false. + */ +bool Format::hasBorderData() const +{ + if (!d) + return false; + + for (int i=FormatPrivate::P_Border_STARTID; i(intProperty(FormatPrivate::P_Fill_Pattern, PatternNone)); +} + +/*! + Sets the fill pattern to the given \a pattern. +*/ +void Format::setFillPattern(FillPattern pattern) +{ + setProperty(FormatPrivate::P_Fill_Pattern, pattern, PatternNone); +} + +/*! + Returns the foreground color of the pattern. +*/ +QColor Format::patternForegroundColor() const +{ + return colorProperty(FormatPrivate::P_Fill_FgColor); +} + +/*! + Sets the foreground color of the pattern with the given \a color. +*/ +void Format::setPatternForegroundColor(const QColor &color) +{ + if (color.isValid() && !hasProperty(FormatPrivate::P_Fill_Pattern)) + setFillPattern(PatternSolid); + setProperty(FormatPrivate::P_Fill_FgColor, XlsxColor(color), XlsxColor()); +} + +/*! + Returns the background color of the pattern. +*/ +QColor Format::patternBackgroundColor() const +{ + return colorProperty(FormatPrivate::P_Fill_BgColor); +} + +/*! + Sets the background color of the pattern with the given \a color. +*/ +void Format::setPatternBackgroundColor(const QColor &color) +{ + if (color.isValid() && !hasProperty(FormatPrivate::P_Fill_Pattern)) + setFillPattern(PatternSolid); + setProperty(FormatPrivate::P_Fill_BgColor, XlsxColor(color), XlsxColor()); +} + +/*! + * \internal + */ +bool Format::fillIndexValid() const +{ + if (!hasFillData()) + return false; + return d->fill_index_valid; +} + +/*! + * \internal + */ +int Format::fillIndex() const +{ + if (fillIndexValid()) + return d->fill_index; + return 0; +} + +/*! + * \internal + */ +void Format::setFillIndex(int index) +{ + d->fill_index = index; + d->fill_index_valid = true; +} + +/*! + * \internal + */ +QByteArray Format::fillKey() const +{ + if (isEmpty()) + return QByteArray(); + + if (d->fill_dirty) { + QByteArray key; + QDataStream stream(&key, QIODevice::WriteOnly); + for (int i=FormatPrivate::P_Fill_STARTID; iproperties.constFind(i); + if (it != d->properties.constEnd()) + stream << i << it.value(); + }; + + const_cast(this)->d->fill_key = key; + const_cast(this)->d->fill_dirty = false; + } + + return d->fill_key; +} + +/*! + \internal + Return true if the format has fill format, otherwise return false. + */ +bool Format::hasFillData() const +{ + if (!d) + return false; + + for (int i=FormatPrivate::P_Fill_STARTID; i