Merge commit '3acd894f373efcdc18f682a826f78f6722f32c59' into new-svg-renderer

This commit is contained in:
Leon Styhre 2022-10-03 20:53:48 +02:00
commit 261f699bb6
39 changed files with 4485 additions and 883 deletions

View file

@ -13,7 +13,7 @@ static SW_FT_Outline* sw_ft_outline_create(int points, int contours)
SW_FT_Outline* ft = malloc(sizeof(SW_FT_Outline)); SW_FT_Outline* ft = malloc(sizeof(SW_FT_Outline));
ft->points = malloc((size_t)(points + contours) * sizeof(SW_FT_Vector)); ft->points = malloc((size_t)(points + contours) * sizeof(SW_FT_Vector));
ft->tags = malloc((size_t)(points + contours) * sizeof(char)); ft->tags = malloc((size_t)(points + contours) * sizeof(char));
ft->contours = malloc((size_t)contours * sizeof(short)); ft->contours = malloc((size_t)contours * sizeof(int));
ft->contours_flag = malloc((size_t)contours * sizeof(char)); ft->contours_flag = malloc((size_t)contours * sizeof(char));
ft->n_points = ft->n_contours = 0; ft->n_points = ft->n_contours = 0;
ft->flags = 0x0; ft->flags = 0x0;

View file

@ -114,12 +114,12 @@
/* */ /* */
typedef struct SW_FT_Outline_ typedef struct SW_FT_Outline_
{ {
short n_contours; /* number of contours in glyph */ int n_contours; /* number of contours in glyph */
short n_points; /* number of points in the glyph */ int n_points; /* number of points in the glyph */
SW_FT_Vector* points; /* the outline's points */ SW_FT_Vector* points; /* the outline's points */
char* tags; /* the points flags */ char* tags; /* the points flags */
short* contours; /* the contour end points */ int* contours; /* the contour end points */
char* contours_flag; /* the contour open flags */ char* contours_flag; /* the contour open flags */
int flags; /* outline masks */ int flags; /* outline masks */

View file

@ -650,8 +650,8 @@ static void ft_stroke_border_export(SW_FT_StrokeBorder border,
{ {
SW_FT_UInt count = border->num_points; SW_FT_UInt count = border->num_points;
SW_FT_Byte* tags = border->tags; SW_FT_Byte* tags = border->tags;
SW_FT_Short* write = outline->contours + outline->n_contours; SW_FT_Int* write = outline->contours + outline->n_contours;
SW_FT_Short idx = (SW_FT_Short)outline->n_points; SW_FT_Int idx = (SW_FT_Int)outline->n_points;
for (; count > 0; count--, tags++, idx++) { for (; count > 0; count--, tags++, idx++) {
if (*tags & SW_FT_STROKE_TAG_END) { if (*tags & SW_FT_STROKE_TAG_END) {
@ -661,7 +661,7 @@ static void ft_stroke_border_export(SW_FT_StrokeBorder border,
} }
} }
outline->n_points = (short)(outline->n_points + border->num_points); outline->n_points = (int)(outline->n_points + border->num_points);
assert(SW_FT_Outline_Check(outline) == 0); assert(SW_FT_Outline_Check(outline) == 0);
} }

View file

@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.3)
project(lunasvg VERSION 2.3.2 LANGUAGES CXX C) project(lunasvg VERSION 2.3.2 LANGUAGES CXX C)
set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
option(BUILD_SHARED_LIBS "Builds as shared library" OFF) option(BUILD_SHARED_LIBS "Builds as shared library" OFF)

View file

@ -39,6 +39,7 @@
namespace lunasvg { namespace lunasvg {
class Rect; class Rect;
class Matrix;
class LUNASVG_API Box class LUNASVG_API Box
{ {
@ -47,6 +48,9 @@ public:
Box(double x, double y, double w, double h); Box(double x, double y, double w, double h);
Box(const Rect& rect); Box(const Rect& rect);
Box& transform(const Matrix& matrix);
Box transformed(const Matrix& matrix) const;
public: public:
double x{0}; double x{0};
double y{0}; double y{0};
@ -78,7 +82,6 @@ public:
Matrix inverted() const; Matrix inverted() const;
Matrix operator*(const Matrix& matrix) const; Matrix operator*(const Matrix& matrix) const;
Box map(const Box& box) const;
static Matrix rotated(double angle); static Matrix rotated(double angle);
static Matrix rotated(double angle, double cx, double cy); static Matrix rotated(double angle, double cx, double cy);
@ -159,63 +162,9 @@ public:
static std::unique_ptr<Document> loadFromData(const char* data); static std::unique_ptr<Document> loadFromData(const char* data);
/** /**
* @brief Pre-Rotates the document matrix clockwise around the current origin * @brief Sets the current transformation matrix of the document
* @param angle - rotation angle, in degrees * @param matrix - current transformation matrix
* @return this
*/ */
Document* rotate(double angle);
/**
* @brief Pre-Rotates the document matrix clockwise around the given point
* @param angle - rotation angle, in degrees
* @param cx - horizontal translation
* @param cy - vertical translation
* @return this
*/
Document* rotate(double angle, double cx, double cy);
/**
* @brief Pre-Scales the document matrix by sx horizontally and sy vertically
* @param sx - horizontal scale factor
* @param sy - vertical scale factor
* @return this
*/
Document* scale(double sx, double sy);
/**
* @brief Pre-Shears the document matrix by shx horizontally and shy vertically
* @param shx - horizontal skew factor, in degree
* @param shy - vertical skew factor, in degree
* @return this
*/
Document* shear(double shx, double shy);
/**
* @brief Pre-Translates the document matrix by tx horizontally and ty vertically
* @param tx - horizontal translation
* @param ty - vertical translation
* @return this
*/
Document* translate(double tx, double ty);
/**
* @brief Pre-Multiplies the document matrix by Matrix(a, b, c, d, e, f)
* @param a - horizontal scale factor
* @param b - horizontal skew factor
* @param c - vertical skew factor
* @param d - vertical scale factor
* @param e - horizontal translation
* @param f - vertical translation
* @return this
*/
Document* transform(double a, double b, double c, double d, double e, double f);
/**
* @brief Resets the document matrix to identity
* @return this
*/
Document* identity();
void setMatrix(const Matrix& matrix); void setMatrix(const Matrix& matrix);
/** /**

View file

@ -20,6 +20,8 @@ PRIVATE
"${CMAKE_CURRENT_LIST_DIR}/svgelement.cpp" "${CMAKE_CURRENT_LIST_DIR}/svgelement.cpp"
"${CMAKE_CURRENT_LIST_DIR}/symbolelement.cpp" "${CMAKE_CURRENT_LIST_DIR}/symbolelement.cpp"
"${CMAKE_CURRENT_LIST_DIR}/useelement.cpp" "${CMAKE_CURRENT_LIST_DIR}/useelement.cpp"
"${CMAKE_CURRENT_LIST_DIR}/cssparser.cpp"
"${CMAKE_CURRENT_LIST_DIR}/cssstylesheet.cpp"
) )
target_include_directories(lunasvg target_include_directories(lunasvg

View file

@ -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_operator_t to_plutovg_operator(BlendMode mode);
static plutovg_line_cap_t to_plutovg_line_cap(LineCap cap); static plutovg_line_cap_t to_plutovg_line_cap(LineCap cap);
static plutovg_line_join_t to_plutovg_line_join(LineJoin join); 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_stops(plutovg_gradient_t* gradient, const GradientStops& stops);
static void to_plutovg_path(plutovg_t* pluto, const Path& path); static void to_plutovg_path(plutovg_t* pluto, const Path& path);
@ -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 gradient = plutovg_gradient_create_linear(x1, y1, x2, y2);
auto matrix = to_plutovg_matrix(transform); auto matrix = to_plutovg_matrix(transform);
to_plutovg_stops(gradient, stops); 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_gradient_set_matrix(gradient, &matrix);
plutovg_set_source_gradient(pluto, gradient); plutovg_set_source_gradient(pluto, gradient);
plutovg_gradient_destroy(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 gradient = plutovg_gradient_create_radial(cx, cy, r, fx, fy, 0);
auto matrix = to_plutovg_matrix(transform); auto matrix = to_plutovg_matrix(transform);
to_plutovg_stops(gradient, stops); 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_gradient_set_matrix(gradient, &matrix);
plutovg_set_source_gradient(pluto, gradient); plutovg_set_source_gradient(pluto, gradient);
plutovg_gradient_destroy(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; 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; return spread == SpreadMethod::Pad ? plutovg_spread_method_pad : spread == SpreadMethod::Reflect ? plutovg_spread_method_reflect : plutovg_spread_method_repeat;
} }

View file

@ -5,13 +5,13 @@
namespace lunasvg { namespace lunasvg {
ClipPathElement::ClipPathElement() ClipPathElement::ClipPathElement()
: GraphicsElement(ElementId::ClipPath) : GraphicsElement(ElementID::ClipPath)
{ {
} }
Units ClipPathElement::clipPathUnits() const Units ClipPathElement::clipPathUnits() const
{ {
auto& value = get(PropertyId::ClipPathUnits); auto& value = get(PropertyID::ClipPathUnits);
return Parser::parseUnits(value, Units::UserSpaceOnUse); return Parser::parseUnits(value, Units::UserSpaceOnUse);
} }

1816
external/lunasvg/source/cssparser.cpp vendored Normal file

File diff suppressed because it is too large Load diff

305
external/lunasvg/source/cssparser.h vendored Normal file
View file

@ -0,0 +1,305 @@
#ifndef CSSPARSER_H
#define CSSPARSER_H
#include "parserutils.h"
#include "cssstylesheet.h"
#include <vector>
namespace lunasvg {
class CSSToken {
public:
enum class Type : uint8_t {
Unknown,
Ident,
Function,
AtKeyword,
Hash,
String,
BadString,
Url,
BadUrl,
Delim,
Number,
Percentage,
Dimension,
Whitespace,
Comment,
CDO,
CDC,
Colon,
Semicolon,
Comma,
LeftParenthesis,
RightParenthesis,
LeftSquareBracket,
RightSquareBracket,
LeftCurlyBracket,
RightCurlyBracket,
EndOfFile
};
enum class HashType : uint8_t {
Identifier,
Unrestricted
};
enum class NumberType : uint8_t {
Integer,
Number
};
enum class NumberSign : uint8_t {
None,
Plus,
Minus
};
CSSToken() = default;
explicit CSSToken(Type type) : m_type(type) {}
CSSToken(Type type, uint32_t delim) : m_type(type), m_delim(delim) {}
CSSToken(Type type, std::string_view data) : m_type(type), m_data(data) {}
CSSToken(Type type, HashType hashType, std::string_view data) : m_type(type), m_hashType(hashType), m_data(data) {}
CSSToken(Type type, NumberType numberType, NumberSign numberSign, double number)
: m_type(type), m_numberType(numberType), m_numberSign(numberSign), m_number(number)
{}
CSSToken(Type type, NumberType numberType, NumberSign numberSign, double number, std::string_view unit)
: m_type(type), m_numberType(numberType), m_numberSign(numberSign), m_number(number), m_data(unit)
{}
Type type() const { return m_type; }
HashType hashType() const { return m_hashType; }
NumberType numberType() const { return m_numberType; }
NumberSign numberSign() const { return m_numberSign; }
uint32_t delim() const { return m_delim; }
double number() const { return m_number; }
int integer() const { return static_cast<int>(m_number); }
const std::string_view& data() const { return m_data; }
static Type closeType(Type type) {
switch(type) {
case Type::Function:
case Type::LeftParenthesis:
return Type::RightParenthesis;
case Type::LeftSquareBracket:
return Type::RightSquareBracket;
case Type::LeftCurlyBracket:
return Type::RightCurlyBracket;
default:
assert(false);
}
}
private:
Type m_type{Type::Unknown};
HashType m_hashType{HashType::Identifier};
NumberType m_numberType{NumberType::Integer};
NumberSign m_numberSign{NumberSign::None};
uint32_t m_delim{0};
double m_number{0};
std::string_view m_data;
};
using CSSTokenList = std::vector<CSSToken>;
class CSSTokenStream {
public:
CSSTokenStream(const CSSToken* begin, const CSSToken* end)
: m_begin(begin), m_end(end)
{}
const CSSToken& peek() const {
static const CSSToken EndOfFileToken(CSSToken::Type::EndOfFile);
if(m_begin == m_end)
return EndOfFileToken;
return *m_begin;
}
void consume() {
assert(m_begin < m_end);
m_begin += 1;
}
void consumeWhitespace() {
while(m_begin < m_end && m_begin->type() == CSSToken::Type::Whitespace) {
m_begin += 1;
}
}
void consumeIncludingWhitespace() {
assert(m_begin < m_end);
do {
m_begin += 1;
} while(m_begin < m_end && m_begin->type() == CSSToken::Type::Whitespace);
}
void consumeComponent() {
assert(m_begin < m_end);
switch(m_begin->type()) {
case CSSToken::Type::Function:
case CSSToken::Type::LeftParenthesis:
case CSSToken::Type::LeftSquareBracket:
case CSSToken::Type::LeftCurlyBracket: {
auto closeType = CSSToken::closeType(m_begin->type());
m_begin += 1;
while(m_begin < m_end && m_begin->type() != closeType)
consumeComponent();
if(m_begin < m_end)
m_begin += 1;
break;
}
default:
m_begin += 1;
break;
}
}
CSSTokenStream consumeBlock() {
assert(m_begin < m_end);
auto closeType = CSSToken::closeType(m_begin->type());
m_begin += 1;
auto blockBegin = m_begin;
while(m_begin < m_end && m_begin->type() != closeType)
consumeComponent();
auto blockEnd = m_begin;
if(m_begin < m_end)
m_begin += 1;
return CSSTokenStream(blockBegin, blockEnd);
}
bool empty() const { return m_begin == m_end; }
const CSSToken& operator*() const { return peek(); }
const CSSToken* operator->() const { return &peek(); }
const CSSToken* begin() const { return m_begin; }
const CSSToken* end() const { return m_end; }
private:
const CSSToken* m_begin;
const CSSToken* m_end;
};
class CSSTokenStreamGuard {
public:
CSSTokenStreamGuard(CSSTokenStream& input)
: m_input(input), m_state(input)
{}
void release() { m_state = m_input; }
~CSSTokenStreamGuard() { m_input = m_state; }
private:
CSSTokenStream& m_input;
CSSTokenStream m_state;
};
class CSSTokenizer {
public:
CSSTokenizer(const std::string_view& input)
: m_input(input)
{}
CSSTokenStream tokenize();
private:
static bool isEscapeSequence(char first, char second);
static bool isIdentSequence(char first, char second, char third);
static bool isNumberSequence(char first, char second, char third);
bool isEscapeSequence() const;
bool isIdentSequence() const;
bool isNumberSequence() const;
bool isExponentSequence() const;
std::string_view substring(size_t offset, size_t count);
std::string_view addstring(std::string&& value);
std::string_view consumeName();
uint32_t consumeEscape();
CSSToken consumeStringToken();
CSSToken consumeNumericToken();
CSSToken consumeIdentLikeToken();
CSSToken consumeUrlToken();
CSSToken consumeBadUrlRemnants();
CSSToken consumeWhitespaceToken();
CSSToken consumeCommentToken();
CSSToken consumeSolidusToken();
CSSToken consumeHashToken();
CSSToken consumePlusSignToken();
CSSToken consumeHyphenMinusToken();
CSSToken consumeFullStopToken();
CSSToken consumeLessThanSignToken();
CSSToken consumeCommercialAtToken();
CSSToken consumeReverseSolidusToken();
CSSToken nextToken();
private:
using StringList = std::vector<std::string>;
ParserString m_input;
CSSTokenList m_tokenList;
StringList m_stringList;
};
class CSSParser {
public:
static void parseSheet(CSSRuleList& rules, const std::string_view& content);
static void parseStyle(CSSPropertyList& properties, const std::string_view& content);
private:
static bool consumeRule(CSSTokenStream& input, CSSRuleList& rules);
static bool consumeStyleRule(CSSTokenStream& input, CSSRuleList& rules);
static bool consumeAtRule(CSSTokenStream& input, CSSRuleList& rules);
static bool consumeImportRule(CSSTokenStream& input, CSSRuleList& rules);
static bool consumeSelectorList(CSSTokenStream& input, CSSSelectorList& selectors);
static bool consumeSelector(CSSTokenStream& input, CSSSelector& selector);
static bool consumeSimpleSelector(CSSTokenStream& input, CSSSimpleSelector& selector);
static bool consumeTagSelector(CSSTokenStream& input, CSSSimpleSelector& selector);
static bool consumeIdSelector(CSSTokenStream& input, CSSSimpleSelector& selector);
static bool consumeClassSelector(CSSTokenStream& input, CSSSimpleSelector& selector);
static bool consumeAttributeSelector(CSSTokenStream& input, CSSSimpleSelector& selector);
static bool consumePseudoSelector(CSSTokenStream& input, CSSSimpleSelector& selector);
static bool consumeCombinator(CSSTokenStream& input, CSSSimpleSelector::Combinator& combinator);
static bool consumeMatchPattern(CSSTokenStream& input, CSSSimpleSelector::Pseudo::MatchPattern& pattern);
static void consumeDeclaractionList(CSSTokenStream& input, CSSPropertyList& properties);
static bool consumeDeclaraction(CSSTokenStream& input, CSSPropertyList& properties);
static bool consumeDeclaractionValue(CSSTokenStream& input, CSSPropertyList& properties, CSSPropertyID id, bool important);
static RefPtr<CSSValue> consumeNone(CSSTokenStream& input);
static RefPtr<CSSValue> consumeNormal(CSSTokenStream& input);
static RefPtr<CSSValue> consumePercent(CSSTokenStream& input, bool negative);
static RefPtr<CSSValue> consumeNumber(CSSTokenStream& input, bool negative);
static RefPtr<CSSValue> consumeLength(CSSTokenStream& input, bool negative, bool unitless);
static RefPtr<CSSValue> consumeLengthOrNormal(CSSTokenStream& input, bool negative, bool unitless);
static RefPtr<CSSValue> consumeLengthOrPercent(CSSTokenStream& input, bool negative, bool unitless);
static RefPtr<CSSValue> consumeNumberOrPercent(CSSTokenStream& input, bool negative);
static RefPtr<CSSValue> consumeUrl(CSSTokenStream& input);
static RefPtr<CSSValue> consumeUrlOrNone(CSSTokenStream& input);
static RefPtr<CSSValue> consumeColor(CSSTokenStream& input);
static RefPtr<CSSValue> consumeRgb(CSSTokenStream& input);
static RefPtr<CSSValue> consumeFillOrStroke(CSSTokenStream& input);
static RefPtr<CSSValue> consumeDashList(CSSTokenStream& input);
static RefPtr<CSSValue> consumeFontWeight(CSSTokenStream& input);
static RefPtr<CSSValue> consumeFontSize(CSSTokenStream& input, bool unitless);
static RefPtr<CSSValue> consumeFontFamilyValue(CSSTokenStream& input);
static RefPtr<CSSValue> consumeFontFamily(CSSTokenStream& input);
static RefPtr<CSSValue> consumeValue(CSSTokenStream& input, CSSPropertyID id);
};
} // namespace lunasvg
#endif // CSSPARSER_H

View file

@ -0,0 +1,378 @@
#include "cssstylesheet.h"
#include "cssparser.h"
#include <cassert>
namespace lunasvg {
RefPtr<CSSInitialValue> CSSInitialValue::create()
{
static auto value = adoptPtr(new CSSInitialValue);
return value;
}
RefPtr<CSSInheritValue> CSSInheritValue::create()
{
static auto value = adoptPtr(new CSSInheritValue);
return value;
}
RefPtr<CSSIdentValue> CSSIdentValue::create(CSSValueID value)
{
static std::map<CSSValueID, RefPtr<CSSIdentValue>> table;
auto it = table.find(value);
if(it == table.end()) {
auto item = adoptPtr(new CSSIdentValue(value));
table.emplace(value, item);
return item;
}
return it->second;
}
RefPtr<CSSIntegerValue> CSSIntegerValue::create(int value)
{
return adoptPtr(new CSSIntegerValue(value));
}
RefPtr<CSSNumberValue> CSSNumberValue::create(double value)
{
return adoptPtr(new CSSNumberValue(value));
}
RefPtr<CSSPercentValue> CSSPercentValue::create(double value)
{
return adoptPtr(new CSSPercentValue(value));
}
RefPtr<CSSLengthValue> CSSLengthValue::create(double value, Unit unit)
{
return adoptPtr(new CSSLengthValue(value, unit));
}
RefPtr<CSSStringValue> CSSStringValue::create(std::string value)
{
return adoptPtr(new CSSStringValue(std::move(value)));
}
RefPtr<CSSUrlValue> CSSUrlValue::create(std::string value)
{
return adoptPtr(new CSSUrlValue(std::move(value)));
}
RefPtr<CSSColorValue> CSSColorValue::create(uint32_t value)
{
return adoptPtr(new CSSColorValue(value));
}
RefPtr<CSSColorValue> CSSColorValue::create(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
return adoptPtr(new CSSColorValue(a << 24 | r << 16 | g << 8 | b));
}
RefPtr<CSSPairValue> CSSPairValue::create(RefPtr<CSSValue> first, RefPtr<CSSValue> second)
{
return adoptPtr(new CSSPairValue(std::move(first), std::move(second)));
}
RefPtr<CSSListValue> CSSListValue::create(CSSValueList values)
{
return adoptPtr(new CSSListValue(std::move(values)));
}
CSSPropertyID csspropertyid(const std::string_view& name)
{
static const struct {
std::string_view name;
CSSPropertyID value;
} table[] = {
{"clip-path", CSSPropertyID::Clip_Path},
{"clip-rule", CSSPropertyID::Clip_Rule},
{"color", CSSPropertyID::Color},
{"display", CSSPropertyID::Display},
{"fill", CSSPropertyID::Fill},
{"fill-opacity", CSSPropertyID::Fill_Opacity},
{"fill-rule", CSSPropertyID::Fill_Rule},
{"font-family", CSSPropertyID::Font_Family},
{"font-size", CSSPropertyID::Font_Size},
{"font-style", CSSPropertyID::Font_Style},
{"font-variant", CSSPropertyID::Font_Variant},
{"font-weight", CSSPropertyID::Font_Weight},
{"letter-spacing", CSSPropertyID::Letter_Spacing},
{"marker-end", CSSPropertyID::Marker_End},
{"marker-mid", CSSPropertyID::Marker_Mid},
{"marker-start", CSSPropertyID::Marker_Start},
{"mask", CSSPropertyID::Mask},
{"opacity", CSSPropertyID::Opacity},
{"overflow", CSSPropertyID::Overflow},
{"solid-color", CSSPropertyID::Solid_Color},
{"solid-opacity", CSSPropertyID::Solid_Opacity},
{"stop-color", CSSPropertyID::Stop_Color},
{"stop-opacity", CSSPropertyID::Stop_Opacity},
{"stroke", CSSPropertyID::Stroke},
{"stroke-dasharray", CSSPropertyID::Stroke_Dasharray},
{"stroke-dashoffset", CSSPropertyID::Stroke_Dashoffset},
{"stroke-linecap", CSSPropertyID::Stroke_Linecap},
{"stroke-linejoin", CSSPropertyID::Stroke_Linejoin},
{"stroke-miterlimit", CSSPropertyID::Stroke_Miterlimit},
{"stroke-opacity", CSSPropertyID::Stroke_Opacity},
{"stroke-width", CSSPropertyID::Stroke_Width},
{"text-anchor", CSSPropertyID::Text_Anchor},
{"text-decoration", CSSPropertyID::Text_Decoration},
{"visibility", CSSPropertyID::Visibility},
{"word-spacing", CSSPropertyID::Word_Spacing},
};
auto it = std::lower_bound(table, std::end(table), name, [](auto& item, auto& name) { return item.name < name; });
if(it != std::end(table) && it->name == name)
return it->value;
return CSSPropertyID::Unknown;
}
bool CSSSimpleSelector::Pseudo::matchnth(size_t count) const
{
auto [a, b] = pattern;
if(a == 0)
return count == b;
if(a > 0) {
if(count < b)
return false;
return (count - b) % a == 0;
}
if(count > b)
return false;
return (b - count) % -a == 0;
}
std::unique_ptr<CSSRule> CSSRule::create(CSSSelectorList selectors, CSSPropertyList properties)
{
return std::unique_ptr<CSSRule>(new CSSRule(std::move(selectors), std::move(properties)));
}
bool CSSRuleData::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 CSSSimpleSelector::Combinator::Child:
case CSSSimpleSelector::Combinator::Descendant:
element = element->parent;
break;
case CSSSimpleSelector::Combinator::DirectAdjacent:
case CSSSimpleSelector::Combinator::InDirectAdjacent:
element = element->previousElement();
break;
default:
assert(false);
}
if(element == nullptr)
return false;
auto match = matchSimpleSelector(*it, element);
if(!match && (it->combinator != CSSSimpleSelector::Combinator::Descendant && it->combinator != CSSSimpleSelector::Combinator::InDirectAdjacent))
return false;
if(match || (it->combinator != CSSSimpleSelector::Combinator::Descendant && it->combinator != CSSSimpleSelector::Combinator::InDirectAdjacent))
++it;
}
return true;
}
bool CSSRuleData::matchSimpleSelector(const CSSSimpleSelector& selector, const Element* element)
{
if(selector.id != ElementID::Star && selector.id != element->id)
return false;
for(auto& attribute : selector.attributes)
if(!matchAttributeSelector(attribute, element))
return false;
for(auto& pseudo : selector.pseudos)
if(!matchPseudoClassSelector(pseudo, element))
return false;
return true;
}
bool CSSRuleData::matchAttributeSelector(const CSSSimpleSelector::Attribute& attribute, const Element* element)
{
auto& value = element->get(attribute.id);
switch(attribute.type) {
case CSSSimpleSelector::Attribute::Type::None:
return !value.empty();
case CSSSimpleSelector::Attribute::Type::Equals:
return equals(value, attribute.value, false);
case CSSSimpleSelector::Attribute::Type::Contains:
return contains(value, attribute.value, false);
case CSSSimpleSelector::Attribute::Type::Includes:
return includes(value, attribute.value, false);
case CSSSimpleSelector::Attribute::Type::StartsWith:
return startswith(value, attribute.value, false);
case CSSSimpleSelector::Attribute::Type::EndsWith:
return endswith(value, attribute.value, false);
case CSSSimpleSelector::Attribute::Type::DashEquals:
return dashequals(value, attribute.value, false);
default:
return false;
}
}
bool CSSRuleData::matchPseudoClassSelector(const CSSSimpleSelector::Pseudo& pseudo, const Element* element)
{
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::Empty)
return element->children.empty();
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::Root)
return element->parent == nullptr;
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::Is) {
for(auto& selector : pseudo.selectors) {
for(auto& sel : selector) {
if(!matchSimpleSelector(sel, element)) {
return false;
}
}
}
return true;
}
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::Not) {
for(auto& selector : pseudo.selectors) {
for(auto& sel : selector) {
if(matchSimpleSelector(sel, element)) {
return false;
}
}
}
return true;
}
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::FirstChild)
return !element->previousElement();
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::LastChild)
return !element->nextElement();
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::OnlyChild)
return !(element->previousElement() || element->nextElement());
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::FirstOfType) {
auto child = element->nextElement();
while(child) {
if(child->id == element->id)
return false;
child = element->nextElement();
}
return true;
}
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::LastOfType) {
auto child = element->nextElement();
while(child) {
if(child->id == element->id)
return false;
child = element->nextElement();
}
return true;
}
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::OnlyOfType) {
auto child = element->nextElement();
while(child) {
if(child->id == element->id)
return false;
child = element->nextElement();
}
child = element->nextElement();
while(child) {
if(child->id == element->id)
return false;
child = element->nextElement();
}
return true;
}
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::NthChild) {
int count = 1;
auto child = element->previousElement();
while(child) {
count += 1;
child = element->previousElement();
}
return pseudo.matchnth(count);
}
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::NthLastChild) {
int count = 1;
auto child = element->nextElement();
while(child) {
count += 1;
child = element->nextElement();
}
return pseudo.matchnth(count);
}
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::NthOfType) {
int count = 1;
auto child = element->previousElement();
while(child && child->id == element->id) {
count += 1;
child = element->previousElement();
}
return pseudo.matchnth(count);
}
if(pseudo.type == CSSSimpleSelector::Pseudo::Type::NthLastOfType) {
int count = 1;
auto child = element->nextElement();
while(child && child->id == element->id) {
count += 1;
child = element->nextElement();
}
return pseudo.matchnth(count);
}
return false;
}
void CSSStyleSheet::parse(const std::string_view& content)
{
auto position = m_ruleList.size();
CSSParser::parseSheet(m_ruleList, content);
for(; position < m_ruleList.size(); ++position) {
auto& rule = m_ruleList[position];
auto& properties = rule->properties();
for(auto& selector : rule->selectors()) {
uint32_t specificity = 0;
for(auto& sel : selector) {
specificity += (sel.id == ElementID::Star) ? 0x0 : 0x1;
for(auto& attribute : sel.attributes) {
specificity += (attribute.id == PropertyID::Id) ? 0x10000 : 0x100;
}
}
m_ruleSet.emplace(selector, properties, specificity, position);
}
}
}
} // namespace lunasvg

495
external/lunasvg/source/cssstylesheet.h vendored Normal file
View file

@ -0,0 +1,495 @@
#ifndef CSSSTYLESHEET_H
#define CSSSTYLESHEET_H
#include "element.h"
#include "pointer.h"
#include <map>
#include <set>
namespace lunasvg {
class CSSValue : public RefCounted<CSSValue> {
public:
virtual ~CSSValue() = default;
virtual bool isInitialValue() const { return false; }
virtual bool isInheritValue() const { return false; }
virtual bool isIdentValue() const { return false; }
virtual bool isIntegerValue() const { return false; }
virtual bool isNumberValue() const { return false; }
virtual bool isPercentValue() const { return false; }
virtual bool isLengthValue() const { return false; }
virtual bool isStringValue() const { return false; }
virtual bool isUrlValue() const { return false; }
virtual bool isColorValue() const { return false; }
virtual bool isPairValue() const { return false; }
virtual bool isListValue() const { return false; }
protected:
CSSValue() = default;
};
using CSSValueList = std::vector<RefPtr<CSSValue>>;
class CSSInitialValue final : public CSSValue {
public:
static RefPtr<CSSInitialValue> create();
bool isInitialValue() const final { return true; }
private:
CSSInitialValue() = default;
};
template<>
struct is<CSSInitialValue> {
static bool check(const CSSValue& value) { return value.isInitialValue(); }
};
class CSSInheritValue final : public CSSValue {
public:
static RefPtr<CSSInheritValue> create();
bool isInheritValue() const final { return true; }
private:
CSSInheritValue() = default;
};
template<>
struct is<CSSInheritValue> {
static bool check(const CSSValue& value) { return value.isInheritValue(); }
};
enum class CSSValueID {
Unknown,
Auto,
Bevel,
Bold,
Bolder,
Butt,
Clip,
Collapse,
Color,
CurrentColor,
End,
Evenodd,
Hidden,
Inherit,
Initial,
Inline,
Italic,
Large,
Larger,
Lighter,
Medium,
Middle,
Miter,
None,
Nonzero,
Normal,
Oblique,
Round,
Small,
SmallCaps,
Smaller,
Square,
Start,
Stroke,
Visible,
XLarge,
XSmall,
XxLarge,
XxSmall,
XxxLarge
};
class CSSIdentValue final : public CSSValue {
public:
static RefPtr<CSSIdentValue> create(CSSValueID value);
CSSValueID value() const { return m_value; }
bool isIdentValue() const final { return true; }
private:
CSSIdentValue(CSSValueID value) : m_value(value) {}
CSSValueID m_value;
};
template<>
struct is<CSSIdentValue> {
static bool check(const CSSValue& value) { return value.isIdentValue(); }
};
class CSSIntegerValue final : public CSSValue {
public:
static RefPtr<CSSIntegerValue> create(int value);
int value() const { return m_value; }
bool isIntegerValue() const final { return true; }
private:
CSSIntegerValue(int value) : m_value(value) {}
int m_value;
};
template<>
struct is<CSSIntegerValue> {
static bool check(const CSSValue& value) { return value.isIntegerValue(); }
};
class CSSNumberValue final : public CSSValue {
public:
static RefPtr<CSSNumberValue> create(double value);
double value() const { return m_value; }
bool isNumberValue() const final { return true; }
private:
CSSNumberValue(double value) : m_value(value) {}
double m_value;
};
template<>
struct is<CSSNumberValue> {
static bool check(const CSSValue& value) { return value.isNumberValue(); }
};
class CSSPercentValue final : public CSSValue {
public:
static RefPtr<CSSPercentValue> create(double value);
double value() const { return m_value; }
bool isPercentValue() const final { return true; }
private:
CSSPercentValue(double value) : m_value(value) {}
double m_value;
};
template<>
struct is<CSSPercentValue> {
static bool check(const CSSValue& value) { return value.isPercentValue(); }
};
class CSSLengthValue final : public CSSValue {
public:
enum class Unit {
None,
Ems,
Exs,
Pixels,
Centimeters,
Millimeters,
Inches,
Points,
Picas,
ViewportWidth,
ViewportHeight,
ViewportMin,
ViewportMax,
Rems,
Chs
};
static RefPtr<CSSLengthValue> create(double value, Unit unit);
double value() const { return m_value; }
Unit unit() const { return m_unit; }
bool isLengthValue() const final { return true; }
private:
CSSLengthValue(double value, Unit unit)
: m_value(value), m_unit(unit)
{}
double m_value;
Unit m_unit;
};
template<>
struct is<CSSLengthValue> {
static bool check(const CSSValue& value) { return value.isLengthValue(); }
};
class CSSStringValue final : public CSSValue {
public:
static RefPtr<CSSStringValue> create(std::string value);
const std::string& value() const { return m_value; }
bool isStringValue() const final { return true; }
private:
CSSStringValue(std::string value) : m_value(std::move(value)) {}
std::string m_value;
};
template<>
struct is<CSSStringValue> {
static bool check(const CSSValue& value) { return value.isStringValue(); }
};
class CSSUrlValue final : public CSSValue {
public:
static RefPtr<CSSUrlValue> create(std::string value);
const std::string& value() const { return m_value; }
bool isUrlValue() const final { return true; }
private:
CSSUrlValue(std::string value) : m_value(std::move(value)) {}
std::string m_value;
};
template<>
struct is<CSSUrlValue> {
static bool check(const CSSValue& value) { return value.isUrlValue(); }
};
class CSSColorValue final : public CSSValue {
public:
static RefPtr<CSSColorValue> create(uint32_t value);
static RefPtr<CSSColorValue> create(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
uint32_t value() const { return m_value; }
bool isColorValue() const final { return true; }
private:
CSSColorValue(uint32_t value) : m_value(value) {}
uint32_t m_value;
};
template<>
struct is<CSSColorValue> {
static bool check(const CSSValue& value) { return value.isColorValue(); }
};
class CSSPairValue final : public CSSValue {
public:
static RefPtr<CSSPairValue> create(RefPtr<CSSValue> first, RefPtr<CSSValue> second);
const RefPtr<CSSValue>& first() const { return m_first; }
const RefPtr<CSSValue>& second() const { return m_second; }
bool isPairValue() const final { return true; }
private:
CSSPairValue(RefPtr<CSSValue> first, RefPtr<CSSValue> second)
: m_first(first), m_second(second)
{}
RefPtr<CSSValue> m_first;
RefPtr<CSSValue> m_second;
};
template<>
struct is<CSSPairValue> {
static bool check(const CSSValue& value) { return value.isPairValue(); }
};
class CSSListValue : public CSSValue {
public:
static RefPtr<CSSListValue> create(CSSValueList values);
size_t length() const { return m_values.size(); }
const RefPtr<CSSValue>& front() const { return m_values.front(); }
const RefPtr<CSSValue>& back() const { return m_values.back(); }
const RefPtr<CSSValue>& at(size_t index) const { return m_values.at(index); }
const CSSValueList& values() const { return m_values; }
bool isListValue() const final { return true; }
protected:
CSSListValue(CSSValueList values) : m_values(std::move(values)) {}
CSSValueList m_values;
};
enum class CSSPropertyID {
Unknown = 0,
Clip_Path,
Clip_Rule,
Color,
Display,
Fill,
Fill_Opacity,
Fill_Rule,
Font_Family,
Font_Size,
Font_Style,
Font_Variant,
Font_Weight,
Letter_Spacing,
Marker_End,
Marker_Mid,
Marker_Start,
Mask,
Opacity,
Overflow,
Solid_Color,
Solid_Opacity,
Stop_Color,
Stop_Opacity,
Stroke,
Stroke_Dasharray,
Stroke_Dashoffset,
Stroke_Linecap,
Stroke_Linejoin,
Stroke_Miterlimit,
Stroke_Opacity,
Stroke_Width,
Text_Anchor,
Text_Decoration,
Visibility,
Word_Spacing
};
CSSPropertyID csspropertyid(const std::string_view& name);
class CSSProperty {
public:
CSSProperty(CSSPropertyID id, bool important, RefPtr<CSSValue> value)
: m_id(id), m_important(important), m_value(value)
{}
CSSPropertyID id() const { return m_id; }
bool important() const { return m_important; }
const RefPtr<CSSValue>& value() const { return m_value; }
private:
CSSPropertyID m_id;
bool m_important;
RefPtr<CSSValue> m_value;
};
using CSSPropertyList = std::vector<CSSProperty>;
using CSSPropertyMap = std::map<CSSPropertyID, CSSProperty>;
struct CSSSimpleSelector;
using CSSSelector = std::vector<CSSSimpleSelector>;
using CSSSelectorList = std::vector<CSSSelector>;
struct CSSSimpleSelector {
struct Attribute {
enum class Type {
None,
Equals,
Contains,
Includes,
StartsWith,
EndsWith,
DashEquals
};
Type type{Type::None};
PropertyID id{PropertyID::Unknown};
std::string value;
};
struct Pseudo {
enum class Type {
Unknown,
Empty,
Root,
Is,
Not,
FirstChild,
LastChild,
OnlyChild,
FirstOfType,
LastOfType,
OnlyOfType,
NthChild,
NthLastChild,
NthOfType,
NthLastOfType
};
using MatchPattern = std::pair<int16_t, int16_t>;
bool matchnth(size_t count) const;
Type type{Type::Unknown};
MatchPattern pattern;
CSSSelectorList selectors;
};
enum class Combinator {
None,
Descendant,
Child,
DirectAdjacent,
InDirectAdjacent
};
Combinator combinator{Combinator::Descendant};
ElementID id{ElementID::Star};
std::vector<Attribute> attributes;
std::vector<Pseudo> pseudos;
};
class CSSRule {
public:
static std::unique_ptr<CSSRule> create(CSSSelectorList selectors, CSSPropertyList properties);
const CSSSelectorList& selectors() const { return m_selectors; }
const CSSPropertyList& properties() const { return m_properties; }
private:
CSSRule(CSSSelectorList selectors, CSSPropertyList properties)
: m_selectors(std::move(selectors)), m_properties(std::move(properties))
{}
CSSSelectorList m_selectors;
CSSPropertyList m_properties;
};
using CSSRuleList = std::vector<std::unique_ptr<CSSRule>>;
class CSSRuleData {
public:
CSSRuleData(const CSSSelector& selector, const CSSPropertyList& properties, uint32_t specificity, uint32_t position)
: m_selector(selector), m_properties(properties), m_specificity(specificity), m_position(position)
{}
const CSSSelector& selector() const { return m_selector; }
const CSSPropertyList& 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:
static bool matchSimpleSelector(const CSSSimpleSelector& selector, const Element* element);
static bool matchAttributeSelector(const CSSSimpleSelector::Attribute& attribute, const Element* element);
static bool matchPseudoClassSelector(const CSSSimpleSelector::Pseudo& pseudo, const Element* element);
private:
const CSSSelector& m_selector;
const CSSPropertyList& m_properties;
uint32_t m_specificity;
uint32_t m_position;
};
inline bool operator<(const CSSRuleData& a, const CSSRuleData& b) { return std::tie(a.specificity(), a.position()) < std::tie(b.specificity(), b.position()); }
inline bool operator>(const CSSRuleData& a, const CSSRuleData& b) { return std::tie(a.specificity(), a.position()) > std::tie(b.specificity(), b.position()); }
using CSSRuleSet = std::multiset<CSSRuleData>;
class ComputedStyle;
class CSSStyleSheet {
public:
CSSStyleSheet() = default;
void parse(const std::string_view& content);
bool empty() const { return m_ruleList.empty(); }
RefPtr<ComputedStyle> styleForElement(const Element* element, const RefPtr<ComputedStyle>& parentStyle) const;
private:
CSSRuleList m_ruleList;
CSSRuleSet m_ruleSet;
};
} // namespace lunasvg
#endif // CSSSTYLESHEET_H

View file

@ -3,7 +3,7 @@
namespace lunasvg { namespace lunasvg {
DefsElement::DefsElement() DefsElement::DefsElement()
: GraphicsElement(ElementId::Defs) : GraphicsElement(ElementID::Defs)
{ {
} }

View file

@ -2,9 +2,146 @@
#include "parser.h" #include "parser.h"
#include "svgelement.h" #include "svgelement.h"
#include <algorithm>
namespace lunasvg { namespace lunasvg {
void PropertyList::set(PropertyId id, const std::string& value, int specificity) ElementID elementid(const std::string_view& name)
{
static const struct {
std::string_view name;
ElementID value;
} table[] = {
{"a", ElementID::A},
{"circle", ElementID::Circle},
{"clipPath", ElementID::ClipPath},
{"defs", ElementID::Defs},
{"ellipse", ElementID::Ellipse},
{"g", ElementID::G},
{"image", ElementID::Image},
{"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},
{"solidColor", ElementID::SolidColor},
{"stop", ElementID::Stop},
{"style", ElementID::Style},
{"svg", ElementID::Svg},
{"switch", ElementID::Switch},
{"symbol", ElementID::Symbol},
{"text", ElementID::Text},
{"textPath", ElementID::TextPath},
{"tref", ElementID::Tref},
{"tspan", ElementID::Tspan},
{"use", ElementID::Use}
};
auto it = std::lower_bound(table, std::end(table), name, [](auto& item, auto& name) { return item.name < name; });
if(it != std::end(table) && it->name == name)
return it->value;
return ElementID::Unknown;
}
PropertyID propertyid(const std::string_view& name)
{
static const struct {
std::string_view name;
PropertyID value;
} table[] = {
{"class", PropertyID::Class},
{"clip-path", PropertyID::Clip_Path},
{"clip-rule", PropertyID::Clip_Rule},
{"clipPathUnits", PropertyID::ClipPathUnits},
{"color", PropertyID::Color},
{"cx", PropertyID::Cx},
{"cy", PropertyID::Cy},
{"d", PropertyID::D},
{"dx", PropertyID::Dx},
{"dy", PropertyID::Dy},
{"display", PropertyID::Display},
{"fill", PropertyID::Fill},
{"fill-opacity", PropertyID::Fill_Opacity},
{"fill-rule", PropertyID::Fill_Rule},
{"font-family", PropertyID::Font_Family},
{"font-size", PropertyID::Font_Size},
{"font-style", PropertyID::Font_Style},
{"font-variant", PropertyID::Font_Variant},
{"font-weight", PropertyID::Font_Weight},
{"fx", PropertyID::Fx},
{"fy", PropertyID::Fy},
{"gradientTransform", PropertyID::GradientTransform},
{"gradientUnits", PropertyID::GradientUnits},
{"height", PropertyID::Height},
{"href", PropertyID::Href},
{"id", PropertyID::Id},
{"letter-spacing", PropertyID::Letter_Spacing},
{"marker-end", PropertyID::Marker_End},
{"marker-mid", PropertyID::Marker_Mid},
{"marker-start", PropertyID::Marker_Start},
{"markerHeight", PropertyID::MarkerHeight},
{"markerUnits", PropertyID::MarkerUnits},
{"markerWidth", PropertyID::MarkerWidth},
{"mask", PropertyID::Mask},
{"maskContentUnits", PropertyID::MaskContentUnits},
{"maskUnits", PropertyID::MaskUnits},
{"offset", PropertyID::Offset},
{"opacity", PropertyID::Opacity},
{"orient", PropertyID::Orient},
{"overflow", PropertyID::Overflow},
{"patternContentUnits", PropertyID::PatternContentUnits},
{"patternTransform", PropertyID::PatternTransform},
{"patternUnits", PropertyID::PatternUnits},
{"points", PropertyID::Points},
{"preserveAspectRatio", PropertyID::PreserveAspectRatio},
{"r", PropertyID::R},
{"refX", PropertyID::RefX},
{"refY", PropertyID::RefY},
{"rotate", PropertyID::Rotate},
{"rx", PropertyID::Rx},
{"ry", PropertyID::Ry},
{"solid-color", PropertyID::Solid_Color},
{"solid-opacity", PropertyID::Solid_Opacity},
{"spreadMethod", PropertyID::SpreadMethod},
{"startOffset", PropertyID::StartOffset},
{"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},
{"style", PropertyID::Style},
{"text-anchor", PropertyID::Text_Anchor},
{"text-decoration", PropertyID::Text_Decoration},
{"transform", PropertyID::Transform},
{"viewBox", PropertyID::ViewBox},
{"visibility", PropertyID::Visibility},
{"width", PropertyID::Width},
{"word-spacing", PropertyID::Word_Spacing},
{"x", PropertyID::X},
{"x1", PropertyID::X1},
{"x2", PropertyID::X2},
{"y", PropertyID::Y},
{"y1", PropertyID::Y1},
{"y2", PropertyID::Y2}
};
auto it = std::lower_bound(table, std::end(table), name, [](auto& item, auto& name) { return item.name < name; });
if(it != std::end(table) && it->name == name)
return it->value;
return PropertyID::Unknown;
}
void PropertyList::set(PropertyID id, const std::string& value, int specificity)
{ {
auto property = get(id); auto property = get(id);
if(property == nullptr) if(property == nullptr)
@ -21,7 +158,7 @@ void PropertyList::set(PropertyId id, const std::string& value, int specificity)
property->value = value; property->value = value;
} }
Property* PropertyList::get(PropertyId id) const Property* PropertyList::get(PropertyID id) const
{ {
auto data = m_properties.data(); auto data = m_properties.data();
auto end = data + m_properties.size(); auto end = data + m_properties.size();
@ -59,19 +196,19 @@ std::unique_ptr<Node> TextNode::clone() const
return std::move(node); return std::move(node);
} }
Element::Element(ElementId id) Element::Element(ElementID id)
: id(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); properties.set(id, value, specificity);
} }
static const std::string EmptyString; 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); auto property = properties.get(id);
if(property == nullptr) if(property == nullptr)
@ -82,7 +219,7 @@ const std::string& Element::get(PropertyId id) const
static const std::string InheritString{"inherit"}; 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; auto element = this;
do { do {
@ -95,12 +232,12 @@ const std::string& Element::find(PropertyId id) const
return EmptyString; return EmptyString;
} }
bool Element::has(PropertyId id) const bool Element::has(PropertyID id) const
{ {
return properties.get(id); return properties.get(id);
} }
Element* Element::previousSibling() const Element* Element::previousElement() const
{ {
if(parent == nullptr) if(parent == nullptr)
return nullptr; return nullptr;
@ -122,7 +259,7 @@ Element* Element::previousSibling() const
return nullptr; return nullptr;
} }
Element* Element::nextSibling() const Element* Element::nextElement() const
{ {
if(parent == nullptr) if(parent == nullptr)
return nullptr; return nullptr;
@ -162,15 +299,15 @@ Rect Element::currentViewport() const
if(parent == nullptr) if(parent == nullptr)
{ {
auto element = static_cast<const SVGElement*>(this); auto element = static_cast<const SVGElement*>(this);
if(element->has(PropertyId::ViewBox)) if(element->has(PropertyID::ViewBox))
return element->viewBox(); return element->viewBox();
return Rect{0, 0, 300, 150}; return Rect{0, 0, 300, 150};
} }
if(parent->id == ElementId::Svg) if(parent->id == ElementID::Svg)
{ {
auto element = static_cast<SVGElement*>(parent); auto element = static_cast<SVGElement*>(parent);
if(element->has(PropertyId::ViewBox)) if(element->has(PropertyID::ViewBox))
return element->viewBox(); return element->viewBox();
LengthContext lengthContext(element); LengthContext lengthContext(element);

View file

@ -8,15 +8,16 @@
namespace lunasvg { namespace lunasvg {
enum class ElementId enum class ElementID {
{
Unknown = 0, Unknown = 0,
Star, Star,
A,
Circle, Circle,
ClipPath, ClipPath,
Defs, Defs,
Ellipse, Ellipse,
G, G,
Image,
Line, Line,
LinearGradient, LinearGradient,
Marker, Marker,
@ -31,12 +32,16 @@ enum class ElementId
Stop, Stop,
Style, Style,
Svg, Svg,
Switch,
Symbol, Symbol,
Text,
TextPath,
Tref,
Tspan,
Use Use
}; };
enum class PropertyId enum class PropertyID {
{
Unknown = 0, Unknown = 0,
Class, Class,
Clip_Path, Clip_Path,
@ -46,10 +51,17 @@ enum class PropertyId
Cx, Cx,
Cy, Cy,
D, D,
Dx,
Dy,
Display, Display,
Fill, Fill,
Fill_Opacity, Fill_Opacity,
Fill_Rule, Fill_Rule,
Font_Family,
Font_Size,
Font_Style,
Font_Variant,
Font_Weight,
Fx, Fx,
Fy, Fy,
GradientTransform, GradientTransform,
@ -57,6 +69,7 @@ enum class PropertyId
Height, Height,
Href, Href,
Id, Id,
Letter_Spacing,
Marker_End, Marker_End,
Marker_Mid, Marker_Mid,
Marker_Start, Marker_Start,
@ -78,11 +91,13 @@ enum class PropertyId
R, R,
RefX, RefX,
RefY, RefY,
Rotate,
Rx, Rx,
Ry, Ry,
Solid_Color, Solid_Color,
Solid_Opacity, Solid_Opacity,
SpreadMethod, SpreadMethod,
StartOffset,
Stop_Color, Stop_Color,
Stop_Opacity, Stop_Opacity,
Stroke, Stroke,
@ -94,10 +109,13 @@ enum class PropertyId
Stroke_Opacity, Stroke_Opacity,
Stroke_Width, Stroke_Width,
Style, Style,
Text_Anchor,
Text_Decoration,
Transform, Transform,
ViewBox, ViewBox,
Visibility, Visibility,
Width, Width,
Word_Spacing,
X, X,
X1, X1,
X2, X2,
@ -106,9 +124,12 @@ enum class PropertyId
Y2 Y2
}; };
ElementID elementid(const std::string_view& name);
PropertyID propertyid(const std::string_view& name);
struct Property struct Property
{ {
PropertyId id; PropertyID id;
std::string value; std::string value;
int specificity; int specificity;
}; };
@ -118,10 +139,11 @@ class PropertyList
public: public:
PropertyList() = default; PropertyList() = default;
void set(PropertyId id, const std::string& value, int specificity); void set(PropertyID id, const std::string& value, int specificity);
Property* get(PropertyId id) const; Property* get(PropertyID id) const;
void add(const Property& property); void add(const Property& property);
void add(const PropertyList& properties); void add(const PropertyList& properties);
void clear() { m_properties.clear(); }
private: private:
std::vector<Property> m_properties; std::vector<Property> m_properties;
@ -159,20 +181,22 @@ public:
std::string text; std::string text;
}; };
using Attribute = std::pair<PropertyID, std::string>;
using AttributeList = std::vector<Attribute>;
using NodeList = std::list<std::unique_ptr<Node>>; using NodeList = std::list<std::unique_ptr<Node>>;
class Element : public Node class Element : public Node
{ {
public: public:
Element(ElementId id); Element(ElementID id);
void set(PropertyId id, const std::string& value, int specificity); void set(PropertyID id, const std::string& value, int specificity);
const std::string& get(PropertyId id) const; const std::string& get(PropertyID id) const;
const std::string& find(PropertyId id) const; const std::string& find(PropertyID id) const;
bool has(PropertyId id) const; bool has(PropertyID id) const;
Element* previousSibling() const; Element* previousElement() const;
Element* nextSibling() const; Element* nextElement() const;
Node* addChild(std::unique_ptr<Node> child); Node* addChild(std::unique_ptr<Node> child);
void layoutChildren(LayoutContext* context, LayoutContainer* current) const; void layoutChildren(LayoutContext* context, LayoutContainer* current) const;
Rect currentViewport() const; Rect currentViewport() const;
@ -208,7 +232,7 @@ public:
} }
public: public:
ElementId id; ElementID id;
NodeList children; NodeList children;
PropertyList properties; PropertyList properties;
}; };

View file

@ -4,7 +4,7 @@
namespace lunasvg { namespace lunasvg {
GElement::GElement() GElement::GElement()
: GraphicsElement(ElementId::G) : GraphicsElement(ElementID::G)
{ {
} }

View file

@ -6,7 +6,7 @@
namespace lunasvg { namespace lunasvg {
GeometryElement::GeometryElement(ElementId id) GeometryElement::GeometryElement(ElementID id)
: GraphicsElement(id) : GraphicsElement(id)
{ {
} }
@ -35,13 +35,13 @@ void GeometryElement::layout(LayoutContext* context, LayoutContainer* current) c
} }
PathElement::PathElement() PathElement::PathElement()
: GeometryElement(ElementId::Path) : GeometryElement(ElementID::Path)
{ {
} }
Path PathElement::d() const Path PathElement::d() const
{ {
auto& value = get(PropertyId::D); auto& value = get(PropertyID::D);
return Parser::parsePath(value); return Parser::parsePath(value);
} }
@ -55,19 +55,19 @@ std::unique_ptr<Node> PathElement::clone() const
return cloneElement<PathElement>(); return cloneElement<PathElement>();
} }
PolyElement::PolyElement(ElementId id) PolyElement::PolyElement(ElementID id)
: GeometryElement(id) : GeometryElement(id)
{ {
} }
PointList PolyElement::points() const PointList PolyElement::points() const
{ {
auto& value = get(PropertyId::Points); auto& value = get(PropertyID::Points);
return Parser::parsePointList(value); return Parser::parsePointList(value);
} }
PolygonElement::PolygonElement() PolygonElement::PolygonElement()
: PolyElement(ElementId::Polygon) : PolyElement(ElementID::Polygon)
{ {
} }
@ -92,7 +92,7 @@ std::unique_ptr<Node> PolygonElement::clone() const
} }
PolylineElement::PolylineElement() PolylineElement::PolylineElement()
: PolyElement(ElementId::Polyline) : PolyElement(ElementID::Polyline)
{ {
} }
@ -116,25 +116,25 @@ std::unique_ptr<Node> PolylineElement::clone() const
} }
CircleElement::CircleElement() CircleElement::CircleElement()
: GeometryElement(ElementId::Circle) : GeometryElement(ElementID::Circle)
{ {
} }
Length CircleElement::cx() const Length CircleElement::cx() const
{ {
auto& value = get(PropertyId::Cx); auto& value = get(PropertyID::Cx);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length CircleElement::cy() const Length CircleElement::cy() const
{ {
auto& value = get(PropertyId::Cy); auto& value = get(PropertyID::Cy);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length CircleElement::r() const Length CircleElement::r() const
{ {
auto& value = get(PropertyId::R); auto& value = get(PropertyID::R);
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
} }
@ -160,31 +160,31 @@ std::unique_ptr<Node> CircleElement::clone() const
} }
EllipseElement::EllipseElement() EllipseElement::EllipseElement()
: GeometryElement(ElementId::Ellipse) : GeometryElement(ElementID::Ellipse)
{ {
} }
Length EllipseElement::cx() const Length EllipseElement::cx() const
{ {
auto& value = get(PropertyId::Cx); auto& value = get(PropertyID::Cx);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length EllipseElement::cy() const Length EllipseElement::cy() const
{ {
auto& value = get(PropertyId::Cy); auto& value = get(PropertyID::Cy);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length EllipseElement::rx() const Length EllipseElement::rx() const
{ {
auto& value = get(PropertyId::Rx); auto& value = get(PropertyID::Rx);
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
} }
Length EllipseElement::ry() const Length EllipseElement::ry() const
{ {
auto& value = get(PropertyId::Ry); auto& value = get(PropertyID::Ry);
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
} }
@ -212,31 +212,31 @@ std::unique_ptr<Node> EllipseElement::clone() const
} }
LineElement::LineElement() LineElement::LineElement()
: GeometryElement(ElementId::Line) : GeometryElement(ElementID::Line)
{ {
} }
Length LineElement::x1() const Length LineElement::x1() const
{ {
auto& value = get(PropertyId::X1); auto& value = get(PropertyID::X1);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length LineElement::y1() const Length LineElement::y1() const
{ {
auto& value = get(PropertyId::Y1); auto& value = get(PropertyID::Y1);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length LineElement::x2() const Length LineElement::x2() const
{ {
auto& value = get(PropertyId::X2); auto& value = get(PropertyID::X2);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length LineElement::y2() const Length LineElement::y2() const
{ {
auto& value = get(PropertyId::Y2); auto& value = get(PropertyID::Y2);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
@ -260,43 +260,43 @@ std::unique_ptr<Node> LineElement::clone() const
} }
RectElement::RectElement() RectElement::RectElement()
: GeometryElement(ElementId::Rect) : GeometryElement(ElementID::Rect)
{ {
} }
Length RectElement::x() const Length RectElement::x() const
{ {
auto& value = get(PropertyId::X); auto& value = get(PropertyID::X);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length RectElement::y() const Length RectElement::y() const
{ {
auto& value = get(PropertyId::Y); auto& value = get(PropertyID::Y);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length RectElement::rx() const Length RectElement::rx() const
{ {
auto& value = get(PropertyId::Rx); auto& value = get(PropertyID::Rx);
return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown); return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown);
} }
Length RectElement::ry() const Length RectElement::ry() const
{ {
auto& value = get(PropertyId::Ry); auto& value = get(PropertyID::Ry);
return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown); return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown);
} }
Length RectElement::width() const Length RectElement::width() const
{ {
auto& value = get(PropertyId::Width); auto& value = get(PropertyID::Width);
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
} }
Length RectElement::height() const Length RectElement::height() const
{ {
auto& value = get(PropertyId::Height); auto& value = get(PropertyID::Height);
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
} }

View file

@ -10,7 +10,7 @@ class LayoutShape;
class GeometryElement : public GraphicsElement class GeometryElement : public GraphicsElement
{ {
public: public:
GeometryElement(ElementId id); GeometryElement(ElementID id);
bool isGeometry() const { return true; } bool isGeometry() const { return true; }
virtual void layout(LayoutContext* context, LayoutContainer* current) const; virtual void layout(LayoutContext* context, LayoutContainer* current) const;
@ -31,7 +31,7 @@ public:
class PolyElement : public GeometryElement class PolyElement : public GeometryElement
{ {
public: public:
PolyElement(ElementId id); PolyElement(ElementID id);
PointList points() const; PointList points() const;
}; };

View file

@ -3,14 +3,14 @@
namespace lunasvg { namespace lunasvg {
GraphicsElement::GraphicsElement(ElementId id) GraphicsElement::GraphicsElement(ElementID id)
: StyledElement(id) : StyledElement(id)
{ {
} }
Transform GraphicsElement::transform() const Transform GraphicsElement::transform() const
{ {
auto& value = get(PropertyId::Transform); auto& value = get(PropertyID::Transform);
return Parser::parseTransform(value); return Parser::parseTransform(value);
} }

View file

@ -8,7 +8,7 @@ namespace lunasvg {
class GraphicsElement : public StyledElement class GraphicsElement : public StyledElement
{ {
public: public:
GraphicsElement(ElementId id); GraphicsElement(ElementID id);
Transform transform() const; Transform transform() const;
}; };

View file

@ -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); state.canvas->blend(canvas.get(), BlendMode::Src_Over, m_mode == RenderMode::Display ? info.opacity : 1.0);
} }
LayoutContext::LayoutContext(const ParseDocument* document, LayoutSymbol* root) LayoutContext::LayoutContext(const TreeBuilder* builder, LayoutSymbol* root)
: m_document(document), m_root(root) : m_builder(builder), m_root(root)
{ {
} }
Element* LayoutContext::getElementById(const std::string& id) const 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 LayoutObject* LayoutContext::getResourcesById(const std::string& id) const
@ -517,7 +517,7 @@ LayoutMask* LayoutContext::getMasker(const std::string& id)
return static_cast<LayoutMask*>(ref); return static_cast<LayoutMask*>(ref);
auto element = getElementById(id); auto element = getElementById(id);
if(element == nullptr || element->id != ElementId::Mask) if(element == nullptr || element->id != ElementID::Mask)
return nullptr; return nullptr;
auto masker = static_cast<MaskElement*>(element)->getMasker(this); auto masker = static_cast<MaskElement*>(element)->getMasker(this);
@ -534,7 +534,7 @@ LayoutClipPath* LayoutContext::getClipper(const std::string& id)
return static_cast<LayoutClipPath*>(ref); return static_cast<LayoutClipPath*>(ref);
auto element = getElementById(id); auto element = getElementById(id);
if(element == nullptr || element->id != ElementId::ClipPath) if(element == nullptr || element->id != ElementID::ClipPath)
return nullptr; return nullptr;
auto clipper = static_cast<ClipPathElement*>(element)->getClipper(this); auto clipper = static_cast<ClipPathElement*>(element)->getClipper(this);
@ -551,7 +551,7 @@ LayoutMarker* LayoutContext::getMarker(const std::string& id)
return static_cast<LayoutMarker*>(ref); return static_cast<LayoutMarker*>(ref);
auto element = getElementById(id); auto element = getElementById(id);
if(element == nullptr || element->id != ElementId::Marker) if(element == nullptr || element->id != ElementID::Marker)
return nullptr; return nullptr;
auto marker = static_cast<MarkerElement*>(element)->getMarker(this); auto marker = static_cast<MarkerElement*>(element)->getMarker(this);

View file

@ -346,14 +346,13 @@ private:
RenderMode m_mode; RenderMode m_mode;
}; };
class ParseDocument; class TreeBuilder;
class StyledElement; class StyledElement;
class GeometryElement; class GeometryElement;
class LayoutContext class LayoutContext {
{
public: public:
LayoutContext(const ParseDocument* document, LayoutSymbol* root); LayoutContext(const TreeBuilder* builder, LayoutSymbol* root);
Element* getElementById(const std::string& id) const; Element* getElementById(const std::string& id) const;
LayoutObject* getResourcesById(const std::string& id) const; LayoutObject* getResourcesById(const std::string& id) const;
@ -373,7 +372,7 @@ public:
bool hasReference(const Element* element) const; bool hasReference(const Element* element) const;
private: private:
const ParseDocument* m_document; const TreeBuilder* m_builder;
LayoutSymbol* m_root; LayoutSymbol* m_root;
std::map<std::string, LayoutObject*> m_resourcesCache; std::map<std::string, LayoutObject*> m_resourcesCache;
std::set<const Element*> m_references; std::set<const Element*> m_references;

View file

@ -141,15 +141,29 @@ void Bitmap::convert(int ri, int gi, int bi, int ai, bool unpremultiply)
Box::Box(double x, double y, double w, double h) Box::Box(double x, double y, double w, double h)
: x(x), y(y), w(w), h(h) : x(x), y(y), w(w), h(h)
{} {
}
Box::Box(const Rect& rect) Box::Box(const Rect& rect)
: x(rect.x), y(rect.y), w(rect.w), h(rect.h) : 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).map(*this);
}
Matrix::Matrix(double a, double b, double c, double d, double e, double f) Matrix::Matrix(double a, double b, double c, double d, double e, double f)
: a(a), b(b), c(c), d(d), e(e), f(f) : a(a), b(b), c(c), d(d), e(e), f(f)
{} {
}
Matrix::Matrix(const Transform& transform) Matrix::Matrix(const Transform& transform)
: a(transform.m00), b(transform.m10), c(transform.m01), d(transform.m11), e(transform.m02), f(transform.m12) : a(transform.m00), b(transform.m10), c(transform.m01), d(transform.m11), e(transform.m02), f(transform.m12)
@ -232,11 +246,6 @@ Matrix Matrix::operator*(const Matrix& matrix) const
return Transform(*this) * Transform(matrix); return Transform(*this) * Transform(matrix);
} }
Box Matrix::map(const Box& box) const
{
return Transform(*this).map(box);
}
Matrix Matrix::rotated(double angle) Matrix Matrix::rotated(double angle)
{ {
return Transform::rotated(angle); return Transform::rotated(angle);
@ -283,11 +292,11 @@ std::unique_ptr<Document> Document::loadFromData(const std::string& string)
std::unique_ptr<Document> Document::loadFromData(const char* data, std::size_t size) std::unique_ptr<Document> Document::loadFromData(const char* data, std::size_t size)
{ {
ParseDocument parser; TreeBuilder builder;
if(!parser.parse(data, size)) if(!builder.parse(data, size))
return nullptr; return nullptr;
auto root = parser.layout(); auto root = builder.build();
if(!root || root->children.empty()) if(!root || root->children.empty())
return nullptr; return nullptr;
@ -301,48 +310,6 @@ std::unique_ptr<Document> Document::loadFromData(const char* data)
return loadFromData(data, std::strlen(data)); return loadFromData(data, std::strlen(data));
} }
Document* Document::rotate(double angle)
{
root->transform.rotate(angle);
return this;
}
Document* Document::rotate(double angle, double cx, double cy)
{
root->transform.rotate(angle, cx, cy);
return this;
}
Document* Document::scale(double sx, double sy)
{
root->transform.scale(sx, sy);
return this;
}
Document* Document::shear(double shx, double shy)
{
root->transform.shear(shx, shy);
return this;
}
Document* Document::translate(double tx, double ty)
{
root->transform.translate(tx, ty);
return this;
}
Document* Document::transform(double a, double b, double c, double d, double e, double f)
{
root->transform.transform(a, b, c, d, e, f);
return this;
}
Document* Document::identity()
{
root->transform.identity();
return this;
}
void Document::setMatrix(const Matrix& matrix) void Document::setMatrix(const Matrix& matrix)
{ {
root->transform = Transform(matrix); root->transform = Transform(matrix);

View file

@ -5,55 +5,55 @@
namespace lunasvg { namespace lunasvg {
MarkerElement::MarkerElement() MarkerElement::MarkerElement()
: StyledElement(ElementId::Marker) : StyledElement(ElementID::Marker)
{ {
} }
Length MarkerElement::refX() const Length MarkerElement::refX() const
{ {
auto& value = get(PropertyId::RefX); auto& value = get(PropertyID::RefX);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length MarkerElement::refY() const Length MarkerElement::refY() const
{ {
auto& value = get(PropertyId::RefY); auto& value = get(PropertyID::RefY);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length MarkerElement::markerWidth() const Length MarkerElement::markerWidth() const
{ {
auto& value = get(PropertyId::MarkerWidth); auto& value = get(PropertyID::MarkerWidth);
return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent);
} }
Length MarkerElement::markerHeight() const Length MarkerElement::markerHeight() const
{ {
auto& value = get(PropertyId::MarkerHeight); auto& value = get(PropertyID::MarkerHeight);
return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent);
} }
Angle MarkerElement::orient() const Angle MarkerElement::orient() const
{ {
auto& value = get(PropertyId::Orient); auto& value = get(PropertyID::Orient);
return Parser::parseAngle(value); return Parser::parseAngle(value);
} }
MarkerUnits MarkerElement::markerUnits() const MarkerUnits MarkerElement::markerUnits() const
{ {
auto& value = get(PropertyId::MarkerUnits); auto& value = get(PropertyID::MarkerUnits);
return Parser::parseMarkerUnits(value); return Parser::parseMarkerUnits(value);
} }
Rect MarkerElement::viewBox() const Rect MarkerElement::viewBox() const
{ {
auto& value = get(PropertyId::ViewBox); auto& value = get(PropertyID::ViewBox);
return Parser::parseViewBox(value); return Parser::parseViewBox(value);
} }
PreserveAspectRatio MarkerElement::preserveAspectRatio() const PreserveAspectRatio MarkerElement::preserveAspectRatio() const
{ {
auto& value = get(PropertyId::PreserveAspectRatio); auto& value = get(PropertyID::PreserveAspectRatio);
return Parser::parsePreserveAspectRatio(value); return Parser::parsePreserveAspectRatio(value);
} }

View file

@ -5,43 +5,43 @@
namespace lunasvg { namespace lunasvg {
MaskElement::MaskElement() MaskElement::MaskElement()
: StyledElement(ElementId::Mask) : StyledElement(ElementID::Mask)
{ {
} }
Length MaskElement::x() const Length MaskElement::x() const
{ {
auto& value = get(PropertyId::X); auto& value = get(PropertyID::X);
return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent); return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent);
} }
Length MaskElement::y() const Length MaskElement::y() const
{ {
auto& value = get(PropertyId::Y); auto& value = get(PropertyID::Y);
return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent); return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent);
} }
Length MaskElement::width() const Length MaskElement::width() const
{ {
auto& value = get(PropertyId::Width); auto& value = get(PropertyID::Width);
return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent);
} }
Length MaskElement::height() const Length MaskElement::height() const
{ {
auto& value = get(PropertyId::Height); auto& value = get(PropertyID::Height);
return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent);
} }
Units MaskElement::maskUnits() const Units MaskElement::maskUnits() const
{ {
auto& value = get(PropertyId::MaskUnits); auto& value = get(PropertyID::MaskUnits);
return Parser::parseUnits(value, Units::ObjectBoundingBox); return Parser::parseUnits(value, Units::ObjectBoundingBox);
} }
Units MaskElement::maskContentUnits() const Units MaskElement::maskContentUnits() const
{ {
auto& value = get(PropertyId::MaskContentUnits); auto& value = get(PropertyID::MaskContentUnits);
return Parser::parseUnits(value, Units::UserSpaceOnUse); return Parser::parseUnits(value, Units::UserSpaceOnUse);
} }

View file

@ -7,37 +7,37 @@
namespace lunasvg { namespace lunasvg {
PaintElement::PaintElement(ElementId id) PaintElement::PaintElement(ElementID id)
: StyledElement(id) : StyledElement(id)
{ {
} }
GradientElement::GradientElement(ElementId id) GradientElement::GradientElement(ElementID id)
: PaintElement(id) : PaintElement(id)
{ {
} }
Transform GradientElement::gradientTransform() const Transform GradientElement::gradientTransform() const
{ {
auto& value = get(PropertyId::GradientTransform); auto& value = get(PropertyID::GradientTransform);
return Parser::parseTransform(value); return Parser::parseTransform(value);
} }
SpreadMethod GradientElement::spreadMethod() const SpreadMethod GradientElement::spreadMethod() const
{ {
auto& value = get(PropertyId::SpreadMethod); auto& value = get(PropertyID::SpreadMethod);
return Parser::parseSpreadMethod(value); return Parser::parseSpreadMethod(value);
} }
Units GradientElement::gradientUnits() const Units GradientElement::gradientUnits() const
{ {
auto& value = get(PropertyId::GradientUnits); auto& value = get(PropertyID::GradientUnits);
return Parser::parseUnits(value, Units::ObjectBoundingBox); return Parser::parseUnits(value, Units::ObjectBoundingBox);
} }
std::string GradientElement::href() const std::string GradientElement::href() const
{ {
auto& value = get(PropertyId::Href); auto& value = get(PropertyID::Href);
return Parser::parseHref(value); return Parser::parseHref(value);
} }
@ -50,7 +50,7 @@ GradientStops GradientElement::buildGradientStops() const
if(child->isText()) if(child->isText())
continue; continue;
auto element = static_cast<Element*>(child.get()); auto element = static_cast<Element*>(child.get());
if(element->id != ElementId::Stop) if(element->id != ElementID::Stop)
continue; continue;
auto stop = static_cast<StopElement*>(element); auto stop = static_cast<StopElement*>(element);
auto offset = std::max(prevOffset, stop->offset()); auto offset = std::max(prevOffset, stop->offset());
@ -62,31 +62,31 @@ GradientStops GradientElement::buildGradientStops() const
} }
LinearGradientElement::LinearGradientElement() LinearGradientElement::LinearGradientElement()
: GradientElement(ElementId::LinearGradient) : GradientElement(ElementID::LinearGradient)
{ {
} }
Length LinearGradientElement::x1() const Length LinearGradientElement::x1() const
{ {
auto& value = get(PropertyId::X1); auto& value = get(PropertyID::X1);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length LinearGradientElement::y1() const Length LinearGradientElement::y1() const
{ {
auto& value = get(PropertyId::Y1); auto& value = get(PropertyID::Y1);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length LinearGradientElement::x2() const Length LinearGradientElement::x2() const
{ {
auto& value = get(PropertyId::X2); auto& value = get(PropertyID::X2);
return Parser::parseLength(value, AllowNegativeLengths, Length::HundredPercent); return Parser::parseLength(value, AllowNegativeLengths, Length::HundredPercent);
} }
Length LinearGradientElement::y2() const Length LinearGradientElement::y2() const
{ {
auto& value = get(PropertyId::Y2); auto& value = get(PropertyID::Y2);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
@ -98,30 +98,30 @@ std::unique_ptr<LayoutObject> LinearGradientElement::getPainter(LayoutContext* c
while(true) while(true)
{ {
if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform)) if(!attributes.hasGradientTransform() && current->has(PropertyID::GradientTransform))
attributes.setGradientTransform(current->gradientTransform()); attributes.setGradientTransform(current->gradientTransform());
if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod)) if(!attributes.hasSpreadMethod() && current->has(PropertyID::SpreadMethod))
attributes.setSpreadMethod(current->spreadMethod()); attributes.setSpreadMethod(current->spreadMethod());
if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits)) if(!attributes.hasGradientUnits() && current->has(PropertyID::GradientUnits))
attributes.setGradientUnits(current->gradientUnits()); attributes.setGradientUnits(current->gradientUnits());
if(!attributes.hasGradientStops()) if(!attributes.hasGradientStops())
attributes.setGradientStops(current->buildGradientStops()); attributes.setGradientStops(current->buildGradientStops());
if(current->id == ElementId::LinearGradient) if(current->id == ElementID::LinearGradient)
{ {
auto element = static_cast<const LinearGradientElement*>(current); auto element = static_cast<const LinearGradientElement*>(current);
if(!attributes.hasX1() && element->has(PropertyId::X1)) if(!attributes.hasX1() && element->has(PropertyID::X1))
attributes.setX1(element->x1()); attributes.setX1(element->x1());
if(!attributes.hasY1() && element->has(PropertyId::Y1)) if(!attributes.hasY1() && element->has(PropertyID::Y1))
attributes.setY1(element->y1()); attributes.setY1(element->y1());
if(!attributes.hasX2() && element->has(PropertyId::X2)) if(!attributes.hasX2() && element->has(PropertyID::X2))
attributes.setX2(element->x2()); attributes.setX2(element->x2());
if(!attributes.hasY2() && element->has(PropertyId::Y2)) if(!attributes.hasY2() && element->has(PropertyID::Y2))
attributes.setY2(element->y2()); attributes.setY2(element->y2());
} }
auto ref = context->getElementById(current->href()); 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; break;
processedGradients.insert(current); processedGradients.insert(current);
@ -164,37 +164,37 @@ std::unique_ptr<Node> LinearGradientElement::clone() const
} }
RadialGradientElement::RadialGradientElement() RadialGradientElement::RadialGradientElement()
: GradientElement(ElementId::RadialGradient) : GradientElement(ElementID::RadialGradient)
{ {
} }
Length RadialGradientElement::cx() const Length RadialGradientElement::cx() const
{ {
auto& value = get(PropertyId::Cx); auto& value = get(PropertyID::Cx);
return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent); return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent);
} }
Length RadialGradientElement::cy() const Length RadialGradientElement::cy() const
{ {
auto& value = get(PropertyId::Cy); auto& value = get(PropertyID::Cy);
return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent); return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent);
} }
Length RadialGradientElement::r() const Length RadialGradientElement::r() const
{ {
auto& value = get(PropertyId::R); auto& value = get(PropertyID::R);
return Parser::parseLength(value, ForbidNegativeLengths, Length::FiftyPercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::FiftyPercent);
} }
Length RadialGradientElement::fx() const Length RadialGradientElement::fx() const
{ {
auto& value = get(PropertyId::Fx); auto& value = get(PropertyID::Fx);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length RadialGradientElement::fy() const Length RadialGradientElement::fy() const
{ {
auto& value = get(PropertyId::Fy); auto& value = get(PropertyID::Fy);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
@ -206,32 +206,32 @@ std::unique_ptr<LayoutObject> RadialGradientElement::getPainter(LayoutContext* c
while(true) while(true)
{ {
if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform)) if(!attributes.hasGradientTransform() && current->has(PropertyID::GradientTransform))
attributes.setGradientTransform(current->gradientTransform()); attributes.setGradientTransform(current->gradientTransform());
if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod)) if(!attributes.hasSpreadMethod() && current->has(PropertyID::SpreadMethod))
attributes.setSpreadMethod(current->spreadMethod()); attributes.setSpreadMethod(current->spreadMethod());
if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits)) if(!attributes.hasGradientUnits() && current->has(PropertyID::GradientUnits))
attributes.setGradientUnits(current->gradientUnits()); attributes.setGradientUnits(current->gradientUnits());
if(!attributes.hasGradientStops()) if(!attributes.hasGradientStops())
attributes.setGradientStops(current->buildGradientStops()); attributes.setGradientStops(current->buildGradientStops());
if(current->id == ElementId::RadialGradient) if(current->id == ElementID::RadialGradient)
{ {
auto element = static_cast<const RadialGradientElement*>(current); auto element = static_cast<const RadialGradientElement*>(current);
if(!attributes.hasCx() && element->has(PropertyId::Cx)) if(!attributes.hasCx() && element->has(PropertyID::Cx))
attributes.setCx(element->cx()); attributes.setCx(element->cx());
if(!attributes.hasCy() && element->has(PropertyId::Cy)) if(!attributes.hasCy() && element->has(PropertyID::Cy))
attributes.setCy(element->cy()); attributes.setCy(element->cy());
if(!attributes.hasR() && element->has(PropertyId::R)) if(!attributes.hasR() && element->has(PropertyID::R))
attributes.setR(element->r()); attributes.setR(element->r());
if(!attributes.hasFx() && element->has(PropertyId::Fx)) if(!attributes.hasFx() && element->has(PropertyID::Fx))
attributes.setFx(element->fx()); attributes.setFx(element->fx());
if(!attributes.hasFy() && element->has(PropertyId::Fy)) if(!attributes.hasFy() && element->has(PropertyID::Fy))
attributes.setFy(element->fy()); attributes.setFy(element->fy());
} }
auto ref = context->getElementById(current->href()); 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; break;
processedGradients.insert(current); processedGradients.insert(current);
@ -278,67 +278,67 @@ std::unique_ptr<Node> RadialGradientElement::clone() const
} }
PatternElement::PatternElement() PatternElement::PatternElement()
: PaintElement(ElementId::Pattern) : PaintElement(ElementID::Pattern)
{ {
} }
Length PatternElement::x() const Length PatternElement::x() const
{ {
auto& value = get(PropertyId::X); auto& value = get(PropertyID::X);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length PatternElement::y() const Length PatternElement::y() const
{ {
auto& value = get(PropertyId::Y); auto& value = get(PropertyID::Y);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length PatternElement::width() const Length PatternElement::width() const
{ {
auto& value = get(PropertyId::Width); auto& value = get(PropertyID::Width);
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
} }
Length PatternElement::height() const Length PatternElement::height() const
{ {
auto& value = get(PropertyId::Height); auto& value = get(PropertyID::Height);
return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero);
} }
Transform PatternElement::patternTransform() const Transform PatternElement::patternTransform() const
{ {
auto& value = get(PropertyId::PatternTransform); auto& value = get(PropertyID::PatternTransform);
return Parser::parseTransform(value); return Parser::parseTransform(value);
} }
Units PatternElement::patternUnits() const Units PatternElement::patternUnits() const
{ {
auto& value = get(PropertyId::PatternUnits); auto& value = get(PropertyID::PatternUnits);
return Parser::parseUnits(value, Units::ObjectBoundingBox); return Parser::parseUnits(value, Units::ObjectBoundingBox);
} }
Units PatternElement::patternContentUnits() const Units PatternElement::patternContentUnits() const
{ {
auto& value = get(PropertyId::PatternContentUnits); auto& value = get(PropertyID::PatternContentUnits);
return Parser::parseUnits(value, Units::UserSpaceOnUse); return Parser::parseUnits(value, Units::UserSpaceOnUse);
} }
Rect PatternElement::viewBox() const Rect PatternElement::viewBox() const
{ {
auto& value = get(PropertyId::ViewBox); auto& value = get(PropertyID::ViewBox);
return Parser::parseViewBox(value); return Parser::parseViewBox(value);
} }
PreserveAspectRatio PatternElement::preserveAspectRatio() const PreserveAspectRatio PatternElement::preserveAspectRatio() const
{ {
auto& value = get(PropertyId::PreserveAspectRatio); auto& value = get(PropertyID::PreserveAspectRatio);
return Parser::parsePreserveAspectRatio(value); return Parser::parsePreserveAspectRatio(value);
} }
std::string PatternElement::href() const std::string PatternElement::href() const
{ {
auto& value = get(PropertyId::Href); auto& value = get(PropertyID::Href);
return Parser::parseHref(value); return Parser::parseHref(value);
} }
@ -353,29 +353,29 @@ std::unique_ptr<LayoutObject> PatternElement::getPainter(LayoutContext* context)
while(true) while(true)
{ {
if(!attributes.hasX() && current->has(PropertyId::X)) if(!attributes.hasX() && current->has(PropertyID::X))
attributes.setX(current->x()); attributes.setX(current->x());
if(!attributes.hasY() && current->has(PropertyId::Y)) if(!attributes.hasY() && current->has(PropertyID::Y))
attributes.setY(current->y()); attributes.setY(current->y());
if(!attributes.hasWidth() && current->has(PropertyId::Width)) if(!attributes.hasWidth() && current->has(PropertyID::Width))
attributes.setWidth(current->width()); attributes.setWidth(current->width());
if(!attributes.hasHeight() && current->has(PropertyId::Height)) if(!attributes.hasHeight() && current->has(PropertyID::Height))
attributes.setHeight(current->height()); attributes.setHeight(current->height());
if(!attributes.hasPatternTransform() && current->has(PropertyId::PatternTransform)) if(!attributes.hasPatternTransform() && current->has(PropertyID::PatternTransform))
attributes.setPatternTransform(current->patternTransform()); attributes.setPatternTransform(current->patternTransform());
if(!attributes.hasPatternUnits() && current->has(PropertyId::PatternUnits)) if(!attributes.hasPatternUnits() && current->has(PropertyID::PatternUnits))
attributes.setPatternUnits(current->patternUnits()); attributes.setPatternUnits(current->patternUnits());
if(!attributes.hasPatternContentUnits() && current->has(PropertyId::PatternContentUnits)) if(!attributes.hasPatternContentUnits() && current->has(PropertyID::PatternContentUnits))
attributes.setPatternContentUnits(current->patternContentUnits()); attributes.setPatternContentUnits(current->patternContentUnits());
if(!attributes.hasViewBox() && current->has(PropertyId::ViewBox)) if(!attributes.hasViewBox() && current->has(PropertyID::ViewBox))
attributes.setViewBox(current->viewBox()); attributes.setViewBox(current->viewBox());
if(!attributes.hasPreserveAspectRatio() && current->has(PropertyId::PreserveAspectRatio)) if(!attributes.hasPreserveAspectRatio() && current->has(PropertyID::PreserveAspectRatio))
attributes.setPreserveAspectRatio(current->preserveAspectRatio()); attributes.setPreserveAspectRatio(current->preserveAspectRatio());
if(!attributes.hasPatternContentElement() && current->children.size()) if(!attributes.hasPatternContentElement() && current->children.size())
attributes.setPatternContentElement(current); attributes.setPatternContentElement(current);
auto ref = context->getElementById(current->href()); auto ref = context->getElementById(current->href());
if(!ref || ref->id != ElementId::Pattern) if(!ref || ref->id != ElementID::Pattern)
break; break;
processedPatterns.insert(current); processedPatterns.insert(current);
@ -413,7 +413,7 @@ std::unique_ptr<Node> PatternElement::clone() const
} }
SolidColorElement::SolidColorElement() SolidColorElement::SolidColorElement()
: PaintElement(ElementId::SolidColor) : PaintElement(ElementID::SolidColor)
{ {
} }

View file

@ -11,7 +11,7 @@ class LayoutObject;
class PaintElement : public StyledElement class PaintElement : public StyledElement
{ {
public: public:
PaintElement(ElementId id); PaintElement(ElementID id);
bool isPaint() const { return true; } bool isPaint() const { return true; }
virtual std::unique_ptr<LayoutObject> getPainter(LayoutContext* context) const = 0; virtual std::unique_ptr<LayoutObject> getPainter(LayoutContext* context) const = 0;
@ -20,7 +20,7 @@ public:
class GradientElement : public PaintElement class GradientElement : public PaintElement
{ {
public: public:
GradientElement(ElementId id); GradientElement(ElementID id);
Transform gradientTransform() const; Transform gradientTransform() const;
SpreadMethod spreadMethod() const; SpreadMethod spreadMethod() const;

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
#define PARSER_H #define PARSER_H
#include <map> #include <map>
#include <set>
#include "property.h" #include "property.h"
#include "element.h" #include "element.h"
@ -63,7 +64,10 @@ private:
static bool parseTransform(const char*& ptr, const char* end, TransformType& type, double* values, int& count); static bool parseTransform(const char*& ptr, const char* end, TransformType& type, double* values, int& count);
}; };
struct Selector; struct SimpleSelector;
using Selector = std::vector<SimpleSelector>;
using SelectorList = std::vector<Selector>;
struct AttributeSelector struct AttributeSelector
{ {
@ -78,20 +82,19 @@ struct AttributeSelector
Contains Contains
}; };
PropertyId id{PropertyId::Unknown};
std::string value;
MatchType matchType{MatchType::None}; MatchType matchType{MatchType::None};
PropertyID id{PropertyID::Unknown};
std::string value;
}; };
using SelectorList = std::vector<Selector>; struct PseudoClassSelector
struct PseudoClass
{ {
enum class Type enum class Type
{ {
Unknown, Unknown,
Empty, Empty,
Root, Root,
Is,
Not, Not,
FirstChild, FirstChild,
LastChild, LastChild,
@ -102,7 +105,9 @@ struct PseudoClass
}; };
Type type{Type::Unknown}; Type type{Type::Unknown};
SelectorList notSelectors; int16_t a{0};
int16_t b{0};
SelectorList subSelectors;
}; };
struct SimpleSelector struct SimpleSelector
@ -115,16 +120,10 @@ struct SimpleSelector
InDirectAdjacent InDirectAdjacent
}; };
ElementId id{ElementId::Star};
std::vector<AttributeSelector> attributeSelectors;
std::vector<PseudoClass> pseudoClasses;
Combinator combinator{Combinator::Descendant}; Combinator combinator{Combinator::Descendant};
}; ElementID id{ElementID::Star};
std::vector<AttributeSelector> attributeSelectors;
struct Selector std::vector<PseudoClassSelector> pseudoClassSelectors;
{
std::vector<SimpleSelector> simpleSelectors;
int specificity{0};
}; };
struct Rule struct Rule
@ -133,21 +132,47 @@ struct Rule
PropertyList declarations; PropertyList declarations;
}; };
class RuleMatchContext class RuleData {
{
public: public:
RuleMatchContext(const std::vector<Rule>& 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)
{}
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 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:
Selector m_selector;
PropertyList m_properties;
uint32_t m_specificity;
uint32_t m_position;
};
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:
StyleSheet() = default;
void parse(const std::string& content);
void add(const Rule& rule);
bool empty() const { return m_position == 0; }
std::vector<const PropertyList*> match(const Element* element) const; std::vector<const PropertyList*> match(const Element* element) const;
private: private:
bool selectorMatch(const Selector* selector, const Element* element) const; std::multiset<RuleData> m_rules;
bool simpleSelectorMatch(const SimpleSelector& selector, const Element* element) const; uint32_t m_position{0};
bool attributeSelectorMatch(const AttributeSelector& selector, const Element* element) const;
bool pseudoClassMatch(const PseudoClass& pseudo, const Element* element) const;
private:
std::multimap<int, std::pair<const Selector*, const PropertyList*>, std::less<int>> m_selectors;
}; };
class CSSParser class CSSParser
@ -155,35 +180,29 @@ class CSSParser
public: public:
CSSParser() = default; CSSParser() = default;
bool parseMore(const std::string& value); static bool parseSheet(StyleSheet* sheet, const std::string& value);
const std::vector<Rule>& rules() const { return m_rules; }
private: private:
bool parseAtRule(const char*& ptr, const char* end) const; static bool parseAtRule(const char*& ptr, const char* end);
bool parseRule(const char*& ptr, const char* end, Rule& rule) const; static bool parseRule(const char*& ptr, const char* end, Rule& rule);
bool parseSelectors(const char*& ptr, const char* end, SelectorList& selectors) const; static bool parseSelectors(const char*& ptr, const char* end, SelectorList& selectors);
bool parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations) const; static bool parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations);
bool parseSelector(const char*& ptr, const char* end, Selector& selector) const; static bool parseSelector(const char*& ptr, const char* end, Selector& selector);
bool parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector) const; static bool parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector);
private:
std::vector<Rule> m_rules;
}; };
class LayoutSymbol; class LayoutSymbol;
class ParseDocument class TreeBuilder {
{
public: public:
ParseDocument(); TreeBuilder();
~ParseDocument(); ~TreeBuilder();
bool parse(const char* data, std::size_t size); bool parse(const char* data, std::size_t size);
SVGElement* rootElement() const { return m_rootElement.get(); } SVGElement* rootElement() const { return m_rootElement.get(); }
Element* getElementById(const std::string& id) const; Element* getElementById(const std::string& id) const;
std::unique_ptr<LayoutSymbol> layout() const; std::unique_ptr<LayoutSymbol> build() const;
private: private:
std::unique_ptr<SVGElement> m_rootElement; std::unique_ptr<SVGElement> m_rootElement;

View file

@ -6,6 +6,8 @@
#include <limits> #include <limits>
#include <string> #include <string>
#include <algorithm> #include <algorithm>
#include <string_view>
#include <cassert>
namespace lunasvg { namespace lunasvg {
@ -262,6 +264,241 @@ inline bool parseNumber(const char*& ptr, const char* end, T& number)
} // namespace Utils } // namespace Utils
class ParserString {
public:
explicit ParserString(const std::string_view& value)
: ParserString(value.data(), value.length())
{}
ParserString(const char* begin, size_t length)
: ParserString(begin, begin + length)
{}
ParserString(const char* begin, const char* end)
: ParserString(begin, begin, end)
{}
ParserString(const char* current, const char* begin, const char* end)
: m_current(current), m_begin(begin), m_end(end)
{}
ParserString operator+(size_t count) const {
auto current = m_current + count;
assert(m_end >= current);
return ParserString(current, m_begin, m_end);
}
ParserString operator-(size_t count) const {
auto current = m_current - count;
assert(current >= m_begin);
return ParserString(current, m_begin, m_end);
}
ParserString& operator+=(size_t count) {
*this = *this + count;
return *this;
}
ParserString& operator-=(size_t count) {
*this = *this - count;
return *this;
}
const char& operator*() const {
assert(m_current < m_end);
return *m_current;
}
char peek(size_t count = 0) const {
auto current = m_current + count;
assert(m_end >= current);
if(current == m_end)
return 0;
return *current;
}
char advance(size_t count = 1) {
m_current += count;
assert(m_end >= m_current);
if(m_current == m_end)
return 0;
return *m_current;
}
char get() const {
assert(m_end >= m_current);
if(m_current == m_end)
return 0;
return *m_current;
}
std::string_view string(size_t offset, size_t count) const { return string().substr(offset, count); }
std::string_view substring(size_t offset, size_t count) const { return substring().substr(offset, count); }
std::string_view string() const { return std::string_view(m_begin, length()); }
std::string_view substring() const { return std::string_view(m_current, sublength()); }
size_t offset() const { return m_current - m_begin; }
size_t length() const { return m_end - m_begin; }
size_t sublength() const { return m_end - m_current; }
const char* current() const { return m_current; }
const char* begin() const { return m_begin; }
const char* end() const { return m_end; }
bool empty() const { return m_current == m_end; }
private:
const char* m_current;
const char* m_begin;
const char* m_end;
};
constexpr bool isspace(int cc) { return (cc == ' ' || cc == '\n' || cc == '\t' || cc == '\r' || cc == '\f'); }
constexpr bool isdigit(int cc) { return (cc >= '0' && cc <= '9'); }
constexpr bool isupper(int cc) { return (cc >= 'A' && cc <= 'Z'); }
constexpr bool islower(int cc) { return (cc >= 'a' && cc <= 'z'); }
constexpr bool isalpha(int cc) { return isupper(cc) || islower(cc); }
constexpr bool isxupper(int cc) { return (cc >= 'A' && cc <= 'F'); }
constexpr bool isxlower(int cc) { return (cc >= 'a' && cc <= 'f'); }
constexpr bool isxdigit(int cc) { return isdigit(cc) || isxupper(cc) || isxlower(cc); }
constexpr int xdigit(int cc) {
if(isdigit(cc))
return cc - '0';
if(isxupper(cc))
return 10 + cc - 'A';
if(isxlower(cc))
return 10 + cc - 'a';
return 0;
}
constexpr char tolower(int cc) {
if(isupper(cc))
return cc + 0x20;
return cc;
}
constexpr bool equals(int a, int b, bool caseSensitive) {
if(caseSensitive)
return a == b;
return tolower(a) == tolower(b);
}
constexpr bool equals(const char* aData, size_t aLength, const char* bData, size_t bLength, bool caseSensitive) {
if(aLength != bLength)
return false;
auto aEnd = aData + aLength;
while(aData != aEnd) {
if(!equals(*aData, *bData, caseSensitive))
return false;
++aData;
++bData;
}
return true;
}
constexpr bool equals(const std::string_view& a, const std::string_view& b, bool caseSensitive) {
return equals(a.data(), a.length(), b.data(), b.length(), caseSensitive);
}
constexpr bool contains(const std::string_view& value, const std::string_view& subvalue, bool caseSensitive) {
if(subvalue.empty() || subvalue.length() > value.length())
return false;
auto it = value.data();
auto end = it + value.length();
while(it < end) {
size_t count = 0;
do {
if(!equals(*it, subvalue[count], caseSensitive))
break;
++count;
++it;
} while(it < end && count < subvalue.length());
if(count == subvalue.length())
return true;
++it;
}
return false;
}
constexpr bool includes(const std::string_view& value, const std::string_view& subvalue, bool caseSensitive) {
if(subvalue.empty() || subvalue.length() > value.length())
return false;
auto it = value.data();
auto end = it + value.length();
while(true) {
while(it < end && isspace(*it))
++it;
if(it >= end)
return false;
size_t count = 0;
auto begin = it;
do {
++count;
++it;
} while(it < end && !isspace(*it));
if(equals(begin, count, subvalue.data(), subvalue.length(), caseSensitive))
return true;
++it;
}
return false;
}
constexpr bool startswith(const std::string_view& value, const std::string_view& subvalue, bool caseSensitive) {
if(subvalue.empty() || subvalue.length() > value.length())
return false;
return equals(value.substr(0, subvalue.size()), subvalue, caseSensitive);
}
constexpr bool endswith(const std::string_view& value, const std::string_view& subvalue, bool caseSensitive) {
if(subvalue.empty() || subvalue.length() > value.length())
return false;
return equals(value.substr(value.size() - subvalue.size(), subvalue.size()), subvalue, caseSensitive);
}
constexpr bool dashequals(const std::string_view& value, const std::string_view& subvalue, bool caseSensitive) {
if(!startswith(value, subvalue, caseSensitive))
return false;
return (value.length() == subvalue.length() || value.at(subvalue.length()) == '-');
}
inline void appendCodepoint(std::string& output, uint32_t cp) {
char c[5] = {0, 0, 0, 0, 0};
if(cp < 0x80) {
c[1] = 0;
c[0] = cp;
} else if(cp < 0x800) {
c[2] = 0;
c[1] = (cp & 0x3F) | 0x80;
cp >>= 6;
c[0] = cp | 0xC0;
} else if(cp < 0x10000) {
c[3] = 0;
c[2] = (cp & 0x3F) | 0x80;
cp >>= 6;
c[1] = (cp & 0x3F) | 0x80;
cp >>= 6;
c[0] = cp | 0xE0;
} else if(cp < 0x110000) {
c[4] = 0;
c[3] = (cp & 0x3F) | 0x80;
cp >>= 6;
c[2] = (cp & 0x3F) | 0x80;
cp >>= 6;
c[1] = (cp & 0x3F) | 0x80;
cp >>= 6;
c[0] = cp | 0xF0;
}
output.append(c);
}
} // namespace lunasvg } // namespace lunasvg
#endif // PARSERUTILS_H #endif // PARSERUTILS_H

254
external/lunasvg/source/pointer.h vendored Normal file
View file

@ -0,0 +1,254 @@
#ifndef POINTER_H
#define POINTER_H
#include <algorithm>
#include <cstdint>
namespace lunasvg {
template<typename T>
class RefCounted {
public:
RefCounted() = default;
void ref() { ++m_refCount; }
void deref() {
if(--m_refCount == 0) {
delete static_cast<T*>(this);
}
}
uint32_t refCount() const { return m_refCount; }
bool hasOneRefCount() const { return m_refCount == 1; }
private:
uint32_t m_refCount{1};
};
template<typename T>
inline void refIfNotNull(T* ptr)
{
if(ptr)
ptr->ref();
}
template<typename T>
inline void derefIfNotNull(T* ptr)
{
if(ptr)
ptr->deref();
}
template<typename T> class RefPtr;
template<typename T> RefPtr<T> adoptPtr(T*);
template<typename T>
class RefPtr {
public:
RefPtr() = default;
RefPtr(std::nullptr_t) : m_ptr(nullptr) {}
RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(m_ptr); }
RefPtr(T& ref) : m_ptr(&ref) { m_ptr->ref(); }
RefPtr(const RefPtr<T>& p) : m_ptr(p.get()) { refIfNotNull(m_ptr); }
RefPtr(RefPtr<T>&& p) : m_ptr(p.release()) {}
template<typename U>
RefPtr(const RefPtr<U>& p) : m_ptr(p.get()) { refIfNotNull(m_ptr); }
template<typename U>
RefPtr(RefPtr<U>&& p) : m_ptr(p.release()) {}
~RefPtr() { derefIfNotNull(m_ptr); }
T* get() const { return m_ptr; }
T& operator*() const { return *m_ptr; }
T* operator->() const { return m_ptr; }
bool empty() const { return !m_ptr; }
bool operator!() const { return !m_ptr; }
operator bool() const { return !!m_ptr; }
RefPtr<T>& operator=(std::nullptr_t)
{
clear();
return *this;
}
RefPtr<T>& operator=(T* o)
{
RefPtr<T> p = o;
swap(p);
return *this;
}
RefPtr<T>& operator=(T& o)
{
RefPtr<T> p = o;
swap(p);
return *this;
}
RefPtr<T>& operator=(const RefPtr<T>& o)
{
RefPtr<T> p = o;
swap(p);
return *this;
}
RefPtr<T>& operator=(RefPtr<T>&& o)
{
RefPtr<T> p = std::move(o);
swap(p);
return *this;
}
template<typename U>
RefPtr<T>& operator=(const RefPtr<U>& o)
{
RefPtr<T> p = o;
swap(p);
return *this;
}
template<typename U>
RefPtr<T>& operator=(RefPtr<U>&& o)
{
RefPtr<T> p = std::move(o);
swap(p);
return *this;
}
void swap(RefPtr<T>& o)
{
std::swap(m_ptr, o.m_ptr);
}
T* release()
{
T* ptr = m_ptr;
m_ptr = nullptr;
return ptr;
}
void clear()
{
derefIfNotNull(m_ptr);
m_ptr = nullptr;
}
friend RefPtr<T> adoptPtr<T>(T*);
private:
RefPtr(T* ptr, std::nullptr_t) : m_ptr(ptr) {}
T* m_ptr{nullptr};
};
template<typename T>
inline RefPtr<T> adoptPtr(T* ptr)
{
return RefPtr<T>(ptr, nullptr);
}
template<class T>
inline void swap(RefPtr<T>& a, RefPtr<T>& b)
{
a.swap(b);
}
template<typename T, typename U>
inline bool operator==(const RefPtr<T>& a, const RefPtr<U>& b)
{
return a.get() == b.get();
}
template<typename T, typename U>
inline bool operator==(const RefPtr<T>& a, const U* b)
{
return a.get() == b;
}
template<typename T, typename U>
inline bool operator==(const T* a, const RefPtr<U>& b)
{
return a == b.get();
}
template<typename T>
inline bool operator==(const RefPtr<T>& a, std::nullptr_t)
{
return a.get() == nullptr;
}
template<typename T, typename U>
inline bool operator!=(const RefPtr<T>& a, const RefPtr<U>& b)
{
return a.get() != b.get();
}
template<typename T, typename U>
inline bool operator!=(const RefPtr<T>& a, const U* b)
{
return a.get() != b;
}
template<typename T, typename U>
inline bool operator!=(const T* a, const RefPtr<U>& b)
{
return a != b.get();
}
template<typename T>
inline bool operator!=(const RefPtr<T>& a, std::nullptr_t)
{
return a.get() != nullptr;
}
template<typename T>
struct is {
template<typename U>
static bool check(const U& value);
};
template<typename T, typename U>
constexpr bool is_a(U& value) {
return is<T>::check(value);
}
template<typename T, typename U>
constexpr bool is_a(const U& value) {
return is<T>::check(value);
}
template<typename T, typename U>
constexpr bool is_a(U* value) {
return value && is<T>::check(*value);
}
template<typename T, typename U>
constexpr bool is_a(const U* value) {
return value && is<T>::check(*value);
}
template<typename T, typename U>
constexpr T* to(U& value) {
return is_a<T>(value) ? static_cast<T*>(&value) : nullptr;
}
template<typename T, typename U>
constexpr const T* to(const U& value) {
return is_a<T>(value) ? static_cast<const T*>(&value) : nullptr;
}
template<typename T, typename U>
constexpr T* to(U* value) {
return is_a<T>(value) ? static_cast<T*>(value) : nullptr;
}
template<typename T, typename U>
constexpr const T* to(const U* value) {
return is_a<T>(value) ? static_cast<const T*>(value) : nullptr;
}
} // namespace lunasvg
#endif // POINTER_H

View file

@ -4,13 +4,13 @@
namespace lunasvg { namespace lunasvg {
StopElement::StopElement() StopElement::StopElement()
: StyledElement(ElementId::Stop) : StyledElement(ElementID::Stop)
{ {
} }
double StopElement::offset() const double StopElement::offset() const
{ {
auto& value = get(PropertyId::Offset); auto& value = get(PropertyID::Offset);
return Parser::parseNumberPercentage(value, 0.0); return Parser::parseNumberPercentage(value, 0.0);
} }

View file

@ -3,164 +3,164 @@
namespace lunasvg { namespace lunasvg {
StyledElement::StyledElement(ElementId id) StyledElement::StyledElement(ElementID id)
: Element(id) : Element(id)
{ {
} }
Paint StyledElement::fill() const Paint StyledElement::fill() const
{ {
auto& value = find(PropertyId::Fill); auto& value = find(PropertyID::Fill);
return Parser::parsePaint(value, this, Color::Black); return Parser::parsePaint(value, this, Color::Black);
} }
Paint StyledElement::stroke() const Paint StyledElement::stroke() const
{ {
auto& value = find(PropertyId::Stroke); auto& value = find(PropertyID::Stroke);
return Parser::parsePaint(value, this, Color::Transparent); return Parser::parsePaint(value, this, Color::Transparent);
} }
Color StyledElement::color() const Color StyledElement::color() const
{ {
auto& value = find(PropertyId::Color); auto& value = find(PropertyID::Color);
return Parser::parseColor(value, this, Color::Black); return Parser::parseColor(value, this, Color::Black);
} }
Color StyledElement::stop_color() const Color StyledElement::stop_color() const
{ {
auto& value = find(PropertyId::Stop_Color); auto& value = find(PropertyID::Stop_Color);
return Parser::parseColor(value, this, Color::Black); return Parser::parseColor(value, this, Color::Black);
} }
Color StyledElement::solid_color() const Color StyledElement::solid_color() const
{ {
auto& value = find(PropertyId::Solid_Color); auto& value = find(PropertyID::Solid_Color);
return Parser::parseColor(value, this, Color::Black); return Parser::parseColor(value, this, Color::Black);
} }
double StyledElement::opacity() const double StyledElement::opacity() const
{ {
auto& value = get(PropertyId::Opacity); auto& value = get(PropertyID::Opacity);
return Parser::parseNumberPercentage(value, 1.0); return Parser::parseNumberPercentage(value, 1.0);
} }
double StyledElement::fill_opacity() const double StyledElement::fill_opacity() const
{ {
auto& value = find(PropertyId::Fill_Opacity); auto& value = find(PropertyID::Fill_Opacity);
return Parser::parseNumberPercentage(value, 1.0); return Parser::parseNumberPercentage(value, 1.0);
} }
double StyledElement::stroke_opacity() const double StyledElement::stroke_opacity() const
{ {
auto& value = find(PropertyId::Stroke_Opacity); auto& value = find(PropertyID::Stroke_Opacity);
return Parser::parseNumberPercentage(value, 1.0); return Parser::parseNumberPercentage(value, 1.0);
} }
double StyledElement::stop_opacity() const double StyledElement::stop_opacity() const
{ {
auto& value = find(PropertyId::Stop_Opacity); auto& value = find(PropertyID::Stop_Opacity);
return Parser::parseNumberPercentage(value, 1.0); return Parser::parseNumberPercentage(value, 1.0);
} }
double StyledElement::solid_opacity() const double StyledElement::solid_opacity() const
{ {
auto& value = find(PropertyId::Solid_Opacity); auto& value = find(PropertyID::Solid_Opacity);
return Parser::parseNumberPercentage(value, 1.0); return Parser::parseNumberPercentage(value, 1.0);
} }
double StyledElement::stroke_miterlimit() const double StyledElement::stroke_miterlimit() const
{ {
auto& value = find(PropertyId::Stroke_Miterlimit); auto& value = find(PropertyID::Stroke_Miterlimit);
return Parser::parseNumber(value, 4.0); return Parser::parseNumber(value, 4.0);
} }
Length StyledElement::stroke_width() const Length StyledElement::stroke_width() const
{ {
auto& value = find(PropertyId::Stroke_Width); auto& value = find(PropertyID::Stroke_Width);
return Parser::parseLength(value, ForbidNegativeLengths, Length::One); return Parser::parseLength(value, ForbidNegativeLengths, Length::One);
} }
Length StyledElement::stroke_dashoffset() const Length StyledElement::stroke_dashoffset() const
{ {
auto& value = find(PropertyId::Stroke_Dashoffset); auto& value = find(PropertyID::Stroke_Dashoffset);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
LengthList StyledElement::stroke_dasharray() const LengthList StyledElement::stroke_dasharray() const
{ {
auto& value = find(PropertyId::Stroke_Dasharray); auto& value = find(PropertyID::Stroke_Dasharray);
return Parser::parseLengthList(value, ForbidNegativeLengths); return Parser::parseLengthList(value, ForbidNegativeLengths);
} }
WindRule StyledElement::fill_rule() const WindRule StyledElement::fill_rule() const
{ {
auto& value = find(PropertyId::Fill_Rule); auto& value = find(PropertyID::Fill_Rule);
return Parser::parseWindRule(value); return Parser::parseWindRule(value);
} }
WindRule StyledElement::clip_rule() const WindRule StyledElement::clip_rule() const
{ {
auto& value = find(PropertyId::Clip_Rule); auto& value = find(PropertyID::Clip_Rule);
return Parser::parseWindRule(value); return Parser::parseWindRule(value);
} }
LineCap StyledElement::stroke_linecap() const LineCap StyledElement::stroke_linecap() const
{ {
auto& value = find(PropertyId::Stroke_Linecap); auto& value = find(PropertyID::Stroke_Linecap);
return Parser::parseLineCap(value); return Parser::parseLineCap(value);
} }
LineJoin StyledElement::stroke_linejoin() const LineJoin StyledElement::stroke_linejoin() const
{ {
auto& value = find(PropertyId::Stroke_Linejoin); auto& value = find(PropertyID::Stroke_Linejoin);
return Parser::parseLineJoin(value); return Parser::parseLineJoin(value);
} }
Display StyledElement::display() const Display StyledElement::display() const
{ {
auto& value = get(PropertyId::Display); auto& value = get(PropertyID::Display);
return Parser::parseDisplay(value); return Parser::parseDisplay(value);
} }
Visibility StyledElement::visibility() const Visibility StyledElement::visibility() const
{ {
auto& value = find(PropertyId::Visibility); auto& value = find(PropertyID::Visibility);
return Parser::parseVisibility(value); return Parser::parseVisibility(value);
} }
Overflow StyledElement::overflow() const Overflow StyledElement::overflow() const
{ {
auto& value = get(PropertyId::Overflow); auto& value = get(PropertyID::Overflow);
return Parser::parseOverflow(value, parent == nullptr ? Overflow::Visible : Overflow::Hidden); return Parser::parseOverflow(value, parent == nullptr ? Overflow::Visible : Overflow::Hidden);
} }
std::string StyledElement::clip_path() const std::string StyledElement::clip_path() const
{ {
auto& value = get(PropertyId::Clip_Path); auto& value = get(PropertyID::Clip_Path);
return Parser::parseUrl(value); return Parser::parseUrl(value);
} }
std::string StyledElement::mask() const std::string StyledElement::mask() const
{ {
auto& value = get(PropertyId::Mask); auto& value = get(PropertyID::Mask);
return Parser::parseUrl(value); return Parser::parseUrl(value);
} }
std::string StyledElement::marker_start() const std::string StyledElement::marker_start() const
{ {
auto& value = find(PropertyId::Marker_Start); auto& value = find(PropertyID::Marker_Start);
return Parser::parseUrl(value); return Parser::parseUrl(value);
} }
std::string StyledElement::marker_mid() const std::string StyledElement::marker_mid() const
{ {
auto& value = find(PropertyId::Marker_Mid); auto& value = find(PropertyID::Marker_Mid);
return Parser::parseUrl(value); return Parser::parseUrl(value);
} }
std::string StyledElement::marker_end() const std::string StyledElement::marker_end() const
{ {
auto& value = find(PropertyId::Marker_End); auto& value = find(PropertyID::Marker_End);
return Parser::parseUrl(value); return Parser::parseUrl(value);
} }

View file

@ -8,7 +8,7 @@ namespace lunasvg {
class StyledElement : public Element class StyledElement : public Element
{ {
public: public:
StyledElement(ElementId id); StyledElement(ElementID id);
Paint fill() const; Paint fill() const;
Paint stroke() const; Paint stroke() const;

View file

@ -3,7 +3,7 @@
namespace lunasvg { namespace lunasvg {
StyleElement::StyleElement() StyleElement::StyleElement()
: Element(ElementId::Style) : Element(ElementID::Style)
{ {
} }

View file

@ -5,47 +5,47 @@
namespace lunasvg { namespace lunasvg {
SVGElement::SVGElement() SVGElement::SVGElement()
: GraphicsElement(ElementId::Svg) : GraphicsElement(ElementID::Svg)
{ {
} }
Length SVGElement::x() const Length SVGElement::x() const
{ {
auto& value = get(PropertyId::X); auto& value = get(PropertyID::X);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length SVGElement::y() const Length SVGElement::y() const
{ {
auto& value = get(PropertyId::Y); auto& value = get(PropertyID::Y);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length SVGElement::width() const Length SVGElement::width() const
{ {
auto& value = get(PropertyId::Width); auto& value = get(PropertyID::Width);
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
} }
Length SVGElement::height() const Length SVGElement::height() const
{ {
auto& value = get(PropertyId::Height); auto& value = get(PropertyID::Height);
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
} }
Rect SVGElement::viewBox() const Rect SVGElement::viewBox() const
{ {
auto& value = get(PropertyId::ViewBox); auto& value = get(PropertyID::ViewBox);
return Parser::parseViewBox(value); return Parser::parseViewBox(value);
} }
PreserveAspectRatio SVGElement::preserveAspectRatio() const PreserveAspectRatio SVGElement::preserveAspectRatio() const
{ {
auto& value = get(PropertyId::PreserveAspectRatio); auto& value = get(PropertyID::PreserveAspectRatio);
return Parser::parsePreserveAspectRatio(value); return Parser::parsePreserveAspectRatio(value);
} }
std::unique_ptr<LayoutSymbol> SVGElement::layoutDocument(const ParseDocument* document) const std::unique_ptr<LayoutSymbol> SVGElement::build(const TreeBuilder* builder) const
{ {
if(isDisplayNone()) if(isDisplayNone())
return nullptr; return nullptr;
@ -73,7 +73,7 @@ std::unique_ptr<LayoutSymbol> SVGElement::layoutDocument(const ParseDocument* do
root->clip = isOverflowHidden() ? preserveAspectRatio.getClip(_w, _h, viewBox) : Rect::Invalid; root->clip = isOverflowHidden() ? preserveAspectRatio.getClip(_w, _h, viewBox) : Rect::Invalid;
root->opacity = opacity(); root->opacity = opacity();
LayoutContext context(document, root.get()); LayoutContext context(builder, root.get());
root->masker = context.getMasker(mask()); root->masker = context.getMasker(mask());
root->clipper = context.getClipper(clip_path()); root->clipper = context.getClipper(clip_path());
layoutChildren(&context, root.get()); layoutChildren(&context, root.get());

View file

@ -5,7 +5,7 @@
namespace lunasvg { namespace lunasvg {
class ParseDocument; class TreeBuilder;
class LayoutSymbol; class LayoutSymbol;
class SVGElement : public GraphicsElement class SVGElement : public GraphicsElement
@ -20,7 +20,7 @@ public:
Rect viewBox() const; Rect viewBox() const;
PreserveAspectRatio preserveAspectRatio() const; PreserveAspectRatio preserveAspectRatio() const;
std::unique_ptr<LayoutSymbol> layoutDocument(const ParseDocument* document) const; std::unique_ptr<LayoutSymbol> build(const TreeBuilder* builder) const;
void layout(LayoutContext* context, LayoutContainer* current) const; void layout(LayoutContext* context, LayoutContainer* current) const;
std::unique_ptr<Node> clone() const; std::unique_ptr<Node> clone() const;

View file

@ -4,43 +4,43 @@
namespace lunasvg { namespace lunasvg {
SymbolElement::SymbolElement() SymbolElement::SymbolElement()
: StyledElement(ElementId::Symbol) : StyledElement(ElementID::Symbol)
{ {
} }
Length SymbolElement::x() const Length SymbolElement::x() const
{ {
auto& value = get(PropertyId::X); auto& value = get(PropertyID::X);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length SymbolElement::y() const Length SymbolElement::y() const
{ {
auto& value = get(PropertyId::Y); auto& value = get(PropertyID::Y);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length SymbolElement::width() const Length SymbolElement::width() const
{ {
auto& value = get(PropertyId::Width); auto& value = get(PropertyID::Width);
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
} }
Length SymbolElement::height() const Length SymbolElement::height() const
{ {
auto& value = get(PropertyId::Height); auto& value = get(PropertyID::Height);
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
} }
Rect SymbolElement::viewBox() const Rect SymbolElement::viewBox() const
{ {
auto& value = get(PropertyId::ViewBox); auto& value = get(PropertyID::ViewBox);
return Parser::parseViewBox(value); return Parser::parseViewBox(value);
} }
PreserveAspectRatio SymbolElement::preserveAspectRatio() const PreserveAspectRatio SymbolElement::preserveAspectRatio() const
{ {
auto& value = get(PropertyId::PreserveAspectRatio); auto& value = get(PropertyID::PreserveAspectRatio);
return Parser::parsePreserveAspectRatio(value); return Parser::parsePreserveAspectRatio(value);
} }

View file

@ -8,47 +8,47 @@
namespace lunasvg { namespace lunasvg {
UseElement::UseElement() UseElement::UseElement()
: GraphicsElement(ElementId::Use) : GraphicsElement(ElementID::Use)
{ {
} }
Length UseElement::x() const Length UseElement::x() const
{ {
auto& value = get(PropertyId::X); auto& value = get(PropertyID::X);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length UseElement::y() const Length UseElement::y() const
{ {
auto& value = get(PropertyId::Y); auto& value = get(PropertyID::Y);
return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero);
} }
Length UseElement::width() const Length UseElement::width() const
{ {
auto& value = get(PropertyId::Width); auto& value = get(PropertyID::Width);
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
} }
Length UseElement::height() const Length UseElement::height() const
{ {
auto& value = get(PropertyId::Height); auto& value = get(PropertyID::Height);
return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent);
} }
std::string UseElement::href() const std::string UseElement::href() const
{ {
auto& value = get(PropertyId::Href); auto& value = get(PropertyID::Href);
return Parser::parseHref(value); return Parser::parseHref(value);
} }
void UseElement::transferWidthAndHeight(Element* element) const void UseElement::transferWidthAndHeight(Element* element) const
{ {
auto& width = get(PropertyId::Width); auto& width = get(PropertyID::Width);
auto& height = get(PropertyId::Height); auto& height = get(PropertyID::Height);
element->set(PropertyId::Width, width, 0x0); element->set(PropertyID::Width, width, 0x0);
element->set(PropertyId::Height, height, 0x0); element->set(PropertyID::Height, height, 0x0);
} }
void UseElement::layout(LayoutContext* context, LayoutContainer* current) const 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 _x = lengthContext.valueForLength(x(), LengthMode::Width);
auto _y = lengthContext.valueForLength(y(), LengthMode::Height); auto _y = lengthContext.valueForLength(y(), LengthMode::Height);
auto transform = get(PropertyId::Transform); auto transform = get(PropertyID::Transform);
transform += "translate("; transform += "translate(";
transform += std::to_string(_x); transform += std::to_string(_x);
transform += ' '; transform += ' ';
transform += std::to_string(_y); transform += std::to_string(_y);
transform += ')'; 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<SVGElement>(); auto element = ref->cloneElement<SVGElement>();
transferWidthAndHeight(element.get()); transferWidthAndHeight(element.get());