diff --git a/include/tidyenum.h b/include/tidyenum.h index 3daee5b38..fe5363e6f 100644 --- a/include/tidyenum.h +++ b/include/tidyenum.h @@ -1343,6 +1343,22 @@ typedef enum TidyAttr_XMLNSXLINK, /**< svg xmls:xlink="url" */ + /* SVG paint attributes (SVG 1.1) */ + TidyAttr_FILL, /**< FILL= */ + TidyAttr_FILLRULE, /**< FILLRULE= */ + TidyAttr_STROKE, /**< STROKE= */ + TidyAttr_STROKEDASHARRAY, /**< STROKEDASHARRAY= */ + TidyAttr_STROKEDASHOFFSET, /**< STROKEDASHOFFSET= */ + TidyAttr_STROKELINECAP, /**< STROKELINECAP= */ + TidyAttr_STROKELINEJOIN, /**< STROKELINEJOIN= */ + TidyAttr_STROKEMITERLIMIT, /**< STROKEMITERLIMIT= */ + TidyAttr_STROKEWIDTH, /**< STROKEWIDTH= */ + TidyAttr_COLORINTERPOLATION, /**< COLORINTERPOLATION= */ + TidyAttr_COLORRENDERING, /**< COLORRENDERING= */ + TidyAttr_OPACITY, /**< OPACITY= */ + TidyAttr_STROKEOPACITY, /**< STROKEOPACITY= */ + TidyAttr_FILLOPACITY, /**< FILLOPACITY= */ + N_TIDY_ATTRIBS /**< Must be last */ } TidyAttrId; diff --git a/src/attrdict.c b/src/attrdict.c index 23322d1a0..0ee3495ec 100644 --- a/src/attrdict.c +++ b/src/attrdict.c @@ -3102,6 +3102,21 @@ const AttrVersion TY_(W3CAttrsFor_SVG)[] = { TidyAttr_BASEPROFILE, xxxx|xxxx|H40T|H41T|X10T|H40F|H41F|X10F|xxxx|H41S|X10S|XH11|xxxx|HT50|XH50 }, { TidyAttr_CONTENTSCRIPTTYPE, xxxx|xxxx|H40T|H41T|X10T|H40F|H41F|X10F|xxxx|H41S|X10S|XH11|xxxx|HT50|XH50 }, { TidyAttr_CONTENTSTYLETYPE, xxxx|xxxx|H40T|H41T|X10T|H40F|H41F|X10F|xxxx|H41S|X10S|XH11|xxxx|HT50|XH50 }, + { TidyAttr_COLOR, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_FILL, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_FILLRULE, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_STROKE, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_STROKEDASHARRAY, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_STROKEDASHOFFSET, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_STROKELINECAP, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_STROKELINEJOIN, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_STROKEMITERLIMIT, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_STROKEWIDTH, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_COLORINTERPOLATION, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_COLORRENDERING, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_OPACITY, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_STROKEOPACITY, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, + { TidyAttr_FILLOPACITY, xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|XH11|xxxx|HT50|XH50 }, INCLUDE_CORE_ATTRIBS INCLUDE_CORE_EVENTS INCLUDE_RDFA diff --git a/src/attrs.c b/src/attrs.c index 4231935b8..11c2c9ff2 100644 --- a/src/attrs.c +++ b/src/attrs.c @@ -51,6 +51,8 @@ static AttrCheck CheckType; static AttrCheck CheckRDFaSafeCURIE; static AttrCheck CheckRDFaTerm; static AttrCheck CheckRDFaPrefix; +static AttrCheck CheckDecimal; +static AttrCheck CheckSvgAttr; #define CH_PCDATA NULL #define CH_CHARSET NULL @@ -95,6 +97,8 @@ static AttrCheck CheckRDFaPrefix; #define CH_RDFASCURIES CheckRDFaSafeCURIE #define CH_RDFATERM CheckRDFaTerm #define CH_RDFATERMS CheckRDFaTerm +#define CH_DECIMAL CheckDecimal +#define CH_SVG CheckSvgAttr static const Attribute attribute_defs [] = { @@ -440,6 +444,22 @@ static const Attribute attribute_defs [] = /* for xmlns:xlink in */ { TidyAttr_XMLNSXLINK, "xmlns:xlink", CH_URL }, + /* SVG paint attributes (SVG 1.1) */ + { TidyAttr_FILL, "fill", CH_SVG }, + { TidyAttr_FILLRULE, "fill-rule", CH_SVG }, + { TidyAttr_STROKE, "stroke", CH_SVG }, + { TidyAttr_STROKEDASHARRAY, "stroke-dasharray", CH_SVG }, + { TidyAttr_STROKEDASHOFFSET, "stroke-dashoffset", CH_SVG }, + { TidyAttr_STROKELINECAP, "stroke-linecap", CH_SVG }, + { TidyAttr_STROKELINEJOIN, "stroke-linejoin", CH_SVG }, + { TidyAttr_STROKEMITERLIMIT, "stroke-miterlimit", CH_SVG }, + { TidyAttr_STROKEWIDTH, "stroke-width", CH_SVG }, + { TidyAttr_COLORINTERPOLATION, "color-interpolation", CH_SVG }, + { TidyAttr_COLORRENDERING, "color-rendering", CH_SVG }, + { TidyAttr_OPACITY, "opacity", CH_SVG }, + { TidyAttr_STROKEOPACITY, "stroke-opacity", CH_SVG }, + { TidyAttr_FILLOPACITY, "fill-opacity", CH_SVG }, + /* this must be the final entry */ { N_TIDY_ATTRIBS, NULL, NULL } }; @@ -2088,6 +2108,180 @@ void CheckType( TidyDocImpl* doc, Node *node, AttVal *attval) return; } +static void CheckDecimal( TidyDocImpl* doc, Node *node, AttVal *attval) +{ + tmbstr p; + Bool hasPoint = no; + + p = attval->value; + + /* Allow leading sign */ + if (*p == '+' || *p == '-') + ++p; + + while (*p) + { + /* Allow a single decimal point */ + if (*p == '.') + { + if (!hasPoint) + hasPoint = yes; + else + TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE); + break; + } + + if (!TY_(IsDigit)(*p)) + { + TY_(ReportAttrError)( doc, node, attval, BAD_ATTRIBUTE_VALUE); + break; + } + ++p; + } +} + +static Bool IsSvgPaintAttr(AttVal *attval) +{ + return attrIsCOLOR(attval) + || attrIsSVG_FILL(attval) + || attrIsSVG_FILLRULE(attval) + || attrIsSVG_STROKE(attval) + || attrIsSVG_STROKEDASHARRAY(attval) + || attrIsSVG_STROKEDASHOFFSET(attval) + || attrIsSVG_STROKELINECAP(attval) + || attrIsSVG_STROKELINEJOIN(attval) + || attrIsSVG_STROKEMITERLIMIT(attval) + || attrIsSVG_STROKEWIDTH(attval) + || attrIsSVG_COLORINTERPOLATION(attval) + || attrIsSVG_COLORRENDERING(attval) + || attrIsSVG_OPACITY(attval) + || attrIsSVG_STROKEOPACITY(attval) + || attrIsSVG_FILLOPACITY(attval); +} + +/* Check SVG attributes */ +static void CheckSvgAttr( TidyDocImpl* doc, Node *node, AttVal *attval) +{ + if (!nodeIsSVG(node)) + { + TY_(ReportAttrError)(doc, node, attval, ATTRIBUTE_IS_NOT_ALLOWED); + return; + } + + /* Issue #903 - check SVG paint attributes */ + if (IsSvgPaintAttr(attval)) + { + /* all valid paint attributes have values */ + if (!AttrHasValue(attval)) + { + TY_(ReportAttrError)(doc, node, attval, MISSING_ATTR_VALUE); + return; + } + /* all paint attributes support an 'inherit' value, + per https://dev.w3.org/SVG/profiles/1.1F2/publish/painting.html#SpecifyingPaint */ + if (AttrValueIs(attval, "inherit")) + { + return; + } + + /* check paint datatypes + see https://dev.w3.org/SVG/profiles/1.1F2/publish/painting.html#SpecifyingPaint + */ + if (attrIsSVG_FILL(attval) || attrIsSVG_STROKE(attval)) + { + /* TODO: support funciri */ + static ctmbstr const values[] = { + "none", "currentColor", NULL}; + + if (AttrValueIsAmong(attval, values)) + CheckLowerCaseAttrValue(doc, node, attval); + else + CheckColor(doc, node, attval); + } + else if (attrIsSVG_FILLRULE(attval)) + { + static ctmbstr const values[] = {"nonzero", "evenodd", NULL}; + + if (AttrValueIsAmong(attval, values)) + CheckLowerCaseAttrValue(doc, node, attval); + else + TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE); + } + else if (attrIsSVG_STROKEDASHARRAY(attval)) + { + static ctmbstr const values[] = {"none", NULL}; + + if (AttrValueIsAmong(attval, values)) + CheckLowerCaseAttrValue(doc, node, attval); + else + { + /* TODO: process dash arrays */ + } + } + else if (attrIsSVG_STROKEDASHOFFSET(attval)) + { + CheckLength(doc, node, attval); + } + else if (attrIsSVG_STROKELINECAP(attval)) + { + static ctmbstr const values[] = {"butt", "round", "square", NULL}; + + if (AttrValueIsAmong(attval, values)) + CheckLowerCaseAttrValue(doc, node, attval); + else + TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE); + } + else if (attrIsSVG_STROKELINEJOIN(attval)) + { + static ctmbstr const values[] = {"miter", "round", "bevel", NULL}; + + if (AttrValueIsAmong(attval, values)) + CheckLowerCaseAttrValue(doc, node, attval); + else + TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE); + } + else if (attrIsSVG_STROKEMITERLIMIT(attval)) + { + CheckNumber(doc, node, attval); + } + else if (attrIsSVG_STROKEWIDTH(attval)) + { + CheckLength(doc, node, attval); + } + else if (attrIsSVG_COLORINTERPOLATION(attval)) + { + static ctmbstr const values[] = {"auto", "sRGB", "linearRGB", NULL}; + + if (AttrValueIsAmong(attval, values)) + CheckLowerCaseAttrValue(doc, node, attval); + else + TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE); + } + else if (attrIsSVG_COLORRENDERING(attval)) + { + static ctmbstr const values[] = { + "auto", "optimizeSpeed", "optimizeQuality", NULL}; + + if (AttrValueIsAmong(attval, values)) + CheckLowerCaseAttrValue(doc, node, attval); + else + TY_(ReportAttrError)(doc, node, attval, BAD_ATTRIBUTE_VALUE); + } + else if(attrIsSVG_OPACITY(attval)) + { + CheckDecimal(doc, node, attval); + } + else if(attrIsSVG_STROKEOPACITY(attval)) + { + CheckDecimal(doc, node, attval); + } + else if(attrIsSVG_FILLOPACITY(attval)) + { + CheckDecimal(doc, node, attval); + } + } +} + static AttVal *SortAttVal( TidyDocImpl* doc, AttVal* list, TidyAttrSortStrategy strat ); diff --git a/src/attrs.h b/src/attrs.h index befdcd125..418c5ac03 100644 --- a/src/attrs.h +++ b/src/attrs.h @@ -374,8 +374,20 @@ Bool TY_(AttributeIsMismatched)(Node* node, AttVal* attval, TidyDocImpl* doc); #define attrIsARIA_VALUEMIN(av) AttrIsId( av, TidyAttr_ARIA_VALUEMIN ) #define attrIsARIA_VALUENOW(av) AttrIsId( av, TidyAttr_ARIA_VALUENOW ) #define attrIsARIA_VALUETEXT(av) AttrIsId( av, TidyAttr_ARIA_VALUETEXT ) - - +#define attrIsSVG_FILL(av) AttrIsId( av, TidyAttr_FILL ) +#define attrIsSVG_FILLRULE(av) AttrIsId( av, TidyAttr_FILLRULE ) +#define attrIsSVG_STROKE(av) AttrIsId( av, TidyAttr_STROKE ) +#define attrIsSVG_STROKEDASHARRAY(av) AttrIsId( av, TidyAttr_STROKEDASHARRAY ) +#define attrIsSVG_STROKEDASHOFFSET(av) AttrIsId( av, TidyAttr_STROKEDASHOFFSET ) +#define attrIsSVG_STROKELINECAP(av) AttrIsId( av, TidyAttr_STROKELINECAP ) +#define attrIsSVG_STROKELINEJOIN(av) AttrIsId( av, TidyAttr_STROKELINEJOIN ) +#define attrIsSVG_STROKEMITERLIMIT(av) AttrIsId( av, TidyAttr_STROKEMITERLIMIT ) +#define attrIsSVG_STROKEWIDTH(av) AttrIsId( av, TidyAttr_STROKEWIDTH ) +#define attrIsSVG_COLORINTERPOLATION(a) AttrIsId( a, TidyAttr_COLORINTERPOLATION ) +#define attrIsSVG_COLORRENDERING(av) AttrIsId( av, TidyAttr_COLORRENDERING ) +#define attrIsSVG_OPACITY(av) AttrIsId( av, TidyAttr_OPACITY ) +#define attrIsSVG_STROKEOPACITY(av) AttrIsId( av, TidyAttr_STROKEOPACITY ) +#define attrIsSVG_FILLOPACITY(av) AttrIsId( av, TidyAttr_FILLOPACITY ) /* Attribute Retrieval macros */ diff --git a/src/tags.h b/src/tags.h index 3faaea3d0..744e3f02f 100644 --- a/src/tags.h +++ b/src/tags.h @@ -452,6 +452,8 @@ uint TY_(nodeHeaderLevel)( Node* node ); #define nodeIsINS( node ) TagIsId( node, TidyTag_INS ) #define nodeIsDEL( node ) TagIsId( node, TidyTag_DEL ) +#define nodeIsSVG( node ) TagIsId( node, TidyTag_SVG ) + /* HTML5 */ #define nodeIsDATALIST( node ) TagIsId( node, TidyTag_DATALIST ) #define nodeIsDATA( node ) TagIsId( node, TidyTag_DATA )