Initial svg support.

This commit is contained in:
James Grogan 2022-12-07 20:58:45 +00:00
parent 101bfb4207
commit 65ac927929
22 changed files with 284 additions and 30 deletions

View file

@ -9,6 +9,8 @@ list(APPEND publishing_HEADERS
pdf/PdfStream.h pdf/PdfStream.h
pdf/PdfXRefTable.h pdf/PdfXRefTable.h
pdf/PdfWriter.h pdf/PdfWriter.h
plotting/GraphPlotter.h
plotting/SvgConverter.h
) )
list(APPEND publishing_LIB_INCLUDES list(APPEND publishing_LIB_INCLUDES
@ -22,6 +24,8 @@ list(APPEND publishing_LIB_INCLUDES
pdf/PdfStream.cpp pdf/PdfStream.cpp
pdf/PdfXRefTable.cpp pdf/PdfXRefTable.cpp
pdf/PdfWriter.cpp pdf/PdfWriter.cpp
plotting/GraphPlotter.h
plotting/SvgConverter.h
DocumentConverter.cpp DocumentConverter.cpp
) )
@ -32,6 +36,6 @@ target_include_directories(publishing PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/pdf" "${CMAKE_CURRENT_SOURCE_DIR}/pdf"
) )
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) target_link_libraries( publishing PUBLIC core web graphics visual_elements)
set_property(TARGET publishing PROPERTY FOLDER src) set_property(TARGET publishing PROPERTY FOLDER src)

View file

View file

View file

View file

View file

@ -27,6 +27,11 @@ list(APPEND web_LIB_INCLUDES
html/elements/HtmlHeadElement.cpp html/elements/HtmlHeadElement.cpp
html/elements/HtmlBodyElement.cpp html/elements/HtmlBodyElement.cpp
html/elements/HtmlParagraphElement.cpp html/elements/HtmlParagraphElement.cpp
svg/SvgDocument.cpp
svg/SvgWriter.cpp
svg/SvgShapeElement.cpp
svg/SvgElement.cpp
svg/elements/SvgShapeElements.cpp
) )
# add the executable # add the executable
@ -34,6 +39,8 @@ add_library(${MODULE_NAME} SHARED ${web_LIB_INCLUDES})
target_include_directories(web PUBLIC target_include_directories(web PUBLIC
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/svg
${CMAKE_CURRENT_SOURCE_DIR}/svg/elements
${CMAKE_CURRENT_SOURCE_DIR}/xml ${CMAKE_CURRENT_SOURCE_DIR}/xml
${CMAKE_CURRENT_SOURCE_DIR}/xml/xml-elements ${CMAKE_CURRENT_SOURCE_DIR}/xml/xml-elements
${CMAKE_CURRENT_SOURCE_DIR}/html ${CMAKE_CURRENT_SOURCE_DIR}/html
@ -41,5 +48,5 @@ target_include_directories(web PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/markdown ${CMAKE_CURRENT_SOURCE_DIR}/markdown
) )
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src) set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER src)
target_link_libraries(${MODULE_NAME} PUBLIC core compiler) target_link_libraries(${MODULE_NAME} PUBLIC core compiler geometry)
set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) set_target_properties( ${MODULE_NAME} PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON )

View file

@ -5,9 +5,8 @@ std::string HtmlParagraphElement::toString(unsigned depth, bool keepInline) cons
const auto prefix = std::string(2*depth, ' '); const auto prefix = std::string(2*depth, ' ');
auto content = prefix + "<" + getTagName(); auto content = prefix + "<" + getTagName();
for (std::size_t idx=0; idx< getNumAttributes(); idx++) for (const auto& [key, attribute] : getAttributes())
{ {
auto attribute = getAttribute(idx);
content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\""; content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\"";
} }

View file

@ -0,0 +1,28 @@
#include "SvgDocument.h"
#include "XmlAttribute.h"
#include <sstream>
SvgDocument::SvgDocument()
: XmlDocument()
{
auto root = XmlElement::Create("svg");
auto xmlns = std::make_unique<XmlAttribute>("xmlns");
xmlns->setValue("http://www.w3.org/2000/svg");
root->addAttribute(std::move(xmlns));
setRoot(std::move(root));
}
void SvgDocument::setViewBox(unsigned x, unsigned y, unsigned w, unsigned h)
{
auto viewbox = std::make_unique<XmlAttribute>("viewBox");
std::stringstream sstr;
sstr << x << " " << y << " " << w << " " << h;
viewbox->setValue(sstr.str());
getRoot()->addAttribute(std::move(viewbox));
}

View file

@ -0,0 +1,10 @@
#pragma once
#include "XmlDocument.h"
class SvgDocument : public XmlDocument
{
public:
SvgDocument();
void setViewBox(unsigned x, unsigned y, unsigned w, unsigned h);
};

View file

@ -0,0 +1,7 @@
#include "SvgElement.h"
SvgElement::SvgElement(const std::string& tagName)
: XmlElement(tagName)
{
}

9
src/web/svg/SvgElement.h Normal file
View file

@ -0,0 +1,9 @@
#pragma once
#include "XmlElement.h"
class SvgElement : public XmlElement
{
public:
SvgElement(const std::string& tagName);
};

View file

@ -0,0 +1,41 @@
#include "SvgShapeElement.h"
#include "XmlAttribute.h"
#include <sstream>
SvgShapeElement::SvgShapeElement(const std::string& tagName)
: SvgElement(tagName)
{
}
void SvgShapeElement::setFill(const Color& fill)
{
auto attr = std::make_unique<XmlAttribute>("fill");
std::stringstream sstr;
sstr << "rgb(" << fill.getR() << "," << fill.getG() << "," << fill.getB() << ")";
attr->setValue(sstr.str());
addAttribute(std::move(attr));
}
void SvgShapeElement::setStrokeWidth(unsigned width)
{
auto attr = std::make_unique<XmlAttribute>("stroke-width");
attr->setValue(std::to_string(width));
addAttribute(std::move(attr));
}
void SvgShapeElement::setStrokeColor(const Color& stroke)
{
auto attr = std::make_unique<XmlAttribute>("stroke");
std::stringstream sstr;
sstr << "rgb(" << stroke.getR() << "," << stroke.getG() << "," << stroke.getB() << ")";
attr->setValue(sstr.str());
addAttribute(std::move(attr));
}

View file

@ -0,0 +1,17 @@
#pragma once
#include "SvgElement.h"
#include "Color.h"
class SvgShapeElement : public SvgElement
{
public:
SvgShapeElement(const std::string& tagName);
void setFill(const Color& fill);
void setStrokeWidth(unsigned width);
void setStrokeColor(const Color& stroke);
};

View file

@ -0,0 +1,13 @@
#include "SvgWriter.h"
#include "SvgDocument.h"
std::string SvgWriter::toString(SvgDocument* document)
{
std::string content = "";
if (auto root = document->getRoot())
{
content += root->toString();
}
return content;
}

View file

@ -0,0 +1,11 @@
#pragma once
#include <string>
class SvgDocument;
class SvgWriter
{
public:
std::string toString(SvgDocument* document);
};

View file

@ -0,0 +1,62 @@
#include "SvgShapeElements.h"
SvgCircle::SvgCircle()
: SvgShapeElement("circle")
{
}
void SvgCircle::setLocation(const DiscretePoint& loc)
{
auto cx = std::make_unique<XmlAttribute>("cx");
auto cy = std::make_unique<XmlAttribute>("cy");
cx->setValue(std::to_string(loc.GetX()));
cy->setValue(std::to_string(loc.GetY()));
addAttribute(std::move(cx));
addAttribute(std::move(cy));
}
void SvgCircle::setRadius(unsigned rad)
{
auto r = std::make_unique<XmlAttribute>("r");
r->setValue(std::to_string(rad));
addAttribute(std::move(r));
}
SvgRectangle::SvgRectangle()
: SvgShapeElement("rect")
{
}
void SvgRectangle::setLocation(const DiscretePoint& loc)
{
auto x = std::make_unique<XmlAttribute>("x");
auto y = std::make_unique<XmlAttribute>("y");
x->setValue(std::to_string(loc.GetX()));
y->setValue(std::to_string(loc.GetY()));
addAttribute(std::move(x));
addAttribute(std::move(y));
}
void SvgRectangle::setWidth(unsigned w)
{
auto width = std::make_unique<XmlAttribute>("width");
width->setValue(std::to_string(w));
addAttribute(std::move(width));
}
void SvgRectangle::setHeight(unsigned h)
{
auto height = std::make_unique<XmlAttribute>("height");
height->setValue(std::to_string(h));
addAttribute(std::move(height));
}

View file

@ -0,0 +1,28 @@
#pragma once
#include "SvgShapeElement.h"
#include "DiscretePoint.h"
#include "XmlAttribute.h"
class SvgCircle : public SvgShapeElement
{
public:
SvgCircle();
void setLocation(const DiscretePoint& loc);
void setRadius(unsigned rad);
};
class SvgRectangle : public SvgShapeElement
{
public:
SvgRectangle();
void setLocation(const DiscretePoint& loc);
void setWidth(unsigned width);
void setHeight(unsigned height);
};

View file

@ -9,9 +9,8 @@ std::string XmlWriter::toString(XmlDocument* document)
if (auto prolog = document->getProlog()) if (auto prolog = document->getProlog())
{ {
content += "<?xml"; content += "<?xml";
for (std::size_t idx=0; idx< prolog->getNumAttributes(); idx++) for (const auto& [key, attribute] : prolog->getAttributes())
{ {
auto attribute = prolog->getAttribute(idx);
content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\""; content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\"";
} }
content += "?>\n"; content += "?>\n";

View file

@ -31,7 +31,7 @@ void XmlElement::addChild(XmlElementPtr child)
void XmlElement::addAttribute(XmlAttributePtr attribute) void XmlElement::addAttribute(XmlAttributePtr attribute)
{ {
mAttributes.push_back(std::move(attribute)); mAttributes[attribute->getName()] = std::move(attribute);
} }
const std::string& XmlElement::getTagName() const const std::string& XmlElement::getTagName() const
@ -64,28 +64,16 @@ XmlElement* XmlElement::getFirstChildWithTagName(const std::string& tag)
XmlAttribute* XmlElement::getAttribute(const std::string& attributeName) const XmlAttribute* XmlElement::getAttribute(const std::string& attributeName) const
{ {
for(const auto& attribute : mAttributes) if (auto iter = mAttributes.find(attributeName); iter != mAttributes.end())
{ {
if(attribute->getName() == attributeName) return iter->second.get();
{
return attribute.get();
}
} }
return nullptr; return nullptr;
} }
XmlAttribute* XmlElement::getAttribute(std::size_t index) const const std::unordered_map<std::string, XmlAttributePtr>& XmlElement::getAttributes() const
{ {
if(index < mAttributes.size()) return mAttributes;
{
return mAttributes[index].get();
}
return nullptr;
}
std::size_t XmlElement::getNumAttributes() const
{
return mAttributes.size();
} }
std::size_t XmlElement::getNumChildren() const std::size_t XmlElement::getNumChildren() const
@ -105,9 +93,8 @@ std::string XmlElement::toString(unsigned depth, bool keepInline) const
std::string line_ending = keepInline ? "" : "\n"; std::string line_ending = keepInline ? "" : "\n";
auto content = prefix + "<" + getTagName(); auto content = prefix + "<" + getTagName();
for (std::size_t idx=0; idx< getNumAttributes(); idx++) for (const auto& [key, attribute] : getAttributes())
{ {
auto attribute = getAttribute(idx);
content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\""; content += " " + attribute->getName() + "=\"" + attribute->getValue() + "\"";
} }

View file

@ -1,10 +1,9 @@
#pragma once #pragma once
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <string> #include <string>
#include <unordered_map>
class XmlAttribute; class XmlAttribute;
using XmlAttributePtr = std::unique_ptr<XmlAttribute>; using XmlAttributePtr = std::unique_ptr<XmlAttribute>;
@ -23,8 +22,7 @@ public:
const std::string& getTagName() const; const std::string& getTagName() const;
const std::string& getText() const; const std::string& getText() const;
XmlAttribute* getAttribute(const std::string& attribute) const; XmlAttribute* getAttribute(const std::string& attribute) const;
XmlAttribute* getAttribute(std::size_t index) const; const std::unordered_map<std::string, XmlAttributePtr>& getAttributes() const;
std::size_t getNumAttributes() const;
std::size_t getNumChildren() const; std::size_t getNumChildren() const;
XmlElement* getChild(std::size_t index) const; XmlElement* getChild(std::size_t index) const;
@ -40,7 +38,7 @@ protected:
std::string mTagName; std::string mTagName;
std::string mText; std::string mText;
std::vector<XmlAttributePtr> mAttributes; std::unordered_map<std::string, XmlAttributePtr> mAttributes;
std::vector<std::unique_ptr<XmlElement> > mChildren; std::vector<std::unique_ptr<XmlElement> > mChildren;
}; };

View file

@ -1,6 +1,7 @@
set(WEB_UNIT_TEST_FILES set(WEB_UNIT_TEST_FILES
web/TestMarkdownParser.cpp web/TestMarkdownParser.cpp
web/TestXmlParser.cpp web/TestXmlParser.cpp
web/TestSvgWriter.cpp
PARENT_SCOPE PARENT_SCOPE
) )

View file

@ -0,0 +1,33 @@
#include "SvgWriter.h"
#include "SvgDocument.h"
#include "SvgShapeElements.h"
#include "File.h"
#include "TestFramework.h"
#include "TestUtils.h"
TEST_CASE(TestSvgWriter, "web")
{
auto document = std::make_unique<SvgDocument>();
document->setViewBox(0, 0, 100, 100);
auto circle = std::make_unique<SvgCircle>();
circle->setRadius(20);
circle->setLocation({10, 30});
circle->setFill({255, 0, 0});
document->getRoot()->addChild(std::move(circle));
auto rectangle = std::make_unique<SvgRectangle>();
rectangle->setLocation({50, 70});
rectangle->setWidth(5);
rectangle->setHeight(10);
rectangle->setFill({0, 0, 255});
document->getRoot()->addChild(std::move(rectangle));
SvgWriter writer;
auto content = writer.toString(document.get());
auto outFile = std::make_unique<File>(TestUtils::getTestOutputDir(__FILE__) / "test_out.svg");
outFile->writeText(content);
}