/* This file is part of the KDE project
Copyright ( C ) 2002 Laurent Montel < lmontel @ mandrakesoft . com >
Copyright ( C ) 2003 David Faure < faure @ kde . org >
Copyright ( C ) 2002 , 2003 , 2004 Nicolas GOUTTE < goutte @ kde . org >
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation ; either
version 2 of the License , or ( at your option ) any later version .
This library is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
Library General Public License for more details .
You should have received a copy of the GNU Library General Public License
along with this library ; see the file COPYING . LIB . If not , write to
the Free Software Foundation , Inc . , 51 Franklin Street , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*/
# include <tqcolor.h>
# include <tqfile.h>
# include <tqfont.h>
# include <tqpen.h>
# include <tqregexp.h>
# include <tqimage.h>
# include "oowriterimport.h"
# include <ooutils.h>
# include <kdeversion.h>
# include <kdebug.h>
# include <kzip.h>
# include <KoDocumentInfo.h>
# include <KoDocument.h>
# include <kgenericfactory.h>
# include <kmessagebox.h>
# include <KoFilterChain.h>
# include <KoUnit.h>
# include <KoPageLayout.h>
# include <KoPicture.h>
# include "conversion.h"
# include <KoRect.h>
# include <KoDom.h>
# if ! KDE_IS_VERSION(3,1,90)
# include <kdebugclasses.h>
# endif
typedef KGenericFactory < OoWriterImport , KoFilter > OoWriterImportFactory ;
K_EXPORT_COMPONENT_FACTORY ( liboowriterimport , OoWriterImportFactory ( " kofficefilters " ) )
OoWriterImport : : OoWriterImport ( KoFilter * , const char * , const TQStringList & )
: KoFilter ( ) ,
m_styleStack ( ooNS : : style , ooNS : : fo ) ,
m_insideOrderedList ( false ) , m_nextItemIsListItem ( false ) ,
m_hasTOC ( false ) , m_hasHeader ( false ) , m_hasFooter ( false ) , m_restartNumbering ( - 1 ) ,
m_pictureNumber ( 0 ) , m_zip ( NULL )
{
m_styles . setAutoDelete ( true ) ;
m_masterPages . setAutoDelete ( true ) ;
m_listStyles . setAutoDelete ( true ) ;
}
OoWriterImport : : ~ OoWriterImport ( )
{
}
KoFilter : : ConversiontqStatus OoWriterImport : : convert ( TQCString const & from , TQCString const & to )
{
kdDebug ( 30518 ) < < " Entering Oowriter Import filter: " < < from < < " - " < < to < < endl ;
if ( ( from ! = " application/vnd.sun.xml.writer "
& & from ! = " application/vnd.sun.xml.writer.template "
& & from ! = " application/vnd.sun.xml.writer.master " )
| | to ! = " application/x-kword " )
{
kdWarning ( 30518 ) < < " Invalid mimetypes " < < from < < " " < < to < < endl ;
return KoFilter : : NotImplemented ;
}
m_zip = new KZip ( m_chain - > inputFile ( ) ) ;
kdDebug ( 30518 ) < < " Store created " < < endl ;
if ( ! m_zip - > open ( IO_ReadOnly ) )
{
kdError ( 30518 ) < < " Couldn't open the requested file " < < m_chain - > inputFile ( ) < < endl ;
return KoFilter : : FileNotFound ;
}
if ( ! m_zip - > directory ( ) )
{
kdError ( 30518 ) < < " Couldn't read ZIP directory of the requested file " < < m_chain - > inputFile ( ) < < endl ;
return KoFilter : : FileNotFound ;
}
KoFilter : : ConversiontqStatus pretqStatus = openFile ( ) ;
TQImage thumbnail ;
if ( pretqStatus = = KoFilter : : OK )
{
// We do not care about the failure
OoUtils : : loadThumbnail ( thumbnail , m_zip ) ;
}
if ( pretqStatus ! = KoFilter : : OK )
{
m_zip - > close ( ) ;
delete m_zip ;
return pretqStatus ;
}
m_currentMasterPage = TQString ( ) ;
TQDomDocument mainDocument ;
TQDomElement framesetsElem ;
prepareDocument ( mainDocument , framesetsElem ) ;
// Load styles from style.xml
if ( ! createStyleMap ( m_stylesDoc , mainDocument ) )
return KoFilter : : UserCancelled ;
// Also load styles from content.xml
if ( ! createStyleMap ( m_content , mainDocument ) )
return KoFilter : : UserCancelled ;
// Create main frameset
TQDomElement mainFramesetElement = mainDocument . createElement ( " FRAMESET " ) ;
mainFramesetElement . setAttribute ( " frameType " , 1 ) ;
mainFramesetElement . setAttribute ( " frameInfo " , 0 ) ;
mainFramesetElement . setAttribute ( " visible " , 1 ) ;
mainFramesetElement . setAttribute ( " name " , i18n ( " Main Text Frameset " ) ) ;
framesetsElem . appendChild ( mainFramesetElement ) ;
createInitialFrame ( mainFramesetElement , 29 , 798 , 42 , 566 , false , Reconnect ) ;
createStyles ( mainDocument ) ;
createDocumentContent ( mainDocument , mainFramesetElement ) ;
finishDocumentContent ( mainDocument ) ;
m_zip - > close ( ) ;
delete m_zip ; // It has to be so late, as pictures might be read.
KoStoreDevice * out = m_chain - > storageFile ( " maindoc.xml " , KoStore : : Write ) ;
if ( ! out ) {
kdError ( 30518 ) < < " Unable to open output file! " < < endl ;
return KoFilter : : StorageCreationError ;
}
else
{
TQCString cstr = mainDocument . toCString ( ) ;
kdDebug ( 30518 ) < < " maindoc: " < < cstr < < endl ;
// WARNING: we cannot use KoStore::write(const TQByteArray&) because it gives an extra NULL character at the end.
out - > writeBlock ( cstr , cstr . length ( ) ) ;
}
TQDomDocument docinfo ;
createDocumentInfo ( docinfo ) ;
// store document info
out = m_chain - > storageFile ( " documentinfo.xml " , KoStore : : Write ) ;
if ( out )
{
TQCString info = docinfo . toCString ( ) ;
kdDebug ( 30518 ) < < " info : " < < info < < endl ;
// WARNING: we cannot use KoStore::write(const TQByteArray&) because it gives an extra NULL character at the end.
out - > writeBlock ( info , info . length ( ) ) ;
}
// store preview
if ( ! thumbnail . isNull ( ) )
{
// ### TODO: thumbnail.setAlphaBuffer( false ); // legacy KOffice previews have no alpha channel
// Legacy KOffice previews are 256x256x8 instead of 128x128x32
TQImage preview ( thumbnail . smoothScale ( 256 , 256 ) . convertDepth ( 8 , Qt : : AvoidDither | Qt : : DiffuseDither ) ) ;
// Not to be able to generate a preview is not an error
if ( ! preview . isNull ( ) )
{
out = m_chain - > storageFile ( " preview.png " , KoStore : : Write ) ;
if ( out )
{
preview . save ( out , " PNG " ) ;
}
}
}
kdDebug ( 30518 ) < < " ######################## OoWriterImport::convert done #################### " < < endl ;
return KoFilter : : OK ;
}
void OoWriterImport : : createStyles ( TQDomDocument & doc )
{
TQDomElement stylesElem = doc . createElement ( " STYLES " ) ;
doc . documentElement ( ) . appendChild ( stylesElem ) ;
TQDomNode fixedStyles = KoDom : : namedItemNS ( m_stylesDoc . documentElement ( ) , ooNS : : office , " styles " ) ;
Q_ASSERT ( ! fixedStyles . isNull ( ) ) ;
TQDomElement e ;
forEachElement ( e , fixedStyles )
{
if ( ! e . hasAttributeNS ( ooNS : : style , " name " ) )
continue ;
// We only generate paragraph styles for now
if ( e . attributeNS ( ooNS : : style , " family " , TQString ( ) ) ! = " paragraph " )
continue ;
// We use the style stack, to flatten out tqparent styles
// Once KWord supports style inheritance, replace this with a single m_styleStack.push.
// (We still need to use StyleStack, since that's what writeLayout/writeFormat read from)
addStyles ( & e ) ;
TQDomElement styleElem = doc . createElement ( " STYLE " ) ;
stylesElem . appendChild ( styleElem ) ;
TQString styleName = kWordStyleName ( e . attributeNS ( ooNS : : style , " name " , TQString ( ) ) ) ;
TQDomElement element = doc . createElement ( " NAME " ) ;
element . setAttribute ( " value " , styleName ) ;
styleElem . appendChild ( element ) ;
//kdDebug(30518) << k_funcinfo << "generating style " << styleName << endl;
TQString followingStyle = m_styleStack . attributeNS ( ooNS : : style , " next-style-name " ) ;
if ( ! followingStyle . isEmpty ( ) )
{
TQDomElement element = doc . createElement ( " FOLLOWING " ) ;
element . setAttribute ( " name " , kWordStyleName ( followingStyle ) ) ;
styleElem . appendChild ( element ) ;
}
// ### In KWord the style says "I'm part of the outline" (TOC)
// ### In OOo the paragraph says that (text:h)
// Hence this hack...
// OASIS solution for this: style:default-outline-level attribute
bool outline = styleName . startsWith ( " Heading " ) ;
if ( outline )
styleElem . setAttribute ( " outline " , " true " ) ;
writeFormat ( doc , styleElem , 1 , 0 , 0 ) ;
writeLayout ( doc , styleElem ) ;
// writeLayout doesn't load the counter. It's modelled differently for parags and for styles.
// ### missing info in the format! (fixed in OASIS)
const int level = styleName . right ( 1 ) . toInt ( ) ; // ## HACK
bool listOK = false ;
if ( level > 0 ) {
if ( outline )
listOK = pushListLevelStyle ( " <outline-style> " , m_outlineStyle , level ) ;
else {
const TQString listStyleName = e . attributeNS ( ooNS : : style , " list-style-name " , TQString ( ) ) ;
listOK = ! listStyleName . isEmpty ( ) ;
if ( listOK )
listOK = pushListLevelStyle ( listStyleName , level ) ;
}
}
if ( listOK ) {
const TQDomElement listStyle = m_listStyleStack . currentListStyle ( ) ;
// The tag is either text:list-level-style-number or text:list-level-style-bullet
bool ordered = listStyle . localName ( ) = = " list-level-style-number " ;
writeCounter ( doc , styleElem , outline , level , ordered ) ;
m_listStyleStack . pop ( ) ;
}
m_styleStack . clear ( ) ;
}
}
void OoWriterImport : : parseBodyOrSimilar ( TQDomDocument & doc , const TQDomElement & tqparent , TQDomElement & currentFramesetElement )
{
TQDomElement oldCurrentFrameset = m_currentFrameset ;
m_currentFrameset = currentFramesetElement ;
Q_ASSERT ( ! m_currentFrameset . isNull ( ) ) ;
TQDomElement t ;
forEachElement ( t , tqparent )
{
m_styleStack . save ( ) ;
const TQString localName = t . localName ( ) ;
const TQString ns = t . namespaceURI ( ) ;
const bool isTextNS = ns = = ooNS : : text ;
TQDomElement e ;
if ( isTextNS & & localName = = " p " ) { // text paragraph
fillStyleStack ( t , ooNS : : text , " style-name " ) ;
e = parseParagraph ( doc , t ) ;
}
else if ( isTextNS & & localName = = " h " ) // heading
{
fillStyleStack ( t , ooNS : : text , " style-name " ) ;
int level = t . attributeNS ( ooNS : : text , " level " , TQString ( ) ) . toInt ( ) ;
bool listOK = false ;
// When a heading is inside a list, it seems that the list prevails.
// Example:
// <text:ordered-list text:style-name="Numbering 1">
// <text:list-item text:start-value="5">
// <text:h text:style-name="P2" text:level="4">The header</text:h>
// where P2 has list-style-name="something else"
// Result: the numbering of the header follows "Numbering 1".
// So we use the style for the outline level only if we're not inside a list:
if ( ! m_nextItemIsListItem )
listOK = pushListLevelStyle ( " <outline-style> " , m_outlineStyle , level ) ;
m_nextItemIsListItem = true ;
if ( t . hasAttributeNS ( ooNS : : text , " start-value " ) )
// OASIS extension http://lists.oasis-open.org/archives/office/200310/msg00033.html
m_restartNumbering = t . attributeNS ( ooNS : : text , " start-value " , TQString ( ) ) . toInt ( ) ;
e = parseParagraph ( doc , t ) ;
if ( listOK )
m_listStyleStack . pop ( ) ;
}
else if ( isTextNS & &
( localName = = " unordered-list " | | localName = = " ordered-list " ) )
{
parseList ( doc , t , currentFramesetElement ) ;
m_styleStack . restore ( ) ;
continue ;
}
else if ( isTextNS & & localName = = " section " ) // Temporary support (###TODO)
{
kdDebug ( 30518 ) < < " Section found! " < < endl ;
fillStyleStack ( t , ooNS : : text , " style-name " ) ;
parseBodyOrSimilar ( doc , t , currentFramesetElement ) ;
}
else if ( localName = = " table " & & ns = = ooNS : : table )
{
kdDebug ( 30518 ) < < " Table found! " < < endl ;
parseTable ( doc , t , currentFramesetElement ) ;
}
else if ( localName = = " image " & & ns = = ooNS : : draw )
{
appendPicture ( doc , t ) ;
}
else if ( localName = = " text-box " & & ns = = ooNS : : draw )
{
appendTextBox ( doc , t ) ;
}
else if ( isTextNS & & localName = = " variable-decls " )
{
// We don't parse variable-decls since we ignore var types right now
// (and just storing a list of available var names wouldn't be much use)
}
else if ( localName = = " table-of-content " & & ns = = ooNS : : text )
{
appendTOC ( doc , t ) ;
}
// TODO text:sequence-decls
else
{
kdWarning ( 30518 ) < < " Unsupported body element ' " < < localName < < " ' " < < endl ;
}
if ( ! e . isNull ( ) )
currentFramesetElement . appendChild ( e ) ;
m_styleStack . restore ( ) ; // remove the styles added by the paragraph or list
}
m_currentFrameset = oldCurrentFrameset ; // in case of recursive invokations
}
void OoWriterImport : : createDocumentContent ( TQDomDocument & doc , TQDomElement & mainFramesetElement )
{
TQDomElement content = m_content . documentElement ( ) ;
TQDomElement body ( KoDom : : namedItemNS ( content , ooNS : : office , " body " ) ) ;
if ( body . isNull ( ) )
{
kdError ( 30518 ) < < " No office:body found! " < < endl ;
return ;
}
parseBodyOrSimilar ( doc , body , mainFramesetElement ) ;
}
void OoWriterImport : : writePageLayout ( TQDomDocument & mainDocument , const TQString & masterPageName )
{
TQDomElement docElement = mainDocument . documentElement ( ) ;
kdDebug ( 30518 ) < < " writePageLayout " < < masterPageName < < endl ;
TQDomElement elementPaper = mainDocument . createElement ( " PAPER " ) ;
KoOrientation orientation ;
double width , height ;
KoFormat paperFormat ;
double marginLeft , marginTop , marginRight , marginBottom ;
bool hasEvenOddHeader = false ;
bool hasEvenOddFooter = false ;
TQDomElement * masterPage = m_masterPages [ masterPageName ] ;
Q_ASSERT ( masterPage ) ;
kdDebug ( 30518 ) < < " page-master-name: " < < masterPage - > attributeNS ( ooNS : : style , " page-master-name " , TQString ( ) ) < < endl ;
TQDomElement * style = masterPage ? m_styles [ masterPage - > attributeNS ( ooNS : : style , " page-master-name " , TQString ( ) ) ] : 0 ;
Q_ASSERT ( style ) ;
if ( style )
{
TQDomElement properties ( KoDom : : namedItemNS ( * style , ooNS : : style , " properties " ) ) ;
Q_ASSERT ( ! properties . isNull ( ) ) ;
orientation = ( ( properties . attributeNS ( ooNS : : style , " print-orientation " , TQString ( ) ) ! = " portrait " ) ? PG_LANDSCAPE : PG_PORTRAIT ) ;
width = KoUnit : : parseValue ( properties . attributeNS ( ooNS : : fo , " page-width " , TQString ( ) ) ) ;
height = KoUnit : : parseValue ( properties . attributeNS ( ooNS : : fo , " page-height " , TQString ( ) ) ) ;
kdDebug ( 30518 ) < < " width= " < < width < < " height= " < < height < < endl ;
// guessFormat takes millimeters
if ( orientation = = PG_LANDSCAPE )
paperFormat = KoPageFormat : : guessFormat ( POINT_TO_MM ( height ) , POINT_TO_MM ( width ) ) ;
else
paperFormat = KoPageFormat : : guessFormat ( POINT_TO_MM ( width ) , POINT_TO_MM ( height ) ) ;
marginLeft = KoUnit : : parseValue ( properties . attributeNS ( ooNS : : fo , " margin-left " , TQString ( ) ) ) ;
marginTop = KoUnit : : parseValue ( properties . attributeNS ( ooNS : : fo , " margin-top " , TQString ( ) ) ) ;
marginRight = KoUnit : : parseValue ( properties . attributeNS ( ooNS : : fo , " margin-right " , TQString ( ) ) ) ;
marginBottom = KoUnit : : parseValue ( properties . attributeNS ( ooNS : : fo , " margin-bottom " , TQString ( ) ) ) ;
TQDomElement footnoteSep = KoDom : : namedItemNS ( properties , ooNS : : style , " footnote-sep " ) ;
if ( ! footnoteSep . isNull ( ) ) {
// style:width="0.018cm" style:distance-before-sep="0.101cm"
// style:distance-after-sep="0.101cm" style:adjustment="left"
// style:rel-width="25%" style:color="#000000"
TQString width = footnoteSep . attributeNS ( ooNS : : style , " width " , TQString ( ) ) ;
elementPaper . setAttribute ( " slFootNoteWidth " , KoUnit : : parseValue ( width ) ) ;
TQString pageWidth = footnoteSep . attributeNS ( ooNS : : style , " rel-width " , TQString ( ) ) ;
if ( pageWidth . endsWith ( " % " ) ) {
pageWidth . truncate ( pageWidth . length ( ) - 1 ) ; // remove '%'
elementPaper . setAttribute ( " slFootNoteLenth " , pageWidth ) ;
}
elementPaper . setAttribute ( " slFootNotePosition " , footnoteSep . attributeNS ( ooNS : : style , " adjustment " , TQString ( ) ) ) ;
// Not in KWord: color, distance before and after separator
// Not in OOo: line type of separator (solid, dot, dash etc.)
}
// Header/Footer
TQDomElement headerStyle = KoDom : : namedItemNS ( * style , ooNS : : style , " header-style " ) ;
TQDomElement footerStyle = KoDom : : namedItemNS ( * style , ooNS : : style , " footer-style " ) ;
TQDomElement headerLeftElem = KoDom : : namedItemNS ( * masterPage , ooNS : : style , " header-left " ) ;
if ( ! headerLeftElem . isNull ( ) ) {
kdDebug ( 30518 ) < < " Found header-left " < < endl ;
hasEvenOddHeader = true ;
importHeaderFooter ( mainDocument , headerLeftElem , hasEvenOddHeader , headerStyle ) ;
}
TQDomElement headerElem = KoDom : : namedItemNS ( * masterPage , ooNS : : style , " header " ) ;
if ( ! headerElem . isNull ( ) ) {
kdDebug ( 30518 ) < < " Found header " < < endl ;
importHeaderFooter ( mainDocument , headerElem , hasEvenOddHeader , headerStyle ) ;
}
TQDomElement footerLeftElem = KoDom : : namedItemNS ( * masterPage , ooNS : : style , " footer-left " ) ;
if ( ! footerLeftElem . isNull ( ) ) {
kdDebug ( 30518 ) < < " Found footer-left " < < endl ;
importHeaderFooter ( mainDocument , footerLeftElem , hasEvenOddFooter , footerStyle ) ;
}
TQDomElement footerElem = KoDom : : namedItemNS ( * masterPage , ooNS : : style , " footer " ) ;
if ( ! footerElem . isNull ( ) ) {
kdDebug ( 30518 ) < < " Found footer " < < endl ;
importHeaderFooter ( mainDocument , footerElem , hasEvenOddFooter , footerStyle ) ;
}
}
else
{
// We have no master page! We need defaults.
kdWarning ( 30518 ) < < " NO MASTER PAGE " < < endl ;
orientation = PG_PORTRAIT ;
paperFormat = PG_DIN_A4 ;
width = MM_TO_POINT ( KoPageFormat : : width ( paperFormat , orientation ) ) ;
height = MM_TO_POINT ( KoPageFormat : : height ( paperFormat , orientation ) ) ;
// ### TODO: better defaults for margins?
marginLeft = MM_TO_POINT ( 10.0 ) ;
marginRight = MM_TO_POINT ( 10.0 ) ;
marginTop = MM_TO_POINT ( 15.0 ) ;
marginBottom = MM_TO_POINT ( 15.0 ) ;
}
elementPaper . setAttribute ( " orientation " , int ( orientation ) ) ;
elementPaper . setAttribute ( " width " , width ) ;
elementPaper . setAttribute ( " height " , height ) ;
elementPaper . setAttribute ( " format " , paperFormat ) ;
elementPaper . setAttribute ( " columns " , 1 ) ; // TODO
elementPaper . setAttribute ( " columnspacing " , 2 ) ; // TODO
elementPaper . setAttribute ( " hType " , hasEvenOddHeader ? 3 : 0 ) ; // ### no support for first-page
elementPaper . setAttribute ( " fType " , hasEvenOddFooter ? 3 : 0 ) ; // ### no support for first-page
elementPaper . setAttribute ( " spHeadBody " , 9 ) ; // where is this in OOo?
elementPaper . setAttribute ( " spFootBody " , 9 ) ; // ?
elementPaper . setAttribute ( " zoom " , 100 ) ;
docElement . appendChild ( elementPaper ) ;
// Page margins
TQDomElement element = mainDocument . createElement ( " PAPERBORDERS " ) ;
element . setAttribute ( " left " , marginLeft ) ;
element . setAttribute ( " top " , marginTop ) ;
element . setAttribute ( " right " , marginRight ) ;
element . setAttribute ( " bottom " , marginBottom ) ;
elementPaper . appendChild ( element ) ;
}
void OoWriterImport : : prepareDocument ( TQDomDocument & mainDocument , TQDomElement & framesetsElem )
{
mainDocument = KoDocument : : createDomDocument ( " kword " , " DOC " , " 1.2 " ) ;
TQDomElement docElement = mainDocument . documentElement ( ) ;
docElement . setAttribute ( " editor " , " KWord's OOWriter Import Filter " ) ;
docElement . setAttribute ( " mime " , " application/x-kword " ) ;
docElement . setAttribute ( " syntaxVersion " , " 2 " ) ;
framesetsElem = mainDocument . createElement ( " FRAMESETS " ) ;
docElement . appendChild ( framesetsElem ) ;
// Now create VARIABLESETTINGS, mostly from meta.xml
TQDomElement varSettings = mainDocument . createElement ( " VARIABLESETTINGS " ) ;
docElement . appendChild ( varSettings ) ;
TQDomNode meta = KoDom : : namedItemNS ( m_meta , ooNS : : office , " document-meta " ) ;
TQDomNode office = KoDom : : namedItemNS ( meta , ooNS : : office , " meta " ) ;
if ( ! office . isNull ( ) ) {
TQDomElement date = KoDom : : namedItemNS ( office , ooNS : : dc , " date " ) ;
if ( ! date . isNull ( ) & & ! date . text ( ) . isEmpty ( ) ) {
// Both use ISO-8601, no conversion needed.
varSettings . setAttribute ( " modificationDate " , date . text ( ) ) ;
}
date = KoDom : : namedItemNS ( office , ooNS : : meta , " creation-date " ) ;
if ( ! date . isNull ( ) & & ! date . text ( ) . isEmpty ( ) ) {
varSettings . setAttribute ( " creationDate " , date . text ( ) ) ;
}
date = KoDom : : namedItemNS ( office , ooNS : : meta , " print-date " ) ;
if ( ! date . isNull ( ) & & ! date . text ( ) . isEmpty ( ) ) {
varSettings . setAttribute ( " lastPrintingDate " , date . text ( ) ) ;
}
}
}
// Copied from the msword importer
TQDomElement OoWriterImport : : createInitialFrame ( TQDomElement & parentFramesetElem , double left , double right , double top , double bottom , bool autoExtend , NewFrameBehavior nfb )
{
TQDomElement frameElementOut = parentFramesetElem . ownerDocument ( ) . createElement ( " FRAME " ) ;
frameElementOut . setAttribute ( " left " , left ) ;
frameElementOut . setAttribute ( " right " , right ) ;
frameElementOut . setAttribute ( " top " , top ) ;
frameElementOut . setAttribute ( " bottom " , bottom ) ;
frameElementOut . setAttribute ( " runaround " , 1 ) ;
// AutoExtendFrame for header/footer/footnote/endnote, AutoCreateNewFrame for body text
frameElementOut . setAttribute ( " autoCreateNewFrame " , autoExtend ? 0 : 1 ) ;
frameElementOut . setAttribute ( " newFrameBehavior " , nfb ) ;
parentFramesetElem . appendChild ( frameElementOut ) ;
return frameElementOut ;
}
KoFilter : : ConversiontqStatus OoWriterImport : : loadAndParse ( const TQString & filename , TQDomDocument & doc )
{
return OoUtils : : loadAndParse ( filename , doc , m_zip ) ;
}
KoFilter : : ConversiontqStatus OoWriterImport : : openFile ( )
{
KoFilter : : ConversiontqStatus status = loadAndParse ( " content.xml " , m_content ) ;
if ( status ! = KoFilter : : OK )
{
kdError ( 30518 ) < < " Content.xml could not be parsed correctly! Aborting! " < < endl ;
return status ;
}
//kdDebug(30518)<<" m_content.toCString() :"<<m_content.toCString()<<endl;
// We need to keep the TQDomDocument for styles too, unfortunately.
// Otherwise styleElement.parentNode() returns a null node
// (see StyleStack::isUserStyle). Most of styles.xml is in m_styles
// anyway, so this doesn't make a big difference.
// We now also rely on this in createStyles.
//TQDomDocument styles;
// We do not stop if the following calls fail.
loadAndParse ( " styles.xml " , m_stylesDoc ) ;
loadAndParse ( " meta.xml " , m_meta ) ;
// not used yet: loadAndParse("settings.xml", m_settings);
emit sigProgress ( 10 ) ;
return KoFilter : : OK ;
}
// Very related to OoImpressImport::createDocumentInfo
void OoWriterImport : : createDocumentInfo ( TQDomDocument & docinfo )
{
docinfo = KoDocument : : createDomDocument ( " document-info " /*DTD name*/ , " document-info " /*tag name*/ , " 1.1 " ) ;
OoUtils : : createDocumentInfo ( m_meta , docinfo ) ;
//kdDebug(30518)<<" meta-info :"<<m_meta.toCString()<<endl;
}
// This mainly fills member variables with the styles.
// The 'doc' argument is only for footnotes-configuration.
bool OoWriterImport : : createStyleMap ( const TQDomDocument & styles , TQDomDocument & doc )
{
TQDomElement docElement = styles . documentElement ( ) ;
TQDomNode docStyles = KoDom : : namedItemNS ( docElement , ooNS : : office , " document-styles " ) ;
if ( docElement . hasAttributeNS ( ooNS : : office , " version " ) )
{
bool ok = true ;
double d = docElement . attributeNS ( ooNS : : office , " version " , TQString ( ) ) . toDouble ( & ok ) ;
if ( ok )
{
kdDebug ( 30518 ) < < " OpenWriter version: " < < d < < endl ;
if ( d > 1.0 )
{
TQString message ( i18n ( " This document was created with OpenOffice.org version '%1'. This filter was written for version 1.0. Reading this file could cause strange behavior, crashes or incorrect display of the data. Do you want to continue converting the document? " ) ) ;
message = message . tqarg ( docElement . attributeNS ( ooNS : : office , " version " , TQString ( ) ) ) ;
if ( KMessageBox : : warningYesNo ( 0 , message , i18n ( " Unsupported document version " ) ) = = KMessageBox : : No )
return false ;
}
}
}
TQDomNode fontStyles = KoDom : : namedItemNS ( docElement , ooNS : : office , " font-decls " ) ;
if ( ! fontStyles . isNull ( ) )
{
kdDebug ( 30518 ) < < " Starting reading in font-decl... " < < endl ;
insertStyles ( fontStyles . toElement ( ) , doc ) ;
}
else
kdDebug ( 30518 ) < < " No items found " < < endl ;
kdDebug ( 30518 ) < < " Starting reading in office:automatic-styles " < < endl ;
TQDomNode autoStyles = KoDom : : namedItemNS ( docElement , ooNS : : office , " automatic-styles " ) ;
if ( ! autoStyles . isNull ( ) )
{
insertStyles ( autoStyles . toElement ( ) , doc ) ;
}
else
kdDebug ( 30518 ) < < " No items found " < < endl ;
kdDebug ( 30518 ) < < " Reading in master styles " < < endl ;
TQDomNode masterStyles = KoDom : : namedItemNS ( docElement , ooNS : : office , " master-styles " ) ;
if ( ! masterStyles . isNull ( ) )
{
TQDomElement master ;
forEachElement ( master , masterStyles )
{
if ( master . localName ( ) = = " master-page " & & master . namespaceURI ( ) = = ooNS : : style )
{
TQString name = master . attributeNS ( ooNS : : style , " name " , TQString ( ) ) ;
kdDebug ( 30518 ) < < " Master style: ' " < < name < < " ' loaded " < < endl ;
m_masterPages . insert ( name , new TQDomElement ( master ) ) ;
} else
kdWarning ( 30518 ) < < " Unknown tag " < < master . tagName ( ) < < " in office:master-styles " < < endl ;
}
}
kdDebug ( 30518 ) < < " Starting reading in office:styles " < < endl ;
TQDomNode fixedStyles = KoDom : : namedItemNS ( docElement , ooNS : : office , " styles " ) ;
if ( ! fixedStyles . isNull ( ) )
insertStyles ( fixedStyles . toElement ( ) , doc ) ;
kdDebug ( 30518 ) < < " Styles read in. " < < endl ;
return true ;
}
// started as a copy of OoImpressImport::insertStyles
void OoWriterImport : : insertStyles ( const TQDomElement & styles , TQDomDocument & doc )
{
//kdDebug(30518) << "Inserting styles from " << styles.tagName() << endl;
TQDomElement e ;
forEachElement ( e , styles )
{
const TQString localName = e . localName ( ) ;
const TQString ns = e . namespaceURI ( ) ;
const TQString name = e . attributeNS ( ooNS : : style , " name " , TQString ( ) ) ;
if ( ns = = ooNS : : style & & (
localName = = " style "
| | localName = = " page-master "
| | localName = = " font-decl " ) )
{
TQDomElement * ep = new TQDomElement ( e ) ;
m_styles . insert ( name , ep ) ;
kdDebug ( 30518 ) < < " Style: ' " < < name < < " ' loaded " < < endl ;
} else if ( localName = = " default-style " & & ns = = ooNS : : style ) {
m_defaultStyle = e ;
} else if ( localName = = " list-style " & & ns = = ooNS : : text ) {
TQDomElement * ep = new TQDomElement ( e ) ;
m_listStyles . insert ( name , ep ) ;
kdDebug ( 30518 ) < < " List style: ' " < < name < < " ' loaded " < < endl ;
} else if ( localName = = " outline-style " & & ns = = ooNS : : text ) {
m_outlineStyle = e ;
} else if ( localName = = " footnotes-configuration " & & ns = = ooNS : : text ) {
importFootnotesConfiguration ( doc , e , false ) ;
} else if ( localName = = " endnotes-configuration " & & ns = = ooNS : : text ) {
importFootnotesConfiguration ( doc , e , true ) ;
} else if ( localName = = " linenumbering-configuration " & & ns = = ooNS : : text ) {
// Not implemented in KWord
} else if ( localName = = " number-style " & & ns = = ooNS : : number ) {
// TODO
} else if ( ( localName = = " date-style "
| | localName = = " time-style " ) & & ns = = ooNS : : number ) {
importDateTimeStyle ( e ) ;
} else {
kdWarning ( 30518 ) < < " Unknown element " < < localName < < " in styles " < < endl ;
}
}
}
// OO spec 2.5.4. p68. Conversion to TQt format: see qdate.html
// OpenCalcImport::loadFormat has similar code, but slower, intermixed with other stuff,
// lacking long-textual forms.
void OoWriterImport : : importDateTimeStyle ( const TQDomElement & tqparent )
{
TQString format ;
TQDomElement e ;
forEachElement ( e , tqparent )
{
const TQString ns = e . namespaceURI ( ) ;
if ( ns ! = ooNS : : number )
continue ;
const TQString localName = e . localName ( ) ;
const TQString numberStyle = e . attributeNS ( ooNS : : number , " style " , TQString ( ) ) ;
const bool shortForm = numberStyle = = " short " | | numberStyle . isEmpty ( ) ;
if ( localName = = " day " ) {
format + = shortForm ? " d " : " dd " ;
} else if ( localName = = " day-of-week " ) {
format + = shortForm ? " ddd " : " dddd " ;
} else if ( localName = = " month " ) {
// TODO the spec has a strange mention of number:format-source
if ( e . attributeNS ( ooNS : : number , " textual " , TQString ( ) ) = = " true " ) {
format + = shortForm ? " MMM " : " MMMM " ;
} else { // month number
format + = shortForm ? " M " : " MM " ;
}
} else if ( localName = = " year " ) {
format + = shortForm ? " yy " : " yyyy " ;
} else if ( localName = = " week-of-year " | | localName = = " quarter " ) {
// ### not supported in TQt
} else if ( localName = = " hours " ) {
format + = shortForm ? " h " : " hh " ;
} else if ( localName = = " minutes " ) {
format + = shortForm ? " m " : " mm " ;
} else if ( localName = = " seconds " ) {
format + = shortForm ? " s " : " ss " ;
} else if ( localName = = " am-pm " ) {
format + = " ap " ;
} else if ( localName = = " text " ) { // litteral
format + = e . text ( ) ;
} // TODO number:decimal-places
}
#if 0
// TQDate doesn't work both ways!!! It can't parse something back from
// a string and a format (e.g. 01/02/03 and dd/MM/yy, it will assume MM/dd/yy).
// So we also need to generate a KLocale-like format, to parse the value
// Update: we don't need to parse the date back.
TQString kdeFormat ;
TQDomElement e ;
forEachElement ( e , tqparent )
{
const TQString ns = e . namespaceURI ( ) ;
if ( ns ! = ooNS : : number )
continue ;
TQString localName = e . tagName ( ) ;
const TQString numberStyle = e . attributeNS ( ooNS : : number , " style " , TQString ( ) ) ;
const bool shortForm = numberStyle = = " short " | | numberStyle . isEmpty ( ) ;
if ( localName = = " day " ) {
kdeFormat + = shortForm ? " %e " : " %d " ;
} else if ( localName = = " day-of-week " ) {
kdeFormat + = shortForm ? " %a " : " %A " ;
} else if ( localName = = " month " ) {
// TODO the spec has a strange mention of number:format-source
if ( e . attributeNS ( ooNS : : number , " textual " , TQString ( ) ) = = " true " ) {
kdeFormat + = shortForm ? " %b " : " %B " ;
} else { // month number
kdeFormat + = shortForm ? " %n " : " %m " ;
}
} else if ( localName = = " year " ) {
kdeFormat + = shortForm ? " %y " : " %Y " ;
} else if ( localName = = " week-of-year " | | localName = = " quarter " ) {
// ### not supported in KLocale
} else if ( localName = = " hours " ) {
kdeFormat + = shortForm ? " %k " : " %H " ; // TODO should depend on presence of am/pm
} else if ( localName = = " minutes " ) {
kdeFormat + = shortForm ? " %M " : " %M " ; // KLocale doesn't have 1-digit minutes
} else if ( localName = = " seconds " ) {
kdeFormat + = shortForm ? " %S " : " %S " ; // KLocale doesn't have 1-digit seconds
} else if ( localName = = " am-pm " ) {
kdeFormat + = " %p " ;
} else if ( localName = = " text " ) { // litteral
kdeFormat + = e . text ( ) ;
} // TODO number:decimal-places
}
# endif
TQString styleName = tqparent . attributeNS ( ooNS : : style , " name " , TQString ( ) ) ;
kdDebug ( 30518 ) < < " datetime style: " < < styleName < < " qt format= " < < format < < endl ;
m_dateTimeFormats . insert ( styleName , format ) ;
}
void OoWriterImport : : fillStyleStack ( const TQDomElement & object , const char * nsURI , const TQString & attrName )
{
// find all styles associated with an object and push them on the stack
// OoImpressImport has more tests here, but I don't think they're relevant to OoWriterImport
if ( object . hasAttributeNS ( nsURI , attrName ) ) {
const TQString styleName = object . attributeNS ( nsURI , attrName , TQString ( ) ) ;
const TQDomElement * style = m_styles [ styleName ] ;
if ( style )
addStyles ( style ) ;
else
kdWarning ( 30518 ) < < " fillStyleStack: no style named " < < styleName < < " found. " < < endl ;
}
}
void OoWriterImport : : addStyles ( const TQDomElement * style )
{
Q_ASSERT ( style ) ;
if ( ! style ) return ;
// this recursive function is necessary as tqparent styles can have parents themselves
if ( style - > hasAttributeNS ( ooNS : : style , " tqparent-style-name " ) ) {
const TQString parentStyleName = style - > attributeNS ( ooNS : : style , " tqparent-style-name " , TQString ( ) ) ;
TQDomElement * parentStyle = m_styles [ parentStyleName ] ;
if ( parentStyle )
addStyles ( parentStyle ) ;
else
kdWarning ( 30518 ) < < " Parent style not found: " < < parentStyleName < < endl ;
}
else if ( ! m_defaultStyle . isNull ( ) ) // on top of all, the default style
m_styleStack . push ( m_defaultStyle ) ;
//kdDebug(30518) << "pushing style " << style->attributeNS( ooNS::style, "name", TQString() ) << endl;
m_styleStack . push ( * style ) ;
}
void OoWriterImport : : applyListStyle ( TQDomDocument & doc , TQDomElement & layoutElement , const TQDomElement & paragraph )
{
// Spec: see 3.3.5 p137
if ( m_listStyleStack . hasListStyle ( ) & & m_nextItemIsListItem ) {
bool heading = paragraph . localName ( ) = = " h " ;
m_nextItemIsListItem = false ;
int level = heading ? paragraph . attributeNS ( ooNS : : text , " level " , TQString ( ) ) . toInt ( ) : m_listStyleStack . level ( ) ;
writeCounter ( doc , layoutElement , heading , level , m_insideOrderedList ) ;
}
}
void OoWriterImport : : writeCounter ( TQDomDocument & doc , TQDomElement & layoutElement , bool heading , int level , bool ordered )
{
const TQDomElement listStyle = m_listStyleStack . currentListStyle ( ) ;
//const TQDomElement listStyleProperties = m_listStyleStack.currentListStyleProperties();
TQDomElement counter = doc . createElement ( " COUNTER " ) ;
counter . setAttribute ( " numberingtype " , heading ? 1 : 0 ) ;
counter . setAttribute ( " depth " , level - 1 ) ; // "depth" starts at 0
//kdDebug(30518) << "Numbered parag. heading=" << heading << " level=" << level
// << " m_restartNumbering=" << m_restartNumbering << endl;
if ( ordered | | heading ) {
counter . setAttribute ( " type " , Conversion : : importCounterType ( listStyle . attributeNS ( ooNS : : style , " num-format " , TQString ( ) ) ) ) ;
counter . setAttribute ( " lefttext " , listStyle . attributeNS ( ooNS : : style , " num-prefix " , TQString ( ) ) ) ;
counter . setAttribute ( " righttext " , listStyle . attributeNS ( ooNS : : style , " num-suffix " , TQString ( ) ) ) ;
TQString dl = listStyle . attributeNS ( ooNS : : text , " display-levels " , TQString ( ) ) ;
if ( dl . isEmpty ( ) )
dl = " 1 " ;
counter . setAttribute ( " display-levels " , dl ) ;
if ( m_restartNumbering ! = - 1 ) {
counter . setAttribute ( " start " , m_restartNumbering ) ;
counter . setAttribute ( " restart " , " true " ) ;
} else {
// useful?
counter . setAttribute ( " start " , listStyle . attributeNS ( ooNS : : text , " start-value " , TQString ( ) ) ) ;
}
}
else { // bullets, see 3.3.6 p138
counter . setAttribute ( " type " , 6 ) ;
TQString bulletChar = listStyle . attributeNS ( ooNS : : text , " bullet-char " , TQString ( ) ) ;
if ( ! bulletChar . isEmpty ( ) ) {
#if 0 // doesn't work well. Fonts lack those symbols!
counter . setAttribute ( " bullet " , bulletChar [ 0 ] . tqunicode ( ) ) ;
kdDebug ( 30518 ) < < " bullet code " < < bulletChar [ 0 ] . tqunicode ( ) < < endl ;
TQString fontName = listStyleProperties . attributeNS ( ooNS : : style , " font-name " , TQString ( ) ) ;
counter . setAttribute ( " bulletfont " , fontName ) ;
# endif
// Reverse engineering, I found those codes:
switch ( bulletChar [ 0 ] . tqunicode ( ) ) {
case 0x2022 : // small disc
counter . setAttribute ( " type " , 10 ) ; // a disc bullet
break ;
case 0x25CF : // large disc
counter . setAttribute ( " type " , 10 ) ; // a disc bullet
break ;
case 0xE00C : // losange - TODO in KWord. Not in OASIS either (reserved Unicode area!)
counter . setAttribute ( " type " , 10 ) ; // a disc bullet
break ;
case 0xE00A : // square. Not in OASIS (reserved Unicode area!)
counter . setAttribute ( " type " , 9 ) ;
break ;
case 0x2794 : // arrow
case 0x27A2 : // two-colors right-pointing triangle (TODO)
counter . setAttribute ( " bullet " , 206 ) ; // simpler arrow symbol
counter . setAttribute ( " bulletfont " , " symbol " ) ;
break ;
case 0x2717 : // cross
counter . setAttribute ( " bullet " , 212 ) ; // simpler cross symbol
counter . setAttribute ( " bulletfont " , " symbol " ) ;
break ;
case 0x2714 : // checkmark
counter . setAttribute ( " bullet " , 246 ) ; // hmm that's sqrt
counter . setAttribute ( " bulletfont " , " symbol " ) ;
break ;
default :
counter . setAttribute ( " type " , 8 ) ; // circle
break ;
}
} else { // can never happen
counter . setAttribute ( " type " , 10 ) ; // a disc bullet
}
}
layoutElement . appendChild ( counter ) ;
}
static TQDomElement findListLevelStyle ( TQDomElement & fullListStyle , int level )
{
TQDomElement listLevelItem ;
forEachElement ( listLevelItem , fullListStyle )
{
if ( listLevelItem . attributeNS ( ooNS : : text , " level " , TQString ( ) ) . toInt ( ) = = level )
return listLevelItem ;
}
return TQDomElement ( ) ;
}
bool OoWriterImport : : pushListLevelStyle ( const TQString & listStyleName , int level )
{
TQDomElement * fullListStyle = m_listStyles [ listStyleName ] ;
if ( ! fullListStyle ) {
kdWarning ( 30518 ) < < " List style " < < listStyleName < < " not found! " < < endl ;
return false ;
}
else
return pushListLevelStyle ( listStyleName , * fullListStyle , level ) ;
}
bool OoWriterImport : : pushListLevelStyle ( const TQString & listStyleName , // for debug only
TQDomElement & fullListStyle , int level )
{
// Find applicable list-level-style for level
int i = level ;
TQDomElement listLevelStyle ;
while ( i > 0 & & listLevelStyle . isNull ( ) ) {
listLevelStyle = findListLevelStyle ( fullListStyle , i ) ;
- - i ;
}
if ( listLevelStyle . isNull ( ) ) {
kdWarning ( 30518 ) < < " List level style for level " < < level < < " in list style " < < listStyleName < < " not found! " < < endl ;
return false ;
}
kdDebug ( 30518 ) < < " Pushing list-level-style from list-style " < < listStyleName < < " level " < < level < < endl ;
m_listStyleStack . push ( listLevelStyle ) ;
return true ;
}
void OoWriterImport : : parseList ( TQDomDocument & doc , const TQDomElement & list , TQDomElement & currentFramesetElement )
{
//kdDebug(30518) << k_funcinfo << "parseList"<< endl;
m_insideOrderedList = ( list . localName ( ) = = " ordered-list " ) ;
TQString oldListStyleName = m_currentListStyleName ;
if ( list . hasAttributeNS ( ooNS : : text , " style-name " ) )
m_currentListStyleName = list . attributeNS ( ooNS : : text , " style-name " , TQString ( ) ) ;
bool listOK = ! m_currentListStyleName . isEmpty ( ) ;
const int level = m_listStyleStack . level ( ) + 1 ;
//kdDebug(30518) << k_funcinfo << " listOK=" << listOK << " level=" << level << endl;
if ( listOK )
listOK = pushListLevelStyle ( m_currentListStyleName , level ) ;
// Iterate over list items
TQDomElement listItem ;
forEachElement ( listItem , list )
{
// It's either list-header (normal text on top of list) or list-item
m_nextItemIsListItem = ( listItem . localName ( ) ! = " list-header " ) ;
m_restartNumbering = - 1 ;
if ( listItem . hasAttributeNS ( ooNS : : text , " start-value " ) )
m_restartNumbering = listItem . attributeNS ( ooNS : : text , " start-value " , TQString ( ) ) . toInt ( ) ;
// ### Oasis: can be p h or list only.
parseBodyOrSimilar ( doc , listItem , currentFramesetElement ) ;
m_restartNumbering = - 1 ;
}
if ( listOK )
m_listStyleStack . pop ( ) ;
m_currentListStyleName = oldListStyleName ;
}
static int numberOfParagraphs ( const TQDomElement & frameset )
{
const TQDomNodeList tqchildren = frameset . childNodes ( ) ;
const TQString paragStr = " PARAGRAPH " ;
int paragCount = 0 ;
for ( unsigned int i = 0 ; i < tqchildren . length ( ) ; + + i ) {
if ( tqchildren . item ( i ) . toElement ( ) . tagName ( ) = = paragStr )
+ + paragCount ;
}
return paragCount ;
}
void OoWriterImport : : parseSpanOrSimilar ( TQDomDocument & doc , const TQDomElement & tqparent ,
TQDomElement & outputParagraph , TQDomElement & outputFormats , TQString & paragraphText , uint & pos )
{
// Parse every child node of the tqparent
// Can't use forEachElement here since we also care about text nodes
for ( TQDomNode node ( tqparent . firstChild ( ) ) ; ! node . isNull ( ) ; node = node . nextSibling ( ) )
{
TQDomElement ts ( node . toElement ( ) ) ;
TQString textData ;
const TQString localName ( ts . localName ( ) ) ;
const TQString ns = ts . namespaceURI ( ) ;
const bool isTextNS = ns = = ooNS : : text ;
TQDomText t ( node . toText ( ) ) ;
bool shouldWriteFormat = false ; // By default no <FORMAT> element should be written
// Try to keep the order of the tag names by probability of happening
if ( isTextNS & & localName = = " span " ) // text:span
{
m_styleStack . save ( ) ;
fillStyleStack ( ts , ooNS : : text , " style-name " ) ;
parseSpanOrSimilar ( doc , ts , outputParagraph , outputFormats , paragraphText , pos ) ;
m_styleStack . restore ( ) ;
}
else if ( isTextNS & & localName = = " s " ) // text:s
{
textData = OoUtils : : expandWhitespace ( ts ) ;
shouldWriteFormat = true ;
}
else if ( isTextNS & & localName = = " tab-stop " ) // text:tab-stop
{
// KWord currently uses \t.
// Known bug: a line with only \t\t\t\t isn't loaded - XML (TQDom) strips out whitespace.
// One more good reason to switch to <text:tab-stop> instead...
textData = ' \t ' ;
shouldWriteFormat = true ;
}
else if ( isTextNS & & localName = = " line-break " )
{
textData = ' \n ' ;
shouldWriteFormat = true ;
}
else if ( isTextNS & &
( localName = = " footnote " | | localName = = " endnote " ) )
{
textData = ' # ' ; // anchor placeholder
importFootnote ( doc , ts , outputFormats , pos , localName ) ;
}
else if ( localName = = " image " & & ns = = ooNS : : draw )
{
textData = ' # ' ; // anchor placeholder
TQString frameName = appendPicture ( doc , ts ) ;
anchorFrameset ( doc , outputFormats , pos , frameName ) ;
}
else if ( localName = = " text-box " & & ns = = ooNS : : draw )
{
textData = ' # ' ; // anchor placeholder
TQString frameName = appendTextBox ( doc , ts ) ;
anchorFrameset ( doc , outputFormats , pos , frameName ) ;
}
else if ( isTextNS & & localName = = " a " )
{
m_styleStack . save ( ) ;
TQString href ( ts . attributeNS ( ooNS : : xlink , " href " , TQString ( ) ) ) ;
if ( href . startsWith ( " # " ) )
{
// We have a reference to a bookmark (### TODO)
// As we do not support it now, treat it as a <text:span> without formatting
parseSpanOrSimilar ( doc , ts , outputParagraph , outputFormats , paragraphText , pos ) ;
}
else
{
// The problem is that KWord's hyperlink text is not inside the normal text, but for OOWriter it is nearly a <text:span>
// So we have to fake.
TQDomElement fakeParagraph , fakeFormats ;
uint fakePos = 0 ;
TQString text ;
parseSpanOrSimilar ( doc , ts , fakeParagraph , fakeFormats , text , fakePos ) ;
textData = ' # ' ; // hyperlink placeholder
TQDomElement linkElement ( doc . createElement ( " LINK " ) ) ;
linkElement . setAttribute ( " hrefName " , ts . attributeNS ( ooNS : : xlink , " href " , TQString ( ) ) ) ;
linkElement . setAttribute ( " linkName " , text ) ;
appendKWordVariable ( doc , outputFormats , ts , pos , " STRING " , 9 , linkElement ) ;
}
m_styleStack . restore ( ) ;
}
else if ( isTextNS & &
( localName = = " date " // fields
| | localName = = " print-time "
| | localName = = " print-date "
| | localName = = " creation-time "
| | localName = = " creation-date "
| | localName = = " modification-time "
| | localName = = " modification-date "
| | localName = = " time "
| | localName = = " page-number "
| | localName = = " chapter "
| | localName = = " file-name "
| | localName = = " author-name "
| | localName = = " author-initials "
| | localName = = " subject "
| | localName = = " title "
| | localName = = " description "
| | localName = = " variable-set "
| | localName = = " page-variable-get "
| | localName = = " user-defined "
| | localName . startsWith ( " sender- " )
) )
// TODO in kword: printed-by, initial-creator
{
textData = " # " ; // field placeholder
appendField ( doc , outputFormats , ts , pos ) ;
}
else if ( isTextNS & & localName = = " bookmark " )
{
// the number of <PARAGRAPH> tags in the frameset element is the parag id
// (-1 for starting at 0, +1 since not written yet)
Q_ASSERT ( ! m_currentFrameset . isNull ( ) ) ;
appendBookmark ( doc , numberOfParagraphs ( m_currentFrameset ) ,
pos , ts . attributeNS ( ooNS : : text , " name " , TQString ( ) ) ) ;
}
else if ( isTextNS & & localName = = " bookmark-start " ) {
m_bookmarkStarts . insert ( ts . attributeNS ( ooNS : : text , " name " , TQString ( ) ) ,
BookmarkStart ( m_currentFrameset . attribute ( " name " ) ,
numberOfParagraphs ( m_currentFrameset ) ,
pos ) ) ;
}
else if ( isTextNS & & localName = = " bookmark-end " ) {
TQString bkName = ts . attributeNS ( ooNS : : text , " name " , TQString ( ) ) ;
BookmarkStartsMap : : iterator it = m_bookmarkStarts . find ( bkName ) ;
if ( it = = m_bookmarkStarts . end ( ) ) { // bookmark end without start. This seems to happen..
// insert simple bookmark then
appendBookmark ( doc , numberOfParagraphs ( m_currentFrameset ) ,
pos , ts . attributeNS ( ooNS : : text , " name " , TQString ( ) ) ) ;
} else {
if ( ( * it ) . frameSetName ! = m_currentFrameset . attribute ( " name " ) ) {
// Oh tell me this never happens...
kdWarning ( 30518 ) < < " Cross-frameset bookmark! Not supported. " < < endl ;
} else {
appendBookmark ( doc , ( * it ) . paragId , ( * it ) . pos ,
numberOfParagraphs ( m_currentFrameset ) , pos , it . key ( ) ) ;
}
m_bookmarkStarts . remove ( it ) ;
}
}
else if ( t . isNull ( ) ) // no textnode, we must ignore
{
kdWarning ( 30518 ) < < " Ignoring tag " < < ts . tagName ( ) < < endl ;
continue ;
}
else
{
textData = t . data ( ) ;
shouldWriteFormat = true ;
}
paragraphText + = textData ;
const uint length = textData . length ( ) ;
if ( shouldWriteFormat )
{
writeFormat ( doc , outputFormats , 1 /* id for normal text */ , pos , length ) ;
}
pos + = length ;
}
}
TQDomElement OoWriterImport : : parseParagraph ( TQDomDocument & doc , const TQDomElement & paragraph )
{
TQDomElement p = doc . createElement ( " PARAGRAPH " ) ;
TQDomElement formats = doc . createElement ( " FORMATS " ) ;
TQString paragraphText ;
uint pos = 0 ;
// parse every child node of the paragraph
parseSpanOrSimilar ( doc , paragraph , p , formats , paragraphText , pos ) ;
TQDomElement text = doc . createElement ( " TEXT " ) ;
text . appendChild ( doc . createTextNode ( paragraphText ) ) ;
text . setAttribute ( " xml:space " , " preserve " ) ;
p . appendChild ( text ) ;
//kdDebug(30518) << k_funcinfo << "Para text is: " << paragraphText << endl;
p . appendChild ( formats ) ;
TQDomElement layoutElement = doc . createElement ( " LAYOUT " ) ;
p . appendChild ( layoutElement ) ;
// Style name
TQString styleName = m_styleStack . userStyleName ( " paragraph " ) ;
if ( ! styleName . isEmpty ( ) )
{
TQDomElement nameElement = doc . createElement ( " NAME " ) ;
nameElement . setAttribute ( " value " , kWordStyleName ( styleName ) ) ;
layoutElement . appendChild ( nameElement ) ;
}
writeLayout ( doc , layoutElement ) ;
writeFormat ( doc , layoutElement , 1 , 0 , 0 ) ; // paragraph format, useful for empty parags
applyListStyle ( doc , layoutElement , paragraph ) ;
TQDomElement * paragraphStyle = m_styles [ paragraph . attributeNS ( ooNS : : text , " style-name " , TQString ( ) ) ] ;
TQString masterPageName = paragraphStyle ? paragraphStyle - > attributeNS ( ooNS : : style , " master-page-name " , TQString ( ) ) : TQString ( ) ;
if ( masterPageName . isEmpty ( ) )
masterPageName = " Standard " ; // Seems to be a builtin name for the default tqlayout...
if ( masterPageName ! = m_currentMasterPage )
{
// Detected a change in the master page -> this means we have to use a new page tqlayout
// and insert a frame break if not on the first paragraph.
// In KWord we don't support sections so the first paragraph is the one that determines the page tqlayout.
if ( m_currentMasterPage . isEmpty ( ) ) {
m_currentMasterPage = masterPageName ; // before writePageLayout to avoid recursion
writePageLayout ( doc , masterPageName ) ;
}
else
{
m_currentMasterPage = masterPageName ;
TQDomElement pageBreakElem = layoutElement . namedItem ( " PAGEBREAKING " ) . toElement ( ) ;
if ( ! pageBreakElem . isNull ( ) ) {
pageBreakElem = doc . createElement ( " PAGEBREAKING " ) ;
layoutElement . appendChild ( pageBreakElem ) ;
}
pageBreakElem . setAttribute ( " hardFrameBreak " , " true " ) ;
// We have no way to store the new page tqlayout, KWord doesn't have sections.
}
}
return p ;
}
void OoWriterImport : : writeFormat ( TQDomDocument & doc , TQDomElement & formats , int id , int pos , int length )
{
// Prepare a FORMAT element for this run of text
TQDomElement format ( doc . createElement ( " FORMAT " ) ) ;
format . setAttribute ( " id " , id ) ;
format . setAttribute ( " pos " , pos ) ;
format . setAttribute ( " len " , length ) ;
// parse the text-properties
// TODO compare with the paragraph style, and only write out if != from style.
// (This is very important, it's not only an optimization. If no attribute is
// specified in the .kwd file, the style's property will be used, which might
// not always be correct).
// On 2nd thought, this doesn't seem to happen. If a style property needs undoing
// OO always writes it down in the XML, so we write out the property just fine.
// Both apps implement a "write property only if necessary" mechanism, I can't'
// find a case that breaks yet.
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " color " ) ) { // 3.10.3
TQColor color ( m_styleStack . attributeNS ( ooNS : : fo , " color " ) ) ; // #rrggbb format
TQDomElement colorElem ( doc . createElement ( " COLOR " ) ) ;
colorElem . setAttribute ( " red " , color . red ( ) ) ;
colorElem . setAttribute ( " blue " , color . blue ( ) ) ;
colorElem . setAttribute ( " green " , color . green ( ) ) ;
format . appendChild ( colorElem ) ;
}
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " font-family " ) // 3.10.9
| | m_styleStack . hasAttributeNS ( ooNS : : style , " font-name " ) ) // 3.10.8
{
// Hmm, the remove "'" could break it's in the middle of the fontname...
TQString fontName = m_styleStack . attributeNS ( ooNS : : fo , " font-family " ) . remove ( " ' " ) ;
if ( fontName . isEmpty ( ) )
{
// ##### TODO. This is wrong. style:font-name refers to a font-decl entry.
// We have to look it up there, and retrieve _all_ font attributes from it, not just the name.
fontName = m_styleStack . attributeNS ( ooNS : : style , " font-name " ) . remove ( " ' " ) ;
}
// 'Thorndale' is not known outside OpenOffice so we substitute it
// with 'Times New Roman' that looks nearly the same.
if ( fontName = = " Thorndale " )
fontName = " Times New Roman " ;
fontName . remove ( TQRegExp ( " \\ sCE$ " ) ) ; // Arial CE -> Arial
TQDomElement fontElem ( doc . createElement ( " FONT " ) ) ;
fontElem . setAttribute ( " name " , fontName ) ;
format . appendChild ( fontElem ) ;
}
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " font-size " ) ) { // 3.10.14
double pointSize = m_styleStack . fontSize ( ) ;
TQDomElement fontSize ( doc . createElement ( " SIZE " ) ) ;
fontSize . setAttribute ( " value " , tqRound ( pointSize ) ) ; // KWord uses toInt()!
format . appendChild ( fontSize ) ;
}
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " font-weight " ) ) { // 3.10.24
TQDomElement weightElem ( doc . createElement ( " WEIGHT " ) ) ;
TQString fontWeight = m_styleStack . attributeNS ( ooNS : : fo , " font-weight " ) ;
int boldness = fontWeight . toInt ( ) ;
if ( fontWeight = = " bold " )
boldness = 75 ;
else if ( boldness = = 0 )
boldness = 50 ;
weightElem . setAttribute ( " value " , boldness ) ;
format . appendChild ( weightElem ) ;
}
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " font-style " ) ) // 3.10.19
if ( m_styleStack . attributeNS ( ooNS : : fo , " font-style " ) = = " italic " | |
m_styleStack . attributeNS ( ooNS : : fo , " font-style " ) = = " oblique " ) // no difference in kotext
{
TQDomElement italic = doc . createElement ( " ITALIC " ) ;
italic . setAttribute ( " value " , 1 ) ;
format . appendChild ( italic ) ;
}
bool wordByWord = ( m_styleStack . hasAttributeNS ( ooNS : : fo , " score-spaces " ) ) // 3.10.25
& & ( m_styleStack . attributeNS ( ooNS : : fo , " score-spaces " ) = = " false " ) ;
if ( m_styleStack . hasAttributeNS ( ooNS : : style , " text-crossing-out " ) ) // 3.10.6
{
TQString strikeOutType = m_styleStack . attributeNS ( ooNS : : style , " text-crossing-out " ) ;
TQDomElement strikeOut = doc . createElement ( " STRIKEOUT " ) ;
if ( strikeOutType = = " double-line " )
{
strikeOut . setAttribute ( " value " , " double " ) ;
strikeOut . setAttribute ( " styleline " , " solid " ) ;
}
else if ( strikeOutType = = " single-line " )
{
strikeOut . setAttribute ( " value " , " single " ) ;
strikeOut . setAttribute ( " styleline " , " solid " ) ;
}
else if ( strikeOutType = = " thick-line " )
{
strikeOut . setAttribute ( " value " , " single-bold " ) ;
strikeOut . setAttribute ( " styleline " , " solid " ) ;
}
if ( wordByWord )
strikeOut . setAttribute ( " wordbyword " , 1 ) ;
// not supported by KWord: "slash" and "X"
// not supported by OO: stylelines (solid, dash, dot, dashdot, dashdotdot)
format . appendChild ( strikeOut ) ;
}
if ( m_styleStack . hasAttributeNS ( ooNS : : style , " text-position " ) ) // 3.10.7
{
TQDomElement vertAlign = doc . createElement ( " VERTALIGN " ) ;
TQString text_position = m_styleStack . attributeNS ( ooNS : : style , " text-position " ) ;
TQString value ;
TQString relativetextsize ;
OoUtils : : importTextPosition ( text_position , value , relativetextsize ) ;
vertAlign . setAttribute ( " value " , value ) ;
if ( ! relativetextsize . isEmpty ( ) )
vertAlign . setAttribute ( " relativetextsize " , relativetextsize ) ;
format . appendChild ( vertAlign ) ;
}
if ( m_styleStack . hasAttributeNS ( ooNS : : style , " text-underline " ) ) // 3.10.22
{
TQString underline ;
TQString styleline ;
OoUtils : : importUnderline ( m_styleStack . attributeNS ( ooNS : : style , " text-underline " ) ,
underline , styleline ) ;
TQDomElement underLineElem = doc . createElement ( " UNDERLINE " ) ;
underLineElem . setAttribute ( " value " , underline ) ;
underLineElem . setAttribute ( " styleline " , styleline ) ;
TQString underLineColor = m_styleStack . attributeNS ( ooNS : : style , " text-underline-color " ) ; // 3.10.23
if ( ! underLineColor . isEmpty ( ) & & underLineColor ! = " font-color " )
underLineElem . setAttribute ( " underlinecolor " , underLineColor ) ;
if ( wordByWord )
underLineElem . setAttribute ( " wordbyword " , 1 ) ;
format . appendChild ( underLineElem ) ;
}
// Small caps, lowercase, uppercase
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " font-variant " ) // 3.10.1
| | m_styleStack . hasAttributeNS ( ooNS : : fo , " text-transform " ) ) // 3.10.2
{
TQDomElement fontAttrib ( doc . createElement ( " FONTATTRIBUTE " ) ) ;
bool smallCaps = m_styleStack . attributeNS ( ooNS : : fo , " font-variant " ) = = " small-caps " ;
if ( smallCaps )
{
fontAttrib . setAttribute ( " value " , " smallcaps " ) ;
} else
{
// Both KWord and OO use "uppercase" and "lowercase".
// TODO in KWord: "capitalize".
fontAttrib . setAttribute ( " value " , m_styleStack . attributeNS ( ooNS : : fo , " text-transform " ) ) ;
}
format . appendChild ( fontAttrib ) ;
}
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " language " ) ) // 3.10.17
{
TQDomElement lang = doc . createElement ( " LANGUAGE " ) ;
TQString tmp = m_styleStack . attributeNS ( ooNS : : fo , " language " ) ;
if ( tmp = = " en " )
lang . setAttribute ( " value " , " en_US " ) ;
else
lang . setAttribute ( " value " , tmp ) ;
format . appendChild ( lang ) ;
}
if ( m_styleStack . hasAttributeNS ( ooNS : : style , " text-background-color " ) ) // 3.10.28
{
TQDomElement bgCol = doc . createElement ( " TEXTBACKGROUNDCOLOR " ) ;
TQColor tmp = m_styleStack . attributeNS ( ooNS : : style , " text-background-color " ) ;
if ( tmp ! = " transparent " )
{
bgCol . setAttribute ( " red " , tmp . red ( ) ) ;
bgCol . setAttribute ( " green " , tmp . green ( ) ) ;
bgCol . setAttribute ( " blue " , tmp . blue ( ) ) ;
format . appendChild ( bgCol ) ;
}
}
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " text-shadow " ) ) // 3.10.21
{
TQDomElement shadow = doc . createElement ( " SHADOW " ) ;
TQString css = m_styleStack . attributeNS ( ooNS : : fo , " text-shadow " ) ;
// Workaround for OOo-1.1 bug: they forgot to save the color.
TQStringList tokens = TQStringList : : split ( ' ' , css ) ;
if ( ! tokens . isEmpty ( ) ) {
TQColor col ( tokens . first ( ) ) ;
if ( ! col . isValid ( ) & & tokens . count ( ) > 1 ) {
col . setNamedColor ( tokens . last ( ) ) ;
}
if ( ! col . isValid ( ) ) // no valid color found at either end -> append gray
css + = " gray " ;
}
shadow . setAttribute ( " text-shadow " , css ) ;
format . appendChild ( shadow ) ;
}
/*
Missing properties :
style : use - window - font - color , 3.10 .4 - this is what KWord uses by default ( fg color from the color style )
OO also switches to another color when necessary to avoid dark - on - dark and light - on - light cases .
( that is TODO in KWord )
style : text - outline , 3.10 .5 - not implemented in kotext
style : font - family - generic , 3.10 .10 - roman , swiss , modern - > map to a font ?
style : font - style - name , 3.10 .11 - can be ignored , says DV , the other ways to specify a font are more precise
style : font - pitch , 3.10 .12 - fixed or variable - > map to a font ?
style : font - charset , 3.10 .14 - not necessary with TQt
style : font - size - rel , 3.10 .15 - TODO in StyleStack : : fontSize ( )
fo : letter - spacing , 3.10 .16 - not implemented in kotext
style : text - relief , 3.10 .20 - not implemented in kotext
style : letter - kerning , 3.10 .20 - not implemented in kotext
style : text - blinking , 3.10 .27 - not implemented in kotext IIRC
style : text - combine , 3.10 .29 / 30 - not implemented , see http : //www.w3.org/TR/WD-i18n-format/
style : text - emphasis , 3.10 .31 - not implemented in kotext
style : text - scale , 3.10 .33 - not implemented in kotext
style : text - rotation - angle , 3.10 .34 - not implemented in kotext ( kpr rotates whole objects )
style : text - rotation - scale , 3.10 .35 - not implemented in kotext ( kpr rotates whole objects )
style : punctuation - wrap , 3.10 .36 - not implemented in kotext
*/
if ( format . hasChildNodes ( ) | | ! length /*hack for styles, they should always have a format*/ )
formats . appendChild ( format ) ;
}
void OoWriterImport : : writeLayout ( TQDomDocument & doc , TQDomElement & layoutElement )
{
Q_ASSERT ( layoutElement . ownerDocument ( ) = = doc ) ;
// Always write out the tqalignment, it's required
TQDomElement flowElement = doc . createElement ( " FLOW " ) ;
/* This was only an intermediate OASIS decision. The final decision is:
* fo : text - align can be " left " , " right " , " center " , " justify " , and
* " start " will mean direction - dependent . However if we use this right now ,
* OOo won ' t understand it . So that ' s for later , we keep our own attribute
* for now , so that export - import works .
*/
if ( m_styleStack . attributeNS ( ooNS : : style , " text-auto-align " ) = = " true " )
flowElement . setAttribute ( " align " , " auto " ) ;
else
{
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " text-align " ) ) // 3.11.4
flowElement . setAttribute ( " align " , Conversion : : importAlignment ( m_styleStack . attributeNS ( ooNS : : fo , " text-align " ) ) ) ;
else
flowElement . setAttribute ( " align " , " auto " ) ;
}
layoutElement . appendChild ( flowElement ) ;
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " writing-mode " ) ) // http://web4.w3.org/TR/xsl/slice7.html#writing-mode
{
// LTR is lr-tb. RTL is rl-tb
TQString writingMode = m_styleStack . attributeNS ( ooNS : : fo , " writing-mode " ) ;
flowElement . setAttribute ( " dir " , writingMode = = " rl-tb " | | writingMode = = " rl " ? " R " : " L " ) ;
}
// Indentation (margins)
OoUtils : : importIndents ( layoutElement , m_styleStack ) ;
// Offset before and after paragraph
OoUtils : : importTopBottomMargin ( layoutElement , m_styleStack ) ;
// Line spacing
OoUtils : : importLineSpacing ( layoutElement , m_styleStack ) ;
// Tabulators
OoUtils : : importTabulators ( layoutElement , m_styleStack ) ;
// Borders
OoUtils : : importBorders ( layoutElement , m_styleStack ) ;
// Page breaking. This isn't in OoUtils since it doesn't apply to KPresenter
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " break-before " ) | |
m_styleStack . hasAttributeNS ( ooNS : : fo , " break-after " ) | |
m_styleStack . hasAttributeNS ( ooNS : : style , " break-inside " ) | |
m_styleStack . hasAttributeNS ( ooNS : : style , " keep-with-next " ) | |
m_styleStack . hasAttributeNS ( ooNS : : fo , " keep-with-next " ) )
{
TQDomElement pageBreak = doc . createElement ( " PAGEBREAKING " ) ;
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " break-before " ) ) { // 3.11.24
bool breakBefore = m_styleStack . attributeNS ( ooNS : : fo , " break-before " ) ! = " auto " ;
// TODO in KWord: implement difference between "column" and "page"
pageBreak . setAttribute ( " hardFrameBreak " , breakBefore ? " true " : " false " ) ;
}
else if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " break-after " ) ) { // 3.11.24
bool breakAfter = m_styleStack . attributeNS ( ooNS : : fo , " break-after " ) ! = " auto " ;
// TODO in KWord: implement difference between "column" and "page"
pageBreak . setAttribute ( " hardFrameBreakAfter " , breakAfter ? " true " : " false " ) ;
}
if ( m_styleStack . hasAttributeNS ( ooNS : : style , " break-inside " ) ) { // 3.11.7
bool breakInside = m_styleStack . attributeNS ( ooNS : : style , " break-inside " ) = = " true " ;
pageBreak . setAttribute ( " linesTogether " , breakInside ? " false " : " true " ) ; // opposite meaning
}
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " keep-with-next " ) ) { // 3.11.31 (the doc said style:keep-with-next but DV said it's wrong)
// OASIS spec says it's "auto"/"always", not a boolean. Not sure which one OO uses.
TQString val = m_styleStack . attributeNS ( ooNS : : fo , " keep-with-next " ) ;
pageBreak . setAttribute ( " keepWithNext " , ( val = = " true " | | val = = " always " ) ? " true " : " false " ) ;
}
layoutElement . appendChild ( pageBreak ) ;
}
// TODO in KWord: padding
/* padding works together with the borders. The margins are around a
* paragraph , and the padding is the space between the border and the
* paragraph . In the OOo UI , you can only select padding when you have
* selected a border .
*
* There ' s some difference in conjunction with other features , in that the
* margin area is outside the paragraph , and the padding area is inside the
* paragraph . So if you set a paragraph background color , the padding area
* will be colored , but the margin area won ' t .
*/
/*
Paragraph properties not implemented in KWord :
style : text - align - last
style : justify - single - word
fo : background - color ( 3.11 .25 , bg color for a paragraph , unlike style : text - background - color )
style : background - image ( 3.11 .26 )
fo : widows
fo : orphans
fo : hyphenate
fo : hyphenation - keep
fo : hyphenation - remain - char - count
fo : hyphenation - push - char - count
fo : hyphenation - ladder - count
style : drop - cap
style : register - true
style : auto - text - indent
" page sequence entry point "
style : background - image
line numbering
punctuation wrap , 3.10 .36
vertical tqalignment - a bit like offsetfrombaseline ( but not for subscript / superscript , in general )
Michael said those are in fact parag properties :
style : text - autospace , 3.10 .32 - not implemented in kotext
style : line - break , 3.10 .37 - apparently that ' s for some Asian languages
*/
}
void OoWriterImport : : importFrame ( TQDomElement & frameElementOut , const TQDomElement & object , bool isText )
{
double width = 100 ;
if ( object . hasAttributeNS ( ooNS : : svg , " width " ) ) { // fixed width
// TODO handle percentage (of enclosing table/frame/page)
width = KoUnit : : parseValue ( object . attributeNS ( ooNS : : svg , " width " , TQString ( ) ) ) ;
} else if ( object . hasAttributeNS ( ooNS : : fo , " min-width " ) ) {
// min-width is not supported in KWord. Let's use it as a fixed width.
width = KoUnit : : parseValue ( object . attributeNS ( ooNS : : fo , " min-width " , TQString ( ) ) ) ;
} else {
kdWarning ( 30518 ) < < " Error in text-box: neither width nor min-width specified! " < < endl ;
}
double height = 100 ;
bool hasMinHeight = false ;
if ( object . hasAttributeNS ( ooNS : : svg , " height " ) ) { // fixed height
// TODO handle percentage (of enclosing table/frame/page)
height = KoUnit : : parseValue ( object . attributeNS ( ooNS : : svg , " height " , TQString ( ) ) ) ;
} else if ( object . hasAttributeNS ( ooNS : : fo , " min-height " ) ) {
height = KoUnit : : parseValue ( object . attributeNS ( ooNS : : fo , " min-height " , TQString ( ) ) ) ;
hasMinHeight = true ;
} else {
kdWarning ( 30518 ) < < " Error in text-box: neither height nor min-height specified! " < < endl ;
}
// draw:textarea-vertical-align, draw:textarea-horizontal-align
// Not supported in KWord: fo:max-height fo:max-width
// Anchor, Shadow (3.11.30), Columns
// #### horizontal-pos horizontal-rel vertical-pos vertical-rel anchor-type
// All the above changes the placement!
// See 3.8 (p199) for details.
// TODO draw:auto-grow-height draw:auto-grow-width - hmm? I thought min-height meant auto-grow-height...
KoRect frameRect ( KoUnit : : parseValue ( object . attributeNS ( ooNS : : svg , " x " , TQString ( ) ) ) ,
KoUnit : : parseValue ( object . attributeNS ( ooNS : : svg , " y " , TQString ( ) ) ) ,
width , height ) ;
frameElementOut . setAttribute ( " left " , frameRect . left ( ) ) ;
frameElementOut . setAttribute ( " right " , frameRect . right ( ) ) ;
frameElementOut . setAttribute ( " top " , frameRect . top ( ) ) ;
frameElementOut . setAttribute ( " bottom " , frameRect . bottom ( ) ) ;
if ( hasMinHeight )
frameElementOut . setAttribute ( " min-height " , height ) ;
frameElementOut . setAttribute ( " z-index " , object . attributeNS ( ooNS : : draw , " z-index " , TQString ( ) ) ) ;
TQPair < int , TQString > attribs = Conversion : : importWrapping ( m_styleStack . attributeNS ( ooNS : : style , " wrap " ) ) ;
frameElementOut . setAttribute ( " runaround " , attribs . first ) ;
if ( ! attribs . second . isEmpty ( ) )
frameElementOut . setAttribute ( " runaroundSide " , attribs . second ) ;
// ## runaroundGap is a problem. KWord-1.3 had one value, OO has 4 (margins on all sides, see p98).
// Fixed in KWord-post-1.3, it has 4 values now.
if ( isText ) {
int overflowBehavior ;
if ( m_styleStack . hasAttributeNS ( ooNS : : style , " overflow-behavior " ) ) { // OASIS extension
overflowBehavior = Conversion : : importOverflowBehavior ( m_styleStack . attributeNS ( ooNS : : style , " overflow-behavior " ) ) ;
} else {
// AutoCreateNewFrame not supported in OO-1.1. The presence of min-height tells if it's an auto-resized frame.
overflowBehavior = hasMinHeight ? 0 /*AutoExtendFrame*/ : 2 /*Ignore, i.e. fixed size*/ ;
}
// Not implemented in KWord: contour wrapping
frameElementOut . setAttribute ( " autoCreateNewFrame " , overflowBehavior ) ;
}
// TODO sheetSide (not implemented in KWord, but in its DTD)
importCommonFrameProperties ( frameElementOut ) ;
}
void OoWriterImport : : importCommonFrameProperties ( TQDomElement & frameElementOut )
{
// padding. fo:padding for 4 values or padding-left/right/top/bottom (3.11.29 p228)
double paddingLeft = KoUnit : : parseValue ( m_styleStack . attributeNS ( ooNS : : fo , " padding " , " left " ) ) ;
double paddingRight = KoUnit : : parseValue ( m_styleStack . attributeNS ( ooNS : : fo , " padding " , " right " ) ) ;
double paddingTop = KoUnit : : parseValue ( m_styleStack . attributeNS ( ooNS : : fo , " padding " , " top " ) ) ;
double paddingBottom = KoUnit : : parseValue ( m_styleStack . attributeNS ( ooNS : : fo , " padding " , " bottom " ) ) ;
if ( paddingLeft ! = 0 )
frameElementOut . setAttribute ( " bleftpt " , paddingLeft ) ;
if ( paddingRight ! = 0 )
frameElementOut . setAttribute ( " brightpt " , paddingRight ) ;
if ( paddingTop ! = 0 )
frameElementOut . setAttribute ( " btoppt " , paddingTop ) ;
if ( paddingBottom ! = 0 )
frameElementOut . setAttribute ( " bbottompt " , paddingBottom ) ;
// background color (3.11.25)
bool transparent = false ;
TQColor bgColor ;
if ( m_styleStack . hasAttributeNS ( ooNS : : fo , " background-color " ) ) {
TQString color = m_styleStack . attributeNS ( ooNS : : fo , " background-color " ) ;
if ( color = = " transparent " )
transparent = true ;
else
bgColor . setNamedColor ( color ) ;
}
if ( transparent )
frameElementOut . setAttribute ( " bkStyle " , 0 ) ;
else if ( bgColor . isValid ( ) ) {
// OOwriter doesn't support fill patterns (bkStyle).
// But the file support is more generic, and supports: draw:stroke, svg:stroke-color, draw:fill, draw:fill-color
frameElementOut . setAttribute ( " bkStyle " , 1 ) ;
frameElementOut . setAttribute ( " bkRed " , bgColor . red ( ) ) ;
frameElementOut . setAttribute ( " bkBlue " , bgColor . blue ( ) ) ;
frameElementOut . setAttribute ( " bkGreen " , bgColor . green ( ) ) ;
}
// borders (3.11.27)
// can be none/hidden, solid and double. General form is the XSL/FO "width|style|color"
{
double width ;
int style ;
TQColor color ;
if ( OoUtils : : parseBorder ( m_styleStack . attributeNS ( ooNS : : fo , " border " , " left " ) , & width , & style , & color ) ) {
frameElementOut . setAttribute ( " lWidth " , width ) ;
if ( color . isValid ( ) ) { // should be always true, but who knows
frameElementOut . setAttribute ( " lRed " , color . red ( ) ) ;
frameElementOut . setAttribute ( " lBlue " , color . blue ( ) ) ;
frameElementOut . setAttribute ( " lGreen " , color . green ( ) ) ;
}
frameElementOut . setAttribute ( " lStyle " , style ) ;
}
if ( OoUtils : : parseBorder ( m_styleStack . attributeNS ( ooNS : : fo , " border " , " right " ) , & width , & style , & color ) ) {
frameElementOut . setAttribute ( " rWidth " , width ) ;
if ( color . isValid ( ) ) { // should be always true, but who knows
frameElementOut . setAttribute ( " rRed " , color . red ( ) ) ;
frameElementOut . setAttribute ( " rBlue " , color . blue ( ) ) ;
frameElementOut . setAttribute ( " rGreen " , color . green ( ) ) ;
}
frameElementOut . setAttribute ( " rStyle " , style ) ;
}
if ( OoUtils : : parseBorder ( m_styleStack . attributeNS ( ooNS : : fo , " border " , " top " ) , & width , & style , & color ) ) {
frameElementOut . setAttribute ( " tWidth " , width ) ;
if ( color . isValid ( ) ) { // should be always true, but who knows
frameElementOut . setAttribute ( " tRed " , color . red ( ) ) ;
frameElementOut . setAttribute ( " tBlue " , color . blue ( ) ) ;
frameElementOut . setAttribute ( " tGreen " , color . green ( ) ) ;
}
frameElementOut . setAttribute ( " tStyle " , style ) ;
}
if ( OoUtils : : parseBorder ( m_styleStack . attributeNS ( ooNS : : fo , " border " , " bottom " ) , & width , & style , & color ) ) {
frameElementOut . setAttribute ( " bWidth " , width ) ;
if ( color . isValid ( ) ) { // should be always true, but who knows
frameElementOut . setAttribute ( " bRed " , color . red ( ) ) ;
frameElementOut . setAttribute ( " bBlue " , color . blue ( ) ) ;
frameElementOut . setAttribute ( " bGreen " , color . green ( ) ) ;
}
frameElementOut . setAttribute ( " bStyle " , style ) ;
}
}
// TODO more refined border spec for double borders (3.11.28)
}
TQString OoWriterImport : : appendTextBox ( TQDomDocument & doc , const TQDomElement & object )
{
const TQString frameName ( object . attributeNS ( ooNS : : draw , " name " , TQString ( ) ) ) ; // ### TODO: what if empty, i.e. non-unique
kdDebug ( 30518 ) < < " appendTextBox " < < frameName < < endl ;
m_styleStack . save ( ) ;
fillStyleStack ( object , ooNS : : draw , " style-name " ) ; // get the style for the graphics element
// Create KWord frameset
TQDomElement framesetElement ( doc . createElement ( " FRAMESET " ) ) ;
framesetElement . setAttribute ( " frameType " , 1 ) ;
framesetElement . setAttribute ( " frameInfo " , 0 ) ;
framesetElement . setAttribute ( " visible " , 1 ) ;
framesetElement . setAttribute ( " name " , frameName ) ;
TQDomElement framesetsPluralElement ( doc . documentElement ( ) . namedItem ( " FRAMESETS " ) . toElement ( ) ) ;
framesetsPluralElement . appendChild ( framesetElement ) ;
TQDomElement frameElementOut ( doc . createElement ( " FRAME " ) ) ;
framesetElement . appendChild ( frameElementOut ) ;
importFrame ( frameElementOut , object , true /*text*/ ) ;
// TODO editable
// We're done with the graphics style
m_styleStack . restore ( ) ;
// Obey draw:text-style-name
if ( m_styleStack . hasAttributeNS ( ooNS : : draw , " text-style-name " ) )
addStyles ( m_styles [ m_styleStack . attributeNS ( ooNS : : draw , " text-style-name " ) ] ) ;
// Parse contents
parseBodyOrSimilar ( doc , object , framesetElement ) ;
return frameName ;
}
// OOo SPEC: 3.6.3 p149
void OoWriterImport : : importFootnote ( TQDomDocument & doc , const TQDomElement & object , TQDomElement & formats , uint pos , const TQString & localName )
{
const TQString frameName ( object . attributeNS ( ooNS : : text , " id " , TQString ( ) ) ) ;
TQDomElement citationElem = KoDom : : namedItemNS ( object , ooNS : : text , ( localName + " -citation " ) . latin1 ( ) ) . toElement ( ) ;
bool endnote = localName = = " endnote " ;
TQString label = citationElem . attributeNS ( ooNS : : text , " label " , TQString ( ) ) ;
bool autoNumbered = label . isEmpty ( ) ;
// The var
TQDomElement footnoteElem = doc . createElement ( " FOOTNOTE " ) ;
if ( autoNumbered )
footnoteElem . setAttribute ( " value " , 1 ) ; // KWord will renumber anyway
else
footnoteElem . setAttribute ( " value " , label ) ;
footnoteElem . setAttribute ( " notetype " , endnote ? " endnote " : " footnote " ) ;
footnoteElem . setAttribute ( " numberingtype " , autoNumbered ? " auto " : " manual " ) ;
footnoteElem . setAttribute ( " frameset " , frameName ) ;
appendKWordVariable ( doc , formats , citationElem , pos , " STRI " , 11 /*KWord code for footnotes*/ , footnoteElem ) ;
// The frameset
TQDomElement framesetElement ( doc . createElement ( " FRAMESET " ) ) ;
framesetElement . setAttribute ( " frameType " , 1 /* text */ ) ;
framesetElement . setAttribute ( " frameInfo " , 7 /* footnote/endnote */ ) ;
framesetElement . setAttribute ( " name " , frameName ) ;
TQDomElement framesetsPluralElement ( doc . documentElement ( ) . namedItem ( " FRAMESETS " ) . toElement ( ) ) ;
framesetsPluralElement . appendChild ( framesetElement ) ;
createInitialFrame ( framesetElement , 29 , 798 , 567 , 567 + 41 , true , NoFollowup ) ;
// TODO importCommonFrameProperties ?
// The text inside the frameset
TQDomElement bodyElem = KoDom : : namedItemNS ( object , ooNS : : text , ( localName + " -body " ) . latin1 ( ) ) . toElement ( ) ;
parseBodyOrSimilar ( doc , bodyElem , framesetElement ) ;
}
TQString OoWriterImport : : appendPicture ( TQDomDocument & doc , const TQDomElement & object )
{
const TQString frameName ( object . attributeNS ( ooNS : : draw , " name " , TQString ( ) ) ) ; // ### TODO: what if empty, i.e. non-unique
const TQString href ( object . attributeNS ( ooNS : : xlink , " href " , TQString ( ) ) ) ;
kdDebug ( 30518 ) < < " Picture: " < < frameName < < " " < < href < < " (in OoWriterImport::appendPicture) " < < endl ;
KoPicture picture ;
if ( href [ 0 ] = = ' # ' )
{
TQString strExtension ;
const int result = href . findRev ( " . " ) ;
if ( result > = 0 )
{
strExtension = href . mid ( result + 1 ) ; // As we are using KoPicture, the extension should be without the dot.
}
TQString filename ( href . mid ( 1 ) ) ;
KoPictureKey key ( filename , TQDateTime : : tqcurrentDateTime ( Qt : : UTC ) ) ;
picture . setKey ( key ) ;
if ( ! m_zip )
return frameName ; // Should not happen
const KArchiveEntry * entry = m_zip - > directory ( ) - > entry ( filename ) ;
if ( ! entry )
{
kdWarning ( 30518 ) < < " Picture " < < filename < < " not found! " < < endl ;
return frameName ;
}
if ( entry - > isDirectory ( ) )
{
kdWarning ( 30518 ) < < " Picture " < < filename < < " is a directory! " < < endl ;
return frameName ;
}
const KZipFileEntry * f = static_cast < const KZipFileEntry * > ( entry ) ;
TQIODevice * io = f - > device ( ) ;
kdDebug ( 30518 ) < < " Picture " < < filename < < " has size " < < f - > size ( ) < < endl ;
if ( ! io )
{
kdWarning ( 30518 ) < < " No TQIODevice for picture " < < frameName < < " " < < href < < endl ;
return frameName ;
}
if ( ! picture . load ( io , strExtension ) )
kdWarning ( 30518 ) < < " Cannot load picture: " < < frameName < < " " < < href < < endl ;
}
else
{
KURL url ;
url . setPath ( href ) ; // ### TODO: is this really right?
picture . setKeyAndDownloadPicture ( url , 0 ) ; // ### TODO: find a better tqparent if possible
}
kdDebug ( 30518 ) < < " Picture ready! Key: " < < picture . getKey ( ) . toString ( ) < < " Size: " < < picture . getOriginalSize ( ) < < endl ;
TQString strStoreName ;
strStoreName = " pictures/picture " ;
strStoreName + = TQString : : number ( + + m_pictureNumber ) ;
strStoreName + = ' . ' ;
strStoreName + = picture . getExtension ( ) ;
kdDebug ( 30518 ) < < " Storage name: " < < strStoreName < < endl ;
KoStoreDevice * out = m_chain - > storageFile ( strStoreName , KoStore : : Write ) ;
if ( out )
{
if ( ! out - > open ( IO_WriteOnly ) )
{
kdWarning ( 30518 ) < < " Cannot open for saving picture: " < < frameName < < " " < < href < < endl ;
return frameName ;
}
if ( ! picture . save ( out ) )
kdWarning ( 30518 ) < < " Cannot save picture: " < < frameName < < " " < < href < < endl ;
out - > close ( ) ;
}
else
{
kdWarning ( 30518 ) < < " Cannot store picture: " < < frameName < < " " < < href < < endl ;
return frameName ;
}
// Now that we have copied the image, we need to make some bookkeeping
TQDomElement docElement ( doc . documentElement ( ) ) ;
TQDomElement framesetsPluralElement ( docElement . namedItem ( " FRAMESETS " ) . toElement ( ) ) ;
TQDomElement framesetElement = doc . createElement ( " FRAMESET " ) ;
framesetElement . setAttribute ( " frameType " , 2 ) ;
framesetElement . setAttribute ( " frameInfo " , 0 ) ;
framesetElement . setAttribute ( " visible " , 1 ) ;
framesetElement . setAttribute ( " name " , frameName ) ;
framesetsPluralElement . appendChild ( framesetElement ) ;
TQDomElement frameElementOut = doc . createElement ( " FRAME " ) ;
framesetElement . appendChild ( frameElementOut ) ;
m_styleStack . save ( ) ;
fillStyleStack ( object , ooNS : : draw , " style-name " ) ; // get the style for the graphics element
importFrame ( frameElementOut , object , false /*not text*/ ) ;
m_styleStack . restore ( ) ;
TQDomElement element = doc . createElement ( " PICTURE " ) ;
element . setAttribute ( " keepAspectRatio " , " true " ) ;
framesetElement . setAttribute ( " frameType " , 2 ) ; // Picture
framesetElement . appendChild ( element ) ;
TQDomElement singleKey ( doc . createElement ( " KEY " ) ) ;
picture . getKey ( ) . saveAttributes ( singleKey ) ;
element . appendChild ( singleKey ) ;
TQDomElement picturesPluralElement ( docElement . namedItem ( " PICTURES " ) . toElement ( ) ) ;
if ( picturesPluralElement . isNull ( ) )
{
// We do not yet have any <PICTURES> element, so we must create it
picturesPluralElement = doc . createElement ( " PICTURES " ) ;
docElement . appendChild ( picturesPluralElement ) ;
}
TQDomElement pluralKey ( doc . createElement ( " KEY " ) ) ;
picture . getKey ( ) . saveAttributes ( pluralKey ) ;
pluralKey . setAttribute ( " name " , strStoreName ) ;
picturesPluralElement . appendChild ( pluralKey ) ;
return frameName ;
}
void OoWriterImport : : anchorFrameset ( TQDomDocument & doc , TQDomElement & formats , uint pos , const TQString & frameSetName )
{
TQDomElement formatElementOut = doc . createElement ( " FORMAT " ) ;
formatElementOut . setAttribute ( " id " , 6 ) ; // Floating frame
formatElementOut . setAttribute ( " pos " , pos ) ; // Start position
formatElementOut . setAttribute ( " len " , 1 ) ; // Start position
formats . appendChild ( formatElementOut ) ; //Append to <FORMATS>
TQDomElement anchor = doc . createElement ( " ANCHOR " ) ;
// No name attribute!
anchor . setAttribute ( " type " , " frameset " ) ;
anchor . setAttribute ( " instance " , frameSetName ) ;
formatElementOut . appendChild ( anchor ) ;
}
void OoWriterImport : : appendField ( TQDomDocument & doc , TQDomElement & outputFormats , TQDomElement & object , uint pos )
// Note: TQDomElement& outputFormats replaces the parameter TQDomElement& e in OoImpressImport::appendField
// (otherwise it should be the same parameters.)
{
const TQString localName ( object . localName ( ) ) ;
//kdDebug(30518) << localName << endl;
int subtype = - 1 ;
if ( localName . endsWith ( " date " ) | | localName . endsWith ( " time " ) )
{
TQString dataStyleName = object . attributeNS ( ooNS : : style , " data-style-name " , TQString ( ) ) ;
TQString dateFormat = " locale " ;
DataFormatsMap : : const_iterator it = m_dateTimeFormats . find ( dataStyleName ) ;
if ( it ! = m_dateTimeFormats . end ( ) )
dateFormat = ( * it ) ;
if ( localName = = " date " )
{
subtype = 1 ; // current (or fixed) date
// Standard form of the date is in date-value. Example: 2004-01-21T10:57:05
TQDateTime dt ( TQDate : : fromString ( object . attributeNS ( ooNS : : text , " date-value " , TQString ( ) ) , Qt : : ISODate ) ) ;
bool fixed = ( object . hasAttributeNS ( ooNS : : text , " fixed " ) & & object . attributeNS ( ooNS : : text , " fixed " , TQString ( ) ) = = " true " ) ;
if ( ! dt . isValid ( ) )
{
dt = TQDateTime : : tqcurrentDateTime ( ) ; // OOo docs say so :)
fixed = false ;
}
const TQDate date ( dt . date ( ) ) ;
const TQTime time ( dt . time ( ) ) ;
if ( fixed )
subtype = 0 ;
TQDomElement dateElement ( doc . createElement ( " DATE " ) ) ;
dateElement . setAttribute ( " fix " , fixed ? 1 : 0 ) ;
dateElement . setAttribute ( " subtype " , subtype ) ;
dateElement . setAttribute ( " day " , date . day ( ) ) ;
dateElement . setAttribute ( " month " , date . month ( ) ) ;
dateElement . setAttribute ( " year " , date . year ( ) ) ;
dateElement . setAttribute ( " hour " , time . hour ( ) ) ;
dateElement . setAttribute ( " minute " , time . minute ( ) ) ;
dateElement . setAttribute ( " second " , time . second ( ) ) ;
if ( object . hasAttributeNS ( ooNS : : text , " date-adjust " ) )
dateElement . setAttribute ( " correct " , object . attributeNS ( ooNS : : text , " date-adjust " , TQString ( ) ) ) ;
appendKWordVariable ( doc , outputFormats , object , pos , " DATE " + dateFormat , 0 , dateElement ) ;
}
else if ( localName = = " time " )
{
// Use TQDateTime to work around a possible problem of TQTime::FromString in TQt 3.2.2
TQDateTime dt ( TQDateTime : : fromString ( object . attributeNS ( ooNS : : text , " time-value " , TQString ( ) ) , Qt : : ISODate ) ) ;
bool fixed = ( object . hasAttributeNS ( ooNS : : text , " fixed " ) & & object . attributeNS ( ooNS : : text , " fixed " , TQString ( ) ) = = " true " ) ;
if ( ! dt . isValid ( ) ) {
dt = TQDateTime : : tqcurrentDateTime ( ) ; // OOo docs say so :)
fixed = false ;
}
const TQTime time ( dt . time ( ) ) ;
TQDomElement timeElement ( doc . createElement ( " TIME " ) ) ;
timeElement . setAttribute ( " fix " , fixed ? 1 : 0 ) ;
timeElement . setAttribute ( " hour " , time . hour ( ) ) ;
timeElement . setAttribute ( " minute " , time . minute ( ) ) ;
timeElement . setAttribute ( " second " , time . second ( ) ) ;
/*if (object.hasAttributeNS( ooNS::text, "time-adjust"))
timeElem . setAttribute ( " correct " , object . attributeNS ( ooNS : : text , " time-adjust " , TQString ( ) ) ) ; */ // ### TODO
appendKWordVariable ( doc , outputFormats , object , pos , " TIME " + dateFormat , 2 , timeElement ) ;
}
else if ( localName = = " print-time "
| | localName = = " print-date "
| | localName = = " creation-time "
| | localName = = " creation-date "
| | localName = = " modification-time "
| | localName = = " modification-date " )
{
if ( localName . startsWith ( " print " ) )
subtype = 2 ;
else if ( localName . startsWith ( " creation " ) )
subtype = 3 ;
else if ( localName . startsWith ( " modification " ) )
subtype = 4 ;
// We do NOT include the date value here. It will be retrieved from
// meta.xml
TQDomElement dateElement ( doc . createElement ( " DATE " ) ) ;
dateElement . setAttribute ( " subtype " , subtype ) ;
if ( object . hasAttributeNS ( ooNS : : text , " date-adjust " ) )
dateElement . setAttribute ( " correct " , object . attributeNS ( ooNS : : text , " date-adjust " , TQString ( ) ) ) ;
appendKWordVariable ( doc , outputFormats , object , pos , " DATE " + dateFormat , 0 , dateElement ) ;
}
} // end of date/time variables
else if ( localName = = " page-number " )
{
subtype = 0 ; // VST_PGNUM_CURRENT
if ( object . hasAttributeNS ( ooNS : : text , " select-page " ) )
{
const TQString select = object . attributeNS ( ooNS : : text , " select-page " , TQString ( ) ) ;
if ( select = = " previous " )
subtype = 3 ; // VST_PGNUM_PREVIOUS
else if ( select = = " next " )
subtype = 4 ; // VST_PGNUM_NEXT
}
TQDomElement pgnumElement ( doc . createElement ( " PGNUM " ) ) ;
pgnumElement . setAttribute ( " subtype " , subtype ) ;
pgnumElement . setAttribute ( " value " , object . text ( ) ) ;
appendKWordVariable ( doc , outputFormats , object , pos , " NUMBER " , 4 , pgnumElement ) ;
}
else if ( localName = = " chapter " )
{
const TQString display = object . attributeNS ( ooNS : : text , " display " , TQString ( ) ) ;
// display can be name, number, number-and-name, plain-number-and-name, plain-number
TQDomElement pgnumElement ( doc . createElement ( " PGNUM " ) ) ;
pgnumElement . setAttribute ( " subtype " , 2 ) ; // VST_CURRENT_SECTION
pgnumElement . setAttribute ( " value " , object . text ( ) ) ;
appendKWordVariable ( doc , outputFormats , object , pos , " STRING " , 4 , pgnumElement ) ;
}
else if ( localName = = " file-name " )
{
subtype = 5 ;
if ( object . hasAttributeNS ( ooNS : : text , " display " ) )
{
const TQString display = object . attributeNS ( ooNS : : text , " display " , TQString ( ) ) ;
if ( display = = " path " )
subtype = 1 ; // VST_DIRECTORYNAME
else if ( display = = " name " )
subtype = 6 ; // VST_FILENAMEWITHOUTEXTENSION
else if ( display = = " name-and-extension " )
subtype = 0 ; // VST_FILENAME
else
subtype = 5 ; // VST_PATHFILENAME
}
TQDomElement fieldElement ( doc . createElement ( " FIELD " ) ) ;
fieldElement . setAttribute ( " subtype " , subtype ) ;
fieldElement . setAttribute ( " value " , object . text ( ) ) ;
appendKWordVariable ( doc , outputFormats , object , pos , " STRING " , 8 , fieldElement ) ;
}
else if ( localName = = " author-name "
| | localName = = " author-initials "
| | localName = = " subject "
| | localName = = " title "
| | localName = = " description "
)
{
subtype = 2 ; // VST_AUTHORNAME
if ( localName = = " author-initials " )
subtype = 16 ; // VST_INITIAL
else if ( localName = = " subject " ) // TODO in kword
subtype = 10 ; // title
else if ( localName = = " title " )
subtype = 10 ;
else if ( localName = = " description " )
subtype = 11 ; // Abstract
TQDomElement authorElem = doc . createElement ( " FIELD " ) ;
authorElem . setAttribute ( " subtype " , subtype ) ;
authorElem . setAttribute ( " value " , object . text ( ) ) ;
appendKWordVariable ( doc , outputFormats , object , pos , " STRING " , 8 , authorElem ) ;
}
else if ( localName . startsWith ( " sender- " ) )
{
int subtype = - 1 ;
const TQCString afterText ( localName . latin1 ( ) + 5 ) ;
if ( afterText = = " sender-company " )
subtype = 4 ; //VST_COMPANYNAME;
else if ( afterText = = " sender-firstname " )
; // ## This is different from author-name, but the notion of 'sender' is unclear...
else if ( afterText = = " sender-lastname " )
; // ## This is different from author-name, but the notion of 'sender' is unclear...
else if ( afterText = = " sender-initials " )
; // ## This is different from author-initials, but the notion of 'sender' is unclear...
else if ( afterText = = " sender-street " )
subtype = 14 ; // VST_STREET;
else if ( afterText = = " sender-country " )
subtype = 9 ; // VST_COUNTRY;
else if ( afterText = = " sender-postal-code " )
subtype = 12 ; //VST_POSTAL_CODE;
else if ( afterText = = " sender-city " )
subtype = 13 ; // VST_CITY;
else if ( afterText = = " sender-title " )
subtype = 15 ; // VST_AUTHORTITLE; // Small hack (it's supposed to be about the sender, not about the author)
else if ( afterText = = " sender-position " )
subtype = 15 ; // VST_AUTHORTITLE; // TODO separate variable
else if ( afterText = = " sender-phone-private " )
subtype = 7 ; // VST_TELEPHONE;
else if ( afterText = = " sender-phone-work " )
subtype = 7 ; // VST_TELEPHONE; // ### TODO separate type
else if ( afterText = = " sender-fax " )
subtype = 8 ; // VST_FAX;
else if ( afterText = = " sender-email " )
subtype = 3 ; // VST_EMAIL;
if ( subtype ! = - 1 )
{
TQDomElement fieldElem = doc . createElement ( " FIELD " ) ;
fieldElem . setAttribute ( " subtype " , subtype ) ;
fieldElem . setAttribute ( " value " , object . text ( ) ) ;
appendKWordVariable ( doc , outputFormats , object , pos , " STRING " , 8 , fieldElem ) ;
}
}
else if ( localName = = " variable-set "
| | localName = = " user-defined " )
{
// We treat both the same. For OO the difference is that
// - variable-set is related to variable-decls (defined in <body>);
// its value can change in the middle of the document.
// - user-defined is related to meta::user-defined in meta.xml
TQDomElement customElem = doc . createElement ( " CUSTOM " ) ;
customElem . setAttribute ( " name " , object . attributeNS ( ooNS : : text , " name " , TQString ( ) ) ) ;
customElem . setAttribute ( " value " , object . text ( ) ) ;
appendKWordVariable ( doc , outputFormats , object , pos , " STRING " , 6 , customElem ) ;
}
else
{
kdWarning ( 30518 ) < < " Unsupported field " < < localName < < endl ;
}
// TODO localName == "page-variable-get", "initial-creator" and many more
}
void OoWriterImport : : appendKWordVariable ( TQDomDocument & doc , TQDomElement & formats , const TQDomElement & object , uint pos ,
const TQString & key , int type , TQDomElement & child )
{
TQDomElement variableElement ( doc . createElement ( " VARIABLE " ) ) ;
TQDomElement typeElement ( doc . createElement ( " TYPE " ) ) ;
typeElement . setAttribute ( " key " , key ) ;
typeElement . setAttribute ( " type " , type ) ;
typeElement . setAttribute ( " text " , object . text ( ) ) ;
variableElement . appendChild ( typeElement ) ; //Append to <VARIABLE>
variableElement . appendChild ( child ) ; //Append to <VARIABLE>
TQDomElement formatElement ( doc . createElement ( " FORMAT " ) ) ;
formatElement . setAttribute ( " id " , 4 ) ; // Variable
formatElement . setAttribute ( " pos " , pos ) ; // Start position
formatElement . setAttribute ( " len " , 1 ) ;
formatElement . appendChild ( variableElement ) ;
formats . appendChild ( formatElement ) ;
}
void OoWriterImport : : parseTable ( TQDomDocument & doc , const TQDomElement & tqparent , TQDomElement & currentFramesetElement )
{
TQString tableName ( tqparent . attributeNS ( ooNS : : table , " name " , TQString ( ) ) ) ; // TODO: what if empty (non-unique?)
kdDebug ( 30518 ) < < " Found table " < < tableName < < endl ;
// In OOWriter a table is never inside a paragraph, in KWord it is always in a paragraph
TQDomElement paragraphElementOut ( doc . createElement ( " PARAGRAPH " ) ) ;
currentFramesetElement . appendChild ( paragraphElementOut ) ;
TQDomElement textElementOut ( doc . createElement ( " TEXT " ) ) ;
textElementOut . appendChild ( doc . createTextNode ( " # " ) ) ;
paragraphElementOut . appendChild ( textElementOut ) ;
TQDomElement formatsPluralElementOut ( doc . createElement ( " FORMATS " ) ) ;
paragraphElementOut . appendChild ( formatsPluralElementOut ) ;
TQDomElement elementFormat ( doc . createElement ( " FORMAT " ) ) ;
elementFormat . setAttribute ( " id " , 6 ) ;
elementFormat . setAttribute ( " pos " , 0 ) ;
elementFormat . setAttribute ( " len " , 1 ) ;
formatsPluralElementOut . appendChild ( elementFormat ) ;
// ### FIXME: we have no <LAYOUT> element!
TQDomElement elementAnchor ( doc . createElement ( " ANCHOR " ) ) ;
elementAnchor . setAttribute ( " type " , " frameset " ) ;
elementAnchor . setAttribute ( " instance " , tableName ) ;
elementFormat . appendChild ( elementAnchor ) ;
// Left position of the cell/column (similar to RTF's \cellx). The last one defined is the right position of the last cell/column
TQMemArray < double > columnLefts ( 4 ) ;
uint maxColumns = columnLefts . size ( ) - 1 ;
uint col = 0 ;
columnLefts [ 0 ] = 0.0 ; // Initialize left of first cell
TQDomElement elem ;
forEachElement ( elem , tqparent )
{
if ( elem . localName ( ) = = " table-column " & & elem . namespaceURI ( ) = = ooNS : : table )
{
uint repeat = elem . attributeNS ( ooNS : : table , " number-columns-repeated " , " 1 " ) . toUInt ( ) ; // Default 1 time
if ( ! repeat )
repeat = 1 ; // At least one column defined!
const TQString styleName ( elem . attributeNS ( ooNS : : table , " style-name " , TQString ( ) ) ) ;
kdDebug ( 30518 ) < < " Column " < < col < < " style " < < styleName < < endl ;
const TQDomElement * style = m_styles . find ( styleName ) ;
double width = 0.0 ;
if ( style )
{
const TQDomElement elemProps ( KoDom : : namedItemNS ( * style , ooNS : : style , " properties " ) ) ;
if ( elemProps . isNull ( ) )
{
kdWarning ( 30518 ) < < " Could not find table column style properties! " < < endl ;
}
const TQString strWidth ( elemProps . attributeNS ( ooNS : : style , " column-width " , TQString ( ) ) ) ;
kdDebug ( 30518 ) < < " - raw style width " < < strWidth < < endl ;
width = KoUnit : : parseValue ( strWidth ) ;
}
else
kdWarning ( 30518 ) < < " Could not find table column style! " < < endl ;
if ( width < 1.0 ) // Something is wrong with the width
{
kdWarning ( 30518 ) < < " Table column width ridiculous, assuming 1 inch! " < < endl ;
width = 72.0 ;
}
else
kdDebug ( 30518 ) < < " - style width " < < width < < endl ;
for ( uint j = 0 ; j < repeat ; j + + )
{
+ + col ;
if ( col > = maxColumns )
{
// We need more columns
maxColumns + = 4 ;
columnLefts . resize ( maxColumns + 1 , TQGArray : : SpeedOptim ) ;
}
columnLefts . at ( col ) = width + columnLefts . at ( col - 1 ) ;
kdDebug ( 30518 ) < < " Cell column " < < col - 1 < < " left " < < columnLefts . at ( col - 1 ) < < " right " < < columnLefts . at ( col ) < < endl ;
}
}
}
uint row = 0 ;
uint column = 0 ;
parseInsideOfTable ( doc , tqparent , currentFramesetElement , tableName , columnLefts , row , column ) ;
}
void OoWriterImport : : parseInsideOfTable ( TQDomDocument & doc , const TQDomElement & tqparent , TQDomElement & currentFramesetElement ,
const TQString & tableName , const TQMemArray < double > & columnLefts , uint & row , uint & column )
{
kdDebug ( 30518 ) < < " parseInsideOfTable: columnLefts.size()= " < < columnLefts . size ( ) < < endl ;
TQDomElement framesetsPluralElement ( doc . documentElement ( ) . namedItem ( " FRAMESETS " ) . toElement ( ) ) ;
if ( framesetsPluralElement . isNull ( ) )
{
kdError ( 30518 ) < < " Cannot find KWord's <FRAMESETS>! Cannot process table! " < < endl ;
return ;
}
TQDomElement e ;
forEachElement ( e , tqparent )
{
m_styleStack . save ( ) ;
const TQString localName = e . localName ( ) ;
const TQString ns = e . namespaceURI ( ) ;
if ( ns ! = ooNS : : table ) {
kdWarning ( 30518 ) < < " Skipping element " < < e . tagName ( ) < < " (in OoWriterImport::parseInsideOfTable) " < < endl ;
continue ;
}
if ( localName = = " table-cell " ) // OOo SPEC 4.8.1 p267
{
const TQString frameName ( i18n ( " Frameset name " , " Table %3, row %1, column %2 " )
. tqarg ( row ) . tqarg ( column ) . tqarg ( tableName ) ) ; // The table name could have a % sequence, so use the table name as last!
kdDebug ( 30518 ) < < " Trying to create " < < frameName < < endl ;
// We need to create a frameset for the cell
TQDomElement framesetElement ( doc . createElement ( " FRAMESET " ) ) ;
framesetElement . setAttribute ( " frameType " , 1 ) ;
framesetElement . setAttribute ( " frameInfo " , 0 ) ;
framesetElement . setAttribute ( " visible " , 1 ) ;
framesetElement . setAttribute ( " name " , frameName ) ;
framesetElement . setAttribute ( " row " , row ) ;
framesetElement . setAttribute ( " col " , column ) ;
int rowSpan = e . attributeNS ( ooNS : : table , " number-rows-spanned " , TQString ( ) ) . toInt ( ) ;
framesetElement . setAttribute ( " rows " , rowSpan = = 0 ? 1 : rowSpan ) ;
int colSpan = e . attributeNS ( ooNS : : table , " number-columns-spanned " , TQString ( ) ) . toInt ( ) ;
framesetElement . setAttribute ( " cols " , colSpan = = 0 ? 1 : colSpan ) ;
framesetElement . setAttribute ( " grpMgr " , tableName ) ;
framesetsPluralElement . appendChild ( framesetElement ) ;
TQDomElement frameElementOut ( doc . createElement ( " FRAME " ) ) ;
frameElementOut . setAttribute ( " left " , columnLefts . at ( column ) ) ;
frameElementOut . setAttribute ( " right " , columnLefts . at ( column + 1 ) ) ;
frameElementOut . setAttribute ( " top " , 0 ) ;
frameElementOut . setAttribute ( " bottom " , 0 ) ;
frameElementOut . setAttribute ( " runaround " , 1 ) ;
frameElementOut . setAttribute ( " autoCreateNewFrame " , 0 ) ; // Very important for cell growing!
// ### TODO: a few attributes are missing
m_styleStack . save ( ) ;
fillStyleStack ( e , ooNS : : table , " style-name " ) ; // get the style for the graphics element
importCommonFrameProperties ( frameElementOut ) ;
m_styleStack . restore ( ) ;
framesetElement . appendChild ( frameElementOut ) ;
parseBodyOrSimilar ( doc , e , framesetElement ) ; // We change the frameset!
column + + ;
}
else if ( localName = = " covered-table-cell " )
{
column + + ;
}
else if ( localName = = " table-row " )
{
column = 0 ;
parseInsideOfTable ( doc , e , currentFramesetElement , tableName , columnLefts , row , column ) ;
row + + ;
}
else if ( localName = = " table-header-rows " ) // Provisory (###TODO)
{
parseInsideOfTable ( doc , e , currentFramesetElement , tableName , columnLefts , row , column ) ;
}
else if ( localName = = " table-column " )
{
// Allready treated in OoWriterImport::parseTable, we do not need to do anything here!
}
// TODO sub-table
else
{
kdWarning ( 30518 ) < < " Skipping element " < < localName < < " (in OoWriterImport::parseInsideOfTable) " < < endl ;
}
m_styleStack . restore ( ) ;
}
}
void OoWriterImport : : appendBookmark ( TQDomDocument & doc , int paragId , int pos , const TQString & name )
{
appendBookmark ( doc , paragId , pos , paragId , pos , name ) ;
}
void OoWriterImport : : appendBookmark ( TQDomDocument & doc , int paragId , int pos , int endParagId , int endPos , const TQString & name )
{
Q_ASSERT ( ! m_currentFrameset . isNull ( ) ) ;
const TQString frameSetName = m_currentFrameset . attribute ( " name " ) ;
Q_ASSERT ( ! frameSetName . isEmpty ( ) ) ;
TQDomElement bookmarks = doc . documentElement ( ) . namedItem ( " BOOKMARKS " ) . toElement ( ) ;
if ( bookmarks . isNull ( ) ) {
bookmarks = doc . createElement ( " BOOKMARKS " ) ;
doc . documentElement ( ) . appendChild ( bookmarks ) ;
}
TQDomElement bkItem = doc . createElement ( " BOOKMARKITEM " ) ;
bkItem . setAttribute ( " name " , name ) ;
bkItem . setAttribute ( " frameset " , frameSetName ) ;
bkItem . setAttribute ( " startparag " , paragId ) ;
bkItem . setAttribute ( " cursorIndexStart " , pos ) ;
bkItem . setAttribute ( " endparag " , endParagId ) ;
bkItem . setAttribute ( " cursorIndexEnd " , endPos ) ;
bookmarks . appendChild ( bkItem ) ;
}
// OOo SPEC: 3.6.1 p146
void OoWriterImport : : importFootnotesConfiguration ( TQDomDocument & doc , const TQDomElement & elem , bool endnote )
{
TQDomElement docElement ( doc . documentElement ( ) ) ;
// can we really be called more than once?
TQString elemName = endnote ? " ENDNOTESETTING " : " FOOTNOTESETTING " ;
Q_ASSERT ( docElement . namedItem ( elemName ) . isNull ( ) ) ;
TQDomElement settings = doc . createElement ( elemName ) ;
docElement . appendChild ( settings ) ;
// BUG in OO (both 1.0.1 and 1.1). It saves it with an off-by-one (reported to xml@).
// So instead of working around it (which would break with the next version, possibly)
// let's ignore this for now.
#if 0
if ( elem . hasAttributeNS ( ooNS : : text , " start-value " ) ) {
int startValue = elem . attributeNS ( ooNS : : text , " start-value " , TQString ( ) ) . toInt ( ) ;
settings . setAttribute ( " start " , startValue ) ;
}
# endif
settings . setAttribute ( " type " , Conversion : : importCounterType ( elem . attributeNS ( ooNS : : style , " num-format " , TQString ( ) ) ) ) ;
settings . setAttribute ( " lefttext " , elem . attributeNS ( ooNS : : style , " num-prefix " , TQString ( ) ) ) ;
settings . setAttribute ( " righttext " , elem . attributeNS ( ooNS : : style , " num-suffix " , TQString ( ) ) ) ;
}
void OoWriterImport : : appendTOC ( TQDomDocument & doc , const TQDomElement & toc )
{
// table-of-content OOo SPEC 7.5 p452
//fillStyleStack( toc, ooNS::text, "style-name" ); that's the section style
//TQDomElement tocSource = KoDom::namedItemNS( toc, ooNS::text, "table-of-content-source" );
// TODO parse templates and generate "Contents ..." styles from it
//for ( TQDomNode n(tocSource.firstChild()); !text.isNull(); text = text.nextSibling() )
//{
//}
TQDomElement tocIndexBody = KoDom : : namedItemNS ( toc , ooNS : : text , " index-body " ) ;
TQDomElement t ;
forEachElement ( t , tocIndexBody )
{
m_styleStack . save ( ) ;
const TQString localName = t . localName ( ) ;
TQDomElement e ;
bool isTextNS = e . namespaceURI ( ) = = ooNS : : text ;
if ( isTextNS & & localName = = " index-title " ) {
parseBodyOrSimilar ( doc , t , m_currentFrameset ) ; // recurse again
} else if ( isTextNS & & localName = = " p " ) {
fillStyleStack ( t , ooNS : : text , " style-name " ) ;
e = parseParagraph ( doc , t ) ;
}
if ( ! e . isNull ( ) )
m_currentFrameset . appendChild ( e ) ;
m_styleStack . restore ( ) ;
}
// KWord has a special attribute to know if a TOC is present
m_hasTOC = true ;
}
// TODO style:num-format, default number format for page styles,
// used for page numbers (2.3.1)
void OoWriterImport : : finishDocumentContent ( TQDomDocument & mainDocument )
{
TQDomElement attributes = mainDocument . createElement ( " ATTRIBUTES " ) ;
TQDomElement docElement = mainDocument . documentElement ( ) ;
docElement . appendChild ( attributes ) ;
attributes . setAttribute ( " hasTOC " , m_hasTOC ? 1 : 0 ) ;
attributes . setAttribute ( " hasHeader " , m_hasHeader ) ;
attributes . setAttribute ( " hasFooter " , m_hasFooter ) ;
// TODO unit?, tabStopValue
// TODO activeFrameset, cursorParagraph, cursorIndex
// Done at the end: write the type of headers/footers,
// depending on which kind of headers and footers we received.
TQDomElement paperElement = docElement . namedItem ( " PAPER " ) . toElement ( ) ;
Q_ASSERT ( ! paperElement . isNull ( ) ) ; // writePageLayout should have been called!
if ( ! paperElement . isNull ( ) )
{
//kdDebug(30513) << k_funcinfo << "m_headerFooters=" << m_headerFooters << endl;
//paperElement.setAttribute("hType", Conversion::headerMaskToHType( m_headerFooters ) );
//paperElement.setAttribute("fType", Conversion::headerMaskToFType( m_headerFooters ) );
}
}
TQString OoWriterImport : : kWordStyleName ( const TQString & ooStyleName )
{
if ( ooStyleName . startsWith ( " Contents " ) ) {
TQString s ( ooStyleName ) ;
return s . replace ( 0 , 9 , TQString ( " Contents Head " ) ) ; // Awful hack for KWord's broken "update TOC" feature
} else {
return ooStyleName ;
}
}
// OOo SPEC: 2.3.3 p59
void OoWriterImport : : importHeaderFooter ( TQDomDocument & doc , const TQDomElement & headerFooter , bool hasEvenOdd , TQDomElement & style )
{
const TQString localName = headerFooter . localName ( ) ;
TQDomElement framesetElement = doc . createElement ( " FRAMESET " ) ;
TQDomElement framesetsPluralElement ( doc . documentElement ( ) . namedItem ( " FRAMESETS " ) . toElement ( ) ) ;
framesetElement . setAttribute ( " frameType " , 1 /* text */ ) ;
framesetElement . setAttribute ( " frameInfo " , Conversion : : headerTypeToFrameInfo ( localName , hasEvenOdd ) ) ;
framesetElement . setAttribute ( " name " , Conversion : : headerTypeToFramesetName ( localName , hasEvenOdd ) ) ;
framesetsPluralElement . appendChild ( framesetElement ) ;
bool isHeader = localName . startsWith ( " header " ) ;
if ( isHeader )
m_hasHeader = true ;
else
m_hasFooter = true ;
TQDomElement frameElementOut = createInitialFrame ( framesetElement , 29 , 798 , isHeader ? 0 : 567 , isHeader ? 41 : 567 + 41 , true , Copy ) ;
if ( ! style . isNull ( ) )
m_styleStack . push ( style ) ;
importCommonFrameProperties ( frameElementOut ) ;
if ( ! style . isNull ( ) )
m_styleStack . pop ( ) ; // don't let it be active when parsing the text
parseBodyOrSimilar ( doc , headerFooter , framesetElement ) ;
}
# include "oowriterimport.moc"