File indexing completed on 2025-07-13 10:51:48
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2002 Norbert Andres <nandres@web.de> 0003 SPDX-FileCopyrightText: 2004-2005 Laurent Montel <montel@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "opencalcimport.h" 0009 #include "ooutils.h" 0010 0011 #include <KoDocumentInfo.h> 0012 #include <kpluginfactory.h> 0013 #include <kmessagebox.h> 0014 #include <kcodecs.h> 0015 #include <KoFilterChain.h> 0016 #include <KoUnit.h> 0017 #include <KoStyleStack.h> 0018 0019 #include "sheets/engine/calligra_sheets_limits.h" 0020 #include <sheets/core/Condition.h> 0021 #include <sheets/engine/NamedAreaManager.h> 0022 #include <sheets/engine/Region.h> 0023 #include <sheets/engine/Value.h> 0024 #include <sheets/engine/ValueParser.h> 0025 #include <sheets/core/CellStorage.h> 0026 #include <sheets/core/ColFormatStorage.h> 0027 #include <sheets/core/DocBase.h> 0028 #include <sheets/core/HeaderFooter.h> 0029 #include <sheets/core/Map.h> 0030 #include <sheets/core/PrintSettings.h> 0031 #include <sheets/core/RowFormatStorage.h> 0032 #include <sheets/core/Sheet.h> 0033 #include <sheets/core/Style.h> 0034 #include <sheets/core/StyleManager.h> 0035 0036 0037 #define SECSPERDAY (24 * 60 * 60) 0038 0039 using namespace Calligra::Sheets; 0040 0041 K_PLUGIN_FACTORY_WITH_JSON(OpenCalcImportFactory, "calligra_filter_opencalc2sheets.json", 0042 registerPlugin<OpenCalcImport>();) 0043 0044 OpenCalcImport::OpenCalcPoint::OpenCalcPoint(QString const & str, Calligra::Sheets::Map *map) 0045 : isRange(false) 0046 { 0047 bool inQuote = false; 0048 0049 int l = str.length(); 0050 int colonPos = -1; 0051 QString range; 0052 0053 // replace '.' with '!' 0054 for (int i = 0; i < l; ++i) { 0055 if (str[i] == '$') 0056 continue; 0057 if (str[i] == '\'') { 0058 inQuote = !inQuote; 0059 } else if (str[i] == '.') { 0060 if (!inQuote) { 0061 if (i != 0 && i != (colonPos + 1)) // no empty table names 0062 range += '!'; 0063 } else 0064 range += '.'; 0065 } else if (str[i] == ':') { 0066 if (!inQuote) { 0067 isRange = true; 0068 colonPos = i; 0069 } 0070 range += ':'; 0071 } else 0072 range += str[i]; 0073 } 0074 0075 translation = range; 0076 0077 const Calligra::Sheets::Region region = map->regionFromName(range); 0078 table = region.firstSheet()->sheetName(); 0079 topLeft = region.firstRange().topLeft(); 0080 botRight = region.firstRange().bottomRight(); 0081 } 0082 0083 0084 OpenCalcImport::OpenCalcImport(QObject* parent, const QVariantList &) 0085 : KoFilter(parent) 0086 { 0087 } 0088 0089 OpenCalcImport::~OpenCalcImport() 0090 { 0091 foreach(KoXmlElement* style, m_styles) delete style; 0092 foreach(Calligra::Sheets::Style* style, m_defaultStyles) delete style; 0093 foreach(QString* format, m_formats) delete format; 0094 } 0095 0096 double timeToNum(int h, int m, int s) 0097 { 0098 int secs = h * 3600 + m * 60 + s; 0099 return (double) secs / (double) SECSPERDAY; 0100 } 0101 0102 bool OpenCalcImport::readRowFormat(KoXmlElement & rowNode, KoXmlElement * rowStyle, 0103 Sheet * table, int & row, int & number, 0104 bool isLast) 0105 { 0106 if (rowNode.isNull()) 0107 return false; 0108 0109 KoXmlNode node; 0110 if (rowStyle) { 0111 node = rowStyle->firstChild(); 0112 qDebug() << "RowStyle:" << rowStyle << "," << rowStyle->tagName(); 0113 } 0114 0115 double height = -1.0; 0116 bool insertPageBreak = false; 0117 Style layout; 0118 0119 while (!node.isNull()) { 0120 KoXmlElement property = node.toElement(); 0121 0122 qDebug() << "Row: Child exists:" << property.tagName(); 0123 if (!property.isNull() && property.localName() == "properties" && property.namespaceURI() == ooNS::style) { 0124 if (property.hasAttributeNS(ooNS::style, "row-height")) { 0125 height = KoUnit::parseValue(property.attributeNS(ooNS::style, "row-height", QString()) , -1); 0126 } 0127 0128 if (property.hasAttributeNS(ooNS::fo, "break-before")) { 0129 if (property.attributeNS(ooNS::fo, "break-before", QString()) == "page") { 0130 insertPageBreak = true; 0131 } 0132 } 0133 0134 loadStyleProperties(&layout, property); 0135 } 0136 0137 node = node.nextSibling(); 0138 } 0139 0140 if (rowNode.hasAttributeNS(ooNS::table, "number-rows-repeated")) { 0141 bool ok = true; 0142 int n = rowNode.attributeNS(ooNS::table, "number-rows-repeated", QString()).toInt(&ok); 0143 if (ok) 0144 number = n; 0145 qDebug() << "Row repeated:" << number; 0146 } 0147 0148 if (isLast) { 0149 if (number > 30) 0150 number = 30; 0151 } else { 0152 if (number > 256) 0153 number = 256; 0154 } 0155 0156 if (height != -1) 0157 table->rowFormats()->setRowHeight(row, row+number-1, height); 0158 for (int i = 0; i < number; ++i) { 0159 table->fullCellStorage()->setStyle(Calligra::Sheets::Region(QRect(1, row, KS_colMax, 1)), layout); 0160 0161 0162 Q_UNUSED(insertPageBreak); //for now, as long as below code is commented. 0163 // if ( insertPageBreak ) TODO: 0164 // rowL->setPageBreak( true ) 0165 0166 // qDebug() <<"Added RowFormat:" << row; 0167 ++row; 0168 } 0169 0170 return true; 0171 } 0172 0173 QString OpenCalcImport::translatePar(QString & par) const 0174 { 0175 OpenCalcPoint point(par, m_doc->map()); 0176 qDebug() << " Parameter:" << par << ", Translation:" << point.translation; 0177 0178 return point.translation; 0179 } 0180 0181 void OpenCalcImport::checkForNamedAreas(QString & formula) const 0182 { 0183 int l = formula.length(); 0184 int i = 0; 0185 QString word; 0186 int start = 0; 0187 while (i < l) { 0188 if (formula[i].isLetterOrNumber()) { 0189 word += formula[i]; 0190 ++i; 0191 continue; 0192 } 0193 if (word.length() > 0) { 0194 if (m_namedAreas.contains(word)) { 0195 formula.replace(start, word.length(), '\'' + word + '\''); 0196 l = formula.length(); 0197 ++i; 0198 qDebug() << "Formula:" << formula << ", L:" << l << ", i:" << i + 1; 0199 } 0200 } 0201 0202 ++i; 0203 word = ""; 0204 start = i; 0205 } 0206 if (word.length() > 0) { 0207 if (m_namedAreas.contains(word)) { 0208 formula.replace(start, word.length(), '\'' + word + '\''); 0209 l = formula.length(); 0210 ++i; 0211 qDebug() << "Formula:" << formula << ", L:" << l << ", i:" << i + 1; 0212 } 0213 } 0214 } 0215 0216 void OpenCalcImport::convertFormula(QString & text, QString const & f) const 0217 { 0218 // TODO Stefan: Check if Oasis::decodeFormula could be used instead 0219 qDebug() << "Parsing formula:" << f; 0220 0221 QString formula; 0222 QString parameter; 0223 0224 int l = f.length(); 0225 int p = 0; 0226 0227 while (p < l) { 0228 if (f[p] == '(' || f[p] == '[') { 0229 break; 0230 } 0231 0232 formula += f[p]; 0233 ++p; 0234 } 0235 0236 if (parameter.isEmpty()) { 0237 checkForNamedAreas(formula); 0238 } 0239 0240 qDebug() << "Formula:" << formula << ", Parameter:" << parameter << ", P:" << p; 0241 0242 0243 // replace formula names here 0244 if (formula == "=MULTIPLE.OPERATIONS") 0245 formula = "=MULTIPLEOPERATIONS"; 0246 0247 QString par; 0248 bool isPar = false; 0249 bool inQuote = false; 0250 0251 while (p < l) { 0252 if (f[p] == '"') { 0253 inQuote = !inQuote; 0254 parameter += '"'; 0255 } else if (f[p] == '[') { 0256 if (!inQuote) 0257 isPar = true; 0258 else 0259 parameter += '['; 0260 } else if (f[p] == ']') { 0261 if (inQuote) { 0262 parameter += ']'; 0263 continue; 0264 } 0265 0266 isPar = false; 0267 parameter += translatePar(par); 0268 par.clear(); 0269 } else if (isPar) { 0270 par += f[p]; 0271 } else if (f[p] == '=') { // TODO: check if StarCalc has a '==' sometimes 0272 if (inQuote) 0273 parameter += '='; 0274 else 0275 parameter += "=="; 0276 } else if (f[p] == ')') { 0277 if (!inQuote) 0278 parameter += ')'; 0279 } else 0280 parameter += f[p]; 0281 0282 ++p; 0283 if (p == l) 0284 checkForNamedAreas(parameter); 0285 } 0286 0287 text = formula + parameter; 0288 qDebug() << "New formula:" << text; 0289 } 0290 0291 bool OpenCalcImport::readCells(KoXmlElement & rowNode, Sheet * table, int row, int & columns) 0292 { 0293 ValueParser *const parser = table->map()->parser(); 0294 0295 bool ok = true; 0296 int spanC = 1; 0297 int spanR = 1; 0298 //Cell* defCell = table->defaultCell(); 0299 0300 KoXmlNode cellNode = KoXml::namedItemNS(rowNode, ooNS::table, "table-cell"); 0301 0302 while (!cellNode.isNull()) { 0303 spanR = 1; spanC = 1; 0304 0305 KoXmlElement e = cellNode.toElement(); 0306 if (e.isNull()) { 0307 ++columns; 0308 0309 cellNode = cellNode.nextSibling(); 0310 continue; 0311 } 0312 0313 Cell cell; 0314 0315 qDebug() << " Cell:" << columns << "," << row; 0316 0317 // ="3" table:number-rows-spanned="1" 0318 if (e.hasAttributeNS(ooNS::table, "number-columns-spanned")) { 0319 int span = e.attributeNS(ooNS::table, "number-columns-spanned", QString()).toInt(&ok); 0320 if (ok) 0321 spanC = span; 0322 } 0323 if (e.hasAttributeNS(ooNS::table, "number-rows-spanned")) { 0324 int span = e.attributeNS(ooNS::table, "number-rows-spanned", QString()).toInt(&ok); 0325 if (ok) 0326 spanR = span; 0327 } 0328 0329 QString text; 0330 KoXmlElement textP = KoXml::namedItemNS(e, ooNS::text, "p"); 0331 if (!textP.isNull()) { 0332 KoXmlElement subText = textP.firstChild().toElement(); // ## wrong 0333 if (!subText.isNull()) { 0334 // something in <text:p>, e.g. links 0335 text = subText.text(); 0336 0337 if (subText.hasAttributeNS(ooNS::xlink, "href")) { 0338 QString link = subText.attributeNS(ooNS::xlink, "href", QString()); 0339 if (link[0] == '#') 0340 link = link.remove(0, 1); 0341 if (!cell) 0342 cell = Cell(table, columns, row); 0343 cell.setLink(link); 0344 } 0345 } else 0346 text = textP.text(); // our text, could contain formatting for value or result of formula 0347 } 0348 KoXmlElement annotation = KoXml::namedItemNS(e, ooNS::office, "annotation"); 0349 if (!annotation.isNull()) { 0350 QString comment; 0351 KoXmlNode node = annotation.firstChild(); 0352 while (!node.isNull()) { 0353 KoXmlElement commentElement = node.toElement(); 0354 if (!commentElement.isNull()) 0355 if (commentElement.localName() == "p" && e.namespaceURI() == ooNS::text) { 0356 if (!comment.isEmpty()) comment.append('\n'); 0357 comment.append(commentElement.text()); 0358 } 0359 0360 node = node.nextSibling(); 0361 } 0362 0363 if (!comment.isEmpty()) { 0364 qDebug() << " columns :" << columns << " row :" << row; 0365 Cell(table, columns, row).setComment(comment); 0366 } 0367 } 0368 0369 qDebug() << "Contains:" << text; 0370 bool isFormula = false; 0371 0372 if (e.hasAttributeNS(ooNS::table, "style-name")) { 0373 if (!cell) 0374 cell = Cell(table, columns, row); 0375 Style style = cell.style(); 0376 0377 QString psName("Default"); 0378 if (e.hasAttributeNS(ooNS::style, "parent-style-name")) 0379 psName = e.attributeNS(ooNS::style, "parent-style-name", QString()); 0380 0381 qDebug() << "Default style:" << psName; 0382 Style * layout = m_defaultStyles[psName]; 0383 0384 if (layout) 0385 style = *layout; 0386 0387 KoXmlElement * st = 0; 0388 if (e.hasAttributeNS(ooNS::table, "style-name")) { 0389 qDebug() << "Style:" << e.attributeNS(ooNS::table, "style-name", QString()); 0390 st = m_styles[ e.attributeNS(ooNS::table, "style-name", QString())]; 0391 } 0392 if (st) { 0393 qDebug() << "Style: adapting"; 0394 KoXmlNode node = st->firstChild(); 0395 bool foundValidation = false; 0396 while (!node.isNull()) { 0397 KoXmlElement property = node.toElement(); 0398 if (!property.isNull()) { 0399 qDebug() << "property.tagName() :" << property.tagName(); 0400 if (property.localName() == "map" && property.namespaceURI() == ooNS::style && !foundValidation) { 0401 loadCondition(cell, property); 0402 foundValidation = true; 0403 } 0404 if (property.localName() == "properties" && property.namespaceURI() == ooNS::style) { 0405 loadStyleProperties(&style, property); 0406 if (style.angle() != 0) { 0407 QFontMetrics fm(style.font()); 0408 int tmpAngle = style.angle(); 0409 int textHeight = static_cast<int>(cos(tmpAngle * M_PI / 180) 0410 * (fm.ascent() + fm.descent()) 0411 + abs((int)(fm.width(cell.displayText()) 0412 * sin(tmpAngle * M_PI / 180)))); 0413 /* 0414 int textWidth = static_cast<int>( abs ( ( int ) ( sin( tmpAngle * M_PI / 180 ) 0415 * ( fm.ascent() + fm.descent() ) ) ) 0416 + fm.width( cell.displayText() ) 0417 * cos( tmpAngle * M_PI / 180 ) ); 0418 */ 0419 qDebug() << "Rotation: height:" << textHeight; 0420 0421 if (table->rowFormats()->rowHeight(row) < textHeight) 0422 table->rowFormats()->setRowHeight(row, row, textHeight + 2); 0423 } 0424 } 0425 } 0426 node = node.nextSibling(); 0427 } 0428 } 0429 } else { 0430 QString psName("Default"); 0431 qDebug() << "Default style:" << psName; 0432 Style * layout = m_defaultStyles[psName]; 0433 0434 if (layout) 0435 table->fullCellStorage()->setStyle(Calligra::Sheets::Region(QPoint(columns, row)), *layout); 0436 } 0437 if (e.hasAttributeNS(ooNS::table, "formula")) { 0438 isFormula = true; 0439 QString formula; 0440 convertFormula(formula, e.attributeNS(ooNS::table, "formula", QString())); 0441 0442 if (!cell) 0443 cell = Cell(table, columns, row); 0444 cell.setUserInput(formula); 0445 } 0446 if (e.hasAttributeNS(ooNS::table, "validation-name")) { 0447 qDebug() << " Celle has a validation :" << e.attributeNS(ooNS::table, "validation-name", QString()); 0448 loadOasisValidation(Cell(table, columns, row).validity(), e.attributeNS(ooNS::table, "validation-name", QString()), parser); 0449 } 0450 if (e.hasAttributeNS(ooNS::table, "value-type")) { 0451 if (!cell) 0452 cell = Cell(table, columns, row); 0453 Style style; 0454 0455 cell.setUserInput(text); 0456 0457 QString value = e.attributeNS(ooNS::table, "value", QString()); 0458 QString type = e.attributeNS(ooNS::table, "value-type", QString()); 0459 0460 qDebug() << "Value:" << value << ", type:" << type; 0461 0462 bool ok = false; 0463 double dv = 0.0; 0464 0465 if ((type == "float") || (type == "currency")) { 0466 dv = value.toDouble(&ok); 0467 if (ok) { 0468 if (!isFormula) 0469 cell.setValue(Value(dv)); 0470 0471 if (type == "currency") { 0472 Currency currency(e.attributeNS(ooNS::table, "currency", QString())); 0473 style.setCurrency(currency); 0474 style.setFormatType(Format::Money); 0475 } 0476 } 0477 } else if (type == "percentage") { 0478 dv = value.toDouble(&ok); 0479 if (ok) { 0480 if (!isFormula) 0481 cell.setValue(Value(dv)); 0482 //TODO fixme 0483 //cell.setFactor( 100 ); 0484 // TODO: replace with custom... 0485 style.setFormatType(Format::Percentage); 0486 } 0487 } else if (type == "boolean") { 0488 if (value.isEmpty()) 0489 value = e.attributeNS(ooNS::table, "boolean-value", QString()); 0490 0491 qDebug() << "Type: boolean"; 0492 if (value == "true") 0493 cell.setValue(Value(true)); 0494 else 0495 cell.setValue(Value(false)); 0496 ok = true; 0497 style.setFormatType(Format::Custom); 0498 } else if (type == "date") { 0499 if (value.isEmpty()) 0500 value = e.attributeNS(ooNS::table, "date-value", QString()); 0501 qDebug() << "Type: date, value:" << value; 0502 0503 // "1980-10-15" 0504 int year = 0, month = 0, day = 0; 0505 ok = false; 0506 0507 int p1 = value.indexOf('-'); 0508 if (p1 > 0) 0509 year = value.leftRef(p1).toInt(&ok); 0510 0511 qDebug() << "year:" << value.left(p1); 0512 0513 int p2 = value.indexOf('-', ++p1); 0514 0515 if (ok) 0516 month = value.midRef(p1, p2 - p1).toInt(&ok); 0517 0518 qDebug() << "month:" << value.mid(p1, p2 - p1); 0519 0520 if (ok) 0521 day = value.rightRef(value.length() - p2 - 1).toInt(&ok); 0522 0523 qDebug() << "day:" << value.right(value.length() - p2); 0524 0525 if (ok) { 0526 cell.setValue(Value(QDate(year, month, day), cell.sheet()->map()->calculationSettings())); 0527 qDebug() << "Set QDate:" << year << " -" << month << " -" << day; 0528 } 0529 } else if (type == "time") { 0530 if (value.isEmpty()) 0531 value = e.attributeNS(ooNS::table, "time-value", QString()); 0532 0533 qDebug() << "Type: time:" << value; 0534 // "PT15H10M12S" 0535 int hours = 0, minutes = 0, seconds = 0; 0536 int l = value.length(); 0537 QString num; 0538 0539 for (int i = 0; i < l; ++i) { 0540 if (value[i].isNumber()) { 0541 num += value[i]; 0542 continue; 0543 } else if (value[i] == 'H') 0544 hours = num.toInt(&ok); 0545 else if (value[i] == 'M') 0546 minutes = num.toInt(&ok); 0547 else if (value[i] == 'S') 0548 seconds = num.toInt(&ok); 0549 else 0550 continue; 0551 0552 qDebug() << "Num:" << num; 0553 0554 num.clear(); 0555 if (!ok) 0556 break; 0557 } 0558 0559 qDebug() << "Hours:" << hours << "," << minutes << "," << seconds; 0560 0561 if (ok) { 0562 // KSpreadValue kval( timeToNum( hours, minutes, seconds ) ); 0563 // cell.setValue( kval ); 0564 cell.setValue(Value(Time(hours % 24, minutes, seconds))); 0565 style.setFormatType(Format::Custom); 0566 } 0567 } 0568 0569 cell.setStyle(style); 0570 if (!ok) // just in case we couldn't set the value directly 0571 cell.parseUserInput(text); 0572 } else if (!text.isEmpty()) { 0573 if (!cell) 0574 cell = Cell(table, columns, row); 0575 cell.parseUserInput(text); 0576 } 0577 0578 if (spanR > 1 || spanC > 1) { 0579 if (!cell) 0580 cell = Cell(table, columns, row); 0581 cell.mergeCells(columns, row, spanC - 1, spanR - 1); 0582 } 0583 0584 cellNode = cellNode.nextSibling(); 0585 0586 if (e.hasAttributeNS(ooNS::table, "number-columns-repeated")) { 0587 // copy cell from left 0588 bool ok = false; 0589 int number = e.attributeNS(ooNS::table, "number-columns-repeated", QString()).toInt(&ok); 0590 Cell cellDest; 0591 0592 // don't repeat more than 10 if it is the last cell and empty 0593 if (!ok || cellNode.isNull()) { 0594 if (number > 10) 0595 number = 10; 0596 } 0597 0598 for (int i = 1; i < number; ++i) { 0599 ++columns; 0600 0601 if (!cell.isNull()) { 0602 cellDest = Cell(table, columns, row); 0603 cellDest.copyAll(cell, Paste::Normal, Paste::OverWrite); 0604 } 0605 } 0606 } 0607 0608 ++columns; 0609 } 0610 0611 return true; 0612 } 0613 0614 0615 void OpenCalcImport::loadCondition(const Cell& cell, const KoXmlElement &property) 0616 { 0617 qDebug() << "void OpenCalcImport::loadCondition( Cell*cell,const KoXmlElement &property )*******"; 0618 loadOasisCondition(cell, property); 0619 } 0620 0621 void OpenCalcImport::loadOasisCondition(const Cell& cell, const KoXmlElement &property) 0622 { 0623 KoXmlElement elementItem(property); 0624 MapBase *const map = cell.sheet()->map(); 0625 ValueParser *const parser = map->parser(); 0626 0627 QLinkedList<Conditional> cond; 0628 while (!elementItem.isNull()) { 0629 qDebug() << "elementItem.tagName() :" << elementItem.tagName(); 0630 0631 if (elementItem.localName() == "map" && property.namespaceURI() == ooNS::style) { 0632 bool ok = true; 0633 qDebug() << "elementItem.attribute(style:condition ) :" << elementItem.attributeNS(ooNS::style, "condition", QString()); 0634 Conditional newCondition; 0635 loadOasisConditionValue(elementItem.attributeNS(ooNS::style, "condition", QString()), newCondition, parser); 0636 if (elementItem.hasAttributeNS(ooNS::style, "apply-style-name")) { 0637 qDebug() << "elementItem.attribute( style:apply-style-name ) :" << elementItem.attributeNS(ooNS::style, "apply-style-name", QString()); 0638 newCondition.styleName = elementItem.attributeNS(ooNS::style, "apply-style-name", QString()); 0639 ok = !newCondition.styleName.isEmpty(); 0640 } 0641 0642 if (ok) 0643 cond.append(newCondition); 0644 else 0645 qDebug() << "Error loading condition" << elementItem.nodeName(); 0646 } 0647 elementItem = elementItem.nextSibling().toElement(); 0648 } 0649 if (!cond.isEmpty()) { 0650 Conditions conditions; 0651 conditions.setConditionList(cond); 0652 Cell(cell).setConditions(conditions); 0653 } 0654 } 0655 0656 void OpenCalcImport::loadOasisConditionValue(const QString &styleCondition, Conditional &newCondition, 0657 const ValueParser *parser) 0658 { 0659 QString val(styleCondition); 0660 if (val.contains("cell-content()")) { 0661 val = val.remove("cell-content()"); 0662 loadOasisCondition(val, newCondition, parser); 0663 } 0664 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value) 0665 //for the moment we support just int/double value, not text/date/time :( 0666 if (val.contains("cell-content-is-between(")) { 0667 val.remove("cell-content-is-between("); 0668 val.remove(')'); 0669 QStringList listVal = val.split(','); 0670 qDebug() << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1]; 0671 newCondition.value1 = parser->parse(listVal[0]); 0672 newCondition.value2 = parser->parse(listVal[1]); 0673 newCondition.cond = Validity::Between; 0674 } 0675 if (val.contains("cell-content-is-not-between(")) { 0676 val.remove("cell-content-is-not-between("); 0677 val.remove(')'); 0678 QStringList listVal = val.split(','); 0679 qDebug() << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1]; 0680 newCondition.value1 = parser->parse(listVal[0]); 0681 newCondition.value2 = parser->parse(listVal[1]); 0682 newCondition.cond = Validity::Different; 0683 } 0684 0685 } 0686 0687 0688 void OpenCalcImport::loadOasisCondition(QString &valExpression, Conditional &newCondition, 0689 const ValueParser *parser) 0690 { 0691 QString value; 0692 if (valExpression.indexOf("<=") == 0) { 0693 value = valExpression.remove(0, 2); 0694 newCondition.cond = Validity::InferiorEqual; 0695 } else if (valExpression.indexOf(">=") == 0) { 0696 value = valExpression.remove(0, 2); 0697 newCondition.cond = Validity::SuperiorEqual; 0698 } else if (valExpression.indexOf("!=") == 0) { 0699 //add Differentto attribute 0700 value = valExpression.remove(0, 2); 0701 newCondition.cond = Validity::DifferentTo; 0702 } else if (valExpression.indexOf('<') == 0) { 0703 value = valExpression.remove(0, 1); 0704 newCondition.cond = Validity::Inferior; 0705 } else if (valExpression.indexOf('>') == 0) { 0706 value = valExpression.remove(0, 1); 0707 newCondition.cond = Validity::Superior; 0708 } else if (valExpression.indexOf('=') == 0) { 0709 value = valExpression.remove(0, 1); 0710 newCondition.cond = Validity::Equal; 0711 } else 0712 qDebug() << " I don't know how to parse it :" << valExpression; 0713 qDebug() << " value :" << value; 0714 newCondition.value1 = parser->parse(value); 0715 } 0716 0717 bool OpenCalcImport::readRowsAndCells(KoXmlElement & content, Sheet * table) 0718 { 0719 qDebug() << "Reading in rows"; 0720 0721 int i = 1; 0722 int row = 1; 0723 int columns = 1; 0724 int backupRow = 1; 0725 KoXmlElement * rowStyle = 0; 0726 //Cell cell; 0727 //Cell cellDest; 0728 //Cell defCell = table->defaultCell(); 0729 KoXmlNode rowNode = KoXml::namedItemNS(content, ooNS::table, "table-row"); 0730 0731 while (!rowNode.isNull()) { 0732 bool collapsed = false; 0733 0734 int number = 1; 0735 KoXmlElement r = rowNode.toElement(); 0736 0737 if (r.isNull()) 0738 return false; 0739 0740 if (r.hasAttributeNS(ooNS::table, "style-name")) { 0741 QString style = r.attributeNS(ooNS::table, "style-name", QString()); 0742 rowStyle = m_styles[ style ]; 0743 qDebug() << "Row style:" << style; 0744 } 0745 0746 collapsed = (r.attributeNS(ooNS::table, "visibility", QString()) == "collapse"); 0747 0748 backupRow = row; 0749 0750 rowNode = rowNode.nextSibling(); 0751 0752 if (!readRowFormat(r, rowStyle, table, row, number, rowNode.isNull())) // updates "row" 0753 return false; 0754 0755 if (!readCells(r, table, backupRow, columns)) 0756 return false; 0757 0758 // RowFormat * srcLayout = table->nonDefaultRowFormat(backupRow); 0759 // RowFormat * layout = 0; 0760 0761 if (collapsed) 0762 table->rowFormats()->setHidden(backupRow, backupRow, true); 0763 0764 for (i = 1; i < number; ++i) { 0765 // FIXME CALLIGRA_SHEETS_NEW_STYLE_STORAGE 0766 // layout = table->nonDefaultRowFormat( backupRow + i ); 0767 // 0768 // table->setStyle( Calligra::Sheets::Region(QRect(1,backupRow + i,KS_colMax,1)), srcLayout ); 0769 0770 /* 0771 * TODO: Test: do we need to copy the cells, too? 0772 * if so we will probably also need to copy them for repeated col layouts. 0773 for ( j = 1; j <= columns; ++j ) 0774 { 0775 Cell cell( table, j, backupRow ); 0776 0777 qDebug() <<"Cell:" << cell <<"DefCell:" << defCell; 0778 if ( cell && (cell != defCell) ) 0779 { 0780 cellDest = Cell( table, j, backupRow + i ); 0781 cellDest->copyAll( cell ); 0782 } 0783 } 0784 */ 0785 } 0786 0787 rowStyle = 0; 0788 columns = 1; 0789 } 0790 0791 qDebug() << "Reading in rows done"; 0792 0793 return true; 0794 } 0795 0796 bool OpenCalcImport::readColLayouts(KoXmlElement & content, Sheet * table) 0797 { 0798 qDebug() << "Reading in columns..."; 0799 0800 KoXmlNode colLayout = KoXml::namedItemNS(content, ooNS::table, "table-column"); 0801 int column = 1; 0802 0803 while (!colLayout.isNull()) { 0804 if (colLayout.nodeName() != "table:table-column") 0805 return true; // all cols read in. 0806 0807 KoXmlElement e = colLayout.toElement(); 0808 0809 if (e.isNull()) 0810 return false; // error, that's it... 0811 0812 qDebug() << "New column:" << column; 0813 0814 int number = 1; 0815 double width = -1.0; 0816 bool collapsed = (e.attributeNS(ooNS::table, "visibility", QString()) == "collapse"); 0817 bool insertPageBreak = false; 0818 Style styleLayout; 0819 0820 qDebug() << "Check table:number-columns-repeated"; 0821 if (e.hasAttributeNS(ooNS::table, "number-columns-repeated")) { 0822 bool ok = true; 0823 number = e.attributeNS(ooNS::table, "number-columns-repeated", QString()).toInt(&ok); 0824 if (!ok) 0825 number = 1; 0826 0827 qDebug() << "Repeated:" << number; 0828 } 0829 0830 qDebug() << "Checking table:default-cell-style-name"; 0831 if (e.hasAttributeNS(ooNS::table, "default-cell-style-name")) { 0832 QString n(e.attributeNS(ooNS::table, "default-cell-style-name", QString())); 0833 qDebug() << "Has attribute default-cell-style-name:" << n; 0834 Style * defaultStyle = m_defaultStyles[ n ]; 0835 if (!defaultStyle) { 0836 QString name = e.attributeNS(ooNS::table, "default-cell-style-name", QString()); 0837 KoXmlElement * st = m_styles[ name ]; 0838 0839 qDebug() << "Default cell style:" << name; 0840 0841 if (st && !st->isNull()) { 0842 Style * layout = new Style(); 0843 0844 readInStyle(layout, *st); 0845 0846 m_defaultStyles.insert(name, layout); 0847 qDebug() << "Insert default cell style:" << name; 0848 0849 defaultStyle = layout; 0850 } 0851 } 0852 0853 if (defaultStyle) { 0854 // qDebug() <<"Copying default style, Font:" << defaultStyle->font().toString(); 0855 styleLayout = *defaultStyle; 0856 } 0857 } 0858 0859 KoXmlElement * colStyle = 0; 0860 if (e.hasAttributeNS(ooNS::table, "style-name")) { 0861 QString style = e.attributeNS(ooNS::table, "style-name", QString()); 0862 colStyle = m_styles[ style ]; 0863 0864 qDebug() << "Col Style:" << style; 0865 } 0866 0867 KoXmlNode node; 0868 0869 if (colStyle) 0870 node = colStyle->firstChild(); 0871 0872 while (!node.isNull()) { 0873 KoXmlElement property = node.toElement(); 0874 if (!property.isNull() && property.localName() == "properties" && property.namespaceURI() == ooNS::style) { 0875 if (property.hasAttributeNS(ooNS::style, "column-width")) { 0876 QString sWidth = property.attributeNS(ooNS::style, "column-width", QString()); 0877 width = KoUnit::parseValue(property.attributeNS(ooNS::style, "column-width", QString()), width); 0878 qDebug() << "Col Width:" << sWidth; 0879 } 0880 0881 if (property.hasAttributeNS(ooNS::fo, "break-before")) { 0882 if (property.attributeNS(ooNS::fo, "break-before", QString()) == "page") { 0883 insertPageBreak = true; 0884 } 0885 } 0886 0887 loadStyleProperties(&styleLayout, property); 0888 } 0889 0890 node = node.nextSibling(); 0891 } 0892 0893 colLayout = colLayout.nextSibling(); 0894 0895 if (colLayout.isNull() && (number > 30)) 0896 number = 30; 0897 0898 ColFormatStorage *cf = table->columnFormats(); 0899 for (int i = 0; i < number; ++i) { 0900 qDebug() << "Inserting colLayout:" << column; 0901 0902 table->fullCellStorage()->setStyle(Calligra::Sheets::Region(QRect(column, 1, 1, KS_rowMax)), styleLayout); 0903 if (width != -1.0) 0904 cf->setColWidth(column, column, width); 0905 0906 if ( insertPageBreak ) 0907 cf->setPageBreak( column, column, true ); 0908 0909 if (collapsed) 0910 cf->setHidden(column, column, true); 0911 0912 ++column; 0913 } 0914 } 0915 0916 return true; 0917 } 0918 0919 void replaceMacro(QString & text, QString const & old, QString const & newS) 0920 { 0921 int n = text.indexOf(old); 0922 if (n != -1) 0923 text.replace(n, old.length(), newS); 0924 } 0925 0926 QString getPart(KoXmlNode const & part) 0927 { 0928 QString result; 0929 KoXmlElement e = KoXml::namedItemNS(part, ooNS::text, "p"); 0930 while (!e.isNull()) { 0931 QString text = e.text(); 0932 qDebug() << "PART:" << text; 0933 0934 KoXmlElement macro = KoXml::namedItemNS(e, ooNS::text, "time"); 0935 if (!macro.isNull()) 0936 replaceMacro(text, macro.text(), "<time>"); 0937 0938 macro = KoXml::namedItemNS(e, ooNS::text, "date"); 0939 if (!macro.isNull()) 0940 replaceMacro(text, macro.text(), "<date>"); 0941 0942 macro = KoXml::namedItemNS(e, ooNS::text, "page-number"); 0943 if (!macro.isNull()) 0944 replaceMacro(text, macro.text(), "<page>"); 0945 0946 macro = KoXml::namedItemNS(e, ooNS::text, "page-count"); 0947 if (!macro.isNull()) 0948 replaceMacro(text, macro.text(), "<pages>"); 0949 0950 macro = KoXml::namedItemNS(e, ooNS::text, "sheet-name"); 0951 if (!macro.isNull()) 0952 replaceMacro(text, macro.text(), "<sheet>"); 0953 0954 macro = KoXml::namedItemNS(e, ooNS::text, "title"); 0955 if (!macro.isNull()) 0956 replaceMacro(text, macro.text(), "<name>"); 0957 0958 macro = KoXml::namedItemNS(e, ooNS::text, "file-name"); 0959 if (!macro.isNull()) 0960 replaceMacro(text, macro.text(), "<file>"); 0961 0962 if (!result.isEmpty()) 0963 result += '\n'; 0964 result += text; 0965 e = e.nextSibling().toElement(); 0966 } 0967 0968 return result; 0969 } 0970 0971 void OpenCalcImport::loadTableMasterStyle(Sheet * table, 0972 QString const & stylename) 0973 { 0974 qDebug() << "Loading table master style:" << stylename; 0975 0976 KoXmlElement * style = m_styles[ stylename ]; 0977 0978 if (!style) { 0979 qDebug() << "Master style not found!"; 0980 return; 0981 } 0982 0983 KoXmlNode header = KoXml::namedItemNS(*style, ooNS::style, "header"); 0984 qDebug() << "Style header"; 0985 0986 QString hleft, hmiddle, hright; 0987 QString fleft, fmiddle, fright; 0988 0989 if (!header.isNull()) { 0990 qDebug() << "Header exists"; 0991 KoXmlNode part = KoXml::namedItemNS(header, ooNS::style, "region-left"); 0992 if (!part.isNull()) { 0993 hleft = getPart(part); 0994 qDebug() << "Header left:" << hleft; 0995 } else 0996 qDebug() << "Style:region:left doesn't exist!"; 0997 part = KoXml::namedItemNS(header, ooNS::style, "region-center"); 0998 if (!part.isNull()) { 0999 hmiddle = getPart(part); 1000 qDebug() << "Header middle:" << hmiddle; 1001 } 1002 part = KoXml::namedItemNS(header, ooNS::style, "region-right"); 1003 if (!part.isNull()) { 1004 hright = getPart(part); 1005 qDebug() << "Header right:" << hright; 1006 } 1007 } 1008 1009 KoXmlNode footer = KoXml::namedItemNS(*style, ooNS::style, "footer"); 1010 1011 if (!footer.isNull()) { 1012 KoXmlNode part = KoXml::namedItemNS(footer, ooNS::style, "region-left"); 1013 if (!part.isNull()) { 1014 fleft = getPart(part); 1015 qDebug() << "Footer left:" << fleft; 1016 } 1017 part = KoXml::namedItemNS(footer, ooNS::style, "region-center"); 1018 if (!part.isNull()) { 1019 fmiddle = getPart(part); 1020 qDebug() << "Footer middle:" << fmiddle; 1021 } 1022 part = KoXml::namedItemNS(footer, ooNS::style, "region-right"); 1023 if (!part.isNull()) { 1024 fright = getPart(part); 1025 qDebug() << "Footer right:" << fright; 1026 } 1027 } 1028 1029 table->headerFooter()->setHeadFootLine(hleft, hmiddle, hright, 1030 fleft, fmiddle, fright); 1031 if (style->hasAttributeNS(ooNS::style, "page-master-name")) { 1032 QString masterPageLayoutStyleName = style->attributeNS(ooNS::style, "page-master-name", QString()); 1033 qDebug() << "masterPageLayoutStyleName :" << masterPageLayoutStyleName; 1034 KoXmlElement *masterLayoutStyle = m_styles[masterPageLayoutStyleName]; 1035 qDebug() << "masterLayoutStyle :" << masterLayoutStyle; 1036 if (!masterLayoutStyle) 1037 return; 1038 KoStyleStack styleStack(ooNS::style, ooNS::fo); 1039 styleStack.push(*masterLayoutStyle); 1040 loadOasisMasterLayoutPage(table, styleStack); 1041 } 1042 } 1043 1044 void OpenCalcImport::loadOasisMasterLayoutPage(Sheet * table, KoStyleStack &styleStack) 1045 { 1046 float leftMargin = 0.0; 1047 float rightMargin = 0.0; 1048 float topMargin = 0.0; 1049 float bottomMargin = 0.0; 1050 float width = 0.0; 1051 float height = 0.0; 1052 QString orientation = "Portrait"; 1053 QString format; 1054 1055 if (styleStack.hasProperty(ooNS::fo, "page-width")) { 1056 width = KoUnit::parseValue(styleStack.property(ooNS::fo, "page-width")); 1057 } 1058 if (styleStack.hasProperty(ooNS::fo, "page-height")) { 1059 height = KoUnit::parseValue(styleStack.property(ooNS::fo, "page-height")); 1060 } 1061 if (styleStack.hasProperty(ooNS::fo, "margin-top")) { 1062 topMargin = KoUnit::parseValue(styleStack.property(ooNS::fo, "margin-top")); 1063 } 1064 if (styleStack.hasProperty(ooNS::fo, "margin-bottom")) { 1065 bottomMargin = KoUnit::parseValue(styleStack.property(ooNS::fo, "margin-bottom")); 1066 } 1067 if (styleStack.hasProperty(ooNS::fo, "margin-left")) { 1068 leftMargin = KoUnit::parseValue(styleStack.property(ooNS::fo, "margin-left")); 1069 } 1070 if (styleStack.hasProperty(ooNS::fo, "margin-right")) { 1071 rightMargin = KoUnit::parseValue(styleStack.property(ooNS::fo, "margin-right")); 1072 } 1073 if (styleStack.hasProperty(ooNS::style, "writing-mode")) { 1074 qDebug() << "styleStack.hasAttribute( style:writing-mode ) :" << styleStack.hasProperty(ooNS::style, "writing-mode"); 1075 } 1076 if (styleStack.hasProperty(ooNS::style, "print-orientation")) { 1077 orientation = (styleStack.property(ooNS::style, "print-orientation") == "landscape") ? "Landscape" : "Portrait" ; 1078 } 1079 if (styleStack.hasProperty(ooNS::style, "num-format")) { 1080 qDebug() << " num-format :" << styleStack.property(ooNS::style, "num-format"); 1081 //todo fixme 1082 } 1083 if (styleStack.hasProperty(ooNS::fo, "background-color")) { 1084 //todo 1085 qDebug() << " fo:background-color :" << styleStack.property(ooNS::fo, "background-color"); 1086 } 1087 if (styleStack.hasProperty(ooNS::style, "print")) { 1088 //todo parsing 1089 QString str = styleStack.property(ooNS::style, "print"); 1090 qDebug() << " style:print :" << str; 1091 1092 if (str.contains("headers")) { 1093 //todo implement it into kspread 1094 } 1095 if (str.contains("grid")) { 1096 table->printSettings()->setPrintGrid(true); 1097 } 1098 if (str.contains("annotations")) { 1099 //todo it's not implemented 1100 } 1101 if (str.contains("objects")) { 1102 //todo it's not implemented 1103 } 1104 if (str.contains("charts")) { 1105 //todo it's not implemented 1106 } 1107 if (str.contains("drawings")) { 1108 //todo it's not implemented 1109 } 1110 if (str.contains("formulas")) { 1111 table->setShowFormula(true); 1112 } 1113 if (str.contains("zero-values")) { 1114 //todo it's not implemented 1115 } 1116 } 1117 if (styleStack.hasProperty(ooNS::style, "table-centering")) { 1118 QString str = styleStack.property(ooNS::style, "table-centering"); 1119 //not implemented into kspread 1120 qDebug() << " styleStack.attribute( style:table-centering ) :" << str; 1121 #if 0 1122 if (str == "horizontal") { 1123 } else if (str == "vertical") { 1124 } else if (str == "both") { 1125 } else if (str == "none") { 1126 } else 1127 qDebug() << " table-centering unknown :" << str; 1128 #endif 1129 } 1130 format = QString("%1x%2").arg(width).arg(height); 1131 qDebug() << " format :" << format; 1132 1133 KoPageLayout pageLayout; 1134 pageLayout.format = KoPageFormat::formatFromString(format); 1135 pageLayout.orientation = (orientation == "Portrait") 1136 ? KoPageFormat::Portrait : KoPageFormat::Landscape; 1137 pageLayout.leftMargin = leftMargin; 1138 pageLayout.rightMargin = rightMargin; 1139 pageLayout.topMargin = topMargin; 1140 pageLayout.bottomMargin = bottomMargin; 1141 table->printSettings()->setPageLayout(pageLayout); 1142 1143 qDebug() << " left margin :" << leftMargin << " right :" << rightMargin 1144 << " top :" << topMargin << " bottom :" << bottomMargin; 1145 //<style:properties fo:page-width="21.8cm" fo:page-height="28.801cm" fo:margin-top="2cm" fo:margin-bottom="2.799cm" fo:margin-left="1.3cm" fo:margin-right="1.3cm" style:writing-mode="lr-tb"/> 1146 // QString format = paper.attribute( "format" ); 1147 // QString orientation = paper.attribute( "orientation" ); 1148 // m_pPrint->setPaperLayout( left, top, right, bottom, format, orientation ); 1149 // } 1150 } 1151 1152 1153 bool OpenCalcImport::parseBody(int numOfTables) 1154 { 1155 KoXmlElement content = m_content.documentElement(); 1156 KoXmlNode body = KoXml::namedItemNS(content, ooNS::office, "body"); 1157 1158 if (body.isNull()) 1159 return false; 1160 1161 loadOasisAreaName(body.toElement()); 1162 loadOasisCellValidation(body.toElement(), m_doc->map()->parser()); 1163 1164 KoXmlNode sheet = KoXml::namedItemNS(body, ooNS::table, "table"); 1165 1166 qDebug() << " sheet :" << sheet.isNull(); 1167 if (sheet.isNull()) 1168 return false; 1169 1170 while (!sheet.isNull()) { 1171 KoXmlElement t = sheet.toElement(); 1172 if (t.isNull()) { 1173 sheet = sheet.nextSibling(); 1174 continue; 1175 } 1176 if (t.nodeName() != "table:table") { 1177 sheet = sheet.nextSibling(); 1178 continue; 1179 } 1180 1181 SheetBase *table = m_doc->map()->addNewSheet(); 1182 1183 table->setSheetName(t.attributeNS(ooNS::table, "name", QString())); 1184 sheet = sheet.nextSibling(); 1185 } 1186 1187 sheet = body.firstChild(); 1188 1189 int step = (int)(80 / numOfTables); 1190 int progress = 15; 1191 1192 m_doc->map()->setDefaultColumnWidth(MM_TO_POINT(22.7)); 1193 m_doc->map()->setDefaultRowHeight(MM_TO_POINT(4.3)); 1194 qDebug() << "Global Height:" << MM_TO_POINT(4.3) << ", Global width:" << MM_TO_POINT(22.7); 1195 1196 while (!sheet.isNull()) { 1197 KoXmlElement t = sheet.toElement(); 1198 if (t.isNull()) { 1199 KMessageBox::sorry(0, i18n("The file seems to be corrupt. Skipping a table.")); 1200 sheet = sheet.nextSibling(); 1201 continue; 1202 } 1203 if (t.nodeName() != "table:table") { 1204 sheet = sheet.nextSibling(); 1205 continue; 1206 } 1207 1208 SheetBase *btable = m_doc->map()->findSheet(t.attributeNS(ooNS::table, "name", QString())); 1209 Sheet *table = dynamic_cast<Sheet *>(btable); 1210 if (!table) { 1211 KMessageBox::sorry(0, i18n("Skipping a table.")); 1212 sheet = sheet.nextSibling(); 1213 continue; 1214 } 1215 1216 Style * defaultStyle = m_defaultStyles[ "Default" ]; 1217 if (defaultStyle) { 1218 qDebug() << "Copy default style to default cell"; 1219 table->fullMap()->styleManager()->defaultStyle()->merge(*defaultStyle); 1220 } 1221 table->doc()->map()->setDefaultRowHeight(MM_TO_POINT(4.3)); 1222 table->doc()->map()->setDefaultColumnWidth(MM_TO_POINT(22.7)); 1223 1224 qDebug() << "Added table:" << t.attributeNS(ooNS::table, "name", QString()); 1225 1226 if (t.hasAttributeNS(ooNS::table, "style-name")) { 1227 QString style = t.attributeNS(ooNS::table, "style-name", QString()); 1228 KoXmlElement * tableStyle = m_styles[ style ]; 1229 1230 KoXmlNode node; 1231 1232 if (tableStyle) 1233 node = tableStyle->firstChild(); 1234 1235 while (!node.isNull()) { 1236 KoXmlElement property = node.toElement(); 1237 if (property.localName() == "properties" && property.namespaceURI() == ooNS::style) { 1238 if (property.hasAttributeNS(ooNS::table, "display")) { 1239 bool visible = (property.attributeNS(ooNS::table, "display", QString()) == "true" ? true : false); 1240 table->hideSheet(!visible); 1241 qDebug() << "Table:" << table->sheetName() << ", hidden:" << !visible; 1242 } 1243 } 1244 1245 node = node.nextSibling(); 1246 } 1247 1248 if (tableStyle && tableStyle->hasAttributeNS(ooNS::style, "master-page-name")) { 1249 QString stylename = "pm" + tableStyle->attributeNS(ooNS::style, "master-page-name", QString()); 1250 1251 loadTableMasterStyle(table, stylename); 1252 1253 } 1254 } 1255 if (t.hasAttributeNS(ooNS::table, "print-ranges")) { 1256 // e.g.: Sheet4.A1:Sheet4.E28 1257 QString range = t.attributeNS(ooNS::table, "print-ranges", QString()); 1258 OpenCalcPoint point(range, m_doc->map()); 1259 1260 qDebug() << "Print range:" << point.translation; 1261 const Calligra::Sheets::Region region = m_doc->map()->regionFromName(point.translation); 1262 1263 qDebug() << "Print table:" << region.firstSheet()->sheetName(); 1264 1265 if (table == region.firstSheet()) 1266 table->printSettings()->setPrintRegion(region); 1267 } 1268 1269 if (!readColLayouts(t, table)) 1270 return false; 1271 1272 if (!readRowsAndCells(t, table)) 1273 return false; 1274 1275 if (t.hasAttributeNS(ooNS::table, "protected")) { 1276 QByteArray passwd(""); 1277 if (t.hasAttributeNS(ooNS::table, "protection-key")) { 1278 QString p = t.attributeNS(ooNS::table, "protection-key", QString()); 1279 QByteArray str(p.toLatin1()); 1280 qDebug() << "Decoding password:" << str; 1281 passwd = KCodecs::base64Decode(str); 1282 } 1283 qDebug() << "Password hash: '" << passwd << "'"; 1284 table->setProtected(passwd); 1285 } 1286 1287 progress += step; 1288 emit sigProgress(progress); 1289 sheet = sheet.nextSibling(); 1290 } 1291 1292 KoXmlElement b = body.toElement(); 1293 if (b.hasAttributeNS(ooNS::table, "structure-protected")) { 1294 QByteArray passwd(""); 1295 if (b.hasAttributeNS(ooNS::table, "protection-key")) { 1296 QString p = b.attributeNS(ooNS::table, "protection-key", QString()); 1297 QByteArray str(p.toLatin1()); 1298 qDebug() << "Decoding password:" << str; 1299 passwd = KCodecs::base64Decode(str); 1300 } 1301 qDebug() << "Password hash: '" << passwd << "'"; 1302 1303 m_doc->map()->setProtected(passwd); 1304 } 1305 1306 emit sigProgress(98); 1307 1308 return true; 1309 } 1310 1311 void OpenCalcImport::insertStyles(KoXmlElement const & element) 1312 { 1313 if (element.isNull()) 1314 return; 1315 1316 KoXmlElement e; 1317 forEachElement(e, element) { 1318 if (e.isNull() || !e.hasAttributeNS(ooNS::style, "name")) { 1319 continue; 1320 } 1321 1322 QString name = e.attributeNS(ooNS::style, "name", QString()); 1323 qDebug() << "Style: '" << name << "' loaded"; 1324 m_styles.insert(name, new KoXmlElement(e)); 1325 } 1326 } 1327 1328 1329 void OpenCalcImport::loadOasisAreaName(const KoXmlElement&body) 1330 { 1331 KoXmlNode namedAreas = KoXml::namedItemNS(body, ooNS::table, "named-expressions"); 1332 if (!namedAreas.isNull()) { 1333 KoXmlElement e; 1334 forEachElement(e, namedAreas) { 1335 if (e.isNull() || !e.hasAttributeNS(ooNS::table, "name") || !e.hasAttributeNS(ooNS::table, "cell-range-address")) { 1336 qDebug() << "Reading in named area failed"; 1337 continue; 1338 } 1339 1340 // TODO: what is: table:base-cell-address 1341 QString name = e.attributeNS(ooNS::table, "name", QString()); 1342 QString areaPoint = e.attributeNS(ooNS::table, "cell-range-address", QString()); 1343 1344 m_namedAreas.append(name); 1345 qDebug() << "Reading in named area, name:" << name << ", area:" << areaPoint; 1346 1347 OpenCalcPoint point(areaPoint, m_doc->map()); 1348 qDebug() << "Area:" << point.translation; 1349 1350 const Calligra::Sheets::Region region = m_doc->map()->regionFromName(point.translation); 1351 1352 m_doc->map()->namedAreaManager()->insert(region, name); 1353 qDebug() << "Area range:" << region.name(); 1354 } 1355 } 1356 } 1357 1358 void OpenCalcImport::loadOasisCellValidation(const KoXmlElement&body, const ValueParser *parser) 1359 { 1360 Q_UNUSED(parser) 1361 KoXmlNode validation = KoXml::namedItemNS(body, ooNS::table, "content-validations"); 1362 if (!validation.isNull()) { 1363 KoXmlElement element; 1364 forEachElement(element, validation) { 1365 if (element.localName() == "content-validation") { 1366 m_validationList.insert(element.attributeNS(ooNS::table, "name", QString()), element); 1367 qDebug() << " validation found :" << element.attributeNS(ooNS::table, "name", QString()); 1368 } else { 1369 qDebug() << " Tag not recognize :" << element.tagName(); 1370 } 1371 } 1372 } 1373 } 1374 1375 1376 QString * OpenCalcImport::loadFormat(KoXmlElement * element, 1377 Format::Type & formatType, 1378 QString name) 1379 { 1380 if (!element) 1381 return 0; 1382 1383 int i; 1384 bool ok; 1385 1386 QString * format = 0; 1387 KoXmlElement e = element->firstChild().toElement(); 1388 int precision = 0; 1389 int leadingZ = 1; 1390 #ifdef __GNUC__ 1391 #warning (coverity) what is the purpose of this constant? 1392 #endif 1393 bool thousandsSep = false; 1394 bool negRed = false; 1395 1396 if (element->localName() == "time-style") 1397 formatType = Format::Custom; 1398 else if (element->localName() == "date-style") 1399 formatType = Format::Custom; 1400 else if (element->localName() == "percentage-style") 1401 formatType = Format::Custom; 1402 else if (element->localName() == "number-style") 1403 formatType = Format::Custom; 1404 else if (element->localName() == "currency-style") 1405 formatType = Format::Custom; 1406 else if (element->localName() == "boolean-style") 1407 formatType = Format::Custom; 1408 1409 if (!e.isNull()) 1410 format = new QString(); 1411 1412 // TODO (element): 1413 // number:automatic-order="true" 1414 // number:truncate-on-overflow="false" 1415 // style:volatile="true" 1416 1417 while (!e.isNull()) { 1418 if (e.localName() == "properties" && e.namespaceURI() == ooNS::style) { 1419 if (e.hasAttributeNS(ooNS::fo, "color")) 1420 negRed = true; // we only support red... 1421 } else if (e.localName() == "text" && e.namespaceURI() == ooNS::number) { 1422 if (negRed && (e.text() == "-")) 1423 ; 1424 else 1425 format->append(e.text()); 1426 } else if (e.localName() == "currency-symbol" && e.namespaceURI() == ooNS::number) { 1427 QString sym(e.text()); 1428 qDebug() << "Currency:" << sym; 1429 format->append(sym); 1430 // number:language="de" number:country="DE">€</number:currency-symbol> 1431 } else if (e.localName() == "day-of-week" && e.namespaceURI() == ooNS::number) { 1432 if (e.hasAttributeNS(ooNS::number, "style")) { 1433 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1434 format->append("dddd"); 1435 else 1436 format->append("ddd"); 1437 } else 1438 format->append("ddd"); 1439 } else if (e.localName() == "day" && e.namespaceURI() == ooNS::number) { 1440 if (e.hasAttributeNS(ooNS::number, "style")) { 1441 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1442 format->append("dd"); 1443 else 1444 format->append("d"); 1445 } else 1446 format->append("d"); 1447 } else if (e.localName() == "month" && e.namespaceURI() == ooNS::number) { 1448 if (e.hasAttributeNS(ooNS::number, "textual")) { 1449 if (e.attributeNS(ooNS::number, "textual", QString()) == "true") 1450 format->append("mm"); 1451 } 1452 1453 if (e.hasAttributeNS(ooNS::number, "style")) { 1454 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1455 format->append("mm"); 1456 else 1457 format->append("m"); 1458 } else 1459 format->append("m"); 1460 } else if (e.localName() == "year" && e.namespaceURI() == ooNS::number) { 1461 if (e.hasAttributeNS(ooNS::number, "style")) { 1462 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1463 format->append("yyyy"); 1464 else 1465 format->append("yy"); 1466 } else 1467 format->append("yy"); 1468 } else if (e.localName() == "hours" && e.namespaceURI() == ooNS::number) { 1469 if (e.hasAttributeNS(ooNS::number, "style")) { 1470 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1471 format->append("hh"); 1472 else 1473 format->append("h"); 1474 } else 1475 format->append("h"); 1476 } else if (e.localName() == "minutes" && e.namespaceURI() == ooNS::number) { 1477 if (e.hasAttributeNS(ooNS::number, "style")) { 1478 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1479 format->append("mm"); 1480 else 1481 format->append("m"); 1482 } else 1483 format->append("m"); 1484 } else if (e.localName() == "seconds" && e.namespaceURI() == ooNS::number) { 1485 if (e.hasAttributeNS(ooNS::number, "style")) { 1486 if (e.attributeNS(ooNS::number, "style", QString()) == "long") 1487 format->append("ss"); 1488 else 1489 format->append("s"); 1490 } else 1491 format->append("s"); 1492 } else if (e.localName() == "am-pm" && e.namespaceURI() == ooNS::number) { 1493 format->append("AM/PM"); 1494 } else if (e.localName() == "number" && e.namespaceURI() == ooNS::number) { 1495 // TODO: number:grouping="true" 1496 1497 if (e.hasAttributeNS(ooNS::number, "decimal-places")) { 1498 int d = e.attributeNS(ooNS::number, "decimal-places", QString()).toInt(&ok); 1499 if (ok) 1500 precision = d; 1501 } 1502 1503 if (e.hasAttributeNS(ooNS::number, "min-integer-digits")) { 1504 int d = e.attributeNS(ooNS::number, "min-integer-digits", QString()).toInt(&ok); 1505 if (ok) 1506 leadingZ = d; 1507 } 1508 1509 #ifdef __GNUC__ 1510 #warning thousandsSep can not be true (CID 3200) 1511 #endif 1512 if (thousandsSep && leadingZ <= 3) { 1513 format->append("#,"); 1514 for (i = leadingZ; i <= 3; ++i) 1515 format->append('#'); 1516 } 1517 1518 for (i = 1; i <= leadingZ; ++i) { 1519 format->append('0'); 1520 if ((i % 3 == 0) && thousandsSep) 1521 format->append(','); 1522 } 1523 1524 format->append('.'); 1525 for (i = 0; i < precision; ++i) 1526 format->append('0'); 1527 } else if (e.localName() == "scientific-number" && e.namespaceURI() == ooNS::number) { 1528 int exp = 2; 1529 1530 if (e.hasAttributeNS(ooNS::number, "decimal-places")) { 1531 int d = e.attributeNS(ooNS::number, "decimal-places", QString()).toInt(&ok); 1532 if (ok) 1533 precision = d; 1534 } 1535 1536 if (e.hasAttributeNS(ooNS::number, "min-integer-digits")) { 1537 int d = e.attributeNS(ooNS::number, "min-integer-digits", QString()).toInt(&ok); 1538 if (ok) 1539 leadingZ = d; 1540 } 1541 1542 if (e.hasAttributeNS(ooNS::number, "min-exponent-digits")) { 1543 int d = e.attributeNS(ooNS::number, "min-exponent-digits", QString()).toInt(&ok); 1544 if (ok) 1545 exp = d; 1546 if (exp <= 0) 1547 exp = 1; 1548 } 1549 1550 if (thousandsSep && leadingZ <= 3) { 1551 format->append("#,"); 1552 for (i = leadingZ; i <= 3; ++i) 1553 format->append('#'); 1554 } 1555 1556 for (i = 1; i <= leadingZ; ++i) { 1557 format->append('0'); 1558 if ((i % 3 == 0) && thousandsSep) 1559 format->append(','); 1560 } 1561 1562 format->append('.'); 1563 for (i = 0; i < precision; ++i) 1564 format->append('0'); 1565 1566 format->append("E+"); 1567 for (i = 0; i < exp; ++i) 1568 format->append('0'); 1569 1570 formatType = Format::Custom; 1571 } else if (e.localName() == "fraction" && e.namespaceURI() == ooNS::number) { 1572 int integer = 0; 1573 int numerator = 1; 1574 int denominator = 1; 1575 1576 if (e.hasAttributeNS(ooNS::number, "min-integer-digits")) { 1577 int d = e.attributeNS(ooNS::number, "min-integer-digits", QString()).toInt(&ok); 1578 if (ok) 1579 integer = d; 1580 } 1581 if (e.hasAttributeNS(ooNS::number, "min-numerator-digits")) { 1582 int d = e.attributeNS(ooNS::number, "min-numerator-digits", QString()).toInt(&ok); 1583 if (ok) 1584 numerator = d; 1585 } 1586 if (e.hasAttributeNS(ooNS::number, "min-denominator-digits")) { 1587 int d = e.attributeNS(ooNS::number, "min-denominator-digits", QString()).toInt(&ok); 1588 if (ok) 1589 denominator = d; 1590 } 1591 1592 for (i = 0; i <= integer; ++i) 1593 format->append('#'); 1594 1595 format->append(' '); 1596 1597 for (i = 0; i <= numerator; ++i) 1598 format->append('?'); 1599 1600 format->append('/'); 1601 1602 for (i = 0; i <= denominator; ++i) 1603 format->append('?'); 1604 } 1605 // Not needed: 1606 // <style:map style:condition="value()>=0" style:apply-style-name="N106P0"/> 1607 // we handle painting negative numbers in red differently 1608 1609 e = e.nextSibling().toElement(); 1610 } 1611 1612 if (negRed) { 1613 QString f(*format); 1614 format->append(";[Red]"); 1615 format->append(f); 1616 } 1617 1618 qDebug() << "*** New FormatString:" << *format; 1619 1620 m_formats.insert(name, format); 1621 1622 return format; 1623 } 1624 1625 void OpenCalcImport::loadFontStyle(Style * layout, KoXmlElement const * font) const 1626 { 1627 if (!font || !layout) 1628 return; 1629 1630 qDebug() << "Copy font style from the layout" << font->tagName() << "," << font->nodeName(); 1631 1632 if (font->hasAttributeNS(ooNS::fo, "font-family")) 1633 layout->setFontFamily(font->attributeNS(ooNS::fo, "font-family", QString())); 1634 if (font->hasAttributeNS(ooNS::fo, "color")) 1635 layout->setFontColor(QColor(font->attributeNS(ooNS::fo, "color", QString()))); 1636 if (font->hasAttributeNS(ooNS::fo, "font-size")) 1637 layout->setFontSize(int(KoUnit::parseValue(font->attributeNS(ooNS::fo, "font-size", QString()), 10))); 1638 else 1639 layout->setFontSize(10); 1640 if (font->hasAttributeNS(ooNS::fo, "font-style")) { 1641 qDebug() << "italic"; 1642 layout->setFontItalic(true); // only thing we support 1643 } 1644 if (font->hasAttributeNS(ooNS::fo, "font-weight")) 1645 layout->setFontBold(true); // only thing we support 1646 if (font->hasAttributeNS(ooNS::fo, "text-underline") || font->hasAttributeNS(ooNS::style, "text-underline")) 1647 layout->setFontUnderline(true); // only thing we support 1648 if (font->hasAttributeNS(ooNS::style, "text-crossing-out")) 1649 layout->setFontStrikeOut(true); // only thing we support 1650 if (font->hasAttributeNS(ooNS::style, "font-pitch")) { 1651 // TODO: possible values: fixed, variable 1652 } 1653 // TODO: 1654 // text-underline-color 1655 } 1656 1657 void OpenCalcImport::loadBorder(Style * layout, QString const & borderDef, bPos pos) const 1658 { 1659 if (borderDef == "none") 1660 return; 1661 1662 int p = borderDef.indexOf(' '); 1663 if (p < 0) 1664 return; 1665 1666 QPen pen; 1667 QString w = borderDef.left(p); 1668 pen.setWidth((int) KoUnit::parseValue(w)); 1669 1670 1671 ++p; 1672 int p2 = borderDef.indexOf(' ', p); 1673 QString s = borderDef.mid(p, p2 - p); 1674 1675 qDebug() << "Borderstyle:" << s; 1676 1677 if (s == "solid" || s == "double") 1678 pen.setStyle(Qt::SolidLine); 1679 else { 1680 #if 0 1681 // TODO: not supported by oocalc 1682 pen.setStyle(Qt::DashLine); 1683 pen.setStyle(Qt::DotLine); 1684 pen.setStyle(Qt::DashDotLine); 1685 pen.setStyle(Qt::DashDotDotLine); 1686 #endif 1687 pen.setStyle(Qt::SolidLine); //default. 1688 } 1689 1690 ++p2; 1691 p = borderDef.indexOf(' ', p2); 1692 if (p == -1) 1693 p = borderDef.length(); 1694 1695 pen.setColor(QColor(borderDef.right(p - p2))); 1696 1697 if (pos == Left) 1698 layout->setLeftBorderPen(pen); 1699 else if (pos == Top) 1700 layout->setTopBorderPen(pen); 1701 else if (pos == Right) 1702 layout->setRightBorderPen(pen); 1703 else if (pos == Bottom) 1704 layout->setBottomBorderPen(pen); 1705 else if (pos == Border) { 1706 layout->setLeftBorderPen(pen); 1707 layout->setTopBorderPen(pen); 1708 layout->setRightBorderPen(pen); 1709 layout->setBottomBorderPen(pen); 1710 } 1711 // TODO Diagonals not supported by oocalc 1712 } 1713 1714 void OpenCalcImport::loadStyleProperties(Style * layout, KoXmlElement const & property) const 1715 { 1716 qDebug() << "*** Loading style properties *****"; 1717 1718 if (property.hasAttributeNS(ooNS::style, "decimal-places")) { 1719 bool ok = false; 1720 int p = property.attributeNS(ooNS::style, "decimal-places", QString()).toInt(&ok); 1721 if (ok) 1722 layout->setPrecision(p); 1723 } 1724 1725 if (property.hasAttributeNS(ooNS::style, "font-name")) { 1726 KoXmlElement * font = m_styles[ property.attributeNS(ooNS::style, "font-name", QString())]; 1727 loadFontStyle(layout, font); // general font style 1728 } 1729 1730 loadFontStyle(layout, &property); // specific font style 1731 1732 // TODO: 1733 // diagonal: fall + goup 1734 // fo:direction="ltr" 1735 // style:text-align-source ("fix") 1736 // style:shadow 1737 // style:text-outline 1738 // indents from right, top, bottom 1739 // style:condition="cell-content()=15" 1740 // => style:apply-style-name="Result" style:base-cell-address="Sheet6.A5"/> 1741 1742 if (property.hasAttributeNS(ooNS::style, "rotation-angle")) { 1743 bool ok = false; 1744 int a = property.attributeNS(ooNS::style, "rotation-angle", QString()).toInt(&ok); 1745 if (ok) 1746 layout->setAngle(-a + 1); 1747 } 1748 1749 if (property.hasAttributeNS(ooNS::fo, "direction")) { 1750 layout->setVerticalText(true); 1751 } 1752 if (property.hasAttributeNS(ooNS::fo, "text-align")) { 1753 QString s = property.attributeNS(ooNS::fo, "text-align", QString()); 1754 if (s == "center") 1755 layout->setHAlign(Style::Center); 1756 else if (s == "end") 1757 layout->setHAlign(Style::Right); 1758 else if (s == "start") 1759 layout->setHAlign(Style::Left); 1760 else if (s == "justify") // TODO in KSpread! 1761 layout->setHAlign(Style::Center); 1762 } 1763 if (property.hasAttributeNS(ooNS::fo, "margin-left")) { 1764 qDebug() << "margin-left :" << KoUnit::parseValue(property.attributeNS(ooNS::fo, "margin-left", QString()), 0.0); 1765 layout->setIndentation(KoUnit::parseValue(property.attributeNS(ooNS::fo, "margin-left", QString()), 0.0)); 1766 } 1767 if (property.hasAttributeNS(ooNS::fo, "background-color")) 1768 layout->setBackgroundColor(QColor(property.attributeNS(ooNS::fo, "background-color", QString()))); 1769 1770 if (property.hasAttributeNS(ooNS::style, "print-content")) { 1771 if (property.attributeNS(ooNS::style, "print-content", QString()) == "false") 1772 layout->setDontPrintText(false); 1773 } 1774 if (property.hasAttributeNS(ooNS::style, "cell-protect")) { 1775 QString prot(property.attributeNS(ooNS::style, "cell-protect", QString())); 1776 if (prot == "none") { 1777 layout->setNotProtected(true); 1778 layout->setHideFormula(false); 1779 layout->setHideAll(false); 1780 } else if (prot == "formula-hidden") { 1781 layout->setNotProtected(true); 1782 layout->setHideFormula(true); 1783 layout->setHideAll(false); 1784 } else if (prot == "protected formula-hidden") { 1785 layout->setNotProtected(false); 1786 layout->setHideFormula(true); 1787 layout->setHideAll(false); 1788 } else if (prot == "hidden-and-protected") { 1789 layout->setNotProtected(false); 1790 layout->setHideFormula(false); 1791 layout->setHideAll(true); 1792 } else if (prot == "protected") { 1793 layout->setNotProtected(false); 1794 layout->setHideFormula(false); 1795 layout->setHideAll(false); 1796 } 1797 qDebug() << "Cell" << prot; 1798 } 1799 1800 if (property.hasAttributeNS(ooNS::fo, "padding-left")) 1801 layout->setIndentation(KoUnit::parseValue(property.attributeNS(ooNS::fo, "padding-left", QString()))); 1802 1803 if (property.hasAttributeNS(ooNS::fo, "vertical-align")) { 1804 QString s = property.attributeNS(ooNS::fo, "vertical-align", QString()); 1805 if (s == "middle") 1806 layout->setVAlign(Style::Middle); 1807 else if (s == "bottom") 1808 layout->setVAlign(Style::Bottom); 1809 else 1810 layout->setVAlign(Style::Top); 1811 } else 1812 layout->setVAlign(Style::Bottom); 1813 1814 if (property.hasAttributeNS(ooNS::fo, "wrap-option")) { 1815 layout->setWrapText(true); 1816 1817 /* we do not support anything else yet 1818 QString s = property.attributeNS( ooNS::fo, "wrap-option", QString() ); 1819 if ( s == "wrap" ) 1820 layout->setMultiRow( true ); 1821 */ 1822 } 1823 1824 if (property.hasAttributeNS(ooNS::fo, "border-bottom")) { 1825 loadBorder(layout, property.attributeNS(ooNS::fo, "border-bottom", QString()), Bottom); 1826 // TODO: style:border-line-width-bottom if double! 1827 } 1828 1829 if (property.hasAttributeNS(ooNS::fo, "border-right")) { 1830 loadBorder(layout, property.attributeNS(ooNS::fo, "border-right", QString()), Right); 1831 // TODO: style:border-line-width-right 1832 } 1833 1834 if (property.hasAttributeNS(ooNS::fo, "border-top")) { 1835 loadBorder(layout, property.attributeNS(ooNS::fo, "border-top", QString()), Top); 1836 // TODO: style:border-line-width-top 1837 } 1838 1839 if (property.hasAttributeNS(ooNS::fo, "border-left")) { 1840 loadBorder(layout, property.attributeNS(ooNS::fo, "border-left", QString()), Left); 1841 // TODO: style:border-line-width-left 1842 } 1843 1844 if (property.hasAttributeNS(ooNS::fo, "border")) { 1845 loadBorder(layout, property.attributeNS(ooNS::fo, "border", QString()), Border); 1846 // TODO: style:border-line-width-left 1847 } 1848 } 1849 1850 void OpenCalcImport::readInStyle(Style * layout, KoXmlElement const & style) 1851 { 1852 qDebug() << "** Reading Style:" << style.tagName() << ";" << style.attributeNS(ooNS::style, "name", QString()); 1853 if (style.localName() == "style" && style.namespaceURI() == ooNS::style) { 1854 if (style.hasAttributeNS(ooNS::style, "parent-style-name")) { 1855 Style * cp 1856 = m_defaultStyles.value(style.attributeNS(ooNS::style, "parent-style-name", QString())); 1857 qDebug() << "Copying layout from" << style.attributeNS(ooNS::style, "parent-style-name", QString()); 1858 1859 if (cp != 0) 1860 layout = cp; 1861 } else if (style.hasAttributeNS(ooNS::style, "family")) { 1862 QString name = style.attribute("style-family") + "default"; 1863 Style * cp = m_defaultStyles.value(name); 1864 1865 qDebug() << "Copying layout from" << name << "," << !cp; 1866 1867 if (cp != 0) 1868 layout = cp; 1869 } 1870 1871 if (style.hasAttributeNS(ooNS::style, "data-style-name")) { 1872 QString * format = m_formats[ style.attributeNS(ooNS::style, "data-style-name", QString())]; 1873 Format::Type formatType = Format::Generic; 1874 1875 if (!format) { 1876 // load and convert it 1877 QString name(style.attributeNS(ooNS::style, "data-style-name", QString())); 1878 format = loadFormat(m_styles[ name ], formatType, name); 1879 } 1880 1881 if (format) { 1882 layout->setCustomFormat(*format); 1883 layout->setFormatType(formatType); 1884 } 1885 1886 // <number:currency-symbol number:language="de" number:country="DE">€</number:currency-symbol> 1887 } 1888 } 1889 1890 KoXmlElement property; 1891 forEachElement(property, style) { 1892 if (property.localName() == "properties" && property.namespaceURI() == ooNS::style) 1893 loadStyleProperties(layout, property); 1894 1895 qDebug() << layout->fontFamily(); 1896 } 1897 } 1898 1899 bool OpenCalcImport::createStyleMap(KoXmlDocument const & styles) 1900 { 1901 KoXmlElement content = styles.documentElement(); 1902 KoXmlNode docStyles = KoXml::namedItemNS(content, ooNS::office, "document-styles"); 1903 1904 if (content.hasAttributeNS(ooNS::office, "version")) { 1905 bool ok = true; 1906 double d = content.attributeNS(ooNS::office, "version", QString()).toDouble(&ok); 1907 1908 if (ok) { 1909 qDebug() << "OpenCalc version:" << d; 1910 if (d > 1.0) { 1911 QString message(i18n("This document was created with OpenOffice.org version '%1'. This filter was written for version 1.0. Reading this file could cause strange behavior, crashes or incorrect display of the data. Do you want to continue converting the document?", content.attributeNS(ooNS::office, "version", QString()))); 1912 if (KMessageBox::warningYesNo(0, message, i18n("Unsupported document version")) == KMessageBox::No) 1913 return false; 1914 } 1915 } 1916 } 1917 1918 KoXmlNode fontStyles = KoXml::namedItemNS(content, ooNS::office, "font-decls"); 1919 1920 if (!fontStyles.isNull()) { 1921 qDebug() << "Starting reading in font-decl..."; 1922 1923 insertStyles(fontStyles.toElement()); 1924 } else 1925 qDebug() << "No items found"; 1926 1927 qDebug() << "Starting reading in auto:styles"; 1928 1929 KoXmlNode autoStyles = KoXml::namedItemNS(content, ooNS::office, "automatic-styles"); 1930 if (!autoStyles.isNull()) 1931 insertStyles(autoStyles.toElement()); 1932 else 1933 qDebug() << "No items found"; 1934 1935 1936 qDebug() << "Reading in master styles"; 1937 1938 KoXmlNode masterStyles = KoXml::namedItemNS(content, ooNS::office, "master-styles"); 1939 1940 if (masterStyles.isNull()) { 1941 qDebug() << "Nothing found"; 1942 } 1943 1944 KoXmlElement master = KoXml::namedItemNS(masterStyles, ooNS::style, "master-page"); 1945 if (!master.isNull()) { 1946 QString name("pm"); 1947 name += master.attributeNS(ooNS::style, "name", QString()); 1948 qDebug() << "Master style: '" << name << "' loaded"; 1949 m_styles.insert(name, new KoXmlElement(master)); 1950 1951 master = master.nextSibling().toElement(); 1952 } 1953 1954 1955 qDebug() << "Starting reading in office:styles"; 1956 1957 KoXmlNode fixedStyles = KoXml::namedItemNS(content, ooNS::office, "styles"); 1958 1959 qDebug() << "Reading in default styles"; 1960 1961 KoXmlNode def = KoXml::namedItemNS(fixedStyles, ooNS::style, "default-style"); 1962 qDebug() << " def !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! :" << def.isNull(); 1963 while (!def.isNull()) { 1964 KoXmlElement e = def.toElement(); 1965 qDebug() << "Style found" << e.nodeName() << ", tag:" << e.tagName(); 1966 1967 if (e.nodeName() != "style:default-style") { 1968 def = def.nextSibling(); 1969 continue; 1970 } 1971 1972 if (!e.isNull()) { 1973 Style * layout = new Style(); 1974 1975 readInStyle(layout, e); 1976 qDebug() << "Default style" << e.attributeNS(ooNS::style, "family", QString()) << "default" << " loaded"; 1977 1978 m_defaultStyles.insert(e.attributeNS(ooNS::style, "family", QString()) + "default", layout); 1979 // QFont font = layout->font(); 1980 // qDebug() <<"Font:" << font.family() <<"," << font.toString(); 1981 } 1982 1983 def = def.nextSibling(); 1984 } 1985 1986 KoXmlElement defs = KoXml::namedItemNS(fixedStyles, ooNS::style, "style"); 1987 while (!defs.isNull()) { 1988 if (defs.nodeName() != "style:style") 1989 break; // done 1990 1991 if (!defs.hasAttributeNS(ooNS::style, "name")) { 1992 // ups... 1993 defs = defs.nextSibling().toElement(); 1994 continue; 1995 } 1996 1997 Style * layout = new Style(); 1998 readInStyle(layout, defs); 1999 qDebug() << "Default style" << defs.attributeNS(ooNS::style, "name", QString()) << " loaded"; 2000 2001 m_defaultStyles.insert(defs.attributeNS(ooNS::style, "name", QString()), layout); 2002 // qDebug() <<"Font:" << layout->font().family() <<"," << layout->font().toString(); 2003 2004 defs = defs.nextSibling().toElement(); 2005 } 2006 2007 if (!fixedStyles.isNull()) 2008 insertStyles(fixedStyles.toElement()); 2009 2010 qDebug() << "Starting reading in automatic styles"; 2011 2012 content = m_content.documentElement(); 2013 autoStyles = KoXml::namedItemNS(content, ooNS::office, "automatic-styles"); 2014 2015 if (!autoStyles.isNull()) 2016 insertStyles(autoStyles.toElement()); 2017 2018 fontStyles = KoXml::namedItemNS(content, ooNS::office, "font-decls"); 2019 2020 if (!fontStyles.isNull()) { 2021 qDebug() << "Starting reading in special font decl"; 2022 2023 insertStyles(fontStyles.toElement()); 2024 } 2025 2026 qDebug() << "Styles read in."; 2027 2028 return true; 2029 } 2030 2031 void OpenCalcImport::loadOasisValidation(Validity validity, const QString& validationName, const ValueParser *parser) 2032 { 2033 qDebug() << "validationName:" << validationName; 2034 KoXmlElement element = m_validationList[validationName]; 2035 if (element.hasAttributeNS(ooNS::table, "condition")) { 2036 QString valExpression = element.attributeNS(ooNS::table, "condition", QString()); 2037 qDebug() << " element.attribute( table:condition )" << valExpression; 2038 //Condition ::= ExtendedTrueCondition | TrueFunction 'and' TrueCondition 2039 //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time() 2040 //ExtendedTrueCondition ::= ExtendedGetFunction | cell-content-text-length() Operator Value 2041 //TrueCondition ::= GetFunction | cell-content() Operator Value 2042 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value) 2043 //ExtendedGetFunction ::= cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) 2044 //Operator ::= '<' | '>' | '<=' | '>=' | '=' | '!=' 2045 //Value ::= NumberValue | String | Formula 2046 //A Formula is a formula without an equals (=) sign at the beginning. See section 8.1.3 for more information. 2047 //A String comprises one or more characters surrounded by quotation marks. 2048 //A NumberValue is a whole or decimal number. It must not contain comma separators for numbers of 1000 or greater. 2049 2050 //ExtendedTrueCondition 2051 if (valExpression.contains("cell-content-text-length()")) { 2052 //"cell-content-text-length()>45" 2053 valExpression.remove("cell-content-text-length()"); 2054 qDebug() << " valExpression = :" << valExpression; 2055 validity.setRestriction(Validity::TextLength); 2056 2057 loadOasisValidationCondition(validity, valExpression, parser); 2058 } 2059 //cell-content-text-length-is-between(Value, Value) | cell-content-text-length-is-not-between(Value, Value) 2060 else if (valExpression.contains("cell-content-text-length-is-between")) { 2061 validity.setRestriction(Validity::TextLength); 2062 validity.setCondition(Validity::Between); 2063 valExpression.remove("cell-content-text-length-is-between("); 2064 qDebug() << " valExpression :" << valExpression; 2065 valExpression.remove(')'); 2066 QStringList listVal = valExpression.split(','); 2067 loadOasisValidationValue(validity, listVal, parser); 2068 } else if (valExpression.contains("cell-content-text-length-is-not-between")) { 2069 validity.setRestriction(Validity::TextLength); 2070 validity.setCondition(Validity::Different); 2071 valExpression.remove("cell-content-text-length-is-not-between("); 2072 qDebug() << " valExpression :" << valExpression; 2073 valExpression.remove(')'); 2074 qDebug() << " valExpression :" << valExpression; 2075 QStringList listVal = valExpression.split(','); 2076 loadOasisValidationValue(validity, listVal, parser); 2077 2078 } 2079 //TrueFunction ::= cell-content-is-whole-number() | cell-content-is-decimal-number() | cell-content-is-date() | cell-content-is-time() 2080 else { 2081 if (valExpression.contains("cell-content-is-whole-number()")) { 2082 validity.setRestriction(Validity::Number); 2083 valExpression.remove("cell-content-is-whole-number() and "); 2084 } else if (valExpression.contains("cell-content-is-decimal-number()")) { 2085 validity.setRestriction(Validity::Integer); 2086 valExpression.remove("cell-content-is-decimal-number() and "); 2087 } else if (valExpression.contains("cell-content-is-date()")) { 2088 validity.setRestriction(Validity::Date); 2089 valExpression.remove("cell-content-is-date() and "); 2090 } else if (valExpression.contains("cell-content-is-time()")) { 2091 validity.setRestriction(Validity::Time); 2092 valExpression.remove("cell-content-is-time() and "); 2093 } 2094 qDebug() << "valExpression :" << valExpression; 2095 2096 if (valExpression.contains("cell-content()")) { 2097 valExpression.remove("cell-content()"); 2098 loadOasisValidationCondition(validity, valExpression, parser); 2099 } 2100 //GetFunction ::= cell-content-is-between(Value, Value) | cell-content-is-not-between(Value, Value) 2101 //for the moment we support just int/double value, not text/date/time :( 2102 if (valExpression.contains("cell-content-is-between(")) { 2103 valExpression.remove("cell-content-is-between("); 2104 valExpression.remove(')'); 2105 QStringList listVal = valExpression.split(','); 2106 loadOasisValidationValue(validity, listVal, parser); 2107 2108 validity.setCondition(Validity::Between); 2109 } 2110 if (valExpression.contains("cell-content-is-not-between(")) { 2111 valExpression.remove("cell-content-is-not-between("); 2112 valExpression.remove(')'); 2113 QStringList listVal = valExpression.split(','); 2114 loadOasisValidationValue(validity, listVal, parser); 2115 validity.setCondition(Validity::Different); 2116 } 2117 } 2118 } 2119 if (element.hasAttributeNS(ooNS::table, "allow-empty-cell")) { 2120 validity.setAllowEmptyCell(((element.attributeNS(ooNS::table, "allow-empty-cell", QString()) == "true") ? true : false)); 2121 2122 } 2123 if (element.hasAttributeNS(ooNS::table, "base-cell-address")) { 2124 //todo what is it ? 2125 } 2126 2127 KoXmlElement help = KoXml::namedItemNS(element, ooNS::table, "help-message"); 2128 if (!help.isNull()) { 2129 if (help.hasAttributeNS(ooNS::table, "title")) 2130 validity.setTitleInfo(help.attributeNS(ooNS::table, "title", QString())); 2131 if (help.hasAttributeNS(ooNS::table, "display")) 2132 validity.setDisplayValidationInformation(((help.attributeNS(ooNS::table, "display", QString()) == "true") ? true : false)); 2133 KoXmlElement attrText = KoXml::namedItemNS(help, ooNS::text, "p"); 2134 if (!attrText.isNull()) 2135 validity.setMessageInfo(attrText.text()); 2136 } 2137 2138 KoXmlElement error = KoXml::namedItemNS(element, ooNS::table, "error-message"); 2139 if (!error.isNull()) { 2140 if (error.hasAttributeNS(ooNS::table, "title")) 2141 validity.setTitle(error.attributeNS(ooNS::table, "title", QString())); 2142 if (error.hasAttributeNS(ooNS::table, "message-type")) { 2143 QString str = error.attributeNS(ooNS::table, "message-type", QString()); 2144 if (str == "warning") 2145 validity.setAction(Validity::Warning); 2146 else if (str == "information") 2147 validity.setAction(Validity::Information); 2148 else if (str == "stop") 2149 validity.setAction(Validity::Stop); 2150 else 2151 qDebug() << "validation : message type unknown :" << str; 2152 } 2153 2154 if (error.hasAttributeNS(ooNS::table, "display")) { 2155 qDebug() << " display message :" << error.attributeNS(ooNS::table, "display", QString()); 2156 validity.setDisplayMessage((error.attributeNS(ooNS::table, "display", QString()) == "true")); 2157 } 2158 KoXmlElement attrText = KoXml::namedItemNS(error, ooNS::text, "p"); 2159 if (!attrText.isNull()) 2160 validity.setMessage(attrText.text()); 2161 } 2162 } 2163 2164 void OpenCalcImport::loadOasisValidationValue(Validity validity, const QStringList &listVal, const ValueParser *parser) 2165 { 2166 qDebug() << " listVal[0] :" << listVal[0] << " listVal[1] :" << listVal[1]; 2167 2168 validity.setMinimumValue(parser->parse(listVal[0])); 2169 validity.setMaximumValue(parser->parse(listVal[1])); 2170 } 2171 2172 2173 void OpenCalcImport::loadOasisValidationCondition(Validity validity, QString &valExpression, const ValueParser *parser) 2174 { 2175 QString value; 2176 if (valExpression.contains("<=")) { 2177 value = valExpression.remove("<="); 2178 validity.setCondition(Validity::InferiorEqual); 2179 } else if (valExpression.contains(">=")) { 2180 value = valExpression.remove(">="); 2181 validity.setCondition(Validity::SuperiorEqual); 2182 } else if (valExpression.contains("!=")) { 2183 //add Differentto attribute 2184 value = valExpression.remove("!="); 2185 validity.setCondition(Validity::DifferentTo); 2186 } else if (valExpression.contains('<')) { 2187 value = valExpression.remove('<'); 2188 validity.setCondition(Validity::Inferior); 2189 } else if (valExpression.contains('>')) { 2190 value = valExpression.remove('>'); 2191 validity.setCondition(Validity::Superior); 2192 } else if (valExpression.contains('=')) { 2193 value = valExpression.remove('='); 2194 validity.setCondition(Validity::Equal); 2195 } else 2196 qDebug() << " I don't know how to parse it :" << valExpression; 2197 2198 qDebug() << " value :" << value; 2199 validity.setMinimumValue(parser->parse(value)); 2200 } 2201 2202 2203 int OpenCalcImport::readMetaData() 2204 { 2205 int result = 5; 2206 KoDocumentInfo * docInfo = m_doc->documentInfo(); 2207 2208 KoXmlNode meta = KoXml::namedItemNS(m_meta, ooNS::office, "document-meta"); 2209 KoXmlNode office = KoXml::namedItemNS(meta, ooNS::office, "meta"); 2210 2211 if (office.isNull()) 2212 return 2; 2213 2214 KoXmlElement e = KoXml::namedItemNS(office, ooNS::dc, "creator"); 2215 if (!e.isNull() && !e.text().isEmpty()) 2216 docInfo->setAuthorInfo("creator", e.text()); 2217 2218 e = KoXml::namedItemNS(office, ooNS::dc, "title"); 2219 if (!e.isNull() && !e.text().isEmpty()) 2220 docInfo->setAboutInfo("title", e.text()); 2221 2222 e = KoXml::namedItemNS(office, ooNS::dc, "description"); 2223 if (!e.isNull() && !e.text().isEmpty()) 2224 docInfo->setAboutInfo("description", e.text()); // ### was: abstract 2225 2226 e = KoXml::namedItemNS(office, ooNS::dc, "subject"); 2227 if (!e.isNull() && !e.text().isEmpty()) 2228 docInfo->setAboutInfo("subject", e.text()); 2229 2230 e = KoXml::namedItemNS(office, ooNS::meta, "keywords"); 2231 if (!e.isNull()) { 2232 e = KoXml::namedItemNS(e, ooNS::meta, "keyword"); 2233 if (!e.isNull() && !e.text().isEmpty()) 2234 docInfo->setAboutInfo("keyword", e.text()); 2235 } 2236 2237 e = KoXml::namedItemNS(office, ooNS::meta, "document-statistic"); 2238 if (!e.isNull() && e.hasAttributeNS(ooNS::meta, "table-count")) { 2239 bool ok = false; 2240 result = e.attributeNS(ooNS::meta, "table-count", QString()).toInt(&ok); 2241 if (!ok) 2242 result = 5; 2243 } 2244 2245 m_meta.clear(); // not needed anymore 2246 2247 return result; 2248 } 2249 2250 KoFilter::ConversionStatus OpenCalcImport::convert(QByteArray const & from, QByteArray const & to) 2251 { 2252 qDebug() << "Entering OpenCalc Import filter:" << from << " -" << to; 2253 2254 KoDocument * document = m_chain->outputDocument(); 2255 if (!document) 2256 return KoFilter::StupidError; 2257 2258 if (!qobject_cast<const Calligra::Sheets::DocBase *>(document)) { // it's safer that way :) 2259 qWarning() << "document isn't a Calligra::Sheets::DocBase but a " << document->metaObject()->className(); 2260 return KoFilter::NotImplemented; 2261 } 2262 2263 if ((from != "application/vnd.sun.xml.calc" && from != "application/vnd.sun.xml.calc.template") || to != "application/x-kspread") { 2264 qWarning() << "Invalid mimetypes " << from << " " << to; 2265 return KoFilter::NotImplemented; 2266 } 2267 2268 m_doc = (DocBase *) document; 2269 2270 if (m_doc->mimeType() != "application/x-kspread") { 2271 qWarning() << "Invalid document mimetype " << m_doc->mimeType(); 2272 return KoFilter::NotImplemented; 2273 } 2274 2275 qDebug() << "Opening file"; 2276 2277 KoFilter::ConversionStatus preStatus = openFile(); 2278 2279 if (preStatus != KoFilter::OK) 2280 return preStatus; 2281 2282 emit sigProgress(13); 2283 int tables = readMetaData(); 2284 2285 emit sigProgress(15); 2286 2287 if (!parseBody(tables)) 2288 return KoFilter::StupidError; 2289 2290 emit sigProgress(100); 2291 return KoFilter::OK; 2292 } 2293 2294 KoFilter::ConversionStatus OpenCalcImport::openFile() 2295 { 2296 KoStore * store = KoStore::createStore(m_chain->inputFile(), KoStore::Read); 2297 2298 qDebug() << "Store created"; 2299 2300 if (!store) { 2301 qWarning() << "Couldn't open the requested file."; 2302 return KoFilter::FileNotFound; 2303 } 2304 2305 qDebug() << "Trying to open content.xml"; 2306 loadAndParse(m_content, "content.xml", store); 2307 qDebug() << "Opened"; 2308 2309 KoXmlDocument styles; 2310 qDebug() << "file content.xml loaded"; 2311 2312 loadAndParse(styles, "styles.xml", store); 2313 2314 loadAndParse(m_meta, "meta.xml", store); 2315 loadAndParse(m_settings, "settings.xml", store); 2316 2317 delete store; 2318 2319 emit sigProgress(10); 2320 2321 if (!createStyleMap(styles)) 2322 return KoFilter::UserCancelled; 2323 2324 return KoFilter::OK; 2325 } 2326 2327 KoFilter::ConversionStatus OpenCalcImport::loadAndParse(KoXmlDocument& doc, const QString& fileName, KoStore *m_store) 2328 { 2329 return OoUtils::loadAndParse(fileName, doc, m_store); 2330 } 2331 2332 #include "opencalcimport.moc" 2333