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/latexexporter.cpp

609 lines
17 KiB

// Copyright (C) 2004 Pino Toscano <toscano.pino@tiscali.it>
// 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 <config.h>
#include "latexexporter.h"
#include "latexexporteroptions.h"
#include "../kig/kig_document.h"
#include "../kig/kig_part.h"
#include "../kig/kig_view.h"
#include "../misc/common.h"
#include "../misc/goniometry.h"
#include "../misc/kigfiledialog.h"
#include "../misc/rect.h"
#include "../objects/circle_imp.h"
#include "../objects/cubic_imp.h"
#include "../objects/curve_imp.h"
#include "../objects/line_imp.h"
#include "../objects/locus_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/polygon_imp.h"
#include "../objects/text_imp.h"
#include <math.h>
#include <algorithm>
#include <tqcheckbox.h>
#include <tqcolor.h>
#include <tqfile.h>
#include <tqtextstream.h>
#include <tdelocale.h>
#include <tdemessagebox.h>
#ifdef HAVE_TRUNC
#define KDE_TRUNC(a) trunc(a)
#else
#define KDE_TRUNC(a) rint(a)
#endif
struct ColorMap {
TQColor color;
TQString name;
};
LatexExporter::~LatexExporter()
{
}
TQString LatexExporter::exportToStatement() const
{
return i18n( "Export to &Latex..." );
}
TQString LatexExporter::menuEntryName() const
{
return i18n( "&Latex..." );
}
TQString LatexExporter::menuIcon() const
{
// TODO
return "text-x-tex";
}
class LatexExportImpVisitor
: public ObjectImpVisitor
{
TQTextStream& mstream;
ObjectHolder* mcurobj;
const KigWidget& mw;
Rect msr;
std::vector<ColorMap> mcolors;
TQString mcurcolorid;
public:
void visit( ObjectHolder* obj );
void mapColor( TQColor color );
LatexExportImpVisitor( TQTextStream& s, const KigWidget& w )
: mstream( s ), mw( w ), msr( mw.showingRect() )
{
}
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 visit( const PolygonImp* imp );
double unit;
private:
/**
* Converts Kig coords to pstrick coord system and sends them to stream
* using the format: (xcoord,ycoord)
*/
void emitCoord( const Coordinate& c );
/**
* Draws a line (segment) or a vector if vector is true.
*/
void emitLine( const Coordinate& a, const Coordinate& b, const int width,
const Qt::PenStyle s, bool vector = false );
/**
* Sends a new line character ( \n ) to stream.
*/
void newLine();
/**
* Searches if a color is already mapped into mcolors, and returns its
* index or -1 if not found.
*/
int findColor( TQColor c );
/**
* Use to convert a dimension "on the screen" to a dimension wrt.
* Kig coordinate system.
*/
double dimRealToCoord( int dim );
/**
* Converts a pen style into latex style string.
*/
TQString writeStyle( Qt::PenStyle style );
/**
* Plots a generic curve though its points calc'ed with getPoint.
*/
void plotGenericCurve( const CurveImp* imp );
};
void LatexExportImpVisitor::emitCoord( const Coordinate& c )
{
mstream << "(" << c.x - msr.left() << "," << c.y - msr.bottom() << ")";
}
void LatexExportImpVisitor::emitLine( const Coordinate& a, const Coordinate& b,
const int width, const Qt::PenStyle s,
bool vector )
{
mstream << "\\psline[linecolor=" << mcurcolorid << ",linewidth=" << width / 100.0
<< "," << writeStyle( s );
if ( vector )
mstream << ",arrowscale=3,arrowinset=1.3";
mstream << "]";
if ( vector )
mstream << "{->}";
emitCoord( a );
emitCoord( b );
newLine();
}
void LatexExportImpVisitor::newLine()
{
mstream << "\n";
}
int LatexExportImpVisitor::findColor( TQColor c )
{
for ( uint i = 0; i < mcolors.size(); ++i )
{
if ( c == mcolors[i].color )
return i;
}
return -1;
}
void LatexExportImpVisitor::mapColor( TQColor color )
{
if ( findColor( color ) == -1 )
{
ColorMap newcolor;
newcolor.color = color;
TQString tmpname = color.name();
tmpname.replace( "#", "" );
newcolor.name = tmpname;
mcolors.push_back( newcolor );
mstream << "\\newrgbcolor{" << tmpname << "}{"
<< color.red() / 255.0 << " " << color.green() / 255.0 << " "
<< color.blue() / 255.0 << "}\n";
}
}
double LatexExportImpVisitor::dimRealToCoord( int dim )
{
TQRect qr( 0, 0, dim, dim );
Rect r = mw.screenInfo().fromScreen( qr );
return fabs( r.width() );
}
TQString LatexExportImpVisitor::writeStyle( Qt::PenStyle style )
{
TQString ret( "linestyle=" );
if ( style == TQt::DashLine )
ret += "dashed";
else if ( style == TQt::DotLine )
ret += "dotted,dotsep=2pt";
else
ret += "solid";
return ret;
}
void LatexExportImpVisitor::plotGenericCurve( const CurveImp* imp )
{
int width = mcurobj->drawer()->width();
if ( width == -1 ) width = 1;
TQString prefix = TQString( "\\pscurve[linecolor=%1,linewidth=%2,%3]" )
.arg( mcurcolorid )
.arg( width / 100.0 )
.arg( writeStyle( mcurobj->drawer()->style() ) );
std::vector< std::vector< Coordinate > > coordlist;
coordlist.push_back( std::vector< Coordinate >() );
uint curid = 0;
Coordinate c;
Coordinate prev = Coordinate::invalidCoord();
for ( double i = 0.0; i <= 1.0; i += 0.005 )
{
c = imp->getPoint( i, mw.document() );
if ( !c.valid() )
{
if ( coordlist[curid].size() > 0 )
{
coordlist.push_back( std::vector< Coordinate >() );
++curid;
prev = Coordinate::invalidCoord();
}
continue;
}
if ( ! ( ( fabs( c.x ) <= 1000 ) && ( fabs( c.y ) <= 1000 ) ) )
continue;
// if there's too much distance between this coordinate and the previous
// one, then it's another piece of curve not joined with the rest
if ( prev.valid() && ( c.distance( prev ) > 4.0 ) )
{
coordlist.push_back( std::vector< Coordinate >() );
++curid;
}
coordlist[curid].push_back( c );
prev = c;
}
// special case for ellipse
if ( const ConicImp* conic = dynamic_cast< const ConicImp* >( imp ) )
{
// if ellipse, close its path
if ( conic->conicType() == 1 && coordlist.size() == 1 && coordlist[0].size() > 1 )
{
coordlist[0].push_back( coordlist[0][0] );
}
}
for ( uint i = 0; i < coordlist.size(); ++i )
{
uint s = coordlist[i].size();
// there's no point in draw curves empty or with only one point
if ( s <= 1 )
continue;
mstream << prefix;
for ( uint j = 0; j < s; ++j )
emitCoord( coordlist[i][j] );
newLine();
}
}
void LatexExportImpVisitor::visit( ObjectHolder* obj )
{
if ( ! obj->drawer()->shown() )
return;
const int id = findColor( obj->drawer()->color() );
if ( id == -1 )
return;
mcurcolorid = mcolors[id].name;
mcurobj = obj;
obj->imp()->visit( this );
}
void LatexExportImpVisitor::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;
emitLine( a, b, width, mcurobj->drawer()->style() );
}
void LatexExportImpVisitor::visit( const PointImp* imp )
{
int width = mcurobj->drawer()->width();
if ( width == -1 ) width = 5;
width /= 5;
mstream << "\\psdots[linecolor=" << mcurcolorid
<< ",dotscale=" << width << ",dotstyle=";
const int ps = mcurobj->drawer()->pointStyle();
TQString pss( "*,fillstyle=solid,fillcolor=" + mcurcolorid );
if ( ps == 1 )
pss = "o,fillstyle=none";
else if ( ps == 2 )
pss = "square*,fillstyle=solid,fillcolor=" + mcurcolorid;
else if ( ps == 3 )
pss = "square,fillstyle=none";
else if ( ps == 4 )
pss = "+,dotangle=45";
mstream << pss << "]";
emitCoord( imp->coordinate() );
newLine();
}
void LatexExportImpVisitor::visit( const TextImp* imp )
{
// FIXME: support multiline texts...
mstream << "\\rput[tl]";
emitCoord( imp->coordinate() );
newLine();
mstream << "{";
newLine();
if ( imp->hasFrame() )
{
mstream << " \\psframebox[linecolor=c5c2c5,linewidth=0.01"
<< ",fillstyle=solid,fillcolor=ffffde]"
<< "{" << imp->text() << "}";
}
else
{
mstream << imp->text();
}
newLine();
mstream << "}";
newLine();
}
void LatexExportImpVisitor::visit( const AngleImp* imp )
{
const Coordinate center = imp->point();
const double radius = dimRealToCoord( 50 ) * unit;
double startangle = imp->startAngle();
double endangle = startangle + imp->angle();
// if ( startangle > M_PI )
// startangle -= 2 * M_PI;
startangle = Goniometry::convert( startangle, Goniometry::Rad, Goniometry::Deg );
// if ( endangle > 2 * M_PI )
// endangle -= 2 * M_PI;
endangle = Goniometry::convert( endangle, Goniometry::Rad, Goniometry::Deg );
int width = mcurobj->drawer()->width();
if ( width == -1 ) width = 1;
mstream << "\\psarc[linecolor=" << mcurcolorid << ",linewidth=" << width / 100.0
<< "," << writeStyle( mcurobj->drawer()->style() ) << ",arrowscale=3,arrowinset=0]{->}";
emitCoord( center );
mstream << "{" << radius << "}{" << startangle << "}{" << endangle << "}";
newLine();
}
void LatexExportImpVisitor::visit( const VectorImp* 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, mcurobj->drawer()->style(), true );
}
void LatexExportImpVisitor::visit( const LocusImp* imp )
{
plotGenericCurve( imp );
}
void LatexExportImpVisitor::visit( const CircleImp* imp )
{
int width = mcurobj->drawer()->width();
if ( width == -1 ) width = 1;
mstream << "\\pscircle[linecolor=" << mcurcolorid << ",linewidth=" << width / 100.0
<< "," << writeStyle( mcurobj->drawer()->style() ) << "]";
emitCoord( imp->center() );
mstream << "{" << imp->radius() * unit << "}";
newLine();
}
void LatexExportImpVisitor::visit( const ConicImp* imp )
{
plotGenericCurve( imp );
}
void LatexExportImpVisitor::visit( const CubicImp* )
{
// FIXME: cubic are not drawn correctly with plotGenericCurve
// plotGenericCurve( imp );
}
void LatexExportImpVisitor::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, mcurobj->drawer()->style() );
}
void LatexExportImpVisitor::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, mcurobj->drawer()->style() );
}
void LatexExportImpVisitor::visit( const ArcImp* imp )
{
const Coordinate center = imp->center();
const double radius = imp->radius() * unit;
double startangle = imp->startAngle();
double endangle = startangle + imp->angle();
// if ( startangle > M_PI )
// startangle -= 2 * M_PI;
startangle = Goniometry::convert( startangle, Goniometry::Rad, Goniometry::Deg );
// if ( endangle > M_PI )
// endangle -= 2 * M_PI;
endangle = Goniometry::convert( endangle, Goniometry::Rad, Goniometry::Deg );
int width = mcurobj->drawer()->width();
if ( width == -1 ) width = 1;
mstream << "\\psarc[linecolor=" << mcurcolorid << ",linewidth=" << width / 100.0
<< "," << writeStyle( mcurobj->drawer()->style() ) << "]";
emitCoord( center );
mstream << "{" << radius << "}{" << startangle << "}{" << endangle << "}";
newLine();
}
void LatexExportImpVisitor::visit( const PolygonImp* imp )
{
int width = mcurobj->drawer()->width();
if ( width == -1 ) width = 1;
mstream << "\\pspolygon[linecolor=" << mcurcolorid << ",linewidth=0"
<< "," << writeStyle( mcurobj->drawer()->style() )
<< ",hatchcolor=" << mcurcolorid << ",hatchwidth=0.5pt,hatchsep=0.5pt"
<< ",fillcolor=" << mcurcolorid << ",fillstyle=crosshatch]";
std::vector<Coordinate> pts = imp->points();
for ( uint i = 0; i < pts.size(); i++ )
{
emitCoord( pts[i] );
}
newLine();
}
void LatexExporter::run( const KigPart& doc, KigWidget& w )
{
KigFileDialog* kfd = new KigFileDialog(
TQString(), i18n( "*.tex|Latex Documents (*.tex)" ),
i18n( "Export as Latex" ), &w );
kfd->setOptionCaption( i18n( "Latex Options" ) );
LatexExporterOptions* opts = new LatexExporterOptions( 0L );
kfd->setOptionsWidget( opts );
opts->showGridCheckBox->setChecked( doc.document().grid() );
opts->showAxesCheckBox->setChecked( doc.document().axes() );
opts->showExtraFrameCheckBox->setChecked( false );
if ( !kfd->exec() )
return;
TQString file_name = kfd->selectedFile();
bool showgrid = opts->showGridCheckBox->isOn();
bool showaxes = opts->showAxesCheckBox->isOn();
bool showframe = opts->showExtraFrameCheckBox->isOn();
delete opts;
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 << "\\documentclass[a4paper]{minimal}\n";
// stream << "\\usepackage[latin1]{inputenc}\n";
stream << "\\usepackage{pstricks}\n";
stream << "\\usepackage{pst-plot}\n";
stream << "\\author{Kig " << KIGVERSION << "}\n";
stream << "\\begin{document}\n";
const double bottom = w.showingRect().bottom();
const double left = w.showingRect().left();
const double height = w.showingRect().height();
const double width = w.showingRect().width();
/*
// TODO: calculating aspect ratio...
if ( 297 / 210 >= height / width )
{
}
*/
const double tmpwidth = 15.0;
const double xunit = tmpwidth / width;
const double yunit = xunit;
stream << "\\begin{pspicture*}(0,0)(" << tmpwidth << "," << yunit * height << ")\n";
stream << "\\psset{xunit=" << xunit << "}\n";
stream << "\\psset{yunit=" << yunit << "}\n";
std::vector<ObjectHolder*> os = doc.document().objects();
LatexExportImpVisitor visitor( stream, w );
visitor.unit = xunit;
for ( std::vector<ObjectHolder*>::const_iterator i = os.begin();
i != os.end(); ++i )
{
if ( ! ( *i )->shown() ) continue;
visitor.mapColor( ( *i )->drawer()->color() );
};
visitor.mapColor( TQColor( 255, 255, 222 ) ); // ffffde - text label background
visitor.mapColor( TQColor( 197, 194, 197 ) ); // c5c2c5 - text label border line
visitor.mapColor( TQColor( 160, 160, 164 ) ); // a0a0a4 - axes color
visitor.mapColor( TQColor( 192, 192, 192 ) ); // c0c0c0 - grid color
// extra frame
if ( showframe )
{
stream << "\\psframe[linecolor=black,linewidth=0.02]"
<< "(0,0)"
<< "(" << width << "," << height << ")"
<< "\n";
}
// grid
if ( showgrid )
{
// vertical lines...
double startingpoint = - left - 1 + static_cast<int>( KDE_TRUNC( left ) );
for ( double i = startingpoint; i < width; ++i )
{
stream << "\\psline[linecolor=c0c0c0,linewidth=0.01,linestyle=dashed]"
<< "(" << i << ",0)"
<< "(" << i << "," << height << ")"
<< "\n";
}
// horizontal lines...
startingpoint = - bottom - 1 + static_cast<int>( KDE_TRUNC( bottom ) );
for ( double i = startingpoint; i < height; ++i )
{
stream << "\\psline[linecolor=c0c0c0,linewidth=0.01,linestyle=dashed]"
<< "(0," << i << ")"
<< "(" << width << "," << i << ")"
<< "\n";
}
}
// axes
if ( showaxes )
{
stream << "\\psaxes[linecolor=a0a0a4,linewidth=0.03,ticks=none,arrowinset=0]{->}"
<< "(" << -left << "," << -bottom << ")"
<< "(0,0)"
<< "(" << width << "," << height << ")"
<< "\n";
}
for ( std::vector<ObjectHolder*>::const_iterator i = os.begin();
i != os.end(); ++i )
{
visitor.visit( *i );
};
stream << "\\end{pspicture*}\n";
stream << "\\end{document}\n";
}