From 0d52738239a3496122cb332378fc58cf9943c0d5 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Mon, 3 Oct 2022 23:55:09 +0200 Subject: [PATCH] Squashed 'external/lunasvg/' changes from d13d8e521..e0f786c9b e0f786c9b Fix parseStyle REVERT: d13d8e521 Refactoring REVERT: 4925c87a8 Update REVERT: 794c38591 Update REVERT: 49eee9643 Update REVERT: 914aee5ea Update REVERT: 3bb00ecee Fix ParserString string-view iterator cast in windows REVERT: fabea2008 Fix string-view iterator cast in windows REVERT: bbcf0d34f Update REVERT: 081df20f2 Update REVERT: fe3101f91 Refactoring REVERT: e9a41dc83 Refactoring git-subtree-dir: external/lunasvg git-subtree-split: e0f786c9be6fae1ffabddfe56fb1e0a1a7eb775d --- CMakeLists.txt | 2 +- source/CMakeLists.txt | 2 - source/canvas.cpp | 8 +- source/clippathelement.cpp | 4 +- source/cssparser.cpp | 1816 ------------------------------------ source/cssparser.h | 305 ------ source/cssstylesheet.cpp | 378 -------- source/cssstylesheet.h | 495 ---------- source/defselement.cpp | 2 +- source/element.cpp | 161 +--- source/element.h | 54 +- source/gelement.cpp | 2 +- source/geometryelement.cpp | 56 +- source/geometryelement.h | 4 +- source/graphicselement.cpp | 4 +- source/graphicselement.h | 2 +- source/layoutcontext.cpp | 12 +- source/layoutcontext.h | 9 +- source/lunasvg.cpp | 6 +- source/markerelement.cpp | 18 +- source/maskelement.cpp | 14 +- source/paintelement.cpp | 118 +-- source/paintelement.h | 4 +- source/parser.cpp | 1012 ++++++++++---------- source/parser.h | 105 +-- source/parserutils.h | 237 ----- source/pointer.h | 254 ----- source/stopelement.cpp | 4 +- source/styledelement.cpp | 54 +- source/styledelement.h | 2 +- source/styleelement.cpp | 2 +- source/svgelement.cpp | 18 +- source/svgelement.h | 4 +- source/symbolelement.cpp | 14 +- source/useelement.cpp | 26 +- 35 files changed, 761 insertions(+), 4447 deletions(-) delete mode 100644 source/cssparser.cpp delete mode 100644 source/cssparser.h delete mode 100644 source/cssstylesheet.cpp delete mode 100644 source/cssstylesheet.h delete mode 100644 source/pointer.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 52d1f6320..b9bf46a88 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.3) project(lunasvg VERSION 2.3.2 LANGUAGES CXX C) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 14) set(CMAKE_C_STANDARD 11) option(BUILD_SHARED_LIBS "Builds as shared library" OFF) diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index c00b891d5..1866748d2 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -20,8 +20,6 @@ PRIVATE "${CMAKE_CURRENT_LIST_DIR}/svgelement.cpp" "${CMAKE_CURRENT_LIST_DIR}/symbolelement.cpp" "${CMAKE_CURRENT_LIST_DIR}/useelement.cpp" - "${CMAKE_CURRENT_LIST_DIR}/cssparser.cpp" - "${CMAKE_CURRENT_LIST_DIR}/cssstylesheet.cpp" ) target_include_directories(lunasvg diff --git a/source/canvas.cpp b/source/canvas.cpp index d97729199..6311ffc17 100644 --- a/source/canvas.cpp +++ b/source/canvas.cpp @@ -9,7 +9,7 @@ static plutovg_fill_rule_t to_plutovg_fill_rule(WindRule winding); static plutovg_operator_t to_plutovg_operator(BlendMode mode); static plutovg_line_cap_t to_plutovg_line_cap(LineCap cap); static plutovg_line_join_t to_plutovg_line_join(LineJoin join); -static plutovg_spread_method_t to_plutovg_spread_method(SpreadMethod spread); +static plutovg_spread_method_t to_plutovg_spread_methood(SpreadMethod spread); static void to_plutovg_stops(plutovg_gradient_t* gradient, const GradientStops& stops); static void to_plutovg_path(plutovg_t* pluto, const Path& path); @@ -67,7 +67,7 @@ void Canvas::setLinearGradient(double x1, double y1, double x2, double y2, const auto gradient = plutovg_gradient_create_linear(x1, y1, x2, y2); auto matrix = to_plutovg_matrix(transform); to_plutovg_stops(gradient, stops); - plutovg_gradient_set_spread(gradient, to_plutovg_spread_method(spread)); + plutovg_gradient_set_spread(gradient, to_plutovg_spread_methood(spread)); plutovg_gradient_set_matrix(gradient, &matrix); plutovg_set_source_gradient(pluto, gradient); plutovg_gradient_destroy(gradient); @@ -78,7 +78,7 @@ void Canvas::setRadialGradient(double cx, double cy, double r, double fx, double auto gradient = plutovg_gradient_create_radial(cx, cy, r, fx, fy, 0); auto matrix = to_plutovg_matrix(transform); to_plutovg_stops(gradient, stops); - plutovg_gradient_set_spread(gradient, to_plutovg_spread_method(spread)); + plutovg_gradient_set_spread(gradient, to_plutovg_spread_methood(spread)); plutovg_gradient_set_matrix(gradient, &matrix); plutovg_set_source_gradient(pluto, gradient); plutovg_gradient_destroy(gradient); @@ -227,7 +227,7 @@ plutovg_line_join_t to_plutovg_line_join(LineJoin join) return join == LineJoin::Miter ? plutovg_line_join_miter : join == LineJoin::Round ? plutovg_line_join_round : plutovg_line_join_bevel; } -static plutovg_spread_method_t to_plutovg_spread_method(SpreadMethod spread) +static plutovg_spread_method_t to_plutovg_spread_methood(SpreadMethod spread) { return spread == SpreadMethod::Pad ? plutovg_spread_method_pad : spread == SpreadMethod::Reflect ? plutovg_spread_method_reflect : plutovg_spread_method_repeat; } diff --git a/source/clippathelement.cpp b/source/clippathelement.cpp index 25ea70097..9472b5ce5 100644 --- a/source/clippathelement.cpp +++ b/source/clippathelement.cpp @@ -5,13 +5,13 @@ namespace lunasvg { ClipPathElement::ClipPathElement() - : GraphicsElement(ElementID::ClipPath) + : GraphicsElement(ElementId::ClipPath) { } Units ClipPathElement::clipPathUnits() const { - auto& value = get(PropertyID::ClipPathUnits); + auto& value = get(PropertyId::ClipPathUnits); return Parser::parseUnits(value, Units::UserSpaceOnUse); } diff --git a/source/cssparser.cpp b/source/cssparser.cpp deleted file mode 100644 index 4ad35907d..000000000 --- a/source/cssparser.cpp +++ /dev/null @@ -1,1816 +0,0 @@ -#include "cssparser.h" - -#include - -namespace lunasvg { - -constexpr bool isNameStart(char cc) { return isalpha(cc) || cc == '_'; } -constexpr bool isNameChar(char cc) { return isNameStart(cc) || isdigit(cc) || cc == '-'; } -constexpr bool isNewLine(char cc) { return (cc == '\n' || cc == '\r' || cc == '\f'); } -constexpr bool isNonPrintable(char cc) { return (cc >= 0 && cc <= 0x8) || cc == 0xb || (cc >= 0xf && cc <= 0x1f) || cc == 0x7f; } - -CSSTokenStream CSSTokenizer::tokenize() -{ - while(true) { - auto token = nextToken(); - if(token.type() == CSSToken::Type::Comment) - continue; - if(token.type() == CSSToken::Type::EndOfFile) - break; - m_tokenList.push_back(token); - } - - auto begin = m_tokenList.data(); - auto end = begin + m_tokenList.size(); - return CSSTokenStream(begin, end); -} - -bool CSSTokenizer::isEscapeSequence(char first, char second) -{ - return first == '\\' && !isNewLine(second); -} - -bool CSSTokenizer::isIdentSequence(char first, char second, char third) -{ - if(isNameStart(first) || isEscapeSequence(first, second)) - return true; - if(first == '-') - return isNameStart(second) || second == '-' || isEscapeSequence(second, third); - return false; -} - -bool CSSTokenizer::isNumberSequence(char first, char second, char third) -{ - if(isdigit(first)) - return true; - if(first == '-' || first == '+') - return isdigit(second) || (second == '.' && isdigit(third)); - if(first == '.') - return isdigit(second); - return false; -} - -bool CSSTokenizer::isEscapeSequence() const -{ - if(m_input.empty()) - return false; - return isEscapeSequence(*m_input, m_input.peek(1)); -} - -bool CSSTokenizer::isIdentSequence() const -{ - if(m_input.empty()) - return false; - auto second = m_input.peek(1); - if(second == 0) - return isIdentSequence(*m_input, 0, 0); - return isIdentSequence(*m_input, second, m_input.peek(2)); -} - -bool CSSTokenizer::isNumberSequence() const -{ - if(m_input.empty()) - return false; - auto second = m_input.peek(1); - if(second == 0) - return isNumberSequence(*m_input, 0, 0); - return isNumberSequence(*m_input, second, m_input.peek(2)); -} - -bool CSSTokenizer::isExponentSequence() const -{ - if(m_input.peek() != 'E' && m_input.peek() != 'e') - return false; - if(m_input.peek(1) == '+' || m_input.peek(1) == '-') - return isdigit(m_input.peek(2)); - return isdigit(m_input.peek(1)); -} - -std::string_view CSSTokenizer::substring(size_t offset, size_t count) -{ - return m_input.string(offset, count); -} - -std::string_view CSSTokenizer::addstring(std::string&& value) -{ - m_stringList.push_back(std::move(value)); - return m_stringList.back(); -} - -std::string_view CSSTokenizer::consumeName() -{ - size_t count = 0; - while(true) { - auto cc = m_input.peek(count); - if(cc == 0 || cc == '\\') - break; - if(!isNameChar(cc)) { - auto offset = m_input.offset(); - m_input.advance(count); - return substring(offset, count); - } - - count += 1; - } - - std::string output; - while(true) { - auto cc = m_input.peek(); - if(isNameChar(cc)) { - output += cc; - m_input.advance(); - } else if(isEscapeSequence()) { - appendCodepoint(output, consumeEscape()); - } else { - break; - } - } - - return addstring(std::move(output)); -} - -uint32_t CSSTokenizer::consumeEscape() -{ - assert(isEscapeSequence()); - auto cc = m_input.advance(); - if(isxdigit(cc)) { - int count = 0; - uint32_t cp = 0; - do { - cp = cp * 16 + xdigit(cc); - cc = m_input.advance(); - count += 1; - } while(count < 6 && isxdigit(cc)); - - if(isspace(cc)) { - if(cc == '\r' && m_input.peek(1) == '\n') - m_input.advance(); - m_input.advance(); - } - - if(cp == 0 || cp >= 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) - return 0xFFFD; - return cp; - } - - if(cc == 0) - return 0xFFFD; - m_input.advance(); - return cc; -} - -CSSToken CSSTokenizer::consumeStringToken() -{ - auto endingCodePoint = m_input.peek(); - assert(endingCodePoint == '\"' || endingCodePoint == '\''); - m_input.advance(); - size_t count = 0; - while(true) { - auto cc = m_input.peek(count); - if(cc == 0 || cc == '\\') - break; - if(cc == endingCodePoint) { - auto offset = m_input.offset(); - m_input.advance(count); - m_input.advance(); - return CSSToken(CSSToken::Type::String, substring(offset, count)); - } - - if(isNewLine(cc)) { - m_input.advance(count); - return CSSToken(CSSToken::Type::BadString); - } - - count += 1; - } - - std::string output; - while(true) { - auto cc = m_input.peek(); - if(cc == 0) - break; - if(cc == endingCodePoint) { - m_input.advance(); - break; - } - - if(isNewLine(cc)) - return CSSToken(CSSToken::Type::BadString); - - if(cc == '\\') { - auto next = m_input.peek(1); - if(next == 0) { - m_input.advance(); - } else if(isNewLine(next)) { - if(next == '\r' && m_input.peek(2) == '\n') - m_input.advance(); - m_input.advance(2); - } else { - appendCodepoint(output, consumeEscape()); - } - } else { - output += cc; - m_input.advance(); - } - } - - if(output.empty()) - return CSSToken(CSSToken::Type::String); - return CSSToken(CSSToken::Type::String, addstring(std::move(output))); -} - -CSSToken CSSTokenizer::consumeNumericToken() -{ - assert(isNumberSequence()); - auto numberType = CSSToken::NumberType::Integer; - auto numberSign = CSSToken::NumberSign::None; - double fraction = 0; - double integer = 0; - int exponent = 0; - int expsign = 1; - - if(m_input.peek() == '-') { - numberSign = CSSToken::NumberSign::Minus; - m_input.advance(); - } else if(m_input.peek() == '+') { - numberSign = CSSToken::NumberSign::Plus; - m_input.advance(); - } - - if(isdigit(m_input.peek())) { - auto cc = m_input.peek(); - do { - integer = 10.0 * integer + (cc - '0'); - cc = m_input.advance(); - } while(isdigit(cc)); - } - - if(m_input.peek() == '.' && isdigit(m_input.peek(1))) { - numberType = CSSToken::NumberType::Number; - auto cc = m_input.advance(); - int count = 0; - do { - fraction = 10.0 * fraction + (cc - '0'); - count += 1; - cc = m_input.advance(); - } while(isdigit(cc)); - fraction *= std::pow(10.0, -count); - } - - if(isExponentSequence()) { - numberType = CSSToken::NumberType::Number; - m_input.advance(); - if(m_input.peek() == '-') { - expsign = -1; - m_input.advance(); - } else if(m_input.peek() == '+') { - m_input.advance(); - } - - auto cc = m_input.peek(); - do { - exponent = 10 * exponent + (cc - '0'); - cc = m_input.advance(); - } while(isdigit(cc)); - } - - double number = (integer + fraction) * std::pow(10.0, exponent * expsign); - if(numberSign == CSSToken::NumberSign::Minus) - number = -number; - - if(m_input.peek() == '%') { - m_input.advance(); - return CSSToken(CSSToken::Type::Percentage, numberType, numberSign, number); - } - - if(isIdentSequence()) - return CSSToken(CSSToken::Type::Dimension, numberType, numberSign, number, consumeName()); - return CSSToken(CSSToken::Type::Number, numberType, numberSign, number); -} - -CSSToken CSSTokenizer::consumeIdentLikeToken() -{ - auto name = consumeName(); - if(equals(name, "url", false) && m_input.peek() == '(') { - auto cc = m_input.advance(); - while(isspace(cc) && isspace(m_input.peek(1))) { - cc = m_input.advance(); - } - - if(isspace(cc)) - cc = m_input.peek(1); - - if(cc == '"' || cc == '\'') - return CSSToken(CSSToken::Type::Function, name); - return consumeUrlToken(); - } - - if(m_input.peek() == '(') { - m_input.advance(); - return CSSToken(CSSToken::Type::Function, name); - } - - return CSSToken(CSSToken::Type::Ident, name); -} - -CSSToken CSSTokenizer::consumeUrlToken() -{ - auto cc = m_input.peek(); - while(isspace(cc)) { - cc = m_input.advance(); - } - - size_t count = 0; - while(true) { - auto cc = m_input.peek(count); - if(cc == 0 || cc == '\\' || isspace(cc)) - break; - if(cc == ')') { - auto offset = m_input.offset(); - m_input.advance(count); - m_input.advance(); - return CSSToken(CSSToken::Type::Url, substring(offset, count)); - } - - if(cc == '"' || cc == '\'' || cc == '(' || isNonPrintable(cc)) { - m_input.advance(count); - return consumeBadUrlRemnants(); - } - - count += 1; - } - - std::string output; - while(true) { - auto cc = m_input.peek(); - if(cc == 0) - break; - if(cc == ')') { - m_input.advance(); - break; - } - - if(cc == '\\') { - if(isEscapeSequence()) { - appendCodepoint(output, consumeEscape()); - continue; - } - - return consumeBadUrlRemnants(); - } - - if(isspace(cc)) { - do { - cc = m_input.advance(); - } while(isspace(cc)); - - if(cc == 0) - break; - if(cc == ')') { - m_input.advance(); - break; - } - - return consumeBadUrlRemnants(); - } - - if(cc == '"' || cc == '\'' || cc == '(' || isNonPrintable(cc)) - return consumeBadUrlRemnants(); - - output += cc; - m_input.advance(); - } - - return CSSToken(CSSToken::Type::Url, addstring(std::move(output))); -} - -CSSToken CSSTokenizer::consumeBadUrlRemnants() -{ - while(true) { - auto cc = m_input.peek(); - if(cc == 0) - break; - if(cc == ')') { - m_input.advance(); - break; - } - - if(isEscapeSequence()) { - consumeEscape(); - } else { - m_input.advance(); - } - } - - return CSSToken(CSSToken::Type::BadUrl); -} - -CSSToken CSSTokenizer::consumeWhitespaceToken() -{ - auto cc = m_input.peek(); - assert(isspace(cc)); - do { - cc = m_input.advance(); - } while(isspace(cc)); - - return CSSToken(CSSToken::Type::Whitespace); -} - -CSSToken CSSTokenizer::consumeCommentToken() -{ - while(true) { - auto cc = m_input.peek(); - if(cc == 0) - break; - if(cc == '*' && m_input.peek(1) == '/') { - m_input.advance(2); - break; - } - - m_input.advance(); - } - - return CSSToken(CSSToken::Type::Comment); -} - -CSSToken CSSTokenizer::consumeSolidusToken() -{ - auto cc = m_input.advance(); - if(cc == '*') { - m_input.advance(); - return consumeCommentToken(); - } - - return CSSToken(CSSToken::Type::Delim, '/'); -} - -CSSToken CSSTokenizer::consumeHashToken() -{ - auto cc = m_input.advance(); - if(isNameChar(cc) || isEscapeSequence()) { - if(isIdentSequence()) - return CSSToken(CSSToken::Type::Hash, CSSToken::HashType::Identifier, consumeName()); - return CSSToken(CSSToken::Type::Hash, CSSToken::HashType::Unrestricted, consumeName()); - } - - return CSSToken(CSSToken::Type::Delim, '#'); -} - -CSSToken CSSTokenizer::consumePlusSignToken() -{ - if(isNumberSequence()) - return consumeNumericToken(); - - m_input.advance(); - return CSSToken(CSSToken::Type::Delim, '+'); -} - -CSSToken CSSTokenizer::consumeHyphenMinusToken() -{ - if(isNumberSequence()) - return consumeNumericToken(); - - if(m_input.peek(1) == '-' && m_input.peek(2) == '>') { - m_input.advance(3); - return CSSToken(CSSToken::Type::CDC); - } - - if(isIdentSequence()) - return consumeIdentLikeToken(); - - m_input.advance(); - return CSSToken(CSSToken::Type::Delim, '-'); -} - -CSSToken CSSTokenizer::consumeFullStopToken() -{ - if(isNumberSequence()) - return consumeNumericToken(); - - m_input.advance(); - return CSSToken(CSSToken::Type::Delim, '.'); -} - -CSSToken CSSTokenizer::consumeLessThanSignToken() -{ - auto cc = m_input.advance(); - if(cc == '!' && m_input.peek(1) == '-' && m_input.peek(2) == '-') { - m_input.advance(3); - return CSSToken(CSSToken::Type::CDO); - } - - return CSSToken(CSSToken::Type::Delim, '<'); -} - -CSSToken CSSTokenizer::consumeCommercialAtToken() -{ - m_input.advance(); - if(isIdentSequence()) - return CSSToken(CSSToken::Type::AtKeyword, consumeName()); - - return CSSToken(CSSToken::Type::Delim, '@'); -} - -CSSToken CSSTokenizer::consumeReverseSolidusToken() -{ - if(isEscapeSequence()) - return consumeIdentLikeToken(); - - m_input.advance(); - return CSSToken(CSSToken::Type::Delim, '\\'); -} - -CSSToken CSSTokenizer::nextToken() -{ - auto cc = m_input.peek(); - if(cc == 0) - return CSSToken(CSSToken::Type::EndOfFile); - - if(isspace(cc)) - return consumeWhitespaceToken(); - - if(isdigit(cc)) - return consumeNumericToken(); - - if(isNameStart(cc)) - return consumeIdentLikeToken(); - - switch(cc) { - case '/': - return consumeSolidusToken(); - case '#': - return consumeHashToken(); - case '+': - return consumePlusSignToken(); - case '-': - return consumeHyphenMinusToken(); - case '.': - return consumeFullStopToken(); - case '<': - return consumeLessThanSignToken(); - case '@': - return consumeCommercialAtToken(); - case '\\': - return consumeReverseSolidusToken(); - case '\"': - return consumeStringToken(); - case '\'': - return consumeStringToken(); - } - - m_input.advance(); - switch(cc) { - case '(': - return CSSToken(CSSToken::Type::LeftParenthesis); - case ')': - return CSSToken(CSSToken::Type::RightParenthesis); - case '[': - return CSSToken(CSSToken::Type::LeftSquareBracket); - case ']': - return CSSToken(CSSToken::Type::RightSquareBracket); - case '{': - return CSSToken(CSSToken::Type::LeftCurlyBracket); - case '}': - return CSSToken(CSSToken::Type::RightCurlyBracket); - case ',': - return CSSToken(CSSToken::Type::Comma); - case ':': - return CSSToken(CSSToken::Type::Colon); - case ';': - return CSSToken(CSSToken::Type::Semicolon); - } - - return CSSToken(CSSToken::Type::Delim, cc); -} - -void CSSParser::parseSheet(CSSRuleList& rules, const std::string_view& content) -{ - CSSTokenizer tokenizer(content); - auto input = tokenizer.tokenize(); - while(!input.empty()) { - input.consumeWhitespace(); - if(input->type() == CSSToken::Type::CDC - || input->type() == CSSToken::Type::CDO) { - input.consume(); - continue; - } - - consumeRule(input, rules); - } -} - -void CSSParser::parseStyle(CSSPropertyList& properties, const std::string_view& content) -{ - CSSTokenizer tokenizer(content); - auto input = tokenizer.tokenize(); - consumeDeclaractionList(input, properties); -} - -bool CSSParser::consumeRule(CSSTokenStream& input, CSSRuleList& rules) -{ - if(input->type() == CSSToken::Type::AtKeyword) - return consumeAtRule(input, rules); - return consumeStyleRule(input, rules); -} - -bool CSSParser::consumeStyleRule(CSSTokenStream& input, CSSRuleList& rules) -{ - auto preludeBegin = input.begin(); - while(!input.empty() && input->type() != CSSToken::Type::LeftCurlyBracket) { - input.consumeComponent(); - } - - CSSTokenStream prelude(preludeBegin, input.begin()); - if(input.empty()) - return false; - - auto block = input.consumeBlock(); - CSSSelectorList selectors; - if(!consumeSelectorList(prelude, selectors)) - return false; - - CSSPropertyList properties; - consumeDeclaractionList(block, properties); - rules.push_back(CSSRule::create(std::move(selectors), std::move(properties))); - return true; -} - -bool CSSParser::consumeAtRule(CSSTokenStream& input, CSSRuleList& rules) -{ - auto name = input->data(); - input.consume(); - auto preludeBegin = input.begin(); - while(input->type() != CSSToken::Type::EndOfFile - && input->type() != CSSToken::Type::LeftCurlyBracket - && input->type() != CSSToken::Type::Semicolon) { - input.consumeComponent(); - } - - CSSTokenStream prelude(preludeBegin, input.begin()); - if(input->type() == CSSToken::Type::EndOfFile - || input->type() == CSSToken::Type::Semicolon) { - if(input->type() == CSSToken::Type::Semicolon) - input.consume(); - if(equals(name, "import", false)) - return consumeImportRule(prelude, rules); - return false; - } - - return false; -} - -bool CSSParser::consumeImportRule(CSSTokenStream& input, CSSRuleList& rules) -{ - return false; -} - -bool CSSParser::consumeSelectorList(CSSTokenStream& input, CSSSelectorList& selectors) -{ - CSSSelector selector; - input.consumeWhitespace(); - if(!consumeSelector(input, selector)) - return false; - - selectors.push_back(std::move(selector)); - while(input->type() == CSSToken::Type::Comma) { - input.consumeIncludingWhitespace(); - if(!consumeSelector(input, selector)) - return false; - selectors.push_back(std::move(selector)); - } - - return input.empty(); -} - -bool CSSParser::consumeSelector(CSSTokenStream& input, CSSSelector& selector) -{ - auto combinator = CSSSimpleSelector::Combinator::None; - do { - CSSSimpleSelector sel; - sel.combinator = combinator; - if(!consumeSimpleSelector(input, sel)) - return combinator == CSSSimpleSelector::Combinator::Descendant; - selector.push_back(std::move(sel)); - } while(consumeCombinator(input, combinator)); - return true; -} - -bool CSSParser::consumeSimpleSelector(CSSTokenStream& input, CSSSimpleSelector& selector) -{ - auto consume = [](auto& input, auto& selector) { - if(input->type() == CSSToken::Type::Hash) - return consumeIdSelector(input, selector); - if(input->type() == CSSToken::Type::Delim && input->delim() == '.') - return consumeClassSelector(input, selector); - if(input->type() == CSSToken::Type::LeftSquareBracket) - return consumeAttributeSelector(input, selector); - if(input->type() == CSSToken::Type::Colon) - return consumePseudoSelector(input, selector); - return false; - }; - - if(!consumeTagSelector(input, selector) - && !consume(input, selector)) { - return false; - } - - while(consume(input, selector)); - return true; -} - -bool CSSParser::consumeTagSelector(CSSTokenStream& input, CSSSimpleSelector& selector) -{ - if(input->type() == CSSToken::Type::Ident) { - selector.id = elementid(input->data()); - input.consume(); - return true; - } - - if(input->type() == CSSToken::Type::Delim && input->delim() == '*') { - selector.id = ElementID::Star; - input.consume(); - return true; - } - - return false; -} - -bool CSSParser::consumeIdSelector(CSSTokenStream& input, CSSSimpleSelector& selector) -{ - if(input->hashType() == CSSToken::HashType::Identifier) { - CSSSimpleSelector::Attribute attribute; - attribute.type = CSSSimpleSelector::Attribute::Type::Equals; - attribute.id = PropertyID::Id; - attribute.value = input->data(); - selector.attributes.push_back(attribute); - input.consume(); - return true; - } - - return false; -} - -bool CSSParser::consumeClassSelector(CSSTokenStream& input, CSSSimpleSelector& selector) -{ - input.consume(); - if(input->type() == CSSToken::Type::Ident) { - CSSSimpleSelector::Attribute attribute; - attribute.type = CSSSimpleSelector::Attribute::Type::Includes; - attribute.id = PropertyID::Class; - attribute.value = input->data(); - selector.attributes.push_back(attribute); - input.consume(); - return true; - } - - return false; -} - -bool CSSParser::consumeAttributeSelector(CSSTokenStream& input, CSSSimpleSelector& selector) -{ - auto block = input.consumeBlock(); - if(block->type() != CSSToken::Type::Ident) - return false; - - CSSSimpleSelector::Attribute attribute; - attribute.id = propertyid(block->data()); - block.consumeIncludingWhitespace(); - if(block.empty()) { - attribute.type = CSSSimpleSelector::Attribute::Type::None; - selector.attributes.push_back(attribute); - return true; - } - - if(block->type() != CSSToken::Type::Delim) - return false; - - switch(block->delim()) { - case '=': - attribute.type = CSSSimpleSelector::Attribute::Type::Equals; - break; - case '*': - attribute.type = CSSSimpleSelector::Attribute::Type::Contains; - break; - case '~': - attribute.type = CSSSimpleSelector::Attribute::Type::Includes; - break; - case '^': - attribute.type = CSSSimpleSelector::Attribute::Type::StartsWith; - break; - case '$': - attribute.type = CSSSimpleSelector::Attribute::Type::EndsWith; - break; - case '|': - attribute.type = CSSSimpleSelector::Attribute::Type::DashEquals; - break; - default: - return false; - } - - if(attribute.type != CSSSimpleSelector::Attribute::Type::Equals) { - block.consume(); - if(block->type() != CSSToken::Type::Delim && block->delim() != '=') - return false; - } - - block.consumeIncludingWhitespace(); - if(block->type() != CSSToken::Type::Ident && block->type() != CSSToken::Type::String) - return false; - - attribute.value = block->data(); - block.consumeIncludingWhitespace(); - if(block.empty()) { - selector.attributes.push_back(attribute); - return true; - } - - return false; -} - -bool CSSParser::consumePseudoSelector(CSSTokenStream& input, CSSSimpleSelector& selector) -{ - input.consume(); - if(input->type() == CSSToken::Type::Ident) { - auto name = input->data(); - input.consume(); - static const struct { - std::string_view name; - CSSSimpleSelector::Pseudo::Type value; - } table[] = { - {"root", CSSSimpleSelector::Pseudo::Type::Root}, - {"empty", CSSSimpleSelector::Pseudo::Type::Empty}, - {"first-child", CSSSimpleSelector::Pseudo::Type::FirstChild}, - {"last-child", CSSSimpleSelector::Pseudo::Type::LastChild}, - {"only-child", CSSSimpleSelector::Pseudo::Type::OnlyChild}, - {"first-of-type", CSSSimpleSelector::Pseudo::Type::FirstOfType}, - {"last-of-type", CSSSimpleSelector::Pseudo::Type::LastOfType}, - {"only-of-type", CSSSimpleSelector::Pseudo::Type::OnlyOfType} - }; - - auto it = std::find_if(table, std::end(table), [name](auto& item) { return equals(name, item.name, false); }); - if(it == std::end(table)) - return false; - CSSSimpleSelector::Pseudo pseudo; - pseudo.type = it->value; - selector.pseudos.push_back(pseudo); - return true; - } - - if(input->type() == CSSToken::Type::Function) { - auto name = input->data(); - auto block = input.consumeBlock(); - block.consumeIncludingWhitespace(); - static const struct { - std::string_view name; - CSSSimpleSelector::Pseudo::Type value; - } table[] = { - {"is", CSSSimpleSelector::Pseudo::Type::Is}, - {"not", CSSSimpleSelector::Pseudo::Type::Not}, - {"nth-child", CSSSimpleSelector::Pseudo::Type::NthChild}, - {"nth-last-child", CSSSimpleSelector::Pseudo::Type::NthLastChild}, - {"nth-of-type", CSSSimpleSelector::Pseudo::Type::NthOfType}, - {"nth-last-of-type", CSSSimpleSelector::Pseudo::Type::NthLastOfType} - }; - - auto it = std::find_if(table, std::end(table), [name](auto& item) { return equals(name, item.name, false); }); - if(it == std::end(table)) - return false; - CSSSimpleSelector::Pseudo pseudo; - pseudo.type = it->value; - switch(pseudo.type) { - case CSSSimpleSelector::Pseudo::Type::Is: - case CSSSimpleSelector::Pseudo::Type::Not: { - if(!consumeSelectorList(block, pseudo.selectors)) - return false; - break; - } - - case CSSSimpleSelector::Pseudo::Type::NthChild: - case CSSSimpleSelector::Pseudo::Type::NthLastChild: - case CSSSimpleSelector::Pseudo::Type::NthOfType: - case CSSSimpleSelector::Pseudo::Type::NthLastOfType: { - if(!consumeMatchPattern(block, pseudo.pattern)) - return false; - break; - } - - default: - assert(false); - } - - selector.pseudos.push_back(pseudo); - block.consumeWhitespace(); - return block.empty(); - } - - return false; -} - -bool CSSParser::consumeCombinator(CSSTokenStream& input, CSSSimpleSelector::Combinator& combinator) -{ - combinator = CSSSimpleSelector::Combinator::None; - while(input->type() == CSSToken::Type::Whitespace) { - combinator = CSSSimpleSelector::Combinator::Descendant; - input.consume(); - } - - if(input->type() == CSSToken::Type::Delim) { - if(input->delim() == '+') { - combinator = CSSSimpleSelector::Combinator::DirectAdjacent; - input.consumeIncludingWhitespace(); - return true; - } - - if(input->delim() == '~') { - combinator = CSSSimpleSelector::Combinator::InDirectAdjacent; - input.consumeIncludingWhitespace(); - return true; - } - - if(input->delim() == '>') { - combinator = CSSSimpleSelector::Combinator::Child; - input.consumeIncludingWhitespace(); - return true; - } - } - - return combinator == CSSSimpleSelector::Combinator::Descendant; -} - -bool CSSParser::consumeMatchPattern(CSSTokenStream& input, CSSSimpleSelector::Pseudo::MatchPattern& pattern) -{ - if(input->type() == CSSToken::Type::Number) { - if(input->numberType() != CSSToken::NumberType::Integer) - return false; - pattern = std::make_pair(0, input->integer()); - input.consume(); - return true; - } - - if(input->type() == CSSToken::Type::Ident) { - if(equals(input->data(), "odd", false)) { - pattern = std::make_pair(2, 1); - input.consume(); - return true; - } - - if(equals(input->data(), "even", false)) { - pattern = std::make_pair(2, 0); - input.consume(); - return true; - } - } - - std::stringstream ss; - if(input->type() == CSSToken::Type::Delim) { - if(input->delim() != '+') - return false; - input.consume(); - if(input->type() != CSSToken::Type::Ident) - return false; - pattern.first = 1; - ss << input->data(); - input.consume(); - } else if(input->type() == CSSToken::Type::Ident) { - auto ident = input->data(); - input.consume(); - if(ident.front() == '-') { - pattern.first = -1; - ss << ident.substr(1); - } else { - pattern.first = 1; - ss << ident; - } - } else if(input->type() == CSSToken::Type::Dimension) { - if(input->numberType() != CSSToken::NumberType::Integer) - return false; - pattern.first = input->integer(); - ss << input->data(); - input.consume(); - } - - constexpr auto eof = std::stringstream::traits_type::eof(); - if(ss.peek() == eof || !equals(ss.get(), 'n', false)) - return false; - - auto sign = CSSToken::NumberSign::None; - if(ss.peek() != eof) { - if(ss.get() != '-') - return false; - sign = CSSToken::NumberSign::Minus; - if(ss.peek() != eof) { - ss >> pattern.second; - if(ss.fail()) - return false; - pattern.second = -pattern.second; - return true; - } - } - - input.consumeWhitespace(); - if(sign == CSSToken::NumberSign::None && input->type() == CSSToken::Type::Delim) { - auto delim = input->delim(); - if(delim == '+') - sign = CSSToken::NumberSign::Plus; - else if(delim == '-') - sign = CSSToken::NumberSign::Minus; - else - return false; - input.consumeIncludingWhitespace(); - } - - if(sign == CSSToken::NumberSign::None && input->type() != CSSToken::Type::Number) { - pattern.second = 0; - return true; - } - - if(input->type() != CSSToken::Type::Number || input->numberType() != CSSToken::NumberType::Integer) - return false; - - if(sign == CSSToken::NumberSign::None && input->numberSign() == CSSToken::NumberSign::None) - return false; - - if(sign != CSSToken::NumberSign::None && input->numberSign() != CSSToken::NumberSign::None) - return false; - - pattern.second = input->integer(); - if(sign == CSSToken::NumberSign::Minus) - pattern.second = -pattern.second; - input.consume(); - return true; -} - -void CSSParser::consumeDeclaractionList(CSSTokenStream& input, CSSPropertyList& properties) -{ - input.consumeWhitespace(); - consumeDeclaraction(input, properties); - while(input->type() == CSSToken::Type::Semicolon) { - input.consumeIncludingWhitespace(); - consumeDeclaraction(input, properties); - } -} - -bool CSSParser::consumeDeclaraction(CSSTokenStream& input, CSSPropertyList& properties) -{ - auto begin = input.begin(); - while(!input.empty() && input->type() != CSSToken::Type::Semicolon) { - input.consumeComponent(); - } - - CSSTokenStream newInput(begin, input.begin()); - if(newInput->type() != CSSToken::Type::Ident) - return false; - - auto id = csspropertyid(newInput->data()); - if(id == CSSPropertyID::Unknown) - return false; - - newInput.consumeIncludingWhitespace(); - if(newInput->type() != CSSToken::Type::Colon) - return false; - - newInput.consumeIncludingWhitespace(); - auto valueBegin = newInput.begin(); - auto valueEnd = newInput.end(); - - auto it = valueEnd - 1; - while(it->type() == CSSToken::Type::Whitespace) { - it -= 1; - } - - bool important = false; - if(it->type() == CSSToken::Type::Ident && equals(it->data(), "important", false)) { - do { - it -= 1; - } while(it->type() == CSSToken::Type::Whitespace); - if(it->type() == CSSToken::Type::Delim && it->delim() == '!') { - important = true; - valueEnd = it; - } - } - - CSSTokenStream value(valueBegin, valueEnd); - return consumeDeclaractionValue(value, properties, id, important); -} - -bool CSSParser::consumeDeclaractionValue(CSSTokenStream& input, CSSPropertyList& properties, CSSPropertyID id, bool important) -{ - if(input->type() == CSSToken::Type::Ident) { - if(equals(input->data(), "inherit", false)) { - input.consumeIncludingWhitespace(); - if(!input.empty()) - return false; - properties.emplace_back(id, important, CSSInheritValue::create()); - return true; - } - - if(equals(input->data(), "initial", false)) { - input.consumeIncludingWhitespace(); - if(!input.empty()) - return false; - properties.emplace_back(id, important, CSSInitialValue::create()); - return true; - } - } - - auto value = consumeValue(input, id); - input.consumeWhitespace(); - if(value == nullptr || !input.empty()) - return false; - properties.emplace_back(id, important, value); - return true; -} - -struct idententry_t { - std::string_view name; - CSSValueID value; -}; - -template -inline CSSValueID matchIdent(const CSSTokenStream& input, const idententry_t(&table)[N]) -{ - if(input->type() != CSSToken::Type::Ident) - return CSSValueID::Unknown; - - auto name = input->data(); - for(auto& entry : table) { - if(equals(name, entry.name, false)) - return entry.value; - } - - return CSSValueID::Unknown; -} - -template -inline RefPtr consumeIdent(CSSTokenStream& input, const idententry_t(&table)[N]) -{ - auto id = matchIdent(input, table); - if(id == CSSValueID::Unknown) - return nullptr; - input.consumeIncludingWhitespace(); - return CSSIdentValue::create(id); -} - -RefPtr CSSParser::consumeNone(CSSTokenStream& input) -{ - if(input->type() == CSSToken::Type::Ident && equals(input->data(), "none", false)) { - input.consumeIncludingWhitespace(); - return CSSIdentValue::create(CSSValueID::None); - } - - return nullptr; -} - -RefPtr CSSParser::consumeNormal(CSSTokenStream& input) -{ - if(input->type() == CSSToken::Type::Ident && equals(input->data(), "normal", false)) { - input.consumeIncludingWhitespace(); - return CSSIdentValue::create(CSSValueID::Normal); - } - - return nullptr; -} - -RefPtr CSSParser::consumePercent(CSSTokenStream& input, bool negative) -{ - if(input->type() != CSSToken::Type::Percentage || (input->number() < 0 && !negative)) - return nullptr; - - auto value = input->number(); - input.consumeIncludingWhitespace(); - return CSSPercentValue::create(value); -} - -RefPtr CSSParser::consumeNumber(CSSTokenStream& input, bool negative) -{ - if(input->type() != CSSToken::Type::Number || (input->number() < 0 && !negative)) - return nullptr; - - auto value = input->number(); - input.consumeIncludingWhitespace(); - return CSSNumberValue::create(value); -} - -RefPtr CSSParser::consumeLength(CSSTokenStream& input, bool negative, bool unitless) -{ - if(input->type() != CSSToken::Type::Dimension && input->type() != CSSToken::Type::Number) - return nullptr; - - auto value = input->number(); - if(value < 0.0 && !negative || (input->type() == CSSToken::Type::Number && !unitless)) - return nullptr; - if(input->type() == CSSToken::Type::Number) { - input.consumeIncludingWhitespace(); - return CSSLengthValue::create(value, CSSLengthValue::Unit::None); - } - - static const struct { - std::string_view name; - CSSLengthValue::Unit value; - } table[] = { - {"em", CSSLengthValue::Unit::Ems}, - {"ex", CSSLengthValue::Unit::Exs}, - {"px", CSSLengthValue::Unit::Pixels}, - {"cm", CSSLengthValue::Unit::Centimeters}, - {"mm", CSSLengthValue::Unit::Millimeters}, - {"in", CSSLengthValue::Unit::Inches}, - {"pt", CSSLengthValue::Unit::Points}, - {"pc", CSSLengthValue::Unit::Picas}, - {"vw", CSSLengthValue::Unit::ViewportWidth}, - {"vh", CSSLengthValue::Unit::ViewportHeight}, - {"vmin", CSSLengthValue::Unit::ViewportMin}, - {"vmax", CSSLengthValue::Unit::ViewportMax}, - {"rem", CSSLengthValue::Unit::Rems}, - {"ch", CSSLengthValue::Unit::Chs} - }; - - auto name = input->data(); - auto it = std::find_if(table, std::end(table), [name](auto& item) { return equals(name, item.name, false); }); - if(it == std::end(table)) - return nullptr; - input.consumeIncludingWhitespace(); - return CSSLengthValue::create(value, it->value); -} - -RefPtr CSSParser::consumeLengthOrNormal(CSSTokenStream& input, bool negative, bool unitless) -{ - if(auto value = consumeNormal(input)) - return value; - return consumeLength(input, negative, unitless); -} - -RefPtr CSSParser::consumeLengthOrPercent(CSSTokenStream& input, bool negative, bool unitless) -{ - auto value = consumeLength(input, negative, unitless); - if(value == nullptr) - return consumePercent(input, negative); - return value; -} - -RefPtr CSSParser::consumeNumberOrPercent(CSSTokenStream& input, bool negative) -{ - auto value = consumeNumber(input, negative); - if(value == nullptr) - return consumePercent(input, negative); - return value; -} - -RefPtr CSSParser::consumeUrl(CSSTokenStream& input) -{ - std::string value; - switch(input->type()) { - case CSSToken::Type::Url: - case CSSToken::Type::String: - value = input->data(); - input.consumeIncludingWhitespace(); - break; - case CSSToken::Type::Function: { - if(!equals(input->data(), "url", false)) - return nullptr; - CSSTokenStreamGuard guard(input); - auto block = input.consumeBlock(); - block.consumeWhitespace(); - value = block->data(); - block.consumeIncludingWhitespace(); - if(!block.empty()) - return nullptr; - input.consumeWhitespace(); - guard.release(); - break; - } - - default: - return nullptr; - } - - return CSSUrlValue::create(std::move(value)); -} - -RefPtr CSSParser::consumeUrlOrNone(CSSTokenStream& input) -{ - if(auto value = consumeNone(input)) - return value; - return consumeUrl(input); -} - -RefPtr CSSParser::consumeColor(CSSTokenStream& input) -{ - if(input->type() == CSSToken::Type::Hash) { - int count = 0; - uint32_t value = 0; - for(auto cc : input->data()) { - if(count >= 6 || !isxdigit(cc)) - return nullptr; - value = value * 16 + xdigit(cc); - count += 1; - } - - if(count != 6 && count != 3) - return nullptr; - if(count == 3) { - value = ((value&0xf00) << 8) | ((value&0x0f0) << 4) | (value&0x00f); - value |= value << 4; - } - - input.consumeIncludingWhitespace(); - return CSSColorValue::create(value | 0xFF000000); - } - - if(input->type() == CSSToken::Type::Function) { - auto name = input->data(); - if(equals(name, "rgb", false) || equals(name, "rgba", false)) - return consumeRgb(input); - return nullptr; - } - - if(input->type() == CSSToken::Type::Ident) { - auto name = input->data(); - if(equals(name, "currentcolor", false)) { - input.consumeIncludingWhitespace(); - return CSSIdentValue::create(CSSValueID::CurrentColor); - } - - if(equals(name, "transparent", false)) { - input.consumeIncludingWhitespace(); - return CSSColorValue::create(0x00000000); - } - - static const struct { - std::string_view name; - uint32_t value; - } table[] = { - {"aliceblue", 0xF0F8FF}, - {"antiquewhite", 0xFAEBD7}, - {"aqua", 0x00FFFF}, - {"aquamarine", 0x7FFFD4}, - {"azure", 0xF0FFFF}, - {"beige", 0xF5F5DC}, - {"bisque", 0xFFE4C4}, - {"black", 0x000000}, - {"blanchedalmond", 0xFFEBCD}, - {"blue", 0x0000FF}, - {"blueviolet", 0x8A2BE2}, - {"brown", 0xA52A2A}, - {"burlywood", 0xDEB887}, - {"cadetblue", 0x5F9EA0}, - {"chartreuse", 0x7FFF00}, - {"chocolate", 0xD2691E}, - {"coral", 0xFF7F50}, - {"cornflowerblue", 0x6495ED}, - {"cornsilk", 0xFFF8DC}, - {"crimson", 0xDC143C}, - {"cyan", 0x00FFFF}, - {"darkblue", 0x00008B}, - {"darkcyan", 0x008B8B}, - {"darkgoldenrod", 0xB8860B}, - {"darkgray", 0xA9A9A9}, - {"darkgreen", 0x006400}, - {"darkgrey", 0xA9A9A9}, - {"darkkhaki", 0xBDB76B}, - {"darkmagenta", 0x8B008B}, - {"darkolivegreen", 0x556B2F}, - {"darkorange", 0xFF8C00}, - {"darkorchid", 0x9932CC}, - {"darkred", 0x8B0000}, - {"darksalmon", 0xE9967A}, - {"darkseagreen", 0x8FBC8F}, - {"darkslateblue", 0x483D8B}, - {"darkslategray", 0x2F4F4F}, - {"darkslategrey", 0x2F4F4F}, - {"darkturquoise", 0x00CED1}, - {"darkviolet", 0x9400D3}, - {"deeppink", 0xFF1493}, - {"deepskyblue", 0x00BFFF}, - {"dimgray", 0x696969}, - {"dimgrey", 0x696969}, - {"dodgerblue", 0x1E90FF}, - {"firebrick", 0xB22222}, - {"floralwhite", 0xFFFAF0}, - {"forestgreen", 0x228B22}, - {"fuchsia", 0xFF00FF}, - {"gainsboro", 0xDCDCDC}, - {"ghostwhite", 0xF8F8FF}, - {"gold", 0xFFD700}, - {"goldenrod", 0xDAA520}, - {"gray", 0x808080}, - {"green", 0x008000}, - {"greenyellow", 0xADFF2F}, - {"grey", 0x808080}, - {"honeydew", 0xF0FFF0}, - {"hotpink", 0xFF69B4}, - {"indianred", 0xCD5C5C}, - {"indigo", 0x4B0082}, - {"ivory", 0xFFFFF0}, - {"khaki", 0xF0E68C}, - {"lavender", 0xE6E6FA}, - {"lavenderblush", 0xFFF0F5}, - {"lawngreen", 0x7CFC00}, - {"lemonchiffon", 0xFFFACD}, - {"lightblue", 0xADD8E6}, - {"lightcoral", 0xF08080}, - {"lightcyan", 0xE0FFFF}, - {"lightgoldenrodyellow", 0xFAFAD2}, - {"lightgray", 0xD3D3D3}, - {"lightgreen", 0x90EE90}, - {"lightgrey", 0xD3D3D3}, - {"lightpink", 0xFFB6C1}, - {"lightsalmon", 0xFFA07A}, - {"lightseagreen", 0x20B2AA}, - {"lightskyblue", 0x87CEFA}, - {"lightslategray", 0x778899}, - {"lightslategrey", 0x778899}, - {"lightsteelblue", 0xB0C4DE}, - {"lightyellow", 0xFFFFE0}, - {"lime", 0x00FF00}, - {"limegreen", 0x32CD32}, - {"linen", 0xFAF0E6}, - {"magenta", 0xFF00FF}, - {"maroon", 0x800000}, - {"mediumaquamarine", 0x66CDAA}, - {"mediumblue", 0x0000CD}, - {"mediumorchid", 0xBA55D3}, - {"mediumpurple", 0x9370DB}, - {"mediumseagreen", 0x3CB371}, - {"mediumslateblue", 0x7B68EE}, - {"mediumspringgreen", 0x00FA9A}, - {"mediumturquoise", 0x48D1CC}, - {"mediumvioletred", 0xC71585}, - {"midnightblue", 0x191970}, - {"mintcream", 0xF5FFFA}, - {"mistyrose", 0xFFE4E1}, - {"moccasin", 0xFFE4B5}, - {"navajowhite", 0xFFDEAD}, - {"navy", 0x000080}, - {"oldlace", 0xFDF5E6}, - {"olive", 0x808000}, - {"olivedrab", 0x6B8E23}, - {"orange", 0xFFA500}, - {"orangered", 0xFF4500}, - {"orchid", 0xDA70D6}, - {"palegoldenrod", 0xEEE8AA}, - {"palegreen", 0x98FB98}, - {"paleturquoise", 0xAFEEEE}, - {"palevioletred", 0xDB7093}, - {"papayawhip", 0xFFEFD5}, - {"peachpuff", 0xFFDAB9}, - {"peru", 0xCD853F}, - {"pink", 0xFFC0CB}, - {"plum", 0xDDA0DD}, - {"powderblue", 0xB0E0E6}, - {"purple", 0x800080}, - {"rebeccapurple", 0x663399}, - {"red", 0xFF0000}, - {"rosybrown", 0xBC8F8F}, - {"royalblue", 0x4169E1}, - {"saddlebrown", 0x8B4513}, - {"salmon", 0xFA8072}, - {"sandybrown", 0xF4A460}, - {"seagreen", 0x2E8B57}, - {"seashell", 0xFFF5EE}, - {"sienna", 0xA0522D}, - {"silver", 0xC0C0C0}, - {"skyblue", 0x87CEEB}, - {"slateblue", 0x6A5ACD}, - {"slategray", 0x708090}, - {"slategrey", 0x708090}, - {"snow", 0xFFFAFA}, - {"springgreen", 0x00FF7F}, - {"steelblue", 0x4682B4}, - {"tan", 0xD2B48C}, - {"teal", 0x008080}, - {"thistle", 0xD8BFD8}, - {"tomato", 0xFF6347}, - {"turquoise", 0x40E0D0}, - {"violet", 0xEE82EE}, - {"wheat", 0xF5DEB3}, - {"white", 0xFFFFFF}, - {"whitesmoke", 0xF5F5F5}, - {"yellow", 0xFFFF00}, - {"yellowgreen", 0x9ACD32} - }; - - 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 nullptr; - input.consumeIncludingWhitespace(); - return CSSColorValue::create(it->value | 0xFF000000); - } - - return nullptr; -} - -inline bool consumeRgbComponent(CSSTokenStream& input, int& component) -{ - if(input->type() != CSSToken::Type::Number - && input->type() != CSSToken::Type::Percentage) { - return false; - } - - auto value = input->number(); - if(input->type() == CSSToken::Type::Percentage) - value *= 2.55; - value = std::clamp(value, 0.0, 255.0); - component = static_cast(std::round(value)); - input.consumeIncludingWhitespace(); - return true; -} - -RefPtr CSSParser::consumeRgb(CSSTokenStream& input) -{ - assert(input->type() == CSSToken::Type::Function); - CSSTokenStreamGuard guard(input); - auto block = input.consumeBlock(); - block.consumeWhitespace(); - - int red = 0; - if(!consumeRgbComponent(block, red)) - return nullptr; - - if(block->type() != CSSToken::Type::Comma) - return nullptr; - - int blue = 0; - block.consumeIncludingWhitespace(); - if(!consumeRgbComponent(block, blue)) - return nullptr; - - if(block->type() != CSSToken::Type::Comma) - return nullptr; - - int green = 0; - block.consumeIncludingWhitespace(); - if(!consumeRgbComponent(block, green)) - return nullptr; - - int alpha = 255; - if(block->type() == CSSToken::Type::Comma) { - block.consumeIncludingWhitespace(); - if(block->type() != CSSToken::Type::Number - && block->type() != CSSToken::Type::Percentage) { - return nullptr; - } - - auto value = block->number(); - if(block->type() == CSSToken::Type::Percentage) - value /= 100.0; - value = std::clamp(value, 0.0, 1.0); - alpha = static_cast(std::round(value * 255.0)); - block.consumeIncludingWhitespace(); - } - - if(!block.empty()) - return nullptr; - input.consumeWhitespace(); - guard.release(); - return CSSColorValue::create(red, green, blue, alpha); -} - -RefPtr CSSParser::consumeFillOrStroke(CSSTokenStream& input) -{ - if(auto value = consumeNone(input)) - return value; - - auto first = consumeUrl(input); - if(first == nullptr) - return consumeColor(input); - - auto second = consumeNone(input); - if(second == nullptr) - second = consumeColor(input); - if(second == nullptr) - return first; - return CSSPairValue::create(first, second); -} - -RefPtr CSSParser::consumeDashList(CSSTokenStream& input) -{ - if(auto value = consumeNone(input)) - return value; - - CSSValueList values; - auto value = consumeLengthOrPercent(input, false, true); - if(value == nullptr) - return nullptr; - - values.push_back(value); - while(input->type() == CSSToken::Type::Comma) { - input.consumeIncludingWhitespace(); - auto value = consumeLengthOrPercent(input, false, true); - if(value == nullptr) - return nullptr; - values.push_back(value); - } - - if(!input.empty()) - return nullptr; - return CSSListValue::create(std::move(values)); -} - -RefPtr CSSParser::consumeFontWeight(CSSTokenStream& input) -{ - static const idententry_t table[] = { - {"normal", CSSValueID::Normal}, - {"bold", CSSValueID::Bold}, - {"bolder", CSSValueID::Bolder}, - {"lighter", CSSValueID::Lighter} - }; - - if(auto value = consumeIdent(input, table)) - return value; - - if(input->type() != CSSToken::Type::Number || input->numberType() != CSSToken::NumberType::Integer) - return nullptr; - auto value = input->integer(); - if(value < 1 || value > 1000) - return nullptr; - input.consumeIncludingWhitespace(); - return CSSIntegerValue::create(value); -} - -RefPtr CSSParser::consumeFontSize(CSSTokenStream& input, bool unitless) -{ - static const idententry_t table[] = { - {"xx-small", CSSValueID::XxSmall}, - {"x-small", CSSValueID::XSmall}, - {"small", CSSValueID::Small}, - {"medium", CSSValueID::Medium}, - {"large", CSSValueID::Large}, - {"x-large", CSSValueID::XLarge}, - {"xx-large", CSSValueID::XxLarge}, - {"xxx-large", CSSValueID::XxxLarge}, - {"smaller", CSSValueID::Smaller}, - {"larger", CSSValueID::Larger} - }; - - if(auto value = consumeIdent(input, table)) - return value; - return consumeLengthOrPercent(input, false, unitless); -} - -RefPtr CSSParser::consumeFontFamilyValue(CSSTokenStream& input) -{ - if(input->type() == CSSToken::Type::String) { - std::string value(input->data()); - input.consumeIncludingWhitespace(); - return CSSStringValue::create(std::move(value)); - } - - std::string value; - while(input->type() == CSSToken::Type::Ident) { - if(!value.empty()) - value += ' '; - value += input->data(); - input.consumeIncludingWhitespace(); - } - - if(value.empty()) - return nullptr; - return CSSStringValue::create(std::move(value)); -} - -RefPtr CSSParser::consumeFontFamily(CSSTokenStream& input) -{ - CSSValueList values; - while(!input.empty()) { - auto value = consumeFontFamilyValue(input); - if(value == nullptr) - return nullptr; - values.push_back(value); - } - - return CSSListValue::create(std::move(values)); -} - -RefPtr CSSParser::consumeValue(CSSTokenStream& input, CSSPropertyID id) -{ - switch(id) { - case CSSPropertyID::Stroke_Miterlimit: - return consumeNumber(input, false); - case CSSPropertyID::Stroke_Dashoffset: - return consumeLengthOrPercent(input, true, true); - case CSSPropertyID::Stroke_Width: - return consumeLengthOrPercent(input, false, true); - case CSSPropertyID::Letter_Spacing: - case CSSPropertyID::Word_Spacing: - return consumeLengthOrNormal(input, true, true); - case CSSPropertyID::Opacity: - case CSSPropertyID::Fill_Opacity: - case CSSPropertyID::Stroke_Opacity: - case CSSPropertyID::Stop_Opacity: - case CSSPropertyID::Solid_Opacity: - return consumeNumberOrPercent(input, false); - case CSSPropertyID::Stroke_Dasharray: - return consumeDashList(input); - case CSSPropertyID::Clip_Path: - case CSSPropertyID::Marker_End: - case CSSPropertyID::Marker_Mid: - case CSSPropertyID::Marker_Start: - case CSSPropertyID::Mask: - return consumeUrlOrNone(input); - case CSSPropertyID::Color: - case CSSPropertyID::Stop_Color: - case CSSPropertyID::Solid_Color: - return consumeColor(input); - case CSSPropertyID::Fill: - case CSSPropertyID::Stroke: - return consumeFillOrStroke(input); - case CSSPropertyID::Font_Weight: - return consumeFontWeight(input); - case CSSPropertyID::Font_Size: - return consumeFontSize(input, true); - case CSSPropertyID::Font_Family: - return consumeFontFamily(input); - case CSSPropertyID::Font_Style: { - static const idententry_t table[] = { - {"normal", CSSValueID::Normal}, - {"italic", CSSValueID::Italic}, - {"oblique", CSSValueID::Oblique} - }; - - return consumeIdent(input, table); - } - - case CSSPropertyID::Font_Variant: { - static const idententry_t table[] = { - {"normal", CSSValueID::Normal}, - {"small-caps", CSSValueID::SmallCaps} - }; - - return consumeIdent(input, table); - } - - case CSSPropertyID::Fill_Rule: - case CSSPropertyID::Clip_Rule: { - static const idententry_t table[] = { - {"nonzero", CSSValueID::Nonzero}, - {"evenodd", CSSValueID::Evenodd} - }; - - return consumeIdent(input, table); - } - - case CSSPropertyID::Overflow: { - static const idententry_t table[] = { - {"auto", CSSValueID::Auto}, - {"visible", CSSValueID::Visible}, - {"hidden", CSSValueID::Hidden} - }; - - return consumeIdent(input, table); - } - - case CSSPropertyID::Stroke_Linecap: { - static const idententry_t table[] = { - {"butt", CSSValueID::Butt}, - {"round", CSSValueID::Round}, - {"square", CSSValueID::Square} - }; - - return consumeIdent(input, table); - } - - case CSSPropertyID::Stroke_Linejoin: { - static const idententry_t table[] = { - {"miter", CSSValueID::Miter}, - {"round", CSSValueID::Round}, - {"bevel", CSSValueID::Bevel} - }; - - return consumeIdent(input, table); - } - - case CSSPropertyID::Text_Anchor: { - static const idententry_t table[] = { - {"start", CSSValueID::Start}, - {"middle", CSSValueID::Middle}, - {"end", CSSValueID::End} - }; - - return consumeIdent(input, table); - } - - case CSSPropertyID::Visibility: { - static const idententry_t table[] = { - {"visible", CSSValueID::Visible}, - {"hidden", CSSValueID::Hidden}, - {"collapse", CSSValueID::Collapse} - }; - - return consumeIdent(input, table); - } - - case CSSPropertyID::Display: { - static const idententry_t table[] = { - {"none", CSSValueID::None}, - {"inline", CSSValueID::Inline} - }; - - return consumeIdent(input, table); - } - - default: - return nullptr; - } -} - -} // namespace lunasvg diff --git a/source/cssparser.h b/source/cssparser.h deleted file mode 100644 index df3a72290..000000000 --- a/source/cssparser.h +++ /dev/null @@ -1,305 +0,0 @@ -#ifndef CSSPARSER_H -#define CSSPARSER_H - -#include "parserutils.h" -#include "cssstylesheet.h" - -#include - -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(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; - -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; - 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 consumeNone(CSSTokenStream& input); - static RefPtr consumeNormal(CSSTokenStream& input); - - static RefPtr consumePercent(CSSTokenStream& input, bool negative); - static RefPtr consumeNumber(CSSTokenStream& input, bool negative); - static RefPtr consumeLength(CSSTokenStream& input, bool negative, bool unitless); - static RefPtr consumeLengthOrNormal(CSSTokenStream& input, bool negative, bool unitless); - static RefPtr consumeLengthOrPercent(CSSTokenStream& input, bool negative, bool unitless); - static RefPtr consumeNumberOrPercent(CSSTokenStream& input, bool negative); - - static RefPtr consumeUrl(CSSTokenStream& input); - static RefPtr consumeUrlOrNone(CSSTokenStream& input); - static RefPtr consumeColor(CSSTokenStream& input); - static RefPtr consumeRgb(CSSTokenStream& input); - static RefPtr consumeFillOrStroke(CSSTokenStream& input); - static RefPtr consumeDashList(CSSTokenStream& input); - - static RefPtr consumeFontWeight(CSSTokenStream& input); - static RefPtr consumeFontSize(CSSTokenStream& input, bool unitless); - static RefPtr consumeFontFamilyValue(CSSTokenStream& input); - static RefPtr consumeFontFamily(CSSTokenStream& input); - - static RefPtr consumeValue(CSSTokenStream& input, CSSPropertyID id); -}; - -} // namespace lunasvg - -#endif // CSSPARSER_H diff --git a/source/cssstylesheet.cpp b/source/cssstylesheet.cpp deleted file mode 100644 index 9ae7d4a86..000000000 --- a/source/cssstylesheet.cpp +++ /dev/null @@ -1,378 +0,0 @@ -#include "cssstylesheet.h" -#include "cssparser.h" - -#include - -namespace lunasvg { - -RefPtr CSSInitialValue::create() -{ - static auto value = adoptPtr(new CSSInitialValue); - return value; -} - -RefPtr CSSInheritValue::create() -{ - static auto value = adoptPtr(new CSSInheritValue); - return value; -} - -RefPtr CSSIdentValue::create(CSSValueID value) -{ - static std::map> 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::create(int value) -{ - return adoptPtr(new CSSIntegerValue(value)); -} - -RefPtr CSSNumberValue::create(double value) -{ - return adoptPtr(new CSSNumberValue(value)); -} - -RefPtr CSSPercentValue::create(double value) -{ - return adoptPtr(new CSSPercentValue(value)); -} - -RefPtr CSSLengthValue::create(double value, Unit unit) -{ - return adoptPtr(new CSSLengthValue(value, unit)); -} - -RefPtr CSSStringValue::create(std::string value) -{ - return adoptPtr(new CSSStringValue(std::move(value))); -} - -RefPtr CSSUrlValue::create(std::string value) -{ - return adoptPtr(new CSSUrlValue(std::move(value))); -} - -RefPtr CSSColorValue::create(uint32_t value) -{ - return adoptPtr(new CSSColorValue(value)); -} - -RefPtr 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::create(RefPtr first, RefPtr second) -{ - return adoptPtr(new CSSPairValue(std::move(first), std::move(second))); -} - -RefPtr 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::create(CSSSelectorList selectors, CSSPropertyList properties) -{ - return std::unique_ptr(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 diff --git a/source/cssstylesheet.h b/source/cssstylesheet.h deleted file mode 100644 index 06f5ef258..000000000 --- a/source/cssstylesheet.h +++ /dev/null @@ -1,495 +0,0 @@ -#ifndef CSSSTYLESHEET_H -#define CSSSTYLESHEET_H - -#include "element.h" -#include "pointer.h" - -#include -#include - -namespace lunasvg { - -class CSSValue : public RefCounted { -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>; - -class CSSInitialValue final : public CSSValue { -public: - static RefPtr create(); - - bool isInitialValue() const final { return true; } - -private: - CSSInitialValue() = default; -}; - -template<> -struct is { - static bool check(const CSSValue& value) { return value.isInitialValue(); } -}; - -class CSSInheritValue final : public CSSValue { -public: - static RefPtr create(); - - bool isInheritValue() const final { return true; } - -private: - CSSInheritValue() = default; -}; - -template<> -struct is { - 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 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 { - static bool check(const CSSValue& value) { return value.isIdentValue(); } -}; - -class CSSIntegerValue final : public CSSValue { -public: - static RefPtr 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 { - static bool check(const CSSValue& value) { return value.isIntegerValue(); } -}; - -class CSSNumberValue final : public CSSValue { -public: - static RefPtr 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 { - static bool check(const CSSValue& value) { return value.isNumberValue(); } -}; - -class CSSPercentValue final : public CSSValue { -public: - static RefPtr 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 { - 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 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 { - static bool check(const CSSValue& value) { return value.isLengthValue(); } -}; - -class CSSStringValue final : public CSSValue { -public: - static RefPtr 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 { - static bool check(const CSSValue& value) { return value.isStringValue(); } -}; - -class CSSUrlValue final : public CSSValue { -public: - static RefPtr 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 { - static bool check(const CSSValue& value) { return value.isUrlValue(); } -}; - -class CSSColorValue final : public CSSValue { -public: - static RefPtr create(uint32_t value); - static RefPtr 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 { - static bool check(const CSSValue& value) { return value.isColorValue(); } -}; - -class CSSPairValue final : public CSSValue { -public: - static RefPtr create(RefPtr first, RefPtr second); - - const RefPtr& first() const { return m_first; } - const RefPtr& second() const { return m_second; } - bool isPairValue() const final { return true; } - -private: - CSSPairValue(RefPtr first, RefPtr second) - : m_first(first), m_second(second) - {} - - RefPtr m_first; - RefPtr m_second; -}; - -template<> -struct is { - static bool check(const CSSValue& value) { return value.isPairValue(); } -}; - -class CSSListValue : public CSSValue { -public: - static RefPtr create(CSSValueList values); - - size_t length() const { return m_values.size(); } - const RefPtr& front() const { return m_values.front(); } - const RefPtr& back() const { return m_values.back(); } - const RefPtr& 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 value) - : m_id(id), m_important(important), m_value(value) - {} - - CSSPropertyID id() const { return m_id; } - bool important() const { return m_important; } - const RefPtr& value() const { return m_value; } - -private: - CSSPropertyID m_id; - bool m_important; - RefPtr m_value; -}; - -using CSSPropertyList = std::vector; -using CSSPropertyMap = std::map; - -struct CSSSimpleSelector; - -using CSSSelector = std::vector; -using CSSSelectorList = std::vector; - -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; - - 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 attributes; - std::vector pseudos; -}; - -class CSSRule { -public: - static std::unique_ptr 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>; - -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; - -class ComputedStyle; - -class CSSStyleSheet { -public: - CSSStyleSheet() = default; - - void parse(const std::string_view& content); - bool empty() const { return m_ruleList.empty(); } - - RefPtr styleForElement(const Element* element, const RefPtr& parentStyle) const; - -private: - CSSRuleList m_ruleList; - CSSRuleSet m_ruleSet; -}; - -} // namespace lunasvg - -#endif // CSSSTYLESHEET_H diff --git a/source/defselement.cpp b/source/defselement.cpp index 9402561d0..b39ddeac9 100644 --- a/source/defselement.cpp +++ b/source/defselement.cpp @@ -3,7 +3,7 @@ namespace lunasvg { DefsElement::DefsElement() - : GraphicsElement(ElementID::Defs) + : GraphicsElement(ElementId::Defs) { } diff --git a/source/element.cpp b/source/element.cpp index c4cc2999d..a3d3a319a 100644 --- a/source/element.cpp +++ b/source/element.cpp @@ -2,146 +2,9 @@ #include "parser.h" #include "svgelement.h" -#include - namespace lunasvg { -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) +void PropertyList::set(PropertyId id, const std::string& value, int specificity) { auto property = get(id); if(property == nullptr) @@ -158,7 +21,7 @@ void PropertyList::set(PropertyID id, const std::string& value, int specificity) property->value = value; } -Property* PropertyList::get(PropertyID id) const +Property* PropertyList::get(PropertyId id) const { auto data = m_properties.data(); auto end = data + m_properties.size(); @@ -196,19 +59,19 @@ std::unique_ptr TextNode::clone() const return std::move(node); } -Element::Element(ElementID id) +Element::Element(ElementId id) : id(id) { } -void Element::set(PropertyID id, const std::string& value, int specificity) +void Element::set(PropertyId id, const std::string& value, int specificity) { properties.set(id, value, specificity); } static const std::string EmptyString; -const std::string& Element::get(PropertyID id) const +const std::string& Element::get(PropertyId id) const { auto property = properties.get(id); if(property == nullptr) @@ -219,7 +82,7 @@ const std::string& Element::get(PropertyID id) const static const std::string InheritString{"inherit"}; -const std::string& Element::find(PropertyID id) const +const std::string& Element::find(PropertyId id) const { auto element = this; do { @@ -232,12 +95,12 @@ const std::string& Element::find(PropertyID id) const return EmptyString; } -bool Element::has(PropertyID id) const +bool Element::has(PropertyId id) const { return properties.get(id); } -Element* Element::previousElement() const +Element* Element::previousSibling() const { if(parent == nullptr) return nullptr; @@ -259,7 +122,7 @@ Element* Element::previousElement() const return nullptr; } -Element* Element::nextElement() const +Element* Element::nextSibling() const { if(parent == nullptr) return nullptr; @@ -299,15 +162,15 @@ Rect Element::currentViewport() const if(parent == nullptr) { auto element = static_cast(this); - if(element->has(PropertyID::ViewBox)) + if(element->has(PropertyId::ViewBox)) return element->viewBox(); return Rect{0, 0, 300, 150}; } - if(parent->id == ElementID::Svg) + if(parent->id == ElementId::Svg) { auto element = static_cast(parent); - if(element->has(PropertyID::ViewBox)) + if(element->has(PropertyId::ViewBox)) return element->viewBox(); LengthContext lengthContext(element); diff --git a/source/element.h b/source/element.h index d7e2bb834..f94f92774 100644 --- a/source/element.h +++ b/source/element.h @@ -8,16 +8,15 @@ namespace lunasvg { -enum class ElementID { +enum class ElementId +{ Unknown = 0, Star, - A, Circle, ClipPath, Defs, Ellipse, G, - Image, Line, LinearGradient, Marker, @@ -32,16 +31,12 @@ enum class ElementID { Stop, Style, Svg, - Switch, Symbol, - Text, - TextPath, - Tref, - Tspan, Use }; -enum class PropertyID { +enum class PropertyId +{ Unknown = 0, Class, Clip_Path, @@ -51,17 +46,10 @@ enum class PropertyID { Cx, Cy, D, - Dx, - Dy, Display, Fill, Fill_Opacity, Fill_Rule, - Font_Family, - Font_Size, - Font_Style, - Font_Variant, - Font_Weight, Fx, Fy, GradientTransform, @@ -69,7 +57,6 @@ enum class PropertyID { Height, Href, Id, - Letter_Spacing, Marker_End, Marker_Mid, Marker_Start, @@ -91,13 +78,11 @@ enum class PropertyID { R, RefX, RefY, - Rotate, Rx, Ry, Solid_Color, Solid_Opacity, SpreadMethod, - StartOffset, Stop_Color, Stop_Opacity, Stroke, @@ -109,13 +94,10 @@ enum class PropertyID { Stroke_Opacity, Stroke_Width, Style, - Text_Anchor, - Text_Decoration, Transform, ViewBox, Visibility, Width, - Word_Spacing, X, X1, X2, @@ -124,12 +106,9 @@ enum class PropertyID { Y2 }; -ElementID elementid(const std::string_view& name); -PropertyID propertyid(const std::string_view& name); - struct Property { - PropertyID id; + PropertyId id; std::string value; int specificity; }; @@ -139,11 +118,10 @@ class PropertyList public: PropertyList() = default; - void set(PropertyID id, const std::string& value, int specificity); - Property* get(PropertyID id) const; + void set(PropertyId id, const std::string& value, int specificity); + Property* get(PropertyId id) const; void add(const Property& property); void add(const PropertyList& properties); - void clear() { m_properties.clear(); } private: std::vector m_properties; @@ -181,22 +159,20 @@ public: std::string text; }; -using Attribute = std::pair; -using AttributeList = std::vector; using NodeList = std::list>; class Element : public Node { public: - Element(ElementID id); + Element(ElementId id); - void set(PropertyID id, const std::string& value, int specificity); - const std::string& get(PropertyID id) const; - const std::string& find(PropertyID id) const; - bool has(PropertyID id) const; + void set(PropertyId id, const std::string& value, int specificity); + const std::string& get(PropertyId id) const; + const std::string& find(PropertyId id) const; + bool has(PropertyId id) const; - Element* previousElement() const; - Element* nextElement() const; + Element* previousSibling() const; + Element* nextSibling() const; Node* addChild(std::unique_ptr child); void layoutChildren(LayoutContext* context, LayoutContainer* current) const; Rect currentViewport() const; @@ -232,7 +208,7 @@ public: } public: - ElementID id; + ElementId id; NodeList children; PropertyList properties; }; diff --git a/source/gelement.cpp b/source/gelement.cpp index 2dc805b94..b0f5c84a7 100644 --- a/source/gelement.cpp +++ b/source/gelement.cpp @@ -4,7 +4,7 @@ namespace lunasvg { GElement::GElement() - : GraphicsElement(ElementID::G) + : GraphicsElement(ElementId::G) { } diff --git a/source/geometryelement.cpp b/source/geometryelement.cpp index 6f848ac72..c93b6d52b 100644 --- a/source/geometryelement.cpp +++ b/source/geometryelement.cpp @@ -6,7 +6,7 @@ namespace lunasvg { -GeometryElement::GeometryElement(ElementID id) +GeometryElement::GeometryElement(ElementId id) : GraphicsElement(id) { } @@ -35,13 +35,13 @@ void GeometryElement::layout(LayoutContext* context, LayoutContainer* current) c } PathElement::PathElement() - : GeometryElement(ElementID::Path) + : GeometryElement(ElementId::Path) { } Path PathElement::d() const { - auto& value = get(PropertyID::D); + auto& value = get(PropertyId::D); return Parser::parsePath(value); } @@ -55,19 +55,19 @@ std::unique_ptr PathElement::clone() const return cloneElement(); } -PolyElement::PolyElement(ElementID id) +PolyElement::PolyElement(ElementId id) : GeometryElement(id) { } PointList PolyElement::points() const { - auto& value = get(PropertyID::Points); + auto& value = get(PropertyId::Points); return Parser::parsePointList(value); } PolygonElement::PolygonElement() - : PolyElement(ElementID::Polygon) + : PolyElement(ElementId::Polygon) { } @@ -92,7 +92,7 @@ std::unique_ptr PolygonElement::clone() const } PolylineElement::PolylineElement() - : PolyElement(ElementID::Polyline) + : PolyElement(ElementId::Polyline) { } @@ -116,25 +116,25 @@ std::unique_ptr PolylineElement::clone() const } CircleElement::CircleElement() - : GeometryElement(ElementID::Circle) + : GeometryElement(ElementId::Circle) { } Length CircleElement::cx() const { - auto& value = get(PropertyID::Cx); + auto& value = get(PropertyId::Cx); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length CircleElement::cy() const { - auto& value = get(PropertyID::Cy); + auto& value = get(PropertyId::Cy); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length CircleElement::r() const { - auto& value = get(PropertyID::R); + auto& value = get(PropertyId::R); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } @@ -160,31 +160,31 @@ std::unique_ptr CircleElement::clone() const } EllipseElement::EllipseElement() - : GeometryElement(ElementID::Ellipse) + : GeometryElement(ElementId::Ellipse) { } Length EllipseElement::cx() const { - auto& value = get(PropertyID::Cx); + auto& value = get(PropertyId::Cx); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length EllipseElement::cy() const { - auto& value = get(PropertyID::Cy); + auto& value = get(PropertyId::Cy); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length EllipseElement::rx() const { - auto& value = get(PropertyID::Rx); + auto& value = get(PropertyId::Rx); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } Length EllipseElement::ry() const { - auto& value = get(PropertyID::Ry); + auto& value = get(PropertyId::Ry); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } @@ -212,31 +212,31 @@ std::unique_ptr EllipseElement::clone() const } LineElement::LineElement() - : GeometryElement(ElementID::Line) + : GeometryElement(ElementId::Line) { } Length LineElement::x1() const { - auto& value = get(PropertyID::X1); + auto& value = get(PropertyId::X1); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LineElement::y1() const { - auto& value = get(PropertyID::Y1); + auto& value = get(PropertyId::Y1); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LineElement::x2() const { - auto& value = get(PropertyID::X2); + auto& value = get(PropertyId::X2); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LineElement::y2() const { - auto& value = get(PropertyID::Y2); + auto& value = get(PropertyId::Y2); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } @@ -260,43 +260,43 @@ std::unique_ptr LineElement::clone() const } RectElement::RectElement() - : GeometryElement(ElementID::Rect) + : GeometryElement(ElementId::Rect) { } Length RectElement::x() const { - auto& value = get(PropertyID::X); + auto& value = get(PropertyId::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length RectElement::y() const { - auto& value = get(PropertyID::Y); + auto& value = get(PropertyId::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length RectElement::rx() const { - auto& value = get(PropertyID::Rx); + auto& value = get(PropertyId::Rx); return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown); } Length RectElement::ry() const { - auto& value = get(PropertyID::Ry); + auto& value = get(PropertyId::Ry); return Parser::parseLength(value, ForbidNegativeLengths, Length::Unknown); } Length RectElement::width() const { - auto& value = get(PropertyID::Width); + auto& value = get(PropertyId::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } Length RectElement::height() const { - auto& value = get(PropertyID::Height); + auto& value = get(PropertyId::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } diff --git a/source/geometryelement.h b/source/geometryelement.h index e2f0bb462..8d302ee39 100644 --- a/source/geometryelement.h +++ b/source/geometryelement.h @@ -10,7 +10,7 @@ class LayoutShape; class GeometryElement : public GraphicsElement { public: - GeometryElement(ElementID id); + GeometryElement(ElementId id); bool isGeometry() const { return true; } virtual void layout(LayoutContext* context, LayoutContainer* current) const; @@ -31,7 +31,7 @@ public: class PolyElement : public GeometryElement { public: - PolyElement(ElementID id); + PolyElement(ElementId id); PointList points() const; }; diff --git a/source/graphicselement.cpp b/source/graphicselement.cpp index 9df1a00c3..60c28f955 100644 --- a/source/graphicselement.cpp +++ b/source/graphicselement.cpp @@ -3,14 +3,14 @@ namespace lunasvg { -GraphicsElement::GraphicsElement(ElementID id) +GraphicsElement::GraphicsElement(ElementId id) : StyledElement(id) { } Transform GraphicsElement::transform() const { - auto& value = get(PropertyID::Transform); + auto& value = get(PropertyId::Transform); return Parser::parseTransform(value); } diff --git a/source/graphicselement.h b/source/graphicselement.h index 1c156a04c..4da8ce8ab 100644 --- a/source/graphicselement.h +++ b/source/graphicselement.h @@ -8,7 +8,7 @@ namespace lunasvg { class GraphicsElement : public StyledElement { public: - GraphicsElement(ElementID id); + GraphicsElement(ElementId id); Transform transform() const; }; diff --git a/source/layoutcontext.cpp b/source/layoutcontext.cpp index 24f4d0f1e..5b92c8779 100644 --- a/source/layoutcontext.cpp +++ b/source/layoutcontext.cpp @@ -479,14 +479,14 @@ void RenderState::endGroup(RenderState& state, const BlendInfo& info) state.canvas->blend(canvas.get(), BlendMode::Src_Over, m_mode == RenderMode::Display ? info.opacity : 1.0); } -LayoutContext::LayoutContext(const TreeBuilder* builder, LayoutSymbol* root) - : m_builder(builder), m_root(root) +LayoutContext::LayoutContext(const ParseDocument* document, LayoutSymbol* root) + : m_document(document), m_root(root) { } Element* LayoutContext::getElementById(const std::string& id) const { - return m_builder->getElementById(id); + return m_document->getElementById(id); } LayoutObject* LayoutContext::getResourcesById(const std::string& id) const @@ -517,7 +517,7 @@ LayoutMask* LayoutContext::getMasker(const std::string& id) return static_cast(ref); auto element = getElementById(id); - if(element == nullptr || element->id != ElementID::Mask) + if(element == nullptr || element->id != ElementId::Mask) return nullptr; auto masker = static_cast(element)->getMasker(this); @@ -534,7 +534,7 @@ LayoutClipPath* LayoutContext::getClipper(const std::string& id) return static_cast(ref); auto element = getElementById(id); - if(element == nullptr || element->id != ElementID::ClipPath) + if(element == nullptr || element->id != ElementId::ClipPath) return nullptr; auto clipper = static_cast(element)->getClipper(this); @@ -551,7 +551,7 @@ LayoutMarker* LayoutContext::getMarker(const std::string& id) return static_cast(ref); auto element = getElementById(id); - if(element == nullptr || element->id != ElementID::Marker) + if(element == nullptr || element->id != ElementId::Marker) return nullptr; auto marker = static_cast(element)->getMarker(this); diff --git a/source/layoutcontext.h b/source/layoutcontext.h index ea1aad28f..13c3d3c5a 100644 --- a/source/layoutcontext.h +++ b/source/layoutcontext.h @@ -346,13 +346,14 @@ private: RenderMode m_mode; }; -class TreeBuilder; +class ParseDocument; class StyledElement; class GeometryElement; -class LayoutContext { +class LayoutContext +{ public: - LayoutContext(const TreeBuilder* builder, LayoutSymbol* root); + LayoutContext(const ParseDocument* document, LayoutSymbol* root); Element* getElementById(const std::string& id) const; LayoutObject* getResourcesById(const std::string& id) const; @@ -372,7 +373,7 @@ public: bool hasReference(const Element* element) const; private: - const TreeBuilder* m_builder; + const ParseDocument* m_document; LayoutSymbol* m_root; std::map m_resourcesCache; std::set m_references; diff --git a/source/lunasvg.cpp b/source/lunasvg.cpp index a77cfefb9..2a75aa3c7 100644 --- a/source/lunasvg.cpp +++ b/source/lunasvg.cpp @@ -292,11 +292,11 @@ std::unique_ptr Document::loadFromData(const std::string& string) std::unique_ptr Document::loadFromData(const char* data, std::size_t size) { - TreeBuilder builder; - if(!builder.parse(data, size)) + ParseDocument parser; + if(!parser.parse(data, size)) return nullptr; - auto root = builder.build(); + auto root = parser.layout(); if(!root || root->children.empty()) return nullptr; diff --git a/source/markerelement.cpp b/source/markerelement.cpp index 5a61aff69..262d7b201 100644 --- a/source/markerelement.cpp +++ b/source/markerelement.cpp @@ -5,55 +5,55 @@ namespace lunasvg { MarkerElement::MarkerElement() - : StyledElement(ElementID::Marker) + : StyledElement(ElementId::Marker) { } Length MarkerElement::refX() const { - auto& value = get(PropertyID::RefX); + auto& value = get(PropertyId::RefX); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length MarkerElement::refY() const { - auto& value = get(PropertyID::RefY); + auto& value = get(PropertyId::RefY); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length MarkerElement::markerWidth() const { - auto& value = get(PropertyID::MarkerWidth); + auto& value = get(PropertyId::MarkerWidth); return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent); } Length MarkerElement::markerHeight() const { - auto& value = get(PropertyID::MarkerHeight); + auto& value = get(PropertyId::MarkerHeight); return Parser::parseLength(value, ForbidNegativeLengths, Length::ThreePercent); } Angle MarkerElement::orient() const { - auto& value = get(PropertyID::Orient); + auto& value = get(PropertyId::Orient); return Parser::parseAngle(value); } MarkerUnits MarkerElement::markerUnits() const { - auto& value = get(PropertyID::MarkerUnits); + auto& value = get(PropertyId::MarkerUnits); return Parser::parseMarkerUnits(value); } Rect MarkerElement::viewBox() const { - auto& value = get(PropertyID::ViewBox); + auto& value = get(PropertyId::ViewBox); return Parser::parseViewBox(value); } PreserveAspectRatio MarkerElement::preserveAspectRatio() const { - auto& value = get(PropertyID::PreserveAspectRatio); + auto& value = get(PropertyId::PreserveAspectRatio); return Parser::parsePreserveAspectRatio(value); } diff --git a/source/maskelement.cpp b/source/maskelement.cpp index 0503657e1..da0a7fbe6 100644 --- a/source/maskelement.cpp +++ b/source/maskelement.cpp @@ -5,43 +5,43 @@ namespace lunasvg { MaskElement::MaskElement() - : StyledElement(ElementID::Mask) + : StyledElement(ElementId::Mask) { } Length MaskElement::x() const { - auto& value = get(PropertyID::X); + auto& value = get(PropertyId::X); return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent); } Length MaskElement::y() const { - auto& value = get(PropertyID::Y); + auto& value = get(PropertyId::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::MinusTenPercent); } Length MaskElement::width() const { - auto& value = get(PropertyID::Width); + auto& value = get(PropertyId::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent); } Length MaskElement::height() const { - auto& value = get(PropertyID::Height); + auto& value = get(PropertyId::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::OneTwentyPercent); } Units MaskElement::maskUnits() const { - auto& value = get(PropertyID::MaskUnits); + auto& value = get(PropertyId::MaskUnits); return Parser::parseUnits(value, Units::ObjectBoundingBox); } Units MaskElement::maskContentUnits() const { - auto& value = get(PropertyID::MaskContentUnits); + auto& value = get(PropertyId::MaskContentUnits); return Parser::parseUnits(value, Units::UserSpaceOnUse); } diff --git a/source/paintelement.cpp b/source/paintelement.cpp index 5bb85c199..0e0776e72 100644 --- a/source/paintelement.cpp +++ b/source/paintelement.cpp @@ -7,37 +7,37 @@ namespace lunasvg { -PaintElement::PaintElement(ElementID id) +PaintElement::PaintElement(ElementId id) : StyledElement(id) { } -GradientElement::GradientElement(ElementID id) +GradientElement::GradientElement(ElementId id) : PaintElement(id) { } Transform GradientElement::gradientTransform() const { - auto& value = get(PropertyID::GradientTransform); + auto& value = get(PropertyId::GradientTransform); return Parser::parseTransform(value); } SpreadMethod GradientElement::spreadMethod() const { - auto& value = get(PropertyID::SpreadMethod); + auto& value = get(PropertyId::SpreadMethod); return Parser::parseSpreadMethod(value); } Units GradientElement::gradientUnits() const { - auto& value = get(PropertyID::GradientUnits); + auto& value = get(PropertyId::GradientUnits); return Parser::parseUnits(value, Units::ObjectBoundingBox); } std::string GradientElement::href() const { - auto& value = get(PropertyID::Href); + auto& value = get(PropertyId::Href); return Parser::parseHref(value); } @@ -50,7 +50,7 @@ GradientStops GradientElement::buildGradientStops() const if(child->isText()) continue; auto element = static_cast(child.get()); - if(element->id != ElementID::Stop) + if(element->id != ElementId::Stop) continue; auto stop = static_cast(element); auto offset = std::max(prevOffset, stop->offset()); @@ -62,31 +62,31 @@ GradientStops GradientElement::buildGradientStops() const } LinearGradientElement::LinearGradientElement() - : GradientElement(ElementID::LinearGradient) + : GradientElement(ElementId::LinearGradient) { } Length LinearGradientElement::x1() const { - auto& value = get(PropertyID::X1); + auto& value = get(PropertyId::X1); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LinearGradientElement::y1() const { - auto& value = get(PropertyID::Y1); + auto& value = get(PropertyId::Y1); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length LinearGradientElement::x2() const { - auto& value = get(PropertyID::X2); + auto& value = get(PropertyId::X2); return Parser::parseLength(value, AllowNegativeLengths, Length::HundredPercent); } Length LinearGradientElement::y2() const { - auto& value = get(PropertyID::Y2); + auto& value = get(PropertyId::Y2); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } @@ -98,30 +98,30 @@ std::unique_ptr LinearGradientElement::getPainter(LayoutContext* c while(true) { - if(!attributes.hasGradientTransform() && current->has(PropertyID::GradientTransform)) + if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform)) attributes.setGradientTransform(current->gradientTransform()); - if(!attributes.hasSpreadMethod() && current->has(PropertyID::SpreadMethod)) + if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod)) attributes.setSpreadMethod(current->spreadMethod()); - if(!attributes.hasGradientUnits() && current->has(PropertyID::GradientUnits)) + if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits)) attributes.setGradientUnits(current->gradientUnits()); if(!attributes.hasGradientStops()) attributes.setGradientStops(current->buildGradientStops()); - if(current->id == ElementID::LinearGradient) + if(current->id == ElementId::LinearGradient) { auto element = static_cast(current); - if(!attributes.hasX1() && element->has(PropertyID::X1)) + if(!attributes.hasX1() && element->has(PropertyId::X1)) attributes.setX1(element->x1()); - if(!attributes.hasY1() && element->has(PropertyID::Y1)) + if(!attributes.hasY1() && element->has(PropertyId::Y1)) attributes.setY1(element->y1()); - if(!attributes.hasX2() && element->has(PropertyID::X2)) + if(!attributes.hasX2() && element->has(PropertyId::X2)) attributes.setX2(element->x2()); - if(!attributes.hasY2() && element->has(PropertyID::Y2)) + if(!attributes.hasY2() && element->has(PropertyId::Y2)) attributes.setY2(element->y2()); } auto ref = context->getElementById(current->href()); - if(!ref || !(ref->id == ElementID::LinearGradient || ref->id == ElementID::RadialGradient)) + if(!ref || !(ref->id == ElementId::LinearGradient || ref->id == ElementId::RadialGradient)) break; processedGradients.insert(current); @@ -164,37 +164,37 @@ std::unique_ptr LinearGradientElement::clone() const } RadialGradientElement::RadialGradientElement() - : GradientElement(ElementID::RadialGradient) + : GradientElement(ElementId::RadialGradient) { } Length RadialGradientElement::cx() const { - auto& value = get(PropertyID::Cx); + auto& value = get(PropertyId::Cx); return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent); } Length RadialGradientElement::cy() const { - auto& value = get(PropertyID::Cy); + auto& value = get(PropertyId::Cy); return Parser::parseLength(value, AllowNegativeLengths, Length::FiftyPercent); } Length RadialGradientElement::r() const { - auto& value = get(PropertyID::R); + auto& value = get(PropertyId::R); return Parser::parseLength(value, ForbidNegativeLengths, Length::FiftyPercent); } Length RadialGradientElement::fx() const { - auto& value = get(PropertyID::Fx); + auto& value = get(PropertyId::Fx); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length RadialGradientElement::fy() const { - auto& value = get(PropertyID::Fy); + auto& value = get(PropertyId::Fy); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } @@ -206,32 +206,32 @@ std::unique_ptr RadialGradientElement::getPainter(LayoutContext* c while(true) { - if(!attributes.hasGradientTransform() && current->has(PropertyID::GradientTransform)) + if(!attributes.hasGradientTransform() && current->has(PropertyId::GradientTransform)) attributes.setGradientTransform(current->gradientTransform()); - if(!attributes.hasSpreadMethod() && current->has(PropertyID::SpreadMethod)) + if(!attributes.hasSpreadMethod() && current->has(PropertyId::SpreadMethod)) attributes.setSpreadMethod(current->spreadMethod()); - if(!attributes.hasGradientUnits() && current->has(PropertyID::GradientUnits)) + if(!attributes.hasGradientUnits() && current->has(PropertyId::GradientUnits)) attributes.setGradientUnits(current->gradientUnits()); if(!attributes.hasGradientStops()) attributes.setGradientStops(current->buildGradientStops()); - if(current->id == ElementID::RadialGradient) + if(current->id == ElementId::RadialGradient) { auto element = static_cast(current); - if(!attributes.hasCx() && element->has(PropertyID::Cx)) + if(!attributes.hasCx() && element->has(PropertyId::Cx)) attributes.setCx(element->cx()); - if(!attributes.hasCy() && element->has(PropertyID::Cy)) + if(!attributes.hasCy() && element->has(PropertyId::Cy)) attributes.setCy(element->cy()); - if(!attributes.hasR() && element->has(PropertyID::R)) + if(!attributes.hasR() && element->has(PropertyId::R)) attributes.setR(element->r()); - if(!attributes.hasFx() && element->has(PropertyID::Fx)) + if(!attributes.hasFx() && element->has(PropertyId::Fx)) attributes.setFx(element->fx()); - if(!attributes.hasFy() && element->has(PropertyID::Fy)) + if(!attributes.hasFy() && element->has(PropertyId::Fy)) attributes.setFy(element->fy()); } auto ref = context->getElementById(current->href()); - if(!ref || !(ref->id == ElementID::LinearGradient || ref->id == ElementID::RadialGradient)) + if(!ref || !(ref->id == ElementId::LinearGradient || ref->id == ElementId::RadialGradient)) break; processedGradients.insert(current); @@ -278,67 +278,67 @@ std::unique_ptr RadialGradientElement::clone() const } PatternElement::PatternElement() - : PaintElement(ElementID::Pattern) + : PaintElement(ElementId::Pattern) { } Length PatternElement::x() const { - auto& value = get(PropertyID::X); + auto& value = get(PropertyId::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length PatternElement::y() const { - auto& value = get(PropertyID::Y); + auto& value = get(PropertyId::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length PatternElement::width() const { - auto& value = get(PropertyID::Width); + auto& value = get(PropertyId::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } Length PatternElement::height() const { - auto& value = get(PropertyID::Height); + auto& value = get(PropertyId::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::Zero); } Transform PatternElement::patternTransform() const { - auto& value = get(PropertyID::PatternTransform); + auto& value = get(PropertyId::PatternTransform); return Parser::parseTransform(value); } Units PatternElement::patternUnits() const { - auto& value = get(PropertyID::PatternUnits); + auto& value = get(PropertyId::PatternUnits); return Parser::parseUnits(value, Units::ObjectBoundingBox); } Units PatternElement::patternContentUnits() const { - auto& value = get(PropertyID::PatternContentUnits); + auto& value = get(PropertyId::PatternContentUnits); return Parser::parseUnits(value, Units::UserSpaceOnUse); } Rect PatternElement::viewBox() const { - auto& value = get(PropertyID::ViewBox); + auto& value = get(PropertyId::ViewBox); return Parser::parseViewBox(value); } PreserveAspectRatio PatternElement::preserveAspectRatio() const { - auto& value = get(PropertyID::PreserveAspectRatio); + auto& value = get(PropertyId::PreserveAspectRatio); return Parser::parsePreserveAspectRatio(value); } std::string PatternElement::href() const { - auto& value = get(PropertyID::Href); + auto& value = get(PropertyId::Href); return Parser::parseHref(value); } @@ -353,29 +353,29 @@ std::unique_ptr PatternElement::getPainter(LayoutContext* context) while(true) { - if(!attributes.hasX() && current->has(PropertyID::X)) + if(!attributes.hasX() && current->has(PropertyId::X)) attributes.setX(current->x()); - if(!attributes.hasY() && current->has(PropertyID::Y)) + if(!attributes.hasY() && current->has(PropertyId::Y)) attributes.setY(current->y()); - if(!attributes.hasWidth() && current->has(PropertyID::Width)) + if(!attributes.hasWidth() && current->has(PropertyId::Width)) attributes.setWidth(current->width()); - if(!attributes.hasHeight() && current->has(PropertyID::Height)) + if(!attributes.hasHeight() && current->has(PropertyId::Height)) attributes.setHeight(current->height()); - if(!attributes.hasPatternTransform() && current->has(PropertyID::PatternTransform)) + if(!attributes.hasPatternTransform() && current->has(PropertyId::PatternTransform)) attributes.setPatternTransform(current->patternTransform()); - if(!attributes.hasPatternUnits() && current->has(PropertyID::PatternUnits)) + if(!attributes.hasPatternUnits() && current->has(PropertyId::PatternUnits)) attributes.setPatternUnits(current->patternUnits()); - if(!attributes.hasPatternContentUnits() && current->has(PropertyID::PatternContentUnits)) + if(!attributes.hasPatternContentUnits() && current->has(PropertyId::PatternContentUnits)) attributes.setPatternContentUnits(current->patternContentUnits()); - if(!attributes.hasViewBox() && current->has(PropertyID::ViewBox)) + if(!attributes.hasViewBox() && current->has(PropertyId::ViewBox)) attributes.setViewBox(current->viewBox()); - if(!attributes.hasPreserveAspectRatio() && current->has(PropertyID::PreserveAspectRatio)) + if(!attributes.hasPreserveAspectRatio() && current->has(PropertyId::PreserveAspectRatio)) attributes.setPreserveAspectRatio(current->preserveAspectRatio()); if(!attributes.hasPatternContentElement() && current->children.size()) attributes.setPatternContentElement(current); auto ref = context->getElementById(current->href()); - if(!ref || ref->id != ElementID::Pattern) + if(!ref || ref->id != ElementId::Pattern) break; processedPatterns.insert(current); @@ -413,7 +413,7 @@ std::unique_ptr PatternElement::clone() const } SolidColorElement::SolidColorElement() - : PaintElement(ElementID::SolidColor) + : PaintElement(ElementId::SolidColor) { } diff --git a/source/paintelement.h b/source/paintelement.h index b2d152a63..70c86cd9f 100644 --- a/source/paintelement.h +++ b/source/paintelement.h @@ -11,7 +11,7 @@ class LayoutObject; class PaintElement : public StyledElement { public: - PaintElement(ElementID id); + PaintElement(ElementId id); bool isPaint() const { return true; } virtual std::unique_ptr getPainter(LayoutContext* context) const = 0; @@ -20,7 +20,7 @@ public: class GradientElement : public PaintElement { public: - GradientElement(ElementID id); + GradientElement(ElementId id); Transform gradientTransform() const; SpreadMethod spreadMethod() const; diff --git a/source/parser.cpp b/source/parser.cpp index 6eab7cdd2..e3b20f67c 100644 --- a/source/parser.cpp +++ b/source/parser.cpp @@ -1024,122 +1024,122 @@ bool Parser::parseTransform(const char*& ptr, const char* end, TransformType& ty return true; } -static const std::map elementmap = { - {"circle", ElementID::Circle}, - {"clipPath", ElementID::ClipPath}, - {"defs", ElementID::Defs}, - {"ellipse", ElementID::Ellipse}, - {"g", ElementID::G}, - {"line", ElementID::Line}, - {"linearGradient", ElementID::LinearGradient}, - {"marker", ElementID::Marker}, - {"mask", ElementID::Mask}, - {"path", ElementID::Path}, - {"pattern", ElementID::Pattern}, - {"polygon", ElementID::Polygon}, - {"polyline", ElementID::Polyline}, - {"radialGradient", ElementID::RadialGradient}, - {"rect", ElementID::Rect}, - {"stop", ElementID::Stop}, - {"style", ElementID::Style}, - {"solidColor", ElementID::SolidColor}, - {"svg", ElementID::Svg}, - {"symbol", ElementID::Symbol}, - {"use", ElementID::Use} +static const std::map elementmap = { + {"circle", ElementId::Circle}, + {"clipPath", ElementId::ClipPath}, + {"defs", ElementId::Defs}, + {"ellipse", ElementId::Ellipse}, + {"g", ElementId::G}, + {"line", ElementId::Line}, + {"linearGradient", ElementId::LinearGradient}, + {"marker", ElementId::Marker}, + {"mask", ElementId::Mask}, + {"path", ElementId::Path}, + {"pattern", ElementId::Pattern}, + {"polygon", ElementId::Polygon}, + {"polyline", ElementId::Polyline}, + {"radialGradient", ElementId::RadialGradient}, + {"rect", ElementId::Rect}, + {"stop", ElementId::Stop}, + {"style", ElementId::Style}, + {"solidColor", ElementId::SolidColor}, + {"svg", ElementId::Svg}, + {"symbol", ElementId::Symbol}, + {"use", ElementId::Use} }; -static const std::map propertymap = { - {"class", PropertyID::Class}, - {"clipPathUnits", PropertyID::ClipPathUnits}, - {"cx", PropertyID::Cx}, - {"cy", PropertyID::Cy}, - {"d", PropertyID::D}, - {"fx", PropertyID::Fx}, - {"fy", PropertyID::Fy}, - {"gradientTransform", PropertyID::GradientTransform}, - {"gradientUnits", PropertyID::GradientUnits}, - {"height", PropertyID::Height}, - {"href", PropertyID::Href}, - {"id", PropertyID::Id}, - {"markerHeight", PropertyID::MarkerHeight}, - {"markerUnits", PropertyID::MarkerUnits}, - {"markerWidth", PropertyID::MarkerWidth}, - {"maskContentUnits", PropertyID::MaskContentUnits}, - {"maskUnits", PropertyID::MaskUnits}, - {"offset", PropertyID::Offset}, - {"orient", PropertyID::Orient}, - {"patternContentUnits", PropertyID::PatternContentUnits}, - {"patternTransform", PropertyID::PatternTransform}, - {"patternUnits", PropertyID::PatternUnits}, - {"points", PropertyID::Points}, - {"preserveAspectRatio", PropertyID::PreserveAspectRatio}, - {"r", PropertyID::R}, - {"refX", PropertyID::RefX}, - {"refY", PropertyID::RefY}, - {"rx", PropertyID::Rx}, - {"ry", PropertyID::Ry}, - {"spreadMethod", PropertyID::SpreadMethod}, - {"style", PropertyID::Style}, - {"transform", PropertyID::Transform}, - {"viewBox", PropertyID::ViewBox}, - {"width", PropertyID::Width}, - {"x", PropertyID::X}, - {"x1", PropertyID::X1}, - {"x2", PropertyID::X2}, - {"xlink:href", PropertyID::Href}, - {"y", PropertyID::Y}, - {"y1", PropertyID::Y1}, - {"y2", PropertyID::Y2} +static const std::map propertymap = { + {"class", PropertyId::Class}, + {"clipPathUnits", PropertyId::ClipPathUnits}, + {"cx", PropertyId::Cx}, + {"cy", PropertyId::Cy}, + {"d", PropertyId::D}, + {"fx", PropertyId::Fx}, + {"fy", PropertyId::Fy}, + {"gradientTransform", PropertyId::GradientTransform}, + {"gradientUnits", PropertyId::GradientUnits}, + {"height", PropertyId::Height}, + {"href", PropertyId::Href}, + {"id", PropertyId::Id}, + {"markerHeight", PropertyId::MarkerHeight}, + {"markerUnits", PropertyId::MarkerUnits}, + {"markerWidth", PropertyId::MarkerWidth}, + {"maskContentUnits", PropertyId::MaskContentUnits}, + {"maskUnits", PropertyId::MaskUnits}, + {"offset", PropertyId::Offset}, + {"orient", PropertyId::Orient}, + {"patternContentUnits", PropertyId::PatternContentUnits}, + {"patternTransform", PropertyId::PatternTransform}, + {"patternUnits", PropertyId::PatternUnits}, + {"points", PropertyId::Points}, + {"preserveAspectRatio", PropertyId::PreserveAspectRatio}, + {"r", PropertyId::R}, + {"refX", PropertyId::RefX}, + {"refY", PropertyId::RefY}, + {"rx", PropertyId::Rx}, + {"ry", PropertyId::Ry}, + {"spreadMethod", PropertyId::SpreadMethod}, + {"style", PropertyId::Style}, + {"transform", PropertyId::Transform}, + {"viewBox", PropertyId::ViewBox}, + {"width", PropertyId::Width}, + {"x", PropertyId::X}, + {"x1", PropertyId::X1}, + {"x2", PropertyId::X2}, + {"xlink:href", PropertyId::Href}, + {"y", PropertyId::Y}, + {"y1", PropertyId::Y1}, + {"y2", PropertyId::Y2} }; -static const std::map csspropertymap = { - {"clip-path", PropertyID::Clip_Path}, - {"clip-rule", PropertyID::Clip_Rule}, - {"color", PropertyID::Color}, - {"display", PropertyID::Display}, - {"fill", PropertyID::Fill}, - {"fill-opacity", PropertyID::Fill_Opacity}, - {"fill-rule", PropertyID::Fill_Rule}, - {"marker-end", PropertyID::Marker_End}, - {"marker-mid", PropertyID::Marker_Mid}, - {"marker-start", PropertyID::Marker_Start}, - {"mask", PropertyID::Mask}, - {"opacity", PropertyID::Opacity}, - {"overflow", PropertyID::Overflow}, - {"solid-color", PropertyID::Solid_Color}, - {"solid-opacity", PropertyID::Solid_Opacity}, - {"stop-color", PropertyID::Stop_Color}, - {"stop-opacity", PropertyID::Stop_Opacity}, - {"stroke", PropertyID::Stroke}, - {"stroke-dasharray", PropertyID::Stroke_Dasharray}, - {"stroke-dashoffset", PropertyID::Stroke_Dashoffset}, - {"stroke-linecap", PropertyID::Stroke_Linecap}, - {"stroke-linejoin", PropertyID::Stroke_Linejoin}, - {"stroke-miterlimit", PropertyID::Stroke_Miterlimit}, - {"stroke-opacity", PropertyID::Stroke_Opacity}, - {"stroke-width", PropertyID::Stroke_Width}, - {"visibility", PropertyID::Visibility} +static const std::map csspropertymap = { + {"clip-path", PropertyId::Clip_Path}, + {"clip-rule", PropertyId::Clip_Rule}, + {"color", PropertyId::Color}, + {"display", PropertyId::Display}, + {"fill", PropertyId::Fill}, + {"fill-opacity", PropertyId::Fill_Opacity}, + {"fill-rule", PropertyId::Fill_Rule}, + {"marker-end", PropertyId::Marker_End}, + {"marker-mid", PropertyId::Marker_Mid}, + {"marker-start", PropertyId::Marker_Start}, + {"mask", PropertyId::Mask}, + {"opacity", PropertyId::Opacity}, + {"overflow", PropertyId::Overflow}, + {"solid-color", PropertyId::Solid_Color}, + {"solid-opacity", PropertyId::Solid_Opacity}, + {"stop-color", PropertyId::Stop_Color}, + {"stop-opacity", PropertyId::Stop_Opacity}, + {"stroke", PropertyId::Stroke}, + {"stroke-dasharray", PropertyId::Stroke_Dasharray}, + {"stroke-dashoffset", PropertyId::Stroke_Dashoffset}, + {"stroke-linecap", PropertyId::Stroke_Linecap}, + {"stroke-linejoin", PropertyId::Stroke_Linejoin}, + {"stroke-miterlimit", PropertyId::Stroke_Miterlimit}, + {"stroke-opacity", PropertyId::Stroke_Opacity}, + {"stroke-width", PropertyId::Stroke_Width}, + {"visibility", PropertyId::Visibility} }; -static inline ElementID elementId(const std::string& name) +static inline ElementId elementId(const std::string& name) { auto it = elementmap.find(name); if(it == elementmap.end()) - return ElementID::Unknown; + return ElementId::Unknown; return it->second; } -static inline PropertyID cssPropertyId(const std::string& name) +static inline PropertyId cssPropertyId(const std::string& name) { auto it = csspropertymap.find(name); if(it == csspropertymap.end()) - return PropertyID::Unknown; + return PropertyId::Unknown; return it->second; } -static inline PropertyID propertyId(const std::string& name) +static inline PropertyId propertyId(const std::string& name) { auto it = propertymap.find(name); if(it == propertymap.end()) @@ -1148,17 +1148,331 @@ static inline PropertyID propertyId(const std::string& name) return it->second; } -bool RuleData::match(const Element* element) const +#define IS_STARTNAMECHAR(c) (IS_ALPHA(c) || (c) == '_' || (c) == ':') +#define IS_NAMECHAR(c) (IS_STARTNAMECHAR(c) || IS_NUM(c) || (c) == '-' || (c) == '.') +static inline bool readIdentifier(const char*& ptr, const char* end, std::string& value) { - if(m_selector.empty()) + if(ptr >= end || !IS_STARTNAMECHAR(*ptr)) return false; - if(m_selector.size() == 1) - return matchSimpleSelector(m_selector.front(), element); + auto start = ptr; + ++ptr; + while(ptr < end && IS_NAMECHAR(*ptr)) + ++ptr; - auto it = m_selector.rbegin(); - auto end = m_selector.rend(); - if(!matchSimpleSelector(*it, element)) + value.assign(start, ptr); + return true; +} + +#define IS_CSS_STARTNAMECHAR(c) (IS_ALPHA(c) || (c) == '_' || (c) == '-') +#define IS_CSS_NAMECHAR(c) (IS_CSS_STARTNAMECHAR(c) || IS_NUM(c)) +static inline bool readCSSIdentifier(const char*& ptr, const char* end, std::string& value) +{ + if(ptr >= end || !IS_CSS_STARTNAMECHAR(*ptr)) + return false; + + auto start = ptr; + ++ptr; + while(ptr < end && IS_CSS_NAMECHAR(*ptr)) + ++ptr; + + value.assign(start, ptr); + return true; +} + +bool CSSParser::parseMore(const std::string& value) +{ + auto ptr = value.data(); + auto end = ptr + value.size(); + + while(ptr < end) + { + Utils::skipWs(ptr, end); + if(Utils::skipDesc(ptr, end, '@')) + { + if(!parseAtRule(ptr, end)) + return false; + continue; + } + + Rule rule; + if(!parseRule(ptr, end, rule)) + return false; + m_rules.push_back(rule); + } + + return true; +} + +bool CSSParser::parseAtRule(const char*& ptr, const char* end) const +{ + int depth = 0; + while(ptr < end) + { + auto ch = *ptr; + ++ptr; + if(ch == ';' && depth == 0) + break; + if(ch == '{') ++depth; + else if(ch == '}' && depth > 0) + { + if(depth == 1) + break; + --depth; + } + } + + return true; +} + +bool CSSParser::parseRule(const char*& ptr, const char* end, Rule& rule) const +{ + if(!parseSelectors(ptr, end, rule.selectors)) + return false; + + if(!parseDeclarations(ptr, end, rule.declarations)) + return false; + + return true; +} + +bool CSSParser::parseSelectors(const char*& ptr, const char* end, SelectorList& selectors) const +{ + Selector selector; + if(!parseSelector(ptr, end, selector)) + return false; + selectors.push_back(selector); + + while(Utils::skipDesc(ptr, end, ',')) + { + Utils::skipWs(ptr, end); + Selector selector; + if(!parseSelector(ptr, end, selector)) + return false; + selectors.push_back(selector); + } + + return true; +} + +bool CSSParser::parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations) const +{ + if(!Utils::skipDesc(ptr, end, '{')) + return false; + + std::string name; + std::string value; + Utils::skipWs(ptr, end); + do { + if(!readCSSIdentifier(ptr, end, name)) + return false; + Utils::skipWs(ptr, end); + if(!Utils::skipDesc(ptr, end, ':')) + return false; + Utils::skipWs(ptr, end); + auto start = ptr; + while(ptr < end && !(*ptr == '!' || *ptr == ';' || *ptr == '}')) + ++ptr; + value.assign(start, Utils::rtrim(start, ptr)); + int specificity = 0x10; + if(Utils::skipDesc(ptr, end, '!')) + { + if(!Utils::skipDesc(ptr, end, "important")) + return false; + specificity = 0x1000; + } + + auto id = cssPropertyId(name); + if(id != PropertyId::Unknown) + declarations.set(id, value, specificity); + Utils::skipWsDelimiter(ptr, end, ';'); + } while(ptr < end && *ptr != '}'); + + return Utils::skipDesc(ptr, end, '}'); +} + +#define IS_SELECTOR_STARTNAMECHAR(c) (IS_CSS_STARTNAMECHAR(c) || (c) == '*' || (c) == '#' || (c) == '.' || (c) == '[' || (c) == ':') +bool CSSParser::parseSelector(const char*& ptr, const char* end, Selector& selector) const +{ + do { + SimpleSelector simpleSelector; + if(!parseSimpleSelector(ptr, end, simpleSelector)) + return false; + + selector.specificity += (simpleSelector.id == ElementId::Star) ? 0x0 : 0x1; + for(auto& attributeSelector : simpleSelector.attributeSelectors) + selector.specificity += (attributeSelector.id == PropertyId::Id) ? 0x10000 : 0x100; + + selector.simpleSelectors.push_back(simpleSelector); + Utils::skipWs(ptr, end); + } while(ptr < end && IS_SELECTOR_STARTNAMECHAR(*ptr)); + + return true; +} + +bool CSSParser::parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector) const +{ + std::string name; + if(Utils::skipDesc(ptr, end, '*')) + simpleSelector.id = ElementId::Star; + else if(readCSSIdentifier(ptr, end, name)) + simpleSelector.id = elementId(name); + + while(ptr < end) + { + if(Utils::skipDesc(ptr, end, '#')) + { + AttributeSelector a; + a.id = PropertyId::Id; + a.matchType = AttributeSelector::MatchType::Equal; + if(!readCSSIdentifier(ptr, end, a.value)) + return false; + simpleSelector.attributeSelectors.push_back(a); + continue; + } + + if(Utils::skipDesc(ptr, end, '.')) + { + AttributeSelector a; + a.id = PropertyId::Class; + a.matchType = AttributeSelector::MatchType::Includes; + if(!readCSSIdentifier(ptr, end, a.value)) + return false; + simpleSelector.attributeSelectors.push_back(a); + continue; + } + + if(Utils::skipDesc(ptr, end, '[')) + { + Utils::skipWs(ptr, end); + if(!readCSSIdentifier(ptr, end, name)) + return false; + AttributeSelector a; + a.id = propertyId(name); + if(Utils::skipDesc(ptr, end, '=')) + a.matchType = AttributeSelector::MatchType::Equal; + else if(Utils::skipDesc(ptr, end, "~=")) + a.matchType = AttributeSelector::MatchType::Includes; + else if(Utils::skipDesc(ptr, end, "|=")) + a.matchType = AttributeSelector::MatchType::DashMatch; + else if(Utils::skipDesc(ptr, end, "^=")) + a.matchType = AttributeSelector::MatchType::StartsWith; + else if(Utils::skipDesc(ptr, end, "$=")) + a.matchType = AttributeSelector::MatchType::EndsWith; + else if(Utils::skipDesc(ptr, end, "*=")) + a.matchType = AttributeSelector::MatchType::Contains; + if(a.matchType != AttributeSelector::MatchType::None) + { + Utils::skipWs(ptr, end); + if(!readCSSIdentifier(ptr, end, a.value)) + { + if(ptr >= end || !(*ptr == '\"' || *ptr == '\'')) + return false; + + auto quote = *ptr; + ++ptr; + if(!Utils::readUntil(ptr, end, quote, a.value)) + return false; + ++ptr; + } + } + + Utils::skipWs(ptr, end); + if(!Utils::skipDesc(ptr, end, ']')) + return false; + simpleSelector.attributeSelectors.push_back(a); + continue; + } + + if(Utils::skipDesc(ptr, end, ':')) + { + if(!readCSSIdentifier(ptr, end, name)) + return false; + PseudoClass pseudo; + if(name.compare("empty") == 0) + pseudo.type = PseudoClass::Type::Empty; + else if(name.compare("root") == 0) + pseudo.type = PseudoClass::Type::Root; + else if(name.compare("not") == 0) + pseudo.type = PseudoClass::Type::Not; + else if(name.compare("first-child") == 0) + pseudo.type = PseudoClass::Type::FirstChild; + else if(name.compare("last-child") == 0) + pseudo.type = PseudoClass::Type::LastChild; + else if(name.compare("only-child") == 0) + pseudo.type = PseudoClass::Type::OnlyChild; + else if(name.compare("first-of-type") == 0) + pseudo.type = PseudoClass::Type::FirstOfType; + else if(name.compare("last-of-type") == 0) + pseudo.type = PseudoClass::Type::LastOfType; + else if(name.compare("only-of-type") == 0) + pseudo.type = PseudoClass::Type::OnlyOfType; + if(pseudo.type == PseudoClass::Type::Not) + { + if(!Utils::skipDesc(ptr, end, '(')) + return false; + + Utils::skipWs(ptr, end); + if(!parseSelectors(ptr, end, pseudo.notSelectors)) + return false; + + Utils::skipWs(ptr, end); + if(!Utils::skipDesc(ptr, end, ')')) + return false; + } + + simpleSelector.pseudoClasses.push_back(pseudo); + continue; + } + + break; + } + + Utils::skipWs(ptr, end); + if(Utils::skipDesc(ptr, end, '>')) + simpleSelector.combinator = SimpleSelector::Combinator::Child; + else if(Utils::skipDesc(ptr, end, '+')) + simpleSelector.combinator = SimpleSelector::Combinator::DirectAdjacent; + else if(Utils::skipDesc(ptr, end, '~')) + simpleSelector.combinator = SimpleSelector::Combinator::InDirectAdjacent; + + return true; +} + +RuleMatchContext::RuleMatchContext(const std::vector& rules) +{ + for(auto& rule : rules) + for(auto& selector : rule.selectors) + m_selectors.emplace(selector.specificity, std::make_pair(&selector, &rule.declarations)); +} + +std::vector RuleMatchContext::match(const Element* element) const +{ + std::vector declarations; + auto it = m_selectors.begin(); + auto end = m_selectors.end(); + for(;it != end;++it) + { + auto& value = it->second; + if(!selectorMatch(std::get<0>(value), element)) + continue; + declarations.push_back(std::get<1>(value)); + } + + return declarations; +} + +bool RuleMatchContext::selectorMatch(const Selector* selector, const Element* element) const +{ + if(selector->simpleSelectors.empty()) + return false; + + if(selector->simpleSelectors.size() == 1) + return simpleSelectorMatch(selector->simpleSelectors.front(), element); + + auto it = selector->simpleSelectors.rbegin(); + auto end = selector->simpleSelectors.rend(); + if(!simpleSelectorMatch(*it, element)) return false; ++it; @@ -1171,14 +1485,14 @@ bool RuleData::match(const Element* element) const break; case SimpleSelector::Combinator::DirectAdjacent: case SimpleSelector::Combinator::InDirectAdjacent: - element = element->previousElement(); + element = element->previousSibling(); break; } if(element == nullptr) return false; - auto match = matchSimpleSelector(*it, element); + auto match = simpleSelectorMatch(*it, element); if(!match && (it->combinator != SimpleSelector::Combinator::Descendant && it->combinator != SimpleSelector::Combinator::InDirectAdjacent)) return false; @@ -1189,23 +1503,23 @@ bool RuleData::match(const Element* element) const return true; } -bool RuleData::matchSimpleSelector(const SimpleSelector& selector, const Element* element) const +bool RuleMatchContext::simpleSelectorMatch(const SimpleSelector& selector, const Element* element) const { - if(selector.id != ElementID::Star && selector.id != element->id) + if(selector.id != ElementId::Star && selector.id != element->id) return false; - for(auto& sel : selector.attributeSelectors) - if(!matchAttributeSelector(sel, element)) + for(auto& attributeSelector : selector.attributeSelectors) + if(!attributeSelectorMatch(attributeSelector, element)) return false; - for(auto& sel : selector.pseudoClassSelectors) - if(!matchPseudoClassSelector(sel, element)) + for(auto& pseudoClass : selector.pseudoClasses) + if(!pseudoClassMatch(pseudoClass, element)) return false; return true; } -bool RuleData::matchAttributeSelector(const AttributeSelector& selector, const Element* element) const +bool RuleMatchContext::attributeSelectorMatch(const AttributeSelector& selector, const Element* element) const { auto& value = element->get(selector.id); if(value.empty()) @@ -1269,70 +1583,52 @@ bool RuleData::matchAttributeSelector(const AttributeSelector& selector, const E return false; } -bool RuleData::matchPseudoClassSelector(const PseudoClassSelector& selector, const Element* element) const +bool RuleMatchContext::pseudoClassMatch(const PseudoClass& pseudo, const Element* element) const { - if(selector.type == PseudoClassSelector::Type::Empty) + if(pseudo.type == PseudoClass::Type::Empty) return element->children.empty(); - if(selector.type == PseudoClassSelector::Type::Root) + if(pseudo.type == PseudoClass::Type::Root) return element->parent == nullptr; - if(selector.type == PseudoClassSelector::Type::Is) + if(pseudo.type == PseudoClass::Type::Not) { - for(auto& selector : selector.subSelectors) { - for(auto& sel : selector) { - if(!matchSimpleSelector(sel, element)) { - return false; - } - } - } - + for(auto& selector : pseudo.notSelectors) + if(selectorMatch(&selector, element)) + return false; return true; } - if(selector.type == PseudoClassSelector::Type::Not) + if(pseudo.type == PseudoClass::Type::FirstChild) + return !element->previousSibling(); + + if(pseudo.type == PseudoClass::Type::LastChild) + return !element->nextSibling(); + + if(pseudo.type == PseudoClass::Type::OnlyChild) + return !(element->previousSibling() || element->nextSibling()); + + if(pseudo.type == PseudoClass::Type::FirstOfType) { - for(auto& selector : selector.subSelectors) { - for(auto& sel : selector) { - if(matchSimpleSelector(sel, element)) { - return false; - } - } - } - - return true; - } - - if(selector.type == PseudoClassSelector::Type::FirstChild) - return !element->previousElement(); - - if(selector.type == PseudoClassSelector::Type::LastChild) - return !element->nextElement(); - - if(selector.type == PseudoClassSelector::Type::OnlyChild) - return !(element->previousElement() || element->nextElement()); - - if(selector.type == PseudoClassSelector::Type::FirstOfType) - { - auto sibling = element->previousElement(); + auto sibling = element->previousSibling(); while(sibling) { if(sibling->id == element->id) return false; - sibling = element->previousElement(); + sibling = element->previousSibling(); } return true; } - if(selector.type == PseudoClassSelector::Type::LastOfType) + if(pseudo.type == PseudoClass::Type::LastOfType) { - auto sibling = element->nextElement(); + auto sibling = element->nextSibling(); while(sibling) { if(sibling->id == element->id) return false; - sibling = element->nextElement(); + sibling = element->nextSibling(); } return true; @@ -1341,372 +1637,50 @@ bool RuleData::matchPseudoClassSelector(const PseudoClassSelector& selector, con return false; } -void StyleSheet::parse(const std::string& content) -{ - CSSParser::parseSheet(this, content); -} - -void StyleSheet::add(const Rule& rule) -{ - for(auto& selector : rule.selectors) { - uint32_t specificity = 0; - for(auto& simpleSelector : selector) { - specificity += (simpleSelector.id == ElementID::Star) ? 0x0 : 0x1; - for(auto& attributeSelector : simpleSelector.attributeSelectors) { - specificity += (attributeSelector.id == PropertyID::Id) ? 0x10000 : 0x100; - } - } - - m_rules.emplace(selector, rule.declarations, specificity, m_position); - } - - m_position += 1; -} - -std::vector StyleSheet::match(const Element *element) const -{ - std::vector declarations; - for(auto& rule : m_rules) { - if(!rule.match(element)) - continue; - declarations.push_back(&rule.properties()); - } - - return declarations; -} - -#define IS_STARTNAMECHAR(c) (IS_ALPHA(c) || (c) == '_' || (c) == ':') -#define IS_NAMECHAR(c) (IS_STARTNAMECHAR(c) || IS_NUM(c) || (c) == '-' || (c) == '.') -static inline bool readIdentifier(const char*& ptr, const char* end, std::string& value) -{ - if(ptr >= end || !IS_STARTNAMECHAR(*ptr)) - return false; - - auto start = ptr; - ++ptr; - while(ptr < end && IS_NAMECHAR(*ptr)) - ++ptr; - - value.assign(start, ptr); - return true; -} - -#define IS_CSS_STARTNAMECHAR(c) (IS_ALPHA(c) || (c) == '_') -#define IS_CSS_NAMECHAR(c) (IS_CSS_STARTNAMECHAR(c) || IS_NUM(c) || (c) == '-') -static inline bool readCSSIdentifier(const char*& ptr, const char* end, std::string& value) -{ - if(ptr >= end || !IS_CSS_STARTNAMECHAR(*ptr)) - return false; - - auto start = ptr; - ++ptr; - while(ptr < end && IS_CSS_NAMECHAR(*ptr)) - ++ptr; - - value.assign(start, ptr); - return true; -} - -bool CSSParser::parseSheet(StyleSheet* sheet, const std::string& value) -{ - auto ptr = value.data(); - auto end = ptr + value.size(); - - Rule rule; - while(ptr < end) - { - Utils::skipWs(ptr, end); - if(Utils::skipDesc(ptr, end, '@')) - { - if(!parseAtRule(ptr, end)) - return false; - continue; - } - - if(!parseRule(ptr, end, rule)) - return false; - sheet->add(rule); - } - - return true; -} - -bool CSSParser::parseAtRule(const char*& ptr, const char* end) -{ - int depth = 0; - while(ptr < end) - { - auto ch = *ptr; - ++ptr; - if(ch == ';' && depth == 0) - break; - if(ch == '{') ++depth; - else if(ch == '}' && depth > 0) - { - if(depth == 1) - break; - --depth; - } - } - - return true; -} - -bool CSSParser::parseRule(const char*& ptr, const char* end, Rule& rule) -{ - if(!parseSelectors(ptr, end, rule.selectors)) - return false; - - if(!parseDeclarations(ptr, end, rule.declarations)) - return false; - - return true; -} - -bool CSSParser::parseSelectors(const char*& ptr, const char* end, SelectorList& selectors) -{ - selectors.clear(); - Selector selector; - if(!parseSelector(ptr, end, selector)) - return false; - selectors.push_back(selector); - - while(Utils::skipDesc(ptr, end, ',')) - { - Utils::skipWs(ptr, end); - Selector selector; - if(!parseSelector(ptr, end, selector)) - return false; - selectors.push_back(selector); - } - - return true; -} - -bool CSSParser::parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations) -{ - declarations.clear(); - if(!Utils::skipDesc(ptr, end, '{')) - return false; - - std::string name; - std::string value; - Utils::skipWs(ptr, end); - do { - if(!readCSSIdentifier(ptr, end, name)) - return false; - Utils::skipWs(ptr, end); - if(!Utils::skipDesc(ptr, end, ':')) - return false; - Utils::skipWs(ptr, end); - auto start = ptr; - while(ptr < end && !(*ptr == '!' || *ptr == ';' || *ptr == '}')) - ++ptr; - value.assign(start, Utils::rtrim(start, ptr)); - int specificity = 0x10; - if(Utils::skipDesc(ptr, end, '!')) - { - if(!Utils::skipDesc(ptr, end, "important")) - return false; - specificity = 0x1000; - } - - auto id = cssPropertyId(name); - if(id != PropertyID::Unknown) - declarations.set(id, value, specificity); - Utils::skipWsDelimiter(ptr, end, ';'); - } while(ptr < end && *ptr != '}'); - - return Utils::skipDesc(ptr, end, '}'); -} - -#define IS_SELECTOR_STARTNAMECHAR(c) (IS_CSS_STARTNAMECHAR(c) || (c) == '*' || (c) == '#' || (c) == '.' || (c) == '[' || (c) == ':') -bool CSSParser::parseSelector(const char*& ptr, const char* end, Selector& selector) -{ - do { - SimpleSelector simpleSelector; - if(!parseSimpleSelector(ptr, end, simpleSelector)) - return false; - selector.push_back(simpleSelector); - Utils::skipWs(ptr, end); - } while(ptr < end && IS_SELECTOR_STARTNAMECHAR(*ptr)); - - return true; -} - -bool CSSParser::parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector) -{ - std::string name; - if(Utils::skipDesc(ptr, end, '*')) - simpleSelector.id = ElementID::Star; - else if(readCSSIdentifier(ptr, end, name)) - simpleSelector.id = elementId(name); - - while(ptr < end) - { - if(Utils::skipDesc(ptr, end, '#')) - { - AttributeSelector a; - a.id = PropertyID::Id; - a.matchType = AttributeSelector::MatchType::Equal; - if(!readCSSIdentifier(ptr, end, a.value)) - return false; - simpleSelector.attributeSelectors.push_back(a); - continue; - } - - if(Utils::skipDesc(ptr, end, '.')) - { - AttributeSelector a; - a.id = PropertyID::Class; - a.matchType = AttributeSelector::MatchType::Includes; - if(!readCSSIdentifier(ptr, end, a.value)) - return false; - simpleSelector.attributeSelectors.push_back(a); - continue; - } - - if(Utils::skipDesc(ptr, end, '[')) - { - Utils::skipWs(ptr, end); - if(!readCSSIdentifier(ptr, end, name)) - return false; - AttributeSelector a; - a.id = propertyId(name); - if(Utils::skipDesc(ptr, end, '=')) - a.matchType = AttributeSelector::MatchType::Equal; - else if(Utils::skipDesc(ptr, end, "~=")) - a.matchType = AttributeSelector::MatchType::Includes; - else if(Utils::skipDesc(ptr, end, "|=")) - a.matchType = AttributeSelector::MatchType::DashMatch; - else if(Utils::skipDesc(ptr, end, "^=")) - a.matchType = AttributeSelector::MatchType::StartsWith; - else if(Utils::skipDesc(ptr, end, "$=")) - a.matchType = AttributeSelector::MatchType::EndsWith; - else if(Utils::skipDesc(ptr, end, "*=")) - a.matchType = AttributeSelector::MatchType::Contains; - if(a.matchType != AttributeSelector::MatchType::None) - { - Utils::skipWs(ptr, end); - if(!readCSSIdentifier(ptr, end, a.value)) - { - if(ptr >= end || !(*ptr == '\"' || *ptr == '\'')) - return false; - - auto quote = *ptr; - ++ptr; - if(!Utils::readUntil(ptr, end, quote, a.value)) - return false; - ++ptr; - } - } - - Utils::skipWs(ptr, end); - if(!Utils::skipDesc(ptr, end, ']')) - return false; - simpleSelector.attributeSelectors.push_back(a); - continue; - } - - if(Utils::skipDesc(ptr, end, ':')) - { - if(!readCSSIdentifier(ptr, end, name)) - return false; - PseudoClassSelector selector; - if(name.compare("empty") == 0) - selector.type = PseudoClassSelector::Type::Empty; - else if(name.compare("root") == 0) - selector.type = PseudoClassSelector::Type::Root; - else if(name.compare("not") == 0) - selector.type = PseudoClassSelector::Type::Not; - else if(name.compare("first-child") == 0) - selector.type = PseudoClassSelector::Type::FirstChild; - else if(name.compare("last-child") == 0) - selector.type = PseudoClassSelector::Type::LastChild; - else if(name.compare("only-child") == 0) - selector.type = PseudoClassSelector::Type::OnlyChild; - else if(name.compare("first-of-type") == 0) - selector.type = PseudoClassSelector::Type::FirstOfType; - else if(name.compare("last-of-type") == 0) - selector.type = PseudoClassSelector::Type::LastOfType; - else if(name.compare("only-of-type") == 0) - selector.type = PseudoClassSelector::Type::OnlyOfType; - if(selector.type == PseudoClassSelector::Type::Is || selector.type == PseudoClassSelector::Type::Not) - { - if(!Utils::skipDesc(ptr, end, '(')) - return false; - - Utils::skipWs(ptr, end); - if(!parseSelectors(ptr, end, selector.subSelectors)) - return false; - - Utils::skipWs(ptr, end); - if(!Utils::skipDesc(ptr, end, ')')) - return false; - } - - simpleSelector.pseudoClassSelectors.push_back(selector); - continue; - } - - break; - } - - Utils::skipWs(ptr, end); - if(Utils::skipDesc(ptr, end, '>')) - simpleSelector.combinator = SimpleSelector::Combinator::Child; - else if(Utils::skipDesc(ptr, end, '+')) - simpleSelector.combinator = SimpleSelector::Combinator::DirectAdjacent; - else if(Utils::skipDesc(ptr, end, '~')) - simpleSelector.combinator = SimpleSelector::Combinator::InDirectAdjacent; - - return true; -} - -static inline std::unique_ptr createElement(ElementID id) +static inline std::unique_ptr createElement(ElementId id) { switch(id) { - case ElementID::Svg: + case ElementId::Svg: return std::make_unique(); - case ElementID::Path: + case ElementId::Path: return std::make_unique(); - case ElementID::G: + case ElementId::G: return std::make_unique(); - case ElementID::Rect: + case ElementId::Rect: return std::make_unique(); - case ElementID::Circle: + case ElementId::Circle: return std::make_unique(); - case ElementID::Ellipse: + case ElementId::Ellipse: return std::make_unique(); - case ElementID::Line: + case ElementId::Line: return std::make_unique(); - case ElementID::Defs: + case ElementId::Defs: return std::make_unique(); - case ElementID::Polygon: + case ElementId::Polygon: return std::make_unique(); - case ElementID::Polyline: + case ElementId::Polyline: return std::make_unique(); - case ElementID::Stop: + case ElementId::Stop: return std::make_unique(); - case ElementID::LinearGradient: + case ElementId::LinearGradient: return std::make_unique(); - case ElementID::RadialGradient: + case ElementId::RadialGradient: return std::make_unique(); - case ElementID::Symbol: + case ElementId::Symbol: return std::make_unique(); - case ElementID::Use: + case ElementId::Use: return std::make_unique(); - case ElementID::Pattern: + case ElementId::Pattern: return std::make_unique(); - case ElementID::Mask: + case ElementId::Mask: return std::make_unique(); - case ElementID::ClipPath: + case ElementId::ClipPath: return std::make_unique(); - case ElementID::SolidColor: + case ElementId::SolidColor: return std::make_unique(); - case ElementID::Marker: + case ElementId::Marker: return std::make_unique(); - case ElementID::Style: + case ElementId::Style: return std::make_unique(); default: break; @@ -1816,22 +1790,26 @@ static inline void parseStyle(const std::string& string, Element* element) ++ptr; value.assign(start, Utils::rtrim(start, ptr)); auto id = cssPropertyId(name); - if(id != PropertyID::Unknown) + if(id != PropertyId::Unknown) element->set(id, value, 0x100); Utils::skipWsDelimiter(ptr, end, ';'); } } -TreeBuilder::TreeBuilder() = default; +ParseDocument::ParseDocument() +{ +} -TreeBuilder::~TreeBuilder() = default; +ParseDocument::~ParseDocument() +{ +} -bool TreeBuilder::parse(const char* data, std::size_t size) +bool ParseDocument::parse(const char* data, std::size_t size) { auto ptr = data; auto end = ptr + size; - StyleSheet styleSheet; + CSSParser cssparser; Element* current = nullptr; std::string name; std::string value; @@ -1847,7 +1825,7 @@ bool TreeBuilder::parse(const char* data, std::size_t size) }; auto handle_text = [&](const char* start, const char* end, bool in_cdata) { - if(ignoring > 0 || current == nullptr || current->id != ElementID::Style) + if(ignoring > 0 || current == nullptr || current->id != ElementId::Style) return; if(in_cdata) @@ -1856,7 +1834,7 @@ bool TreeBuilder::parse(const char* data, std::size_t size) decodeText(start, end, value); remove_comments(value); - styleSheet.parse(value); + cssparser.parseMore(value); }; while(ptr < end) @@ -1962,8 +1940,8 @@ bool TreeBuilder::parse(const char* data, std::size_t size) if(!readIdentifier(ptr, end, name)) return false; - auto id = ignoring == 0 ? elementId(name) : ElementID::Unknown; - if(id == ElementID::Unknown) + auto id = ignoring == 0 ? elementId(name) : ElementId::Unknown; + if(id == ElementId::Unknown) ++ignoring; Element* element = nullptr; @@ -1974,7 +1952,7 @@ bool TreeBuilder::parse(const char* data, std::size_t size) if(m_rootElement == nullptr) { - if(id != ElementID::Svg) + if(id != ElementId::Svg) return false; m_rootElement = std::make_unique(); @@ -2010,18 +1988,18 @@ bool TreeBuilder::parse(const char* data, std::size_t size) if(ptr >= end || *ptr != quote) return false; - auto id = element ? propertyId(name) : PropertyID::Unknown; - if(id != PropertyID::Unknown) + auto id = element ? propertyId(name) : PropertyId::Unknown; + if(id != PropertyId::Unknown) { decodeText(start, Utils::rtrim(start, ptr), value); - if(id == PropertyID::Style) + if(id == PropertyId::Style) { remove_comments(value); parseStyle(value, element); } else { - if(id == PropertyID::Id) + if(id == PropertyId::Id) m_idCache.emplace(value, element); element->set(id, value, 0x1); } @@ -2059,14 +2037,16 @@ bool TreeBuilder::parse(const char* data, std::size_t size) if(!m_rootElement || ptr < end || ignoring > 0) return false; - if(!styleSheet.empty()) + const auto& rules = cssparser.rules(); + if(!rules.empty()) { - m_rootElement->transverse([&styleSheet](Node* node) { + RuleMatchContext context(rules); + m_rootElement->transverse([&context](Node* node) { if(node->isText()) return false; auto element = static_cast(node); - auto declarations = styleSheet.match(element); + auto declarations = context.match(element); for(auto& declaration : declarations) element->properties.add(*declaration); return false; @@ -2076,7 +2056,7 @@ bool TreeBuilder::parse(const char* data, std::size_t size) return true; } -Element* TreeBuilder::getElementById(const std::string& id) const +Element* ParseDocument::getElementById(const std::string& id) const { auto it = m_idCache.find(id); if(it == m_idCache.end()) @@ -2085,9 +2065,9 @@ Element* TreeBuilder::getElementById(const std::string& id) const return it->second; } -std::unique_ptr TreeBuilder::build() const +std::unique_ptr ParseDocument::layout() const { - return m_rootElement->build(this); + return m_rootElement->layoutDocument(this); } } // namespace lunasvg diff --git a/source/parser.h b/source/parser.h index 1f0e5e813..6ced83e16 100644 --- a/source/parser.h +++ b/source/parser.h @@ -2,7 +2,6 @@ #define PARSER_H #include -#include #include "property.h" #include "element.h" @@ -64,10 +63,7 @@ private: static bool parseTransform(const char*& ptr, const char* end, TransformType& type, double* values, int& count); }; -struct SimpleSelector; - -using Selector = std::vector; -using SelectorList = std::vector; +struct Selector; struct AttributeSelector { @@ -82,19 +78,20 @@ struct AttributeSelector Contains }; - MatchType matchType{MatchType::None}; - PropertyID id{PropertyID::Unknown}; + PropertyId id{PropertyId::Unknown}; std::string value; + MatchType matchType{MatchType::None}; }; -struct PseudoClassSelector +using SelectorList = std::vector; + +struct PseudoClass { enum class Type { Unknown, Empty, Root, - Is, Not, FirstChild, LastChild, @@ -105,9 +102,7 @@ struct PseudoClassSelector }; Type type{Type::Unknown}; - int16_t a{0}; - int16_t b{0}; - SelectorList subSelectors; + SelectorList notSelectors; }; struct SimpleSelector @@ -120,10 +115,16 @@ struct SimpleSelector InDirectAdjacent }; - Combinator combinator{Combinator::Descendant}; - ElementID id{ElementID::Star}; + ElementId id{ElementId::Star}; std::vector attributeSelectors; - std::vector pseudoClassSelectors; + std::vector pseudoClasses; + Combinator combinator{Combinator::Descendant}; +}; + +struct Selector +{ + std::vector simpleSelectors; + int specificity{0}; }; struct Rule @@ -132,47 +133,21 @@ struct Rule PropertyList declarations; }; -class RuleData { +class RuleMatchContext +{ public: - 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; } + RuleMatchContext(const std::vector& rules); std::vector match(const Element* element) const; private: - std::multiset m_rules; - uint32_t m_position{0}; + bool selectorMatch(const Selector* selector, const Element* element) const; + bool simpleSelectorMatch(const SimpleSelector& selector, const Element* element) const; + bool attributeSelectorMatch(const AttributeSelector& selector, const Element* element) const; + bool pseudoClassMatch(const PseudoClass& pseudo, const Element* element) const; + +private: + std::multimap, std::less> m_selectors; }; class CSSParser @@ -180,29 +155,35 @@ class CSSParser public: CSSParser() = default; - static bool parseSheet(StyleSheet* sheet, const std::string& value); + bool parseMore(const std::string& value); + + const std::vector& rules() const { return m_rules; } private: - static bool parseAtRule(const char*& ptr, const char* end); - static bool parseRule(const char*& ptr, const char* end, Rule& rule); - static bool parseSelectors(const char*& ptr, const char* end, SelectorList& selectors); - static bool parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations); - static bool parseSelector(const char*& ptr, const char* end, Selector& selector); - static bool parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector); + bool parseAtRule(const char*& ptr, const char* end) const; + bool parseRule(const char*& ptr, const char* end, Rule& rule) const; + bool parseSelectors(const char*& ptr, const char* end, SelectorList& selectors) const; + bool parseDeclarations(const char*& ptr, const char* end, PropertyList& declarations) const; + bool parseSelector(const char*& ptr, const char* end, Selector& selector) const; + bool parseSimpleSelector(const char*& ptr, const char* end, SimpleSelector& simpleSelector) const; + +private: + std::vector m_rules; }; class LayoutSymbol; -class TreeBuilder { +class ParseDocument +{ public: - TreeBuilder(); - ~TreeBuilder(); + ParseDocument(); + ~ParseDocument(); bool parse(const char* data, std::size_t size); SVGElement* rootElement() const { return m_rootElement.get(); } Element* getElementById(const std::string& id) const; - std::unique_ptr build() const; + std::unique_ptr layout() const; private: std::unique_ptr m_rootElement; diff --git a/source/parserutils.h b/source/parserutils.h index 90fd3dcae..e83746d6d 100644 --- a/source/parserutils.h +++ b/source/parserutils.h @@ -6,8 +6,6 @@ #include #include #include -#include -#include namespace lunasvg { @@ -264,241 +262,6 @@ inline bool parseNumber(const char*& ptr, const char* end, T& number) } // 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 #endif // PARSERUTILS_H diff --git a/source/pointer.h b/source/pointer.h deleted file mode 100644 index 9568f7e33..000000000 --- a/source/pointer.h +++ /dev/null @@ -1,254 +0,0 @@ -#ifndef POINTER_H -#define POINTER_H - -#include -#include - -namespace lunasvg { - -template -class RefCounted { -public: - RefCounted() = default; - - void ref() { ++m_refCount; } - void deref() { - if(--m_refCount == 0) { - delete static_cast(this); - } - } - - uint32_t refCount() const { return m_refCount; } - bool hasOneRefCount() const { return m_refCount == 1; } - -private: - uint32_t m_refCount{1}; -}; - -template -inline void refIfNotNull(T* ptr) -{ - if(ptr) - ptr->ref(); -} - -template -inline void derefIfNotNull(T* ptr) -{ - if(ptr) - ptr->deref(); -} - -template class RefPtr; -template RefPtr adoptPtr(T*); - -template -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& p) : m_ptr(p.get()) { refIfNotNull(m_ptr); } - RefPtr(RefPtr&& p) : m_ptr(p.release()) {} - - template - RefPtr(const RefPtr& p) : m_ptr(p.get()) { refIfNotNull(m_ptr); } - - template - RefPtr(RefPtr&& 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& operator=(std::nullptr_t) - { - clear(); - return *this; - } - - RefPtr& operator=(T* o) - { - RefPtr p = o; - swap(p); - return *this; - } - - RefPtr& operator=(T& o) - { - RefPtr p = o; - swap(p); - return *this; - } - - RefPtr& operator=(const RefPtr& o) - { - RefPtr p = o; - swap(p); - return *this; - } - - RefPtr& operator=(RefPtr&& o) - { - RefPtr p = std::move(o); - swap(p); - return *this; - } - - template - RefPtr& operator=(const RefPtr& o) - { - RefPtr p = o; - swap(p); - return *this; - } - - template - RefPtr& operator=(RefPtr&& o) - { - RefPtr p = std::move(o); - swap(p); - return *this; - } - - void swap(RefPtr& 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 adoptPtr(T*); - -private: - RefPtr(T* ptr, std::nullptr_t) : m_ptr(ptr) {} - T* m_ptr{nullptr}; -}; - -template -inline RefPtr adoptPtr(T* ptr) -{ - return RefPtr(ptr, nullptr); -} - -template -inline void swap(RefPtr& a, RefPtr& b) -{ - a.swap(b); -} - -template -inline bool operator==(const RefPtr& a, const RefPtr& b) -{ - return a.get() == b.get(); -} - -template -inline bool operator==(const RefPtr& a, const U* b) -{ - return a.get() == b; -} - -template -inline bool operator==(const T* a, const RefPtr& b) -{ - return a == b.get(); -} - -template -inline bool operator==(const RefPtr& a, std::nullptr_t) -{ - return a.get() == nullptr; -} - -template -inline bool operator!=(const RefPtr& a, const RefPtr& b) -{ - return a.get() != b.get(); -} - -template -inline bool operator!=(const RefPtr& a, const U* b) -{ - return a.get() != b; -} - -template -inline bool operator!=(const T* a, const RefPtr& b) -{ - return a != b.get(); -} - -template -inline bool operator!=(const RefPtr& a, std::nullptr_t) -{ - return a.get() != nullptr; -} - -template -struct is { - template - static bool check(const U& value); -}; - -template -constexpr bool is_a(U& value) { - return is::check(value); -} - -template -constexpr bool is_a(const U& value) { - return is::check(value); -} - -template -constexpr bool is_a(U* value) { - return value && is::check(*value); -} - -template -constexpr bool is_a(const U* value) { - return value && is::check(*value); -} - -template -constexpr T* to(U& value) { - return is_a(value) ? static_cast(&value) : nullptr; -} - -template -constexpr const T* to(const U& value) { - return is_a(value) ? static_cast(&value) : nullptr; -} - -template -constexpr T* to(U* value) { - return is_a(value) ? static_cast(value) : nullptr; -} - -template -constexpr const T* to(const U* value) { - return is_a(value) ? static_cast(value) : nullptr; -} - -} // namespace lunasvg - -#endif // POINTER_H diff --git a/source/stopelement.cpp b/source/stopelement.cpp index d36ff7fe8..453138356 100644 --- a/source/stopelement.cpp +++ b/source/stopelement.cpp @@ -4,13 +4,13 @@ namespace lunasvg { StopElement::StopElement() - : StyledElement(ElementID::Stop) + : StyledElement(ElementId::Stop) { } double StopElement::offset() const { - auto& value = get(PropertyID::Offset); + auto& value = get(PropertyId::Offset); return Parser::parseNumberPercentage(value, 0.0); } diff --git a/source/styledelement.cpp b/source/styledelement.cpp index fda779d87..bfb5ad8f0 100644 --- a/source/styledelement.cpp +++ b/source/styledelement.cpp @@ -3,164 +3,164 @@ namespace lunasvg { -StyledElement::StyledElement(ElementID id) +StyledElement::StyledElement(ElementId id) : Element(id) { } Paint StyledElement::fill() const { - auto& value = find(PropertyID::Fill); + auto& value = find(PropertyId::Fill); return Parser::parsePaint(value, this, Color::Black); } Paint StyledElement::stroke() const { - auto& value = find(PropertyID::Stroke); + auto& value = find(PropertyId::Stroke); return Parser::parsePaint(value, this, Color::Transparent); } Color StyledElement::color() const { - auto& value = find(PropertyID::Color); + auto& value = find(PropertyId::Color); return Parser::parseColor(value, this, Color::Black); } Color StyledElement::stop_color() const { - auto& value = find(PropertyID::Stop_Color); + auto& value = find(PropertyId::Stop_Color); return Parser::parseColor(value, this, Color::Black); } Color StyledElement::solid_color() const { - auto& value = find(PropertyID::Solid_Color); + auto& value = find(PropertyId::Solid_Color); return Parser::parseColor(value, this, Color::Black); } double StyledElement::opacity() const { - auto& value = get(PropertyID::Opacity); + auto& value = get(PropertyId::Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::fill_opacity() const { - auto& value = find(PropertyID::Fill_Opacity); + auto& value = find(PropertyId::Fill_Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::stroke_opacity() const { - auto& value = find(PropertyID::Stroke_Opacity); + auto& value = find(PropertyId::Stroke_Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::stop_opacity() const { - auto& value = find(PropertyID::Stop_Opacity); + auto& value = find(PropertyId::Stop_Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::solid_opacity() const { - auto& value = find(PropertyID::Solid_Opacity); + auto& value = find(PropertyId::Solid_Opacity); return Parser::parseNumberPercentage(value, 1.0); } double StyledElement::stroke_miterlimit() const { - auto& value = find(PropertyID::Stroke_Miterlimit); + auto& value = find(PropertyId::Stroke_Miterlimit); return Parser::parseNumber(value, 4.0); } Length StyledElement::stroke_width() const { - auto& value = find(PropertyID::Stroke_Width); + auto& value = find(PropertyId::Stroke_Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::One); } Length StyledElement::stroke_dashoffset() const { - auto& value = find(PropertyID::Stroke_Dashoffset); + auto& value = find(PropertyId::Stroke_Dashoffset); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } LengthList StyledElement::stroke_dasharray() const { - auto& value = find(PropertyID::Stroke_Dasharray); + auto& value = find(PropertyId::Stroke_Dasharray); return Parser::parseLengthList(value, ForbidNegativeLengths); } WindRule StyledElement::fill_rule() const { - auto& value = find(PropertyID::Fill_Rule); + auto& value = find(PropertyId::Fill_Rule); return Parser::parseWindRule(value); } WindRule StyledElement::clip_rule() const { - auto& value = find(PropertyID::Clip_Rule); + auto& value = find(PropertyId::Clip_Rule); return Parser::parseWindRule(value); } LineCap StyledElement::stroke_linecap() const { - auto& value = find(PropertyID::Stroke_Linecap); + auto& value = find(PropertyId::Stroke_Linecap); return Parser::parseLineCap(value); } LineJoin StyledElement::stroke_linejoin() const { - auto& value = find(PropertyID::Stroke_Linejoin); + auto& value = find(PropertyId::Stroke_Linejoin); return Parser::parseLineJoin(value); } Display StyledElement::display() const { - auto& value = get(PropertyID::Display); + auto& value = get(PropertyId::Display); return Parser::parseDisplay(value); } Visibility StyledElement::visibility() const { - auto& value = find(PropertyID::Visibility); + auto& value = find(PropertyId::Visibility); return Parser::parseVisibility(value); } Overflow StyledElement::overflow() const { - auto& value = get(PropertyID::Overflow); + auto& value = get(PropertyId::Overflow); return Parser::parseOverflow(value, parent == nullptr ? Overflow::Visible : Overflow::Hidden); } std::string StyledElement::clip_path() const { - auto& value = get(PropertyID::Clip_Path); + auto& value = get(PropertyId::Clip_Path); return Parser::parseUrl(value); } std::string StyledElement::mask() const { - auto& value = get(PropertyID::Mask); + auto& value = get(PropertyId::Mask); return Parser::parseUrl(value); } std::string StyledElement::marker_start() const { - auto& value = find(PropertyID::Marker_Start); + auto& value = find(PropertyId::Marker_Start); return Parser::parseUrl(value); } std::string StyledElement::marker_mid() const { - auto& value = find(PropertyID::Marker_Mid); + auto& value = find(PropertyId::Marker_Mid); return Parser::parseUrl(value); } std::string StyledElement::marker_end() const { - auto& value = find(PropertyID::Marker_End); + auto& value = find(PropertyId::Marker_End); return Parser::parseUrl(value); } diff --git a/source/styledelement.h b/source/styledelement.h index 4d0ceb0a0..1257548ad 100644 --- a/source/styledelement.h +++ b/source/styledelement.h @@ -8,7 +8,7 @@ namespace lunasvg { class StyledElement : public Element { public: - StyledElement(ElementID id); + StyledElement(ElementId id); Paint fill() const; Paint stroke() const; diff --git a/source/styleelement.cpp b/source/styleelement.cpp index 3b1de5765..800146cf5 100644 --- a/source/styleelement.cpp +++ b/source/styleelement.cpp @@ -3,7 +3,7 @@ namespace lunasvg { StyleElement::StyleElement() - : Element(ElementID::Style) + : Element(ElementId::Style) { } diff --git a/source/svgelement.cpp b/source/svgelement.cpp index e4b1a1d82..1468045c8 100644 --- a/source/svgelement.cpp +++ b/source/svgelement.cpp @@ -5,47 +5,47 @@ namespace lunasvg { SVGElement::SVGElement() - : GraphicsElement(ElementID::Svg) + : GraphicsElement(ElementId::Svg) { } Length SVGElement::x() const { - auto& value = get(PropertyID::X); + auto& value = get(PropertyId::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length SVGElement::y() const { - auto& value = get(PropertyID::Y); + auto& value = get(PropertyId::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length SVGElement::width() const { - auto& value = get(PropertyID::Width); + auto& value = get(PropertyId::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Length SVGElement::height() const { - auto& value = get(PropertyID::Height); + auto& value = get(PropertyId::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Rect SVGElement::viewBox() const { - auto& value = get(PropertyID::ViewBox); + auto& value = get(PropertyId::ViewBox); return Parser::parseViewBox(value); } PreserveAspectRatio SVGElement::preserveAspectRatio() const { - auto& value = get(PropertyID::PreserveAspectRatio); + auto& value = get(PropertyId::PreserveAspectRatio); return Parser::parsePreserveAspectRatio(value); } -std::unique_ptr SVGElement::build(const TreeBuilder* builder) const +std::unique_ptr SVGElement::layoutDocument(const ParseDocument* document) const { if(isDisplayNone()) return nullptr; @@ -73,7 +73,7 @@ std::unique_ptr SVGElement::build(const TreeBuilder* builder) cons root->clip = isOverflowHidden() ? preserveAspectRatio.getClip(_w, _h, viewBox) : Rect::Invalid; root->opacity = opacity(); - LayoutContext context(builder, root.get()); + LayoutContext context(document, root.get()); root->masker = context.getMasker(mask()); root->clipper = context.getClipper(clip_path()); layoutChildren(&context, root.get()); diff --git a/source/svgelement.h b/source/svgelement.h index 789a7cad4..b6e62343e 100644 --- a/source/svgelement.h +++ b/source/svgelement.h @@ -5,7 +5,7 @@ namespace lunasvg { -class TreeBuilder; +class ParseDocument; class LayoutSymbol; class SVGElement : public GraphicsElement @@ -20,7 +20,7 @@ public: Rect viewBox() const; PreserveAspectRatio preserveAspectRatio() const; - std::unique_ptr build(const TreeBuilder* builder) const; + std::unique_ptr layoutDocument(const ParseDocument* document) const; void layout(LayoutContext* context, LayoutContainer* current) const; std::unique_ptr clone() const; diff --git a/source/symbolelement.cpp b/source/symbolelement.cpp index 03673d8a9..10d451cb7 100644 --- a/source/symbolelement.cpp +++ b/source/symbolelement.cpp @@ -4,43 +4,43 @@ namespace lunasvg { SymbolElement::SymbolElement() - : StyledElement(ElementID::Symbol) + : StyledElement(ElementId::Symbol) { } Length SymbolElement::x() const { - auto& value = get(PropertyID::X); + auto& value = get(PropertyId::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length SymbolElement::y() const { - auto& value = get(PropertyID::Y); + auto& value = get(PropertyId::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length SymbolElement::width() const { - auto& value = get(PropertyID::Width); + auto& value = get(PropertyId::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Length SymbolElement::height() const { - auto& value = get(PropertyID::Height); + auto& value = get(PropertyId::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Rect SymbolElement::viewBox() const { - auto& value = get(PropertyID::ViewBox); + auto& value = get(PropertyId::ViewBox); return Parser::parseViewBox(value); } PreserveAspectRatio SymbolElement::preserveAspectRatio() const { - auto& value = get(PropertyID::PreserveAspectRatio); + auto& value = get(PropertyId::PreserveAspectRatio); return Parser::parsePreserveAspectRatio(value); } diff --git a/source/useelement.cpp b/source/useelement.cpp index ced0ec004..f67383303 100644 --- a/source/useelement.cpp +++ b/source/useelement.cpp @@ -8,47 +8,47 @@ namespace lunasvg { UseElement::UseElement() - : GraphicsElement(ElementID::Use) + : GraphicsElement(ElementId::Use) { } Length UseElement::x() const { - auto& value = get(PropertyID::X); + auto& value = get(PropertyId::X); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length UseElement::y() const { - auto& value = get(PropertyID::Y); + auto& value = get(PropertyId::Y); return Parser::parseLength(value, AllowNegativeLengths, Length::Zero); } Length UseElement::width() const { - auto& value = get(PropertyID::Width); + auto& value = get(PropertyId::Width); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } Length UseElement::height() const { - auto& value = get(PropertyID::Height); + auto& value = get(PropertyId::Height); return Parser::parseLength(value, ForbidNegativeLengths, Length::HundredPercent); } std::string UseElement::href() const { - auto& value = get(PropertyID::Href); + auto& value = get(PropertyId::Href); return Parser::parseHref(value); } void UseElement::transferWidthAndHeight(Element* element) const { - auto& width = get(PropertyID::Width); - auto& height = get(PropertyID::Height); + auto& width = get(PropertyId::Width); + auto& height = get(PropertyId::Height); - element->set(PropertyID::Width, width, 0x0); - element->set(PropertyID::Height, height, 0x0); + element->set(PropertyId::Width, width, 0x0); + element->set(PropertyId::Height, height, 0x0); } void UseElement::layout(LayoutContext* context, LayoutContainer* current) const @@ -69,15 +69,15 @@ void UseElement::layout(LayoutContext* context, LayoutContainer* current) const auto _x = lengthContext.valueForLength(x(), LengthMode::Width); auto _y = lengthContext.valueForLength(y(), LengthMode::Height); - auto transform = get(PropertyID::Transform); + auto transform = get(PropertyId::Transform); transform += "translate("; transform += std::to_string(_x); transform += ' '; transform += std::to_string(_y); transform += ')'; - group->set(PropertyID::Transform, transform, 0x10); + group->set(PropertyId::Transform, transform, 0x10); - if(ref->id == ElementID::Svg || ref->id == ElementID::Symbol) + if(ref->id == ElementId::Svg || ref->id == ElementId::Symbol) { auto element = ref->cloneElement(); transferWidthAndHeight(element.get());