Skip to content

Add SVG paint attributes #907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions include/tidyenum.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
15 changes: 15 additions & 0 deletions src/attrdict.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
194 changes: 194 additions & 0 deletions src/attrs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 [] =
{
Expand Down Expand Up @@ -440,6 +444,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 }
};
Expand Down Expand Up @@ -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 );

Expand Down
16 changes: 14 additions & 2 deletions src/attrs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
2 changes: 2 additions & 0 deletions src/tags.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down