File: KoUnavailShape.cpp

package info (click to toggle)
calligra 1%3A2.4.4-3
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 290,028 kB
  • sloc: cpp: 1,105,019; xml: 24,940; ansic: 11,807; python: 8,457; perl: 2,792; sh: 1,507; yacc: 1,307; ruby: 1,248; sql: 903; lex: 455; makefile: 89
file content (658 lines) | stat: -rw-r--r-- 24,021 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
/* This file is part of the KDE project
 *
 * Copyright (C) 2010-2011 Inge Wallin <inge@lysator.liu.se>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */


// Own
#include "KoUnavailShape.h"

// Qt
#include <QPen>
#include <QPainter>
#include <QByteArray>
#include <QBuffer>
#include <QDataStream>
#include <QPixmap>
#include <QStringList>
#include <QSvgRenderer>

// KDE
#include <kstandarddirs.h>
#include <KDebug>

// Calligra
#include "KoUnit.h"
#include "KoStore.h"
#include "KoXmlNS.h"
#include "KoXmlReader.h"
#include <KoXmlWriter.h>
#include <KoXmlNS.h>
#include <KoOdfManifestEntry.h>
#include <KoOdfLoadingContext.h>
#include <KoShapeLoadingContext.h>
#include <KoShapeSavingContext.h>
#include <KoEmbeddedDocumentSaver.h>
#include "KoShapeContainerDefaultModel.h"
#include "KoShapeRegistry.h"
#include <KoShapeBackground.h>


// The XML of a frame looks something like this:
// 
// 1. <draw:frame ...attributes...>
// 2.   <draw:object xlink:href="./Object1" ...more attributes>
// 3.   <draw:image xlink:href="./ObjectReplacements/Object1" ...more attributes>
// 4. </draw:frame>
//
// or
// 
// 1. <draw:frame ...attributes...>
// 2.   <math:math>...inline xml here...</math:math>    
// 3.   <draw:image xlink:href="./ObjectReplacements/Object1" ...more attributes>
// 4. </draw:frame>
//
// We define each Xml statement on lines 2 and 3 above as an "object".
// (Strictly only the first child element is an object in the ODF sense,
// but we have to have some terminology here.)
// 
// In an ODF frame, only the first line, i.e. the first object
// contains the real contents.  All the rest of the objects are used /
// shown if we cannot handle the first one.  The most common cases are
// that there is only one object inside the frame OR that there are 2
// and the 2nd is a picture.
//
// Sometimes, e.g. in the case of an embedded document, the reference
// points not to a file but to a directory structure inside the ODF
// store. 
//
// When we load and save in the UnavailShape, we have to be general
// enough to cover all possible cases of references and inline XML,
// embedded files and embedded directory structures.
//
// We also have to be careful because we cannot reuse the object names
// that are in the original files when saving.  Instead we need to
// create new object names because the ones that were used in the
// original file may already be used by other embedded files/objects
// that are saved by other shapes.
//
// FIXME: There should only be ONE place where new object / file names
//        are generated, not 2(?) like there are now:
//        KoEmbeddedDocumentSaver and the KoImageCollection.
//


// An ObjectEntry is used to store information about objects in the
// frame, as defined above.
struct ObjectEntry {
    QByteArray objectXmlContents; // the XML tree in the object
    QString objectName;       // object name in the frame without "./"
    // This is extracted from objectXmlContents.
    bool isDir;
    KoOdfManifestEntry *manifestEntry; // manifest entry for the above.
};

// A FileEntry is used to store information about embedded files
// inside (i.e. referred to by) an object.
struct FileEntry {
    QString path;           // Normalized filename, i.e. without "./".
    QString mimeType;
    bool  isDir;
    QByteArray contents;
};


class KoUnavailShape::Private
{
public:
    Private(KoUnavailShape* qq);
    ~Private();

    void draw(QPainter &painter) const;
    void drawNull(QPainter &painter) const;

    void storeObjects(const KoXmlElement &element);
    void storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer,
                           ObjectEntry *object, QHash<QString, QString> &unknownNamespaces);
    void storeFile(const QString &filename, KoShapeLoadingContext &context);
    QByteArray loadFile(const QString &filename, KoShapeLoadingContext &context);

    // Objects inside the frame.  For each file, we store:
    //  - The XML code for the object
    //  - Any embedded files (names, contents) that are referenced by xlink:href
    //  - Whether they are directories, i.e. if they contain a file tree and not just one file.
    //  - The manifest entries
    QList<ObjectEntry*> objectEntries;

    // Embedded files
    QList<FileEntry*> embeddedFiles; // List of embedded files.

    // Some cached values.
    QPixmap questionMark;
    QPixmap pixmapPreview;
    QSvgRenderer *scalablePreview;

    KoUnavailShape* q;
};

KoUnavailShape::Private::Private(KoUnavailShape* qq)
: scalablePreview(new QSvgRenderer())
, q(qq)
{
    // Get the question mark "icon".
    questionMark.load(KStandardDirs::locate("data", "calligra/icons/questionmark.png"));
}

KoUnavailShape::Private::~Private()
{
    qDeleteAll(objectEntries);
    qDeleteAll(embeddedFiles);

    // It's a QObject, but we haven't parented it.
    delete(scalablePreview);
}


// ----------------------------------------------------------------
//                         The main class


KoUnavailShape::KoUnavailShape()
: KoFrameShape( "", "" )
, KoShapeContainer(new KoShapeContainerDefaultModel())
, d(new Private(this))
{
    setShapeId(KoUnavailShape_SHAPEID);

    // Default size of the shape.
    KoShape::setSize( QSizeF( CM_TO_POINT( 5 ), CM_TO_POINT( 3 ) ) );
}

KoUnavailShape::~KoUnavailShape()
{
    delete d;
}


void KoUnavailShape::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &)
{
    applyConversion(painter, converter);

    // If the frame is empty, just draw a background.
    kDebug(30006) << "Number of objects:" << d->objectEntries.size();
    if (d->objectEntries.isEmpty()) {
        // But... only try to draw the background if there's one such
        if (background()) {
            QPainterPath p;
            p.addRect(QRectF(QPointF(), size()));
            background()->paint(painter, p);
        }
    } else {
        if(shapes().isEmpty()) {
            d->draw(painter);
        }
    }
}

void KoUnavailShape::paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &)
{
    Q_UNUSED(painter);
    Q_UNUSED(converter);
}

void KoUnavailShape::Private::draw(QPainter &painter) const
{
    painter.save();
    painter.setRenderHint(QPainter::Antialiasing);
    // Run through the previews in order of preference. Draw a placeholder
    // questionmark if there is no preview available for rendering.
    if (scalablePreview->isValid()) {
        QRect bounds(0, 0, q->boundingRect().width(), q->boundingRect().height());
        scalablePreview->render(&painter, bounds);
    }
    else if (!pixmapPreview.isNull()) {
        QRect bounds(0, 0, q->boundingRect().width(), q->boundingRect().height());
        painter.setRenderHint(QPainter::SmoothPixmapTransform);
        painter.drawPixmap(bounds, pixmapPreview);
    }
    else if (q->shapes().isEmpty()) {
        // Draw a nice question mark with a frame around it if there
        // is no other preview image. If there is a contained image
        // shape, we don't need to draw anything.

        // Get the question mark "icon".
        // FIXME: We should be able to use d->questionMark here.
        QPixmap questionMark;
        questionMark.load(KStandardDirs::locate("data", "calligra/icons/questionmark.png"));

        // The size of the image is:
        //  - the size of the shape if  shapesize < 2cm
        //  - 2 cm                  if  2cm <= shapesize <= 8cm
        //  - shapesize / 4         if  shapesize > 8cm
        qreal  width = q->size().width();
        qreal  height = q->size().height();
        qreal  picSize = CM_TO_POINT(2); // Default size is 2 cm.
        if (width < CM_TO_POINT(2) || height < CM_TO_POINT(2))
            picSize = qMin(width, height);
        else if (width > CM_TO_POINT(8) && height > CM_TO_POINT(8))
            picSize = qMin(width, height) / qreal(4.0);

        painter.drawPixmap((width - picSize) / qreal(2.0), (height - picSize) / qreal(2.0),
                           picSize, picSize, questionMark);

        // Draw a gray rectangle around the shape.
        painter.setPen(QPen(QColor(172, 196, 206)));
        painter.drawRect(QRectF(QPointF(0,0), q->size()));

    }
    painter.restore();
}

void KoUnavailShape::Private::drawNull(QPainter &painter) const
{
    QRectF  rect(QPointF(0,0), q->size());
    painter.save();

    // Draw a simple cross in a rectangle just to indicate that there is something here.
    painter.drawLine(rect.topLeft(), rect.bottomRight());
    painter.drawLine(rect.bottomLeft(), rect.topRight());

    painter.restore();
}


// ----------------------------------------------------------------
//                         Loading and Saving


void KoUnavailShape::saveOdf(KoShapeSavingContext & context) const
{
    kDebug(30006) << "START SAVING ##################################################";

    KoEmbeddedDocumentSaver &fileSaver = context.embeddedSaver();
    KoXmlWriter &writer = context.xmlWriter();

    writer.startElement("draw:frame");

    // See also loadOdf() in loadOdfAttributes.
    saveOdfAttributes( context, OdfAllAttributes );

    // Write the stored XML to the file, but don't reuse object names.
    int lap = 0;
    QString newName;
    foreach (const ObjectEntry *object, d->objectEntries) {
        QByteArray xmlArray(object->objectXmlContents);
        QString objectName(object->objectName); // Possibly empty.
        KoOdfManifestEntry *manifestEntry(object->manifestEntry);

        // Create a name for this object. If this is not the first
        // object, i.e. a replacement object (most likely a picture),
        // then reuse the name but put it in ReplacementObjects.
        if (++lap == 1) {
            // The first lap in the loop is the actual object.  All
            // other laps are replacement objects.
            newName = fileSaver.getFilename("Object ");
        }
        else if (lap == 2) {
            newName = "ObjectReplacements/" + newName;
        }
        else
            // FIXME: what should replacement 2 and onwards be called?
            newName = newName + "_";

        // If there was a previous object name, replace it with the new one.
        if (!objectName.isEmpty() && manifestEntry) {
            // FIXME: We must make a copy of the byte array here because
            //        otherwise we won't be able to save > 1 time.
            xmlArray.replace(objectName.toLatin1(), newName.toLatin1());
        }

        writer.addCompleteElement(xmlArray.data());

        // If the objectName is empty, this may be inline XML.
        // If so, we are done now.
        if (objectName.isEmpty() || !manifestEntry) {
            continue;
        }

        // Save embedded files for this object.
        foreach (FileEntry *entry, d->embeddedFiles) {
            QString  fileName(entry->path);

            // If we found a file for this object, we need to write it
            // but with the new object name instead of the old one.
            if (!fileName.startsWith(objectName))
                continue;

            kDebug(30006) << "Object name: " << objectName << "newName: " << newName
            << "filename: " << fileName << "isDir: " << entry->isDir;

            fileName.replace(objectName, newName);
            fileName.prepend("./");
            kDebug(30006) << "New filename: " << fileName;

            // FIXME: Check if we need special treatment of directories.
            fileSaver.saveFile(fileName, entry->mimeType.toLatin1(), entry->contents);
        }

        // Write the manifest entry for the object itself.  If it's a
        // file, the manifest is already written by saveFile, so skip
        // it here.
        if (object->isDir) {
            fileSaver.saveManifestEntry(newName + '/', manifestEntry->mediaType(),
                                        manifestEntry->version());
        }
    }

    writer.endElement(); // draw:frame
}


bool KoUnavailShape::loadOdf(const KoXmlElement &frameElement, KoShapeLoadingContext &context)
{
    kDebug(30006) << "START LOADING ##################################################";
    //kDebug(30006) << "Loading ODF frame in the KoUnavailShape. Element = "
    //              << frameElement.tagName();

    loadOdfAttributes(frameElement, context, OdfAllAttributes);

    // NOTE: We cannot use loadOdfFrame() because we want to save all
    //       the things inside the frame, not just one of them, like
    //       loadOdfFrame() provides.

    // Get the manifest.
    QList<KoOdfManifestEntry*> manifest = context.odfLoadingContext().manifestEntries();

#if 0   // Enable to show all manifest entries.
    kDebug(30006) << "MANIFEST: ";
    foreach (KoOdfManifestEntry *entry, manifest) {
        kDebug(30006) << entry->mediaType() << entry->fullPath() << entry->version();
    }
#endif

    // 1. Get the XML contents of the objects from the draw:frame.  As
    //    a side effect, this extracts the object names from all
    //    xlink:href and stores them into d->objectNames.  The saved
    //    xml contents itself is saved into d->objectXmlContents
    //    (QByteArray) so we can save it back from saveOdf().
    d->storeObjects(frameElement);

#if 1
    // Debug only
    kDebug(30006) << "----------------------------------------------------------------";
    kDebug(30006) << "After storeObjects():";
    foreach (ObjectEntry *object, d->objectEntries) {
        kDebug(30006) << "objectXmlContents: " << object->objectXmlContents
        << "objectName: " << object->objectName;
        // Note: at this point, isDir and manifestEntry are not set.
#endif
    }

    // 2. Loop through the objects that were found in the frame and
    //    save all the files associated with them.  Some of the
    //    objects are files, and some are directories.  The
    //    directories are searched and the files within are saved as
    //    well.
    //
    // In this loop, isDir and manifestEntry of each ObjectEntry are set.
    bool foundPreview = false;
    foreach (ObjectEntry *object, d->objectEntries) {
        QString objectName = object->objectName;

        if (objectName.isEmpty())
            continue;

        kDebug(30006) << "Storing files for object named:" << objectName;

        // Try to find out if the entry is a directory.
        // If the object is a directory, then save all the files
        // inside it, otherwise save the file as it is.
        QString dirName = objectName + '/';
        bool isDir = !context.odfLoadingContext().mimeTypeForPath(dirName).isEmpty();
        if (isDir) {
            // A directory: the files can be found in the manifest.
            foreach (KoOdfManifestEntry *entry, manifest) {
                if (entry->fullPath() == dirName)
                    continue;

                if (entry->fullPath().startsWith(dirName)) {
                    d->storeFile(entry->fullPath(), context);
                }
            }
        }
        else {
            // A file: save it.
            d->storeFile(objectName, context);
        }

        // Get the manifest entry for this object.
        KoOdfManifestEntry *entry = 0;
        QString entryName = isDir ? dirName : objectName;
        for (int j = 0; j < manifest.size(); ++j) {
            KoOdfManifestEntry *temp = manifest.value(j);

            if (temp->fullPath() == entryName) {
                entry = new KoOdfManifestEntry(*temp);
                break;
            }
        }
        object->isDir = isDir;
        object->manifestEntry = entry;

        // If we have not already found a preview in previous times
        // through the loop, then see if this one may be a preview.
        if (!foundPreview) {
            kDebug(30006) << "Attempting to load preview from " << objectName;
            QByteArray previewData = d->loadFile(objectName, context);
            // Check to see if we know the mimetype for this entry. Specifically:
            // 1. Check to see if the item is a loadable SVG file

            // FIXME: Perhaps check in the manifest first? But this
            //        seems to work well.
            d->scalablePreview->load(previewData);
            if (d->scalablePreview->isValid()) {
                kDebug(30006) << "Found scalable preview image!";
                d->scalablePreview->setViewBox(d->scalablePreview->boundsOnElement("svg"));
                foundPreview = true;
                continue;
            }
            // 2. Otherwise check to see if it's a loadable pixmap file
            d->pixmapPreview.loadFromData(previewData);
            if (!d->pixmapPreview.isNull()) {
                kDebug(30006) << "Found pixel based preview image!";
                foundPreview = true;
            }
        }
    }

#if 0   // Enable to get more detailed debug messages
    kDebug(30006) << "Object manifest entries:";
    for (int i = 0; i < d->manifestEntries.size(); ++i) {
        KoOdfManifestEntry *entry = d->manifestEntries.value(i);
        kDebug(30006) << i << ":" << entry;
        if (entry)
            kDebug(30006) << entry->fullPath() << entry->mediaType() << entry->version();
        else
            kDebug(30006) << "--";
    }
    kDebug(30006) << "END LOADING ####################################################";
#endif

    return true;
}


// Load the actual contents inside the frame.
bool KoUnavailShape::loadOdfFrameElement(const KoXmlElement & /*element*/,
                                         KoShapeLoadingContext &/*context*/)
{
    return true;
}


// ----------------------------------------------------------------
//                         Private functions

void KoUnavailShape::Private::storeObjects(const KoXmlElement &element)
{
    // Loop through all the child elements of the draw:frame and save them.
    KoXmlNode n = element.firstChild();
    for (; !n.isNull(); n = n.nextSibling()) {
        kDebug(30006) << "In draw:frame, node =" << n.nodeName();

        // This disregards #text, but that's not in the spec anyway so
        // it doesn't need to be saved.
        if (!n.isElement())
            continue;
        KoXmlElement el = n.toElement();

        ObjectEntry  *object = new ObjectEntry;

        QByteArray contentsTmp;
        QBuffer buffer(&contentsTmp); // the member
        KoXmlWriter writer(&buffer);

        // 1. Find out the objectName
        // Save the normalized filename, i.e. without a starting "./".
        // An empty string is saved if no name is found.
        QString  name = el.attributeNS(KoXmlNS::xlink, "href", QString());
        if (name.startsWith("./"))
            name = name.mid(2);
        object->objectName = name;

        // 2. Copy the XML code.
        QHash<QString, QString> unknownNamespaces;
        storeXmlRecursive(el, writer, object, unknownNamespaces);
        object->objectXmlContents = contentsTmp;

        // 3, 4: the isDir and manifestEntry members are not set here,
        // but initialize them anyway. .
        object->isDir = false;  // Has to be initialized to something.
        object->manifestEntry = 0;

        objectEntries.append(object);
    }
}

void KoUnavailShape::Private::storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer,
                                                ObjectEntry *object, QHash<QString, QString> &unknownNamespaces)
{
    // Start the element;
    // keep the name in a QByteArray so that it stays valid until end element is called.
    const QByteArray name(el.nodeName().toAscii());
    writer.startElement(name.constData());

    // Copy all the attributes, including namespaces.
    QList< QPair<QString, QString> >  attributeNames = el.attributeFullNames();
    for (int i = 0; i < attributeNames.size(); ++i) {
        QPair<QString, QString> attrPair(attributeNames.value(i));
        if (attrPair.first.isEmpty()) {
            writer.addAttribute(attrPair.second.toAscii(), el.attribute(attrPair.second));
        }
        else {
            // This somewhat convoluted code is because we need the
            // namespace, not the namespace URI.
            QString nsShort = KoXmlNS::nsURI2NS(attrPair.first.toAscii());
            // in case we don't find the namespace in our list create a own one and use that
            // so the document created on saving is valid.
            if (nsShort.isEmpty()) {
                nsShort = unknownNamespaces.value(attrPair.first);
                if (nsShort.isEmpty()) {
                    nsShort = QString("ns%1").arg(unknownNamespaces.size() + 1);
                    unknownNamespaces.insert(attrPair.first, nsShort);
                }
                writer.addAttribute("xmlns:" + nsShort.toAscii(), attrPair.first);
            }
            QString attr(nsShort + ':' + attrPair.second);
            writer.addAttribute(attr.toAscii(), el.attributeNS(attrPair.first,
                                                               attrPair.second));
        }
    }

    // Child elements
    // Loop through all the child elements of the draw:frame.
    KoXmlNode n = el.firstChild();
    for (; !n.isNull(); n = n.nextSibling()) {
        if (n.isElement()) {
            storeXmlRecursive(n.toElement(), writer, object, unknownNamespaces);
        }
        else if (n.isText()) {
            writer.addTextNode(n.toText().data()/*.toUtf8()*/);
        }
    }

    // End the element
    writer.endElement();
}

/**
 * This function stores the embedded file in an internal store - it does not save files to disk,
 * and thus it is named in this manner, to avoid the function being confused with functions which
 * save files to disk.
 */
void KoUnavailShape::Private::storeFile(const QString &fileName, KoShapeLoadingContext &context)
{
    kDebug(30006) << "Saving file: " << fileName;

    // Directories need to be saved too, but they don't have any file contents.
    if (fileName.endsWith('/')) {
        FileEntry *entry = new FileEntry;
        entry->path = fileName;
        entry->mimeType = context.odfLoadingContext().mimeTypeForPath(entry->path);
        entry->isDir = true;
        embeddedFiles.append(entry);
    }

    QByteArray fileContent = loadFile(fileName, context);
    if (fileContent.isNull())
        return;

    // Actually store the file in the list.
        FileEntry *entry = new FileEntry;
        entry->path = fileName;
        if (entry->path.startsWith("./"))
                    entry->path = entry->path.mid(2);
        entry->mimeType = context.odfLoadingContext().mimeTypeForPath(entry->path);
        entry->isDir = false;
        entry->contents = fileContent;
        embeddedFiles.append(entry);

        kDebug(30006) << "File length: " << fileContent.size();
}

QByteArray KoUnavailShape::Private::loadFile(const QString &fileName, KoShapeLoadingContext &context)
{
    // Can't load a file which is a directory, return an invalid QByteArray
    if (fileName.endsWith('/'))
        return QByteArray();

    KoStore *store = context.odfLoadingContext().store();
    QByteArray fileContent;

    if (!store->open(fileName)) {
        store->close();
        return QByteArray();
    }

    int fileSize = store->size();
    fileContent = store->read(fileSize);
    store->close();

    //kDebug(30006) << "File content: " << fileContent;
    return fileContent;
}