1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Cyril Concolato
5  *			Copyright (c) Telecom ParisTech 2004-2012
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / Scene Compositor sub-project
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/utf.h>
27 
28 #ifndef GPAC_DISABLE_SVG
29 
30 #include "visual_manager.h"
31 #include "nodes_stacks.h"
32 
33 typedef struct
34 {
35 	Drawable *drawable;
36 	Fixed prev_size;
37 	u32 prev_flags;
38 	u32 prev_anchor;
39 	GF_List *spans;
40 	GF_Rect bounds;
41 } SVG_TextStack;
42 
svg_reset_text_stack(SVG_TextStack * st)43 static void svg_reset_text_stack(SVG_TextStack *st)
44 {
45 	while (gf_list_count(st->spans)) {
46 		GF_TextSpan *span = (GF_TextSpan*)gf_list_get(st->spans, 0);
47 		gf_list_rem(st->spans, 0);
48 		gf_font_manager_delete_span(NULL, span);
49 	}
50 }
51 
svg_update_bounds(SVG_TextStack * st)52 static void svg_update_bounds(SVG_TextStack *st)
53 {
54 	u32 i=0;
55 	GF_TextSpan *span;
56 	/*finally compute text bounds*/
57 	st->bounds.width = st->bounds.height = 0;
58 	st->bounds.x = st->bounds.y = 0;
59 	while ( (span = (GF_TextSpan*)gf_list_enum(st->spans, &i)) ) {
60 		gf_font_manager_refresh_span_bounds(span);
61 		gf_rect_union(&st->bounds, &span->bounds);
62 	}
63 }
64 
svg_finalize_sort(DrawableContext * ctx,SVG_TextStack * st,GF_TraverseState * tr_state)65 static void svg_finalize_sort(DrawableContext *ctx, SVG_TextStack *st, GF_TraverseState * tr_state)
66 {
67 #ifndef GPAC_DISABLE_3D
68 	if (tr_state->visual->type_3d) {
69 		gf_font_spans_draw_3d(st->spans, tr_state, &ctx->aspect, 0, GF_FALSE);
70 
71 		drawable_check_focus_highlight(ctx->drawable->node, tr_state, &st->bounds);
72 		ctx->drawable = NULL;
73 		return;
74 	}
75 #endif
76 	/*if text selection mode, we must force redraw of the entire text span because we don't
77 	if glyphs have been (un)selected*/
78 	if (!tr_state->immediate_draw &&
79 	        /*text selection on*/
80 	        (tr_state->visual->compositor->text_selection
81 	         /*text sel release*/
82 	         || (tr_state->visual->compositor->store_text_state==GF_SC_TSEL_RELEASED))
83 	   ) {
84 		GF_TextSpan *span;
85 		u32 i = 0;
86 		Bool unselect = (tr_state->visual->compositor->store_text_state==GF_SC_TSEL_RELEASED) ? GF_TRUE : GF_FALSE;
87 		while ((span = (GF_TextSpan*)gf_list_enum(st->spans, &i))) {
88 			if (span->flags & GF_TEXT_SPAN_SELECTED) {
89 				if (unselect) span->flags &= ~GF_TEXT_SPAN_SELECTED;
90 				ctx->flags |= CTX_APP_DIRTY;
91 			}
92 		}
93 	}
94 	drawable_finalize_sort(ctx, tr_state, &st->bounds);
95 }
96 
97 /*@styles indicates font styles (PLAIN, BOLD, ITALIC, BOLDITALIC and UNDERLINED, STRIKEOUT)*/
svg_get_font_styles(GF_TraverseState * tr_state)98 static u32 svg_get_font_styles(GF_TraverseState * tr_state)
99 {
100 	u32 styles = 0;
101 	switch(*tr_state->svg_props->font_style) {
102 	case SVG_FONTSTYLE_ITALIC:
103 		styles = GF_FONT_ITALIC;
104 		break;
105 	case SVG_FONTSTYLE_OBLIQUE:
106 		styles = GF_FONT_OBLIQUE;
107 		break;
108 	}
109 	if (*tr_state->svg_props->font_variant==SVG_FONTVARIANT_SMALLCAPS)
110 		styles |= GF_FONT_SMALLCAPS;
111 
112 	switch(*tr_state->svg_props->font_weight) {
113 	case SVG_FONTWEIGHT_100:
114 		styles |= GF_FONT_WEIGHT_100;
115 		break;
116 	case SVG_FONTWEIGHT_LIGHTER:
117 		styles |= GF_FONT_WEIGHT_LIGHTER;
118 		break;
119 	case SVG_FONTWEIGHT_200:
120 		styles |= GF_FONT_WEIGHT_200;
121 		break;
122 	case SVG_FONTWEIGHT_300:
123 		styles |= GF_FONT_WEIGHT_300;
124 		break;
125 	case SVG_FONTWEIGHT_400:
126 		styles |= GF_FONT_WEIGHT_400;
127 		break;
128 	case SVG_FONTWEIGHT_NORMAL:
129 		styles |= GF_FONT_WEIGHT_NORMAL;
130 		break;
131 	case SVG_FONTWEIGHT_500:
132 		styles |= GF_FONT_WEIGHT_500;
133 		break;
134 	case SVG_FONTWEIGHT_600:
135 		styles |= GF_FONT_WEIGHT_600;
136 		break;
137 	case SVG_FONTWEIGHT_700:
138 		styles |= GF_FONT_WEIGHT_700;
139 		break;
140 	case SVG_FONTWEIGHT_BOLD:
141 		styles |= GF_FONT_WEIGHT_BOLD;
142 		break;
143 	case SVG_FONTWEIGHT_800:
144 		styles |= GF_FONT_WEIGHT_800;
145 		break;
146 	case SVG_FONTWEIGHT_900:
147 		styles |= GF_FONT_WEIGHT_900;
148 		break;
149 	case SVG_FONTWEIGHT_BOLDER:
150 		styles |= GF_FONT_WEIGHT_BOLDER;
151 		break;
152 	}
153 
154 	return styles;
155 }
156 
157 
gf_compositor_svg_set_font(GF_FontManager * fm,char * a_font,u32 styles,Bool check_only)158 GF_Font *gf_compositor_svg_set_font(GF_FontManager *fm, char *a_font, u32 styles, Bool check_only)
159 {
160 	GF_Font *font = NULL;
161 	char *fonts[50];
162 	u32 nb_fonts = 0;
163 
164 	while (a_font) {
165 		char *sep;
166 		while (strchr("\t\r\n ", a_font[0])) a_font++;
167 
168 		sep = strchr(a_font, ',');
169 		if (sep) sep[0] = 0;
170 
171 		if (a_font[0] == '\'') {
172 			char *sep_end = strchr(a_font+1, '\'');
173 			if (sep_end) sep_end[0] = 0;
174 			a_font++;
175 			fonts[nb_fonts] = gf_strdup(a_font);
176 			nb_fonts++;
177 			if (sep_end) sep_end[0] = '\'';
178 		} else {
179 			u32 skip = 0;
180 			size_t len = strlen(a_font)-1;
181 
182 			while (a_font[len-skip] == ' ') skip++;
183 			if (skip) a_font[len-skip+1] = 0;
184 			fonts[nb_fonts] = gf_strdup(a_font);
185 			nb_fonts++;
186 			if (skip) a_font[len-skip] = ' ';
187 		}
188 
189 		if (sep) {
190 			sep[0] = ',';
191 			a_font = sep+1;
192 		} else {
193 			a_font = NULL;
194 		}
195 		if (nb_fonts==50) break;
196 	}
197 	font = gf_font_manager_set_font_ex(fm, fonts, nb_fonts, styles, check_only);
198 	while (nb_fonts) {
199 		gf_free(fonts[nb_fonts-1]);
200 		nb_fonts--;
201 	}
202 	return font;
203 }
204 
svg_set_font(GF_TraverseState * tr_state,GF_FontManager * fm)205 static GF_Font *svg_set_font(GF_TraverseState * tr_state, GF_FontManager *fm)
206 {
207 	return gf_compositor_svg_set_font(fm, tr_state->svg_props->font_family->value, svg_get_font_styles(tr_state), GF_FALSE);
208 }
209 
210 
211 
svg_apply_text_anchor(GF_TraverseState * tr_state,Fixed * width)212 static void svg_apply_text_anchor(GF_TraverseState * tr_state, Fixed *width)
213 {
214 	Bool reversed = GF_FALSE;
215 	if (!tr_state->svg_props->text_anchor) {
216 		*width = 0;
217 		return;
218 	}
219 	if (*width < 0) {
220 		*width *= -1;
221 		reversed = GF_TRUE;
222 	}
223 	switch(*tr_state->svg_props->text_anchor) {
224 	case SVG_TEXTANCHOR_MIDDLE:
225 		*width = -(*width)/2;
226 		break;
227 	case SVG_TEXTANCHOR_END:
228 		*width = reversed ? 0 : -(*width);
229 		break;
230 	case SVG_TEXTANCHOR_START:
231 	default:
232 		*width = reversed ? -(*width) : 0;
233 		break;
234 	}
235 }
236 
svg_get_text_span(GF_FontManager * fm,GF_Font * font,Fixed font_size,Bool x_offsets,Bool y_offsets,Bool rotate,SVGAllAttributes * atts,char * textContent,const char * lang,GF_TraverseState * tr_state)237 static GF_TextSpan *svg_get_text_span(GF_FontManager *fm, GF_Font *font, Fixed font_size, Bool x_offsets, Bool y_offsets, Bool rotate, SVGAllAttributes *atts, char *textContent, const char *lang, GF_TraverseState *tr_state)
238 {
239 	GF_TextSpan *span = NULL;
240 	char *dup_text;
241 	u32 i, j, len;
242 	char prev;
243 
244 	Bool preserve = (atts->xml_space && (*atts->xml_space==XML_SPACE_PRESERVE)) ? GF_TRUE : GF_FALSE;
245 
246 	len = (u32) strlen(textContent);
247 	dup_text = (char*)gf_malloc(len+1);
248 
249 	switch (tr_state->last_char_type) {
250 	case 2:
251 		prev = 0;
252 		break;
253 	case 0:
254 	case 1:
255 	default:
256 		prev = ' ';
257 		break;
258 	}
259 	for (i = 0, j = 0; i < len; i++) {
260 		if (textContent[i] == ' ') {
261 			if (prev == ' ' && !preserve) {
262 				/* ignore space */
263 			} else {
264 				dup_text[j] = textContent[i];
265 				prev = dup_text[j];
266 				j++;
267 			}
268 		} else if (textContent[i] == '\t') {
269 			if (prev == ' ' && !preserve) {
270 				/* ignore space */
271 			} else {
272 				dup_text[j] = ' ';
273 				prev = dup_text[j];
274 				j++;
275 			}
276 		} else if ((textContent[i] == '\n') ||
277 		           (textContent[i] == '\r')
278 		          ) {
279 			if (prev == ' ' && preserve) {
280 				dup_text[j] = ' ';
281 				prev = dup_text[j];
282 				j++;
283 			} else if (!i && !prev) {
284 				prev = dup_text[j] = ' ';
285 				j++;
286 			}
287 		} else if (
288 		    (((u8) textContent[i] == 0xc2) && ((u8) textContent[i+1] == 0xa0))
289 		) {
290 			if (prev == ' ' && !preserve) {
291 				/* ignore space */
292 			} else {
293 				dup_text[j] = ' ';
294 				prev = dup_text[j];
295 				j++;
296 			}
297 			i++;
298 		} else {
299 			dup_text[j] = textContent[i];
300 			prev = dup_text[j];
301 			j++;
302 		}
303 	}
304 	dup_text[j] = 0;
305 	if (!j) tr_state->last_char_type = 1;
306 	else tr_state->last_char_type = (dup_text[j-1]==' ') ? 1 : 2;
307 	/*SVG text is fliped by default (text y-axis is the inverse of SVG y-axis*/
308 	span = gf_font_manager_create_span(fm, font, dup_text, font_size, x_offsets, y_offsets, rotate, lang, GF_TRUE, 0, tr_state->text_parent);
309 	gf_free(dup_text);
310 	if (span) span->flags |= GF_TEXT_SPAN_HORIZONTAL;
311 	return span;
312 }
313 
314 
315 
316 typedef struct
317 {
318 	GF_TextSpan *span;
319 	u32 first_glyph, last_glyph;
320 } textArea_state;
321 
svg_text_area_reset_state(GF_TraverseState * tr_state)322 static void svg_text_area_reset_state(GF_TraverseState *tr_state)
323 {
324 	Fixed remain = 0;
325 	u32 i, count;
326 	count = gf_list_count(tr_state->x_anchors);
327 
328 	if (tr_state->svg_props->text_align && tr_state->text_end_x) {
329 		switch(*tr_state->svg_props->text_align) {
330 		case SVG_TEXTALIGN_CENTER:
331 			remain = (tr_state->max_length - tr_state->text_end_x) / 2;
332 			break;
333 		case SVG_TEXTALIGN_END:
334 			remain = tr_state->max_length - tr_state->text_end_x;
335 			break;
336 		default:
337 			remain = 0;
338 			break;
339 		}
340 	}
341 
342 
343 	for (i=0; i<count; i++) {
344 		textArea_state *st = (textArea_state*)gf_list_get(tr_state->x_anchors, i);
345 		if (remain) {
346 			u32 j;
347 			for (j=st->first_glyph; j<st->last_glyph; j++) {
348 				st->span->dx[j] += remain;
349 			}
350 			tr_state->refresh_children_bounds = 1;
351 		}
352 		gf_free(st);
353 	}
354 	gf_list_reset(tr_state->x_anchors);
355 }
356 
svg_text_area_queue_state(GF_TraverseState * tr_state,GF_TextSpan * span,u32 first_glyph,u32 last_glyph)357 static void svg_text_area_queue_state(GF_TraverseState *tr_state, GF_TextSpan *span, u32 first_glyph, u32 last_glyph)
358 {
359 	textArea_state *st;
360 	u32 i, count;
361 	count = gf_list_count(tr_state->x_anchors);
362 	for (i=0; i<count; i++) {
363 		st = (textArea_state*)gf_list_get(tr_state->x_anchors, i);
364 		if (st->span==span) {
365 			st->last_glyph = last_glyph;
366 			return;
367 		}
368 	}
369 	st = (textArea_state*)gf_malloc(sizeof(textArea_state));
370 	st->first_glyph = first_glyph;
371 	st->last_glyph = last_glyph;
372 	st->span = span;
373 	gf_list_add(tr_state->x_anchors, st);
374 
375 }
376 
svg_text_area_apply_diff_baselines(GF_TraverseState * tr_state,Fixed diff)377 static void svg_text_area_apply_diff_baselines(GF_TraverseState *tr_state, Fixed diff)
378 {
379 	u32 i, count, j;
380 	count = gf_list_count(tr_state->x_anchors);
381 	tr_state->refresh_children_bounds++;
382 
383 	for (i=0; i<count; i++) {
384 		textArea_state *st = (textArea_state*)gf_list_get(tr_state->x_anchors, i);
385 
386 		for (j=st->first_glyph; j<st->last_glyph; j++) {
387 			st->span->dy[j] += diff;
388 		}
389 	}
390 }
391 
392 
svg_traverse_dom_text_area(GF_Node * node,SVGAllAttributes * atts,GF_TraverseState * tr_state,GF_List * spans)393 static void svg_traverse_dom_text_area(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state, GF_List *spans)
394 {
395 	GF_DOMText *dom_text = (GF_DOMText *)node;
396 	u32 word_start;
397 	u32 i, j;
398 	Fixed line_spacing;
399 	GF_Font *font;
400 	GF_FontManager *fm;
401 	GF_TextSpan *span;
402 
403 	if (!dom_text->textContent) return;
404 	if (tr_state->svg_props->font_size->value > tr_state->max_height) return;
405 
406 	fm = tr_state->visual->compositor->font_manager;
407 	if (!fm) return;
408 
409 	font = svg_set_font(tr_state, fm);
410 	if (!font) return;
411 
412 	span = svg_get_text_span(fm, font, tr_state->svg_props->font_size->value, GF_TRUE, GF_TRUE, GF_FALSE, atts, dom_text->textContent, atts->xml_lang ? *atts->xml_lang : NULL, tr_state);
413 	if (!span) return;
414 
415 	/*first run of the line, inc text y*/
416 	if (tr_state->line_spacing==0) {
417 		if (tr_state->svg_props->line_increment->type != SVG_NUMBER_AUTO) {
418 			tr_state->line_spacing = tr_state->svg_props->line_increment->value;
419 		} else {
420 			tr_state->line_spacing = gf_mulfix(span->font_size, FLT2FIX(1.0) );
421 		}
422 		tr_state->text_end_y += tr_state->line_spacing;
423 	}
424 
425 	line_spacing = gf_mulfix(span->font_size, FLT2FIX(1.0) );
426 
427 	word_start = 0;
428 	i = 0;
429 	/* boucle principale: mot par mot */
430 	while (i<span->nb_glyphs) {
431 		Fixed word_size, last_char_size, offset, word_descent;
432 		u32 break_glyph = 0;
433 
434 		word_descent = (-span->font->descent) * span->font_scale;
435 		word_start = i;
436 		word_size = last_char_size = 0;
437 		while (i<span->nb_glyphs) {
438 			Fixed glyph_size;
439 			if (span->glyphs[i]) {
440 				/*look for word boundaries*/
441 				if ( (span->glyphs[i]->utf_name==' ') || (span->glyphs[i]->utf_name=='-') ) {
442 					last_char_size = span->glyphs[i]->horiz_advance * span->font_scale;
443 					i++;
444 					break;
445 				}
446 				glyph_size = span->glyphs[i]->horiz_advance * span->font_scale;
447 				if (word_size + glyph_size> tr_state->max_length) {
448 					break_glyph = i;
449 					break;
450 				}
451 				word_size += glyph_size;
452 			} else {
453 				// no glyph;
454 				word_size += font->max_advance_h * span->font_scale;
455 			}
456 			i++;
457 		}
458 
459 		/* word doesn't fit on line, escape*/
460 		if (!word_size && !last_char_size) break;
461 
462 		if (tr_state->text_end_x + word_size > tr_state->max_length) {
463 			/* if the word doesn't fit on line, escape*/
464 			if (word_size > tr_state->max_length) {
465 				word_start=break_glyph;
466 				break;
467 			}
468 			svg_text_area_reset_state(tr_state);
469 			tr_state->text_end_x = 0;
470 			tr_state->line_spacing = line_spacing;
471 
472 			tr_state->text_end_y += (tr_state->svg_props->line_increment->type == SVG_NUMBER_AUTO ? tr_state->line_spacing : tr_state->svg_props->line_increment->value);
473 			/* out of area, abort processing*/
474 			if (tr_state->text_end_y > tr_state->max_height) break;
475 		} else {
476 			/* first word is too high for the area*/
477 			if (tr_state->text_end_y + word_descent > tr_state->max_height)
478 				break;
479 
480 			/* stay on current line*/
481 			if (line_spacing > tr_state->line_spacing) {
482 				svg_text_area_apply_diff_baselines(tr_state, line_spacing - tr_state->line_spacing);
483 				tr_state->text_end_y -= tr_state->line_spacing;
484 				tr_state->text_end_y += line_spacing;
485 				tr_state->line_spacing = line_spacing;
486 			}
487 
488 		}
489 		word_size += last_char_size;
490 
491 
492 		offset = tr_state->base_x + tr_state->text_end_x;
493 		for (j=word_start; j<i; j++) {
494 			span->dx[j] = offset;
495 			span->dy[j] = tr_state->base_y + tr_state->text_end_y;
496 			offset += (span->glyphs[j] ? span->glyphs[j]->horiz_advance : font->max_advance_h) * span->font_scale;
497 		}
498 		tr_state->text_end_x += word_size;
499 //		if (tr_state->y_step < word_height) tr_state->y_step = word_height;
500 
501 		svg_text_area_queue_state(tr_state, span, word_start, i);
502 		word_start = i;
503 	}
504 	span->nb_glyphs = word_start;
505 	/*add span path to list of spans*/
506 	gf_list_add(spans, span);
507 }
508 
get_domtext_width(GF_Node * node,SVGAllAttributes * atts,GF_TraverseState * tr_state)509 static void get_domtext_width(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state)
510 {
511 	u32 i;
512 	GF_Font *font;
513 	Fixed block_width, *entry;
514 	GF_FontManager *fm;
515 	GF_TextSpan *span;
516 	GF_DOMText *dom_text = (GF_DOMText *)node;
517 
518 	if (!dom_text->textContent) return;
519 
520 	fm = tr_state->visual->compositor->font_manager;
521 	if (!fm) return;
522 
523 	font = svg_set_font(tr_state, fm);
524 	if (!font) return;
525 
526 	span = svg_get_text_span(fm, font, tr_state->svg_props->font_size->value, (tr_state->count_x>1), (tr_state->count_y>1), GF_FALSE, atts, dom_text->textContent, atts->xml_lang ? *atts->xml_lang : NULL, tr_state);
527 	if (!span) return;
528 
529 	i=0;
530 	//count_x, _y: number of x- (y-) position of characters to come in the text flow
531 	while ( (i<span->nb_glyphs)
532 	        && ( (tr_state->count_x>1) || (tr_state->count_y>1) )
533 	      ) {
534 		block_width = (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale;
535 
536 		//store width in tr_state->x_anchors
537 		entry = (Fixed*)gf_malloc(sizeof(Fixed));
538 		if (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT) *entry = -block_width;
539 		else *entry = block_width;
540 
541 		gf_list_add(tr_state->x_anchors, entry);
542 
543 		if (tr_state->count_x>0) tr_state->count_x--;
544 		if (tr_state->count_y>0) tr_state->count_y--;
545 		i++;
546 	}
547 
548 	//chars are taken one by one while there are indicated positions, then remaining chars are treated as a block
549 	if (i<span->nb_glyphs) {
550 		block_width = 0;
551 		while (i<span->nb_glyphs) {
552 			block_width += (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale;
553 			i++;
554 		}
555 		//if last indicated position, create a new item
556 		if ((tr_state->count_x==1)||(tr_state->count_y==1)
557 		        || !gf_list_count(tr_state->x_anchors) ) {
558 			entry = (Fixed*)gf_malloc(sizeof(Fixed));
559 
560 			if (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT) *entry = -block_width;
561 			else *entry = block_width;
562 
563 			gf_list_add(tr_state->x_anchors, entry);
564 		} else { // (count_x == 0 && count_y == 0) otherwise increment last one
565 			Fixed *prec_lw = gf_list_last(tr_state->x_anchors);
566 			(*prec_lw) += block_width;
567 		}
568 		//force counters to 0 for next spans/DOM texts
569 		if (tr_state->count_x==1) tr_state->count_x = 0;
570 		if (tr_state->count_y==1) tr_state->count_y = 0;
571 	}
572 	gf_font_manager_delete_span(fm, span);
573 }
574 
get_tspan_width(GF_Node * node,void * rs)575 static void get_tspan_width(GF_Node *node, void *rs)
576 {
577 	SVGPropertiesPointers backup_props;
578 	u32 backup_flags;
579 	GF_TraverseState *tr_state = (GF_TraverseState *)rs;
580 	SVG_Element *tspan = (SVG_Element *)node;
581 	SVGAllAttributes atts;
582 	GF_ChildNodeItem *child;
583 
584 	gf_svg_flatten_attributes(tspan, &atts);
585 	if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags))
586 		return;
587 
588 	child = ((GF_ParentNode *) tspan)->children;
589 	while (child) {
590 		switch  (gf_node_get_tag(child->node)) {
591 		case TAG_DOMText:
592 			get_domtext_width(child->node, &atts, tr_state);
593 			break;
594 		case TAG_SVG_tspan:
595 			get_tspan_width(child->node, tr_state);
596 			break;
597 		default:
598 			break;
599 		}
600 		child=child->next;
601 	}
602 
603 	memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
604 	tr_state->svg_flags = backup_flags;
605 }
606 
svg_traverse_domtext(GF_Node * node,SVGAllAttributes * atts,GF_TraverseState * tr_state,GF_List * spans,GF_Node * anchor_node)607 void svg_traverse_domtext(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state, GF_List *spans, GF_Node *anchor_node)
608 {
609 	GF_DOMText *dom_text = (GF_DOMText *)node;
610 	Fixed x, y;
611 	u32 i;
612 	Fixed x_anchor, *ptr;
613 	GF_Font *font;
614 	Fixed block_width;
615 	GF_FontManager *fm;
616 	GF_TextSpan *span;
617 
618 	if (!dom_text->textContent) return;
619 
620 	if (tr_state->in_svg_text_area) {
621 		svg_traverse_dom_text_area(node, atts, tr_state, spans);
622 		return;
623 	}
624 
625 	fm = tr_state->visual->compositor->font_manager;
626 	if (!fm) return;
627 
628 	font = svg_set_font(tr_state, fm);
629 	if (!font) return;
630 	if (font->not_loaded) {
631 		tr_state->visual->compositor->reset_fonts = GF_TRUE;
632 		tr_state->visual->compositor->skip_flush = 1;
633 		gf_sc_next_frame_state(tr_state->visual->compositor, GF_SC_DRAW_FRAME);
634 		return;
635 	}
636 
637 
638 	span = svg_get_text_span(fm, font, tr_state->svg_props->font_size->value, (tr_state->count_x>1), (tr_state->count_y>1), tr_state->count_rotate, atts, dom_text->textContent, atts->xml_lang ? *atts->xml_lang : NULL, tr_state);
639 	if (!span) return;
640 
641 	i=0;
642 	/*
643 	if character position is given in (x, y) attributes, use it.
644 	Otherwise add text at tr_state->text_end_x.
645 	*/
646 	while ((i<span->nb_glyphs)
647 	        && ( (tr_state->count_x>1) || (tr_state->count_y>1) )
648 	      ) {
649 		//get x and y positions
650 		if (tr_state->count_x==0) {
651 			x = tr_state->text_end_x;
652 		} else {
653 			SVG_Coordinate *xc = (SVG_Coordinate *) gf_list_get(*tr_state->text_x, tr_state->chunk_index);
654 			x = xc->value;
655 			(tr_state->count_x)--;
656 		}
657 		if (tr_state->count_y==0) {
658 			y = tr_state->text_end_y;
659 		} else {
660 			SVG_Coordinate *yc = (SVG_Coordinate *) gf_list_get(*tr_state->text_y, tr_state->chunk_index);
661 			y = yc->value;
662 			(tr_state->count_y)--;
663 		}
664 
665 
666 		/*apply x-anchor*/
667 		ptr = (Fixed *)gf_list_get(tr_state->x_anchors, tr_state->chunk_index);
668 		x_anchor = ptr ? *ptr : 0;
669 		if (span->dx) span->dx[i] = x_anchor + x;
670 		else if (!i) span->off_x = x_anchor + x;
671 		if (span->dy) span->dy[i] = y;
672 		else span->off_y = y;
673 
674 		if (tr_state->count_rotate) {
675 			SVG_Coordinate *rc = (SVG_Coordinate *) gf_list_get(*tr_state->text_rotate, tr_state->idx_rotate);
676 			span->rot[i] = gf_mulfix(GF_PI/180, rc->value);
677 			if (tr_state->idx_rotate+1<tr_state->count_rotate) tr_state->idx_rotate++;
678 		}
679 
680 		/*update last glyph position*/
681 		block_width = (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale;
682 		tr_state->text_end_x = x+block_width;
683 		tr_state->text_end_y = y;
684 		(tr_state->chunk_index)++;
685 		i++;
686 	}
687 
688 	/* no more positions, add remaining glyphs as a block*/
689 	if (i<span->nb_glyphs) {
690 		Fixed offset;
691 		if ((tr_state->count_x==1) && tr_state->text_x) {
692 			SVG_Coordinate *xc = (SVG_Coordinate *) gf_list_get(*tr_state->text_x, tr_state->chunk_index);
693 			tr_state->text_end_x = xc->value;
694 			(tr_state->count_x)--;
695 		}
696 		if ((tr_state->count_y==1) && tr_state->text_y) {
697 			SVG_Coordinate *yc = (SVG_Coordinate *) gf_list_get(*tr_state->text_y, tr_state->chunk_index);
698 			tr_state->text_end_y = yc->value;
699 			(tr_state->count_y)--;
700 		}
701 
702 		x = tr_state->text_end_x;
703 		y = tr_state->text_end_y;
704 
705 		/*apply x anchor*/
706 		ptr = (Fixed *)gf_list_get(tr_state->x_anchors, tr_state->chunk_index);
707 		x_anchor = ptr ? *ptr : 0;
708 
709 		offset = x_anchor + x - (span->dx ? span->dx[i] : span->off_x);
710 
711 		if (!span->dx && (tr_state->text_x || x_anchor)) span->off_x = x_anchor + x;
712 		if (!span->dy && tr_state->text_y) span->off_y = y;
713 
714 		block_width = 0;
715 		while (i<span->nb_glyphs) {
716 
717 			if (span->rot) {
718 				SVG_Coordinate *rc = (SVG_Coordinate *) gf_list_get(*tr_state->text_rotate, tr_state->idx_rotate);
719 				span->rot[i] = gf_mulfix(GF_PI/180, rc->value);
720 				if (tr_state->idx_rotate+1<tr_state->count_rotate) tr_state->idx_rotate++;
721 			}
722 			if (span->dx) span->dx[i] = offset + block_width;
723 			if (span->dy) span->dy[i] = y;
724 			block_width += (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale;
725 
726 			i++;
727 		}
728 		tr_state->text_end_x += block_width;
729 	}
730 
731 	/*add span path to list of spans*/
732 	gf_list_add(spans, span);
733 	span->anchor = anchor_node;
734 }
735 
736 
svg_compute_text_width(GF_Node * node,SVGAllAttributes * atts,GF_TraverseState * tr_state)737 static void svg_compute_text_width(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state )
738 {
739 	GF_ChildNodeItem *child;
740 	Bool is_switch = GF_FALSE;
741 	/*compute length of all text blocks*/
742 	switch  (gf_node_get_tag(node)) {
743 	case TAG_DOMText:
744 		get_domtext_width(node, atts, tr_state);
745 		break;
746 	case TAG_SVG_tspan:
747 		get_tspan_width(node, tr_state);
748 		break;
749 	case TAG_SVG_switch:
750 		is_switch = GF_TRUE;
751 	case TAG_SVG_a:
752 		child = ((GF_ParentNode *)node)->children;
753 		while (child) {
754 			if (is_switch) {
755 				SVGAllAttributes a_atts;
756 				gf_svg_flatten_attributes((SVG_Element*)child->node, &a_atts);
757 				if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &a_atts)) {
758 					svg_compute_text_width(child->node, atts, tr_state);
759 					break;
760 				}
761 			} else {
762 				svg_compute_text_width(child->node, atts, tr_state);
763 			}
764 			child = child->next;
765 		}
766 		break;
767 	default:
768 		break;
769 	}
770 }
771 
svg_traverse_text_block(GF_Node * node,SVGAllAttributes * atts,GF_TraverseState * tr_state,GF_List * spans)772 static void svg_traverse_text_block(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state, GF_List *spans)
773 {
774 	GF_ChildNodeItem *child;
775 	Bool is_switch = GF_FALSE;
776 	switch  (gf_node_get_tag(node)) {
777 	case TAG_DOMText:
778 		svg_traverse_domtext(node, atts, tr_state, spans, NULL);
779 		break;
780 	case TAG_SVG_tspan:
781 		/*mark tspan as dirty to force rebuild*/
782 		gf_node_dirty_set(node, 0, GF_FALSE);
783 		gf_node_traverse(node, tr_state);
784 		break;
785 	case TAG_SVG_switch:
786 		is_switch = GF_TRUE;
787 	case TAG_SVG_a:
788 		child = ((GF_ParentNode *)node)->children;
789 		while (child) {
790 			if (is_switch) {
791 				SVGAllAttributes a_atts;
792 				gf_svg_flatten_attributes((SVG_Element*)child->node, &a_atts);
793 				if (compositor_svg_evaluate_conditional(tr_state->visual->compositor, &a_atts)) {
794 					svg_traverse_text_block(child->node, atts, tr_state, spans);
795 					break;
796 				}
797 			} else if (gf_node_get_tag(child->node)==TAG_DOMText) {
798 				svg_traverse_domtext(child->node, atts, tr_state, spans, node);
799 			}
800 			child = child->next;
801 		}
802 		break;
803 	default:
804 		break;
805 	}
806 }
807 
svg_text_draw_2d(SVG_TextStack * st,GF_TraverseState * tr_state)808 static void svg_text_draw_2d(SVG_TextStack *st, GF_TraverseState *tr_state)
809 {
810 	gf_font_spans_draw_2d(st->spans, tr_state, 0, GF_FALSE, &st->bounds);
811 }
812 
813 
svg_text_area_shift_bounds(SVG_TextStack * st,GF_TraverseState * tr_state)814 static void svg_text_area_shift_bounds(SVG_TextStack *st, GF_TraverseState *tr_state)
815 {
816 	u32 i=0;
817 	GF_TextSpan *span;
818 	/*finally compute text bounds*/
819 	st->bounds.width = st->bounds.height = 0;
820 	st->bounds.x = st->bounds.y = 0;
821 	while ( (span = (GF_TextSpan*)gf_list_enum(st->spans, &i)) ) {
822 		u32 j;
823 		for (j=0; j<span->nb_glyphs; j++)
824 			span->dy[j] += tr_state->base_shift;
825 
826 		gf_font_manager_refresh_span_bounds(span);
827 		gf_rect_union(&st->bounds, &span->bounds);
828 	}
829 }
830 
831 
svg_traverse_text(GF_Node * node,void * rs,Bool is_destroy)832 static void svg_traverse_text(GF_Node *node, void *rs, Bool is_destroy)
833 {
834 	SVGPropertiesPointers backup_props;
835 	u32 backup_flags;
836 	GF_Matrix2D backup_matrix;
837 	GF_Matrix mx3d;
838 	GF_ChildNodeItem *child;
839 	DrawableContext *ctx;
840 	SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node);
841 	GF_TraverseState *tr_state = (GF_TraverseState *)rs;
842 	SVG_Element *text = (SVG_Element *)node;
843 	SVGAllAttributes atts;
844 	u32 i,imax;
845 
846 	if (is_destroy) {
847 		drawable_del(st->drawable);
848 		svg_reset_text_stack(st);
849 		gf_list_del(st->spans);
850 		gf_free(st);
851 		return;
852 	}
853 
854 	if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) {
855 		svg_text_draw_2d(st, tr_state);
856 		return;
857 	}
858 	else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) {
859 		tr_state->text_parent = node;
860 		gf_font_spans_get_selection(node, st->spans, tr_state);
861 		/*and browse children*/
862 		child = ((GF_ParentNode *) text)->children;
863 		while (child) {
864 			switch  (gf_node_get_tag(child->node)) {
865 			case TAG_SVG_tspan:
866 				gf_node_traverse(child->node, tr_state);
867 				break;
868 			}
869 			child = child->next;
870 		}
871 		tr_state->text_parent = NULL;
872 		return;
873 	}
874 
875 	gf_svg_flatten_attributes(text, &atts);
876 	if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags))
877 		return;
878 
879 	tr_state->in_svg_text++;
880 	tr_state->text_parent = node;
881 
882 	if (tr_state->traversing_mode==TRAVERSE_PICK) {
883 		compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d);
884 		if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE)
885 			gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, 1, st->drawable);
886 
887 		/*and browse children*/
888 		child = ((GF_ParentNode *) text)->children;
889 		while (child) {
890 			switch  (gf_node_get_tag(child->node)) {
891 			case TAG_SVG_tspan:
892 				gf_node_traverse(child->node, tr_state);
893 				break;
894 			}
895 			child = child->next;
896 		}
897 		memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
898 		compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d);
899 		tr_state->svg_flags = backup_flags;
900 		tr_state->text_parent = NULL;
901 		tr_state->in_svg_text--;
902 		return;
903 	}
904 	else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) {
905 		gf_font_spans_get_selection(node, st->spans, tr_state);
906 		memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
907 		tr_state->svg_flags = backup_flags;
908 		tr_state->text_parent = NULL;
909 		tr_state->in_svg_text--;
910 		return;
911 	}
912 
913 	compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d);
914 
915 	if ( (st->prev_size != tr_state->svg_props->font_size->value) ||
916 	        (st->prev_flags != *tr_state->svg_props->font_style) ||
917 	        (st->prev_anchor != *tr_state->svg_props->text_anchor) ||
918 	        (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) )
919 	        || tr_state->visual->compositor->reset_fonts
920 	   ) {
921 		u32 mode;
922 		child = ((GF_ParentNode *) text)->children;
923 
924 		svg_reset_text_stack(st);
925 		tr_state->text_end_x = 0;
926 		tr_state->text_end_y = 0;
927 		/*init the xml:space algo*/
928 		tr_state->last_char_type = 0;
929 
930 		/*initialize x and y counters - stored at the traverse level for handling tspan & co*/
931 		if (atts.text_x) tr_state->count_x = gf_list_count(*atts.text_x);
932 		else tr_state->count_x=0;
933 		if (atts.text_y) tr_state->count_y = gf_list_count(*atts.text_y);
934 		else tr_state->count_y=0;
935 		if (atts.text_rotate) tr_state->count_rotate = gf_list_count(*atts.text_rotate);
936 		else tr_state->count_rotate=0;
937 
938 		/*horizontal justifiers container*/
939 		tr_state->x_anchors = gf_list_new();
940 
941 		/*compute length of all text blocks*/
942 		while (child) {
943 			svg_compute_text_width(child->node, &atts, tr_state);
944 			child=child->next;
945 		}
946 
947 		/*apply justification of all blocks*/
948 		imax=gf_list_count(tr_state->x_anchors);
949 		for (i=0; i<imax; i++) {
950 			Fixed *lw = gf_list_get(tr_state->x_anchors, i);
951 			svg_apply_text_anchor(tr_state, lw);
952 		}
953 
954 		/*re-initialize x and y counters for final compute*/
955 		if (atts.text_x) tr_state->count_x = gf_list_count(*atts.text_x);
956 		else tr_state->count_x=0;
957 		if (atts.text_y) tr_state->count_y = gf_list_count(*atts.text_y);
958 		else tr_state->count_y=0;
959 		if (atts.text_rotate) tr_state->count_rotate = gf_list_count(*atts.text_rotate);
960 		else tr_state->count_rotate=0;
961 		tr_state->idx_rotate = 0;
962 		tr_state->chunk_index = 0;
963 
964 		/*initialize current text position*/
965 		if (!tr_state->text_end_x) {
966 			SVG_Coordinate *xc = (atts.text_x ? (SVG_Coordinate *) gf_list_get(*atts.text_x, 0) : NULL);
967 			tr_state->text_end_x = (xc ? xc->value : 0);
968 		}
969 		if (!tr_state->text_end_y) {
970 			SVG_Coordinate *yc = (atts.text_y ? (SVG_Coordinate *) gf_list_get(*atts.text_y, 0) : NULL);
971 			tr_state->text_end_y = (yc ? yc->value : 0);
972 		}
973 
974 		/*pass x and y to children*/
975 		tr_state->text_x = atts.text_x;
976 		tr_state->text_y = atts.text_y;
977 		tr_state->text_rotate = atts.text_rotate;
978 
979 		drawable_reset_path(st->drawable);
980 
981 		/*switch to bounds mode, and recompute children*/
982 		mode = tr_state->traversing_mode;
983 		tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
984 		tr_state->last_char_type = 0;
985 
986 		child = ((GF_ParentNode *) text)->children;
987 		while (child) {
988 			svg_traverse_text_block(child->node, &atts, tr_state, st->spans);
989 			child = child->next;
990 		}
991 		tr_state->traversing_mode = mode;
992 		gf_node_dirty_clear(node, 0);
993 		drawable_mark_modified(st->drawable, tr_state);
994 		st->prev_size = tr_state->svg_props->font_size->value;
995 		st->prev_flags = *tr_state->svg_props->font_style;
996 		st->prev_anchor = *tr_state->svg_props->text_anchor;
997 
998 		while (gf_list_count(tr_state->x_anchors)) {
999 			Fixed *f = gf_list_last(tr_state->x_anchors);
1000 			gf_list_rem_last(tr_state->x_anchors);
1001 			gf_free(f);
1002 		}
1003 		gf_list_del(tr_state->x_anchors);
1004 		tr_state->x_anchors = NULL;
1005 
1006 		svg_update_bounds(st);
1007 	}
1008 
1009 	if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
1010 		if (!compositor_svg_is_display_off(tr_state->svg_props))
1011 			tr_state->bounds = st->bounds;
1012 
1013 	} else if ((tr_state->traversing_mode == TRAVERSE_SORT)
1014 	           && !compositor_svg_is_display_off(tr_state->svg_props)
1015 	           && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN)
1016 	          ) {
1017 		ctx = drawable_init_context_svg(st->drawable, tr_state);
1018 		if (ctx) svg_finalize_sort(ctx, st, tr_state);
1019 
1020 		/*and browse children*/
1021 		child = ((GF_ParentNode *) text)->children;
1022 		while (child) {
1023 			switch  (gf_node_get_tag(child->node)) {
1024 			case TAG_SVG_tspan:
1025 				gf_node_traverse(child->node, tr_state);
1026 				break;
1027 			case TAG_SVG_switch:
1028 				gf_node_traverse(child->node, tr_state);
1029 				break;
1030 			}
1031 			child = child->next;
1032 		}
1033 	}
1034 	tr_state->in_svg_text--;
1035 	tr_state->text_parent = NULL;
1036 
1037 	compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d);
1038 	memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
1039 	tr_state->svg_flags = backup_flags;
1040 }
1041 
1042 
compositor_init_svg_text(GF_Compositor * compositor,GF_Node * node)1043 void compositor_init_svg_text(GF_Compositor *compositor, GF_Node *node)
1044 {
1045 	SVG_TextStack *stack;
1046 	GF_SAFEALLOC(stack, SVG_TextStack);
1047 	if (!stack) {
1048 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg text stack\n"));
1049 		return;
1050 	}
1051 	stack->drawable = drawable_new();
1052 	stack->drawable->node = node;
1053 	stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW;
1054 	stack->spans = gf_list_new();
1055 	gf_node_set_private(node, stack);
1056 	gf_node_set_callback_function(node, svg_traverse_text);
1057 }
1058 
1059 
svg_traverse_tspan(GF_Node * node,void * rs,Bool is_destroy)1060 static void svg_traverse_tspan(GF_Node *node, void *rs, Bool is_destroy)
1061 {
1062 	SVGPropertiesPointers backup_props;
1063 	u32 backup_flags;
1064 	GF_Matrix2D backup_matrix;
1065 	GF_Matrix mx3d;
1066 	DrawableContext *ctx;
1067 	SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node);
1068 	GF_TraverseState *tr_state = (GF_TraverseState *)rs;
1069 	SVG_Element *tspan = (SVG_Element *)node;
1070 	SVGAllAttributes atts;
1071 	GF_ChildNodeItem *child;
1072 
1073 	if (is_destroy) {
1074 		drawable_del(st->drawable);
1075 		svg_reset_text_stack(st);
1076 		gf_list_del(st->spans);
1077 		gf_free(st);
1078 		return;
1079 	}
1080 	if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) {
1081 		svg_text_draw_2d(st, tr_state);
1082 		return;
1083 	}
1084 	else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) {
1085 		gf_font_spans_get_selection(node, st->spans, tr_state);
1086 		/*and browse children*/
1087 		child = ((GF_ParentNode *) tspan)->children;
1088 		while (child) {
1089 			switch  (gf_node_get_tag(child->node)) {
1090 			case TAG_SVG_tspan:
1091 				gf_node_traverse(child->node, tr_state);
1092 				break;
1093 			}
1094 			child = child->next;
1095 		}
1096 		return;
1097 	}
1098 
1099 	if (!tr_state->in_svg_text && !tr_state->in_svg_text_area) return;
1100 
1101 	gf_svg_flatten_attributes(tspan, &atts);
1102 	if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags))
1103 		return;
1104 
1105 	if (tr_state->traversing_mode==TRAVERSE_PICK) {
1106 		if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE)
1107 			gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, GF_TRUE, st->drawable);
1108 
1109 		/*and browse children*/
1110 		child = ((GF_ParentNode *) tspan)->children;
1111 		while (child) {
1112 			switch  (gf_node_get_tag(child->node)) {
1113 			case TAG_SVG_tspan:
1114 				gf_node_traverse(child->node, tr_state);
1115 				break;
1116 			}
1117 			child = child->next;
1118 		}
1119 		memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
1120 		tr_state->svg_flags = backup_flags;
1121 		return;
1122 	}
1123 
1124 	compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d);
1125 
1126 	if ( (st->prev_size != tr_state->svg_props->font_size->value) ||
1127 	        (st->prev_flags != *tr_state->svg_props->font_style) ||
1128 	        (st->prev_anchor != *tr_state->svg_props->text_anchor) ||
1129 	        (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) )
1130 	   ) {
1131 		u32 mode;
1132 
1133 		/*tspan has been modified in the SORT stage, which means that an anim local to tspan has modified the node.
1134 		The result of the parent (text, textArea) will thus be wrong if we try to update the tspan. We therefore
1135 		keep the previous computed drawable, and invalidate the parent for next frame*/
1136 		if (tr_state->traversing_mode==TRAVERSE_SORT) {
1137 			gf_node_dirty_set(node, 0, GF_TRUE);
1138 			goto skip_changes;
1139 		}
1140 
1141 		/*switch to bounds mode, and recompute children*/
1142 		mode = tr_state->traversing_mode;
1143 		tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
1144 
1145 		svg_reset_text_stack(st);
1146 		child = ((GF_ParentNode *) tspan)->children;
1147 
1148 		while (child) {
1149 			switch  (gf_node_get_tag(child->node)) {
1150 			case TAG_DOMText:
1151 				svg_traverse_domtext(child->node, &atts, tr_state, st->spans, NULL);
1152 				break;
1153 			case TAG_SVG_tspan:
1154 				gf_node_dirty_set(child->node, 0, GF_FALSE);
1155 				gf_node_traverse(child->node, tr_state);
1156 				break;
1157 			case TAG_SVG_switch:
1158 			case TAG_SVG_a:
1159 			case TAG_SVG_tbreak:
1160 				gf_node_traverse(child->node, tr_state);
1161 				break;
1162 			default:
1163 				break;
1164 			}
1165 			child = child->next;
1166 		}
1167 		tr_state->traversing_mode = mode;
1168 		gf_node_dirty_clear(node, 0);
1169 		drawable_mark_modified(st->drawable, tr_state);
1170 		st->prev_size = tr_state->svg_props->font_size->value;
1171 		st->prev_flags = *tr_state->svg_props->font_style;
1172 		st->prev_anchor = *tr_state->svg_props->text_anchor;
1173 
1174 		svg_update_bounds(st);
1175 	}
1176 skip_changes:
1177 
1178 	if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
1179 		if (tr_state->refresh_children_bounds) {
1180 			if (tr_state->base_shift)
1181 				svg_text_area_shift_bounds(st, tr_state);
1182 			else
1183 				svg_update_bounds(st);
1184 			child = ((GF_ParentNode *) tspan)->children;
1185 			while (child) {
1186 				switch  (gf_node_get_tag(child->node)) {
1187 				case TAG_SVG_tspan:
1188 				case TAG_SVG_switch:
1189 				case TAG_SVG_a:
1190 					gf_node_traverse(child->node, tr_state);
1191 					break;
1192 				default:
1193 					break;
1194 				}
1195 				child = child->next;
1196 			}
1197 		}
1198 		if (!compositor_svg_is_display_off(tr_state->svg_props))
1199 			tr_state->bounds = st->bounds;
1200 
1201 	}
1202 	else if (
1203 	    (tr_state->traversing_mode == TRAVERSE_SORT)
1204 	    && !compositor_svg_is_display_off(tr_state->svg_props)
1205 	    && ( *(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN)
1206 	) {
1207 		child = ((GF_ParentNode *) tspan)->children;
1208 
1209 		ctx = drawable_init_context_svg(st->drawable, tr_state);
1210 		if (ctx) svg_finalize_sort(ctx, st, tr_state);
1211 
1212 		while (child) {
1213 			switch  (gf_node_get_tag(child->node)) {
1214 			case TAG_SVG_tspan:
1215 			case TAG_SVG_switch:
1216 			case TAG_SVG_a:
1217 				gf_node_traverse(child->node, tr_state);
1218 				break;
1219 			default:
1220 				break;
1221 			}
1222 			child = child->next;
1223 		}
1224 	}
1225 
1226 	compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d);
1227 	memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
1228 	tr_state->svg_flags = backup_flags;
1229 }
1230 
compositor_init_svg_tspan(GF_Compositor * compositor,GF_Node * node)1231 void compositor_init_svg_tspan(GF_Compositor *compositor, GF_Node *node)
1232 {
1233 	SVG_TextStack *stack;
1234 	GF_SAFEALLOC(stack, SVG_TextStack);
1235 	if (!stack) {
1236 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg tspan stack\n"));
1237 		return;
1238 	}
1239 	stack->drawable = drawable_new();
1240 	stack->drawable->node = node;
1241 	stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW;
1242 	stack->spans = gf_list_new();
1243 	gf_node_set_private(node, stack);
1244 	gf_node_set_callback_function(node, svg_traverse_tspan);
1245 }
1246 
1247 
svg_traverse_textArea(GF_Node * node,void * rs,Bool is_destroy)1248 static void svg_traverse_textArea(GF_Node *node, void *rs, Bool is_destroy)
1249 {
1250 	SVGPropertiesPointers backup_props;
1251 	u32 backup_flags;
1252 	GF_Matrix mx3d;
1253 	GF_Matrix2D backup_matrix;
1254 	DrawableContext *ctx = NULL;
1255 	GF_ChildNodeItem *child;
1256 	SVG_TextStack *st = (SVG_TextStack *)gf_node_get_private(node);
1257 	GF_TraverseState *tr_state = (GF_TraverseState *)rs;
1258 	SVG_Element *text = (SVG_Element *)node;
1259 	SVGAllAttributes atts;
1260 
1261 	if (is_destroy) {
1262 		drawable_del(st->drawable);
1263 		svg_reset_text_stack(st);
1264 		gf_list_del(st->spans);
1265 		gf_free(st);
1266 		return;
1267 	}
1268 
1269 	if (tr_state->traversing_mode==TRAVERSE_DRAW_2D) {
1270 		svg_text_draw_2d(st, tr_state);
1271 		return;
1272 	}
1273 	else if (tr_state->traversing_mode==TRAVERSE_GET_TEXT) {
1274 		tr_state->text_parent = node;
1275 		gf_font_spans_get_selection(node, st->spans, tr_state);
1276 		/*and browse children*/
1277 		child = ((GF_ParentNode *) text)->children;
1278 		while (child) {
1279 			switch  (gf_node_get_tag(child->node)) {
1280 			case TAG_SVG_tspan:
1281 			case TAG_SVG_a:
1282 				gf_node_traverse(child->node, tr_state);
1283 				break;
1284 			}
1285 			child = child->next;
1286 		}
1287 		tr_state->text_parent = NULL;
1288 		return;
1289 	}
1290 
1291 
1292 	gf_svg_flatten_attributes(text, &atts);
1293 	if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags))
1294 		return;
1295 
1296 	tr_state->text_parent = node;
1297 	tr_state->in_svg_text_area++;
1298 
1299 	if (tr_state->traversing_mode==TRAVERSE_PICK) {
1300 		if (*tr_state->svg_props->pointer_events!=SVG_POINTEREVENTS_NONE) {
1301 			compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d);
1302 			gf_font_spans_pick(node, st->spans, tr_state, &st->bounds, GF_TRUE, st->drawable);
1303 
1304 			/*and browse children*/
1305 			child = ((GF_ParentNode *) node)->children;
1306 			while (child) {
1307 				gf_node_traverse(child->node, tr_state);
1308 				child = child->next;
1309 			}
1310 			compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d);
1311 			memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
1312 			tr_state->svg_flags = backup_flags;
1313 		}
1314 		tr_state->in_svg_text_area--;
1315 		tr_state->text_parent = NULL;
1316 		return;
1317 	}
1318 
1319 	compositor_svg_apply_local_transformation(tr_state, &atts, &backup_matrix, &mx3d);
1320 
1321 	if ( (st->prev_size != tr_state->svg_props->font_size->value) ||
1322 	        (st->prev_flags != *tr_state->svg_props->font_style) ||
1323 	        (st->prev_anchor != *tr_state->svg_props->text_anchor) ||
1324 	        (gf_node_dirty_get(node) & (GF_SG_SVG_GEOMETRY_DIRTY | GF_SG_CHILD_DIRTY) )
1325 	        || tr_state->visual->compositor->reset_fonts
1326 	   ) {
1327 		u32 mode;
1328 
1329 		svg_reset_text_stack(st);
1330 		gf_node_dirty_clear(node, 0);
1331 		drawable_mark_modified(st->drawable, tr_state);
1332 		drawable_reset_path(st->drawable);
1333 
1334 		tr_state->max_length = (atts.width ? (atts.width->type == SVG_NUMBER_AUTO ? FIX_MAX : atts.width->value) : FIX_MAX);
1335 		tr_state->max_height = (atts.height ? (atts.height->type == SVG_NUMBER_AUTO ? FIX_MAX : atts.height->value) : FIX_MAX);
1336 		tr_state->base_x = (atts.x ? atts.x->value : 0);
1337 		tr_state->base_y = (atts.y ? atts.y->value : 0);
1338 		/*init the xml:space algo*/
1339 		tr_state->last_char_type = 0;
1340 		/*let it initialize from first font*/
1341 		tr_state->line_spacing = 0;
1342 		tr_state->text_end_x = 0;
1343 		tr_state->text_end_y = (tr_state->svg_props->line_increment->type == SVG_NUMBER_AUTO ? 0 : tr_state->svg_props->line_increment->value);
1344 
1345 		if (tr_state->svg_props->font_size && (tr_state->svg_props->font_size->value <= tr_state->max_height)) {
1346 			Fixed remain;
1347 			u32 c, refresh_to_idx, prev_refresh;
1348 			tr_state->x_anchors = gf_list_new();
1349 
1350 			/*switch to bounds mode, and recompute children*/
1351 			mode = tr_state->traversing_mode;
1352 			tr_state->traversing_mode = TRAVERSE_GET_BOUNDS;
1353 
1354 			prev_refresh = tr_state->refresh_children_bounds;
1355 			tr_state->refresh_children_bounds = 0;
1356 			c = refresh_to_idx = 0;
1357 			child = ((GF_ParentNode *) text)->children;
1358 			while (child) {
1359 				c++;
1360 				switch  (gf_node_get_tag(child->node)) {
1361 				case TAG_DOMText:
1362 					svg_traverse_dom_text_area(child->node, &atts, tr_state, st->spans);
1363 					break;
1364 				case TAG_SVG_tspan:
1365 					/*mark tspan as dirty to force rebuild*/
1366 					gf_node_dirty_set(child->node, 0, GF_FALSE);
1367 					gf_node_traverse(child->node, tr_state);
1368 					break;
1369 				case TAG_SVG_switch:
1370 				case TAG_SVG_a:
1371 				case TAG_SVG_tbreak:
1372 					gf_node_traverse(child->node, tr_state);
1373 					break;
1374 				default:
1375 					break;
1376 				}
1377 				if (tr_state->refresh_children_bounds) {
1378 					tr_state->refresh_children_bounds=0;
1379 					refresh_to_idx=c;
1380 				}
1381 
1382 				child=child->next;
1383 			}
1384 			st->prev_size = tr_state->svg_props->font_size->value;
1385 			st->prev_flags = *tr_state->svg_props->font_style;
1386 			st->prev_anchor = *tr_state->svg_props->text_anchor;
1387 
1388 			svg_text_area_reset_state(tr_state);
1389 			gf_list_del(tr_state->x_anchors);
1390 			tr_state->x_anchors = NULL;
1391 
1392 			if (tr_state->refresh_children_bounds) {
1393 				refresh_to_idx = (u32) -1;
1394 				tr_state->base_shift = 0;
1395 			}
1396 			if (tr_state->svg_props->display_align) {
1397 				switch (*tr_state->svg_props->display_align) {
1398 				case SVG_DISPLAYALIGN_CENTER:
1399 					remain = (tr_state->max_height-tr_state->text_end_y) / 2;
1400 					break;
1401 				case SVG_DISPLAYALIGN_AFTER:
1402 					remain = tr_state->max_height - tr_state->text_end_y;
1403 					break;
1404 				default:
1405 					remain = 0;
1406 					break;
1407 				}
1408 				if (remain<0) remain=0;
1409 				if (remain) {
1410 					refresh_to_idx = (u32) -1;
1411 					tr_state->base_shift = remain;
1412 					svg_text_area_shift_bounds(st, tr_state);
1413 				}
1414 			}
1415 
1416 			if (refresh_to_idx) {
1417 				tr_state->refresh_children_bounds=1;
1418 				/*and retraverse in case of bounds adjustements*/
1419 				child = ((GF_ParentNode *) text)->children;
1420 				while (child) {
1421 					switch (gf_node_get_tag(child->node)) {
1422 					case TAG_DOMText:
1423 						break;
1424 					case TAG_SVG_tspan:
1425 					case TAG_SVG_switch:
1426 					case TAG_SVG_a:
1427 						gf_node_traverse(child->node, tr_state);
1428 						break;
1429 					default:
1430 						break;
1431 					}
1432 					child=child->next;
1433 					refresh_to_idx--;
1434 					if (!refresh_to_idx) break;
1435 				}
1436 				tr_state->base_shift = 0;
1437 			}
1438 			tr_state->traversing_mode = mode;
1439 			tr_state->refresh_children_bounds = prev_refresh;
1440 		}
1441 		svg_update_bounds(st);
1442 	}
1443 
1444 	if (tr_state->traversing_mode == TRAVERSE_GET_BOUNDS) {
1445 		if (!compositor_svg_is_display_off(tr_state->svg_props))
1446 			tr_state->bounds = st->bounds;
1447 	} else if ( (tr_state->traversing_mode == TRAVERSE_SORT)
1448 	            && !compositor_svg_is_display_off(tr_state->svg_props)
1449 	            && (*(tr_state->svg_props->visibility) != SVG_VISIBILITY_HIDDEN)
1450 	          ) {
1451 
1452 		ctx = drawable_init_context_svg(st->drawable, tr_state);
1453 		if (ctx) svg_finalize_sort(ctx, st, tr_state);
1454 
1455 		child = ((GF_ParentNode *) text)->children;
1456 		while (child) {
1457 			switch  (gf_node_get_tag(child->node)) {
1458 			case TAG_DOMText:
1459 				break;
1460 			case TAG_SVG_tspan:
1461 			case TAG_SVG_switch:
1462 			case TAG_SVG_a:
1463 				gf_node_traverse(child->node, tr_state);
1464 				break;
1465 			default:
1466 				break;
1467 			}
1468 			child = child->next;
1469 		}
1470 	}
1471 	tr_state->in_svg_text_area--;
1472 	tr_state->text_parent = NULL;
1473 
1474 
1475 	compositor_svg_restore_parent_transformation(tr_state, &backup_matrix, &mx3d);
1476 	memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
1477 	tr_state->svg_flags = backup_flags;
1478 }
1479 
compositor_init_svg_textarea(GF_Compositor * compositor,GF_Node * node)1480 void compositor_init_svg_textarea(GF_Compositor *compositor, GF_Node *node)
1481 {
1482 	SVG_TextStack *stack;
1483 	GF_SAFEALLOC(stack, SVG_TextStack);
1484 	if (!stack) {
1485 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate svg textarea stack\n"));
1486 		return;
1487 	}
1488 	stack->drawable = drawable_new();
1489 	stack->drawable->node = node;
1490 	stack->drawable->flags = DRAWABLE_USE_TRAVERSE_DRAW;
1491 	stack->spans = gf_list_new();
1492 	gf_node_set_private(node, stack);
1493 	gf_node_set_callback_function(node, svg_traverse_textArea);
1494 }
1495 
svg_traverse_tbreak(GF_Node * node,void * rs,Bool is_destroy)1496 static void svg_traverse_tbreak(GF_Node *node, void *rs, Bool is_destroy)
1497 {
1498 	SVGPropertiesPointers backup_props;
1499 	u32 backup_flags;
1500 	GF_TraverseState *tr_state = (GF_TraverseState *)rs;
1501 	SVGAllAttributes atts;
1502 
1503 	if (is_destroy) return;
1504 	if (tr_state->traversing_mode!=TRAVERSE_GET_BOUNDS) return;
1505 
1506 	gf_svg_flatten_attributes((SVG_Element*)node, &atts);
1507 	if (!compositor_svg_traverse_base(node, &atts, tr_state, &backup_props, &backup_flags))
1508 		return;
1509 
1510 	svg_text_area_reset_state(tr_state);
1511 	/*beginning of a line, force a break of current fontSize*/
1512 	if (!tr_state->text_end_x) {
1513 		if (tr_state->svg_props->line_increment->type != SVG_NUMBER_AUTO) {
1514 			tr_state->text_end_y += tr_state->svg_props->line_increment->value;
1515 		} else {
1516 			tr_state->text_end_y += tr_state->svg_props->font_size->value;
1517 		}
1518 	}
1519 	tr_state->line_spacing = 0;
1520 	tr_state->text_end_x = 0;
1521 	tr_state->last_char_type = 0;
1522 
1523 	memcpy(tr_state->svg_props, &backup_props, sizeof(SVGPropertiesPointers));
1524 	tr_state->svg_flags = backup_flags;
1525 }
1526 
compositor_init_svg_tbreak(GF_Compositor * compositor,GF_Node * node)1527 void compositor_init_svg_tbreak(GF_Compositor *compositor, GF_Node *node)
1528 {
1529 	gf_node_set_callback_function(node, svg_traverse_tbreak);
1530 }
1531 
1532 #endif /*GPAC_DISABLE_SVG*/
1533