2881 lines
96 KiB
C++
2881 lines
96 KiB
C++
![]() |
// xlsxworksheet.cpp
|
||
|
|
||
|
#include "xlsxworksheet.h"
|
||
|
|
||
|
#include "xlsxcell.h"
|
||
|
#include "xlsxcell_p.h"
|
||
|
#include "xlsxcellformula.h"
|
||
|
#include "xlsxcellformula_p.h"
|
||
|
#include "xlsxcelllocation.h"
|
||
|
#include "xlsxcellrange.h"
|
||
|
#include "xlsxcellreference.h"
|
||
|
#include "xlsxchart.h"
|
||
|
#include "xlsxconditionalformatting_p.h"
|
||
|
#include "xlsxdrawing_p.h"
|
||
|
#include "xlsxdrawinganchor_p.h"
|
||
|
#include "xlsxformat.h"
|
||
|
#include "xlsxformat_p.h"
|
||
|
#include "xlsxrichstring.h"
|
||
|
#include "xlsxsharedstrings_p.h"
|
||
|
#include "xlsxstyles_p.h"
|
||
|
#include "xlsxutility_p.h"
|
||
|
#include "xlsxworkbook.h"
|
||
|
#include "xlsxworksheet_p.h"
|
||
|
|
||
|
#include <cmath>
|
||
|
|
||
|
#include <QBuffer>
|
||
|
#include <QDate>
|
||
|
#include <QDateTime>
|
||
|
#include <QDebug>
|
||
|
#include <QDir>
|
||
|
#include <QFile>
|
||
|
#include <QMap>
|
||
|
#include <QMapIterator>
|
||
|
#include <QPoint>
|
||
|
#include <QTextDocument>
|
||
|
#include <QTime>
|
||
|
#include <QUrl>
|
||
|
#include <QVariant>
|
||
|
#include <QXmlStreamReader>
|
||
|
#include <QXmlStreamWriter>
|
||
|
|
||
|
QT_BEGIN_NAMESPACE_XLSX
|
||
|
|
||
|
namespace {
|
||
|
const int XLSX_ROW_MAX = 1048576;
|
||
|
const int XLSX_COLUMN_MAX = 16384;
|
||
|
const int XLSX_STRING_MAX = 32767;
|
||
|
} // namespace
|
||
|
|
||
|
WorksheetPrivate::WorksheetPrivate(Worksheet *p, Worksheet::CreateFlag flag)
|
||
|
: AbstractSheetPrivate(p, flag)
|
||
|
, windowProtection(false)
|
||
|
, showFormulas(false)
|
||
|
, showGridLines(true)
|
||
|
, showRowColHeaders(true)
|
||
|
, showZeros(true)
|
||
|
, rightToLeft(false)
|
||
|
, tabSelected(false)
|
||
|
, showRuler(false)
|
||
|
, showOutlineSymbols(true)
|
||
|
, showWhiteSpace(true)
|
||
|
, urlPattern(QStringLiteral("^([fh]tt?ps?://)|(mailto:)|(file://)"))
|
||
|
{
|
||
|
}
|
||
|
|
||
|
WorksheetPrivate::~WorksheetPrivate()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Calculate the "spans" attribute of the <row> tag. This is an
|
||
|
XLSX optimisation and isn't strictly required. However, it
|
||
|
makes comparing files easier. The span is the same for each
|
||
|
block of 16 rows.
|
||
|
*/
|
||
|
void WorksheetPrivate::calculateSpans() const
|
||
|
{
|
||
|
row_spans.clear();
|
||
|
int span_min = XLSX_COLUMN_MAX + 1;
|
||
|
int span_max = -1;
|
||
|
|
||
|
for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) {
|
||
|
for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) {
|
||
|
if (cellTable.contains(row_num, col_num)) {
|
||
|
if (span_max == -1) {
|
||
|
span_min = col_num;
|
||
|
span_max = col_num;
|
||
|
} else {
|
||
|
if (col_num < span_min)
|
||
|
span_min = col_num;
|
||
|
else if (col_num > span_max)
|
||
|
span_max = col_num;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
auto cIt = comments.constFind(row_num);
|
||
|
if (cIt != comments.constEnd()) {
|
||
|
for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn();
|
||
|
col_num++) {
|
||
|
if (cIt->contains(col_num)) {
|
||
|
if (span_max == -1) {
|
||
|
span_min = col_num;
|
||
|
span_max = col_num;
|
||
|
} else {
|
||
|
if (col_num < span_min)
|
||
|
span_min = col_num;
|
||
|
else if (col_num > span_max)
|
||
|
span_max = col_num;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (row_num % 16 == 0 || row_num == dimension.lastRow()) {
|
||
|
if (span_max != -1) {
|
||
|
row_spans[row_num / 16] = QStringLiteral("%1:%2").arg(span_min).arg(span_max);
|
||
|
span_min = XLSX_COLUMN_MAX + 1;
|
||
|
span_max = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QString WorksheetPrivate::generateDimensionString() const
|
||
|
{
|
||
|
if (!dimension.isValid())
|
||
|
return QStringLiteral("A1");
|
||
|
else
|
||
|
return dimension.toString();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Check that row and col are valid and store the max and min
|
||
|
values for use in other methods/elements. The ignore_row /
|
||
|
ignore_col flags is used to indicate that we wish to perform
|
||
|
the dimension check without storing the value. The ignore
|
||
|
flags are use by setRow() and dataValidate.
|
||
|
*/
|
||
|
int WorksheetPrivate::checkDimensions(int row, int col, bool ignore_row, bool ignore_col)
|
||
|
{
|
||
|
Q_ASSERT_X(row != 0, "checkDimensions", "row should start from 1 instead of 0");
|
||
|
Q_ASSERT_X(col != 0, "checkDimensions", "column should start from 1 instead of 0");
|
||
|
|
||
|
if (row > XLSX_ROW_MAX || row < 1 || col > XLSX_COLUMN_MAX || col < 1)
|
||
|
return -1;
|
||
|
|
||
|
if (!ignore_row) {
|
||
|
if (row < dimension.firstRow() || dimension.firstRow() == -1)
|
||
|
dimension.setFirstRow(row);
|
||
|
if (row > dimension.lastRow())
|
||
|
dimension.setLastRow(row);
|
||
|
}
|
||
|
if (!ignore_col) {
|
||
|
if (col < dimension.firstColumn() || dimension.firstColumn() == -1)
|
||
|
dimension.setFirstColumn(col);
|
||
|
if (col > dimension.lastColumn())
|
||
|
dimension.setLastColumn(col);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\class Worksheet
|
||
|
\inmodule QtXlsx
|
||
|
\brief Represent one worksheet in the workbook.
|
||
|
*/
|
||
|
|
||
|
/*!
|
||
|
* \internal
|
||
|
*/
|
||
|
Worksheet::Worksheet(const QString &name, int id, Workbook *workbook, CreateFlag flag)
|
||
|
: AbstractSheet(name, id, workbook, new WorksheetPrivate(this, flag))
|
||
|
{
|
||
|
if (!workbook) // For unit test propose only. Ignore the memory leak.
|
||
|
d_func()->workbook = new Workbook(flag);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \internal
|
||
|
*
|
||
|
* Make a copy of this sheet.
|
||
|
*/
|
||
|
|
||
|
Worksheet *Worksheet::copy(const QString &distName, int distId) const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
auto sheet = new Worksheet(distName, distId, d->workbook, F_NewFromScratch);
|
||
|
WorksheetPrivate *sheet_d = sheet->d_func();
|
||
|
|
||
|
sheet_d->dimension = d->dimension;
|
||
|
|
||
|
for (auto it = d->cellTable.cells.begin(); it != d->cellTable.cells.end(); ++it) {
|
||
|
int row = it.key();
|
||
|
for (auto it2 = it.value().begin(); it2 != it.value().end(); ++it2) {
|
||
|
int col = it2.key();
|
||
|
|
||
|
auto cell = std::make_shared<Cell>(it2.value().get());
|
||
|
cell->d_ptr->parent = sheet;
|
||
|
|
||
|
if (cell->cellType() == Cell::SharedStringType)
|
||
|
d->workbook->sharedStrings()->addSharedString(cell->d_ptr->richString);
|
||
|
|
||
|
sheet_d->cellTable.setValue(row, col, cell);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// for (auto it = d->cellTable.cells.begin(); it != d->cellTable.cells.end(); ++it) {
|
||
|
// auto cell = std::make_shared<Cell>(it.value().get());
|
||
|
// cell->d_ptr->parent = sheet;
|
||
|
|
||
|
// if (cell->cellType() == Cell::SharedStringType)
|
||
|
// d->workbook->sharedStrings()->addSharedString(cell->d_ptr->richString);
|
||
|
|
||
|
// sheet_d->cellTable.setValue(CellTable::row(it.key()), CellTable::column(it.key()), cell);
|
||
|
// }
|
||
|
|
||
|
sheet_d->merges = d->merges;
|
||
|
// sheet_d->rowsInfo = d->rowsInfo;
|
||
|
// sheet_d->colsInfo = d->colsInfo;
|
||
|
// sheet_d->colsInfoHelper = d->colsInfoHelper;
|
||
|
// sheet_d->dataValidationsList = d->dataValidationsList;
|
||
|
// sheet_d->conditionalFormattingList = d->conditionalFormattingList;
|
||
|
|
||
|
return sheet;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Destroys this workssheet.
|
||
|
*/
|
||
|
Worksheet::~Worksheet()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Returns whether sheet is protected.
|
||
|
*/
|
||
|
bool Worksheet::isWindowProtected() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->windowProtection;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Protects/unprotects the sheet based on \a protect.
|
||
|
*/
|
||
|
void Worksheet::setWindowProtected(bool protect)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->windowProtection = protect;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Return whether formulas instead of their calculated results shown in cells
|
||
|
*/
|
||
|
bool Worksheet::isFormulasVisible() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->showFormulas;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Show formulas in cells instead of their calculated results when \a visible is true.
|
||
|
*/
|
||
|
void Worksheet::setFormulasVisible(bool visible)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->showFormulas = visible;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Return whether gridlines is shown or not.
|
||
|
*/
|
||
|
bool Worksheet::isGridLinesVisible() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->showGridLines;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Show or hide the gridline based on \a visible
|
||
|
*/
|
||
|
void Worksheet::setGridLinesVisible(bool visible)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->showGridLines = visible;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Return whether is row and column headers is vislbe.
|
||
|
*/
|
||
|
bool Worksheet::isRowColumnHeadersVisible() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->showRowColHeaders;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Show or hide the row column headers based on \a visible
|
||
|
*/
|
||
|
void Worksheet::setRowColumnHeadersVisible(bool visible)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->showRowColHeaders = visible;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Return whether the sheet is shown right-to-left or not.
|
||
|
*/
|
||
|
bool Worksheet::isRightToLeft() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->rightToLeft;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Enable or disable the right-to-left based on \a enable.
|
||
|
*/
|
||
|
void Worksheet::setRightToLeft(bool enable)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->rightToLeft = enable;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Return whether is cells that have zero value show a zero.
|
||
|
*/
|
||
|
bool Worksheet::isZerosVisible() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->showZeros;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Show a zero in cells that have zero value if \a visible is true.
|
||
|
*/
|
||
|
void Worksheet::setZerosVisible(bool visible)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->showZeros = visible;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Return whether this tab is selected.
|
||
|
*/
|
||
|
bool Worksheet::isSelected() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->tabSelected;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Select this sheet if \a select is true.
|
||
|
*/
|
||
|
void Worksheet::setSelected(bool select)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->tabSelected = select;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Return whether is ruler is shown.
|
||
|
*/
|
||
|
bool Worksheet::isRulerVisible() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->showRuler;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Show or hide the ruler based on \a visible.
|
||
|
*/
|
||
|
void Worksheet::setRulerVisible(bool visible)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->showRuler = visible;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Return whether is outline symbols is shown.
|
||
|
*/
|
||
|
bool Worksheet::isOutlineSymbolsVisible() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->showOutlineSymbols;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Show or hide the outline symbols based ib \a visible.
|
||
|
*/
|
||
|
void Worksheet::setOutlineSymbolsVisible(bool visible)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->showOutlineSymbols = visible;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Return whether is white space is shown.
|
||
|
*/
|
||
|
bool Worksheet::isWhiteSpaceVisible() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->showWhiteSpace;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Show or hide the white space based on \a visible.
|
||
|
*/
|
||
|
void Worksheet::setWhiteSpaceVisible(bool visible)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
d->showWhiteSpace = visible;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Write \a value to cell (\a row, \a column) with the \a format.
|
||
|
* Both \a row and \a column are all 1-indexed value.
|
||
|
*
|
||
|
* Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::write(int row, int column, const QVariant &value, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
bool ret = true;
|
||
|
if (value.isNull()) {
|
||
|
// Blank
|
||
|
ret = writeBlank(row, column, format);
|
||
|
} else if (value.userType() == QMetaType::QString) {
|
||
|
// String
|
||
|
QString token = value.toString();
|
||
|
bool ok;
|
||
|
|
||
|
if (token.startsWith(QLatin1String("="))) {
|
||
|
// convert to formula
|
||
|
ret = writeFormula(row, column, CellFormula(token), format);
|
||
|
} else if (d->workbook->isStringsToHyperlinksEnabled() && token.contains(d->urlPattern)) {
|
||
|
// convert to url
|
||
|
ret = writeHyperlink(row, column, QUrl(token));
|
||
|
} else if (d->workbook->isStringsToNumbersEnabled() && (value.toDouble(&ok), ok)) {
|
||
|
// Try convert string to number if the flag enabled.
|
||
|
ret = writeNumeric(row, column, value.toDouble(), format);
|
||
|
} else {
|
||
|
// normal string now
|
||
|
ret = writeString(row, column, token, format);
|
||
|
}
|
||
|
} else if (value.userType() == qMetaTypeId<RichString>()) {
|
||
|
ret = writeString(row, column, value.value<RichString>(), format);
|
||
|
} else if (value.userType() == QMetaType::Int || value.userType() == QMetaType::UInt ||
|
||
|
value.userType() == QMetaType::LongLong ||
|
||
|
value.userType() == QMetaType::ULongLong || value.userType() == QMetaType::Double ||
|
||
|
value.userType() == QMetaType::Float) {
|
||
|
// Number
|
||
|
|
||
|
ret = writeNumeric(row, column, value.toDouble(), format);
|
||
|
} else if (value.userType() == QMetaType::Bool) {
|
||
|
// Bool
|
||
|
ret = writeBool(row, column, value.toBool(), format);
|
||
|
} else if (value.userType() == QMetaType::QDateTime) // dev67
|
||
|
{
|
||
|
// DateTime, Date
|
||
|
// note that, QTime can't convert to QDateTime
|
||
|
ret = writeDateTime(row, column, value.toDateTime(), format);
|
||
|
} else if (value.userType() == QMetaType::QDate) // dev67
|
||
|
{
|
||
|
ret = writeDate(row, column, value.toDate(), format);
|
||
|
} else if (value.userType() == QMetaType::QTime) {
|
||
|
// Time
|
||
|
ret = writeTime(row, column, value.toTime(), format);
|
||
|
} else if (value.userType() == QMetaType::QUrl) {
|
||
|
// Url
|
||
|
ret = writeHyperlink(row, column, value.toUrl(), format);
|
||
|
} else {
|
||
|
// Wrong type
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \overload
|
||
|
* Write \a value to cell \a row_column with the \a format.
|
||
|
* Both row and column are all 1-indexed value.
|
||
|
* Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::write(const CellReference &row_column, const QVariant &value, const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return write(row_column.row(), row_column.column(), value, format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
Return the contents of the cell \a row_column.
|
||
|
*/
|
||
|
QVariant Worksheet::read(const CellReference &row_column) const
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return QVariant();
|
||
|
|
||
|
return read(row_column.row(), row_column.column());
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Return the contents of the cell (\a row, \a column).
|
||
|
*/
|
||
|
QVariant Worksheet::read(int row, int column) const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
|
||
|
auto cell = cellAt(row, column);
|
||
|
if (!cell)
|
||
|
return QVariant();
|
||
|
|
||
|
if (cell->hasFormula()) {
|
||
|
if (cell->formula().formulaType() == CellFormula::NormalType) {
|
||
|
return QVariant(QLatin1String("=") + cell->formula().formulaText());
|
||
|
} else if (cell->formula().formulaType() == CellFormula::SharedType) {
|
||
|
if (!cell->formula().formulaText().isEmpty()) {
|
||
|
return QVariant(QLatin1String("=") + cell->formula().formulaText());
|
||
|
} else {
|
||
|
int si = cell->formula().sharedIndex();
|
||
|
const CellFormula &rootFormula = d->sharedFormulaMap[si];
|
||
|
CellReference rootCellRef = rootFormula.reference().topLeft();
|
||
|
QString rootFormulaText = rootFormula.formulaText();
|
||
|
QString newFormulaText =
|
||
|
convertSharedFormula(rootFormulaText, rootCellRef, CellReference(row, column));
|
||
|
return QVariant(QLatin1String("=") + newFormulaText);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (cell->isDateTime()) {
|
||
|
QVariant vDateTime = cell->dateTime();
|
||
|
return vDateTime;
|
||
|
}
|
||
|
|
||
|
return cell->value();
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Returns the cell at the given \a row_column. If there
|
||
|
* is no cell at the specified position, the function returns 0.
|
||
|
*/
|
||
|
std::shared_ptr<Cell> Worksheet::cellAt(const CellReference &row_column) const
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return {};
|
||
|
|
||
|
return cellAt(row_column.row(), row_column.column());
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Returns the cell at the given \a row and \a column. If there
|
||
|
* is no cell at the specified position, the function returns 0.
|
||
|
*/
|
||
|
std::shared_ptr<Cell> Worksheet::cellAt(int row, int col) const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->cellTable.cellAt(row, col);
|
||
|
}
|
||
|
|
||
|
Format WorksheetPrivate::cellFormat(int row, int col) const
|
||
|
{
|
||
|
auto cell = cellTable.cellAt(row, col);
|
||
|
if (cell) {
|
||
|
return cell->format();
|
||
|
}
|
||
|
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
Write string \a value to the cell \a row_column with the \a format.
|
||
|
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeString(const CellReference &row_column,
|
||
|
const RichString &value,
|
||
|
const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeString(row_column.row(), row_column.column(), value, format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Write string \a value to the cell (\a row, \a column) with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeString(int row, int column, const RichString &value, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
// QString content = value.toPlainString();
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
// if (content.size() > d->xls_strmax) {
|
||
|
// content = content.left(d->xls_strmax);
|
||
|
// error = -2;
|
||
|
// }
|
||
|
|
||
|
d->sharedStrings()->addSharedString(value);
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
if (value.fragmentCount() == 1 && value.fragmentFormat(0).isValid())
|
||
|
fmt.mergeFormat(value.fragmentFormat(0));
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
auto cell = std::make_shared<Cell>(value.toPlainString(), Cell::SharedStringType, fmt, this);
|
||
|
cell->d_ptr->richString = value;
|
||
|
d->cellTable.setValue(row, column, cell);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
Write string \a value to the cell \a row_column with the \a format.
|
||
|
*/
|
||
|
bool Worksheet::writeString(const CellReference &row_column,
|
||
|
const QString &value,
|
||
|
const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeString(row_column.row(), row_column.column(), value, format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
|
||
|
Write string \a value to the cell (\a row, \a column) with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeString(int row, int column, const QString &value, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
RichString rs;
|
||
|
if (d->workbook->isHtmlToRichStringEnabled() && Qt::mightBeRichText(value))
|
||
|
rs.setHtml(value);
|
||
|
else
|
||
|
rs.addFragment(value, Format());
|
||
|
|
||
|
return writeString(row, column, rs, format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
Write string \a value to the cell \a row_column with the \a format
|
||
|
*/
|
||
|
bool Worksheet::writeInlineString(const CellReference &row_column,
|
||
|
const QString &value,
|
||
|
const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeInlineString(row_column.row(), row_column.column(), value, format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Write string \a value to the cell (\a row, \a column) with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeInlineString(int row, int column, const QString &value, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
// int error = 0;
|
||
|
QString content = value;
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
if (value.size() > XLSX_STRING_MAX) {
|
||
|
content = value.left(XLSX_STRING_MAX);
|
||
|
// error = -2;
|
||
|
}
|
||
|
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
auto cell = std::make_shared<Cell>(value, Cell::InlineStringType, fmt, this);
|
||
|
d->cellTable.setValue(row, column, cell);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
Write numeric \a value to the cell \a row_column with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeNumeric(const CellReference &row_column, double value, const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeNumeric(row_column.row(), row_column.column(), value, format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Write numeric \a value to the cell (\a row, \a column) with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeNumeric(int row, int column, double value, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
auto cell = std::make_shared<Cell>(value, Cell::NumberType, fmt, this);
|
||
|
d->cellTable.setValue(row, column, cell);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
Write \a formula to the cell \a row_column with the \a format and \a result.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeFormula(const CellReference &row_column,
|
||
|
const CellFormula &formula,
|
||
|
const Format &format,
|
||
|
double result)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeFormula(row_column.row(), row_column.column(), formula, format, result);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Write \a formula_ to the cell (\a row, \a column) with the \a format and \a result.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeFormula(int row,
|
||
|
int column,
|
||
|
const CellFormula &formula_,
|
||
|
const Format &format,
|
||
|
double result)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
|
||
|
CellFormula formula = formula_;
|
||
|
formula.d->ca = true;
|
||
|
if (formula.formulaType() == CellFormula::SharedType) {
|
||
|
// Assign proper shared index for shared formula
|
||
|
int si = 0;
|
||
|
while (d->sharedFormulaMap.contains(si)) {
|
||
|
++si;
|
||
|
}
|
||
|
formula.d->si = si;
|
||
|
d->sharedFormulaMap[si] = formula;
|
||
|
}
|
||
|
|
||
|
auto data = std::make_shared<Cell>(result, Cell::NumberType, fmt, this);
|
||
|
data->d_ptr->formula = formula;
|
||
|
d->cellTable.setValue(row, column, data);
|
||
|
|
||
|
CellRange range = formula.reference();
|
||
|
if (formula.formulaType() == CellFormula::SharedType) {
|
||
|
CellFormula sf(QString(), CellFormula::SharedType);
|
||
|
sf.d->si = formula.sharedIndex();
|
||
|
for (int r = range.firstRow(); r <= range.lastRow(); ++r) {
|
||
|
for (int c = range.firstColumn(); c <= range.lastColumn(); ++c) {
|
||
|
if (!(r == row && c == column)) {
|
||
|
if (auto cell = cellAt(r, c)) {
|
||
|
cell->d_ptr->formula = sf;
|
||
|
} else {
|
||
|
auto newCell = std::make_shared<Cell>(result, Cell::NumberType, fmt, this);
|
||
|
newCell->d_ptr->formula = sf;
|
||
|
d->cellTable.setValue(row, column, newCell);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
Write a empty cell \a row_column with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeBlank(const CellReference &row_column, const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeBlank(row_column.row(), row_column.column(), format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Write a empty cell (\a row, \a column) with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeBlank(int row, int column, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
|
||
|
// Note: NumberType with an invalid QVariant value means blank.
|
||
|
auto cell = std::make_shared<Cell>(QVariant{}, Cell::NumberType, fmt, this);
|
||
|
d->cellTable.setValue(row, column, cell);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
/*!
|
||
|
\overload
|
||
|
Write a bool \a value to the cell \a row_column with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeBool(const CellReference &row_column, bool value, const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeBool(row_column.row(), row_column.column(), value, format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Write a bool \a value to the cell (\a row, \a column) with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeBool(int row, int column, bool value, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
auto cell = std::make_shared<Cell>(value, Cell::BooleanType, fmt, this);
|
||
|
d->cellTable.setValue(row, column, cell);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
/*!
|
||
|
\overload
|
||
|
Write a QDateTime \a dt to the cell \a row_column with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeDateTime(const CellReference &row_column,
|
||
|
const QDateTime &dt,
|
||
|
const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeDateTime(row_column.row(), row_column.column(), dt, format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Write a QDateTime \a dt to the cell (\a row, \a column) with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeDateTime(int row, int column, const QDateTime &dt, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
if (!fmt.isValid() || !fmt.isDateTimeFormat())
|
||
|
fmt.setNumberFormat(d->workbook->defaultDateFormat());
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
|
||
|
double value = datetimeToNumber(dt, d->workbook->isDate1904());
|
||
|
|
||
|
auto cell = std::make_shared<Cell>(value, Cell::NumberType, fmt, this);
|
||
|
d->cellTable.setValue(row, column, cell);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// dev67
|
||
|
bool Worksheet::writeDate(const CellReference &row_column, const QDate &dt, const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeDate(row_column.row(), row_column.column(), dt, format);
|
||
|
}
|
||
|
|
||
|
// dev67
|
||
|
bool Worksheet::writeDate(int row, int column, const QDate &dt, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
|
||
|
if (!fmt.isValid() || !fmt.isDateTimeFormat())
|
||
|
fmt.setNumberFormat(d->workbook->defaultDateFormat());
|
||
|
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
|
||
|
double value = datetimeToNumber(QDateTime(dt, QTime(0, 0, 0)), d->workbook->isDate1904());
|
||
|
|
||
|
auto cell = std::make_shared<Cell>(value, Cell::NumberType, fmt, this);
|
||
|
d->cellTable.setValue(row, column, cell);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
Write a QTime \a t to the cell \a row_column with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeTime(const CellReference &row_column, const QTime &t, const Format &format)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeTime(row_column.row(), row_column.column(), t, format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Write a QTime \a t to the cell (\a row, \a column) with the \a format.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeTime(int row, int column, const QTime &t, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
if (!fmt.isValid() || !fmt.isDateTimeFormat())
|
||
|
fmt.setNumberFormat(QStringLiteral("hh:mm:ss"));
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
|
||
|
auto cell = std::make_shared<Cell>(timeToNumber(t), Cell::NumberType, fmt, this);
|
||
|
d->cellTable.setValue(row, column, cell);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
Write a QUrl \a url to the cell \a row_column with the given \a format \a display and \a
|
||
|
tip. Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeHyperlink(const CellReference &row_column,
|
||
|
const QUrl &url,
|
||
|
const Format &format,
|
||
|
const QString &display,
|
||
|
const QString &tip)
|
||
|
{
|
||
|
if (!row_column.isValid())
|
||
|
return false;
|
||
|
|
||
|
return writeHyperlink(row_column.row(), row_column.column(), url, format, display, tip);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Write a QUrl \a url to the cell (\a row, \a column) with the given \a format \a display and
|
||
|
\a tip. Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::writeHyperlink(int row,
|
||
|
int column,
|
||
|
const QUrl &url,
|
||
|
const Format &format,
|
||
|
const QString &display,
|
||
|
const QString &tip)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (d->checkDimensions(row, column))
|
||
|
return false;
|
||
|
|
||
|
// int error = 0;
|
||
|
|
||
|
QString urlString = url.toString();
|
||
|
|
||
|
// Generate proper display string
|
||
|
QString displayString = display.isEmpty() ? urlString : display;
|
||
|
if (displayString.startsWith(QLatin1String("mailto:")))
|
||
|
displayString.replace(QLatin1String("mailto:"), QString());
|
||
|
if (displayString.size() > XLSX_STRING_MAX) {
|
||
|
displayString = displayString.left(XLSX_STRING_MAX);
|
||
|
// error = -2;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Location within target. If target is a workbook (or this workbook)
|
||
|
this shall refer to a sheet and cell or a defined name. Can also
|
||
|
be an HTML anchor if target is HTML file.
|
||
|
|
||
|
c:\temp\file.xlsx#Sheet!A1
|
||
|
http://a.com/aaa.html#aaaaa
|
||
|
*/
|
||
|
QString locationString;
|
||
|
if (url.hasFragment()) {
|
||
|
locationString = url.fragment();
|
||
|
urlString = url.toString(QUrl::RemoveFragment);
|
||
|
}
|
||
|
|
||
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
||
|
// Given a default style for hyperlink
|
||
|
if (!fmt.isValid()) {
|
||
|
fmt.setVerticalAlignment(Format::AlignVCenter);
|
||
|
fmt.setFontColor(Qt::blue);
|
||
|
fmt.setFontUnderline(Format::FontUnderlineSingle);
|
||
|
}
|
||
|
d->workbook->styles()->addXfFormat(fmt);
|
||
|
|
||
|
// Write the hyperlink string as normal string.
|
||
|
d->sharedStrings()->addSharedString(displayString);
|
||
|
auto cell = std::make_shared<Cell>(displayString, Cell::SharedStringType, fmt, this);
|
||
|
d->cellTable.setValue(row, column, cell);
|
||
|
|
||
|
// Store the hyperlink data in a separate table
|
||
|
d->urlTable[row][column] = std::make_shared<XlsxHyperlinkData>(
|
||
|
XlsxHyperlinkData::External, urlString, locationString, QString{}, tip);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Add one DataValidation \a validation to the sheet.
|
||
|
* Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::addDataValidation(const DataValidation &validation)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (validation.ranges().isEmpty() || validation.validationType() == DataValidation::None)
|
||
|
return false;
|
||
|
|
||
|
d->dataValidationsList.append(validation);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Add one ConditionalFormatting \a cf to the sheet.
|
||
|
* Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::addConditionalFormatting(const ConditionalFormatting &cf)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (cf.ranges().isEmpty())
|
||
|
return false;
|
||
|
|
||
|
for (int i = 0; i < cf.d->cfRules.size(); ++i) {
|
||
|
const std::shared_ptr<XlsxCfRuleData> &rule = cf.d->cfRules[i];
|
||
|
if (!rule->dxfFormat.isEmpty())
|
||
|
d->workbook->styles()->addDxfFormat(rule->dxfFormat);
|
||
|
rule->priority = 1;
|
||
|
}
|
||
|
d->conditionalFormattingList.append(cf);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Insert an \a image at the position \a row, \a column
|
||
|
* Returns true on success.
|
||
|
*/
|
||
|
int Worksheet::insertImage(int row, int column, const QImage &image)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
int imageIndex = 0;
|
||
|
|
||
|
if (image.isNull())
|
||
|
return imageIndex;
|
||
|
|
||
|
if (!d->drawing) {
|
||
|
d->drawing = std::make_shared<Drawing>(this, F_NewFromScratch);
|
||
|
}
|
||
|
|
||
|
auto anchor = new DrawingOneCellAnchor(d->drawing.get(), DrawingAnchor::Picture);
|
||
|
|
||
|
/*
|
||
|
The size are expressed as English Metric Units (EMUs).
|
||
|
EMU is 1/360 000 of centimiter.
|
||
|
*/
|
||
|
anchor->from = XlsxMarker(row, column, 0, 0);
|
||
|
float scaleX = 36e6F / std::max(1, image.dotsPerMeterX());
|
||
|
float scaleY = 36e6F / std::max(1, image.dotsPerMeterY());
|
||
|
anchor->ext = QSize(int(image.width() * scaleX), int(image.height() * scaleY));
|
||
|
|
||
|
anchor->setObjectPicture(image);
|
||
|
|
||
|
imageIndex = anchor->getm_id();
|
||
|
|
||
|
return imageIndex;
|
||
|
}
|
||
|
|
||
|
bool Worksheet::getImage(int imageIndex, QImage &img)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
if (imageIndex <= (-1)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (d->drawing == nullptr) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int realImageIndex = imageIndex - 1; // minus one
|
||
|
|
||
|
DrawingAnchor *danchor = d->drawing->anchors.at(realImageIndex);
|
||
|
// std::shared_ptr<Drawing> // for multithread
|
||
|
if (danchor == nullptr) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ret = danchor->getObjectPicture(img);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
bool Worksheet::getImage(int row, int column, QImage &img)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
if (d->drawing == nullptr) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < d->drawing->anchors.size(); i++) {
|
||
|
if (d->drawing->anchors[i]->row() == row && d->drawing->anchors[i]->col() == column) {
|
||
|
DrawingAnchor *danchor = d->drawing->anchors.at(i);
|
||
|
|
||
|
if (danchor == nullptr) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool ret = danchor->getObjectPicture(img);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
uint Worksheet::getImageCount()
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
if (d->drawing == nullptr) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
int size = d->drawing->anchors.size();
|
||
|
return uint(size);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* Creates an chart with the given \a size and insert
|
||
|
* at the position \a row, \a column.
|
||
|
* The chart will be returned.
|
||
|
*/
|
||
|
Chart *Worksheet::insertChart(int row, int column, const QSize &size)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
if (!d->drawing)
|
||
|
d->drawing = std::make_shared<Drawing>(this, F_NewFromScratch);
|
||
|
|
||
|
auto anchor = new DrawingOneCellAnchor(d->drawing.get(), DrawingAnchor::Picture);
|
||
|
|
||
|
/*
|
||
|
The size are expressed as English Metric Units (EMUs). There are
|
||
|
12,700 EMUs per point. Therefore, 12,700 * 3 /4 = 9,525 EMUs per
|
||
|
pixel
|
||
|
*/
|
||
|
anchor->from = XlsxMarker(row, column, 0, 0);
|
||
|
anchor->ext = size * 9525;
|
||
|
|
||
|
auto chart = std::shared_ptr<Chart>(new Chart(this, F_NewFromScratch));
|
||
|
anchor->setObjectGraphicFrame(chart);
|
||
|
|
||
|
return chart.get();
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Merge a \a range of cells. The first cell should contain the data and the others should
|
||
|
be blank. All cells will be applied the same style if a valid \a format is given.
|
||
|
Returns true on success.
|
||
|
|
||
|
\note All cells except the top-left one will be cleared.
|
||
|
*/
|
||
|
bool Worksheet::mergeCells(const CellRange &range, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
if (range.rowCount() < 2 && range.columnCount() < 2)
|
||
|
return false;
|
||
|
|
||
|
if (d->checkDimensions(range.firstRow(), range.firstColumn()))
|
||
|
return false;
|
||
|
|
||
|
if (format.isValid()) {
|
||
|
d->workbook->styles()->addXfFormat(format);
|
||
|
}
|
||
|
|
||
|
for (int row = range.firstRow(); row <= range.lastRow(); ++row) {
|
||
|
for (int col = range.firstColumn(); col <= range.lastColumn(); ++col) {
|
||
|
if (row == range.firstRow() && col == range.firstColumn()) {
|
||
|
auto cell = cellAt(row, col);
|
||
|
if (cell) {
|
||
|
if (format.isValid())
|
||
|
cell->d_ptr->format = format;
|
||
|
} else {
|
||
|
writeBlank(row, col, format);
|
||
|
}
|
||
|
} else {
|
||
|
writeBlank(row, col, format);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
d->merges.append(range);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Unmerge the cells in the \a range. Returns true on success.
|
||
|
|
||
|
*/
|
||
|
bool Worksheet::unmergeCells(const CellRange &range)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
return d->merges.removeOne(range);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Returns all the merged cells.
|
||
|
*/
|
||
|
QList<CellRange> Worksheet::mergedCells() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
|
||
|
// dev57
|
||
|
|
||
|
QList<CellRange> emptyList;
|
||
|
|
||
|
if (d->type == AbstractSheet::ST_WorkSheet) {
|
||
|
return d->merges;
|
||
|
} else if (d->type == AbstractSheet::ST_ChartSheet) {
|
||
|
} else if (d->type == AbstractSheet::ST_DialogSheet) {
|
||
|
} else if (d->type == AbstractSheet::ST_MacroSheet) {
|
||
|
} else { // undefined
|
||
|
}
|
||
|
|
||
|
return emptyList;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \internal
|
||
|
*/
|
||
|
void Worksheet::saveToXmlFile(QIODevice *device) const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
d->relationships->clear();
|
||
|
|
||
|
QXmlStreamWriter writer(device);
|
||
|
|
||
|
writer.writeStartDocument(QStringLiteral("1.0"), true);
|
||
|
writer.writeStartElement(QStringLiteral("worksheet"));
|
||
|
writer.writeAttribute(
|
||
|
QStringLiteral("xmlns"),
|
||
|
QStringLiteral("http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
|
||
|
writer.writeAttribute(
|
||
|
QStringLiteral("xmlns:r"),
|
||
|
QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
|
||
|
|
||
|
// for Excel 2010
|
||
|
// writer.writeAttribute("xmlns:mc",
|
||
|
// "http://schemas.openxmlformats.org/markup-compatibility/2006");
|
||
|
// writer.writeAttribute("xmlns:x14ac",
|
||
|
// "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac");
|
||
|
// writer.writeAttribute("mc:Ignorable", "x14ac");
|
||
|
|
||
|
writer.writeStartElement(QStringLiteral("dimension"));
|
||
|
writer.writeAttribute(QStringLiteral("ref"), d->generateDimensionString());
|
||
|
writer.writeEndElement(); // dimension
|
||
|
|
||
|
writer.writeStartElement(QStringLiteral("sheetViews"));
|
||
|
writer.writeStartElement(QStringLiteral("sheetView"));
|
||
|
if (d->windowProtection)
|
||
|
writer.writeAttribute(QStringLiteral("windowProtection"), QStringLiteral("1"));
|
||
|
if (d->showFormulas)
|
||
|
writer.writeAttribute(QStringLiteral("showFormulas"), QStringLiteral("1"));
|
||
|
if (!d->showGridLines)
|
||
|
writer.writeAttribute(QStringLiteral("showGridLines"), QStringLiteral("0"));
|
||
|
if (!d->showRowColHeaders)
|
||
|
writer.writeAttribute(QStringLiteral("showRowColHeaders"), QStringLiteral("0"));
|
||
|
if (!d->showZeros)
|
||
|
writer.writeAttribute(QStringLiteral("showZeros"), QStringLiteral("0"));
|
||
|
if (d->rightToLeft)
|
||
|
writer.writeAttribute(QStringLiteral("rightToLeft"), QStringLiteral("1"));
|
||
|
if (d->tabSelected)
|
||
|
writer.writeAttribute(QStringLiteral("tabSelected"), QStringLiteral("1"));
|
||
|
if (!d->showRuler)
|
||
|
writer.writeAttribute(QStringLiteral("showRuler"), QStringLiteral("0"));
|
||
|
if (!d->showOutlineSymbols)
|
||
|
writer.writeAttribute(QStringLiteral("showOutlineSymbols"), QStringLiteral("0"));
|
||
|
if (!d->showWhiteSpace)
|
||
|
writer.writeAttribute(QStringLiteral("showWhiteSpace"), QStringLiteral("0"));
|
||
|
writer.writeAttribute(QStringLiteral("workbookViewId"), QStringLiteral("0"));
|
||
|
writer.writeEndElement(); // sheetView
|
||
|
writer.writeEndElement(); // sheetViews
|
||
|
|
||
|
writer.writeStartElement(QStringLiteral("sheetFormatPr"));
|
||
|
writer.writeAttribute(QStringLiteral("defaultRowHeight"),
|
||
|
QString::number(d->sheetFormatProps.defaultRowHeight));
|
||
|
writer.writeAttribute(QStringLiteral("customHeight"),
|
||
|
xsdBoolean(d->sheetFormatProps.customHeight));
|
||
|
writer.writeAttribute(QStringLiteral("zeroHeight"), xsdBoolean(d->sheetFormatProps.zeroHeight));
|
||
|
writer.writeAttribute(QStringLiteral("outlineLevelRow"),
|
||
|
QString::number(d->sheetFormatProps.outlineLevelRow));
|
||
|
writer.writeAttribute(QStringLiteral("outlineLevelCol"),
|
||
|
QString::number(d->sheetFormatProps.outlineLevelCol));
|
||
|
// for Excel 2010
|
||
|
// writer.writeAttribute("x14ac:dyDescent", "0.25");
|
||
|
writer.writeEndElement(); // sheetFormatPr
|
||
|
|
||
|
if (!d->colsInfo.isEmpty()) {
|
||
|
writer.writeStartElement(QStringLiteral("cols"));
|
||
|
|
||
|
for (auto it = d->colsInfo.begin(); it != d->colsInfo.end(); ++it) {
|
||
|
std::shared_ptr<XlsxColumnInfo> col_info = it.value();
|
||
|
writer.writeStartElement(QStringLiteral("col"));
|
||
|
writer.writeAttribute(QStringLiteral("min"), QString::number(col_info->firstColumn));
|
||
|
writer.writeAttribute(QStringLiteral("max"), QString::number(col_info->lastColumn));
|
||
|
if (col_info->width)
|
||
|
writer.writeAttribute(QStringLiteral("width"),
|
||
|
QString::number(col_info->width, 'g', 15));
|
||
|
if (!col_info->format.isEmpty())
|
||
|
writer.writeAttribute(QStringLiteral("style"),
|
||
|
QString::number(col_info->format.xfIndex()));
|
||
|
if (col_info->hidden)
|
||
|
writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1"));
|
||
|
if (col_info->width)
|
||
|
writer.writeAttribute(QStringLiteral("customWidth"), QStringLiteral("1"));
|
||
|
if (col_info->outlineLevel)
|
||
|
writer.writeAttribute(QStringLiteral("outlineLevel"),
|
||
|
QString::number(col_info->outlineLevel));
|
||
|
if (col_info->collapsed)
|
||
|
writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1"));
|
||
|
writer.writeEndElement(); // col
|
||
|
}
|
||
|
writer.writeEndElement(); // cols
|
||
|
}
|
||
|
|
||
|
writer.writeStartElement(QStringLiteral("sheetData"));
|
||
|
if (d->dimension.isValid())
|
||
|
d->saveXmlSheetData(writer);
|
||
|
writer.writeEndElement(); // sheetData
|
||
|
|
||
|
d->saveXmlMergeCells(writer);
|
||
|
for (const ConditionalFormatting &cf : d->conditionalFormattingList)
|
||
|
cf.saveToXml(writer);
|
||
|
d->saveXmlDataValidations(writer);
|
||
|
|
||
|
//{{ liufeijin : write pagesettings add by liufeijin 20181028
|
||
|
|
||
|
// fixed by j2doll [dev18]
|
||
|
// NOTE: empty element is not problem. but, empty structure of element is not parsed by Excel.
|
||
|
|
||
|
// pageMargins
|
||
|
if (false == d->PMleft.isEmpty() && false == d->PMright.isEmpty() &&
|
||
|
false == d->PMtop.isEmpty() && false == d->PMbotton.isEmpty() &&
|
||
|
false == d->PMheader.isEmpty() && false == d->PMfooter.isEmpty()) {
|
||
|
writer.writeStartElement(QStringLiteral("pageMargins"));
|
||
|
|
||
|
writer.writeAttribute(QStringLiteral("left"), d->PMleft);
|
||
|
writer.writeAttribute(QStringLiteral("right"), d->PMright);
|
||
|
writer.writeAttribute(QStringLiteral("top"), d->PMtop);
|
||
|
writer.writeAttribute(QStringLiteral("bottom"), d->PMbotton);
|
||
|
writer.writeAttribute(QStringLiteral("header"), d->PMheader);
|
||
|
writer.writeAttribute(QStringLiteral("footer"), d->PMfooter);
|
||
|
|
||
|
writer.writeEndElement(); // pageMargins
|
||
|
}
|
||
|
|
||
|
// dev57
|
||
|
if (!d->Prid.isEmpty()) {
|
||
|
writer.writeStartElement(QStringLiteral("pageSetup")); // pageSetup
|
||
|
|
||
|
writer.writeAttribute(QStringLiteral("r:id"), d->Prid);
|
||
|
|
||
|
if (!d->PverticalDpi.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("verticalDpi"), d->PverticalDpi);
|
||
|
}
|
||
|
|
||
|
if (!d->PhorizontalDpi.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("horizontalDpi"), d->PhorizontalDpi);
|
||
|
}
|
||
|
|
||
|
if (!d->PuseFirstPageNumber.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("useFirstPageNumber"), d->PuseFirstPageNumber);
|
||
|
}
|
||
|
|
||
|
if (!d->PfirstPageNumber.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("firstPageNumber"), d->PfirstPageNumber);
|
||
|
}
|
||
|
|
||
|
if (!d->Pscale.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("scale"), d->Pscale);
|
||
|
}
|
||
|
|
||
|
if (!d->PpaperSize.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("paperSize"), d->PpaperSize);
|
||
|
}
|
||
|
|
||
|
if (!d->Porientation.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("orientation"), d->Porientation);
|
||
|
}
|
||
|
|
||
|
if (!d->Pcopies.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("copies"), d->Pcopies);
|
||
|
}
|
||
|
|
||
|
writer.writeEndElement(); // pageSetup
|
||
|
|
||
|
} // if ( !d->Prid.isEmpty() )
|
||
|
|
||
|
// headerFooter
|
||
|
if (!(d->ModdHeader.isNull()) || !(d->MoodFooter.isNull())) {
|
||
|
writer.writeStartElement(QStringLiteral("headerFooter")); // headerFooter
|
||
|
|
||
|
if (!d->MoodalignWithMargins.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("alignWithMargins"), d->MoodalignWithMargins);
|
||
|
}
|
||
|
|
||
|
if (!d->ModdHeader.isNull()) {
|
||
|
writer.writeStartElement(QStringLiteral("oddHeader"));
|
||
|
writer.writeCharacters(d->ModdHeader);
|
||
|
writer.writeEndElement(); // oddHeader
|
||
|
}
|
||
|
|
||
|
if (!d->MoodFooter.isNull()) {
|
||
|
writer.writeTextElement(QStringLiteral("oddFooter"), d->MoodFooter);
|
||
|
}
|
||
|
|
||
|
writer.writeEndElement(); // headerFooter
|
||
|
}
|
||
|
|
||
|
d->saveXmlHyperlinks(writer);
|
||
|
d->saveXmlDrawings(writer);
|
||
|
|
||
|
writer.writeEndElement(); // worksheet
|
||
|
writer.writeEndDocument();
|
||
|
}
|
||
|
|
||
|
//{{ liufeijin
|
||
|
bool Worksheet::setStartPage(int spagen)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
d->PfirstPageNumber = QString::number(spagen);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
//}}
|
||
|
|
||
|
void WorksheetPrivate::saveXmlSheetData(QXmlStreamWriter &writer) const
|
||
|
{
|
||
|
calculateSpans();
|
||
|
|
||
|
for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) {
|
||
|
auto ctIt = cellTable.cells.constFind(row_num);
|
||
|
auto riIt = rowsInfo.constFind(row_num);
|
||
|
if (ctIt == cellTable.cells.constEnd() && riIt == rowsInfo.constEnd() &&
|
||
|
!comments.contains(row_num)) {
|
||
|
// Only process rows with cell data / comments / formatting
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
int span_index = (row_num - 1) / 16;
|
||
|
QString span;
|
||
|
auto rsIt = row_spans.constFind(span_index);
|
||
|
if (rsIt != row_spans.constEnd())
|
||
|
span = rsIt.value();
|
||
|
|
||
|
writer.writeStartElement(QStringLiteral("row"));
|
||
|
writer.writeAttribute(QStringLiteral("r"), QString::number(row_num));
|
||
|
|
||
|
if (!span.isEmpty())
|
||
|
writer.writeAttribute(QStringLiteral("spans"), span);
|
||
|
|
||
|
if (riIt != rowsInfo.constEnd()) {
|
||
|
std::shared_ptr<XlsxRowInfo> rowInfo = riIt.value();
|
||
|
if (!rowInfo->format.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("s"),
|
||
|
QString::number(rowInfo->format.xfIndex()));
|
||
|
writer.writeAttribute(QStringLiteral("customFormat"), QStringLiteral("1"));
|
||
|
}
|
||
|
|
||
|
//! Todo: support customHeight from info struct
|
||
|
//! Todo: where does this magic number '15' come from?
|
||
|
if (rowInfo->customHeight) {
|
||
|
writer.writeAttribute(QStringLiteral("ht"), QString::number(rowInfo->height));
|
||
|
writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1"));
|
||
|
} else {
|
||
|
writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("0"));
|
||
|
}
|
||
|
|
||
|
if (rowInfo->hidden)
|
||
|
writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1"));
|
||
|
if (rowInfo->outlineLevel > 0)
|
||
|
writer.writeAttribute(QStringLiteral("outlineLevel"),
|
||
|
QString::number(rowInfo->outlineLevel));
|
||
|
if (rowInfo->collapsed)
|
||
|
writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1"));
|
||
|
}
|
||
|
|
||
|
// Write cell data if row contains filled cells
|
||
|
if (ctIt != cellTable.cells.constEnd()) {
|
||
|
for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn();
|
||
|
col_num++) {
|
||
|
auto cellIt = ctIt->find(col_num);
|
||
|
if (cellIt != ctIt->end()) {
|
||
|
saveXmlCellData(writer, row_num, col_num, *cellIt);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
writer.writeEndElement(); // row
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::saveXmlCellData(QXmlStreamWriter &writer,
|
||
|
int row,
|
||
|
int col,
|
||
|
std::shared_ptr<Cell> cell) const
|
||
|
{
|
||
|
Q_Q(const Worksheet);
|
||
|
|
||
|
// This is the innermost loop so efficiency is important.
|
||
|
QString cell_pos = CellReference(row, col).toString();
|
||
|
|
||
|
writer.writeStartElement(QStringLiteral("c"));
|
||
|
writer.writeAttribute(QStringLiteral("r"), cell_pos);
|
||
|
|
||
|
// Style used by the cell, row or col
|
||
|
if (!cell->format().isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("s"), QString::number(cell->format().xfIndex()));
|
||
|
} else {
|
||
|
auto rIt = rowsInfo.constFind(row);
|
||
|
if (rIt != rowsInfo.constEnd() && !(*rIt)->format.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("s"), QString::number((*rIt)->format.xfIndex()));
|
||
|
} else {
|
||
|
auto cIt = colsInfoHelper.constFind(col);
|
||
|
if (cIt != colsInfoHelper.constEnd() && !(*cIt)->format.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("s"),
|
||
|
QString::number((*cIt)->format.xfIndex()));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (cell->cellType() == Cell::SharedStringType) // 's'
|
||
|
{
|
||
|
int sst_idx;
|
||
|
if (cell->isRichString())
|
||
|
sst_idx = sharedStrings()->getSharedStringIndex(cell->d_ptr->richString);
|
||
|
else
|
||
|
sst_idx = sharedStrings()->getSharedStringIndex(cell->value().toString());
|
||
|
|
||
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("s"));
|
||
|
writer.writeTextElement(QStringLiteral("v"), QString::number(sst_idx));
|
||
|
} else if (cell->cellType() == Cell::InlineStringType) // 'inlineStr'
|
||
|
{
|
||
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("inlineStr"));
|
||
|
writer.writeStartElement(QStringLiteral("is"));
|
||
|
if (cell->isRichString()) {
|
||
|
// Rich text string
|
||
|
RichString string = cell->d_ptr->richString;
|
||
|
for (int i = 0; i < string.fragmentCount(); ++i) {
|
||
|
writer.writeStartElement(QStringLiteral("r"));
|
||
|
if (string.fragmentFormat(i).hasFontData()) {
|
||
|
writer.writeStartElement(QStringLiteral("rPr"));
|
||
|
//: Todo
|
||
|
writer.writeEndElement(); // rPr
|
||
|
}
|
||
|
writer.writeStartElement(QStringLiteral("t"));
|
||
|
if (isSpaceReserveNeeded(string.fragmentText(i)))
|
||
|
writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
|
||
|
writer.writeCharacters(string.fragmentText(i));
|
||
|
writer.writeEndElement(); // t
|
||
|
writer.writeEndElement(); // r
|
||
|
}
|
||
|
} else {
|
||
|
writer.writeStartElement(QStringLiteral("t"));
|
||
|
QString string = cell->value().toString();
|
||
|
if (isSpaceReserveNeeded(string))
|
||
|
writer.writeAttribute(QStringLiteral("xml:space"), QStringLiteral("preserve"));
|
||
|
writer.writeCharacters(string);
|
||
|
writer.writeEndElement(); // t
|
||
|
}
|
||
|
writer.writeEndElement(); // is
|
||
|
} else if (cell->cellType() == Cell::NumberType) // 'n'
|
||
|
{
|
||
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("n")); // dev67
|
||
|
|
||
|
if (cell->hasFormula()) {
|
||
|
QString strFormula = cell->formula().d->formula;
|
||
|
Q_UNUSED(strFormula);
|
||
|
cell->formula().saveToXml(writer);
|
||
|
}
|
||
|
|
||
|
if (cell->value().isValid()) { // note that, invalid value means 'v' is blank
|
||
|
double value = cell->value().toDouble();
|
||
|
writer.writeTextElement(QStringLiteral("v"), QString::number(value, 'g', 15));
|
||
|
}
|
||
|
} else if (cell->cellType() == Cell::StringType) // 'str'
|
||
|
{
|
||
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("str"));
|
||
|
if (cell->hasFormula())
|
||
|
cell->formula().saveToXml(writer);
|
||
|
|
||
|
writer.writeTextElement(QStringLiteral("v"), cell->value().toString());
|
||
|
} else if (cell->cellType() == Cell::BooleanType) // 'b'
|
||
|
{
|
||
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("b"));
|
||
|
|
||
|
// dev34
|
||
|
|
||
|
if (cell->hasFormula()) {
|
||
|
QString strFormula = cell->formula().d->formula;
|
||
|
Q_UNUSED(strFormula);
|
||
|
cell->formula().saveToXml(writer);
|
||
|
}
|
||
|
|
||
|
writer.writeTextElement(QStringLiteral("v"),
|
||
|
cell->value().toBool() ? QStringLiteral("1") : QStringLiteral("0"));
|
||
|
} else if (cell->cellType() == Cell::DateType) // 'd'
|
||
|
{
|
||
|
// number type. see for 18.18.11 ST_CellType (Cell Type) more information.
|
||
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("n"));
|
||
|
writer.writeTextElement(QStringLiteral("v"), cell->value().toString());
|
||
|
|
||
|
} else if (cell->cellType() == Cell::ErrorType) // 'e'
|
||
|
{
|
||
|
writer.writeAttribute(QStringLiteral("t"), QStringLiteral("e"));
|
||
|
writer.writeTextElement(QStringLiteral("v"), cell->value().toString());
|
||
|
} else // if (cell->cellType() == Cell::CustomType)
|
||
|
{
|
||
|
// custom type
|
||
|
|
||
|
if (cell->hasFormula()) {
|
||
|
QString strFormula = cell->formula().d->formula;
|
||
|
Q_UNUSED(strFormula);
|
||
|
cell->formula().saveToXml(writer);
|
||
|
}
|
||
|
|
||
|
if (cell->value().isValid()) { // note that, invalid value means 'v' is blank
|
||
|
double value = cell->value().toDouble();
|
||
|
writer.writeTextElement(QStringLiteral("v"), QString::number(value, 'g', 15));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
writer.writeEndElement(); // c
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::saveXmlMergeCells(QXmlStreamWriter &writer) const
|
||
|
{
|
||
|
if (merges.isEmpty())
|
||
|
return;
|
||
|
|
||
|
writer.writeStartElement(QStringLiteral("mergeCells"));
|
||
|
writer.writeAttribute(QStringLiteral("count"), QString::number(merges.size()));
|
||
|
|
||
|
for (const CellRange &range : merges) {
|
||
|
writer.writeEmptyElement(QStringLiteral("mergeCell"));
|
||
|
writer.writeAttribute(QStringLiteral("ref"), range.toString());
|
||
|
}
|
||
|
|
||
|
writer.writeEndElement(); // mergeCells
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::saveXmlDataValidations(QXmlStreamWriter &writer) const
|
||
|
{
|
||
|
if (dataValidationsList.isEmpty())
|
||
|
return;
|
||
|
|
||
|
writer.writeStartElement(QStringLiteral("dataValidations"));
|
||
|
writer.writeAttribute(QStringLiteral("count"), QString::number(dataValidationsList.size()));
|
||
|
|
||
|
for (const DataValidation &validation : dataValidationsList)
|
||
|
validation.saveToXml(writer);
|
||
|
|
||
|
writer.writeEndElement(); // dataValidations
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::saveXmlHyperlinks(QXmlStreamWriter &writer) const
|
||
|
{
|
||
|
if (urlTable.isEmpty())
|
||
|
return;
|
||
|
|
||
|
writer.writeStartElement(QStringLiteral("hyperlinks"));
|
||
|
for (auto it = urlTable.begin(); it != urlTable.end(); ++it) {
|
||
|
int row = it.key();
|
||
|
|
||
|
for (auto it2 = it.value().begin(); it2 != it.value().end(); ++it2) {
|
||
|
int col = it2.key();
|
||
|
std::shared_ptr<XlsxHyperlinkData> data = it2.value();
|
||
|
QString ref = CellReference(row, col).toString();
|
||
|
|
||
|
// dev57
|
||
|
// writer.writeEmptyElement(QStringLiteral("hyperlink"));
|
||
|
writer.writeStartElement(QStringLiteral("hyperlink"));
|
||
|
|
||
|
writer.writeAttribute(QStringLiteral("ref"), ref); // required field
|
||
|
|
||
|
if (data->linkType == XlsxHyperlinkData::External) {
|
||
|
// Update relationships
|
||
|
relationships->addWorksheetRelationship(
|
||
|
QStringLiteral("/hyperlink"), data->target, QStringLiteral("External"));
|
||
|
|
||
|
writer.writeAttribute(QStringLiteral("r:id"),
|
||
|
QStringLiteral("rId%1").arg(relationships->count()));
|
||
|
}
|
||
|
|
||
|
if (!data->location.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("location"), data->location);
|
||
|
}
|
||
|
|
||
|
if (!data->display.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("display"), data->display);
|
||
|
}
|
||
|
|
||
|
if (!data->tooltip.isEmpty()) {
|
||
|
writer.writeAttribute(QStringLiteral("tooltip"), data->tooltip);
|
||
|
}
|
||
|
|
||
|
// dev57
|
||
|
writer.writeEndElement(); // hyperlink
|
||
|
}
|
||
|
}
|
||
|
|
||
|
writer.writeEndElement(); // hyperlinks
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::saveXmlDrawings(QXmlStreamWriter &writer) const
|
||
|
{
|
||
|
if (!drawing)
|
||
|
return;
|
||
|
|
||
|
int idx = workbook->drawings().indexOf(drawing.get());
|
||
|
relationships->addWorksheetRelationship(
|
||
|
QStringLiteral("/drawing"), QStringLiteral("../drawings/drawing%1.xml").arg(idx + 1));
|
||
|
|
||
|
writer.writeEmptyElement(QStringLiteral("drawing"));
|
||
|
writer.writeAttribute(QStringLiteral("r:id"),
|
||
|
QStringLiteral("rId%1").arg(relationships->count()));
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::splitColsInfo(int colFirst, int colLast)
|
||
|
{
|
||
|
// Split current columnInfo, for example, if "A:H" has been set,
|
||
|
// we are trying to set "B:D", there should be "A", "B:D", "E:H".
|
||
|
// This will be more complex if we try to set "C:F" after "B:D".
|
||
|
{
|
||
|
for (auto it = colsInfo.begin(); it != colsInfo.end(); ++it) {
|
||
|
std::shared_ptr<XlsxColumnInfo> info = it.value();
|
||
|
if (colFirst > info->firstColumn && colFirst <= info->lastColumn) {
|
||
|
// split the range,
|
||
|
std::shared_ptr<XlsxColumnInfo> info2(new XlsxColumnInfo(*info));
|
||
|
info->lastColumn = colFirst - 1;
|
||
|
info2->firstColumn = colFirst;
|
||
|
colsInfo.insert(colFirst, info2);
|
||
|
for (int c = info2->firstColumn; c <= info2->lastColumn; ++c)
|
||
|
colsInfoHelper[c] = info2;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
{
|
||
|
for (auto it = colsInfo.begin(); it != colsInfo.end(); ++it) {
|
||
|
std::shared_ptr<XlsxColumnInfo> info = it.value();
|
||
|
if (colLast >= info->firstColumn && colLast < info->lastColumn) {
|
||
|
std::shared_ptr<XlsxColumnInfo> info2(new XlsxColumnInfo(*info));
|
||
|
info->lastColumn = colLast;
|
||
|
info2->firstColumn = colLast + 1;
|
||
|
colsInfo.insert(colLast + 1, info2);
|
||
|
for (int c = info2->firstColumn; c <= info2->lastColumn; ++c)
|
||
|
colsInfoHelper[c] = info2;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool WorksheetPrivate::isColumnRangeValid(int colFirst, int colLast)
|
||
|
{
|
||
|
bool ignore_row = true;
|
||
|
bool ignore_col = false;
|
||
|
|
||
|
if (colFirst > colLast)
|
||
|
return false;
|
||
|
|
||
|
if (checkDimensions(1, colLast, ignore_row, ignore_col))
|
||
|
return false;
|
||
|
if (checkDimensions(1, colFirst, ignore_row, ignore_col))
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
QList<int> WorksheetPrivate ::getColumnIndexes(int colFirst, int colLast)
|
||
|
{
|
||
|
splitColsInfo(colFirst, colLast);
|
||
|
|
||
|
QList<int> nodes;
|
||
|
nodes.append(colFirst);
|
||
|
for (int col = colFirst; col <= colLast; ++col) {
|
||
|
auto it = colsInfo.constFind(col);
|
||
|
if (it != colsInfo.constEnd()) {
|
||
|
if (nodes.last() != col)
|
||
|
nodes.append(col);
|
||
|
|
||
|
int nextCol = (*it)->lastColumn + 1;
|
||
|
if (nextCol <= colLast)
|
||
|
nodes.append(nextCol);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nodes;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Sets width in characters of a \a range of columns to \a width.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::setColumnWidth(const CellRange &range, double width)
|
||
|
{
|
||
|
if (!range.isValid())
|
||
|
return false;
|
||
|
|
||
|
return setColumnWidth(range.firstColumn(), range.lastColumn(), width);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Sets format property of a \a range of columns to \a format. Columns are 1-indexed.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::setColumnFormat(const CellRange &range, const Format &format)
|
||
|
{
|
||
|
if (!range.isValid())
|
||
|
return false;
|
||
|
|
||
|
return setColumnFormat(range.firstColumn(), range.lastColumn(), format);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Sets hidden property of a \a range of columns to \a hidden. Columns are 1-indexed.
|
||
|
Hidden columns are not visible.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::setColumnHidden(const CellRange &range, bool hidden)
|
||
|
{
|
||
|
if (!range.isValid())
|
||
|
return false;
|
||
|
|
||
|
return setColumnHidden(range.firstColumn(), range.lastColumn(), hidden);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Sets width in characters for columns [\a colFirst, \a colLast] to \a width.
|
||
|
Columns are 1-indexed.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::setColumnWidth(int colFirst, int colLast, double width)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
const QList<std::shared_ptr<XlsxColumnInfo>> columnInfoList =
|
||
|
d->getColumnInfoList(colFirst, colLast);
|
||
|
for (const std::shared_ptr<XlsxColumnInfo> &columnInfo : columnInfoList) {
|
||
|
columnInfo->width = width;
|
||
|
}
|
||
|
|
||
|
return (columnInfoList.count() > 0);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Sets format property of a range of columns [\a colFirst, \a colLast] to \a format.
|
||
|
Columns are 1-indexed.
|
||
|
Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::setColumnFormat(int colFirst, int colLast, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
const QList<std::shared_ptr<XlsxColumnInfo>> columnInfoList =
|
||
|
d->getColumnInfoList(colFirst, colLast);
|
||
|
for (const std::shared_ptr<XlsxColumnInfo> &columnInfo : columnInfoList)
|
||
|
columnInfo->format = format;
|
||
|
|
||
|
if (columnInfoList.count() > 0) {
|
||
|
d->workbook->styles()->addXfFormat(format);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Sets hidden property of a range of columns [\a colFirst, \a colLast] to \a hidden.
|
||
|
Columns are 1-indexed. Returns true on success.
|
||
|
*/
|
||
|
bool Worksheet::setColumnHidden(int colFirst, int colLast, bool hidden)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
const QList<std::shared_ptr<XlsxColumnInfo>> columnInfoList =
|
||
|
d->getColumnInfoList(colFirst, colLast);
|
||
|
for (const std::shared_ptr<XlsxColumnInfo> &columnInfo : columnInfoList)
|
||
|
columnInfo->hidden = hidden;
|
||
|
|
||
|
return (columnInfoList.count() > 0);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Returns width of the \a column in characters of the normal font. Columns are 1-indexed.
|
||
|
*/
|
||
|
double Worksheet::columnWidth(int column)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
QList<std::shared_ptr<XlsxColumnInfo>> columnInfoList = d->getColumnInfoList(column, column);
|
||
|
|
||
|
if (columnInfoList.count() == 1) {
|
||
|
// column information is found
|
||
|
// qDebug() << "[debug]" << __FUNCTION__ << "column (info) is found. " << column <<
|
||
|
// oneColWidth;
|
||
|
double oneColWidth = columnInfoList.at(0)->width;
|
||
|
bool isSetWidth = columnInfoList.at(0)->isSetWidth;
|
||
|
if (isSetWidth) {
|
||
|
return oneColWidth;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// use default width
|
||
|
double defaultColWidth = d->sheetFormatProps.defaultColWidth;
|
||
|
return defaultColWidth;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Returns formatting of the \a column. Columns are 1-indexed.
|
||
|
*/
|
||
|
Format Worksheet::columnFormat(int column)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
QList<std::shared_ptr<XlsxColumnInfo>> columnInfoList = d->getColumnInfoList(column, column);
|
||
|
if (columnInfoList.count() == 1)
|
||
|
return columnInfoList.at(0)->format;
|
||
|
|
||
|
return Format();
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Returns true if \a column is hidden. Columns are 1-indexed.
|
||
|
*/
|
||
|
bool Worksheet::isColumnHidden(int column)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
QList<std::shared_ptr<XlsxColumnInfo>> columnInfoList = d->getColumnInfoList(column, column);
|
||
|
if (columnInfoList.count() == 1)
|
||
|
return columnInfoList.at(0)->hidden;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Sets the \a height of the rows including and between \a rowFirst and \a rowLast.
|
||
|
Row height measured in point size.
|
||
|
Rows are 1-indexed.
|
||
|
|
||
|
Returns true if success.
|
||
|
*/
|
||
|
bool Worksheet::setRowHeight(int rowFirst, int rowLast, double height)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
const QList<std::shared_ptr<XlsxRowInfo>> rowInfoList = d->getRowInfoList(rowFirst, rowLast);
|
||
|
for (const std::shared_ptr<XlsxRowInfo> &rowInfo : rowInfoList) {
|
||
|
rowInfo->height = height;
|
||
|
rowInfo->customHeight = true;
|
||
|
}
|
||
|
|
||
|
return rowInfoList.count() > 0;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Sets the \a format of the rows including and between \a rowFirst and \a rowLast.
|
||
|
Rows are 1-indexed.
|
||
|
|
||
|
Returns true if success.
|
||
|
*/
|
||
|
bool Worksheet::setRowFormat(int rowFirst, int rowLast, const Format &format)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
const QList<std::shared_ptr<XlsxRowInfo>> rowInfoList = d->getRowInfoList(rowFirst, rowLast);
|
||
|
for (const std::shared_ptr<XlsxRowInfo> &rowInfo : rowInfoList)
|
||
|
rowInfo->format = format;
|
||
|
|
||
|
d->workbook->styles()->addXfFormat(format);
|
||
|
return rowInfoList.count() > 0;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Sets the \a hidden property of the rows including and between \a rowFirst and \a rowLast.
|
||
|
Rows are 1-indexed. If hidden is true rows will not be visible.
|
||
|
|
||
|
Returns true if success.
|
||
|
*/
|
||
|
bool Worksheet::setRowHidden(int rowFirst, int rowLast, bool hidden)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
const QList<std::shared_ptr<XlsxRowInfo>> rowInfoList = d->getRowInfoList(rowFirst, rowLast);
|
||
|
for (const std::shared_ptr<XlsxRowInfo> &rowInfo : rowInfoList)
|
||
|
rowInfo->hidden = hidden;
|
||
|
|
||
|
return rowInfoList.count() > 0;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Returns height of \a row in points.
|
||
|
*/
|
||
|
double Worksheet::rowHeight(int row)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
const int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1;
|
||
|
|
||
|
auto it = d->rowsInfo.constFind(row);
|
||
|
if (d->checkDimensions(row, min_col, false, true) || it == d->rowsInfo.constEnd()) {
|
||
|
return d->sheetFormatProps.defaultRowHeight; // return default on invalid row
|
||
|
}
|
||
|
|
||
|
return (*it)->height;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Returns format of \a row.
|
||
|
*/
|
||
|
Format Worksheet::rowFormat(int row)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
const int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1;
|
||
|
auto it = d->rowsInfo.constFind(row);
|
||
|
if (d->checkDimensions(row, min_col, false, true) || it == d->rowsInfo.constEnd())
|
||
|
return Format(); // return default on invalid row
|
||
|
|
||
|
return (*it)->format;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Returns true if \a row is hidden.
|
||
|
*/
|
||
|
bool Worksheet::isRowHidden(int row)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
const int min_col = d->dimension.isValid() ? d->dimension.firstColumn() : 1;
|
||
|
auto it = d->rowsInfo.constFind(row);
|
||
|
if (d->checkDimensions(row, min_col, false, true) || it == d->rowsInfo.constEnd())
|
||
|
return false; // return default on invalid row
|
||
|
|
||
|
return (*it)->hidden;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Groups rows from \a rowFirst to \a rowLast with the given \a collapsed.
|
||
|
|
||
|
Returns false if error occurs.
|
||
|
*/
|
||
|
bool Worksheet::groupRows(int rowFirst, int rowLast, bool collapsed)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
for (int row = rowFirst; row <= rowLast; ++row) {
|
||
|
auto it = d->rowsInfo.find(row);
|
||
|
if (it != d->rowsInfo.end()) {
|
||
|
(*it)->outlineLevel += 1;
|
||
|
} else {
|
||
|
auto info = std::make_shared<XlsxRowInfo>();
|
||
|
info->outlineLevel += 1;
|
||
|
it = d->rowsInfo.insert(row, info);
|
||
|
}
|
||
|
if (collapsed)
|
||
|
(*it)->hidden = true;
|
||
|
}
|
||
|
if (collapsed) {
|
||
|
auto it = d->rowsInfo.find(rowLast + 1);
|
||
|
if (it == d->rowsInfo.end())
|
||
|
it = d->rowsInfo.insert(rowLast + 1, std::make_shared<XlsxRowInfo>());
|
||
|
(*it)->collapsed = true;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
\overload
|
||
|
|
||
|
Groups columns with the given \a range and \a collapsed.
|
||
|
*/
|
||
|
bool Worksheet::groupColumns(const CellRange &range, bool collapsed)
|
||
|
{
|
||
|
if (!range.isValid())
|
||
|
return false;
|
||
|
|
||
|
return groupColumns(range.firstColumn(), range.lastColumn(), collapsed);
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Groups columns from \a colFirst to \a colLast with the given \a collapsed.
|
||
|
Returns false if error occurs.
|
||
|
*/
|
||
|
bool Worksheet::groupColumns(int colFirst, int colLast, bool collapsed)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
d->splitColsInfo(colFirst, colLast);
|
||
|
|
||
|
QList<int> nodes;
|
||
|
nodes.append(colFirst);
|
||
|
for (int col = colFirst; col <= colLast; ++col) {
|
||
|
auto it = d->colsInfo.constFind(col);
|
||
|
if (it != d->colsInfo.constEnd()) {
|
||
|
if (nodes.last() != col)
|
||
|
nodes.append(col);
|
||
|
int nextCol = (*it)->lastColumn + 1;
|
||
|
if (nextCol <= colLast)
|
||
|
nodes.append(nextCol);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (int idx = 0; idx < nodes.size(); ++idx) {
|
||
|
int colStart = nodes[idx];
|
||
|
auto it = d->colsInfo.constFind(colStart);
|
||
|
if (it != d->colsInfo.constEnd()) {
|
||
|
(*it)->outlineLevel += 1;
|
||
|
if (collapsed)
|
||
|
(*it)->hidden = true;
|
||
|
} else {
|
||
|
int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx + 1] - 1;
|
||
|
std::shared_ptr<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd, false));
|
||
|
info->outlineLevel += 1;
|
||
|
d->colsInfo.insert(colFirst, info);
|
||
|
if (collapsed)
|
||
|
info->hidden = true;
|
||
|
for (int c = colStart; c <= colEnd; ++c)
|
||
|
d->colsInfoHelper[c] = info;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (collapsed) {
|
||
|
int col = colLast + 1;
|
||
|
d->splitColsInfo(col, col);
|
||
|
auto it = d->colsInfo.constFind(col);
|
||
|
if (it != d->colsInfo.constEnd())
|
||
|
(*it)->collapsed = true;
|
||
|
else {
|
||
|
std::shared_ptr<XlsxColumnInfo> info(new XlsxColumnInfo(col, col, false));
|
||
|
info->collapsed = true;
|
||
|
d->colsInfo.insert(col, info);
|
||
|
d->colsInfoHelper[col] = info;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
Return the range that contains cell data.
|
||
|
*/
|
||
|
CellRange Worksheet::dimension() const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
return d->dimension;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Convert the height of a cell from user's units to pixels. If the
|
||
|
height hasn't been set by the user we use the default value. If
|
||
|
the row is hidden it has a value of zero.
|
||
|
*/
|
||
|
int WorksheetPrivate::rowPixelsSize(int row) const
|
||
|
{
|
||
|
double height;
|
||
|
auto it = row_sizes.constFind(row);
|
||
|
if (it != row_sizes.constEnd())
|
||
|
height = it.value();
|
||
|
else
|
||
|
height = sheetFormatProps.defaultRowHeight;
|
||
|
return static_cast<int>(4.0 / 3.0 * height);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Convert the width of a cell from user's units to pixels. Excel rounds
|
||
|
the column width to the nearest pixel. If the width hasn't been set
|
||
|
by the user we use the default value. If the column is hidden it
|
||
|
has a value of zero.
|
||
|
*/
|
||
|
int WorksheetPrivate::colPixelsSize(int col) const
|
||
|
{
|
||
|
double max_digit_width = 7.0; // For Calabri 11
|
||
|
double padding = 5.0;
|
||
|
int pixels = 0;
|
||
|
|
||
|
auto it = col_sizes.constFind(col);
|
||
|
if (it != col_sizes.constEnd()) {
|
||
|
double width = it.value();
|
||
|
if (width < 1)
|
||
|
pixels = static_cast<int>(std::lround(width * (max_digit_width + padding)));
|
||
|
else
|
||
|
pixels = static_cast<int>(std::lround(width * max_digit_width)) + padding;
|
||
|
} else {
|
||
|
pixels = 64;
|
||
|
}
|
||
|
return pixels;
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::loadXmlSheetData(QXmlStreamReader &reader)
|
||
|
{
|
||
|
Q_Q(Worksheet);
|
||
|
|
||
|
Q_ASSERT(reader.name() == QLatin1String("sheetData"));
|
||
|
|
||
|
int row_num = 0;
|
||
|
int col_num = 0;
|
||
|
|
||
|
while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetData") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
if (reader.readNextStartElement()) {
|
||
|
if (reader.name() == QLatin1String("row")) {
|
||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||
|
|
||
|
if (attributes.hasAttribute(QLatin1String("customFormat")) ||
|
||
|
attributes.hasAttribute(QLatin1String("customHeight")) ||
|
||
|
attributes.hasAttribute(QLatin1String("hidden")) ||
|
||
|
attributes.hasAttribute(QLatin1String("outlineLevel")) ||
|
||
|
attributes.hasAttribute(QLatin1String("collapsed"))) {
|
||
|
|
||
|
std::shared_ptr<XlsxRowInfo> info(new XlsxRowInfo);
|
||
|
if (attributes.hasAttribute(QLatin1String("customFormat")) &&
|
||
|
attributes.hasAttribute(QLatin1String("s"))) {
|
||
|
int idx = attributes.value(QLatin1String("s")).toInt();
|
||
|
info->format = workbook->styles()->xfFormat(idx);
|
||
|
}
|
||
|
|
||
|
if (attributes.hasAttribute(QLatin1String("customHeight"))) {
|
||
|
info->customHeight =
|
||
|
attributes.value(QLatin1String("customHeight")) == QLatin1String("1");
|
||
|
// Row height is only specified when customHeight is set
|
||
|
if (attributes.hasAttribute(QLatin1String("ht"))) {
|
||
|
info->height = attributes.value(QLatin1String("ht")).toDouble();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// both "hidden" and "collapsed" default are false
|
||
|
info->hidden = attributes.value(QLatin1String("hidden")) == QLatin1String("1");
|
||
|
info->collapsed =
|
||
|
attributes.value(QLatin1String("collapsed")) == QLatin1String("1");
|
||
|
|
||
|
if (attributes.hasAttribute(QLatin1String("outlineLevel")))
|
||
|
info->outlineLevel =
|
||
|
attributes.value(QLatin1String("outlineLevel")).toInt();
|
||
|
|
||
|
//"r" is optional too.
|
||
|
if (attributes.hasAttribute(QLatin1String("r"))) {
|
||
|
int row = attributes.value(QLatin1String("r")).toInt();
|
||
|
rowsInfo[row] = info;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (attributes.hasAttribute(QLatin1String("r")))
|
||
|
row_num = attributes.value(QLatin1String("r")).toInt();
|
||
|
else
|
||
|
++row_num;
|
||
|
col_num = 0;
|
||
|
|
||
|
} else if (reader.name() == QLatin1String("c")) // Cell
|
||
|
{
|
||
|
|
||
|
// Cell
|
||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||
|
QString r = attributes.value(QLatin1String("r")).toString();
|
||
|
CellReference pos(r);
|
||
|
if (r.isEmpty()) {
|
||
|
pos.setRow(row_num);
|
||
|
pos.setColumn(++col_num);
|
||
|
}
|
||
|
|
||
|
// get format
|
||
|
Format format;
|
||
|
qint32 styleIndex = -1;
|
||
|
if (attributes.hasAttribute(
|
||
|
QLatin1String("s"))) // Style (defined in the styles.xml file)
|
||
|
{
|
||
|
//"s" == style index
|
||
|
int idx = attributes.value(QLatin1String("s")).toInt();
|
||
|
format = workbook->styles()->xfFormat(idx);
|
||
|
styleIndex = idx;
|
||
|
}
|
||
|
|
||
|
// Cell::CellType cellType = Cell::NumberType;
|
||
|
Cell::CellType cellType = Cell::CustomType;
|
||
|
|
||
|
if (attributes.hasAttribute(QLatin1String("t"))) // Type
|
||
|
{
|
||
|
const auto typeString = attributes.value(QLatin1String("t"));
|
||
|
if (typeString == QLatin1String("s")) // Shared string
|
||
|
{
|
||
|
cellType = Cell::SharedStringType;
|
||
|
} else if (typeString == QLatin1String("inlineStr")) // Inline String
|
||
|
{
|
||
|
cellType = Cell::InlineStringType;
|
||
|
} else if (typeString == QLatin1String("str")) // String
|
||
|
{
|
||
|
cellType = Cell::StringType;
|
||
|
} else if (typeString == QLatin1String("b")) // Boolean
|
||
|
{
|
||
|
cellType = Cell::BooleanType;
|
||
|
} else if (typeString == QLatin1String("e")) // Error
|
||
|
{
|
||
|
cellType = Cell::ErrorType;
|
||
|
} else if (typeString == QLatin1String("d")) // Date
|
||
|
{
|
||
|
cellType = Cell::DateType;
|
||
|
} else if (typeString == QLatin1String("n")) // Number
|
||
|
{
|
||
|
cellType = Cell::NumberType;
|
||
|
} else {
|
||
|
// custom type
|
||
|
cellType = Cell::CustomType;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (Cell::isDateType(cellType, format)) {
|
||
|
cellType = Cell::DateType;
|
||
|
}
|
||
|
|
||
|
// create a heap of new cell
|
||
|
auto cell = std::make_shared<Cell>(QVariant{}, cellType, format, q, styleIndex);
|
||
|
|
||
|
while (!reader.atEnd() && !(reader.name() == QLatin1String("c") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
if (reader.readNextStartElement()) {
|
||
|
if (reader.name() == QLatin1String("f")) // formula
|
||
|
{
|
||
|
CellFormula &formula = cell->d_func()->formula;
|
||
|
formula.loadFromXml(reader);
|
||
|
if (formula.formulaType() == CellFormula::SharedType &&
|
||
|
!formula.formulaText().isEmpty()) {
|
||
|
int si = formula.sharedIndex();
|
||
|
sharedFormulaMap[si] = formula;
|
||
|
}
|
||
|
} else if (reader.name() == QLatin1String("v")) // Value
|
||
|
{
|
||
|
QString value = reader.readElementText();
|
||
|
if (cellType == Cell::SharedStringType) {
|
||
|
int sst_idx = value.toInt();
|
||
|
sharedStrings()->incRefByStringIndex(sst_idx);
|
||
|
RichString rs = sharedStrings()->getSharedString(sst_idx);
|
||
|
QString strPlainString = rs.toPlainString();
|
||
|
cell->d_func()->value = strPlainString;
|
||
|
if (rs.isRichString())
|
||
|
cell->d_func()->richString = rs;
|
||
|
} else if (cellType == Cell::NumberType) {
|
||
|
cell->d_func()->value = value.toDouble();
|
||
|
} else if (cellType == Cell::BooleanType) {
|
||
|
cell->d_func()->value = value.toInt() ? true : false;
|
||
|
} else if (cellType == Cell::DateType) {
|
||
|
// [dev54] DateType
|
||
|
|
||
|
double dValue = value.toDouble(); // days from 1900(or 1904)
|
||
|
bool bIsDate1904 = q->workbook()->isDate1904();
|
||
|
|
||
|
QVariant vDatetimeValue = datetimeFromNumber(dValue, bIsDate1904);
|
||
|
Q_UNUSED(vDatetimeValue);
|
||
|
// cell->d_func()->value = vDatetimeValue;
|
||
|
cell->d_func()->value = dValue; // dev67
|
||
|
} else {
|
||
|
// ELSE type
|
||
|
cell->d_func()->value = value;
|
||
|
}
|
||
|
|
||
|
} else if (reader.name() == QLatin1String("is")) {
|
||
|
while (!reader.atEnd() &&
|
||
|
!(reader.name() == QLatin1String("is") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
if (reader.readNextStartElement()) {
|
||
|
//: Todo, add rich text read support
|
||
|
if (reader.name() == QLatin1String("t")) {
|
||
|
cell->d_func()->value = reader.readElementText();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else if (reader.name() == QLatin1String("extLst")) {
|
||
|
// skip extLst element
|
||
|
while (!reader.atEnd() &&
|
||
|
!(reader.name() == QLatin1String("extLst") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
reader.readNextStartElement();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cellTable.setValue(pos.row(), pos.column(), cell);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (dimension.lastRow() < row_num)
|
||
|
dimension.setLastRow(row_num);
|
||
|
|
||
|
if (dimension.lastColumn() < col_num)
|
||
|
dimension.setLastColumn(col_num);
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::loadXmlColumnsInfo(QXmlStreamReader &reader)
|
||
|
{
|
||
|
Q_ASSERT(reader.name() == QLatin1String("cols"));
|
||
|
|
||
|
while (!reader.atEnd() && !(reader.name() == QLatin1String("cols") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
reader.readNextStartElement();
|
||
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||
|
if (reader.name() == QLatin1String("col")) {
|
||
|
std::shared_ptr<XlsxColumnInfo> info(new XlsxColumnInfo(0, 1, false));
|
||
|
|
||
|
QXmlStreamAttributes colAttrs = reader.attributes();
|
||
|
int min = colAttrs.value(QLatin1String("min")).toInt();
|
||
|
int max = colAttrs.value(QLatin1String("max")).toInt();
|
||
|
info->firstColumn = min;
|
||
|
info->lastColumn = max;
|
||
|
|
||
|
// Flag indicating that the column width for the affected column(s) is different
|
||
|
// from the
|
||
|
// default or has been manually set
|
||
|
if (colAttrs.hasAttribute(QLatin1String("customWidth"))) {
|
||
|
info->customWidth =
|
||
|
colAttrs.value(QLatin1String("customWidth")) == QLatin1String("1");
|
||
|
}
|
||
|
|
||
|
// Note, node may have "width" without "customWidth"
|
||
|
// [dev54]
|
||
|
if (colAttrs.hasAttribute(QLatin1String("width"))) {
|
||
|
double width = colAttrs.value(QLatin1String("width")).toDouble();
|
||
|
info->width = width;
|
||
|
info->isSetWidth = true; // [dev54]
|
||
|
}
|
||
|
|
||
|
info->hidden = colAttrs.value(QLatin1String("hidden")) == QLatin1String("1");
|
||
|
info->collapsed = colAttrs.value(QLatin1String("collapsed")) == QLatin1String("1");
|
||
|
|
||
|
if (colAttrs.hasAttribute(QLatin1String("style"))) {
|
||
|
int idx = colAttrs.value(QLatin1String("style")).toInt();
|
||
|
info->format = workbook->styles()->xfFormat(idx);
|
||
|
}
|
||
|
|
||
|
if (colAttrs.hasAttribute(QLatin1String("outlineLevel"))) {
|
||
|
info->outlineLevel = colAttrs.value(QLatin1String("outlineLevel")).toInt();
|
||
|
}
|
||
|
|
||
|
// qDebug() << "[debug] " << __FUNCTION__ << min << max << info->width << hasWidth;
|
||
|
|
||
|
colsInfo.insert(min, info);
|
||
|
for (int col = min; col <= max; ++col) {
|
||
|
colsInfoHelper[col] = info;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::loadXmlMergeCells(QXmlStreamReader &reader)
|
||
|
{
|
||
|
// issue #173 https://github.com/QtExcel/QXlsx/issues/173
|
||
|
|
||
|
Q_ASSERT(reader.name() == QLatin1String("mergeCells"));
|
||
|
|
||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||
|
|
||
|
bool isCount = attributes.hasAttribute(QLatin1String("count"));
|
||
|
int count = 0;
|
||
|
if (!isCount) {
|
||
|
qWarning("no count");
|
||
|
} else {
|
||
|
count = attributes.value(QLatin1String("count")).toInt();
|
||
|
}
|
||
|
|
||
|
while (!reader.atEnd() && !(reader.name() == QLatin1String("mergeCells") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
reader.readNextStartElement();
|
||
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||
|
if (reader.name() == QLatin1String("mergeCell")) {
|
||
|
QXmlStreamAttributes attrs = reader.attributes();
|
||
|
QString rangeStr = attrs.value(QLatin1String("ref")).toString();
|
||
|
merges.append(CellRange(rangeStr));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (isCount) {
|
||
|
int mergesSize = merges.size();
|
||
|
if (mergesSize != count) {
|
||
|
qWarning("read merge cells error");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::loadXmlDataValidations(QXmlStreamReader &reader)
|
||
|
{
|
||
|
Q_ASSERT(reader.name() == QLatin1String("dataValidations"));
|
||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||
|
int count = attributes.value(QLatin1String("count")).toInt();
|
||
|
|
||
|
while (!reader.atEnd() && !(reader.name() == QLatin1String("dataValidations") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
reader.readNextStartElement();
|
||
|
if (reader.tokenType() == QXmlStreamReader::StartElement &&
|
||
|
reader.name() == QLatin1String("dataValidation")) {
|
||
|
dataValidationsList.append(DataValidation::loadFromXml(reader));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (dataValidationsList.size() != count)
|
||
|
qDebug("read data validation error");
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::loadXmlSheetViews(QXmlStreamReader &reader)
|
||
|
{
|
||
|
Q_ASSERT(reader.name() == QLatin1String("sheetViews"));
|
||
|
|
||
|
while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetViews") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
reader.readNextStartElement();
|
||
|
if (reader.tokenType() == QXmlStreamReader::StartElement &&
|
||
|
reader.name() == QLatin1String("sheetView")) {
|
||
|
QXmlStreamAttributes attrs = reader.attributes();
|
||
|
// default false
|
||
|
windowProtection = attrs.value(QLatin1String("windowProtection")) == QLatin1String("1");
|
||
|
showFormulas = attrs.value(QLatin1String("showFormulas")) == QLatin1String("1");
|
||
|
rightToLeft = attrs.value(QLatin1String("rightToLeft")) == QLatin1String("1");
|
||
|
tabSelected = attrs.value(QLatin1String("tabSelected")) == QLatin1String("1");
|
||
|
// default true
|
||
|
showGridLines = attrs.value(QLatin1String("showGridLines")) != QLatin1String("0");
|
||
|
showRowColHeaders =
|
||
|
attrs.value(QLatin1String("showRowColHeaders")) != QLatin1String("0");
|
||
|
showZeros = attrs.value(QLatin1String("showZeros")) != QLatin1String("0");
|
||
|
showRuler = attrs.value(QLatin1String("showRuler")) != QLatin1String("0");
|
||
|
showOutlineSymbols =
|
||
|
attrs.value(QLatin1String("showOutlineSymbols")) != QLatin1String("0");
|
||
|
showWhiteSpace = attrs.value(QLatin1String("showWhiteSpace")) != QLatin1String("0");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::loadXmlSheetFormatProps(QXmlStreamReader &reader)
|
||
|
{
|
||
|
Q_ASSERT(reader.name() == QLatin1String("sheetFormatPr"));
|
||
|
|
||
|
const QXmlStreamAttributes attributes = reader.attributes();
|
||
|
XlsxSheetFormatProps formatProps;
|
||
|
bool isSetWidth = false;
|
||
|
|
||
|
// Retain default values
|
||
|
for (const QXmlStreamAttribute &attrib : attributes) {
|
||
|
if (attrib.name() == QLatin1String("baseColWidth")) {
|
||
|
formatProps.baseColWidth = attrib.value().toInt();
|
||
|
} else if (attrib.name() == QLatin1String("customHeight")) {
|
||
|
formatProps.customHeight = attrib.value() == QLatin1String("1");
|
||
|
} else if (attrib.name() == QLatin1String("defaultColWidth")) {
|
||
|
double dDefaultColWidth = attrib.value().toDouble();
|
||
|
formatProps.defaultColWidth = dDefaultColWidth;
|
||
|
isSetWidth = true;
|
||
|
} else if (attrib.name() == QLatin1String("defaultRowHeight")) {
|
||
|
formatProps.defaultRowHeight = attrib.value().toDouble();
|
||
|
} else if (attrib.name() == QLatin1String("outlineLevelCol")) {
|
||
|
formatProps.outlineLevelCol = attrib.value().toInt();
|
||
|
} else if (attrib.name() == QLatin1String("outlineLevelRow")) {
|
||
|
formatProps.outlineLevelRow = attrib.value().toInt();
|
||
|
} else if (attrib.name() == QLatin1String("thickBottom")) {
|
||
|
formatProps.thickBottom = attrib.value() == QLatin1String("1");
|
||
|
} else if (attrib.name() == QLatin1String("thickTop")) {
|
||
|
formatProps.thickTop = attrib.value() == QLatin1String("1");
|
||
|
} else if (attrib.name() == QLatin1String("zeroHeight")) {
|
||
|
formatProps.zeroHeight = attrib.value() == QLatin1String("1");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if (formatProps.defaultColWidth == 0.0)
|
||
|
if (!isSetWidth) {
|
||
|
// not set
|
||
|
double dCalcWidth = WorksheetPrivate::calculateColWidth(formatProps.baseColWidth);
|
||
|
formatProps.defaultColWidth = dCalcWidth;
|
||
|
}
|
||
|
|
||
|
// [dev54]
|
||
|
// Where is code of setting 'formatProps'?
|
||
|
this->sheetFormatProps = formatProps;
|
||
|
}
|
||
|
double WorksheetPrivate::calculateColWidth(int characters)
|
||
|
{
|
||
|
// //!Todo
|
||
|
// Take normal style' font maximum width and add padding and margin pixels
|
||
|
// return characters + 0.5;
|
||
|
return characters;
|
||
|
}
|
||
|
|
||
|
void WorksheetPrivate::loadXmlHyperlinks(QXmlStreamReader &reader)
|
||
|
{
|
||
|
Q_ASSERT(reader.name() == QLatin1String("hyperlinks"));
|
||
|
|
||
|
while (!reader.atEnd() && !(reader.name() == QLatin1String("hyperlinks") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
reader.readNextStartElement();
|
||
|
if (reader.tokenType() == QXmlStreamReader::StartElement &&
|
||
|
reader.name() == QLatin1String("hyperlink")) {
|
||
|
QXmlStreamAttributes attrs = reader.attributes();
|
||
|
CellReference pos(attrs.value(QLatin1String("ref")).toString());
|
||
|
if (pos.isValid()) { // Valid
|
||
|
std::shared_ptr<XlsxHyperlinkData> link(new XlsxHyperlinkData);
|
||
|
link->display = attrs.value(QLatin1String("display")).toString();
|
||
|
link->tooltip = attrs.value(QLatin1String("tooltip")).toString();
|
||
|
link->location = attrs.value(QLatin1String("location")).toString();
|
||
|
|
||
|
if (attrs.hasAttribute(QLatin1String("r:id"))) {
|
||
|
link->linkType = XlsxHyperlinkData::External;
|
||
|
XlsxRelationship ship = relationships->getRelationshipById(
|
||
|
attrs.value(QLatin1String("r:id")).toString());
|
||
|
link->target = ship.target;
|
||
|
} else {
|
||
|
link->linkType = XlsxHyperlinkData::Internal;
|
||
|
}
|
||
|
|
||
|
urlTable[pos.row()][pos.column()] = link;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QList<std::shared_ptr<XlsxColumnInfo>> WorksheetPrivate::getColumnInfoList(int colFirst,
|
||
|
int colLast)
|
||
|
{
|
||
|
QList<std::shared_ptr<XlsxColumnInfo>> columnsInfoList;
|
||
|
if (isColumnRangeValid(colFirst, colLast)) {
|
||
|
QList<int> nodes = getColumnIndexes(colFirst, colLast);
|
||
|
|
||
|
for (int idx = 0; idx < nodes.size(); ++idx) {
|
||
|
int colStart = nodes[idx];
|
||
|
auto it = colsInfo.constFind(colStart);
|
||
|
if (it != colsInfo.constEnd()) {
|
||
|
columnsInfoList.append(*it);
|
||
|
} else {
|
||
|
int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx + 1] - 1;
|
||
|
std::shared_ptr<XlsxColumnInfo> info(new XlsxColumnInfo(colStart, colEnd, false));
|
||
|
colsInfo.insert(colFirst, info);
|
||
|
columnsInfoList.append(info);
|
||
|
for (int c = colStart; c <= colEnd; ++c) {
|
||
|
colsInfoHelper[c] = info;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return columnsInfoList;
|
||
|
}
|
||
|
|
||
|
QList<std::shared_ptr<XlsxRowInfo>> WorksheetPrivate::getRowInfoList(int rowFirst, int rowLast)
|
||
|
{
|
||
|
QList<std::shared_ptr<XlsxRowInfo>> rowInfoList;
|
||
|
|
||
|
int min_col = dimension.firstColumn() < 1 ? 1 : dimension.firstColumn();
|
||
|
|
||
|
for (int row = rowFirst; row <= rowLast; ++row) {
|
||
|
if (checkDimensions(row, min_col, false, true))
|
||
|
continue;
|
||
|
|
||
|
std::shared_ptr<XlsxRowInfo> rowInfo;
|
||
|
if (!(rowsInfo[row])) {
|
||
|
rowsInfo[row] = std::make_shared<XlsxRowInfo>();
|
||
|
}
|
||
|
rowInfoList.append(rowsInfo[row]);
|
||
|
}
|
||
|
|
||
|
return rowInfoList;
|
||
|
}
|
||
|
|
||
|
bool Worksheet::loadFromXmlFile(QIODevice *device)
|
||
|
{
|
||
|
Q_D(Worksheet);
|
||
|
|
||
|
QXmlStreamReader reader(device);
|
||
|
while (!reader.atEnd()) {
|
||
|
reader.readNextStartElement();
|
||
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
||
|
if (reader.name() == QLatin1String("dimension")) {
|
||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||
|
QString range = attributes.value(QLatin1String("ref")).toString();
|
||
|
d->dimension = CellRange(range);
|
||
|
} else if (reader.name() == QLatin1String("sheetViews")) {
|
||
|
d->loadXmlSheetViews(reader);
|
||
|
} else if (reader.name() == QLatin1String("sheetFormatPr")) {
|
||
|
d->loadXmlSheetFormatProps(reader);
|
||
|
} else if (reader.name() == QLatin1String("cols")) {
|
||
|
d->loadXmlColumnsInfo(reader);
|
||
|
} else if (reader.name() == QLatin1String("sheetData")) {
|
||
|
d->loadXmlSheetData(reader);
|
||
|
} else if (reader.name() == QLatin1String("mergeCells")) {
|
||
|
d->loadXmlMergeCells(reader);
|
||
|
} else if (reader.name() == QLatin1String("dataValidations")) {
|
||
|
d->loadXmlDataValidations(reader);
|
||
|
} else if (reader.name() == QLatin1String("conditionalFormatting")) {
|
||
|
ConditionalFormatting cf;
|
||
|
cf.loadFromXml(reader, workbook()->styles());
|
||
|
d->conditionalFormattingList.append(cf);
|
||
|
} else if (reader.name() == QLatin1String("hyperlinks")) {
|
||
|
d->loadXmlHyperlinks(reader);
|
||
|
} else if (reader.name() == QLatin1String("pageSetup")) {
|
||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||
|
|
||
|
d->PpaperSize = attributes.value(QLatin1String("paperSize")).toString().trimmed();
|
||
|
d->Pscale = attributes.value(QLatin1String("scale")).toString().trimmed();
|
||
|
d->PfirstPageNumber =
|
||
|
attributes.value(QLatin1String("firstPageNumber")).toString().trimmed();
|
||
|
d->Porientation =
|
||
|
attributes.value(QLatin1String("orientation")).toString().trimmed();
|
||
|
d->PuseFirstPageNumber =
|
||
|
attributes.value(QLatin1String("useFirstPageNumber")).toString().trimmed();
|
||
|
d->PhorizontalDpi =
|
||
|
attributes.value(QLatin1String("horizontalDpi")).toString().trimmed();
|
||
|
d->PverticalDpi =
|
||
|
attributes.value(QLatin1String("verticalDpi")).toString().trimmed();
|
||
|
d->Prid = attributes.value(QLatin1String("r:id")).toString().trimmed();
|
||
|
d->Pcopies = attributes.value(QLatin1String("copies")).toString().trimmed();
|
||
|
} else if (reader.name() == QLatin1String("pageMargins")) {
|
||
|
QXmlStreamAttributes attributes = reader.attributes();
|
||
|
|
||
|
d->PMfooter = attributes.value(QLatin1String("footer")).toString().trimmed();
|
||
|
d->PMheader = attributes.value(QLatin1String("header")).toString().trimmed();
|
||
|
d->PMbotton = attributes.value(QLatin1String("bottom")).toString().trimmed();
|
||
|
d->PMtop = attributes.value(QLatin1String("top")).toString().trimmed();
|
||
|
d->PMright = attributes.value(QLatin1String("right")).toString().trimmed();
|
||
|
d->PMleft = attributes.value(QLatin1String("left")).toString().trimmed();
|
||
|
} else if (reader.name() == QLatin1String("headerFooter")) {
|
||
|
// dev40
|
||
|
while (reader.readNextStartElement()) {
|
||
|
if (reader.name() == QLatin1String("oddHeader"))
|
||
|
d->ModdHeader = reader.readElementText();
|
||
|
|
||
|
if (reader.name() == QLatin1String("oddFooter"))
|
||
|
d->MoodFooter = reader.readElementText();
|
||
|
}
|
||
|
} else if (reader.name() == QLatin1String("drawing")) {
|
||
|
QString rId = reader.attributes().value(QStringLiteral("r:id")).toString();
|
||
|
QString name = d->relationships->getRelationshipById(rId).target;
|
||
|
|
||
|
const auto parts = splitPath(filePath());
|
||
|
QString path = QDir::cleanPath(parts.first() + QLatin1String("/") + name);
|
||
|
|
||
|
d->drawing = std::make_shared<Drawing>(this, F_LoadFromExists);
|
||
|
d->drawing->setFilePath(path);
|
||
|
} else if (reader.name() == QLatin1String("extLst")) {
|
||
|
// Todo: add extLst support
|
||
|
while (!reader.atEnd() && !(reader.name() == QLatin1String("extLst") &&
|
||
|
reader.tokenType() == QXmlStreamReader::EndElement)) {
|
||
|
reader.readNextStartElement();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
d->validateDimension();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Documents imported from Google Docs does not contain dimension data.
|
||
|
*/
|
||
|
void WorksheetPrivate::validateDimension()
|
||
|
{
|
||
|
if (dimension.isValid() || cellTable.isEmpty())
|
||
|
return;
|
||
|
|
||
|
CellRange cr(
|
||
|
cellTable.firstRow, cellTable.firstColumn, cellTable.lastRow, cellTable.lastColumn);
|
||
|
|
||
|
if (cr.isValid())
|
||
|
dimension = cr;
|
||
|
}
|
||
|
|
||
|
/*!
|
||
|
* \internal
|
||
|
* Unit test can use this member to get sharedString object.
|
||
|
*/
|
||
|
SharedStrings *WorksheetPrivate::sharedStrings() const
|
||
|
{
|
||
|
return workbook->sharedStrings();
|
||
|
}
|
||
|
|
||
|
QVector<CellLocation> Worksheet::getFullCells(int *maxRow, int *maxCol) const
|
||
|
{
|
||
|
Q_D(const Worksheet);
|
||
|
|
||
|
// return values
|
||
|
(*maxRow) = -1;
|
||
|
(*maxCol) = -1;
|
||
|
QVector<CellLocation> ret;
|
||
|
|
||
|
// QString privateName = d->name; // name of sheet (not object type)
|
||
|
// qDebug() << privateName ;
|
||
|
|
||
|
if (d->type == AbstractSheet::ST_WorkSheet) {
|
||
|
// use current sheet
|
||
|
} else if (d->type == AbstractSheet::ST_ChartSheet) {
|
||
|
return ret;
|
||
|
} else {
|
||
|
qWarning("unsupported sheet type.");
|
||
|
Q_ASSERT(false);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
const auto sortedRows = d->cellTable.sortedRows();
|
||
|
for (const auto row : sortedRows) {
|
||
|
const auto &columns = d->cellTable.cells[row];
|
||
|
const auto columnsSorted = CellTable::sorteIntList(columns.keys());
|
||
|
for (const auto &col : columnsSorted) {
|
||
|
// It's faster to iterate but cellTable is unordered which might not
|
||
|
// be what callers want?
|
||
|
auto cell = std::make_shared<Cell>(columns.value(col).get());
|
||
|
|
||
|
CellLocation cl;
|
||
|
|
||
|
cl.row = row;
|
||
|
if (row > (*maxRow)) {
|
||
|
(*maxRow) = row;
|
||
|
}
|
||
|
|
||
|
cl.col = col;
|
||
|
if (col > (*maxCol)) {
|
||
|
(*maxCol) = col;
|
||
|
}
|
||
|
|
||
|
cl.cell = cell;
|
||
|
|
||
|
ret.push_back(cl);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
QT_END_NAMESPACE_XLSX
|