filters

epsexport.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002, 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 <tqapplication.h>
21#include <tqcstring.h>
22#include <tqdatetime.h> // For creation date/time.
23#include <tqdom.h>
24#include <tqfile.h>
25#include <tqstring.h>
26#include <tqvaluelist.h>
27
28#include <kdebug.h>
29#include <kgenericfactory.h>
30#include <KoDocumentInfo.h>
31#include <KoFilter.h>
32#include <KoFilterChain.h>
33#include <KoStore.h>
34
35#include "epsexport.h"
36#include "epsexportdlg.h"
37#include "vcolor.h"
38#include "vcomposite.h"
39#include "vdashpattern.h"
40#include "vdocument.h"
41#include "vfill.h"
42#include "vgroup.h"
43#include "vlayer.h"
44#include "vpath.h"
45#include "vsegment.h"
46#include "vselection.h"
47#include "vstroke.h"
48#include "vtext.h"
49#include "vcomputeboundingbox.h"
50
51// Define PostScript level1 operators.
52static char l1_newpath = 'N';
53static char l1_closepath = 'C';
54static char l1_moveto = 'm';
55static char l1_curveto = 'c';
56static char l1_lineto = 'l';
57static char l1_stroke = 's';
58static char l1_fill = 'f';
59//static char l1_eofill = 'e';
60static char l1_setlinewidth = 'w';
61static char l1_setdash = 'd';
62static char l1_setrgbcolor = 'r';
63static char l1_gsave = 'S';
64static char l1_grestore = 'R';
65
66
67class EpsExportFactory : KGenericFactory<EpsExport, KoFilter>
68{
69public:
70 EpsExportFactory( void )
71 : KGenericFactory<EpsExport, KoFilter>( "karbonepsexport" )
72 {}
73
74protected:
75 virtual void setupTranslations( void )
76 {
77 TDEGlobal::locale()->insertCatalogue( "kofficefilters" );
78 }
79};
80
81
82K_EXPORT_COMPONENT_FACTORY( libkarbonepsexport, EpsExportFactory() )
83
84
85EpsExport::EpsExport( KoFilter*, const char*, const TQStringList& )
86 : KoFilter(), m_exportHidden( true )
87{
88}
89
90KoFilter::ConversionStatus
91EpsExport::convert( const TQCString& from, const TQCString& to )
92{
93 if ( to != "image/x-eps" || from != "application/x-karbon" )
94 {
95 return KoFilter::NotImplemented;
96 }
97
98
99 KoStoreDevice* storeIn = m_chain->storageFile( "root", KoStore::Read );
100
101 if( !storeIn )
102 return KoFilter::StupidError;
103
104
105 KoFilter::ConversionStatus status = KoFilter::OK;
106
107 // Ask questions about PS level etc.
108 EpsExportDlg* dialog = new EpsExportDlg();
109
110 TQApplication::setOverrideCursor( TQt::arrowCursor );
111
112 if( dialog->exec() )
113 {
114 // Which PostScript level to support?
115 m_psLevel = dialog->psLevel() + 1;
116 m_exportHidden = dialog->exportHidden();
117
118 TQFile fileOut( m_chain->outputFile() );
119 if( !fileOut.open( IO_WriteOnly ) )
120 {
121 TQApplication::restoreOverrideCursor();
122 delete( dialog );
123
124 return KoFilter::StupidError;
125 }
126
127 TQDomDocument domIn;
128 domIn.setContent( storeIn );
129 TQDomElement docNode = domIn.documentElement();
130
131 m_stream = new TQTextStream( &fileOut );
132
133 // Load the document.
134 VDocument doc;
135 doc.load( docNode );
136
137 // Process the document.
138 doc.accept( *this );
139
140 delete m_stream;
141 fileOut.close();
142 }
143 else
144 {
145 // Dialog cancelled.
146 status = KoFilter::UserCancelled;
147 }
148
149 TQApplication::restoreOverrideCursor();
150 delete( dialog );
151
152 return status;
153}
154
155void
156EpsExport::visitVDocument( VDocument& document )
157{
158 // calculate the documents bounding box
159 VComputeBoundingBox bbox( ! m_exportHidden );
160 document.accept( bbox );
161 const KoRect &rect = bbox.boundingRect();
162
163 // Print a header.
164 *m_stream <<
165 "%!PS-Adobe-3.0 EPSF-3.0\n"
166 "%%BoundingBox: " <<
167 // Round down.
168 tqRound( rect.left() - 0.5 ) << " " <<
169 tqRound( rect.top() - 0.5 ) << " " <<
170 // Round up.
171 tqRound( rect.right() + 0.5 ) << " " <<
172 tqRound( rect.bottom() + 0.5 ) << "\n" <<
173 "%%HiResBoundingBox: " <<
174 rect.left() << " " <<
175 rect.top() << " " <<
176 rect.right() << " " <<
177 rect.bottom() << "\n"
178 "%%Creator: Karbon14 EPS Exportfilter 0.5"
179 << endl;
180
181 // Process document info.
182 KoStoreDevice* storeIn;
183 storeIn = m_chain->storageFile( "documentinfo.xml", KoStore::Read );
184
185 if( storeIn )
186 {
187 TQDomDocument domIn;
188 domIn.setContent( storeIn );
189
190 KoDocumentInfo docInfo;
191 docInfo.load( domIn );
192
193 KoDocumentInfoAuthor* authorPage =
194 static_cast<KoDocumentInfoAuthor*>( docInfo.page( "author" ) );
195
196 // Get creation date/time = "now".
197 TQDateTime now( TQDateTime::currentDateTime() );
198
199 *m_stream <<
200 "%%CreationDate: (" << now.toString( TQt::LocalDate ) << ")\n"
201 "%%For: (" << authorPage->fullName() << ") (" << authorPage->company() << ")\n"
202 "%%Title: (" << docInfo.title() << ")"
203 << endl;
204 }
205
206
207 // Print operator definitions.
208 *m_stream <<
209 "\n"
210 "/" << l1_newpath << " {newpath} def\n"
211 "/" << l1_closepath << " {closepath} def\n"
212 "/" << l1_moveto << " {moveto} def\n"
213 "/" << l1_curveto << " {curveto} def\n"
214 "/" << l1_lineto << " {lineto} def\n"
215 "/" << l1_stroke << " {stroke} def\n"
216 "/" << l1_fill << " {fill} def\n"
217 "/" << l1_setlinewidth << " {setlinewidth} def\n"
218 "/" << l1_setdash << " {setdash} def\n"
219 "/" << l1_setrgbcolor << " {setrgbcolor} def\n"
220 "/" << l1_gsave << " {gsave} def\n"
221 "/" << l1_grestore << " {grestore} def\n"
222 << endl;
223
224 // Export layers.
225 VVisitor::visitVDocument( document );
226
227 // Finished.
228 *m_stream <<
229 "%%EOF"
230 << endl;
231}
232
233void
234EpsExport::visitVGroup( VGroup& group )
235{
236 VObjectListIterator itr( group.objects() );
237
238 for( ; itr.current(); ++itr )
239 {
240 // do not export hidden child objects
241 if( ! m_exportHidden && ! isVisible( itr.current() ) )
242 continue;
243 itr.current()->accept( *this );
244 }
245}
246
247void
248EpsExport::visitVLayer( VLayer& layer )
249{
250 // do not export hidden layers
251 if( ! m_exportHidden && ! isVisible( &layer ) )
252 return;
253
254 VObjectListIterator itr( layer.objects() );
255
256 for( ; itr.current(); ++itr )
257 {
258 // do not export hidden objects
259 if( ! m_exportHidden && ! isVisible( itr.current() ) )
260 continue;
261 itr.current()->accept( *this );
262 }
263}
264
265void
266EpsExport::visitVPath( VPath& composite )
267{
268 *m_stream << l1_newpath << "\n";
269
270 VVisitor::visitVPath( composite );
271
272 getFill( *composite.fill() );
273 getStroke( *composite.stroke() );
274
275 *m_stream << endl;
276}
277
278void
279EpsExport::visitVSubpath( VSubpath& path )
280{
281 // Export segments.
282 VSubpathIterator itr( path );
283
284 for( ; itr.current(); ++itr )
285 {
286 VSegment *segment = itr.current();
287 if ( segment->isCurve() ) {
288 *m_stream <<
289 itr.current()->point( 0 ).x() << " " <<
290 itr.current()->point( 0 ).y() << " " <<
291 itr.current()->point( 1 ).x() << " " <<
292 itr.current()->point( 1 ).y() << " " <<
293 itr.current()->knot().x() << " " <<
294 itr.current()->knot().y() << " " <<
295 l1_curveto << "\n";
296 } else if ( segment->isLine() ) {
297 *m_stream <<
298 itr.current()->knot().x() << " " <<
299 itr.current()->knot().y() << " " <<
300 l1_lineto << "\n";
301 } else if ( segment->isBegin() ) {
302 *m_stream <<
303 itr.current()->knot().x() << " " <<
304 itr.current()->knot().y() << " " <<
305 l1_moveto << "\n";
306 }
307 }
308
309 if( path.isClosed() )
310 *m_stream << l1_closepath << "\n";
311}
312
313void
314EpsExport::visitVText( VText& text )
315{
316 // TODO: currently we only export the glyphs if available.
317
318 // Export the glyphs (= VPaths).
319 VPathListIterator itr( text.glyphs() );
320
321 for( ; itr.current(); ++itr )
322 {
323 visit( *itr.current() );
324 }
325}
326
327void
328EpsExport::getStroke( const VStroke& stroke )
329{
330 // Solid stroke.
331 if( stroke.type() == VStroke::solid )
332 {
333 // Dash pattern.
334 *m_stream << "[";
335
336 const TQValueList<float>&
337 array( stroke.dashPattern().array() );
338
339 TQValueListConstIterator<float> itr = array.begin();
340 for( ; itr != array.end(); ++itr )
341 *m_stream << *itr << " ";
342
343 *m_stream <<
344 "] " << stroke.dashPattern().offset() <<
345 " " << l1_setdash << " ";
346
347 getColor( stroke.color() );
348
349 // "setlinewidth", "stroke".
350 *m_stream <<
351 " " << stroke.lineWidth() <<
352 " " << l1_setlinewidth <<
353 " " << l1_stroke << "\n";
354 }
355 else if( stroke.type() == VStroke::grad )
356 {
357 if( m_psLevel == 3 )
358 {
359
360 }
361 }
362}
363
364void
365EpsExport::getFill( const VFill& fill )
366{
367 // Solid fill.
368 if( fill.type() == VFill::solid )
369 {
370 // "gsave".
371 *m_stream << l1_gsave << " ";
372
373 // "setrgbcolor".
374 getColor( fill.color() );
375
376 // "fill", "grestore".
377 *m_stream << " " << l1_fill << " " << l1_grestore << "\n";
378 }
379 // Gradient.
380 else if( fill.type() == VFill::grad )
381 {
382 if( m_psLevel == 3 )
383 {
384 // "gsave".
385 *m_stream << l1_gsave << " ";
386
387 VGradient grad = fill.gradient();
388 TQPtrVector<VColorStop> ramp = grad.colorStops();
389 if( ramp.size() < 2 )
390 {
391 if( ramp.size() == 1 )
392 getColor( ramp[0]->color );
393 }
394 if( ramp.size() > 2 || ramp.size() == 2 && ramp[0]->midPoint != 0.5 )
395 {
396 // Gradient with more than two colors or asymmetrical midpoint.
397 for( uint i = 1;i < ramp.size();i++ )
398 {
399 char name[15];
400 sprintf( name, "Function%d", 2 * i - 1 );
401
402 VColorStop stop1 = *ramp[i - 1];
403 VColorStop stop2 = *ramp[i];
404 VColor mid;
405 mid.set( 0.5 * ( stop1.color[0] + stop2.color[0] ), 0.5 * ( stop1.color[1] + stop2.color[1] ), 0.5 * ( stop1.color[2] + stop2.color[2] ) );
406 *m_stream << "/" << name << " 7 dict def " << name << " begin\n" << "\t/FunctionType 2 def\n"
407 << "\t/Domain [ 0 1 ] def\n" << "\t/C0 [ " << stop1.color[0] << " " << stop1.color[1] << " "
408 << stop1.color[2] << " ] def\n" << "\t/C1 [ " << mid[0] << " " << mid[1] << " "
409 << mid[2] << " ] def\n" << "\t/N 1 def\n" << "end\n";
410
411 sprintf( name, "Function%d", 2 * i );
412
413 *m_stream << "/" << name << " 7 dict def " << name << " begin\n" << "\t/FunctionType 2 def\n" << "\t/Domain [ 0 1 ] def\n"
414 << "\t/C0 [ " << mid[0] << " " << mid[1] << " " << mid[2] << " ] def\n" << "\t/C1 [ " << stop2.color[0] << " "
415 << stop2.color[1] << " " << stop2.color[2] << " ] def\n" << "\t/N 1 def\n" << "end\n";
416 }
417 }
418 if( grad.type() == VGradient::linear )
419 *m_stream << "clip newpath\n" << "/DeviceRGB setcolorspace\n" << "<<\n" << "\t/ShadingType 2\n" << "\t/ColorSpace /DeviceRGB\n" << "\t/Coords [ "
420 << grad.origin().x() << " " << grad.origin().y() << " " << grad.vector().x() << " " << grad.vector().y() << " ]\n\t/Extend[ true true ]\n" << "\t/Function <<\n";
421 else if( grad.type() == VGradient::radial )
422 {
423 double r = sqrt( pow( grad.vector().x() - grad.origin().x(), 2 ) + pow( grad.vector().y() - grad.origin().y(), 2 ) );
424 *m_stream << "clip newpath\n" << "/DeviceRGB setcolorspace\n" << "<<\n" << "\t/ShadingType 3\n" << "\t/ColorSpace /DeviceRGB\n" << "\t/Coords [ "
425 << grad.origin().x() << " " << grad.origin().y() << " 0.0 " << grad.origin().x() << " " << grad.origin().y()
426 << " " << r << "]\n\t\t/Extend [ false true ]\n" << "\t/Function <<\n";
427 }
428 if( ramp.size() == 2 && ramp[0]->midPoint == 0.5 )
429 {
430 // Gradient with only two colors and symmetrical midpoint.
431 VColorStop stop1 = *ramp[0];
432 VColorStop stop2 = *ramp[1];
433 *m_stream << "\t\t/FunctionType 2\n" << "\t\t/C0 [ " << stop1.color[0] << " " << stop1.color[1] << " " << stop1.color[2]
434 << " ]\n" << "\t\t/C1 [ " << stop2.color[0] << " " << stop2.color[1] << " " << stop2.color[2] << " ]\n" << "\t\t/N 1\n";
435 }
436 else if( ramp.size() > 2 || ramp.size() == 2 && ramp[0]->midPoint != 0.5 )
437 {
438 // Gradient with more than two colors or asymmetrical midpoint.
439 *m_stream << "\t\t/FunctionType 3\n" << "\t\t/Functions [ ";
440 for( uint i = 1; i < ( 2 * ramp.size() - 1 );i++ )
441 *m_stream << "Function" << i << " ";
442 *m_stream << "]\n" << "\t\t/Bounds [";
443 for( uint i = 0;i < ramp.size() - 1;i++ )
444 {
445 VColorStop stop = *ramp[i];
446 if( i > 0 )
447 *m_stream << " " << stop.rampPoint;
448 *m_stream << " " << ( stop.rampPoint + ( ramp[i + 1]->rampPoint - stop.rampPoint ) * stop.midPoint );
449 }
450 *m_stream << " ]\n" << "\t\t/Encode [ ";
451 for( uint i = 0;i < 2 * ramp.size() - 2;i++ )
452 *m_stream << "0 1 ";
453 *m_stream << "]\n";
454 }
455 *m_stream << "\t\t/Domain [ " << ramp[0]->rampPoint << " "
456 << ramp[ramp.size() - 1]->rampPoint << " ]\n" << "\t>>\n" << ">>\n";
457 // "shfill", "grestore".
458 *m_stream << " shfill " << l1_grestore << "\n";
459 }
460 }
461}
462
463void
464EpsExport::getColor( const VColor& color )
465{
466 VColor copy( color );
467 copy.setColorSpace( VColor::rgb );
468
469 *m_stream <<
470 copy[0] << " " <<
471 copy[1] << " " <<
472 copy[2] << " " << l1_setrgbcolor;
473}
474
475bool
476EpsExport::isVisible( const VObject* object ) const
477{
478 return object->state() != VObject::hidden && object->state() != VObject::hidden_locked;
479}
480
481#include "epsexport.moc"