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