From 3087709f11b05b943c20436c9c64a09e09223208 Mon Sep 17 00:00:00 2001 From: Thomas White Date: Mon, 28 Mar 2016 17:03:01 +0200 Subject: Basic renderer using PangoLayouts --- src/wrap.c | 1032 ------------------------------------------------------------ 1 file changed, 1032 deletions(-) delete mode 100644 src/wrap.c (limited to 'src/wrap.c') diff --git a/src/wrap.c b/src/wrap.c deleted file mode 100644 index 6d0ea60..0000000 --- a/src/wrap.c +++ /dev/null @@ -1,1032 +0,0 @@ -/* - * wrap.c - * - * Text wrapping, hyphenation, justification etc - * - * Copyright © 2013-2016 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 -#include - -#include "sc_parse.h" -#include "sc_interp.h" -#include "wrap.h" -#include "frame.h" -#include "presentation.h" -#include "boxvec.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; -} - - -void initialise_line(struct wrap_line *l) -{ - l->width = 0; - l->height = 0; - l->ascent = 0; - l->last_line = 0; - l->overfull = 0; - l->boxes = bv_new(); -} - - -#if 0 -static struct wrap_line *get_cursor_line(struct frame *fr, size_t pos, - double *yposd) -{ - int line = 0; - int i; - int found = 0; - - *yposd = 0; - - for ( i=0; in_lines; i++ ) { - if ( fr->lines[i].sc_offset > pos ) { - line = i-1; - found = 1; - break; - } - } - if ( !found ) { - /* Cursor is on the last line */ - line = fr->n_lines-1; - } - if ( line < 0 ) { - printf("Couldn't find cursor.\n"); - return NULL; - } - for ( i=0; ilines[i].height; - } - - *yposd /= PANGO_SCALE; - *yposd += fr->pad_t; - - return &fr->lines[line]; -} -#endif - - -int which_segment(struct wrap_box *box, int pos, int *err) -{ - int i = 0; - int ch = 0; - - if ( box->type != WRAP_BOX_PANGO ) { - fprintf(stderr, "which_segment() called on wrong box type.\n"); - *err = 1; - return 0; - } - - do { - if ( ch + box->segs[i].len_chars >= pos ) break; - ch += box->segs[i++].len_chars; - } while ( i < box->n_segs ); - - if ( i == box->n_segs ) { - fprintf(stderr, "Position not found in box!\n"); - *err = 1; - return 0; - } - - *err = 0; - return i; -} - - -/* Return the horizontal position of "pos" within "box", in cairo units */ -static double text_box_index_to_x(struct wrap_box *box, int pos) -{ - double x = 0.0; - int nseg; - struct text_seg *seg; - const char *seg_text; - const char *ep; - int p; - int i; - int err; - ptrdiff_t offs_p; - - nseg = which_segment(box, pos, &err); - if ( err ) return 0.0; - - for ( i=0; isegs[i].glyphs, - box->font, NULL, &rect); - x += rect.width; - } - - /* We are in "seg" inside "box" */ - seg = &box->segs[nseg]; - seg_text = g_utf8_offset_to_pointer(sc_block_contents(box->scblock), - box->offs_char + seg->offs_char); - ep = g_utf8_offset_to_pointer(seg_text, seg->len_chars); - - /* Make character position relative to segment start (not box start) */ - pos -= seg->offs_char; - - /* Convert the character position to a byte offset */ - offs_p = g_utf8_offset_to_pointer(seg_text, pos) - seg_text; - - pango_glyph_string_index_to_x(seg->glyphs, (char *)seg_text, - ep - seg_text, &seg->analysis, - offs_p, FALSE, &p); - - return pango_units_to_double(x+p); -} - - -void get_cursor_pos(struct wrap_box *box, int pos, - double *xposd, double *yposd, double *line_height) -{ - *xposd = 0.0; - *yposd = 0.0; - *line_height = 20.0; - - if ( box == NULL ) return; - - *line_height = pango_units_to_double(box->height); - - if ( !box->editable ) { - *xposd += pango_units_to_double(box->width); - return; - } - - switch ( box->type ) { - - case WRAP_BOX_PANGO : - *xposd += text_box_index_to_x(box, pos); - break; - - case WRAP_BOX_IMAGE : - if ( pos > 0 ) { - *xposd += pango_units_to_double(box->width); - } /* else zero */ - break; - - case WRAP_BOX_CALLBACK : - if ( pos > 0 ) { - *xposd += pango_units_to_double(box->width); - } - break; - - case WRAP_BOX_NOTHING : - case WRAP_BOX_SENTINEL : - *xposd = 0.0; - break; - - } -} - - -static int find_cursor_line(struct frame *fr, double yposd, int *end) -{ - int i; - double y = fr->pad_t; - - *end = 0; - - for ( i=0; in_lines; i++ ) { - double height = pango_units_to_double(fr->lines[i].height); - if ( yposd < y + height ) { - return i; - } - y += height; - } - - *end = 1; - return fr->n_lines-1; -} - - -static int find_cursor_box(struct frame *fr, struct wrap_line *l, - double xposd, double *x_pos, int *end) -{ - int i; - double x = fr->pad_l; - - *end = 0; - - for ( i=0; iboxes->n_boxes; i++ ) { - double width = pango_units_to_double(bv_box(l->boxes, i)->width); - width += pango_units_to_double(bv_box(l->boxes, i)->sp); - if ( xposd < x + width ) { - *x_pos = xposd - x; - return i; - } - x += width; - } - - *end = 1; - *x_pos = x; - return bv_len(l->boxes)-1; -} - - -void find_cursor(struct frame *fr, double xposd, double yposd, - int *line, int *box, int *pos) -{ - struct wrap_line *l; - struct wrap_box *b; - int end; - double x_pos = 0.0; - int idx, trail; - int x_pos_i; - size_t offs; - int ln, bn; - const char *block_text; - const char *box_text; - - if ( fr->n_lines == 0 ) { - *line = 0; - *box = 0; - *pos = 0; - return; - } - - ln = find_cursor_line(fr, yposd, &end); - l = &fr->lines[ln]; - *line = ln; - - if ( end ) { - bn = l->boxes->n_boxes-1; - } else { - bn = find_cursor_box(fr, l, xposd, &x_pos, &end); - } - b = bv_box(l->boxes, bn); - *box = bn; - if ( end ) { - *pos = b->len_chars; - return; - } - - if ( !b->editable ) { - *pos = 0; - return; - } - - switch ( b->type ) { - - case WRAP_BOX_NOTHING: - case WRAP_BOX_SENTINEL: - case WRAP_BOX_IMAGE: - case WRAP_BOX_CALLBACK: - offs = 0; - break; - - case WRAP_BOX_PANGO: - x_pos_i = pango_units_from_double(x_pos); - if ( x_pos_i < b->width ) { - block_text = sc_block_contents(b->scblock); - box_text = g_utf8_offset_to_pointer(block_text, - b->offs_char); - printf("box text '%s'\n", box_text); - /* cast because this function is not const-clean */ - /* FIXME: Assumes one segment per box! */ - pango_glyph_string_x_to_index(b->segs[0].glyphs, - (char *)box_text, - strlen(box_text), - &b->segs[0].analysis, - x_pos_i, &idx, &trail); - offs = idx + trail; - /* FIXME: Bug in Pango? */ - if ( offs > b->len_chars ) offs = b->len_chars; - } else { - offs = 0; - }; - break; - - default: - fprintf(stderr, "find_cursor(): box %i\n", b->type); - offs = 0; - break; - - } - - *pos = offs; -} - - -static void calc_line_geometry(struct wrap_line *line) -{ - int i; - - line->width = 0; - line->ascent = 0; - line->height = 0; - - for ( i=0; iboxes->n_boxes; i++ ) { - - struct wrap_box *box = bv_box(line->boxes, i); - - line->width += box->width; - if ( box->space == WRAP_SPACE_EOP ) box->sp = 0.0; - line->width += box->sp; - if ( box->height > line->height ) line->height = box->height; - if ( box->ascent > line->ascent ) line->ascent = box->ascent; - - } - - line->height *= 1.07; -} - - -/* Normal width of space */ -static double sp_x(enum wrap_box_space s) -{ - switch ( s ) { - - case WRAP_SPACE_INTERWORD : - return 10.0*PANGO_SCALE; - - case WRAP_SPACE_EOP : - return 0.0; - - default: - case WRAP_SPACE_NONE : - return 0.0; - - } -} - - -/* Stretchability of space */ -static double sp_y(enum wrap_box_space s) -{ - switch ( s ) { - - case WRAP_SPACE_INTERWORD : - return 10.0*PANGO_SCALE; - - case WRAP_SPACE_EOP : - return INFINITY; - - default: - case WRAP_SPACE_NONE : - return 0.0; - - } -} - - -/* Shrinkability of space */ -static double sp_z(enum wrap_box_space s) -{ - switch ( s ) { - - case WRAP_SPACE_INTERWORD : - return 7.0*PANGO_SCALE; - - case WRAP_SPACE_EOP : - return 0.0; - - default: - case WRAP_SPACE_NONE : - return 0.0; - - } -} - - -/* Minimum width of space */ -static double sp_zp(enum wrap_box_space s) -{ - return sp_x(s) - sp_z(s); -} - - -/* Maximum width of space */ -static double sp_yp(enum wrap_box_space s, double rho) -{ - return sp_x(s) + rho*sp_y(s); -} - - -static void consider_break(double sigma_prime, double sigma_prime_max, - double sigma_prime_min, double line_length, - double *s, int j, double *dprime, int *jprime, - double rho) -{ - double r; - double d; - - if ( sigma_prime < line_length ) { - r = rho*(line_length - sigma_prime) - / (sigma_prime_max - sigma_prime); - } else if ( sigma_prime > line_length ) { - r = rho*(line_length - sigma_prime) - / (sigma_prime - sigma_prime_min); - } else { - r = 0.0; - } - - d = s[j] + pow(1.0 + 100.0 * pow(abs(r),3.0), 2.0); - if ( d < *dprime ) { - *dprime = d; - *jprime = j; - } -} - - -static double width(struct boxvec *boxes, int i) -{ - /* Indices in Knuth paper go from 1...n. Indices in array go - * from 0...n-1 */ - return bv_box(boxes, i-1)->width; -} - - -static enum wrap_box_space space(struct boxvec *boxes, int i) -{ - /* Indices in Knuth paper go from 1...n. Indices in array go - * from 0...n-1 */ - return bv_box(boxes, i-1)->space; -} - - -static void UNUSED distribute_spaces(struct wrap_line *line, double l, - double rho) -{ - int i; - double L, Y, Z, r; - int overfull = 0; - int underfull = 0; - - l = pango_units_from_double(l); - - L = 0.0; Y = 0.0; Z = 0.0; - for ( i=0; iboxes); i++ ) { - L += bv_box(line->boxes, i)->width; - L += sp_x(bv_box(line->boxes, i)->space); - Y += sp_y(bv_box(line->boxes, i)->space); - Z += sp_z(bv_box(line->boxes, i)->space); - } - L += bv_last(line->boxes)->width; - - if ( L < l ) { - r = (l - L)/Y; - } else if ( L > l ) { - r = (l - L)/Z; - } else { - r = 0.0; - } - - if ( r >= 0.0 ) { - for ( i=0; iboxes)-1; i++ ) { - bv_box(line->boxes, i)->sp = sp_x(bv_box(line->boxes, i)->space); - bv_box(line->boxes, i)->sp += r*sp_y(bv_box(line->boxes, i)->space); - } - } else { - for ( i=0; bv_len(line->boxes)-1; i++ ) { - bv_box(line->boxes, i)->sp = sp_x(bv_box(line->boxes, i)->space); - bv_box(line->boxes, i)->sp += r*sp_z(bv_box(line->boxes, i)->space); - } - } - - bv_box(line->boxes, bv_len(line->boxes)-1)->sp = 0.0; - line->overfull = overfull; - line->underfull = underfull; -} - - -static void output_line(int q, int s, struct frame *fr, struct boxvec *boxes) -{ - struct wrap_line *l; - int j; - - l = &fr->lines[fr->n_lines]; - fr->n_lines++; - initialise_line(l); - - bv_ensure_space(l->boxes, s-q); - for ( j=q; jboxes, bv_box(boxes, j)); - } -} - - -static void output(int a, int i, int *p, struct frame *fr, struct boxvec *boxes) -{ - int q = i; - int r; - int s = 0; - - if ( fr->n_lines + (i-a) + 1 > fr->max_lines ) { - fr->max_lines += 32; - alloc_lines(fr); - if ( fr->n_lines == fr->max_lines ) return; - } - - while ( q != a ) { - r = p[q]; - p[q] = s; - s = q; - q = r; - } - - while ( q != i ) { - - output_line(q, s, fr, boxes); - - q = s; - s = p[q]; - - } - -} - - -/* This is the "suboptimal fit" algorithm from Knuth and Plass, Software - - * Practice and Experience 11 (1981) p1119-1184. Despite the name, it's - * supposed to work as well as the full TeX algorithm in almost all of the cases - * that we care about here. */ -static void UNUSED knuth_suboptimal_fit(struct boxvec *boxes, - double line_length, struct frame *fr, - double rho) -{ - int a = 0; - int *p; - double *s; - struct wrap_box *box; - int j; - double sigma_prime, sigma_max_prime, sigma_min_prime; - double dprime; - int n; - int reject; - - n = boxes->n_boxes; - - /* Set the space for the last box to be "end of paragraph" */ - bv_last(boxes)->space = WRAP_SPACE_EOP; - - /* Add empty zero-width box at end */ - box = malloc(sizeof(struct wrap_box)); - if ( box== NULL ) { - fprintf(stderr, "Couldn't allocate sentinel box\n"); - return; - } - box->type = WRAP_BOX_SENTINEL; - box->space = WRAP_SPACE_NONE; - box->font = NULL; - box->width = 0; - box->ascent = 0; - box->height = 0; - box->editable = 1; - box->scblock = NULL; - box->offs_char = 0; - bv_add(boxes, box); - - line_length *= PANGO_SCALE; - - reject = 0; - for ( j=0; jwidth > line_length ) { - fprintf(stderr, "ERROR: Box %i too long (%i %f)\n", j, - bv_box(boxes, j)->width, line_length); - fr->trouble = 1; - reject = 1; - } - } - if ( reject ) return; - - p = malloc((n+2) * sizeof(int)); /* p[0]..p[n+1] inclusive */ - if ( p == NULL ) { - fprintf(stderr, "Failed to allocate p_k\n"); - return; - } - - s = malloc((n+2) * sizeof(double)); /* s[0]..s[n+1] inclusive */ - if ( s == NULL ) { - fprintf(stderr, "Failed to allocate s_k\n"); - return; - } - - do { - - int i = a; - int k = i+1; - double sigma = width(boxes, k); - double sigma_min = sigma; - double sigma_max = sigma; - int m = 1; - int jprime = 999; - - s[i] = 0; - - do { - - while ( sigma_min > line_length ) { - - /* Begin */ - if ( s[i] < INFINITY ) m--; - i++; - sigma -= width(boxes, i); - sigma -= sp_z(space(boxes, i)); - - sigma_max -= width(boxes, i); - sigma_max -= sp_yp(space(boxes, i), rho); - - sigma_min -= width(boxes, i); - sigma_min -= sp_zp(space(boxes, i)); - /* End */ - - } - - /* Begin */ - j = i; - sigma_prime = sigma; - sigma_max_prime = sigma_max; - sigma_min_prime = sigma_min; - dprime = INFINITY; - while ( sigma_max_prime >= line_length ) { - if ( s[j] < INFINITY ) { - consider_break(sigma_prime, - sigma_max_prime, - sigma_min_prime, - line_length, s, j, - &dprime, &jprime, - rho); - } - j++; - sigma_prime -= width(boxes, j); - sigma_prime -= sp_x(space(boxes, j)); - - sigma_max_prime -= width(boxes, j); - sigma_max_prime -= sp_yp(space(boxes, j), - rho); - - sigma_min_prime -= width(boxes, j); - sigma_min_prime -= sp_zp(space(boxes, j)); - } - /* End */ - - s[k] = dprime; - if ( dprime < INFINITY ) { - m++; - p[k] = jprime; - } - if ( (m == 0) || (k > n) ) break; - sigma += width(boxes, k+1); - sigma += sp_x(space(boxes, k)); - - sigma_max += width(boxes, k+1); - sigma_max += sp_yp(space(boxes, k), rho); - - sigma_min += width(boxes, k+1); - sigma_min += sp_zp(space(boxes, k)); - k++; - - } while ( 1 ); - - if ( k > n ) { - output(a, n+1, p, fr, boxes); - break; - } else { - - /* Begin */ - do { - - sigma += width(boxes, i); - sigma += sp_x(space(boxes, i)); - - sigma_max += width(boxes, i); - sigma_max += sp_yp(space(boxes, i), rho); - - sigma_min += width(boxes, i); - sigma_min += sp_zp(space(boxes, i)); - i--; - - } while ( s[i] >= INFINITY ); - output(a, i, p, fr, boxes); - - /* Begin */ - jprime = 0; - dprime = INFINITY; - /* Test hyphenation points here */ - /* End */ - - /* Begin */ - output_line(i, k-1, fr, boxes); - /* End */ - - /* End */ - - a = k-1; - - } - - } while ( 1 ); - - fr->lines[fr->n_lines-1].last_line = 1; - - free(p); - free(s); - free(box); /* The sentinel box */ -} - - -static struct wrap_line *new_line(struct frame *fr) -{ - struct wrap_line *l; - - if ( fr->n_lines + 1 > fr->max_lines ) { - fr->max_lines += 32; - alloc_lines(fr); - if ( fr->n_lines == fr->max_lines ) return NULL; - } - - l = &fr->lines[fr->n_lines]; - fr->n_lines++; - initialise_line(l); - return l; -} - - -static void first_fit(struct boxvec *boxes, double line_length, - struct frame *fr) -{ - struct wrap_line *line; - double len; - int j = 0; - - line_length *= PANGO_SCALE; - - line = new_line(fr); - len = 0.0; - - do { - - bv_box(boxes, j)->sp = sp_x(bv_box(boxes, j)->space); - - len += bv_box(boxes, j)->width; - - if ( len > line_length ) { - line = new_line(fr); - len = bv_box(boxes, j)->width; - } - bv_add(line->boxes, bv_box(boxes, j)); - j++; - - if ( (j > 0) && (bv_box(boxes, j-1)->type != WRAP_BOX_SENTINEL) ) - { - len += sp_x(bv_box(boxes, j-1)->space); - } - - } while ( j < boxes->n_boxes ); - - fr->lines[fr->n_lines-1].last_line = 1; -} - - -void wrap_line_free(struct wrap_line *l) -{ - bv_free(l->boxes); -} - - -static struct boxvec *split_paragraph(struct boxvec *boxes, int *n, int *eop) -{ - int i; - int start = *n; - int end; - - if ( start >= bv_len(boxes) ) return NULL; - - *eop = 0; - for ( i=start; ispace == WRAP_SPACE_EOP ) { - *eop = 1; - break; - } - } - end = i + 1; - *n = end; - if ( i == bv_len(boxes) ) end--; - - if ( end-start > 0 ) { - struct boxvec *para = bv_new(); - bv_ensure_space(para, end-start); - for ( i=start; iscblock != NULL ) { - const char *block_text = sc_block_contents(box->scblock); - box_text = g_utf8_offset_to_pointer(block_text, - box->offs_char); - } else if ( box->type == WRAP_BOX_IMAGE ) { - box_text = box->filename; - } else { - box_text = NULL; - } - printf(" t=%i s=%i %10i %5i %8.2f '%s'\n", - box->type, box->space, box->width, box->offs_char, - box->sp, box_text); - } -} - - -static int wrap_everything(struct frame *fr, double wrap_w) -{ - int i; - - fr->paragraph_start_lines = malloc(fr->n_paragraphs*sizeof(int)); - if ( fr->paragraph_start_lines == NULL ) { - fprintf(stderr, "Failed to allocate paragraph start lines\n"); - return 1; - } - - /* Split paragraphs into lines */ - fr->paragraph_start_lines[0] = 0; - for ( i=0; in_paragraphs; i++ ) { - - //int n; - - /* Choose wrapping algorithm here */ - //knuth_suboptimal_fit(para, wrap_w, fr, rho); - first_fit(fr->paragraphs[i], wrap_w, fr); - - //fr->paragraph_start_lines = n; - - } - - return 0; -} - - -/* Wrap the StoryCode inside "fr->sc" so that it fits within width "fr->w", - * and generate fr->lines */ -int wrap_contents(struct frame *fr) -{ - struct boxvec *para; - int i, eop = 0; - //const double rho = 2.0; - const double wrap_w = fr->w - fr->pad_l - fr->pad_r; - int max_para = 64; - - /* Clear lines */ - fr->n_lines = 0; - fr->max_lines = 32; - fr->lines = NULL; - fr->trouble = 0; - alloc_lines(fr); - - for ( i=0; in_paragraphs; i++ ) { - bv_free(fr->paragraphs[i]); - } - free(fr->paragraphs); - free(fr->paragraph_start_lines); - for ( i=0; in_lines; i++ ) { - wrap_line_free(&fr->lines[i]); - } - - /* Split text into paragraphs */ - i = 0; - fr->n_paragraphs = 0; - fr->paragraphs = malloc(max_para * sizeof(struct boxvec *)); - if ( fr->paragraphs == NULL ) { - fprintf(stderr, "Failed to allocate paragraphs\n"); - return 1; - } - do { - para = split_paragraph(fr->boxes, &i, &eop); - if ( para != NULL ) { - fr->paragraphs[fr->n_paragraphs++] = para; - } - if ( fr->n_paragraphs == max_para ) { - max_para += 64; - fr->paragraphs = realloc(fr->paragraphs, - max_para*sizeof(struct boxvec *)); - if ( fr->paragraphs == NULL ) { - fprintf(stderr, "Failed to allocate space" - " for paragraphs\n"); - return 1; - } - } - } while ( para != NULL ); - - if ( fr->n_paragraphs > 0 ) { - if ( wrap_everything(fr, wrap_w) ) return 1; - } else { - fr->paragraph_start_lines = NULL; - return 0; - } - - for ( i=0; in_lines; i++ ) { - - struct wrap_line *line = &fr->lines[i]; - - //distribute_spaces(line, wrap_w, rho); - - /* Strip any sentinel boxes added by the wrapping algorithm */ - if ( (bv_last(line->boxes) != NULL) - && (bv_last(line->boxes)->type == WRAP_BOX_SENTINEL) ) - { - line->boxes->n_boxes--; - } - - } - - for ( i=0; in_lines; i++ ) { - calc_line_geometry(&fr->lines[i]); - } - - return 0; -} - - -double total_height(struct frame *fr) -{ - int i; - double tot = 0.0; - - if ( fr == NULL ) return 0.0; - - for ( i=0; in_lines; i++ ) { - tot += fr->lines[i].height; - } - - return pango_units_to_double(tot); -} - - -/* Insert a new box, which will be at position 'pos' */ -int insert_box(struct wrap_line *l, int pos) -{ - int i; - -/* FIXME ! */ -#if 0 - if ( l->n_boxes == l->max_boxes ) { - l->max_boxes += 32; - if ( alloc_boxes(l) ) return 1; - } - - /* Shuffle the boxes along */ - for ( i=l->n_boxes; i>pos+1; i-- ) { - l->boxes[i] = l->boxes[i-1]; - } - - l->n_boxes++; -#endif -return 1; - - return 0; -} -- cgit v1.2.3