File indexing completed on 2024-05-12 16:37:11
0001 /* This file is part of the KDE project 0002 * Copyright (C) 2005 David Faure <faure@kde.org> 0003 * Copyright (C) 2007 Thomas Zander <zander@kde.org> 0004 * Copyright (C) 2007 Sebastian Sauer <mail@dipe.org> 0005 * Copyright (C) 2007 Pierre Ducroquet <pinaraf@gmail.com> 0006 * Copyright (C) 2007-2008 Thorsten Zachmann <zachmann@kde.org> 0007 * 0008 * This library is free software; you can redistribute it and/or 0009 * modify it under the terms of the GNU Library General Public 0010 * License as published by the Free Software Foundation; either 0011 * version 2 of the License, or (at your option) any later version. 0012 * 0013 * This library is distributed in the hope that it will be useful, 0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 * Library General Public License for more details. 0017 * 0018 * You should have received a copy of the GNU Library General Public License 0019 * along with this library; see the file COPYING.LIB. If not, write to 0020 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0021 * Boston, MA 02110-1301, USA. 0022 */ 0023 0024 #include "KWOdfLoader.h" 0025 0026 #include "KWOdfSharedLoadingData.h" 0027 #include "KWDocument.h" 0028 #include "KWPage.h" 0029 #include "KWPageManager.h" 0030 #include "frames/KWTextFrameSet.h" 0031 #include "WordsDebug.h" 0032 0033 // calligra 0034 #include <KoOdfStylesReader.h> 0035 #include <KoOasisSettings.h> 0036 #include <KoOdfReadStore.h> 0037 #include <KoXmlReader.h> 0038 #include <KoXmlNS.h> 0039 #include <KoShapeFactoryBase.h> 0040 #include <KoTextShapeData.h> 0041 #include <KoTextDocument.h> 0042 #include <KoTextEditor.h> 0043 #include <KoShapeLoadingContext.h> 0044 #include <KoStyleManager.h> 0045 #include <KoOdfLoadingContext.h> 0046 #include <KoUpdater.h> 0047 #include <KoProgressUpdater.h> 0048 #include <KoVariableManager.h> 0049 #include <KoInlineTextObjectManager.h> 0050 #include <KoUnit.h> 0051 #include <KoSectionModel.h> 0052 0053 #ifdef SHOULD_BUILD_RDF 0054 #include <KoDocumentRdf.h> 0055 #endif 0056 0057 // Qt includes 0058 #include <QTextCursor> 0059 0060 #include <KoDocumentRdfBase.h> 0061 0062 KWOdfLoader::KWOdfLoader(KWDocument *document) 0063 : QObject(document), 0064 m_document(document) 0065 { 0066 connect(this, SIGNAL(progressUpdate(int)), m_document, SIGNAL(sigProgress(int))); 0067 } 0068 0069 KWOdfLoader::~KWOdfLoader() 0070 { 0071 } 0072 0073 KWDocument *KWOdfLoader::document() const 0074 { 0075 return m_document; 0076 } 0077 0078 //1.6: KWDocument::loadOasis 0079 bool KWOdfLoader::load(KoOdfReadStore &odfStore) 0080 { 0081 //debugWords << "========================> KWOdfLoader::load START"; 0082 0083 QPointer<KoUpdater> updater; 0084 QPointer<KoUpdater> loadUpdater; 0085 if (m_document->progressUpdater()) { 0086 updater = m_document->progressUpdater()->startSubtask(1, "KWOdfLoader::load"); 0087 loadUpdater = m_document->progressUpdater()->startSubtask(5, "KWOdfLoader::loadOdf"); 0088 updater->setProgress(0); 0089 loadUpdater->setProgress(0); 0090 } 0091 0092 KoXmlElement content = odfStore.contentDoc().documentElement(); 0093 KoXmlElement realBody(KoXml::namedItemNS(content, KoXmlNS::office, "body")); 0094 if (realBody.isNull()) { 0095 errorWords << "No office:body found!" << endl; 0096 m_document->setErrorMessage(i18n("Invalid OASIS OpenDocument file. No office:body tag found.")); 0097 return false; 0098 } 0099 0100 KoXmlElement body = KoXml::namedItemNS(realBody, KoXmlNS::office, "text"); 0101 if (body.isNull()) { 0102 errorWords << "No office:text found!" << endl; 0103 KoXmlElement childElem; 0104 QString localName; 0105 forEachElement(childElem, realBody) 0106 localName = childElem.localName(); 0107 if (localName.isEmpty()) 0108 m_document->setErrorMessage(i18n("Invalid OASIS OpenDocument file. No tag found inside office:body.")); 0109 else 0110 m_document->setErrorMessage(i18n("This is not a word processing document, but %1. Please try opening it with the appropriate application.", KoDocument::tagNameToDocumentType(localName))); 0111 return false; 0112 } 0113 0114 // Load attributes from the office:text. These are text:global and text:use-soft-page-breaks. 0115 QString textGlobal = body.attributeNS(KoXmlNS::text, "global"); 0116 bool isTextGlobal = (textGlobal == "true"); 0117 if (isTextGlobal) { 0118 m_document->setIsMasterDocument(true); 0119 } 0120 // FIXME: text:use-soft-page-breaks 0121 0122 if (updater) updater->setProgress(20); 0123 0124 KoOdfLoadingContext odfContext(odfStore.styles(), odfStore.store(), QLatin1String("calligrawords/styles/")); 0125 KoShapeLoadingContext sc(odfContext, m_document->resourceManager()); 0126 sc.setDocumentRdf(m_document->documentRdf()); 0127 0128 // Load user defined variable declarations 0129 if (KoVariableManager *variableManager = m_document->inlineTextObjectManager()->variableManager()) { 0130 variableManager->loadOdf(body); 0131 } 0132 0133 // Load all styles before the corresponding paragraphs try to use them! 0134 KWOdfSharedLoadingData *sharedData = new KWOdfSharedLoadingData(this); 0135 sc.addSharedData(KOTEXT_SHARED_LOADING_ID, sharedData); 0136 KoStyleManager *styleManager = m_document->resourceManager()->resource(KoText::StyleManager).value<KoStyleManager*>(); 0137 Q_ASSERT(styleManager); 0138 sharedData->loadOdfStyles(sc, styleManager); 0139 0140 if (updater) updater->setProgress(40); 0141 0142 loadMasterPageStyles(sc); 0143 0144 // add page background frame set 0145 KWFrameSet *pageBackgroundFrameSet = new KWFrameSet(Words::BackgroundFrameSet); 0146 m_document->addFrameSet(pageBackgroundFrameSet); 0147 0148 #if 0 //1.6: 0149 KWOasisLoader oasisLoader(this); 0150 // <text:page-sequence> oasis extension for DTP (2003-10-27 post by Daniel) 0151 m_processingType = (!KoXml::namedItemNS(body, KoXmlNS::text, "page-sequence").isNull()) ? DTP : WP; 0152 m_hasTOC = false; 0153 m_tabStop = MM_TO_POINT(15); 0154 const KoXmlElement *defaultParagStyle = styles.defaultStyle("paragraph"); 0155 if (defaultParagStyle) { 0156 KoStyleStack stack; 0157 stack.push(*defaultParagStyle); 0158 stack.setTypeProperties("paragraph"); 0159 QString tabStopVal = stack.property(KoXmlNS::style, "tab-stop-distance"); 0160 if (!tabStopVal.isEmpty()) m_tabStop = KoUnit::parseValue(tabStopVal); 0161 } 0162 m_initialEditing = 0; 0163 // TODO MAILMERGE 0164 // Variable settings 0165 // By default display real variable value 0166 if (!isReadWrite()) 0167 m_varColl->variableSetting()->setDisplayFieldCode(false); 0168 #endif 0169 0170 // Load all styles before the corresponding paragraphs try to use them! 0171 #if 0 //1.6: 0172 if (m_frameStyleColl->loadOasisStyles(context) == 0) { 0173 // no styles loaded -> load default styles 0174 loadDefaultFrameStyleTemplates(); 0175 } 0176 if (m_tableStyleColl->loadOasisStyles(context, *m_styleColl, *m_frameStyleColl) == 0) { 0177 // no styles loaded -> load default styles 0178 loadDefaultTableStyleTemplates(); 0179 } 0180 static_cast<KWVariableSettings *>(m_varColl->variableSetting())->loadNoteConfiguration(styles.officeStyle()); 0181 loadDefaultTableTemplates(); 0182 //#else 0183 /* 0184 // We always needs at least one valid default paragraph style 0185 KoParagraphStyle *defaultParagraphStyle = m_document->styleManager()->defaultParagraphStyle(); 0186 //const KoXmlElement *defaultParagraphStyle = context.stylesReader().defaultStyle("paragraph"); 0187 //if(! defaultParagraphStyle) { 0188 KoParagraphStyle *parastyle = new KoParagraphStyle(); 0189 parastyle->setName("Standard"); 0190 m_document->styleManager()->add(parastyle); 0191 context.styleStack().setTypeProperties("paragraph"); // load all style attributes from "style:paragraph-properties" 0192 parastyle->loadOasis(context.styleStack()); // load the KoParagraphStyle from the stylestack 0193 KoCharacterStyle *charstyle = parastyle->characterStyle(); 0194 context.styleStack().setTypeProperties("text"); // load all style attributes from "style:text-properties" 0195 charstyle->loadOasis(context.styleStack()); // load the KoCharacterStyle from the stylestack 0196 //} 0197 */ 0198 #endif 0199 0200 // load text:page-sequence 0201 KoXmlElement pageSequence = KoXml::namedItemNS(body, KoXmlNS::text, "page-sequence"); 0202 if (! pageSequence.isNull()) { 0203 KWPageManager *pageManager = m_document->pageManager(); 0204 KoXmlElement page; 0205 forEachElement(page, pageSequence) { 0206 if (page.namespaceURI() == KoXmlNS::text && page.localName() == "page") { 0207 QString master = page.attributeNS(KoXmlNS::text, "master-page-name", QString()); 0208 pageManager->appendPage(pageManager->pageStyle(master)); 0209 } 0210 } 0211 } 0212 0213 if (updater) updater->setProgress(50); 0214 0215 KoTextShapeData textShapeData; 0216 KWTextFrameSet *mainFs = new KWTextFrameSet(m_document, Words::MainTextFrameSet); 0217 mainFs->setPageStyle(m_document->pageManager()->pageStyle("Standard")); 0218 m_document->addFrameSet(mainFs); 0219 textShapeData.setDocument(mainFs->document(), false); 0220 sc.setSectionModel(new KoSectionModel(mainFs->document())); 0221 0222 // disable the undo recording during load so the kotexteditor is in sync with 0223 // the app's undostack 0224 textShapeData.document()->setUndoRedoEnabled(false); 0225 0226 if (updater) updater->setProgress(60); 0227 0228 // load the main text shape right here so we can use the progress information of the KoTextLoader 0229 KoTextLoader loader(sc); 0230 QTextCursor cursor(textShapeData.document()); 0231 0232 if (loadUpdater) { 0233 connect(&loader, SIGNAL(sigProgress(int)), loadUpdater, SLOT(setProgress(int))); 0234 } 0235 0236 loader.loadBody(body, cursor); // now let's load the body from the ODF KoXmlElement. 0237 0238 sharedData->connectFlowingTextShapes(); 0239 0240 if (loadUpdater) { 0241 loadUpdater->setProgress(100); 0242 } 0243 0244 //reenable the undo recording 0245 textShapeData.document()->setUndoRedoEnabled(true); 0246 0247 KoTextEditor *editor = KoTextDocument(textShapeData.document()).textEditor(); 0248 if (editor) // at one point we have to get the position from the odf doc instead. 0249 editor->setPosition(0); 0250 0251 if (updater) updater->setProgress(90); 0252 0253 // Grab weak references to all the Rdf stuff that was loaded 0254 if (KoDocumentRdfBase *rdf = m_document->documentRdf()) { 0255 rdf->updateInlineRdfStatements(textShapeData.document()); 0256 } 0257 0258 if (updater) updater->setProgress(95); 0259 0260 loadSettings(odfStore.settingsDoc(), textShapeData.document()); 0261 0262 if (updater) updater->setProgress(100); 0263 return true; 0264 } 0265 0266 void KWOdfLoader::loadSettings(const KoXmlDocument &settingsDoc, QTextDocument *textDoc) 0267 { 0268 KoTextDocument(textDoc).setRelativeTabs(true); 0269 if (settingsDoc.isNull()) 0270 return; 0271 0272 debugWords << "KWOdfLoader::loadSettings"; 0273 KoOasisSettings settings(settingsDoc); 0274 KoOasisSettings::Items viewSettings = settings.itemSet("ooo:view-settings"); 0275 if (!viewSettings.isNull()) { 0276 m_document->setUnit(KoUnit::fromSymbol(viewSettings.parseConfigItemString("unit"))); 0277 } 0278 0279 KoOasisSettings::Items configurationSettings = settings.itemSet("ooo:configuration-settings"); 0280 if (!configurationSettings.isNull()) { 0281 const QString ignorelist = configurationSettings.parseConfigItemString("SpellCheckerIgnoreList"); 0282 debugWords << "Ignorelist:" << ignorelist; 0283 0284 KoTextDocument(textDoc).setRelativeTabs(configurationSettings.parseConfigItemBool("TabsRelativeToIndent", true)); 0285 0286 KoTextDocument(textDoc).setParaTableSpacingAtStart(configurationSettings.parseConfigItemBool("AddParaTableSpacingAtStart", true)); 0287 } 0288 //1.6: m_document->variableCollection()->variableSetting()->loadOasis(settings); 0289 } 0290 0291 void KWOdfLoader::loadMasterPageStyles(KoShapeLoadingContext &context) 0292 { 0293 debugWords << " !!!!!!!!!!!!!! loadMasterPageStyles called !!!!!!!!!!!!!!"; 0294 debugWords << "Number of items :" << context.odfLoadingContext().stylesReader().masterPages().size(); 0295 0296 //TODO probably we should introduce more logic to handle the "standard" even 0297 //in faulty documents. See also bugreport #129585 as example. 0298 const KoOdfStylesReader &styles = context.odfLoadingContext().stylesReader(); 0299 QHashIterator<QString, KoXmlElement *> it(styles.masterPages()); 0300 while (it.hasNext()) { 0301 it.next(); 0302 Q_ASSERT(! it.key().isEmpty()); 0303 const KoXmlElement *masterNode = it.value(); 0304 Q_ASSERT(masterNode); 0305 QString displayName = masterNode->attributeNS(KoXmlNS::style, "display-name", QString()); 0306 KWPageStyle masterPage = m_document->pageManager()->pageStyle(it.key()); 0307 if (!masterPage.isValid()) // use display-name as fall-back if there is no page-style with the defined name. See bug 281922 and 282082. 0308 masterPage = m_document->pageManager()->pageStyle(displayName); 0309 bool alreadyExists = masterPage.isValid(); 0310 if (!alreadyExists) 0311 masterPage = KWPageStyle(it.key(), displayName); 0312 const KoXmlElement *masterPageStyle = styles.findStyle(masterNode->attributeNS(KoXmlNS::style, "page-layout-name", QString())); 0313 if (masterPageStyle) { 0314 masterPage.loadOdf(context.odfLoadingContext(), *masterNode, *masterPageStyle, m_document->resourceManager()); 0315 loadHeaderFooter(context, masterPage, *masterNode, LoadHeader); 0316 loadHeaderFooter(context, masterPage, *masterNode, LoadFooter); 0317 } 0318 if (!alreadyExists) 0319 m_document->pageManager()->addPageStyle(masterPage); 0320 } 0321 } 0322 0323 // helper function to create a KWTextFrameSet for a header/footer. 0324 void KWOdfLoader::loadHeaderFooterFrame(KoShapeLoadingContext &context, const KWPageStyle &pageStyle, const KoXmlElement &elem, Words::TextFrameSetType fsType) 0325 { 0326 KWTextFrameSet *fs = new KWTextFrameSet(m_document, fsType); 0327 fs->setPageStyle(pageStyle); 0328 m_document->addFrameSet(fs); 0329 0330 debugWords << "KWOdfLoader::loadHeaderFooterFrame localName=" << elem.localName() << " type=" << fs->name(); 0331 0332 // use auto-styles from styles.xml, not those from content.xml 0333 context.odfLoadingContext().setUseStylesAutoStyles(true); 0334 0335 // disable the undo recording during load so the kotexteditor is in sync with 0336 // the app's undostack 0337 fs->document()->setUndoRedoEnabled(false); 0338 0339 KoTextLoader loader(context); 0340 QTextCursor cursor(fs->document()); 0341 loader.loadBody(elem, cursor); 0342 0343 fs->document()->setUndoRedoEnabled(true); 0344 0345 // restore use of auto-styles from content.xml, not those from styles.xml 0346 context.odfLoadingContext().setUseStylesAutoStyles(false); 0347 } 0348 0349 //1.6: KWOasisLoader::loadOasisHeaderFooter 0350 void KWOdfLoader::loadHeaderFooter(KoShapeLoadingContext &context, KWPageStyle &pageStyle, 0351 const KoXmlElement &masterPage, HFLoadType headerFooter) 0352 { 0353 // The actual content of the header/footer. 0354 KoXmlElement elem = KoXml::namedItemNS(masterPage, KoXmlNS::style, 0355 headerFooter == LoadHeader ? "header" : "footer"); 0356 0357 // The two additional elements <style:header-left> and <style:footer-left> 0358 // specifies if defined that even and odd pages should be displayed 0359 // different. If they are missing, the content of odd and even (aka left 0360 // and right) pages are the same. 0361 KoXmlElement leftElem = KoXml::namedItemNS(masterPage, KoXmlNS::style, 0362 headerFooter == LoadHeader ? "header-left" : "footer-left"); 0363 0364 // Used in KWPageStyle to determine if, and what kind of header/footer to use. 0365 Words::HeaderFooterType hfType = elem.isNull() ? Words::HFTypeNone 0366 : leftElem.isNull() ? Words::HFTypeUniform 0367 : Words::HFTypeEvenOdd; 0368 0369 // header-left and footer-left 0370 if (! leftElem.isNull()) { 0371 loadHeaderFooterFrame(context, pageStyle, leftElem, 0372 headerFooter == LoadHeader ? Words::EvenPagesHeaderTextFrameSet 0373 : Words::EvenPagesFooterTextFrameSet); 0374 } 0375 0376 // header and footer 0377 if (! elem.isNull()) { 0378 loadHeaderFooterFrame(context, pageStyle, elem, 0379 headerFooter == LoadHeader ? Words::OddPagesHeaderTextFrameSet 0380 : Words::OddPagesFooterTextFrameSet); 0381 } 0382 0383 if (headerFooter == LoadHeader) { 0384 pageStyle.setHeaderPolicy(hfType); 0385 } else { 0386 pageStyle.setFooterPolicy(hfType); 0387 } 0388 } 0389