/*
- * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
+ * Copyright (c) 2020 - 2022 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* SOFTWARE.
*/
+#include <cstring>
#include <ctype.h>
#include <string>
/* Internal Class Implementation */
/************************************************************************/
-#ifdef THORVG_LOG_ENABLED
-
-#include <stdio.h>
-
-string simpleXmlNodeTypeToString(SvgNodeType type)
-{
- switch (type) {
- case SvgNodeType::Doc: return "Svg";
- case SvgNodeType::G: return "G";
- case SvgNodeType::Defs: return "Defs";
- case SvgNodeType::Animation: return "Animation";
- case SvgNodeType::Arc: return "Arc";
- case SvgNodeType::Circle: return "Circle";
- case SvgNodeType::Ellipse: return "Ellipse";
- case SvgNodeType::Image: return "Image";
- case SvgNodeType::Line: return "Line";
- case SvgNodeType::Path: return "Path";
- case SvgNodeType::Polygon: return "Polygon";
- case SvgNodeType::Polyline: return "Polyline";
- case SvgNodeType::Rect: return "Rect";
- case SvgNodeType::Text: return "Text";
- case SvgNodeType::TextArea: return "TextArea";
- case SvgNodeType::Tspan: return "Tspan";
- case SvgNodeType::Use: return "Use";
- case SvgNodeType::Video: return "Video";
- case SvgNodeType::ClipPath: return "ClipPath";
- case SvgNodeType::Mask: return "Mask";
- default: return "Unknown";
- }
- return "Unknown";
-}
-
-bool isIgnoreUnsupportedLogElements(const char* tagName)
-{
- const auto elementsNum = 1;
- const char* const elements[] = { "title" };
-
- for (unsigned int i = 0; i < elementsNum; ++i) {
- if (!strncmp(tagName, elements[i], strlen(tagName))) {
- return true;
- }
- }
- return false;
-}
-
-bool _isIgnoreUnsupportedLogAttributes(const char* tagAttribute, const char* tagValue)
+bool _isIgnoreUnsupportedLogAttributes(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue)
{
+#ifdef THORVG_LOG_ENABLED
const auto attributesNum = 6;
const struct
{
}
}
return false;
+#endif
+ return true;
}
-#endif
static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
{
static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd)
{
auto p = itr;
- while (*itr == '&' && itr < itrEnd) {
+ while (itr < itrEnd && *itr == '&') {
for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
if (strncmp(itr, xmlEntity[i], xmlEntityLength[i]) == 0) {
itr += xmlEntityLength[i];
static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrStart)
{
auto p = itr;
- while (*(itr - 1) == ';' && itr > itrStart) {
+ while (itr > itrStart && *(itr - 1) == ';') {
for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
if (itr - xmlEntityLength[i] > itrStart &&
strncmp(itr - xmlEntityLength[i], xmlEntity[i], xmlEntityLength[i]) == 0) {
}
+static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff)
+{
+ toff = 0;
+ if (itr[1] == '/') {
+ toff = 1;
+ return SimpleXMLType::Close;
+ } else if (itr[1] == '?') {
+ toff = 1;
+ return SimpleXMLType::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;
+ } else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
+ toff = sizeof("![CDATA[") - 1;
+ return SimpleXMLType::CData;
+ } else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
+ toff = sizeof("!--") - 1;
+ return SimpleXMLType::Comment;
+ } else if (itr + sizeof("<!>") - 1 < itrEnd) {
+ toff = sizeof("!") - 1;
+ return SimpleXMLType::DoctypeChild;
+ }
+ return SimpleXMLType::Open;
+ }
+ return SimpleXMLType::Open;
+}
+
+
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
+const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type)
+{
+#ifdef THORVG_LOG_ENABLED
+ static const char* TYPE_NAMES[] = {
+ "Svg",
+ "G",
+ "Defs",
+ "Animation",
+ "Arc",
+ "Circle",
+ "Ellipse",
+ "Image",
+ "Line",
+ "Path",
+ "Polygon",
+ "Polyline",
+ "Rect",
+ "Text",
+ "TextArea",
+ "Tspan",
+ "Use",
+ "Video",
+ "ClipPath",
+ "Mask",
+ "Unknown",
+ };
+ return TYPE_NAMES[(int) type];
+#endif
+ return nullptr;
+}
+
+
+bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName)
+{
+#ifdef THORVG_LOG_ENABLED
+ const auto elementsNum = 1;
+ const char* const elements[] = { "title" };
+
+ for (unsigned int i = 0; i < elementsNum; ++i) {
+ if (!strncmp(tagName, elements[i], strlen(tagName))) {
+ return true;
+ }
+ }
+ return false;
+#else
+ return true;
+#endif
+}
+
bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
{
}
tval[i] = '\0';
-#ifdef THORVG_LOG_ENABLED
if (!func((void*)data, tmpBuf, tval)) {
- if (!_isIgnoreUnsupportedLogAttributes(tmpBuf, tval)) printf("SVG: Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]\n", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type).c_str(), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id->c_str() : "NO_ID", tmpBuf, tval ? tval : "NONE");
+ 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");
+ }
}
-#else
- func((void*)data, tmpBuf, tval);
-#endif
}
return true;
}
if (!buf || !func) return false;
-#define CB(type, start, end) \
- do { \
- size_t _sz = end - start; \
- bool _ret; \
- _ret = func((void*)data, type, start, _sz); \
- if (!_ret) \
- return false; \
- } while (0)
-
while (itr < itrEnd) {
if (itr[0] == '<') {
- if (itr + 1 >= itrEnd) {
- CB(SimpleXMLType::Error, itr, itrEnd);
- return false;
- } else {
- SimpleXMLType type;
- size_t toff;
- const char* p;
-
- if (itr[1] == '/') {
- type = SimpleXMLType::Close;
- toff = 1;
- } else if (itr[1] == '?') {
- type = SimpleXMLType::Processing;
- toff = 1;
- } 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])))) {
- type = SimpleXMLType::Doctype;
- toff = sizeof("!DOCTYPE") - 1;
- } else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
- type = SimpleXMLType::Comment;
- toff = sizeof("!--") - 1;
- } else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
- type = SimpleXMLType::CData;
- toff = sizeof("![CDATA[") - 1;
- } else if (itr + sizeof("<!>") - 1 < itrEnd) {
- type = SimpleXMLType::DoctypeChild;
- toff = sizeof("!") - 1;
- } else {
- type = SimpleXMLType::Open;
- toff = 0;
+ //Invalid case
+ if (itr + 1 >= itrEnd) return false;
+
+ size_t toff = 0;
+ SimpleXMLType 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 (p) {
+ //Invalid case: '<' nested
+ if (*p == '<') return false;
+ const char *start, *end;
+
+ start = itr + 1 + toff;
+ end = p;
+
+ switch (type) {
+ case SimpleXMLType::Open: {
+ if (p[-1] == '/') {
+ type = SimpleXMLType::OpenEmpty;
+ end--;
+ }
+ break;
+ }
+ case SimpleXMLType::CData: {
+ if (!memcmp(p - 2, "]]", 2)) end -= 2;
+ break;
+ }
+ case SimpleXMLType::Processing: {
+ if (p[-1] == '?') end--;
+ break;
+ }
+ case SimpleXMLType::Comment: {
+ if (!memcmp(p - 2, "--", 2)) end -= 2;
+ break;
+ }
+ default: {
+ break;
}
- } else {
- type = SimpleXMLType::Open;
- toff = 0;
}
- 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 ((p) && (*p == '<')) {
- type = SimpleXMLType::Error;
- toff = 0;
+ if (strip && (type != SimpleXMLType::CData)) {
+ start = _skipWhiteSpacesAndXmlEntities(start, end);
+ end = _unskipWhiteSpacesAndXmlEntities(end, start);
}
- if (p) {
- const char *start, *end;
-
- start = itr + 1 + toff;
- end = p;
-
- switch (type) {
- case SimpleXMLType::Open: {
- if (p[-1] == '/') {
- type = SimpleXMLType::OpenEmpty;
- end--;
- }
- break;
- }
- case SimpleXMLType::CData: {
- if (!memcmp(p - 2, "]]", 2)) end -= 2;
- break;
- }
- case SimpleXMLType::Processing: {
- if (p[-1] == '?') end--;
- break;
- }
- case SimpleXMLType::Comment: {
- if (!memcmp(p - 2, "--", 2)) end -= 2;
- break;
- }
- default: {
- break;
- }
- }
-
- if ((strip) && (type != SimpleXMLType::Error) && (type != SimpleXMLType::CData)) {
- start = _skipWhiteSpacesAndXmlEntities(start, end);
- end = _unskipWhiteSpacesAndXmlEntities(end, start);
- }
-
- CB(type, start, end);
+ if (!func((void*)data, type, start, (unsigned int)(end - start))) return false;
- if (type != SimpleXMLType::Error) itr = p + 1;
- else itr = p;
- } else {
- CB(SimpleXMLType::Error, itr, itrEnd);
- return false;
- }
+ itr = p + 1;
+ } else {
+ return false;
}
} else {
const char *p, *end;
p = itr;
p = _skipWhiteSpacesAndXmlEntities(p, itrEnd);
if (p) {
- CB(SimpleXMLType::Ignored, itr, p);
+ if (!func((void*)data, SimpleXMLType::Ignored, itr, (unsigned int)(p - itr))) return false;
itr = p;
}
}
end = p;
if (strip) end = _unskipWhiteSpacesAndXmlEntities(end, itr);
- if (itr != end) CB(SimpleXMLType::Data, itr, end);
+ if (itr != end && !func((void*)data, SimpleXMLType::Data, itr, (unsigned int)(end - itr))) return false;
- if ((strip) && (end < p)) CB(SimpleXMLType::Ignored, end, p);
+ if (strip && (end < p) && !func((void*)data, SimpleXMLType::Ignored, end, (unsigned int)(p - end))) return false;
itr = p;
}
}
-
-#undef CB
-
return true;
}
-bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data)
+bool simpleXmlParseW3CAttribute(const char* buf, unsigned buflen, simpleXMLAttributeCb func, const void* data)
{
const char* end;
char* key;
if (!buf) return false;
- end = buf + strlen(buf);
+ end = buf + buflen;
key = (char*)alloca(end - buf + 1);
val = (char*)alloca(end - buf + 1);
do {
char* sep = (char*)strchr(buf, ':');
next = (char*)strchr(buf, ';');
+ if (sep >= end)
+ {
+ next = nullptr;
+ sep = nullptr;
+ }
+ if (next >= end) next = nullptr;
key[0] = '\0';
val[0] = '\0';
}
if (key[0]) {
- if (!func((void*)data, key, val)) return false;
+ 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';
+
+ 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");
+ }
+ }
}
buf = next + 1;
}
+/*
+ * Supported formats:
+ * tag {}, .name {}, tag.name{}
+ */
+const char* simpleXmlParseCSSAttribute(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 itrEnd = (const char*)memchr(buf, '{', bufLength);
+
+ if (!itrEnd || itr == itrEnd) return nullptr;
+
+ auto nextElement = (const char*)memchr(itrEnd, '}', bufLength - (itrEnd - buf));
+ if (!nextElement) return nullptr;
+
+ *attrs = itrEnd + 1;
+ *attrsLength = nextElement - *attrs;
+
+ const char *p;
+
+ itrEnd = _simpleXmlUnskipWhiteSpace(itrEnd, itr);
+ if (*(itrEnd - 1) == '.') return nullptr;
+
+ for (p = itr; p < itrEnd; p++) {
+ if (*p == '.') break;
+ }
+
+ if (p == itr) *tag = strdup("all");
+ else *tag = strndup(itr, p - itr);
+
+ if (p == itrEnd) *name = nullptr;
+ else *name = strndup(p + 1, itrEnd - p - 1);
+
+ return (nextElement ? nextElement + 1 : nullptr);
+}
+
+
const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
{
const char *itr = buf, *itrEnd = buf + bufLength;