filters

svgimport.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002, 2003, The Karbon Developers
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20#include <svgimport.h>
21#include "color.h"
22#include <KoFilterChain.h>
23#include <KoPageLayout.h>
24#include <kgenericfactory.h>
25#include <kdebug.h>
26#include <KoUnit.h>
27#include <KoGlobal.h>
28#include <shapes/vellipse.h>
29#include <shapes/vrectangle.h>
30#include <shapes/vpolygon.h>
31#include <commands/vtransformcmd.h>
32#include <core/vsegment.h>
33#include <core/vtext.h>
34#include <core/vglobal.h>
35#include <core/vgroup.h>
36#include <core/vimage.h>
37#include <core/vlayer.h>
38#include <tqcolor.h>
39#include <tqfile.h>
40#include <tqregexp.h>
41#include <kfilterdev.h>
42
43typedef KGenericFactory<SvgImport, KoFilter> SvgImportFactory;
44K_EXPORT_COMPONENT_FACTORY( libkarbonsvgimport, SvgImportFactory( "kofficefilters" ) )
45
46SvgImport::SvgImport(KoFilter *, const char *, const TQStringList&) :
47 KoFilter(),
48 outdoc( "DOC" )
49{
50 m_gc.setAutoDelete( true );
51}
52
53SvgImport::~SvgImport()
54{
55}
56
57KoFilter::ConversionStatus SvgImport::convert(const TQCString& from, const TQCString& to)
58{
59 // check for proper conversion
60 if( to != "application/x-karbon" || from != "image/svg+xml" )
61 return KoFilter::NotImplemented;
62
63 //Find the last extension
64 TQString strExt;
65 TQString fileIn ( m_chain->inputFile() );
66 const int result=fileIn.findRev('.');
67 if (result>=0)
68 strExt=fileIn.mid(result).lower();
69
70 TQString strMime; // Mime type of the compressor
71 if ((strExt==".gz") //in case of .svg.gz (logical extension)
72 ||(strExt==".svgz")) //in case of .svgz (extension used prioritary)
73 strMime="application/x-gzip"; // Compressed with gzip
74 else if (strExt==".bz2") //in case of .svg.bz2 (logical extension)
75 strMime="application/x-bzip2"; // Compressed with bzip2
76 else
77 strMime="text/plain";
78
79 /*kdDebug(30514) << "File extension: -" << strExt << "- Compression: " << strMime << endl;*/
80
81 TQIODevice* in = KFilterDev::deviceForFile(fileIn,strMime);
82
83 if (!in->open(IO_ReadOnly))
84 {
85 kdError(30514) << "Cannot open file! Aborting!" << endl;
86 delete in;
87 return KoFilter::FileNotFound;
88 }
89
90 int line, col;
91 TQString errormessage;
92
93 const bool parsed=inpdoc.setContent( in, &errormessage, &line, &col );
94
95 in->close();
96 delete in;
97
98 if ( ! parsed )
99 {
100 kdError(30514) << "Error while parsing file: "
101 << "at line " << line << " column: " << col
102 << " message: " << errormessage << endl;
103 // ### TODO: feedback to the user
104 return KoFilter::ParsingError;
105 }
106
107 // Do the conversion!
108 convert();
109 // add paper info, we always need custom for svg (Rob)
110 TQDomElement paper = outdoc.createElement( "PAPER" );
111 outdoc.documentElement().appendChild( paper );
112 paper.setAttribute( "format", PG_CUSTOM );
113 paper.setAttribute( "width", m_document.width() );
114 paper.setAttribute( "height", m_document.height() );
115
116 KoStoreDevice* out = m_chain->storageFile( "root", KoStore::Write );
117 if( !out )
118 {
119 kdError(30514) << "Unable to open output file!" << endl;
120 return KoFilter::StorageCreationError;
121 }
122 TQCString cstring = outdoc.toCString(); // utf-8 already
123 out->writeBlock( cstring.data(), cstring.length() );
124
125 return KoFilter::OK; // was successful
126}
127
128void SvgImport::convert()
129{
130 SvgGraphicsContext *gc = new SvgGraphicsContext;
131 TQDomElement docElem = inpdoc.documentElement();
132 KoRect bbox( 0, 0, 550.0, 841.0 );
133 double width = !docElem.attribute( "width" ).isEmpty() ? parseUnit( docElem.attribute( "width" ), true, false, bbox ) : 550.0;
134 double height = !docElem.attribute( "height" ).isEmpty() ? parseUnit( docElem.attribute( "height" ), false, true, bbox ) : 841.0;
135 m_document.setWidth( width );
136 m_document.setHeight( height );
137
138 m_outerRect = m_document.boundingBox();
139
140 // undo y-mirroring
141 //m_debug->append(TQString("%1\tUndo Y-mirroring.").arg(m_time.elapsed()));
142 if( !docElem.attribute( "viewBox" ).isEmpty() )
143 {
144 // allow for viewbox def with ',' or whitespace
145 TQString viewbox( docElem.attribute( "viewBox" ) );
146 TQStringList points = TQStringList::split( ' ', viewbox.replace( ',', ' ').simplifyWhiteSpace() );
147
148 gc->matrix.scale( width / points[2].toFloat() , height / points[3].toFloat() );
149 m_outerRect.setWidth( m_outerRect.width() * ( points[2].toFloat() / width ) );
150 m_outerRect.setHeight( m_outerRect.height() * ( points[3].toFloat() / height ) );
151 }
152
153 m_gc.push( gc );
154 parseGroup( 0L, docElem );
155
156 TQWMatrix mat;
157 mat.scale( 1, -1 );
158 mat.translate( 0, -m_document.height() );
159 VTransformCmd trafo( 0L, mat );
160 trafo.visit( m_document );
161 outdoc = m_document.saveXML();
162}
163
164#define DPI 90
165
166// Helper functions
167// ---------------------------------------------------------------------------------------
168
169double SvgImport::toPercentage( TQString s )
170{
171 if( s.endsWith( "%" ) )
172 return s.remove( '%' ).toDouble();
173 else
174 return s.toDouble() * 100.0;
175}
176
177double SvgImport::fromPercentage( TQString s )
178{
179 if( s.endsWith( "%" ) )
180 return s.remove( '%' ).toDouble() / 100.0;
181 else
182 return s.toDouble();
183}
184
185double SvgImport::getScalingFromMatrix( TQWMatrix &matrix )
186{
187 double xscale = matrix.m11() + matrix.m12();
188 double yscale = matrix.m22() + matrix.m21();
189 return sqrt( xscale*xscale + yscale*yscale ) / sqrt( 2.0 );
190}
191
192// parses the number into parameter number
193const char * getNumber( const char *ptr, double &number )
194{
195 int integer, exponent;
196 double decimal, frac;
197 int sign, expsign;
198
199 exponent = 0;
200 integer = 0;
201 frac = 1.0;
202 decimal = 0;
203 sign = 1;
204 expsign = 1;
205
206 // read the sign
207 if(*ptr == '+')
208 ptr++;
209 else if(*ptr == '-')
210 {
211 ptr++;
212 sign = -1;
213 }
214
215 // read the integer part
216 while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
217 integer = (integer * 10) + *(ptr++) - '0';
218 if(*ptr == '.') // read the decimals
219 {
220 ptr++;
221 while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
222 decimal += (*(ptr++) - '0') * (frac *= 0.1);
223 }
224
225 if(*ptr == 'e' || *ptr == 'E') // read the exponent part
226 {
227 ptr++;
228
229 // read the sign of the exponent
230 if(*ptr == '+')
231 ptr++;
232 else if(*ptr == '-')
233 {
234 ptr++;
235 expsign = -1;
236 }
237
238 exponent = 0;
239 while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
240 {
241 exponent *= 10;
242 exponent += *ptr - '0';
243 ptr++;
244 }
245 }
246 number = integer + decimal;
247 number *= sign * pow( (double)10, double( expsign * exponent ) );
248
249 return ptr;
250}
251
252void SvgImport::addGraphicContext()
253{
254 SvgGraphicsContext *gc = new SvgGraphicsContext;
255 // set as default
256 if( m_gc.current() )
257 *gc = *( m_gc.current() );
258 m_gc.push( gc );
259}
260
261void SvgImport::setupTransform( const TQDomElement &e )
262{
263 SvgGraphicsContext *gc = m_gc.current();
264
265 TQWMatrix mat = VPath::parseTransform( e.attribute( "transform" ) );
266 gc->matrix = mat * gc->matrix;
267}
268
269VObject* SvgImport::findObject( const TQString &name, VGroup* group )
270{
271 if( ! group )
272 return 0L;
273
274 VObjectListIterator itr = group->objects();
275
276 for( uint objcount = 1; itr.current(); ++itr, objcount++ )
277 if( itr.current()->state() != VObject::deleted )
278 {
279 if( itr.current()->name() == name )
280 return itr.current();
281
282 if( dynamic_cast<VGroup *>( itr.current() ) )
283 {
284 VObject *obj = findObject( name, dynamic_cast<VGroup *>( itr.current() ) );
285 if( obj )
286 return obj;
287 }
288 }
289
290 return 0L;
291}
292
293VObject* SvgImport::findObject( const TQString &name )
294{
295 TQPtrVector<VLayer> vector;
296 m_document.layers().toVector( &vector );
297 for( int i = vector.count() - 1; i >= 0; i-- )
298 {
299 if ( vector[i]->state() != VObject::deleted )
300 {
301 VObject* obj = findObject( name, dynamic_cast<VGroup *>( vector[i] ) );
302 if( obj )
303 return obj;
304 }
305 }
306
307 return 0L;
308}
309
310SvgImport::GradientHelper* SvgImport::findGradient( const TQString &id, const TQString &href)
311{
312 // check if gradient was already parsed, and return it
313 if( m_gradients.contains( id ) )
314 return &m_gradients[ id ];
315
316 // check if gradient was stored for later parsing
317 if( !m_defs.contains( id ) )
318 return 0L;
319
320 TQDomElement e = m_defs[ id ];
321 if(e.childNodes().count() == 0)
322 {
323 TQString mhref = e.attribute("xlink:href").mid(1);
324
325 if(m_defs.contains(mhref))
326 return findGradient(mhref, id);
327 else
328 return 0L;
329 }
330 else
331 {
332 // ok parse gradient now
333 parseGradient( m_defs[ id ], m_defs[ href ] );
334 }
335
336 // return successfully parsed gradient or NULL
337 TQString n;
338 if(href.isEmpty())
339 n = id;
340 else
341 n = href;
342
343 if( m_gradients.contains( n ) )
344 return &m_gradients[ n ];
345 else
346 return 0L;
347}
348
349TQDomElement SvgImport::mergeStyles( const TQDomElement &referencedBy, const TQDomElement &referencedElement )
350{
351 // First use all the style attributes of the element being referenced.
352 TQDomElement e = referencedElement;
353
354 // Now go through the style attributes of the element that is referencing and substitute the original ones.
355 if( !referencedBy.attribute( "color" ).isEmpty() )
356 e.setAttribute( "color", referencedBy.attribute( "color" ) );
357 if( !referencedBy.attribute( "fill" ).isEmpty() )
358 e.setAttribute( "fill", referencedBy.attribute( "fill" ) );
359 if( !referencedBy.attribute( "fill-rule" ).isEmpty() )
360 e.setAttribute( "fill-rule", referencedBy.attribute( "fill-rule" ) );
361 if( !referencedBy.attribute( "stroke" ).isEmpty() )
362 e.setAttribute( "stroke", referencedBy.attribute( "stroke" ) );
363 if( !referencedBy.attribute( "stroke-width" ).isEmpty() )
364 e.setAttribute( "stroke-width", referencedBy.attribute( "stroke-width" ) );
365 if( !referencedBy.attribute( "stroke-linejoin" ).isEmpty() )
366 e.setAttribute( "stroke-linejoin", referencedBy.attribute( "stroke-linejoin" ) );
367 if( !referencedBy.attribute( "stroke-linecap" ).isEmpty() )
368 e.setAttribute( "stroke-linecap", referencedBy.attribute( "stroke-linecap" ) );
369 if( !referencedBy.attribute( "stroke-dasharray" ).isEmpty() )
370 e.setAttribute( "stroke-dasharray", referencedBy.attribute( "stroke-dasharray" ) );
371 if( !referencedBy.attribute( "stroke-dashoffset" ).isEmpty() )
372 e.setAttribute( "stroke-dashoffset", referencedBy.attribute( "stroke-dashoffset" ) );
373 if( !referencedBy.attribute( "stroke-opacity" ).isEmpty() )
374 e.setAttribute( "stroke-opacity", referencedBy.attribute( "stroke-opacity" ) );
375 if( !referencedBy.attribute( "stroke-miterlimit" ).isEmpty() )
376 e.setAttribute( "stroke-miterlimit", referencedBy.attribute( "stroke-miterlimit" ) );
377 if( !referencedBy.attribute( "fill-opacity" ).isEmpty() )
378 e.setAttribute( "fill-opacity", referencedBy.attribute( "fill-opacity" ) );
379 if( !referencedBy.attribute( "opacity" ).isEmpty() )
380 e.setAttribute( "opacity", referencedBy.attribute( "opacity" ) );
381
382 // TODO merge style attribute too.
383
384 return e;
385}
386
387
388// Parsing functions
389// ---------------------------------------------------------------------------------------
390
391double SvgImport::parseUnit( const TQString &unit, bool horiz, bool vert, KoRect bbox )
392{
393 // TODO : percentage?
394 double value = 0;
395 const char *start = unit.latin1();
396 if(!start) {
397 return 0;
398 }
399 const char *end = getNumber( start, value );
400
401 if( uint( end - start ) < unit.length() )
402 {
403 if( unit.right( 2 ) == "pt" )
404 value = ( value / 72.0 ) * DPI;
405 else if( unit.right( 2 ) == "cm" )
406 value = ( value / 2.54 ) * DPI;
407 else if( unit.right( 2 ) == "pc" )
408 value = ( value / 6.0 ) * DPI;
409 else if( unit.right( 2 ) == "mm" )
410 value = ( value / 25.4 ) * DPI;
411 else if( unit.right( 2 ) == "in" )
412 value = value * DPI;
413 else if( unit.right( 2 ) == "pt" )
414 value = ( value / 72.0 ) * DPI;
415 else if( unit.right( 2 ) == "em" )
416 value = value * m_gc.current()->font.pointSize() / ( sqrt( pow( m_gc.current()->matrix.m11(), 2 ) + pow( m_gc.current()->matrix.m22(), 2 ) ) / sqrt( 2.0 ) );
417 else if( unit.right( 1 ) == "%" )
418 {
419 if( horiz && vert )
420 value = ( value / 100.0 ) * (sqrt( pow( bbox.width(), 2 ) + pow( bbox.height(), 2 ) ) / sqrt( 2.0 ) );
421 else if( horiz )
422 value = ( value / 100.0 ) * bbox.width();
423 else if( vert )
424 value = ( value / 100.0 ) * bbox.height();
425 }
426 }
427 /*else
428 {
429 if( m_gc.current() )
430 {
431 if( horiz && vert )
432 value *= sqrt( pow( m_gc.current()->matrix.m11(), 2 ) + pow( m_gc.current()->matrix.m22(), 2 ) ) / sqrt( 2.0 );
433 else if( horiz )
434 value /= m_gc.current()->matrix.m11();
435 else if( vert )
436 value /= m_gc.current()->matrix.m22();
437 }
438 }*/
439 return value;
440}
441
442TQColor SvgImport::parseColor( const TQString &rgbColor )
443{
444 int r, g, b;
445 keywordToRGB( rgbColor, r, g, b );
446 return TQColor( r, g, b );
447}
448
449void SvgImport::parseColor( VColor &color, const TQString &s )
450{
451 if( s.startsWith( "rgb(" ) )
452 {
453 TQString parse = s.stripWhiteSpace();
454 TQStringList colors = TQStringList::split( ',', parse );
455 TQString r = colors[0].right( ( colors[0].length() - 4 ) );
456 TQString g = colors[1];
457 TQString b = colors[2].left( ( colors[2].length() - 1 ) );
458
459 if( r.contains( "%" ) )
460 {
461 r = r.left( r.length() - 1 );
462 r = TQString::number( int( ( double( 255 * r.toDouble() ) / 100.0 ) ) );
463 }
464
465 if( g.contains( "%" ) )
466 {
467 g = g.left( g.length() - 1 );
468 g = TQString::number( int( ( double( 255 * g.toDouble() ) / 100.0 ) ) );
469 }
470
471 if( b.contains( "%" ) )
472 {
473 b = b.left( b.length() - 1 );
474 b = TQString::number( int( ( double( 255 * b.toDouble() ) / 100.0 ) ) );
475 }
476
477 TQColor c( r.toInt(), g.toInt(), b.toInt() );
478 color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
479 }
480 else if( s == "currentColor" )
481 {
482 SvgGraphicsContext *gc = m_gc.current();
483 color = gc->color;
484 }
485 else
486 {
487 TQString rgbColor = s.stripWhiteSpace();
488 TQColor c;
489 if( rgbColor.startsWith( "#" ) )
490 c.setNamedColor( rgbColor );
491 else
492 c = parseColor( rgbColor );
493 color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
494 }
495}
496
497void SvgImport::parseColorStops( VGradient *gradient, const TQDomElement &e )
498{
499 VColor c;
500 for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
501 {
502 TQDomElement stop = n.toElement();
503 if( stop.tagName() == "stop" )
504 {
505 float offset;
506 TQString temp = stop.attribute( "offset" );
507 if( temp.contains( '%' ) )
508 {
509 temp = temp.left( temp.length() - 1 );
510 offset = temp.toFloat() / 100.0;
511 }
512 else
513 offset = temp.toFloat();
514
515 if( !stop.attribute( "stop-color" ).isEmpty() )
516 parseColor( c, stop.attribute( "stop-color" ) );
517 else
518 {
519 // try style attr
520 TQString style = stop.attribute( "style" ).simplifyWhiteSpace();
521 TQStringList substyles = TQStringList::split( ';', style );
522 for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
523 {
524 TQStringList substyle = TQStringList::split( ':', (*it) );
525 TQString command = substyle[0].stripWhiteSpace();
526 TQString params = substyle[1].stripWhiteSpace();
527 if( command == "stop-color" )
528 parseColor( c, params );
529 if( command == "stop-opacity" )
530 c.setOpacity( params.toDouble() );
531 }
532
533 }
534 if( !stop.attribute( "stop-opacity" ).isEmpty() )
535 c.setOpacity( stop.attribute( "stop-opacity" ).toDouble() );
536 gradient->addStop( c, offset, 0.5 );
537 }
538 }
539}
540
541void SvgImport::parseGradient( const TQDomElement &e , const TQDomElement &referencedBy)
542{
543 // IMPROVEMENTS:
544 // - Store the parsed colorstops in some sort of a cache so they don't need to be parsed again.
545 // - A gradient inherits attributes it does not have from the referencing gradient.
546 // - Gradients with no color stops have no fill or stroke.
547 // - Gradients with one color stop have a solid color.
548
549 SvgGraphicsContext *gc = m_gc.current();
550 if( !gc ) return;
551
552 GradientHelper gradhelper;
553 gradhelper.gradient.clearStops();
554 gradhelper.gradient.setRepeatMethod( VGradient::none );
555
556 if(e.childNodes().count() == 0)
557 {
558 TQString href = e.attribute("xlink:href").mid(1);
559
560 if(href.isEmpty())
561 {
562 //gc->fill.setType( VFill::none ); // <--- TODO Fill OR Stroke are none
563 return;
564 }
565 else
566 {
567 // copy the referenced gradient if found
568 GradientHelper *pGrad = findGradient( href );
569 if( pGrad )
570 gradhelper = *pGrad;
571 }
572 }
573
574 // Use the gradient that is referencing, or if there isn't one, the original gradient.
575 TQDomElement b;
576 if( !referencedBy.isNull() )
577 b = referencedBy;
578 else
579 b = e;
580
581 TQString id = b.attribute("id");
582 if( !id.isEmpty() )
583 {
584 // Copy existing gradient if it exists
585 if( m_gradients.find( id ) != m_gradients.end() )
586 gradhelper.gradient = m_gradients[ id ].gradient;
587 }
588
589 gradhelper.bbox = b.attribute( "gradientUnits" ) != "userSpaceOnUse";
590
591 // parse color prop
592 VColor c = m_gc.current()->color;
593
594 if( !b.attribute( "color" ).isEmpty() )
595 {
596 parseColor( c, b.attribute( "color" ) );
597 }
598 else
599 {
600 // try style attr
601 TQString style = b.attribute( "style" ).simplifyWhiteSpace();
602 TQStringList substyles = TQStringList::split( ';', style );
603 for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
604 {
605 TQStringList substyle = TQStringList::split( ':', (*it) );
606 TQString command = substyle[0].stripWhiteSpace();
607 TQString params = substyle[1].stripWhiteSpace();
608 if( command == "color" )
609 parseColor( c, params );
610 }
611 }
612 m_gc.current()->color = c;
613
614 if( b.tagName() == "linearGradient" )
615 {
616 if( gradhelper.bbox )
617 {
618 gradhelper.gradient.setOrigin( KoPoint( toPercentage( b.attribute( "x1", "0%" ) ), toPercentage( b.attribute( "y1", "0%" ) ) ) );
619 gradhelper.gradient.setVector( KoPoint( toPercentage( b.attribute( "x2", "100%" ) ), toPercentage( b.attribute( "y2", "0%" ) ) ) );
620 }
621 else
622 {
623 gradhelper.gradient.setOrigin( KoPoint( b.attribute( "x1" ).toDouble(), b.attribute( "y1" ).toDouble() ) );
624 gradhelper.gradient.setVector( KoPoint( b.attribute( "x2" ).toDouble(), b.attribute( "y2" ).toDouble() ) );
625 }
626 gradhelper.gradient.setType( VGradient::linear );
627 }
628 else
629 {
630 if( gradhelper.bbox )
631 {
632 gradhelper.gradient.setOrigin( KoPoint( toPercentage( b.attribute( "cx", "50%" ) ), toPercentage( b.attribute( "cy", "50%" ) ) ) );
633 gradhelper.gradient.setVector( KoPoint( toPercentage( b.attribute( "cx", "50%" ) ) + toPercentage( b.attribute( "r", "50%" ) ), toPercentage( b.attribute( "cy", "50%" ) ) ) );
634 gradhelper.gradient.setFocalPoint( KoPoint( toPercentage( b.attribute( "fx", "50%" ) ), toPercentage( b.attribute( "fy", "50%" ) ) ) );
635 }
636 else
637 {
638 gradhelper.gradient.setOrigin( KoPoint( b.attribute( "cx" ).toDouble(), b.attribute( "cy" ).toDouble() ) );
639 gradhelper.gradient.setFocalPoint( KoPoint( b.attribute( "fx" ).toDouble(), b.attribute( "fy" ).toDouble() ) );
640 gradhelper.gradient.setVector( KoPoint( b.attribute( "cx" ).toDouble() + b.attribute( "r" ).toDouble(), b.attribute( "cy" ).toDouble() ) );
641 }
642 gradhelper.gradient.setType( VGradient::radial );
643 }
644 // handle spread method
645 TQString spreadMethod = b.attribute( "spreadMethod" );
646 if( !spreadMethod.isEmpty() )
647 {
648 if( spreadMethod == "reflect" )
649 gradhelper.gradient.setRepeatMethod( VGradient::reflect );
650 else if( spreadMethod == "repeat" )
651 gradhelper.gradient.setRepeatMethod( VGradient::repeat );
652 else
653 gradhelper.gradient.setRepeatMethod( VGradient::none );
654 }
655 else
656 gradhelper.gradient.setRepeatMethod( VGradient::none );
657
658 // Parse the color stops. The referencing gradient does not have colorstops,
659 // so use the stops from the gradient it references to (e in this case and not b)
660 parseColorStops( &gradhelper.gradient, e );
661 //gradient.setGradientTransform( parseTransform( e.attribute( "gradientTransform" ) ) );
662 gradhelper.gradientTransform = VPath::parseTransform( b.attribute( "gradientTransform" ) );
663 m_gradients.insert( b.attribute( "id" ), gradhelper );
664}
665
666void SvgImport::parsePA( VObject *obj, SvgGraphicsContext *gc, const TQString &command, const TQString &params )
667{
668 VColor fillcolor = gc->fill.color();
669 VColor strokecolor = gc->stroke.color();
670
671 if( params == "inherit" ) return;
672
673 if( command == "fill" )
674 {
675 if( params == "none" )
676 gc->fill.setType( VFill::none );
677 else if( params.startsWith( "url(" ) )
678 {
679 unsigned int start = params.find("#") + 1;
680 unsigned int end = params.findRev(")");
681 TQString key = params.mid( start, end - start );
682 GradientHelper *gradHelper = findGradient( key );
683 if( gradHelper )
684 {
685 gc->fill.gradient() = gradHelper->gradient;
686
687 if( gradHelper->bbox )
688 {
689 // adjust to bbox
690 KoRect bbox = obj->boundingBox();
691 //kdDebug() << "bbox x : " << bbox.x() << endl;
692 //kdDebug() << "!!!!!!bbox y : " << bbox.y() << endl;
693 //kdDebug() << gc->fill.gradient().origin().x() << endl;
694 //kdDebug() << gc->fill.gradient().vector().x() << endl;
695 double offsetx = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().origin().x() ), true, false, bbox );
696 double offsety = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().origin().y() ), false, true, bbox );
697 gc->fill.gradient().setOrigin( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
698 if(gc->fill.gradient().type() == VGradient::radial)
699 {
700 offsetx = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().focalPoint().x() ), true, false, bbox );
701 offsety = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().focalPoint().y() ), false, true, bbox );
702 gc->fill.gradient().setFocalPoint( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
703 }
704 offsetx = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().vector().x() ), true, false, bbox );
705 offsety = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().vector().y() ), false, true, bbox );
706 gc->fill.gradient().setVector( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
707 //kdDebug() << offsety << endl;
708 //kdDebug() << gc->fill.gradient().origin().x() << endl;
709 //kdDebug() << gc->fill.gradient().origin().y() << endl;
710 //kdDebug() << gc->fill.gradient().vector().x() << endl;
711 //kdDebug() << gc->fill.gradient().vector().y() << endl;
712 }
713 gc->fill.gradient().transform( gradHelper->gradientTransform );
714
715 if( !gradHelper->bbox )
716 gc->fill.gradient().transform( gc->matrix );
717
718 gc->fill.setType( VFill::grad );
719 }
720 else
721 gc->fill.setType( VFill::none );
722 }
723 else
724 {
725 parseColor( fillcolor, params );
726 gc->fill.setType( VFill::solid );
727 }
728 }
729 else if( command == "fill-rule" )
730 {
731 if( params == "nonzero" )
732 gc->fillRule = winding;
733 else if( params == "evenodd" )
734 gc->fillRule = evenOdd;
735 }
736 else if( command == "stroke" )
737 {
738 if( params == "none" )
739 gc->stroke.setType( VStroke::none );
740 else if( params.startsWith( "url(" ) )
741 {
742 unsigned int start = params.find("#") + 1;
743 unsigned int end = params.findRev(")");
744 TQString key = params.mid( start, end - start );
745
746 GradientHelper *gradHelper = findGradient( key );
747 if( gradHelper )
748 {
749 gc->stroke.gradient() = gradHelper->gradient;
750 gc->stroke.gradient().transform( gradHelper->gradientTransform );
751 gc->stroke.gradient().transform( gc->matrix );
752 gc->stroke.setType( VStroke::grad );
753 }
754 else
755 gc->stroke.setType( VStroke::none );
756 }
757 else
758 {
759 parseColor( strokecolor, params );
760 gc->stroke.setType( VStroke::solid );
761 }
762 }
763 else if( command == "stroke-width" )
764 gc->stroke.setLineWidth( parseUnit( params, true, true, m_outerRect ) );
765 else if( command == "stroke-linejoin" )
766 {
767 if( params == "miter" )
768 gc->stroke.setLineJoin( VStroke::joinMiter );
769 else if( params == "round" )
770 gc->stroke.setLineJoin( VStroke::joinRound );
771 else if( params == "bevel" )
772 gc->stroke.setLineJoin( VStroke::joinBevel );
773 }
774 else if( command == "stroke-linecap" )
775 {
776 if( params == "butt" )
777 gc->stroke.setLineCap( VStroke::capButt );
778 else if( params == "round" )
779 gc->stroke.setLineCap( VStroke::capRound );
780 else if( params == "square" )
781 gc->stroke.setLineCap( VStroke::capSquare );
782 }
783 else if( command == "stroke-miterlimit" )
784 gc->stroke.setMiterLimit( params.toFloat() );
785 else if( command == "stroke-dasharray" )
786 {
787 TQValueList<float> array;
788 if(params != "none")
789 {
790 // with "stroke-dasharray", the separator is a white space
791 // inside style attribute, stroke-dasharray is separated by comma (,)
792 TQStringList dashes = TQStringList::split( TQRegExp("[\\s,]"), params );
793 for( TQStringList::Iterator it = dashes.begin(); it != dashes.end(); ++it )
794 array.append( (*it).toFloat() );
795 }
796 gc->stroke.dashPattern().setArray( array );
797 }
798 else if( command == "stroke-dashoffset" )
799 gc->stroke.dashPattern().setOffset( params.toFloat() );
800 // handle opacity
801 else if( command == "stroke-opacity" )
802 strokecolor.setOpacity( fromPercentage( params ) );
803 else if( command == "fill-opacity" )
804 fillcolor.setOpacity( fromPercentage( params ) );
805 else if( command == "opacity" )
806 {
807 fillcolor.setOpacity( fromPercentage( params ) );
808 strokecolor.setOpacity( fromPercentage( params ) );
809 }
810 else if( command == "font-family" )
811 {
812 TQString family = params;
813 family.replace( '\'' , ' ' );
814 gc->font.setFamily( family );
815 }
816 else if( command == "font-size" )
817 {
818 float pointSize = parseUnit( params );
819 gc->font.setPointSizeFloat( pointSize * getScalingFromMatrix( gc->matrix ) );
820 }
821 else if( command == "font-weight" )
822 {
823 int weight = TQFont::Normal;
824
825 // map svg weight to tqt weight
826 // svg value tqt value
827 // 100,200,300 1, 17, 33
828 // 400 50 (normal)
829 // 500,600 58,66
830 // 700 75 (bold)
831 // 800,900 87,99
832
833 if( params == "bold" )
834 weight = TQFont::Bold;
835 else if( params == "lighter" )
836 {
837 weight = gc->font.weight();
838 if( weight <= 17 )
839 weight = 1;
840 else if( weight <= 33 )
841 weight = 17;
842 else if( weight <= 50 )
843 weight = 33;
844 else if( weight <= 58 )
845 weight = 50;
846 else if( weight <= 66 )
847 weight = 58;
848 else if( weight <= 75 )
849 weight = 66;
850 else if( weight <= 87 )
851 weight = 75;
852 else if( weight <= 99 )
853 weight = 87;
854 }
855 else if( params == "bolder" )
856 {
857 weight = gc->font.weight();
858 if( weight >= 87 )
859 weight = 99;
860 else if( weight >= 75 )
861 weight = 87;
862 else if( weight >= 66 )
863 weight = 75;
864 else if( weight >= 58 )
865 weight = 66;
866 else if( weight >= 50 )
867 weight = 58;
868 else if( weight >= 33 )
869 weight = 50;
870 else if( weight >= 17 )
871 weight = 50;
872 else if( weight >= 1 )
873 weight = 17;
874 }
875 else
876 {
877 bool ok;
878 // try to read numerical weight value
879 weight = params.toInt( &ok, 10 );
880
881 if( !ok )
882 return;
883
884 switch( weight )
885 {
886 case 100: weight = 1; break;
887 case 200: weight = 17; break;
888 case 300: weight = 33; break;
889 case 400: weight = 50; break;
890 case 500: weight = 58; break;
891 case 600: weight = 66; break;
892 case 700: weight = 75; break;
893 case 800: weight = 87; break;
894 case 900: weight = 99; break;
895 }
896 }
897 gc->font.setWeight( weight );
898 }
899 else if( command == "text-decoration" )
900 {
901 if( params == "line-through" )
902 gc->font.setStrikeOut( true );
903 else if( params == "underline" )
904 gc->font.setUnderline( true );
905 }
906 else if( command == "color" )
907 {
908 VColor color;
909 parseColor( color, params );
910 gc->color = color;
911 }
912 if( gc->fill.type() != VFill::none )
913 gc->fill.setColor( fillcolor, false );
914 //if( gc->stroke.type() == VStroke::solid )
915 gc->stroke.setColor( strokecolor );
916}
917
918void SvgImport::parseStyle( VObject *obj, const TQDomElement &e )
919{
920 SvgGraphicsContext *gc = m_gc.current();
921 if( !gc ) return;
922
923 // try normal PA
924 if( !e.attribute( "color" ).isEmpty() )
925 parsePA( obj, gc, "color", e.attribute( "color" ) );
926 if( !e.attribute( "fill" ).isEmpty() )
927 parsePA( obj, gc, "fill", e.attribute( "fill" ) );
928 if( !e.attribute( "fill-rule" ).isEmpty() )
929 parsePA( obj, gc, "fill-rule", e.attribute( "fill-rule" ) );
930 if( !e.attribute( "stroke" ).isEmpty() )
931 parsePA( obj, gc, "stroke", e.attribute( "stroke" ) );
932 if( !e.attribute( "stroke-width" ).isEmpty() )
933 parsePA( obj, gc, "stroke-width", e.attribute( "stroke-width" ) );
934 if( !e.attribute( "stroke-linejoin" ).isEmpty() )
935 parsePA( obj, gc, "stroke-linejoin", e.attribute( "stroke-linejoin" ) );
936 if( !e.attribute( "stroke-linecap" ).isEmpty() )
937 parsePA( obj, gc, "stroke-linecap", e.attribute( "stroke-linecap" ) );
938 if( !e.attribute( "stroke-dasharray" ).isEmpty() )
939 parsePA( obj, gc, "stroke-dasharray", e.attribute( "stroke-dasharray" ) );
940 if( !e.attribute( "stroke-dashoffset" ).isEmpty() )
941 parsePA( obj, gc, "stroke-dashoffset", e.attribute( "stroke-dashoffset" ) );
942 if( !e.attribute( "stroke-opacity" ).isEmpty() )
943 parsePA( obj, gc, "stroke-opacity", e.attribute( "stroke-opacity" ) );
944 if( !e.attribute( "stroke-miterlimit" ).isEmpty() )
945 parsePA( obj, gc, "stroke-miterlimit", e.attribute( "stroke-miterlimit" ) );
946 if( !e.attribute( "fill-opacity" ).isEmpty() )
947 parsePA( obj, gc, "fill-opacity", e.attribute( "fill-opacity" ) );
948 if( !e.attribute( "opacity" ).isEmpty() )
949 parsePA( obj, gc, "opacity", e.attribute( "opacity" ) );
950
951 // try style attr
952 TQString style = e.attribute( "style" ).simplifyWhiteSpace();
953 TQStringList substyles = TQStringList::split( ';', style );
954 for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
955 {
956 TQStringList substyle = TQStringList::split( ':', (*it) );
957 TQString command = substyle[0].stripWhiteSpace();
958 TQString params = substyle[1].stripWhiteSpace();
959 parsePA( obj, gc, command, params );
960 }
961
962 if(!obj)
963 return;
964
965 obj->setFill( gc->fill );
966 if( dynamic_cast<VPath *>( obj ) )
967 dynamic_cast<VPath *>( obj )->setFillRule( gc->fillRule );
968 // stroke scaling
969 double lineWidth = gc->stroke.lineWidth();
970 gc->stroke.setLineWidth( lineWidth * getScalingFromMatrix( gc->matrix ) );
971 obj->setStroke( gc->stroke );
972 gc->stroke.setLineWidth( lineWidth );
973}
974
975void SvgImport::parseFont( const TQDomElement &e )
976{
977 SvgGraphicsContext *gc = m_gc.current();
978 if( !gc ) return;
979
980 if( ! e.attribute( "font-family" ).isEmpty() )
981 parsePA( 0L, m_gc.current(), "font-family", e.attribute( "font-family" ) );
982 if( ! e.attribute( "font-size" ).isEmpty() )
983 parsePA( 0L, m_gc.current(), "font-size", e.attribute( "font-size" ) );
984 if( ! e.attribute( "font-weight" ).isEmpty() )
985 parsePA( 0L, m_gc.current(), "font-weight", e.attribute( "font-weight" ) );
986 if( ! e.attribute( "text-decoration" ).isEmpty() )
987 parsePA( 0L, m_gc.current(), "text-decoration", e.attribute( "text-decoration" ) );
988}
989
990void SvgImport::parseUse( VGroup *grp, const TQDomElement &e )
991{
992 TQString id = e.attribute( "xlink:href" );
993
994 if( !id.isEmpty() )
995 {
996 addGraphicContext();
997 setupTransform( e );
998
999 TQString key = id.mid( 1 );
1000
1001 if( !e.attribute( "x" ).isEmpty() && !e.attribute( "y" ).isEmpty() )
1002 {
1003 double tx = e.attribute( "x" ).toDouble();
1004 double ty = e.attribute( "y" ).toDouble();
1005
1006 m_gc.current()->matrix.translate(tx,ty);
1007 }
1008
1009 if(m_defs.contains(key))
1010 {
1011 TQDomElement a = m_defs[key];
1012 if(a.tagName() == "g" || a.tagName() == "a")
1013 parseGroup( grp, a);
1014 else
1015 {
1016 // Create the object with the merged styles.
1017 // The object inherits all style attributes from the use tag, but keeps it's own attributes.
1018 // So, not just use the style attributes of the use tag, but merge them first.
1019 createObject( grp, a, VObject::normal, mergeStyles(e, a) );
1020 }
1021 }
1022 delete( m_gc.pop() );
1023 }
1024}
1025
1026void SvgImport::parseGroup( VGroup *grp, const TQDomElement &e )
1027{
1028 for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
1029 {
1030 TQDomElement b = n.toElement();
1031 if( b.isNull() ) continue;
1032
1033 // treat svg link <a> as group so we don't miss its child elements
1034 if( b.tagName() == "g" || b.tagName() == "a" )
1035 {
1036 VGroup *group;
1037 if ( grp )
1038 group = new VGroup( grp );
1039 else
1040 group = new VGroup( &m_document );
1041
1042 addGraphicContext();
1043 setupTransform( b );
1044 parseStyle( group, b );
1045 parseFont( b );
1046 parseGroup( group, b );
1047
1048 // handle id
1049 if( !b.attribute("id").isEmpty() )
1050 group->setName( b.attribute("id") );
1051 if( grp )
1052 grp->append( group );
1053 else
1054 m_document.append( group );
1055 delete( m_gc.pop() );
1056 continue;
1057 }
1058 if( b.tagName() == "defs" )
1059 {
1060 parseDefs( b );
1061 continue;
1062 }
1063 else if( b.tagName() == "linearGradient" || b.tagName() == "radialGradient" )
1064 {
1065 parseGradient( b );
1066 continue;
1067 }
1068 if( b.tagName() == "rect" ||
1069 b.tagName() == "ellipse" ||
1070 b.tagName() == "circle" ||
1071 b.tagName() == "line" ||
1072 b.tagName() == "polyline" ||
1073 b.tagName() == "polygon" ||
1074 b.tagName() == "path" ||
1075 b.tagName() == "image" )
1076 {
1077 createObject( grp, b );
1078 continue;
1079 }
1080 else if( b.tagName() == "text" )
1081 {
1082 createText( grp, b );
1083 continue;
1084 }
1085 else if( b.tagName() == "use" )
1086 {
1087 parseUse( grp, b );
1088 continue;
1089 }
1090 }
1091}
1092
1093void SvgImport::parseDefs( const TQDomElement &e )
1094{
1095 for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
1096 {
1097 TQDomElement b = n.toElement();
1098 if( b.isNull() ) continue;
1099
1100 TQString definition = b.attribute( "id" );
1101 if( !definition.isEmpty() )
1102 {
1103 if( !m_defs.contains( definition ) )
1104 m_defs.insert( definition, b );
1105 }
1106 }
1107}
1108
1109
1110// Creating functions
1111// ---------------------------------------------------------------------------------------
1112
1113void SvgImport::createText( VGroup *grp, const TQDomElement &b )
1114{
1115 const double pathLength = 10.0;
1116
1117 VText *text = 0L;
1118 TQString content;
1119 TQString anchor;
1120 VSubpath base( 0L );
1121 VPath *path = 0L;
1122 double offset = 0.0;
1123
1124 addGraphicContext();
1125 setupTransform( b );
1126
1127 parseFont( b );
1128
1129 if( ! b.attribute( "text-anchor" ).isEmpty() )
1130 anchor = b.attribute( "text-anchor" );
1131
1132 if( b.hasChildNodes() )
1133 {
1134 if( base.isEmpty() && ! b.attribute( "x" ).isEmpty() && ! b.attribute( "y" ).isEmpty() )
1135 {
1136 double x = parseUnit( b.attribute( "x" ) );
1137 double y = parseUnit( b.attribute( "y" ) );
1138 base.moveTo( KoPoint( x, y ) );
1139 base.lineTo( KoPoint( x + pathLength, y ) );
1140 }
1141
1142 for( TQDomNode n = b.firstChild(); !n.isNull(); n = n.nextSibling() )
1143 {
1144 TQDomElement e = n.toElement();
1145 if( e.isNull() )
1146 {
1147 content += n.toCharacterData().data();
1148 }
1149 else if( e.tagName() == "textPath" )
1150 {
1151 if( e.attribute( "xlink:href" ).isEmpty() )
1152 continue;
1153
1154 TQString key = e.attribute( "xlink:href" ).mid( 1 );
1155 if( ! m_defs.contains(key) )
1156 {
1157 // try to find referenced object in document
1158 VObject* obj = findObject( key );
1159 // try to find referenced object in actual group, which is not yet part of document
1160 if( ! obj )
1161 obj = findObject( key, grp );
1162 if( obj )
1163 path = dynamic_cast<VPath*>( obj );
1164 }
1165 else
1166 {
1167 TQDomElement p = m_defs[key];
1168 createObject( grp, p, VObject::deleted);
1169 }
1170 if( ! path )
1171 continue;
1172 base = *path->paths().getFirst();
1173 content += e.text();
1174
1175 if( ! e.attribute( "startOffset" ).isEmpty() )
1176 {
1177 TQString start = e.attribute( "startOffset" );
1178 if( start.endsWith( "%" ) )
1179 offset = 0.01 * start.remove( '%' ).toDouble();
1180 else
1181 {
1182 float pathLength = 0;
1183 VSubpathIterator pIt( base );
1184
1185 for( ; pIt.current(); ++pIt )
1186 pathLength += pIt.current()->length();
1187
1188 if( pathLength > 0.0 )
1189 offset = start.toDouble() / pathLength;
1190 }
1191 }
1192 }
1193 else if( e.tagName() == "tspan" )
1194 {
1195 // only use text of tspan element, as we are not supporting text
1196 // with different styles
1197 content += e.text();
1198 if( base.isEmpty() && ! e.attribute( "x" ).isEmpty() && ! e.attribute( "y" ).isEmpty() )
1199 {
1200 TQStringList posX = TQStringList::split( ", ", e.attribute( "x" ) );
1201 TQStringList posY = TQStringList::split( ", ", e.attribute( "y" ) );
1202 if( posX.count() && posY.count() )
1203 {
1204 double x = parseUnit( posX.first() );
1205 double y = parseUnit( posY.first() );
1206 base.moveTo( KoPoint( x, y ) );
1207 base.lineTo( KoPoint( x + pathLength, y ) );
1208 }
1209 }
1210 }
1211 else if( e.tagName() == "tref" )
1212 {
1213 if( e.attribute( "xlink:href" ).isEmpty() )
1214 continue;
1215
1216 TQString key = e.attribute( "xlink:href" ).mid( 1 );
1217 if( ! m_defs.contains(key) )
1218 {
1219 // try to find referenced object in document
1220 VObject* obj = findObject( key );
1221 // try to find referenced object in actual group, which is not yet part of document
1222 if( ! obj )
1223 obj = findObject( key, grp );
1224 if( obj )
1225 content += dynamic_cast<VText*>( obj )->text();
1226 }
1227 else
1228 {
1229 TQDomElement p = m_defs[key];
1230 content += p.text();
1231 }
1232 }
1233 else
1234 continue;
1235
1236 if( ! e.attribute( "text-anchor" ).isEmpty() )
1237 anchor = e.attribute( "text-anchor" );
1238 }
1239 text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, content.simplifyWhiteSpace() );
1240 }
1241 else
1242 {
1243 VSubpath base( 0L );
1244 double x = parseUnit( b.attribute( "x" ) );
1245 double y = parseUnit( b.attribute( "y" ) );
1246 base.moveTo( KoPoint( x, y ) );
1247 base.lineTo( KoPoint( x + pathLength, y ) );
1248 text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, b.text().simplifyWhiteSpace() );
1249 }
1250
1251 if( text )
1252 {
1253 text->setParent( &m_document );
1254
1255 parseStyle( text, b );
1256
1257 text->setFont( m_gc.current()->font );
1258
1259 VTransformCmd trafo( 0L, m_gc.current()->matrix );
1260 trafo.visit( *text );
1261
1262 if( !b.attribute("id").isEmpty() )
1263 text->setName( b.attribute("id") );
1264
1265 if( anchor == "middle" )
1266 text->setAlignment( VText::Center );
1267 else if( anchor == "end" )
1268 text->setAlignment( VText::Right );
1269
1270 if( offset > 0.0 )
1271 text->setOffset( offset );
1272
1273 if( grp )
1274 grp->append( text );
1275 else
1276 m_document.append( text );
1277 }
1278
1279 delete( m_gc.pop() );
1280}
1281
1282void SvgImport::createObject( VGroup *grp, const TQDomElement &b, const VObject::VState state, const TQDomElement &style )
1283{
1284 VObject *obj = 0L;
1285
1286 addGraphicContext();
1287 setupTransform( b );
1288
1289 if( b.tagName() == "rect" )
1290 {
1291 double x = parseUnit( b.attribute( "x" ), true, false, m_outerRect );
1292 double y = parseUnit( b.attribute( "y" ), false, true, m_outerRect );
1293 double width = parseUnit( b.attribute( "width" ), true, false, m_outerRect );
1294 double height = parseUnit( b.attribute( "height" ), false, true, m_outerRect );
1295 double rx = parseUnit( b.attribute( "rx" ) );
1296 double ry = parseUnit( b.attribute( "ry" ) );
1297 obj = new VRectangle( 0L, KoPoint( x, height + y ) , width, height, rx, ry );
1298 }
1299 else if( b.tagName() == "ellipse" )
1300 {
1301 double rx = parseUnit( b.attribute( "rx" ) );
1302 double ry = parseUnit( b.attribute( "ry" ) );
1303 double left = parseUnit( b.attribute( "cx" ) ) - rx;
1304 double top = parseUnit( b.attribute( "cy" ) ) - ry;
1305 obj = new VEllipse( 0L, KoPoint( left, top ), rx * 2.0, ry * 2.0 );
1306 }
1307 else if( b.tagName() == "circle" )
1308 {
1309 double r = parseUnit( b.attribute( "r" ) );
1310 double left = parseUnit( b.attribute( "cx" ) ) - r;
1311 double top = parseUnit( b.attribute( "cy" ) ) - r;
1312 obj = new VEllipse( 0L, KoPoint( left, top ), r * 2.0, r * 2.0 );
1313 }
1314 else if( b.tagName() == "line" )
1315 {
1316 VPath *path = new VPath( &m_document );
1317 double x1 = b.attribute( "x1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x1" ) );
1318 double y1 = b.attribute( "y1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y1" ) );
1319 double x2 = b.attribute( "x2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x2" ) );
1320 double y2 = b.attribute( "y2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y2" ) );
1321 path->moveTo( KoPoint( x1, y1 ) );
1322 path->lineTo( KoPoint( x2, y2 ) );
1323 obj = path;
1324 }
1325 else if( b.tagName() == "polyline" || b.tagName() == "polygon" )
1326 {
1327 VPath *path = new VPath( &m_document );
1328 bool bFirst = true;
1329
1330 TQString points = b.attribute( "points" ).simplifyWhiteSpace();
1331 points.replace( ',', ' ' );
1332 points.remove( '\r' );
1333 points.remove( '\n' );
1334 TQStringList pointList = TQStringList::split( ' ', points );
1335 for( TQStringList::Iterator it = pointList.begin(); it != pointList.end(); ++it)
1336 {
1337 KoPoint point;
1338 point.setX( (*it).toDouble() );
1339 ++it;
1340 point.setY( (*it).toDouble() );
1341 if( bFirst )
1342 {
1343 path->moveTo( point );
1344 bFirst = false;
1345 }
1346 else
1347 path->lineTo( point );
1348 }
1349 if( b.tagName() == "polygon" ) path->close();
1350 obj = path;
1351 }
1352 else if( b.tagName() == "path" )
1353 {
1354 VPath *path = new VPath( &m_document );
1355 path->loadSvgPath( b.attribute( "d" ) );
1356 obj = path;
1357 }
1358 else if( b.tagName() == "image" )
1359 {
1360 TQString fname = b.attribute("xlink:href");
1361 obj = new VImage( 0L, fname );
1362 }
1363
1364 if( !obj )
1365 return;
1366
1367 if (state != VObject::normal)
1368 obj->setState(state);
1369
1370 VTransformCmd trafo( 0L, m_gc.current()->matrix );
1371 trafo.visit( *obj );
1372
1373 if( !style.isNull() )
1374 parseStyle( obj, style );
1375 else
1376 parseStyle( obj, b );
1377
1378 // handle id
1379 if( !b.attribute("id").isEmpty() )
1380 obj->setName( b.attribute("id") );
1381 if( grp )
1382 grp->append( obj );
1383 else
1384 m_document.append( obj );
1385
1386 delete( m_gc.pop() );
1387}
1388
1389#include <svgimport.moc>