diff options
author | Paul Gardiner <paul@glidos.net> | 2012-06-12 13:35:51 +0100 |
---|---|---|
committer | Paul Gardiner <paul@glidos.net> | 2012-06-12 13:35:51 +0100 |
commit | 97ca857023f9c8b4f9dedabaf710bc575664cd3e (patch) | |
tree | 843d7c321c791d27c242344f90de203d60121524 /pdf/pdf_form.c | |
parent | 5d59fc757417e4dbd4eae0bb5a152adfb87afbe5 (diff) | |
download | mupdf-97ca857023f9c8b4f9dedabaf710bc575664cd3e.tar.xz |
Forms: improved handling of text scaling in multiline text widgets
Diffstat (limited to 'pdf/pdf_form.c')
-rw-r--r-- | pdf/pdf_form.c | 199 |
1 files changed, 126 insertions, 73 deletions
diff --git a/pdf/pdf_form.c b/pdf/pdf_form.c index f07c396f..abee963d 100644 --- a/pdf/pdf_form.c +++ b/pdf/pdf_form.c @@ -499,24 +499,43 @@ static void measure_ascent_descent(pdf_document *doc, font_info *finf, char *tex typedef struct text_splitter_s { font_info *info; + float width; + float height; + float scale; + float unscaled_width; + float fontsize; char *text; int done; float x_orig; float y_orig; float x; float x_end; - float width; int text_start; int text_end; - int text_count; + int max_lines; + int retry; } text_splitter; -static void text_splitter_init(text_splitter *splitter, font_info *info, char *text, float width) +static void text_splitter_init(text_splitter *splitter, font_info *info, char *text, float width, float height, int variable) { + float fontsize = info->da_rec.font_size; + memset(splitter, 0, sizeof(*splitter)); splitter->info = info; splitter->text = text; splitter->width = width; + splitter->unscaled_width = width; + splitter->height = height; + splitter->fontsize = fontsize; + splitter->scale = 1.0; + splitter->max_lines = variable ? height/fontsize : FLT_MAX; +} + +static void text_splitter_start_pass(text_splitter *splitter) +{ + splitter->text_end = 0; + splitter->x_orig = 0; + splitter->y_orig = 0; } static void text_splitter_start_line(text_splitter *splitter) @@ -537,7 +556,7 @@ static int text_splitter_layout(fz_context *ctx, text_splitter *splitter) splitter->text_start = splitter->text_end; text = splitter->text + splitter->text_start; - room = splitter->width - splitter->x; + room = splitter->unscaled_width - splitter->x; if (text[0] == ' ') { @@ -551,22 +570,54 @@ static int text_splitter_layout(fz_context *ctx, text_splitter *splitter) len ++; } - stride = pdf_text_stride(ctx, splitter->info->font, text, len, room/fontsize, &count); - stride *= fontsize; + stride = pdf_text_stride(ctx, splitter->info->font, fontsize, text, len, room, &count); - /* Don't split a word if other than at the beggining of a line - * because we may fit the whole word on the next line */ - if (splitter->x > 0.0 && count < len) - { - return 0; - } - else + if (count < len && splitter->retry) { - splitter->text_end = splitter->text_start + count; - splitter->x_end = splitter->x + stride; - splitter->done = (splitter->text[splitter->text_end] == '\0'); - return 1; + /* The word didn't fit and we are in retry mode. Work out the + * least additional scaling that may help */ + float fitwidth; /* width if we force the word in */ + float hstretchwidth; /* width if we just bump by 10% */ + float vstretchwidth; /* width resulting from forcing in another line */ + float bestwidth; + + fitwidth = splitter->x + + pdf_text_stride(ctx, splitter->info->font, fontsize, text, len, FLT_MAX, &count); + /* FIXME: temporary fiddle factor. Would be better to work in integers */ + fitwidth *= 1.001; + + /* Stretching by 10% is worth trying only if processing the first word on the line */ + hstretchwidth = splitter->x == 0.0 + ? splitter->width * 1.1 / splitter->scale + : FLT_MAX; + + vstretchwidth = splitter->width * (splitter->max_lines + 1) * splitter->fontsize + / splitter->height; + + bestwidth = MIN(fitwidth, MIN(hstretchwidth, vstretchwidth)); + + if (bestwidth == vstretchwidth) + splitter->max_lines ++; + + splitter->scale = splitter->width / bestwidth; + splitter->unscaled_width = bestwidth; + + splitter->retry = 0; + + /* Try again */ + room = splitter->unscaled_width - splitter->x; + stride = pdf_text_stride(ctx, splitter->info->font, fontsize, text, len, room, &count); } + + /* This is not the first word on the line. Best to give up on this line and push + * the word onto the next */ + if (count < len && splitter->x > 0.0) + return 0; + + splitter->text_end = splitter->text_start + count; + splitter->x_end = splitter->x + stride; + splitter->done = (splitter->text[splitter->text_end] == '\0'); + return 1; } static void text_splitter_move(text_splitter *splitter, float newy, float *relx, float *rely) @@ -578,10 +629,8 @@ static void text_splitter_move(text_splitter *splitter, float newy, float *relx, splitter->y_orig = newy; } -static void fzbuf_print_text_start(fz_context *ctx, fz_buffer *fzbuf, fz_rect *clip, font_info *font, float scale) +static void fzbuf_print_text_start(fz_context *ctx, fz_buffer *fzbuf, fz_rect *clip, font_info *font, fz_matrix *tm) { - fz_matrix tm = fz_scale(scale, scale); - fz_buffer_printf(ctx, fzbuf, fmt_Tx_BMC); fz_buffer_printf(ctx, fzbuf, fmt_q); @@ -597,7 +646,7 @@ static void fzbuf_print_text_start(fz_context *ctx, fz_buffer *fzbuf, fz_rect *c fzbuf_print_da(ctx, fzbuf, &font->da_rec); fz_buffer_printf(ctx, fzbuf, "\n"); - fz_buffer_printf(ctx, fzbuf, fmt_Tm, tm.a, tm.b, tm.c, tm.d, tm.e, tm.f); + fz_buffer_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f); } static void fzbuf_print_text_end(fz_context *ctx, fz_buffer *fzbuf) @@ -622,9 +671,11 @@ static void fzbuf_print_text_word(fz_context *ctx, fz_buffer *fzbuf, float x, fl fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *oldtm, text_widget_info *info, char *text) { fz_context *ctx = doc->ctx; - int fontsize, da_fontsize; + int fontsize; + int variable; float height, width; fz_buffer *fzbuf = NULL; + fz_buffer *fztmp = NULL; fz_rect rect; fz_rect tbox; rect = *bbox; @@ -641,66 +692,42 @@ fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *o width = rect.x1 - rect.x0; fz_var(fzbuf); + fz_var(fztmp); fz_try(ctx) { float ascent, descent; fz_matrix tm; - da_fontsize = info->font_rec.da_rec.font_size; - fontsize = da_fontsize - ? da_fontsize - : (info->multiline ? 12.0 : floor(height)); + variable = (info->font_rec.da_rec.font_size == 0); + fontsize = variable + ? (info->multiline ? 14.0 : floor(height)) + : info->font_rec.da_rec.font_size == 0; + info->font_rec.da_rec.font_size = fontsize; measure_ascent_descent(doc, &info->font_rec, text, &ascent, &descent); - if (oldtm) - { - tm = *oldtm; - } - else - { - tm = fz_identity; - tm.e = 2.0; - tm.f = 2.0 + fontsize * descent; - - switch(info->q) - { - case Q_Right: tm.e += width; break; - case Q_Cent: tm.e += width/2; break; - } - } - - if (info->multiline) { text_splitter splitter; - float scale = 1.0; - int max_lines = da_fontsize - ? INT_MAX - : (int)(height / fontsize); - memset(&splitter, 0, sizeof(splitter)); + text_splitter_init(&splitter, &info->font_rec, text, width, height, variable); while (!splitter.done) { - /* Try to layout into max_lines or less */ + /* Try a layout pass */ int line = 0; - /* Offsets to apply to the first Td operator */ - float x_off = rect.x0; - float y_off = height - (1.0+ascent-descent)*fontsize*scale/2.0; - fz_drop_buffer(ctx, fzbuf); - fzbuf = NULL; - fzbuf = fz_new_buffer(ctx, 0); + fz_drop_buffer(ctx, fztmp); + fztmp = NULL; + fztmp = fz_new_buffer(ctx, 0); - fzbuf_print_text_start(ctx, fzbuf, &rect, &info->font_rec, scale); + text_splitter_start_pass(&splitter); /* Layout unscaled text to a scaled-up width, so that * the scaled-down text will fit the unscaled width */ - text_splitter_init(&splitter, &info->font_rec, text, width/scale); - while (!splitter.done && line < max_lines) + while (!splitter.done && line < splitter.max_lines) { /* Layout a line */ text_splitter_start_line(&splitter); @@ -714,30 +741,52 @@ fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *o int wordlen = splitter.text_end-splitter.text_start; text_splitter_move(&splitter, -line*fontsize, &x, &y); - fzbuf_print_text_word(ctx, fzbuf, x_off+x, y_off+y, word, wordlen); - x_off = y_off = 0.0; /* Offset has been applied */ + fzbuf_print_text_word(ctx, fztmp, x, y, word, wordlen); } } line ++; } - if (splitter.done) - { - fzbuf_print_text_end(ctx, fzbuf); - } - else - { - /* Failed to layout into max_lines. Try scaling down to - * fit one more line in */ - max_lines ++; - scale = height / (fontsize * max_lines); - } + if (!splitter.done) + splitter.retry = 1; } + + fzbuf = fz_new_buffer(ctx, 0); + + tm.a = splitter.scale; + tm.b = 0.0; + tm.c = 0.0; + tm.d = splitter.scale; + tm.e = rect.x0; + tm.f = rect.y1 - (1.0+ascent-descent)*fontsize*splitter.scale/2.0; + + fzbuf_print_text_start(ctx, fzbuf, &rect, &info->font_rec, &tm); + + fz_buffer_cat(ctx, fzbuf, fztmp); + + fzbuf_print_text_end(ctx, fzbuf); } else { - if (!da_fontsize) + if (oldtm) + { + tm = *oldtm; + } + else + { + tm = fz_identity; + tm.e = 2.0; + tm.f = 2.0 + fontsize * descent; + + switch(info->q) + { + case Q_Right: tm.e += width; break; + case Q_Cent: tm.e += width/2; break; + } + } + + if (variable) { tbox = measure_text(doc, &info->font_rec, &tm, text); @@ -753,6 +802,10 @@ fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *o fzbuf = create_aligned_text_buffer(doc, &rect, info, &tm, text); } } + fz_always(ctx) + { + fz_drop_buffer(ctx, fztmp); + } fz_catch(ctx) { fz_drop_buffer(ctx, fzbuf); |