diff options
author | Paul Gardiner <paulg.artifex@glidos.net> | 2013-03-18 10:59:43 +0000 |
---|---|---|
committer | Robin Watts <robin.watts@artifex.com> | 2013-03-18 11:32:55 +0000 |
commit | 65d12cd5b97b4b6b43767713917bb422e7e1a834 (patch) | |
tree | 4a6096af48082192f87754c75fc3c561ef69528d /pdf | |
parent | 1b4b5fdfa6b74827631d42efd2a61226125f036b (diff) | |
download | mupdf-65d12cd5b97b4b6b43767713917bb422e7e1a834.tar.xz |
Auto-generate appearance streams for strikeout, underline, highlight
This fixes bug #693664, and also simplifies app code.
The example file attached to the bug produces strange results, but that
is because the QuadPoint information is incorrect.
Diffstat (limited to 'pdf')
-rw-r--r-- | pdf/mupdf-internal.h | 3 | ||||
-rw-r--r-- | pdf/pdf_annot.c | 185 | ||||
-rw-r--r-- | pdf/pdf_form.c | 56 |
3 files changed, 227 insertions, 17 deletions
diff --git a/pdf/mupdf-internal.h b/pdf/mupdf-internal.h index 74db6d1b..b38f7095 100644 --- a/pdf/mupdf-internal.h +++ b/pdf/mupdf-internal.h @@ -595,6 +595,7 @@ void pdf_remove_item(fz_context *ctx, fz_store_free_fn *free, pdf_obj *key); int pdf_has_unsaved_changes(pdf_document *doc); int pdf_pass_event(pdf_document *doc, pdf_page *page, fz_ui_event *ui_event); void pdf_update_page(pdf_document *doc, pdf_page *page); +fz_annot_type pdf_annot_obj_type(pdf_obj *obj); pdf_annot *pdf_poll_changed_annot(pdf_document *idoc, pdf_page *page); fz_widget *pdf_focused_widget(pdf_document *doc); fz_widget *pdf_first_widget(pdf_document *doc, pdf_page *page); @@ -611,6 +612,8 @@ pdf_annot *pdf_create_annot(pdf_document *doc, pdf_page *page, fz_annot_type typ void pdf_delete_annot(pdf_document *doc, pdf_page *page, pdf_annot *annot); void pdf_set_annot_appearance(pdf_document *doc, pdf_annot *annot, fz_rect *rect, fz_display_list *disp_list); void pdf_set_markup_annot_quadpoints(pdf_document *doc, pdf_annot *annot, fz_point *qp, int n); +void pdf_set_markup_obj_appearance(pdf_document *doc, pdf_obj *annot, float color[3], float alpha, float line_thickness, float line_height); +void pdf_set_markup_appearance(pdf_document *doc, pdf_annot *annot, float color[3], float alpha, float line_thickness, float line_height); void pdf_set_doc_event_callback(pdf_document *doc, fz_doc_event_cb *event_cb, void *data); void pdf_event_issue_alert(pdf_document *doc, fz_alert_event *event); diff --git a/pdf/pdf_annot.c b/pdf/pdf_annot.c index ccb2549f..5252ce9a 100644 --- a/pdf/pdf_annot.c +++ b/pdf/pdf_annot.c @@ -1,6 +1,8 @@ #include "fitz-internal.h" #include "mupdf-internal.h" +#define SMALL_FLOAT (0.00001) + static pdf_obj * resolve_dest_rec(pdf_document *xref, pdf_obj *dest, int depth) { @@ -356,7 +358,7 @@ pdf_transform_annot(pdf_annot *annot) fz_pre_scale(fz_translate(&annot->matrix, x, y), w, h); } -static int annot_type(pdf_obj *obj) +fz_annot_type pdf_annot_obj_type(pdf_obj *obj) { char *subtype = pdf_to_name(pdf_dict_gets(obj, "Subtype")); if (!strcmp(subtype, "Text")) @@ -510,7 +512,7 @@ pdf_load_annots(pdf_document *xref, pdf_obj *annots, pdf_page *page) annot->pagerect = annot->rect; fz_transform_rect(&annot->pagerect, &page->ctm); annot->ap = NULL; - annot->annot_type = annot_type(obj); + annot->annot_type = pdf_annot_obj_type(obj); annot->widget_type = annot->annot_type == FZ_ANNOT_WIDGET ? pdf_field_type(xref, obj) : FZ_WIDGET_TYPE_NOT_WIDGET; if (pdf_is_stream(xref, pdf_to_num(n), pdf_to_gen(n))) @@ -766,6 +768,44 @@ pdf_delete_annot(pdf_document *doc, pdf_page *page, pdf_annot *annot) doc->dirty = 1; } +static fz_point * +quadpoints(pdf_document *doc, pdf_obj *annot, int *nout) +{ + fz_context *ctx = doc->ctx; + pdf_obj *quad = pdf_dict_gets(annot, "QuadPoints"); + fz_point *qp = NULL; + int i, n; + + if (!quad) + return NULL; + + n = pdf_array_len(quad); + + if (n%8 != 0) + return NULL; + + fz_var(qp); + fz_try(ctx) + { + qp = fz_malloc_array(ctx, n/2, sizeof(fz_point)); + + for (i = 0; i < n; i += 2) + { + qp[i/2].x = pdf_to_real(pdf_array_get(quad, i)); + qp[i/2].y = pdf_to_real(pdf_array_get(quad, i+1)); + } + } + fz_catch(ctx) + { + fz_free(ctx, qp); + fz_rethrow(ctx); + } + + *nout = n/2; + + return qp; +} + void pdf_set_markup_annot_quadpoints(pdf_document *doc, pdf_annot *annot, fz_point *qp, int n) { @@ -792,14 +832,17 @@ pdf_set_markup_annot_quadpoints(pdf_document *doc, pdf_annot *annot, fz_point *q } void -pdf_set_annot_appearance(pdf_document *doc, pdf_annot *annot, fz_rect *rect, fz_display_list *disp_list) +pdf_set_annot_obj_appearance(pdf_document *doc, pdf_obj *obj, const fz_matrix *page_ctm, fz_rect *rect, fz_display_list *disp_list) { fz_context *ctx = doc->ctx; fz_matrix ctm; fz_matrix mat = fz_identity; fz_device *dev = NULL; + pdf_xobject *xobj = NULL; - fz_invert_matrix(&ctm, &annot->page->ctm); + fz_invert_matrix(&ctm, page_ctm); + + fz_var(dev); fz_try(ctx) { pdf_obj *ap_obj; @@ -807,17 +850,17 @@ pdf_set_annot_appearance(pdf_document *doc, pdf_annot *annot, fz_rect *rect, fz_ fz_transform_rect(&trect, &ctm); - pdf_dict_puts_drop(annot->obj, "Rect", pdf_new_rect(ctx, &trect)); + pdf_dict_puts_drop(obj, "Rect", pdf_new_rect(ctx, &trect)); /* See if there is a current normal appearance */ - ap_obj = pdf_dict_getp(annot->obj, "AP/N"); - if (!pdf_is_stream(doc, pdf_to_num(annot->obj), pdf_to_gen(annot->obj))) + ap_obj = pdf_dict_getp(obj, "AP/N"); + if (!pdf_is_stream(doc, pdf_to_num(obj), pdf_to_gen(obj))) ap_obj = NULL; if (ap_obj == NULL) { ap_obj = pdf_new_xobject(doc, &trect, &mat); - pdf_dict_putp_drop(annot->obj, "AP/N", ap_obj); + pdf_dict_putp_drop(obj, "AP/N", ap_obj); } else { @@ -825,18 +868,18 @@ pdf_set_annot_appearance(pdf_document *doc, pdf_annot *annot, fz_rect *rect, fz_ pdf_dict_puts_drop(ap_obj, "Matrix", pdf_new_matrix(ctx, &mat)); } - /* Remove annot reference to the xobject and don't recreate it - so that pdf_update_page counts it as dirty */ - pdf_drop_xobject(ctx, annot->ap); - annot->ap = NULL; - - annot->rect = trect; - annot->pagerect = *rect; - dev = pdf_new_pdf_device(doc, ap_obj, pdf_dict_gets(ap_obj, "Resources"), &mat); fz_run_display_list(disp_list, dev, &ctm, &fz_infinite_rect, NULL); fz_free_device(dev); + /* Mark the appearance as changed - required for partial update */ + xobj = pdf_load_xobject(doc, ap_obj); + if (xobj) + { + xobj->iteration++; + pdf_drop_xobject(ctx, xobj); + } + doc->dirty = 1; } fz_catch(ctx) @@ -845,3 +888,113 @@ pdf_set_annot_appearance(pdf_document *doc, pdf_annot *annot, fz_rect *rect, fz_ fz_rethrow(ctx); } } + +static void update_rect(fz_context *ctx, pdf_annot *annot) +{ + pdf_to_rect(ctx, pdf_dict_gets(annot->obj, "Rect"), &annot->rect); + annot->pagerect = annot->rect; + fz_transform_rect(&annot->pagerect, &annot->page->ctm); +} + +void +pdf_set_annot_appearance(pdf_document *doc, pdf_annot *annot, fz_rect *rect, fz_display_list *disp_list) +{ + pdf_set_annot_obj_appearance(doc, annot->obj, &annot->page->ctm, rect, disp_list); + update_rect(doc->ctx, annot); +} + +void +pdf_set_markup_obj_appearance(pdf_document *doc, pdf_obj *annot, float color[3], float alpha, float line_thickness, float line_height) +{ + fz_context *ctx = doc->ctx; + fz_path *path = NULL; + fz_stroke_state *stroke = NULL; + fz_device *dev = NULL; + fz_display_list *strike_list = NULL; + int i, n; + fz_point *qp = quadpoints(doc, annot, &n); + + if (!qp || n <= 0) + return; + + fz_var(path); + fz_var(stroke); + fz_var(dev); + fz_var(strike_list); + fz_try(ctx) + { + 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); + dev = fz_new_list_device(ctx, strike_list); + + for (i = 0; i < n; i += 4) + { + fz_point pt0 = qp[i]; + fz_point pt1 = qp[i+1]; + fz_point up; + float thickness; + + up.x = qp[i+2].x - qp[i+1].x; + up.y = qp[i+2].y - qp[i+1].y; + + pt0.x += line_height * up.x; + pt0.y += line_height * up.y; + pt1.x += line_height * up.x; + pt1.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(dev, path, stroke, &fz_identity, fz_device_rgb, color, alpha); + fz_drop_stroke_state(ctx, stroke); + stroke = NULL; + fz_free_path(ctx, path); + path = NULL; + } + + stroke = fz_new_stroke_state(ctx); + stroke->linewidth = thickness; + path = fz_new_path(ctx); + } + + fz_moveto(ctx, path, pt0.x, pt0.y); + fz_lineto(ctx, path, pt1.x, pt1.y); + } + + if (stroke) + { + fz_stroke_path(dev, path, stroke, &fz_identity, fz_device_rgb, color, alpha); + } + + pdf_set_annot_obj_appearance(doc, annot, &fz_identity, &rect, strike_list); + } + fz_always(ctx) + { + fz_free(ctx, qp); + fz_free_device(dev); + fz_drop_stroke_state(ctx, stroke); + fz_free_path(ctx, path); + fz_free_display_list(ctx, strike_list); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + +void +pdf_set_markup_appearance(pdf_document *doc, pdf_annot *annot, float color[3], float alpha, float line_thickness, float line_height) +{ + pdf_set_markup_obj_appearance(doc, annot->obj, color, alpha, line_thickness, line_height); + update_rect(doc->ctx, annot); +} diff --git a/pdf/pdf_form.c b/pdf/pdf_form.c index 118c818f..168e56aa 100644 --- a/pdf/pdf_form.c +++ b/pdf/pdf_form.c @@ -3,6 +3,11 @@ #define MATRIX_COEFS (6) +#define STRIKE_HEIGHT (0.375f) +#define UNDERLINE_HEIGHT (0.075f) +#define LINE_THICKNESS (0.07f) +#define SMALL_FLOAT (0.00001) + enum { F_Invisible = 1 << (1-1), @@ -1713,12 +1718,55 @@ static void execute_action(pdf_document *doc, pdf_obj *obj, pdf_obj *a) } } +static void update_text_markup_appearance(pdf_document *doc, pdf_obj *annot, fz_annot_type type) +{ + fz_context *ctx = doc->ctx; + float color[3]; + float alpha; + float line_height; + float line_thickness; + + switch (type) + { + case FZ_ANNOT_HIGHLIGHT: + color[0] = 1.0; + color[1] = 1.0; + color[2] = 0.0; + alpha = 0.5; + line_thickness = 1.0; + line_height = 0.5; + break; + case FZ_ANNOT_UNDERLINE: + color[0] = 0.0; + color[1] = 0.0; + color[2] = 1.0; + alpha = 1.0; + line_thickness = LINE_THICKNESS; + line_height = UNDERLINE_HEIGHT; + break; + case FZ_ANNOT_STRIKEOUT: + color[0] = 1.0; + color[1] = 0.0; + color[2] = 0.0; + alpha = 1.0; + line_thickness = LINE_THICKNESS; + line_height = STRIKE_HEIGHT; + break; + default: + return; + } + + pdf_set_markup_obj_appearance(doc, annot, color, alpha, line_thickness, line_height); +} + void pdf_update_appearance(pdf_document *doc, pdf_obj *obj) { if (!pdf_dict_gets(obj, "AP") || pdf_dict_gets(obj, "Dirty")) { - if (!strcmp(pdf_to_name(pdf_dict_gets(obj, "Subtype")), "Widget")) + fz_annot_type type = pdf_annot_obj_type(obj); + switch (type) { + case FZ_ANNOT_WIDGET: switch(pdf_field_type(doc, obj)) { case FZ_WIDGET_TYPE_TEXT: @@ -1753,6 +1801,12 @@ void pdf_update_appearance(pdf_document *doc, pdf_obj *obj) update_combobox_appearance(doc, obj); break; } + break; + case FZ_ANNOT_STRIKEOUT: + case FZ_ANNOT_UNDERLINE: + case FZ_ANNOT_HIGHLIGHT: + update_text_markup_appearance(doc, obj, type); + break; } pdf_dict_dels(obj, "Dirty"); |