File indexing completed on 2025-01-19 13:27:37

0001 /*
0002  * This file is part of Office 2007 Filters for Calligra
0003  *
0004  * Copyright (C) 2010 Sebastian Sauer <sebsauer@kdab.com>
0005  * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
0006  *
0007  * Contact: Suresh Chande suresh.chande@nokia.com
0008  *
0009  * This library is free software; you can redistribute it and/or
0010  * modify it under the terms of the GNU Lesser General Public License
0011  * version 2.1 as published by the Free Software Foundation.
0012  *
0013  * This library is distributed in the hope that it will be useful, but
0014  * WITHOUT ANY WARRANTY; without even the implied warranty of
0015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0016  * Lesser General Public License for more details.
0017  *
0018  * You should have received a copy of the GNU Lesser General Public
0019  * License along with this library; if not, write to the Free Software
0020  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
0021  * 02110-1301 USA
0022  *
0023  */
0024 
0025 // Own
0026 #include "XlsxUtils.h"
0027 #include "XlsxXmlWorksheetReader.h"
0028 
0029 #include "XlsxXmlCommentsReader.h"
0030 #include "XlsxXmlStylesReader.h"
0031 #include "XlsxXmlDocumentReader.h"
0032 #include "XlsxXmlDrawingReader.h"
0033 #include "XlsxXmlChartReader.h"
0034 #include "XlsxXmlTableReader.h"
0035 #include "XlsxImport.h"
0036 #include "Charting.h"
0037 #include "XlsxChartOdfWriter.h"
0038 #include "FormulaParser.h"
0039 
0040 #include <MsooXmlRelationships.h>
0041 #include <MsooXmlSchemas.h>
0042 #include <MsooXmlUtils.h>
0043 #include <MsooXmlUnits.h>
0044 #include <MsooXmlGlobal.h>
0045 
0046 #include <KoUnit.h>
0047 #include <KoXmlWriter.h>
0048 #include <KoGenStyles.h>
0049 #include <styles/KoCharacterStyle.h>
0050 
0051 #include <sheets/Util.h>
0052 
0053 #include <QBrush>
0054 #include <QRegExp>
0055 #include <QString>
0056 #include <QList>
0057 #include <QCache>
0058 
0059 #include "NumberFormatParser.h"
0060 
0061 #define XLSXXMLWORKSHEETREADER_CPP
0062 
0063 #define UNICODE_EUR 0x20AC
0064 #define UNICODE_GBP 0x00A3
0065 #define UNICODE_JPY 0x00A5
0066 
0067 #undef  MSOOXML_CURRENT_NS // tags without namespace
0068 #define MSOOXML_CURRENT_CLASS XlsxXmlWorksheetReader
0069 #define BIND_READ_CLASS MSOOXML_CURRENT_CLASS
0070 
0071 #include <MsooXmlReader_p.h>
0072 
0073 #include <cmath>
0074 #include <algorithm>
0075 
0076 // ----------------------------------------------------------------
0077 // Include implementation of common tags
0078 
0079 #include <MsooXmlCommonReaderImpl.h> // this adds p, pPr, t, r, etc.
0080 
0081 #undef  MSOOXML_CURRENT_NS // tags without namespace
0082 #define MSOOXML_CURRENT_NS
0083 
0084 // ----------------------------------------------------------------
0085 
0086 #define NO_DRAWINGML_NS
0087 #define NO_DRAWINGML_PIC_NS // DrawingML/Picture
0088 
0089 #include <MsooXmlCommonReaderDrawingMLImpl.h> // this adds pic, etc.
0090 
0091 #include "XlsxXmlWorksheetReader_p.h"
0092 
0093 XlsxXmlWorksheetReaderContext::XlsxXmlWorksheetReaderContext(
0094     uint _worksheetNumber,
0095     uint _numberOfWorkSheets,
0096     const QString& _worksheetName,
0097     const QString& _state,
0098     const QString _path, const QString _file,
0099     MSOOXML::DrawingMLTheme*& _themes,
0100     const QVector<QString>& _sharedStrings,
0101     const XlsxComments& _comments,
0102     const XlsxStyles& _styles,
0103     MSOOXML::MsooXmlRelationships& _relationships,
0104     XlsxImport* _import,
0105     QMap<QString, QString> _oleReplacements,
0106     QMap<QString, QString> _oleBeginFrames,
0107     QVector<XlsxXmlDocumentReaderContext::AutoFilter>& autoFilters)
0108         : MSOOXML::MsooXmlReaderContext(&_relationships)
0109         , sheet(new Sheet(_worksheetName))
0110         , worksheetNumber(_worksheetNumber)
0111         , numberOfWorkSheets(_numberOfWorkSheets)
0112         , worksheetName(_worksheetName)
0113         , state(_state)
0114         , themes(_themes)
0115         , sharedStrings(&_sharedStrings)
0116         , comments(&_comments)
0117         , styles(&_styles)
0118         , import(_import)
0119         , path(_path)
0120         , file(_file)
0121         , oleReplacements(_oleReplacements)
0122         , oleFrameBegins(_oleBeginFrames)
0123         , autoFilters(autoFilters)
0124 {
0125 }
0126 
0127 XlsxXmlWorksheetReaderContext::~XlsxXmlWorksheetReaderContext()
0128 {
0129     delete sheet;
0130 }
0131 
0132 static void splitToRowAndColumn(const char *source, int sourceStart, int sourceLength, QString& row, int& column)
0133 {
0134     // find the position of the first number
0135     int pos = 0;
0136     while (pos < sourceLength) {
0137         if (source[sourceStart + pos] < 65) {
0138             break;
0139         }
0140         row.append(source[sourceStart + pos]);
0141         pos++;
0142     }
0143 
0144     char *pEnd = 0;
0145     column = strtol(source + sourceStart + pos, &pEnd, 10);
0146 }
0147 
0148 //! @return value @a cm with cm suffix
0149 static QString printCm(double cm)
0150 {
0151     QString string;
0152     string.sprintf("%3.3fcm", cm);
0153     return string;
0154 }
0155 
0156 
0157 QList<QMap<QString, QString> > XlsxXmlWorksheetReaderContext::conditionalStyleForPosition(const QString& positionLetter, int positionNumber)
0158 {
0159     QString startLetter, endLetter;
0160     int startNumber, endNumber;
0161 
0162     QList<QMap<QString, QString> > returnMaps;
0163 
0164     // Known positions which are hits/misses for this
0165     // purpose is to optimize this code part for a large set of conditions
0166     QList<QString> cachedHits, cachedMisses;
0167 
0168     // We do not wish to add the same condition twice
0169     QList<QString> addedConditions;
0170 
0171     int index = 0;
0172     while (index < conditionalStyles.size()) {
0173         startLetter.clear();
0174         endLetter.clear();
0175 
0176         QString range = conditionalStyles.at(index).first;
0177         if (cachedHits.contains(range)) {
0178             if (!addedConditions.contains(conditionalStyles.at(index).second.value("style:condition"))) {
0179                 returnMaps.push_back(conditionalStyles.at(index).second);
0180                 addedConditions.push_back(conditionalStyles.at(index).second.value("style:condition"));
0181             }
0182             ++index;
0183             continue;
0184         }
0185         if (cachedMisses.contains(range)) {
0186             ++index;
0187             continue;
0188         }
0189 
0190         QByteArray ba = range.toLatin1();
0191 
0192         int columnIndex = ba.indexOf(':');
0193         if (columnIndex < 0) {
0194             splitToRowAndColumn(ba.constData(), 0, ba.length(), startLetter, startNumber);
0195             endLetter.clear();
0196         }
0197         else {
0198             splitToRowAndColumn(ba.constData(), 0, columnIndex, startLetter, startNumber);
0199             splitToRowAndColumn(ba.constData(), columnIndex + 1, ba.size() - (columnIndex + 1), endLetter, endNumber);
0200         }
0201 
0202         if ((positionLetter == startLetter && positionNumber == startNumber && endLetter.isEmpty()) ||
0203             (positionLetter >= startLetter && positionNumber >= startNumber &&
0204              positionLetter <= endLetter && positionNumber <= endNumber)) {
0205             if (!addedConditions.contains(conditionalStyles.at(index).second.value("style:condition"))) {
0206                 returnMaps.push_back(conditionalStyles.at(index).second);
0207                 addedConditions.push_back(conditionalStyles.at(index).second.value("style:condition"));
0208             }
0209             cachedHits.push_back(range);
0210             ++index;
0211             continue;
0212         }
0213         else {
0214             cachedMisses.push_back(range);
0215             ++index;
0216             continue;
0217         }
0218         ++index;
0219     }
0220 
0221     return returnMaps;
0222 }
0223 
0224 const char XlsxXmlWorksheetReader::officeValue[] = "office:value";
0225 const char XlsxXmlWorksheetReader::officeDateValue[] = "office:date-value";
0226 const char XlsxXmlWorksheetReader::officeStringValue[] = "office:string-value";
0227 const char XlsxXmlWorksheetReader::officeTimeValue[] = "office:time-value";
0228 const char XlsxXmlWorksheetReader::officeBooleanValue[] = "office:boolean-value";
0229 
0230 class XlsxXmlWorksheetReader::Private
0231 {
0232 public:
0233     Private( XlsxXmlWorksheetReader* qq )
0234      : q( qq ),
0235        warningAboutWorksheetSizeDisplayed(false),
0236        drawingNumber(0)
0237     {
0238     }
0239     //~Private(){ qDeleteAll( savedStyles ); }
0240 
0241     XlsxXmlWorksheetReader* const q;
0242     bool warningAboutWorksheetSizeDisplayed;
0243     int drawingNumber;
0244     QHash<int, Cell*> sharedFormulas;
0245     QHash<QString, QString > savedStyles;
0246 };
0247 
0248 XlsxXmlWorksheetReader::XlsxXmlWorksheetReader(KoOdfWriters *writers)
0249         : MSOOXML::MsooXmlCommonReader(writers)
0250         , m_context(0)
0251         , d(new Private( this ) )
0252 {
0253     init();
0254 }
0255 
0256 XlsxXmlWorksheetReader::~XlsxXmlWorksheetReader()
0257 {
0258     delete d;
0259 }
0260 
0261 void XlsxXmlWorksheetReader::init()
0262 {
0263     initInternal(); // MsooXmlCommonReaderImpl.h
0264     initDrawingML();
0265     m_defaultNamespace = "";
0266     m_columnCount = 0;
0267     m_currentRow = 0;
0268     m_currentColumn = 0;
0269 }
0270 
0271 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read(MSOOXML::MsooXmlReaderContext* context)
0272 {
0273     m_context = dynamic_cast<XlsxXmlWorksheetReaderContext*>(context);
0274     Q_ASSERT(m_context);
0275     const KoFilter::ConversionStatus result = readInternal();
0276     m_context = 0;
0277 
0278     return result;
0279 }
0280 
0281 KoFilter::ConversionStatus XlsxXmlWorksheetReader::readInternal()
0282 {
0283     qCDebug(lcXlsxImport) << "=============================";
0284     Q_ASSERT(m_context);
0285 
0286     readNext();
0287     if (!isStartDocument()) {
0288         return KoFilter::WrongFormat;
0289     }
0290 
0291     // worksheet
0292     readNext();
0293     //qCDebug(lcXlsxImport) << *this << namespaceUri();
0294 
0295     if (name() != "worksheet" && name() != "dialogsheet" && name() != "chartsheet") {
0296         return KoFilter::WrongFormat;
0297     }
0298     if (!expectNS(MSOOXML::Schemas::spreadsheetml)) {
0299         return KoFilter::WrongFormat;
0300     }
0301 
0302     m_context->sheet->setVisible( m_context->state.toLower() != "hidden" );
0303 
0304     QXmlStreamNamespaceDeclarations namespaces(namespaceDeclarations());
0305     for (int i = 0; i < namespaces.count(); i++) {
0306         qCDebug(lcXlsxImport) << "NS prefix:" << namespaces[i].prefix() << "uri:" << namespaces[i].namespaceUri();
0307     }
0308 //! @todo find out whether the namespace returned by namespaceUri()
0309 //!       is exactly the same ref as the element of namespaceDeclarations()
0310     if (!namespaces.contains(QXmlStreamNamespaceDeclaration("", MSOOXML::Schemas::spreadsheetml))) {
0311         raiseError(i18n("Namespace \"%1\" not found", QLatin1String(MSOOXML::Schemas::spreadsheetml)));
0312         return KoFilter::WrongFormat;
0313     }
0314 //! @todo expect other namespaces too...
0315 
0316     if (name() == "worksheet") {
0317         TRY_READ(worksheet)
0318     }
0319     else if (name() == "dialogsheet") {
0320         TRY_READ(dialogsheet)
0321     }
0322 
0323     qCDebug(lcXlsxImport) << "===========finished============";
0324     return KoFilter::OK;
0325 }
0326 
0327 
0328 QString XlsxXmlWorksheetReader::computeColumnWidth(qreal widthNumber) const
0329 {
0330     //! CASE #S3300
0331     //! Column width measured as the number of characters of the maximum digit width of the
0332     //! numbers 0, 1, 2, …, 9 as rendered in the normal style's font. There are 4 pixels of margin
0333     //! padding (two on each side), plus 1 pixel padding for the gridlines.
0334     //! For explanation of width, see p. 1778
0335     //simplified:
0336     //! @todo hardcoded, not 100% accurate
0337     qCDebug(lcXlsxImport) << "PT_TO_PX(11.0):" << PT_TO_PX(11.0);
0338     const double realSize = round(PT_TO_PX(11.0)) * 0.75;
0339     qCDebug(lcXlsxImport) << "realSize:" << realSize;
0340     const double averageDigitWidth = realSize * 2.0 / 3.0;
0341     qCDebug(lcXlsxImport) << "averageDigitWidth:" << averageDigitWidth;
0342 
0343     QString result;
0344     if (averageDigitWidth * widthNumber == 0) {
0345         result = QLatin1String("0cm");
0346     }
0347     else
0348     {
0349         result = printCm(PX_TO_CM(averageDigitWidth * widthNumber));
0350     }
0351 
0352     return result;
0353 }
0354 
0355 void XlsxXmlWorksheetReader::showWarningAboutWorksheetSize()
0356 {
0357     if (d->warningAboutWorksheetSizeDisplayed)
0358         return;
0359     d->warningAboutWorksheetSizeDisplayed = true;
0360     qCWarning(lcXlsxImport) << i18n("The data could not be loaded completely because the maximum size of "
0361         "sheet was exceeded.");
0362 }
0363 
0364 inline static QString encodeLabelText(int col, int row)
0365 {
0366     return Calligra::Sheets::Util::encodeColumnLabelText(col) + QString::number(row);
0367 }
0368 
0369 void XlsxXmlWorksheetReader::saveAnnotation(int col, int row)
0370 {
0371     QString ref(encodeLabelText(col + 1, row + 1));
0372     qCDebug(lcXlsxImport) << ref;
0373     XlsxComment *comment = m_context->comments->value(ref);
0374     if (!comment)
0375         return;
0376     //qCDebug(lcXlsxImport) << "Saving annotation for cell" << ref;
0377     body->startElement("office:annotation");
0378     body->startElement("dc:creator");
0379     body->addTextNode(comment->author(m_context->comments));
0380     body->endElement(); // dc:creator
0381     //! @todo support dc:date
0382     body->startElement("text:p");
0383     body->addCompleteElement(comment->texts.toUtf8());
0384     body->endElement(); // text:p
0385     body->endElement(); // office:annotation
0386 }
0387 
0388 #undef CURRENT_EL
0389 #define CURRENT_EL chartsheet
0390 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_chartsheet()
0391 {
0392     READ_PROLOGUE
0393 
0394     return read_sheetHelper("chartsheet");
0395 
0396     READ_EPILOGUE
0397 }
0398 
0399 #undef CURRENT_EL
0400 #define CURRENT_EL dialogsheet
0401 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_dialogsheet()
0402 {
0403     READ_PROLOGUE
0404 
0405     return read_sheetHelper("dialogsheet");
0406 
0407     READ_EPILOGUE
0408 }
0409 
0410 #undef CURRENT_EL
0411 #define CURRENT_EL worksheet
0412 //! worksheet handler (Worksheet)
0413 /*! ECMA-376, 18.3.1.99, p. 1894.
0414  Root element of Worksheet parts within a SpreadsheetML document.
0415  Child elements:
0416  - [done] autoFilter (AutoFilter Settings) §18.3.1.2
0417  - cellWatches (Cell Watch Items) §18.3.1.9
0418  - colBreaks (Vertical Page Breaks) §18.3.1.14
0419  - [done] cols (Column Information) §18.3.1.17
0420  - [done] conditionalFormatting (Conditional Formatting) §18.3.1.18
0421  - [done] controls (Embedded Controls) §18.3.1.21
0422  - customProperties (Custom Properties) §18.3.1.23
0423  - customSheetViews (Custom Sheet Views) §18.3.1.27
0424  - dataConsolidate (Data Consolidate) §18.3.1.29
0425  - dataValidations (Data Validations) §18.3.1.33
0426  - dimension (Worksheet Dimensions) §18.3.1.35
0427  - [done] drawing (Drawing) §18.3.1.36
0428  - drawingHF (Drawing Reference in Header Footer) §18.3.1.37
0429  - extLst (Future Feature Data Storage Area) §18.2.10
0430  - headerFooter (Header Footer Settings) §18.3.1.46
0431  - [done] hyperlinks (Hyperlinks) §18.3.1.48
0432  - ignoredErrors (Ignored Errors) §18.3.1.51
0433  - [done] mergeCells (Merge Cells) §18.3.1.55
0434  - [done] oleObjects (Embedded Objects) §18.3.1.60
0435  - pageMargins (Page Margins) §18.3.1.62
0436  - pageSetup (Page Setup Settings) §18.3.1.63
0437  - phoneticPr (Phonetic Properties) §18.4.3
0438  - [done] picture (Background Image) §18.3.1.67
0439  - printOptions (Print Options) §18.3.1.70
0440  - protectedRanges (Protected Ranges) §18.3.1.72
0441  - rowBreaks (Horizontal Page Breaks (Row)) §18.3.1.74
0442  - scenarios (Scenarios) §18.3.1.76
0443  - sheetCalcPr (Sheet Calculation Properties) §18.3.1.79
0444  - [done] sheetData (Sheet Data) §18.3.1.80
0445  - sheetFormatPr (Sheet Format Properties) §18.3.1.81
0446  - sheetPr (Sheet Properties) §18.3.1.82
0447  - sheetProtection (Sheet Protection Options) §18.3.1.85
0448  - sheetViews (Sheet Views) §18.3.1.88
0449  - smartTags (Smart Tags) §18.3.1.90
0450  - sortState (Sort State) §18.3.1.92
0451  - [done] tableParts (Table Parts) §18.3.1.95
0452  - webPublishItems (Web Publishing Items) §18.3.1.98
0453 
0454  @todo support all child elements
0455 */
0456 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_worksheet()
0457 {
0458     READ_PROLOGUE
0459 
0460     return read_sheetHelper("worksheet");
0461 
0462     READ_EPILOGUE
0463 }
0464 
0465 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_sheetHelper(const QString& type)
0466 {
0467     // In the first round we do not wish to output anything
0468     QBuffer fakeBuffer;
0469     KoXmlWriter fakeBody(&fakeBuffer);
0470     KoXmlWriter *oldBody = body;
0471     if (m_context->firstRoundOfReading) {
0472         body = &fakeBody;
0473     }
0474 
0475     body->startElement("table:table");
0476 
0477 //! @todo implement CASE #S202 for fixing the name
0478     body->addAttribute("table:name", m_context->worksheetName);
0479 
0480     m_tableStyle = KoGenStyle(KoGenStyle::TableAutoStyle, "table");
0481 //! @todo hardcoded master page name
0482     m_tableStyle.addAttribute("style:master-page-name",
0483                                   QString("PageStyle_5f_Test_20_sheet_20__5f_%1").arg(m_context->worksheetNumber));
0484 
0485     m_tableStyle.addProperty("table:display", m_context->sheet->visible());
0486 
0487     //The style might be changed depending on what elements we find,
0488     //hold the body writer so that we can set the proper style
0489     KoXmlWriter* heldBody = body;
0490     QBuffer bodyBuffer;
0491     bodyBuffer.open(QIODevice::ReadWrite);
0492     body = new KoXmlWriter(&bodyBuffer);
0493 
0494     QBuffer drawingBuffer;
0495     drawingBuffer.open(QIODevice::ReadWrite);
0496 
0497     while (!atEnd()) {
0498         readNext();
0499         qCDebug(lcXlsxImport) << *this;
0500         if (isEndElement() && name() == type) {
0501             break;
0502         }
0503         if (isStartElement() && !m_context->firstRoundOfReading) {
0504             TRY_READ_IF(sheetFormatPr)
0505             ELSE_TRY_READ_IF(cols)
0506             ELSE_TRY_READ_IF(sheetData) // does fill the m_context->sheet
0507             ELSE_TRY_READ_IF(mergeCells)
0508             else if (name() == "drawing") {
0509                 KoXmlWriter *tempBodyHolder = body;
0510                 body = new KoXmlWriter(&drawingBuffer);
0511                 TRY_READ(drawing)
0512                 delete body;
0513                 body = tempBodyHolder;
0514             }
0515             ELSE_TRY_READ_IF(legacyDrawing)
0516             ELSE_TRY_READ_IF(hyperlinks)
0517             ELSE_TRY_READ_IF(picture)
0518             ELSE_TRY_READ_IF(oleObjects)
0519             else if (name() == "controls") {
0520                 KoXmlWriter *tempBodyHolder = body;
0521                 body = new KoXmlWriter(&drawingBuffer);
0522                 TRY_READ(controls)
0523                 delete body;
0524                 body = tempBodyHolder;
0525             }
0526             ELSE_TRY_READ_IF(autoFilter)
0527             SKIP_UNKNOWN
0528         }
0529         else if (isStartElement() && m_context->firstRoundOfReading) {
0530             TRY_READ_IF(conditionalFormatting)
0531             ELSE_TRY_READ_IF(tableParts)
0532             SKIP_UNKNOWN
0533         }
0534     }
0535 
0536     if (m_context->firstRoundOfReading) {
0537         // Sorting conditional styles according to the priority
0538 
0539         typedef QPair<int, QMap<QString, QString> > Condition;
0540 
0541         // Transforming to a list for easier handling
0542         QList<QPair<QPair<QString, QMap<QString, QString> >, int> > diffFormulasList;
0543         QMapIterator<QString, QList<Condition> > i(m_conditionalStyles);
0544         while (i.hasNext()) {
0545             i.next();
0546             int index = 0;
0547             QList<Condition> conditions = i.value();
0548             QPair<QString, QMap<QString, QString> > innerPair;
0549             QPair<QPair<QString, QMap<QString, QString> >, int> outerPair;
0550 
0551             while (index < conditions.size()) {
0552                 innerPair.first = i.key();
0553                 innerPair.second = conditions.at(index).second;
0554                 outerPair.first = innerPair;
0555                 outerPair.second = conditions.at(index).first;
0556                 diffFormulasList.push_back(outerPair);
0557                 ++index;
0558             }
0559         }
0560         QList<QPair<int, int> > priorityActualIndex;
0561         int index = 0;
0562         while (index < diffFormulasList.size()) {
0563             priorityActualIndex.push_back(QPair<int, int>(diffFormulasList.at(index).second, index));
0564             ++index;
0565         }
0566         std::sort(priorityActualIndex.begin(), priorityActualIndex.end());
0567 
0568         // Finally we have the list sorted and we can store the conditions in right priority order
0569         index = 0;
0570         while (index < priorityActualIndex.size()) {
0571             QPair<QString, QMap<QString, QString> > odfValue;
0572             odfValue.first = diffFormulasList.at(priorityActualIndex.at(index).second).first.first;
0573             odfValue.second = diffFormulasList.at(priorityActualIndex.at(index).second).first.second;
0574             m_context->conditionalStyles.push_back(odfValue);
0575             ++index;
0576         }
0577     }
0578 
0579     if( !m_context->sheet->pictureBackgroundPath().isNull() ) {
0580         QBuffer buffer;
0581         buffer.open(QIODevice::WriteOnly);
0582         KoXmlWriter writer(&buffer);
0583 
0584         writer.startElement("style:background-image");
0585         writer.addAttribute("xlink:href", m_context->sheet->pictureBackgroundPath());
0586         writer.addAttribute("xlink:type", "simple");
0587         writer.addAttribute("xlink:show", "embed");
0588         writer.addAttribute("xlink:actuate", "onLoad");
0589         writer.endElement();
0590 
0591         buffer.close();
0592         m_tableStyle.addChildElement("style:background-image", QString::fromUtf8(buffer.buffer(), buffer.buffer().size()));
0593     }
0594 
0595     const QString currentTableStyleName(mainStyles->insert(m_tableStyle, "ta"));
0596     heldBody->addAttribute("table:style-name", currentTableStyleName);
0597     heldBody->addCompleteElement(&bodyBuffer);
0598     delete body;
0599     body = heldBody;
0600 
0601     // Adding drawings, if there are any
0602     if (drawingBuffer.size() > 0) {
0603         body->startElement("table:shapes");
0604         body->addCompleteElement(&drawingBuffer);
0605         body->endElement(); // table:shapes
0606     }
0607 
0608     // now we have everything to start writing the actual cells
0609     int c = 0;
0610     while (c <= m_context->sheet->maxColumn()) {
0611         body->startElement("table:table-column");
0612         int repeatedColumns = 1;
0613         bool currentColumnHidden = false;
0614         Column* column = m_context->sheet->column(c, false);
0615         if (column) {
0616             if (!column->styleName.isEmpty()) {
0617                 body->addAttribute("table:style-name", column->styleName);
0618             } else {
0619                 if (m_context->sheet->m_defaultColWidth != -1.0) {
0620                     saveColumnStyle( computeColumnWidth( m_context->sheet->m_defaultColWidth ) );
0621                 }
0622             }
0623         }
0624 
0625         if (column && column->hidden) {
0626             body->addAttribute("table:visibility", "collapse");
0627             currentColumnHidden = true;
0628         }
0629         ++c;
0630         while (c <= m_context->sheet->maxColumn()) {
0631             column = m_context->sheet->column(c, false);
0632             if (column && column->hidden ) {
0633                 if (currentColumnHidden) {
0634                     ++repeatedColumns;
0635                 }
0636                 else {
0637                     break;
0638                 }
0639             }
0640             else {
0641                 if (!currentColumnHidden) {
0642                     ++repeatedColumns;
0643                 }
0644                 else {
0645                     break;
0646                 }
0647             }
0648             ++c;
0649         }
0650         if (repeatedColumns > 1) {
0651            body->addAttribute("table:number-columns-repeated", repeatedColumns);
0652         }
0653         body->endElement();  // table:table-column
0654     }
0655 
0656     const int rowCount = m_context->sheet->maxRow();
0657     for(int r = 0; r <= rowCount; ++r) {
0658         const int columnCount = m_context->sheet->maxCellsInRow(r);
0659         Row* row = m_context->sheet->row(r, false);
0660         body->startElement("table:table-row");
0661         if (row) {
0662             if (!row->styleName.isEmpty()) {
0663                 body->addAttribute("table:style-name", row->styleName);
0664             } else if (m_context->sheet->m_defaultRowHeight != -1.0) {
0665                 QString styleName = processRowStyle(m_context->sheet->m_defaultRowHeight); // in pt
0666                 body->addAttribute("table:style-name", styleName);
0667             }
0668 
0669             if (row->hidden) {
0670                 body->addAttribute("table:visibility", "collapse");
0671             }
0672             //body->addAttribute("table:number-rows-repeated", QByteArray::number(row->repeated));
0673 
0674             for(int c = 0; c <= columnCount; ++c) {
0675                 body->startElement("table:table-cell");
0676                 if (Cell* cell = m_context->sheet->cell(c, r, false)) {
0677                     const bool hasHyperlink = ! cell->hyperlink().isEmpty();
0678 
0679                     if (!cell->styleName.isEmpty()) {
0680                         body->addAttribute("table:style-name", cell->styleName);
0681                     }
0682                     //body->addAttribute("table:number-columns-repeated", QByteArray::number(cell->repeated));
0683                     if (!hasHyperlink) {
0684                         switch(cell->valueType) {
0685                             case Cell::ConstNone:
0686                                 break;
0687                             case Cell::ConstString:
0688                                 body->addAttribute("office:value-type", MsooXmlReader::constString);
0689                                 break;
0690                             case Cell::ConstBoolean:
0691                                 body->addAttribute("office:value-type", MsooXmlReader::constBoolean);
0692                                 break;
0693                             case Cell::ConstDate:
0694                                 body->addAttribute("office:value-type", MsooXmlReader::constDate);
0695                                 break;
0696                             case Cell::ConstFloat:
0697                                 body->addAttribute("office:value-type", MsooXmlReader::constFloat);
0698                                 break;
0699                         }
0700                     }
0701 
0702                     if (cell->valueAttrValue) {
0703                         switch(cell->valueAttr) {
0704                             case Cell::OfficeNone:
0705                                 break;
0706                             case Cell::OfficeValue:
0707                                 body->addAttribute(XlsxXmlWorksheetReader::officeValue, *cell->valueAttrValue);
0708                                 break;
0709                             case Cell::OfficeStringValue:
0710                                 body->addAttribute(XlsxXmlWorksheetReader::officeStringValue, *cell->valueAttrValue);
0711                                 break;
0712                             case Cell::OfficeBooleanValue:
0713                                 // Treat boolean values specially (ODF1.1 chapter 6.7.1)
0714                                 //! @todo This breaks down if the value is a formula and not constant.
0715                                 body->addAttribute(XlsxXmlWorksheetReader::officeBooleanValue,
0716                                                 *cell->valueAttrValue == "0" ? "false" : "true");
0717                                 break;
0718                             case Cell::OfficeDateValue:
0719                                 body->addAttribute(XlsxXmlWorksheetReader::officeDateValue, *cell->valueAttrValue);
0720                                 break;
0721                         }
0722                     }
0723 
0724                     if (cell->formula) {
0725                         QString formula;
0726                         if (cell->formula->isShared()) {
0727                             Cell *referencedCell = static_cast<SharedFormula*>(cell->formula)->m_referencedCell;
0728                             Q_ASSERT(referencedCell);
0729                             formula = MSOOXML::convertFormulaReference(referencedCell, cell);
0730                         } else  {
0731                             formula = static_cast<FormulaImpl*>(cell->formula)->m_formula;
0732                         }
0733                         if (!formula.isEmpty()) {
0734                             body->addAttribute("table:formula", formula);
0735                         }
0736                     }
0737 
0738                     if (cell->rowsMerged > 1) {
0739                         body->addAttribute("table:number-rows-spanned", cell->rowsMerged);
0740                     }
0741                     if (cell->columnsMerged > 1) {
0742                         body->addAttribute("table:number-columns-spanned", cell->columnsMerged);
0743                     }
0744 
0745                     saveAnnotation(c, r);
0746 
0747                     if (!cell->text.isEmpty() || !cell->charStyleName.isEmpty() || hasHyperlink) {
0748                         body->startElement("text:p", false);
0749                         if (!cell->charStyleName.isEmpty()) {
0750                             body->startElement( "text:span" );
0751                             body->addAttribute( "text:style-name", cell->charStyleName);
0752                         }
0753                         if (hasHyperlink) {
0754                             body->startElement("text:a");
0755                             body->addAttribute("xlink:href", cell->hyperlink());
0756                             body->addAttribute("xlink:type", "simple");
0757                             //body->addAttribute("office:target-frame-name", targetFrameName);
0758                             if(cell->text.isEmpty()) {
0759                                 body->addTextNode(cell->hyperlink());
0760                             }
0761                             else {
0762                                 body->addCompleteElement(cell->text.toUtf8());
0763                             }
0764                             body->endElement(); // text:a
0765                         } else if (!cell->text.isEmpty()) {
0766                             body->addCompleteElement(cell->text.toUtf8());
0767                         }
0768                         if (!cell->charStyleName.isEmpty()) {
0769                             body->endElement(); // text:span
0770                         }
0771                         body->endElement(); // text:p
0772                     }
0773 
0774                     // handle drawing objects like e.g. charts, diagrams and pictures
0775                     if ( cell->embedded ) {
0776                         foreach(XlsxDrawingObject* drawing, cell->embedded->drawings) {
0777                             drawing->save(body);
0778                         }
0779 
0780                         typedef QPair<QString,QString> OleObject;
0781                         int listIndex = 0;
0782                         foreach( const OleObject& oleObject, cell->embedded->oleObjects ) {
0783                             const QString olePath = oleObject.first;
0784                             const QString previewPath = oleObject.second;
0785                             body->addCompleteElement(cell->embedded->oleFrameBegins.at(listIndex).toUtf8());
0786                             ++listIndex;
0787 
0788                             body->startElement("draw:object-ole");
0789                             body->addAttribute("xlink:href", olePath);
0790                             body->addAttribute("xlink:type", "simple");
0791                             body->addAttribute("xlink:show", "embed");
0792                             body->addAttribute("xlink:actuate", "onLoad");
0793                             body->endElement(); // draw:object-ole
0794 
0795                             body->startElement("draw:image");
0796                             body->addAttribute("xlink:href", previewPath);
0797                             body->addAttribute("xlink:type", "simple");
0798                             body->addAttribute("xlink:show", "embed");
0799                             body->addAttribute("xlink:actuate", "onLoad");
0800                             body->endElement(); // draw:image
0801 
0802                             body->addCompleteElement("</draw:frame>");
0803                         }
0804                     }
0805                 }
0806                 body->endElement(); // table:table-cell
0807             }
0808         }
0809 
0810         if (!row || columnCount <= 0) {
0811             // element table:table-row may not be empty
0812             body->startElement("table:table-cell");
0813             body->endElement(); // table:table-cell
0814         }
0815         body->endElement(); // table:table-row
0816     }
0817 
0818     body->endElement(); // table:table
0819 
0820     if (m_context->firstRoundOfReading) {
0821         body = oldBody;
0822     }
0823 
0824 
0825 
0826     return KoFilter::OK;
0827 }
0828 
0829 #undef CURRENT_EL
0830 #define CURRENT_EL conditionalFormatting
0831 /*
0832  Parent elements:
0833  - [done] worksheet (§18.3.1.99)
0834 
0835  Child elements:
0836  - [done] cfRule (Conditional Formatting Rule) §18.3.1.10
0837  - extLst (Future Feature Data Storage Area) §18.2.10
0838 
0839 */
0840 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_conditionalFormatting()
0841 {
0842     READ_PROLOGUE
0843 
0844     const QXmlStreamAttributes attrs(attributes());
0845     TRY_READ_ATTR_WITHOUT_NS(sqref)
0846 
0847     // Getting rid of previously handled conditions
0848     m_conditionalIndices.clear();
0849 
0850     while (!atEnd()) {
0851         readNext();
0852         BREAK_IF_END_OF(CURRENT_EL)
0853         if (isStartElement()) {
0854             TRY_READ_IF(cfRule)
0855             SKIP_UNKNOWN
0856         }
0857     }
0858 
0859     QList<QString> areas;
0860     while (sqref.indexOf(' ') > 0) {
0861         QString conditionArea = sqref.left(sqref.indexOf(' '));
0862         sqref.remove(0, conditionArea.length() + 1);
0863         areas.push_back(conditionArea);
0864     }
0865     areas.push_back(sqref);
0866 
0867     typedef QPair<int, QMap<QString, QString> > Condition;
0868 
0869     // Adding conditions to list of conditions and making sure that only the one with highest priority
0870     // remains if there are multiple conditions with same area & condition
0871     // This is done because some ooxml files have same condition for some area listed multiple times but
0872     // with different priorities
0873     int index = 0;
0874     while (index < m_conditionalIndices.size()) {
0875         QString conditionalArea;
0876         Condition examinedCondition = m_conditionalIndices.at(index);
0877         QString sqrefOriginal = sqref;
0878         int areaIndex = 0;
0879         Condition previousCond;
0880 
0881         while (areaIndex < areas.size()) {
0882             conditionalArea = areas.at(areaIndex);
0883             QList<Condition> previousConditions = m_conditionalStyles.value(conditionalArea);
0884             if (previousConditions.isEmpty()) {
0885                 previousConditions.push_back(examinedCondition);
0886                 m_conditionalStyles[conditionalArea] = previousConditions;
0887             }
0888             else {
0889                 int conditionIndex = 0;
0890                 bool hasTheSameCondition = false;
0891                 while (conditionIndex < previousConditions.size()) {
0892                     // When comparing we only care about the condition, not the style
0893                     if (previousConditions.at(conditionIndex).second.value("style:condition") ==
0894                         examinedCondition.second.value("style:condition")) {
0895                         hasTheSameCondition = true;
0896                         previousCond = previousConditions.at(conditionIndex);
0897                         if (previousCond.first > examinedCondition.first) {
0898                             previousConditions.replace(conditionIndex, examinedCondition);
0899                             m_conditionalStyles[conditionalArea] = previousConditions;
0900                         }
0901                         break;
0902                     }
0903                     ++conditionIndex;
0904                 }
0905 
0906                 if (!hasTheSameCondition) {
0907                     previousConditions.push_back(examinedCondition);
0908                     m_conditionalStyles[conditionalArea] = previousConditions;
0909                 }
0910             }
0911             ++areaIndex;
0912         }
0913         ++index;
0914     }
0915     READ_EPILOGUE
0916 }
0917 
0918 #undef CURRENT_EL
0919 #define CURRENT_EL cfRule
0920 /*
0921  Parent elements:
0922  - [done] conditionalFormatting (§18.3.1.18)
0923 
0924  Child elements:
0925  - colorScale (Color Scale) §18.3.1.16
0926  - dataBar (Data Bar) §18.3.1.28
0927  - extLst (Future Feature Data Storage Area) §18.2.10
0928  - [done] formula (Formula) §18.3.1.43
0929  - iconSet (Icon Set) §18.3.1.49
0930 
0931 */
0932 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_cfRule()
0933 {
0934     READ_PROLOGUE
0935 
0936     const QXmlStreamAttributes attrs(attributes());
0937     TRY_READ_ATTR_WITHOUT_NS(type)
0938     TRY_READ_ATTR_WITHOUT_NS(dxfId)
0939     TRY_READ_ATTR_WITHOUT_NS(priority)
0940     QString op = attrs.value("operator").toString();
0941 
0942     QList<QString> formulas;
0943 
0944     while (!atEnd()) {
0945         readNext();
0946         BREAK_IF_END_OF(CURRENT_EL)
0947         if (isStartElement()) {
0948             if (name() == "formula") {
0949                 TRY_READ(formula)
0950                 formulas.push_back(m_formula);
0951             }
0952             SKIP_UNKNOWN
0953         }
0954     }
0955 
0956     QMap<QString, QString> odf;
0957     // TODO, use attributes to really interpret this
0958     // The default one here is valid for type="cellIs" operator="equal"
0959     if (op == "equal") {
0960         odf["style:condition"] = QString("cell-content()=%1").arg(m_formula);
0961     }
0962     else if (op == "lessThan") {
0963         odf["style:condition"] = QString("cell-content()<%1").arg(m_formula);
0964     }
0965     else if (op == "greaterThan") {
0966         odf["style:condition"] = QString("cell-content()>%1").arg(m_formula);
0967     }
0968     else if (op == "between") {
0969         odf["style:condition"] = QString("cell-content-is-between(%1, %2)").arg(formulas.at(0)).arg(formulas.at(1));
0970     }
0971     odf["style:apply-style-name"] = m_context->styles->conditionalStyle(dxfId.toInt() + 1);
0972 
0973     m_conditionalIndices.push_back(QPair<int, QMap<QString, QString> >(priority.toInt(), odf));
0974 
0975     READ_EPILOGUE
0976 }
0977 
0978 #undef CURRENT_EL
0979 #define CURRENT_EL formula
0980 /*
0981  Parent elements:
0982  - [done] cfRule (§18.3.1.10)
0983  - rdn (§18.11.1.13)
0984 
0985  Child elements:
0986  - none
0987 
0988 */
0989 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_formula()
0990 {
0991     READ_PROLOGUE
0992 
0993     READ_PROLOGUE
0994     while (!atEnd()) {
0995         readNext();
0996         if (isCharacters()) {
0997             m_formula = text().toString();
0998         }
0999         BREAK_IF_END_OF(CURRENT_EL)
1000     }
1001     READ_EPILOGUE
1002 }
1003 
1004 #undef CURRENT_EL
1005 #define CURRENT_EL sheetFormatPr
1006 //! sheetFormatPr handler (Sheet Format Properties)
1007 /*! ECMA-376, 18.3.1.81, p. 1866.
1008  Sheet formatting properties.
1009 
1010  No child elements.
1011 
1012  Parent elements:
1013  - dialogsheet (§18.3.1.34)
1014  - [done] worksheet (§18.3.1.99)
1015 
1016  @todo support all attributes and elements
1017 */
1018 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_sheetFormatPr()
1019 {
1020     READ_PROLOGUE
1021     const QXmlStreamAttributes attrs(attributes());
1022     TRY_READ_ATTR_WITHOUT_NS(defaultRowHeight) // in pt
1023     TRY_READ_ATTR_WITHOUT_NS(defaultColWidth)
1024     TRY_READ_ATTR_WITHOUT_NS(baseColWidth)
1025     bool ok;
1026 
1027     const double drh = defaultRowHeight.toDouble(&ok);
1028     if (ok) {
1029         m_context->sheet->m_defaultRowHeight = drh;
1030     }
1031 
1032     const double dcw = defaultColWidth.toDouble(&ok);
1033     if (ok) {
1034         m_context->sheet->m_defaultColWidth = dcw;
1035     }
1036 
1037     const double bcw = baseColWidth.toDouble(&ok);
1038     if (ok) {
1039         m_context->sheet->m_baseColWidth = bcw;
1040     }
1041 
1042     readNext();
1043     READ_EPILOGUE
1044 }
1045 
1046 #undef CURRENT_EL
1047 #define CURRENT_EL cols
1048 //! cols handler (Column Information)
1049 /*! ECMA-376, 18.3.1.17, p. 1782.
1050  Information about whole columns of the worksheet.
1051  Child elements:
1052  - [done] col (Column Width & Formatting) §18.3.1.13
1053 
1054  Parent elements:
1055  - [done] worksheet (§18.3.1.99)
1056 */
1057 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_cols()
1058 {
1059     READ_PROLOGUE
1060     while (!atEnd()) {
1061         readNext();
1062         qCDebug(lcXlsxImport) << *this;
1063         BREAK_IF_END_OF(CURRENT_EL)
1064         if (isStartElement()) {
1065             TRY_READ_IF(col)
1066             ELSE_WRONG_FORMAT
1067         }
1068     }
1069     READ_EPILOGUE_WITHOUT_RETURN
1070 
1071     // append remaining empty columns
1072     appendTableColumns(MSOOXML::maximumSpreadsheetColumns() - m_columnCount);
1073     return KoFilter::OK;
1074 }
1075 
1076 //! Saves information about column style
1077 void XlsxXmlWorksheetReader::saveColumnStyle(const QString& widthString)
1078 {
1079     if ( !d->savedStyles.contains( widthString ) )
1080     {
1081         KoGenStyle tableColumnStyle(KoGenStyle::TableColumnAutoStyle, "table-column");
1082         tableColumnStyle.addProperty("style:column-width", widthString);
1083         tableColumnStyle.addProperty("fo:break-before", "auto");
1084 
1085         const QString currentTableColumnStyleName(mainStyles->insert(tableColumnStyle, "co"));
1086         body->addAttribute("table:style-name", currentTableColumnStyleName);
1087         d->savedStyles[widthString] = currentTableColumnStyleName;
1088     }
1089     else
1090     {
1091         const QString currentTableColumnStyleName(d->savedStyles[widthString]);
1092         body->addAttribute("table:style-name", currentTableColumnStyleName);
1093     }
1094 }
1095 
1096 void XlsxXmlWorksheetReader::appendTableColumns(int columns, const QString& width)
1097 {
1098     qCDebug(lcXlsxImport) << "columns:" << columns;
1099     if (columns <= 0)
1100         return;
1101     body->startElement("table:table-column");
1102     if (columns > 1)
1103         body->addAttribute("table:number-columns-repeated", QByteArray::number(columns));
1104 //! @todo hardcoded table:default-cell-style-name
1105     body->addAttribute("table:default-cell-style-name", "Excel_20_Built-in_20_Normal");
1106 //! @todo hardcoded default style:column-width
1107     saveColumnStyle(width.isEmpty() ? QLatin1String("1.707cm") : width);
1108     body->endElement(); // table:table-column
1109 }
1110 
1111 #undef CURRENT_EL
1112 #define CURRENT_EL col
1113 //! col handler (Column Width & Formatting)
1114 /*! ECMA-376, 18.3.1.13, p. 1777.
1115  Defines column width and column formatting for one or more columns of the worksheet.
1116  No child elements.
1117 
1118  Parent elements:
1119  - [done] cols (§18.3.1.17)
1120 
1121  @todo support more attributes
1122 */
1123 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_col()
1124 {
1125     READ_PROLOGUE
1126 
1127     const QXmlStreamAttributes attrs(attributes());
1128 
1129     Column* column = m_context->sheet->column(m_columnCount, true);
1130     ++m_columnCount;
1131 
1132 //moved    body->startElement("table:table-column"); // CASE #S2500?
1133     int minCol = m_columnCount;
1134     int maxCol = m_columnCount;
1135     QString minStr, maxStr;
1136     TRY_READ_ATTR_WITHOUT_NS_INTO(min, minStr)
1137     STRING_TO_INT(minStr, minCol, "col@min")
1138     TRY_READ_ATTR_WITHOUT_NS_INTO(max, maxStr)
1139     STRING_TO_INT(maxStr, maxCol, "col@min")
1140     if (minCol > maxCol)
1141         qSwap(minCol, maxCol);
1142 
1143     if (m_columnCount < minCol) {
1144         appendTableColumns(minCol - m_columnCount);
1145         m_columnCount = minCol;
1146     }
1147 
1148     TRY_READ_ATTR_WITHOUT_NS(width)
1149     QString realWidthString;
1150     if (!width.isEmpty()) {
1151         bool ok;
1152         double widthNumber = width.toDouble(&ok);
1153         if (!ok)
1154             return KoFilter::WrongFormat;
1155 
1156         realWidthString = computeColumnWidth(widthNumber);
1157         qCDebug(lcXlsxImport) << "realWidthString:" << realWidthString;
1158 //moved        saveColumnStyle(realWidthString);
1159 //! @todo hardcoded table:default-cell-style-name
1160 //moved        body->addAttribute("table:default-cell-style-name", "Excel_20_Built-in_20_Normal");
1161     }
1162     // we apparently don't need "customWidth" attr
1163 
1164     TRY_READ_ATTR_WITHOUT_NS(hidden)
1165     if (!hidden.isEmpty()) {
1166         column->hidden = hidden.toInt() > 0;
1167     }
1168 
1169 //moved    body->endElement(); // table:table-column
1170     appendTableColumns(maxCol - minCol + 1, realWidthString);
1171     if (d->savedStyles.contains(realWidthString)) {
1172         column->styleName = d->savedStyles.value(realWidthString);
1173     }
1174 
1175     m_columnCount += (maxCol - minCol);
1176 
1177     if (m_columnCount > (int)MSOOXML::maximumSpreadsheetColumns()) {
1178         showWarningAboutWorksheetSize();
1179     }
1180 
1181     readNext();
1182     READ_EPILOGUE
1183 }
1184 
1185 #undef CURRENT_EL
1186 #define CURRENT_EL sheetData
1187 //! sheetData handler (Sheet Data)
1188 /*! ECMA-376, 18.3.1.80, p. 1866.
1189  This collection represents the cell table itself. This collection expresses information
1190  about each cell, grouped together by rows in the worksheet.
1191 
1192  Child elements:
1193  - [done] row (Row) §18.3.1.73
1194 
1195  Parent elements:
1196  - [done] worksheet (§18.3.1.99)
1197 */
1198 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_sheetData()
1199 {
1200     READ_PROLOGUE
1201     m_currentRow = 0;
1202     while (!atEnd()) {
1203         readNext();
1204         qCDebug(lcXlsxImport) << *this;
1205         BREAK_IF_END_OF(CURRENT_EL)
1206         if (isStartElement()) {
1207             TRY_READ_IF(row)
1208             ELSE_WRONG_FORMAT
1209         }
1210     }
1211     READ_EPILOGUE
1212 }
1213 
1214 QString XlsxXmlWorksheetReader::processRowStyle(qreal height)
1215 {
1216     if (height == -1.0) {
1217         height = m_context->sheet->m_defaultRowHeight;
1218     }
1219     KoGenStyle tableRowStyle(KoGenStyle::TableRowAutoStyle, "table-row");
1220 //! @todo alter fo:break-before?
1221     tableRowStyle.addProperty("fo:break-before", MsooXmlReader::constAuto);
1222 //! @todo alter style:use-optimal-row-height?
1223     tableRowStyle.addProperty("style:use-optimal-row-height", MsooXmlReader::constFalse);
1224     if (height >= 0.0) {
1225         tableRowStyle.addProperty("style:row-height", printCm(POINT_TO_CM(height)));
1226     }
1227     const QString currentTableRowStyleName(mainStyles->insert(tableRowStyle, "ro"));
1228     return currentTableRowStyleName;
1229 }
1230 
1231 void XlsxXmlWorksheetReader::appendTableCells(int cells)
1232 {
1233     if (cells <= 0)
1234         return;
1235     body->startElement("table:table-cell");
1236     if (cells > 1)
1237         body->addAttribute("table:number-columns-repeated", QByteArray::number(cells));
1238     body->endElement(); // table:table-cell
1239 }
1240 
1241 #undef CURRENT_EL
1242 #define CURRENT_EL row
1243 //! row handler (Row)
1244 /*! ECMA-376, 18.3.1.73, p. 1855.
1245  The element expresses information about an entire row of a worksheet,
1246  and contains all cell definitions for a particular row in the worksheet.
1247 
1248  Child elements:
1249  - [done] c (Cell) §18.3.1.4
1250  - extLst (Future Feature Data Storage Area) §18.2.10
1251 
1252  Parent elements:
1253  - [done] sheetData (§18.3.1.80)
1254 
1255  @todo support all child elements
1256 */
1257 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_row()
1258 {
1259     READ_PROLOGUE
1260     const QXmlStreamAttributes attrs(attributes());
1261     TRY_READ_ATTR_WITHOUT_NS(r)
1262     //TRY_READ_ATTR_WITHOUT_NS(spans) // spans are only an optional help
1263     TRY_READ_ATTR_WITHOUT_NS(ht)
1264     //TRY_READ_ATTR_WITHOUT_NS(customHeight) not used atm
1265     TRY_READ_ATTR_WITHOUT_NS(hidden)
1266 
1267     if (!r.isEmpty()) {
1268         bool ok;
1269         m_currentRow = r.toInt(&ok) - 1;
1270         if (!ok || m_currentRow < 0)
1271             return KoFilter::WrongFormat;
1272     }
1273     if (m_currentRow > (int)MSOOXML::maximumSpreadsheetRows()) {
1274         showWarningAboutWorksheetSize();
1275     }
1276 
1277     m_currentColumn = 0;
1278     Row* row = m_context->sheet->row(m_currentRow, true);
1279     if (!ht.isEmpty()) {
1280         bool ok;
1281         qreal height = ht.toDouble(&ok);
1282         if (ok) {
1283             row->styleName = processRowStyle(height);
1284         }
1285     }
1286 
1287     if (!hidden.isEmpty()) {
1288         row->hidden = hidden.toInt() > 0;
1289     }
1290 
1291     qreal range = (55.0/m_context->numberOfWorkSheets);
1292     int counter = 0;
1293     while (!atEnd()) {
1294         readNext();
1295         qCDebug(lcXlsxImport) << *this;
1296         BREAK_IF_END_OF(CURRENT_EL)
1297         if (isStartElement()) {
1298             if (counter == 40) {
1299                 // set the progress by the position of what was read
1300                 qreal progress = 45 + range * (m_context->worksheetNumber - 1)
1301                                + range * device()->pos() / device()->size();
1302                 m_context->import->reportProgress(progress);
1303                 counter = 0;
1304             }
1305             ++counter;
1306             TRY_READ_IF(c) // modifies m_currentColumn
1307             SKIP_UNKNOWN
1308         }
1309     }
1310 
1311     ++m_currentRow; // This row is done now. Select the next row.
1312 
1313     READ_EPILOGUE
1314 }
1315 
1316 //! @return true if @a v represents an integer or floating-point number
1317 static bool valueIsNumeric(const QString& v)
1318 {
1319     bool ok;
1320     v.toDouble(&ok);
1321     return ok;
1322 }
1323 
1324 #undef CURRENT_EL
1325 #define CURRENT_EL c
1326 //! c handler (Cell)
1327 /*! ECMA-376, 18.3.1.4, p. 1767.
1328  This collection represents a cell in the worksheet.
1329  Information about the cell's location (reference), value, data
1330  type, formatting, and formula is expressed here.
1331 
1332  Child elements:
1333  - extLst (Future Feature Data Storage Area) §18.2.10
1334  - [done] f (Formula) §18.3.1.40
1335  - is (Rich Text Inline) §18.3.1.53
1336  - [done] v (Cell Value) §18.3.1.96
1337 
1338  Parent elements:
1339  - [done] row (§18.3.1.73)
1340 
1341  @todo support all child elements
1342 */
1343 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_c()
1344 {
1345     Row* row = m_context->sheet->row(m_currentRow, false);
1346     Q_ASSERT(row);
1347     Q_UNUSED(row);
1348 
1349     READ_PROLOGUE
1350     const QXmlStreamAttributes attrs(attributes());
1351     TRY_READ_ATTR_WITHOUT_NS(r)
1352     if (!r.isEmpty()) {
1353         m_currentColumn = Calligra::Sheets::Util::decodeColumnLabelText(r) - 1;
1354         if (m_currentColumn < 0)
1355             return KoFilter::WrongFormat;
1356     }
1357 
1358     TRY_READ_ATTR_WITHOUT_NS(s)
1359     TRY_READ_ATTR_WITHOUT_NS(t)
1360 
1361     m_value.clear();
1362 
1363     Cell* cell = m_context->sheet->cell(m_currentColumn, m_currentRow, true);
1364 
1365     while (!atEnd()) {
1366         readNext();
1367         qCDebug(lcXlsxImport) << *this;
1368         BREAK_IF_END_OF(CURRENT_EL)
1369         if (isStartElement()) {
1370             TRY_READ_IF(f)
1371             ELSE_TRY_READ_IF(v)
1372             SKIP_UNKNOWN
1373         }
1374     }
1375 
1376     bool ok;
1377     uint styleId = s.toUInt(&ok);
1378     const XlsxCellFormat* cellFormat = m_context->styles->cellFormat(styleId);
1379 
1380     QString formattedStyle;
1381     if (cellFormat->applyNumberFormat)
1382         formattedStyle = m_context->styles->numberFormatStyleName( cellFormat->numFmtId );
1383 
1384     //qCDebug(lcXlsxImport) << "type=" << t << "styleId=" << styleId << "applyNumberFormat=" << cellFormat->applyNumberFormat << "numberFormat=" << numberFormat << "value=" << m_value;
1385 
1386     QString charStyleName;
1387 
1388 //    const bool addTextPElement = true;//m_value.isEmpty() || t != QLatin1String("s");
1389 
1390     if (!m_value.isEmpty()) {
1391         /* depending on type: 18.18.11 ST_CellType (Cell Type), p. 2679:
1392             b (Boolean)  Cell containing a boolean.
1393             d (Date)     Cell contains a date in the ISO 8601 format.
1394             e (Error)    Cell containing an error.
1395             inlineStr    (Inline String) Cell containing an (inline) rich string, i.e.
1396                          one not in the shared string table. If this cell type is used,
1397                          then the cell value is in the is element rather than the v
1398                          element in the cell (c element).
1399             n (Number)   Cell containing a number.
1400             s (Shared String) Cell containing a shared string.
1401             str (String) Cell containing a formula string.
1402 
1403             Converting into values described in ODF1.1: "6.7.1. Variable Value Types and Values".
1404         */
1405 
1406         if (t == QLatin1String("s")) {
1407             bool ok;
1408             const int stringIndex = m_value.toInt(&ok);
1409             if (!ok || stringIndex < 0 || stringIndex >= m_context->sharedStrings->size()) {
1410                 return KoFilter::WrongFormat;
1411             }
1412             QString sharedstring = m_context->sharedStrings->at(stringIndex);
1413             cell->text = sharedstring;
1414             cell->valueType = Cell::ConstString;
1415             m_value = sharedstring;
1416             // no valueAttr
1417         } else if ((t.isEmpty() && !valueIsNumeric(m_value)) || t == QLatin1String("inlineStr")) {
1418 //! @todo handle value properly
1419             cell->text = m_value;
1420             cell->valueType = Cell::ConstString;
1421             // no valueAttr
1422         } else if (t == QLatin1String("b")) {
1423             cell->text = m_value;
1424             cell->valueType = Cell::ConstBoolean;
1425             cell->valueAttr = Cell::OfficeBooleanValue;
1426         } else if (t == QLatin1String("d")) {
1427 //! @todo handle value properly
1428             cell->text = m_value;
1429             cell->valueType = Cell::ConstDate;
1430             cell->valueAttr = Cell::OfficeDateValue;
1431         } else if (t == QLatin1String("str")) {
1432 //! @todo handle value properly
1433             cell->text = m_value;
1434             cell->valueType = Cell::ConstString;
1435             // no valueAttr
1436         } else if (t == QLatin1String("n") || t.isEmpty() /* already checked if numeric */) {
1437             if (!t.isEmpty()) { // sanity check
1438                 if (!valueIsNumeric(m_value)) {
1439                     raiseError(i18n("Expected integer or floating point number"));
1440                     return KoFilter::WrongFormat;
1441                 }
1442             }
1443             const KoGenStyle* const style = mainStyles->style( formattedStyle, "" );
1444             if( style == 0 || valueIsNumeric(m_value) ) {
1445 //            body->addTextSpan(m_value);
1446                 cell->valueType = Cell::ConstFloat;
1447                 cell->valueAttr = Cell::OfficeValue;
1448             } else {
1449                 // Tests showed that this code is never executed even when a style was set.
1450                 switch( style->type() ) {
1451                 case KoGenStyle::NumericDateStyle:
1452                     cell->valueType = Cell::ConstDate;
1453                     cell->valueAttr = Cell::OfficeDateValue;
1454                     m_value = QDate( 1899, 12, 30 ).addDays( m_value.toInt() ).toString( Qt::ISODate );
1455                     break;
1456                 case KoGenStyle::NumericTextStyle:
1457                     cell->valueType = Cell::ConstString;
1458                     cell->valueAttr = Cell::OfficeStringValue;
1459                     break;
1460                 default:
1461                     cell->valueType = Cell::ConstFloat;
1462                     cell->valueAttr = Cell::OfficeValue;
1463                     break;
1464                 }
1465             }
1466         } else if (t == QLatin1String("e")) {
1467             if (m_value == QLatin1String("#REF!"))
1468                 cell->text = "#NAME?";
1469             else
1470                 cell->text = m_value;
1471 //! @todo full parsing needed to retrieve the type
1472             cell->valueType = Cell::ConstFloat;
1473             cell->valueAttr = Cell::OfficeValue;
1474             m_value = QLatin1String("0");
1475         } else {
1476             raiseUnexpectedAttributeValueError(t, "c@t");
1477             return KoFilter::WrongFormat;
1478         }
1479     }
1480 
1481     // cell style
1482     if (!s.isEmpty()) {
1483         if (!ok || !cellFormat) {
1484             raiseUnexpectedAttributeValueError(s, "c@s");
1485             return KoFilter::WrongFormat;
1486         }
1487         KoGenStyle cellStyle(KoGenStyle::TableCellAutoStyle, "table-cell");
1488 
1489         if (charStyleName.isEmpty()) {
1490             KoGenStyle* fontStyle = m_context->styles->fontStyle(cellFormat->fontId);
1491             if (!fontStyle) {
1492                 qCWarning(lcXlsxImport) << "No font with ID:" << cellFormat->fontId;
1493             } else {
1494                 KoGenStyle::copyPropertiesFromStyle(*fontStyle, cellStyle, KoGenStyle::TextType);
1495             }
1496         }
1497         if (!cellFormat->setupCellStyle(m_context->styles, &cellStyle)) {
1498             return KoFilter::WrongFormat;
1499         }
1500 
1501         if (!formattedStyle.isEmpty()) {
1502             cellStyle.addAttribute( "style:data-style-name", formattedStyle );
1503         }
1504 
1505         if (!m_context->conditionalStyles.isEmpty()) {
1506             QString positionLetter;
1507             int positionNumber;
1508             splitToRowAndColumn(r.toLatin1().constData(), 0, r.size(), positionLetter, positionNumber);
1509             QList<QMap<QString, QString> > maps = m_context->conditionalStyleForPosition(positionLetter, positionNumber);
1510             int index = maps.size();
1511             // Adding the lists in reversed priority order, as KoGenStyle when creating the style
1512             // adds last added first
1513             while (index > 0) {
1514                 cellStyle.addStyleMap(maps.at(index - 1));
1515                 --index;
1516             }
1517         }
1518 
1519         const QString cellStyleName = mainStyles->insert( cellStyle, "ce" );
1520         cell->styleName = cellStyleName;
1521     }
1522 
1523     delete cell->valueAttrValue;
1524     if (m_value.isEmpty()) {
1525         cell->valueAttrValue = 0;
1526     } else {
1527         cell->valueAttrValue = new QString(m_value);
1528     }
1529 
1530     ++m_currentColumn; // This cell is done now. Select the next cell.
1531 
1532     READ_EPILOGUE
1533 }
1534 
1535 #undef CURRENT_EL
1536 #define CURRENT_EL f
1537 
1538 //! f handler (Formula)
1539 /*! ECMA-376, 18.3.1.40, p. 1813.
1540  Formula for the cell. The formula expression is contained in the character node of this element.
1541 
1542  No child elements.
1543 
1544  Parent elements:
1545  - [done] c (§18.3.1.4)
1546  - nc (§18.11.1.3)
1547  - oc (§18.11.1.5)
1548 
1549  @todo support all elements
1550 */
1551 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_f()
1552 {
1553     Cell* cell = m_context->sheet->cell(m_currentColumn, m_currentRow, false);
1554     Q_ASSERT(cell);
1555 
1556     READ_PROLOGUE
1557     const QXmlStreamAttributes attrs(attributes());
1558 
1559     // Range of cells which the formula applies to. Only required for shared formula, array
1560     // formula or data table. Only written on the master formula, not subsequent formula's
1561     // belonging to the same shared group, array, or data table.
1562     //TRY_READ_ATTR(ref)
1563     // Type of formula. The possible values defined by the ST_CellFormulaType (§18.18.6), p. 2677
1564     TRY_READ_ATTR(t)
1565 
1566     // Shared formula groups.
1567     int sharedGroupIndex = -1;
1568     if (t == QLatin1String("shared")) {
1569         TRY_READ_ATTR(si)
1570         STRING_TO_INT(si, sharedGroupIndex, "f@si")
1571     }
1572 
1573     while (!atEnd() && !hasError()) {
1574         readNext();
1575         BREAK_IF_END_OF(CURRENT_EL)
1576         if (isCharacters()) {
1577             delete cell->formula;
1578             cell->formula = new FormulaImpl(Calligra::Sheets::MSOOXML::convertFormula(text().toString()));
1579         }
1580     }
1581 
1582     if (!t.isEmpty()) {
1583         if (t == QLatin1String("shared")) {
1584             if (sharedGroupIndex >= 0) {
1585                 /* Shared Group Index, p. 1815
1586                 Optional attribute to optimize load performance by sharing formulas.
1587                 When a formula is a shared formula (t value is shared) then this value indicates the
1588                 group to which this particular cell's formula belongs. The first formula in a group of
1589                 shared formulas is saved in the f element. This is considered the 'master' formula cell.
1590                 Subsequent cells sharing this formula need not have the formula written in their f
1591                 element. Instead, the attribute si value for a particular cell is used to figure what the
1592                 formula expression should be based on the cell's relative location to the master formula
1593                 cell.
1594                 */
1595                 if (d->sharedFormulas.contains(sharedGroupIndex)) {
1596                     if (!cell->formula /* || cell->formula->isEmpty() */) { // don't do anything if the cell already defines a formula
1597                         QHash<int, Cell*>::iterator it = d->sharedFormulas.find(sharedGroupIndex);
1598                         if (it != d->sharedFormulas.end()) {
1599                             delete cell->formula;
1600                             cell->formula = new SharedFormula(it.value());
1601                         }
1602                     }
1603                 } else if (cell->formula /* && !cell->formula->isEmpty()*/) { // is this cell the master cell?
1604                     d->sharedFormulas[sharedGroupIndex] = cell;
1605                 }
1606             }
1607         }
1608     }
1609 
1610     /*
1611     if (!ref.isEmpty()) {
1612         const int pos = ref.indexOf(':');
1613         if (pos > 0) {
1614             const QString fromCell = ref.left(pos);
1615             const QString toCell = ref.mid(pos + 1);
1616             const int c1 = Calligra::Sheets::Util::decodeColumnLabelText(fromCell) - 1;
1617             const int r1 = Calligra::Sheets::Util::decodeRowLabelText(fromCell) - 1;
1618             const int c2 = Calligra::Sheets::Util::decodeColumnLabelText(toCell) - 1;
1619             const int r2 = Calligra::Sheets::Util::decodeRowLabelText(toCell) - 1;
1620             if (c1 >= 0 && r1 >= 0 && c2 >= c1 && r2 >= r1) {
1621                 for (int col = c1; col <= c2; ++col) {
1622                     for (int row = r1; row <= r2; ++row) {
1623                         if (col != m_currentColumn || row != m_currentRow) {
1624                             if (Cell* c = m_context->sheet->cell(col, row, true))
1625                                 c->formula = convertFormulaReference(cell, c);
1626                         }
1627                     }
1628                 }
1629             }
1630         }
1631     }
1632     */
1633 
1634     READ_EPILOGUE
1635 }
1636 
1637 #undef CURRENT_EL
1638 #define CURRENT_EL v
1639 //! v handler (Cell Value)
1640 /*! ECMA-376, 18.3.1.96, p. 1891.
1641  This element expresses the value contained in a cell.
1642 
1643  No child elements.
1644  Parent elements:
1645  - [done] c (§18.3.1.4)
1646  - cell (§18.14.1)
1647  - nc (§18.11.1.3)
1648  - oc (§18.11.1.5)
1649  - tp (§18.15.3)
1650 
1651  @todo support all parent elements
1652 */
1653 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_v()
1654 {
1655     READ_PROLOGUE
1656     readNext();
1657 
1658     // It is possible to have empty <v/> element
1659     if (name() == "v" && isEndElement()) {
1660         READ_EPILOGUE
1661     }
1662 
1663     m_value = text().toString();
1664     m_value.replace('&', "&amp;");
1665     m_value.replace('<', "&lt;");
1666     m_value.replace('>', "&gt;");
1667     m_value.replace('\\', "&apos;");
1668     m_value.replace('"', "&quot;");
1669 
1670     readNext();
1671     READ_EPILOGUE
1672 }
1673 
1674 #undef CURRENT_EL
1675 #define CURRENT_EL mergeCell
1676 /*
1677  Parent elements:
1678  - [done] mergeCells (§18.3.1.55)
1679 
1680  Child elements:
1681  - none
1682 */
1683 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_mergeCell()
1684 {
1685     READ_PROLOGUE
1686     const QXmlStreamAttributes attrs(attributes());
1687     TRY_READ_ATTR_WITHOUT_NS(ref)
1688     QStringList refList = ref.split(':');
1689     if (refList.count() >= 2) {
1690         const QString fromCell = refList[0];
1691         const QString toCell = refList[1];
1692         QRegExp rx("([A-Za-z]+)([0-9]+)");
1693         if(rx.exactMatch(fromCell)) {
1694             const int fromRow = rx.cap(2).toInt() - 1;
1695             const int fromCol = Calligra::Sheets::Util::decodeColumnLabelText(fromCell) - 1;
1696             if(rx.exactMatch(toCell)) {
1697                 Cell* cell = m_context->sheet->cell(fromCol, fromRow, true);
1698                 cell->rowsMerged = rx.cap(2).toInt() - fromRow;
1699                 cell->columnsMerged = Calligra::Sheets::Util::decodeColumnLabelText(toCell) - fromCol;
1700 
1701                 // correctly take right/bottom borders from the cells that are merged into this one
1702                 const KoGenStyle* origCellStyle = mainStyles->style(cell->styleName, "table-cell");
1703                 KoGenStyle cellStyle;
1704                 if (origCellStyle) {
1705                     cellStyle = *origCellStyle;
1706                 }
1707                 qCDebug(lcXlsxImport) << cell->rowsMerged << cell->columnsMerged << cell->styleName;
1708                 if (cell->rowsMerged > 1) {
1709                     Cell* lastCell = m_context->sheet->cell(fromCol, fromRow + cell->rowsMerged - 1, false);
1710                     qCDebug(lcXlsxImport) << lastCell;
1711                     if (lastCell) {
1712                         const KoGenStyle* style = mainStyles->style(lastCell->styleName, "table-cell");
1713                         qCDebug(lcXlsxImport) << lastCell->styleName;
1714                         if (style) {
1715                             QString val = style->property("fo:border-bottom");
1716                             qCDebug(lcXlsxImport) << val;
1717                             if (!val.isEmpty()) cellStyle.addProperty("fo:border-bottom", val);
1718                             val = style->property("fo:border-line-width-bottom");
1719                             if (!val.isEmpty()) cellStyle.addProperty("fo:border-line-width-bottom", val);
1720                         }
1721                     }
1722                 }
1723                 if (cell->columnsMerged > 1) {
1724                     Cell* lastCell = m_context->sheet->cell(fromCol + cell->columnsMerged - 1, fromRow, false);
1725                     if (lastCell) {
1726                         const KoGenStyle* style = mainStyles->style(lastCell->styleName, "table-cell");
1727                         if (style) {
1728                             QString val = style->property("fo:border-right");
1729                             if (!val.isEmpty()) cellStyle.addProperty("fo:border-right", val);
1730                             val = style->property("fo:border-line-width-right");
1731                             if (!val.isEmpty()) cellStyle.addProperty("fo:border-line-width-right", val);
1732                         }
1733                     }
1734                 }
1735                 cell->styleName = mainStyles->insert(cellStyle, "ce");
1736             }
1737         }
1738     }
1739 
1740     readNext();
1741     READ_EPILOGUE
1742 }
1743 
1744 #undef CURRENT_EL
1745 #define CURRENT_EL mergeCells
1746 /*
1747  Parent elements:
1748  - [done] worksheet (§18.3.1.99)
1749 
1750  Child elements:
1751  - mergeCell (Merged Cell) §18.3.1.54
1752 */
1753 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_mergeCells()
1754 {
1755     READ_PROLOGUE
1756     while (!atEnd()) {
1757         readNext();
1758         BREAK_IF_END_OF(CURRENT_EL)
1759         if (isStartElement()) {
1760             TRY_READ_IF(mergeCell)
1761             ELSE_WRONG_FORMAT
1762         }
1763     }
1764     READ_EPILOGUE
1765 }
1766 
1767 #undef CURRENT_EL
1768 #define CURRENT_EL drawing
1769 
1770 //! drawing handler (Drawing)
1771 /*! ECMA-376, 18.3.1.36, p.1804.
1772 
1773  This element indicates that the sheet contains drawing components built
1774  on the drawingML platform. The relationship Id references the part containing
1775  the drawingML definitions.
1776 
1777  Parent elements:
1778  - chartsheet (§18.3.1.12)
1779  - dialogsheet (§18.3.1.34)
1780  - [done] worksheet (§18.3.1.99)
1781 
1782  Child elements - see DrawingML.
1783 */
1784 KoFilter::ConversionStatus MSOOXML_CURRENT_CLASS::read_drawing()
1785 {
1786     READ_PROLOGUE
1787     const QXmlStreamAttributes attrs(attributes());
1788     TRY_READ_ATTR_WITH_NS(r, id)
1789     if(!r_id.isEmpty() && !this->m_context->path.isEmpty()) {
1790         QString drawingPathAndFile = m_context->relationships->target(m_context->path, m_context->file, r_id);
1791         QString drawingPath, drawingFile;
1792         MSOOXML::Utils::splitPathAndFile(drawingPathAndFile, &drawingPath, &drawingFile);
1793 
1794         XlsxXmlDrawingReaderContext context(m_context, m_context->sheet, drawingPath, drawingFile);
1795         XlsxXmlDrawingReader reader(this);
1796         const KoFilter::ConversionStatus result = m_context->import->loadAndParseDocument(&reader, drawingPathAndFile, &context);
1797         if (result != KoFilter::OK) {
1798             raiseError(reader.errorString());
1799             return result;
1800         }
1801 
1802 #if 0 //TODO
1803         if (context->m_positions.contains(XlsxDrawingObject::FromAnchor)) {
1804             XlsxDrawingObject::Position pos = context->m_positions[XlsxDrawingObject::FromAnchor];
1805             Cell* cell = m_context->sheet->cell(pos.m_col, pos.m_row, true);
1806             cell->drawings << context;
1807         } else {
1808             delete context;
1809         }
1810 #endif
1811     }
1812     while (!atEnd()) {
1813         readNext();
1814         BREAK_IF_END_OF(CURRENT_EL)
1815     }
1816     READ_EPILOGUE
1817 }
1818 
1819 #undef CURRENT_EL
1820 #define CURRENT_EL hyperlink
1821 /*
1822  Parent elements:
1823  - [done] hyperlinks (§18.3.1.48)
1824 
1825  Child elements:
1826  - none
1827 */
1828 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_hyperlink()
1829 {
1830     READ_PROLOGUE
1831     const QXmlStreamAttributes attrs(attributes());
1832     TRY_READ_ATTR_WITHOUT_NS(ref)
1833     TRY_READ_ATTR_WITHOUT_NS(location)
1834     TRY_READ_ATTR_WITH_NS(r, id)
1835     if (!ref.isEmpty() && (!r_id.isEmpty() || !location.isEmpty())) {
1836         const int col = Calligra::Sheets::Util::decodeColumnLabelText(ref) - 1;
1837         const int row = Calligra::Sheets::Util::decodeRowLabelText(ref) - 1;
1838         if(col >= 0 && row >= 0) {
1839             QString link = m_context->relationships->target(m_context->path, m_context->file, r_id);
1840             // it follows a hack to get right of the prepended m_context->path...
1841             if (link.startsWith(m_context->path))
1842                 link.remove(0, m_context->path.length()+1);
1843 
1844             // append location
1845             if (!location.isEmpty()) link += '#' + location;
1846 
1847             Cell* cell = m_context->sheet->cell(col, row, true);
1848             cell->setHyperLink( link );
1849         }
1850     }
1851 
1852     readNext();
1853     READ_EPILOGUE
1854 }
1855 
1856 #undef CURRENT_EL
1857 #define CURRENT_EL hyperlinks
1858 /*
1859  Parent elements:
1860  - [done] worksheet (§18.3.1.99)
1861 
1862  Child elements:
1863  - [done] hyperlink (Hyperlink) §18.3.1.47
1864 */
1865 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_hyperlinks()
1866 {
1867     READ_PROLOGUE
1868     while (!atEnd()) {
1869         readNext();
1870         BREAK_IF_END_OF(CURRENT_EL)
1871         if (isStartElement()) {
1872             TRY_READ_IF(hyperlink)
1873             ELSE_WRONG_FORMAT
1874         }
1875     }
1876     READ_EPILOGUE
1877 }
1878 
1879 #undef CURRENT_EL
1880 #define CURRENT_EL customFilters
1881 /*
1882  Parent elements:
1883  - [done] filterColumn (§18.3.2.7)
1884 
1885  Child elements:
1886  - [done] customFilter (Custom Filter Criteria) §18.3.2.2
1887 */
1888 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_customFilters()
1889 {
1890     READ_PROLOGUE
1891 
1892     const QXmlStreamAttributes attrs(attributes());
1893     QString andValue = attrs.value("and").toString();
1894 
1895     while (!atEnd()) {
1896         readNext();
1897         BREAK_IF_END_OF(CURRENT_EL)
1898         if (isStartElement()) {
1899             TRY_READ_IF(customFilter)
1900             ELSE_WRONG_FORMAT
1901         }
1902     }
1903 
1904     if (!m_context->autoFilters.isEmpty()) {
1905         if (andValue == "1") {
1906             m_context->autoFilters.last().type = "and";
1907         } else {
1908             m_context->autoFilters.last().type = "or";
1909         }
1910     }
1911 
1912     READ_EPILOGUE
1913 }
1914 
1915 #undef CURRENT_EL
1916 #define CURRENT_EL filters
1917 /*
1918  Parent elements:
1919  - [done] filterColumn (§18.3.2.7)
1920 
1921  Child elements:
1922  - dateGroupItem (Date Grouping) §18.3.2.4
1923  - [done] filter (Filter) §18.3.2.6
1924 */
1925 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_filters()
1926 {
1927     READ_PROLOGUE
1928 
1929     const QXmlStreamAttributes attrs(attributes());
1930     TRY_READ_ATTR_WITHOUT_NS(blank)
1931 
1932     m_context->currentFilterCondition.value = "^(";
1933 
1934     bool hasValueAlready = false;
1935 
1936     while (!atEnd()) {
1937         readNext();
1938         BREAK_IF_END_OF(CURRENT_EL)
1939         if (isStartElement()) {
1940             if (name() == "filter") {
1941                 if (hasValueAlready) {
1942                     m_context->currentFilterCondition.value += "|";
1943                 }
1944                 hasValueAlready = true;
1945                 TRY_READ(filter)
1946             }
1947             SKIP_UNKNOWN
1948         }
1949     }
1950 
1951     m_context->currentFilterCondition.value += ")$";
1952     m_context->currentFilterCondition.opField = "match";
1953 
1954     if (blank == "1") {
1955         m_context->currentFilterCondition.value = "0";
1956         m_context->currentFilterCondition.opField = "empty";
1957     }
1958 
1959     if (!m_context->autoFilters.isEmpty()) {
1960         m_context->autoFilters.last().filterConditions.push_back(m_context->currentFilterCondition);
1961     }
1962 
1963     READ_EPILOGUE
1964 }
1965 
1966 #undef CURRENT_EL
1967 #define CURRENT_EL customFilter
1968 /*
1969  Parent elements:
1970  - [done] customFilters (§18.3.2.2)
1971 
1972  Child elements:
1973  - none
1974 */
1975 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_customFilter()
1976 {
1977     READ_PROLOGUE
1978 
1979     const QXmlStreamAttributes attrs(attributes());
1980     QString opValue = attrs.value("operator").toString();
1981 
1982     TRY_READ_ATTR_WITHOUT_NS(val)
1983     m_context->currentFilterCondition.value = val;
1984 
1985     if (opValue == "notEqual") {
1986         m_context->currentFilterCondition.opField = "!=";
1987     }
1988     else {
1989         m_context->currentFilterCondition.opField = "=";
1990     }
1991 
1992     if (!m_context->autoFilters.isEmpty()) {
1993         m_context->autoFilters.last().filterConditions.push_back(m_context->currentFilterCondition);
1994     }
1995 
1996     readNext();
1997 
1998     READ_EPILOGUE
1999 }
2000 
2001 #undef CURRENT_EL
2002 #define CURRENT_EL filter
2003 /*
2004  Parent elements:
2005  - [done] filters (§18.3.2.8)
2006 
2007  Child elements:
2008  - none
2009 */
2010 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_filter()
2011 {
2012     READ_PROLOGUE
2013 
2014     const QXmlStreamAttributes attrs(attributes());
2015     TRY_READ_ATTR_WITHOUT_NS(val)
2016 
2017     m_context->currentFilterCondition.value += val;
2018 
2019     readNext();
2020 
2021     READ_EPILOGUE
2022 }
2023 
2024 #undef CURRENT_EL
2025 #define CURRENT_EL filterColumn
2026 /*
2027  Parent elements:
2028  - [done] autoFilter (§18.3.1.2)
2029 
2030  Child elements:
2031  - colorFilter (Color Filter Criteria) §18.3.2.1
2032  - [done] customFilters (Custom Filters) §18.3.2.3
2033  - dynamicFilter (Dynamic Filter) §18.3.2.5
2034  - extLst (Future Feature Data Storage Area) §18.2.10
2035  - [done] filters (Filter Criteria) §18.3.2.8
2036  - iconFilter (Icon Filter) §18.3.2.9
2037  - top10 (Top 10) §18.3.2.10
2038 */
2039 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_filterColumn()
2040 {
2041     READ_PROLOGUE
2042 
2043     const QXmlStreamAttributes attrs(attributes());
2044     TRY_READ_ATTR_WITHOUT_NS(colId)
2045 
2046     m_context->currentFilterCondition.field = colId;
2047 
2048     while (!atEnd()) {
2049         readNext();
2050         BREAK_IF_END_OF(CURRENT_EL)
2051         if (isStartElement()) {
2052             TRY_READ_IF(filters)
2053             ELSE_TRY_READ_IF(customFilters)
2054             SKIP_UNKNOWN
2055         }
2056     }
2057 
2058     READ_EPILOGUE
2059 }
2060 
2061 #undef CURRENT_EL
2062 #define CURRENT_EL autoFilter
2063 /*
2064  Parent elements:
2065  - customSheetView (§18.3.1.25)
2066  - filter (§18.10.1.33)
2067  - table (§18.5.1.2)
2068  - [done] worksheet (§18.3.1.99)
2069 
2070  Child elements:
2071  - extLst (Future Feature Data Storage Area) §18.2.10
2072  - [done] filterColumn (AutoFilter Column) §18.3.2.7
2073  - sortState (Sort State) §18.3.1.92
2074 */
2075 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_autoFilter()
2076 {
2077     READ_PROLOGUE
2078     const QXmlStreamAttributes attrs(attributes());
2079     TRY_READ_ATTR_WITHOUT_NS(ref)
2080 
2081     // take last numbers and replace it with max row
2082     ref.replace(QRegExp("[0-9]+$"), QString::number(m_context->sheet->maxRow()+1));
2083 
2084     ref.prepend(".");
2085     QString sheetName = m_context->worksheetName;
2086     if (sheetName.contains('.') || sheetName.contains(' ') || sheetName.contains('\'')) {
2087         sheetName = '\'' + sheetName.replace('\'', "''") + '\'';
2088     }
2089     ref.prepend(sheetName);
2090 
2091     int colon = ref.indexOf(':');
2092     if (colon > 0) {
2093         ref.insert(colon + 1, '.');
2094         ref.insert(colon + 1, sheetName);
2095     }
2096 
2097     XlsxXmlDocumentReaderContext::AutoFilter autoFilter;
2098     autoFilter.area = ref;
2099     m_context->autoFilters.push_back(autoFilter);
2100 
2101     while (!atEnd()) {
2102         readNext();
2103         BREAK_IF_END_OF(CURRENT_EL)
2104         if (isStartElement()) {
2105             TRY_READ_IF(filterColumn)
2106             SKIP_UNKNOWN
2107         }
2108     }
2109 
2110     READ_EPILOGUE
2111 }
2112 
2113 #undef CURRENT_EL
2114 #define CURRENT_EL picture
2115 /*
2116  Parent elements:
2117  - chartsheet (§18.3.1.12)
2118  - [done] worksheet (§18.3.1.99)
2119 
2120  Child elements:
2121  - none
2122 */
2123 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_picture()
2124 {
2125     READ_PROLOGUE
2126     const QXmlStreamAttributes attrs(attributes());
2127     TRY_READ_ATTR_WITH_NS(r, id)
2128     const QString link = m_context->relationships->target(m_context->path, m_context->file, r_id);
2129     QString destinationName = QLatin1String("Pictures/") + link.mid(link.lastIndexOf('/') + 1);
2130     RETURN_IF_ERROR( m_context->import->copyFile(link, destinationName, false ) )
2131     addManifestEntryForFile(destinationName);
2132 
2133     m_context->sheet->setPictureBackgroundPath(destinationName);
2134 
2135     readNext();
2136     READ_EPILOGUE
2137 }
2138 
2139 #undef CURRENT_EL
2140 #define CURRENT_EL tableParts
2141 /*
2142  Parent elements:
2143  - [done] worksheet (§18.3.1.99)
2144 
2145  Child elements:
2146  - [done] tablePart (Table Part) §18.3.1.94
2147 
2148 */
2149 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_tableParts()
2150 {
2151     READ_PROLOGUE
2152     while (!atEnd()) {
2153         readNext();
2154         BREAK_IF_END_OF(CURRENT_EL)
2155         if( isStartElement() ) {
2156             TRY_READ_IF(tablePart)
2157             ELSE_WRONG_FORMAT
2158         }
2159     }
2160     READ_EPILOGUE
2161 }
2162 
2163 #undef CURRENT_EL
2164 #define CURRENT_EL tablePart
2165 /*
2166  Parent elements:
2167  - [done] tableParts (§18.3.1.95)
2168 
2169  Child elements:
2170  - none
2171 */
2172 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_tablePart()
2173 {
2174     READ_PROLOGUE
2175 
2176     const QXmlStreamAttributes attrs(attributes());
2177     READ_ATTR_WITH_NS(r, id)
2178     QString tablePathAndFile = m_context->relationships->target(m_context->path, m_context->file, r_id);
2179 
2180     XlsxXmlTableReaderContext context;
2181     XlsxXmlTableReader reader(this);
2182     const KoFilter::ConversionStatus result = m_context->import->loadAndParseDocument(&reader, tablePathAndFile, &context);
2183     if (result != KoFilter::OK) {
2184         raiseError(reader.errorString());
2185         return result;
2186     }
2187 
2188     readNext();
2189     READ_EPILOGUE
2190 }
2191 
2192 #undef CURRENT_EL
2193 #define CURRENT_EL legacyDrawing
2194 // todo
2195 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_legacyDrawing()
2196 {
2197     READ_PROLOGUE
2198     readNext();
2199     READ_EPILOGUE
2200 }
2201 
2202 #undef CURRENT_EL
2203 #define CURRENT_EL controls
2204 /*
2205  Parent elements:
2206  - [done] worksheet (§18.3.1.99)
2207 
2208  Child elements:
2209  - [done] control (Embedded Control) §18.3.1.19
2210 */
2211 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_controls()
2212 {
2213     READ_PROLOGUE
2214     while (!atEnd()) {
2215         readNext();
2216         BREAK_IF_END_OF(CURRENT_EL)
2217         if( isStartElement() ) {
2218             TRY_READ_IF(control)
2219             ELSE_WRONG_FORMAT
2220         }
2221     }
2222     READ_EPILOGUE
2223 }
2224 
2225 #undef CURRENT_EL
2226 #define CURRENT_EL oleObjects
2227 /*
2228  Parent elements:
2229  - [done] dialogsheet (§18.3.1.34)
2230  - [done] worksheet (§18.3.1.99)
2231 
2232  Child elements:
2233  - [done] oleObject (Embedded Object) §18.3.1.59
2234 */
2235 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_oleObjects()
2236 {
2237     READ_PROLOGUE
2238     while (!atEnd()) {
2239         readNext();
2240         BREAK_IF_END_OF(CURRENT_EL)
2241         if( isStartElement() ) {
2242             TRY_READ_IF(oleObject)
2243             // It seems that MSO 2010 has a concept of Alternate
2244             // Content, which it throws in at unexpected times.
2245             // This is one such time.  So let's try to find the
2246             // oleObject inside an mc:AlternateContent tag if possible.
2247             ELSE_TRY_READ_IF_NS(mc, AlternateContent)   // Should be more specialized what we are looking for
2248             ELSE_WRONG_FORMAT
2249         }
2250     }
2251     READ_EPILOGUE
2252 }
2253 
2254 #undef CURRENT_EL
2255 #define CURRENT_EL control
2256 /*
2257  Parent elements:
2258  - [done] controls (§18.3.1.21)
2259 
2260  Child elements:
2261  - controlPr (Embedded Control Properties) §18.3.1.20
2262 
2263 */
2264 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_control()
2265 {
2266     READ_PROLOGUE
2267 
2268     const QXmlStreamAttributes attrs(attributes());
2269     TRY_READ_ATTR_WITHOUT_NS(shapeId)
2270 
2271     // TODO: Maybe we want to do something with the actual control element.
2272 
2273     // In vmldrawing, the shape identifier has also the extra chars below, therefore
2274     // we have to add them here for the match
2275     shapeId = "_x0000_s" + shapeId;
2276 
2277     body->addCompleteElement(m_context->oleFrameBegins.value(shapeId).toUtf8());
2278     body->startElement("draw:image");
2279     body->addAttribute("xlink:href", m_context->oleReplacements.value(shapeId));
2280     body->addAttribute("xlink:type", "simple");
2281     body->addAttribute("xlink:show", "embed");
2282     body->addAttribute("xlink:actuate", "onLoad");
2283     body->endElement(); // draw:image
2284     body->addCompleteElement("</draw:frame>");
2285 
2286     while (!atEnd()) {
2287         readNext();
2288         BREAK_IF_END_OF(CURRENT_EL)
2289     }
2290     READ_EPILOGUE
2291 }
2292 
2293 #undef CURRENT_EL
2294 #define CURRENT_EL oleObject
2295 /*
2296  Parent elements:
2297  - [done] oleObjects (§18.3.1.60)
2298 
2299  Child elements:
2300  - objectPr (Embedded Object Properties) §18.3.1.56
2301 
2302 */
2303 KoFilter::ConversionStatus XlsxXmlWorksheetReader::read_oleObject()
2304 {
2305     READ_PROLOGUE
2306 
2307     const QXmlStreamAttributes attrs(attributes());
2308     READ_ATTR_WITH_NS(r, id)
2309     READ_ATTR_WITHOUT_NS(progId)
2310     TRY_READ_ATTR_WITHOUT_NS(shapeId)
2311 
2312     // In vmldrawing, the shape identifier has also the extra chars below, therefore
2313     // we have to add them here for the match
2314     shapeId = "_x0000_s" + shapeId;
2315 
2316     const QString link = m_context->relationships->target(m_context->path, m_context->file, r_id);
2317     QString destinationName = QLatin1String("") + link.mid(link.lastIndexOf('/') + 1);
2318     KoFilter::ConversionStatus status = m_context->import->copyFile(link, destinationName, false);
2319     if (status == KoFilter::OK) {
2320         addManifestEntryForFile(destinationName);
2321     }
2322 
2323     //TODO find out which cell to pick
2324     Cell* cell = m_context->sheet->cell(0, 0, true);
2325     cell->appendOleObject( qMakePair<QString,QString>(destinationName, m_context->oleReplacements.value(shapeId)), m_context->oleFrameBegins.value(shapeId));
2326 
2327     while (!atEnd()) {
2328         readNext();
2329         BREAK_IF_END_OF(CURRENT_EL)
2330     }
2331     READ_EPILOGUE
2332 }