1 /**
2 * @file lv_svg_token.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9
10 #include "lv_svg_token.h"
11 #if LV_USE_SVG
12
13 #include "../../../lvgl.h"
14 #include <ctype.h>
15 #include <string.h>
16
17 /*********************
18 * DEFINES
19 *********************/
20
21 /**********************
22 * TYPEDEFS
23 **********************/
24
25 /*
26 * tag mask quote mask tag search comment doc type xml inst
27 * | 0 0 0 | 0 0 | 0 | 0 | 0 | 0 | 0 |
28 */
29 enum {
30 SVG_TAG_MASK = (1 << 3) - 1,
31 SVG_QUOTE_MASK = (1 << 5) - (1 << 3),
32 SVG_TAG = 1 << 5,
33 SVG_SEARCH = 1 << 6,
34 SVG_COMMENT = 1 << 7,
35 SVG_DOCTYPE = 1 << 8,
36 SVG_XMLINST = 1 << 9,
37 };
38 typedef uint32_t _lv_svg_parser_bits_t;
39
40 enum {
41 SVG_NO_QUOTE = 0,
42 SVG_SINGLE_QUOTE = 1,
43 SVG_DOUBLE_QUOTE = 2,
44 };
45 typedef uint32_t _lv_svg_parser_quote_t;
46
47 enum {
48 SVG_NO_TAG = 0,
49 SVG_TAG_NAME = 1,
50 SVG_ATTR_START = 2,
51 SVG_ATTR_NAME = 3,
52 SVG_SEARCH_EQUAL = 4,
53 SVG_SEARCH_VALUE = 5,
54 SVG_QUOTE_VALUE = 6,
55 SVG_VALUE = 7,
56 };
57 typedef uint32_t _lv_svg_parser_tag_state_t;
58
59 typedef struct {
60 uint32_t flags;
61 const char * cur;
62 const char * end;
63 } _lv_svg_parser_state_t;
64
65 /**********************
66 * STATIC PROTOTYPES
67 **********************/
_set_state(_lv_svg_parser_state_t * state,uint32_t bit)68 static void _set_state(_lv_svg_parser_state_t * state, uint32_t bit)
69 {
70 state->flags |= bit;
71 }
72
_clear_state(_lv_svg_parser_state_t * state,uint32_t bit)73 static void _clear_state(_lv_svg_parser_state_t * state, uint32_t bit)
74 {
75 state->flags &= ~bit;
76 }
77
_is_state(_lv_svg_parser_state_t * state,uint32_t bit)78 static bool _is_state(_lv_svg_parser_state_t * state, uint32_t bit)
79 {
80 return state->flags & bit;
81 }
82
_set_tag_state(_lv_svg_parser_state_t * state,uint32_t bit)83 static void _set_tag_state(_lv_svg_parser_state_t * state, uint32_t bit)
84 {
85 state->flags = (state->flags & ~SVG_TAG_MASK) | bit;
86 }
87
_set_quote_state(_lv_svg_parser_state_t * state,uint32_t bit)88 static void _set_quote_state(_lv_svg_parser_state_t * state, uint32_t bit)
89 {
90 state->flags = (state->flags & ~SVG_QUOTE_MASK) | (bit << 3);
91 }
92
_special_handle(_lv_svg_parser_state_t * state)93 static bool _special_handle(_lv_svg_parser_state_t * state)
94 {
95 return state->flags & (SVG_TAG | SVG_SEARCH | SVG_TAG_MASK | SVG_COMMENT | SVG_DOCTYPE | SVG_XMLINST);
96 }
97
_lv_svg_token_init(_lv_svg_token_t * token)98 static void _lv_svg_token_init(_lv_svg_token_t * token)
99 {
100 token->start = NULL;
101 token->end = NULL;
102 token->type = LV_SVG_TOKEN_CONTENT;
103 token->flat = false;
104 token->cur_attr = NULL;
105 lv_array_init(&token->attrs, LV_ARRAY_DEFAULT_CAPACITY, sizeof(_lv_svg_token_attr_t));
106 }
107
_lv_svg_token_reset(_lv_svg_token_t * token)108 static void _lv_svg_token_reset(_lv_svg_token_t * token)
109 {
110 token->start = NULL;
111 token->end = NULL;
112 token->type = LV_SVG_TOKEN_CONTENT;
113 token->flat = false;
114 token->cur_attr = NULL;
115 lv_array_clear(&token->attrs);
116 }
117
_lv_svg_token_process(_lv_svg_token_t * token,svg_token_process cb,void * data)118 static bool _lv_svg_token_process(_lv_svg_token_t * token, svg_token_process cb, void * data)
119 {
120 if(!token->start || SVG_TOKEN_LEN(token) == 0)
121 return true;
122
123 bool ret = cb(token, data);
124 _lv_svg_token_reset(token);
125 return ret;
126 }
127
_new_svg_attr(_lv_svg_token_t * token)128 static _lv_svg_token_attr_t * _new_svg_attr(_lv_svg_token_t * token)
129 {
130 if((lv_array_size(&token->attrs) + 1) > lv_array_capacity(&token->attrs)) {
131 lv_array_resize(&token->attrs, token->attrs.capacity << 1);
132 }
133
134 token->attrs.size++;
135 _lv_svg_token_attr_t * attr = lv_array_at(&token->attrs, token->attrs.size - 1);
136 lv_memset(attr, 0, sizeof(_lv_svg_token_attr_t));
137 return attr;
138 }
139
_svg_parser_xml_inst(_lv_svg_parser_state_t * state,_lv_svg_token_t * token)140 static void _svg_parser_xml_inst(_lv_svg_parser_state_t * state, _lv_svg_token_t * token)
141 {
142 LV_UNUSED(token);
143
144 while(state->cur <= state->end) {
145 char ch = *(state->cur);
146 if(ch == '>' && (*(state->cur - 1)) == '?') {
147 _clear_state(state, SVG_XMLINST);
148 state->cur++;
149 break;
150 }
151 state->cur++;
152 }
153 }
154
_svg_parser_comment(_lv_svg_parser_state_t * state,_lv_svg_token_t * token)155 static void _svg_parser_comment(_lv_svg_parser_state_t * state, _lv_svg_token_t * token)
156 {
157 LV_UNUSED(token);
158
159 while(state->cur <= state->end) {
160 char ch = *(state->cur);
161 if(ch == '>' && (*(state->cur - 1)) == '-' && (*(state->cur - 2)) == '-') {
162 _clear_state(state, SVG_COMMENT);
163 state->cur++;
164 break;
165 }
166 state->cur++;
167 }
168 }
169
_svg_parser_doctype(_lv_svg_parser_state_t * state,_lv_svg_token_t * token)170 static void _svg_parser_doctype(_lv_svg_parser_state_t * state, _lv_svg_token_t * token)
171 {
172 LV_UNUSED(token);
173
174 //TODO: processing DTD type
175 while(state->cur <= state->end) {
176 char ch = *(state->cur);
177 if(ch == '>') {
178 _clear_state(state, SVG_DOCTYPE);
179 state->cur++;
180 break;
181 }
182 state->cur++;
183 }
184 }
185
_svg_parser_tag(_lv_svg_parser_state_t * state,_lv_svg_token_t * token,svg_token_process cb,void * data)186 static bool _svg_parser_tag(_lv_svg_parser_state_t * state, _lv_svg_token_t * token, svg_token_process cb, void * data)
187 {
188 while(state->cur <= state->end) {
189 switch(state->flags & SVG_TAG_MASK) {
190 case SVG_NO_TAG: {
191 if(!_lv_svg_token_process(token, cb, data)) {
192 return false;
193 }
194 state->cur++;
195 }
196 return true;
197 case SVG_TAG_NAME: {
198 char ch = *(state->cur);
199 if(ch == '/') {
200 token->type = LV_SVG_TOKEN_END;
201 state->cur++;
202 if(!token->start) {
203 token->start = state->cur;
204 }
205 continue;
206 }
207 else if(ch == '>' || isspace(ch)) {
208 token->end = state->cur;
209 _set_tag_state(state, SVG_ATTR_START);
210 continue;
211 }
212 else {
213 if(!token->start) {
214 token->type = LV_SVG_TOKEN_BEGIN;
215 token->start = state->cur;
216 }
217 state->cur++;
218 continue;
219 }
220 }
221 break;
222 case SVG_ATTR_START: {
223 char ch = *(state->cur);
224 if(!isspace(ch) && ch != '\'' && ch != '\"') {
225 if(ch == '/') {
226 token->flat = true;
227 state->cur++;
228 continue;
229 }
230 if(ch == '>') {
231 _set_tag_state(state, SVG_NO_TAG);
232 }
233 else {
234 token->cur_attr = NULL;
235 _set_tag_state(state, SVG_ATTR_NAME);
236 }
237 continue;
238 }
239 }
240 break;
241 case SVG_ATTR_NAME: {
242 if(!token->cur_attr) {
243 token->cur_attr = _new_svg_attr(token);
244 }
245 char ch = *(state->cur);
246 if(isspace(ch) || ch == '=' || ch == '/' || ch == '>') {
247 token->cur_attr->name_end = state->cur;
248 _set_tag_state(state, SVG_SEARCH_EQUAL);
249 continue;
250 }
251 else {
252 if(!token->cur_attr->name_start) {
253 token->cur_attr->name_start = state->cur;
254 }
255 state->cur++;
256 continue;
257 }
258 }
259 break;
260 case SVG_SEARCH_EQUAL: {
261 char ch = *(state->cur);
262 if(!isspace(ch) && ch != '/' && ch != '\'' && ch != '\"') {
263 if(ch == '=') {
264 _set_tag_state(state, SVG_SEARCH_VALUE);
265 }
266 else {
267 // attr name has empty value
268 token->cur_attr = NULL;
269 _set_tag_state(state, SVG_ATTR_START);
270 continue;
271 }
272 }
273 }
274 break;
275 case SVG_SEARCH_VALUE: {
276 char ch = *(state->cur);
277 if(!isspace(ch)) {
278 if(ch == '\'' || ch == '\"') {
279 if(ch == '\'') {
280 _set_quote_state(state, SVG_SINGLE_QUOTE);
281 }
282 else {
283 _set_quote_state(state, SVG_DOUBLE_QUOTE);
284 }
285 _set_tag_state(state, SVG_QUOTE_VALUE);
286 }
287 else {
288 _set_tag_state(state, SVG_VALUE);
289 continue;
290 }
291 }
292 }
293 break;
294 case SVG_QUOTE_VALUE: {
295 char ch = *(state->cur);
296 if((ch == '\'' && ((state->flags & SVG_QUOTE_MASK) >> 3) == SVG_SINGLE_QUOTE)
297 || (ch == '\"' && ((state->flags & SVG_QUOTE_MASK) >> 3) == SVG_DOUBLE_QUOTE)) {
298 if(!token->cur_attr->value_start) {
299 token->cur_attr->value_start = state->cur;
300 }
301 token->cur_attr->value_end = state->cur;
302 _set_quote_state(state, SVG_NO_QUOTE);
303 _set_tag_state(state, SVG_ATTR_START);
304 continue;
305 }
306 else {
307 if(!token->cur_attr->value_start) {
308 token->cur_attr->value_start = state->cur;
309 }
310 state->cur++;
311 continue;
312 }
313 }
314 break;
315 case SVG_VALUE: {
316 char ch = *(state->cur);
317 if(isspace(ch) || ch == '>' || ch == '/') {
318 if(!token->cur_attr->value_start) {
319 token->cur_attr->value_start = state->cur;
320 }
321 token->cur_attr->value_end = state->cur;
322 _set_quote_state(state, SVG_NO_QUOTE);
323 _set_tag_state(state, SVG_ATTR_START);
324 continue;
325 }
326 else {
327 if(!token->cur_attr->value_start) {
328 token->cur_attr->value_start = state->cur;
329 }
330 state->cur++;
331 continue;
332 }
333 }
334 break;
335 }
336 state->cur++;
337 }
338 return true;
339 }
340
341 /**********************
342 * STATIC VARIABLES
343 **********************/
344
345 /**********************
346 * MACROS
347 **********************/
348
349 /**********************
350 * GLOBAL FUNCTIONS
351 **********************/
352
_lv_svg_tokenizer(const char * svg_data,uint32_t data_len,svg_token_process cb,void * data)353 bool _lv_svg_tokenizer(const char * svg_data, uint32_t data_len, svg_token_process cb, void * data)
354 {
355 LV_ASSERT_NULL(svg_data);
356 LV_ASSERT(data_len > 0);
357 LV_ASSERT_NULL(cb);
358 LV_ASSERT_NULL(data);
359
360 _lv_svg_token_t token;
361 _lv_svg_token_init(&token);
362 _lv_svg_parser_state_t state = {
363 .flags = 0,
364 .cur = svg_data,
365 .end = svg_data + data_len,
366 };
367
368 while(state.cur <= state.end) {
369 char ch = *(state.cur);
370 if(ch == '\r' || ch == '\n') { // skip LR character
371 state.cur++;
372 continue;
373 }
374 else if(_special_handle(&state)) {
375 if(_is_state(&state, SVG_TAG)) {
376 _clear_state(&state, SVG_TAG);
377 switch(ch) {
378 case '/': // end tag
379 _set_tag_state(&state, SVG_TAG_NAME);
380 break;
381 case '!': {
382 // <!-- comment or <!DOCTYPE>
383 _set_state(&state, SVG_SEARCH); // get more character
384 state.cur++;
385 }
386 break;
387 case '?': {
388 // xml instruction
389 _set_state(&state, SVG_XMLINST);
390 state.cur++;
391 }
392 break;
393 default: {
394 if(isalpha(ch)) {
395 _set_tag_state(&state, SVG_TAG_NAME);
396 }
397 else {
398 LV_LOG_ERROR("svg document parser error!");
399 lv_array_deinit(&token.attrs);
400 return false;
401 }
402 }
403 }
404 // process token
405 if(!_lv_svg_token_process(&token, cb, data)) {
406 LV_LOG_ERROR("svg document parser error!");
407 lv_array_deinit(&token.attrs);
408 return false;
409 }
410 }
411 else if(_is_state(&state, SVG_SEARCH)) {
412 if(ch == '-' || isalpha(ch)) {
413 if(!token.start) {
414 token.start = state.cur;
415 }
416 token.end = state.cur;
417 }
418 else {
419 // processing as a normal tag name.
420 _clear_state(&state, SVG_SEARCH);
421 _set_tag_state(&state, SVG_TAG_NAME);
422 continue;
423 }
424
425 if(((token.end - token.start) == 1) && (token.start[0] == '-') && (token.start[1] == '-')) {
426 // is <!-- comment start
427 _clear_state(&state, SVG_SEARCH);
428 token.start = token.end = NULL;
429 _set_state(&state, SVG_COMMENT);
430 }
431 else if(((token.end - token.start) == 6) && (strncmp(token.start, "DOCTYPE", 7) == 0)) {
432 _clear_state(&state, SVG_SEARCH);
433 token.start = token.end = NULL;
434 _set_state(&state, SVG_DOCTYPE);
435 }
436 state.cur++;
437 }
438 else if(_is_state(&state, SVG_COMMENT)) {
439 _svg_parser_comment(&state, &token);
440 }
441 else if(_is_state(&state, SVG_DOCTYPE)) {
442 _svg_parser_doctype(&state, &token);
443 }
444 else if(_is_state(&state, SVG_TAG_MASK)) {
445 if(!_svg_parser_tag(&state, &token, cb, data)) {
446 LV_LOG_ERROR("svg document parser error!");
447 lv_array_deinit(&token.attrs);
448 return false;
449 }
450 }
451 else if(_is_state(&state, SVG_XMLINST)) {
452 _svg_parser_xml_inst(&state, &token);
453 }
454 }
455 else {
456 switch(ch) {
457 case '<': {
458 _set_state(&state, SVG_TAG); // start a new tag
459 state.cur++;
460 }
461 break;
462 default: {
463 if(!token.start) {
464 token.start = state.cur;
465 }
466 if(state.cur == state.end) {
467 goto finish;
468 }
469 token.end = ++state.cur;
470 }
471 }
472 }
473 }
474
475 finish:
476 lv_array_deinit(&token.attrs);
477 return true;
478 }
479
480 /**********************
481 * STATIC FUNCTIONS
482 **********************/
483 #endif /*LV_USE_SVG*/
484