blob: b6efd6018902336003bab0d6219ea0ae22ed66f9 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/private/base/SkTPin.h"
#include "include/utils/SkParse.h"
#include "modules/svg/include/SkSVGAttributeParser.h"
#include "modules/svg/include/SkSVGTypes.h"
#include "src/base/SkUTF.h"
namespace {
// TODO: these should be shared with SkParse.cpp
inline bool is_between(char c, char min, char max) {
SkASSERT(min <= max);
return (unsigned)(c - min) <= (unsigned)(max - min);
}
inline bool is_ws(char c) {
return is_between(c, 1, 32);
}
inline bool is_sep(char c) {
return is_ws(c) || c == ',' || c == ';';
}
inline bool is_nl(char c) {
return c == '\n' || c == '\r' || c == '\f';
}
inline bool is_hex(char c) {
return is_between(c, 'a', 'f') ||
is_between(c, 'A', 'F') ||
is_between(c, '0', '9');
}
} // namespace
SkSVGAttributeParser::SkSVGAttributeParser(const char attributeString[])
// TODO: need actual UTF-8 with length.
: fCurPos(attributeString), fEndPos(fCurPos + strlen(attributeString)) {}
template <typename F>
inline bool SkSVGAttributeParser::advanceWhile(F f) {
auto initial = fCurPos;
while (fCurPos < fEndPos && f(*fCurPos)) {
fCurPos++;
}
return fCurPos != initial;
}
bool SkSVGAttributeParser::matchStringToken(const char* token, const char** newPos) const {
const char* c = fCurPos;
while (c < fEndPos && *token && *c == *token) {
c++;
token++;
}
if (*token) {
return false;
}
if (newPos) {
*newPos = c;
}
return true;
}
bool SkSVGAttributeParser::parseEOSToken() {
return fCurPos == fEndPos;
}
bool SkSVGAttributeParser::parseSepToken() {
return this->advanceWhile(is_sep);
}
bool SkSVGAttributeParser::parseWSToken() {
return this->advanceWhile(is_ws);
}
bool SkSVGAttributeParser::parseCommaWspToken() {
// comma-wsp:
// (wsp+ comma? wsp*) | (comma wsp*)
return this->parseWSToken() || this->parseExpectedStringToken(",");
}
bool SkSVGAttributeParser::parseExpectedStringToken(const char* expected) {
const char* newPos;
if (!matchStringToken(expected, &newPos)) {
return false;
}
fCurPos = newPos;
return true;
}
bool SkSVGAttributeParser::parseScalarToken(SkScalar* res) {
if (const char* next = SkParse::FindScalar(fCurPos, res)) {
fCurPos = next;
return true;
}
return false;
}
bool SkSVGAttributeParser::parseInt32Token(int32_t* res) {
if (const char* next = SkParse::FindS32(fCurPos, res)) {
fCurPos = next;
return true;
}
return false;
}
bool SkSVGAttributeParser::matchHexToken(const char** newPos) const {
*newPos = fCurPos;
while (*newPos < fEndPos && is_hex(**newPos)) { ++*newPos; }
return *newPos != fCurPos;
}
bool SkSVGAttributeParser::parseEscape(SkUnichar* c) {
// \(hexDigit{1,6}whitespace?|[^newline|hexDigit])
RestoreCurPos restoreCurPos(this);
if (!this->parseExpectedStringToken("\\")) {
return false;
}
const char* hexEnd;
if (this->matchHexToken(&hexEnd)) {
if (hexEnd - fCurPos > 6) {
hexEnd = fCurPos + 6;
}
char hexString[7];
size_t hexSize = hexEnd - fCurPos;
memcpy(hexString, fCurPos, hexSize);
hexString[hexSize] = '\0';
uint32_t cp;
const char* hexFound = SkParse::FindHex(hexString, &cp);
if (!hexFound || cp < 1 || (0xD800 <= cp && cp <= 0xDFFF) || 0x10FFFF < cp) {
cp = 0xFFFD;
}
*c = cp;
fCurPos = hexEnd;
this->parseWSToken();
} else if (this->parseEOSToken() || is_nl(*fCurPos)) {
*c = 0xFFFD;
return false;
} else {
if ((*c = SkUTF::NextUTF8(&fCurPos, fEndPos)) < 0) {
return false;
}
}
restoreCurPos.clear();
return true;
}
bool SkSVGAttributeParser::parseIdentToken(SkString* ident) {
// <ident-token>
// (--|-?([a-z|A-Z|_|non-ASCII]|escape))([a-z|A-Z|0-9|_|-|non-ASCII]|escape)?
RestoreCurPos restoreCurPos(this);
SkUnichar c;
if (this->parseExpectedStringToken("--")) {
ident->append("--");
} else {
if (this->parseExpectedStringToken("-")) {
ident->append("-");
}
if (this->parseEscape(&c)) {
ident->appendUnichar(c);
} else {
if ((c = SkUTF::NextUTF8(&fCurPos, fEndPos)) < 0) {
return false;
}
if ((c < 'a' || 'z' < c) &&
(c < 'A' || 'Z' < c) &&
(c != '_') &&
(c < 0x80 || 0x10FFFF < c))
{
return false;
}
ident->appendUnichar(c);
}
}
while (fCurPos < fEndPos) {
if (this->parseEscape(&c)) {
ident->appendUnichar(c);
continue;
}
const char* next = fCurPos;
if ((c = SkUTF::NextUTF8(&next, fEndPos)) < 0) {
break;
}
if ((c < 'a' || 'z' < c) &&
(c < 'A' || 'Z' < c) &&
(c < '0' || '9' < c) &&
(c != '_') &&
(c != '-') &&
(c < 0x80 || 0x10FFFF < c))
{
break;
}
ident->appendUnichar(c);
fCurPos = next;
}
restoreCurPos.clear();
return true;
}
bool SkSVGAttributeParser::parseLengthUnitToken(SkSVGLength::Unit* unit) {
static const struct {
const char* fUnitName;
SkSVGLength::Unit fUnit;
} gUnitInfo[] = {
{ "%" , SkSVGLength::Unit::kPercentage },
{ "em", SkSVGLength::Unit::kEMS },
{ "ex", SkSVGLength::Unit::kEXS },
{ "px", SkSVGLength::Unit::kPX },
{ "cm", SkSVGLength::Unit::kCM },
{ "mm", SkSVGLength::Unit::kMM },
{ "in", SkSVGLength::Unit::kIN },
{ "pt", SkSVGLength::Unit::kPT },
{ "pc", SkSVGLength::Unit::kPC },
};
for (size_t i = 0; i < std::size(gUnitInfo); ++i) {
if (this->parseExpectedStringToken(gUnitInfo[i].fUnitName)) {
*unit = gUnitInfo[i].fUnit;
return true;
}
}
return false;
}
// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
bool SkSVGAttributeParser::parseNamedColorToken(SkColor* c) {
RestoreCurPos restoreCurPos(this);
SkString ident;
if (!this->parseIdentToken(&ident)) {
return false;
}
if (!SkParse::FindNamedColor(ident.c_str(), ident.size(), c)) {
return false;
}
restoreCurPos.clear();
return true;
}
bool SkSVGAttributeParser::parseHexColorToken(SkColor* c) {
RestoreCurPos restoreCurPos(this);
const char* hexEnd;
if (!this->parseExpectedStringToken("#") || !this->matchHexToken(&hexEnd)) {
return false;
}
uint32_t v;
SkString hexString(fCurPos, hexEnd - fCurPos);
SkParse::FindHex(hexString.c_str(), &v);
switch (hexString.size()) {
case 6:
// matched #xxxxxxx
break;
case 3:
// matched '#xxx;
v = ((v << 12) & 0x00f00000) |
((v << 8) & 0x000ff000) |
((v << 4) & 0x00000ff0) |
((v << 0) & 0x0000000f);
break;
default:
return false;
}
*c = v | 0xff000000;
fCurPos = hexEnd;
restoreCurPos.clear();
return true;
}
bool SkSVGAttributeParser::parseColorComponentToken(int32_t* c) {
const auto parseIntegral = [this](int32_t* c) -> bool {
const char* p = SkParse::FindS32(fCurPos, c);
if (!p || *p == '.') {
// No value parsed, or fractional value.
return false;
}
if (*p == '%') {
*c = SkScalarRoundToInt(*c * 255.0f / 100);
p++;
}
fCurPos = p;
return true;
};
const auto parseFractional = [this](int32_t* c) -> bool {
SkScalar s;
const char* p = SkParse::FindScalar(fCurPos, &s);
if (!p || *p != '%') {
// Floating point must be a percentage (CSS2 rgb-percent syntax).
return false;
}
p++; // Skip '%'
*c = SkScalarRoundToInt(s * 255.0f / 100);
fCurPos = p;
return true;
};
if (!parseIntegral(c) && !parseFractional(c)) {
return false;
}
*c = SkTPin<int32_t>(*c, 0, 255);
return true;
}
bool SkSVGAttributeParser::parseRGBColorToken(SkColor* c) {
return this->parseParenthesized("rgb", [this](SkColor* c) -> bool {
int32_t r, g, b;
if (this->parseColorComponentToken(&r) &&
this->parseSepToken() &&
this->parseColorComponentToken(&g) &&
this->parseSepToken() &&
this->parseColorComponentToken(&b)) {
*c = SkColorSetRGB(static_cast<uint8_t>(r),
static_cast<uint8_t>(g),
static_cast<uint8_t>(b));
return true;
}
return false;
}, c);
}
bool SkSVGAttributeParser::parseColorToken(SkColor* c) {
return this->parseHexColorToken(c) ||
this->parseNamedColorToken(c) ||
this->parseRGBColorToken(c);
}
bool SkSVGAttributeParser::parseSVGColorType(SkSVGColorType* color) {
SkColor c;
if (!this->parseColorToken(&c)) {
return false;
}
*color = SkSVGColorType(c);
return true;
}
// https://www.w3.org/TR/SVG11/types.html#DataTypeColor
// And https://www.w3.org/TR/CSS2/syndata.html#color-units for the alternative
// forms supported by SVG (e.g. RGB percentages).
template <>
bool SkSVGAttributeParser::parse(SkSVGColorType* color) {
this->parseWSToken();
if (!this->parseSVGColorType(color)) {
return false;
}
this->parseWSToken();
return this->parseEOSToken();
}
bool SkSVGAttributeParser::parseSVGColor(SkSVGColor* color, SkSVGColor::Vars&& vars) {
static const constexpr int kVarsLimit = 32;
if (SkSVGColorType c; this->parseSVGColorType(&c)) {
*color = SkSVGColor(c, std::move(vars));
return true;
}
if (this->parseExpectedStringToken("currentColor")) {
*color = SkSVGColor(SkSVGColor::Type::kCurrentColor, std::move(vars));
return true;
}
// https://drafts.csswg.org/css-variables/#using-variables
if (this->parseParenthesized("var", [this, &vars](SkSVGColor* colorResult) -> bool {
SkString ident;
if (!this->parseIdentToken(&ident) || ident.size() < 2 || !ident.startsWith("--")) {
return false;
}
ident.remove(0, 2);
vars.push_back(std::move(ident));
this->parseWSToken();
if (!this->parseExpectedStringToken(",")) {
*colorResult = SkSVGColor(SK_ColorBLACK, std::move(vars));
return true;
}
this->parseWSToken();
if (this->matchStringToken(")")) {
*colorResult = SkSVGColor(SK_ColorBLACK, std::move(vars));
return true;
}
return vars.size() < kVarsLimit && this->parseSVGColor(colorResult, std::move(vars));
}, color))
{
return true;
}
return false;
}
// https://www.w3.org/TR/SVG11/types.html#InterfaceSVGColor
template <>
bool SkSVGAttributeParser::parse(SkSVGColor* color) {
this->parseWSToken();
if (!this->parseSVGColor(color, SkSVGColor::Vars())) {
return false;
}
this->parseWSToken();
return this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/linking.html#IRIReference
template <>
bool SkSVGAttributeParser::parse(SkSVGIRI* iri) {
// consume preceding whitespace
this->parseWSToken();
SkSVGIRI::Type iriType;
if (this->parseExpectedStringToken("#")) {
iriType = SkSVGIRI::Type::kLocal;
} else if (this->matchStringToken("data:")) {
iriType = SkSVGIRI::Type::kDataURI;
} else {
iriType = SkSVGIRI::Type::kNonlocal;
}
const auto* start = fCurPos;
if (!this->advanceWhile([](char c) -> bool { return c != ')'; })) {
return false;
}
*iri = SkSVGIRI(iriType, SkString(start, fCurPos - start));
return true;
}
// https://www.w3.org/TR/SVG11/types.html#DataTypeFuncIRI
bool SkSVGAttributeParser::parseFuncIRI(SkSVGFuncIRI* iri) {
return this->parseParenthesized("url", [this](SkSVGFuncIRI* iriResult) -> bool {
SkSVGIRI iri;
if (this->parse(&iri)) {
*iriResult = SkSVGFuncIRI(std::move(iri));
return true;
}
return false;
}, iri);
}
template <>
bool SkSVGAttributeParser::parse(SkSVGStringType* result) {
if (this->parseEOSToken()) {
return false;
}
*result = SkSVGStringType(fCurPos);
fCurPos += result->size();
return this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
template <>
bool SkSVGAttributeParser::parse(SkSVGNumberType* number) {
// consume WS
this->parseWSToken();
SkScalar s;
if (this->parseScalarToken(&s)) {
*number = SkSVGNumberType(s);
// consume trailing separators
this->parseSepToken();
return true;
}
return false;
}
// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
bool SkSVGAttributeParser::parseInteger(SkSVGIntegerType* number) {
// consume WS
this->parseWSToken();
// consume optional '+'
this->parseExpectedStringToken("+");
SkSVGIntegerType i;
if (this->parseInt32Token(&i)) {
*number = SkSVGNumberType(i);
// consume trailing separators
this->parseSepToken();
return true;
}
return false;
}
// https://www.w3.org/TR/SVG11/types.html#DataTypeLength
template <>
bool SkSVGAttributeParser::parse(SkSVGLength* length) {
SkScalar s;
SkSVGLength::Unit u = SkSVGLength::Unit::kNumber;
if (this->parseScalarToken(&s) &&
(this->parseLengthUnitToken(&u) || this->parseSepToken() || this->parseEOSToken())) {
*length = SkSVGLength(s, u);
// consume trailing separators
this->parseSepToken();
return true;
}
return false;
}
// https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
bool SkSVGAttributeParser::parseViewBox(SkSVGViewBoxType* vb) {
SkScalar x, y, w, h;
this->parseWSToken();
bool parsedValue = false;
if (this->parseScalarToken(&x) && this->parseSepToken() &&
this->parseScalarToken(&y) && this->parseSepToken() &&
this->parseScalarToken(&w) && this->parseSepToken() &&
this->parseScalarToken(&h)) {
*vb = SkSVGViewBoxType(SkRect::MakeXYWH(x, y, w, h));
parsedValue = true;
// consume trailing whitespace
this->parseWSToken();
}
return parsedValue && this->parseEOSToken();
}
template <typename Func, typename T>
bool SkSVGAttributeParser::parseParenthesized(const char* prefix, Func f, T* result) {
RestoreCurPos restoreCurPos(this);
this->parseWSToken();
if (prefix && !this->parseExpectedStringToken(prefix)) {
return false;
}
this->parseWSToken();
if (!this->parseExpectedStringToken("(")) {
return false;
}
this->parseWSToken();
if (!f(result)) {
return false;
}
this->parseWSToken();
if (!this->parseExpectedStringToken(")")) {
return false;
}
restoreCurPos.clear();
return true;
}
bool SkSVGAttributeParser::parseMatrixToken(SkMatrix* matrix) {
return this->parseParenthesized("matrix", [this](SkMatrix* m) -> bool {
SkScalar scalars[6];
for (int i = 0; i < 6; ++i) {
if (!(this->parseScalarToken(scalars + i) &&
(i > 4 || this->parseSepToken()))) {
return false;
}
}
m->setAll(scalars[0], scalars[2], scalars[4], scalars[1], scalars[3], scalars[5], 0, 0, 1);
return true;
}, matrix);
}
bool SkSVGAttributeParser::parseTranslateToken(SkMatrix* matrix) {
return this->parseParenthesized("translate", [this](SkMatrix* m) -> bool {
SkScalar tx = 0.0, ty = 0.0;
this->parseWSToken();
if (!this->parseScalarToken(&tx)) {
return false;
}
if (!this->parseSepToken() || !this->parseScalarToken(&ty)) {
ty = 0.0;
}
m->setTranslate(tx, ty);
return true;
}, matrix);
}
bool SkSVGAttributeParser::parseScaleToken(SkMatrix* matrix) {
return this->parseParenthesized("scale", [this](SkMatrix* m) -> bool {
SkScalar sx = 0.0, sy = 0.0;
if (!this->parseScalarToken(&sx)) {
return false;
}
if (!(this->parseSepToken() && this->parseScalarToken(&sy))) {
sy = sx;
}
m->setScale(sx, sy);
return true;
}, matrix);
}
bool SkSVGAttributeParser::parseRotateToken(SkMatrix* matrix) {
return this->parseParenthesized("rotate", [this](SkMatrix* m) -> bool {
SkScalar angle;
if (!this->parseScalarToken(&angle)) {
return false;
}
SkScalar cx = 0;
SkScalar cy = 0;
// optional [<cx> <cy>]
if (this->parseSepToken() && this->parseScalarToken(&cx)) {
if (!(this->parseSepToken() && this->parseScalarToken(&cy))) {
return false;
}
}
m->setRotate(angle, cx, cy);
return true;
}, matrix);
}
bool SkSVGAttributeParser::parseSkewXToken(SkMatrix* matrix) {
return this->parseParenthesized("skewX", [this](SkMatrix* m) -> bool {
SkScalar angle;
if (!this->parseScalarToken(&angle)) {
return false;
}
m->setSkewX(tanf(SkDegreesToRadians(angle)));
return true;
}, matrix);
}
bool SkSVGAttributeParser::parseSkewYToken(SkMatrix* matrix) {
return this->parseParenthesized("skewY", [this](SkMatrix* m) -> bool {
SkScalar angle;
if (!this->parseScalarToken(&angle)) {
return false;
}
m->setSkewY(tanf(SkDegreesToRadians(angle)));
return true;
}, matrix);
}
// https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
template <>
bool SkSVGAttributeParser::parse(SkSVGTransformType* t) {
SkMatrix matrix = SkMatrix::I();
bool parsed = false;
while (true) {
SkMatrix m;
if (!( this->parseMatrixToken(&m)
|| this->parseTranslateToken(&m)
|| this->parseScaleToken(&m)
|| this->parseRotateToken(&m)
|| this->parseSkewXToken(&m)
|| this->parseSkewYToken(&m))) {
break;
}
matrix.preConcat(m);
parsed = true;
this->parseCommaWspToken();
}
this->parseWSToken();
if (!parsed || !this->parseEOSToken()) {
return false;
}
*t = SkSVGTransformType(matrix);
return true;
}
// https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
template <>
bool SkSVGAttributeParser::parse(SkSVGPaint* paint) {
SkSVGColor c;
SkSVGFuncIRI iri;
bool parsedValue = false;
this->parseWSToken();
if (this->parseSVGColor(&c, SkSVGColor::Vars())) {
*paint = SkSVGPaint(std::move(c));
parsedValue = true;
} else if (this->parseExpectedStringToken("none")) {
*paint = SkSVGPaint(SkSVGPaint::Type::kNone);
parsedValue = true;
} else if (this->parseFuncIRI(&iri)) {
// optional fallback color
this->parseWSToken();
this->parseSVGColor(&c, SkSVGColor::Vars());
*paint = SkSVGPaint(iri.iri(), std::move(c));
parsedValue = true;
}
this->parseWSToken();
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/masking.html#ClipPathProperty
// https://www.w3.org/TR/SVG11/masking.html#MaskProperty
// https://www.w3.org/TR/SVG11/filters.html#FilterProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGFuncIRI* firi) {
SkSVGStringType iri;
bool parsedValue = false;
if (this->parseExpectedStringToken("none")) {
*firi = SkSVGFuncIRI();
parsedValue = true;
} else if (this->parseFuncIRI(firi)) {
parsedValue = true;
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/painting.html#StrokeLinecapProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGLineCap* cap) {
static const struct {
SkSVGLineCap fType;
const char* fName;
} gCapInfo[] = {
{ SkSVGLineCap::kButt , "butt" },
{ SkSVGLineCap::kRound , "round" },
{ SkSVGLineCap::kSquare , "square" },
};
bool parsedValue = false;
for (size_t i = 0; i < std::size(gCapInfo); ++i) {
if (this->parseExpectedStringToken(gCapInfo[i].fName)) {
*cap = SkSVGLineCap(gCapInfo[i].fType);
parsedValue = true;
break;
}
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/painting.html#StrokeLinejoinProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGLineJoin* join) {
static const struct {
SkSVGLineJoin::Type fType;
const char* fName;
} gJoinInfo[] = {
{ SkSVGLineJoin::Type::kMiter , "miter" },
{ SkSVGLineJoin::Type::kRound , "round" },
{ SkSVGLineJoin::Type::kBevel , "bevel" },
{ SkSVGLineJoin::Type::kInherit, "inherit" },
};
bool parsedValue = false;
for (size_t i = 0; i < std::size(gJoinInfo); ++i) {
if (this->parseExpectedStringToken(gJoinInfo[i].fName)) {
*join = SkSVGLineJoin(gJoinInfo[i].fType);
parsedValue = true;
break;
}
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits
template <>
bool SkSVGAttributeParser::parse(SkSVGObjectBoundingBoxUnits* objectBoundingBoxUnits) {
bool parsedValue = false;
if (this->parseExpectedStringToken("userSpaceOnUse")) {
*objectBoundingBoxUnits =
SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse);
parsedValue = true;
} else if (this->parseExpectedStringToken("objectBoundingBox")) {
*objectBoundingBoxUnits =
SkSVGObjectBoundingBoxUnits(SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox);
parsedValue = true;
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/shapes.html#PolygonElementPointsAttribute
template <>
bool SkSVGAttributeParser::parse(SkSVGPointsType* points) {
SkTDArray<SkPoint> pts;
// Skip initial wsp.
// list-of-points:
// wsp* coordinate-pairs? wsp*
this->advanceWhile(is_ws);
bool parsedValue = false;
for (;;) {
// Adjacent coordinate-pairs separated by comma-wsp.
// coordinate-pairs:
// coordinate-pair
// | coordinate-pair comma-wsp coordinate-pairs
if (parsedValue && !this->parseCommaWspToken()) {
break;
}
SkScalar x, y;
if (!this->parseScalarToken(&x)) {
break;
}
// Coordinate values separated by comma-wsp or '-'.
// coordinate-pair:
// coordinate comma-wsp coordinate
// | coordinate negative-coordinate
if (!this->parseCommaWspToken() && !this->parseEOSToken() && *fCurPos != '-') {
break;
}
if (!this->parseScalarToken(&y)) {
break;
}
pts.push_back(SkPoint::Make(x, y));
parsedValue = true;
}
if (parsedValue && this->parseEOSToken()) {
*points = pts;
return true;
}
return false;
}
// https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGFillRule* fillRule) {
static const struct {
SkSVGFillRule::Type fType;
const char* fName;
} gFillRuleInfo[] = {
{ SkSVGFillRule::Type::kNonZero, "nonzero" },
{ SkSVGFillRule::Type::kEvenOdd, "evenodd" },
{ SkSVGFillRule::Type::kInherit, "inherit" },
};
bool parsedValue = false;
for (size_t i = 0; i < std::size(gFillRuleInfo); ++i) {
if (this->parseExpectedStringToken(gFillRuleInfo[i].fName)) {
*fillRule = SkSVGFillRule(gFillRuleInfo[i].fType);
parsedValue = true;
break;
}
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGVisibility* visibility) {
static const struct {
SkSVGVisibility::Type fType;
const char* fName;
} gVisibilityInfo[] = {
{ SkSVGVisibility::Type::kVisible , "visible" },
{ SkSVGVisibility::Type::kHidden , "hidden" },
{ SkSVGVisibility::Type::kCollapse, "collapse" },
{ SkSVGVisibility::Type::kInherit , "inherit" },
};
bool parsedValue = false;
for (const auto& parseInfo : gVisibilityInfo) {
if (this->parseExpectedStringToken(parseInfo.fName)) {
*visibility = SkSVGVisibility(parseInfo.fType);
parsedValue = true;
break;
}
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGDashArray* dashArray) {
bool parsedValue = false;
if (this->parseExpectedStringToken("none")) {
*dashArray = SkSVGDashArray(SkSVGDashArray::Type::kNone);
parsedValue = true;
} else if (this->parseExpectedStringToken("inherit")) {
*dashArray = SkSVGDashArray(SkSVGDashArray::Type::kInherit);
parsedValue = true;
} else {
SkTDArray<SkSVGLength> dashes;
for (;;) {
SkSVGLength dash;
// parseLength() also consumes trailing separators.
if (!this->parse(&dash)) {
break;
}
dashes.push_back(dash);
parsedValue = true;
}
if (parsedValue) {
*dashArray = SkSVGDashArray(std::move(dashes));
}
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/text.html#FontFamilyProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGFontFamily* family) {
bool parsedValue = false;
if (this->parseExpectedStringToken("inherit")) {
*family = SkSVGFontFamily();
parsedValue = true;
} else {
// The spec allows specifying a comma-separated list for explicit fallback order.
// For now, we only use the first entry and rely on the font manager to handle fallback.
const auto* comma = strchr(fCurPos, ',');
auto family_name = comma ? SkString(fCurPos, comma - fCurPos)
: SkString(fCurPos);
*family = SkSVGFontFamily(family_name.c_str());
fCurPos += strlen(fCurPos);
parsedValue = true;
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/text.html#FontSizeProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGFontSize* size) {
bool parsedValue = false;
if (this->parseExpectedStringToken("inherit")) {
*size = SkSVGFontSize();
parsedValue = true;
} else {
SkSVGLength length;
if (this->parse(&length)) {
*size = SkSVGFontSize(length);
parsedValue = true;
}
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/text.html#FontStyleProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGFontStyle* style) {
static constexpr std::tuple<const char*, SkSVGFontStyle::Type> gStyleMap[] = {
{ "normal" , SkSVGFontStyle::Type::kNormal },
{ "italic" , SkSVGFontStyle::Type::kItalic },
{ "oblique", SkSVGFontStyle::Type::kOblique },
{ "inherit", SkSVGFontStyle::Type::kInherit },
};
bool parsedValue = false;
SkSVGFontStyle::Type type;
if (this->parseEnumMap(gStyleMap, &type)) {
*style = SkSVGFontStyle(type);
parsedValue = true;
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/text.html#FontWeightProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGFontWeight* weight) {
static constexpr std::tuple<const char*, SkSVGFontWeight::Type> gWeightMap[] = {
{ "normal" , SkSVGFontWeight::Type::kNormal },
{ "bold" , SkSVGFontWeight::Type::kBold },
{ "bolder" , SkSVGFontWeight::Type::kBolder },
{ "lighter", SkSVGFontWeight::Type::kLighter },
{ "100" , SkSVGFontWeight::Type::k100 },
{ "200" , SkSVGFontWeight::Type::k200 },
{ "300" , SkSVGFontWeight::Type::k300 },
{ "400" , SkSVGFontWeight::Type::k400 },
{ "500" , SkSVGFontWeight::Type::k500 },
{ "600" , SkSVGFontWeight::Type::k600 },
{ "700" , SkSVGFontWeight::Type::k700 },
{ "800" , SkSVGFontWeight::Type::k800 },
{ "900" , SkSVGFontWeight::Type::k900 },
{ "inherit", SkSVGFontWeight::Type::kInherit },
};
bool parsedValue = false;
SkSVGFontWeight::Type type;
if (this->parseEnumMap(gWeightMap, &type)) {
*weight = SkSVGFontWeight(type);
parsedValue = true;
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/text.html#TextAnchorProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGTextAnchor* anchor) {
static constexpr std::tuple<const char*, SkSVGTextAnchor::Type> gAnchorMap[] = {
{ "start" , SkSVGTextAnchor::Type::kStart },
{ "middle" , SkSVGTextAnchor::Type::kMiddle },
{ "end" , SkSVGTextAnchor::Type::kEnd },
{ "inherit", SkSVGTextAnchor::Type::kInherit},
};
bool parsedValue = false;
SkSVGTextAnchor::Type type;
if (this->parseEnumMap(gAnchorMap, &type)) {
*anchor = SkSVGTextAnchor(type);
parsedValue = true;
}
return parsedValue && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/coords.html#PreserveAspectRatioAttribute
bool SkSVGAttributeParser::parsePreserveAspectRatio(SkSVGPreserveAspectRatio* par) {
static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Align> gAlignMap[] = {
{ "none" , SkSVGPreserveAspectRatio::kNone },
{ "xMinYMin", SkSVGPreserveAspectRatio::kXMinYMin },
{ "xMidYMin", SkSVGPreserveAspectRatio::kXMidYMin },
{ "xMaxYMin", SkSVGPreserveAspectRatio::kXMaxYMin },
{ "xMinYMid", SkSVGPreserveAspectRatio::kXMinYMid },
{ "xMidYMid", SkSVGPreserveAspectRatio::kXMidYMid },
{ "xMaxYMid", SkSVGPreserveAspectRatio::kXMaxYMid },
{ "xMinYMax", SkSVGPreserveAspectRatio::kXMinYMax },
{ "xMidYMax", SkSVGPreserveAspectRatio::kXMidYMax },
{ "xMaxYMax", SkSVGPreserveAspectRatio::kXMaxYMax },
};
static constexpr std::tuple<const char*, SkSVGPreserveAspectRatio::Scale> gScaleMap[] = {
{ "meet" , SkSVGPreserveAspectRatio::kMeet },
{ "slice", SkSVGPreserveAspectRatio::kSlice },
};
bool parsedValue = false;
// ignoring optional 'defer'
this->parseExpectedStringToken("defer");
this->parseWSToken();
if (this->parseEnumMap(gAlignMap, &par->fAlign)) {
parsedValue = true;
// optional scaling selector
this->parseWSToken();
this->parseEnumMap(gScaleMap, &par->fScale);
}
return parsedValue && this->parseEOSToken();
}
template <>
bool SkSVGAttributeParser::parse(SkSVGPreserveAspectRatio* par) {
return this->parsePreserveAspectRatio(par);
}
// https://www.w3.org/TR/SVG11/types.html#DataTypeCoordinates
template <typename T>
bool SkSVGAttributeParser::parseList(std::vector<T>* vals) {
SkASSERT(vals->empty());
T v;
for (;;) {
if (!this->parse(&v)) {
break;
}
vals->push_back(v);
this->parseCommaWspToken();
}
return !vals->empty() && this->parseEOSToken();
}
template <>
bool SkSVGAttributeParser::parse(std::vector<SkSVGLength>* lengths) {
return this->parseList(lengths);
}
template <>
bool SkSVGAttributeParser::parse(std::vector<SkSVGNumberType>* numbers) {
return this->parseList(numbers);
}
template <>
bool SkSVGAttributeParser::parse(SkSVGColorspace* colorspace) {
static constexpr std::tuple<const char*, SkSVGColorspace> gColorspaceMap[] = {
{ "auto" , SkSVGColorspace::kAuto },
{ "sRGB" , SkSVGColorspace::kSRGB },
{ "linearRGB", SkSVGColorspace::kLinearRGB },
};
return this->parseEnumMap(gColorspaceMap, colorspace) && this->parseEOSToken();
}
// https://www.w3.org/TR/SVG11/painting.html#DisplayProperty
template <>
bool SkSVGAttributeParser::parse(SkSVGDisplay* display) {
static const struct {
SkSVGDisplay fType;
const char* fName;
} gDisplayInfo[] = {
{ SkSVGDisplay::kInline, "inline" },
{ SkSVGDisplay::kNone , "none" },
};
bool parsedValue = false;
for (const auto& parseInfo : gDisplayInfo) {
if (this->parseExpectedStringToken(parseInfo.fName)) {
*display = SkSVGDisplay(parseInfo.fType);
parsedValue = true;
break;
}
}
return parsedValue && this->parseEOSToken();
}