// xlsxworksheet.cpp
|
|
#include <QtGlobal>
|
#include <QVariant>
|
#include <QDateTime>
|
#include <QDate>
|
#include <QTime>
|
#include <QPoint>
|
#include <QFile>
|
#include <QUrl>
|
#include <QDebug>
|
#include <QBuffer>
|
#include <QXmlStreamWriter>
|
#include <QXmlStreamReader>
|
#include <QTextDocument>
|
#include <QDir>
|
#include <QMapIterator>
|
#include <QMap>
|
|
#include <cmath>
|
|
#include "xlsxrichstring.h"
|
#include "xlsxcellreference.h"
|
#include "xlsxworksheet.h"
|
#include "xlsxworksheet_p.h"
|
#include "xlsxworkbook.h"
|
#include "xlsxformat.h"
|
#include "xlsxformat_p.h"
|
#include "xlsxutility_p.h"
|
#include "xlsxsharedstrings_p.h"
|
#include "xlsxdrawing_p.h"
|
#include "xlsxstyles_p.h"
|
#include "xlsxcell.h"
|
#include "xlsxcell_p.h"
|
#include "xlsxcellrange.h"
|
#include "xlsxconditionalformatting_p.h"
|
#include "xlsxdrawinganchor_p.h"
|
#include "xlsxchart.h"
|
#include "xlsxcellformula.h"
|
#include "xlsxcellformula_p.h"
|
#include "xlsxcelllocation.h"
|
|
QT_BEGIN_NAMESPACE_XLSX
|
|
WorksheetPrivate::WorksheetPrivate(Worksheet *p, Worksheet::CreateFlag flag)
|
: AbstractSheetPrivate(p, flag),
|
windowProtection(false),
|
showFormulas(false),
|
showGridLines(true),
|
showRowColHeaders(true),
|
showZeros(true),
|
rightToLeft(false),
|
tabSelected(false),
|
showRuler(false),
|
showOutlineSymbols(true),
|
showWhiteSpace(true),
|
urlPattern(QStringLiteral("^([fh]tt?ps?://)|(mailto:)|(file://)"))
|
{
|
previous_row = 0;
|
|
outline_row_level = 0;
|
outline_col_level = 0;
|
|
default_row_height = 15;
|
default_row_zeroed = false;
|
}
|
|
WorksheetPrivate::~WorksheetPrivate()
|
{
|
}
|
|
/*
|
Calculate the "spans" attribute of the <row> tag. This is an
|
XLSX optimisation and isn't strictly required. However, it
|
makes comparing files easier. The span is the same for each
|
block of 16 rows.
|
*/
|
void WorksheetPrivate::calculateSpans() const
|
{
|
row_spans.clear();
|
int span_min = XLSX_COLUMN_MAX+1;
|
int span_max = -1;
|
|
for (int row_num = dimension.firstRow(); row_num <= dimension.lastRow(); row_num++) {
|
auto it = cellTable.constFind(row_num);
|
if (it != cellTable.constEnd()) {
|
for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++) {
|
if (it->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;
|
}
|
}
|
}
|
}
|
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 memery leak.
|
d_func()->workbook = new Workbook(flag);
|
}
|
|
/*!
|
* \internal
|
*
|
* Make a copy of this sheet.
|
*/
|
|
Worksheet *Worksheet::copy(const QString &distName, int distId) const
|
{
|
Q_D(const Worksheet);
|
Worksheet *sheet = new Worksheet(distName, distId, d->workbook, F_NewFromScratch);
|
WorksheetPrivate *sheet_d = sheet->d_func();
|
|
sheet_d->dimension = d->dimension;
|
|
QMapIterator<int, QMap<int, std::shared_ptr<Cell> > > it(d->cellTable);
|
while (it.hasNext())
|
{
|
it.next();
|
int row = it.key();
|
QMapIterator<int, std::shared_ptr<Cell> > it2(it.value());
|
while (it2.hasNext())
|
{
|
it2.next();
|
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[row][col] = cell;
|
}
|
}
|
|
sheet_d->merges = d->merges;
|
// sheet_d->rowsInfo = d->rowsInfo;
|
// sheet_d->colsInfo = d->colsInfo;
|
// sheet_d->colsInfoHelper = d->colsInfoHelper;
|
// sheet_d->dataValidationsList = d->dataValidationsList;
|
// sheet_d->conditionalFormattingList = d->conditionalFormattingList;
|
|
return sheet;
|
}
|
|
/*!
|
* Destroys this workssheet.
|
*/
|
Worksheet::~Worksheet()
|
{
|
}
|
|
/*!
|
* Returns whether sheet is protected.
|
*/
|
bool Worksheet::isWindowProtected() const
|
{
|
Q_D(const Worksheet);
|
return d->windowProtection;
|
}
|
|
/*!
|
* Protects/unprotects the sheet based on \a protect.
|
*/
|
void Worksheet::setWindowProtected(bool protect)
|
{
|
Q_D(Worksheet);
|
d->windowProtection = protect;
|
}
|
|
/*!
|
* Return whether formulas instead of their calculated results shown in cells
|
*/
|
bool Worksheet::isFormulasVisible() const
|
{
|
Q_D(const Worksheet);
|
return d->showFormulas;
|
}
|
|
/*!
|
* Show formulas in cells instead of their calculated results when \a visible is true.
|
*/
|
void Worksheet::setFormulasVisible(bool visible)
|
{
|
Q_D(Worksheet);
|
d->showFormulas = visible;
|
}
|
|
/*!
|
* Return whether gridlines is shown or not.
|
*/
|
bool Worksheet::isGridLinesVisible() const
|
{
|
Q_D(const Worksheet);
|
return d->showGridLines;
|
}
|
|
/*!
|
* Show or hide the gridline based on \a visible
|
*/
|
void Worksheet::setGridLinesVisible(bool visible)
|
{
|
Q_D(Worksheet);
|
d->showGridLines = visible;
|
}
|
|
/*!
|
* Return whether is row and column headers is vislbe.
|
*/
|
bool Worksheet::isRowColumnHeadersVisible() const
|
{
|
Q_D(const Worksheet);
|
return d->showRowColHeaders;
|
}
|
|
/*!
|
* Show or hide the row column headers based on \a visible
|
*/
|
void Worksheet::setRowColumnHeadersVisible(bool visible)
|
{
|
Q_D(Worksheet);
|
d->showRowColHeaders = visible;
|
}
|
|
|
/*!
|
* Return whether the sheet is shown right-to-left or not.
|
*/
|
bool Worksheet::isRightToLeft() const
|
{
|
Q_D(const Worksheet);
|
return d->rightToLeft;
|
}
|
|
/*!
|
* Enable or disable the right-to-left based on \a enable.
|
*/
|
void Worksheet::setRightToLeft(bool enable)
|
{
|
Q_D(Worksheet);
|
d->rightToLeft = enable;
|
}
|
|
/*!
|
* Return whether is cells that have zero value show a zero.
|
*/
|
bool Worksheet::isZerosVisible() const
|
{
|
Q_D(const Worksheet);
|
return d->showZeros;
|
}
|
|
/*!
|
* Show a zero in cells that have zero value if \a visible is true.
|
*/
|
void Worksheet::setZerosVisible(bool visible)
|
{
|
Q_D(Worksheet);
|
d->showZeros = visible;
|
}
|
|
/*!
|
* Return whether this tab is selected.
|
*/
|
bool Worksheet::isSelected() const
|
{
|
Q_D(const Worksheet);
|
return d->tabSelected;
|
}
|
|
/*!
|
* Select this sheet if \a select is true.
|
*/
|
void Worksheet::setSelected(bool select)
|
{
|
Q_D(Worksheet);
|
d->tabSelected = select;
|
}
|
|
/*!
|
* Return whether is ruler is shown.
|
*/
|
bool Worksheet::isRulerVisible() const
|
{
|
Q_D(const Worksheet);
|
return d->showRuler;
|
|
}
|
|
/*!
|
* Show or hide the ruler based on \a visible.
|
*/
|
void Worksheet::setRulerVisible(bool visible)
|
{
|
Q_D(Worksheet);
|
d->showRuler = visible;
|
|
}
|
|
/*!
|
* Return whether is outline symbols is shown.
|
*/
|
bool Worksheet::isOutlineSymbolsVisible() const
|
{
|
Q_D(const Worksheet);
|
return d->showOutlineSymbols;
|
}
|
|
/*!
|
* Show or hide the outline symbols based ib \a visible.
|
*/
|
void Worksheet::setOutlineSymbolsVisible(bool visible)
|
{
|
Q_D(Worksheet);
|
d->showOutlineSymbols = visible;
|
}
|
|
/*!
|
* Return whether is white space is shown.
|
*/
|
bool Worksheet::isWhiteSpaceVisible() const
|
{
|
Q_D(const Worksheet);
|
return d->showWhiteSpace;
|
}
|
|
/*!
|
* Show or hide the white space based on \a visible.
|
*/
|
void Worksheet::setWhiteSpaceVisible(bool visible)
|
{
|
Q_D(Worksheet);
|
d->showWhiteSpace = visible;
|
}
|
|
/*!
|
* Write \a value to cell (\a row, \a column) with the \a format.
|
* Both \a row and \a column are all 1-indexed value.
|
*
|
* Returns true on success.
|
*/
|
bool Worksheet::write(int row, int column, const QVariant &value, const Format &format)
|
{
|
Q_D(Worksheet);
|
|
if (d->checkDimensions(row, column))
|
return false;
|
|
bool ret = true;
|
if (value.isNull())
|
{
|
//Blank
|
ret = writeBlank(row, column, format);
|
}
|
else if (value.userType() == QMetaType::QString)
|
{
|
//String
|
QString token = value.toString();
|
bool ok;
|
|
if (token.startsWith(QLatin1String("=")))
|
{
|
//convert to formula
|
ret = writeFormula(row, column, CellFormula(token), format);
|
}
|
else if (d->workbook->isStringsToHyperlinksEnabled() && token.contains(d->urlPattern))
|
{
|
//convert to url
|
ret = writeHyperlink(row, column, QUrl(token));
|
}
|
else if (d->workbook->isStringsToNumbersEnabled() && (value.toDouble(&ok), ok))
|
{
|
//Try convert string to number if the flag enabled.
|
ret = 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 cann'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);
|
|
Cell *cell = cellAt(row, column);
|
if (!cell)
|
return QVariant();
|
|
if (cell->hasFormula())
|
{
|
if (cell->formula().formulaType() == CellFormula::NormalType)
|
{
|
return QVariant(QLatin1String("=")+cell->formula().formulaText());
|
}
|
else if (cell->formula().formulaType() == CellFormula::SharedType)
|
{
|
if (!cell->formula().formulaText().isEmpty())
|
{
|
return QVariant(QLatin1String("=")+cell->formula().formulaText());
|
}
|
else
|
{
|
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.
|
*/
|
Cell *Worksheet::cellAt(const CellReference &row_column) const
|
{
|
if (!row_column.isValid())
|
return 0;
|
|
return cellAt(row_column.row(), row_column.column());
|
}
|
|
/*!
|
* Returns the cell at the given \a row and \a column. If there
|
* is no cell at the specified position, the function returns 0.
|
*/
|
Cell *Worksheet::cellAt(int row, int col) const
|
{
|
Q_D(const Worksheet);
|
auto it = d->cellTable.constFind(row);
|
if (it == d->cellTable.constEnd())
|
return 0;
|
if (!it->contains(col))
|
return 0;
|
|
return (*it)[col].get();
|
}
|
|
Format WorksheetPrivate::cellFormat(int row, int col) const
|
{
|
auto it = cellTable.constFind(row);
|
if (it == cellTable.constEnd())
|
return Format();
|
if (!it->contains(col))
|
return Format();
|
return (*it)[col]->format();
|
}
|
|
/*!
|
\overload
|
Write string \a value to the cell \a row_column with the \a format.
|
|
Returns true on success.
|
*/
|
bool Worksheet::writeString(const CellReference &row_column, const RichString &value, const Format &format)
|
{
|
if (!row_column.isValid())
|
return false;
|
|
return writeString(row_column.row(), row_column.column(), value, format);
|
}
|
|
/*!
|
Write string \a value to the cell (\a row, \a column) with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeString(int row, int column, const RichString &value, const Format &format)
|
{
|
Q_D(Worksheet);
|
// QString content = value.toPlainString();
|
if (d->checkDimensions(row, column))
|
return false;
|
|
// if (content.size() > d->xls_strmax) {
|
// content = content.left(d->xls_strmax);
|
// error = -2;
|
// }
|
|
d->sharedStrings()->addSharedString(value);
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
if (value.fragmentCount() == 1 && value.fragmentFormat(0).isValid())
|
fmt.mergeFormat(value.fragmentFormat(0));
|
d->workbook->styles()->addXfFormat(fmt);
|
auto cell = std::make_shared<Cell>(value.toPlainString(), Cell::SharedStringType, fmt, this);
|
cell->d_ptr->richString = value;
|
d->cellTable[row][column] = cell;
|
return true;
|
}
|
|
/*!
|
\overload
|
Write string \a value to the cell \a row_column with the \a format.
|
*/
|
bool Worksheet::writeString(const CellReference &row_column, const QString &value, const Format &format)
|
{
|
if (!row_column.isValid())
|
return false;
|
|
return writeString(row_column.row(), row_column.column(), value, format);
|
}
|
|
/*!
|
\overload
|
|
Write string \a value to the cell (\a row, \a column) with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeString(int row, int column, const QString &value, const Format &format)
|
{
|
Q_D(Worksheet);
|
if (d->checkDimensions(row, column))
|
return false;
|
|
RichString rs;
|
if (d->workbook->isHtmlToRichStringEnabled() && Qt::mightBeRichText(value))
|
rs.setHtml(value);
|
else
|
rs.addFragment(value, Format());
|
|
return writeString(row, column, rs, format);
|
}
|
|
/*!
|
\overload
|
Write string \a value to the cell \a row_column with the \a format
|
*/
|
bool Worksheet::writeInlineString(const CellReference &row_column, const QString &value, const Format &format)
|
{
|
if (!row_column.isValid())
|
return false;
|
|
return writeInlineString(row_column.row(), row_column.column(), value, format);
|
}
|
|
/*!
|
Write string \a value to the cell (\a row, \a column) with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeInlineString(int row, int column, const QString &value, const Format &format)
|
{
|
Q_D(Worksheet);
|
//int error = 0;
|
QString content = value;
|
if (d->checkDimensions(row, column))
|
return false;
|
|
if (value.size() > XLSX_STRING_MAX) {
|
content = value.left(XLSX_STRING_MAX);
|
//error = -2;
|
}
|
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
d->workbook->styles()->addXfFormat(fmt);
|
d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::InlineStringType, fmt, this);
|
return true;
|
}
|
|
/*!
|
\overload
|
Write numeric \a value to the cell \a row_column with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeNumeric(const CellReference &row_column, double value, const Format &format)
|
{
|
if (!row_column.isValid())
|
return false;
|
|
return writeNumeric(row_column.row(), row_column.column(), value, format);
|
}
|
|
/*!
|
Write numeric \a value to the cell (\a row, \a column) with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeNumeric(int row, int column, double value, const Format &format)
|
{
|
Q_D(Worksheet);
|
if (d->checkDimensions(row, column))
|
return false;
|
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
d->workbook->styles()->addXfFormat(fmt);
|
d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::NumberType, fmt, this);
|
return true;
|
}
|
|
|
/*!
|
\overload
|
Write \a formula to the cell \a row_column with the \a format and \a result.
|
Returns true on success.
|
*/
|
bool Worksheet::writeFormula(const CellReference &row_column, const CellFormula &formula, const Format &format, double result)
|
{
|
if (!row_column.isValid())
|
return false;
|
|
return writeFormula(row_column.row(), row_column.column(), formula, format, result);
|
}
|
|
/*!
|
Write \a formula_ to the cell (\a row, \a column) with the \a format and \a result.
|
Returns true on success.
|
*/
|
bool Worksheet::writeFormula(int row, int column, const CellFormula &formula_, const Format &format, double result)
|
{
|
Q_D(Worksheet);
|
|
if (d->checkDimensions(row, column))
|
return false;
|
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
d->workbook->styles()->addXfFormat(fmt);
|
|
CellFormula formula = formula_;
|
formula.d->ca = true;
|
if (formula.formulaType() == CellFormula::SharedType)
|
{
|
//Assign proper shared index for shared formula
|
int si = 0;
|
while ( d->sharedFormulaMap.contains(si) )
|
{
|
++si;
|
}
|
formula.d->si = si;
|
d->sharedFormulaMap[si] = formula;
|
}
|
|
auto data = std::make_shared<Cell>(result, Cell::NumberType, fmt, this);
|
data->d_ptr->formula = formula;
|
d->cellTable[row][column] = data;
|
|
CellRange range = formula.reference();
|
if (formula.formulaType() == CellFormula::SharedType) {
|
CellFormula sf(QString(), CellFormula::SharedType);
|
sf.d->si = formula.sharedIndex();
|
for (int r=range.firstRow(); r<=range.lastRow(); ++r) {
|
for (int c=range.firstColumn(); c<=range.lastColumn(); ++c) {
|
if (!(r==row && c==column)) {
|
if(Cell *cell = cellAt(r, c)) {
|
cell->d_ptr->formula = sf;
|
} else {
|
auto newCell = std::make_shared<Cell>(result, Cell::NumberType, fmt, this);
|
newCell->d_ptr->formula = sf;
|
d->cellTable[r][c] = 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.
|
d->cellTable[row][column] = std::make_shared<Cell>(QVariant{}, Cell::NumberType, fmt, this);
|
|
return true;
|
}
|
/*!
|
\overload
|
Write a bool \a value to the cell \a row_column with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeBool(const CellReference &row_column, bool value, const Format &format)
|
{
|
if (!row_column.isValid())
|
return false;
|
|
return writeBool(row_column.row(), row_column.column(), value, format);
|
}
|
|
/*!
|
Write a bool \a value to the cell (\a row, \a column) with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeBool(int row, int column, bool value, const Format &format)
|
{
|
Q_D(Worksheet);
|
if (d->checkDimensions(row, column))
|
return false;
|
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
d->workbook->styles()->addXfFormat(fmt);
|
d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::BooleanType, fmt, this);
|
|
return true;
|
}
|
/*!
|
\overload
|
Write a QDateTime \a dt to the cell \a row_column with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeDateTime(const CellReference &row_column, const QDateTime &dt, const Format &format)
|
{
|
if (!row_column.isValid())
|
return false;
|
|
return writeDateTime(row_column.row(), row_column.column(), dt, format);
|
}
|
|
/*!
|
Write a QDateTime \a dt to the cell (\a row, \a column) with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeDateTime(int row, int column, const QDateTime &dt, const Format &format)
|
{
|
Q_D(Worksheet);
|
if (d->checkDimensions(row, column))
|
return false;
|
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
if (!fmt.isValid() || !fmt.isDateTimeFormat())
|
fmt.setNumberFormat(d->workbook->defaultDateFormat());
|
d->workbook->styles()->addXfFormat(fmt);
|
|
double value = datetimeToNumber(dt, d->workbook->isDate1904());
|
|
d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::NumberType, fmt, this);
|
|
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());
|
|
d->cellTable[row][column] = std::make_shared<Cell>(value, Cell::NumberType, fmt, this);
|
|
return true;
|
}
|
|
/*!
|
\overload
|
Write a QTime \a t to the cell \a row_column with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeTime(const CellReference &row_column, const QTime &t, const Format &format)
|
{
|
if (!row_column.isValid())
|
return false;
|
|
return writeTime(row_column.row(), row_column.column(), t, format);
|
}
|
|
/*!
|
Write a QTime \a t to the cell (\a row, \a column) with the \a format.
|
Returns true on success.
|
*/
|
bool Worksheet::writeTime(int row, int column, const QTime &t, const Format &format)
|
{
|
Q_D(Worksheet);
|
if (d->checkDimensions(row, column))
|
return false;
|
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
if (!fmt.isValid() || !fmt.isDateTimeFormat())
|
fmt.setNumberFormat(QStringLiteral("hh:mm:ss"));
|
d->workbook->styles()->addXfFormat(fmt);
|
|
d->cellTable[row][column] = std::make_shared<Cell>(timeToNumber(t), Cell::NumberType, fmt, this);
|
|
return true;
|
}
|
|
/*!
|
\overload
|
Write a QUrl \a url to the cell \a row_column with the given \a format \a display and \a tip.
|
Returns true on success.
|
*/
|
bool Worksheet::writeHyperlink(const CellReference &row_column, const QUrl &url, const Format &format, const QString &display, const QString &tip)
|
{
|
if (!row_column.isValid())
|
return false;
|
|
return writeHyperlink(row_column.row(), row_column.column(), url, format, display, tip);
|
}
|
|
/*!
|
Write a QUrl \a url to the cell (\a row, \a column) with the given \a format \a display and \a tip.
|
Returns true on success.
|
*/
|
bool Worksheet::writeHyperlink(int row, int column, const QUrl &url, const Format &format, const QString &display, const QString &tip)
|
{
|
Q_D(Worksheet);
|
if (d->checkDimensions(row, column))
|
return false;
|
|
//int error = 0;
|
|
QString urlString = url.toString();
|
|
//Generate proper display string
|
QString displayString = display.isEmpty() ? urlString : display;
|
if (displayString.startsWith(QLatin1String("mailto:")))
|
displayString.replace(QLatin1String("mailto:"), QString());
|
if (displayString.size() > XLSX_STRING_MAX) {
|
displayString = displayString.left(XLSX_STRING_MAX);
|
//error = -2;
|
}
|
|
/*
|
Location within target. If target is a workbook (or this workbook)
|
this shall refer to a sheet and cell or a defined name. Can also
|
be an HTML anchor if target is HTML file.
|
|
c:\temp\file.xlsx#Sheet!A1
|
http://a.com/aaa.html#aaaaa
|
*/
|
QString locationString;
|
if (url.hasFragment()) {
|
locationString = url.fragment();
|
urlString = url.toString(QUrl::RemoveFragment);
|
}
|
|
Format fmt = format.isValid() ? format : d->cellFormat(row, column);
|
//Given a default style for hyperlink
|
if (!fmt.isValid()) {
|
fmt.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);
|
d->cellTable[row][column] = std::make_shared<Cell>(displayString, Cell::SharedStringType, fmt, this);
|
|
//Store the hyperlink data in a separate table
|
d->urlTable[row][column] = QSharedPointer<XlsxHyperlinkData>(new XlsxHyperlinkData(XlsxHyperlinkData::External, urlString, locationString, QString(), tip));
|
|
return true;
|
}
|
|
/*!
|
* Add one DataValidation \a validation to the sheet.
|
* Returns true on success.
|
*/
|
bool Worksheet::addDataValidation(const DataValidation &validation)
|
{
|
Q_D(Worksheet);
|
if (validation.ranges().isEmpty() || validation.validationType()==DataValidation::None)
|
return false;
|
|
d->dataValidationsList.append(validation);
|
return true;
|
}
|
|
/*!
|
* Add one ConditionalFormatting \a cf to the sheet.
|
* Returns true on success.
|
*/
|
bool Worksheet::addConditionalFormatting(const ConditionalFormatting &cf)
|
{
|
Q_D(Worksheet);
|
if (cf.ranges().isEmpty())
|
return false;
|
|
for (int i=0; i<cf.d->cfRules.size(); ++i) {
|
const 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);
|
}
|
|
DrawingOneCellAnchor* 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 );
|
// QSharedPointer<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);
|
|
DrawingOneCellAnchor *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;
|
|
QSharedPointer<Chart> chart = QSharedPointer<Chart>(new Chart(this, F_NewFromScratch));
|
anchor->setObjectGraphicFrame(chart);
|
|
return chart.data();
|
}
|
|
/*!
|
Merge a \a range of cells. The first cell should contain the data and the others should
|
be blank. All cells will be applied the same style if a valid \a format is given.
|
Returns true on success.
|
|
\note All cells except the top-left one will be cleared.
|
*/
|
bool Worksheet::mergeCells(const CellRange &range, const Format &format)
|
{
|
Q_D(Worksheet);
|
if (range.rowCount() < 2 && range.columnCount() < 2)
|
return false;
|
|
if (d->checkDimensions(range.firstRow(), range.firstColumn()))
|
return false;
|
|
if (format.isValid())
|
{
|
d->workbook->styles()->addXfFormat(format);
|
}
|
|
for (int row = range.firstRow(); row <= range.lastRow(); ++row)
|
{
|
for (int col = range.firstColumn(); col <= range.lastColumn(); ++col)
|
{
|
if (row == range.firstRow() && col == range.firstColumn())
|
{
|
Cell *cell = cellAt(row, col);
|
if (cell)
|
{
|
if (format.isValid())
|
cell->d_ptr->format = format;
|
}
|
else
|
{
|
writeBlank(row, col, format);
|
}
|
}
|
else
|
{
|
writeBlank(row, col, format);
|
}
|
}
|
}
|
|
d->merges.append(range);
|
return true;
|
}
|
|
/*!
|
Unmerge the cells in the \a range. Returns true on success.
|
|
*/
|
bool Worksheet::unmergeCells(const CellRange &range)
|
{
|
Q_D(Worksheet);
|
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->default_row_height));
|
if (d->default_row_height != 15)
|
writer.writeAttribute(QStringLiteral("customHeight"), QStringLiteral("1"));
|
if (d->default_row_zeroed)
|
writer.writeAttribute(QStringLiteral("zeroHeight"), QStringLiteral("1"));
|
if (d->outline_row_level)
|
writer.writeAttribute(QStringLiteral("outlineLevelRow"), QString::number(d->outline_row_level));
|
if (d->outline_col_level)
|
writer.writeAttribute(QStringLiteral("outlineLevelCol"), QString::number(d->outline_col_level));
|
//for Excel 2010
|
// writer.writeAttribute("x14ac:dyDescent", "0.25");
|
writer.writeEndElement();//sheetFormatPr
|
|
if (!d->colsInfo.isEmpty())
|
{
|
writer.writeStartElement(QStringLiteral("cols"));
|
QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(d->colsInfo);
|
while (it.hasNext())
|
{
|
it.next();
|
QSharedPointer<XlsxColumnInfo> col_info = it.value();
|
writer.writeStartElement(QStringLiteral("col"));
|
writer.writeAttribute(QStringLiteral("min"), QString::number(col_info->firstColumn));
|
writer.writeAttribute(QStringLiteral("max"), QString::number(col_info->lastColumn));
|
if (col_info->width)
|
writer.writeAttribute(QStringLiteral("width"), QString::number(col_info->width, 'g', 15));
|
if (!col_info->format.isEmpty())
|
writer.writeAttribute(QStringLiteral("style"), QString::number(col_info->format.xfIndex()));
|
if (col_info->hidden)
|
writer.writeAttribute(QStringLiteral("hidden"), QStringLiteral("1"));
|
if (col_info->width)
|
writer.writeAttribute(QStringLiteral("customWidth"), QStringLiteral("1"));
|
if (col_info->outlineLevel)
|
writer.writeAttribute(QStringLiteral("outlineLevel"), QString::number(col_info->outlineLevel));
|
if (col_info->collapsed)
|
writer.writeAttribute(QStringLiteral("collapsed"), QStringLiteral("1"));
|
writer.writeEndElement();//col
|
}
|
writer.writeEndElement();//cols
|
}
|
|
writer.writeStartElement(QStringLiteral("sheetData"));
|
if (d->dimension.isValid())
|
d->saveXmlSheetData(writer);
|
writer.writeEndElement();//sheetData
|
|
d->saveXmlMergeCells(writer);
|
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.constFind(row_num);
|
auto riIt = rowsInfo.constFind(row_num);
|
if (ctIt == cellTable.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())
|
{
|
QSharedPointer<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.constEnd())
|
{
|
for (int col_num = dimension.firstColumn(); col_num <= dimension.lastColumn(); col_num++)
|
{
|
if (ctIt->contains(col_num))
|
{
|
saveXmlCellData(writer, row_num, col_num, (*ctIt)[col_num]);
|
}
|
}
|
}
|
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);
|
|
QMap<int, QSharedPointer<XlsxRowInfo> >::ConstIterator rIt;
|
QMap<int, QSharedPointer<XlsxColumnInfo> >::ConstIterator cIt;
|
|
//Style used by the cell, row or col
|
if (!cell->format().isEmpty())
|
writer.writeAttribute(QStringLiteral("s"), QString::number(cell->format().xfIndex()));
|
else if ((rIt = rowsInfo.constFind(row)) != rowsInfo.constEnd() && !(*rIt)->format.isEmpty())
|
writer.writeAttribute(QStringLiteral("s"), QString::number((*rIt)->format.xfIndex()));
|
else if ((cIt = colsInfoHelper.constFind(col)) != 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'
|
{
|
// dev67
|
|
double num = cell->value().toDouble();
|
bool is1904 = q->workbook()->isDate1904();
|
if (!is1904 && num > 60) // for mac os excel
|
{
|
num = num - 1;
|
}
|
|
// 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"));
|
QMapIterator<int, QMap< int, QSharedPointer<XlsxHyperlinkData> > > it(urlTable);
|
|
while (it.hasNext())
|
{
|
it.next();
|
int row = it.key();
|
QMapIterator< int, QSharedPointer<XlsxHyperlinkData> > it2(it.value());
|
|
while (it2.hasNext())
|
{
|
it2.next();
|
int col = it2.key();
|
QSharedPointer<XlsxHyperlinkData> data = it2.value();
|
QString ref = CellReference(row, col).toString();
|
|
// 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".
|
{
|
QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo);
|
while (it.hasNext()) {
|
it.next();
|
QSharedPointer<XlsxColumnInfo> info = it.value();
|
if (colFirst > info->firstColumn && colFirst <= info->lastColumn) {
|
//split the range,
|
QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info));
|
info->lastColumn = colFirst - 1;
|
info2->firstColumn = colFirst;
|
colsInfo.insert(colFirst, info2);
|
for (int c = info2->firstColumn; c <= info2->lastColumn; ++c)
|
colsInfoHelper[c] = info2;
|
|
break;
|
}
|
}
|
}
|
{
|
QMapIterator<int, QSharedPointer<XlsxColumnInfo> > it(colsInfo);
|
while (it.hasNext()) {
|
it.next();
|
QSharedPointer<XlsxColumnInfo> info = it.value();
|
if (colLast >= info->firstColumn && colLast < info->lastColumn) {
|
QSharedPointer<XlsxColumnInfo> info2(new XlsxColumnInfo(*info));
|
info->lastColumn = colLast;
|
info2->firstColumn = colLast + 1;
|
colsInfo.insert(colLast + 1, info2);
|
for (int c = info2->firstColumn; c <= info2->lastColumn; ++c)
|
colsInfoHelper[c] = info2;
|
|
break;
|
}
|
}
|
}
|
}
|
|
bool WorksheetPrivate::isColumnRangeValid(int colFirst, int colLast)
|
{
|
bool ignore_row = true;
|
bool ignore_col = false;
|
|
if (colFirst > colLast)
|
return false;
|
|
if (checkDimensions(1, colLast, ignore_row, ignore_col))
|
return false;
|
if (checkDimensions(1, colFirst, ignore_row, ignore_col))
|
return false;
|
|
return true;
|
}
|
|
QList<int> WorksheetPrivate ::getColumnIndexes(int colFirst, int colLast)
|
{
|
splitColsInfo(colFirst, colLast);
|
|
QList<int> nodes;
|
nodes.append(colFirst);
|
for (int col = colFirst; col <= colLast; ++col)
|
{
|
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 <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast);
|
for (const QSharedPointer<XlsxColumnInfo> &columnInfo : columnInfoList)
|
{
|
columnInfo->width = width;
|
}
|
|
return (columnInfoList.count() > 0);
|
}
|
|
/*!
|
Sets format property of a range of columns [\a colFirst, \a colLast] to \a format.
|
Columns are 1-indexed.
|
Returns true on success.
|
*/
|
bool Worksheet::setColumnFormat(int colFirst, int colLast, const Format &format)
|
{
|
Q_D(Worksheet);
|
|
const QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast);
|
for (const QSharedPointer<XlsxColumnInfo> &columnInfo : columnInfoList)
|
columnInfo->format = format;
|
|
if(columnInfoList.count() > 0) {
|
d->workbook->styles()->addXfFormat(format);
|
return true;
|
}
|
|
return false;
|
}
|
|
/*!
|
Sets hidden property of a range of columns [\a colFirst, \a colLast] to \a hidden.
|
Columns are 1-indexed. Returns true on success.
|
*/
|
bool Worksheet::setColumnHidden(int colFirst, int colLast, bool hidden)
|
{
|
Q_D(Worksheet);
|
|
const QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(colFirst, colLast);
|
for (const QSharedPointer<XlsxColumnInfo> &columnInfo : columnInfoList)
|
columnInfo->hidden = hidden;
|
|
return (columnInfoList.count() > 0);
|
}
|
|
/*!
|
Returns width of the \a column in characters of the normal font. Columns are 1-indexed.
|
*/
|
double Worksheet::columnWidth(int column)
|
{
|
Q_D(Worksheet);
|
|
QList< QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column);
|
|
// [dev54]
|
if ( columnInfoList.size() == 0 )
|
{
|
// column information is not found
|
// qDebug() << "[debug]" << __FUNCTION__ << "column (info) is not found. " << 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 <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column);
|
if (columnInfoList.count() == 1)
|
return columnInfoList.at(0)->format;
|
|
return Format();
|
}
|
|
/*!
|
Returns true if \a column is hidden. Columns are 1-indexed.
|
*/
|
bool Worksheet::isColumnHidden(int column)
|
{
|
Q_D(Worksheet);
|
|
QList <QSharedPointer<XlsxColumnInfo> > columnInfoList = d->getColumnInfoList(column, column);
|
if (columnInfoList.count() == 1)
|
return columnInfoList.at(0)->hidden;
|
|
return false;
|
}
|
|
/*!
|
Sets the \a height of the rows including and between \a rowFirst and \a rowLast.
|
Row height measured in point size.
|
Rows are 1-indexed.
|
|
Returns true if success.
|
*/
|
bool Worksheet::setRowHeight(int rowFirst,int rowLast, double height)
|
{
|
Q_D(Worksheet);
|
|
const QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast);
|
for (const QSharedPointer<XlsxRowInfo> &rowInfo : rowInfoList) {
|
rowInfo->height = height;
|
rowInfo->customHeight = true;
|
}
|
|
return rowInfoList.count() > 0;
|
}
|
|
/*!
|
Sets the \a format of the rows including and between \a rowFirst and \a rowLast.
|
Rows are 1-indexed.
|
|
Returns true if success.
|
*/
|
bool Worksheet::setRowFormat(int rowFirst,int rowLast, const Format &format)
|
{
|
Q_D(Worksheet);
|
|
const QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast);
|
for (const QSharedPointer<XlsxRowInfo> &rowInfo : rowInfoList)
|
rowInfo->format = format;
|
|
d->workbook->styles()->addXfFormat(format);
|
return rowInfoList.count() > 0;
|
}
|
|
/*!
|
Sets the \a hidden proeprty of the rows including and between \a rowFirst and \a rowLast.
|
Rows are 1-indexed. If hidden is true rows will not be visible.
|
|
Returns true if success.
|
*/
|
bool Worksheet::setRowHidden(int rowFirst,int rowLast, bool hidden)
|
{
|
Q_D(Worksheet);
|
|
const QList <QSharedPointer<XlsxRowInfo> > rowInfoList = d->getRowInfoList(rowFirst,rowLast);
|
for (const QSharedPointer<XlsxRowInfo> &rowInfo : rowInfoList)
|
rowInfo->hidden = hidden;
|
|
return rowInfoList.count() > 0;
|
}
|
|
/*!
|
Returns height of \a row in points.
|
*/
|
double Worksheet::rowHeight(int row)
|
{
|
Q_D(Worksheet);
|
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 {
|
QSharedPointer<XlsxRowInfo> info(new 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, QSharedPointer<XlsxRowInfo>(new 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;
|
QSharedPointer<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 {
|
QSharedPointer<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 = default_row_height;
|
return static_cast<int>(4.0 / 3.0 *height);
|
}
|
|
/*
|
Convert the width of a cell from user's units to pixels. Excel rounds
|
the column width to the nearest pixel. If the width hasn't been set
|
by the user we use the default value. If the column is hidden it
|
has a value of zero.
|
*/
|
int WorksheetPrivate::colPixelsSize(int col) const
|
{
|
double max_digit_width = 7.0; //For Calabri 11
|
double padding = 5.0;
|
int pixels = 0;
|
|
auto it = col_sizes.constFind(col);
|
if (it != col_sizes.constEnd()) {
|
double width = it.value();
|
if (width < 1)
|
pixels = static_cast<int>(width * (max_digit_width + padding) + 0.5);
|
else
|
pixels = static_cast<int>(width * max_digit_width + 0.5) + padding;
|
} else {
|
pixels = 64;
|
}
|
return pixels;
|
}
|
|
void WorksheetPrivate::loadXmlSheetData(QXmlStreamReader &reader)
|
{
|
Q_Q(Worksheet);
|
|
Q_ASSERT(reader.name() == QLatin1String("sheetData"));
|
|
while (!reader.atEnd() && !(reader.name() == QLatin1String("sheetData") && reader.tokenType() == QXmlStreamReader::EndElement))
|
{
|
if (reader.readNextStartElement())
|
{
|
if (reader.name() == QLatin1String("row"))
|
{
|
QXmlStreamAttributes attributes = reader.attributes();
|
|
if (attributes.hasAttribute(QLatin1String("customFormat"))
|
|| attributes.hasAttribute(QLatin1String("customHeight"))
|
|| attributes.hasAttribute(QLatin1String("hidden"))
|
|| attributes.hasAttribute(QLatin1String("outlineLevel"))
|
|| attributes.hasAttribute(QLatin1String("collapsed")))
|
{
|
|
QSharedPointer<XlsxRowInfo> info(new XlsxRowInfo);
|
if (attributes.hasAttribute(QLatin1String("customFormat")) &&
|
attributes.hasAttribute(QLatin1String("s")))
|
{
|
int idx = attributes.value(QLatin1String("s")).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;
|
}
|
}
|
|
}
|
else if (reader.name() == QLatin1String("c")) // Cell
|
{
|
|
//Cell
|
QXmlStreamAttributes attributes = reader.attributes();
|
QString r = attributes.value(QLatin1String("r")).toString();
|
CellReference pos(r);
|
|
//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[ pos.row() ][ pos.column() ] = cell;
|
|
}
|
}
|
}
|
}
|
|
void WorksheetPrivate::loadXmlColumnsInfo(QXmlStreamReader &reader)
|
{
|
Q_ASSERT(reader.name() == QLatin1String("cols"));
|
|
while (!reader.atEnd() &&
|
!(reader.name() == QLatin1String("cols") &&
|
reader.tokenType() == QXmlStreamReader::EndElement))
|
{
|
reader.readNextStartElement();
|
if (reader.tokenType() == QXmlStreamReader::StartElement)
|
{
|
if (reader.name() == QLatin1String("col"))
|
{
|
QSharedPointer<XlsxColumnInfo> info(new XlsxColumnInfo(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
|
QSharedPointer<XlsxHyperlinkData> link(new XlsxHyperlinkData);
|
link->display = attrs.value(QLatin1String("display")).toString();
|
link->tooltip = attrs.value(QLatin1String("tooltip")).toString();
|
link->location = attrs.value(QLatin1String("location")).toString();
|
|
if (attrs.hasAttribute(QLatin1String("r:id"))) {
|
link->linkType = XlsxHyperlinkData::External;
|
XlsxRelationship ship = relationships->getRelationshipById(attrs.value(QLatin1String("r:id")).toString());
|
link->target = ship.target;
|
} else {
|
link->linkType = XlsxHyperlinkData::Internal;
|
}
|
|
urlTable[pos.row()][pos.column()] = link;
|
}
|
}
|
}
|
}
|
|
QList <QSharedPointer<XlsxColumnInfo> > WorksheetPrivate::getColumnInfoList(int colFirst, int colLast)
|
{
|
QList <QSharedPointer<XlsxColumnInfo> > columnsInfoList;
|
if (isColumnRangeValid(colFirst,colLast))
|
{
|
QList<int> nodes = getColumnIndexes(colFirst, colLast);
|
|
for (int idx = 0; idx < nodes.size(); ++idx)
|
{
|
int colStart = nodes[idx];
|
auto it = colsInfo.constFind(colStart);
|
if (it != colsInfo.constEnd())
|
{
|
columnsInfoList.append(*it);
|
}
|
else
|
{
|
int colEnd = (idx == nodes.size() - 1) ? colLast : nodes[idx+1] - 1;
|
QSharedPointer<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 <QSharedPointer<XlsxRowInfo> > WorksheetPrivate::getRowInfoList(int rowFirst, int rowLast)
|
{
|
QList <QSharedPointer<XlsxRowInfo> > rowInfoList;
|
|
int min_col = dimension.firstColumn() < 1 ? 1 : dimension.firstColumn();
|
|
for(int row = rowFirst; row <= rowLast; ++row) {
|
if (checkDimensions(row, min_col, false, true))
|
continue;
|
|
QSharedPointer<XlsxRowInfo> rowInfo;
|
if ((rowsInfo[row]).isNull()){
|
rowsInfo[row] = QSharedPointer<XlsxRowInfo>(new XlsxRowInfo());
|
}
|
rowInfoList.append(rowsInfo[row]);
|
}
|
|
return rowInfoList;
|
}
|
|
bool Worksheet::loadFromXmlFile(QIODevice *device)
|
{
|
Q_D(Worksheet);
|
|
QXmlStreamReader reader(device);
|
while (!reader.atEnd())
|
{
|
reader.readNextStartElement();
|
if (reader.tokenType() == QXmlStreamReader::StartElement)
|
{
|
if (reader.name() == QLatin1String("dimension"))
|
{
|
QXmlStreamAttributes attributes = reader.attributes();
|
QString range = attributes.value(QLatin1String("ref")).toString();
|
d->dimension = CellRange(range);
|
}
|
else if (reader.name() == QLatin1String("sheetViews"))
|
{
|
d->loadXmlSheetViews(reader);
|
}
|
else if (reader.name() == QLatin1String("sheetFormatPr"))
|
{
|
d->loadXmlSheetFormatProps(reader);
|
}
|
else if (reader.name() == QLatin1String("cols"))
|
{
|
d->loadXmlColumnsInfo(reader);
|
}
|
else if (reader.name() == QLatin1String("sheetData"))
|
{
|
d->loadXmlSheetData(reader);
|
}
|
else if (reader.name() == QLatin1String("mergeCells"))
|
{
|
d->loadXmlMergeCells(reader);
|
}
|
else if (reader.name() == QLatin1String("dataValidations"))
|
{
|
d->loadXmlDataValidations(reader);
|
}
|
else if (reader.name() == QLatin1String("conditionalFormatting"))
|
{
|
ConditionalFormatting cf;
|
cf.loadFromXml(reader, workbook()->styles());
|
d->conditionalFormattingList.append(cf);
|
}
|
else if (reader.name() == QLatin1String("hyperlinks"))
|
{
|
d->loadXmlHyperlinks(reader);
|
}
|
else if(reader.name() == QLatin1String("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;
|
|
const auto firstRow = cellTable.constBegin().key();
|
|
const auto lastRow = (--cellTable.constEnd()).key();
|
|
int firstColumn = -1;
|
int lastColumn = -1;
|
|
for ( auto&& it = cellTable.constBegin()
|
; it != cellTable.constEnd()
|
; ++it )
|
{
|
Q_ASSERT(!it.value().isEmpty());
|
|
if (firstColumn == -1 || it.value().constBegin().key() < firstColumn)
|
firstColumn = it.value().constBegin().key();
|
|
if (lastColumn == -1 || (--it.value().constEnd()).key() > lastColumn)
|
{
|
lastColumn = (--it.value().constEnd()).key();
|
}
|
}
|
|
CellRange cr(firstRow, firstColumn, lastRow, lastColumn);
|
|
if (cr.isValid())
|
dimension = cr;
|
}
|
|
/*!
|
* \internal
|
* Unit test can use this member to get sharedString object.
|
*/
|
SharedStrings *WorksheetPrivate::sharedStrings() const
|
{
|
return workbook->sharedStrings();
|
}
|
|
QVector<CellLocation> Worksheet::getFullCells(int* maxRow, int* maxCol)
|
{
|
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;
|
}
|
|
QMapIterator< int, QMap< int, std::shared_ptr<Cell> > > _it( d->cellTable );
|
|
while ( _it.hasNext() )
|
{
|
_it.next();
|
|
int keyI = _it.key(); // key (cell row)
|
QMapIterator<int, std::shared_ptr<Cell> > _iit( _it.value() ); // value
|
|
while ( _iit.hasNext() )
|
{
|
_iit.next();
|
|
int keyII = _iit.key(); // key (cell column)
|
std::shared_ptr<Cell> ptrCell = _iit.value(); // value
|
|
CellLocation cl;
|
|
cl.row = keyI;
|
if ( keyI > (*maxRow) )
|
{
|
(*maxRow) = keyI;
|
}
|
|
cl.col = keyII;
|
if ( keyII > (*maxCol) )
|
{
|
(*maxCol) = keyII;
|
}
|
|
cl.cell = ptrCell;
|
|
ret.push_back( cl );
|
}
|
}
|
|
return ret;
|
}
|
|
QT_END_NAMESPACE_XLSX
|