| 1 | #include "mupdf/fitz.h" |
| 2 | #include "mupdf/pdf.h" |
| 3 | #include "../fitz/fitz-imp.h" // ick! |
| 4 | |
| 5 | #include <string.h> |
| 6 | #include <time.h> |
| 7 | |
| 8 | #ifdef _WIN32 |
| 9 | #define timegm _mkgmtime |
| 10 | #endif |
| 11 | |
| 12 | #define isdigit(c) (c >= '0' && c <= '9') |
| 13 | |
| 14 | pdf_annot * |
| 15 | pdf_keep_annot(fz_context *ctx, pdf_annot *annot) |
| 16 | { |
| 17 | return fz_keep_imp(ctx, annot, &annot->refs); |
| 18 | } |
| 19 | |
| 20 | void |
| 21 | pdf_drop_annot(fz_context *ctx, pdf_annot *annot) |
| 22 | { |
| 23 | if (fz_drop_imp(ctx, annot, &annot->refs)) |
| 24 | { |
| 25 | pdf_drop_obj(ctx, annot->ap); |
| 26 | pdf_drop_obj(ctx, annot->obj); |
| 27 | fz_free(ctx, annot); |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | void |
| 32 | pdf_drop_annots(fz_context *ctx, pdf_annot *annot) |
| 33 | { |
| 34 | while (annot) |
| 35 | { |
| 36 | pdf_annot *next = annot->next; |
| 37 | pdf_drop_annot(ctx, annot); |
| 38 | annot = next; |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | /* Create transform to fit appearance stream to annotation Rect */ |
| 43 | fz_matrix |
| 44 | pdf_annot_transform(fz_context *ctx, pdf_annot *annot) |
| 45 | { |
| 46 | fz_rect bbox, rect; |
| 47 | fz_matrix matrix; |
| 48 | float w, h, x, y; |
| 49 | |
| 50 | rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
| 51 | bbox = pdf_xobject_bbox(ctx, annot->ap); |
| 52 | matrix = pdf_xobject_matrix(ctx, annot->ap); |
| 53 | |
| 54 | bbox = fz_transform_rect(bbox, matrix); |
| 55 | if (bbox.x1 == bbox.x0) |
| 56 | w = 0; |
| 57 | else |
| 58 | w = (rect.x1 - rect.x0) / (bbox.x1 - bbox.x0); |
| 59 | if (bbox.y1 == bbox.y0) |
| 60 | h = 0; |
| 61 | else |
| 62 | h = (rect.y1 - rect.y0) / (bbox.y1 - bbox.y0); |
| 63 | x = rect.x0 - bbox.x0; |
| 64 | y = rect.y0 - bbox.y0; |
| 65 | |
| 66 | return fz_pre_scale(fz_translate(x, y), w, h); |
| 67 | } |
| 68 | |
| 69 | /* |
| 70 | Internal function for creating a new pdf annotation. |
| 71 | */ |
| 72 | static pdf_annot * |
| 73 | pdf_new_annot(fz_context *ctx, pdf_page *page, pdf_obj *obj) |
| 74 | { |
| 75 | pdf_annot *annot; |
| 76 | |
| 77 | annot = fz_malloc_struct(ctx, pdf_annot); |
| 78 | annot->refs = 1; |
| 79 | annot->page = page; /* only borrowed, as the page owns the annot */ |
| 80 | annot->obj = pdf_keep_obj(ctx, obj); |
| 81 | |
| 82 | return annot; |
| 83 | } |
| 84 | |
| 85 | void |
| 86 | pdf_load_annots(fz_context *ctx, pdf_page *page, pdf_obj *annots) |
| 87 | { |
| 88 | pdf_annot *annot; |
| 89 | pdf_obj *subtype; |
| 90 | int i, n; |
| 91 | |
| 92 | n = pdf_array_len(ctx, annots); |
| 93 | for (i = 0; i < n; ++i) |
| 94 | { |
| 95 | pdf_obj *obj = pdf_array_get(ctx, annots, i); |
| 96 | if (pdf_is_dict(ctx, obj)) |
| 97 | { |
| 98 | subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype)); |
| 99 | if (pdf_name_eq(ctx, subtype, PDF_NAME(Link))) |
| 100 | continue; |
| 101 | if (pdf_name_eq(ctx, subtype, PDF_NAME(Popup))) |
| 102 | continue; |
| 103 | |
| 104 | annot = pdf_new_annot(ctx, page, obj); |
| 105 | fz_try(ctx) |
| 106 | { |
| 107 | pdf_update_annot(ctx, annot); |
| 108 | annot->has_new_ap = 0; |
| 109 | } |
| 110 | fz_catch(ctx) |
| 111 | fz_warn(ctx, "could not update appearance for annotation" ); |
| 112 | |
| 113 | if (pdf_name_eq(ctx, subtype, PDF_NAME(Widget))) |
| 114 | { |
| 115 | *page->widget_tailp = annot; |
| 116 | page->widget_tailp = &annot->next; |
| 117 | } |
| 118 | else |
| 119 | { |
| 120 | *page->annot_tailp = annot; |
| 121 | page->annot_tailp = &annot->next; |
| 122 | } |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | pdf_annot * |
| 128 | pdf_first_annot(fz_context *ctx, pdf_page *page) |
| 129 | { |
| 130 | return page->annots; |
| 131 | } |
| 132 | |
| 133 | pdf_annot * |
| 134 | pdf_next_annot(fz_context *ctx, pdf_annot *annot) |
| 135 | { |
| 136 | return annot->next; |
| 137 | } |
| 138 | |
| 139 | /* |
| 140 | Return the rectangle for an annotation on a page. |
| 141 | */ |
| 142 | fz_rect |
| 143 | pdf_bound_annot(fz_context *ctx, pdf_annot *annot) |
| 144 | { |
| 145 | fz_matrix page_ctm; |
| 146 | fz_rect rect; |
| 147 | int flags; |
| 148 | |
| 149 | rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
| 150 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 151 | |
| 152 | flags = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F)); |
| 153 | if (flags & PDF_ANNOT_IS_NO_ROTATE) |
| 154 | { |
| 155 | int rotate = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, annot->page->obj, PDF_NAME(Rotate))); |
| 156 | fz_point tp = fz_transform_point_xy(rect.x0, rect.y1, page_ctm); |
| 157 | page_ctm = fz_concat(page_ctm, fz_translate(-tp.x, -tp.y)); |
| 158 | page_ctm = fz_concat(page_ctm, fz_rotate(-rotate)); |
| 159 | page_ctm = fz_concat(page_ctm, fz_translate(tp.x, tp.y)); |
| 160 | } |
| 161 | |
| 162 | return fz_transform_rect(rect, page_ctm); |
| 163 | } |
| 164 | |
| 165 | void |
| 166 | pdf_dirty_annot(fz_context *ctx, pdf_annot *annot) |
| 167 | { |
| 168 | annot->needs_new_ap = 1; |
| 169 | if (annot->page && annot->page->doc) |
| 170 | annot->page->doc->dirty = 1; |
| 171 | } |
| 172 | |
| 173 | const char * |
| 174 | pdf_string_from_annot_type(fz_context *ctx, enum pdf_annot_type type) |
| 175 | { |
| 176 | switch (type) |
| 177 | { |
| 178 | case PDF_ANNOT_TEXT: return "Text" ; |
| 179 | case PDF_ANNOT_LINK: return "Link" ; |
| 180 | case PDF_ANNOT_FREE_TEXT: return "FreeText" ; |
| 181 | case PDF_ANNOT_LINE: return "Line" ; |
| 182 | case PDF_ANNOT_SQUARE: return "Square" ; |
| 183 | case PDF_ANNOT_CIRCLE: return "Circle" ; |
| 184 | case PDF_ANNOT_POLYGON: return "Polygon" ; |
| 185 | case PDF_ANNOT_POLY_LINE: return "PolyLine" ; |
| 186 | case PDF_ANNOT_HIGHLIGHT: return "Highlight" ; |
| 187 | case PDF_ANNOT_UNDERLINE: return "Underline" ; |
| 188 | case PDF_ANNOT_SQUIGGLY: return "Squiggly" ; |
| 189 | case PDF_ANNOT_STRIKE_OUT: return "StrikeOut" ; |
| 190 | case PDF_ANNOT_REDACT: return "Redact" ; |
| 191 | case PDF_ANNOT_STAMP: return "Stamp" ; |
| 192 | case PDF_ANNOT_CARET: return "Caret" ; |
| 193 | case PDF_ANNOT_INK: return "Ink" ; |
| 194 | case PDF_ANNOT_POPUP: return "Popup" ; |
| 195 | case PDF_ANNOT_FILE_ATTACHMENT: return "FileAttachment" ; |
| 196 | case PDF_ANNOT_SOUND: return "Sound" ; |
| 197 | case PDF_ANNOT_MOVIE: return "Movie" ; |
| 198 | case PDF_ANNOT_WIDGET: return "Widget" ; |
| 199 | case PDF_ANNOT_SCREEN: return "Screen" ; |
| 200 | case PDF_ANNOT_PRINTER_MARK: return "PrinterMark" ; |
| 201 | case PDF_ANNOT_TRAP_NET: return "TrapNet" ; |
| 202 | case PDF_ANNOT_WATERMARK: return "Watermark" ; |
| 203 | case PDF_ANNOT_3D: return "3D" ; |
| 204 | default: return "UNKNOWN" ; |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | int |
| 209 | pdf_annot_type_from_string(fz_context *ctx, const char *subtype) |
| 210 | { |
| 211 | if (!strcmp("Text" , subtype)) return PDF_ANNOT_TEXT; |
| 212 | if (!strcmp("Link" , subtype)) return PDF_ANNOT_LINK; |
| 213 | if (!strcmp("FreeText" , subtype)) return PDF_ANNOT_FREE_TEXT; |
| 214 | if (!strcmp("Line" , subtype)) return PDF_ANNOT_LINE; |
| 215 | if (!strcmp("Square" , subtype)) return PDF_ANNOT_SQUARE; |
| 216 | if (!strcmp("Circle" , subtype)) return PDF_ANNOT_CIRCLE; |
| 217 | if (!strcmp("Polygon" , subtype)) return PDF_ANNOT_POLYGON; |
| 218 | if (!strcmp("PolyLine" , subtype)) return PDF_ANNOT_POLY_LINE; |
| 219 | if (!strcmp("Highlight" , subtype)) return PDF_ANNOT_HIGHLIGHT; |
| 220 | if (!strcmp("Underline" , subtype)) return PDF_ANNOT_UNDERLINE; |
| 221 | if (!strcmp("Squiggly" , subtype)) return PDF_ANNOT_SQUIGGLY; |
| 222 | if (!strcmp("StrikeOut" , subtype)) return PDF_ANNOT_STRIKE_OUT; |
| 223 | if (!strcmp("Redact" , subtype)) return PDF_ANNOT_REDACT; |
| 224 | if (!strcmp("Stamp" , subtype)) return PDF_ANNOT_STAMP; |
| 225 | if (!strcmp("Caret" , subtype)) return PDF_ANNOT_CARET; |
| 226 | if (!strcmp("Ink" , subtype)) return PDF_ANNOT_INK; |
| 227 | if (!strcmp("Popup" , subtype)) return PDF_ANNOT_POPUP; |
| 228 | if (!strcmp("FileAttachment" , subtype)) return PDF_ANNOT_FILE_ATTACHMENT; |
| 229 | if (!strcmp("Sound" , subtype)) return PDF_ANNOT_SOUND; |
| 230 | if (!strcmp("Movie" , subtype)) return PDF_ANNOT_MOVIE; |
| 231 | if (!strcmp("Widget" , subtype)) return PDF_ANNOT_WIDGET; |
| 232 | if (!strcmp("Screen" , subtype)) return PDF_ANNOT_SCREEN; |
| 233 | if (!strcmp("PrinterMark" , subtype)) return PDF_ANNOT_PRINTER_MARK; |
| 234 | if (!strcmp("TrapNet" , subtype)) return PDF_ANNOT_TRAP_NET; |
| 235 | if (!strcmp("Watermark" , subtype)) return PDF_ANNOT_WATERMARK; |
| 236 | if (!strcmp("3D" , subtype)) return PDF_ANNOT_3D; |
| 237 | return PDF_ANNOT_UNKNOWN; |
| 238 | } |
| 239 | |
| 240 | static int is_allowed_subtype(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed) |
| 241 | { |
| 242 | pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); |
| 243 | while (*allowed) { |
| 244 | if (pdf_name_eq(ctx, subtype, *allowed)) |
| 245 | return 1; |
| 246 | allowed++; |
| 247 | } |
| 248 | |
| 249 | return 0; |
| 250 | } |
| 251 | |
| 252 | static void check_allowed_subtypes(fz_context *ctx, pdf_annot *annot, pdf_obj *property, pdf_obj **allowed) |
| 253 | { |
| 254 | pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); |
| 255 | if (!is_allowed_subtype(ctx, annot, property, allowed)) |
| 256 | fz_throw(ctx, FZ_ERROR_GENERIC, "%s annotations have no %s property" , pdf_to_name(ctx, subtype), pdf_to_name(ctx, property)); |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | create a new annotation of the specified type on the |
| 261 | specified page. The returned pdf_annot structure is owned by the page |
| 262 | and does not need to be freed. |
| 263 | */ |
| 264 | pdf_annot * |
| 265 | pdf_create_annot_raw(fz_context *ctx, pdf_page *page, enum pdf_annot_type type) |
| 266 | { |
| 267 | pdf_annot *annot = NULL; |
| 268 | pdf_document *doc = page->doc; |
| 269 | pdf_obj *annot_obj = pdf_new_dict(ctx, doc, 0); |
| 270 | pdf_obj *ind_obj = NULL; |
| 271 | |
| 272 | fz_var(annot); |
| 273 | fz_var(ind_obj); |
| 274 | fz_try(ctx) |
| 275 | { |
| 276 | int ind_obj_num; |
| 277 | const char *type_str; |
| 278 | pdf_obj *annot_arr; |
| 279 | |
| 280 | type_str = pdf_string_from_annot_type(ctx, type); |
| 281 | if (type == PDF_ANNOT_UNKNOWN) |
| 282 | fz_throw(ctx, FZ_ERROR_GENERIC, "cannot create unknown annotation" ); |
| 283 | |
| 284 | annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots)); |
| 285 | if (annot_arr == NULL) |
| 286 | { |
| 287 | annot_arr = pdf_new_array(ctx, doc, 0); |
| 288 | pdf_dict_put_drop(ctx, page->obj, PDF_NAME(Annots), annot_arr); |
| 289 | } |
| 290 | |
| 291 | pdf_dict_put(ctx, annot_obj, PDF_NAME(Type), PDF_NAME(Annot)); |
| 292 | pdf_dict_put_name(ctx, annot_obj, PDF_NAME(Subtype), type_str); |
| 293 | |
| 294 | /* |
| 295 | Both annotation object and annotation structure are now created. |
| 296 | Insert the object in the hierarchy and the structure in the |
| 297 | page's array. |
| 298 | */ |
| 299 | ind_obj_num = pdf_create_object(ctx, doc); |
| 300 | pdf_update_object(ctx, doc, ind_obj_num, annot_obj); |
| 301 | ind_obj = pdf_new_indirect(ctx, doc, ind_obj_num, 0); |
| 302 | pdf_array_push(ctx, annot_arr, ind_obj); |
| 303 | |
| 304 | annot = pdf_new_annot(ctx, page, ind_obj); |
| 305 | annot->ap = NULL; |
| 306 | |
| 307 | /* |
| 308 | Linking must be done after any call that might throw because |
| 309 | pdf_drop_annots below actually frees a list. Put the new annot |
| 310 | at the end of the list, so that it will be drawn last. |
| 311 | */ |
| 312 | *page->annot_tailp = annot; |
| 313 | page->annot_tailp = &annot->next; |
| 314 | |
| 315 | doc->dirty = 1; |
| 316 | } |
| 317 | fz_always(ctx) |
| 318 | { |
| 319 | pdf_drop_obj(ctx, annot_obj); |
| 320 | pdf_drop_obj(ctx, ind_obj); |
| 321 | } |
| 322 | fz_catch(ctx) |
| 323 | { |
| 324 | pdf_drop_annots(ctx, annot); |
| 325 | fz_rethrow(ctx); |
| 326 | } |
| 327 | |
| 328 | return annot; |
| 329 | } |
| 330 | |
| 331 | pdf_annot * |
| 332 | pdf_create_annot(fz_context *ctx, pdf_page *page, enum pdf_annot_type type) |
| 333 | { |
| 334 | static const float black[3] = { 0, 0, 0 }; |
| 335 | static const float red[3] = { 1, 0, 0 }; |
| 336 | static const float green[3] = { 0, 1, 0 }; |
| 337 | static const float blue[3] = { 0, 0, 1 }; |
| 338 | static const float yellow[3] = { 1, 1, 0 }; |
| 339 | static const float magenta[3] = { 1, 0, 1 }; |
| 340 | |
| 341 | int flags = PDF_ANNOT_IS_PRINT; /* Make printable as default */ |
| 342 | |
| 343 | pdf_annot *annot = pdf_create_annot_raw(ctx, page, type); |
| 344 | |
| 345 | switch (type) |
| 346 | { |
| 347 | default: |
| 348 | break; |
| 349 | |
| 350 | case PDF_ANNOT_TEXT: |
| 351 | case PDF_ANNOT_FILE_ATTACHMENT: |
| 352 | case PDF_ANNOT_SOUND: |
| 353 | { |
| 354 | fz_rect icon_rect = { 12, 12, 12+20, 12+20 }; |
| 355 | flags = PDF_ANNOT_IS_PRINT | PDF_ANNOT_IS_NO_ZOOM | PDF_ANNOT_IS_NO_ROTATE; |
| 356 | pdf_set_annot_rect(ctx, annot, icon_rect); |
| 357 | pdf_set_annot_color(ctx, annot, 3, yellow); |
| 358 | } |
| 359 | break; |
| 360 | |
| 361 | case PDF_ANNOT_FREE_TEXT: |
| 362 | { |
| 363 | fz_rect text_rect = { 12, 12, 12+200, 12+100 }; |
| 364 | |
| 365 | /* Use undocumented Adobe property to match page rotation. */ |
| 366 | int rot = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(Rotate))); |
| 367 | if (rot != 0) |
| 368 | pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Rotate), rot); |
| 369 | |
| 370 | pdf_set_annot_rect(ctx, annot, text_rect); |
| 371 | pdf_set_annot_border(ctx, annot, 0); |
| 372 | pdf_set_annot_default_appearance(ctx, annot, "Helv" , 12, black); |
| 373 | } |
| 374 | break; |
| 375 | |
| 376 | case PDF_ANNOT_STAMP: |
| 377 | { |
| 378 | fz_rect stamp_rect = { 12, 12, 12+190, 12+50 }; |
| 379 | pdf_set_annot_rect(ctx, annot, stamp_rect); |
| 380 | pdf_set_annot_color(ctx, annot, 3, red); |
| 381 | } |
| 382 | break; |
| 383 | |
| 384 | case PDF_ANNOT_CARET: |
| 385 | { |
| 386 | fz_rect caret_rect = { 12, 12, 12+18, 12+15 }; |
| 387 | pdf_set_annot_rect(ctx, annot, caret_rect); |
| 388 | pdf_set_annot_color(ctx, annot, 3, blue); |
| 389 | } |
| 390 | break; |
| 391 | |
| 392 | case PDF_ANNOT_LINE: |
| 393 | { |
| 394 | fz_point a = { 12, 12 }, b = { 12 + 100, 12 + 50 }; |
| 395 | pdf_set_annot_line(ctx, annot, a, b); |
| 396 | pdf_set_annot_border(ctx, annot, 1); |
| 397 | pdf_set_annot_color(ctx, annot, 3, red); |
| 398 | } |
| 399 | break; |
| 400 | |
| 401 | case PDF_ANNOT_SQUARE: |
| 402 | case PDF_ANNOT_CIRCLE: |
| 403 | { |
| 404 | fz_rect shape_rect = { 12, 12, 12+100, 12+50 }; |
| 405 | pdf_set_annot_rect(ctx, annot, shape_rect); |
| 406 | pdf_set_annot_border(ctx, annot, 1); |
| 407 | pdf_set_annot_color(ctx, annot, 3, red); |
| 408 | } |
| 409 | break; |
| 410 | |
| 411 | case PDF_ANNOT_POLYGON: |
| 412 | case PDF_ANNOT_POLY_LINE: |
| 413 | case PDF_ANNOT_INK: |
| 414 | pdf_set_annot_border(ctx, annot, 1); |
| 415 | pdf_set_annot_color(ctx, annot, 3, red); |
| 416 | break; |
| 417 | |
| 418 | case PDF_ANNOT_HIGHLIGHT: |
| 419 | pdf_set_annot_color(ctx, annot, 3, yellow); |
| 420 | break; |
| 421 | case PDF_ANNOT_UNDERLINE: |
| 422 | pdf_set_annot_color(ctx, annot, 3, green); |
| 423 | break; |
| 424 | case PDF_ANNOT_STRIKE_OUT: |
| 425 | pdf_set_annot_color(ctx, annot, 3, red); |
| 426 | break; |
| 427 | case PDF_ANNOT_SQUIGGLY: |
| 428 | pdf_set_annot_color(ctx, annot, 3, magenta); |
| 429 | break; |
| 430 | } |
| 431 | |
| 432 | pdf_dict_put(ctx, annot->obj, PDF_NAME(P), page->obj); |
| 433 | pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags); |
| 434 | |
| 435 | return pdf_keep_annot(ctx, annot); |
| 436 | } |
| 437 | |
| 438 | void |
| 439 | pdf_delete_annot(fz_context *ctx, pdf_page *page, pdf_annot *annot) |
| 440 | { |
| 441 | pdf_document *doc = annot->page->doc; |
| 442 | pdf_annot **annotptr; |
| 443 | pdf_obj *annot_arr; |
| 444 | int i; |
| 445 | |
| 446 | if (annot == NULL) |
| 447 | return; |
| 448 | |
| 449 | /* Remove annot from page's list */ |
| 450 | for (annotptr = &page->annots; *annotptr; annotptr = &(*annotptr)->next) |
| 451 | { |
| 452 | if (*annotptr == annot) |
| 453 | break; |
| 454 | } |
| 455 | |
| 456 | /* Check the passed annotation was of this page */ |
| 457 | if (*annotptr == NULL) |
| 458 | return; |
| 459 | |
| 460 | *annotptr = annot->next; |
| 461 | |
| 462 | /* If the removed annotation was the last in the list adjust the end pointer */ |
| 463 | if (*annotptr == NULL) |
| 464 | page->annot_tailp = annotptr; |
| 465 | |
| 466 | /* Remove the annot from the "Annots" array. */ |
| 467 | annot_arr = pdf_dict_get(ctx, page->obj, PDF_NAME(Annots)); |
| 468 | i = pdf_array_find(ctx, annot_arr, annot->obj); |
| 469 | if (i >= 0) |
| 470 | pdf_array_delete(ctx, annot_arr, i); |
| 471 | |
| 472 | /* The garbage collection pass when saving will remove the annot object, |
| 473 | * removing it here may break files if multiple pages use the same annot. */ |
| 474 | |
| 475 | /* And free it. */ |
| 476 | pdf_drop_annot(ctx, annot); |
| 477 | |
| 478 | doc->dirty = 1; |
| 479 | } |
| 480 | |
| 481 | enum pdf_annot_type |
| 482 | pdf_annot_type(fz_context *ctx, pdf_annot *annot) |
| 483 | { |
| 484 | pdf_obj *obj = annot->obj; |
| 485 | pdf_obj *subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype)); |
| 486 | return pdf_annot_type_from_string(ctx, pdf_to_name(ctx, subtype)); |
| 487 | } |
| 488 | |
| 489 | int |
| 490 | pdf_annot_flags(fz_context *ctx, pdf_annot *annot) |
| 491 | { |
| 492 | return pdf_dict_get_int(ctx, annot->obj, PDF_NAME(F)); |
| 493 | } |
| 494 | |
| 495 | void |
| 496 | pdf_set_annot_flags(fz_context *ctx, pdf_annot *annot, int flags) |
| 497 | { |
| 498 | pdf_dict_put_int(ctx, annot->obj, PDF_NAME(F), flags); |
| 499 | pdf_dirty_annot(ctx, annot); |
| 500 | } |
| 501 | |
| 502 | fz_rect |
| 503 | pdf_annot_rect(fz_context *ctx, pdf_annot *annot) |
| 504 | { |
| 505 | fz_matrix page_ctm; |
| 506 | fz_rect annot_rect; |
| 507 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 508 | annot_rect = pdf_dict_get_rect(ctx, annot->obj, PDF_NAME(Rect)); |
| 509 | return fz_transform_rect(annot_rect, page_ctm); |
| 510 | } |
| 511 | |
| 512 | void |
| 513 | pdf_set_annot_rect(fz_context *ctx, pdf_annot *annot, fz_rect rect) |
| 514 | { |
| 515 | fz_matrix page_ctm, inv_page_ctm; |
| 516 | |
| 517 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 518 | inv_page_ctm = fz_invert_matrix(page_ctm); |
| 519 | rect = fz_transform_rect(rect, inv_page_ctm); |
| 520 | |
| 521 | pdf_dict_put_rect(ctx, annot->obj, PDF_NAME(Rect), rect); |
| 522 | pdf_dirty_annot(ctx, annot); |
| 523 | } |
| 524 | |
| 525 | const char * |
| 526 | pdf_annot_contents(fz_context *ctx, pdf_annot *annot) |
| 527 | { |
| 528 | return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(Contents)); |
| 529 | } |
| 530 | |
| 531 | void |
| 532 | pdf_set_annot_contents(fz_context *ctx, pdf_annot *annot, const char *text) |
| 533 | { |
| 534 | pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(Contents), text); |
| 535 | pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */ |
| 536 | pdf_dirty_annot(ctx, annot); |
| 537 | } |
| 538 | |
| 539 | static pdf_obj *open_subtypes[] = { |
| 540 | PDF_NAME(Popup), |
| 541 | PDF_NAME(Text), |
| 542 | NULL, |
| 543 | }; |
| 544 | |
| 545 | int |
| 546 | pdf_annot_has_open(fz_context *ctx, pdf_annot *annot) |
| 547 | { |
| 548 | return is_allowed_subtype(ctx, annot, PDF_NAME(Open), open_subtypes); |
| 549 | } |
| 550 | |
| 551 | int |
| 552 | pdf_annot_is_open(fz_context *ctx, pdf_annot *annot) |
| 553 | { |
| 554 | check_allowed_subtypes(ctx, annot, PDF_NAME(Open), open_subtypes); |
| 555 | return pdf_dict_get_bool(ctx, annot->obj, PDF_NAME(Open)); |
| 556 | } |
| 557 | |
| 558 | void |
| 559 | pdf_set_annot_is_open(fz_context *ctx, pdf_annot *annot, int is_open) |
| 560 | { |
| 561 | check_allowed_subtypes(ctx, annot, PDF_NAME(Open), open_subtypes); |
| 562 | pdf_dict_put_bool(ctx, annot->obj, PDF_NAME(Open), is_open); |
| 563 | pdf_dirty_annot(ctx, annot); |
| 564 | } |
| 565 | |
| 566 | static pdf_obj *icon_name_subtypes[] = { |
| 567 | PDF_NAME(FileAttachment), |
| 568 | PDF_NAME(Sound), |
| 569 | PDF_NAME(Stamp), |
| 570 | PDF_NAME(Text), |
| 571 | NULL, |
| 572 | }; |
| 573 | |
| 574 | int |
| 575 | pdf_annot_has_icon_name(fz_context *ctx, pdf_annot *annot) |
| 576 | { |
| 577 | return is_allowed_subtype(ctx, annot, PDF_NAME(Name), icon_name_subtypes); |
| 578 | } |
| 579 | |
| 580 | const char * |
| 581 | pdf_annot_icon_name(fz_context *ctx, pdf_annot *annot) |
| 582 | { |
| 583 | pdf_obj *name; |
| 584 | check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes); |
| 585 | name = pdf_dict_get(ctx, annot->obj, PDF_NAME(Name)); |
| 586 | if (!name) |
| 587 | { |
| 588 | pdf_obj *subtype = pdf_dict_get(ctx, annot->obj, PDF_NAME(Subtype)); |
| 589 | if (pdf_name_eq(ctx, subtype, PDF_NAME(Text))) |
| 590 | return "Note" ; |
| 591 | if (pdf_name_eq(ctx, subtype, PDF_NAME(Stamp))) |
| 592 | return "Draft" ; |
| 593 | if (pdf_name_eq(ctx, subtype, PDF_NAME(FileAttachment))) |
| 594 | return "PushPin" ; |
| 595 | if (pdf_name_eq(ctx, subtype, PDF_NAME(Sound))) |
| 596 | return "Speaker" ; |
| 597 | } |
| 598 | return pdf_to_name(ctx, name); |
| 599 | } |
| 600 | |
| 601 | void |
| 602 | pdf_set_annot_icon_name(fz_context *ctx, pdf_annot *annot, const char *name) |
| 603 | { |
| 604 | check_allowed_subtypes(ctx, annot, PDF_NAME(Name), icon_name_subtypes); |
| 605 | pdf_dict_put_name(ctx, annot->obj, PDF_NAME(Name), name); |
| 606 | pdf_dirty_annot(ctx, annot); |
| 607 | } |
| 608 | |
| 609 | enum pdf_line_ending pdf_line_ending_from_name(fz_context *ctx, pdf_obj *end) |
| 610 | { |
| 611 | if (pdf_name_eq(ctx, end, PDF_NAME(None))) return PDF_ANNOT_LE_NONE; |
| 612 | else if (pdf_name_eq(ctx, end, PDF_NAME(Square))) return PDF_ANNOT_LE_SQUARE; |
| 613 | else if (pdf_name_eq(ctx, end, PDF_NAME(Circle))) return PDF_ANNOT_LE_CIRCLE; |
| 614 | else if (pdf_name_eq(ctx, end, PDF_NAME(Diamond))) return PDF_ANNOT_LE_DIAMOND; |
| 615 | else if (pdf_name_eq(ctx, end, PDF_NAME(OpenArrow))) return PDF_ANNOT_LE_OPEN_ARROW; |
| 616 | else if (pdf_name_eq(ctx, end, PDF_NAME(ClosedArrow))) return PDF_ANNOT_LE_CLOSED_ARROW; |
| 617 | else if (pdf_name_eq(ctx, end, PDF_NAME(Butt))) return PDF_ANNOT_LE_BUTT; |
| 618 | else if (pdf_name_eq(ctx, end, PDF_NAME(ROpenArrow))) return PDF_ANNOT_LE_R_OPEN_ARROW; |
| 619 | else if (pdf_name_eq(ctx, end, PDF_NAME(RClosedArrow))) return PDF_ANNOT_LE_R_CLOSED_ARROW; |
| 620 | else if (pdf_name_eq(ctx, end, PDF_NAME(Slash))) return PDF_ANNOT_LE_SLASH; |
| 621 | else return PDF_ANNOT_LE_NONE; |
| 622 | } |
| 623 | |
| 624 | enum pdf_line_ending pdf_line_ending_from_string(fz_context *ctx, const char *end) |
| 625 | { |
| 626 | if (!strcmp(end, "None" )) return PDF_ANNOT_LE_NONE; |
| 627 | else if (!strcmp(end, "Square" )) return PDF_ANNOT_LE_SQUARE; |
| 628 | else if (!strcmp(end, "Circle" )) return PDF_ANNOT_LE_CIRCLE; |
| 629 | else if (!strcmp(end, "Diamond" )) return PDF_ANNOT_LE_DIAMOND; |
| 630 | else if (!strcmp(end, "OpenArrow" )) return PDF_ANNOT_LE_OPEN_ARROW; |
| 631 | else if (!strcmp(end, "ClosedArrow" )) return PDF_ANNOT_LE_CLOSED_ARROW; |
| 632 | else if (!strcmp(end, "Butt" )) return PDF_ANNOT_LE_BUTT; |
| 633 | else if (!strcmp(end, "ROpenArrow" )) return PDF_ANNOT_LE_R_OPEN_ARROW; |
| 634 | else if (!strcmp(end, "RClosedArrow" )) return PDF_ANNOT_LE_R_CLOSED_ARROW; |
| 635 | else if (!strcmp(end, "Slash" )) return PDF_ANNOT_LE_SLASH; |
| 636 | else return PDF_ANNOT_LE_NONE; |
| 637 | } |
| 638 | |
| 639 | pdf_obj *pdf_name_from_line_ending(fz_context *ctx, enum pdf_line_ending end) |
| 640 | { |
| 641 | switch (end) |
| 642 | { |
| 643 | default: |
| 644 | case PDF_ANNOT_LE_NONE: return PDF_NAME(None); |
| 645 | case PDF_ANNOT_LE_SQUARE: return PDF_NAME(Square); |
| 646 | case PDF_ANNOT_LE_CIRCLE: return PDF_NAME(Circle); |
| 647 | case PDF_ANNOT_LE_DIAMOND: return PDF_NAME(Diamond); |
| 648 | case PDF_ANNOT_LE_OPEN_ARROW: return PDF_NAME(OpenArrow); |
| 649 | case PDF_ANNOT_LE_CLOSED_ARROW: return PDF_NAME(ClosedArrow); |
| 650 | case PDF_ANNOT_LE_BUTT: return PDF_NAME(Butt); |
| 651 | case PDF_ANNOT_LE_R_OPEN_ARROW: return PDF_NAME(ROpenArrow); |
| 652 | case PDF_ANNOT_LE_R_CLOSED_ARROW: return PDF_NAME(RClosedArrow); |
| 653 | case PDF_ANNOT_LE_SLASH: return PDF_NAME(Slash); |
| 654 | } |
| 655 | } |
| 656 | |
| 657 | const char *pdf_string_from_line_ending(fz_context *ctx, enum pdf_line_ending end) |
| 658 | { |
| 659 | switch (end) |
| 660 | { |
| 661 | default: |
| 662 | case PDF_ANNOT_LE_NONE: return "None" ; |
| 663 | case PDF_ANNOT_LE_SQUARE: return "Square" ; |
| 664 | case PDF_ANNOT_LE_CIRCLE: return "Circle" ; |
| 665 | case PDF_ANNOT_LE_DIAMOND: return "Diamond" ; |
| 666 | case PDF_ANNOT_LE_OPEN_ARROW: return "OpenArrow" ; |
| 667 | case PDF_ANNOT_LE_CLOSED_ARROW: return "ClosedArrow" ; |
| 668 | case PDF_ANNOT_LE_BUTT: return "Butt" ; |
| 669 | case PDF_ANNOT_LE_R_OPEN_ARROW: return "ROpenArrow" ; |
| 670 | case PDF_ANNOT_LE_R_CLOSED_ARROW: return "RClosedArrow" ; |
| 671 | case PDF_ANNOT_LE_SLASH: return "Slash" ; |
| 672 | } |
| 673 | } |
| 674 | |
| 675 | static pdf_obj *line_ending_subtypes[] = { |
| 676 | PDF_NAME(FreeText), |
| 677 | PDF_NAME(Line), |
| 678 | PDF_NAME(PolyLine), |
| 679 | PDF_NAME(Polygon), |
| 680 | NULL, |
| 681 | }; |
| 682 | |
| 683 | int |
| 684 | pdf_annot_has_line_ending_styles(fz_context *ctx, pdf_annot *annot) |
| 685 | { |
| 686 | return is_allowed_subtype(ctx, annot, PDF_NAME(LE), line_ending_subtypes); |
| 687 | } |
| 688 | |
| 689 | void |
| 690 | pdf_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot, |
| 691 | enum pdf_line_ending *start_style, |
| 692 | enum pdf_line_ending *end_style) |
| 693 | { |
| 694 | pdf_obj *style; |
| 695 | check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes); |
| 696 | style = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); |
| 697 | *start_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 0)); |
| 698 | *end_style = pdf_line_ending_from_name(ctx, pdf_array_get(ctx, style, 1)); |
| 699 | } |
| 700 | |
| 701 | enum pdf_line_ending |
| 702 | pdf_annot_line_start_style(fz_context *ctx, pdf_annot *annot) |
| 703 | { |
| 704 | pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); |
| 705 | return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 0)); |
| 706 | } |
| 707 | |
| 708 | enum pdf_line_ending |
| 709 | pdf_annot_line_end_style(fz_context *ctx, pdf_annot *annot) |
| 710 | { |
| 711 | pdf_obj *le = pdf_dict_get(ctx, annot->obj, PDF_NAME(LE)); |
| 712 | return pdf_line_ending_from_name(ctx, pdf_array_get(ctx, le, 1)); |
| 713 | } |
| 714 | |
| 715 | void |
| 716 | pdf_set_annot_line_ending_styles(fz_context *ctx, pdf_annot *annot, |
| 717 | enum pdf_line_ending start_style, |
| 718 | enum pdf_line_ending end_style) |
| 719 | { |
| 720 | pdf_document *doc = annot->page->doc; |
| 721 | pdf_obj *style; |
| 722 | check_allowed_subtypes(ctx, annot, PDF_NAME(LE), line_ending_subtypes); |
| 723 | style = pdf_new_array(ctx, doc, 2); |
| 724 | pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(LE), style); |
| 725 | pdf_array_put_drop(ctx, style, 0, pdf_name_from_line_ending(ctx, start_style)); |
| 726 | pdf_array_put_drop(ctx, style, 1, pdf_name_from_line_ending(ctx, end_style)); |
| 727 | pdf_dirty_annot(ctx, annot); |
| 728 | } |
| 729 | |
| 730 | void |
| 731 | pdf_set_annot_line_start_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending s) |
| 732 | { |
| 733 | enum pdf_line_ending e = pdf_annot_line_end_style(ctx, annot); |
| 734 | pdf_set_annot_line_ending_styles(ctx, annot, s, e); |
| 735 | } |
| 736 | |
| 737 | void |
| 738 | pdf_set_annot_line_end_style(fz_context *ctx, pdf_annot *annot, enum pdf_line_ending e) |
| 739 | { |
| 740 | enum pdf_line_ending s = pdf_annot_line_start_style(ctx, annot); |
| 741 | pdf_set_annot_line_ending_styles(ctx, annot, s, e); |
| 742 | } |
| 743 | |
| 744 | float |
| 745 | pdf_annot_border(fz_context *ctx, pdf_annot *annot) |
| 746 | { |
| 747 | pdf_obj *bs, *bs_w; |
| 748 | bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); |
| 749 | bs_w = pdf_dict_get(ctx, bs, PDF_NAME(W)); |
| 750 | if (pdf_is_number(ctx, bs_w)) |
| 751 | return pdf_to_real(ctx, bs_w); |
| 752 | return 1; |
| 753 | } |
| 754 | |
| 755 | void |
| 756 | pdf_set_annot_border(fz_context *ctx, pdf_annot *annot, float w) |
| 757 | { |
| 758 | pdf_obj *bs = pdf_dict_get(ctx, annot->obj, PDF_NAME(BS)); |
| 759 | if (!pdf_is_dict(ctx, bs)) |
| 760 | bs = pdf_dict_put_dict(ctx, annot->obj, PDF_NAME(BS), 1); |
| 761 | pdf_dict_put_real(ctx, bs, PDF_NAME(W), w); |
| 762 | |
| 763 | pdf_dict_del(ctx, annot->obj, PDF_NAME(Border)); /* deprecated */ |
| 764 | pdf_dict_del(ctx, annot->obj, PDF_NAME(BE)); /* not supported */ |
| 765 | |
| 766 | pdf_dirty_annot(ctx, annot); |
| 767 | } |
| 768 | |
| 769 | int |
| 770 | pdf_annot_quadding(fz_context *ctx, pdf_annot *annot) |
| 771 | { |
| 772 | int q = pdf_dict_get_int(ctx, annot->obj, PDF_NAME(Q)); |
| 773 | return (q < 0 || q > 2) ? 0 : q; |
| 774 | } |
| 775 | |
| 776 | void |
| 777 | pdf_set_annot_quadding(fz_context *ctx, pdf_annot *annot, int q) |
| 778 | { |
| 779 | q = (q < 0 || q > 2) ? 0 : q; |
| 780 | pdf_dict_put_int(ctx, annot->obj, PDF_NAME(Q), q); |
| 781 | pdf_dirty_annot(ctx, annot); |
| 782 | } |
| 783 | |
| 784 | float pdf_annot_opacity(fz_context *ctx, pdf_annot *annot) |
| 785 | { |
| 786 | pdf_obj *ca = pdf_dict_get(ctx, annot->obj, PDF_NAME(CA)); |
| 787 | if (pdf_is_number(ctx, ca)) |
| 788 | return pdf_to_real(ctx, ca); |
| 789 | return 1; |
| 790 | } |
| 791 | |
| 792 | void pdf_set_annot_opacity(fz_context *ctx, pdf_annot *annot, float opacity) |
| 793 | { |
| 794 | if (opacity != 1) |
| 795 | pdf_dict_put_real(ctx, annot->obj, PDF_NAME(CA), opacity); |
| 796 | else |
| 797 | pdf_dict_del(ctx, annot->obj, PDF_NAME(CA)); |
| 798 | pdf_dirty_annot(ctx, annot); |
| 799 | } |
| 800 | |
| 801 | static void pdf_annot_color_imp(fz_context *ctx, pdf_obj *arr, int *n, float color[4]) |
| 802 | { |
| 803 | switch (pdf_array_len(ctx, arr)) |
| 804 | { |
| 805 | case 0: |
| 806 | if (n) |
| 807 | *n = 0; |
| 808 | break; |
| 809 | case 1: |
| 810 | case 2: |
| 811 | if (n) |
| 812 | *n = 1; |
| 813 | if (color) |
| 814 | color[0] = pdf_array_get_real(ctx, arr, 0); |
| 815 | break; |
| 816 | case 3: |
| 817 | if (n) |
| 818 | *n = 3; |
| 819 | if (color) |
| 820 | { |
| 821 | color[0] = pdf_array_get_real(ctx, arr, 0); |
| 822 | color[1] = pdf_array_get_real(ctx, arr, 1); |
| 823 | color[2] = pdf_array_get_real(ctx, arr, 2); |
| 824 | } |
| 825 | break; |
| 826 | case 4: |
| 827 | default: |
| 828 | if (n) |
| 829 | *n = 4; |
| 830 | if (color) |
| 831 | { |
| 832 | color[0] = pdf_array_get_real(ctx, arr, 0); |
| 833 | color[1] = pdf_array_get_real(ctx, arr, 1); |
| 834 | color[2] = pdf_array_get_real(ctx, arr, 2); |
| 835 | color[3] = pdf_array_get_real(ctx, arr, 3); |
| 836 | } |
| 837 | break; |
| 838 | } |
| 839 | } |
| 840 | |
| 841 | static int pdf_annot_color_rgb(fz_context *ctx, pdf_obj *arr, float rgb[3]) |
| 842 | { |
| 843 | float color[4]; |
| 844 | int n; |
| 845 | pdf_annot_color_imp(ctx, arr, &n, color); |
| 846 | if (n == 0) |
| 847 | { |
| 848 | return 0; |
| 849 | } |
| 850 | else if (n == 1) |
| 851 | { |
| 852 | rgb[0] = rgb[1] = rgb[2] = color[0]; |
| 853 | } |
| 854 | else if (n == 3) |
| 855 | { |
| 856 | rgb[0] = color[0]; |
| 857 | rgb[1] = color[1]; |
| 858 | rgb[2] = color[2]; |
| 859 | } |
| 860 | else if (n == 4) |
| 861 | { |
| 862 | rgb[0] = 1 - fz_min(1, color[0] + color[3]); |
| 863 | rgb[1] = 1 - fz_min(1, color[1] + color[3]); |
| 864 | rgb[2] = 1 - fz_min(1, color[2] + color[3]); |
| 865 | } |
| 866 | return 1; |
| 867 | } |
| 868 | |
| 869 | static void pdf_set_annot_color_imp(fz_context *ctx, pdf_annot *annot, pdf_obj *key, int n, const float color[4], pdf_obj **allowed) |
| 870 | { |
| 871 | pdf_document *doc = annot->page->doc; |
| 872 | pdf_obj *arr; |
| 873 | |
| 874 | if (allowed) |
| 875 | check_allowed_subtypes(ctx, annot, key, allowed); |
| 876 | if (n != 0 && n != 1 && n != 3 && n != 4) |
| 877 | fz_throw(ctx, FZ_ERROR_GENERIC, "color must be 0, 1, 3 or 4 components" ); |
| 878 | if (!color) |
| 879 | fz_throw(ctx, FZ_ERROR_GENERIC, "no color given" ); |
| 880 | |
| 881 | arr = pdf_new_array(ctx, doc, n); |
| 882 | fz_try(ctx) |
| 883 | { |
| 884 | switch (n) |
| 885 | { |
| 886 | case 1: |
| 887 | pdf_array_push_real(ctx, arr, color[0]); |
| 888 | break; |
| 889 | case 3: |
| 890 | pdf_array_push_real(ctx, arr, color[0]); |
| 891 | pdf_array_push_real(ctx, arr, color[1]); |
| 892 | pdf_array_push_real(ctx, arr, color[2]); |
| 893 | break; |
| 894 | case 4: |
| 895 | pdf_array_push_real(ctx, arr, color[0]); |
| 896 | pdf_array_push_real(ctx, arr, color[1]); |
| 897 | pdf_array_push_real(ctx, arr, color[2]); |
| 898 | pdf_array_push_real(ctx, arr, color[3]); |
| 899 | break; |
| 900 | } |
| 901 | } |
| 902 | fz_catch(ctx) |
| 903 | { |
| 904 | pdf_drop_obj(ctx, arr); |
| 905 | fz_rethrow(ctx); |
| 906 | } |
| 907 | |
| 908 | pdf_dict_put_drop(ctx, annot->obj, key, arr); |
| 909 | pdf_dirty_annot(ctx, annot); |
| 910 | } |
| 911 | |
| 912 | void |
| 913 | pdf_annot_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4]) |
| 914 | { |
| 915 | pdf_obj *c = pdf_dict_get(ctx, annot->obj, PDF_NAME(C)); |
| 916 | pdf_annot_color_imp(ctx, c, n, color); |
| 917 | } |
| 918 | |
| 919 | void |
| 920 | pdf_annot_MK_BG(fz_context *ctx, pdf_annot *annot, int *n, float color[4]) |
| 921 | { |
| 922 | pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG)); |
| 923 | pdf_annot_color_imp(ctx, mk_bg, n, color); |
| 924 | } |
| 925 | |
| 926 | int |
| 927 | pdf_annot_MK_BG_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3]) |
| 928 | { |
| 929 | pdf_obj *mk_bg = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BG)); |
| 930 | return pdf_annot_color_rgb(ctx, mk_bg, rgb); |
| 931 | } |
| 932 | |
| 933 | void |
| 934 | pdf_annot_MK_BC(fz_context *ctx, pdf_annot *annot, int *n, float color[4]) |
| 935 | { |
| 936 | pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC)); |
| 937 | pdf_annot_color_imp(ctx, mk_bc, n, color); |
| 938 | } |
| 939 | |
| 940 | int |
| 941 | pdf_annot_MK_BC_rgb(fz_context *ctx, pdf_annot *annot, float rgb[3]) |
| 942 | { |
| 943 | pdf_obj *mk_bc = pdf_dict_get(ctx, pdf_dict_get(ctx, annot->obj, PDF_NAME(MK)), PDF_NAME(BC)); |
| 944 | return pdf_annot_color_rgb(ctx, mk_bc, rgb); |
| 945 | } |
| 946 | |
| 947 | void |
| 948 | pdf_set_annot_color(fz_context *ctx, pdf_annot *annot, int n, const float color[4]) |
| 949 | { |
| 950 | pdf_set_annot_color_imp(ctx, annot, PDF_NAME(C), n, color, NULL); |
| 951 | } |
| 952 | |
| 953 | static pdf_obj *interior_color_subtypes[] = { |
| 954 | PDF_NAME(Circle), |
| 955 | PDF_NAME(Line), |
| 956 | PDF_NAME(PolyLine), |
| 957 | PDF_NAME(Polygon), |
| 958 | PDF_NAME(Square), |
| 959 | NULL, |
| 960 | }; |
| 961 | |
| 962 | int |
| 963 | pdf_annot_has_interior_color(fz_context *ctx, pdf_annot *annot) |
| 964 | { |
| 965 | return is_allowed_subtype(ctx, annot, PDF_NAME(IC), interior_color_subtypes); |
| 966 | } |
| 967 | |
| 968 | void |
| 969 | pdf_annot_interior_color(fz_context *ctx, pdf_annot *annot, int *n, float color[4]) |
| 970 | { |
| 971 | pdf_obj *ic = pdf_dict_get(ctx, annot->obj, PDF_NAME(IC)); |
| 972 | pdf_annot_color_imp(ctx, ic, n, color); |
| 973 | } |
| 974 | |
| 975 | void |
| 976 | pdf_set_annot_interior_color(fz_context *ctx, pdf_annot *annot, int n, const float color[4]) |
| 977 | { |
| 978 | pdf_set_annot_color_imp(ctx, annot, PDF_NAME(IC), n, color, interior_color_subtypes); |
| 979 | } |
| 980 | |
| 981 | static pdf_obj *line_subtypes[] = { |
| 982 | PDF_NAME(Line), |
| 983 | NULL, |
| 984 | }; |
| 985 | |
| 986 | int |
| 987 | pdf_annot_has_line(fz_context *ctx, pdf_annot *annot) |
| 988 | { |
| 989 | return is_allowed_subtype(ctx, annot, PDF_NAME(L), line_subtypes); |
| 990 | } |
| 991 | |
| 992 | void |
| 993 | pdf_annot_line(fz_context *ctx, pdf_annot *annot, fz_point *a, fz_point *b) |
| 994 | { |
| 995 | fz_matrix page_ctm; |
| 996 | pdf_obj *line; |
| 997 | |
| 998 | check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes); |
| 999 | |
| 1000 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1001 | |
| 1002 | line = pdf_dict_get(ctx, annot->obj, PDF_NAME(L)); |
| 1003 | a->x = pdf_array_get_real(ctx, line, 0); |
| 1004 | a->y = pdf_array_get_real(ctx, line, 1); |
| 1005 | b->x = pdf_array_get_real(ctx, line, 2); |
| 1006 | b->y = pdf_array_get_real(ctx, line, 3); |
| 1007 | *a = fz_transform_point(*a, page_ctm); |
| 1008 | *b = fz_transform_point(*b, page_ctm); |
| 1009 | } |
| 1010 | |
| 1011 | void |
| 1012 | pdf_set_annot_line(fz_context *ctx, pdf_annot *annot, fz_point a, fz_point b) |
| 1013 | { |
| 1014 | fz_matrix page_ctm, inv_page_ctm; |
| 1015 | pdf_obj *line; |
| 1016 | |
| 1017 | check_allowed_subtypes(ctx, annot, PDF_NAME(L), line_subtypes); |
| 1018 | |
| 1019 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1020 | inv_page_ctm = fz_invert_matrix(page_ctm); |
| 1021 | |
| 1022 | a = fz_transform_point(a, inv_page_ctm); |
| 1023 | b = fz_transform_point(b, inv_page_ctm); |
| 1024 | |
| 1025 | line = pdf_new_array(ctx, annot->page->doc, 4); |
| 1026 | pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(L), line); |
| 1027 | pdf_array_push_real(ctx, line, a.x); |
| 1028 | pdf_array_push_real(ctx, line, a.y); |
| 1029 | pdf_array_push_real(ctx, line, b.x); |
| 1030 | pdf_array_push_real(ctx, line, b.y); |
| 1031 | |
| 1032 | pdf_dirty_annot(ctx, annot); |
| 1033 | } |
| 1034 | |
| 1035 | static pdf_obj *vertices_subtypes[] = { |
| 1036 | PDF_NAME(PolyLine), |
| 1037 | PDF_NAME(Polygon), |
| 1038 | NULL, |
| 1039 | }; |
| 1040 | |
| 1041 | int |
| 1042 | pdf_annot_has_vertices(fz_context *ctx, pdf_annot *annot) |
| 1043 | { |
| 1044 | return is_allowed_subtype(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); |
| 1045 | } |
| 1046 | |
| 1047 | int |
| 1048 | pdf_annot_vertex_count(fz_context *ctx, pdf_annot *annot) |
| 1049 | { |
| 1050 | pdf_obj *vertices; |
| 1051 | check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); |
| 1052 | vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); |
| 1053 | return pdf_array_len(ctx, vertices) / 2; |
| 1054 | } |
| 1055 | |
| 1056 | fz_point |
| 1057 | pdf_annot_vertex(fz_context *ctx, pdf_annot *annot, int i) |
| 1058 | { |
| 1059 | pdf_obj *vertices; |
| 1060 | fz_matrix page_ctm; |
| 1061 | fz_point point; |
| 1062 | |
| 1063 | check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); |
| 1064 | |
| 1065 | vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); |
| 1066 | |
| 1067 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1068 | |
| 1069 | point.x = pdf_array_get_real(ctx, vertices, i * 2); |
| 1070 | point.y = pdf_array_get_real(ctx, vertices, i * 2 + 1); |
| 1071 | return fz_transform_point(point, page_ctm); |
| 1072 | } |
| 1073 | |
| 1074 | void |
| 1075 | pdf_set_annot_vertices(fz_context *ctx, pdf_annot *annot, int n, const fz_point *v) |
| 1076 | { |
| 1077 | pdf_document *doc = annot->page->doc; |
| 1078 | fz_matrix page_ctm, inv_page_ctm; |
| 1079 | pdf_obj *vertices; |
| 1080 | fz_point point; |
| 1081 | int i; |
| 1082 | |
| 1083 | check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); |
| 1084 | if (n <= 0 || !v) |
| 1085 | fz_throw(ctx, FZ_ERROR_GENERIC, "invalid number of vertices" ); |
| 1086 | |
| 1087 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1088 | inv_page_ctm = fz_invert_matrix(page_ctm); |
| 1089 | |
| 1090 | vertices = pdf_new_array(ctx, doc, n * 2); |
| 1091 | for (i = 0; i < n; ++i) |
| 1092 | { |
| 1093 | point = fz_transform_point(v[i], inv_page_ctm); |
| 1094 | pdf_array_push_real(ctx, vertices, point.x); |
| 1095 | pdf_array_push_real(ctx, vertices, point.y); |
| 1096 | } |
| 1097 | pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(Vertices), vertices); |
| 1098 | pdf_dirty_annot(ctx, annot); |
| 1099 | } |
| 1100 | |
| 1101 | void pdf_clear_annot_vertices(fz_context *ctx, pdf_annot *annot) |
| 1102 | { |
| 1103 | check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); |
| 1104 | pdf_dict_del(ctx, annot->obj, PDF_NAME(Vertices)); |
| 1105 | pdf_dirty_annot(ctx, annot); |
| 1106 | } |
| 1107 | |
| 1108 | void pdf_add_annot_vertex(fz_context *ctx, pdf_annot *annot, fz_point p) |
| 1109 | { |
| 1110 | pdf_document *doc = annot->page->doc; |
| 1111 | fz_matrix page_ctm, inv_page_ctm; |
| 1112 | pdf_obj *vertices; |
| 1113 | |
| 1114 | check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); |
| 1115 | |
| 1116 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1117 | inv_page_ctm = fz_invert_matrix(page_ctm); |
| 1118 | |
| 1119 | vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); |
| 1120 | if (!pdf_is_array(ctx, vertices)) |
| 1121 | { |
| 1122 | vertices = pdf_new_array(ctx, doc, 32); |
| 1123 | pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(Vertices), vertices); |
| 1124 | } |
| 1125 | |
| 1126 | p = fz_transform_point(p, inv_page_ctm); |
| 1127 | pdf_array_push_real(ctx, vertices, p.x); |
| 1128 | pdf_array_push_real(ctx, vertices, p.y); |
| 1129 | |
| 1130 | pdf_dirty_annot(ctx, annot); |
| 1131 | } |
| 1132 | |
| 1133 | void pdf_set_annot_vertex(fz_context *ctx, pdf_annot *annot, int i, fz_point p) |
| 1134 | { |
| 1135 | fz_matrix page_ctm, inv_page_ctm; |
| 1136 | pdf_obj *vertices; |
| 1137 | |
| 1138 | check_allowed_subtypes(ctx, annot, PDF_NAME(Vertices), vertices_subtypes); |
| 1139 | |
| 1140 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1141 | inv_page_ctm = fz_invert_matrix(page_ctm); |
| 1142 | |
| 1143 | p = fz_transform_point(p, inv_page_ctm); |
| 1144 | |
| 1145 | vertices = pdf_dict_get(ctx, annot->obj, PDF_NAME(Vertices)); |
| 1146 | pdf_array_put_drop(ctx, vertices, i * 2 + 0, pdf_new_real(ctx, p.x)); |
| 1147 | pdf_array_put_drop(ctx, vertices, i * 2 + 1, pdf_new_real(ctx, p.y)); |
| 1148 | } |
| 1149 | |
| 1150 | static pdf_obj *quad_point_subtypes[] = { |
| 1151 | PDF_NAME(Highlight), |
| 1152 | PDF_NAME(Link), |
| 1153 | PDF_NAME(Squiggly), |
| 1154 | PDF_NAME(StrikeOut), |
| 1155 | PDF_NAME(Underline), |
| 1156 | PDF_NAME(Redact), |
| 1157 | NULL, |
| 1158 | }; |
| 1159 | |
| 1160 | int |
| 1161 | pdf_annot_has_quad_points(fz_context *ctx, pdf_annot *annot) |
| 1162 | { |
| 1163 | return is_allowed_subtype(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); |
| 1164 | } |
| 1165 | |
| 1166 | int |
| 1167 | pdf_annot_quad_point_count(fz_context *ctx, pdf_annot *annot) |
| 1168 | { |
| 1169 | pdf_obj *quad_points; |
| 1170 | check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); |
| 1171 | quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
| 1172 | return pdf_array_len(ctx, quad_points) / 8; |
| 1173 | } |
| 1174 | |
| 1175 | void |
| 1176 | pdf_annot_quad_point(fz_context *ctx, pdf_annot *annot, int idx, float v[8]) |
| 1177 | { |
| 1178 | pdf_obj *quad_points; |
| 1179 | fz_matrix page_ctm; |
| 1180 | int i; |
| 1181 | |
| 1182 | check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); |
| 1183 | quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
| 1184 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1185 | |
| 1186 | for (i = 0; i < 8; i += 2) |
| 1187 | { |
| 1188 | fz_point point; |
| 1189 | point.x = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 0); |
| 1190 | point.y = pdf_array_get_real(ctx, quad_points, idx * 8 + i + 1); |
| 1191 | point = fz_transform_point(point, page_ctm); |
| 1192 | v[i+0] = point.x; |
| 1193 | v[i+1] = point.y; |
| 1194 | } |
| 1195 | } |
| 1196 | |
| 1197 | void |
| 1198 | pdf_set_annot_quad_points(fz_context *ctx, pdf_annot *annot, int n, const float *v) |
| 1199 | { |
| 1200 | pdf_document *doc = annot->page->doc; |
| 1201 | fz_matrix page_ctm, inv_page_ctm; |
| 1202 | pdf_obj *quad_points; |
| 1203 | fz_point point; |
| 1204 | int i, k; |
| 1205 | |
| 1206 | check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); |
| 1207 | if (n <= 0 || !v) |
| 1208 | fz_throw(ctx, FZ_ERROR_GENERIC, "invalid number of quadrilaterals" ); |
| 1209 | |
| 1210 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1211 | inv_page_ctm = fz_invert_matrix(page_ctm); |
| 1212 | |
| 1213 | quad_points = pdf_new_array(ctx, doc, n * 8); |
| 1214 | for (i = 0; i < n; ++i) |
| 1215 | { |
| 1216 | for (k = 0; k < 4; ++k) |
| 1217 | { |
| 1218 | point.x = v[i * 8 + k * 2 + 0]; |
| 1219 | point.y = v[i * 8 + k * 2 + 1]; |
| 1220 | point = fz_transform_point(point, inv_page_ctm); |
| 1221 | pdf_array_push_real(ctx, quad_points, point.x); |
| 1222 | pdf_array_push_real(ctx, quad_points, point.y); |
| 1223 | } |
| 1224 | } |
| 1225 | pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(QuadPoints), quad_points); |
| 1226 | pdf_dirty_annot(ctx, annot); |
| 1227 | } |
| 1228 | |
| 1229 | void |
| 1230 | pdf_clear_annot_quad_points(fz_context *ctx, pdf_annot *annot) |
| 1231 | { |
| 1232 | check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); |
| 1233 | pdf_dict_del(ctx, annot->obj, PDF_NAME(QuadPoints)); |
| 1234 | pdf_dirty_annot(ctx, annot); |
| 1235 | } |
| 1236 | |
| 1237 | void |
| 1238 | pdf_add_annot_quad_point(fz_context *ctx, pdf_annot *annot, fz_quad quad) |
| 1239 | { |
| 1240 | pdf_document *doc = annot->page->doc; |
| 1241 | fz_matrix page_ctm, inv_page_ctm; |
| 1242 | pdf_obj *quad_points; |
| 1243 | |
| 1244 | check_allowed_subtypes(ctx, annot, PDF_NAME(QuadPoints), quad_point_subtypes); |
| 1245 | |
| 1246 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1247 | inv_page_ctm = fz_invert_matrix(page_ctm); |
| 1248 | |
| 1249 | quad_points = pdf_dict_get(ctx, annot->obj, PDF_NAME(QuadPoints)); |
| 1250 | if (!pdf_is_array(ctx, quad_points)) |
| 1251 | { |
| 1252 | quad_points = pdf_new_array(ctx, doc, 8); |
| 1253 | pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(QuadPoints), quad_points); |
| 1254 | } |
| 1255 | |
| 1256 | /* Contrary to the specification, the points within a QuadPoint are NOT ordered |
| 1257 | * in a counterclockwise fashion. Experiments with Adobe's implementation |
| 1258 | * indicates a cross-wise ordering is intended: ul, ur, ll, lr. |
| 1259 | */ |
| 1260 | quad = fz_transform_quad(quad, inv_page_ctm); |
| 1261 | pdf_array_push_real(ctx, quad_points, quad.ul.x); |
| 1262 | pdf_array_push_real(ctx, quad_points, quad.ul.y); |
| 1263 | pdf_array_push_real(ctx, quad_points, quad.ur.x); |
| 1264 | pdf_array_push_real(ctx, quad_points, quad.ur.y); |
| 1265 | pdf_array_push_real(ctx, quad_points, quad.ll.x); |
| 1266 | pdf_array_push_real(ctx, quad_points, quad.ll.y); |
| 1267 | pdf_array_push_real(ctx, quad_points, quad.lr.x); |
| 1268 | pdf_array_push_real(ctx, quad_points, quad.lr.y); |
| 1269 | |
| 1270 | pdf_dirty_annot(ctx, annot); |
| 1271 | } |
| 1272 | |
| 1273 | static pdf_obj *ink_list_subtypes[] = { |
| 1274 | PDF_NAME(Ink), |
| 1275 | NULL, |
| 1276 | }; |
| 1277 | |
| 1278 | int |
| 1279 | pdf_annot_has_ink_list(fz_context *ctx, pdf_annot *annot) |
| 1280 | { |
| 1281 | return is_allowed_subtype(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); |
| 1282 | } |
| 1283 | |
| 1284 | int |
| 1285 | pdf_annot_ink_list_count(fz_context *ctx, pdf_annot *annot) |
| 1286 | { |
| 1287 | pdf_obj *ink_list; |
| 1288 | check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); |
| 1289 | ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); |
| 1290 | return pdf_array_len(ctx, ink_list); |
| 1291 | } |
| 1292 | |
| 1293 | int |
| 1294 | pdf_annot_ink_list_stroke_count(fz_context *ctx, pdf_annot *annot, int i) |
| 1295 | { |
| 1296 | pdf_obj *ink_list; |
| 1297 | pdf_obj *stroke; |
| 1298 | check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); |
| 1299 | ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); |
| 1300 | stroke = pdf_array_get(ctx, ink_list, i); |
| 1301 | return pdf_array_len(ctx, stroke) / 2; |
| 1302 | } |
| 1303 | |
| 1304 | fz_point |
| 1305 | pdf_annot_ink_list_stroke_vertex(fz_context *ctx, pdf_annot *annot, int i, int k) |
| 1306 | { |
| 1307 | pdf_obj *ink_list; |
| 1308 | pdf_obj *stroke; |
| 1309 | fz_matrix page_ctm; |
| 1310 | fz_point point; |
| 1311 | |
| 1312 | check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); |
| 1313 | |
| 1314 | ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); |
| 1315 | stroke = pdf_array_get(ctx, ink_list, i); |
| 1316 | |
| 1317 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1318 | |
| 1319 | point.x = pdf_array_get_real(ctx, stroke, k * 2 + 0); |
| 1320 | point.y = pdf_array_get_real(ctx, stroke, k * 2 + 1); |
| 1321 | return fz_transform_point(point, page_ctm); |
| 1322 | } |
| 1323 | |
| 1324 | void |
| 1325 | pdf_set_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, const int *count, const fz_point *v) |
| 1326 | { |
| 1327 | pdf_document *doc = annot->page->doc; |
| 1328 | fz_matrix page_ctm, inv_page_ctm; |
| 1329 | pdf_obj *ink_list, *stroke; |
| 1330 | fz_point point; |
| 1331 | int i, k; |
| 1332 | |
| 1333 | check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); |
| 1334 | |
| 1335 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1336 | inv_page_ctm = fz_invert_matrix(page_ctm); |
| 1337 | |
| 1338 | // TODO: update Rect (in update appearance perhaps?) |
| 1339 | |
| 1340 | ink_list = pdf_new_array(ctx, doc, n); |
| 1341 | for (i = 0; i < n; ++i) |
| 1342 | { |
| 1343 | stroke = pdf_new_array(ctx, doc, count[i] * 2); |
| 1344 | for (k = 0; k < count[i]; ++k) |
| 1345 | { |
| 1346 | point = fz_transform_point(*v++, inv_page_ctm); |
| 1347 | pdf_array_push_real(ctx, stroke, point.x); |
| 1348 | pdf_array_push_real(ctx, stroke, point.y); |
| 1349 | } |
| 1350 | pdf_array_push_drop(ctx, ink_list, stroke); |
| 1351 | } |
| 1352 | pdf_dict_put_drop(ctx, annot->obj, PDF_NAME(InkList), ink_list); |
| 1353 | pdf_dirty_annot(ctx, annot); |
| 1354 | } |
| 1355 | |
| 1356 | void |
| 1357 | pdf_clear_annot_ink_list(fz_context *ctx, pdf_annot *annot) |
| 1358 | { |
| 1359 | check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); |
| 1360 | pdf_dict_del(ctx, annot->obj, PDF_NAME(InkList)); |
| 1361 | pdf_dirty_annot(ctx, annot); |
| 1362 | } |
| 1363 | |
| 1364 | void |
| 1365 | pdf_add_annot_ink_list(fz_context *ctx, pdf_annot *annot, int n, fz_point p[]) |
| 1366 | { |
| 1367 | fz_matrix page_ctm, inv_page_ctm; |
| 1368 | pdf_obj *ink_list, *stroke; |
| 1369 | int i; |
| 1370 | |
| 1371 | check_allowed_subtypes(ctx, annot, PDF_NAME(InkList), ink_list_subtypes); |
| 1372 | |
| 1373 | pdf_page_transform(ctx, annot->page, NULL, &page_ctm); |
| 1374 | inv_page_ctm = fz_invert_matrix(page_ctm); |
| 1375 | |
| 1376 | ink_list = pdf_dict_get(ctx, annot->obj, PDF_NAME(InkList)); |
| 1377 | if (!pdf_is_array(ctx, ink_list)) |
| 1378 | ink_list = pdf_dict_put_array(ctx, annot->obj, PDF_NAME(InkList), 10); |
| 1379 | |
| 1380 | stroke = pdf_array_push_array(ctx, ink_list, n * 2); |
| 1381 | for (i = 0; i < n; ++i) |
| 1382 | { |
| 1383 | fz_point tp = fz_transform_point(p[i], inv_page_ctm); |
| 1384 | pdf_array_push_real(ctx, stroke, tp.x); |
| 1385 | pdf_array_push_real(ctx, stroke, tp.y); |
| 1386 | } |
| 1387 | |
| 1388 | pdf_dirty_annot(ctx, annot); |
| 1389 | } |
| 1390 | |
| 1391 | static void |
| 1392 | pdf_format_date(fz_context *ctx, char *s, int n, time_t secs) |
| 1393 | { |
| 1394 | #ifdef _POSIX_SOURCE |
| 1395 | struct tm tmbuf, *tm = gmtime_r(&secs, &tmbuf); |
| 1396 | #else |
| 1397 | struct tm *tm = gmtime(&secs); |
| 1398 | #endif |
| 1399 | if (!tm) |
| 1400 | fz_strlcpy(s, "D:19700101000000Z" , n); |
| 1401 | else |
| 1402 | strftime(s, n, "D:%Y%m%d%H%M%SZ" , tm); |
| 1403 | } |
| 1404 | |
| 1405 | static int64_t |
| 1406 | pdf_parse_date(fz_context *ctx, const char *s) |
| 1407 | { |
| 1408 | int tz_sign, tz_hour, tz_min, tz_adj; |
| 1409 | struct tm tm; |
| 1410 | time_t utc; |
| 1411 | |
| 1412 | if (!s) |
| 1413 | return 0; |
| 1414 | |
| 1415 | memset(&tm, 0, sizeof tm); |
| 1416 | tm.tm_mday = 1; |
| 1417 | |
| 1418 | tz_sign = 1; |
| 1419 | tz_hour = 0; |
| 1420 | tz_min = 0; |
| 1421 | |
| 1422 | if (s[0] == 'D' && s[1] == ':') |
| 1423 | s += 2; |
| 1424 | |
| 1425 | if (!isdigit(s[0]) || !isdigit(s[1]) || !isdigit(s[2]) || !isdigit(s[3])) |
| 1426 | { |
| 1427 | fz_warn(ctx, "invalid date format (missing year)" ); |
| 1428 | return 0; |
| 1429 | } |
| 1430 | tm.tm_year = (s[0]-'0')*1000 + (s[1]-'0')*100 + (s[2]-'0')*10 + (s[3]-'0') - 1900; |
| 1431 | s += 4; |
| 1432 | |
| 1433 | if (isdigit(s[0]) && isdigit(s[1])) |
| 1434 | { |
| 1435 | tm.tm_mon = (s[0]-'0')*10 + (s[1]-'0') - 1; /* month is 0-11 in struct tm */ |
| 1436 | s += 2; |
| 1437 | if (isdigit(s[0]) && isdigit(s[1])) |
| 1438 | { |
| 1439 | tm.tm_mday = (s[0]-'0')*10 + (s[1]-'0'); |
| 1440 | s += 2; |
| 1441 | if (isdigit(s[0]) && isdigit(s[1])) |
| 1442 | { |
| 1443 | tm.tm_hour = (s[0]-'0')*10 + (s[1]-'0'); |
| 1444 | s += 2; |
| 1445 | if (isdigit(s[0]) && isdigit(s[1])) |
| 1446 | { |
| 1447 | tm.tm_min = (s[0]-'0')*10 + (s[1]-'0'); |
| 1448 | s += 2; |
| 1449 | if (isdigit(s[0]) && isdigit(s[1])) |
| 1450 | { |
| 1451 | tm.tm_sec = (s[0]-'0')*10 + (s[1]-'0'); |
| 1452 | s += 2; |
| 1453 | } |
| 1454 | } |
| 1455 | } |
| 1456 | } |
| 1457 | } |
| 1458 | |
| 1459 | if (s[0] == 'Z') |
| 1460 | { |
| 1461 | s += 1; |
| 1462 | } |
| 1463 | else if ((s[0] == '-' || s[0] == '+') && isdigit(s[1]) && isdigit(s[2])) |
| 1464 | { |
| 1465 | tz_sign = (s[0] == '-') ? -1 : 1; |
| 1466 | tz_hour = (s[1]-'0')*10 + (s[2]-'0'); |
| 1467 | s += 3; |
| 1468 | if (s[0] == '\'' && isdigit(s[1]) && isdigit(s[2])) |
| 1469 | { |
| 1470 | tz_min = (s[1]-'0')*10 + (s[2]-'0'); |
| 1471 | s += 3; |
| 1472 | if (s[0] == '\'') |
| 1473 | s += 1; |
| 1474 | } |
| 1475 | } |
| 1476 | |
| 1477 | if (s[0] != 0) |
| 1478 | fz_warn(ctx, "invalid date format (garbage at end)" ); |
| 1479 | |
| 1480 | utc = timegm(&tm); |
| 1481 | if (utc == (time_t)-1) |
| 1482 | { |
| 1483 | fz_warn(ctx, "date overflow error" ); |
| 1484 | return 0; |
| 1485 | } |
| 1486 | |
| 1487 | tz_adj = tz_sign * (tz_hour * 3600 + tz_min * 60); |
| 1488 | return utc - tz_adj; |
| 1489 | } |
| 1490 | |
| 1491 | static pdf_obj *markup_subtypes[] = { |
| 1492 | PDF_NAME(Text), |
| 1493 | PDF_NAME(FreeText), |
| 1494 | PDF_NAME(Line), |
| 1495 | PDF_NAME(Square), |
| 1496 | PDF_NAME(Circle), |
| 1497 | PDF_NAME(Polygon), |
| 1498 | PDF_NAME(PolyLine), |
| 1499 | PDF_NAME(Highlight), |
| 1500 | PDF_NAME(Underline), |
| 1501 | PDF_NAME(Squiggly), |
| 1502 | PDF_NAME(StrikeOut), |
| 1503 | PDF_NAME(Redact), |
| 1504 | PDF_NAME(Stamp), |
| 1505 | PDF_NAME(Caret), |
| 1506 | PDF_NAME(Ink), |
| 1507 | PDF_NAME(FileAttachment), |
| 1508 | PDF_NAME(Sound), |
| 1509 | NULL, |
| 1510 | }; |
| 1511 | |
| 1512 | /* |
| 1513 | Get annotation's modification date in seconds since the epoch. |
| 1514 | */ |
| 1515 | int64_t |
| 1516 | pdf_annot_modification_date(fz_context *ctx, pdf_annot *annot) |
| 1517 | { |
| 1518 | pdf_obj *date = pdf_dict_get(ctx, annot->obj, PDF_NAME(M)); |
| 1519 | return date ? pdf_parse_date(ctx, pdf_to_str_buf(ctx, date)) : 0; |
| 1520 | } |
| 1521 | |
| 1522 | /* |
| 1523 | Set annotation's modification date in seconds since the epoch. |
| 1524 | */ |
| 1525 | void |
| 1526 | pdf_set_annot_modification_date(fz_context *ctx, pdf_annot *annot, int64_t secs) |
| 1527 | { |
| 1528 | char s[40]; |
| 1529 | |
| 1530 | check_allowed_subtypes(ctx, annot, PDF_NAME(M), markup_subtypes); |
| 1531 | |
| 1532 | pdf_format_date(ctx, s, sizeof s, secs); |
| 1533 | pdf_dict_put_string(ctx, annot->obj, PDF_NAME(M), s, strlen(s)); |
| 1534 | pdf_dirty_annot(ctx, annot); |
| 1535 | } |
| 1536 | |
| 1537 | int |
| 1538 | pdf_annot_has_author(fz_context *ctx, pdf_annot *annot) |
| 1539 | { |
| 1540 | return is_allowed_subtype(ctx, annot, PDF_NAME(T), markup_subtypes); |
| 1541 | } |
| 1542 | |
| 1543 | const char * |
| 1544 | pdf_annot_author(fz_context *ctx, pdf_annot *annot) |
| 1545 | { |
| 1546 | check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes); |
| 1547 | return pdf_dict_get_text_string(ctx, annot->obj, PDF_NAME(T)); |
| 1548 | } |
| 1549 | |
| 1550 | void |
| 1551 | pdf_set_annot_author(fz_context *ctx, pdf_annot *annot, const char *author) |
| 1552 | { |
| 1553 | check_allowed_subtypes(ctx, annot, PDF_NAME(T), markup_subtypes); |
| 1554 | pdf_dict_put_text_string(ctx, annot->obj, PDF_NAME(T), author); |
| 1555 | pdf_dirty_annot(ctx, annot); |
| 1556 | } |
| 1557 | |
| 1558 | void |
| 1559 | pdf_parse_default_appearance(fz_context *ctx, const char *da, const char **font, float *size, float color[3]) |
| 1560 | { |
| 1561 | char buf[100], *p = buf, *tok, *end; |
| 1562 | float stack[3] = { 0, 0, 0 }; |
| 1563 | int top = 0; |
| 1564 | |
| 1565 | *font = "Helv" ; |
| 1566 | *size = 12; |
| 1567 | color[0] = color[1] = color[2] = 0; |
| 1568 | |
| 1569 | fz_strlcpy(buf, da, sizeof buf); |
| 1570 | while ((tok = fz_strsep(&p, " \n\r\t" )) != NULL) |
| 1571 | { |
| 1572 | if (tok[0] == 0) |
| 1573 | ; |
| 1574 | else if (tok[0] == '/') |
| 1575 | { |
| 1576 | if (!strcmp(tok+1, "Cour" )) *font = "Cour" ; |
| 1577 | if (!strcmp(tok+1, "Helv" )) *font = "Helv" ; |
| 1578 | if (!strcmp(tok+1, "TiRo" )) *font = "TiRo" ; |
| 1579 | if (!strcmp(tok+1, "Symb" )) *font = "Symb" ; |
| 1580 | if (!strcmp(tok+1, "ZaDb" )) *font = "ZaDb" ; |
| 1581 | } |
| 1582 | else if (!strcmp(tok, "Tf" )) |
| 1583 | { |
| 1584 | *size = stack[0]; |
| 1585 | top = 0; |
| 1586 | } |
| 1587 | else if (!strcmp(tok, "g" )) |
| 1588 | { |
| 1589 | color[0] = color[1] = color[2] = stack[0]; |
| 1590 | top = 0; |
| 1591 | } |
| 1592 | else if (!strcmp(tok, "rg" )) |
| 1593 | { |
| 1594 | color[0] = stack[0]; |
| 1595 | color[1] = stack[1]; |
| 1596 | color[2] = stack[2]; |
| 1597 | top=0; |
| 1598 | } |
| 1599 | else |
| 1600 | { |
| 1601 | if (top < 3) |
| 1602 | stack[top] = fz_strtof(tok, &end); |
| 1603 | if (*end == 0) |
| 1604 | ++top; |
| 1605 | else |
| 1606 | top = 0; |
| 1607 | } |
| 1608 | } |
| 1609 | } |
| 1610 | |
| 1611 | void |
| 1612 | pdf_print_default_appearance(fz_context *ctx, char *buf, int nbuf, const char *font, float size, const float color[3]) |
| 1613 | { |
| 1614 | if (color[0] > 0 || color[1] > 0 || color[2] > 0) |
| 1615 | fz_snprintf(buf, nbuf, "/%s %g Tf %g %g %g rg" , font, size, color[0], color[1], color[2]); |
| 1616 | else |
| 1617 | fz_snprintf(buf, nbuf, "/%s %g Tf" , font, size); |
| 1618 | } |
| 1619 | |
| 1620 | void |
| 1621 | pdf_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char **font, float *size, float color[3]) |
| 1622 | { |
| 1623 | pdf_obj *da = pdf_dict_get_inheritable(ctx, annot->obj, PDF_NAME(DA)); |
| 1624 | if (!da) |
| 1625 | { |
| 1626 | pdf_obj *trailer = pdf_trailer(ctx, annot->page->doc); |
| 1627 | da = pdf_dict_getl(ctx, trailer, PDF_NAME(Root), PDF_NAME(AcroForm), PDF_NAME(DA), NULL); |
| 1628 | } |
| 1629 | pdf_parse_default_appearance(ctx, pdf_to_str_buf(ctx, da), font, size, color); |
| 1630 | } |
| 1631 | |
| 1632 | void |
| 1633 | pdf_set_annot_default_appearance(fz_context *ctx, pdf_annot *annot, const char *font, float size, const float color[3]) |
| 1634 | { |
| 1635 | char buf[100]; |
| 1636 | |
| 1637 | pdf_print_default_appearance(ctx, buf, sizeof buf, font, size, color); |
| 1638 | |
| 1639 | pdf_dict_put_string(ctx, annot->obj, PDF_NAME(DA), buf, strlen(buf)); |
| 1640 | |
| 1641 | pdf_dict_del(ctx, annot->obj, PDF_NAME(DS)); /* not supported */ |
| 1642 | pdf_dict_del(ctx, annot->obj, PDF_NAME(RC)); /* not supported */ |
| 1643 | |
| 1644 | pdf_dirty_annot(ctx, annot); |
| 1645 | } |
| 1646 | |