Add SVG paint attributes (#907)

Fixes #903
This commit is contained in:
Caleb Callaway 2020-11-22 09:02:00 -08:00 committed by GitHub
parent 3202273421
commit 91ae1274ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 241 additions and 2 deletions

View file

@ -1346,6 +1346,22 @@ typedef enum
TidyAttr_SLOT, /**< SLOT= */
TidyAttr_LOADING, /**< LOADING= */
/* 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;

View file

@ -3116,6 +3116,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

View file

@ -52,6 +52,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
@ -97,6 +99,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 [] =
{
@ -444,6 +448,22 @@ static const Attribute attribute_defs [] =
/* for xmlns:xlink in <svg> */
{ 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 }
};
@ -2099,6 +2119,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 );

View file

@ -375,8 +375,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
*/

View file

@ -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 )