Warning, file /office/calligra/libs/flake/KoUnavailShape.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* This file is part of the KDE project
0002  *
0003  * Copyright (C) 2010-2011 Inge Wallin <inge@lysator.liu.se>
0004  *
0005  * This library is free software; you can redistribute it and/or
0006  * modify it under the terms of the GNU Library General Public
0007  * License as published by the Free Software Foundation; either
0008  * version 2 of the License, or (at your option) any later version.
0009  *
0010  * This library is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  * Library General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU Library General Public License
0016  * along with this library; see the file COPYING.LIB.  If not, write to
0017  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019  */
0020 
0021 
0022 // Own
0023 #include "KoUnavailShape.h"
0024 
0025 // Qt
0026 #include <QPen>
0027 #include <QPainter>
0028 #include <QPainterPath>
0029 #include <QByteArray>
0030 #include <QBuffer>
0031 #include <QDataStream>
0032 #include <QPixmap>
0033 #include <QStringList>
0034 #include <QSvgRenderer>
0035 #include <QStandardPaths>
0036 
0037 // Calligra
0038 #include <KoUnit.h>
0039 #include <KoStore.h>
0040 #include <KoXmlReader.h>
0041 #include <KoXmlWriter.h>
0042 #include <KoXmlNS.h>
0043 #include <KoOdfManifestEntry.h>
0044 #include <KoOdfLoadingContext.h>
0045 #include <KoEmbeddedDocumentSaver.h>
0046 #include "KoShapeLoadingContext.h"
0047 #include "KoShapeSavingContext.h"
0048 #include "KoShapeContainerDefaultModel.h"
0049 #include "KoShapeBackground.h"
0050 
0051 #include <FlakeDebug.h>
0052 
0053 
0054 // The XML of a frame looks something like this:
0055 // 
0056 // 1. <draw:frame ...attributes...>
0057 // 2.   <draw:object xlink:href="./Object1" ...more attributes>
0058 // 3.   <draw:image xlink:href="./ObjectReplacements/Object1" ...more attributes>
0059 // 4. </draw:frame>
0060 //
0061 // or
0062 // 
0063 // 1. <draw:frame ...attributes...>
0064 // 2.   <math:math>...inline xml here...</math:math>    
0065 // 3.   <draw:image xlink:href="./ObjectReplacements/Object1" ...more attributes>
0066 // 4. </draw:frame>
0067 //
0068 // We define each Xml statement on lines 2 and 3 above as an "object".
0069 // (Strictly only the first child element is an object in the ODF sense,
0070 // but we have to have some terminology here.)
0071 // 
0072 // In an ODF frame, only the first line, i.e. the first object
0073 // contains the real contents.  All the rest of the objects are used /
0074 // shown if we cannot handle the first one.  The most common cases are
0075 // that there is only one object inside the frame OR that there are 2
0076 // and the 2nd is a picture.
0077 //
0078 // Sometimes, e.g. in the case of an embedded document, the reference
0079 // points not to a file but to a directory structure inside the ODF
0080 // store. 
0081 //
0082 // When we load and save in the UnavailShape, we have to be general
0083 // enough to cover all possible cases of references and inline XML,
0084 // embedded files and embedded directory structures.
0085 //
0086 // We also have to be careful because we cannot reuse the object names
0087 // that are in the original files when saving.  Instead we need to
0088 // create new object names because the ones that were used in the
0089 // original file may already be used by other embedded files/objects
0090 // that are saved by other shapes.
0091 //
0092 // FIXME: There should only be ONE place where new object / file names
0093 //        are generated, not 2(?) like there are now:
0094 //        KoEmbeddedDocumentSaver and the KoImageCollection.
0095 //
0096 
0097 
0098 // An ObjectEntry is used to store information about objects in the
0099 // frame, as defined above.
0100 struct ObjectEntry {
0101     QByteArray objectXmlContents; // the XML tree in the object
0102     QString objectName;       // object name in the frame without "./"
0103     // This is extracted from objectXmlContents.
0104     bool isDir;
0105     KoOdfManifestEntry *manifestEntry; // manifest entry for the above.
0106 };
0107 
0108 // A FileEntry is used to store information about embedded files
0109 // inside (i.e. referred to by) an object.
0110 struct FileEntry {
0111     QString path;           // Normalized filename, i.e. without "./".
0112     QString mimeType;
0113     bool  isDir;
0114     QByteArray contents;
0115 };
0116 
0117 
0118 class KoUnavailShape::Private
0119 {
0120 public:
0121     Private(KoUnavailShape* qq);
0122     ~Private();
0123 
0124     void draw(QPainter &painter) const;
0125     void drawNull(QPainter &painter) const;
0126 
0127     void storeObjects(const KoXmlElement &element);
0128     void storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer,
0129                            ObjectEntry *object, QHash<QString, QString> &unknownNamespaces);
0130     void storeFile(const QString &filename, KoShapeLoadingContext &context);
0131     QByteArray loadFile(const QString &filename, KoShapeLoadingContext &context);
0132 
0133     // Objects inside the frame.  For each file, we store:
0134     //  - The XML code for the object
0135     //  - Any embedded files (names, contents) that are referenced by xlink:href
0136     //  - Whether they are directories, i.e. if they contain a file tree and not just one file.
0137     //  - The manifest entries
0138     QList<ObjectEntry*> objectEntries;
0139 
0140     // Embedded files
0141     QList<FileEntry*> embeddedFiles; // List of embedded files.
0142 
0143     // Some cached values.
0144     QPixmap questionMark;
0145     QPixmap pixmapPreview;
0146     QSvgRenderer *scalablePreview;
0147 
0148     KoUnavailShape* q;
0149 };
0150 
0151 KoUnavailShape::Private::Private(KoUnavailShape* qq)
0152 : scalablePreview(new QSvgRenderer())
0153 , q(qq)
0154 {
0155     // Get the question mark "icon".
0156     questionMark.load(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "calligra/pics/questionmark.png"));
0157 }
0158 
0159 KoUnavailShape::Private::~Private()
0160 {
0161     qDeleteAll(objectEntries);
0162     qDeleteAll(embeddedFiles);
0163 
0164     // It's a QObject, but we haven't parented it.
0165     delete(scalablePreview);
0166 }
0167 
0168 
0169 // ----------------------------------------------------------------
0170 //                         The main class
0171 
0172 
0173 KoUnavailShape::KoUnavailShape()
0174 : KoFrameShape( "", "" )
0175 , KoShapeContainer(new KoShapeContainerDefaultModel())
0176 , d(new Private(this))
0177 {
0178     setShapeId(KoUnavailShape_SHAPEID);
0179 
0180     // Default size of the shape.
0181     KoShape::setSize( QSizeF( CM_TO_POINT( 5 ), CM_TO_POINT( 3 ) ) );
0182 }
0183 
0184 KoUnavailShape::~KoUnavailShape()
0185 {
0186     delete d;
0187 }
0188 
0189 
0190 void KoUnavailShape::paint(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &paintContext)
0191 {
0192     applyConversion(painter, converter);
0193 
0194     // If the frame is empty, just draw a background.
0195     debugFlake << "Number of objects:" << d->objectEntries.size();
0196     if (d->objectEntries.isEmpty()) {
0197         // But... only try to draw the background if there's one such
0198         if (background()) {
0199             QPainterPath p;
0200             p.addRect(QRectF(QPointF(), size()));
0201             background()->paint(painter, converter, paintContext, p);
0202         }
0203     } else {
0204         if(shapes().isEmpty()) {
0205             d->draw(painter);
0206         }
0207     }
0208 }
0209 
0210 void KoUnavailShape::paintComponent(QPainter &painter, const KoViewConverter &converter, KoShapePaintingContext &)
0211 {
0212     Q_UNUSED(painter);
0213     Q_UNUSED(converter);
0214 }
0215 
0216 void KoUnavailShape::Private::draw(QPainter &painter) const
0217 {
0218     painter.save();
0219     painter.setRenderHint(QPainter::Antialiasing);
0220     // Run through the previews in order of preference. Draw a placeholder
0221     // questionmark if there is no preview available for rendering.
0222     if (scalablePreview->isValid()) {
0223         QRect bounds(0, 0, q->boundingRect().width(), q->boundingRect().height());
0224         scalablePreview->render(&painter, bounds);
0225     }
0226     else if (!pixmapPreview.isNull()) {
0227         QRect bounds(0, 0, q->boundingRect().width(), q->boundingRect().height());
0228         painter.setRenderHint(QPainter::SmoothPixmapTransform);
0229         painter.drawPixmap(bounds, pixmapPreview);
0230     }
0231     else if (q->shapes().isEmpty()) {
0232         // Draw a nice question mark with a frame around it if there
0233         // is no other preview image. If there is a contained image
0234         // shape, we don't need to draw anything.
0235 
0236         // Get the question mark "icon".
0237         // FIXME: We should be able to use d->questionMark here.
0238         QPixmap questionMark;
0239         questionMark.load(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "calligra/pics/questionmark.png"));
0240 
0241         // The size of the image is:
0242         //  - the size of the shape if  shapesize < 2cm
0243         //  - 2 cm                  if  2cm <= shapesize <= 8cm
0244         //  - shapesize / 4         if  shapesize > 8cm
0245         qreal  width = q->size().width();
0246         qreal  height = q->size().height();
0247         qreal  picSize = CM_TO_POINT(2); // Default size is 2 cm.
0248         if (width < CM_TO_POINT(2) || height < CM_TO_POINT(2))
0249             picSize = qMin(width, height);
0250         else if (width > CM_TO_POINT(8) && height > CM_TO_POINT(8))
0251             picSize = qMin(width, height) / qreal(4.0);
0252 
0253         painter.drawPixmap((width - picSize) / qreal(2.0), (height - picSize) / qreal(2.0),
0254                            picSize, picSize, questionMark);
0255 
0256         // Draw a gray rectangle around the shape.
0257         painter.setPen(QPen(QColor(172, 196, 206), 0));
0258         painter.drawRect(QRectF(QPointF(0,0), q->size()));
0259 
0260     }
0261     painter.restore();
0262 }
0263 
0264 void KoUnavailShape::Private::drawNull(QPainter &painter) const
0265 {
0266     QRectF  rect(QPointF(0,0), q->size());
0267     painter.save();
0268 
0269     // Draw a simple cross in a rectangle just to indicate that there is something here.
0270     painter.drawLine(rect.topLeft(), rect.bottomRight());
0271     painter.drawLine(rect.bottomLeft(), rect.topRight());
0272 
0273     painter.restore();
0274 }
0275 
0276 
0277 // ----------------------------------------------------------------
0278 //                         Loading and Saving
0279 
0280 
0281 void KoUnavailShape::saveOdf(KoShapeSavingContext & context) const
0282 {
0283     debugFlake << "START SAVING ##################################################";
0284 
0285     KoEmbeddedDocumentSaver &fileSaver = context.embeddedSaver();
0286     KoXmlWriter &writer = context.xmlWriter();
0287 
0288     writer.startElement("draw:frame");
0289 
0290     // See also loadOdf() in loadOdfAttributes.
0291     saveOdfAttributes( context, OdfAllAttributes );
0292 
0293     // Write the stored XML to the file, but don't reuse object names.
0294     int lap = 0;
0295     QString newName;
0296     foreach (const ObjectEntry *object, d->objectEntries) {
0297         QByteArray xmlArray(object->objectXmlContents);
0298         QString objectName(object->objectName); // Possibly empty.
0299         KoOdfManifestEntry *manifestEntry(object->manifestEntry);
0300 
0301         // Create a name for this object. If this is not the first
0302         // object, i.e. a replacement object (most likely a picture),
0303         // then reuse the name but put it in ReplacementObjects.
0304         if (++lap == 1) {
0305             // The first lap in the loop is the actual object.  All
0306             // other laps are replacement objects.
0307             newName = fileSaver.getFilename("Object ");
0308         }
0309         else if (lap == 2) {
0310             newName = "ObjectReplacements/" + newName;
0311         }
0312         else
0313             // FIXME: what should replacement 2 and onwards be called?
0314             newName = newName + "_";
0315 
0316         // If there was a previous object name, replace it with the new one.
0317         if (!objectName.isEmpty() && manifestEntry) {
0318             // FIXME: We must make a copy of the byte array here because
0319             //        otherwise we won't be able to save > 1 time.
0320             xmlArray.replace(objectName.toLatin1(), newName.toLatin1());
0321         }
0322 
0323         writer.addCompleteElement(xmlArray.data());
0324 
0325         // If the objectName is empty, this may be inline XML.
0326         // If so, we are done now.
0327         if (objectName.isEmpty() || !manifestEntry) {
0328             continue;
0329         }
0330 
0331         // Save embedded files for this object.
0332         foreach (FileEntry *entry, d->embeddedFiles) {
0333             QString  fileName(entry->path);
0334 
0335             // If we found a file for this object, we need to write it
0336             // but with the new object name instead of the old one.
0337             if (!fileName.startsWith(objectName))
0338                 continue;
0339 
0340             debugFlake << "Object name: " << objectName << "newName: " << newName
0341             << "filename: " << fileName << "isDir: " << entry->isDir;
0342 
0343             fileName.replace(objectName, newName);
0344             fileName.prepend("./");
0345             debugFlake << "New filename: " << fileName;
0346 
0347             // FIXME: Check if we need special treatment of directories.
0348             fileSaver.saveFile(fileName, entry->mimeType.toLatin1(), entry->contents);
0349         }
0350 
0351         // Write the manifest entry for the object itself.  If it's a
0352         // file, the manifest is already written by saveFile, so skip
0353         // it here.
0354         if (object->isDir) {
0355             fileSaver.saveManifestEntry(newName + '/', manifestEntry->mediaType(),
0356                                         manifestEntry->version());
0357         }
0358     }
0359 
0360     writer.endElement(); // draw:frame
0361 }
0362 
0363 
0364 bool KoUnavailShape::loadOdf(const KoXmlElement &frameElement, KoShapeLoadingContext &context)
0365 {
0366     debugFlake << "START LOADING ##################################################";
0367     //debugFlake << "Loading ODF frame in the KoUnavailShape. Element = "
0368     //              << frameElement.tagName();
0369 
0370     loadOdfAttributes(frameElement, context, OdfAllAttributes);
0371 
0372     // NOTE: We cannot use loadOdfFrame() because we want to save all
0373     //       the things inside the frame, not just one of them, like
0374     //       loadOdfFrame() provides.
0375 
0376     // Get the manifest.
0377     QList<KoOdfManifestEntry*> manifest = context.odfLoadingContext().manifestEntries();
0378 
0379 #if 0   // Enable to show all manifest entries.
0380     debugFlake << "MANIFEST: ";
0381     foreach (KoOdfManifestEntry *entry, manifest) {
0382         debugFlake << entry->mediaType() << entry->fullPath() << entry->version();
0383     }
0384 #endif
0385 
0386     // 1. Get the XML contents of the objects from the draw:frame.  As
0387     //    a side effect, this extracts the object names from all
0388     //    xlink:href and stores them into d->objectNames.  The saved
0389     //    xml contents itself is saved into d->objectXmlContents
0390     //    (QByteArray) so we can save it back from saveOdf().
0391     d->storeObjects(frameElement);
0392 
0393 #if 1
0394     // Debug only
0395     debugFlake << "----------------------------------------------------------------";
0396     debugFlake << "After storeObjects():";
0397     foreach (ObjectEntry *object, d->objectEntries) {
0398         debugFlake << "objectXmlContents: " << object->objectXmlContents
0399         << "objectName: " << object->objectName;
0400         // Note: at this point, isDir and manifestEntry are not set.
0401 #endif
0402     }
0403 
0404     // 2. Loop through the objects that were found in the frame and
0405     //    save all the files associated with them.  Some of the
0406     //    objects are files, and some are directories.  The
0407     //    directories are searched and the files within are saved as
0408     //    well.
0409     //
0410     // In this loop, isDir and manifestEntry of each ObjectEntry are set.
0411     bool foundPreview = false;
0412     foreach (ObjectEntry *object, d->objectEntries) {
0413         QString objectName = object->objectName;
0414 
0415         if (objectName.isEmpty())
0416             continue;
0417 
0418         debugFlake << "Storing files for object named:" << objectName;
0419 
0420         // Try to find out if the entry is a directory.
0421         // If the object is a directory, then save all the files
0422         // inside it, otherwise save the file as it is.
0423         QString dirName = objectName + '/';
0424         bool isDir = !context.odfLoadingContext().mimeTypeForPath(dirName).isEmpty();
0425         if (isDir) {
0426             // A directory: the files can be found in the manifest.
0427             foreach (KoOdfManifestEntry *entry, manifest) {
0428                 if (entry->fullPath() == dirName)
0429                     continue;
0430 
0431                 if (entry->fullPath().startsWith(dirName)) {
0432                     d->storeFile(entry->fullPath(), context);
0433                 }
0434             }
0435         }
0436         else {
0437             // A file: save it.
0438             d->storeFile(objectName, context);
0439         }
0440 
0441         // Get the manifest entry for this object.
0442         KoOdfManifestEntry *entry = 0;
0443         QString entryName = isDir ? dirName : objectName;
0444         for (int j = 0; j < manifest.size(); ++j) {
0445             KoOdfManifestEntry *temp = manifest.value(j);
0446 
0447             if (temp->fullPath() == entryName) {
0448                 entry = new KoOdfManifestEntry(*temp);
0449                 break;
0450             }
0451         }
0452         object->isDir = isDir;
0453         object->manifestEntry = entry;
0454 
0455         // If we have not already found a preview in previous times
0456         // through the loop, then see if this one may be a preview.
0457         if (!foundPreview) {
0458             debugFlake << "Attempting to load preview from " << objectName;
0459             QByteArray previewData = d->loadFile(objectName, context);
0460             // Check to see if we know the mimetype for this entry. Specifically:
0461             // 1. Check to see if the item is a loadable SVG file
0462 
0463             // FIXME: Perhaps check in the manifest first? But this
0464             //        seems to work well.
0465             d->scalablePreview->load(previewData);
0466             if (d->scalablePreview->isValid()) {
0467                 debugFlake << "Found scalable preview image!";
0468                 d->scalablePreview->setViewBox(d->scalablePreview->boundsOnElement("svg"));
0469                 foundPreview = true;
0470                 continue;
0471             }
0472             // 2. Otherwise check to see if it's a loadable pixmap file
0473             d->pixmapPreview.loadFromData(previewData);
0474             if (!d->pixmapPreview.isNull()) {
0475                 debugFlake << "Found pixel based preview image!";
0476                 foundPreview = true;
0477             }
0478         }
0479     }
0480 
0481 #if 0   // Enable to get more detailed debug messages
0482     debugFlake << "Object manifest entries:";
0483     for (int i = 0; i < d->manifestEntries.size(); ++i) {
0484         KoOdfManifestEntry *entry = d->manifestEntries.value(i);
0485         debugFlake << i << ":" << entry;
0486         if (entry)
0487             debugFlake << entry->fullPath() << entry->mediaType() << entry->version();
0488         else
0489             debugFlake << "--";
0490     }
0491     debugFlake << "END LOADING ####################################################";
0492 #endif
0493 
0494     return true;
0495 }
0496 
0497 
0498 // Load the actual contents inside the frame.
0499 bool KoUnavailShape::loadOdfFrameElement(const KoXmlElement & /*element*/,
0500                                          KoShapeLoadingContext &/*context*/)
0501 {
0502     return true;
0503 }
0504 
0505 
0506 // ----------------------------------------------------------------
0507 //                         Private functions
0508 
0509 void KoUnavailShape::Private::storeObjects(const KoXmlElement &element)
0510 {
0511     // Loop through all the child elements of the draw:frame and save them.
0512     KoXmlNode n = element.firstChild();
0513     for (; !n.isNull(); n = n.nextSibling()) {
0514         debugFlake << "In draw:frame, node =" << n.nodeName();
0515 
0516         // This disregards #text, but that's not in the spec anyway so
0517         // it doesn't need to be saved.
0518         if (!n.isElement())
0519             continue;
0520         KoXmlElement el = n.toElement();
0521 
0522         ObjectEntry  *object = new ObjectEntry;
0523 
0524         QByteArray contentsTmp;
0525         QBuffer buffer(&contentsTmp); // the member
0526         KoXmlWriter writer(&buffer);
0527 
0528         // 1. Find out the objectName
0529         // Save the normalized filename, i.e. without a starting "./".
0530         // An empty string is saved if no name is found.
0531         QString  name = el.attributeNS(KoXmlNS::xlink, "href", QString());
0532         if (name.startsWith(QLatin1String("./")))
0533             name.remove(0, 2);
0534         object->objectName = name;
0535 
0536         // 2. Copy the XML code.
0537         QHash<QString, QString> unknownNamespaces;
0538         storeXmlRecursive(el, writer, object, unknownNamespaces);
0539         object->objectXmlContents = contentsTmp;
0540 
0541         // 3, 4: the isDir and manifestEntry members are not set here,
0542         // but initialize them anyway. .
0543         object->isDir = false;  // Has to be initialized to something.
0544         object->manifestEntry = 0;
0545 
0546         objectEntries.append(object);
0547     }
0548 }
0549 
0550 void KoUnavailShape::Private::storeXmlRecursive(const KoXmlElement &el, KoXmlWriter &writer,
0551                                                 ObjectEntry *object, QHash<QString, QString> &unknownNamespaces)
0552 {
0553     // Start the element;
0554     // keep the name in a QByteArray so that it stays valid until end element is called.
0555     const QByteArray name(el.nodeName().toLatin1());
0556     writer.startElement(name.constData());
0557 
0558     // Copy all the attributes, including namespaces.
0559     QList< QPair<QString, QString> >  attributeNames = el.attributeFullNames();
0560     for (int i = 0; i < attributeNames.size(); ++i) {
0561         QPair<QString, QString> attrPair(attributeNames.value(i));
0562         if (attrPair.first.isEmpty()) {
0563             writer.addAttribute(attrPair.second.toLatin1(), el.attribute(attrPair.second));
0564         }
0565         else {
0566             // This somewhat convoluted code is because we need the
0567             // namespace, not the namespace URI.
0568             QString nsShort = KoXmlNS::nsURI2NS(attrPair.first.toLatin1());
0569             // in case we don't find the namespace in our list create a own one and use that
0570             // so the document created on saving is valid.
0571             if (nsShort.isEmpty()) {
0572                 nsShort = unknownNamespaces.value(attrPair.first);
0573                 if (nsShort.isEmpty()) {
0574                     nsShort = QString("ns%1").arg(unknownNamespaces.size() + 1);
0575                     unknownNamespaces.insert(attrPair.first, nsShort);
0576                 }
0577                 QString name = QString("xmlns:") + nsShort;
0578                 writer.addAttribute(name.toLatin1(), attrPair.first.toLatin1());
0579             }
0580             QString attr(nsShort + ':' + attrPair.second);
0581             writer.addAttribute(attr.toLatin1(), el.attributeNS(attrPair.first,
0582                                                                attrPair.second));
0583         }
0584     }
0585 
0586     // Child elements
0587     // Loop through all the child elements of the draw:frame.
0588     KoXmlNode n = el.firstChild();
0589     for (; !n.isNull(); n = n.nextSibling()) {
0590         if (n.isElement()) {
0591             storeXmlRecursive(n.toElement(), writer, object, unknownNamespaces);
0592         }
0593         else if (n.isText()) {
0594             writer.addTextNode(n.toText().data()/*.toUtf8()*/);
0595         }
0596     }
0597 
0598     // End the element
0599     writer.endElement();
0600 }
0601 
0602 /**
0603  * This function stores the embedded file in an internal store - it does not save files to disk,
0604  * and thus it is named in this manner, to avoid the function being confused with functions which
0605  * save files to disk.
0606  */
0607 void KoUnavailShape::Private::storeFile(const QString &fileName, KoShapeLoadingContext &context)
0608 {
0609     debugFlake << "Saving file: " << fileName;
0610 
0611     // Directories need to be saved too, but they don't have any file contents.
0612     if (fileName.endsWith('/')) {
0613         FileEntry *entry = new FileEntry;
0614         entry->path = fileName;
0615         entry->mimeType = context.odfLoadingContext().mimeTypeForPath(entry->path);
0616         entry->isDir = true;
0617         embeddedFiles.append(entry);
0618     }
0619 
0620     QByteArray fileContent = loadFile(fileName, context);
0621     if (fileContent.isNull())
0622         return;
0623 
0624     // Actually store the file in the list.
0625         FileEntry *entry = new FileEntry;
0626         entry->path = fileName;
0627         if (entry->path.startsWith(QLatin1String("./")))
0628             entry->path.remove(0, 2);
0629         entry->mimeType = context.odfLoadingContext().mimeTypeForPath(entry->path);
0630         entry->isDir = false;
0631         entry->contents = fileContent;
0632         embeddedFiles.append(entry);
0633 
0634         debugFlake << "File length: " << fileContent.size();
0635 }
0636 
0637 QByteArray KoUnavailShape::Private::loadFile(const QString &fileName, KoShapeLoadingContext &context)
0638 {
0639     // Can't load a file which is a directory, return an invalid QByteArray
0640     if (fileName.endsWith('/'))
0641         return QByteArray();
0642 
0643     KoStore *store = context.odfLoadingContext().store();
0644     QByteArray fileContent;
0645 
0646     if (!store->open(fileName)) {
0647         store->close();
0648         return QByteArray();
0649     }
0650 
0651     int fileSize = store->size();
0652     fileContent = store->read(fileSize);
0653     store->close();
0654 
0655     //debugFlake << "File content: " << fileContent;
0656     return fileContent;
0657 }