doc: linguistic changes in C++ API docs
[platform/core/graphics/tizenvg.git] / src / loaders / svg / tvgXmlParser.cpp
1 /*
2  * Copyright (c) 2020-2021 Samsung Electronics Co., Ltd. All rights reserved.
3
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22
23 #include <ctype.h>
24 #include <string>
25
26 #ifdef _WIN32
27     #include <malloc.h>
28 #else
29     #include <alloca.h>
30 #endif
31
32 #include "tvgXmlParser.h"
33
34 /************************************************************************/
35 /* Internal Class Implementation                                        */
36 /************************************************************************/
37
38 #ifdef THORVG_LOG_ENABLED
39
40 #include <stdio.h>
41
42 string simpleXmlNodeTypeToString(SvgNodeType type)
43 {
44     switch (type) {
45         case SvgNodeType::Doc: return "Doc";
46         case SvgNodeType::G: return "G";
47         case SvgNodeType::Defs: return "Defs";
48         case SvgNodeType::Animation: return "Animation";
49         case SvgNodeType::Arc: return "Arc";
50         case SvgNodeType::Circle: return "Circle";
51         case SvgNodeType::Ellipse: return "Ellipse";
52         case SvgNodeType::Image: return "Image";
53         case SvgNodeType::Line: return "Line";
54         case SvgNodeType::Path: return "Path";
55         case SvgNodeType::Polygon: return "Polygon";
56         case SvgNodeType::Polyline: return "Polyline";
57         case SvgNodeType::Rect: return "Rect";
58         case SvgNodeType::Text: return "Text";
59         case SvgNodeType::TextArea: return "TextArea";
60         case SvgNodeType::Tspan: return "Tspan";
61         case SvgNodeType::Use: return "Use";
62         case SvgNodeType::Video: return "Video";
63         case SvgNodeType::ClipPath: return "ClipPath";
64         default: return "Unknown";
65     }
66     return "Unknown";
67 }
68 #endif
69
70 static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
71 {
72     for (; itr < itrEnd; itr++) {
73         if (isspace((unsigned char)*itr)) break;
74     }
75     return itr;
76 }
77
78
79 static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd)
80 {
81     for (; itr < itrEnd; itr++) {
82         if (!isspace((unsigned char)*itr)) break;
83     }
84     return itr;
85 }
86
87
88 static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrStart)
89 {
90     for (itr--; itr > itrStart; itr--) {
91         if (!isspace((unsigned char)*itr)) break;
92     }
93     return itr + 1;
94 }
95
96
97 static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd)
98 {
99     auto p = itr;
100     while (*itr == '&' && itr < itrEnd) {
101         for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
102             if (strncmp(itr, xmlEntity[i], xmlEntityLength[i]) == 0) {
103                 itr += xmlEntityLength[i];
104                 break;
105             }
106         }
107         if (itr == p) break;
108         p = itr;
109     }
110     return itr;
111 }
112
113
114 static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrStart)
115 {
116     auto p = itr;
117     while (*(itr - 1) == ';' && itr > itrStart) {
118         for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
119             if (itr - xmlEntityLength[i] > itrStart &&
120                 strncmp(itr - xmlEntityLength[i], xmlEntity[i], xmlEntityLength[i]) == 0) {
121                 itr -= xmlEntityLength[i];
122                 break;
123             }
124         }
125         if (itr == p) break;
126         p = itr;
127     }
128     return itr;
129 }
130
131
132 static const char* _skipWhiteSpacesAndXmlEntities(const char* itr, const char* itrEnd)
133 {
134     itr = _simpleXmlSkipWhiteSpace(itr, itrEnd);
135     auto p = itr;
136     while (true) {
137         if (p != (itr = _simpleXmlSkipXmlEntities(itr, itrEnd))) p = itr;
138         else break;
139         if (p != (itr = _simpleXmlSkipWhiteSpace(itr, itrEnd))) p = itr;
140         else break;
141     }
142     return itr;
143 }
144
145
146 static const char* _unskipWhiteSpacesAndXmlEntities(const char* itr, const char* itrStart)
147 {
148     itr = _simpleXmlUnskipWhiteSpace(itr, itrStart);
149     auto p = itr;
150     while (true) {
151         if (p != (itr = _simpleXmlUnskipXmlEntities(itr, itrStart))) p = itr;
152         else break;
153         if (p != (itr = _simpleXmlUnskipWhiteSpace(itr, itrStart))) p = itr;
154         else break;
155     }
156     return itr;
157 }
158
159
160 static const char* _simpleXmlFindStartTag(const char* itr, const char* itrEnd)
161 {
162     return (const char*)memchr(itr, '<', itrEnd - itr);
163 }
164
165
166 static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd)
167 {
168     bool insideQuote = false;
169     for (; itr < itrEnd; itr++) {
170         if (*itr == '"') insideQuote = !insideQuote;
171         if (!insideQuote) {
172             if ((*itr == '>') || (*itr == '<'))
173                 return itr;
174         }
175     }
176     return nullptr;
177 }
178
179
180 static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrEnd)
181 {
182     for (; itr < itrEnd; itr++) {
183         if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
184     }
185     return nullptr;
186 }
187
188
189 static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd)
190 {
191     for (; itr < itrEnd; itr++) {
192         if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
193     }
194     return nullptr;
195 }
196
197
198 static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd)
199 {
200     for (; itr < itrEnd; itr++) {
201         if (*itr == '>') return itr;
202     }
203     return nullptr;
204 }
205
206
207 /************************************************************************/
208 /* External Class Implementation                                        */
209 /************************************************************************/
210
211
212 bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
213 {
214     const char *itr = buf, *itrEnd = buf + bufLength;
215     char* tmpBuf = (char*)alloca(bufLength + 1);
216
217     if (!buf || !func) return false;
218
219     while (itr < itrEnd) {
220         const char* p = _skipWhiteSpacesAndXmlEntities(itr, itrEnd);
221         const char *key, *keyEnd, *value, *valueEnd;
222         char* tval;
223
224         if (p == itrEnd) return true;
225
226         key = p;
227         for (keyEnd = key; keyEnd < itrEnd; keyEnd++) {
228             if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
229         }
230         if (keyEnd == itrEnd) return false;
231         if (keyEnd == key) continue;
232
233         if (*keyEnd == '=') value = keyEnd + 1;
234         else {
235             value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd);
236             if (!value) return false;
237             value++;
238         }
239         keyEnd = _simpleXmlUnskipXmlEntities(keyEnd, key);
240
241         value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
242         if (value == itrEnd) return false;
243
244         if ((*value == '"') || (*value == '\'')) {
245             valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value);
246             if (!valueEnd) return false;
247             value++;
248         } else {
249             valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd);
250         }
251
252         itr = valueEnd + 1;
253
254         value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
255         valueEnd = _unskipWhiteSpacesAndXmlEntities(valueEnd, value);
256
257         memcpy(tmpBuf, key, keyEnd - key);
258         tmpBuf[keyEnd - key] = '\0';
259
260         tval = tmpBuf + (keyEnd - key) + 1;
261         int i = 0;
262         while (value < valueEnd) {
263             value = _simpleXmlSkipXmlEntities(value, valueEnd);
264             tval[i++] = *value;
265             value++;
266         }
267         tval[i] = '\0';
268
269 #ifdef THORVG_LOG_ENABLED
270         if (!func((void*)data, tmpBuf, tval)) printf("SVG: Unsupported attributes used [Elements type: %s][Attribute: %s]\n", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type).c_str(), tmpBuf);
271 #else
272         func((void*)data, tmpBuf, tval);
273 #endif
274     }
275     return true;
276 }
277
278
279 bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data)
280 {
281     const char *itr = buf, *itrEnd = buf + bufLength;
282
283     if (!buf || !func) return false;
284
285 #define CB(type, start, end)                                     \
286     do {                                                         \
287         size_t _sz = end - start;                                \
288         bool _ret;                                               \
289         _ret = func((void*)data, type, start, _sz);              \
290         if (!_ret)                                               \
291             return false;                                        \
292     } while (0)
293
294     while (itr < itrEnd) {
295         if (itr[0] == '<') {
296             if (itr + 1 >= itrEnd) {
297                 CB(SimpleXMLType::Error, itr, itrEnd);
298                 return false;
299             } else {
300                 SimpleXMLType type;
301                 size_t toff;
302                 const char* p;
303
304                 if (itr[1] == '/') {
305                     type = SimpleXMLType::Close;
306                     toff = 1;
307                 } else if (itr[1] == '?') {
308                     type = SimpleXMLType::Processing;
309                     toff = 1;
310                 } else if (itr[1] == '!') {
311                     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])))) {
312                         type = SimpleXMLType::Doctype;
313                         toff = sizeof("!DOCTYPE") - 1;
314                     } else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
315                         type = SimpleXMLType::Comment;
316                         toff = sizeof("!--") - 1;
317                     } else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
318                         type = SimpleXMLType::CData;
319                         toff = sizeof("![CDATA[") - 1;
320                     } else if (itr + sizeof("<!>") - 1 < itrEnd) {
321                         type = SimpleXMLType::DoctypeChild;
322                         toff = sizeof("!") - 1;
323                     } else {
324                         type = SimpleXMLType::Open;
325                         toff = 0;
326                     }
327                 } else {
328                     type = SimpleXMLType::Open;
329                     toff = 0;
330                 }
331
332                 if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
333                 else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
334                 else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
335                 else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
336
337                 if ((p) && (*p == '<')) {
338                     type = SimpleXMLType::Error;
339                     toff = 0;
340                 }
341
342                 if (p) {
343                     const char *start, *end;
344
345                     start = itr + 1 + toff;
346                     end = p;
347
348                     switch (type) {
349                         case SimpleXMLType::Open: {
350                             if (p[-1] == '/') {
351                                 type = SimpleXMLType::OpenEmpty;
352                                 end--;
353                             }
354                             break;
355                         }
356                         case SimpleXMLType::CData: {
357                             if (!memcmp(p - 2, "]]", 2)) end -= 2;
358                             break;
359                         }
360                         case SimpleXMLType::Processing: {
361                             if (p[-1] == '?') end--;
362                             break;
363                         }
364                         case SimpleXMLType::Comment: {
365                             if (!memcmp(p - 2, "--", 2)) end -= 2;
366                             break;
367                         }
368                         default: {
369                             break;
370                         }
371                     }
372
373                     if ((strip) && (type != SimpleXMLType::Error) && (type != SimpleXMLType::CData)) {
374                         start = _skipWhiteSpacesAndXmlEntities(start, end);
375                         end = _unskipWhiteSpacesAndXmlEntities(end, start);
376                     }
377
378                     CB(type, start, end);
379
380                     if (type != SimpleXMLType::Error) itr = p + 1;
381                     else itr = p;
382                 } else {
383                     CB(SimpleXMLType::Error, itr, itrEnd);
384                     return false;
385                 }
386             }
387         } else {
388             const char *p, *end;
389
390             if (strip) {
391                 p = itr;
392                 p = _skipWhiteSpacesAndXmlEntities(p, itrEnd);
393                 if (p) {
394                     CB(SimpleXMLType::Ignored, itr, p);
395                     itr = p;
396                 }
397             }
398
399             p = _simpleXmlFindStartTag(itr, itrEnd);
400             if (!p) p = itrEnd;
401
402             end = p;
403             if (strip) end = _unskipWhiteSpacesAndXmlEntities(end, itr);
404
405             if (itr != end) CB(SimpleXMLType::Data, itr, end);
406
407             if ((strip) && (end < p)) CB(SimpleXMLType::Ignored, end, p);
408
409             itr = p;
410         }
411     }
412
413 #undef CB
414
415     return true;
416 }
417
418
419 bool simpleXmlParseW3CAttribute(const char* buf, simpleXMLAttributeCb func, const void* data)
420 {
421     const char* end;
422     char* key;
423     char* val;
424     char* next;
425
426     if (!buf) return false;
427
428     end = buf + strlen(buf);
429     key = (char*)alloca(end - buf + 1);
430     val = (char*)alloca(end - buf + 1);
431
432     if (buf == end) return true;
433
434     do {
435         char* sep = (char*)strchr(buf, ':');
436         next = (char*)strchr(buf, ';');
437
438         key[0] = '\0';
439         val[0] = '\0';
440
441         if (next == nullptr && sep != nullptr) {
442             memcpy(key, buf, sep - buf);
443             key[sep - buf] = '\0';
444
445             memcpy(val, sep + 1, end - sep - 1);
446             val[end - sep - 1] = '\0';
447         } else if (sep < next && sep != nullptr) {
448             memcpy(key, buf, sep - buf);
449             key[sep - buf] = '\0';
450
451             memcpy(val, sep + 1, next - sep - 1);
452             val[next - sep - 1] = '\0';
453         } else if (next) {
454             memcpy(key, buf, next - buf);
455             key[next - buf] = '\0';
456         }
457
458         if (key[0]) {
459             if (!func((void*)data, key, val)) return false;
460         }
461
462         buf = next + 1;
463     } while (next != nullptr);
464
465     return true;
466 }
467
468
469 const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
470 {
471     const char *itr = buf, *itrEnd = buf + bufLength;
472
473     for (; itr < itrEnd; itr++) {
474         if (!isspace((unsigned char)*itr)) {
475             //User skip tagname and already gave it the attributes.
476             if (*itr == '=') return buf;
477         } else {
478             itr = _simpleXmlUnskipXmlEntities(itr, buf);
479             if (itr == itrEnd) return nullptr;
480             return itr;
481         }
482     }
483
484     return nullptr;
485 }