summaryrefslogtreecommitdiffstats
path: root/filters/karbon/svg/svgexport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filters/karbon/svg/svgexport.cpp')
-rw-r--r--filters/karbon/svg/svgexport.cpp512
1 files changed, 512 insertions, 0 deletions
diff --git a/filters/karbon/svg/svgexport.cpp b/filters/karbon/svg/svgexport.cpp
new file mode 100644
index 000000000..41fd6a542
--- /dev/null
+++ b/filters/karbon/svg/svgexport.cpp
@@ -0,0 +1,512 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002, 2003 The Karbon Developers
+
+ 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 <tqcstring.h>
+#include <tqdom.h>
+#include <tqfile.h>
+#include <tqstring.h>
+#include <tqvaluelist.h>
+
+#include <kgenericfactory.h>
+#include <KoFilter.h>
+#include <KoFilterChain.h>
+#include <KoStore.h>
+
+#include "svgexport.h"
+#include "vcolor.h"
+#include "vcomposite.h"
+#include "vdashpattern.h"
+#include "vdocument.h"
+#include "vfill.h"
+#include "vgradient.h"
+#include "vgroup.h"
+#include "vimage.h"
+#include "vlayer.h"
+#include "vpath.h"
+#include "vpattern.h"
+#include "vsegment.h"
+#include "vselection.h"
+#include "vstroke.h"
+#include "vtext.h"
+#include <commands/vtransformcmd.h>
+
+#include <kdebug.h>
+
+TQString INDENT(" ");
+
+void
+printIndentation( TQTextStream *stream, unsigned int indent )
+{
+ for( unsigned int i = 0; i < indent;++i)
+ *stream << INDENT;
+}
+
+typedef KGenericFactory<SvgExport, KoFilter> SvgExportFactory;
+K_EXPORT_COMPONENT_FACTORY( libkarbonsvgexport, SvgExportFactory( "kofficefilters" ) )
+
+
+SvgExport::SvgExport( KoFilter*, const char*, const TQStringList& )
+ : KoFilter(), m_indent( 0 ), m_indent2( 0 ), m_trans( 0L )
+{
+ m_gc.setAutoDelete( true );
+}
+
+KoFilter::ConversionStatus
+SvgExport::convert( const TQCString& from, const TQCString& to )
+{
+ if ( to != "image/svg+xml" || from != "application/x-karbon" )
+ {
+ return KoFilter::NotImplemented;
+ }
+
+ KoStoreDevice* storeIn = m_chain->storageFile( "root", KoStore::Read );
+ if( !storeIn )
+ return KoFilter::StupidError;
+
+ TQFile fileOut( m_chain->outputFile() );
+ if( !fileOut.open( IO_WriteOnly ) )
+ {
+ delete storeIn;
+ return KoFilter::StupidError;
+ }
+
+ TQDomDocument domIn;
+ domIn.setContent( storeIn );
+ TQDomElement docNode = domIn.documentElement();
+
+ m_stream = new TQTextStream( &fileOut );
+ TQString body;
+ m_body = new TQTextStream( &body, IO_ReadWrite );
+ TQString defs;
+ m_defs = new TQTextStream( &defs, IO_ReadWrite );
+
+ // load the document and export it:
+ VDocument doc;
+ doc.load( docNode );
+ doc.accept( *this );
+
+ *m_stream << defs;
+ *m_stream << body;
+
+ fileOut.close();
+
+ delete m_stream;
+ delete m_defs;
+ delete m_body;
+
+ return KoFilter::OK;
+}
+
+void
+SvgExport::visitVDocument( VDocument& document )
+{
+ // select all objects:
+ document.selection()->append();
+
+ // get the bounding box of the page
+ KoRect rect( 0, 0, document.width(), document.height() );
+
+ // standard header:
+ *m_defs <<
+ "<?xml version=\"1.0\" standalone=\"no\"?>\n" <<
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" " <<
+ "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">"
+ << endl;
+
+ // add some PR. one line is more than enough.
+ *m_defs <<
+ "<!-- Created using Karbon14, part of koffice: http://www.trinitydesktop.org -->" << endl;
+
+ *m_defs <<
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"" <<
+ rect.width() << "px\" height=\"" << rect.height() << "px\">" << endl;
+ printIndentation( m_defs, ++m_indent2 );
+ *m_defs << "<defs>" << endl;
+
+ m_indent++;
+ m_indent2++;
+
+ // we dont need the selection anymore:
+ document.selection()->clear();
+
+ // set up gc
+ SvgGraphicsContext *gc = new SvgGraphicsContext;
+ m_gc.push( gc );
+
+ TQWMatrix mat;
+ mat.scale( 1, -1 );
+ mat.translate( 0, -document.height() );
+
+ m_trans = new VTransformCmd( 0L, mat, false );
+
+ // export layers:
+ VVisitor::visitVDocument( document );
+
+ delete m_trans;
+ m_trans = 0L;
+
+ // end tag:
+ printIndentation( m_defs, --m_indent2 );
+ *m_defs << "</defs>" << endl;
+ *m_body << "</svg>" << endl;
+}
+
+TQString
+SvgExport::getID( VObject *obj )
+{
+ if( obj && !obj->name().isEmpty() )
+ return TQString( " id=\"%1\"" ).arg( obj->name() );
+ return TQString();
+}
+
+void
+SvgExport::visitVGroup( VGroup& group )
+{
+ printIndentation( m_body, m_indent++ );
+ *m_body << "<g" << getID( &group ) << ">" << endl;
+ VVisitor::visitVGroup( group );
+ printIndentation( m_body, --m_indent );
+ *m_body << "</g>" << endl;
+}
+
+// horrible but at least something gets exported now
+// will need this for patterns
+void
+SvgExport::visitVImage( VImage& image )
+{
+ printIndentation( m_body, m_indent );
+ *m_body << "<image ";
+ VVisitor::visitVImage( image );
+ *m_body << "x=\"" << "\" ";
+ *m_body << "y=\"" << "\" ";
+ *m_body << "width=\"" << "\" ";
+ *m_body << "height=\"" << "\" ";
+ *m_body << "xlink:href=\"" << "\"";
+ *m_body << " />" << endl;
+}
+
+void
+SvgExport::visitVLayer( VLayer& layer )
+{
+ printIndentation( m_body, m_indent++ );
+ *m_body << "<g" << getID( &layer ) << ">" << endl;
+ //*m_body << " transform=\"scale(1, -1) translate(0, -" << layer.document()->height() << ")\">" << endl;
+ VVisitor::visitVLayer( layer );
+ printIndentation( m_body, --m_indent );
+ *m_body << "</g>" << endl;
+}
+
+void
+SvgExport::writePathToStream( VPath &composite, const TQString &id, TQTextStream *stream, unsigned int indent )
+{
+ if( ! stream )
+ return;
+
+ printIndentation( stream, indent );
+ *stream << "<path" << id;
+
+ VVisitor::visitVPath( composite );
+
+ getFill( *( composite.fill() ), stream );
+ getStroke( *( composite.stroke() ), stream );
+
+ TQString d;
+ composite.saveSvgPath( d );
+ *stream << " d=\"" << d << "\" ";
+
+ if( composite.fillRule() != m_gc.current()->fillRule )
+ {
+ if( composite.fillRule() == evenOdd )
+ *stream << " fill-rule=\"evenodd\"";
+ else
+ *stream << " fill-rule=\"nonzero\"";
+ }
+
+ *stream << " />" << endl;
+}
+
+void
+SvgExport::visitVPath( VPath& composite )
+{
+ m_trans->visitVPath( composite );
+ writePathToStream( composite, getID( &composite ), m_body, m_indent );
+ m_trans->visitVPath( composite );
+}
+
+void
+SvgExport::visitVSubpath( VSubpath& )
+{
+}
+
+TQString createUID()
+{
+ static unsigned int nr = 0;
+
+ return "defitem" + TQString().setNum( nr++ );
+}
+
+void
+SvgExport::getColorStops( const TQPtrVector<VColorStop> &colorStops )
+{
+ m_indent2++;
+ for( unsigned int i = 0; i < colorStops.count() ; i++ )
+ {
+ printIndentation( m_defs, m_indent2 );
+ *m_defs << "<stop stop-color=\"";
+ getHexColor( m_defs, colorStops.at( i )->color );
+ *m_defs << "\" offset=\"" << TQString().setNum( colorStops.at( i )->rampPoint );
+ *m_defs << "\" stop-opacity=\"" << colorStops.at( i )->color.opacity() << "\"" << " />" << endl;
+ }
+ m_indent2--;
+}
+
+void
+SvgExport::getGradient( const VGradient& grad )
+{
+ TQString uid = createUID();
+ if( grad.type() == VGradient::linear )
+ {
+ printIndentation( m_defs, m_indent2 );
+ // do linear grad
+ *m_defs << "<linearGradient id=\"" << uid << "\" ";
+ *m_defs << "gradientUnits=\"userSpaceOnUse\" ";
+ *m_defs << "x1=\"" << grad.origin().x() << "\" ";
+ *m_defs << "y1=\"" << grad.origin().y() << "\" ";
+ *m_defs << "x2=\"" << grad.vector().x() << "\" ";
+ *m_defs << "y2=\"" << grad.vector().y() << "\" ";
+ if( grad.repeatMethod() == VGradient::reflect )
+ *m_defs << "spreadMethod=\"reflect\" ";
+ else if( grad.repeatMethod() == VGradient::repeat )
+ *m_defs << "spreadMethod=\"repeat\" ";
+ *m_defs << ">" << endl;
+
+ // color stops
+ getColorStops( grad.colorStops() );
+
+ printIndentation( m_defs, m_indent2 );
+ *m_defs << "</linearGradient>" << endl;
+ *m_body << "url(#" << uid << ")";
+ }
+ else if( grad.type() == VGradient::radial )
+ {
+ // do radial grad
+ printIndentation( m_defs, m_indent2 );
+ *m_defs << "<radialGradient id=\"" << uid << "\" ";
+ *m_defs << "gradientUnits=\"userSpaceOnUse\" ";
+ *m_defs << "cx=\"" << grad.origin().x() << "\" ";
+ *m_defs << "cy=\"" << grad.origin().y() << "\" ";
+ *m_defs << "fx=\"" << grad.focalPoint().x() << "\" ";
+ *m_defs << "fy=\"" << grad.focalPoint().y() << "\" ";
+ double r = sqrt( pow( grad.vector().x() - grad.origin().x(), 2 ) + pow( grad.vector().y() - grad.origin().y(), 2 ) );
+ *m_defs << "r=\"" << TQString().setNum( r ) << "\" ";
+ if( grad.repeatMethod() == VGradient::reflect )
+ *m_defs << "spreadMethod=\"reflect\" ";
+ else if( grad.repeatMethod() == VGradient::repeat )
+ *m_defs << "spreadMethod=\"repeat\" ";
+ *m_defs << ">" << endl;
+
+ // color stops
+ getColorStops( grad.colorStops() );
+
+ printIndentation( m_defs, m_indent2 );
+ *m_defs << "</radialGradient>" << endl;
+ *m_body << "url(#" << uid << ")";
+ }
+ // gah! pointless abbreviation of conical to conic
+ else if( grad.type() == VGradient::conic )
+ {
+ // fake conical grad as radial.
+ // fugly but better than data loss.
+ printIndentation( m_defs, m_indent2 );
+ *m_defs << "<radialGradient id=\"" << uid << "\" ";
+ *m_defs << "gradientUnits=\"userSpaceOnUse\" ";
+ *m_defs << "cx=\"" << grad.origin().x() << "\" ";
+ *m_defs << "cy=\"" << grad.origin().y() << "\" ";
+ *m_defs << "fx=\"" << grad.focalPoint().x() << "\" ";
+ *m_defs << "fy=\"" << grad.focalPoint().y() << "\" ";
+ double r = sqrt( pow( grad.vector().x() - grad.origin().x(), 2 ) + pow( grad.vector().y() - grad.origin().y(), 2 ) );
+ *m_defs << "r=\"" << TQString().setNum( r ) << "\" ";
+ if( grad.repeatMethod() == VGradient::reflect )
+ *m_defs << "spreadMethod=\"reflect\" ";
+ else if( grad.repeatMethod() == VGradient::repeat )
+ *m_defs << "spreadMethod=\"repeat\" ";
+ *m_defs << ">" << endl;
+
+ // color stops
+ getColorStops( grad.colorStops() );
+
+ printIndentation( m_defs, m_indent2 );
+ *m_defs << "</radialGradient>" << endl;
+ *m_body << "url(#" << uid << ")";
+ }
+}
+
+// better than nothing
+void
+SvgExport::getPattern( const VPattern & )
+{
+ TQString uid = createUID();
+ printIndentation( m_defs, m_indent2 );
+ *m_defs << "<pattern id=\"" << uid << "\" ";
+ *m_defs << "width=\"" << "\" ";
+ *m_defs << "height=\"" << "\" ";
+ *m_defs << "patternUnits=\"userSpaceOnUse\" ";
+ *m_defs << "patternContentUnits=\"userSpaceOnUse\" ";
+ *m_defs << " />" << endl;
+ // TODO: insert hard work here ;)
+ printIndentation( m_defs, m_indent2 );
+ *m_defs << "</pattern>" << endl;
+ *m_body << "url(#" << uid << ")";
+}
+
+void
+SvgExport::getFill( const VFill& fill, TQTextStream *stream )
+{
+ *stream << " fill=\"";
+ if( fill.type() == VFill::none )
+ *stream << "none";
+ else if( fill.type() == VFill::grad )
+ getGradient( fill.gradient() );
+ else if( fill.type() == VFill::patt )
+ getPattern( fill.pattern() );
+ else
+ getHexColor( stream, fill.color() );
+ *stream << "\"";
+
+ if( fill.color().opacity() != m_gc.current()->fill.color().opacity() )
+ *stream << " fill-opacity=\"" << fill.color().opacity() << "\"";
+}
+
+void
+SvgExport::getStroke( const VStroke& stroke, TQTextStream *stream )
+{
+ if( stroke.type() != m_gc.current()->stroke.type() )
+ {
+ *stream << " stroke=\"";
+ if( stroke.type() == VStroke::none )
+ *stream << "none";
+ else if( stroke.type() == VStroke::grad )
+ getGradient( stroke.gradient() );
+ else
+ getHexColor( stream, stroke.color() );
+ *stream << "\"";
+ }
+
+ if( stroke.color().opacity() != m_gc.current()->stroke.color().opacity() )
+ *stream << " stroke-opacity=\"" << stroke.color().opacity() << "\"";
+
+ if( stroke.lineWidth() != m_gc.current()->stroke.lineWidth() )
+ *stream << " stroke-width=\"" << stroke.lineWidth() << "\"";
+
+ if( stroke.lineCap() != m_gc.current()->stroke.lineCap() )
+ {
+ if( stroke.lineCap() == VStroke::capButt )
+ *stream << " stroke-linecap=\"butt\"";
+ else if( stroke.lineCap() == VStroke::capRound )
+ *stream << " stroke-linecap=\"round\"";
+ else if( stroke.lineCap() == VStroke::capSquare )
+ *stream << " stroke-linecap=\"square\"";
+ }
+
+ if( stroke.lineJoin() != m_gc.current()->stroke.lineJoin() )
+ {
+ if( stroke.lineJoin() == VStroke::joinMiter )
+ {
+ *stream << " stroke-linejoin=\"miter\"";
+ *stream << " stroke-miterlimit=\"" << stroke.miterLimit() << "\"";
+ }
+ else if( stroke.lineJoin() == VStroke::joinRound )
+ *stream << " stroke-linejoin=\"round\"";
+ else if( stroke.lineJoin() == VStroke::joinBevel )
+ *stream << " stroke-linejoin=\"bevel\"";
+ }
+
+ // dash
+ if( stroke.dashPattern().array().count() > 0 )
+ {
+ *stream << " stroke-dashoffset=\"" << stroke.dashPattern().offset() << "\"";
+ *stream << " stroke-dasharray=\" ";
+
+ TQValueListConstIterator<float> itr;
+ for(itr = stroke.dashPattern().array().begin(); itr != stroke.dashPattern().array().end(); ++itr )
+ {
+ *stream << *itr << " ";
+ }
+ *stream << "\"";
+ }
+}
+
+void
+SvgExport::getHexColor( TQTextStream *stream, const VColor& color )
+{
+ // Convert the various color-spaces to hex
+
+ TQString Output;
+
+ VColor copy( color );
+ copy.setColorSpace( VColor::rgb );
+
+ Output.sprintf( "#%02x%02x%02x", int( copy[0] * 255.0 ), int( copy[1] * 255.0 ), int( copy[2] * 255.0 ) );
+
+ *stream << Output;
+}
+
+void
+SvgExport::visitVText( VText& text )
+{
+ VPath path( 0L );
+ path.combinePath( text.basePath() );
+
+ m_trans->visitVPath( path );
+
+ TQString id = createUID();
+ writePathToStream( path, " id=\""+ id + "\"", m_defs, m_indent2 );
+
+ printIndentation( m_body, m_indent++ );
+ *m_body << "<text" << getID( &text );
+ //*m_body << " transform=\"scale(1, -1) translate(0, -" << text.document()->height() << ")\"";
+ getFill( *( text.fill() ), m_body );
+ getStroke( *( text.stroke() ), m_body );
+
+ *m_body << " font-family=\"" << text.font().family() << "\"";
+ *m_body << " font-size=\"" << text.font().pointSize() << "\"";
+ if( text.font().bold() )
+ *m_body << " font-weight=\"bold\"";
+ if( text.font().italic() )
+ *m_body << " font-style=\"italic\"";
+ if( text.alignment() == VText::Center )
+ *m_body << " text-anchor=\"middle\"";
+ else if( text.alignment() == VText::Right )
+ *m_body << " text-anchor=\"end\"";
+
+ *m_body << ">" << endl;
+
+ printIndentation( m_body, m_indent );
+ *m_body << "<textPath xlink:href=\"#" << id << "\"";
+ if( text.offset() > 0.0 )
+ *m_body << " startOffset=\"" << text.offset() * 100.0 << "%\"";
+ *m_body << ">";
+ *m_body << text.text();
+ *m_body << "</textPath>" << endl;
+ printIndentation( m_body, --m_indent );
+ *m_body << "</text>" << endl;
+}
+
+#include "svgexport.moc"
+