/* * shape.c * * Copyright © 2014-2015 Thomas White * * This file is part of Colloquium. * * Colloquium is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "wrap.h" #include "sc_interp.h" #include "shape.h" #include "boxvec.h" static void shape_segment(struct wrap_box *box, struct text_seg *seg) { PangoRectangle rect; const char *tp; const char *ep; if ( seg->len_chars == 0 ) { fprintf(stderr, "Shaping a zero-length segment\n"); return; } tp = g_utf8_offset_to_pointer(sc_block_contents(box->scblock), box->offs_char + seg->offs_char); ep = g_utf8_offset_to_pointer(sc_block_contents(box->scblock), box->offs_char + seg->offs_char + seg->len_chars); if ( seg->glyphs != NULL ) { pango_glyph_string_free(seg->glyphs); } //printf("shaping '%s' (%i chars)\n", tp, seg->len_chars); seg->glyphs = pango_glyph_string_new(); pango_shape(tp, ep-tp, &seg->analysis, seg->glyphs); pango_glyph_string_extents(seg->glyphs, box->font, NULL, &rect); seg->width = rect.width; seg->height = rect.height; seg->ascent = PANGO_ASCENT(rect); } static void calc_box_geometry(struct wrap_box *box) { int i; if ( box->type != WRAP_BOX_PANGO ) return; box->width = 0; box->height = 0; box->ascent = 0; for ( i=0; in_segs; i++ ) { struct text_seg *seg = &box->segs[i]; box->width += seg->width; if ( seg->height > box->height ) box->height = seg->height; if ( seg->ascent > box->ascent ) box->ascent = seg->ascent; } } void shape_box(struct wrap_box *box) { int i; printf("shaping box %p! n_segs=%i\n", box, box->n_segs); for ( i=0; in_segs; i++ ) { shape_segment(box, &box->segs[i]); } calc_box_geometry(box); } static void add_nothing_box(struct boxvec *boxes, SCBlock *scblock, int editable, enum wrap_box_space sp, SCInterpreter *scin, size_t offs) { struct wrap_box *box; box = calloc(1, sizeof(struct wrap_box)); if ( box == NULL ) { fprintf(stderr, "Failed to allocate a nothing box.\n"); return; } box->type = WRAP_BOX_NOTHING; box->scblock = scblock; box->offs_char = offs; box->space = sp; box->width = 0; box->len_chars = 0; box->ascent = sc_interp_get_ascent(scin); box->height = sc_interp_get_height(scin); box->filename = NULL; box->editable = editable; box->segs = NULL; box->n_segs = 0; bv_add(boxes, box); } static UNUSED char *swizzle(const char *inp, size_t len) { int i; char *out = malloc(len+1); strncpy(out, inp, len); out[len] = '\0'; for ( i=0; isegs[box->n_segs].analysis = item->analysis; box->segs[box->n_segs].glyphs = NULL; box->segs[box->n_segs].offs_char = item->offset; box->segs[box->n_segs].len_chars = item->num_chars; shape_segment(box, &box->segs[box->n_segs++]); } int itemize_and_shape(struct wrap_box *box, PangoContext *pc) { GList *pango_items; PangoAttrList *attrs; PangoAttribute *attr; char *tptr; char *eptr; const char *text; int nseg; text = sc_block_contents(box->scblock); tptr = g_utf8_offset_to_pointer(text, box->offs_char); eptr = g_utf8_offset_to_pointer(tptr, box->len_chars); /* Fill in the font, needed later for rendering */ box->font = pango_font_map_load_font(pango_context_get_font_map(pc), pc, box->fontdesc); attrs = pango_attr_list_new(); attr = pango_attr_font_desc_new(box->fontdesc); pango_attr_list_insert_before(attrs, attr); pango_items = pango_itemize(pc, tptr, 0, eptr-tptr, attrs, NULL); nseg = g_list_length(pango_items); box->segs = malloc(nseg * sizeof(struct text_seg)); if ( box->segs == NULL ) return 1; box->n_segs = 0; g_list_foreach(pango_items, add_seg, box); g_list_free(pango_items); pango_attr_list_unref(attrs); calc_box_geometry(box); return 0; } /* Add "text", followed by a space of type "space", to "line" */ static int add_text_box(struct boxvec *boxes, enum wrap_box_space space, PangoContext *pc, SCInterpreter *scin, SCBlock *bl, size_t offs, size_t len, int editable) { struct wrap_box *box; const char *tp; double *col; while ( len==0 ) { add_nothing_box(boxes, bl, editable, space, scin, offs); return 0; } /* Create the box */ box = calloc(1, sizeof(struct wrap_box)); if ( box == NULL ) { fprintf(stderr, "Failed to allocate a text box.\n"); return 1; } box->type = WRAP_BOX_PANGO; box->space = space; box->fontdesc = pango_font_description_copy(sc_interp_get_fontdesc(scin)); box->width = 0; box->editable = editable; box->ascent = sc_interp_get_ascent(scin); box->height = sc_interp_get_height(scin); /* Link to the actual text */ box->scblock = bl; tp = sc_block_contents(bl); box->offs_char = g_utf8_pointer_to_offset(tp, tp+offs); box->len_chars = g_utf8_strlen(tp+offs, len); col = sc_interp_get_fgcol(scin); box->col[0] = col[0]; /* Red */ box->col[1] = col[1]; /* Green */ box->col[2] = col[2]; /* Blue */ box->col[3] = col[3]; /* Alpha */ if ( itemize_and_shape(box, pc) == 0 ) { bv_add(boxes, box); } else { fprintf(stderr, "Shaping error!\n"); return 1; } return 0; } void add_callback_box(struct boxvec *boxes, double w, double h, SCCallbackDrawFunc draw_func, SCCallbackClickFunc click_func, void *bvp, void *vp) { struct wrap_box *box; box = calloc(1, sizeof(struct wrap_box)); if ( box == NULL ) { fprintf(stderr, "Failed to allocate a callback box.\n"); return; } box->type = WRAP_BOX_CALLBACK; box->scblock = NULL; box->offs_char = 0; box->space = WRAP_SPACE_NONE; box->width = pango_units_from_double(w); box->ascent = pango_units_from_double(h); box->height = pango_units_from_double(h); box->draw_func = draw_func; box->click_func = click_func; box->bvp = bvp; box->vp = vp; box->editable = 0; bv_add(boxes, box); } void add_image_box(struct boxvec *boxes, const char *filename, int w, int h, int editable) { struct wrap_box *box; box = calloc(1, sizeof(struct wrap_box)); if ( box == NULL ) { fprintf(stderr, "Failed to allocate a callback box.\n"); return; } box->type = WRAP_BOX_IMAGE; box->scblock = NULL; box->offs_char = 0; box->space = WRAP_SPACE_NONE; box->width = pango_units_from_double(w); box->ascent = pango_units_from_double(h); box->height = pango_units_from_double(h); box->filename = strdup(filename); box->editable = editable; bv_add(boxes, box); } int split_words(struct boxvec *boxes, PangoContext *pc, SCBlock *bl, PangoLanguage *lang, int editable, SCInterpreter *scin) { PangoLogAttr *log_attrs; glong len_chars, i; size_t len_bytes, start; int chars_done; const char *text = sc_block_contents(bl); /* Empty block? */ if ( text == NULL ) return 1; len_chars = g_utf8_strlen(text, -1); if ( len_chars == 0 ) { add_text_box(boxes, WRAP_SPACE_NONE, pc, scin, bl, 0, 0, editable); return 1; } len_bytes = strlen(text); log_attrs = malloc((len_chars+1)*sizeof(PangoLogAttr)); if ( log_attrs == NULL ) return 1; pango_get_log_attrs(text, len_bytes, -1, lang, log_attrs, len_chars+1); //debug_log_attrs(len_chars, text, log_attrs); start = 0; chars_done = 0; for ( i=0; i0) && (g_utf8_prev_char(ptr)[0]=='\n') ) len--; } else if ( (i>0) && log_attrs[i-1].is_expandable_space ) { type = WRAP_SPACE_INTERWORD; len--; } else { type = WRAP_SPACE_NONE; } if ( add_text_box(boxes, type, pc, scin, bl, start, len, editable) ) { fprintf(stderr, "Failed to add wrap box.\n"); } start = offs; chars_done = i; } } /* Add the stuff left over at the end */ if ( i > chars_done ) { size_t l = strlen(text+start); if ( (text[start+l-1] == '\n') ) { /* There is a newline at the end of the SC */ add_text_box(boxes, WRAP_SPACE_EOP, pc, scin, bl, start, l-1, editable); } else { add_text_box(boxes, WRAP_SPACE_NONE, pc, scin, bl, start, l, editable); } } free(log_attrs); return 0; }