#include "DocSVG.h"
#include "Util.h"
const char SVGOperation::PathOperationChar[] =
{
'M', 'L', 'H', 'V', 'C', 'S', 'Q', 'T', 'A', 'Z',
'm', 'l', 'h', 'v', 'c', 's', 'q', 't', 'a', 'z', ' ', ' ', ' '
};
const int SVGOperation::PathOperationArgs[] =
{
2, 2, 1, 1, 6, 4, 4, 2, 7, 0,
2, 2, 1, 1, 6, 4, 4, 2, 7, 0, 0, 0, 0
};
DocSVG::DocSVG(double a_scale) : m_scale(a_scale)
{
currentGradient = nullptr;
}
DocSVG::~DocSVG()
{
}
/// Visit a document.
bool DocSVG::VisitEnter( const TiXmlDocument& /* doc */ )
{
return true;
}
/// Visit a document.
bool DocSVG::VisitExit( const TiXmlDocument& /* doc */ )
{
return true;
}
/// Visit a declaration
bool DocSVG::Visit( const TiXmlDeclaration& /* declaration */ )
{
return true;
}
/// Visit a stylesheet reference
bool DocSVG::Visit( const TiXmlStylesheetReference& /* stylesheet */ )
{
return true;
}
/// Visit a comment node
bool DocSVG::Visit( const TiXmlComment& /* comment */ )
{
return true;
}
/// Visit an unknown node
bool DocSVG::Visit( const TiXmlUnknown& /* unknown */ )
{
return true;
}
// static
DocSVG::SVGStyle DocSVG::parseStyle(const char* a_str)
{
SVGStyle style = { 0.0, 0.0, 0, "", 0.0, 0, 0.0, 0, 0 };
std::vector<std::string> list = split(a_str, ';');
for (size_t i = 0; i < list.size(); ++i)
{
std::vector<std::string> kvp = split(list[i], ':');
std::string s = kvp[0];
if (s == "opacity") style.opacity = str2float(kvp[1]);
else if (s == "fill-opacity") style.fillOpacity = str2float(kvp[1]);
else if (s == "fill") style.fillString = kvp[1], style.fillColor = parseColor(kvp[1].c_str());
else if (s == "stroke-opacity") style.strokeOpacity = str2float(kvp[1]);
else if (s == "stroke") style.strokeColor = parseColor(kvp[1].c_str());
else if (s == "stroke-width") style.strokeWidth = str2float(kvp[1]);
else if (s == "stroke-linecap") style.strokeLineCap = parseLineCap(kvp[1]);
else if (s == "stroke-linejoin") style.strokeLineJoin = parseLineJoin(kvp[1]);
}
return style;
}
// static
uint32_t DocSVG::parseLineCap(const std::string& s)
{
if (s == "butt") return 0;
else if (s == "round") return 1;
else if (s == "square") return 2;
printf("Unknown line-cap: %s\n", s.c_str());
return -1;
}
// static
uint32_t DocSVG::parseLineJoin(const std::string& s)
{
if (s == "round") return 0;
else if (s == "bevel") return 1;
else if (s == "miter") return 2;
printf("Unknown line-join: %s\n", s.c_str());
return -1;
}
// static
uint32_t DocSVG::parseColor(const char* a_str)
{
std::string s = a_str;
if (s == "red") return 0xffff0000;
else if (s == "blue") return 0xff0000ff;
else if (s == "green") return 0xff00ff00;
else if (s == "yellow") return 0xffffff00;
else if (s == "purple") return 0xffe00fe0;
else if (s == "cyan") return 0xff00ffff;
else if (s == "magenta") return 0xffff00ff;
else if (s == "black") return 0xff000000;
else if (s == "white") return 0xffffffff;
else if (s == "none") return 0;
return str2col(s);
}
/// Visit an element.
bool DocSVG::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute )
{
// Inkscape:
// style="fill:#729fcf;stroke:none"
// Spec:
// fill="yellow" stroke="blue" stroke-width="5"
SVGPath path;
if (strcmp(element.ValueTStr().c_str(), "stop") == 0) {
if (currentGradient != nullptr)
{
SVGColorStop stop;
const TiXmlAttribute* attrib = firstAttribute;
while ( attrib ) {
if (strcmp(attrib->Name(), "offset") == 0) {
stop.offset = str2float(attrib->Value());
} else if (strcmp(attrib->Name(), "style") == 0) {
std::vector<std::string> list = split(attrib->Value(), ';');
for (size_t i = 0; i < list.size(); ++i)
{
std::vector<std::string> kvp = split(list[i], ':');
std::string s = kvp[0];
if (s == "stop-color") stop.color = parseColor(kvp[1].c_str());
else if (s == "stop-opacity") stop.opacity = str2float(kvp[1]);
}
}
attrib = attrib->Next();
}
currentGradient->stops.push_back(stop);
}
}
if (strcmp(element.ValueTStr().c_str(), "linearGradient") == 0) {
SVGGradient gradient;
const TiXmlAttribute* attrib = firstAttribute;
while ( attrib ) {
if (strcmp(attrib->Name(), "id") == 0) {
gradient.name = attrib->Value();
} else if (strcmp(attrib->Name(), "xlink:href") == 0) {
gradient.stops = gradients[attrib->Value() + 1].stops;
} else if (strcmp(attrib->Name(), "x1") == 0) {
gradient.x1 = str2float(attrib->Value());
} else if (strcmp(attrib->Name(), "y1") == 0) {
gradient.y1 = str2float(attrib->Value());
} else if (strcmp(attrib->Name(), "x2") == 0) {
gradient.x2 = str2float(attrib->Value());
} else if (strcmp(attrib->Name(), "y2") == 0) {
gradient.y2 = str2float(attrib->Value());
}
attrib = attrib->Next();
}
gradients[gradient.name] = gradient;
currentGradient = &gradients[gradient.name];
}
if (strcmp(element.ValueTStr().c_str(), "path") == 0) {
const TiXmlAttribute* attrib = firstAttribute;
while ( attrib ) {
if (strcmp(attrib->Name(), "d") == 0) {
path.operations = ParsePath(attrib->Value());
} else if (strcmp(attrib->Name(), "style") == 0) {
path.style = parseStyle(attrib->Value());
} else if (strcmp(attrib->Name(), "fill") == 0) {
path.style.fillColor = parseColor(attrib->Value());
} else if (strcmp(attrib->Name(), "stroke") == 0) {
path.style.strokeColor = parseColor(attrib->Value());
} else if (strcmp(attrib->Name(), "stroke-width") == 0) {
path.style.strokeWidth = str2float(attrib->Value());
}
attrib = attrib->Next();
}
}
if (strcmp(element.ValueTStr().c_str(), "rect") == 0) {
const TiXmlAttribute* attrib = firstAttribute;
float x, y, w, h;
while ( attrib ) {
if (strcmp(attrib->Name(), "x") == 0) {
x = str2float(attrib->Value());
} else if (strcmp(attrib->Name(), "y") == 0) {
y = str2float(attrib->Value());
} else if (strcmp(attrib->Name(), "width") == 0) {
w = str2float(attrib->Value());
} else if (strcmp(attrib->Name(), "height") == 0) {
h = str2float(attrib->Value());
} else if (strcmp(attrib->Name(), "style") == 0) {
path.style = parseStyle(attrib->Value());
} else if (strcmp(attrib->Name(), "fill") == 0) {
path.style.fillColor = parseColor(attrib->Value());
} else if (strcmp(attrib->Name(), "stroke") == 0) {
path.style.strokeColor = parseColor(attrib->Value());
} else if (strcmp(attrib->Name(), "stroke-width") == 0) {
path.style.strokeWidth = str2float(attrib->Value());
}
attrib = attrib->Next();
}
SVGOperation currentOp;
currentOp.m_type = SVGOperation::BeginPathOperation;
path.operations.push_back(currentOp);
currentOp.m_type = SVGOperation::MoveToAbs;
currentOp.m_values[0] = x;
currentOp.m_values[1] = y;
path.operations.push_back(currentOp);
currentOp.m_type = SVGOperation::HorizontalLineToRel;
currentOp.m_values[0] = w;
path.operations.push_back(currentOp);
currentOp.m_type = SVGOperation::VerticalLineToRel;
currentOp.m_values[0] = h;
path.operations.push_back(currentOp);
currentOp.m_type = SVGOperation::HorizontalLineToRel;
currentOp.m_values[0] = -w;
path.operations.push_back(currentOp);
currentOp.m_type = SVGOperation::ClosePath;
path.operations.push_back(currentOp);
currentOp.m_type = SVGOperation::EndPathOperation;
path.operations.push_back(currentOp);
}
paths.push_back(path);
return true;
}
/// Visit an element.
bool DocSVG::VisitExit( const TiXmlElement& /* element */ )
{
return true;
}
/// Visit a text node
bool DocSVG::Visit( const TiXmlText& /* text */ )
{
return true;
}