diff --git a/examples/xlsx/extractdata/main.cpp b/examples/xlsx/extractdata/main.cpp index bd6154d..041aa5d 100644 --- a/examples/xlsx/extractdata/main.cpp +++ b/examples/xlsx/extractdata/main.cpp @@ -30,5 +30,12 @@ int main() qDebug()<value(); + } + //![2] + return 0; } diff --git a/examples/xlsx/formulas/main.cpp b/examples/xlsx/formulas/main.cpp index c1af539..c824af3 100644 --- a/examples/xlsx/formulas/main.cpp +++ b/examples/xlsx/formulas/main.cpp @@ -81,6 +81,11 @@ int main() //Make sure that read/write works well. Document xlsx2("Book1.xlsx"); + Worksheet *sharedFormulaSheet = dynamic_cast(xlsx2.sheet("SharedFormula")); + for (int row=2; row<20; ++row) { + qDebug()<read(row, 4); + } + xlsx2.saveAs("Book2.xlsx"); return 0; diff --git a/src/xlsx/xlsxutility.cpp b/src/xlsx/xlsxutility.cpp index 50f5797..ea8395e 100755 --- a/src/xlsx/xlsxutility.cpp +++ b/src/xlsx/xlsxutility.cpp @@ -23,6 +23,7 @@ ** ****************************************************************************/ #include "xlsxutility_p.h" +#include "xlsxcellreference.h" #include #include @@ -145,4 +146,82 @@ bool isSpaceReserveNeeded(const QString &s) return !s.isEmpty() && (spaces.contains(s.at(0))||spaces.contains(s.at(s.length()-1))); } +/* + * Convert shared formula for non-root cells. + * + * For example, if "B1:B10" have shared formula "=A1*A1", this function will return "=A2*A2" + * for "B2" cell, "=A3*A3" for "B3" cell, etc. + * + * Note, the formula "=A1*A1" for B1 can also be written as "=RC[-1]*RC[-1]", which is the same + * for all other cells. In other words, this formula is shared. + * + * For long run, we need a formula parser. + */ +QString convertSharedFormula(const QString &rootFormula, const CellReference &rootCell, const CellReference &cell) +{ + //Find all the "[A-Z]+[0-9]+" patterns in the rootFormula. + QList > segments; + + QString segment; + bool inQuote = false; + int cellFlag = 0; //-1, 0, 1, 2 ==> Invalid, Empty, A-Z ready, A1 ready + foreach (QChar ch, rootFormula) { + if (inQuote) { + segment.append(ch); + if (ch == QLatin1Char('"')) { + segments.append(qMakePair(segment, false)); + segment = QString(); + inQuote = false; + cellFlag = 0; + } + } else { + if (ch == QLatin1Char('"')) { + segments.append(qMakePair(segment, false)); + segment = QString(ch); + inQuote = true; + } else if (ch >= QLatin1Char('A') && ch <=QLatin1Char('Z')) { + if (cellFlag == 0 || cellFlag == 1) { + segment.append(ch); + } else { + segments.append(qMakePair(segment, (cellFlag == 2))); + segment = QString(ch); //start new "A1" segment + } + cellFlag = 1; + } else if (ch >= QLatin1Char('0') && ch <=QLatin1Char('9')) { + segment.append(ch); + if (cellFlag == 1) + cellFlag = 2; + } else { + if (cellFlag == 2) { + segments.append(qMakePair(segment, true)); //find one "A1" segment + segment = QString(ch); + } else { + segment.append(ch); + } + cellFlag = -1; + } + } + } + + if (!segment.isEmpty()) + segments.append(qMakePair(segment, (cellFlag == 2))); + + //Replace "A1" segment with proper one. + QStringList result; + typedef QPair PairType; + foreach (PairType p, segments) { + if (p.second) { + CellReference oldRef(p.first); + CellReference newRef(oldRef.row()-rootCell.row()+cell.row(), + oldRef.column()-rootCell.column()+cell.column()); + result.append(newRef.toString()); + } else { + result.append(p.first); + } + } + + //OK + return result.join(QString()); +} + } //namespace QXlsx diff --git a/src/xlsx/xlsxutility_p.h b/src/xlsx/xlsxutility_p.h index e1f1018..fcd7cc6 100755 --- a/src/xlsx/xlsxutility_p.h +++ b/src/xlsx/xlsxutility_p.h @@ -45,6 +45,7 @@ class QDateTime; class QTime; namespace QXlsx { +class CellReference; XLSX_AUTOTEST_EXPORT bool parseXsdBoolean(const QString &value, bool defaultValue=false); @@ -58,5 +59,7 @@ XLSX_AUTOTEST_EXPORT double timeToNumber(const QTime &t); XLSX_AUTOTEST_EXPORT QString createSafeSheetName(const QString &nameProposal); XLSX_AUTOTEST_EXPORT bool isSpaceReserveNeeded(const QString &string); +XLSX_AUTOTEST_EXPORT QString convertSharedFormula(const QString &rootFormula, const CellReference &rootCell, const CellReference &cell); + } //QXlsx #endif // XLSXUTILITY_H diff --git a/src/xlsx/xlsxworksheet.cpp b/src/xlsx/xlsxworksheet.cpp index e813ed0..a407fa3 100755 --- a/src/xlsx/xlsxworksheet.cpp +++ b/src/xlsx/xlsxworksheet.cpp @@ -512,11 +512,28 @@ QVariant Worksheet::read(const CellReference &row_column) const */ QVariant Worksheet::read(int row, int column) const { + Q_D(const Worksheet); + Cell *cell = cellAt(row, column); if (!cell) return QVariant(); - if (cell->hasFormula() && cell->formula().formulaType() == CellFormula::NormalType) - return QVariant(QLatin1String("=")+cell->formula().formulaText()); + + 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 { + const CellFormula &rootFormula = d->sharedFormulaMap[cell->formula().sharedIndex()]; + CellReference rootCellRef = rootFormula.reference().topLeft(); + QString rootFormulaText = rootFormula.formulaText(); + QString newFormulaText = convertSharedFormula(rootFormulaText, rootCellRef, CellReference(row, column)); + return QVariant(QLatin1String("=")+newFormulaText); + } + } + } + if (cell->isDateTime()) { double val = cell->value().toDouble(); QDateTime dt = cell->dateTime(); @@ -526,6 +543,7 @@ QVariant Worksheet::read(int row, int column) const return dt.date(); return dt; } + return cell->value(); } diff --git a/tests/auto/utility/tst_utilitytest.cpp b/tests/auto/utility/tst_utilitytest.cpp index a53f745..3c3a3c9 100644 --- a/tests/auto/utility/tst_utilitytest.cpp +++ b/tests/auto/utility/tst_utilitytest.cpp @@ -23,6 +23,7 @@ ** ****************************************************************************/ #include "private/xlsxutility_p.h" +#include "xlsxcellreference.h" #include #include #include @@ -46,6 +47,9 @@ private Q_SLOTS: void test_createSafeSheetName_data(); void test_createSafeSheetName(); + + void test_convertSharedFormula_data(); + void test_convertSharedFormula(); }; UtilityTest::UtilityTest() @@ -148,6 +152,30 @@ void UtilityTest::test_createSafeSheetName() QCOMPARE(QXlsx::createSafeSheetName(original), result); } +void UtilityTest::test_convertSharedFormula_data() +{ + QTest::addColumn("original"); + QTest::addColumn("rootCell"); + QTest::addColumn("cell"); + QTest::addColumn("result"); + + QTest::newRow("[Simple B2]") << QString("A1*A1")<