xamlimport.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 "xamlimport.h"
21#include "color.h"
22#include <KoFilterChain.h>
23#include <kgenericfactory.h>
24#include <kdebug.h>
25#include <KoUnit.h>
26#include <KoGlobal.h>
27#include <shapes/vellipse.h>
28#include <shapes/vrectangle.h>
29#include <shapes/vpolygon.h>
30#include <commands/vtransformcmd.h>
31#include <core/vsegment.h>
32#include <core/vtext.h>
33#include <core/vglobal.h>
34#include <core/vgroup.h>
35#include <core/vimage.h>
36#include <core/vlayer.h>
37#include <tqcolor.h>
38#include <tqfile.h>
39#include <kfilterdev.h>
40
41typedef KGenericFactory<XAMLImport, KoFilter> XAMLImportFactory;
42K_EXPORT_COMPONENT_FACTORY( libkarbonxamlimport, XAMLImportFactory( "kofficefilters" ) )
43
44XAMLImport::XAMLImport(KoFilter *, const char *, const TQStringList&) :
45 KoFilter(),
46 outdoc( "DOC" )
47{
48 m_gc.setAutoDelete( true );
49}
50
51XAMLImport::~XAMLImport()
52{
53}
54
55KoFilter::ConversionStatus XAMLImport::convert(const TQCString& from, const TQCString& to)
56{
57 // check for proper conversion
58 if( to != "application/x-karbon" || from != "image/wvg+xml" )
59 return KoFilter::NotImplemented;
60
61 //Find the last extension
62 TQString strExt;
63 TQString fileIn ( m_chain->inputFile() );
64 const int result=fileIn.findRev('.');
65 if (result>=0)
66 {
67 strExt=fileIn.mid(result).lower();
68 }
69
70 TQString strMime; // Mime type of the compressor
71 if ((strExt==".gz") //in case of .svg.gz (logical extension)
72 ||(strExt==".wvgz")) //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 const bool parsed=inpdoc.setContent( in, &errormessage, &line, &col );
93 in->close();
94 delete in;
95 if ( ! parsed )
96 {
97 kdError(30514) << "Error while parsing file: "
98 << "at line " << line << " column: " << col
99 << " message: " << errormessage << endl;
100 // ### TODO: feedback to the user
101 return KoFilter::ParsingError;
102 }
103
104 // Do the conversion!
105 convert();
106
107 KoStoreDevice* out = m_chain->storageFile( "root", KoStore::Write );
108 if( !out )
109 {
110 kdError(30514) << "Unable to open output file!" << endl;
111 return KoFilter::StorageCreationError;
112 }
113 TQCString cstring = outdoc.toCString(); // utf-8 already
114 out->writeBlock( cstring.data(), cstring.length() );
115
116 return KoFilter::OK; // was successful
117}
118
119void
120XAMLImport::convert()
121{
122 XAMLGraphicsContext *gc = new XAMLGraphicsContext;
123 TQDomElement docElem = inpdoc.documentElement();
124 KoRect bbox( 0, 0, 550.0, 841.0 );
125 double width = !docElem.attribute( "width" ).isEmpty() ? parseUnit( docElem.attribute( "width" ), true, false, bbox ) : 550.0;
126 double height = !docElem.attribute( "height" ).isEmpty() ? parseUnit( docElem.attribute( "height" ), false, true, bbox ) : 841.0;
127 m_document.setWidth( width );
128 m_document.setHeight( height );
129 m_outerRect = m_document.boundingBox();
130
131 // undo y-mirroring
132 if( !docElem.attribute( "viewBox" ).isEmpty() )
133 {
134 // allow for viewbox def with ',' or whitespace
135 TQString viewbox( docElem.attribute( "viewBox" ) );
136 TQStringList points = TQStringList::split( ' ', viewbox.replace( ',', ' ').simplifyWhiteSpace() );
137
138 gc->matrix.scale( width / points[2].toFloat() , height / points[3].toFloat() );
139 m_outerRect.setWidth( m_outerRect.width() * ( points[2].toFloat() / width ) );
140 m_outerRect.setHeight( m_outerRect.height() * ( points[3].toFloat() / height ) );
141 }
142 m_gc.push( gc );
143 parseGroup( 0L, docElem );
144
145 TQWMatrix mat;
146 mat.scale( 1, -1 );
147 mat.translate( 0, -m_document.height() );
148 VTransformCmd trafo( 0L, mat );
149 trafo.visit( m_document );
150 outdoc = m_document.saveXML();
151}
152
153#define DPI 90
154
155double
156XAMLImport::toPercentage( TQString s )
157{
158 if( s.endsWith( "%" ) )
159 return s.remove( '%' ).toDouble();
160 else
161 return s.toDouble() * 100.0;
162}
163
164double
165XAMLImport::fromPercentage( TQString s )
166{
167 if( s.endsWith( "%" ) )
168 return s.remove( '%' ).toDouble() / 100.0;
169 else
170 return s.toDouble();
171}
172
173// parses the number into parameter number
174const char *
175getNumber( const char *ptr, double &number )
176{
177 int integer, exponent;
178 double decimal, frac;
179 int sign, expsign;
180
181 exponent = 0;
182 integer = 0;
183 frac = 1.0;
184 decimal = 0;
185 sign = 1;
186 expsign = 1;
187
188 // read the sign
189 if(*ptr == '+')
190 ptr++;
191 else if(*ptr == '-')
192 {
193 ptr++;
194 sign = -1;
195 }
196
197 // read the integer part
198 while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
199 integer = (integer * 10) + *(ptr++) - '0';
200 if(*ptr == '.') // read the decimals
201 {
202 ptr++;
203 while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
204 decimal += (*(ptr++) - '0') * (frac *= 0.1);
205 }
206
207 if(*ptr == 'e' || *ptr == 'E') // read the exponent part
208 {
209 ptr++;
210
211 // read the sign of the exponent
212 if(*ptr == '+')
213 ptr++;
214 else if(*ptr == '-')
215 {
216 ptr++;
217 expsign = -1;
218 }
219
220 exponent = 0;
221 while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9')
222 {
223 exponent *= 10;
224 exponent += *ptr - '0';
225 ptr++;
226 }
227 }
228 number = integer + decimal;
229 number *= sign * pow( (double)10, double( expsign * exponent ) );
230
231 return ptr;
232}
233
234
235double
236XAMLImport::parseUnit( const TQString &unit, bool horiz, bool vert, KoRect bbox )
237{
238 // TODO : percentage?
239 double value = 0;
240 const char *start = unit.latin1();
241 if(!start) {
242 return 0;
243 }
244 const char *end = getNumber( start, value );
245
246 if( uint( end - start ) < unit.length() )
247 {
248 if( unit.right( 2 ) == "pt" )
249 value = ( value / 72.0 ) * DPI;
250 else if( unit.right( 2 ) == "cm" )
251 value = ( value / 2.54 ) * DPI;
252 else if( unit.right( 2 ) == "pc" )
253 value = ( value / 6.0 ) * DPI;
254 else if( unit.right( 2 ) == "mm" )
255 value = ( value / 25.4 ) * DPI;
256 else if( unit.right( 2 ) == "in" )
257 value = value * DPI;
258 else if( unit.right( 2 ) == "pt" )
259 value = ( value / 72.0 ) * DPI;
260 else if( unit.right( 2 ) == "em" )
261 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 ) );
262 else if( unit.right( 1 ) == "%" )
263 {
264 if( horiz && vert )
265 value = ( value / 100.0 ) * (sqrt( pow( bbox.width(), 2 ) + pow( bbox.height(), 2 ) ) / sqrt( 2.0 ) );
266 else if( horiz )
267 value = ( value / 100.0 ) * bbox.width();
268 else if( vert )
269 value = ( value / 100.0 ) * bbox.height();
270 }
271 }
272 /*else
273 {
274 if( m_gc.current() )
275 {
276 if( horiz && vert )
277 value *= sqrt( pow( m_gc.current()->matrix.m11(), 2 ) + pow( m_gc.current()->matrix.m22(), 2 ) ) / sqrt( 2.0 );
278 else if( horiz )
279 value /= m_gc.current()->matrix.m11();
280 else if( vert )
281 value /= m_gc.current()->matrix.m22();
282 }
283 }*/
284 return value;
285}
286
287TQColor
288XAMLImport::parseColor( const TQString &rgbColor )
289{
290 int r, g, b;
291 keywordToRGB( rgbColor, r, g, b );
292 return TQColor( r, g, b );
293}
294
295void
296XAMLImport::parseColor( VColor &color, const TQString &s )
297{
298 if( s.startsWith( "rgb(" ) )
299 {
300 TQString parse = s.stripWhiteSpace();
301 TQStringList colors = TQStringList::split( ',', parse );
302 TQString r = colors[0].right( ( colors[0].length() - 4 ) );
303 TQString g = colors[1];
304 TQString b = colors[2].left( ( colors[2].length() - 1 ) );
305
306 if( r.contains( "%" ) )
307 {
308 r = r.left( r.length() - 1 );
309 r = TQString::number( int( ( double( 255 * r.toDouble() ) / 100.0 ) ) );
310 }
311
312 if( g.contains( "%" ) )
313 {
314 g = g.left( g.length() - 1 );
315 g = TQString::number( int( ( double( 255 * g.toDouble() ) / 100.0 ) ) );
316 }
317
318 if( b.contains( "%" ) )
319 {
320 b = b.left( b.length() - 1 );
321 b = TQString::number( int( ( double( 255 * b.toDouble() ) / 100.0 ) ) );
322 }
323
324 TQColor c( r.toInt(), g.toInt(), b.toInt() );
325 color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
326 }
327 else
328 {
329 TQString rgbColor = s.stripWhiteSpace();
330 TQColor c;
331 if( rgbColor.startsWith( "#" ) )
332 c.setNamedColor( rgbColor );
333 else
334 c = parseColor( rgbColor );
335 color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
336 }
337}
338
339void
340XAMLImport::parseColorStops( VGradient *gradient, const TQDomElement &e )
341{
342 VColor c;
343 for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
344 {
345 TQDomElement stop = n.toElement();
346 if( stop.tagName() == "stop" )
347 {
348 float offset;
349 TQString temp = stop.attribute( "offset" );
350 if( temp.contains( '%' ) )
351 {
352 temp = temp.left( temp.length() - 1 );
353 offset = temp.toFloat() / 100.0;
354 }
355 else
356 offset = temp.toFloat();
357
358 if( !stop.attribute( "stop-color" ).isEmpty() )
359 parseColor( c, stop.attribute( "stop-color" ) );
360 else
361 {
362 // try style attr
363 TQString style = stop.attribute( "style" ).simplifyWhiteSpace();
364 TQStringList substyles = TQStringList::split( ';', style );
365 for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
366 {
367 TQStringList substyle = TQStringList::split( ':', (*it) );
368 TQString command = substyle[0].stripWhiteSpace();
369 TQString params = substyle[1].stripWhiteSpace();
370 if( command == "stop-color" )
371 parseColor( c, params );
372 if( command == "stop-opacity" )
373 c.setOpacity( params.toDouble() );
374 }
375
376 }
377 if( !stop.attribute( "stop-opacity" ).isEmpty() )
378 c.setOpacity( stop.attribute( "stop-opacity" ).toDouble() );
379 gradient->addStop( c, offset, 0.5 );
380 }
381 }
382}
383
384void
385XAMLImport::parseGradient( const TQDomElement &e )
386{
387 GradientHelper gradhelper;
388 gradhelper.gradient.clearStops();
389 gradhelper.gradient.setRepeatMethod( VGradient::none );
390
391 TQString href = e.attribute( "xlink:href" ).mid( 1 );
392 if( !href.isEmpty() )
393 {
394 //kdDebug() << "Indexing with href : " << href.latin1() << endl;
395 gradhelper.gradient = m_gradients[ href ].gradient;
396 }
397
398 gradhelper.bbox = e.attribute( "gradientUnits" ) != "userSpaceOnUse";
399
400 if( e.tagName() == "linearGradient" )
401 {
402 if( gradhelper.bbox )
403 {
404 gradhelper.gradient.setOrigin( KoPoint( toPercentage( e.attribute( "x1", "0%" ) ), toPercentage( e.attribute( "y1", "0%" ) ) ) );
405 gradhelper.gradient.setVector( KoPoint( toPercentage( e.attribute( "x2", "100%" ) ), toPercentage( e.attribute( "y2", "0%" ) ) ) );
406 }
407 else
408 {
409 gradhelper.gradient.setOrigin( KoPoint( e.attribute( "x1" ).toDouble(), e.attribute( "y1" ).toDouble() ) );
410 gradhelper.gradient.setVector( KoPoint( e.attribute( "x2" ).toDouble(), e.attribute( "y2" ).toDouble() ) );
411 }
412 }
413 else
414 {
415 if( gradhelper.bbox )
416 {
417 gradhelper.gradient.setOrigin( KoPoint( toPercentage( e.attribute( "cx", "50%" ) ), toPercentage( e.attribute( "cy", "50%" ) ) ) );
418 gradhelper.gradient.setVector( KoPoint( toPercentage( e.attribute( "cx", "50%" ) ) + toPercentage( e.attribute( "r", "50%" ) ),
419 toPercentage( e.attribute( "cy", "50%" ) ) ) );
420 gradhelper.gradient.setFocalPoint( KoPoint( toPercentage( e.attribute( "fx", "50%" ) ), toPercentage( e.attribute( "fy", "50%" ) ) ) );
421 }
422 else
423 {
424 gradhelper.gradient.setOrigin( KoPoint( e.attribute( "cx" ).toDouble(), e.attribute( "cy" ).toDouble() ) );
425 gradhelper.gradient.setFocalPoint( KoPoint( e.attribute( "fx" ).toDouble(), e.attribute( "fy" ).toDouble() ) );
426 gradhelper.gradient.setVector( KoPoint( e.attribute( "cx" ).toDouble() + e.attribute( "r" ).toDouble(), e.attribute( "cy" ).toDouble() ) );
427 }
428 gradhelper.gradient.setType( VGradient::radial );
429 }
430 // handle spread method
431 TQString spreadMethod = e.attribute( "spreadMethod" );
432 if( !spreadMethod.isEmpty() )
433 {
434 if( spreadMethod == "reflect" )
435 gradhelper.gradient.setRepeatMethod( VGradient::reflect );
436 else if( spreadMethod == "repeat" )
437 gradhelper.gradient.setRepeatMethod( VGradient::repeat );
438 }
439 parseColorStops( &gradhelper.gradient, e );
440 //gradient.setGradientTransform( parseTransform( e.attribute( "gradientTransform" ) ) );
441 gradhelper.gradientTransform = VPath::parseTransform( e.attribute( "gradientTransform" ) );
442 m_gradients.insert( e.attribute( "id" ), gradhelper );
443}
444
445void
446XAMLImport::parsePA( VObject *obj, XAMLGraphicsContext *gc, const TQString &command, const TQString &params )
447{
448 VColor fillcolor = gc->fill.color();
449 VColor strokecolor = gc->stroke.color();
450
451 if( command == "fill" )
452 {
453 if( params == "none" )
454 gc->fill.setType( VFill::none );
455 else if( params.startsWith( "url(" ) )
456 {
457 unsigned int start = params.find("#") + 1;
458 unsigned int end = params.findRev(")");
459 TQString key = params.mid( start, end - start );
460 gc->fill.gradient() = m_gradients[ key ].gradient;
461 if( m_gradients[ key ].bbox )
462 {
463 // adjust to bbox
464 KoRect bbox = obj->boundingBox();
465 //kdDebug() << "bbox x : " << bbox.x() << endl;
466 //kdDebug() << "!!!!!!bbox y : " << bbox.y() << endl;
467 //kdDebug() << gc->fill.gradient().origin().x() << endl;
468 //kdDebug() << gc->fill.gradient().vector().x() << endl;
469 double offsetx = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().origin().x() ), true, false, bbox );
470 double offsety = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().origin().y() ), false, true, bbox );
471 gc->fill.gradient().setOrigin( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
472 offsetx = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().focalPoint().x() ), true, false, bbox );
473 offsety = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().focalPoint().y() ), false, true, bbox );
474 gc->fill.gradient().setFocalPoint( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
475 offsetx = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().vector().x() ), true, false, bbox );
476 offsety = parseUnit( TQString( "%1%" ).arg( gc->fill.gradient().vector().y() ), false, true, bbox );
477 gc->fill.gradient().setVector( KoPoint( bbox.x() + offsetx, bbox.y() + offsety ) );
478 //kdDebug() << offsety << endl;
479 //kdDebug() << gc->fill.gradient().origin().x() << endl;
480 //kdDebug() << gc->fill.gradient().origin().y() << endl;
481 //kdDebug() << gc->fill.gradient().vector().x() << endl;
482 //kdDebug() << gc->fill.gradient().vector().y() << endl;
483 }
484 gc->fill.gradient().transform( m_gradients[ key ].gradientTransform );
485 if( !m_gradients[ key ].bbox )
486 gc->fill.gradient().transform( gc->matrix );
487 gc->fill.setType( VFill::grad );
488 }
489 else
490 {
491 parseColor( fillcolor, params );
492 gc->fill.setType( VFill::solid );
493 }
494 }
495 else if( command == "fill-rule" )
496 {
497 if( params == "nonzero" )
498 gc->fillRule = winding;
499 else if( params == "evenodd" )
500 gc->fillRule = evenOdd;
501 }
502 else if( command == "stroke" )
503 {
504 if( params == "none" )
505 gc->stroke.setType( VStroke::none );
506 else if( params.startsWith( "url(" ) )
507 {
508 unsigned int start = params.find("#") + 1;
509 unsigned int end = params.findRev(")");
510 TQString key = params.mid( start, end - start );
511 gc->stroke.gradient() = m_gradients[ key ].gradient;
512 gc->stroke.gradient().transform( m_gradients[ key ].gradientTransform );
513 gc->stroke.gradient().transform( gc->matrix );
514 gc->stroke.setType( VStroke::grad );
515 }
516 else
517 {
518 parseColor( strokecolor, params );
519 gc->stroke.setType( VStroke::solid );
520 }
521 }
522 else if( command == "stroke-width" )
523 gc->stroke.setLineWidth( parseUnit( params, true, true, m_outerRect ) );
524 else if( command == "stroke-linejoin" )
525 {
526 if( params == "miter" )
527 gc->stroke.setLineJoin( VStroke::joinMiter );
528 else if( params == "round" )
529 gc->stroke.setLineJoin( VStroke::joinRound );
530 else if( params == "bevel" )
531 gc->stroke.setLineJoin( VStroke::joinBevel );
532 }
533 else if( command == "stroke-linecap" )
534 {
535 if( params == "butt" )
536 gc->stroke.setLineCap( VStroke::capButt );
537 else if( params == "round" )
538 gc->stroke.setLineCap( VStroke::capRound );
539 else if( params == "square" )
540 gc->stroke.setLineCap( VStroke::capSquare );
541 }
542 else if( command == "stroke-miterlimit" )
543 gc->stroke.setMiterLimit( params.toFloat() );
544 else if( command == "stroke-dasharray" )
545 {
546 TQValueList<float> array;
547 if(params != "none")
548 {
549 TQStringList dashes = TQStringList::split( ' ', params );
550 for( TQStringList::Iterator it = dashes.begin(); it != dashes.end(); ++it )
551 array.append( (*it).toFloat() );
552 }
553 gc->stroke.dashPattern().setArray( array );
554 }
555 else if( command == "stroke-dashoffset" )
556 gc->stroke.dashPattern().setOffset( params.toFloat() );
557 // handle opacity
558 else if( command == "stroke-opacity" )
559 strokecolor.setOpacity( fromPercentage( params ) );
560 else if( command == "fill-opacity" )
561 fillcolor.setOpacity( fromPercentage( params ) );
562 else if( command == "opacity" )
563 {
564 fillcolor.setOpacity( fromPercentage( params ) );
565 strokecolor.setOpacity( fromPercentage( params ) );
566 }
567 else if( command == "font-family" )
568 {
569 TQString family = params;
570 family.replace( '\'' , ' ' );
571 gc->font.setFamily( family );
572 }
573 else if( command == "font-size" )
574 {
575 float pointSize = parseUnit( params );
576 pointSize *= gc->matrix.m22() > 0 ? gc->matrix.m22() : -1.0 * gc->matrix.m22();
577 gc->font.setPointSizeFloat( pointSize );
578 }
579 else if( command == "text-decoration" )
580 {
581 if( params == "line-through" )
582 gc->font.setStrikeOut( true );
583 else if( params == "underline" )
584 gc->font.setUnderline( true );
585 }
586 if( gc->fill.type() != VFill::none )
587 gc->fill.setColor( fillcolor, false );
588 //if( gc->stroke.type() == VStroke::solid )
589 gc->stroke.setColor( strokecolor );
590}
591
592void
593XAMLImport::addGraphicContext()
594{
595 XAMLGraphicsContext *gc = new XAMLGraphicsContext;
596 // set as default
597 if( m_gc.current() )
598 *gc = *( m_gc.current() );
599 m_gc.push( gc );
600}
601
602void
603XAMLImport::setupTransform( const TQDomElement &e )
604{
605 XAMLGraphicsContext *gc = m_gc.current();
606
607 TQWMatrix mat = VPath::parseTransform( e.attribute( "transform" ) );
608 gc->matrix = mat * gc->matrix;
609}
610
611void
612XAMLImport::parseStyle( VObject *obj, const TQDomElement &e )
613{
614 XAMLGraphicsContext *gc = m_gc.current();
615 if( !gc ) return;
616
617 // try normal PA
618 if( !e.attribute( "fill" ).isEmpty() )
619 parsePA( obj, gc, "fill", e.attribute( "fill" ) );
620 if( !e.attribute( "fill-rule" ).isEmpty() )
621 parsePA( obj, gc, "fill-rule", e.attribute( "fill-rule" ) );
622 if( !e.attribute( "stroke" ).isEmpty() )
623 parsePA( obj, gc, "stroke", e.attribute( "stroke" ) );
624 if( !e.attribute( "stroke-width" ).isEmpty() )
625 parsePA( obj, gc, "stroke-width", e.attribute( "stroke-width" ) );
626 if( !e.attribute( "stroke-linejoin" ).isEmpty() )
627 parsePA( obj, gc, "stroke-linejoin", e.attribute( "stroke-linejoin" ) );
628 if( !e.attribute( "stroke-linecap" ).isEmpty() )
629 parsePA( obj, gc, "stroke-linecap", e.attribute( "stroke-linecap" ) );
630 if( !e.attribute( "stroke-dasharray" ).isEmpty() )
631 parsePA( obj, gc, "stroke-dasharray", e.attribute( "stroke-dasharray" ) );
632 if( !e.attribute( "stroke-dashoffset" ).isEmpty() )
633 parsePA( obj, gc, "stroke-dashoffset", e.attribute( "stroke-dashoffset" ) );
634 if( !e.attribute( "stroke-opacity" ).isEmpty() )
635 parsePA( obj, gc, "stroke-opacity", e.attribute( "stroke-opacity" ) );
636 if( !e.attribute( "stroke-miterlimit" ).isEmpty() )
637 parsePA( obj, gc, "stroke-miterlimit", e.attribute( "stroke-miterlimit" ) );
638 if( !e.attribute( "fill-opacity" ).isEmpty() )
639 parsePA( obj, gc, "fill-opacity", e.attribute( "fill-opacity" ) );
640 if( !e.attribute( "opacity" ).isEmpty() )
641 parsePA( obj, gc, "opacity", e.attribute( "opacity" ) );
642
643 // try style attr
644 TQString style = e.attribute( "style" ).simplifyWhiteSpace();
645 TQStringList substyles = TQStringList::split( ';', style );
646 for( TQStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it )
647 {
648 TQStringList substyle = TQStringList::split( ':', (*it) );
649 TQString command = substyle[0].stripWhiteSpace();
650 TQString params = substyle[1].stripWhiteSpace();
651 parsePA( obj, gc, command, params );
652 }
653
654 obj->setFill( gc->fill );
655 if( dynamic_cast<VPath *>( obj ) )
656 dynamic_cast<VPath *>( obj )->setFillRule( gc->fillRule );
657 // stroke scaling
658 double lineWidth = gc->stroke.lineWidth();
659 gc->stroke.setLineWidth( lineWidth * sqrt( pow( m_gc.current()->matrix.m11(), 2 ) + pow( m_gc.current()->matrix.m22(), 2 ) ) / sqrt( 2.0 ) );
660 obj->setStroke( gc->stroke );
661 gc->stroke.setLineWidth( lineWidth );
662}
663
664void
665XAMLImport::parseFont( const TQDomElement &e )
666{
667 XAMLGraphicsContext *gc = m_gc.current();
668 if( !gc ) return;
669
670 if( ! e.attribute( "font-family" ).isEmpty() )
671 parsePA( 0L, m_gc.current(), "font-family", e.attribute( "font-family" ) );
672 if( ! e.attribute( "font-size" ).isEmpty() )
673 parsePA( 0L, m_gc.current(), "font-size", e.attribute( "font-size" ) );
674 if( ! e.attribute( "text-decoration" ).isEmpty() )
675 parsePA( 0L, m_gc.current(), "text-decoration", e.attribute( "text-decoration" ) );
676}
677
678void
679XAMLImport::parseGroup( VGroup *grp, const TQDomElement &e )
680{
681 bool isDef = false;
682 if( e.tagName() == "defs" )
683 isDef = true;
684
685 for( TQDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
686 {
687 TQDomElement b = n.toElement();
688 if( b.isNull() ) continue;
689 VObject *obj = 0L;
690 if( b.tagName() == "g" )
691 {
692 VGroup *group;
693 if ( grp )
694 group = new VGroup( grp );
695 else
696 group = new VGroup( &m_document );
697
698 addGraphicContext();
699 setupTransform( b );
700 parseStyle( group, b );
701 parseFont( b );
702 parseGroup( group, b );
703
704 // handle id
705 if( !b.attribute("id").isEmpty() )
706 group->setName( b.attribute("id") );
707 if( grp )
708 grp->append( group );
709 else
710 m_document.append( group );
711 delete( m_gc.pop() );
712 continue;
713 }
714 if( b.tagName() == "defs" )
715 {
716 parseGroup( 0L, b ); // try for gradients at least
717 continue;
718 }
719 else if( b.tagName() == "linearGradient" || b.tagName() == "radialGradient" )
720 {
721 parseGradient( b );
722 continue;
723 }
724 else if( b.tagName() == "rect" ||
725 b.tagName() == "ellipse" ||
726 b.tagName() == "circle" ||
727 b.tagName() == "line" ||
728 b.tagName() == "polyline" ||
729 b.tagName() == "polygon" ||
730 b.tagName() == "path" ||
731 b.tagName() == "image" )
732 {
733 if (!isDef)
734 obj = createObject( b );
735 else
736 m_paths.insert( b.attribute( "id" ), b );
737 }
738 else if( b.tagName() == "text" )
739 {
740 if( isDef )
741 m_paths.insert( b.attribute( "id" ), b );
742 else
743 createText( grp, b );
744 }
745 else if( b.tagName() == "use" )
746 {
747 double tx = b.attribute( "x" ).toDouble();
748 double ty = b.attribute( "y" ).toDouble();
749
750 if( !b.attribute( "xlink:href" ).isEmpty() )
751 {
752 TQString params = b.attribute( "xlink:href" );
753 unsigned int start = params.find("#") + 1;
754 unsigned int end = params.findRev(")");
755 TQString key = params.mid( start, end - start );
756 if(m_paths.contains(key))
757 {
758 TQDomElement a = m_paths[key];
759 obj = createObject( a );
760 m_gc.current()->matrix.translate(tx,ty);
761 parsePA( grp, m_gc.current(), "fill", b.attribute( "fill" ) );
762 }
763 }
764 }
765 if( !obj ) continue;
766 VTransformCmd trafo( 0L, m_gc.current()->matrix );
767 trafo.visit( *obj );
768 parseStyle( obj, b );
769 // handle id
770 if( !b.attribute("id").isEmpty() )
771 obj->setName( b.attribute("id") );
772 if( grp )
773 grp->append( obj );
774 else
775 m_document.append( obj );
776 delete( m_gc.pop() );
777 }
778}
779
780VObject* XAMLImport::findObject( const TQString &name, VGroup* group )
781{
782 if( ! group )
783 return 0L;
784
785 VObjectListIterator itr = group->objects();
786
787 for( uint objcount = 1; itr.current(); ++itr, objcount++ )
788 if( itr.current()->state() != VObject::deleted )
789 {
790 if( itr.current()->name() == name )
791 return itr.current();
792
793 if( dynamic_cast<VGroup *>( itr.current() ) )
794 {
795 VObject *obj = findObject( name, dynamic_cast<VGroup *>( itr.current() ) );
796 if( obj )
797 return obj;
798 }
799 }
800
801 return 0L;
802}
803
804VObject* XAMLImport::findObject( const TQString &name )
805{
806 TQPtrVector<VLayer> vector;
807 m_document.layers().toVector( &vector );
808 for( int i = vector.count() - 1; i >= 0; i-- )
809 {
810 if ( vector[i]->state() != VObject::deleted )
811 {
812 VObject* obj = findObject( name, dynamic_cast<VGroup *>( vector[i] ) );
813 if( obj )
814 return obj;
815 }
816 }
817
818 return 0L;
819}
820
821void XAMLImport::createText( VGroup *grp, const TQDomElement &b )
822{
823 VText *text = 0L;
824 TQString content;
825 VSubpath base( 0L );
826 VPath *path = 0L;
827
828 addGraphicContext();
829 setupTransform( b );
830 VTransformCmd trafo( 0L, m_gc.current()->matrix );
831
832 parseFont( b );
833
834 if( b.hasChildNodes() )
835 {
836 if( base.isEmpty() && ! b.attribute( "x" ).isEmpty() && ! b.attribute( "y" ).isEmpty() )
837 {
838 double x = parseUnit( b.attribute( "x" ) );
839 double y = parseUnit( b.attribute( "y" ) );
840 base.moveTo( KoPoint( x, y ) );
841 base.lineTo( KoPoint( x + 10, y ) );
842 }
843
844 for( TQDomNode n = b.firstChild(); !n.isNull(); n = n.nextSibling() )
845 {
846 TQDomElement e = n.toElement();
847 if( e.isNull() )
848 {
849 content += n.toCharacterData().data();
850 }
851 else if( e.tagName() == "textPath" )
852 {
853 if( e.attribute( "xlink:href" ).isEmpty() )
854 continue;
855
856 TQString uri = e.attribute( "xlink:href" );
857 unsigned int start = uri.find("#") + 1;
858 unsigned int end = uri.findRev(")");
859 TQString key = uri.mid( start, end - start );
860 if( ! m_paths.contains(key) )
861 {
862 VObject* obj = findObject( key );
863 if( obj )
864 path = dynamic_cast<VPath*>( obj );
865 }
866 else
867 {
868 TQDomElement p = m_paths[key];
869 path = dynamic_cast<VPath*>( createObject( p ) );
870 if( path )
871 path->setState( VObject::deleted );
872 }
873 if( ! path )
874 continue;
875 base = *path->paths().getFirst();
876 content += e.text();
877 }
878 else if( e.tagName() == "tspan" )
879 {
880 // only use text of tspan element, as we are not supporting text
881 // with different styles
882 content += e.text();
883 if( base.isEmpty() && ! e.attribute( "x" ).isEmpty() && ! e.attribute( "y" ).isEmpty() )
884 {
885 TQStringList posX = TQStringList::split( ", ", e.attribute( "x" ) );
886 TQStringList posY = TQStringList::split( ", ", e.attribute( "y" ) );
887 if( posX.count() && posY.count() )
888 {
889 double x = parseUnit( posX.first() );
890 double y = parseUnit( posY.first() );
891 base.moveTo( KoPoint( x, y ) );
892 base.lineTo( KoPoint( x + 10, y ) );
893 }
894 }
895 }
896 else if( e.tagName() == "tref" )
897 {
898 if( e.attribute( "xlink:href" ).isEmpty() )
899 continue;
900
901 TQString uri = e.attribute( "xlink:href" );
902 unsigned int start = uri.find("#") + 1;
903 unsigned int end = uri.findRev(")");
904 TQString key = uri.mid( start, end - start );
905
906 if( ! m_paths.contains(key) )
907 {
908 VObject* obj = findObject( key );
909 if( obj )
910 content += dynamic_cast<VText*>( obj )->text();
911 }
912 else
913 {
914 TQDomElement p = m_paths[key];
915 content += p.text();
916 }
917 }
918 else
919 continue;
920 }
921 text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, content.simplifyWhiteSpace() );
922 }
923 else
924 {
925 VSubpath base( 0L );
926 double x = parseUnit( b.attribute( "x" ) );
927 double y = parseUnit( b.attribute( "y" ) );
928 base.moveTo( KoPoint( x, y ) );
929 base.lineTo( KoPoint( x + 10, y ) );
930 text = new VText( m_gc.current()->font, base, VText::Above, VText::Left, b.text().simplifyWhiteSpace() );
931 }
932
933 if( text )
934 {
935 text->setParent( &m_document );
936
937 parseStyle( text, b );
938 trafo.visit( *text );
939
940 if( !b.attribute("id").isEmpty() )
941 text->setName( b.attribute("id") );
942
943 if( grp )
944 grp->append( text );
945 else
946 m_document.append( text );
947 }
948 delete( m_gc.pop() );
949}
950
951VObject* XAMLImport::createObject( const TQDomElement &b )
952{
953 if( b.tagName() == "rect" )
954 {
955 addGraphicContext();
956 double x = parseUnit( b.attribute( "x" ), true, false, m_outerRect );
957 double y = parseUnit( b.attribute( "y" ), false, true, m_outerRect );
958 double width = parseUnit( b.attribute( "width" ), true, false, m_outerRect );
959 double height = parseUnit( b.attribute( "height" ), false, true, m_outerRect );
960 setupTransform( b );
961 return new VRectangle( 0L, KoPoint( x, height + y ) , width, height );
962 }
963 else if( b.tagName() == "ellipse" )
964 {
965 addGraphicContext();
966 setupTransform( b );
967 double rx = parseUnit( b.attribute( "rx" ) );
968 double ry = parseUnit( b.attribute( "ry" ) );
969 double left = parseUnit( b.attribute( "cx" ) ) - rx;
970 double top = parseUnit( b.attribute( "cy" ) ) - ry;
971 return new VEllipse( 0L, KoPoint( left, top ), rx * 2.0, ry * 2.0 );
972 }
973 else if( b.tagName() == "circle" )
974 {
975 addGraphicContext();
976 setupTransform( b );
977 double r = parseUnit( b.attribute( "r" ) );
978 double left = parseUnit( b.attribute( "cx" ) ) - r;
979 double top = parseUnit( b.attribute( "cy" ) ) - r;
980 return new VEllipse( 0L, KoPoint( left, top ), r * 2.0, r * 2.0 );
981 }
982 else if( b.tagName() == "line" )
983 {
984 addGraphicContext();
985 setupTransform( b );
986 VPath *path = new VPath( &m_document );
987 double x1 = b.attribute( "x1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x1" ) );
988 double y1 = b.attribute( "y1" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y1" ) );
989 double x2 = b.attribute( "x2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "x2" ) );
990 double y2 = b.attribute( "y2" ).isEmpty() ? 0.0 : parseUnit( b.attribute( "y2" ) );
991 path->moveTo( KoPoint( x1, y1 ) );
992 path->lineTo( KoPoint( x2, y2 ) );
993 return path;
994 }
995 else if( b.tagName() == "polyline" || b.tagName() == "polygon" )
996 {
997 addGraphicContext();
998 setupTransform( b );
999 VPath *path = new VPath( &m_document );
1000 bool bFirst = true;
1001
1002 TQString points = b.attribute( "points" ).simplifyWhiteSpace();
1003 points.replace( ',', ' ' );
1004 points.remove( '\r' );
1005 points.remove( '\n' );
1006 TQStringList pointList = TQStringList::split( ' ', points );
1007 for( TQStringList::Iterator it = pointList.begin(); it != pointList.end(); ++it)
1008 {
1009 KoPoint point;
1010 point.setX( (*it).toDouble() );
1011 point.setY( (*it).toDouble() );
1012 if( bFirst )
1013 {
1014 path->moveTo( point );
1015 bFirst = false;
1016 }
1017 else
1018 path->lineTo( point );
1019 }
1020 if( b.tagName() == "polygon" ) path->close();
1021 return path;
1022 }
1023 else if( b.tagName() == "path" )
1024 {
1025 addGraphicContext();
1026 setupTransform( b );
1027 VPath *path = new VPath( &m_document );
1028 path->loadSvgPath( b.attribute( "d" ) );
1029 return path;
1030 }
1031 else if( b.tagName() == "image" )
1032 {
1033 addGraphicContext();
1034 setupTransform( b );
1035 TQString fname = b.attribute("xlink:href");
1036 return new VImage( 0L, fname );
1037 }
1038
1039 return 0L;
1040}
1041
1042#include <xamlimport.moc>