kword

KWOasisLoader.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2005 David Faure <faure@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 * Boston, MA 02110-1301, USA.
17*/
18
19#include "KWOasisLoader.h"
20#include "KWDocument.h"
21#include "KWTextDocument.h"
22#include "KWTextFrameSet.h"
23#include "KWFrameSet.h"
24#include "KWPictureFrameSet.h"
25#include "KWLoadingInfo.h"
26#include "KWTableFrameSet.h"
27#include "KWPartFrameSet.h"
28#include "KWPageManager.h"
29#include "KWFormulaFrameSet.h"
30
31#include <KoStore.h>
32#include <KoOasisContext.h>
33#include <KoOasisStyles.h>
34#include <KoOasisStore.h>
35#include <KoOasisSettings.h>
36#include <KoXmlNS.h>
37#include <KoDom.h>
38#include <KoXmlNS.h>
39
40#include <kdebug.h>
41#include <tdelocale.h>
42
43#include <tqvaluelist.h>
44#include <tqdom.h>
45
46KWOasisLoader::KWOasisLoader( KWDocument* doc )
47 : m_doc( doc )
48{
49}
50
51// Warning, this method has no undo/redo support, it is *called* by the undo/redo commands.
52// cursor is set when pasting into a textframesetedit (kwcommand), 0 otherwise.
53TQValueList<KWFrame *> KWOasisLoader::insertOasisData( KoStore* store, KoTextCursor* cursor )
54{
55 TQValueList<KWFrame *> frames;
56 if ( store->bad() || !store->hasFile( "content.xml" ) )
57 {
58 kdError(32001) << "Invalid ZIP store in memory" << endl;
59 if ( !store->hasFile( "content.xml" ) )
60 kdError(32001) << "No content.xml file" << endl;
61 return frames;
62 }
63 store->disallowNameExpansion();
64
65 KoOasisStore oasisStore( store );
66 TQDomDocument contentDoc;
67 TQString errorMessage;
68 bool ok = oasisStore.loadAndParse( "content.xml", contentDoc, errorMessage );
69 if ( !ok ) {
70 kdError(32001) << "Error parsing content.xml: " << errorMessage << endl;
71 return frames;
72 }
73
74 KoOasisStyles oasisStyles;
75 TQDomDocument stylesDoc;
76 (void)oasisStore.loadAndParse( "styles.xml", stylesDoc, errorMessage );
77 // Load styles from style.xml
78 oasisStyles.createStyleMap( stylesDoc, true );
79 // Also load styles from content.xml
80 oasisStyles.createStyleMap( contentDoc, false );
81
82 m_doc->createLoadingInfo();
83
84 TQDomElement content = contentDoc.documentElement();
85
86 TQDomElement body( KoDom::namedItemNS( content, KoXmlNS::office, "body" ) );
87 if ( body.isNull() ) {
88 kdError(32001) << "No office:body found!" << endl;
89 return frames;
90 }
91 // We then want to use whichever element is the child of <office:body>,
92 // whether that's <office:text> or <office:presentation> or whatever.
93 TQDomElement iter, realBody;
94 forEachElement( iter, body ) {
95 realBody = iter;
96 }
97 if ( realBody.isNull() ) {
98 kdError(32001) << "No element found inside office:body!" << endl;
99 return frames;
100 }
101
102 KoOasisContext context( m_doc, *m_doc->variableCollection(), oasisStyles, store );
103 if ( cursor )
104 {
105 KWTextDocument * textdoc = static_cast<KWTextDocument *>(cursor->parag()->document());
106 KWTextFrameSet * textFs = textdoc->textFrameSet();
107
108 *cursor = textFs->textObject()->pasteOasisText( realBody, context, *cursor, m_doc->styleCollection() );
109
110 textFs->textObject()->setNeedSpellCheck( true );
111 }
112 else // No cursor available, load only the frames
113 {
114 // The main loop from KoTextDocument::loadOasisText but for frames only
115 // (can't paste text if there is no text-frameset being edited, where would it go?)
116 TQDomElement tag;
117 forEachElement( tag, realBody )
118 {
119 context.styleStack().save();
120 const TQString bodyTagLocalName = tag.localName();
121 kdDebug() << k_funcinfo << bodyTagLocalName << endl;
122 if ( bodyTagLocalName == "frame" && tag.namespaceURI() == KoXmlNS::draw )
123 {
124 KWFrame * frame = loadFrame( tag, context, KoPoint( 10, 10 ) /*offset pasted object*/ );
125 if ( frame )
126 {
127 frames.append( frame );
128 }
129 }
130#if 0 // TODO OASIS table:table
131 else if ( bodyTagLocalName == "table" && tag.namespaceURI() == KoXmlNS::table )
132 ;
133#endif
134 }
135 }
136
137 //kdDebug() << "KWOasisLoader::execute calling doc->completePasting" << endl;
138 m_doc->completeOasisPasting();
139 m_doc->deleteLoadingInfo();
140 return frames;
141}
142
143void KWOasisLoader::loadOasisSettings( const TQDomDocument& settingsDoc )
144{
145 KoOasisSettings settings( settingsDoc );
146 KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" );
147 if ( !viewSettings.isNull() )
148 {
149 m_doc->setUnit( KoUnit::unit(viewSettings.parseConfigItemString("unit")) );
150 }
151 loadOasisIgnoreList( settings );
152 m_doc->variableCollection()->variableSetting()->loadOasis( settings );
153}
154
155static TQString headerTypeToFramesetName( const TQString& localName, bool hasEvenOdd )
156{
157 if ( localName == "header" )
158 return hasEvenOdd ? i18n("Odd Pages Header") : i18n( "Header" );
159 if ( localName == "header-left" )
160 return i18n("Even Pages Header");
161 if ( localName == "footer" )
162 return hasEvenOdd ? i18n("Odd Pages Footer") : i18n( "Footer" );
163 if ( localName == "footer-left" )
164 return i18n("Even Pages Footer");
165 if ( localName == "header-first" ) // NOT OASIS COMPLIANT
166 return i18n("First Page Header");
167 if ( localName == "footer-first" ) // NOT OASIS COMPLIANT
168 return i18n("First Page Footer");
169 kdWarning(32001) << "Unknown tag in headerTypeToFramesetName: " << localName << endl;
170 return TQString();
171}
172
173static KWFrameSet::Info headerTypeToFrameInfo( const TQString& localName, bool /*hasEvenOdd*/ )
174{
175 if ( localName == "header" )
176 return KWFrameSet::FI_ODD_HEADER;
177 if ( localName == "header-left" )
178 return KWFrameSet::FI_EVEN_HEADER;
179 if ( localName == "footer" )
180 return KWFrameSet::FI_ODD_FOOTER;
181 if ( localName == "footer-left" )
182 return KWFrameSet::FI_EVEN_FOOTER;
183
184 // ######## KWord extension, because I'm too lazy.
185 // TODO: the real solution is a separate page layout for the first page.
186 if ( localName == "header-first" ) // NOT OASIS COMPLIANT
187 return KWFrameSet::FI_FIRST_HEADER;
188 if ( localName == "footer-first" ) // NOT OASIS COMPLIANT
189 return KWFrameSet::FI_FIRST_FOOTER;
190 return KWFrameSet::FI_BODY;
191}
192
193void KWOasisLoader::loadOasisHeaderFooter( const TQDomElement& headerFooter, bool hasEvenOdd, TQDomElement& style, KoOasisContext& context )
194{
195 const TQString localName = headerFooter.localName();
196 bool isHeader = localName.startsWith( "header" );
197
198 KWTextFrameSet *fs = new KWTextFrameSet( m_doc, headerTypeToFramesetName( localName, hasEvenOdd ) );
199 fs->setFrameSetInfo( headerTypeToFrameInfo( localName, hasEvenOdd ) );
200 m_doc->addFrameSet( fs, false );
201
202 if ( !style.isNull() )
203 context.styleStack().push( style );
204 KWFrame* frame = new KWFrame( fs, 29, isHeader?0:567, 798-29, 41 );
205 frame->loadCommonOasisProperties( context, fs, "header-footer" );
206 const TQString minHeight = context.styleStack().attributeNS( KoXmlNS::fo, "min-height" );
207 if ( !minHeight.isEmpty() )
208 frame->setMinimumFrameHeight( KoUnit::parseValue( minHeight ) );
209
210 frame->setFrameBehavior( KWFrame::AutoExtendFrame );
211 frame->setNewFrameBehavior( KWFrame::Copy );
212 fs->addFrame( frame );
213 if ( !style.isNull() )
214 context.styleStack().pop(); // don't let it be active when parsing the text
215
216 context.setUseStylesAutoStyles( true ); // use auto-styles from styles.xml, not those from content.xml
217 fs->loadOasisContent( headerFooter, context );
218 context.setUseStylesAutoStyles( false );
219
220 if ( isHeader )
221 m_doc->m_headerVisible = true;
222 else
223 m_doc->m_footerVisible = true;
224}
225
226void KWOasisLoader::loadOasisIgnoreList( const KoOasisSettings& settings )
227{
228 KoOasisSettings::Items configurationSettings = settings.itemSet( "configuration-settings" );
229 if ( !configurationSettings.isNull() )
230 {
231 const TQString ignorelist = configurationSettings.parseConfigItemString( "SpellCheckerIgnoreList" );
232 kdDebug()<<" ignorelist :"<<ignorelist<<endl;
233 m_doc->setSpellCheckIgnoreList( TQStringList::split( ',', ignorelist ) );
234 }
235}
236
237KWFrame* KWOasisLoader::loadFrame( const TQDomElement& frameTag, KoOasisContext& context, const KoPoint& offset )
238{
239 KWFrame* frame = 0;
240 TQDomElement elem;
241 forEachElement( elem, frameTag )
242 {
243 if ( elem.namespaceURI() != KoXmlNS::draw )
244 continue;
245 const TQString localName = elem.localName();
246 if ( localName == "text-box" )
247 {
248 //kdDebug()<<" append text-box\n";
249 frame = loadOasisTextBox( frameTag, elem, context );
250 break;
251 }
252 else if ( localName == "image" )
253 {
254 KWFrameSet* fs = new KWPictureFrameSet( m_doc, frameTag, elem, context );
255 m_doc->addFrameSet( fs, false );
256 frame = fs->frame(0);
257 break;
258 } else if ( localName == "object" )
259 {
260 TQDomElement mathElem; // will be set if we find <math:math>
261 TQDomElement childElem;
262 forEachElement( childElem, elem )
263 {
264 if ( childElem.localName() == "math"
265 && childElem.namespaceURI() == KoXmlNS::math ) {
266 mathElem = childElem;
267 }
268 }
269 if ( !mathElem.isNull() ) {
270 KWFormulaFrameSet* fs = new KWFormulaFrameSet( m_doc, frameTag, mathElem, context );
271 m_doc->addFrameSet( fs, false );
272 frame = fs->frame(0);
273 } else {
274 KWPartFrameSet* fs = new KWPartFrameSet( m_doc, frameTag, elem, context );
275 m_doc->addFrameSet( fs, false );
276 frame = fs->frame(0);
277 }
278 break;
279 }
280 }
281 if ( frame ) {
282 const TQString anchorType = frameTag.attributeNS( KoXmlNS::text, "anchor-type", TQString() );
283 if ( anchorType == "page" ) {
284 double x = KoUnit::parseValue( frameTag.attributeNS( KoXmlNS::svg, "x", TQString() ) );
285 double y = KoUnit::parseValue( frameTag.attributeNS( KoXmlNS::svg, "y", TQString() ) );
286 int pageNum = frameTag.attributeNS( KoXmlNS::text, "anchor-page-number", TQString() ).toInt();
287 // Ensure that we have enough pages
288 KWPageManager* pageManager = m_doc->pageManager();
289 while ( pageNum > pageManager->lastPageNumber() )
290 pageManager->appendPage();
291 frame->moveTopLeft( KoPoint( x, y + pageManager->topOfPage(pageNum) ) );
292 }
293 frame->moveBy( offset.x(), offset.y() );
294 }
295 return frame;
296}
297
298KWFrame* KWOasisLoader::loadOasisTextBox( const TQDomElement& frameTag, const TQDomElement& tag,
299 KoOasisContext& context )
300{
301 // Text frame chains. When seeing frame 'B' is chained to this frame A when loading,
302 // we store 'B' -> A, so that when loading B we can add it to A's frameset.
303 // If we load B first, no problem: when loading A we can chain.
304 // This is all made simpler by the fact that we don't have manually configurable order in KWord...
305 // But it's made more complex by the fact that frames don't have names in KWord (framesets do).
306 // Hence the framename temporary storage in KWLoadingInfo
307
308 KWLoadingInfo* loadingInfo = m_doc->loadingInfo();
309 KWTextFrameSet* fs = 0;
310 TQString frameName = frameTag.attributeNS( KoXmlNS::draw, "name", TQString() );
311 TQString chainNextName = tag.attributeNS( KoXmlNS::draw, "chain-next-name", TQString() );
312 if ( !chainNextName.isEmpty() && loadingInfo ) { // 'B' in the above example
313 kdDebug(32001) << "Loading " << frameName << " : next-in-chain=" << chainNextName << endl;
314 // Check if we already loaded that frame (then we need to go 'before' it)
315 KWFrame* nextFrame = loadingInfo->frameByName( chainNextName );
316 if ( nextFrame ) {
317 fs = dynamic_cast<KWTextFrameSet *>( nextFrame->frameSet() );
318 chainNextName = TQString(); // already found, no need to store it
319 kdDebug(32001) << " found " << nextFrame << " -> frameset " << ( fs ? fs->name() : TQString() ) << endl;
320 }
321 }
322 KWFrame* prevFrame = loadingInfo->chainPrevFrame( frameName );
323 //kdDebug(32001) << "Loading " << frameName << " : chainPrevFrame=" << prevFrame << endl;
324 if ( prevFrame ) {
325 if ( fs ) // we are between prevFrame and nextFrame. They'd better be for the same fs!!
326 Q_ASSERT( fs == prevFrame->frameSet() );
327 fs = dynamic_cast<KWTextFrameSet *>( prevFrame->frameSet() );
328 //kdDebug(32001) << " found " << prevFrame << " -> frameset " << fs->name() << endl;
329 }
330 KWFrame* frame = 0;
331 if ( !fs ) {
332 fs = new KWTextFrameSet( m_doc, frameTag, context );
333 m_doc->addFrameSet( fs, false );
334 frame = fs->loadOasis( frameTag, tag, context );
335 } else { // Adding frame to existing frameset
336 context.styleStack().save();
337 context.fillStyleStack( frameTag, KoXmlNS::draw, "style-name", "graphic" ); // get the style for the graphics element
338 frame = fs->loadOasisTextFrame( frameTag, tag, context );
339 context.styleStack().restore();
340 }
341
342 loadingInfo->storeFrameName( frame, frameName );
343
344 if ( !chainNextName.isEmpty() ) {
345 loadingInfo->storeNextFrame( frame, chainNextName );
346 }
347 return frame;
348}
349
350KWTableFrameSet* KWOasisLoader::loadOasisTable( const TQDomElement& tag,
351 KoOasisContext& context )
352{
353 const TQString name = tag.attributeNS( KoXmlNS::table, "name", i18n( "Unnamed Table" ) ); // ### check for unicity?
354 KWTableFrameSet* table = new KWTableFrameSet( m_doc, name );
355 table->loadOasis( tag, context );
356 m_doc->addFrameSet(table, false);
357 return table;
358}
Info
The different types of textFramesets (that TEXT is important here!) FI_BODY = normal text frames.
Definition KWFrameSet.h:91
KWFrame * frame(unsigned int _num) const
get a frame by number
TQString name() const
get/set frameset name. For tables in particular, this must be unique
Definition KWFrameSet.h:428
void setFrameSetInfo(Info fi)
Set the type of TextFrameSet this is.
Definition KWFrameSet.h:97
This class represents a single frame.
Definition KWFrame.h:68
KWFrameSet * frameSet() const
Data stucture methods.
Definition KWFrame.h:143
void setMinimumFrameHeight(double h)
The property minimum frame height is used to make the automatic frame shrinking code stop.
Definition KWFrame.h:279
double topOfPage(int pageNumber) const
Return the y-offset in this document of the top of page with pageNumber Note that pageNumber is NOT a...
KWPage * appendPage()
Append a new page at the end of the document.
int lastPageNumber() const
return the highest page number we have in this document.
This class implements tables by acting as the manager for the frame(set)s which make up the table cel...
KWTextFrameSet * textFrameSet() const
Return the text frameset in which this document is.
KoTextObject * textObject() const
Return the contained text object.
KWFrame * loadOasisTextFrame(const TQDomElement &frameTag, const TQDomElement &tag, KoOasisContext &context)
Load a frame and add it to this frameset - called by KWOasisLoader.
KWFrame * loadOasis(const TQDomElement &frame, const TQDomElement &bodyElem, KoOasisContext &context)
Load a complete textbox (frame and text).
void loadOasisContent(const TQDomElement &bodyElem, KoOasisContext &context)
Load the contents of a frame (i.e. the text).