xamlexport.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// based on the SVG exporter. Not intended for public release
21// Microsoft WVG renamed to XAML Graphics. Worry about that later.
22
23#include <tqcstring.h>
24#include <tqdom.h>
25#include <tqfile.h>
26#include <tqstring.h>
27#include <tqvaluelist.h>
28
29#include <kgenericfactory.h>
30#include <KoFilter.h>
31#include <KoFilterChain.h>
32#include <KoStore.h>
33
34#include "xamlexport.h"
35#include "vcolor.h"
36#include "vcomposite.h"
37#include "vdashpattern.h"
38#include "vdocument.h"
39#include "vfill.h"
40#include "vgradient.h"
41#include "vgroup.h"
42#include "vlayer.h"
43#include "vpath.h"
44#include "vsegment.h"
45#include "vselection.h"
46#include "vstroke.h"
47//#include "vtext.h" // TODO Convert Text to Paths for basic export. Not our problem.
48// TODO inline Images?
49
50#include <kdebug.h>
51
52
53typedef KGenericFactory<XAMLExport, KoFilter> XAMLExportFactory;
54K_EXPORT_COMPONENT_FACTORY( libkarbonxamlexport, XAMLExportFactory( "kofficefilters" ) )
55
56
57XAMLExport::XAMLExport( KoFilter*, const char*, const TQStringList& )
58 : KoFilter()
59{
60 m_gc.setAutoDelete( true );
61}
62
63KoFilter::ConversionStatus
64XAMLExport::convert( const TQCString& from, const TQCString& to )
65{
66 // TODO: ???
67 if ( to != "image/wvg+xml" || from != "application/x-karbon" )
68 {
69 return KoFilter::NotImplemented;
70 }
71
72 KoStoreDevice* storeIn = m_chain->storageFile( "root", KoStore::Read );
73 if( !storeIn )
74 return KoFilter::StupidError;
75
76 TQFile fileOut( m_chain->outputFile() );
77 if( !fileOut.open( IO_WriteOnly ) )
78 {
79 delete storeIn;
80 return KoFilter::StupidError;
81 }
82
83 TQDomDocument domIn;
84 domIn.setContent( storeIn );
85 TQDomElement docNode = domIn.documentElement();
86
87 m_stream = new TQTextStream( &fileOut );
88 TQString body;
89 m_body = new TQTextStream( &body, IO_ReadWrite );
90 TQString defs;
91 m_defs = new TQTextStream( &defs, IO_ReadWrite );
92
93
94 // load the document and export it:
95 VDocument doc;
96 doc.load( docNode );
97 doc.accept( *this );
98
99 *m_stream << defs;
100 *m_stream << body;
101
102 fileOut.close();
103
104 delete m_stream;
105 delete m_defs;
106 delete m_body;
107
108 return KoFilter::OK;
109}
110
111void
112XAMLExport::visitVDocument( VDocument& document )
113{
114 // select all objects:
115 document.selection()->append();
116
117 // get the bounding box of the page
118 KoRect rect( 0, 0, document.width(), document.height() );
119
120 // standard header:
121 *m_defs <<
122 "<?xml version=\"1.0\" ?>\n" <<
123 /* "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\" " <<* */
124 // "\"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">"
125 /*<<*/ endl;
126
127 // Add one line comment to identify Content Creator,
128 // probably remove this later
129 // TODO: schemas
130 // http://schemas.microsoft.com/winfx/avalon/2005
131 // http://schemas.microsoft.com/2003/xaml
132 // need to mention defs too Defenitions namespace xmlns:def="Definition"
133 *m_defs <<
134 "<!-- Generator: Karbon14 WVG XAML Graphics export filter $VERSION/$DATE. -->" << endl;
135 *m_defs <<
136 "<Canvas xmlns=\"http://schemas.microsoft.com/winfx/avalon/2005\" Width=\"" << rect.width() <<
137 "Height=\"" << rect.height() << "\">" << endl;
138 *m_defs << "<Canvas.Resources>" << endl;
139
140 // bleuch: this is horrible, do something about it TODO
141 // Microsoft Acrylic has a transform group just like this
142 *m_body << "<Transform=\"scale(1, -1) Translate(0, -" << rect.height() << ")\">" << endl;
143
144 // we dont need the selection anymore:
145 document.selection()->clear();
146
147 // set up gc
148 XAMLGraphicsContext *gc = new XAMLGraphicsContext;
149 m_gc.push( gc );
150
151 // export layers:
152 VVisitor::visitVDocument( document );
153
154 // end tag:
155 *m_body << "</Canvas>" << endl;
156 *m_defs << "</Canvas.Resources>" << endl;
157 *m_body << "</Canvas>" << endl;
158}
159
160TQString
161XAMLExport::getID( VObject *obj )
162{
163 if( obj && !obj->name().isEmpty() )
164 return TQString( " Name=\"%1\"" ).arg( obj->name() );
165 return TQString();
166}
167
168// which markup to use? Group or Canvas?
169// for now assume Group will work. TODO: Test properly!
170void
171XAMLExport::visitVGroup( VGroup& group )
172{
173 *m_body << "<Canvas" << getID( &group ) << ">" << endl;
174 VVisitor::visitVGroup( group );
175 *m_body << "</Canvas>" << endl;
176}
177
178void
179XAMLExport::visitVPath( VPath& composite )
180{
181 *m_body << "<Path" << getID( &composite );
182
183 VVisitor::visitVPath( composite );
184
185 getFill( *( composite.fill() ) );
186 getStroke( *( composite.stroke() ) );
187
188 TQString d;
189 composite.saveSvgPath( d );
190 *m_body << " Data=\"" << d << "\" ";
191
192 if( composite.fillRule() != m_gc.current()->fillRule )
193 {
194 if( composite.fillRule() == evenOdd )
195 *m_body << " FillRule=\"EvenOdd\"";
196 else
197 *m_body << " FillRule=\"NonZero\"";
198 }
199
200 *m_body << " />" << endl;
201}
202
203void
204XAMLExport::visitVSubpath( VSubpath& )
205{
206}
207
208TQString createUID()
209{
210 static unsigned int nr = 0;
211
212 return "defitem" + TQString().setNum( nr++ );
213}
214
215void
216XAMLExport::getColorStops( const TQPtrVector<VColorStop> &colorStops )
217{
218 for( unsigned int i = 0; i < colorStops.count() ; i++ )
219 {
220 *m_defs << "<GradientStop Color=\"";
221 getHexColor( m_defs, colorStops.at( i )->color );
222 *m_defs << "\" Offset=\"" << TQString().setNum( colorStops.at( i )->rampPoint );
223 // XAML uses ARGB values and other methods such as masks for Transparency/Opacity # aa rrggbb
224 // *m_defs << "\" stop-opacity=\"" << colorStops.at( i )->color.opacity() << "\"" << " />" << endl;
225 // Maybe this only applies to gradients, need to check.
226 }
227}
228
229void
230XAMLExport::getGradient( const VGradient& grad )
231{
232 TQString uid = createUID();
233 if( grad.type() == VGradient::linear )
234 {
235 // do linear grad
236 *m_defs << "<LinearGradientBrush id=\"" << uid << "\" ";
237 *m_defs << "GradientUnits=\"UserSpaceOnUse\" ";
238 *m_defs << "StartPoint=\"" << grad.origin().x() << ",";
239 *m_defs << grad.origin().y() << "\" ";
240 *m_defs << "EndPoint=\"" << grad.vector().x() << ",";
241 *m_defs << grad.vector().y() << "\" ";
242 if( grad.repeatMethod() == VGradient::reflect )
243 *m_defs << "SpreadMethod=\"Reflect\" ";
244 else if( grad.repeatMethod() == VGradient::repeat )
245 *m_defs << "SpreadMethod=\"Repeat\" ";
246 *m_defs << ">" << endl;
247
248 // color stops
249 getColorStops( grad.colorStops() );
250
251 *m_defs << "</LinearGradientBrush>" << endl;
252 *m_body << "url(#" << uid << ")";
253 }
254 else if( grad.type() == VGradient::radial )
255 {
256 // do radial grad
257 *m_defs << "<RadialGradientBrush Name=\"" << uid << "\" ";
258 // *m_defs << "gradientUnits=\"userSpaceOnUse\" "; // Absolute?
259 *m_defs << "Center=\"" << grad.origin().x() << ",";
260 *m_defs << grad.origin().y() << "\" ";
261 // Gradient Origin also known as Focus
262 *m_defs << "GradientOrigin=\"" << grad.focalPoint().x() << ",";
263 *m_defs << grad.focalPoint().y() << "\" ";
264 double r = sqrt( pow( grad.vector().x() - grad.origin().x(), 2 ) + pow( grad.vector().y() - grad.origin().y(), 2 ) );
265 *m_defs << "Radius=\"" << TQString().setNum( r ) << "\" ";
266 if( grad.repeatMethod() == VGradient::reflect )
267 *m_defs << "SpreadMethod=\"Reflect\" ";
268 else if( grad.repeatMethod() == VGradient::repeat )
269 *m_defs << "SpreadMethod=\"Repeat\" ";
270 *m_defs << ">" << endl;
271
272 // color stops
273 getColorStops( grad.colorStops() );
274
275 *m_defs << "</RadialGradientBrush>" << endl;
276 *m_body << "url(#" << uid << ")";
277 }
278}
279
280void
281XAMLExport::getFill( const VFill& fill )
282{
283 *m_body << " Fill=\"";
284 if( fill.type() == VFill::none )
285 *m_body << "none";
286 else if( fill.type() == VFill::grad )
287 getGradient( fill.gradient() );
288 else
289 getHexColor( m_body, fill.color() );
290 *m_body << "\"";
291
292 if( fill.color().opacity() != m_gc.current()->fill.color().opacity() )
293 *m_body << " FillOpacity=\"" << fill.color().opacity() << "\"";
294}
295
296void
297XAMLExport::getStroke( const VStroke& stroke )
298{
299 if( stroke.type() != m_gc.current()->stroke.type() )
300 {
301 *m_body << " Stroke=\"";
302 if( stroke.type() == VStroke::none )
303 *m_body << "None";
304 else if( stroke.type() == VStroke::grad )
305 getGradient( stroke.gradient() );
306 else
307 getHexColor( m_body, stroke.color() );
308 *m_body << "\"";
309 }
310
311 if( stroke.color().opacity() != m_gc.current()->stroke.color().opacity() )
312 *m_body << " StrokeOpacity=\"" << stroke.color().opacity() << "\"";
313
314 if( stroke.lineWidth() != m_gc.current()->stroke.lineWidth() )
315 *m_body << " StrokeThickness=\"" << stroke.lineWidth() << "\"";
316
317 if( stroke.lineCap() != m_gc.current()->stroke.lineCap() )
318 {
319 if( stroke.lineCap() == VStroke::capButt )
320 *m_body << " StrokeLineCap=\"Butt\"";
321 else if( stroke.lineCap() == VStroke::capRound )
322 *m_body << " StrokeLineCap=\"round\"";
323 else if( stroke.lineCap() == VStroke::capSquare )
324 *m_body << " StrokeLineCap=\"square\"";
325 }
326
327 if( stroke.lineJoin() != m_gc.current()->stroke.lineJoin() )
328 {
329 if( stroke.lineJoin() == VStroke::joinMiter )
330 {
331 *m_body << " StrokeLineJoin=\"Miter\"";
332 *m_body << " StrokeMiterLimit=\"" << stroke.miterLimit() << "\"";
333 }
334 else if( stroke.lineJoin() == VStroke::joinRound )
335 *m_body << " StrokeLineJoin=\"Round\"";
336 else if( stroke.lineJoin() == VStroke::joinBevel )
337 *m_body << " StrokeLineJoin=\"Bevel\"";
338 }
339
340 // dash
341 if( stroke.dashPattern().array().count() > 0 )
342 {
343 *m_body << " StrokeDashOffset=\"" << stroke.dashPattern().offset() << "\"";
344 *m_body << " StrokeDashArray=\" ";
345
346 TQValueListConstIterator<float> itr;
347 for(itr = stroke.dashPattern().array().begin(); itr != stroke.dashPattern().array().end(); ++itr )
348 {
349 *m_body << *itr << " ";
350 }
351 *m_body << "\"";
352 }
353}
354
355void
356XAMLExport::getHexColor( TQTextStream *stream, const VColor& color )
357{
358 // Convert the various color-spaces to hex
359
360 TQString Output;
361
362 VColor copy( color );
363 copy.setColorSpace( VColor::rgb );
364
365 Output.sprintf( "#%02x%02x%02x", int( copy[0] * 255.0 ), int( copy[1] * 255.0 ), int( copy[2] * 255.0 ) );
366
367 *stream << Output;
368}
369
370#include "xamlexport.moc"
371