1
// -*- Mode: c++-mode; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4; -*-
2
/* This file is part of the KDE project
3
Copyright (c) 2003 Rob Buis <buis@kde.org>
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Library General Public
7
License as published by the Free Software Foundation; either
8
version 2 of the License, or (at your option) any later version.
10
This library is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Library General Public License for more details.
15
You should have received a copy of the GNU Library General Public License
16
along with this library; see the file COPYING.LIB. If not, write to
17
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
Boston, MA 02111-1307, USA.
21
#include "oodrawimport.h"
26
#include <qdatetime.h>
27
#include <qfileinfo.h>
34
#include <koDocumentInfo.h>
35
#include <koDocument.h>
37
#include <kgenericfactory.h>
38
#include <koFilterChain.h>
40
#include <stylestack.h>
42
#include <shapes/vellipse.h>
43
#include <shapes/vrectangle.h>
44
#include <shapes/vpolygon.h>
46
#include <core/vfill.h>
47
#include <core/vgroup.h>
49
typedef KGenericFactory<OoDrawImport, KoFilter> OoDrawImportFactory;
50
K_EXPORT_COMPONENT_FACTORY( liboodrawimport, OoDrawImportFactory( "oodrawimport" ) )
53
OoDrawImport::OoDrawImport( KoFilter *, const char *, const QStringList & )
57
m_styles.setAutoDelete( true );
60
OoDrawImport::~OoDrawImport()
64
KoFilter::ConversionStatus
65
OoDrawImport::convert( QCString const & from, QCString const & to )
67
kdDebug() << "Entering Oodraw Import filter: " << from << " - " << to << endl;
69
if( from != "application/vnd.sun.xml.draw" || to != "application/x-karbon" )
71
kdWarning() << "Invalid mimetypes " << from << " " << to << endl;
72
return KoFilter::NotImplemented;
75
KoFilter::ConversionStatus preStatus = openFile();
77
if( preStatus != KoFilter::OK )
80
/*QDomDocument docinfo;
81
createDocumentInfo( docinfo );
83
// store document info
84
KoStoreDevice* out = m_chain->storageFile( "documentinfo.xml", KoStore::Write );
87
QCString info = docinfo.toCString();
88
//kdDebug() << " info :" << info << endl;
89
// WARNING: we cannot use KoStore::write(const QByteArray&) because it gives an extra NULL character at the end.
90
out->writeBlock( info , info.length() );
94
QDomDocument outdoc = m_document.saveXML();
96
// store document content
97
KoStoreDevice *out = m_chain->storageFile( "maindoc.xml", KoStore::Write );
100
QCString content = outdoc.toCString();
101
kdDebug() << " content :" << content << endl;
102
out->writeBlock( content , content.length() );
105
kdDebug() << "######################## OoDrawImport::convert done ####################" << endl;
109
// Very related to OoWriterImport::openFile()
110
KoFilter::ConversionStatus
111
OoDrawImport::openFile()
113
KoStore *store = KoStore::createStore( m_chain->inputFile(), KoStore::Read);
117
kdWarning() << "Couldn't open the requested file." << endl;
118
return KoFilter::FileNotFound;
121
if( !store->open( "content.xml" ) )
123
kdWarning() << "This file doesn't seem to be a valid OoDraw file" << endl;
125
return KoFilter::WrongFormat;
130
m_content.setContent( store->device() );
133
//kdDebug() << "m_content.toCString() :" << m_content.toCString() << endl;
134
kdDebug() << "File containing content loaded " << endl;
136
if( store->open( "styles.xml" ) )
138
styles.setContent( store->device() );
141
//kdDebug() << "styles.toCString() :" << styles.toCString() << endl;
142
kdDebug() << "File containing styles loaded" << endl;
145
kdWarning() << "Style definitions do not exist!" << endl;
147
if( store->open( "meta.xml" ) )
149
m_meta.setContent( store->device() );
152
kdDebug() << "File containing meta definitions loaded" << endl;
155
kdWarning() << "Meta definitions do not exist!" << endl;
157
if( store->open( "settings.xml" ) )
159
m_settings.setContent( store->device() );
162
kdDebug() << "File containing settings loaded" << endl;
165
kdWarning() << "Settings do not exist!" << endl;
169
emit sigProgress( 10 );
170
createStyleMap( styles );
176
OoDrawImport::convert()
178
m_document.saveAsPath( false );
180
QDomElement content = m_content.documentElement();
182
// content.xml contains some automatic-styles that we need to store
183
QDomNode automaticStyles = content.namedItem( "office:automatic-styles" );
184
if( !automaticStyles.isNull() )
185
insertStyles( automaticStyles.toElement() );
187
QDomNode body = content.namedItem( "office:body" );
191
// we take the settings of the first slide for the whole document.
192
QDomNode drawPage = body.namedItem( "draw:page" );
193
if( drawPage.isNull() ) // no pages? give up.
196
QDomElement dp = drawPage.toElement();
197
QDomElement *master = m_styles[dp.attribute( "draw:master-page-name" )];
198
QDomElement *style = m_styles[master->attribute( "style:page-master-name" )];
199
QDomElement properties = style->namedItem( "style:properties" ).toElement();
201
if( properties.isNull() )
203
m_document.setWidth( 550.0 );
204
m_document.setHeight( 841.0 );
208
m_document.setWidth( KoUnit::parseValue(properties.attribute( "fo:page-width" ) ) );
209
m_document.setHeight( KoUnit::parseValue(properties.attribute( "fo:page-height" ) ) );
213
for( QDomNode drawPage = body.firstChild(); !drawPage.isNull(); drawPage = drawPage.nextSibling() )
215
QDomElement dp = drawPage.toElement();
216
m_styleStack.clear(); // remove all styles
217
fillStyleStack( dp );
218
//m_styleStack.setPageMark();
221
//QDomElement titleElement = doc.createElement( "Title" );
222
//titleElement.setAttribute( "title", dp.attribute( "draw:name" ) );
223
//pageTitleElement.appendChild( titleElement );
225
parseGroup( 0L, dp );
230
OoDrawImport::parseGroup( VGroup *parent, const QDomElement& parentobject )
233
for( QDomNode object = parentobject.firstChild(); !object.isNull(); object = object.nextSibling() )
235
QDomElement o = object.toElement();
236
QString name = o.tagName();
237
QString drawID = o.attribute("draw:id");
240
if( name == "draw:g" ) // polyline
242
storeObjectStyles( o );
243
VGroup *group = new VGroup( parent );
245
appendBrush( *group );
247
parseGroup( group, o );
249
else if( name == "draw:rect" ) // rectangle
251
storeObjectStyles( o );
252
double x = KoUnit::parseValue( o.attribute( "svg:x" ) );
253
double y = ymirror( KoUnit::parseValue( o.attribute( "svg:y" ) ) );
254
double w = KoUnit::parseValue( o.attribute( "svg:width" ) );
255
double h = KoUnit::parseValue( o.attribute( "svg:height" ) );
256
int corner = static_cast<int>( KoUnit::parseValue( o.attribute( "draw:corner-radius" ) ) );
257
VRectangle *rect = new VRectangle( parent, KoPoint( x, y ), w, h, corner );
259
appendBrush( *rect );
262
else if( name == "draw:circle" || name == "draw:ellipse" )
264
storeObjectStyles( o );
265
double w = KoUnit::parseValue( o.attribute( "svg:width" ) );
266
double h = KoUnit::parseValue( o.attribute( "svg:height" ) );
267
double x = KoUnit::parseValue( o.attribute( "svg:x" ) );
268
double y = ymirror( KoUnit::parseValue( o.attribute( "svg:y" ) ) ) - h;
269
double start = o.attribute( "draw:start-angle" ).toDouble();
270
double end = o.attribute( "draw:end-angle" ).toDouble();
271
QString kind = o.attribute( "draw:kind" );
272
VEllipse::VEllipseType type = VEllipse::full;
273
if( !kind.isEmpty() )
275
if( kind == "section" )
276
type = VEllipse::cut;
277
else if( kind == "cut" )
278
type = VEllipse::section;
279
else if( kind == "arc" )
280
type = VEllipse::arc;
282
VEllipse *ellipse = new VEllipse( parent, KoPoint( x, y ), w, h, type, start, end );
283
appendPen( *ellipse );
286
appendBrush( *ellipse );
289
else if( name == "draw:line" ) // line
291
storeObjectStyles( o );
292
VPath *line = new VPath( parent );
293
double x1 = KoUnit::parseValue( o.attribute( "svg:x1" ) );
294
double y1 = ymirror( KoUnit::parseValue( o.attribute( "svg:y1" ) ) );
295
double x2 = KoUnit::parseValue( o.attribute( "svg:x2" ) );
296
double y2 = ymirror( KoUnit::parseValue( o.attribute( "svg:y2" ) ) );
297
line->moveTo( KoPoint( x1, y1 ) );
298
line->lineTo( KoPoint( x2, y2 ) );
300
appendBrush( *line );
303
else if( name == "draw:polyline" ) // polyline
305
storeObjectStyles( o );
306
VPath *polyline = new VPath( parent );
307
appendPoints( *polyline, o);
308
appendPen( *polyline );
309
appendBrush( *polyline );
312
else if( name == "draw:polygon" ) // polygon
314
storeObjectStyles( o );
315
//VPolygon *polygon = new VPolygon( parent );
316
//polygon->load( o );
317
VPath *polygon = new VPath( parent );
318
appendPoints( *polygon, o );
319
appendPen( *polygon );
320
appendBrush( *polygon );
323
else if( name == "draw:path" ) // path
325
storeObjectStyles( o );
326
VPath *path = new VPath( parent );
327
path->loadSvgPath( o.attribute( "svg:d" ) );
328
KoRect rect = parseViewBox( o );
329
double x = KoUnit::parseValue( o.attribute( "svg:x" ) );
330
double y = ymirror( KoUnit::parseValue( o.attribute( "svg:y" ) ) );
331
double w = KoUnit::parseValue( o.attribute( "svg:width" ) );
332
double h = KoUnit::parseValue( o.attribute( "svg:height" ) );
334
mat.translate( x, y );
335
mat.scale( w / rect.width(), -h / rect.height() );
336
path->transform( mat );
338
appendBrush( *path );
341
/*else if( name == "draw:image" ) // image
343
storeObjectStyles( o );
344
e = doc.createElement( "OBJECT" );
345
e.setAttribute( "type", 0 );
346
appendImage( doc, e, pictureElement, o );
350
kdDebug() << "Unsupported object '" << name << "'" << endl;
354
parent->append( obj );
356
m_document.append( obj );
361
OoDrawImport::appendPen( VObject &obj )
363
if( m_styleStack.hasAttribute( "draw:stroke" ) )
367
if( m_styleStack.attribute( "draw:stroke" ) == "none" )
368
stroke.setType( VStroke::none );
369
else if( m_styleStack.attribute( "draw:stroke" ) == "solid" )
370
stroke.setType( VStroke::solid );
371
else if( m_styleStack.attribute( "draw:stroke" ) == "dash" )
373
QValueList<float> dashes;
374
stroke.setType( VStroke::solid );
375
QString style = m_styleStack.attribute( "draw:stroke-dash" );
376
if( style == "Ultrafine Dashed" ||
377
style == "Fine Dashed (var)" || style == "Dashed (var)" )
378
stroke.dashPattern().setArray( dashes << 2 << 2 );
379
else if( style == "Fine Dashed" )
380
stroke.dashPattern().setArray( dashes << 10 << 10 );
381
else if( style == "Fine Dotted" || style == "Ultrafine Dotted (var)" ||
382
style == "Line with Fine Dots" )
383
stroke.dashPattern().setArray( dashes << 2 << 10 );
384
else if( style == "3 Dashes 3 Dots (var)" || style == "Ultrafine 2 Dots 3 Dashes" )
385
stroke.dashPattern().setArray( dashes << 3 << 3 );
386
else if( style == "2 Dots 1 Dash" )
387
stroke.dashPattern().setArray( dashes << 2 << 1 );
389
if( m_styleStack.hasAttribute( "svg:stroke-width" ) )
391
double lwidth = KoUnit::parseValue( m_styleStack.attribute( "svg:stroke-width" ) );
394
stroke.setLineWidth( lwidth );
396
if( m_styleStack.hasAttribute( "svg:stroke-color" ) )
399
parseColor( c, m_styleStack.attribute( "svg:stroke-color" ) );
400
stroke.setColor( c );
403
obj.setStroke( stroke );
408
OoDrawImport::appendBrush( VObject &obj )
410
if( m_styleStack.hasAttribute( "draw:fill" ) )
412
const QString fill = m_styleStack.attribute( "draw:fill" );
415
if( fill == "solid" )
417
f.setType( VFill::solid );
418
if( m_styleStack.hasAttribute( "draw:fill-color" ) )
421
parseColor( c, m_styleStack.attribute( "draw:fill-color" ) );
425
else if( fill == "gradient" )
428
gradient.clearStops();
429
gradient.setRepeatMethod( VGradient::none );
430
QString style = m_styleStack.attribute( "draw:fill-gradient-name" );
432
QDomElement* draw = m_draws[style];
436
if( draw->hasAttribute( "draw:border" ) )
437
border += draw->attribute( "draw:border" ).remove( '%' ).toDouble() / 100.0;
439
parseColor( c, draw->attribute( "draw:start-color" ) );
440
gradient.addStop( c, border, 0.5 );
441
parseColor( c, draw->attribute( "draw:end-color" ) );
442
gradient.addStop( c, 1.0, 0.5 );
444
QString type = draw->attribute( "draw:style" );
445
if( type == "linear" || type == "axial" )
447
gradient.setType( VGradient::linear );
448
int angle = draw->attribute( "draw:angle" ).toInt() / 10;
450
// make sure the angle is between 0 and 359
451
angle = abs( angle );
452
angle -= ( (int) ( angle / 360 ) ) * 360;
454
// What we are trying to do here is to find out if the given
455
// angle belongs to a horizontal, vertical or diagonal gradient.
456
int lower, upper, nearAngle = 0;
457
for ( lower = 0, upper = 45; upper < 360; lower += 45, upper += 45 )
461
int distanceToUpper = abs( angle - upper );
462
int distanceToLower = abs( angle - lower );
463
nearAngle = distanceToUpper > distanceToLower ? lower : upper;
467
KoRect rect = obj.boundingBox();
468
KoPoint origin, vector;
469
// nearAngle should now be one of: 0, 45, 90, 135, 180...
470
kdDebug() << "nearAngle: " << nearAngle << endl;
471
if( nearAngle == 0 || nearAngle == 180 )
473
origin.setX( rect.x() + rect.width() );
474
origin.setY( rect.y() + rect.height());
475
vector.setX( rect.x() + rect.width() );
476
vector.setY( rect.y() );
478
else if( nearAngle == 90 || nearAngle == 270 )
480
origin.setX( rect.x() );
481
origin.setY( rect.y() + rect.height() );
482
vector.setX( rect.x() + rect.width() );
483
vector.setY( rect.y() + rect.height() );
485
else if( nearAngle == 45 || nearAngle == 225 )
487
origin.setX( rect.x() );
488
origin.setY( rect.y() );
489
vector.setX( rect.x() + rect.width() );
490
vector.setY( rect.y() + rect.height() );
492
else if( nearAngle == 135 || nearAngle == 315 )
494
origin.setX( rect.x() + rect.width() );
495
origin.setY( rect.y() + rect.height() );
496
vector.setX( rect.x() );
497
vector.setY( rect.y() );
500
gradient.setOrigin( origin );
501
gradient.setVector( vector );
503
else if( type == "radial" || type == "ellipsoid" )
505
gradient.setType( VGradient::radial );
506
//else if( type == "square" || type == "rectangular" )
507
//gradient.setAttribute( "type", 6 ); // rectangle
508
//else if( type == "axial" )
509
//gradient.setAttribute( "type", 7 ); // pipecross
511
// Hard to map between x- and y-center settings of oodraw
512
// and (un-)balanced settings of kpresenter. Let's try it.
514
if( draw->hasAttribute( "draw:cx" ) )
515
x = draw->attribute( "draw:cx" ).remove( '%' ).toDouble() / 100.0;
519
if( draw->hasAttribute( "draw:cy" ) )
520
y = draw->attribute( "draw:cy" ).remove( '%' ).toDouble() / 100.0;
524
KoRect rect = obj.boundingBox();
525
gradient.setOrigin( KoPoint( rect.x() + x * rect.width(),
526
rect.y() + y * rect.height() ) );
527
gradient.setFocalPoint( KoPoint( rect.x() + x * rect.width(),
528
rect.y() + y * rect.height() ) );
529
gradient.setVector( KoPoint( rect.x() + rect.width(),
530
rect.y() + y * rect.height() ) );
532
f.gradient() = gradient;
533
f.setType( VFill::grad );
538
/*else if( fill == "hatch" )
540
QDomElement brush = doc.createElement( "BRUSH" );
541
QString style = m_styleStack.attribute( "draw:fill-hatch-name" );
542
if( style == "Black 0 Degrees" )
543
brush.setAttribute( "style", 9 );
544
else if( style == "Black 90 Degrees" )
545
brush.setAttribute( "style", 10 );
546
else if( style == "Red Crossed 0 Degrees" || style == "Blue Crossed 0 Degrees" )
547
brush.setAttribute( "style", 11 );
548
else if( style == "Black 45 Degrees" || style == "Black 45 Degrees Wide" )
549
brush.setAttribute( "style", 12 );
550
else if( style == "Black -45 Degrees" )
551
brush.setAttribute( "style", 13 );
552
else if( style == "Red Crossed 45 Degrees" || style == "Blue Crossed 45 Degrees" )
553
brush.setAttribute( "style", 14 );
555
QDomElement* draw = m_draws[style];
556
if( draw && draw->hasAttribute( "draw:color" ) )
557
brush.setAttribute( "color", draw->attribute( "draw:color" ) );
558
e.appendChild( brush );
563
OoDrawImport::createStyleMap( QDomDocument &docstyles )
565
QDomElement styles = docstyles.documentElement();
566
if( styles.isNull() )
569
QDomNode fixedStyles = styles.namedItem( "office:styles" );
570
if( !fixedStyles.isNull() )
572
insertDraws( fixedStyles.toElement() );
573
insertStyles( fixedStyles.toElement() );
575
QDomNode automaticStyles = styles.namedItem( "office:automatic-styles" );
576
if( !automaticStyles.isNull() )
577
insertStyles( automaticStyles.toElement() );
579
QDomNode masterStyles = styles.namedItem( "office:master-styles" );
580
if( !masterStyles.isNull() )
581
insertStyles( masterStyles.toElement() );
585
OoDrawImport::insertDraws( const QDomElement& styles )
587
for( QDomNode n = styles.firstChild(); !n.isNull(); n = n.nextSibling() )
589
QDomElement e = n.toElement();
591
if( !e.hasAttribute( "draw:name" ) )
594
QString name = e.attribute( "draw:name" );
595
m_draws.insert( name, new QDomElement( e ) );
601
OoDrawImport::insertStyles( const QDomElement& styles )
603
for ( QDomNode n = styles.firstChild(); !n.isNull(); n = n.nextSibling() )
605
QDomElement e = n.toElement();
607
if( !e.hasAttribute( "style:name" ) )
610
QString name = e.attribute( "style:name" );
611
m_styles.insert( name, new QDomElement( e ) );
612
//kdDebug() << "Style: '" << name << "' loaded " << endl;
617
OoDrawImport::fillStyleStack( const QDomElement& object )
619
// find all styles associated with an object and push them on the stack
620
if( object.hasAttribute( "presentation:style-name" ) )
621
addStyles( m_styles[object.attribute( "presentation:style-name" )] );
623
if( object.hasAttribute( "draw:style-name" ) )
624
addStyles( m_styles[object.attribute( "draw:style-name" )] );
626
if( object.hasAttribute( "draw:text-style-name" ) )
627
addStyles( m_styles[object.attribute( "draw:text-style-name" )] );
629
if( object.hasAttribute( "text:style-name" ) )
630
addStyles( m_styles[object.attribute( "text:style-name" )] );
634
OoDrawImport::addStyles( const QDomElement* style )
636
// this function is necessary as parent styles can have parents themself
637
if( style->hasAttribute( "style:parent-style-name" ) )
638
addStyles( m_styles[style->attribute( "style:parent-style-name" )] );
640
m_styleStack.push( *style );
644
OoDrawImport::storeObjectStyles( const QDomElement& object )
646
//m_styleStack.clearPageMark(); // remove styles of previous object
647
fillStyleStack( object );
648
//m_styleStack.setObjectMark();
652
OoDrawImport::parseViewBox( const QDomElement& object )
655
if( !object.attribute( "svg:viewBox" ).isEmpty() )
657
// allow for viewbox def with ',' or whitespace
658
QString viewbox( object.attribute( "svg:viewBox" ) );
659
QStringList points = QStringList::split( ' ', viewbox.replace( QRegExp(","), " ").simplifyWhiteSpace() );
661
rect.setX( points[0].toFloat() );
662
rect.setY( points[1].toFloat() );
663
rect.setWidth( points[2].toFloat() );
664
rect.setHeight( points[3].toFloat() );
670
OoDrawImport::appendPoints(VPath &path, const QDomElement& object)
672
double x = KoUnit::parseValue( object.attribute( "svg:x" ) );
673
double y = KoUnit::parseValue( object.attribute( "svg:y" ) );
674
double w = KoUnit::parseValue( object.attribute( "svg:width" ) );
675
double h = KoUnit::parseValue( object.attribute( "svg:height" ) );
677
KoRect rect = parseViewBox( object );
678
rect.setX( rect.x() + x );
679
rect.setY( rect.y() + y );
681
QStringList ptList = QStringList::split( ' ', object.attribute( "draw:points" ) );
687
for( QStringList::Iterator it = ptList.begin(); it != ptList.end(); ++it )
689
tmp_x = rect.x() + ( (*it).section( ',', 0, 0 ).toInt() * w ) / rect.width();
690
tmp_y = rect.y() + ( (*it).section( ',', 1, 1 ).toInt() * h ) / rect.height();
693
point.setY( ymirror( tmp_y ) );
696
path.moveTo( point );
700
path.lineTo( point );
705
OoDrawImport::parseColor( VColor &color, const QString &s )
707
if( s.startsWith( "rgb(" ) )
709
QString parse = s.stripWhiteSpace();
710
QStringList colors = QStringList::split( ',', parse );
711
QString r = colors[0].right( ( colors[0].length() - 4 ) );
712
QString g = colors[1];
713
QString b = colors[2].left( ( colors[2].length() - 1 ) );
715
if( r.contains( "%" ) )
717
r = r.left( r.length() - 1 );
718
r = QString::number( int( ( double( 255 * r.toDouble() ) / 100.0 ) ) );
721
if( g.contains( "%" ) )
723
g = g.left( g.length() - 1 );
724
g = QString::number( int( ( double( 255 * g.toDouble() ) / 100.0 ) ) );
727
if( b.contains( "%" ) )
729
b = b.left( b.length() - 1 );
730
b = QString::number( int( ( double( 255 * b.toDouble() ) / 100.0 ) ) );
733
QColor c( r.toInt(), g.toInt(), b.toInt() );
734
color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
738
QString rgbColor = s.stripWhiteSpace();
740
if( rgbColor.startsWith( "#" ) )
741
c.setNamedColor( rgbColor );
743
// c = parseColor( rgbColor );
744
color.set( c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0 );
749
OoDrawImport::ymirror( double y )
751
return m_document.height() - y;
754
#include "oodrawimport.moc"