filters

kword13parser.cpp

00001 /* This file is part of the KDE project
00002    Copyright 2001, 2002, 2003, 2004 Nicolas GOUTTE <goutte@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <kdebug.h>
00021 
00022 #include "kword13formatone.h"
00023 #include "kword13formatother.h"
00024 #include "kword13layout.h"
00025 #include "kword13frameset.h"
00026 #include "kword13picture.h"
00027 #include "kword13document.h"
00028 #include "kword13parser.h"
00029 
00030 KWord13StackItem::KWord13StackItem() : elementType( KWord13TypeUnknown ), m_currentFrameset( 0 )
00031 {
00032 }
00033 
00034 KWord13StackItem::~KWord13StackItem()
00035 {
00036 }
00037 
00038 KWord13Parser::KWord13Parser( KWord13Document* kwordDocument ) 
00039     : m_kwordDocument(kwordDocument), m_currentParagraph( 0 ), 
00040     m_currentLayout( 0 ), m_currentFormat( 0 )
00041 {
00042     parserStack.setAutoDelete( true );
00043     KWord13StackItem* bottom = new KWord13StackItem;
00044     bottom->elementType = KWord13TypeBottom;
00045     parserStack.push( bottom ); //Security item (not to empty the stack)
00046 }
00047 
00048 KWord13Parser::~KWord13Parser( void )
00049 {
00050     parserStack.clear();
00051     delete m_currentParagraph;
00052     delete m_currentLayout;
00053     delete m_currentFormat;
00054 }
00055 
00056 bool KWord13Parser::startElementFormatOneProperty( const QString& name, const QXmlAttributes& attributes, KWord13StackItem *stackItem)
00057 {
00058     // ### TODO: check status
00059     if ( stackItem->elementType == KWord13TypeLayoutFormatOne )
00060     {
00061         if ( ! m_currentLayout )
00062         {
00063              kdError(30520) << "No current LAYOUT for storing FORMAT property: " << name << endl;
00064              return false;
00065         }
00066         for (int i = 0; i < attributes.count(); ++i )
00067         {
00068             QString attrName ( name );
00069             attrName += ':';
00070             attrName += attributes.qName( i );
00071             m_currentLayout->m_format.m_properties[ attrName ] = attributes.value( i );
00072             kdDebug(30520) << "Format Property (for LAYOUT): " << attrName << " = " << attributes.value( i ) << endl;
00073         }
00074         stackItem->elementType = KWord13TypeEmpty;
00075         return true;
00076     }
00077     else if ( stackItem->elementType == KWord13TypeFormat )
00078     {
00079         if ( ! m_currentFormat )
00080         {
00081              kdError(30520) << "No current FORMAT for storing FORMAT property: " << name << endl;
00082              return false;
00083         }
00084         KWord13FormatOneData* data = m_currentFormat->getFormatOneData();
00085         
00086         if ( ! data )
00087         {
00088              kdError(30520) << "Current FORMAT cannot store FORMAT text property: " << name << endl;
00089              return false;
00090         }
00091         
00092         for (int i = 0; i < attributes.count(); ++i )
00093         {
00094             QString attrName ( name );
00095             attrName += ':';
00096             attrName += attributes.qName( i );
00097             data->m_properties[ attrName ] = attributes.value( i );
00098             kdDebug(30520) << "Format Property (for FORMATS): " << attrName << " = " << attributes.value( i ) << endl;
00099         }
00100         stackItem->elementType = KWord13TypeEmpty;
00101         return true;
00102     }
00103     else if ( stackItem->elementType == KWord13TypeIgnore )
00104     {
00105         return true;
00106     }
00107     else
00108     {
00109         kdError(30520) << "Wrong parents for FORMAT property: " << name << endl;
00110         return false;
00111     }
00112 }
00113 
00114 bool KWord13Parser::startElementLayoutProperty( const QString& name, const QXmlAttributes& attributes, KWord13StackItem *stackItem)
00115 {
00116     // ### TODO: check status
00117     if ( stackItem->elementType == KWord13TypeIgnore )
00118     {
00119         return true;
00120     }
00121     else if ( m_currentLayout )
00122     {
00123         for (int i = 0; i < attributes.count(); ++i )
00124         {
00125             QString attrName ( name );
00126             attrName += ':';
00127             attrName += attributes.qName( i );
00128             m_currentLayout->m_layoutProperties[ attrName ] = attributes.value( i );
00129             kdDebug(30520) << "Layout Property: " << attrName << " = " << attributes.value( i ) << endl;
00130         }
00131         stackItem->elementType = KWord13TypeEmpty;
00132         return true;
00133     }
00134     else
00135     {
00136         kdError(30520) << "No current layout for storing property: " << name << endl;
00137         return false;
00138     }
00139 }
00140 
00141 bool KWord13Parser::startElementName( const QString&, const QXmlAttributes& attributes, KWord13StackItem *stackItem )
00142 {
00143     if ( stackItem->elementType != KWord13TypeLayout )
00144     {
00145         // We have something else than a LAYOU/STYLE, so ignore for now.
00146         stackItem->elementType = KWord13TypeIgnore;
00147         return true;
00148     }
00149     
00150     stackItem->elementType = KWord13TypeEmpty;
00151     
00152     if ( m_currentLayout )
00153     {
00154         m_currentLayout->m_name = attributes.value( "value" );
00155     }
00156     return  true;
00157 }
00158 
00159 bool KWord13Parser::startElementFormat( const QString&, const QXmlAttributes& attributes, KWord13StackItem *stackItem )
00160 {
00161     // ### TODO: check parent?
00162     if ( stackItem->elementType == KWord13TypeIgnore )
00163     {
00164         return true;
00165     }
00166     else if ( stackItem->elementType == KWord13TypeLayout )
00167     {
00168         stackItem->elementType = KWord13TypeLayoutFormatOne;
00169         return true; // Everything is done directly on the layout
00170     }
00171     else if ( stackItem->elementType != KWord13TypeFormatsPlural )
00172     {
00173         kdError(30520) << "<FORMAT> is child neither of <FORMATS> nor of <LAYOUT> nor of <STYLE>! Aborting!" << endl;
00174         return false; // Assume parsing error!
00175     }
00176         
00177     stackItem->elementType = KWord13TypeFormat;
00178     
00179     if ( m_currentFormat )
00180     {
00181         kdWarning(30520) << "Current format already defined!" << endl;
00182         delete m_currentFormat;
00183         m_currentFormat = 0;
00184     }
00185     
00186     bool ok = false;
00187     const int id = attributes.value( "id" ).toInt( &ok );
00188     
00189     if ( id == 1 && ok ) // Normal text
00190     {
00191         KWord13FormatOne* one = new KWord13FormatOne;
00192         const int len = attributes.value( "len" ).toInt( &ok );
00193         if ( ok )
00194             one->m_length = len;
00195         m_currentFormat = one;
00196     }
00197     else if ( id == 4 && ok ) // Variable
00198     {
00199         stackItem->elementType = KWord13TypeVariable;
00200         m_currentFormat = new KWord13FormatFour;
00201     }
00202     else if ( id == 6 && ok ) // Anchor
00203     {
00204         stackItem->elementType = KWord13TypeAnchor;
00205         m_currentFormat = new KWord13FormatSix;
00206     }
00207     else
00208     {
00209         // ### TODO: provisory
00210         stackItem->elementType = KWord13TypeIgnore;
00211         m_currentFormat = new KWord13Format;
00212         if ( ok )
00213             m_currentFormat->m_id = id;
00214     }
00215     const int pos = attributes.value( "pos" ).toInt( &ok );
00216     if ( ok )
00217     {
00218         m_currentFormat->m_pos = pos;
00219     }
00220     else
00221     {
00222         kdWarning(30520) << "Cannot set position of <FORMAT>: " << attributes.value( "pos" ) << endl;
00223         return false; // Assume parse error!
00224     }
00225     
00226     kdDebug(30520) << "<FORMAT id=\"" << id << "\" pos=\"" << pos << "\" len=\"" << attributes.value( "len" ) << "\">" << endl;
00227         
00228     return true;    
00229 }
00230 
00231 bool KWord13Parser::startElementLayout( const QString&, const QXmlAttributes& attributes, KWord13StackItem *stackItem )
00232 {
00233     // ### TODO: check parent?
00234     if ( stackItem->elementType == KWord13TypeIgnore )
00235     {
00236         return true;
00237     }
00238     
00239     stackItem->elementType = KWord13TypeLayout;
00240     
00241     if ( m_currentFormat )
00242     {
00243         kdWarning(30520) << "Current format defined! (Layout)" << endl;
00244         delete m_currentFormat;
00245         m_currentFormat = 0;
00246     }
00247     if ( m_currentLayout )
00248     {
00249         // Delete an eventually already existing paragraph (should not happen)
00250         kdWarning(30520) << "Current layout already defined!" << endl;
00251         delete m_currentLayout;
00252     }
00253         
00254     m_currentLayout = new KWord13Layout;
00255     m_currentLayout->m_outline = ( attributes.value( "outline" ) == "true" );
00256         
00257     return true;    
00258 }
00259 
00260 bool KWord13Parser::startElementParagraph( const QString&, const QXmlAttributes&, KWord13StackItem *stackItem )
00261 {
00262     if ( stackItem->elementType == KWord13TypeUnknownFrameset )
00263     {
00264         stackItem->elementType = KWord13TypeIgnore;
00265         return true;
00266     }
00267 
00268     stackItem->elementType = KWord13TypeParagraph;
00269     
00270     if ( m_currentParagraph )
00271     {
00272         // Delete an eventually already existing paragraph (should not happen)
00273         kdWarning(30520) << "Current paragraph already defined!" << endl;
00274         delete m_currentParagraph;
00275     }
00276         
00277     m_currentParagraph = new KWord13Paragraph;
00278     
00279     return true;
00280 }
00281 
00282 bool KWord13Parser::startElementFrame( const QString& name, const QXmlAttributes& attributes, KWord13StackItem *stackItem )
00283 {
00284     if ( stackItem->elementType == KWord13TypeFrameset || stackItem->elementType == KWord13TypePictureFrameset )
00285     {
00286         stackItem->elementType = KWord13TypeEmpty;
00287         if ( stackItem->m_currentFrameset )
00288         {
00289             const int num = ++stackItem->m_currentFrameset->m_numFrames;
00290             for (int i = 0; i < attributes.count(); ++i )
00291             {
00292                 QString attrName ( name );
00293                 attrName += ':';
00294                 attrName += QString::number( num );
00295                 attrName += ':';
00296                 attrName += attributes.qName( i );
00297                 stackItem->m_currentFrameset->m_frameData[ attrName ] = attributes.value( i );
00298                 kdDebug(30520) << "FrameData: " << attrName << " = " << attributes.value( i ) << endl;
00299             }
00300             
00301         }
00302         else
00303         {
00304             kdError(30520) << "Data of <FRAMESET> not found" << endl;
00305             return false;
00306         }
00307     }
00308     else if ( stackItem->elementType != KWord13TypeUnknownFrameset )
00309     {
00310         kdError(30520) << "<FRAME> not child of <FRAMESET>" << endl;
00311         return false;
00312     }
00313     return true;
00314 }
00315 
00316 bool KWord13Parser::startElementFrameset( const QString& name, const QXmlAttributes& attributes, KWord13StackItem *stackItem )
00317 {
00318     const QString frameTypeStr( attributes.value( "frameType" ) );
00319     const QString frameInfoStr( attributes.value( "frameInfo" ) );
00320     
00321     if ( frameTypeStr.isEmpty() || frameInfoStr.isEmpty() )
00322     {
00323         kdError(30520) << "<FRAMESET> without frameType or frameInfo attribute!" << endl;
00324         return false;
00325     }
00326     
00327     const int frameType = frameTypeStr.toInt();
00328     const int frameInfo = frameInfoStr.toInt();
00329     
00330     if ( frameType == 1 )
00331     {
00332         stackItem->elementType = KWord13TypeFrameset;
00333         KWordTextFrameset* frameset = new KWordTextFrameset( frameType, frameInfo, attributes.value( "name" ) );
00334         
00335         // Normal text frame (in or outside a table)
00336         if ( ( !frameInfo ) && attributes.value( "grpMgr" ).isEmpty() )
00337         {
00338             m_kwordDocument->m_normalTextFramesetList.append( frameset );
00339             stackItem->m_currentFrameset = m_kwordDocument->m_normalTextFramesetList.current();
00340         }
00341         else if ( !frameInfo )
00342         {
00343             // We just store the frameset in the frameset table list
00344             // Grouping the framesets by table will be done after the parsing, not now.
00345             m_kwordDocument->m_tableFramesetList.append( frameset );
00346             stackItem->m_currentFrameset = m_kwordDocument->m_tableFramesetList.current();
00347         }
00348         else if ( frameInfo >= 1 && frameInfo <= 6 )
00349         {
00350             m_kwordDocument->m_headerFooterFramesetList.append( frameset );
00351             stackItem->m_currentFrameset = m_kwordDocument->m_headerFooterFramesetList.current();
00352         }
00353         else if ( frameInfo == 7 )
00354         {
00355             m_kwordDocument->m_footEndNoteFramesetList.append( frameset );
00356             stackItem->m_currentFrameset = m_kwordDocument->m_footEndNoteFramesetList.current();
00357         }
00358         else
00359         {
00360             kdError(30520) << "Unknown text frameset!" << endl;
00361             m_kwordDocument->m_otherFramesetList.append( frameset );
00362             stackItem->m_currentFrameset = m_kwordDocument->m_otherFramesetList.current();
00363         }
00364     }
00365     else if ( ( frameType == 2 ) // picture or image
00366         || ( frameType == 5 ) ) // ciipart
00367     {
00368         if ( !frameInfo )
00369         {
00370             kdWarning(30520) << "Unknown FrameInfo for pictures: " << frameInfo << endl;
00371         }
00372         stackItem->elementType = KWord13TypePictureFrameset;
00373         KWord13PictureFrameset* frameset = new KWord13PictureFrameset( frameType, frameInfo, attributes.value( "name" ) );
00374         m_kwordDocument->m_otherFramesetList.append( frameset );
00375         stackItem->m_currentFrameset = m_kwordDocument->m_otherFramesetList.current();
00376     }
00377     // ### frameType == 6 : horizontal line (however KWord did not save it correctly)
00378     // ### frameType == 4 : formula
00379     // ### frametype == 3 : embedded (but only in <SETTINGS>)
00380     else
00381     {
00382         // Frame of unknown/unsupported type
00383         kdWarning(30520) << "Unknown/unsupported <FRAMESET> type! Type: " << frameTypeStr << " Info: " << frameInfoStr << endl;
00384         stackItem->elementType = KWord13TypeUnknownFrameset;
00385         KWord13Frameset* frameset = new KWord13Frameset( frameType, frameInfo, attributes.value( "name" ) );
00386         m_kwordDocument->m_otherFramesetList.append( frameset );
00387         stackItem->m_currentFrameset = m_kwordDocument->m_otherFramesetList.current();
00388     }
00389     return true;
00390 }
00391 
00392 
00393 bool KWord13Parser::startElementDocumentAttributes( const QString& name, const QXmlAttributes& attributes, KWord13StackItem *stackItem,
00394      const KWord13StackItemType& allowedParentType, const KWord13StackItemType& newType )
00395 {
00396     if ( parserStack.current()->elementType == allowedParentType )
00397     {
00398         stackItem->elementType = newType;
00399         for (int i = 0; i < attributes.count(); ++i )
00400         {
00401             QString attrName ( name );
00402             attrName += ':';
00403             attrName += attributes.qName( i );
00404             m_kwordDocument->m_documentProperties[ attrName ] = attributes.value( i );
00405             kdDebug(30520) << "DocAttr: " <<  attrName << " = " << attributes.value( i ) << endl;
00406         }
00407         return true;
00408     }
00409     else
00410     {
00411         kdError(30520) << "Wrong parent!" << endl;
00412         return false;
00413     }
00414 }
00415 
00416 bool KWord13Parser::startElementKey( const QString& name, const QXmlAttributes& attributes, KWord13StackItem *stackItem )
00417 {
00418     const QString key( calculatePictureKey( attributes.value( "filename" ),
00419             attributes.value( "year" ), attributes.value( "month" ),  attributes.value( "day" ),
00420             attributes.value( "hour" ), attributes.value( "minute" ), attributes.value( "second" ),
00421             attributes.value( "msec" ) ) );
00422     kdDebug(30520) << "Picture key: " << key << endl;
00423             
00424     if ( stackItem->elementType == KWord13TypePicturesPlural )
00425     {
00426         KWord13Picture* pic = new KWord13Picture;
00427         pic->m_storeName = attributes.value( "name" );
00428         if ( pic->m_storeName.isEmpty() )
00429         {
00430             kdError(30520) << "Picture defined without store name! Aborting!" << endl;
00431             return false; // Assume parse error
00432         }
00433         // ### TODO: catch duplicate keys (should not happen but who knows?)
00434         m_kwordDocument->m_pictureDict.insert( key, pic );
00435     }
00436     else if ( stackItem->elementType == KWord13TypePicture )
00437     {
00438         // ### TODO: error messages?
00439         if ( stackItem->m_currentFrameset )
00440         {
00441             stackItem->m_currentFrameset->setKey( key );
00442         }
00443     }
00444     else
00445     {
00446         // Neither child of <PICTURES>, <PIXMAPS>, <CLIPARTS>
00447         // nor of <PICTURE>, <IMAGE>, <CLIPART>
00448         // ### TODO: parse error?
00449     }
00450     return true;
00451 }
00452 
00453 bool KWord13Parser::startElementAnchor( const QString& name, const QXmlAttributes& attributes, KWord13StackItem *stackItem )
00454 {
00455     if ( stackItem->elementType == KWord13TypeAnchor )
00456     {
00457         const QString anchorType ( attributes.value( "type" ) );
00458     if ( anchorType == "grpMgr" )
00459         kdWarning(30520) << "Anchor of type grpMgr! Not tested!" << endl; // ### TODO
00460         else if ( anchorType != "frameset" )
00461     {
00462         kdError(30520) << "Unsupported anchor type: " << anchorType << endl;
00463         return false;
00464     } 
00465     const QString frameset ( attributes.value( "instance" ) );
00466     if ( frameset.isEmpty() )
00467     {
00468         kdError(30520) << "Anchor to an empty frameset name! Aborting!" << endl;
00469         return false;
00470     }
00471     if ( m_currentFormat )
00472     {
00473         KWord13FormatSix* six = (KWord13FormatSix*) m_currentFormat;
00474         six->m_anchorName = frameset;
00475     }
00476     // add frameset name to the list of anchored framesets
00477     if ( m_kwordDocument->m_anchoredFramesetNames.find( frameset ) == m_kwordDocument->m_anchoredFramesetNames.end() )
00478     {
00479         m_kwordDocument->m_anchoredFramesetNames.append( frameset );
00480     }
00481     }
00482     else
00483     {
00484         kdError(30520) << "Anchor not child of <FORMAT id=\"6\"> Aborting!" << endl;
00485     return false;
00486     }
00487     return true;
00488 }
00489 
00490 
00491 bool KWord13Parser::startElement( const QString&, const QString&, const QString& name, const QXmlAttributes& attributes )
00492 {
00493     kdDebug(30520) << indent << "<" << name << ">" << endl; // DEBUG
00494     indent += "*"; //DEBUG
00495     if (parserStack.isEmpty())
00496     {
00497         kdError(30520) << "Stack is empty!! Aborting! (in KWordParser::startElement)" << endl;
00498         return false;
00499     }
00500     
00501     // Create a new stack element copying the top of the stack.
00502     KWord13StackItem *stackItem=new KWord13StackItem(*parserStack.current());
00503 
00504     if (!stackItem)
00505     {
00506         kdError(30520) << "Could not create Stack Item! Aborting! (in StructureParser::startElement)" << endl;
00507         return false;
00508     }
00509 
00510     stackItem->itemName=name;
00511 
00512     bool success=false;
00513 
00514     // Order of element names: probability in a document
00515     if ( name == "COLOR" || name == "FONT" || name =="SIZE" 
00516         || name == "WEIGHT" || name == "ITALIC" || name == "UNDERLINE" 
00517         || name == "STRIKEOUT" || name == "VERTALIGN" || name == "SHADOW"
00518         || name == "FONTATTRIBUTE" || name == "LANGUAGE"
00519         || name == "TEXTBACKGROUNDCOLOR" || name == "OFFSETFROMBASELINE" )
00520     {
00521         success = startElementFormatOneProperty( name, attributes, stackItem );
00522     }
00523     else if ( name == "FLOW" || name == "INDENTS" || name == "OFFSETS"
00524         || name == "LINESPACING" || name == "PAGEBREAKING" 
00525         || name == "LEFTBORDER" || name == "RIGHTBORDER" || name == "FOLLOWING"
00526         || name == "TOPBORDER" || name == "BOTTOMBORDER" || name == "COUNTER" )
00527     {
00528         success = startElementLayoutProperty( name, attributes, stackItem );
00529     }
00530     else if ( name == "TEXT" )
00531     {
00532         if ( stackItem->elementType == KWord13TypeParagraph && m_currentParagraph )
00533         {
00534             stackItem->elementType = KWord13TypeText;
00535             m_currentParagraph->setText( QString::null );
00536         }
00537         else
00538         {
00539             stackItem->elementType = KWord13TypeIgnore;
00540         }
00541         success = true;
00542     }
00543     else if ( name == "NAME" )
00544     {
00545         success = startElementName( name, attributes, stackItem );
00546     }
00547     else if ( name == "FORMATS" )
00548     {
00549         if ( stackItem->elementType == KWord13TypeParagraph && m_currentParagraph )
00550         {
00551             stackItem->elementType = KWord13TypeFormatsPlural;
00552         }
00553         else
00554         {
00555             stackItem->elementType = KWord13TypeIgnore;
00556         }
00557         success = true;
00558     }
00559     else if ( name == "PARAGRAPH" )
00560     {
00561         success = startElementParagraph( name, attributes, stackItem );
00562     }
00563     else if ( name == "FORMAT" )
00564     {
00565         success = startElementFormat( name, attributes, stackItem );
00566     }
00567     else if (name == "LAYOUT" )
00568     {
00569         success = startElementLayout( name, attributes, stackItem );
00570     }
00571     else if ( name == "TYPE" )
00572     {
00573         // ### TEMPORARY
00574         if ( m_currentFormat && ( stackItem->elementType == KWord13TypeVariable ) )
00575         {
00576             ( (KWord13FormatFour*) m_currentFormat ) -> m_text =  attributes.value( "text" );
00577         }
00578         success = true;
00579     }
00580     else if ( name == "KEY" )
00581     {
00582         success = startElementKey( name, attributes, stackItem );
00583     }
00584     else if ( name == "ANCHOR" )
00585     {
00586         success = startElementAnchor( name, attributes, stackItem );
00587     }
00588     else if ( name == "PICTURE" || name == "IMAGE" || name == "CLIPART" )
00589     {
00590         // ### TODO: keepAspectRatio (but how to transform it to OASIS)
00591         if ( stackItem->elementType == KWord13TypePictureFrameset )
00592         {
00593             stackItem->elementType = KWord13TypePicture;
00594         }
00595         success = true;
00596     }
00597     else if ( name == "FRAME" )
00598     {
00599         success = startElementFrame( name, attributes, stackItem );
00600     }
00601     else if ( name == "FRAMESET" )
00602     {
00603         success = startElementFrameset( name, attributes, stackItem );
00604     }
00605     else if (name == "STYLE" )
00606     {
00607         success = startElementLayout( name, attributes, stackItem );
00608     }
00609     else if ( name == "DOC" )
00610     {
00611         success = startElementDocumentAttributes( name, attributes, stackItem, KWord13TypeBottom, KWord13TypeDocument );
00612     }
00613     else if  ( name == "PAPER") 
00614     {
00615         success = startElementDocumentAttributes( name, attributes, stackItem, KWord13TypeDocument, KWord13TypePaper );
00616     }
00617     else if ( name == "PAPERBORDERS" )
00618     {
00619         success = startElementDocumentAttributes( name, attributes, stackItem, KWord13TypePaper, KWord13TypeEmpty );
00620     }
00621     else if ( ( name == "ATTRIBUTES" ) || ( name == "VARIABLESETTINGS" )
00622          || ( name == "FOOTNOTESETTINGS" ) || ( name == "ENDNOTESETTINGS" ) )
00623     {
00624         success = startElementDocumentAttributes( name, attributes, stackItem, KWord13TypeDocument, KWord13TypeEmpty );
00625     }
00626     else if ( name == "FRAMESTYLE" )
00627     {
00628         // ### TODO, but some of the <STYLE> children are also children of <FRAMESTYLE>, so we have to set it to "ignore"
00629         stackItem->elementType = KWord13TypeIgnore;
00630         success = true;
00631     }
00632     else if ( name == "PICTURES" || name == "PIXMAPS" || name == "CLIPARTS" )
00633     {
00634         // We just need a separate "type" for the <KEY> children
00635         stackItem->elementType = KWord13TypePicturesPlural;
00636         success = true;
00637     }
00638     else
00639     {
00640         stackItem->elementType = KWord13TypeUnknown;
00641         success = true;
00642     }
00643 
00644     if ( success )
00645     {
00646         parserStack.push( stackItem );
00647     }
00648     else
00649     {   // We have a problem so destroy our resources.
00650         delete stackItem;
00651     }
00652     
00653     return success;
00654 }
00655 
00656 bool KWord13Parser :: endElement( const QString&, const QString& , const QString& name)
00657 {
00658     indent.remove( 0, 1 ); // DEBUG
00659     //kdDebug(30520) << indent << "</" << name << ">" << endl; // DEBUG
00660     if (parserStack.isEmpty())
00661     {
00662         kdError(30520) << "Stack is empty!! Aborting! (in StructureParser::endElement)" << endl;
00663         return false;
00664     }
00665 
00666     bool success=false;
00667     
00668     KWord13StackItem *stackItem=parserStack.pop();
00669         
00670     if ( name == "PARAGRAPH" )
00671     {
00672         if ( stackItem->m_currentFrameset && m_currentParagraph )
00673         {
00674             if ( stackItem->m_currentFrameset->addParagraph( *m_currentParagraph ) )
00675             {
00676                 success = true;
00677             }
00678             // ### HACK: do not delete the data of <FORMATS>
00679             m_currentParagraph->m_formats.setAutoDelete( false );
00680         }
00681         else if ( stackItem->elementType == KWord13TypeIgnore )
00682         {
00683             success = true;
00684         }
00685         delete m_currentParagraph;
00686         m_currentParagraph = 0;
00687     }
00688     else if ( name == "FORMAT" )
00689     {
00690         if ( stackItem->elementType == KWord13TypeFormat )
00691         {
00692             if ( m_currentParagraph )
00693             {
00694                 m_currentParagraph->m_formats.append( m_currentFormat );
00695                 kdDebug(30520) << "Adding to <FORMATS>: " << ((void*) m_currentFormat) << endl;
00696                 m_currentFormat = 0;
00697             }
00698             else
00699             {
00700                 kdError(30520) << "No paragraph to store <FORMAT>! Aborting!" << endl;
00701                 delete m_currentFormat;
00702                 m_currentFormat = 0;
00703                 return false; // Assume parsing error!
00704             }
00705 
00706         }
00707         else if ( stackItem->elementType == KWord13TypeLayoutFormatOne )
00708         {
00709             // Nothing to do!
00710         }
00711         success = true;
00712     }
00713     else if ( name == "LAYOUT" )
00714     {
00715         if ( m_currentLayout && m_currentParagraph )
00716         {
00717             m_currentParagraph->m_layout = *m_currentLayout;
00718         }
00719         delete m_currentLayout;
00720         m_currentLayout = 0;
00721         success = true;
00722     }
00723     else if ( name == "STYLE" )
00724     {
00725         if ( m_kwordDocument && m_currentLayout )
00726         {
00727             if ( m_currentLayout->m_name.isEmpty() )
00728             {
00729                 // ### TODO: what should be really done with anonymous styles (should not happen but it would have consequences)
00730                 kdError(30520) << "Anonymous style found! Aborting" << endl;
00731                 return false; // Assume a parsing error!
00732             }
00733             m_kwordDocument->m_styles.append( *m_currentLayout );
00734             success = true;
00735         }
00736         delete m_currentLayout;
00737         m_currentLayout = 0;
00738     }
00739     else if ( name == "DOC" )
00740     {
00741         success = true;
00742     }
00743     else
00744     {
00745         success = true; // No problem, so authorisation to continue parsing
00746     }
00747     
00748     if (!success)
00749     {
00750         // If we have no success, then it was surely a tag mismatch. Help debugging!
00751         kdError(30506) << "Found closing tag name: " << name << " expected: " << stackItem->itemName << endl;
00752     }
00753     
00754     delete stackItem;
00755     
00756     return success;
00757 }
00758 
00759 bool KWord13Parser :: characters ( const QString & ch )
00760 {
00761 #if 0
00762     // DEBUG start
00763     if (ch=="\n")
00764     {
00765         kdDebug(30520) << indent << " (LINEFEED)" << endl;
00766     }
00767     else if (ch.length()> 40)
00768     {   // 40 characters are enough (especially for image data)
00769         kdDebug(30520) << indent << " :" << ch.left(40) << "..." << endl;
00770     }
00771     else
00772     {
00773         kdDebug(30520) << indent << " :" << ch << ":" << endl;
00774     }
00775     // DEBUG end
00776 #endif
00777 
00778     if (parserStack.isEmpty())
00779     {
00780         kdError(30520) << "Stack is empty!! Aborting! (in StructureParser::characters)" << endl;
00781         return false;
00782     }
00783 
00784     bool success=false;
00785 
00786     KWord13StackItem *stackItem = parserStack.current();
00787 
00788     if ( stackItem->elementType == KWord13TypeText )
00789     { 
00790         // <TEXT>
00791         if ( m_currentParagraph )
00792         {
00793             bool found = false; // Some unexpected control character?
00794             // ### TODO: this is perhaps a good point to check for non-XML characters
00795             const uint length = ch.length();
00796             for ( uint i = 0; i < length; ++i )
00797             {
00798                 const ushort uni = ch.at(i).unicode();
00799                 if ( uni >= 32 )
00800                     continue; // Normal character
00801                 else if ( uni == 9 || uni == 10 || uni == 13)
00802                     continue; // Tabulator, Line Feed, Carriage Return
00803                 else if ( uni == 1 )
00804                 {
00805                     // Old KWord documents have a QChar(1) as anchor character
00806                     // So replace it with the anchor character of recent KWord versions
00807                     ch[i]='#';
00808                 }
00809                 else
00810                 {
00811                     ch[i]='?';
00812                     found = true;
00813                 }
00814             }
00815             if ( found )
00816                 kdWarning(30520) << "Unexcepted control characters found in text!" << endl;
00817             m_currentParagraph->appendText( ch );
00818             success = true;
00819         }
00820         else
00821         {
00822             kdError(30520) << "No current paragraph defined! Tag mismatch?" << endl;
00823             success = false;
00824         }
00825     }
00826     else if (stackItem->elementType==KWord13TypeEmpty)
00827     {
00828         success=ch.stripWhiteSpace().isEmpty();
00829         if (!success)
00830         {
00831             // We have a parsing error, so abort!
00832             kdError(30520) << "Empty element "<< stackItem->itemName <<" is not empty! Aborting! (in KWordParser::characters)" << endl;
00833         }
00834     }
00835     else
00836     {
00837         success=true;
00838     }
00839 
00840     return success;
00841 }
00842 
00843 bool KWord13Parser::warning(const QXmlParseException& exception)
00844 {
00845     kdWarning(30520) << "XML parsing warning: line " << exception.lineNumber()
00846         << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
00847     return true;
00848 }
00849 
00850 bool KWord13Parser::error(const QXmlParseException& exception)
00851 {
00852     // A XML error is recoverable, so it is only a KDE warning
00853     kdWarning(30520) << "XML parsing error: line " << exception.lineNumber()
00854         << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
00855     return true;
00856 }
00857 
00858 bool KWord13Parser::fatalError (const QXmlParseException& exception)
00859 {
00860     kdError(30520) << "XML parsing fatal error: line " << exception.lineNumber()
00861         << " col " << exception.columnNumber() << " message: " << exception.message() << endl;
00862     // ### TODO: user message box
00863     return false; // Stop parsing now, we do not need further errors.
00864 }
00865 
00866 QString KWord13Parser::calculatePictureKey( const QString& filename,
00867      const QString& year,  const QString& month,  const QString& day,
00868      const QString& hour,  const QString& minute,  const QString& second,
00869      const QString& microsecond ) const
00870 {
00871     bool ok;
00872     bool globalOk = true;
00873     
00874     ok = false;
00875     const int iYear = year.toInt( & ok );
00876     globalOk = globalOk && ok; 
00877 
00878     ok = false;
00879     const int iMonth = month.toInt( & ok );
00880     globalOk = globalOk && ok; 
00881 
00882     ok = false;
00883     const int iDay = day.toInt( & ok );
00884     globalOk = globalOk && ok; 
00885 
00886     ok = false;
00887     const int iHour = hour.toInt( & ok );
00888     globalOk = globalOk && ok; 
00889 
00890     ok = false;
00891     const int iMinute = minute.toInt( & ok );
00892     globalOk = globalOk && ok; 
00893 
00894     ok = false;
00895     const int iSecond = second.toInt( & ok );
00896     globalOk = globalOk && ok; 
00897 
00898     ok = false;
00899     const int iMicrosecond = microsecond.toInt( & ok );
00900     globalOk = globalOk && ok; 
00901     
00902     if ( globalOk )
00903     {
00904         // No error until then, so check if the date/time is valid at all
00905         globalOk = globalOk && QDate::isValid( iYear, iMonth, iDay );
00906         globalOk = globalOk && QTime::isValid( iHour, iMinute, iSecond, iMicrosecond ); 
00907     }
00908 
00909     QDateTime dt;
00910     if ( globalOk )
00911     {
00912         // The date/time seems correct
00913         dt = QDateTime( QDate( iYear, iMonth, iDay ), QTime( iHour, iMinute, iSecond, iMicrosecond ) );
00914     }
00915     else
00916     {
00917         // *NIX epoch (We do not really care if it is UTC or local time)
00918         dt = QDateTime( QDate( 1970, 1, 1 ), QTime( 0, 0, 0, 0 ) );
00919     }
00920     
00921     // We put the date/time first, as if the date is useful, it will have faster a difference than a path
00922     // where the common pth might be very long.
00923     
00924     // Output the date/time as compact as possible
00925     QString result ( dt.toString( "yyyyMMddhhmmsszzz" ) );
00926     result += '@'; // A separator
00927     result += filename;
00928     return result;
00929 }
KDE Home | KDE Accessibility Home | Description of Access Keys