1 /* This file is part of the KDE project
2 Copyright (C) 2003-2006 Ariya Hidayat <ariya@kde.org>
3 Copyright (C) 2006 Marijn Kruisselbrink <mkruisselbrink@kde.org>
4 Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
5 Contact: Manikandaprasad Chandrasekar <manikandaprasad.chandrasekar@nokia.com>
6 Copyright (c) 2010 Carlos Licea <carlos@kdab.com>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "ExcelImport.h"
25
26 #include <QString>
27 #include <QDate>
28 #include <QBuffer>
29 #include <QFontMetricsF>
30 #include <QPair>
31 #include <QTextCursor>
32
33 #include <KoFilterChain.h>
34 #include <kpluginfactory.h>
35 #include <klocale.h>
36
37 #include <KoXmlWriter.h>
38 #include <KoGenStyles.h>
39 #include <KoGenStyle.h>
40 #include <KoXmlNS.h>
41 #include <KoShapeLoadingContext.h>
42 #include <KoShapeRegistry.h>
43 #include <KoOdfStylesReader.h>
44 #include <KoOdfLoadingContext.h>
45 #include <KoShape.h>
46 #include <KoDocumentInfo.h>
47 #include <KoTextDocument.h>
48
49 #include <DocBase.h>
50 #include <sheets/Sheet.h>
51 #include <sheets/Condition.h>
52 #include <sheets/odf/OdfLoadingContext.h>
53 #include <CalculationSettings.h>
54 #include <CellStorage.h>
55 #include <HeaderFooter.h>
56 #include <LoadingInfo.h>
57 #include <Map.h>
58 #include <NamedAreaManager.h>
59 #include <RowColumnFormat.h>
60 #include <RowFormatStorage.h>
61 #include <Sheet.h>
62 #include <SheetPrint.h>
63 #include <Style.h>
64 #include <StyleManager.h>
65 #include <StyleStorage.h>
66 #include <ValueConverter.h>
67 #include <ShapeApplicationData.h>
68 #include <Util.h>
69 #include <odf/SheetsOdf.h>
70
71 #include <Charting.h>
72 #include <KoOdfChartWriter.h>
73 #include <NumberFormatParser.h>
74
75 #include <iostream>
76
77 #include "swinder.h"
78 #include "objects.h"
79 #include "ODrawClient.h"
80 #include "ImportUtils.h"
81 #include "conditionals.h"
82
83 // Enable this definition to make the filter output to an ods file instead of
84 // using m_chain.outputDocument() to write the spreadsheet to.
85 //#define OUTPUT_AS_ODS_FILE
86
87 K_PLUGIN_FACTORY_WITH_JSON(ExcelImportFactory, "calligra_filter_xls2ods.json", registerPlugin<ExcelImport>();)
88
89 static const qreal SIDEWINDERPROGRESS = 40.0;
90 static const qreal ODFPROGRESS = 40.0;
91 static const qreal EMBEDDEDPROGRESS = 15.0;
92
93 using namespace Swinder;
94 using namespace XlsUtils;
95
offset(unsigned long dimension,unsigned long offset,qreal factor)96 static qreal offset( unsigned long dimension, unsigned long offset, qreal factor ) {
97 return (float)dimension * (float)offset / factor;
98 }
99
columnWidth(Sheet * sheet,unsigned long col)100 static qreal columnWidth(Sheet* sheet, unsigned long col) {
101 if( sheet->column(col, false) )
102 return sheet->column(col)->width();
103
104 return sheet->defaultColWidth();
105 }
106
rowHeight(Sheet * sheet,unsigned long row)107 static qreal rowHeight(Sheet* sheet, unsigned long row) {
108 if( sheet->row(row, false) )
109 return sheet->row(row)->height();
110
111 return sheet->defaultRowHeight();
112 }
113
114 class ExcelImport::Private
115 {
116 public:
Private(ExcelImport * q)117 Private(ExcelImport *q)
118 : q(q)
119 {
120 }
121
122 QString inputFile;
123 Calligra::Sheets::DocBase* outputDoc;
124
125 Workbook *workbook;
126
127 // for embedded shapes
128 KoStore* storeout;
129 KoGenStyles *shapeStyles;
130 KoGenStyles *dataStyles;
131 KoXmlWriter *shapesXml;
132
133 void processMetaData();
134 void processSheet(Sheet* isheet, Calligra::Sheets::Sheet* osheet);
135 void processSheetForHeaderFooter(Sheet* isheet, Calligra::Sheets::Sheet* osheet);
136 void processSheetForFilters(Sheet* isheet, Calligra::Sheets::Sheet* osheet);
137 void processSheetForConditionals(Sheet* isheet, Calligra::Sheets::Sheet* osheet);
138 void processColumn(Sheet* isheet, unsigned column, Calligra::Sheets::Sheet* osheet);
139 void processRow(Sheet* isheet, unsigned row, Calligra::Sheets::Sheet* osheet);
140 void processCell(Cell* icell, Calligra::Sheets::Cell ocell);
141 void processCellObjects(Cell* icell, Calligra::Sheets::Cell ocell);
142 void processEmbeddedObjects(const KoXmlElement& rootElement, KoStore* store);
143 void processNumberFormats();
144
145 QString convertHeaderFooter(const QString& xlsHeader);
146
147 int convertStyle(const Format* format, const QString& formula = QString());
148 QHash<CellFormatKey, int> styleCache;
149 QList<Calligra::Sheets::Style> styleList;
150 QHash<QString, Calligra::Sheets::Style> dataStyleCache;
151 QHash<QString, Calligra::Sheets::Conditions> dataStyleConditions;
152
153 void processFontFormat(const FormatFont& font, Calligra::Sheets::Style& style);
154 QTextCharFormat convertFontToCharFormat(const FormatFont& font);
155 QPen convertBorder(const Pen& pen);
156
157 int rowsCountTotal, rowsCountDone;
158 void addProgress(int addValue);
159
160 QHash<int, QRegion> cellStyles;
161 QHash<int, QRegion> rowStyles;
162 QHash<int, QRegion> columnStyles;
163 QList<QPair<QRegion, Calligra::Sheets::Conditions> > cellConditions;
164
165 QList<KoOdfChartWriter*> charts;
166 void processCharts(KoXmlWriter* manifestWriter);
167
168 void addManifestEntries(KoXmlWriter* ManifestWriter);
169 void insertPictureManifest(const QString& fileName);
170 QMap<QString,QString> manifestEntries;
171
172 KoXmlWriter* beginMemoryXmlWriter(const char* docElement);
173 KoXmlDocument endMemoryXmlWriter(KoXmlWriter* writer);
174
175 QDateTime convertDate(double timestamp) const;
176
177 ExcelImport *q;
178
179 };
180
ExcelImport(QObject * parent,const QVariantList &)181 ExcelImport::ExcelImport(QObject* parent, const QVariantList&)
182 : KoFilter(parent)
183 {
184 d = new Private(this);
185 d->storeout = 0;
186 }
187
~ExcelImport()188 ExcelImport::~ExcelImport()
189 {
190 delete d->storeout;
191 delete d;
192 }
193
convert(const QByteArray & from,const QByteArray & to)194 KoFilter::ConversionStatus ExcelImport::convert(const QByteArray& from, const QByteArray& to)
195 {
196 if (from != "application/vnd.ms-excel")
197 return KoFilter::NotImplemented;
198
199 if (to != "application/vnd.oasis.opendocument.spreadsheet")
200 return KoFilter::NotImplemented;
201
202 d->inputFile = m_chain->inputFile();
203
204 #ifndef OUTPUT_AS_ODS_FILE
205 KoDocument* document = m_chain->outputDocument();
206 if (!document)
207 return KoFilter::StupidError;
208
209 d->outputDoc = qobject_cast<Calligra::Sheets::DocBase*>(document);
210 if (!d->outputDoc) {
211 qCWarning(lcExcelImport) << "document isn't a Calligra::Sheets::Doc but a " << document->metaObject()->className();
212 return KoFilter::WrongFormat;
213 }
214 #else
215 d->outputDoc = new Calligra::Sheets::DocBase();
216 #endif
217 d->outputDoc->setOutputMimeType(to);
218
219 emit sigProgress(0);
220
221
222 QBuffer storeBuffer; // TODO: use temporary file instead
223 delete d->storeout;
224 d->storeout = KoStore::createStore(&storeBuffer, KoStore::Write);
225
226 // open inputFile
227 d->workbook = new Swinder::Workbook(d->storeout);
228 connect(d->workbook, SIGNAL(sigProgress(int)), this, SLOT(slotSigProgress(int)));
229 if (!d->workbook->load(d->inputFile.toLocal8Bit())) {
230 delete d->workbook;
231 d->workbook = 0;
232 delete d->storeout;
233 d->storeout = 0;
234 return KoFilter::InvalidFormat;
235 }
236
237 if (d->workbook->isPasswordProtected()) {
238 delete d->workbook;
239 d->workbook = 0;
240 delete d->storeout;
241 d->storeout = 0;
242 return KoFilter::PasswordProtected;
243 }
244
245 emit sigProgress(-1);
246 emit sigProgress(0);
247
248 // count the number of rows in total to provide a good progress value
249 d->rowsCountTotal = d->rowsCountDone = 0;
250 for (unsigned i = 0; i < d->workbook->sheetCount(); ++i) {
251 Sheet* sheet = d->workbook->sheet(i);
252 d->rowsCountTotal += qMin(maximalRowCount, sheet->maxRow());
253 }
254
255 d->shapeStyles = new KoGenStyles();
256 d->dataStyles = new KoGenStyles();
257
258 // convert number formats
259 d->processNumberFormats();
260
261 d->processMetaData();
262
263 d->shapesXml = d->beginMemoryXmlWriter("table:shapes");
264
265 Calligra::Sheets::Map* map = d->outputDoc->map();
266 for (unsigned i = 0; i < d->workbook->sheetCount(); ++i) {
267 d->shapesXml->startElement("table:table");
268 d->shapesXml->addAttribute("table:id", i);
269 Sheet* sheet = d->workbook->sheet(i);
270 if (i == 0) {
271 map->setDefaultColumnWidth(sheet->defaultColWidth());
272 map->setDefaultRowHeight(sheet->defaultRowHeight());
273 }
274 Calligra::Sheets::Sheet* ksheet = map->addNewSheet(sheet->name());
275 d->processSheet(sheet, ksheet);
276 d->shapesXml->endElement();
277 }
278
279 // named expressions
280 const std::map<std::pair<unsigned, QString>, QString>& namedAreas = d->workbook->namedAreas();
281 for (std::map<std::pair<unsigned, QString>, QString>::const_iterator it = namedAreas.begin(); it != namedAreas.end(); ++it) {
282 QString range = it->second;
283 if(range.startsWith(QLatin1Char('[')) && range.endsWith(QLatin1Char(']'))) {
284 range.remove(0, 1).chop(1);
285 }
286 Calligra::Sheets::Region region(Calligra::Sheets::Odf::loadRegion(range), d->outputDoc->map());
287 if (!region.isValid() || !region.lastSheet()) {
288 qCDebug(lcExcelImport) << "invalid area" << range;
289 continue;
290 }
291 d->outputDoc->map()->namedAreaManager()->insert(region, it->first.second);
292 }
293
294 QBuffer manifestBuffer;
295 KoXmlWriter manifestWriter(&manifestBuffer);
296 manifestWriter.startDocument("manifest:manifest");
297 manifestWriter.startElement("manifest:manifest");
298 manifestWriter.addAttribute("xmlns:manifest", KoXmlNS::manifest);
299 manifestWriter.addManifestEntry("/", "application/vnd.oasis.opendocument.spreadsheet");
300
301 d->processCharts(&manifestWriter);
302 d->addManifestEntries(&manifestWriter);
303
304 manifestWriter.endElement();
305 manifestWriter.endDocument();
306 if (d->storeout->open("META-INF/manifest.xml")) {
307 d->storeout->write(manifestBuffer.buffer());
308 d->storeout->close();
309 }
310
311 delete d->storeout;
312 d->storeout = 0;
313 storeBuffer.close();
314
315 KoStore *store = KoStore::createStore(&storeBuffer, KoStore::Read);
316
317
318 // Debug odf for shapes
319 #if 0
320 d->shapesXml->endElement();
321 d->shapesXml->endDocument();
322
323 d->shapesXml->device()->seek(0);
324
325 QTextStream input(d->shapesXml->device());
326 qCDebug(lcExcelImport) << "-- START SHAPES_XML -- size : " << d->shapesXml->device()->size();
327 qCDebug(lcExcelImport) << input.readAll();
328 qCDebug(lcExcelImport) << "-- SHAPES_XML --";
329 #endif
330
331 KoXmlDocument xmlDoc = d->endMemoryXmlWriter(d->shapesXml);
332
333 d->processEmbeddedObjects(xmlDoc.documentElement(), store);
334
335 // sheet background images
336 for (unsigned i = 0; i < d->workbook->sheetCount(); ++i) {
337 Sheet* sheet = d->workbook->sheet(i);
338 Calligra::Sheets::Sheet* ksheet = map->sheet(i);
339 qCDebug(lcExcelImport) << i << sheet->backgroundImage();
340 if (sheet->backgroundImage().isEmpty()) continue;
341
342 QByteArray data;
343 store->extractFile(sheet->backgroundImage(), data);
344 QImage image = QImage::fromData(data);
345 if (image.isNull()) continue;
346
347 ksheet->setBackgroundImage(image);
348 ksheet->setBackgroundImageProperties(Calligra::Sheets::Sheet::BackgroundImageProperties());
349 }
350
351 #ifndef OUTPUT_AS_ODS_FILE
352 d->outputDoc->map()->completeLoading(store);
353 #endif
354
355 delete store;
356
357 // ensure at least one sheet
358 if (d->outputDoc->map()->count() == 0) {
359 d->outputDoc->map()->addNewSheet();
360 }
361
362 // active sheet
363 qCDebug(lcExcelImport) << "ACTIVE " << d->workbook->activeTab();
364 d->outputDoc->map()->loadingInfo()->setInitialActiveSheet(d->outputDoc->map()->sheet(d->workbook->activeTab()));
365 d->outputDoc->setModified(false);
366
367 #ifdef OUTPUT_AS_ODS_FILE
368 d->outputDoc->saveNativeFormat(m_chain->outputFile());
369 delete d->outputDoc;
370 #endif
371
372 delete d->workbook;
373 delete d->shapeStyles;
374 delete d->dataStyles;
375 d->inputFile.clear();
376 d->outputDoc = 0;
377 d->shapesXml = 0;
378
379
380 emit sigProgress(100);
381 return KoFilter::OK;
382 }
383
processMetaData()384 void ExcelImport::Private::processMetaData()
385 {
386 KoDocumentInfo* info = outputDoc->documentInfo();
387
388 if (workbook->hasProperty(Workbook::PIDSI_TITLE)) {
389 info->setAboutInfo("title", workbook->property(Workbook::PIDSI_TITLE).toString());
390 }
391 if (workbook->hasProperty(Workbook::PIDSI_SUBJECT)) {
392 info->setAboutInfo("subject", workbook->property(Workbook::PIDSI_SUBJECT).toString());
393 }
394 if (workbook->hasProperty(Workbook::PIDSI_AUTHOR)) {
395 info->setAuthorInfo("creator", workbook->property(Workbook::PIDSI_AUTHOR).toString());
396 }
397 if (workbook->hasProperty(Workbook::PIDSI_KEYWORDS)) {
398 info->setAboutInfo("keyword", workbook->property(Workbook::PIDSI_KEYWORDS).toString());
399 }
400 if (workbook->hasProperty(Workbook::PIDSI_COMMENTS)) {
401 info->setAboutInfo("comments", workbook->property(Workbook::PIDSI_COMMENTS).toString());
402 }
403 if (workbook->hasProperty(Workbook::PIDSI_REVNUMBER)) {
404 info->setAboutInfo("editing-cycles", workbook->property(Workbook::PIDSI_REVNUMBER).toString());
405 }
406 if (workbook->hasProperty(Workbook::PIDSI_LASTPRINTED_DTM)) {
407 info->setAboutInfo("print-date", workbook->property(Workbook::PIDSI_LASTPRINTED_DTM).toString());
408 }
409 if (workbook->hasProperty(Workbook::PIDSI_CREATE_DTM)) {
410 info->setAboutInfo("creation-date", workbook->property(Workbook::PIDSI_CREATE_DTM).toString());
411 }
412 if (workbook->hasProperty(Workbook::PIDSI_LASTSAVED_DTM)) {
413 info->setAboutInfo("date", workbook->property(Workbook::PIDSI_LASTSAVED_DTM).toString());
414 }
415 // template
416 // lastauthor
417 // edittime
418
419 switch (workbook->version()) {
420 case Workbook::Excel95:
421 info->setOriginalGenerator("Calligra xls Filter/Excel 95");
422 break;
423 case Workbook::Excel97:
424 info->setOriginalGenerator("Calligra xls Filter/Excel 97");
425 break;
426 case Workbook::Excel2000:
427 info->setOriginalGenerator("Calligra xls Filter/Excel 2000");
428 break;
429 case Workbook::Excel2002:
430 info->setOriginalGenerator("Calligra xls Filter/Excel 2002");
431 break;
432 case Workbook::Excel2003:
433 info->setOriginalGenerator("Calligra xls Filter/Excel 2003");
434 break;
435 case Workbook::Excel2007:
436 info->setOriginalGenerator("Calligra xls Filter/Excel 2007");
437 break;
438 case Workbook::Excel2010:
439 info->setOriginalGenerator("Calligra xls Filter/Excel 2010");
440 break;
441 default:
442 info->setOriginalGenerator("Calligra xls Filter/Unknown");
443 }
444 }
445
processEmbeddedObjects(const KoXmlElement & rootElement,KoStore * store)446 void ExcelImport::Private::processEmbeddedObjects(const KoXmlElement& rootElement, KoStore* store)
447 {
448 // save styles to xml
449 KoXmlWriter *stylesXml = beginMemoryXmlWriter("office:styles");
450 shapeStyles->saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, stylesXml);
451
452 KoXmlDocument stylesDoc = endMemoryXmlWriter(stylesXml);
453
454 // Register additional attributes, that identify shapes anchored in cells.
455 // Their dimensions need adjustment after all rows are loaded,
456 // because the position of the end cell is not always known yet.
457 KoShapeLoadingContext::addAdditionalAttributeData(KoShapeLoadingContext::AdditionalAttributeData(
458 KoXmlNS::table, "end-cell-address",
459 "table:end-cell-address"));
460 KoShapeLoadingContext::addAdditionalAttributeData(KoShapeLoadingContext::AdditionalAttributeData(
461 KoXmlNS::table, "end-x",
462 "table:end-x"));
463 KoShapeLoadingContext::addAdditionalAttributeData(KoShapeLoadingContext::AdditionalAttributeData(
464 KoXmlNS::table, "end-y",
465 "table:end-y"));
466
467
468 KoOdfStylesReader odfStyles;
469 odfStyles.createStyleMap(stylesDoc, false);
470 KoOdfLoadingContext odfContext(odfStyles, store);
471 KoShapeLoadingContext shapeContext(odfContext, outputDoc->resourceManager());
472
473 int numSheetTotal = rootElement.childNodesCount();
474 int currentSheet = 0;
475 KoXmlElement sheetElement;
476 forEachElement(sheetElement, rootElement) {
477 Q_ASSERT(sheetElement.namespaceURI() == KoXmlNS::table && sheetElement.localName() == "table");
478 int sheetId = sheetElement.attributeNS(KoXmlNS::table, "id").toInt();
479 Calligra::Sheets::Sheet* sheet = outputDoc->map()->sheet(sheetId);
480
481 KoXmlElement cellElement;
482 int numCellElements = sheetElement.childNodesCount();
483 int currentCell = 0;
484 forEachElement(cellElement, sheetElement) {
485 Q_ASSERT(cellElement.namespaceURI() == KoXmlNS::table);
486 if (cellElement.localName() == "shapes") {
487 KoXmlElement element;
488 forEachElement(element, cellElement) {
489 Calligra::Sheets::Odf::loadSheetObject(sheet, element, shapeContext);
490 }
491 } else {
492 Q_ASSERT(cellElement.localName() == "table-cell");
493 int row = cellElement.attributeNS(KoXmlNS::table, "row").toInt();
494 int col = cellElement.attributeNS(KoXmlNS::table, "column").toInt();
495 Calligra::Sheets::Cell cell(sheet, col, row);
496
497 KoXmlElement element;
498 forEachElement(element, cellElement) {
499 Calligra::Sheets::Odf::loadObject(&cell, element, shapeContext);
500 }
501 }
502 ++currentCell;
503 const int progress = int(currentSheet / qreal(numSheetTotal) * EMBEDDEDPROGRESS
504 + (EMBEDDEDPROGRESS / qreal(numSheetTotal) * currentCell/numCellElements)
505 + SIDEWINDERPROGRESS + ODFPROGRESS) + 0.5;
506 emit q->sigProgress(progress);
507 }
508
509 ++currentSheet;
510 const int progress = int(currentSheet / qreal(numSheetTotal) * EMBEDDEDPROGRESS + SIDEWINDERPROGRESS + ODFPROGRESS + 0.5);
511 emit q->sigProgress(progress);
512 }
513 }
514
getRect(const MSO::OfficeArtFSPGR & r)515 static QRectF getRect(const MSO::OfficeArtFSPGR &r)
516 {
517 return QRect(r.xLeft, r.yTop, r.xRight - r.xLeft, r.yBottom - r.yTop);
518 }
519
processSheet(Sheet * is,Calligra::Sheets::Sheet * os)520 void ExcelImport::Private::processSheet(Sheet* is, Calligra::Sheets::Sheet* os)
521 {
522 os->setHidden(!is->visible());
523 //os->setProtected(is->protect());
524 os->setAutoCalculationEnabled(is->autoCalc());
525 os->setHideZero(!is->showZeroValues());
526 os->setShowGrid(is->showGrid());
527 os->setFirstLetterUpper(false);
528 os->map()->loadingInfo()->setCursorPosition(os, is->firstVisibleCell() + QPoint(1, 1));
529 os->setShowFormulaIndicator(false);
530 os->setShowCommentIndicator(true);
531 os->setShowPageOutline(is->isPageBreakViewEnabled());
532 os->setLcMode(false);
533 os->setShowColumnNumber(false);
534 os->setLayoutDirection(is->isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight);
535
536 // TODO: page layout
537 processSheetForHeaderFooter(is, os);
538
539 if(is->password() != 0) {
540 //TODO
541 }
542
543
544 const unsigned columnCount = qMin(maximalColumnCount, is->maxColumn());
545 for (unsigned i = 0; i <= columnCount; ++i) {
546 processColumn(is, i, os);
547 }
548
549 cellStyles.clear();
550 rowStyles.clear();
551 columnStyles.clear();
552 cellConditions.clear();
553 const unsigned rowCount = qMin(maximalRowCount, is->maxRow());
554 for (unsigned i = 0; i <= rowCount && i < KS_rowMax; ++i) {
555 processRow(is, i, os);
556 }
557
558 QList<QPair<QRegion, Calligra::Sheets::Style> > styles;
559 for (QHash<int, QRegion>::const_iterator it = columnStyles.constBegin(); it != columnStyles.constEnd(); ++it) {
560 styles.append(qMakePair(it.value(), styleList[it.key()]));
561 }
562 for (QHash<int, QRegion>::const_iterator it = rowStyles.constBegin(); it != rowStyles.constEnd(); ++it) {
563 styles.append(qMakePair(it.value(), styleList[it.key()]));
564 }
565 for (QHash<int, QRegion>::const_iterator it = cellStyles.constBegin(); it != cellStyles.constEnd(); ++it) {
566 styles.append(qMakePair(it.value(), styleList[it.key()]));
567 }
568 os->cellStorage()->loadStyles(styles);
569
570 // sheet shapes
571 if (!is->drawObjects().isEmpty() || is->drawObjectsGroupCount()) {
572 shapesXml->startElement("table:shapes");
573
574 ODrawClient client = ODrawClient(is);
575 ODrawToOdf odraw(client);
576 Writer writer(*shapesXml, *shapeStyles, false);
577
578 const QList<OfficeArtObject*> objs = is->drawObjects();
579 for (int i = 0; i < objs.size(); ++i) {
580 OfficeArtObject* o = objs[i];
581 client.setShapeText(o->text());
582 client.setZIndex(o->index());
583 client.setStyleManager(outputDoc->map()->textStyleManager());
584 odraw.processDrawingObject(o->object(), writer);
585 }
586
587 for (int i = is->drawObjectsGroupCount()-1; i >= 0; --i) {
588 shapesXml->startElement("draw:g");
589
590 const MSO::OfficeArtSpgrContainer& group = is->drawObjectsGroup(i);
591 const MSO::OfficeArtSpContainer* first = group.rgfb.first().anon.get<MSO::OfficeArtSpContainer>();
592 if (first && first->clientAnchor && first->shapeGroup) {
593 QRectF oldCoords = client.getGlobalRect(*first->clientAnchor);
594 QRectF newCoords = getRect(*first->shapeGroup);
595 Writer transw = writer.transform(oldCoords, newCoords);
596 const QList<OfficeArtObject*> gobjs = is->drawObjects(i);
597 for (int j = 0; j < gobjs.size(); ++j) {
598 OfficeArtObject* o = gobjs[j];
599 client.setShapeText(o->text());
600 client.setZIndex(o->index());
601 client.setStyleManager(outputDoc->map()->textStyleManager());
602 odraw.processDrawingObject(o->object(), transw);
603 }
604 } else {
605 const QList<OfficeArtObject*> gobjs = is->drawObjects(i);
606 for (int j = 0; j < gobjs.size(); ++j) {
607 OfficeArtObject* o = gobjs[j];
608 client.setShapeText(o->text());
609 client.setZIndex(o->index());
610 client.setStyleManager(outputDoc->map()->textStyleManager());
611 odraw.processDrawingObject(o->object(), writer);
612 }
613 }
614 shapesXml->endElement(); // draw:g
615 }
616
617 shapesXml->endElement();
618 }
619
620
621 processSheetForFilters(is, os);
622 processSheetForConditionals(is, os);
623
624 os->cellStorage()->loadConditions(cellConditions);
625 }
626
processSheetForHeaderFooter(Sheet * is,Calligra::Sheets::Sheet * os)627 void ExcelImport::Private::processSheetForHeaderFooter(Sheet* is, Calligra::Sheets::Sheet* os)
628 {
629 os->print()->headerFooter()->setHeadFootLine(
630 convertHeaderFooter(is->leftHeader()), convertHeaderFooter(is->centerHeader()),
631 convertHeaderFooter(is->rightHeader()), convertHeaderFooter(is->leftFooter()),
632 convertHeaderFooter(is->centerFooter()), convertHeaderFooter(is->rightFooter()));
633 }
634
processSheetForFilters(Sheet * is,Calligra::Sheets::Sheet * os)635 void ExcelImport::Private::processSheetForFilters(Sheet* is, Calligra::Sheets::Sheet* os)
636 {
637 static int rangeId = 0; // not very nice to do this this way, but I only care about sort of unique names
638 QList<QRect> filters = workbook->filterRanges(is);
639 foreach (const QRect& filter, filters) {
640 Calligra::Sheets::Database db;
641 db.setName(QString("excel-database-%1").arg(++rangeId));
642 db.setDisplayFilterButtons(true);
643 QRect r = filter.adjusted(1, 1, 1, 1);
644 r.setBottom(is->maxRow()+1);
645 Calligra::Sheets::Region range(r, os);
646 db.setRange(range);
647 db.setFilter(is->autoFilters());
648 os->cellStorage()->setDatabase(range, db);
649
650 // xls files don't seem to make a difference between hidden and filtered rows, so
651 // assume all rows in a database range are filtered, not explicitly hidden
652 int row = r.top() + 1;
653 while (row <= r.bottom()) {
654 int lastRow;
655 bool isHidden = os->rowFormats()->isHidden(row, &lastRow);
656 if (isHidden) {
657 os->rowFormats()->setHidden(row, lastRow, false);
658 os->rowFormats()->setFiltered(row, lastRow, true);
659 }
660 row = lastRow + 1;
661 }
662 }
663 }
664
convertValue(const Value & v)665 static Calligra::Sheets::Value convertValue(const Value& v)
666 {
667 if (v.isBoolean()) {
668 return Calligra::Sheets::Value(v.asBoolean());
669 } else if (v.isFloat()) {
670 return Calligra::Sheets::Value(v.asFloat());
671 } else if (v.isInteger()) {
672 return Calligra::Sheets::Value(v.asInteger());
673 } else if (v.isText()) {
674 return Calligra::Sheets::Value(v.asString());
675 } else if (v.isError()) {
676 Calligra::Sheets::Value kv(Calligra::Sheets::Value::Error);
677 kv.setError(v.asString());
678 return kv;
679 } else {
680 return Calligra::Sheets::Value();
681 }
682 }
683
processSheetForConditionals(Sheet * is,Calligra::Sheets::Sheet * os)684 void ExcelImport::Private::processSheetForConditionals(Sheet* is, Calligra::Sheets::Sheet* os)
685 {
686 static int styleNameId = 0;
687 const QList<ConditionalFormat*> conditionals = is->conditionalFormats();
688 Calligra::Sheets::StyleManager* styleManager = os->map()->styleManager();
689 foreach (ConditionalFormat* cf, conditionals) {
690 QRegion r = cf->region().translated(1, 1);
691 QLinkedList<Calligra::Sheets::Conditional> conds;
692 foreach (const Conditional& c, cf->conditionals()) {
693 Calligra::Sheets::Conditional kc;
694 switch (c.cond) {
695 case Conditional::None:
696 kc.cond = Calligra::Sheets::Conditional::None;
697 break;
698 case Conditional::Formula:
699 kc.cond = Calligra::Sheets::Conditional::IsTrueFormula;
700 break;
701 case Conditional::Between:
702 kc.cond = Calligra::Sheets::Conditional::Between;
703 break;
704 case Conditional::Outside:
705 kc.cond = Calligra::Sheets::Conditional::Different;
706 break;
707 case Conditional::Equal:
708 kc.cond = Calligra::Sheets::Conditional::Equal;
709 break;
710 case Conditional::NotEqual:
711 kc.cond = Calligra::Sheets::Conditional::DifferentTo;
712 break;
713 case Conditional::Greater:
714 kc.cond = Calligra::Sheets::Conditional::Superior;
715 break;
716 case Conditional::Less:
717 kc.cond = Calligra::Sheets::Conditional::Inferior;
718 break;
719 case Conditional::GreaterOrEqual:
720 kc.cond = Calligra::Sheets::Conditional::SuperiorEqual;
721 break;
722 case Conditional::LessOrEqual:
723 kc.cond = Calligra::Sheets::Conditional::InferiorEqual;
724 break;
725 }
726 qCDebug(lcExcelImport) << "FRM:" << c.cond << kc.cond;
727 kc.value1 = convertValue(c.value1);
728 kc.value2 = convertValue(c.value2);
729 kc.baseCellAddress = Swinder::encodeAddress(is->name(), cf->region().boundingRect().left(), cf->region().boundingRect().top());
730
731 Calligra::Sheets::CustomStyle* style = new Calligra::Sheets::CustomStyle(QString("Excel-Condition-Style-%1").arg(styleNameId++));
732 kc.styleName = style->name();
733
734 // TODO: valueFormat
735 if (c.hasFontItalic()) {
736 style->setFontItalic(c.font().italic());
737 }
738 if (c.hasFontStrikeout()) {
739 style->setFontStrikeOut(c.font().strikeout());
740 }
741 if (c.hasFontBold()) {
742 style->setFontBold(c.font().bold());
743 }
744 // TODO: sub/superscript
745 if (c.hasFontUnderline()) {
746 style->setFontUnderline(c.font().underline());
747 }
748 if (c.hasFontColor()) {
749 style->setFontColor(c.font().color());
750 }
751 // TODO: other properties
752
753 styleManager->insertStyle(style);
754 conds.append(kc);
755 }
756 Calligra::Sheets::Conditions kcs;
757 kcs.setConditionList(conds);
758 cellConditions.append(qMakePair(r, kcs));
759 }
760 }
761
convertHeaderFooter(const QString & text)762 QString ExcelImport::Private::convertHeaderFooter(const QString& text)
763 {
764 QString result;
765 bool skipUnsupported = false;
766 int lastPos;
767 int pos = text.indexOf('&');
768 int len = text.length();
769 if ((pos < 0) && (text.length() > 0)) // If there is no &
770 result += text;
771 else if (pos > 0) // Some text and '&'
772 result += text.mid(0, pos - 1);
773
774 while (pos >= 0) {
775 switch (text[pos + 1].unicode()) {
776 case 'D':
777 result += "<date>";
778 break;
779 case 'T':
780 result += "<time>";
781 break;
782 case 'P':
783 result += "<page>";
784 break;
785 case 'N':
786 result += "<pages>";
787 break;
788 case 'F':
789 result += "<name>";
790 break;
791 case 'A':
792 result += "<sheet>";
793 break;
794 case '\"':
795 default:
796 skipUnsupported = true;
797 break;
798 }
799 lastPos = pos;
800 pos = text.indexOf('&', lastPos + 1);
801 if (!skipUnsupported && (pos > (lastPos + 1)))
802 result += text.mid(lastPos + 2, (pos - lastPos - 2));
803 else if (!skipUnsupported && (pos < 0)) //Remaining text
804 result += text.mid(lastPos + 2, len - (lastPos + 2));
805 else
806 skipUnsupported = false;
807 }
808 return result;
809 }
810
processColumn(Sheet * is,unsigned columnIndex,Calligra::Sheets::Sheet * os)811 void ExcelImport::Private::processColumn(Sheet* is, unsigned columnIndex, Calligra::Sheets::Sheet* os)
812 {
813 Column* column = is->column(columnIndex, false);
814
815 if (!column) return;
816
817 Calligra::Sheets::ColumnFormat* oc = os->nonDefaultColumnFormat(columnIndex+1);
818 oc->setWidth(column->width());
819 oc->setHidden(!column->visible());
820
821 int styleId = convertStyle(&column->format());
822 columnStyles[styleId] += QRect(columnIndex+1, 1, 1, KS_rowMax);
823 }
824
processRow(Sheet * is,unsigned rowIndex,Calligra::Sheets::Sheet * os)825 void ExcelImport::Private::processRow(Sheet* is, unsigned rowIndex, Calligra::Sheets::Sheet* os)
826 {
827 Row *row = is->row(rowIndex, false);
828
829 if (!row) {
830 if (is->defaultRowHeight() != os->map()->defaultRowFormat()->height()) {
831 os->rowFormats()->setRowHeight(rowIndex+1, rowIndex+1, is->defaultRowHeight());
832 }
833 return;
834 }
835
836 os->rowFormats()->setRowHeight(rowIndex+1, rowIndex+1, row->height());
837 os->rowFormats()->setHidden(rowIndex+1, rowIndex+1, !row->visible());
838 // TODO default cell style
839
840 // find the column of the rightmost cell (if any)
841 const int lastCol = row->sheet()->maxCellsInRow(rowIndex);
842 for (int i = 0; i <= lastCol; ++i) {
843 Cell* cell = is->cell(i, rowIndex, false);
844 if (!cell) continue;
845 processCell(cell, Calligra::Sheets::Cell(os, i+1, rowIndex+1));
846 }
847
848 addProgress(1);
849 }
850
cellFormulaNamespace(const QString & formula)851 static QString cellFormulaNamespace(const QString& formula)
852 {
853 if (!formula.isEmpty()) {
854 if(formula.startsWith("ROUNDUP(") || formula.startsWith("ROUNDDOWN(") || formula.startsWith("ROUND(") || formula.startsWith("RAND(")) {
855 // Special case Excel formulas that differ from OpenFormula
856 return "msoxl:";
857 } else if (!formula.isEmpty()) {
858 return "of:";
859 }
860 }
861 return QString();
862 }
863
convertDate(double timestamp) const864 QDateTime ExcelImport::Private::convertDate(double timestamp) const
865 {
866 QDateTime dt(workbook->baseDate());
867 dt = dt.addMSecs((qint64)(timestamp * 86400 * 1000));
868 return dt;
869 }
870
convertTime(double timestamp)871 static QTime convertTime(double timestamp)
872 {
873 QTime tt;
874 tt = tt.addMSecs(qRound((timestamp - (qint64)timestamp) * 86400 * 1000));
875 return tt;
876 }
877
processCell(Cell * ic,Calligra::Sheets::Cell oc)878 void ExcelImport::Private::processCell(Cell* ic, Calligra::Sheets::Cell oc)
879 {
880 int colSpan = ic->columnSpan();
881 int rowSpan = ic->rowSpan();
882 if (colSpan > 1 || rowSpan > 1) {
883 oc.mergeCells(oc.column(), oc.row(), colSpan - 1, rowSpan - 1);
884 }
885
886 const QString formula = ic->formula();
887 const bool isFormula = !formula.isEmpty();
888 if (isFormula) {
889 const QString nsPrefix = cellFormulaNamespace(formula);
890 const QString decodedFormula = Calligra::Sheets::Odf::decodeFormula('=' + formula, oc.locale(), nsPrefix);
891 oc.setRawUserInput(decodedFormula);
892 }
893
894 int styleId = convertStyle(&ic->format(), formula);
895
896 Value value = ic->value();
897 if (value.isBoolean()) {
898 oc.setValue(Calligra::Sheets::Value(value.asBoolean()));
899 if (!isFormula)
900 oc.setRawUserInput(oc.sheet()->map()->converter()->asString(oc.value()).asString());
901 } else if (value.isNumber()) {
902 const QString valueFormat = ic->format().valueFormat();
903
904 if (isPercentageFormat(valueFormat)) {
905 Calligra::Sheets::Value v(value.asFloat());
906 v.setFormat(Calligra::Sheets::Value::fmt_Percent);
907 oc.setValue(v);
908 } else if (Calligra::Sheets::Format::isDate(styleList[styleId].formatType())) {
909 QDateTime date = convertDate(value.asFloat());
910 oc.setValue(Calligra::Sheets::Value(date, outputDoc->map()->calculationSettings()));
911 KLocale* locale = outputDoc->map()->calculationSettings()->locale();
912 if (!isFormula) {
913 if (true /* TODO somehow determine if time should be included */) {
914 oc.setRawUserInput(locale->formatDate(date.date()));
915 } else {
916 oc.setRawUserInput(locale->formatDateTime(date));
917 }
918 }
919 } else if (Calligra::Sheets::Format::isTime(styleList[styleId].formatType())) {
920 QTime time = convertTime(value.asFloat());
921 oc.setValue(Calligra::Sheets::Value(time));
922 KLocale* locale = outputDoc->map()->calculationSettings()->locale();
923 if (!isFormula)
924 oc.setRawUserInput(locale->formatTime(time, true));
925 } else /* fraction or normal */ {
926 oc.setValue(Calligra::Sheets::Value(value.asFloat()));
927 if (!isFormula)
928 oc.setRawUserInput(oc.sheet()->map()->converter()->asString(oc.value()).asString());
929 }
930 } else if (value.isText()) {
931 QString txt = value.asString();
932
933 Hyperlink link = ic->hyperlink();
934 if (link.isValid) {
935 if (!link.location.isEmpty()) {
936 if (link.location[0] == '#') {
937 oc.setLink(link.location.mid(1));
938 } else {
939 oc.setLink(link.location);
940 }
941
942 if (!link.displayName.trimmed().isEmpty())
943 txt = link.displayName.trimmed();
944 }
945 }
946
947 oc.setValue(Calligra::Sheets::Value(txt));
948 if (!isFormula) {
949 if (txt.startsWith('='))
950 oc.setRawUserInput('\'' + txt);
951 else
952 oc.setRawUserInput(txt);
953 }
954 if (value.isRichText() || ic->format().font().subscript() || ic->format().font().superscript()) {
955 std::map<unsigned, FormatFont> formatRuns = value.formatRuns();
956 // add sentinel to list of format runs
957 if (!formatRuns.count(0))
958 formatRuns[0] = ic->format().font();
959 formatRuns[txt.length()] = ic->format().font();
960
961 QSharedPointer<QTextDocument> doc(new QTextDocument(txt));
962 KoTextDocument(doc.data()).setStyleManager(oc.sheet()->map()->textStyleManager());
963 QTextCursor c(doc.data());
964 for (std::map<unsigned, FormatFont>::iterator it = formatRuns.begin(); it != formatRuns.end(); ++it) {
965 std::map<unsigned, FormatFont>::iterator it2 = it; ++it2;
966 if (it2 != formatRuns.end()) {
967 // select block
968 c.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, it2->first - it->first);
969 c.setCharFormat(convertFontToCharFormat(it->second));
970 c.clearSelection();
971 }
972 }
973 oc.setRichText(doc);
974 }
975 } else if (value.isError()) {
976 Calligra::Sheets::Value v(Calligra::Sheets::Value::Error);
977 v.setError(value.asString());
978 oc.setValue(v);
979 }
980
981 QString note = ic->note();
982 if (!note.isEmpty())
983 oc.setComment(note);
984
985 cellStyles[styleId] += QRect(oc.column(), oc.row(), 1, 1);
986 QHash<QString, Calligra::Sheets::Conditions>::ConstIterator conds = dataStyleConditions.constFind(ic->format().valueFormat());
987 if (conds != dataStyleConditions.constEnd()) {
988 cellConditions.append(qMakePair(QRegion(oc.column(), oc.row(), 1, 1), conds.value()));
989 }
990
991 processCellObjects(ic, oc);
992 }
993
processCellObjects(Cell * ic,Calligra::Sheets::Cell oc)994 void ExcelImport::Private::processCellObjects(Cell* ic, Calligra::Sheets::Cell oc)
995 {
996 bool hasObjects = false;
997
998 // handle charts
999 foreach(ChartObject *chart, ic->charts()) {
1000 Sheet* const sheet = ic->sheet();
1001 if(chart->m_chart->m_impl==0) {
1002 qCDebug(lcExcelImport) << "Invalid chart to be created, no implementation.";
1003 continue;
1004 }
1005
1006 if (!hasObjects) {
1007 shapesXml->startElement("table:table-cell");
1008 shapesXml->addAttribute("table:row", oc.row());
1009 shapesXml->addAttribute("table:column", oc.column());
1010 hasObjects = true;
1011 }
1012
1013 KoOdfChartWriter *c = new KoOdfChartWriter(chart->m_chart);
1014 c->setSheetReplacement( false );
1015 c->m_href = QString("Chart%1").arg(this->charts.count()+1);
1016 c->m_endCellAddress = Swinder::encodeAddress(sheet->name(), chart->m_colR, chart->m_rwB);
1017 c->m_end_x = offset(columnWidth(sheet, chart->m_colR), chart->m_dxR, 1024);
1018 c->m_end_y = offset(columnWidth(sheet, chart->m_rwB), chart->m_dyB, 256);
1019 c->m_notifyOnUpdateOfRanges = "Sheet1.D2:Sheet1.F2"; //TODO don't hardcode!
1020
1021 const unsigned long colL = chart->m_colL;
1022 const unsigned long dxL = chart->m_dxL;
1023 const unsigned long dyT = chart->m_dyT;
1024 const unsigned long rwT = chart->m_rwT;
1025
1026 c->m_x = offset(columnWidth(sheet, colL), dxL, 1024);
1027 c->m_y = offset(rowHeight(sheet, rwT), dyT, 256);
1028
1029 if (!chart->m_chart->m_cellRangeAddress.isNull() )
1030 c->m_cellRangeAddress = Swinder::encodeAddress(sheet->name(), chart->m_chart->m_cellRangeAddress.left(), chart->m_chart->m_cellRangeAddress.top()) + ":" +
1031 Swinder::encodeAddress(sheet->name(), chart->m_chart->m_cellRangeAddress.right(), chart->m_chart->m_cellRangeAddress.bottom());
1032
1033 this->charts << c;
1034
1035 c->saveIndex(shapesXml);
1036 }
1037
1038
1039
1040 // handle ODraw objects
1041 QList<OfficeArtObject*> objects = ic->drawObjects();
1042 if (!objects.empty()) {
1043 if (!hasObjects) {
1044 shapesXml->startElement("table:table-cell");
1045 shapesXml->addAttribute("table:row", oc.row());
1046 shapesXml->addAttribute("table:column", oc.column());
1047 hasObjects = true;
1048 }
1049 ODrawClient client = ODrawClient(ic->sheet());
1050
1051 ODrawToOdf odraw(client);
1052 Writer writer(*shapesXml, *shapeStyles, false);
1053 for (int i = 0; i < objects.size(); ++i) {
1054 OfficeArtObject* o = objects[i];
1055 client.setShapeText(o->text());
1056 client.setZIndex(o->index());
1057 client.setStyleManager(outputDoc->map()->textStyleManager());
1058 odraw.processDrawingObject(o->object(), writer);
1059 }
1060 }
1061
1062 if (hasObjects) {
1063 shapesXml->endElement();
1064 }
1065 }
1066
processCharts(KoXmlWriter * manifestWriter)1067 void ExcelImport::Private::processCharts(KoXmlWriter* manifestWriter)
1068 {
1069 foreach(KoOdfChartWriter *c, this->charts) {
1070 c->set2003ColorPalette( workbook->colorTable() );
1071 c->saveContent(this->storeout, manifestWriter);
1072 }
1073 }
1074
convertStyle(const Format * format,const QString & formula)1075 int ExcelImport::Private::convertStyle(const Format* format, const QString& formula)
1076 {
1077 CellFormatKey key(format, formula);
1078 int& styleId = styleCache[key];
1079 if (!styleId) {
1080 Calligra::Sheets::Style style;
1081 style.setDefault();
1082
1083 if (!key.isGeneral) {
1084 style.merge(dataStyleCache.value(format->valueFormat(), Calligra::Sheets::Style()));
1085 } else {
1086 if (key.decimalCount >= 0) {
1087 style.setFormatType(Calligra::Sheets::Format::Number);
1088 style.setPrecision(key.decimalCount);
1089 QString format = ".";
1090 for (int i = 0; i < key.decimalCount; i++) {
1091 format += '0';
1092 }
1093 style.setCustomFormat(format);
1094 }
1095 }
1096
1097 processFontFormat(format->font(), style);
1098
1099 FormatAlignment align = format->alignment();
1100 if (!align.isNull()) {
1101 switch (align.alignY()) {
1102 case Format::Top:
1103 style.setVAlign(Calligra::Sheets::Style::Top);
1104 break;
1105 case Format::Middle:
1106 style.setVAlign(Calligra::Sheets::Style::Middle);
1107 break;
1108 case Format::Bottom:
1109 style.setVAlign(Calligra::Sheets::Style::Bottom);
1110 break;
1111 case Format::VJustify:
1112 style.setVAlign(Calligra::Sheets::Style::VJustified);
1113 break;
1114 case Format::VDistributed:
1115 style.setVAlign(Calligra::Sheets::Style::VDistributed);
1116 break;
1117 }
1118
1119 style.setWrapText(align.wrap());
1120
1121 if (align.rotationAngle()) {
1122 style.setAngle(align.rotationAngle());
1123 }
1124
1125 if (align.stackedLetters()) {
1126 style.setVerticalText(true);
1127 }
1128
1129 if (align.shrinkToFit()) {
1130 style.setShrinkToFit(true);
1131 }
1132
1133 switch (align.alignX()) {
1134 case Format::Left:
1135 style.setHAlign(Calligra::Sheets::Style::Left);
1136 break;
1137 case Format::Center:
1138 style.setHAlign(Calligra::Sheets::Style::Center);
1139 break;
1140 case Format::Right:
1141 style.setHAlign(Calligra::Sheets::Style::Right);
1142 break;
1143 case Format::Justify:
1144 case Format::Distributed:
1145 style.setHAlign(Calligra::Sheets::Style::Justified);
1146 break;
1147 }
1148
1149 if (align.indentLevel() != 0) {
1150 style.setIndentation(align.indentLevel() * 10);
1151 }
1152 }
1153
1154 FormatBorders borders = format->borders();
1155 if (!borders.isNull()) {
1156 style.setLeftBorderPen(convertBorder(borders.leftBorder()));
1157 style.setRightBorderPen(convertBorder(borders.rightBorder()));
1158 style.setTopBorderPen(convertBorder(borders.topBorder()));
1159 style.setBottomBorderPen(convertBorder(borders.bottomBorder()));
1160 style.setFallDiagonalPen(convertBorder(borders.topLeftBorder()));
1161 style.setGoUpDiagonalPen(convertBorder(borders.bottomLeftBorder()));
1162 }
1163
1164 FormatBackground back = format->background();
1165 if (!back.isNull() && back.pattern() != FormatBackground::EmptyPattern) {
1166 QColor backColor = back.backgroundColor();
1167 if (back.pattern() == FormatBackground::SolidPattern)
1168 backColor = back.foregroundColor();
1169 style.setBackgroundColor(backColor);
1170
1171 QBrush brush;
1172 switch (back.pattern()) {
1173 case FormatBackground::SolidPattern:
1174 brush.setStyle(Qt::SolidPattern);
1175 brush.setColor(backColor);
1176 break;
1177 case FormatBackground::Dense3Pattern: // 88% gray
1178 brush.setStyle(Qt::Dense2Pattern);
1179 brush.setColor(Qt::black);
1180 break;
1181 case FormatBackground::Dense4Pattern: // 50% gray
1182 brush.setStyle(Qt::Dense4Pattern);
1183 brush.setColor(Qt::black);
1184 break;
1185 case FormatBackground::Dense5Pattern: // 37% gray
1186 brush.setStyle(Qt::Dense5Pattern);
1187 brush.setColor(Qt::black);
1188 break;
1189 case FormatBackground::Dense6Pattern: // 12% gray
1190 brush.setStyle(Qt::Dense6Pattern);
1191 brush.setColor(Qt::black);
1192 break;
1193 case FormatBackground::Dense7Pattern: // 6% gray
1194 brush.setStyle(Qt::Dense7Pattern);
1195 brush.setColor(Qt::black);
1196 break;
1197
1198 case FormatBackground::Dense1Pattern:
1199 case FormatBackground::HorPattern:
1200 brush.setStyle(Qt::HorPattern);
1201 brush.setColor(Qt::black);
1202 break;
1203 case FormatBackground::VerPattern:
1204 brush.setStyle(Qt::VerPattern);
1205 brush.setColor(Qt::black);
1206 break;
1207 case FormatBackground::Dense2Pattern:
1208 case FormatBackground::BDiagPattern:
1209 brush.setStyle(Qt::BDiagPattern);
1210 brush.setColor(Qt::black);
1211 break;
1212 case FormatBackground::FDiagPattern:
1213 brush.setStyle(Qt::FDiagPattern);
1214 brush.setColor(Qt::black);
1215 break;
1216 case FormatBackground::CrossPattern:
1217 brush.setStyle(Qt::CrossPattern);
1218 brush.setColor(Qt::black);
1219 break;
1220 case FormatBackground::DiagCrossPattern:
1221 brush.setStyle(Qt::DiagCrossPattern);
1222 brush.setColor(Qt::black);
1223 break;
1224 }
1225 style.setBackgroundBrush(brush);
1226 }
1227
1228 styleId = styleList.size();
1229 styleList.append(style);
1230 }
1231 return styleId;
1232 }
1233
processFontFormat(const FormatFont & font,Calligra::Sheets::Style & style)1234 void ExcelImport::Private::processFontFormat(const FormatFont& font, Calligra::Sheets::Style& style)
1235 {
1236 if (font.isNull()) return;
1237
1238 QFont f;
1239 f.setBold(font.bold());
1240 f.setItalic(font.italic());
1241 f.setUnderline(font.underline());
1242 f.setStrikeOut(font.strikeout());
1243 f.setFamily(font.fontFamily());
1244 f.setPointSizeF(font.fontSize());
1245 style.setFont(f);
1246 style.setFontColor(font.color());
1247 }
1248
convertFontToCharFormat(const FormatFont & font)1249 QTextCharFormat ExcelImport::Private::convertFontToCharFormat(const FormatFont& font)
1250 {
1251 QTextCharFormat frm;
1252 QFont f;
1253 f.setBold(font.bold());
1254 f.setItalic(font.italic());
1255 f.setUnderline(font.underline());
1256 f.setStrikeOut(font.strikeout());
1257 f.setFamily(font.fontFamily());
1258 f.setPointSizeF(font.fontSize());
1259 frm.setFont(f);
1260 frm.setForeground(font.color());
1261 if (font.subscript())
1262 frm.setVerticalAlignment(QTextCharFormat::AlignSubScript);
1263 if (font.superscript())
1264 frm.setVerticalAlignment(QTextCharFormat::AlignSuperScript);
1265 return frm;
1266 }
1267
convertBorder(const Pen & pen)1268 QPen ExcelImport::Private::convertBorder(const Pen& pen)
1269 {
1270 if (pen.style == Pen::NoLine || pen.width == 0) {
1271 return QPen(Qt::NoPen);
1272 } else {
1273 QPen op;
1274 if (pen.style == Pen::DoubleLine) {
1275 op.setWidthF(pen.width * 3);
1276 } else {
1277 op.setWidthF(pen.width);
1278 }
1279
1280 switch (pen.style) {
1281 case Pen::SolidLine: op.setStyle(Qt::SolidLine); break;
1282 case Pen::DashLine: op.setStyle(Qt::DashLine); break;
1283 case Pen::DotLine: op.setStyle(Qt::DotLine); break;
1284 case Pen::DashDotLine: op.setStyle(Qt::DashDotLine); break;
1285 case Pen::DashDotDotLine: op.setStyle(Qt::DashDotDotLine); break;
1286 case Pen::DoubleLine: op.setStyle(Qt::SolidLine); break; // TODO
1287 }
1288
1289 op.setColor(pen.color);
1290
1291 return op;
1292 }
1293 }
1294
insertPictureManifest(const QString & fileName)1295 void ExcelImport::Private::insertPictureManifest(const QString& fileName)
1296 {
1297 QString mimeType;
1298 const QString extension = fileName.right(fileName.size() - fileName.lastIndexOf('.') - 1);
1299
1300 if( extension == "gif" ) {
1301 mimeType = "image/gif";
1302 }
1303 else if( extension == "jpg" || extension == "jpeg"
1304 || extension == "jpe" || extension == "jfif" ) {
1305 mimeType = "image/jpeg";
1306 }
1307 else if( extension == "tif" || extension == "tiff" ) {
1308 mimeType = "image/tiff";
1309 }
1310 else if( extension == "png" ) {
1311 mimeType = "image/png";
1312 }
1313 else if( extension == "emf" ) {
1314 mimeType = "application/x-openoffice-wmf;windows_formatname=\"Image EMF\"";
1315 }
1316 else if( extension == "wmf" ) {
1317 mimeType = "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
1318 }
1319 else if( extension == "bmp" ) {
1320 mimeType = "image/bmp";
1321 }
1322
1323 manifestEntries.insert("Pictures/" + fileName, mimeType);
1324 }
1325
addManifestEntries(KoXmlWriter * manifestWriter)1326 void ExcelImport::Private::addManifestEntries(KoXmlWriter* manifestWriter)
1327 {
1328 QMap<QString, QString>::const_iterator iterator = manifestEntries.constBegin();
1329 QMap<QString, QString>::const_iterator end = manifestEntries.constEnd();
1330 while( iterator != end ) {
1331 manifestWriter->addManifestEntry(iterator.key(), iterator.value());
1332 ++iterator;
1333 }
1334 }
1335
1336
1337 // Updates the displayed progress information
addProgress(int addValue)1338 void ExcelImport::Private::addProgress(int addValue)
1339 {
1340 rowsCountDone += addValue;
1341 const int progress = int(rowsCountDone / qreal(rowsCountTotal) * ODFPROGRESS + 0.5 + SIDEWINDERPROGRESS);
1342 emit q->sigProgress(progress);
1343 }
1344
beginMemoryXmlWriter(const char * docElement)1345 KoXmlWriter* ExcelImport::Private::beginMemoryXmlWriter(const char* docElement)
1346 {
1347 QIODevice* d = new QBuffer;
1348 d->open(QIODevice::ReadWrite);
1349 KoXmlWriter* xml = new KoXmlWriter(d);
1350 xml->startDocument(docElement);
1351 xml->startElement(docElement);
1352 xml->addAttribute("xmlns:office", KoXmlNS::office);
1353 xml->addAttribute("xmlns:meta", KoXmlNS::meta);
1354 xml->addAttribute("xmlns:config", KoXmlNS::config);
1355 xml->addAttribute("xmlns:text", KoXmlNS::text);
1356 xml->addAttribute("xmlns:table", KoXmlNS::table);
1357 xml->addAttribute("xmlns:draw", KoXmlNS::draw);
1358 xml->addAttribute("xmlns:presentation", KoXmlNS::presentation);
1359 xml->addAttribute("xmlns:dr3d", KoXmlNS::dr3d);
1360 xml->addAttribute("xmlns:chart", KoXmlNS::chart);
1361 xml->addAttribute("xmlns:form", KoXmlNS::form);
1362 xml->addAttribute("xmlns:script", KoXmlNS::script);
1363 xml->addAttribute("xmlns:style", KoXmlNS::style);
1364 xml->addAttribute("xmlns:number", KoXmlNS::number);
1365 xml->addAttribute("xmlns:math", KoXmlNS::math);
1366 xml->addAttribute("xmlns:svg", KoXmlNS::svg);
1367 xml->addAttribute("xmlns:fo", KoXmlNS::fo);
1368 xml->addAttribute("xmlns:anim", KoXmlNS::anim);
1369 xml->addAttribute("xmlns:smil", KoXmlNS::smil);
1370 xml->addAttribute("xmlns:calligra", KoXmlNS::calligra);
1371 xml->addAttribute("xmlns:officeooo", KoXmlNS::officeooo);
1372 xml->addAttribute("xmlns:dc", KoXmlNS::dc);
1373 xml->addAttribute("xmlns:xlink", KoXmlNS::xlink);
1374 return xml;
1375 }
1376
endMemoryXmlWriter(KoXmlWriter * writer)1377 KoXmlDocument ExcelImport::Private::endMemoryXmlWriter(KoXmlWriter* writer)
1378 {
1379 writer->endElement();
1380 writer->endDocument();
1381 QBuffer* b = static_cast<QBuffer*>(writer->device());
1382
1383
1384 b->seek(0);
1385 KoXmlDocument doc;
1386 QString errorMsg; int errorLine, errorColumn;
1387 if (!doc.setContent(b, true, &errorMsg, &errorLine, &errorColumn)) {
1388 qCDebug(lcExcelImport) << errorMsg << errorLine << errorColumn;
1389 }
1390 delete b;
1391 delete writer;
1392 return doc;
1393 }
1394
processNumberFormats()1395 void ExcelImport::Private::processNumberFormats()
1396 {
1397 static const QString sNoStyle = QString::fromLatin1("NOSTYLE");
1398 QHash<QString, QString> dataStyleMap;
1399
1400 for (int i = 0; i < workbook->formatCount(); i++) {
1401 Format* f = workbook->format(i);
1402 QString& styleName = dataStyleMap[f->valueFormat()];
1403 if (styleName.isEmpty()) {
1404 KoGenStyle s = NumberFormatParser::parse(f->valueFormat(), dataStyles);
1405 if (s.type() != KoGenStyle::ParagraphAutoStyle) {
1406 styleName = dataStyles->insert(s, "N");
1407 } else {
1408 styleName = sNoStyle; // assign it a name anyway to prevent converting it twice
1409 }
1410 }
1411 }
1412
1413 KoXmlWriter *stylesXml = beginMemoryXmlWriter("office:styles");
1414 dataStyles->saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, stylesXml);
1415
1416 KoXmlDocument stylesDoc = endMemoryXmlWriter(stylesXml);
1417
1418 KoOdfStylesReader odfStyles;
1419 odfStyles.createStyleMap(stylesDoc, false);
1420
1421 for (int i = 0; i < workbook->formatCount(); i++) {
1422 Format* f = workbook->format(i);
1423 const QString& styleName = dataStyleMap[f->valueFormat()];
1424 if (styleName != sNoStyle) {
1425 Calligra::Sheets::Style& style = dataStyleCache[f->valueFormat()];
1426 if (style.isEmpty()) {
1427 Calligra::Sheets::Conditions conditions;
1428 Calligra::Sheets::Odf::loadDataStyle(&style, odfStyles, styleName, conditions, outputDoc->map()->styleManager(), outputDoc->map()->parser());
1429
1430 if (!conditions.isEmpty())
1431 dataStyleConditions[f->valueFormat()] = conditions;
1432 }
1433 }
1434 }
1435 }
1436
slotSigProgress(int progress)1437 void ExcelImport::slotSigProgress(int progress)
1438 {
1439 emit sigProgress(int(SIDEWINDERPROGRESS/100.0 * progress + 0.5));
1440 }
1441
1442 #include "ExcelImport.moc"
1443