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