~ubuntu-branches/ubuntu/oneiric/koffice/oneiric-updates

« back to all changes in this revision

Viewing changes to filters/kspread/excel/import/excelimporttoods.cc

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2010-10-27 17:52:57 UTC
  • mfrom: (0.12.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20101027175257-s04zqqk5bs8ckm9o
Tags: 1:2.2.83-0ubuntu1
* Merge with Debian git remaining changes:
 - Add build-deps on librcps-dev, opengtl-dev, libqtgtl-dev, freetds-dev,
   create-resources, libspnav-dev
 - Remove needless build-dep on libwv2-dev
 - koffice-libs recommends create-resources
 - krita recommends pstoedit
 - Keep our patches
* New upstream release 2.3 beta 3
  - Remove debian/patches fixed by upstream
  - Update install files

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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>
 
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 <excelimporttoods.h>
 
25
#include <excelimporttoods.moc>
 
26
 
 
27
#include <QString>
 
28
#include <QDate>
 
29
#include <QBuffer>
 
30
#include <QFontMetricsF>
 
31
#include <QPair>
 
32
#include <kdebug.h>
 
33
#include <KoFilterChain.h>
 
34
#include <KoGlobal.h>
 
35
#include <KoUnit.h>
 
36
#include <kgenericfactory.h>
 
37
 
 
38
#include <KoXmlWriter.h>
 
39
#include <KoOdfWriteStore.h>
 
40
#include <KoGenStyles.h>
 
41
#include <KoGenStyle.h>
 
42
#include <KoOdfNumberStyles.h>
 
43
 
 
44
#include <Charting.h>
 
45
#include <ChartExport.h>
 
46
#include <NumberFormatParser.h>
 
47
 
 
48
#include "swinder.h"
 
49
#include "objects.h"
 
50
#include <iostream>
 
51
#include "ODrawClient.h"
 
52
#include "ImportUtils.h"
 
53
 
 
54
typedef KGenericFactory<ExcelImport> ExcelImportFactory;
 
55
K_EXPORT_COMPONENT_FACTORY(libexcelimport, ExcelImportFactory("kofficefilters"))
 
56
 
 
57
#define UNICODE_EUR 0x20AC
 
58
#define UNICODE_GBP 0x00A3
 
59
#define UNICODE_JPY 0x00A5
 
60
 
 
61
namespace Swinder
 
62
{
 
63
// qHash function to support hashing by Swinder::FormatFont instances.
 
64
static inline uint qHash(const Swinder::FormatFont& font)
 
65
{
 
66
    // TODO: make this a better hash
 
67
    return qHash(font.fontFamily()) ^ qRound(font.fontSize() * 100);
 
68
}
 
69
 
 
70
static qreal offset( unsigned long dimension, unsigned long offset, qreal factor ) {
 
71
    return (float)dimension * (float)offset / factor;
 
72
}
 
73
 
 
74
static qreal columnWidth(Sheet* sheet, unsigned long col) {
 
75
    if( sheet->column(col, false) )
 
76
        return sheet->column(col)->width();
 
77
 
 
78
    return sheet->defaultColWidth();
 
79
}
 
80
 
 
81
static qreal rowHeight(Sheet* sheet, unsigned long row) {
 
82
    if( sheet->row(row, false) )
 
83
        return sheet->row(row)->height();
 
84
 
 
85
    return sheet->defaultRowHeight();
 
86
}
 
87
 
 
88
// Returns A for 1, B for 2, C for 3, etc.
 
89
static QString columnName(uint column)
 
90
{
 
91
    QString s;
 
92
    unsigned digits = 1;
 
93
    unsigned offset = 0;
 
94
    for (unsigned limit = 26; column >= limit + offset; limit *= 26, digits++)
 
95
        offset += limit;
 
96
    for (unsigned col = column - offset; digits; --digits, col /= 26)
 
97
        s.prepend(QChar('A' + (col % 26)));
 
98
    return s;
 
99
}
 
100
 
 
101
static QString encodeSheetName(const QString& name)
 
102
{
 
103
    QString sheetName = name;
 
104
    if (sheetName.contains(' ') || sheetName.contains('.') || sheetName.contains('\''))
 
105
        sheetName = '\'' + sheetName.replace('\'', "''") + '\'';
 
106
    return sheetName;
 
107
}
 
108
 
 
109
static QString encodeAddress(const QString& sheetName, uint column, uint row)
 
110
{
 
111
    return QString("%1.%2%3").arg(encodeSheetName(sheetName)).arg(columnName(column)).arg(row+1);
 
112
}
 
113
 
 
114
}
 
115
 
 
116
using namespace Swinder;
 
117
using namespace XlsUtils;
 
118
 
 
119
class ExcelImport::Private
 
120
{
 
121
public:
 
122
    QString inputFile;
 
123
    QString outputFile;
 
124
 
 
125
    KoStore* storeout;
 
126
    Workbook *workbook;
 
127
 
 
128
    KoGenStyles *styles;
 
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;
 
142
 
 
143
    QHash<Row*,int> rowsRepeatedHash;
 
144
    int rowsRepeated(Row* row, int rowIndex);
 
145
 
 
146
    int rowsCountTotal, rowsCountDone;
 
147
    void addProgress(int addValue);
 
148
 
 
149
    bool createStyles(KoStore* store, KoXmlWriter* manifestWriter, KoGenStyles* mainStyles);
 
150
    bool createContent(KoOdfWriteStore* store);
 
151
    bool createMeta(KoOdfWriteStore* store);
 
152
    bool createSettings(KoOdfWriteStore* store);
 
153
 
 
154
    int sheetFormatIndex;
 
155
    int columnFormatIndex;
 
156
    int rowFormatIndex;
 
157
    int cellFormatIndex;
 
158
 
 
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);
 
177
 
 
178
    void createDefaultColumnStyle( Sheet* sheet );
 
179
    void processSheetBackground(Sheet* sheet, KoGenStyle& style);
 
180
    void addManifestEntries(KoXmlWriter* ManifestWriter);
 
181
    void insertPictureManifest(PictureObject* picture);
 
182
 
 
183
    bool isDateFormat(const QString& valueFormat);
 
184
 
 
185
    QList<QString> defaultColumnStyles;
 
186
    int defaultColumnStyleIndex;
 
187
    QMap<QString,QString> manifestEntries;
 
188
};
 
189
 
 
190
ExcelImport::ExcelImport(QObject* parent, const QStringList&)
 
191
        : KoFilter(parent)
 
192
{
 
193
    d = new Private;
 
194
}
 
195
 
 
196
ExcelImport::~ExcelImport()
 
197
{
 
198
    delete d;
 
199
}
 
200
 
 
201
KoFilter::ConversionStatus ExcelImport::convert(const QByteArray& from, const QByteArray& to)
 
202
{
 
203
    if (from != "application/vnd.ms-excel")
 
204
        return KoFilter::NotImplemented;
 
205
 
 
206
    if (to != "application/vnd.oasis.opendocument.spreadsheet")
 
207
        return KoFilter::NotImplemented;
 
208
 
 
209
    d->inputFile = m_chain->inputFile();
 
210
    d->outputFile = m_chain->outputFile();
 
211
 
 
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.";
 
217
        delete d->workbook;
 
218
        delete d->storeout;
 
219
        return KoFilter::FileNotFound;
 
220
    }
 
221
 
 
222
    emit sigProgress(0);
 
223
 
 
224
    // Tell KoStore not to touch the file names
 
225
    d->storeout->disallowNameExpansion();
 
226
 
 
227
    // open inputFile
 
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())) {
 
231
        delete d->workbook;
 
232
        d->workbook = 0;
 
233
        return KoFilter::StupidError;
 
234
    }
 
235
 
 
236
    if (d->workbook->isPasswordProtected()) {
 
237
        delete d->workbook;
 
238
        d->workbook = 0;
 
239
        return KoFilter::PasswordProtected;
 
240
    }
 
241
 
 
242
    emit sigProgress(-1);
 
243
    emit sigProgress(0);
 
244
 
 
245
    d->styles = new KoGenStyles();
 
246
    d->mainStyles = new KoGenStyles();
 
247
 
 
248
    KoOdfWriteStore oasisStore(d->storeout);
 
249
    KoXmlWriter* manifestWriter = oasisStore.manifestWriter("application/vnd.oasis.opendocument.spreadsheet");
 
250
 
 
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'.";
 
256
        delete d->workbook;
 
257
        delete d->storeout;
 
258
        return KoFilter::CreationError;
 
259
    }
 
260
 
 
261
    // store document styles
 
262
    if (!d->createStyles(d->storeout, manifestWriter, d->mainStyles)) {
 
263
        kWarning() << "Couldn't open the file 'styles.xml'.";
 
264
        delete d->workbook;
 
265
        delete d->storeout;
 
266
        return KoFilter::CreationError;
 
267
    }
 
268
 
 
269
    // store meta content
 
270
    if (!d->createMeta(&oasisStore)) {
 
271
        kWarning() << "Couldn't open the file 'meta.xml'.";
 
272
        delete d->workbook;
 
273
        delete d->storeout;
 
274
        return KoFilter::CreationError;
 
275
    }
 
276
 
 
277
    // store settings
 
278
    if (!d->createSettings(&oasisStore)) {
 
279
        kWarning() << "Couldn't open the file 'settings.xml'.";
 
280
        delete d->workbook;
 
281
        delete d->storeout;
 
282
        return KoFilter::CreationError;
 
283
    }
 
284
 
 
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");
 
289
 
 
290
    d->processCharts(manifestWriter);
 
291
    d->addManifestEntries(manifestWriter);
 
292
    oasisStore.closeManifestWriter();
 
293
 
 
294
    // we are done!
 
295
    delete d->workbook;
 
296
    delete d->styles;
 
297
    delete d->mainStyles;
 
298
    delete d->storeout;
 
299
    d->inputFile.clear();
 
300
    d->outputFile.clear();
 
301
    d->workbook = 0;
 
302
    d->styles = 0;
 
303
    d->mainStyles = 0;
 
304
    d->cellStyles.clear();
 
305
    d->rowStyles.clear();
 
306
    d->colStyles.clear();
 
307
    d->colCellStyles.clear();
 
308
    d->sheetStyles.clear();
 
309
 
 
310
    emit sigProgress(100);
 
311
    return KoFilter::OK;
 
312
}
 
313
 
 
314
// Updates the displayed progress information
 
315
void ExcelImport::Private::addProgress(int addValue)
 
316
{
 
317
    rowsCountDone += addValue;
 
318
    const int progress = int(rowsCountDone / double(rowsCountTotal) * 100.0 + 0.5);
 
319
    workbook->emitProgress(progress);
 
320
}
 
321
 
 
322
int ExcelImport::Private::rowsRepeated(Row* row, int rowIndex)
 
323
{
 
324
    if(rowsRepeatedHash.contains(row))
 
325
        return rowsRepeatedHash[row];
 
326
    // a row does usually at least repeat itself
 
327
    int repeat = 1;
 
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);
 
334
        if(!nextRow) break;
 
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
 
345
            }
 
346
        }
 
347
        if (!cellsAreSame) break;
 
348
        ++repeat;
 
349
    }
 
350
    rowsRepeatedHash[row] = repeat; // cache the result
 
351
    return repeat;
 
352
}
 
353
 
 
354
// Writes the spreadsheet content into the content.xml
 
355
bool ExcelImport::Private::createContent(KoOdfWriteStore* store)
 
356
{
 
357
    KoXmlWriter* bodyWriter = store->bodyWriter();
 
358
    KoXmlWriter* contentWriter = store->contentWriter();
 
359
    if (!bodyWriter || !contentWriter)
 
360
        return false;
 
361
 
 
362
    if(workbook->password() != 0) {
 
363
        contentWriter->addAttribute("table:structure-protected-excel", "true");
 
364
        contentWriter->addAttribute("table:protection-key-excel" , uint(workbook->password()));
 
365
    }
 
366
 
 
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", "&apos;Times New Roman&apos;");
 
376
    contentWriter->endElement(); // style:font-face
 
377
    contentWriter->endElement(); // office:font-face-decls
 
378
 
 
379
 
 
380
    defaultColumnStyleIndex = 0;
 
381
    // office:automatic-styles
 
382
    processWorkbookForStyle(workbook, contentWriter);
 
383
    styles->saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, contentWriter);
 
384
 
 
385
    // important: reset all indexes
 
386
    sheetFormatIndex = 0;
 
387
    columnFormatIndex = 0;
 
388
    rowFormatIndex = 0;
 
389
    cellFormatIndex = 0;
 
390
 
 
391
 
 
392
    // office:body
 
393
    bodyWriter->startElement("office:body");
 
394
    processWorkbookForBody(store, workbook, bodyWriter);
 
395
    bodyWriter->endElement();  // office:body
 
396
 
 
397
    return store->closeContentWriter();
 
398
}
 
399
 
 
400
 
 
401
 
 
402
// Writes the styles.xml
 
403
bool ExcelImport::Private::createStyles(KoStore* store, KoXmlWriter* manifestWriter, KoGenStyles* mainStyles)
 
404
{
 
405
    Q_UNUSED(manifestWriter);
 
406
    if (!store->open("styles.xml"))
 
407
        return false;
 
408
    KoStoreDevice dev(store);
 
409
    KoXmlWriter* stylesWriter = new KoXmlWriter(&dev);
 
410
 
 
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");
 
429
 
 
430
    mainStyles->saveOdfStyles(KoGenStyles::MasterStyles, stylesWriter);
 
431
    mainStyles->saveOdfStyles(KoGenStyles::DocumentStyles, stylesWriter); // office:style
 
432
    mainStyles->saveOdfStyles(KoGenStyles::DocumentAutomaticStyles, stylesWriter); // office:automatic-styles
 
433
 
 
434
    stylesWriter->endElement();  // office:document-styles
 
435
    stylesWriter->endDocument();
 
436
 
 
437
    delete stylesWriter;
 
438
    return store->close();
 
439
}
 
440
 
 
441
// Writes meta-informations into the meta.xml
 
442
bool ExcelImport::Private::createMeta(KoOdfWriteStore* store)
 
443
{
 
444
    if (!store->store()->open("meta.xml"))
 
445
        return false;
 
446
 
 
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");
 
456
 
 
457
    if (workbook->hasProperty(Workbook::PIDSI_TITLE)) {
 
458
        metaWriter->startElement("dc:title");
 
459
        metaWriter->addTextNode(workbook->property(Workbook::PIDSI_TITLE).toString());
 
460
        metaWriter->endElement();
 
461
    }
 
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();
 
466
    }
 
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();
 
471
    }
 
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();
 
476
    }
 
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();
 
481
    }
 
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();
 
486
    }
 
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();
 
491
    }
 
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();
 
496
    }
 
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();
 
501
    }
 
502
 
 
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() );
 
506
 
 
507
    metaWriter->endElement(); // office:meta
 
508
    metaWriter->endElement(); // office:document-meta
 
509
    metaWriter->endDocument();
 
510
 
 
511
    delete metaWriter;
 
512
    return store->store()->close();
 
513
}
 
514
 
 
515
// Writes configuration-settings into the settings.xml
 
516
bool ExcelImport::Private::createSettings(KoOdfWriteStore* store)
 
517
{
 
518
    if (!store->store()->open("settings.xml"))
 
519
        return false;
 
520
 
 
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");
 
526
 
 
527
    // units...
 
528
 
 
529
    // 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());
 
536
 
 
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();
 
559
    }
 
560
    settingsWriter->endElement(); // config:config-item-map-named
 
561
 
 
562
    settingsWriter->endElement(); // config:config-item-map-entry
 
563
    settingsWriter->endElement(); // config:config-item-map-indexed
 
564
    settingsWriter->endElement(); // config:config-item-set
 
565
 
 
566
    settingsWriter->endElement(); // office:settings
 
567
    settingsWriter->endElement(); // Root:element
 
568
    settingsWriter->endDocument();
 
569
    delete settingsWriter;
 
570
    return store->store()->close();
 
571
}
 
572
 
 
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)
 
575
{
 
576
    if (!workbook) return;
 
577
    if (!xmlWriter) return;
 
578
 
 
579
    xmlWriter->startElement("office:spreadsheet");
 
580
 
 
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
 
586
    }
 
587
 
 
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);
 
592
    }
 
593
 
 
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]
 
605
        }
 
606
        xmlWriter->endElement();
 
607
    }
 
608
 
 
609
    bool openedDBRanges = false;
 
610
    int rangeId = 1;
 
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;
 
617
 
 
618
            foreach (const QRect& filter, filters) {
 
619
                QString sRange(encodeAddress(sheetName, filter.left(), filter.top()));
 
620
                sRange.append(":");
 
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
 
627
            }
 
628
        }
 
629
    }
 
630
    if (openedDBRanges) xmlWriter->endElement(); // table:database-ranges
 
631
 
 
632
    xmlWriter->endElement();  // office:spreadsheet
 
633
}
 
634
 
 
635
// Processes the workbook styles. The workbook is the top-level element for content.
 
636
void ExcelImport::Private::processWorkbookForStyle(Workbook* workbook, KoXmlWriter* xmlWriter)
 
637
{
 
638
    if (!workbook) return;
 
639
    if (!xmlWriter) return;
 
640
 
 
641
    QString contentElement;
 
642
    QString masterStyleName("Default");
 
643
    QString pageLayoutStyleName("Mpm");
 
644
 
 
645
    KoGenStyle pageLayoutStyle(KoGenStyle::PageLayoutStyle);
 
646
    pageLayoutStyle.addProperty("style:writing-mode", "lr-tb");
 
647
 
 
648
    QBuffer buf;
 
649
    buf.open(QIODevice::WriteOnly);
 
650
    KoXmlWriter writer(&buf);
 
651
 
 
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");
 
659
    writer.endElement();
 
660
    writer.endElement();
 
661
 
 
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");
 
668
    writer.endElement();
 
669
    writer.endElement();
 
670
    QString pageLyt = QString::fromUtf8(buf.buffer(), buf.buffer().size());
 
671
    buf.close();
 
672
    buf.setData("", 0);
 
673
 
 
674
    pageLayoutStyle.addProperty("1header-footer-style", pageLyt, KoGenStyle::StyleChildElement);
 
675
    pageLayoutStyleName = mainStyles->insert(pageLayoutStyle, pageLayoutStyleName, KoGenStyles::DontAddNumberToName);
 
676
 
 
677
    for (unsigned i = 0; i < workbook->sheetCount(); i++) {
 
678
        Sheet* sheet = workbook->sheet(i);
 
679
        processSheetForStyle(sheet, xmlWriter);
 
680
 
 
681
        buf.open(QIODevice::WriteOnly);
 
682
        processSheetForHeaderFooter(workbook->sheet(0), &writer);
 
683
        contentElement = QString::fromUtf8(buf.buffer(), buf.buffer().size());
 
684
        buf.close();
 
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);
 
689
 
 
690
        masterStyleName = mainStyles->insert(masterStyle, masterStyleName, KoGenStyles::DontAddNumberToName);
 
691
        masterStyle.addAttribute("style:name", masterStyleName);
 
692
    }
 
693
}
 
694
 
 
695
// Processes a sheet.
 
696
void ExcelImport::Private::processSheetForBody(KoOdfWriteStore* store, Sheet* sheet, KoXmlWriter* xmlWriter)
 
697
{
 
698
    if (!sheet) return;
 
699
    if (!xmlWriter) return;
 
700
 
 
701
    xmlWriter->startElement("table:table");
 
702
 
 
703
    xmlWriter->addAttribute("table:name", sheet->name());
 
704
    xmlWriter->addAttribute("table:print", "false");
 
705
    xmlWriter->addAttribute("table:style-name", sheetStyles[sheetFormatIndex]);
 
706
    ++sheetFormatIndex;
 
707
 
 
708
    if(sheet->password() != 0) {
 
709
        //TODO
 
710
       //xmlWriter->addAttribute("table:protected", "true");
 
711
       //xmlWriter->addAttribute("table:protection-key", uint(sheet->password()));
 
712
    }
 
713
 
 
714
    if (!sheet->drawObjects().isEmpty()) {
 
715
        xmlWriter->startElement("table:shapes");
 
716
        xmlWriter->addCompleteElement(sheetShapes[sheet]);
 
717
        xmlWriter->endElement(); // table:shapes
 
718
    }
 
719
 
 
720
 
 
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);
 
725
    }
 
726
    while (outlineLevel > 0) {
 
727
        xmlWriter->endElement(); // table:table-column-group
 
728
        outlineLevel--;
 
729
    }
 
730
 
 
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();
 
739
    }
 
740
 
 
741
    // add rows
 
742
    const unsigned rowCount = qMin(maximalRowCount, sheet->maxRow());
 
743
    for (unsigned i = 0; i <= rowCount;) {
 
744
        i += processRowForBody(store, sheet, i, xmlWriter, outlineLevel);
 
745
    }
 
746
    while (outlineLevel > 0) {
 
747
        xmlWriter->endElement(); // table:table-row-group
 
748
        outlineLevel--;
 
749
    }
 
750
 
 
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();
 
756
    }
 
757
 
 
758
    xmlWriter->endElement();  // table:table
 
759
    ++defaultColumnStyleIndex;
 
760
}
 
761
 
 
762
static QRectF getRect(const MSO::OfficeArtFSPGR &r)
 
763
{
 
764
    return QRect(r.xLeft, r.yTop, r.xRight - r.xLeft, r.yBottom - r.yTop);
 
765
}
 
766
 
 
767
// Processes styles for a sheet.
 
768
void ExcelImport::Private::processSheetForStyle(Sheet* sheet, KoXmlWriter* xmlWriter)
 
769
{
 
770
    if (!sheet) return;
 
771
    if (!xmlWriter) return;
 
772
 
 
773
    KoGenStyle style(KoGenStyle::TableAutoStyle, "table");
 
774
    style.addAttribute("style:master-page-name", "Default");
 
775
 
 
776
    style.addProperty("table:display", sheet->visible() ? "true" : "false");
 
777
    style.addProperty("table:writing-mode", "lr-tb");
 
778
 
 
779
    processSheetBackground(sheet, style);
 
780
 
 
781
    QString styleName = styles->insert(style, "ta");
 
782
    sheetStyles.append(styleName);
 
783
 
 
784
    createDefaultColumnStyle( sheet );
 
785
 
 
786
    const unsigned columnCount = qMin(maximalColumnCount, sheet->maxColumn());
 
787
    for (unsigned i = 0; i <= columnCount; ++i) {
 
788
        processColumnForStyle(sheet, i, xmlWriter);
 
789
    }
 
790
 
 
791
    const unsigned rowCount = qMin(maximalRowCount, sheet->maxRow());
 
792
    for (unsigned i = 0; i <= rowCount;) {
 
793
        i += processRowForStyle(sheet, i, xmlWriter);
 
794
    }
 
795
 
 
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);
 
801
        QBuffer b;
 
802
        KoXmlWriter xml(&b);
 
803
        Writer writer(xml, *styles, false);
 
804
        foreach (const OfficeArtObject* o, objects) {
 
805
            client.setShapeText(o->text());
 
806
            odraw.processDrawingObject(o->object(), writer);
 
807
        }
 
808
        for (int i = 0; i < drawObjectGroups; ++i) {
 
809
            xml.startElement("draw:g");
 
810
 
 
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);
 
820
                }
 
821
            } else {
 
822
                foreach (const OfficeArtObject* o, sheet->drawObjects(i)) {
 
823
                    client.setShapeText(o->text());
 
824
                    odraw.processDrawingObject(o->object(), writer);
 
825
                }
 
826
            }
 
827
            xml.endElement(); // draw:g
 
828
        }
 
829
        sheetShapes[sheet] = b.data();
 
830
        //qDebug() << b.data();
 
831
    }
 
832
}
 
833
 
 
834
// Processes headers and footers for a sheet.
 
835
void ExcelImport::Private::processSheetForHeaderFooter(Sheet* sheet, KoXmlWriter* xmlWriter)
 
836
{
 
837
    if (!sheet) return;
 
838
    if (!xmlWriter) return;
 
839
 
 
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();
 
847
    }
 
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();
 
854
    }
 
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();
 
861
    }
 
862
    xmlWriter->endElement();
 
863
 
 
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();
 
871
    }
 
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();
 
878
    }
 
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();
 
885
    }
 
886
    xmlWriter->endElement();
 
887
}
 
888
 
 
889
// Processes the styles of a headers and footers for a sheet.
 
890
void ExcelImport::Private::processHeaderFooterStyle(const QString& text, KoXmlWriter* xmlWriter)
 
891
{
 
892
    QString content;
 
893
    bool skipUnsupported = false;
 
894
    int lastPos;
 
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));
 
901
 
 
902
    while (pos >= 0) {
 
903
        switch (text[pos + 1].unicode()) {
 
904
        case 'D':
 
905
            xmlWriter->startElement("text:date");
 
906
            xmlWriter->addTextNode(QDate::currentDate().toString("DD/MM/YYYY"));
 
907
            xmlWriter->endElement();
 
908
            break;
 
909
        case 'T':
 
910
            xmlWriter->startElement("text:time");
 
911
            xmlWriter->addTextNode(QTime::currentTime().toString("HH:MM:SS"));
 
912
            xmlWriter->endElement();
 
913
            break;
 
914
        case 'P':
 
915
            xmlWriter->startElement("text:page-number");
 
916
            xmlWriter->addTextNode("1");
 
917
            xmlWriter->endElement();
 
918
            break;
 
919
        case 'N':
 
920
            xmlWriter->startElement("text:page-count");
 
921
            xmlWriter->addTextNode("999");
 
922
            xmlWriter->endElement();
 
923
            break;
 
924
        case 'F':
 
925
            xmlWriter->startElement("text:title");
 
926
            xmlWriter->addTextNode("???");
 
927
            xmlWriter->endElement();
 
928
            break;
 
929
        case 'A':
 
930
            xmlWriter->startElement("text:sheet-name");
 
931
            xmlWriter->addTextNode("???");
 
932
            xmlWriter->endElement();
 
933
            break;
 
934
        case '\"':
 
935
        default:
 
936
            skipUnsupported = true;
 
937
            break;
 
938
        }
 
939
        lastPos = pos;
 
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)));
 
945
        else
 
946
            skipUnsupported = false;
 
947
    }
 
948
}
 
949
 
 
950
// Processes a column in a sheet.
 
951
void ExcelImport::Private::processColumnForBody(Sheet* sheet, int columnIndex, KoXmlWriter* xmlWriter, unsigned& outlineLevel)
 
952
{
 
953
    Column* column = sheet->column(columnIndex, false);
 
954
 
 
955
    if (!xmlWriter) return;
 
956
 
 
957
    unsigned newOutlineLevel = column ? column->outlineLevel() : 0;
 
958
    while (newOutlineLevel > outlineLevel) {
 
959
        xmlWriter->startElement("table:table-column-group");
 
960
        outlineLevel++;
 
961
        if (outlineLevel == newOutlineLevel && column->collapsed())
 
962
            xmlWriter->addAttribute("table:display", "false");
 
963
    }
 
964
    while (newOutlineLevel < outlineLevel) {
 
965
        xmlWriter->endElement(); // table:table-column-group
 
966
        outlineLevel--;
 
967
    }
 
968
 
 
969
    if (!column) {
 
970
        xmlWriter->startElement("table:table-column");
 
971
        Q_ASSERT(defaultColumnStyleIndex < defaultColumnStyles.count());
 
972
        xmlWriter->addAttribute("table:style-name", defaultColumnStyles[defaultColumnStyleIndex] );
 
973
        xmlWriter->endElement();
 
974
        return;
 
975
    }
 
976
    Q_ASSERT(columnFormatIndex < colStyles.count());
 
977
    Q_ASSERT(columnFormatIndex < colCellStyles.count());
 
978
    const QString styleName = colStyles[columnFormatIndex];
 
979
    const QString defaultStyleName = colCellStyles[columnFormatIndex];
 
980
    columnFormatIndex++;
 
981
 
 
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
 
988
}
 
989
 
 
990
// Processes the style of a column in a sheet.
 
991
void ExcelImport::Private::processColumnForStyle(Sheet* sheet, int columnIndex, KoXmlWriter* xmlWriter)
 
992
{
 
993
    Column* column = sheet->column(columnIndex, false);
 
994
 
 
995
    if (!xmlWriter) return;
 
996
    if (!column) return;
 
997
 
 
998
    KoGenStyle style(KoGenStyle::TableColumnAutoStyle, "table-column");
 
999
    style.addProperty("fo:break-before", "auto");
 
1000
    style.addPropertyPt("style:column-width", column->width());
 
1001
 
 
1002
    QString styleName = styles->insert(style, "co");
 
1003
    colStyles.append(styleName);
 
1004
 
 
1005
    const Format* format = &column->format();
 
1006
    QString cellStyleName = processCellFormat(format);
 
1007
    colCellStyles.append(cellStyleName);
 
1008
}
 
1009
 
 
1010
// Processes a row in a sheet.
 
1011
int ExcelImport::Private::processRowForBody(KoOdfWriteStore* store, Sheet* sheet, int rowIndex, KoXmlWriter* xmlWriter, unsigned& outlineLevel)
 
1012
{
 
1013
    int repeat = 1;
 
1014
 
 
1015
    if (!xmlWriter) return repeat;
 
1016
    Row *row = sheet->row(rowIndex, false);
 
1017
 
 
1018
    unsigned newOutlineLevel = row ? row->outlineLevel() : 0;
 
1019
    while (newOutlineLevel > outlineLevel) {
 
1020
        xmlWriter->startElement("table:table-row-group");
 
1021
        outlineLevel++;
 
1022
        if (outlineLevel == newOutlineLevel && row->collapsed())
 
1023
            xmlWriter->addAttribute("table:display", "false");
 
1024
    }
 
1025
    while (newOutlineLevel < outlineLevel) {
 
1026
        xmlWriter->endElement(); // table:table-row-group
 
1027
        outlineLevel--;
 
1028
    }
 
1029
 
 
1030
 
 
1031
    if (!row) {
 
1032
        xmlWriter->startElement("table:table-row");
 
1033
        xmlWriter->endElement();
 
1034
        return repeat;
 
1035
    }
 
1036
    if (!row->sheet()) return repeat;
 
1037
 
 
1038
    const QString styleName = rowStyles[rowFormatIndex];
 
1039
    rowFormatIndex++;
 
1040
 
 
1041
    repeat = rowsRepeated(row, rowIndex);
 
1042
 
 
1043
    xmlWriter->startElement("table:table-row");
 
1044
    xmlWriter->addAttribute("table:visibility", row->visible() ? "visible" : "collapse");
 
1045
    xmlWriter->addAttribute("table:style-name", styleName);
 
1046
 
 
1047
    if(repeat > 1)
 
1048
        xmlWriter->addAttribute("table:number-rows-repeated", repeat);
 
1049
 
 
1050
    // find the column of the rightmost cell (if any)
 
1051
    const int lastCol = row->sheet()->maxCellsInRow(rowIndex);
 
1052
    int i = 0;
 
1053
    while(i <= lastCol) {
 
1054
        Cell* cell = row->sheet()->cell(i, row->index(), false);
 
1055
        if (cell) {
 
1056
            processCellForBody(store, cell, repeat, xmlWriter);
 
1057
            i += cell->columnRepeat();
 
1058
        } else { // empty cell
 
1059
            xmlWriter->startElement("table:table-cell");
 
1060
            xmlWriter->endElement();
 
1061
            ++i;
 
1062
        }
 
1063
    }
 
1064
 
 
1065
    xmlWriter->endElement();  // table:table-row
 
1066
    addProgress(repeat);
 
1067
    return repeat;
 
1068
}
 
1069
 
 
1070
// Processes the style of a row in a sheet.
 
1071
int ExcelImport::Private::processRowForStyle(Sheet* sheet, int rowIndex, KoXmlWriter* xmlWriter)
 
1072
{
 
1073
    int repeat = 1;
 
1074
    Row* row = sheet->row(rowIndex, false);
 
1075
 
 
1076
    if (!row) return repeat;
 
1077
    if (!row->sheet()) return repeat;
 
1078
    if (!xmlWriter) return repeat;
 
1079
 
 
1080
    repeat = rowsRepeated(row, rowIndex);
 
1081
 
 
1082
    Format format = row->format();
 
1083
    QString cellStyleName = processRowFormat(&format, "auto", repeat, row->height());
 
1084
    rowStyles.append(cellStyleName);
 
1085
 
 
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);
 
1089
        if (cell) {
 
1090
            processCellForStyle(cell, xmlWriter);
 
1091
            i += cell->columnRepeat();
 
1092
        } else { // row has no style
 
1093
            ++i;
 
1094
        }
 
1095
    }
 
1096
 
 
1097
    addProgress(repeat);
 
1098
    return repeat;
 
1099
}
 
1100
 
 
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)
 
1110
{
 
1111
    const QString text = removeEscaped(_text);
 
1112
#if 1
 
1113
    if (text.startsWith('_') && text.length() >= 3) {
 
1114
        QChar end;
 
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()) {
 
1120
            {
 
1121
                QString regex = QString("^_%1(.*\"\\$\".*)%2;.*").arg(QString("\\%1").arg(text[1])).arg(QString("\\%1").arg(end));
 
1122
                QRegExp ex(regex);
 
1123
                ex.setMinimal(true);
 
1124
                if (ex.indexIn(text) >= 0) return ex.cap(1);
 
1125
            }
 
1126
            {
 
1127
                QString regex = QString("^_%1(.*\\[\\$.*\\].*)%2;.*").arg(QString("\\%1").arg(text[1])).arg(QString("\\%1").arg(end));
 
1128
                QRegExp ex(regex);
 
1129
                ex.setMinimal(true);
 
1130
                if (ex.indexIn(text) >= 0) return ex.cap(1);
 
1131
            }
 
1132
        }
 
1133
    }
 
1134
#else
 
1135
    if (text.startsWith('_')) {
 
1136
        return text.split(";").first();
 
1137
    }
 
1138
#endif
 
1139
    return text;
 
1140
}
 
1141
 
 
1142
// Currency or accounting format.
 
1143
// "$"* #,##0.0000_
 
1144
// [$EUR]\ #,##0.00"
 
1145
// [$₩-412]* #,##0.0000
 
1146
// * #,##0_)[$€-1]_
 
1147
static bool currencyFormat(const QString& valueFormat, QString *currencyVal = 0, QString * formatVal = 0)
 
1148
{
 
1149
    QString vf = extractConditional(valueFormat);
 
1150
 
 
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);
 
1156
        return true;
 
1157
    }
 
1158
 
 
1159
    // every other currency or accounting has a [$...] identifier
 
1160
    QRegExp crRegEx("\\[\\$(.*)\\]");
 
1161
    crRegEx.setMinimal(true);
 
1162
    if (crRegEx.indexIn(vf) >= 0) {
 
1163
        if (currencyVal) {
 
1164
            *currencyVal = crRegEx.cap(1);
 
1165
        }
 
1166
        if (formatVal) {
 
1167
            QRegExp vlRegEx("([#,]*[\\d]+(|.[#0]+))");
 
1168
            *formatVal = vlRegEx.indexIn(vf) >= 0 ? vlRegEx.cap(1) : QString();
 
1169
        }
 
1170
        return true;
 
1171
    }
 
1172
 
 
1173
    return false;
 
1174
}
 
1175
 
 
1176
// Checks if the as argument passed formatstring defines a date-format or not.
 
1177
bool ExcelImport::Private::isDateFormat(const QString& valueFormat)
 
1178
{
 
1179
    KoGenStyle& style = valueFormatCache[valueFormat];
 
1180
    if (style.isEmpty())
 
1181
        style = NumberFormatParser::parse( valueFormat );
 
1182
    return style.type() == KoGenStyle::NumericDateStyle;
 
1183
}
 
1184
 
 
1185
static QByteArray convertCurrency(double currency, const QString& valueFormat)
 
1186
{
 
1187
    Q_UNUSED(valueFormat);
 
1188
    return QByteArray::number(currency, 'g', 15);
 
1189
}
 
1190
 
 
1191
static QString convertDate(double serialNo, const QString& valueFormat)
 
1192
{
 
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
 
1197
 
 
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
 
1201
 
 
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");
 
1205
}
 
1206
 
 
1207
static QString convertTime(double serialNo, const QString& valueFormat)
 
1208
{
 
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
 
1213
 
 
1214
    // reference is midnight 30 Dec 1899
 
1215
    QTime tt;
 
1216
    tt = tt.addMSecs(qRound((serialNo - (int)serialNo) * 86400 * 1000));
 
1217
    qDebug() << tt;
 
1218
    return tt.toString("'PT'hh'H'mm'M'ss'S'");
 
1219
}
 
1220
 
 
1221
static QByteArray convertFraction(double serialNo, const QString& valueFormat)
 
1222
{
 
1223
    Q_UNUSED(valueFormat);
 
1224
    return QByteArray::number(serialNo, 'g', 15);
 
1225
}
 
1226
 
 
1227
QString cellFormula(Cell* cell)
 
1228
{
 
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("=");
 
1236
        }
 
1237
    }
 
1238
    return formula;
 
1239
}
 
1240
 
 
1241
QString currencyValue(const QString &value)
 
1242
{
 
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);
 
1250
    return QString();
 
1251
}
 
1252
 
 
1253
// Processes a cell within a sheet.
 
1254
void ExcelImport::Private::processCellForBody(KoOdfWriteStore* store, Cell* cell, int rowsRepeat, KoXmlWriter* xmlWriter)
 
1255
{
 
1256
    if (!cell) return;
 
1257
    if (!xmlWriter) return;
 
1258
 
 
1259
    if (cell->isCovered())
 
1260
        xmlWriter->startElement("table:covered-table-cell");
 
1261
    else
 
1262
        xmlWriter->startElement("table:table-cell");
 
1263
 
 
1264
    Q_ASSERT(cellFormatIndex >= 0 && cellFormatIndex < cellStyles.count());
 
1265
    xmlWriter->addAttribute("table:style-name", cellStyles[cellFormatIndex]);
 
1266
    cellFormatIndex++;
 
1267
 
 
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());
 
1274
 
 
1275
    const QString formula = cellFormula(cell);
 
1276
    if (!formula.isEmpty())
 
1277
        xmlWriter->addAttribute("table:formula", formula);
 
1278
 
 
1279
    Value value = cell->value();
 
1280
 
 
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();
 
1286
 
 
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());
 
1305
        }
 
1306
    } else if (value.isText() || value.isError()) {
 
1307
        QString str = value.asString();
 
1308
        QString linkName, linkLocation;
 
1309
 
 
1310
        Hyperlink link = cell->hyperlink();
 
1311
        if (link.isValid) {
 
1312
            linkLocation = link.location;
 
1313
            if(!linkLocation.isEmpty()) {
 
1314
                linkName = link.displayName.trimmed();
 
1315
                if(linkName.isEmpty())
 
1316
                    linkName = str;
 
1317
                str.clear(); // at Excel cells with links don't have additional text content
 
1318
            }
 
1319
        }
 
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);
 
1324
        }
 
1325
 
 
1326
        xmlWriter->startElement("text:p", false);
 
1327
 
 
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);
 
1333
                else
 
1334
                    xmlWriter->addAttribute("text:style-name", superScriptStyle);
 
1335
            }
 
1336
 
 
1337
            if (value.isString()) {
 
1338
                xmlWriter->addTextNode(str);
 
1339
            } else {
 
1340
                // rich text
 
1341
                std::map<unsigned, FormatFont> formatRuns = value.formatRuns();
 
1342
 
 
1343
                // add sentinel to list of format runs
 
1344
                formatRuns[str.length()] = cell->format().font();
 
1345
 
 
1346
                unsigned index = 0;
 
1347
                QString style;
 
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);
 
1352
                    }
 
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
 
1357
                    }
 
1358
 
 
1359
                    index = it->first;
 
1360
 
 
1361
                    if (it->second == cell->format().font())
 
1362
                        style = "";
 
1363
                    else {
 
1364
                        style = fontStyles.value(it->second);
 
1365
                    }
 
1366
                }
 
1367
            }
 
1368
 
 
1369
            if (cell->format().font().subscript() || cell->format().font().superscript())
 
1370
                xmlWriter->endElement(); // text:span
 
1371
        }
 
1372
 
 
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
 
1381
        }
 
1382
 
 
1383
        xmlWriter->endElement(); //  text:p
 
1384
    }
 
1385
 
 
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
 
1396
    }
 
1397
 
 
1398
    // handle pictures
 
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;
 
1409
 
 
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));
 
1418
 
 
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
 
1426
 
 
1427
        insertPictureManifest(picture);
 
1428
    }
 
1429
 
 
1430
    // handle charts
 
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.";
 
1435
            continue;
 
1436
        }
 
1437
 
 
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";
 
1442
 
 
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;
 
1451
 
 
1452
        c->m_x = offset(columnWidth(sheet, colL), dxL, 1024);
 
1453
        c->m_y = offset(rowHeight(sheet, rwT), dyT, 256);
 
1454
 
 
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());
 
1458
 
 
1459
        this->charts << c;
 
1460
 
 
1461
        c->saveIndex(xmlWriter);
 
1462
    }
 
1463
 
 
1464
    // handle graphics objects
 
1465
    if (!cell->drawObjects().isEmpty()) {
 
1466
        xmlWriter->addCompleteElement(cellShapes[cell].data());
 
1467
    }
 
1468
 
 
1469
 
 
1470
    xmlWriter->endElement(); // table:[covered-]table-cell
 
1471
}
 
1472
 
 
1473
void ExcelImport::Private::processCharts(KoXmlWriter* manifestWriter)
 
1474
{
 
1475
    foreach(ChartExport *c, this->charts) {
 
1476
        c->saveContent(this->storeout, manifestWriter);
 
1477
    }
 
1478
}
 
1479
 
 
1480
// Processes style for a cell within a sheet.
 
1481
void ExcelImport::Private::processCellForStyle(Cell* cell, KoXmlWriter* xmlWriter)
 
1482
{
 
1483
    if (!cell) return;
 
1484
    if (!xmlWriter) return;
 
1485
 
 
1486
    // TODO optimize with hash table
 
1487
    const Format* format = &cell->format();
 
1488
    QString styleName = processCellFormat(format, cellFormula(cell));
 
1489
    cellStyles.append(styleName);
 
1490
 
 
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;
 
1499
        }
 
1500
    }
 
1501
 
 
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");
 
1506
    }
 
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");
 
1511
    }
 
1512
 
 
1513
    QList<OfficeArtObject*> objects = cell->drawObjects();
 
1514
    if (!objects.empty()) {
 
1515
        ODrawClient client = ODrawClient(cell->sheet());
 
1516
        ODrawToOdf odraw( client);
 
1517
        QBuffer b;
 
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);
 
1523
        }
 
1524
        cellShapes[cell] = b.data();
 
1525
        //qDebug() << cell->columnLabel() << cell->row() << b.data();
 
1526
    }
 
1527
}
 
1528
 
 
1529
 
 
1530
// Processes styles for a cell within a sheet.
 
1531
QString ExcelImport::Private::processCellFormat(const Format* format, const QString& formula)
 
1532
{
 
1533
    CellFormatKey key(format, formula);
 
1534
    QString& styleName = cellFormatCache[key];
 
1535
    if (styleName.isEmpty()) {
 
1536
        // handle data format, e.g. number style
 
1537
        QString refName;
 
1538
        if (!key.isGeneral) {
 
1539
            refName = processValueFormat(format->valueFormat());
 
1540
        } else {
 
1541
            if (key.decimalCount >= 0) {
 
1542
                KoGenStyle style(KoGenStyle::NumericNumberStyle);
 
1543
                QBuffer buffer;
 
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");
 
1552
            }
 
1553
        }
 
1554
 
 
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);
 
1559
 
 
1560
        processFormat(format, style);
 
1561
        styleName = styles->insert(style, "ce");
 
1562
    }
 
1563
    return styleName;
 
1564
}
 
1565
 
 
1566
// Processes styles for a row within a sheet.
 
1567
QString ExcelImport::Private::processRowFormat(Format* format, const QString& breakBefore, int rowRepeat, double rowHeight)
 
1568
{
 
1569
    QString refName;
 
1570
    QString valueFormat = format->valueFormat();
 
1571
    if (valueFormat != QString("General"))
 
1572
        refName = processValueFormat(valueFormat);
 
1573
 
 
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);
 
1578
    // set break-before
 
1579
    if(!breakBefore.isEmpty())
 
1580
        style.addProperty("fo:break-before", breakBefore);
 
1581
    // set how often the row should be repeated
 
1582
    if (rowRepeat > 1)
 
1583
        style.addAttribute("table:number-rows-repeated", rowRepeat);
 
1584
    // set the height of the row
 
1585
    if (rowHeight >= 0)
 
1586
        style.addPropertyPt("style:row-height", rowHeight);
 
1587
 
 
1588
    processFormat(format, style);
 
1589
    QString styleName = styles->insert(style, "ro");
 
1590
    return styleName;
 
1591
}
 
1592
 
 
1593
QString convertColor(const QColor& color)
 
1594
{
 
1595
    char buf[8];
 
1596
    sprintf(buf, "#%02x%02x%02x", color.red(), color.green(), color.blue());
 
1597
    return QString(buf);
 
1598
}
 
1599
 
 
1600
void convertBorder(const QString& which, const QString& lineWidthProperty, const Pen& pen, KoGenStyle& style)
 
1601
{
 
1602
    if (pen.style == Pen::NoLine || pen.width == 0) {
 
1603
        //style.addProperty(which, "none");
 
1604
    } else {
 
1605
        QString result;
 
1606
        if (pen.style == Pen::DoubleLine) {
 
1607
            result += QString::number(pen.width * 3);
 
1608
        } else {
 
1609
            result = QString::number(pen.width);
 
1610
        }
 
1611
        result += "pt ";
 
1612
 
 
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;
 
1620
        }
 
1621
 
 
1622
        result += convertColor(pen.color);
 
1623
 
 
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);
 
1629
        }
 
1630
    }
 
1631
}
 
1632
 
 
1633
void ExcelImport::Private::processFontFormat(const FormatFont& font, KoGenStyle& style, bool allProps)
 
1634
{
 
1635
    if (font.isNull()) return;
 
1636
 
 
1637
    if (font.bold()) {
 
1638
        style.addProperty("fo:font-weight", "bold", KoGenStyle::TextType);
 
1639
    } else if (allProps) {
 
1640
        style.addProperty("fo:font-weight", "normal", KoGenStyle::TextType);
 
1641
    }
 
1642
 
 
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);
 
1647
    }
 
1648
 
 
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);
 
1657
    }
 
1658
 
 
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);
 
1662
    } else {
 
1663
        style.addProperty("style:text-line-through-type", "none", KoGenStyle::TextType);
 
1664
        style.addProperty("style:text-line-through-style", "none", KoGenStyle::TextType);
 
1665
    }
 
1666
 
 
1667
    if (!font.fontFamily().isEmpty())
 
1668
        style.addProperty("fo:font-family", font.fontFamily(), KoGenStyle::TextType);
 
1669
 
 
1670
    style.addPropertyPt("fo:font-size", font.fontSize(), KoGenStyle::TextType);
 
1671
 
 
1672
    style.addProperty("fo:color", convertColor(font.color()), KoGenStyle::TextType);
 
1673
}
 
1674
 
 
1675
// Processes a formatting.
 
1676
void ExcelImport::Private::processFormat(const Format* format, KoGenStyle& style)
 
1677
{
 
1678
    if (!format) return;
 
1679
 
 
1680
    FormatFont font = format->font();
 
1681
    FormatAlignment align = format->alignment();
 
1682
    FormatBackground back = format->background();
 
1683
    FormatBorders borders = format->borders();
 
1684
 
 
1685
    processFontFormat(font, style);
 
1686
 
 
1687
    if (!align.isNull()) {
 
1688
        switch (align.alignY()) {
 
1689
        case Format::Top:
 
1690
            style.addProperty("style:vertical-align", "top");
 
1691
            break;
 
1692
        case Format::Middle:
 
1693
            style.addProperty("style:vertical-align", "middle");
 
1694
            break;
 
1695
        case Format::Bottom:
 
1696
            style.addProperty("style:vertical-align", "bottom");
 
1697
            break;
 
1698
        case Format::VJustify:
 
1699
            style.addProperty("style:vertical-align", "top");
 
1700
            style.addProperty("koffice:vertical-distributed", "distributed");
 
1701
            break;
 
1702
        case Format::VDistributed:
 
1703
            style.addProperty("style:vertical-align", "middle");
 
1704
            style.addProperty("koffice:vertical-distributed", "distributed");
 
1705
            break;
 
1706
        }
 
1707
 
 
1708
        style.addProperty("fo:wrap-option", align.wrap() ? "wrap" : "no-wrap");
 
1709
 
 
1710
        if (align.rotationAngle()) {
 
1711
            style.addProperty("style:rotation-angle", QString::number(align.rotationAngle()));
 
1712
        }
 
1713
 
 
1714
        if (align.stackedLetters()) {
 
1715
            style.addProperty("style:direction", "ttb");
 
1716
        }
 
1717
 
 
1718
        if (align.shrinkToFit()) {
 
1719
            style.addProperty("style:shrink-to-fit", "true");
 
1720
        }
 
1721
    }
 
1722
 
 
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);
 
1730
    }
 
1731
 
 
1732
    if (!back.isNull() && back.pattern() != FormatBackground::EmptyPattern) {
 
1733
        KoGenStyle fillStyle = KoGenStyle(KoGenStyle::GraphicAutoStyle, "graphic");
 
1734
 
 
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");
 
1745
                break;
 
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");
 
1750
                break;
 
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");
 
1755
                break;
 
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");
 
1760
                break;
 
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");
 
1765
                break;
 
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");
 
1770
                break;
 
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);
 
1787
                    break;
 
1788
                case FormatBackground::VerPattern:
 
1789
                    hatchStyle.addAttribute("draw:style", "single");
 
1790
                    hatchStyle.addAttribute("draw:rotation", 900);
 
1791
                    break;
 
1792
                case FormatBackground::Dense2Pattern:
 
1793
                case FormatBackground::BDiagPattern:
 
1794
                    hatchStyle.addAttribute("draw:style", "single");
 
1795
                    hatchStyle.addAttribute("draw:rotation", 450);
 
1796
                    break;
 
1797
                case FormatBackground::FDiagPattern:
 
1798
                    hatchStyle.addAttribute("draw:style", "single");
 
1799
                    hatchStyle.addAttribute("draw:rotation", 1350);
 
1800
                    break;
 
1801
                case FormatBackground::CrossPattern:
 
1802
                    hatchStyle.addAttribute("draw:style", "double");
 
1803
                    hatchStyle.addAttribute("draw:rotation", 0);
 
1804
                    break;
 
1805
                case FormatBackground::DiagCrossPattern:
 
1806
                    hatchStyle.addAttribute("draw:style", "double");
 
1807
                    hatchStyle.addAttribute("draw:rotation", 450);
 
1808
                    break;
 
1809
                default:
 
1810
                    break;
 
1811
                }
 
1812
                fillStyle.addProperty("draw:fill-hatch-name", mainStyles->insert(hatchStyle, "hatch"));
 
1813
            } break;
 
1814
            default:
 
1815
                break;
 
1816
        }
 
1817
        style.addProperty("draw:style-name", styles->insert(fillStyle, "gr"));
 
1818
    }
 
1819
 
 
1820
    if (!align.isNull()) {
 
1821
        switch (align.alignX()) {
 
1822
        case Format::Left:
 
1823
            style.addProperty("fo:text-align", "start", KoGenStyle::ParagraphType); break;
 
1824
        case Format::Center:
 
1825
            style.addProperty("fo:text-align", "center", KoGenStyle::ParagraphType); break;
 
1826
        case Format::Right:
 
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;
 
1831
        }
 
1832
 
 
1833
        if (align.indentLevel() != 0)
 
1834
            style.addProperty("fo:margin-left", QString::number(align.indentLevel()) + "0pt", KoGenStyle::ParagraphType);
 
1835
    }
 
1836
}
 
1837
 
 
1838
// 3.8.31 numFmts
 
1839
QString ExcelImport::Private::processValueFormat(const QString& valueFormat)
 
1840
{
 
1841
    KoGenStyle& style = valueFormatCache[valueFormat];
 
1842
    if (style.isEmpty()) {
 
1843
        NumberFormatParser::setStyles( styles );
 
1844
        style = NumberFormatParser::parse( valueFormat );
 
1845
    }
 
1846
    if( style.type() == KoGenStyle::ParagraphAutoStyle ) {
 
1847
        return QString();
 
1848
    }
 
1849
 
 
1850
    return styles->insert( style, "N" );
 
1851
}
 
1852
 
 
1853
void ExcelImport::Private::createDefaultColumnStyle( Sheet* sheet ) {
 
1854
    KoGenStyle style(KoGenStyle::TableColumnAutoStyle, "table-column");
 
1855
 
 
1856
    style.addProperty("fo:break-before", "auto");
 
1857
    style.addPropertyPt("style:column-width", sheet->defaultColWidth() );
 
1858
 
 
1859
    const QString styleName = styles->insert(style, "co");
 
1860
    defaultColumnStyles.append( styleName );
 
1861
}
 
1862
 
 
1863
void ExcelImport::Private::processSheetBackground(Sheet* sheet, KoGenStyle& style)
 
1864
{
 
1865
    if( sheet->backgroundImage().isEmpty() )
 
1866
        return;
 
1867
 
 
1868
    QBuffer buffer;
 
1869
    buffer.open(QIODevice::WriteOnly);
 
1870
    KoXmlWriter writer(&buffer);
 
1871
 
 
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();
 
1879
 
 
1880
    buffer.close();
 
1881
    style.addChildElement("style:background-image", QString::fromUtf8(buffer.buffer(), buffer.buffer().size()));
 
1882
    manifestEntries.insert(sheet->backgroundImage(), "image/bmp");
 
1883
}
 
1884
 
 
1885
void ExcelImport::Private::addManifestEntries(KoXmlWriter* manifestWriter)
 
1886
{
 
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());
 
1891
        ++iterator;
 
1892
    }
 
1893
}
 
1894
 
 
1895
void ExcelImport::Private::insertPictureManifest(PictureObject* picture)
 
1896
{
 
1897
    QString mimeType;
 
1898
    const QString fileName = picture->fileName();
 
1899
    const QString extension = fileName.right(fileName.size() - fileName.lastIndexOf('.') - 1);
 
1900
 
 
1901
    if( extension == "gif" ) {
 
1902
        mimeType = "image/gif";
 
1903
    }
 
1904
    else if( extension == "jpg" || extension == "jpeg"
 
1905
            || extension == "jpe" || extension == "jfif" ) {
 
1906
        mimeType = "image/jpeg";
 
1907
    }
 
1908
    else if( extension == "tif" || extension == "tiff" ) {
 
1909
        mimeType = "image/tiff";
 
1910
    }
 
1911
    else if( extension == "png" ) {
 
1912
        mimeType = "image/png";
 
1913
    }
 
1914
    else if( extension == "emf" ) {
 
1915
        mimeType = "application/x-openoffice-wmf;windows_formatname=\"Image EMF\"";
 
1916
    }
 
1917
    else if( extension == "wmf" ) {
 
1918
        mimeType = "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"";
 
1919
    }
 
1920
    else if( extension == "bmp" ) {
 
1921
        mimeType = "image/bmp";
 
1922
    }
 
1923
 
 
1924
    manifestEntries.insert(fileName, mimeType);
 
1925
}
 
1926