LCOV - code coverage report
Current view: top level - compositor - svg_text.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 596 815 73.1 %
Date: 2021-04-29 23:48:07 Functions: 24 26 92.3 %

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

Generated by: LCOV version 1.13