Add svg conversion.
This commit is contained in:
parent
1f85954e98
commit
dfbc87cb09
33 changed files with 602 additions and 79 deletions
|
@ -28,3 +28,16 @@ double Transform::getScaleZ() const
|
|||
{
|
||||
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;
|
||||
|
||||
}
|
|
@ -7,6 +7,10 @@ class Transform
|
|||
public:
|
||||
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;
|
||||
|
||||
double getScaleX() const;
|
||||
|
|
|
@ -89,3 +89,10 @@ void Point::apply(const Transform& transform)
|
|||
mY *= transform.getScaleY();
|
||||
mZ *= transform.getScaleZ();
|
||||
}
|
||||
|
||||
void Point::move(double x, double y, double z)
|
||||
{
|
||||
mX += x;
|
||||
mY += y;
|
||||
mZ += z;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ public:
|
|||
|
||||
void apply(const Transform& transform);
|
||||
|
||||
void move(double x, double y, double z = 0);
|
||||
|
||||
double getX() const;
|
||||
|
||||
double getY() const;
|
||||
|
|
|
@ -10,12 +10,13 @@ list(APPEND publishing_HEADERS
|
|||
pdf/PdfXRefTable.h
|
||||
pdf/PdfWriter.h
|
||||
plotting/GraphPlotter.h
|
||||
plotting/SvgConverter.h
|
||||
plotting/PlotNode.h
|
||||
plotting/EquationNode.h
|
||||
latex/LatexDocument.h
|
||||
latex/LatexMathExpression.h
|
||||
latex/LatexSymbols.h
|
||||
svg/SvgNode.h
|
||||
svg/SvgPainter.h
|
||||
DocumentConverter.h
|
||||
)
|
||||
|
||||
|
@ -34,10 +35,11 @@ list(APPEND publishing_LIB_INCLUDES
|
|||
latex/LatexMathExpression.cpp
|
||||
latex/LatexSymbols.cpp
|
||||
plotting/GraphPlotter.h
|
||||
plotting/SvgConverter.cpp
|
||||
plotting/PlotNode.cpp
|
||||
plotting/EquationNode.cpp
|
||||
DocumentConverter.cpp
|
||||
svg/SvgNode.cpp
|
||||
svg/SvgPainter.cpp
|
||||
)
|
||||
|
||||
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}/plotting
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/latex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/svg
|
||||
)
|
||||
set_target_properties( publishing PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )
|
||||
target_link_libraries( publishing PUBLIC core web graphics visual_elements)
|
||||
|
|
|
@ -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;
|
||||
};
|
90
src/publishing/svg/SvgNode.cpp
Normal file
90
src/publishing/svg/SvgNode.cpp
Normal 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;
|
||||
}
|
||||
}
|
25
src/publishing/svg/SvgNode.h
Normal file
25
src/publishing/svg/SvgNode.h
Normal 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;
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
#include "SvgConverter.h"
|
||||
#include "SvgPainter.h"
|
||||
|
||||
#include "SvgDocument.h"
|
||||
#include "Scene.h"
|
||||
|
@ -17,38 +17,38 @@
|
|||
#include "SvgShapeElements.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>();
|
||||
doc->setViewBox(0.0, 0.0, width, height);
|
||||
|
||||
scene->update();
|
||||
|
||||
for(auto item : scene->getItems())
|
||||
for (auto item : scene->getItems())
|
||||
{
|
||||
if (item->getType() == SceneItem::Type::MODEL)
|
||||
{
|
||||
auto model = dynamic_cast<SceneModel*>(item);
|
||||
if (model->getGeometry())
|
||||
{
|
||||
convertPrimitive(doc.get(), model);
|
||||
paintPrimitive(doc.get(), model);
|
||||
}
|
||||
else
|
||||
{
|
||||
convertMesh(doc.get(), model, scene->shouldShowMeshOutline());
|
||||
paintMesh(doc.get(), model, scene->shouldShowMeshOutline());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto text = dynamic_cast<SceneText*>(item);
|
||||
convertText(doc.get(), text);
|
||||
paintText(doc.get(), text);
|
||||
}
|
||||
}
|
||||
|
||||
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 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();
|
||||
|
||||
|
@ -111,7 +111,7 @@ void SvgConverter::setStyle(SceneModel* model, SvgShapeElement* element) const
|
|||
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)
|
||||
{
|
||||
|
@ -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");
|
||||
|
32
src/publishing/svg/SvgPainter.h
Normal file
32
src/publishing/svg/SvgPainter.h
Normal 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;
|
||||
};
|
|
@ -3,6 +3,8 @@
|
|||
#include "DirectX2dInterface.h"
|
||||
#include "AbstractGeometricItem.h"
|
||||
#include "Rectangle.h"
|
||||
#include "Circle.h"
|
||||
|
||||
#include "SceneModel.h"
|
||||
|
||||
void DirectX2dPainter::startDrawing()
|
||||
|
@ -49,6 +51,25 @@ void DirectX2dPainter::paint(SceneModel* model)
|
|||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
76
src/web/svg/SvgReader.cpp
Normal 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
20
src/web/svg/SvgReader.h
Normal 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;
|
||||
};
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
5
test/data/circles.svg
Normal file
5
test/data/circles.svg
Normal 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 |
|
@ -34,11 +34,9 @@ TEST_CASE(TestD2dWidgetRendering, "graphics")
|
|||
auto scene = gui_app->getMainWindowScene();
|
||||
|
||||
Widget widget;
|
||||
widget.setBackgroundColor({ 0, 200, 0 });
|
||||
widget.setBounds(300, 300);
|
||||
|
||||
auto button = Button::Create();
|
||||
button->setBackgroundColor({ 200, 0, 0 });
|
||||
button->setLabel("Test Button");
|
||||
button->setMaxWidth(100);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ set(PUBLISHING_UNIT_TEST_FILES
|
|||
publishing/TestPdfWriter.cpp
|
||||
publishing/TestDocumentConverter.cpp
|
||||
publishing/TestSvgConverter.cpp
|
||||
publishing/TestSvgToNodeConverter.cpp
|
||||
publishing/TestLatexConverter.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "SvgWriter.h"
|
||||
#include "SvgDocument.h"
|
||||
#include "SvgConverter.h"
|
||||
#include "SvgPainter.h"
|
||||
#include "Scene.h"
|
||||
|
||||
#include "CircleNode.h"
|
||||
|
@ -24,13 +24,10 @@ TEST_CASE(TestSvgConverter, "[publishing]")
|
|||
//rectangle.setFillColor({255, 0, 0});
|
||||
//scene.addNode(&rectangle);
|
||||
|
||||
SvgConverter converter;
|
||||
auto svg_document = converter.convert(&scene);
|
||||
SvgPainter painter;
|
||||
auto svg_document = painter.paint(&scene);
|
||||
svg_document->setViewBox(0, 0, 200, 200);
|
||||
|
||||
SvgWriter writer;
|
||||
auto content = writer.toString(svg_document.get());
|
||||
|
||||
auto outFile = std::make_unique<File>(TestUtils::getTestOutputDir(__FILE__) / "scene.svg");
|
||||
outFile->writeText(content);
|
||||
writer.toFile(TestUtils::getTestOutputDir(__FILE__) / "scene.svg", svg_document.get());
|
||||
}
|
||||
|
|
25
test/publishing/TestSvgToNodeConverter.cpp
Normal file
25
test/publishing/TestSvgToNodeConverter.cpp
Normal 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");
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
#include "AbstractPainter.h"
|
||||
#include "Scene.h"
|
||||
|
||||
#include "SvgConverter.h"
|
||||
#include "SvgPainter.h"
|
||||
#include "SvgWriter.h"
|
||||
#include "SvgDocument.h"
|
||||
|
||||
|
@ -25,6 +25,8 @@ public:
|
|||
mDrawingContext = std::make_unique<DrawingContext>(mSurface.get());
|
||||
}
|
||||
|
||||
|
||||
|
||||
Scene* getScene() const
|
||||
{
|
||||
return mSurface->getScene();
|
||||
|
@ -47,8 +49,8 @@ public:
|
|||
|
||||
static void writeSvg(const Path& path, Scene* scene)
|
||||
{
|
||||
SvgConverter converter;
|
||||
auto svg_document = converter.convert(scene);
|
||||
SvgPainter painter;
|
||||
auto svg_document = painter.paint(scene);
|
||||
|
||||
SvgWriter writer;
|
||||
auto svg_content = writer.toString(svg_document.get());
|
||||
|
|
Loading…
Reference in a new issue