From 3cc324ea9eaec9fdef84615dac325e55267d757c Mon Sep 17 00:00:00 2001 From: Paul Gardiner Date: Tue, 25 Feb 2014 11:18:53 +0000 Subject: Support text (aka sticky note) annotations --- source/pdf/pdf-annot.c | 34 ++++++++++ source/pdf/pdf-appearance.c | 148 ++++++++++++++++++++++++++++++++++++-------- source/pdf/pdf-form.c | 3 + 3 files changed, 159 insertions(+), 26 deletions(-) (limited to 'source') diff --git a/source/pdf/pdf-annot.c b/source/pdf/pdf-annot.c index 0aada455..e9702125 100644 --- a/source/pdf/pdf-annot.c +++ b/source/pdf/pdf-annot.c @@ -1,5 +1,7 @@ #include "mupdf/pdf.h" +#define TEXT_ANNOT_SIZE (25.0) + static pdf_obj * resolve_dest_rec(pdf_document *doc, pdf_obj *dest, fz_link_kind kind, int depth) { @@ -967,6 +969,38 @@ static void find_free_font_name(pdf_obj *fdict, char *buf, int buf_size) } } +void pdf_set_text_annot_position(pdf_document *doc, pdf_annot *annot, fz_point pt) +{ + fz_matrix ctm; + fz_rect rect; + int flags; + + fz_invert_matrix(&ctm, &annot->page->ctm); + rect.x0 = pt.x; + rect.x1 = pt.x + TEXT_ANNOT_SIZE; + rect.y0 = pt.y; + rect.y1 = pt.y + TEXT_ANNOT_SIZE; + fz_transform_rect(&rect, &ctm); + + pdf_dict_puts_drop(annot->obj, "Rect", pdf_new_rect(doc, &rect)); + + flags = pdf_to_int(pdf_dict_gets(annot->obj, "F")); + flags |= (F_NoZoom|F_NoRotate); + pdf_dict_puts_drop(annot->obj, "F", pdf_new_int(doc, flags)); + + update_rect(doc->ctx, annot); +} + +void pdf_set_annot_contents(pdf_document *doc, pdf_annot *annot, char *text) +{ + pdf_dict_puts_drop(annot->obj, "Contents", pdf_new_string(doc, text, strlen(text))); +} + +char *pdf_annot_contents(pdf_document *doc, pdf_annot *annot) +{ + return pdf_to_str_buf(pdf_dict_getp(annot->obj, "Contents")); +} + void pdf_set_free_text_details(pdf_document *doc, pdf_annot *annot, fz_point *pos, char *text, char *font_name, float font_size, float color[3]) { fz_context *ctx = doc->ctx; diff --git a/source/pdf/pdf-appearance.c b/source/pdf/pdf-appearance.c index e57a3202..07b60d82 100644 --- a/source/pdf/pdf-appearance.c +++ b/source/pdf/pdf-appearance.c @@ -1797,6 +1797,128 @@ static fz_text *fit_text(fz_context *ctx, font_info *font_rec, char *str, fz_rec return text; } +static void rect_center(const fz_rect *rect, fz_point *c) +{ + c->x = (rect->x0 + rect->x1) / 2.0f; + c->y = (rect->y0 + rect->y1) / 2.0f; +} + +static void center_rect_within_rect(const fz_rect *tofit, const fz_rect *within, fz_matrix *mat) +{ + 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); +} + +static const float outline_thickness = 15.0f; + +static void draw_rounded_rect(fz_context *ctx, fz_path *path) +{ + fz_moveto(ctx, path, 20.0, 60.0); + fz_curveto(ctx, path, 20.0, 30.0, 30.0, 20.0, 60.0, 20.0); + fz_lineto(ctx, path, 340.0, 20.0); + fz_curveto(ctx, path, 370.0, 20.0, 380.0, 30.0, 380.0, 60.0); + fz_lineto(ctx, path, 380.0, 340.0); + fz_curveto(ctx, path, 380.0, 370.0, 370.0, 380.0, 340.0, 380.0); + fz_lineto(ctx, path, 60.0, 380.0); + fz_curveto(ctx, path, 30.0, 380.0, 20.0, 370.0, 20.0, 340.0); + fz_closepath(ctx, path); +} + +static void draw_speech_bubble(fz_context *ctx, fz_path *path) +{ + 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); +} + +void pdf_update_text_annot_appearance(pdf_document *doc, pdf_annot *annot) +{ + static float white[3] = {1.0, 1.0, 1.0}; + static float yellow[3] = {1.0, 1.0, 0.0}; + static float black[3] = {0.0, 0.0, 0.0}; + fz_context *ctx = doc->ctx; + const fz_matrix *page_ctm = &annot->page->ctm; + pdf_obj *obj = annot->obj; + fz_display_list *dlist = NULL; + fz_device *dev = NULL; + fz_colorspace *cs = NULL; + fz_path *path = NULL; + fz_stroke_state *stroke = NULL; + + fz_var(path); + fz_var(stroke); + fz_var(dlist); + fz_var(dev); + fz_var(cs); + fz_try(ctx) + { + fz_rect rect; + fz_rect bounds; + fz_matrix tm; + + pdf_to_rect(ctx, pdf_dict_gets(annot->obj, "Rect"), &rect); + dlist = fz_new_display_list(ctx); + 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); + fz_fill_path(dev, path, 0, &tm, cs, yellow, 1.0f); + fz_stroke_path(dev, path, stroke, &tm, cs, black, 1.0f); + fz_free_path(ctx, path); + path = NULL; + + path = fz_new_path(ctx); + draw_speech_bubble(ctx, path); + fz_fill_path(dev, path, 0, &tm, cs, white, 1.0f); + fz_stroke_path(dev, path, stroke, &tm, cs, black, 1.0f); + + fz_transform_rect(&rect, page_ctm); + pdf_set_annot_appearance(doc, annot, &rect, dlist); + + /* Drop the cached xobject from the annotation structure to + * force a redraw on next pdf_update_page call */ + pdf_drop_xobject(ctx, annot->ap); + annot->ap = NULL; + } + fz_always(ctx) + { + fz_free_device(dev); + fz_drop_display_list(ctx, dlist); + fz_drop_stroke_state(ctx, stroke); + fz_free_path(ctx, path); + fz_drop_colorspace(ctx, cs); + } + fz_catch(ctx) + { + fz_rethrow(ctx); + } +} + void pdf_update_free_text_annot_appearance(pdf_document *doc, pdf_annot *annot) { fz_context *ctx = doc->ctx; @@ -1922,32 +2044,6 @@ static void draw_logo(fz_context *ctx, fz_path *path) fz_closepath(ctx, path); }; -static void rect_center(const fz_rect *rect, fz_point *c) -{ - c->x = (rect->x0 + rect->x1) / 2.0f; - c->y = (rect->y0 + rect->y1) / 2.0f; -} - -static void center_rect_within_rect(const fz_rect *tofit, const fz_rect *within, fz_matrix *mat) -{ - 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); -} - static void insert_signature_appearance_layers(pdf_document *doc, pdf_annot *annot) { fz_context *ctx = doc->ctx; diff --git a/source/pdf/pdf-form.c b/source/pdf/pdf-form.c index c08ecd4e..a659192b 100644 --- a/source/pdf/pdf-form.c +++ b/source/pdf/pdf-form.c @@ -464,6 +464,9 @@ void pdf_update_appearance(pdf_document *doc, pdf_annot *annot) break; } break; + case FZ_ANNOT_TEXT: + pdf_update_text_annot_appearance(doc, annot); + break; case FZ_ANNOT_FREETEXT: pdf_update_free_text_annot_appearance(doc, annot); break; -- cgit v1.2.3