00001 #pragma ident "$Id: SVGImage.cpp 3140 2012-06-18 15:03:02Z susancummins $"
00002
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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
00098
00099 if ( ll )
00100 {
00101 ostr << endl << " <g transform=\"translate(0," << canvasHeight;
00102 ostr << ") scale(1,-1)\">" << endl;
00103
00104 tab=string(" ");
00105 }
00106 else
00107 tab=string(" ");
00108 }
00109
00110 void SVGImage::outputFooter(void)
00111 {
00112 using namespace std;
00113
00114
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
00179
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
00209
00210
00211 ostr << "/>" << endl;
00212 }
00213 else
00214 {
00215
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
00236 ostr << "/>" << endl;
00237 }
00238 }
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
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
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
00283 ostr.fill('0');
00284 ostr << "stroke-width=\".2pt\" " << "fill=\"none\">\n";
00285
00286
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
00313
00314
00315
00316
00317
00318
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
00349
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
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
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
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
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
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
00509
00510 string t = tmp.str();
00511 if(t.size()!=0)
00512 ostr << endl << tab << " style=\"" << t << "\"";
00513
00514
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
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
00548 outputFooter();
00549
00550
00551 ostr.flush();
00552
00553
00554 viewerManager.registerViewer("rsvg-view -b white");
00555 viewerManager.registerViewer("ksvg");
00556 viewerManager.registerViewer("inkscape");
00557 viewerManager.registerViewer("firefox");
00558
00559
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
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
00622 if(ss.getWidth()!=1)
00623 ostr << "stroke-width:" << ss.getWidth() << "pt;";
00624
00625 if (!ss.getSolid())
00626 {
00627
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
00641 ostr << ";";
00642 }
00643
00644 return string(ostr.str());
00645 }
00646
00647
00648 }