diff options
-rw-r--r-- | apps/win_res.rc | 8 | ||||
-rw-r--r-- | pdf/mupdf-internal.h | 3 | ||||
-rw-r--r-- | pdf/pdf_font.c | 31 | ||||
-rw-r--r-- | pdf/pdf_form.c | 239 |
4 files changed, 247 insertions, 34 deletions
diff --git a/apps/win_res.rc b/apps/win_res.rc index 3e8da3d3..e57a3bb7 100644 --- a/apps/win_res.rc +++ b/apps/win_res.rc @@ -49,14 +49,14 @@ BEGIN LTEXT "", 0x27, 60, 125, 100, 10, 0 END -IDD_DLOGTEXT DIALOG 50, 50, 204, 47 +IDD_DLOGTEXT DIALOG 50, 50, 204, 85 //STYLE DS_MODALFRAME | WS_POPUP CAPTION " MuPDF: fill out form" FONT 8, "MS Shell Dlg" BEGIN - EDITTEXT 3,8,7,183,12 - DEFPUSHBUTTON "Okay",1,89,26,50,14 - PUSHBUTTON "Cancel",2,147,26,50,14 + EDITTEXT 3,8,7,183,50,4 + DEFPUSHBUTTON "Okay",1,89,64,50,14 + PUSHBUTTON "Cancel",2,147,64,50,14 END IDD_DLOGABOUT DIALOG 50, 50, 200, 220 diff --git a/pdf/mupdf-internal.h b/pdf/mupdf-internal.h index 03534256..cfebe713 100644 --- a/pdf/mupdf-internal.h +++ b/pdf/mupdf-internal.h @@ -514,7 +514,8 @@ pdf_font_desc *pdf_keep_font(fz_context *ctx, pdf_font_desc *fontdesc); void pdf_drop_font(fz_context *ctx, pdf_font_desc *font); void pdf_print_font(fz_context *ctx, pdf_font_desc *fontdesc); -fz_rect pdf_measure_text(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char *buf, int len, int *stride); +fz_rect pdf_measure_text(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char *buf, int len); +float pdf_text_stride(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char *buf, int len, float room, int *count); /* * Interactive features diff --git a/pdf/pdf_font.c b/pdf/pdf_font.c index 2c1138b8..9e67b83c 100644 --- a/pdf/pdf_font.c +++ b/pdf/pdf_font.c @@ -1158,7 +1158,7 @@ pdf_print_font(fz_context *ctx, pdf_font_desc *fontdesc) } } -fz_rect pdf_measure_text(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char *buf, int len, int *stride) +fz_rect pdf_measure_text(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char *buf, int len) { pdf_hmtx h; int gid; @@ -1178,8 +1178,31 @@ fz_rect pdf_measure_text(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char x += h.w / 1000.0; } - if (stride) - *stride = x; - return acc; +} + +float pdf_text_stride(fz_context *ctx, pdf_font_desc *fontdesc, unsigned char *buf, int len, float room, int *count) +{ + pdf_hmtx h; + int gid; + int i; + float x = 0.0; + + for (i = 0; i < len && x <= room; i++) + { + gid = pdf_font_cid_to_gid(ctx, fontdesc, buf[i]); + h = pdf_lookup_hmtx(ctx, fontdesc, buf[i]); + + x += h.w / 1000.0; + } + + if (x > room) + { + i --; + x -= h.w / 1000.0; + } + + *count = i; + + return x; }
\ No newline at end of file diff --git a/pdf/pdf_form.c b/pdf/pdf_form.c index 44570ae0..f07c396f 100644 --- a/pdf/pdf_form.c +++ b/pdf/pdf_form.c @@ -5,6 +5,7 @@ enum { + Ff_Multiline = 1 << (13-1), Ff_NoToggleToOff = 1 << (15-1), Ff_Radio = 1 << (16-1), Ff_Pushbutton = 1 << (17-1), @@ -76,6 +77,7 @@ static const char *fmt_W = "W\n"; static const char *fmt_n = "n\n"; static const char *fmt_BT = "BT\n"; static const char *fmt_Tm = "%1.2f %1.2f %1.2f %1.2f %1.2f %1.2f Tm\n"; +static const char *fmt_Td = "%f %f Td\n"; static const char *fmt_Tj = "(%s) Tj\n"; static const char *fmt_ET = "ET\n"; static const char *fmt_Q = "Q\n"; @@ -173,18 +175,12 @@ static char *get_string_or_stream(pdf_document *doc, pdf_obj *obj) static char *get_field_type_name(pdf_document *doc, pdf_obj *obj) { - pdf_obj *type = get_inheritable(doc, obj, "FT"); - - return type ? pdf_to_name(type) - : NULL; + return pdf_to_name(get_inheritable(doc, obj, "FT")); } static int get_field_flags(pdf_document *doc, pdf_obj *obj) { - pdf_obj *flags = get_inheritable(doc, obj, "Ff"); - - return flags ? pdf_to_int(flags) - : 0; + return pdf_to_int(get_inheritable(doc, obj, "Ff")); } static int get_field_type(pdf_document *doc, pdf_obj *obj) @@ -284,7 +280,7 @@ static void da_info_fin(fz_context *ctx, da_info *di) di->font_name = NULL; } -static void da_check_stack(int *stack, int *top) +static void da_check_stack(float *stack, int *top) { if (*top == 32) { @@ -387,7 +383,7 @@ static void get_text_widget_info(pdf_document *doc, pdf_obj *widget, text_widget info->dr = get_inheritable(doc, widget, "DR"); info->q = pdf_to_int(get_inheritable(doc, widget, "Q")); - info->multiline = 0; + info->multiline = (get_field_flags(doc, widget) & Ff_Multiline) != 0; get_font_info(doc, info->dr, da, &info->font_rec); } @@ -402,7 +398,7 @@ static void fzbuf_print_da(fz_context *ctx, fz_buffer *fzbuf, da_info *di) static fz_rect measure_text(pdf_document *doc, font_info *font_rec, fz_matrix *tm, char *text) { - fz_rect bbox = pdf_measure_text(doc->ctx, font_rec->font, text, strlen(text), NULL); + fz_rect bbox = pdf_measure_text(doc->ctx, font_rec->font, text, strlen(text)); bbox.x0 *= font_rec->da_rec.font_size * tm->a; bbox.y0 *= font_rec->da_rec.font_size * tm->d; @@ -500,6 +496,129 @@ static void measure_ascent_descent(pdf_document *doc, font_info *finf, char *tex } } +typedef struct text_splitter_s +{ + font_info *info; + 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; +} text_splitter; + +static void text_splitter_init(text_splitter *splitter, font_info *info, char *text, float width) +{ + memset(splitter, 0, sizeof(*splitter)); + splitter->info = info; + splitter->text = text; + splitter->width = width; +} + +static void text_splitter_start_line(text_splitter *splitter) +{ + splitter->x_end = 0; +} + +static int text_splitter_layout(fz_context *ctx, text_splitter *splitter) +{ + char *text; + float room; + float stride; + int count; + int len; + float fontsize = splitter->info->da_rec.font_size; + + splitter->x = splitter->x_end; + splitter->text_start = splitter->text_end; + + text = splitter->text + splitter->text_start; + room = splitter->width - splitter->x; + + if (text[0] == ' ') + { + /* Treat each space as a word */ + len = 1; + } + else + { + len = 0; + while (text[len] != '\0' && text[len] != ' ') + len ++; + } + + stride = pdf_text_stride(ctx, splitter->info->font, text, len, room/fontsize, &count); + stride *= fontsize; + + /* 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 + { + 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) +{ + *relx = splitter->x - splitter->x_orig; + *rely = newy - splitter->y_orig; + + splitter->x_orig = splitter->x; + splitter->y_orig = newy; +} + +static void fzbuf_print_text_start(fz_context *ctx, fz_buffer *fzbuf, fz_rect *clip, font_info *font, float scale) +{ + fz_matrix tm = fz_scale(scale, scale); + + fz_buffer_printf(ctx, fzbuf, fmt_Tx_BMC); + fz_buffer_printf(ctx, fzbuf, fmt_q); + + if (clip) + { + fz_buffer_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0); + fz_buffer_printf(ctx, fzbuf, fmt_W); + fz_buffer_printf(ctx, fzbuf, fmt_n); + } + + fz_buffer_printf(ctx, fzbuf, fmt_BT); + + 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); +} + +static void fzbuf_print_text_end(fz_context *ctx, fz_buffer *fzbuf) +{ + fz_buffer_printf(ctx, fzbuf, fmt_ET); + fz_buffer_printf(ctx, fzbuf, fmt_Q); +} + +static void fzbuf_print_text_word(fz_context *ctx, fz_buffer *fzbuf, float x, float y, char *text, int count) +{ + int i; + + fz_buffer_printf(ctx, fzbuf, fmt_Td, x, y); + fz_buffer_printf(ctx, fzbuf, "("); + + for (i = 0; i < count; i++) + fz_buffer_printf(ctx, fzbuf, "%c", text[i]); + + fz_buffer_printf(ctx, fzbuf, ") Tj\n"); +} + 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; @@ -510,7 +629,7 @@ fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *o fz_rect tbox; rect = *bbox; - if (rect.x1 - rect.x0 >= 2.0 && rect.y1 - rect.y0 >= 2.0) + if (rect.x1 - rect.x0 > 3.0 && rect.y1 - rect.y0 > 3.0) { rect.x0 += 1.0; rect.x1 -= 1.0; @@ -518,8 +637,8 @@ fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *o rect.y1 -= 1.0; } - height = MAX(bbox->y1 - bbox->y0 - 4.0, 1); - width = MAX(bbox->x1 - bbox->x0 - 4.0, 1); + height = rect.y1 - rect.y0; + width = rect.x1 - rect.x0; fz_var(fzbuf); fz_try(ctx) @@ -528,7 +647,12 @@ fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *o fz_matrix tm; da_fontsize = info->font_rec.da_rec.font_size; - fontsize = da_fontsize ? da_fontsize : floor(height); + fontsize = da_fontsize + ? da_fontsize + : (info->multiline ? 12.0 : floor(height)); + info->font_rec.da_rec.font_size = fontsize; + + measure_ascent_descent(doc, &info->font_rec, text, &ascent, &descent); if (oldtm) { @@ -536,7 +660,6 @@ fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *o } else { - measure_ascent_descent(doc, &info->font_rec, text, &ascent, &descent); tm = fz_identity; tm.e = 2.0; tm.f = 2.0 + fontsize * descent; @@ -548,21 +671,87 @@ fz_buffer *create_text_appearance(pdf_document *doc, fz_rect *bbox, fz_matrix *o } } - if (!da_fontsize) + + if (info->multiline) { - info->font_rec.da_rec.font_size = fontsize; - tbox = measure_text(doc, &info->font_rec, &tm, text); + text_splitter splitter; + float scale = 1.0; + int max_lines = da_fontsize + ? INT_MAX + : (int)(height / fontsize); + + memset(&splitter, 0, sizeof(splitter)); - if (tbox.x1 - tbox.x0 > width) + while (!splitter.done) { - /* Scale the text to fit but use the same offset - * to keep the baseline constant */ - tm.a *= width / (tbox.x1 - tbox.x0); - tm.d *= width / (tbox.x1 - tbox.x0); + /* Try to layout into max_lines or less */ + 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); + + fzbuf_print_text_start(ctx, fzbuf, &rect, &info->font_rec, scale); + + /* 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) + { + /* Layout a line */ + text_splitter_start_line(&splitter); + + while (!splitter.done && text_splitter_layout(ctx, &splitter)) + { + if (splitter.text[splitter.text_start] != ' ') + { + float x, y; + char *word = text+splitter.text_start; + 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 */ + } + } + + 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); + } } } + else + { + if (!da_fontsize) + { + tbox = measure_text(doc, &info->font_rec, &tm, text); - fzbuf = create_aligned_text_buffer(doc, &rect, info, &tm, text); + if (tbox.x1 - tbox.x0 > width) + { + /* Scale the text to fit but use the same offset + * to keep the baseline constant */ + tm.a *= width / (tbox.x1 - tbox.x0); + tm.d *= width / (tbox.x1 - tbox.x0); + } + } + + fzbuf = create_aligned_text_buffer(doc, &rect, info, &tm, text); + } } fz_catch(ctx) { |