filters

svgexport.cc

00001 /* This file is part of the KDE project
00002    Copyright (C) 2002, 2003 The Karbon Developers
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include <qcstring.h>
00021 #include <qdom.h>
00022 #include <qfile.h>
00023 #include <qstring.h>
00024 #include <qvaluelist.h>
00025 
00026 #include <kgenericfactory.h>
00027 #include <KoFilter.h>
00028 #include <KoFilterChain.h>
00029 #include <KoStore.h>
00030 
00031 #include "svgexport.h"
00032 #include "vcolor.h"
00033 #include "vcomposite.h"
00034 #include "vdashpattern.h"
00035 #include "vdocument.h"
00036 #include "vfill.h"
00037 #include "vgradient.h"
00038 #include "vgroup.h"
00039 #include "vimage.h"
00040 #include "vlayer.h"
00041 #include "vpath.h"
00042 #include "vpattern.h"
00043 #include "vsegment.h"
00044 #include "vselection.h"
00045 #include "vstroke.h"
00046 #include "vtext.h"
00047 #include <commands/vtransformcmd.h>
00048 
00049 #include <kdebug.h>
00050 
00051 QString INDENT("  ");
00052 
00053 void
00054 printIndentation( QTextStream *stream, unsigned int indent )
00055 {
00056     for( unsigned int i = 0; i < indent;++i)
00057         *stream << INDENT;
00058 }
00059 
00060 typedef KGenericFactory<SvgExport, KoFilter> SvgExportFactory;
00061 K_EXPORT_COMPONENT_FACTORY( libkarbonsvgexport, SvgExportFactory( "kofficefilters" ) )
00062 
00063 
00064 SvgExport::SvgExport( KoFilter*, const char*, const QStringList& )
00065     : KoFilter(), m_indent( 0 ), m_indent2( 0 ), m_trans( 0L )
00066 {
00067     m_gc.setAutoDelete( true );
00068 }
00069 
00070 KoFilter::ConversionStatus
00071 SvgExport::convert( const QCString& from, const QCString& to )
00072 {
00073     if ( to != "image/svg+xml" || from != "application/x-karbon" )
00074     {
00075         return KoFilter::NotImplemented;
00076     }
00077 
00078     KoStoreDevice* storeIn = m_chain->storageFile( "root", KoStore::Read );
00079     if( !storeIn )
00080         return KoFilter::StupidError;
00081 
00082     QFile fileOut( m_chain->outputFile() );
00083     if( !fileOut.open( IO_WriteOnly ) )
00084     {
00085         delete storeIn;
00086         return KoFilter::StupidError;
00087     }
00088 
00089     QDomDocument domIn;
00090     domIn.setContent( storeIn );
00091     QDomElement docNode = domIn.documentElement();
00092 
00093     m_stream = new QTextStream( &fileOut );
00094     QString body;
00095     m_body = new QTextStream( &body, IO_ReadWrite );
00096     QString defs;
00097     m_defs = new QTextStream( &defs, IO_ReadWrite );
00098 
00099     // load the document and export it:
00100     VDocument doc;
00101     doc.load( docNode );
00102     doc.accept( *this );
00103 
00104     *m_stream << defs;
00105     *m_stream << body;
00106 
00107     fileOut.close();
00108 
00109     delete m_stream;
00110     delete m_defs;
00111     delete m_body;
00112 
00113     return KoFilter::OK;
00114 }
00115 
00116 void
00117 SvgExport::visitVDocument( VDocument& document )
00118 {
00119     // select all objects:
00120     document.selection()->append();
00121 
00122     // get the bounding box of the page
00123     KoRect rect( 0, 0, document.width(), document.height() );
00124 
00125     // standard header:
00126     *m_defs <<
00127         "<?xml version=\"1.0\" standalone=\"no\"?>\n" <<
00128         "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" " <<
00129         "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">"
00130     << endl;
00131 
00132     // add some PR.  one line is more than enough.  
00133     *m_defs <<
00134         "<!-- Created using Karbon14, part of koffice: http://www.koffice.org/karbon -->" << endl;
00135 
00136     *m_defs <<
00137         "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"" <<
00138         rect.width() << "px\" height=\"" << rect.height() << "px\">" << endl;
00139     printIndentation( m_defs, ++m_indent2 );
00140     *m_defs << "<defs>" << endl;
00141 
00142     m_indent++;
00143     m_indent2++;
00144 
00145     // we dont need the selection anymore:
00146     document.selection()->clear();
00147 
00148     // set up gc
00149     SvgGraphicsContext *gc = new SvgGraphicsContext;
00150     m_gc.push( gc );
00151 
00152     QWMatrix mat;
00153     mat.scale( 1, -1 );
00154     mat.translate( 0, -document.height() );
00155 
00156     m_trans = new VTransformCmd( 0L, mat, false );
00157     
00158     // export layers:
00159     VVisitor::visitVDocument( document );
00160 
00161     delete m_trans;
00162     m_trans = 0L;
00163 
00164     // end tag:
00165     printIndentation( m_defs, --m_indent2 );
00166     *m_defs << "</defs>" << endl;
00167     *m_body << "</svg>" << endl;
00168 }
00169 
00170 QString
00171 SvgExport::getID( VObject *obj )
00172 {
00173     if( obj && !obj->name().isEmpty() )
00174         return QString( " id=\"%1\"" ).arg( obj->name() );
00175     return QString();
00176 }
00177 
00178 void
00179 SvgExport::visitVGroup( VGroup& group )
00180 {
00181     printIndentation( m_body, m_indent++ );
00182     *m_body << "<g" << getID( &group ) << ">" << endl;
00183     VVisitor::visitVGroup( group );
00184     printIndentation( m_body, --m_indent );
00185     *m_body << "</g>" << endl;
00186 }
00187 
00188 // horrible but at least something gets exported now
00189 // will need this for patterns
00190 void
00191 SvgExport::visitVImage( VImage& image )
00192 {
00193     printIndentation( m_body, m_indent );
00194     *m_body << "<image ";
00195     VVisitor::visitVImage( image );
00196     *m_body << "x=\"" << "\" ";
00197     *m_body << "y=\"" << "\" ";
00198     *m_body << "width=\"" << "\" ";
00199     *m_body << "height=\"" << "\" ";
00200     *m_body << "xlink:href=\"" << "\"";
00201     *m_body << " />" << endl;
00202 }
00203 
00204 void
00205 SvgExport::visitVLayer( VLayer& layer )
00206 {
00207     printIndentation( m_body, m_indent++ );
00208     *m_body << "<g" << getID( &layer ) << ">" << endl;
00209     //*m_body << " transform=\"scale(1, -1) translate(0, -" << layer.document()->height() << ")\">" << endl;
00210     VVisitor::visitVLayer( layer );
00211     printIndentation( m_body, --m_indent );
00212     *m_body << "</g>" << endl;
00213 }
00214 
00215 void
00216 SvgExport::writePathToStream( VPath &composite, const QString &id, QTextStream *stream, unsigned int indent )
00217 {
00218     if( ! stream )
00219         return;
00220 
00221     printIndentation( stream, indent );
00222     *stream << "<path" << id;
00223 
00224     VVisitor::visitVPath( composite );
00225 
00226     getFill( *( composite.fill() ), stream );
00227     getStroke( *( composite.stroke() ), stream );
00228 
00229     QString d;
00230     composite.saveSvgPath( d );
00231     *stream << " d=\"" << d << "\" ";
00232 
00233     if( composite.fillRule() != m_gc.current()->fillRule )
00234     {
00235         if( composite.fillRule() == evenOdd )
00236             *stream << " fill-rule=\"evenodd\"";
00237         else
00238             *stream << " fill-rule=\"nonzero\"";
00239     }
00240 
00241     *stream << " />" << endl;
00242 }
00243 
00244 void
00245 SvgExport::visitVPath( VPath& composite )
00246 {
00247     m_trans->visitVPath( composite );
00248     writePathToStream( composite, getID( &composite ), m_body, m_indent );
00249     m_trans->visitVPath( composite );
00250 }
00251 
00252 void
00253 SvgExport::visitVSubpath( VSubpath& )
00254 {
00255 }
00256 
00257 QString createUID()
00258 {
00259     static unsigned int nr = 0;
00260 
00261     return "defitem" + QString().setNum( nr++ );
00262 }
00263 
00264 void
00265 SvgExport::getColorStops( const QPtrVector<VColorStop> &colorStops )
00266 {
00267     m_indent2++;
00268     for( unsigned int i = 0; i < colorStops.count() ; i++ )
00269     {
00270         printIndentation( m_defs, m_indent2 );
00271         *m_defs << "<stop stop-color=\"";
00272         getHexColor( m_defs, colorStops.at( i )->color );
00273         *m_defs << "\" offset=\"" << QString().setNum( colorStops.at( i )->rampPoint );
00274         *m_defs << "\" stop-opacity=\"" << colorStops.at( i )->color.opacity() << "\"" << " />" << endl;
00275     }
00276     m_indent2--;
00277 }
00278 
00279 void
00280 SvgExport::getGradient( const VGradient& grad )
00281 {
00282     QString uid = createUID();
00283     if( grad.type() == VGradient::linear )
00284     {
00285         printIndentation( m_defs, m_indent2 );
00286         // do linear grad
00287         *m_defs << "<linearGradient id=\"" << uid << "\" ";
00288         *m_defs << "gradientUnits=\"userSpaceOnUse\" ";
00289         *m_defs << "x1=\"" << grad.origin().x() << "\" ";
00290         *m_defs << "y1=\"" << grad.origin().y() << "\" ";
00291         *m_defs << "x2=\"" << grad.vector().x() << "\" ";
00292         *m_defs << "y2=\"" << grad.vector().y() << "\" ";
00293         if( grad.repeatMethod() == VGradient::reflect )
00294             *m_defs << "spreadMethod=\"reflect\" ";
00295         else if( grad.repeatMethod() == VGradient::repeat )
00296             *m_defs << "spreadMethod=\"repeat\" ";
00297         *m_defs << ">" << endl;
00298 
00299         // color stops
00300         getColorStops( grad.colorStops() );
00301 
00302         printIndentation( m_defs, m_indent2 );
00303         *m_defs << "</linearGradient>" << endl;
00304         *m_body << "url(#" << uid << ")";
00305     }
00306     else if( grad.type() == VGradient::radial )
00307     {
00308         // do radial grad
00309         printIndentation( m_defs, m_indent2 );
00310         *m_defs << "<radialGradient id=\"" << uid << "\" ";
00311         *m_defs << "gradientUnits=\"userSpaceOnUse\" ";
00312         *m_defs << "cx=\"" << grad.origin().x() << "\" ";
00313         *m_defs << "cy=\"" << grad.origin().y() << "\" ";
00314         *m_defs << "fx=\"" << grad.focalPoint().x() << "\" ";
00315         *m_defs << "fy=\"" << grad.focalPoint().y() << "\" ";
00316         double r = sqrt( pow( grad.vector().x() - grad.origin().x(), 2 ) + pow( grad.vector().y() - grad.origin().y(), 2 ) );
00317         *m_defs << "r=\"" << QString().setNum( r ) << "\" ";
00318         if( grad.repeatMethod() == VGradient::reflect )
00319             *m_defs << "spreadMethod=\"reflect\" ";
00320         else if( grad.repeatMethod() == VGradient::repeat )
00321             *m_defs << "spreadMethod=\"repeat\" ";
00322         *m_defs << ">" << endl;
00323 
00324         // color stops
00325         getColorStops( grad.colorStops() );
00326 
00327         printIndentation( m_defs, m_indent2 );
00328         *m_defs << "</radialGradient>" << endl;
00329         *m_body << "url(#" << uid << ")";
00330     }
00331     // gah! pointless abbreviation of conical to conic
00332     else if( grad.type() == VGradient::conic )
00333     {
00334         // fake conical grad as radial.  
00335         // fugly but better than data loss.  
00336         printIndentation( m_defs, m_indent2 );
00337         *m_defs << "<radialGradient id=\"" << uid << "\" ";
00338         *m_defs << "gradientUnits=\"userSpaceOnUse\" ";
00339         *m_defs << "cx=\"" << grad.origin().x() << "\" ";
00340         *m_defs << "cy=\"" << grad.origin().y() << "\" ";
00341         *m_defs << "fx=\"" << grad.focalPoint().x() << "\" ";
00342         *m_defs << "fy=\"" << grad.focalPoint().y() << "\" ";
00343         double r = sqrt( pow( grad.vector().x() - grad.origin().x(), 2 ) + pow( grad.vector().y() - grad.origin().y(), 2 ) );
00344         *m_defs << "r=\"" << QString().setNum( r ) << "\" ";
00345         if( grad.repeatMethod() == VGradient::reflect )
00346             *m_defs << "spreadMethod=\"reflect\" ";
00347         else if( grad.repeatMethod() == VGradient::repeat )
00348             *m_defs << "spreadMethod=\"repeat\" ";
00349         *m_defs << ">" << endl;
00350 
00351         // color stops
00352         getColorStops( grad.colorStops() );
00353 
00354         printIndentation( m_defs, m_indent2 );
00355         *m_defs << "</radialGradient>" << endl;
00356         *m_body << "url(#" << uid << ")";
00357     }
00358 }
00359 
00360 // better than nothing
00361 void
00362 SvgExport::getPattern( const VPattern & )
00363 {
00364     QString uid = createUID();
00365     printIndentation( m_defs, m_indent2 );
00366     *m_defs << "<pattern id=\"" << uid << "\" ";
00367     *m_defs << "width=\"" << "\" ";
00368     *m_defs << "height=\"" << "\" ";
00369     *m_defs << "patternUnits=\"userSpaceOnUse\" ";
00370     *m_defs << "patternContentUnits=\"userSpaceOnUse\" "; 
00371     *m_defs << " />" << endl;
00372     // TODO: insert hard work here ;)
00373     printIndentation( m_defs, m_indent2 );
00374     *m_defs << "</pattern>" << endl;
00375     *m_body << "url(#" << uid << ")";
00376 }
00377 
00378 void
00379 SvgExport::getFill( const VFill& fill, QTextStream *stream )
00380 {
00381     *stream << " fill=\"";
00382     if( fill.type() == VFill::none )
00383         *stream << "none";
00384     else if( fill.type() == VFill::grad )
00385         getGradient( fill.gradient() );
00386     else if( fill.type() == VFill::patt )
00387         getPattern( fill.pattern() );
00388     else
00389         getHexColor( stream, fill.color() );
00390     *stream << "\"";
00391 
00392     if( fill.color().opacity() != m_gc.current()->fill.color().opacity() )
00393         *stream << " fill-opacity=\"" << fill.color().opacity() << "\"";
00394 }
00395 
00396 void
00397 SvgExport::getStroke( const VStroke& stroke, QTextStream *stream )
00398 {
00399     if( stroke.type() != m_gc.current()->stroke.type() )
00400     {
00401         *stream << " stroke=\"";
00402         if( stroke.type() == VStroke::none )
00403             *stream << "none";
00404         else if( stroke.type() == VStroke::grad )
00405             getGradient( stroke.gradient() );
00406         else
00407             getHexColor( stream, stroke.color() );
00408         *stream << "\"";
00409     }
00410 
00411     if( stroke.color().opacity() != m_gc.current()->stroke.color().opacity() )
00412         *stream << " stroke-opacity=\"" << stroke.color().opacity() << "\"";
00413 
00414     if( stroke.lineWidth() != m_gc.current()->stroke.lineWidth() )
00415         *stream << " stroke-width=\"" << stroke.lineWidth() << "\"";
00416 
00417     if( stroke.lineCap() != m_gc.current()->stroke.lineCap() )
00418     {
00419         if( stroke.lineCap() == VStroke::capButt )
00420             *stream << " stroke-linecap=\"butt\"";
00421         else if( stroke.lineCap() == VStroke::capRound )
00422             *stream << " stroke-linecap=\"round\"";
00423         else if( stroke.lineCap() == VStroke::capSquare )
00424             *stream << " stroke-linecap=\"square\"";
00425     }
00426 
00427     if( stroke.lineJoin() != m_gc.current()->stroke.lineJoin() )
00428     {
00429         if( stroke.lineJoin() == VStroke::joinMiter )
00430         {
00431             *stream << " stroke-linejoin=\"miter\"";
00432             *stream << " stroke-miterlimit=\"" << stroke.miterLimit() << "\"";
00433         }
00434         else if( stroke.lineJoin() == VStroke::joinRound )
00435             *stream << " stroke-linejoin=\"round\"";
00436         else if( stroke.lineJoin() == VStroke::joinBevel )
00437                 *stream << " stroke-linejoin=\"bevel\"";
00438     }
00439 
00440     // dash
00441     if( stroke.dashPattern().array().count() > 0 )
00442     {
00443         *stream << " stroke-dashoffset=\"" << stroke.dashPattern().offset() << "\"";
00444         *stream << " stroke-dasharray=\" ";
00445 
00446         QValueListConstIterator<float> itr;
00447         for(itr = stroke.dashPattern().array().begin(); itr != stroke.dashPattern().array().end(); ++itr )
00448         {
00449             *stream << *itr << " ";
00450         }
00451         *stream << "\"";
00452     }
00453 }
00454 
00455 void
00456 SvgExport::getHexColor( QTextStream *stream, const VColor& color )
00457 {
00458     // Convert the various color-spaces to hex
00459 
00460     QString Output;
00461 
00462     VColor copy( color );
00463     copy.setColorSpace( VColor::rgb );
00464 
00465     Output.sprintf( "#%02x%02x%02x", int( copy[0] * 255.0 ), int( copy[1] * 255.0 ), int( copy[2] * 255.0 ) );
00466 
00467     *stream << Output;
00468 }
00469 
00470 void
00471 SvgExport::visitVText( VText& text )
00472 {
00473     VPath path( 0L );
00474     path.combinePath( text.basePath() );
00475 
00476     m_trans->visitVPath( path );
00477 
00478     QString id = createUID();
00479     writePathToStream( path, " id=\""+ id + "\"", m_defs, m_indent2 );
00480 
00481     printIndentation( m_body, m_indent++ );
00482     *m_body << "<text" << getID( &text );
00483     //*m_body << " transform=\"scale(1, -1) translate(0, -" << text.document()->height() << ")\"";
00484     getFill( *( text.fill() ), m_body );
00485     getStroke( *( text.stroke() ), m_body );
00486 
00487     *m_body << " font-family=\"" << text.font().family() << "\"";
00488     *m_body << " font-size=\"" << text.font().pointSize() << "\"";
00489     if( text.font().bold() )
00490         *m_body << " font-weight=\"bold\"";
00491     if( text.font().italic() )
00492         *m_body << " font-style=\"italic\"";
00493     if( text.alignment() == VText::Center )
00494         *m_body << " text-anchor=\"middle\"";
00495     else if( text.alignment() == VText::Right )
00496         *m_body << " text-anchor=\"end\"";
00497 
00498     *m_body << ">" << endl;
00499     
00500     printIndentation( m_body, m_indent );
00501     *m_body << "<textPath xlink:href=\"#" << id << "\"";
00502     if( text.offset() > 0.0 )
00503         *m_body << " startOffset=\"" << text.offset() * 100.0 << "%\""; 
00504     *m_body << ">";
00505     *m_body << text.text();
00506     *m_body << "</textPath>" << endl;
00507     printIndentation( m_body, --m_indent );
00508     *m_body << "</text>" << endl;
00509 }
00510 
00511 #include "svgexport.moc"
00512 
KDE Home | KDE Accessibility Home | Description of Access Keys