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