cppfw/svgdom

Create new element to insert custom strings when generating XMLs

Closed this issue · 8 comments

Add a new element whose sole purpose is to insert custom strings when generating XMLs. This new element could be called StringElement.

You can create such element by just deriving from svgdom::Element. What needs to be done is to expose the StringWriter visitor class, so that it is also possible to extend it.

@JaimeIvanCervantes what is you exact problem, i.e. what do you want to achieve here? Is it writing the SVG which contains custom elements to string?

I just submitted a PR with my solution: #10

What I want to do is very simple, I just want to insert any text to the XML string. I need this to insert code for custom elements that are not found in the SVG spec. I created an Element called CustomStringElement (in files Custom.hpp and Custom.cpp). Here's an example:

auto dom = utki::makeUnique<svgdom::SvgElement>();
svgdom::PathElement path;

svgdom::PathElement::Step step;
step.type = svgdom::PathElement::Step::Type_e::MOVE_ABS;
step.x = 0;
step.y = 0;
path.path.push_back(step);

step.type = svgdom::PathElement::Step::Type_e::LINE_ABS;
step.x = 0;
step.y = 300;
path.path.push_back(step);

step.type = svgdom::PathElement::Step::Type_e::LINE_ABS;
step.x = 300;
step.y = 300;
path.path.push_back(step);

step.type = svgdom::PathElement::Step::Type_e::LINE_ABS;
step.x = 300;
step.y = 0;
path.path.push_back(step);

dom->children.push_back(utki::makeUnique<svgdom::PathElement>(path));

svgdom::CustomStringElement custom("Any string goes here.");
  
dom->children.push_back(utki::makeUnique<svgdom::CustomStringElement>(custom));

cout<<dom->toString()<<std::endl;

The line custom.setCustomString("Any string goes here.") inserts a string in the XML. Here's the output:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
	<path d="M0,0L0,300 300,300 300,0"/>
	Any string goes here.
</svg>

I wrote this out of pure necessity, and I tried to use your code conventions, but feel free to add or rename anything you want.

I made changes to support custom elements. I refactored the StreamWriter so that it now allows writing of custom elements and adding even custom attributes to standard elements.
I added a test cases which demonstrates how to achieve that: https://github.com/igagis/svgdom/blob/master/tests/customElement/main.cpp
The svgdom version 0.2.61 should be available within an hour. Please check if it suits your needs.

Unfortunately my project requires me to insert strings that are not necessarily svg elements or attributes. I know that this might not be a usual case, so I decided to use the following code to create my custom element:

struct CustomElement : public svgdom::Element{
	std::string str;
	
	CustomElement(std::string s) : str(s) {}
	
	void accept(svgdom::Visitor& visitor) const override;
};

class CustomStreamWriter : public svgdom::StreamWriter{
public:
	CustomStreamWriter(std::ostream& s) :
			svgdom::StreamWriter(s)
	{}
	
	using svgdom::StreamWriter::visit;

	virtual void visit(const CustomElement& e){
		auto ind = indentStr();
		this->s << ind;
		this->s << e.str;
		this->s << std::endl;
	}
};

void CustomElement::accept(svgdom::Visitor& visitor) const{
	if(auto v = dynamic_cast<CustomStreamWriter*>(&visitor)){
		v->visit(*this);
	}else{
		visitor.defaultVisit(*this);
	}
}


int main(int argc, char** argv) {
	auto dom = utki::makeUnique<svgdom::SvgElement>();

	svgdom::PathElement path;

	svgdom::PathElement::Step step;
	step.type = svgdom::PathElement::Step::Type_e::MOVE_ABS;
	step.x = 0;
	step.y = 0;
	path.path.push_back(step);

	step.type = svgdom::PathElement::Step::Type_e::LINE_ABS;
	step.x = 0;
	step.y = 300;
	path.path.push_back(step);

	step.type = svgdom::PathElement::Step::Type_e::LINE_ABS;
	step.x = 300;
	step.y = 300;
	path.path.push_back(step);

	step.type = svgdom::PathElement::Step::Type_e::LINE_ABS;
	step.x = 300;
	step.y = 0;
	path.path.push_back(step);

	dom->children.push_back(utki::makeUnique<svgdom::PathElement>(path));

	dom->children.push_back(utki::makeUnique<CustomElement>("Any string goes here."));

	std::stringstream ss;
	CustomStreamWriter writer(ss);
	dom->accept(writer);
	auto str = ss.str();
	cout<<str<<endl;
}

This will produce the following output:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
	<path d="M0,0 L0,300 300,300 300,0"/>
	Any string goes here.
</svg>

In order for my custom element to work though, I would need to merge the following PR where I am only moving s, indentStr(), and indent members from private to protected: #11

This is really important for my project :).

The PR seems OK, but I asked you to add a comment there. Please also check the updated test https://github.com/igagis/svgdom/blob/master/tests/customElement/main.cpp and update your code accordingly, because it is not very good to cast to CustomStreamWriter in the accept method.

PR merged