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 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 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) { GF_TextSpan *span = NULL; char *dup_text; u32 i, j, len; char prev; Bool preserve = (atts->xml_space && (*atts->xml_space==XML_SPACE_PRESERVE)) ? GF_TRUE : GF_FALSE; len = (u32) strlen(textContent); dup_text = (char*)gf_malloc(len+1); switch (tr_state->last_char_type) { case 2: prev = 0; break; case 0: case 1: default: prev = ' '; break; } for (i = 0, j = 0; i < len; i++) { if (textContent[i] == ' ') { if (prev == ' ' && !preserve) { /* ignore space */ } else { dup_text[j] = textContent[i]; prev = dup_text[j]; j++; } } else if (textContent[i] == '\t') { if (prev == ' ' && !preserve) { /* ignore space */ } else { dup_text[j] = ' '; prev = dup_text[j]; j++; } } else if ((textContent[i] == '\n') || (textContent[i] == '\r') ) { if (prev == ' ' && preserve) { dup_text[j] = ' '; prev = dup_text[j]; j++; } else if (!i && !prev) { prev = dup_text[j] = ' '; j++; } } else if ( (((u8) textContent[i] == 0xc2) && ((u8) textContent[i+1] == 0xa0)) ) { if (prev == ' ' && !preserve) { /* ignore space */ } else { dup_text[j] = ' '; prev = dup_text[j]; j++; } i++; } else { dup_text[j] = textContent[i]; prev = dup_text[j]; j++; } } dup_text[j] = 0; if (!j) tr_state->last_char_type = 1; else tr_state->last_char_type = (dup_text[j-1]==' ') ? 1 : 2; /*SVG text is fliped by default (text y-axis is the inverse of SVG y-axis*/ 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); gf_free(dup_text); if (span) span->flags |= GF_TEXT_SPAN_HORIZONTAL; return span; }