diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/frame.c | 29 | ||||
-rw-r--r-- | src/frame.h | 54 | ||||
-rw-r--r-- | src/render.c | 139 | ||||
-rw-r--r-- | src/sc_interp.c | 237 | ||||
-rw-r--r-- | src/sc_interp.h | 13 | ||||
-rw-r--r-- | src/sc_parse.c | 36 | ||||
-rw-r--r-- | src/sc_parse.h | 8 | ||||
-rw-r--r-- | src/shape.c | 234 | ||||
-rw-r--r-- | src/shape.h | 38 | ||||
-rw-r--r-- | src/wrap.c | 494 | ||||
-rw-r--r-- | src/wrap.h | 7 |
11 files changed, 626 insertions, 663 deletions
diff --git a/src/frame.c b/src/frame.c index 8dbbba2..eb0ce7a 100644 --- a/src/frame.c +++ b/src/frame.c @@ -60,7 +60,7 @@ struct frame *frame_new() n->num_children = 0; - n->sc = NULL; + n->scblocks = NULL; return n; } @@ -84,6 +84,7 @@ struct frame *add_subframe(struct frame *fr) } +#if 0 static LengthUnits get_units(const char *t) { size_t len = strlen(t); @@ -96,7 +97,7 @@ static LengthUnits get_units(const char *t) } -static void parse_option(struct frame *fr, const char *opt, StyleSheet *ss) +static void parse_option(struct frame *fr, const char *opt) { if ( (index(opt, 'x') != NULL) && (index(opt, '+') != NULL) && (index(opt, '+') != rindex(opt, '+')) ) @@ -152,29 +153,6 @@ static void parse_option(struct frame *fr, const char *opt, StyleSheet *ss) } } - - if ( strncmp(opt, "style=", 6) == 0 ) { - - char *s; - char *tmp; - - tmp = strdup(opt); - - if ( opt[strlen(opt)-1] == '*' ) { - fr->lop_from_style = 1; - tmp[strlen(tmp)-1] = '\0'; - } else { - fr->lop_from_style = 0; - } - - s = index(tmp, '=') + 1; - fr->style = lookup_style(ss, s); - free(tmp); - if ( fr->style == NULL ) { - fprintf(stderr, "Invalid style '%s'\n", opt); - return; - } - } } @@ -289,3 +267,4 @@ void show_hierarchy(struct frame *fr, const char *t) } } +#endif diff --git a/src/frame.h b/src/frame.h index f183c1b..8431836 100644 --- a/src/frame.h +++ b/src/frame.h @@ -1,7 +1,7 @@ /* * frame.h * - * Copyright © 2013 Thomas White <taw@bitwiz.org.uk> + * Copyright © 2013-2014 Thomas White <taw@bitwiz.org.uk> * * This file is part of Colloquium. * @@ -30,21 +30,7 @@ #include <pango/pango.h> #include <cairo.h> -typedef struct _stylesheet StyleSheet; - - -typedef enum -{ - DIR_NONE, - DIR_UL, - DIR_U, - DIR_UR, - DIR_R, - DIR_DR, - DIR_D, - DIR_DL, - DIR_L -} Direction; +#include "sc_parse.h" typedef enum @@ -54,36 +40,13 @@ typedef enum } LengthUnits; -struct layout_parameters -{ - double margin_l; - double margin_r; - double margin_t; - double margin_b; - - double pad_l; - double pad_r; - double pad_t; - double pad_b; - - double x; - double y; - - double w; - LengthUnits w_units; - double h; - LengthUnits h_units; -}; - - struct frame { struct frame **children; int num_children; int max_children; - char *sc; /* Storycode */ - size_t sc_len; /* Space allocated for sc */ + SCBlock *scblocks; int n_lines; int max_lines; @@ -91,28 +54,31 @@ struct frame size_t pos; - struct layout_parameters lop; - /* The rectangle allocated to this frame, determined by the renderer */ double x; double y; double w; double h; + double pad_t; + double pad_b; + double pad_l; + double pad_r; + /* True if this frame should be deleted on the next mouse click */ int empty; /* True if the aspect ratio of this frame should be maintained */ int is_image; - /* True if wrapping failed for this box */ + /* True if wrapping failed for this frame */ int trouble; }; extern struct frame *frame_new(void); extern struct frame *add_subframe(struct frame *fr); -extern struct frame *sc_unpack(const char *sc, StyleSheet *ss); + extern void show_hierarchy(struct frame *fr, const char *t); #endif /* FRAME_H */ diff --git a/src/render.c b/src/render.c index 6ea1afa..ce23f0c 100644 --- a/src/render.c +++ b/src/render.c @@ -36,6 +36,7 @@ #include <gdk/gdk.h> #include "sc_parse.h" +#include "sc_interp.h" #include "presentation.h" #include "frame.h" #include "render.h" @@ -185,23 +186,27 @@ static void render_boxes(struct wrap_line *line, cairo_t *cr, ImageStore *is, static void draw_overfull_marker(cairo_t *cr, struct frame *fr, int i) { +#if 0 cairo_move_to(cr, fr->w - fr->lop.pad_l- fr->lop.pad_r, 0.0); cairo_line_to(cr, fr->w - fr->lop.pad_l - fr->lop.pad_r, pango_units_to_double(fr->lines[i].height)); cairo_set_source_rgb(cr, 1.0, 0.0, 0.0); cairo_set_line_width(cr, 4.0); cairo_stroke(cr); +#endif } static void draw_underfull_marker(cairo_t *cr, struct frame *fr, int i) { +#if 0 cairo_move_to(cr, fr->w - fr->lop.pad_l- fr->lop.pad_r, 0.0); cairo_line_to(cr, fr->w - fr->lop.pad_l - fr->lop.pad_r, pango_units_to_double(fr->lines[i].height)); cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); cairo_set_line_width(cr, 4.0); cairo_stroke(cr); +#endif } @@ -250,47 +255,22 @@ static void render_lines(struct frame *fr, cairo_t *cr, ImageStore *is, } } +#if 0 + GdkRGBA col; -static void run_render_sc(cairo_t *cr, struct frame *fr, const char *sc) -{ - SCBlockList *bl; - SCBlockListIterator *iter; - struct scblock *b; - - bl = sc_find_blocks(sc, "bgcol"); - if ( bl == NULL ) return; - - for ( b = sc_block_list_first(bl, &iter); - b != NULL; - b = sc_block_list_next(bl, iter) ) - { - GdkRGBA col; - - if ( b->contents == NULL ) continue; - gdk_rgba_parse(&col, b->contents); - cairo_rectangle(cr, 0, 0, fr->w, fr->h); - gdk_cairo_set_source_rgba(cr, &col); - cairo_fill(cr); - - } - sc_block_list_free(bl); -} - + if ( b->contents == NULL ) continue; + gdk_rgba_parse(&col, b->contents); + cairo_rectangle(cr, 0, 0, fr->w, fr->h); + gdk_cairo_set_source_rgba(cr, &col); + cairo_fill(cr); +#endif +#if 0 /* Render Level 1 Storycode (no subframes) */ static int render_sc(cairo_t *cr, struct frame *fr, ImageStore *is, enum is_size isz, struct slide_constants *scc, struct presentation_constants *pcc, PangoContext *pc) { - int i; - - for ( i=0; i<fr->n_lines; i++ ) { - wrap_line_free(&fr->lines[i]); - } - free(fr->lines); - fr->lines = NULL; - fr->n_lines = 0; - fr->max_lines = 0; /* Set up lines */ if ( wrap_contents(fr, pc, scc, pcc) ) { @@ -313,6 +293,7 @@ static int render_sc(cairo_t *cr, struct frame *fr, ImageStore *is, return 0; } +#endif static int render_frame(cairo_t *cr, struct frame *fr, ImageStore *is, @@ -320,73 +301,29 @@ static int render_frame(cairo_t *cr, struct frame *fr, ImageStore *is, struct presentation_constants *pcc, PangoContext *pc) { + SCInterpreter *scin; int i; + SCBlock *bl = fr->scblocks; - /* Render all subframes */ - for ( i=0; i<fr->num_children; i++ ) { - - struct frame *ch = fr->children[i]; -#if 0 -/* Frame geometry calculation */ - - double mtot; - - if ( ch->style != NULL ) { - if ( ch->lop_from_style ) { - memcpy(&ch->lop, &ch->style->lop, - sizeof(struct layout_parameters)); - } else { - double x, y, w, h; - LengthUnits wu, hu; - x = ch->lop.x; y = ch->lop.y; - w = ch->lop.w; h = ch->lop.h; - wu = ch->lop.w_units; hu = ch->lop.h_units; - memcpy(&ch->lop, &ch->style->lop, - sizeof(struct layout_parameters)); - ch->lop.x = x; ch->lop.y = y; - ch->lop.w = w; ch->lop.h = h; - ch->lop.w_units = wu; ch->lop.h_units = hu; - } - } - - mtot = ch->lop.margin_l + ch->lop.margin_r; - mtot += fr->lop.pad_l + fr->lop.pad_r; - switch ( ch->lop.w_units ) { - - case UNITS_SLIDE : - ch->w = ch->lop.w; - break; - - case UNITS_FRAC : - ch->w = fr->w * ch->lop.w - mtot; - break; - - } - - mtot = ch->lop.margin_t + ch->lop.margin_b; - mtot += fr->lop.pad_t + fr->lop.pad_b; - switch ( ch->lop.h_units ) { - - case UNITS_SLIDE : - ch->h = ch->lop.h; - break; - - case UNITS_FRAC : - ch->h = fr->h * ch->lop.h - mtot; - break; + scin = sc_interp_new(); + if ( scin == NULL ) { + fprintf(stderr, "Failed to set up interpreter.\n"); + return 1; + } - } -#endif - ch->x = ch->lop.x + fr->lop.pad_l + ch->lop.margin_l; - ch->y = ch->lop.y + fr->lop.pad_t + ch->lop.margin_t; - cairo_save(cr); - cairo_translate(cr, ch->x, ch->y); - render_frame(cr, ch, is, isz, scc, pcc, pc); - cairo_restore(cr); + for ( i=0; i<fr->n_lines; i++ ) { + wrap_line_free(&fr->lines[i]); + } + free(fr->lines); + fr->lines = NULL; + fr->n_lines = 0; + fr->max_lines = 0; + while ( bl != NULL ) { + bl = sc_block_next(bl); } - render_sc(cr, fr, is, isz, scc, pcc, pc); + sc_interp_destroy(scin); return 0; } @@ -448,10 +385,8 @@ cairo_surface_t *render_slide(struct slide *s, int w, double ww, double hh, h = (hh/ww)*w; scale = w/ww; - s->top->lop.x = 0.0; - s->top->lop.y = 0.0; - s->top->lop.w = ww; - s->top->lop.h = hh; + s->top->x = 0.0; + s->top->y = 0.0; s->top->w = ww; s->top->h = hh; @@ -534,10 +469,8 @@ int export_pdf(struct presentation *p, const char *filename) cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); cairo_fill(cr); - s->top->lop.x = 0.0; - s->top->lop.y = 0.0; - s->top->lop.w = w; - s->top->lop.h = w*r; + s->top->x = 0.0; + s->top->y = 0.0; s->top->w = w; s->top->h = w*r; diff --git a/src/sc_interp.c b/src/sc_interp.c index 28182a9..65b113e 100644 --- a/src/sc_interp.c +++ b/src/sc_interp.c @@ -28,13 +28,246 @@ #include <assert.h> #include <stdlib.h> #include <string.h> +#include <pango/pangocairo.h> +#include <gdk/gdk.h> #include "sc_parse.h" #include "sc_interp.h" +#include "shape.h" +#include "wrap.h" struct _scinterp { - int dummy; + PangoContext *pc; + PangoLanguage *lang; + + struct slide_constants *s_constants; + struct presentation_constants *p_constants; + + struct sc_font *fontstack; + int n_fonts; + int max_fonts; }; -f
\ No newline at end of file + +static void set_font(SCInterpreter *scin, const char *font_name) +{ + PangoFontMetrics *metrics; + struct sc_font *scf; + + scf = &scin->fontstack[scin->n_fonts-1]; + + scf->fontdesc = pango_font_description_from_string(font_name); + if ( scf->fontdesc == NULL ) { + fprintf(stderr, "Couldn't describe font.\n"); + return; + } + scf->font = pango_font_map_load_font(pango_context_get_font_map(scin->pc), + scin->pc, scf->fontdesc); + if ( scf->font == NULL ) { + fprintf(stderr, "Couldn't load font.\n"); + return; + } + + /* FIXME: Language for box */ + metrics = pango_font_get_metrics(scf->font, NULL); + scf->ascent = pango_font_metrics_get_ascent(metrics); + scf->height = scf->ascent + pango_font_metrics_get_descent(metrics); + pango_font_metrics_unref(metrics); + + scf->free_font_on_pop = 1; +} + + +/* This sets the colour for the font at the top of the stack */ +static void set_colour(SCInterpreter *scin, const char *colour) +{ + GdkRGBA col; + struct sc_font *scf = &scin->fontstack[scin->n_fonts-1]; + + if ( colour == NULL ) { + printf("Invalid colour\n"); + scf->col[0] = 0.0; + scf->col[1] = 0.0; + scf->col[2] = 0.0; + scf->col[3] = 1.0; + return; + } + + gdk_rgba_parse(&col, colour); + + scf->col[0] = col.red; + scf->col[1] = col.green; + scf->col[2] = col.blue; + scf->col[3] = col.alpha; +} + + +static void copy_top_font(SCInterpreter *scin) +{ + if ( scin->n_fonts == scin->max_fonts ) { + + struct sc_font *stack_new; + + stack_new = realloc(scin->fontstack, sizeof(struct sc_font) + * ((scin->max_fonts)+8)); + if ( stack_new == NULL ) { + fprintf(stderr, "Failed to push font or colour.\n"); + return; + } + + scin->fontstack = stack_new; + scin->max_fonts += 8; + + } + + /* When n_fonts=0, we leave the first font uninitialised. This allows + * the stack to be "bootstrapped", but requires the first caller to do + * set_font and set_colour straight away. */ + if ( scin->n_fonts > 0 ) { + scin->fontstack[scin->n_fonts] = scin->fontstack[scin->n_fonts-1]; + } + + /* This is a copy, so don't free it later */ + scin->fontstack[scin->n_fonts].free_font_on_pop = 0; + + scin->n_fonts++; +} + + +static void push_font(SCInterpreter *scin, const char *font_name) +{ + copy_top_font(scin); + set_font(scin, font_name); +} + + +static void push_colour(SCInterpreter *scin, const char *colour) +{ + copy_top_font(scin); + set_colour(scin, colour); +} + + +static void pop_font_or_colour(SCInterpreter *scin) +{ + struct sc_font *scf = &scin->fontstack[scin->n_fonts-1]; + + if ( scf->free_font_on_pop ) { + pango_font_description_free(scf->fontdesc); + } + + scin->n_fonts--; +} + + +SCInterpreter *sc_interp_new() +{ + SCInterpreter *scin; + + scin = malloc(sizeof(SCInterpreter)); + if ( scin == NULL ) return NULL; + + scin->fontstack = NULL; + scin->n_fonts = 0; + scin->max_fonts = 0; + + scin->pc = NULL; + scin->s_constants = NULL; + scin->p_constants = NULL; + + /* FIXME: Determine proper language (somehow...) */ + scin->lang = pango_language_from_string("en_GB"); + + /* The "ultimate" default font */ + push_font(scin, "Sans 12"); + set_colour(scin, "#000000"); + + return scin; +} + + +void sc_interp_destroy(SCInterpreter *scin) +{ + /* Empty the stack */ + while ( scin->n_fonts > 0 ) { + pop_font_or_colour(scin); + } + + free(scin); +} + + +int sc_interp_add_blocks(SCInterpreter *scin, const SCBlock *bl) +{ + struct wrap_line *boxes; + + boxes = malloc(sizeof(struct wrap_line)); + if ( boxes == NULL ) { + fprintf(stderr, "Failed to allocate boxes.\n"); + return 1; + } + initialise_line(boxes); + + while ( bl != NULL ) { + + const char *name = sc_block_name(bl); + const char *contents = sc_block_contents(bl); + + if ( name == NULL ) { + split_words(boxes, scin->pc, contents, + scin->lang, 1, + &scin->fontstack[scin->n_fonts-1]); + +#if 0 + /* FIXME ... ! */ + + } else if ( (strcmp(name, "font")==0) && (contents == NULL) ) { + set_font(fonts, b->options, pc); + + } else if ( (strcmp(name, "font")==0) && (contents != NULL) ) { + push_font(fonts, b->options, pc); + run_sc(b->contents, fonts, pc, boxes, lang, offset, + editable, fr, slide_constants, + presentation_constants); + pop_font_or_colour(fonts); + + } else if ( (strcmp(name, "fgcol")==0) + && (contents == NULL) ) { + set_colour(fonts, b->options); + + } else if ( (strcmp(name, "fgcol")==0) && (contents != NULL) ) { + push_colour(fonts, b->options); + run_sc(b->contents, fonts, pc, boxes, lang, offset, + editable, fr, slide_constants, + presentation_constants); + pop_font_or_colour(fonts); + + } else if ( (strcmp(name, "image")==0) + && (contents != NULL) && (b->options != NULL) ) { + int w, h; + if ( get_size(b->options, fr, &w, &h) == 0 ) { + add_image_box(boxes, b->contents, offset, w, h, + editable); + } + + } else if ( strcmp(name, "slidenumber")==0) { + char *tmp = malloc(64); + if ( tmp != NULL ) { + snprintf(tmp, 63, "%i", + slide_constants->slide_number); + add_wrap_box(boxes, tmp, offset, + WRAP_SPACE_NONE, pc, + &fonts->stack[fonts->n_fonts-1], + 0); + } /* else go away and sulk about it */ +#endif + + } + + bl = sc_block_next(bl); + + } + + return 0; +} diff --git a/src/sc_interp.h b/src/sc_interp.h index b5fba5e..ad59193 100644 --- a/src/sc_interp.h +++ b/src/sc_interp.h @@ -27,6 +27,17 @@ #include <config.h> #endif + +struct sc_font +{ + PangoFontDescription *fontdesc; + PangoFont *font; + double col[4]; + int ascent; + int height; + int free_font_on_pop; +}; + typedef struct _scinterp SCInterpreter; extern SCInterpreter *sc_interp_new(void); @@ -35,6 +46,6 @@ extern void sc_interp_destroy(SCInterpreter *scin); extern void sc_interp_save(SCInterpreter *scin); extern void sc_interp_restore(SCInterpreter *scin); -extern void sc_interp_run(SCInterpreter *scin, const char *sc); +extern int sc_interp_add_blocks(SCInterpreter *scin, const SCBlock *bl); #endif /* SC_INTERP_H */ diff --git a/src/sc_parse.c b/src/sc_parse.c index bb2a5f5..69755c7 100644 --- a/src/sc_parse.c +++ b/src/sc_parse.c @@ -56,6 +56,30 @@ SCBlock *sc_block_new() } +SCBlock *sc_block_next(const SCBlock *bl) +{ + return bl->next; +} + + +const char *sc_block_name(const SCBlock *bl) +{ + return bl->name; +} + + +const char *sc_block_options(const SCBlock *bl) +{ + return bl->options; +} + + +const char *sc_block_contents(const SCBlock *bl) +{ + return bl->contents; +} + + /* Insert a new block after "bl". "name", "options" and "contents" * will not be copied. Returns the block just created, or NULL on error. * If *blfp points to NULL, it will updated to point at the new block. */ @@ -102,7 +126,7 @@ void sc_block_free(SCBlock *bl) } -static void recursive_show_sc_blocks(const char *prefix, SCBlock *bl) +static void recursive_show_sc_blocks(const char *prefix, const SCBlock *bl) { while ( bl != NULL ) { @@ -125,7 +149,7 @@ static void recursive_show_sc_blocks(const char *prefix, SCBlock *bl) } -void show_sc_blocks(SCBlock *bl) +void show_sc_blocks(const SCBlock *bl) { recursive_show_sc_blocks("", bl); } @@ -263,6 +287,14 @@ SCBlock *sc_parse(const char *sc) char *opt = NULL; char *contents = NULL; + /* Is this an escaped backslash? */ + if ( sc[i+1] == '\\' ) { + tbuf[j++] = '\\'; + i += 2; /* Skip both backslashes */ + continue; + } + + /* No, it's a real block. Dispatch the previous block */ if ( j != 0 ) { tbuf[j] = '\0'; bl = sc_block_append(bl, NULL, NULL, diff --git a/src/sc_parse.h b/src/sc_parse.h index 23bba89..ac4ce35 100644 --- a/src/sc_parse.h +++ b/src/sc_parse.h @@ -30,8 +30,14 @@ typedef struct _scblock SCBlock; extern SCBlock *sc_parse(const char *sc); + extern void sc_block_free(SCBlock *bl); -extern void show_sc_blocks(SCBlock *bl); +extern SCBlock *sc_block_next(const SCBlock *bl); +extern const char *sc_block_name(const SCBlock *bl); +extern const char *sc_block_options(const SCBlock *bl); +extern const char *sc_block_contents(const SCBlock *bl); + +extern void show_sc_blocks(const SCBlock *bl); #endif /* SC_PARSE_H */ diff --git a/src/shape.c b/src/shape.c new file mode 100644 index 0000000..93625e0 --- /dev/null +++ b/src/shape.c @@ -0,0 +1,234 @@ +/* + * shape.c + * + * Copyright © 2014 Thomas White <taw@bitwiz.org.uk> + * + * 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 <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 "wrap.h" +#include "sc_interp.h" +#include "shape.h" + + +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(); + box->item = item; + + 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); + } +} + + +/* Add "text", followed by a space of type "space", to "line" */ +static int add_wrap_box(struct wrap_line *line, char *text, size_t offset, + enum wrap_box_space space, PangoContext *pc, + struct sc_font *font, int editable) +{ + GList *pango_items; + struct wrap_box *box; + PangoAttrList *attrs; + PangoAttribute *attr; + + 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]; + if ( !editable ) offset = 0; + box->sc_offset = offset; + box->type = WRAP_BOX_PANGO; + box->text = text; + box->space = space; + box->font = font->font; + box->width = 0; + box->editable = editable; + box->ascent = font->ascent; + box->height = font->height; + box->col[0] = font->col[0]; /* Red */ + box->col[1] = font->col[1]; /* Green */ + box->col[2] = font->col[2]; /* Blue */ + box->col[3] = font->col[3]; /* Alpha */ + line->n_boxes++; + + if ( strlen(text) == 0 ) { + box->type = WRAP_BOX_NOTHING; + return 0; + } + + attrs = pango_attr_list_new(); + attr = pango_attr_font_desc_new(font->fontdesc); + pango_attr_list_insert_before(attrs, attr); + pango_items = pango_itemize(pc, text, 0, strlen(text), attrs, NULL); + g_list_foreach(pango_items, shape_and_measure, box); + g_list_free(pango_items); + pango_attr_list_unref(attrs); + + return 0; +} + + +static void add_image_box(struct wrap_line *line, const char *filename, + size_t offset, int w, int h, int editable) +{ + struct wrap_box *box; + + box = &line->boxes[line->n_boxes]; + if ( !editable ) offset = 0; + box->sc_offset = offset; + box->type = WRAP_BOX_IMAGE; + box->text = NULL; + 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; + line->n_boxes++; +} + + +int split_words(struct wrap_line *boxes, PangoContext *pc, const char *text, + PangoLanguage *lang, int editable, struct sc_font *font) +{ + PangoLogAttr *log_attrs; + glong len_chars, i; + size_t len_bytes, start; + + /* Empty block? */ + if ( text == NULL ) return 1; + + len_chars = g_utf8_strlen(text, -1); + if ( len_chars == 0 ) return 1; + + len_bytes = strlen(text); + + log_attrs = malloc((len_chars+1)*sizeof(PangoLogAttr)); + if ( log_attrs == NULL ) return 1; + + /* Create glyph string */ + pango_get_log_attrs(text, len_bytes, -1, lang, log_attrs, len_chars+1); + + start = 0; + for ( i=0; i<len_chars; i++ ) { + + if ( log_attrs[i].is_line_break ) { + + char *word; + enum wrap_box_space type; + size_t len; + char *ptr; + size_t offs; + + ptr = g_utf8_offset_to_pointer(text, i); + offs = ptr - text; + + /* Stuff up to (but not including) sc[i] forms a + * wrap box */ + len = offs - start; + if ( log_attrs[i].is_mandatory_break ) { + type = WRAP_SPACE_EOP; + if ( (offs>0) && (text[offs-1] == '\n') ) len--; + if ( (offs>0) && (text[offs-1] == '\r') ) len--; + } else if ( (i>0) + && log_attrs[i-1].is_expandable_space ) { + type = WRAP_SPACE_INTERWORD; + len--; /* Not interested in spaces */ + } else { + type = WRAP_SPACE_NONE; + } + + word = strndup(text+start, len); + if ( word == NULL ) { + fprintf(stderr, "strndup() failed.\n"); + free(log_attrs); + return 1; + } + + if ( add_wrap_box(boxes, word, start, type, + pc, font, editable) ) { + fprintf(stderr, "Failed to add wrap box.\n"); + } + start = offs; + + } + + } + if ( i > start ) { + + char *word; + size_t l; + + word = strdup(text+start); /* to the end */ + if ( word == NULL ) { + fprintf(stderr, "strndup() failed.\n"); + free(log_attrs); + return 1; + } + l = strlen(word); + + if ( (word[l-1] == '\n') ) { + + /* There is a newline at the end of the SC */ + char *word2; + + word2 = strndup(word, l-1); + add_wrap_box(boxes, word2, start, + WRAP_SPACE_EOP, pc, font, editable); + add_wrap_box(boxes, strdup(""), len_bytes, + WRAP_SPACE_NONE, pc, font, editable); + + } else { + + add_wrap_box(boxes, word, start, + WRAP_SPACE_NONE, pc, font, editable); + + } + + } + + free(log_attrs); + return 0; +} diff --git a/src/shape.h b/src/shape.h new file mode 100644 index 0000000..3e218f4 --- /dev/null +++ b/src/shape.h @@ -0,0 +1,38 @@ +/* + * shape.h + * + * Copyright © 2014 Thomas White <taw@bitwiz.org.uk> + * + * 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 <http://www.gnu.org/licenses/>. + * + */ + +#ifndef SHAPE_H +#define SHAPE_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pango/pangocairo.h> + +#include "wrap.h" + +extern int split_words(struct wrap_line *boxes, PangoContext *pc, + const char *text, PangoLanguage *lang, int editable, + struct sc_font *font); + +#endif /* SHAPE_H */ @@ -3,7 +3,7 @@ * * Text wrapping, hyphenation, justification and shaping * - * Copyright © 2013 Thomas White <taw@bitwiz.org.uk> + * Copyright © 2013-2014 Thomas White <taw@bitwiz.org.uk> * * This file is part of Colloquium. * @@ -35,6 +35,7 @@ #include <gdk/gdk.h> #include "sc_parse.h" +#include "sc_interp.h" #include "wrap.h" #include "frame.h" #include "presentation.h" @@ -54,7 +55,7 @@ static void alloc_lines(struct frame *fr) } -static void alloc_boxes(struct wrap_line *l) +void alloc_boxes(struct wrap_line *l) { struct wrap_box *boxes_new; @@ -68,7 +69,7 @@ static void alloc_boxes(struct wrap_line *l) } -static void initialise_line(struct wrap_line *l) +void initialise_line(struct wrap_line *l) { l->n_boxes = 0; l->max_boxes = 32; @@ -112,7 +113,7 @@ static struct wrap_line *get_cursor_line(struct frame *fr, size_t pos, } *yposd /= PANGO_SCALE; - *yposd += fr->lop.pad_t; + *yposd += fr->pad_t; return &fr->lines[line]; } @@ -146,7 +147,7 @@ void get_cursor_pos(struct frame *fr, size_t pos, } box--; - *xposd = fr->lop.pad_l; + *xposd = fr->pad_l; for ( i=0; i<box; i++ ) { *xposd += pango_units_to_double(l->boxes[i].width); *xposd += pango_units_to_double(l->boxes[i].sp); @@ -181,7 +182,7 @@ static struct wrap_line *find_cursor_line(struct frame *fr, double yposd, int *end) { int i; - double y = fr->lop.pad_t; + double y = fr->pad_t; *end = 0; @@ -202,7 +203,7 @@ static struct wrap_box *find_cursor_box(struct frame *fr, struct wrap_line *l, double xposd, double *x_pos, int *end) { int i; - double x = fr->lop.pad_l; + double x = fr->pad_l; *end = 0; @@ -269,31 +270,6 @@ size_t find_cursor_pos(struct frame *fr, double xposd, double yposd) } -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(); - box->item = item; - - 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; @@ -313,313 +289,6 @@ static void calc_line_geometry(struct wrap_line *line) } -struct sc_font -{ - PangoFontDescription *fontdesc; - PangoFont *font; - double col[4]; - int ascent; - int height; - int free_font_on_pop; -}; - - -/* Add "text", followed by a space of type "space", to "line" */ -static int add_wrap_box(struct wrap_line *line, char *text, size_t offset, - enum wrap_box_space space, PangoContext *pc, - struct sc_font *font, int editable) -{ - GList *pango_items; - struct wrap_box *box; - PangoAttrList *attrs; - PangoAttribute *attr; - - 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]; - if ( !editable ) offset = 0; - box->sc_offset = offset; - box->type = WRAP_BOX_PANGO; - box->text = text; - box->space = space; - box->font = font->font; - box->width = 0; - box->editable = editable; - box->ascent = font->ascent; - box->height = font->height; - box->col[0] = font->col[0]; /* Red */ - box->col[1] = font->col[1]; /* Green */ - box->col[2] = font->col[2]; /* Blue */ - box->col[3] = font->col[3]; /* Alpha */ - line->n_boxes++; - - if ( strlen(text) == 0 ) { - box->type = WRAP_BOX_NOTHING; - return 0; - } - - attrs = pango_attr_list_new(); - attr = pango_attr_font_desc_new(font->fontdesc); - pango_attr_list_insert_before(attrs, attr); - pango_items = pango_itemize(pc, text, 0, strlen(text), attrs, NULL); - g_list_foreach(pango_items, shape_and_measure, box); - g_list_free(pango_items); - pango_attr_list_unref(attrs); - - return 0; -} - - -static void add_image_box(struct wrap_line *line, const char *filename, - size_t offset, int w, int h, int editable) -{ - struct wrap_box *box; - - box = &line->boxes[line->n_boxes]; - if ( !editable ) offset = 0; - box->sc_offset = offset; - box->type = WRAP_BOX_IMAGE; - box->text = NULL; - 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; - line->n_boxes++; -} - - -static int split_words(struct wrap_line *boxes, PangoContext *pc, char *sc, - size_t sc_offset, PangoLanguage *lang, int editable, - struct sc_font *font) -{ - PangoLogAttr *log_attrs; - glong len_chars, i; - size_t len_bytes, start; - - /* Empty block? */ - if ( sc == NULL ) return 1; - - len_chars = g_utf8_strlen(sc, -1); - if ( len_chars == 0 ) return 1; - - len_bytes = strlen(sc); - - log_attrs = malloc((len_chars+1)*sizeof(PangoLogAttr)); - if ( log_attrs == NULL ) return 1; - - /* Create glyph string */ - pango_get_log_attrs(sc, len_bytes, -1, lang, log_attrs, len_chars+1); - - start = 0; - for ( i=0; i<len_chars; i++ ) { - - if ( log_attrs[i].is_line_break ) { - - char *word; - enum wrap_box_space type; - size_t len; - char *ptr; - size_t offs; - - ptr = g_utf8_offset_to_pointer(sc, i); - offs = ptr - sc; - - /* Stuff up to (but not including) sc[i] forms a - * wrap box */ - len = offs - start; - if ( log_attrs[i].is_mandatory_break ) { - type = WRAP_SPACE_EOP; - if ( (offs>0) && (sc[offs-1] == '\n') ) len--; - if ( (offs>0) && (sc[offs-1] == '\r') ) len--; - } else if ( (i>0) - && log_attrs[i-1].is_expandable_space ) { - type = WRAP_SPACE_INTERWORD; - len--; /* Not interested in spaces */ - } else { - type = WRAP_SPACE_NONE; - } - - word = strndup(sc+start, len); - if ( word == NULL ) { - fprintf(stderr, "strndup() failed.\n"); - free(log_attrs); - return 1; - } - - if ( add_wrap_box(boxes, word, start+sc_offset, type, - pc, font, editable) ) { - fprintf(stderr, "Failed to add wrap box.\n"); - } - start = offs; - - } - - } - if ( i > start ) { - - char *word; - size_t l; - - word = strdup(sc+start); /* to the end */ - if ( word == NULL ) { - fprintf(stderr, "strndup() failed.\n"); - free(log_attrs); - return 1; - } - l = strlen(word); - - if ( (word[l-1] == '\n') ) { - - /* There is a newline at the end of the SC */ - char *word2; - - word2 = strndup(word, l-1); - add_wrap_box(boxes, word2, start+sc_offset, - WRAP_SPACE_EOP, pc, font, editable); - add_wrap_box(boxes, strdup(""), len_bytes+sc_offset, - WRAP_SPACE_NONE, pc, font, editable); - - } else { - - add_wrap_box(boxes, word, start+sc_offset, - WRAP_SPACE_NONE, pc, font, editable); - - } - - } - - free(log_attrs); - return 0; -} - - -struct sc_font_stack -{ - struct sc_font *stack; - int n_fonts; - int max_fonts; -}; - - -static void set_font(struct sc_font_stack *stack, const char *font_name, - PangoContext *pc) -{ - PangoFontMetrics *metrics; - struct sc_font *scf; - - scf = &stack->stack[stack->n_fonts-1]; - - scf->fontdesc = pango_font_description_from_string(font_name); - if ( scf->fontdesc == NULL ) { - fprintf(stderr, "Couldn't describe font.\n"); - return; - } - scf->font = pango_font_map_load_font(pango_context_get_font_map(pc), - pc, scf->fontdesc); - if ( scf->font == NULL ) { - fprintf(stderr, "Couldn't load font.\n"); - return; - } - - /* FIXME: Language for box */ - metrics = pango_font_get_metrics(scf->font, NULL); - scf->ascent = pango_font_metrics_get_ascent(metrics); - scf->height = scf->ascent + pango_font_metrics_get_descent(metrics); - pango_font_metrics_unref(metrics); - - scf->free_font_on_pop = 1; -} - - -/* This sets the colour for the font at the top of the stack */ -static void set_colour(struct sc_font_stack *stack, const char *colour) -{ - GdkRGBA col; - struct sc_font *scf = &stack->stack[stack->n_fonts-1]; - - if ( colour == NULL ) { - printf("Invalid colour\n"); - scf->col[0] = 0.0; - scf->col[1] = 0.0; - scf->col[2] = 0.0; - scf->col[3] = 1.0; - return; - } - - gdk_rgba_parse(&col, colour); - - scf->col[0] = col.red; - scf->col[1] = col.green; - scf->col[2] = col.blue; - scf->col[3] = col.alpha; -} - - -static void copy_top_font(struct sc_font_stack *stack) -{ - if ( stack->n_fonts == stack->max_fonts ) { - - struct sc_font *stack_new; - - stack_new = realloc(stack->stack, sizeof(struct sc_font) - * ((stack->max_fonts)+8)); - if ( stack_new == NULL ) { - fprintf(stderr, "Failed to push font or colour.\n"); - return; - } - - stack->stack = stack_new; - stack->max_fonts += 8; - - } - - /* When n_fonts=0, we leave the first font uninitialised. This allows - * the stack to be "bootstrapped", but requires the first caller to do - * set_font and set_colour straight away. */ - if ( stack->n_fonts > 0 ) { - stack->stack[stack->n_fonts] = stack->stack[stack->n_fonts-1]; - } - - /* This is a copy, so don't free it later */ - stack->stack[stack->n_fonts].free_font_on_pop = 0; - - stack->n_fonts++; -} - - -static void push_font(struct sc_font_stack *stack, const char *font_name, - PangoContext *pc) -{ - copy_top_font(stack); - set_font(stack, font_name, pc); -} - - -static void push_colour(struct sc_font_stack *stack, const char *colour) -{ - copy_top_font(stack); - set_colour(stack, colour); -} - - -static void pop_font_or_colour(struct sc_font_stack *stack) -{ - struct sc_font *scf = &stack->stack[stack->n_fonts-1]; - - if ( scf->free_font_on_pop ) { - pango_font_description_free(scf->fontdesc); - } - - stack->n_fonts--; -} - - static int get_size(const char *a, struct frame *fr, int *wp, int *hp) { char *x; @@ -635,12 +304,12 @@ static int get_size(const char *a, struct frame *fr, int *wp, int *hp) hs = strdup(x+1); if ( strcmp(ws, "fit") == 0 ) { - *wp = fr->w - (fr->lop.pad_l+fr->lop.pad_r); + *wp = fr->w - (fr->pad_l+fr->pad_r); } else { *wp = strtoul(ws, NULL, 10); } if ( strcmp(ws, "fit") == 0 ) { - *hp = fr->h - (fr->lop.pad_t+fr->lop.pad_b); + *hp = fr->h - (fr->pad_t+fr->pad_b); } else { *hp = strtoul(hs, NULL, 10); } @@ -656,128 +325,6 @@ invalid: } -static void run_sc(const char *sc, struct sc_font_stack *fonts, - PangoContext *pc, struct wrap_line *boxes, - PangoLanguage *lang, size_t g_offset, int editable, - struct frame *fr, struct slide_constants *slide_constants, - struct presentation_constants *presentation_constants) -{ - SCBlockList *bl; - SCBlockListIterator *iter; - struct scblock *b; - - bl = sc_find_blocks(sc, NULL); - - if ( bl == NULL ) { - printf("Failed to find blocks.\n"); - return; - } - - for ( b = sc_block_list_first(bl, &iter); - b != NULL; - b = sc_block_list_next(bl, iter) ) - { - size_t offset = b->offset + g_offset; - - if ( b->name == NULL ) { - split_words(boxes, pc, b->contents, offset, - lang, editable, - &fonts->stack[fonts->n_fonts-1]); - - } else if ( (strcmp(b->name, "font")==0) - && (b->contents == NULL) ) { - set_font(fonts, b->options, pc); - - } else if ( (strcmp(b->name, "font")==0) - && (b->contents != NULL) ) { - push_font(fonts, b->options, pc); - run_sc(b->contents, fonts, pc, boxes, lang, offset, - editable, fr, slide_constants, - presentation_constants); - pop_font_or_colour(fonts); - - } else if ( (strcmp(b->name, "fgcol")==0) - && (b->contents == NULL) ) { - set_colour(fonts, b->options); - - } else if ( (strcmp(b->name, "fgcol")==0) - && (b->contents != NULL) ) { - push_colour(fonts, b->options); - run_sc(b->contents, fonts, pc, boxes, lang, offset, - editable, fr, slide_constants, - presentation_constants); - pop_font_or_colour(fonts); - - } else if ( (strcmp(b->name, "image")==0) - && (b->contents != NULL) && (b->options != NULL) ) { - int w, h; - if ( get_size(b->options, fr, &w, &h) == 0 ) { - add_image_box(boxes, b->contents, offset, w, h, - editable); - } - - } else if ( strcmp(b->name, "slidenumber")==0) { - char *tmp = malloc(64); - if ( tmp != NULL ) { - snprintf(tmp, 63, "%i", - slide_constants->slide_number); - add_wrap_box(boxes, tmp, offset, - WRAP_SPACE_NONE, pc, - &fonts->stack[fonts->n_fonts-1], - 0); - } /* else go away and sulk about it */ - } - - } - sc_block_list_free(bl); -} - - -static struct wrap_line *sc_to_wrap_boxes(const char *sc, const char *prefix, - PangoContext *pc, struct frame *fr, - struct slide_constants *s_constants, - struct presentation_constants *p_constants) -{ - struct wrap_line *boxes; - PangoLanguage *lang; - struct sc_font_stack fonts; - - boxes = malloc(sizeof(struct wrap_line)); - if ( boxes == NULL ) { - fprintf(stderr, "Failed to allocate boxes.\n"); - return NULL; - } - initialise_line(boxes); - - fonts.stack = NULL; - fonts.n_fonts = 0; - fonts.max_fonts = 0; - - /* FIXME: Determine proper language (somehow...) */ - lang = pango_language_from_string("en_GB"); - - /* The "ultimate" default font */ - push_font(&fonts, "Sans 12", pc); - set_colour(&fonts, "#000000"); - - if ( prefix != NULL ) { - run_sc(prefix, &fonts, pc, boxes, lang, 0, 0, fr, - s_constants, p_constants); - } - if ( sc != NULL ) { - run_sc(sc, &fonts, pc, boxes, lang, 0, 1, fr, - s_constants, p_constants); - } - - /* Empty the stack */ - while ( fonts.n_fonts > 0 ) { - pop_font_or_colour(&fonts); - } - - return boxes; -} - - /* Normal width of space */ static double sp_x(enum wrap_box_space s) { @@ -1256,29 +803,12 @@ void show_boxes(struct wrap_line *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 slide_constants *scc, - struct presentation_constants *pcc) +int wrap_contents(struct frame *fr, struct wrap_line *boxes) { - struct wrap_line *boxes; struct wrap_line *para; int i; const double rho = 2.0; - const double wrap_w = fr->w - fr->lop.pad_l - fr->lop.pad_r; - char *prologue; - - if ( fr->style != NULL ) { - prologue = fr->style->sc_prologue; - } else { - prologue = NULL; - } - - /* Turn the StoryCode into wrap boxes, all on one line */ - boxes = sc_to_wrap_boxes(fr->sc, prologue, pc, fr, scc, pcc); - if ( boxes == NULL ) { - fprintf(stderr, "Failed to create wrap boxes.\n"); - return 1; - } + const double wrap_w = fr->w - fr->pad_l - fr->pad_r; /* Clear lines */ fr->n_lines = 0; @@ -99,15 +99,16 @@ struct wrap_line }; -extern int wrap_contents(struct frame *fr, PangoContext *pc, - struct slide_constants *scc, - struct presentation_constants *pcc); +extern int wrap_contents(struct frame *fr, struct wrap_line *boxes); extern void get_cursor_pos(struct frame *fr, size_t pos, double *xposd, double *yposd, double *line_height); extern size_t find_cursor_pos(struct frame *fr, double xposd, double yposd); +extern void alloc_boxes(struct wrap_line *l); +extern void initialise_line(struct wrap_line *l); + extern void wrap_line_free(struct wrap_line *l); extern void show_boxes(struct wrap_line *boxes); |