diff options
Diffstat (limited to 'platform/gl/gl-annotate.c')
-rw-r--r-- | platform/gl/gl-annotate.c | 1123 |
1 files changed, 1123 insertions, 0 deletions
diff --git a/platform/gl/gl-annotate.c b/platform/gl/gl-annotate.c new file mode 100644 index 00000000..f23de5ba --- /dev/null +++ b/platform/gl/gl-annotate.c @@ -0,0 +1,1123 @@ +#include "gl-app.h" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <limits.h> + +#ifndef PATH_MAX +#define PATH_MAX 2048 +#endif + +static char save_filename[PATH_MAX]; +static pdf_write_options save_opts; + +static int pdf_filter(const char *fn) +{ + const char *suffix = strrchr(fn, '.'); + if (suffix && !fz_strcasecmp(suffix, ".pdf")) + return 1; + return 0; +} + +static void init_save_pdf_options(void) +{ + memset(&save_opts, 0, sizeof save_opts); + save_opts.do_garbage = 1; +} + +static void save_pdf_options(void) +{ + ui_layout(T, X, NW, 2, 2); + ui_label("PDF write options:"); + ui_layout(T, X, NW, 4, 2); + ui_checkbox("Incremental", &save_opts.do_incremental); + ui_checkbox("Pretty-print", &save_opts.do_pretty); + ui_checkbox("Ascii", &save_opts.do_ascii); + ui_checkbox("Compress", &save_opts.do_compress); + ui_checkbox("Compress images", &save_opts.do_compress_images); + ui_checkbox("Compress fonts", &save_opts.do_compress_fonts); + ui_checkbox("Decompress", &save_opts.do_decompress); + ui_checkbox("Garbage collect", &save_opts.do_garbage); + ui_checkbox("Linearize", &save_opts.do_linear); + ui_checkbox("Clean syntax", &save_opts.do_clean); + ui_checkbox("Sanitize syntax", &save_opts.do_sanitize); +} + +static void save_pdf_dialog(void) +{ + if (ui_save_file(save_filename, save_pdf_options)) + { + ui.dialog = NULL; + if (save_filename[0] != 0) + { + if (save_opts.do_garbage) + save_opts.do_garbage = 2; + fz_try(ctx) + { + pdf_save_document(ctx, pdf, save_filename, &save_opts); + fz_strlcpy(filename, save_filename, PATH_MAX); + update_title(); + } + fz_catch(ctx) + { + ui_show_warning_dialog(fz_caught_message(ctx)); + } + } + } +} + +static int rects_differ(const fz_rect *a, const fz_rect *b, float threshold) +{ + if (fz_abs(a->x0 - b->x0) > threshold) return 1; + if (fz_abs(a->y0 - b->y0) > threshold) return 1; + if (fz_abs(a->x1 - b->x1) > threshold) return 1; + if (fz_abs(a->y1 - b->y1) > threshold) return 1; + return 0; +} + +static int points_differ(const fz_point *a, const fz_point *b, float threshold) +{ + if (fz_abs(a->x - b->x) > threshold) return 1; + if (fz_abs(a->y - b->y) > threshold) return 1; + return 0; +} + +static const char *getuser(void) +{ + const char *u; + u = getenv("USER"); + if (!u) u = getenv("USERNAME"); + if (!u) u = "user"; + return u; +} + +static void new_annot(int type) +{ + static const float black[3] = { 0, 0, 0 }; + static const float red[3] = { 1, 0, 0 }; + static const float green[3] = { 0, 1, 0 }; + static const float blue[3] = { 0, 0, 1 }; + static const float yellow[3] = { 1, 1, 0 }; + static const float magenta[3] = { 1, 0, 1 }; + + selected_annot = pdf_create_annot(ctx, page, type); + + pdf_set_annot_modification_date(ctx, selected_annot, time(NULL)); + if (pdf_annot_has_author(ctx, selected_annot)) + pdf_set_annot_author(ctx, selected_annot, getuser()); + + switch (type) + { + case PDF_ANNOT_TEXT: + case PDF_ANNOT_FILE_ATTACHMENT: + case PDF_ANNOT_SOUND: + { + fz_rect icon_rect = { 12, 12, 12+20, 12+20 }; + pdf_set_annot_rect(ctx, selected_annot, &icon_rect); + pdf_set_annot_color(ctx, selected_annot, 3, yellow); + } + break; + + case PDF_ANNOT_FREE_TEXT: + { + fz_rect text_rect = { 12, 12, 12+200, 12+100 }; + pdf_set_annot_rect(ctx, selected_annot, &text_rect); + pdf_set_annot_border(ctx, selected_annot, 0); + pdf_set_annot_default_appearance(ctx, selected_annot, "Helv", 12, black); + } + break; + + case PDF_ANNOT_STAMP: + { + fz_rect stamp_rect = { 12, 12, 12+190, 12+50 }; + pdf_set_annot_rect(ctx, selected_annot, &stamp_rect); + pdf_set_annot_color(ctx, selected_annot, 3, red); + } + break; + + case PDF_ANNOT_CARET: + { + fz_rect caret_rect = { 12, 12, 12+18, 12+15 }; + pdf_set_annot_rect(ctx, selected_annot, &caret_rect); + pdf_set_annot_color(ctx, selected_annot, 3, blue); + } + break; + + case PDF_ANNOT_LINE: + { + fz_point a = { 12, 12 }, b = { 12 + 100, 12 + 50 }; + pdf_set_annot_line(ctx, selected_annot, a, b); + pdf_set_annot_border(ctx, selected_annot, 1); + pdf_set_annot_color(ctx, selected_annot, 3, red); + } + break; + + case PDF_ANNOT_SQUARE: + case PDF_ANNOT_CIRCLE: + { + fz_rect shape_rect = { 12, 12, 12+100, 12+50 }; + pdf_set_annot_rect(ctx, selected_annot, &shape_rect); + pdf_set_annot_border(ctx, selected_annot, 1); + pdf_set_annot_color(ctx, selected_annot, 3, red); + } + break; + + case PDF_ANNOT_POLYGON: + case PDF_ANNOT_POLY_LINE: + case PDF_ANNOT_INK: + pdf_set_annot_border(ctx, selected_annot, 1); + pdf_set_annot_color(ctx, selected_annot, 3, red); + break; + + case PDF_ANNOT_HIGHLIGHT: + pdf_set_annot_color(ctx, selected_annot, 3, yellow); + break; + case PDF_ANNOT_UNDERLINE: + pdf_set_annot_color(ctx, selected_annot, 3, green); + break; + case PDF_ANNOT_STRIKE_OUT: + pdf_set_annot_color(ctx, selected_annot, 3, red); + break; + case PDF_ANNOT_SQUIGGLY: + pdf_set_annot_color(ctx, selected_annot, 3, magenta); + break; + } + + pdf_update_appearance(ctx, selected_annot); + render_page(); +} + +static void do_annotate_flags(void) +{ + char buf[4096]; + int f = pdf_annot_flags(ctx, selected_annot); + fz_strlcpy(buf, "Flags:", sizeof buf); + if (f & PDF_ANNOT_IS_INVISIBLE) fz_strlcat(buf, " inv", sizeof buf); + if (f & PDF_ANNOT_IS_HIDDEN) fz_strlcat(buf, " hidden", sizeof buf); + if (f & PDF_ANNOT_IS_PRINT) fz_strlcat(buf, " print", sizeof buf); + if (f & PDF_ANNOT_IS_NO_ZOOM) fz_strlcat(buf, " nz", sizeof buf); + if (f & PDF_ANNOT_IS_NO_ROTATE) fz_strlcat(buf, " nr", sizeof buf); + if (f & PDF_ANNOT_IS_NO_VIEW) fz_strlcat(buf, " nv", sizeof buf); + if (f & PDF_ANNOT_IS_READ_ONLY) fz_strlcat(buf, " ro", sizeof buf); + if (f & PDF_ANNOT_IS_LOCKED) fz_strlcat(buf, " lock", sizeof buf); + if (f & PDF_ANNOT_IS_TOGGLE_NO_VIEW) fz_strlcat(buf, " tnv", sizeof buf); + if (f & PDF_ANNOT_IS_LOCKED_CONTENTS) fz_strlcat(buf, " lc", sizeof buf); + if (!f) fz_strlcat(buf, " none", sizeof buf); + ui_label("%s", buf); +} + +static const char *color_names[] = { + "None", + "Aqua", + "Black", + "Blue", + "Fuchsia", + "Gray", + "Green", + "Lime", + "Maroon", + "Navy", + "Olive", + "Orange", + "Purple", + "Red", + "Silver", + "Teal", + "White", + "Yellow", +}; + +static unsigned int color_values[] = { + 0x00000000, /* transparent */ + 0xff00ffff, /* aqua */ + 0xff000000, /* black */ + 0xff0000ff, /* blue */ + 0xffff00ff, /* fuchsia */ + 0xff808080, /* gray */ + 0xff008000, /* green */ + 0xff00ff00, /* lime */ + 0xff800000, /* maroon */ + 0xff000080, /* navy */ + 0xff808000, /* olive */ + 0xffffa500, /* orange */ + 0xff800080, /* purple */ + 0xffff0000, /* red */ + 0xffc0c0c0, /* silver */ + 0xff008080, /* teal */ + 0xffffffff, /* white */ + 0xffffff00, /* yellow */ +}; + +static unsigned int hex_from_color(int n, float color[4]) +{ + float rgb[4]; + int r, g, b; + switch (n) + { + default: + return 0; + case 1: + r = color[0] * 255; + return 0xff000000 | (r<<16) | (r<<8) | r; + case 3: + r = color[0] * 255; + g = color[1] * 255; + b = color[2] * 255; + return 0xff000000 | (r<<16) | (g<<8) | b; + case 4: + fz_convert_color(ctx, NULL, NULL, fz_device_rgb(ctx), rgb, fz_device_cmyk(ctx), color); + r = rgb[0] * 255; + g = rgb[1] * 255; + b = rgb[2] * 255; + return 0xff000000 | (r<<16) | (g<<8) | b; + } +} + +static const char *name_from_hex(unsigned int hex) +{ + static char buf[10]; + int i; + for (i = 0; i < nelem(color_names); ++i) + if (color_values[i] == hex) + return color_names[i]; + fz_snprintf(buf, sizeof buf, "#%06x", hex & 0xffffff); + return buf; +} + +static void do_annotate_color(char *label, + void (*get_color)(fz_context *ctx, pdf_annot *annot, int *n, float color[4]), + void (*set_color)(fz_context *ctx, pdf_annot *annot, int n, const float color[4])) +{ + float color[4]; + int hex, choice, n; + get_color(ctx, selected_annot, &n, color); + ui_label("%s:", label); + choice = ui_select(label, name_from_hex(hex_from_color(n, color)), color_names, nelem(color_names)); + if (choice != -1) + { + hex = color_values[choice]; + if (hex == 0) + set_color(ctx, selected_annot, 0, color); + else + { + color[0] = ((hex>>16)&0xff) / 255.0f; + color[1] = ((hex>>8)&0xff) / 255.0f; + color[2] = ((hex)&0xff) / 255.0f; + set_color(ctx, selected_annot, 3, color); + } + } +} + +static void do_annotate_author(void) +{ + if (pdf_annot_has_author(ctx, selected_annot)) + { + char *author = pdf_copy_annot_author(ctx, selected_annot); + if (author && strlen(author) > 0) + ui_label("Author: %s", author); + fz_free(ctx, author); + } +} + +static void do_annotate_date(void) +{ + time_t secs = pdf_annot_modification_date(ctx, selected_annot); + if (secs > 0) + { +#ifdef _POSIX_SOURCE + struct tm tmbuf, *tm = gmtime_r(&secs, &tmbuf); +#else + struct tm *tm = gmtime(&secs); +#endif + char buf[100]; + if (tm) + { + strftime(buf, sizeof buf, "%Y-%m-%d %H:%M UTC", tm); + ui_label("Date: %s", buf); + } + } +} + +static void do_annotate_contents(void) +{ + static pdf_annot *last_annot = NULL; + static struct input input; + char *contents; + + if (selected_annot != last_annot) + { + last_annot = selected_annot; + contents = pdf_copy_annot_contents(ctx, selected_annot); + ui_input_init(&input, contents); + fz_free(ctx, contents); + } + + ui_label("Contents:"); + if (ui_input(&input, 0) >= UI_INPUT_EDIT) + pdf_set_annot_contents(ctx, selected_annot, input.text); +} + +static const char *file_attachment_icons[] = { "Graph", "Paperclip", "PushPin", "Tag" }; +static const char *sound_icons[] = { "Speaker", "Mic" }; +static const char *stamp_icons[] = { + "Approved", "AsIs", "Confidential", "Departmental", "Draft", + "Experimental", "Expired", "Final", "ForComment", "ForPublicRelease", + "NotApproved", "NotForPublicRelease", "Sold", "TopSecret" }; +static const char *text_icons[] = { + "Comment", "Help", "Insert", "Key", "NewParagraph", "Note", "Paragraph" }; +static const char *line_ending_styles[] = { + "None", "Square", "Circle", "Diamond", "OpenArrow", "ClosedArrow", "Butt", + "ROpenArrow", "RClosedArrow", "Slash" }; +static const char *quadding_names[] = { "Left", "Center", "Right" }; + +static int should_edit_border(enum pdf_annot_type subtype) +{ + switch (subtype) { + default: + return 0; + case PDF_ANNOT_FREE_TEXT: + return 1; + case PDF_ANNOT_INK: + case PDF_ANNOT_LINE: + case PDF_ANNOT_SQUARE: + case PDF_ANNOT_CIRCLE: + case PDF_ANNOT_POLYGON: + case PDF_ANNOT_POLY_LINE: + return 1; + } +} + +static int should_edit_color(enum pdf_annot_type subtype) +{ + switch (subtype) { + default: + return 0; + case PDF_ANNOT_STAMP: + case PDF_ANNOT_TEXT: + case PDF_ANNOT_FILE_ATTACHMENT: + case PDF_ANNOT_SOUND: + case PDF_ANNOT_CARET: + return 1; + case PDF_ANNOT_FREE_TEXT: + return 1; + case PDF_ANNOT_INK: + case PDF_ANNOT_LINE: + case PDF_ANNOT_SQUARE: + case PDF_ANNOT_CIRCLE: + case PDF_ANNOT_POLYGON: + case PDF_ANNOT_POLY_LINE: + return 1; + case PDF_ANNOT_HIGHLIGHT: + case PDF_ANNOT_UNDERLINE: + case PDF_ANNOT_STRIKE_OUT: + case PDF_ANNOT_SQUIGGLY: + return 1; + } +} + +static int should_edit_icolor(enum pdf_annot_type subtype) +{ + switch (subtype) { + default: + return 0; + case PDF_ANNOT_LINE: + case PDF_ANNOT_SQUARE: + case PDF_ANNOT_CIRCLE: + return 1; + } +} + +void do_annotate_panel(void) +{ + static struct list annot_list; + pdf_annot *annot; + int n; + + int was_dirty = pdf->dirty; + + ui_layout(T, X, NW, 2, 2); + + if (ui_popup("CreateAnnotPopup", "Create...", 1, 14)) + { + if (ui_popup_item("Text")) new_annot(PDF_ANNOT_TEXT); + if (ui_popup_item("FreeText")) new_annot(PDF_ANNOT_FREE_TEXT); + if (ui_popup_item("Stamp")) new_annot(PDF_ANNOT_STAMP); + if (ui_popup_item("Caret")) new_annot(PDF_ANNOT_CARET); + if (ui_popup_item("Ink")) new_annot(PDF_ANNOT_INK); + if (ui_popup_item("Square")) new_annot(PDF_ANNOT_SQUARE); + if (ui_popup_item("Circle")) new_annot(PDF_ANNOT_CIRCLE); + if (ui_popup_item("Line")) new_annot(PDF_ANNOT_LINE); + if (ui_popup_item("Polygon")) new_annot(PDF_ANNOT_POLYGON); + if (ui_popup_item("PolyLine")) new_annot(PDF_ANNOT_POLY_LINE); + if (ui_popup_item("Highlight")) new_annot(PDF_ANNOT_HIGHLIGHT); + if (ui_popup_item("Underline")) new_annot(PDF_ANNOT_UNDERLINE); + if (ui_popup_item("StrikeOut")) new_annot(PDF_ANNOT_STRIKE_OUT); + if (ui_popup_item("Squiggly")) new_annot(PDF_ANNOT_SQUIGGLY); + ui_popup_end(); + } + + n = 0; + for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot)) + ++n; + + ui_list_begin(&annot_list, n, 0, ui.lineheight * 10 + 4); + for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot)) + { + char buf[256]; + int num = pdf_to_num(ctx, annot->obj); + const char *type = pdf_string_from_annot_type(ctx, pdf_annot_type(ctx, annot)); + fz_snprintf(buf, sizeof buf, "%d: %s", num, type); + if (ui_list_item(&annot_list, annot->obj, buf, selected_annot == annot)) + selected_annot = annot; + } + ui_list_end(&annot_list); + + if (selected_annot) + { + enum pdf_annot_type subtype = pdf_annot_type(ctx, selected_annot); + fz_rect rect; + fz_irect irect; + int n, choice; + pdf_obj *obj; + + if (ui_button("Delete")) + { + pdf_delete_annot(ctx, page, selected_annot); + selected_annot = NULL; + render_page(); + return; + } + + ui_spacer(); + + /* common annotation properties */ + + pdf_annot_rect(ctx, selected_annot, &rect); + fz_irect_from_rect(&irect, &rect); + ui_label("Rect: %d %d %d %d", irect.x0, irect.y0, irect.x1, irect.y1); + + do_annotate_flags(); + do_annotate_author(); + do_annotate_date(); + + obj = pdf_dict_get(ctx, selected_annot->obj, PDF_NAME(Popup)); + if (obj) + ui_label("Popup: %d 0 R", pdf_to_num(ctx, obj)); + + ui_spacer(); + + do_annotate_contents(); + + ui_spacer(); + + if (subtype == PDF_ANNOT_FREE_TEXT) + { + int q = pdf_annot_quadding(ctx, selected_annot); + ui_label("Text Alignment:"); + choice = ui_select("Q", quadding_names[q], quadding_names, nelem(quadding_names)); + if (choice != -1) + pdf_set_annot_quadding(ctx, selected_annot, choice); + } + + if (subtype == PDF_ANNOT_LINE) + { + enum pdf_line_ending s, e; + int s_choice, e_choice; + + pdf_annot_line_ending_styles(ctx, selected_annot, &s, &e); + + ui_label("Line Start:"); + s_choice = ui_select("LE0", line_ending_styles[s], line_ending_styles, nelem(line_ending_styles)); + + ui_label("Line End:"); + e_choice = ui_select("LE1", line_ending_styles[e], line_ending_styles, nelem(line_ending_styles)); + + if (s_choice != -1 || e_choice != -1) + { + if (s_choice != -1) s = s_choice; + if (e_choice != -1) e = e_choice; + pdf_set_annot_line_ending_styles(ctx, selected_annot, s, e); + } + } + + if (pdf_annot_has_icon_name(ctx, selected_annot)) + { + const char *name = pdf_annot_icon_name(ctx, selected_annot); + ui_label("Icon:"); + switch (pdf_annot_type(ctx, selected_annot)) + { + case PDF_ANNOT_TEXT: + choice = ui_select("Icon", name, text_icons, nelem(text_icons)); + if (choice != -1) + pdf_set_annot_icon_name(ctx, selected_annot, text_icons[choice]); + break; + case PDF_ANNOT_FILE_ATTACHMENT: + choice = ui_select("Icon", name, file_attachment_icons, nelem(file_attachment_icons)); + if (choice != -1) + pdf_set_annot_icon_name(ctx, selected_annot, file_attachment_icons[choice]); + break; + case PDF_ANNOT_SOUND: + choice = ui_select("Icon", name, sound_icons, nelem(sound_icons)); + if (choice != -1) + pdf_set_annot_icon_name(ctx, selected_annot, sound_icons[choice]); + break; + case PDF_ANNOT_STAMP: + choice = ui_select("Icon", name, stamp_icons, nelem(stamp_icons)); + if (choice != -1) + pdf_set_annot_icon_name(ctx, selected_annot, stamp_icons[choice]); + break; + } + } + + if (should_edit_border(subtype)) + { + static int border; + border = pdf_annot_border(ctx, selected_annot); + ui_label("Border: %d", border); + if (ui_slider(&border, 0, 10, 100)) + pdf_set_annot_border(ctx, selected_annot, border); + } + + if (should_edit_color(subtype)) + do_annotate_color("Color", pdf_annot_color, pdf_set_annot_color); + if (should_edit_icolor(subtype)) + do_annotate_color("Interior Color", pdf_annot_interior_color, pdf_set_annot_interior_color); + + if (subtype == PDF_ANNOT_HIGHLIGHT) + { + static int opacity; + opacity = pdf_annot_opacity(ctx, selected_annot) * 255; + ui_label("Opacity:"); + if (ui_slider(&opacity, 0, 255, 256)) + pdf_set_annot_opacity(ctx, selected_annot, opacity / 255.0f); + } + + if (pdf_annot_has_open(ctx, selected_annot)) + { + int is_open = pdf_annot_is_open(ctx, selected_annot); + int start_is_open = is_open; + ui_checkbox("Open", &is_open); + if (start_is_open != is_open) + pdf_set_annot_is_open(ctx, selected_annot, is_open); + } + + ui_spacer(); + + if (pdf_annot_has_quad_points(ctx, selected_annot)) + { + n = pdf_annot_quad_point_count(ctx, selected_annot); + ui_label("QuadPoints: %d", n); + if (ui_button("Clear")) + pdf_clear_annot_quad_points(ctx, selected_annot); + } + + if (pdf_annot_has_vertices(ctx, selected_annot)) + { + n = pdf_annot_vertex_count(ctx, selected_annot); + ui_label("Vertices: %d", n); + if (ui_button("Clear")) + pdf_clear_annot_vertices(ctx, selected_annot); + } + + if (pdf_annot_has_ink_list(ctx, selected_annot)) + { + n = pdf_annot_ink_list_count(ctx, selected_annot); + ui_label("InkList: %d strokes", n); + if (ui_button("Clear")) + pdf_clear_annot_ink_list(ctx, selected_annot); + } + + if (selected_annot && selected_annot->needs_new_ap) + { + pdf_update_appearance(ctx, selected_annot); + render_page(); + } + } + + ui_layout(B, X, NW, 2, 2); + if (ui_button("Save PDF...")) + { + init_save_pdf_options(); + ui_init_save_file(filename, pdf_filter); + ui.dialog = save_pdf_dialog; + } + + if (was_dirty != pdf->dirty) + update_title(); +} + +static void do_edit_icon(fz_irect canvas_area, fz_irect area, fz_rect *rect) +{ + static fz_point start_pt; + static float w, h; + static int moving = 0; + + if (ui_mouse_inside(&canvas_area) && ui_mouse_inside(&area)) + { + ui.hot = selected_annot; + if (!ui.active && ui.down) + { + ui.active = selected_annot; + start_pt.x = rect->x0; + start_pt.y = rect->y0; + w = rect->x1 - rect->x0; + h = rect->y1 - rect->y0; + moving = 1; + } + } + + if (ui.active == selected_annot && moving) + { + rect->x0 = start_pt.x + (ui.x - ui.down_x); + rect->y0 = start_pt.y + (ui.y - ui.down_y); + + /* Clamp to fit on page */ + rect->x0 = fz_clamp(rect->x0, view_page_area.x0, view_page_area.x1-w); + rect->y0 = fz_clamp(rect->y0, view_page_area.y0, view_page_area.y1-h); + rect->x1 = rect->x0 + w; + rect->y1 = rect->y0 + h; + + /* cancel on right click */ + if (ui.right) + moving = 0; + + /* Commit movement on mouse-up */ + if (!ui.down) + { + moving = 0; + if (fz_abs(start_pt.x - rect->x0) > 0.1f || fz_abs(start_pt.x - rect->y0) > 0.1f) + { + fz_rect trect = *rect; + fz_transform_rect(&trect, &view_page_inv_ctm); + pdf_set_annot_rect(ctx, selected_annot, &trect); + } + } + } +} + +static void do_edit_rect(fz_irect canvas_area, fz_irect area, fz_rect *rect) +{ + enum { + ER_N=1, ER_E=2, ER_S=4, ER_W=8, + ER_NONE = 0, + ER_NW = ER_N|ER_W, + ER_NE = ER_N|ER_E, + ER_SW = ER_S|ER_W, + ER_SE = ER_S|ER_E, + ER_MOVE = ER_N|ER_E|ER_S|ER_W, + }; + static fz_rect start_rect; + static int state = ER_NONE; + + fz_expand_irect(&area, 5); + if (ui_mouse_inside(&canvas_area) && ui_mouse_inside(&area)) + { + ui.hot = selected_annot; + if (!ui.active && ui.down) + { + ui.active = selected_annot; + start_rect = *rect; + state = ER_NONE; + if (ui.x <= area.x0 + 10) state |= ER_W; + if (ui.x >= area.x1 - 10) state |= ER_E; + if (ui.y <= area.y0 + 10) state |= ER_N; + if (ui.y >= area.y1 - 10) state |= ER_S; + if (!state) state = ER_MOVE; + } + } + + if (ui.active == selected_annot && state != ER_NONE) + { + *rect = start_rect; + if (state & ER_W) rect->x0 += (ui.x - ui.down_x); + if (state & ER_E) rect->x1 += (ui.x - ui.down_x); + if (state & ER_N) rect->y0 += (ui.y - ui.down_y); + if (state & ER_S) rect->y1 += (ui.y - ui.down_y); + if (rect->x1 < rect->x0) { float t = rect->x1; rect->x1 = rect->x0; rect->x0 = t; } + if (rect->y1 < rect->y0) { float t = rect->y1; rect->y1 = rect->y0; rect->y0 = t; } + if (rect->x1 < rect->x0 + 10) rect->x1 = rect->x0 + 10; + if (rect->y1 < rect->y0 + 10) rect->y1 = rect->y0 + 10; + + /* cancel on right click */ + if (ui.right) + state = ER_NONE; + + /* commit on mouse-up */ + if (!ui.down) + { + state = ER_NONE; + if (rects_differ(&start_rect, rect, 1)) + { + fz_rect trect = *rect; + fz_transform_rect(&trect, &view_page_inv_ctm); + pdf_set_annot_rect(ctx, selected_annot, &trect); + } + } + } +} + +static void do_edit_line(fz_irect canvas_area, fz_irect area, fz_rect *rect) +{ + enum { EL_NONE, EL_A=1, EL_B=2, EL_MOVE=EL_A|EL_B }; + static fz_point start_a, start_b; + static int state = EL_NONE; + fz_irect a_grab, b_grab; + fz_point a, b; + float lw; + + fz_expand_irect(&area, 5); + if (ui_mouse_inside(&canvas_area) && ui_mouse_inside(&area)) + { + ui.hot = selected_annot; + if (!ui.active && ui.down) + { + ui.active = selected_annot; + pdf_annot_line(ctx, selected_annot, &start_a, &start_b); + fz_transform_point(&start_a, &view_page_ctm); + fz_transform_point(&start_b, &view_page_ctm); + a_grab = fz_make_irect(start_a.x, start_a.y, start_a.x, start_a.y); + b_grab = fz_make_irect(start_b.x, start_b.y, start_b.x, start_b.y); + fz_expand_irect(&a_grab, 10); + fz_expand_irect(&b_grab, 10); + state = EL_NONE; + if (ui_mouse_inside(&a_grab)) state |= EL_A; + if (ui_mouse_inside(&b_grab)) state |= EL_B; + if (!state) state = EL_MOVE; + } + } + + if (ui.active == selected_annot && state != 0) + { + a = start_a; + b = start_b; + if (state & EL_A) { a.x += (ui.x - ui.down_x); a.y += (ui.y - ui.down_y); } + if (state & EL_B) { b.x += (ui.x - ui.down_x); b.y += (ui.y - ui.down_y); } + + glBegin(GL_LINES); + glColor4f(1, 0, 0, 1); + glVertex2f(a.x, a.y); + glVertex2f(b.x, b.y); + glEnd(); + + 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); + lw = pdf_annot_border(ctx, selected_annot); + fz_expand_rect(rect, fz_matrix_expansion(&view_page_ctm) * lw); + + /* cancel on right click */ + if (ui.right) + state = EL_NONE; + + /* commit on mouse-up */ + if (!ui.down) + { + state = EL_NONE; + if (points_differ(&start_a, &a, 1) || points_differ(&start_b, &b, 1)) + { + fz_transform_point(&a, &view_page_inv_ctm); + fz_transform_point(&b, &view_page_inv_ctm); + pdf_set_annot_line(ctx, selected_annot, a, b); + } + } + } +} + +static void do_edit_polygon(fz_irect canvas_area, int close) +{ + static int drawing = 0; + fz_point a, p; + + if (ui_mouse_inside(&canvas_area) && ui_mouse_inside(&view_page_area)) + { + ui.hot = selected_annot; + if (!ui.active || ui.active == selected_annot) + ui.cursor = GLUT_CURSOR_CROSSHAIR; + if (!ui.active && ui.down) + { + ui.active = selected_annot; + drawing = 1; + } + } + + if (ui.active == selected_annot && drawing) + { + int n = pdf_annot_vertex_count(ctx, selected_annot); + if (n > 0) + { + p = pdf_annot_vertex(ctx, selected_annot, n-1); + fz_transform_point(&p, &view_page_ctm); + if (close) + { + a = pdf_annot_vertex(ctx, selected_annot, 0); + fz_transform_point(&a, &view_page_ctm); + } + glBegin(GL_LINE_STRIP); + glColor4f(1, 0, 0, 1); + glVertex2f(p.x, p.y); + glVertex2f(ui.x, ui.y); + if (close) + glVertex2f(a.x, a.y); + glEnd(); + } + + glColor4f(1, 0, 0, 1); + glPointSize(4); + glBegin(GL_POINTS); + glVertex2f(ui.x, ui.y); + glEnd(); + + /* cancel on right click */ + if (ui.right) + drawing = 0; + + /* commit point on mouse-up */ + if (!ui.down) + { + fz_point p = { ui.x, ui.y }; + fz_transform_point(&p, &view_page_inv_ctm); + pdf_add_annot_vertex(ctx, selected_annot, p); + drawing = 0; + } + } +} + +static void do_edit_ink(fz_irect canvas_area) +{ + static int drawing = 0; + static fz_point p[1000]; + static int n, last_x, last_y; + int i; + + if (ui_mouse_inside(&canvas_area) && ui_mouse_inside(&view_page_area)) + { + ui.hot = selected_annot; + if (!ui.active || ui.active == selected_annot) + ui.cursor = GLUT_CURSOR_CROSSHAIR; + if (!ui.active && ui.down) + { + ui.active = selected_annot; + drawing = 1; + n = 0; + last_x = INT_MIN; + last_y = INT_MIN; + } + } + + if (ui.active == selected_annot && drawing) + { + if (n < nelem(p) && (ui.x != last_x || ui.y != last_y)) + { + p[n].x = fz_clamp(ui.x, view_page_area.x0, view_page_area.x1); + p[n].y = fz_clamp(ui.y, view_page_area.y0, view_page_area.y1); + ++n; + } + last_x = ui.x; + last_y = ui.y; + + if (n > 1) + { + glBegin(GL_LINE_STRIP); + glColor4f(1, 0, 0, 1); + for (i = 0; i < n; ++i) + glVertex2f(p[i].x, p[i].y); + glEnd(); + } + + /* cancel on right click */ + if (ui.right) + { + drawing = 0; + n = 0; + } + + /* commit stroke on mouse-up */ + if (!ui.down) + { + if (n > 1) + { + for (i = 0; i < n; ++i) + fz_transform_point(&p[i], &view_page_inv_ctm); + pdf_add_annot_ink_list(ctx, selected_annot, n, p); + } + drawing = 0; + n = 0; + } + } +} + +static void do_edit_quad_points(void) +{ + static fz_point pt = { 0, 0 }; + static int marking = 0; + fz_rect hits[1000]; + int i, n; + + if (ui_mouse_inside(&view_page_area)) + { + ui.hot = selected_annot; + if (!ui.active || ui.active == selected_annot) + ui.cursor = GLUT_CURSOR_TEXT; + if (!ui.active && ui.down) + { + ui.active = selected_annot; + marking = 1; + pt.x = ui.x; + pt.y = ui.y; + } + } + + if (ui.active == selected_annot && marking) + { + fz_point page_a = { pt.x, pt.y }; + fz_point page_b = { ui.x, ui.y }; + + fz_transform_point(&page_a, &view_page_inv_ctm); + fz_transform_point(&page_b, &view_page_inv_ctm); + + n = fz_highlight_selection(ctx, page_text, page_a, page_b, hits, nelem(hits)); + + glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); /* invert destination color */ + glEnable(GL_BLEND); + + glColor4f(1, 1, 1, 1); + for (i = 0; i < n; ++i) + { + fz_rect thit = hits[i]; + fz_transform_rect(&thit, &view_page_ctm); + glRectf(thit.x0, thit.y0, thit.x1 + 1, thit.y1 + 1); + } + + glDisable(GL_BLEND); + + /* cancel on right click */ + if (ui.right) + marking = 0; + + if (!ui.down) + { + if (n > 0) + { + pdf_clear_annot_quad_points(ctx, selected_annot); + for (i = 0; i < n; ++i) + pdf_add_annot_quad_point(ctx, selected_annot, hits[i]); + } + marking = 0; + } + } +} + +void do_annotate_canvas(fz_irect canvas_area) +{ + fz_rect bounds; + fz_irect area; + pdf_annot *annot; + + int was_dirty = pdf->dirty; + + for (annot = pdf_first_annot(ctx, page); annot; annot = pdf_next_annot(ctx, annot)) + { + pdf_bound_annot(ctx, annot, &bounds); + fz_transform_rect(&bounds, &view_page_ctm); + fz_irect_from_rect(&area, &bounds); + + if (ui_mouse_inside(&canvas_area) && ui_mouse_inside(&area)) + { + ui.hot = annot; + if (!ui.active && ui.right) + { + ui.active = annot; + selected_annot = annot; + } + } + + if (annot == selected_annot) + { + switch (pdf_annot_type(ctx, selected_annot)) + { + default: + break; + + /* Popup window */ + case PDF_ANNOT_POPUP: + do_edit_rect(canvas_area, area, &bounds); + break; + + /* Icons */ + case PDF_ANNOT_TEXT: + case PDF_ANNOT_CARET: + case PDF_ANNOT_FILE_ATTACHMENT: + case PDF_ANNOT_SOUND: + do_edit_icon(canvas_area, area, &bounds); + break; + + case PDF_ANNOT_STAMP: + do_edit_rect(canvas_area, area, &bounds); + break; + + case PDF_ANNOT_FREE_TEXT: + do_edit_rect(canvas_area, area, &bounds); + break; + + /* Drawings */ + case PDF_ANNOT_LINE: + do_edit_line(canvas_area, area, &bounds); + break; + case PDF_ANNOT_CIRCLE: + case PDF_ANNOT_SQUARE: + do_edit_rect(canvas_area, area, &bounds); + break; + case PDF_ANNOT_POLYGON: + do_edit_polygon(canvas_area, 1); + break; + case PDF_ANNOT_POLY_LINE: + do_edit_polygon(canvas_area, 0); + break; + + case PDF_ANNOT_INK: + do_edit_ink(canvas_area); + break; + + case PDF_ANNOT_HIGHLIGHT: + case PDF_ANNOT_UNDERLINE: + case PDF_ANNOT_STRIKE_OUT: + case PDF_ANNOT_SQUIGGLY: + do_edit_quad_points(); + break; + } + + glLineStipple(1, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); + glEnable(GL_BLEND); + glColor4f(1, 1, 1, 1); + glBegin(GL_LINE_LOOP); + fz_irect_from_rect(&area, &bounds); + glVertex2f(area.x0-0.5f, area.y0-0.5f); + glVertex2f(area.x1+0.5f, area.y0-0.5f); + glVertex2f(area.x1+0.5f, area.y1+0.5f); + glVertex2f(area.x0-0.5f, area.y1+0.5f); + glEnd(); + glDisable(GL_BLEND); + glDisable(GL_LINE_STIPPLE); + + if (annot->needs_new_ap) + { + pdf_update_appearance(ctx, annot); + render_page(); + } + } + } + + if (ui_mouse_inside(&canvas_area) && ui.right) + { + if (!ui.active) + selected_annot = NULL; + } + + if (was_dirty != pdf->dirty) + update_title(); +} |