From 208cee1f6967db42329a3e665401ef51b3f6d9ff Mon Sep 17 00:00:00 2001 From: Sam Moore Date: Tue, 12 Aug 2014 14:08:00 +0800 Subject: [PATCH] Deal with groups in SVG parsing ParseSVGNode will recursively parse , and nodes. Basically just going to try and implement a bunch more of SVG now. The "transform" attribute would be a useful one to deal with because svg editors (at least, inkscape) seem to be rather liberal in using it. I hope the timestamp for this is correct, I ran git commit --amend but I don't know if that will fix it. --- src/document.cpp | 113 +++++++++++++++++++++++++---------------------- src/document.h | 18 +++++++- 2 files changed, 75 insertions(+), 56 deletions(-) diff --git a/src/document.cpp b/src/document.cpp index a6c6fad..8f35644 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -3,6 +3,8 @@ #include #include +#include "../contrib/pugixml-1.4/src/pugixml.cpp" + #include "stb_truetype.h" using namespace IPDF; @@ -260,53 +262,50 @@ bool Document::operator==(const Document & equ) const } -#include "../contrib/pugixml-1.4/src/pugixml.hpp" -#include "../contrib/pugixml-1.4/src/pugixml.cpp" -/** - * Load an SVG into a rectangle - */ -void Document::LoadSVG(const string & filename, const Rect & bounds) -{ - using namespace pugi; - - xml_document doc_xml; - ifstream input(filename.c_str(), ios_base::in); - xml_parse_result result = doc_xml.load(input); - - if (!result) - Fatal("Couldn't load \"%s\" - %s", filename.c_str(), result.description()); - - Debug("Loaded XML - %s", result.description()); - - input.close(); - // Combine all SVG tags into one thing because lazy - for (xml_node svg = doc_xml.child("svg"); svg; svg = svg.next_sibling("svg")) +void Document::ParseSVGNode(pugi::xml_node & root, const Rect & bounds, Real & width, Real & height) +{ + Debug("Parse node <%s>", root.name()); + pugi::xml_attribute attrib_w = root.attribute("width"); + pugi::xml_attribute attrib_h = root.attribute("height"); + if (!attrib_w.empty()) + width = attrib_w.as_float() * bounds.w; + if (!attrib_h.empty()) + height = attrib_h.as_float() * bounds.h; + + for (pugi::xml_node child = root.first_child(); child; child = child.next_sibling()) { - Real width = Real(svg.attribute("width").as_float()) * bounds.w; - Real height = Real(svg.attribute("width").as_float()) * bounds.h; - + - // Rectangles - Real coords[4]; - const char * attrib_names[] = {"x", "y", "width", "height"}; - for (pugi::xml_node rect = svg.child("rect"); rect; rect = rect.next_sibling("rect")) + if (strcmp(child.name(), "svg") == 0 || strcmp(child.name(),"g") == 0 + || strcmp(child.name(), "group") == 0) + { + //TODO: Handle translates etc here + ParseSVGNode(child, bounds, width, height); + continue; + } + else if (strcmp(child.name(), "path") == 0) + { + string d = child.attribute("d").as_string(); + Debug("Path data attribute is \"%s\"", d.c_str()); + ParseSVGPathData(d, Rect(bounds.x,bounds.y,width,height)); + } + else if (strcmp(child.name(), "rect") == 0) { + Real coords[4]; + const char * attrib_names[] = {"x", "y", "width", "height"}; for (size_t i = 0; i < 4; ++i) - coords[i] = rect.attribute(attrib_names[i]).as_float(); + coords[i] = child.attribute(attrib_names[i]).as_float(); - bool outline = !(rect.attribute("fill")); + bool outline = !(child.attribute("fill")); Add(outline?RECT_OUTLINE:RECT_FILLED, Rect(coords[0]/width + bounds.x, coords[1]/height + bounds.y, coords[2]/width, coords[3]/height),0); - Debug("Added rectangle"); - } - - // Circles - for (pugi::xml_node circle = svg.child("circle"); circle; circle = circle.next_sibling("circle")) + } + else if (strcmp(child.name(), "circle") == 0) { - Real cx = circle.attribute("cx").as_float(); - Real cy = circle.attribute("cy").as_float(); - Real r = circle.attribute("r").as_float(); + Real cx = child.attribute("cx").as_float(); + Real cy = child.attribute("cy").as_float(); + Real r = child.attribute("r").as_float(); Real x = (cx - r)/width + bounds.x; Real y = (cy - r)/height + bounds.y; @@ -315,25 +314,31 @@ void Document::LoadSVG(const string & filename, const Rect & bounds) Rect rect(x,y,w,h); Add(CIRCLE_FILLED, rect,0); - Debug("Added Circle %s", rect.Str().c_str()); - - } - - // paths - for (pugi::xml_node path = svg.child("path"); path; path = path.next_sibling("path")) - { - - string d = path.attribute("d").as_string(); - Debug("Path data attribute is \"%s\"", d.c_str()); - AddPathFromString(d, Rect(bounds.x,bounds.y,width,height)); - + Debug("Added Circle %s", rect.Str().c_str()); } } +} + +/** + * Load an SVG into a rectangle + */ +void Document::LoadSVG(const string & filename, const Rect & bounds) +{ + using namespace pugi; - //Fatal("Done"); + xml_document doc_xml; + ifstream input(filename.c_str(), ios_base::in); + xml_parse_result result = doc_xml.load(input); + if (!result) + Fatal("Couldn't load \"%s\" - %s", filename.c_str(), result.description()); + + Debug("Loaded XML - %s", result.description()); - + input.close(); + Real width(1); + Real height(1); + ParseSVGNode(doc_xml, bounds,width,height); } // Behold my amazing tokenizing abilities @@ -357,14 +362,14 @@ static string & GetToken(const string & d, string & token, unsigned & i) } token += d[i++]; } - Debug("Got token \"%s\"", token.c_str()); + //Debug("Got token \"%s\"", token.c_str()); return token; } // Fear the wrath of the tokenizing svg data // Seriously this isn't really very DOM-like at all is it? -void Document::AddPathFromString(const string & d, const Rect & bounds) +void Document::ParseSVGPathData(const string & d, const Rect & bounds) { Real x[4] = {0,0,0,0}; Real y[4] = {0,0,0,0}; diff --git a/src/document.h b/src/document.h index 62f624c..8e63f5b 100644 --- a/src/document.h +++ b/src/document.h @@ -4,6 +4,9 @@ #include "ipdf.h" #include "quadtree.h" +#include "../contrib/pugixml-1.4/src/pugixml.hpp" + + typedef struct stbtt_fontinfo stbtt_fontinfo; namespace IPDF @@ -14,7 +17,7 @@ namespace IPDF Document(const std::string & filename = "") : m_objects(), m_count(0) {Load(filename);} virtual ~Document() {} - void LoadSVG(const std::string & filename, const Rect & bounds = Rect(0,0,1,1)); + void Load(const std::string & filename = ""); void Save(const std::string & filename); @@ -29,7 +32,18 @@ namespace IPDF void Add(ObjectType type, const Rect & bounds, unsigned data_index = 0); unsigned AddBezierData(const Bezier & bezier); - void AddPathFromString(const std::string & d, const Rect & bounds); + + + + /** SVG Related functions **/ + + /** Load an SVG text file and add to the document **/ + void LoadSVG(const std::string & filename, const Rect & bounds = Rect(0,0,1,1)); + + /** Parse an SVG node or SVG-group node, adding children to the document **/ + void ParseSVGNode(pugi::xml_node & root, const Rect & bounds, Real & width, Real & height); + /** Parse an SVG path with string **/ + void ParseSVGPathData(const std::string & d, const Rect & bounds); void AddFontGlyphAtPoint(stbtt_fontinfo *font, int character, Real scale, Real x, Real y); -- 2.20.1