/* Copyright (C) 1998 Kai-Uwe Sattler Copyright (C) 2001, Rob Buis Copyright (C) 2003, Rob Buis 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 *tqparent, const char *name ) : KoFilter(tqparent, 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 tqlayout; ifstream fin( file.local8Bit() ); if (! fin) return false; KIllustratorDocument *kidoc = (KIllustratorDocument *) doc; GDocument *gdoc = kidoc->gdoc(); //GPage *activePage = gdoc->activePage(); tqlayout = 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) tqlayout.orientation = PG_LANDSCAPE; else if (::strcmp (buf, "Portrait") == 0) tqlayout.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) tqlayout.unit = PG_MM; else if (::strcmp (buf, "Inches") == 0) tqlayout.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.tqmask = 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.tqmask = 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::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]); } }