Mercurial > clickerconvert
changeset 1:93d3106bb9a4
Add qt xlsx library
line wrap: on
line diff
--- a/src/CMakeLists.txt Mon Mar 07 17:35:09 2016 +0100 +++ b/src/CMakeLists.txt Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,64 @@ +# Copyright (C) 2016 by ETH Zürich +# Software engineering by Intevation GmbH +# +# This file is Free Software under the GNU GPL (v>=2) +# and comes with ABSOLUTELY NO WARRANTY! +# See LICENSE.txt for details. + +include_directories(${Qt5Core_INCLUDE_DIRS}) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +add_subdirectory(xlsx) +include_directories(xlsx) + +set(APPLICATION_SRC + main.cpp + converter.cpp + strhelp.c +) + +find_package(Qt5LinguistTools) + +if(Qt5LinguistTools_FOUND) + + set(FILES_TO_TRANSLATE + ${APPLICATION_SRC} + ) + + # Include translation as a resource + # This works in the source directory to enable the rcc dependencies to be found + # and it also updates the currently available localization. + configure_file(l10n/l10n.qrc.in l10n.qrc) + qt5_add_resources(APPLICATION_SRC ${CMAKE_CURRENT_BINARY_DIR}/l10n.qrc) + qt5_create_translation(TRANSLATION_SRC ${FILES_TO_TRANSLATE} + ${CMAKE_CURRENT_SOURCE_DIR}/l10n/main_de_DE.ts) +endif() + + +add_executable(${PROJECT_NAME} + ${_add_executable_params} + ${APPLICATION_SRC} + ${TRANSLATION_SRC} +) + +target_link_libraries(${PROJECT_NAME} + Qt5::Core + Qt5::Gui + ${PROJECT_NAME}_libqtxslx +) + +if (WIN32) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-municode") +endif(WIN32) + +install(TARGETS ${PROJECT_NAME} DESTINATION bin BUNDLE DESTINATION .) + +if(APPLE) + install(CODE " + include(BundleUtilities) + fixup_bundle(\"${APPS}\" \"\${QTPLUGINS}\" \"${DIRS}\") + " COMPONENT Runtime) +endif() +set(CPACK_BINARY_DRAGNDROP ON) +include(CPack)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/CMakeLists.txt Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,49 @@ + +# Copyright (C) 2016 by ETH Zürich +# Software engineering by Intevation GmbH +# +# This file is Free Software under the GNU GPL (v>=2) +# and comes with ABSOLUTELY NO WARRANTY! +# See LICENSE.txt for details. + +set (qtxslx_SRC + xlsxabstractooxmlfile.cpp + xlsxabstractsheet.cpp + xlsxcell.cpp + xlsxcellformula.cpp + xlsxcellrange.cpp + xlsxcellreference.cpp + xlsxchart.cpp + xlsxchartsheet.cpp + xlsxcolor.cpp + xlsxconditionalformatting.cpp + xlsxcontenttypes.cpp + xlsxdatavalidation.cpp + xlsxdocpropsapp.cpp + xlsxdocpropscore.cpp + xlsxdocument.cpp + xlsxdrawinganchor.cpp + xlsxdrawing.cpp + xlsxformat.cpp + xlsxmediafile.cpp + xlsxnumformatparser.cpp + xlsxrelationships.cpp + xlsxrichstring.cpp + xlsxsharedstrings.cpp + xlsxsimpleooxmlfile.cpp + xlsxstyles.cpp + xlsxtheme.cpp + xlsxutility.cpp + xlsxworkbook.cpp + xlsxworksheet.cpp + xlsxzipreader.cpp + xlsxzipwriter.cpp +) +include_directories(${Qt5Gui_INCLUDE_DIRS}) +include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) + +add_library(${PROJECT_NAME}_libqtxslx STATIC ${qtxslx_SRC}) +target_link_libraries(${PROJECT_NAME}_libqtxslx + Qt5::Core + Qt5::Gui +)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/LICENSE Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,24 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/README Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,5 @@ +This is the QtXlsxWriter library from http://qtxlsx.debao.me + +Based on git rev. ad90b6a + +Added CMake Build system for tooling convenience.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxabstractooxmlfile.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,119 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "xlsxabstractooxmlfile.h" +#include "xlsxabstractooxmlfile_p.h" + +#include <QBuffer> +#include <QByteArray> + +QT_BEGIN_NAMESPACE_XLSX + +AbstractOOXmlFilePrivate::AbstractOOXmlFilePrivate(AbstractOOXmlFile *q, AbstractOOXmlFile::CreateFlag flag=AbstractOOXmlFile::F_NewFromScratch) + :relationships(new Relationships), flag(flag), q_ptr(q) +{ + +} + +AbstractOOXmlFilePrivate::~AbstractOOXmlFilePrivate() +{ + +} + +/*! + * \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() +{ + if (d_ptr->relationships) + delete d_ptr->relationships; + 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxabstractooxmlfile.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,70 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QXLSX_XLSXABSTRACTOOXMLFILE_H +#define QXLSX_XLSXABSTRACTOOXMLFILE_H + +#include "xlsxglobal.h" + +class QIODevice; +class QByteArray; + +QT_BEGIN_NAMESPACE_XLSX +class Relationships; +class AbstractOOXmlFilePrivate; + +class Q_XLSX_EXPORT AbstractOOXmlFile +{ + Q_DECLARE_PRIVATE(AbstractOOXmlFile) +public: + enum CreateFlag + { + F_NewFromScratch, + F_LoadFromExists + }; + + 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxabstractooxmlfile_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,64 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef XLSXOOXMLFILE_P_H +#define XLSXOOXMLFILE_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 "xlsxabstractooxmlfile.h" +#include "xlsxrelationships_p.h" + +#include <QString> + +QT_BEGIN_NAMESPACE_XLSX + +class XLSX_AUTOTEST_EXPORT AbstractOOXmlFilePrivate +{ + Q_DECLARE_PUBLIC(AbstractOOXmlFile) + +public: + AbstractOOXmlFilePrivate(AbstractOOXmlFile *q, AbstractOOXmlFile::CreateFlag flag); + virtual ~AbstractOOXmlFilePrivate(); + + QString filePathInPackage;//such as "xl/worksheets/sheet1.xml" + //used when load the .xlsx file + Relationships *relationships; + AbstractOOXmlFile::CreateFlag flag; + AbstractOOXmlFile *q_ptr; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXOOXMLFILE_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxabstractsheet.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,206 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "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.data(); +} + +/*! + * Return the workbook + */ +Workbook *AbstractSheet::workbook() const +{ + Q_D(const AbstractSheet); + return d->workbook; +} + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxabstractsheet.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,76 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXABSTRACTSHEET_H +#define XLSXABSTRACTSHEET_H + +#include "xlsxabstractooxmlfile.h" +#include <QStringList> +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE_XLSX +class Workbook; +class Drawing; +class AbstractSheetPrivate; +class Q_XLSX_EXPORT AbstractSheet : public AbstractOOXmlFile +{ + Q_DECLARE_PRIVATE(AbstractSheet) +public: + enum SheetType { + ST_WorkSheet, + ST_ChartSheet, + ST_DialogSheet, + ST_MacroSheet + }; + + enum SheetState { + SS_Visible, + SS_Hidden, + SS_VeryHidden + }; + + 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); + + Workbook *workbook() const; + +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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxabstractsheet_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,64 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXABSTRACTSHEET_P_H +#define XLSXABSTRACTSHEET_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 "xlsxglobal.h" +#include "xlsxabstractsheet.h" +#include "xlsxabstractooxmlfile_p.h" + +#include <QSharedPointer> + +namespace QXlsx { + +class XLSX_AUTOTEST_EXPORT AbstractSheetPrivate : public AbstractOOXmlFilePrivate +{ + Q_DECLARE_PUBLIC(AbstractSheet) +public: + AbstractSheetPrivate(AbstractSheet *p, AbstractSheet::CreateFlag flag); + ~AbstractSheetPrivate(); + + Workbook *workbook; + QSharedPointer<Drawing> drawing; + + QString name; + int id; + AbstractSheet::SheetState sheetState; + AbstractSheet::SheetType type; +}; + +} +#endif // XLSXABSTRACTSHEET_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcell.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,178 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxcell.h" +#include "xlsxcell_p.h" +#include "xlsxformat.h" +#include "xlsxformat_p.h" +#include "xlsxutility_p.h" +#include "xlsxworksheet.h" +#include "xlsxworkbook.h" +#include <QDateTime> + +QT_BEGIN_NAMESPACE_XLSX + +CellPrivate::CellPrivate(Cell *p) : + q_ptr(p) +{ + +} + +CellPrivate::CellPrivate(const CellPrivate * const cp) + : value(cp->value), formula(cp->formula), cellType(cp->cellType) + , format(cp->format), richString(cp->richString), parent(cp->parent) +{ + +} + +/*! + \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. + */ +Cell::Cell(const QVariant &data, CellType type, const Format &format, Worksheet *parent) : + d_ptr(new CellPrivate(this)) +{ + d_ptr->value = data; + d_ptr->cellType = type; + d_ptr->format = format; + d_ptr->parent = parent; +} + +/*! + * \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() +{ + 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 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); + if (d->cellType == NumberType && d->value.toDouble() >=0 + && d->format.isValid() && d->format.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()); +} + +/*! + * 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(); +} + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcell.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,77 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_XLSXCELL_H +#define QXLSX_XLSXCELL_H + +#include "xlsxglobal.h" +#include "xlsxformat.h" +#include <QVariant> + +QT_BEGIN_NAMESPACE_XLSX + +class Worksheet; +class Format; +class CellFormula; +class CellPrivate; +class WorksheetPrivate; + +class Q_XLSX_EXPORT Cell +{ + Q_DECLARE_PRIVATE(Cell) +public: + enum CellType { + BooleanType, //t="b" + NumberType, //t="n" (default) + ErrorType, //t="e" + SharedStringType, //t="s" + StringType, //t="str" + InlineStringType //t="inlineStr" + }; + + CellType cellType() const; + QVariant value() const; + Format format() const; + + bool hasFormula() const; + CellFormula formula() const; + + bool isDateTime() const; + QDateTime dateTime() const; + + bool isRichString() const; + + ~Cell(); +private: + friend class Worksheet; + friend class WorksheetPrivate; + + Cell(const QVariant &data=QVariant(), CellType type=NumberType, const Format &format=Format(), Worksheet *parent=0); + Cell(const Cell * const cell); + CellPrivate * const d_ptr; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXCELL_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcell_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,69 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXCELL_P_H +#define XLSXCELL_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 "xlsxglobal.h" +#include "xlsxcell.h" +#include "xlsxcellrange.h" +#include "xlsxrichstring.h" +#include "xlsxcellformula.h" +#include <QList> +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE_XLSX + +class CellPrivate +{ + Q_DECLARE_PUBLIC(Cell) +public: + CellPrivate(Cell *p); + CellPrivate(const CellPrivate * const cp); + + QVariant value; + CellFormula formula; + Cell::CellType cellType; + Format format; + + RichString richString; + + Worksheet *parent; + Cell *q_ptr; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXCELL_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcellformula.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,259 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxcellformula.h" +#include "xlsxcellformula_p.h" +#include "xlsxutility_p.h" + +#include <QXmlStreamReader> +#include <QXmlStreamWriter> + +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; +} + +/*! + * \internal + */ +bool CellFormula::saveToXml(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("f")); + QString t; + switch (d->type) { + case CellFormula::ArrayType: + t = QStringLiteral("array"); + break; + case CellFormula::SharedType: + t = QStringLiteral("shared"); + break; + default: + break; + } + if (!t.isEmpty()) + writer.writeAttribute(QStringLiteral("t"), t); + if (d->reference.isValid()) + writer.writeAttribute(QStringLiteral("ref"), d->reference.toString()); + if (d->ca) + writer.writeAttribute(QStringLiteral("ca"), QStringLiteral("1")); + if (d->type == CellFormula::SharedType) + writer.writeAttribute(QStringLiteral("si"), QString::number(d->si)); + + if (!d->formula.isEmpty()) + writer.writeCharacters(d->formula); + + writer.writeEndElement(); //f + + return true; +} + +/*! + * \internal + */ +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(); + if (typeString == QLatin1String("array")) + d->type = ArrayType; + else if (typeString == QLatin1String("shared")) + d->type = SharedType; + else + d->type = NormalType; + + if (attributes.hasAttribute(QLatin1String("ref"))) { + QString refString = attributes.value(QLatin1String("ref")).toString(); + d->reference = CellRange(refString); + } + + QString ca = attributes.value(QLatin1String("si")).toString(); + d->ca = parseXsdBoolean(ca, false); + + if (attributes.hasAttribute(QLatin1String("si"))) + d->si = attributes.value(QLatin1String("si")).toString().toInt(); + + d->formula = reader.readElementText(); + 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcellformula.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,78 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_XLSXCELLFORMULA_H +#define QXLSX_XLSXCELLFORMULA_H + +#include "xlsxglobal.h" +#include <QExplicitlySharedDataPointer> + +class QXmlStreamWriter; +class QXmlStreamReader; + +QT_BEGIN_NAMESPACE_XLSX + +class CellFormulaPrivate; +class CellRange; +class Worksheet; +class WorksheetPrivate; + +class Q_XLSX_EXPORT CellFormula +{ +public: + enum FormulaType { + NormalType, + ArrayType, + DataTableType, + SharedType + }; + + 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(); + 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<CellFormulaPrivate> d; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXCELLFORMULA_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcellformula_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,64 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXCELLFORMULA_P_H +#define XLSXCELLFORMULA_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 "xlsxglobal.h" +#include "xlsxcellformula.h" +#include "xlsxcellrange.h" + +#include <QSharedData> +#include <QString> + +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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcellrange.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,147 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxcellrange.h" +#include "xlsxcellreference.h" +#include <QString> +#include <QPoint> +#include <QStringList> + +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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcellrange.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,79 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_XLSXCELLRANGE_H +#define QXLSX_XLSXCELLRANGE_H +#include "xlsxglobal.h" +#include "xlsxcellreference.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Q_XLSX_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 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, left, bottom, right; +}; + +QT_END_NAMESPACE_XLSX + +Q_DECLARE_TYPEINFO(QXlsx::CellRange, Q_MOVABLE_TYPE); + +#endif // QXLSX_XLSXCELLRANGE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcellreference.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,174 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxcellreference.h" +#include <QStringList> +#include <QMap> +#include <QRegularExpression> + +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 QMap<int, QString> col_cache; + + if (!col_cache.contains(col_num)) { + 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; + } + col_cache.insert(col_num, col_str); + } + + return col_cache[col_num]; +} + +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 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcellreference.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,66 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_XLSXCELLREFERENCE_H +#define QXLSX_XLSXCELLREFERENCE_H +#include "xlsxglobal.h" + +QT_BEGIN_NAMESPACE_XLSX + +class Q_XLSX_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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxchart.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,645 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "xlsxchart_p.h" +#include "xlsxworksheet.h" +#include "xlsxcellrange.h" +#include "xlsxutility_p.h" + +#include <QIODevice> +#include <QXmlStreamReader> +#include <QXmlStreamWriter> +#include <QDebug> + +QT_BEGIN_NAMESPACE_XLSX + +ChartPrivate::ChartPrivate(Chart *q, Chart::CreateFlag flag) + :AbstractOOXmlFilePrivate(q, flag), chartType(static_cast<Chart::ChartType>(0)) +{ + +} + +ChartPrivate::~ChartPrivate() +{ + +} + +/*! + * \class Chart + * \inmodule QtXlsx + * \brief Main class for the charts. + */ + +/*! + \enum Chart::ChartType + + \value CT_Area + \value CT_Area3D, + \value CT_Line, + \value CT_Line3D, + \value CT_Scatter, + \value CT_Pie, + \value CT_Pie3D, + \value CT_Doughnut, + \value CT_Bar, + \value CT_Bar3D, + + \omitvalue CT_Stock, + \omitvalue CT_Radar, + \omitvalue CT_OfPie, + \omitvalue CT_Surface, + \omitvalue CT_Surface3D, + \omitvalue CT_Bubble +*/ + +/*! + * \internal + */ +Chart::Chart(AbstractSheet *parent, CreateFlag flag) + :AbstractOOXmlFile(new ChartPrivate(this, flag)) +{ + d_func()->sheet = parent; +} + +/*! + * Destroys the chart. + */ +Chart::~Chart() +{ +} + +/*! + * Add the data series which is in the range \a range of the \a sheet. + */ +void Chart::addSeries(const CellRange &range, AbstractSheet *sheet) +{ + Q_D(Chart); + if (!range.isValid()) + return; + if (sheet && sheet->sheetType() != AbstractSheet::ST_WorkSheet) + return; + if (!sheet && d->sheet->sheetType() != AbstractSheet::ST_WorkSheet) + return; + + QString sheetName = sheet ? sheet->sheetName() : d->sheet->sheetName(); + //In case sheetName contains space or ' + sheetName = escapeSheetName(sheetName); + + if (range.columnCount() == 1 || range.rowCount() == 1) { + QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries); + series->numberDataSource_numRef = sheetName + QLatin1String("!") + range.toString(true, true); + d->seriesList.append(series); + } else if (range.columnCount() < range.rowCount()) { + //Column based series + int firstDataColumn = range.firstColumn(); + QString axDataSouruce_numRef; + if (d->chartType == CT_Scatter || d->chartType == CT_Bubble) { + firstDataColumn += 1; + CellRange subRange(range.firstRow(), range.firstColumn(), range.lastRow(), range.firstColumn()); + axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + } + + for (int col=firstDataColumn; col<=range.lastColumn(); ++col) { + CellRange subRange(range.firstRow(), col, range.lastRow(), col); + QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries); + series->axDataSource_numRef = axDataSouruce_numRef; + series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + d->seriesList.append(series); + } + + } else { + //Row based series + int firstDataRow = range.firstRow(); + QString axDataSouruce_numRef; + if (d->chartType == CT_Scatter || d->chartType == CT_Bubble) { + firstDataRow += 1; + CellRange subRange(range.firstRow(), range.firstColumn(), range.firstRow(), range.lastColumn()); + axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + } + + for (int row=firstDataRow; row<=range.lastRow(); ++row) { + CellRange subRange(row, range.firstColumn(), row, range.lastColumn()); + QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries); + series->axDataSource_numRef = axDataSouruce_numRef; + series->numberDataSource_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true); + d->seriesList.append(series); + } + } +} + +/*! + * Set the type of the chart to \a type + */ +void Chart::setChartType(ChartType type) +{ + Q_D(Chart); + d->chartType = type; +} + +/*! + * \internal + * + */ +void Chart::setChartStyle(int id) +{ + Q_UNUSED(id) + //!Todo +} + +/*! + * \internal + */ +void Chart::saveToXmlFile(QIODevice *device) const +{ + Q_D(const Chart); + + QXmlStreamWriter writer(device); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("c:chartSpace")); + writer.writeAttribute(QStringLiteral("xmlns:c"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart")); + writer.writeAttribute(QStringLiteral("xmlns:a"), QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main")); + writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships")); + + d->saveXmlChart(writer); + + writer.writeEndElement();//c:chartSpace + writer.writeEndDocument(); +} + +/*! + * \internal + */ +bool Chart::loadFromXmlFile(QIODevice *device) +{ + Q_D(Chart); + + QXmlStreamReader reader(device); + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("chart")) { + if (!d->loadXmlChart(reader)) + return false; + } + } + } + return true; +} + +bool ChartPrivate::loadXmlChart(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("chart")); + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("plotArea")) { + if (!loadXmlPlotArea(reader)) + return false; + } else if (reader.name() == QLatin1String("legend")) { + //!Todo + } + } else if (reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("chart")) { + break; + } + } + return true; +} + +bool ChartPrivate::loadXmlPlotArea(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("plotArea")); + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("layout")) { + //!ToDo + } else if (reader.name().endsWith(QLatin1String("Chart"))) { + //For pieChart, barChart, ... + loadXmlXxxChart(reader); + } else if (reader.name().endsWith(QLatin1String("Ax"))) { + //For valAx, catAx, serAx, dateAx + loadXmlAxis(reader); + } + + } else if (reader.tokenType() == QXmlStreamReader::EndElement && + reader.name() == QLatin1String("plotArea")) { + break; + } + } + return true; +} + +bool ChartPrivate::loadXmlXxxChart(QXmlStreamReader &reader) +{ + QStringRef name = reader.name(); + if (name == QLatin1String("pieChart")) chartType = Chart::CT_Pie; + else if (name == QLatin1String("pie3DChart")) chartType = Chart::CT_Pie3D; + else if (name == QLatin1String("barChart")) chartType = Chart::CT_Bar; + else if (name == QLatin1String("bar3DChart")) chartType = Chart::CT_Bar3D; + else if (name == QLatin1String("lineChart")) chartType = Chart::CT_Line; + else if (name == QLatin1String("line3DChart")) chartType = Chart::CT_Line3D; + else if (name == QLatin1String("scatterChart")) chartType = Chart::CT_Scatter; + else if (name == QLatin1String("areaChart")) chartType = Chart::CT_Area; + else if (name == QLatin1String("area3DChart")) chartType = Chart::CT_Area3D; + else if (name == QLatin1String("doughnutChart")) chartType = Chart::CT_Doughnut; + else qDebug()<<"Cann't load chart: "<<name; + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("ser")) { + loadXmlSer(reader); + } else if (reader.name() == QLatin1String("axId")) { + + } + } else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == name) { + break; + } + } + return true; +} + +bool ChartPrivate::loadXmlSer(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("ser")); + + QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries); + seriesList.append(series); + + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("ser"))) { + if (reader.readNextStartElement()) { + QStringRef name = reader.name(); + if (name == QLatin1String("cat") || name == QLatin1String("xVal")) { + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == name)) { + if (reader.readNextStartElement()) { + if (reader.name() == QLatin1String("numRef")) + series->axDataSource_numRef = loadXmlNumRef(reader); + } + } + } else if (name == QLatin1String("val") || name == QLatin1String("yVal")) { + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == name)) { + if (reader.readNextStartElement()) { + if (reader.name() == QLatin1String("numRef")) + series->numberDataSource_numRef = loadXmlNumRef(reader); + } + } + } else if (name == QLatin1String("extLst")) { + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == name)) { + reader.readNextStartElement(); + } + } + } + } + + return true; +} + + +QString ChartPrivate::loadXmlNumRef(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("numRef")); + + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("numRef"))) { + if (reader.readNextStartElement()) { + if (reader.name() == QLatin1String("f")) + return reader.readElementText(); + } + } + + return QString(); +} + +void ChartPrivate::saveXmlChart(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("c:chart")); + writer.writeStartElement(QStringLiteral("c:plotArea")); + switch (chartType) { + case Chart::CT_Pie: + case Chart::CT_Pie3D: + saveXmlPieChart(writer); + break; + case Chart::CT_Bar: + case Chart::CT_Bar3D: + saveXmlBarChart(writer); + break; + case Chart::CT_Line: + case Chart::CT_Line3D: + saveXmlLineChart(writer); + break; + case Chart::CT_Scatter: + saveXmlScatterChart(writer); + break; + case Chart::CT_Area: + case Chart::CT_Area3D: + saveXmlAreaChart(writer); + break; + case Chart::CT_Doughnut: + saveXmlDoughnutChart(writer); + break; + default: + break; + } + saveXmlAxes(writer); + writer.writeEndElement(); //plotArea + +// saveXmlLegend(writer); + + writer.writeEndElement(); //chart +} + +void ChartPrivate::saveXmlPieChart(QXmlStreamWriter &writer) const +{ + QString name = chartType==Chart::CT_Pie ? QStringLiteral("c:pieChart") : QStringLiteral("c:pie3DChart"); + + writer.writeStartElement(name); + + //Do the same behavior as Excel, Pie prefer varyColors + writer.writeEmptyElement(QStringLiteral("c:varyColors")); + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1")); + + for (int i=0; i<seriesList.size(); ++i) + saveXmlSer(writer, seriesList[i].data(), i); + + writer.writeEndElement(); //pieChart, pie3DChart +} + +void ChartPrivate::saveXmlBarChart(QXmlStreamWriter &writer) const +{ + QString name = chartType==Chart::CT_Bar ? QStringLiteral("c:barChart") : QStringLiteral("c:bar3DChart"); + + writer.writeStartElement(name); + + writer.writeEmptyElement(QStringLiteral("c:barDir")); + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("col")); + + for (int i=0; i<seriesList.size(); ++i) + saveXmlSer(writer, seriesList[i].data(), i); + + if (axisList.isEmpty()) { + //The order the axes?? + const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1))); + const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0))); + } + + //Note: Bar3D have 2~3 axes + Q_ASSERT(axisList.size()==2 || (axisList.size()==3 && chartType==Chart::CT_Bar3D)); + + for (int i=0; i<axisList.size(); ++i) { + writer.writeEmptyElement(QStringLiteral("c:axId")); + writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId)); + } + + writer.writeEndElement(); //barChart, bar3DChart +} + +void ChartPrivate::saveXmlLineChart(QXmlStreamWriter &writer) const +{ + QString name = chartType==Chart::CT_Line ? QStringLiteral("c:lineChart") : QStringLiteral("c:line3DChart"); + + writer.writeStartElement(name); + + writer.writeEmptyElement(QStringLiteral("grouping")); + + for (int i=0; i<seriesList.size(); ++i) + saveXmlSer(writer, seriesList[i].data(), i); + + if (axisList.isEmpty()) { + const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1))); + const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0))); + if (chartType==Chart::CT_Line3D) + const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Ser, XlsxAxis::Bottom, 2, 0))); + } + + Q_ASSERT((axisList.size()==2||chartType==Chart::CT_Line)|| (axisList.size()==3 && chartType==Chart::CT_Line3D)); + + for (int i=0; i<axisList.size(); ++i) { + writer.writeEmptyElement(QStringLiteral("c:axId")); + writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId)); + } + + writer.writeEndElement(); //lineChart, line3DChart +} + +void ChartPrivate::saveXmlScatterChart(QXmlStreamWriter &writer) const +{ + const QString name = QStringLiteral("c:scatterChart"); + + writer.writeStartElement(name); + + writer.writeEmptyElement(QStringLiteral("c:scatterStyle")); + + for (int i=0; i<seriesList.size(); ++i) + saveXmlSer(writer, seriesList[i].data(), i); + + if (axisList.isEmpty()) { + const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Bottom, 0, 1))); + const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0))); + } + + Q_ASSERT(axisList.size()==2); + + for (int i=0; i<axisList.size(); ++i) { + writer.writeEmptyElement(QStringLiteral("c:axId")); + writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId)); + } + + writer.writeEndElement(); //c:scatterChart +} + +void ChartPrivate::saveXmlAreaChart(QXmlStreamWriter &writer) const +{ + QString name = chartType==Chart::CT_Area ? QStringLiteral("c:areaChart") : QStringLiteral("c:area3DChart"); + + writer.writeStartElement(name); + + writer.writeEmptyElement(QStringLiteral("grouping")); + + for (int i=0; i<seriesList.size(); ++i) + saveXmlSer(writer, seriesList[i].data(), i); + + if (axisList.isEmpty()) { + const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1))); + const_cast<ChartPrivate*>(this)->axisList.append(QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0))); + } + + //Note: Area3D have 2~3 axes + Q_ASSERT(axisList.size()==2 || (axisList.size()==3 && chartType==Chart::CT_Area3D)); + + for (int i=0; i<axisList.size(); ++i) { + writer.writeEmptyElement(QStringLiteral("c:axId")); + writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId)); + } + + writer.writeEndElement(); //lineChart, line3DChart +} + +void ChartPrivate::saveXmlDoughnutChart(QXmlStreamWriter &writer) const +{ + QString name = QStringLiteral("c:doughnutChart"); + + writer.writeStartElement(name); + + writer.writeEmptyElement(QStringLiteral("c:varyColors")); + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1")); + + for (int i=0; i<seriesList.size(); ++i) + saveXmlSer(writer, seriesList[i].data(), i); + + writer.writeStartElement(QStringLiteral("c:holeSize")); + writer.writeAttribute(QStringLiteral("val"), QString::number(50)); + + writer.writeEndElement(); +} + +void ChartPrivate::saveXmlSer(QXmlStreamWriter &writer, XlsxSeries *ser, int id) const +{ + writer.writeStartElement(QStringLiteral("c:ser")); + writer.writeEmptyElement(QStringLiteral("c:idx")); + writer.writeAttribute(QStringLiteral("val"), QString::number(id)); + writer.writeEmptyElement(QStringLiteral("c:order")); + writer.writeAttribute(QStringLiteral("val"), QString::number(id)); + + if (!ser->axDataSource_numRef.isEmpty()) { + if (chartType == Chart::CT_Scatter || chartType == Chart::CT_Bubble) + writer.writeStartElement(QStringLiteral("c:xVal")); + else + writer.writeStartElement(QStringLiteral("c:cat")); + writer.writeStartElement(QStringLiteral("c:numRef")); + writer.writeTextElement(QStringLiteral("c:f"), ser->axDataSource_numRef); + writer.writeEndElement();//c:numRef + writer.writeEndElement();//c:cat or c:xVal + } + + if (!ser->numberDataSource_numRef.isEmpty()) { + if (chartType == Chart::CT_Scatter || chartType == Chart::CT_Bubble) + writer.writeStartElement(QStringLiteral("c:yVal")); + else + writer.writeStartElement(QStringLiteral("c:val")); + writer.writeStartElement(QStringLiteral("c:numRef")); + writer.writeTextElement(QStringLiteral("c:f"), ser->numberDataSource_numRef); + writer.writeEndElement();//c:numRef + writer.writeEndElement();//c:val or c:yVal + } + + writer.writeEndElement();//c:ser +} + +bool ChartPrivate::loadXmlAxis(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name().endsWith(QLatin1String("Ax"))); + QString name = reader.name().toString(); + + XlsxAxis *axis = new XlsxAxis; + if (name == QLatin1String("valAx")) + axis->type = XlsxAxis::T_Val; + else if (name == QLatin1String("catAx")) + axis->type = XlsxAxis::T_Cat; + else if (name == QLatin1String("serAx")) + axis->type = XlsxAxis::T_Ser; + else + axis->type = XlsxAxis::T_Date; + + axisList.append(QSharedPointer<XlsxAxis>(axis)); + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("axPos")) { + QXmlStreamAttributes attrs = reader.attributes(); + QStringRef pos = attrs.value(QLatin1String("val")); + if (pos==QLatin1String("l")) + axis->axisPos = XlsxAxis::Left; + else if (pos==QLatin1String("r")) + axis->axisPos = XlsxAxis::Right; + else if (pos==QLatin1String("b")) + axis->axisPos = XlsxAxis::Bottom; + else + axis->axisPos = XlsxAxis::Top; + } else if (reader.name() == QLatin1String("axId")) { + axis->axisId = reader.attributes().value(QLatin1String("val")).toString().toInt(); + } else if (reader.name() == QLatin1String("crossAx")) { + axis->crossAx = reader.attributes().value(QLatin1String("val")).toString().toInt(); + } + } else if (reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == name) { + break; + } + } + + return true; +} + +void ChartPrivate::saveXmlAxes(QXmlStreamWriter &writer) const +{ + for (int i=0; i<axisList.size(); ++i) { + XlsxAxis *axis = axisList[i].data(); + QString name; + switch (axis->type) { + case XlsxAxis::T_Cat: name = QStringLiteral("c:catAx"); break; + case XlsxAxis::T_Val: name = QStringLiteral("c:valAx"); break; + case XlsxAxis::T_Ser: name = QStringLiteral("c:serAx"); break; + case XlsxAxis::T_Date: name = QStringLiteral("c:dateAx"); break; + default: break; + } + + QString pos; + switch (axis->axisPos) { + case XlsxAxis::Top: pos = QStringLiteral("t"); break; + case XlsxAxis::Bottom: pos = QStringLiteral("b"); break; + case XlsxAxis::Left: pos = QStringLiteral("l"); break; + case XlsxAxis::Right: pos = QStringLiteral("r"); break; + default: break; + } + + writer.writeStartElement(name); + writer.writeEmptyElement(QStringLiteral("c:axId")); + writer.writeAttribute(QStringLiteral("val"), QString::number(axis->axisId)); + + writer.writeStartElement(QStringLiteral("c:scaling")); + writer.writeEmptyElement(QStringLiteral("c:orientation")); + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("minMax")); + writer.writeEndElement();//c:scaling + + writer.writeEmptyElement(QStringLiteral("c:axPos")); + writer.writeAttribute(QStringLiteral("val"), pos); + + writer.writeEmptyElement(QStringLiteral("c:crossAx")); + writer.writeAttribute(QStringLiteral("val"), QString::number(axis->crossAx)); + + writer.writeEndElement();//name + } +} + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxchart.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,89 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QXLSX_CHART_H +#define QXLSX_CHART_H + +#include "xlsxabstractooxmlfile.h" + +#include <QSharedPointer> + +class QXmlStreamReader; +class QXmlStreamWriter; + +QT_BEGIN_NAMESPACE_XLSX + +class AbstractSheet; +class Worksheet; +class ChartPrivate; +class CellRange; +class DrawingAnchor; + +class Q_XLSX_EXPORT Chart : public AbstractOOXmlFile +{ + Q_DECLARE_PRIVATE(Chart) + +public: + enum ChartType { + CT_Area = 1, //Zero is internally used for unknown types + CT_Area3D, + CT_Line, + CT_Line3D, + CT_Stock, + CT_Radar, + CT_Scatter, + CT_Pie, + CT_Pie3D, + CT_Doughnut, + CT_Bar, + CT_Bar3D, + CT_OfPie, + CT_Surface, + CT_Surface3D, + CT_Bubble + }; + + ~Chart(); + + void addSeries(const CellRange &range, AbstractSheet *sheet=0); + void setChartType(ChartType type); + void setChartStyle(int id); + + void saveToXmlFile(QIODevice *device) const; + bool loadFromXmlFile(QIODevice *device); + +private: + friend class AbstractSheet; + friend class Worksheet; + friend class Chartsheet; + friend class DrawingAnchor; + + Chart(AbstractSheet *parent, CreateFlag flag); +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_CHART_H +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxchart_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,125 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QXLSX_CHART_P_H +#define QXLSX_CHART_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 "xlsxabstractooxmlfile_p.h" +#include "xlsxchart.h" + +#include <QSharedPointer> + +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace QXlsx { + +class XlsxSeries +{ +public: + //At present, we care about number cell ranges only! + QString numberDataSource_numRef; //yval, val + QString axDataSource_numRef; //xval, cat +}; + +class XlsxAxis +{ +public: + enum Type + { + T_Cat, + T_Val, + T_Date, + T_Ser + }; + + enum Pos + { + Left, + Right, + Top, + Bottom + }; + + XlsxAxis(){} + + XlsxAxis(Type t, Pos p, int id, int crossId) + :type(t), axisPos(p), axisId(id), crossAx(crossId) + { + } + + Type type; + Pos axisPos; //l,r,b,t + int axisId; + int crossAx; +}; + +class ChartPrivate : public AbstractOOXmlFilePrivate +{ + Q_DECLARE_PUBLIC(Chart) + +public: + ChartPrivate(Chart *q, Chart::CreateFlag flag); + ~ChartPrivate(); + + bool loadXmlChart(QXmlStreamReader &reader); + bool loadXmlPlotArea(QXmlStreamReader &reader); + bool loadXmlXxxChart(QXmlStreamReader &reader); + bool loadXmlSer(QXmlStreamReader &reader); + QString loadXmlNumRef(QXmlStreamReader &reader); + bool loadXmlAxis(QXmlStreamReader &reader); + + void saveXmlChart(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 saveXmlAxes(QXmlStreamWriter &writer) const; + + Chart::ChartType chartType; + + QList<QSharedPointer<XlsxSeries> > seriesList; + QList<QSharedPointer<XlsxAxis> > axisList; + + AbstractSheet *sheet; +}; + +} // namespace QXlsx + +#endif // QXLSX_CHART_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxchartsheet.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,159 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxchartsheet.h" +#include "xlsxchartsheet_p.h" +#include "xlsxworkbook.h" +#include "xlsxutility_p.h" +#include "xlsxdrawing_p.h" +#include "xlsxdrawinganchor_p.h" +#include "xlsxchart.h" + +#include <QXmlStreamReader> +#include <QXmlStreamWriter> +#include <QDir> + +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 = QSharedPointer<Drawing>(new Drawing(this, flag)); + + DrawingAbsoluteAnchor *anchor = new DrawingAbsoluteAnchor(drawing(), DrawingAnchor::Picture); + + anchor->pos = QPoint(0, 0); + anchor->ext = QSize(9293679, 6068786); + + QSharedPointer<Chart> chart = QSharedPointer<Chart>(new Chart(this, flag)); + chart->setChartType(Chart::CT_Bar); + 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.data()); + 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; + QString path = QDir::cleanPath(splitPath(filePath())[0] + QLatin1String("/") + name); + d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_LoadFromExists)); + d->drawing->setFilePath(path); + } + } + } + + return true; +} + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxchartsheet.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,56 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXCHARTSHEET_H +#define XLSXCHARTSHEET_H + +#include "xlsxabstractsheet.h" +#include <QStringList> +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE_XLSX +class Workbook; +class DocumentPrivate; +class ChartsheetPrivate; +class Chart; +class Q_XLSX_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; + + void saveToXmlFile(QIODevice *device) const; + bool loadFromXmlFile(QIODevice *device); +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXCHARTSHEET_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxchartsheet_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,56 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXCHARTSHEET_P_H +#define XLSXCHARTSHEET_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 "xlsxglobal.h" +#include "xlsxchartsheet.h" +#include "xlsxabstractsheet_p.h" + +namespace QXlsx { + +class XLSX_AUTOTEST_EXPORT ChartsheetPrivate : public AbstractSheetPrivate +{ + Q_DECLARE_PUBLIC(Chartsheet) +public: + ChartsheetPrivate(Chartsheet *p, Chartsheet::CreateFlag flag); + ~ChartsheetPrivate(); + + Chart *chart; +}; + +} +#endif // XLSXCHARTSHEET_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcolor.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,198 @@ +#include "xlsxcolor_p.h" +#include "xlsxstyles_p.h" +#include "xlsxutility_p.h" + +#include <QDataStream> +#include <QXmlStreamReader> +#include <QXmlStreamWriter> +#include <QDebug> + +namespace QXlsx { + + +XlsxColor::XlsxColor(const QColor &color) +{ + if (color.isValid()) + val.setValue(color); +} + +XlsxColor::XlsxColor(const QString &theme, const QString &tint) + :val(QStringList()<<theme<<tint) +{ + +} + +XlsxColor::XlsxColor(int index) + :val(index) +{ + +} + +bool XlsxColor::isRgbColor() const +{ + if (val.userType() == qMetaTypeId<QColor>() && val.value<QColor>().isValid()) + return true; + return false; +} + +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 +{ + if (isRgbColor()) + return val.value<QColor>(); + return QColor(); +} + +int XlsxColor::indexedColor() const +{ + if (isIndexedColor()) + return val.toInt(); + return -1; +} + +QStringList XlsxColor::themeColor() const +{ + if (isThemeColor()) + return val.toStringList(); + return 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<QColor>()) { + writer.writeAttribute(QStringLiteral("rgb"), XlsxColor::toARGBString(val.value<QColor>())); + } 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) +{ + QXmlStreamAttributes attributes = reader.attributes(); + + if (attributes.hasAttribute(QLatin1String("rgb"))) { + QString colorString = attributes.value(QLatin1String("rgb")).toString(); + val.setValue(fromARGBString(colorString)); + } else if (attributes.hasAttribute(QLatin1String("indexed"))) { + int index = attributes.value(QLatin1String("indexed")).toString().toInt(); + val.setValue(index); + } else if (attributes.hasAttribute(QLatin1String("theme"))) { + QString theme = attributes.value(QLatin1String("theme")).toString(); + QString tint = attributes.value(QLatin1String("tint")).toString(); + val.setValue(QStringList()<<theme<<tint); + } + return true; +} + +XlsxColor::operator QVariant() const +{ + return QVariant(qMetaTypeId<XlsxColor>(), this); +} + + +QColor XlsxColor::fromARGBString(const QString &c) +{ + Q_ASSERT(c.length() == 8); + QColor color; + color.setAlpha(c.mid(0, 2).toInt(0, 16)); + color.setRed(c.mid(2, 2).toInt(0, 16)); + color.setGreen(c.mid(4, 2).toInt(0, 16)); + color.setBlue(c.mid(6, 2).toInt(0, 16)); + return color; +} + +QString XlsxColor::toARGBString(const QColor &c) +{ + QString color; + color.sprintf("%02X%02X%02X%02X", c.alpha(), c.red(), c.green(), c.blue()); + return color; +} + +#if !defined(QT_NO_DATASTREAM) +QDataStream &operator<<(QDataStream &s, const XlsxColor &color) +{ + if (color.isInvalid()) + s<<0; + else if (color.isRgbColor()) + s<<1<<color.rgbColor(); + else if (color.isIndexedColor()) + s<<2<<color.indexedColor(); + else if (color.isThemeColor()) + s<<3<<color.themeColor(); + else + s<<4; + + return s; +} + +QDataStream &operator>>(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 + +} // namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcolor_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,92 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QXLSX_XLSXCOLOR_P_H +#define QXLSX_XLSXCOLOR_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 "xlsxglobal.h" +#include <QVariant> +#include <QColor> + +class QXmlStreamWriter; +class QXmlStreamReader; + +namespace QXlsx { + +class Styles; + +class Q_XLSX_EXPORT 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) +Q_XLSX_EXPORT QDataStream &operator<<(QDataStream &, const XlsxColor &); +Q_XLSX_EXPORT QDataStream &operator>>(QDataStream &, XlsxColor &); +#endif + +#ifndef QT_NO_DEBUG_STREAM +Q_XLSX_EXPORT QDebug operator<<(QDebug dbg, const XlsxColor &c); +#endif + +} // namespace QXlsx + +Q_DECLARE_METATYPE(QXlsx::XlsxColor) + +#endif // QXLSX_XLSXCOLOR_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxconditionalformatting.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,735 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "xlsxconditionalformatting.h" +#include "xlsxconditionalformatting_p.h" +#include "xlsxworksheet.h" +#include "xlsxcellrange.h" +#include "xlsxstyles_p.h" + +#include <QXmlStreamReader> +#include <QXmlStreamWriter> +#include <QDebug> + +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; + + QSharedPointer<XlsxCfRuleData> cfRule(new XlsxCfRuleData); + 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) +{ + QSharedPointer<XlsxCfRuleData> cfRule(new XlsxCfRuleData); + + 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"); + + QSharedPointer<XlsxCfRuleData> cfRule(new XlsxCfRuleData); + + 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"); + + QSharedPointer<XlsxCfRuleData> cfRule(new XlsxCfRuleData); + + 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<CellRange> 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")).toString().toInt(); + if (styles) + rule->dxfFormat = styles->dxfFormat(id); + else + rule->dxfFormat.setDxfIndex(id); + } + rule->priority = attrs.value(QLatin1String("priority")).toString().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")) { + 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_cfvo2] = 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(); + QString sqref = attrs.value(QLatin1String("sqref")).toString(); + foreach (QString range, sqref.split(QLatin1Char(' '))) + this->addRange(range); + + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("cfRule")) { + QSharedPointer<XlsxCfRuleData> cfRule(new XlsxCfRuleData); + d->readCfRule(reader, cfRule.data(), 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; + foreach (CellRange range, ranges()) + sqref.append(range.toString()); + writer.writeAttribute(QStringLiteral("sqref"), sqref.join(QLatin1Char(' '))); + + for (int i=0; i<d->cfRules.size(); ++i) { + const QSharedPointer<XlsxCfRuleData> &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)); + if (rule->attrs.contains(XlsxCfRuleData::A_stopIfTrue)) + writer.writeAttribute(QStringLiteral("stopIfTrue"), rule->attrs[XlsxCfRuleData::A_stopIfTrue].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_aboveAverage)) + writer.writeAttribute(QStringLiteral("aboveAverage"), rule->attrs[XlsxCfRuleData::A_aboveAverage].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_percent)) + writer.writeAttribute(QStringLiteral("percent"), rule->attrs[XlsxCfRuleData::A_percent].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_bottom)) + writer.writeAttribute(QStringLiteral("bottom"), rule->attrs[XlsxCfRuleData::A_bottom].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_operator)) + writer.writeAttribute(QStringLiteral("operator"), rule->attrs[XlsxCfRuleData::A_operator].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_text)) + writer.writeAttribute(QStringLiteral("text"), rule->attrs[XlsxCfRuleData::A_text].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_timePeriod)) + writer.writeAttribute(QStringLiteral("timePeriod"), rule->attrs[XlsxCfRuleData::A_timePeriod].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_rank)) + writer.writeAttribute(QStringLiteral("rank"), rule->attrs[XlsxCfRuleData::A_rank].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_stdDev)) + writer.writeAttribute(QStringLiteral("stdDev"), rule->attrs[XlsxCfRuleData::A_stdDev].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_equalAverage)) + writer.writeAttribute(QStringLiteral("equalAverage"), rule->attrs[XlsxCfRuleData::A_equalAverage].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<XlsxCfVoData>()); + d->writeCfVo(writer, rule->attrs[XlsxCfRuleData::A_cfvo2].value<XlsxCfVoData>()); + rule->attrs[XlsxCfRuleData::A_color1].value<XlsxColor>().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<XlsxCfVoData>()); + d->writeCfVo(writer, rule->attrs[XlsxCfRuleData::A_cfvo2].value<XlsxCfVoData>()); + if (rule->attrs.contains(XlsxCfRuleData::A_cfvo3)) + d->writeCfVo(writer, rule->attrs[XlsxCfRuleData::A_cfvo3].value<XlsxCfVoData>()); + + rule->attrs[XlsxCfRuleData::A_color1].value<XlsxColor>().saveToXml(writer); + rule->attrs[XlsxCfRuleData::A_color2].value<XlsxColor>().saveToXml(writer); + if (rule->attrs.contains(XlsxCfRuleData::A_color3)) + rule->attrs[XlsxCfRuleData::A_color3].value<XlsxColor>().saveToXml(writer); + + writer.writeEndElement();//colorScale + } + + + if (rule->attrs.contains(XlsxCfRuleData::A_formula1_temp)) { + QString startCell = ranges()[0].toString().split(QLatin1Char(':'))[0]; + writer.writeTextElement(QStringLiteral("formula"), rule->attrs[XlsxCfRuleData::A_formula1_temp].toString().arg(startCell)); + } else if (rule->attrs.contains(XlsxCfRuleData::A_formula1)) { + writer.writeTextElement(QStringLiteral("formula"), rule->attrs[XlsxCfRuleData::A_formula1].toString()); + } + if (rule->attrs.contains(XlsxCfRuleData::A_formula2)) + writer.writeTextElement(QStringLiteral("formula"), rule->attrs[XlsxCfRuleData::A_formula2].toString()); + if (rule->attrs.contains(XlsxCfRuleData::A_formula3)) + writer.writeTextElement(QStringLiteral("formula"), rule->attrs[XlsxCfRuleData::A_formula3].toString()); + + writer.writeEndElement(); //cfRule + } + + writer.writeEndElement(); //conditionalFormatting + return true; +} + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxconditionalformatting.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,135 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_XLSXCONDITIONALFORMATTING_H +#define QXLSX_XLSXCONDITIONALFORMATTING_H + +#include "xlsxglobal.h" +#include "xlsxcellrange.h" +#include "xlsxcellreference.h" +#include <QSharedDataPointer> +#include <QString> +#include <QList> + +class QXmlStreamReader; +class QXmlStreamWriter; +class QColor; +class ConditionalFormattingTest; + +QT_BEGIN_NAMESPACE_XLSX + +class Format; +class Worksheet; +class Styles; + +class ConditionalFormattingPrivate; +class Q_XLSX_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 + }; + + ConditionalFormatting(); + ConditionalFormatting(const ConditionalFormatting &other); + ~ConditionalFormatting(); + + 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<CellRange> 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; + bool saveToXml(QXmlStreamWriter &writer) const; + bool loadFromXml(QXmlStreamReader &reader, Styles *styles=0); + QSharedDataPointer<ConditionalFormattingPrivate> d; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXCONDITIONALFORMATTING_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxconditionalformatting_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,131 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef XLSXCONDITIONALFORMATTING_P_H +#define XLSXCONDITIONALFORMATTING_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 "xlsxconditionalformatting.h" +#include "xlsxformat.h" +#include "xlsxcolor_p.h" +#include <QSharedData> +#include <QSharedPointer> +#include <QMap> + +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<int, QVariant> 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<QSharedPointer<XlsxCfRuleData> >cfRules; + QList<CellRange> ranges; +}; + +QT_END_NAMESPACE_XLSX + +Q_DECLARE_METATYPE(QXlsx::XlsxCfVoData) +#endif // XLSXCONDITIONALFORMATTING_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcontenttypes.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,205 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxcontenttypes_p.h" +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QFile> +#include <QMapIterator> +#include <QBuffer> +#include <QDebug> + +namespace QXlsx { + +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 + QStringLiteral("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 + QStringLiteral("extended-properties+xml")); +} + +void ContentTypes::addDocPropCore() +{ + addOverride(QStringLiteral("/docProps/core.xml"), m_package_prefix + QStringLiteral("core-properties+xml")); +} + +void ContentTypes::addStyles() +{ + addOverride(QStringLiteral("/xl/styles.xml"), m_document_prefix + QStringLiteral("spreadsheetml.styles+xml")); +} + +void ContentTypes::addTheme() +{ + addOverride(QStringLiteral("/xl/theme/theme1.xml"), m_document_prefix + QStringLiteral("theme+xml")); +} + +void ContentTypes::addWorkbook() +{ + addOverride(QStringLiteral("/xl/workbook.xml"), m_document_prefix + QStringLiteral("spreadsheetml.sheet.main+xml")); +} + +void ContentTypes::addWorksheetName(const QString &name) +{ + addOverride(QStringLiteral("/xl/worksheets/%1.xml").arg(name), m_document_prefix + QStringLiteral("spreadsheetml.worksheet+xml")); +} + +void ContentTypes::addChartsheetName(const QString &name) +{ + addOverride(QStringLiteral("/xl/chartsheets/%1.xml").arg(name), m_document_prefix + QStringLiteral("spreadsheetml.chartsheet+xml")); +} + +void ContentTypes::addDrawingName(const QString &name) +{ + addOverride(QStringLiteral("/xl/drawings/%1.xml").arg(name), m_document_prefix + QStringLiteral("drawing+xml")); +} + +void ContentTypes::addChartName(const QString &name) +{ + addOverride(QStringLiteral("/xl/charts/%1.xml").arg(name), m_document_prefix + QStringLiteral("drawingml.chart+xml")); +} + +void ContentTypes::addCommentName(const QString &name) +{ + addOverride(QStringLiteral("/xl/%1.xml").arg(name), m_document_prefix + QStringLiteral("spreadsheetml.comments+xml")); +} + +void ContentTypes::addTableName(const QString &name) +{ + addOverride(QStringLiteral("/xl/tables/%1.xml").arg(name), m_document_prefix + QStringLiteral("spreadsheetml.table+xml")); +} + +void ContentTypes::addExternalLinkName(const QString &name) +{ + addOverride(QStringLiteral("/xl/externalLinks/%1.xml").arg(name), m_document_prefix + QStringLiteral("spreadsheetml.externalLink+xml")); +} + +void ContentTypes::addSharedString() +{ + addOverride(QStringLiteral("/xl/sharedStrings.xml"), m_document_prefix + QStringLiteral("spreadsheetml.sharedStrings+xml")); +} + +void ContentTypes::addVmlName() +{ + addOverride(QStringLiteral("vml"), m_document_prefix + QStringLiteral("vmlDrawing")); +} + +void ContentTypes::addCalcChain() +{ + addOverride(QStringLiteral("/xl/calcChain.xml"), m_document_prefix + QStringLiteral("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<QString, QString> 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<QString, QString> 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()<<reader.errorString(); + } + } + return true; +} + +} //namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxcontenttypes_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,88 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXCONTENTTYPES_H +#define XLSXCONTENTTYPES_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 <QString> +#include <QStringList> +#include <QMap> + +class QIODevice; + +namespace QXlsx { + +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; + bool loadFromXmlFile(QIODevice *device); +private: + QMap<QString, QString> m_defaults; + QMap<QString, QString> m_overrides; + + QString m_package_prefix; + QString m_document_prefix; +}; + +} +#endif // XLSXCONTENTTYPES_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdatavalidation.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,552 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "xlsxdatavalidation.h" +#include "xlsxdatavalidation_p.h" +#include "xlsxworksheet.h" +#include "xlsxcellrange.h" + +#include <QXmlStreamReader> +#include <QXmlStreamWriter> + +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<CellRange> 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 QMap<DataValidation::ValidationType, QString> typeMap; + static QMap<DataValidation::ValidationOperator, QString> opMap; + static QMap<DataValidation::ErrorStyle, QString> esMap; + if (typeMap.isEmpty()) { + typeMap.insert(DataValidation::None, QStringLiteral("none")); + typeMap.insert(DataValidation::Whole, QStringLiteral("whole")); + typeMap.insert(DataValidation::Decimal, QStringLiteral("decimal")); + typeMap.insert(DataValidation::List, QStringLiteral("list")); + typeMap.insert(DataValidation::Date, QStringLiteral("date")); + typeMap.insert(DataValidation::Time, QStringLiteral("time")); + typeMap.insert(DataValidation::TextLength, QStringLiteral("textLength")); + typeMap.insert(DataValidation::Custom, QStringLiteral("custom")); + + opMap.insert(DataValidation::Between, QStringLiteral("between")); + opMap.insert(DataValidation::NotBetween, QStringLiteral("notBetween")); + opMap.insert(DataValidation::Equal, QStringLiteral("equal")); + opMap.insert(DataValidation::NotEqual, QStringLiteral("notEqual")); + opMap.insert(DataValidation::LessThan, QStringLiteral("lessThan")); + opMap.insert(DataValidation::LessThanOrEqual, QStringLiteral("lessThanOrEqual")); + opMap.insert(DataValidation::GreaterThan, QStringLiteral("greaterThan")); + opMap.insert(DataValidation::GreaterThanOrEqual, QStringLiteral("greaterThanOrEqual")); + + esMap.insert(DataValidation::Stop, QStringLiteral("stop")); + esMap.insert(DataValidation::Warning, QStringLiteral("warning")); + esMap.insert(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; + foreach (CellRange range, ranges()) + sqref.append(range.toString()); + writer.writeAttribute(QStringLiteral("sqref"), sqref.join(QLatin1Char(' '))); + + 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 QMap<QString, DataValidation::ValidationType> typeMap; + static QMap<QString, DataValidation::ValidationOperator> opMap; + static QMap<QString, DataValidation::ErrorStyle> esMap; + if (typeMap.isEmpty()) { + typeMap.insert(QStringLiteral("none"), DataValidation::None); + typeMap.insert(QStringLiteral("whole"), DataValidation::Whole); + typeMap.insert(QStringLiteral("decimal"), DataValidation::Decimal); + typeMap.insert(QStringLiteral("list"), DataValidation::List); + typeMap.insert(QStringLiteral("date"), DataValidation::Date); + typeMap.insert(QStringLiteral("time"), DataValidation::Time); + typeMap.insert(QStringLiteral("textLength"), DataValidation::TextLength); + typeMap.insert(QStringLiteral("custom"), DataValidation::Custom); + + opMap.insert(QStringLiteral("between"), DataValidation::Between); + opMap.insert(QStringLiteral("notBetween"), DataValidation::NotBetween); + opMap.insert(QStringLiteral("equal"), DataValidation::Equal); + opMap.insert(QStringLiteral("notEqual"), DataValidation::NotEqual); + opMap.insert(QStringLiteral("lessThan"), DataValidation::LessThan); + opMap.insert(QStringLiteral("lessThanOrEqual"), DataValidation::LessThanOrEqual); + opMap.insert(QStringLiteral("greaterThan"), DataValidation::GreaterThan); + opMap.insert(QStringLiteral("greaterThanOrEqual"), DataValidation::GreaterThanOrEqual); + + esMap.insert(QStringLiteral("stop"), DataValidation::Stop); + esMap.insert(QStringLiteral("warning"), DataValidation::Warning); + esMap.insert(QStringLiteral("information"), DataValidation::Information); + } + + DataValidation validation; + QXmlStreamAttributes attrs = reader.attributes(); + + QString sqref = attrs.value(QLatin1String("sqref")).toString(); + foreach (QString range, sqref.split(QLatin1Char(' '))) + validation.addRange(range); + + if (attrs.hasAttribute(QLatin1String("type"))) { + QString t = attrs.value(QLatin1String("type")).toString(); + validation.setValidationType(typeMap.contains(t) ? typeMap[t] : DataValidation::None); + } + if (attrs.hasAttribute(QLatin1String("errorStyle"))) { + QString es = attrs.value(QLatin1String("errorStyle")).toString(); + validation.setErrorStyle(esMap.contains(es) ? esMap[es] : DataValidation::Stop); + } + if (attrs.hasAttribute(QLatin1String("operator"))) { + QString op = attrs.value(QLatin1String("operator")).toString(); + validation.setValidationOperator(opMap.contains(op) ? opMap[op] : 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdatavalidation.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,123 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_XLSXDATAVALIDATION_H +#define QXLSX_XLSXDATAVALIDATION_H + +#include "xlsxglobal.h" +#include <QSharedDataPointer> +#include <QString> +#include <QList> + +class QXmlStreamReader; +class QXmlStreamWriter; + +QT_BEGIN_NAMESPACE_XLSX + +class Worksheet; +class CellRange; +class CellReference; + +class DataValidationPrivate; +class Q_XLSX_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<CellRange> 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<DataValidationPrivate> d; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXDATAVALIDATION_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdatavalidation_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,69 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef XLSXDATAVALIDATION_P_H +#define XLSXDATAVALIDATION_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 "xlsxdatavalidation.h" +#include <QSharedData> + +QT_BEGIN_NAMESPACE_XLSX + +class Q_XLSX_EXPORT 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<CellRange> ranges; +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXDATAVALIDATION_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdocpropsapp.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,157 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxdocpropsapp_p.h" + +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QDir> +#include <QFile> +#include <QDateTime> +#include <QVariant> +#include <QBuffer> + +namespace QXlsx { + +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(qMakePair(name, value)); +} + +bool DocPropsApp::setProperty(const QString &name, const QString &value) +{ + static QStringList validKeys; + if (validKeys.isEmpty()) { + 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 +{ + if (m_properties.contains(name)) + return m_properties[name]; + + 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")); + typedef QPair<QString,int> PairType; //Make foreach happy + foreach (PairType 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")); + foreach (QString title, m_titlesOfPartsList) + writer.writeTextElement(vt, QStringLiteral("lpstr"), title); + writer.writeEndElement();//vt:vector + writer.writeEndElement();//TitlesOfParts + + if (m_properties.contains(QStringLiteral("manager"))) + writer.writeTextElement(QStringLiteral("Manager"), m_properties[QStringLiteral("manager")]); + //Not like "manager", "company" always exists for Excel generated file. + writer.writeTextElement(QStringLiteral("Company"), m_properties.contains(QStringLiteral("company")) ? m_properties[QStringLiteral("company")]: 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; +} + +} //namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdocpropsapp_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,72 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXDOCPROPSAPP_H +#define XLSXDOCPROPSAPP_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 "xlsxabstractooxmlfile.h" +#include <QList> +#include <QPair> +#include <QStringList> +#include <QMap> + +class QIODevice; + +namespace QXlsx { + +class XLSX_AUTOTEST_EXPORT 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; + bool loadFromXmlFile(QIODevice *device); + +private: + QStringList m_titlesOfPartsList; + QList<QPair<QString, int> > m_headingPairsList; + QMap<QString, QString> m_properties; +}; + +} +#endif // XLSXDOCPROPSAPP_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdocpropscore.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,168 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxdocpropscore_p.h" + +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QDir> +#include <QFile> +#include <QDateTime> +#include <QDebug> +#include <QBuffer> + +namespace QXlsx { + +DocPropsCore::DocPropsCore(CreateFlag flag) + :AbstractOOXmlFile(flag) +{ +} + +bool DocPropsCore::setProperty(const QString &name, const QString &value) +{ + static QStringList validKeys; + if (validKeys.isEmpty()) { + 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 +{ + if (m_properties.contains(name)) + return m_properties[name]; + + 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")); + + if (m_properties.contains(QStringLiteral("title"))) + writer.writeTextElement(dc, QStringLiteral("title"), m_properties[QStringLiteral("title")]); + + if (m_properties.contains(QStringLiteral("subject"))) + writer.writeTextElement(dc, QStringLiteral("subject"), m_properties[QStringLiteral("subject")]); + + writer.writeTextElement(dc, QStringLiteral("creator"), m_properties.contains(QStringLiteral("creator")) ? m_properties[QStringLiteral("creator")] : QStringLiteral("Qt Xlsx Library")); + + if (m_properties.contains(QStringLiteral("keywords"))) + writer.writeTextElement(cp, QStringLiteral("keywords"), m_properties[QStringLiteral("keywords")]); + + if (m_properties.contains(QStringLiteral("description"))) + writer.writeTextElement(dc, QStringLiteral("description"), m_properties[QStringLiteral("description")]); + + writer.writeTextElement(cp, QStringLiteral("lastModifiedBy"), m_properties.contains(QStringLiteral("creator")) ? m_properties[QStringLiteral("creator")] : QStringLiteral("Qt Xlsx Library")); + + writer.writeStartElement(dcterms, QStringLiteral("created")); + writer.writeAttribute(xsi, QStringLiteral("type"), QStringLiteral("dcterms:W3CDTF")); + writer.writeCharacters(m_properties.contains(QStringLiteral("created")) ? m_properties[QStringLiteral("created")] : 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 + + if (m_properties.contains(QStringLiteral("category"))) + writer.writeTextElement(cp, QStringLiteral("category"), m_properties[QStringLiteral("category")]); + + if (m_properties.contains(QStringLiteral("status"))) + writer.writeTextElement(cp, QStringLiteral("contentStatus"), m_properties[QStringLiteral("status")]); + + 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 QStringRef nsUri = reader.namespaceUri(); + const QStringRef 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; +} + +} //namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdocpropscore_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,65 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXDOCPROPSCORE_H +#define XLSXDOCPROPSCORE_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 "xlsxabstractooxmlfile.h" +#include <QMap> +#include <QStringList> + +class QIODevice; + +namespace QXlsx { + +class XLSX_AUTOTEST_EXPORT 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; + bool loadFromXmlFile(QIODevice *device); + +private: + QMap<QString, QString> m_properties; +}; + +} +#endif // XLSXDOCPROPSCORE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdocument.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,1046 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "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" + +#include <QFile> +#include <QPointF> +#include <QBuffer> +#include <QDir> + +QT_BEGIN_NAMESPACE_XLSX + +/* + 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. +*/ + +DocumentPrivate::DocumentPrivate(Document *p) : + q_ptr(p), defaultPackageName(QStringLiteral("Book1.xlsx")) +{ +} + +void DocumentPrivate::init() +{ + if (contentTypes.isNull()) + contentTypes = QSharedPointer<ContentTypes>(new ContentTypes(ContentTypes::F_NewFromScratch)); + + if (workbook.isNull()) + workbook = QSharedPointer<Workbook>(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 = QSharedPointer<ContentTypes>(new ContentTypes(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<XlsxRelationship> 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)); + foreach (QString name, props.propertyNames()) + q->setDocumentProperty(name, props.property(name)); + } + + //load app property + QList<XlsxRelationship> 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)); + foreach (QString name, props.propertyNames()) + 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<Workbook>(new Workbook(Workbook::F_LoadFromExists)); + QList<XlsxRelationship> rels_xl = rootRels.documentRelationships(QStringLiteral("/officeDocument")); + if (rels_xl.isEmpty()) + return false; + QString xlworkbook_Path = rels_xl[0].target; + QString xlworkbook_Dir = splitPath(xlworkbook_Path)[0]; + workbook->relationships()->loadFromXmlData(zipReader.fileData(getRelFilePath(xlworkbook_Path))); + workbook->setFilePath(xlworkbook_Path); + workbook->loadFromXmlData(zipReader.fileData(xlworkbook_Path)); + + //load styles + QList<XlsxRelationship> 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; + QString path = xlworkbook_Dir + QLatin1String("/") + name; + QSharedPointer<Styles> styles (new Styles(Styles::F_LoadFromExists)); + styles->loadFromXmlData(zipReader.fileData(path)); + workbook->d_func()->styles = styles; + } + + //load sharedStrings + QList<XlsxRelationship> 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<XlsxRelationship> 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; i<workbook->sheetCount(); ++i) { + AbstractSheet *sheet = workbook->sheet(i); + QString rel_path = getRelFilePath(sheet->filePath()); + //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; i<workbook->d_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; i<workbook->drawings().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<QSharedPointer<Chart> > chartFileToLoad = workbook->chartFiles(); + for (int i=0; i<chartFileToLoad.size(); ++i) { + QSharedPointer<Chart> cf = chartFileToLoad[i]; + cf->loadFromXmlData(zipReader.fileData(cf->filePath())); + } + + //load media files + QList<QSharedPointer<MediaFile> > mediaFileToLoad = workbook->mediaFiles(); + for (int i=0; i<mediaFileToLoad.size(); ++i) { + QSharedPointer<MediaFile> mf = mediaFileToLoad[i]; + const QString path = mf->fileName(); + const QString suffix = path.mid(path.lastIndexOf(QLatin1Char('.'))+1); + mf->set(zipReader.fileData(path), suffix); + } + + 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<QSharedPointer<AbstractSheet> > worksheets = workbook->getSheetsByTypes(AbstractSheet::ST_WorkSheet); + if (!worksheets.isEmpty()) + docPropsApp.addHeadingPair(QStringLiteral("Worksheets"), worksheets.size()); + for (int i=0; i<worksheets.size(); ++i) { + QSharedPointer<AbstractSheet> 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<QSharedPointer<AbstractSheet> > chartsheets = workbook->getSheetsByTypes(AbstractSheet::ST_ChartSheet); + if (!chartsheets.isEmpty()) + docPropsApp.addHeadingPair(QStringLiteral("Chartsheets"), chartsheets.size()); + for (int i=0; i<chartsheets.size(); ++i) { + QSharedPointer<AbstractSheet> 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; i<workbook->d_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; i<workbook->drawings().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 + foreach (QString name, q->documentPropertyNames()) { + 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 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; i<workbook->chartFiles().size(); ++i) { + contentTypes->addChartName(QStringLiteral("chart%1").arg(i+1)); + QSharedPointer<Chart> cf = workbook->chartFiles()[i]; + zipWriter.addFile(QStringLiteral("xl/charts/chart%1.xml").arg(i+1), cf->saveToXmlData()); + } + + // save image files + for (int i=0; i<workbook->mediaFiles().size(); ++i) { + QSharedPointer<MediaFile> mf = workbook->mediaFiles()[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; +} + + +/*! + \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)) + d_ptr->loadPackage(&xlsx); + } + 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()) + d_ptr->loadPackage(device); + 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. + */ +bool Document::insertImage(int row, int column, const QImage &image) +{ + if (Worksheet *sheet = currentWorksheet()) + return sheet->insertImage(row, column, image); + return false; +} + +/*! + * 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); + if (d->documentProperties.contains(key)) + return d->documentProperties[key]; + 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<Worksheet *>(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)); +} + +/*! + * 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); +} + +/*! + * Destroys the document and cleans up. + */ +Document::~Document() +{ + delete d_ptr; +} + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdocument.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,133 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QXLSX_XLSXDOCUMENT_H +#define QXLSX_XLSXDOCUMENT_H + +#include "xlsxglobal.h" +#include "xlsxformat.h" +#include "xlsxworksheet.h" +#include <QObject> +#include <QVariant> +class QIODevice; +class QImage; + +QT_BEGIN_NAMESPACE_XLSX + +class Workbook; +class Cell; +class CellRange; +class DataValidation; +class ConditionalFormatting; +class Chart; +class CellReference; + +class DocumentPrivate; +class Q_XLSX_EXPORT Document : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(Document) + +public: + explicit Document(QObject *parent = 0); + Document(const QString &xlsxName, QObject *parent=0); + Document(QIODevice *device, QObject *parent=0); + ~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; + bool insertImage(int row, int col, const QImage &image); + 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 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; + +private: + Q_DISABLE_COPY(Document) + DocumentPrivate * const d_ptr; +}; + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_XLSXDOCUMENT_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdocument_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,69 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef XLSXDOCUMENT_P_H +#define XLSXDOCUMENT_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 "xlsxdocument.h" +#include "xlsxworkbook.h" +#include "xlsxcontenttypes_p.h" + +#include <QMap> + +namespace QXlsx { + +class DocumentPrivate +{ + Q_DECLARE_PUBLIC(Document) +public: + DocumentPrivate(Document *p); + void init(); + + bool loadPackage(QIODevice *device); + bool savePackage(QIODevice *device) const; + + Document *q_ptr; + const QString defaultPackageName; //default name when package name not specified + QString packageName; //name of the .xlsx file + + QMap<QString, QString> documentProperties; //core, app and custom properties + QSharedPointer<Workbook> workbook; + QSharedPointer<ContentTypes> contentTypes; +}; + +} + +#endif // XLSXDOCUMENT_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdrawing.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,87 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "xlsxdrawing_p.h" +#include "xlsxdrawinganchor_p.h" +#include "xlsxabstractsheet.h" + +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QBuffer> + +namespace QXlsx { + +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")); + + foreach (DrawingAnchor *anchor, anchors) + anchor->saveToXml(writer); + + writer.writeEndElement();//xdr:wsDr + writer.writeEndDocument(); +} + +bool Drawing::loadFromXmlFile(QIODevice *device) +{ + QXmlStreamReader reader(device); + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("absoluteAnchor")) { + DrawingAbsoluteAnchor * anchor = new DrawingAbsoluteAnchor(this); + anchor->loadFromXml(reader); + } else if (reader.name() == QLatin1String("oneCellAnchor")) { + DrawingOneCellAnchor * anchor = new DrawingOneCellAnchor(this); + anchor->loadFromXml(reader); + } else if (reader.name() == QLatin1String("twoCellAnchor")) { + DrawingTwoCellAnchor * anchor = new DrawingTwoCellAnchor(this); + anchor->loadFromXml(reader); + } + } + } + + return true; +} + +} // namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdrawing_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,72 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QXLSX_DRAWING_H +#define QXLSX_DRAWING_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 "xlsxrelationships_p.h" +#include "xlsxabstractooxmlfile.h" + +#include <QList> +#include <QString> +#include <QSharedPointer> + +class QIODevice; +class QXmlStreamWriter; + +namespace QXlsx { + +class DrawingAnchor; +class Workbook; +class AbstractSheet; +class MediaFile; + +class Drawing : public AbstractOOXmlFile +{ +public: + Drawing(AbstractSheet *sheet, CreateFlag flag); + ~Drawing(); + void saveToXmlFile(QIODevice *device) const; + bool loadFromXmlFile(QIODevice *device); + + AbstractSheet *sheet; + Workbook *workbook; + QList<DrawingAnchor *> anchors; +}; + +} // namespace QXlsx + +#endif // QXLSX_DRAWING_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdrawinganchor.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,529 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "xlsxdrawinganchor_p.h" +#include "xlsxdrawing_p.h" +#include "xlsxmediafile_p.h" +#include "xlsxchart.h" +#include "xlsxworkbook.h" +#include "xlsxutility_p.h" + +#include <QXmlStreamReader> +#include <QXmlStreamWriter> +#include <QBuffer> +#include <QDir> + +namespace QXlsx { + +/* + 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 = QSharedPointer<MediaFile>(new MediaFile(ba, QStringLiteral("png"), QStringLiteral("image/png"))); + m_drawing->workbook->addMediaFile(m_pictureFile); + + m_objectType = Picture; +} + +void DrawingAnchor::setObjectGraphicFrame(QSharedPointer<Chart> chart) +{ + m_chartFile = chart; + m_drawing->workbook->addChartFile(chart); + + m_objectType = GraphicFrame; +} + +QPoint DrawingAnchor::loadXmlPos(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("pos")); + + QPoint pos; + QXmlStreamAttributes attrs = reader.attributes(); + pos.setX(attrs.value(QLatin1String("x")).toString().toInt()); + pos.setY(attrs.value(QLatin1String("y")).toString().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")).toString().toInt()); + size.setHeight(attrs.value(QLatin1String("cy")).toString().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; + loadXmlObjectShape(reader); + } 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; + loadXmlObjectConnectionShape(reader); + } else if (reader.name() == QLatin1String("pic")) { + //Picture + m_objectType = Picture; + loadXmlObjectPicture(reader); + } +} + +void DrawingAnchor::loadXmlObjectConnectionShape(QXmlStreamReader &reader) +{ + Q_UNUSED(reader) +} + +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; + QString path = QDir::cleanPath(splitPath(m_drawing->filePath())[0] + QLatin1String("/") + name); + + bool exist = false; + QList<QSharedPointer<Chart> > cfs = m_drawing->workbook->chartFiles(); + for (int i=0; i<cfs.size(); ++i) { + if (cfs[i]->filePath() == path) { + //already exist + exist = true; + m_chartFile = cfs[i]; + } + } + if (!exist) { + m_chartFile = QSharedPointer<Chart> (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; + QString path = QDir::cleanPath(splitPath(m_drawing->filePath())[0] + QLatin1String("/") + name); + + bool exist = false; + QList<QSharedPointer<MediaFile> > mfs = m_drawing->workbook->mediaFiles(); + for (int i=0; i<mfs.size(); ++i) { + if (mfs[i]->fileName() == path) { + //already exist + exist = true; + m_pictureFile = mfs[i]; + } + } + if (!exist) { + m_pictureFile = QSharedPointer<MediaFile> (new MediaFile(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_UNUSED(reader) +} + +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 +{ + Q_UNUSED(writer) +} + +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.isNull()); + + writer.writeStartElement(QStringLiteral("xdr:pic")); + + writer.writeStartElement(QStringLiteral("xdr:nvPicPr")); + writer.writeEmptyElement(QStringLiteral("xdr:cNvPr")); + writer.writeAttribute(QStringLiteral("id"), QString::number(m_id)); + 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 +} + +void DrawingAnchor::saveXmlObjectShape(QXmlStreamWriter &writer) const +{ + Q_UNUSED(writer) +} + +//absolute anchor + +DrawingAbsoluteAnchor::DrawingAbsoluteAnchor(Drawing *drawing, ObjectType objectType) + :DrawingAnchor(drawing, objectType) +{ + +} + +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) +{ + +} + +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) +{ + +} + +bool DrawingTwoCellAnchor::loadFromXml(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("twoCellAnchor")); + 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 { + 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")); + writer.writeAttribute(QStringLiteral("editAs"), 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 +} + +} // namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxdrawinganchor_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,151 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QXLSX_XLSXDRAWINGANCHOR_P_H +#define QXLSX_XLSXDRAWINGANCHOR_P_H + +#include "xlsxglobal.h" + +#include <QPoint> +#include <QSize> +#include <QString> +#include <QSharedPointer> + +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace QXlsx { + +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); + void setObjectGraphicFrame(QSharedPointer<Chart> chart); + + virtual bool loadFromXml(QXmlStreamReader &reader) = 0; + virtual void saveToXml(QXmlStreamWriter &writer) const = 0; + +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; + QSharedPointer<MediaFile> m_pictureFile; + QSharedPointer<Chart> m_chartFile; + + int m_id; +}; + +class DrawingAbsoluteAnchor : public DrawingAnchor +{ +public: + DrawingAbsoluteAnchor(Drawing *drawing, ObjectType objectType=Unknown); + + QPoint pos; + QSize ext; + + bool loadFromXml(QXmlStreamReader &reader); + void saveToXml(QXmlStreamWriter &writer) const; +}; + +class DrawingOneCellAnchor : public DrawingAnchor +{ +public: + DrawingOneCellAnchor(Drawing *drawing, ObjectType objectType=Unknown); + + XlsxMarker from; + QSize ext; + + bool loadFromXml(QXmlStreamReader &reader); + void saveToXml(QXmlStreamWriter &writer) const; +}; + +class DrawingTwoCellAnchor : public DrawingAnchor +{ +public: + DrawingTwoCellAnchor(Drawing *drawing, ObjectType objectType=Unknown); + + XlsxMarker from; + XlsxMarker to; + + bool loadFromXml(QXmlStreamReader &reader); + void saveToXml(QXmlStreamWriter &writer) const; +}; + +} // namespace QXlsx + +#endif // QXLSX_XLSXDRAWINGANCHOR_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxformat.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,1432 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxformat.h" +#include "xlsxformat_p.h" +#include "xlsxcolor_p.h" +#include "xlsxnumformatparser_p.h" +#include <QDataStream> +#include <QDebug> + +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 +{ + 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<Format::FontScript>(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<Format::FontUnderline>(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 <fonts > 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; i<FormatPrivate::P_Font_ENDID; ++i) { + if (d->properties.contains(i)) + stream << i << d->properties[i]; + }; + + const_cast<Format*>(this)->d->font_key = key; + const_cast<Format*>(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<FormatPrivate::P_Font_ENDID; ++i) { + if (hasProperty(i)) + return true; + } + return false; +} + +/*! + * Return the horizontal alignment. + */ +Format::HorizontalAlignment Format::horizontalAlignment() const +{ + return static_cast<Format::HorizontalAlignment>(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<Format::VerticalAlignment>(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::setTextWarp(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<FormatPrivate::P_Alignment_ENDID; ++i) { + if (hasProperty(i)) + return true; + } + return false; +} + +/*! + * Set the border style with the given \a style. + */ +void Format::setBorderStyle(BorderStyle style) +{ + setLeftBorderStyle(style); + setRightBorderStyle(style); + setBottomBorderStyle(style); + setTopBorderStyle(style); +} + +/*! + * Sets the border color with the given \a color. + */ +void Format::setBorderColor(const QColor &color) +{ + setLeftBorderColor(color); + setRightBorderColor(color); + setTopBorderColor(color); + setBottomBorderColor(color); +} + +/*! + * Returns the left border style + */ +Format::BorderStyle Format::leftBorderStyle() const +{ + return static_cast<BorderStyle>(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<BorderStyle>(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<BorderStyle>(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<BorderStyle>(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<BorderStyle>(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<DiagonalBorderType>(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; i<FormatPrivate::P_Border_ENDID; ++i) { + if (d->properties.contains(i)) + stream << i << d->properties[i]; + }; + + const_cast<Format*>(this)->d->border_key = key; + const_cast<Format*>(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<FormatPrivate::P_Border_ENDID; ++i) { + if (hasProperty(i)) + return true; + } + return false; +} + +/*! + Return the fill pattern. +*/ +Format::FillPattern Format::fillPattern() const +{ + return static_cast<FillPattern>(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; i<FormatPrivate::P_Fill_ENDID; ++i) { + if (d->properties.contains(i)) + stream << i << d->properties[i]; + }; + + const_cast<Format*>(this)->d->fill_key = key; + const_cast<Format*>(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<FormatPrivate::P_Fill_ENDID; ++i) { + if (hasProperty(i)) + return true; + } + return false; +} + +/*! + Returns whether the hidden protection property is set to true. +*/ +bool Format::hidden() const +{ + return boolProperty(FormatPrivate::P_Protection_Hidden); +} + +/*! + Sets the hidden protection property with the given \a hidden. +*/ +void Format::setHidden(bool hidden) +{ + setProperty(FormatPrivate::P_Protection_Hidden, hidden); +} + +/*! + Returns whether the locked protection property is set to true. +*/ +bool Format::locked() const +{ + return boolProperty(FormatPrivate::P_Protection_Locked); +} + +/*! + Sets the locked protection property with the given \a locked. +*/ +void Format::setLocked(bool locked) +{ + setProperty(FormatPrivate::P_Protection_Locked, locked); +} + +/*! + \internal + Return true if the format has protection data, otherwise return false. + */ +bool Format::hasProtectionData() const +{ + if (!d) + return false; + + if (hasProperty(FormatPrivate::P_Protection_Hidden + || FormatPrivate::P_Protection_Locked)) { + return true; + } + return false; +} + +/*! + Merges the current format with the properties described by format \a modifier. + */ +void Format::mergeFormat(const Format &modifier) +{ + if (!modifier.isValid()) + return; + + if (!isValid()) { + d = modifier.d; + return; + } + + QMapIterator<int, QVariant> it(modifier.d->properties); + while(it.hasNext()) { + it.next(); + setProperty(it.key(), it.value()); + } +} + +/*! + Returns true if the format is valid; otherwise returns false. + */ +bool Format::isValid() const +{ + if (d) + return true; + return false; +} + +/*! + Returns true if the format is empty; otherwise returns false. + */ +bool Format::isEmpty() const +{ + if (!d) + return true; + return d->properties.isEmpty(); +} + +/*! + * \internal + */ +QByteArray Format::formatKey() const +{ + if (isEmpty()) + return QByteArray(); + + if (d->dirty) { + QByteArray key; + QDataStream stream(&key, QIODevice::WriteOnly); + + QMapIterator<int, QVariant> i(d->properties); + while (i.hasNext()) { + i.next(); + stream<<i.key()<<i.value(); + } + + d->formatKey = key; + d->dirty = false; + } + + return d->formatKey; +} + +/*! + * \internal + * Called by QXlsx::Styles or some unittests. + */ +void Format::setXfIndex(int index) +{ + if (!d) + d = new FormatPrivate; + d->xf_index = index; + d->xf_indexValid = true; +} + +/*! + * \internal + */ +int Format::xfIndex() const +{ + if (!d) + return -1; + return d->xf_index; +} + +/*! + * \internal + */ +bool Format::xfIndexValid() const +{ + if (!d) + return false; + return d->xf_indexValid; +} + +/*! + * \internal + * Called by QXlsx::Styles or some unittests. + */ +void Format::setDxfIndex(int index) +{ + if (!d) + d = new FormatPrivate; + d->dxf_index = index; + d->dxf_indexValid = true; +} + +/*! + * \internal + * Returns the index in the styles dxfs. + */ +int Format::dxfIndex() const +{ + if (!d) + return -1; + return d->dxf_index; +} + +/*! + * \internal + * Returns whether the dxf index is valid or not. + */ +bool Format::dxfIndexValid() const +{ + if (!d) + return false; + return d->dxf_indexValid; +} + +/*! + Returns ture if the \a format is equal to this format. +*/ +bool Format::operator ==(const Format &format) const +{ + return this->formatKey() == format.formatKey(); +} + +/*! + Returns ture if the \a format is not equal to this format. +*/ +bool Format::operator !=(const Format &format) const +{ + return this->formatKey() != format.formatKey(); +} + +int Format::theme() const +{ + return d->theme; +} + +/*! + * \internal + */ +QVariant Format::property(int propertyId, const QVariant &defaultValue) const +{ + if (d && d->properties.contains(propertyId)) + return d->properties[propertyId]; + return defaultValue; +} + +/*! + * \internal + */ +void Format::setProperty(int propertyId, const QVariant &value, const QVariant &clearValue, bool detach) +{ + if (!d) + d = new FormatPrivate; + + if (value != clearValue) { + if (d->properties.contains(propertyId) && d->properties[propertyId] == value) + return; + if (detach) + d.detach(); + d->properties[propertyId] = value; + } else { + if (!d->properties.contains(propertyId)) + return; + if (detach) + d.detach(); + d->properties.remove(propertyId); + } + + d->dirty = true; + d->xf_indexValid = false; + d->dxf_indexValid = false; + + if (propertyId >= FormatPrivate::P_Font_STARTID && propertyId < FormatPrivate::P_Font_ENDID) { + d->font_dirty = true; + d->font_index_valid = false; + } else if (propertyId >= FormatPrivate::P_Border_STARTID && propertyId < FormatPrivate::P_Border_ENDID) { + d->border_dirty = true; + d->border_index_valid = false; + } else if (propertyId >= FormatPrivate::P_Fill_STARTID && propertyId < FormatPrivate::P_Fill_ENDID) { + d->fill_dirty = true; + d->fill_index_valid = false; + } +} + +/*! + * \internal + */ +void Format::clearProperty(int propertyId) +{ + setProperty(propertyId, QVariant()); +} + +/*! + * \internal + */ +bool Format::hasProperty(int propertyId) const +{ + if (!d) + return false; + return d->properties.contains(propertyId); +} + +/*! + * \internal + */ +bool Format::boolProperty(int propertyId, bool defaultValue) const +{ + if (!hasProperty(propertyId)) + return defaultValue; + + const QVariant prop = d->properties[propertyId]; + if (prop.userType() != QMetaType::Bool) + return defaultValue; + return prop.toBool(); +} + +/*! + * \internal + */ +int Format::intProperty(int propertyId, int defaultValue) const +{ + if (!hasProperty(propertyId)) + return defaultValue; + + const QVariant prop = d->properties[propertyId]; + if (prop.userType() != QMetaType::Int) + return defaultValue; + return prop.toInt(); +} + +/*! + * \internal + */ +double Format::doubleProperty(int propertyId, double defaultValue) const +{ + if (!hasProperty(propertyId)) + return defaultValue; + + const QVariant prop = d->properties[propertyId]; + if (prop.userType() != QMetaType::Double && prop.userType() != QMetaType::Float) + return defaultValue; + return prop.toDouble(); +} + +/*! + * \internal + */ +QString Format::stringProperty(int propertyId, const QString &defaultValue) const +{ + if (!hasProperty(propertyId)) + return defaultValue; + + const QVariant prop = d->properties[propertyId]; + if (prop.userType() != QMetaType::QString) + return defaultValue; + return prop.toString(); +} + +/*! + * \internal + */ +QColor Format::colorProperty(int propertyId, const QColor &defaultValue) const +{ + if (!hasProperty(propertyId)) + return defaultValue; + + const QVariant prop = d->properties[propertyId]; + if (prop.userType() != qMetaTypeId<XlsxColor>()) + return defaultValue; + return qvariant_cast<XlsxColor>(prop).rgbColor(); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const Format &f) +{ + dbg.nospace() << "QXlsx::Format(" << f.d->properties << ")"; + return dbg.space(); +} +#endif + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxformat.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,282 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_FORMAT_H +#define QXLSX_FORMAT_H + +#include "xlsxglobal.h" +#include <QFont> +#include <QColor> +#include <QByteArray> +#include <QList> +#include <QExplicitlySharedDataPointer> +#include <QVariant> + +class FormatTest; + +QT_BEGIN_NAMESPACE_XLSX + +class Styles; +class Worksheet; +class WorksheetPrivate; +class RichStringPrivate; +class SharedStrings; + +class FormatPrivate; +class Q_XLSX_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 setTextWarp(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 Q_XLSX_EXPORT QDebug operator<<(QDebug, const Format &f); + + int theme() const; + + QExplicitlySharedDataPointer<FormatPrivate> d; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_XLSX_EXPORT QDebug operator<<(QDebug dbg, const Format &f); +#endif + +QT_END_NAMESPACE_XLSX + +#endif // QXLSX_FORMAT_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxformat_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,161 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXFORMAT_P_H +#define XLSXFORMAT_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 "xlsxformat.h" +#include <QSharedData> +#include <QMap> +#include <QSet> + +namespace QXlsx { + +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<int, QVariant> properties; +}; + +} + +#endif // XLSXFORMAT_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxglobal.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,49 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXGLOBAL_H +#define XLSXGLOBAL_H +#include <QtGlobal> + +#define QT_BEGIN_NAMESPACE_XLSX namespace QXlsx { +#define QT_END_NAMESPACE_XLSX } +#define QTXLSX_USE_NAMESPACE using namespace QXlsx; + +#if !defined(QT_STATIC) && !defined(XLSX_NO_LIB) +# if defined(QT_BUILD_XLSX_LIB) +# define Q_XLSX_EXPORT Q_DECL_EXPORT +# else +# define Q_XLSX_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_XLSX_EXPORT +#endif + +#ifdef XLSX_TEST +# define XLSX_AUTOTEST_EXPORT Q_XLSX_EXPORT +#else +# define XLSX_AUTOTEST_EXPORT +#endif + +#endif // XLSXGLOBAL_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxmediafile.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,99 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "xlsxmediafile_p.h" +#include <QCryptographicHash> + +namespace QXlsx { + +MediaFile::MediaFile(const QByteArray &bytes, const QString &suffix, const QString &mimeType) + : m_contents(bytes), m_suffix(suffix), m_mimeType(mimeType) + , m_index(0), m_indexValid(false) +{ + m_hashKey = QCryptographicHash::hash(m_contents, QCryptographicHash::Md5); +} + +MediaFile::MediaFile(const QString &fileName) + :m_fileName(fileName), m_index(0), m_indexValid(false) +{ + +} + +void MediaFile::set(const QByteArray &bytes, const QString &suffix, const QString &mimeType) +{ + m_contents = bytes; + m_suffix = suffix; + m_mimeType = mimeType; + m_hashKey = QCryptographicHash::hash(m_contents, QCryptographicHash::Md5); + m_indexValid = false; +} + +void MediaFile::setFileName(const QString &name) +{ + m_fileName = name; +} + +QString MediaFile::fileName() const +{ + return m_fileName; +} + +QString MediaFile::suffix() const +{ + return m_suffix; +} + +QString MediaFile::mimeType() const +{ + return m_mimeType; +} + +QByteArray MediaFile::contents() const +{ + return m_contents; +} + +int MediaFile::index() const +{ + return m_index; +} + +bool MediaFile::isIndexValid() const +{ + return m_indexValid; +} + +void MediaFile::setIndex(int idx) +{ + m_index = idx; + m_indexValid = true; +} + +QByteArray MediaFile::hashKey() const +{ + return m_hashKey; +} + +} // namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxmediafile_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,80 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QXLSX_XLSXMEDIAFILE_H +#define QXLSX_XLSXMEDIAFILE_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 <QString> +#include <QByteArray> + +namespace QXlsx { + +class MediaFile +{ +public: + MediaFile(const QString &fileName); + MediaFile(const QByteArray &bytes, const QString &suffix, const QString &mimeType=QString()); + + 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; + +private: + QString m_fileName; //... + QByteArray m_contents; + QString m_suffix; + QString m_mimeType; + + int m_index; + bool m_indexValid; + QByteArray m_hashKey; +}; + +} // namespace QXlsx + +#endif // QXLSX_XLSXMEDIAFILE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxnumformatparser.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,94 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxnumformatparser_p.h" + +#include <QString> + +namespace QXlsx { + +bool NumFormatParser::isDateTime(const QString &formatCode) +{ + for (int i = 0; i < formatCode.length(); ++i) { + const QChar &c = formatCode[i]; + + switch (c.unicode()) { + case '[': + // [h], [m], [s] are valid format for time + if (i < formatCode.length()-2 && formatCode[i+2] == QLatin1Char(']')) { + const QChar cc = formatCode[i+1].toLower(); + if (cc == QLatin1Char('h') || cc == QLatin1Char('m') || cc == QLatin1Char('s')) + return true; + i+=2; + break; + } else { + // condition or color: don't care, ignore + while (i < formatCode.length() && formatCode[i] != QLatin1Char(']')) + ++i; + break; + } + + // quoted plain text block: don't care, ignore + case '"': + while (i < formatCode.length()-1 && formatCode[++i] != QLatin1Char('"')) + ; + break; + + // escaped char: don't care, ignore + case '\\': + if (i < formatCode.length() - 1) + ++i; + break; + + // date/time can only be positive number, + // so only the first section of the format make sense. + case ';': + return false; + break; + + // days + case 'D': + case 'd': + // years + case 'Y': + case 'y': + // hours + case 'H': + case 'h': + // seconds + case 'S': + case 's': + // minutes or months, depending on context + case 'M': + case 'm': + return true; + + default: + break; + } + } + return false; +} + +} // namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxnumformatparser_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,51 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#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" + +namespace QXlsx { + +class NumFormatParser +{ +public: + static bool isDateTime(const QString &formatCode); +}; + +} // namespace QXlsx + +#endif // QXLSX_NUMFORMATPARSER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxrelationships.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,189 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxrelationships_p.h" +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QDir> +#include <QFile> +#include <QBuffer> + +namespace QXlsx { + +const QString schema_doc = QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"); +const QString schema_msPackage = QStringLiteral("http://schemas.microsoft.com/office/2006/relationships"); +const QString schema_package = QStringLiteral("http://schemas.openxmlformats.org/package/2006/relationships"); +//const QString schema_worksheet = QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"); +Relationships::Relationships() +{ +} + +QList<XlsxRelationship> Relationships::documentRelationships(const QString &relativeType) const +{ + return relationships(schema_doc + relativeType); +} + +void Relationships::addDocumentRelationship(const QString &relativeType, const QString &target) +{ + addRelationship(schema_doc + relativeType, target); +} + +QList<XlsxRelationship> Relationships::msPackageRelationships(const QString &relativeType) const +{ + return relationships(schema_msPackage + relativeType); +} + +void Relationships::addMsPackageRelationship(const QString &relativeType, const QString &target) +{ + addRelationship(schema_msPackage + relativeType, target); +} + +QList<XlsxRelationship> Relationships::packageRelationships(const QString &relativeType) const +{ + return relationships(schema_package + relativeType); +} + +void Relationships::addPackageRelationship(const QString &relativeType, const QString &target) +{ + addRelationship(schema_package + relativeType, target); +} + +QList<XlsxRelationship> Relationships::worksheetRelationships(const QString &relativeType) const +{ + return relationships(schema_doc + relativeType); +} + +void Relationships::addWorksheetRelationship(const QString &relativeType, const QString &target, const QString &targetMode) +{ + addRelationship(schema_doc + relativeType, target, targetMode); +} + +QList<XlsxRelationship> Relationships::relationships(const QString &type) const +{ + QList<XlsxRelationship> res; + foreach (XlsxRelationship ship, m_relationships) { + if (ship.type == type) + res.append(ship); + } + return res; +} + +void Relationships::addRelationship(const QString &type, const QString &target, const QString &targetMode) +{ + XlsxRelationship relation; + relation.id = QStringLiteral("rId%1").arg(m_relationships.size()+1); + relation.type = type; + relation.target = target; + relation.targetMode = targetMode; + + m_relationships.append(relation); +} + +void Relationships::saveToXmlFile(QIODevice *device) const +{ + QXmlStreamWriter writer(device); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("Relationships")); + writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/package/2006/relationships")); + foreach (XlsxRelationship relation, m_relationships) { + writer.writeStartElement(QStringLiteral("Relationship")); + writer.writeAttribute(QStringLiteral("Id"), relation.id); + writer.writeAttribute(QStringLiteral("Type"), relation.type); + writer.writeAttribute(QStringLiteral("Target"), relation.target); + if (!relation.targetMode.isNull()) + writer.writeAttribute(QStringLiteral("TargetMode"), relation.targetMode); + writer.writeEndElement(); + } + writer.writeEndElement();//Relationships + writer.writeEndDocument(); +} + +QByteArray Relationships::saveToXmlData() const +{ + QByteArray data; + QBuffer buffer(&data); + buffer.open(QIODevice::WriteOnly); + saveToXmlFile(&buffer); + + return data; +} + +bool Relationships::loadFromXmlFile(QIODevice *device) +{ + clear(); + QXmlStreamReader reader(device); + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (reader.name() == QStringLiteral("Relationship")) { + QXmlStreamAttributes attributes = reader.attributes(); + XlsxRelationship relationship; + relationship.id = attributes.value(QLatin1String("Id")).toString(); + relationship.type = attributes.value(QLatin1String("Type")).toString(); + relationship.target = attributes.value(QLatin1String("Target")).toString(); + relationship.targetMode = attributes.value(QLatin1String("TargetMode")).toString(); + m_relationships.append(relationship); + } + } + + if (reader.hasError()) + return false; + } + return true; +} + +bool Relationships::loadFromXmlData(const QByteArray &data) +{ + QBuffer buffer; + buffer.setData(data); + buffer.open(QIODevice::ReadOnly); + return loadFromXmlFile(&buffer); +} + +XlsxRelationship Relationships::getRelationshipById(const QString &id) const +{ + foreach (XlsxRelationship ship, m_relationships) { + if (ship.id == id) + return ship; + } + return XlsxRelationship(); +} + +void Relationships::clear() +{ + m_relationships.clear(); +} + +int Relationships::count() const +{ + return m_relationships.count(); +} + +bool Relationships::isEmpty() const +{ + return m_relationships.isEmpty(); +} + +} //namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxrelationships_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,87 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#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 <QList> +#include <QString> +class QIODevice; + +namespace QXlsx { + +struct XlsxRelationship +{ + QString id; + QString type; + QString target; + QString targetMode; +}; + +class XLSX_AUTOTEST_EXPORT Relationships +{ +public: + Relationships(); + + QList<XlsxRelationship> documentRelationships(const QString &relativeType) const; + QList<XlsxRelationship> packageRelationships(const QString &relativeType) const; + QList<XlsxRelationship> msPackageRelationships(const QString &relativeType) const; + QList<XlsxRelationship> 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<XlsxRelationship> relationships(const QString &type) const; + void addRelationship(const QString &type, const QString &target, const QString &targetMode=QString()); + + QList<XlsxRelationship> m_relationships; +}; + +} +#endif // XLSXRELATIONSHIPS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxrichstring.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,343 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxrichstring.h" +#include "xlsxrichstring_p.h" +#include "xlsxformat_p.h" +#include <QDebug> +#include <QTextDocument> +#include <QTextFragment> + +QT_BEGIN_NAMESPACE_XLSX + +RichStringPrivate::RichStringPrivate() + :_dirty(true) +{ + +} + +RichStringPrivate::RichStringPrivate(const RichStringPrivate &other) + :QSharedData(other), fragmentTexts(other.fragmentTexts) + ,fragmentFormats(other.fragmentFormats) + , _idKey(other.idKey()), _dirty(other._dirty) +{ + +} + +RichStringPrivate::~RichStringPrivate() +{ + +} + +/*! + \class RichString + \inmodule QtXlsx + \brief This class add support for the rich text string of the cell. +*/ + +/*! + Constructs a null string. + */ +RichString::RichString() + :d(new RichStringPrivate) +{ +} + +/*! + Constructs a plain string with the given \a text. +*/ +RichString::RichString(const QString text) + :d(new RichStringPrivate) +{ + addFragment(text, Format()); +} + +/*! + Constructs a copy of \a other. + */ +RichString::RichString(const RichString &other) + :d(other.d) +{ + +} + +/*! + Destructs the string. + */ +RichString::~RichString() +{ + +} + +/*! + Assigns \a other to this string and returns a reference to this string + */ +RichString &RichString::operator =(const RichString &other) +{ + this->d = other.d; + return *this; +} + +/*! + Returns the rich string as a QVariant +*/ +RichString::operator QVariant() const +{ + return QVariant(qMetaTypeId<RichString>(), this); +} + +/*! + Returns true if this is rich text string. + */ +bool RichString::isRichString() const +{ + if (fragmentCount() > 1) //Is this enough?? + return true; + return false; +} + +/*! + Returns true is this is an Null string. + */ +bool RichString::isNull() const +{ + return d->fragmentTexts.size() == 0; +} + +/*! + Returns true is this is an empty string. + */ +bool RichString::isEmtpy() const +{ + foreach (const QString str, d->fragmentTexts) { + if (!str.isEmpty()) + return false; + } + + return true; +} + +/*! + Converts to plain text string. +*/ +QString RichString::toPlainString() const +{ + if (isEmtpy()) + return QString(); + if (d->fragmentTexts.size() == 1) + return d->fragmentTexts[0]; + + return d->fragmentTexts.join(QString()); +} + +/*! + Converts to html string +*/ +QString RichString::toHtml() const +{ + //: Todo + return QString(); +} + +/*! + Replaces the entire contents of the document + with the given HTML-formatted text in the \a text string +*/ +void RichString::setHtml(const QString &text) +{ + QTextDocument doc; + doc.setHtml(text); + QTextBlock block = doc.firstBlock(); + QTextBlock::iterator it; + for (it = block.begin(); !(it.atEnd()); ++it) { + QTextFragment textFragment = it.fragment(); + if (textFragment.isValid()) { + Format fmt; + fmt.setFont(textFragment.charFormat().font()); + fmt.setFontColor(textFragment.charFormat().foreground().color()); + addFragment(textFragment.text(), fmt); + } + } +} + +/*! + Returns fragment count. + */ +int RichString::fragmentCount() const +{ + return d->fragmentTexts.size(); +} + +/*! + Appends a fragment with the given \a text and \a format. + */ +void RichString::addFragment(const QString &text, const Format &format) +{ + d->fragmentTexts.append(text); + d->fragmentFormats.append(format); + d->_dirty = true; +} + +/*! + Returns fragment text at the position \a index. + */ +QString RichString::fragmentText(int index) const +{ + if (index < 0 || index >= fragmentCount()) + return QString(); + + return d->fragmentTexts[index]; +} + +/*! + Returns fragment format at the position \a index. + */ +Format RichString::fragmentFormat(int index) const +{ + if (index < 0 || index >= fragmentCount()) + return Format(); + + return d->fragmentFormats[index]; +} + +/*! + * \internal + */ +QByteArray RichStringPrivate::idKey() const +{ + if (_dirty) { + RichStringPrivate *rs = const_cast<RichStringPrivate *>(this); + QByteArray bytes; + if (fragmentTexts.size() == 1) { + bytes = fragmentTexts[0].toUtf8(); + } else { + //Generate a hash value base on QByteArray ? + bytes.append("@@QtXlsxRichString="); + for (int i=0; i<fragmentTexts.size(); ++i) { + bytes.append("@Text"); + bytes.append(fragmentTexts[i].toUtf8()); + bytes.append("@Format"); + if (fragmentFormats[i].hasFontData()) + bytes.append(fragmentFormats[i].fontKey()); + } + } + rs->_idKey = bytes; + rs->_dirty = false; + } + + return _idKey; +} + +/*! + Returns true if this string \a rs1 is equal to string \a rs2; + otherwise returns false. + */ +bool operator==(const RichString &rs1, const RichString &rs2) +{ + if (rs1.fragmentCount() != rs2.fragmentCount()) + return false; + + return rs1.d->idKey() == rs2.d->idKey(); +} + +/*! + Returns true if this string \a rs1 is not equal to string \a rs2; + otherwise returns false. + */ +bool operator!=(const RichString &rs1, const RichString &rs2) +{ + if (rs1.fragmentCount() != rs2.fragmentCount()) + return true; + + return rs1.d->idKey() != rs2.d->idKey(); +} + +/*! + * \internal + */ +bool operator<(const RichString &rs1, const RichString &rs2) +{ + return rs1.d->idKey() < rs2.d->idKey(); +} + +/*! + \overload + Returns true if this string \a rs1 is equal to string \a rs2; + otherwise returns false. + */ +bool operator ==(const RichString &rs1, const QString &rs2) +{ + if (rs1.fragmentCount() == 1 && rs1.fragmentText(0) == rs2) //format == 0 + return true; + + return false; +} + +/*! + \overload + Returns true if this string \a rs1 is not equal to string \a rs2; + otherwise returns false. + */ +bool operator !=(const RichString &rs1, const QString &rs2) +{ + if (rs1.fragmentCount() == 1 && rs1.fragmentText(0) == rs2) //format == 0 + return false; + + return true; +} + +/*! + \overload + Returns true if this string \a rs1 is equal to string \a rs2; + otherwise returns false. + */ +bool operator ==(const QString &rs1, const RichString &rs2) +{ + return rs2 == rs1; +} + +/*! + \overload + Returns true if this string \a rs1 is not equal to string \a rs2; + otherwise returns false. + */ +bool operator !=(const QString &rs1, const RichString &rs2) +{ + return rs2 != rs1; +} + +uint qHash(const RichString &rs, uint seed) Q_DECL_NOTHROW +{ + return qHash(rs.d->idKey(), seed); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const RichString &rs) +{ + dbg.nospace() << "QXlsx::RichString(" << rs.d->fragmentTexts << ")"; + return dbg.space(); +} +#endif + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxrichstring.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,89 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXRICHSTRING_H +#define XLSXRICHSTRING_H + +#include "xlsxglobal.h" +#include "xlsxformat.h" +#include <QVariant> +#include <QStringList> +#include <QSharedDataPointer> + +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) +Q_XLSX_EXPORT uint qHash(const RichString &rs, uint seed = 0) Q_DECL_NOTHROW; + +class Q_XLSX_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 Q_XLSX_EXPORT uint qHash(const RichString &rs, uint seed) Q_DECL_NOTHROW; + friend Q_XLSX_EXPORT bool operator==(const RichString &rs1, const RichString &rs2); + friend Q_XLSX_EXPORT bool operator!=(const RichString &rs1, const RichString &rs2); + friend Q_XLSX_EXPORT bool operator<(const RichString &rs1, const RichString &rs2); + friend Q_XLSX_EXPORT QDebug operator<<(QDebug dbg, const RichString &rs); + + QSharedDataPointer<RichStringPrivate> d; +}; + +Q_XLSX_EXPORT bool operator==(const RichString &rs1, const RichString &rs2); +Q_XLSX_EXPORT bool operator!=(const RichString &rs1, const RichString &rs2); +Q_XLSX_EXPORT bool operator<(const RichString &rs1, const RichString &rs2); +Q_XLSX_EXPORT bool operator==(const RichString &rs1, const QString &rs2); +Q_XLSX_EXPORT bool operator==(const QString &rs1, const RichString &rs2); +Q_XLSX_EXPORT bool operator!=(const RichString &rs1, const QString &rs2); +Q_XLSX_EXPORT bool operator!=(const QString &rs1, const RichString &rs2); + +#ifndef QT_NO_DEBUG_STREAM +Q_XLSX_EXPORT QDebug operator<<(QDebug dbg, const RichString &rs); +#endif + +QT_END_NAMESPACE_XLSX + +Q_DECLARE_METATYPE(QXlsx::RichString) + +#endif // XLSXRICHSTRING_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxrichstring_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,60 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#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<Format> fragmentFormats; + QByteArray _idKey; + bool _dirty; +}; + +QT_END_NAMESPACE_XLSX + +#endif // XLSXRICHSTRING_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxsharedstrings.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,400 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxrichstring.h" +#include "xlsxsharedstrings_p.h" +#include "xlsxutility_p.h" +#include "xlsxformat_p.h" +#include "xlsxcolor_p.h" +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QDir> +#include <QFile> +#include <QDebug> +#include <QBuffer> + +namespace QXlsx { + +/* + * Note that, when we open an existing .xlsx file (broken file?), + * duplicated string items may exist in the shared string table. + * + * In such case, the size of stringList will larger than stringTable. + * Duplicated items can be removed once we loaded all the worksheets. + */ + +SharedStrings::SharedStrings(CreateFlag flag) + :AbstractOOXmlFile(flag) +{ + m_stringCount = 0; +} + +int SharedStrings::count() const +{ + return m_stringCount; +} + +bool SharedStrings::isEmpty() const +{ + return m_stringList.isEmpty(); +} + +int SharedStrings::addSharedString(const QString &string) +{ + return addSharedString(RichString(string)); +} + +int SharedStrings::addSharedString(const RichString &string) +{ + m_stringCount += 1; + + if (m_stringTable.contains(string)) { + XlsxSharedStringInfo &item = m_stringTable[string]; + item.count += 1; + return item.index; + } + + int index = m_stringList.size(); + m_stringTable[string] = XlsxSharedStringInfo(index); + m_stringList.append(string); + return index; +} + +void SharedStrings::incRefByStringIndex(int idx) +{ + if (idx <0 || idx >= m_stringList.size()) { + qDebug("SharedStrings: invlid index"); + return; + } + + addSharedString(m_stringList[idx]); +} + +/* + * Broken, don't use. + */ +void SharedStrings::removeSharedString(const QString &string) +{ + removeSharedString(RichString(string)); +} + +/* + * Broken, don't use. + */ +void SharedStrings::removeSharedString(const RichString &string) +{ + if (!m_stringTable.contains(string)) + return; + + m_stringCount -= 1; + + XlsxSharedStringInfo &item = m_stringTable[string]; + item.count -= 1; + + if (item.count <= 0) { + for (int i=item.index+1; i<m_stringList.size(); ++i) + m_stringTable[m_stringList[i]].index -= 1; + + m_stringList.removeAt(item.index); + m_stringTable.remove(string); + } +} + +int SharedStrings::getSharedStringIndex(const QString &string) const +{ + return getSharedStringIndex(RichString(string)); +} + +int SharedStrings::getSharedStringIndex(const RichString &string) const +{ + if (m_stringTable.contains(string)) + return m_stringTable[string].index; + return -1; +} + +RichString SharedStrings::getSharedString(int index) const +{ + if (index < m_stringList.count() && index >= 0) + return m_stringList[index]; + return RichString(); +} + +QList<RichString> SharedStrings::getSharedStrings() const +{ + return m_stringList; +} + +void SharedStrings::writeRichStringPart_rPr(QXmlStreamWriter &writer, const Format &format) const +{ + if (!format.hasFontData()) + return; + + if (format.fontBold()) + writer.writeEmptyElement(QStringLiteral("b")); + if (format.fontItalic()) + writer.writeEmptyElement(QStringLiteral("i")); + if (format.fontStrikeOut()) + writer.writeEmptyElement(QStringLiteral("strike")); + if (format.fontOutline()) + writer.writeEmptyElement(QStringLiteral("outline")); + if (format.boolProperty(FormatPrivate::P_Font_Shadow)) + writer.writeEmptyElement(QStringLiteral("shadow")); + if (format.hasProperty(FormatPrivate::P_Font_Underline)) { + Format::FontUnderline u = format.fontUnderline(); + if (u != Format::FontUnderlineNone) { + writer.writeEmptyElement(QStringLiteral("u")); + if (u== Format::FontUnderlineDouble) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double")); + else if (u == Format::FontUnderlineSingleAccounting) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting")); + else if (u == Format::FontUnderlineDoubleAccounting) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting")); + } + } + if (format.hasProperty(FormatPrivate::P_Font_Script)) { + Format::FontScript s = format.fontScript(); + if (s != Format::FontScriptNormal) { + writer.writeEmptyElement(QStringLiteral("vertAlign")); + if (s == Format::FontScriptSuper) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript")); + else + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript")); + } + } + + if (format.hasProperty(FormatPrivate::P_Font_Size)) { + writer.writeEmptyElement(QStringLiteral("sz")); + writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize())); + } + + if (format.hasProperty(FormatPrivate::P_Font_Color)) { + XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>(); + color.saveToXml(writer); + } + + if (!format.fontName().isEmpty()) { + writer.writeEmptyElement(QStringLiteral("rFont")); + writer.writeAttribute(QStringLiteral("val"), format.fontName()); + } + if (format.hasProperty(FormatPrivate::P_Font_Family)) { + writer.writeEmptyElement(QStringLiteral("family")); + writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family))); + } + + if (format.hasProperty(FormatPrivate::P_Font_Scheme)) { + writer.writeEmptyElement(QStringLiteral("scheme")); + writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme)); + } +} + +void SharedStrings::saveToXmlFile(QIODevice *device) const +{ + QXmlStreamWriter writer(device); + + if (m_stringList.size() != m_stringTable.size()) { + //Duplicated string items exist in m_stringList + //Clean up can not be done here, as the indices + //have been used when we save the worksheets part. + } + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("sst")); + writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main")); + writer.writeAttribute(QStringLiteral("count"), QString::number(m_stringCount)); + writer.writeAttribute(QStringLiteral("uniqueCount"), QString::number(m_stringList.size())); + + foreach (RichString string, m_stringList) { + writer.writeStartElement(QStringLiteral("si")); + if (string.isRichString()) { + //Rich text string + for (int i=0; i<string.fragmentCount(); ++i) { + writer.writeStartElement(QStringLiteral("r")); + if (string.fragmentFormat(i).hasFontData()) { + writer.writeStartElement(QStringLiteral("rPr")); + writeRichStringPart_rPr(writer, string.fragmentFormat(i)); + writer.writeEndElement();// rPr + } + writer.writeStartElement(QStringLiteral("t")); + if (isSpaceReserveNeeded(string.fragmentText(i))) + writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve")); + writer.writeCharacters(string.fragmentText(i)); + writer.writeEndElement();// t + + writer.writeEndElement(); //r + } + } else { + writer.writeStartElement(QStringLiteral("t")); + QString pString = string.toPlainString(); + if (isSpaceReserveNeeded(pString)) + writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve")); + writer.writeCharacters(pString); + writer.writeEndElement();//t + } + writer.writeEndElement();//si + } + + writer.writeEndElement(); //sst + writer.writeEndDocument(); +} + +void SharedStrings::readString(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("si")); + + RichString richString; + + while (!reader.atEnd() && !(reader.name() == QLatin1String("si") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("r")) + readRichStringPart(reader, richString); + else if (reader.name() == QLatin1String("t")) + readPlainStringPart(reader, richString); + } + } + + int idx = m_stringList.size(); + m_stringTable[richString] = XlsxSharedStringInfo(idx, 0); + m_stringList.append(richString); +} + +void SharedStrings::readRichStringPart(QXmlStreamReader &reader, RichString &richString) +{ + Q_ASSERT(reader.name() == QLatin1String("r")); + + QString text; + Format format; + while (!reader.atEnd() && !(reader.name() == QLatin1String("r") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("rPr")) { + format = readRichStringPart_rPr(reader); + } else if (reader.name() == QLatin1String("t")) { + text = reader.readElementText(); + } + } + } + richString.addFragment(text, format); +} + +void SharedStrings::readPlainStringPart(QXmlStreamReader &reader, RichString &richString) +{ + Q_ASSERT(reader.name() == QLatin1String("t")); + + //QXmlStreamAttributes attributes = reader.attributes(); + + QString text = reader.readElementText(); + richString.addFragment(text, Format()); +} + +Format SharedStrings::readRichStringPart_rPr(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("rPr")); + Format format; + while (!reader.atEnd() && !(reader.name() == QLatin1String("rPr") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + QXmlStreamAttributes attributes = reader.attributes(); + if (reader.name() == QLatin1String("rFont")) { + format.setFontName(attributes.value(QLatin1String("val")).toString()); + } else if (reader.name() == QLatin1String("charset")) { + format.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("family")) { + format.setProperty(FormatPrivate::P_Font_Family, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("b")) { + format.setFontBold(true); + } else if (reader.name() == QLatin1String("i")) { + format.setFontItalic(true); + } else if (reader.name() == QLatin1String("strike")) { + format.setFontStrikeOut(true); + } else if (reader.name() == QLatin1String("outline")) { + format.setFontOutline(true); + } else if (reader.name() == QLatin1String("shadow")) { + format.setProperty(FormatPrivate::P_Font_Shadow, true); + } else if (reader.name() == QLatin1String("condense")) { + format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("extend")) { + format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("color")) { + XlsxColor color; + color.loadFromXml(reader); + format.setProperty(FormatPrivate::P_Font_Color, color); + } else if (reader.name() == QLatin1String("sz")) { + format.setFontSize(attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("u")) { + QString value = attributes.value(QLatin1String("val")).toString(); + if (value == QLatin1String("double")) + format.setFontUnderline(Format::FontUnderlineDouble); + else if (value == QLatin1String("doubleAccounting")) + format.setFontUnderline(Format::FontUnderlineDoubleAccounting); + else if (value == QLatin1String("singleAccounting")) + format.setFontUnderline(Format::FontUnderlineSingleAccounting); + else + format.setFontUnderline(Format::FontUnderlineSingle); + } else if (reader.name() == QLatin1String("vertAlign")) { + QString value = attributes.value(QLatin1String("val")).toString(); + if (value == QLatin1String("superscript")) + format.setFontScript(Format::FontScriptSuper); + else if (value == QLatin1String("subscript")) + format.setFontScript(Format::FontScriptSub); + } else if (reader.name() == QLatin1String("scheme")) { + format.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString()); + } + } + } + return format; +} + +bool SharedStrings::loadFromXmlFile(QIODevice *device) +{ + QXmlStreamReader reader(device); + int count = 0; + bool hasUniqueCountAttr=true; + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("sst")) { + QXmlStreamAttributes attributes = reader.attributes(); + if ((hasUniqueCountAttr = attributes.hasAttribute(QLatin1String("uniqueCount")))) + count = attributes.value(QLatin1String("uniqueCount")).toString().toInt(); + } else if (reader.name() == QLatin1String("si")) { + readString(reader); + } + } + } + + if (hasUniqueCountAttr && m_stringList.size() != count) { + qDebug("Error: Shared string count"); + return false; + } + + if (m_stringList.size() != m_stringTable.size()) { + //qDebug("Warning: Duplicated items exist in shared string table."); + //Nothing we can do here, as indices of the strings will be used when loading sheets. + } + + return true; +} + +} //namespace
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxsharedstrings_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,98 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#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 "xlsxglobal.h" +#include "xlsxrichstring.h" +#include "xlsxabstractooxmlfile.h" +#include <QHash> +#include <QStringList> +#include <QSharedPointer> + +class QIODevice; +class QXmlStreamReader; +class QXmlStreamWriter; + +namespace QXlsx { + +class XlsxSharedStringInfo +{ +public: + XlsxSharedStringInfo(int index=0, int count = 1) : + index(index), count(count) + { + } + + int index; + int count; +}; + +class XLSX_AUTOTEST_EXPORT 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<RichString> getSharedStrings() const; + + void saveToXmlFile(QIODevice *device) const; + bool loadFromXmlFile(QIODevice *device); + +private: + void readString(QXmlStreamReader &reader); // <si> + void readRichStringPart(QXmlStreamReader &reader, RichString &rich); // <r> + void readPlainStringPart(QXmlStreamReader &reader, RichString &rich); // <v> + Format readRichStringPart_rPr(QXmlStreamReader &reader); + void writeRichStringPart_rPr(QXmlStreamWriter &writer, const Format &format) const; + + QHash<RichString, XlsxSharedStringInfo> m_stringTable; //for fast lookup + QList<RichString> m_stringList; + int m_stringCount; +}; + +} +#endif // XLSXSHAREDSTRINGS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxsimpleooxmlfile.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,56 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxsimpleooxmlfile_p.h" +#include <QIODevice> + +namespace QXlsx { +SimpleOOXmlFile::SimpleOOXmlFile(CreateFlag flag) + :AbstractOOXmlFile(flag) +{ +} + +void SimpleOOXmlFile::saveToXmlFile(QIODevice *device) const +{ + device->write(xmlData); +} + +QByteArray SimpleOOXmlFile::saveToXmlData() const +{ + return xmlData; +} + +bool SimpleOOXmlFile::loadFromXmlData(const QByteArray &data) +{ + xmlData = data; + return true; +} + +bool SimpleOOXmlFile::loadFromXmlFile(QIODevice *device) +{ + xmlData = device->readAll(); + return true; +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxsimpleooxmlfile_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,59 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#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 <QString> +class QIODevice; + +namespace QXlsx { + +class SimpleOOXmlFile : public AbstractOOXmlFile +{ +public: + SimpleOOXmlFile(CreateFlag flag); + + void saveToXmlFile(QIODevice *device) const; + QByteArray saveToXmlData() const; + bool loadFromXmlData(const QByteArray &data); + bool loadFromXmlFile(QIODevice *device); + + QByteArray xmlData; +}; + +} +#endif // XLSXSIMPLEOOXMLFILE_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxstyles.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,1334 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxstyles_p.h" +#include "xlsxformat_p.h" +#include "xlsxutility_p.h" +#include "xlsxcolor_p.h" +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QFile> +#include <QMap> +#include <QDataStream> +#include <QDebug> +#include <QBuffer> + +namespace QXlsx { + +/* + When loading from existing .xlsx file. we should create a clean styles object. + otherwise, default formats should be added. + +*/ +Styles::Styles(CreateFlag flag) + : AbstractOOXmlFile(flag), m_nextCustomNumFmtId(176), m_isIndexedColorsDefault(true) + , m_emptyFormatAdded(false) +{ + //!Fix me. Should the custom num fmt Id starts with 164 or 176 or others?? + + //!Fix me! Where should we put these register code? + if (QMetaType::type("XlsxColor") == QMetaType::UnknownType) { + qRegisterMetaType<XlsxColor>("XlsxColor"); + qRegisterMetaTypeStreamOperators<XlsxColor>("XlsxColor"); +#if QT_VERSION >= 0x050200 + QMetaType::registerDebugStreamOperator<XlsxColor>(); +#endif + } + + if (flag == F_NewFromScratch) { + //Add default Format + Format defaultFmt; + addXfFormat(defaultFmt); + + //Add another fill format + Format fillFmt; + fillFmt.setFillPattern(Format::PatternGray125); + m_fillsList.append(fillFmt); + m_fillsHash.insert(fillFmt.fillKey(), fillFmt); + } +} + +Styles::~Styles() +{ +} + +Format Styles::xfFormat(int idx) const +{ + if (idx <0 || idx >= m_xf_formatsList.size()) + return Format(); + + return m_xf_formatsList[idx]; +} + +Format Styles::dxfFormat(int idx) const +{ + if (idx <0 || idx >= m_dxf_formatsList.size()) + return Format(); + + return m_dxf_formatsList[idx]; +} + +void Styles::fixNumFmt(const Format &format) +{ + if (!format.hasNumFmtData()) + return; + + if (format.hasProperty(FormatPrivate::P_NumFmt_Id) + && !format.stringProperty(FormatPrivate::P_NumFmt_FormatCode).isEmpty()) { + return; + } + + if (m_builtinNumFmtsHash.isEmpty()) { + m_builtinNumFmtsHash.insert(QStringLiteral("General"), 0); + m_builtinNumFmtsHash.insert(QStringLiteral("0"), 1); + m_builtinNumFmtsHash.insert(QStringLiteral("0.00"), 2); + m_builtinNumFmtsHash.insert(QStringLiteral("#,##0"), 3); + m_builtinNumFmtsHash.insert(QStringLiteral("#,##0.00"), 4); +// m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0_);($#,##0)"), 5); +// m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0_);[Red]($#,##0)"), 6); +// m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0.00_);($#,##0.00)"), 7); +// m_builtinNumFmtsHash.insert(QStringLiteral("($#,##0.00_);[Red]($#,##0.00)"), 8); + m_builtinNumFmtsHash.insert(QStringLiteral("0%"), 9); + m_builtinNumFmtsHash.insert(QStringLiteral("0.00%"), 10); + m_builtinNumFmtsHash.insert(QStringLiteral("0.00E+00"), 11); + m_builtinNumFmtsHash.insert(QStringLiteral("# ?/?"), 12); + m_builtinNumFmtsHash.insert(QStringLiteral("# ?\?/??"), 13);// Note: "??/" is a c++ trigraph, so escape one "?" + m_builtinNumFmtsHash.insert(QStringLiteral("m/d/yy"), 14); + m_builtinNumFmtsHash.insert(QStringLiteral("d-mmm-yy"), 15); + m_builtinNumFmtsHash.insert(QStringLiteral("d-mmm"), 16); + m_builtinNumFmtsHash.insert(QStringLiteral("mmm-yy"), 17); + m_builtinNumFmtsHash.insert(QStringLiteral("h:mm AM/PM"), 18); + m_builtinNumFmtsHash.insert(QStringLiteral("h:mm:ss AM/PM"), 19); + m_builtinNumFmtsHash.insert(QStringLiteral("h:mm"), 20); + m_builtinNumFmtsHash.insert(QStringLiteral("h:mm:ss"), 21); + m_builtinNumFmtsHash.insert(QStringLiteral("m/d/yy h:mm"), 22); + + m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0_);(#,##0)"), 37); + m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0_);[Red](#,##0)"), 38); + m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0.00_);(#,##0.00)"), 39); + m_builtinNumFmtsHash.insert(QStringLiteral("(#,##0.00_);[Red](#,##0.00)"), 40); +// m_builtinNumFmtsHash.insert(QStringLiteral("_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(_)"), 41); +// m_builtinNumFmtsHash.insert(QStringLiteral("_($* #,##0_);_($* (#,##0);_($* \"-\"_);_(_)"), 42); +// m_builtinNumFmtsHash.insert(QStringLiteral("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(_)"), 43); +// m_builtinNumFmtsHash.insert(QStringLiteral("_($* #,##0.00_);_($* (#,##0.00);_($* \"-\"??_);_(_)"), 44); + m_builtinNumFmtsHash.insert(QStringLiteral("mm:ss"), 45); + m_builtinNumFmtsHash.insert(QStringLiteral("[h]:mm:ss"), 46); + m_builtinNumFmtsHash.insert(QStringLiteral("mm:ss.0"), 47); + m_builtinNumFmtsHash.insert(QStringLiteral("##0.0E+0"), 48); + m_builtinNumFmtsHash.insert(QStringLiteral("@"), 49); + } + + const QString str = format.numberFormat(); + if (!str.isEmpty()) { + //Assign proper number format index + if (m_builtinNumFmtsHash.contains(str)) { + const_cast<Format *>(&format)->fixNumberFormat(m_builtinNumFmtsHash[str], str); + } else if (m_customNumFmtsHash.contains(str)) { + const_cast<Format *>(&format)->fixNumberFormat(m_customNumFmtsHash[str]->formatIndex, str); + } else { + //Assign a new fmt Id. + const_cast<Format *>(&format)->fixNumberFormat(m_nextCustomNumFmtId, str); + + QSharedPointer<XlsxFormatNumberData> fmt(new XlsxFormatNumberData); + fmt->formatIndex = m_nextCustomNumFmtId; + fmt->formatString = str; + m_customNumFmtIdMap.insert(m_nextCustomNumFmtId, fmt); + m_customNumFmtsHash.insert(str, fmt); + + m_nextCustomNumFmtId += 1; + } + } else { + int id = format.numberFormatIndex(); + //Assign proper format code, this is needed by dxf format + if (m_customNumFmtIdMap.contains(id)) { + const_cast<Format *>(&format)->fixNumberFormat(id, m_customNumFmtIdMap[id]->formatString); + } else { + QHashIterator<QString, int> it(m_builtinNumFmtsHash); + bool find=false; + while (it.hasNext()) { + it.next(); + if (it.value() == id) { + const_cast<Format *>(&format)->fixNumberFormat(id, it.key()); + find = true; + break; + } + } + + if (!find) { + //Wrong numFmt + const_cast<Format *>(&format)->fixNumberFormat(id, QStringLiteral("General")); + } + } + } +} + +/* + Assign index to Font/Fill/Border and Format + + When \a force is true, add the format to the format list, even other format has + the same key have been in. + This is useful when reading existing .xlsx files which may contains duplicated formats. +*/ +void Styles::addXfFormat(const Format &format, bool force) +{ + if (format.isEmpty()) { + //Try do something for empty Format. + if (m_emptyFormatAdded && !force) + return; + m_emptyFormatAdded = true; + } + + //numFmt + if (format.hasNumFmtData() && !format.hasProperty(FormatPrivate::P_NumFmt_Id)) + fixNumFmt(format); + + //Font + if (format.hasFontData() && !format.fontIndexValid()) { + //Assign proper font index, if has font data. + if (!m_fontsHash.contains(format.fontKey())) + const_cast<Format *>(&format)->setFontIndex(m_fontsList.size()); + else + const_cast<Format *>(&format)->setFontIndex(m_fontsHash[format.fontKey()].fontIndex()); + } + if (!m_fontsHash.contains(format.fontKey())) { + //Still a valid font if the format has no fontData. (All font properties are default) + m_fontsList.append(format); + m_fontsHash[format.fontKey()] = format; + } + + //Fill + if (format.hasFillData() && !format.fillIndexValid()) { + //Assign proper fill index, if has fill data. + if (!m_fillsHash.contains(format.fillKey())) + const_cast<Format *>(&format)->setFillIndex(m_fillsList.size()); + else + const_cast<Format *>(&format)->setFillIndex(m_fillsHash[format.fillKey()].fillIndex()); + } + if (!m_fillsHash.contains(format.fillKey())) { + //Still a valid fill if the format has no fillData. (All fill properties are default) + m_fillsList.append(format); + m_fillsHash[format.fillKey()] = format; + } + + //Border + if (format.hasBorderData() && !format.borderIndexValid()) { + //Assign proper border index, if has border data. + if (!m_bordersHash.contains(format.borderKey())) + const_cast<Format *>(&format)->setBorderIndex(m_bordersList.size()); + else + const_cast<Format *>(&format)->setBorderIndex(m_bordersHash[format.borderKey()].borderIndex()); + } + if (!m_bordersHash.contains(format.borderKey())) { + //Still a valid border if the format has no borderData. (All border properties are default) + m_bordersList.append(format); + m_bordersHash[format.borderKey()] = format; + } + + //Format + if (!format.isEmpty() && !format.xfIndexValid()) { + if (m_xf_formatsHash.contains(format.formatKey())) + const_cast<Format *>(&format)->setXfIndex(m_xf_formatsHash[format.formatKey()].xfIndex()); + else + const_cast<Format *>(&format)->setXfIndex(m_xf_formatsList.size()); + } + if (!m_xf_formatsHash.contains(format.formatKey()) || force) { + m_xf_formatsList.append(format); + m_xf_formatsHash[format.formatKey()] = format; + } +} + +void Styles::addDxfFormat(const Format &format, bool force) +{ + //numFmt + if (format.hasNumFmtData()) + fixNumFmt(format); + + if (!format.isEmpty() && !format.dxfIndexValid()) { + if (m_dxf_formatsHash.contains(format.formatKey())) + const_cast<Format *>(&format)->setDxfIndex(m_dxf_formatsHash[format.formatKey()].dxfIndex()); + else + const_cast<Format *>(&format)->setDxfIndex(m_dxf_formatsList.size()); + } + if (!m_dxf_formatsHash.contains(format.formatKey()) || force) { + m_dxf_formatsList.append(format); + m_dxf_formatsHash[format.formatKey()] = format; + } +} + +void Styles::saveToXmlFile(QIODevice *device) const +{ + QXmlStreamWriter writer(device); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("styleSheet")); + writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main")); + + writeNumFmts(writer); + writeFonts(writer); + writeFills(writer); + writeBorders(writer); + + writer.writeStartElement(QStringLiteral("cellStyleXfs")); + writer.writeAttribute(QStringLiteral("count"), QStringLiteral("1")); + writer.writeStartElement(QStringLiteral("xf")); + writer.writeAttribute(QStringLiteral("numFmtId"), QStringLiteral("0")); + writer.writeAttribute(QStringLiteral("fontId"), QStringLiteral("0")); + writer.writeAttribute(QStringLiteral("fillId"), QStringLiteral("0")); + writer.writeAttribute(QStringLiteral("borderId"), QStringLiteral("0")); + writer.writeEndElement();//xf + writer.writeEndElement();//cellStyleXfs + + writeCellXfs(writer); + + writer.writeStartElement(QStringLiteral("cellStyles")); + writer.writeAttribute(QStringLiteral("count"), QStringLiteral("1")); + writer.writeStartElement(QStringLiteral("cellStyle")); + writer.writeAttribute(QStringLiteral("name"), QStringLiteral("Normal")); + writer.writeAttribute(QStringLiteral("xfId"), QStringLiteral("0")); + writer.writeAttribute(QStringLiteral("builtinId"), QStringLiteral("0")); + writer.writeEndElement();//cellStyle + writer.writeEndElement();//cellStyles + + writeDxfs(writer); + + writer.writeStartElement(QStringLiteral("tableStyles")); + writer.writeAttribute(QStringLiteral("count"), QStringLiteral("0")); + writer.writeAttribute(QStringLiteral("defaultTableStyle"), QStringLiteral("TableStyleMedium9")); + writer.writeAttribute(QStringLiteral("defaultPivotStyle"), QStringLiteral("PivotStyleLight16")); + writer.writeEndElement();//tableStyles + + writeColors(writer); + + writer.writeEndElement();//styleSheet + writer.writeEndDocument(); +} + +void Styles::writeNumFmts(QXmlStreamWriter &writer) const +{ + if (m_customNumFmtIdMap.size() == 0) + return; + + writer.writeStartElement(QStringLiteral("numFmts")); + writer.writeAttribute(QStringLiteral("count"), QString::number(m_customNumFmtIdMap.count())); + + QMapIterator<int, QSharedPointer<XlsxFormatNumberData> > it(m_customNumFmtIdMap); + while (it.hasNext()) { + it.next(); + writer.writeEmptyElement(QStringLiteral("numFmt")); + writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(it.value()->formatIndex)); + writer.writeAttribute(QStringLiteral("formatCode"), it.value()->formatString); + } + writer.writeEndElement();//numFmts +} + +/* +*/ +void Styles::writeFonts(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("fonts")); + writer.writeAttribute(QStringLiteral("count"), QString::number(m_fontsList.count())); + for (int i=0; i<m_fontsList.size(); ++i) + writeFont(writer, m_fontsList[i], false); + writer.writeEndElement();//fonts +} + +void Styles::writeFont(QXmlStreamWriter &writer, const Format &format, bool isDxf) const +{ + writer.writeStartElement(QStringLiteral("font")); + + //The condense and extend elements are mainly used in dxf format + if (format.hasProperty(FormatPrivate::P_Font_Condense) + && !format.boolProperty(FormatPrivate::P_Font_Condense)) { + writer.writeEmptyElement(QStringLiteral("condense")); + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0")); + } + if (format.hasProperty(FormatPrivate::P_Font_Extend) + && !format.boolProperty(FormatPrivate::P_Font_Extend)) { + writer.writeEmptyElement(QStringLiteral("extend")); + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("0")); + } + + if (format.fontBold()) + writer.writeEmptyElement(QStringLiteral("b")); + if (format.fontItalic()) + writer.writeEmptyElement(QStringLiteral("i")); + if (format.fontStrikeOut()) + writer.writeEmptyElement(QStringLiteral("strike")); + if (format.fontOutline()) + writer.writeEmptyElement(QStringLiteral("outline")); + if (format.boolProperty(FormatPrivate::P_Font_Shadow)) + writer.writeEmptyElement(QStringLiteral("shadow")); + if (format.hasProperty(FormatPrivate::P_Font_Underline)) { + Format::FontUnderline u = format.fontUnderline(); + if (u != Format::FontUnderlineNone) { + writer.writeEmptyElement(QStringLiteral("u")); + if (u== Format::FontUnderlineDouble) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("double")); + else if (u == Format::FontUnderlineSingleAccounting) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("singleAccounting")); + else if (u == Format::FontUnderlineDoubleAccounting) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("doubleAccounting")); + } + } + if (format.hasProperty(FormatPrivate::P_Font_Script)) { + Format::FontScript s = format.fontScript(); + if (s != Format::FontScriptNormal) { + writer.writeEmptyElement(QStringLiteral("vertAlign")); + if (s == Format::FontScriptSuper) + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("superscript")); + else + writer.writeAttribute(QStringLiteral("val"), QStringLiteral("subscript")); + } + } + + if (!isDxf && format.hasProperty(FormatPrivate::P_Font_Size)) { + writer.writeEmptyElement(QStringLiteral("sz")); + writer.writeAttribute(QStringLiteral("val"), QString::number(format.fontSize())); + } + + if (format.hasProperty(FormatPrivate::P_Font_Color)) { + XlsxColor color = format.property(FormatPrivate::P_Font_Color).value<XlsxColor>(); + color.saveToXml(writer); + } + + if (!isDxf) { + if (!format.fontName().isEmpty()) { + writer.writeEmptyElement(QStringLiteral("name")); + writer.writeAttribute(QStringLiteral("val"), format.fontName()); + } + if (format.hasProperty(FormatPrivate::P_Font_Charset)) { + writer.writeEmptyElement(QStringLiteral("charset")); + writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Charset))); + } + if (format.hasProperty(FormatPrivate::P_Font_Family)) { + writer.writeEmptyElement(QStringLiteral("family")); + writer.writeAttribute(QStringLiteral("val"), QString::number(format.intProperty(FormatPrivate::P_Font_Family))); + } + + if (format.hasProperty(FormatPrivate::P_Font_Scheme)) { + writer.writeEmptyElement(QStringLiteral("scheme")); + writer.writeAttribute(QStringLiteral("val"), format.stringProperty(FormatPrivate::P_Font_Scheme)); + } + } + writer.writeEndElement(); //font +} + +void Styles::writeFills(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("fills")); + writer.writeAttribute(QStringLiteral("count"), QString::number(m_fillsList.size())); + + for (int i=0; i<m_fillsList.size(); ++i) + writeFill(writer, m_fillsList[i]); + + writer.writeEndElement(); //fills +} + +void Styles::writeFill(QXmlStreamWriter &writer, const Format &fill, bool isDxf) const +{ + static QMap<int, QString> patternStrings; + if (patternStrings.isEmpty()) { + patternStrings[Format::PatternNone] = QStringLiteral("none"); + patternStrings[Format::PatternSolid] = QStringLiteral("solid"); + patternStrings[Format::PatternMediumGray] = QStringLiteral("mediumGray"); + patternStrings[Format::PatternDarkGray] = QStringLiteral("darkGray"); + patternStrings[Format::PatternLightGray] = QStringLiteral("lightGray"); + patternStrings[Format::PatternDarkHorizontal] = QStringLiteral("darkHorizontal"); + patternStrings[Format::PatternDarkVertical] = QStringLiteral("darkVertical"); + patternStrings[Format::PatternDarkDown] = QStringLiteral("darkDown"); + patternStrings[Format::PatternDarkUp] = QStringLiteral("darkUp"); + patternStrings[Format::PatternDarkGrid] = QStringLiteral("darkGrid"); + patternStrings[Format::PatternDarkTrellis] = QStringLiteral("darkTrellis"); + patternStrings[Format::PatternLightHorizontal] = QStringLiteral("lightHorizontal"); + patternStrings[Format::PatternLightVertical] = QStringLiteral("lightVertical"); + patternStrings[Format::PatternLightDown] = QStringLiteral("lightDown"); + patternStrings[Format::PatternLightUp] = QStringLiteral("lightUp"); + patternStrings[Format::PatternLightTrellis] = QStringLiteral("lightTrellis"); + patternStrings[Format::PatternGray125] = QStringLiteral("gray125"); + patternStrings[Format::PatternGray0625] = QStringLiteral("gray0625"); + patternStrings[Format::PatternLightGrid] = QStringLiteral("lightGrid"); + } + + writer.writeStartElement(QStringLiteral("fill")); + writer.writeStartElement(QStringLiteral("patternFill")); + Format::FillPattern pattern = fill.fillPattern(); + // For normal fill formats, Excel prefer to outputing the default "none" attribute + // But for dxf, Excel prefer to omiting the default "none" + // Though not make any difference, but it make easier to compare origin files with generate files during debug + if (!(pattern == Format::PatternNone && isDxf)) + writer.writeAttribute(QStringLiteral("patternType"), patternStrings[pattern]); + // For a solid fill, Excel reverses the role of foreground and background colours + if (fill.fillPattern() == Format::PatternSolid) { + if (fill.hasProperty(FormatPrivate::P_Fill_BgColor)) + fill.property(FormatPrivate::P_Fill_BgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("fgColor")); + if (fill.hasProperty(FormatPrivate::P_Fill_FgColor)) + fill.property(FormatPrivate::P_Fill_FgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("bgColor")); + } else { + if (fill.hasProperty(FormatPrivate::P_Fill_FgColor)) + fill.property(FormatPrivate::P_Fill_FgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("fgColor")); + if (fill.hasProperty(FormatPrivate::P_Fill_BgColor)) + fill.property(FormatPrivate::P_Fill_BgColor).value<XlsxColor>().saveToXml(writer, QStringLiteral("bgColor")); + } + writer.writeEndElement();//patternFill + writer.writeEndElement();//fill +} + +void Styles::writeBorders(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("borders")); + writer.writeAttribute(QStringLiteral("count"), QString::number(m_bordersList.count())); + for (int i=0; i<m_bordersList.size(); ++i) + writeBorder(writer, m_bordersList[i]); + writer.writeEndElement();//borders +} + +void Styles::writeBorder(QXmlStreamWriter &writer, const Format &border, bool isDxf) const +{ + writer.writeStartElement(QStringLiteral("border")); + if (border.hasProperty(FormatPrivate::P_Border_DiagonalType)) { + Format::DiagonalBorderType t = border.diagonalBorderType(); + if (t == Format::DiagonalBorderUp) { + writer.writeAttribute(QStringLiteral("diagonalUp"), QStringLiteral("1")); + } else if (t == Format::DiagonalBorderDown) { + writer.writeAttribute(QStringLiteral("diagonalDown"), QStringLiteral("1")); + } else if (t == Format::DiagnoalBorderBoth) { + writer.writeAttribute(QStringLiteral("diagonalUp"), QStringLiteral("1")); + writer.writeAttribute(QStringLiteral("diagonalDown"), QStringLiteral("1")); + } + } + + writeSubBorder(writer, QStringLiteral("left"), border.leftBorderStyle(), border.property(FormatPrivate::P_Border_LeftColor).value<XlsxColor>()); + writeSubBorder(writer, QStringLiteral("right"), border.rightBorderStyle(), border.property(FormatPrivate::P_Border_RightColor).value<XlsxColor>()); + writeSubBorder(writer, QStringLiteral("top"), border.topBorderStyle(), border.property(FormatPrivate::P_Border_TopColor).value<XlsxColor>()); + writeSubBorder(writer, QStringLiteral("bottom"), border.bottomBorderStyle(), border.property(FormatPrivate::P_Border_BottomColor).value<XlsxColor>()); + + //Condition DXF formats don't allow diagonal style + if (!isDxf) + writeSubBorder(writer, QStringLiteral("diagonal"), border.diagonalBorderStyle(), border.property(FormatPrivate::P_Border_DiagonalColor).value<XlsxColor>()); + + if (isDxf) { +// writeSubBorder(wirter, QStringLiteral("vertical"), ); +// writeSubBorder(writer, QStringLiteral("horizontal"), ); + } + + writer.writeEndElement();//border +} + +void Styles::writeSubBorder(QXmlStreamWriter &writer, const QString &type, int style, const XlsxColor &color) const +{ + if (style == Format::BorderNone) { + writer.writeEmptyElement(type); + return; + } + + static QMap<int, QString> stylesString; + if (stylesString.isEmpty()) { + stylesString[Format::BorderNone] = QStringLiteral("none"); + stylesString[Format::BorderThin] = QStringLiteral("thin"); + stylesString[Format::BorderMedium] = QStringLiteral("medium"); + stylesString[Format::BorderDashed] = QStringLiteral("dashed"); + stylesString[Format::BorderDotted] = QStringLiteral("dotted"); + stylesString[Format::BorderThick] = QStringLiteral("thick"); + stylesString[Format::BorderDouble] = QStringLiteral("double"); + stylesString[Format::BorderHair] = QStringLiteral("hair"); + stylesString[Format::BorderMediumDashed] = QStringLiteral("mediumDashed"); + stylesString[Format::BorderDashDot] = QStringLiteral("dashDot"); + stylesString[Format::BorderMediumDashDot] = QStringLiteral("mediumDashDot"); + stylesString[Format::BorderDashDotDot] = QStringLiteral("dashDotDot"); + stylesString[Format::BorderMediumDashDotDot] = QStringLiteral("mediumDashDotDot"); + stylesString[Format::BorderSlantDashDot] = QStringLiteral("slantDashDot"); + } + + writer.writeStartElement(type); + writer.writeAttribute(QStringLiteral("style"), stylesString[style]); + color.saveToXml(writer); //write color element + + writer.writeEndElement();//type +} + +void Styles::writeCellXfs(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("cellXfs")); + writer.writeAttribute(QStringLiteral("count"), QString::number(m_xf_formatsList.size())); + foreach (const Format &format, m_xf_formatsList) { + int xf_id = 0; + writer.writeStartElement(QStringLiteral("xf")); + writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(format.numberFormatIndex())); + writer.writeAttribute(QStringLiteral("fontId"), QString::number(format.fontIndex())); + writer.writeAttribute(QStringLiteral("fillId"), QString::number(format.fillIndex())); + writer.writeAttribute(QStringLiteral("borderId"), QString::number(format.borderIndex())); + writer.writeAttribute(QStringLiteral("xfId"), QString::number(xf_id)); + if (format.hasNumFmtData()) + writer.writeAttribute(QStringLiteral("applyNumberFormat"), QStringLiteral("1")); + if (format.hasFontData()) + writer.writeAttribute(QStringLiteral("applyFont"), QStringLiteral("1")); + if (format.hasFillData()) + writer.writeAttribute(QStringLiteral("applyFill"), QStringLiteral("1")); + if (format.hasBorderData()) + writer.writeAttribute(QStringLiteral("applyBorder"), QStringLiteral("1")); + if (format.hasAlignmentData()) + writer.writeAttribute(QStringLiteral("applyAlignment"), QStringLiteral("1")); + + if (format.hasAlignmentData()) { + writer.writeEmptyElement(QStringLiteral("alignment")); + if (format.hasProperty(FormatPrivate::P_Alignment_AlignH)) { + switch (format.horizontalAlignment()) { + case Format::AlignLeft: + writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("left")); + break; + case Format::AlignHCenter: + writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("center")); + break; + case Format::AlignRight: + writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("right")); + break; + case Format::AlignHFill: + writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("fill")); + break; + case Format::AlignHJustify: + writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("justify")); + break; + case Format::AlignHMerge: + writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("centerContinuous")); + break; + case Format::AlignHDistributed: + writer.writeAttribute(QStringLiteral("horizontal"), QStringLiteral("distributed")); + break; + default: + break; + } + } + + if (format.hasProperty(FormatPrivate::P_Alignment_AlignV)) { + switch (format.verticalAlignment()) { + case Format::AlignTop: + writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("top")); + break; + case Format::AlignVCenter: + writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("center")); + break; + case Format::AlignVJustify: + writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("justify")); + break; + case Format::AlignVDistributed: + writer.writeAttribute(QStringLiteral("vertical"), QStringLiteral("distributed")); + break; + default: + break; + } + } + if (format.hasProperty(FormatPrivate::P_Alignment_Indent)) + writer.writeAttribute(QStringLiteral("indent"), QString::number(format.indent())); + if (format.hasProperty(FormatPrivate::P_Alignment_Wrap) && format.textWrap()) + writer.writeAttribute(QStringLiteral("wrapText"), QStringLiteral("1")); + if (format.hasProperty(FormatPrivate::P_Alignment_ShinkToFit) && format.shrinkToFit()) + writer.writeAttribute(QStringLiteral("shrinkToFit"), QStringLiteral("1")); + if (format.hasProperty(FormatPrivate::P_Alignment_Rotation)) + writer.writeAttribute(QStringLiteral("textRotation"), QString::number(format.rotation())); + } + + writer.writeEndElement();//xf + } + writer.writeEndElement();//cellXfs +} + +void Styles::writeDxfs(QXmlStreamWriter &writer) const +{ + writer.writeStartElement(QStringLiteral("dxfs")); + writer.writeAttribute(QStringLiteral("count"), QString::number(m_dxf_formatsList.size())); + foreach (const Format &format, m_dxf_formatsList) + writeDxf(writer, format); + writer.writeEndElement(); //dxfs +} + +void Styles::writeDxf(QXmlStreamWriter &writer, const Format &format) const +{ + writer.writeStartElement(QStringLiteral("dxf")); + + if (format.hasFontData()) + writeFont(writer, format, true); + + if (format.hasNumFmtData()) { + writer.writeEmptyElement(QStringLiteral("numFmt")); + writer.writeAttribute(QStringLiteral("numFmtId"), QString::number(format.numberFormatIndex())); + writer.writeAttribute(QStringLiteral("formatCode"), format.numberFormat()); + } + + if (format.hasFillData()) + writeFill(writer, format, true); + + if (format.hasBorderData()) + writeBorder(writer, format, true); + + writer.writeEndElement();//dxf +} + +void Styles::writeColors(QXmlStreamWriter &writer) const +{ + if (m_isIndexedColorsDefault) //Don't output the default indexdeColors + return; + + writer.writeStartElement(QStringLiteral("colors")); + + writer.writeStartElement(QStringLiteral("indexedColors")); + foreach(QColor color, m_indexedColors) { + writer.writeEmptyElement(QStringLiteral("rgbColor")); + writer.writeAttribute(QStringLiteral("rgb"), XlsxColor::toARGBString(color)); + } + + writer.writeEndElement();//indexedColors + + writer.writeEndElement();//colors +} + +bool Styles::readNumFmts(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("numFmts")); + QXmlStreamAttributes attributes = reader.attributes(); + bool hasCount = attributes.hasAttribute(QLatin1String("count")); + int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1; + + //Read utill we find the numFmts end tag or .... + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("numFmts"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("numFmt")) { + QXmlStreamAttributes attributes = reader.attributes(); + QSharedPointer<XlsxFormatNumberData> fmt (new XlsxFormatNumberData); + fmt->formatIndex = attributes.value(QLatin1String("numFmtId")).toString().toInt(); + fmt->formatString = attributes.value(QLatin1String("formatCode")).toString(); + if (fmt->formatIndex >= m_nextCustomNumFmtId) + m_nextCustomNumFmtId = fmt->formatIndex + 1; + m_customNumFmtIdMap.insert(fmt->formatIndex, fmt); + m_customNumFmtsHash.insert(fmt->formatString, fmt); + } + } + } + + if (reader.hasError()) + qWarning()<<reader.errorString(); + + if (hasCount && (count != m_customNumFmtIdMap.size())) + qWarning("error read custom numFmts"); + + return true; +} + +bool Styles::readFonts(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("fonts")); + QXmlStreamAttributes attributes = reader.attributes(); + bool hasCount = attributes.hasAttribute(QLatin1String("count")); + int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1; + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("fonts"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("font")) { + Format format; + readFont(reader, format); + m_fontsList.append(format); + m_fontsHash.insert(format.fontKey(), format); + if (format.isValid()) + format.setFontIndex(m_fontsList.size()-1); + } + } + } + if (reader.hasError()) + qWarning()<<reader.errorString(); + + if (hasCount && (count != m_fontsList.size())) + qWarning("error read fonts"); + return true; +} + +bool Styles::readFont(QXmlStreamReader &reader, Format &format) +{ + Q_ASSERT(reader.name() == QLatin1String("font")); + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("font"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + QXmlStreamAttributes attributes = reader.attributes(); + if (reader.name() == QLatin1String("name")) { + format.setFontName(attributes.value(QLatin1String("val")).toString()); + } else if (reader.name() == QLatin1String("charset")) { + format.setProperty(FormatPrivate::P_Font_Charset, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("family")) { + format.setProperty(FormatPrivate::P_Font_Family, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("b")) { + format.setFontBold(true); + } else if (reader.name() == QLatin1String("i")) { + format.setFontItalic(true); + } else if (reader.name() == QLatin1String("strike")) { + format.setFontStrikeOut(true); + } else if (reader.name() == QLatin1String("outline")) { + format.setFontOutline(true); + } else if (reader.name() == QLatin1String("shadow")) { + format.setProperty(FormatPrivate::P_Font_Shadow, true); + } else if (reader.name() == QLatin1String("condense")) { + format.setProperty(FormatPrivate::P_Font_Condense, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("extend")) { + format.setProperty(FormatPrivate::P_Font_Extend, attributes.value(QLatin1String("val")).toString().toInt()); + } else if (reader.name() == QLatin1String("color")) { + XlsxColor color; + color.loadFromXml(reader); + format.setProperty(FormatPrivate::P_Font_Color, color); + } else if (reader.name() == QLatin1String("sz")) { + int sz = attributes.value(QLatin1String("val")).toString().toInt(); + format.setFontSize(sz); + } else if (reader.name() == QLatin1String("u")) { + QString value = attributes.value(QLatin1String("val")).toString(); + if (value == QLatin1String("double")) + format.setFontUnderline(Format::FontUnderlineDouble); + else if (value == QLatin1String("doubleAccounting")) + format.setFontUnderline(Format::FontUnderlineDoubleAccounting); + else if (value == QLatin1String("singleAccounting")) + format.setFontUnderline(Format::FontUnderlineSingleAccounting); + else + format.setFontUnderline(Format::FontUnderlineSingle); + } else if (reader.name() == QLatin1String("vertAlign")) { + QString value = attributes.value(QLatin1String("val")).toString(); + if (value == QLatin1String("superscript")) + format.setFontScript(Format::FontScriptSuper); + else if (value == QLatin1String("subscript")) + format.setFontScript(Format::FontScriptSub); + } else if (reader.name() == QLatin1String("scheme")) { + format.setProperty(FormatPrivate::P_Font_Scheme, attributes.value(QLatin1String("val")).toString()); + } + } + } + return true; +} + +bool Styles::readFills(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("fills")); + + QXmlStreamAttributes attributes = reader.attributes(); + bool hasCount = attributes.hasAttribute(QLatin1String("count")); + int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1; + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("fills"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("fill")) { + Format fill; + readFill(reader, fill); + m_fillsList.append(fill); + m_fillsHash.insert(fill.fillKey(), fill); + if (fill.isValid()) + fill.setFillIndex(m_fillsList.size()-1); + } + } + } + if (reader.hasError()) + qWarning()<<reader.errorString(); + + if (hasCount && (count != m_fillsList.size())) + qWarning("error read fills"); + return true; +} + +bool Styles::readFill(QXmlStreamReader &reader, Format &fill) +{ + Q_ASSERT(reader.name() == QLatin1String("fill")); + + static QMap<QString, Format::FillPattern> patternValues; + if (patternValues.isEmpty()) { + patternValues[QStringLiteral("none")] = Format::PatternNone; + patternValues[QStringLiteral("solid")] = Format::PatternSolid; + patternValues[QStringLiteral("mediumGray")] = Format::PatternMediumGray; + patternValues[QStringLiteral("darkGray")] = Format::PatternDarkGray; + patternValues[QStringLiteral("lightGray")] = Format::PatternLightGray; + patternValues[QStringLiteral("darkHorizontal")] = Format::PatternDarkHorizontal; + patternValues[QStringLiteral("darkVertical")] = Format::PatternDarkVertical; + patternValues[QStringLiteral("darkDown")] = Format::PatternDarkDown; + patternValues[QStringLiteral("darkUp")] = Format::PatternDarkUp; + patternValues[QStringLiteral("darkGrid")] = Format::PatternDarkGrid; + patternValues[QStringLiteral("darkTrellis")] = Format::PatternDarkTrellis; + patternValues[QStringLiteral("lightHorizontal")] = Format::PatternLightHorizontal; + patternValues[QStringLiteral("lightVertical")] = Format::PatternLightVertical; + patternValues[QStringLiteral("lightDown")] = Format::PatternLightDown; + patternValues[QStringLiteral("lightUp")] = Format::PatternLightUp; + patternValues[QStringLiteral("lightTrellis")] = Format::PatternLightTrellis; + patternValues[QStringLiteral("gray125")] = Format::PatternGray125; + patternValues[QStringLiteral("gray0625")] = Format::PatternGray0625; + patternValues[QStringLiteral("lightGrid")] = Format::PatternLightGrid; + } + + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("fill"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("patternFill")) { + QXmlStreamAttributes attributes = reader.attributes(); + if (attributes.hasAttribute(QLatin1String("patternType"))) { + QString pattern = attributes.value(QLatin1String("patternType")).toString(); + fill.setFillPattern(patternValues.contains(pattern) ? patternValues[pattern] : Format::PatternNone); + + //parse foreground and background colors if they exist + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("patternFill"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("fgColor")) { + XlsxColor c; + c.loadFromXml(reader); + if (fill.fillPattern() == Format::PatternSolid) + fill.setProperty(FormatPrivate::P_Fill_BgColor, c); + else + fill.setProperty(FormatPrivate::P_Fill_FgColor, c); + } else if (reader.name() == QLatin1String("bgColor")) { + XlsxColor c; + c.loadFromXml(reader); + if (fill.fillPattern() == Format::PatternSolid) + fill.setProperty(FormatPrivate::P_Fill_FgColor, c); + else + fill.setProperty(FormatPrivate::P_Fill_BgColor, c); + } + } + } + } + } + } + } + + return true; +} + +bool Styles::readBorders(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("borders")); + + QXmlStreamAttributes attributes = reader.attributes(); + bool hasCount = attributes.hasAttribute(QLatin1String("count")); + int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1; + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("borders"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("border")) { + Format border; + readBorder(reader, border); + m_bordersList.append(border); + m_bordersHash.insert(border.borderKey(), border); + if (border.isValid()) + border.setBorderIndex(m_bordersList.size()-1); + } + } + } + + if (reader.hasError()) + qWarning()<<reader.errorString(); + + if (hasCount && (count != m_bordersList.size())) + qWarning("error read borders"); + + return true; +} + +bool Styles::readBorder(QXmlStreamReader &reader, Format &border) +{ + Q_ASSERT(reader.name() == QLatin1String("border")); + + QXmlStreamAttributes attributes = reader.attributes(); + bool isUp = attributes.hasAttribute(QLatin1String("diagonalUp")); + bool isDown = attributes.hasAttribute(QLatin1String("diagonalUp")); + if (isUp && isDown) + border.setDiagonalBorderType(Format::DiagnoalBorderBoth); + else if (isUp) + border.setDiagonalBorderType(Format::DiagonalBorderUp); + else if (isDown) + border.setDiagonalBorderType(Format::DiagonalBorderDown); + + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("border"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("left") || reader.name() == QLatin1String("right") + || reader.name() == QLatin1String("top") || reader.name() == QLatin1String("bottom") + || reader.name() == QLatin1String("diagonal") ) { + Format::BorderStyle style(Format::BorderNone); + XlsxColor color; + readSubBorder(reader, reader.name().toString(), style, color); + + if (reader.name() == QLatin1String("left")) { + border.setLeftBorderStyle(style); + if (!color.isInvalid()) + border.setProperty(FormatPrivate::P_Border_LeftColor, color); + } else if (reader.name() == QLatin1String("right")) { + border.setRightBorderStyle(style); + if (!color.isInvalid()) + border.setProperty(FormatPrivate::P_Border_RightColor, color); + } else if (reader.name() == QLatin1String("top")) { + border.setTopBorderStyle(style); + if (!color.isInvalid()) + border.setProperty(FormatPrivate::P_Border_TopColor, color); + } else if (reader.name() == QLatin1String("bottom")) { + border.setBottomBorderStyle(style); + if (!color.isInvalid()) + border.setProperty(FormatPrivate::P_Border_BottomColor, color); + } else if (reader.name() == QLatin1String("diagonal")) { + border.setDiagonalBorderStyle(style); + if (!color.isInvalid()) + border.setProperty(FormatPrivate::P_Border_DiagonalColor, color); + } + } + } + + if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == QLatin1String("border")) + break; + } + + return true; +} + +bool Styles::readSubBorder(QXmlStreamReader &reader, const QString &name, Format::BorderStyle &style, XlsxColor &color) +{ + Q_ASSERT(reader.name() == name); + + static QMap<QString, Format::BorderStyle> stylesStringsMap; + if (stylesStringsMap.isEmpty()) { + stylesStringsMap[QStringLiteral("none")] = Format::BorderNone; + stylesStringsMap[QStringLiteral("thin")] = Format::BorderThin; + stylesStringsMap[QStringLiteral("medium")] = Format::BorderMedium; + stylesStringsMap[QStringLiteral("dashed")] = Format::BorderDashed; + stylesStringsMap[QStringLiteral("dotted")] = Format::BorderDotted; + stylesStringsMap[QStringLiteral("thick")] = Format::BorderThick; + stylesStringsMap[QStringLiteral("double")] = Format::BorderDouble; + stylesStringsMap[QStringLiteral("hair")] = Format::BorderHair; + stylesStringsMap[QStringLiteral("mediumDashed")] = Format::BorderMediumDashed; + stylesStringsMap[QStringLiteral("dashDot")] = Format::BorderDashDot; + stylesStringsMap[QStringLiteral("mediumDashDot")] = Format::BorderMediumDashDot; + stylesStringsMap[QStringLiteral("dashDotDot")] = Format::BorderDashDotDot; + stylesStringsMap[QStringLiteral("mediumDashDotDot")] = Format::BorderMediumDashDotDot; + stylesStringsMap[QStringLiteral("slantDashDot")] = Format::BorderSlantDashDot; + } + + QXmlStreamAttributes attributes = reader.attributes(); + if (attributes.hasAttribute(QLatin1String("style"))) { + QString styleString = attributes.value(QLatin1String("style")).toString(); + if (stylesStringsMap.contains(styleString)) { + //get style + style = stylesStringsMap[styleString]; + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == name)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("color")) + color.loadFromXml(reader); + } + } + } + } + + return true; +} + +bool Styles::readCellXfs(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("cellXfs")); + QXmlStreamAttributes attributes = reader.attributes(); + bool hasCount = attributes.hasAttribute(QLatin1String("count")); + int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1; + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("cellXfs"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("xf")) { + + Format format; + QXmlStreamAttributes xfAttrs = reader.attributes(); + + // qDebug()<<reader.name()<<reader.tokenString()<<" ........."; + // for (int i=0; i<xfAttrs.size(); ++i) + // qDebug()<<"... "<<i<<" "<<xfAttrs[i].name()<<xfAttrs[i].value(); + + if (xfAttrs.hasAttribute(QLatin1String("numFmtId"))) { + int numFmtIndex = xfAttrs.value(QLatin1String("numFmtId")).toString().toInt(); + bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyNumberFormat")).toString()); + if(apply) { + if (!m_customNumFmtIdMap.contains(numFmtIndex)) + format.setNumberFormatIndex(numFmtIndex); + else + format.setNumberFormat(numFmtIndex, m_customNumFmtIdMap[numFmtIndex]->formatString); + } + } + + if (xfAttrs.hasAttribute(QLatin1String("fontId"))) { + int fontIndex = xfAttrs.value(QLatin1String("fontId")).toString().toInt(); + if (fontIndex >= m_fontsList.size()) { + qDebug("Error read styles.xml, cellXfs fontId"); + } else { + bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyFont")).toString()); + if(apply) { + Format fontFormat = m_fontsList[fontIndex]; + for (int i=FormatPrivate::P_Font_STARTID; i<FormatPrivate::P_Font_ENDID; ++i) { + if (fontFormat.hasProperty(i)) + format.setProperty(i, fontFormat.property(i)); + } + } + } + } + + if (xfAttrs.hasAttribute(QLatin1String("fillId"))) { + int id = xfAttrs.value(QLatin1String("fillId")).toString().toInt(); + if (id >= m_fillsList.size()) { + qDebug("Error read styles.xml, cellXfs fillId"); + } else { + bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyFill")).toString()); + if(apply) { + Format fillFormat = m_fillsList[id]; + for (int i=FormatPrivate::P_Fill_STARTID; i<FormatPrivate::P_Fill_ENDID; ++i) { + if (fillFormat.hasProperty(i)) + format.setProperty(i, fillFormat.property(i)); + } + } + } + } + + if (xfAttrs.hasAttribute(QLatin1String("borderId"))) { + int id = xfAttrs.value(QLatin1String("borderId")).toString().toInt(); + if (id >= m_bordersList.size()) { + qDebug("Error read styles.xml, cellXfs borderId"); + } else { + bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyBorder")).toString()); + if(apply) { + Format borderFormat = m_bordersList[id]; + for (int i=FormatPrivate::P_Border_STARTID; i<FormatPrivate::P_Border_ENDID; ++i) { + if (borderFormat.hasProperty(i)) + format.setProperty(i, borderFormat.property(i)); + } + } + } + } + + bool apply = parseXsdBoolean(xfAttrs.value(QLatin1String("applyAlignment")).toString()); + if(apply) { + reader.readNextStartElement(); + if (reader.name() == QLatin1String("alignment")) { + QXmlStreamAttributes alignAttrs = reader.attributes(); + + if (alignAttrs.hasAttribute(QLatin1String("horizontal"))) { + static QMap<QString, Format::HorizontalAlignment> alignStringMap; + if (alignStringMap.isEmpty()) { + alignStringMap.insert(QStringLiteral("left"), Format::AlignLeft); + alignStringMap.insert(QStringLiteral("center"), Format::AlignHCenter); + alignStringMap.insert(QStringLiteral("right"), Format::AlignRight); + alignStringMap.insert(QStringLiteral("justify"), Format::AlignHJustify); + alignStringMap.insert(QStringLiteral("centerContinuous"), Format::AlignHMerge); + alignStringMap.insert(QStringLiteral("distributed"), Format::AlignHDistributed); + } + QString str = alignAttrs.value(QLatin1String("horizontal")).toString(); + if (alignStringMap.contains(str)) + format.setHorizontalAlignment(alignStringMap[str]); + } + + if (alignAttrs.hasAttribute(QLatin1String("vertical"))) { + static QMap<QString, Format::VerticalAlignment> alignStringMap; + if (alignStringMap.isEmpty()) { + alignStringMap.insert(QStringLiteral("top"), Format::AlignTop); + alignStringMap.insert(QStringLiteral("center"), Format::AlignVCenter); + alignStringMap.insert(QStringLiteral("justify"), Format::AlignVJustify); + alignStringMap.insert(QStringLiteral("distributed"), Format::AlignVDistributed); + } + QString str = alignAttrs.value(QLatin1String("vertical")).toString(); + if (alignStringMap.contains(str)) + format.setVerticalAlignment(alignStringMap[str]); + } + + if (alignAttrs.hasAttribute(QLatin1String("indent"))) { + int indent = alignAttrs.value(QLatin1String("indent")).toString().toInt(); + format.setIndent(indent); + } + + if (alignAttrs.hasAttribute(QLatin1String("textRotation"))) { + int rotation = alignAttrs.value(QLatin1String("textRotation")).toString().toInt(); + format.setRotation(rotation); + } + + if (alignAttrs.hasAttribute(QLatin1String("wrapText"))) + format.setTextWarp(true); + + if (alignAttrs.hasAttribute(QLatin1String("shrinkToFit"))) + format.setShrinkToFit(true); + + } + } + + addXfFormat(format, true); + } + } + } + + if (reader.hasError()) + qWarning()<<reader.errorString(); + + if (hasCount && (count != m_xf_formatsList.size())) + qWarning("error read CellXfs"); + + return true; +} + +bool Styles::readDxfs(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("dxfs")); + QXmlStreamAttributes attributes = reader.attributes(); + bool hasCount = attributes.hasAttribute(QLatin1String("count")); + int count = hasCount ? attributes.value(QLatin1String("count")).toString().toInt() : -1; + while (!reader.atEnd() && !(reader.tokenType() == QXmlStreamReader::EndElement + && reader.name() == QLatin1String("dxfs"))) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("dxf")) + readDxf(reader); + } + } + if (reader.hasError()) + qWarning()<<reader.errorString(); + + if (hasCount && (count != m_dxf_formatsList.size())) + qWarning("error read dxfs"); + + return true; +} + +bool Styles::readDxf(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("dxf")); + Format format; + while (!reader.atEnd() && !(reader.name() == QLatin1String("dxf") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("numFmt")) { + QXmlStreamAttributes attributes = reader.attributes(); + int id = attributes.value(QLatin1String("numFmtId")).toString().toInt(); + QString code = attributes.value(QLatin1String("formatCode")).toString(); + format.setNumberFormat(id, code); + } else if (reader.name() == QLatin1String("font")) { + readFont(reader, format); + } else if (reader.name() == QLatin1String("fill")) { + readFill(reader, format); + } else if (reader.name() == QLatin1String("border")) { + readBorder(reader, format); + } + } + } + addDxfFormat(format, true); + return true; +} + +bool Styles::readColors(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("colors")); + while (!reader.atEnd() && !(reader.name() == QLatin1String("colors") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("indexedColors")) { + readIndexedColors(reader); + } else if (reader.name() == QLatin1String("mruColors")) { + + } + } + } + return true; +} + +bool Styles::readIndexedColors(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("indexedColors")); + m_indexedColors.clear(); + while (!reader.atEnd() && !(reader.name() == QLatin1String("indexedColors") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("rgbColor")) { + QString color = reader.attributes().value(QLatin1String("rgb")).toString(); + m_indexedColors.append(XlsxColor::fromARGBString(color)); + } + } + } + if (!m_indexedColors.isEmpty()) + m_isIndexedColorsDefault = false; + return true; +} + +bool Styles::loadFromXmlFile(QIODevice *device) +{ + QXmlStreamReader reader(device); + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("numFmts")) { + readNumFmts(reader); + } else if (reader.name() == QLatin1String("fonts")) { + readFonts(reader); + } else if (reader.name() == QLatin1String("fills")) { + readFills(reader); + } else if (reader.name() == QLatin1String("borders")) { + readBorders(reader); + } else if (reader.name() == QLatin1String("cellStyleXfs")) { + + } else if (reader.name() == QLatin1String("cellXfs")) { + readCellXfs(reader); + } else if (reader.name() == QLatin1String("cellStyles")) { + + } else if (reader.name() == QLatin1String("dxfs")) { + readDxfs(reader); + } else if (reader.name() == QLatin1String("colors")) { + readColors(reader); + } + } + + if (reader.hasError()) { + qDebug()<<"Error when read style file: "<<reader.errorString(); + } + } + return true; +} + +QColor Styles::getColorByIndex(int idx) +{ + if (m_indexedColors.isEmpty()) { + m_indexedColors<<QColor("#000000") <<QColor("#FFFFFF") <<QColor("#FF0000") <<QColor("#00FF00") + <<QColor("#0000FF") <<QColor("#FFFF00") <<QColor("#FF00FF") <<QColor("#00FFFF") + <<QColor("#000000") <<QColor("#FFFFFF") <<QColor("#FF0000") <<QColor("#00FF00") + <<QColor("#0000FF") <<QColor("#FFFF00") <<QColor("#FF00FF") <<QColor("#00FFFF") + <<QColor("#800000") <<QColor("#008000") <<QColor("#000080") <<QColor("#808000") + <<QColor("#800080") <<QColor("#008080") <<QColor("#C0C0C0") <<QColor("#808080") + <<QColor("#9999FF") <<QColor("#993366") <<QColor("#FFFFCC") <<QColor("#CCFFFF") + <<QColor("#660066") <<QColor("#FF8080") <<QColor("#0066CC") <<QColor("#CCCCFF") + <<QColor("#000080") <<QColor("#FF00FF") <<QColor("#FFFF00") <<QColor("#00FFFF") + <<QColor("#800080") <<QColor("#800000") <<QColor("#008080") <<QColor("#0000FF") + <<QColor("#00CCFF") <<QColor("#CCFFFF") <<QColor("#CCFFCC") <<QColor("#FFFF99") + <<QColor("#99CCFF") <<QColor("#FF99CC") <<QColor("#CC99FF") <<QColor("#FFCC99") + <<QColor("#3366FF") <<QColor("#33CCCC") <<QColor("#99CC00") <<QColor("#FFCC00") + <<QColor("#FF9900") <<QColor("#FF6600") <<QColor("#666699") <<QColor("#969696") + <<QColor("#003366") <<QColor("#339966") <<QColor("#003300") <<QColor("#333300") + <<QColor("#993300") <<QColor("#993366") <<QColor("#333399") <<QColor("#333333"); + m_isIndexedColorsDefault = true; + } + if (idx < 0 || idx >= m_indexedColors.size()) + return QColor(); + return m_indexedColors[idx]; +} + +} //namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxstyles_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,139 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#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 "xlsxglobal.h" +#include "xlsxformat.h" +#include "xlsxabstractooxmlfile.h" +#include <QSharedPointer> +#include <QHash> +#include <QList> +#include <QMap> +#include <QStringList> +#include <QVector> + +class QXmlStreamWriter; +class QXmlStreamReader; +class QIODevice; +class StylesTest; + +namespace QXlsx { + +class Format; +class XlsxColor; + +struct XlsxFormatNumberData +{ + XlsxFormatNumberData() : formatIndex(0) {} + + int formatIndex; + QString formatString; +}; + +class XLSX_AUTOTEST_EXPORT 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; + bool loadFromXmlFile(QIODevice *device); + + 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); + + QHash<QString, int> m_builtinNumFmtsHash; + QMap<int, QSharedPointer<XlsxFormatNumberData> > m_customNumFmtIdMap; + QHash<QString, QSharedPointer<XlsxFormatNumberData> > m_customNumFmtsHash; + int m_nextCustomNumFmtId; + QList<Format> m_fontsList; + QList<Format> m_fillsList; + QList<Format> m_bordersList; + QHash<QByteArray, Format> m_fontsHash; + QHash<QByteArray, Format> m_fillsHash; + QHash<QByteArray, Format> m_bordersHash; + + QVector<QColor> m_indexedColors; + bool m_isIndexedColorsDefault; + + QList<Format> m_xf_formatsList; + QHash<QByteArray, Format> m_xf_formatsHash; + + QList<Format> m_dxf_formatsList; + QHash<QByteArray, Format> m_dxf_formatsHash; + + bool m_emptyFormatAdded; +}; + +} +#endif // XLSXSTYLES_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxtheme.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,237 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxtheme_p.h" +#include <QIODevice> + +namespace QXlsx { + +const char *defaultXmlData = + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n" + "<a:theme xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" name=\"Office \xe4\xb8\xbb\xe9\xa2\x98\">" + "<a:themeElements>" + "<a:clrScheme name=\"Office\">" + "<a:dk1><a:sysClr val=\"windowText\" lastClr=\"000000\"/></a:dk1>" + "<a:lt1><a:sysClr val=\"window\" lastClr=\"FFFFFF\"/></a:lt1>" + "<a:dk2><a:srgbClr val=\"1F497D\"/></a:dk2>" + "<a:lt2><a:srgbClr val=\"EEECE1\"/></a:lt2>" + "<a:accent1><a:srgbClr val=\"4F81BD\"/></a:accent1>" + "<a:accent2><a:srgbClr val=\"C0504D\"/></a:accent2>" + "<a:accent3><a:srgbClr val=\"9BBB59\"/></a:accent3>" + "<a:accent4><a:srgbClr val=\"8064A2\"/></a:accent4>" + "<a:accent5><a:srgbClr val=\"4BACC6\"/></a:accent5>" + "<a:accent6><a:srgbClr val=\"F79646\"/></a:accent6>" + "<a:hlink><a:srgbClr val=\"0000FF\"/></a:hlink>" + "<a:folHlink><a:srgbClr val=\"800080\"/></a:folHlink>" + "</a:clrScheme>" + "<a:fontScheme name=\"Office\">" + "<a:majorFont>" + "<a:latin typeface=\"Cambria\"/>" + "<a:ea typeface=\"\"/>" + "<a:cs typeface=\"\"/>" + "<a:font script=\"Jpan\" typeface=\"\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf\"/>" + "<a:font script=\"Hang\" typeface=\"\xeb\xa7\x91\xec\x9d\x80 \xea\xb3\xa0\xeb\x94\x95\"/>" + "<a:font script=\"Hans\" typeface=\"\xe5\xae\x8b\xe4\xbd\x93\"/>" + "<a:font script=\"Hant\" typeface=\"\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94\"/>" + "<a:font script=\"Arab\" typeface=\"Times New Roman\"/>" + "<a:font script=\"Hebr\" typeface=\"Times New Roman\"/>" + "<a:font script=\"Thai\" typeface=\"Tahoma\"/>" + "<a:font script=\"Ethi\" typeface=\"Nyala\"/>" + "<a:font script=\"Beng\" typeface=\"Vrinda\"/>" + "<a:font script=\"Gujr\" typeface=\"Shruti\"/>" + "<a:font script=\"Khmr\" typeface=\"MoolBoran\"/>" + "<a:font script=\"Knda\" typeface=\"Tunga\"/>" + "<a:font script=\"Guru\" typeface=\"Raavi\"/>" + "<a:font script=\"Cans\" typeface=\"Euphemia\"/>" + "<a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/>" + "<a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/>" + "<a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/>" + "<a:font script=\"Thaa\" typeface=\"MV Boli\"/>" + "<a:font script=\"Deva\" typeface=\"Mangal\"/>" + "<a:font script=\"Telu\" typeface=\"Gautami\"/>" + "<a:font script=\"Taml\" typeface=\"Latha\"/>" + "<a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/>" + "<a:font script=\"Orya\" typeface=\"Kalinga\"/>" + "<a:font script=\"Mlym\" typeface=\"Kartika\"/>" + "<a:font script=\"Laoo\" typeface=\"DokChampa\"/>" + "<a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/>" + "<a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/>" + "<a:font script=\"Viet\" typeface=\"Times New Roman\"/>" + "<a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/>" + "</a:majorFont>" + "<a:minorFont>" + "<a:latin typeface=\"Calibri\"/>" + "<a:ea typeface=\"\"/>" + "<a:cs typeface=\"\"/>" + "<a:font script=\"Jpan\" typeface=\"\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf\"/>" + "<a:font script=\"Hang\" typeface=\"\xeb\xa7\x91\xec\x9d\x80 \xea\xb3\xa0\xeb\x94\x95\"/>" + "<a:font script=\"Hans\" typeface=\"\xe5\xae\x8b\xe4\xbd\x93\"/>" + "<a:font script=\"Hant\" typeface=\"\xe6\x96\xb0\xe7\xb4\xb0\xe6\x98\x8e\xe9\xab\x94\"/>" + "<a:font script=\"Arab\" typeface=\"Arial\"/>" + "<a:font script=\"Hebr\" typeface=\"Arial\"/>" + "<a:font script=\"Thai\" typeface=\"Tahoma\"/>" + "<a:font script=\"Ethi\" typeface=\"Nyala\"/>" + "<a:font script=\"Beng\" typeface=\"Vrinda\"/>" + "<a:font script=\"Gujr\" typeface=\"Shruti\"/>" + "<a:font script=\"Khmr\" typeface=\"DaunPenh\"/>" + "<a:font script=\"Knda\" typeface=\"Tunga\"/>" + "<a:font script=\"Guru\" typeface=\"Raavi\"/>" + "<a:font script=\"Cans\" typeface=\"Euphemia\"/>" + "<a:font script=\"Cher\" typeface=\"Plantagenet Cherokee\"/>" + "<a:font script=\"Yiii\" typeface=\"Microsoft Yi Baiti\"/>" + "<a:font script=\"Tibt\" typeface=\"Microsoft Himalaya\"/>" + "<a:font script=\"Thaa\" typeface=\"MV Boli\"/>" + "<a:font script=\"Deva\" typeface=\"Mangal\"/>" + "<a:font script=\"Telu\" typeface=\"Gautami\"/>" + "<a:font script=\"Taml\" typeface=\"Latha\"/>" + "<a:font script=\"Syrc\" typeface=\"Estrangelo Edessa\"/>" + "<a:font script=\"Orya\" typeface=\"Kalinga\"/>" + "<a:font script=\"Mlym\" typeface=\"Kartika\"/>" + "<a:font script=\"Laoo\" typeface=\"DokChampa\"/>" + "<a:font script=\"Sinh\" typeface=\"Iskoola Pota\"/>" + "<a:font script=\"Mong\" typeface=\"Mongolian Baiti\"/>" + "<a:font script=\"Viet\" typeface=\"Arial\"/>" + "<a:font script=\"Uigh\" typeface=\"Microsoft Uighur\"/>" + "</a:minorFont>" + "</a:fontScheme>" + "<a:fmtScheme name=\"Office\">" + "<a:fillStyleLst>" + "<a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill>" + "<a:gradFill rotWithShape=\"1\">" + "<a:gsLst>" + "<a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:tint val=\"50000\"/><a:satMod val=\"300000\"/></a:schemeClr></a:gs>" + "<a:gs pos=\"35000\"><a:schemeClr val=\"phClr\"><a:tint val=\"37000\"/><a:satMod val=\"300000\"/></a:schemeClr></a:gs>" + "<a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:tint val=\"15000\"/><a:satMod val=\"350000\"/></a:schemeClr></a:gs>" + "</a:gsLst>" + "<a:lin ang=\"16200000\" scaled=\"1\"/>" + "</a:gradFill>" + "<a:gradFill rotWithShape=\"1\">" + "<a:gsLst>" + "<a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:shade val=\"51000\"/><a:satMod val=\"130000\"/></a:schemeClr></a:gs>" + "<a:gs pos=\"80000\"><a:schemeClr val=\"phClr\"><a:shade val=\"93000\"/><a:satMod val=\"130000\"/></a:schemeClr></a:gs>" + "<a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:shade val=\"94000\"/><a:satMod val=\"135000\"/></a:schemeClr></a:gs>" + "</a:gsLst>" + "<a:lin ang=\"16200000\" scaled=\"0\"/>" + "</a:gradFill>" + "</a:fillStyleLst>" + "<a:lnStyleLst>" + "<a:ln w=\"9525\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">" + "<a:solidFill><a:schemeClr val=\"phClr\"><a:shade val=\"95000\"/><a:satMod val=\"105000\"/></a:schemeClr></a:solidFill>" + "<a:prstDash val=\"solid\"/>" + "</a:ln>" + "<a:ln w=\"25400\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">" + "<a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill>" + "<a:prstDash val=\"solid\"/>" + "</a:ln>" + "<a:ln w=\"38100\" cap=\"flat\" cmpd=\"sng\" algn=\"ctr\">" + "<a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill>" + "<a:prstDash val=\"solid\"/>" + "</a:ln>" + "</a:lnStyleLst>" + "<a:effectStyleLst>" + "<a:effectStyle>" + "<a:effectLst>" + "<a:outerShdw blurRad=\"40000\" dist=\"20000\" dir=\"5400000\" rotWithShape=\"0\">" + "<a:srgbClr val=\"000000\"><a:alpha val=\"38000\"/></a:srgbClr>" + "</a:outerShdw>" + "</a:effectLst>" + "</a:effectStyle>" + "<a:effectStyle>" + "<a:effectLst>" + "<a:outerShdw blurRad=\"40000\" dist=\"23000\" dir=\"5400000\" rotWithShape=\"0\">" + "<a:srgbClr val=\"000000\"><a:alpha val=\"35000\"/></a:srgbClr>" + "</a:outerShdw>" + "</a:effectLst>" + "</a:effectStyle>" + "<a:effectStyle>" + "<a:effectLst>" + "<a:outerShdw blurRad=\"40000\" dist=\"23000\" dir=\"5400000\" rotWithShape=\"0\">" + "<a:srgbClr val=\"000000\"><a:alpha val=\"35000\"/></a:srgbClr>" + "</a:outerShdw>" + "</a:effectLst>" + "<a:scene3d>" + "<a:camera prst=\"orthographicFront\"><a:rot lat=\"0\" lon=\"0\" rev=\"0\"/></a:camera>" + "<a:lightRig rig=\"threePt\" dir=\"t\"><a:rot lat=\"0\" lon=\"0\" rev=\"1200000\"/></a:lightRig>" + "</a:scene3d>" + "<a:sp3d><a:bevelT w=\"63500\" h=\"25400\"/></a:sp3d>" + "</a:effectStyle>" + "</a:effectStyleLst>" + "<a:bgFillStyleLst>" + "<a:solidFill><a:schemeClr val=\"phClr\"/></a:solidFill>" + "<a:gradFill rotWithShape=\"1\">" + "<a:gsLst>" + "<a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:tint val=\"40000\"/><a:satMod val=\"350000\"/></a:schemeClr></a:gs>" + "<a:gs pos=\"40000\"><a:schemeClr val=\"phClr\"><a:tint val=\"45000\"/><a:shade val=\"99000\"/><a:satMod val=\"350000\"/></a:schemeClr></a:gs>" + "<a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:shade val=\"20000\"/><a:satMod val=\"255000\"/></a:schemeClr></a:gs></a:gsLst>" + "<a:path path=\"circle\"><a:fillToRect l=\"50000\" t=\"-80000\" r=\"50000\" b=\"180000\"/></a:path>" + "</a:gradFill>" + "<a:gradFill rotWithShape=\"1\">" + "<a:gsLst>" + "<a:gs pos=\"0\"><a:schemeClr val=\"phClr\"><a:tint val=\"80000\"/><a:satMod val=\"300000\"/></a:schemeClr></a:gs>" + "<a:gs pos=\"100000\"><a:schemeClr val=\"phClr\"><a:shade val=\"30000\"/><a:satMod val=\"200000\"/></a:schemeClr></a:gs>" + "</a:gsLst>" + "<a:path path=\"circle\"><a:fillToRect l=\"50000\" t=\"50000\" r=\"50000\" b=\"50000\"/></a:path>" + "</a:gradFill>" + "</a:bgFillStyleLst>" + "</a:fmtScheme>" + "</a:themeElements>" + "<a:objectDefaults/>" + "<a:extraClrSchemeLst/>" + "</a:theme>" + ; + +Theme::Theme(CreateFlag flag) + :AbstractOOXmlFile(flag) +{ +} + +void Theme::saveToXmlFile(QIODevice *device) const +{ + if (xmlData.isEmpty()) + device->write(defaultXmlData); + else + device->write(xmlData); +} + +QByteArray Theme::saveToXmlData() const +{ + if (xmlData.isEmpty()) + return defaultXmlData; + else + return xmlData; +} + +bool Theme::loadFromXmlData(const QByteArray &data) +{ + xmlData = data; + return true; +} + +bool Theme::loadFromXmlFile(QIODevice *device) +{ + xmlData = device->readAll(); + return true; +} + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxtheme_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,59 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXTHEME_H +#define XLSXTHEME_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 <QString> +class QIODevice; + +namespace QXlsx { + +class Theme : public AbstractOOXmlFile +{ +public: + Theme(CreateFlag flag); + + void saveToXmlFile(QIODevice *device) const; + QByteArray saveToXmlData() const; + bool loadFromXmlData(const QByteArray &data); + bool loadFromXmlFile(QIODevice *device); + + QByteArray xmlData; +}; + +} +#endif // XLSXTHEME_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxutility.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,276 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxutility_p.h" +#include "xlsxcellreference.h" + +#include <QString> +#include <QPoint> +#include <QRegularExpression> +#include <QMap> +#include <QStringList> +#include <QColor> +#include <QDateTime> +#include <QDebug> + +namespace QXlsx { + +bool parseXsdBoolean(const QString &value, bool defaultValue) +{ + if (value == QLatin1String("1") || value == QLatin1String("true")) + return true; + if (value == QLatin1String("0") || value == QLatin1String("false")) + return false; + return defaultValue; +} + +QStringList splitPath(const QString &path) +{ + int idx = path.lastIndexOf(QLatin1Char('/')); + if (idx == -1) + return QStringList()<<QStringLiteral(".")<<path; + + return QStringList()<<path.left(idx)<<path.mid(idx+1); +} + +/* + * Return the .rel file path based on filePath + */ +QString getRelFilePath(const QString &filePath) +{ + int idx = filePath.lastIndexOf(QLatin1Char('/')); + if (idx == -1) + return QString(); + + return QString(filePath.left(idx) + QLatin1String("/_rels/") + + filePath.mid(idx+1) + QLatin1String(".rels")); +} + +double datetimeToNumber(const QDateTime &dt, bool is1904) +{ + //Note, for number 0, Excel2007 shown as 1900-1-0, which should be 1899-12-31 + QDateTime epoch(is1904 ? QDate(1904, 1, 1): QDate(1899, 12, 31), QTime(0,0)); + + double excel_time = epoch.msecsTo(dt) / (1000*60*60*24.0); + +#if QT_VERSION >= 0x050200 + if (dt.isDaylightTime()) // Add one hour if the date is Daylight + excel_time += 1.0 / 24.0; +#endif + + if (!is1904 && excel_time > 59) {//31+28 + //Account for Excel erroneously treating 1900 as a leap year. + excel_time += 1; + } + + return excel_time; +} + +double timeToNumber(const QTime &time) +{ + return QTime(0,0).msecsTo(time) / (1000*60*60*24.0); +} + +QDateTime datetimeFromNumber(double num, bool is1904) +{ + if (!is1904 && num > 60) + num = num - 1; + + qint64 msecs = static_cast<qint64>(num * 1000*60*60*24.0 + 0.5); + QDateTime epoch(is1904 ? QDate(1904, 1, 1): QDate(1899, 12, 31), QTime(0,0)); + + QDateTime dt = epoch.addMSecs(msecs); + +#if QT_VERSION >= 0x050200 + // Remove one hour to see whether the date is Daylight + QDateTime dt2 = dt.addMSecs(-3600); + if (dt2.isDaylightTime()) + return dt2; +#endif + + return dt; +} + +/* + Creates a valid sheet name + minimum length is 1 + maximum length is 31 + doesn't contain special chars: / \ ? * ] [ : + Sheet names must not begin or end with ' (apostrophe) + + Invalid characters are replaced by one space character ' '. + */ +QString createSafeSheetName(const QString &nameProposal) +{ + if (nameProposal.isEmpty()) + return QString(); + + QString ret = nameProposal; + if (nameProposal.length() > 2 && nameProposal.startsWith(QLatin1Char('\'')) && nameProposal.endsWith(QLatin1Char('\''))) + ret = unescapeSheetName(ret); + + //Replace invalid chars with space. + if (nameProposal.contains(QRegularExpression(QStringLiteral("[/\\\\?*\\][:]")))) + ret.replace(QRegularExpression(QStringLiteral("[/\\\\?*\\][:]")), QStringLiteral(" ")); + if (ret.startsWith(QLatin1Char('\''))) + ret[0] = QLatin1Char(' '); + if (ret.endsWith(QLatin1Char('\''))) + ret[ret.size()-1] = QLatin1Char(' '); + + if (ret.size() > 31) + ret = ret.left(31); + return ret; +} + +/* + * When sheetName contains space or apostrophe, escaped is needed by cellFormula/definedName/chartSerials. + */ +QString escapeSheetName(const QString &sheetName) +{ + //Already escaped. + Q_ASSERT(!sheetName.startsWith(QLatin1Char('\'')) && !sheetName.endsWith(QLatin1Char('\''))); + + //These is no need to escape + if (!sheetName.contains(QRegularExpression(QStringLiteral("[ +\\-,%^=<>'&]")))) + return sheetName; + + //OK, escape is needed. + QString name = sheetName; + name.replace(QLatin1Char('\''), QLatin1String("\'\'")); + return QLatin1Char('\'') + name + QLatin1Char('\''); +} + +/* + */ +QString unescapeSheetName(const QString &sheetName) +{ + Q_ASSERT(sheetName.length() > 2 && sheetName.startsWith(QLatin1Char('\'')) && sheetName.endsWith(QLatin1Char('\''))); + + QString name = sheetName.mid(1, sheetName.length()-2); + name.replace(QLatin1String("\'\'"), QLatin1String("\'")); + return name; +} + +/* + * whether the string s starts or ends with space + */ +bool isSpaceReserveNeeded(const QString &s) +{ + QString spaces(QStringLiteral(" \t\n\r")); + return !s.isEmpty() && (spaces.contains(s.at(0))||spaces.contains(s.at(s.length()-1))); +} + +/* + * Convert shared formula for non-root cells. + * + * For example, if "B1:B10" have shared formula "=A1*A1", this function will return "=A2*A2" + * for "B2" cell, "=A3*A3" for "B3" cell, etc. + * + * Note, the formula "=A1*A1" for B1 can also be written as "=RC[-1]*RC[-1]", which is the same + * for all other cells. In other words, this formula is shared. + * + * For long run, we need a formula parser. + */ +QString convertSharedFormula(const QString &rootFormula, const CellReference &rootCell, const CellReference &cell) +{ + //Find all the "$?[A-Z]+$?[0-9]+" patterns in the rootFormula. + QList<QPair<QString, int> > segments; + + QString segment; + bool inQuote = false; + enum RefState{INVALID, PRE_AZ, AZ, PRE_09, _09}; + RefState refState = INVALID; + int refFlag = 0; // 0x00, 0x01, 0x02, 0x03 ==> A1, $A1, A$1, $A$1 + foreach (QChar ch, rootFormula) { + if (inQuote) { + segment.append(ch); + if (ch == QLatin1Char('"')) + inQuote = false; + } else { + if (ch == QLatin1Char('"')) { + inQuote = true; + refState = INVALID; + segment.append(ch); + } else if (ch == QLatin1Char('$')) { + if (refState == AZ) { + segment.append(ch); + refState = PRE_09; + refFlag |= 0x02; + } else { + segments.append(qMakePair(segment, refState==_09 ? refFlag : -1)); + segment = QString(ch); //Start new segment. + refState = PRE_AZ; + refFlag = 0x01; + } + } else if (ch >= QLatin1Char('A') && ch <=QLatin1Char('Z')) { + if (refState == PRE_AZ || refState == AZ) { + segment.append(ch); + } else { + segments.append(qMakePair(segment, refState==_09 ? refFlag : -1)); + segment = QString(ch); //Start new segment. + refFlag = 0x00; + } + refState = AZ; + } else if (ch >= QLatin1Char('0') && ch <=QLatin1Char('9')) { + segment.append(ch); + + if (refState == AZ || refState == PRE_09 || refState == _09) + refState = _09; + else + refState = INVALID; + } else { + if (refState == _09) { + segments.append(qMakePair(segment, refFlag)); + segment = QString(ch); //Start new segment. + } else { + segment.append(ch); + } + refState = INVALID; + } + } + } + + if (!segment.isEmpty()) + segments.append(qMakePair(segment, refState==_09 ? refFlag : -1)); + + //Replace "A1", "$A1", "A$1" segment with proper one. + QStringList result; + typedef QPair<QString, int> PairType; + foreach (PairType p, segments) { + //qDebug()<<p.first<<p.second; + if (p.second != -1 && p.second != 3) { + CellReference oldRef(p.first); + int row = p.second & 0x02 ? oldRef.row() : oldRef.row()-rootCell.row()+cell.row(); + int col = p.second & 0x01 ? oldRef.column() : oldRef.column()-rootCell.column()+cell.column(); + result.append(CellReference(row, col).toString(p.second & 0x02, p.second & 0x01)); + } else { + result.append(p.first); + } + } + + //OK + return result.join(QString()); +} + +} //namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxutility_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,68 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXUTILITY_H +#define XLSXUTILITY_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" +class QPoint; +class QString; +class QStringList; +class QColor; +class QDateTime; +class QTime; + +namespace QXlsx { +class CellReference; + +XLSX_AUTOTEST_EXPORT bool parseXsdBoolean(const QString &value, bool defaultValue=false); + +XLSX_AUTOTEST_EXPORT QStringList splitPath(const QString &path); +XLSX_AUTOTEST_EXPORT QString getRelFilePath(const QString &filePath); + +XLSX_AUTOTEST_EXPORT double datetimeToNumber(const QDateTime &dt, bool is1904=false); +XLSX_AUTOTEST_EXPORT QDateTime datetimeFromNumber(double num, bool is1904=false); +XLSX_AUTOTEST_EXPORT double timeToNumber(const QTime &t); + +XLSX_AUTOTEST_EXPORT QString createSafeSheetName(const QString &nameProposal); +XLSX_AUTOTEST_EXPORT QString escapeSheetName(const QString &sheetName); +XLSX_AUTOTEST_EXPORT QString unescapeSheetName(const QString &sheetName); + +XLSX_AUTOTEST_EXPORT bool isSpaceReserveNeeded(const QString &string); + +XLSX_AUTOTEST_EXPORT QString convertSharedFormula(const QString &rootFormula, const CellReference &rootCell, const CellReference &cell); + +} //QXlsx +#endif // XLSXUTILITY_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxworkbook.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,693 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxworkbook.h" +#include "xlsxworkbook_p.h" +#include "xlsxsharedstrings_p.h" +#include "xlsxworksheet.h" +#include "xlsxchartsheet.h" +#include "xlsxstyles_p.h" +#include "xlsxformat.h" +#include "xlsxworksheet_p.h" +#include "xlsxformat_p.h" +#include "xlsxmediafile_p.h" +#include "xlsxutility_p.h" + +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QFile> +#include <QBuffer> +#include <QDir> + +QT_BEGIN_NAMESPACE_XLSX + +WorkbookPrivate::WorkbookPrivate(Workbook *q, Workbook::CreateFlag flag) : + AbstractOOXmlFilePrivate(q, flag) +{ + sharedStrings = QSharedPointer<SharedStrings> (new SharedStrings(flag)); + styles = QSharedPointer<Styles>(new Styles(flag)); + theme = QSharedPointer<Theme>(new Theme(flag)); + + x_window = 240; + y_window = 15; + window_width = 16095; + window_height = 9660; + + strings_to_numbers_enabled = false; + strings_to_hyperlinks_enabled = true; + html_to_richstring_enabled = false; + date1904 = false; + defaultDateFormat = QStringLiteral("yyyy-mm-dd"); + activesheetIndex = 0; + firstsheet = 0; + table_count = 0; + + last_worksheet_index = 0; + last_chartsheet_index = 0; + last_sheet_id = 0; +} + +Workbook::Workbook(CreateFlag flag) + : AbstractOOXmlFile(new WorkbookPrivate(this, flag)) +{ + +} + +Workbook::~Workbook() +{ +} + +bool Workbook::isDate1904() const +{ + Q_D(const Workbook); + return d->date1904; +} + +/*! + Excel for Windows uses a default epoch of 1900 and Excel + for Mac uses an epoch of 1904. However, Excel on either + platform will convert automatically between one system + and the other. Qt Xlsx stores dates in the 1900 format + by default. + + \note This function should be called before any date/time + has been written. +*/ +void Workbook::setDate1904(bool date1904) +{ + Q_D(Workbook); + d->date1904 = date1904; +} + +/* + Enable the worksheet.write() method to convert strings + to numbers, where possible, using float() in order to avoid + an Excel warning about "Numbers Stored as Text". + + The default is false + */ +void Workbook::setStringsToNumbersEnabled(bool enable) +{ + Q_D(Workbook); + d->strings_to_numbers_enabled = enable; +} + +bool Workbook::isStringsToNumbersEnabled() const +{ + Q_D(const Workbook); + return d->strings_to_numbers_enabled; +} + +void Workbook::setStringsToHyperlinksEnabled(bool enable) +{ + Q_D(Workbook); + d->strings_to_hyperlinks_enabled = enable; +} + +bool Workbook::isStringsToHyperlinksEnabled() const +{ + Q_D(const Workbook); + return d->strings_to_hyperlinks_enabled; +} + +void Workbook::setHtmlToRichStringEnabled(bool enable) +{ + Q_D(Workbook); + d->html_to_richstring_enabled = enable; +} + +bool Workbook::isHtmlToRichStringEnabled() const +{ + Q_D(const Workbook); + return d->html_to_richstring_enabled; +} + +QString Workbook::defaultDateFormat() const +{ + Q_D(const Workbook); + return d->defaultDateFormat; +} + +void Workbook::setDefaultDateFormat(const QString &format) +{ + Q_D(Workbook); + d->defaultDateFormat = format; +} + +/*! + * \brief Create a defined name in the workbook. + * \param name The defined name + * \param formula The cell or range that the defined name refers to. + * \param comment + * \param scope The name of one worksheet, or empty which means golbal scope. + * \return Return false if the name invalid. + */ +bool Workbook::defineName(const QString &name, const QString &formula, const QString &comment, const QString &scope) +{ + Q_D(Workbook); + + //Remove the = sign from the formula if it exists. + QString formulaString = formula; + if (formulaString.startsWith(QLatin1Char('='))) + formulaString = formula.mid(1); + + int id=-1; + if (!scope.isEmpty()) { + for (int i=0; i<d->sheets.size(); ++i) { + if (d->sheets[i]->sheetName() == scope) { + id = d->sheets[i]->sheetId(); + break; + } + } + } + + d->definedNamesList.append(XlsxDefineNameData(name, formulaString, comment, id)); + return true; +} + +AbstractSheet *Workbook::addSheet(const QString &name, AbstractSheet::SheetType type) +{ + Q_D(Workbook); + return insertSheet(d->sheets.size(), name, type); +} + +/*! + * \internal + */ +QStringList Workbook::worksheetNames() const +{ + Q_D(const Workbook); + return d->sheetNames; +} + +/*! + * \internal + * Used only when load the xlsx file!! + */ +AbstractSheet *Workbook::addSheet(const QString &name, int sheetId, AbstractSheet::SheetType type) +{ + Q_D(Workbook); + if (sheetId > d->last_sheet_id) + d->last_sheet_id = sheetId; + AbstractSheet *sheet=0; + if (type == AbstractSheet::ST_WorkSheet) { + sheet = new Worksheet(name, sheetId, this, F_LoadFromExists); + } else if (type == AbstractSheet::ST_ChartSheet) { + sheet = new Chartsheet(name, sheetId, this, F_LoadFromExists); + } else { + qWarning("unsupported sheet type."); + Q_ASSERT(false); + } + d->sheets.append(QSharedPointer<AbstractSheet>(sheet)); + d->sheetNames.append(name); + return sheet; +} + +AbstractSheet *Workbook::insertSheet(int index, const QString &name, AbstractSheet::SheetType type) +{ + Q_D(Workbook); + QString sheetName = createSafeSheetName(name); + if (!sheetName.isEmpty()) { + //If user given an already in-used name, we should not continue any more! + if (d->sheetNames.contains(sheetName)) + return 0; + } else { + if (type == AbstractSheet::ST_WorkSheet) { + do { + ++d->last_worksheet_index; + sheetName = QStringLiteral("Sheet%1").arg(d->last_worksheet_index); + } while (d->sheetNames.contains(sheetName)); + } else if (type == AbstractSheet::ST_ChartSheet) { + do { + ++d->last_chartsheet_index; + sheetName = QStringLiteral("Chart%1").arg(d->last_chartsheet_index); + } while (d->sheetNames.contains(sheetName)); + } else { + qWarning("unsupported sheet type."); + return 0; + } + } + + ++d->last_sheet_id; + AbstractSheet *sheet; + if (type == AbstractSheet::ST_WorkSheet) + sheet = new Worksheet(sheetName, d->last_sheet_id, this, F_NewFromScratch); + else + sheet = new Chartsheet(sheetName, d->last_sheet_id, this, F_NewFromScratch); + + d->sheets.insert(index, QSharedPointer<AbstractSheet>(sheet)); + d->sheetNames.insert(index, sheetName); + d->activesheetIndex = index; + return sheet; +} + +/*! + * Returns current active worksheet. + */ +AbstractSheet *Workbook::activeSheet() const +{ + Q_D(const Workbook); + if (d->sheets.isEmpty()) + const_cast<Workbook*>(this)->addSheet(); + return d->sheets[d->activesheetIndex].data(); +} + +bool Workbook::setActiveSheet(int index) +{ + Q_D(Workbook); + if (index < 0 || index >= d->sheets.size()) { + //warning + return false; + } + d->activesheetIndex = index; + return true; +} + +/*! + * Rename the worksheet at the \a index to \a newName. + */ +bool Workbook::renameSheet(int index, const QString &newName) +{ + Q_D(Workbook); + QString name = createSafeSheetName(newName); + if (index < 0 || index >= d->sheets.size()) + return false; + + //If user given an already in-used name, return false + for (int i=0; i<d->sheets.size(); ++i) { + if (d->sheets[i]->sheetName() == name) + return false; + } + + d->sheets[index]->setSheetName(name); + d->sheetNames[index] = name; + return true; +} + +/*! + * Remove the worksheet at pos \a index. + */ +bool Workbook::deleteSheet(int index) +{ + Q_D(Workbook); + if (d->sheets.size() <= 1) + return false; + if (index < 0 || index >= d->sheets.size()) + return false; + d->sheets.removeAt(index); + d->sheetNames.removeAt(index); + return true; +} + +/*! + * Moves the worksheet form \a srcIndex to \a distIndex. + */ +bool Workbook::moveSheet(int srcIndex, int distIndex) +{ + Q_D(Workbook); + if (srcIndex == distIndex) + return false; + + if (srcIndex < 0 || srcIndex >= d->sheets.size()) + return false; + + QSharedPointer<AbstractSheet> sheet = d->sheets.takeAt(srcIndex); + d->sheetNames.takeAt(srcIndex); + if (distIndex >= 0 || distIndex <= d->sheets.size()) { + d->sheets.insert(distIndex, sheet); + d->sheetNames.insert(distIndex, sheet->sheetName()); + } else { + d->sheets.append(sheet); + d->sheetNames.append(sheet->sheetName()); + } + return true; +} + +bool Workbook::copySheet(int index, const QString &newName) +{ + Q_D(Workbook); + if (index < 0 || index >= d->sheets.size()) + return false; + + QString worksheetName = createSafeSheetName(newName); + if (!newName.isEmpty()) { + //If user given an already in-used name, we should not continue any more! + if (d->sheetNames.contains(newName)) + return false; + } else { + int copy_index = 1; + do { + ++copy_index; + worksheetName = QStringLiteral("%1(%2)").arg(d->sheets[index]->sheetName()).arg(copy_index); + } while (d->sheetNames.contains(worksheetName)); + } + + ++d->last_sheet_id; + AbstractSheet *sheet = d->sheets[index]->copy(worksheetName, d->last_sheet_id); + d->sheets.append(QSharedPointer<AbstractSheet> (sheet)); + d->sheetNames.append(sheet->sheetName()); + + return false; +} + +/*! + * Returns count of worksheets. + */ +int Workbook::sheetCount() const +{ + Q_D(const Workbook); + return d->sheets.count(); +} + +/*! + * Returns the sheet object at index \a sheetIndex. + */ +AbstractSheet *Workbook::sheet(int index) const +{ + Q_D(const Workbook); + if (index < 0 || index >= d->sheets.size()) + return 0; + return d->sheets.at(index).data(); +} + +SharedStrings *Workbook::sharedStrings() const +{ + Q_D(const Workbook); + return d->sharedStrings.data(); +} + +Styles *Workbook::styles() +{ + Q_D(Workbook); + return d->styles.data(); +} + +Theme *Workbook::theme() +{ + Q_D(Workbook); + return d->theme.data(); +} + +/*! + * \internal + * + * Unlike media files, drawing file is a property of the sheet. + */ +QList<Drawing *> Workbook::drawings() +{ + Q_D(Workbook); + QList<Drawing *> ds; + for (int i=0; i<d->sheets.size(); ++i) { + QSharedPointer<AbstractSheet> sheet = d->sheets[i]; + if (sheet->drawing()) + ds.append(sheet->drawing()); + } + + return ds; +} + +/*! + * \internal + */ +QList<QSharedPointer<AbstractSheet> > Workbook::getSheetsByTypes(AbstractSheet::SheetType type) const +{ + Q_D(const Workbook); + QList<QSharedPointer<AbstractSheet> > list; + for (int i=0; i<d->sheets.size(); ++i) { + if (d->sheets[i]->sheetType() == type) + list.append(d->sheets[i]); + } + return list; +} + +void Workbook::saveToXmlFile(QIODevice *device) const +{ + Q_D(const Workbook); + d->relationships->clear(); + if (d->sheets.isEmpty()) + const_cast<Workbook *>(this)->addSheet(); + + QXmlStreamWriter writer(device); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("workbook")); + writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main")); + writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships")); + + writer.writeEmptyElement(QStringLiteral("fileVersion")); + writer.writeAttribute(QStringLiteral("appName"), QStringLiteral("xl")); + writer.writeAttribute(QStringLiteral("lastEdited"), QStringLiteral("4")); + writer.writeAttribute(QStringLiteral("lowestEdited"), QStringLiteral("4")); + writer.writeAttribute(QStringLiteral("rupBuild"), QStringLiteral("4505")); +// writer.writeAttribute(QStringLiteral("codeName"), QStringLiteral("{37E998C4-C9E5-D4B9-71C8-EB1FF731991C}")); + + writer.writeEmptyElement(QStringLiteral("workbookPr")); + if (d->date1904) + writer.writeAttribute(QStringLiteral("date1904"), QStringLiteral("1")); + writer.writeAttribute(QStringLiteral("defaultThemeVersion"), QStringLiteral("124226")); + + writer.writeStartElement(QStringLiteral("bookViews")); + writer.writeEmptyElement(QStringLiteral("workbookView")); + writer.writeAttribute(QStringLiteral("xWindow"), QString::number(d->x_window)); + writer.writeAttribute(QStringLiteral("yWindow"), QString::number(d->y_window)); + writer.writeAttribute(QStringLiteral("windowWidth"), QString::number(d->window_width)); + writer.writeAttribute(QStringLiteral("windowHeight"), QString::number(d->window_height)); + //Store the firstSheet when it isn't the default + //For example, when "the first sheet 0 is hidden", the first sheet will be 1 + if (d->firstsheet > 0) + writer.writeAttribute(QStringLiteral("firstSheet"), QString::number(d->firstsheet + 1)); + //Store the activeTab when it isn't the first sheet + if (d->activesheetIndex > 0) + writer.writeAttribute(QStringLiteral("activeTab"), QString::number(d->activesheetIndex)); + writer.writeEndElement();//bookViews + + writer.writeStartElement(QStringLiteral("sheets")); + int worksheetIndex = 0; + int chartsheetIndex = 0; + for (int i=0; i<d->sheets.size(); ++i) { + QSharedPointer<AbstractSheet> sheet = d->sheets[i]; + writer.writeEmptyElement(QStringLiteral("sheet")); + writer.writeAttribute(QStringLiteral("name"), sheet->sheetName()); + writer.writeAttribute(QStringLiteral("sheetId"), QString::number(sheet->sheetId())); + if (sheet->sheetState() == AbstractSheet::SS_Hidden) + writer.writeAttribute(QStringLiteral("state"), QStringLiteral("hidden")); + else if (sheet->sheetState() == AbstractSheet::SS_VeryHidden) + writer.writeAttribute(QStringLiteral("state"), QStringLiteral("veryHidden")); + + if (sheet->sheetType() == AbstractSheet::ST_WorkSheet) + d->relationships->addDocumentRelationship(QStringLiteral("/worksheet"), QStringLiteral("worksheets/sheet%1.xml").arg(++worksheetIndex)); + else + d->relationships->addDocumentRelationship(QStringLiteral("/chartsheet"), QStringLiteral("chartsheets/sheet%1.xml").arg(++chartsheetIndex)); + + writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(d->relationships->count())); + } + writer.writeEndElement();//sheets + + if (d->externalLinks.size() > 0) { + writer.writeStartElement(QStringLiteral("externalReferences")); + for (int i=0; i<d->externalLinks.size(); ++i) { + writer.writeEmptyElement(QStringLiteral("externalReference")); + d->relationships->addDocumentRelationship(QStringLiteral("/externalLink"), QStringLiteral("externalLinks/externalLink%1.xml").arg(i+1)); + writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(d->relationships->count())); + } + writer.writeEndElement();//externalReferences + } + + if (!d->definedNamesList.isEmpty()) { + writer.writeStartElement(QStringLiteral("definedNames")); + foreach (XlsxDefineNameData data, d->definedNamesList) { + writer.writeStartElement(QStringLiteral("definedName")); + writer.writeAttribute(QStringLiteral("name"), data.name); + if (!data.comment.isEmpty()) + writer.writeAttribute(QStringLiteral("comment"), data.comment); + if (data.sheetId != -1) { + //find the local index of the sheet. + for (int i=0; i<d->sheets.size(); ++i) { + if (d->sheets[i]->sheetId() == data.sheetId) { + writer.writeAttribute(QStringLiteral("localSheetId"), QString::number(i)); + break; + } + } + } + writer.writeCharacters(data.formula); + writer.writeEndElement();//definedName + } + writer.writeEndElement();//definedNames + } + + writer.writeStartElement(QStringLiteral("calcPr")); + writer.writeAttribute(QStringLiteral("calcId"), QStringLiteral("124519")); + writer.writeEndElement(); //calcPr + + writer.writeEndElement();//workbook + writer.writeEndDocument(); + + d->relationships->addDocumentRelationship(QStringLiteral("/theme"), QStringLiteral("theme/theme1.xml")); + d->relationships->addDocumentRelationship(QStringLiteral("/styles"), QStringLiteral("styles.xml")); + if (!sharedStrings()->isEmpty()) + d->relationships->addDocumentRelationship(QStringLiteral("/sharedStrings"), QStringLiteral("sharedStrings.xml")); +} + +bool Workbook::loadFromXmlFile(QIODevice *device) +{ + Q_D(Workbook); + + QXmlStreamReader reader(device); + while (!reader.atEnd()) { + QXmlStreamReader::TokenType token = reader.readNext(); + if (token == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("sheet")) { + QXmlStreamAttributes attributes = reader.attributes(); + const QString name = attributes.value(QLatin1String("name")).toString(); + int sheetId = attributes.value(QLatin1String("sheetId")).toString().toInt(); + const QString rId = attributes.value(QLatin1String("r:id")).toString(); + const QStringRef &stateString = attributes.value(QLatin1String("state")); + AbstractSheet::SheetState state = AbstractSheet::SS_Visible; + if (stateString == QLatin1String("hidden")) + state = AbstractSheet::SS_Hidden; + else if (stateString == QLatin1String("veryHidden")) + state = AbstractSheet::SS_VeryHidden; + + XlsxRelationship relationship = d->relationships->getRelationshipById(rId); + + AbstractSheet::SheetType type = AbstractSheet::ST_WorkSheet; + if (relationship.type.endsWith(QLatin1String("/worksheet"))) + type = AbstractSheet::ST_WorkSheet; + else if (relationship.type.endsWith(QLatin1String("/chartsheet"))) + type = AbstractSheet::ST_ChartSheet; + else if (relationship.type.endsWith(QLatin1String("/dialogsheet"))) + type = AbstractSheet::ST_DialogSheet; + else if (relationship.type.endsWith(QLatin1String("/xlMacrosheet"))) + type = AbstractSheet::ST_MacroSheet; + else + qWarning("unknown sheet type"); + + AbstractSheet *sheet = addSheet(name, sheetId, type); + sheet->setSheetState(state); + const QString fullPath = QDir::cleanPath(splitPath(filePath())[0] +QLatin1String("/")+ relationship.target); + sheet->setFilePath(fullPath); + } else if (reader.name() == QLatin1String("workbookPr")) { + QXmlStreamAttributes attrs = reader.attributes(); + if (attrs.hasAttribute(QLatin1String("date1904"))) + d->date1904 = true; + } else if (reader.name() == QLatin1String("bookviews")) { + while (!(reader.name() == QLatin1String("bookviews") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("workbookView")) { + QXmlStreamAttributes attrs = reader.attributes(); + if (attrs.hasAttribute(QLatin1String("xWindow"))) + d->x_window = attrs.value(QLatin1String("xWindow")).toString().toInt(); + if (attrs.hasAttribute(QLatin1String("yWindow"))) + d->y_window = attrs.value(QLatin1String("yWindow")).toString().toInt(); + if (attrs.hasAttribute(QLatin1String("windowWidth"))) + d->window_width = attrs.value(QLatin1String("windowWidth")).toString().toInt(); + if (attrs.hasAttribute(QLatin1String("windowHeight"))) + d->window_height = attrs.value(QLatin1String("windowHeight")).toString().toInt(); + if (attrs.hasAttribute(QLatin1String("firstSheet"))) + d->firstsheet = attrs.value(QLatin1String("firstSheet")).toString().toInt(); + if (attrs.hasAttribute(QLatin1String("activeTab"))) + d->activesheetIndex = attrs.value(QLatin1String("activeTab")).toString().toInt(); + } + } + } + } else if (reader.name() == QLatin1String("externalReference")) { + QXmlStreamAttributes attributes = reader.attributes(); + const QString rId = attributes.value(QLatin1String("r:id")).toString(); + XlsxRelationship relationship = d->relationships->getRelationshipById(rId); + + QSharedPointer<SimpleOOXmlFile> link(new SimpleOOXmlFile(F_LoadFromExists)); + const QString fullPath = QDir::cleanPath(splitPath(filePath())[0] +QLatin1String("/")+ relationship.target); + link->setFilePath(fullPath); + d->externalLinks.append(link); + } else if (reader.name() == QLatin1String("definedName")) { + QXmlStreamAttributes attrs = reader.attributes(); + XlsxDefineNameData data; + + data.name = attrs.value(QLatin1String("name")).toString(); + if (attrs.hasAttribute(QLatin1String("comment"))) + data.comment = attrs.value(QLatin1String("comment")).toString(); + if (attrs.hasAttribute(QLatin1String("localSheetId"))) { + int localId = attrs.value(QLatin1String("localSheetId")).toString().toInt(); + int sheetId = d->sheets.at(localId)->sheetId(); + data.sheetId = sheetId; + } + data.formula = reader.readElementText(); + d->definedNamesList.append(data); + } + } + } + return true; +} + +/*! + * \internal + */ +QList<QSharedPointer<MediaFile> > Workbook::mediaFiles() const +{ + Q_D(const Workbook); + + return d->mediaFiles; +} + +/*! + * \internal + */ +void Workbook::addMediaFile(QSharedPointer<MediaFile> media, bool force) +{ + Q_D(Workbook); + if (!force) { + for (int i=0; i<d->mediaFiles.size(); ++i) { + if (d->mediaFiles[i]->hashKey() == media->hashKey()) { + media->setIndex(i); + return; + } + } + } + media->setIndex(d->mediaFiles.size()); + d->mediaFiles.append(media); +} + +/*! + * \internal + */ +QList<QSharedPointer<Chart> > Workbook::chartFiles() const +{ + Q_D(const Workbook); + + return d->chartFiles; +} + +/*! + * \internal + */ +void Workbook::addChartFile(QSharedPointer<Chart> chart) +{ + Q_D(Workbook); + + if (!d->chartFiles.contains(chart)) + d->chartFiles.append(chart); +} + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxworkbook.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,114 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXWORKBOOK_H +#define XLSXWORKBOOK_H + +#include "xlsxglobal.h" +#include "xlsxabstractooxmlfile.h" +#include "xlsxabstractsheet.h" +#include <QList> +#include <QImage> +#include <QSharedPointer> + +class QIODevice; + +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 Q_XLSX_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(QSharedPointer<MediaFile> media, bool force=false); + QList<QSharedPointer<MediaFile> > mediaFiles() const; + void addChartFile(QSharedPointer<Chart> chartFile); + QList<QSharedPointer<Chart> > 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; + bool loadFromXmlFile(QIODevice *device); + + SharedStrings *sharedStrings() const; + Styles *styles(); + Theme *theme(); + QList<QImage> images(); + QList<Drawing *> drawings(); + QList<QSharedPointer<AbstractSheet> > 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxworkbook_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,107 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXWORKBOOK_P_H +#define XLSXWORKBOOK_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 "xlsxworkbook.h" +#include "xlsxabstractooxmlfile_p.h" +#include "xlsxtheme_p.h" +#include "xlsxsimpleooxmlfile_p.h" +#include "xlsxrelationships_p.h" + +#include <QSharedPointer> +#include <QPair> +#include <QStringList> + +namespace QXlsx { + +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> sharedStrings; + QList<QSharedPointer<AbstractSheet> > sheets; + QList<QSharedPointer<SimpleOOXmlFile> > externalLinks; + QStringList sheetNames; + QSharedPointer<Styles> styles; + QSharedPointer<Theme> theme; + QList<QSharedPointer<MediaFile> > mediaFiles; + QList<QSharedPointer<Chart> > chartFiles; + QList<XlsxDefineNameData> 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; +}; + +} + +#endif // XLSXWORKBOOK_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxworksheet.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,2344 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxrichstring.h" +#include "xlsxcellreference.h" +#include "xlsxworksheet.h" +#include "xlsxworksheet_p.h" +#include "xlsxworkbook.h" +#include "xlsxformat.h" +#include "xlsxformat_p.h" +#include "xlsxutility_p.h" +#include "xlsxsharedstrings_p.h" +#include "xlsxdrawing_p.h" +#include "xlsxstyles_p.h" +#include "xlsxcell.h" +#include "xlsxcell_p.h" +#include "xlsxcellrange.h" +#include "xlsxconditionalformatting_p.h" +#include "xlsxdrawinganchor_p.h" +#include "xlsxchart.h" +#include "xlsxcellformula.h" +#include "xlsxcellformula_p.h" + +#include <QVariant> +#include <QDateTime> +#include <QPoint> +#include <QFile> +#include <QUrl> +#include <QRegularExpression> +#include <QDebug> +#include <QBuffer> +#include <QXmlStreamWriter> +#include <QXmlStreamReader> +#include <QTextDocument> +#include <QDir> + +#include <math.h> + +QT_BEGIN_NAMESPACE_XLSX + +WorksheetPrivate::WorksheetPrivate(Worksheet *p, Worksheet::CreateFlag flag) + : AbstractSheetPrivate(p, flag) + , windowProtection(false), showFormulas(false), showGridLines(true), showRowColHeaders(true) + , showZeros(true), rightToLeft(false), tabSelected(false), showRuler(false) + , showOutlineSymbols(true), showWhiteSpace(true), urlPattern(QStringLiteral("^([fh]tt?ps?://)|(mailto:)|(file://)")) +{ + previous_row = 0; + + outline_row_level = 0; + outline_col_level = 0; + + default_row_height = 15; + default_row_zeroed = false; +} + +WorksheetPrivate::~WorksheetPrivate() +{ +} + +/* + Calculate the "spans" attribute of the <row> tag. This is an + XLSX optimisation and isn't strictly required. However, it + makes comparing files easier. The span is the same for each + block of 16 rows. + */ +void WorksheetPrivate::calculateSpans() const +{ + row_spans.clear(); + int span_min = XLSX_COLUMN_MAX+1; + int span_max = -1; + + for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) { + if (cellTable.contains(row_num)) { + for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) { + if (cellTable[row_num].contains(col_num)) { + if (span_max == -1) { + span_min = col_num; + span_max = col_num; + } else { + if (col_num < span_min) + span_min = col_num; + else if (col_num > span_max) + span_max = col_num; + } + } + } + } + if (comments.contains(row_num)) { + for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) { + if (comments[row_num].contains(col_num)) { + if (span_max == -1) { + span_min = col_num; + span_max = col_num; + } else { + if (col_num < span_min) + span_min = col_num; + else if (col_num > span_max) + span_max = col_num; + } + } + } + } + + if (row_num%16 == 0 || row_num == dimension.lastRow()) { + if (span_max != -1) { + row_spans[row_num / 16] = QStringLiteral("%1:%2").arg(span_min).arg(span_max); + span_min = XLSX_COLUMN_MAX+1; + span_max = -1; + } + } + } +} + + +QString WorksheetPrivate::generateDimensionString() const +{ + if (!dimension.isValid()) + return QStringLiteral("A1"); + else + return dimension.toString(); +} + +/* + Check that row and col are valid and store the max and min + values for use in other methods/elements. The ignore_row / + ignore_col flags is used to indicate that we wish to perform + the dimension check without storing the value. The ignore + flags are use by setRow() and dataValidate. +*/ +int WorksheetPrivate::checkDimensions(int row, int col, bool ignore_row, bool ignore_col) +{ + Q_ASSERT_X(row!=0, "checkDimensions", "row should start from 1 instead of 0"); + Q_ASSERT_X(col!=0, "checkDimensions", "column should start from 1 instead of 0"); + + if (row > XLSX_ROW_MAX || row < 1 || col > XLSX_COLUMN_MAX || col < 1) + return -1; + + if (!ignore_row) { + if (row < dimension.firstRow() || dimension.firstRow() == -1) dimension.setFirstRow(row); + if (row > dimension.lastRow()) dimension.setLastRow(row); + } + if (!ignore_col) { + if (col < dimension.firstColumn() || dimension.firstColumn() == -1) dimension.setFirstColumn(col); + if (col > dimension.lastColumn()) dimension.setLastColumn(col); + } + + return 0; +} + +/*! + \class Worksheet + \inmodule QtXlsx + \brief Represent one worksheet in the workbook. +*/ + +/*! + * \internal + */ +Worksheet::Worksheet(const QString &name, int id, Workbook *workbook, CreateFlag flag) + :AbstractSheet(name, id, workbook, new WorksheetPrivate(this, flag)) +{ + if (!workbook) //For unit test propose only. Ignore the memery leak. + d_func()->workbook = new Workbook(flag); +} + +/*! + * \internal + * + * Make a copy of this sheet. + */ + +Worksheet *Worksheet::copy(const QString &distName, int distId) const +{ + Q_D(const Worksheet); + Worksheet *sheet = new Worksheet(distName, distId, d->workbook, F_NewFromScratch); + WorksheetPrivate *sheet_d = sheet->d_func(); + + sheet_d->dimension = d->dimension; + + QMapIterator<int, QMap<int, QSharedPointer<Cell> > > it(d->cellTable); + while (it.hasNext()) { + it.next(); + int row = it.key(); + QMapIterator<int, QSharedPointer<Cell> > it2(it.value()); + while (it2.hasNext()) { + it2.next(); + int col = it2.key(); + + QSharedPointer<Cell> cell(new Cell(it2.value().data())); + cell->d_ptr->parent = sheet; + + if (cell->cellType() == Cell::SharedStringType) + d->workbook->sharedStrings()->addSharedString(cell->d_ptr->richString); + + sheet_d->cellTable[row][col] = cell; + } + } + + sheet_d->merges = d->merges; +// sheet_d->rowsInfo = d->rowsInfo; +// sheet_d->colsInfo = d->colsInfo; +// sheet_d->colsInfoHelper = d->colsInfoHelper; +// sheet_d->dataValidationsList = d->dataValidationsList; +// sheet_d->conditionalFormattingList = d->conditionalFormattingList; + + return sheet; +} + +/*! + * Destroys this workssheet. + */ +Worksheet::~Worksheet() +{ +} + +/*! + * Returns whether sheet is protected. + */ +bool Worksheet::isWindowProtected() const +{ + Q_D(const Worksheet); + return d->windowProtection; +} + +/*! + * Protects/unprotects the sheet based on \a protect. + */ +void Worksheet::setWindowProtected(bool protect) +{ + Q_D(Worksheet); + d->windowProtection = protect; +} + +/*! + * Return whether formulas instead of their calculated results shown in cells + */ +bool Worksheet::isFormulasVisible() const +{ + Q_D(const Worksheet); + return d->showFormulas; +} + +/*! + * Show formulas in cells instead of their calculated results when \a visible is true. + */ +void Worksheet::setFormulasVisible(bool visible) +{ + Q_D(Worksheet); + d->showFormulas = visible; +} + +/*! + * Return whether gridlines is shown or not. + */ +bool Worksheet::isGridLinesVisible() const +{ + Q_D(const Worksheet); + return d->showGridLines; +} + +/*! + * Show or hide the gridline based on \a visible + */ +void Worksheet::setGridLinesVisible(bool visible) +{ + Q_D(Worksheet); + d->showGridLines = visible; +} + +/*! + * Return whether is row and column headers is vislbe. + */ +bool Worksheet::isRowColumnHeadersVisible() const +{ + Q_D(const Worksheet); + return d->showRowColHeaders; +} + +/*! + * Show or hide the row column headers based on \a visible + */ +void Worksheet::setRowColumnHeadersVisible(bool visible) +{ + Q_D(Worksheet); + d->showRowColHeaders = visible; +} + + +/*! + * Return whether the sheet is shown right-to-left or not. + */ +bool Worksheet::isRightToLeft() const +{ + Q_D(const Worksheet); + return d->rightToLeft; +} + +/*! + * Enable or disable the right-to-left based on \a enable. + */ +void Worksheet::setRightToLeft(bool enable) +{ + Q_D(Worksheet); + d->rightToLeft = enable; +} + +/*! + * Return whether is cells that have zero value show a zero. + */ +bool Worksheet::isZerosVisible() const +{ + Q_D(const Worksheet); + return d->showZeros; +} + +/*! + * Show a zero in cells that have zero value if \a visible is true. + */ +void Worksheet::setZerosVisible(bool visible) +{ + Q_D(Worksheet); + d->showZeros = visible; +} + +/*! + * Return whether this tab is selected. + */ +bool Worksheet::isSelected() const +{ + Q_D(const Worksheet); + return d->tabSelected; +} + +/*! + * Select this sheet if \a select is true. + */ +void Worksheet::setSelected(bool select) +{ + Q_D(Worksheet); + d->tabSelected = select; +} + +/*! + * Return whether is ruler is shown. + */ +bool Worksheet::isRulerVisible() const +{ + Q_D(const Worksheet); + return d->showRuler; + +} + +/*! + * Show or hide the ruler based on \a visible. + */ +void Worksheet::setRulerVisible(bool visible) +{ + Q_D(Worksheet); + d->showRuler = visible; + +} + +/*! + * Return whether is outline symbols is shown. + */ +bool Worksheet::isOutlineSymbolsVisible() const +{ + Q_D(const Worksheet); + return d->showOutlineSymbols; +} + +/*! + * Show or hide the outline symbols based ib \a visible. + */ +void Worksheet::setOutlineSymbolsVisible(bool visible) +{ + Q_D(Worksheet); + d->showOutlineSymbols = visible; +} + +/*! + * Return whether is white space is shown. + */ +bool Worksheet::isWhiteSpaceVisible() const +{ + Q_D(const Worksheet); + return d->showWhiteSpace; +} + +/*! + * Show or hide the white space based on \a visible. + */ +void Worksheet::setWhiteSpaceVisible(bool visible) +{ + Q_D(Worksheet); + d->showWhiteSpace = visible; +} + +/*! + * Write \a value to cell (\a row, \a column) with the \a format. + * Both \a row and \a column are all 1-indexed value. + * + * Returns true on success. + */ +bool Worksheet::write(int row, int column, const QVariant &value, const Format &format) +{ + Q_D(Worksheet); + + if (d->checkDimensions(row, column)) + return false; + + bool ret = true; + if (value.isNull()) { + //Blank + ret = writeBlank(row, column, format); + } else if (value.userType() == QMetaType::QString) { + //String + QString token = value.toString(); + bool ok; + + if (token.startsWith(QLatin1String("="))) { + //convert to formula + ret = writeFormula(row, column, CellFormula(token), format); + } else if (d->workbook->isStringsToHyperlinksEnabled() && token.contains(d->urlPattern)) { + //convert to url + ret = writeHyperlink(row, column, QUrl(token)); + } else if (d->workbook->isStringsToNumbersEnabled() && (value.toDouble(&ok), ok)) { + //Try convert string to number if the flag enabled. + ret = writeString(row, column, value.toString(), format); + } else { + //normal string now + ret = writeString(row, column, token, format); + } + } else if (value.userType() == qMetaTypeId<RichString>()) { + ret = writeString(row, column, value.value<RichString>(), format); + } else if (value.userType() == QMetaType::Int || value.userType() == QMetaType::UInt + || value.userType() == QMetaType::LongLong || value.userType() == QMetaType::ULongLong + || value.userType() == QMetaType::Double || value.userType() == QMetaType::Float) { + //Number + + ret = writeNumeric(row, column, value.toDouble(), format); + } else if (value.userType() == QMetaType::Bool) { + //Bool + ret = writeBool(row,column, value.toBool(), format); + } else if (value.userType() == QMetaType::QDateTime || value.userType() == QMetaType::QDate) { + //DateTime, Date + // note that, QTime cann't convert to QDateTime + ret = writeDateTime(row, column, value.toDateTime(), format); + } else if (value.userType() == QMetaType::QTime) { + //Time + ret = writeTime(row, column, value.toTime(), format); + } else if (value.userType() == QMetaType::QUrl) { + //Url + ret = writeHyperlink(row, column, value.toUrl(), format); + } else { + //Wrong type + return false; + } + + return ret; +} + +/*! + * \overload + * Write \a value to cell \a row_column with the \a format. + * Both row and column are all 1-indexed value. + * Returns true on success. + */ +bool Worksheet::write(const CellReference &row_column, const QVariant &value, const Format &format) +{ + if (!row_column.isValid()) + return false; + + return write(row_column.row(), row_column.column(), value, format); +} + +/*! + \overload + Return the contents of the cell \a row_column. + */ +QVariant Worksheet::read(const CellReference &row_column) const +{ + if (!row_column.isValid()) + return QVariant(); + + return read(row_column.row(), row_column.column()); +} + +/*! + Return the contents of the cell (\a row, \a column). + */ +QVariant Worksheet::read(int row, int column) const +{ + Q_D(const Worksheet); + + Cell *cell = cellAt(row, column); + if (!cell) + return QVariant(); + + if (cell->hasFormula()) { + if (cell->formula().formulaType() == CellFormula::NormalType) { + return QVariant(QLatin1String("=")+cell->formula().formulaText()); + } else if (cell->formula().formulaType() == CellFormula::SharedType) { + if (!cell->formula().formulaText().isEmpty()) { + return QVariant(QLatin1String("=")+cell->formula().formulaText()); + } else { + const CellFormula &rootFormula = d->sharedFormulaMap[cell->formula().sharedIndex()]; + CellReference rootCellRef = rootFormula.reference().topLeft(); + QString rootFormulaText = rootFormula.formulaText(); + QString newFormulaText = convertSharedFormula(rootFormulaText, rootCellRef, CellReference(row, column)); + return QVariant(QLatin1String("=")+newFormulaText); + } + } + } + + if (cell->isDateTime()) { + double val = cell->value().toDouble(); + QDateTime dt = cell->dateTime(); + if (val < 1) + return dt.time(); + if (fmod(val, 1.0) < 1.0/(1000*60*60*24)) //integer + return dt.date(); + return dt; + } + + return cell->value(); +} + +/*! + * Returns the cell at the given \a row_column. If there + * is no cell at the specified position, the function returns 0. + */ +Cell *Worksheet::cellAt(const CellReference &row_column) const +{ + if (!row_column.isValid()) + return 0; + + return cellAt(row_column.row(), row_column.column()); +} + +/*! + * Returns the cell at the given \a row and \a column. If there + * is no cell at the specified position, the function returns 0. + */ +Cell *Worksheet::cellAt(int row, int column) const +{ + Q_D(const Worksheet); + if (!d->cellTable.contains(row)) + return 0; + if (!d->cellTable[row].contains(column)) + return 0; + + return d->cellTable[row][column].data(); +} + +Format WorksheetPrivate::cellFormat(int row, int col) const +{ + if (!cellTable.contains(row)) + return Format(); + if (!cellTable[row].contains(col)) + return Format(); + return cellTable[row][col]->format(); +} + +/*! + \overload + Write string \a value to the cell \a row_column with the \a format. + + Returns true on success. + */ +bool Worksheet::writeString(const CellReference &row_column, const RichString &value, const Format &format) +{ + if (!row_column.isValid()) + return false; + + return writeString(row_column.row(), row_column.column(), value, format); +} + +/*! + Write string \a value to the cell (\a row, \a column) with the \a format. + Returns true on success. +*/ +bool Worksheet::writeString(int row, int column, const RichString &value, const Format &format) +{ + Q_D(Worksheet); +// QString content = value.toPlainString(); + if (d->checkDimensions(row, column)) + return false; + +// if (content.size() > d->xls_strmax) { +// content = content.left(d->xls_strmax); +// error = -2; +// } + + d->sharedStrings()->addSharedString(value); + Format fmt = format.isValid() ? format : d->cellFormat(row, column); + if (value.fragmentCount() == 1 && value.fragmentFormat(0).isValid()) + fmt.mergeFormat(value.fragmentFormat(0)); + d->workbook->styles()->addXfFormat(fmt); + QSharedPointer<Cell> cell = QSharedPointer<Cell>(new Cell(value.toPlainString(), Cell::SharedStringType, fmt, this)); + cell->d_ptr->richString = value; + d->cellTable[row][column] = cell; + return true; +} + +/*! + \overload + Write string \a value to the cell \a row_column with the \a format. + */ +bool Worksheet::writeString(const CellReference &row_column, const QString &value, const Format &format) +{ + if (!row_column.isValid()) + return false; + + return writeString(row_column.row(), row_column.column(), value, format); +} + +/*! + \overload + + Write string \a value to the cell (\a row, \a column) with the \a format. + Returns true on success. +*/ +bool Worksheet::writeString(int row, int column, const QString &value, const Format &format) +{ + Q_D(Worksheet); + if (d->checkDimensions(row, column)) + return false; + + RichString rs; + if (d->workbook->isHtmlToRichStringEnabled() && Qt::mightBeRichText(value)) + rs.setHtml(value); + else + rs.addFragment(value, Format()); + + return writeString(row, column, rs, format); +} + +/*! + \overload + Write string \a value to the cell \a row_column with the \a format + */ +bool Worksheet::writeInlineString(const CellReference &row_column, const QString &value, const Format &format) +{ + if (!row_column.isValid()) + return false; + + return writeInlineString(row_column.row(), row_column.column(), value, format); +} + +/*! + Write string \a value to the cell (\a row, \a column) with the \a format. + Returns true on success. +*/ +bool Worksheet::writeInlineString(int row, int column, const QString &value, const Format &format) +{ + Q_D(Worksheet); + //int error = 0; + QString content = value; + if (d->checkDimensions(row, column)) + return false; + + if (value.size() > XLSX_STRING_MAX) { + content = value.left(XLSX_STRING_MAX); + //error = -2; + } + + Format fmt = format.isValid() ? format : d->cellFormat(row, column); + d->workbook->styles()->addXfFormat(fmt); + d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::InlineStringType, fmt, this)); + return true; +} + +/*! + \overload + Write numeric \a value to the cell \a row_column with the \a format. + Returns true on success. + */ +bool Worksheet::writeNumeric(const CellReference &row_column, double value, const Format &format) +{ + if (!row_column.isValid()) + return false; + + return writeNumeric(row_column.row(), row_column.column(), value, format); +} + +/*! + Write numeric \a value to the cell (\a row, \a column) with the \a format. + Returns true on success. +*/ +bool Worksheet::writeNumeric(int row, int column, double value, const Format &format) +{ + Q_D(Worksheet); + if (d->checkDimensions(row, column)) + return false; + + Format fmt = format.isValid() ? format : d->cellFormat(row, column); + d->workbook->styles()->addXfFormat(fmt); + d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::NumberType, fmt, this)); + return true; +} + +/*! + \overload + Write \a formula to the cell \a row_column with the \a format and \a result. + Returns true on success. + */ +bool Worksheet::writeFormula(const CellReference &row_column, const CellFormula &formula, const Format &format, double result) +{ + if (!row_column.isValid()) + return false; + + return writeFormula(row_column.row(), row_column.column(), formula, format, result); +} + +/*! + Write \a formula_ to the cell (\a row, \a column) with the \a format and \a result. + Returns true on success. +*/ +bool Worksheet::writeFormula(int row, int column, const CellFormula &formula_, const Format &format, double result) +{ + Q_D(Worksheet); + if (d->checkDimensions(row, column)) + return false; + + Format fmt = format.isValid() ? format : d->cellFormat(row, column); + d->workbook->styles()->addXfFormat(fmt); + + CellFormula formula = formula_; + formula.d->ca = true; + if (formula.formulaType() == CellFormula::SharedType) { + //Assign proper shared index for shared formula + int si=0; + while(d->sharedFormulaMap.contains(si)) + ++si; + formula.d->si = si; + d->sharedFormulaMap[si] = formula; + } + + QSharedPointer<Cell> data = QSharedPointer<Cell>(new Cell(result, Cell::NumberType, fmt, this)); + data->d_ptr->formula = formula; + d->cellTable[row][column] = data; + + CellRange range = formula.reference(); + if (formula.formulaType() == CellFormula::SharedType) { + CellFormula sf(QString(), CellFormula::SharedType); + sf.d->si = formula.sharedIndex(); + for (int r=range.firstRow(); r<=range.lastRow(); ++r) { + for (int c=range.firstColumn(); c<=range.lastColumn(); ++c) { + if (!(r==row && c==column)) { + if(Cell *cell = cellAt(r, c)) { + cell->d_ptr->formula = sf; + } else { + QSharedPointer<Cell> newCell = QSharedPointer<Cell>(new Cell(result, Cell::NumberType, fmt, this)); + newCell->d_ptr->formula = sf; + d->cellTable[r][c] = newCell; + } + } + } + } + } else if (formula.formulaType() == CellFormula::SharedType) { + + } + + return true; +} + +/*! + \overload + Write a empty cell \a row_column with the \a format. + Returns true on success. + */ +bool Worksheet::writeBlank(const CellReference &row_column, const Format &format) +{ + if (!row_column.isValid()) + return false; + + return writeBlank(row_column.row(), row_column.column(), format); +} + +/*! + Write a empty cell (\a row, \a column) with the \a format. + Returns true on success. + */ +bool Worksheet::writeBlank(int row, int column, const Format &format) +{ + Q_D(Worksheet); + if (d->checkDimensions(row, column)) + return false; + + Format fmt = format.isValid() ? format : d->cellFormat(row, column); + d->workbook->styles()->addXfFormat(fmt); + + //Note: NumberType with an invalid QVariant value means blank. + d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(QVariant(), Cell::NumberType, fmt, this)); + + return true; +} +/*! + \overload + Write a bool \a value to the cell \a row_column with the \a format. + Returns true on success. + */ +bool Worksheet::writeBool(const CellReference &row_column, bool value, const Format &format) +{ + if (!row_column.isValid()) + return false; + + return writeBool(row_column.row(), row_column.column(), value, format); +} + +/*! + Write a bool \a value to the cell (\a row, \a column) with the \a format. + Returns true on success. + */ +bool Worksheet::writeBool(int row, int column, bool value, const Format &format) +{ + Q_D(Worksheet); + if (d->checkDimensions(row, column)) + return false; + + Format fmt = format.isValid() ? format : d->cellFormat(row, column); + d->workbook->styles()->addXfFormat(fmt); + d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::BooleanType, fmt, this)); + + return true; +} +/*! + \overload + Write a QDateTime \a dt to the cell \a row_column with the \a format. + Returns true on success. + */ +bool Worksheet::writeDateTime(const CellReference &row_column, const QDateTime &dt, const Format &format) +{ + if (!row_column.isValid()) + return false; + + return writeDateTime(row_column.row(), row_column.column(), dt, format); +} + +/*! + Write a QDateTime \a dt to the cell (\a row, \a column) with the \a format. + Returns true on success. + */ +bool Worksheet::writeDateTime(int row, int column, const QDateTime &dt, const Format &format) +{ + Q_D(Worksheet); + if (d->checkDimensions(row, column)) + return false; + + Format fmt = format.isValid() ? format : d->cellFormat(row, column); + if (!fmt.isValid() || !fmt.isDateTimeFormat()) + fmt.setNumberFormat(d->workbook->defaultDateFormat()); + d->workbook->styles()->addXfFormat(fmt); + + double value = datetimeToNumber(dt, d->workbook->isDate1904()); + + d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::NumberType, fmt, this)); + + return true; +} + +/*! + \overload + Write a QTime \a t to the cell \a row_column with the \a format. + Returns true on success. + */ +bool Worksheet::writeTime(const CellReference &row_column, const QTime &t, const Format &format) +{ + if (!row_column.isValid()) + return false; + + return writeTime(row_column.row(), row_column.column(), t, format); +} + +/*! + Write a QTime \a t to the cell (\a row, \a column) with the \a format. + Returns true on success. + */ +bool Worksheet::writeTime(int row, int column, const QTime &t, const Format &format) +{ + Q_D(Worksheet); + if (d->checkDimensions(row, column)) + return false; + + Format fmt = format.isValid() ? format : d->cellFormat(row, column); + if (!fmt.isValid() || !fmt.isDateTimeFormat()) + fmt.setNumberFormat(QStringLiteral("hh:mm:ss")); + d->workbook->styles()->addXfFormat(fmt); + + d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(timeToNumber(t), Cell::NumberType, fmt, this)); + + return true; +} + +/*! + \overload + Write a QUrl \a url to the cell \a row_column with the given \a format \a display and \a tip. + Returns true on success. + */ +bool Worksheet::writeHyperlink(const CellReference &row_column, const QUrl &url, const Format &format, const QString &display, const QString &tip) +{ + if (!row_column.isValid()) + return false; + + return writeHyperlink(row_column.row(), row_column.column(), url, format, display, tip); +} + +/*! + Write a QUrl \a url to the cell (\a row, \a column) with the given \a format \a display and \a tip. + Returns true on success. + */ +bool Worksheet::writeHyperlink(int row, int column, const QUrl &url, const Format &format, const QString &display, const QString &tip) +{ + Q_D(Worksheet); + if (d->checkDimensions(row, column)) + return false; + + //int error = 0; + + QString urlString = url.toString(); + + //Generate proper display string + QString displayString = display.isEmpty() ? urlString : display; + if (displayString.startsWith(QLatin1String("mailto:"))) + displayString.replace(QLatin1String("mailto:"), QString()); + if (displayString.size() > XLSX_STRING_MAX) { + displayString = displayString.left(XLSX_STRING_MAX); + //error = -2; + } + + /* + Location within target. If target is a workbook (or this workbook) + this shall refer to a sheet and cell or a defined name. Can also + be an HTML anchor if target is HTML file. + + c:\temp\file.xlsx#Sheet!A1 + http://a.com/aaa.html#aaaaa + */ + QString locationString; + if (url.hasFragment()) { + locationString = url.fragment(); + urlString = url.toString(QUrl::RemoveFragment); + } + + Format fmt = format.isValid() ? format : d->cellFormat(row, column); + //Given a default style for hyperlink + if (!fmt.isValid()) { + fmt.setFontColor(Qt::blue); + fmt.setFontUnderline(Format::FontUnderlineSingle); + } + d->workbook->styles()->addXfFormat(fmt); + + //Write the hyperlink string as normal string. + d->sharedStrings()->addSharedString(displayString); + d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(displayString, Cell::SharedStringType, fmt, this)); + + //Store the hyperlink data in a separate table + d->urlTable[row][column] = QSharedPointer<XlsxHyperlinkData>(new XlsxHyperlinkData(XlsxHyperlinkData::External, urlString, locationString, QString(), tip)); + + return true; +} + +/*! + * Add one DataValidation \a validation to the sheet. + * Returns true on success. + */ +bool Worksheet::addDataValidation(const DataValidation &validation) +{ + Q_D(Worksheet); + if (validation.ranges().isEmpty() || validation.validationType()==DataValidation::None) + return false; + + d->dataValidationsList.append(validation); + return true; +} + +/*! + * Add one ConditionalFormatting \a cf to the sheet. + * Returns true on success. + */ +bool Worksheet::addConditionalFormatting(const ConditionalFormatting &cf) +{ + Q_D(Worksheet); + if (cf.ranges().isEmpty()) + return false; + + for (int i=0; i<cf.d->cfRules.size(); ++i) { + const QSharedPointer<XlsxCfRuleData> &rule = cf.d->cfRules[i]; + if (!rule->dxfFormat.isEmpty()) + d->workbook->styles()->addDxfFormat(rule->dxfFormat); + rule->priority = 1; + } + d->conditionalFormattingList.append(cf); + return true; +} + +/*! + * Insert an \a image at the position \a row, \a column + * Returns true on success. + */ +bool Worksheet::insertImage(int row, int column, const QImage &image) +{ + Q_D(Worksheet); + + if (image.isNull()) + return false; + + if (!d->drawing) + d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch)); + + DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing.data(), DrawingAnchor::Picture); + + /* + The size are expressed as English Metric Units (EMUs). There are + 12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per + pixel + */ + anchor->from = XlsxMarker(row, column, 0, 0); + anchor->ext = QSize(image.width() * 9525, image.height() * 9525); + + anchor->setObjectPicture(image); + return true; +} + +/*! + * Creates an chart with the given \a size and insert + * at the position \a row, \a column. + * The chart will be returned. + */ +Chart *Worksheet::insertChart(int row, int column, const QSize &size) +{ + Q_D(Worksheet); + + if (!d->drawing) + d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch)); + + DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing.data(), DrawingAnchor::Picture); + + /* + The size are expressed as English Metric Units (EMUs). There are + 12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per + pixel + */ + anchor->from = XlsxMarker(row, column, 0, 0); + anchor->ext = size * 9525; + + QSharedPointer<Chart> chart = QSharedPointer<Chart>(new Chart(this, F_NewFromScratch)); + anchor->setObjectGraphicFrame(chart); + + return chart.data(); +} + +/*! + 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 Worksheet::mergeCells(const CellRange &range, const Format &format) +{ + Q_D(Worksheet); + if (range.rowCount() < 2 && range.columnCount() < 2) + return false; + + if (d->checkDimensions(range.firstRow(), range.firstColumn())) + return false; + + if (format.isValid()) + d->workbook->styles()->addXfFormat(format); + + for (int row = range.firstRow(); row <= range.lastRow(); ++row) { + for (int col = range.firstColumn(); col <= range.lastColumn(); ++col) { + if (row == range.firstRow() && col == range.firstColumn()) { + Cell *cell = cellAt(row, col); + if (cell) { + if (format.isValid()) + cell->d_ptr->format = format; + } else { + writeBlank(row, col, format); + } + } else { + writeBlank(row, col, format); + } + } + } + + d->merges.append(range); + return true; +} + +/*! + Unmerge the cells in the \a range. Returns true on success. + +*/ +bool Worksheet::unmergeCells(const CellRange &range) +{ + Q_D(Worksheet); + if (!d->merges.contains(range)) + return false; + + d->merges.removeOne(range); + return true; +} + +/*! + Returns all the merged cells. +*/ +QList<CellRange> Worksheet::mergedCells() const +{ + Q_D(const Worksheet); + return d->merges; +} + +/*! + * \internal + */ +void Worksheet::saveToXmlFile(QIODevice *device) const +{ + Q_D(const Worksheet); + d->relationships->clear(); + + QXmlStreamWriter writer(device); + + writer.writeStartDocument(QStringLiteral("1.0"), true); + writer.writeStartElement(QStringLiteral("worksheet")); + writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main")); + writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships")); + + //for Excel 2010 + // writer.writeAttribute("xmlns:mc", "http://schemas.openxmlformats.org/markup-compatibility/2006"); + // writer.writeAttribute("xmlns:x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"); + // writer.writeAttribute("mc:Ignorable", "x14ac"); + + writer.writeStartElement(QStringLiteral("dimension")); + writer.writeAttribute(QStringLiteral("ref"), d->generateDimensionString()); + writer.writeEndElement();//dimension + + writer.writeStartElement(QStringLiteral("sheetViews")); + writer.writeStartElement(QStringLiteral("sheetView")); + if (d->windowProtection) + writer.writeAttribute(QStringLiteral("windowProtection"), QStringLiteral("1")); + if (d->showFormulas) + writer.writeAttribute(QStringLiteral("showFormulas"), QStringLiteral("1")); + if (!d->showGridLines) + writer.writeAttribute(QStringLiteral("showGridLines"), QStringLiteral("0")); + if (!d->showRowColHeaders) + writer.writeAttribute(QStringLiteral("showRowColHeaders"), QStringLiteral("0")); + if (!d->showZeros) + writer.writeAttribute(QStringLiteral("showZeros"), QStringLiteral("0")); + if (d->rightToLeft) + writer.writeAttribute(QStringLiteral("rightToLeft"), QStringLiteral("1")); + if (d->tabSelected) + writer.writeAttribute(QStringLiteral("tabSelected"), QStringLiteral("1")); + if (!d->showRuler) + writer.writeAttribute(QStringLiteral("showRuler"), QStringLiteral("0")); + if (!d->showOutlineSymbols) + writer.writeAttribute(QStringLiteral("showOutlineSymbols"), QStringLiteral("0")); + if (!d->showWhiteSpace) + writer.writeAttribute(QStringLiteral("showWhiteSpace"), QStringLiteral("0")); + writer.writeAttribute(QStringLiteral("workbookViewId"), QStringLiteral("0")); + writer.writeEndElement();//sheetView + writer.writeEndElement();//sheetViews + + writer.writeStartElement(QStringLiteral("sheetFormatPr")); + writer.writeAttribute(QStringLiteral("defaultRowHeight"), QString::number(d->default_row_height)); + if (d->default_row_height != 15) + writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1")); + if (d->default_row_zeroed) + writer.writeAttribute(QStringLiteral("zeroHeight"), QStringLiteral("1")); + if (d->outline_row_level) + writer.writeAttribute(QStringLiteral("outlineLevelRow"), QString::number(d->outline_row_level)); + if (d->outline_col_level) + writer.writeAttribute(QStringLiteral("outlineLevelCol"), QString::number(d->outline_col_level)); + //for Excel 2010 + // writer.writeAttribute("x14ac:dyDescent", "0.25"); + writer.writeEndElement();//sheetFormatPr + + if (!d->colsInfo.isEmpty()) { + writer.writeStartElement(QStringLiteral("cols")); + QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(d->colsInfo); + while (it.hasNext()) { + it.next(); + QSharedPointer<XlsxColumnInfo> col_info = it.value(); + writer.writeStartElement(QStringLiteral("col")); + writer.writeAttribute(QStringLiteral("min"), QString::number(col_info->firstColumn)); + writer.writeAttribute(QStringLiteral("max"), QString::number(col_info->lastColumn)); + if (col_info->width) + writer.writeAttribute(QStringLiteral("width"), QString::number(col_info->width, 'g', 15)); + if (!col_info->format.isEmpty()) + writer.writeAttribute(QStringLiteral("style"), QString::number(col_info->format.xfIndex())); + if (col_info->hidden) + writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1")); + if (col_info->width) + writer.writeAttribute(QStringLiteral("customWidth"), QStringLiteral("1")); + if (col_info->outlineLevel) + writer.writeAttribute(QStringLiteral("outlineLevel"), QString::number(col_info->outlineLevel)); + if (col_info->collapsed) + writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1")); + writer.writeEndElement();//col + } + writer.writeEndElement();//cols + } + + writer.writeStartElement(QStringLiteral("sheetData")); + if (d->dimension.isValid()) + d->saveXmlSheetData(writer); + writer.writeEndElement();//sheetData + + d->saveXmlMergeCells(writer); + foreach (const ConditionalFormatting cf, d->conditionalFormattingList) + cf.saveToXml(writer); + d->saveXmlDataValidations(writer); + d->saveXmlHyperlinks(writer); + d->saveXmlDrawings(writer); + + writer.writeEndElement();//worksheet + writer.writeEndDocument(); +} + +void WorksheetPrivate::saveXmlSheetData(QXmlStreamWriter &writer) const +{ + calculateSpans(); + for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) { + if (!(cellTable.contains(row_num) || comments.contains(row_num) || rowsInfo.contains(row_num))) { + //Only process rows with cell data / comments / formatting + continue; + } + + int span_index = (row_num-1) / 16; + QString span; + if (row_spans.contains(span_index)) + span = row_spans[span_index]; + + writer.writeStartElement(QStringLiteral("row")); + writer.writeAttribute(QStringLiteral("r"), QString::number(row_num)); + + if (!span.isEmpty()) + writer.writeAttribute(QStringLiteral("spans"), span); + + if (rowsInfo.contains(row_num)) { + QSharedPointer<XlsxRowInfo> rowInfo = rowsInfo[row_num]; + if (!rowInfo->format.isEmpty()) { + writer.writeAttribute(QStringLiteral("s"), QString::number(rowInfo->format.xfIndex())); + writer.writeAttribute(QStringLiteral("customFormat"), QStringLiteral("1")); + } + //!Todo: support customHeight from info struct + //!Todo: where does this magic number '15' come from? + if (rowInfo->customHeight) { + writer.writeAttribute(QStringLiteral("ht"), QString::number(rowInfo->height)); + writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1")); + } else { + writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("0")); + } + + if (rowInfo->hidden) + writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1")); + if (rowInfo->outlineLevel > 0) + writer.writeAttribute(QStringLiteral("outlineLevel"), QString::number(rowInfo->outlineLevel)); + if (rowInfo->collapsed) + writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1")); + } + + //Write cell data if row contains filled cells + if (cellTable.contains(row_num)) { + for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) { + if (cellTable[row_num].contains(col_num)) { + saveXmlCellData(writer, row_num, col_num, cellTable[row_num][col_num]); + } + } + } + writer.writeEndElement(); //row + } +} + +void WorksheetPrivate::saveXmlCellData(QXmlStreamWriter &writer, int row, int col, QSharedPointer<Cell> cell) const +{ + //This is the innermost loop so efficiency is important. + QString cell_pos = CellReference(row, col).toString(); + + writer.writeStartElement(QStringLiteral("c")); + writer.writeAttribute(QStringLiteral("r"), cell_pos); + + //Style used by the cell, row or col + if (!cell->format().isEmpty()) + writer.writeAttribute(QStringLiteral("s"), QString::number(cell->format().xfIndex())); + else if (rowsInfo.contains(row) && !rowsInfo[row]->format.isEmpty()) + writer.writeAttribute(QStringLiteral("s"), QString::number(rowsInfo[row]->format.xfIndex())); + else if (colsInfoHelper.contains(col) && !colsInfoHelper[col]->format.isEmpty()) + writer.writeAttribute(QStringLiteral("s"), QString::number(colsInfoHelper[col]->format.xfIndex())); + + if (cell->cellType() == Cell::SharedStringType) { + int sst_idx; + if (cell->isRichString()) + sst_idx = sharedStrings()->getSharedStringIndex(cell->d_ptr->richString); + else + sst_idx = sharedStrings()->getSharedStringIndex(cell->value().toString()); + + writer.writeAttribute(QStringLiteral("t"), QStringLiteral("s")); + writer.writeTextElement(QStringLiteral("v"), QString::number(sst_idx)); + } else if (cell->cellType() == Cell::InlineStringType) { + writer.writeAttribute(QStringLiteral("t"), QStringLiteral("inlineStr")); + writer.writeStartElement(QStringLiteral("is")); + if (cell->isRichString()) { + //Rich text string + RichString string = cell->d_ptr->richString; + for (int i=0; i<string.fragmentCount(); ++i) { + writer.writeStartElement(QStringLiteral("r")); + if (string.fragmentFormat(i).hasFontData()) { + writer.writeStartElement(QStringLiteral("rPr")); + //:Todo + writer.writeEndElement();// rPr + } + writer.writeStartElement(QStringLiteral("t")); + if (isSpaceReserveNeeded(string.fragmentText(i))) + writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve")); + writer.writeCharacters(string.fragmentText(i)); + writer.writeEndElement();// t + writer.writeEndElement(); // r + } + } else { + writer.writeStartElement(QStringLiteral("t")); + QString string = cell->value().toString(); + if (isSpaceReserveNeeded(string)) + writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve")); + writer.writeCharacters(string); + writer.writeEndElement(); // t + } + writer.writeEndElement();//is + } else if (cell->cellType() == Cell::NumberType){ + if (cell->hasFormula()) + cell->formula().saveToXml(writer); + if (cell->value().isValid()) {//note that, invalid value means 'v' is blank + double value = cell->value().toDouble(); + writer.writeTextElement(QStringLiteral("v"), QString::number(value, 'g', 15)); + } + } else if (cell->cellType() == Cell::StringType) { + writer.writeAttribute(QStringLiteral("t"), QStringLiteral("str")); + if (cell->hasFormula()) + cell->formula().saveToXml(writer); + writer.writeTextElement(QStringLiteral("v"), cell->value().toString()); + } else if (cell->cellType() == Cell::BooleanType) { + writer.writeAttribute(QStringLiteral("t"), QStringLiteral("b")); + writer.writeTextElement(QStringLiteral("v"), cell->value().toBool() ? QStringLiteral("1") : QStringLiteral("0")); + } + writer.writeEndElement(); //c +} + +void WorksheetPrivate::saveXmlMergeCells(QXmlStreamWriter &writer) const +{ + if (merges.isEmpty()) + return; + + writer.writeStartElement(QStringLiteral("mergeCells")); + writer.writeAttribute(QStringLiteral("count"), QString::number(merges.size())); + + foreach (CellRange range, merges) { + writer.writeEmptyElement(QStringLiteral("mergeCell")); + writer.writeAttribute(QStringLiteral("ref"), range.toString()); + } + + writer.writeEndElement(); //mergeCells +} + +void WorksheetPrivate::saveXmlDataValidations(QXmlStreamWriter &writer) const +{ + if (dataValidationsList.isEmpty()) + return; + + writer.writeStartElement(QStringLiteral("dataValidations")); + writer.writeAttribute(QStringLiteral("count"), QString::number(dataValidationsList.size())); + + foreach (DataValidation validation, dataValidationsList) + validation.saveToXml(writer); + + writer.writeEndElement(); //dataValidations +} + +void WorksheetPrivate::saveXmlHyperlinks(QXmlStreamWriter &writer) const +{ + if (urlTable.isEmpty()) + return; + + writer.writeStartElement(QStringLiteral("hyperlinks")); + QMapIterator<int, QMap<int, QSharedPointer<XlsxHyperlinkData> > > it(urlTable); + while (it.hasNext()) { + it.next(); + int row = it.key(); + QMapIterator <int, QSharedPointer<XlsxHyperlinkData> > it2(it.value()); + while (it2.hasNext()) { + it2.next(); + int col = it2.key(); + QSharedPointer<XlsxHyperlinkData> data = it2.value(); + QString ref = CellReference(row, col).toString(); + writer.writeEmptyElement(QStringLiteral("hyperlink")); + writer.writeAttribute(QStringLiteral("ref"), ref); + if (data->linkType == XlsxHyperlinkData::External) { + //Update relationships + relationships->addWorksheetRelationship(QStringLiteral("/hyperlink"), data->target, QStringLiteral("External")); + + writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(relationships->count())); + } + + if (!data->location.isEmpty()) + writer.writeAttribute(QStringLiteral("location"), data->location); + if (!data->display.isEmpty()) + writer.writeAttribute(QStringLiteral("display"), data->display); + if (!data->tooltip.isEmpty()) + writer.writeAttribute(QStringLiteral("tooltip"), data->tooltip); + } + } + + writer.writeEndElement();//hyperlinks +} + +void WorksheetPrivate::saveXmlDrawings(QXmlStreamWriter &writer) const +{ + if (!drawing) + return; + + int idx = workbook->drawings().indexOf(drawing.data()); + 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(relationships->count())); +} + +void WorksheetPrivate::splitColsInfo(int colFirst, int colLast) +{ + // Split current columnInfo, for example, if "A:H" has been set, + // we are trying to set "B:D", there should be "A", "B:D", "E:H". + // This will be more complex if we try to set "C:F" after "B:D". + { + QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo); + while (it.hasNext()) { + it.next(); + QSharedPointer<XlsxColumnInfo> info = it.value(); + if (colFirst > info->firstColumn && colFirst <= info->lastColumn) { + //split the range, + QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info)); + info->lastColumn = colFirst - 1; + info2->firstColumn = colFirst; + colsInfo.insert(colFirst, info2); + for (int c = info2->firstColumn; c <= info2->lastColumn; ++c) + colsInfoHelper[c] = info2; + + break; + } + } + } + { + QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo); + while (it.hasNext()) { + it.next(); + QSharedPointer<XlsxColumnInfo> info = it.value(); + if (colLast >= info->firstColumn && colLast < info->lastColumn) { + QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info)); + info->lastColumn = colLast; + info2->firstColumn = colLast + 1; + colsInfo.insert(colLast + 1, info2); + for (int c = info2->firstColumn; c <= info2->lastColumn; ++c) + colsInfoHelper[c] = info2; + + break; + } + } + } +} + +bool WorksheetPrivate::isColumnRangeValid(int colFirst, int colLast) +{ + bool ignore_row = true; + bool ignore_col = false; + + if (colFirst > colLast) + return false; + + if (checkDimensions(1, colLast, ignore_row, ignore_col)) + return false; + if (checkDimensions(1, colFirst, ignore_row, ignore_col)) + return false; + + return true; +} + +QList<int> WorksheetPrivate ::getColumnIndexes(int colFirst, int colLast) +{ + splitColsInfo(colFirst, colLast); + + QList<int> nodes; + nodes.append(colFirst); + for (int col = colFirst; col <= colLast; ++col) { + if (colsInfo.contains(col)) { + if (nodes.last() != col) + nodes.append(col); + int nextCol = colsInfo[col]->lastColumn + 1; + if (nextCol <= colLast) + nodes.append(nextCol); + } + } + + return nodes; +} + +/*! + Sets width in characters of a \a range of columns to \a width. + Returns true on success. + */ +bool Worksheet::setColumnWidth(const CellRange &range, double width) +{ + if (!range.isValid()) + return false; + + return setColumnWidth(range.firstColumn(), range.lastColumn(), width); +} + +/*! + Sets format property of a \a range of columns to \a format. Columns are 1-indexed. + Returns true on success. + */ +bool Worksheet::setColumnFormat(const CellRange& range, const Format &format) +{ + if (!range.isValid()) + return false; + + return setColumnFormat(range.firstColumn(), range.lastColumn(), format); +} + +/*! + Sets hidden property of a \a range of columns to \a hidden. Columns are 1-indexed. + Hidden columns are not visible. + Returns true on success. + */ +bool Worksheet::setColumnHidden(const CellRange &range, bool hidden) +{ + if (!range.isValid()) + return false; + + return setColumnHidden(range.firstColumn(), range.lastColumn(), hidden); +} + +/*! + Sets width in characters for columns [\a colFirst, \a colLast] to \a width. + Columns are 1-indexed. + Returns true on success. + */ +bool Worksheet::setColumnWidth(int colFirst, int colLast, double width) +{ + Q_D(Worksheet); + + QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast); + foreach(QSharedPointer<XlsxColumnInfo> columnInfo, columnInfoList) + columnInfo->width = width; + + return (columnInfoList.count() > 0); +} + +/*! + Sets format property of a range of columns [\a colFirst, \a colLast] to \a format. + Columns are 1-indexed. + Returns true on success. + */ +bool Worksheet::setColumnFormat(int colFirst, int colLast, const Format &format) +{ + Q_D(Worksheet); + + QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast); + foreach(QSharedPointer<XlsxColumnInfo> columnInfo, columnInfoList) + columnInfo->format = format; + + if(columnInfoList.count() > 0) { + d->workbook->styles()->addXfFormat(format); + return true; + } + + return false; +} + +/*! + Sets hidden property of a range of columns [\a colFirst, \a colLast] to \a hidden. + Columns are 1-indexed. Returns true on success. + */ +bool Worksheet::setColumnHidden(int colFirst, int colLast, bool hidden) +{ + Q_D(Worksheet); + + QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast); + foreach(QSharedPointer<XlsxColumnInfo> columnInfo, columnInfoList) + columnInfo->hidden = hidden; + + return (columnInfoList.count() > 0); +} + +/*! + Returns width of the \a column in characters of the normal font. Columns are 1-indexed. + */ +double Worksheet::columnWidth(int column) +{ + Q_D(Worksheet); + + QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column); + if (columnInfoList.count() == 1) + return columnInfoList.at(0)->width ; + + return d->sheetFormatProps.defaultColWidth; +} + +/*! + Returns formatting of the \a column. Columns are 1-indexed. + */ +Format Worksheet::columnFormat(int column) +{ + Q_D(Worksheet); + + QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column); + if (columnInfoList.count() == 1) + return columnInfoList.at(0)->format; + + return Format(); +} + +/*! + Returns true if \a column is hidden. Columns are 1-indexed. + */ +bool Worksheet::isColumnHidden(int column) +{ + Q_D(Worksheet); + + QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column); + if (columnInfoList.count() == 1) + return columnInfoList.at(0)->hidden; + + return false; +} + +/*! + 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 Worksheet::setRowHeight(int rowFirst,int rowLast, double height) +{ + Q_D(Worksheet); + + QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast); + + foreach(QSharedPointer<XlsxRowInfo> rowInfo, rowInfoList) { + rowInfo->height = height; + rowInfo->customHeight = true; + } + + return rowInfoList.count() > 0; +} + +/*! + Sets the \a format of the rows including and between \a rowFirst and \a rowLast. + Rows are 1-indexed. + + Returns true if success. +*/ +bool Worksheet::setRowFormat(int rowFirst,int rowLast, const Format &format) +{ + Q_D(Worksheet); + + QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast); + + foreach(QSharedPointer<XlsxRowInfo> rowInfo, rowInfoList) + rowInfo->format = format; + + d->workbook->styles()->addXfFormat(format); + return rowInfoList.count() > 0; +} + +/*! + Sets the \a hidden proeprty 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 Worksheet::setRowHidden(int rowFirst,int rowLast, bool hidden) +{ + Q_D(Worksheet); + + QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast); + foreach(QSharedPointer<XlsxRowInfo> rowInfo, rowInfoList) + rowInfo->hidden = hidden; + + return rowInfoList.count() > 0; +} + +/*! + Returns height of \a row in points. +*/ +double Worksheet::rowHeight(int row) +{ + Q_D(Worksheet); + int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1; + + if (d->checkDimensions(row, min_col, false, true) || !d->rowsInfo.contains(row)) + return d->sheetFormatProps.defaultRowHeight; //return default on invalid row + + + return d->rowsInfo[row]->height; +} + +/*! + Returns format of \a row. +*/ +Format Worksheet::rowFormat(int row) +{ + Q_D(Worksheet); + int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1; + if (d->checkDimensions(row, min_col, false, true) || !d->rowsInfo.contains(row)) + return Format(); //return default on invalid row + + return d->rowsInfo[row]->format; +} + +/*! + Returns true if \a row is hidden. +*/ +bool Worksheet::isRowHidden(int row) +{ + Q_D(Worksheet); + int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1; + if (d->checkDimensions(row, min_col, false, true) || !d->rowsInfo.contains(row)) + return false; //return default on invalid row + + return d->rowsInfo[row]->hidden; +} + +/*! + Groups rows from \a rowFirst to \a rowLast with the given \a collapsed. + + Returns false if error occurs. + */ +bool Worksheet::groupRows(int rowFirst, int rowLast, bool collapsed) +{ + Q_D(Worksheet); + + for (int row=rowFirst; row<=rowLast; ++row) { + if (d->rowsInfo.contains(row)) { + d->rowsInfo[row]->outlineLevel += 1; + } else { + QSharedPointer<XlsxRowInfo> info(new XlsxRowInfo); + info->outlineLevel += 1; + d->rowsInfo.insert(row, info); + } + if (collapsed) + d->rowsInfo[row]->hidden = true; + } + if (collapsed) { + if (!d->rowsInfo.contains(rowLast+1)) + d->rowsInfo.insert(rowLast+1, QSharedPointer<XlsxRowInfo>(new XlsxRowInfo)); + d->rowsInfo[rowLast+1]->collapsed = true; + } + return true; +} + +/*! + \overload + + Groups columns with the given \a range and \a collapsed. + */ +bool Worksheet::groupColumns(const CellRange &range, bool collapsed) +{ + if (!range.isValid()) + return false; + + return groupColumns(range.firstColumn(), range.lastColumn(), collapsed); +} + +/*! + Groups columns from \a colFirst to \a colLast with the given \a collapsed. + Returns false if error occurs. +*/ +bool Worksheet::groupColumns(int colFirst, int colLast, bool collapsed) +{ + Q_D(Worksheet); + + d->splitColsInfo(colFirst, colLast); + + QList<int> nodes; + nodes.append(colFirst); + for (int col = colFirst; col <= colLast; ++col) { + if (d->colsInfo.contains(col)) { + if (nodes.last() != col) + nodes.append(col); + int nextCol = d->colsInfo[col]->lastColumn + 1; + if (nextCol <= colLast) + nodes.append(nextCol); + } + } + + for (int idx = 0; idx < nodes.size(); ++idx) { + int colStart = nodes[idx]; + if (d->colsInfo.contains(colStart)) { + QSharedPointer<XlsxColumnInfo> info = d->colsInfo[colStart]; + info->outlineLevel += 1; + if (collapsed) + info->hidden = true; + } else { + int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1; + QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd)); + info->outlineLevel += 1; + d->colsInfo.insert(colFirst, info); + if (collapsed) + info->hidden = true; + for (int c = colStart; c <= colEnd; ++c) + d->colsInfoHelper[c] = info; + } + } + + if (collapsed) { + int col = colLast+1; + d->splitColsInfo(col, col); + if (d->colsInfo.contains(col)) + d->colsInfo[col]->collapsed = true; + else { + QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(col, col)); + info->collapsed = true; + d->colsInfo.insert(col, info); + d->colsInfoHelper[col] = info; + } + } + + return false; +} + +/*! + Return the range that contains cell data. + */ +CellRange Worksheet::dimension() const +{ + Q_D(const Worksheet); + return d->dimension; +} + +/* + Convert the height of a cell from user's units to pixels. If the + height hasn't been set by the user we use the default value. If + the row is hidden it has a value of zero. +*/ +int WorksheetPrivate::rowPixelsSize(int row) const +{ + double height; + if (row_sizes.contains(row)) + height = row_sizes[row]; + else + height = default_row_height; + return static_cast<int>(4.0 / 3.0 *height); +} + +/* + Convert the width of a cell from user's units to pixels. Excel rounds + the column width to the nearest pixel. If the width hasn't been set + by the user we use the default value. If the column is hidden it + has a value of zero. +*/ +int WorksheetPrivate::colPixelsSize(int col) const +{ + double max_digit_width = 7.0; //For Calabri 11 + double padding = 5.0; + int pixels = 0; + + if (col_sizes.contains(col)) { + double width = col_sizes[col]; + if (width < 1) + pixels = static_cast<int>(width * (max_digit_width + padding) + 0.5); + else + pixels = static_cast<int>(width * max_digit_width + 0.5) + padding; + } else { + pixels = 64; + } + return pixels; +} + +void WorksheetPrivate::loadXmlSheetData(QXmlStreamReader &reader) +{ + Q_Q(Worksheet); + Q_ASSERT(reader.name() == QLatin1String("sheetData")); + + while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetData") && reader.tokenType() == QXmlStreamReader::EndElement)) { + if (reader.readNextStartElement()) { + if (reader.name() == QLatin1String("row")) { + QXmlStreamAttributes attributes = reader.attributes(); + + if (attributes.hasAttribute(QLatin1String("customFormat")) + || attributes.hasAttribute(QLatin1String("customHeight")) + || attributes.hasAttribute(QLatin1String("hidden")) + || attributes.hasAttribute(QLatin1String("outlineLevel")) + || attributes.hasAttribute(QLatin1String("collapsed"))) { + + QSharedPointer<XlsxRowInfo> info(new XlsxRowInfo); + if (attributes.hasAttribute(QLatin1String("customFormat")) && attributes.hasAttribute(QLatin1String("s"))) { + int idx = attributes.value(QLatin1String("s")).toString().toInt(); + info->format = workbook->styles()->xfFormat(idx); + } + + if (attributes.hasAttribute(QLatin1String("customHeight"))) { + info->customHeight = attributes.value(QLatin1String("customHeight")) == QLatin1String("1"); + //Row height is only specified when customHeight is set + if(attributes.hasAttribute(QLatin1String("ht"))) { + info->height = attributes.value(QLatin1String("ht")).toString().toDouble(); + } + } + + //both "hidden" and "collapsed" default are false + info->hidden = attributes.value(QLatin1String("hidden")) == QLatin1String("1"); + info->collapsed = attributes.value(QLatin1String("collapsed")) == QLatin1String("1"); + + if (attributes.hasAttribute(QLatin1String("outlineLevel"))) + info->outlineLevel = attributes.value(QLatin1String("outlineLevel")).toString().toInt(); + + //"r" is optional too. + if (attributes.hasAttribute(QLatin1String("r"))) { + int row = attributes.value(QLatin1String("r")).toString().toInt(); + rowsInfo[row] = info; + } + } + + } else if (reader.name() == QLatin1String("c")) { //Cell + QXmlStreamAttributes attributes = reader.attributes(); + QString r = attributes.value(QLatin1String("r")).toString(); + CellReference pos(r); + + //get format + Format format; + if (attributes.hasAttribute(QLatin1String("s"))) { //"s" == style index + int idx = attributes.value(QLatin1String("s")).toString().toInt(); + format = workbook->styles()->xfFormat(idx); + ////Empty format exists in styles xf table of real .xlsx files, see issue #65. + //if (!format.isValid()) + // qDebug()<<QStringLiteral("<c s=\"%1\">Invalid style index: ").arg(idx)<<idx; + } + + Cell::CellType cellType = Cell::NumberType; + if (attributes.hasAttribute(QLatin1String("t"))) { + QString typeString = attributes.value(QLatin1String("t")).toString(); + if (typeString == QLatin1String("s")) + cellType = Cell::SharedStringType; + else if (typeString == QLatin1String("inlineStr")) + cellType = Cell::InlineStringType; + else if (typeString == QLatin1String("str")) + cellType = Cell::StringType; + else if (typeString == QLatin1String("b")) + cellType = Cell::BooleanType; + else if (typeString == QLatin1String("e")) + cellType = Cell::ErrorType; + else + cellType = Cell::NumberType; + } + + QSharedPointer<Cell> cell(new Cell(QVariant() ,cellType, format, q)); + while (!reader.atEnd() && !(reader.name() == QLatin1String("c") && reader.tokenType() == QXmlStreamReader::EndElement)) { + if (reader.readNextStartElement()) { + if (reader.name() == QLatin1String("f")) { + CellFormula &formula = cell->d_func()->formula; + formula.loadFromXml(reader); + if (formula.formulaType() == CellFormula::SharedType && !formula.formulaText().isEmpty()) { + sharedFormulaMap[formula.sharedIndex()] = formula; + } + } else if (reader.name() == QLatin1String("v")) { + QString value = reader.readElementText(); + if (cellType == Cell::SharedStringType) { + int sst_idx = value.toInt(); + sharedStrings()->incRefByStringIndex(sst_idx); + RichString rs = sharedStrings()->getSharedString(sst_idx); + cell->d_func()->value = rs.toPlainString(); + if (rs.isRichString()) + cell->d_func()->richString = rs; + } else if (cellType == Cell::NumberType) { + cell->d_func()->value = value.toDouble(); + } else if (cellType == Cell::BooleanType) { + cell->d_func()->value = value.toInt() ? true : false; + } else { //Cell::ErrorType and Cell::StringType + cell->d_func()->value = value; + } + } else if (reader.name() == QLatin1String("is")) { + while (!reader.atEnd() && !(reader.name() == QLatin1String("is") && reader.tokenType() == QXmlStreamReader::EndElement)) { + if (reader.readNextStartElement()) { + //:Todo, add rich text read support + if (reader.name() == QLatin1String("t")) { + cell->d_func()->value = reader.readElementText(); + } + } + } + } else if (reader.name() == QLatin1String("extLst")) { + //skip extLst element + while (!reader.atEnd() && !(reader.name() == QLatin1String("extLst") + && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + } + } + } + } + cellTable[pos.row()][pos.column()] = cell; + } + } + } +} + +void WorksheetPrivate::loadXmlColumnsInfo(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("cols")); + + while (!reader.atEnd() && !(reader.name() == QLatin1String("cols") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("col")) { + QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo); + + QXmlStreamAttributes colAttrs = reader.attributes(); + int min = colAttrs.value(QLatin1String("min")).toString().toInt(); + int max = colAttrs.value(QLatin1String("max")).toString().toInt(); + info->firstColumn = min; + info->lastColumn = max; + + //Flag indicating that the column width for the affected column(s) is different from the + // default or has been manually set + if(colAttrs.hasAttribute(QLatin1String("customWidth"))) { + info->customWidth = colAttrs.value(QLatin1String("customWidth")) == QLatin1String("1"); + } + //Note, node may have "width" without "customWidth" + if (colAttrs.hasAttribute(QLatin1String("width"))) { + double width = colAttrs.value(QLatin1String("width")).toString().toDouble(); + info->width = width; + } + + info->hidden = colAttrs.value(QLatin1String("hidden")) == QLatin1String("1"); + info->collapsed = colAttrs.value(QLatin1String("collapsed")) == QLatin1String("1"); + + if (colAttrs.hasAttribute(QLatin1String("style"))) { + int idx = colAttrs.value(QLatin1String("style")).toString().toInt(); + info->format = workbook->styles()->xfFormat(idx); + } + if (colAttrs.hasAttribute(QLatin1String("outlineLevel"))) + info->outlineLevel = colAttrs.value(QLatin1String("outlineLevel")).toString().toInt(); + + colsInfo.insert(min, info); + for (int col=min; col<=max; ++col) + colsInfoHelper[col] = info; + } + } + } +} + +void WorksheetPrivate::loadXmlMergeCells(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("mergeCells")); + + QXmlStreamAttributes attributes = reader.attributes(); + int count = attributes.value(QLatin1String("count")).toString().toInt(); + + while (!reader.atEnd() && !(reader.name() == QLatin1String("mergeCells") && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("mergeCell")) { + QXmlStreamAttributes attrs = reader.attributes(); + QString rangeStr = attrs.value(QLatin1String("ref")).toString(); + merges.append(CellRange(rangeStr)); + } + } + } + + if (merges.size() != count) + qDebug("read merge cells error"); +} + +void WorksheetPrivate::loadXmlDataValidations(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("dataValidations")); + QXmlStreamAttributes attributes = reader.attributes(); + int count = attributes.value(QLatin1String("count")).toString().toInt(); + + while (!reader.atEnd() && !(reader.name() == QLatin1String("dataValidations") + && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement + && reader.name() == QLatin1String("dataValidation")) { + dataValidationsList.append(DataValidation::loadFromXml(reader)); + } + } + + if (dataValidationsList.size() != count) + qDebug("read data validation error"); +} + +void WorksheetPrivate::loadXmlSheetViews(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("sheetViews")); + + while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetViews") + && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement && reader.name() == QLatin1String("sheetView")) { + QXmlStreamAttributes attrs = reader.attributes(); + //default false + windowProtection = attrs.value(QLatin1String("windowProtection")) == QLatin1String("1"); + showFormulas = attrs.value(QLatin1String("showFormulas")) == QLatin1String("1"); + rightToLeft = attrs.value(QLatin1String("rightToLeft")) == QLatin1String("1"); + tabSelected = attrs.value(QLatin1String("tabSelected")) == QLatin1String("1"); + //default true + showGridLines = attrs.value(QLatin1String("showGridLines")) != QLatin1String("0"); + showRowColHeaders = attrs.value(QLatin1String("showRowColHeaders")) != QLatin1String("0"); + showZeros = attrs.value(QLatin1String("showZeros")) != QLatin1String("0"); + showRuler = attrs.value(QLatin1String("showRuler")) != QLatin1String("0"); + showOutlineSymbols = attrs.value(QLatin1String("showOutlineSymbols")) != QLatin1String("0"); + showWhiteSpace = attrs.value(QLatin1String("showWhiteSpace")) != QLatin1String("0"); + } + } +} + +void WorksheetPrivate::loadXmlSheetFormatProps(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("sheetFormatPr")); + QXmlStreamAttributes attributes = reader.attributes(); + XlsxSheetFormatProps formatProps; + + //Retain default values + foreach (QXmlStreamAttribute attrib, attributes) { + if(attrib.name() == QLatin1String("baseColWidth") ) { + formatProps.baseColWidth = attrib.value().toString().toInt(); + } else if(attrib.name() == QLatin1String("customHeight")) { + formatProps.customHeight = attrib.value() == QLatin1String("1"); + } else if(attrib.name() == QLatin1String("defaultColWidth")) { + formatProps.defaultColWidth = attrib.value().toString().toDouble(); + } else if(attrib.name() == QLatin1String("defaultRowHeight")) { + formatProps.defaultRowHeight = attrib.value().toString().toDouble(); + } else if(attrib.name() == QLatin1String("outlineLevelCol")) { + formatProps.outlineLevelCol = attrib.value().toString().toInt(); + } else if(attrib.name() == QLatin1String("outlineLevelRow")) { + formatProps.outlineLevelRow = attrib.value().toString().toInt(); + } else if(attrib.name() == QLatin1String("thickBottom")) { + formatProps.thickBottom = attrib.value() == QLatin1String("1"); + } else if(attrib.name() == QLatin1String("thickTop")) { + formatProps.thickTop = attrib.value() == QLatin1String("1"); + } else if(attrib.name() == QLatin1String("zeroHeight")) { + formatProps.zeroHeight = attrib.value() == QLatin1String("1"); + } + } + + if(formatProps.defaultColWidth == 0.0) { //not set + formatProps.defaultColWidth = WorksheetPrivate::calculateColWidth(formatProps.baseColWidth); + } + +} +double WorksheetPrivate::calculateColWidth(int characters) +{ + //!Todo + //Take normal style' font maximum width and add padding and margin pixels + return characters + 0.5; +} + +void WorksheetPrivate::loadXmlHyperlinks(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("hyperlinks")); + + while (!reader.atEnd() && !(reader.name() == QLatin1String("hyperlinks") + && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement && reader.name() == QLatin1String("hyperlink")) { + QXmlStreamAttributes attrs = reader.attributes(); + CellReference pos(attrs.value(QLatin1String("ref")).toString()); + if (pos.isValid()) { //Valid + QSharedPointer<XlsxHyperlinkData> link(new XlsxHyperlinkData); + link->display = attrs.value(QLatin1String("display")).toString(); + link->tooltip = attrs.value(QLatin1String("tooltip")).toString(); + link->location = attrs.value(QLatin1String("location")).toString(); + + if (attrs.hasAttribute(QLatin1String("r:id"))) { + link->linkType = XlsxHyperlinkData::External; + XlsxRelationship ship = relationships->getRelationshipById(attrs.value(QLatin1String("r:id")).toString()); + link->target = ship.target; + } else { + link->linkType = XlsxHyperlinkData::Internal; + } + + urlTable[pos.row()][pos.column()] = link; + } + } + } +} + +QList <QSharedPointer<XlsxColumnInfo> > WorksheetPrivate::getColumnInfoList(int colFirst, int colLast) +{ + QList <QSharedPointer<XlsxColumnInfo> > columnsInfoList; + if(isColumnRangeValid(colFirst,colLast)) + { + QList<int> nodes = getColumnIndexes(colFirst, colLast); + + for (int idx = 0; idx < nodes.size(); ++idx) { + int colStart = nodes[idx]; + if (colsInfo.contains(colStart)) { + QSharedPointer<XlsxColumnInfo> info = colsInfo[colStart]; + columnsInfoList.append(info); + } else { + int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1; + QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd)); + colsInfo.insert(colFirst, info); + columnsInfoList.append(info); + for (int c = colStart; c <= colEnd; ++c) + colsInfoHelper[c] = info; + } + } + } + + return columnsInfoList; +} + +QList <QSharedPointer<XlsxRowInfo> > WorksheetPrivate::getRowInfoList(int rowFirst, int rowLast) +{ + QList <QSharedPointer<XlsxRowInfo> > rowInfoList; + + int min_col = dimension.firstColumn() < 1 ? 1 : dimension.firstColumn(); + + for(int row = rowFirst; row <= rowLast; ++row) { + if (checkDimensions(row, min_col, false, true)) + continue; + + QSharedPointer<XlsxRowInfo> rowInfo; + if ((rowsInfo[row]).isNull()){ + rowsInfo[row] = QSharedPointer<XlsxRowInfo>(new XlsxRowInfo()); + } + rowInfoList.append(rowsInfo[row]); + } + + return rowInfoList; +} + +bool Worksheet::loadFromXmlFile(QIODevice *device) +{ + Q_D(Worksheet); + + QXmlStreamReader reader(device); + while (!reader.atEnd()) { + reader.readNextStartElement(); + if (reader.tokenType() == QXmlStreamReader::StartElement) { + if (reader.name() == QLatin1String("dimension")) { + QXmlStreamAttributes attributes = reader.attributes(); + QString range = attributes.value(QLatin1String("ref")).toString(); + d->dimension = CellRange(range); + } else if (reader.name() == QLatin1String("sheetViews")) { + d->loadXmlSheetViews(reader); + } else if (reader.name() == QLatin1String("sheetFormatPr")) { + d->loadXmlSheetFormatProps(reader); + } else if (reader.name() == QLatin1String("cols")) { + d->loadXmlColumnsInfo(reader); + } else if (reader.name() == QLatin1String("sheetData")) { + d->loadXmlSheetData(reader); + } else if (reader.name() == QLatin1String("mergeCells")) { + d->loadXmlMergeCells(reader); + } else if (reader.name() == QLatin1String("dataValidations")) { + d->loadXmlDataValidations(reader); + } else if (reader.name() == QLatin1String("conditionalFormatting")) { + ConditionalFormatting cf; + cf.loadFromXml(reader, workbook()->styles()); + d->conditionalFormattingList.append(cf); + } else if (reader.name() == QLatin1String("hyperlinks")) { + d->loadXmlHyperlinks(reader); + } else if (reader.name() == QLatin1String("drawing")) { + QString rId = reader.attributes().value(QStringLiteral("r:id")).toString(); + QString name = d->relationships->getRelationshipById(rId).target; + QString path = QDir::cleanPath(splitPath(filePath())[0] + QLatin1String("/") + name); + d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_LoadFromExists)); + d->drawing->setFilePath(path); + } else if (reader.name() == QLatin1String("extLst")) { + //Todo: add extLst support + while (!reader.atEnd() && !(reader.name() == QLatin1String("extLst") + && reader.tokenType() == QXmlStreamReader::EndElement)) { + reader.readNextStartElement(); + } + } + } + } + + d->validateDimension(); + return true; +} + +/* + * Documents imported from Google Docs does not contain dimension data. + */ +void WorksheetPrivate::validateDimension() +{ + if (dimension.isValid() || cellTable.isEmpty()) + return; + + int firstRow = cellTable.constBegin().key(); + int lastRow = (cellTable.constEnd()-1).key(); + int firstColumn = -1; + int lastColumn = -1; + + for (QMap<int, QMap<int, QSharedPointer<Cell> > >::const_iterator it = cellTable.begin(); it != cellTable.end(); ++it) + { + Q_ASSERT(!it.value().isEmpty()); + + if (firstColumn == -1 || it.value().constBegin().key() < firstColumn) + firstColumn = it.value().constBegin().key(); + + if (lastColumn == -1 || (it.value().constEnd()-1).key() > lastColumn) + lastColumn = (it.value().constEnd()-1).key(); + } + + CellRange cr(firstRow, firstColumn, lastRow, lastColumn); + + if (cr.isValid()) + dimension = cr; +} + +/*! + * \internal + * Unit test can use this member to get sharedString object. + */ +SharedStrings *WorksheetPrivate::sharedStrings() const +{ + return workbook->sharedStrings(); +} + +QT_END_NAMESPACE_XLSX
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxworksheet.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,158 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXWORKSHEET_H +#define XLSXWORKSHEET_H + +#include "xlsxabstractsheet.h" +#include "xlsxcell.h" +#include "xlsxcellrange.h" +#include "xlsxcellreference.h" +#include <QStringList> +#include <QMap> +#include <QVariant> +#include <QPointF> +#include <QSharedPointer> +class QIODevice; +class QDateTime; +class QUrl; +class QImage; +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 Q_XLSX_EXPORT Worksheet : public AbstractSheet +{ + Q_DECLARE_PRIVATE(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()); + 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; + + bool insertImage(int row, int column, const QImage &image); + Chart *insertChart(int row, int column, const QSize &size); + + bool mergeCells(const CellRange &range, const Format &format=Format()); + bool unmergeCells(const CellRange &range); + QList<CellRange> 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); + + ~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; + + void saveToXmlFile(QIODevice *device) const; + bool loadFromXmlFile(QIODevice *device); +}; + +QT_END_NAMESPACE_XLSX +#endif // XLSXWORKSHEET_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxworksheet_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,233 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef XLSXWORKSHEET_P_H +#define XLSXWORKSHEET_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 "xlsxworksheet.h" +#include "xlsxabstractsheet_p.h" +#include "xlsxcell.h" +#include "xlsxdatavalidation.h" +#include "xlsxconditionalformatting.h" +#include "xlsxcellformula.h" + +#include <QImage> +#include <QSharedPointer> +#include <QRegularExpression> + +class QXmlStreamWriter; +class QXmlStreamReader; + +namespace QXlsx { + +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 = 0.0, + 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, double width=0, const Format &format=Format(), bool hidden=false) : + firstColumn(firstColumn), lastColumn(lastColumn), customWidth(false), width(width), format(format), hidden(hidden) + , outlineLevel(0), collapsed(false) + { + + } + int firstColumn; + int lastColumn; + bool customWidth; + double width; + Format format; + bool hidden; + int outlineLevel; + bool collapsed; +}; + +class XLSX_AUTOTEST_EXPORT WorksheetPrivate : public AbstractSheetPrivate +{ + Q_DECLARE_PUBLIC(Worksheet) +public: + WorksheetPrivate(Worksheet *p, Worksheet::CreateFlag flag); + ~WorksheetPrivate(); + 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, QSharedPointer<Cell> 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<QSharedPointer<XlsxRowInfo> > getRowInfoList(int rowFirst, int rowLast); + QList <QSharedPointer<XlsxColumnInfo> > getColumnInfoList(int colFirst, int colLast); + QList<int> getColumnIndexes(int colFirst, int colLast); + bool isColumnRangeValid(int colFirst, int colLast); + + SharedStrings *sharedStrings() const; + + QMap<int, QMap<int, QSharedPointer<Cell> > > cellTable; + QMap<int, QMap<int, QString> > comments; + QMap<int, QMap<int, QSharedPointer<XlsxHyperlinkData> > > urlTable; + QList<CellRange> merges; + QMap<int, QSharedPointer<XlsxRowInfo> > rowsInfo; + QMap<int, QSharedPointer<XlsxColumnInfo> > colsInfo; + QMap<int, QSharedPointer<XlsxColumnInfo> > colsInfoHelper; + + QList<DataValidation> dataValidationsList; + QList<ConditionalFormatting> conditionalFormattingList; + QMap<int, CellFormula> sharedFormulaMap; + + CellRange dimension; + int previous_row; + + mutable QMap<int, QString> row_spans; + QMap<int, double> row_sizes; + QMap<int, double> col_sizes; + + int outline_row_level; + int outline_col_level; + + int default_row_height; + bool default_row_zeroed; + + 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); +}; + +} +#endif // XLSXWORKSHEET_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxzipreader.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,73 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#include "xlsxzipreader_p.h" + +#include <private/qzipreader_p.h> + +namespace QXlsx { + +ZipReader::ZipReader(const QString &filePath) : + m_reader(new QZipReader(filePath)) +{ + init(); +} + +ZipReader::ZipReader(QIODevice *device) : + m_reader(new QZipReader(device)) +{ + init(); +} + +ZipReader::~ZipReader() +{ + +} + +void ZipReader::init() +{ + QList<QZipReader::FileInfo> allFiles = m_reader->fileInfoList(); + foreach (const QZipReader::FileInfo &fi, allFiles) { + if (fi.isFile) + m_filePaths.append(fi.filePath); + } +} + +bool ZipReader::exists() const +{ + return m_reader->exists(); +} + +QStringList ZipReader::filePaths() const +{ + return m_filePaths; +} + +QByteArray ZipReader::fileData(const QString &fileName) const +{ + return m_reader->fileData(fileName); +} + +} // namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxzipreader_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,67 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ + +#ifndef QXLSX_XLSXZIPREADER_P_H +#define QXLSX_XLSXZIPREADER_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 "xlsxglobal.h" +#include <QScopedPointer> +#include <QStringList> +class QZipReader; +class QIODevice; + +namespace QXlsx { + +class XLSX_AUTOTEST_EXPORT 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<QZipReader> m_reader; + QStringList m_filePaths; +}; + +} // namespace QXlsx + +#endif // QXLSX_XLSXZIPREADER_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxzipwriter.cpp Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,68 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#include "xlsxzipwriter_p.h" +#include <QDebug> +#include <private/qzipwriter_p.h> + +namespace QXlsx { + +ZipWriter::ZipWriter(const QString &filePath) +{ + m_writer = new QZipWriter(filePath, QIODevice::WriteOnly); + m_writer->setCompressionPolicy(QZipWriter::AutoCompress); +} + +ZipWriter::ZipWriter(QIODevice *device) +{ + m_writer = new QZipWriter(device); + m_writer->setCompressionPolicy(QZipWriter::AutoCompress); +} + +ZipWriter::~ZipWriter() +{ + delete m_writer; +} + +bool ZipWriter::error() const +{ + return m_writer->status() != QZipWriter::NoError; +} + +void ZipWriter::addFile(const QString &filePath, QIODevice *device) +{ + m_writer->addFile(filePath, device); +} + +void ZipWriter::addFile(const QString &filePath, const QByteArray &data) +{ + m_writer->addFile(filePath, data); +} + +void ZipWriter::close() +{ + m_writer->close(); +} + +} // namespace QXlsx
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/xlsx/xlsxzipwriter_p.h Tue Mar 22 10:38:08 2016 +0100 @@ -0,0 +1,63 @@ +/**************************************************************************** +** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me> +** All right reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining +** a copy of this software and associated documentation files (the +** "Software"), to deal in the Software without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Software, and to +** permit persons to whom the Software is furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be +** included in all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +** +****************************************************************************/ +#ifndef QXLSX_ZIPWRITER_H +#define QXLSX_ZIPWRITER_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 <QString> +class QIODevice; +class QZipWriter; + +namespace QXlsx { + +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; +}; + +} // namespace QXlsx + +#endif // QXLSX_ZIPWRITER_H