From 5f85864f4d82a42f30160299d7897720e865c454 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Sat, 2 Mar 2013 23:56:05 +0100 Subject: New wrapping stuff --- src/wrap.c | 319 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 src/wrap.c (limited to 'src/wrap.c') diff --git a/src/wrap.c b/src/wrap.c new file mode 100644 index 0000000..9948f70 --- /dev/null +++ b/src/wrap.c @@ -0,0 +1,319 @@ +/* + * wrap.c + * + * Colloquium - A tiny presentation program + * + * Copyright (c) 2012 Thomas White + * + * This program 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 + +#include "wrap.h" +#include "frame.h" + + +static void alloc_lines(struct frame *fr) +{ + struct wrap_line *lines_new; + + lines_new = realloc(fr->lines, fr->max_lines * sizeof(struct wrap_line)); + if ( lines_new == NULL ) { + fprintf(stderr, "Couldn't allocate memory for lines!\n"); + return; + } + + fr->lines = lines_new; +} + + +static void alloc_boxes(struct wrap_line *l) +{ + struct wrap_box *boxes_new; + + boxes_new = realloc(l->boxes, l->max_boxes * sizeof(struct wrap_box)); + if ( boxes_new == NULL ) { + fprintf(stderr, "Couldn't allocate memory for boxes!\n"); + return; + } + + l->boxes = boxes_new; +} + + +static void initialise_line(struct wrap_line *l) +{ + l->n_boxes = 0; + l->max_boxes = 32; + l->boxes = NULL; + l->width = 0; + l->height = 0; + l->ascent = 0; + alloc_boxes(l); +} + + +void get_cursor_pos(struct frame *fr, size_t pos, + double *xposd, double *yposd, double *line_height) +{ + signed int line; + int i; + + *xposd = 0; + *yposd = 0; + + line = 0; + for ( i=0; in_lines; i++ ) { + line = i; + if ( fr->lines[i].sc_offset > pos ) { + line = i-1; + break; + } + *yposd += fr->lines[i].height; + *line_height = fr->lines[i].height / PANGO_SCALE; + } + assert(line >= 0); + + *xposd += fr->lop.pad_l; + + *yposd /= PANGO_SCALE; + *yposd += fr->lop.pad_t; +} + + +static void shape_and_measure(gpointer data, gpointer user_data) +{ + struct wrap_box *box = user_data; + PangoItem *item = data; + PangoRectangle rect; + + /* FIXME: Don't assume only one run per wrap box */ + box->glyphs = pango_glyph_string_new(); + + pango_shape(box->text+item->offset, item->length, &item->analysis, + box->glyphs); + + pango_glyph_string_extents(box->glyphs, box->font, NULL, &rect); + + box->width += rect.width; + if ( rect.height > box->height ) { + box->height = rect.height; + } + if ( PANGO_ASCENT(rect) > box->ascent ) { + box->ascent = PANGO_ASCENT(rect); + } +} + + +static void calc_line_geometry(struct wrap_line *line) +{ + int i; + + line->width = 0; + + for ( i=0; in_boxes; i++ ) { + struct wrap_box *box = &line->boxes[i]; + line->width += box->width; + if ( box->height > line->height ) line->height = box->height; + if ( box->ascent > line->ascent ) line->ascent = box->ascent; + } +} + + +/* Add "text", followed by a space of type "space", to "line" */ +static int add_wrap_box(struct wrap_line *line, char *text, + enum wrap_box_space space, PangoContext *pc, + PangoFont *font, double col[4]) +{ + GList *pango_items; + struct wrap_box *box; + + if ( line->n_boxes == line->max_boxes ) { + line->max_boxes += 32; + alloc_boxes(line); + if ( line->n_boxes == line->max_boxes ) return 1; + } + + box = &line->boxes[line->n_boxes]; + box->type = WRAP_BOX_PANGO; + box->text = text; + box->space = space; + box->font = font; + box->width = 0; + box->ascent = 0; + box->height = 0; + line->n_boxes++; + + pango_items = pango_itemize(pc, text, 0, strlen(text), NULL, NULL); + g_list_foreach(pango_items, shape_and_measure, box); + g_list_free(pango_items); + + return 0; +} + + +static int split_words(struct wrap_line *boxes, PangoContext *pc, char *sc, + PangoFont *font, double col[4]) +{ + PangoLogAttr *log_attrs; + size_t len; + size_t i, start; + + /* Empty block? */ + if ( sc == NULL ) return 1; + + len = strlen(sc); + if ( len == 0 ) return 1; + + log_attrs = malloc((len+1)*sizeof(PangoLogAttr)); + if ( log_attrs == NULL ) return 1; + + /* Create glyph string */ + pango_get_log_attrs(sc, len, -1, pango_language_get_default(), + log_attrs, len+1); + + start = 0; + for ( i=0; i start ) { + + char *word; + word = strndup(sc+start, i-start); + if ( word == NULL ) { + fprintf(stderr, "strndup() failed.\n"); + free(log_attrs); + return 1; + } + + add_wrap_box(boxes, word, WRAP_SPACE_NONE, pc, font, col); + + } + + free(log_attrs); + return 0; +} + + +static struct wrap_line *sc_to_wrap_boxes(const char *sc, PangoContext *pc) +{ + struct wrap_line *boxes; + SCBlockList *bl; + SCBlockListIterator *iter; + struct scblock *b; + PangoFontDescription *fontdesc; + PangoFont *font; + double col[4]; + + boxes = malloc(sizeof(struct wrap_line)); + if ( boxes == NULL ) { + fprintf(stderr, "Failed to allocate boxes.\n"); + return NULL; + } + initialise_line(boxes); + + bl = sc_find_blocks(sc, NULL); + + if ( bl == NULL ) { + printf("Failed to find blocks.\n"); + return NULL; + } + + /* FIXME: Determine the proper font to use */ + fontdesc = pango_font_description_from_string("Sorts Mill Goudy 16"); + font = pango_font_map_load_font(pango_context_get_font_map(pc), + pc, fontdesc); + pango_font_description_free(fontdesc); + col[0] = 0.0; col[1] = 0.0; col[2] = 0.0; col[3] = 1.0; + + /* Iterate through SC blocks and send each one in turn for processing */ + for ( b = sc_block_list_first(bl, &iter); + b != NULL; + b = sc_block_list_next(bl, iter) ) + { + if ( b->name == NULL ) { + if ( split_words(boxes, pc, b->contents, font, col) ) { + fprintf(stderr, "Splitting failed.\n"); + } + } + + /* FIXME: Handle images */ + } + sc_block_list_free(bl); + + return boxes; +} + + +/* Wrap the StoryCode inside "fr->sc" so that it fits within width "fr->w", + * and generate fr->lines */ +int wrap_contents(struct frame *fr, PangoContext *pc) +{ + struct wrap_line *boxes; + + /* Turn the StoryCode into wrap boxes, all on one line */ + boxes = sc_to_wrap_boxes(fr->sc, pc); + if ( boxes == NULL ) { + fprintf(stderr, "Failed to create wrap boxes.\n"); + return 1; + } + + /* FIXME: Hack for test purposes */ + calc_line_geometry(boxes); + fr->lines = boxes; + fr->n_lines = 1; + fr->max_lines = 1; + + return 0; +} + -- cgit v1.2.3