comparison src/xlsx/xlsxworksheet.cpp @ 1:93d3106bb9a4

Add qt xlsx library
author Andre Heinecke <andre.heinecke@intevation.de>
date Tue, 22 Mar 2016 10:38:08 +0100
parents
children
comparison
equal deleted inserted replaced
0:49cd5cc0b072 1:93d3106bb9a4
1 /****************************************************************************
2 ** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
3 ** All right reserved.
4 **
5 ** Permission is hereby granted, free of charge, to any person obtaining
6 ** a copy of this software and associated documentation files (the
7 ** "Software"), to deal in the Software without restriction, including
8 ** without limitation the rights to use, copy, modify, merge, publish,
9 ** distribute, sublicense, and/or sell copies of the Software, and to
10 ** permit persons to whom the Software is furnished to do so, subject to
11 ** the following conditions:
12 **
13 ** The above copyright notice and this permission notice shall be
14 ** included in all copies or substantial portions of the Software.
15 **
16 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 ** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 ** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 ** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 ** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 **
24 ****************************************************************************/
25 #include "xlsxrichstring.h"
26 #include "xlsxcellreference.h"
27 #include "xlsxworksheet.h"
28 #include "xlsxworksheet_p.h"
29 #include "xlsxworkbook.h"
30 #include "xlsxformat.h"
31 #include "xlsxformat_p.h"
32 #include "xlsxutility_p.h"
33 #include "xlsxsharedstrings_p.h"
34 #include "xlsxdrawing_p.h"
35 #include "xlsxstyles_p.h"
36 #include "xlsxcell.h"
37 #include "xlsxcell_p.h"
38 #include "xlsxcellrange.h"
39 #include "xlsxconditionalformatting_p.h"
40 #include "xlsxdrawinganchor_p.h"
41 #include "xlsxchart.h"
42 #include "xlsxcellformula.h"
43 #include "xlsxcellformula_p.h"
44
45 #include <QVariant>
46 #include <QDateTime>
47 #include <QPoint>
48 #include <QFile>
49 #include <QUrl>
50 #include <QRegularExpression>
51 #include <QDebug>
52 #include <QBuffer>
53 #include <QXmlStreamWriter>
54 #include <QXmlStreamReader>
55 #include <QTextDocument>
56 #include <QDir>
57
58 #include <math.h>
59
60 QT_BEGIN_NAMESPACE_XLSX
61
62 WorksheetPrivate::WorksheetPrivate(Worksheet *p, Worksheet::CreateFlag flag)
63 : AbstractSheetPrivate(p, flag)
64 , windowProtection(false), showFormulas(false), showGridLines(true), showRowColHeaders(true)
65 , showZeros(true), rightToLeft(false), tabSelected(false), showRuler(false)
66 , showOutlineSymbols(true), showWhiteSpace(true), urlPattern(QStringLiteral("^([fh]tt?ps?://)|(mailto:)|(file://)"))
67 {
68 previous_row = 0;
69
70 outline_row_level = 0;
71 outline_col_level = 0;
72
73 default_row_height = 15;
74 default_row_zeroed = false;
75 }
76
77 WorksheetPrivate::~WorksheetPrivate()
78 {
79 }
80
81 /*
82 Calculate the "spans" attribute of the <row> tag. This is an
83 XLSX optimisation and isn't strictly required. However, it
84 makes comparing files easier. The span is the same for each
85 block of 16 rows.
86 */
87 void WorksheetPrivate::calculateSpans() const
88 {
89 row_spans.clear();
90 int span_min = XLSX_COLUMN_MAX+1;
91 int span_max = -1;
92
93 for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) {
94 if (cellTable.contains(row_num)) {
95 for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) {
96 if (cellTable[row_num].contains(col_num)) {
97 if (span_max == -1) {
98 span_min = col_num;
99 span_max = col_num;
100 } else {
101 if (col_num < span_min)
102 span_min = col_num;
103 else if (col_num > span_max)
104 span_max = col_num;
105 }
106 }
107 }
108 }
109 if (comments.contains(row_num)) {
110 for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) {
111 if (comments[row_num].contains(col_num)) {
112 if (span_max == -1) {
113 span_min = col_num;
114 span_max = col_num;
115 } else {
116 if (col_num < span_min)
117 span_min = col_num;
118 else if (col_num > span_max)
119 span_max = col_num;
120 }
121 }
122 }
123 }
124
125 if (row_num%16 == 0 || row_num == dimension.lastRow()) {
126 if (span_max != -1) {
127 row_spans[row_num / 16] = QStringLiteral("%1:%2").arg(span_min).arg(span_max);
128 span_min = XLSX_COLUMN_MAX+1;
129 span_max = -1;
130 }
131 }
132 }
133 }
134
135
136 QString WorksheetPrivate::generateDimensionString() const
137 {
138 if (!dimension.isValid())
139 return QStringLiteral("A1");
140 else
141 return dimension.toString();
142 }
143
144 /*
145 Check that row and col are valid and store the max and min
146 values for use in other methods/elements. The ignore_row /
147 ignore_col flags is used to indicate that we wish to perform
148 the dimension check without storing the value. The ignore
149 flags are use by setRow() and dataValidate.
150 */
151 int WorksheetPrivate::checkDimensions(int row, int col, bool ignore_row, bool ignore_col)
152 {
153 Q_ASSERT_X(row!=0, "checkDimensions", "row should start from 1 instead of 0");
154 Q_ASSERT_X(col!=0, "checkDimensions", "column should start from 1 instead of 0");
155
156 if (row > XLSX_ROW_MAX || row < 1 || col > XLSX_COLUMN_MAX || col < 1)
157 return -1;
158
159 if (!ignore_row) {
160 if (row < dimension.firstRow() || dimension.firstRow() == -1) dimension.setFirstRow(row);
161 if (row > dimension.lastRow()) dimension.setLastRow(row);
162 }
163 if (!ignore_col) {
164 if (col < dimension.firstColumn() || dimension.firstColumn() == -1) dimension.setFirstColumn(col);
165 if (col > dimension.lastColumn()) dimension.setLastColumn(col);
166 }
167
168 return 0;
169 }
170
171 /*!
172 \class Worksheet
173 \inmodule QtXlsx
174 \brief Represent one worksheet in the workbook.
175 */
176
177 /*!
178 * \internal
179 */
180 Worksheet::Worksheet(const QString &name, int id, Workbook *workbook, CreateFlag flag)
181 :AbstractSheet(name, id, workbook, new WorksheetPrivate(this, flag))
182 {
183 if (!workbook) //For unit test propose only. Ignore the memery leak.
184 d_func()->workbook = new Workbook(flag);
185 }
186
187 /*!
188 * \internal
189 *
190 * Make a copy of this sheet.
191 */
192
193 Worksheet *Worksheet::copy(const QString &distName, int distId) const
194 {
195 Q_D(const Worksheet);
196 Worksheet *sheet = new Worksheet(distName, distId, d->workbook, F_NewFromScratch);
197 WorksheetPrivate *sheet_d = sheet->d_func();
198
199 sheet_d->dimension = d->dimension;
200
201 QMapIterator<int, QMap<int, QSharedPointer<Cell> > > it(d->cellTable);
202 while (it.hasNext()) {
203 it.next();
204 int row = it.key();
205 QMapIterator<int, QSharedPointer<Cell> > it2(it.value());
206 while (it2.hasNext()) {
207 it2.next();
208 int col = it2.key();
209
210 QSharedPointer<Cell> cell(new Cell(it2.value().data()));
211 cell->d_ptr->parent = sheet;
212
213 if (cell->cellType() == Cell::SharedStringType)
214 d->workbook->sharedStrings()->addSharedString(cell->d_ptr->richString);
215
216 sheet_d->cellTable[row][col] = cell;
217 }
218 }
219
220 sheet_d->merges = d->merges;
221 // sheet_d->rowsInfo = d->rowsInfo;
222 // sheet_d->colsInfo = d->colsInfo;
223 // sheet_d->colsInfoHelper = d->colsInfoHelper;
224 // sheet_d->dataValidationsList = d->dataValidationsList;
225 // sheet_d->conditionalFormattingList = d->conditionalFormattingList;
226
227 return sheet;
228 }
229
230 /*!
231 * Destroys this workssheet.
232 */
233 Worksheet::~Worksheet()
234 {
235 }
236
237 /*!
238 * Returns whether sheet is protected.
239 */
240 bool Worksheet::isWindowProtected() const
241 {
242 Q_D(const Worksheet);
243 return d->windowProtection;
244 }
245
246 /*!
247 * Protects/unprotects the sheet based on \a protect.
248 */
249 void Worksheet::setWindowProtected(bool protect)
250 {
251 Q_D(Worksheet);
252 d->windowProtection = protect;
253 }
254
255 /*!
256 * Return whether formulas instead of their calculated results shown in cells
257 */
258 bool Worksheet::isFormulasVisible() const
259 {
260 Q_D(const Worksheet);
261 return d->showFormulas;
262 }
263
264 /*!
265 * Show formulas in cells instead of their calculated results when \a visible is true.
266 */
267 void Worksheet::setFormulasVisible(bool visible)
268 {
269 Q_D(Worksheet);
270 d->showFormulas = visible;
271 }
272
273 /*!
274 * Return whether gridlines is shown or not.
275 */
276 bool Worksheet::isGridLinesVisible() const
277 {
278 Q_D(const Worksheet);
279 return d->showGridLines;
280 }
281
282 /*!
283 * Show or hide the gridline based on \a visible
284 */
285 void Worksheet::setGridLinesVisible(bool visible)
286 {
287 Q_D(Worksheet);
288 d->showGridLines = visible;
289 }
290
291 /*!
292 * Return whether is row and column headers is vislbe.
293 */
294 bool Worksheet::isRowColumnHeadersVisible() const
295 {
296 Q_D(const Worksheet);
297 return d->showRowColHeaders;
298 }
299
300 /*!
301 * Show or hide the row column headers based on \a visible
302 */
303 void Worksheet::setRowColumnHeadersVisible(bool visible)
304 {
305 Q_D(Worksheet);
306 d->showRowColHeaders = visible;
307 }
308
309
310 /*!
311 * Return whether the sheet is shown right-to-left or not.
312 */
313 bool Worksheet::isRightToLeft() const
314 {
315 Q_D(const Worksheet);
316 return d->rightToLeft;
317 }
318
319 /*!
320 * Enable or disable the right-to-left based on \a enable.
321 */
322 void Worksheet::setRightToLeft(bool enable)
323 {
324 Q_D(Worksheet);
325 d->rightToLeft = enable;
326 }
327
328 /*!
329 * Return whether is cells that have zero value show a zero.
330 */
331 bool Worksheet::isZerosVisible() const
332 {
333 Q_D(const Worksheet);
334 return d->showZeros;
335 }
336
337 /*!
338 * Show a zero in cells that have zero value if \a visible is true.
339 */
340 void Worksheet::setZerosVisible(bool visible)
341 {
342 Q_D(Worksheet);
343 d->showZeros = visible;
344 }
345
346 /*!
347 * Return whether this tab is selected.
348 */
349 bool Worksheet::isSelected() const
350 {
351 Q_D(const Worksheet);
352 return d->tabSelected;
353 }
354
355 /*!
356 * Select this sheet if \a select is true.
357 */
358 void Worksheet::setSelected(bool select)
359 {
360 Q_D(Worksheet);
361 d->tabSelected = select;
362 }
363
364 /*!
365 * Return whether is ruler is shown.
366 */
367 bool Worksheet::isRulerVisible() const
368 {
369 Q_D(const Worksheet);
370 return d->showRuler;
371
372 }
373
374 /*!
375 * Show or hide the ruler based on \a visible.
376 */
377 void Worksheet::setRulerVisible(bool visible)
378 {
379 Q_D(Worksheet);
380 d->showRuler = visible;
381
382 }
383
384 /*!
385 * Return whether is outline symbols is shown.
386 */
387 bool Worksheet::isOutlineSymbolsVisible() const
388 {
389 Q_D(const Worksheet);
390 return d->showOutlineSymbols;
391 }
392
393 /*!
394 * Show or hide the outline symbols based ib \a visible.
395 */
396 void Worksheet::setOutlineSymbolsVisible(bool visible)
397 {
398 Q_D(Worksheet);
399 d->showOutlineSymbols = visible;
400 }
401
402 /*!
403 * Return whether is white space is shown.
404 */
405 bool Worksheet::isWhiteSpaceVisible() const
406 {
407 Q_D(const Worksheet);
408 return d->showWhiteSpace;
409 }
410
411 /*!
412 * Show or hide the white space based on \a visible.
413 */
414 void Worksheet::setWhiteSpaceVisible(bool visible)
415 {
416 Q_D(Worksheet);
417 d->showWhiteSpace = visible;
418 }
419
420 /*!
421 * Write \a value to cell (\a row, \a column) with the \a format.
422 * Both \a row and \a column are all 1-indexed value.
423 *
424 * Returns true on success.
425 */
426 bool Worksheet::write(int row, int column, const QVariant &value, const Format &format)
427 {
428 Q_D(Worksheet);
429
430 if (d->checkDimensions(row, column))
431 return false;
432
433 bool ret = true;
434 if (value.isNull()) {
435 //Blank
436 ret = writeBlank(row, column, format);
437 } else if (value.userType() == QMetaType::QString) {
438 //String
439 QString token = value.toString();
440 bool ok;
441
442 if (token.startsWith(QLatin1String("="))) {
443 //convert to formula
444 ret = writeFormula(row, column, CellFormula(token), format);
445 } else if (d->workbook->isStringsToHyperlinksEnabled() && token.contains(d->urlPattern)) {
446 //convert to url
447 ret = writeHyperlink(row, column, QUrl(token));
448 } else if (d->workbook->isStringsToNumbersEnabled() && (value.toDouble(&ok), ok)) {
449 //Try convert string to number if the flag enabled.
450 ret = writeString(row, column, value.toString(), format);
451 } else {
452 //normal string now
453 ret = writeString(row, column, token, format);
454 }
455 } else if (value.userType() == qMetaTypeId<RichString>()) {
456 ret = writeString(row, column, value.value<RichString>(), format);
457 } else if (value.userType() == QMetaType::Int || value.userType() == QMetaType::UInt
458 || value.userType() == QMetaType::LongLong || value.userType() == QMetaType::ULongLong
459 || value.userType() == QMetaType::Double || value.userType() == QMetaType::Float) {
460 //Number
461
462 ret = writeNumeric(row, column, value.toDouble(), format);
463 } else if (value.userType() == QMetaType::Bool) {
464 //Bool
465 ret = writeBool(row,column, value.toBool(), format);
466 } else if (value.userType() == QMetaType::QDateTime || value.userType() == QMetaType::QDate) {
467 //DateTime, Date
468 // note that, QTime cann't convert to QDateTime
469 ret = writeDateTime(row, column, value.toDateTime(), format);
470 } else if (value.userType() == QMetaType::QTime) {
471 //Time
472 ret = writeTime(row, column, value.toTime(), format);
473 } else if (value.userType() == QMetaType::QUrl) {
474 //Url
475 ret = writeHyperlink(row, column, value.toUrl(), format);
476 } else {
477 //Wrong type
478 return false;
479 }
480
481 return ret;
482 }
483
484 /*!
485 * \overload
486 * Write \a value to cell \a row_column with the \a format.
487 * Both row and column are all 1-indexed value.
488 * Returns true on success.
489 */
490 bool Worksheet::write(const CellReference &row_column, const QVariant &value, const Format &format)
491 {
492 if (!row_column.isValid())
493 return false;
494
495 return write(row_column.row(), row_column.column(), value, format);
496 }
497
498 /*!
499 \overload
500 Return the contents of the cell \a row_column.
501 */
502 QVariant Worksheet::read(const CellReference &row_column) const
503 {
504 if (!row_column.isValid())
505 return QVariant();
506
507 return read(row_column.row(), row_column.column());
508 }
509
510 /*!
511 Return the contents of the cell (\a row, \a column).
512 */
513 QVariant Worksheet::read(int row, int column) const
514 {
515 Q_D(const Worksheet);
516
517 Cell *cell = cellAt(row, column);
518 if (!cell)
519 return QVariant();
520
521 if (cell->hasFormula()) {
522 if (cell->formula().formulaType() == CellFormula::NormalType) {
523 return QVariant(QLatin1String("=")+cell->formula().formulaText());
524 } else if (cell->formula().formulaType() == CellFormula::SharedType) {
525 if (!cell->formula().formulaText().isEmpty()) {
526 return QVariant(QLatin1String("=")+cell->formula().formulaText());
527 } else {
528 const CellFormula &rootFormula = d->sharedFormulaMap[cell->formula().sharedIndex()];
529 CellReference rootCellRef = rootFormula.reference().topLeft();
530 QString rootFormulaText = rootFormula.formulaText();
531 QString newFormulaText = convertSharedFormula(rootFormulaText, rootCellRef, CellReference(row, column));
532 return QVariant(QLatin1String("=")+newFormulaText);
533 }
534 }
535 }
536
537 if (cell->isDateTime()) {
538 double val = cell->value().toDouble();
539 QDateTime dt = cell->dateTime();
540 if (val < 1)
541 return dt.time();
542 if (fmod(val, 1.0) < 1.0/(1000*60*60*24)) //integer
543 return dt.date();
544 return dt;
545 }
546
547 return cell->value();
548 }
549
550 /*!
551 * Returns the cell at the given \a row_column. If there
552 * is no cell at the specified position, the function returns 0.
553 */
554 Cell *Worksheet::cellAt(const CellReference &row_column) const
555 {
556 if (!row_column.isValid())
557 return 0;
558
559 return cellAt(row_column.row(), row_column.column());
560 }
561
562 /*!
563 * Returns the cell at the given \a row and \a column. If there
564 * is no cell at the specified position, the function returns 0.
565 */
566 Cell *Worksheet::cellAt(int row, int column) const
567 {
568 Q_D(const Worksheet);
569 if (!d->cellTable.contains(row))
570 return 0;
571 if (!d->cellTable[row].contains(column))
572 return 0;
573
574 return d->cellTable[row][column].data();
575 }
576
577 Format WorksheetPrivate::cellFormat(int row, int col) const
578 {
579 if (!cellTable.contains(row))
580 return Format();
581 if (!cellTable[row].contains(col))
582 return Format();
583 return cellTable[row][col]->format();
584 }
585
586 /*!
587 \overload
588 Write string \a value to the cell \a row_column with the \a format.
589
590 Returns true on success.
591 */
592 bool Worksheet::writeString(const CellReference &row_column, const RichString &value, const Format &format)
593 {
594 if (!row_column.isValid())
595 return false;
596
597 return writeString(row_column.row(), row_column.column(), value, format);
598 }
599
600 /*!
601 Write string \a value to the cell (\a row, \a column) with the \a format.
602 Returns true on success.
603 */
604 bool Worksheet::writeString(int row, int column, const RichString &value, const Format &format)
605 {
606 Q_D(Worksheet);
607 // QString content = value.toPlainString();
608 if (d->checkDimensions(row, column))
609 return false;
610
611 // if (content.size() > d->xls_strmax) {
612 // content = content.left(d->xls_strmax);
613 // error = -2;
614 // }
615
616 d->sharedStrings()->addSharedString(value);
617 Format fmt = format.isValid() ? format : d->cellFormat(row, column);
618 if (value.fragmentCount() == 1 && value.fragmentFormat(0).isValid())
619 fmt.mergeFormat(value.fragmentFormat(0));
620 d->workbook->styles()->addXfFormat(fmt);
621 QSharedPointer<Cell> cell = QSharedPointer<Cell>(new Cell(value.toPlainString(), Cell::SharedStringType, fmt, this));
622 cell->d_ptr->richString = value;
623 d->cellTable[row][column] = cell;
624 return true;
625 }
626
627 /*!
628 \overload
629 Write string \a value to the cell \a row_column with the \a format.
630 */
631 bool Worksheet::writeString(const CellReference &row_column, const QString &value, const Format &format)
632 {
633 if (!row_column.isValid())
634 return false;
635
636 return writeString(row_column.row(), row_column.column(), value, format);
637 }
638
639 /*!
640 \overload
641
642 Write string \a value to the cell (\a row, \a column) with the \a format.
643 Returns true on success.
644 */
645 bool Worksheet::writeString(int row, int column, const QString &value, const Format &format)
646 {
647 Q_D(Worksheet);
648 if (d->checkDimensions(row, column))
649 return false;
650
651 RichString rs;
652 if (d->workbook->isHtmlToRichStringEnabled() && Qt::mightBeRichText(value))
653 rs.setHtml(value);
654 else
655 rs.addFragment(value, Format());
656
657 return writeString(row, column, rs, format);
658 }
659
660 /*!
661 \overload
662 Write string \a value to the cell \a row_column with the \a format
663 */
664 bool Worksheet::writeInlineString(const CellReference &row_column, const QString &value, const Format &format)
665 {
666 if (!row_column.isValid())
667 return false;
668
669 return writeInlineString(row_column.row(), row_column.column(), value, format);
670 }
671
672 /*!
673 Write string \a value to the cell (\a row, \a column) with the \a format.
674 Returns true on success.
675 */
676 bool Worksheet::writeInlineString(int row, int column, const QString &value, const Format &format)
677 {
678 Q_D(Worksheet);
679 //int error = 0;
680 QString content = value;
681 if (d->checkDimensions(row, column))
682 return false;
683
684 if (value.size() > XLSX_STRING_MAX) {
685 content = value.left(XLSX_STRING_MAX);
686 //error = -2;
687 }
688
689 Format fmt = format.isValid() ? format : d->cellFormat(row, column);
690 d->workbook->styles()->addXfFormat(fmt);
691 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::InlineStringType, fmt, this));
692 return true;
693 }
694
695 /*!
696 \overload
697 Write numeric \a value to the cell \a row_column with the \a format.
698 Returns true on success.
699 */
700 bool Worksheet::writeNumeric(const CellReference &row_column, double value, const Format &format)
701 {
702 if (!row_column.isValid())
703 return false;
704
705 return writeNumeric(row_column.row(), row_column.column(), value, format);
706 }
707
708 /*!
709 Write numeric \a value to the cell (\a row, \a column) with the \a format.
710 Returns true on success.
711 */
712 bool Worksheet::writeNumeric(int row, int column, double value, const Format &format)
713 {
714 Q_D(Worksheet);
715 if (d->checkDimensions(row, column))
716 return false;
717
718 Format fmt = format.isValid() ? format : d->cellFormat(row, column);
719 d->workbook->styles()->addXfFormat(fmt);
720 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::NumberType, fmt, this));
721 return true;
722 }
723
724 /*!
725 \overload
726 Write \a formula to the cell \a row_column with the \a format and \a result.
727 Returns true on success.
728 */
729 bool Worksheet::writeFormula(const CellReference &row_column, const CellFormula &formula, const Format &format, double result)
730 {
731 if (!row_column.isValid())
732 return false;
733
734 return writeFormula(row_column.row(), row_column.column(), formula, format, result);
735 }
736
737 /*!
738 Write \a formula_ to the cell (\a row, \a column) with the \a format and \a result.
739 Returns true on success.
740 */
741 bool Worksheet::writeFormula(int row, int column, const CellFormula &formula_, const Format &format, double result)
742 {
743 Q_D(Worksheet);
744 if (d->checkDimensions(row, column))
745 return false;
746
747 Format fmt = format.isValid() ? format : d->cellFormat(row, column);
748 d->workbook->styles()->addXfFormat(fmt);
749
750 CellFormula formula = formula_;
751 formula.d->ca = true;
752 if (formula.formulaType() == CellFormula::SharedType) {
753 //Assign proper shared index for shared formula
754 int si=0;
755 while(d->sharedFormulaMap.contains(si))
756 ++si;
757 formula.d->si = si;
758 d->sharedFormulaMap[si] = formula;
759 }
760
761 QSharedPointer<Cell> data = QSharedPointer<Cell>(new Cell(result, Cell::NumberType, fmt, this));
762 data->d_ptr->formula = formula;
763 d->cellTable[row][column] = data;
764
765 CellRange range = formula.reference();
766 if (formula.formulaType() == CellFormula::SharedType) {
767 CellFormula sf(QString(), CellFormula::SharedType);
768 sf.d->si = formula.sharedIndex();
769 for (int r=range.firstRow(); r<=range.lastRow(); ++r) {
770 for (int c=range.firstColumn(); c<=range.lastColumn(); ++c) {
771 if (!(r==row && c==column)) {
772 if(Cell *cell = cellAt(r, c)) {
773 cell->d_ptr->formula = sf;
774 } else {
775 QSharedPointer<Cell> newCell = QSharedPointer<Cell>(new Cell(result, Cell::NumberType, fmt, this));
776 newCell->d_ptr->formula = sf;
777 d->cellTable[r][c] = newCell;
778 }
779 }
780 }
781 }
782 } else if (formula.formulaType() == CellFormula::SharedType) {
783
784 }
785
786 return true;
787 }
788
789 /*!
790 \overload
791 Write a empty cell \a row_column with the \a format.
792 Returns true on success.
793 */
794 bool Worksheet::writeBlank(const CellReference &row_column, const Format &format)
795 {
796 if (!row_column.isValid())
797 return false;
798
799 return writeBlank(row_column.row(), row_column.column(), format);
800 }
801
802 /*!
803 Write a empty cell (\a row, \a column) with the \a format.
804 Returns true on success.
805 */
806 bool Worksheet::writeBlank(int row, int column, const Format &format)
807 {
808 Q_D(Worksheet);
809 if (d->checkDimensions(row, column))
810 return false;
811
812 Format fmt = format.isValid() ? format : d->cellFormat(row, column);
813 d->workbook->styles()->addXfFormat(fmt);
814
815 //Note: NumberType with an invalid QVariant value means blank.
816 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(QVariant(), Cell::NumberType, fmt, this));
817
818 return true;
819 }
820 /*!
821 \overload
822 Write a bool \a value to the cell \a row_column with the \a format.
823 Returns true on success.
824 */
825 bool Worksheet::writeBool(const CellReference &row_column, bool value, const Format &format)
826 {
827 if (!row_column.isValid())
828 return false;
829
830 return writeBool(row_column.row(), row_column.column(), value, format);
831 }
832
833 /*!
834 Write a bool \a value to the cell (\a row, \a column) with the \a format.
835 Returns true on success.
836 */
837 bool Worksheet::writeBool(int row, int column, bool value, const Format &format)
838 {
839 Q_D(Worksheet);
840 if (d->checkDimensions(row, column))
841 return false;
842
843 Format fmt = format.isValid() ? format : d->cellFormat(row, column);
844 d->workbook->styles()->addXfFormat(fmt);
845 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::BooleanType, fmt, this));
846
847 return true;
848 }
849 /*!
850 \overload
851 Write a QDateTime \a dt to the cell \a row_column with the \a format.
852 Returns true on success.
853 */
854 bool Worksheet::writeDateTime(const CellReference &row_column, const QDateTime &dt, const Format &format)
855 {
856 if (!row_column.isValid())
857 return false;
858
859 return writeDateTime(row_column.row(), row_column.column(), dt, format);
860 }
861
862 /*!
863 Write a QDateTime \a dt to the cell (\a row, \a column) with the \a format.
864 Returns true on success.
865 */
866 bool Worksheet::writeDateTime(int row, int column, const QDateTime &dt, const Format &format)
867 {
868 Q_D(Worksheet);
869 if (d->checkDimensions(row, column))
870 return false;
871
872 Format fmt = format.isValid() ? format : d->cellFormat(row, column);
873 if (!fmt.isValid() || !fmt.isDateTimeFormat())
874 fmt.setNumberFormat(d->workbook->defaultDateFormat());
875 d->workbook->styles()->addXfFormat(fmt);
876
877 double value = datetimeToNumber(dt, d->workbook->isDate1904());
878
879 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(value, Cell::NumberType, fmt, this));
880
881 return true;
882 }
883
884 /*!
885 \overload
886 Write a QTime \a t to the cell \a row_column with the \a format.
887 Returns true on success.
888 */
889 bool Worksheet::writeTime(const CellReference &row_column, const QTime &t, const Format &format)
890 {
891 if (!row_column.isValid())
892 return false;
893
894 return writeTime(row_column.row(), row_column.column(), t, format);
895 }
896
897 /*!
898 Write a QTime \a t to the cell (\a row, \a column) with the \a format.
899 Returns true on success.
900 */
901 bool Worksheet::writeTime(int row, int column, const QTime &t, const Format &format)
902 {
903 Q_D(Worksheet);
904 if (d->checkDimensions(row, column))
905 return false;
906
907 Format fmt = format.isValid() ? format : d->cellFormat(row, column);
908 if (!fmt.isValid() || !fmt.isDateTimeFormat())
909 fmt.setNumberFormat(QStringLiteral("hh:mm:ss"));
910 d->workbook->styles()->addXfFormat(fmt);
911
912 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(timeToNumber(t), Cell::NumberType, fmt, this));
913
914 return true;
915 }
916
917 /*!
918 \overload
919 Write a QUrl \a url to the cell \a row_column with the given \a format \a display and \a tip.
920 Returns true on success.
921 */
922 bool Worksheet::writeHyperlink(const CellReference &row_column, const QUrl &url, const Format &format, const QString &display, const QString &tip)
923 {
924 if (!row_column.isValid())
925 return false;
926
927 return writeHyperlink(row_column.row(), row_column.column(), url, format, display, tip);
928 }
929
930 /*!
931 Write a QUrl \a url to the cell (\a row, \a column) with the given \a format \a display and \a tip.
932 Returns true on success.
933 */
934 bool Worksheet::writeHyperlink(int row, int column, const QUrl &url, const Format &format, const QString &display, const QString &tip)
935 {
936 Q_D(Worksheet);
937 if (d->checkDimensions(row, column))
938 return false;
939
940 //int error = 0;
941
942 QString urlString = url.toString();
943
944 //Generate proper display string
945 QString displayString = display.isEmpty() ? urlString : display;
946 if (displayString.startsWith(QLatin1String("mailto:")))
947 displayString.replace(QLatin1String("mailto:"), QString());
948 if (displayString.size() > XLSX_STRING_MAX) {
949 displayString = displayString.left(XLSX_STRING_MAX);
950 //error = -2;
951 }
952
953 /*
954 Location within target. If target is a workbook (or this workbook)
955 this shall refer to a sheet and cell or a defined name. Can also
956 be an HTML anchor if target is HTML file.
957
958 c:\temp\file.xlsx#Sheet!A1
959 http://a.com/aaa.html#aaaaa
960 */
961 QString locationString;
962 if (url.hasFragment()) {
963 locationString = url.fragment();
964 urlString = url.toString(QUrl::RemoveFragment);
965 }
966
967 Format fmt = format.isValid() ? format : d->cellFormat(row, column);
968 //Given a default style for hyperlink
969 if (!fmt.isValid()) {
970 fmt.setFontColor(Qt::blue);
971 fmt.setFontUnderline(Format::FontUnderlineSingle);
972 }
973 d->workbook->styles()->addXfFormat(fmt);
974
975 //Write the hyperlink string as normal string.
976 d->sharedStrings()->addSharedString(displayString);
977 d->cellTable[row][column] = QSharedPointer<Cell>(new Cell(displayString, Cell::SharedStringType, fmt, this));
978
979 //Store the hyperlink data in a separate table
980 d->urlTable[row][column] = QSharedPointer<XlsxHyperlinkData>(new XlsxHyperlinkData(XlsxHyperlinkData::External, urlString, locationString, QString(), tip));
981
982 return true;
983 }
984
985 /*!
986 * Add one DataValidation \a validation to the sheet.
987 * Returns true on success.
988 */
989 bool Worksheet::addDataValidation(const DataValidation &validation)
990 {
991 Q_D(Worksheet);
992 if (validation.ranges().isEmpty() || validation.validationType()==DataValidation::None)
993 return false;
994
995 d->dataValidationsList.append(validation);
996 return true;
997 }
998
999 /*!
1000 * Add one ConditionalFormatting \a cf to the sheet.
1001 * Returns true on success.
1002 */
1003 bool Worksheet::addConditionalFormatting(const ConditionalFormatting &cf)
1004 {
1005 Q_D(Worksheet);
1006 if (cf.ranges().isEmpty())
1007 return false;
1008
1009 for (int i=0; i<cf.d->cfRules.size(); ++i) {
1010 const QSharedPointer<XlsxCfRuleData> &rule = cf.d->cfRules[i];
1011 if (!rule->dxfFormat.isEmpty())
1012 d->workbook->styles()->addDxfFormat(rule->dxfFormat);
1013 rule->priority = 1;
1014 }
1015 d->conditionalFormattingList.append(cf);
1016 return true;
1017 }
1018
1019 /*!
1020 * Insert an \a image at the position \a row, \a column
1021 * Returns true on success.
1022 */
1023 bool Worksheet::insertImage(int row, int column, const QImage &image)
1024 {
1025 Q_D(Worksheet);
1026
1027 if (image.isNull())
1028 return false;
1029
1030 if (!d->drawing)
1031 d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch));
1032
1033 DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing.data(), DrawingAnchor::Picture);
1034
1035 /*
1036 The size are expressed as English Metric Units (EMUs). There are
1037 12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per
1038 pixel
1039 */
1040 anchor->from = XlsxMarker(row, column, 0, 0);
1041 anchor->ext = QSize(image.width() * 9525, image.height() * 9525);
1042
1043 anchor->setObjectPicture(image);
1044 return true;
1045 }
1046
1047 /*!
1048 * Creates an chart with the given \a size and insert
1049 * at the position \a row, \a column.
1050 * The chart will be returned.
1051 */
1052 Chart *Worksheet::insertChart(int row, int column, const QSize &size)
1053 {
1054 Q_D(Worksheet);
1055
1056 if (!d->drawing)
1057 d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_NewFromScratch));
1058
1059 DrawingOneCellAnchor *anchor = new DrawingOneCellAnchor(d->drawing.data(), DrawingAnchor::Picture);
1060
1061 /*
1062 The size are expressed as English Metric Units (EMUs). There are
1063 12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per
1064 pixel
1065 */
1066 anchor->from = XlsxMarker(row, column, 0, 0);
1067 anchor->ext = size * 9525;
1068
1069 QSharedPointer<Chart> chart = QSharedPointer<Chart>(new Chart(this, F_NewFromScratch));
1070 anchor->setObjectGraphicFrame(chart);
1071
1072 return chart.data();
1073 }
1074
1075 /*!
1076 Merge a \a range of cells. The first cell should contain the data and the others should
1077 be blank. All cells will be applied the same style if a valid \a format is given.
1078 Returns true on success.
1079
1080 \note All cells except the top-left one will be cleared.
1081 */
1082 bool Worksheet::mergeCells(const CellRange &range, const Format &format)
1083 {
1084 Q_D(Worksheet);
1085 if (range.rowCount() < 2 && range.columnCount() < 2)
1086 return false;
1087
1088 if (d->checkDimensions(range.firstRow(), range.firstColumn()))
1089 return false;
1090
1091 if (format.isValid())
1092 d->workbook->styles()->addXfFormat(format);
1093
1094 for (int row = range.firstRow(); row <= range.lastRow(); ++row) {
1095 for (int col = range.firstColumn(); col <= range.lastColumn(); ++col) {
1096 if (row == range.firstRow() && col == range.firstColumn()) {
1097 Cell *cell = cellAt(row, col);
1098 if (cell) {
1099 if (format.isValid())
1100 cell->d_ptr->format = format;
1101 } else {
1102 writeBlank(row, col, format);
1103 }
1104 } else {
1105 writeBlank(row, col, format);
1106 }
1107 }
1108 }
1109
1110 d->merges.append(range);
1111 return true;
1112 }
1113
1114 /*!
1115 Unmerge the cells in the \a range. Returns true on success.
1116
1117 */
1118 bool Worksheet::unmergeCells(const CellRange &range)
1119 {
1120 Q_D(Worksheet);
1121 if (!d->merges.contains(range))
1122 return false;
1123
1124 d->merges.removeOne(range);
1125 return true;
1126 }
1127
1128 /*!
1129 Returns all the merged cells.
1130 */
1131 QList<CellRange> Worksheet::mergedCells() const
1132 {
1133 Q_D(const Worksheet);
1134 return d->merges;
1135 }
1136
1137 /*!
1138 * \internal
1139 */
1140 void Worksheet::saveToXmlFile(QIODevice *device) const
1141 {
1142 Q_D(const Worksheet);
1143 d->relationships->clear();
1144
1145 QXmlStreamWriter writer(device);
1146
1147 writer.writeStartDocument(QStringLiteral("1.0"), true);
1148 writer.writeStartElement(QStringLiteral("worksheet"));
1149 writer.writeAttribute(QStringLiteral("xmlns"), QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
1150 writer.writeAttribute(QStringLiteral("xmlns:r"), QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
1151
1152 //for Excel 2010
1153 // writer.writeAttribute("xmlns:mc", "http://schemas.openxmlformats.org/markup-compatibility/2006");
1154 // writer.writeAttribute("xmlns:x14ac", "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
1155 // writer.writeAttribute("mc:Ignorable", "x14ac");
1156
1157 writer.writeStartElement(QStringLiteral("dimension"));
1158 writer.writeAttribute(QStringLiteral("ref"), d->generateDimensionString());
1159 writer.writeEndElement();//dimension
1160
1161 writer.writeStartElement(QStringLiteral("sheetViews"));
1162 writer.writeStartElement(QStringLiteral("sheetView"));
1163 if (d->windowProtection)
1164 writer.writeAttribute(QStringLiteral("windowProtection"), QStringLiteral("1"));
1165 if (d->showFormulas)
1166 writer.writeAttribute(QStringLiteral("showFormulas"), QStringLiteral("1"));
1167 if (!d->showGridLines)
1168 writer.writeAttribute(QStringLiteral("showGridLines"), QStringLiteral("0"));
1169 if (!d->showRowColHeaders)
1170 writer.writeAttribute(QStringLiteral("showRowColHeaders"), QStringLiteral("0"));
1171 if (!d->showZeros)
1172 writer.writeAttribute(QStringLiteral("showZeros"), QStringLiteral("0"));
1173 if (d->rightToLeft)
1174 writer.writeAttribute(QStringLiteral("rightToLeft"), QStringLiteral("1"));
1175 if (d->tabSelected)
1176 writer.writeAttribute(QStringLiteral("tabSelected"), QStringLiteral("1"));
1177 if (!d->showRuler)
1178 writer.writeAttribute(QStringLiteral("showRuler"), QStringLiteral("0"));
1179 if (!d->showOutlineSymbols)
1180 writer.writeAttribute(QStringLiteral("showOutlineSymbols"), QStringLiteral("0"));
1181 if (!d->showWhiteSpace)
1182 writer.writeAttribute(QStringLiteral("showWhiteSpace"), QStringLiteral("0"));
1183 writer.writeAttribute(QStringLiteral("workbookViewId"), QStringLiteral("0"));
1184 writer.writeEndElement();//sheetView
1185 writer.writeEndElement();//sheetViews
1186
1187 writer.writeStartElement(QStringLiteral("sheetFormatPr"));
1188 writer.writeAttribute(QStringLiteral("defaultRowHeight"), QString::number(d->default_row_height));
1189 if (d->default_row_height != 15)
1190 writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1"));
1191 if (d->default_row_zeroed)
1192 writer.writeAttribute(QStringLiteral("zeroHeight"), QStringLiteral("1"));
1193 if (d->outline_row_level)
1194 writer.writeAttribute(QStringLiteral("outlineLevelRow"), QString::number(d->outline_row_level));
1195 if (d->outline_col_level)
1196 writer.writeAttribute(QStringLiteral("outlineLevelCol"), QString::number(d->outline_col_level));
1197 //for Excel 2010
1198 // writer.writeAttribute("x14ac:dyDescent", "0.25");
1199 writer.writeEndElement();//sheetFormatPr
1200
1201 if (!d->colsInfo.isEmpty()) {
1202 writer.writeStartElement(QStringLiteral("cols"));
1203 QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(d->colsInfo);
1204 while (it.hasNext()) {
1205 it.next();
1206 QSharedPointer<XlsxColumnInfo> col_info = it.value();
1207 writer.writeStartElement(QStringLiteral("col"));
1208 writer.writeAttribute(QStringLiteral("min"), QString::number(col_info->firstColumn));
1209 writer.writeAttribute(QStringLiteral("max"), QString::number(col_info->lastColumn));
1210 if (col_info->width)
1211 writer.writeAttribute(QStringLiteral("width"), QString::number(col_info->width, 'g', 15));
1212 if (!col_info->format.isEmpty())
1213 writer.writeAttribute(QStringLiteral("style"), QString::number(col_info->format.xfIndex()));
1214 if (col_info->hidden)
1215 writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1"));
1216 if (col_info->width)
1217 writer.writeAttribute(QStringLiteral("customWidth"), QStringLiteral("1"));
1218 if (col_info->outlineLevel)
1219 writer.writeAttribute(QStringLiteral("outlineLevel"), QString::number(col_info->outlineLevel));
1220 if (col_info->collapsed)
1221 writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1"));
1222 writer.writeEndElement();//col
1223 }
1224 writer.writeEndElement();//cols
1225 }
1226
1227 writer.writeStartElement(QStringLiteral("sheetData"));
1228 if (d->dimension.isValid())
1229 d->saveXmlSheetData(writer);
1230 writer.writeEndElement();//sheetData
1231
1232 d->saveXmlMergeCells(writer);
1233 foreach (const ConditionalFormatting cf, d->conditionalFormattingList)
1234 cf.saveToXml(writer);
1235 d->saveXmlDataValidations(writer);
1236 d->saveXmlHyperlinks(writer);
1237 d->saveXmlDrawings(writer);
1238
1239 writer.writeEndElement();//worksheet
1240 writer.writeEndDocument();
1241 }
1242
1243 void WorksheetPrivate::saveXmlSheetData(QXmlStreamWriter &writer) const
1244 {
1245 calculateSpans();
1246 for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) {
1247 if (!(cellTable.contains(row_num) || comments.contains(row_num) || rowsInfo.contains(row_num))) {
1248 //Only process rows with cell data / comments / formatting
1249 continue;
1250 }
1251
1252 int span_index = (row_num-1) / 16;
1253 QString span;
1254 if (row_spans.contains(span_index))
1255 span = row_spans[span_index];
1256
1257 writer.writeStartElement(QStringLiteral("row"));
1258 writer.writeAttribute(QStringLiteral("r"), QString::number(row_num));
1259
1260 if (!span.isEmpty())
1261 writer.writeAttribute(QStringLiteral("spans"), span);
1262
1263 if (rowsInfo.contains(row_num)) {
1264 QSharedPointer<XlsxRowInfo> rowInfo = rowsInfo[row_num];
1265 if (!rowInfo->format.isEmpty()) {
1266 writer.writeAttribute(QStringLiteral("s"), QString::number(rowInfo->format.xfIndex()));
1267 writer.writeAttribute(QStringLiteral("customFormat"), QStringLiteral("1"));
1268 }
1269 //!Todo: support customHeight from info struct
1270 //!Todo: where does this magic number '15' come from?
1271 if (rowInfo->customHeight) {
1272 writer.writeAttribute(QStringLiteral("ht"), QString::number(rowInfo->height));
1273 writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1"));
1274 } else {
1275 writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("0"));
1276 }
1277
1278 if (rowInfo->hidden)
1279 writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1"));
1280 if (rowInfo->outlineLevel > 0)
1281 writer.writeAttribute(QStringLiteral("outlineLevel"), QString::number(rowInfo->outlineLevel));
1282 if (rowInfo->collapsed)
1283 writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1"));
1284 }
1285
1286 //Write cell data if row contains filled cells
1287 if (cellTable.contains(row_num)) {
1288 for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) {
1289 if (cellTable[row_num].contains(col_num)) {
1290 saveXmlCellData(writer, row_num, col_num, cellTable[row_num][col_num]);
1291 }
1292 }
1293 }
1294 writer.writeEndElement(); //row
1295 }
1296 }
1297
1298 void WorksheetPrivate::saveXmlCellData(QXmlStreamWriter &writer, int row, int col, QSharedPointer<Cell> cell) const
1299 {
1300 //This is the innermost loop so efficiency is important.
1301 QString cell_pos = CellReference(row, col).toString();
1302
1303 writer.writeStartElement(QStringLiteral("c"));
1304 writer.writeAttribute(QStringLiteral("r"), cell_pos);
1305
1306 //Style used by the cell, row or col
1307 if (!cell->format().isEmpty())
1308 writer.writeAttribute(QStringLiteral("s"), QString::number(cell->format().xfIndex()));
1309 else if (rowsInfo.contains(row) && !rowsInfo[row]->format.isEmpty())
1310 writer.writeAttribute(QStringLiteral("s"), QString::number(rowsInfo[row]->format.xfIndex()));
1311 else if (colsInfoHelper.contains(col) && !colsInfoHelper[col]->format.isEmpty())
1312 writer.writeAttribute(QStringLiteral("s"), QString::number(colsInfoHelper[col]->format.xfIndex()));
1313
1314 if (cell->cellType() == Cell::SharedStringType) {
1315 int sst_idx;
1316 if (cell->isRichString())
1317 sst_idx = sharedStrings()->getSharedStringIndex(cell->d_ptr->richString);
1318 else
1319 sst_idx = sharedStrings()->getSharedStringIndex(cell->value().toString());
1320
1321 writer.writeAttribute(QStringLiteral("t"), QStringLiteral("s"));
1322 writer.writeTextElement(QStringLiteral("v"), QString::number(sst_idx));
1323 } else if (cell->cellType() == Cell::InlineStringType) {
1324 writer.writeAttribute(QStringLiteral("t"), QStringLiteral("inlineStr"));
1325 writer.writeStartElement(QStringLiteral("is"));
1326 if (cell->isRichString()) {
1327 //Rich text string
1328 RichString string = cell->d_ptr->richString;
1329 for (int i=0; i<string.fragmentCount(); ++i) {
1330 writer.writeStartElement(QStringLiteral("r"));
1331 if (string.fragmentFormat(i).hasFontData()) {
1332 writer.writeStartElement(QStringLiteral("rPr"));
1333 //:Todo
1334 writer.writeEndElement();// rPr
1335 }
1336 writer.writeStartElement(QStringLiteral("t"));
1337 if (isSpaceReserveNeeded(string.fragmentText(i)))
1338 writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
1339 writer.writeCharacters(string.fragmentText(i));
1340 writer.writeEndElement();// t
1341 writer.writeEndElement(); // r
1342 }
1343 } else {
1344 writer.writeStartElement(QStringLiteral("t"));
1345 QString string = cell->value().toString();
1346 if (isSpaceReserveNeeded(string))
1347 writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
1348 writer.writeCharacters(string);
1349 writer.writeEndElement(); // t
1350 }
1351 writer.writeEndElement();//is
1352 } else if (cell->cellType() == Cell::NumberType){
1353 if (cell->hasFormula())
1354 cell->formula().saveToXml(writer);
1355 if (cell->value().isValid()) {//note that, invalid value means 'v' is blank
1356 double value = cell->value().toDouble();
1357 writer.writeTextElement(QStringLiteral("v"), QString::number(value, 'g', 15));
1358 }
1359 } else if (cell->cellType() == Cell::StringType) {
1360 writer.writeAttribute(QStringLiteral("t"), QStringLiteral("str"));
1361 if (cell->hasFormula())
1362 cell->formula().saveToXml(writer);
1363 writer.writeTextElement(QStringLiteral("v"), cell->value().toString());
1364 } else if (cell->cellType() == Cell::BooleanType) {
1365 writer.writeAttribute(QStringLiteral("t"), QStringLiteral("b"));
1366 writer.writeTextElement(QStringLiteral("v"), cell->value().toBool() ? QStringLiteral("1") : QStringLiteral("0"));
1367 }
1368 writer.writeEndElement(); //c
1369 }
1370
1371 void WorksheetPrivate::saveXmlMergeCells(QXmlStreamWriter &writer) const
1372 {
1373 if (merges.isEmpty())
1374 return;
1375
1376 writer.writeStartElement(QStringLiteral("mergeCells"));
1377 writer.writeAttribute(QStringLiteral("count"), QString::number(merges.size()));
1378
1379 foreach (CellRange range, merges) {
1380 writer.writeEmptyElement(QStringLiteral("mergeCell"));
1381 writer.writeAttribute(QStringLiteral("ref"), range.toString());
1382 }
1383
1384 writer.writeEndElement(); //mergeCells
1385 }
1386
1387 void WorksheetPrivate::saveXmlDataValidations(QXmlStreamWriter &writer) const
1388 {
1389 if (dataValidationsList.isEmpty())
1390 return;
1391
1392 writer.writeStartElement(QStringLiteral("dataValidations"));
1393 writer.writeAttribute(QStringLiteral("count"), QString::number(dataValidationsList.size()));
1394
1395 foreach (DataValidation validation, dataValidationsList)
1396 validation.saveToXml(writer);
1397
1398 writer.writeEndElement(); //dataValidations
1399 }
1400
1401 void WorksheetPrivate::saveXmlHyperlinks(QXmlStreamWriter &writer) const
1402 {
1403 if (urlTable.isEmpty())
1404 return;
1405
1406 writer.writeStartElement(QStringLiteral("hyperlinks"));
1407 QMapIterator<int, QMap<int, QSharedPointer<XlsxHyperlinkData> > > it(urlTable);
1408 while (it.hasNext()) {
1409 it.next();
1410 int row = it.key();
1411 QMapIterator <int, QSharedPointer<XlsxHyperlinkData> > it2(it.value());
1412 while (it2.hasNext()) {
1413 it2.next();
1414 int col = it2.key();
1415 QSharedPointer<XlsxHyperlinkData> data = it2.value();
1416 QString ref = CellReference(row, col).toString();
1417 writer.writeEmptyElement(QStringLiteral("hyperlink"));
1418 writer.writeAttribute(QStringLiteral("ref"), ref);
1419 if (data->linkType == XlsxHyperlinkData::External) {
1420 //Update relationships
1421 relationships->addWorksheetRelationship(QStringLiteral("/hyperlink"), data->target, QStringLiteral("External"));
1422
1423 writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(relationships->count()));
1424 }
1425
1426 if (!data->location.isEmpty())
1427 writer.writeAttribute(QStringLiteral("location"), data->location);
1428 if (!data->display.isEmpty())
1429 writer.writeAttribute(QStringLiteral("display"), data->display);
1430 if (!data->tooltip.isEmpty())
1431 writer.writeAttribute(QStringLiteral("tooltip"), data->tooltip);
1432 }
1433 }
1434
1435 writer.writeEndElement();//hyperlinks
1436 }
1437
1438 void WorksheetPrivate::saveXmlDrawings(QXmlStreamWriter &writer) const
1439 {
1440 if (!drawing)
1441 return;
1442
1443 int idx = workbook->drawings().indexOf(drawing.data());
1444 relationships->addWorksheetRelationship(QStringLiteral("/drawing"), QStringLiteral("../drawings/drawing%1.xml").arg(idx+1));
1445
1446 writer.writeEmptyElement(QStringLiteral("drawing"));
1447 writer.writeAttribute(QStringLiteral("r:id"), QStringLiteral("rId%1").arg(relationships->count()));
1448 }
1449
1450 void WorksheetPrivate::splitColsInfo(int colFirst, int colLast)
1451 {
1452 // Split current columnInfo, for example, if "A:H" has been set,
1453 // we are trying to set "B:D", there should be "A", "B:D", "E:H".
1454 // This will be more complex if we try to set "C:F" after "B:D".
1455 {
1456 QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo);
1457 while (it.hasNext()) {
1458 it.next();
1459 QSharedPointer<XlsxColumnInfo> info = it.value();
1460 if (colFirst > info->firstColumn && colFirst <= info->lastColumn) {
1461 //split the range,
1462 QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info));
1463 info->lastColumn = colFirst - 1;
1464 info2->firstColumn = colFirst;
1465 colsInfo.insert(colFirst, info2);
1466 for (int c = info2->firstColumn; c <= info2->lastColumn; ++c)
1467 colsInfoHelper[c] = info2;
1468
1469 break;
1470 }
1471 }
1472 }
1473 {
1474 QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo);
1475 while (it.hasNext()) {
1476 it.next();
1477 QSharedPointer<XlsxColumnInfo> info = it.value();
1478 if (colLast >= info->firstColumn && colLast < info->lastColumn) {
1479 QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info));
1480 info->lastColumn = colLast;
1481 info2->firstColumn = colLast + 1;
1482 colsInfo.insert(colLast + 1, info2);
1483 for (int c = info2->firstColumn; c <= info2->lastColumn; ++c)
1484 colsInfoHelper[c] = info2;
1485
1486 break;
1487 }
1488 }
1489 }
1490 }
1491
1492 bool WorksheetPrivate::isColumnRangeValid(int colFirst, int colLast)
1493 {
1494 bool ignore_row = true;
1495 bool ignore_col = false;
1496
1497 if (colFirst > colLast)
1498 return false;
1499
1500 if (checkDimensions(1, colLast, ignore_row, ignore_col))
1501 return false;
1502 if (checkDimensions(1, colFirst, ignore_row, ignore_col))
1503 return false;
1504
1505 return true;
1506 }
1507
1508 QList<int> WorksheetPrivate ::getColumnIndexes(int colFirst, int colLast)
1509 {
1510 splitColsInfo(colFirst, colLast);
1511
1512 QList<int> nodes;
1513 nodes.append(colFirst);
1514 for (int col = colFirst; col <= colLast; ++col) {
1515 if (colsInfo.contains(col)) {
1516 if (nodes.last() != col)
1517 nodes.append(col);
1518 int nextCol = colsInfo[col]->lastColumn + 1;
1519 if (nextCol <= colLast)
1520 nodes.append(nextCol);
1521 }
1522 }
1523
1524 return nodes;
1525 }
1526
1527 /*!
1528 Sets width in characters of a \a range of columns to \a width.
1529 Returns true on success.
1530 */
1531 bool Worksheet::setColumnWidth(const CellRange &range, double width)
1532 {
1533 if (!range.isValid())
1534 return false;
1535
1536 return setColumnWidth(range.firstColumn(), range.lastColumn(), width);
1537 }
1538
1539 /*!
1540 Sets format property of a \a range of columns to \a format. Columns are 1-indexed.
1541 Returns true on success.
1542 */
1543 bool Worksheet::setColumnFormat(const CellRange& range, const Format &format)
1544 {
1545 if (!range.isValid())
1546 return false;
1547
1548 return setColumnFormat(range.firstColumn(), range.lastColumn(), format);
1549 }
1550
1551 /*!
1552 Sets hidden property of a \a range of columns to \a hidden. Columns are 1-indexed.
1553 Hidden columns are not visible.
1554 Returns true on success.
1555 */
1556 bool Worksheet::setColumnHidden(const CellRange &range, bool hidden)
1557 {
1558 if (!range.isValid())
1559 return false;
1560
1561 return setColumnHidden(range.firstColumn(), range.lastColumn(), hidden);
1562 }
1563
1564 /*!
1565 Sets width in characters for columns [\a colFirst, \a colLast] to \a width.
1566 Columns are 1-indexed.
1567 Returns true on success.
1568 */
1569 bool Worksheet::setColumnWidth(int colFirst, int colLast, double width)
1570 {
1571 Q_D(Worksheet);
1572
1573 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast);
1574 foreach(QSharedPointer<XlsxColumnInfo> columnInfo, columnInfoList)
1575 columnInfo->width = width;
1576
1577 return (columnInfoList.count() > 0);
1578 }
1579
1580 /*!
1581 Sets format property of a range of columns [\a colFirst, \a colLast] to \a format.
1582 Columns are 1-indexed.
1583 Returns true on success.
1584 */
1585 bool Worksheet::setColumnFormat(int colFirst, int colLast, const Format &format)
1586 {
1587 Q_D(Worksheet);
1588
1589 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast);
1590 foreach(QSharedPointer<XlsxColumnInfo> columnInfo, columnInfoList)
1591 columnInfo->format = format;
1592
1593 if(columnInfoList.count() > 0) {
1594 d->workbook->styles()->addXfFormat(format);
1595 return true;
1596 }
1597
1598 return false;
1599 }
1600
1601 /*!
1602 Sets hidden property of a range of columns [\a colFirst, \a colLast] to \a hidden.
1603 Columns are 1-indexed. Returns true on success.
1604 */
1605 bool Worksheet::setColumnHidden(int colFirst, int colLast, bool hidden)
1606 {
1607 Q_D(Worksheet);
1608
1609 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast);
1610 foreach(QSharedPointer<XlsxColumnInfo> columnInfo, columnInfoList)
1611 columnInfo->hidden = hidden;
1612
1613 return (columnInfoList.count() > 0);
1614 }
1615
1616 /*!
1617 Returns width of the \a column in characters of the normal font. Columns are 1-indexed.
1618 */
1619 double Worksheet::columnWidth(int column)
1620 {
1621 Q_D(Worksheet);
1622
1623 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column);
1624 if (columnInfoList.count() == 1)
1625 return columnInfoList.at(0)->width ;
1626
1627 return d->sheetFormatProps.defaultColWidth;
1628 }
1629
1630 /*!
1631 Returns formatting of the \a column. Columns are 1-indexed.
1632 */
1633 Format Worksheet::columnFormat(int column)
1634 {
1635 Q_D(Worksheet);
1636
1637 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column);
1638 if (columnInfoList.count() == 1)
1639 return columnInfoList.at(0)->format;
1640
1641 return Format();
1642 }
1643
1644 /*!
1645 Returns true if \a column is hidden. Columns are 1-indexed.
1646 */
1647 bool Worksheet::isColumnHidden(int column)
1648 {
1649 Q_D(Worksheet);
1650
1651 QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column);
1652 if (columnInfoList.count() == 1)
1653 return columnInfoList.at(0)->hidden;
1654
1655 return false;
1656 }
1657
1658 /*!
1659 Sets the \a height of the rows including and between \a rowFirst and \a rowLast.
1660 Row height measured in point size.
1661 Rows are 1-indexed.
1662
1663 Returns true if success.
1664 */
1665 bool Worksheet::setRowHeight(int rowFirst,int rowLast, double height)
1666 {
1667 Q_D(Worksheet);
1668
1669 QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast);
1670
1671 foreach(QSharedPointer<XlsxRowInfo> rowInfo, rowInfoList) {
1672 rowInfo->height = height;
1673 rowInfo->customHeight = true;
1674 }
1675
1676 return rowInfoList.count() > 0;
1677 }
1678
1679 /*!
1680 Sets the \a format of the rows including and between \a rowFirst and \a rowLast.
1681 Rows are 1-indexed.
1682
1683 Returns true if success.
1684 */
1685 bool Worksheet::setRowFormat(int rowFirst,int rowLast, const Format &format)
1686 {
1687 Q_D(Worksheet);
1688
1689 QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast);
1690
1691 foreach(QSharedPointer<XlsxRowInfo> rowInfo, rowInfoList)
1692 rowInfo->format = format;
1693
1694 d->workbook->styles()->addXfFormat(format);
1695 return rowInfoList.count() > 0;
1696 }
1697
1698 /*!
1699 Sets the \a hidden proeprty of the rows including and between \a rowFirst and \a rowLast.
1700 Rows are 1-indexed. If hidden is true rows will not be visible.
1701
1702 Returns true if success.
1703 */
1704 bool Worksheet::setRowHidden(int rowFirst,int rowLast, bool hidden)
1705 {
1706 Q_D(Worksheet);
1707
1708 QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast);
1709 foreach(QSharedPointer<XlsxRowInfo> rowInfo, rowInfoList)
1710 rowInfo->hidden = hidden;
1711
1712 return rowInfoList.count() > 0;
1713 }
1714
1715 /*!
1716 Returns height of \a row in points.
1717 */
1718 double Worksheet::rowHeight(int row)
1719 {
1720 Q_D(Worksheet);
1721 int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1;
1722
1723 if (d->checkDimensions(row, min_col, false, true) || !d->rowsInfo.contains(row))
1724 return d->sheetFormatProps.defaultRowHeight; //return default on invalid row
1725
1726
1727 return d->rowsInfo[row]->height;
1728 }
1729
1730 /*!
1731 Returns format of \a row.
1732 */
1733 Format Worksheet::rowFormat(int row)
1734 {
1735 Q_D(Worksheet);
1736 int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1;
1737 if (d->checkDimensions(row, min_col, false, true) || !d->rowsInfo.contains(row))
1738 return Format(); //return default on invalid row
1739
1740 return d->rowsInfo[row]->format;
1741 }
1742
1743 /*!
1744 Returns true if \a row is hidden.
1745 */
1746 bool Worksheet::isRowHidden(int row)
1747 {
1748 Q_D(Worksheet);
1749 int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1;
1750 if (d->checkDimensions(row, min_col, false, true) || !d->rowsInfo.contains(row))
1751 return false; //return default on invalid row
1752
1753 return d->rowsInfo[row]->hidden;
1754 }
1755
1756 /*!
1757 Groups rows from \a rowFirst to \a rowLast with the given \a collapsed.
1758
1759 Returns false if error occurs.
1760 */
1761 bool Worksheet::groupRows(int rowFirst, int rowLast, bool collapsed)
1762 {
1763 Q_D(Worksheet);
1764
1765 for (int row=rowFirst; row<=rowLast; ++row) {
1766 if (d->rowsInfo.contains(row)) {
1767 d->rowsInfo[row]->outlineLevel += 1;
1768 } else {
1769 QSharedPointer<XlsxRowInfo> info(new XlsxRowInfo);
1770 info->outlineLevel += 1;
1771 d->rowsInfo.insert(row, info);
1772 }
1773 if (collapsed)
1774 d->rowsInfo[row]->hidden = true;
1775 }
1776 if (collapsed) {
1777 if (!d->rowsInfo.contains(rowLast+1))
1778 d->rowsInfo.insert(rowLast+1, QSharedPointer<XlsxRowInfo>(new XlsxRowInfo));
1779 d->rowsInfo[rowLast+1]->collapsed = true;
1780 }
1781 return true;
1782 }
1783
1784 /*!
1785 \overload
1786
1787 Groups columns with the given \a range and \a collapsed.
1788 */
1789 bool Worksheet::groupColumns(const CellRange &range, bool collapsed)
1790 {
1791 if (!range.isValid())
1792 return false;
1793
1794 return groupColumns(range.firstColumn(), range.lastColumn(), collapsed);
1795 }
1796
1797 /*!
1798 Groups columns from \a colFirst to \a colLast with the given \a collapsed.
1799 Returns false if error occurs.
1800 */
1801 bool Worksheet::groupColumns(int colFirst, int colLast, bool collapsed)
1802 {
1803 Q_D(Worksheet);
1804
1805 d->splitColsInfo(colFirst, colLast);
1806
1807 QList<int> nodes;
1808 nodes.append(colFirst);
1809 for (int col = colFirst; col <= colLast; ++col) {
1810 if (d->colsInfo.contains(col)) {
1811 if (nodes.last() != col)
1812 nodes.append(col);
1813 int nextCol = d->colsInfo[col]->lastColumn + 1;
1814 if (nextCol <= colLast)
1815 nodes.append(nextCol);
1816 }
1817 }
1818
1819 for (int idx = 0; idx < nodes.size(); ++idx) {
1820 int colStart = nodes[idx];
1821 if (d->colsInfo.contains(colStart)) {
1822 QSharedPointer<XlsxColumnInfo> info = d->colsInfo[colStart];
1823 info->outlineLevel += 1;
1824 if (collapsed)
1825 info->hidden = true;
1826 } else {
1827 int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1;
1828 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd));
1829 info->outlineLevel += 1;
1830 d->colsInfo.insert(colFirst, info);
1831 if (collapsed)
1832 info->hidden = true;
1833 for (int c = colStart; c <= colEnd; ++c)
1834 d->colsInfoHelper[c] = info;
1835 }
1836 }
1837
1838 if (collapsed) {
1839 int col = colLast+1;
1840 d->splitColsInfo(col, col);
1841 if (d->colsInfo.contains(col))
1842 d->colsInfo[col]->collapsed = true;
1843 else {
1844 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(col, col));
1845 info->collapsed = true;
1846 d->colsInfo.insert(col, info);
1847 d->colsInfoHelper[col] = info;
1848 }
1849 }
1850
1851 return false;
1852 }
1853
1854 /*!
1855 Return the range that contains cell data.
1856 */
1857 CellRange Worksheet::dimension() const
1858 {
1859 Q_D(const Worksheet);
1860 return d->dimension;
1861 }
1862
1863 /*
1864 Convert the height of a cell from user's units to pixels. If the
1865 height hasn't been set by the user we use the default value. If
1866 the row is hidden it has a value of zero.
1867 */
1868 int WorksheetPrivate::rowPixelsSize(int row) const
1869 {
1870 double height;
1871 if (row_sizes.contains(row))
1872 height = row_sizes[row];
1873 else
1874 height = default_row_height;
1875 return static_cast<int>(4.0 / 3.0 *height);
1876 }
1877
1878 /*
1879 Convert the width of a cell from user's units to pixels. Excel rounds
1880 the column width to the nearest pixel. If the width hasn't been set
1881 by the user we use the default value. If the column is hidden it
1882 has a value of zero.
1883 */
1884 int WorksheetPrivate::colPixelsSize(int col) const
1885 {
1886 double max_digit_width = 7.0; //For Calabri 11
1887 double padding = 5.0;
1888 int pixels = 0;
1889
1890 if (col_sizes.contains(col)) {
1891 double width = col_sizes[col];
1892 if (width < 1)
1893 pixels = static_cast<int>(width * (max_digit_width + padding) + 0.5);
1894 else
1895 pixels = static_cast<int>(width * max_digit_width + 0.5) + padding;
1896 } else {
1897 pixels = 64;
1898 }
1899 return pixels;
1900 }
1901
1902 void WorksheetPrivate::loadXmlSheetData(QXmlStreamReader &reader)
1903 {
1904 Q_Q(Worksheet);
1905 Q_ASSERT(reader.name() == QLatin1String("sheetData"));
1906
1907 while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetData") && reader.tokenType() == QXmlStreamReader::EndElement)) {
1908 if (reader.readNextStartElement()) {
1909 if (reader.name() == QLatin1String("row")) {
1910 QXmlStreamAttributes attributes = reader.attributes();
1911
1912 if (attributes.hasAttribute(QLatin1String("customFormat"))
1913 || attributes.hasAttribute(QLatin1String("customHeight"))
1914 || attributes.hasAttribute(QLatin1String("hidden"))
1915 || attributes.hasAttribute(QLatin1String("outlineLevel"))
1916 || attributes.hasAttribute(QLatin1String("collapsed"))) {
1917
1918 QSharedPointer<XlsxRowInfo> info(new XlsxRowInfo);
1919 if (attributes.hasAttribute(QLatin1String("customFormat")) && attributes.hasAttribute(QLatin1String("s"))) {
1920 int idx = attributes.value(QLatin1String("s")).toString().toInt();
1921 info->format = workbook->styles()->xfFormat(idx);
1922 }
1923
1924 if (attributes.hasAttribute(QLatin1String("customHeight"))) {
1925 info->customHeight = attributes.value(QLatin1String("customHeight")) == QLatin1String("1");
1926 //Row height is only specified when customHeight is set
1927 if(attributes.hasAttribute(QLatin1String("ht"))) {
1928 info->height = attributes.value(QLatin1String("ht")).toString().toDouble();
1929 }
1930 }
1931
1932 //both "hidden" and "collapsed" default are false
1933 info->hidden = attributes.value(QLatin1String("hidden")) == QLatin1String("1");
1934 info->collapsed = attributes.value(QLatin1String("collapsed")) == QLatin1String("1");
1935
1936 if (attributes.hasAttribute(QLatin1String("outlineLevel")))
1937 info->outlineLevel = attributes.value(QLatin1String("outlineLevel")).toString().toInt();
1938
1939 //"r" is optional too.
1940 if (attributes.hasAttribute(QLatin1String("r"))) {
1941 int row = attributes.value(QLatin1String("r")).toString().toInt();
1942 rowsInfo[row] = info;
1943 }
1944 }
1945
1946 } else if (reader.name() == QLatin1String("c")) { //Cell
1947 QXmlStreamAttributes attributes = reader.attributes();
1948 QString r = attributes.value(QLatin1String("r")).toString();
1949 CellReference pos(r);
1950
1951 //get format
1952 Format format;
1953 if (attributes.hasAttribute(QLatin1String("s"))) { //"s" == style index
1954 int idx = attributes.value(QLatin1String("s")).toString().toInt();
1955 format = workbook->styles()->xfFormat(idx);
1956 ////Empty format exists in styles xf table of real .xlsx files, see issue #65.
1957 //if (!format.isValid())
1958 // qDebug()<<QStringLiteral("<c s=\"%1\">Invalid style index: ").arg(idx)<<idx;
1959 }
1960
1961 Cell::CellType cellType = Cell::NumberType;
1962 if (attributes.hasAttribute(QLatin1String("t"))) {
1963 QString typeString = attributes.value(QLatin1String("t")).toString();
1964 if (typeString == QLatin1String("s"))
1965 cellType = Cell::SharedStringType;
1966 else if (typeString == QLatin1String("inlineStr"))
1967 cellType = Cell::InlineStringType;
1968 else if (typeString == QLatin1String("str"))
1969 cellType = Cell::StringType;
1970 else if (typeString == QLatin1String("b"))
1971 cellType = Cell::BooleanType;
1972 else if (typeString == QLatin1String("e"))
1973 cellType = Cell::ErrorType;
1974 else
1975 cellType = Cell::NumberType;
1976 }
1977
1978 QSharedPointer<Cell> cell(new Cell(QVariant() ,cellType, format, q));
1979 while (!reader.atEnd() && !(reader.name() == QLatin1String("c") && reader.tokenType() == QXmlStreamReader::EndElement)) {
1980 if (reader.readNextStartElement()) {
1981 if (reader.name() == QLatin1String("f")) {
1982 CellFormula &formula = cell->d_func()->formula;
1983 formula.loadFromXml(reader);
1984 if (formula.formulaType() == CellFormula::SharedType && !formula.formulaText().isEmpty()) {
1985 sharedFormulaMap[formula.sharedIndex()] = formula;
1986 }
1987 } else if (reader.name() == QLatin1String("v")) {
1988 QString value = reader.readElementText();
1989 if (cellType == Cell::SharedStringType) {
1990 int sst_idx = value.toInt();
1991 sharedStrings()->incRefByStringIndex(sst_idx);
1992 RichString rs = sharedStrings()->getSharedString(sst_idx);
1993 cell->d_func()->value = rs.toPlainString();
1994 if (rs.isRichString())
1995 cell->d_func()->richString = rs;
1996 } else if (cellType == Cell::NumberType) {
1997 cell->d_func()->value = value.toDouble();
1998 } else if (cellType == Cell::BooleanType) {
1999 cell->d_func()->value = value.toInt() ? true : false;
2000 } else { //Cell::ErrorType and Cell::StringType
2001 cell->d_func()->value = value;
2002 }
2003 } else if (reader.name() == QLatin1String("is")) {
2004 while (!reader.atEnd() && !(reader.name() == QLatin1String("is") && reader.tokenType() == QXmlStreamReader::EndElement)) {
2005 if (reader.readNextStartElement()) {
2006 //:Todo, add rich text read support
2007 if (reader.name() == QLatin1String("t")) {
2008 cell->d_func()->value = reader.readElementText();
2009 }
2010 }
2011 }
2012 } else if (reader.name() == QLatin1String("extLst")) {
2013 //skip extLst element
2014 while (!reader.atEnd() && !(reader.name() == QLatin1String("extLst")
2015 && reader.tokenType() == QXmlStreamReader::EndElement)) {
2016 reader.readNextStartElement();
2017 }
2018 }
2019 }
2020 }
2021 cellTable[pos.row()][pos.column()] = cell;
2022 }
2023 }
2024 }
2025 }
2026
2027 void WorksheetPrivate::loadXmlColumnsInfo(QXmlStreamReader &reader)
2028 {
2029 Q_ASSERT(reader.name() == QLatin1String("cols"));
2030
2031 while (!reader.atEnd() && !(reader.name() == QLatin1String("cols") && reader.tokenType() == QXmlStreamReader::EndElement)) {
2032 reader.readNextStartElement();
2033 if (reader.tokenType() == QXmlStreamReader::StartElement) {
2034 if (reader.name() == QLatin1String("col")) {
2035 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo);
2036
2037 QXmlStreamAttributes colAttrs = reader.attributes();
2038 int min = colAttrs.value(QLatin1String("min")).toString().toInt();
2039 int max = colAttrs.value(QLatin1String("max")).toString().toInt();
2040 info->firstColumn = min;
2041 info->lastColumn = max;
2042
2043 //Flag indicating that the column width for the affected column(s) is different from the
2044 // default or has been manually set
2045 if(colAttrs.hasAttribute(QLatin1String("customWidth"))) {
2046 info->customWidth = colAttrs.value(QLatin1String("customWidth")) == QLatin1String("1");
2047 }
2048 //Note, node may have "width" without "customWidth"
2049 if (colAttrs.hasAttribute(QLatin1String("width"))) {
2050 double width = colAttrs.value(QLatin1String("width")).toString().toDouble();
2051 info->width = width;
2052 }
2053
2054 info->hidden = colAttrs.value(QLatin1String("hidden")) == QLatin1String("1");
2055 info->collapsed = colAttrs.value(QLatin1String("collapsed")) == QLatin1String("1");
2056
2057 if (colAttrs.hasAttribute(QLatin1String("style"))) {
2058 int idx = colAttrs.value(QLatin1String("style")).toString().toInt();
2059 info->format = workbook->styles()->xfFormat(idx);
2060 }
2061 if (colAttrs.hasAttribute(QLatin1String("outlineLevel")))
2062 info->outlineLevel = colAttrs.value(QLatin1String("outlineLevel")).toString().toInt();
2063
2064 colsInfo.insert(min, info);
2065 for (int col=min; col<=max; ++col)
2066 colsInfoHelper[col] = info;
2067 }
2068 }
2069 }
2070 }
2071
2072 void WorksheetPrivate::loadXmlMergeCells(QXmlStreamReader &reader)
2073 {
2074 Q_ASSERT(reader.name() == QLatin1String("mergeCells"));
2075
2076 QXmlStreamAttributes attributes = reader.attributes();
2077 int count = attributes.value(QLatin1String("count")).toString().toInt();
2078
2079 while (!reader.atEnd() && !(reader.name() == QLatin1String("mergeCells") && reader.tokenType() == QXmlStreamReader::EndElement)) {
2080 reader.readNextStartElement();
2081 if (reader.tokenType() == QXmlStreamReader::StartElement) {
2082 if (reader.name() == QLatin1String("mergeCell")) {
2083 QXmlStreamAttributes attrs = reader.attributes();
2084 QString rangeStr = attrs.value(QLatin1String("ref")).toString();
2085 merges.append(CellRange(rangeStr));
2086 }
2087 }
2088 }
2089
2090 if (merges.size() != count)
2091 qDebug("read merge cells error");
2092 }
2093
2094 void WorksheetPrivate::loadXmlDataValidations(QXmlStreamReader &reader)
2095 {
2096 Q_ASSERT(reader.name() == QLatin1String("dataValidations"));
2097 QXmlStreamAttributes attributes = reader.attributes();
2098 int count = attributes.value(QLatin1String("count")).toString().toInt();
2099
2100 while (!reader.atEnd() && !(reader.name() == QLatin1String("dataValidations")
2101 && reader.tokenType() == QXmlStreamReader::EndElement)) {
2102 reader.readNextStartElement();
2103 if (reader.tokenType() == QXmlStreamReader::StartElement
2104 && reader.name() == QLatin1String("dataValidation")) {
2105 dataValidationsList.append(DataValidation::loadFromXml(reader));
2106 }
2107 }
2108
2109 if (dataValidationsList.size() != count)
2110 qDebug("read data validation error");
2111 }
2112
2113 void WorksheetPrivate::loadXmlSheetViews(QXmlStreamReader &reader)
2114 {
2115 Q_ASSERT(reader.name() == QLatin1String("sheetViews"));
2116
2117 while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetViews")
2118 && reader.tokenType() == QXmlStreamReader::EndElement)) {
2119 reader.readNextStartElement();
2120 if (reader.tokenType() == QXmlStreamReader::StartElement && reader.name() == QLatin1String("sheetView")) {
2121 QXmlStreamAttributes attrs = reader.attributes();
2122 //default false
2123 windowProtection = attrs.value(QLatin1String("windowProtection")) == QLatin1String("1");
2124 showFormulas = attrs.value(QLatin1String("showFormulas")) == QLatin1String("1");
2125 rightToLeft = attrs.value(QLatin1String("rightToLeft")) == QLatin1String("1");
2126 tabSelected = attrs.value(QLatin1String("tabSelected")) == QLatin1String("1");
2127 //default true
2128 showGridLines = attrs.value(QLatin1String("showGridLines")) != QLatin1String("0");
2129 showRowColHeaders = attrs.value(QLatin1String("showRowColHeaders")) != QLatin1String("0");
2130 showZeros = attrs.value(QLatin1String("showZeros")) != QLatin1String("0");
2131 showRuler = attrs.value(QLatin1String("showRuler")) != QLatin1String("0");
2132 showOutlineSymbols = attrs.value(QLatin1String("showOutlineSymbols")) != QLatin1String("0");
2133 showWhiteSpace = attrs.value(QLatin1String("showWhiteSpace")) != QLatin1String("0");
2134 }
2135 }
2136 }
2137
2138 void WorksheetPrivate::loadXmlSheetFormatProps(QXmlStreamReader &reader)
2139 {
2140 Q_ASSERT(reader.name() == QLatin1String("sheetFormatPr"));
2141 QXmlStreamAttributes attributes = reader.attributes();
2142 XlsxSheetFormatProps formatProps;
2143
2144 //Retain default values
2145 foreach (QXmlStreamAttribute attrib, attributes) {
2146 if(attrib.name() == QLatin1String("baseColWidth") ) {
2147 formatProps.baseColWidth = attrib.value().toString().toInt();
2148 } else if(attrib.name() == QLatin1String("customHeight")) {
2149 formatProps.customHeight = attrib.value() == QLatin1String("1");
2150 } else if(attrib.name() == QLatin1String("defaultColWidth")) {
2151 formatProps.defaultColWidth = attrib.value().toString().toDouble();
2152 } else if(attrib.name() == QLatin1String("defaultRowHeight")) {
2153 formatProps.defaultRowHeight = attrib.value().toString().toDouble();
2154 } else if(attrib.name() == QLatin1String("outlineLevelCol")) {
2155 formatProps.outlineLevelCol = attrib.value().toString().toInt();
2156 } else if(attrib.name() == QLatin1String("outlineLevelRow")) {
2157 formatProps.outlineLevelRow = attrib.value().toString().toInt();
2158 } else if(attrib.name() == QLatin1String("thickBottom")) {
2159 formatProps.thickBottom = attrib.value() == QLatin1String("1");
2160 } else if(attrib.name() == QLatin1String("thickTop")) {
2161 formatProps.thickTop = attrib.value() == QLatin1String("1");
2162 } else if(attrib.name() == QLatin1String("zeroHeight")) {
2163 formatProps.zeroHeight = attrib.value() == QLatin1String("1");
2164 }
2165 }
2166
2167 if(formatProps.defaultColWidth == 0.0) { //not set
2168 formatProps.defaultColWidth = WorksheetPrivate::calculateColWidth(formatProps.baseColWidth);
2169 }
2170
2171 }
2172 double WorksheetPrivate::calculateColWidth(int characters)
2173 {
2174 //!Todo
2175 //Take normal style' font maximum width and add padding and margin pixels
2176 return characters + 0.5;
2177 }
2178
2179 void WorksheetPrivate::loadXmlHyperlinks(QXmlStreamReader &reader)
2180 {
2181 Q_ASSERT(reader.name() == QLatin1String("hyperlinks"));
2182
2183 while (!reader.atEnd() && !(reader.name() == QLatin1String("hyperlinks")
2184 && reader.tokenType() == QXmlStreamReader::EndElement)) {
2185 reader.readNextStartElement();
2186 if (reader.tokenType() == QXmlStreamReader::StartElement && reader.name() == QLatin1String("hyperlink")) {
2187 QXmlStreamAttributes attrs = reader.attributes();
2188 CellReference pos(attrs.value(QLatin1String("ref")).toString());
2189 if (pos.isValid()) { //Valid
2190 QSharedPointer<XlsxHyperlinkData> link(new XlsxHyperlinkData);
2191 link->display = attrs.value(QLatin1String("display")).toString();
2192 link->tooltip = attrs.value(QLatin1String("tooltip")).toString();
2193 link->location = attrs.value(QLatin1String("location")).toString();
2194
2195 if (attrs.hasAttribute(QLatin1String("r:id"))) {
2196 link->linkType = XlsxHyperlinkData::External;
2197 XlsxRelationship ship = relationships->getRelationshipById(attrs.value(QLatin1String("r:id")).toString());
2198 link->target = ship.target;
2199 } else {
2200 link->linkType = XlsxHyperlinkData::Internal;
2201 }
2202
2203 urlTable[pos.row()][pos.column()] = link;
2204 }
2205 }
2206 }
2207 }
2208
2209 QList <QSharedPointer<XlsxColumnInfo> > WorksheetPrivate::getColumnInfoList(int colFirst, int colLast)
2210 {
2211 QList <QSharedPointer<XlsxColumnInfo> > columnsInfoList;
2212 if(isColumnRangeValid(colFirst,colLast))
2213 {
2214 QList<int> nodes = getColumnIndexes(colFirst, colLast);
2215
2216 for (int idx = 0; idx < nodes.size(); ++idx) {
2217 int colStart = nodes[idx];
2218 if (colsInfo.contains(colStart)) {
2219 QSharedPointer<XlsxColumnInfo> info = colsInfo[colStart];
2220 columnsInfoList.append(info);
2221 } else {
2222 int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1;
2223 QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd));
2224 colsInfo.insert(colFirst, info);
2225 columnsInfoList.append(info);
2226 for (int c = colStart; c <= colEnd; ++c)
2227 colsInfoHelper[c] = info;
2228 }
2229 }
2230 }
2231
2232 return columnsInfoList;
2233 }
2234
2235 QList <QSharedPointer<XlsxRowInfo> > WorksheetPrivate::getRowInfoList(int rowFirst, int rowLast)
2236 {
2237 QList <QSharedPointer<XlsxRowInfo> > rowInfoList;
2238
2239 int min_col = dimension.firstColumn() < 1 ? 1 : dimension.firstColumn();
2240
2241 for(int row = rowFirst; row <= rowLast; ++row) {
2242 if (checkDimensions(row, min_col, false, true))
2243 continue;
2244
2245 QSharedPointer<XlsxRowInfo> rowInfo;
2246 if ((rowsInfo[row]).isNull()){
2247 rowsInfo[row] = QSharedPointer<XlsxRowInfo>(new XlsxRowInfo());
2248 }
2249 rowInfoList.append(rowsInfo[row]);
2250 }
2251
2252 return rowInfoList;
2253 }
2254
2255 bool Worksheet::loadFromXmlFile(QIODevice *device)
2256 {
2257 Q_D(Worksheet);
2258
2259 QXmlStreamReader reader(device);
2260 while (!reader.atEnd()) {
2261 reader.readNextStartElement();
2262 if (reader.tokenType() == QXmlStreamReader::StartElement) {
2263 if (reader.name() == QLatin1String("dimension")) {
2264 QXmlStreamAttributes attributes = reader.attributes();
2265 QString range = attributes.value(QLatin1String("ref")).toString();
2266 d->dimension = CellRange(range);
2267 } else if (reader.name() == QLatin1String("sheetViews")) {
2268 d->loadXmlSheetViews(reader);
2269 } else if (reader.name() == QLatin1String("sheetFormatPr")) {
2270 d->loadXmlSheetFormatProps(reader);
2271 } else if (reader.name() == QLatin1String("cols")) {
2272 d->loadXmlColumnsInfo(reader);
2273 } else if (reader.name() == QLatin1String("sheetData")) {
2274 d->loadXmlSheetData(reader);
2275 } else if (reader.name() == QLatin1String("mergeCells")) {
2276 d->loadXmlMergeCells(reader);
2277 } else if (reader.name() == QLatin1String("dataValidations")) {
2278 d->loadXmlDataValidations(reader);
2279 } else if (reader.name() == QLatin1String("conditionalFormatting")) {
2280 ConditionalFormatting cf;
2281 cf.loadFromXml(reader, workbook()->styles());
2282 d->conditionalFormattingList.append(cf);
2283 } else if (reader.name() == QLatin1String("hyperlinks")) {
2284 d->loadXmlHyperlinks(reader);
2285 } else if (reader.name() == QLatin1String("drawing")) {
2286 QString rId = reader.attributes().value(QStringLiteral("r:id")).toString();
2287 QString name = d->relationships->getRelationshipById(rId).target;
2288 QString path = QDir::cleanPath(splitPath(filePath())[0] + QLatin1String("/") + name);
2289 d->drawing = QSharedPointer<Drawing>(new Drawing(this, F_LoadFromExists));
2290 d->drawing->setFilePath(path);
2291 } else if (reader.name() == QLatin1String("extLst")) {
2292 //Todo: add extLst support
2293 while (!reader.atEnd() && !(reader.name() == QLatin1String("extLst")
2294 && reader.tokenType() == QXmlStreamReader::EndElement)) {
2295 reader.readNextStartElement();
2296 }
2297 }
2298 }
2299 }
2300
2301 d->validateDimension();
2302 return true;
2303 }
2304
2305 /*
2306 * Documents imported from Google Docs does not contain dimension data.
2307 */
2308 void WorksheetPrivate::validateDimension()
2309 {
2310 if (dimension.isValid() || cellTable.isEmpty())
2311 return;
2312
2313 int firstRow = cellTable.constBegin().key();
2314 int lastRow = (cellTable.constEnd()-1).key();
2315 int firstColumn = -1;
2316 int lastColumn = -1;
2317
2318 for (QMap<int, QMap<int, QSharedPointer<Cell> > >::const_iterator it = cellTable.begin(); it != cellTable.end(); ++it)
2319 {
2320 Q_ASSERT(!it.value().isEmpty());
2321
2322 if (firstColumn == -1 || it.value().constBegin().key() < firstColumn)
2323 firstColumn = it.value().constBegin().key();
2324
2325 if (lastColumn == -1 || (it.value().constEnd()-1).key() > lastColumn)
2326 lastColumn = (it.value().constEnd()-1).key();
2327 }
2328
2329 CellRange cr(firstRow, firstColumn, lastRow, lastColumn);
2330
2331 if (cr.isValid())
2332 dimension = cr;
2333 }
2334
2335 /*!
2336 * \internal
2337 * Unit test can use this member to get sharedString object.
2338 */
2339 SharedStrings *WorksheetPrivate::sharedStrings() const
2340 {
2341 return workbook->sharedStrings();
2342 }
2343
2344 QT_END_NAMESPACE_XLSX
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)