Warning, file /office/calligra/libs/odf/KoOdfLoadingContext.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) 2005 David Faure <faure@kde.org> 0003 Copyright (C) 2010 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 // Own 0022 #include "KoOdfLoadingContext.h" 0023 0024 #include "OdfDebug.h" 0025 0026 // Calligra 0027 #include <KoOdfReadStore.h> 0028 #include <KoOdfStylesReader.h> 0029 #include <KoStore.h> 0030 #include <KoStoreDevice.h> 0031 #include <KoXmlNS.h> 0032 #include <KoOdfManifestEntry.h> 0033 #include "KoStyleStack.h" 0034 0035 // Qt 0036 #include <QStandardPaths> 0037 #include <QMimeDatabase> 0038 #include <QMimeType> 0039 0040 0041 class Q_DECL_HIDDEN KoOdfLoadingContext::Private 0042 { 0043 public: 0044 Private(KoOdfStylesReader &sr, KoStore *s) 0045 : store(s), 0046 stylesReader(sr), 0047 generatorType(KoOdfLoadingContext::Unknown), 0048 metaXmlParsed(false), 0049 useStylesAutoStyles(false) 0050 { 0051 } 0052 0053 ~Private() { 0054 qDeleteAll(manifestEntries); 0055 } 0056 0057 KoStore *store; 0058 KoOdfStylesReader &stylesReader; 0059 KoStyleStack styleStack; 0060 0061 mutable QString generator; 0062 GeneratorType generatorType; 0063 mutable bool metaXmlParsed; 0064 bool useStylesAutoStyles; 0065 0066 KoXmlDocument manifestDoc; 0067 QHash<QString, KoOdfManifestEntry *> manifestEntries; 0068 0069 0070 KoOdfStylesReader defaultStylesReader; 0071 KoXmlDocument doc; // the doc needs to be kept around so it is possible to access the styles 0072 }; 0073 0074 KoOdfLoadingContext::KoOdfLoadingContext(KoOdfStylesReader &stylesReader, KoStore* store, const QString &defaultStylesResourcePath) 0075 : d(new Private(stylesReader, store)) 0076 { 0077 // Ideally this should be done by KoDocument and passed as argument here... 0078 KoOdfReadStore oasisStore(store); 0079 QString dummy; 0080 (void)oasisStore.loadAndParse("tar:/META-INF/manifest.xml", d->manifestDoc, dummy); 0081 0082 if (!defaultStylesResourcePath.isEmpty()) { 0083 Q_ASSERT(defaultStylesResourcePath.endsWith(QLatin1Char('/'))); 0084 const QString fileName = 0085 QStandardPaths::locate(QStandardPaths::GenericDataLocation, 0086 defaultStylesResourcePath + "defaultstyles.xml"); 0087 if ( ! fileName.isEmpty() ) { 0088 QFile file( fileName ); 0089 QString errorMessage; 0090 if ( KoOdfReadStore::loadAndParse( &file, d->doc, errorMessage, fileName ) ) { 0091 d->defaultStylesReader.createStyleMap( d->doc, true ); 0092 } 0093 else { 0094 warnOdf << "reading of defaultstyles.xml failed:" << errorMessage; 0095 } 0096 } 0097 else { 0098 warnOdf << "defaultstyles.xml not found"; 0099 } 0100 } 0101 0102 if (!parseManifest(d->manifestDoc)) { 0103 debugOdf << "could not parse manifest document"; 0104 } 0105 } 0106 0107 KoOdfLoadingContext::~KoOdfLoadingContext() 0108 { 0109 delete d; 0110 } 0111 0112 void KoOdfLoadingContext::setManifestFile(const QString& fileName) { 0113 KoOdfReadStore oasisStore(d->store); 0114 QString dummy; 0115 (void)oasisStore.loadAndParse(fileName, d->manifestDoc, dummy); 0116 if (!parseManifest(d->manifestDoc)) { 0117 debugOdf << "could not parse manifest document"; 0118 } 0119 } 0120 0121 void KoOdfLoadingContext::fillStyleStack(const KoXmlElement& object, const QString &nsURI, const QString &attrName, const QString &family) 0122 { 0123 // find all styles associated with an object and push them on the stack 0124 if (object.hasAttributeNS(nsURI, attrName)) { 0125 const QString styleName = object.attributeNS(nsURI, attrName, QString()); 0126 const KoXmlElement * style = d->stylesReader.findStyle(styleName, family, d->useStylesAutoStyles); 0127 0128 if (style) 0129 addStyles(style, family, d->useStylesAutoStyles); 0130 else 0131 warnOdf << "style" << styleName << "not found in" << (d->useStylesAutoStyles ? "styles.xml" : "content.xml"); 0132 } 0133 } 0134 0135 void KoOdfLoadingContext::addStyles(const KoXmlElement* style, const QString &family, bool usingStylesAutoStyles) 0136 { 0137 Q_ASSERT(style); 0138 if (!style) return; 0139 0140 // this recursive function is necessary as parent styles can have parents themselves 0141 if (style->hasAttributeNS(KoXmlNS::style, "parent-style-name")) { 0142 const QString parentStyleName = style->attributeNS(KoXmlNS::style, "parent-style-name", QString()); 0143 const KoXmlElement* parentStyle = d->stylesReader.findStyle(parentStyleName, family, usingStylesAutoStyles); 0144 0145 if (parentStyle) 0146 addStyles(parentStyle, family, usingStylesAutoStyles); 0147 else { 0148 warnOdf << "Parent style not found: " << family << parentStyleName << usingStylesAutoStyles; 0149 //we are handling a non compliant odf file. let's at the very least load the application default, and the eventual odf default 0150 if (!family.isEmpty()) { 0151 const KoXmlElement* def = d->stylesReader.defaultStyle(family); 0152 if (def) { // then, the default style for this family 0153 d->styleStack.push(*def); 0154 } 0155 } 0156 } 0157 } else if (!family.isEmpty()) { 0158 const KoXmlElement* def = d->stylesReader.defaultStyle(family); 0159 if (def) { // then, the default style for this family 0160 d->styleStack.push(*def); 0161 } 0162 } 0163 0164 //debugOdf <<"pushing style" << style->attributeNS( KoXmlNS::style,"name", QString() ); 0165 d->styleStack.push(*style); 0166 } 0167 0168 void KoOdfLoadingContext::parseGenerator() const 0169 { 0170 // Regardless of whether we cd into the parent directory 0171 // or not to find a meta.xml, restore the directory that 0172 // we were in afterwards. 0173 d->store->pushDirectory(); 0174 0175 // Some embedded documents to not contain their own meta.xml 0176 // Use the parent directory's instead. 0177 if (!d->store->hasFile("meta.xml")) 0178 // Only has an effect if there is a parent directory 0179 d->store->leaveDirectory(); 0180 0181 if (d->store->hasFile("meta.xml")) { 0182 KoXmlDocument metaDoc; 0183 KoOdfReadStore oasisStore(d->store); 0184 QString errorMsg; 0185 if (oasisStore.loadAndParse("meta.xml", metaDoc, errorMsg)) { 0186 KoXmlNode meta = KoXml::namedItemNS(metaDoc, KoXmlNS::office, "document-meta"); 0187 KoXmlNode office = KoXml::namedItemNS(meta, KoXmlNS::office, "meta"); 0188 KoXmlElement generator = KoXml::namedItemNS(office, KoXmlNS::meta, "generator"); 0189 if (!generator.isNull()) { 0190 d->generator = generator.text(); 0191 if (d->generator.startsWith(QLatin1String("Calligra"))) { 0192 d->generatorType = Calligra; 0193 } 0194 // NeoOffice is a port of OpenOffice to Mac OS X 0195 else if (d->generator.startsWith(QLatin1String("OpenOffice.org")) || 0196 d->generator.startsWith(QLatin1String("NeoOffice")) || 0197 d->generator.startsWith(QLatin1String("LibreOffice")) || 0198 d->generator.startsWith(QLatin1String("StarOffice")) || 0199 d->generator.startsWith(QLatin1String("Lotus Symphony"))) { 0200 d->generatorType = OpenOffice; 0201 } 0202 else if (d->generator.startsWith(QLatin1String("MicrosoftOffice"))) { 0203 d->generatorType = MicrosoftOffice; 0204 } 0205 } 0206 } 0207 } 0208 d->metaXmlParsed = true; 0209 0210 d->store->popDirectory(); 0211 } 0212 0213 QString KoOdfLoadingContext::generator() const 0214 { 0215 if (!d->metaXmlParsed && d->store) { 0216 parseGenerator(); 0217 } 0218 return d->generator; 0219 } 0220 0221 KoOdfLoadingContext::GeneratorType KoOdfLoadingContext::generatorType() const 0222 { 0223 if (!d->metaXmlParsed && d->store) { 0224 parseGenerator(); 0225 } 0226 return d->generatorType; 0227 } 0228 0229 KoStore *KoOdfLoadingContext::store() const 0230 { 0231 return d->store; 0232 } 0233 0234 KoOdfStylesReader &KoOdfLoadingContext::stylesReader() 0235 { 0236 return d->stylesReader; 0237 } 0238 0239 /** 0240 * Get the application default styles styleReader 0241 */ 0242 KoOdfStylesReader &KoOdfLoadingContext::defaultStylesReader() 0243 { 0244 return d->defaultStylesReader; 0245 } 0246 0247 KoStyleStack &KoOdfLoadingContext::styleStack() const 0248 { 0249 return d->styleStack; 0250 } 0251 0252 void KoOdfLoadingContext::setUseStylesAutoStyles(bool useStylesAutoStyles) 0253 { 0254 d->useStylesAutoStyles = useStylesAutoStyles; 0255 } 0256 0257 bool KoOdfLoadingContext::useStylesAutoStyles() const 0258 { 0259 return d->useStylesAutoStyles; 0260 } 0261 0262 QString KoOdfLoadingContext::mimeTypeForPath(const QString& path, bool guess) const 0263 { 0264 QHash<QString, KoOdfManifestEntry *>::ConstIterator it(d->manifestEntries.constFind(path)); 0265 if (it == d->manifestEntries.constEnd()) { 0266 // try to find it with an added / at the end 0267 QString dirPath = path + '/'; 0268 it = d->manifestEntries.constFind(dirPath); 0269 } 0270 if (it != d->manifestEntries.constEnd()) { 0271 QString mimeType = it.value()->mediaType(); 0272 0273 // figure out mimetype by content if it is not provided 0274 if (mimeType.isEmpty() && guess) { 0275 Q_ASSERT(!d->store->isOpen()); 0276 if (d->store->open(path)) { 0277 KoStoreDevice device(d->store); 0278 QByteArray data = device.read(16384); 0279 d->store->close(); 0280 QMimeDatabase db; 0281 QMimeType mtp = db.mimeTypeForData(data); 0282 mimeType = mtp.name(); 0283 if (!mimeType.isEmpty()) { 0284 it.value()->setMediaType(mimeType); 0285 } 0286 } 0287 } 0288 0289 return mimeType; 0290 } 0291 else { 0292 return QString(); 0293 } 0294 } 0295 0296 QList<KoOdfManifestEntry*> KoOdfLoadingContext::manifestEntries() const 0297 { 0298 return d->manifestEntries.values(); 0299 } 0300 0301 bool KoOdfLoadingContext::parseManifest(const KoXmlDocument &manifestDocument) 0302 { 0303 // First find the manifest:manifest node. 0304 KoXmlNode n = manifestDocument.firstChild(); 0305 debugOdf << "Searching for manifest:manifest " << n.toElement().nodeName(); 0306 for (; !n.isNull(); n = n.nextSibling()) { 0307 if (!n.isElement()) { 0308 debugOdf << "NOT element"; 0309 continue; 0310 } else { 0311 debugOdf << "element"; 0312 } 0313 0314 debugOdf << "name:" << n.toElement().localName() 0315 << "namespace:" << n.toElement().namespaceURI(); 0316 0317 if (n.toElement().localName() == "manifest" 0318 && n.toElement().namespaceURI() == KoXmlNS::manifest) 0319 { 0320 debugOdf << "found manifest:manifest"; 0321 break; 0322 } 0323 } 0324 if (n.isNull()) { 0325 debugOdf << "Could not find manifest:manifest"; 0326 return false; 0327 } 0328 0329 // Now loop through the children of the manifest:manifest and 0330 // store all the manifest:file-entry elements. 0331 const KoXmlElement manifestElement = n.toElement(); 0332 for (n = manifestElement.firstChild(); !n.isNull(); n = n.nextSibling()) { 0333 0334 if (!n.isElement()) 0335 continue; 0336 0337 KoXmlElement el = n.toElement(); 0338 if (!(el.localName() == "file-entry" && el.namespaceURI() == KoXmlNS::manifest)) 0339 continue; 0340 0341 QString fullPath = el.attributeNS(KoXmlNS::manifest, "full-path", QString()); 0342 QString mediaType = el.attributeNS(KoXmlNS::manifest, "media-type", QString("")); 0343 QString version = el.attributeNS(KoXmlNS::manifest, "version", QString()); 0344 0345 // Only if fullPath is valid, should we store this entry. 0346 // If not, we don't bother to find out exactly what is wrong, we just skip it. 0347 if (!fullPath.isNull()) { 0348 d->manifestEntries.insert(fullPath, 0349 new KoOdfManifestEntry(fullPath, mediaType, version)); 0350 } 0351 } 0352 0353 return true; 0354 }