Add svg conversion.

This commit is contained in:
jmsgrogan 2023-01-17 17:41:27 +00:00
parent 1f85954e98
commit dfbc87cb09
33 changed files with 602 additions and 79 deletions

View file

@ -29,10 +29,12 @@ list(APPEND web_LIB_INCLUDES
html/elements/HtmlParagraphElement.cpp
svg/SvgDocument.h
svg/SvgWriter.h
svg/SvgReader.h
svg/SvgShapeElement.h
svg/SvgElement.h
svg/elements/SvgShapeElements.h
svg/SvgDocument.cpp
svg/SvgReader.cpp
svg/SvgWriter.cpp
svg/SvgShapeElement.cpp
svg/SvgElement.cpp

View file

@ -10,7 +10,7 @@ std::string HtmlParagraphElement::toString(unsigned depth, bool keepInline) cons
content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\"";
}
const auto num_children = getNumChildren();
const auto num_children = mChildren.size();
if (num_children == 0 && getText().empty())
{
content += "/>\n";
@ -30,9 +30,9 @@ std::string HtmlParagraphElement::toString(unsigned depth, bool keepInline) cons
{
content += "\n";
}
for (std::size_t idx=0; idx< getNumChildren(); idx++)
for(const auto& child : mChildren)
{
auto child = getChild(idx);
content += child->toString(depth+1, true);
}
if (num_children>0)

View file

@ -1,6 +1,7 @@
#include "SvgDocument.h"
#include "XmlAttribute.h"
#include "StringUtils.h"
#include <sstream>
@ -19,10 +20,39 @@ SvgDocument::SvgDocument()
void SvgDocument::setViewBox(double x, double y, double w, double h)
{
auto viewbox = std::make_unique<XmlAttribute>("viewBox");
std::stringstream sstr;
sstr << x << " " << y << " " << w << " " << h;
viewbox->setValue(sstr.str());
setViewBox(sstr.str());
}
void SvgDocument::setViewBox(const std::string& data)
{
auto viewbox = std::make_unique<XmlAttribute>("viewBox");
viewbox->setValue(data);
getRoot()->addAttribute(std::move(viewbox));
}
SvgDocument::ViewBox SvgDocument::getViewBox() const
{
ViewBox viewBox;
if (!getRoot())
{
return viewBox;
}
if (auto vb_attr = getRoot()->getAttribute("viewBox"); vb_attr)
{
auto entries = StringUtils::split(vb_attr->getValue());
if (entries.size() != 4)
{
return viewBox;
}
viewBox.mX = std::stod(entries[0]);
viewBox.mY = std::stod(entries[1]);
viewBox.mW = std::stod(entries[2]);
viewBox.mH = std::stod(entries[3]);
}
return viewBox;
}

View file

@ -5,6 +5,18 @@
class SvgDocument : public XmlDocument
{
public:
struct ViewBox
{
double mX{ 0 };
double mY{ 0 };
double mW{ 0 };
double mH{ 0 };
};
SvgDocument();
ViewBox getViewBox() const;
void setViewBox(const std::string& data);
void setViewBox(double x, double y, double w, double h);
};

76
src/web/svg/SvgReader.cpp Normal file
View file

@ -0,0 +1,76 @@
#include "SvgReader.h"
#include "File.h"
#include "XmlParser.h"
#include "XmlAttribute.h"
#include "SvgShapeElements.h"
#include "FileLogger.h"
std::unique_ptr<SvgDocument> SvgReader::read(const Path& path) const
{
auto svg_doc = std::make_unique<SvgDocument>();
File in_file(path);
auto lines = in_file.readLines();
XmlParser parser;
for (const auto& line : lines)
{
parser.processLine(line);
}
auto xml_doc = parser.getDocument();
if (xml_doc->getRoot() && xml_doc->getRoot()->getTagName() == "svg")
{
onRoot(xml_doc->getRoot(), svg_doc.get());
}
return svg_doc;
}
void SvgReader::onRoot(XmlElement* element, SvgDocument* doc) const
{
for (const auto& attr : element->getAttributes())
{
doc->getRoot()->addAttribute(attr.first, attr.second->getValue());
}
for (const auto& child : element->getChildren())
{
onChild(child.get(), doc->getRoot());
}
}
void SvgReader::onChild(XmlElement* element, XmlElement* svg_parent) const
{
std::unique_ptr<XmlElement> new_svg;
if (element->getTagName() == "circle")
{
new_svg = std::make_unique<SvgCircle>();
}
else if (element->getTagName() == "ellipse")
{
new_svg = std::make_unique<SvgCircle>(SvgCircle::Type::ELLIPSE);
}
else if (element->getTagName() == "rect")
{
new_svg = std::make_unique<SvgRectangle>();
}
else
{
return;
}
for (const auto& attr : element->getAttributes())
{
new_svg->addAttribute(attr.first, attr.second->getValue());
}
for (const auto& child : element->getChildren())
{
onChild(child.get(), new_svg.get());
}
svg_parent->addChild(std::move(new_svg));
}

20
src/web/svg/SvgReader.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#include "SvgDocument.h"
#include <filesystem>
using Path = std::filesystem::path;
class SvgElement;
class SvgReader
{
public:
std::unique_ptr<SvgDocument> read(const Path& path) const;
private:
void onRoot(XmlElement* element, SvgDocument* doc) const;
void onChild(XmlElement* element, XmlElement* svg_parent) const;
};

View file

@ -1,6 +1,7 @@
#include "SvgShapeElement.h"
#include "XmlAttribute.h"
#include "StringUtils.h"
#include <sstream>
@ -10,6 +11,96 @@ SvgShapeElement::SvgShapeElement(const std::string& tagName)
}
Transform SvgShapeElement::getTransform() const
{
Transform transform;
const std::string translate_key = "translate";
const std::string scale_key = "scale";
if (auto attr = getAttribute("transform"); attr)
{
const auto val = attr->getValue();
std::string working_string;
bool in_translate = false;
bool in_scale = false;
for (auto c : val)
{
if (StringUtils::stripSurroundingWhitepsace(working_string) == translate_key)
{
in_translate = true;
working_string.clear();
continue;
}
else if (StringUtils::stripSurroundingWhitepsace(working_string) == scale_key)
{
in_scale = true;
working_string.clear();
continue;
}
else if (c == '(')
{
continue;
}
else if (c == ')')
{
if (in_translate)
{
const auto loc = parsePoint(working_string, 0.0);
transform.setLocation(loc);
in_translate = false;
}
else if (in_scale)
{
const auto loc = parsePoint(working_string, 1.0);
transform.setScale(loc.getX(), loc.getY(), loc.getZ());
in_scale = false;
}
working_string.clear();
}
else
{
working_string += c;
}
}
}
return transform;
}
std::string SvgShapeElement::getLabelledContent(const std::string& key, const std::string& content) const
{
return content.substr(key.size(), content.size() - key.size() - 2);
}
Point SvgShapeElement::parsePoint(const std::string& pointString, double defaultVal) const
{
double x = defaultVal;
double y = defaultVal;
double z = defaultVal;
const auto split = StringUtils::split(pointString);
for (std::size_t idx = 0; idx < split.size(); idx++)
{
if (idx == 0)
{
x = std::stod(split[idx]);
}
else if (idx == 1)
{
y = std::stod(split[idx]);
}
else if (idx == 2)
{
z = std::stod(split[idx]);
}
}
return { x, y, z };
}
bool SvgShapeElement::hasTransform() const
{
return bool(getAttribute("transform"));
}
void SvgShapeElement::setNoFill()
{
auto attr = std::make_unique<XmlAttribute>("fill");

View file

@ -2,12 +2,17 @@
#include "SvgElement.h"
#include "Color.h"
#include "Transform.h"
class SvgShapeElement : public SvgElement
{
public:
SvgShapeElement(const std::string& tagName);
Transform getTransform() const;
bool hasTransform() const;
void setFill(const Color& fill);
void setNoFill();
@ -17,4 +22,8 @@ public:
void setStrokeColor(const Color& stroke);
void setNoStroke();
private:
std::string getLabelledContent(const std::string& key, const std::string& content) const;
Point parsePoint(const std::string& pointString, double defaultVal = 0.0) const;
};

View file

@ -1,8 +1,9 @@
#include "SvgWriter.h"
#include "SvgDocument.h"
#include "File.h"
std::string SvgWriter::toString(SvgDocument* document)
std::string SvgWriter::toString(SvgDocument* document) const
{
std::string content = "";
if (auto root = document->getRoot())
@ -11,3 +12,9 @@ std::string SvgWriter::toString(SvgDocument* document)
}
return content;
}
void SvgWriter::toFile(const Path& path, SvgDocument* document) const
{
File out_file(path);
out_file.writeText(toString(document));
}

View file

@ -1,11 +1,16 @@
#pragma once
#include <string>
#include <filesystem>
using Path = std::filesystem::path;
class SvgDocument;
class SvgWriter
{
public:
std::string toString(SvgDocument* document);
std::string toString(SvgDocument* document) const;
void toFile(const Path& path, SvgDocument* document) const;
};

View file

@ -9,6 +9,50 @@ SvgCircle::SvgCircle(Type type)
}
Point SvgCircle::getLocation() const
{
double cx = 0.0;
double cy = 0.0;
if (auto attr = getAttribute("cx"); attr)
{
cx = std::stod(attr->getValue());
}
if (auto attr = getAttribute("cy"); attr)
{
cy = std::stod(attr->getValue());
}
return { cx, cy };
}
double SvgCircle::getRadius() const
{
double radius = 1.0;
if (auto attr = getAttribute("rx"); attr)
{
radius = std::stod(attr->getValue());
}
else if (auto attr = getAttribute("r"); attr)
{
radius = std::stod(attr->getValue());
}
return radius;
}
SvgCircle::Type SvgCircle::getType() const
{
return mType;
}
double SvgCircle::getMinorRadius() const
{
double radius = 0.0;
if (auto attr = getAttribute("ry"); attr)
{
radius = std::stod(attr->getValue());
}
return radius;
}
void SvgCircle::setLocation(const Point& loc)
{
auto cx = std::make_unique<XmlAttribute>("cx");

View file

@ -16,6 +16,14 @@ public:
SvgCircle(Type type = Type::REGULAR);
Point getLocation() const;
double getRadius() const;
Type getType() const;
double getMinorRadius() const;
void setLocation(const Point& loc);
void setRadius(double rad);

View file

@ -193,6 +193,7 @@ void XmlParser::onRightBracket()
case LS::Await_Attribute_Name:
case LS::Await_Attribute_Name_End:
case LS::Await_Attribute_Value:
case LS::Await_Tag_Inline_Close:
{
onTagClose();
break;
@ -220,13 +221,21 @@ void XmlParser::onQuestionMark()
void XmlParser::onForwardSlash()
{
if(mLineState == LS::Await_Tag_Follower)
if (mLineState == LS::Await_Tag_Follower)
{
if(mDocumentState == DS::Await_Element || mDocumentState == DS::Build_Element)
if (mDocumentState == DS::Await_Element || mDocumentState == DS::Build_Element)
{
mDocumentState = DS::Close_Element;
}
}
else if(mLineState == LS::Await_Attribute_Name)
{
mLineState = LS::Await_Tag_Inline_Close;
}
else
{
onNonAlphaNumeric(StringUtils::FORWARD_SLASH);
}
}
void XmlParser::onEquals()
@ -270,6 +279,13 @@ void XmlParser::onTagClose()
{
onTagNameEnd();
}
else if (mLineState == LS::Await_Tag_Inline_Close)
{
if (!mWorkingElements.empty())
{
mWorkingElements.pop();
}
}
onElementTagEnd();
}
else if(mDocumentState == DS::Close_Element)

View file

@ -27,6 +27,7 @@ public:
Await_Tag_Follower,
Await_Tag_Name,
Await_Tag_Name_End,
Await_Tag_Inline_Close,
Await_Attribute_Name,
Await_Attribute_Name_End,
Await_Attribute_Value,
@ -82,8 +83,6 @@ private:
void onAttributeValueEnd();
void onPrologId();
void onStartProlog();
void onFinishProlog();

View file

@ -34,6 +34,13 @@ void XmlElement::addAttribute(XmlAttributePtr attribute)
mAttributes[attribute->getName()] = std::move(attribute);
}
void XmlElement::addAttribute(const std::string& name, const std::string& value)
{
auto attr = std::make_unique<XmlAttribute>(name);
attr->setValue(value);
addAttribute(std::move(attr));
}
const std::string& XmlElement::getTagName() const
{
return mTagName;
@ -62,6 +69,11 @@ XmlElement* XmlElement::getFirstChildWithTagName(const std::string& tag)
return nullptr;
}
bool XmlElement::hasAttribute(const std::string& attribute) const
{
return (bool)(getAttribute(attribute));
}
XmlAttribute* XmlElement::getAttribute(const std::string& attributeName) const
{
if (auto iter = mAttributes.find(attributeName); iter != mAttributes.end())
@ -76,14 +88,9 @@ const std::unordered_map<std::string, XmlAttributePtr>& XmlElement::getAttribute
return mAttributes;
}
std::size_t XmlElement::getNumChildren() const
const std::vector<std::unique_ptr<XmlElement> >& XmlElement::getChildren() const
{
return mChildren.size();
}
XmlElement* XmlElement::getChild(std::size_t index) const
{
return mChildren[index].get();
return mChildren;
}
std::string XmlElement::toString(unsigned depth, bool keepInline) const
@ -98,7 +105,7 @@ std::string XmlElement::toString(unsigned depth, bool keepInline) const
content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\"";
}
const auto num_children = getNumChildren();
const auto num_children = mChildren.size();
if (num_children == 0 && getText().empty())
{
content += "/>" + line_ending;
@ -118,9 +125,9 @@ std::string XmlElement::toString(unsigned depth, bool keepInline) const
{
content += line_ending;
}
for (std::size_t idx=0; idx< getNumChildren(); idx++)
for(const auto& child : mChildren)
{
auto child = getChild(idx);
content += child->toString(depth+1, keepInline);
}
if (num_children>0)

View file

@ -17,15 +17,17 @@ public:
static std::unique_ptr<XmlElement> Create(const std::string& tagName);
void addAttribute(XmlAttributePtr attribute);
void addAttribute(const std::string& name, const std::string& value);
void addChild(std::unique_ptr<XmlElement> child);
const std::string& getTagName() const;
const std::string& getText() const;
bool hasAttribute(const std::string& attribute) const;
XmlAttribute* getAttribute(const std::string& attribute) const;
const std::unordered_map<std::string, XmlAttributePtr>& getAttributes() const;
std::size_t getNumChildren() const;
XmlElement* getChild(std::size_t index) const;
const std::vector<std::unique_ptr<XmlElement> >& getChildren() const;
XmlElement* getFirstChildWithTagName(const std::string& tag);