From 41f21ccb1787e04e25245290233172353d817c3d Mon Sep 17 00:00:00 2001 From: sammycage Date: Sun, 9 Oct 2022 12:46:54 +0100 Subject: [PATCH] Release v2.3.3 --- CMakeLists.txt | 4 +- README.md | 2 +- source/canvas.cpp | 12 +- source/clippathelement.cpp | 4 +- source/defselement.cpp | 2 +- source/element.cpp | 24 +- source/element.h | 27 +- source/gelement.cpp | 2 +- source/geometryelement.cpp | 56 +-- source/geometryelement.h | 4 +- source/graphicselement.cpp | 4 +- source/graphicselement.h | 2 +- source/layoutcontext.cpp | 12 +- source/layoutcontext.h | 6 +- source/lunasvg.cpp | 6 +- source/markerelement.cpp | 18 +- source/maskelement.cpp | 14 +- source/paintelement.cpp | 120 +++--- source/paintelement.h | 4 +- source/parser.cpp | 792 +++++++++++++++++++------------------ source/parser.h | 105 +++-- source/property.cpp | 23 +- source/property.h | 26 +- source/stopelement.cpp | 6 +- source/styledelement.cpp | 54 +-- source/styledelement.h | 2 +- source/styleelement.cpp | 2 +- source/svgelement.cpp | 18 +- source/svgelement.h | 4 +- source/symbolelement.cpp | 14 +- source/useelement.cpp | 26 +- 31 files changed, 718 insertions(+), 677 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9bf46a..3e28cd3 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.3) -project(lunasvg VERSION 2.3.2 LANGUAGES CXX C) +project(lunasvg VERSION 2.3.3 LANGUAGES CXX C) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_C_STANDARD 11) option(BUILD_SHARED_LIBS "Builds as shared library" OFF) diff --git a/README.md b/README.md index 54ae043..40cb82c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Releases](https://img.shields.io/badge/Version-2.3.2-orange.svg)](https://github.com/sammycage/lunasvg/releases) +[![Releases](https://img.shields.io/badge/Version-2.3.3-orange.svg)](https://github.com/sammycage/lunasvg/releases) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/sammycage/lunasvg/blob/master/LICENSE) [![Build Status](https://github.com/sammycage/lunasvg/actions/workflows/ci.yml/badge.svg)](https://github.com/sammycage/lunasvg/actions) diff --git a/source/canvas.cpp b/source/canvas.cpp index 6311ffc..32db99a 100644 --- a/source/canvas.cpp +++ b/source/canvas.cpp @@ -9,7 +9,7 @@ static plutovg_fill_rule_t to_plutovg_fill_rule(WindRule winding); static plutovg_operator_t to_plutovg_operator(BlendMode mode); static plutovg_line_cap_t to_plutovg_line_cap(LineCap cap); static plutovg_line_join_t to_plutovg_line_join(LineJoin join); -static plutovg_spread_method_t to_plutovg_spread_methood(SpreadMethod spread); +static plutovg_spread_method_t to_plutovg_spread_method(SpreadMethod spread); static void to_plutovg_stops(plutovg_gradient_t* gradient, const GradientStops& stops); static void to_plutovg_path(plutovg_t* pluto, const Path& path); @@ -59,7 +59,7 @@ Canvas::~Canvas() void Canvas::setColor(const Color& color) { - plutovg_set_source_rgba(pluto, color.r, color.g, color.b, color.a); + plutovg_set_source_rgba(pluto, color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0, color.alpha() / 255.0); } void Canvas::setLinearGradient(double x1, double y1, double x2, double y2, const GradientStops& stops, SpreadMethod spread, const Transform& transform) @@ -67,7 +67,7 @@ void Canvas::setLinearGradient(double x1, double y1, double x2, double y2, const auto gradient = plutovg_gradient_create_linear(x1, y1, x2, y2); auto matrix = to_plutovg_matrix(transform); to_plutovg_stops(gradient, stops); - plutovg_gradient_set_spread(gradient, to_plutovg_spread_methood(spread)); + plutovg_gradient_set_spread(gradient, to_plutovg_spread_method(spread)); plutovg_gradient_set_matrix(gradient, &matrix); plutovg_set_source_gradient(pluto, gradient); plutovg_gradient_destroy(gradient); @@ -78,7 +78,7 @@ void Canvas::setRadialGradient(double cx, double cy, double r, double fx, double auto gradient = plutovg_gradient_create_radial(cx, cy, r, fx, fy, 0); auto matrix = to_plutovg_matrix(transform); to_plutovg_stops(gradient, stops); - plutovg_gradient_set_spread(gradient, to_plutovg_spread_methood(spread)); + plutovg_gradient_set_spread(gradient, to_plutovg_spread_method(spread)); plutovg_gradient_set_matrix(gradient, &matrix); plutovg_set_source_gradient(pluto, gradient); plutovg_gradient_destroy(gradient); @@ -227,7 +227,7 @@ plutovg_line_join_t to_plutovg_line_join(LineJoin join) return join == LineJoin::Miter ? plutovg_line_join_miter : join == LineJoin::Round ? plutovg_line_join_round : plutovg_line_join_bevel; } -static plutovg_spread_method_t to_plutovg_spread_methood(SpreadMethod spread) +static plutovg_spread_method_t to_plutovg_spread_method(SpreadMethod spread) { return spread == SpreadMethod::Pad ? plutovg_spread_method_pad : spread == SpreadMethod::Reflect ? plutovg_spread_method_reflect : plutovg_spread_method_repeat; } @@ -238,7 +238,7 @@ static void to_plutovg_stops(plutovg_gradient_t* gradient, const GradientStops& { auto offset = std::get<0>(stop); auto& color = std::get<1>(stop); - plutovg_gradient_add_stop_rgba(gradient, offset, color.r, color.g, color.b, color.a); + plutovg_gradient_add_stop_rgba(gradient, offset, color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0, color.alpha() / 255.0); } } diff --git a/source/clippathelement.cpp b/source/clippathelement.cpp index 9472b5c..25ea700 100644 --- a/source/clippathelement.cpp +++ b/source/clippathelement.cpp @@ -5,13 +5,13 @@ namespace lunasvg { ClipPathElement::ClipPathElement() - : GraphicsElement(ElementId::ClipPath) + : GraphicsElement(ElementID::ClipPath) { } Units ClipPathElement::clipPathUnits() const { - auto& value = get(PropertyId::ClipPathUnits); + auto& value = get(PropertyID::ClipPathUnits); return Parser::parseUnits(value, Units::UserSpaceOnUse); } diff --git a/source/defselement.cpp b/source/defselement.cpp index b39ddea..9402561 100644 --- a/source/defselement.cpp +++ b/source/defselement.cpp @@ -3,7 +3,7 @@ namespace lunasvg { DefsElement::DefsElement() - : GraphicsElement(ElementId::Defs) + : GraphicsElement(ElementID::Defs) { } diff --git a/source/element.cpp b/source/element.cpp index a3d3a31..518556f 100644 --- a/source/element.cpp +++ b/source/element.cpp @@ -4,7 +4,7 @@ namespace lunasvg { -void PropertyList::set(PropertyId id, const std::string& value, int specificity) +void PropertyList::set(PropertyID id, const std::string& value, int specificity) { auto property = get(id); if(property == nullptr) @@ -21,7 +21,7 @@ void PropertyList::set(PropertyId id, const std::string& value, int specificity) property->value = value; } -Property* PropertyList::get(PropertyId id) const +Property* PropertyList::get(PropertyID id) const { auto data = m_properties.data(); auto end = data + m_properties.size(); @@ -59,19 +59,19 @@ std::unique_ptr TextNode::clone() const return std::move(node); } -Element::Element(ElementId id) +Element::Element(ElementID id) : id(id) { } -void Element::set(PropertyId id, const std::string& value, int specificity) +void Element::set(PropertyID id, const std::string& value, int specificity) { properties.set(id, value, specificity); } static const std::string EmptyString; -const std::string& Element::get(PropertyId id) const +const std::string& Element::get(PropertyID id) const { auto property = properties.get(id); if(property == nullptr) @@ -82,7 +82,7 @@ const std::string& Element::get(PropertyId id) const static const std::string InheritString{"inherit"}; -const std::string& Element::find(PropertyId id) const +const std::string& Element::find(PropertyID id) const { auto element = this; do { @@ -95,12 +95,12 @@ const std::string& Element::find(PropertyId id) const return EmptyString; } -bool Element::has(PropertyId id) const +bool Element::has(PropertyID id) const { return properties.get(id); } -Element* Element::previousSibling() const +Element* Element::previousElement() const { if(parent == nullptr) return nullptr; @@ -122,7 +122,7 @@ Element* Element::previousSibling() const return nullptr; } -Element* Element::nextSibling() const +Element* Element::nextElement() const { if(parent == nullptr) return nullptr; @@ -162,15 +162,15 @@ Rect Element::currentViewport() const if(parent == nullptr) { auto element = static_cast(this); - if(element->has(PropertyId::ViewBox)) + if(element->has(PropertyID::ViewBox)) return element->viewBox(); return Rect{0, 0, 300, 150}; } - if(parent->id == ElementId::Svg) + if(parent->id == ElementID::Svg) { auto element = static_cast(parent); - if(element->has(PropertyId::ViewBox)) + if(element->has(PropertyID::ViewBox)) return element->viewBox(); LengthContext lengthContext(element); diff --git a/source/element.h b/source/element.h index f94f927..e6c6689 100644 --- a/source/element.h +++ b/source/element.h @@ -8,7 +8,7 @@ namespace lunasvg { -enum class ElementId +enum class ElementID { Unknown = 0, Star, @@ -35,7 +35,7 @@ enum class ElementId Use }; -enum class PropertyId +enum class PropertyID { Unknown = 0, Class, @@ -108,7 +108,7 @@ enum class PropertyId struct Property { - PropertyId id; + PropertyID id; std::string value; int specificity; }; @@ -118,10 +118,11 @@ class PropertyList public: PropertyList() = default; - void set(PropertyId id, const std::string& value, int specificity); - Property* get(PropertyId id) const; + void set(PropertyID id, const std::string& value, int specificity); + Property* get(PropertyID id) const; void add(const Property& property); void add(const PropertyList& properties); + void clear() { m_properties.clear(); } private: std::vector m_properties; @@ -164,15 +165,15 @@ using NodeList = std::list>; class Element : public Node { public: - Element(ElementId id); + Element(ElementID id); - void set(PropertyId id, const std::string& value, int specificity); - const std::string& get(PropertyId id) const; - const std::string& find(PropertyId id) const; - bool has(PropertyId id) const; + void set(PropertyID id, const std::string& value, int specificity); + const std::string& get(PropertyID id) const; + const std::string& find(PropertyID id) const; + bool has(PropertyID id) const; - Element* previousSibling() const; - Element* nextSibling() const; + Element* previousElement() const; + Element* nextElement() const; Node* addChild(std::unique_ptr child); void layoutChildren(LayoutContext* context, LayoutContainer* current) const; Rect currentViewport() const; @@ -208,7 +209,7 @@ class Element : public Node } public: - ElementId id; + ElementID id; NodeList children; PropertyList properties; }; diff --git a/source/gelement.cpp b/source/gelement.cpp index b0f5c84..2dc805b 100644 --- a/source/gelement.cpp +++ b/source/gelement.cpp @@ -4,7 +4,7 @@ namespace lunasvg { GElement::GElement() - : GraphicsElement(ElementId::G) + : GraphicsElement(ElementID::G) { } diff --git a/source/geometryelement.cpp b/source/geometryelement.cpp index c93b6d5..6f848ac 100644 --- a/source/geometryelement.cpp +++ b/source/geometryelement.cpp @@ -6,7 +6,7 @@ namespace lunasvg { -GeometryElement::GeometryElement(ElementId id) +GeometryElement::GeometryElement(ElementID id) : GraphicsElement(id) { } @@ -35,13 +35,13 @@ void GeometryElement::layout(LayoutContext* context, LayoutContainer* current) c } PathElement::PathElement() - : GeometryElement(ElementId::Path) + : GeometryElement(ElementID::Path) { } Path PathElement::d() const { - auto& value = get(PropertyId::D); + auto& value = get(PropertyID::D); return Parser::parsePath(value); } @@ -55,19 +55,19 @@ std::unique_ptr PathElement::clone() const return cloneElement(); } -PolyElement::PolyElement(ElementId id) +PolyElement::PolyElement(ElementID id) : GeometryElement(id) { } PointList PolyElement::points() const { - auto& value = get(PropertyId::Points); + auto& value = get(PropertyID::Points); return Parser::parsePointList(value); } PolygonElement::PolygonElement() - : PolyElement(ElementId::Polygon) + : PolyElement(ElementID::Polygon) { } @@ -92,7 +92,7 @@ std::unique_ptr PolygonElement::clone() const } PolylineElement::PolylineElement() - : PolyElement(ElementId::Polyline) + : PolyElement(ElementID::Polyline) { } @@ -116,25 +116,25 @@ std::unique_ptr PolylineElement::clone() const } CircleElement::CircleElement() - : GeometryElement(ElementId::Circle) + : GeometryElement(ElementID::Circle) { } Length CircleElement::cx() const { - auto& value = get(PropertyId::Cx); + auto& value = get(PropertyID::Cx); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length CircleElement::cy() const { - auto& value = get(PropertyId::Cy); + auto& value = get(PropertyID::Cy); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length CircleElement::r() const { - auto& value = get(PropertyId::R); + auto& value = get(PropertyID::R); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } @@ -160,31 +160,31 @@ std::unique_ptr CircleElement::clone() const } EllipseElement::EllipseElement() - : GeometryElement(ElementId::Ellipse) + : GeometryElement(ElementID::Ellipse) { } Length EllipseElement::cx() const { - auto& value = get(PropertyId::Cx); + auto& value = get(PropertyID::Cx); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length EllipseElement::cy() const { - auto& value = get(PropertyId::Cy); + auto& value = get(PropertyID::Cy); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length EllipseElement::rx() const { - auto& value = get(PropertyId::Rx); + auto& value = get(PropertyID::Rx); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } Length EllipseElement::ry() const { - auto& value = get(PropertyId::Ry); + auto& value = get(PropertyID::Ry); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } @@ -212,31 +212,31 @@ std::unique_ptr EllipseElement::clone() const } LineElement::LineElement() - : GeometryElement(ElementId::Line) + : GeometryElement(ElementID::Line) { } Length LineElement::x1() const { - auto& value = get(PropertyId::X1); + auto& value = get(PropertyID::X1); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LineElement::y1() const { - auto& value = get(PropertyId::Y1); + auto& value = get(PropertyID::Y1); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LineElement::x2() const { - auto& value = get(PropertyId::X2); + auto& value = get(PropertyID::X2); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LineElement::y2() const { - auto& value = get(PropertyId::Y2); + auto& value = get(PropertyID::Y2); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } @@ -260,43 +260,43 @@ std::unique_ptr LineElement::clone() const } RectElement::RectElement() - : GeometryElement(ElementId::Rect) + : GeometryElement(ElementID::Rect) { } Length RectElement::x() const { - auto& value = get(PropertyId::X); + auto& value = get(PropertyID::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length RectElement::y() const { - auto& value = get(PropertyId::Y); + auto& value = get(PropertyID::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length RectElement::rx() const { - auto& value = get(PropertyId::Rx); + auto& value = get(PropertyID::Rx); return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown); } Length RectElement::ry() const { - auto& value = get(PropertyId::Ry); + auto& value = get(PropertyID::Ry); return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown); } Length RectElement::width() const { - auto& value = get(PropertyId::Width); + auto& value = get(PropertyID::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } Length RectElement::height() const { - auto& value = get(PropertyId::Height); + auto& value = get(PropertyID::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } diff --git a/source/geometryelement.h b/source/geometryelement.h index 8d302ee..e2f0bb4 100644 --- a/source/geometryelement.h +++ b/source/geometryelement.h @@ -10,7 +10,7 @@ class LayoutShape; class GeometryElement : public GraphicsElement { public: - GeometryElement(ElementId id); + GeometryElement(ElementID id); bool isGeometry() const { return true; } virtual void layout(LayoutContext* context, LayoutContainer* current) const; @@ -31,7 +31,7 @@ class PathElement : public GeometryElement class PolyElement : public GeometryElement { public: - PolyElement(ElementId id); + PolyElement(ElementID id); PointList points() const; }; diff --git a/source/graphicselement.cpp b/source/graphicselement.cpp index 60c28f9..9df1a00 100644 --- a/source/graphicselement.cpp +++ b/source/graphicselement.cpp @@ -3,14 +3,14 @@ namespace lunasvg { -GraphicsElement::GraphicsElement(ElementId id) +GraphicsElement::GraphicsElement(ElementID id) : StyledElement(id) { } Transform GraphicsElement::transform() const { - auto& value = get(PropertyId::Transform); + auto& value = get(PropertyID::Transform); return Parser::parseTransform(value); } diff --git a/source/graphicselement.h b/source/graphicselement.h index 4da8ce8..1c156a0 100644 --- a/source/graphicselement.h +++ b/source/graphicselement.h @@ -8,7 +8,7 @@ namespace lunasvg { class GraphicsElement : public StyledElement { public: - GraphicsElement(ElementId id); + GraphicsElement(ElementID id); Transform transform() const; }; diff --git a/source/layoutcontext.cpp b/source/layoutcontext.cpp index 5b92c87..24f4d0f 100644 --- a/source/layoutcontext.cpp +++ b/source/layoutcontext.cpp @@ -479,14 +479,14 @@ void RenderState::endGroup(RenderState& state, const BlendInfo& info) state.canvas->blend(canvas.get(), BlendMode::Src_Over, m_mode == RenderMode::Display ? info.opacity : 1.0); } -LayoutContext::LayoutContext(const ParseDocument* document, LayoutSymbol* root) - : m_document(document), m_root(root) +LayoutContext::LayoutContext(const TreeBuilder* builder, LayoutSymbol* root) + : m_builder(builder), m_root(root) { } Element* LayoutContext::getElementById(const std::string& id) const { - return m_document->getElementById(id); + return m_builder->getElementById(id); } LayoutObject* LayoutContext::getResourcesById(const std::string& id) const @@ -517,7 +517,7 @@ LayoutMask* LayoutContext::getMasker(const std::string& id) return static_cast(ref); auto element = getElementById(id); - if(element == nullptr || element->id != ElementId::Mask) + if(element == nullptr || element->id != ElementID::Mask) return nullptr; auto masker = static_cast(element)->getMasker(this); @@ -534,7 +534,7 @@ LayoutClipPath* LayoutContext::getClipper(const std::string& id) return static_cast(ref); auto element = getElementById(id); - if(element == nullptr || element->id != ElementId::ClipPath) + if(element == nullptr || element->id != ElementID::ClipPath) return nullptr; auto clipper = static_cast(element)->getClipper(this); @@ -551,7 +551,7 @@ LayoutMarker* LayoutContext::getMarker(const std::string& id) return static_cast(ref); auto element = getElementById(id); - if(element == nullptr || element->id != ElementId::Marker) + if(element == nullptr || element->id != ElementID::Marker) return nullptr; auto marker = static_cast(element)->getMarker(this); diff --git a/source/layoutcontext.h b/source/layoutcontext.h index 13c3d3c..e81be5a 100644 --- a/source/layoutcontext.h +++ b/source/layoutcontext.h @@ -346,14 +346,14 @@ class RenderState RenderMode m_mode; }; -class ParseDocument; +class TreeBuilder; class StyledElement; class GeometryElement; class LayoutContext { public: - LayoutContext(const ParseDocument* document, LayoutSymbol* root); + LayoutContext(const TreeBuilder* builder, LayoutSymbol* root); Element* getElementById(const std::string& id) const; LayoutObject* getResourcesById(const std::string& id) const; @@ -373,7 +373,7 @@ class LayoutContext bool hasReference(const Element* element) const; private: - const ParseDocument* m_document; + const TreeBuilder* m_builder; LayoutSymbol* m_root; std::map m_resourcesCache; std::set m_references; diff --git a/source/lunasvg.cpp b/source/lunasvg.cpp index 2a75aa3..a77cfef 100644 --- a/source/lunasvg.cpp +++ b/source/lunasvg.cpp @@ -292,11 +292,11 @@ std::unique_ptr Document::loadFromData(const std::string& string) std::unique_ptr Document::loadFromData(const char* data, std::size_t size) { - ParseDocument parser; - if(!parser.parse(data, size)) + TreeBuilder builder; + if(!builder.parse(data, size)) return nullptr; - auto root = parser.layout(); + auto root = builder.build(); if(!root || root->children.empty()) return nullptr; diff --git a/source/markerelement.cpp b/source/markerelement.cpp index 262d7b2..5a61aff 100644 --- a/source/markerelement.cpp +++ b/source/markerelement.cpp @@ -5,55 +5,55 @@ namespace lunasvg { MarkerElement::MarkerElement() - : StyledElement(ElementId::Marker) + : StyledElement(ElementID::Marker) { } Length MarkerElement::refX() const { - auto& value = get(PropertyId::RefX); + auto& value = get(PropertyID::RefX); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length MarkerElement::refY() const { - auto& value = get(PropertyId::RefY); + auto& value = get(PropertyID::RefY); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length MarkerElement::markerWidth() const { - auto& value = get(PropertyId::MarkerWidth); + auto& value = get(PropertyID::MarkerWidth); return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent); } Length MarkerElement::markerHeight() const { - auto& value = get(PropertyId::MarkerHeight); + auto& value = get(PropertyID::MarkerHeight); return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent); } Angle MarkerElement::orient() const { - auto& value = get(PropertyId::Orient); + auto& value = get(PropertyID::Orient); return Parser::parseAngle(value); } MarkerUnits MarkerElement::markerUnits() const { - auto& value = get(PropertyId::MarkerUnits); + auto& value = get(PropertyID::MarkerUnits); return Parser::parseMarkerUnits(value); } Rect MarkerElement::viewBox() const { - auto& value = get(PropertyId::ViewBox); + auto& value = get(PropertyID::ViewBox); return Parser::parseViewBox(value); } PreserveAspectRatio MarkerElement::preserveAspectRatio() const { - auto& value = get(PropertyId::PreserveAspectRatio); + auto& value = get(PropertyID::PreserveAspectRatio); return Parser::parsePreserveAspectRatio(value); } diff --git a/source/maskelement.cpp b/source/maskelement.cpp index da0a7fb..0503657 100644 --- a/source/maskelement.cpp +++ b/source/maskelement.cpp @@ -5,43 +5,43 @@ namespace lunasvg { MaskElement::MaskElement() - : StyledElement(ElementId::Mask) + : StyledElement(ElementID::Mask) { } Length MaskElement::x() const { - auto& value = get(PropertyId::X); + auto& value = get(PropertyID::X); return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent); } Length MaskElement::y() const { - auto& value = get(PropertyId::Y); + auto& value = get(PropertyID::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent); } Length MaskElement::width() const { - auto& value = get(PropertyId::Width); + auto& value = get(PropertyID::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent); } Length MaskElement::height() const { - auto& value = get(PropertyId::Height); + auto& value = get(PropertyID::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent); } Units MaskElement::maskUnits() const { - auto& value = get(PropertyId::MaskUnits); + auto& value = get(PropertyID::MaskUnits); return Parser::parseUnits(value, Units::ObjectBoundingBox); } Units MaskElement::maskContentUnits() const { - auto& value = get(PropertyId::MaskContentUnits); + auto& value = get(PropertyID::MaskContentUnits); return Parser::parseUnits(value, Units::UserSpaceOnUse); } diff --git a/source/paintelement.cpp b/source/paintelement.cpp index 0e0776e..7cc77a6 100644 --- a/source/paintelement.cpp +++ b/source/paintelement.cpp @@ -7,37 +7,37 @@ namespace lunasvg { -PaintElement::PaintElement(ElementId id) +PaintElement::PaintElement(ElementID id) : StyledElement(id) { } -GradientElement::GradientElement(ElementId id) +GradientElement::GradientElement(ElementID id) : PaintElement(id) { } Transform GradientElement::gradientTransform() const { - auto& value = get(PropertyId::GradientTransform); + auto& value = get(PropertyID::GradientTransform); return Parser::parseTransform(value); } SpreadMethod GradientElement::spreadMethod() const { - auto& value = get(PropertyId::SpreadMethod); + auto& value = get(PropertyID::SpreadMethod); return Parser::parseSpreadMethod(value); } Units GradientElement::gradientUnits() const { - auto& value = get(PropertyId::GradientUnits); + auto& value = get(PropertyID::GradientUnits); return Parser::parseUnits(value, Units::ObjectBoundingBox); } std::string GradientElement::href() const { - auto& value = get(PropertyId::Href); + auto& value = get(PropertyID::Href); return Parser::parseHref(value); } @@ -50,7 +50,7 @@ GradientStops GradientElement::buildGradientStops() const if(child->isText()) continue; auto element = static_cast(child.get()); - if(element->id != ElementId::Stop) + if(element->id != ElementID::Stop) continue; auto stop = static_cast(element); auto offset = std::max(prevOffset, stop->offset()); @@ -62,31 +62,31 @@ GradientStops GradientElement::buildGradientStops() const } LinearGradientElement::LinearGradientElement() - : GradientElement(ElementId::LinearGradient) + : GradientElement(ElementID::LinearGradient) { } Length LinearGradientElement::x1() const { - auto& value = get(PropertyId::X1); + auto& value = get(PropertyID::X1); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LinearGradientElement::y1() const { - auto& value = get(PropertyId::Y1); + auto& value = get(PropertyID::Y1); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LinearGradientElement::x2() const { - auto& value = get(PropertyId::X2); + auto& value = get(PropertyID::X2); return Parser::parseLength(value, AllowNegativeLengths, Length::HundredPercent); } Length LinearGradientElement::y2() const { - auto& value = get(PropertyId::Y2); + auto& value = get(PropertyID::Y2); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } @@ -98,30 +98,30 @@ std::unique_ptr LinearGradientElement::getPainter(LayoutContext* c while(true) { - if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform)) + if(!attributes.hasGradientTransform() && current->has(PropertyID::GradientTransform)) attributes.setGradientTransform(current->gradientTransform()); - if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod)) + if(!attributes.hasSpreadMethod() && current->has(PropertyID::SpreadMethod)) attributes.setSpreadMethod(current->spreadMethod()); - if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits)) + if(!attributes.hasGradientUnits() && current->has(PropertyID::GradientUnits)) attributes.setGradientUnits(current->gradientUnits()); if(!attributes.hasGradientStops()) attributes.setGradientStops(current->buildGradientStops()); - if(current->id == ElementId::LinearGradient) + if(current->id == ElementID::LinearGradient) { auto element = static_cast(current); - if(!attributes.hasX1() && element->has(PropertyId::X1)) + if(!attributes.hasX1() && element->has(PropertyID::X1)) attributes.setX1(element->x1()); - if(!attributes.hasY1() && element->has(PropertyId::Y1)) + if(!attributes.hasY1() && element->has(PropertyID::Y1)) attributes.setY1(element->y1()); - if(!attributes.hasX2() && element->has(PropertyId::X2)) + if(!attributes.hasX2() && element->has(PropertyID::X2)) attributes.setX2(element->x2()); - if(!attributes.hasY2() && element->has(PropertyId::Y2)) + if(!attributes.hasY2() && element->has(PropertyID::Y2)) attributes.setY2(element->y2()); } auto ref = context->getElementById(current->href()); - if(!ref || !(ref->id == ElementId::LinearGradient || ref->id == ElementId::RadialGradient)) + if(!ref || !(ref->id == ElementID::LinearGradient || ref->id == ElementID::RadialGradient)) break; processedGradients.insert(current); @@ -164,37 +164,37 @@ std::unique_ptr LinearGradientElement::clone() const } RadialGradientElement::RadialGradientElement() - : GradientElement(ElementId::RadialGradient) + : GradientElement(ElementID::RadialGradient) { } Length RadialGradientElement::cx() const { - auto& value = get(PropertyId::Cx); + auto& value = get(PropertyID::Cx); return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent); } Length RadialGradientElement::cy() const { - auto& value = get(PropertyId::Cy); + auto& value = get(PropertyID::Cy); return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent); } Length RadialGradientElement::r() const { - auto& value = get(PropertyId::R); + auto& value = get(PropertyID::R); return Parser::parseLength(value, ForbidNegativeLengths, Length::FiftyPercent); } Length RadialGradientElement::fx() const { - auto& value = get(PropertyId::Fx); + auto& value = get(PropertyID::Fx); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length RadialGradientElement::fy() const { - auto& value = get(PropertyId::Fy); + auto& value = get(PropertyID::Fy); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } @@ -206,32 +206,32 @@ std::unique_ptr RadialGradientElement::getPainter(LayoutContext* c while(true) { - if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform)) + if(!attributes.hasGradientTransform() && current->has(PropertyID::GradientTransform)) attributes.setGradientTransform(current->gradientTransform()); - if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod)) + if(!attributes.hasSpreadMethod() && current->has(PropertyID::SpreadMethod)) attributes.setSpreadMethod(current->spreadMethod()); - if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits)) + if(!attributes.hasGradientUnits() && current->has(PropertyID::GradientUnits)) attributes.setGradientUnits(current->gradientUnits()); if(!attributes.hasGradientStops()) attributes.setGradientStops(current->buildGradientStops()); - if(current->id == ElementId::RadialGradient) + if(current->id == ElementID::RadialGradient) { auto element = static_cast(current); - if(!attributes.hasCx() && element->has(PropertyId::Cx)) + if(!attributes.hasCx() && element->has(PropertyID::Cx)) attributes.setCx(element->cx()); - if(!attributes.hasCy() && element->has(PropertyId::Cy)) + if(!attributes.hasCy() && element->has(PropertyID::Cy)) attributes.setCy(element->cy()); - if(!attributes.hasR() && element->has(PropertyId::R)) + if(!attributes.hasR() && element->has(PropertyID::R)) attributes.setR(element->r()); - if(!attributes.hasFx() && element->has(PropertyId::Fx)) + if(!attributes.hasFx() && element->has(PropertyID::Fx)) attributes.setFx(element->fx()); - if(!attributes.hasFy() && element->has(PropertyId::Fy)) + if(!attributes.hasFy() && element->has(PropertyID::Fy)) attributes.setFy(element->fy()); } auto ref = context->getElementById(current->href()); - if(!ref || !(ref->id == ElementId::LinearGradient || ref->id == ElementId::RadialGradient)) + if(!ref || !(ref->id == ElementID::LinearGradient || ref->id == ElementID::RadialGradient)) break; processedGradients.insert(current); @@ -278,67 +278,67 @@ std::unique_ptr RadialGradientElement::clone() const } PatternElement::PatternElement() - : PaintElement(ElementId::Pattern) + : PaintElement(ElementID::Pattern) { } Length PatternElement::x() const { - auto& value = get(PropertyId::X); + auto& value = get(PropertyID::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length PatternElement::y() const { - auto& value = get(PropertyId::Y); + auto& value = get(PropertyID::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length PatternElement::width() const { - auto& value = get(PropertyId::Width); + auto& value = get(PropertyID::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } Length PatternElement::height() const { - auto& value = get(PropertyId::Height); + auto& value = get(PropertyID::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } Transform PatternElement::patternTransform() const { - auto& value = get(PropertyId::PatternTransform); + auto& value = get(PropertyID::PatternTransform); return Parser::parseTransform(value); } Units PatternElement::patternUnits() const { - auto& value = get(PropertyId::PatternUnits); + auto& value = get(PropertyID::PatternUnits); return Parser::parseUnits(value, Units::ObjectBoundingBox); } Units PatternElement::patternContentUnits() const { - auto& value = get(PropertyId::PatternContentUnits); + auto& value = get(PropertyID::PatternContentUnits); return Parser::parseUnits(value, Units::UserSpaceOnUse); } Rect PatternElement::viewBox() const { - auto& value = get(PropertyId::ViewBox); + auto& value = get(PropertyID::ViewBox); return Parser::parseViewBox(value); } PreserveAspectRatio PatternElement::preserveAspectRatio() const { - auto& value = get(PropertyId::PreserveAspectRatio); + auto& value = get(PropertyID::PreserveAspectRatio); return Parser::parsePreserveAspectRatio(value); } std::string PatternElement::href() const { - auto& value = get(PropertyId::Href); + auto& value = get(PropertyID::Href); return Parser::parseHref(value); } @@ -353,29 +353,29 @@ std::unique_ptr PatternElement::getPainter(LayoutContext* context) while(true) { - if(!attributes.hasX() && current->has(PropertyId::X)) + if(!attributes.hasX() && current->has(PropertyID::X)) attributes.setX(current->x()); - if(!attributes.hasY() && current->has(PropertyId::Y)) + if(!attributes.hasY() && current->has(PropertyID::Y)) attributes.setY(current->y()); - if(!attributes.hasWidth() && current->has(PropertyId::Width)) + if(!attributes.hasWidth() && current->has(PropertyID::Width)) attributes.setWidth(current->width()); - if(!attributes.hasHeight() && current->has(PropertyId::Height)) + if(!attributes.hasHeight() && current->has(PropertyID::Height)) attributes.setHeight(current->height()); - if(!attributes.hasPatternTransform() && current->has(PropertyId::PatternTransform)) + if(!attributes.hasPatternTransform() && current->has(PropertyID::PatternTransform)) attributes.setPatternTransform(current->patternTransform()); - if(!attributes.hasPatternUnits() && current->has(PropertyId::PatternUnits)) + if(!attributes.hasPatternUnits() && current->has(PropertyID::PatternUnits)) attributes.setPatternUnits(current->patternUnits()); - if(!attributes.hasPatternContentUnits() && current->has(PropertyId::PatternContentUnits)) + if(!attributes.hasPatternContentUnits() && current->has(PropertyID::PatternContentUnits)) attributes.setPatternContentUnits(current->patternContentUnits()); - if(!attributes.hasViewBox() && current->has(PropertyId::ViewBox)) + if(!attributes.hasViewBox() && current->has(PropertyID::ViewBox)) attributes.setViewBox(current->viewBox()); - if(!attributes.hasPreserveAspectRatio() && current->has(PropertyId::PreserveAspectRatio)) + if(!attributes.hasPreserveAspectRatio() && current->has(PropertyID::PreserveAspectRatio)) attributes.setPreserveAspectRatio(current->preserveAspectRatio()); if(!attributes.hasPatternContentElement() && current->children.size()) attributes.setPatternContentElement(current); auto ref = context->getElementById(current->href()); - if(!ref || ref->id != ElementId::Pattern) + if(!ref || ref->id != ElementID::Pattern) break; processedPatterns.insert(current); @@ -413,7 +413,7 @@ std::unique_ptr PatternElement::clone() const } SolidColorElement::SolidColorElement() - : PaintElement(ElementId::SolidColor) + : PaintElement(ElementID::SolidColor) { } @@ -421,7 +421,7 @@ std::unique_ptr SolidColorElement::getPainter(LayoutContext*) cons { auto solid = std::make_unique(); solid->color = solid_color(); - solid->color.a = solid_opacity(); + solid->color.combine(solid_opacity()); return std::move(solid); } diff --git a/source/paintelement.h b/source/paintelement.h index 70c86cd..b2d152a 100644 --- a/source/paintelement.h +++ b/source/paintelement.h @@ -11,7 +11,7 @@ class LayoutObject; class PaintElement : public StyledElement { public: - PaintElement(ElementId id); + PaintElement(ElementID id); bool isPaint() const { return true; } virtual std::unique_ptr getPainter(LayoutContext* context) const = 0; @@ -20,7 +20,7 @@ class PaintElement : public StyledElement class GradientElement : public PaintElement { public: - GradientElement(ElementId id); + GradientElement(ElementID id); Transform gradientTransform() const; SpreadMethod spreadMethod() const; diff --git a/source/parser.cpp b/source/parser.cpp index e3b20f6..5e4dee0 100644 --- a/source/parser.cpp +++ b/source/parser.cpp @@ -690,16 +690,12 @@ Color Parser::parseColor(const std::string& string, const StyledElement* element value |= value << 4; } - auto r = (value&0xff0000)>>16; - auto g = (value&0x00ff00)>>8; - auto b = (value&0x0000ff)>>0; - - return Color{r / 255.0, g / 255.0, b / 255.0}; + return Color(value | 0xFF000000); } if(Utils::skipDesc(ptr, end, "rgb(")) { - double r, g, b; + int r, g, b; if(!Utils::skipWs(ptr, end) || !parseColorComponent(ptr, end, r) || !Utils::skipWsComma(ptr, end) @@ -710,7 +706,7 @@ Color Parser::parseColor(const std::string& string, const StyledElement* element || !Utils::skipDesc(ptr, end, ')')) return defaultValue; - return Color{r / 255.0, g / 255.0, b / 255.0}; + return Color(r, g, b, 255); } if(Utils::skipDesc(ptr, end, "none")) @@ -722,13 +718,7 @@ Color Parser::parseColor(const std::string& string, const StyledElement* element auto it = colormap.find(string); if(it == colormap.end()) return defaultValue; - - auto value = it->second; - auto r = (value&0xff0000)>>16; - auto g = (value&0x00ff00)>>8; - auto b = (value&0x0000ff)>>0; - - return Color{r / 255.0, g / 255.0, b / 255.0}; + return Color(it->second | 0xFF000000); } Paint Parser::parsePaint(const std::string& string, const StyledElement* element, const Color& defaultValue) @@ -907,15 +897,17 @@ bool Parser::parseArcFlag(const char*& ptr, const char* end, bool& flag) return true; } -bool Parser::parseColorComponent(const char*& ptr, const char* end, double& value) +bool Parser::parseColorComponent(const char*& ptr, const char* end, int& component) { + double value = 0; if(!Utils::parseNumber(ptr, end, value)) return false; if(Utils::skipDesc(ptr, end, '%')) value *= 2.55; - value = (value < 0.0) ? 0.0 : (value > 255.0) ? 255.0 : std::round(value); + value = std::clamp(value, 0.0, 255.0); + component = static_cast(std::round(value)); return true; } @@ -1024,122 +1016,122 @@ bool Parser::parseTransform(const char*& ptr, const char* end, TransformType& ty return true; } -static const std::map elementmap = { - {"circle", ElementId::Circle}, - {"clipPath", ElementId::ClipPath}, - {"defs", ElementId::Defs}, - {"ellipse", ElementId::Ellipse}, - {"g", ElementId::G}, - {"line", ElementId::Line}, - {"linearGradient", ElementId::LinearGradient}, - {"marker", ElementId::Marker}, - {"mask", ElementId::Mask}, - {"path", ElementId::Path}, - {"pattern", ElementId::Pattern}, - {"polygon", ElementId::Polygon}, - {"polyline", ElementId::Polyline}, - {"radialGradient", ElementId::RadialGradient}, - {"rect", ElementId::Rect}, - {"stop", ElementId::Stop}, - {"style", ElementId::Style}, - {"solidColor", ElementId::SolidColor}, - {"svg", ElementId::Svg}, - {"symbol", ElementId::Symbol}, - {"use", ElementId::Use} +static const std::map elementmap = { + {"circle", ElementID::Circle}, + {"clipPath", ElementID::ClipPath}, + {"defs", ElementID::Defs}, + {"ellipse", ElementID::Ellipse}, + {"g", ElementID::G}, + {"line", ElementID::Line}, + {"linearGradient", ElementID::LinearGradient}, + {"marker", ElementID::Marker}, + {"mask", ElementID::Mask}, + {"path", ElementID::Path}, + {"pattern", ElementID::Pattern}, + {"polygon", ElementID::Polygon}, + {"polyline", ElementID::Polyline}, + {"radialGradient", ElementID::RadialGradient}, + {"rect", ElementID::Rect}, + {"stop", ElementID::Stop}, + {"style", ElementID::Style}, + {"solidColor", ElementID::SolidColor}, + {"svg", ElementID::Svg}, + {"symbol", ElementID::Symbol}, + {"use", ElementID::Use} }; -static const std::map propertymap = { - {"class", PropertyId::Class}, - {"clipPathUnits", PropertyId::ClipPathUnits}, - {"cx", PropertyId::Cx}, - {"cy", PropertyId::Cy}, - {"d", PropertyId::D}, - {"fx", PropertyId::Fx}, - {"fy", PropertyId::Fy}, - {"gradientTransform", PropertyId::GradientTransform}, - {"gradientUnits", PropertyId::GradientUnits}, - {"height", PropertyId::Height}, - {"href", PropertyId::Href}, - {"id", PropertyId::Id}, - {"markerHeight", PropertyId::MarkerHeight}, - {"markerUnits", PropertyId::MarkerUnits}, - {"markerWidth", PropertyId::MarkerWidth}, - {"maskContentUnits", PropertyId::MaskContentUnits}, - {"maskUnits", PropertyId::MaskUnits}, - {"offset", PropertyId::Offset}, - {"orient", PropertyId::Orient}, - {"patternContentUnits", PropertyId::PatternContentUnits}, - {"patternTransform", PropertyId::PatternTransform}, - {"patternUnits", PropertyId::PatternUnits}, - {"points", PropertyId::Points}, - {"preserveAspectRatio", PropertyId::PreserveAspectRatio}, - {"r", PropertyId::R}, - {"refX", PropertyId::RefX}, - {"refY", PropertyId::RefY}, - {"rx", PropertyId::Rx}, - {"ry", PropertyId::Ry}, - {"spreadMethod", PropertyId::SpreadMethod}, - {"style", PropertyId::Style}, - {"transform", PropertyId::Transform}, - {"viewBox", PropertyId::ViewBox}, - {"width", PropertyId::Width}, - {"x", PropertyId::X}, - {"x1", PropertyId::X1}, - {"x2", PropertyId::X2}, - {"xlink:href", PropertyId::Href}, - {"y", PropertyId::Y}, - {"y1", PropertyId::Y1}, - {"y2", PropertyId::Y2} +static const std::map propertymap = { + {"class", PropertyID::Class}, + {"clipPathUnits", PropertyID::ClipPathUnits}, + {"cx", PropertyID::Cx}, + {"cy", PropertyID::Cy}, + {"d", PropertyID::D}, + {"fx", PropertyID::Fx}, + {"fy", PropertyID::Fy}, + {"gradientTransform", PropertyID::GradientTransform}, + {"gradientUnits", PropertyID::GradientUnits}, + {"height", PropertyID::Height}, + {"href", PropertyID::Href}, + {"id", PropertyID::Id}, + {"markerHeight", PropertyID::MarkerHeight}, + {"markerUnits", PropertyID::MarkerUnits}, + {"markerWidth", PropertyID::MarkerWidth}, + {"maskContentUnits", PropertyID::MaskContentUnits}, + {"maskUnits", PropertyID::MaskUnits}, + {"offset", PropertyID::Offset}, + {"orient", PropertyID::Orient}, + {"patternContentUnits", PropertyID::PatternContentUnits}, + {"patternTransform", PropertyID::PatternTransform}, + {"patternUnits", PropertyID::PatternUnits}, + {"points", PropertyID::Points}, + {"preserveAspectRatio", PropertyID::PreserveAspectRatio}, + {"r", PropertyID::R}, + {"refX", PropertyID::RefX}, + {"refY", PropertyID::RefY}, + {"rx", PropertyID::Rx}, + {"ry", PropertyID::Ry}, + {"spreadMethod", PropertyID::SpreadMethod}, + {"style", PropertyID::Style}, + {"transform", PropertyID::Transform}, + {"viewBox", PropertyID::ViewBox}, + {"width", PropertyID::Width}, + {"x", PropertyID::X}, + {"x1", PropertyID::X1}, + {"x2", PropertyID::X2}, + {"xlink:href", PropertyID::Href}, + {"y", PropertyID::Y}, + {"y1", PropertyID::Y1}, + {"y2", PropertyID::Y2} }; -static const std::map csspropertymap = { - {"clip-path", PropertyId::Clip_Path}, - {"clip-rule", PropertyId::Clip_Rule}, - {"color", PropertyId::Color}, - {"display", PropertyId::Display}, - {"fill", PropertyId::Fill}, - {"fill-opacity", PropertyId::Fill_Opacity}, - {"fill-rule", PropertyId::Fill_Rule}, - {"marker-end", PropertyId::Marker_End}, - {"marker-mid", PropertyId::Marker_Mid}, - {"marker-start", PropertyId::Marker_Start}, - {"mask", PropertyId::Mask}, - {"opacity", PropertyId::Opacity}, - {"overflow", PropertyId::Overflow}, - {"solid-color", PropertyId::Solid_Color}, - {"solid-opacity", PropertyId::Solid_Opacity}, - {"stop-color", PropertyId::Stop_Color}, - {"stop-opacity", PropertyId::Stop_Opacity}, - {"stroke", PropertyId::Stroke}, - {"stroke-dasharray", PropertyId::Stroke_Dasharray}, - {"stroke-dashoffset", PropertyId::Stroke_Dashoffset}, - {"stroke-linecap", PropertyId::Stroke_Linecap}, - {"stroke-linejoin", PropertyId::Stroke_Linejoin}, - {"stroke-miterlimit", PropertyId::Stroke_Miterlimit}, - {"stroke-opacity", PropertyId::Stroke_Opacity}, - {"stroke-width", PropertyId::Stroke_Width}, - {"visibility", PropertyId::Visibility} +static const std::map csspropertymap = { + {"clip-path", PropertyID::Clip_Path}, + {"clip-rule", PropertyID::Clip_Rule}, + {"color", PropertyID::Color}, + {"display", PropertyID::Display}, + {"fill", PropertyID::Fill}, + {"fill-opacity", PropertyID::Fill_Opacity}, + {"fill-rule", PropertyID::Fill_Rule}, + {"marker-end", PropertyID::Marker_End}, + {"marker-mid", PropertyID::Marker_Mid}, + {"marker-start", PropertyID::Marker_Start}, + {"mask", PropertyID::Mask}, + {"opacity", PropertyID::Opacity}, + {"overflow", PropertyID::Overflow}, + {"solid-color", PropertyID::Solid_Color}, + {"solid-opacity", PropertyID::Solid_Opacity}, + {"stop-color", PropertyID::Stop_Color}, + {"stop-opacity", PropertyID::Stop_Opacity}, + {"stroke", PropertyID::Stroke}, + {"stroke-dasharray", PropertyID::Stroke_Dasharray}, + {"stroke-dashoffset", PropertyID::Stroke_Dashoffset}, + {"stroke-linecap", PropertyID::Stroke_Linecap}, + {"stroke-linejoin", PropertyID::Stroke_Linejoin}, + {"stroke-miterlimit", PropertyID::Stroke_Miterlimit}, + {"stroke-opacity", PropertyID::Stroke_Opacity}, + {"stroke-width", PropertyID::Stroke_Width}, + {"visibility", PropertyID::Visibility} }; -static inline ElementId elementId(const std::string& name) +static inline ElementID elementId(const std::string& name) { auto it = elementmap.find(name); if(it == elementmap.end()) - return ElementId::Unknown; + return ElementID::Unknown; return it->second; } -static inline PropertyId cssPropertyId(const std::string& name) +static inline PropertyID cssPropertyId(const std::string& name) { auto it = csspropertymap.find(name); if(it == csspropertymap.end()) - return PropertyId::Unknown; + return PropertyID::Unknown; return it->second; } -static inline PropertyId propertyId(const std::string& name) +static inline PropertyID propertyId(const std::string& name) { auto it = propertymap.find(name); if(it == propertymap.end()) @@ -1148,6 +1140,233 @@ static inline PropertyId propertyId(const std::string& name) return it->second; } +bool RuleData::match(const Element* element) const +{ + if(m_selector.empty()) + return false; + + if(m_selector.size() == 1) + return matchSimpleSelector(m_selector.front(), element); + + auto it = m_selector.rbegin(); + auto end = m_selector.rend(); + if(!matchSimpleSelector(*it, element)) + return false; + ++it; + + while(it != end) + { + switch(it->combinator) { + case SimpleSelector::Combinator::Child: + case SimpleSelector::Combinator::Descendant: + element = element->parent; + break; + case SimpleSelector::Combinator::DirectAdjacent: + case SimpleSelector::Combinator::InDirectAdjacent: + element = element->previousElement(); + break; + } + + if(element == nullptr) + return false; + + auto match = matchSimpleSelector(*it, element); + if(!match && (it->combinator != SimpleSelector::Combinator::Descendant && it->combinator != SimpleSelector::Combinator::InDirectAdjacent)) + return false; + + if(match || (it->combinator != SimpleSelector::Combinator::Descendant && it->combinator != SimpleSelector::Combinator::InDirectAdjacent)) + ++it; + } + + return true; +} + +bool RuleData::matchSimpleSelector(const SimpleSelector& selector, const Element* element) const +{ + if(selector.id != ElementID::Star && selector.id != element->id) + return false; + + for(auto& sel : selector.attributeSelectors) + if(!matchAttributeSelector(sel, element)) + return false; + + for(auto& sel : selector.pseudoClassSelectors) + if(!matchPseudoClassSelector(sel, element)) + return false; + + return true; +} + +bool RuleData::matchAttributeSelector(const AttributeSelector& selector, const Element* element) const +{ + auto& value = element->get(selector.id); + if(value.empty()) + return false; + + if(selector.matchType == AttributeSelector::MatchType::None) + return true; + + if(selector.matchType == AttributeSelector::MatchType::Equal) + return selector.value == value; + + if(selector.matchType == AttributeSelector::MatchType::Includes) + { + auto ptr = value.data(); + auto end = ptr + value.size(); + while(ptr < end) + { + auto start = ptr; + while(ptr < end && !IS_WS(*ptr)) + ++ptr; + + if(selector.value == std::string(start, ptr)) + return true; + Utils::skipWs(ptr, end); + } + + return false; + } + + auto starts_with = [](const std::string& string, const std::string& prefix) { + if(prefix.empty() || prefix.size() > string.size()) + return false; + + return string.compare(0, prefix.size(), prefix) == 0; + }; + + auto ends_with = [](const std::string& string, const std::string& suffix) { + if(suffix.empty() || suffix.size() > string.size()) + return false; + + return string.compare(string.size() - suffix.size(), suffix.size(), suffix) == 0; + }; + + if(selector.matchType == AttributeSelector::MatchType::DashMatch) + { + if(selector.value == value) + return true; + + return starts_with(value, selector.value + '-'); + } + + if(selector.matchType == AttributeSelector::MatchType::StartsWith) + return starts_with(value, selector.value); + + if(selector.matchType == AttributeSelector::MatchType::EndsWith) + return ends_with(value, selector.value); + + if(selector.matchType == AttributeSelector::MatchType::Contains) + return value.find(selector.value) != std::string::npos; + + return false; +} + +bool RuleData::matchPseudoClassSelector(const PseudoClassSelector& selector, const Element* element) const +{ + if(selector.type == PseudoClassSelector::Type::Empty) + return element->children.empty(); + + if(selector.type == PseudoClassSelector::Type::Root) + return element->parent == nullptr; + + if(selector.type == PseudoClassSelector::Type::Is) + { + for(auto& selector : selector.subSelectors) { + for(auto& sel : selector) { + if(!matchSimpleSelector(sel, element)) { + return false; + } + } + } + + return true; + } + + if(selector.type == PseudoClassSelector::Type::Not) + { + for(auto& selector : selector.subSelectors) { + for(auto& sel : selector) { + if(matchSimpleSelector(sel, element)) { + return false; + } + } + } + + return true; + } + + if(selector.type == PseudoClassSelector::Type::FirstChild) + return !element->previousElement(); + + if(selector.type == PseudoClassSelector::Type::LastChild) + return !element->nextElement(); + + if(selector.type == PseudoClassSelector::Type::OnlyChild) + return !(element->previousElement() || element->nextElement()); + + if(selector.type == PseudoClassSelector::Type::FirstOfType) + { + auto sibling = element->previousElement(); + while(sibling) + { + if(sibling->id == element->id) + return false; + sibling = element->previousElement(); + } + + return true; + } + + if(selector.type == PseudoClassSelector::Type::LastOfType) + { + auto sibling = element->nextElement(); + while(sibling) + { + if(sibling->id == element->id) + return false; + sibling = element->nextElement(); + } + + return true; + } + + return false; +} + +void StyleSheet::parse(const std::string& content) +{ + CSSParser::parseSheet(this, content); +} + +void StyleSheet::add(const Rule& rule) +{ + for(auto& selector : rule.selectors) { + uint32_t specificity = 0; + for(auto& simpleSelector : selector) { + specificity += (simpleSelector.id == ElementID::Star) ? 0x0 : 0x1; + for(auto& attributeSelector : simpleSelector.attributeSelectors) { + specificity += (attributeSelector.id == PropertyID::Id) ? 0x10000 : 0x100; + } + } + + m_rules.emplace(selector, rule.declarations, specificity, m_position); + } + + m_position += 1; +} + +std::vector StyleSheet::match(const Element *element) const +{ + std::vector declarations; + for(auto& rule : m_rules) { + if(!rule.match(element)) + continue; + declarations.push_back(&rule.properties()); + } + + return declarations; +} + #define IS_STARTNAMECHAR(c) (IS_ALPHA(c) || (c) == '_' || (c) == ':') #define IS_NAMECHAR(c) (IS_STARTNAMECHAR(c) || IS_NUM(c) || (c) == '-' || (c) == '.') static inline bool readIdentifier(const char*& ptr, const char* end, std::string& value) @@ -1180,11 +1399,12 @@ static inline bool readCSSIdentifier(const char*& ptr, const char* end, std::str return true; } -bool CSSParser::parseMore(const std::string& value) +bool CSSParser::parseSheet(StyleSheet* sheet, const std::string& value) { auto ptr = value.data(); auto end = ptr + value.size(); + Rule rule; while(ptr < end) { Utils::skipWs(ptr, end); @@ -1195,16 +1415,15 @@ bool CSSParser::parseMore(const std::string& value) continue; } - Rule rule; if(!parseRule(ptr, end, rule)) return false; - m_rules.push_back(rule); + sheet->add(rule); } return true; } -bool CSSParser::parseAtRule(const char*& ptr, const char* end) const +bool CSSParser::parseAtRule(const char*& ptr, const char* end) { int depth = 0; while(ptr < end) @@ -1225,7 +1444,7 @@ bool CSSParser::parseAtRule(const char*& ptr, const char* end) const return true; } -bool CSSParser::parseRule(const char*& ptr, const char* end, Rule& rule) const +bool CSSParser::parseRule(const char*& ptr, const char* end, Rule& rule) { if(!parseSelectors(ptr, end, rule.selectors)) return false; @@ -1236,8 +1455,9 @@ bool CSSParser::parseRule(const char*& ptr, const char* end, Rule& rule) const return true; } -bool CSSParser::parseSelectors(const char*& ptr, const char* end, SelectorList& selectors) const +bool CSSParser::parseSelectors(const char*& ptr, const char* end, SelectorList& selectors) { + selectors.clear(); Selector selector; if(!parseSelector(ptr, end, selector)) return false; @@ -1255,8 +1475,9 @@ bool CSSParser::parseSelectors(const char*& ptr, const char* end, SelectorList& return true; } -bool CSSParser::parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations) const +bool CSSParser::parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations) { + declarations.clear(); if(!Utils::skipDesc(ptr, end, '{')) return false; @@ -1283,7 +1504,7 @@ bool CSSParser::parseDeclarations(const char*& ptr, const char* end, PropertyLis } auto id = cssPropertyId(name); - if(id != PropertyId::Unknown) + if(id != PropertyID::Unknown) declarations.set(id, value, specificity); Utils::skipWsDelimiter(ptr, end, ';'); } while(ptr < end && *ptr != '}'); @@ -1292,29 +1513,24 @@ bool CSSParser::parseDeclarations(const char*& ptr, const char* end, PropertyLis } #define IS_SELECTOR_STARTNAMECHAR(c) (IS_CSS_STARTNAMECHAR(c) || (c) == '*' || (c) == '#' || (c) == '.' || (c) == '[' || (c) == ':') -bool CSSParser::parseSelector(const char*& ptr, const char* end, Selector& selector) const +bool CSSParser::parseSelector(const char*& ptr, const char* end, Selector& selector) { do { SimpleSelector simpleSelector; if(!parseSimpleSelector(ptr, end, simpleSelector)) return false; - - selector.specificity += (simpleSelector.id == ElementId::Star) ? 0x0 : 0x1; - for(auto& attributeSelector : simpleSelector.attributeSelectors) - selector.specificity += (attributeSelector.id == PropertyId::Id) ? 0x10000 : 0x100; - - selector.simpleSelectors.push_back(simpleSelector); + selector.push_back(simpleSelector); Utils::skipWs(ptr, end); } while(ptr < end && IS_SELECTOR_STARTNAMECHAR(*ptr)); return true; } -bool CSSParser::parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector) const +bool CSSParser::parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector) { std::string name; if(Utils::skipDesc(ptr, end, '*')) - simpleSelector.id = ElementId::Star; + simpleSelector.id = ElementID::Star; else if(readCSSIdentifier(ptr, end, name)) simpleSelector.id = elementId(name); @@ -1323,7 +1539,7 @@ bool CSSParser::parseSimpleSelector(const char*& ptr, const char* end, SimpleSel if(Utils::skipDesc(ptr, end, '#')) { AttributeSelector a; - a.id = PropertyId::Id; + a.id = PropertyID::Id; a.matchType = AttributeSelector::MatchType::Equal; if(!readCSSIdentifier(ptr, end, a.value)) return false; @@ -1334,7 +1550,7 @@ bool CSSParser::parseSimpleSelector(const char*& ptr, const char* end, SimpleSel if(Utils::skipDesc(ptr, end, '.')) { AttributeSelector a; - a.id = PropertyId::Class; + a.id = PropertyID::Class; a.matchType = AttributeSelector::MatchType::Includes; if(!readCSSIdentifier(ptr, end, a.value)) return false; @@ -1388,32 +1604,32 @@ bool CSSParser::parseSimpleSelector(const char*& ptr, const char* end, SimpleSel { if(!readCSSIdentifier(ptr, end, name)) return false; - PseudoClass pseudo; + PseudoClassSelector selector; if(name.compare("empty") == 0) - pseudo.type = PseudoClass::Type::Empty; + selector.type = PseudoClassSelector::Type::Empty; else if(name.compare("root") == 0) - pseudo.type = PseudoClass::Type::Root; + selector.type = PseudoClassSelector::Type::Root; else if(name.compare("not") == 0) - pseudo.type = PseudoClass::Type::Not; + selector.type = PseudoClassSelector::Type::Not; else if(name.compare("first-child") == 0) - pseudo.type = PseudoClass::Type::FirstChild; + selector.type = PseudoClassSelector::Type::FirstChild; else if(name.compare("last-child") == 0) - pseudo.type = PseudoClass::Type::LastChild; + selector.type = PseudoClassSelector::Type::LastChild; else if(name.compare("only-child") == 0) - pseudo.type = PseudoClass::Type::OnlyChild; + selector.type = PseudoClassSelector::Type::OnlyChild; else if(name.compare("first-of-type") == 0) - pseudo.type = PseudoClass::Type::FirstOfType; + selector.type = PseudoClassSelector::Type::FirstOfType; else if(name.compare("last-of-type") == 0) - pseudo.type = PseudoClass::Type::LastOfType; + selector.type = PseudoClassSelector::Type::LastOfType; else if(name.compare("only-of-type") == 0) - pseudo.type = PseudoClass::Type::OnlyOfType; - if(pseudo.type == PseudoClass::Type::Not) + selector.type = PseudoClassSelector::Type::OnlyOfType; + if(selector.type == PseudoClassSelector::Type::Is || selector.type == PseudoClassSelector::Type::Not) { if(!Utils::skipDesc(ptr, end, '(')) return false; Utils::skipWs(ptr, end); - if(!parseSelectors(ptr, end, pseudo.notSelectors)) + if(!parseSelectors(ptr, end, selector.subSelectors)) return false; Utils::skipWs(ptr, end); @@ -1421,7 +1637,7 @@ bool CSSParser::parseSimpleSelector(const char*& ptr, const char* end, SimpleSel return false; } - simpleSelector.pseudoClasses.push_back(pseudo); + simpleSelector.pseudoClassSelectors.push_back(selector); continue; } @@ -1439,248 +1655,50 @@ bool CSSParser::parseSimpleSelector(const char*& ptr, const char* end, SimpleSel return true; } -RuleMatchContext::RuleMatchContext(const std::vector& rules) -{ - for(auto& rule : rules) - for(auto& selector : rule.selectors) - m_selectors.emplace(selector.specificity, std::make_pair(&selector, &rule.declarations)); -} - -std::vector RuleMatchContext::match(const Element* element) const -{ - std::vector declarations; - auto it = m_selectors.begin(); - auto end = m_selectors.end(); - for(;it != end;++it) - { - auto& value = it->second; - if(!selectorMatch(std::get<0>(value), element)) - continue; - declarations.push_back(std::get<1>(value)); - } - - return declarations; -} - -bool RuleMatchContext::selectorMatch(const Selector* selector, const Element* element) const -{ - if(selector->simpleSelectors.empty()) - return false; - - if(selector->simpleSelectors.size() == 1) - return simpleSelectorMatch(selector->simpleSelectors.front(), element); - - auto it = selector->simpleSelectors.rbegin(); - auto end = selector->simpleSelectors.rend(); - if(!simpleSelectorMatch(*it, element)) - return false; - ++it; - - while(it != end) - { - switch(it->combinator) { - case SimpleSelector::Combinator::Child: - case SimpleSelector::Combinator::Descendant: - element = element->parent; - break; - case SimpleSelector::Combinator::DirectAdjacent: - case SimpleSelector::Combinator::InDirectAdjacent: - element = element->previousSibling(); - break; - } - - if(element == nullptr) - return false; - - auto match = simpleSelectorMatch(*it, element); - if(!match && (it->combinator != SimpleSelector::Combinator::Descendant && it->combinator != SimpleSelector::Combinator::InDirectAdjacent)) - return false; - - if(match || (it->combinator != SimpleSelector::Combinator::Descendant && it->combinator != SimpleSelector::Combinator::InDirectAdjacent)) - ++it; - } - - return true; -} - -bool RuleMatchContext::simpleSelectorMatch(const SimpleSelector& selector, const Element* element) const -{ - if(selector.id != ElementId::Star && selector.id != element->id) - return false; - - for(auto& attributeSelector : selector.attributeSelectors) - if(!attributeSelectorMatch(attributeSelector, element)) - return false; - - for(auto& pseudoClass : selector.pseudoClasses) - if(!pseudoClassMatch(pseudoClass, element)) - return false; - - return true; -} - -bool RuleMatchContext::attributeSelectorMatch(const AttributeSelector& selector, const Element* element) const -{ - auto& value = element->get(selector.id); - if(value.empty()) - return false; - - if(selector.matchType == AttributeSelector::MatchType::None) - return true; - - if(selector.matchType == AttributeSelector::MatchType::Equal) - return selector.value == value; - - if(selector.matchType == AttributeSelector::MatchType::Includes) - { - auto ptr = value.data(); - auto end = ptr + value.size(); - while(ptr < end) - { - auto start = ptr; - while(ptr < end && !IS_WS(*ptr)) - ++ptr; - - if(selector.value == std::string(start, ptr)) - return true; - Utils::skipWs(ptr, end); - } - - return false; - } - - auto starts_with = [](const std::string& string, const std::string& prefix) { - if(prefix.empty() || prefix.size() > string.size()) - return false; - - return string.compare(0, prefix.size(), prefix) == 0; - }; - - auto ends_with = [](const std::string& string, const std::string& suffix) { - if(suffix.empty() || suffix.size() > string.size()) - return false; - - return string.compare(string.size() - suffix.size(), suffix.size(), suffix) == 0; - }; - - if(selector.matchType == AttributeSelector::MatchType::DashMatch) - { - if(selector.value == value) - return true; - - return starts_with(value, selector.value + '-'); - } - - if(selector.matchType == AttributeSelector::MatchType::StartsWith) - return starts_with(value, selector.value); - - if(selector.matchType == AttributeSelector::MatchType::EndsWith) - return ends_with(value, selector.value); - - if(selector.matchType == AttributeSelector::MatchType::Contains) - return value.find(selector.value) != std::string::npos; - - return false; -} - -bool RuleMatchContext::pseudoClassMatch(const PseudoClass& pseudo, const Element* element) const -{ - if(pseudo.type == PseudoClass::Type::Empty) - return element->children.empty(); - - if(pseudo.type == PseudoClass::Type::Root) - return element->parent == nullptr; - - if(pseudo.type == PseudoClass::Type::Not) - { - for(auto& selector : pseudo.notSelectors) - if(selectorMatch(&selector, element)) - return false; - return true; - } - - if(pseudo.type == PseudoClass::Type::FirstChild) - return !element->previousSibling(); - - if(pseudo.type == PseudoClass::Type::LastChild) - return !element->nextSibling(); - - if(pseudo.type == PseudoClass::Type::OnlyChild) - return !(element->previousSibling() || element->nextSibling()); - - if(pseudo.type == PseudoClass::Type::FirstOfType) - { - auto sibling = element->previousSibling(); - while(sibling) - { - if(sibling->id == element->id) - return false; - sibling = element->previousSibling(); - } - - return true; - } - - if(pseudo.type == PseudoClass::Type::LastOfType) - { - auto sibling = element->nextSibling(); - while(sibling) - { - if(sibling->id == element->id) - return false; - sibling = element->nextSibling(); - } - - return true; - } - - return false; -} - -static inline std::unique_ptr createElement(ElementId id) +static inline std::unique_ptr createElement(ElementID id) { switch(id) { - case ElementId::Svg: + case ElementID::Svg: return std::make_unique(); - case ElementId::Path: + case ElementID::Path: return std::make_unique(); - case ElementId::G: + case ElementID::G: return std::make_unique(); - case ElementId::Rect: + case ElementID::Rect: return std::make_unique(); - case ElementId::Circle: + case ElementID::Circle: return std::make_unique(); - case ElementId::Ellipse: + case ElementID::Ellipse: return std::make_unique(); - case ElementId::Line: + case ElementID::Line: return std::make_unique(); - case ElementId::Defs: + case ElementID::Defs: return std::make_unique(); - case ElementId::Polygon: + case ElementID::Polygon: return std::make_unique(); - case ElementId::Polyline: + case ElementID::Polyline: return std::make_unique(); - case ElementId::Stop: + case ElementID::Stop: return std::make_unique(); - case ElementId::LinearGradient: + case ElementID::LinearGradient: return std::make_unique(); - case ElementId::RadialGradient: + case ElementID::RadialGradient: return std::make_unique(); - case ElementId::Symbol: + case ElementID::Symbol: return std::make_unique(); - case ElementId::Use: + case ElementID::Use: return std::make_unique(); - case ElementId::Pattern: + case ElementID::Pattern: return std::make_unique(); - case ElementId::Mask: + case ElementID::Mask: return std::make_unique(); - case ElementId::ClipPath: + case ElementID::ClipPath: return std::make_unique(); - case ElementId::SolidColor: + case ElementID::SolidColor: return std::make_unique(); - case ElementId::Marker: + case ElementID::Marker: return std::make_unique(); - case ElementId::Style: + case ElementID::Style: return std::make_unique(); default: break; @@ -1790,26 +1808,22 @@ static inline void parseStyle(const std::string& string, Element* element) ++ptr; value.assign(start, Utils::rtrim(start, ptr)); auto id = cssPropertyId(name); - if(id != PropertyId::Unknown) + if(id != PropertyID::Unknown) element->set(id, value, 0x100); Utils::skipWsDelimiter(ptr, end, ';'); } } -ParseDocument::ParseDocument() -{ -} +TreeBuilder::TreeBuilder() = default; -ParseDocument::~ParseDocument() -{ -} +TreeBuilder::~TreeBuilder() = default; -bool ParseDocument::parse(const char* data, std::size_t size) +bool TreeBuilder::parse(const char* data, std::size_t size) { auto ptr = data; auto end = ptr + size; - CSSParser cssparser; + StyleSheet styleSheet; Element* current = nullptr; std::string name; std::string value; @@ -1825,7 +1839,7 @@ bool ParseDocument::parse(const char* data, std::size_t size) }; auto handle_text = [&](const char* start, const char* end, bool in_cdata) { - if(ignoring > 0 || current == nullptr || current->id != ElementId::Style) + if(ignoring > 0 || current == nullptr || current->id != ElementID::Style) return; if(in_cdata) @@ -1834,7 +1848,7 @@ bool ParseDocument::parse(const char* data, std::size_t size) decodeText(start, end, value); remove_comments(value); - cssparser.parseMore(value); + styleSheet.parse(value); }; while(ptr < end) @@ -1940,8 +1954,8 @@ bool ParseDocument::parse(const char* data, std::size_t size) if(!readIdentifier(ptr, end, name)) return false; - auto id = ignoring == 0 ? elementId(name) : ElementId::Unknown; - if(id == ElementId::Unknown) + auto id = ignoring == 0 ? elementId(name) : ElementID::Unknown; + if(id == ElementID::Unknown) ++ignoring; Element* element = nullptr; @@ -1952,7 +1966,7 @@ bool ParseDocument::parse(const char* data, std::size_t size) if(m_rootElement == nullptr) { - if(id != ElementId::Svg) + if(id != ElementID::Svg) return false; m_rootElement = std::make_unique(); @@ -1988,18 +2002,18 @@ bool ParseDocument::parse(const char* data, std::size_t size) if(ptr >= end || *ptr != quote) return false; - auto id = element ? propertyId(name) : PropertyId::Unknown; - if(id != PropertyId::Unknown) + auto id = element ? propertyId(name) : PropertyID::Unknown; + if(id != PropertyID::Unknown) { decodeText(start, Utils::rtrim(start, ptr), value); - if(id == PropertyId::Style) + if(id == PropertyID::Style) { remove_comments(value); parseStyle(value, element); } else { - if(id == PropertyId::Id) + if(id == PropertyID::Id) m_idCache.emplace(value, element); element->set(id, value, 0x1); } @@ -2037,16 +2051,14 @@ bool ParseDocument::parse(const char* data, std::size_t size) if(!m_rootElement || ptr < end || ignoring > 0) return false; - const auto& rules = cssparser.rules(); - if(!rules.empty()) + if(!styleSheet.empty()) { - RuleMatchContext context(rules); - m_rootElement->transverse([&context](Node* node) { + m_rootElement->transverse([&styleSheet](Node* node) { if(node->isText()) return false; auto element = static_cast(node); - auto declarations = context.match(element); + auto declarations = styleSheet.match(element); for(auto& declaration : declarations) element->properties.add(*declaration); return false; @@ -2056,7 +2068,7 @@ bool ParseDocument::parse(const char* data, std::size_t size) return true; } -Element* ParseDocument::getElementById(const std::string& id) const +Element* TreeBuilder::getElementById(const std::string& id) const { auto it = m_idCache.find(id); if(it == m_idCache.end()) @@ -2065,9 +2077,9 @@ Element* ParseDocument::getElementById(const std::string& id) const return it->second; } -std::unique_ptr ParseDocument::layout() const +std::unique_ptr TreeBuilder::build() const { - return m_rootElement->layoutDocument(this); + return m_rootElement->build(this); } } // namespace lunasvg diff --git a/source/parser.h b/source/parser.h index 6ced83e..76864d3 100644 --- a/source/parser.h +++ b/source/parser.h @@ -2,6 +2,7 @@ #define PARSER_H #include +#include #include "property.h" #include "element.h" @@ -58,12 +59,15 @@ class Parser static bool parseLength(const char*& ptr, const char* end, double& value, LengthUnits& units, LengthNegativeValuesMode mode); static bool parseNumberList(const char*& ptr, const char* end, double* values, int count); static bool parseArcFlag(const char*& ptr, const char* end, bool& flag); - static bool parseColorComponent(const char*& ptr, const char* end, double& value); + static bool parseColorComponent(const char*& ptr, const char* end, int& component); static bool parseUrlFragment(const char*& ptr, const char* end, std::string& ref); static bool parseTransform(const char*& ptr, const char* end, TransformType& type, double* values, int& count); }; -struct Selector; +struct SimpleSelector; + +using Selector = std::vector; +using SelectorList = std::vector; struct AttributeSelector { @@ -78,20 +82,19 @@ struct AttributeSelector Contains }; - PropertyId id{PropertyId::Unknown}; - std::string value; MatchType matchType{MatchType::None}; + PropertyID id{PropertyID::Unknown}; + std::string value; }; -using SelectorList = std::vector; - -struct PseudoClass +struct PseudoClassSelector { enum class Type { Unknown, Empty, Root, + Is, Not, FirstChild, LastChild, @@ -102,7 +105,9 @@ struct PseudoClass }; Type type{Type::Unknown}; - SelectorList notSelectors; + int16_t a{0}; + int16_t b{0}; + SelectorList subSelectors; }; struct SimpleSelector @@ -115,16 +120,10 @@ struct SimpleSelector InDirectAdjacent }; - ElementId id{ElementId::Star}; - std::vector attributeSelectors; - std::vector pseudoClasses; Combinator combinator{Combinator::Descendant}; -}; - -struct Selector -{ - std::vector simpleSelectors; - int specificity{0}; + ElementID id{ElementID::Star}; + std::vector attributeSelectors; + std::vector pseudoClassSelectors; }; struct Rule @@ -133,57 +132,77 @@ struct Rule PropertyList declarations; }; -class RuleMatchContext -{ +class RuleData { public: - RuleMatchContext(const std::vector& rules); + RuleData(const Selector& selector, const PropertyList& properties, uint32_t specificity, uint32_t position) + : m_selector(selector), m_properties(properties), m_specificity(specificity), m_position(position) + {} - std::vector match(const Element* element) const; + const Selector& selector() const { return m_selector; } + const PropertyList& properties() const { return m_properties; } + const uint32_t& specificity() const { return m_specificity; } + const uint32_t& position() const { return m_position; } + + bool match(const Element* element) const; private: - bool selectorMatch(const Selector* selector, const Element* element) const; - bool simpleSelectorMatch(const SimpleSelector& selector, const Element* element) const; - bool attributeSelectorMatch(const AttributeSelector& selector, const Element* element) const; - bool pseudoClassMatch(const PseudoClass& pseudo, const Element* element) const; + bool matchSimpleSelector(const SimpleSelector& selector, const Element* element) const; + bool matchAttributeSelector(const AttributeSelector& selector, const Element* element) const; + bool matchPseudoClassSelector(const PseudoClassSelector& selector, const Element* element) const; private: - std::multimap, std::less> m_selectors; + Selector m_selector; + PropertyList m_properties; + uint32_t m_specificity; + uint32_t m_position; }; -class CSSParser -{ +inline bool operator<(const RuleData& a, const RuleData& b) { return std::tie(a.specificity(), a.position()) < std::tie(b.specificity(), b.position()); } +inline bool operator>(const RuleData& a, const RuleData& b) { return std::tie(a.specificity(), a.position()) > std::tie(b.specificity(), b.position()); } + +class StyleSheet { public: - CSSParser() = default; + StyleSheet() = default; - bool parseMore(const std::string& value); + void parse(const std::string& content); + void add(const Rule& rule); + bool empty() const { return m_position == 0; } - const std::vector& rules() const { return m_rules; } + std::vector match(const Element* element) const; private: - bool parseAtRule(const char*& ptr, const char* end) const; - bool parseRule(const char*& ptr, const char* end, Rule& rule) const; - bool parseSelectors(const char*& ptr, const char* end, SelectorList& selectors) const; - bool parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations) const; - bool parseSelector(const char*& ptr, const char* end, Selector& selector) const; - bool parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector) const; + std::multiset m_rules; + uint32_t m_position{0}; +}; + +class CSSParser +{ +public: + CSSParser() = default; + + static bool parseSheet(StyleSheet* sheet, const std::string& value); private: - std::vector m_rules; + static bool parseAtRule(const char*& ptr, const char* end); + static bool parseRule(const char*& ptr, const char* end, Rule& rule); + static bool parseSelectors(const char*& ptr, const char* end, SelectorList& selectors); + static bool parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations); + static bool parseSelector(const char*& ptr, const char* end, Selector& selector); + static bool parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector); }; class LayoutSymbol; -class ParseDocument -{ +class TreeBuilder { public: - ParseDocument(); - ~ParseDocument(); + TreeBuilder(); + ~TreeBuilder(); bool parse(const char* data, std::size_t size); SVGElement* rootElement() const { return m_rootElement.get(); } Element* getElementById(const std::string& id) const; - std::unique_ptr layout() const; + std::unique_ptr build() const; private: std::unique_ptr m_rootElement; diff --git a/source/property.cpp b/source/property.cpp index 97ddf37..054ec23 100644 --- a/source/property.cpp +++ b/source/property.cpp @@ -2,21 +2,26 @@ #include "styledelement.h" #include "lunasvg.h" +#include #include namespace lunasvg { -const Color Color::Black{0, 0, 0, 1}; -const Color Color::White{1, 1, 1, 1}; -const Color Color::Red{1, 0, 0, 1}; -const Color Color::Green{0, 1, 0, 1}; -const Color Color::Blue{0, 0, 1, 1}; -const Color Color::Yellow{1, 1, 0, 1}; -const Color Color::Transparent{0, 0, 0, 0}; +const Color Color::Black(0x000000FF); +const Color Color::White(0xFFFFFFFF); +const Color Color::Transparent(0x00000000); -Color::Color(double r, double g, double b, double a) - : r(r), g(g), b(b), a(a) +Color& Color::combine(double opacity) { + *this = combined(opacity); + return *this; +} + +Color Color::combined(double opacity) const +{ + auto rgb = m_value & 0x00FFFFFF; + auto a = static_cast(std::clamp(0.0, 255.0, opacity * alpha())); + return Color(rgb | a << 24); } Paint::Paint(const Color& color) diff --git a/source/property.h b/source/property.h index 52a07e5..6195810 100644 --- a/source/property.h +++ b/source/property.h @@ -68,23 +68,27 @@ class Color { public: Color() = default; - Color(double r, double g, double b, double a = 1); + explicit Color(uint32_t value) : m_value(value) {} + Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : m_value(a << 24 | r << 16 | g << 8 | b) {} - bool isNone() const { return a == 0.0; } + uint8_t alpha() const { return (m_value >> 24) & 0xff; } + uint8_t red() const { return (m_value >> 16) & 0xff; } + uint8_t green() const { return (m_value >> 8) & 0xff; } + uint8_t blue() const { return (m_value >> 0) & 0xff; } + + uint32_t value() const { return m_value; } + + Color& combine(double opacity); + Color combined(double opacity) const; + + bool isNone() const { return m_value == 0; } static const Color Black; static const Color White; - static const Color Red; - static const Color Green; - static const Color Blue; - static const Color Yellow; static const Color Transparent; -public: - double r{0}; - double g{0}; - double b{0}; - double a{1}; +private: + uint32_t m_value{0}; }; class Paint diff --git a/source/stopelement.cpp b/source/stopelement.cpp index 4531383..f38d771 100644 --- a/source/stopelement.cpp +++ b/source/stopelement.cpp @@ -4,20 +4,20 @@ namespace lunasvg { StopElement::StopElement() - : StyledElement(ElementId::Stop) + : StyledElement(ElementID::Stop) { } double StopElement::offset() const { - auto& value = get(PropertyId::Offset); + auto& value = get(PropertyID::Offset); return Parser::parseNumberPercentage(value, 0.0); } Color StopElement::stopColorWithOpacity() const { auto color = stop_color(); - color.a = stop_opacity(); + color.combine(stop_opacity()); return color; } diff --git a/source/styledelement.cpp b/source/styledelement.cpp index bfb5ad8..fda779d 100644 --- a/source/styledelement.cpp +++ b/source/styledelement.cpp @@ -3,164 +3,164 @@ namespace lunasvg { -StyledElement::StyledElement(ElementId id) +StyledElement::StyledElement(ElementID id) : Element(id) { } Paint StyledElement::fill() const { - auto& value = find(PropertyId::Fill); + auto& value = find(PropertyID::Fill); return Parser::parsePaint(value, this, Color::Black); } Paint StyledElement::stroke() const { - auto& value = find(PropertyId::Stroke); + auto& value = find(PropertyID::Stroke); return Parser::parsePaint(value, this, Color::Transparent); } Color StyledElement::color() const { - auto& value = find(PropertyId::Color); + auto& value = find(PropertyID::Color); return Parser::parseColor(value, this, Color::Black); } Color StyledElement::stop_color() const { - auto& value = find(PropertyId::Stop_Color); + auto& value = find(PropertyID::Stop_Color); return Parser::parseColor(value, this, Color::Black); } Color StyledElement::solid_color() const { - auto& value = find(PropertyId::Solid_Color); + auto& value = find(PropertyID::Solid_Color); return Parser::parseColor(value, this, Color::Black); } double StyledElement::opacity() const { - auto& value = get(PropertyId::Opacity); + auto& value = get(PropertyID::Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::fill_opacity() const { - auto& value = find(PropertyId::Fill_Opacity); + auto& value = find(PropertyID::Fill_Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::stroke_opacity() const { - auto& value = find(PropertyId::Stroke_Opacity); + auto& value = find(PropertyID::Stroke_Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::stop_opacity() const { - auto& value = find(PropertyId::Stop_Opacity); + auto& value = find(PropertyID::Stop_Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::solid_opacity() const { - auto& value = find(PropertyId::Solid_Opacity); + auto& value = find(PropertyID::Solid_Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::stroke_miterlimit() const { - auto& value = find(PropertyId::Stroke_Miterlimit); + auto& value = find(PropertyID::Stroke_Miterlimit); return Parser::parseNumber(value, 4.0); } Length StyledElement::stroke_width() const { - auto& value = find(PropertyId::Stroke_Width); + auto& value = find(PropertyID::Stroke_Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::One); } Length StyledElement::stroke_dashoffset() const { - auto& value = find(PropertyId::Stroke_Dashoffset); + auto& value = find(PropertyID::Stroke_Dashoffset); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } LengthList StyledElement::stroke_dasharray() const { - auto& value = find(PropertyId::Stroke_Dasharray); + auto& value = find(PropertyID::Stroke_Dasharray); return Parser::parseLengthList(value, ForbidNegativeLengths); } WindRule StyledElement::fill_rule() const { - auto& value = find(PropertyId::Fill_Rule); + auto& value = find(PropertyID::Fill_Rule); return Parser::parseWindRule(value); } WindRule StyledElement::clip_rule() const { - auto& value = find(PropertyId::Clip_Rule); + auto& value = find(PropertyID::Clip_Rule); return Parser::parseWindRule(value); } LineCap StyledElement::stroke_linecap() const { - auto& value = find(PropertyId::Stroke_Linecap); + auto& value = find(PropertyID::Stroke_Linecap); return Parser::parseLineCap(value); } LineJoin StyledElement::stroke_linejoin() const { - auto& value = find(PropertyId::Stroke_Linejoin); + auto& value = find(PropertyID::Stroke_Linejoin); return Parser::parseLineJoin(value); } Display StyledElement::display() const { - auto& value = get(PropertyId::Display); + auto& value = get(PropertyID::Display); return Parser::parseDisplay(value); } Visibility StyledElement::visibility() const { - auto& value = find(PropertyId::Visibility); + auto& value = find(PropertyID::Visibility); return Parser::parseVisibility(value); } Overflow StyledElement::overflow() const { - auto& value = get(PropertyId::Overflow); + auto& value = get(PropertyID::Overflow); return Parser::parseOverflow(value, parent == nullptr ? Overflow::Visible : Overflow::Hidden); } std::string StyledElement::clip_path() const { - auto& value = get(PropertyId::Clip_Path); + auto& value = get(PropertyID::Clip_Path); return Parser::parseUrl(value); } std::string StyledElement::mask() const { - auto& value = get(PropertyId::Mask); + auto& value = get(PropertyID::Mask); return Parser::parseUrl(value); } std::string StyledElement::marker_start() const { - auto& value = find(PropertyId::Marker_Start); + auto& value = find(PropertyID::Marker_Start); return Parser::parseUrl(value); } std::string StyledElement::marker_mid() const { - auto& value = find(PropertyId::Marker_Mid); + auto& value = find(PropertyID::Marker_Mid); return Parser::parseUrl(value); } std::string StyledElement::marker_end() const { - auto& value = find(PropertyId::Marker_End); + auto& value = find(PropertyID::Marker_End); return Parser::parseUrl(value); } diff --git a/source/styledelement.h b/source/styledelement.h index 1257548..4d0ceb0 100644 --- a/source/styledelement.h +++ b/source/styledelement.h @@ -8,7 +8,7 @@ namespace lunasvg { class StyledElement : public Element { public: - StyledElement(ElementId id); + StyledElement(ElementID id); Paint fill() const; Paint stroke() const; diff --git a/source/styleelement.cpp b/source/styleelement.cpp index 800146c..3b1de57 100644 --- a/source/styleelement.cpp +++ b/source/styleelement.cpp @@ -3,7 +3,7 @@ namespace lunasvg { StyleElement::StyleElement() - : Element(ElementId::Style) + : Element(ElementID::Style) { } diff --git a/source/svgelement.cpp b/source/svgelement.cpp index 1468045..e4b1a1d 100644 --- a/source/svgelement.cpp +++ b/source/svgelement.cpp @@ -5,47 +5,47 @@ namespace lunasvg { SVGElement::SVGElement() - : GraphicsElement(ElementId::Svg) + : GraphicsElement(ElementID::Svg) { } Length SVGElement::x() const { - auto& value = get(PropertyId::X); + auto& value = get(PropertyID::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length SVGElement::y() const { - auto& value = get(PropertyId::Y); + auto& value = get(PropertyID::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length SVGElement::width() const { - auto& value = get(PropertyId::Width); + auto& value = get(PropertyID::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Length SVGElement::height() const { - auto& value = get(PropertyId::Height); + auto& value = get(PropertyID::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Rect SVGElement::viewBox() const { - auto& value = get(PropertyId::ViewBox); + auto& value = get(PropertyID::ViewBox); return Parser::parseViewBox(value); } PreserveAspectRatio SVGElement::preserveAspectRatio() const { - auto& value = get(PropertyId::PreserveAspectRatio); + auto& value = get(PropertyID::PreserveAspectRatio); return Parser::parsePreserveAspectRatio(value); } -std::unique_ptr SVGElement::layoutDocument(const ParseDocument* document) const +std::unique_ptr SVGElement::build(const TreeBuilder* builder) const { if(isDisplayNone()) return nullptr; @@ -73,7 +73,7 @@ std::unique_ptr SVGElement::layoutDocument(const ParseDocument* do root->clip = isOverflowHidden() ? preserveAspectRatio.getClip(_w, _h, viewBox) : Rect::Invalid; root->opacity = opacity(); - LayoutContext context(document, root.get()); + LayoutContext context(builder, root.get()); root->masker = context.getMasker(mask()); root->clipper = context.getClipper(clip_path()); layoutChildren(&context, root.get()); diff --git a/source/svgelement.h b/source/svgelement.h index b6e6234..789a7ca 100644 --- a/source/svgelement.h +++ b/source/svgelement.h @@ -5,7 +5,7 @@ namespace lunasvg { -class ParseDocument; +class TreeBuilder; class LayoutSymbol; class SVGElement : public GraphicsElement @@ -20,7 +20,7 @@ class SVGElement : public GraphicsElement Rect viewBox() const; PreserveAspectRatio preserveAspectRatio() const; - std::unique_ptr layoutDocument(const ParseDocument* document) const; + std::unique_ptr build(const TreeBuilder* builder) const; void layout(LayoutContext* context, LayoutContainer* current) const; std::unique_ptr clone() const; diff --git a/source/symbolelement.cpp b/source/symbolelement.cpp index 10d451c..03673d8 100644 --- a/source/symbolelement.cpp +++ b/source/symbolelement.cpp @@ -4,43 +4,43 @@ namespace lunasvg { SymbolElement::SymbolElement() - : StyledElement(ElementId::Symbol) + : StyledElement(ElementID::Symbol) { } Length SymbolElement::x() const { - auto& value = get(PropertyId::X); + auto& value = get(PropertyID::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length SymbolElement::y() const { - auto& value = get(PropertyId::Y); + auto& value = get(PropertyID::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length SymbolElement::width() const { - auto& value = get(PropertyId::Width); + auto& value = get(PropertyID::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Length SymbolElement::height() const { - auto& value = get(PropertyId::Height); + auto& value = get(PropertyID::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Rect SymbolElement::viewBox() const { - auto& value = get(PropertyId::ViewBox); + auto& value = get(PropertyID::ViewBox); return Parser::parseViewBox(value); } PreserveAspectRatio SymbolElement::preserveAspectRatio() const { - auto& value = get(PropertyId::PreserveAspectRatio); + auto& value = get(PropertyID::PreserveAspectRatio); return Parser::parsePreserveAspectRatio(value); } diff --git a/source/useelement.cpp b/source/useelement.cpp index f673833..ced0ec0 100644 --- a/source/useelement.cpp +++ b/source/useelement.cpp @@ -8,47 +8,47 @@ namespace lunasvg { UseElement::UseElement() - : GraphicsElement(ElementId::Use) + : GraphicsElement(ElementID::Use) { } Length UseElement::x() const { - auto& value = get(PropertyId::X); + auto& value = get(PropertyID::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length UseElement::y() const { - auto& value = get(PropertyId::Y); + auto& value = get(PropertyID::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length UseElement::width() const { - auto& value = get(PropertyId::Width); + auto& value = get(PropertyID::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Length UseElement::height() const { - auto& value = get(PropertyId::Height); + auto& value = get(PropertyID::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } std::string UseElement::href() const { - auto& value = get(PropertyId::Href); + auto& value = get(PropertyID::Href); return Parser::parseHref(value); } void UseElement::transferWidthAndHeight(Element* element) const { - auto& width = get(PropertyId::Width); - auto& height = get(PropertyId::Height); + auto& width = get(PropertyID::Width); + auto& height = get(PropertyID::Height); - element->set(PropertyId::Width, width, 0x0); - element->set(PropertyId::Height, height, 0x0); + element->set(PropertyID::Width, width, 0x0); + element->set(PropertyID::Height, height, 0x0); } void UseElement::layout(LayoutContext* context, LayoutContainer* current) const @@ -69,15 +69,15 @@ void UseElement::layout(LayoutContext* context, LayoutContainer* current) const auto _x = lengthContext.valueForLength(x(), LengthMode::Width); auto _y = lengthContext.valueForLength(y(), LengthMode::Height); - auto transform = get(PropertyId::Transform); + auto transform = get(PropertyID::Transform); transform += "translate("; transform += std::to_string(_x); transform += ' '; transform += std::to_string(_y); transform += ')'; - group->set(PropertyId::Transform, transform, 0x10); + group->set(PropertyID::Transform, transform, 0x10); - if(ref->id == ElementId::Svg || ref->id == ElementId::Symbol) + if(ref->id == ElementID::Svg || ref->id == ElementID::Symbol) { auto element = ref->cloneElement(); transferWidthAndHeight(element.get());