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.
koffice/filters/karbon/xfig/xfigimport.cc

761 lines
21 KiB

/*
Copyright (C) 1998 Kai-Uwe Sattler <kus@iti.cs.uni-magdeburg.de>
Copyright (C) 2001, Rob Buis <rwlbuis@wanadoo.nl>
Copyright (C) 2003, Rob Buis <buis@kde.org>
This file is part of the KDE project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
DESCRIPTION
*/
#include <assert.h>
#include <fstream.h>
#include <limits.h>
#include <math.h>
#include <GDocument.h>
#include <GPage.h>
#include <GGroup.h>
#include <GPolyline.h>
#include <GOval.h>
#include <GPolygon.h>
#include <GText.h>
#include <xfigimport.h>
#include <xfigimport.moc>
#include <tqtl.h>
#include <kdebug.h>
#include <core/vdocument.h>
namespace std { };
using namespace std;
#define RAD_FACTOR 180.0 / M_PI
unsigned int colors[] = {
0x000090,
0x0000b0,
0x0000d0,
0x87ceff,
0x009000,
0x00b000,
0x00d000,
0x009090,
0x00b0b0,
0x00d0d0,
0x900000,
0xb00000,
0xd00000,
0x900090,
0xb000b0,
0xd000d0,
0x803000,
0xa04000,
0xc06000,
0xff8080,
0xffa0a0,
0xffc0c0,
0xffe0e0,
0xffd700
};
int arrow_ids[] = {
6, 1, 2, 7
};
struct PSFont {
const char* family;
TQFont::Weight weight;
bool italic;
} psFontTable[] = {
{ "times", TQFont::Normal, false }, // Times Roman
{ "times", TQFont::Normal, true }, // Times Italic
{ "times", TQFont::Bold, false }, // Times Bold
{ "times", TQFont::Bold, true }, // Times Bold Italic
{ "avantgarde", TQFont::Normal, false }, // AvantGarde Book
{ "avantgarde", TQFont::Normal, true }, // AvantGarde Book Oblique
{ "avantgarde", TQFont::DemiBold, false }, // AvantGarde Demi
{ "avantgarde", TQFont::DemiBold, true }, // AvantGarde Demi Oblique
{ "bookman", TQFont::Light, false }, // Bookman Light
{ "bookman", TQFont::Light, true }, // Bookman Light Italic
{ "bookman", TQFont::DemiBold, false }, // Bookman Demi
{ "bookman", TQFont::DemiBold, true }, // Bookman Demi Italic
{ "courier", TQFont::Normal, false }, // Courier
{ "courier", TQFont::Normal, true }, // Courier Oblique
{ "courier", TQFont::Bold, false }, // Courier Bold
{ "courier", TQFont::Bold, true }, // Courier Bold Oblique
{ "helvetica", TQFont::Normal, false }, // Helvetica
{ "helvetica", TQFont::Normal, true }, // Helvetica Oblique
{ "helvetica", TQFont::Bold, false }, // Helvetica Bold
{ "helvetica", TQFont::Bold, true }, // Helvetica Bold Oblique
{ "helvetica", TQFont::Normal, false }, // Helvetica Narrow
{ "helvetica", TQFont::Normal, true }, // Helvetica Narrow Oblique
{ "helvetica", TQFont::Bold, false }, // Helvetica Narrow Bold
{ "helvetica", TQFont::Bold, true }, // Helvetica Narrow Bold Oblique
{ "newcenturyschoolbook", TQFont::Normal, false },// New Century Schoolbook
{ "newcenturyschoolbook", TQFont::Normal, true }, // New Century Italic
{ "newcenturyschoolbook", TQFont::Bold, false }, // New Century Bold
{ "newcenturyschoolbook", TQFont::Bold, true }, // New Century Bold Italic
{ "palatino", TQFont::Normal, false }, // Palatino Roman
{ "palatino", TQFont::Normal, true }, // Palatino Italic
{ "palatino", TQFont::Bold, false }, // Palatino Bold
{ "palatino", TQFont::Bold, true }, // Palatino Bold Italic
{ "symbol", TQFont::Normal, false }, // Symbol
{ "zapfchancery", TQFont::Normal, false }, // Zapf Chancery Medium Italic
{ "zapfdingbats", TQFont::Normal, false }, // Zapf Dingbats
};
int hexstrToInt (const char *str) {
const int fak[] = { 16, 1 };
int value = 0, v;
for (int i = 0; i < 2; i++) {
if (str[i] >= '0' && str[i] <= '9')
v = str[i] - '0';
else
v = str[i] - 'a' + 10;
value += v * fak[i];
}
return value;
}
XFIGImport::XFIGImport( KoFilter *parent, const char *name ) : KoFilter(parent, name)
{
fig_resolution = 1200.0 / 72.0;
coordinate_system = 2;
colorTable.insert (0, new TQColor (TQt::black));
colorTable.insert (1, new TQColor (TQt::blue));
colorTable.insert (2, new TQColor (TQt::green));
colorTable.insert (3, new TQColor (TQt::cyan));
colorTable.insert (4, new TQColor (TQt::red));
colorTable.insert (5, new TQColor (TQt::magenta));
colorTable.insert (6, new TQColor (TQt::yellow));
colorTable.insert (7, new TQColor (TQt::white));
for (int i = 0; i <= 23; i++)
colorTable.insert (i + 8, new TQColor (colors[i]));
objList.clear ();
}
XFIGImport::~XFIGImport()
{
}
bool XFIGImport::filterImport( const TQString &file, KoDocument *doc,
const TQString &from, const TQString &to,
const TQString & ) {
if( to != "application/x-karbon" || from != "image/x-xfig" )
return false;
char buf[255];
int value;
KoPageLayout layout;
ifstream fin( file.local8Bit() );
if (! fin)
return false;
KIllustratorDocument *kidoc = (KIllustratorDocument *) doc;
GDocument *gdoc = kidoc->gdoc();
//GPage *activePage = gdoc->activePage();
layout = gdoc->activePage()->pageLayout ();
fin.getline (buf, 255);
if (::strncmp (buf, "#FIG 3", 6)) {
kdDebug() << "ERROR: no xfig file or wrong header" << endl;
return false;
}
if (buf[7] == '2') {
version = 320;
}
else if (buf[7] == '1') {
version = 310;
}
else {
kdDebug() << "ERROR: unsupported xfig version" << endl;
return false;
}
/*
* read the header
*/
// orientation
fin.getline (buf, 255);
if (::strcmp (buf, "Landscape") == 0)
layout.orientation = PG_LANDSCAPE;
else if (::strcmp (buf, "Portrait") == 0)
layout.orientation = PG_PORTRAIT;
else
kdDebug() << "ERROR: invalid orientation" << endl;
// justification (don't know how to handle this)
fin.getline (buf, 255);
// units
fin.getline (buf, 255);
if (::strcmp (buf, "Metric") == 0)
layout.unit = PG_MM;
else if (::strcmp (buf, "Inches") == 0)
layout.unit = PG_INCH;
else
kdDebug() << "ERROR: invalid units" << endl;
if (version >= 320) {
// paper size (don't know how to handle this)
fin.getline (buf, 255);
// magnification
float magnification;
fin >> magnification;
fin.ignore (INT_MAX, '\n');
//multiple page (not supported yet)
fin.getline (buf, 255);
// transparent color (not supported yet)
int transColor;
fin >> transColor;
fin.ignore (INT_MAX, '\n');
}
// resolution and coordinate system
fin >> value >> coordinate_system;
fig_resolution = value / 72.0;
fin.ignore (INT_MAX, '\n');
// now read in the objects
while (! fin.eof ()) {
int tag = -1;
fin >> tag;
if (tag == -1) {
// EOF
buildDocument (gdoc);
return true;
}
switch (tag) {
case 0:
// a color pseudo object
parseColorObject (fin);
break;
case 1:
// a ellipse
parseEllipse (fin, gdoc);
break;
case 2:
// a polyline
parsePolyline (fin, gdoc);
break;
case 3:
// a spline
parseSpline (fin, gdoc);
break;
case 4:
// a text
parseText (fin, gdoc);
break;
case 5:
// an arc
parseArc (fin, gdoc);
break;
case 6:
// a compound object
parseCompoundObject (fin, gdoc);
break;
case -6:
// end of compound object --> ignore it
break;
default:
// should not occur
kdDebug() << "unknown object type: " << tag << endl;
break;
}
}
buildDocument (gdoc);
return true;
}
void XFIGImport::parseColorObject (istream& fin) {
int number, red, green, blue;
char buf[20], red_str[3], green_str[3], blue_str[3];
fin >> number >> buf;
strncpy (red_str, &buf[1], 2); red_str[2] = '\0';
strncpy (green_str, &buf[3], 2); green_str[2] = '\0';
strncpy (blue_str, &buf[5], 2); blue_str[2] = '\0';
red = hexstrToInt (red_str);
green = hexstrToInt (green_str);
blue = hexstrToInt (blue_str);
colorTable.insert (number, new TQColor (red, green, blue));
}
void XFIGImport::parseArc (istream& fin, GDocument* doc) {
int sub_type, line_style, thickness, pen_color, fill_color,
depth, pen_style, area_fill, cap_style, direction,
forward_arrow, backward_arrow, x1, y1, x2, y2, x3, y3;
float center_x, center_y;
float style_val;
GOval *obj = new GOval (doc);
// first line
fin >> sub_type >> line_style >> thickness >> pen_color >> fill_color
>> depth >> pen_style >> area_fill >> style_val >> cap_style
>> direction >> forward_arrow >> backward_arrow
>> center_x >> center_y >> x1 >> y1 >> x2 >> y2 >> x3 >> y3;
if (forward_arrow > 0) {
// forward arow line
fin.ignore (INT_MAX, '\n');
}
if (backward_arrow > 0) {
// backward arrow line
fin.ignore (INT_MAX, '\n');
}
// compute radius
float dx = x1 - center_x;
float dy = y1 - center_y;
float radius = sqrt (dx * dx + dy * dy);
if (radius==0) {
delete obj;
return;
}
Coord p1 ((center_x - radius) / fig_resolution,
(center_y - radius) / fig_resolution);
Coord p2 ((center_x + radius) / fig_resolution,
(center_y + radius) / fig_resolution);
obj->setStartPoint (p1);
obj->setEndPoint (p2);
if (sub_type == 0)
obj->setOutlineShape (GObject::OutlineInfo::PieShape);
else if (sub_type == 1)
obj->setOutlineShape (GObject::OutlineInfo::ArcShape);
p1 = Coord (center_x / fig_resolution, center_y /fig_resolution);
float m;
float angle1;
p2 = Coord (x1 / fig_resolution, y1 /fig_resolution);
if (p2.x () == p1.x ()) {
if (p2.y () > p1.y ())
angle1 = 90;
else
angle1 = -90;
}
else {
m = ((p2.y () - p1.y ()) / (p2.x () - p1.x ()));
if ( p2.x () > p1.x ())
angle1 = atan (m) * RAD_FACTOR;
else
angle1 = 180 + atan (m) * RAD_FACTOR;
}
float angle2;
p2 = Coord (x3 / fig_resolution, y3 /fig_resolution);
if (p2.x () == p1.x ()) {
if (p2.y () > p1.y ())
angle2 = 90;
else
angle2 = -90;
}
else {
m = ((p2.y () - p1.y ()) / (p2.x () - p1.x ()));
if ( p2.x () > p1.x ())
angle2 = atan (m) * RAD_FACTOR;
else
angle2 = 180 + atan (m) * RAD_FACTOR;
}
if (direction==0) // clockwise
obj->setAngles (angle2, angle1);
else if (direction==1) // counterclockwise
obj->setAngles (angle1, angle2);
// now set the properties
setProperties (obj, pen_color, pen_style, thickness, area_fill, fill_color);
objList.append( GObjectListItem( depth, obj ) );
}
void XFIGImport::parseEllipse (istream& fin, GDocument* doc) {
int sub_type, line_style, thickness, pen_color, fill_color,
depth, pen_style, area_fill, direction, center_x, center_y,
radius_x, radius_y, start_x, start_y, end_x, end_y;
float style_val, angle;
GOval *obj = new GOval (doc);
// first line
fin >> sub_type >> line_style >> thickness >> pen_color >> fill_color
>> depth >> pen_style >> area_fill >> style_val >> direction
>> angle >> center_x >> center_y >> radius_x >> radius_y
>> start_x >> start_y >> end_x >> end_y;
Coord p1, p2;
p1 = Coord ((center_x - radius_x) /fig_resolution,
(center_y - radius_y) /fig_resolution);
p2 = Coord ((center_x + radius_x) /fig_resolution,
(center_y + radius_y) /fig_resolution);
obj->setStartPoint (p1);
obj->setEndPoint (p2);
// now set the properties
setProperties (obj, pen_color, pen_style, thickness, area_fill, fill_color);
objList.append( GObjectListItem( depth, obj ) );
}
void XFIGImport::parsePolyline (istream& fin, GDocument* doc) {
int sub_type, line_style, thickness, pen_color, fill_color,
depth, pen_style, area_fill, join_style, cap_style, radius,
forward_arrow, backward_arrow, npoints;
float style_val;
GPolyline *obj = NULL;
// first line
fin >> sub_type >> line_style >> thickness >> pen_color >> fill_color
>> depth >> pen_style >> area_fill >> style_val >> join_style
>> cap_style >> radius >> forward_arrow >> backward_arrow
>> npoints;
fin.ignore (INT_MAX, '\n');
switch (sub_type) {
case 1: // polyline
obj = new GPolyline (doc);
break;
case 2: // box
obj = new GPolygon (doc);
break;
case 3: // polygon
obj = new GPolygon (doc);
break;
case 4: // arc-box
obj = new GPolygon (doc);
break;
case 5: // imported picture
return;
break;
default:
// doesn't occur
kdDebug() << "unknown subtype: " << sub_type << endl;
break;
}
assert (obj != NULL);
int arrow_type, arrow_style;
float arrow_thickness, arrow_width, arrow_height;
GObject::OutlineInfo oinfo;
oinfo.mask = GObject::OutlineInfo::Custom;
oinfo.startArrowId = oinfo.endArrowId = 0;
if (forward_arrow > 0) {
// forward arrow line
fin >> arrow_type >> arrow_style >> arrow_thickness
>> arrow_width >> arrow_height;
oinfo.endArrowId = arrow_ids[arrow_type];
if (oinfo.endArrowId == 1 && arrow_style == 0)
oinfo.endArrowId = 4;
fin.ignore (INT_MAX, '\n');
}
if (backward_arrow > 0) {
// backward arrow line
fin >> arrow_type >> arrow_style >> arrow_thickness
>> arrow_width >> arrow_height;
oinfo.startArrowId = arrow_ids[arrow_type];
if (oinfo.startArrowId == 1 && arrow_style == 0)
oinfo.startArrowId = 4;
fin.ignore (INT_MAX, '\n');
}
// points line
for (int i = 0; i < npoints; i++) {
int x, y;
fin >> x >> y;
if ((sub_type == 2 || sub_type == 3) && i == npoints -1)
// first point == last point
break;
Coord p (x / fig_resolution, y / fig_resolution);
obj->_addPoint (i, p);
}
if (oinfo.startArrowId || oinfo.endArrowId)
obj->setOutlineInfo (oinfo);
// now set the properties
setProperties (obj, pen_color, line_style, thickness, area_fill, fill_color);
// and insert the object
objList.append( GObjectListItem( depth, obj ) );
}
void XFIGImport::parseSpline (istream& fin, GDocument* doc)
{
int sub_type, line_style, thickness, pen_color, fill_color, depth,
pen_style, area_fill, cap_style, forward_arrow, backward_arrow, npoints;
float style_val;
// this should be a spline
GPolyline *obj = 0L;
fin >> sub_type >> line_style >> thickness >> pen_color >> fill_color
>> depth >> pen_style >> area_fill >> style_val >> cap_style
>> forward_arrow >> backward_arrow >> npoints;
if (sub_type == 1 || sub_type == 3 || sub_type == 5)
obj = new GPolygon (doc);
else
obj = new GPolyline (doc);
int arrow_type, arrow_style;
float arrow_thickness, arrow_width, arrow_height;
GObject::OutlineInfo oinfo;
oinfo.mask = GObject::OutlineInfo::Custom;
oinfo.startArrowId = oinfo.endArrowId = 0;
if (forward_arrow > 0) {
// forward arrow line
fin >> arrow_type >> arrow_style >> arrow_thickness
>> arrow_width >> arrow_height;
oinfo.endArrowId = arrow_ids[arrow_type];
if (oinfo.endArrowId == 1 && arrow_style == 0)
oinfo.endArrowId = 4;
fin.ignore (INT_MAX, '\n');
}
if (backward_arrow > 0) {
// backward arrow line
fin >> arrow_type >> arrow_style >> arrow_thickness
>> arrow_width >> arrow_height;
oinfo.startArrowId = arrow_ids[arrow_type];
if (oinfo.startArrowId == 1 && arrow_style == 0)
oinfo.startArrowId = 4;
fin.ignore (INT_MAX, '\n');
}
// points line
for (int i = 0; i < npoints; i++) {
int x, y;
fin >> x >> y;
if ((sub_type == 1 || sub_type == 3 || sub_type == 5) && i == npoints -1)
// first point == last point
break;
Coord p (x / fig_resolution, y / fig_resolution);
obj->_addPoint (i, p);
}
// control points line
for (int i = 0; i < npoints; i++) {
float fac;
fin >> fac;
// ignore it now
// fin.ignore (INT_MAX, '\n');
}
if (oinfo.startArrowId || oinfo.endArrowId)
obj->setOutlineInfo (oinfo);
// now set the properties
setProperties (obj, pen_color, line_style, thickness, area_fill, fill_color);
// and insert the object
objList.append( GObjectListItem( depth, obj ) );
}
void XFIGImport::parseText (istream& fin, GDocument* doc)
{
int sub_type, color, depth, pen_style, font, font_flags, x, y;
float font_size, angle, height, length;
GText *obj = new GText (doc);
char c;
char ocode[4];
bool finished = false;
TQString text;
TQFont qfont;
fin >> sub_type >> color >> depth >> pen_style >> font >> font_size
>> angle >> font_flags >> height >> length >> x >> y;
if (font_flags & 4) {
// PostScript font
if (font == -1)
font = 0;
qfont = TQFont (psFontTable[font].family, tqRound (font_size),
psFontTable[font].weight, psFontTable[font].italic);
}
else {
// LaTeX font
switch (font) {
case 1: // Roman
qfont.setFamily ("times");
break;
case 2: // Bold
qfont.setBold (true);
break;
case 3: // Italic
qfont.setItalic (true);
break;
case 4: // Sans Serif
qfont.setFamily ("helvetica");
break;
case 5: // Typewriter
qfont.setFamily ("Courier");
break;
default:
break;
}
}
qfont.setPointSize (tqRound (font_size));
obj->setFont (qfont);
while (! finished) {
fin.get (c);
if (c == '\\') {
fin.get (ocode, 4);
int code = (ocode[0] - '0') * 64 +
(ocode[1] - '0') * 8 +
(ocode[2] - '0');
if (code == 1)
finished = true;
else
text += (char) code;
}
else
text += c;
}
obj->setText (text);
if (sub_type == 1) {
GText::TextInfo ti = obj->getTextInfo ();
ti.align = GText::TextInfo::AlignCenter;
obj->setTextInfo (ti);
}
else if (sub_type == 2) {
GText::TextInfo ti = obj->getTextInfo ();
ti.align = GText::TextInfo::AlignRight;
obj->setTextInfo (ti);
}
Coord origin (x / fig_resolution, y / fig_resolution - qfont.pointSize ());
obj->setOrigin (origin);
if (angle != 0) {
// rotate the text
float nangle = angle * RAD_FACTOR;
TQWMatrix m1, m2, m3;
Coord rotCenter;
if (sub_type == 0) {
rotCenter = Coord (obj->boundingBox ().left (),
obj->boundingBox ().bottom ());
}
else if (sub_type == 1) {
rotCenter = Coord (obj->boundingBox ().width () / 2,
obj->boundingBox ().bottom ());
}
else if (sub_type == 2) {
rotCenter = Coord (obj->boundingBox ().right (),
obj->boundingBox ().bottom ());
}
m1.translate (-rotCenter.x (), -rotCenter.y ());
m2.rotate (-nangle);
m3.translate (rotCenter.x (), rotCenter.y ());
obj->transform (m1);
obj->transform (m2);
obj->transform (m3, true);
}
objList.append( GObjectListItem( depth, obj ) );
}
void XFIGImport::parseCompoundObject (istream& fin, GDocument* /*doc*/) {
int upperright_x, upperright_y, lowerleft_x, lowerleft_y;
fin >> upperright_x >> upperright_y >> lowerleft_x >> lowerleft_y;
fin.ignore (INT_MAX, '\n');
}
/**
* Copy all parsed objects from the sorted list to the document.
*/
void XFIGImport::buildDocument (GDocument *doc) {
doc->setAutoUpdate (false);
// This will sort all object, by decreasing depth
qBubbleSort(objList);
// Now all we need to do is insert them in the document, in that order
TQValueList<GObjectListItem>::Iterator it=objList.begin();
for ( ; it != objList.end() ; ++it )
{
//kdDebug() << "Inserting object with depth=" << (*it).depth << endl;
GObject* obj = (*it).object;
obj->ref ();
doc->activePage()->insertObject (obj);
}
doc->setAutoUpdate (true);
objList.clear(); // save memory
}
void XFIGImport::setProperties (GObject* obj, int pen_color, int style,
int thickness, int area_fill, int fill_color) {
if (pen_color >= 0)
obj->setOutlineColor (*colorTable[pen_color]);
if (style < 1)
obj->setOutlineStyle (TQt::SolidLine);
else if (style == 1)
obj->setOutlineStyle (TQt::DashLine);
else if (style == 2)
obj->setOutlineStyle (TQt::DotLine);
obj->setOutlineWidth (thickness * 72.0 / 80.0);
if (area_fill == -1)
obj->setFillStyle (GObject::FillInfo::NoFill);
else {
obj->setFillStyle (GObject::FillInfo::SolidFill);
if (fill_color < 1) {
// for BLACK or DEFAULT color
int val = tqRound ((20 - area_fill) * 255.0 / 20.0);
obj->setFillColor (TQColor (val, val, val));
}
else if (fill_color == 7) {
// for WHITE color
int val = tqRound ( area_fill * 255.0 / 20.0);
obj->setFillColor (TQColor (val, val, val));
}
else
obj->setFillColor (*colorTable[fill_color]);
}
}