1 /********************************************************************** 2 svgpainter.cpp - Implementation of rendering in SVG 3 4 Copyright (C) 2009 by Chris Morley 5 6 This file is part of the Open Babel project. 7 For more information, see <http://openbabel.org/> 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation version 2 of the License. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 ***********************************************************************/ 18 #include <openbabel/depict/svgpainter.h> 19 20 #include <iostream> 21 #include <iomanip> 22 #include <sstream> 23 #include <math.h> 24 using namespace std; 25 26 #if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 27 #include <float.h> 28 #define isfinite _finite 29 #endif 30 31 namespace OpenBabel 32 { 33 SVGPainter(ostream & ofs,std::set<ColorGradient> * gradients,bool withViewBox,double width,double height)34 SVGPainter::SVGPainter(ostream& ofs, std::set<ColorGradient>* gradients, bool withViewBox, 35 double width, double height) 36 : m_ofs(ofs), m_withViewBox(withViewBox), m_width(width), m_height(height), 37 m_Pencolor("black"), m_Fillcolor("white"), m_Gradientcolor(make_pair(OBColor("white"),OBColor("white"))), m_PenWidth(1), 38 m_fontPointSize(16), m_isFillcolor(true), m_Gradients(gradients) {} 39 ~SVGPainter()40 SVGPainter::~SVGPainter() 41 { 42 43 } 44 NewCanvas(double width,double height)45 void SVGPainter::NewCanvas(double width, double height) 46 { 47 if(m_withViewBox) 48 m_ofs << "<svg width=\"" << m_width << "\" height=\"" << m_height << "\" " 49 << "x=\"0\" y=\"0\" " 50 << "viewBox=\"0 0 " << width << ' ' << height << "\"\n"; 51 else 52 m_ofs << "<svg width=\"" << width << "\" height=\"" << height << "\" " 53 << "x=\"0\" y=\"0\" "; 54 55 //Bond color and width are the initial m_Pencolor and m_PenWidth 56 m_ofs << "font-family=\"" << m_fontFamily << "\" stroke=" << MakeRGB(m_Pencolor) 57 << "stroke-width=\"" << m_PenWidth << "\" stroke-linecap=\"round\"" << ">\n"; 58 59 if(!m_withViewBox && m_Fillcolor.alpha!=0.0)//Background color for single molecule. Handled by outer svg when table. 60 m_ofs << "<rect x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\" stroke-width=\"0\" fill=" 61 << MakeRGB(m_Fillcolor) << " />\n"; 62 m_OrigBondcolor = m_Pencolor; 63 } 64 EndCanvas()65 void SVGPainter::EndCanvas() 66 { 67 m_ofs << "</svg>\n"; 68 } IsGood() const69 bool SVGPainter::IsGood() const 70 { 71 return true; 72 } 73 SetFontSize(int pointSize)74 void SVGPainter::SetFontSize(int pointSize) 75 { 76 m_fontPointSize = pointSize; 77 } 78 SetFontFamily(const std::string & fontFamily)79 void SVGPainter::SetFontFamily(const std::string &fontFamily) 80 { 81 m_fontFamily = fontFamily; 82 } 83 SetFillColor(const OBColor & color)84 void SVGPainter::SetFillColor(const OBColor &color) 85 { 86 m_Fillcolor = color; //value when NewCanvas called used for background 87 m_isFillcolor = true; 88 } 89 SetFillRadial(const OBColor & start,const OBColor & end)90 void SVGPainter::SetFillRadial(const OBColor &start, const OBColor &end) 91 { 92 m_Gradientcolor = make_pair(start,end); 93 m_Gradients->insert(m_Gradientcolor); 94 m_isFillcolor = false; 95 } 96 SetPenColor(const OBColor & color)97 void SVGPainter::SetPenColor(const OBColor &color) 98 { 99 m_Pencolor = color; //value when NewCanvas called used for bonds 100 } 101 SetPenWidth(double width)102 void SVGPainter::SetPenWidth(double width) 103 { 104 m_PenWidth = width; //value when NewCanvas called used for bonds 105 } 106 GetPenWidth()107 double SVGPainter::GetPenWidth() 108 { 109 return m_PenWidth; 110 } 111 DrawLine(double x1,double y1,double x2,double y2,const std::vector<double> & dashes)112 void SVGPainter::DrawLine(double x1, double y1, double x2, double y2, const std::vector<double>& dashes) 113 { 114 streamsize oldprec = m_ofs.precision(1); 115 m_ofs << fixed << "<line x1=\"" << x1 << "\" y1=\"" << y1 << "\" x2=\"" 116 << x2 << "\" y2=\"" << y2 << "\""; 117 m_ofs << " opacity=\"" << m_Pencolor.alpha << "\""; 118 // if(m_Pencolor!=m_OrigBondcolor) // TODO: Bring this line back once Pybel is fine with this 119 m_ofs << " stroke=" << MakeRGB(m_Pencolor); 120 m_ofs << " stroke-width=\"" << m_PenWidth << "\""; 121 if (!dashes.empty()) { 122 std::vector<double>::const_iterator it = dashes.begin(); 123 m_ofs << " stroke-dasharray=\"" << *it; 124 for (; it!=dashes.end() ; ++it) 125 m_ofs << "," << *it; 126 m_ofs << "\""; 127 128 } 129 m_ofs << "/>\n"; 130 m_ofs.precision(oldprec); 131 } 132 DrawPolygon(const std::vector<std::pair<double,double>> & points)133 void SVGPainter::DrawPolygon(const std::vector<std::pair<double,double> > &points) 134 { 135 m_ofs << "<polygon points=\""; 136 std::vector<std::pair<double,double> >::const_iterator i; 137 for (i = points.begin(); i != points.end(); ++i) 138 m_ofs << i->first << ' ' << i->second << ' '; 139 m_ofs << "\""; 140 m_ofs << " stroke-width=\"" << m_PenWidth << "\""; 141 m_ofs << " fill=" << MakeRGB(m_Pencolor); 142 m_ofs << " stroke=" << MakeRGB(m_Pencolor); 143 m_ofs << "/>\n"; 144 } 145 DrawCircle(double x,double y,double r)146 void SVGPainter::DrawCircle(double x, double y, double r) 147 { 148 m_ofs << "<circle cx=\"" << x << "\" y=\"" << y << "\" r=\"" << r << "\" />\n"; 149 } 150 DrawText(double x,double y,const std::string & text)151 void SVGPainter::DrawText(double x, double y, const std::string &text) 152 { 153 m_ofs << "<text x=\"" << x << "\" y=\"" << y << "\"" 154 << " fill=" << MakeRGB(m_Pencolor) << "stroke-width=\"0\" font-weight=\"bold\" " 155 << "font-size=\"" << m_fontPointSize << "\" >" 156 << text << "</text>\n"; 157 } 158 GetFontMetrics(const std::string & text)159 OBFontMetrics SVGPainter::GetFontMetrics(const std::string &text) 160 { 161 OBFontMetrics metrics; 162 metrics.fontSize = m_fontPointSize; 163 metrics.ascent = m_fontPointSize; 164 metrics.descent = m_fontPointSize * -0.23; // Offset from baseline of bottom of text 165 metrics.height = m_fontPointSize * 1.26; // Distance between successive lines of text 166 metrics.width = 0.0; 167 for(string::size_type i=0;i<text.size();++i) 168 metrics.width += m_fontPointSize * (isalpha(text[i]) ? 0.75 : 0.5); 169 170 return metrics; 171 } 172 WriteImage(const std::string & filename)173 void SVGPainter::WriteImage(const std::string &filename) 174 { 175 } 176 DrawBall(double x,double y,double r,double opacity)177 void SVGPainter::DrawBall(double x, double y, double r, double opacity) 178 { 179 if (!isfinite(opacity)) 180 opacity = 1.0; 181 if (opacity < 0.2) 182 opacity = 0.2; 183 184 m_ofs << "<circle cx=\"" << x << "\" cy=\"" << y << "\" r=\"" << r << "\" "; 185 m_ofs << "opacity=\"" << opacity << "\" "; 186 if (m_isFillcolor) { 187 m_ofs << "style=\"stroke:black;fill:" << MakeRGB(m_Fillcolor) << "\"/>\n"; 188 } else { 189 m_ofs << "style=\"stroke:black;stroke-width:0.5;fill:url(#radial"; 190 m_ofs << RGBcode(m_Gradientcolor.first)<< RGBcode(m_Gradientcolor.second) << ")\"/>\n"; 191 } 192 } 193 WriteDefs()194 void SVGPainter::WriteDefs() 195 { 196 if (!m_Gradients->empty()) { 197 m_ofs << "<defs>\n"; 198 for (std::set<ColorGradient>::iterator it=m_Gradients->begin(); it!=m_Gradients->end(); ++it) { 199 m_ofs << "<radialGradient id='radial"; 200 m_ofs << RGBcode(it->first)<< RGBcode(it->second) << "' "; 201 m_ofs << "cx='50%' cy='50%' r='50%' fx='30%' fy='30%'>\n"; 202 m_ofs << " <stop offset=' 0%' stop-color=" << MakeRGB(it->first) << " stop-opacity='1.0'/>\n"; 203 m_ofs << " <stop offset='100%' stop-color=" << MakeRGB(it->second) << " stop-opacity ='1.0'/>\n"; 204 m_ofs << "</radialGradient>\n"; 205 } 206 m_ofs << "</defs>\n"; 207 } 208 } 209 MakeRGB(OBColor color)210 string SVGPainter::MakeRGB(OBColor color) 211 { 212 stringstream ss; 213 ss << "\"rgb(" << (int)(255*color.red) << ',' << (int)(255*color.green) 214 << ',' << (int)(255*color.blue) << ")\" "; 215 return ss.str(); 216 } 217 RGBcode(OBColor color)218 string SVGPainter::RGBcode(OBColor color) 219 { 220 stringstream ss; 221 ss << std::hex << std::setfill('0') << std::setw(2) << (int)(255*color.red) << (int)(255*color.green) 222 << (int)(255*color.blue); 223 return ss.str(); 224 225 } 226 } 227