aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas White <taw@bitwiz.org.uk>2013-03-02 23:56:05 +0100
committerThomas White <taw@bitwiz.org.uk>2013-03-02 23:56:05 +0100
commit5f85864f4d82a42f30160299d7897720e865c454 (patch)
tree7d7298d480f16706a547520bac908a638c098313
parent59c7b765c44ee2ae3e81366bea2fc149e2466d13 (diff)
New wrapping stuff
-rw-r--r--Makefile.am9
-rw-r--r--src/frame.h29
-rw-r--r--src/mainwindow.c14
-rw-r--r--src/render.c416
-rw-r--r--src/render.h2
-rw-r--r--src/wrap.c319
-rw-r--r--src/wrap.h96
7 files changed, 497 insertions, 388 deletions
diff --git a/Makefile.am b/Makefile.am
index 980e5fe..3ee669c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,11 +13,11 @@ LDADD = $(top_builddir)/lib/libgnu.la @IGNORE_UNUSED_LIBRARIES_CFLAGS@ \
src_colloquium_SOURCES = src/colloquium.c src/render.c \
src/mainwindow.c src/presentation.c \
src/stylesheet.c src/loadsave.c src/frame.c \
- src/slideshow.c
+ src/slideshow.c src/wrap.c
INCLUDES = -Ilibstorycode/src -Iharfatum/src
-EXTRA_DIST += src/presentation.h src/render.h \
+EXTRA_DIST += src/presentation.h src/render.h src/wrap.h \
src/stylesheet.h src/loadsave.h src/slideshow.h
colloquiumdir = $(datadir)/colloquium
@@ -32,7 +32,8 @@ EXTRA_DIST += $(colloquium_DATA)
noinst_PROGRAMS = tests/render_test tests/render_test_sc1
TESTS = tests/render_test tests/render_test_sc1
-tests_render_test_SOURCES = tests/render_test.c src/render.c src/frame.c
+tests_render_test_SOURCES = tests/render_test.c src/render.c src/frame.c \
+ src/wrap.c
tests_render_test_sc1_SOURCES = tests/render_test_sc1.c \
- src/render.c src/frame.c
+ src/render.c src/frame.c src/wrap.c
diff --git a/src/frame.h b/src/frame.h
index 832473c..6c643ae 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -74,35 +74,6 @@ struct layout_parameters
};
-enum wrap_box_type
-{
- WRAP_BOX_PANGO,
-};
-
-
-struct wrap_box
-{
- enum wrap_box_type type;
- int width; /* Pango units */
-
- /* For type == WRAP_BOX_PANGO */
- PangoGlyphItem *glyph_item;
- char *text;
-};
-
-
-struct wrap_line
-{
- int width;
- int height; /* Pango units */
- int ascent; /* Pango units */
-
- int n_boxes;
- int max_boxes;
- struct wrap_box *boxes;
-};
-
-
struct frame
{
struct frame **children;
diff --git a/src/mainwindow.c b/src/mainwindow.c
index 4eb3549..05f5a3d 100644
--- a/src/mainwindow.c
+++ b/src/mainwindow.c
@@ -769,20 +769,15 @@ static void draw_editing_box(cairo_t *cr, struct frame *fr)
static void draw_caret(cairo_t *cr, struct frame *fr, int pos)
{
-#if 0
- double xposd, yposd, cx;
- double clow, chigh;
+ double xposd, yposd, line_height;
+ double cx, clow, chigh;
const double t = 1.8;
- pango_layout_get_cursor_pos(o->layout,
- o->insertion_point+o->insertion_trail,
- &pos, NULL);
+ get_cursor_pos(fr, pos, &xposd, &yposd, &line_height);
- xposd = pos.x/PANGO_SCALE;
cx = fr->x + xposd;
- yposd = pos.y/PANGO_SCALE;
clow = fr->y + yposd;
- chigh = clow + (pos.height/PANGO_SCALE);
+ chigh = clow + line_height;
cairo_move_to(cr, cx, clow);
cairo_line_to(cr, cx, chigh);
@@ -800,7 +795,6 @@ static void draw_caret(cairo_t *cr, struct frame *fr, int pos)
cairo_set_source_rgb(cr, 0.86, 0.0, 0.0);
cairo_set_line_width(cr, 1.0);
cairo_stroke(cr);
-#endif
}
diff --git a/src/render.c b/src/render.c
index 0650f96..3077c36 100644
--- a/src/render.c
+++ b/src/render.c
@@ -38,293 +38,22 @@
#include "presentation.h"
#include "frame.h"
#include "render.h"
+#include "wrap.h"
-struct renderstuff
+static void render_glyph_box(cairo_t *cr, struct wrap_box *box)
{
- cairo_t *cr;
- PangoContext *pc;
- PangoFontMap *fontmap;
- PangoAttrList *attrs;
-
- /* Text for the block currently being processed */
- const char *cur_text;
- int cur_len;
-
- int wrap_w; /* Pango units */
-
- struct frame *fr;
-};
-
-
-static void free_line_bits(struct wrap_line *l)
-{
- int i;
- for ( i=0; i<l->n_boxes; i++ ) {
-
- switch ( l->boxes[i].type ) {
-
- case WRAP_BOX_PANGO :
- pango_glyph_item_free(l->boxes[i].glyph_item);
- free(l->boxes[i].text);
- break;
-
- }
-
- }
-
- free(l->boxes);
-}
-
-
-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 add_glyph_box_to_line(struct wrap_line *line, PangoGlyphItem *gi,
- char *text)
-{
- PangoRectangle rect;
- int ascent;
-
- if ( line->n_boxes == line->max_boxes ) {
- line->max_boxes += 32;
- alloc_boxes(line);
- if ( line->n_boxes == line->max_boxes ) return;
- }
-
- pango_glyph_string_extents(gi->glyphs, gi->item->analysis.font,
- NULL, &rect);
-
- line->boxes[line->n_boxes].type = WRAP_BOX_PANGO;
- line->boxes[line->n_boxes].glyph_item = gi;
- line->boxes[line->n_boxes].text = text;
- line->boxes[line->n_boxes].width = rect.width;
- line->n_boxes++;
-
- line->width += rect.width;
- if ( line->height < rect.height ) line->height = rect.height;
-
- ascent = PANGO_ASCENT(rect);
- if ( ascent > line->ascent ) line->ascent = ascent;
-}
-
-
-static const char *add_chars_to_line(struct renderstuff *s,
- PangoGlyphItem *orig, int n,
- const char *cur_text_ptr)
-{
- int split_len;
- char *split_ptr;
- char *before_text;
-
- split_ptr = g_utf8_offset_to_pointer(cur_text_ptr, n);
- before_text = strndup(cur_text_ptr, split_ptr-cur_text_ptr);
- if ( before_text == NULL ) {
- fprintf(stderr, "Failed to split text\n");
- /* But continue */
- }
-
- if ( n < orig->item->num_chars ) {
-
- PangoGlyphItem *before;
-
- split_len = split_ptr - cur_text_ptr;
-
- before = pango_glyph_item_split(orig, cur_text_ptr, split_len);
-
- add_glyph_box_to_line(&s->fr->lines[s->fr->n_lines],
- before, before_text);
-
- orig->item->offset = 0;
-
- } else {
-
- PangoGlyphItem *copy;
- copy = pango_glyph_item_copy(orig);
- add_glyph_box_to_line(&s->fr->lines[s->fr->n_lines],
- copy, before_text);
-
- }
-
- return split_ptr;
-}
-
-
-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);
-}
-
-
-static void dispatch_line(struct renderstuff *s)
-{
- s->fr->n_lines++;
-
- if ( s->fr->n_lines == s->fr->max_lines ) {
- s->fr->max_lines += 32;
- alloc_lines(s->fr);
- if ( s->fr->n_lines == s->fr->max_lines ) return;
- }
-
- initialise_line(&s->fr->lines[s->fr->n_lines]);
-}
-
-
-static void wrap_text(gpointer data, gpointer user_data)
-{
- struct renderstuff *s = user_data;
- PangoItem *item = data;
- PangoGlyphString *glyphs;
- PangoLogAttr *log_attrs;
- PangoGlyphItem gitem;
- int *log_widths;
- int width_remain;
- int width_used;
- int i, pos, n;
- const char *ptr;
-
- log_attrs = calloc(item->num_chars+1, sizeof(PangoLogAttr));
- if ( log_attrs == NULL ) {
- fprintf(stderr, "Failed to allocate memory for log attrs\n");
- return;
- }
-
- log_widths = calloc(item->num_chars+1, sizeof(int));
- if ( log_widths == NULL ) {
- fprintf(stderr, "Failed to allocate memory for log widths\n");
- return;
- }
-
- pango_get_log_attrs(s->cur_text, s->cur_len, -1,
- pango_language_get_default(),
- log_attrs, item->num_chars+1);
-
- glyphs = pango_glyph_string_new();
- pango_shape(s->cur_text+item->offset, item->length, &item->analysis,
- glyphs);
-
- pango_glyph_string_get_logical_widths(glyphs, s->cur_text+item->offset,
- item->length, 0, log_widths);
- gitem.glyphs = glyphs;
- gitem.item = item;
-
- /* FIXME: Replace this with a real typesetting algorithm */
- width_remain = s->wrap_w*PANGO_SCALE
- - s->fr->lines[s->fr->n_lines].width;
- width_used = 0;
- pos = 0;
- n = item->num_chars;
- ptr = s->cur_text;
- for ( i=0; i<n; i++ ) {
-
- if ( !log_attrs[i].is_char_break ) {
- width_used += log_widths[i];
- pos++;
- continue;
- }
-
- if ( log_attrs[i].is_mandatory_break
- || (log_widths[i] + width_used > width_remain) ) {
-
- ptr = add_chars_to_line(s, &gitem, pos, ptr);
-
- /* New line */
- dispatch_line(s);
- width_remain = s->wrap_w * PANGO_SCALE;
- width_used = 0;
- pos = 0;
-
- }
-
- pos++;
- width_used += log_widths[i];
-
- }
- ptr = add_chars_to_line(s, &gitem, pos, ptr);
-
- /* Don't dispatch the last line, because the next item might add
- * more text to it, or the next SC block might add something else. */
-
- free(log_widths);
- free(log_attrs);
-}
-
-
-static void process_sc_block(struct renderstuff *s, const char *sc_name,
- const char *sc_options, const char *sc_contents)
-{
- size_t len;
- GList *item_list;
-
- /* Only process textual blocks, for now */
- if ( sc_name != NULL ) return;
- if ( sc_options != NULL ) {
- fprintf(stderr, "Block has options. WTF?\n");
- return;
- }
-
- /* Empty block? */
- if ( sc_contents == NULL ) return;
-
- len = strlen(sc_contents);
- if ( len == 0 ) return;
-
- /* Create glyph string */
- item_list = pango_itemize(s->pc, sc_contents, 0, len, s->attrs, NULL);
- s->cur_text = sc_contents;
- s->cur_len = len;
- g_list_foreach(item_list, wrap_text, s);
-
- g_list_free(item_list);
-}
-
-
-static double render_glyph_box(cairo_t *cr, struct wrap_box *box)
-{
- double box_w;
- if ( box->glyph_item == NULL ) {
+ if ( box->glyphs == NULL ) {
fprintf(stderr, "Box %p has NULL pointer.\n", box);
- return 0.0;
+ return;
}
- cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); /* FIXME: Colour */
- pango_cairo_show_glyph_item(cr, box->text, box->glyph_item);
- box_w = pango_glyph_string_get_width(box->glyph_item->glyphs);
- return box_w;
+ cairo_set_source_rgba(cr, box->col[0], box->col[1], box->col[2],
+ box->col[3]);
+ pango_cairo_glyph_string_path(cr, box->font, box->glyphs);
}
-static void render_boxes(struct wrap_line *line, struct renderstuff *s)
+static void render_boxes(struct wrap_line *line, cairo_t *cr)
{
int j;
double x_pos = 0.0;
@@ -333,65 +62,89 @@ static void render_boxes(struct wrap_line *line, struct renderstuff *s)
struct wrap_box *box;
- cairo_save(s->cr);
+ cairo_save(cr);
box = &line->boxes[j];
- cairo_rel_move_to(s->cr, x_pos, 0.0);
+ cairo_rel_move_to(cr, x_pos, 0.0);
switch ( line->boxes[j].type ) {
case WRAP_BOX_PANGO :
- render_glyph_box(s->cr, box);
+ render_glyph_box(cr, box);
+ break;
+
+ case WRAP_BOX_IMAGE :
+ /* FIXME ! */
break;
}
x_pos += (double)line->boxes[j].width / PANGO_SCALE;
- cairo_restore(s->cr);
+ cairo_restore(cr);
}
}
-static void render_lines(struct renderstuff *s)
+static void render_lines(struct frame *fr, cairo_t *cr)
{
int i;
double y_pos = 0.0;
- for ( i=0; i<s->fr->n_lines; i++ ) {
+ for ( i=0; i<fr->n_lines; i++ ) {
- double asc = s->fr->lines[i].ascent/PANGO_SCALE;
+ double asc = fr->lines[i].ascent/PANGO_SCALE;
- //cairo_move_to(s->cr, 0, y_pos+asc+0.5);
- //cairo_line_to(s->cr, s->lines[i].width, y_pos+asc+0.5);
- //cairo_set_source_rgb(s->cr, 0.0, 0.0, 0.0);
- //cairo_set_line_width(s->cr, 1.0);
- //cairo_stroke(s->cr);
+ //cairo_move_to(cr, 0, y_pos+asc+0.5);
+ //cairo_line_to(cr, s->lines[i].width, y_pos+asc+0.5);
+ //cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
+ //cairo_set_line_width(cr, 1.0);
+ //cairo_stroke(cr);
/* Move to beginning of the line */
- cairo_move_to(s->cr, 0.0, asc+y_pos);
+ cairo_move_to(cr, 0.0, asc+y_pos);
/* Render the line */
- render_boxes(&s->fr->lines[i], s);
+ render_boxes(&fr->lines[i], cr);
/* FIXME: line spacing */
- y_pos += s->fr->lines[i].height/PANGO_SCALE + 0.0;
+ y_pos += fr->lines[i].height/PANGO_SCALE + 0.0;
+
+ }
+}
+
+
+static void free_line_bits(struct wrap_line *l)
+{
+ int i;
+ for ( i=0; i<l->n_boxes; i++ ) {
+
+ switch ( l->boxes[i].type ) {
+
+ case WRAP_BOX_PANGO :
+ pango_glyph_string_free(l->boxes[i].glyphs);
+ free(l->boxes[i].text);
+ break;
+
+ case WRAP_BOX_IMAGE :
+ break;
+
+ }
}
+
+ free(l->boxes);
}
/* Render Level 1 Storycode (no subframes) */
static int render_sc(struct frame *fr)
{
- PangoFontDescription *fontdesc;
- struct renderstuff s;
- PangoAttribute *attr_font;
- SCBlockList *bl;
- SCBlockListIterator *iter;
- struct scblock *b;
int i;
+ cairo_t *cr;
+ PangoFontMap *fontmap;
+ PangoContext *pc;
for ( i=0; i<fr->n_lines; i++ ) {
free_line_bits(&fr->lines[i]);
@@ -403,62 +156,37 @@ static int render_sc(struct frame *fr)
if ( fr->sc == NULL ) return 0;
- bl = sc_find_blocks(fr->sc, NULL);
-
- if ( bl == NULL ) {
- printf("Failed to find blocks.\n");
- return 0;
- }
-
- s.wrap_w = fr->w - fr->lop.pad_l - fr->lop.pad_r;
- s.fr = fr;
- s.fr->lines = NULL;
- s.fr->n_lines = 0;
- s.fr->max_lines = 64;
- alloc_lines(s.fr);
- initialise_line(&s.fr->lines[0]);
-
- /* Find and load font */
- s.fontmap = pango_cairo_font_map_get_default();
- s.pc = pango_font_map_create_context(s.fontmap);
- fontdesc = pango_font_description_from_string("Sorts Mill Goudy 16");
-
- /* Set up attribute list to use the font */
- s.attrs = pango_attr_list_new();
- attr_font = pango_attr_font_desc_new(fontdesc);
- pango_attr_list_insert_before(s.attrs, attr_font);
- pango_font_description_free(fontdesc);
-
- /* 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) )
- {
- process_sc_block(&s, b->name, b->options, b->contents);
- }
- sc_block_list_free(bl);
- dispatch_line(&s);
-
- /* Create surface and render the contents */
+ /* Create surface and Cairo stuff */
if ( fr->contents != NULL ) cairo_surface_destroy(fr->contents);
fr->contents = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
fr->w, fr->h);
- s.cr = cairo_create(fr->contents);
+ cr = cairo_create(fr->contents);
cairo_font_options_t *fopts;
fopts = cairo_font_options_create();
cairo_font_options_set_hint_style(fopts, CAIRO_HINT_STYLE_NONE);
cairo_font_options_set_hint_metrics(fopts, CAIRO_HINT_METRICS_DEFAULT);
cairo_font_options_set_antialias(fopts, CAIRO_ANTIALIAS_GRAY);
- cairo_set_font_options(s.cr, fopts);
+ cairo_set_font_options(cr, fopts);
- cairo_translate(s.cr, fr->lop.pad_l, fr->lop.pad_t);
- render_lines(&s);
+ /* Find and load font */
+ fontmap = pango_cairo_font_map_get_default();
+ pc = pango_font_map_create_context(fontmap);
+ /* Set up lines */
+ if ( wrap_contents(fr, pc) ) {
+ fprintf(stderr, "Failed to wrap lines.\n");
+ return 1;
+ }
+
+ /* Actually render the lines */
+ cairo_translate(cr, fr->lop.pad_l, fr->lop.pad_t);
+ render_lines(fr, cr);
+
+ /* Tidy up */
cairo_font_options_destroy(fopts);
- cairo_destroy(s.cr);
- pango_attr_list_unref(s.attrs);
- g_object_unref(s.pc);
+ cairo_destroy(cr);
+ g_object_unref(pc);
return 0;
}
diff --git a/src/render.h b/src/render.h
index 1ce9176..2502590 100644
--- a/src/render.h
+++ b/src/render.h
@@ -3,7 +3,7 @@
*
* Colloquium - A tiny presentation program
*
- * Copyright (c) 2011 Thomas White <taw@bitwiz.org.uk>
+ * Copyright (c) 2011-2013 Thomas White <taw@bitwiz.org.uk>
*
* 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
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 <taw@bitwiz.org.uk>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pango/pangocairo.h>
+
+#include <storycode.h>
+
+#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; i<fr->n_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; i<line->n_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<len; i++ ) {
+
+ if ( log_attrs[i].is_line_break ) {
+
+ char *word;
+ enum wrap_box_space type;
+
+ /* Stuff up to (but not including) sc[i] forms a
+ * wap box */
+ word = strndup(sc+start, i-start);
+ if ( word == NULL ) {
+ fprintf(stderr, "strndup() failed.\n");
+ free(log_attrs);
+ return 1;
+ }
+
+ if ( log_attrs[i].is_mandatory_break ) {
+ type = WRAP_SPACE_EOP;
+ } else if ( log_attrs[i].is_expandable_space ) {
+ type = WRAP_SPACE_INTERWORD;
+ } else {
+ type = WRAP_SPACE_NONE;
+ }
+
+ add_wrap_box(boxes, word, type, pc, font, col);
+ start = i;
+
+ }
+
+ }
+ if ( 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;
+}
+
diff --git a/src/wrap.h b/src/wrap.h
new file mode 100644
index 0000000..85609a3
--- /dev/null
+++ b/src/wrap.h
@@ -0,0 +1,96 @@
+/*
+ * wrap.h
+ *
+ * Colloquium - A tiny presentation program
+ *
+ * Copyright (c) 2012 Thomas White <taw@bitwiz.org.uk>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef WRAP_H
+#define WRAP_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "frame.h"
+
+
+enum wrap_box_type
+{
+ WRAP_BOX_PANGO,
+ WRAP_BOX_IMAGE
+};
+
+
+/* Possible types of space following a wrap box */
+enum wrap_box_space
+{
+ WRAP_SPACE_INTERWORD, /* Normal inter-word space */
+ WRAP_SPACE_NONE, /* Box ends with an explicit hyphen or an SC
+ * block boundary */
+ WRAP_SPACE_EOP /* End of paragraph */
+ /* TODO: Space between sentences etc */
+};
+
+
+/* A wrap box is a run of content - could be text, an image or so one - that is
+ * one logical unit as far as Colloquium is concerned. It might consist of
+ * multiple units, for example, in Pango's mind. */
+struct wrap_box
+{
+ enum wrap_box_type type;
+
+ /* Pango units */
+ int width;
+ int height;
+ int ascent;
+
+ enum wrap_box_space space; /* Type of "space" following box */
+
+ /* For type == WRAP_BOX_PANGO */
+ PangoGlyphString *glyphs;
+ PangoFont *font;
+ char *text;
+ double col[4]; /* rgba colour */
+
+ /* For type == WRAP_BOX_IMAGE */
+ /* Nothing yet */
+};
+
+
+struct wrap_line
+{
+ int width;
+ int height; /* Pango units */
+ int ascent; /* Pango units */
+
+ int n_boxes;
+ int max_boxes;
+ struct wrap_box *boxes;
+
+ size_t sc_offset;
+};
+
+
+extern int wrap_contents(struct frame *fr, PangoContext *pc);
+
+extern void get_cursor_pos(struct frame *fr, size_t pos,
+ double *xposd, double *yposd, double *line_height);
+
+
+#endif /* WRAP_H */