Warning, file /office/calligra/libs/odf/KoEmbeddedDocumentSaver.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    Copyright (C) 2004-2006 David Faure <faure@kde.org>
0003    Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org>
0004    Copyright (C) 2010 Thomas Zander <zander@kde.org>
0005    Copyright (C) 2011 Inge Wallin <inge@lysator.liu.se>
0006 
0007    This library is free software; you can redistribute it and/or
0008    modify it under the terms of the GNU Library General Public
0009    License as published by the Free Software Foundation; either
0010    version 2 of the License, or (at your option) any later version.
0011 
0012    This library is distributed in the hope that it will be useful,
0013    but WITHOUT ANY WARRANTY; without even the implied warranty of
0014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015    Library General Public License for more details.
0016 
0017    You should have received a copy of the GNU Library General Public License
0018    along with this library; see the file COPYING.LIB.  If not, write to
0019    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020  * Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "KoEmbeddedDocumentSaver.h"
0024 
0025 #include <QList>
0026 
0027 #include <OdfDebug.h>
0028 #include <QUrl>
0029 
0030 #include <KoStore.h>
0031 #include <KoXmlWriter.h>
0032 #include <KoOdfWriteStore.h>
0033 
0034 #include "KoDocumentBase.h"
0035 #include <KoOdfManifestEntry.h>
0036 
0037 
0038 #define INTERNAL_PROTOCOL "intern"
0039 
0040 struct FileEntry {
0041     QString path;
0042     QByteArray mimeType;       // QBA because this is what addManifestEntry wants
0043     QByteArray contents;
0044 };
0045 
0046 
0047 class Q_DECL_HIDDEN KoEmbeddedDocumentSaver::Private
0048 {
0049 public:
0050     Private() {}
0051 
0052     QHash<QString, int> prefixes; // Used in getFilename();
0053 
0054     // These will be saved when saveEmbeddedDocuments() is called.
0055     QList<KoDocumentBase*> documents; // Embedded documents
0056     QList<FileEntry*> files;    // Embedded files.
0057     QList<KoOdfManifestEntry*> manifestEntries;
0058 };
0059 
0060 KoEmbeddedDocumentSaver::KoEmbeddedDocumentSaver()
0061     : d(new Private())
0062 {
0063 }
0064 
0065 KoEmbeddedDocumentSaver::~KoEmbeddedDocumentSaver()
0066 {
0067     qDeleteAll(d->files);
0068     qDeleteAll(d->manifestEntries);
0069     delete d;
0070 }
0071 
0072 
0073 QString KoEmbeddedDocumentSaver::getFilename(const QString &prefix)
0074 {
0075     int index = 1;
0076     if (d->prefixes.contains(prefix)) {
0077         index = d->prefixes.value(prefix);
0078     }
0079 
0080     // This inserts prefix into the map if it's not there.
0081     d->prefixes[prefix] = index + 1;
0082 
0083     //return prefix + QString("%1").arg(index, 4, 10, QChar('0'));
0084     return prefix + QString("%1").arg(index);
0085 }
0086 
0087 void KoEmbeddedDocumentSaver::embedDocument(KoXmlWriter &writer, KoDocumentBase * doc)
0088 {
0089     Q_ASSERT(doc);
0090     d->documents.append(doc);
0091 
0092     QString ref;
0093     if (!doc->isStoredExtern()) {
0094         const QString name = getFilename("Object ");
0095 
0096         // set URL in document so that saveEmbeddedDocuments will save
0097         // the actual embedded object with the right name in the store.
0098         QUrl u;
0099         u.setScheme(INTERNAL_PROTOCOL);
0100         u.setPath(name);
0101         debugOdf << u;
0102         doc->setUrl(u);
0103         ref = "./" + name;
0104     } else {
0105         ref = doc->url().url();
0106     }
0107 
0108     debugOdf << "saving reference to embedded document as" << ref;
0109     writer.addAttribute("xlink:href", /*"#" + */ref);
0110 
0111     //<draw:object xlink:type="simple" xlink:show="embed"
0112     //    xlink:actuate="onLoad" xlink:href="#./Object 1"/>
0113     writer.addAttribute("xlink:type", "simple");
0114     writer.addAttribute("xlink:show", "embed");
0115     writer.addAttribute("xlink:actuate", "onLoad");
0116 
0117 }
0118 
0119 // Examples:
0120 // Videos/Video1.mov  ← the number is autogenerated
0121 // Videos/Video2.mov
0122 // Object1/foo  ← the number is autogenerated
0123 // Object1/bar
0124 
0125 // Note: The contents QByteArray is implicitly shared.  It needs to be
0126 //       copied since otherwise the actual array may disappear before
0127 //       the real saving is done.
0128 //
0129 void KoEmbeddedDocumentSaver::embedFile(KoXmlWriter &writer, const char *element,
0130                                         const QString &path, const QByteArray &mimeType,
0131                                         const QByteArray &contents)
0132 {
0133     // Put the file in the list of files to be written to the store later.
0134     FileEntry  *entry = new FileEntry;
0135     entry->mimeType = mimeType;
0136     entry->path = path;
0137     entry->contents = contents;
0138     d->files.append(entry);
0139 
0140     writer.startElement(element);
0141     // Write the attributes that refer to the file.
0142 
0143     //<draw:object xlink:href="#./Object 1" xlink:type="simple" xlink:show="embed"
0144     //             xlink:actuate="onLoad"/>
0145     writer.addAttribute("xlink:type", "simple");
0146     writer.addAttribute("xlink:show", "embed");
0147     writer.addAttribute("xlink:actuate", "onLoad");
0148 
0149     debugOdf << "saving reference to embedded file as" << path;
0150     writer.addAttribute("xlink:href", path);
0151     writer.endElement();
0152 }
0153 
0154 void KoEmbeddedDocumentSaver::saveFile(const QString &path, const QByteArray &mimeType,
0155                                        const QByteArray &contents)
0156 {
0157     // Put the file in the list of files to be written to the store later.
0158     FileEntry  *entry = new FileEntry;
0159     entry->mimeType = mimeType;
0160     entry->path     = path;
0161     entry->contents = contents;
0162     d->files.append(entry);
0163 
0164     debugOdf << "saving reference to embedded file as" << path;
0165 }
0166 
0167 /**
0168  *
0169  */
0170 void KoEmbeddedDocumentSaver::saveManifestEntry(const QString &fullPath, const QString &mediaType,
0171                                                 const QString &version)
0172 {
0173     d->manifestEntries.append(new KoOdfManifestEntry(fullPath, mediaType, version));
0174 }
0175 
0176 
0177 bool KoEmbeddedDocumentSaver::saveEmbeddedDocuments(KoDocumentBase::SavingContext & documentContext)
0178 {
0179     KoStore *store = documentContext.odfStore.store();
0180 
0181     // Write embedded documents.
0182     foreach(KoDocumentBase *doc, d->documents) {
0183         QString path;
0184         if (doc->isStoredExtern()) {
0185             debugOdf << " external (don't save) url:" << doc->url().url();
0186             path = doc->url().url();
0187         } else {
0188             // The name comes from addEmbeddedDocument (which was set while saving the document).
0189             Q_ASSERT(doc->url().scheme() == INTERNAL_PROTOCOL);
0190             const QString name = doc->url().path();
0191             debugOdf << "saving" << name;
0192 
0193             if (doc->nativeOasisMimeType().isEmpty()) {
0194                 // Embedded object doesn't support OpenDocument, save in the old format.
0195                 debugOdf << "Embedded object doesn't support OpenDocument, save in the old format.";
0196 
0197                 if (!doc->saveToStore(store, name)) {
0198                     return false;
0199                 }
0200             } else {
0201                 // To make the children happy cd to the correct directory
0202                 store->pushDirectory();
0203                 store->enterDirectory(name);
0204 
0205                 bool ok = doc->saveOdf(documentContext);
0206 
0207                 // Now that we're done leave the directory again
0208                 store->popDirectory();
0209 
0210                 if (!ok) {
0211                     warnOdf << "KoEmbeddedDocumentSaver::saveEmbeddedDocuments failed";
0212                     return false;
0213                 }
0214             }
0215 
0216             Q_ASSERT(doc->url().scheme() == INTERNAL_PROTOCOL);
0217             path = store->currentPath();
0218             if (!path.isEmpty()) {
0219                 path += '/';
0220             }
0221             path += doc->url().path();
0222             if (path.startsWith(QLatin1Char('/'))) {
0223                 path.remove(0, 1);   // remove leading '/', no wanted in manifest
0224             }
0225         }
0226 
0227         // OOo uses a trailing slash for the path to embedded objects (== directories)
0228         if (!path.endsWith('/')) {
0229             path += '/';
0230         }
0231         QByteArray mimetype = doc->nativeOasisMimeType();
0232         documentContext.odfStore.manifestWriter()->addManifestEntry(path, mimetype);
0233     }
0234 
0235     // Write the embedded files.
0236     foreach(FileEntry *entry, d->files) {
0237         QString path = entry->path;
0238         debugOdf << "saving" << path;
0239 
0240         // To make the children happy cd to the correct directory
0241         store->pushDirectory();
0242 
0243         int index = path.lastIndexOf('/');
0244         const QString dirPath = path.left(index);
0245         const QString fileName = path.right(path.size() - index - 1);
0246         store->enterDirectory(dirPath);
0247 
0248         if (!store->open(fileName)) {
0249             return false;
0250         }
0251         store->write(entry->contents);
0252         store->close();
0253 
0254         // Now that we're done leave the directory again
0255         store->popDirectory();
0256 
0257         // Create the manifest entry.
0258         if (path.startsWith(QLatin1String("./"))) {
0259             path.remove(0, 2);   // remove leading './', not wanted in manifest
0260         }
0261         documentContext.odfStore.manifestWriter()->addManifestEntry(path, entry->mimeType);
0262     }
0263 
0264     // Write the manifest entries.
0265     KoXmlWriter *manifestWriter = documentContext.odfStore.manifestWriter();
0266     foreach(KoOdfManifestEntry *entry, d->manifestEntries) {
0267         manifestWriter->startElement("manifest:file-entry");
0268         manifestWriter->addAttribute("manifest:version", entry->version());
0269         manifestWriter->addAttribute("manifest:media-type", entry->mediaType());
0270         manifestWriter->addAttribute("manifest:full-path", entry->fullPath());
0271         manifestWriter->endElement(); // manifest:file-entry
0272     }
0273 
0274     return true;
0275 }