22#include <KoFilterChain.h>
23#include <KoPageLayout.h>
24#include <kgenericfactory.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>
41#include <kfilterdev.h>
43typedef KGenericFactory<SvgImport, KoFilter> SvgImportFactory;
44K_EXPORT_COMPONENT_FACTORY( libkarbonsvgimport, SvgImportFactory(
"kofficefilters" ) )
46SvgImport::SvgImport(KoFilter *, const
char *, const TQStringList&) :
50 m_gc.setAutoDelete(
true );
53SvgImport::~SvgImport()
57KoFilter::ConversionStatus SvgImport::convert(
const TQCString& from,
const TQCString& to)
60 if( to !=
"application/x-karbon" || from !=
"image/svg+xml" )
61 return KoFilter::NotImplemented;
65 TQString fileIn ( m_chain->inputFile() );
66 const int result=fileIn.findRev(
'.');
68 strExt=fileIn.mid(result).lower();
73 strMime=
"application/x-gzip";
74 else if (strExt==
".bz2")
75 strMime=
"application/x-bzip2";
81 TQIODevice* in = KFilterDev::deviceForFile(fileIn,strMime);
83 if (!in->open(IO_ReadOnly))
85 kdError(30514) <<
"Cannot open file! Aborting!" << endl;
87 return KoFilter::FileNotFound;
91 TQString errormessage;
93 const bool parsed=inpdoc.setContent( in, &errormessage, &line, &col );
100 kdError(30514) <<
"Error while parsing file: "
101 <<
"at line " << line <<
" column: " << col
102 <<
" message: " << errormessage << endl;
104 return KoFilter::ParsingError;
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() );
116 KoStoreDevice* out = m_chain->storageFile(
"root", KoStore::Write );
119 kdError(30514) <<
"Unable to open output file!" << endl;
120 return KoFilter::StorageCreationError;
122 TQCString cstring = outdoc.toCString();
123 out->writeBlock( cstring.data(), cstring.length() );
128void SvgImport::convert()
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 );
138 m_outerRect = m_document.boundingBox();
142 if( !docElem.attribute(
"viewBox" ).isEmpty() )
145 TQString viewbox( docElem.attribute(
"viewBox" ) );
146 TQStringList points = TQStringList::split(
' ', viewbox.replace(
',',
' ').simplifyWhiteSpace() );
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 ) );
154 parseGroup( 0L, docElem );
158 mat.translate( 0, -m_document.height() );
159 VTransformCmd trafo( 0L, mat );
160 trafo.visit( m_document );
161 outdoc = m_document.saveXML();
169double SvgImport::toPercentage( TQString s )
171 if( s.endsWith(
"%" ) )
172 return s.remove(
'%' ).toDouble();
174 return s.toDouble() * 100.0;
177double SvgImport::fromPercentage( TQString s )
179 if( s.endsWith(
"%" ) )
180 return s.remove(
'%' ).toDouble() / 100.0;
185double SvgImport::getScalingFromMatrix( TQWMatrix &matrix )
187 double xscale = matrix.m11() + matrix.m12();
188 double yscale = matrix.m22() + matrix.m21();
189 return sqrt( xscale*xscale + yscale*yscale ) / sqrt( 2.0 );
193const char * getNumber(
const char *ptr,
double &number )
195 int integer, exponent;
196 double decimal, frac;
216 while(*ptr !=
'\0' && *ptr >=
'0' && *ptr <=
'9')
217 integer = (integer * 10) + *(ptr++) -
'0';
221 while(*ptr !=
'\0' && *ptr >=
'0' && *ptr <=
'9')
222 decimal += (*(ptr++) -
'0') * (frac *= 0.1);
225 if(*ptr ==
'e' || *ptr ==
'E')
239 while(*ptr !=
'\0' && *ptr >=
'0' && *ptr <=
'9')
242 exponent += *ptr -
'0';
246 number = integer + decimal;
247 number *= sign * pow( (
double)10,
double( expsign * exponent ) );
252void SvgImport::addGraphicContext()
254 SvgGraphicsContext *gc =
new SvgGraphicsContext;
257 *gc = *( m_gc.current() );
261void SvgImport::setupTransform(
const TQDomElement &e )
263 SvgGraphicsContext *gc = m_gc.current();
265 TQWMatrix mat = VPath::parseTransform( e.attribute(
"transform" ) );
266 gc->matrix = mat * gc->matrix;
269VObject* SvgImport::findObject(
const TQString &name, VGroup* group )
274 VObjectListIterator itr = group->objects();
276 for( uint objcount = 1; itr.current(); ++itr, objcount++ )
277 if( itr.current()->state() != VObject::deleted )
279 if( itr.current()->name() == name )
280 return itr.current();
282 if(
dynamic_cast<VGroup *
>( itr.current() ) )
284 VObject *obj = findObject( name,
dynamic_cast<VGroup *
>( itr.current() ) );
293VObject* SvgImport::findObject(
const TQString &name )
295 TQPtrVector<VLayer> vector;
296 m_document.layers().toVector( &vector );
297 for(
int i = vector.count() - 1; i >= 0; i-- )
299 if ( vector[i]->state() != VObject::deleted )
301 VObject* obj = findObject( name,
dynamic_cast<VGroup *
>( vector[i] ) );
310SvgImport::GradientHelper* SvgImport::findGradient(
const TQString &
id,
const TQString &href)
313 if( m_gradients.contains(
id ) )
314 return &m_gradients[ id ];
317 if( !m_defs.contains(
id ) )
320 TQDomElement e = m_defs[ id ];
321 if(e.childNodes().count() == 0)
323 TQString mhref = e.attribute(
"xlink:href").mid(1);
325 if(m_defs.contains(mhref))
326 return findGradient(mhref,
id);
333 parseGradient( m_defs[
id ], m_defs[ href ] );
343 if( m_gradients.contains( n ) )
344 return &m_gradients[ n ];
349TQDomElement SvgImport::mergeStyles(
const TQDomElement &referencedBy,
const TQDomElement &referencedElement )
352 TQDomElement e = referencedElement;
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" ) );
391double SvgImport::parseUnit(
const TQString &unit,
bool horiz,
bool vert, KoRect bbox )
395 const char *start = unit.latin1();
399 const char *end = getNumber( start, value );
401 if( uint( end - start ) < unit.length() )
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" )
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 ) ==
"%" )
420 value = ( value / 100.0 ) * (sqrt( pow( bbox.width(), 2 ) + pow( bbox.height(), 2 ) ) / sqrt( 2.0 ) );
422 value = ( value / 100.0 ) * bbox.width();
424 value = ( value / 100.0 ) * bbox.height();
442TQColor SvgImport::parseColor(
const TQString &rgbColor )
445 keywordToRGB( rgbColor, r, g, b );
446 return TQColor( r, g, b );
449void SvgImport::parseColor( VColor &color,
const TQString &s )
451 if( s.startsWith(
"rgb(" ) )
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 ) );
459 if( r.contains(
"%" ) )
461 r = r.left( r.length() - 1 );
462 r = TQString::number(
int( (
double( 255 * r.toDouble() ) / 100.0 ) ) );
465 if( g.contains(
"%" ) )
467 g = g.left( g.length() - 1 );
468 g = TQString::number(
int( (
double( 255 * g.toDouble() ) / 100.0 ) ) );
471 if( b.contains(
"%" ) )
473 b = b.left( b.length() - 1 );
474 b = TQString::number(
int( (
double( 255 * b.toDouble() ) / 100.0 ) ) );
477 TQColor c( r.toInt(), g.toInt(), b.toInt() );
478 color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
480 else if( s ==
"currentColor" )
482 SvgGraphicsContext *gc = m_gc.current();
487 TQString rgbColor = s.stripWhiteSpace();
489 if( rgbColor.startsWith(
"#" ) )
490 c.setNamedColor( rgbColor );
492 c = parseColor( rgbColor );
493 color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
497void SvgImport::parseColorStops( VGradient *gradient,
const TQDomElement &e )
500 for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
502 TQDomElement stop = n.toElement();
503 if( stop.tagName() ==
"stop" )
506 TQString temp = stop.attribute(
"offset" );
507 if( temp.contains(
'%' ) )
509 temp = temp.left( temp.length() - 1 );
510 offset = temp.toFloat() / 100.0;
513 offset = temp.toFloat();
515 if( !stop.attribute(
"stop-color" ).isEmpty() )
516 parseColor( c, stop.attribute(
"stop-color" ) );
520 TQString style = stop.attribute(
"style" ).simplifyWhiteSpace();
521 TQStringList substyles = TQStringList::split(
';', style );
522 for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
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() );
534 if( !stop.attribute(
"stop-opacity" ).isEmpty() )
535 c.setOpacity( stop.attribute(
"stop-opacity" ).toDouble() );
536 gradient->addStop( c, offset, 0.5 );
541void SvgImport::parseGradient(
const TQDomElement &e ,
const TQDomElement &referencedBy)
549 SvgGraphicsContext *gc = m_gc.current();
552 GradientHelper gradhelper;
553 gradhelper.gradient.clearStops();
554 gradhelper.gradient.setRepeatMethod( VGradient::none );
556 if(e.childNodes().count() == 0)
558 TQString href = e.attribute(
"xlink:href").mid(1);
568 GradientHelper *pGrad = findGradient( href );
576 if( !referencedBy.isNull() )
581 TQString
id = b.attribute(
"id");
585 if( m_gradients.find(
id ) != m_gradients.end() )
586 gradhelper.gradient = m_gradients[ id ].gradient;
589 gradhelper.bbox = b.attribute(
"gradientUnits" ) !=
"userSpaceOnUse";
592 VColor c = m_gc.current()->color;
594 if( !b.attribute(
"color" ).isEmpty() )
596 parseColor( c, b.attribute(
"color" ) );
601 TQString style = b.attribute(
"style" ).simplifyWhiteSpace();
602 TQStringList substyles = TQStringList::split(
';', style );
603 for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
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 );
612 m_gc.current()->color = c;
614 if( b.tagName() ==
"linearGradient" )
616 if( gradhelper.bbox )
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%" ) ) ) );
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() ) );
626 gradhelper.gradient.setType( VGradient::linear );
630 if( gradhelper.bbox )
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%" ) ) ) );
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() ) );
642 gradhelper.gradient.setType( VGradient::radial );
645 TQString spreadMethod = b.attribute(
"spreadMethod" );
646 if( !spreadMethod.isEmpty() )
648 if( spreadMethod ==
"reflect" )
649 gradhelper.gradient.setRepeatMethod( VGradient::reflect );
650 else if( spreadMethod ==
"repeat" )
651 gradhelper.gradient.setRepeatMethod( VGradient::repeat );
653 gradhelper.gradient.setRepeatMethod( VGradient::none );
656 gradhelper.gradient.setRepeatMethod( VGradient::none );
660 parseColorStops( &gradhelper.gradient, e );
662 gradhelper.gradientTransform = VPath::parseTransform( b.attribute(
"gradientTransform" ) );
663 m_gradients.insert( b.attribute(
"id" ), gradhelper );
666void SvgImport::parsePA( VObject *obj, SvgGraphicsContext *gc,
const TQString &command,
const TQString ¶ms )
668 VColor fillcolor = gc->fill.color();
669 VColor strokecolor = gc->stroke.color();
671 if( params ==
"inherit" )
return;
673 if( command ==
"fill" )
675 if( params ==
"none" )
676 gc->fill.setType( VFill::none );
677 else if( params.startsWith(
"url(" ) )
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 );
685 gc->fill.gradient() = gradHelper->gradient;
687 if( gradHelper->bbox )
690 KoRect bbox = obj->boundingBox();
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)
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 ) );
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 ) );
713 gc->fill.gradient().transform( gradHelper->gradientTransform );
715 if( !gradHelper->bbox )
716 gc->fill.gradient().transform( gc->matrix );
718 gc->fill.setType( VFill::grad );
721 gc->fill.setType( VFill::none );
725 parseColor( fillcolor, params );
726 gc->fill.setType( VFill::solid );
729 else if( command ==
"fill-rule" )
731 if( params ==
"nonzero" )
732 gc->fillRule = winding;
733 else if( params ==
"evenodd" )
734 gc->fillRule = evenOdd;
736 else if( command ==
"stroke" )
738 if( params ==
"none" )
739 gc->stroke.setType( VStroke::none );
740 else if( params.startsWith(
"url(" ) )
742 unsigned int start = params.find(
"#") + 1;
743 unsigned int end = params.findRev(
")");
744 TQString key = params.mid( start, end - start );
746 GradientHelper *gradHelper = findGradient( key );
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 );
755 gc->stroke.setType( VStroke::none );
759 parseColor( strokecolor, params );
760 gc->stroke.setType( VStroke::solid );
763 else if( command ==
"stroke-width" )
764 gc->stroke.setLineWidth( parseUnit( params,
true,
true, m_outerRect ) );
765 else if( command ==
"stroke-linejoin" )
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 );
774 else if( command ==
"stroke-linecap" )
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 );
783 else if( command ==
"stroke-miterlimit" )
784 gc->stroke.setMiterLimit( params.toFloat() );
785 else if( command ==
"stroke-dasharray" )
787 TQValueList<float> array;
792 TQStringList dashes = TQStringList::split( TQRegExp(
"[\\s,]"), params );
793 for( TQStringList::Iterator it = dashes.begin(); it != dashes.end(); ++it )
794 array.append( (*it).toFloat() );
796 gc->stroke.dashPattern().setArray( array );
798 else if( command ==
"stroke-dashoffset" )
799 gc->stroke.dashPattern().setOffset( params.toFloat() );
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" )
807 fillcolor.setOpacity( fromPercentage( params ) );
808 strokecolor.setOpacity( fromPercentage( params ) );
810 else if( command ==
"font-family" )
812 TQString family = params;
813 family.replace(
'\'' ,
' ' );
814 gc->font.setFamily( family );
816 else if( command ==
"font-size" )
818 float pointSize = parseUnit( params );
819 gc->font.setPointSizeFloat( pointSize * getScalingFromMatrix( gc->matrix ) );
821 else if( command ==
"font-weight" )
823 int weight = TQFont::Normal;
833 if( params ==
"bold" )
834 weight = TQFont::Bold;
835 else if( params ==
"lighter" )
837 weight = gc->font.weight();
840 else if( weight <= 33 )
842 else if( weight <= 50 )
844 else if( weight <= 58 )
846 else if( weight <= 66 )
848 else if( weight <= 75 )
850 else if( weight <= 87 )
852 else if( weight <= 99 )
855 else if( params ==
"bolder" )
857 weight = gc->font.weight();
860 else if( weight >= 75 )
862 else if( weight >= 66 )
864 else if( weight >= 58 )
866 else if( weight >= 50 )
868 else if( weight >= 33 )
870 else if( weight >= 17 )
872 else if( weight >= 1 )
879 weight = params.toInt( &ok, 10 );
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;
897 gc->font.setWeight( weight );
899 else if( command ==
"text-decoration" )
901 if( params ==
"line-through" )
902 gc->font.setStrikeOut(
true );
903 else if( params ==
"underline" )
904 gc->font.setUnderline(
true );
906 else if( command ==
"color" )
909 parseColor( color, params );
912 if( gc->fill.type() != VFill::none )
913 gc->fill.setColor( fillcolor,
false );
915 gc->stroke.setColor( strokecolor );
918void SvgImport::parseStyle( VObject *obj,
const TQDomElement &e )
920 SvgGraphicsContext *gc = m_gc.current();
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" ) );
952 TQString style = e.attribute(
"style" ).simplifyWhiteSpace();
953 TQStringList substyles = TQStringList::split(
';', style );
954 for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
956 TQStringList substyle = TQStringList::split(
':', (*it) );
957 TQString command = substyle[0].stripWhiteSpace();
958 TQString params = substyle[1].stripWhiteSpace();
959 parsePA( obj, gc, command, params );
965 obj->setFill( gc->fill );
966 if(
dynamic_cast<VPath *
>( obj ) )
967 dynamic_cast<VPath *
>( obj )->setFillRule( gc->fillRule );
969 double lineWidth = gc->stroke.lineWidth();
970 gc->stroke.setLineWidth( lineWidth * getScalingFromMatrix( gc->matrix ) );
971 obj->setStroke( gc->stroke );
972 gc->stroke.setLineWidth( lineWidth );
975void SvgImport::parseFont(
const TQDomElement &e )
977 SvgGraphicsContext *gc = m_gc.current();
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" ) );
990void SvgImport::parseUse( VGroup *grp,
const TQDomElement &e )
992 TQString
id = e.attribute(
"xlink:href" );
999 TQString key =
id.mid( 1 );
1001 if( !e.attribute(
"x" ).isEmpty() && !e.attribute(
"y" ).isEmpty() )
1003 double tx = e.attribute(
"x" ).toDouble();
1004 double ty = e.attribute(
"y" ).toDouble();
1006 m_gc.current()->matrix.translate(tx,ty);
1009 if(m_defs.contains(key))
1011 TQDomElement a = m_defs[key];
1012 if(a.tagName() ==
"g" || a.tagName() ==
"a")
1013 parseGroup( grp, a);
1019 createObject( grp, a, VObject::normal, mergeStyles(e, a) );
1022 delete( m_gc.pop() );
1026void SvgImport::parseGroup( VGroup *grp,
const TQDomElement &e )
1028 for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
1030 TQDomElement b = n.toElement();
1031 if( b.isNull() )
continue;
1034 if( b.tagName() ==
"g" || b.tagName() ==
"a" )
1038 group =
new VGroup( grp );
1040 group =
new VGroup( &m_document );
1042 addGraphicContext();
1043 setupTransform( b );
1044 parseStyle( group, b );
1046 parseGroup( group, b );
1049 if( !b.attribute(
"id").isEmpty() )
1050 group->setName( b.attribute(
"id") );
1052 grp->append( group );
1054 m_document.append( group );
1055 delete( m_gc.pop() );
1058 if( b.tagName() ==
"defs" )
1063 else if( b.tagName() ==
"linearGradient" || b.tagName() ==
"radialGradient" )
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" )
1077 createObject( grp, b );
1080 else if( b.tagName() ==
"text" )
1082 createText( grp, b );
1085 else if( b.tagName() ==
"use" )
1093void SvgImport::parseDefs(
const TQDomElement &e )
1095 for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
1097 TQDomElement b = n.toElement();
1098 if( b.isNull() )
continue;
1100 TQString definition = b.attribute(
"id" );
1101 if( !definition.isEmpty() )
1103 if( !m_defs.contains( definition ) )
1104 m_defs.insert( definition, b );
1113void SvgImport::createText( VGroup *grp,
const TQDomElement &b )
1115 const double pathLength = 10.0;
1120 VSubpath base( 0L );
1122 double offset = 0.0;
1124 addGraphicContext();
1125 setupTransform( b );
1129 if( ! b.attribute(
"text-anchor" ).isEmpty() )
1130 anchor = b.attribute(
"text-anchor" );
1132 if( b.hasChildNodes() )
1134 if( base.isEmpty() && ! b.attribute(
"x" ).isEmpty() && ! b.attribute(
"y" ).isEmpty() )
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 ) );
1142 for( TQDomNode n = b.firstChild(); !n.isNull(); n = n.nextSibling() )
1144 TQDomElement e = n.toElement();
1147 content += n.toCharacterData().data();
1149 else if( e.tagName() ==
"textPath" )
1151 if( e.attribute(
"xlink:href" ).isEmpty() )
1154 TQString key = e.attribute(
"xlink:href" ).mid( 1 );
1155 if( ! m_defs.contains(key) )
1158 VObject* obj = findObject( key );
1161 obj = findObject( key, grp );
1163 path =
dynamic_cast<VPath*
>( obj );
1167 TQDomElement p = m_defs[key];
1168 createObject( grp, p, VObject::deleted);
1172 base = *path->paths().getFirst();
1173 content += e.text();
1175 if( ! e.attribute(
"startOffset" ).isEmpty() )
1177 TQString start = e.attribute(
"startOffset" );
1178 if( start.endsWith(
"%" ) )
1179 offset = 0.01 * start.remove(
'%' ).toDouble();
1182 float pathLength = 0;
1183 VSubpathIterator pIt( base );
1185 for( ; pIt.current(); ++pIt )
1186 pathLength += pIt.current()->length();
1188 if( pathLength > 0.0 )
1189 offset = start.toDouble() / pathLength;
1193 else if( e.tagName() ==
"tspan" )
1197 content += e.text();
1198 if( base.isEmpty() && ! e.attribute(
"x" ).isEmpty() && ! e.attribute(
"y" ).isEmpty() )
1200 TQStringList posX = TQStringList::split(
", ", e.attribute(
"x" ) );
1201 TQStringList posY = TQStringList::split(
", ", e.attribute(
"y" ) );
1202 if( posX.count() && posY.count() )
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 ) );
1211 else if( e.tagName() ==
"tref" )
1213 if( e.attribute(
"xlink:href" ).isEmpty() )
1216 TQString key = e.attribute(
"xlink:href" ).mid( 1 );
1217 if( ! m_defs.contains(key) )
1220 VObject* obj = findObject( key );
1223 obj = findObject( key, grp );
1225 content +=
dynamic_cast<VText*
>( obj )->text();
1229 TQDomElement p = m_defs[key];
1230 content += p.text();
1236 if( ! e.attribute(
"text-anchor" ).isEmpty() )
1237 anchor = e.attribute(
"text-anchor" );
1239 text =
new VText( m_gc.current()->font, base, VText::Above, VText::Left, content.simplifyWhiteSpace() );
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() );
1253 text->setParent( &m_document );
1255 parseStyle( text, b );
1257 text->setFont( m_gc.current()->font );
1259 VTransformCmd trafo( 0L, m_gc.current()->matrix );
1260 trafo.visit( *text );
1262 if( !b.attribute(
"id").isEmpty() )
1263 text->setName( b.attribute(
"id") );
1265 if( anchor ==
"middle" )
1266 text->setAlignment( VText::Center );
1267 else if( anchor ==
"end" )
1268 text->setAlignment( VText::Right );
1271 text->setOffset( offset );
1274 grp->append( text );
1276 m_document.append( text );
1279 delete( m_gc.pop() );
1282void SvgImport::createObject( VGroup *grp,
const TQDomElement &b,
const VObject::VState state,
const TQDomElement &style )
1286 addGraphicContext();
1287 setupTransform( b );
1289 if( b.tagName() ==
"rect" )
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 );
1299 else if( b.tagName() ==
"ellipse" )
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 );
1307 else if( b.tagName() ==
"circle" )
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 );
1314 else if( b.tagName() ==
"line" )
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 ) );
1325 else if( b.tagName() ==
"polyline" || b.tagName() ==
"polygon" )
1327 VPath *path =
new VPath( &m_document );
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)
1338 point.setX( (*it).toDouble() );
1340 point.setY( (*it).toDouble() );
1343 path->moveTo( point );
1347 path->lineTo( point );
1349 if( b.tagName() ==
"polygon" ) path->close();
1352 else if( b.tagName() ==
"path" )
1354 VPath *path =
new VPath( &m_document );
1355 path->loadSvgPath( b.attribute(
"d" ) );
1358 else if( b.tagName() ==
"image" )
1360 TQString fname = b.attribute(
"xlink:href");
1361 obj =
new VImage( 0L, fname );
1367 if (state != VObject::normal)
1368 obj->setState(state);
1370 VTransformCmd trafo( 0L, m_gc.current()->matrix );
1371 trafo.visit( *obj );
1373 if( !style.isNull() )
1374 parseStyle( obj, style );
1376 parseStyle( obj, b );
1379 if( !b.attribute(
"id").isEmpty() )
1380 obj->setName( b.attribute(
"id") );
1384 m_document.append( obj );
1386 delete( m_gc.pop() );
1389#include <svgimport.moc>