You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kig/filters/exporter.cc

625 lines
19 KiB

// Copyright (C) 2003 Dominique Devriese <devriese@kde.org>
// 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 <tqcheckbox.h>
#include <tqcolor.h>
#include <tqfile.h>
#include <tqiconset.h>
#include <tqtextstream.h>
#include <tdeaction.h>
#include <kiconloader.h>
#include <kimageio.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#include <knuminput.h>
#include <map>
// 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;
TDEIconLoader* l = doc->instance()->iconLoader();
TQPixmap icon = l->loadIcon( iconstr, TDEIcon::Small, 16, TDEIcon::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<TQColor, int> 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<ObjectHolder*> os = doc.document().objects();
XFigExportImpVisitor visitor( stream, w );
for ( std::vector<ObjectHolder*>::const_iterator i = os.begin();
i != os.end(); ++i )
{
visitor.mapColor( ( *i )->drawer() );
};
for ( std::vector<ObjectHolder*>::const_iterator i = os.begin();
i != os.end(); ++i )
{
visitor.visit( *i );
};
}