1 /*
2  * The MIT License
3  *
4  * Copyright (c) 2016-2018, Torsten Paul <torsten.paul@gmx.de>,
5  *                          Marius Kintel <marius@kintel.net>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 #include <map>
26 #include <stack>
27 #include <vector>
28 #include <Eigen/Core>
29 #include <Eigen/Geometry>
30 #include <boost/filesystem.hpp>
31 #include <boost/format.hpp>
32 #include <libxml/xmlreader.h>
33 
34 #include "libsvg.h"
35 
36 #include "shape.h"
37 #include "circle.h"
38 #include "ellipse.h"
39 #include "line.h"
40 #include "polygon.h"
41 #include "polyline.h"
42 #include "rect.h"
43 
44 namespace fs = boost::filesystem;
45 
46 namespace libsvg {
47 
48 #define SVG_DEBUG 0
49 
50 static bool in_defs = false;
51 static shapes_list_t stack;
52 static shapes_list_t *shape_list;
53 
54 #if SVG_DEBUG
dump_stack()55 static std::string dump_stack() {
56 	bool first = true;
57 	std::stringstream s;
58 	s << "[";
59 	for (const auto &shape : stack) {
60 		s << (first ? "" : "|") << shape->get_name();
61 		first = false;
62 	}
63 	return s.str() + "]";
64 }
65 #endif
66 
read_attributes(xmlTextReaderPtr reader)67 attr_map_t read_attributes(xmlTextReaderPtr reader)
68 {
69 	attr_map_t attrs;
70 	int attr_count = xmlTextReaderAttributeCount(reader);
71 	for (int idx = 0; idx < attr_count; ++idx) {
72 		xmlTextReaderMoveToAttributeNo(reader, idx);
73 		const char *name = reinterpret_cast<const char *> (xmlTextReaderName(reader));
74 		const char *value = reinterpret_cast<const char *> (xmlTextReaderValue(reader));
75 		attrs[name] = value;
76 	}
77 	return attrs;
78 }
79 
processNode(xmlTextReaderPtr reader)80 void processNode(xmlTextReaderPtr reader)
81 {
82 	const char *name = reinterpret_cast<const char *> (xmlTextReaderName(reader));
83 	if (name == nullptr) name = reinterpret_cast<const char *> (xmlStrdup(BAD_CAST "--"));
84 
85 	bool isEmpty;
86 	xmlChar *value = xmlTextReaderValue(reader);
87 	int node_type = xmlTextReaderNodeType(reader);
88 	switch (node_type) {
89 	case XML_READER_TYPE_ELEMENT:
90 		isEmpty = xmlTextReaderIsEmptyElement(reader);
91 	{
92 #if SVG_DEBUG
93 		printf("XML_READER_TYPE_ELEMENT (%s %s): %d %d %s\n",
94 			dump_stack().c_str(), name,
95 			xmlTextReaderDepth(reader),
96 			xmlTextReaderNodeType(reader),
97 			value);
98 #endif
99 
100 		if (std::string("defs") == name) {
101 			in_defs = true;
102 		}
103 
104 		auto s = shared_ptr<shape>(shape::create_from_name(name));
105 		if (!in_defs && s) {
106 			attr_map_t attrs = read_attributes(reader);
107 			s->set_attrs(attrs);
108 			shape_list->push_back(s);
109 			if (!stack.empty()) {
110 				stack.back()->add_child(s.get());
111 			}
112 			if (s->is_container()) {
113 				stack.push_back(s);
114 			}
115 			s->apply_transform();
116 		}
117 	}
118 	if (!isEmpty) {
119 		break;
120 	}
121 	/* fall through */
122 	case XML_READER_TYPE_END_ELEMENT:
123 	{
124 		if (std::string("defs") == name) {
125 			in_defs = false;
126 		}
127 		if (in_defs) {
128 			return;
129 		}
130 
131 		if (std::string("g") == name) {
132 			stack.pop_back();
133 		} else if (std::string("tspan") == name) {
134 			stack.pop_back();
135 		} else if (std::string("text") == name) {
136 			stack.pop_back();
137 		}
138 #if SVG_DEBUG
139 		printf("XML_READER_TYPE_END_ELEMENT (%s %s): %d %d %s\n",
140 			dump_stack().c_str(), name,
141 			xmlTextReaderDepth(reader),
142 			xmlTextReaderNodeType(reader),
143 			value);
144 #endif
145 	}
146 		break;
147 	case XML_READER_TYPE_TEXT:
148 	{
149 		attr_map_t attrs;
150 		attrs["text"] = reinterpret_cast<const char *>(value);
151 		auto s = shared_ptr<shape>(shape::create_from_name("data"));
152 		s->set_attrs(attrs);
153 		shape_list->push_back(s);
154 		if (!stack.empty()) {
155 			stack.back()->add_child(s.get());
156 		}
157 	}
158 		break;
159 	}
160 
161 	xmlFree(value);
162 	xmlFree((void *) (name));
163 }
164 
streamFile(const char * filename)165 int streamFile(const char *filename)
166 {
167 	xmlTextReaderPtr reader;
168 
169 	in_defs = false;
170 	reader = xmlNewTextReaderFilename(filename);
171 	xmlTextReaderSetParserProp(reader, XML_PARSER_SUBST_ENTITIES, 1);
172 	if (reader != nullptr) {
173 		int ret = xmlTextReaderRead(reader);
174 		while (ret == 1) {
175 			processNode(reader);
176 			ret = xmlTextReaderRead(reader);
177 		}
178 		xmlFreeTextReader(reader);
179 		if (ret != 0) {
180 			throw SvgException((boost::format("Error parsing file '%1%'") % filename).str());
181 		}
182 	} else {
183 		throw SvgException((boost::format("Can't open file '%1%'") % filename).str());
184 	}
185 	return 0;
186 }
187 
dump(int idx,shape * s)188 void dump(int idx, shape *s) {
189 	for (int a = 0; a < idx; ++a) {
190 		std::cout << "  ";
191 	}
192 	std::cout << "=> " << s->dump() << std::endl;
193 	for (const auto& c : s->get_children()) {
194 		dump(idx + 1, c);
195 	}
196 }
197 
198 shapes_list_t *
libsvg_read_file(const char * filename)199 libsvg_read_file(const char *filename)
200 {
201 	shape_list = new shapes_list_t();
202 	streamFile(filename);
203 
204 //#ifdef DEBUG
205 //	if (!shape_list->empty()) {
206 //		dump(0, shape_list->front().get());
207 //	}
208 //#endif
209 
210 	return shape_list;
211 }
212 
213 void
libsvg_free(shapes_list_t * shapes)214 libsvg_free(shapes_list_t *shapes)
215 {
216 	delete shapes;
217 }
218 
219 }
220