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

@ -27,4 +27,17 @@ double Transform::getScaleY() const
double Transform::getScaleZ() const double Transform::getScaleZ() const
{ {
return mScaleZ; return mScaleZ;
}
void Transform::setLocation(const Point& loc)
{
mLocation = loc;
}
void Transform::setScale(double scaleX, double scaleY, double scaleZ)
{
mScaleX = scaleX;
mScaleY = scaleY;
mScaleZ = scaleZ;
} }

View file

@ -7,6 +7,10 @@ class Transform
public: public:
Transform(const Point& location = {}, double scaleX = 1.0, double scaleY = 1.0, double scaleZ = 1.0); Transform(const Point& location = {}, double scaleX = 1.0, double scaleY = 1.0, double scaleZ = 1.0);
void setLocation(const Point& loc);
void setScale(double scaleX, double scaleY = 1.0, double scaleZ = 1.0);
const Point& getLocation() const; const Point& getLocation() const;
double getScaleX() const; double getScaleX() const;

View file

@ -89,3 +89,10 @@ void Point::apply(const Transform& transform)
mY *= transform.getScaleY(); mY *= transform.getScaleY();
mZ *= transform.getScaleZ(); mZ *= transform.getScaleZ();
} }
void Point::move(double x, double y, double z)
{
mX += x;
mY += y;
mZ += z;
}

View file

@ -23,6 +23,8 @@ public:
void apply(const Transform& transform); void apply(const Transform& transform);
void move(double x, double y, double z = 0);
double getX() const; double getX() const;
double getY() const; double getY() const;

View file

@ -10,12 +10,13 @@ list(APPEND publishing_HEADERS
pdf/PdfXRefTable.h pdf/PdfXRefTable.h
pdf/PdfWriter.h pdf/PdfWriter.h
plotting/GraphPlotter.h plotting/GraphPlotter.h
plotting/SvgConverter.h
plotting/PlotNode.h plotting/PlotNode.h
plotting/EquationNode.h plotting/EquationNode.h
latex/LatexDocument.h latex/LatexDocument.h
latex/LatexMathExpression.h latex/LatexMathExpression.h
latex/LatexSymbols.h latex/LatexSymbols.h
svg/SvgNode.h
svg/SvgPainter.h
DocumentConverter.h DocumentConverter.h
) )
@ -34,10 +35,11 @@ list(APPEND publishing_LIB_INCLUDES
latex/LatexMathExpression.cpp latex/LatexMathExpression.cpp
latex/LatexSymbols.cpp latex/LatexSymbols.cpp
plotting/GraphPlotter.h plotting/GraphPlotter.h
plotting/SvgConverter.cpp
plotting/PlotNode.cpp plotting/PlotNode.cpp
plotting/EquationNode.cpp plotting/EquationNode.cpp
DocumentConverter.cpp DocumentConverter.cpp
svg/SvgNode.cpp
svg/SvgPainter.cpp
) )
add_library(publishing SHARED ${publishing_LIB_INCLUDES} ${publishing_INCLUDES} ${publishing_HEADERS}) add_library(publishing SHARED ${publishing_LIB_INCLUDES} ${publishing_INCLUDES} ${publishing_HEADERS})
@ -47,6 +49,7 @@ target_include_directories(publishing PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/pdf ${CMAKE_CURRENT_SOURCE_DIR}/pdf
${CMAKE_CURRENT_SOURCE_DIR}/plotting ${CMAKE_CURRENT_SOURCE_DIR}/plotting
${CMAKE_CURRENT_SOURCE_DIR}/latex ${CMAKE_CURRENT_SOURCE_DIR}/latex
${CMAKE_CURRENT_SOURCE_DIR}/svg
) )
set_target_properties( publishing PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) set_target_properties( publishing PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
target_link_libraries( publishing PUBLIC core web graphics visual_elements) target_link_libraries( publishing PUBLIC core web graphics visual_elements)

View file

@ -1,30 +0,0 @@
#pragma once
#include <memory>
class SvgDocument;
class SvgShapeElement;
class XmlAttribute;
class Scene;
class SceneModel;
class SceneText;
class Transform;
class SvgConverter
{
public:
std::unique_ptr<SvgDocument> convert(Scene* scene, double width = 800.0, double height = 800.0) const;
private:
void convertMesh(SvgDocument* document, SceneModel* model, bool showOutline = false) const;
void convertPrimitive(SvgDocument* document, SceneModel* model) const;
void convertText(SvgDocument* document, SceneText* model) const;
void setStyle(SceneModel* model, SvgShapeElement* element) const;
std::unique_ptr<XmlAttribute> toTransform(const Transform& transform) const;
};

View file

@ -0,0 +1,90 @@
#include "SvgNode.h"
#include "CircleNode.h"
#include "SvgShapeElements.h"
SvgNode::SvgNode(const Point& location)
: AbstractVisualNode(location)
{
}
void SvgNode::setContent(std::unique_ptr<SvgDocument> doc)
{
mContent = std::move(doc);
mContentDirty = true;
mChildren.clear();
mManagedChildren.clear();
}
void SvgNode::updateTransform()
{
}
void SvgNode::createOrUpdateGeometry(SceneInfo* sceneInfo)
{
if (!mContent->getRoot())
{
return;
}
auto viewbox = mContent->getViewBox();
for (const auto& svg_element : mContent->getRoot()->getChildren())
{
std::unique_ptr<AbstractVisualNode> node;
if (svg_element->getTagName() == "circle")
{
auto svg_circle = dynamic_cast<SvgCircle*>(svg_element.get());
auto loc = svg_circle->getLocation();
auto radius = svg_circle->getRadius();
auto minor_radius = radius;
if (svg_circle->getType() == SvgCircle::Type::ELLIPSE)
{
minor_radius = svg_circle->getMinorRadius();
}
if (svg_element->hasAttribute("transform"))
{
const auto transform = svg_circle->getTransform();
loc.move(transform.getLocation().getX(), transform.getLocation().getY());
radius *= transform.getScaleX();
minor_radius *= transform.getScaleY();
}
auto circle_node = std::make_unique<CircleNode>(loc, radius);
circle_node->setMinorRadius(minor_radius);
node = std::move(circle_node);
}
if (!node)
{
continue;
}
auto raw_node = node.get();
mManagedChildren.push_back(std::move(node));
addChild(raw_node);
}
}
void SvgNode::update(SceneInfo* sceneInfo)
{
if (mContent && mContentDirty)
{
createOrUpdateGeometry(sceneInfo);
mContentDirty = false;
}
if (mTransformIsDirty)
{
updateTransform();
mTransformIsDirty = false;
}
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "AbstractVisualNode.h"
#include "SvgDocument.h"
#include <memory>
#include <vector>
class SvgNode : public AbstractVisualNode
{
public:
SvgNode(const Point& location);
void setContent(std::unique_ptr<SvgDocument> doc);
void update(SceneInfo* sceneInfo);
private:
void createOrUpdateGeometry(SceneInfo* sceneInfo);
void updateTransform();
bool mContentDirty{ true };
std::vector<std::unique_ptr<AbstractVisualNode> > mManagedChildren;
std::unique_ptr<SvgDocument> mContent;
};

View file

@ -1,4 +1,4 @@
#include "SvgConverter.h" #include "SvgPainter.h"
#include "SvgDocument.h" #include "SvgDocument.h"
#include "Scene.h" #include "Scene.h"
@ -17,38 +17,38 @@
#include "SvgShapeElements.h" #include "SvgShapeElements.h"
#include "XmlAttribute.h" #include "XmlAttribute.h"
std::unique_ptr<SvgDocument> SvgConverter::convert(Scene* scene, double width, double height) const std::unique_ptr<SvgDocument> SvgPainter::paint(Scene* scene, double width, double height) const
{ {
auto doc = std::make_unique<SvgDocument>(); auto doc = std::make_unique<SvgDocument>();
doc->setViewBox(0.0, 0.0, width, height); doc->setViewBox(0.0, 0.0, width, height);
scene->update(); scene->update();
for(auto item : scene->getItems()) for (auto item : scene->getItems())
{ {
if (item->getType() == SceneItem::Type::MODEL) if (item->getType() == SceneItem::Type::MODEL)
{ {
auto model = dynamic_cast<SceneModel*>(item); auto model = dynamic_cast<SceneModel*>(item);
if (model->getGeometry()) if (model->getGeometry())
{ {
convertPrimitive(doc.get(), model); paintPrimitive(doc.get(), model);
} }
else else
{ {
convertMesh(doc.get(), model, scene->shouldShowMeshOutline()); paintMesh(doc.get(), model, scene->shouldShowMeshOutline());
} }
} }
else else
{ {
auto text = dynamic_cast<SceneText*>(item); auto text = dynamic_cast<SceneText*>(item);
convertText(doc.get(), text); paintText(doc.get(), text);
} }
} }
return std::move(doc); return std::move(doc);
} }
void SvgConverter::convertMesh(SvgDocument* document, SceneModel* model, bool showOutline) const void SvgPainter::paintMesh(SvgDocument* document, SceneModel* model, bool showOutline) const
{ {
auto mesh = model->getMesh(); auto mesh = model->getMesh();
auto transform = model->getTransform(); auto transform = model->getTransform();
@ -87,7 +87,7 @@ void SvgConverter::convertMesh(SvgDocument* document, SceneModel* model, bool sh
} }
} }
void SvgConverter::setStyle(SceneModel* model, SvgShapeElement* element) const void SvgPainter::setStyle(SceneModel* model, SvgShapeElement* element) const
{ {
auto transform = model->getTransform(); auto transform = model->getTransform();
@ -111,7 +111,7 @@ void SvgConverter::setStyle(SceneModel* model, SvgShapeElement* element) const
element->addAttribute(std::move(toTransform(transform))); element->addAttribute(std::move(toTransform(transform)));
} }
void SvgConverter::convertPrimitive(SvgDocument* document, SceneModel* model) const void SvgPainter::paintPrimitive(SvgDocument* document, SceneModel* model) const
{ {
if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE) if (model->getGeometry()->getType() == AbstractGeometricItem::Type::RECTANGLE)
{ {
@ -139,12 +139,12 @@ void SvgConverter::convertPrimitive(SvgDocument* document, SceneModel* model) co
} }
} }
void SvgConverter::convertText(SvgDocument* document, SceneText* model) const void SvgPainter::paintText(SvgDocument* document, SceneText* model) const
{ {
} }
std::unique_ptr<XmlAttribute> SvgConverter::toTransform(const Transform& transform) const std::unique_ptr<XmlAttribute> SvgPainter::toTransform(const Transform& transform) const
{ {
auto svg_transform = std::make_unique<XmlAttribute>("transform"); auto svg_transform = std::make_unique<XmlAttribute>("transform");

View file

@ -0,0 +1,32 @@
#pragma once
#include "AbstractPainter.h"
#include <memory>
class SvgDocument;
class SvgShapeElement;
class XmlAttribute;
class Scene;
class SceneModel;
class SceneText;
class Transform;
class SvgPainter
{
public:
std::unique_ptr<SvgDocument> paint(Scene* scene, double width = 800.0, double height = 800.0) const;
private:
void paintMesh(SvgDocument* document, SceneModel* model, bool showOutline = false) const;
void paintPrimitive(SvgDocument* document, SceneModel* model) const;
void paintText(SvgDocument* document, SceneText* model) const;
void setStyle(SceneModel* model, SvgShapeElement* element) const;
std::unique_ptr<XmlAttribute> toTransform(const Transform& transform) const;
};

View file

@ -3,6 +3,8 @@
#include "DirectX2dInterface.h" #include "DirectX2dInterface.h"
#include "AbstractGeometricItem.h" #include "AbstractGeometricItem.h"
#include "Rectangle.h" #include "Rectangle.h"
#include "Circle.h"
#include "SceneModel.h" #include "SceneModel.h"
void DirectX2dPainter::startDrawing() void DirectX2dPainter::startDrawing()
@ -49,6 +51,25 @@ void DirectX2dPainter::paint(SceneModel* model)
rt->DrawRectangle(d2d_rect, mSolidBrush.Get(), 1.0f); rt->DrawRectangle(d2d_rect, mSolidBrush.Get(), 1.0f);
} }
} }
else if (model->getGeometry()->getType() == AbstractGeometricItem::Type::CIRCLE)
{
const auto loc = model->getTransform().getLocation();
const auto scale_x = model->getTransform().getScaleX();
const auto scale_y = model->getTransform().getScaleY();
auto circle = dynamic_cast<Circle*>(model->getGeometry());
const auto radius = circle->getRadius() * scale_x;
const auto radiusy = circle->getMinorRadius() * scale_y;
D2D1_POINT_2F d2d_centre{ static_cast<float>(loc.getX()), static_cast<float>(loc.getY()) };
D2D1_ELLIPSE ellipse{ d2d_centre, static_cast<float>(radius), static_cast<float>(radiusy) };
if (model->hasOutlineColor())
{
mSolidBrush->SetColor(toD2dColor(model->getOutlineColor()));
rt->DrawEllipse(ellipse, mSolidBrush.Get(), 1.0f);
}
}
} }
void DirectX2dPainter::setD2dInterface(DirectX2dInterface* d2dIterface) void DirectX2dPainter::setD2dInterface(DirectX2dInterface* d2dIterface)

View file

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

View file

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

View file

@ -1,6 +1,7 @@
#include "SvgDocument.h" #include "SvgDocument.h"
#include "XmlAttribute.h" #include "XmlAttribute.h"
#include "StringUtils.h"
#include <sstream> #include <sstream>
@ -19,10 +20,39 @@ SvgDocument::SvgDocument()
void SvgDocument::setViewBox(double x, double y, double w, double h) void SvgDocument::setViewBox(double x, double y, double w, double h)
{ {
auto viewbox = std::make_unique<XmlAttribute>("viewBox");
std::stringstream sstr; std::stringstream sstr;
sstr << x << " " << y << " " << w << " " << h; 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)); 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 class SvgDocument : public XmlDocument
{ {
public: public:
struct ViewBox
{
double mX{ 0 };
double mY{ 0 };
double mW{ 0 };
double mH{ 0 };
};
SvgDocument(); SvgDocument();
ViewBox getViewBox() const;
void setViewBox(const std::string& data);
void setViewBox(double x, double y, double w, double h); 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 "SvgShapeElement.h"
#include "XmlAttribute.h" #include "XmlAttribute.h"
#include "StringUtils.h"
#include <sstream> #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() void SvgShapeElement::setNoFill()
{ {
auto attr = std::make_unique<XmlAttribute>("fill"); auto attr = std::make_unique<XmlAttribute>("fill");

View file

@ -2,12 +2,17 @@
#include "SvgElement.h" #include "SvgElement.h"
#include "Color.h" #include "Color.h"
#include "Transform.h"
class SvgShapeElement : public SvgElement class SvgShapeElement : public SvgElement
{ {
public: public:
SvgShapeElement(const std::string& tagName); SvgShapeElement(const std::string& tagName);
Transform getTransform() const;
bool hasTransform() const;
void setFill(const Color& fill); void setFill(const Color& fill);
void setNoFill(); void setNoFill();
@ -17,4 +22,8 @@ public:
void setStrokeColor(const Color& stroke); void setStrokeColor(const Color& stroke);
void setNoStroke(); 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 "SvgWriter.h"
#include "SvgDocument.h" #include "SvgDocument.h"
#include "File.h"
std::string SvgWriter::toString(SvgDocument* document) std::string SvgWriter::toString(SvgDocument* document) const
{ {
std::string content = ""; std::string content = "";
if (auto root = document->getRoot()) if (auto root = document->getRoot())
@ -11,3 +12,9 @@ std::string SvgWriter::toString(SvgDocument* document)
} }
return content; 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 #pragma once
#include <string> #include <string>
#include <filesystem>
using Path = std::filesystem::path;
class SvgDocument; class SvgDocument;
class SvgWriter class SvgWriter
{ {
public: 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) void SvgCircle::setLocation(const Point& loc)
{ {
auto cx = std::make_unique<XmlAttribute>("cx"); auto cx = std::make_unique<XmlAttribute>("cx");

View file

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

View file

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

View file

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

View file

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

View file

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

5
test/data/circles.svg Normal file
View file

@ -0,0 +1,5 @@
<svg viewBox="0 0 800 800" xmlns="http://www.w3.org/2000/svg">
<ellipse rx="0.500000" stroke-width="0.005000" ry="0.250000" fill="none" stroke="rgb(0,0,0)" transform="translate(50.000000 50.000000) scale(100.000000 100.000000) "/>
<circle stroke="rgb(0,0,0)" r="0.500000" fill="none" stroke-width="0.010000" transform="translate(50.000000 50.000000) scale(100.000000 100.000000) "/>
<circle stroke="none" r="0.500000" fill="rgb(0,0,0)" transform="translate(50.000000 50.000000) scale(4.000000 4.000000) "/>
</svg>

After

Width:  |  Height:  |  Size: 520 B

View file

@ -34,11 +34,9 @@ TEST_CASE(TestD2dWidgetRendering, "graphics")
auto scene = gui_app->getMainWindowScene(); auto scene = gui_app->getMainWindowScene();
Widget widget; Widget widget;
widget.setBackgroundColor({ 0, 200, 0 });
widget.setBounds(300, 300); widget.setBounds(300, 300);
auto button = Button::Create(); auto button = Button::Create();
button->setBackgroundColor({ 200, 0, 0 });
button->setLabel("Test Button"); button->setLabel("Test Button");
button->setMaxWidth(100); button->setMaxWidth(100);

View file

@ -1,7 +1,8 @@
set(PUBLISHING_UNIT_TEST_FILES set(PUBLISHING_UNIT_TEST_FILES
publishing/TestPdfWriter.cpp publishing/TestPdfWriter.cpp
publishing/TestDocumentConverter.cpp publishing/TestDocumentConverter.cpp
publishing/TestSvgConverter.cpp publishing/TestSvgConverter.cpp
publishing/TestSvgToNodeConverter.cpp
publishing/TestLatexConverter.cpp publishing/TestLatexConverter.cpp
PARENT_SCOPE PARENT_SCOPE
) )

View file

@ -1,6 +1,6 @@
#include "SvgWriter.h" #include "SvgWriter.h"
#include "SvgDocument.h" #include "SvgDocument.h"
#include "SvgConverter.h" #include "SvgPainter.h"
#include "Scene.h" #include "Scene.h"
#include "CircleNode.h" #include "CircleNode.h"
@ -24,13 +24,10 @@ TEST_CASE(TestSvgConverter, "[publishing]")
//rectangle.setFillColor({255, 0, 0}); //rectangle.setFillColor({255, 0, 0});
//scene.addNode(&rectangle); //scene.addNode(&rectangle);
SvgConverter converter; SvgPainter painter;
auto svg_document = converter.convert(&scene); auto svg_document = painter.paint(&scene);
svg_document->setViewBox(0, 0, 200, 200); svg_document->setViewBox(0, 0, 200, 200);
SvgWriter writer; SvgWriter writer;
auto content = writer.toString(svg_document.get()); writer.toFile(TestUtils::getTestOutputDir(__FILE__) / "scene.svg", svg_document.get());
auto outFile = std::make_unique<File>(TestUtils::getTestOutputDir(__FILE__) / "scene.svg");
outFile->writeText(content);
} }

View file

@ -0,0 +1,25 @@
#include "TestFramework.h"
#include "TestRenderUtils.h"
#include "TestUtils.h"
#include "SvgReader.h"
#include "SvgWriter.h"
#include "SvgNode.h"
TEST_CASE(TestSvgToNodeConverter, "publishing")
{
SvgReader svg_reader;
auto svg_doc = svg_reader.read(TestUtils::getTestDataDir() / "circles.svg");
SvgWriter svg_writer;
svg_writer.toFile(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodeConverter.svg", svg_doc.get());
TestRenderer renderer;
auto svg_node = std::make_unique<SvgNode>(Point(0.0, 0.0));
svg_node->setContent(std::move(svg_doc));
renderer.getScene()->addNode(svg_node.get());
renderer.write(TestUtils::getTestOutputDir(__FILE__) / "TestSvgToNodeConverter.png");
}

View file

@ -5,7 +5,7 @@
#include "AbstractPainter.h" #include "AbstractPainter.h"
#include "Scene.h" #include "Scene.h"
#include "SvgConverter.h" #include "SvgPainter.h"
#include "SvgWriter.h" #include "SvgWriter.h"
#include "SvgDocument.h" #include "SvgDocument.h"
@ -25,6 +25,8 @@ public:
mDrawingContext = std::make_unique<DrawingContext>(mSurface.get()); mDrawingContext = std::make_unique<DrawingContext>(mSurface.get());
} }
Scene* getScene() const Scene* getScene() const
{ {
return mSurface->getScene(); return mSurface->getScene();
@ -47,8 +49,8 @@ public:
static void writeSvg(const Path& path, Scene* scene) static void writeSvg(const Path& path, Scene* scene)
{ {
SvgConverter converter; SvgPainter painter;
auto svg_document = converter.convert(scene); auto svg_document = painter.paint(scene);
SvgWriter writer; SvgWriter writer;
auto svg_content = writer.toString(svg_document.get()); auto svg_content = writer.toString(svg_document.get());