1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
// -*- C++ -*-
/* svgparser.cc
*
* By Dan Dennedy <dan@dennedy.org>
*
* Copyright (C) 2003 The libxml++ development team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <iostream>
#include <libxml/tree.h>
#include "svgparser.h"
#include "svgdocument.h"
#include "svgelement.h"
#include "svgpath.h"
#include "svggroup.h"
namespace SVG {
Parser::Parser(xmlpp::Document& document)
: xmlpp::SaxParser(true), m_doc(document)
{
set_substitute_entities(true);
}
Parser::~Parser()
{
}
void Parser::on_start_element(const Glib::ustring& name,
const AttributeList& attributes)
{
//This method replaces the normal libxml++ node
//with an instance of a derived node.
//This is not a recommended technique, and might not
//work with future versions of libxml++.
// Parse namespace prefix and save for later:
Glib::ustring elementPrefix;
Glib::ustring elementName = name;
Glib::ustring::size_type idx = name.find(':');
if (idx != Glib::ustring::npos) //If the separator was found
{
elementPrefix = name.substr(0, idx);
elementName = name.substr(idx + 1);
}
xmlpp::Element* element_normal = nullptr;
// Create a normal libxml++ node:
if (m_doc.get_root_node() == nullptr)
{
// Create the root node if necessary:
element_normal = m_doc.create_root_node(elementName);
}
else
{
// Create the other elements as child nodes of the last nodes:
element_normal = m_context.top()->add_child(elementName);
}
// TODO: The following is a hack because it leverages knowledge of libxml++
// implementation rather than interface - specifically that deleting the C++
// instance will leave the underlying C instance intact and part of the libxml DOM tree.
//
// Delete the xmlpp::Element created above so we can link the libxml2
// node with the derived Element object we create below.
auto node = element_normal->cobj(); //Save it for later.
delete element_normal;
element_normal = nullptr;
// TODO: Again, this requires knowledge of the libxml++ implemenation -
// specifically that the base xmlpp::Node() constructor will reassociate
// the underyling C instance with this new C++ instance, by seeting _private.
//
// Construct a custom Element based upon prefix and name.
// This will then be deleted by libxml++, just as libxml++ would normally have
// deleted its own node.
// TODO: Don't delete the original (above) if it isn't one of these node names.
xmlpp::Element* element_derived = nullptr;
if (elementName == "g")
element_derived = new SVG::Group(node);
else if (elementName == "path")
element_derived = new SVG::Path(node);
else
element_derived = new SVG::Element(node);
if(element_derived)
{
//Set the context, so that child nodes will be added to this node,
//until on_end_element():
m_context.push(element_derived);
// Copy the attributes form the old node to the new derived node:
// In theory, you could change the attributes here.
for(const auto& attr_pair : attributes)
{
const auto attr_name = attr_pair.name;
const auto attr_value = attr_pair.value;
const auto idx_colon = attr_name.find(':');
if (idx_colon == Glib::ustring::npos) // If the separator was not found.
{
if (attr_name == "xmlns") // This is a namespace declaration.
{
//There is no second part, so this is a default namespace declaration.
element_derived->set_namespace_declaration(attr_value);
}
else
{
//This is just an attribute value:
element_derived->set_attribute(attr_name, attr_value);
}
}
else
{
//The separator was found:
auto prefix = attr_name.substr(0, idx_colon);
auto suffix = attr_name.substr(idx_colon + 1);
if (prefix == "xmlns") // This is a namespace declaration.
element_derived->set_namespace_declaration(attr_value, suffix);
else
{
//This is a namespaced attribute value.
//(The namespace must have been declared already)
auto attr = element_derived->set_attribute(suffix, attr_value);
attr->set_namespace(prefix); //alternatively, we could have specified the whole name to set_attribute().
}
}
}
// We have to set the element namespace after the attributes because
// an attribute might declare the namespace used in the actual element's name.
if (!elementPrefix.empty())
element_derived->set_namespace(elementPrefix);
}
}
void Parser::on_end_element(const Glib::ustring& /* name */)
{
// This causes the next child elements to be added to the sibling, not this node.
m_context.pop();
}
void Parser::on_characters(const Glib::ustring& text)
{
if(!m_context.empty())
m_context.top()->add_child_text(text);
}
void Parser::on_comment(const Glib::ustring& text)
{
if(!m_context.empty())
m_context.top()->add_child_comment(text);
else
m_doc.add_comment(text);
}
void Parser::on_warning(const Glib::ustring& text)
{
std::cout << "on_warning(): " << text << std::endl;
}
void Parser::on_error(const Glib::ustring& text)
{
std::cout << "on_error(): " << text << std::endl;
}
void Parser::on_fatal_error(const Glib::ustring& text)
{
std::cout << "on_fatal_error(): " << text << std::endl;
}
void Parser::on_cdata_block(const Glib::ustring& text)
{
if(!m_context.empty())
m_context.top()->add_child_cdata(text);
}
}
|