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 <m.kruisselbrink@student.tue.nl>
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>
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.
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.
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.
24
#include <excelimporttoods.h>
25
#include <excelimporttoods.moc>
30
#include <QFontMetricsF>
33
#include <KoFilterChain.h>
36
#include <kgenericfactory.h>
38
#include <KoXmlWriter.h>
39
#include <KoOdfWriteStore.h>
40
#include <KoGenStyles.h>
41
#include <KoGenStyle.h>
42
#include <KoOdfNumberStyles.h>
45
#include <ChartExport.h>
46
#include <NumberFormatParser.h>
51
#include "ODrawClient.h"
52
#include "ImportUtils.h"
54
typedef KGenericFactory<ExcelImport> ExcelImportFactory;
55
K_EXPORT_COMPONENT_FACTORY(libexcelimport, ExcelImportFactory("kofficefilters"))
57
#define UNICODE_EUR 0x20AC
58
#define UNICODE_GBP 0x00A3
59
#define UNICODE_JPY 0x00A5
63
// qHash function to support hashing by Swinder::FormatFont instances.
64
static inline uint qHash(const Swinder::FormatFont& font)
66
// TODO: make this a better hash
67
return qHash(font.fontFamily()) ^ qRound(font.fontSize() * 100);
70
static qreal offset( unsigned long dimension, unsigned long offset, qreal factor ) {
71
return (float)dimension * (float)offset / factor;
74
static qreal columnWidth(Sheet* sheet, unsigned long col) {
75
if( sheet->column(col, false) )
76
return sheet->column(col)->width();
78
return sheet->defaultColWidth();
81
static qreal rowHeight(Sheet* sheet, unsigned long row) {
82
if( sheet->row(row, false) )
83
return sheet->row(row)->height();
85
return sheet->defaultRowHeight();
88
// Returns A for 1, B for 2, C for 3, etc.
89
static QString columnName(uint column)
94
for (unsigned limit = 26; column >= limit + offset; limit *= 26, digits++)
96
for (unsigned col = column - offset; digits; --digits, col /= 26)
97
s.prepend(QChar('A' + (col % 26)));
101
static QString encodeSheetName(const QString& name)
103
QString sheetName = name;
104
if (sheetName.contains(' ') || sheetName.contains('.') || sheetName.contains('\''))
105
sheetName = '\'' + sheetName.replace('\'', "''") + '\'';
109
static QString encodeAddress(const QString& sheetName, uint column, uint row)
111
return QString("%1.%2%3").arg(encodeSheetName(sheetName)).arg(columnName(column)).arg(row+1);
116
using namespace Swinder;
117
using namespace XlsUtils;
119
class ExcelImport::Private
129
KoGenStyles *mainStyles;
130
QList<QString> cellStyles;
131
QList<QString> rowStyles;
132
QList<QString> colStyles;
133
QList<QString> colCellStyles;
134
QList<QString> sheetStyles;
135
QHash<FormatFont, QString> fontStyles;
136
QString subScriptStyle, superScriptStyle;
137
QHash<QString, KoGenStyle> valueFormatCache;
138
QHash<CellFormatKey, QString> cellFormatCache;
139
QList<ChartExport*> charts;
140
QHash<Cell*, QByteArray> cellShapes;
141
QHash<Sheet*, QByteArray> sheetShapes;
143
QHash<Row*,int> rowsRepeatedHash;
144
int rowsRepeated(Row* row, int rowIndex);
146
int rowsCountTotal, rowsCountDone;
147
void addProgress(int addValue);
149
bool createStyles(KoStore* store, KoXmlWriter* manifestWriter, KoGenStyles* mainStyles);
150
bool createContent(KoOdfWriteStore* store);
151
bool createMeta(KoOdfWriteStore* store);
152
bool createSettings(KoOdfWriteStore* store);
154
int sheetFormatIndex;
155
int columnFormatIndex;
159
void processWorkbookForBody(KoOdfWriteStore* store, Workbook* workbook, KoXmlWriter* xmlWriter);
160
void processWorkbookForStyle(Workbook* workbook, KoXmlWriter* xmlWriter);
161
void processSheetForBody(KoOdfWriteStore* store, Sheet* sheet, KoXmlWriter* xmlWriter);
162
void processSheetForStyle(Sheet* sheet, KoXmlWriter* xmlWriter);
163
void processSheetForHeaderFooter(Sheet* sheet, KoXmlWriter* writer);
164
void processHeaderFooterStyle(const QString& text, KoXmlWriter* xmlWriter);
165
void processColumnForBody(Sheet* sheet, int columnIndex, KoXmlWriter* xmlWriter, unsigned& outlineLevel);
166
void processColumnForStyle(Sheet* sheet, int columnIndex, KoXmlWriter* xmlWriter);
167
int processRowForBody(KoOdfWriteStore* store, Sheet* sheet, int rowIndex, KoXmlWriter* xmlWriter, unsigned& outlineLevel);
168
int processRowForStyle(Sheet* sheet, int rowIndex, KoXmlWriter* xmlWriter);
169
void processCellForBody(KoOdfWriteStore* store, Cell* cell, int rowsRepeat, KoXmlWriter* xmlWriter);
170
void processCellForStyle(Cell* cell, KoXmlWriter* xmlWriter);
171
QString processCellFormat(const Format* format, const QString& formula = QString());
172
QString processRowFormat(Format* format, const QString& breakBefore = QString(), int rowRepeat = 1, double rowHeight = -1);
173
void processFormat(const Format* format, KoGenStyle& style);
174
QString processValueFormat(const QString& valueFormat);
175
void processFontFormat(const FormatFont& font, KoGenStyle& style, bool allProps = false);
176
void processCharts(KoXmlWriter* manifestWriter);
178
void createDefaultColumnStyle( Sheet* sheet );
179
void processSheetBackground(Sheet* sheet, KoGenStyle& style);
180
void addManifestEntries(KoXmlWriter* ManifestWriter);
181
void insertPictureManifest(PictureObject* picture);
183
bool isDateFormat(const QString& valueFormat);
185
QList<QString> defaultColumnStyles;
186
int defaultColumnStyleIndex;
187
QMap<QString,QString> manifestEntries;
190
ExcelImport::ExcelImport(QObject* parent, const QStringList&)
196
ExcelImport::~ExcelImport()
201
KoFilter::ConversionStatus ExcelImport::convert(const QByteArray& from, const QByteArray& to)
203
if (from != "application/vnd.ms-excel")
204
return KoFilter::NotImplemented;
206
if (to != "application/vnd.oasis.opendocument.spreadsheet")
207
return KoFilter::NotImplemented;
209
d->inputFile = m_chain->inputFile();
210
d->outputFile = m_chain->outputFile();
212
// create output store
213
d->storeout = KoStore::createStore(d->outputFile, KoStore::Write,
214
"application/vnd.oasis.opendocument.spreadsheet", KoStore::Zip);
215
if (!d->storeout || d->storeout->bad()) {
216
kWarning() << "Couldn't open the requested file.";
219
return KoFilter::FileNotFound;
224
// Tell KoStore not to touch the file names
225
d->storeout->disallowNameExpansion();
228
d->workbook = new Swinder::Workbook(d->storeout);
229
connect(d->workbook, SIGNAL(sigProgress(int)), this, SIGNAL(sigProgress(int)));
230
if (!d->workbook->load(d->inputFile.toLocal8Bit())) {
233
return KoFilter::StupidError;
236
if (d->workbook->isPasswordProtected()) {
239
return KoFilter::PasswordProtected;
242
emit sigProgress(-1);
245
d->styles = new KoGenStyles();
246
d->mainStyles = new KoGenStyles();
248
KoOdfWriteStore oasisStore(d->storeout);
249
KoXmlWriter* manifestWriter = oasisStore.manifestWriter("application/vnd.oasis.opendocument.spreadsheet");
251
// header and footer are read from each sheet and saved in styles
252
// So creating content before styles
253
// store document content
254
if (!d->createContent(&oasisStore)) {
255
kWarning() << "Couldn't open the file 'content.xml'.";
258
return KoFilter::CreationError;
261
// store document styles
262
if (!d->createStyles(d->storeout, manifestWriter, d->mainStyles)) {
263
kWarning() << "Couldn't open the file 'styles.xml'.";
266
return KoFilter::CreationError;
269
// store meta content
270
if (!d->createMeta(&oasisStore)) {
271
kWarning() << "Couldn't open the file 'meta.xml'.";
274
return KoFilter::CreationError;
278
if (!d->createSettings(&oasisStore)) {
279
kWarning() << "Couldn't open the file 'settings.xml'.";
282
return KoFilter::CreationError;
285
manifestWriter->addManifestEntry("meta.xml", "text/xml");
286
manifestWriter->addManifestEntry("styles.xml", "text/xml");
287
manifestWriter->addManifestEntry("content.xml", "text/xml");
288
manifestWriter->addManifestEntry("settings.xml", "text/xml");
290
d->processCharts(manifestWriter);
291
d->addManifestEntries(manifestWriter);
292
oasisStore.closeManifestWriter();
297
delete d->mainStyles;
299
d->inputFile.clear();
300
d->outputFile.clear();
304
d->cellStyles.clear();
305
d->rowStyles.clear();
306
d->colStyles.clear();
307
d->colCellStyles.clear();
308
d->sheetStyles.clear();
310
emit sigProgress(100);
314
// Updates the displayed progress information
315
void ExcelImport::Private::addProgress(int addValue)
317
rowsCountDone += addValue;
318
const int progress = int(rowsCountDone / double(rowsCountTotal) * 100.0 + 0.5);
319
workbook->emitProgress(progress);
322
int ExcelImport::Private::rowsRepeated(Row* row, int rowIndex)
324
if(rowsRepeatedHash.contains(row))
325
return rowsRepeatedHash[row];
326
// a row does usually at least repeat itself
328
// find the column of the rightmost cell (if any)
329
int lastCol = row->sheet()->maxCellsInRow(rowIndex);
330
// find repeating rows by forward searching
331
const unsigned rowCount = qMin(maximalRowCount, row->sheet()->maxRow());
332
for (unsigned i = rowIndex + 1; i <= rowCount; ++i) {
333
Row *nextRow = row->sheet()->row(i, false);
335
if (*row != *nextRow) break; // do the rows have the same properties?
336
const int nextLastCol = row->sheet()->maxCellsInRow(i);
337
if (lastCol != nextLastCol) break;
338
bool cellsAreSame = true;
339
for(int c = 0; c <= lastCol; ++c) {
340
Cell* c1 = row->sheet()->cell(c, row->index(), false);
341
Cell* c2 = nextRow->sheet()->cell(c, nextRow->index(), false);
342
if (!c1 != !c2 || (c1 && *c1 != *c2)) {
343
cellsAreSame = false;
344
break; // job done, abort loop
347
if (!cellsAreSame) break;
350
rowsRepeatedHash[row] = repeat; // cache the result
354
// Writes the spreadsheet content into the content.xml
355
bool ExcelImport::Private::createContent(KoOdfWriteStore* store)
357
KoXmlWriter* bodyWriter = store->bodyWriter();
358
KoXmlWriter* contentWriter = store->contentWriter();
359
if (!bodyWriter || !contentWriter)
362
if(workbook->password() != 0) {
363
contentWriter->addAttribute("table:structure-protected-excel", "true");
364
contentWriter->addAttribute("table:protection-key-excel" , uint(workbook->password()));
367
// FIXME this is dummy and hardcoded, replace with real font names
368
contentWriter->startElement("office:font-face-decls");
369
contentWriter->startElement("style:font-face");
370
contentWriter->addAttribute("style:name", "Arial");
371
contentWriter->addAttribute("svg:font-family", "Arial");
372
contentWriter->endElement(); // style:font-face
373
contentWriter->startElement("style:font-face");
374
contentWriter->addAttribute("style:name", "Times New Roman");
375
contentWriter->addAttribute("svg:font-family", "'Times New Roman'");
376
contentWriter->endElement(); // style:font-face
377
contentWriter->endElement(); // office:font-face-decls
380
defaultColumnStyleIndex = 0;
381
// office:automatic-styles
382
processWorkbookForStyle(workbook, contentWriter);
383
styles->saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, contentWriter);
385
// important: reset all indexes
386
sheetFormatIndex = 0;
387
columnFormatIndex = 0;
393
bodyWriter->startElement("office:body");
394
processWorkbookForBody(store, workbook, bodyWriter);
395
bodyWriter->endElement(); // office:body
397
return store->closeContentWriter();
402
// Writes the styles.xml
403
bool ExcelImport::Private::createStyles(KoStore* store, KoXmlWriter* manifestWriter, KoGenStyles* mainStyles)
405
Q_UNUSED(manifestWriter);
406
if (!store->open("styles.xml"))
408
KoStoreDevice dev(store);
409
KoXmlWriter* stylesWriter = new KoXmlWriter(&dev);
411
stylesWriter->startDocument("office:document-styles");
412
stylesWriter->startElement("office:document-styles");
413
stylesWriter->addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0");
414
stylesWriter->addAttribute("xmlns:style", "urn:oasis:names:tc:opendocument:xmlns:style:1.0");
415
stylesWriter->addAttribute("xmlns:text", "urn:oasis:names:tc:opendocument:xmlns:text:1.0");
416
stylesWriter->addAttribute("xmlns:table", "urn:oasis:names:tc:opendocument:xmlns:table:1.0");
417
stylesWriter->addAttribute("xmlns:draw", "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0");
418
stylesWriter->addAttribute("xmlns:fo", "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0");
419
stylesWriter->addAttribute("xmlns:svg", "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0");
420
stylesWriter->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
421
stylesWriter->addAttribute("xmlns:chart", "urn:oasis:names:tc:opendocument:xmlns:chart:1.0");
422
stylesWriter->addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
423
stylesWriter->addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
424
stylesWriter->addAttribute("xmlns:number", "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0");
425
//stylesWriter->addAttribute("xmlns:dr3d", "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0");
426
stylesWriter->addAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML");
427
stylesWriter->addAttribute("xmlns:of", "urn:oasis:names:tc:opendocument:xmlns:of:1.2");
428
stylesWriter->addAttribute("office:version", "1.2");
430
mainStyles->saveOdfStyles(KoGenStyles::MasterStyles, stylesWriter);
431
mainStyles->saveOdfStyles(KoGenStyles::DocumentStyles, stylesWriter); // office:style
432
mainStyles->saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, stylesWriter); // office:automatic-styles
434
stylesWriter->endElement(); // office:document-styles
435
stylesWriter->endDocument();
438
return store->close();
441
// Writes meta-informations into the meta.xml
442
bool ExcelImport::Private::createMeta(KoOdfWriteStore* store)
444
if (!store->store()->open("meta.xml"))
447
KoStoreDevice dev(store->store());
448
KoXmlWriter* metaWriter = new KoXmlWriter(&dev);
449
metaWriter->startDocument("office:document-meta");
450
metaWriter->startElement("office:document-meta");
451
metaWriter->addAttribute("xmlns:office", "urn:oasis:names:tc:opendocument:xmlns:office:1.0");
452
metaWriter->addAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
453
metaWriter->addAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/");
454
metaWriter->addAttribute("xmlns:meta", "urn:oasis:names:tc:opendocument:xmlns:meta:1.0");
455
metaWriter->startElement("office:meta");
457
if (workbook->hasProperty(Workbook::PIDSI_TITLE)) {
458
metaWriter->startElement("dc:title");
459
metaWriter->addTextNode(workbook->property(Workbook::PIDSI_TITLE).toString());
460
metaWriter->endElement();
462
if (workbook->hasProperty(Workbook::PIDSI_SUBJECT)) {
463
metaWriter->startElement("dc:subject", false);
464
metaWriter->addTextNode(workbook->property(Workbook::PIDSI_SUBJECT).toString());
465
metaWriter->endElement();
467
if (workbook->hasProperty(Workbook::PIDSI_AUTHOR)) {
468
metaWriter->startElement("dc:creator", false);
469
metaWriter->addTextNode(workbook->property(Workbook::PIDSI_AUTHOR).toString());
470
metaWriter->endElement();
472
if (workbook->hasProperty(Workbook::PIDSI_KEYWORDS)) {
473
metaWriter->startElement("meta:keyword", false);
474
metaWriter->addTextNode(workbook->property(Workbook::PIDSI_KEYWORDS).toString());
475
metaWriter->endElement();
477
if (workbook->hasProperty(Workbook::PIDSI_COMMENTS)) {
478
metaWriter->startElement("meta:comments", false);
479
metaWriter->addTextNode(workbook->property(Workbook::PIDSI_COMMENTS).toString());
480
metaWriter->endElement();
482
if (workbook->hasProperty(Workbook::PIDSI_REVNUMBER)) {
483
metaWriter->startElement("meta:editing-cycles", false);
484
metaWriter->addTextNode(workbook->property(Workbook::PIDSI_REVNUMBER).toString());
485
metaWriter->endElement();
487
if (workbook->hasProperty(Workbook::PIDSI_LASTPRINTED_DTM)) {
488
metaWriter->startElement("dc:print-date", false);
489
metaWriter->addTextNode(workbook->property(Workbook::PIDSI_LASTPRINTED_DTM).toString());
490
metaWriter->endElement();
492
if (workbook->hasProperty(Workbook::PIDSI_CREATE_DTM)) {
493
metaWriter->startElement("meta:creation-date", false);
494
metaWriter->addTextNode(workbook->property(Workbook::PIDSI_CREATE_DTM).toString());
495
metaWriter->endElement();
497
if (workbook->hasProperty(Workbook::PIDSI_LASTSAVED_DTM)) {
498
metaWriter->startElement("dc:date", false);
499
metaWriter->addTextNode(workbook->property(Workbook::PIDSI_LASTSAVED_DTM).toString());
500
metaWriter->endElement();
503
//if( workbook->hasProperty( Workbook::PIDSI_TEMPLATE ) ) metaWriter->addAttribute( "dc:", workbook->property( Workbook::PIDSI_TEMPLATE ).toString() );
504
//if( workbook->hasProperty( Workbook::PIDSI_LASTAUTHOR ) ) metaWriter->addAttribute( "dc:", workbook->property( Workbook::PIDSI_LASTAUTHOR ).toString() );
505
//if( workbook->hasProperty( Workbook::PIDSI_EDITTIME ) ) metaWriter->addAttribute( "dc:date", workbook->property( Workbook::PIDSI_EDITTIME ).toString() );
507
metaWriter->endElement(); // office:meta
508
metaWriter->endElement(); // office:document-meta
509
metaWriter->endDocument();
512
return store->store()->close();
515
// Writes configuration-settings into the settings.xml
516
bool ExcelImport::Private::createSettings(KoOdfWriteStore* store)
518
if (!store->store()->open("settings.xml"))
521
KoStoreDevice dev(store->store());
522
KoXmlWriter* settingsWriter = KoOdfWriteStore::createOasisXmlWriter(&dev, "office:document-settings");
523
settingsWriter->startElement("office:settings");
524
settingsWriter->startElement("config:config-item-set");
525
settingsWriter->addAttribute("config:name", "view-settings");
530
settingsWriter->startElement("config:config-item-map-indexed");
531
settingsWriter->addAttribute("config:name", "Views");
532
settingsWriter->startElement("config:config-item-map-entry");
533
settingsWriter->addConfigItem("ViewId", QString::fromLatin1("View1"));
534
if(Sheet *sheet = workbook->sheet(workbook->activeTab()))
535
settingsWriter->addConfigItem("ActiveTable", sheet->name());
537
settingsWriter->startElement("config:config-item-map-named");
538
settingsWriter->addAttribute("config:name", "Tables");
539
for(uint i = 0; i < workbook->sheetCount(); ++i) {
540
Sheet* sheet = workbook->sheet(i);
541
settingsWriter->startElement("config:config-item-map-entry");
542
settingsWriter->addAttribute("config:name", sheet->name());
543
QPoint point = sheet->firstVisibleCell();
544
settingsWriter->addConfigItem("CursorPositionX", point.x());
545
settingsWriter->addConfigItem("CursorPositionY", point.y());
546
//TODO how should we replace these settings?
547
// settingsWriter->addConfigItem("xOffset", columnWidth(sheet,point.x()));
548
// settingsWriter->addConfigItem("yOffset", rowHeight(sheet,point.y()));
549
settingsWriter->addConfigItem("ShowZeroValues", sheet->showZeroValues());
550
settingsWriter->addConfigItem("ShowGrid", sheet->showGrid());
551
settingsWriter->addConfigItem("FirstLetterUpper", false);
552
settingsWriter->addConfigItem("ShowFormulaIndicator", false);
553
settingsWriter->addConfigItem("ShowCommentIndicator", true);
554
settingsWriter->addConfigItem("ShowPageBorders", sheet->isPageBreakViewEnabled()); // best match kspread provides
555
settingsWriter->addConfigItem("lcmode", false);
556
settingsWriter->addConfigItem("autoCalc", sheet->autoCalc());
557
settingsWriter->addConfigItem("ShowColumnNumber", false);
558
settingsWriter->endElement();
560
settingsWriter->endElement(); // config:config-item-map-named
562
settingsWriter->endElement(); // config:config-item-map-entry
563
settingsWriter->endElement(); // config:config-item-map-indexed
564
settingsWriter->endElement(); // config:config-item-set
566
settingsWriter->endElement(); // office:settings
567
settingsWriter->endElement(); // Root:element
568
settingsWriter->endDocument();
569
delete settingsWriter;
570
return store->store()->close();
573
// Processes the workbook content. The workbook is the top-level element for content.
574
void ExcelImport::Private::processWorkbookForBody(KoOdfWriteStore* store, Workbook* workbook, KoXmlWriter* xmlWriter)
576
if (!workbook) return;
577
if (!xmlWriter) return;
579
xmlWriter->startElement("office:spreadsheet");
581
// count the number of rows in total to provide a good progress value
582
rowsCountTotal = rowsCountDone = 0;
583
for (unsigned i = 0; i < workbook->sheetCount(); i++) {
584
Sheet* sheet = workbook->sheet(i);
585
rowsCountTotal += qMin(maximalRowCount, sheet->maxRow()) * 2; // double cause we will count them 2 times, once for styles and once for content
588
// now start the whole work
589
for (unsigned i = 0; i < workbook->sheetCount(); i++) {
590
Sheet* sheet = workbook->sheet(i);
591
processSheetForBody(store, sheet, xmlWriter);
594
std::map<std::pair<unsigned, QString>, QString> &namedAreas = workbook->namedAreas();
595
if(namedAreas.size() > 0) {
596
xmlWriter->startElement("table:named-expressions");
597
for(std::map<std::pair<unsigned, QString>, QString>::iterator it = namedAreas.begin(); it != namedAreas.end(); it++) {
598
xmlWriter->startElement("table:named-range");
599
xmlWriter->addAttribute("table:name", it->first.second ); // e.g. "My Named Range"
600
QString range = it->second;
601
if(range.startsWith('[') && range.endsWith(']'))
602
range = range.mid(1, range.length() - 2);
603
xmlWriter->addAttribute("table:cell-range-address", range); // e.g. "$Sheet1.$B$2:.$B$3"
604
xmlWriter->endElement();//[Sheet1.$B$2:$B$3]
606
xmlWriter->endElement();
609
bool openedDBRanges = false;
611
for (unsigned i = 0; i < workbook->sheetCount(); i++) {
612
QList<QRect> filters = workbook->filterRanges(i);
613
QString sheetName = workbook->sheet(i)->name();
614
if (filters.size()) {
615
if (!openedDBRanges) xmlWriter->startElement("table:database-ranges");
616
openedDBRanges = true;
618
foreach (const QRect& filter, filters) {
619
QString sRange(encodeAddress(sheetName, filter.left(), filter.top()));
621
sRange.append(encodeAddress(sheetName, filter.right(), workbook->sheet(i)->maxRow()));
622
xmlWriter->startElement("table:database-range");
623
xmlWriter->addAttribute("table:name", QString("excel-database-%1").arg(rangeId++));
624
xmlWriter->addAttribute("table:display-filter-buttons", "true");
625
xmlWriter->addAttribute("table:target-range-address", sRange);
626
xmlWriter->endElement(); // table:database-range
630
if (openedDBRanges) xmlWriter->endElement(); // table:database-ranges
632
xmlWriter->endElement(); // office:spreadsheet
635
// Processes the workbook styles. The workbook is the top-level element for content.
636
void ExcelImport::Private::processWorkbookForStyle(Workbook* workbook, KoXmlWriter* xmlWriter)
638
if (!workbook) return;
639
if (!xmlWriter) return;
641
QString contentElement;
642
QString masterStyleName("Default");
643
QString pageLayoutStyleName("Mpm");
645
KoGenStyle pageLayoutStyle(KoGenStyle::PageLayoutStyle);
646
pageLayoutStyle.addProperty("style:writing-mode", "lr-tb");
649
buf.open(QIODevice::WriteOnly);
650
KoXmlWriter writer(&buf);
652
//Hardcoded page-layout
653
writer.startElement("style:header-style");
654
writer.startElement("style:header-footer-properties");
655
writer.addAttribute("fo:min-height", "20pt");
656
writer.addAttribute("fo:margin-left", "0pt");
657
writer.addAttribute("fo:margin-right", "0pt");
658
writer.addAttribute("fo:margin-bottom", "10pt");
662
writer.startElement("style:footer-style");
663
writer.startElement("style:header-footer-properties");
664
writer.addAttribute("fo:min-height", "20pt");
665
writer.addAttribute("fo:margin-left", "0pt");
666
writer.addAttribute("fo:margin-right", "0pt");
667
writer.addAttribute("fo:margin-top", "10pt");
670
QString pageLyt = QString::fromUtf8(buf.buffer(), buf.buffer().size());
674
pageLayoutStyle.addProperty("1header-footer-style", pageLyt, KoGenStyle::StyleChildElement);
675
pageLayoutStyleName = mainStyles->insert(pageLayoutStyle, pageLayoutStyleName, KoGenStyles::DontAddNumberToName);
677
for (unsigned i = 0; i < workbook->sheetCount(); i++) {
678
Sheet* sheet = workbook->sheet(i);
679
processSheetForStyle(sheet, xmlWriter);
681
buf.open(QIODevice::WriteOnly);
682
processSheetForHeaderFooter(workbook->sheet(0), &writer);
683
contentElement = QString::fromUtf8(buf.buffer(), buf.buffer().size());
685
QString childElementName = QString::number(i).append("master-style");
686
KoGenStyle masterStyle(KoGenStyle::MasterPageStyle);
687
masterStyle.addChildElement(childElementName, contentElement);
688
masterStyle.addAttribute("style:page-layout-name", pageLayoutStyleName);
690
masterStyleName = mainStyles->insert(masterStyle, masterStyleName, KoGenStyles::DontAddNumberToName);
691
masterStyle.addAttribute("style:name", masterStyleName);
695
// Processes a sheet.
696
void ExcelImport::Private::processSheetForBody(KoOdfWriteStore* store, Sheet* sheet, KoXmlWriter* xmlWriter)
699
if (!xmlWriter) return;
701
xmlWriter->startElement("table:table");
703
xmlWriter->addAttribute("table:name", sheet->name());
704
xmlWriter->addAttribute("table:print", "false");
705
xmlWriter->addAttribute("table:style-name", sheetStyles[sheetFormatIndex]);
708
if(sheet->password() != 0) {
710
//xmlWriter->addAttribute("table:protected", "true");
711
//xmlWriter->addAttribute("table:protection-key", uint(sheet->password()));
714
if (!sheet->drawObjects().isEmpty()) {
715
xmlWriter->startElement("table:shapes");
716
xmlWriter->addCompleteElement(sheetShapes[sheet]);
717
xmlWriter->endElement(); // table:shapes
721
const unsigned columnCount = qMin(maximalColumnCount, sheet->maxColumn());
722
unsigned outlineLevel = 0;
723
for (unsigned i = 0; i <= columnCount; ++i) {
724
processColumnForBody(sheet, i, xmlWriter, outlineLevel);
726
while (outlineLevel > 0) {
727
xmlWriter->endElement(); // table:table-column-group
731
// in odf default-cell-style's only apply to cells/rows/columns that are present in the file while in Excel
732
// row/column styles should apply to all cells in that row/column. So, try to fake that behavior by writting
733
// a number-columns-repeated to apply the styles/formattings to "all" columns.
734
if (columnCount < maximalColumnCount-1) {
735
xmlWriter->startElement("table:table-column");
736
xmlWriter->addAttribute("table:style-name", defaultColumnStyles[defaultColumnStyleIndex]);
737
xmlWriter->addAttribute("table:number-columns-repeated", maximalColumnCount - 1 - columnCount);
738
xmlWriter->endElement();
742
const unsigned rowCount = qMin(maximalRowCount, sheet->maxRow());
743
for (unsigned i = 0; i <= rowCount;) {
744
i += processRowForBody(store, sheet, i, xmlWriter, outlineLevel);
746
while (outlineLevel > 0) {
747
xmlWriter->endElement(); // table:table-row-group
751
// same we did above with columns is also needed for rows.
752
if(rowCount < maximalRowCount-1) {
753
xmlWriter->startElement("table:table-row");
754
xmlWriter->addAttribute("table:number-rows-repeated", maximalRowCount - 1 - rowCount);
755
xmlWriter->endElement();
758
xmlWriter->endElement(); // table:table
759
++defaultColumnStyleIndex;
762
static QRectF getRect(const MSO::OfficeArtFSPGR &r)
764
return QRect(r.xLeft, r.yTop, r.xRight - r.xLeft, r.yBottom - r.yTop);
767
// Processes styles for a sheet.
768
void ExcelImport::Private::processSheetForStyle(Sheet* sheet, KoXmlWriter* xmlWriter)
771
if (!xmlWriter) return;
773
KoGenStyle style(KoGenStyle::TableAutoStyle, "table");
774
style.addAttribute("style:master-page-name", "Default");
776
style.addProperty("table:display", sheet->visible() ? "true" : "false");
777
style.addProperty("table:writing-mode", "lr-tb");
779
processSheetBackground(sheet, style);
781
QString styleName = styles->insert(style, "ta");
782
sheetStyles.append(styleName);
784
createDefaultColumnStyle( sheet );
786
const unsigned columnCount = qMin(maximalColumnCount, sheet->maxColumn());
787
for (unsigned i = 0; i <= columnCount; ++i) {
788
processColumnForStyle(sheet, i, xmlWriter);
791
const unsigned rowCount = qMin(maximalRowCount, sheet->maxRow());
792
for (unsigned i = 0; i <= rowCount;) {
793
i += processRowForStyle(sheet, i, xmlWriter);
796
QList<OfficeArtObject*> objects = sheet->drawObjects();
797
int drawObjectGroups = sheet->drawObjectsGroupCount();
798
if (!objects.empty() || drawObjectGroups) {
799
ODrawClient client = ODrawClient(sheet);
800
ODrawToOdf odraw(client);
803
Writer writer(xml, *styles, false);
804
foreach (const OfficeArtObject* o, objects) {
805
client.setShapeText(o->text());
806
odraw.processDrawingObject(o->object(), writer);
808
for (int i = 0; i < drawObjectGroups; ++i) {
809
xml.startElement("draw:g");
811
const MSO::OfficeArtSpgrContainer& group = sheet->drawObjectsGroup(i);
812
const MSO::OfficeArtSpContainer* first = group.rgfb.first().anon.get<MSO::OfficeArtSpContainer>();
813
if (first && first->clientAnchor && first->shapeGroup) {
814
QRectF oldCoords = client.getGlobalRect(*first->clientAnchor);
815
QRectF newCoords = getRect(*first->shapeGroup);
816
Writer transw = writer.transform(oldCoords, newCoords);
817
foreach (const OfficeArtObject* o, sheet->drawObjects(i)) {
818
client.setShapeText(o->text());
819
odraw.processDrawingObject(o->object(), transw);
822
foreach (const OfficeArtObject* o, sheet->drawObjects(i)) {
823
client.setShapeText(o->text());
824
odraw.processDrawingObject(o->object(), writer);
827
xml.endElement(); // draw:g
829
sheetShapes[sheet] = b.data();
830
//qDebug() << b.data();
834
// Processes headers and footers for a sheet.
835
void ExcelImport::Private::processSheetForHeaderFooter(Sheet* sheet, KoXmlWriter* xmlWriter)
838
if (!xmlWriter) return;
840
xmlWriter->startElement("style:header");
841
if (!sheet->leftHeader().isEmpty()) {
842
xmlWriter->startElement("style:region-left");
843
xmlWriter->startElement("text:p");
844
processHeaderFooterStyle(sheet->leftHeader(), xmlWriter);
845
xmlWriter->endElement();
846
xmlWriter->endElement();
848
if (!sheet->centerHeader().isEmpty()) {
849
xmlWriter->startElement("style:region-center");
850
xmlWriter->startElement("text:p");
851
processHeaderFooterStyle(sheet->centerHeader(), xmlWriter);
852
xmlWriter->endElement();
853
xmlWriter->endElement();
855
if (!sheet->rightHeader().isEmpty()) {
856
xmlWriter->startElement("style:region-right");
857
xmlWriter->startElement("text:p");
858
processHeaderFooterStyle(sheet->rightHeader(), xmlWriter);
859
xmlWriter->endElement();
860
xmlWriter->endElement();
862
xmlWriter->endElement();
864
xmlWriter->startElement("style:footer");
865
if (!sheet->leftFooter().isEmpty()) {
866
xmlWriter->startElement("style:region-left");
867
xmlWriter->startElement("text:p");
868
processHeaderFooterStyle(sheet->leftFooter(), xmlWriter);
869
xmlWriter->endElement();
870
xmlWriter->endElement();
872
if (!sheet->centerFooter().isEmpty()) {
873
xmlWriter->startElement("style:region-center");
874
xmlWriter->startElement("text:p");
875
processHeaderFooterStyle(sheet->centerFooter(), xmlWriter);
876
xmlWriter->endElement();
877
xmlWriter->endElement();
879
if (!sheet->rightFooter().isEmpty()) {
880
xmlWriter->startElement("style:region-right");
881
xmlWriter->startElement("text:p");
882
processHeaderFooterStyle(sheet->rightFooter(), xmlWriter);
883
xmlWriter->endElement();
884
xmlWriter->endElement();
886
xmlWriter->endElement();
889
// Processes the styles of a headers and footers for a sheet.
890
void ExcelImport::Private::processHeaderFooterStyle(const QString& text, KoXmlWriter* xmlWriter)
893
bool skipUnsupported = false;
895
int pos = text.indexOf('&');
896
int len = text.length();
897
if ((pos < 0) && (text.length() > 0)) // If ther is no &
898
xmlWriter->addTextNode(text);
899
else if (pos > 0) // Some text and '&'
900
xmlWriter->addTextNode(text.mid(0, pos - 1));
903
switch (text[pos + 1].unicode()) {
905
xmlWriter->startElement("text:date");
906
xmlWriter->addTextNode(QDate::currentDate().toString("DD/MM/YYYY"));
907
xmlWriter->endElement();
910
xmlWriter->startElement("text:time");
911
xmlWriter->addTextNode(QTime::currentTime().toString("HH:MM:SS"));
912
xmlWriter->endElement();
915
xmlWriter->startElement("text:page-number");
916
xmlWriter->addTextNode("1");
917
xmlWriter->endElement();
920
xmlWriter->startElement("text:page-count");
921
xmlWriter->addTextNode("999");
922
xmlWriter->endElement();
925
xmlWriter->startElement("text:title");
926
xmlWriter->addTextNode("???");
927
xmlWriter->endElement();
930
xmlWriter->startElement("text:sheet-name");
931
xmlWriter->addTextNode("???");
932
xmlWriter->endElement();
936
skipUnsupported = true;
940
pos = text.indexOf('&', lastPos + 1);
941
if (!skipUnsupported && (pos > (lastPos + 1)))
942
xmlWriter->addTextNode(text.mid(lastPos + 2, (pos - lastPos - 2)));
943
else if (!skipUnsupported && (pos < 0)) //Remaining text
944
xmlWriter->addTextNode(text.mid(lastPos + 2, len - (lastPos + 2)));
946
skipUnsupported = false;
950
// Processes a column in a sheet.
951
void ExcelImport::Private::processColumnForBody(Sheet* sheet, int columnIndex, KoXmlWriter* xmlWriter, unsigned& outlineLevel)
953
Column* column = sheet->column(columnIndex, false);
955
if (!xmlWriter) return;
957
unsigned newOutlineLevel = column ? column->outlineLevel() : 0;
958
while (newOutlineLevel > outlineLevel) {
959
xmlWriter->startElement("table:table-column-group");
961
if (outlineLevel == newOutlineLevel && column->collapsed())
962
xmlWriter->addAttribute("table:display", "false");
964
while (newOutlineLevel < outlineLevel) {
965
xmlWriter->endElement(); // table:table-column-group
970
xmlWriter->startElement("table:table-column");
971
Q_ASSERT(defaultColumnStyleIndex < defaultColumnStyles.count());
972
xmlWriter->addAttribute("table:style-name", defaultColumnStyles[defaultColumnStyleIndex] );
973
xmlWriter->endElement();
976
Q_ASSERT(columnFormatIndex < colStyles.count());
977
Q_ASSERT(columnFormatIndex < colCellStyles.count());
978
const QString styleName = colStyles[columnFormatIndex];
979
const QString defaultStyleName = colCellStyles[columnFormatIndex];
982
xmlWriter->startElement("table:table-column");
983
xmlWriter->addAttribute("table:default-cell-style-name", defaultStyleName);
984
xmlWriter->addAttribute("table:visibility", column->visible() ? "visible" : "collapse");
985
//xmlWriter->addAttribute("table:number-columns-repeated", );
986
xmlWriter->addAttribute("table:style-name", styleName);
987
xmlWriter->endElement(); // table:table-column
990
// Processes the style of a column in a sheet.
991
void ExcelImport::Private::processColumnForStyle(Sheet* sheet, int columnIndex, KoXmlWriter* xmlWriter)
993
Column* column = sheet->column(columnIndex, false);
995
if (!xmlWriter) return;
998
KoGenStyle style(KoGenStyle::TableColumnAutoStyle, "table-column");
999
style.addProperty("fo:break-before", "auto");
1000
style.addPropertyPt("style:column-width", column->width());
1002
QString styleName = styles->insert(style, "co");
1003
colStyles.append(styleName);
1005
const Format* format = &column->format();
1006
QString cellStyleName = processCellFormat(format);
1007
colCellStyles.append(cellStyleName);
1010
// Processes a row in a sheet.
1011
int ExcelImport::Private::processRowForBody(KoOdfWriteStore* store, Sheet* sheet, int rowIndex, KoXmlWriter* xmlWriter, unsigned& outlineLevel)
1015
if (!xmlWriter) return repeat;
1016
Row *row = sheet->row(rowIndex, false);
1018
unsigned newOutlineLevel = row ? row->outlineLevel() : 0;
1019
while (newOutlineLevel > outlineLevel) {
1020
xmlWriter->startElement("table:table-row-group");
1022
if (outlineLevel == newOutlineLevel && row->collapsed())
1023
xmlWriter->addAttribute("table:display", "false");
1025
while (newOutlineLevel < outlineLevel) {
1026
xmlWriter->endElement(); // table:table-row-group
1032
xmlWriter->startElement("table:table-row");
1033
xmlWriter->endElement();
1036
if (!row->sheet()) return repeat;
1038
const QString styleName = rowStyles[rowFormatIndex];
1041
repeat = rowsRepeated(row, rowIndex);
1043
xmlWriter->startElement("table:table-row");
1044
xmlWriter->addAttribute("table:visibility", row->visible() ? "visible" : "collapse");
1045
xmlWriter->addAttribute("table:style-name", styleName);
1048
xmlWriter->addAttribute("table:number-rows-repeated", repeat);
1050
// find the column of the rightmost cell (if any)
1051
const int lastCol = row->sheet()->maxCellsInRow(rowIndex);
1053
while(i <= lastCol) {
1054
Cell* cell = row->sheet()->cell(i, row->index(), false);
1056
processCellForBody(store, cell, repeat, xmlWriter);
1057
i += cell->columnRepeat();
1058
} else { // empty cell
1059
xmlWriter->startElement("table:table-cell");
1060
xmlWriter->endElement();
1065
xmlWriter->endElement(); // table:table-row
1066
addProgress(repeat);
1070
// Processes the style of a row in a sheet.
1071
int ExcelImport::Private::processRowForStyle(Sheet* sheet, int rowIndex, KoXmlWriter* xmlWriter)
1074
Row* row = sheet->row(rowIndex, false);
1076
if (!row) return repeat;
1077
if (!row->sheet()) return repeat;
1078
if (!xmlWriter) return repeat;
1080
repeat = rowsRepeated(row, rowIndex);
1082
Format format = row->format();
1083
QString cellStyleName = processRowFormat(&format, "auto", repeat, row->height());
1084
rowStyles.append(cellStyleName);
1086
const int lastCol = row->sheet()->maxCellsInRow(rowIndex);
1087
for (int i = 0; i <= lastCol;) {
1088
Cell* cell = row->sheet()->cell(i, row->index(), false);
1090
processCellForStyle(cell, xmlWriter);
1091
i += cell->columnRepeat();
1092
} else { // row has no style
1097
addProgress(repeat);
1101
// Another form of conditional formats are those that define a different format
1102
// depending on the value. In following examples the different states are
1103
// splittet with a ; char, the first is usually used if the value is positive,
1104
// the second if the value if negavtive, the third if the value is invalid and
1105
// the forth defines a common formatting mask.
1106
// _("$"* #,##0.0000_);_("$"* \(#,##0.0000\);_("$"* "-"????_);_(@_)
1107
// _-[$₩-412]* #,##0.0000_-;\-[$₩-412]* #,##0.0000_-;_-[$₩-412]* "-"????_-;_-@_-
1108
// _ * #,##0_)[$€-1]_ ;_ * #,##0[$€-1]_ ;_ * "-"_)[$€-1]_ ;_ @_ "
1109
QString extractConditional(const QString &_text)
1111
const QString text = removeEscaped(_text);
1113
if (text.startsWith('_') && text.length() >= 3) {
1115
if (text[1] == '(') end = ')';
1116
else if (text[1] == '_') end = '_';
1117
else if (text[1] == ' ') end = ' ';
1118
else kDebug() << "Probably unhandled condition=" << text[1] << "in text=" << text;
1119
if (! end.isNull()) {
1121
QString regex = QString("^_%1(.*\"\\$\".*)%2;.*").arg(QString("\\%1").arg(text[1])).arg(QString("\\%1").arg(end));
1123
ex.setMinimal(true);
1124
if (ex.indexIn(text) >= 0) return ex.cap(1);
1127
QString regex = QString("^_%1(.*\\[\\$.*\\].*)%2;.*").arg(QString("\\%1").arg(text[1])).arg(QString("\\%1").arg(end));
1129
ex.setMinimal(true);
1130
if (ex.indexIn(text) >= 0) return ex.cap(1);
1135
if (text.startsWith('_')) {
1136
return text.split(";").first();
1142
// Currency or accounting format.
1144
// [$EUR]\ #,##0.00"
1145
// [$₩-412]* #,##0.0000
1147
static bool currencyFormat(const QString& valueFormat, QString *currencyVal = 0, QString * formatVal = 0)
1149
QString vf = extractConditional(valueFormat);
1151
// dollar is special cause it starts with a $
1152
QRegExp dollarRegEx("^\"\\$\"[*\\-\\s]*([#,]*[\\d]+(|.[#0]+)).*");
1153
if (dollarRegEx.indexIn(vf) >= 0) {
1154
if (currencyVal) *currencyVal = "$";
1155
if (formatVal) *formatVal = dollarRegEx.cap(1);
1159
// every other currency or accounting has a [$...] identifier
1160
QRegExp crRegEx("\\[\\$(.*)\\]");
1161
crRegEx.setMinimal(true);
1162
if (crRegEx.indexIn(vf) >= 0) {
1164
*currencyVal = crRegEx.cap(1);
1167
QRegExp vlRegEx("([#,]*[\\d]+(|.[#0]+))");
1168
*formatVal = vlRegEx.indexIn(vf) >= 0 ? vlRegEx.cap(1) : QString();
1176
// Checks if the as argument passed formatstring defines a date-format or not.
1177
bool ExcelImport::Private::isDateFormat(const QString& valueFormat)
1179
KoGenStyle& style = valueFormatCache[valueFormat];
1180
if (style.isEmpty())
1181
style = NumberFormatParser::parse( valueFormat );
1182
return style.type() == KoGenStyle::NumericDateStyle;
1185
static QByteArray convertCurrency(double currency, const QString& valueFormat)
1187
Q_UNUSED(valueFormat);
1188
return QByteArray::number(currency, 'g', 15);
1191
static QString convertDate(double serialNo, const QString& valueFormat)
1193
QString vf = valueFormat;
1194
QString locale = extractLocale(vf);
1195
Q_UNUSED(locale); //TODO http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
1196
Q_UNUSED(vf); //TODO
1198
// reference is midnight 30 Dec 1899
1199
QDateTime dt(QDate(1899, 12, 30));
1200
dt = dt.addMSecs((qint64)(serialNo * 86400 * 1000)); // TODO: we probably need double precision here
1202
//TODO atm we always return a datetime. This works great (time ignored if only date was defined) with KSpread but probably not with other customers...
1203
//return dd.toString("yyyy-MM-dd");
1204
return dt.toString("yyyy-MM-ddThh:mm:ss");
1207
static QString convertTime(double serialNo, const QString& valueFormat)
1209
QString vf = valueFormat;
1210
QString locale = extractLocale(vf);
1211
Q_UNUSED(locale); //TODO http://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
1212
Q_UNUSED(vf); //TODO
1214
// reference is midnight 30 Dec 1899
1216
tt = tt.addMSecs(qRound((serialNo - (int)serialNo) * 86400 * 1000));
1218
return tt.toString("'PT'hh'H'mm'M'ss'S'");
1221
static QByteArray convertFraction(double serialNo, const QString& valueFormat)
1223
Q_UNUSED(valueFormat);
1224
return QByteArray::number(serialNo, 'g', 15);
1227
QString cellFormula(Cell* cell)
1229
QString formula = cell->formula();
1230
if (!formula.isEmpty()) {
1231
if(formula.startsWith("ROUNDUP(") || formula.startsWith("ROUNDDOWN(") || formula.startsWith("ROUND(") || formula.startsWith("RAND(")) {
1232
// Special case Excel formulas that differ from OpenFormula
1233
formula.prepend("msoxl:=");
1234
} else if (!formula.isEmpty()) {
1235
formula.prepend("=");
1241
QString currencyValue(const QString &value)
1243
if (value.isEmpty()) return QString();
1244
if (value[0] == '$') return "USD";
1245
if (value[0] == QChar(UNICODE_EUR)) return "EUR";
1246
if (value[0] == QChar(UNICODE_GBP)) return "GBP";
1247
if (value[0] == QChar(UNICODE_JPY)) return "JPY";
1248
QRegExp symbolRegEx("^([^a-zA-Z0-9\\-_\\s]+)");
1249
if (symbolRegEx.indexIn(value) >= 0) return symbolRegEx.cap(1);
1253
// Processes a cell within a sheet.
1254
void ExcelImport::Private::processCellForBody(KoOdfWriteStore* store, Cell* cell, int rowsRepeat, KoXmlWriter* xmlWriter)
1257
if (!xmlWriter) return;
1259
if (cell->isCovered())
1260
xmlWriter->startElement("table:covered-table-cell");
1262
xmlWriter->startElement("table:table-cell");
1264
Q_ASSERT(cellFormatIndex >= 0 && cellFormatIndex < cellStyles.count());
1265
xmlWriter->addAttribute("table:style-name", cellStyles[cellFormatIndex]);
1268
if (cell->columnSpan() > 1)
1269
xmlWriter->addAttribute("table:number-columns-spanned", cell->columnSpan());
1270
if (cell->rowSpan() > 1)
1271
xmlWriter->addAttribute("table:number-rows-spanned", cell->rowSpan());
1272
if (cell->columnRepeat() > 1)
1273
xmlWriter->addAttribute("table:number-columns-repeated", cell->columnRepeat());
1275
const QString formula = cellFormula(cell);
1276
if (!formula.isEmpty())
1277
xmlWriter->addAttribute("table:formula", formula);
1279
Value value = cell->value();
1281
if (value.isBoolean()) {
1282
xmlWriter->addAttribute("office:value-type", "boolean");
1283
xmlWriter->addAttribute("office:boolean-value", value.asBoolean() ? "true" : "false");
1284
} else if (value.isFloat() || value.isInteger()) {
1285
const QString valueFormat = cell->format().valueFormat();
1287
if (isPercentageFormat(valueFormat)) {
1288
xmlWriter->addAttribute("office:value-type", "percentage");
1289
xmlWriter->addAttribute("office:value", value.asFloat());
1290
} else if (isDateFormat(valueFormat)) {
1291
const QString dateValue = convertDate(value.asFloat(), valueFormat);
1292
xmlWriter->addAttribute("office:value-type", "date");
1293
xmlWriter->addAttribute("office:date-value", dateValue);
1294
} else if (value.asFloat() < 1.0 && isTimeFormat(valueFormat)) {
1295
const QString timeValue = convertTime(value.asFloat(), valueFormat);
1296
xmlWriter->addAttribute("office:value-type", "time");
1297
xmlWriter->addAttribute("office:time-value", timeValue);
1298
} else if (isFractionFormat(valueFormat)) {
1299
const QString fractionValue = convertFraction(value.asFloat(), valueFormat);
1300
xmlWriter->addAttribute("office:value-type", "float");
1301
xmlWriter->addAttribute("office:value", fractionValue);
1302
} else { // fallback is the generic float format
1303
xmlWriter->addAttribute("office:value-type", "float");
1304
xmlWriter->addAttribute("office:value", value.asFloat());
1306
} else if (value.isText() || value.isError()) {
1307
QString str = value.asString();
1308
QString linkName, linkLocation;
1310
Hyperlink link = cell->hyperlink();
1312
linkLocation = link.location;
1313
if(!linkLocation.isEmpty()) {
1314
linkName = link.displayName.trimmed();
1315
if(linkName.isEmpty())
1317
str.clear(); // at Excel cells with links don't have additional text content
1320
if (linkLocation.isEmpty() && value.isString()) {
1321
xmlWriter->addAttribute("office:value-type", "string");
1322
if (!(cell->format().font().subscript() || cell->format().font().superscript()))
1323
xmlWriter->addAttribute("office:string-value", str);
1326
xmlWriter->startElement("text:p", false);
1328
if(!str.isEmpty()) {
1329
if (cell->format().font().subscript() || cell->format().font().superscript()) {
1330
xmlWriter->startElement("text:span");
1331
if (cell->format().font().subscript())
1332
xmlWriter->addAttribute("text:style-name", subScriptStyle);
1334
xmlWriter->addAttribute("text:style-name", superScriptStyle);
1337
if (value.isString()) {
1338
xmlWriter->addTextNode(str);
1341
std::map<unsigned, FormatFont> formatRuns = value.formatRuns();
1343
// add sentinel to list of format runs
1344
formatRuns[str.length()] = cell->format().font();
1348
for (std::map<unsigned, FormatFont>::iterator it = formatRuns.begin(); it != formatRuns.end(); ++it) {
1349
if (!style.isEmpty() && it->first > index) {
1350
xmlWriter->startElement("text:span");
1351
xmlWriter->addAttribute("text:style-name", style);
1353
if (it->first > index)
1354
xmlWriter->addTextNode(str.mid(index, it->first - index));
1355
if (!style.isEmpty() && it->first > index) {
1356
xmlWriter->endElement(); // text:span
1361
if (it->second == cell->format().font())
1364
style = fontStyles.value(it->second);
1369
if (cell->format().font().subscript() || cell->format().font().superscript())
1370
xmlWriter->endElement(); // text:span
1373
if (!linkName.isEmpty()) {
1374
xmlWriter->startElement("text:a");
1375
xmlWriter->addAttribute("xlink:href", linkLocation);
1376
const QString targetFrameName = link.targetFrameName;
1377
if (! targetFrameName.isEmpty())
1378
xmlWriter->addAttribute("office:target-frame-name", targetFrameName);
1379
xmlWriter->addTextNode(linkName);
1380
xmlWriter->endElement(); // text:a
1383
xmlWriter->endElement(); // text:p
1386
const QString note = cell->note();
1387
if (! note.isEmpty()) {
1388
xmlWriter->startElement("office:annotation");
1389
//xmlWriter->startElement("dc:creator");
1390
//xmlWriter->addTextNode(authorName); //TODO
1391
//xmlWriter->endElement(); // dc:creator
1392
xmlWriter->startElement("text:p");
1393
xmlWriter->addTextNode(note);
1394
xmlWriter->endElement(); // text:p
1395
xmlWriter->endElement(); // office:annotation
1399
foreach(PictureObject *picture, cell->pictures()) {
1400
Sheet* const sheet = cell->sheet();
1401
const unsigned long colL = picture->m_colL;
1402
const unsigned long dxL = picture->m_dxL;
1403
const unsigned long colR = picture->m_colR;
1404
const unsigned long dxR = picture->m_dxR;
1405
const unsigned long rwB = picture->m_rwB;
1406
const unsigned long dyT = picture->m_dyT;
1407
const unsigned long rwT = picture->m_rwT;
1408
const unsigned long dyB = picture->m_dyB;
1410
xmlWriter->startElement("draw:frame");
1411
//xmlWriter->addAttribute("draw:name", "Graphics 1");
1412
xmlWriter->addAttribute("table:end-cell-address", encodeAddress(sheet->name(), picture->m_colR, picture->m_rwB));
1413
xmlWriter->addAttributePt("table:end-x", offset(columnWidth(sheet, colR), dxR, 1024));
1414
xmlWriter->addAttributePt("table:end-y", offset(rowHeight(sheet, rwB), dyB, 256));
1415
xmlWriter->addAttribute("draw:z-index", "0");
1416
xmlWriter->addAttributePt("svg:x", offset(columnWidth(sheet, colL), dxL, 1024) );
1417
xmlWriter->addAttributePt("svg:y", offset(rowHeight(sheet, rwT), dyT, 256));
1419
xmlWriter->startElement("draw:image");
1420
xmlWriter->addAttribute("xlink:href", "Pictures/" + picture->fileName());
1421
xmlWriter->addAttribute("xlink:type", "simple");
1422
xmlWriter->addAttribute("xlink:show", "embed");
1423
xmlWriter->addAttribute("xlink:actuate", "onLoad");
1424
xmlWriter->endElement(); // draw:image
1425
xmlWriter->endElement(); // draw:frame
1427
insertPictureManifest(picture);
1431
foreach(ChartObject *chart, cell->charts()) {
1432
Sheet* const sheet = cell->sheet();
1433
if(chart->m_chart->m_impl==0) {
1434
kDebug() << "Invalid chart to be created, no implementation.";
1438
ChartExport *c = new ChartExport(chart->m_chart);
1439
c->m_href = QString("Chart%1").arg(this->charts.count()+1);
1440
c->m_endCellAddress = encodeAddress(sheet->name(), chart->m_colR, chart->m_rwB);
1441
c->m_notifyOnUpdateOfRanges = "Sheet1.D2:Sheet1.F2";
1443
const unsigned long colL = chart->m_colL;
1444
const unsigned long dxL = chart->m_dxL;
1445
const unsigned long colR = chart->m_colR;
1446
const unsigned long dxR = chart->m_dxR;
1447
const unsigned long rwB = chart->m_rwB;
1448
const unsigned long dyT = chart->m_dyT;
1449
const unsigned long rwT = chart->m_rwT;
1450
const unsigned long dyB = chart->m_dyB;
1452
c->m_x = offset(columnWidth(sheet, colL), dxL, 1024);
1453
c->m_y = offset(rowHeight(sheet, rwT), dyT, 256);
1455
if (!chart->m_chart->m_cellRangeAddress.isNull() )
1456
c->m_cellRangeAddress = encodeAddress(sheet->name(), chart->m_chart->m_cellRangeAddress.left(), chart->m_chart->m_cellRangeAddress.top()) + ":" +
1457
encodeAddress(sheet->name(), chart->m_chart->m_cellRangeAddress.right(), chart->m_chart->m_cellRangeAddress.bottom());
1461
c->saveIndex(xmlWriter);
1464
// handle graphics objects
1465
if (!cell->drawObjects().isEmpty()) {
1466
xmlWriter->addCompleteElement(cellShapes[cell].data());
1470
xmlWriter->endElement(); // table:[covered-]table-cell
1473
void ExcelImport::Private::processCharts(KoXmlWriter* manifestWriter)
1475
foreach(ChartExport *c, this->charts) {
1476
c->saveContent(this->storeout, manifestWriter);
1480
// Processes style for a cell within a sheet.
1481
void ExcelImport::Private::processCellForStyle(Cell* cell, KoXmlWriter* xmlWriter)
1484
if (!xmlWriter) return;
1486
// TODO optimize with hash table
1487
const Format* format = &cell->format();
1488
QString styleName = processCellFormat(format, cellFormula(cell));
1489
cellStyles.append(styleName);
1491
if (cell->value().isRichText()) {
1492
std::map<unsigned, FormatFont> formatRuns = cell->value().formatRuns();
1493
for (std::map<unsigned, FormatFont>::iterator it = formatRuns.begin(); it != formatRuns.end(); ++it) {
1494
if (fontStyles.contains(it->second)) continue;
1495
KoGenStyle style(KoGenStyle::TextAutoStyle, "text");
1496
processFontFormat(it->second, style, true);
1497
QString styleName = styles->insert(style, "T");
1498
fontStyles[it->second] = styleName;
1502
if (format->font().superscript() && superScriptStyle.isEmpty()) {
1503
KoGenStyle style(KoGenStyle::TextAutoStyle, "text");
1504
style.addProperty("style:text-position", "super", KoGenStyle::TextType);
1505
superScriptStyle = styles->insert(style, "T");
1507
if (format->font().subscript() && subScriptStyle.isEmpty()) {
1508
KoGenStyle style(KoGenStyle::TextAutoStyle, "text");
1509
style.addProperty("style:text-position", "sub", KoGenStyle::TextType);
1510
subScriptStyle = styles->insert(style, "T");
1513
QList<OfficeArtObject*> objects = cell->drawObjects();
1514
if (!objects.empty()) {
1515
ODrawClient client = ODrawClient(cell->sheet());
1516
ODrawToOdf odraw( client);
1518
KoXmlWriter xml(&b);
1519
Writer writer(xml, *styles, false);
1520
foreach (OfficeArtObject* o, objects) {
1521
client.setShapeText(o->text());
1522
odraw.processDrawingObject(o->object(), writer);
1524
cellShapes[cell] = b.data();
1525
//qDebug() << cell->columnLabel() << cell->row() << b.data();
1530
// Processes styles for a cell within a sheet.
1531
QString ExcelImport::Private::processCellFormat(const Format* format, const QString& formula)
1533
CellFormatKey key(format, formula);
1534
QString& styleName = cellFormatCache[key];
1535
if (styleName.isEmpty()) {
1536
// handle data format, e.g. number style
1538
if (!key.isGeneral) {
1539
refName = processValueFormat(format->valueFormat());
1541
if (key.decimalCount >= 0) {
1542
KoGenStyle style(KoGenStyle::NumericNumberStyle);
1544
buffer.open(QIODevice::WriteOnly);
1545
KoXmlWriter xmlWriter(&buffer); // TODO pass indentation level
1546
xmlWriter.startElement("number:number");
1547
xmlWriter.addAttribute("number:decimal-places", key.decimalCount);
1548
xmlWriter.endElement(); // number:number
1549
QString elementContents = QString::fromUtf8(buffer.buffer(), buffer.buffer().size());
1550
style.addChildElement("number", elementContents);
1551
refName = styles->insert(style, "N");
1555
KoGenStyle style(KoGenStyle::TableCellAutoStyle, "table-cell");
1556
// now the real table-cell
1557
if (!refName.isEmpty())
1558
style.addAttribute("style:data-style-name", refName);
1560
processFormat(format, style);
1561
styleName = styles->insert(style, "ce");
1566
// Processes styles for a row within a sheet.
1567
QString ExcelImport::Private::processRowFormat(Format* format, const QString& breakBefore, int rowRepeat, double rowHeight)
1570
QString valueFormat = format->valueFormat();
1571
if (valueFormat != QString("General"))
1572
refName = processValueFormat(valueFormat);
1574
KoGenStyle style(KoGenStyle::TableRowAutoStyle, "table-row");
1575
// now the real table-cell
1576
if (!refName.isEmpty())
1577
style.addAttribute("style:data-style-name", refName);
1579
if(!breakBefore.isEmpty())
1580
style.addProperty("fo:break-before", breakBefore);
1581
// set how often the row should be repeated
1583
style.addAttribute("table:number-rows-repeated", rowRepeat);
1584
// set the height of the row
1586
style.addPropertyPt("style:row-height", rowHeight);
1588
processFormat(format, style);
1589
QString styleName = styles->insert(style, "ro");
1593
QString convertColor(const QColor& color)
1596
sprintf(buf, "#%02x%02x%02x", color.red(), color.green(), color.blue());
1597
return QString(buf);
1600
void convertBorder(const QString& which, const QString& lineWidthProperty, const Pen& pen, KoGenStyle& style)
1602
if (pen.style == Pen::NoLine || pen.width == 0) {
1603
//style.addProperty(which, "none");
1606
if (pen.style == Pen::DoubleLine) {
1607
result += QString::number(pen.width * 3);
1609
result = QString::number(pen.width);
1613
switch (pen.style) {
1614
case Pen::SolidLine: result += "solid "; break;
1615
case Pen::DashLine: result += "dashed "; break;
1616
case Pen::DotLine: result += "dotted "; break;
1617
case Pen::DashDotLine: result += "dot-dash "; break;
1618
case Pen::DashDotDotLine: result += "dot-dot-dash "; break;
1619
case Pen::DoubleLine: result += "double "; break;
1622
result += convertColor(pen.color);
1624
style.addProperty(which, result);
1625
if (pen.style == Pen::DoubleLine) {
1626
result = QString::number(pen.width);
1627
result = result + "pt " + result + "pt " + result + "pt";
1628
style.addProperty(lineWidthProperty, result);
1633
void ExcelImport::Private::processFontFormat(const FormatFont& font, KoGenStyle& style, bool allProps)
1635
if (font.isNull()) return;
1638
style.addProperty("fo:font-weight", "bold", KoGenStyle::TextType);
1639
} else if (allProps) {
1640
style.addProperty("fo:font-weight", "normal", KoGenStyle::TextType);
1643
if (font.italic()) {
1644
style.addProperty("fo:font-style", "italic", KoGenStyle::TextType);
1645
} else if (allProps) {
1646
style.addProperty("fo:font-style", "normal", KoGenStyle::TextType);
1649
if (font.underline()) {
1650
style.addProperty("style:text-underline-type", "single", KoGenStyle::TextType);
1651
style.addProperty("style:text-underline-style", "solid", KoGenStyle::TextType);
1652
style.addProperty("style:text-underline-width", "auto", KoGenStyle::TextType);
1653
style.addProperty("style:text-underline-color", "font-color", KoGenStyle::TextType);
1654
} else if (allProps) {
1655
style.addProperty("style:text-underline-type", "none", KoGenStyle::TextType);
1656
style.addProperty("style:text-underline-style", "none", KoGenStyle::TextType);
1659
if (font.strikeout()) {
1660
style.addProperty("style:text-line-through-type", "single", KoGenStyle::TextType);
1661
style.addProperty("style:text-line-through-style", "solid", KoGenStyle::TextType);
1663
style.addProperty("style:text-line-through-type", "none", KoGenStyle::TextType);
1664
style.addProperty("style:text-line-through-style", "none", KoGenStyle::TextType);
1667
if (!font.fontFamily().isEmpty())
1668
style.addProperty("fo:font-family", font.fontFamily(), KoGenStyle::TextType);
1670
style.addPropertyPt("fo:font-size", font.fontSize(), KoGenStyle::TextType);
1672
style.addProperty("fo:color", convertColor(font.color()), KoGenStyle::TextType);
1675
// Processes a formatting.
1676
void ExcelImport::Private::processFormat(const Format* format, KoGenStyle& style)
1678
if (!format) return;
1680
FormatFont font = format->font();
1681
FormatAlignment align = format->alignment();
1682
FormatBackground back = format->background();
1683
FormatBorders borders = format->borders();
1685
processFontFormat(font, style);
1687
if (!align.isNull()) {
1688
switch (align.alignY()) {
1690
style.addProperty("style:vertical-align", "top");
1692
case Format::Middle:
1693
style.addProperty("style:vertical-align", "middle");
1695
case Format::Bottom:
1696
style.addProperty("style:vertical-align", "bottom");
1698
case Format::VJustify:
1699
style.addProperty("style:vertical-align", "top");
1700
style.addProperty("koffice:vertical-distributed", "distributed");
1702
case Format::VDistributed:
1703
style.addProperty("style:vertical-align", "middle");
1704
style.addProperty("koffice:vertical-distributed", "distributed");
1708
style.addProperty("fo:wrap-option", align.wrap() ? "wrap" : "no-wrap");
1710
if (align.rotationAngle()) {
1711
style.addProperty("style:rotation-angle", QString::number(align.rotationAngle()));
1714
if (align.stackedLetters()) {
1715
style.addProperty("style:direction", "ttb");
1718
if (align.shrinkToFit()) {
1719
style.addProperty("style:shrink-to-fit", "true");
1723
if (!borders.isNull()) {
1724
convertBorder("fo:border-left", "fo:border-line-width-left", borders.leftBorder(), style);
1725
convertBorder("fo:border-right", "fo:border-line-width-right", borders.rightBorder(), style);
1726
convertBorder("fo:border-top", "fo:border-line-width-top", borders.topBorder(), style);
1727
convertBorder("fo:border-bottom", "fo:border-line-width-bottom", borders.bottomBorder(), style);
1728
convertBorder("style:diagonal-tl-br", "style:diagonal-tl-br-widths", borders.topLeftBorder(), style);
1729
convertBorder("style:diagonal-bl-tr", "style:diagonal-bl-tr-widths", borders.bottomLeftBorder(), style);
1732
if (!back.isNull() && back.pattern() != FormatBackground::EmptyPattern) {
1733
KoGenStyle fillStyle = KoGenStyle(KoGenStyle::GraphicAutoStyle, "graphic");
1735
QColor backColor = back.backgroundColor();
1736
if (back.pattern() == FormatBackground::SolidPattern)
1737
backColor = back.foregroundColor();
1738
const QString bgColor = convertColor(backColor);
1739
style.addProperty("fo:background-color", bgColor);
1740
switch(back.pattern()) {
1741
case FormatBackground::SolidPattern:
1742
fillStyle.addProperty("draw:fill-color", bgColor);
1743
fillStyle.addProperty("draw:transparency", "0%");
1744
fillStyle.addProperty("draw:fill", "solid");
1746
case FormatBackground::Dense3Pattern: // 88% gray
1747
fillStyle.addProperty("draw:fill-color", "#000000");
1748
fillStyle.addProperty("draw:transparency", "88%");
1749
fillStyle.addProperty("draw:fill", "solid");
1751
case FormatBackground::Dense4Pattern: // 50% gray
1752
fillStyle.addProperty("draw:fill-color", "#000000");
1753
fillStyle.addProperty("draw:transparency", "50%");
1754
fillStyle.addProperty("draw:fill", "solid");
1756
case FormatBackground::Dense5Pattern: // 37% gray
1757
fillStyle.addProperty("draw:fill-color", "#000000");
1758
fillStyle.addProperty("draw:transparency", "37%");
1759
fillStyle.addProperty("draw:fill", "solid");
1761
case FormatBackground::Dense6Pattern: // 12% gray
1762
fillStyle.addProperty("draw:fill-color", "#000000");
1763
fillStyle.addProperty("draw:transparency", "12%");
1764
fillStyle.addProperty("draw:fill", "solid");
1766
case FormatBackground::Dense7Pattern: // 6% gray
1767
fillStyle.addProperty("draw:fill-color", "#000000");
1768
fillStyle.addProperty("draw:transparency", "6%");
1769
fillStyle.addProperty("draw:fill", "solid");
1771
case FormatBackground::Dense1Pattern: // diagonal crosshatch
1772
case FormatBackground::Dense2Pattern: // thick diagonal crosshatch
1773
case FormatBackground::HorPattern: // Horizonatal lines
1774
case FormatBackground::VerPattern: // Vertical lines
1775
case FormatBackground::BDiagPattern: // Left-bottom to right-top diagonal lines
1776
case FormatBackground::FDiagPattern: // Left-top to right-bottom diagonal lines
1777
case FormatBackground::CrossPattern: // Horizontal and Vertical lines
1778
case FormatBackground::DiagCrossPattern: { // Crossing diagonal lines
1779
fillStyle.addProperty("draw:fill", "hatch");
1780
KoGenStyle hatchStyle(KoGenStyle::HatchStyle);
1781
hatchStyle.addAttribute("draw:color", "#000000");
1782
switch (back.pattern()) {
1783
case FormatBackground::Dense1Pattern:
1784
case FormatBackground::HorPattern:
1785
hatchStyle.addAttribute("draw:style", "single");
1786
hatchStyle.addAttribute("draw:rotation", 0);
1788
case FormatBackground::VerPattern:
1789
hatchStyle.addAttribute("draw:style", "single");
1790
hatchStyle.addAttribute("draw:rotation", 900);
1792
case FormatBackground::Dense2Pattern:
1793
case FormatBackground::BDiagPattern:
1794
hatchStyle.addAttribute("draw:style", "single");
1795
hatchStyle.addAttribute("draw:rotation", 450);
1797
case FormatBackground::FDiagPattern:
1798
hatchStyle.addAttribute("draw:style", "single");
1799
hatchStyle.addAttribute("draw:rotation", 1350);
1801
case FormatBackground::CrossPattern:
1802
hatchStyle.addAttribute("draw:style", "double");
1803
hatchStyle.addAttribute("draw:rotation", 0);
1805
case FormatBackground::DiagCrossPattern:
1806
hatchStyle.addAttribute("draw:style", "double");
1807
hatchStyle.addAttribute("draw:rotation", 450);
1812
fillStyle.addProperty("draw:fill-hatch-name", mainStyles->insert(hatchStyle, "hatch"));
1817
style.addProperty("draw:style-name", styles->insert(fillStyle, "gr"));
1820
if (!align.isNull()) {
1821
switch (align.alignX()) {
1823
style.addProperty("fo:text-align", "start", KoGenStyle::ParagraphType); break;
1824
case Format::Center:
1825
style.addProperty("fo:text-align", "center", KoGenStyle::ParagraphType); break;
1827
style.addProperty("fo:text-align", "end", KoGenStyle::ParagraphType); break;
1828
case Format::Justify:
1829
case Format::Distributed:
1830
style.addProperty("fo:text-align", "justify", KoGenStyle::ParagraphType); break;
1833
if (align.indentLevel() != 0)
1834
style.addProperty("fo:margin-left", QString::number(align.indentLevel()) + "0pt", KoGenStyle::ParagraphType);
1839
QString ExcelImport::Private::processValueFormat(const QString& valueFormat)
1841
KoGenStyle& style = valueFormatCache[valueFormat];
1842
if (style.isEmpty()) {
1843
NumberFormatParser::setStyles( styles );
1844
style = NumberFormatParser::parse( valueFormat );
1846
if( style.type() == KoGenStyle::ParagraphAutoStyle ) {
1850
return styles->insert( style, "N" );
1853
void ExcelImport::Private::createDefaultColumnStyle( Sheet* sheet ) {
1854
KoGenStyle style(KoGenStyle::TableColumnAutoStyle, "table-column");
1856
style.addProperty("fo:break-before", "auto");
1857
style.addPropertyPt("style:column-width", sheet->defaultColWidth() );
1859
const QString styleName = styles->insert(style, "co");
1860
defaultColumnStyles.append( styleName );
1863
void ExcelImport::Private::processSheetBackground(Sheet* sheet, KoGenStyle& style)
1865
if( sheet->backgroundImage().isEmpty() )
1869
buffer.open(QIODevice::WriteOnly);
1870
KoXmlWriter writer(&buffer);
1872
//TODO add the manifest entry
1873
writer.startElement("style:background-image");
1874
writer.addAttribute("xlink:href", sheet->backgroundImage());
1875
writer.addAttribute("xlink:type", "simple");
1876
writer.addAttribute("xlink:show", "embed");
1877
writer.addAttribute("xlink:actuate", "onLoad");
1878
writer.endElement();
1881
style.addChildElement("style:background-image", QString::fromUtf8(buffer.buffer(), buffer.buffer().size()));
1882
manifestEntries.insert(sheet->backgroundImage(), "image/bmp");
1885
void ExcelImport::Private::addManifestEntries(KoXmlWriter* manifestWriter)
1887
QMap<QString, QString>::const_iterator iterator = manifestEntries.constBegin();
1888
QMap<QString, QString>::const_iterator end = manifestEntries.constEnd();
1889
while( iterator != end ) {
1890
manifestWriter->addManifestEntry(iterator.key(), iterator.value());
1895
void ExcelImport::Private::insertPictureManifest(PictureObject* picture)
1898
const QString fileName = picture->fileName();
1899
const QString extension = fileName.right(fileName.size() - fileName.lastIndexOf('.') - 1);
1901
if( extension == "gif" ) {
1902
mimeType = "image/gif";
1904
else if( extension == "jpg" || extension == "jpeg"
1905
|| extension == "jpe" || extension == "jfif" ) {
1906
mimeType = "image/jpeg";
1908
else if( extension == "tif" || extension == "tiff" ) {
1909
mimeType = "image/tiff";
1911
else if( extension == "png" ) {
1912
mimeType = "image/png";
1914
else if( extension == "emf" ) {
1915
mimeType = "application/x-openoffice-wmf;windows_formatname=\"Image EMF\"";
1917
else if( extension == "wmf" ) {
1918
mimeType = "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
1920
else if( extension == "bmp" ) {
1921
mimeType = "image/bmp";
1924
manifestEntries.insert(fileName, mimeType);