From 8fc19912a4898ca6993a2acf92116e621d140f7a Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Thu, 15 Mar 2018 22:42:15 +0100 Subject: Create appearance streams for annotations. Now handles more annotation types, and does not use the pdf-write device. Handles many of the usual annotation properties, such as border width, color, interior color, line ending styles. * Ink * Highlight, Underline, Strike-Out, Squiggly * Line (with arrow-heads) * Polygon * PolyLine * Square * Circle * Caret * Text (needs better icons) * FileAttachment (needs better icons) * Sound (needs better icons) * Stamp * FreeText Partially complete: * Widget (treats everything like a plain text field) Not done, but on the to-do list: * /BS style (solid/dashed/bevel/inset/underline) * /BS dash pattern Not done and not on the list: * Movie * Screen * Printer's Mark * Trap Network * Watermark * /Border corner radii (ignored by acrobat) * /BE cloudy border effect --- source/pdf/annotation-icons.h | 273 ++++ source/pdf/pdf-annot-edit.c | 163 +-- source/pdf/pdf-appearance.c | 3182 +++++++++++------------------------------ source/pdf/pdf-form.c | 61 +- source/pdf/pdf-object.c | 12 + source/pdf/pdf-signature.c | 3 +- 6 files changed, 1235 insertions(+), 2459 deletions(-) create mode 100644 source/pdf/annotation-icons.h (limited to 'source') diff --git a/source/pdf/annotation-icons.h b/source/pdf/annotation-icons.h new file mode 100644 index 00000000..37c91ceb --- /dev/null +++ b/source/pdf/annotation-icons.h @@ -0,0 +1,273 @@ +/* +These 8x8 icons have been converted from the MIT licensed +https://github.com/iconic/open-iconic + */ + +static const char *icon_comment = + ".09 0 m\n" + ".03 0 0 .04 0 .09 c\n" + "0 5.9 l\n" + "0 5.95 .04 6 .09 6 c\n" + "6 6 l\n" + "8 8 l\n" + "8 .08 l\n" + "8 .02 7.96 -.01 7.91 -.01 c\n" + ".1 -.01 l\n" + "h\n" + "f"; + +static const char *icon_key = + "5.5 0 m\n" + "4.12 0 3 1.12 3 2.5 c\n" + "3 2.66 3 2.82 3.03 2.97 c\n" + "0 6 l\n" + "0 8 l\n" + "3 8 l\n" + "3 6 l\n" + "5 6 l\n" + "5 5 l\n" + "5.03 4.97 l\n" + "5.18 5 5.34 5 5.5 5 c\n" + "6.88 5 8 3.88 8 2.5 c\n" + "8 1.12 6.88 0 5.5 0 c\n" + "h\n" + "6 1 m\n" + "6.55 1 7 1.45 7 2 c\n" + "7 2.55 6.55 3 6 3 c\n" + "5.45 3 5 2.55 5 2 c\n" + "5 1.45 5.45 1 6 1 c\n" + "h\n" + "f"; + +static const char *icon_note = + "0 0 8 1 re\n" + "0 2 8 1 re\n" + "0 4 8 1 re\n" + "0 6 8 1 re\n" + "f"; + +static const char *icon_help = + "1 0 0 1 1 0 cm\n" + "2.47 0 m\n" + "1.62 0 .99 .26 .59 .66 c\n" + ".19 1.06 .05 1.56 0 1.94 c\n" + "1 2.07 l\n" + "1.04 1.82 1.12 1.57 1.31 1.38 c\n" + "1.50 1.19 1.80 1 2.47 1.00 c\n" + "3.13 1 3.49 1.16 3.69 1.34 c\n" + "3.89 1.52 3.97 1.74 3.97 2 c\n" + "3.97 2.83 3.63 3.06 3.13 3.50 c\n" + "2.63 3.94 1.97 4.58 1.97 5.75 c\n" + "1.97 6 l\n" + "2.97 6 l\n" + "2.97 5.75 l\n" + "2.97 4.92 3.28 4.69 3.78 4.25 c\n" + "4.28 3.81 4.97 3.17 4.97 2 c\n" + "4.97 1.52 4.80 .98 4.38 .59 c\n" + "3.95 .20 3.31 0 2.47 .00 c\n" + "h\n" + "1.97 7 m\n" + "1.97 8 l\n" + "2.97 8 l\n" + "2.97 7 l\n" + "1.97 7 l\n" + "h\n" + "f"; + +static const char *icon_graph = + "7.03 0 m\n" + "4 3 l\n" + "3 2 l\n" + "0 5.03 l\n" + "1 6.03 l\n" + "3 4 l\n" + "4 5 l\n" + "8 1 l\n" + "7.03 0 l\n" + "h\n" + "0 7 m 0 8 l 8 8 l 8 7 l 0 7 l h\n" + "f"; + +static const char *icon_push_pin = + "1.34 0 m\n" + ".92 .04 .76 .64 1.1 .89 c\n" + "1.34 1.08 1.65 .97 1.93 1 c\n" + "2.08 .98 1.96 1.22 2 1.32 c\n" + "2 1.88 2 2.44 2 3 c\n" + "1.6 3.01 1.2 2.98 .8 3.02 c\n" + ".35 3.11 -.01 3.54 0 4 c\n" + "1 4 2 4 3 4 c\n" + "3 5 3 6 3 7 c\n" + "3.146 7.33 3.29 7.67 3.44 8 c\n" + "3.62 7.66 3.83 7.32 4 6.98 c\n" + "4 5.99 4 4.99 4 4 c\n" + "5 4 6 4 7 4 c\n" + "7.02 3.42 6.46 2.94 5.89 3 c\n" + "5.6 3 5.3 3 5 3 c\n" + "5 2.33 5 1.67 5 1 c\n" + "5.30 .98 5.67 1.09 5.89 .81 c\n" + "6.16 .5 5.89 -.038 5.48 0 c\n" + "4.15 0 2.83 0 1.5 0 c\n" + "h\n" + "f"; + +static const char *icon_paperclip = + "5 0 m\n" + "4.49 0 3.98 .21 3.59 .59 c\n" + ".81 3.31 l\n" + "-.26 4.38 -.26 6.11 .81 7.19 c\n" + "1.88 8.26 3.61 8.26 4.69 7.19 c\n" + "5.94 5.94 l\n" + "5.25 5.25 l\n" + "4.09 6.38 l\n" + "4 6.51 l\n" + "3.31 7.2 2.19 7.2 1.5 6.51 c\n" + ".82 5.83 .84 4.73 1.5 4.04 c\n" + "4.28 1.29 l\n" + "4.67 .9 5.32 .9 5.72 1.29 c\n" + "6.11 1.68 6.09 2.3 5.72 2.7 c\n" + "3.22 5.17 l\n" + "3.12 5.27 2.95 5.27 2.84 5.17 c\n" + "2.74 5.07 2.74 4.9 2.84 4.79 c\n" + "2.9 4.76 l\n" + "3.81 3.82 l\n" + "3.12 3.13 l\n" + "2.15 4.1 l\n" + "1.67 4.58 1.67 5.37 2.15 5.85 c\n" + "2.63 6.33 3.42 6.34 3.9 5.85 c\n" + "6.4 3.41 l\n" + "7.18 2.63 7.18 1.37 6.4 .6 c\n" + "6.01 .21 5.51 .01 4.99 .01 c\n" + "h\n" + "f"; + +static const char *icon_tag = + "0 0 m\n" + "0 3 l\n" + "5 8 l\n" + "8 5 l\n" + "3 0 l\n" + "0 0 l\n" + "h\n" + "2 1 m\n" + "2.55 1 3 1.45 3 2 c\n" + "3 2.55 2.55 3 2 3 c\n" + "1.45 3 1 2.55 1 2 c\n" + "1 1.45 1.45 1 2 1 c\n" + "h\n" + "f"; + +static const char *icon_speaker = + "3.34 0 m\n" + "2 2 l\n" + "0 2 l\n" + "0 6 l\n" + "2 6 l\n" + "3.34 8 l\n" + "4 8 l\n" + "4 0 l\n" + "3.34 0 l\n" + "h\n" + "5 1 m\n" + "5 2 l\n" + "5.17 2 5.34 2.02 5.5 2.06 c\n" + "6.36 2.28 7 3.06 7 4 c\n" + "7 4.94 6.37 5.72 5.5 5.94 c\n" + "5.34 5.98 5.17 6 5 6 c\n" + "5 7 l\n" + "5.25 7 5.48 6.96 5.72 6.91 c\n" + "5.75 6.91 l\n" + "7.05 6.58 8 5.4 8 4 c\n" + "8 2.6 7.05 1.42 5.75 1.09 c\n" + "5.52 1.03 5.26 1 5 1 c\n" + "h\n" + "5 3 m\n" + "5 5 l\n" + "5.09 5 5.18 4.99 5.25 4.97 c\n" + "5.68 4.86 6 4.46 6 4 c\n" + "6 3.54 5.69 3.14 5.25 3.03 c\n" + "5.17 3.01 5.08 3 5 3 c\n" + "h\n" + "f"; + +static const char *icon_mic = + "1 0 0 1 1 0 cm\n" + "2.91 -.03 m\n" + "2.49 .03 2.11 .34 2.02 .76 c\n" + "1.97 1.12 2.01 1.48 2 1.84 c\n" + "2.01 2.29 1.98 2.73 2.02 3.17 c\n" + "2.1 3.72 2.68 4.11 3.21 3.98 c\n" + "3.7 3.89 4.05 3.39 4 2.9 c\n" + "4 2.21 4.01 1.53 3.99 .84 c\n" + "3.94 .32 3.43 -.09 2.91 -.03 c\n" + "h\n" + ".34 2 m\n" + ".1 2.07 -.04 2.34 - 2.58 c\n" + "-.02 3.18 .03 3.81 .32 4.34 c\n" + ".75 5.18 1.58 5.78 2.5 5.94 c\n" + "2.5 6.29 2.5 6.65 2.5 7 c\n" + "2.11 7.01 1.68 6.94 1.36 7.23 c\n" + "1.14 7.41 .96 7.75 1.02 8 c\n" + "2.35 8 3.67 8 5 8 c\n" + "5.02 7.43 4.47 6.94 3.9 7 c\n" + "3.77 7 3.63 7 3.5 7 c\n" + "3.5 6.65 3.5 6.29 3.5 5.94 c\n" + "4.82 5.73 5.92 4.55 5.99 3.21 c\n" + "5.99 2.93 6.02 2.65 5.98 2.37 c\n" + "5.91 2.05 5.49 1.89 5.23 2.08 c\n" + "5.03 2.2 4.97 2.45 5 2.66 c\n" + "5.02 3.15 4.97 3.66 4.69 4.08 c\n" + "4.18 4.9 3.05 5.24 2.17 4.82 c\n" + "1.42 4.5 .94 3.67 1 2.86 c\n" + ".99 2.61 1.05 2.32 .85 2.13 c\n" + ".71 1.99 .52 1.98 .34 2 c\n" + "h\n" + "f"; + +static const char *icon_star = + "4 0 m\n" + "3 3 l\n" + "0 3 l\n" + "2.5 5 l\n" + "1.5 8 l\n" + "4 6 l\n" + "6.5 8 l\n" + "5.5 5 l\n" + "8 3 l\n" + "5 3 l\n" + "4 0 l\n" + "h\n" + "f"; + +/* +These 8x8 icons have been converted from the MIT licensed +https://github.com/michaelampr/jam +*/ + +static const char *icon_insert = + "8 5 m\n" + "4 0 l\n" + "0 5 l\n" + "f"; + +static const char *icon_new_paragraph = + "8 8 m\n" + "4 0 l\n" + "0 8 l\n" + "f"; + +static const char *icon_paragraph = + "7 0 m\n" + "2 0 l\n" + "1 0 0 1 0 2 c\n" + "0 3 1 4 2 4 c\n" + "3 4 l\n" + "3 8 l\n" + "4 8 l\n" + "4 1 l\n" + "5 1 l\n" + "5 8 l\n" + "6 8 l\n" + "6 1 l\n" + "7 1 l\n" + "f"; diff --git a/source/pdf/pdf-annot-edit.c b/source/pdf/pdf-annot-edit.c index c06fa05a..55821e4f 100644 --- a/source/pdf/pdf-annot-edit.c +++ b/source/pdf/pdf-annot-edit.c @@ -1275,113 +1275,82 @@ pdf_set_annot_author(fz_context *ctx, pdf_annot *annot, const char *author) pdf_dirty_annot(ctx, annot); } -static void find_free_font_name(fz_context *ctx, pdf_obj *fdict, char *buf, int buf_size) +void +pdf_parse_default_appearance(fz_context *ctx, const char *da, const char **font, float *size, float color[3]) { - int i; + char buf[100], *p = buf, *tok, *end; + float stack[3] = { 0, 0, 0 }; + int top = 0; - /* Find a number X such that /FX doesn't occur as a key in fdict */ - for (i = 0; 1; i++) - { - fz_snprintf(buf, buf_size, "F%d", i); + *font = "Helv"; + *size = 12; + color[0] = color[1] = color[2] = 0; - if (!pdf_dict_gets(ctx, fdict, buf)) - break; + fz_strlcpy(buf, da, sizeof buf); + while ((tok = fz_strsep(&p, " ")) != NULL) + { + if (tok[0] == 0) + ; + else if (tok[0] == '/') + { + if (!strcmp(tok+1, "Cour")) *font = "Cour"; + if (!strcmp(tok+1, "Helv")) *font = "Helv"; + if (!strcmp(tok+1, "TiRo")) *font = "TiRo"; + if (!strcmp(tok+1, "Symb")) *font = "Symb"; + if (!strcmp(tok+1, "ZaDb")) *font = "ZaDb"; + } + else if (!strcmp(tok, "Tf")) + { + *size = stack[0]; + top = 0; + } + else if (!strcmp(tok, "g")) + { + color[0] = color[1] = color[2] = stack[0]; + top = 0; + } + else if (!strcmp(tok, "rg")) + { + color[0] = stack[0]; + color[1] = stack[1]; + color[2] = stack[2]; + top=0; + } + else + { + if (top < 3) + stack[top] = fz_strtof(tok, &end); + if (*end == 0) + ++top; + else + top = 0; + } } } void -pdf_set_free_text_details(fz_context *ctx, pdf_annot *annot, fz_point *pos, char *text, char *font_name, float font_size, float color[3]) +pdf_print_default_appearance(fz_context *ctx, char *buf, int nbuf, const char *font, float size, const float color[3]) { - pdf_document *doc = annot->page->doc; - char nbuf[32]; - pdf_obj *dr; - pdf_obj *form_fonts; - pdf_obj *font = NULL; - pdf_obj *ref; - pdf_font_desc *font_desc = NULL; - fz_matrix page_ctm, inv_page_ctm; - pdf_da_info da_info; - fz_buffer *fzbuf = NULL; - fz_point page_pos; - - pdf_page_transform(ctx, annot->page, NULL, &page_ctm); - fz_invert_matrix(&inv_page_ctm, &page_ctm); - - dr = pdf_dict_get(ctx, annot->page->obj, PDF_NAME(Resources)); - if (!dr) - { - dr = pdf_new_dict(ctx, doc, 1); - pdf_dict_put_drop(ctx, annot->page->obj, PDF_NAME(Resources), dr); - } - - /* Ensure the resource dictionary includes a font dict */ - form_fonts = pdf_dict_get(ctx, dr, PDF_NAME(Font)); - if (!form_fonts) - { - form_fonts = pdf_new_dict(ctx, doc, 1); - pdf_dict_put_drop(ctx, dr, PDF_NAME(Font), form_fonts); - /* form_fonts is still valid if execution continues past the above call */ - } - - fz_var(fzbuf); - fz_var(font); - fz_try(ctx) - { - unsigned char *da_str; - size_t da_len; - fz_rect bounds; - - find_free_font_name(ctx, form_fonts, nbuf, sizeof(nbuf)); - - font = pdf_new_dict(ctx, doc, 5); - ref = pdf_add_object(ctx, doc, font); - pdf_dict_puts_drop(ctx, form_fonts, nbuf, ref); - - pdf_dict_put(ctx, font, PDF_NAME(Type), PDF_NAME(Font)); - pdf_dict_put(ctx, font, PDF_NAME(Subtype), PDF_NAME(Type1)); - pdf_dict_put_name(ctx, font, PDF_NAME(BaseFont), font_name); - pdf_dict_put(ctx, font, PDF_NAME(Encoding), PDF_NAME(WinAnsiEncoding)); - - memcpy(da_info.col, color, sizeof(float)*3); - da_info.col_size = 3; - da_info.font_name = nbuf; - da_info.font_size = font_size; - - fzbuf = fz_new_buffer(ctx, 0); - pdf_fzbuf_print_da(ctx, fzbuf, &da_info); - - da_len = fz_buffer_storage(ctx, fzbuf, &da_str); - pdf_dict_put_string(ctx, annot->obj, PDF_NAME(DA), (char *)da_str, da_len); - - /* FIXME: should convert to WinAnsiEncoding */ - pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), text); - - font_desc = pdf_load_font(ctx, doc, NULL, font, 0); - pdf_measure_text(ctx, font_desc, (unsigned char *)text, strlen(text), &bounds); + if (color[0] > 0 || color[1] > 0 || color[2] > 0) + fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g rg", font, size, color[0], color[1], color[2]); + else + fz_snprintf(buf, nbuf, "/%s %g Tf", font, size); +} - page_pos = *pos; - fz_transform_point(&page_pos, &inv_page_ctm); +void +pdf_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char **font, float *size, float color[3]) +{ + pdf_obj *da = pdf_dict_get(ctx, annot->obj, PDF_NAME(DA)); + pdf_parse_default_appearance(ctx, pdf_to_str_buf(ctx, da), font, size, color); +} - bounds.x0 *= font_size; - bounds.x1 *= font_size; - bounds.y0 *= font_size; - bounds.y1 *= font_size; +void +pdf_set_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char *font, float size, const float color[3]) +{ + char buf[100]; - bounds.x0 += page_pos.x; - bounds.x1 += page_pos.x; - bounds.y0 += page_pos.y; - bounds.y1 += page_pos.y; + pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, color); - pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), &bounds); - } - fz_always(ctx) - { - pdf_drop_obj(ctx, font); - fz_drop_buffer(ctx, fzbuf); - pdf_drop_font(ctx, font_desc); - } - fz_catch(ctx) - { - fz_rethrow(ctx); - } + pdf_dict_put_string(ctx, annot->obj, PDF_NAME(DA), buf, strlen(buf)); + pdf_dirty_annot(ctx, annot); } diff --git a/source/pdf/pdf-appearance.c b/source/pdf/pdf-appearance.c index 2bb025aa..c6c05055 100644 --- a/source/pdf/pdf-appearance.c +++ b/source/pdf/pdf-appearance.c @@ -4,2532 +4,1056 @@ #include #include #include +#include -#include -#include FT_FREETYPE_H -#include FT_ADVANCES_H +#include -#define MATRIX_COEFS (6) -#define STRIKE_HEIGHT (0.375f) -#define UNDERLINE_HEIGHT (0.075f) -#define LINE_THICKNESS (0.07f) -#define SMALL_FLOAT (0.00001f) -#define LIST_SEL_COLOR_R (0.6f) -#define LIST_SEL_COLOR_G (0.75f) -#define LIST_SEL_COLOR_B (0.85f) +#include "annotation-icons.h" -enum -{ - Q_Left = 0, - Q_Cent = 1, - Q_Right = 2 -}; - -enum -{ - BS_Solid, - BS_Dashed, - BS_Beveled, - BS_Inset, - BS_Underline -}; - -typedef struct font_info_s -{ - pdf_da_info da_rec; - pdf_font_desc *font; - float lineheight; -} font_info; - -typedef struct text_widget_info_s -{ - pdf_obj *dr; - pdf_obj *col; - font_info font_rec; - int q; - int multiline; - int comb; - int max_len; -} text_widget_info; - -static const char *fmt_re = "%g %g %g %g re\n"; -static const char *fmt_f = "f\n"; -static const char *fmt_s = "s\n"; -static const char *fmt_g = "%g g\n"; -static const char *fmt_m = "%g %g m\n"; -static const char *fmt_l = "%g %g l\n"; -static const char *fmt_w = "%g w\n"; -static const char *fmt_Tx_BMC = "/Tx BMC\n"; -static const char *fmt_q = "q\n"; -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 = "%g %g %g %g %g %g Tm\n"; -static const char *fmt_Td = "%g %g Td\n"; -static const char *fmt_Tj = " Tj\n"; -static const char *fmt_ET = "ET\n"; -static const char *fmt_Q = "Q\n"; -static const char *fmt_EMC = "EMC\n"; - -void pdf_da_info_fin(fz_context *ctx, pdf_da_info *di) -{ - fz_free(ctx, di->font_name); - di->font_name = NULL; -} - -static void da_check_stack(float *stack, int *top) -{ - if (*top == 32) - { - memmove(stack, stack + 1, 31 * sizeof(stack[0])); - *top = 31; - } -} - -void pdf_parse_da(fz_context *ctx, char *da, pdf_da_info *di) -{ - float stack[32] = { 0.0f }; - int top = 0; - pdf_token tok; - char *name = NULL; - pdf_lexbuf lbuf; - fz_stream *str = fz_open_memory(ctx, (unsigned char *)da, strlen(da)); - - pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL); - - fz_var(str); - fz_var(name); - fz_try(ctx) - { - for (tok = pdf_lex(ctx, str, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str, &lbuf)) - { - switch (tok) - { - case PDF_TOK_NAME: - fz_free(ctx, name); - name = fz_strdup(ctx, lbuf.scratch); - break; - - case PDF_TOK_INT: - da_check_stack(stack, &top); - stack[top] = lbuf.i; - top ++; - break; - - case PDF_TOK_REAL: - da_check_stack(stack, &top); - stack[top] = lbuf.f; - top ++; - break; - - case PDF_TOK_KEYWORD: - if (!strcmp(lbuf.scratch, "Tf")) - { - di->font_size = stack[0]; - fz_free(ctx, di->font_name); - di->font_name = name; - name = NULL; - } - else if (!strcmp(lbuf.scratch, "rg")) - { - di->col[0] = stack[0]; - di->col[1] = stack[1]; - di->col[2] = stack[2]; - di->col_size = 3; - } - else if (!strcmp(lbuf.scratch, "g")) - { - di->col[0] = stack[0]; - di->col_size = 1; - } - - fz_free(ctx, name); - name = NULL; - top = 0; - break; - - default: - break; - } - } - } - fz_always(ctx) - { - fz_free(ctx, name); - fz_drop_stream(ctx, str); - pdf_lexbuf_fin(ctx, &lbuf); - } - fz_catch(ctx) - { - fz_rethrow(ctx); - } -} - -static void get_font_info(fz_context *ctx, pdf_document *doc, pdf_obj *dr, char *da, font_info *font_rec) -{ - pdf_font_desc *font; - pdf_obj *fontobj; - - pdf_parse_da(ctx, da, &font_rec->da_rec); - if (font_rec->da_rec.font_name == NULL) - fz_throw(ctx, FZ_ERROR_GENERIC, "No font name in default appearance"); - - fontobj = pdf_dict_gets(ctx, pdf_dict_get(ctx, dr, PDF_NAME(Font)), font_rec->da_rec.font_name); - if (!fontobj) - { - fz_font *helv = fz_new_base14_font(ctx, "Helvetica"); - fz_warn(ctx, "form resource dictionary is missing the default appearance font"); - fontobj = pdf_add_simple_font(ctx, doc, helv, PDF_SIMPLE_ENCODING_LATIN); - pdf_dict_puts_drop(ctx, pdf_dict_get(ctx, dr, PDF_NAME(Font)), font_rec->da_rec.font_name, fontobj); - fz_drop_font(ctx, helv); - } - font_rec->font = font = pdf_load_font(ctx, doc, dr, fontobj, 0); - font_rec->lineheight = 1.0f; - if (font && font->ascent != 0.0f && font->descent != 0.0f) - font_rec->lineheight = (font->ascent - font->descent) / 1000.0f; -} - -static void font_info_fin(fz_context *ctx, font_info *font_rec) -{ - pdf_drop_font(ctx, font_rec->font); - font_rec->font = NULL; - pdf_da_info_fin(ctx, &font_rec->da_rec); -} - -static void get_text_widget_info(fz_context *ctx, pdf_document *doc, pdf_obj *widget, text_widget_info *info) -{ - char *da = pdf_to_str_buf(ctx, pdf_get_inheritable(ctx, doc, widget, PDF_NAME(DA))); - int ff = pdf_get_field_flags(ctx, doc, widget); - pdf_obj *ml = pdf_get_inheritable(ctx, doc, widget, PDF_NAME(MaxLen)); - - info->dr = pdf_get_inheritable(ctx, doc, widget, PDF_NAME(DR)); - info->col = pdf_dict_getl(ctx, widget, PDF_NAME(MK), PDF_NAME(BG), NULL); - info->q = pdf_to_int(ctx, pdf_get_inheritable(ctx, doc, widget, PDF_NAME(Q))); - info->multiline = (ff & Ff_Multiline) != 0; - info->comb = (ff & (Ff_Multiline|Ff_Password|Ff_FileSelect|Ff_Comb)) == Ff_Comb; - - if (ml == NULL) - info->comb = 0; - else - info->max_len = pdf_to_int(ctx, ml); - - get_font_info(ctx, doc, info->dr, da, &info->font_rec); -} - -void pdf_fzbuf_print_da(fz_context *ctx, fz_buffer *fzbuf, pdf_da_info *di) -{ - if (di->font_name != NULL && di->font_size != 0) - fz_append_printf(ctx, fzbuf, "/%s %d Tf", di->font_name, di->font_size); - - switch (di->col_size) - { - case 1: - fz_append_printf(ctx, fzbuf, " %g g", di->col[0]); - break; - - case 3: - fz_append_printf(ctx, fzbuf, " %g %g %g rg", di->col[0], di->col[1], di->col[2]); - break; - - case 4: - fz_append_printf(ctx, fzbuf, " %g %g %g %g k", di->col[0], di->col[1], di->col[2], di->col[3]); - break; - - default: - fz_append_string(ctx, fzbuf, " 0 g"); - break; - } -} - -static fz_rect *measure_text(fz_context *ctx, pdf_document *doc, font_info *font_rec, const fz_matrix *tm, char *text, fz_rect *bbox) -{ - pdf_measure_text(ctx, font_rec->font, (unsigned char *)text, strlen(text), bbox); - - bbox->x0 *= font_rec->da_rec.font_size * tm->a; - bbox->y0 *= font_rec->da_rec.font_size * tm->d; - bbox->x1 *= font_rec->da_rec.font_size * tm->a; - bbox->y1 *= font_rec->da_rec.font_size * tm->d; - - return bbox; -} - -static void fzbuf_print_color(fz_context *ctx, fz_buffer *fzbuf, pdf_obj *arr, int stroke, float adj) -{ - switch (pdf_array_len(ctx, arr)) - { - case 1: - fz_append_printf(ctx, fzbuf, stroke?"%g G\n":"%g g\n", - pdf_to_real(ctx, pdf_array_get(ctx, arr, 0)) + adj); - break; - case 3: - fz_append_printf(ctx, fzbuf, stroke?"%g %g %g RG\n":"%g %g %g rg\n", - pdf_to_real(ctx, pdf_array_get(ctx, arr, 0)) + adj, - pdf_to_real(ctx, pdf_array_get(ctx, arr, 1)) + adj, - pdf_to_real(ctx, pdf_array_get(ctx, arr, 2)) + adj); - break; - case 4: - fz_append_printf(ctx, fzbuf, stroke?"%g %g %g %g K\n":"%g %g %g %g k\n", - pdf_to_real(ctx, pdf_array_get(ctx, arr, 0)), - pdf_to_real(ctx, pdf_array_get(ctx, arr, 1)), - pdf_to_real(ctx, pdf_array_get(ctx, arr, 2)), - pdf_to_real(ctx, pdf_array_get(ctx, arr, 3))); - break; - } -} - -static void fzbuf_print_rect_fill(fz_context *ctx, fz_buffer *fzbuf, const fz_rect *clip, - float col[4], int col_size, const fz_matrix *tm, const fz_rect *rect) -{ - if (clip) - { - fz_append_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0); - fz_append_printf(ctx, fzbuf, fmt_W); - } - if (col_size > 0) - { - switch (col_size) - { - case 1: - fz_append_printf(ctx, fzbuf, "%g g\n", col[0]); - break; - case 3: - fz_append_printf(ctx, fzbuf, "%g %g %g rg\n", col[0], col[1], col[2]); - break; - case 4: - fz_append_printf(ctx, fzbuf, "%g %g %g %g k\n", col[0], col[1], col[2], col[3]); - break; - default: - break; - } - } - if (tm) - fz_append_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f); - - fz_append_printf(ctx, fzbuf, fmt_re, rect->x0, rect->y0, rect->x1 - rect->x0, rect->y1 - rect->y0); - fz_append_printf(ctx, fzbuf, fmt_f); -} +#define REPLACEMENT 0xB7 +#define CIRCLE_MAGIC 0.551915f -static void fzbuf_print_text(fz_context *ctx, fz_buffer *fzbuf, const fz_rect *clip, pdf_obj *col, font_info *font_rec, const fz_matrix *tm, char *text) +static float pdf_write_border_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) { - fz_append_printf(ctx, fzbuf, fmt_q); - if (clip) - { - fz_append_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0); - fz_append_printf(ctx, fzbuf, fmt_W); - if (col) - { - fzbuf_print_color(ctx, fzbuf, col, 0, 0.0f); - fz_append_printf(ctx, fzbuf, fmt_f); - } - else - { - fz_append_printf(ctx, fzbuf, fmt_n); - } - } - - fz_append_printf(ctx, fzbuf, fmt_BT); - - pdf_fzbuf_print_da(ctx, fzbuf, &font_rec->da_rec); - - fz_append_printf(ctx, fzbuf, "\n"); - if (tm) - fz_append_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f); - - fz_append_pdf_string(ctx, fzbuf, text); - fz_append_printf(ctx, fzbuf, fmt_Tj); - fz_append_printf(ctx, fzbuf, fmt_ET); - fz_append_printf(ctx, fzbuf, fmt_Q); + float w = pdf_annot_border(ctx, annot); + fz_append_printf(ctx, buf, "%g w\n", w); + return w; } -static fz_buffer *create_text_buffer(fz_context *ctx, const fz_rect *clip, text_widget_info *info, const fz_matrix *tm, char *text) +static int pdf_write_stroke_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) { - fz_buffer *fzbuf = fz_new_buffer(ctx, 0); - - fz_try(ctx) - { - fz_append_printf(ctx, fzbuf, fmt_Tx_BMC); - fzbuf_print_text(ctx, fzbuf, clip, info->col, &info->font_rec, tm, text); - fz_append_printf(ctx, fzbuf, fmt_EMC); - } - fz_catch(ctx) + float color[4]; + int n; + pdf_annot_color(ctx, annot, &n, color); + switch (n) { - fz_drop_buffer(ctx, fzbuf); - fz_rethrow(ctx); + default: return 0; + case 1: fz_append_printf(ctx, buf, "%g G\n", color[0]); break; + case 3: fz_append_printf(ctx, buf, "%g %g %g RG\n", color[0], color[1], color[2]); break; + case 4: fz_append_printf(ctx, buf, "%g %g %g %g K\n", color[0], color[1], color[2], color[3]); break; } - - return fzbuf; + return 1; } -static fz_buffer *create_aligned_text_buffer(fz_context *ctx, pdf_document *doc, const fz_rect *clip, text_widget_info *info, const fz_matrix *tm, char *text) +static int pdf_write_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) { - fz_matrix atm = *tm; - - if (info->q != Q_Left) + float color[4]; + int n; + pdf_annot_color(ctx, annot, &n, color); + switch (n) { - fz_rect rect; - - measure_text(ctx, doc, &info->font_rec, tm, text, &rect); - atm.e -= info->q == Q_Right ? rect.x1 : (rect.x1 - rect.x0) / 2; + default: return 0; + case 1: fz_append_printf(ctx, buf, "%g g\n", color[0]); break; + case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]); break; + case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n", color[0], color[1], color[2], color[3]); break; } - - return create_text_buffer(ctx, clip, info, &atm, text); + return 1; } -static void measure_ascent_descent(fz_context *ctx, pdf_document *doc, font_info *finf, char *text, float *ascent, float *descent) +static int pdf_write_interior_fill_color_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf) { - char *testtext = NULL; - fz_rect bbox; - font_info tinf = *finf; - - fz_var(testtext); - fz_try(ctx) - { - /* Heuristic: adding "My" to text will in most cases - * produce a measurement that will encompass all chars */ - testtext = fz_malloc(ctx, strlen(text) + 3); - strcpy(testtext, "My"); - strcat(testtext, text); - tinf.da_rec.font_size = 1; - measure_text(ctx, doc, &tinf, &fz_identity, testtext, &bbox); - *descent = -bbox.y0; - *ascent = bbox.y1; - } - fz_always(ctx) - { - fz_free(ctx, testtext); - } - fz_catch(ctx) + float color[4]; + int n; + pdf_annot_interior_color(ctx, annot, &n, color); + switch (n) { - fz_rethrow(ctx); + default: return 0; + case 1: fz_append_printf(ctx, buf, "%g g\n", color[0]); break; + case 3: fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]); break; + case 4: fz_append_printf(ctx, buf, "%g %g %g %g k\n", color[0], color[1], color[2], color[3]); break; } + return 1; } -typedef struct text_splitter_s -{ - font_info *info; - float width; - float height; - float scale; - float unscaled_width; - float fontsize; - float lineheight; - const char *text; - int done; - float x_orig; - float y_orig; - float x; - float x_end; - size_t text_start; - size_t text_end; - int max_lines; - int retry; -} text_splitter; - -static void text_splitter_init(text_splitter *splitter, font_info *info, const char *text, float width, float height, int variable) +static fz_point rotate_vector(float angle, float x, float y) { - 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.0f; - splitter->lineheight = fontsize * info->lineheight ; - /* RJW: The cast in the following line is important, as otherwise - * under MSVC in the variable = 0 case, splitter->max_lines becomes - * INT_MIN. */ - splitter->max_lines = variable ? (int)(height/splitter->lineheight) : INT_MAX; + float ca = cosf(angle); + float sa = sinf(angle); + return fz_make_point(x*ca - y*sa, x*sa + y*ca); } -static void text_splitter_start_pass(text_splitter *splitter) +static void pdf_write_arrow_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, float x, float y, float dx, float dy, float w) { - splitter->text_end = 0; - splitter->x_orig = 0; - splitter->y_orig = 0; + float r = fz_max(1, w); + float angle = atan2f(dy, dx); + fz_point v, a, b; + + v = rotate_vector(angle, 8.8f*r, 4.5f*r); + a = fz_make_point(x + v.x, y + v.y); + v = rotate_vector(angle, 8.8f*r, -4.5f*r); + b = fz_make_point(x + v.x, y + v.y); + + fz_include_point_in_rect(rect, &a); + fz_include_point_in_rect(rect, &b); + fz_expand_rect(rect, w); + + fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); + fz_append_printf(ctx, buf, "%g %g l\n", x, y); + fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); } -static void text_splitter_start_line(text_splitter *splitter) +static void include_cap(fz_rect *rect, float x, float y, float r) { - splitter->x_end = 0; + rect->x0 = fz_min(rect->x0, x-r); + rect->y0 = fz_min(rect->y0, y-r); + rect->x1 = fz_max(rect->x1, x+r); + rect->y1 = fz_max(rect->y1, y+r); } -static int text_splitter_layout(fz_context *ctx, text_splitter *splitter) +static void +pdf_write_line_cap_appearance(fz_context *ctx, fz_buffer *buf, fz_rect *rect, + float x, float y, float dx, float dy, float w, int ic, pdf_obj *cap) { - const char *text; - float room; - float stride; - size_t count; - size_t 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->unscaled_width - splitter->x; - - if (strchr("\r\n", text[0])) - { - /* Consume return chars and report end of line */ - splitter->text_end += strspn(text, "\r\n"); - splitter->text_start = splitter->text_end; - splitter->done = (splitter->text[splitter->text_end] == '\0'); - return 0; - } - else if (text[0] == ' ') - { - /* Treat each space as a word */ - len = 1; - } - else - { - len = 0; - while (text[len] != '\0' && !strchr(" \r\n", text[len])) - len ++; - } - - stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, len, room, &count); - - /* If not a single char fits although the line is empty, then force one char */ - if (count == 0 && splitter->x == 0.0f) - stride = pdf_text_stride(ctx, splitter->info->font, fontsize, (unsigned char *)text, 1, FLT_MAX, &count); - - if (count < len && splitter->retry) - { - /* 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, (unsigned char *)text, len, FLT_MAX, &count); - /* FIXME: temporary fiddle factor. Would be better to work in integers */ - fitwidth *= 1.001f; - - /* Stretching by 10% is worth trying only if processing the first word on the line */ - hstretchwidth = splitter->x == 0.0f - ? splitter->width * 1.1f / splitter->scale - : FLT_MAX; - - vstretchwidth = splitter->width * (splitter->max_lines + 1) * splitter->lineheight - / splitter->height; - - bestwidth = fz_min(fitwidth, fz_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, (unsigned char *)text, len, room, &count); + if (cap == PDF_NAME(Square)) + { + float r = fz_max(2.5f, w * 2.5f); + fz_append_printf(ctx, buf, "%g %g %g %g re\n", x-r, y-r, r*2, r*2); + fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); + include_cap(rect, x, y, r); + } + else if (cap == PDF_NAME(Circle)) + { + float r = fz_max(2.5f, w * 2.5f); + float m = r * CIRCLE_MAGIC; + fz_append_printf(ctx, buf, "%g %g m\n", x, y+r); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+m, y+r, x+r, y+m, x+r, y); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x+r, y-m, x+m, y-r, x, y-r); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-m, y-r, x-r, y-m, x-r, y); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", x-r, y+m, x-m, y+r, x, y+r); + fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); + include_cap(rect, x, y, r); + } + else if (cap == PDF_NAME(Diamond)) + { + float r = fz_max(2.5f, w * 2.5f); + fz_append_printf(ctx, buf, "%g %g m\n", x, y+r); + fz_append_printf(ctx, buf, "%g %g l\n", x+r, y); + fz_append_printf(ctx, buf, "%g %g l\n", x, y-r); + fz_append_printf(ctx, buf, "%g %g l\n", x-r, y); + fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); + include_cap(rect, x, y, r); + } + else if (cap == PDF_NAME(OpenArrow)) + { + pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w); + fz_append_string(ctx, buf, "S\n"); + } + else if (cap == PDF_NAME(ClosedArrow)) + { + pdf_write_arrow_appearance(ctx, buf, rect, x, y, dx, dy, w); + fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); + } + /* PDF 1.5 */ + else if (cap == PDF_NAME(Butt)) + { + float r = fz_max(3, w * 3); + fz_point a = { x-dy*r, y+dx*r }; + fz_point b = { x+dy*r, y-dx*r }; + fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); + fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); + fz_append_string(ctx, buf, "S\n"); + fz_include_point_in_rect(rect, &a); + fz_include_point_in_rect(rect, &b); + } + /* PDF 1.6 */ + else if (cap == PDF_NAME(ROpenArrow)) + { + pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w); + fz_append_string(ctx, buf, "S\n"); + } + else if (cap == PDF_NAME(RClosedArrow)) + { + pdf_write_arrow_appearance(ctx, buf, rect, x, y, -dx, -dy, w); + fz_append_string(ctx, buf, ic ? "b\n" : "s\n"); + } + else if (cap == PDF_NAME(Slash)) + { + float r = fz_max(5, w * 5); + float angle = atan2f(dy, dx) - (30 * FZ_PI / 180); + fz_point a, b, v; + v = rotate_vector(angle, 0, r); + a = fz_make_point(x + v.x, y + v.y); + v = rotate_vector(angle, 0, -r); + b = fz_make_point(x + v.x, y + v.y); + fz_append_printf(ctx, buf, "%g %g m\n", a.x, a.y); + fz_append_printf(ctx, buf, "%g %g l\n", b.x, b.y); + fz_append_string(ctx, buf, "S\n"); + fz_include_point_in_rect(rect, &a); + fz_include_point_in_rect(rect, &b); } - - /* 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.0f) - 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) -{ - *relx = splitter->x - splitter->x_orig; - *rely = newy * splitter->lineheight - splitter->y_orig; - - splitter->x_orig = splitter->x; - splitter->y_orig = newy * splitter->lineheight; } -static void text_splitter_retry(text_splitter *splitter) +static void +pdf_write_line_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) { - if (splitter->retry) - { - /* Already tried expanding lines. Overflow must - * be caused by carriage control */ - splitter->max_lines ++; - splitter->retry = 0; - splitter->unscaled_width = splitter->width * splitter->max_lines * splitter->lineheight - / splitter->height; - splitter->scale = splitter->width / splitter->unscaled_width; - } - else - { - splitter->retry = 1; - } + pdf_obj *line, *le; + fz_point a, b; + float w; + int ic; + + w = pdf_write_border_appearance(ctx, annot, buf); + pdf_write_stroke_color_appearance(ctx, annot, buf); + ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); + + line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L)); + a.x = pdf_array_get_real(ctx, line, 0); + a.y = pdf_array_get_real(ctx, line, 1); + b.x = pdf_array_get_real(ctx, line, 2); + b.y = pdf_array_get_real(ctx, line, 3); + + fz_append_printf(ctx, buf, "%g %g m\n%g %g l\nS\n", a.x, a.y, b.x, b.y); + + rect->x0 = fz_min(a.x, b.x); + rect->y0 = fz_min(a.y, b.y); + rect->x1 = fz_max(a.x, b.x); + rect->y1 = fz_max(a.y, b.y); + + le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); + if (pdf_array_len(ctx, le) == 2) + { + float dx = b.x - a.x; + float dy = b.y - a.y; + float l = sqrtf(dx*dx + dy*dy); + pdf_write_line_cap_appearance(ctx, buf, rect, a.x, a.y, dx/l, dy/l, w, ic, pdf_array_get(ctx, le, 0)); + pdf_write_line_cap_appearance(ctx, buf, rect, b.x, b.y, -dx/l, -dy/l, w, ic, pdf_array_get(ctx, le, 1)); + } + fz_expand_rect(rect, fz_max(1, w)); } -static void fzbuf_print_text_start1(fz_context *ctx, fz_buffer *fzbuf, const fz_rect *clip, pdf_obj *col) +static void +pdf_write_square_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) { - fz_append_printf(ctx, fzbuf, fmt_Tx_BMC); - fz_append_printf(ctx, fzbuf, fmt_q); - - if (clip) - { - fz_append_printf(ctx, fzbuf, fmt_re, clip->x0, clip->y0, clip->x1 - clip->x0, clip->y1 - clip->y0); - fz_append_printf(ctx, fzbuf, fmt_W); - if (col) - { - fzbuf_print_color(ctx, fzbuf, col, 0, 0.0f); - fz_append_printf(ctx, fzbuf, fmt_f); - } - else - { - fz_append_printf(ctx, fzbuf, fmt_n); - } - } -} + float x, y, w, h; + float lw; + int ic; -static void fzbuf_print_text_start2(fz_context *ctx, fz_buffer *fzbuf, font_info *font, const fz_matrix *tm) -{ - fz_append_printf(ctx, fzbuf, fmt_BT); + lw = pdf_write_border_appearance(ctx, annot, buf); + pdf_write_stroke_color_appearance(ctx, annot, buf); + ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); - pdf_fzbuf_print_da(ctx, fzbuf, &font->da_rec); - fz_append_printf(ctx, fzbuf, "\n"); + x = rect->x0 + lw; + y = rect->y0 + lw; + w = rect->x1 - x - lw; + h = rect->y1 - y - lw; - fz_append_printf(ctx, fzbuf, fmt_Tm, tm->a, tm->b, tm->c, tm->d, tm->e, tm->f); + fz_append_printf(ctx, buf, "%g %g %g %g re\n", x, y, w, h); + fz_append_string(ctx, buf, ic ? "b" : "s"); } -static void fzbuf_print_text_end(fz_context *ctx, fz_buffer *fzbuf) +static void +pdf_write_circle_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) { - fz_append_printf(ctx, fzbuf, fmt_ET); - fz_append_printf(ctx, fzbuf, fmt_Q); - fz_append_printf(ctx, fzbuf, fmt_EMC); + float rx, ry, cx, cy, mx, my; + float lw; + int ic; + + lw = pdf_write_border_appearance(ctx, annot, buf); + pdf_write_stroke_color_appearance(ctx, annot, buf); + ic = pdf_write_interior_fill_color_appearance(ctx, annot, buf); + + rx = (rect->x1 - rect->x0) / 2 - lw; + ry = (rect->y1 - rect->y0) / 2 - lw; + cx = rect->x0 + lw + rx; + cy = rect->y0 + lw + ry; + mx = rx * CIRCLE_MAGIC; + my = ry * CIRCLE_MAGIC; + + fz_append_printf(ctx, buf, "%g %g m\n", cx, cy+ry); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+mx, cy+ry, cx+rx, cy+my, cx+rx, cy); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx+rx, cy-my, cx+mx, cy-ry, cx, cy-ry); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-mx, cy-ry, cx-rx, cy-my, cx-rx, cy); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", cx-rx, cy+my, cx-mx, cy+ry, cx, cy+ry); + fz_append_string(ctx, buf, ic ? "b" : "s"); } -static void fzbuf_print_text_word(fz_context *ctx, fz_buffer *fzbuf, float x, float y, char *text, size_t count) +static void +pdf_write_polygon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, int close) { - size_t i; - - fz_append_printf(ctx, fzbuf, fmt_Td, x, y); - fz_append_printf(ctx, fzbuf, "("); - - for (i = 0; i < count; i++) - fz_append_printf(ctx, fzbuf, "%c", text[i]); - - fz_append_printf(ctx, fzbuf, ") Tj\n"); -} + pdf_obj *verts; + fz_point p; + int i, n; + float lw; -static fz_buffer *create_text_appearance(fz_context *ctx, pdf_document *doc, const fz_rect *bbox, const fz_matrix *oldtm, text_widget_info *info, char *text) -{ - int fontsize; - int variable; - float height, width, full_width; - fz_buffer *fzbuf = NULL; - fz_buffer *fztmp = NULL; - fz_rect rect; - fz_rect tbox; - rect = *bbox; - - if (rect.x1 - rect.x0 > 3.0f && rect.y1 - rect.y0 > 3.0f) - { - rect.x0 += 1.0f; - rect.x1 -= 1.0f; - rect.y0 += 1.0f; - rect.y1 -= 1.0f; - } + lw = pdf_write_border_appearance(ctx, annot, buf); + pdf_write_stroke_color_appearance(ctx, annot, buf); - height = rect.y1 - rect.y0; - width = rect.x1 - rect.x0; - full_width = bbox->x1 - bbox->x0; + *rect = fz_empty_rect; - fz_var(fzbuf); - fz_var(fztmp); - fz_try(ctx) + verts = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); + n = pdf_array_len(ctx, verts) / 2; + if (n > 0) { - float ascent, descent; - fz_matrix tm; - - variable = (info->font_rec.da_rec.font_size == 0); - fontsize = variable - ? (info->multiline ? 14.0f : height / info->font_rec.lineheight) - : info->font_rec.da_rec.font_size; - - info->font_rec.da_rec.font_size = fontsize; - - measure_ascent_descent(ctx, doc, &info->font_rec, text, &ascent, &descent); - - if (info->multiline) - { - text_splitter splitter; - - text_splitter_init(&splitter, &info->font_rec, text, width, height, variable); - - while (!splitter.done) - { - /* Try a layout pass */ - int line = 0; - - fz_drop_buffer(ctx, fztmp); - fztmp = NULL; - fztmp = fz_new_buffer(ctx, 0); - - text_splitter_start_pass(&splitter); - - /* Layout unscaled text to a scaled-up width, so that - * the scaled-down text will fit the unscaled width */ - - while (!splitter.done && line < splitter.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; - size_t wordlen = splitter.text_end-splitter.text_start; - - text_splitter_move(&splitter, -line, &x, &y); - fzbuf_print_text_word(ctx, fztmp, x, y, word, wordlen); - } - } - - line ++; - } - - if (!splitter.done) - text_splitter_retry(&splitter); - } - - fzbuf = fz_new_buffer(ctx, 0); - - tm.a = splitter.scale; - tm.b = 0.0f; - tm.c = 0.0f; - tm.d = splitter.scale; - tm.e = rect.x0; - tm.f = rect.y1 - (1.0f+ascent-descent)*fontsize*splitter.scale/2.0f; - - fzbuf_print_text_start1(ctx, fzbuf, &rect, info->col); - fzbuf_print_text_start2(ctx, fzbuf, &info->font_rec, &tm); - - fz_append_buffer(ctx, fzbuf, fztmp); - - fzbuf_print_text_end(ctx, fzbuf); - } - else if (info->comb) - { - int i, n = fz_mini((int)strlen(text), info->max_len); - float comb_width = full_width/info->max_len; - float char_width = pdf_text_stride(ctx, info->font_rec.font, fontsize, (unsigned char *)"M", 1, FLT_MAX, NULL); - float init_skip = (comb_width - char_width)/2.0f; - - fz_translate(&tm, rect.x0, rect.y1 - (height+(ascent-descent)*fontsize)/2.0f); - - fzbuf = fz_new_buffer(ctx, 0); - - fzbuf_print_text_start1(ctx, fzbuf, &rect, info->col); - fzbuf_print_text_start2(ctx, fzbuf, &info->font_rec, &tm); - - for (i = 0; i < n; i++) - fzbuf_print_text_word(ctx, fzbuf, i == 0 ? init_skip : comb_width, 0.0f, text+i, 1); - - fzbuf_print_text_end(ctx, fzbuf); - } - else + for (i = 0; i < n; ++i) { - if (oldtm) + p.x = pdf_array_get_real(ctx, verts, i*2+0); + p.y = pdf_array_get_real(ctx, verts, i*2+1); + if (i == 0) { - tm = *oldtm; + rect->x0 = rect->x1 = p.x; + rect->y0 = rect->y1 = p.y; } else - { - fz_translate(&tm, rect.x0, rect.y1 - (height+(ascent-descent)*fontsize)/2.0f); - - switch (info->q) - { - case Q_Right: tm.e += width; break; - case Q_Cent: tm.e += width/2; break; - } - } - - if (variable) - { - measure_text(ctx, doc, &info->font_rec, &tm, text, &tbox); - - 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(ctx, doc, &rect, info, &tm, text); + fz_include_point_in_rect(rect, &p); + if (i == 0) + fz_append_printf(ctx, buf, "%g %g m\n", p.x, p.y); + else + fz_append_printf(ctx, buf, "%g %g l\n", p.x, p.y); } + fz_append_string(ctx, buf, close ? "s" : "S"); + fz_expand_rect(rect, lw); } - fz_always(ctx) - { - fz_drop_buffer(ctx, fztmp); - } - fz_catch(ctx) - { - fz_drop_buffer(ctx, fzbuf); - fz_rethrow(ctx); - } - - return fzbuf; -} - -static int get_matrix(fz_context *ctx, pdf_document *doc, pdf_obj *form, int q, fz_matrix *mt) -{ - int found = 0; - pdf_lexbuf lbuf; - fz_stream *str; - - str = pdf_open_stream(ctx, form); - - pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL); - - fz_try(ctx) - { - int tok; - float coefs[MATRIX_COEFS]; - int coef_i = 0; - - /* Look for the text matrix Tm in the stream */ - for (tok = pdf_lex(ctx, str, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str, &lbuf)) - { - if (tok == PDF_TOK_INT || tok == PDF_TOK_REAL) - { - if (coef_i >= MATRIX_COEFS) - { - int i; - for (i = 0; i < MATRIX_COEFS-1; i++) - coefs[i] = coefs[i+1]; - - coef_i = MATRIX_COEFS-1; - } - - coefs[coef_i++] = tok == PDF_TOK_INT ? lbuf.i : lbuf.f; - } - else - { - if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "Tm") && coef_i == MATRIX_COEFS) - { - found = 1; - mt->a = coefs[0]; - mt->b = coefs[1]; - mt->c = coefs[2]; - mt->d = coefs[3]; - mt->e = coefs[4]; - mt->f = coefs[5]; - } - - coef_i = 0; - } - } - - if (found) - { - fz_rect bbox; - pdf_to_rect(ctx, pdf_dict_get(ctx, form, PDF_NAME(BBox)), &bbox); - - switch (q) - { - case Q_Left: - mt->e = bbox.x0 + 1; - break; - - case Q_Cent: - mt->e = (bbox.x1 - bbox.x0) / 2; - break; - - case Q_Right: - mt->e = bbox.x1 - 1; - break; - } - } - } - fz_always(ctx) - { - fz_drop_stream(ctx, str); - pdf_lexbuf_fin(ctx, &lbuf); - } - fz_catch(ctx) - { - fz_rethrow(ctx); - } - - return found; -} - -static char *to_font_encoding(fz_context *ctx, pdf_font_desc *font, char *utf8) -{ - size_t i; - int needs_converting = 0; - - /* Temporary partial solution. We are using a slow lookup in the conversion - * below, so we avoid performing the conversion unnecessarily. We check for - * top-bit-set chars, and convert only if they are present. We should also - * check that the font encoding is one that agrees with utf8 from 0 to 7f, - * but for now we get away without doing so. This is after all an improvement - * on just strdup */ - for (i = 0; utf8[i] != '\0'; i++) - { - if (utf8[i] & 0x80) - needs_converting = 1; - } - - /* Even if we need to convert, we cannot do so if the font has no cid_to_ucs mapping */ - if (needs_converting && font->cid_to_ucs) - { - char *buf = fz_malloc(ctx, strlen(utf8) + 1); - char *bufp = buf; - - fz_try(ctx) - { - while(*utf8) - { - if (*utf8 & 0x80) - { - int rune; - - utf8 += fz_chartorune(&rune, utf8); - - /* Slow search for the cid that maps to the unicode value held in 'rune" */ - for (i = 0; i < font->cid_to_ucs_len && font->cid_to_ucs[i] != rune; i++) - ; - - /* If found store the cid */ - if (i < font->cid_to_ucs_len) - *bufp++ = (char)i; - } - else - { - *bufp++ = *utf8++; - } - } - - *bufp = '\0'; - } - fz_catch(ctx) - { - fz_free(ctx, buf); - fz_rethrow(ctx); - } - - return buf; - } - else - { - /* If either no conversion is needed or the font has no cid_to_ucs - * mapping then leave unconverted, although in the latter case the result - * is likely incorrect */ - return fz_strdup(ctx, utf8); - } -} - -static void account_for_rot(fz_rect *rect, fz_matrix *mat, int rot) -{ - float width = rect->x1; - float height = rect->y1; - - switch (rot) - { - default: - *mat = fz_identity; - break; - case 90: - fz_pre_rotate(fz_translate(mat, width, 0), rot); - rect->x1 = height; - rect->y1 = width; - break; - case 180: - fz_pre_rotate(fz_translate(mat, width, height), rot); - break; - case 270: - fz_pre_rotate(fz_translate(mat, 0, height), rot); - rect->x1 = height; - rect->y1 = width; - break; - } -} - -static void copy_resources(fz_context *ctx, pdf_obj *dst, pdf_obj *src) -{ - int i, len; - - len = pdf_dict_len(ctx, src); - for (i = 0; i < len; i++) - { - pdf_obj *key = pdf_dict_get_key(ctx, src, i); - - if (!pdf_dict_get(ctx, dst, key)) - pdf_dict_put(ctx, dst, key, pdf_dict_get_val(ctx, src, i)); - } -} - -static pdf_obj *load_or_create_form(fz_context *ctx, pdf_document *doc, pdf_obj *obj, fz_rect *rect) -{ - pdf_obj *ap = NULL; - fz_matrix mat; - int rot; - pdf_obj *formobj = NULL; - fz_buffer *fzbuf = NULL; - int create_form = 0; - - fz_var(formobj); - fz_var(fzbuf); - fz_try(ctx) - { - rot = pdf_to_int(ctx, pdf_dict_getl(ctx, obj, PDF_NAME(MK), PDF_NAME(R), NULL)); - pdf_to_rect(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Rect)), rect); - rect->x1 -= rect->x0; - rect->y1 -= rect->y0; - rect->x0 = rect->y0 = 0; - account_for_rot(rect, &mat, rot); - - ap = pdf_dict_get(ctx, obj, PDF_NAME(AP)); - if (ap == NULL) - { - ap = pdf_new_dict(ctx, doc, 1); - pdf_dict_put_drop(ctx, obj, PDF_NAME(AP), ap); - } - - formobj = pdf_dict_get(ctx, ap, PDF_NAME(N)); - if (formobj == NULL) - { - formobj = pdf_new_xobject(ctx, doc, rect, &mat, NULL, NULL); - pdf_dict_put_drop(ctx, ap, PDF_NAME(N), formobj); - create_form = 1; - } - - if (create_form) - { - fzbuf = fz_new_buffer(ctx, 1); - pdf_update_stream(ctx, doc, formobj, fzbuf, 0); - } - - copy_resources(ctx, pdf_xobject_resources(ctx, formobj), pdf_get_inheritable(ctx, doc, obj, PDF_NAME(DR))); - } - fz_always(ctx) - { - fz_drop_buffer(ctx, fzbuf); - } - fz_catch(ctx) - { - fz_rethrow(ctx); - } - - return pdf_keep_obj(ctx, formobj); -} - -static void update_marked_content(fz_context *ctx, pdf_document *doc, pdf_obj *form, fz_buffer *fzbuf) -{ - pdf_token tok; - pdf_lexbuf lbuf; - fz_stream *str_outer = NULL; - fz_stream *str_inner = NULL; - unsigned char *buf; - size_t len; - fz_buffer *newbuf = NULL; - - pdf_lexbuf_init(ctx, &lbuf, PDF_LEXBUF_SMALL); - - fz_var(str_outer); - fz_var(str_inner); - fz_var(newbuf); - fz_try(ctx) - { - int bmc_found; - int first = 1; - - newbuf = fz_new_buffer(ctx, 0); - str_outer = pdf_open_stream(ctx, form); - len = fz_buffer_storage(ctx, fzbuf, &buf); - str_inner = fz_open_memory(ctx, buf, len); - - /* Copy the existing appearance stream to newbuf while looking for BMC */ - for (tok = pdf_lex(ctx, str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str_outer, &lbuf)) - { - if (first) - first = 0; - else - fz_append_printf(ctx, newbuf, " "); - - pdf_append_token(ctx, newbuf, tok, &lbuf); - if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "BMC")) - break; - } - - bmc_found = (tok != PDF_TOK_EOF); - - if (bmc_found) - { - /* Drop Tx BMC from the replacement appearance stream */ - (void)pdf_lex(ctx, str_inner, &lbuf); - (void)pdf_lex(ctx, str_inner, &lbuf); - } - - /* Copy the replacement appearance stream to newbuf */ - for (tok = pdf_lex(ctx, str_inner, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str_inner, &lbuf)) - { - fz_append_printf(ctx, newbuf, " "); - pdf_append_token(ctx, newbuf, tok, &lbuf); - } - - if (bmc_found) - { - /* Drop the rest of the existing appearance stream until EMC found */ - for (tok = pdf_lex(ctx, str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str_outer, &lbuf)) - { - if (tok == PDF_TOK_KEYWORD && !strcmp(lbuf.scratch, "EMC")) - break; - } - - /* Copy the rest of the existing appearance stream to newbuf */ - for (tok = pdf_lex(ctx, str_outer, &lbuf); tok != PDF_TOK_EOF; tok = pdf_lex(ctx, str_outer, &lbuf)) - { - fz_append_printf(ctx, newbuf, " "); - pdf_append_token(ctx, newbuf, tok, &lbuf); - } - } - - /* Use newbuf in place of the existing appearance stream */ - pdf_update_stream(ctx, doc, form, newbuf, 0); - } - fz_always(ctx) - { - fz_drop_stream(ctx, str_outer); - fz_drop_stream(ctx, str_inner); - fz_drop_buffer(ctx, newbuf); - pdf_lexbuf_fin(ctx, &lbuf); - } - fz_catch(ctx) - { - fz_rethrow(ctx); - } -} - -static int get_border_style(fz_context *ctx, pdf_obj *obj) -{ - pdf_obj *sname = pdf_dict_getl(ctx, obj, PDF_NAME(BS), PDF_NAME(S), NULL); - - if (pdf_name_eq(ctx, PDF_NAME(D), sname)) - return BS_Dashed; - else if (pdf_name_eq(ctx, PDF_NAME(B), sname)) - return BS_Beveled; - else if (pdf_name_eq(ctx, PDF_NAME(I), sname)) - return BS_Inset; - else if (pdf_name_eq(ctx, PDF_NAME(U), sname)) - return BS_Underline; - else - return BS_Solid; -} - -static float get_border_width(fz_context *ctx, pdf_obj *obj) -{ - float w = pdf_to_real(ctx, pdf_dict_getl(ctx, obj, PDF_NAME(BS), PDF_NAME(W), NULL)); - return w == 0.0f ? 1.0f : w; -} - -void pdf_update_text_appearance(fz_context *ctx, pdf_document *doc, pdf_obj *obj, char *eventValue) -{ - text_widget_info info; - fz_buffer *fzbuf = NULL; - pdf_obj *form = NULL; - fz_matrix tm; - fz_rect rect, form_bbox; - int has_tm; - char *text = NULL; - - memset(&info, 0, sizeof(info)); - - fz_var(info); - fz_var(fzbuf); - fz_var(form); - fz_var(text); - fz_try(ctx) - { - get_text_widget_info(ctx, doc, obj, &info); - - if (eventValue) - text = to_font_encoding(ctx, info.font_rec.font, eventValue); - else - text = pdf_field_value(ctx, doc, obj); - - form = load_or_create_form(ctx, doc, obj, &rect); - - pdf_xobject_bbox(ctx, form, &form_bbox); - has_tm = get_matrix(ctx, doc, form, info.q, &tm); - fzbuf = create_text_appearance(ctx, doc, &form_bbox, has_tm ? &tm : NULL, &info, text?text:""); - update_marked_content(ctx, doc, form, fzbuf); - } - fz_always(ctx) - { - pdf_drop_obj(ctx, form); - fz_free(ctx, text); - fz_drop_buffer(ctx, fzbuf); - font_info_fin(ctx, &info.font_rec); - } - fz_catch(ctx) - { - fz_warn(ctx, "update_text_appearance failed"); - } -} - -void pdf_update_listbox_appearance(fz_context *ctx, pdf_document *doc, pdf_obj *obj) -{ - text_widget_info info; - pdf_obj *form = NULL; - fz_buffer *fzbuf = NULL; - fz_matrix tm; - fz_rect clip_rect; - fz_rect fill_rect; - pdf_obj *valarr; - pdf_obj *optarr; - char *text; - int n, m, i, j; - char **opts = NULL; - char **vals = NULL; - int *sel_indices = NULL; - char **pos; - int index = -1; - float height, width; - float ascent, descent, lineheight; - int variable; - int fontsize; - float items_height; - fz_rect bbox; - int num_sel = 0; - int found_count; - int val_opt_ok = 1; - float color[4]; - - memset(&info, 0, sizeof(info)); - - fz_var(info); - fz_var(form); - fz_var(fzbuf); - fz_var(opts); - fz_var(vals); - fz_var(sel_indices); - fz_try(ctx) - { - optarr = pdf_dict_get(ctx, obj, PDF_NAME(Opt)); - n = pdf_array_len(ctx, optarr); - opts = (char **)fz_malloc(ctx, n * sizeof(*opts)); - vals = (char **)fz_malloc(ctx, n * sizeof(*vals)); - sel_indices = (int*)fz_malloc(ctx, n * sizeof(int)); - for (i = 0; i < n; i++) - { - m = pdf_array_len(ctx, pdf_array_get(ctx, optarr, i)); - if (m == 2) - { - vals[i] = pdf_to_str_buf(ctx, pdf_array_get(ctx, pdf_array_get(ctx, optarr, i), 0)); - opts[i] = pdf_to_str_buf(ctx, pdf_array_get(ctx, pdf_array_get(ctx, optarr, i), 1)); - } - else - { - opts[i] = pdf_to_str_buf(ctx, pdf_array_get(ctx, optarr, i)); - val_opt_ok = 0; - } - } - - /* If ANY of the entries are not an array then just use the opts not the vals. */ - if (val_opt_ok) - pos = vals; - else - pos = opts; - - get_text_widget_info(ctx, doc, obj, &info); - form = load_or_create_form(ctx, doc, obj, &clip_rect); - - /* See which ones are selected */ - valarr = pdf_get_inheritable(ctx, doc, obj, PDF_NAME(V)); - if (pdf_is_array(ctx, valarr)) - { - num_sel = pdf_array_len(ctx, valarr); - /* Multiple selected */ - found_count = 0; - for (i = 0; i < num_sel; i++) - { - text = pdf_to_str_buf(ctx, pdf_array_get(ctx, valarr, i)); - /* See if we can find it. */ - index = -1; - for (j = 0; j < n; j++) - { - if (!strcmp(text, pos[j])) - { - index = j; - break; - } - } - if (index > -1) - { - sel_indices[found_count] = index; - found_count += 1; - } - } - num_sel = found_count; - } - else - { - /* One or none selected */ - text = pdf_to_str_buf(ctx, valarr); - num_sel = 0; - if (text) - { - index = -1; - for (i = 0; i < n; i++) - { - if (!strcmp(text, pos[i])) - { - index = i; - break; - } - } - if (index > -1) - { - num_sel = 1; - sel_indices[0] = index; - } - } - } - - if (clip_rect.x1 - clip_rect.x0 > 3.0f && clip_rect.y1 - clip_rect.y0 > 3.0f) - { - clip_rect.x0 += 1.0f; - clip_rect.x1 -= 1.0f; - clip_rect.y0 += 1.0f; - clip_rect.y1 -= 1.0f; - } - height = clip_rect.y1 - clip_rect.y0; - width = clip_rect.x1 - clip_rect.x0; - variable = (info.font_rec.da_rec.font_size == 0); - fontsize = variable - ? (info.multiline ? 14.0f : height / info.font_rec.lineheight) - : info.font_rec.da_rec.font_size; - - /* Get the ascent, descent across the items list. */ - ascent = 0; - descent = 0; - info.font_rec.da_rec.font_size = 1; - for (i = 0; i < n; i++) - { - measure_text(ctx, doc, &(info.font_rec), &fz_identity, opts[i], &bbox); - descent = fz_min(-bbox.y0, descent); - ascent = fz_max(bbox.y1, ascent); - } - info.font_rec.da_rec.font_size = fontsize; - lineheight = ascent - descent; - - /* Check if all items will fit. If not, then place the "selected" item at the top of our widget rect. */ - items_height = n * fontsize * lineheight; - if (items_height <= height || !num_sel) - fz_translate(&tm, clip_rect.x0, clip_rect.y1 - lineheight * fontsize); - else - fz_translate(&tm, clip_rect.x0, clip_rect.y1 + (sel_indices[0] - 1) * lineheight * fontsize); - - fzbuf = fz_new_buffer(ctx, 0); - /* Start the marked content */ - fzbuf_print_text_start1(ctx, fzbuf, &clip_rect, NULL); - /* Add the selection rects */ - if (num_sel > 0) - { - color[0] = LIST_SEL_COLOR_R; - color[1] = LIST_SEL_COLOR_G; - color[2] = LIST_SEL_COLOR_B; - fill_rect.x0 = 0.0f; - fill_rect.x1 = width; - for (i = 0; i < num_sel; i++) - { - fill_rect.y0 = height - fontsize * lineheight * (sel_indices[i] + 1); - fill_rect.y1 = fill_rect.y0 + lineheight * fontsize; - fzbuf_print_rect_fill(ctx, fzbuf, NULL, color, 3, NULL, &fill_rect); - } - } - /* Continue with text related details */ - fzbuf_print_text_start2(ctx, fzbuf, &info.font_rec, &tm); - /* And now finish with the text content */ - for (i = 0; i < n; i++) - { - fzbuf_print_text_word(ctx, fzbuf, 0.0f, i == 0 ? 0 : -fontsize * - lineheight, opts[i], (int)strlen(opts[i])); - } - fzbuf_print_text_end(ctx, fzbuf); - update_marked_content(ctx, doc, form, fzbuf); - } - fz_always(ctx) - { - fz_free(ctx, opts); - fz_free(ctx, vals); - fz_free(ctx, sel_indices); - pdf_drop_obj(ctx, form); - fz_drop_buffer(ctx, fzbuf); - font_info_fin(ctx, &info.font_rec); - } - fz_catch(ctx) - { - fz_warn(ctx, "update_text_appearance failed"); - } -} - -void pdf_update_combobox_appearance(fz_context *ctx, pdf_document *doc, pdf_obj *obj) -{ - text_widget_info info; - pdf_obj *form = NULL; - fz_buffer *fzbuf = NULL; - fz_matrix tm; - fz_rect rect, form_bbox; - int has_tm; - pdf_obj *val; - char *text; - - memset(&info, 0, sizeof(info)); - - fz_var(info); - fz_var(form); - fz_var(fzbuf); - fz_try(ctx) - { - get_text_widget_info(ctx, doc, obj, &info); - - val = pdf_get_inheritable(ctx, doc, obj, PDF_NAME(V)); - - if (pdf_is_array(ctx, val)) - val = pdf_array_get(ctx, val, 0); - - text = pdf_to_str_buf(ctx, val); - - if (!text) - text = ""; - - form = load_or_create_form(ctx, doc, obj, &rect); - - pdf_xobject_bbox(ctx, form, &form_bbox); - has_tm = get_matrix(ctx, doc, form, info.q, &tm); - fzbuf = create_text_appearance(ctx, doc, &form_bbox, has_tm ? &tm : NULL, &info, - text?text:""); - update_marked_content(ctx, doc, form, fzbuf); - } - fz_always(ctx) - { - pdf_drop_obj(ctx, form); - fz_drop_buffer(ctx, fzbuf); - font_info_fin(ctx, &info.font_rec); - } - fz_catch(ctx) - { - fz_warn(ctx, "update_text_appearance failed"); - } -} - -void pdf_update_pushbutton_appearance(fz_context *ctx, pdf_document *doc, pdf_obj *obj) -{ - fz_rect rect = fz_empty_rect; - pdf_obj *form = NULL; - fz_buffer *fzbuf = NULL; - pdf_obj *tobj = NULL; - font_info font_rec; - int bstyle; - float bwidth; - float btotal; - - memset(&font_rec, 0, sizeof(font_rec)); - - fz_var(font_rec); - fz_var(form); - fz_var(fzbuf); - fz_try(ctx) - { - form = load_or_create_form(ctx, doc, obj, &rect); - fzbuf = fz_new_buffer(ctx, 0); - tobj = pdf_dict_getl(ctx, obj, PDF_NAME(MK), PDF_NAME(BG), NULL); - if (pdf_is_array(ctx, tobj)) - { - fzbuf_print_color(ctx, fzbuf, tobj, 0, 0.0f); - fz_append_printf(ctx, fzbuf, fmt_re, - rect.x0, rect.y0, rect.x1, rect.y1); - fz_append_printf(ctx, fzbuf, fmt_f); - } - bstyle = get_border_style(ctx, obj); - bwidth = get_border_width(ctx, obj); - btotal = bwidth; - if (bstyle == BS_Beveled || bstyle == BS_Inset) - { - btotal += bwidth; - - if (bstyle == BS_Beveled) - fz_append_printf(ctx, fzbuf, fmt_g, 1.0f); - else - fz_append_printf(ctx, fzbuf, fmt_g, 0.33f); - fz_append_printf(ctx, fzbuf, fmt_m, bwidth, bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, bwidth, rect.y1 - bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - bwidth, rect.y1 - bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, rect.y1 - 2 * bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, 2 * bwidth, rect.y1 - 2 * bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, 2 * bwidth, 2 * bwidth); - fz_append_printf(ctx, fzbuf, fmt_f); - if (bstyle == BS_Beveled) - fzbuf_print_color(ctx, fzbuf, tobj, 0, -0.25f); - else - fz_append_printf(ctx, fzbuf, fmt_g, 0.66f); - fz_append_printf(ctx, fzbuf, fmt_m, rect.x1 - bwidth, rect.y1 - bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - bwidth, bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, bwidth, bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, 2 * bwidth, 2 * bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, 2 * bwidth); - fz_append_printf(ctx, fzbuf, fmt_l, rect.x1 - 2 * bwidth, rect.y1 - 2 * bwidth); - fz_append_printf(ctx, fzbuf, fmt_f); - } - - tobj = pdf_dict_getl(ctx, obj, PDF_NAME(MK), PDF_NAME(BC), NULL); - if (tobj) - { - fzbuf_print_color(ctx, fzbuf, tobj, 1, 0.0f); - fz_append_printf(ctx, fzbuf, fmt_w, bwidth); - fz_append_printf(ctx, fzbuf, fmt_re, - bwidth/2, bwidth/2, - rect.x1 -bwidth/2, rect.y1 - bwidth/2); - fz_append_printf(ctx, fzbuf, fmt_s); - } - - tobj = pdf_dict_getl(ctx, obj, PDF_NAME(MK), PDF_NAME(CA), NULL); - if (tobj) - { - fz_rect clip = rect; - fz_rect bounds; - fz_matrix mat; - char *da = pdf_to_str_buf(ctx, pdf_get_inheritable(ctx, doc, obj, PDF_NAME(DA))); - char *text = pdf_to_str_buf(ctx, tobj); - - clip.x0 += btotal; - clip.y0 += btotal; - clip.x1 -= btotal; - clip.y1 -= btotal; - - get_font_info(ctx, doc, pdf_xobject_resources(ctx, form), da, &font_rec); - measure_text(ctx, doc, &font_rec, &fz_identity, text, &bounds); - fz_translate(&mat, (rect.x1 - bounds.x1)/2, (rect.y1 - bounds.y1)/2); - fzbuf_print_text(ctx, fzbuf, &clip, NULL, &font_rec, &mat, text); - } - - pdf_update_stream(ctx, doc, form, fzbuf, 0); - } - fz_always(ctx) - { - font_info_fin(ctx, &font_rec); - fz_drop_buffer(ctx, fzbuf); - pdf_drop_obj(ctx, form); - } - fz_catch(ctx) - { - fz_rethrow(ctx); - } -} - -void pdf_update_text_markup_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot, enum pdf_annot_type type) -{ - float color[3]; - float alpha; - float line_height; - float line_thickness; - pdf_obj *annotcolor; - - switch (type) - { - case PDF_ANNOT_HIGHLIGHT: - color[0] = 1.0f; - color[1] = 1.0f; - color[2] = 0.0f; - alpha = 0.5f; - line_thickness = 1.0f; - line_height = 0.5f; - break; - case PDF_ANNOT_UNDERLINE: - color[0] = 0.0f; - color[1] = 0.0f; - color[2] = 1.0f; - alpha = 1.0f; - line_thickness = LINE_THICKNESS; - line_height = UNDERLINE_HEIGHT; - break; - case PDF_ANNOT_STRIKE_OUT: - color[0] = 1.0f; - color[1] = 0.0f; - color[2] = 0.0f; - alpha = 1.0f; - line_thickness = LINE_THICKNESS; - line_height = STRIKE_HEIGHT; - break; - default: - return; - } - - annotcolor = pdf_dict_get(ctx, annot->obj, PDF_NAME(C)); - - if (pdf_is_array(ctx, annotcolor)) - { - color[0] = pdf_to_int(ctx, pdf_array_get(ctx, annotcolor, 0)); - color[1] = pdf_to_int(ctx, pdf_array_get(ctx, annotcolor, 1)); - color[2] = pdf_to_int(ctx, pdf_array_get(ctx, annotcolor, 2)); - } - - pdf_set_markup_appearance(ctx, doc, annot, color, alpha, line_thickness, line_height); } -void pdf_set_annot_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot, fz_rect *rect, fz_display_list *disp_list) +static void +pdf_write_ink_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) { - pdf_obj *obj = annot->obj; - fz_device *dev = NULL; - fz_matrix page_ctm, inv_page_ctm; + pdf_obj *ink_list, *stroke; + int i, n, k, m; + float lw; + fz_point p; - pdf_obj *resources; - fz_buffer *contents; + lw = pdf_write_border_appearance(ctx, annot, buf); + pdf_write_stroke_color_appearance(ctx, annot, buf); - pdf_obj *ap_obj; - fz_rect trect = *rect; + *rect = fz_empty_rect; - pdf_page_transform(ctx, annot->page, NULL, &page_ctm); - fz_invert_matrix(&inv_page_ctm, &page_ctm); - - fz_transform_rect(&trect, &inv_page_ctm); - - pdf_dict_put_drop(ctx, obj, PDF_NAME(Rect), pdf_new_rect(ctx, doc, &trect)); - - /* See if there is a current normal appearance */ - ap_obj = pdf_dict_getl(ctx, obj, PDF_NAME(AP), PDF_NAME(N), NULL); - if (!pdf_is_stream(ctx, ap_obj)) - ap_obj = NULL; - - if (ap_obj == NULL) + ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); + n = pdf_array_len(ctx, ink_list); + for (i = 0; i < n; ++i) { - ap_obj = pdf_new_xobject(ctx, doc, &trect, &fz_identity, NULL, NULL); - pdf_dict_putl_drop(ctx, obj, ap_obj, PDF_NAME(AP), PDF_NAME(N), NULL); - } - else - { - pdf_xref_ensure_incremental_object(ctx, doc, pdf_to_num(ctx, ap_obj)); - /* Update bounding box and matrix in reused xobject obj */ - pdf_dict_put_drop(ctx, ap_obj, PDF_NAME(BBox), pdf_new_rect(ctx, doc, &trect)); - pdf_dict_put_drop(ctx, ap_obj, PDF_NAME(Matrix), pdf_new_matrix(ctx, doc, &fz_identity)); + stroke = pdf_array_get(ctx, ink_list, i); + m = pdf_array_len(ctx, stroke) / 2; + for (k = 0; k < m; ++k) + { + p.x = pdf_array_get_real(ctx, stroke, k*2+0); + p.y = pdf_array_get_real(ctx, stroke, k*2+1); + if (i == 0 && k == 0) + { + rect->x0 = rect->x1 = p.x; + rect->y0 = rect->y1 = p.y; + } + else + fz_include_point_in_rect(rect, &p); + fz_append_printf(ctx, buf, "%g %g %c\n", p.x, p.y, k == 0 ? 'm' : 'l'); + } } + fz_append_printf(ctx, buf, "S"); + fz_expand_rect(rect, lw); +} - resources = pdf_dict_get(ctx, ap_obj, PDF_NAME(Resources)); +static fz_rect pdf_rect_from_quad_point(fz_context *ctx, pdf_obj *qp, int i) +{ + fz_rect box; - contents = fz_new_buffer(ctx, 0); + /* Contrary to the specification, the points within a QuadPoint are NOT + * ordered in a counterclockwise fashion starting with the lower left. + * Experiments with Adobe's implementation indicates a cross-wise + * ordering is intended: ul, ur, ll, lr. + */ - fz_var(dev); - fz_try(ctx) - { - dev = pdf_new_pdf_device(ctx, doc, &fz_identity, &trect, resources, contents); - fz_run_display_list(ctx, disp_list, dev, &inv_page_ctm, &fz_infinite_rect, NULL); - fz_close_device(ctx, dev); - pdf_update_stream(ctx, doc, ap_obj, contents, 0); - } - fz_always(ctx) - { - fz_drop_device(ctx, dev); - fz_drop_buffer(ctx, contents); - } - fz_catch(ctx) - fz_rethrow(ctx); + box.x0 = pdf_array_get_real(ctx, qp, i+4); /* ll */ + box.y0 = pdf_array_get_real(ctx, qp, i+5); + box.x1 = pdf_array_get_real(ctx, qp, i+2); /* ur */ + box.y1 = pdf_array_get_real(ctx, qp, i+3); - /* Mark the appearance as changed - required for partial update */ - annot->has_new_ap = 1; - annot->needs_new_ap = 0; + return box; } -static fz_point * -quadpoints(fz_context *ctx, pdf_document *doc, pdf_obj *annot, int *nout) +static void +pdf_write_highlight_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) { - pdf_obj *quad = pdf_dict_get(ctx, annot, PDF_NAME(QuadPoints)); - fz_point *qp = NULL; + pdf_obj *res_egs, *res_egs_h; + pdf_obj *qp; + float opacity; int i, n; - if (!quad) - return NULL; + *rect = fz_empty_rect; - n = pdf_array_len(ctx, quad); + /* /Resources << /ExtGState << /H << /Type/ExtGState /BM/Multiply /CA %g >> >> >> */ + *res = pdf_new_dict(ctx, annot->page->doc, 1); + res_egs = pdf_dict_put_dict(ctx, *res, PDF_NAME(ExtGState), 1); + res_egs_h = pdf_dict_put_dict(ctx, res_egs, PDF_NAME(H), 2); + pdf_dict_put(ctx, res_egs_h, PDF_NAME(Type), PDF_NAME(ExtGState)); + pdf_dict_put(ctx, res_egs_h, PDF_NAME(BM), PDF_NAME(Multiply)); + opacity = pdf_annot_opacity(ctx, annot); + if (opacity < 1) + pdf_dict_put_real(ctx, res_egs_h, PDF_NAME(ca), opacity); - if (n%8 != 0) - return NULL; + pdf_write_fill_color_appearance(ctx, annot, buf); - fz_var(qp); - fz_try(ctx) - { - qp = fz_malloc_array(ctx, n/2, sizeof(fz_point)); + fz_append_printf(ctx, buf, "/H gs\n"); - for (i = 0; i < n; i += 2) + qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); + n = pdf_array_len(ctx, qp); + if (n > 0) + { + for (i = 0; i < n; i += 8) { - qp[i/2].x = pdf_to_real(ctx, pdf_array_get(ctx, quad, i)); - qp[i/2].y = pdf_to_real(ctx, pdf_array_get(ctx, quad, i+1)); + fz_rect box = pdf_rect_from_quad_point(ctx, qp, i); + float h = box.y1 - box.y0; + float m = h / 4.2425f; /* magic number that matches adobe's appearance */ + fz_append_printf(ctx, buf, "%g %g m\n", box.x0, box.y0); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", + box.x0-m, box.y0+m, box.x0-m, box.y1-m, box.x0, box.y1); + fz_append_printf(ctx, buf, "%g %g l\n", box.x1, box.y1); + fz_append_printf(ctx, buf, "%g %g %g %g %g %g c\n", + box.x1+m, box.y1-m, box.x1+m, box.y0+m, box.x1, box.y0); + fz_append_printf(ctx, buf, "f\n"); + box.x0 -= m; + box.x1 += m; + fz_union_rect(rect, &box); } } - fz_catch(ctx) - { - fz_free(ctx, qp); - fz_rethrow(ctx); - } - - *nout = n/2; - - return qp; } -void pdf_set_markup_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot, float color[3], float alpha, float line_thickness, float line_height) +static void +pdf_write_underline_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) { - fz_path *path = NULL; - fz_stroke_state *stroke = NULL; - fz_device *dev = NULL; - fz_display_list *strike_list = NULL; + pdf_obj *qp; int i, n; - fz_point *qp = quadpoints(ctx, doc, annot->obj, &n); - fz_matrix page_ctm; - pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + *rect = fz_empty_rect; - if (!qp || n <= 0) - return; + pdf_write_stroke_color_appearance(ctx, annot, buf); - fz_var(path); - fz_var(stroke); - fz_var(dev); - fz_var(strike_list); - fz_try(ctx) + qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); + n = pdf_array_len(ctx, qp); + if (n > 0) { - fz_rect rect = fz_empty_rect; - - rect.x0 = rect.x1 = qp[0].x; - rect.y0 = rect.y1 = qp[0].y; - for (i = 0; i < n; i++) - fz_include_point_in_rect(&rect, &qp[i]); - - strike_list = fz_new_display_list(ctx, NULL); - dev = fz_new_list_device(ctx, strike_list); - - for (i = 0; i < n; i += 4) + for (i = 0; i < n; i += 8) { - /* Contrary to the specification, the points within a QuadPoint are NOT ordered - * in a counterclockwise fashion. Experiments with Adobe's implementation - * indicates a cross-wise ordering is intended: ll, lr, ul, ur. */ - fz_point ll = qp[i]; - fz_point lr = qp[i+1]; - fz_point up; - float thickness; + fz_rect box = pdf_rect_from_quad_point(ctx, qp, i); + float h = box.y1 - box.y0; - up.x = qp[i+2].x - qp[i].x; /* ul - ll vector */ - up.y = qp[i+2].y - qp[i].y; + /* Acrobat draws the line at 1/7 of the box width from the bottom + * of the box and 1/16 thick of the box width. */ + fz_append_printf(ctx, buf, "%g w\n", h/16); + fz_append_printf(ctx, buf, "%g %g m\n", box.x0, box.y0 + h/7); + fz_append_printf(ctx, buf, "%g %g l\n", box.x1, box.y0 + h/7); + fz_append_printf(ctx, buf, "S\n"); - ll.x += line_height * up.x; - ll.y += line_height * up.y; - lr.x += line_height * up.x; - lr.y += line_height * up.y; - - thickness = sqrtf(up.x * up.x + up.y * up.y) * line_thickness; - - if (!stroke || fz_abs(stroke->linewidth - thickness) < SMALL_FLOAT) - { - if (stroke) - { - // assert(path) - fz_stroke_path(ctx, dev, path, stroke, &page_ctm, fz_device_rgb(ctx), color, alpha, NULL); - fz_drop_stroke_state(ctx, stroke); - stroke = NULL; - fz_drop_path(ctx, path); - path = NULL; - } + fz_union_rect(rect, &box); + } + } +} - stroke = fz_new_stroke_state(ctx); - stroke->linewidth = thickness; - path = fz_new_path(ctx); - } +static void +pdf_write_strike_out_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) +{ + pdf_obj *qp; + int i, n; - fz_moveto(ctx, path, ll.x, ll.y); - fz_lineto(ctx, path, lr.x, lr.y); - } + pdf_write_stroke_color_appearance(ctx, annot, buf); - if (stroke) + qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); + n = pdf_array_len(ctx, qp); + if (n > 0) + { + *rect = fz_empty_rect; + for (i = 0; i < n; i += 8) { - fz_stroke_path(ctx, dev, path, stroke, &page_ctm, fz_device_rgb(ctx), color, alpha, NULL); - } + fz_rect box = pdf_rect_from_quad_point(ctx, qp, i); + float h = box.y1 - box.y0; - fz_close_device(ctx, dev); + /* Acrobat draws the line at 3/7 of the box width from the bottom + * of the box and 1/16 thick of the box width. */ + fz_append_printf(ctx, buf, "%g w\n", h/16); + fz_append_printf(ctx, buf, "%g %g m\n", box.x0, box.y0 + 3*h/7); + fz_append_printf(ctx, buf, "%g %g l\n", box.x1, box.y0 + 3*h/7); + fz_append_printf(ctx, buf, "S\n"); - fz_transform_rect(&rect, &page_ctm); - pdf_set_annot_appearance(ctx, doc, annot, &rect, strike_list); - } - fz_always(ctx) - { - fz_free(ctx, qp); - fz_drop_device(ctx, dev); - fz_drop_stroke_state(ctx, stroke); - fz_drop_path(ctx, path); - fz_drop_display_list(ctx, strike_list); - } - fz_catch(ctx) - { - fz_rethrow(ctx); + fz_union_rect(rect, &box); + } } } -/* Returns borrowed colorspace reference */ -static fz_colorspace *pdf_to_color(fz_context *ctx, pdf_document *doc, pdf_obj *col, float color[4]) +static void +pdf_write_squiggly_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect) { - fz_colorspace *cs; - int i, ncol = pdf_array_len(ctx, col); - - switch (ncol) - { - case 1: cs = fz_device_gray(ctx); break; - case 3: cs = fz_device_rgb(ctx); break; - case 4: cs = fz_device_cmyk(ctx); break; - default: return NULL; - } + pdf_obj *qp; + int i, n; - for (i = 0; i < ncol; i++) - color[i] = pdf_to_real(ctx, pdf_array_get(ctx, col, i)); + *rect = fz_empty_rect; - return cs; -} + pdf_write_stroke_color_appearance(ctx, annot, buf); -void pdf_update_ink_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot) -{ - fz_path *path = NULL; - fz_stroke_state *stroke = NULL; - fz_device *dev = NULL; - fz_display_list *strike_list = NULL; - fz_colorspace *cs = NULL; - fz_matrix page_ctm; - - pdf_page_transform(ctx, annot->page, NULL, &page_ctm); - - fz_var(path); - fz_var(stroke); - fz_var(dev); - fz_var(strike_list); - fz_var(cs); - fz_try(ctx) + qp = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); + n = pdf_array_len(ctx, qp); + if (n > 0) { - fz_rect rect = fz_empty_rect; - float color[4]; - float width; - pdf_obj *list; - int n, m, i, j; - int empty = 1; - - width = pdf_annot_border(ctx, annot); - if (width == 0.0f) - width = 1.0f; - - list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); - - n = pdf_array_len(ctx, list); - - strike_list = fz_new_display_list(ctx, NULL); - dev = fz_new_list_device(ctx, strike_list); - path = fz_new_path(ctx); - stroke = fz_new_stroke_state(ctx); - stroke->linewidth = width; - stroke->start_cap = stroke->end_cap = FZ_LINECAP_ROUND; - stroke->linejoin = FZ_LINEJOIN_ROUND; - - for (i = 0; i < n; i ++) + for (i = 0; i < n; i += 8) { - fz_point pt_last; - pdf_obj *arc = pdf_array_get(ctx, list, i); - m = pdf_array_len(ctx, arc); - - for (j = 0; j < m-1; j += 2) + fz_rect box = pdf_rect_from_quad_point(ctx, qp, i); + float h = box.y1 - box.y0; + float x = box.x0; + int up = 1; + + fz_append_printf(ctx, buf, "%g w\n", h/16); + fz_append_printf(ctx, buf, "%g %g m\n", box.x0, box.y0); + while (x < box.x1) { - fz_point pt; - pt.x = pdf_to_real(ctx, pdf_array_get(ctx, arc, j)); - pt.y = pdf_to_real(ctx, pdf_array_get(ctx, arc, j+1)); - - if (i == 0 && j == 0) - { - rect.x0 = rect.x1 = pt.x; - rect.y0 = rect.y1 = pt.y; - empty = 0; - } - else - { - fz_include_point_in_rect(&rect, &pt); - } - - if (j == 0) - fz_moveto(ctx, path, pt.x, pt.y); - else - fz_curvetov(ctx, path, pt_last.x, pt_last.y, (pt.x + pt_last.x) / 2, (pt.y + pt_last.y) / 2); - pt_last = pt; + x += h/7; + fz_append_printf(ctx, buf, "%g %g l\n", x, box.y0 + (up?h/7:0)); + up = !up; } - fz_lineto(ctx, path, pt_last.x, pt_last.y); - } - - cs = pdf_to_color(ctx, doc, pdf_dict_get(ctx, annot->obj, PDF_NAME(C)), color); - fz_stroke_path(ctx, dev, path, stroke, &page_ctm, cs, color, 1.0f, NULL); + fz_append_printf(ctx, buf, "S\n"); - fz_expand_rect(&rect, width); - /* - Expand the rectangle by width all around. We cannot use - fz_expand_rect because the rectangle might be empty in the - single point case - */ - if (!empty) - { - rect.x0 -= width; - rect.y0 -= width; - rect.x1 += width; - rect.y1 += width; + box.x1 = x; + fz_expand_rect(&box, h/16); + fz_union_rect(rect, &box); } - - fz_close_device(ctx, dev); - - fz_transform_rect(&rect, &page_ctm); - pdf_set_annot_appearance(ctx, doc, annot, &rect, strike_list); - } - fz_always(ctx) - { - fz_drop_device(ctx, dev); - fz_drop_stroke_state(ctx, stroke); - fz_drop_path(ctx, path); - fz_drop_display_list(ctx, strike_list); - } - fz_catch(ctx) - { - fz_rethrow(ctx); } } -static void add_text(fz_context *ctx, font_info *font_rec, fz_text *text, const char *str, size_t str_len, const fz_matrix *tm_) +static void +pdf_write_caret_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox) { - fz_font *font = font_rec->font->font; - fz_matrix tm = *tm_; - int ucs, gid, n; + float xc = (rect->x0 + rect->x1) / 2; + float yc = (rect->y0 + rect->y1) / 2; - while (str_len > 0) - { - n = fz_chartorune(&ucs, str); - str += n; - str_len -= n; - gid = fz_encode_character(ctx, font, ucs); - fz_show_glyph(ctx, text, font, &tm, gid, ucs, 0, 0, FZ_BIDI_NEUTRAL, FZ_LANG_UNSET); - tm.e += fz_advance_glyph(ctx, font, gid, 0) * font_rec->da_rec.font_size; - } + pdf_write_fill_color_appearance(ctx, annot, buf); + + fz_append_string(ctx, buf, "0 0 m\n"); + fz_append_string(ctx, buf, "10 0 10 7 10 14 c\n"); + fz_append_string(ctx, buf, "10 7 10 0 20 0 c\n"); + fz_append_string(ctx, buf, "f"); + + *rect = fz_make_rect(xc - 10, yc - 7, xc + 10, yc + 7); + *bbox = fz_make_rect(0, 0, 20, 14); } -static fz_text *layout_text(fz_context *ctx, font_info *font_rec, char *str, float x, float y) +static void +pdf_write_icon_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox) { - fz_text *text; - fz_matrix tm; - - fz_scale(&tm, font_rec->da_rec.font_size, font_rec->da_rec.font_size); - tm.e = x; - tm.f = y; + const char *name; + float xc = (rect->x0 + rect->x1) / 2; + float yc = (rect->y0 + rect->y1) / 2; + + if (!pdf_write_fill_color_appearance(ctx, annot, buf)) + fz_append_string(ctx, buf, "1 g\n"); + + fz_append_string(ctx, buf, "1 w\n0.5 0.5 15 15 re b\n"); + fz_append_string(ctx, buf, "0 g\n1 0 0 -1 4 12 cm\n"); + + name = pdf_annot_icon_name(ctx, annot); + + /* Text names */ + if (!strcmp(name, "Comment")) + fz_append_string(ctx, buf, icon_comment); + else if (!strcmp(name, "Key")) + fz_append_string(ctx, buf, icon_key); + else if (!strcmp(name, "Note")) + fz_append_string(ctx, buf, icon_note); + else if (!strcmp(name, "Help")) + fz_append_string(ctx, buf, icon_help); + else if (!strcmp(name, "NewParagraph")) + fz_append_string(ctx, buf, icon_new_paragraph); + else if (!strcmp(name, "Paragraph")) + fz_append_string(ctx, buf, icon_paragraph); + else if (!strcmp(name, "Insert")) + fz_append_string(ctx, buf, icon_insert); + + /* FileAttachment names */ + else if (!strcmp(name, "Graph")) + fz_append_string(ctx, buf, icon_graph); + else if (!strcmp(name, "PushPin")) + fz_append_string(ctx, buf, icon_push_pin); + else if (!strcmp(name, "Paperclip")) + fz_append_string(ctx, buf, icon_paperclip); + else if (!strcmp(name, "Tag")) + fz_append_string(ctx, buf, icon_tag); + + /* Sound names */ + else if (!strcmp(name, "Speaker")) + fz_append_string(ctx, buf, icon_speaker); + else if (!strcmp(name, "Mic")) + fz_append_string(ctx, buf, icon_mic); + + /* Unknown */ + else + fz_append_string(ctx, buf, icon_star); - text = fz_new_text(ctx); + *rect = fz_make_rect(xc - 9, yc - 9, xc + 9, yc + 9); + *bbox = fz_make_rect(0, 0, 16, 16); +} - fz_try(ctx) - { - add_text(ctx, font_rec, text, str, (int)strlen(str), &tm); - } - fz_catch(ctx) +static float +measure_stamp_string(fz_context *ctx, fz_font *font, const char *text) +{ + float w = 0; + while (*text) { - fz_drop_text(ctx, text); - fz_rethrow(ctx); + int c, g; + text += fz_chartorune(&c, text); + /* WinAnsi is close enough to Latin-1 to not matter. Use middle dot for unencodable characters. */ + if (c >= 256) c = REPLACEMENT; + g = fz_encode_character(ctx, font, c); + w += fz_advance_glyph(ctx, font, g, 0); } - - return text; + return w; } -static fz_text *fit_text(fz_context *ctx, font_info *font_rec, const char *str, fz_rect *bounds) +static void +write_simple_string(fz_context *ctx, fz_buffer *buf, const char *a, const char *b) { - float width = bounds->x1 - bounds->x0; - float height = bounds->y1 - bounds->y0; - fz_matrix tm; - fz_text *text = NULL; - fz_text_span *span; - text_splitter splitter; - float ascender; + fz_append_byte(ctx, buf, '('); + while (a < b) + { + int c; + a += fz_chartorune(&c, a); + /* WinAnsi is close enough to Latin-1 to not matter. Use middle dot for unencodable characters. */ + if (c >= 256) c = REPLACEMENT; + if (c == '(' || c == ')' || c == '\\') + fz_append_byte(ctx, buf, '\\'); + fz_append_byte(ctx, buf, c); + } + fz_append_byte(ctx, buf, ')'); +} - /* Initially aim for one-line of text */ - font_rec->da_rec.font_size = height / font_rec->lineheight; +static void +write_stamp_string(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text) +{ + write_simple_string(ctx, buf, text, text+strlen(text)); +} - text_splitter_init(&splitter, font_rec, str, width, height, 1); +static void +write_stamp(fz_context *ctx, fz_buffer *buf, fz_font *font, const char *text, float y, float h) +{ + float tw = measure_stamp_string(ctx, font, text) * h; + fz_append_string(ctx, buf, "BT\n"); + fz_append_printf(ctx, buf, "/Times %g Tf\n", h); + fz_append_printf(ctx, buf, "%g %g Td\n", (190-tw)/2, y); + write_stamp_string(ctx, buf, font, text); + fz_append_string(ctx, buf, " Tj\n"); + fz_append_string(ctx, buf, "ET\n"); +} - fz_var(text); +static void +pdf_write_stamp_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, fz_rect *bbox, pdf_obj **res) +{ + fz_font *font; + pdf_obj *res_font; + pdf_obj *name; + float w, h, xs, ys; + fz_matrix rotate; + + name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name)); + if (!name) + name = PDF_NAME(Draft); + + h = rect->y1 - rect->y0; + w = rect->x1 - rect->x0; + xs = w / 190; + ys = h / 50; + + font = fz_new_base14_font(ctx, "Times-Bold"); fz_try(ctx) { - int i; - while (!splitter.done) + /* /Resources << /Font << /Times %d 0 R >> >> */ + *res = pdf_new_dict(ctx, annot->page->doc, 1); + res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1); + pdf_dict_put_drop(ctx, res_font, PDF_NAME(Times), pdf_add_simple_font(ctx, annot->page->doc, font, 0)); + + pdf_write_fill_color_appearance(ctx, annot, buf); + pdf_write_stroke_color_appearance(ctx, annot, buf); + fz_rotate(&rotate, 0.6f); + fz_append_printf(ctx, buf, "%M cm\n", &rotate); + fz_append_string(ctx, buf, "2 w\n2 2 186 44 re\nS\n"); + + if (name == PDF_NAME(Approved)) + write_stamp(ctx, buf, font, "APPROVED", 13, 30); + else if (name == PDF_NAME(AsIs)) + write_stamp(ctx, buf, font, "AS IS", 13, 30); + else if (name == PDF_NAME(Confidential)) + write_stamp(ctx, buf, font, "CONFIDENTIAL", 17, 20); + else if (name == PDF_NAME(Departmental)) + write_stamp(ctx, buf, font, "DEPARTMENTAL", 17, 20); + else if (name == PDF_NAME(Experimental)) + write_stamp(ctx, buf, font, "EXPERIMENTAL", 17, 20); + else if (name == PDF_NAME(Expired)) + write_stamp(ctx, buf, font, "EXPIRED", 13, 30); + else if (name == PDF_NAME(Final)) + write_stamp(ctx, buf, font, "FINAL", 13, 30); + else if (name == PDF_NAME(ForComment)) + write_stamp(ctx, buf, font, "FOR COMMENT", 17, 20); + else if (name == PDF_NAME(ForPublicRelease)) { - /* Try a layout pass */ - int line = 0; - float font_size; - - fz_drop_text(ctx, text); - text = NULL; - font_size = font_rec->da_rec.font_size; - fz_scale(&tm, font_size, font_size); - tm.e = 0; - tm.f = 0; - - text = fz_new_text(ctx); - - text_splitter_start_pass(&splitter); - - /* Layout unscaled text to a scaled-up width, so that - * the scaled-down text will fit the unscaled width */ - - while (!splitter.done && line < splitter.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 dx, dy; - const char *word = str+splitter.text_start; - size_t wordlen = splitter.text_end-splitter.text_start; - - text_splitter_move(&splitter, -line, &dx, &dy); - tm.e += dx; - tm.f += dy; - add_text(ctx, font_rec, text, word, wordlen, &tm); - } - } - - line ++; - } - - if (!splitter.done) - text_splitter_retry(&splitter); + write_stamp(ctx, buf, font, "FOR PUBLIC", 26, 18); + write_stamp(ctx, buf, font, "RELEASE", 8.5f, 18); } - - /* Post process text with the scale determined by the splitter - * and with the required offset */ - for (span = text->head; span; span = span->next) + else if (name == PDF_NAME(NotApproved)) + write_stamp(ctx, buf, font, "NOT APPROVED", 17, 20); + else if (name == PDF_NAME(NotForPublicRelease)) { - fz_pre_scale(&span->trm, splitter.scale, splitter.scale); - ascender = font_rec->font->ascent * font_rec->da_rec.font_size * splitter.scale / 1000.0f; - for (i = 0; i < span->len; i++) - { - span->items[i].x = span->items[i].x * splitter.scale + bounds->x0; - span->items[i].y = span->items[i].y * splitter.scale + bounds->y1 - ascender; - } + write_stamp(ctx, buf, font, "NOT FOR", 26, 18); + write_stamp(ctx, buf, font, "PUBLIC RELEASE", 8.5, 18); } + else if (name == PDF_NAME(Sold)) + write_stamp(ctx, buf, font, "SOLD", 13, 30); + else if (name == PDF_NAME(TopSecret)) + write_stamp(ctx, buf, font, "TOP SECRET", 14, 26); + else if (name == PDF_NAME(Draft)) + write_stamp(ctx, buf, font, "DRAFT", 13, 30); + else + write_stamp(ctx, buf, font, pdf_to_name(ctx, name), 17, 20); } + fz_always(ctx) + fz_drop_font(ctx, font); fz_catch(ctx) - { - fz_drop_text(ctx, text); fz_rethrow(ctx); - } - return text; + *bbox = fz_make_rect(0, 0, 190, 50); + if (xs > ys) + { + float xc = (rect->x1+rect->x0) / 2; + rect->x0 = xc - 95 * ys; + rect->x1 = xc + 95 * ys; + } + else + { + float yc = (rect->y1+rect->y0) / 2; + rect->y0 = yc - 25 * xs; + rect->y1 = yc + 25 * xs; + } } -static void rect_center(const fz_rect *rect, fz_point *c) +static float +break_simple_string(fz_context *ctx, fz_font *font, float size, const char *a, const char **endp, float maxw) { - c->x = (rect->x0 + rect->x1) / 2.0f; - c->y = (rect->y0 + rect->y1) / 2.0f; + const char *space = NULL; + float space_x, x = 0; + int c, g; + while (*a) + { + a += fz_chartorune(&c, a); + if (c >= 256) + c = REPLACEMENT; + if (c == '\n' || c == '\r') + break; + if (c == ' ') + { + space = a; + space_x = x; + } + g = fz_encode_character(ctx, font, c); + x += fz_advance_glyph(ctx, font, g, 0) * size; + if (space && x > maxw) + return *endp = space, space_x; + } + return *endp = a, x; } -static void center_rect_within_rect(const fz_rect *tofit, const fz_rect *within, fz_matrix *mat) +static void +write_simple_string_with_quadding(fz_context *ctx, fz_buffer *buf, fz_font *font, float size, + const char *a, float maxw, int q) { - float xscale = (within->x1 - within->x0) / (tofit->x1 - tofit->x0); - float yscale = (within->y1 - within->y0) / (tofit->y1 - tofit->y0); - float scale = fz_min(xscale, yscale); - fz_point tofit_center; - fz_point within_center; - - rect_center(within, &within_center); - rect_center(tofit, &tofit_center); - - /* Translate "tofit" to be centered on the origin - * Scale "tofit" to a size that fits within "within" - * Translate "tofit" to "within's" center - * Do all the above in reverse order so that we can use the fz_pre_xx functions */ - fz_translate(mat, within_center.x, within_center.y); - fz_pre_scale(mat, scale, scale); - fz_pre_translate(mat, -tofit_center.x, -tofit_center.y); + const char *b; + float px = 0, x, w; + while (*a) + { + w = break_simple_string(ctx, font, size, a, &b, maxw); + if (b > a) + { + if (q > 0) + { + if (q == 1) + x = (maxw - w) / 2; + else + x = (maxw - w); + fz_append_printf(ctx, buf, "%g %g Td ", x - px, -size); + } + if (b[-1] == '\n' || b[-1] == '\r') + write_simple_string(ctx, buf, a, b-1); + else + write_simple_string(ctx, buf, a, b); + a = b; + px = x; + fz_append_string(ctx, buf, (q > 0) ? "Tj\n" : "'\n"); + } + } } -static const float outline_thickness = 15.0f; - -static void draw_rounded_rect(fz_context *ctx, fz_path *path) +static const char *full_font_name(const char **name) { - fz_moveto(ctx, path, 20.0f, 60.0f); - fz_curveto(ctx, path, 20.0f, 30.0f, 30.0f, 20.0f, 60.0f, 20.0f); - fz_lineto(ctx, path, 340.0f, 20.0f); - fz_curveto(ctx, path, 370.0f, 20.0f, 380.0f, 30.0f, 380.0f, 60.0f); - fz_lineto(ctx, path, 380.0f, 340.0f); - fz_curveto(ctx, path, 380.0f, 370.0f, 370.0f, 380.0f, 340.0f, 380.0f); - fz_lineto(ctx, path, 60.0f, 380.0f); - fz_curveto(ctx, path, 30.0f, 380.0f, 20.0f, 370.0f, 20.0f, 340.0f); - fz_closepath(ctx, path); + if (!strcmp(*name, "Cour")) return "Courier"; + if (!strcmp(*name, "Helv")) return "Helvetica"; + if (!strcmp(*name, "TiRo")) return "Times-Roman"; + if (!strcmp(*name, "Symb")) return "Symbol"; + if (!strcmp(*name, "ZaDb")) return "ZapfDingbats"; + return *name = "Helv", "Helvetica"; } -static void draw_speech_bubble(fz_context *ctx, fz_path *path) +static void +write_variable_text(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, pdf_obj **res, + char *text, const char *fontname, float size, float color[3], int q, + float x, float y, float w, float h, float lw) { - fz_moveto(ctx, path, 199.0f, 315.6f); - fz_curveto(ctx, path, 35.6f, 315.6f, 27.0f, 160.8f, 130.2f, 131.77f); - fz_curveto(ctx, path, 130.2f, 93.07f, 113.0f, 83.4f, 113.0f, 83.4f); - fz_curveto(ctx, path, 138.8f, 73.72f, 173.2f, 83.4f, 190.4f, 122.1f); - fz_curveto(ctx, path, 391.64f, 122.1f, 362.4f, 315.6f, 199.0f, 315.6f); - fz_closepath(ctx, path); -} + pdf_obj *res_font; + fz_font *font; -void pdf_update_text_annot_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot) -{ - static float white[3] = {1.0f, 1.0f, 1.0f}; - static float yellow[3] = {1.0f, 1.0f, 0.0f}; - static float black[3] = {0.0f, 0.0f, 0.0f}; - - fz_display_list *dlist = NULL; - fz_device *dev = NULL; - fz_colorspace *cs = NULL; - fz_path *path = NULL; - fz_stroke_state *stroke = NULL; - fz_matrix page_ctm; - - pdf_page_transform(ctx, annot->page, NULL, &page_ctm); - - fz_var(path); - fz_var(stroke); - fz_var(dlist); - fz_var(dev); - fz_var(cs); + font = fz_new_base14_font(ctx, full_font_name(&fontname)); fz_try(ctx) { - fz_rect rect; - fz_rect bounds; - fz_matrix tm; - - pdf_to_rect(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect)), &rect); - dlist = fz_new_display_list(ctx, NULL); - dev = fz_new_list_device(ctx, dlist); - stroke = fz_new_stroke_state(ctx); - stroke->linewidth = outline_thickness; - stroke->linejoin = FZ_LINEJOIN_ROUND; - - path = fz_new_path(ctx); - draw_rounded_rect(ctx, path); - fz_bound_path(ctx, path, NULL, &fz_identity, &bounds); - fz_expand_rect(&bounds, outline_thickness); - center_rect_within_rect(&bounds, &rect, &tm); - fz_concat(&tm, &tm, &page_ctm); - cs = fz_device_rgb(ctx); /* Borrowed reference */ - fz_fill_path(ctx, dev, path, 0, &tm, cs, yellow, 1.0f, NULL); - fz_stroke_path(ctx, dev, path, stroke, &tm, cs, black, 1.0f, NULL); - fz_drop_path(ctx, path); - path = NULL; - - path = fz_new_path(ctx); - draw_speech_bubble(ctx, path); - fz_fill_path(ctx, dev, path, 0, &tm, cs, white, 1.0f, NULL); - fz_stroke_path(ctx, dev, path, stroke, &tm, cs, black, 1.0f, NULL); - - fz_close_device(ctx, dev); - - fz_transform_rect(&rect, &page_ctm); - pdf_set_annot_appearance(ctx, doc, annot, &rect, dlist); - - /* Drop the cached xobject from the annotation structure to - * force a redraw on next pdf_update_page call */ - pdf_drop_obj(ctx, annot->ap); - annot->ap = NULL; + /* /Resources << /Font << /Helv %d 0 R >> >> */ + *res = pdf_new_dict(ctx, annot->page->doc, 1); + res_font = pdf_dict_put_dict(ctx, *res, PDF_NAME(Font), 1); + pdf_dict_puts_drop(ctx, res_font, fontname, pdf_add_simple_font(ctx, annot->page->doc, font, 0)); + + fz_append_printf(ctx, buf, "%g %g %g rg\n", color[0], color[1], color[2]); + fz_append_string(ctx, buf, "BT\n"); + fz_append_printf(ctx, buf, "/%s %g Tf\n", fontname, size); + fz_append_printf(ctx, buf, "%g TL\n", size); + fz_append_printf(ctx, buf, "%g %g Td\n", x+lw, y+h-lw); + write_simple_string_with_quadding(ctx, buf, font, size, text, w-lw*2, q); + fz_append_string(ctx, buf, "ET\n"); } fz_always(ctx) - { - fz_drop_device(ctx, dev); - fz_drop_display_list(ctx, dlist); - fz_drop_stroke_state(ctx, stroke); - fz_drop_path(ctx, path); - } + fz_drop_font(ctx, font); fz_catch(ctx) - { fz_rethrow(ctx); - } } -void pdf_update_free_text_annot_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot) +static void +pdf_write_free_text_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) { - pdf_obj *obj = annot->obj; - pdf_obj *dr = pdf_dict_get(ctx, annot->page->obj, PDF_NAME(Resources)); - fz_display_list *dlist = NULL; - fz_device *dev = NULL; - font_info font_rec; - fz_text *text = NULL; - fz_matrix page_ctm; + const char *font; + float size, color[3]; + char *text; + float x, y, w, h, lw; + int q; - pdf_page_transform(ctx, annot->page, NULL, &page_ctm); + q = pdf_annot_quadding(ctx, annot); + pdf_annot_default_appearance(ctx, annot, &font, &size, color); - memset(&font_rec, 0, sizeof(font_rec)); + x = rect->x0; + y = rect->y0; + w = rect->x1 - rect->x0; + h = rect->y1 - rect->y0; - /* Set some sane defaults in case the parsing of the font_rec fails */ - font_rec.da_rec.col_size = 1; /* Default to greyscale */ - font_rec.da_rec.font_size = 12; /* Default to 12 point */ + if (pdf_write_fill_color_appearance(ctx, annot, buf)) + fz_append_printf(ctx, buf, "%g %g %g %g re\nf\n", x, y, w, h); - fz_var(dlist); - fz_var(dev); - fz_var(text); - fz_try(ctx) + lw = pdf_write_border_appearance(ctx, annot, buf); + if (lw > 0) { - char *contents = pdf_to_str_buf(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Contents))); - char *da = pdf_to_str_buf(ctx, pdf_dict_get(ctx, obj, PDF_NAME(DA))); - fz_colorspace *cs; - fz_point pos; - fz_rect rect; - - pdf_to_rect(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect)), &rect); - - get_font_info(ctx, doc, dr, da, &font_rec); - - switch (font_rec.da_rec.col_size) - { - default: cs = fz_device_gray(ctx); break; - case 3: cs = fz_device_rgb(ctx); break; - case 4: cs = fz_device_cmyk(ctx); break; - } + fz_append_printf(ctx, buf, "%g %g %g RG\n", color[0], color[1], color[2]); + fz_append_printf(ctx, buf, "%g %g %g %g re\nS\n", x+lw/2, y+lw/2, w-lw, h-lw); + x += size * 0.2f; + w -= size * 0.4f; + } - /* Adjust for the descender */ - pos.x = rect.x0; - pos.y = rect.y0 - font_rec.font->descent * font_rec.da_rec.font_size / 1000.0f; + text = pdf_copy_annot_contents(ctx, annot); + fz_try(ctx) + write_variable_text(ctx, annot, buf, res, text, font, size, color, q, x, y, w, h, lw); + fz_always(ctx) + fz_free(ctx, text); + fz_catch(ctx) + fz_rethrow(ctx); +} - text = layout_text(ctx, &font_rec, contents, pos.x, pos.y); +static void +pdf_write_tx_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) +{ + const char *font; + float size, color[3]; + char *text; + int q; - dlist = fz_new_display_list(ctx, NULL); - dev = fz_new_list_device(ctx, dlist); - fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f, NULL); - fz_close_device(ctx, dev); + q = pdf_annot_quadding(ctx, annot); + pdf_annot_default_appearance(ctx, annot, &font, &size, color); - fz_transform_rect(&rect, &page_ctm); - pdf_set_annot_appearance(ctx, doc, annot, &rect, dlist); - } + text = pdf_field_value(ctx, annot->page->doc, annot->obj); + fz_try(ctx) + write_variable_text(ctx, annot, buf, res, text, font, size, color, q, + rect->x0, rect->y0, rect->x1-rect->x0, rect->y1-rect->y0, 0); fz_always(ctx) - { - fz_drop_device(ctx, dev); - fz_drop_display_list(ctx, dlist); - font_info_fin(ctx, &font_rec); - fz_drop_text(ctx, text); - } + fz_free(ctx, text); fz_catch(ctx) - { fz_rethrow(ctx); - } } -static void draw_logo(fz_context *ctx, fz_path *path) +static void +pdf_write_ch_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) { - fz_moveto(ctx, path, 122.25f, 0.0f); - fz_lineto(ctx, path, 122.25f, 14.249f); - fz_curveto(ctx, path, 125.98f, 13.842f, 129.73f, 13.518f, 133.5f, 13.277f); - fz_lineto(ctx, path, 133.5f, 0.0f); - fz_lineto(ctx, path, 122.25f, 0.0f); - fz_closepath(ctx, path); - fz_moveto(ctx, path, 140.251f, 0.0f); - fz_lineto(ctx, path, 140.251f, 12.935f); - fz_curveto(ctx, path, 152.534f, 12.477f, 165.03f, 12.899f, 177.75f, 14.249f); - fz_lineto(ctx, path, 177.75f, 21.749f); - fz_curveto(ctx, path, 165.304f, 20.413f, 152.809f, 19.871f, 140.251f, 20.348f); - fz_lineto(ctx, path, 140.251f, 39.0f); - fz_lineto(ctx, path, 133.5f, 39.0f); - fz_lineto(ctx, path, 133.5f, 20.704f); - fz_curveto(ctx, path, 129.756f, 20.956f, 126.006f, 21.302f, 122.25f, 21.749f); - fz_lineto(ctx, path, 122.25f, 50.999f); - fz_lineto(ctx, path, 177.751f, 50.999f); - fz_lineto(ctx, path, 177.751f, 0.0f); - fz_lineto(ctx, path, 140.251f, 0.0f); - fz_closepath(ctx, path); - fz_moveto(ctx, path, 23.482f, 129.419f); - fz_curveto(ctx, path, -20.999f, 199.258f, -0.418f, 292.039f, 69.42f, 336.519f); - fz_curveto(ctx, path, 139.259f, 381.0f, 232.04f, 360.419f, 276.52f, 290.581f); - fz_curveto(ctx, path, 321.001f, 220.742f, 300.42f, 127.961f, 230.582f, 83.481f); - fz_curveto(ctx, path, 160.743f, 39.0f, 67.962f, 59.581f, 23.482f, 129.419f); - fz_closepath(ctx, path); - fz_moveto(ctx, path, 254.751f, 128.492f); - fz_curveto(ctx, path, 303.074f, 182.82f, 295.364f, 263.762f, 237.541f, 309.165f); - fz_curveto(ctx, path, 179.718f, 354.568f, 93.57f, 347.324f, 45.247f, 292.996f); - fz_curveto(ctx, path, -3.076f, 238.668f, 4.634f, 157.726f, 62.457f, 112.323f); - fz_curveto(ctx, path, 120.28f, 66.92f, 206.428f, 74.164f, 254.751f, 128.492f); - fz_closepath(ctx, path); - fz_moveto(ctx, path, 111.0f, 98.999f); - fz_curveto(ctx, path, 87.424f, 106.253f, 68.25f, 122.249f, 51.75f, 144.749f); - fz_lineto(ctx, path, 103.5f, 297.749f); - fz_lineto(ctx, path, 213.75f, 298.499f); - fz_curveto(ctx, path, 206.25f, 306.749f, 195.744f, 311.478f, 185.25f, 314.249f); - fz_curveto(ctx, path, 164.22f, 319.802f, 141.22f, 319.775f, 120.0f, 314.999f); - fz_curveto(ctx, path, 96.658f, 309.745f, 77.25f, 298.499f, 55.5f, 283.499f); - fz_curveto(ctx, path, 69.75f, 299.249f, 84.617f, 311.546f, 102.75f, 319.499f); - fz_curveto(ctx, path, 117.166f, 325.822f, 133.509f, 327.689f, 149.25f, 327.749f); - fz_curveto(ctx, path, 164.21f, 327.806f, 179.924f, 326.532f, 193.5f, 320.249f); - fz_curveto(ctx, path, 213.95f, 310.785f, 232.5f, 294.749f, 245.25f, 276.749f); - fz_lineto(ctx, path, 227.25f, 276.749f); - fz_curveto(ctx, path, 213.963f, 276.749f, 197.25f, 263.786f, 197.25f, 250.499f); - fz_lineto(ctx, path, 197.25f, 112.499f); - fz_curveto(ctx, path, 213.75f, 114.749f, 228.0f, 127.499f, 241.5f, 140.999f); - fz_curveto(ctx, path, 231.75f, 121.499f, 215.175f, 109.723f, 197.25f, 101.249f); - fz_curveto(ctx, path, 181.5f, 95.249f, 168.412f, 94.775f, 153.0f, 94.499f); - fz_curveto(ctx, path, 139.42f, 94.256f, 120.75f, 95.999f, 111.0f, 98.999f); - fz_closepath(ctx, path); - fz_moveto(ctx, path, 125.25f, 105.749f); - fz_lineto(ctx, path, 125.25f, 202.499f); - fz_lineto(ctx, path, 95.25f, 117.749f); - fz_curveto(ctx, path, 105.75f, 108.749f, 114.0f, 105.749f, 125.25f, 105.749f); - fz_closepath(ctx, path); + pdf_write_tx_widget_appearance(ctx, annot, buf, rect, res); } -static void insert_signature_appearance_layers(fz_context *ctx, pdf_document *doc, pdf_annot *annot) +static void +pdf_write_widget_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, fz_rect *rect, pdf_obj **res) { - pdf_obj *ap = pdf_dict_getl(ctx, annot->obj, PDF_NAME(AP), PDF_NAME(N), NULL); - pdf_obj *main_ap = NULL; - pdf_obj *frm = NULL; - pdf_obj *n0 = NULL; - fz_rect bbox; - fz_buffer *fzbuf = NULL; - - pdf_to_rect(ctx, pdf_dict_get(ctx, ap, PDF_NAME(BBox)), &bbox); - - fz_var(main_ap); - fz_var(frm); - fz_var(n0); - fz_var(fzbuf); - fz_try(ctx) - { - main_ap = pdf_new_xobject(ctx, doc, &bbox, &fz_identity, NULL, NULL); - frm = pdf_new_xobject(ctx, doc, &bbox, &fz_identity, NULL, NULL); - n0 = pdf_new_xobject(ctx, doc, &bbox, &fz_identity, NULL, NULL); - - pdf_dict_putl(ctx, main_ap, frm, PDF_NAME(Resources), PDF_NAME(XObject), PDF_NAME(FRM), NULL); - fzbuf = fz_new_buffer(ctx, 8); - fz_append_printf(ctx, fzbuf, "/FRM Do"); - pdf_update_stream(ctx, doc, main_ap, fzbuf, 0); - fz_drop_buffer(ctx, fzbuf); - fzbuf = NULL; - - pdf_dict_putl(ctx, frm, n0, PDF_NAME(Resources), PDF_NAME(XObject), PDF_NAME(n0), NULL); - pdf_dict_putl(ctx, frm, ap, PDF_NAME(Resources), PDF_NAME(XObject), PDF_NAME(n2), NULL); - fzbuf = fz_new_buffer(ctx, 8); - fz_append_printf(ctx, fzbuf, "q 1 0 0 1 0 0 cm /n0 Do Q q 1 0 0 1 0 0 cm /n2 Do Q"); - pdf_update_stream(ctx, doc, frm, fzbuf, 0); - fz_drop_buffer(ctx, fzbuf); - fzbuf = NULL; - - fzbuf = fz_new_buffer(ctx, 8); - fz_append_printf(ctx, fzbuf, "%% DSBlank"); - pdf_update_stream(ctx, doc, n0, fzbuf, 0); - fz_drop_buffer(ctx, fzbuf); - fzbuf = NULL; - - pdf_dict_putl(ctx, annot->obj, main_ap, PDF_NAME(AP), PDF_NAME(N), NULL); - } - fz_always(ctx) - { - pdf_drop_obj(ctx, main_ap); - pdf_drop_obj(ctx, frm); - pdf_drop_obj(ctx, n0); - } - fz_catch(ctx) + pdf_obj *ft = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(FT)); + if (pdf_name_eq(ctx, ft, PDF_NAME(Tx))) + pdf_write_tx_widget_appearance(ctx, annot, buf, rect, res); + else if (pdf_name_eq(ctx, ft, PDF_NAME(Ch))) + pdf_write_ch_widget_appearance(ctx, annot, buf, rect, res); + else + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream for %s widgets", pdf_to_name(ctx, ft)); +} + +static void +pdf_write_appearance(fz_context *ctx, pdf_annot *annot, fz_buffer *buf, + fz_rect *rect, fz_rect *bbox, fz_matrix *matrix, pdf_obj **res) +{ + switch (pdf_annot_type(ctx, annot)) { - fz_drop_buffer(ctx, fzbuf); - fz_rethrow(ctx); + default: + fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create appearance stream"); + case PDF_ANNOT_WIDGET: + pdf_write_widget_appearance(ctx, annot, buf, rect, res); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_INK: + pdf_write_ink_appearance(ctx, annot, buf, rect); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_POLYGON: + pdf_write_polygon_appearance(ctx, annot, buf, rect, 1); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_POLY_LINE: + pdf_write_polygon_appearance(ctx, annot, buf, rect, 0); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_LINE: + pdf_write_line_appearance(ctx, annot, buf, rect); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_SQUARE: + pdf_write_square_appearance(ctx, annot, buf, rect); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_CIRCLE: + pdf_write_circle_appearance(ctx, annot, buf, rect); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_CARET: + pdf_write_caret_appearance(ctx, annot, buf, rect, bbox); + *matrix = fz_identity; + break; + case PDF_ANNOT_TEXT: + case PDF_ANNOT_FILE_ATTACHMENT: + case PDF_ANNOT_SOUND: + pdf_write_icon_appearance(ctx, annot, buf, rect, bbox); + *matrix = fz_identity; + break; + case PDF_ANNOT_HIGHLIGHT: + pdf_write_highlight_appearance(ctx, annot, buf, rect, res); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_UNDERLINE: + pdf_write_underline_appearance(ctx, annot, buf, rect); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_STRIKE_OUT: + pdf_write_strike_out_appearance(ctx, annot, buf, rect); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_SQUIGGLY: + pdf_write_squiggly_appearance(ctx, annot, buf, rect); + *matrix = fz_identity; + *bbox = *rect; + break; + case PDF_ANNOT_STAMP: + pdf_write_stamp_appearance(ctx, annot, buf, rect, bbox, res); + *matrix = fz_identity; + break; + case PDF_ANNOT_FREE_TEXT: + pdf_write_free_text_appearance(ctx, annot, buf, rect, res); + *matrix = fz_identity; + *bbox = *rect; + break; } } -/* MuPDF blue */ -static float logo_color[3] = {(float)0x25/(float)0xFF, (float)0x72/(float)0xFF, (float)0xAC/(float)0xFF}; - -void pdf_set_signature_appearance(fz_context *ctx, pdf_document *doc, pdf_annot *annot, char *name, const char *dn, char *date) +void pdf_update_signature_appearance(fz_context *ctx, pdf_annot *annot, const char *name, const char *text, const char *date) { - pdf_obj *obj = annot->obj; - pdf_obj *dr = pdf_dict_getl(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DR), NULL); - fz_display_list *dlist = NULL; - fz_device *dev = NULL; - font_info font_rec; - fz_text *text = NULL; - fz_path *path = NULL; - fz_buffer *fzbuf = NULL; - fz_matrix page_ctm; - - pdf_page_transform(ctx, annot->page, NULL, &page_ctm); - - if (!dr) - pdf_dict_putl_drop(ctx, pdf_trailer(ctx, doc), pdf_new_dict(ctx, doc, 1), PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DR), NULL); - - memset(&font_rec, 0, sizeof(font_rec)); - - fz_var(path); - fz_var(dlist); - fz_var(dev); - fz_var(text); - fz_var(fzbuf); - fz_try(ctx) - { - char *da = pdf_to_str_buf(ctx, pdf_dict_get(ctx, obj, PDF_NAME(DA))); - fz_rect annot_rect; - fz_rect logo_bounds; - fz_matrix logo_tm; - fz_rect rect; - fz_colorspace *cs = fz_device_rgb(ctx); /* Borrowed reference */ + fz_throw(ctx, FZ_ERROR_GENERIC, "writing digital signature appearance streams is not yet implemented"); +} - pdf_to_rect(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect)), &annot_rect); - rect = annot_rect; +void pdf_update_appearance(fz_context *ctx, pdf_annot *annot) +{ + pdf_obj *subtype; + pdf_obj *ap, *ap_n, *as; - dlist = fz_new_display_list(ctx, NULL); - dev = fz_new_list_device(ctx, dlist); + if (annot->ap && !annot->needs_new_ap) + return; - path = fz_new_path(ctx); - draw_logo(ctx, path); - fz_bound_path(ctx, path, NULL, &fz_identity, &logo_bounds); - center_rect_within_rect(&logo_bounds, &rect, &logo_tm); - fz_concat(&logo_tm, &logo_tm, &page_ctm); - fz_fill_path(ctx, dev, path, 0, &logo_tm, cs, logo_color, 1.0f, NULL); + subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); + if (subtype == PDF_NAME(Popup)) + return; + if (subtype == PDF_NAME(Link)) + return; - get_font_info(ctx, doc, dr, da, &font_rec); + as = pdf_dict_get(ctx, annot->obj, PDF_NAME(AS)); + ap = pdf_dict_get(ctx, annot->obj, PDF_NAME(AP)); + ap_n = pdf_dict_get(ctx, ap, PDF_NAME(N)); + if (!pdf_is_stream(ctx, ap_n)) + ap_n = pdf_dict_get(ctx, ap_n, as); - switch (font_rec.da_rec.col_size) + if (pdf_is_stream(ctx, ap_n)) + { + fz_try(ctx) { - case 1: cs = fz_device_gray(ctx); break; - case 3: cs = fz_device_rgb(ctx); break; - case 4: cs = fz_device_cmyk(ctx); break; + pdf_drop_obj(ctx, annot->ap); + annot->ap = NULL; + annot->ap = pdf_keep_obj(ctx, ap_n); } - - /* Display the name in the left-hand half of the form field */ - rect.x1 = (rect.x0 + rect.x1)/2.0f; - text = fit_text(ctx, &font_rec, name, &rect); - fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f, NULL); - fz_drop_text(ctx, text); - text = NULL; - - /* Display the distinguished name in the right-hand half */ - fzbuf = fz_new_buffer(ctx, 256); - fz_append_printf(ctx, fzbuf, "Digitally signed by %s", name); - fz_append_printf(ctx, fzbuf, "\nDN: %s", dn); - if (date) - fz_append_printf(ctx, fzbuf, "\nDate: %s", date); - rect = annot_rect; - rect.x0 = (rect.x0 + rect.x1)/2.0f; - text = fit_text(ctx, &font_rec, fz_string_from_buffer(ctx, fzbuf), &rect); - fz_fill_text(ctx, dev, text, &page_ctm, cs, font_rec.da_rec.col, 1.0f, NULL); - - fz_close_device(ctx, dev); - - rect = annot_rect; - fz_transform_rect(&rect, &page_ctm); - pdf_set_annot_appearance(ctx, doc, annot, &rect, dlist); - - /* Drop the cached xobject from the annotation structure to - * force a redraw on next pdf_update_page call */ - pdf_drop_obj(ctx, annot->ap); - annot->ap = NULL; - - insert_signature_appearance_layers(ctx, doc, annot); - } - fz_always(ctx) - { - fz_drop_device(ctx, dev); - fz_drop_display_list(ctx, dlist); - font_info_fin(ctx, &font_rec); - fz_drop_path(ctx, path); - fz_drop_text(ctx, text); - fz_drop_buffer(ctx, fzbuf); - } - fz_catch(ctx) - { - fz_rethrow(ctx); + fz_catch(ctx) + fz_warn(ctx, "cannot load appearance stream"); } -} - -void pdf_update_appearance(fz_context *ctx, pdf_annot *annot) -{ - pdf_document *doc = annot->page->doc; - pdf_obj *obj = annot->obj; - pdf_obj *ap = pdf_dict_get(ctx, obj, PDF_NAME(AP)); - if (!ap || !pdf_dict_get(ctx, ap, PDF_NAME(N)) || pdf_obj_is_dirty(ctx, obj) || annot->needs_new_ap) + if (!annot->ap || annot->needs_new_ap) { - enum pdf_annot_type type = pdf_annot_type(ctx, annot); - switch (type) + fz_rect rect, bbox; + fz_matrix matrix = fz_identity; + fz_buffer *buf = fz_new_buffer(ctx, 1024); + pdf_obj *res = NULL; + fz_var(res); + fz_try(ctx) { - case PDF_ANNOT_WIDGET: - switch (pdf_field_type(ctx, doc, obj)) + pdf_to_rect(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(Rect)), &rect); + pdf_write_appearance(ctx, annot, buf, &rect, &bbox, &matrix, &res); + pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), &rect); + + if (!ap_n) { - case PDF_WIDGET_TYPE_TEXT: + if (!ap) { - #if 0 - pdf_obj *formatting = pdf_dict_getl(ctx, obj, PDF_NAME(AA), PDF_NAME(F), NULL); - if (formatting && doc->js) - { - /* Apply formatting */ - pdf_js_event e; - e.target = obj; - e.value = pdf_field_value(ctx, doc, obj); - fz_try(ctx) - { - pdf_js_setup_event(doc->js, &e); - } - fz_always(ctx) - { - fz_free(ctx, e.value); - } - fz_catch(ctx) - { - fz_rethrow(ctx); - } - execute_action(ctx, doc, obj, formatting); - /* Update appearance from JS event.value */ - pdf_update_text_appearance(ctx, doc, obj, pdf_js_get_event(doc->js)->value); - } - else - #endif - { - /* Update appearance from field value */ - pdf_update_text_appearance(ctx, doc, obj, NULL); - } + ap = pdf_new_dict(ctx, annot->page->doc, 1); + pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(AP), ap); } - break; - case PDF_WIDGET_TYPE_PUSHBUTTON: - pdf_update_pushbutton_appearance(ctx, doc, obj); - break; - case PDF_WIDGET_TYPE_LISTBOX: - pdf_update_listbox_appearance(ctx, doc, obj); - break; - case PDF_WIDGET_TYPE_COMBOBOX: - pdf_update_combobox_appearance(ctx, doc, obj); - break; + ap_n = pdf_new_xobject(ctx, annot->page->doc, &bbox, &matrix, res, buf); + pdf_dict_put_drop(ctx, ap, PDF_NAME(N), ap_n); } - annot->has_new_ap = 1; - break; - case PDF_ANNOT_TEXT: - pdf_update_text_annot_appearance(ctx, doc, annot); - break; - case PDF_ANNOT_FREE_TEXT: - pdf_update_free_text_annot_appearance(ctx, doc, annot); - break; - case PDF_ANNOT_STRIKE_OUT: - case PDF_ANNOT_UNDERLINE: - case PDF_ANNOT_HIGHLIGHT: - pdf_update_text_markup_appearance(ctx, doc, annot, type); - break; - case PDF_ANNOT_INK: - pdf_update_ink_appearance(ctx, doc, annot); - break; - default: - break; + else + { + pdf_update_xobject(ctx, annot->page->doc, ap_n, &bbox, &matrix, res, buf); + } + + pdf_drop_obj(ctx, annot->ap); + annot->ap = NULL; + annot->ap = pdf_keep_obj(ctx, ap_n); + } + fz_always(ctx) + { + fz_drop_buffer(ctx, buf); + pdf_drop_obj(ctx, res); } + fz_catch(ctx) + fz_warn(ctx, "cannot create appearance stream"); - pdf_clean_obj(ctx, obj); + annot->needs_new_ap = 0; + annot->has_new_ap = 1; } } @@ -2539,9 +1063,15 @@ pdf_update_annot(fz_context *ctx, pdf_annot *annot) pdf_document *doc = annot->page->doc; pdf_obj *obj, *ap, *as, *n; - pdf_update_appearance(ctx, annot); - + /* TODO: handle form field updates without using the annot pdf_obj dirty flag */ obj = annot->obj; + if (pdf_obj_is_dirty(ctx, obj)) + { + pdf_clean_obj(ctx, obj); + annot->needs_new_ap = 1; + } + + pdf_update_appearance(ctx, annot); ap = pdf_dict_get(ctx, obj, PDF_NAME(AP)); as = pdf_dict_get(ctx, obj, PDF_NAME(AS)); diff --git a/source/pdf/pdf-form.c b/source/pdf/pdf-form.c index 96b8d652..167e1729 100644 --- a/source/pdf/pdf-form.c +++ b/source/pdf/pdf-form.c @@ -976,45 +976,36 @@ void pdf_field_set_fill_color(fz_context *ctx, pdf_document *doc, pdf_obj *field void pdf_field_set_text_color(fz_context *ctx, pdf_document *doc, pdf_obj *field, pdf_obj *col) { - pdf_da_info di; - fz_buffer *fzbuf = NULL; - char *da = pdf_to_str_buf(ctx, pdf_get_inheritable(ctx, doc, field, PDF_NAME(DA))); - unsigned char *buf; - int ilen; - size_t len; - pdf_obj *daobj; - - memset(&di, 0, sizeof(di)); - - fz_var(fzbuf); - fz_var(di); - fz_try(ctx) - { - int i; + char buf[100]; + const char *font; + float size, color[3], black; + const char *da = pdf_to_str_buf(ctx, pdf_get_inheritable(ctx, doc, field, PDF_NAME(DA))); - pdf_parse_da(ctx, da, &di); - di.col_size = pdf_array_len(ctx, col); + pdf_parse_default_appearance(ctx, da, &font, &size, color); - ilen = fz_mini(di.col_size, (int)nelem(di.col)); - for (i = 0; i < ilen; i++) - di.col[i] = pdf_array_get_real(ctx, col, i); - - fzbuf = fz_new_buffer(ctx, 0); - pdf_fzbuf_print_da(ctx, fzbuf, &di); - len = fz_buffer_storage(ctx, fzbuf, &buf); - daobj = pdf_new_string(ctx, (char *)buf, len); - pdf_dict_put_drop(ctx, field, PDF_NAME(DA), daobj); - pdf_field_mark_dirty(ctx, doc, field); - } - fz_always(ctx) + switch (pdf_array_len(ctx, col)) { - pdf_da_info_fin(ctx, &di); - fz_drop_buffer(ctx, fzbuf); - } - fz_catch(ctx) - { - fz_warn(ctx, "%s", fz_caught_message(ctx)); + default: + color[0] = color[1] = color[2] = 0; + case 1: + color[0] = color[1] = color[2] = pdf_array_get_real(ctx, col, 0); + break; + case 3: + color[0] = pdf_array_get_real(ctx, col, 0); + color[1] = pdf_array_get_real(ctx, col, 1); + color[2] = pdf_array_get_real(ctx, col, 2); + break; + case 4: + black = pdf_array_get_real(ctx, col, 3); + color[0] = 1 - fz_min(1, pdf_array_get_real(ctx, col, 0) + black); + color[1] = 1 - fz_min(1, pdf_array_get_real(ctx, col, 1) + black); + color[2] = 1 - fz_min(1, pdf_array_get_real(ctx, col, 2) + black); + break; } + + pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, color); + pdf_dict_put_string(ctx, field, PDF_NAME(DA), buf, strlen(buf)); + pdf_field_mark_dirty(ctx, doc, field); } fz_rect *pdf_bound_widget(fz_context *ctx, pdf_widget *widget, fz_rect *rect) diff --git a/source/pdf/pdf-object.c b/source/pdf/pdf-object.c index b00bf286..ec66376a 100644 --- a/source/pdf/pdf-object.c +++ b/source/pdf/pdf-object.c @@ -2122,6 +2122,18 @@ int pdf_obj_refs(fz_context *ctx, pdf_obj *obj) /* Convenience functions */ +pdf_obj *pdf_dict_get_inheritable(fz_context *ctx, pdf_obj *dict, pdf_obj *key) +{ + pdf_obj *val = NULL; + while (!val && dict) + { + val = pdf_dict_get(ctx, dict, key); + if (!val) + dict = pdf_dict_get(ctx, dict, PDF_NAME(Parent)); + } + return val; +} + void pdf_dict_put_bool(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int x) { pdf_dict_put(ctx, dict, key, x ? PDF_TRUE : PDF_FALSE); diff --git a/source/pdf/pdf-signature.c b/source/pdf/pdf-signature.c index ca06fa56..6ccb7ab2 100644 --- a/source/pdf/pdf-signature.c +++ b/source/pdf/pdf-signature.c @@ -72,6 +72,7 @@ void pdf_sign_signature(fz_context *ctx, pdf_document *doc, pdf_widget *widget, pdf_signature_set_value(ctx, doc, wobj, signer); pdf_to_rect(ctx, pdf_dict_get(ctx, wobj, PDF_NAME(Rect)), &rect); + /* Create an appearance stream only if the signature is intended to be visible */ if (!fz_is_empty_rect(&rect)) { @@ -95,7 +96,7 @@ void pdf_sign_signature(fz_context *ctx, pdf_document *doc, pdf_widget *widget, fz_append_printf(ctx, fzbuf, ", c=%s", dn->c); dn_str = fz_string_from_buffer(ctx, fzbuf); - pdf_set_signature_appearance(ctx, doc, (pdf_annot *)widget, dn->cn, dn_str, NULL); + pdf_update_signature_appearance(ctx, (pdf_annot *)widget, dn->cn, dn_str, NULL); } } fz_always(ctx) -- cgit v1.2.3