File indexing completed on 2025-03-23 03:32:52
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