static void svg_reset_text_stack(SVG_TextStack *st) { while (gf_list_count(st->spans)) { GF_TextSpan *span = (GF_TextSpan*)gf_list_get(st->spans, 0); gf_list_rem(st->spans, 0); gf_font_manager_delete_span(NULL, span); } }
void text_clean_paths(GF_Compositor *compositor, TextStack *stack) { GF_TextSpan *span; /*delete all path objects*/ while (gf_list_count(stack->spans)) { span = (GF_TextSpan*) gf_list_get(stack->spans, 0); gf_list_rem(stack->spans, 0); gf_font_manager_delete_span(compositor->font_manager, span); } stack->bounds.width = stack->bounds.height = 0; drawable_reset_path(stack->graph); }
static void build_text_split(TextStack *st, M_Text *txt, GF_TraverseState *tr_state) { u32 i, j, k, len, styles, idx, first_char; Bool split_words = 0; GF_Font *font; GF_TextSpan *tspan; GF_FontManager *ft_mgr = tr_state->visual->compositor->font_manager; Fixed fontSize, start_y; M_FontStyle *fs = (M_FontStyle *)txt->fontStyle; fontSize = FSSIZE; if (fontSize <= 0) { fontSize = INT2FIX(12); if (!tr_state->pixel_metrics) fontSize = gf_divfix(fontSize, tr_state->visual->compositor->output_width); } styles = 0; if (fs && fs->style.buffer) { if (strstr(fs->style.buffer, "BOLD") || strstr(fs->style.buffer, "bold")) styles |= GF_FONT_WEIGHT_BOLD; if (strstr(fs->style.buffer, "ITALIC") || strstr(fs->style.buffer, "italic")) styles |= GF_FONT_ITALIC; if (strstr(fs->style.buffer, "UNDERLINED") || strstr(fs->style.buffer, "underlined")) styles |= GF_FONT_UNDERLINED; } font = gf_font_manager_set_font(ft_mgr, fs ? fs->family.vals : NULL, fs ? fs->family.count : 0, styles); if (!font) return; st->ascent = (fontSize*font->ascent) / font->em_size; st->descent = -(fontSize*font->descent) / font->em_size; if (!strcmp(FSMINOR, "MIDDLE")) { start_y = (st->descent + st->ascent)/2; } else if (!strcmp(FSMINOR, "BEGIN")) { start_y = st->descent; } else if (!strcmp(FSMINOR, "END")) { start_y = st->descent + st->ascent; } else { start_y = st->ascent; } st->bounds.width = st->bounds.x = st->bounds.height = 0; idx = 0; split_words = (tr_state->text_split_mode==1) ? 1 : 0; for (i=0; i < txt->string.count; i++) { char *str = txt->string.vals[i]; if (!str || !strlen(str)) continue; tspan = gf_font_manager_create_span(ft_mgr, font, str, fontSize, 0, 0, 0, NULL, 0, styles, (GF_Node*)txt); if (!tspan) continue; len = tspan->nb_glyphs; tspan->flags |= GF_TEXT_SPAN_HORIZONTAL; first_char = 0; for (j=0; j<len; j++) { u32 is_space = 0; GF_TextSpan *span; if (!tspan->glyphs[j]) continue; /*we currently only split sentences at spaces*/ if (tspan->glyphs[j]->utf_name == (unsigned short) ' ') is_space = 1; else if (tspan->glyphs[j]->utf_name == (unsigned short) '\n') is_space = 2; if (split_words && (j+1!=len) && !is_space) continue; span = (GF_TextSpan*) gf_malloc(sizeof(GF_TextSpan)); memcpy(span, tspan, sizeof(GF_TextSpan)); span->nb_glyphs = split_words ? (j - first_char) : 1; if (split_words && !is_space) span->nb_glyphs++; span->glyphs = gf_malloc(sizeof(void *)*span->nb_glyphs); span->bounds.height = st->ascent + st->descent; span->bounds.y = start_y; span->bounds.x = 0; span->bounds.width = 0; if (split_words) { for (k=0; k<span->nb_glyphs; k++) { span->glyphs[k] = tspan->glyphs[FSLTR ? (first_char+k) : (len - first_char - k - 1)]; span->bounds.width += tspan->font_scale * (span->glyphs[k] ? span->glyphs[k]->horiz_advance : tspan->font->max_advance_h); } } else { span->glyphs[0] = tspan->glyphs[FSLTR ? j : (len - j - 1) ]; span->glyphs[0] = tspan->glyphs[j]; span->bounds.width = tspan->font_scale * (span->glyphs[0] ? span->glyphs[0]->horiz_advance : tspan->font->max_advance_h); } gf_list_add(st->spans, span); /*request a context (first one is always valid when entering sort phase)*/ if (idx) parent_node_start_group(tr_state->parent, NULL, 0); idx++; parent_node_end_text_group(tr_state->parent, &span->bounds, st->ascent, st->descent, idx); if (is_space && split_words) { span = (GF_TextSpan*) gf_malloc(sizeof(GF_TextSpan)); memcpy(span, tspan, sizeof(GF_TextSpan)); span->nb_glyphs = 1; span->glyphs = gf_malloc(sizeof(void *)); gf_list_add(st->spans, span); span->bounds.height = st->ascent + st->descent; span->bounds.y = start_y; span->bounds.x = 0; k = (j - first_char); span->glyphs[0] = tspan->glyphs[FSLTR ? (first_char+k) : (len - first_char - k - 1)]; span->bounds.width = tspan->font_scale * (span->glyphs[0] ? span->glyphs[0]->horiz_advance : tspan->font->max_advance_h); parent_node_start_group(tr_state->parent, NULL, is_space); idx++; parent_node_end_text_group(tr_state->parent, &span->bounds, st->ascent, st->descent, idx); } first_char = j+1; } gf_font_manager_delete_span(ft_mgr, tspan); } }
static void build_text(TextStack *st, M_Text *txt, GF_TraverseState *tr_state) { u32 i, j, int_major, k, styles, count; Fixed fontSize, start_x, start_y, line_spacing, tot_width, tot_height, max_scale, maxExtent; u32 size, trim_size; GF_Font *font; Bool horizontal; GF_TextSpan *trim_tspan = NULL; GF_FontManager *ft_mgr = tr_state->visual->compositor->font_manager; M_FontStyle *fs = (M_FontStyle *)txt->fontStyle; fontSize = FSSIZE; if (fontSize <= 0) { fontSize = INT2FIX(12); if (!tr_state->pixel_metrics) fontSize = gf_divfix(fontSize, tr_state->visual->compositor->output_width); } horizontal = FSHORIZ; start_x = start_y = 0; styles = 0; if (fs && fs->style.buffer) { if (strstr(fs->style.buffer, "BOLD") || strstr(fs->style.buffer, "bold")) styles |= GF_FONT_WEIGHT_BOLD; if (strstr(fs->style.buffer, "ITALIC") || strstr(fs->style.buffer, "italic")) styles |= GF_FONT_ITALIC; if (strstr(fs->style.buffer, "UNDERLINED") || strstr(fs->style.buffer, "underlined")) styles |= GF_FONT_UNDERLINED; } font = gf_font_manager_set_font(ft_mgr, fs ? fs->family.vals : NULL, fs ? fs->family.count : 0, styles); if (!font) return; /*NOTA: we could use integer maths here but we have a risk of overflow with large fonts, so use fixed maths*/ st->ascent = gf_muldiv(fontSize, INT2FIX(font->ascent), INT2FIX(font->em_size)); st->descent = -gf_muldiv(fontSize, INT2FIX(font->descent), INT2FIX(font->em_size)); line_spacing = gf_mulfix(FSSPACE, fontSize); maxExtent = txt->maxExtent; trim_size = 0; if (maxExtent<0) { trim_tspan = gf_font_manager_create_span(ft_mgr, font, "...", fontSize, 0, 0, 0, NULL, 0, styles, (GF_Node*)txt); for (i=0; i<trim_tspan->nb_glyphs; i++) { if (horizontal) { trim_size += trim_tspan->glyphs[i] ? trim_tspan->glyphs[i]->horiz_advance : trim_tspan->font->max_advance_h; } else { trim_size += trim_tspan->glyphs[i] ? trim_tspan->glyphs[i]->vert_advance : trim_tspan->font->max_advance_v; } } } tot_width = tot_height = 0; for (i=0; i < txt->string.count; i++) { GF_TextSpan *tspan; char *str = txt->string.vals[i]; if (!str) continue; tspan = gf_font_manager_create_span(ft_mgr, font, txt->string.vals[i], fontSize, 0, 0, 0, NULL, 0, styles, (GF_Node*)txt); if (!tspan) continue; if (horizontal) tspan->flags |= GF_TEXT_SPAN_HORIZONTAL; size = 0; if (trim_size) { for (j=0; j<tspan->nb_glyphs; j++) { if (horizontal) { size += tspan->glyphs[j] ? tspan->glyphs[j]->horiz_advance : tspan->font->max_advance_h; } else { size += tspan->glyphs[j] ? tspan->glyphs[j]->vert_advance : tspan->font->max_advance_v; } /*word is bigger than allowed extent, rewrite 3 previous chars*/ if ((s32)size*tspan->font_scale >= -maxExtent) { u32 k; u32 nb_chars = (j<2) ? j : 3; for (k=0; k<nb_chars; k++) { u32 idx = nb_chars-k-1; if (horizontal) { size -= tspan->glyphs[j-k] ? tspan->glyphs[j-k]->horiz_advance : tspan->font->max_advance_h; size += trim_tspan->glyphs[idx] ? trim_tspan->glyphs[idx]->horiz_advance : tspan->font->max_advance_h; } else { size -= tspan->glyphs[j-k] ? tspan->glyphs[j-k]->vert_advance : tspan->font->max_advance_v; size += trim_tspan->glyphs[idx] ? trim_tspan->glyphs[idx]->vert_advance : tspan->font->max_advance_v; } tspan->glyphs[j-k] = trim_tspan->glyphs[idx]; } tspan->nb_glyphs = j+1; break; } } } if ((horizontal && !FSLTR) || (!horizontal && !FSTTB)) { for (k=0; k<tspan->nb_glyphs/2; k++) { GF_Glyph *g = tspan->glyphs[k]; tspan->glyphs[k] = tspan->glyphs[tspan->nb_glyphs-1-k]; tspan->glyphs[tspan->nb_glyphs-k-1] = g; } } if (!size) { for (j=0; j<tspan->nb_glyphs; j++) { if (horizontal) { size += tspan->glyphs[j] ? tspan->glyphs[j]->horiz_advance : tspan->font->max_advance_h; } else { size += tspan->glyphs[j] ? tspan->glyphs[j]->vert_advance : tspan->font->max_advance_v; } } } gf_list_add(st->spans, tspan); if (horizontal) { tspan->bounds.width = tspan->font_scale * size; /*apply length*/ if ((txt->length.count>i) && (txt->length.vals[i]>0)) { tspan->x_scale = gf_divfix(txt->length.vals[i], tspan->bounds.width); tspan->bounds.width = txt->length.vals[i]; } if (tot_width < tspan->bounds.width ) tot_width = tspan->bounds.width; } else { tspan->bounds.height = tspan->font_scale * size; /*apply length*/ if ((txt->length.count>i) && (txt->length.vals[i]>0)) { tspan->y_scale = gf_divfix(txt->length.vals[i], tspan->bounds.height); tspan->bounds.height = txt->length.vals[i]; } if (tot_height < tspan->bounds.height) tot_height = tspan->bounds.height; } } if (trim_tspan) gf_font_manager_delete_span(ft_mgr, trim_tspan); max_scale = FIX_ONE; if (horizontal) { if ((maxExtent > 0) && (tot_width>maxExtent)) { max_scale = gf_divfix(maxExtent, tot_width); tot_width = maxExtent; } tot_height = (txt->string.count-1) * line_spacing + (st->ascent + st->descent); st->bounds.height = tot_height; if (!strcmp(FSMINOR, "MIDDLE")) { if (FSTTB) { start_y = tot_height/2; st->bounds.y = start_y; } else { start_y = st->descent + st->ascent - tot_height/2; st->bounds.y = tot_height/2; } } else if (!strcmp(FSMINOR, "BEGIN")) { if (FSTTB) { start_y = st->descent; start_y = 0; st->bounds.y = start_y; } else { st->bounds.y = st->bounds.height; start_y = st->descent + st->ascent; } } else if (!strcmp(FSMINOR, "END")) { if (FSTTB) { start_y = tot_height; st->bounds.y = start_y; } else { start_y = -tot_height + 2*st->descent + st->ascent; st->bounds.y = start_y - (st->descent + st->ascent) + tot_height; } } else { start_y = st->ascent; st->bounds.y = FSTTB ? start_y : (tot_height - st->descent); } } else { if ((maxExtent > 0) && (tot_height>maxExtent) ) { max_scale = gf_divfix(maxExtent, tot_height); tot_height = maxExtent; } tot_width = txt->string.count * line_spacing; st->bounds.width = tot_width; if (!strcmp(FSMINOR, "MIDDLE")) { if (FSLTR) { start_x = -tot_width/2; st->bounds.x = start_x; } else { start_x = tot_width/2 - line_spacing; st->bounds.x = - tot_width + line_spacing; } } else if (!strcmp(FSMINOR, "END")) { if (FSLTR) { start_x = -tot_width; st->bounds.x = start_x; } else { start_x = tot_width-line_spacing; st->bounds.x = 0; } } else { if (FSLTR) { start_x = 0; st->bounds.x = start_x; } else { start_x = -line_spacing; st->bounds.x = -tot_width; } } } /*major-justification*/ if (!strcmp(FSMAJOR, "MIDDLE") ) { int_major = 0; } else if (!strcmp(FSMAJOR, "END") ) { int_major = 1; } else { int_major = 2; } st->bounds.width = st->bounds.height = 0; count = gf_list_count(st->spans); for (i=0; i < count; i++) { GF_TextSpan *span = gf_list_get(st->spans, i); switch (int_major) { /*major-justification MIDDLE*/ case 0: if (horizontal) { start_x = -span->bounds.width/2; } else { //start_y = FSTTB ? span->bounds.height/2 : (-span->bounds.height/2 + space); start_y = span->bounds.height/2; } break; /*major-justification END*/ case 1: if (horizontal) { start_x = (FSLTR) ? -span->bounds.width : 0; } else { //start_y = FSTTB ? span->bounds.height : (-span->bounds.height + space); start_y = FSTTB ? span->bounds.height : 0; } break; /*BEGIN, FIRST or default*/ default: if (horizontal) { start_x = (FSLTR) ? 0 : -span->bounds.width; } else { //start_y = FSTTB ? 0 : space; start_y = FSTTB ? 0 : span->bounds.height; } break; } span->off_x = start_x; span->bounds.x = start_x; if (horizontal) { span->off_y = start_y - st->ascent; span->x_scale = gf_mulfix(span->x_scale, max_scale); span->bounds.y = start_y; } else { span->y_scale = gf_mulfix(span->y_scale, max_scale); span->off_y = start_y - gf_mulfix(st->ascent, span->y_scale); span->bounds.y = start_y; } span->off_x = gf_mulfix(span->off_x, max_scale); span->off_y = gf_mulfix(span->off_y, max_scale); if (horizontal) { start_y += FSTTB ? -line_spacing : line_spacing; span->bounds.height = st->descent + st->ascent; } else { start_x += FSLTR ? line_spacing : -line_spacing; span->bounds.width = line_spacing; } gf_rect_union(&st->bounds, &span->bounds); } }
static void get_domtext_width(GF_Node *node, SVGAllAttributes *atts, GF_TraverseState *tr_state) { u32 i; GF_Font *font; Fixed block_width, *entry; GF_FontManager *fm; GF_TextSpan *span; GF_DOMText *dom_text = (GF_DOMText *)node; if (!dom_text->textContent) return; fm = tr_state->visual->compositor->font_manager; if (!fm) return; font = svg_set_font(tr_state, fm); if (!font) return; 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); if (!span) return; i=0; //count_x, _y: number of x- (y-) position of characters to come in the text flow while ( (i<span->nb_glyphs) && ( (tr_state->count_x>1) || (tr_state->count_y>1) ) ) { block_width = (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale; //store width in tr_state->x_anchors entry = (Fixed*)gf_malloc(sizeof(Fixed)); if (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT) *entry = -block_width; else *entry = block_width; gf_list_add(tr_state->x_anchors, entry); if (tr_state->count_x>0) tr_state->count_x--; if (tr_state->count_y>0) tr_state->count_y--; i++; } //chars are taken one by one while there are indicated positions, then remaining chars are treated as a block if (i<span->nb_glyphs) { block_width = 0; while (i<span->nb_glyphs) { block_width += (span->glyphs[i] ? span->glyphs[i]->horiz_advance : font->max_advance_h) * span->font_scale; i++; } //if last indicated position, create a new item if ((tr_state->count_x==1)||(tr_state->count_y==1) || !gf_list_count(tr_state->x_anchors) ) { entry = (Fixed*)gf_malloc(sizeof(Fixed)); *entry = block_width; if (span->flags & GF_TEXT_SPAN_RIGHT_TO_LEFT) *entry = -block_width; else *entry = block_width; gf_list_add(tr_state->x_anchors, entry); } else { // (count_x == 0 && count_y == 0) otherwise increment last one Fixed *prec_lw = gf_list_last(tr_state->x_anchors); (*prec_lw) += block_width; } //force counters to 0 for next spans/DOM texts if (tr_state->count_x==1) tr_state->count_x = 0; if (tr_state->count_y==1) tr_state->count_y = 0; } gf_font_manager_delete_span(fm, span); }