diff options
Diffstat (limited to 'filters/kpresenter/ooimpress/ooimpressimport.cpp')
-rw-r--r-- | filters/kpresenter/ooimpress/ooimpressimport.cpp | 2432 |
1 files changed, 2432 insertions, 0 deletions
diff --git a/filters/kpresenter/ooimpress/ooimpressimport.cpp b/filters/kpresenter/ooimpress/ooimpressimport.cpp new file mode 100644 index 000000000..5d6c9da4a --- /dev/null +++ b/filters/kpresenter/ooimpress/ooimpressimport.cpp @@ -0,0 +1,2432 @@ +/* This file is part of the KDE project + Copyright (C) 2002 Laurent Montel <lmontel@mandrakesoft.com> + Copyright (c) 2003 Lukas Tinkl <lukas@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 "ooimpressimport.h" + +#include <math.h> + +#include <tqregexp.h> +#include <tqdatetime.h> +#include <tqfileinfo.h> +#include <tqdir.h> + +#include <kzip.h> +#include <karchive.h> +#include <kdebug.h> +#include <KoUnit.h> +#include <KoDocumentInfo.h> +#include <KoDocument.h> + +#include <kgenericfactory.h> +#include <KoFilterChain.h> +#include <KoGlobal.h> +#include <ooutils.h> +#include <KoDom.h> +#include <KoOasisSettings.h> + +typedef KGenericFactory<OoImpressImport, KoFilter> OoImpressImportFactory; +K_EXPORT_COMPONENT_FACTORY( libooimpressimport, OoImpressImportFactory( "kofficefilters" ) ) + + +OoImpressImport::OoImpressImport( KoFilter *, const char *, const TQStringList & ) + : KoFilter(), + m_numPicture( 1 ), + m_numSound(1), + m_styles( 23, true ), + m_styleStack( ooNS::style, ooNS::fo ) +{ + m_styles.setAutoDelete( true ); + m_listStyles.setAutoDelete( true ); +} + +OoImpressImport::~OoImpressImport() +{ + TQDictIterator<animationList> it( m_animations ); // See TQDictIterator + for( ; it.current(); ++it ) + { + delete it.current()->element; + } + m_animations.clear(); +} + +KoFilter::ConversionStatus OoImpressImport::convert( TQCString const & from, TQCString const & to ) +{ + kdDebug(30518) << "Entering Ooimpress Import filter: " << from << " - " << to << endl; + + if ( (from != "application/vnd.sun.xml.impress" && from != "application/vnd.sun.xml.impress.template" ) + || to != "application/x-kpresenter" ) + { + kdWarning(30518) << "Invalid mimetypes " << from << " " << to << endl; + return KoFilter::NotImplemented; + } + + m_zip = new KZip( m_chain->inputFile() ); + + if ( !m_zip->open( IO_ReadOnly ) ) + { + kdError(30518) << "Couldn't open the requested file "<< m_chain->inputFile() << endl; + delete m_zip; + return KoFilter::FileNotFound; + } + + KoFilter::ConversionStatus preStatus = openFile(); + + if ( preStatus != KoFilter::OK ) + { + m_zip->close(); + delete m_zip; + return preStatus; + } + + TQDomDocument docinfo; + createDocumentInfo( docinfo ); + + // store document info + KoStoreDevice* 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() ); + } + + TQDomDocument doccontent; + createDocumentContent( doccontent ); + + // store document content + out = m_chain->storageFile( "maindoc.xml", KoStore::Write ); + if( out ) + { + TQCString content = doccontent.toCString(); + kdDebug(30518) << " content :" << content << endl; + out->writeBlock( content , content.length() ); + } + + m_zip->close(); + delete m_zip; + + kdDebug(30518) << "######################## OoImpressImport::convert done ####################" << endl; + return KoFilter::OK; +} + +// Very related to OoWriterImport::openFile() +KoFilter::ConversionStatus OoImpressImport::openFile() +{ + KoFilter::ConversionStatus status = loadAndParse( "content.xml", m_content ); + if ( status != KoFilter::OK ) + { + kdError(30518) << "Content.xml could not be parsed correctly! Aborting!" << endl; + return status; + } + + // We do not stop if the following calls fail. + TQDomDocument styles; + loadAndParse( "styles.xml", styles ); + loadAndParse( "meta.xml", m_meta ); + loadAndParse( "settings.xml", m_settings ); + + emit sigProgress( 10 ); + createStyleMap( styles ); + + return KoFilter::OK; +} + +KoFilter::ConversionStatus OoImpressImport::loadAndParse(const TQString& filename, TQDomDocument& doc) +{ + return OoUtils::loadAndParse( filename, doc, m_zip); +} + +// Very related to OoWriterImport::createDocumentInfo +void OoImpressImport::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; +} + +void OoImpressImport::createDocumentContent( TQDomDocument &doccontent ) +{ + TQDomDocument doc = KoDocument::createDomDocument( "kpresenter", "DOC", "1.2" ); + TQDomElement docElement = doc.documentElement(); + docElement.setAttribute( "editor", "KPresenter" ); + docElement.setAttribute( "mime", "application/x-kpresenter" ); + docElement.setAttribute( "syntaxVersion", "2" ); + + TQDomElement content = m_content.documentElement(); + + // content.xml contains some automatic-styles that we need to store + TQDomNode automaticStyles = KoDom::namedItemNS( content, ooNS::office, "automatic-styles" ); + if ( !automaticStyles.isNull() ) + insertStyles( automaticStyles.toElement() ); + + TQDomNode body = KoDom::namedItemNS( content, ooNS::office, "body" ); + if ( body.isNull() ) + return; + + TQDomElement customSlideShow = doc.createElement( "CUSTOMSLIDESHOWCONFIG" ); + + // presentation settings + TQDomElement settings = KoDom::namedItemNS( body, ooNS::presentation, "settings"); + if (!settings.isNull()) + { + if (settings.attributeNS( ooNS::presentation, "endless", TQString())=="true") + { + TQDomElement infElem = doc.createElement("INFINITLOOP"); + infElem.setAttribute("value", 1); + docElement.appendChild(infElem); + } + + if (settings.attributeNS( ooNS::presentation, "show-end-of-presentation-slide", TQString())=="true") + { + TQDomElement infElem = doc.createElement("SHOWENDOFPRESENTATIONSLIDE"); + infElem.setAttribute("value", 1); + docElement.appendChild(infElem); + } + + if (settings.attributeNS( ooNS::presentation, "force-manual", TQString())=="true") + { + TQDomElement manualElem = doc.createElement("MANUALSWITCH"); + manualElem.setAttribute("value", 1); + docElement.appendChild(manualElem); + } + if ( settings.hasAttributeNS( ooNS::presentation, "show") ) + { + TQDomElement defaultPage = doc.createElement("DEFAULTCUSTOMSLIDESHOWNAME"); + defaultPage.setAttribute("name", settings.attributeNS( ooNS::presentation, "show", TQString()) ); + docElement.appendChild(defaultPage); + } + } + + TQDomElement presentationShow; + forEachElement( presentationShow, settings ) + { + if ( presentationShow.localName()=="show" && presentationShow.namespaceURI() == ooNS::presentation ) + { + if ( presentationShow.hasAttributeNS( ooNS::presentation, "pages") && + presentationShow.hasAttributeNS( ooNS::presentation, "name")) + { + TQDomElement slide=doc.createElement("CUSTOMSLIDESHOW"); + slide.setAttribute( "pages", presentationShow.attributeNS( ooNS::presentation, "pages", TQString() )); + slide.setAttribute( "name", presentationShow.attributeNS( ooNS::presentation, "name", TQString() )); + customSlideShow.appendChild( slide ); + } + } + } + // it seems that ooimpress has different paper-settings for every slide. + // we take the settings of the first slide for the whole document. + TQDomElement drawPage = KoDom::namedItemNS( body, ooNS::draw, "page" ); + if ( drawPage.isNull() ) // no slides? give up. + return; + + TQDomElement objectElement = doc.createElement( "OBJECTS" ); + TQDomElement pictureElement = doc.createElement( "PICTURES" ); + TQDomElement pageTitleElement = doc.createElement( "PAGETITLES" ); + TQDomElement pageNoteElement = doc.createElement( "PAGENOTES" ); + TQDomElement backgroundElement = doc.createElement( "BACKGROUND" ); + TQDomElement soundElement = doc.createElement( "SOUNDS" ); + TQDomElement selSlideElement = doc.createElement( "SELSLIDES" ); + TQDomElement helpLineElement = doc.createElement( "HELPLINES" ); + TQDomElement attributeElement = doc.createElement( "ATTRIBUTES" ); + TQDomElement *master = m_styles[drawPage.attributeNS( ooNS::draw, "master-page-name", TQString() )]; + + appendObject(*master, doc, soundElement,pictureElement,pageNoteElement,objectElement, 0, true); + + TQDomElement *style = m_styles[master->attributeNS( ooNS::style, "page-master-name", TQString() )]; + TQDomElement properties = KoDom::namedItemNS( *style, ooNS::style, "properties" ); + //kdDebug(30518)<<" master->attribute( draw:style-name ) :"<<master->attributeNS( ooNS::draw, "style-name", TQString() )<<endl; + TQDomElement *backgroundStyle = m_stylesPresentation[ master->attributeNS( ooNS::draw, "style-name", TQString() ).isEmpty() ? "Standard-background" : master->attributeNS( ooNS::draw, "style-name", TQString() ) ]; + + //kdDebug(30518)<<" backgroundStyle :"<<backgroundStyle<<endl; + double pageHeight; + TQDomElement paperElement = doc.createElement( "PAPER" ); + if ( properties.isNull() ) + { + paperElement.setAttribute( "ptWidth", CM_TO_POINT(28) ); + paperElement.setAttribute( "ptHeight", CM_TO_POINT(21) ); + paperElement.setAttribute( "unit", 0 ); + paperElement.setAttribute( "format", 5 ); + paperElement.setAttribute( "tabStopValue", 42.5198 ); + paperElement.setAttribute( "orientation", 0 ); + pageHeight = 21; + + TQDomElement paperBorderElement = doc.createElement( "PAPERBORDERS" ); + paperBorderElement.setAttribute( "ptRight", 0 ); + paperBorderElement.setAttribute( "ptBottom", 0 ); + paperBorderElement.setAttribute( "ptLeft", 0 ); + paperBorderElement.setAttribute( "ptTop", 0 ); + paperElement.appendChild( paperBorderElement ); + } + else + { + paperElement.setAttribute( "ptWidth", KoUnit::parseValue(properties.attributeNS( ooNS::fo, "page-width", TQString() ) ) ); + paperElement.setAttribute( "ptHeight", KoUnit::parseValue(properties.attributeNS( ooNS::fo, "page-height", TQString() ) ) ); +// paperElement.setAttribute( "unit", 0 ); +// paperElement.setAttribute( "format", 5 ); +// paperElement.setAttribute( "tabStopValue", 42.5198 ); +// paperElement.setAttribute( "orientation", 0 ); + // Keep pageHeight in cm to avoid rounding-errors that would + // get multiplied with every new slide. + + if (properties.attributeNS( ooNS::style, "print-orientation", TQString())=="portrait") + paperElement.setAttribute("orientation", 0); + else if (properties.attributeNS( ooNS::style, "print-orientation", TQString())=="landscape") + paperElement.setAttribute("orientation", 1); + + + + pageHeight = properties.attributeNS( ooNS::fo, "page-height", TQString() ).remove( "cm" ).toDouble(); + + TQDomElement paperBorderElement = doc.createElement( "PAPERBORDERS" ); + paperBorderElement.setAttribute( "ptRight", KoUnit::parseValue( properties.attributeNS( ooNS::fo, "margin-right", TQString() ) ) ); + paperBorderElement.setAttribute( "ptBottom", KoUnit::parseValue( properties.attributeNS( ooNS::fo, "margin-bottom", TQString() ) ) ); + paperBorderElement.setAttribute( "ptLeft", KoUnit::parseValue( properties.attributeNS( ooNS::fo, "margin-left", TQString() ) ) ); + paperBorderElement.setAttribute( "ptTop", KoUnit::parseValue( properties.attributeNS( ooNS::fo, "margin-top", TQString() ) ) ); + paperElement.appendChild( paperBorderElement ); + } + + + // parse all pages + forEachElement( drawPage, body ) + { + if ( drawPage.localName()=="page" && drawPage.namespaceURI() == ooNS::draw && drawPage.hasAttributeNS( ooNS::draw, "id" )) + { + m_styleStack.clear(); // remove all styles + fillStyleStack( drawPage ); + m_styleStack.save(); + int pagePos = drawPage.attributeNS( ooNS::draw, "id", TQString() ).toInt() - 1; + // take care of a possible page background or slide transition or sound + if ( m_styleStack.hasAttributeNS( ooNS::draw, "fill" ) + || m_styleStack.hasAttributeNS( ooNS::presentation, "transition-style" )) + { + appendBackgroundPage( doc, backgroundElement,pictureElement, soundElement ); + } + else if ( !m_styleStack.hasAttributeNS( ooNS::draw, "fill" ) && backgroundStyle) + { + m_styleStack.save(); + m_styleStack.push( *backgroundStyle ); + appendBackgroundPage( doc, backgroundElement,pictureElement, soundElement ); + m_styleStack.restore(); + kdDebug(30518)<<" load standard bacground \n"; + } + if ( m_styleStack.hasAttributeNS( ooNS::presentation, "visibility" ) ) + { + TQString str = m_styleStack.attributeNS( ooNS::presentation, "visibility" ); + TQDomElement slide = doc.createElement("SLIDE"); + slide.setAttribute( "nr", pagePos ); + slide.setAttribute( "show", ( ( str=="hidden" ) ? "0" : "1" )); + selSlideElement.appendChild( slide ); + + //todo add support + kdDebug(30518)<<"m_styleStack.hasAttribute( presentation:visibility ) :"<<str<<" position page "<<pagePos<<endl; + } + // set the pagetitle + TQDomElement titleElement = doc.createElement( "Title" ); + titleElement.setAttribute( "title", drawPage.attributeNS( ooNS::draw, "name", TQString() ) ); + pageTitleElement.appendChild( titleElement ); + + // The '+1' is necessary to avoid that objects that start on the first line + // of a slide will show up on the last line of the previous slide. + double offset = CM_TO_POINT( ( drawPage.attributeNS( ooNS::draw, "id", TQString() ).toInt() - 1 ) * pageHeight ) + 1; + + // animations (object effects) + createPresentationAnimation(KoDom::namedItemNS( drawPage, ooNS::presentation, "animations") ); + + // parse all objects + appendObject(drawPage, doc, soundElement,pictureElement,pageNoteElement,objectElement, offset); + + //m_animations.clear(); + m_styleStack.restore(); + } + } + + docElement.appendChild( paperElement ); + docElement.appendChild( backgroundElement ); + if ( parseSettings( doc, helpLineElement, attributeElement ) ) + docElement.appendChild( helpLineElement ); + docElement.appendChild( attributeElement ); + docElement.appendChild( pageTitleElement ); + docElement.appendChild( pageNoteElement ); + docElement.appendChild( objectElement ); + docElement.appendChild( selSlideElement ); + docElement.appendChild( customSlideShow ); + docElement.appendChild( soundElement ); + docElement.appendChild( pictureElement ); + + doccontent.appendChild( doc ); +} + +bool OoImpressImport::parseSettings( TQDomDocument &doc, TQDomElement &helpLineElement, TQDomElement &attributeElement ) +{ + bool foundElement = false; + KoOasisSettings settings( m_settings, ooNS::office, ooNS::config ); + KoOasisSettings::Items viewSettings = settings.itemSet( "view-settings" ); + //setUnit(KoUnit::unit(viewSettings.parseConfigItemString("unit"))); + KoOasisSettings::IndexedMap viewMap = viewSettings.indexedMap( "Views" ); + KoOasisSettings::Items firstView = viewMap.entry( 0 ); + //<config:config-item config:name="SnapLinesDrawing" config:type="string">V7939H1139</config:config-item> + //by default show line + + if ( !firstView.isNull() ) + { + TQString str = firstView.parseConfigItemString( "SnapLinesDrawing" ); + if ( !str.isEmpty() ) + { + parseHelpLine( doc, helpLineElement, str ); + //display it by default + helpLineElement.setAttribute( "show", true ); + foundElement = true; + } + + int gridX = firstView.parseConfigItemInt( "GridFineWidth" ); + int gridY = firstView.parseConfigItemInt( "GridFineHeight" ); + bool snapToGrid = firstView.parseConfigItemBool( "IsSnapToGrid" ); + int selectedPage = firstView.parseConfigItemInt( "SelectedPage" ); + + attributeElement.setAttribute( "activePage", selectedPage ); + attributeElement.setAttribute( "gridx", MM_TO_POINT( gridX / 100.0 ) ); + attributeElement.setAttribute( "gridy", MM_TO_POINT( gridY / 100.0 ) ); + attributeElement.setAttribute( "snaptogrid", (int)snapToGrid ); + + } + + //kdDebug(30518)<<" gridX :"<<gridX<<" gridY :"<<gridY<<" snapToGrid :"<<snapToGrid<<" selectedPage :"<<selectedPage<<endl; + return foundElement; +} + +void OoImpressImport::parseHelpLine( TQDomDocument &doc,TQDomElement &helpLineElement, const TQString &text ) +{ + TQString str; + int newPos = text.length()-1; //start to element = 1 + for ( int pos = text.length()-1; pos >=0;--pos ) + { + if ( text[pos]=='P' ) + { + + //point + str = text.mid( pos+1, ( newPos-pos ) ); + TQDomElement point=doc.createElement("HelpPoint"); + + //kdDebug(30518)<<" point element :"<< str <<endl; + TQStringList listVal = TQStringList::split( ",", str ); + int posX = ( listVal[0].toInt()/100 ); + int posY = ( listVal[1].toInt()/100 ); + point.setAttribute("posX", MM_TO_POINT( posX )); + point.setAttribute("posY", MM_TO_POINT( posY )); + + helpLineElement.appendChild( point ); + newPos = pos-1; + } + else if ( text[pos]=='V' ) + { + TQDomElement lines=doc.createElement("Vertical"); + //vertical element + str = text.mid( pos+1, ( newPos-pos ) ); + //kdDebug(30518)<<" vertical :"<< str <<endl; + int posX = ( str.toInt()/100 ); + lines.setAttribute( "value", MM_TO_POINT( posX ) ); + helpLineElement.appendChild( lines ); + + newPos = ( pos-1 ); + + } + else if ( text[pos]=='H' ) + { + //horizontal element + TQDomElement lines=doc.createElement("Horizontal"); + str = text.mid( pos+1, ( newPos-pos ) ); + //kdDebug(30518)<<" horizontal :"<< str <<endl; + int posY = ( str.toInt()/100 ); + lines.setAttribute( "value", MM_TO_POINT( posY ) ); + helpLineElement.appendChild( lines ); + newPos = pos-1; + } + } +} + +void OoImpressImport::appendObject(TQDomNode & drawPage, TQDomDocument & doc, TQDomElement & soundElement, TQDomElement & pictureElement, TQDomElement & pageNoteElement, TQDomElement &objectElement, double offset, bool sticky) +{ + TQDomElement o; + forEachElement( o, drawPage ) + { + const TQString localName = o.localName(); + const TQString ns = o.namespaceURI(); + const TQString drawID = o.attributeNS( ooNS::draw, "id", TQString()); + m_styleStack.save(); + + TQDomElement e; + if ( localName == "text-box" && ns == ooNS::draw ) // textbox + { + fillStyleStack( o, sticky ); + e = doc.createElement( "OBJECT" ); + e.setAttribute( "type", 4 ); + if ( sticky ) + e.setAttribute( "sticky", "1" ); + append2DGeometry( doc, e, o, (int)offset ); + appendName(doc, e, o); + appendPen( doc, e ); + appendBrush( doc, e ); + appendRounding( doc, e, o ); + appendShadow( doc, e ); + appendObjectEffect(doc, e, o, soundElement); + e.appendChild( parseTextBox( doc, o ) ); + } + else if ( localName == "rect" && ns == ooNS::draw ) // rectangle + { + fillStyleStack( o, sticky ); + e = doc.createElement( "OBJECT" ); + e.setAttribute( "type", 2 ); + if ( sticky ) + e.setAttribute( "sticky", "1" ); + append2DGeometry( doc, e, o, (int)offset ); + appendName(doc, e, o); + appendPen( doc, e ); + appendBrush( doc, e ); + appendRounding( doc, e, o ); + appendShadow( doc, e ); + + appendObjectEffect(doc, e, o, soundElement); + } + else if ( ( localName == "circle" || localName == "ellipse" ) && ns == ooNS::draw ) + { + fillStyleStack( o, sticky ); + e = doc.createElement( "OBJECT" ); + if ( sticky ) + e.setAttribute( "sticky", "1" ); + append2DGeometry( doc, e, o, (int)offset ); + appendName(doc, e, o); + appendPen( doc, e ); + appendShadow( doc, e ); + appendLineEnds( doc, e ); + appendObjectEffect(doc, e, o, soundElement); + + if ( o.hasAttributeNS( ooNS::draw, "kind" ) ) // pie, chord or arc + { + e.setAttribute( "type", 8 ); + appendPie( doc, e, o ); + TQDomElement type = doc.createElement( "PIETYPE" ); + + TQString kind = o.attributeNS( ooNS::draw, "kind", TQString() ); + if ( kind == "section" ) + { + appendBrush( doc, e ); + type.setAttribute( "value", 0 ); + } + else if ( kind == "cut" ) + { + appendBrush( doc, e ); + type.setAttribute( "value", 2 ); + } + else if ( kind == "arc" ) + { + // arc has no brush + type.setAttribute( "value", 1 ); + } + e.appendChild( type ); + } + else // circle or ellipse + { + e.setAttribute( "type", 3 ); + appendBrush( doc, e ); + } + } + else if ( localName == "line" && ns == ooNS::draw ) // line + { + fillStyleStack( o, sticky ); + e = doc.createElement( "OBJECT" ); + e.setAttribute( "type", 1 ); + if ( sticky ) + e.setAttribute( "sticky", "1" ); + bool orderEndStartLine = appendLineGeometry( doc, e, o, (int)offset ); + appendName(doc, e, o); + appendPen( doc, e ); + appendBrush( doc, e ); + appendShadow( doc, e ); + appendLineEnds( doc, e, orderEndStartLine ); + appendObjectEffect(doc, e, o, soundElement); + } + else if ( localName=="polyline" && ns == ooNS::draw ) { // polyline + fillStyleStack(o, sticky); + e = doc.createElement("OBJECT"); + e.setAttribute("type", 12); + if ( sticky ) + e.setAttribute( "sticky", "1" ); + append2DGeometry(doc, e, o, (int)offset); + appendName(doc, e, o); + appendPoints(doc, e, o); + appendPen(doc, e); + appendBrush(doc, e); + appendLineEnds(doc, e); + //appendShadow(doc, e); + appendObjectEffect(doc, e, o, soundElement); + } + else if ( localName=="polygon" && ns == ooNS::draw ) { // polygon + fillStyleStack(o, sticky); + e = doc.createElement("OBJECT"); + e.setAttribute("type", 16); + if ( sticky ) + e.setAttribute( "sticky", "1" ); + append2DGeometry(doc, e, o, (int)offset); + appendName(doc, e, o); + appendPoints(doc, e, o); + appendPen(doc, e); + appendBrush(doc, e); + //appendLineEnds(doc, e); + //appendShadow(doc, e); + appendObjectEffect(doc, e, o, soundElement); + } + else if ( localName == "image" && ns == ooNS::draw ) // image + { + fillStyleStack( o, sticky ); + e = doc.createElement( "OBJECT" ); + e.setAttribute( "type", 0 ); + if ( sticky ) + e.setAttribute( "sticky", "1" ); + append2DGeometry( doc, e, o, (int)offset ); + appendName(doc, e, o); + appendImage( doc, e, pictureElement, o ); + appendObjectEffect(doc, e, o, soundElement); + } + else if ( localName == "object" && ns == ooNS::draw ) + { + //todo add part object + } + else if ( localName == "g" && ns == ooNS::draw ) + { + //todo add group object + } + else if ( localName == "path" && ns == ooNS::draw ) + { + //todo add path object (freehand/cubic/quadricbeziercurve + } + else if ( localName == "notes" && ns == ooNS::presentation ) // notes + { + TQDomNode textBox = KoDom::namedItemNS( o, ooNS::draw, "text-box" ); + if ( !textBox.isNull() ) + { + TQString note; + TQDomElement t; + forEachElement( t, textBox ) + { + // We don't care about styles as they are not supported in kpresenter. + // Only add a linebreak for every child. + note += t.text() + "\n"; + } + TQDomElement notesElement = doc.createElement( "Note" ); + notesElement.setAttribute( "note", note ); + pageNoteElement.appendChild( notesElement ); + } + } + else + { + kdDebug(30518) << "Unsupported object '" << localName << "'" << endl; + m_styleStack.restore(); + continue; + } + + objectElement.appendChild( e ); + m_styleStack.restore(); + } +} + +void OoImpressImport::appendBackgroundPage( TQDomDocument &doc, TQDomElement &backgroundElement, TQDomElement & pictureElement, TQDomElement &soundElement) +{ + TQDomElement bgPage = doc.createElement( "PAGE" ); + + // background + if ( m_styleStack.hasAttributeNS( ooNS::draw, "fill" ) ) + { + const TQString fill = m_styleStack.attributeNS( ooNS::draw, "fill" ); + if ( fill == "solid" ) + { + TQDomElement backColor1 = doc.createElement( "BACKCOLOR1" ); + backColor1.setAttribute( "color", m_styleStack.attributeNS( ooNS::draw, "fill-color" ) ); + bgPage.appendChild( backColor1 ); + + TQDomElement bcType = doc.createElement( "BCTYPE" ); + bcType.setAttribute( "value", 0 ); // plain + bgPage.appendChild( bcType ); + + TQDomElement backType = doc.createElement( "BACKTYPE" ); + backType.setAttribute( "value", 0 ); // color/gradient + bgPage.appendChild( backType ); + } + else if ( fill == "gradient" ) + { + TQString style = m_styleStack.attributeNS( ooNS::draw, "fill-gradient-name" ); + TQDomElement* draw = m_draws[style]; + appendBackgroundGradient( doc, bgPage, *draw ); + } + else if ( fill == "bitmap" ) + { + TQString style = m_styleStack.attributeNS( ooNS::draw, "fill-image-name" ); + TQDomElement* draw = m_draws[style]; + appendBackgroundImage( doc, bgPage, pictureElement, *draw ); + + TQDomElement backView = doc.createElement( "BACKVIEW" ); + if ( m_styleStack.hasAttributeNS( ooNS::style, "repeat" ) ) + { + TQString repeat = m_styleStack.attributeNS( ooNS::style, "repeat" ); + if ( repeat == "stretch" ) + backView.setAttribute( "value", 0 ); // zoomed + else if ( repeat == "no-repeat" ) + backView.setAttribute( "value", 1 ); // centered + else + backView.setAttribute( "value", 2 ); // use tiled as default + } + else + backView.setAttribute( "value", 2 ); // use tiled as default + bgPage.appendChild( backView ); + + TQDomElement backType = doc.createElement( "BACKTYPE" ); + backType.setAttribute( "value", 1 ); // image + bgPage.appendChild( backType ); + } + } + + if ( m_styleStack.hasAttributeNS( ooNS::presentation, "duration" ) ) + { + TQString str = m_styleStack.attributeNS( ooNS::presentation, "duration"); + kdDebug(30518)<<"styleStack.hasAttribute(presentation:duration ) :"<<str<<endl; + //convert date duration + int hour( str.mid( 2, 2 ).toInt() ); + int minute( str.mid( 5, 2 ).toInt() ); + int second( str.mid( 8, 2 ).toInt() ); + int pageTimer = second + minute*60 + hour*60*60; + TQDomElement pgEffect = doc.createElement("PGTIMER"); + pgEffect.setAttribute( "timer", pageTimer ); + bgPage.appendChild(pgEffect); + } + // slide transition + if (m_styleStack.hasAttributeNS( ooNS::presentation, "transition-style")) + { + TQDomElement pgEffect = doc.createElement("PGEFFECT"); + + const TQString effect = m_styleStack.attributeNS( ooNS::presentation, "transition-style"); + //kdDebug(30518) << "Transition name: " << effect << endl; + int pef; + + if (effect=="vertical-stripes" || effect=="vertical-lines") // PEF_BLINDS_VER + pef=14; + else if (effect=="horizontal-stripes" || effect=="horizontal-lines") // PEF_BLINDS_HOR + pef=13; + else if (effect=="spiralin-left" || effect=="spiralin-right" + || effect== "spiralout-left" || effect=="spiralout-right") // PEF_SURROUND1 + pef=11; + else if (effect=="fade-from-upperleft") // PEF_STRIPS_RIGHT_DOWN + pef=39; + else if (effect=="fade-from-upperright") // PEF_STRIPS_LEFT_DOWN + pef=37; + else if (effect=="fade-from-lowerleft") // PEF_STRIPS_RIGHT_UP + pef=38; + else if (effect=="fade-from-lowerright") // PEF_STRIPS_LEFT_UP + pef=36; + else if (effect=="fade-from-top") // PEF_COVER_DOWN + pef=19; + else if (effect=="fade-from-bottom") // PEF_COVER_UP + pef=21; + else if (effect=="fade-from-left") // PEF_COVER_RIGHT + pef=25; + else if (effect=="fade-from-right") // PEF_COVER_LEFT + pef=23; + else if (effect=="fade-to-center") // PEF_CLOSE_ALL + pef=3; + else if (effect=="fade-from-center") // PEF_OPEN_ALL + pef=6; + else if (effect=="open-vertical") // PEF_OPEN_HORZ; really, no kidding ;) + pef=4; + else if (effect=="open-horizontal") // PEF_OPEN_VERT + pef=5; + else if (effect=="close-vertical") // PEF_CLOSE_HORZ + pef=1; + else if (effect=="close-horizontal") // PEF_CLOSE_VERT + pef=2; + else if (effect=="dissolve") // PEF_DISSOLVE; perfect hit ;) + pef=35; + else if (effect=="horizontal-checkerboard") // PEF_CHECKBOARD_ACROSS + pef=17; + else if (effect=="vertical-checkerboard") // PEF_CHECKBOARD_DOWN + pef=18; + else if (effect=="roll-from-left") // PEF_UNCOVER_RIGHT + pef=26; + else if (effect=="roll-from-right") // PEF_UNCOVER_LEFT + pef=24; + else if (effect=="roll-from-bottom") // PEF_UNCOVER_UP + pef=22; + else if (effect=="roll-from-top") // PEF_UNCOVER_DOWN + pef=20; + else if (effect=="random") // PEF_RANDOM + pef=-1; + else // we choose a random transition instead of the unsupported ones ;) + pef=-1; + + pgEffect.setAttribute("value", pef); + bgPage.appendChild(pgEffect); + } + + // slide transition sound + if (m_styleStack.hasChildNodeNS( ooNS::presentation, "sound")) + { + TQString soundUrl = storeSound(m_styleStack.childNodeNS( ooNS::presentation, "sound"), + soundElement, doc); + + if (!soundUrl.isNull()) + { + TQDomElement pseElem = doc.createElement("PGSOUNDEFFECT"); + pseElem.setAttribute("soundEffect", 1); + pseElem.setAttribute("soundFileName", soundUrl); + + bgPage.appendChild(pseElem); + } + } + + backgroundElement.appendChild(bgPage); +} + +void OoImpressImport::appendName(TQDomDocument& doc, TQDomElement& e, const TQDomElement& object) +{ + if( object.hasAttributeNS( ooNS::draw, "name" )) + { + TQDomElement name = doc.createElement( "OBJECTNAME" ); + name.setAttribute( "objectName", object.attributeNS( ooNS::draw, "name", TQString() )); + e.appendChild( name ); + } +} + +void OoImpressImport::append2DGeometry( TQDomDocument& doc, TQDomElement& e, const TQDomElement& object, int offset ) +{ + TQDomElement orig = doc.createElement( "ORIG" ); + orig.setAttribute( "x", KoUnit::parseValue( object.attributeNS( ooNS::svg, "x", TQString() ) ) ); + orig.setAttribute( "y", KoUnit::parseValue( object.attributeNS( ooNS::svg, "y", TQString() ) ) + offset ); + e.appendChild( orig ); + + TQDomElement size = doc.createElement( "SIZE" ); + size.setAttribute( "width", KoUnit::parseValue( object.attributeNS( ooNS::svg, "width", TQString() ) ) ); + size.setAttribute( "height", KoUnit::parseValue( object.attributeNS( ooNS::svg, "height", TQString() ) ) ); + e.appendChild( size ); + if( object.hasAttributeNS( ooNS::draw, "transform" )) + { + kdDebug(30518)<<" object transform \n"; + //todo parse it + TQString transform = object.attributeNS( ooNS::draw, "transform", TQString() ); + if( transform.contains("rotate (")) + { + //kdDebug(30518)<<" rotate object \n"; + transform = transform.remove("rotate (" ); + transform = transform.left(transform.find(")")); + //kdDebug(30518)<<" transform :"<<transform<<endl; + bool ok; + double radian = transform.toDouble(&ok); + if( ok ) + { + TQDomElement angle = doc.createElement( "ANGLE" ); + //angle is defined as a radian in oo but degree into kpresenter. + angle.setAttribute("value", (-1 * ((radian*180)/M_PI))); + + e.appendChild( angle ); + } + } + } +} + +//return true if (x1 < x2) necessary to load correctly start-line and end-line +bool OoImpressImport::appendLineGeometry( TQDomDocument& doc, TQDomElement& e, const TQDomElement& object, int offset ) +{ + double x1 = KoUnit::parseValue( object.attributeNS( ooNS::svg, "x1", TQString() ) ); + double y1 = KoUnit::parseValue( object.attributeNS( ooNS::svg, "y1", TQString() ) ); + double x2 = KoUnit::parseValue( object.attributeNS( ooNS::svg, "x2", TQString() ) ); + double y2 = KoUnit::parseValue( object.attributeNS( ooNS::svg, "y2", TQString() ) ); + + double x = TQMIN( x1, x2 ); + double y = TQMIN( y1, y2 ); + + TQDomElement orig = doc.createElement( "ORIG" ); + orig.setAttribute( "x", x ); + orig.setAttribute( "y", y + offset ); + e.appendChild( orig ); + + TQDomElement size = doc.createElement( "SIZE" ); + size.setAttribute( "width", fabs( x1 - x2 ) ); + size.setAttribute( "height", fabs( y1 - y2 ) ); + e.appendChild( size ); + + TQDomElement linetype = doc.createElement( "LINETYPE" ); + if ( ( x1 < x2 && y1 < y2 ) || ( x1 > x2 && y1 > y2 ) ) + linetype.setAttribute( "value", 2 ); + else + linetype.setAttribute( "value", 3 ); + + e.appendChild( linetype ); + return (x1 < x2); +} + +void OoImpressImport::appendPen( TQDomDocument& doc, TQDomElement& e ) +{ + if ( m_styleStack.hasAttributeNS( ooNS::draw, "stroke" )) + { + TQDomElement pen = doc.createElement( "PEN" ); + if ( m_styleStack.attributeNS( ooNS::draw, "stroke" ) == "none" ) + pen.setAttribute( "style", 0 ); + else if ( m_styleStack.attributeNS( ooNS::draw, "stroke" ) == "solid" ) + pen.setAttribute( "style", 1 ); + else if ( m_styleStack.attributeNS( ooNS::draw, "stroke" ) == "dash" ) + { + TQString style = m_styleStack.attributeNS( ooNS::draw, "stroke-dash" ); + if ( style == "Ultrafine Dashed" || style == "Fine Dashed" || + style == "Fine Dashed (var)" || style == "Dashed (var)" ) + pen.setAttribute( "style", 2 ); + else if ( style == "Fine Dotted" || style == "Ultrafine Dotted (var)" || + style == "Line with Fine Dots" ) + pen.setAttribute( "style", 3 ); + else if ( style == "3 Dashes 3 Dots (var)" || style == "Ultrafine 2 Dots 3 Dashes" ) + pen.setAttribute( "style", 4 ); + else if ( style == "2 Dots 1 Dash" ) + pen.setAttribute( "style", 5 ); + } + + if ( m_styleStack.hasAttributeNS( ooNS::svg, "stroke-width" ) ) + pen.setAttribute( "width", (int) KoUnit::parseValue( m_styleStack.attributeNS( ooNS::svg, "stroke-width" ) ) ); + if ( m_styleStack.hasAttributeNS( ooNS::svg, "stroke-color" ) ) + pen.setAttribute( "color", m_styleStack.attributeNS( ooNS::svg, "stroke-color" ) ); + e.appendChild( pen ); + } +} + +void OoImpressImport::appendBrush( TQDomDocument& doc, TQDomElement& e ) +{ + if ( m_styleStack.hasAttributeNS( ooNS::draw, "fill" ) ) + { + const TQString fill = m_styleStack.attributeNS( ooNS::draw, "fill" ); + //kdDebug(30518)<<"void OoImpressImport::appendBrush( TQDomDocument& doc, TQDomElement& e ) :"<<fill<<endl; + if ( fill == "solid" ) + { + TQDomElement brush = doc.createElement( "BRUSH" ); + if ( m_styleStack.hasAttributeNS( ooNS::draw, "transparency" ) ) + { + TQString transparency = m_styleStack.attributeNS( ooNS::draw, "transparency" ); + transparency = transparency.remove( '%' ); + int value = transparency.toInt(); + if ( value >= 94 && value <= 99 ) + { + brush.setAttribute( "style", 2 ); + } + else if ( value>=64 && value <= 93 ) + { + brush.setAttribute( "style", 3 ); + } + else if ( value>=51 && value <= 63 ) + { + brush.setAttribute( "style", 4 ); + } + else if ( value>=38 && value <= 50 ) + { + brush.setAttribute( "style", 5 ); + } + else if ( value>=13 && value <= 37 ) + { + brush.setAttribute( "style", 6 ); + } + else if ( value>=7 && value <= 12 ) + { + brush.setAttribute( "style", 7 ); + } + else if ( value>=1 && value <= 6 ) + { + brush.setAttribute( "style", 8 ); + } + } + else + brush.setAttribute( "style", 1 ); + if ( m_styleStack.hasAttributeNS( ooNS::draw, "fill-color" ) ) + brush.setAttribute( "color", m_styleStack.attributeNS( ooNS::draw, "fill-color" ) ); + e.appendChild( brush ); + } + else if ( fill == "hatch" ) + { + TQDomElement brush = doc.createElement( "BRUSH" ); + TQString style = m_styleStack.attributeNS( ooNS::draw, "fill-hatch-name" ); + TQDomElement* draw = m_draws[style]; + if ( draw ) + { + if( draw->hasAttributeNS( ooNS::draw, "color" ) ) + brush.setAttribute( "color", draw->attributeNS( ooNS::draw, "color", TQString() ) ); + int angle = 0; + if( draw->hasAttributeNS( ooNS::draw, "rotation" )) + { + angle = (draw->attributeNS( ooNS::draw, "rotation", TQString() ).toInt())/10; + kdDebug(30518)<<"angle :"<<angle<<endl; + } + if( draw->hasAttributeNS( ooNS::draw, "style" )) + { + TQString styleHash = draw->attributeNS( ooNS::draw, "style", TQString() ); + if( styleHash == "single") + { + switch( angle ) + { + case 0: + case 180: + brush.setAttribute( "style", 9 ); + break; + case 45: + case 225: + brush.setAttribute( "style", 12 ); + break; + case 90: + case 270: + brush.setAttribute( "style", 10 ); + break; + case 135: + case 315: + brush.setAttribute( "style", 13 ); + break; + default: + //todo fixme when we will have a kopaint + kdDebug(30518)<<" draw:rotation 'angle' : "<<angle<<endl; + break; + } + } + else if( styleHash == "double") + { + switch( angle ) + { + case 0: + case 180: + case 90: + case 270: + brush.setAttribute("style", 11 ); + break; + case 45: + case 135: + case 225: + case 315: + brush.setAttribute("style",14 ); + break; + default: + //todo fixme when we will have a kopaint + kdDebug(30518)<<" draw:rotation 'angle' : "<<angle<<endl; + break; + } + + } + else if( styleHash == "triple") + { + kdDebug(30518)<<" it is not implemented :( \n"; + } + + } + } + e.appendChild( brush ); + } + else if ( fill == "gradient" ) + { + // We have to set a brush with brushstyle != no background fill + // otherwise the properties dialog for the object won't + // display the preview for the gradient. + TQDomElement brush = doc.createElement( "BRUSH" ); + brush.setAttribute( "style", 1 ); + e.appendChild( brush ); + + TQDomElement gradient = doc.createElement( "GRADIENT" ); + TQString style = m_styleStack.attributeNS( ooNS::draw, "fill-gradient-name" ); + + TQDomElement* draw = m_draws[style]; + if ( draw ) + { + gradient.setAttribute( "color1", draw->attributeNS( ooNS::draw, "start-color", TQString() ) ); + gradient.setAttribute( "color2", draw->attributeNS( ooNS::draw, "end-color", TQString() ) ); + + TQString type = draw->attributeNS( ooNS::draw, "style", TQString() ); + //kdDebug(30518)<<" type !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! :"<<type<<endl; + if ( type == "linear" ) + { + int angle = draw->attributeNS( ooNS::draw, "angle", TQString() ).toInt() / 10; + + // make sure the angle is between 0 and 359 + angle = abs( angle ); + angle -= ( (int) ( angle / 360 ) ) * 360; + + // What we are trying to do here is to find out if the given + // angle belongs to a horizontal, vertical or diagonal gradient. + int lower, upper, nearAngle = 0; + for ( lower = 0, upper = 45; upper < 360; lower += 45, upper += 45 ) + { + if ( upper >= angle ) + { + int distanceToUpper = abs( angle - upper ); + int distanceToLower = abs( angle - lower ); + nearAngle = distanceToUpper > distanceToLower ? lower : upper; + break; + } + } + kdDebug(30518)<<"nearAngle :"<<nearAngle<<endl; + // nearAngle should now be one of: 0, 45, 90, 135, 180... + if ( nearAngle == 0 || nearAngle == 180 ) + gradient.setAttribute( "type", 1 ); // horizontal + else if ( nearAngle == 90 || nearAngle == 270 ) + gradient.setAttribute( "type", 2 ); // vertical + else if ( nearAngle == 45 || nearAngle == 225 ) + gradient.setAttribute( "type", 3 ); // diagonal 1 + else if ( nearAngle == 135 || nearAngle == 315 ) + gradient.setAttribute( "type", 4 ); // diagonal 2 + } + else if ( type == "radial" || type == "ellipsoid" ) + gradient.setAttribute( "type", 5 ); // circle + else if ( type == "square" || type == "rectangular" ) + gradient.setAttribute( "type", 6 ); // rectangle + else if ( type == "axial" ) + gradient.setAttribute( "type", 7 ); // pipecross + + // Hard to map between x- and y-center settings of ooimpress + // and (un-)balanced settings of kpresenter. Let's try it. + int x, y; + if ( draw->hasAttributeNS( ooNS::draw, "cx" ) ) + x = draw->attributeNS( ooNS::draw, "cx", TQString() ).remove( '%' ).toInt(); + else + x = 50; + + if ( draw->hasAttributeNS( ooNS::draw, "cy" ) ) + y = draw->attributeNS( ooNS::draw, "cy", TQString() ).remove( '%' ).toInt(); + else + y = 50; + + if ( x == 50 && y == 50 ) + { + gradient.setAttribute( "unbalanced", 0 ); + gradient.setAttribute( "xfactor", 100 ); + gradient.setAttribute( "yfactor", 100 ); + } + else + { + gradient.setAttribute( "unbalanced", 1 ); + // map 0 - 100% to -200 - 200 + gradient.setAttribute( "xfactor", 4 * x - 200 ); + gradient.setAttribute( "yfactor", 4 * y - 200 ); + } + } + e.appendChild( gradient ); + + TQDomElement fillType = doc.createElement( "FILLTYPE" ); + fillType.setAttribute( "value", 1 ); + e.appendChild( fillType ); + } + } +} + +void OoImpressImport::appendPie( TQDomDocument& doc, TQDomElement& e, const TQDomElement& object ) +{ + TQDomElement angle = doc.createElement( "PIEANGLE" ); + int start = (int) ( object.attributeNS( ooNS::draw, "start-angle", TQString() ).toDouble() ); + angle.setAttribute( "value", start * 16 ); + e.appendChild( angle ); + + TQDomElement length = doc.createElement( "PIELENGTH" ); + int end = (int) ( object.attributeNS( ooNS::draw, "end-angle", TQString() ).toDouble() ); + if ( end < start ) + length.setAttribute( "value", ( 360 - start + end ) * 16 ); + else + length.setAttribute( "value", ( end - start ) * 16 ); + e.appendChild( length ); +} + +void OoImpressImport::appendImage( TQDomDocument& doc, TQDomElement& e, TQDomElement& p, + const TQDomElement& object ) +{ + TQString fileName = storeImage( object ); + + // create a key for the picture + TQTime time = TQTime::currentTime(); + TQDate date = TQDate::currentDate(); + + TQDomElement image = doc.createElement( "KEY" ); + image.setAttribute( "msec", time.msec() ); + image.setAttribute( "second", time.second() ); + image.setAttribute( "minute", time.minute() ); + image.setAttribute( "hour", time.hour() ); + image.setAttribute( "day", date.day() ); + image.setAttribute( "month", date.month() ); + image.setAttribute( "year", date.year() ); + image.setAttribute( "filename", fileName ); + e.appendChild( image ); + + TQDomElement settings = doc.createElement( "PICTURESETTINGS" ); + if ( m_styleStack.hasAttributeNS( ooNS::draw, "color-mode" ) && ( m_styleStack.attributeNS( ooNS::draw, "color-mode" )=="greyscale" ) ) + settings.setAttribute( "grayscal", 1 ); + else + settings.setAttribute( "grayscal", 0 ); + + if ( m_styleStack.hasAttributeNS( ooNS::draw, "luminance" ) ) + { + TQString str( m_styleStack.attributeNS( ooNS::draw, "luminance" ) ); + str = str.remove( '%' ); + settings.setAttribute( "bright", str ); + } + else + settings.setAttribute( "bright", 0 ); + + settings.setAttribute( "mirrorType", 0 ); + settings.setAttribute( "swapRGB", 0 ); + settings.setAttribute( "depth", 0 ); + e.appendChild( settings ); + + TQDomElement effects = doc.createElement( "EFFECTS" ); + bool hasEffect = false; + if ( m_styleStack.hasAttributeNS( ooNS::draw, "contrast" ) ) + { + TQString str( m_styleStack.attributeNS( ooNS::draw, "contrast" ) ); + str = str.remove( '%' ); + int val = str.toInt(); + val = ( int )( 255.0 *val/100.0 ); + effects.setAttribute( "type", "5" ); + effects.setAttribute( "param1", TQString::number( val ) ); + hasEffect = true; + } + if ( hasEffect ) + e.appendChild( effects ); + + TQDomElement key = image.cloneNode().toElement(); + key.setAttribute( "name", "pictures/" + fileName ); + p.appendChild( key ); +} + +void OoImpressImport::appendBackgroundImage( TQDomDocument& doc, TQDomElement& e, + TQDomElement& p, const TQDomElement& object ) +{ + TQString fileName = storeImage( object ); + + // create a key for the picture + TQTime time = TQTime::currentTime(); + TQDate date = TQDate::currentDate(); + + TQDomElement image = doc.createElement( "BACKPICTUREKEY" ); + image.setAttribute( "msec", time.msec() ); + image.setAttribute( "second", time.second() ); + image.setAttribute( "minute", time.minute() ); + image.setAttribute( "hour", time.hour() ); + image.setAttribute( "day", date.day() ); + image.setAttribute( "month", date.month() ); + image.setAttribute( "year", date.year() ); + image.setAttribute( "filename", fileName ); + e.appendChild( image ); + + TQDomElement key = image.cloneNode().toElement(); + key.setTagName( "KEY" ); + key.setAttribute( "name", "pictures/" + fileName ); + p.appendChild( key ); +} + +void OoImpressImport::appendBackgroundGradient( TQDomDocument& doc, TQDomElement& e, + const TQDomElement& object ) +{ + TQDomElement backColor1 = doc.createElement( "BACKCOLOR1" ); + backColor1.setAttribute( "color", object.attributeNS( ooNS::draw, "start-color", TQString() ) ); + e.appendChild( backColor1 ); + + TQDomElement backColor2 = doc.createElement( "BACKCOLOR2" ); + backColor2.setAttribute( "color", object.attributeNS( ooNS::draw, "end-color", TQString() ) ); + e.appendChild( backColor2 ); + + TQDomElement backType = doc.createElement( "BACKTYPE" ); + backType.setAttribute( "value", 0 ); // color/gradient + e.appendChild( backType ); + + TQDomElement bcType = doc.createElement( "BCTYPE" ); + TQString type = object.attributeNS( ooNS::draw, "style", TQString() ); + if ( type == "linear" ) + { + int angle = object.attributeNS( ooNS::draw, "angle", TQString() ).toInt() / 10; + + // make sure the angle is between 0 and 359 + angle = abs( angle ); + angle -= ( (int) ( angle / 360 ) ) * 360; + + // What we are trying to do here is to find out if the given + // angle belongs to a horizontal, vertical or diagonal gradient. + int lower, upper, nearAngle = 0; + for ( lower = 0, upper = 45; upper < 360; lower += 45, upper += 45 ) + { + if ( upper >= angle ) + { + int distanceToUpper = abs( angle - upper ); + int distanceToLower = abs( angle - lower ); + nearAngle = distanceToUpper > distanceToLower ? lower : upper; + break; + } + } + + // nearAngle should now be one of: 0, 45, 90, 135, 180... + if ( nearAngle == 0 || nearAngle == 180 ) + bcType.setAttribute( "value", 1 ); // horizontal + else if ( nearAngle == 90 || nearAngle == 270 ) + bcType.setAttribute( "value", 2 ); // vertical + else if ( nearAngle == 45 || nearAngle == 225 ) + bcType.setAttribute( "value", 3 ); // diagonal 1 + else if ( nearAngle == 135 || nearAngle == 315 ) + bcType.setAttribute( "value", 4 ); // diagonal 2 + } + else if ( type == "radial" || type == "ellipsoid" ) + bcType.setAttribute( "value", 5 ); // circle + else if ( type == "square" || type == "rectangular" ) + bcType.setAttribute( "value", 6 ); // rectangle + else if ( type == "axial" ) + bcType.setAttribute( "value", 7 ); // pipecross + + e.appendChild( bcType ); + + TQDomElement bGradient = doc.createElement( "BGRADIENT" ); + + // Hard to map between x- and y-center settings of ooimpress + // and (un-)balanced settings of kpresenter. Let's try it. + int x, y; + if ( object.hasAttributeNS( ooNS::draw, "cx" ) ) + x = object.attributeNS( ooNS::draw, "cx", TQString() ).remove( '%' ).toInt(); + else + x = 50; + + if ( object.hasAttributeNS( ooNS::draw, "cy" ) ) + y = object.attributeNS( ooNS::draw, "cy", TQString() ).remove( '%' ).toInt(); + else + y = 50; + + if ( x == 50 && y == 50 ) + { + bGradient.setAttribute( "unbalanced", 0 ); + bGradient.setAttribute( "xfactor", 100 ); + bGradient.setAttribute( "yfactor", 100 ); + } + else + { + bGradient.setAttribute( "unbalanced", 1 ); + // map 0 - 100% to -200 - 200 + bGradient.setAttribute( "xfactor", 4 * x - 200 ); + bGradient.setAttribute( "yfactor", 4 * y - 200 ); + } + + e.appendChild( bGradient ); +} + +void OoImpressImport::appendRounding( TQDomDocument& doc, TQDomElement& e, const TQDomElement& object ) +{ + if ( object.hasAttributeNS( ooNS::draw, "corner-radius" ) ) + { + // kpresenter uses percent, ooimpress uses cm ... hmm? + TQDomElement rounding = doc.createElement( "RNDS" ); + int corner = static_cast<int>(KoUnit::parseValue(object.attributeNS( ooNS::draw, "corner-radius", TQString()))); + rounding.setAttribute( "x", corner ); + rounding.setAttribute( "y", corner ); + e.appendChild( rounding ); + } +} + +void OoImpressImport::appendShadow( TQDomDocument& doc, TQDomElement& e ) +{ + // Note that ooimpress makes a difference between shadowed text and + // a shadowed object while kpresenter only knows the attribute 'shadow'. + // This means that a shadowed textobject in kpresenter will always show + // a shadowed text but no shadow for the object itself. + + // make sure this is a textobject or textspan + if ( !e.hasAttribute( "type" ) || + ( e.hasAttribute( "type" ) && e.attribute( "type" ) == "4" ) ) + { + if ( m_styleStack.hasAttributeNS( ooNS::fo, "text-shadow" ) && + m_styleStack.attributeNS( ooNS::fo, "text-shadow" ) != "none" ) + { + // use the shadow attribute to indicate a text-shadow + TQDomElement shadow = doc.createElement( "SHADOW" ); + TQString distance = m_styleStack.attributeNS( ooNS::fo, "text-shadow" ); + distance.truncate( distance.find( ' ' ) ); + shadow.setAttribute( "distance", KoUnit::parseValue( distance ) ); + shadow.setAttribute( "direction", 5 ); + shadow.setAttribute( "color", "#a0a0a0" ); + e.appendChild( shadow ); + } + } + else if ( m_styleStack.hasAttributeNS( ooNS::draw, "shadow" ) && + m_styleStack.attributeNS( ooNS::draw, "shadow" ) == "visible" ) + { + // use the shadow attribute to indicate an object-shadow + TQDomElement shadow = doc.createElement( "SHADOW" ); + double x = KoUnit::parseValue( m_styleStack.attributeNS( ooNS::draw, "shadow-offset-x" ) ); + double y = KoUnit::parseValue( m_styleStack.attributeNS( ooNS::draw, "shadow-offset-y" ) ); + + if ( x < 0 && y < 0 ) + { + shadow.setAttribute( "direction", 1 ); + shadow.setAttribute( "distance", (int) fabs ( x ) ); + } + else if ( x == 0 && y < 0 ) + { + shadow.setAttribute( "direction", 2 ); + shadow.setAttribute( "distance", (int) fabs ( y ) ); + } + else if ( x > 0 && y < 0 ) + { + shadow.setAttribute( "direction", 3 ); + shadow.setAttribute( "distance", (int) fabs ( x ) ); + } + else if ( x > 0 && y == 0 ) + { + shadow.setAttribute( "direction", 4 ); + shadow.setAttribute( "distance", (int) fabs ( x ) ); + } + else if ( x > 0 && y > 0 ) + { + shadow.setAttribute( "direction", 5 ); + shadow.setAttribute( "distance", (int) fabs ( x ) ); + } + else if ( x == 0 && y > 0 ) + { + shadow.setAttribute( "direction", 6 ); + shadow.setAttribute( "distance", (int) fabs ( y ) ); + } + else if ( x < 0 && y > 0 ) + { + shadow.setAttribute( "direction", 7 ); + shadow.setAttribute( "distance", (int) fabs ( x ) ); + } + else if ( x < 0 && y == 0 ) + { + shadow.setAttribute( "direction", 8 ); + shadow.setAttribute( "distance", (int) fabs ( x ) ); + } + + if ( m_styleStack.hasAttributeNS( ooNS::draw, "shadow-color" ) ) + shadow.setAttribute( "color", m_styleStack.attributeNS( ooNS::draw, "shadow-color" ) ); + + e.appendChild( shadow ); + } + if ( m_styleStack.hasAttributeNS( ooNS::draw, "size-protect" ) || m_styleStack.hasAttributeNS( ooNS::draw, "move-protect" ) ) + { + bool b = ( m_styleStack.attributeNS( ooNS::draw, "size-protect" ) == "true" ) || ( m_styleStack.attributeNS( ooNS::draw, "move-protect" ) == "true" ); + if ( b ) + { + TQDomElement protect = doc.createElement( "PROTECT" ); + protect.setAttribute("state" , b); + e.appendChild(protect); + } + } +} + +void OoImpressImport::appendLineEnds( TQDomDocument& doc, TQDomElement& e, bool orderEndStartLine) +{ + const char* attr = orderEndStartLine ? "marker-start" : "marker-end"; + if ( m_styleStack.hasAttributeNS( ooNS::draw, attr ) ) + { + TQDomElement lineBegin = doc.createElement( "LINEBEGIN" ); + TQString type = m_styleStack.attributeNS( ooNS::draw, attr ); + if ( type == "Arrow" || type == "Small Arrow" || type == "Rounded short Arrow" || + type == "Symmetric Arrow" || type == "Rounded large Arrow" || type == "Arrow concave" ) + lineBegin.setAttribute( "value", 1 ); + else if ( type == "Square" ) + lineBegin.setAttribute( "value", 2 ); + else if ( type == "Circle" || type == "Square 45" ) + lineBegin.setAttribute( "value", 3 ); + else if ( type == "Line Arrow" ) + lineBegin.setAttribute( "value", 4 ); + else if ( type == "Dimension Lines" ) + lineBegin.setAttribute( "value", 5 ); + else if ( type == "Double Arrow" ) + lineBegin.setAttribute( "value", 6 ); + e.appendChild( lineBegin ); + } + attr = orderEndStartLine ? "marker-end" : "marker-start"; + if ( m_styleStack.hasAttributeNS( ooNS::draw, attr ) ) + { + TQDomElement lineEnd = doc.createElement( "LINEEND" ); + TQString type = m_styleStack.attributeNS( ooNS::draw, attr ); + if ( type == "Arrow" || type == "Small Arrow" || type == "Rounded short Arrow" || + type == "Symmetric Arrow" || type == "Rounded large Arrow" || type == "Arrow concave" ) + lineEnd.setAttribute( "value", 1 ); + else if ( type == "Square" ) + lineEnd.setAttribute( "value", 2 ); + else if ( type == "Circle" || type == "Square 45" ) + lineEnd.setAttribute( "value", 3 ); + else if ( type == "Line Arrow" ) + lineEnd.setAttribute( "value", 4 ); + else if ( type == "Dimension Lines" ) + lineEnd.setAttribute( "value", 5 ); + else if ( type == "Double Arrow" ) + lineEnd.setAttribute( "value", 6 ); + e.appendChild( lineEnd ); + } +} + +void OoImpressImport::appendTextObjectMargin( TQDomDocument& /*doc*/, TQDomElement& e ) +{ + if ( m_styleStack.hasAttributeNS( ooNS::fo, "padding" ) ) + { + double tmpValue = KoUnit::parseValue(m_styleStack.attributeNS( ooNS::fo, "padding" ) ); + e.setAttribute( "btoppt", tmpValue ); + e.setAttribute( "bbottompt", tmpValue ); + e.setAttribute( "bleftpt", tmpValue ); + e.setAttribute( "brightpt", tmpValue ); + } + else + { + if( m_styleStack.hasAttributeNS( ooNS::fo, "padding-top" ) ) + e.setAttribute( "btoppt", KoUnit::parseValue( m_styleStack.attributeNS( ooNS::fo, "padding-top" ) ) ); + if( m_styleStack.hasAttributeNS( ooNS::fo, "padding-bottom" ) ) + e.setAttribute( "bbottompt", KoUnit::parseValue( m_styleStack.attributeNS( ooNS::fo, "padding-bottom" ) ) ); + if( m_styleStack.hasAttributeNS( ooNS::fo, "padding-left" ) ) + e.setAttribute( "bleftpt", KoUnit::parseValue( m_styleStack.attributeNS( ooNS::fo, "padding-left" ) ) ); + if( m_styleStack.hasAttributeNS( ooNS::fo, "padding-right" ) ) + e.setAttribute( "brightpt", KoUnit::parseValue( m_styleStack.attributeNS( ooNS::fo, "padding-right" ) ) ); + } +} + +TQDomElement OoImpressImport::parseTextBox( TQDomDocument& doc, const TQDomElement& textBox ) +{ + TQDomElement textObjectElement = doc.createElement( "TEXTOBJ" ); + appendTextObjectMargin( doc, textObjectElement ); + + // vertical alignment + if ( m_styleStack.hasAttributeNS( ooNS::draw, "textarea-vertical-align" ) ) + { + TQString alignment = m_styleStack.attributeNS( ooNS::draw, "textarea-vertical-align" ); + if ( alignment == "top" ) + textObjectElement.setAttribute( "verticalAlign", "top" ); + else if ( alignment == "middle" ) + textObjectElement.setAttribute( "verticalAlign", "center" ); + else if ( alignment == "bottom" ) + textObjectElement.setAttribute( "verticalAlign", "bottom" ); + + textObjectElement.setAttribute("verticalValue", 0.0); + } + + parseParagraphs( doc, textObjectElement, textBox ); + + return textObjectElement; +} + +void OoImpressImport::parseParagraphs( TQDomDocument& doc, TQDomElement& textObjectElement, const TQDomElement& parent ) +{ + TQDomElement t; + forEachElement( t, parent ) + { + 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 + e = parseParagraph( doc, t ); + else if ( isTextNS && localName == "h" ) // heading - can this happen in ooimpress? + { + e = parseParagraph( doc, t ); + } + else if ( isTextNS && ( localName == "unordered-list" || localName == "ordered-list" ) ) + { + parseList( doc, textObjectElement, t ); + m_styleStack.restore(); + continue; + } + // TODO text:sequence-decls + else + { + kdDebug(30518) << "Unsupported texttype '" << localName << "'" << endl; + } + + if ( !e.isNull() ) + textObjectElement.appendChild( e ); + m_styleStack.restore(); // remove the styles added by the paragraph or list + } +} + +void OoImpressImport::applyListStyle( TQDomElement& paragraph ) +{ + // Spec: see 3.3.5 p137 + if ( m_listStyleStack.hasListStyle() && m_nextItemIsListItem ) { + //const TQDomElement listStyle = m_listStyleStack.currentListStyle(); + //bool heading = paragraph.localName() == "h"; + m_nextItemIsListItem = false; + /*int level = heading ? paragraph.attributeNS( ooNS::text, "level", TQString() ).toInt() + : m_listStyleStack.level();*/ + + TQDomElement counter = paragraph.ownerDocument().createElement( "COUNTER" ); + counter.setAttribute( "numberingtype", 0 ); + counter.setAttribute( "depth", 0 ); + + if ( m_insideOrderedList ) + counter.setAttribute( "type", 1 ); + else + counter.setAttribute( "type", 10 ); // a disc bullet + paragraph.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 OoImpressImport::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 OoImpressImport::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 OoImpressImport::parseList( TQDomDocument& doc, TQDomElement& textObjectElement, const TQDomElement& list ) +{ + //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. + parseParagraphs( doc, textObjectElement, listItem ); + m_restartNumbering = -1; + } + if ( listOK ) + m_listStyleStack.pop(); + m_currentListStyleName = oldListStyleName; +} + +TQDomElement OoImpressImport::parseParagraph( TQDomDocument& doc, const TQDomElement& paragraph ) +{ + TQDomElement p = doc.createElement( "P" ); + + // parse the paragraph-properties + fillStyleStack( paragraph ); + + // Style name + TQString styleName = m_styleStack.userStyleName("paragraph"); + if ( !styleName.isEmpty() ) + { + TQDomElement nameElem = doc.createElement("NAME"); + nameElem.setAttribute("value", styleName); + p.appendChild(nameElem); + } + + // Paragraph alignment + if ( m_styleStack.hasAttributeNS( ooNS::fo, "text-align" ) ) + { + TQString align = m_styleStack.attributeNS( ooNS::fo, "text-align" ); + if ( align == "center" ) + p.setAttribute( "align", 4 ); + else if ( align == "justify" ) + p.setAttribute( "align", 8 ); + else if ( align == "start" ) + p.setAttribute( "align", 0 ); + else if ( align == "end" ) + p.setAttribute( "align", 2 ); + } + else + p.setAttribute( "align", 0 ); // use left aligned as default + + + // Offset before and after paragraph + OoUtils::importTopBottomMargin( p, m_styleStack ); + + // Indentation (margins) + OoUtils::importIndents( p, m_styleStack ); + + // Line spacing + OoUtils::importLineSpacing( p, m_styleStack ); + + // Tabulators + OoUtils::importTabulators( p, m_styleStack ); + + // Borders + OoUtils::importBorders( p, m_styleStack ); + + applyListStyle( p ); + + uint pos = 0; + + m_styleStack.save(); + // parse every childnode of the paragraph + parseSpanOrSimilar( doc, paragraph, p, pos); + m_styleStack.restore(); // remove possible garbage (should not be needed) + + return p; +} + +void OoImpressImport::parseSpanOrSimilar( TQDomDocument& doc, const TQDomElement& parent, + TQDomElement& outputParagraph, uint& pos) +{ + // Parse every child node of the parent + // Can't use forEachElement here since we also care about text nodes + for( TQDomNode node = parent.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(); + + // Try to keep the order of the tag names by probability of happening + if ( isTextNS && localName == "span" ) // text:span + { + m_styleStack.save(); + fillStyleStack( ts ); + parseSpanOrSimilar( doc, ts, outputParagraph, pos); + m_styleStack.restore(); + } + else if ( isTextNS && localName == "s" ) // text:s + { + textData = OoUtils::expandWhitespace(ts); + } + else if ( isTextNS && localName == "tab-stop" ) // text:tab-stop + { + // KPresenter 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'; + } + else if ( isTextNS && localName == "line-break" ) + { + textData = '\n'; + } + else if ( localName == "image" && ns == ooNS::draw ) + { + textData = '#'; // anchor placeholder + // TODO + } + 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, pos); + } + else + { +#if 0 // TODO + // The problem is that KPresenter'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); + appendVariable(doc, ts, pos, "STRING", 9, text, linkElement); +#endif + } + m_styleStack.restore(); + } + else if ( isTextNS && + (localName == "date" // fields + || localName == "time" + || localName == "page-number" + || localName == "file-name" + || localName == "author-name" + || localName == "author-initials" ) ) + { + textData = "#"; // field placeholder + appendField(doc, outputParagraph, ts, pos); + } + else if ( t.isNull() ) // no textnode, we must ignore + { + kdWarning(30518) << "Ignoring tag " << ts.tagName() << endl; + continue; + } + else + textData = t.data(); + + pos += textData.length(); + + TQDomElement text = saveHelper(textData, doc); + + kdDebug(30518) << k_funcinfo << "Para text is: " << textData << endl; + + if (m_styleStack.hasAttributeNS( ooNS::fo, "language" )) { + TQString lang = m_styleStack.attributeNS( ooNS::fo, "language" ); + if (lang=="en") + text.setAttribute("language", "en_US"); + else + text.setAttribute("language", lang); + } + + // parse the text-properties + if ( m_styleStack.hasAttributeNS( ooNS::fo, "color" ) ) { + kdDebug(30518) << "color=" << m_styleStack.attributeNS( ooNS::fo, "color" ) << endl; + text.setAttribute( "color", m_styleStack.attributeNS( ooNS::fo, "color" ) ); + } + if ( m_styleStack.hasAttributeNS( ooNS::fo, "font-family" ) // 3.10.9 + || m_styleStack.hasAttributeNS( ooNS::style, "font-name") )//3.10.8 + { + // 'Thorndale/Albany' are not known outside OpenOffice so we substitute them + // with 'Times New Roman/Arial' that look nearly the same. + if ( m_styleStack.attributeNS( ooNS::fo, "font-family" ) == "Thorndale" ) + text.setAttribute( "family", "Times New Roman" ); + else if ( m_styleStack.attributeNS( ooNS::fo, "font-family" ) == "Albany" ) + text.setAttribute( "family", "Arial" ); + else + text.setAttribute( "family", m_styleStack.attributeNS( ooNS::fo, "font-family" ).remove( "'" ) ); + } + if ( m_styleStack.hasAttributeNS( ooNS::fo, "font-size" ) ) + { + double pointSize = m_styleStack.fontSize(); + text.setAttribute( "pointSize", tqRound(pointSize) ); // KPresenter uses toInt()! + } + if ( m_styleStack.hasAttributeNS( ooNS::fo, "font-weight" ) ) // 3.10.24 + if ( m_styleStack.attributeNS( ooNS::fo, "font-weight" ) == "bold" ) + text.setAttribute( "bold", 1 ); + if ( m_styleStack.hasAttributeNS( ooNS::fo, "font-style" ) ) + if ( m_styleStack.attributeNS( ooNS::fo, "font-style" ) == "italic" ) + text.setAttribute( "italic", 1 ); + + if ( m_styleStack.hasAttributeNS( ooNS::style, "text-position" ) ) // 3.10.17 + { + TQString text_position = m_styleStack.attributeNS( ooNS::style, "text-position"); + TQString value; + TQString relativetextsize; + OoUtils::importTextPosition( text_position, value, relativetextsize ); + text.setAttribute( "VERTALIGN", value ); + if ( !relativetextsize.isEmpty() ) + text.setAttribute( "relativetextsize", relativetextsize ); + } + + bool wordByWord = (m_styleStack.hasAttributeNS( ooNS::fo, "score-spaces"))// 3.10.25 + && (m_styleStack.attributeNS( ooNS::fo, "score-spaces") == "false"); + + // strikeout + if ( m_styleStack.hasAttributeNS( ooNS::style, "text-crossing-out")// 3.10.6 + && m_styleStack.attributeNS( ooNS::style, "text-crossing-out") != "none") + { + TQString strikeOutType = m_styleStack.attributeNS( ooNS::style, "text-crossing-out" ); + if ( strikeOutType =="double-line" ) + { + text.setAttribute( "strikeOut", "double" ); + text.setAttribute( "strikeoutstyleline", "solid" ); + } + else if ( strikeOutType =="thick-line" ) + { + text.setAttribute( "strikeOut", "single-bold" ); + text.setAttribute( "strikeoutstyleline", "solid" ); + } + else //if ( strikeOutType == "single-line" ) //fall back to the default strikeout + { + text.setAttribute( "strikeOut", "single" ); + text.setAttribute( "strikeoutstyleline", "solid" ); + } + + if (wordByWord) + text.setAttribute("wordbyword", 1); + } + + // underlining + 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 ); + TQString underLineColor = m_styleStack.attributeNS( ooNS::style, "text-underline-color" );// 3.10.23 + + text.setAttribute( "value", underline ); + text.setAttribute( "styleline", styleline ); + + if ( !underLineColor.isEmpty() && underLineColor != "font-color" ) + text.setAttribute("underlinecolor", underLineColor); + if ( wordByWord ) + text.setAttribute("wordbyword", 1); + } +#if 0 // strange ooimpress doesn't implement it + // 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 ) + { + text.setAttribute( "fontattribute", "smallcaps" ); + } else + { + // Both KWord/KPresenter and OO use "uppercase" and "lowercase". + // TODO in KWord: "capitalize". + text.setAttribute( "fontattribute", m_styleStack.attributeNS( ooNS::fo, "text-transform" ) ); + } + } +#endif + // background color (property of the paragraph in OOo, of the text in kword/kpresenter) + if (m_styleStack.hasAttributeNS( ooNS::fo, "background-color" )) + { + TQString bgColor = m_styleStack.attributeNS( ooNS::fo, "background-color"); + if (bgColor != "transparent") + text.setAttribute("textbackcolor", bgColor); + } + + appendShadow( doc, outputParagraph ); // this is necessary to take care of shadowed paragraphs + outputParagraph.appendChild( text ); + } // for each text span +} + +void OoImpressImport::createStyleMap( TQDomDocument &docstyles ) +{ + TQDomElement styles = docstyles.documentElement(); + if ( styles.isNull() ) + return; + + TQDomNode fixedStyles = KoDom::namedItemNS( styles, ooNS::office, "styles" ); + if ( !fixedStyles.isNull() ) + { + insertDraws( fixedStyles.toElement() ); + insertStyles( fixedStyles.toElement() ); + insertStylesPresentation( fixedStyles.toElement() ); + } + + TQDomNode automaticStyles = KoDom::namedItemNS( styles, ooNS::office, "automatic-styles" ); + if ( !automaticStyles.isNull() ) + { + insertStyles( automaticStyles.toElement() ); + insertStylesPresentation( automaticStyles.toElement() ); + } + TQDomNode masterStyles = KoDom::namedItemNS( styles, ooNS::office, "master-styles" ); + if ( !masterStyles.isNull() ) + insertStyles( masterStyles.toElement() ); +} + +void OoImpressImport::insertDraws( const TQDomElement& styles ) +{ + TQDomElement e; + forEachElement( e, styles ) + { + if ( !e.hasAttributeNS( ooNS::draw, "name" ) ) + continue; + + TQString name = e.attributeNS( ooNS::draw, "name", TQString() ); + m_draws.insert( name, new TQDomElement( e ) ); + } +} + +void OoImpressImport::insertStyles( const TQDomElement& styles ) +{ + TQDomElement e; + forEachElement( e, styles ) + { + const TQString localName = e.localName(); + const TQString ns = e.namespaceURI(); + if ( !e.hasAttributeNS( ooNS::style, "name" ) ) + continue; + + const TQString name = e.attributeNS( ooNS::style, "name", TQString() ); + 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 + { + m_styles.insert( name, new TQDomElement( e ) ); + kdDebug(30518) << "Style: '" << name << "' loaded " << endl; + } + } +} + +void OoImpressImport::insertStylesPresentation( const TQDomElement& styles ) +{ + TQDomElement e; + forEachElement( e, styles ) + { + if ( !e.hasAttributeNS( ooNS::style, "name" ) ) + continue; + + TQString name = e.attributeNS( ooNS::style, "name", TQString() ); + m_stylesPresentation.insert( name, new TQDomElement( e ) ); + //kdDebug(30518) << "Style: '" << name << "' loaded " << endl; + } +} + +void OoImpressImport::fillStyleStack( const TQDomElement& object, bool sticky ) +{ + // find all styles associated with an object and push them on the stack + if ( object.hasAttributeNS( ooNS::presentation, "style-name" ) ) + { + kdDebug(30518)<<" presentation:style-name **************************** :"<<object.attributeNS( ooNS::presentation, "style-name", TQString() )<<endl; + if ( sticky ) + addStyles( m_stylesPresentation[object.attributeNS( ooNS::presentation, "style-name", TQString() )] ); + else + addStyles( m_styles[object.attributeNS( ooNS::presentation, "style-name", TQString() )] ); + } + if ( object.hasAttributeNS( ooNS::draw, "style-name" ) ) + addStyles( m_styles[object.attributeNS( ooNS::draw, "style-name", TQString() )] ); + + if ( object.hasAttributeNS( ooNS::draw, "text-style-name" ) ) + addStyles( m_styles[object.attributeNS( ooNS::draw, "text-style-name", TQString() )] ); + + if ( object.hasAttributeNS( ooNS::text, "style-name" ) ) { + TQString styleName = object.attributeNS( ooNS::text, "style-name", TQString() ); + //kdDebug(30518) << "adding style " << styleName << endl; + addStyles( m_styles[styleName] ); + } +} + +void OoImpressImport::addStyles( const TQDomElement* style ) +{ + kdDebug(30518)<<" addStyle :" << style->attributeNS( ooNS::style, "name", TQString() ) <<endl; + // this function is necessary as parent styles can have parents themself + if ( style->hasAttributeNS( ooNS::style, "parent-style-name" ) ) + { + //kdDebug(30518)<<"m_styles[style->attribute( style:parent-style-name )] :"<<m_styles[style->attributeNS( ooNS::style, "parent-style-name", TQString() )]<<endl; + addStyles( m_styles[style->attributeNS( ooNS::style, "parent-style-name", TQString() )] ); + } + //kdDebug(30518)<<" void OoImpressImport::addStyles( const TQDomElement* style ) :"<<style<<endl; + m_styleStack.push( *style ); +} + +TQString OoImpressImport::storeImage( const TQDomElement& object ) +{ + // store the picture + TQString url = object.attributeNS( ooNS::xlink, "href", TQString() ).remove( '#' ); + KArchiveFile* file = (KArchiveFile*) m_zip->directory()->entry( url ); + + TQString extension = url.mid( url.find( '.' ) ); + TQString fileName = TQString( "picture%1" ).arg( m_numPicture++ ) + extension; + KoStoreDevice* out = m_chain->storageFile( "pictures/" + fileName, KoStore::Write ); + + if ( file && out ) + { + TQByteArray buffer = file->data(); + out->writeBlock( buffer.data(), buffer.size() ); + } + + return fileName; +} + +TQString OoImpressImport::storeSound(const TQDomElement & object, TQDomElement & p, TQDomDocument & doc) +{ + TQFileInfo fi(m_chain->inputFile()); // handle relative URLs + TQDir::setCurrent(fi.dirPath(true)); + fi.setFile(object.attributeNS( ooNS::xlink, "href", TQString())); + TQString url = fi.absFilePath(); + + //kdDebug(30518) << "Sound URL: " << url << endl; + + TQFile file(url); + if (!file.exists()) + return TQString(); + + TQString extension = url.mid( url.find( '.' ) ); + TQString fileName = TQString( "sound%1" ).arg( m_numSound++ ) + extension; + fileName = "sounds/" + fileName; + KoStoreDevice* out = m_chain->storageFile( fileName, KoStore::Write ); + + if (out) + { + if (!file.open(IO_ReadOnly)) + return TQString(); + + TQByteArray data(8*1024); + + uint total = 0; + for ( int block = 0; ( block = file.readBlock(data.data(), data.size()) ) > 0; + total += block ) + out->writeBlock(data.data(), data.size()); + + Q_ASSERT(total == fi.size()); + + file.close(); + } + else + return TQString(); + + TQDomElement key = doc.createElement("FILE"); + key.setAttribute("name", fileName); + key.setAttribute("filename", url); + p.appendChild(key); + + return url; +} + +TQDomElement OoImpressImport::saveHelper(const TQString &tmpText, TQDomDocument &doc) +{ + TQDomElement element=doc.createElement("TEXT"); + + if(tmpText.stripWhiteSpace().isEmpty()) // ### careful, this also strips \t and \n .... + // working around a bug in TQDom + element.setAttribute("whitespace", tmpText.length()); + + element.appendChild(doc.createTextNode(tmpText)); + return element; +} + +void OoImpressImport::appendPoints(TQDomDocument& doc, TQDomElement& e, const TQDomElement& object) +{ + TQDomElement ptsElem = doc.createElement("POINTS"); + + TQStringList ptList = TQStringList::split(' ', object.attributeNS( ooNS::draw, "points", TQString())); + + TQString pt_x, pt_y; + double tmp_x, tmp_y; + for (TQStringList::Iterator it = ptList.begin(); it != ptList.end(); ++it) + { + TQDomElement point = doc.createElement("Point"); + + tmp_x = (*it).section(',',0,0).toInt() / 100; + tmp_y = (*it).section(',',1,1).toInt() / 100; + + pt_x.setNum(tmp_x); + pt_x+="mm"; + + pt_y.setNum(tmp_y); + pt_y+="mm"; + + point.setAttribute("point_x", KoUnit::parseValue(pt_x)); + point.setAttribute("point_y", KoUnit::parseValue(pt_y)); + ptsElem.appendChild(point); + } + + e.appendChild(ptsElem); +} + +void OoImpressImport::appendField(TQDomDocument& doc, TQDomElement& e, const TQDomElement& object, uint pos) +{ + const TQString tag = object.localName(); + const TQString ns = object.namespaceURI(); + const bool isTextNS = ns == ooNS::text; + + TQDomElement custom = doc.createElement("CUSTOM"); + custom.setAttribute("pos", pos); + TQDomElement variable = doc.createElement("VARIABLE"); + + if (isTextNS && tag == "date") + { + 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::currentDateTime(); // OOo docs say so :) + fixed = false; + } + + TQDomElement typeElem = doc.createElement("TYPE"); + typeElem.setAttribute("key", "DATE0locale"); // ### find out the correlation between KOffice and OOo date/time types + typeElem.setAttribute("type", 0); // VT_DATE + typeElem.setAttribute("text", object.text()); + + variable.appendChild(typeElem); + + const TQDate date(dt.date()); + const TQTime time(dt.time()); + TQDomElement dateElement = doc.createElement("DATE"); + dateElement.setAttribute("subtype", fixed ? 0 : 1); // VST_DATE_FIX, VST_DATE_CURRENT + dateElement.setAttribute("fix", fixed ? 1 : 0); + 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())); + + variable.appendChild(dateElement); + } + else if (isTextNS && tag == "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::currentDateTime(); // OOo docs say so :) + fixed = false; + } + + TQDomElement typeElem = doc.createElement("TYPE"); + typeElem.setAttribute("key", "TIMElocale"); // ### find out the correlation between KOffice and OOo date/time types + typeElem.setAttribute("type", 2); // VT_TIME + typeElem.setAttribute("text", object.text()); + + variable.appendChild(typeElem); + + const TQTime time(dt.time()); + TQDomElement timeElement = doc.createElement("TIME"); + timeElement.setAttribute("subtype", fixed ? 0 : 1); // VST_TIME_FIX, VST_TIME_CURRENT + 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 + + variable.appendChild(timeElement); + } + else if (isTextNS && tag == "page-number") + { + TQDomElement typeElem = doc.createElement("TYPE"); + typeElem.setAttribute("key", "NUMBER"); + typeElem.setAttribute("type", 4); // VT_PGNUM + typeElem.setAttribute("text", object.text()); + + variable.appendChild(typeElem); + + TQDomElement pgNumElem = doc.createElement("PGNUM"); + + int 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 + else + subtype = 0; // VST_PGNUM_CURRENT + } + + pgNumElem.setAttribute("subtype", subtype); + pgNumElem.setAttribute("value", object.text()); + + variable.appendChild(pgNumElem); + } + else if (isTextNS && tag == "file-name") + { + TQDomElement typeElem = doc.createElement("TYPE"); + typeElem.setAttribute("key", "STRING"); + typeElem.setAttribute("type", 8); // VT_FIELD + typeElem.setAttribute("text", object.text()); + + variable.appendChild(typeElem); + + int 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 fileNameElem = doc.createElement("FIELD"); + fileNameElem.setAttribute("subtype", subtype); + fileNameElem.setAttribute("value", object.text()); + + variable.appendChild(fileNameElem); + } + else if (isTextNS && tag == "author-name" + || isTextNS && tag == "author-initials") + { + TQDomElement typeElem = doc.createElement("TYPE"); + typeElem.setAttribute("key", "STRING"); + typeElem.setAttribute("type", 8); // VT_FIELD + typeElem.setAttribute("text", object.text()); + + variable.appendChild(typeElem); + + int subtype = 2; // VST_AUTHORNAME + + if (isTextNS && tag == "author-initials") + subtype = 16; // VST_INITIAL + + TQDomElement authorElem = doc.createElement("FIELD"); + authorElem.setAttribute("subtype", subtype); + authorElem.setAttribute("value", object.text()); + + variable.appendChild(authorElem); + } + + custom.appendChild(variable); + e.appendChild(custom); +} + +void OoImpressImport::createPresentationAnimation(const TQDomElement& element) +{ + int order = 0; + TQDomElement e; + forEachElement( e, element ) + { + const TQString localName = e.localName(); + const TQString ns = e.namespaceURI(); + if ( ns == ooNS::presentation && localName == "show-shape" && e.hasAttributeNS( ooNS::draw, "shape-id" ) ) + { + TQString name = e.attributeNS( ooNS::draw, "shape-id", TQString() ); + //kdDebug(30518)<<" insert animation style : name :"<<name<<endl; + animationList *lst = new animationList; + TQDomElement* ep = new TQDomElement( e ); + lst->element = ep; + lst->order = order; + m_animations.insert( name, lst ); + ++order; + } + } +} + +TQDomElement OoImpressImport::findAnimationByObjectID(const TQString & id, int & order) +{ + kdDebug(30518)<<"TQDomElement OoImpressImport::findAnimationByObjectID(const TQString & id) :"<<id<<endl; + if (m_animations.isEmpty() ) + return TQDomElement(); + + animationList *animation = m_animations[id]; + //kdDebug(30518)<<"TQDomElement *animation = m_animations[id]; :"<<animation<<endl; + if ( !animation ) + return TQDomElement(); + for (TQDomNode node = *( animation->element ); !node.isNull(); node = node.nextSibling()) + { + TQDomElement e = node.toElement(); + order = animation->order; + kdDebug(30518)<<"e.tagName() :"<<e.tagName()<<" e.attribute(draw:shape-id) :"<<e.attributeNS( ooNS::draw, "shape-id", TQString())<<endl; + if (e.tagName()=="presentation:show-shape" && e.attributeNS( ooNS::draw, "shape-id", TQString())==id) + return e; + } + + return TQDomElement(); +} + + +void OoImpressImport::appendObjectEffect(TQDomDocument& doc, TQDomElement& e, const TQDomElement& object, + TQDomElement& sound) +{ + int order = 0; + TQDomElement origEffect = findAnimationByObjectID(object.attributeNS( ooNS::draw, "id", TQString()), order).toElement(); + + if (origEffect.isNull()) + return; + + TQString effect = origEffect.attributeNS( ooNS::presentation, "effect", TQString()); + TQString dir = origEffect.attributeNS( ooNS::presentation, "direction", TQString()); + TQString speed = origEffect.attributeNS( ooNS::presentation, "speed", TQString()); + kdDebug(30518)<<"speed :"<<speed<<endl; + //todo implement speed value. + + int effVal=0; + //kdDebug(30518)<<" effect :"<<effect<<" dir :"<<dir<<endl; + if (effect=="fade") + { + if (dir=="from-right") + effVal=10; // EF_WIPE_RIGHT + else if (dir=="from-left") + effVal=9; // EF_WIPE_LEFT + else if (dir=="from-top") + effVal=11; // EF_WIPE_TOP + else if (dir=="from-bottom") + effVal=12; // EF_WIPE_BOTTOM + else + return; + } + else if (effect=="move") + { + if (dir=="from-right") + effVal=1; // EF_COME_RIGHT + else if (dir=="from-left") + effVal=2; // EF_COME_LEFT + else if (dir=="from-top") + effVal=3; // EF_COME_TOP + else if (dir=="from-bottom") + effVal=4; // EF_COME_BOTTOM + else if (dir=="from-upper-right") + effVal=5; // EF_COME_RIGHT_TOP + else if (dir=="from-lower-right") + effVal=6; // EF_COME_RIGHT_BOTTOM + else if (dir=="from-upper-left") + effVal=7; // EF_COME_LEFT_TOP + else if (dir=="from-lower-left") + effVal=8; // EF_COME_LEFT_BOTTOM + else + return; + } + else + return; // sorry, no more supported effects :( + + TQDomElement effElem = doc.createElement("EFFECTS"); + effElem.setAttribute("effect", effVal); + e.appendChild(effElem); + + TQDomElement presNum = doc.createElement( "PRESNUM" ); + presNum.setAttribute("value", order); + e.appendChild( presNum ); + + // sound effect + TQDomElement origSoundEff = KoDom::namedItemNS( origEffect, ooNS::presentation, "sound"); + if (!origSoundEff.isNull()) + { + TQString soundUrl = storeSound(origSoundEff, sound, doc); + + if (!soundUrl.isNull()) + { + TQDomElement pseElem = doc.createElement("APPEARSOUNDEFFECT"); + pseElem.setAttribute("appearSoundEffect", 1); + pseElem.setAttribute("appearSoundFileName", soundUrl); + + e.appendChild(pseElem); + } + } +} + +#include "ooimpressimport.moc" |