svg: clean & neat code++

no logical changes
This commit is contained in:
Hermet Park 2025-03-17 17:35:34 +09:00
parent 468a1db1fa
commit ad888019e9
3 changed files with 275 additions and 372 deletions

View file

@ -45,9 +45,12 @@
#define STR_AS(A, B) !strcmp((A), (B))
typedef bool (*parseAttributes)(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data);
typedef bool (*parseAttributes)(const char* buf, unsigned bufLength, xmlAttributeCb func, const void* data);
typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func);
typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
static bool _parseStyleAttr(void* data, const char* key, const char* value);
static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style);
static char* _skipSpace(const char* str, const char* end)
{
@ -62,7 +65,6 @@ static char* _copyId(const char* str)
{
if (!str) return nullptr;
if (strlen(str) == 0) return nullptr;
return duplicate(str);
}
@ -77,7 +79,7 @@ static const char* _skipComma(const char* content)
static bool _parseNumber(const char** content, const char** end, float* number)
{
const char* _end = end ? *end : nullptr;
auto _end = end ? *end : nullptr;
*number = toFloat(*content, (char**)&_end);
//If the start of string is not number
@ -166,7 +168,7 @@ static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool&
{
char* end = nullptr;
float parsedValue = toFloat(str, &end);
auto parsedValue = toFloat(str, &end);
isPercentage = false;
if (strstr(str, "%")) {
@ -189,7 +191,7 @@ static float _toOffset(const char* str)
char* end = nullptr;
auto strEnd = str + strlen(str);
float parsedValue = toFloat(str, &end);
auto parsedValue = toFloat(str, &end);
end = _skipSpace(end, nullptr);
auto ptr = strstr(str, "%");
@ -206,7 +208,7 @@ static float _toOffset(const char* str)
static int _toOpacity(const char* str)
{
char* end = nullptr;
float opacity = toFloat(str, &end);
auto opacity = toFloat(str, &end);
if (end) {
if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
@ -228,9 +230,9 @@ static SvgMaskType _toMaskType(const char* str)
//If any is omitted, will be rendered in its default order after the specified ones.
static bool _toPaintOrder(const char* str)
{
uint8_t position = 1;
uint8_t strokePosition = 0;
uint8_t fillPosition = 0;
auto position = 1;
auto strokePosition = 0;
auto fillPosition = 0;
while (*str != '\0') {
str = _skipSpace(str, nullptr);
@ -334,7 +336,7 @@ static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* das
while (*str) {
str = _skipComma(str);
float parsedValue = toFloat(str, &end);
auto parsedValue = toFloat(str, &end);
if (str == end) break;
if (parsedValue <= 0.0f) break;
if (*end == '%') {
@ -398,9 +400,7 @@ static size_t _srcFromUrl(const char* url, char*& src)
static unsigned char _parseColor(const char* value, char** end)
{
float r;
r = toFloat(value, end);
auto r = toFloat(value, end);
*end = _skipSpace(*end, nullptr);
if (**end == '%') {
r = 255 * r / 100;
@ -574,79 +574,77 @@ static constexpr struct
static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* red, uint8_t* green, uint8_t* blue)
{
if (!red || !green || !blue) return false;
float sv, vsf, f, p, q, t, v;
float _red = 0, _green = 0, _blue = 0;
uint32_t i = 0;
auto r = 0.0f, g = 0.0f, b = 0.0f;
auto i = 0;
while (hue < 0) hue += 360.0f;
hue = fmod(hue, 360.0f);
saturation = saturation > 0 ? std::min(saturation, 1.0f) : 0.0f;
brightness = brightness > 0 ? std::min(brightness, 1.0f) : 0.0f;
if (tvg::zero(saturation)) _red = _green = _blue = brightness;
if (tvg::zero(saturation)) r = g = b = brightness;
else {
if (tvg::equal(hue, 360.0)) hue = 0.0f;
hue /= 60.0f;
v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation));
p = brightness + brightness - v;
auto v = (brightness <= 0.5f) ? (brightness * (1.0f + saturation)) : (brightness + saturation - (brightness * saturation));
auto p = brightness + brightness - v;
float sv;
if (!tvg::zero(v)) sv = (v - p) / v;
else sv = 0;
else sv = 0.0f;
i = static_cast<uint8_t>(hue);
f = hue - i;
auto f = hue - i;
vsf = v * sv * f;
auto vsf = v * sv * f;
t = p + vsf;
q = v - vsf;
auto t = p + vsf;
auto q = v - vsf;
switch (i) {
case 0: {
_red = v;
_green = t;
_blue = p;
r = v;
g = t;
b = p;
break;
}
case 1: {
_red = q;
_green = v;
_blue = p;
r = q;
g = v;
b = p;
break;
}
case 2: {
_red = p;
_green = v;
_blue = t;
r = p;
g = v;
b = t;
break;
}
case 3: {
_red = p;
_green = q;
_blue = v;
r = p;
g = q;
b = v;
break;
}
case 4: {
_red = t;
_green = p;
_blue = v;
r = t;
g = p;
b = v;
break;
}
case 5: {
_red = v;
_green = p;
_blue = q;
r = v;
g = p;
b = q;
break;
}
}
}
*red = (uint8_t)nearbyint(_red * 255.0f);
*green = (uint8_t)nearbyint(_green * 255.0f);
*blue = (uint8_t)nearbyint(_blue * 255.0f);
*red = (uint8_t)nearbyint(r * 255.0f);
*green = (uint8_t)nearbyint(g * 255.0f);
*blue = (uint8_t)nearbyint(b * 255.0f);
return true;
}
@ -654,9 +652,7 @@ static bool _hslToRgb(float hue, float saturation, float brightness, uint8_t* re
static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref)
{
unsigned int len = strlen(str);
char *red, *green, *blue;
unsigned char tr, tg, tb;
auto len = strlen(str);
if (len == 4 && str[0] == '#') {
//Case for "#456" should be interpreted as "#445566"
@ -688,11 +684,12 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
}
return true;
} else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') {
tr = _parseColor(str + 4, &red);
char *red, *green, *blue;
auto tr = _parseColor(str + 4, &red);
if (red && *red == ',') {
tg = _parseColor(red + 1, &green);
auto tg = _parseColor(red + 1, &green);
if (green && *green == ',') {
tb = _parseColor(green + 1, &blue);
auto tb = _parseColor(green + 1, &blue);
if (blue && blue[0] == ')' && blue[1] == '\0') {
*r = tr;
*g = tg;
@ -747,10 +744,10 @@ static bool _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char**
static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
{
int count = 0;
char* end = nullptr;
str = _skipSpace(str, nullptr);
while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
char* end = nullptr;
points[count++] = toFloat(str, &end);
str = end;
str = _skipSpace(str, nullptr);
@ -807,8 +804,8 @@ static Matrix* _parseTransformationMatrix(const char* value)
float points[POINT_CNT];
int ptCount = 0;
char* str = (char*)value;
char* end = str + strlen(str);
auto str = (char*)value;
auto end = str + strlen(str);
while (str < end) {
auto state = MatrixState::Unknown;
@ -902,10 +899,6 @@ static void _postpone(Array<SvgNodeIdPair>& nodes, SvgNode *node, char* id)
}
static bool _parseStyleAttr(void* data, const char* key, const char* value);
static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style);
static bool _attrParseSvgNode(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
@ -951,7 +944,7 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
} else if (STR_AS(key, "preserveAspectRatio")) {
_parseAspectRatio(&value, &doc->align, &doc->meetOrSlice);
} else if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
#ifdef THORVG_LOG_ENABLED
} else if ((STR_AS(key, "x") || STR_AS(key, "y")) && fabsf(toFloat(value, nullptr)) > FLOAT_EPSILON) {
TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
@ -1051,8 +1044,7 @@ static void _handleStrokeMiterlimitAttr(SvgLoaderData* loader, SvgNode* node, co
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
// - A negative value for stroke-miterlimit must be treated as an illegal value.
if (miterlimit < 0.0f) {
TVGERR("SVG", "A stroke-miterlimit change (%f <- %f) with a negative value is omitted.",
node->style->stroke.miterlimit, miterlimit);
TVGERR("SVG", "A stroke-miterlimit change (%f <- %f) with a negative value is omitted.", node->style->stroke.miterlimit, miterlimit);
return;
}
@ -1261,7 +1253,7 @@ static bool _attrParseGNode(void* data, const char* key, const char* value)
SvgNode* node = loader->svgParse->node;
if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "transform")) {
node->transform = _parseTransformationMatrix(value);
} else if (STR_AS(key, "id")) {
@ -1292,7 +1284,7 @@ static bool _attrParseClipPathNode(void* data, const char* key, const char* valu
SvgClipNode* clip = &(node->node.clip);
if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "transform")) {
node->transform = _parseTransformationMatrix(value);
} else if (STR_AS(key, "id")) {
@ -1311,12 +1303,12 @@ static bool _attrParseClipPathNode(void* data, const char* key, const char* valu
static bool _attrParseMaskNode(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
SvgNode* node = loader->svgParse->node;
SvgMaskNode* mask = &(node->node.mask);
auto loader = (SvgLoaderData*)data;
auto node = loader->svgParse->node;
auto mask = &(node->node.mask);
if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "transform")) {
node->transform = _parseTransformationMatrix(value);
} else if (STR_AS(key, "id")) {
@ -1337,8 +1329,8 @@ static bool _attrParseMaskNode(void* data, const char* key, const char* value)
static bool _attrParseCssStyleNode(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
SvgNode* node = loader->svgParse->node;
auto loader = (SvgLoaderData*)data;
auto node = loader->svgParse->node;
if (STR_AS(key, "id")) {
if (value) tvg::free(node->id);
@ -1373,7 +1365,6 @@ static bool _attrParseSymbolNode(void* data, const char* key, const char* value)
} else {
return _attrParseGNode(data, key, value);
}
return true;
}
@ -1433,7 +1424,6 @@ static bool _attrParseFilterNode(void* data, const char* key, const char* value)
} else if (STR_AS(key, "filterUnits")) {
if (STR_AS(value, "userSpaceOnUse")) filter->filterUserSpace = true;
}
return true;
}
@ -1484,22 +1474,13 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
{
SvgNode* node = tvg::calloc<SvgNode*>(1, sizeof(SvgNode));
if (!node) return nullptr;
//Default fill property
node->style = tvg::calloc<SvgStyleProperty*>(1, sizeof(SvgStyleProperty));
if (!node->style) {
tvg::free(node);
return nullptr;
}
//Set the default values other than 0/false: https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
node->style->opacity = 255;
node->style->fill.opacity = 255;
node->style->fill.fillRule = FillRule::NonZero;
node->style->stroke.paint.none = true;
node->style->stroke.opacity = 255;
node->style->stroke.width = 1;
@ -1507,11 +1488,8 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
node->style->stroke.join = StrokeJoin::Miter;
node->style->stroke.miterlimit = 4.0f;
node->style->stroke.scale = 1.0;
node->style->paintOrder = _toPaintOrder("fill stroke");
node->style->display = true;
node->parent = parent;
node->type = type;
@ -1523,19 +1501,14 @@ static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength, TVG_UNUSED parseAttributes func)
{
if (loader->def && loader->doc->node.doc.defs) return loader->def;
SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
loader->def = node;
loader->doc->node.doc.defs = node;
return node;
loader->def = loader->doc->node.doc.defs = _createNode(nullptr, SvgNodeType::Defs);
return loader->def;
}
static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::G);
if (!loader->svgParse->node) return nullptr;
func(buf, bufLength, _attrParseGNode, loader);
return loader->svgParse->node;
}
@ -1544,8 +1517,7 @@ static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent,
static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
{
loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
if (!loader->svgParse->node) return nullptr;
SvgDocNode* doc = &(loader->svgParse->node->node.doc);
auto doc = &(loader->svgParse->node->node.doc);
loader->svgParse->global.w = 1.0f;
loader->svgParse->global.h = 1.0f;
@ -1663,7 +1635,7 @@ static bool _attrParsePathNode(void* data, const char* key, const char* value)
//Temporary: need to copy
path->path = _copyId(value);
} else if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (STR_AS(key, "mask")) {
@ -1727,7 +1699,7 @@ static bool _attrParseCircleNode(void* data, const char* key, const char* value)
}
if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (STR_AS(key, "mask")) {
@ -1796,7 +1768,7 @@ static bool _attrParseEllipseNode(void* data, const char* key, const char* value
} else if (STR_AS(key, "class")) {
_handleCssClassAttr(loader, node, value);
} else if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (STR_AS(key, "mask")) {
@ -1847,7 +1819,7 @@ static bool _attrParsePolygonNode(void* data, const char* key, const char* value
if (STR_AS(key, "points")) {
return _attrParsePolygonPoints(value, polygon);
} else if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (STR_AS(key, "mask")) {
@ -1936,7 +1908,7 @@ static bool _attrParseRectNode(void* data, const char* key, const char* value)
} else if (STR_AS(key, "class")) {
_handleCssClassAttr(loader, node, value);
} else if (STR_AS(key, "style")) {
ret = simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
ret = xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (STR_AS(key, "mask")) {
@ -2001,7 +1973,7 @@ static bool _attrParseLineNode(void* data, const char* key, const char* value)
} else if (STR_AS(key, "class")) {
_handleCssClassAttr(loader, node, value);
} else if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (STR_AS(key, "mask")) {
@ -2076,7 +2048,7 @@ static bool _attrParseImageNode(void* data, const char* key, const char* value)
} else if (STR_AS(key, "class")) {
_handleCssClassAttr(loader, node, value);
} else if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (STR_AS(key, "mask")) {
@ -2143,7 +2115,7 @@ static SvgNode* _getDefsNode(SvgNode* node)
{
if (!node) return nullptr;
while (node->parent != nullptr) {
while (node->parent) {
node = node->parent;
}
@ -2158,16 +2130,14 @@ static SvgNode* _findNodeById(SvgNode *node, const char* id)
{
if (!node) return nullptr;
SvgNode* result = nullptr;
if (node->id && STR_AS(node->id, id)) return node;
if (node->child.count > 0) {
ARRAY_FOREACH(p, node->child) {
result = _findNodeById(*p, id);
if (result) break;
if (auto result = _findNodeById(*p, id)) return result;
}
}
return result;
return nullptr;
}
@ -2291,7 +2261,7 @@ static bool _attrParseTextNode(void* data, const char* key, const char* value)
text->fontFamily = duplicate(value);
}
} else if (STR_AS(key, "style")) {
return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
return xmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
} else if (STR_AS(key, "clip-path")) {
_handleClipPathAttr(loader, node, value);
} else if (STR_AS(key, "mask")) {
@ -2677,7 +2647,7 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
loader->svgParse->gradient.parsedFx = false;
loader->svgParse->gradient.parsedFy = false;
simpleXmlParseAttributes(buf, bufLength,
xmlParseAttributes(buf, bufLength,
_attrParseRadialGradientNode, loader);
for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
@ -2748,7 +2718,7 @@ static bool _attrParseStops(void* data, const char* key, const char* value)
_toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
}
} else if (STR_AS(key, "style")) {
simpleXmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data);
xmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data);
} else {
return false;
}
@ -2957,7 +2927,7 @@ static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char
grad->linear->x2 = 1.0f;
grad->linear->isX2Percentage = true;
simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
xmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace);
@ -3454,7 +3424,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
GradientFactoryMethod gradientMethod;
SvgNode *node = nullptr, *parent = nullptr;
loader->level++;
attrs = simpleXmlFindAttributesTag(content, length);
attrs = xmlFindAttributesTag(content, length);
if (!attrs) {
//Parse the empty tag
@ -3478,7 +3448,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
if (empty) return;
if (!loader->doc) {
if (!STR_AS(tagName, "svg")) return; //Not a valid svg document
node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
node = method(loader, nullptr, attrs, attrsLength, xmlParseAttributes);
loader->doc = node;
} else {
if (STR_AS(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
@ -3488,13 +3458,13 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
// TODO: For now only the first style node is saved. After the css id selector
// is introduced this if condition shouldn't be necessary any more
if (!loader->cssStyle) {
node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
node = method(loader, nullptr, attrs, attrsLength, xmlParseAttributes);
loader->cssStyle = node;
loader->doc->node.doc.style = node;
loader->openedTag = OpenedTagType::Style;
}
} else {
node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
node = method(loader, parent, attrs, attrsLength, xmlParseAttributes);
}
}
@ -3506,7 +3476,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
} else if ((method = _findGraphicsFactory(tagName))) {
if (loader->stack.count > 0) parent = loader->stack.last();
else parent = loader->doc;
node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
node = method(loader, parent, attrs, attrsLength, xmlParseAttributes);
if (node && !empty) {
if (STR_AS(tagName, "text")) loader->openedTag = OpenedTagType::Text;
auto defs = _createDefsNode(loader, nullptr, nullptr, 0, nullptr);
@ -3537,7 +3507,7 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
/* default value for opacity */
loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
loader->svgParse->flags = SvgStopStyleFlags::StopDefault;
simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
xmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
loader->latestGradient->stops.push(loader->svgParse->gradStop);
} else {
loader->latestGradient = nullptr;
@ -3548,8 +3518,8 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
static void _svgLoaderParserText(SvgLoaderData* loader, const char* content, unsigned int length)
{
auto text = &loader->svgParse->node->node.text;
text->text = append(text->text, content, length);
auto& text = loader->svgParse->node->node.text;
text.text = append(text.text, content, length);
}
@ -3564,19 +3534,19 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte
GradientFactoryMethod gradientMethod;
SvgNode *node = nullptr;
while (auto next = simpleXmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) {
while (auto next = xmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) {
if ((method = _findGroupFactory(tag))) {
if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
if ((node = method(loader, loader->cssStyle, attrs, attrsLength, xmlParseW3CAttribute))) node->id = _copyId(name);
} else if ((method = _findGraphicsFactory(tag))) {
if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
if ((node = method(loader, loader->cssStyle, attrs, attrsLength, xmlParseW3CAttribute))) node->id = _copyId(name);
} else if ((gradientMethod = _findGradientFactory(tag))) {
TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
} else if (STR_AS(tag, "stop")) {
TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
} else if (STR_AS(tag, "all")) {
if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, xmlParseW3CAttribute))) node->id = _copyId(name);
} else if (STR_AS(tag, "@font-face")) { //css at-rule specifying font
_createFontFace(loader, attrs, attrsLength, simpleXmlParseW3CAttribute);
_createFontFace(loader, attrs, attrsLength, xmlParseW3CAttribute);
} else if (!isIgnoreUnsupportedLogElements(tag)) {
TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
}
@ -3591,35 +3561,35 @@ static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* conte
}
static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length)
static bool _svgLoaderParser(void* data, XMLType type, const char* content, unsigned int length)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
switch (type) {
case SimpleXMLType::Open: {
case XMLType::Open: {
_svgLoaderParserXmlOpen(loader, content, length, false);
break;
}
case SimpleXMLType::OpenEmpty: {
case XMLType::OpenEmpty: {
_svgLoaderParserXmlOpen(loader, content, length, true);
break;
}
case SimpleXMLType::Close: {
case XMLType::Close: {
_svgLoaderParserXmlClose(loader, content, length);
break;
}
case SimpleXMLType::Data:
case SimpleXMLType::CData: {
case XMLType::Data:
case XMLType::CData: {
if (loader->openedTag == OpenedTagType::Style) _svgLoaderParserXmlCssStyle(loader, content, length);
else if (loader->openedTag == OpenedTagType::Text) _svgLoaderParserText(loader, content, length);
break;
}
case SimpleXMLType::DoctypeChild: {
case XMLType::DoctypeChild: {
break;
}
case SimpleXMLType::Ignored:
case SimpleXMLType::Comment:
case SimpleXMLType::Doctype: {
case XMLType::Ignored:
case XMLType::Comment:
case XMLType::Doctype: {
break;
}
default: {
@ -3631,91 +3601,44 @@ static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content
}
static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
{
#ifdef THORVG_LOG_ENABLED
auto type = simpleXmlNodeTypeToString(node->type);
if (!node->style->display && node->type != SvgNodeType::ClipPath) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
switch (node->type) {
case SvgNodeType::Path: {
if (!node->node.path.path) TVGLOG("SVG", "Inefficient elements used [Empty path][Node Type : %s]", type);
break;
}
case SvgNodeType::Ellipse: {
if (node->node.ellipse.rx == 0 && node->node.ellipse.ry == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
break;
}
case SvgNodeType::Polygon:
case SvgNodeType::Polyline: {
if (node->node.polygon.pts.count < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type);
break;
}
case SvgNodeType::Circle: {
if (node->node.circle.r == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
break;
}
case SvgNodeType::Rect: {
if (node->node.rect.w == 0 && node->node.rect.h) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
break;
}
case SvgNodeType::Line: {
if (node->node.line.x1 == node->node.line.x2 && node->node.line.y1 == node->node.line.y2) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
break;
}
default: break;
}
#endif
}
static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
{
_styleInherit(node->style, parentStyle);
_inefficientNodeCheck(node);
ARRAY_FOREACH(p, node->child) {
_updateStyle(*p, node->style);
}
}
static SvgStyleGradient* _gradientDup(SvgLoaderData* loader, Array<SvgStyleGradient*>* gradients, const char* id)
static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array<SvgStyleGradient*>* gradients)
{
SvgStyleGradient* result = nullptr;
auto duplicate = [&](SvgLoaderData* loader, Array<SvgStyleGradient*>* gradients, const char* id) -> SvgStyleGradient* {
SvgStyleGradient* result = nullptr;
ARRAY_FOREACH(p, *gradients) {
if ((*p)->id && STR_AS((*p)->id, id)) {
result = _cloneGradient(*p);
break;
}
}
if (result && result->ref) {
ARRAY_FOREACH(p, *gradients) {
if ((*p)->id && STR_AS((*p)->id, result->ref)) {
_inheritGradient(loader, result, *p);
if ((*p)->id && STR_AS((*p)->id, id)) {
result = _cloneGradient(*p);
break;
}
}
}
if (result && result->ref) {
ARRAY_FOREACH(p, *gradients) {
if ((*p)->id && STR_AS((*p)->id, result->ref)) {
_inheritGradient(loader, result, *p);
break;
}
}
}
return result;
};
return result;
}
static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array<SvgStyleGradient*>* gradients)
{
if (node->child.count > 0) {
ARRAY_FOREACH(p, node->child) {
_updateGradient(loader, *p, gradients);
}
} else {
if (node->style->fill.paint.url) {
auto newGrad = _gradientDup(loader, gradients, node->style->fill.paint.url);
auto newGrad = duplicate(loader, gradients, node->style->fill.paint.url);
if (newGrad) {
if (node->style->fill.paint.gradient) {
node->style->fill.paint.gradient->clear();
@ -3725,7 +3648,7 @@ static void _updateGradient(SvgLoaderData* loader, SvgNode* node, Array<SvgStyle
}
}
if (node->style->stroke.paint.url) {
auto newGrad = _gradientDup(loader, gradients, node->style->stroke.paint.url);
auto newGrad = duplicate(loader, gradients, node->style->stroke.paint.url);
if (newGrad) {
if (node->style->stroke.paint.gradient) {
node->style->stroke.paint.gradient->clear();
@ -3759,14 +3682,10 @@ static void _updateComposite(SvgNode* node, SvgNode* root)
static void _updateFilter(SvgNode* node, SvgNode* root)
{
if (node->style->filter.url && !node->style->filter.node) {
SvgNode* findResult = _findNodeById(root, node->style->filter.url);
if (findResult) node->style->filter.node = findResult;
node->style->filter.node = _findNodeById(root, node->style->filter.url);
}
if (node->child.count > 0) {
auto child = node->child.data;
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
_updateFilter(*child, root);
}
ARRAY_FOREACH(child, node->child) {
_updateFilter(*child, root);
}
}
@ -3858,7 +3777,7 @@ static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const ch
SvgNode *node = nullptr;
int attrsLength = 0;
loader->level++;
attrs = simpleXmlFindAttributesTag(content, length);
attrs = xmlFindAttributesTag(content, length);
if (!attrs) {
//Parse the empty tag
@ -3878,7 +3797,7 @@ static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const ch
if ((method = _findGroupFactory(tagName))) {
if (!loader->doc) {
if (!STR_AS(tagName, "svg")) return true; //Not a valid svg document
node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
node = method(loader, nullptr, attrs, attrsLength, xmlParseAttributes);
loader->doc = node;
loader->stack.push(node);
return false;
@ -3888,24 +3807,16 @@ static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const ch
}
static bool _svgLoaderParserForValidCheck(void* data, SimpleXMLType type, const char* content, unsigned int length)
static bool _svgLoaderParserForValidCheck(void* data, XMLType type, const char* content, unsigned int length)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
bool res = true;;
switch (type) {
case SimpleXMLType::Open:
case SimpleXMLType::OpenEmpty: {
case XMLType::Open:
case XMLType::OpenEmpty: {
//If 'res' is false, it means <svg> tag is found.
res = _svgLoaderParserForValidCheckXmlOpen(loader, content, length);
break;
}
default: {
break;
return _svgLoaderParserForValidCheckXmlOpen(static_cast<SvgLoaderData*>(data), content, length);
}
default: return true;
}
return res;
}
@ -3973,7 +3884,7 @@ void SvgLoader::run(unsigned tid)
return;
}
if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
if (!xmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
if (loaderData.doc) {
auto defs = loaderData.doc->node.doc.defs;
@ -3994,16 +3905,17 @@ void SvgLoader::run(unsigned tid)
if (loaderData.gradients.count > 0) _updateGradient(&loaderData, loaderData.doc, &loaderData.gradients);
if (defs) _updateGradient(&loaderData, loaderData.doc, &defs->node.defs.gradients);
}
root = svgSceneBuild(loaderData, vbox, w, h, align, meetOrSlice, svgPath, viewFlag);
//In case no viewbox and width/height data is provided the completion of loading
//has to be forced, in order to establish this data based on the whole picture.
if (!(viewFlag & SvgViewFlag::Viewbox)) {
//Override viewbox & size again after svg loading.
vbox = loaderData.doc->node.doc.vbox;
w = loaderData.doc->node.doc.w;
h = loaderData.doc->node.doc.h;
root = svgSceneBuild(loaderData, vbox, w, h, align, meetOrSlice, svgPath, viewFlag);
//In case no viewbox and width/height data is provided the completion of loading
//has to be forced, in order to establish this data based on the whole picture.
if (!(viewFlag & SvgViewFlag::Viewbox)) {
//Override viewbox & size again after svg loading.
vbox = loaderData.doc->node.doc.vbox;
w = loaderData.doc->node.doc.w;
h = loaderData.doc->node.doc.h;
}
}
clear(false);
@ -4016,70 +3928,67 @@ bool SvgLoader::header()
//If the <svg> tag is found, the loaded file is valid and stores viewbox information.
//After that, the remaining content data is parsed in order with async.
loaderData.svgParse = tvg::malloc<SvgParser*>(sizeof(SvgParser));
if (!loaderData.svgParse) return false;
loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
viewFlag = SvgViewFlag::None;
simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
xmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
viewFlag = loaderData.doc->node.doc.viewFlag;
align = loaderData.doc->node.doc.align;
meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
if (viewFlag & SvgViewFlag::Viewbox) {
vbox = loaderData.doc->node.doc.vbox;
if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w;
else {
w = loaderData.doc->node.doc.vbox.w;
if (viewFlag & SvgViewFlag::WidthInPercent) {
w *= loaderData.doc->node.doc.w;
viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent);
}
viewFlag = (viewFlag | SvgViewFlag::Width);
}
if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h;
else {
h = loaderData.doc->node.doc.vbox.h;
if (viewFlag & SvgViewFlag::HeightInPercent) {
h *= loaderData.doc->node.doc.h;
viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent);
}
viewFlag = (viewFlag | SvgViewFlag::Height);
}
//In case no viewbox and width/height data is provided the completion of loading
//has to be forced, in order to establish this data based on the whole picture.
} else {
//Before loading, set default viewbox & size if they are empty
vbox.x = vbox.y = 0.0f;
if (viewFlag & SvgViewFlag::Width) {
vbox.w = w = loaderData.doc->node.doc.w;
} else {
vbox.w = 1.0f;
if (viewFlag & SvgViewFlag::WidthInPercent) {
w = loaderData.doc->node.doc.w;
} else w = 1.0f;
}
if (viewFlag & SvgViewFlag::Height) {
vbox.h = h = loaderData.doc->node.doc.h;
} else {
vbox.h = 1.0f;
if (viewFlag & SvgViewFlag::HeightInPercent) {
h = loaderData.doc->node.doc.h;
} else h = 1.0f;
}
run(0);
}
return true;
if (!loaderData.doc || loaderData.doc->type != SvgNodeType::Doc) {
TVGLOG("SVG", "No SVG File. There is no <svg/>");
return false;
}
TVGLOG("SVG", "No SVG File. There is no <svg/>");
return false;
viewFlag = loaderData.doc->node.doc.viewFlag;
align = loaderData.doc->node.doc.align;
meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
if (viewFlag & SvgViewFlag::Viewbox) {
vbox = loaderData.doc->node.doc.vbox;
if (viewFlag & SvgViewFlag::Width) w = loaderData.doc->node.doc.w;
else {
w = loaderData.doc->node.doc.vbox.w;
if (viewFlag & SvgViewFlag::WidthInPercent) {
w *= loaderData.doc->node.doc.w;
viewFlag = (viewFlag ^ SvgViewFlag::WidthInPercent);
}
viewFlag = (viewFlag | SvgViewFlag::Width);
}
if (viewFlag & SvgViewFlag::Height) h = loaderData.doc->node.doc.h;
else {
h = loaderData.doc->node.doc.vbox.h;
if (viewFlag & SvgViewFlag::HeightInPercent) {
h *= loaderData.doc->node.doc.h;
viewFlag = (viewFlag ^ SvgViewFlag::HeightInPercent);
}
viewFlag = (viewFlag | SvgViewFlag::Height);
}
//In case no viewbox and width/height data is provided the completion of loading
//has to be forced, in order to establish this data based on the whole picture.
} else {
//Before loading, set default viewbox & size if they are empty
vbox.x = vbox.y = 0.0f;
if (viewFlag & SvgViewFlag::Width) {
vbox.w = w = loaderData.doc->node.doc.w;
} else {
vbox.w = 1.0f;
if (viewFlag & SvgViewFlag::WidthInPercent) {
w = loaderData.doc->node.doc.w;
} else w = 1.0f;
}
if (viewFlag & SvgViewFlag::Height) {
vbox.h = h = loaderData.doc->node.doc.h;
} else {
vbox.h = 1.0f;
if (viewFlag & SvgViewFlag::HeightInPercent) {
h = loaderData.doc->node.doc.h;
} else h = 1.0f;
}
run(0);
}
return true;
}

View file

@ -29,7 +29,7 @@
/* Internal Class Implementation */
/************************************************************************/
bool _isIgnoreUnsupportedLogAttributes(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue)
bool _unsupported(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue)
{
#ifdef THORVG_LOG_ENABLED
const auto attributesNum = 6;
@ -48,22 +48,18 @@ bool _isIgnoreUnsupportedLogAttributes(TVG_UNUSED const char* tagAttribute, TVG_
};
for (unsigned int i = 0; i < attributesNum; ++i) {
if (!strncmp(tagAttribute, attributes[i].tag, attributes[i].tagWildcard ? strlen(attributes[i].tag) : strlen(tagAttribute))) {
if (attributes[i].value && tagValue) {
if (!strncmp(tagValue, attributes[i].value, strlen(tagValue))) {
return true;
} else continue;
}
return true;
if (tvg::equal(tagAttribute, attributes[i].tag)) {
if (!attributes[i].value) return false;
if (tvg::equal(tagValue, attributes[i].value)) return false;
}
}
return false;
#endif
return true;
#endif
return false;
}
static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
static const char* _xmlFindWhiteSpace(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if (isspace((unsigned char)*itr)) break;
@ -72,7 +68,7 @@ static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
}
static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd)
static const char* _xmlSkipWhiteSpace(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if (!isspace((unsigned char)*itr)) break;
@ -81,7 +77,7 @@ static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd)
}
static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrStart)
static const char* _xmlUnskipWhiteSpace(const char* itr, const char* itrStart)
{
for (itr--; itr > itrStart; itr--) {
if (!isspace((unsigned char)*itr)) break;
@ -90,7 +86,7 @@ static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrSt
}
static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd)
static const char* _xmlSkipXmlEntities(const char* itr, const char* itrEnd)
{
auto p = itr;
while (itr < itrEnd && *itr == '&') {
@ -107,7 +103,7 @@ static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd
}
static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrStart)
static const char* _xmlUnskipXmlEntities(const char* itr, const char* itrStart)
{
auto p = itr;
while (itr > itrStart && *(itr - 1) == ';') {
@ -127,12 +123,12 @@ static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrS
static const char* _skipWhiteSpacesAndXmlEntities(const char* itr, const char* itrEnd)
{
itr = _simpleXmlSkipWhiteSpace(itr, itrEnd);
itr = _xmlSkipWhiteSpace(itr, itrEnd);
auto p = itr;
while (true) {
if (p != (itr = _simpleXmlSkipXmlEntities(itr, itrEnd))) p = itr;
if (p != (itr = _xmlSkipXmlEntities(itr, itrEnd))) p = itr;
else break;
if (p != (itr = _simpleXmlSkipWhiteSpace(itr, itrEnd))) p = itr;
if (p != (itr = _xmlSkipWhiteSpace(itr, itrEnd))) p = itr;
else break;
}
return itr;
@ -141,25 +137,25 @@ static const char* _skipWhiteSpacesAndXmlEntities(const char* itr, const char* i
static const char* _unskipWhiteSpacesAndXmlEntities(const char* itr, const char* itrStart)
{
itr = _simpleXmlUnskipWhiteSpace(itr, itrStart);
itr = _xmlUnskipWhiteSpace(itr, itrStart);
auto p = itr;
while (true) {
if (p != (itr = _simpleXmlUnskipXmlEntities(itr, itrStart))) p = itr;
if (p != (itr = _xmlUnskipXmlEntities(itr, itrStart))) p = itr;
else break;
if (p != (itr = _simpleXmlUnskipWhiteSpace(itr, itrStart))) p = itr;
if (p != (itr = _xmlUnskipWhiteSpace(itr, itrStart))) p = itr;
else break;
}
return itr;
}
static const char* _simpleXmlFindStartTag(const char* itr, const char* itrEnd)
static const char* _xmlFindStartTag(const char* itr, const char* itrEnd)
{
return (const char*)memchr(itr, '<', itrEnd - itr);
}
static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd)
static const char* _xmlFindEndTag(const char* itr, const char* itrEnd)
{
bool insideQuote[2] = {false, false}; // 0: ", 1: '
for (; itr < itrEnd; itr++) {
@ -174,7 +170,7 @@ static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd)
}
static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrEnd)
static const char* _xmlFindEndCommentTag(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
@ -183,7 +179,7 @@ static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrE
}
static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd)
static const char* _xmlFindEndCdataTag(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
@ -192,7 +188,7 @@ static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd
}
static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd)
static const char* _xmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd)
{
for (; itr < itrEnd; itr++) {
if (*itr == '>') return itr;
@ -201,32 +197,32 @@ static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char*
}
static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff)
static XMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff)
{
toff = 0;
if (itr[1] == '/') {
toff = 1;
return SimpleXMLType::Close;
return XMLType::Close;
} else if (itr[1] == '?') {
toff = 1;
return SimpleXMLType::Processing;
return XMLType::Processing;
} else if (itr[1] == '!') {
if ((itr + sizeof("<!DOCTYPE>") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) {
toff = sizeof("!DOCTYPE") - 1;
return SimpleXMLType::Doctype;
return XMLType::Doctype;
} else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
toff = sizeof("![CDATA[") - 1;
return SimpleXMLType::CData;
return XMLType::CData;
} else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
toff = sizeof("!--") - 1;
return SimpleXMLType::Comment;
return XMLType::Comment;
} else if (itr + sizeof("<!>") - 1 < itrEnd) {
toff = sizeof("!") - 1;
return SimpleXMLType::DoctypeChild;
return XMLType::DoctypeChild;
}
return SimpleXMLType::Open;
return XMLType::Open;
}
return SimpleXMLType::Open;
return XMLType::Open;
}
@ -234,7 +230,7 @@ static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &to
/* External Class Implementation */
/************************************************************************/
const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type)
const char* xmlNodeTypeToString(TVG_UNUSED SvgNodeType type)
{
#ifdef THORVG_LOG_ENABLED
static const char* TYPE_NAMES[] = {
@ -285,7 +281,7 @@ bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName)
}
bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
bool xmlParseAttributes(const char* buf, unsigned bufLength, xmlAttributeCb func, const void* data)
{
const char *itr = buf, *itrEnd = buf + bufLength;
char* tmpBuf = tvg::malloc<char*>(bufLength + 1);
@ -315,7 +311,7 @@ bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttr
if (!value) goto error;
value++;
}
keyEnd = _simpleXmlUnskipXmlEntities(keyEnd, key);
keyEnd = _xmlUnskipXmlEntities(keyEnd, key);
value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
if (value == itrEnd) goto error;
@ -325,7 +321,7 @@ bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttr
if (!valueEnd) goto error;
value++;
} else {
valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd);
valueEnd = _xmlFindWhiteSpace(value, itrEnd);
}
itr = valueEnd + 1;
@ -339,15 +335,15 @@ bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttr
tval = tmpBuf + (keyEnd - key) + 1;
int i = 0;
while (value < valueEnd) {
value = _simpleXmlSkipXmlEntities(value, valueEnd);
value = _xmlSkipXmlEntities(value, valueEnd);
tval[i++] = *value;
value++;
}
tval[i] = '\0';
if (!func((void*)data, tmpBuf, tval)) {
if (!_isIgnoreUnsupportedLogAttributes(tmpBuf, tval)) {
TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", tmpBuf, tval ? tval : "NONE");
if (_unsupported(tmpBuf, tval)) {
TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", xmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", tmpBuf, tval ? tval : "NONE");
}
}
}
@ -362,51 +358,49 @@ error:
}
bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data)
bool xmlParse(const char* buf, unsigned bufLength, bool strip, xmlCb func, const void* data)
{
const char *itr = buf, *itrEnd = buf + bufLength;
if (!buf || !func) return false;
while (itr < itrEnd) {
if (itr[0] == '<') {
//Invalid case
if (itr + 1 >= itrEnd) return false;
size_t toff = 0;
SimpleXMLType type = _getXMLType(itr, itrEnd, toff);
XMLType type = _getXMLType(itr, itrEnd, toff);
const char* p;
if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
if (type == XMLType::CData) p = _xmlFindEndCdataTag(itr + 1 + toff, itrEnd);
else if (type == XMLType::DoctypeChild) p = _xmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
else if (type == XMLType::Comment) p = _xmlFindEndCommentTag(itr + 1 + toff, itrEnd);
else p = _xmlFindEndTag(itr + 1 + toff, itrEnd);
if (p) {
//Invalid case: '<' nested
if (*p == '<' && type != SimpleXMLType::Doctype) return false;
if (*p == '<' && type != XMLType::Doctype) return false;
const char *start, *end;
start = itr + 1 + toff;
end = p;
switch (type) {
case SimpleXMLType::Open: {
case XMLType::Open: {
if (p[-1] == '/') {
type = SimpleXMLType::OpenEmpty;
type = XMLType::OpenEmpty;
end--;
}
break;
}
case SimpleXMLType::CData: {
case XMLType::CData: {
if (!memcmp(p - 2, "]]", 2)) end -= 2;
break;
}
case SimpleXMLType::Processing: {
case XMLType::Processing: {
if (p[-1] == '?') end--;
break;
}
case SimpleXMLType::Comment: {
case XMLType::Comment: {
if (!memcmp(p - 2, "--", 2)) end -= 2;
break;
}
@ -415,7 +409,7 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb
}
}
if (strip && (type != SimpleXMLType::CData)) {
if (strip && (type != XMLType::CData)) {
start = _skipWhiteSpacesAndXmlEntities(start, end);
end = _unskipWhiteSpacesAndXmlEntities(end, start);
}
@ -433,20 +427,20 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb
p = itr;
p = _skipWhiteSpacesAndXmlEntities(p, itrEnd);
if (p) {
if (!func((void*)data, SimpleXMLType::Ignored, itr, (unsigned int)(p - itr))) return false;
if (!func((void*)data, XMLType::Ignored, itr, (unsigned int)(p - itr))) return false;
itr = p;
}
}
p = _simpleXmlFindStartTag(itr, itrEnd);
p = _xmlFindStartTag(itr, itrEnd);
if (!p) p = itrEnd;
end = p;
if (strip) end = _unskipWhiteSpacesAndXmlEntities(end, itr);
if (itr != end && !func((void*)data, SimpleXMLType::Data, itr, (unsigned int)(end - itr))) return false;
if (itr != end && !func((void*)data, XMLType::Data, itr, (unsigned int)(end - itr))) return false;
if (strip && (end < p) && !func((void*)data, SimpleXMLType::Ignored, end, (unsigned int)(p - end))) return false;
if (strip && (end < p) && !func((void*)data, XMLType::Ignored, end, (unsigned int)(p - end))) return false;
itr = p;
}
@ -455,7 +449,7 @@ bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb
}
bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
bool xmlParseW3CAttribute(const char* buf, unsigned bufLength, xmlAttributeCb func, const void* data)
{
const char* end;
char* key;
@ -508,14 +502,14 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
}
if (key[0]) {
key = const_cast<char*>(_simpleXmlSkipWhiteSpace(key, key + strlen(key)));
key[_simpleXmlUnskipWhiteSpace(key + strlen(key) , key) - key] = '\0';
val = const_cast<char*>(_simpleXmlSkipWhiteSpace(val, val + strlen(val)));
val[_simpleXmlUnskipWhiteSpace(val + strlen(val) , val) - val] = '\0';
key = const_cast<char*>(_xmlSkipWhiteSpace(key, key + strlen(key)));
key[_xmlUnskipWhiteSpace(key + strlen(key) , key) - key] = '\0';
val = const_cast<char*>(_xmlSkipWhiteSpace(val, val + strlen(val)));
val[_xmlUnskipWhiteSpace(val + strlen(val) , val) - val] = '\0';
if (!func((void*)data, key, val)) {
if (!_isIgnoreUnsupportedLogAttributes(key, val)) {
TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", key, val ? val : "NONE");
if (!_unsupported(key, val)) {
TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", xmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", key, val ? val : "NONE");
}
}
}
@ -532,14 +526,14 @@ bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAt
* Supported formats:
* tag {}, .name {}, tag.name{}
*/
const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength)
const char* xmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength)
{
if (!buf) return nullptr;
*tag = *name = nullptr;
*attrsLength = 0;
auto itr = _simpleXmlSkipWhiteSpace(buf, buf + bufLength);
auto itr = _xmlSkipWhiteSpace(buf, buf + bufLength);
auto itrEnd = (const char*)memchr(buf, '{', bufLength);
if (!itrEnd || itr == itrEnd) return nullptr;
@ -552,7 +546,7 @@ const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char
const char *p;
itrEnd = _simpleXmlUnskipWhiteSpace(itrEnd, itr);
itrEnd = _xmlUnskipWhiteSpace(itrEnd, itr);
if (*(itrEnd - 1) == '.') return nullptr;
for (p = itr; p < itrEnd; p++) {
@ -569,7 +563,7 @@ const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char
}
const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
const char* xmlFindAttributesTag(const char* buf, unsigned bufLength)
{
const char *itr = buf, *itrEnd = buf + bufLength;
@ -578,7 +572,7 @@ const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
//User skip tagname and already gave it the attributes.
if (*itr == '=') return buf;
} else {
itr = _simpleXmlUnskipXmlEntities(itr, buf);
itr = _xmlUnskipXmlEntities(itr, buf);
if (itr == itrEnd) return nullptr;
return itr;
}

View file

@ -20,8 +20,8 @@
* SOFTWARE.
*/
#ifndef _TVG_SIMPLE_XML_PARSER_H_
#define _TVG_SIMPLE_XML_PARSER_H_
#ifndef _TVG_XML_PARSER_H_
#define _TVG_XML_PARSER_H_
#include "tvgSvgLoaderCommon.h"
@ -29,7 +29,7 @@
const char* const xmlEntity[] = {"&#10;", "&quot;", "&nbsp;", "&apos;", "&amp;", "&lt;", "&gt;", "&#035;", "&#039;"};
const int xmlEntityLength[] = {5, 6, 6, 6, 5, 4, 4, 6, 6};
enum class SimpleXMLType
enum class XMLType
{
Open = 0, //!< \<tag attribute="value"\>
OpenEmpty, //!< \<tag attribute="value" /\>
@ -44,15 +44,15 @@ enum class SimpleXMLType
DoctypeChild //!< \<!doctype_child
};
typedef bool (*simpleXMLCb)(void* data, SimpleXMLType type, const char* content, unsigned int length);
typedef bool (*simpleXMLAttributeCb)(void* data, const char* key, const char* value);
typedef bool (*xmlCb)(void* data, XMLType type, const char* content, unsigned int length);
typedef bool (*xmlAttributeCb)(void* data, const char* key, const char* value);
bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data);
bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data);
bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data);
const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength);
const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength);
bool xmlParseAttributes(const char* buf, unsigned bufLength, xmlAttributeCb func, const void* data);
bool xmlParse(const char* buf, unsigned bufLength, bool strip, xmlCb func, const void* data);
bool xmlParseW3CAttribute(const char* buf, unsigned bufLength, xmlAttributeCb func, const void* data);
const char* xmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength);
const char* xmlFindAttributesTag(const char* buf, unsigned bufLength);
bool isIgnoreUnsupportedLogElements(const char* tagName);
const char* simpleXmlNodeTypeToString(SvgNodeType type);
const char* xmlNodeTypeToString(SvgNodeType type);
#endif //_TVG_SIMPLE_XML_PARSER_H_
#endif //_TVG_XML_PARSER_H_