// Copyright (C) 2003 Dominique Devriese // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA. #include "exporter.h" #include "imageexporteroptions.h" #include "latexexporter.h" #include "svgexporter.h" #include "../kig/kig_document.h" #include "../kig/kig_part.h" #include "../kig/kig_view.h" #include "../misc/common.h" #include "../misc/kigfiledialog.h" #include "../misc/kigpainter.h" #include "../objects/circle_imp.h" #include "../objects/line_imp.h" #include "../objects/object_drawer.h" #include "../objects/object_holder.h" #include "../objects/object_imp.h" #include "../objects/other_imp.h" #include "../objects/point_imp.h" #include "../objects/text_imp.h" #include #include #include #include #include #include #include #include #include #include #include #include // we need this for storing colors in a std::map.. static bool operator<( const TQColor& a, const TQColor& b ) { return a.rgb() < b.rgb(); } class ExporterAction : public TDEAction { KigExporter* mexp; const KigPart* mdoc; KigWidget* mw; public: ExporterAction( const KigPart* doc, KigWidget* w, TDEActionCollection* parent, KigExporter* exp ); void slotActivated(); }; ExporterAction::ExporterAction( const KigPart* doc, KigWidget* w, TDEActionCollection* parent, KigExporter* exp ) : TDEAction( exp->menuEntryName(), TDEShortcut(), 0, 0, parent ), mexp( exp ), mdoc( doc ), mw( w ) { TQString iconstr = exp->menuIcon(); if ( iconstr.isEmpty() ) return; KIconLoader* l = doc->instance()->iconLoader(); TQPixmap icon = l->loadIcon( iconstr, KIcon::Small, 16, KIcon::DefaultState, 0L, true ); if ( !icon.isNull() ) setIconSet( TQIconSet( icon ) ); } void ExporterAction::slotActivated() { mexp->run( *mdoc, *mw ); } KigExporter::~KigExporter() { } ImageExporter::~ImageExporter() { } TQString ImageExporter::exportToStatement() const { return i18n( "&Export to image" ); } TQString ImageExporter::menuEntryName() const { return i18n( "&Image..." ); } TQString ImageExporter::menuIcon() const { return "image"; } void ImageExporter::run( const KigPart& doc, KigWidget& w ) { static bool kimageioRegistered = false; if ( ! kimageioRegistered ) { KImageIO::registerFormats(); kimageioRegistered = true; } KigFileDialog* kfd = new KigFileDialog( TQString(), KImageIO::pattern( KImageIO::Writing ), i18n( "Export as Image" ), &w ); kfd->setOptionCaption( i18n( "Image Options" ) ); ImageExporterOptions* opts = new ImageExporterOptions( 0L, w.size() ); kfd->setOptionsWidget( opts ); opts->WidthInput->setValue( w.size().width() ); opts->HeightInput->setValue( w.size().height() ); opts->showGridCheckBox->setChecked( doc.document().grid() ); opts->showAxesCheckBox->setChecked( doc.document().axes() ); if ( !kfd->exec() ) return; TQString filename = kfd->selectedFile(); bool showgrid = opts->showGridCheckBox->isOn(); bool showaxes = opts->showAxesCheckBox->isOn(); int width = opts->WidthInput->value(); int height = opts->HeightInput->value(); delete opts; delete kfd; TQString type = KImageIO::type( filename ); if ( type.isNull() ) { KMessageBox::sorry( &w, i18n( "Sorry, this file format is not supported." ) ); return; }; kdDebug() << k_funcinfo << type << endl; TQFile file( filename ); if ( ! file.open( IO_WriteOnly ) ) { KMessageBox::sorry( &w, i18n( "The file \"%1\" could not be opened. Please check if the file permissions are set correctly." ) .arg( filename ) ); return; }; TQPixmap img( TQSize( width, height ) ); img.fill( TQt::white ); KigPainter p( ScreenInfo( w.screenInfo().shownRect(), img.rect() ), TQT_TQPAINTDEVICE(&img), doc.document() ); p.setWholeWinOverlay(); p.drawGrid( doc.document().coordinateSystem(), showgrid, showaxes ); // FIXME: show the selections ? p.drawObjects( doc.document().objects(), false ); if ( ! img.save( filename, type.latin1() ) ) { KMessageBox::error( &w, i18n( "Sorry, something went wrong while saving to image \"%1\"" ).arg( filename ) ); } } KigExportManager::KigExportManager() { mexporters.push_back( new ImageExporter ); // working on this one ;) mexporters.push_back( new XFigExporter ); mexporters.push_back( new LatexExporter ); mexporters.push_back( new SVGExporter ); } KigExportManager::~KigExportManager() { for ( uint i = 0; i < mexporters.size(); ++i ) delete mexporters[i]; } void KigExportManager::addMenuAction( const KigPart* doc, KigWidget* w, TDEActionCollection* coll ) { TDEActionMenu* m = new TDEActionMenu( i18n( "&Export To" ), coll, "file_export" ); for ( uint i = 0; i < mexporters.size(); ++i ) m->insert( new ExporterAction( doc, w, coll, mexporters[i] ) ); } KigExportManager* KigExportManager::instance() { static KigExportManager m; return &m; } XFigExporter::~XFigExporter() { } TQString XFigExporter::exportToStatement() const { return i18n( "Export to &XFig file" ); } TQString XFigExporter::menuEntryName() const { return i18n( "&XFig File..." ); } TQString XFigExporter::menuIcon() const { return "kig_xfig"; } class XFigExportImpVisitor : public ObjectImpVisitor { TQTextStream& mstream; ObjectHolder* mcurobj; const KigWidget& mw; Rect msr; std::map mcolormap; int mnextcolorid; int mcurcolorid; TQPoint convertCoord( const Coordinate& c ) { Coordinate ret = ( c - msr.bottomLeft() ); ret.y = msr.height() - ret.y; // kdDebug() << "msr: " << msr << endl // << "ret: " << ret << endl // << "c: " << c << endl; ret *= 9450; ret /= msr.width(); return ret.toTQPoint(); } void emitLine( const Coordinate& a, const Coordinate& b, int width, bool vector = false ); public: void visit( ObjectHolder* obj ); void mapColor( const ObjectDrawer* obj ); XFigExportImpVisitor( TQTextStream& s, const KigWidget& w ) : mstream( s ), mw( w ), msr( mw.showingRect() ), mnextcolorid( 32 ) { // predefined colors in XFig.. mcolormap[TQt::black] = 0; mcolormap[TQt::blue] = 1; mcolormap[TQt::green] = 2; mcolormap[TQt::cyan] = 3; mcolormap[TQt::red] = 4; mcolormap[TQt::magenta] = 5; mcolormap[TQt::yellow] = 6; mcolormap[TQt::white] = 7; } void visit( const LineImp* imp ); void visit( const PointImp* imp ); void visit( const TextImp* imp ); void visit( const AngleImp* imp ); void visit( const VectorImp* imp ); void visit( const LocusImp* imp ); void visit( const CircleImp* imp ); void visit( const ConicImp* imp ); void visit( const CubicImp* imp ); void visit( const SegmentImp* imp ); void visit( const RayImp* imp ); void visit( const ArcImp* imp ); }; void XFigExportImpVisitor::mapColor( const ObjectDrawer* obj ) { if ( ! obj->shown() ) return; TQColor color = obj->color(); if ( mcolormap.find( color ) == mcolormap.end() ) { int newcolorid = mnextcolorid++; mstream << "0 " << newcolorid << " " << color.name() << "\n"; mcolormap[color] = newcolorid; } } void XFigExportImpVisitor::visit( ObjectHolder* obj ) { if ( ! obj->drawer()->shown() ) return; assert( mcolormap.find( obj->drawer()->color() ) != mcolormap.end() ); mcurcolorid = mcolormap[ obj->drawer()->color() ]; mcurobj = obj; obj->imp()->visit( this ); } void XFigExportImpVisitor::visit( const LineImp* imp ) { Coordinate a = imp->data().a; Coordinate b = imp->data().b; calcBorderPoints( a, b, msr ); int width = mcurobj->drawer()->width(); if ( width == -1 ) width = 1; if ( a != b ) emitLine( a, b, width ); } void XFigExportImpVisitor::emitLine( const Coordinate& a, const Coordinate& b, int width, bool vector ) { mstream << "2 "; // polyline type; mstream << "1 "; // polyline subtype; mstream << "0 "; // line_style: Solid mstream << width << " "; // thickness: *1/80 inch mstream << mcurcolorid << " "; // pen_color: default mstream << "7 "; // fill_color: white mstream << "50 "; // depth: 50 mstream << "-1 "; // pen_style: unused by XFig mstream << "-1 "; // area_fill: no fill mstream << "0.000 "; // style_val: the distance between dots and // dashes in case of dotted or dashed lines.. mstream << "0 "; // join_style: Miter mstream << "0 "; // cap_style: Butt mstream << "-1 "; // radius in case of an arc-box, but we're a // polyline, so nothing here.. if ( ! vector ) mstream << "0 "; // forward arrow: no else mstream << "1 "; // forward arrow: yes mstream << "0 "; // backward arrow: no mstream << "2"; // a two points polyline.. mstream << "\n\t "; if ( vector ) { // first the arrow line in case of a vector.. mstream << "1 " // arrow_type: closed triangle << "1 " // arrow_style: filled with pen color.. << "1.00 " // arrow_thickness: 1 << "195.00 " // arrow_width << "165.00 " // arrow_height << "\n\t"; } TQPoint ca = convertCoord( a ); TQPoint cb = convertCoord( b ); mstream << ca.x() << " " << ca.y() << " " << cb.x() << " " << cb.y() << "\n"; } void XFigExportImpVisitor::visit( const PointImp* imp ) { const TQPoint center = convertCoord( imp->coordinate() ); int width = mcurobj->drawer()->width(); if ( width == -1 ) width = 5; width *= 10; mstream << "1 " // Ellipse type << "3 " // circle defined by radius subtype << "0 "; // line_style: Solid mstream << "1 " << " " // thickness: *1/80 inch << mcurcolorid << " " // pen_color: default << mcurcolorid << " " // fill_color: black << "50 " // depth: 50 << "-1 " // pen_style: unused by XFig << "20 " // area_fill: full saturation of the fill color << "0.000 " // style_val: the distance between dots and // dashes in case of dotted or dashed lines.. << "1 " // direction: always 1 << "0.0000 " // angle: the radius of the x-axis: 0 << center.x() << " " << center.y() << " " // the center.. << width << " " << width << " " // radius_x and radius_y << center.x() << " " // start_x and start_y, appear << center.y() << " " // unused.. << center.x() + width << " " // end_x and end_y, << center.y() << "\n"; // appear unused too... } void XFigExportImpVisitor::visit( const TextImp* imp ) { TQString text = imp->text(); TQPoint coord = convertCoord( imp->surroundingRect().bottomLeft() ); mstream << "4 " // text type << "0 " // subtype: left justfied << mcurcolorid << " " // color: black << "50 " // depth: 50 << "-1 " // pen style: unused << "0 " // font: default << "11 " // font-size: 11 << "0 " // angle << "0 " // font-flags: all the defaults.. << "500 500 " // height, width: large enough.. << coord.x() << " " // x, y << coord.y() << " " << text.ascii() << "\\001" // text, terminated by \001 << "\n"; } void XFigExportImpVisitor::visit( const AngleImp* ) { } void XFigExportImpVisitor::visit( const VectorImp* imp ) { int width = mcurobj->drawer()->width(); if ( width == -1 ) width = 1; emitLine( imp->a(), imp->b(), width, true ); } void XFigExportImpVisitor::visit( const LocusImp* ) { } void XFigExportImpVisitor::visit( const CircleImp* imp ) { const TQPoint center = convertCoord( imp->center() ); const int radius = ( convertCoord( imp->center() + Coordinate( imp->radius(), 0 ) ) - center ).x(); mstream << "1 " // Ellipse type << "3 " // circle defined by radius subtype << "0 "; // line_style: Solid int width = mcurobj->drawer()->width(); if ( width == -1 ) width = 1; mstream << width << " " // thickness: *1/80 inch << mcurcolorid << " " // pen_color: default << "7 " // fill_color: white << "50 " // depth: 50 << "-1 " // pen_style: unused by XFig << "-1 " // area_fill: no fill << "0.000 " // style_val: the distance between dots and // dashes in case of dotted or dashed lines.. << "1 " // direction: always 1 << "0.0000 " // angle: the radius of the x-axis: 0 << center.x() << " " << center.y() << " " // the center.. << radius << " " << radius << " " // radius_x and radius_y << center.x() << " " // start_x and start_y, appear << center.y() << " " // unused.. << center.x() + radius << " " // end_x and end_y, << center.y() << "\n"; // appear unused too... } void XFigExportImpVisitor::visit( const ConicImp* imp ) { int width = mcurobj->drawer()->width(); if ( width == -1 ) width = 1; if ( imp->conicType() == 1 ) { // an ellipse, this is good, cause this allows us to export to a // real ellipse type.. const ConicPolarData data = imp->polarData(); // gather some necessary data.. // the angle of the x axis.. double anglex = atan2( data.esintheta0, data.ecostheta0 ); // the exentricity double e = hypot( data.esintheta0, data.ecostheta0 ); // the x radius is easy to find.. double radiusx = data.pdimen / ( 1 - e * e ); // the y radius is a bit harder: we first find the distance from // the focus point to the mid point of the two focuses, this is: double d = -e * data.pdimen / ( 1 - e * e ); // the distance from the first focus to the intersection of the // second axis with the conic equals radiusx: double r = radiusx; double radiusy = sqrt( r*r - d*d ); Coordinate center = data.focus1 - Coordinate( cos( anglex ), sin( anglex ) ).normalize( d ); const TQPoint qcenter = convertCoord( center ); const int radius_x = ( convertCoord( center + Coordinate( radiusx, 0 ) ) - convertCoord( center ) ).x(); const int radius_y = ( convertCoord( center + Coordinate( radiusy, 0 ) ) - convertCoord( center ) ).x(); const TQPoint qpoint2 = convertCoord( center + Coordinate( -sin( anglex ), cos( anglex ) ) * radiusy ); mstream << "1 " // ellipse type << "1 " // subtype: ellipse defined by readii << "0 " // line_style: Solid << width << " " // thickness << mcurcolorid << " " // pen_color: black << "7 " // fill_color: white << "50 " // depth: 50 << "-1 " // pan_style: not used << "-1 " // area_fill: no fill << "0.000 " // style_val: not used << "1 " // direction: always 1 << anglex << " " // angle of the main axis << qcenter.x() << " " // center << qcenter.y() << " " << radius_x << " " // radiuses << radius_y << " " << qcenter.x() << " " // start point << qcenter.y() << " " << qpoint2.x() << " " // end point << qpoint2.y() << " "; } else return; } void XFigExportImpVisitor::visit( const CubicImp* ) { } void XFigExportImpVisitor::visit( const SegmentImp* imp ) { Coordinate a = imp->data().a; Coordinate b = imp->data().b; int width = mcurobj->drawer()->width(); if ( width == -1 ) width = 1; emitLine( a, b, width ); } void XFigExportImpVisitor::visit( const RayImp* imp ) { Coordinate a = imp->data().a; Coordinate b = imp->data().b; calcRayBorderPoints( a, b, msr ); int width = mcurobj->drawer()->width(); if ( width == -1 ) width = 1; emitLine( a, b, width ); } void XFigExportImpVisitor::visit( const ArcImp* imp ) { const Coordinate center = imp->center(); const double radius = imp->radius(); const double startangle = imp->startAngle(); const double endangle = startangle + imp->angle(); const double middleangle = ( startangle + endangle ) / 2; const Coordinate ad = Coordinate( cos( startangle ), sin( startangle ) ).normalize( radius ); const Coordinate bd = Coordinate( cos( middleangle ), sin( middleangle ) ).normalize( radius ); const Coordinate cd = Coordinate( cos( endangle ), sin( endangle ) ).normalize( radius ); const TQPoint a = convertCoord( center + ad ); const TQPoint b = convertCoord( center + bd ); const TQPoint c = convertCoord( center + cd ); const TQPoint cent = convertCoord( center ); mstream << "5 " // Ellipse type << "1 " // subtype: open ended arc << "0 "; // line_style: Solid int width = mcurobj->drawer()->width(); if ( width == -1 ) width = 1; mstream << width << " " // thickness: *1/80 inch << mcurcolorid << " " // pen_color: default << "7 " // fill_color: white << "50 " // depth: 50 << "-1 " // pen_style: unused by XFig << "-1 " // area_fill: no fill << "0.000 " // style_val: the distance between dots and // dashes in case of dotted or dashed lines.. << "0 "; // cap_style: Butt.. // 0 is clockwise, 1 is counterclockwise . int direction = imp->angle() > 0 ? 1 : 0; // direction next mstream << direction << " " // direction.. << "0 " // forward_arrow: no << "0 " // backward_arrow: no << cent.x() << " " << cent.y() << " " // the center.. << a.x() << " " << a.y() << " " // x1, y1 << b.x() << " " << b.y() << " " // x2, y2 << c.x() << " " << c.y() << " " // x3, y3 << "\n"; } void XFigExporter::run( const KigPart& doc, KigWidget& w ) { KigFileDialog* kfd = new KigFileDialog( ":document", i18n( "*.fig|XFig Documents (*.fig)" ), i18n( "Export as XFig File" ), &w ); if ( !kfd->exec() ) return; TQString file_name = kfd->selectedFile(); delete kfd; TQFile file( file_name ); if ( ! file.open( IO_WriteOnly ) ) { KMessageBox::sorry( &w, i18n( "The file \"%1\" could not be opened. Please " "check if the file permissions are set correctly." ) .arg( file_name ) ); return; }; TQTextStream stream( &file ); stream << "#FIG 3.2\n"; stream << "Landscape\n"; stream << "Center\n"; stream << "Metric\n"; stream << "A4\n"; stream << "100.00\n"; stream << "Single\n"; stream << "-2\n"; stream << "1200 2\n"; std::vector os = doc.document().objects(); XFigExportImpVisitor visitor( stream, w ); for ( std::vector::const_iterator i = os.begin(); i != os.end(); ++i ) { visitor.mapColor( ( *i )->drawer() ); }; for ( std::vector::const_iterator i = os.begin(); i != os.end(); ++i ) { visitor.visit( *i ); }; }