#include "lunasvg.h" #include "svgelement.h" #include "svgrenderstate.h" #include #include #include int lunasvg_version() { return LUNASVG_VERSION; } const char* lunasvg_version_string() { return LUNASVG_VERSION_STRING; } bool lunasvg_add_font_face_from_file(const char* family, bool bold, bool italic, const char* filename) { return lunasvg::fontFaceCache()->addFontFace(family, bold, italic, lunasvg::FontFace(filename)); } bool lunasvg_add_font_face_from_data(const char* family, bool bold, bool italic, const void* data, size_t length, lunasvg_destroy_func_t destroy_func, void* closure) { return lunasvg::fontFaceCache()->addFontFace(family, bold, italic, lunasvg::FontFace(data, length, destroy_func, closure)); } namespace lunasvg { Bitmap::Bitmap(int width, int height) : m_surface(plutovg_surface_create(width, height)) { } Bitmap::Bitmap(uint8_t* data, int width, int height, int stride) : m_surface(plutovg_surface_create_for_data(data, width, height, stride)) { } Bitmap::Bitmap(const Bitmap& bitmap) : m_surface(plutovg_surface_reference(bitmap.surface())) { } Bitmap::Bitmap(Bitmap&& bitmap) : m_surface(bitmap.release()) { } Bitmap::~Bitmap() { plutovg_surface_destroy(m_surface); } Bitmap& Bitmap::operator=(const Bitmap& bitmap) { Bitmap(bitmap).swap(*this); return *this; } void Bitmap::swap(Bitmap& bitmap) { std::swap(m_surface, bitmap.m_surface); } uint8_t* Bitmap::data() const { if(m_surface) return plutovg_surface_get_data(m_surface); return nullptr; } int Bitmap::width() const { if(m_surface) return plutovg_surface_get_width(m_surface); return 0; } int Bitmap::height() const { if(m_surface) return plutovg_surface_get_height(m_surface); return 0; } int Bitmap::stride() const { if(m_surface) return plutovg_surface_get_stride(m_surface); return 0; } void Bitmap::clear(uint32_t value) { if(m_surface == nullptr) return; plutovg_color_t color; plutovg_color_init_rgba32(&color, value); plutovg_surface_clear(m_surface, &color); } void Bitmap::convertToRGBA() { if(m_surface == nullptr) return; auto data = plutovg_surface_get_data(m_surface); auto width = plutovg_surface_get_width(m_surface); auto height = plutovg_surface_get_height(m_surface); auto stride = plutovg_surface_get_stride(m_surface); plutovg_convert_argb_to_rgba(data, data, width, height, stride); } Bitmap& Bitmap::operator=(Bitmap&& bitmap) { Bitmap(std::move(bitmap)).swap(*this); return *this; } bool Bitmap::writeToPng(const std::string& filename) const { if(m_surface) return plutovg_surface_write_to_png(m_surface, filename.data()); return false; } bool Bitmap::writeToPng(lunasvg_write_func_t callback, void* closure) const { if(m_surface) return plutovg_surface_write_to_png_stream(m_surface, callback, closure); return false; } plutovg_surface_t* Bitmap::release() { return std::exchange(m_surface, nullptr); } Box::Box(float x, float y, float w, float h) : x(x), y(y), w(w), h(h) { } Box::Box(const Rect& rect) : x(rect.x), y(rect.y), w(rect.w), h(rect.h) { } Box& Box::transform(const Matrix &matrix) { *this = transformed(matrix); return *this; } Box Box::transformed(const Matrix& matrix) const { return Transform(matrix).mapRect(*this); } Matrix::Matrix(float a, float b, float c, float d, float e, float f) : a(a), b(b), c(c), d(d), e(e), f(f) { } Matrix::Matrix(const plutovg_matrix_t& matrix) : a(matrix.a), b(matrix.b), c(matrix.c), d(matrix.d), e(matrix.e), f(matrix.f) { } Matrix::Matrix(const Transform& transform) : Matrix(transform.matrix()) { } Matrix Matrix::operator*(const Matrix& matrix) const { return Transform(*this) * Transform(matrix); } Matrix& Matrix::operator*=(const Matrix &matrix) { return (*this = *this * matrix); } Matrix& Matrix::multiply(const Matrix& matrix) { return (*this *= matrix); } Matrix& Matrix::scale(float sx, float sy) { return multiply(scaled(sx, sy)); } Matrix& Matrix::translate(float tx, float ty) { return multiply(translated(tx, ty)); } Matrix& Matrix::rotate(float angle, float cx, float cy) { return multiply(rotated(angle, cx, cy)); } Matrix& Matrix::shear(float shx, float shy) { return multiply(sheared(shx, shy)); } Matrix Matrix::inverse() const { return Transform(*this).inverse(); } Matrix& Matrix::invert() { return (*this = inverse()); } void Matrix::reset() { *this = Matrix(1, 0, 0, 1, 0, 0); } Matrix Matrix::translated(float tx, float ty) { return Transform::translated(tx, ty); } Matrix Matrix::scaled(float sx, float sy) { return Transform::scaled(sx, sy); } Matrix Matrix::rotated(float angle, float cx, float cy) { return Transform::rotated(angle, cx, cy); } Matrix Matrix::sheared(float shx, float shy) { return Transform::sheared(shx, shy); } Node::Node(SVGNode* node) : m_node(node) { } bool Node::isTextNode() const { return m_node && m_node->isTextNode(); } bool Node::isElement() const { return m_node && m_node->isElement(); } TextNode Node::toTextNode() const { if(m_node && m_node->isTextNode()) return static_cast(m_node); return TextNode(); } Element Node::toElement() const { if(m_node && m_node->isElement()) return static_cast(m_node); return Element(); } Element Node::parentElement() const { if(m_node) return m_node->parentElement(); return Element(); } TextNode::TextNode(SVGTextNode* text) : Node(text) { } const std::string& TextNode::data() const { if(m_node) return text()->data(); return emptyString; } void TextNode::setData(const std::string& data) { if(m_node) { text()->setData(data); } } SVGTextNode* TextNode::text() const { return static_cast(m_node); } Element::Element(SVGElement* element) : Node(element) { } bool Element::hasAttribute(const std::string& name) const { if(m_node) return element()->hasAttribute(name); return false; } const std::string& Element::getAttribute(const std::string& name) const { if(m_node) return element()->getAttribute(name); return emptyString; } void Element::setAttribute(const std::string& name, const std::string& value) { if(m_node) { element()->setAttribute(name, value); } } void Element::render(Bitmap& bitmap, const Matrix& matrix) const { if(m_node == nullptr || bitmap.isNull()) return; auto canvas = Canvas::create(bitmap); SVGRenderState state(nullptr, nullptr, matrix, SVGRenderMode::Painting, canvas); element(true)->render(state); } Bitmap Element::renderToBitmap(int width, int height, uint32_t backgroundColor) const { if(m_node == nullptr) return Bitmap(); auto elementBounds = element(true)->localTransform().mapRect(element()->paintBoundingBox()); if(elementBounds.isEmpty()) return Bitmap(); if(width <= 0 && height <= 0) { width = static_cast(std::ceil(elementBounds.w)); height = static_cast(std::ceil(elementBounds.h)); } else if(width > 0 && height <= 0) { height = static_cast(std::ceil(width * elementBounds.h / elementBounds.w)); } else if(height > 0 && width <= 0) { width = static_cast(std::ceil(height * elementBounds.w / elementBounds.h)); } auto xScale = width / elementBounds.w; auto yScale = height / elementBounds.h; Matrix matrix(xScale, 0, 0, yScale, -elementBounds.x * xScale, -elementBounds.y * yScale); Bitmap bitmap(width, height); if(backgroundColor) bitmap.clear(backgroundColor); render(bitmap, matrix); return bitmap; } Matrix Element::getLocalMatrix() const { if(m_node) return element(true)->localTransform(); return Matrix(); } Matrix Element::getGlobalMatrix() const { if(m_node == nullptr) return Matrix(); auto transform = element(true)->localTransform(); for(auto parent = element()->parentElement(); parent; parent = parent->parentElement()) transform.postMultiply(parent->localTransform()); return transform; } Box Element::getLocalBoundingBox() const { return getBoundingBox().transformed(getLocalMatrix()); } Box Element::getGlobalBoundingBox() const { return getBoundingBox().transformed(getGlobalMatrix()); } Box Element::getBoundingBox() const { if(m_node) return element(true)->paintBoundingBox(); return Box(); } NodeList Element::children() const { if(m_node == nullptr) return NodeList(); NodeList children; for(const auto& child : element()->children()) children.push_back(child.get()); return children; } SVGElement* Element::element(bool layoutIfNeeded) const { auto element = static_cast(m_node); if(element && layoutIfNeeded) element->rootElement()->layoutIfNeeded(); return element; } std::unique_ptr Document::loadFromFile(const std::string& filename) { std::ifstream fs; fs.open(filename); if(!fs.is_open()) return nullptr; std::string content; std::getline(fs, content, '\0'); fs.close(); return loadFromData(content); } std::unique_ptr Document::loadFromData(const std::string& string) { return loadFromData(string.data(), string.size()); } std::unique_ptr Document::loadFromData(const char* data) { return loadFromData(data, std::strlen(data)); } std::unique_ptr Document::loadFromData(const char* data, size_t length) { std::unique_ptr document(new Document); if(!document->parse(data, length)) return nullptr; return document; } float Document::width() const { return rootElement(true)->intrinsicWidth(); } float Document::height() const { return rootElement(true)->intrinsicHeight(); } Box Document::boundingBox() const { return rootElement(true)->localTransform().mapRect(rootElement()->paintBoundingBox()); } void Document::updateLayout() { m_rootElement->layoutIfNeeded(); } void Document::forceLayout() { m_rootElement->forceLayout(); } void Document::render(Bitmap& bitmap, const Matrix& matrix) const { if(bitmap.isNull()) return; auto canvas = Canvas::create(bitmap); SVGRenderState state(nullptr, nullptr, matrix, SVGRenderMode::Painting, canvas); rootElement(true)->render(state); } Bitmap Document::renderToBitmap(int width, int height, uint32_t backgroundColor) const { auto intrinsicWidth = rootElement(true)->intrinsicWidth(); auto intrinsicHeight = rootElement()->intrinsicHeight(); if(intrinsicWidth == 0.f || intrinsicHeight == 0.f) return Bitmap(); if(width <= 0 && height <= 0) { width = static_cast(std::ceil(intrinsicWidth)); height = static_cast(std::ceil(intrinsicHeight)); } else if(width > 0 && height <= 0) { height = static_cast(std::ceil(width * intrinsicHeight / intrinsicWidth)); } else if(height > 0 && width <= 0) { width = static_cast(std::ceil(height * intrinsicWidth / intrinsicHeight)); } auto xScale = width / intrinsicWidth; auto yScale = height / intrinsicHeight; Matrix matrix(xScale, 0, 0, yScale, 0, 0); Bitmap bitmap(width, height); if(backgroundColor) bitmap.clear(backgroundColor); render(bitmap, matrix); return bitmap; } Element Document::elementFromPoint(float x, float y) const { return rootElement(true)->elementFromPoint(x, y); } Element Document::getElementById(const std::string& id) const { return m_rootElement->getElementById(id); } Element Document::documentElement() const { return m_rootElement.get(); } SVGRootElement* Document::rootElement(bool layoutIfNeeded) const { if(layoutIfNeeded) m_rootElement->layoutIfNeeded(); return m_rootElement.get(); } Document::Document(Document&&) = default; Document& Document::operator=(Document&&) = default; Document::Document() = default; Document::~Document() = default; } // namespace lunasvg