File indexing completed on 2024-09-15 03:31:21

0001 // xlsxworksheet.cpp
0002 
0003 #include <QtGlobal>
0004 #include <QVariant>
0005 #include <QDateTime>
0006 #include <QDate>
0007 #include <QTime>
0008 #include <QPoint>
0009 #include <QFile>
0010 #include <QUrl>
0011 #include <QDebug>
0012 #include <QBuffer>
0013 #include <QXmlStreamWriter>
0014 #include <QXmlStreamReader>
0015 #include <QTextDocument>
0016 #include <QDir>
0017 #include <QMapIterator>
0018 #include <QMap>
0019 
0020 #include <cmath>
0021 
0022 #include "xlsxrichstring.h"
0023 #include "xlsxcellreference.h"
0024 #include "xlsxworksheet.h"
0025 #include "xlsxworksheet_p.h"
0026 #include "xlsxworkbook.h"
0027 #include "xlsxformat.h"
0028 #include "xlsxformat_p.h"
0029 #include "xlsxutility_p.h"
0030 #include "xlsxsharedstrings_p.h"
0031 #include "xlsxdrawing_p.h"
0032 #include "xlsxstyles_p.h"
0033 #include "xlsxcell.h"
0034 #include "xlsxcell_p.h"
0035 #include "xlsxcellrange.h"
0036 #include "xlsxconditionalformatting_p.h"
0037 #include "xlsxdrawinganchor_p.h"
0038 #include "xlsxchart.h"
0039 #include "xlsxcellformula.h"
0040 #include "xlsxcellformula_p.h"
0041 #include "xlsxcelllocation.h"
0042 
0043 QT_BEGIN_NAMESPACE_XLSX
0044 
0045 WorksheetPrivate::WorksheetPrivate(Worksheet *p, Worksheet::CreateFlag flag)
0046 : AbstractSheetPrivate(p, flag),
0047   windowProtection(false),
0048   showFormulas(false),
0049   showGridLines(true),
0050   showRowColHeaders(true),
0051   showZeros(true),
0052   rightToLeft(false),
0053   tabSelected(false),
0054   showRuler(false),
0055   showOutlineSymbols(true),
0056   showWhiteSpace(true),
0057   urlPattern(QStringLiteral("^([fh]tt?ps?://)|(mailto:)|(file://)"))
0058 {
0059     previous_row = 0;
0060 
0061     outline_row_level = 0;
0062     outline_col_level = 0;
0063 
0064     default_row_height = 15;
0065     default_row_zeroed = false;
0066 }
0067 
0068 WorksheetPrivate::~WorksheetPrivate()
0069 {
0070 }
0071 
0072 /*
0073   Calculate the "spans" attribute of the <row> tag. This is an
0074   XLSX optimisation and isn't strictly required. However, it
0075   makes comparing files easier. The span is the same for each
0076   block of 16 rows.
0077  */
0078 void WorksheetPrivate::calculateSpans() const
0079 {
0080     row_spans.clear();
0081     int span_min = XLSX_COLUMN_MAX+1;
0082     int span_max = -1;
0083 
0084     for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) {
0085         auto it = cellTable.constFind(row_num);
0086         if (it != cellTable.constEnd()) {
0087             for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) {
0088                 if (it->contains(col_num)) {
0089                     if (span_max == -1) {
0090                         span_min = col_num;
0091                         span_max = col_num;
0092                     } else {
0093                         if (col_num < span_min)
0094                             span_min = col_num;
0095                         else if (col_num > span_max)
0096                             span_max = col_num;
0097                     }
0098                 }
0099             }
0100         }
0101         auto cIt = comments.constFind(row_num);
0102         if (cIt != comments.constEnd()) {
0103             for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) {
0104                 if (cIt->contains(col_num)) {
0105                     if (span_max == -1) {
0106                         span_min = col_num;
0107                         span_max = col_num;
0108                     } else {
0109                         if (col_num < span_min)
0110                             span_min = col_num;
0111                         else if (col_num > span_max)
0112                             span_max = col_num;
0113                     }
0114                 }
0115             }
0116         }
0117 
0118         if (row_num%16 == 0 || row_num == dimension.lastRow()) {
0119             if (span_max != -1) {
0120                 row_spans[row_num / 16] = QStringLiteral("%1:%2").arg(span_min).arg(span_max);
0121                 span_min = XLSX_COLUMN_MAX+1;
0122                 span_max = -1;
0123             }
0124         }
0125     }
0126 }
0127 
0128 
0129 QString WorksheetPrivate::generateDimensionString() const
0130 {
0131     if (!dimension.isValid())
0132         return QStringLiteral("A1");
0133     else
0134         return dimension.toString();
0135 }
0136 
0137 /*
0138   Check that row and col are valid and store the max and min
0139   values for use in other methods/elements. The ignore_row /
0140   ignore_col flags is used to indicate that we wish to perform
0141   the dimension check without storing the value. The ignore
0142   flags are use by setRow() and dataValidate.
0143 */
0144 int WorksheetPrivate::checkDimensions(int row, int col, bool ignore_row, bool ignore_col)
0145 {
0146     Q_ASSERT_X(row!=0, "checkDimensions", "row should start from 1 instead of 0");
0147     Q_ASSERT_X(col!=0, "checkDimensions", "column should start from 1 instead of 0");
0148 
0149     if (row > XLSX_ROW_MAX || row < 1 || col > XLSX_COLUMN_MAX || col < 1)
0150         return -1;
0151 
0152     if (!ignore_row) {
0153         if (row < dimension.firstRow() || dimension.firstRow() == -1) dimension.setFirstRow(row);
0154         if (row > dimension.lastRow()) dimension.setLastRow(row);
0155     }
0156     if (!ignore_col) {
0157         if (col < dimension.firstColumn() || dimension.firstColumn() == -1) dimension.setFirstColumn(col);
0158         if (col > dimension.lastColumn()) dimension.setLastColumn(col);
0159     }
0160 
0161     return 0;
0162 }
0163 
0164 /*!
0165   \class Worksheet
0166   \inmodule QtXlsx
0167   \brief Represent one worksheet in the workbook.
0168 */
0169 
0170 /*!
0171  * \internal
0172  */
0173 Worksheet::Worksheet(const QString &name, int id, Workbook *workbook, CreateFlag flag)
0174     :AbstractSheet(name, id, workbook, new WorksheetPrivate(this, flag))
0175 {
0176     if (!workbook) //For unit test propose only. Ignore the memery leak.
0177         d_func()->workbook = new Workbook(flag);
0178 }
0179 
0180 /*!
0181  * \internal
0182  *
0183  * Make a copy of this sheet.
0184  */
0185 
0186 Worksheet *Worksheet::copy(const QString &distName, int distId) const
0187 {
0188     Q_D(const Worksheet);
0189     Worksheet *sheet = new Worksheet(distName, distId, d->workbook, F_NewFromScratch);
0190     WorksheetPrivate *sheet_d = sheet->d_func();
0191 
0192     sheet_d->dimension = d->dimension;
0193 
0194     QMapIterator<int, QMap<int, std::shared_ptr<Cell> > > it(d->cellTable);
0195     while (it.hasNext())
0196     {
0197         it.next();
0198         int row = it.key();
0199         QMapIterator<int, std::shared_ptr<Cell> > it2(it.value());
0200         while (it2.hasNext())
0201         {
0202             it2.next();
0203             int col = it2.key();
0204 
0205             auto cell = std::make_shared<Cell>(it2.value().get());
0206             cell->d_ptr->parent = sheet;
0207 
0208             if (cell->cellType() == Cell::SharedStringType)
0209                 d->workbook->sharedStrings()->addSharedString(cell->d_ptr->richString);
0210 
0211             sheet_d->cellTable[row][col] = cell;
0212         }
0213     }
0214 
0215     sheet_d->merges = d->merges;
0216 //    sheet_d->rowsInfo = d->rowsInfo;
0217 //    sheet_d->colsInfo = d->colsInfo;
0218 //    sheet_d->colsInfoHelper = d->colsInfoHelper;
0219 //    sheet_d->dataValidationsList = d->dataValidationsList;
0220 //    sheet_d->conditionalFormattingList = d->conditionalFormattingList;
0221 
0222     return sheet;
0223 }
0224 
0225 /*!
0226  * Destroys this workssheet.
0227  */
0228 Worksheet::~Worksheet()
0229 {
0230 }
0231 
0232 /*!
0233  * Returns whether sheet is protected.
0234  */
0235 bool Worksheet::isWindowProtected() const
0236 {
0237     Q_D(const Worksheet);
0238     return d->windowProtection;
0239 }
0240 
0241 /*!
0242  * Protects/unprotects the sheet based on \a protect.
0243  */
0244 void Worksheet::setWindowProtected(bool protect)
0245 {
0246     Q_D(Worksheet);
0247     d->windowProtection = protect;
0248 }
0249 
0250 /*!
0251  * Return whether formulas instead of their calculated results shown in cells
0252  */
0253 bool Worksheet::isFormulasVisible() const
0254 {
0255     Q_D(const Worksheet);
0256     return d->showFormulas;
0257 }
0258 
0259 /*!
0260  * Show formulas in cells instead of their calculated results when \a visible is true.
0261  */
0262 void Worksheet::setFormulasVisible(bool visible)
0263 {
0264     Q_D(Worksheet);
0265     d->showFormulas = visible;
0266 }
0267 
0268 /*!
0269  * Return whether gridlines is shown or not.
0270  */
0271 bool Worksheet::isGridLinesVisible() const
0272 {
0273     Q_D(const Worksheet);
0274     return d->showGridLines;
0275 }
0276 
0277 /*!
0278  * Show or hide the gridline based on \a visible
0279  */
0280 void Worksheet::setGridLinesVisible(bool visible)
0281 {
0282     Q_D(Worksheet);
0283     d->showGridLines = visible;
0284 }
0285 
0286 /*!
0287  * Return whether is row and column headers is vislbe.
0288  */
0289 bool Worksheet::isRowColumnHeadersVisible() const
0290 {
0291     Q_D(const Worksheet);
0292     return d->showRowColHeaders;
0293 }
0294 
0295 /*!
0296  * Show or hide the row column headers based on \a visible
0297  */
0298 void Worksheet::setRowColumnHeadersVisible(bool visible)
0299 {
0300     Q_D(Worksheet);
0301     d->showRowColHeaders = visible;
0302 }
0303 
0304 
0305 /*!
0306  * Return whether the sheet is shown right-to-left or not.
0307  */
0308 bool Worksheet::isRightToLeft() const
0309 {
0310     Q_D(const Worksheet);
0311     return d->rightToLeft;
0312 }
0313 
0314 /*!
0315  * Enable or disable the right-to-left based on \a enable.
0316  */
0317 void Worksheet::setRightToLeft(bool enable)
0318 {
0319     Q_D(Worksheet);
0320     d->rightToLeft = enable;
0321 }
0322 
0323 /*!
0324  * Return whether is cells that have zero value show a zero.
0325  */
0326 bool Worksheet::isZerosVisible() const
0327 {
0328     Q_D(const Worksheet);
0329     return d->showZeros;
0330 }
0331 
0332 /*!
0333  * Show a zero in cells that have zero value if \a visible is true.
0334  */
0335 void Worksheet::setZerosVisible(bool visible)
0336 {
0337     Q_D(Worksheet);
0338     d->showZeros = visible;
0339 }
0340 
0341 /*!
0342  * Return whether this tab is selected.
0343  */
0344 bool Worksheet::isSelected() const
0345 {
0346     Q_D(const Worksheet);
0347     return d->tabSelected;
0348 }
0349 
0350 /*!
0351  * Select this sheet if \a select is true.
0352  */
0353 void Worksheet::setSelected(bool select)
0354 {
0355     Q_D(Worksheet);
0356     d->tabSelected = select;
0357 }
0358 
0359 /*!
0360  * Return whether is ruler is shown.
0361  */
0362 bool Worksheet::isRulerVisible() const
0363 {
0364     Q_D(const Worksheet);
0365     return d->showRuler;
0366 
0367 }
0368 
0369 /*!
0370  * Show or hide the ruler based on \a visible.
0371  */
0372 void Worksheet::setRulerVisible(bool visible)
0373 {
0374     Q_D(Worksheet);
0375     d->showRuler = visible;
0376 
0377 }
0378 
0379 /*!
0380  * Return whether is outline symbols is shown.
0381  */
0382 bool Worksheet::isOutlineSymbolsVisible() const
0383 {
0384     Q_D(const Worksheet);
0385     return d->showOutlineSymbols;
0386 }
0387 
0388 /*!
0389  * Show or hide the outline symbols based ib \a visible.
0390  */
0391 void Worksheet::setOutlineSymbolsVisible(bool visible)
0392 {
0393     Q_D(Worksheet);
0394     d->showOutlineSymbols = visible;
0395 }
0396 
0397 /*!
0398  * Return whether is white space is shown.
0399  */
0400 bool Worksheet::isWhiteSpaceVisible() const
0401 {
0402     Q_D(const Worksheet);
0403     return d->showWhiteSpace;
0404 }
0405 
0406 /*!
0407  * Show or hide the white space based on \a visible.
0408  */
0409 void Worksheet::setWhiteSpaceVisible(bool visible)
0410 {
0411     Q_D(Worksheet);
0412     d->showWhiteSpace = visible;
0413 }
0414 
0415 /*!
0416  * Write \a value to cell (\a row, \a column) with the \a format.
0417  * Both \a row and \a column are all 1-indexed value.
0418  *
0419  * Returns true on success.
0420  */
0421 bool Worksheet::write(int row, int column, const QVariant &value, const Format &format)
0422 {
0423     Q_D(Worksheet);
0424 
0425     if (d->checkDimensions(row, column))
0426         return false;
0427 
0428     bool ret = true;
0429     if (value.isNull())
0430     {
0431         //Blank
0432         ret = writeBlank(row, column, format);
0433     }
0434     else if (value.userType() == QMetaType::QString)
0435     {
0436         //String
0437         QString token = value.toString();
0438         bool ok;
0439 
0440         if (token.startsWith(QLatin1String("=")))
0441         {
0442             //convert to formula
0443             ret = writeFormula(row, column, CellFormula(token), format);
0444         }
0445         else if (d->workbook->isStringsToHyperlinksEnabled() && token.contains(d->urlPattern))
0446         {
0447             //convert to url
0448             ret = writeHyperlink(row, column, QUrl(token));
0449         }
0450         else if (d->workbook->isStringsToNumbersEnabled() && (value.toDouble(&ok), ok))
0451         {
0452             //Try convert string to number if the flag enabled.
0453             ret = writeNumeric(row, column, value.toDouble(), format);
0454         }
0455         else
0456         {
0457             //normal string now
0458             ret = writeString(row, column, token, format);
0459         }
0460     }
0461     else if (value.userType() == qMetaTypeId<RichString>())
0462     {
0463         ret = writeString(row, column, value.value<RichString>(), format);
0464     }
0465     else if (value.userType() == QMetaType::Int || value.userType() == QMetaType::UInt
0466                || value.userType() == QMetaType::LongLong || value.userType() == QMetaType::ULongLong
0467                || value.userType() == QMetaType::Double || value.userType() == QMetaType::Float)
0468     {
0469         //Number
0470 
0471         ret = writeNumeric(row, column, value.toDouble(), format);
0472     }
0473     else if (value.userType() == QMetaType::Bool)
0474     {
0475         //Bool
0476         ret = writeBool(row,column, value.toBool(), format);
0477     }
0478     else if (value.userType() == QMetaType::QDateTime ) // dev67
0479     {
0480         //DateTime, Date
0481         //  note that, QTime cann't convert to QDateTime
0482         ret = writeDateTime(row, column, value.toDateTime(), format);
0483     }
0484     else if ( value.userType() == QMetaType::QDate ) // dev67
0485     {
0486         ret = writeDate(row, column, value.toDate(), format);
0487     }
0488     else if (value.userType() == QMetaType::QTime)
0489     {
0490         //Time
0491         ret = writeTime(row, column, value.toTime(), format);
0492     }
0493     else if (value.userType() == QMetaType::QUrl)
0494     {
0495         //Url
0496         ret = writeHyperlink(row, column, value.toUrl(), format);
0497     }
0498     else
0499     {
0500         //Wrong type
0501         return false;
0502     }
0503 
0504     return ret;
0505 }
0506 
0507 /*!
0508  * \overload
0509  * Write \a value to cell \a row_column with the \a format.
0510  * Both row and column are all 1-indexed value.
0511  * Returns true on success.
0512  */
0513 bool Worksheet::write(const CellReference &row_column, const QVariant &value, const Format &format)
0514 {
0515     if (!row_column.isValid())
0516         return false;
0517 
0518     return write(row_column.row(), row_column.column(), value, format);
0519 }
0520 
0521 /*!
0522     \overload
0523     Return the contents of the cell \a row_column.
0524  */
0525 QVariant Worksheet::read(const CellReference &row_column) const
0526 {
0527     if (!row_column.isValid())
0528         return QVariant();
0529 
0530     return read(row_column.row(), row_column.column());
0531 }
0532 
0533 /*!
0534     Return the contents of the cell (\a row, \a column).
0535  */
0536 QVariant Worksheet::read(int row, int column) const
0537 {
0538     Q_D(const Worksheet);
0539 
0540     Cell *cell = cellAt(row, column);
0541     if (!cell)
0542         return QVariant();
0543 
0544     if (cell->hasFormula())
0545     {
0546         if (cell->formula().formulaType() == CellFormula::NormalType)
0547         {
0548             return QVariant(QLatin1String("=")+cell->formula().formulaText());
0549         }
0550         else if (cell->formula().formulaType() == CellFormula::SharedType)
0551         {
0552             if (!cell->formula().formulaText().isEmpty())
0553             {
0554                 return QVariant(QLatin1String("=")+cell->formula().formulaText());
0555             }
0556             else
0557             {
0558                 int si = cell->formula().sharedIndex();
0559                 const CellFormula &rootFormula = d->sharedFormulaMap[ si ];
0560                 CellReference rootCellRef = rootFormula.reference().topLeft();
0561                 QString rootFormulaText = rootFormula.formulaText();
0562                 QString newFormulaText = convertSharedFormula(rootFormulaText, rootCellRef, CellReference(row, column));
0563                 return QVariant(QLatin1String("=")+newFormulaText);
0564             }
0565         }
0566     }
0567 
0568     if (cell->isDateTime())
0569     {
0570         QVariant vDateTime = cell->dateTime();
0571         return vDateTime;
0572     }
0573 
0574     return cell->value();
0575 }
0576 
0577 /*!
0578  * Returns the cell at the given \a row_column. If there
0579  * is no cell at the specified position, the function returns 0.
0580  */
0581 Cell *Worksheet::cellAt(const CellReference &row_column) const
0582 {
0583     if (!row_column.isValid())
0584         return nullptr;
0585 
0586     return cellAt(row_column.row(), row_column.column());
0587 }
0588 
0589 /*!
0590  * Returns the cell at the given \a row and \a column. If there
0591  * is no cell at the specified position, the function returns 0.
0592  */
0593 Cell *Worksheet::cellAt(int row, int col) const
0594 {
0595     Q_D(const Worksheet);
0596     auto it = d->cellTable.constFind(row);
0597     if (it == d->cellTable.constEnd())
0598         return nullptr;
0599     if (!it->contains(col))
0600         return nullptr;
0601 
0602     return (*it)[col].get();
0603 }
0604 
0605 Format WorksheetPrivate::cellFormat(int row, int col) const
0606 {
0607     auto it = cellTable.constFind(row);
0608     if (it == cellTable.constEnd())
0609         return Format();
0610     if (!it->contains(col))
0611         return Format();
0612     return (*it)[col]->format();
0613 }
0614 
0615 /*!
0616   \overload
0617   Write string \a value to the cell \a row_column with the \a format.
0618 
0619   Returns true on success.
0620  */
0621 bool Worksheet::writeString(const CellReference &row_column, const RichString &value, const Format &format)
0622 {
0623     if (!row_column.isValid())
0624         return false;
0625 
0626     return writeString(row_column.row(), row_column.column(), value, format);
0627 }
0628 
0629 /*!
0630   Write string \a value to the cell (\a row, \a column) with the \a format.
0631   Returns true on success.
0632 */
0633 bool Worksheet::writeString(int row, int column, const RichString &value, const Format &format)
0634 {
0635     Q_D(Worksheet);
0636 //    QString content = value.toPlainString();
0637     if (d->checkDimensions(row, column))
0638         return false;
0639 
0640 //    if (content.size() > d->xls_strmax) {
0641 //        content = content.left(d->xls_strmax);
0642 //        error = -2;
0643 //    }
0644 
0645     d->sharedStrings()->addSharedString(value);
0646     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
0647     if (value.fragmentCount() == 1 && value.fragmentFormat(0).isValid())
0648         fmt.mergeFormat(value.fragmentFormat(0));
0649     d->workbook->styles()->addXfFormat(fmt);
0650     auto cell = std::make_shared<Cell>(value.toPlainString(), Cell::SharedStringType, fmt, this);
0651     cell->d_ptr->richString = value;
0652     d->cellTable[row][column] = cell;
0653     return true;
0654 }
0655 
0656 /*!
0657     \overload
0658     Write string \a value to the cell \a row_column with the \a format.
0659  */
0660 bool Worksheet::writeString(const CellReference &row_column, const QString &value, const Format &format)
0661 {
0662     if (!row_column.isValid())
0663         return false;
0664 
0665     return writeString(row_column.row(), row_column.column(), value, format);
0666 }
0667 
0668 /*!
0669     \overload
0670 
0671     Write string \a value to the cell (\a row, \a column) with the \a format.
0672     Returns true on success.
0673 */
0674 bool Worksheet::writeString(int row, int column, const QString &value, const Format &format)
0675 {
0676     Q_D(Worksheet);
0677     if (d->checkDimensions(row, column))
0678         return false;
0679 
0680     RichString rs;
0681     if (d->workbook->isHtmlToRichStringEnabled() && Qt::mightBeRichText(value))
0682         rs.setHtml(value);
0683     else
0684         rs.addFragment(value, Format());
0685 
0686     return writeString(row, column, rs, format);
0687 }
0688 
0689 /*!
0690     \overload
0691     Write string \a value to the cell \a row_column with the \a format
0692  */
0693 bool Worksheet::writeInlineString(const CellReference &row_column, const QString &value, const Format &format)
0694 {
0695     if (!row_column.isValid())
0696         return false;
0697 
0698     return writeInlineString(row_column.row(), row_column.column(), value, format);
0699 }
0700 
0701 /*!
0702     Write string \a value to the cell (\a row, \a column) with the \a format.
0703     Returns true on success.
0704 */
0705 bool Worksheet::writeInlineString(int row, int column, const QString &value, const Format &format)
0706 {
0707     Q_D(Worksheet);
0708     //int error = 0;
0709     QString content = value;
0710     if (d->checkDimensions(row, column))
0711         return false;
0712 
0713     if (value.size() > XLSX_STRING_MAX) {
0714         content = value.left(XLSX_STRING_MAX);
0715         //error = -2;
0716     }
0717 
0718     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
0719     d->workbook->styles()->addXfFormat(fmt);
0720     d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::InlineStringType, fmt, this);
0721     return true;
0722 }
0723 
0724 /*!
0725     \overload
0726     Write numeric \a value to the cell \a row_column with the \a format.
0727     Returns true on success.
0728  */
0729 bool Worksheet::writeNumeric(const CellReference &row_column, double value, const Format &format)
0730 {
0731     if (!row_column.isValid())
0732         return false;
0733 
0734     return writeNumeric(row_column.row(), row_column.column(), value, format);
0735 }
0736 
0737 /*!
0738     Write numeric \a value to the cell (\a row, \a column) with the \a format.
0739     Returns true on success.
0740 */
0741 bool Worksheet::writeNumeric(int row, int column, double value, const Format &format)
0742 {
0743     Q_D(Worksheet);
0744     if (d->checkDimensions(row, column))
0745         return false;
0746 
0747     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
0748     d->workbook->styles()->addXfFormat(fmt);
0749     d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::NumberType, fmt, this);
0750     return true;
0751 }
0752 
0753 
0754 /*!
0755     \overload
0756     Write \a formula to the cell \a row_column with the \a format and \a result.
0757     Returns true on success.
0758  */
0759 bool Worksheet::writeFormula(const CellReference &row_column, const CellFormula &formula, const Format &format, double result)
0760 {
0761     if (!row_column.isValid())
0762         return false;
0763 
0764     return writeFormula(row_column.row(), row_column.column(), formula, format, result);
0765 }
0766 
0767 /*!
0768     Write \a formula_ to the cell (\a row, \a column) with the \a format and \a result.
0769     Returns true on success.
0770 */
0771 bool Worksheet::writeFormula(int row, int column, const CellFormula &formula_, const Format &format, double result)
0772 {
0773     Q_D(Worksheet);
0774 
0775     if (d->checkDimensions(row, column))
0776         return false;
0777 
0778     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
0779     d->workbook->styles()->addXfFormat(fmt);
0780 
0781     CellFormula formula = formula_;
0782     formula.d->ca = true;
0783     if (formula.formulaType() == CellFormula::SharedType)
0784     {
0785         //Assign proper shared index for shared formula
0786         int si = 0;
0787         while ( d->sharedFormulaMap.contains(si) )
0788         {
0789             ++si;
0790         }
0791         formula.d->si = si;
0792         d->sharedFormulaMap[si] = formula;
0793     }
0794 
0795     auto data = std::make_shared<Cell>(result, Cell::NumberType, fmt, this);
0796     data->d_ptr->formula = formula;
0797     d->cellTable[row][column] = data;
0798 
0799     CellRange range = formula.reference();
0800     if (formula.formulaType() == CellFormula::SharedType) {
0801         CellFormula sf(QString(), CellFormula::SharedType);
0802         sf.d->si = formula.sharedIndex();
0803         for (int r=range.firstRow(); r<=range.lastRow(); ++r) {
0804             for (int c=range.firstColumn(); c<=range.lastColumn(); ++c) {
0805                 if (!(r==row && c==column)) {
0806                     if(Cell *cell = cellAt(r, c)) {
0807                         cell->d_ptr->formula = sf;
0808                     } else {
0809                         auto newCell = std::make_shared<Cell>(result, Cell::NumberType, fmt, this);
0810                         newCell->d_ptr->formula = sf;
0811                         d->cellTable[r][c] = newCell;
0812                     }
0813                 }
0814             }
0815         }
0816     }
0817 
0818     return true;
0819 }
0820 
0821 /*!
0822     \overload
0823     Write a empty cell \a row_column with the \a format.
0824     Returns true on success.
0825  */
0826 bool Worksheet::writeBlank(const CellReference &row_column, const Format &format)
0827 {
0828     if (!row_column.isValid())
0829         return false;
0830 
0831     return writeBlank(row_column.row(), row_column.column(), format);
0832 }
0833 
0834 /*!
0835     Write a empty cell (\a row, \a column) with the \a format.
0836     Returns true on success.
0837  */
0838 bool Worksheet::writeBlank(int row, int column, const Format &format)
0839 {
0840     Q_D(Worksheet);
0841     if (d->checkDimensions(row, column))
0842         return false;
0843 
0844     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
0845     d->workbook->styles()->addXfFormat(fmt);
0846 
0847     //Note: NumberType with an invalid QVariant value means blank.
0848     d->cellTable[row][column] = std::make_shared<Cell>(QVariant{}, Cell::NumberType, fmt, this);
0849 
0850     return true;
0851 }
0852 /*!
0853     \overload
0854     Write a bool \a value to the cell \a row_column with the \a format.
0855     Returns true on success.
0856  */
0857 bool Worksheet::writeBool(const CellReference &row_column, bool value, const Format &format)
0858 {
0859     if (!row_column.isValid())
0860         return false;
0861 
0862     return writeBool(row_column.row(), row_column.column(), value, format);
0863 }
0864 
0865 /*!
0866     Write a bool \a value to the cell (\a row, \a column) with the \a format.
0867     Returns true on success.
0868  */
0869 bool Worksheet::writeBool(int row, int column, bool value, const Format &format)
0870 {
0871     Q_D(Worksheet);
0872     if (d->checkDimensions(row, column))
0873         return false;
0874 
0875     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
0876     d->workbook->styles()->addXfFormat(fmt);
0877     d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::BooleanType, fmt, this);
0878 
0879     return true;
0880 }
0881 /*!
0882     \overload
0883     Write a QDateTime \a dt to the cell \a row_column with the \a format.
0884     Returns true on success.
0885  */
0886 bool Worksheet::writeDateTime(const CellReference &row_column, const QDateTime &dt, const Format &format)
0887 {
0888     if (!row_column.isValid())
0889         return false;
0890 
0891     return writeDateTime(row_column.row(), row_column.column(), dt, format);
0892 }
0893 
0894 /*!
0895     Write a QDateTime \a dt to the cell (\a row, \a column) with the \a format.
0896     Returns true on success.
0897  */
0898 bool Worksheet::writeDateTime(int row, int column, const QDateTime &dt, const Format &format)
0899 {
0900     Q_D(Worksheet);
0901     if (d->checkDimensions(row, column))
0902         return false;
0903 
0904     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
0905     if (!fmt.isValid() || !fmt.isDateTimeFormat())
0906         fmt.setNumberFormat(d->workbook->defaultDateFormat());
0907     d->workbook->styles()->addXfFormat(fmt);
0908 
0909     double value = datetimeToNumber(dt, d->workbook->isDate1904());
0910 
0911     d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::NumberType, fmt, this);
0912 
0913     return true;
0914 }
0915 
0916 // dev67
0917 bool Worksheet::writeDate(const CellReference &row_column, const QDate &dt, const Format &format)
0918 {
0919     if (!row_column.isValid())
0920         return false;
0921 
0922     return writeDate(row_column.row(), row_column.column(), dt, format);
0923 }
0924 
0925 // dev67
0926 bool Worksheet::writeDate(int row, int column, const QDate &dt, const Format &format)
0927 {
0928     Q_D(Worksheet);
0929     if (d->checkDimensions(row, column))
0930         return false;
0931 
0932     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
0933 
0934     if (!fmt.isValid() || !fmt.isDateTimeFormat())
0935         fmt.setNumberFormat(d->workbook->defaultDateFormat());
0936 
0937     d->workbook->styles()->addXfFormat(fmt);
0938 
0939     double value = datetimeToNumber(QDateTime(dt, QTime(0,0,0)), d->workbook->isDate1904());
0940 
0941     d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::NumberType, fmt, this);
0942 
0943     return true;
0944 }
0945 
0946 /*!
0947     \overload
0948     Write a QTime \a t to the cell \a row_column with the \a format.
0949     Returns true on success.
0950  */
0951 bool Worksheet::writeTime(const CellReference &row_column, const QTime &t, const Format &format)
0952 {
0953     if (!row_column.isValid())
0954         return false;
0955 
0956     return writeTime(row_column.row(), row_column.column(), t, format);
0957 }
0958 
0959 /*!
0960     Write a QTime \a t to the cell (\a row, \a column) with the \a format.
0961     Returns true on success.
0962  */
0963 bool Worksheet::writeTime(int row, int column, const QTime &t, const Format &format)
0964 {
0965     Q_D(Worksheet);
0966     if (d->checkDimensions(row, column))
0967         return false;
0968 
0969     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
0970     if (!fmt.isValid() || !fmt.isDateTimeFormat())
0971         fmt.setNumberFormat(QStringLiteral("hh:mm:ss"));
0972     d->workbook->styles()->addXfFormat(fmt);
0973 
0974     d->cellTable[row][column] = std::make_shared<Cell>(timeToNumber(t), Cell::NumberType, fmt, this);
0975 
0976     return true;
0977 }
0978 
0979 /*!
0980     \overload
0981     Write a QUrl \a url to the cell \a row_column with the given \a format \a display and \a tip.
0982     Returns true on success.
0983  */
0984 bool Worksheet::writeHyperlink(const CellReference &row_column, const QUrl &url, const Format &format, const QString &display, const QString &tip)
0985 {
0986     if (!row_column.isValid())
0987         return false;
0988 
0989     return writeHyperlink(row_column.row(), row_column.column(), url, format, display, tip);
0990 }
0991 
0992 /*!
0993     Write a QUrl \a url to the cell (\a row, \a column) with the given \a format \a display and \a tip.
0994     Returns true on success.
0995  */
0996 bool Worksheet::writeHyperlink(int row, int column, const QUrl &url, const Format &format, const QString &display, const QString &tip)
0997 {
0998     Q_D(Worksheet);
0999     if (d->checkDimensions(row, column))
1000         return false;
1001 
1002     //int error = 0;
1003 
1004     QString urlString = url.toString();
1005 
1006     //Generate proper display string
1007     QString displayString = display.isEmpty() ? urlString : display;
1008     if (displayString.startsWith(QLatin1String("mailto:")))
1009         displayString.replace(QLatin1String("mailto:"), QString());
1010     if (displayString.size() > XLSX_STRING_MAX) {
1011         displayString = displayString.left(XLSX_STRING_MAX);
1012         //error = -2;
1013     }
1014 
1015     /*
1016       Location within target. If target is a workbook (or this workbook)
1017       this shall refer to a sheet and cell or a defined name. Can also
1018       be an HTML anchor if target is HTML file.
1019 
1020       c:\temp\file.xlsx#Sheet!A1
1021       http://a.com/aaa.html#aaaaa
1022     */
1023     QString locationString;
1024     if (url.hasFragment()) {
1025         locationString = url.fragment();
1026         urlString = url.toString(QUrl::RemoveFragment);
1027     }
1028 
1029     Format fmt = format.isValid() ? format : d->cellFormat(row, column);
1030     //Given a default style for hyperlink
1031     if (!fmt.isValid()) {
1032         fmt.setVerticalAlignment(Format::AlignVCenter);
1033         fmt.setFontColor(Qt::blue);
1034         fmt.setFontUnderline(Format::FontUnderlineSingle);
1035     }
1036     d->workbook->styles()->addXfFormat(fmt);
1037 
1038     //Write the hyperlink string as normal string.
1039     d->sharedStrings()->addSharedString(displayString);
1040     d->cellTable[row][column] = std::make_shared<Cell>(displayString, Cell::SharedStringType, fmt, this);
1041 
1042     //Store the hyperlink data in a separate table
1043     d->urlTable[row][column] = QSharedPointer<XlsxHyperlinkData>(new XlsxHyperlinkData(XlsxHyperlinkData::External, urlString, locationString, QString(), tip));
1044 
1045     return true;
1046 }
1047 
1048 /*!
1049  * Add one DataValidation \a validation to the sheet.
1050  * Returns true on success.
1051  */
1052 bool Worksheet::addDataValidation(const DataValidation &validation)
1053 {
1054     Q_D(Worksheet);
1055     if (validation.ranges().isEmpty() || validation.validationType()==DataValidation::None)
1056         return false;
1057 
1058     d->dataValidationsList.append(validation);
1059     return true;
1060 }
1061 
1062 /*!
1063  * Add one ConditionalFormatting \a cf to the sheet.
1064  * Returns true on success.
1065  */
1066 bool Worksheet::addConditionalFormatting(const ConditionalFormatting &cf)
1067 {
1068     Q_D(Worksheet);
1069     if (cf.ranges().isEmpty())
1070         return false;
1071 
1072     for (int i=0; i<cf.d->cfRules.size(); ++i) {
1073         const std::shared_ptr<XlsxCfRuleData> &rule = cf.d->cfRules[i];
1074         if (!rule->dxfFormat.isEmpty())
1075             d->workbook->styles()->addDxfFormat(rule->dxfFormat);
1076         rule->priority = 1;
1077     }
1078     d->conditionalFormattingList.append(cf);
1079     return true;
1080 }
1081 
1082 /*!
1083  * Insert an \a image  at the position \a row, \a column
1084  * Returns true on success.
1085  */
1086 int Worksheet::insertImage(int row, int column, const QImage &image)
1087 {
1088     Q_D(Worksheet);
1089 
1090     int imageIndex = 0;
1091 
1092     if (image.isNull())
1093         return imageIndex;
1094 
1095     if (!d->drawing)
1096     {
1097         d->drawing = std::make_shared<Drawing>(this, F_NewFromScratch);
1098     }
1099 
1100     DrawingOneCellAnchor* anchor = new DrawingOneCellAnchor(d->drawing.get(), DrawingAnchor::Picture);
1101 
1102     /*
1103         The size are expressed as English Metric Units (EMUs).
1104         EMU is 1/360 000 of centimiter.
1105     */
1106     anchor->from = XlsxMarker(row, column, 0, 0);
1107     float scaleX = 36e6f / std::max(1,image.dotsPerMeterX());
1108     float scaleY = 36e6f / std::max(1,image.dotsPerMeterY());
1109     anchor->ext = QSize( int(image.width() * scaleX), int(image.height() * scaleY) );
1110 
1111     anchor->setObjectPicture(image);
1112 
1113     imageIndex = anchor->getm_id();
1114 
1115     return imageIndex;
1116 }
1117 
1118 bool Worksheet::getImage(int imageIndex, QImage& img)
1119 {
1120     Q_D(Worksheet);
1121 
1122     if( imageIndex <= (-1) )
1123     {
1124         return false;
1125     }
1126 
1127     if ( d->drawing == nullptr )
1128     {
1129         return false;
1130     }
1131 
1132     int realImageIndex = imageIndex - 1; // minus one
1133 
1134    DrawingAnchor* danchor = d->drawing->anchors.at( realImageIndex );
1135    // QSharedPointer<Drawing> // for multithread
1136    if ( danchor == nullptr )
1137    {
1138        return false;
1139    }
1140 
1141    bool ret= danchor->getObjectPicture(img);
1142    return ret;
1143 }
1144 
1145 bool Worksheet::getImage(int row, int column, QImage &img)
1146 {
1147     Q_D(Worksheet);
1148 
1149     if ( d->drawing == nullptr )
1150     {
1151         return false;
1152     }
1153 
1154     for(int i = 0; i < d->drawing->anchors.size(); i++)
1155     {
1156         if(d->drawing->anchors[i]->row() == row && d->drawing->anchors[i]->col() == column)
1157         {
1158             DrawingAnchor* danchor = d->drawing->anchors.at( i );
1159 
1160             if ( danchor == nullptr )
1161             {
1162                 return false;
1163             }
1164 
1165             bool ret= danchor->getObjectPicture(img);
1166             return ret;
1167         }
1168     }
1169     return false;
1170 }
1171 
1172 uint Worksheet::getImageCount()
1173 {
1174     Q_D(Worksheet);
1175 
1176     if ( d->drawing == nullptr )
1177     {
1178         return false;
1179     }
1180 
1181     int size = d->drawing->anchors.size();
1182     return uint(size);
1183 }
1184 
1185 
1186 
1187 /*!
1188  * Creates an chart with the given \a size and insert
1189  * at the position \a row, \a column.
1190  * The chart will be returned.
1191  */
1192 Chart *Worksheet::insertChart(int row, int column, const QSize &size)
1193 {
1194     Q_D(Worksheet);
1195 
1196     if (!d->drawing)
1197         d->drawing = std::make_shared<Drawing>(this, F_NewFromScratch);
1198 
1199     DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing.get(), DrawingAnchor::Picture);
1200 
1201     /*
1202         The size are expressed as English Metric Units (EMUs). There are
1203         12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per
1204         pixel
1205     */
1206     anchor->from = XlsxMarker(row, column, 0, 0);
1207     anchor->ext = size * 9525;
1208 
1209     QSharedPointer<Chart> chart = QSharedPointer<Chart>(new Chart(this, F_NewFromScratch));
1210     anchor->setObjectGraphicFrame(chart);
1211 
1212     return chart.data();
1213 }
1214 
1215 /*!
1216     Merge a \a range of cells. The first cell should contain the data and the others should
1217     be blank. All cells will be applied the same style if a valid \a format is given.
1218     Returns true on success.
1219 
1220     \note All cells except the top-left one will be cleared.
1221  */
1222 bool Worksheet::mergeCells(const CellRange &range, const Format &format)
1223 {
1224     Q_D(Worksheet);
1225     if (range.rowCount() < 2 && range.columnCount() < 2)
1226         return false;
1227 
1228     if (d->checkDimensions(range.firstRow(), range.firstColumn()))
1229         return false;
1230 
1231     if (format.isValid())
1232     {
1233         d->workbook->styles()->addXfFormat(format);
1234     }
1235 
1236     for (int row = range.firstRow(); row <= range.lastRow(); ++row)
1237     {
1238         for (int col = range.firstColumn(); col <= range.lastColumn(); ++col)
1239         {
1240             if (row == range.firstRow() && col == range.firstColumn())
1241             {
1242                 Cell *cell = cellAt(row, col);
1243                 if (cell)
1244                 {
1245                     if (format.isValid())
1246                         cell->d_ptr->format = format;
1247                 }
1248                 else
1249                 {
1250                     writeBlank(row, col, format);
1251                 }
1252             }
1253             else
1254             {
1255                 writeBlank(row, col, format);
1256             }
1257         }
1258     }
1259 
1260     d->merges.append(range);
1261     return true;
1262 }
1263 
1264 /*!
1265     Unmerge the cells in the \a range. Returns true on success.
1266 
1267 */
1268 bool Worksheet::unmergeCells(const CellRange &range)
1269 {
1270     Q_D(Worksheet);
1271     return d->merges.removeOne(range);
1272 }
1273 
1274 /*!
1275   Returns all the merged cells.
1276 */
1277 QList<CellRange> Worksheet::mergedCells() const
1278 {
1279     Q_D(const Worksheet);
1280 
1281     // dev57
1282 
1283     QList<CellRange> emptyList;
1284 
1285     if ( d->type == AbstractSheet::ST_WorkSheet )
1286     {
1287         return d->merges;
1288     }
1289     else if ( d->type == AbstractSheet::ST_ChartSheet )
1290     {
1291     }
1292     else if ( d->type == AbstractSheet::ST_DialogSheet )
1293     {
1294     }
1295     else if ( d->type == AbstractSheet::ST_MacroSheet )
1296     {
1297     }
1298     else
1299     { // undefined
1300     }
1301 
1302     return emptyList;
1303 }
1304 
1305 /*!
1306  * \internal
1307  */
1308 void Worksheet::saveToXmlFile(QIODevice *device) const
1309 {
1310     Q_D(const Worksheet);
1311     d->relationships->clear();
1312 
1313     QXmlStreamWriter writer(device);
1314 
1315     writer.writeStartDocument(QStringLiteral("1.0"), true);
1316     writer.writeStartElement(QStringLiteral("worksheet"));
1317     writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
1318     writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
1319 
1320     //for Excel 2010
1321     //    writer.writeAttribute("xmlns:mc", "http://schemas.openxmlformats.org/markup-compatibility/2006");
1322     //    writer.writeAttribute("xmlns:x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
1323     //    writer.writeAttribute("mc:Ignorable", "x14ac");
1324 
1325     writer.writeStartElement(QStringLiteral("dimension"));
1326     writer.writeAttribute(QStringLiteral("ref"), d->generateDimensionString());
1327     writer.writeEndElement();//dimension
1328 
1329     writer.writeStartElement(QStringLiteral("sheetViews"));
1330     writer.writeStartElement(QStringLiteral("sheetView"));
1331     if (d->windowProtection)
1332         writer.writeAttribute(QStringLiteral("windowProtection"), QStringLiteral("1"));
1333     if (d->showFormulas)
1334         writer.writeAttribute(QStringLiteral("showFormulas"), QStringLiteral("1"));
1335     if (!d->showGridLines)
1336         writer.writeAttribute(QStringLiteral("showGridLines"), QStringLiteral("0"));
1337     if (!d->showRowColHeaders)
1338         writer.writeAttribute(QStringLiteral("showRowColHeaders"), QStringLiteral("0"));
1339     if (!d->showZeros)
1340         writer.writeAttribute(QStringLiteral("showZeros"), QStringLiteral("0"));
1341     if (d->rightToLeft)
1342         writer.writeAttribute(QStringLiteral("rightToLeft"), QStringLiteral("1"));
1343     if (d->tabSelected)
1344         writer.writeAttribute(QStringLiteral("tabSelected"), QStringLiteral("1"));
1345     if (!d->showRuler)
1346         writer.writeAttribute(QStringLiteral("showRuler"), QStringLiteral("0"));
1347     if (!d->showOutlineSymbols)
1348         writer.writeAttribute(QStringLiteral("showOutlineSymbols"), QStringLiteral("0"));
1349     if (!d->showWhiteSpace)
1350         writer.writeAttribute(QStringLiteral("showWhiteSpace"), QStringLiteral("0"));
1351     writer.writeAttribute(QStringLiteral("workbookViewId"), QStringLiteral("0"));
1352     writer.writeEndElement();//sheetView
1353     writer.writeEndElement();//sheetViews
1354 
1355     writer.writeStartElement(QStringLiteral("sheetFormatPr"));
1356     writer.writeAttribute(QStringLiteral("defaultRowHeight"), QString::number(d->default_row_height));
1357     if (d->default_row_height != 15)
1358         writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1"));
1359     if (d->default_row_zeroed)
1360         writer.writeAttribute(QStringLiteral("zeroHeight"), QStringLiteral("1"));
1361     if (d->outline_row_level)
1362         writer.writeAttribute(QStringLiteral("outlineLevelRow"), QString::number(d->outline_row_level));
1363     if (d->outline_col_level)
1364         writer.writeAttribute(QStringLiteral("outlineLevelCol"), QString::number(d->outline_col_level));
1365     //for Excel 2010
1366     //    writer.writeAttribute("x14ac:dyDescent", "0.25");
1367     writer.writeEndElement();//sheetFormatPr
1368 
1369     if (!d->colsInfo.isEmpty())
1370     {
1371         writer.writeStartElement(QStringLiteral("cols"));
1372         QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(d->colsInfo);
1373         while (it.hasNext())
1374         {
1375             it.next();
1376             QSharedPointer<XlsxColumnInfo> col_info = it.value();
1377             writer.writeStartElement(QStringLiteral("col"));
1378             writer.writeAttribute(QStringLiteral("min"), QString::number(col_info->firstColumn));
1379             writer.writeAttribute(QStringLiteral("max"), QString::number(col_info->lastColumn));
1380             if (col_info->width)
1381                 writer.writeAttribute(QStringLiteral("width"), QString::number(col_info->width, 'g', 15));
1382             if (!col_info->format.isEmpty())
1383                 writer.writeAttribute(QStringLiteral("style"), QString::number(col_info->format.xfIndex()));
1384             if (col_info->hidden)
1385                 writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1"));
1386             if (col_info->width)
1387                 writer.writeAttribute(QStringLiteral("customWidth"), QStringLiteral("1"));
1388             if (col_info->outlineLevel)
1389                 writer.writeAttribute(QStringLiteral("outlineLevel"), QString::number(col_info->outlineLevel));
1390             if (col_info->collapsed)
1391                 writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1"));
1392             writer.writeEndElement();//col
1393         }
1394         writer.writeEndElement();//cols
1395     }
1396 
1397     writer.writeStartElement(QStringLiteral("sheetData"));
1398     if (d->dimension.isValid())
1399         d->saveXmlSheetData(writer);
1400     writer.writeEndElement();//sheetData
1401 
1402     d->saveXmlMergeCells(writer);
1403     for (const ConditionalFormatting &cf : d->conditionalFormattingList)
1404         cf.saveToXml(writer);
1405     d->saveXmlDataValidations(writer);
1406 
1407     //{{ liufeijin :  write  pagesettings  add by liufeijin 20181028
1408 
1409     // fixed by j2doll [dev18]
1410     // NOTE: empty element is not problem. but, empty structure of element is not parsed by Excel.
1411 
1412     // pageMargins
1413     if ( false == d->PMleft.isEmpty() &&
1414          false == d->PMright.isEmpty() &&
1415          false == d->PMtop.isEmpty() &&
1416          false == d->PMbotton.isEmpty() &&
1417          false == d->PMheader.isEmpty() &&
1418          false == d->PMfooter.isEmpty()
1419          )
1420     {
1421         writer.writeStartElement(QStringLiteral("pageMargins"));
1422 
1423         writer.writeAttribute(QStringLiteral("left"),   d->PMleft );
1424         writer.writeAttribute(QStringLiteral("right"),  d->PMright );
1425         writer.writeAttribute(QStringLiteral("top"),    d->PMtop );
1426         writer.writeAttribute(QStringLiteral("bottom"), d->PMbotton );
1427         writer.writeAttribute(QStringLiteral("header"), d->PMheader );
1428         writer.writeAttribute(QStringLiteral("footer"), d->PMfooter );
1429 
1430         writer.writeEndElement(); // pageMargins
1431     }
1432 
1433     // dev57
1434     if ( !d->Prid.isEmpty() )
1435     {
1436         writer.writeStartElement(QStringLiteral("pageSetup")); // pageSetup
1437 
1438         writer.writeAttribute(QStringLiteral("r:id"), d->Prid);
1439 
1440         if ( !d->PverticalDpi.isEmpty() )
1441         {
1442             writer.writeAttribute(QStringLiteral("verticalDpi"), d->PverticalDpi);
1443         }
1444 
1445         if ( !d->PhorizontalDpi.isEmpty() )
1446         {
1447             writer.writeAttribute(QStringLiteral("horizontalDpi"), d->PhorizontalDpi);
1448         }
1449 
1450         if ( !d->PuseFirstPageNumber.isEmpty() )
1451         {
1452             writer.writeAttribute(QStringLiteral("useFirstPageNumber"), d->PuseFirstPageNumber);
1453         }
1454 
1455         if ( !d->PfirstPageNumber.isEmpty() )
1456         {
1457             writer.writeAttribute(QStringLiteral("firstPageNumber"), d->PfirstPageNumber);
1458         }
1459 
1460         if ( !d->Pscale.isEmpty() )
1461         {
1462             writer.writeAttribute(QStringLiteral("scale"), d->Pscale);
1463         }
1464 
1465         if ( !d->PpaperSize.isEmpty() )
1466         {
1467             writer.writeAttribute(QStringLiteral("paperSize"), d->PpaperSize);
1468         }
1469 
1470         if ( !d->Porientation.isEmpty() )
1471         {
1472             writer.writeAttribute(QStringLiteral("orientation"), d->Porientation);
1473         }
1474 
1475         if(!d->Pcopies.isEmpty())
1476         {
1477             writer.writeAttribute(QStringLiteral("copies"), d->Pcopies);
1478         }
1479 
1480         writer.writeEndElement(); // pageSetup
1481 
1482     } // if ( !d->Prid.isEmpty() )
1483 
1484     // headerFooter
1485     if( !(d->ModdHeader.isNull()) ||
1486         !(d->MoodFooter.isNull()) )
1487     {
1488         writer.writeStartElement(QStringLiteral("headerFooter")); // headerFooter
1489 
1490         if ( !d->MoodalignWithMargins.isEmpty() )
1491         {
1492             writer.writeAttribute(QStringLiteral("alignWithMargins"), d->MoodalignWithMargins);
1493         }
1494 
1495         if ( !d->ModdHeader.isNull() )
1496         {
1497             writer.writeStartElement(QStringLiteral("oddHeader"));
1498             writer.writeCharacters(d->ModdHeader);
1499             writer.writeEndElement(); // oddHeader
1500         }
1501 
1502         if ( !d->MoodFooter.isNull() )
1503         {
1504             writer.writeTextElement(QStringLiteral("oddFooter"), d->MoodFooter);
1505         }
1506 
1507         writer.writeEndElement(); // headerFooter
1508     }
1509 
1510     d->saveXmlHyperlinks(writer);
1511     d->saveXmlDrawings(writer);
1512 
1513     writer.writeEndElement(); // worksheet
1514     writer.writeEndDocument();
1515 }
1516 
1517 //{{ liufeijin
1518 bool Worksheet::setStartPage(int spagen)
1519 {
1520     Q_D(Worksheet);
1521 
1522     d->PfirstPageNumber=QString::number(spagen);
1523 
1524     return true;
1525 }
1526 //}}
1527 
1528 void WorksheetPrivate::saveXmlSheetData(QXmlStreamWriter &writer) const
1529 {
1530     calculateSpans();
1531     for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++)
1532     {
1533         auto ctIt = cellTable.constFind(row_num);
1534         auto riIt = rowsInfo.constFind(row_num);
1535         if (ctIt == cellTable.constEnd() && riIt == rowsInfo.constEnd() && !comments.contains(row_num))
1536         {
1537             //Only process rows with cell data / comments / formatting
1538             continue;
1539         }
1540 
1541         int span_index = (row_num-1) / 16;
1542         QString span;
1543         auto rsIt = row_spans.constFind(span_index);
1544         if (rsIt != row_spans.constEnd())
1545             span = rsIt.value();
1546 
1547         writer.writeStartElement(QStringLiteral("row"));
1548         writer.writeAttribute(QStringLiteral("r"), QString::number(row_num));
1549 
1550         if (!span.isEmpty())
1551             writer.writeAttribute(QStringLiteral("spans"), span);
1552 
1553         if (riIt != rowsInfo.constEnd())
1554         {
1555             QSharedPointer<XlsxRowInfo> rowInfo = riIt.value();
1556             if (!rowInfo->format.isEmpty())
1557             {
1558                 writer.writeAttribute(QStringLiteral("s"), QString::number(rowInfo->format.xfIndex()));
1559                 writer.writeAttribute(QStringLiteral("customFormat"), QStringLiteral("1"));
1560             }
1561 
1562             //!Todo: support customHeight from info struct
1563             //!Todo: where does this magic number '15' come from?
1564             if (rowInfo->customHeight) {
1565                 writer.writeAttribute(QStringLiteral("ht"), QString::number(rowInfo->height));
1566                 writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1"));
1567             } else {
1568                 writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("0"));
1569             }
1570 
1571             if (rowInfo->hidden)
1572                 writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1"));
1573             if (rowInfo->outlineLevel > 0)
1574                 writer.writeAttribute(QStringLiteral("outlineLevel"), QString::number(rowInfo->outlineLevel));
1575             if (rowInfo->collapsed)
1576                 writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1"));
1577         }
1578 
1579         //Write cell data if row contains filled cells
1580         if (ctIt != cellTable.constEnd())
1581         {
1582             for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++)
1583             {
1584                 if (ctIt->contains(col_num))
1585                 {
1586                     saveXmlCellData(writer, row_num, col_num, (*ctIt)[col_num]);
1587                 }
1588             }
1589         }
1590         writer.writeEndElement(); //row
1591     }
1592 }
1593 
1594 void WorksheetPrivate::saveXmlCellData(QXmlStreamWriter &writer, int row, int col, std::shared_ptr<Cell> cell) const
1595 {
1596     Q_Q(const Worksheet);
1597 
1598     //This is the innermost loop so efficiency is important.
1599     QString cell_pos = CellReference(row, col).toString();
1600 
1601     writer.writeStartElement(QStringLiteral("c"));
1602     writer.writeAttribute(QStringLiteral("r"), cell_pos);
1603 
1604     QMap<int, QSharedPointer<XlsxRowInfo> >::ConstIterator rIt;
1605     QMap<int, QSharedPointer<XlsxColumnInfo> >::ConstIterator cIt;
1606 
1607     //Style used by the cell, row or col
1608     if (!cell->format().isEmpty())
1609         writer.writeAttribute(QStringLiteral("s"), QString::number(cell->format().xfIndex()));
1610     else if ((rIt = rowsInfo.constFind(row)) != rowsInfo.constEnd() && !(*rIt)->format.isEmpty())
1611         writer.writeAttribute(QStringLiteral("s"), QString::number((*rIt)->format.xfIndex()));
1612     else if ((cIt = colsInfoHelper.constFind(col)) != colsInfoHelper.constEnd() && !(*cIt)->format.isEmpty())
1613         writer.writeAttribute(QStringLiteral("s"), QString::number((*cIt)->format.xfIndex()));
1614 
1615     if (cell->cellType() == Cell::SharedStringType) // 's'
1616     {
1617         int sst_idx;
1618         if (cell->isRichString())
1619             sst_idx = sharedStrings()->getSharedStringIndex(cell->d_ptr->richString);
1620         else
1621             sst_idx = sharedStrings()->getSharedStringIndex(cell->value().toString());
1622 
1623         writer.writeAttribute(QStringLiteral("t"), QStringLiteral("s"));
1624         writer.writeTextElement(QStringLiteral("v"), QString::number(sst_idx));
1625     }
1626     else if (cell->cellType() == Cell::InlineStringType) // 'inlineStr'
1627     {
1628         writer.writeAttribute(QStringLiteral("t"), QStringLiteral("inlineStr"));
1629         writer.writeStartElement(QStringLiteral("is"));
1630         if (cell->isRichString())
1631         {
1632             //Rich text string
1633             RichString string = cell->d_ptr->richString;
1634             for (int i=0; i<string.fragmentCount(); ++i)
1635             {
1636                 writer.writeStartElement(QStringLiteral("r"));
1637                 if (string.fragmentFormat(i).hasFontData())
1638                 {
1639                     writer.writeStartElement(QStringLiteral("rPr"));
1640                     //:Todo
1641                     writer.writeEndElement();// rPr
1642                 }
1643                 writer.writeStartElement(QStringLiteral("t"));
1644                 if (isSpaceReserveNeeded(string.fragmentText(i)))
1645                     writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
1646                 writer.writeCharacters(string.fragmentText(i));
1647                 writer.writeEndElement();// t
1648                 writer.writeEndElement(); // r
1649             }
1650         }
1651         else
1652         {
1653             writer.writeStartElement(QStringLiteral("t"));
1654             QString string = cell->value().toString();
1655             if (isSpaceReserveNeeded(string))
1656                 writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
1657             writer.writeCharacters(string);
1658             writer.writeEndElement(); // t
1659         }
1660         writer.writeEndElement();//is
1661     }
1662     else if (cell->cellType() == Cell::NumberType) // 'n'
1663     {
1664         writer.writeAttribute(QStringLiteral("t"), QStringLiteral("n")); // dev67
1665 
1666         if (cell->hasFormula())
1667         {
1668             QString strFormula = cell->formula().d->formula;
1669             Q_UNUSED(strFormula);
1670             cell->formula().saveToXml(writer);
1671         }
1672 
1673         if (cell->value().isValid())
1674         {   //note that, invalid value means 'v' is blank
1675             double value = cell->value().toDouble();
1676             writer.writeTextElement(QStringLiteral("v"), QString::number(value, 'g', 15));
1677         }
1678     }
1679     else if (cell->cellType() == Cell::StringType) // 'str'
1680     {
1681         writer.writeAttribute(QStringLiteral("t"), QStringLiteral("str"));
1682         if (cell->hasFormula())
1683             cell->formula().saveToXml(writer);
1684 
1685         writer.writeTextElement(QStringLiteral("v"), cell->value().toString());
1686     }
1687     else if (cell->cellType() == Cell::BooleanType) // 'b'
1688     {
1689         writer.writeAttribute(QStringLiteral("t"), QStringLiteral("b"));
1690 
1691         // dev34
1692 
1693         if (cell->hasFormula())
1694         {
1695             QString strFormula = cell->formula().d->formula;
1696             Q_UNUSED(strFormula);
1697             cell->formula().saveToXml(writer);
1698         }
1699 
1700         writer.writeTextElement(QStringLiteral("v"), cell->value().toBool() ? QStringLiteral("1") : QStringLiteral("0"));
1701     }
1702     else if (cell->cellType() == Cell::DateType) // 'd'
1703     {
1704         // dev67
1705 
1706          double num = cell->value().toDouble();
1707          bool is1904 = q->workbook()->isDate1904();
1708          if (!is1904 && num > 60) // for mac os excel
1709          {
1710              num = num - 1;
1711          }
1712 
1713          // number type. see for 18.18.11 ST_CellType (Cell Type) more information.
1714          writer.writeAttribute(QStringLiteral("t"), QStringLiteral("n"));
1715          writer.writeTextElement(QStringLiteral("v"), cell->value().toString() );
1716 
1717     }
1718     else if (cell->cellType() == Cell::ErrorType) // 'e'
1719     {
1720         writer.writeAttribute(QStringLiteral("t"), QStringLiteral("e"));
1721         writer.writeTextElement(QStringLiteral("v"), cell->value().toString() );
1722     }
1723     else // if (cell->cellType() == Cell::CustomType)
1724     {
1725         // custom type
1726 
1727         if (cell->hasFormula())
1728         {
1729             QString strFormula = cell->formula().d->formula;
1730             Q_UNUSED(strFormula);
1731             cell->formula().saveToXml(writer);
1732         }
1733 
1734         if (cell->value().isValid())
1735         {   //note that, invalid value means 'v' is blank
1736             double value = cell->value().toDouble();
1737             writer.writeTextElement(QStringLiteral("v"), QString::number(value, 'g', 15));
1738         }
1739     }
1740 
1741     writer.writeEndElement(); // c
1742 }
1743 
1744 void WorksheetPrivate::saveXmlMergeCells(QXmlStreamWriter &writer) const
1745 {
1746     if (merges.isEmpty())
1747         return;
1748 
1749     writer.writeStartElement(QStringLiteral("mergeCells"));
1750     writer.writeAttribute(QStringLiteral("count"), QString::number(merges.size()));
1751 
1752     for (const CellRange &range : merges)
1753     {
1754         writer.writeEmptyElement(QStringLiteral("mergeCell"));
1755         writer.writeAttribute(QStringLiteral("ref"), range.toString());
1756     }
1757 
1758     writer.writeEndElement(); //mergeCells
1759 }
1760 
1761 void WorksheetPrivate::saveXmlDataValidations(QXmlStreamWriter &writer) const
1762 {
1763     if (dataValidationsList.isEmpty())
1764         return;
1765 
1766     writer.writeStartElement(QStringLiteral("dataValidations"));
1767     writer.writeAttribute(QStringLiteral("count"), QString::number(dataValidationsList.size()));
1768 
1769     for (const DataValidation &validation : dataValidationsList)
1770         validation.saveToXml(writer);
1771 
1772     writer.writeEndElement(); //dataValidations
1773 }
1774 
1775 void WorksheetPrivate::saveXmlHyperlinks(QXmlStreamWriter &writer) const
1776 {
1777     if (urlTable.isEmpty())
1778         return;
1779 
1780     writer.writeStartElement(QStringLiteral("hyperlinks"));
1781     QMapIterator<int, QMap< int, QSharedPointer<XlsxHyperlinkData> > > it(urlTable);
1782 
1783     while (it.hasNext())
1784     {
1785         it.next();
1786         int row = it.key();
1787         QMapIterator< int, QSharedPointer<XlsxHyperlinkData> > it2(it.value());
1788 
1789         while (it2.hasNext())
1790         {
1791             it2.next();
1792             int col = it2.key();
1793             QSharedPointer<XlsxHyperlinkData> data = it2.value();
1794             QString ref = CellReference(row, col).toString();
1795 
1796             // dev57
1797             // writer.writeEmptyElement(QStringLiteral("hyperlink"));
1798             writer.writeStartElement(QStringLiteral("hyperlink"));
1799 
1800             writer.writeAttribute(QStringLiteral("ref"), ref); // required field
1801 
1802             if ( data->linkType == XlsxHyperlinkData::External )
1803             {
1804                 // Update relationships
1805                 relationships->addWorksheetRelationship(QStringLiteral("/hyperlink"), data->target, QStringLiteral("External"));
1806 
1807                 writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(relationships->count()));
1808             }
1809 
1810             if (!data->location.isEmpty())
1811             {
1812                 writer.writeAttribute(QStringLiteral("location"), data->location);
1813             }
1814 
1815             if (!data->display.isEmpty())
1816             {
1817                 writer.writeAttribute(QStringLiteral("display"), data->display);
1818             }
1819 
1820             if (!data->tooltip.isEmpty())
1821             {
1822                 writer.writeAttribute(QStringLiteral("tooltip"), data->tooltip);
1823             }
1824 
1825             // dev57
1826             writer.writeEndElement(); // hyperlink
1827         }
1828     }
1829 
1830     writer.writeEndElement(); // hyperlinks
1831 }
1832 
1833 void WorksheetPrivate::saveXmlDrawings(QXmlStreamWriter &writer) const
1834 {
1835     if (!drawing)
1836         return;
1837 
1838     int idx = workbook->drawings().indexOf(drawing.get());
1839     relationships->addWorksheetRelationship(QStringLiteral("/drawing"), QStringLiteral("../drawings/drawing%1.xml").arg(idx+1));
1840 
1841     writer.writeEmptyElement(QStringLiteral("drawing"));
1842     writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(relationships->count()));
1843 }
1844 
1845 void WorksheetPrivate::splitColsInfo(int colFirst, int colLast)
1846 {
1847     // Split current columnInfo, for example, if "A:H" has been set,
1848     // we are trying to set "B:D", there should be "A", "B:D", "E:H".
1849     // This will be more complex if we try to set "C:F" after "B:D".
1850     {
1851         QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo);
1852         while (it.hasNext()) {
1853             it.next();
1854             QSharedPointer<XlsxColumnInfo> info = it.value();
1855             if (colFirst > info->firstColumn && colFirst <= info->lastColumn) {
1856                 //split the range,
1857                 QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info));
1858                 info->lastColumn = colFirst - 1;
1859                 info2->firstColumn = colFirst;
1860                 colsInfo.insert(colFirst, info2);
1861                 for (int c = info2->firstColumn; c <= info2->lastColumn; ++c)
1862                     colsInfoHelper[c] = info2;
1863 
1864                 break;
1865             }
1866         }
1867     }
1868     {
1869         QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo);
1870         while (it.hasNext()) {
1871             it.next();
1872             QSharedPointer<XlsxColumnInfo> info = it.value();
1873             if (colLast >= info->firstColumn && colLast < info->lastColumn) {
1874                 QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info));
1875                 info->lastColumn = colLast;
1876                 info2->firstColumn = colLast + 1;
1877                 colsInfo.insert(colLast + 1, info2);
1878                 for (int c = info2->firstColumn; c <= info2->lastColumn; ++c)
1879                     colsInfoHelper[c] = info2;
1880 
1881                 break;
1882             }
1883         }
1884     }
1885 }
1886 
1887 bool WorksheetPrivate::isColumnRangeValid(int colFirst, int colLast)
1888 {
1889     bool ignore_row = true;
1890     bool ignore_col = false;
1891 
1892     if (colFirst > colLast)
1893         return false;
1894 
1895     if (checkDimensions(1, colLast, ignore_row, ignore_col))
1896         return false;
1897     if (checkDimensions(1, colFirst, ignore_row, ignore_col))
1898         return false;
1899 
1900     return true;
1901 }
1902 
1903 QList<int> WorksheetPrivate ::getColumnIndexes(int colFirst, int colLast)
1904 {
1905     splitColsInfo(colFirst, colLast);
1906 
1907     QList<int> nodes;
1908     nodes.append(colFirst);
1909     for (int col = colFirst; col <= colLast; ++col)
1910     {
1911         auto it = colsInfo.constFind(col);
1912         if (it != colsInfo.constEnd())
1913         {
1914             if (nodes.last() != col)
1915                 nodes.append(col);
1916 
1917             int nextCol = (*it)->lastColumn + 1;
1918             if (nextCol <= colLast)
1919                 nodes.append(nextCol);
1920         }
1921     }
1922 
1923     return nodes;
1924 }
1925 
1926 /*!
1927   Sets width in characters of a \a range of columns to \a width.
1928   Returns true on success.
1929  */
1930 bool Worksheet::setColumnWidth(const CellRange &range, double width)
1931 {
1932     if (!range.isValid())
1933         return false;
1934 
1935     return setColumnWidth(range.firstColumn(), range.lastColumn(), width);
1936 }
1937 
1938 /*!
1939   Sets format property of a \a range of columns to \a format. Columns are 1-indexed.
1940   Returns true on success.
1941  */
1942 bool Worksheet::setColumnFormat(const CellRange& range, const Format &format)
1943 {
1944     if (!range.isValid())
1945         return false;
1946 
1947     return setColumnFormat(range.firstColumn(), range.lastColumn(), format);
1948 }
1949 
1950 /*!
1951   Sets hidden property of a \a range of columns to \a hidden. Columns are 1-indexed.
1952   Hidden columns are not visible.
1953   Returns true on success.
1954  */
1955 bool Worksheet::setColumnHidden(const CellRange &range, bool hidden)
1956 {
1957     if (!range.isValid())
1958         return false;
1959 
1960     return setColumnHidden(range.firstColumn(), range.lastColumn(), hidden);
1961 }
1962 
1963 /*!
1964   Sets width in characters for columns [\a colFirst, \a colLast] to \a width.
1965   Columns are 1-indexed.
1966   Returns true on success.
1967  */
1968 bool Worksheet::setColumnWidth(int colFirst, int colLast, double width)
1969 {
1970     Q_D(Worksheet);
1971 
1972     const QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast);
1973     for (const QSharedPointer<XlsxColumnInfo> &columnInfo : columnInfoList)
1974     {
1975        columnInfo->width = width;
1976     }
1977 
1978     return (columnInfoList.count() > 0);
1979 }
1980 
1981 /*!
1982   Sets format property of a range of columns [\a colFirst, \a colLast] to \a format.
1983   Columns are 1-indexed.
1984   Returns true on success.
1985  */
1986 bool Worksheet::setColumnFormat(int colFirst, int colLast, const Format &format)
1987 {
1988     Q_D(Worksheet);
1989 
1990     const QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast);
1991     for (const QSharedPointer<XlsxColumnInfo> &columnInfo : columnInfoList)
1992        columnInfo->format = format;
1993 
1994     if(columnInfoList.count() > 0) {
1995        d->workbook->styles()->addXfFormat(format);
1996        return true;
1997     }
1998 
1999     return false;
2000 }
2001 
2002 /*!
2003   Sets hidden property of a range of columns [\a colFirst, \a colLast] to \a hidden.
2004   Columns are 1-indexed. Returns true on success.
2005  */
2006 bool Worksheet::setColumnHidden(int colFirst, int colLast, bool hidden)
2007 {
2008     Q_D(Worksheet);
2009 
2010     const QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast);
2011     for (const QSharedPointer<XlsxColumnInfo> &columnInfo : columnInfoList)
2012        columnInfo->hidden = hidden;
2013 
2014     return (columnInfoList.count() > 0);
2015 }
2016 
2017 /*!
2018   Returns width of the \a column in characters of the normal font. Columns are 1-indexed.
2019  */
2020 double Worksheet::columnWidth(int column)
2021 {
2022     Q_D(Worksheet);
2023 
2024     QList< QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column);
2025 
2026     // [dev54]
2027     if ( columnInfoList.size() == 0 )
2028     {
2029         // column information is not found
2030         // qDebug() << "[debug]" << __FUNCTION__ <<  "column (info) is not found. " << column;
2031     }
2032 
2033     if (columnInfoList.count() == 1)
2034     {
2035         // column information is found
2036         // qDebug() << "[debug]" << __FUNCTION__ <<  "column (info) is found. " << column << oneColWidth;
2037         double oneColWidth = columnInfoList.at(0)->width;
2038         bool isSetWidth = columnInfoList.at(0)->isSetWidth;
2039         if ( isSetWidth )
2040         {
2041             return oneColWidth;
2042         }
2043     }
2044 
2045     // use default width
2046     double defaultColWidth = d->sheetFormatProps.defaultColWidth;
2047     return defaultColWidth;
2048 }
2049 
2050 /*!
2051   Returns formatting of the \a column. Columns are 1-indexed.
2052  */
2053 Format Worksheet::columnFormat(int column)
2054 {
2055     Q_D(Worksheet);
2056 
2057     QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column);
2058     if (columnInfoList.count() == 1)
2059        return columnInfoList.at(0)->format;
2060 
2061     return Format();
2062 }
2063 
2064 /*!
2065   Returns true if \a column is hidden. Columns are 1-indexed.
2066  */
2067 bool Worksheet::isColumnHidden(int column)
2068 {
2069     Q_D(Worksheet);
2070 
2071     QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column);
2072     if (columnInfoList.count() == 1)
2073        return columnInfoList.at(0)->hidden;
2074 
2075     return false;
2076 }
2077 
2078 /*!
2079   Sets the \a height of the rows including and between \a rowFirst and \a rowLast.
2080   Row height measured in point size.
2081   Rows are 1-indexed.
2082 
2083   Returns true if success.
2084 */
2085 bool Worksheet::setRowHeight(int rowFirst,int rowLast, double height)
2086 {
2087     Q_D(Worksheet);
2088 
2089     const QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast);
2090     for (const QSharedPointer<XlsxRowInfo> &rowInfo : rowInfoList) {
2091         rowInfo->height = height;
2092         rowInfo->customHeight = true;
2093     }
2094 
2095     return rowInfoList.count() > 0;
2096 }
2097 
2098 /*!
2099   Sets the \a format of the rows including and between \a rowFirst and \a rowLast.
2100   Rows are 1-indexed.
2101 
2102   Returns true if success.
2103 */
2104 bool Worksheet::setRowFormat(int rowFirst,int rowLast, const Format &format)
2105 {
2106     Q_D(Worksheet);
2107 
2108     const QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast);
2109     for (const QSharedPointer<XlsxRowInfo> &rowInfo : rowInfoList)
2110         rowInfo->format = format;
2111 
2112     d->workbook->styles()->addXfFormat(format);
2113     return rowInfoList.count() > 0;
2114 }
2115 
2116 /*!
2117   Sets the \a hidden proeprty of the rows including and between \a rowFirst and \a rowLast.
2118   Rows are 1-indexed. If hidden is true rows will not be visible.
2119 
2120   Returns true if success.
2121 */
2122 bool Worksheet::setRowHidden(int rowFirst,int rowLast, bool hidden)
2123 {
2124     Q_D(Worksheet);
2125 
2126     const QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast);
2127     for (const QSharedPointer<XlsxRowInfo> &rowInfo : rowInfoList)
2128         rowInfo->hidden = hidden;
2129 
2130     return rowInfoList.count() > 0;
2131 }
2132 
2133 /*!
2134  Returns height of \a row in points.
2135 */
2136 double Worksheet::rowHeight(int row)
2137 {
2138     Q_D(Worksheet);
2139     const int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1;
2140 
2141     auto it = d->rowsInfo.constFind(row);
2142     if (d->checkDimensions(row, min_col, false, true) || it == d->rowsInfo.constEnd())
2143     {
2144         return d->sheetFormatProps.defaultRowHeight; //return default on invalid row
2145     }
2146 
2147     return (*it)->height;
2148 }
2149 
2150 /*!
2151  Returns format of \a row.
2152 */
2153 Format Worksheet::rowFormat(int row)
2154 {
2155     Q_D(Worksheet);
2156     const int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1;
2157     auto it = d->rowsInfo.constFind(row);
2158     if (d->checkDimensions(row, min_col, false, true) || it == d->rowsInfo.constEnd())
2159         return Format(); //return default on invalid row
2160 
2161     return (*it)->format;
2162 }
2163 
2164 /*!
2165  Returns true if \a row is hidden.
2166 */
2167 bool Worksheet::isRowHidden(int row)
2168 {
2169     Q_D(Worksheet);
2170     const int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1;
2171     auto it = d->rowsInfo.constFind(row);
2172     if (d->checkDimensions(row, min_col, false, true) || it == d->rowsInfo.constEnd())
2173         return false; //return default on invalid row
2174 
2175     return (*it)->hidden;
2176 }
2177 
2178 /*!
2179    Groups rows from \a rowFirst to \a rowLast with the given \a collapsed.
2180 
2181    Returns false if error occurs.
2182  */
2183 bool Worksheet::groupRows(int rowFirst, int rowLast, bool collapsed)
2184 {
2185     Q_D(Worksheet);
2186 
2187     for (int row=rowFirst; row<=rowLast; ++row) {
2188         auto it = d->rowsInfo.find(row);
2189         if (it != d->rowsInfo.end()) {
2190             (*it)->outlineLevel += 1;
2191         } else {
2192             QSharedPointer<XlsxRowInfo> info(new XlsxRowInfo);
2193             info->outlineLevel += 1;
2194             it = d->rowsInfo.insert(row, info);
2195         }
2196         if (collapsed)
2197             (*it)->hidden = true;
2198     }
2199     if (collapsed) {
2200         auto it = d->rowsInfo.find(rowLast+1);
2201         if (it == d->rowsInfo.end())
2202             it = d->rowsInfo.insert(rowLast+1, QSharedPointer<XlsxRowInfo>(new XlsxRowInfo));
2203         (*it)->collapsed = true;
2204     }
2205     return true;
2206 }
2207 
2208 /*!
2209     \overload
2210 
2211     Groups columns with the given \a range and \a collapsed.
2212  */
2213 bool Worksheet::groupColumns(const CellRange &range, bool collapsed)
2214 {
2215     if (!range.isValid())
2216         return false;
2217 
2218     return groupColumns(range.firstColumn(), range.lastColumn(), collapsed);
2219 }
2220 
2221 /*!
2222    Groups columns from \a colFirst to \a colLast with the given \a collapsed.
2223    Returns false if error occurs.
2224 */
2225 bool Worksheet::groupColumns(int colFirst, int colLast, bool collapsed)
2226 {
2227     Q_D(Worksheet);
2228 
2229     d->splitColsInfo(colFirst, colLast);
2230 
2231     QList<int> nodes;
2232     nodes.append(colFirst);
2233     for (int col = colFirst; col <= colLast; ++col) {
2234         auto it = d->colsInfo.constFind(col);
2235         if (it != d->colsInfo.constEnd()) {
2236             if (nodes.last() != col)
2237                 nodes.append(col);
2238             int nextCol = (*it)->lastColumn + 1;
2239             if (nextCol <= colLast)
2240                 nodes.append(nextCol);
2241         }
2242     }
2243 
2244     for (int idx = 0; idx < nodes.size(); ++idx)
2245     {
2246         int colStart = nodes[idx];
2247         auto it = d->colsInfo.constFind(colStart);
2248         if (it != d->colsInfo.constEnd())
2249         {
2250             (*it)->outlineLevel += 1;
2251             if (collapsed)
2252                 (*it)->hidden = true;
2253         }
2254         else
2255         {
2256             int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1;
2257             QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd, false));
2258             info->outlineLevel += 1;
2259             d->colsInfo.insert(colFirst, info);
2260             if (collapsed)
2261                 info->hidden = true;
2262             for (int c = colStart; c <= colEnd; ++c)
2263                 d->colsInfoHelper[c] = info;
2264         }
2265     }
2266 
2267     if (collapsed) {
2268         int col = colLast+1;
2269         d->splitColsInfo(col, col);
2270         auto it = d->colsInfo.constFind(col);
2271         if (it != d->colsInfo.constEnd())
2272             (*it)->collapsed = true;
2273         else {
2274             QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(col, col, false));
2275             info->collapsed = true;
2276             d->colsInfo.insert(col, info);
2277             d->colsInfoHelper[col] = info;
2278         }
2279     }
2280 
2281     return false;
2282 }
2283 
2284 /*!
2285     Return the range that contains cell data.
2286  */
2287 CellRange Worksheet::dimension() const
2288 {
2289     Q_D(const Worksheet);
2290     return d->dimension;
2291 }
2292 
2293 /*
2294  Convert the height of a cell from user's units to pixels. If the
2295  height hasn't been set by the user we use the default value. If
2296  the row is hidden it has a value of zero.
2297 */
2298 int WorksheetPrivate::rowPixelsSize(int row) const
2299 {
2300     double height;
2301     auto it = row_sizes.constFind(row);
2302     if (it != row_sizes.constEnd())
2303         height = it.value();
2304     else
2305         height = default_row_height;
2306     return static_cast<int>(4.0 / 3.0 *height);
2307 }
2308 
2309 /*
2310  Convert the width of a cell from user's units to pixels. Excel rounds
2311  the column width to the nearest pixel. If the width hasn't been set
2312  by the user we use the default value. If the column is hidden it
2313  has a value of zero.
2314 */
2315 int WorksheetPrivate::colPixelsSize(int col) const
2316 {
2317     double max_digit_width = 7.0; //For Calabri 11
2318     double padding = 5.0;
2319     int pixels = 0;
2320 
2321     auto it = col_sizes.constFind(col);
2322     if (it != col_sizes.constEnd()) {
2323         double width = it.value();
2324         if (width < 1)
2325             pixels = static_cast<int>(width * (max_digit_width + padding) + 0.5);
2326         else
2327             pixels = static_cast<int>(width * max_digit_width + 0.5) + padding;
2328     } else {
2329         pixels = 64;
2330     }
2331     return pixels;
2332 }
2333 
2334 void WorksheetPrivate::loadXmlSheetData(QXmlStreamReader &reader)
2335 {
2336     Q_Q(Worksheet);
2337 
2338     Q_ASSERT(reader.name() == QLatin1String("sheetData"));
2339 
2340     while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetData") && reader.tokenType() == QXmlStreamReader::EndElement))
2341     {
2342         if (reader.readNextStartElement())
2343         {
2344             if (reader.name() == QLatin1String("row"))
2345             {
2346                 QXmlStreamAttributes attributes = reader.attributes();
2347 
2348                 if (attributes.hasAttribute(QLatin1String("customFormat"))
2349                         || attributes.hasAttribute(QLatin1String("customHeight"))
2350                         || attributes.hasAttribute(QLatin1String("hidden"))
2351                         || attributes.hasAttribute(QLatin1String("outlineLevel"))
2352                         || attributes.hasAttribute(QLatin1String("collapsed")))
2353                 {
2354 
2355                     QSharedPointer<XlsxRowInfo> info(new XlsxRowInfo);
2356                     if (attributes.hasAttribute(QLatin1String("customFormat")) &&
2357                             attributes.hasAttribute(QLatin1String("s")))
2358                     {
2359                         int idx = attributes.value(QLatin1String("s")).toInt();
2360                         info->format = workbook->styles()->xfFormat(idx);
2361                     }
2362 
2363                     if (attributes.hasAttribute(QLatin1String("customHeight")))
2364                     {
2365                         info->customHeight = attributes.value(QLatin1String("customHeight")) == QLatin1String("1");
2366                         //Row height is only specified when customHeight is set
2367                         if(attributes.hasAttribute(QLatin1String("ht")))
2368                         {
2369                             info->height = attributes.value(QLatin1String("ht")).toDouble();
2370                         }
2371                     }
2372 
2373                     //both "hidden" and "collapsed" default are false
2374                     info->hidden = attributes.value(QLatin1String("hidden")) == QLatin1String("1");
2375                     info->collapsed = attributes.value(QLatin1String("collapsed")) == QLatin1String("1");
2376 
2377                     if (attributes.hasAttribute(QLatin1String("outlineLevel")))
2378                         info->outlineLevel = attributes.value(QLatin1String("outlineLevel")).toInt();
2379 
2380                     //"r" is optional too.
2381                     if (attributes.hasAttribute(QLatin1String("r")))
2382                     {
2383                         int row = attributes.value(QLatin1String("r")).toInt();
2384                         rowsInfo[row] = info;
2385                     }
2386                 }
2387 
2388             }
2389             else if (reader.name() == QLatin1String("c")) // Cell
2390             {
2391 
2392                 //Cell
2393                 QXmlStreamAttributes attributes = reader.attributes();
2394                 QString r = attributes.value(QLatin1String("r")).toString();
2395                 CellReference pos(r);
2396 
2397                 //get format
2398                 Format format;
2399                 qint32 styleIndex = -1;
2400                 if (attributes.hasAttribute(QLatin1String("s"))) // Style (defined in the styles.xml file)
2401                 {
2402                     //"s" == style index
2403                     int idx = attributes.value(QLatin1String("s")).toInt();
2404                     format = workbook->styles()->xfFormat(idx);
2405                     styleIndex = idx;
2406                 }
2407 
2408                 // Cell::CellType cellType = Cell::NumberType;
2409                 Cell::CellType cellType = Cell::CustomType;
2410 
2411                 if (attributes.hasAttribute(QLatin1String("t"))) // Type
2412                 {
2413                     const auto typeString = attributes.value(QLatin1String("t"));
2414                     if (typeString == QLatin1String("s")) // Shared string
2415                     {
2416                         cellType = Cell::SharedStringType;
2417                     }
2418                     else if (typeString == QLatin1String("inlineStr")) //  Inline String
2419                     {
2420                         cellType = Cell::InlineStringType;
2421                     }
2422                     else if (typeString == QLatin1String("str")) // String
2423                     {
2424                         cellType = Cell::StringType;
2425                     }
2426                     else if (typeString == QLatin1String("b")) // Boolean
2427                     {
2428                         cellType = Cell::BooleanType;
2429                     }
2430                     else if (typeString == QLatin1String("e")) // Error
2431                     {
2432                         cellType = Cell::ErrorType;
2433                     }
2434                     else if (typeString == QLatin1String("d")) // Date
2435                     {
2436                         cellType = Cell::DateType;
2437                     }
2438                     else if (typeString == QLatin1String("n")) // Number
2439                     {
2440                         cellType = Cell::NumberType;
2441                     }
2442                     else
2443                     {
2444                         // custom type
2445                         cellType = Cell::CustomType;
2446                     }
2447                 }
2448 
2449                 if (Cell::isDateType(cellType, format))
2450                 {
2451                     cellType = Cell::DateType;
2452                 }
2453 
2454                 // create a heap of new cell
2455                 auto cell = std::make_shared<Cell>(QVariant{}, cellType, format, q, styleIndex);
2456 
2457                 while (!reader.atEnd() &&
2458                        !(reader.name() == QLatin1String("c") &&
2459                          reader.tokenType() == QXmlStreamReader::EndElement))
2460                 {
2461                     if (reader.readNextStartElement())
2462                     {
2463                         if (reader.name() == QLatin1String("f")) // formula
2464                         {
2465                             CellFormula &formula = cell->d_func()->formula;
2466                             formula.loadFromXml(reader);
2467                             if (formula.formulaType() == CellFormula::SharedType &&
2468                                     !formula.formulaText().isEmpty())
2469                             {
2470                                 int si = formula.sharedIndex();
2471                                 sharedFormulaMap[ si ] = formula;
2472                             }
2473                         }
2474                         else if (reader.name() == QLatin1String("v")) // Value
2475                         {
2476                             QString value = reader.readElementText();
2477                             if (cellType == Cell::SharedStringType)
2478                             {
2479                                 int sst_idx = value.toInt();
2480                                 sharedStrings()->incRefByStringIndex(sst_idx);
2481                                 RichString rs = sharedStrings()->getSharedString(sst_idx);
2482                                 QString strPlainString = rs.toPlainString();
2483                                 cell->d_func()->value = strPlainString;
2484                                 if (rs.isRichString())
2485                                     cell->d_func()->richString = rs;
2486                             }
2487                             else if (cellType == Cell::NumberType)
2488                             {
2489                                 cell->d_func()->value = value.toDouble();
2490                             }
2491                             else if (cellType == Cell::BooleanType)
2492                             {
2493                                 cell->d_func()->value = value.toInt() ? true : false;
2494                             }
2495                             else  if (cellType == Cell::DateType)
2496                             {
2497                                 // [dev54] DateType
2498 
2499                                 double dValue = value.toDouble(); // days from 1900(or 1904)
2500                                 bool bIsDate1904 = q->workbook()->isDate1904();
2501 
2502                                 QVariant vDatetimeValue = datetimeFromNumber( dValue, bIsDate1904 );
2503                                 Q_UNUSED(vDatetimeValue);
2504                                 // cell->d_func()->value = vDatetimeValue;
2505                                 cell->d_func()->value = dValue; // dev67
2506                             }
2507                             else
2508                             {
2509                                 // ELSE type
2510                                 cell->d_func()->value = value;
2511                             }
2512 
2513                         }
2514                         else if (reader.name() == QLatin1String("is"))
2515                         {
2516                             while (!reader.atEnd() &&
2517                                    !(reader.name() == QLatin1String("is") &&
2518                                    reader.tokenType() == QXmlStreamReader::EndElement))
2519                             {
2520                                 if (reader.readNextStartElement())
2521                                 {
2522                                     //:Todo, add rich text read support
2523                                     if (reader.name() == QLatin1String("t"))
2524                                     {
2525                                         cell->d_func()->value = reader.readElementText();
2526                                     }
2527                                 }
2528                             }
2529                         }
2530                         else if (reader.name() == QLatin1String("extLst"))
2531                         {
2532                             //skip extLst element
2533                             while ( !reader.atEnd() &&
2534                                     !(reader.name() == QLatin1String("extLst") &&
2535                                     reader.tokenType() == QXmlStreamReader::EndElement))
2536                             {
2537                                 reader.readNextStartElement();
2538                             }
2539                         }
2540                     }
2541                 }
2542 
2543                 cellTable[ pos.row() ][ pos.column() ] = cell;
2544 
2545             }
2546         }
2547     }
2548 }
2549 
2550 void WorksheetPrivate::loadXmlColumnsInfo(QXmlStreamReader &reader)
2551 {
2552     Q_ASSERT(reader.name() == QLatin1String("cols"));
2553 
2554     while (!reader.atEnd() &&
2555            !(reader.name() == QLatin1String("cols") &&
2556              reader.tokenType() == QXmlStreamReader::EndElement))
2557     {
2558         reader.readNextStartElement();
2559         if (reader.tokenType() == QXmlStreamReader::StartElement)
2560         {
2561             if (reader.name() == QLatin1String("col"))
2562             {
2563                 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(0, 1, false));
2564 
2565                 QXmlStreamAttributes colAttrs = reader.attributes();
2566                 int min = colAttrs.value(QLatin1String("min")).toInt();
2567                 int max = colAttrs.value(QLatin1String("max")).toInt();
2568                 info->firstColumn = min;
2569                 info->lastColumn = max;
2570 
2571                 //Flag indicating that the column width for the affected column(s) is different from the
2572                 // default or has been manually set
2573                 if(colAttrs.hasAttribute(QLatin1String("customWidth")))
2574                 {
2575                     info->customWidth = colAttrs.value(QLatin1String("customWidth")) == QLatin1String("1");
2576                 }
2577 
2578                 //Note, node may have "width" without "customWidth"
2579                 // [dev54]
2580                 if (colAttrs.hasAttribute(QLatin1String("width")))
2581                 {
2582                     double width = colAttrs.value(QLatin1String("width")).toDouble();
2583                     info->width = width;
2584                     info->isSetWidth = true; // [dev54]
2585                 }
2586 
2587                 info->hidden = colAttrs.value(QLatin1String("hidden")) == QLatin1String("1");
2588                 info->collapsed = colAttrs.value(QLatin1String("collapsed")) == QLatin1String("1");
2589 
2590                 if (colAttrs.hasAttribute(QLatin1String("style")))
2591                 {
2592                     int idx = colAttrs.value(QLatin1String("style")).toInt();
2593                     info->format = workbook->styles()->xfFormat(idx);
2594                 }
2595 
2596                 if (colAttrs.hasAttribute(QLatin1String("outlineLevel")))
2597                 {
2598                     info->outlineLevel = colAttrs.value(QLatin1String("outlineLevel")).toInt();
2599                 }
2600 
2601                 // qDebug() << "[debug] " << __FUNCTION__ << min << max << info->width << hasWidth;
2602 
2603                 colsInfo.insert(min, info);
2604                 for (int col = min ; col <= max ; ++col)
2605                 {
2606                     colsInfoHelper[col] = info;
2607                 }
2608             }
2609         }
2610     }
2611 }
2612 
2613 void WorksheetPrivate::loadXmlMergeCells(QXmlStreamReader &reader)
2614 {
2615     // issue #173 https://github.com/QtExcel/QXlsx/issues/173
2616 
2617     Q_ASSERT(reader.name() == QLatin1String("mergeCells"));
2618 
2619     QXmlStreamAttributes attributes = reader.attributes();
2620 
2621     bool isCount = attributes.hasAttribute(QLatin1String("count"));
2622     int count = 0;
2623     if ( !isCount )
2624     {
2625         qWarning("no count");
2626     }
2627     else
2628     {
2629         count = attributes.value(QLatin1String("count")).toInt();
2630     }
2631 
2632     while ( !reader.atEnd() &&
2633             !(reader.name() == QLatin1String("mergeCells") &&
2634             reader.tokenType() == QXmlStreamReader::EndElement) )
2635     {
2636         reader.readNextStartElement();
2637         if (reader.tokenType() == QXmlStreamReader::StartElement)
2638         {
2639             if (reader.name() == QLatin1String("mergeCell"))
2640             {
2641                 QXmlStreamAttributes attrs = reader.attributes();
2642                 QString rangeStr = attrs.value(QLatin1String("ref")).toString();
2643                 merges.append(CellRange(rangeStr));
2644             }
2645         }
2646     }
2647 
2648     if (isCount)
2649     {
2650         int mergesSize = merges.size();
2651         if ( mergesSize != count )
2652         {
2653             qWarning("read merge cells error");
2654         }
2655     }
2656 
2657 }
2658 
2659 void WorksheetPrivate::loadXmlDataValidations(QXmlStreamReader &reader)
2660 {
2661     Q_ASSERT(reader.name() == QLatin1String("dataValidations"));
2662     QXmlStreamAttributes attributes = reader.attributes();
2663     int count = attributes.value(QLatin1String("count")).toInt();
2664 
2665     while (!reader.atEnd() && !(reader.name() == QLatin1String("dataValidations")
2666             && reader.tokenType() == QXmlStreamReader::EndElement)) {
2667         reader.readNextStartElement();
2668         if (reader.tokenType() == QXmlStreamReader::StartElement
2669                 && reader.name() == QLatin1String("dataValidation")) {
2670             dataValidationsList.append(DataValidation::loadFromXml(reader));
2671         }
2672     }
2673 
2674     if (dataValidationsList.size() != count)
2675         qDebug("read data validation error");
2676 }
2677 
2678 void WorksheetPrivate::loadXmlSheetViews(QXmlStreamReader &reader)
2679 {
2680     Q_ASSERT(reader.name() == QLatin1String("sheetViews"));
2681 
2682     while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetViews")
2683             && reader.tokenType() == QXmlStreamReader::EndElement)) {
2684         reader.readNextStartElement();
2685         if (reader.tokenType() == QXmlStreamReader::StartElement && reader.name() == QLatin1String("sheetView")) {
2686             QXmlStreamAttributes attrs = reader.attributes();
2687             //default false
2688             windowProtection = attrs.value(QLatin1String("windowProtection")) == QLatin1String("1");
2689             showFormulas = attrs.value(QLatin1String("showFormulas")) == QLatin1String("1");
2690             rightToLeft = attrs.value(QLatin1String("rightToLeft")) == QLatin1String("1");
2691             tabSelected = attrs.value(QLatin1String("tabSelected")) == QLatin1String("1");
2692             //default true
2693             showGridLines = attrs.value(QLatin1String("showGridLines")) != QLatin1String("0");
2694             showRowColHeaders = attrs.value(QLatin1String("showRowColHeaders")) != QLatin1String("0");
2695             showZeros = attrs.value(QLatin1String("showZeros")) != QLatin1String("0");
2696             showRuler = attrs.value(QLatin1String("showRuler")) != QLatin1String("0");
2697             showOutlineSymbols = attrs.value(QLatin1String("showOutlineSymbols")) != QLatin1String("0");
2698             showWhiteSpace = attrs.value(QLatin1String("showWhiteSpace")) != QLatin1String("0");
2699         }
2700     }
2701 }
2702 
2703 void WorksheetPrivate::loadXmlSheetFormatProps(QXmlStreamReader &reader)
2704 {
2705     Q_ASSERT(reader.name() == QLatin1String("sheetFormatPr"));
2706 
2707     const QXmlStreamAttributes attributes = reader.attributes();
2708     XlsxSheetFormatProps formatProps;
2709     bool isSetWidth = false;
2710 
2711     // Retain default values
2712     for  (const QXmlStreamAttribute &attrib : attributes)
2713     {
2714         if(attrib.name() == QLatin1String("baseColWidth") )
2715         {
2716             formatProps.baseColWidth = attrib.value().toInt();
2717         }
2718         else if(attrib.name() == QLatin1String("customHeight"))
2719         {
2720             formatProps.customHeight = attrib.value() == QLatin1String("1");
2721         }
2722         else if(attrib.name() == QLatin1String("defaultColWidth"))
2723         {
2724             double dDefaultColWidth = attrib.value().toDouble();
2725             formatProps.defaultColWidth = dDefaultColWidth;
2726             isSetWidth = true;
2727         }
2728         else if(attrib.name() == QLatin1String("defaultRowHeight"))
2729         {
2730             formatProps.defaultRowHeight = attrib.value().toDouble();
2731         }
2732         else if(attrib.name() == QLatin1String("outlineLevelCol"))
2733         {
2734             formatProps.outlineLevelCol = attrib.value().toInt();
2735         }
2736         else if(attrib.name() == QLatin1String("outlineLevelRow"))
2737         {
2738             formatProps.outlineLevelRow = attrib.value().toInt();
2739         }
2740         else if(attrib.name() == QLatin1String("thickBottom"))
2741         {
2742             formatProps.thickBottom = attrib.value() == QLatin1String("1");
2743         }
2744         else if(attrib.name() == QLatin1String("thickTop"))
2745         {
2746             formatProps.thickTop  = attrib.value() == QLatin1String("1");
2747         }
2748         else if(attrib.name() == QLatin1String("zeroHeight"))
2749         {
2750             formatProps.zeroHeight = attrib.value() == QLatin1String("1");
2751         }
2752     }
2753 
2754     // if (formatProps.defaultColWidth == 0.0)
2755     if ( !isSetWidth )
2756     {
2757         //not set
2758         double dCalcWidth = WorksheetPrivate::calculateColWidth(formatProps.baseColWidth);
2759         formatProps.defaultColWidth = dCalcWidth;
2760     }
2761 
2762     // [dev54]
2763     // Where is code of setting 'formatProps'?
2764     this->sheetFormatProps = formatProps;
2765 
2766 }
2767 double WorksheetPrivate::calculateColWidth(int characters)
2768 {
2769     // //!Todo
2770     //Take normal style' font maximum width and add padding and margin pixels
2771     // return characters + 0.5;
2772     return characters;
2773 }
2774 
2775 void WorksheetPrivate::loadXmlHyperlinks(QXmlStreamReader &reader)
2776 {
2777     Q_ASSERT(reader.name() == QLatin1String("hyperlinks"));
2778 
2779     while (!reader.atEnd() && !(reader.name() == QLatin1String("hyperlinks")
2780             && reader.tokenType() == QXmlStreamReader::EndElement)) {
2781         reader.readNextStartElement();
2782         if (reader.tokenType() == QXmlStreamReader::StartElement && reader.name() == QLatin1String("hyperlink")) {
2783             QXmlStreamAttributes attrs = reader.attributes();
2784             CellReference pos(attrs.value(QLatin1String("ref")).toString());
2785             if (pos.isValid()) { //Valid
2786                 QSharedPointer<XlsxHyperlinkData> link(new XlsxHyperlinkData);
2787                 link->display = attrs.value(QLatin1String("display")).toString();
2788                 link->tooltip = attrs.value(QLatin1String("tooltip")).toString();
2789                 link->location = attrs.value(QLatin1String("location")).toString();
2790 
2791                 if (attrs.hasAttribute(QLatin1String("r:id"))) {
2792                     link->linkType = XlsxHyperlinkData::External;
2793                     XlsxRelationship ship = relationships->getRelationshipById(attrs.value(QLatin1String("r:id")).toString());
2794                     link->target = ship.target;
2795                 } else {
2796                     link->linkType = XlsxHyperlinkData::Internal;
2797                 }
2798 
2799                 urlTable[pos.row()][pos.column()] = link;
2800             }
2801         }
2802     }
2803 }
2804 
2805 QList <QSharedPointer<XlsxColumnInfo> > WorksheetPrivate::getColumnInfoList(int colFirst, int colLast)
2806 {
2807     QList <QSharedPointer<XlsxColumnInfo> > columnsInfoList;
2808     if (isColumnRangeValid(colFirst,colLast))
2809     {
2810         QList<int> nodes = getColumnIndexes(colFirst, colLast);
2811 
2812         for (int idx = 0; idx < nodes.size(); ++idx)
2813         {
2814             int colStart = nodes[idx];
2815             auto it = colsInfo.constFind(colStart);
2816             if (it != colsInfo.constEnd())
2817             {
2818                 columnsInfoList.append(*it);
2819             }
2820             else
2821             {
2822                 int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1;
2823                 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd, false));
2824                 colsInfo.insert(colFirst, info);
2825                 columnsInfoList.append(info);
2826                 for (int c = colStart; c <= colEnd; ++c)
2827                 {
2828                     colsInfoHelper[c] = info;
2829                 }
2830             }
2831         }
2832     }
2833 
2834     return columnsInfoList;
2835 }
2836 
2837 QList <QSharedPointer<XlsxRowInfo> > WorksheetPrivate::getRowInfoList(int rowFirst, int rowLast)
2838 {
2839     QList <QSharedPointer<XlsxRowInfo> > rowInfoList;
2840 
2841     int min_col = dimension.firstColumn() < 1 ? 1 : dimension.firstColumn();
2842 
2843     for(int row = rowFirst; row <= rowLast; ++row) {
2844         if (checkDimensions(row, min_col, false, true))
2845             continue;
2846 
2847         QSharedPointer<XlsxRowInfo> rowInfo;
2848         if ((rowsInfo[row]).isNull()){
2849             rowsInfo[row] = QSharedPointer<XlsxRowInfo>(new XlsxRowInfo());
2850         }
2851         rowInfoList.append(rowsInfo[row]);
2852     }
2853 
2854     return rowInfoList;
2855 }
2856 
2857 bool Worksheet::loadFromXmlFile(QIODevice *device)
2858 {
2859     Q_D(Worksheet);
2860 
2861     QXmlStreamReader reader(device);
2862     while (!reader.atEnd())
2863     {
2864         reader.readNextStartElement();
2865         if (reader.tokenType() == QXmlStreamReader::StartElement)
2866         {
2867             if (reader.name() == QLatin1String("dimension"))
2868             {
2869                 QXmlStreamAttributes attributes = reader.attributes();
2870                 QString range = attributes.value(QLatin1String("ref")).toString();
2871                 d->dimension = CellRange(range);
2872             }
2873             else if (reader.name() == QLatin1String("sheetViews"))
2874             {
2875                 d->loadXmlSheetViews(reader);
2876             }
2877             else if (reader.name() == QLatin1String("sheetFormatPr"))
2878             {
2879                 d->loadXmlSheetFormatProps(reader);
2880             }
2881             else if (reader.name() == QLatin1String("cols"))
2882             {
2883                 d->loadXmlColumnsInfo(reader);
2884             }
2885             else if (reader.name() == QLatin1String("sheetData"))
2886             {
2887                 d->loadXmlSheetData(reader);
2888             }
2889             else if (reader.name() == QLatin1String("mergeCells"))
2890             {
2891                 d->loadXmlMergeCells(reader);
2892             }
2893             else if (reader.name() == QLatin1String("dataValidations"))
2894             {
2895                 d->loadXmlDataValidations(reader);
2896             }
2897             else if (reader.name() == QLatin1String("conditionalFormatting"))
2898             {
2899                 ConditionalFormatting cf;
2900                 cf.loadFromXml(reader, workbook()->styles());
2901                 d->conditionalFormattingList.append(cf);
2902             }
2903             else if (reader.name() == QLatin1String("hyperlinks"))
2904             {
2905                 d->loadXmlHyperlinks(reader);
2906             }
2907             else if(reader.name() == QLatin1String("pageSetup"))
2908             {
2909                 QXmlStreamAttributes attributes = reader.attributes();
2910 
2911                 d->PpaperSize = attributes.value(QLatin1String("paperSize")).toString().trimmed();
2912                 d->Pscale = attributes.value(QLatin1String("scale")).toString().trimmed();
2913                 d->PfirstPageNumber = attributes.value(QLatin1String("firstPageNumber")).toString().trimmed();
2914                 d->Porientation = attributes.value(QLatin1String("orientation")).toString().trimmed();
2915                 d->PuseFirstPageNumber = attributes.value(QLatin1String("useFirstPageNumber")).toString().trimmed();
2916                 d->PhorizontalDpi = attributes.value(QLatin1String("horizontalDpi")).toString().trimmed();
2917                 d->PverticalDpi = attributes.value(QLatin1String("verticalDpi")).toString().trimmed();
2918                 d->Prid = attributes.value(QLatin1String("r:id")).toString().trimmed();
2919                 d->Pcopies = attributes.value(QLatin1String("copies")).toString().trimmed();
2920             }
2921             else if(reader.name() == QLatin1String("pageMargins"))
2922             {
2923                 QXmlStreamAttributes attributes = reader.attributes();
2924 
2925                 d->PMfooter= attributes.value(QLatin1String("footer")).toString().trimmed();
2926                 d->PMheader = attributes.value(QLatin1String("header")).toString().trimmed();
2927                 d->PMbotton = attributes.value(QLatin1String("bottom")).toString().trimmed();
2928                 d->PMtop = attributes.value(QLatin1String("top")).toString().trimmed();
2929                 d->PMright = attributes.value(QLatin1String("right")).toString().trimmed();
2930                 d->PMleft = attributes.value(QLatin1String("left")).toString().trimmed();
2931             }
2932             else if(reader.name() == QLatin1String("headerFooter"))
2933             {
2934                 // dev40
2935                 while (reader.readNextStartElement())
2936                 {
2937                     if (reader.name() == QLatin1String("oddHeader"))
2938                         d->ModdHeader = reader.readElementText();
2939 
2940                     if (reader.name() == QLatin1String("oddFooter"))
2941                             d->MoodFooter = reader.readElementText();
2942                 }
2943             }
2944             else if (reader.name() == QLatin1String("drawing"))
2945             {
2946                 QString rId = reader.attributes().value(QStringLiteral("r:id")).toString();
2947                 QString name = d->relationships->getRelationshipById(rId).target;
2948 
2949                 const auto parts = splitPath(filePath());
2950                 QString path = QDir::cleanPath(parts.first() + QLatin1String("/") + name);
2951 
2952                 d->drawing = std::make_shared<Drawing>(this, F_LoadFromExists);
2953                 d->drawing->setFilePath(path);
2954             }
2955             else if (reader.name() == QLatin1String("extLst"))
2956             {
2957                 //Todo: add extLst support
2958                 while ( !reader.atEnd() &&
2959                         !(reader.name() == QLatin1String("extLst") &&
2960                           reader.tokenType() == QXmlStreamReader::EndElement))
2961                 {
2962                     reader.readNextStartElement();
2963                 }
2964             }
2965         }
2966     }
2967 
2968     d->validateDimension();
2969     return true;
2970 }
2971 
2972 /*
2973  *  Documents imported from Google Docs does not contain dimension data.
2974  */
2975 void WorksheetPrivate::validateDimension()
2976 {
2977     if (dimension.isValid() || cellTable.isEmpty())
2978         return;
2979 
2980     const auto firstRow = cellTable.constBegin().key();
2981 
2982     const auto lastRow = (--cellTable.constEnd()).key();
2983 
2984     int firstColumn = -1;
2985     int lastColumn = -1;
2986 
2987     for ( auto&& it = cellTable.constBegin()
2988             ; it != cellTable.constEnd()
2989             ; ++it )
2990     {
2991         Q_ASSERT(!it.value().isEmpty());
2992 
2993         if (firstColumn == -1 || it.value().constBegin().key() < firstColumn)
2994             firstColumn = it.value().constBegin().key();
2995 
2996         if (lastColumn == -1 || (--it.value().constEnd()).key() > lastColumn)
2997         {
2998             lastColumn = (--it.value().constEnd()).key();
2999         }
3000     }
3001 
3002     CellRange cr(firstRow, firstColumn, lastRow, lastColumn);
3003 
3004     if (cr.isValid())
3005         dimension = cr;
3006 }
3007 
3008 /*!
3009  * \internal
3010  *  Unit test can use this member to get sharedString object.
3011  */
3012 SharedStrings *WorksheetPrivate::sharedStrings() const
3013 {
3014     return workbook->sharedStrings();
3015 }
3016 
3017 QVector<CellLocation> Worksheet::getFullCells(int* maxRow, int* maxCol)
3018 {
3019     Q_D(const Worksheet);
3020 
3021     // return values
3022     (*maxRow) = -1;
3023     (*maxCol) = -1;
3024     QVector<CellLocation> ret;
3025 
3026     // QString privateName = d->name; // name of sheet (not object type)
3027     // qDebug() << privateName ;
3028 
3029     if ( d->type == AbstractSheet::ST_WorkSheet  )
3030     {
3031         // use current sheet
3032     }
3033     else if ( d->type == AbstractSheet::ST_ChartSheet )
3034     {
3035         return ret;
3036     }
3037     else
3038     {
3039         qWarning("unsupported sheet type.");
3040         Q_ASSERT(false);
3041         return ret;
3042     }
3043 
3044     QMapIterator< int, QMap< int, std::shared_ptr<Cell> > > _it( d->cellTable );
3045 
3046     while ( _it.hasNext() )
3047     {
3048         _it.next();
3049 
3050         int keyI = _it.key(); // key (cell row)
3051         QMapIterator<int, std::shared_ptr<Cell> > _iit( _it.value() ); // value
3052 
3053         while ( _iit.hasNext() )
3054         {
3055             _iit.next();
3056 
3057             int keyII = _iit.key(); // key (cell column)
3058             std::shared_ptr<Cell> ptrCell = _iit.value(); // value
3059 
3060             CellLocation cl;
3061 
3062             cl.row = keyI;
3063             if ( keyI > (*maxRow) )
3064             {
3065                 (*maxRow) = keyI;
3066             }
3067 
3068             cl.col = keyII;
3069             if ( keyII > (*maxCol) )
3070             {
3071                 (*maxCol) = keyII;
3072             }
3073 
3074             cl.cell = ptrCell;
3075 
3076             ret.push_back( cl );
3077         }
3078     }
3079 
3080     return ret;
3081 }
3082 
3083 QT_END_NAMESPACE_XLSX