SVGImage.cpp

Go to the documentation of this file.
00001 #pragma ident "$Id: SVGImage.cpp 3140 2012-06-18 15:03:02Z susancummins $"
00002 
00004 
00005 //============================================================================
00006 //
00007 //  This file is part of GPSTk, the GPS Toolkit.
00008 //
00009 //  The GPSTk is free software; you can redistribute it and/or modify
00010 //  it under the terms of the GNU Lesser General Public License as published
00011 //  by the Free Software Foundation; either version 2.1 of the License, or
00012 //  any later version.
00013 //
00014 //  The GPSTk is distributed in the hope that it will be useful,
00015 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 //  GNU Lesser General Public License for more details.
00018 //
00019 //  You should have received a copy of the GNU Lesser General Public
00020 //  License along with GPSTk; if not, write to the Free Software Foundation,
00021 //  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
00022 //  
00023 //  Copyright 2004, The University of Texas at Austin
00024 //
00025 //===========================================================================
00026 
00027 #include <sstream>
00028 #include <iomanip>
00029 #include "SVGImage.hpp"
00030 #include "PNG.hpp"
00031 #include "Base64Encoder.hpp"
00032 
00033 namespace vdraw
00034 {
00035   static char VIEWER_ENV_VAR_NAME[]="VDRAW_SVG_VIEWER";
00036 
00041   SVGImage::SVGImage(std::ostream& stream, double width, double height, ORIGIN_LOCATION iloc):
00042     VGImage(width, height, iloc),
00043     viewerManager(VIEWER_ENV_VAR_NAME),
00044     ostr(stream)
00045   {
00046     outputHeader();
00047   }
00048 
00049   SVGImage::SVGImage(const char* fname, double width, double height, ORIGIN_LOCATION iloc):
00050     VGImage(width, height, iloc), 
00051     viewerManager(VIEWER_ENV_VAR_NAME),
00052     filename(fname), myfstream(fname), ostr(myfstream)
00053   {
00054     outputHeader();
00055   }
00056 
00057   SVGImage::~SVGImage(void)
00058   {
00059     if (!footerHasBeenWritten) outputFooter();
00060   }
00061 
00066   void SVGImage::outputHeader()
00067   {
00068     using namespace std;
00069 
00070     ostr << "<?xml version=\"1.0\"?>" << endl;
00071     ostr << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"" << endl;
00072     ostr << " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << endl;
00073     ostr << endl;
00074     ostr << "<!-- Created by vplot -->" << endl;
00075     ostr << endl;
00076     ostr << "<svg width=\"" << canvasWidth << "pt\" height=\""; 
00077     ostr << canvasHeight << "pt\"";
00079     ostr << " xmlns=\"http://www.w3.org/2000/svg\"";
00080     ostr << " xmlns:xlink=\"http://www.w3.org/1999/xlink\"";
00081     ostr << " version=\"1.1\">" << endl;
00082 
00083     // Output defaults?
00084     ostr << "  <defs>" << endl
00085          << "    <style type=\"text/css\">" << endl
00086          << "      line    { fill: none; stroke: black; stroke-width: 1pt }" << endl
00087          << "      polyline{ fill: none; stroke: black; stroke-width: 1pt }" << endl         
00088          << "      rect    { fill: none; stroke: black; stroke-width: 1pt }" << endl
00089          << "      circle  { fill: none; stroke: black; stroke-width: 1pt }" << endl
00090          << "      polygon { fill: none; stroke: black; stroke-width: 1pt }" << endl                  
00091          << "      path    { fill: none; stroke: black; stroke-width: 1pt }" << endl         
00092          << "      text    { fill: black; font-size: 12pt; "
00093          << "font-family:monospace; text-anchor:start }" << endl
00094          << "    </style>" << endl
00095          << "  </defs>" << endl;
00096 
00097     // Add a transform to flip y so that the origin is in the bottom left 
00098     // corner
00099     if ( ll )
00100     {
00101       ostr << endl << "   <g transform=\"translate(0," << canvasHeight;
00102       ostr << ") scale(1,-1)\">" << endl;
00103       // Set the indentation
00104       tab=string("      ");
00105     } 
00106     else
00107       tab=string("   ");
00108   }
00109 
00110   void SVGImage::outputFooter(void)
00111   {
00112     using namespace std;
00113 
00114     // Close our transformation of origin to bottom right
00115     if ( ll )
00116       ostr << "   </g>" << endl; 
00117 
00118     ostr << "</svg>" << endl;
00119     footerHasBeenWritten = true;
00120   }
00121 
00122   void SVGImage::comment (const Comment& comment)
00123   {
00124     using namespace std;
00125 
00126     ostr << tab << "<!-- ";
00127     string com(comment.str);
00128     string::size_type loc;
00129     while( (loc = com.find("-->")) != string::npos)
00130       com = com.replace(loc+1,1,"=");
00131     bool hadnl = false;
00132     if((loc = com.find("\n")) != string::npos)
00133     {
00134       hadnl = true;
00135       ostr << endl;
00136       string::size_type lastloc = 0;
00137       while( (loc = com.find("\n",lastloc)) != string::npos )
00138       {
00139         ostr << tab << "     " << com.substr(lastloc,loc-lastloc+1);
00140         lastloc = loc + 1;
00141       }
00142       if( lastloc < (com.length()) )
00143         ostr << tab << "     " << com.substr(lastloc) << endl;
00144       ostr << tab << "-->" << endl;        
00145     }
00146     else
00147       ostr << com << " -->" << endl;
00148   }
00149 
00150   void SVGImage::line (const Line& line) 
00151   {
00152     using namespace std;
00153 
00154     if ( line.size() == 0 )
00155     {
00156       comment("Empty Line ignored...");
00157       return;
00158     }
00159 
00160     Marker cmarker;
00161     StyleType tmarker = getCorrectMarker(&cmarker,line);
00162     if( tmarker == SHAPE )
00163     {
00164       if ( !markerDefined || (lastMarker != line.getMarker()) )
00165       {
00166         defineMarker( line.getMarker() );
00167       }
00168     }
00169 
00170     StrokeStyle ss;
00171     StyleType tss = getCorrectStrokeStyle(&ss,line);
00172 
00173     if ( line.size() <= 2 )
00174     {
00175       ostr << tab << "<line";
00176       auto_ptr<Path> abspath = line.asAbsolute();
00177       Path::const_iterator i=abspath->begin();
00178       // Since there is no method for drawing a single point and there already
00179       // is a marker defined...
00180       if( line.size() == 1 )
00181       {
00182         ostr << " x1=\"" << i->first << "\"";
00183         ostr << " y1=\"" << i->second << "\"";
00184         ostr << " x2=\"" << i->first << "\"";
00185         ostr << " y2=\"" << i->second << "\"";
00186       }
00187       else
00188       {
00189         ostr << " x1=\"" << i->first << "\"";
00190         ostr << " y1=\"" << i->second << "\"";
00191         i++;
00192         ostr << " x2=\"" << i->first << "\"";
00193         ostr << " y2=\"" << i->second << "\"";
00194       }
00195 
00196       string tmp = strokeDesc(ss,tss,((tmarker!=NONE)&&(tmarker!=CLEAR)));
00197       if(tmp.size())
00198         ostr << " style=\"" << tmp << "\"";
00199 
00200       if( tmarker != NONE && tmarker != CLEAR )
00201       {
00202         string mname = cmarker.uniqueName();
00203         ostr << endl << tab << "   marker-end=\"url(#" 
00204           << mname << ")\" marker-mid=\"url(#" 
00205           << mname << ")\" marker-start=\"url(#"
00206           << mname << ")\"";
00207       }
00208       //else if( hasDefaultMarker )
00209       //  ostr << endl << tab << "   marker-end=\"url(#default)\" marker-mid=\"url(#default)\" marker-start=\"url(#default)\"";
00210 
00211       ostr << "/>" << endl;
00212     }
00213     else
00214     {
00215       // open line tag and specify x,y coords of two end points
00216       ostr << tab << "<polyline";
00217 
00218       string tmp = strokeDesc(ss,tss,((tmarker!=NONE)&&(tmarker!=CLEAR)));
00219       if(tmp.size())
00220         ostr << " style=\"" << tmp << "\"";
00221 
00222       if( tmarker != NONE && tmarker != CLEAR )
00223       {
00224         string mname = cmarker.uniqueName();
00225         ostr << endl << tab << "   marker-end=\"url(#" 
00226           << mname << ")\" marker-mid=\"url(#" 
00227           << mname << ")\" marker-start=\"url(#"
00228           << mname << ")\"";
00229       }
00230 
00231       ostr << endl;
00232 
00233       outputPoints(line, ostr);
00234 
00235       // Close xml tag  
00236       ostr << "/>" << endl;
00237     }
00238   }
00239 
00240   /*
00241      <defs>
00242      <marker id="mDot1" markerUnits="strokeWidth" markerWidth="2" markerHeight="2" viewBox="0 0 2 2" refX="1" refY="1"
00243      stroke="red" stroke-width="1pt" fill="red">
00244      <circle cx="1" cy="1" r=".3"/>
00245      </marker>
00246      </defs>
00247      <polygon points="1,1 72,0 72,72 100,100 150,150 0,72" stroke="black" stroke-opacity="0" fill="none" 
00248      marker-end="url(#mDot1)" marker-mid="url(#mDot1)" marker-start="url(#mDot1)" />
00249    */
00250 
00251   void SVGImage::defineMarker (const Marker& marker, int dfltname)
00252   {
00253     if( lastMarker == marker ) return;
00254 
00255     using namespace std;
00256 
00257     string name = marker.uniqueName();
00258     //cout << name << endl;;
00259 
00260     string savedtab=tab;
00261     string marktab=string("         ");
00262     string intab=string("            ");
00263 
00264     ostr << tab << "<defs>\n";
00265 
00266     double r = marker.getRange();
00267     double r2 = 2*r;
00268 
00269     ostr << marktab << "<marker id=\"" << name << "\" markerUnits=\"strokeWidth\" "
00270       << "markerWidth=\"" <<  r2 << "\" markerHeight=\"" <<  r2 << "\" "
00271       << "viewBox=\"0 0 " << r2 << " " << r2 << "\" \n"
00272       << marktab << "        "  << "refX=\"" << r << "\" refY=\"" << r << "\" ";
00273 
00274     string col;
00275     {
00276       stringstream ss;
00277       ss.fill('0');      
00278       ss << '#' << hex << setw(6) << marker.getColor().getRGB();
00279       col = ss.str();
00280     }
00281 
00282     // These are here just in case.  
00283     ostr.fill('0');
00284     ostr << "stroke-width=\".2pt\" " << "fill=\"none\">\n"; 
00285 
00286     // Initial marker tab is set up...now to put the shape in...
00287     if (marker.hasDefaultMark())
00288     {
00289       Marker::Mark mark = marker.getMark();
00290       switch(mark)      
00291       {
00292         case Marker::DOT:
00293           ostr << intab << "<circle cx=\"" << r << "\" cy=\"" << r << "\" "
00294             <<"r=\"" << r << "\" style=\"fill:" << col << ";stroke:" << col << "\"/>\n";
00295           break;
00296         case Marker::PLUS:
00297           ostr << intab << "<line x1=\"0\" y1=\"" << r << "\" "
00298             << "x2=\"" << r2 << "\" y2=\"" << r << "\"/>\n";
00299           ostr << intab << "<line x1=\"" << r << "\" y1=\"0\" "
00300             << "x2=\"" << r << "\" y2=\"" << r2 << "\"/>\n";
00301           break;
00302         case Marker::X:
00303           ostr << intab << "<line x1=\"0\" y1=\"0\" "
00304             << "x2=\"" << r2 << "\" y2=\"" << r2 << "\"/>\n";
00305           ostr << intab << "<line x1=\"" << r2 << "\" y1=\"0\" "
00306             << "x2=\"0\" y2=\"" << r2 << "\"/>\n";
00307           break;
00308       }
00309     } 
00310     else
00311     {
00312       //This should not be called as BasicShapes were removed...but left 
00313       // here as a placeholder if it ever comes back.
00314       //Todo:
00315       // * Make sure marker color works as necessary (useMarkerColor)
00316       // * Add some to the tab variable?
00317       // * Need to fix shape coordinates?
00318       //ostr << intab << markerShape << endl;
00319     } 
00320 
00321     ostr << marktab << "</marker>\n";
00322 
00323     ostr << tab << "</defs>\n";
00324 
00325     markerDefined = true;
00326     lastMarker = marker;
00327   }
00328 
00329 
00330 
00331   void SVGImage::rectangle (const Rectangle& rect)
00332   {
00333     using namespace std;
00334     
00335     StrokeStyle ss;
00336     StyleType tss = getCorrectStrokeStyle(&ss,rect);
00337     Color fc;
00338     StyleType tfc = getCorrectFillColor(&fc,rect);
00339 
00340     if((tss==CLEAR) && (tfc==CLEAR || tfc==NONE))
00341     {
00342         comment("invisible rectangle ignored...");
00343         return;
00344     }
00345 
00346     ostr << tab;
00347 
00348     // open rect tag and specify x,y coords of the top left corner and 
00349     // the width and height of the box
00350     ostr << "<rect x=\"" << rect.x1 << "\" y=\"" << rect.y1
00351       << "\" width=\"" << rect.x2-rect.x1 << "\" height=\"" << rect.y2-rect.y1 << "\"";
00352 
00353     string f =  fill(fc,tfc);
00354     string tmp = strokeDesc(ss,tss,((tfc!=NONE)&&(tfc!=CLEAR)));
00355     if(tmp.size()||f.size())
00356       ostr << " style=\"" << f << tmp << "\"";
00357 
00358     // Close xml tag  
00359     ostr << "/>" << endl;
00360   }
00361 
00362   void SVGImage::circle (const Circle& circle)
00363   {
00364     using namespace std;
00365 
00366     StrokeStyle ss;
00367     StyleType tss = getCorrectStrokeStyle(&ss,circle);
00368     Color fc;
00369     StyleType tfc = getCorrectFillColor(&fc,circle);
00370 
00371     if((tss==CLEAR) && (tfc==CLEAR || tfc==NONE))
00372     {
00373         comment("invisible circle ignored...");
00374         return;
00375     }
00376 
00377     ostr << tab;
00378 
00379     ostr << "<circle cx=\"" << circle.xc << "\" cy=\"" << circle.yc
00380       << "\" r=\"" << circle.radius << "\"";
00381 
00382     string f =  fill(fc,tfc);
00383     string tmp = strokeDesc(ss,tss,((tfc!=NONE)&&(tfc!=CLEAR)));
00384     if(tmp.size()||f.size())
00385       ostr << " style=\"" << f << tmp << "\"";
00386 
00387     // Close xml tag  
00388     ostr << "/>" << endl;
00389   }
00390 
00391   void SVGImage::polygon (const Polygon& polygon) 
00392   {
00393     using namespace std;
00394 
00395     StrokeStyle ss;
00396     StyleType tss = getCorrectStrokeStyle(&ss,polygon);
00397     Color fc;
00398     StyleType tfc = getCorrectFillColor(&fc,polygon);
00399 
00400     if((tss==CLEAR) && (tfc==CLEAR || tfc==NONE))
00401     {
00402         comment("invisible polygon ignored...");
00403         return;
00404     }
00405 
00406     ostr << tab;
00407 
00408     ostr << "<polygon ";
00409 
00410     string f =  fill(fc,tfc);
00411     string tmp = strokeDesc(ss,tss,((tfc!=NONE)&&(tfc!=CLEAR)));
00412     if(tmp.size()||f.size())
00413       ostr << " style=\"" << f << tmp << "\"";
00414 
00415     outputPoints(polygon, ostr);
00416 
00417     // Close xml tag  
00418     ostr << "/>" << endl;
00419   }
00420 
00421   void SVGImage::bitmap (const Bitmap& bitmap) 
00422   {
00423     using namespace std;
00424 
00425     if(bitmap.boxes)
00426     {
00427       comment("Bitmap Boxes");
00428       rectangles(bitmap);
00429     }
00430     else
00431     {
00432       comment("Bitmap Image");      
00433       ostr << tab << "<image"
00434         << " x=\"" << bitmap.x1 << "\""
00435         << " y=\"" << bitmap.y1 << "\""
00436         << " width=\"" << bitmap.x2-bitmap.x1 << "\""
00437         << " height=\"" << bitmap.y2-bitmap.y1 << "\""
00438         << " filter=\"none\"" << endl
00439         << tab << "  image-rendering=\"optimizeSpeed\" preserveAspectRatio=\"none\"" << endl
00440         << tab << "  xmlns:xlink=\"http://www.w3.org/1999/xlink\" xlink:show=\"embed\"" << endl;
00441 
00442       if(!ll)
00443         ostr << " transform=\"scale(1,-1) translate(0,-" << bitmap.y1+bitmap.y2 << ")\"" << endl;
00444 
00445       ostr << tab << "  xlink:href=\"data:image/png;base64,"; 
00446      
00447       ostr << Base64Encoder::encode(PNG::png(bitmap));
00448 
00449       ostr << "\"/>" << endl;
00450     }
00451   }
00452 
00453   void SVGImage::text (const Text& text) 
00454   {
00455     using namespace std;
00456 
00457     TextStyle ts;
00458     StyleType tts = getCorrectTextStyle(&ts,text);
00459 
00460     if( tts == CLEAR )
00461     {
00462       comment("Text with clear font ignored:%s",text.getString().c_str());
00463       return;
00464     }
00465 
00466     Color tc = ts.getColor();    
00467     int angle = text.getAngle();    
00468 
00469     ostr << tab;
00470 
00471     ostr << "<text x=\"" << text.x << "\" y=\"" << ((ll)?(canvasHeight-text.y):text.y) << "\"";
00472 
00473     //For the moment...to correct the transform
00474     if ( ll )
00475     {
00476       ostr << " transform=\"scale(1,-1) translate(0,-" << canvasHeight << ")";
00477       if ( angle )
00478         ostr << " rotate(" << setbase(10) << -angle << "," << text.x << "," 
00479           << ((ll)?(canvasHeight-text.y):text.y) << ")";
00480       ostr << "\""; 
00481     }
00482     else if ( angle )
00483       ostr << " transform=\"rotate(" << setbase(10) << -angle << "," << text.x << "," 
00484         << ((ll)?(canvasHeight-text.y):text.y) << ")\"";
00485 
00486     stringstream tmp;
00487 
00488     if(tc!=Color::BLACK)
00489     {
00490       tmp.fill('0');
00491       tmp << "fill:#" << hex << setw(6) << tc.getRGB() << ";";
00492     }
00493 
00494     if(ts.getPointSize()!=12)
00495       tmp << "font-size:" << ts.getPointSize() << "pt;";
00496 
00497     if(!ts.isMonospace())
00498       tmp << "font-family:" << (ts.isSerif()?"serif":(ts.isSansSerif()?"sans-serif":"")) << ";";
00499     //<< (ts.isMonospace()?"monospace":(ts.isSerif()?"serif":(ts.isSansSerif()?"sans-serif":""))) << ";"
00500 
00501     tmp << (ts.isBold()?"font-weight:bold;":"")
00502       << (ts.isItalic()?"font-style:italic;":"")
00503       << (ts.isUnderline()?"text-decoration:underline;":"")
00504       << (ts.isStrike()?"text-decoration:line-through;":"");
00505 
00506     if(!text.isLeft())
00507       tmp << "text-anchor:" << (text.isCenter()?"middle":"end") << ";";
00508     // << (text.isCenter()?"middle":(text.isLeft()?"start":"end")) << ";"
00509 
00510     string t = tmp.str();
00511     if(t.size()!=0)
00512       ostr << endl << tab << " style=\"" << t << "\"";
00513 
00514     // Close first xml tag  
00515     ostr << ">" << endl;
00516 
00517     ostr << tab << text.getString() << endl;
00518 
00519     ostr << tab <<"</text>" << endl;
00520    }
00521 
00522   void SVGImage::outputPoints(const Path& path, std::ostream& ostr)
00523   {
00524     using namespace std;
00525 
00526     // output each point
00527     ostr << tab << " points=";
00528     ostr << "\"";
00529     auto_ptr<Path> temp = path.asAbsolute();
00530     Path::const_iterator i;
00531     short count=1, width=5;
00532     for (i=temp->begin(); i!=temp->end(); i++, count++)
00533     {
00534       ostr << i->first << "," << i->second << " ";
00535       if (count == width )
00536       {
00537         count = 0;
00538         ostr << endl << tab << "         ";
00539       }
00540     }
00541     ostr << "\"";
00542   }
00543 
00544   void SVGImage::view(void) throw (VDrawException)
00545   {
00546 
00547     // close up the file's contents
00548     outputFooter();
00549 
00550     // First flush the file stream.
00551     ostr.flush();
00552 
00553     // Register viewers in case they haven't been registered.
00554     viewerManager.registerViewer("rsvg-view -b white");
00555     viewerManager.registerViewer("ksvg");
00556     viewerManager.registerViewer("inkscape");
00557     viewerManager.registerViewer("firefox");
00558 
00559     // Use the viewerManager
00560     viewerManager.view(filename);
00561 
00562     return;
00563   }
00564 
00565   std::string SVGImage::fill (Color& fc, StyleType tfc) const
00566   {
00567     if( tfc == NONE || tfc == CLEAR )
00568       return "";
00569     else
00570       return convertFillColor( fc );
00571   }
00572 
00573   std::string SVGImage::convertFillColor (const Color& color) const
00574   {
00575     using namespace std;
00576     stringstream ostr(stringstream::in | stringstream::out); 
00577 
00578     ostr.fill('0');
00579     ostr << "fill:#" << hex << noshowbase << setw(6) << color.getRGB() << ";";
00580     ostr.fill(' ');
00581 
00582     return string(ostr.str());
00583   }
00584 
00585   std::string SVGImage::strokeDesc (StrokeStyle& ss, StyleType tss, bool noneclear) const
00586   {
00587     if( tss == NONE )
00588     {
00589       if( noneclear )
00590         return "stroke-opacity:0;";        
00591       else
00592         return "";
00593     }
00594     else if( tss == CLEAR )
00595       return "stroke-opacity:0;";
00596     else
00597       return convertStrokeStyle(ss);
00598   }
00599 
00600   std::string SVGImage::convertStrokeStyle (const StrokeStyle& ss) const
00601   {
00602     using namespace std;
00603     stringstream ostr(stringstream::in | stringstream::out); 
00604 
00605     if(ss.getColor().isClear())
00606     {
00607       ostr << "stroke-opacity:0;";
00608       return string(ostr.str());        
00609     }
00610 
00611     // Color
00612     if(ss.getColor()!=Color::BLACK)
00613     {
00614       ostr << "stroke:rgb(";
00615       short red, blue, green;
00616       ss.getColor().getRGBTriplet(red, blue, green);
00617       ostr << red << "," << blue << "," << green;
00618       ostr << ");";
00619     }
00620 
00621     // Width
00622     if(ss.getWidth()!=1)
00623       ostr << "stroke-width:" << ss.getWidth() << "pt;";
00624 
00625     if (!ss.getSolid())
00626     {
00627       // Dash array list: comma separated variables
00628       ostr << "stroke-dasharray:";
00629 
00630       StrokeStyle::dashLengthList dll=ss.getDashList();
00631       StrokeStyle::dashLengthList::iterator i, inext;
00632 
00633       for (i=dll.begin(); i!=dll.end(); i++)
00634       {
00635         ostr << (i==dll.begin()?"":",") << *i;
00636         inext = i; inext++; 
00637         if (inext!=dll.end())
00638           cout << ",";
00639       }
00640       // Final delimiter
00641       ostr << ";";   
00642     }
00643 
00644     return string(ostr.str());
00645   }
00646 
00647 
00648 } // namespace vdraw

Generated on Tue Jun 18 03:31:10 2013 for GPS ToolKit Software Library by  doxygen 1.3.9.1