epdfinfo.c (106982B)
1 /* Copyright (C) 2013, 2014 Andreas Politz 2 * 3 * Author: Andreas Politz <politza@fh-trier.de> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include <config.h> 19 20 #include <assert.h> 21 #ifdef HAVE_ERR_H 22 # include <err.h> 23 #endif 24 #ifdef HAVE_ERROR_H 25 # include <error.h> 26 #endif 27 #include <glib.h> 28 #include <poppler.h> 29 #include <cairo.h> 30 #include <stdarg.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <errno.h> 39 #include <png.h> 40 #include <math.h> 41 #include <regex.h> 42 #include "synctex_parser.h" 43 #include "epdfinfo.h" 44 45 46 /* ================================================================== * 47 * Helper Functions 48 * ================================================================== */ 49 50 #ifndef HAVE_ERR_H 51 /** 52 * Print error message and quit. 53 * 54 * @param eval Return code 55 * @param fmt Formatting string 56 */ 57 static void 58 err(int eval, const char *fmt, ...) 59 { 60 va_list args; 61 62 fprintf (stderr, "epdfinfo: "); 63 if (fmt != NULL) 64 { 65 va_start (args, fmt); 66 vfprintf (stderr, fmt, args); 67 va_end (args); 68 fprintf (stderr, ": %s\n", strerror(errno)); 69 } 70 else 71 { 72 fprintf (stderr, "\n"); 73 } 74 75 fflush (stderr); 76 exit (eval); 77 } 78 #endif 79 80 #ifndef HAVE_GETLINE 81 /** 82 * Read one line from a file. 83 * 84 * @param lineptr Pointer to malloc() allocated buffer 85 * @param n Pointer to size of buffer 86 * @param stream File pointer to read from 87 */ 88 static ssize_t 89 getline(char **lineptr, size_t *n, FILE *stream) 90 { 91 size_t len = 0; 92 int ch; 93 94 if ((lineptr == NULL) || (n == NULL)) 95 { 96 errno = EINVAL; 97 return -1; 98 } 99 100 if (*lineptr == NULL) 101 { 102 *lineptr = malloc (128); 103 *n = 128; 104 } 105 106 while ((ch = fgetc (stream)) != EOF) 107 { 108 (*lineptr)[len] = ch; 109 110 if (++len >= *n) 111 { 112 *n += 128; 113 *lineptr = realloc (*lineptr, *n); 114 } 115 116 if (ch == '\n') 117 break; 118 } 119 (*lineptr)[len] = '\0'; 120 121 if (!len) 122 { 123 len = -1; 124 } 125 126 return len; 127 } 128 #endif 129 130 /** 131 * Free a list of command arguments. 132 * 133 * @param args An array of command arguments. 134 * @param n The length of the array. 135 */ 136 static void 137 free_command_args (command_arg_t *args, size_t n) 138 { 139 if (! args) 140 return; 141 142 g_free (args); 143 } 144 145 /** 146 * Free resources held by document. 147 * 148 * @param doc The document to be freed. 149 */ 150 static void 151 free_document (document_t *doc) 152 { 153 if (! doc) 154 return; 155 156 g_free (doc->filename); 157 g_free (doc->passwd); 158 if (doc->annotations.pages) 159 { 160 int npages = poppler_document_get_n_pages (doc->pdf); 161 int i; 162 for (i = 0; i < npages; ++i) 163 { 164 GList *item; 165 GList *annots = doc->annotations.pages[i]; 166 for (item = annots; item; item = item->next) 167 { 168 annotation_t *a = (annotation_t*) item->data; 169 poppler_annot_mapping_free(a->amap); 170 g_free (a->key); 171 g_free (a); 172 } 173 g_list_free (annots); 174 } 175 g_hash_table_destroy (doc->annotations.keys); 176 g_free (doc->annotations.pages); 177 } 178 g_object_unref (doc->pdf); 179 g_free (doc); 180 } 181 182 /** 183 * Parse a list of whitespace separated double values. 184 * 185 * @param str The input string. 186 * @param values[out] Values are put here. 187 * @param nvalues How many values to parse. 188 * 189 * @return TRUE, if str contained exactly nvalues, else FALSE. 190 */ 191 static gboolean 192 parse_double_list (const char *str, gdouble *values, size_t nvalues) 193 { 194 char *end; 195 int i; 196 197 if (! str) 198 return FALSE; 199 200 errno = 0; 201 for (i = 0; i < nvalues; ++i) 202 { 203 gdouble n = g_ascii_strtod (str, &end); 204 205 if (str == end || errno) 206 return FALSE; 207 208 values[i] = n; 209 str = end; 210 } 211 212 if (*end) 213 return FALSE; 214 215 return TRUE; 216 } 217 218 static gboolean 219 parse_rectangle (const char *str, PopplerRectangle *r) 220 { 221 gdouble values[4]; 222 223 if (! r) 224 return FALSE; 225 226 if (! parse_double_list (str, values, 4)) 227 return FALSE; 228 229 r->x1 = values[0]; 230 r->y1 = values[1]; 231 r->x2 = values[2]; 232 r->y2 = values[3]; 233 234 return TRUE; 235 } 236 237 static gboolean 238 parse_edges_or_position (const char *str, PopplerRectangle *r) 239 { 240 return (parse_rectangle (str, r) 241 && r->x1 >= 0 && r->x1 <= 1 242 && r->x2 <= 1 243 && r->y1 >= 0 && r->y1 <= 1 244 && r->y2 <= 1); 245 } 246 247 static gboolean 248 parse_edges (const char *str, PopplerRectangle *r) 249 { 250 return (parse_rectangle (str, r) 251 && r->x1 >= 0 && r->x1 <= 1 252 && r->x2 >= 0 && r->x2 <= 1 253 && r->y1 >= 0 && r->y1 <= 1 254 && r->y2 >= 0 && r->y2 <= 1); 255 } 256 257 /** 258 * Print a string properly escaped for a response. 259 * 260 * @param str The string to be printed. 261 * @param suffix_char Append a newline if NEWLINE, a colon if COLON. 262 */ 263 static void 264 print_response_string (const char *str, enum suffix_char suffix) 265 { 266 if (str) 267 { 268 while (*str) 269 { 270 switch (*str) 271 { 272 case '\n': 273 printf ("\\n"); 274 break; 275 case '\\': 276 printf ("\\\\"); 277 break; 278 case ':': 279 printf ("\\:"); 280 break; 281 default: 282 putchar (*str); 283 } 284 ++str; 285 } 286 } 287 288 switch (suffix) 289 { 290 case NEWLINE: 291 putchar ('\n'); 292 break; 293 case COLON: 294 putchar (':'); 295 break; 296 default: ; 297 } 298 } 299 300 301 /** 302 * Print a formatted error response. 303 * 304 * @param fmt The printf-like format string. 305 */ 306 static void 307 printf_error_response (const char *fmt, ...) 308 { 309 va_list va; 310 puts ("ERR"); 311 va_start (va, fmt); 312 vprintf (fmt, va); 313 va_end (va); 314 puts ("\n."); 315 fflush (stdout); 316 } 317 318 /** 319 * Remove one trailing newline character. Does nothing, if str does 320 * not end with a newline. 321 * 322 * @param str The string. 323 * 324 * @return str with trailing newline removed. 325 */ 326 static char* 327 strchomp (char *str) 328 { 329 size_t length; 330 331 if (! str) 332 return str; 333 334 length = strlen (str); 335 if (str[length - 1] == '\n') 336 str[length - 1] = '\0'; 337 338 return str; 339 } 340 341 /** 342 * Create a new, temporary file and returns its name. 343 * 344 * @return The filename. 345 */ 346 static char* 347 mktempfile() 348 { 349 char *filename = NULL; 350 int tries = 3; 351 while (! filename && tries-- > 0) 352 { 353 354 filename = tempnam(NULL, "epdfinfo"); 355 if (filename) 356 { 357 int fd = open(filename, O_CREAT | O_EXCL | O_RDONLY, S_IRUSR | S_IWUSR); 358 if (fd > 0) 359 close (fd); 360 else 361 { 362 free (filename); 363 filename = NULL; 364 } 365 } 366 } 367 if (! filename) 368 fprintf (stderr, "Unable to create tempfile"); 369 370 return filename; 371 } 372 373 /* Holds RGB, HSL, HSV, Lab, or Lch... but note that the order in memory for HSL 374 * and HSV are actually VSH and LSH. */ 375 struct color 376 { 377 union 378 { 379 double r, v, l; 380 }; 381 union 382 { 383 double g, s, a; 384 }; 385 union 386 { 387 double b, h; 388 }; 389 }; 390 391 #define struct_color(x) (*((struct color *) x)) 392 #define vec_color(x) ((double *) &x) 393 394 // Using values reported at https://bottosson.github.io/posts/oklab/#converting-from-linear-srgb-to-oklab 395 // instead of going through xyz. This ensures any whitepoint is ignored 396 static struct color 397 rgb2oklab(struct color rgb) 398 { 399 struct color srgb; 400 401 for (int i = 0; i < 3; i++) 402 { 403 double val = vec_color(rgb)[i]; 404 vec_color(srgb)[i] = ((val > 0.04045) 405 ? pow((val + 0.055) / 1.055, 2.4) 406 : (val / 12.92)); 407 } 408 409 double l = 0.4121656120 * srgb.r + 0.5362752080 * srgb.g + 0.0514575653 * srgb.b; 410 double m = 0.2118591070 * srgb.r + 0.6807189584 * srgb.g + 0.1074065790 * srgb.b; 411 double s = 0.0883097947 * srgb.r + 0.2818474174 * srgb.g + 0.6302613616 * srgb.b; 412 413 l = cbrt(l); 414 m = cbrt(m); 415 s = cbrt(s); 416 417 return (struct color) { 418 .l = 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s, 419 .a = 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s, 420 .b = 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s 421 }; 422 } 423 424 static double clamp(double x, double low, double high) 425 { 426 return ((x < low) 427 ? low 428 : ((x > high) ? high : x)); 429 } 430 431 static struct color 432 oklab2rgb(struct color lab) 433 { 434 double l = lab.l + 0.3963377774 * lab.a + 0.2158037573 * lab.b; 435 double m = lab.l - 0.1055613458 * lab.a - 0.0638541728 * lab.b; 436 double s = lab.l - 0.0894841775 * lab.a - 1.2914855480 * lab.b; 437 438 l = l * l * l; 439 m = m * m * m; 440 s = s * s * s; 441 442 struct color srgb = { 443 .r = 4.0767245293 * l - 3.3072168827 * m + 0.2307590544 * s, 444 .g = -1.2681437731 * l + 2.6093323231 * m - 0.3411344290 * s, 445 .b = -0.0041119885 * l - 0.7034763098 * m + 1.7068625689 * s 446 }; 447 448 struct color rgb; 449 for (int i = 0; i < 3; i++) 450 { 451 double val = vec_color(srgb)[i]; 452 val = ((val > 0.0031308) 453 ? (1.055 * pow(val, 1 / 2.4) - 0.055) 454 : (12.92 * val)); 455 456 vec_color(rgb)[i] = clamp(val, 0.0, 1.0); 457 } 458 459 return rgb; 460 } 461 462 #undef struct_color 463 #undef vec_color 464 465 static inline gboolean color_equal(struct color a, struct color b) 466 { 467 return (a.r == b.r && a.g == b.g && a.b == b.b); 468 } 469 470 static void 471 image_recolor (cairo_surface_t * surface, const PopplerColor * fg, 472 const PopplerColor * bg, int usecolors) 473 { 474 /* Performs one of two kinds of image recoloring depending on the value of usecolors: 475 476 1 -> Bg-Fg Interpolation: maps source document colors to colors 477 interpolated between the background and foreground values in 478 pdf-view-midnight-colors via the lightness of the source color. This 479 discards hue information but allows you to fit your color theme 480 perfectly. 481 482 2 -> Hue-Preserving interpolation: same as above, similar to above, but 483 attempts to preserve hue while still respecting the background and 484 foreground colors. This is done by matching source document white and 485 black to the specified background and foreground as above, but mixes 486 hue/saturation with the background color. This preserves hue but is 487 more expensive. 488 */ 489 490 const unsigned int page_width = cairo_image_surface_get_width (surface); 491 const unsigned int page_height = cairo_image_surface_get_height (surface); 492 const int rowstride = cairo_image_surface_get_stride (surface); 493 unsigned char *image = cairo_image_surface_get_data (surface); 494 495 /* RGB weights for computing lightness. Must sum to one */ 496 static const double a[] = { 0.30, 0.59, 0.11 }; 497 498 const double f = 65535.; 499 const struct color rgb_fg = { 500 .r = fg->red / f, 501 .g = fg->green / f, 502 .b = fg->blue / f 503 }; 504 const struct color rgb_bg = { 505 .r = bg->red / f, 506 .g = bg->green / f, 507 .b = bg->blue / f 508 }; 509 510 const struct color rgb_diff = { 511 .r = rgb_bg.r - rgb_fg.r, 512 .g = rgb_bg.g - rgb_fg.g, 513 .b = rgb_bg.b - rgb_fg.b 514 }; 515 516 switch (usecolors) 517 { 518 case 0: 519 break; 520 case 1: 521 { 522 unsigned int y; 523 for (y = 0; y < page_height * rowstride; y += rowstride) 524 { 525 unsigned char *data = image + y; 526 527 unsigned int x; 528 for (x = 0; x < page_width; x++, data += 4) 529 { 530 /* Careful. data color components blue, green, red. */ 531 struct color rgb = { 532 .r = (double) data[2] / 256., 533 .g = (double) data[1] / 256., 534 .b = (double) data[0] / 256. 535 }; 536 537 /* Linear interpolation between bg and fg based on the 538 perceptual lightness measure l */ 539 /* compute h, s, l data */ 540 double l = a[0] * rgb.r + a[1] * rgb.g + a[2] * rgb.b; 541 542 /* linear interpolation between dark and light with color 543 lightness as a parameter */ 544 data[2] = 545 (unsigned char) round (255. * (l * rgb_diff.r + rgb_fg.r)); 546 data[1] = 547 (unsigned char) round (255. * (l * rgb_diff.g + rgb_fg.g)); 548 data[0] = 549 (unsigned char) round (255. * (l * rgb_diff.b + rgb_fg.b)); 550 } 551 } 552 } 553 break; 554 case 2: 555 { 556 /* If using the Oklab transform, it is relatively expensive. Precompute 557 white->background and black->foreground and have a single entry cache to 558 speed up computation */ 559 const struct color white = {.r = 1.0, .g = 1.0, .b = 1.0}; 560 struct color precomputed_rgb = white; 561 struct color precomputed_inv_rgb = rgb_bg; 562 563 /* Must match the transformation of colors below. */ 564 struct color oklab_fg = rgb2oklab(rgb_fg); 565 struct color oklab_bg = rgb2oklab(rgb_bg); 566 567 const double oklab_diff_l = oklab_fg.l - oklab_bg.l; 568 569 unsigned int y; 570 for (y = 0; y < page_height * rowstride; y += rowstride) 571 { 572 unsigned char *data = image + y; 573 574 unsigned int x; 575 for (x = 0; x < page_width; x++, data += 4) 576 { 577 /* Careful. data color components blue, green, red. */ 578 struct color rgb = { 579 .r = (double) data[2] / 256., 580 .g = (double) data[1] / 256., 581 .b = (double) data[0] / 256. 582 }; 583 584 /* Convert to Oklab coordinates, invert perceived lightness, 585 convert back to RGB. */ 586 if (color_equal(white, rgb)) 587 { 588 rgb = rgb_bg; 589 } 590 else if (color_equal(precomputed_rgb, rgb)) 591 { 592 rgb = precomputed_inv_rgb; 593 } 594 else 595 { 596 struct color oklab = rgb2oklab(rgb); 597 precomputed_rgb = rgb; 598 599 /* Invert the perceived lightness, and scales it */ 600 double l = oklab.l; 601 double inv_l = 1.0 - l; 602 oklab.l = oklab_bg.l + oklab_diff_l * inv_l; 603 604 /* Have a and b parameters (which encode hue and saturation) 605 start at the background value and interpolate up to 606 foreground */ 607 oklab.a = (oklab.a + oklab_bg.a * l + oklab_fg.a * inv_l); 608 oklab.b = (oklab.b + oklab_bg.b * l + oklab_fg.b * inv_l); 609 610 rgb = oklab2rgb(oklab); 611 612 precomputed_inv_rgb = rgb; 613 } 614 615 data[2] = (unsigned char) round(255. * rgb.r); 616 data[1] = (unsigned char) round(255. * rgb.g); 617 data[0] = (unsigned char) round(255. * rgb.b); 618 } 619 } 620 } 621 break; 622 default: 623 internal_error ("image_recolor switch fell through"); 624 } 625 } 626 627 /** 628 * Render a PDF page. 629 * 630 * @param pdf The PDF document. 631 * @param page The page to be rendered. 632 * @param width The desired width of the image. 633 * 634 * @return A cairo_t context encapsulating the rendered image, or 635 * NULL, if rendering failed for some reason. 636 */ 637 static cairo_surface_t* 638 image_render_page(PopplerDocument *pdf, PopplerPage *page, 639 int width, gboolean do_render_annotaions, 640 const render_options_t *options) 641 { 642 cairo_t *cr = NULL; 643 cairo_surface_t *surface = NULL; 644 double pt_width, pt_height; 645 int height; 646 double scale = 1; 647 648 if (! page || ! pdf) 649 return NULL; 650 651 if (width < 1) 652 width = 1; 653 654 poppler_page_get_size (page, &pt_width, &pt_height); 655 scale = width / pt_width; 656 height = (int) ((scale * pt_height) + 0.5); 657 658 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 659 width, height); 660 661 if (cairo_surface_status (surface) != CAIRO_STATUS_SUCCESS) 662 { 663 fprintf (stderr, "Failed to create cairo surface\n"); 664 goto error; 665 } 666 667 cr = cairo_create (surface); 668 if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) 669 { 670 fprintf (stderr, "Failed to create cairo handle\n"); 671 goto error; 672 } 673 674 cairo_translate (cr, 0, 0); 675 cairo_scale (cr, scale, scale); 676 /* Render w/o annotations. */ 677 if (! do_render_annotaions || (options && options->printed)) 678 poppler_page_render_for_printing_with_options 679 (page, cr, POPPLER_PRINT_DOCUMENT); 680 else 681 poppler_page_render (page, cr) ; 682 if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) 683 { 684 fprintf (stderr, "Failed to render page\n"); 685 goto error; 686 } 687 688 /* This makes the colors look right. */ 689 cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER); 690 cairo_set_source_rgb (cr, 1., 1., 1.); 691 692 cairo_paint (cr); 693 694 if (options && (options->usecolors)) 695 image_recolor (surface, &options->fg, &options->bg, options->usecolors); 696 697 cairo_destroy (cr); 698 699 return surface; 700 701 error: 702 if (surface != NULL) 703 cairo_surface_destroy (surface); 704 if (cr != NULL) 705 cairo_destroy (cr); 706 return NULL; 707 } 708 709 /** 710 * Write an image to a filename. 711 * 712 * @param cr The cairo context encapsulating the image. 713 * @param filename The filename to be written to. 714 * @param type The desired image type. 715 * 716 * @return 1 if the image was written successfully, else 0. 717 */ 718 static gboolean 719 image_write (cairo_surface_t *surface, const char *filename, enum image_type type) 720 { 721 722 int i, j; 723 unsigned char *data; 724 int width, height; 725 FILE *file = NULL; 726 gboolean success = 0; 727 728 if (! surface || 729 cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) 730 { 731 fprintf (stderr, "Invalid cairo surface\n"); 732 return 0; 733 } 734 735 if (! (file = fopen (filename, "wb"))) 736 { 737 fprintf (stderr, "Can not open file: %s\n", filename); 738 return 0; 739 } 740 741 cairo_surface_flush (surface); 742 width = cairo_image_surface_get_width (surface); 743 height = cairo_image_surface_get_height (surface); 744 data = cairo_image_surface_get_data (surface); 745 746 switch (type) 747 { 748 case PPM: 749 { 750 unsigned char *buffer = g_malloc (width * height * 3); 751 unsigned char *buffer_p = buffer; 752 753 fprintf (file, "P6\n%d %d\n255\n", width, height); 754 for (i = 0; i < width * height; ++i, data += 4, buffer_p += 3) 755 ARGB_TO_RGB (buffer_p, data); 756 fwrite (buffer, 1, width * height * 3, file); 757 g_free (buffer); 758 success = 1; 759 } 760 break; 761 case PNG: 762 { 763 png_infop info_ptr = NULL; 764 png_structp png_ptr = NULL; 765 unsigned char *row = NULL; 766 767 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 768 if (!png_ptr) 769 goto finalize; 770 771 info_ptr = png_create_info_struct(png_ptr); 772 if (!info_ptr) 773 goto finalize; 774 775 if (setjmp(png_jmpbuf(png_ptr))) 776 goto finalize; 777 778 png_init_io (png_ptr, file); 779 png_set_compression_level (png_ptr, 1); 780 png_set_IHDR (png_ptr, info_ptr, width, height, 781 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, 782 PNG_COMPRESSION_TYPE_BASE, 783 PNG_FILTER_TYPE_DEFAULT); 784 785 png_set_filter (png_ptr, PNG_FILTER_TYPE_BASE, 786 PNG_FILTER_NONE); 787 png_write_info (png_ptr, info_ptr); 788 row = g_malloc (3 * width); 789 for (i = 0; i < height; ++i) 790 { 791 unsigned char *row_p = row; 792 for (j = 0; j < width; ++j, data += 4, row_p += 3) 793 { 794 ARGB_TO_RGB (row_p, data); 795 } 796 png_write_row (png_ptr, row); 797 } 798 png_write_end (png_ptr, NULL); 799 success = 1; 800 finalize: 801 if (png_ptr) 802 png_destroy_write_struct (&png_ptr, &info_ptr); 803 if (row) 804 g_free (row); 805 if (! success) 806 fprintf (stderr, "Error writing png data\n"); 807 } 808 break; 809 default: 810 internal_error ("switch fell through"); 811 } 812 813 fclose (file); 814 return success; 815 } 816 817 static void 818 image_write_print_response(cairo_surface_t *surface, enum image_type type) 819 { 820 char *filename = mktempfile (); 821 822 perror_if_not (filename, "Unable to create temporary file"); 823 if (image_write (surface, filename, type)) 824 { 825 OK_BEGIN (); 826 print_response_string (filename, NEWLINE); 827 OK_END (); 828 } 829 else 830 { 831 printf_error_response ("Unable to write image"); 832 } 833 free (filename); 834 error: 835 return; 836 } 837 838 static void 839 region_print (cairo_region_t *region, double width, double height) 840 { 841 int i; 842 843 for (i = 0; i < cairo_region_num_rectangles (region); ++i) 844 { 845 cairo_rectangle_int_t r; 846 847 cairo_region_get_rectangle (region, i, &r); 848 printf ("%f %f %f %f", 849 r.x / width, 850 r.y / height, 851 (r.x + r.width) / width, 852 (r.y + r.height) / height); 853 if (i < cairo_region_num_rectangles (region) - 1) 854 putchar (':'); 855 } 856 if (0 == cairo_region_num_rectangles (region)) 857 printf ("0.0 0.0 0.0 0.0"); 858 } 859 860 /** 861 * Return a string representation of a PopplerActionType. 862 * 863 * @param type The PopplerActionType. 864 * 865 * @return Its string representation. 866 */ 867 static const char * 868 xpoppler_action_type_string(PopplerActionType type) 869 { 870 switch (type) 871 { 872 case POPPLER_ACTION_UNKNOWN: return "unknown"; 873 case POPPLER_ACTION_NONE: return "none"; 874 case POPPLER_ACTION_GOTO_DEST: return "goto-dest"; 875 case POPPLER_ACTION_GOTO_REMOTE: return "goto-remote"; 876 case POPPLER_ACTION_LAUNCH: return "launch"; 877 case POPPLER_ACTION_URI: return "uri"; 878 case POPPLER_ACTION_NAMED: return "goto-dest"; /* actually "named" */ 879 case POPPLER_ACTION_MOVIE: return "movie"; 880 case POPPLER_ACTION_RENDITION: return "rendition"; 881 case POPPLER_ACTION_OCG_STATE: return "ocg-state"; 882 case POPPLER_ACTION_JAVASCRIPT: return "javascript"; 883 default: return "invalid"; 884 } 885 } 886 887 /** 888 * Return a string representation of a PopplerAnnotType. 889 * 890 * @param type The PopplerAnnotType. 891 * 892 * @return Its string representation. 893 */ 894 static const char * 895 xpoppler_annot_type_string (PopplerAnnotType type) 896 { 897 switch (type) 898 { 899 case POPPLER_ANNOT_UNKNOWN: return "unknown"; 900 case POPPLER_ANNOT_TEXT: return "text"; 901 case POPPLER_ANNOT_LINK: return "link"; 902 case POPPLER_ANNOT_FREE_TEXT: return "free-text"; 903 case POPPLER_ANNOT_LINE: return "line"; 904 case POPPLER_ANNOT_SQUARE: return "square"; 905 case POPPLER_ANNOT_CIRCLE: return "circle"; 906 case POPPLER_ANNOT_POLYGON: return "polygon"; 907 case POPPLER_ANNOT_POLY_LINE: return "poly-line"; 908 case POPPLER_ANNOT_HIGHLIGHT: return "highlight"; 909 case POPPLER_ANNOT_UNDERLINE: return "underline"; 910 case POPPLER_ANNOT_SQUIGGLY: return "squiggly"; 911 case POPPLER_ANNOT_STRIKE_OUT: return "strike-out"; 912 case POPPLER_ANNOT_STAMP: return "stamp"; 913 case POPPLER_ANNOT_CARET: return "caret"; 914 case POPPLER_ANNOT_INK: return "ink"; 915 case POPPLER_ANNOT_POPUP: return "popup"; 916 case POPPLER_ANNOT_FILE_ATTACHMENT: return "file"; 917 case POPPLER_ANNOT_SOUND: return "sound"; 918 case POPPLER_ANNOT_MOVIE: return "movie"; 919 case POPPLER_ANNOT_WIDGET: return "widget"; 920 case POPPLER_ANNOT_SCREEN: return "screen"; 921 case POPPLER_ANNOT_PRINTER_MARK: return "printer-mark"; 922 case POPPLER_ANNOT_TRAP_NET: return "trap-net"; 923 case POPPLER_ANNOT_WATERMARK: return "watermark"; 924 case POPPLER_ANNOT_3D: return "3d"; 925 default: return "invalid"; 926 } 927 } 928 929 /** 930 * Return a string representation of a PopplerAnnotTextState. 931 * 932 * @param type The PopplerAnnotTextState. 933 * 934 * @return Its string representation. 935 */ 936 static const char * 937 xpoppler_annot_text_state_string (PopplerAnnotTextState state) 938 { 939 switch (state) 940 { 941 case POPPLER_ANNOT_TEXT_STATE_MARKED: return "marked"; 942 case POPPLER_ANNOT_TEXT_STATE_UNMARKED: return "unmarked"; 943 case POPPLER_ANNOT_TEXT_STATE_ACCEPTED: return "accepted"; 944 case POPPLER_ANNOT_TEXT_STATE_REJECTED: return "rejected"; 945 case POPPLER_ANNOT_TEXT_STATE_CANCELLED: return "cancelled"; 946 case POPPLER_ANNOT_TEXT_STATE_COMPLETED: return "completed"; 947 case POPPLER_ANNOT_TEXT_STATE_NONE: return "none"; 948 case POPPLER_ANNOT_TEXT_STATE_UNKNOWN: 949 default: return "unknown"; 950 } 951 }; 952 953 /** 954 * Validate a PopplerSelectionStyle by replacing invalid styles 955 * with a default of POPPLER_SELECTION_GLYPH. 956 * 957 * @param selection_style The selection style. 958 * 959 * @return selection_style for valid styles, otherwise POPPLER_SELECTION_GLYPH. 960 */ 961 static PopplerSelectionStyle 962 xpoppler_validate_selection_style (int selection_style) 963 { 964 switch (selection_style) { 965 case POPPLER_SELECTION_GLYPH: 966 case POPPLER_SELECTION_WORD: 967 case POPPLER_SELECTION_LINE: 968 return selection_style; 969 } 970 return POPPLER_SELECTION_GLYPH; 971 } 972 973 static document_t* 974 document_open (const epdfinfo_t *ctx, const char *filename, 975 const char *passwd, GError **gerror) 976 { 977 char *uri; 978 document_t *doc = g_hash_table_lookup (ctx->documents, filename); 979 980 if (NULL != doc) 981 return doc; 982 983 doc = g_malloc0(sizeof (document_t)); 984 uri = g_filename_to_uri (filename, NULL, gerror); 985 if (uri != NULL) 986 doc->pdf = poppler_document_new_from_file(uri, passwd, gerror); 987 988 if (NULL == doc->pdf) 989 { 990 g_free (doc); 991 doc = NULL; 992 } 993 else 994 { 995 doc->filename = g_strdup (filename); 996 doc->passwd = g_strdup (passwd); 997 g_hash_table_insert (ctx->documents, doc->filename, doc); 998 } 999 g_free (uri); 1000 return doc; 1001 } 1002 1003 /** 1004 * Split command args into a list of strings. 1005 * 1006 * @param args The colon separated list of arguments. 1007 * @param nargs[out] The number of returned arguments. 1008 * 1009 * @return The list of arguments, which should be freed by the caller. 1010 */ 1011 static char ** 1012 command_arg_split (const char *args, int *nargs) 1013 { 1014 char **list = g_malloc (sizeof (char*) * 16); 1015 int i = 0; 1016 size_t allocated = 16; 1017 char *buffer = NULL; 1018 gboolean last = FALSE; 1019 1020 if (! args) 1021 goto theend; 1022 1023 buffer = g_malloc (strlen (args) + 1); 1024 1025 while (*args || last) 1026 { 1027 gboolean esc = FALSE; 1028 char *buffer_p = buffer; 1029 1030 while (*args && (*args != ':' || esc)) 1031 { 1032 if (esc) 1033 { 1034 if (*args == 'n') 1035 { 1036 ++args; 1037 *buffer_p++ = '\n'; 1038 } 1039 else 1040 { 1041 *buffer_p++ = *args++; 1042 } 1043 esc = FALSE; 1044 } 1045 else if (*args == '\\') 1046 { 1047 ++args; 1048 esc = TRUE; 1049 } 1050 else 1051 { 1052 *buffer_p++ = *args++; 1053 } 1054 } 1055 1056 *buffer_p = '\0'; 1057 1058 if (i >= allocated) 1059 { 1060 allocated = 2 * allocated + 1; 1061 list = g_realloc (list, sizeof (char*) * allocated); 1062 } 1063 list[i++] = g_strdup (buffer); 1064 1065 last = FALSE; 1066 if (*args) 1067 { 1068 ++args; 1069 if (! *args) 1070 last = TRUE; 1071 } 1072 } 1073 1074 theend: 1075 g_free (buffer); 1076 *nargs = i; 1077 1078 return list; 1079 } 1080 1081 static gboolean 1082 command_arg_parse_arg (const epdfinfo_t *ctx, const char *arg, 1083 command_arg_t *cmd_arg, command_arg_type_t type, 1084 gchar **error_msg) 1085 { 1086 GError *gerror = NULL; 1087 1088 if (! arg || !cmd_arg) 1089 return FALSE; 1090 1091 switch (type) 1092 { 1093 case ARG_DOC: 1094 { 1095 document_t *doc = document_open (ctx, arg, NULL, &gerror); 1096 cerror_if_not (doc, error_msg, 1097 "Error opening %s:%s", arg, 1098 gerror ? gerror->message : "Unknown reason"); 1099 1100 cmd_arg->value.doc = doc; 1101 break; 1102 } 1103 case ARG_BOOL: 1104 cerror_if_not (! strcmp (arg, "0") || ! strcmp (arg, "1"), 1105 error_msg, "Expected 0 or 1:%s", arg); 1106 cmd_arg->value.flag = *arg == '1'; 1107 break; 1108 case ARG_NONEMPTY_STRING: 1109 cerror_if_not (*arg, error_msg, "Non-empty string expected"); 1110 /* fall through */ 1111 case ARG_STRING: 1112 cmd_arg->value.string = arg; 1113 break; 1114 case ARG_NATNUM: 1115 { 1116 char *endptr; 1117 long n = strtol (arg, &endptr, 0); 1118 cerror_if_not (! (*endptr || (n < 0)), error_msg, 1119 "Expected natural number:%s", arg); 1120 cmd_arg->value.natnum = n; 1121 } 1122 break; 1123 case ARG_EDGES_OR_POSITION: 1124 { 1125 PopplerRectangle *r = &cmd_arg->value.rectangle; 1126 cerror_if_not (parse_edges_or_position (arg, r), 1127 error_msg, 1128 "Expected a relative position or rectangle: %s", arg); 1129 } 1130 break; 1131 case ARG_EDGES: 1132 { 1133 PopplerRectangle *r = &cmd_arg->value.rectangle; 1134 cerror_if_not (parse_edges (arg, r), 1135 error_msg, 1136 "Expected a relative rectangle: %s", arg); 1137 } 1138 break; 1139 case ARG_EDGE_OR_NEGATIVE: 1140 case ARG_EDGE: 1141 { 1142 char *endptr; 1143 double n = strtod (arg, &endptr); 1144 cerror_if_not (! (*endptr || (type != ARG_EDGE_OR_NEGATIVE && n < 0.0) || n > 1.0), 1145 error_msg, "Expected a relative edge: %s", arg); 1146 cmd_arg->value.edge = n; 1147 } 1148 break; 1149 case ARG_COLOR: 1150 { 1151 guint r,g,b; 1152 cerror_if_not ((strlen (arg) == 7 1153 && 3 == sscanf (arg, "#%2x%2x%2x", &r, &g, &b)), 1154 error_msg, "Invalid color: %s", arg); 1155 cmd_arg->value.color.red = r << 8; 1156 cmd_arg->value.color.green = g << 8; 1157 cmd_arg->value.color.blue = b << 8; 1158 } 1159 break; 1160 case ARG_INVALID: 1161 default: 1162 internal_error ("switch fell through"); 1163 } 1164 1165 cmd_arg->type = type; 1166 1167 return TRUE; 1168 error: 1169 if (gerror) 1170 { 1171 g_error_free (gerror); 1172 gerror = NULL; 1173 } 1174 return FALSE; 1175 } 1176 1177 /** 1178 * Parse arguments for a command. 1179 * 1180 * @param ctx The epdfinfo context. 1181 * @param args A string holding the arguments. This is either empty 1182 * or the suffix of the command starting at the first 1183 * colon after the command name. 1184 * @param len The length of args. 1185 * @param cmd The command for which the arguments should be parsed. 1186 * 1187 * @return 1188 */ 1189 static command_arg_t* 1190 command_arg_parse(epdfinfo_t *ctx, char **args, int nargs, 1191 const command_t *cmd, gchar **error_msg) 1192 { 1193 command_arg_t *cmd_args = g_malloc0 (cmd->nargs * sizeof (command_arg_t)); 1194 int i; 1195 1196 if (nargs < cmd->nargs - 1 1197 || (nargs == cmd->nargs - 1 1198 && cmd->args_spec[cmd->nargs - 1] != ARG_REST) 1199 || (nargs > cmd->nargs 1200 && (cmd->nargs == 0 1201 || cmd->args_spec[cmd->nargs - 1] != ARG_REST))) 1202 { 1203 if (error_msg) 1204 { 1205 *error_msg = 1206 g_strdup_printf ("Command `%s' expects %d argument(s), %d given", 1207 cmd->name, cmd->nargs, nargs); 1208 } 1209 goto failure; 1210 } 1211 1212 for (i = 0; i < cmd->nargs; ++i) 1213 { 1214 if (i == cmd->nargs - 1 && cmd->args_spec[i] == ARG_REST) 1215 { 1216 cmd_args[i].value.rest.args = args + i; 1217 cmd_args[i].value.rest.nargs = nargs - i; 1218 cmd_args[i].type = ARG_REST; 1219 } 1220 else if (i >= nargs 1221 || ! command_arg_parse_arg (ctx, args[i], cmd_args + i, 1222 cmd->args_spec[i], error_msg)) 1223 { 1224 goto failure; 1225 } 1226 } 1227 1228 return cmd_args; 1229 1230 failure: 1231 free_command_args (cmd_args, cmd->nargs); 1232 return NULL; 1233 } 1234 1235 static void 1236 command_arg_print(const command_arg_t *arg) 1237 { 1238 switch (arg->type) 1239 { 1240 case ARG_INVALID: 1241 printf ("[invalid]"); 1242 break; 1243 case ARG_DOC: 1244 print_response_string (arg->value.doc->filename, NONE); 1245 break; 1246 case ARG_BOOL: 1247 printf ("%d", arg->value.flag ? 1 : 0); 1248 break; 1249 case ARG_NONEMPTY_STRING: /* fall */ 1250 case ARG_STRING: 1251 print_response_string (arg->value.string, NONE); 1252 break; 1253 case ARG_NATNUM: 1254 printf ("%ld", arg->value.natnum); 1255 break; 1256 case ARG_EDGE_OR_NEGATIVE: /* fall */ 1257 case ARG_EDGE: 1258 printf ("%f", arg->value.edge); 1259 break; 1260 case ARG_EDGES_OR_POSITION: /* fall */ 1261 case ARG_EDGES: 1262 { 1263 const PopplerRectangle *r = &arg->value.rectangle; 1264 if (r->x2 < 0 && r->y2 < 0) 1265 printf ("%f %f", r->x1, r->y1); 1266 else 1267 printf ("%f %f %f %f", r->x1, r->y1, r->x2, r->y2); 1268 break; 1269 } 1270 case ARG_COLOR: 1271 { 1272 const PopplerColor *c = &arg->value.color; 1273 printf ("#%.2x%.2x%.2x", c->red >> 8, 1274 c->green >> 8, c->blue >> 8); 1275 break; 1276 } 1277 case ARG_REST: 1278 { 1279 int i; 1280 for (i = 0; i < arg->value.rest.nargs; ++i) 1281 print_response_string (arg->value.rest.args[i], COLON); 1282 if (arg->value.rest.nargs > 0) 1283 print_response_string (arg->value.rest.args[i], NONE); 1284 break; 1285 } 1286 default: 1287 internal_error ("switch fell through"); 1288 } 1289 } 1290 1291 static size_t 1292 command_arg_type_size(command_arg_type_t type) 1293 { 1294 command_arg_t arg; 1295 switch (type) 1296 { 1297 case ARG_INVALID: return 0; 1298 case ARG_DOC: return sizeof (arg.value.doc); 1299 case ARG_BOOL: return sizeof (arg.value.flag); 1300 case ARG_NONEMPTY_STRING: /* fall */ 1301 case ARG_STRING: return sizeof (arg.value.string); 1302 case ARG_NATNUM: return sizeof (arg.value.natnum); 1303 case ARG_EDGE_OR_NEGATIVE: /* fall */ 1304 case ARG_EDGE: return sizeof (arg.value.edge); 1305 case ARG_EDGES_OR_POSITION: /* fall */ 1306 case ARG_EDGES: return sizeof (arg.value.rectangle); 1307 case ARG_COLOR: return sizeof (arg.value.color); 1308 case ARG_REST: return sizeof (arg.value.rest); 1309 default: 1310 internal_error ("switch fell through"); 1311 return 0; 1312 } 1313 } 1314 1315 1316 /* ------------------------------------------------------------------ * 1317 * PDF Actions 1318 * ------------------------------------------------------------------ */ 1319 1320 static gboolean 1321 action_is_handled (PopplerAction *action) 1322 { 1323 if (! action) 1324 return FALSE; 1325 1326 switch (action->any.type) 1327 { 1328 case POPPLER_ACTION_GOTO_REMOTE: 1329 case POPPLER_ACTION_GOTO_DEST: 1330 case POPPLER_ACTION_NAMED: 1331 /* case POPPLER_ACTION_LAUNCH: */ 1332 case POPPLER_ACTION_URI: 1333 return TRUE; 1334 default: ; 1335 } 1336 return FALSE; 1337 } 1338 1339 static void 1340 action_print_destination (PopplerDocument *doc, PopplerAction *action) 1341 { 1342 PopplerDest *dest = NULL; 1343 gboolean free_dest = FALSE; 1344 double width, height, top; 1345 PopplerPage *page; 1346 int saved_stdin; 1347 1348 if (action->any.type == POPPLER_ACTION_GOTO_DEST 1349 && action->goto_dest.dest->type == POPPLER_DEST_NAMED) 1350 { 1351 DISCARD_STDOUT (saved_stdin); 1352 /* poppler_document_find_dest reports errors to stdout, so 1353 discard them. */ 1354 dest = poppler_document_find_dest 1355 (doc, action->goto_dest.dest->named_dest); 1356 UNDISCARD_STDOUT (saved_stdin); 1357 free_dest = TRUE; 1358 } 1359 else if (action->any.type == POPPLER_ACTION_NAMED) 1360 1361 { 1362 DISCARD_STDOUT (saved_stdin); 1363 dest = poppler_document_find_dest (doc, action->named.named_dest); 1364 UNDISCARD_STDOUT (saved_stdin); 1365 free_dest = TRUE; 1366 } 1367 1368 else if (action->any.type == POPPLER_ACTION_GOTO_REMOTE) 1369 { 1370 print_response_string (action->goto_remote.file_name, COLON); 1371 dest = action->goto_remote.dest; 1372 } 1373 else if (action->any.type == POPPLER_ACTION_GOTO_DEST) 1374 dest = action->goto_dest.dest; 1375 1376 if (!dest 1377 || dest->type == POPPLER_DEST_UNKNOWN 1378 || dest->page_num < 1 1379 || dest->page_num > poppler_document_get_n_pages (doc)) 1380 { 1381 printf (":"); 1382 goto theend; 1383 } 1384 1385 printf ("%d:", dest->page_num); 1386 1387 if (action->type == POPPLER_ACTION_GOTO_REMOTE 1388 || NULL == (page = poppler_document_get_page (doc, dest->page_num - 1))) 1389 { 1390 goto theend; 1391 } 1392 1393 poppler_page_get_size (page, &width, &height); 1394 g_object_unref (page); 1395 top = (height - dest->top) / height; 1396 1397 /* adapted from xpdf */ 1398 switch (dest->type) 1399 { 1400 case POPPLER_DEST_XYZ: 1401 if (dest->change_top) 1402 printf ("%f", top); 1403 break; 1404 case POPPLER_DEST_FIT: 1405 case POPPLER_DEST_FITB: 1406 case POPPLER_DEST_FITH: 1407 case POPPLER_DEST_FITBH: 1408 putchar ('0'); 1409 break; 1410 case POPPLER_DEST_FITV: 1411 case POPPLER_DEST_FITBV: 1412 case POPPLER_DEST_FITR: 1413 printf ("%f", top); 1414 break; 1415 default: ; 1416 } 1417 1418 theend: 1419 if (free_dest) 1420 poppler_dest_free (dest); 1421 } 1422 1423 static void 1424 action_print (PopplerDocument *doc, PopplerAction *action) 1425 { 1426 if (! action_is_handled (action)) 1427 return; 1428 1429 print_response_string (xpoppler_action_type_string (action->any.type), COLON); 1430 print_response_string (action->any.title, COLON); 1431 switch (action->any.type) 1432 { 1433 case POPPLER_ACTION_GOTO_REMOTE: 1434 case POPPLER_ACTION_GOTO_DEST: 1435 case POPPLER_ACTION_NAMED: 1436 action_print_destination (doc, action); 1437 putchar ('\n'); 1438 break; 1439 case POPPLER_ACTION_LAUNCH: 1440 print_response_string (action->launch.file_name, COLON); 1441 print_response_string (action->launch.params, NEWLINE); 1442 break; 1443 case POPPLER_ACTION_URI: 1444 print_response_string (action->uri.uri, NEWLINE); 1445 break; 1446 default: 1447 ; 1448 } 1449 } 1450 1451 1452 /* ------------------------------------------------------------------ * 1453 * PDF Annotations and Attachments 1454 * ------------------------------------------------------------------ */ 1455 1456 /* static gint 1457 * annotation_cmp_edges (const annotation_t *a1, const annotation_t *a2) 1458 * { 1459 * PopplerRectangle *e1 = &a1->amap->area; 1460 * PopplerRectangle *e2 = &a2->amap->area; 1461 * 1462 * return (e1->y1 > e2->y1 ? -1 1463 * : e1->y1 < e2->y1 ? 1 1464 * : e1->x1 < e2->x1 ? -1 1465 * : e1->x1 != e2->x1); 1466 * } */ 1467 1468 static GList* 1469 annoation_get_for_page (document_t *doc, gint pn) 1470 { 1471 1472 GList *annot_list, *item; 1473 PopplerPage *page; 1474 gint i = 0; 1475 gint npages = poppler_document_get_n_pages (doc->pdf); 1476 1477 if (pn < 1 || pn > npages) 1478 return NULL; 1479 1480 if (! doc->annotations.pages) 1481 doc->annotations.pages = g_malloc0 (npages * sizeof(GList*)); 1482 1483 if (doc->annotations.pages[pn - 1]) 1484 return doc->annotations.pages[pn - 1]; 1485 1486 if (! doc->annotations.keys) 1487 doc->annotations.keys = g_hash_table_new (g_str_hash, g_str_equal); 1488 1489 page = poppler_document_get_page (doc->pdf, pn - 1); 1490 if (NULL == page) 1491 return NULL; 1492 1493 annot_list = poppler_page_get_annot_mapping (page); 1494 for (item = annot_list; item; item = item->next) 1495 { 1496 PopplerAnnotMapping *map = (PopplerAnnotMapping *)item->data; 1497 gchar *key = g_strdup_printf ("annot-%d-%d", pn, i); 1498 annotation_t *a = g_malloc (sizeof (annotation_t)); 1499 a->amap = map; 1500 a->key = key; 1501 doc->annotations.pages[pn - 1] = 1502 g_list_prepend (doc->annotations.pages[pn - 1], a); 1503 assert (NULL == g_hash_table_lookup (doc->annotations.keys, key)); 1504 g_hash_table_insert (doc->annotations.keys, key, a); 1505 ++i; 1506 } 1507 g_list_free (annot_list); 1508 g_object_unref (page); 1509 return doc->annotations.pages[pn - 1]; 1510 } 1511 1512 static annotation_t* 1513 annotation_get_by_key (document_t *doc, const gchar *key) 1514 { 1515 if (! doc->annotations.keys) 1516 return NULL; 1517 1518 return g_hash_table_lookup (doc->annotations.keys, key); 1519 } 1520 1521 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1522 static cairo_region_t* 1523 annotation_markup_get_text_regions (PopplerPage *page, PopplerAnnotTextMarkup *a) 1524 { 1525 GArray *quads = poppler_annot_text_markup_get_quadrilaterals (a); 1526 int i; 1527 cairo_region_t *region = cairo_region_create (); 1528 gdouble height; 1529 1530 poppler_page_get_size (page, NULL, &height); 1531 1532 for (i = 0; i < quads->len; ++i) 1533 { 1534 PopplerQuadrilateral *q = &g_array_index (quads, PopplerQuadrilateral, i); 1535 cairo_rectangle_int_t r; 1536 1537 q->p1.y = height - q->p1.y; 1538 q->p2.y = height - q->p2.y; 1539 q->p3.y = height - q->p3.y; 1540 q->p4.y = height - q->p4.y; 1541 1542 r.x = (int) (MIN (q->p1.x, MIN (q->p2.x, MIN (q->p3.x, q->p4.x))) + 0.5); 1543 r.y = (int) (MIN (q->p1.y, MIN (q->p2.y, MIN (q->p3.y, q->p4.y))) + 0.5); 1544 r.width = (int) (MAX (q->p1.x, MAX (q->p2.x, MAX (q->p3.x, q->p4.x))) + 0.5) 1545 - r.x; 1546 r.height = (int) (MAX (q->p1.y, MAX (q->p2.y, MAX (q->p3.y, q->p4.y))) + 0.5) 1547 - r.y; 1548 1549 cairo_region_union_rectangle (region, &r); 1550 } 1551 g_array_unref (quads); 1552 return region; 1553 } 1554 1555 /** 1556 * Append quadrilaterals equivalent to region to an array. 1557 * 1558 * @param page The page of the annotation. This is used to get the 1559 * text regions and pagesize. 1560 * @param selection_style The selection style. 1561 * @param region The region to add. 1562 * @param garray[in,out] An array of PopplerQuadrilateral, where the 1563 * new quadrilaterals will be appended. 1564 */ 1565 static void 1566 annotation_markup_append_text_region (PopplerPage *page, 1567 PopplerSelectionStyle selection_style, 1568 PopplerRectangle *region, 1569 GArray *garray) 1570 { 1571 gdouble height; 1572 /* poppler_page_get_selection_region is deprecated w/o a 1573 replacement. (poppler_page_get_selected_region returns a union 1574 of rectangles.) */ 1575 GList *regions = 1576 poppler_page_get_selection_region (page, 1.0, selection_style, region); 1577 GList *item; 1578 1579 poppler_page_get_size (page, NULL, &height); 1580 for (item = regions; item; item = item->next) 1581 { 1582 PopplerRectangle *r = item->data; 1583 PopplerQuadrilateral q; 1584 1585 q.p1.x = r->x1; 1586 q.p1.y = height - r->y1; 1587 q.p2.x = r->x2; 1588 q.p2.y = height - r->y1; 1589 q.p4.x = r->x2; 1590 q.p4.y = height - r->y2; 1591 q.p3.x = r->x1; 1592 q.p3.y = height - r->y2; 1593 1594 g_array_append_val (garray, q); 1595 } 1596 g_list_free (regions); 1597 } 1598 1599 #endif 1600 /** 1601 * Create a new annotation. 1602 * 1603 * @param doc The document for which to create it. 1604 * @param type The type of the annotation. 1605 * @param selection_style The selection style. 1606 * @param r The rectangle where annotation will end up on the page. 1607 * 1608 * @return The new annotation, or NULL, if the annotation type is 1609 * not available. 1610 */ 1611 static PopplerAnnot* 1612 annotation_new (const epdfinfo_t *ctx, document_t *doc, PopplerPage *page, 1613 const char *type, PopplerSelectionStyle selection_style, 1614 PopplerRectangle *r, const command_arg_t *rest, 1615 char **error_msg) 1616 { 1617 1618 PopplerAnnot *a = NULL; 1619 int nargs = rest->value.rest.nargs; 1620 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1621 char * const *args = rest->value.rest.args; 1622 int i; 1623 GArray *garray = NULL; 1624 command_arg_t carg; 1625 double width, height; 1626 cairo_region_t *region = NULL; 1627 #endif 1628 1629 if (! strcmp (type, "text")) 1630 { 1631 cerror_if_not (nargs == 0, error_msg, "%s", "Too many arguments"); 1632 return poppler_annot_text_new (doc->pdf, r); 1633 } 1634 1635 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1636 garray = g_array_new (FALSE, FALSE, sizeof (PopplerQuadrilateral)); 1637 poppler_page_get_size (page, &width, &height); 1638 for (i = 0; i < nargs; ++i) 1639 { 1640 PopplerRectangle *rr = &carg.value.rectangle; 1641 1642 error_if_not (command_arg_parse_arg (ctx, args[i], &carg, 1643 ARG_EDGES, error_msg)); 1644 rr->x1 *= width; rr->x2 *= width; 1645 rr->y1 *= height; rr->y2 *= height; 1646 annotation_markup_append_text_region (page, selection_style, rr, garray); 1647 } 1648 cerror_if_not (garray->len > 0, error_msg, "%s", 1649 "Unable to create empty markup annotation"); 1650 1651 if (! strcmp (type, "highlight")) 1652 a = poppler_annot_text_markup_new_highlight (doc->pdf, r, garray); 1653 else if (! strcmp (type, "squiggly")) 1654 a = poppler_annot_text_markup_new_squiggly (doc->pdf, r, garray); 1655 else if (! strcmp (type, "strike-out")) 1656 a = poppler_annot_text_markup_new_strikeout (doc->pdf, r, garray); 1657 else if (! strcmp (type, "underline")) 1658 a = poppler_annot_text_markup_new_underline (doc->pdf, r, garray); 1659 else 1660 cerror_if_not (0, error_msg, "Unknown annotation type: %s", type); 1661 1662 #endif 1663 error: 1664 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1665 if (garray) g_array_unref (garray); 1666 if (region) cairo_region_destroy (region); 1667 #endif 1668 return a; 1669 } 1670 1671 static gboolean 1672 annotation_edit_validate (const epdfinfo_t *ctx, const command_arg_t *rest, 1673 PopplerAnnot *annotation, char **error_msg) 1674 { 1675 int nargs = rest->value.rest.nargs; 1676 char * const *args = rest->value.rest.args; 1677 int i = 0; 1678 command_arg_t carg; 1679 1680 const char* error_fmt = 1681 "Can modify `%s' property only for %s annotations"; 1682 1683 while (i < nargs) 1684 { 1685 command_arg_type_t atype = ARG_INVALID; 1686 const char *key = args[i++]; 1687 1688 cerror_if_not (i < nargs, error_msg, "Missing a value argument"); 1689 1690 if (! strcmp (key, "flags")) 1691 atype = ARG_NATNUM; 1692 else if (! strcmp (key, "color")) 1693 atype = ARG_COLOR; 1694 else if (! strcmp (key, "contents")) 1695 atype = ARG_STRING; 1696 else if (! strcmp (key, "edges")) 1697 atype = ARG_EDGES_OR_POSITION; 1698 else if (! strcmp (key, "label")) 1699 { 1700 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1701 error_fmt, key, "markup"); 1702 atype = ARG_STRING; 1703 } 1704 else if (! strcmp (key, "opacity")) 1705 { 1706 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1707 error_fmt, key, "markup"); 1708 atype = ARG_EDGE; 1709 } 1710 else if (! strcmp (key, "popup")) 1711 { 1712 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1713 error_fmt, key, "markup"); 1714 atype = ARG_EDGES; 1715 } 1716 else if (! strcmp (key, "popup-is-open")) 1717 { 1718 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1719 error_fmt, key, "markup"); 1720 atype = ARG_BOOL; 1721 } 1722 else if (! strcmp (key, "icon")) 1723 { 1724 cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg, 1725 error_fmt, key, "text"); 1726 atype = ARG_STRING; 1727 } 1728 else if (! strcmp (key, "is-open")) 1729 { 1730 cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg, 1731 error_fmt, key, "text"); 1732 atype = ARG_BOOL; 1733 } 1734 else 1735 { 1736 cerror_if_not (0, error_msg, 1737 "Unable to modify property `%s'", key); 1738 } 1739 1740 if (! command_arg_parse_arg (ctx, args[i++], &carg, atype, error_msg)) 1741 return FALSE; 1742 } 1743 1744 return TRUE; 1745 1746 error: 1747 return FALSE; 1748 } 1749 1750 static void 1751 annotation_print (const annotation_t *annot, /* const */ PopplerPage *page) 1752 { 1753 double width, height; 1754 PopplerAnnotMapping *m; 1755 const gchar *key; 1756 PopplerAnnot *a; 1757 PopplerAnnotMarkup *ma; 1758 PopplerAnnotText *ta; 1759 PopplerRectangle r; 1760 PopplerColor *color; 1761 gchar *text; 1762 gdouble opacity; 1763 cairo_region_t *region = NULL; 1764 GDate *date; 1765 1766 if (! annot || ! page) 1767 return; 1768 1769 m = annot->amap; 1770 key = annot->key; 1771 a = m->annot; 1772 poppler_page_get_size (page, &width, &height); 1773 1774 r.x1 = m->area.x1; 1775 r.x2 = m->area.x2; 1776 r.y1 = height - m->area.y2; 1777 r.y2 = height - m->area.y1; 1778 1779 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1780 if (POPPLER_IS_ANNOT_TEXT_MARKUP (a)) 1781 { 1782 region = annotation_markup_get_text_regions (page, POPPLER_ANNOT_TEXT_MARKUP (a)); 1783 perror_if_not (region, "%s", "Unable to extract annotation's text regions"); 1784 } 1785 #endif 1786 1787 /* >>> Any Annotation >>> */ 1788 /* Page */ 1789 printf ("%d:", poppler_page_get_index (page) + 1); 1790 /* Area */ 1791 printf ("%f %f %f %f:", r.x1 / width, r.y1 / height 1792 , r.x2 / width, r.y2 / height); 1793 1794 /* Type */ 1795 printf ("%s:", xpoppler_annot_type_string (poppler_annot_get_annot_type (a))); 1796 /* Internal Key */ 1797 print_response_string (key, COLON); 1798 1799 /* Flags */ 1800 printf ("%d:", poppler_annot_get_flags (a)); 1801 1802 /* Color */ 1803 color = poppler_annot_get_color (a); 1804 if (color) 1805 { 1806 /* Reduce 2 Byte to 1 Byte color space */ 1807 printf ("#%.2x%.2x%.2x", (color->red >> 8) 1808 , (color->green >> 8) 1809 , (color->blue >> 8)); 1810 g_free (color); 1811 } 1812 1813 putchar (':'); 1814 1815 /* Text Contents */ 1816 text = poppler_annot_get_contents (a); 1817 print_response_string (text, COLON); 1818 g_free (text); 1819 1820 /* Modified Date */ 1821 text = poppler_annot_get_modified (a); 1822 print_response_string (text, NONE); 1823 g_free (text); 1824 1825 /* <<< Any Annotation <<< */ 1826 1827 /* >>> Markup Annotation >>> */ 1828 if (! POPPLER_IS_ANNOT_MARKUP (a)) 1829 { 1830 putchar ('\n'); 1831 goto theend; 1832 } 1833 1834 putchar (':'); 1835 ma = POPPLER_ANNOT_MARKUP (a); 1836 /* Label */ 1837 text = poppler_annot_markup_get_label (ma); 1838 print_response_string (text, COLON); 1839 g_free (text); 1840 1841 /* Subject */ 1842 text = poppler_annot_markup_get_subject (ma); 1843 print_response_string (text, COLON); 1844 g_free (text); 1845 1846 /* Opacity */ 1847 opacity = poppler_annot_markup_get_opacity (ma); 1848 printf ("%f:", opacity); 1849 1850 /* Popup (Area + isOpen) */ 1851 if (poppler_annot_markup_has_popup (ma) 1852 && poppler_annot_markup_get_popup_rectangle (ma, &r)) 1853 { 1854 gdouble tmp = r.y1; 1855 r.y1 = height - r.y2; 1856 r.y2 = height - tmp; 1857 printf ("%f %f %f %f:%d:", r.x1 / width, r.y1 / height 1858 , r.x2 / width, r.y2 / height 1859 , poppler_annot_markup_get_popup_is_open (ma) ? 1 : 0); 1860 1861 } 1862 else 1863 printf ("::"); 1864 1865 /* Creation Date */ 1866 date = poppler_annot_markup_get_date (ma); 1867 if (date != NULL && g_date_valid(date)) 1868 { 1869 gchar datebuf[128]; 1870 g_date_strftime (datebuf, 127, "%x", date); 1871 print_response_string (datebuf, NONE); 1872 g_date_free (date); 1873 } 1874 1875 /* <<< Markup Annotation <<< */ 1876 1877 /* >>> Text Annotation >>> */ 1878 if (POPPLER_IS_ANNOT_TEXT (a)) 1879 { 1880 putchar (':'); 1881 ta = POPPLER_ANNOT_TEXT (a); 1882 /* Text Icon */ 1883 text = poppler_annot_text_get_icon (ta); 1884 print_response_string (text, COLON); 1885 g_free (text); 1886 /* Text State */ 1887 printf ("%s:%d", 1888 xpoppler_annot_text_state_string (poppler_annot_text_get_state (ta)), 1889 poppler_annot_text_get_is_open (ta)); 1890 } 1891 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1892 /* <<< Text Annotation <<< */ 1893 else if (POPPLER_IS_ANNOT_TEXT_MARKUP (a)) 1894 { 1895 /* >>> Markup Text Annotation >>> */ 1896 putchar (':'); 1897 region_print (region, width, height); 1898 /* <<< Markup Text Annotation <<< */ 1899 } 1900 #endif 1901 putchar ('\n'); 1902 theend: 1903 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1904 error: 1905 #endif 1906 if (region) cairo_region_destroy (region); 1907 } 1908 1909 static void 1910 attachment_print (PopplerAttachment *att, const char *id, gboolean do_save) 1911 { 1912 time_t time; 1913 1914 print_response_string (id, COLON); 1915 print_response_string (att->name, COLON); 1916 print_response_string (att->description, COLON); 1917 if (att->size + 1 != 0) 1918 printf ("%" G_GSIZE_FORMAT ":", att->size); 1919 else 1920 printf ("-1:"); 1921 time = (time_t) att->mtime; 1922 print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON); 1923 time = (time_t) att->ctime; 1924 print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON); 1925 print_response_string (att->checksum ? att->checksum->str : "" , COLON); 1926 if (do_save) 1927 { 1928 char *filename = mktempfile (); 1929 GError *error = NULL; 1930 if (filename) 1931 { 1932 if (! poppler_attachment_save (att, filename, &error)) 1933 { 1934 fprintf (stderr, "Writing attachment failed: %s" 1935 , error ? error->message : "reason unknown"); 1936 if (error) 1937 g_free (error); 1938 } 1939 else 1940 { 1941 print_response_string (filename, NONE); 1942 } 1943 free (filename); 1944 } 1945 } 1946 putchar ('\n'); 1947 } 1948 1949 1950 1951 /* ================================================================== * 1952 * Server command implementations 1953 * ================================================================== */ 1954 1955 /* Name: features 1956 Args: None 1957 Returns: A list of compile-time features. 1958 Errors: None 1959 */ 1960 1961 const command_arg_type_t cmd_features_spec[] = {}; 1962 1963 static void 1964 cmd_features (const epdfinfo_t *ctx, const command_arg_t *args) 1965 { 1966 const char *features[] = { 1967 #ifdef HAVE_POPPLER_FIND_OPTS 1968 "case-sensitive-search", 1969 #else 1970 "no-case-sensitive-search", 1971 #endif 1972 #ifdef HAVE_POPPLER_ANNOT_WRITE 1973 "writable-annotations", 1974 #else 1975 "no-writable-annotations", 1976 #endif 1977 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1978 "markup-annotations" 1979 #else 1980 "no-markup-annotations" 1981 #endif 1982 }; 1983 int i; 1984 OK_BEGIN (); 1985 for (i = 0; i < G_N_ELEMENTS (features); ++i) 1986 { 1987 printf ("%s", features[i]); 1988 if (i < G_N_ELEMENTS (features) - 1) 1989 putchar (':'); 1990 } 1991 putchar ('\n'); 1992 OK_END (); 1993 } 1994 1995 1996 /* Name: open 1997 Args: filename password 1998 Returns: Nothing 1999 Errors: If file can't be opened or is not a PDF document. 2000 */ 2001 2002 const command_arg_type_t cmd_open_spec[] = 2003 { 2004 ARG_NONEMPTY_STRING, /* filename */ 2005 ARG_STRING, /* password */ 2006 }; 2007 2008 static void 2009 cmd_open (const epdfinfo_t *ctx, const command_arg_t *args) 2010 { 2011 const char *filename = args[0].value.string; 2012 const char *passwd = args[1].value.string; 2013 GError *gerror = NULL; 2014 document_t *doc; 2015 2016 if (! *passwd) 2017 passwd = NULL; 2018 2019 doc = document_open(ctx, filename, passwd, &gerror); 2020 perror_if_not (doc, "Error opening %s:%s", filename, 2021 gerror ? gerror->message : "unknown error"); 2022 OK (); 2023 2024 error: 2025 if (gerror) 2026 { 2027 g_error_free (gerror); 2028 gerror = NULL; 2029 } 2030 } 2031 2032 /* Name: close 2033 Args: filename 2034 Returns: 1 if file was open, otherwise 0. 2035 Errors: None 2036 */ 2037 2038 const command_arg_type_t cmd_close_spec[] = 2039 { 2040 ARG_NONEMPTY_STRING /* filename */ 2041 }; 2042 2043 static void 2044 cmd_close (const epdfinfo_t *ctx, const command_arg_t *args) 2045 { 2046 document_t *doc = g_hash_table_lookup(ctx->documents, args->value.string); 2047 2048 g_hash_table_remove (ctx->documents, args->value.string); 2049 free_document (doc); 2050 OK_BEGIN (); 2051 puts (doc ? "1" : "0"); 2052 OK_END (); 2053 } 2054 2055 /* Name: closeall 2056 Args: None 2057 Returns: Nothing 2058 Errors: None 2059 */ 2060 static void 2061 cmd_closeall (const epdfinfo_t *ctx, const command_arg_t *args) 2062 { 2063 GHashTableIter iter; 2064 gpointer key, value; 2065 2066 g_hash_table_iter_init (&iter, ctx->documents); 2067 while (g_hash_table_iter_next (&iter, &key, &value)) 2068 { 2069 document_t *doc = (document_t*) value; 2070 free_document (doc); 2071 g_hash_table_iter_remove (&iter); 2072 } 2073 OK (); 2074 } 2075 2076 2077 const command_arg_type_t cmd_search_regexp_spec[] = 2078 { 2079 ARG_DOC, 2080 ARG_NATNUM, /* first page */ 2081 ARG_NATNUM, /* last page */ 2082 ARG_NONEMPTY_STRING, /* regexp */ 2083 ARG_NATNUM, /* compile flags */ 2084 ARG_NATNUM /* match flags */ 2085 }; 2086 2087 static void 2088 cmd_search_regexp(const epdfinfo_t *ctx, const command_arg_t *args) 2089 { 2090 PopplerDocument *doc = args[0].value.doc->pdf; 2091 int first = args[1].value.natnum; 2092 int last = args[2].value.natnum; 2093 const gchar *regexp = args[3].value.string; 2094 GRegexCompileFlags cflags = args[4].value.natnum; 2095 GRegexMatchFlags mflags = args[5].value.natnum; 2096 double width, height; 2097 int pn; 2098 GError *gerror = NULL; 2099 GRegex *re = NULL; 2100 2101 NORMALIZE_PAGE_ARG (doc, &first, &last); 2102 2103 re = g_regex_new (regexp, cflags, mflags, &gerror); 2104 perror_if_not (NULL == gerror, "Invalid regexp: %s", gerror->message); 2105 2106 OK_BEGIN (); 2107 for (pn = first; pn <= last; ++pn) 2108 { 2109 PopplerPage *page = poppler_document_get_page(doc, pn - 1); 2110 char *text; 2111 PopplerRectangle *rectangles = NULL; 2112 guint nrectangles; 2113 GMatchInfo *match = NULL; 2114 2115 if (! page) 2116 continue; 2117 2118 text = poppler_page_get_text (page); 2119 poppler_page_get_text_layout (page, &rectangles, &nrectangles); 2120 poppler_page_get_size (page, &width, &height); 2121 g_regex_match (re, text, 0, &match); 2122 2123 while (g_match_info_matches (match)) 2124 { 2125 const double scale = 100.0; 2126 gint start, end, ustart, ulen; 2127 gchar *string = NULL; 2128 gchar *line = NULL; 2129 int i; 2130 2131 /* Does this ever happen ? */ 2132 if (! g_match_info_fetch_pos (match, 0, &start, &end)) 2133 continue; 2134 2135 string = g_match_info_fetch (match, 0); 2136 ustart = g_utf8_strlen (text, start); 2137 ulen = g_utf8_strlen (string, -1); 2138 2139 cairo_region_t *region = cairo_region_create (); 2140 /* Merge matched glyph rectangles. Scale them so we're able 2141 to use cairo . */ 2142 if (ulen > 0) 2143 { 2144 assert (ustart < nrectangles 2145 && ustart + ulen <= nrectangles); 2146 line = poppler_page_get_selected_text 2147 (page, POPPLER_SELECTION_LINE, rectangles + ustart); 2148 2149 for (i = ustart; i < ustart + ulen; ++i) 2150 { 2151 PopplerRectangle *r = rectangles + i; 2152 cairo_rectangle_int_t c; 2153 2154 c.x = (int) (scale * r->x1 + 0.5); 2155 c.y = (int) (scale * r->y1 + 0.5); 2156 c.width = (int) (scale * (r->x2 - r->x1) + 0.5); 2157 c.height = (int) (scale * (r->y2 - r->y1) + 0.5); 2158 2159 cairo_region_union_rectangle (region, &c); 2160 } 2161 2162 } 2163 2164 printf ("%d:", pn); 2165 print_response_string (string, COLON); 2166 print_response_string (strchomp (line), COLON); 2167 region_print (region, width * scale, height * scale); 2168 putchar ('\n'); 2169 cairo_region_destroy (region); 2170 g_free (string); 2171 g_free (line); 2172 g_match_info_next (match, NULL); 2173 } 2174 g_free (rectangles); 2175 g_object_unref (page); 2176 g_free (text); 2177 g_match_info_free (match); 2178 } 2179 OK_END (); 2180 2181 error: 2182 if (re) g_regex_unref (re); 2183 if (gerror) g_error_free (gerror); 2184 } 2185 2186 const command_arg_type_t cmd_regexp_flags_spec[] = 2187 { 2188 }; 2189 2190 static void 2191 cmd_regexp_flags (const epdfinfo_t *ctx, const command_arg_t *args) 2192 { 2193 OK_BEGIN (); 2194 printf ("caseless:%d\n", G_REGEX_CASELESS); 2195 printf ("multiline:%d\n", G_REGEX_MULTILINE); 2196 printf ("dotall:%d\n", G_REGEX_DOTALL); 2197 printf ("extended:%d\n", G_REGEX_EXTENDED); 2198 printf ("anchored:%d\n", G_REGEX_ANCHORED); 2199 printf ("dollar-endonly:%d\n", G_REGEX_DOLLAR_ENDONLY); 2200 printf ("ungreedy:%d\n", G_REGEX_UNGREEDY); 2201 printf ("raw:%d\n", G_REGEX_RAW); 2202 printf ("no-auto-capture:%d\n", G_REGEX_NO_AUTO_CAPTURE); 2203 printf ("optimize:%d\n", G_REGEX_OPTIMIZE); 2204 printf ("dupnames:%d\n", G_REGEX_DUPNAMES); 2205 printf ("newline-cr:%d\n", G_REGEX_NEWLINE_CR); 2206 printf ("newline-lf:%d\n", G_REGEX_NEWLINE_LF); 2207 printf ("newline-crlf:%d\n", G_REGEX_NEWLINE_CRLF); 2208 2209 printf ("match-anchored:%d\n", G_REGEX_MATCH_ANCHORED); 2210 printf ("match-notbol:%d\n", G_REGEX_MATCH_NOTBOL); 2211 printf ("match-noteol:%d\n", G_REGEX_MATCH_NOTEOL); 2212 printf ("match-notempty:%d\n", G_REGEX_MATCH_NOTEMPTY); 2213 printf ("match-partial:%d\n", G_REGEX_MATCH_PARTIAL); 2214 printf ("match-newline-cr:%d\n", G_REGEX_MATCH_NEWLINE_CR); 2215 printf ("match-newline-lf:%d\n", G_REGEX_MATCH_NEWLINE_LF); 2216 printf ("match-newline-crlf:%d\n", G_REGEX_MATCH_NEWLINE_CRLF); 2217 printf ("match-newline-any:%d\n", G_REGEX_MATCH_NEWLINE_ANY); 2218 2219 OK_END (); 2220 } 2221 2222 2223 const command_arg_type_t cmd_search_string_spec[] = 2224 { 2225 ARG_DOC, 2226 ARG_NATNUM, /* first page */ 2227 ARG_NATNUM, /* last page */ 2228 ARG_NONEMPTY_STRING, /* search string */ 2229 ARG_BOOL, /* ignore-case */ 2230 }; 2231 2232 static void 2233 cmd_search_string(const epdfinfo_t *ctx, const command_arg_t *args) 2234 { 2235 PopplerDocument *doc = args[0].value.doc->pdf; 2236 int first = args[1].value.natnum; 2237 int last = args[2].value.natnum; 2238 const char *string = args[3].value.string; 2239 gboolean ignore_case = args[4].value.flag; 2240 GList *list, *item; 2241 double width, height; 2242 int pn; 2243 #ifdef HAVE_POPPLER_FIND_OPTS 2244 PopplerFindFlags flags = ignore_case ? 0 : POPPLER_FIND_CASE_SENSITIVE; 2245 #endif 2246 2247 NORMALIZE_PAGE_ARG (doc, &first, &last); 2248 OK_BEGIN (); 2249 for (pn = first; pn <= last; ++pn) 2250 { 2251 PopplerPage *page = poppler_document_get_page(doc, pn - 1); 2252 2253 if (! page) 2254 continue; 2255 2256 #ifdef HAVE_POPPLER_FIND_OPTS 2257 list = poppler_page_find_text_with_options(page, string, flags); 2258 #else 2259 list = poppler_page_find_text(page, string); 2260 #endif 2261 2262 poppler_page_get_size (page, &width, &height); 2263 2264 for (item = list; item; item = item->next) 2265 { 2266 gchar *line, *match; 2267 PopplerRectangle *r = item->data; 2268 gdouble y1 = r->y1; 2269 2270 r->y1 = height - r->y2; 2271 r->y2 = height - y1; 2272 2273 printf ("%d:", pn); 2274 line = strchomp (poppler_page_get_selected_text 2275 (page, POPPLER_SELECTION_LINE, r)); 2276 match = strchomp (poppler_page_get_selected_text 2277 (page, POPPLER_SELECTION_GLYPH, r)); 2278 print_response_string (match, COLON); 2279 print_response_string (line, COLON); 2280 printf ("%f %f %f %f\n", 2281 r->x1 / width, r->y1 / height, 2282 r->x2 / width, r->y2 / height); 2283 g_free (line); 2284 g_free (match); 2285 poppler_rectangle_free (r); 2286 } 2287 g_list_free (list); 2288 g_object_unref (page); 2289 } 2290 OK_END (); 2291 } 2292 2293 /* Name: metadata 2294 Args: filename 2295 Returns: PDF's metadata 2296 Errors: None 2297 2298 title author subject keywords creator producer pdf-version create-date mod-date 2299 2300 Dates are in seconds since the epoche. 2301 2302 */ 2303 2304 const command_arg_type_t cmd_metadata_spec[] = 2305 { 2306 ARG_DOC, 2307 }; 2308 2309 static void 2310 cmd_metadata (const epdfinfo_t *ctx, const command_arg_t *args) 2311 { 2312 PopplerDocument *doc = args[0].value.doc->pdf; 2313 time_t date; 2314 gchar *md[6]; 2315 gchar *title; 2316 int i; 2317 char *time_str; 2318 2319 OK_BEGIN (); 2320 2321 title = poppler_document_get_title (doc); 2322 print_response_string (title, COLON); 2323 g_free (title); 2324 2325 md[0] = poppler_document_get_author (doc); 2326 md[1] = poppler_document_get_subject (doc); 2327 md[2] = poppler_document_get_keywords (doc); 2328 md[3] = poppler_document_get_creator (doc); 2329 md[4] = poppler_document_get_producer (doc); 2330 md[5] = poppler_document_get_pdf_version_string (doc); 2331 2332 for (i = 0; i < 6; ++i) 2333 { 2334 print_response_string (md[i], COLON); 2335 g_free (md[i]); 2336 } 2337 2338 date = poppler_document_get_creation_date (doc); 2339 time_str = strchomp (ctime (&date)); 2340 print_response_string (time_str ? time_str : "", COLON); 2341 date = poppler_document_get_modification_date (doc); 2342 time_str = strchomp (ctime (&date)); 2343 print_response_string (time_str ? time_str : "", NEWLINE); 2344 OK_END (); 2345 } 2346 2347 /* Name: outline 2348 Args: filename 2349 2350 Returns: The documents outline (or index) as a, possibly empty, 2351 list of records: 2352 2353 tree-level ACTION 2354 2355 See cmd_pagelinks for how ACTION is constructed. 2356 2357 Errors: None 2358 */ 2359 2360 static void 2361 cmd_outline_walk (PopplerDocument *doc, PopplerIndexIter *iter, int depth) 2362 { 2363 do 2364 { 2365 PopplerIndexIter *child; 2366 PopplerAction *action = poppler_index_iter_get_action (iter); 2367 2368 if (! action) 2369 continue; 2370 2371 if (action_is_handled (action)) 2372 { 2373 printf ("%d:", depth); 2374 action_print (doc, action); 2375 } 2376 2377 child = poppler_index_iter_get_child (iter); 2378 if (child) 2379 { 2380 cmd_outline_walk (doc, child, depth + 1); 2381 } 2382 poppler_action_free (action); 2383 poppler_index_iter_free (child); 2384 } while (poppler_index_iter_next (iter)); 2385 } 2386 2387 const command_arg_type_t cmd_outline_spec[] = 2388 { 2389 ARG_DOC, 2390 }; 2391 2392 static void 2393 cmd_outline (const epdfinfo_t *ctx, const command_arg_t *args) 2394 { 2395 PopplerIndexIter *iter = poppler_index_iter_new (args->value.doc->pdf); 2396 OK_BEGIN (); 2397 if (iter) 2398 { 2399 cmd_outline_walk (args->value.doc->pdf, iter, 1); 2400 poppler_index_iter_free (iter); 2401 } 2402 OK_END (); 2403 } 2404 2405 /* Name: quit 2406 Args: None 2407 Returns: Nothing 2408 Errors: None 2409 2410 Close all documents and exit. 2411 */ 2412 2413 2414 const command_arg_type_t cmd_quit_spec[] = {}; 2415 2416 static void 2417 cmd_quit (const epdfinfo_t *ctx, const command_arg_t *args) 2418 { 2419 cmd_closeall (ctx, args); 2420 exit (EXIT_SUCCESS); 2421 } 2422 2423 /* Name: number-of-pages 2424 Args: filename 2425 Returns: The number of pages. 2426 Errors: None 2427 */ 2428 2429 2430 const command_arg_type_t cmd_number_of_pages_spec[] = 2431 { 2432 ARG_DOC 2433 }; 2434 2435 static void 2436 cmd_number_of_pages (const epdfinfo_t *ctx, const command_arg_t *args) 2437 { 2438 int npages = poppler_document_get_n_pages (args->value.doc->pdf); 2439 OK_BEGIN (); 2440 printf ("%d\n", npages); 2441 OK_END (); 2442 } 2443 2444 /* Name: pagelinks 2445 Args: filename page 2446 Returns: A list of linkmaps: 2447 2448 edges ACTION , 2449 2450 where ACTION is one of 2451 2452 'goto-dest' title page top 2453 'goto-remote' title filename page top 2454 'uri' title URI 2455 'launch' title program arguments 2456 2457 top is desired vertical position, filename is the target PDF of the 2458 `goto-remote' link. 2459 2460 Errors: None 2461 */ 2462 2463 2464 const command_arg_type_t cmd_pagelinks_spec[] = 2465 { 2466 ARG_DOC, 2467 ARG_NATNUM /* page number */ 2468 }; 2469 2470 static void 2471 cmd_pagelinks(const epdfinfo_t *ctx, const command_arg_t *args) 2472 { 2473 PopplerDocument *doc = args[0].value.doc->pdf; 2474 PopplerPage *page = NULL; 2475 int pn = args[1].value.natnum; 2476 double width, height; 2477 GList *link_map = NULL, *item; 2478 2479 page = poppler_document_get_page (doc, pn - 1); 2480 perror_if_not (page, "No such page %d", pn); 2481 poppler_page_get_size (page, &width, &height); 2482 link_map = poppler_page_get_link_mapping (page); 2483 2484 OK_BEGIN (); 2485 for (item = g_list_last (link_map); item; item = item->prev) 2486 { 2487 2488 PopplerLinkMapping *link = item->data; 2489 PopplerRectangle *r = &link->area; 2490 gdouble y1 = r->y1; 2491 /* LinkMappings have a different gravity. */ 2492 r->y1 = height - r->y2; 2493 r->y2 = height - y1; 2494 2495 if (! action_is_handled (link->action)) 2496 continue; 2497 2498 printf ("%f %f %f %f:", 2499 r->x1 / width, r->y1 / height, 2500 r->x2 / width, r->y2 / height); 2501 action_print (doc, link->action); 2502 } 2503 OK_END (); 2504 error: 2505 if (page) g_object_unref (page); 2506 if (link_map) poppler_page_free_link_mapping (link_map); 2507 } 2508 2509 /* Name: gettext 2510 Args: filename page edges selection-style 2511 Returns: The selection's text. 2512 Errors: If page is out of range. 2513 2514 For the selection-style argument see getselection command. 2515 */ 2516 2517 2518 const command_arg_type_t cmd_gettext_spec[] = 2519 { 2520 ARG_DOC, 2521 ARG_NATNUM, /* page number */ 2522 ARG_EDGES, /* selection */ 2523 ARG_NATNUM /* selection-style */ 2524 }; 2525 2526 static void 2527 cmd_gettext(const epdfinfo_t *ctx, const command_arg_t *args) 2528 { 2529 PopplerDocument *doc = args[0].value.doc->pdf; 2530 int pn = args[1].value.natnum; 2531 PopplerRectangle r = args[2].value.rectangle; 2532 int selection_style = args[3].value.natnum; 2533 PopplerPage *page = NULL; 2534 double width, height; 2535 gchar *text = NULL; 2536 2537 selection_style = xpoppler_validate_selection_style (selection_style); 2538 page = poppler_document_get_page (doc, pn - 1); 2539 perror_if_not (page, "No such page %d", pn); 2540 poppler_page_get_size (page, &width, &height); 2541 r.x1 = r.x1 * width; 2542 r.x2 = r.x2 * width; 2543 r.y1 = r.y1 * height; 2544 r.y2 = r.y2 * height; 2545 /* printf ("%f %f %f %f , %f %f\n", r.x1, r.y1, r.x2, r.y2, width, height); */ 2546 text = poppler_page_get_selected_text (page, selection_style, &r); 2547 2548 OK_BEGIN (); 2549 print_response_string (text, NEWLINE); 2550 OK_END (); 2551 2552 error: 2553 g_free (text); 2554 if (page) g_object_unref (page); 2555 } 2556 2557 /* Name: getselection 2558 Args: filename page edges selection-selection_style 2559 Returns: The selection's text. 2560 Errors: If page is out of range. 2561 2562 selection-selection_style should be as follows. 2563 2564 0 (POPPLER_SELECTION_GLYPH) 2565 glyph is the minimum unit for selection 2566 2567 1 (POPPLER_SELECTION_WORD) 2568 word is the minimum unit for selection 2569 2570 2 (POPPLER_SELECTION_LINE) 2571 line is the minimum unit for selection 2572 */ 2573 2574 2575 const command_arg_type_t cmd_getselection_spec[] = 2576 { 2577 ARG_DOC, 2578 ARG_NATNUM, /* page number */ 2579 ARG_EDGES, /* selection */ 2580 ARG_NATNUM /* selection-style */ 2581 }; 2582 2583 static void 2584 cmd_getselection (const epdfinfo_t *ctx, const command_arg_t *args) 2585 { 2586 PopplerDocument *doc = args[0].value.doc->pdf; 2587 int pn = args[1].value.natnum; 2588 PopplerRectangle r = args[2].value.rectangle; 2589 int selection_style = args[3].value.natnum; 2590 gdouble width, height; 2591 cairo_region_t *region = NULL; 2592 PopplerPage *page = NULL; 2593 int i; 2594 2595 selection_style = xpoppler_validate_selection_style (selection_style); 2596 page = poppler_document_get_page (doc, pn - 1); 2597 perror_if_not (page, "No such page %d", pn); 2598 poppler_page_get_size (page, &width, &height); 2599 2600 r.x1 = r.x1 * width; 2601 r.x2 = r.x2 * width; 2602 r.y1 = r.y1 * height; 2603 r.y2 = r.y2 * height; 2604 2605 region = poppler_page_get_selected_region (page, 1.0, selection_style, &r); 2606 2607 OK_BEGIN (); 2608 for (i = 0; i < cairo_region_num_rectangles (region); ++i) 2609 { 2610 cairo_rectangle_int_t r; 2611 2612 cairo_region_get_rectangle (region, i, &r); 2613 printf ("%f %f %f %f\n", 2614 r.x / width, 2615 r.y / height, 2616 (r.x + r.width) / width, 2617 (r.y + r.height) / height); 2618 } 2619 OK_END (); 2620 2621 error: 2622 if (region) cairo_region_destroy (region); 2623 if (page) g_object_unref (page); 2624 } 2625 2626 /* Name: pagesize 2627 Args: filename page 2628 Returns: width height 2629 Errors: If page is out of range. 2630 */ 2631 2632 2633 const command_arg_type_t cmd_pagesize_spec[] = 2634 { 2635 ARG_DOC, 2636 ARG_NATNUM /* page number */ 2637 }; 2638 2639 static void 2640 cmd_pagesize(const epdfinfo_t *ctx, const command_arg_t *args) 2641 { 2642 PopplerDocument *doc = args[0].value.doc->pdf; 2643 int pn = args[1].value.natnum; 2644 PopplerPage *page = NULL; 2645 double width, height; 2646 2647 2648 page = poppler_document_get_page (doc, pn - 1); 2649 perror_if_not (page, "No such page %d", pn); 2650 poppler_page_get_size (page, &width, &height); 2651 2652 OK_BEGIN (); 2653 printf ("%f:%f\n", width, height); 2654 OK_END (); 2655 2656 error: 2657 if (page) g_object_unref (page); 2658 } 2659 2660 /* Annotations */ 2661 2662 /* Name: getannots 2663 Args: filename firstpage lastpage 2664 Returns: The list of annotations of this page. 2665 2666 For all annotations 2667 2668 page edges type key flags color contents mod-date 2669 2670 ,where 2671 2672 name is a document-unique name, 2673 flags is PopplerAnnotFlag bitmask, 2674 color is 3-byte RGB hex number and 2675 2676 Then 2677 2678 label subject opacity popup-edges popup-is-open create-date 2679 2680 if this is a markup annotation and additionally 2681 2682 text-icon text-state 2683 2684 for markup text annotations. 2685 2686 Errors: If page is out of range. 2687 */ 2688 2689 2690 const command_arg_type_t cmd_getannots_spec[] = 2691 { 2692 ARG_DOC, 2693 ARG_NATNUM, /* first page */ 2694 ARG_NATNUM /* last page */ 2695 }; 2696 2697 static void 2698 cmd_getannots(const epdfinfo_t *ctx, const command_arg_t *args) 2699 { 2700 PopplerDocument *doc = args[0].value.doc->pdf; 2701 gint first = args[1].value.natnum; 2702 gint last = args[2].value.natnum; 2703 GList *list; 2704 gint pn; 2705 2706 first = MAX(1, first); 2707 if (last <= 0) 2708 last = poppler_document_get_n_pages (doc); 2709 else 2710 last = MIN(last, poppler_document_get_n_pages (doc)); 2711 2712 OK_BEGIN (); 2713 for (pn = first; pn <= last; ++pn) 2714 { 2715 GList *annots = annoation_get_for_page (args->value.doc, pn); 2716 PopplerPage *page = poppler_document_get_page (doc, pn - 1); 2717 2718 if (! page) 2719 continue; 2720 2721 for (list = annots; list; list = list->next) 2722 { 2723 annotation_t *annot = (annotation_t *)list->data; 2724 annotation_print (annot, page); 2725 } 2726 g_object_unref (page); 2727 } 2728 OK_END (); 2729 } 2730 2731 /* Name: getannot 2732 Args: filename name 2733 Returns: The annotation for name, see cmd_getannots. 2734 Errors: If no annotation named ,name' exists. 2735 */ 2736 2737 2738 const command_arg_type_t cmd_getannot_spec[] = 2739 { 2740 ARG_DOC, 2741 ARG_NONEMPTY_STRING, /* annotation's key */ 2742 }; 2743 2744 static void 2745 cmd_getannot (const epdfinfo_t *ctx, const command_arg_t *args) 2746 { 2747 document_t *doc = args->value.doc; 2748 const gchar *key = args[1].value.string; 2749 PopplerPage *page = NULL; 2750 annotation_t *a = annotation_get_by_key (doc, key); 2751 gint index; 2752 2753 perror_if_not (a, "No such annotation: %s", key); 2754 index = poppler_annot_get_page_index (a->amap->annot); 2755 if (index >= 0) 2756 page = poppler_document_get_page (doc->pdf, index); 2757 perror_if_not (page, "Unable to get page %d", index + 1); 2758 2759 OK_BEGIN (); 2760 annotation_print (a, page); 2761 OK_END (); 2762 2763 error: 2764 if (page) g_object_unref (page); 2765 } 2766 2767 /* Name: getannot_attachment 2768 Args: filename name [output-filename] 2769 Returns: name description size mtime ctime output-filename 2770 Errors: If no annotation named ,name' exists or output-filename is 2771 not writable. 2772 */ 2773 2774 2775 const command_arg_type_t cmd_getattachment_from_annot_spec[] = 2776 { 2777 ARG_DOC, 2778 ARG_NONEMPTY_STRING, /* annotation's name */ 2779 ARG_BOOL /* save attachment */ 2780 }; 2781 2782 static void 2783 cmd_getattachment_from_annot (const epdfinfo_t *ctx, const command_arg_t *args) 2784 { 2785 document_t *doc = args->value.doc; 2786 const gchar *key = args[1].value.string; 2787 gboolean do_save = args[2].value.flag; 2788 PopplerAttachment *att = NULL; 2789 annotation_t *a = annotation_get_by_key (doc, key); 2790 gchar *id = NULL; 2791 2792 perror_if_not (a, "No such annotation: %s", key); 2793 perror_if_not (POPPLER_IS_ANNOT_FILE_ATTACHMENT (a->amap->annot), 2794 "Not a file annotation: %s", key); 2795 att = poppler_annot_file_attachment_get_attachment 2796 (POPPLER_ANNOT_FILE_ATTACHMENT (a->amap->annot)); 2797 perror_if_not (att, "Unable to get attachment: %s", key); 2798 id = g_strdup_printf ("attachment-%s", key); 2799 2800 OK_BEGIN (); 2801 attachment_print (att, id, do_save); 2802 OK_END (); 2803 2804 error: 2805 if (att) g_object_unref (att); 2806 if (id) g_free (id); 2807 } 2808 2809 2810 /* document-level attachments */ 2811 const command_arg_type_t cmd_getattachments_spec[] = 2812 { 2813 ARG_DOC, 2814 ARG_BOOL, /* save attachments */ 2815 }; 2816 2817 static void 2818 cmd_getattachments (const epdfinfo_t *ctx, const command_arg_t *args) 2819 { 2820 document_t *doc = args->value.doc; 2821 gboolean do_save = args[1].value.flag; 2822 GList *item; 2823 GList *attmnts = poppler_document_get_attachments (doc->pdf); 2824 int i; 2825 2826 OK_BEGIN (); 2827 for (item = attmnts, i = 0; item; item = item->next, ++i) 2828 { 2829 PopplerAttachment *att = (PopplerAttachment*) item->data; 2830 gchar *id = g_strdup_printf ("attachment-document-%d", i); 2831 2832 attachment_print (att, id, do_save); 2833 g_object_unref (att); 2834 g_free (id); 2835 } 2836 g_list_free (attmnts); 2837 2838 OK_END (); 2839 } 2840 2841 #ifdef HAVE_POPPLER_ANNOT_WRITE 2842 2843 const command_arg_type_t cmd_addannot_spec[] = 2844 { 2845 ARG_DOC, 2846 ARG_NATNUM, /* page number */ 2847 ARG_STRING, /* type */ 2848 ARG_NATNUM, /* selection-style */ 2849 ARG_EDGES_OR_POSITION, /* edges or position (uses default size) */ 2850 ARG_REST, /* markup regions */ 2851 }; 2852 2853 static void 2854 cmd_addannot (const epdfinfo_t *ctx, const command_arg_t *args) 2855 { 2856 2857 document_t *doc = args->value.doc; 2858 gint pn = args[1].value.natnum; 2859 const char *type_string = args[2].value.string; 2860 int selection_style = args[3].value.natnum; 2861 PopplerRectangle r = args[4].value.rectangle; 2862 int i; 2863 PopplerPage *page = NULL; 2864 double width, height; 2865 PopplerAnnot *pa; 2866 PopplerAnnotMapping *amap; 2867 annotation_t *a; 2868 gchar *key; 2869 GList *annotations; 2870 gdouble y2; 2871 char *error_msg = NULL; 2872 2873 selection_style = xpoppler_validate_selection_style (selection_style); 2874 page = poppler_document_get_page (doc->pdf, pn - 1); 2875 perror_if_not (page, "Unable to get page %d", pn); 2876 poppler_page_get_size (page, &width, &height); 2877 r.x1 *= width; r.x2 *= width; 2878 r.y1 *= height; r.y2 *= height; 2879 if (r.y2 < 0) 2880 r.y2 = r.y1 + 24; 2881 if (r.x2 < 0) 2882 r.x2 = r.x1 + 24; 2883 y2 = r.y2; 2884 r.y2 = height - r.y1; 2885 r.y1 = height - y2; 2886 2887 pa = annotation_new (ctx, doc, page, type_string, selection_style, &r, &args[5], 2888 &error_msg); 2889 perror_if_not (pa, "Creating annotation failed: %s", 2890 error_msg ? error_msg : "Reason unknown"); 2891 amap = poppler_annot_mapping_new (); 2892 amap->area = r; 2893 amap->annot = pa; 2894 annotations = annoation_get_for_page (doc, pn); 2895 2896 i = g_list_length (annotations); 2897 key = g_strdup_printf ("annot-%d-%d", pn, i); 2898 while (g_hash_table_lookup (doc->annotations.keys, key)) 2899 { 2900 g_free (key); 2901 key = g_strdup_printf ("annot-%d-%d", pn, ++i); 2902 } 2903 a = g_malloc (sizeof (annotation_t)); 2904 a->amap = amap; 2905 a->key = key; 2906 doc->annotations.pages[pn - 1] = 2907 g_list_prepend (annotations, a); 2908 g_hash_table_insert (doc->annotations.keys, key, a); 2909 poppler_page_add_annot (page, pa); 2910 OK_BEGIN (); 2911 annotation_print (a, page); 2912 OK_END (); 2913 2914 error: 2915 if (page) g_object_unref (page); 2916 if (error_msg) g_free (error_msg); 2917 } 2918 2919 2920 const command_arg_type_t cmd_delannot_spec[] = 2921 { 2922 ARG_DOC, 2923 ARG_NONEMPTY_STRING /* Annotation's key */ 2924 }; 2925 2926 static void 2927 cmd_delannot (const epdfinfo_t *ctx, const command_arg_t *args) 2928 { 2929 document_t *doc = args->value.doc; 2930 const gchar *key = args[1].value.string; 2931 PopplerPage *page = NULL; 2932 annotation_t *a = annotation_get_by_key (doc, key); 2933 gint pn; 2934 2935 perror_if_not (a, "No such annotation: %s", key); 2936 pn = poppler_annot_get_page_index (a->amap->annot) + 1; 2937 if (pn >= 1) 2938 page = poppler_document_get_page (doc->pdf, pn - 1); 2939 perror_if_not (page, "Unable to get page %d", pn); 2940 poppler_page_remove_annot (page, a->amap->annot); 2941 doc->annotations.pages[pn - 1] = 2942 g_list_remove (doc->annotations.pages[pn - 1], a); 2943 g_hash_table_remove (doc->annotations.keys, a->key); 2944 poppler_annot_mapping_free(a->amap); 2945 OK (); 2946 2947 error: 2948 if (a) 2949 { 2950 g_free (a->key); 2951 g_free (a); 2952 } 2953 if (page) g_object_unref (page); 2954 } 2955 2956 const command_arg_type_t cmd_editannot_spec[] = 2957 { 2958 ARG_DOC, 2959 ARG_NONEMPTY_STRING, /* annotation key */ 2960 ARG_REST /* (KEY VALUE ...) */ 2961 }; 2962 2963 static void 2964 cmd_editannot (const epdfinfo_t *ctx, const command_arg_t *args) 2965 { 2966 document_t *doc = args->value.doc; 2967 const char *key = args[1].value.string; 2968 int nrest_args = args[2].value.rest.nargs; 2969 char * const *rest_args = args[2].value.rest.args; 2970 annotation_t *a = annotation_get_by_key (doc, key); 2971 PopplerAnnot *pa; 2972 PopplerPage *page = NULL; 2973 int i = 0; 2974 gint index; 2975 char *error_msg = NULL; 2976 command_arg_t carg; 2977 const char *unexpected_parse_error = "Internal error while parsing arg `%s'"; 2978 2979 perror_if_not (a, "No such annotation: %s", key); 2980 pa = a->amap->annot; 2981 perror_if_not (annotation_edit_validate (ctx, &args[2], pa, &error_msg), 2982 "%s", error_msg); 2983 index = poppler_annot_get_page_index (pa); 2984 page = poppler_document_get_page (doc->pdf, index); 2985 perror_if_not (page, "Unable to get page %d for annotation", index); 2986 2987 for (i = 0; i < nrest_args; ++i) 2988 { 2989 const char *key = rest_args[i++]; 2990 2991 if (! strcmp (key, "flags")) 2992 { 2993 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2994 ARG_NATNUM, NULL), 2995 unexpected_parse_error, rest_args[i]); 2996 poppler_annot_set_flags (pa, carg.value.natnum); 2997 } 2998 else if (! strcmp (key, "color")) 2999 { 3000 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3001 ARG_COLOR, NULL), 3002 unexpected_parse_error, rest_args[i]); 3003 poppler_annot_set_color (pa, &carg.value.color); 3004 } 3005 else if (! strcmp (key, "contents")) 3006 { 3007 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3008 ARG_STRING, NULL), 3009 unexpected_parse_error, rest_args[i]); 3010 poppler_annot_set_contents (pa, carg.value.string); 3011 } 3012 else if (! strcmp (key, "edges")) 3013 { 3014 PopplerRectangle *area = &a->amap->area; 3015 gdouble width, height; 3016 PopplerRectangle r; 3017 3018 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3019 ARG_EDGES_OR_POSITION, NULL), 3020 unexpected_parse_error, rest_args[i]); 3021 r = carg.value.rectangle; 3022 poppler_page_get_size (page, &width, &height); 3023 3024 /* Translate Gravity and maybe keep the width and height. */ 3025 if (r.x2 < 0) 3026 area->x2 += (r.x1 * width) - area->x1; 3027 else 3028 area->x2 = r.x2 * width; 3029 3030 if (r.y2 < 0) 3031 area->y1 -= (r.y1 * height) - (height - area->y2); 3032 else 3033 area->y1 = height - (r.y2 * height); 3034 3035 area->x1 = r.x1 * width; 3036 area->y2 = height - (r.y1 * height); 3037 3038 poppler_annot_set_rectangle (pa, area); 3039 } 3040 else if (! strcmp (key, "label")) 3041 { 3042 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 3043 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3044 ARG_STRING, NULL), 3045 unexpected_parse_error, rest_args[i]); 3046 poppler_annot_markup_set_label (ma, carg.value.string); 3047 } 3048 else if (! strcmp (key, "opacity")) 3049 { 3050 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 3051 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3052 ARG_EDGE, NULL), 3053 unexpected_parse_error, rest_args[i]); 3054 poppler_annot_markup_set_opacity (ma, carg.value.edge); 3055 } 3056 else if (! strcmp (key, "popup")) 3057 { 3058 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 3059 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3060 ARG_EDGES, NULL), 3061 unexpected_parse_error, rest_args[i]); 3062 poppler_annot_markup_set_popup (ma, &carg.value.rectangle); 3063 } 3064 else if (! strcmp (key, "popup-is-open")) 3065 { 3066 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 3067 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3068 ARG_BOOL, NULL), 3069 unexpected_parse_error, rest_args[i]); 3070 poppler_annot_markup_set_popup_is_open (ma, carg.value.flag); 3071 } 3072 else if (! strcmp (key, "icon")) 3073 { 3074 PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa); 3075 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3076 ARG_STRING, NULL), 3077 unexpected_parse_error, rest_args[i]); 3078 poppler_annot_text_set_icon (ta, carg.value.string); 3079 } 3080 else if (! strcmp (key, "is-open")) 3081 { 3082 PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa); 3083 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3084 ARG_BOOL, NULL), 3085 unexpected_parse_error, rest_args[i]); 3086 poppler_annot_text_set_is_open (ta, carg.value.flag); 3087 } 3088 else 3089 { 3090 perror_if_not (0, "internal error: annotation property validation failed"); 3091 } 3092 } 3093 3094 OK_BEGIN (); 3095 annotation_print (a, page); 3096 OK_END (); 3097 3098 error: 3099 if (error_msg) g_free (error_msg); 3100 if (page) g_object_unref (page); 3101 } 3102 3103 const command_arg_type_t cmd_save_spec[] = 3104 { 3105 ARG_DOC, 3106 }; 3107 3108 static void 3109 cmd_save (const epdfinfo_t *ctx, const command_arg_t *args) 3110 { 3111 document_t *doc = args->value.doc; 3112 char *filename = mktempfile (); 3113 GError *gerror = NULL; 3114 gchar *uri; 3115 gboolean success = FALSE; 3116 3117 if (!filename) 3118 { 3119 printf_error_response ("Unable to create temporary file"); 3120 return; 3121 } 3122 3123 uri = g_filename_to_uri (filename, NULL, &gerror); 3124 3125 if (uri) 3126 { 3127 success = poppler_document_save (doc->pdf, uri, &gerror); 3128 g_free (uri); 3129 } 3130 if (! success) 3131 { 3132 printf_error_response ("Error while saving %s:%s" 3133 , filename, gerror ? gerror->message : "?"); 3134 if (gerror) 3135 g_error_free (gerror); 3136 return; 3137 } 3138 OK_BEGIN (); 3139 print_response_string (filename, NEWLINE); 3140 OK_END (); 3141 } 3142 3143 #endif /* HAVE_POPPLER_ANNOT_WRITE */ 3144 3145 3146 const command_arg_type_t cmd_synctex_forward_search_spec[] = 3147 { 3148 ARG_DOC, 3149 ARG_NONEMPTY_STRING, /* source file */ 3150 ARG_NATNUM, /* line number */ 3151 ARG_NATNUM /* column number */ 3152 }; 3153 3154 static void 3155 cmd_synctex_forward_search (const epdfinfo_t *ctx, const command_arg_t *args) 3156 { 3157 document_t *doc = args[0].value.doc; 3158 const char *source = args[1].value.string; 3159 int line = args[2].value.natnum; 3160 int column = args[3].value.natnum; 3161 synctex_scanner_p scanner = NULL; 3162 synctex_node_p node; 3163 float x1, y1, x2, y2; 3164 PopplerPage *page = NULL; 3165 double width, height; 3166 int pn; 3167 3168 scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1); 3169 perror_if_not (scanner, "Unable to create synctex scanner,\ 3170 did you run latex with `--synctex=1' ?"); 3171 3172 perror_if_not (synctex_display_query (scanner, source, line, column, 0) 3173 && (node = synctex_scanner_next_result (scanner)), 3174 "Destination not found"); 3175 3176 pn = synctex_node_page (node); 3177 page = poppler_document_get_page(doc->pdf, pn - 1); 3178 perror_if_not (page, "Page not found"); 3179 x1 = synctex_node_box_visible_h (node); 3180 y1 = synctex_node_box_visible_v (node) 3181 - synctex_node_box_visible_height (node); 3182 x2 = synctex_node_box_visible_width (node) + x1; 3183 y2 = synctex_node_box_visible_depth (node) 3184 + synctex_node_box_visible_height (node) + y1; 3185 poppler_page_get_size (page, &width, &height); 3186 x1 /= width; 3187 y1 /= height; 3188 x2 /= width; 3189 y2 /= height; 3190 3191 OK_BEGIN (); 3192 printf("%d:%f:%f:%f:%f\n", pn, x1, y1, x2, y2); 3193 OK_END (); 3194 3195 error: 3196 if (page) g_object_unref (page); 3197 if (scanner) synctex_scanner_free (scanner); 3198 } 3199 3200 3201 const command_arg_type_t cmd_synctex_backward_search_spec[] = 3202 { 3203 ARG_DOC, 3204 ARG_NATNUM, /* page number */ 3205 ARG_EDGE, /* x */ 3206 ARG_EDGE /* y */ 3207 }; 3208 3209 static void 3210 cmd_synctex_backward_search (const epdfinfo_t *ctx, const command_arg_t *args) 3211 { 3212 document_t *doc = args[0].value.doc; 3213 int pn = args[1].value.natnum; 3214 double x = args[2].value.edge; 3215 double y = args[3].value.edge; 3216 synctex_scanner_p scanner = NULL; 3217 const char *filename; 3218 PopplerPage *page = NULL; 3219 synctex_node_p node; 3220 double width, height; 3221 int line, column; 3222 3223 scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1); 3224 perror_if_not (scanner, "Unable to create synctex scanner,\ 3225 did you run latex with `--synctex=1' ?"); 3226 3227 page = poppler_document_get_page(doc->pdf, pn - 1); 3228 perror_if_not (page, "Page not found"); 3229 poppler_page_get_size (page, &width, &height); 3230 x = x * width; 3231 y = y * height; 3232 3233 if (! synctex_edit_query (scanner, pn, x, y) 3234 || ! (node = synctex_scanner_next_result (scanner)) 3235 || ! (filename = 3236 synctex_scanner_get_name (scanner, synctex_node_tag (node)))) 3237 { 3238 printf_error_response ("Destination not found"); 3239 goto error; 3240 } 3241 3242 line = synctex_node_line (node); 3243 column = synctex_node_column (node); 3244 3245 OK_BEGIN (); 3246 print_response_string (filename, COLON); 3247 printf("%d:%d\n", line, column); 3248 OK_END (); 3249 3250 error: 3251 if (page) g_object_unref (page); 3252 if (scanner) synctex_scanner_free (scanner); 3253 } 3254 3255 3256 const command_arg_type_t cmd_renderpage_spec[] = 3257 { 3258 ARG_DOC, 3259 ARG_NATNUM, /* page number */ 3260 ARG_NATNUM, /* width */ 3261 ARG_REST, /* commands */ 3262 }; 3263 3264 static void 3265 cmd_renderpage (const epdfinfo_t *ctx, const command_arg_t *args) 3266 { 3267 document_t *doc = args[0].value.doc; 3268 int pn = args[1].value.natnum; 3269 int width = args[2].value.natnum; 3270 int nrest_args = args[3].value.rest.nargs; 3271 char * const *rest_args = args[3].value.rest.args; 3272 PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1); 3273 cairo_surface_t *surface = NULL; 3274 cairo_t *cr = NULL; 3275 command_arg_t rest_arg; 3276 gchar *error_msg = NULL; 3277 double pt_width, pt_height; 3278 PopplerColor fg = { 0, 0, 0 }; 3279 PopplerColor bg = { 65535, 0, 0 }; 3280 double alpha = 1.0; 3281 double line_width = 1.5; 3282 PopplerSelectionStyle selection_style = POPPLER_SELECTION_GLYPH; 3283 PopplerRectangle cb = {0.0, 0.0, 1.0, 1.0}; 3284 int i = 0; 3285 3286 perror_if_not (page, "No such page %d", pn); 3287 poppler_page_get_size (page, &pt_width, &pt_height); 3288 surface = image_render_page (doc->pdf, page, width, 1, 3289 &doc->options.render); 3290 perror_if_not (surface, "Failed to render page %d", pn); 3291 3292 if (! nrest_args) 3293 goto theend; 3294 3295 cr = cairo_create (surface); 3296 cairo_scale (cr, width / pt_width, width / pt_width); 3297 3298 while (i < nrest_args) 3299 { 3300 const char* keyword; 3301 3302 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3303 ARG_STRING, &error_msg), 3304 "%s", error_msg); 3305 keyword = rest_arg.value.string; 3306 ++i; 3307 3308 perror_if_not (i < nrest_args, "Keyword is `%s' missing an argument", 3309 keyword); 3310 3311 if (! strcmp (keyword, ":foreground") 3312 || ! strcmp (keyword, ":background")) 3313 { 3314 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3315 ARG_COLOR, &error_msg), 3316 "%s", error_msg); 3317 ++i; 3318 if (! strcmp (keyword, ":foreground")) 3319 fg = rest_arg.value.color; 3320 else 3321 bg = rest_arg.value.color; 3322 3323 } 3324 else if (! strcmp (keyword, ":alpha")) 3325 { 3326 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3327 ARG_EDGE, &error_msg), 3328 "%s", error_msg); 3329 ++i; 3330 alpha = rest_arg.value.edge; 3331 } 3332 else if (! strcmp (keyword, ":crop-to") 3333 || ! strcmp (keyword, ":highlight-region") 3334 || ! strcmp (keyword, ":highlight-text") 3335 || ! strcmp (keyword, ":highlight-line")) 3336 { 3337 PopplerRectangle *r; 3338 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3339 ARG_EDGES, &error_msg), 3340 "%s", error_msg); 3341 3342 ++i; 3343 r = &rest_arg.value.rectangle; 3344 3345 if (! strcmp (keyword, ":crop-to")) 3346 { 3347 gdouble w = (cb.x2 - cb.x1); 3348 gdouble h = (cb.y2 - cb.y1); 3349 gdouble x1 = cb.x1; 3350 gdouble y1 = cb.y1; 3351 3352 cb.x1 = r->x1 * w + x1; 3353 cb.x2 = r->x2 * w + x1; 3354 cb.y1 = r->y1 * h + y1; 3355 cb.y2 = r->y2 * h + y1; 3356 3357 } 3358 else 3359 { 3360 r->x1 = pt_width * r->x1 * (cb.x2 - cb.x1) + pt_width * cb.x1; 3361 r->x2 = pt_width * r->x2 * (cb.x2 - cb.x1) + pt_width * cb.x1; 3362 r->y1 = pt_height * r->y1 * (cb.y2 - cb.y1) + pt_height * cb.y1; 3363 r->y2 = pt_height * r->y2 * (cb.y2 - cb.y1) + pt_height * cb.y1; 3364 3365 if (! strcmp (keyword, ":highlight-region")) 3366 { 3367 const double deg = M_PI / 180.0; 3368 double rad; 3369 3370 r->x1 += line_width / 2; 3371 r->x2 -= line_width / 2; 3372 r->y1 += line_width / 2; 3373 r->y2 -= line_width / 2; 3374 3375 rad = MIN (5, MIN (r->x2 - r->x1, r->y2 - r->y1) / 2.0); 3376 3377 cairo_move_to (cr, r->x1 , r->y1 + rad); 3378 cairo_arc (cr, r->x1 + rad, r->y1 + rad, rad, 180 * deg, 270 * deg); 3379 cairo_arc (cr, r->x2 - rad, r->y1 + rad, rad, 270 * deg, 360 * deg); 3380 cairo_arc (cr, r->x2 - rad, r->y2 - rad, rad, 0 * deg, 90 * deg); 3381 cairo_arc (cr, r->x1 + rad, r->y2 - rad, rad, 90 * deg, 180 * deg); 3382 cairo_close_path (cr); 3383 3384 cairo_set_source_rgba (cr, 3385 bg.red / 65535.0, 3386 bg.green / 65535.0, 3387 bg.blue / 65535.0, alpha); 3388 cairo_fill_preserve (cr); 3389 cairo_set_source_rgba (cr, 3390 fg.red / 65535.0, 3391 fg.green / 65535.0, 3392 fg.blue / 65535.0, 1.0); 3393 cairo_set_line_width (cr, line_width); 3394 cairo_stroke (cr); 3395 } 3396 else 3397 { 3398 gboolean is_single_line = ! strcmp (keyword, ":highlight-line"); 3399 3400 if (is_single_line) 3401 { 3402 gdouble m = r->y1 + (r->y2 - r->y1) / 2; 3403 3404 /* Make the rectangle flat, otherwise poppler frequently 3405 renders neighboring lines.*/ 3406 r->y1 = m; 3407 r->y2 = m; 3408 } 3409 3410 poppler_page_render_selection (page, cr, r, NULL, 3411 selection_style, &fg, &bg); 3412 } 3413 } 3414 } 3415 else if (! strcmp (keyword, ":selection-style")) 3416 { 3417 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3418 ARG_NATNUM, &error_msg), 3419 "%s", error_msg); 3420 ++i; 3421 selection_style = xpoppler_validate_selection_style (rest_arg.value.natnum); 3422 } 3423 else 3424 perror_if_not (0, "Unknown render command: %s", keyword); 3425 } 3426 if (cb.x1 != 0 || cb.y1 != 0 || cb.x2 != 1 || cb.y2 != 1) 3427 { 3428 int height = cairo_image_surface_get_height (surface); 3429 cairo_rectangle_int_t r = {(int) (width * cb.x1 + 0.5), 3430 (int) (height * cb.y1 + 0.5), 3431 (int) (width * (cb.x2 - cb.x1) + 0.5), 3432 (int) (height * (cb.y2 - cb.y1) + 0.5)}; 3433 cairo_surface_t *nsurface = 3434 cairo_image_surface_create (CAIRO_FORMAT_ARGB32, r.width, r.height); 3435 perror_if_not (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS, 3436 "%s", "Failed to create cairo surface"); 3437 cairo_destroy (cr); 3438 cr = cairo_create (nsurface); 3439 perror_if_not (cairo_status (cr) == CAIRO_STATUS_SUCCESS, 3440 "%s", "Failed to create cairo context"); 3441 cairo_set_source_surface (cr, surface, -r.x, -r.y); 3442 cairo_paint (cr); 3443 cairo_surface_destroy (surface); 3444 surface = nsurface; 3445 } 3446 3447 theend: 3448 image_write_print_response (surface, PNG); 3449 3450 error: 3451 if (error_msg) g_free (error_msg); 3452 if (cr) cairo_destroy (cr); 3453 if (surface) cairo_surface_destroy (surface); 3454 if (page) g_object_unref (page); 3455 } 3456 3457 const command_arg_type_t cmd_boundingbox_spec[] = 3458 { 3459 ARG_DOC, 3460 ARG_NATNUM, /* page number */ 3461 /* region */ 3462 }; 3463 3464 static void 3465 cmd_boundingbox (const epdfinfo_t *ctx, const command_arg_t *args) 3466 { 3467 document_t *doc = args[0].value.doc; 3468 int pn = args[1].value.natnum; 3469 PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1); 3470 cairo_surface_t *surface = NULL; 3471 int width, height; 3472 double pt_width, pt_height; 3473 unsigned char *data, *data_p; 3474 PopplerRectangle bbox; 3475 int i, j; 3476 3477 perror_if_not (page, "No such page %d", pn); 3478 poppler_page_get_size (page, &pt_width, &pt_height); 3479 surface = image_render_page (doc->pdf, page, (int) pt_width, 1, 3480 &doc->options.render); 3481 3482 perror_if_not (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS, 3483 "Failed to render page"); 3484 3485 width = cairo_image_surface_get_width (surface); 3486 height = cairo_image_surface_get_height (surface); 3487 data = cairo_image_surface_get_data (surface); 3488 3489 /* Determine the bbox by comparing each pixel in the 4 corner 3490 stripes with the origin. */ 3491 for (i = 0; i < width; ++i) 3492 { 3493 data_p = data + 4 * i; 3494 for (j = 0; j < height; ++j, data_p += 4 * width) 3495 { 3496 if (! ARGB_EQUAL (data, data_p)) 3497 break; 3498 } 3499 if (j < height) 3500 break; 3501 } 3502 bbox.x1 = i; 3503 3504 for (i = width - 1; i > -1; --i) 3505 { 3506 data_p = data + 4 * i; 3507 for (j = 0; j < height; ++j, data_p += 4 * width) 3508 { 3509 if (! ARGB_EQUAL (data, data_p)) 3510 break; 3511 } 3512 if (j < height) 3513 break; 3514 } 3515 bbox.x2 = i + 1; 3516 3517 for (i = 0; i < height; ++i) 3518 { 3519 data_p = data + 4 * i * width; 3520 for (j = 0; j < width; ++j, data_p += 4) 3521 { 3522 if (! ARGB_EQUAL (data, data_p)) 3523 break; 3524 } 3525 if (j < width) 3526 break; 3527 } 3528 bbox.y1 = i; 3529 3530 for (i = height - 1; i > -1; --i) 3531 { 3532 data_p = data + 4 * i * width; 3533 for (j = 0; j < width; ++j, data_p += 4) 3534 { 3535 if (! ARGB_EQUAL (data, data_p)) 3536 break; 3537 } 3538 if (j < width) 3539 break; 3540 } 3541 bbox.y2 = i + 1; 3542 3543 OK_BEGIN (); 3544 if (bbox.x1 >= bbox.x2 || bbox.y1 >= bbox.y2) 3545 { 3546 /* empty page */ 3547 puts ("0:0:1:1"); 3548 } 3549 else 3550 { 3551 printf ("%f:%f:%f:%f\n", 3552 bbox.x1 / width, 3553 bbox.y1 / height, 3554 bbox.x2 / width, 3555 bbox.y2 / height); 3556 } 3557 OK_END (); 3558 3559 error: 3560 if (surface) cairo_surface_destroy (surface); 3561 if (page) g_object_unref (page); 3562 } 3563 3564 const command_arg_type_t cmd_charlayout_spec[] = 3565 { 3566 ARG_DOC, 3567 ARG_NATNUM, /* page number */ 3568 ARG_EDGES_OR_POSITION, /* region or position */ 3569 }; 3570 3571 static void 3572 cmd_charlayout(const epdfinfo_t *ctx, const command_arg_t *args) 3573 { 3574 PopplerDocument *doc = args[0].value.doc->pdf; 3575 int pn = args[1].value.natnum; 3576 PopplerRectangle region = args[2].value.rectangle; 3577 double width, height; 3578 PopplerPage *page = poppler_document_get_page(doc, pn - 1); 3579 char *text = NULL; 3580 char *text_p; 3581 PopplerRectangle *rectangles = NULL; 3582 guint nrectangles; 3583 int i; 3584 gboolean have_position = region.y2 < 0; 3585 3586 perror_if_not (page, "No such page %d", pn); 3587 3588 text = poppler_page_get_text (page); 3589 text_p = text; 3590 poppler_page_get_text_layout (page, &rectangles, &nrectangles); 3591 poppler_page_get_size (page, &width, &height); 3592 region.x1 *= width; 3593 region.x2 *= width; 3594 region.y1 *= height; 3595 region.y2 *= height; 3596 3597 OK_BEGIN (); 3598 for (i = 0; i < nrectangles && *text_p; ++i) 3599 { 3600 PopplerRectangle *r = &rectangles[i]; 3601 char *nextc = g_utf8_offset_to_pointer (text_p, 1); 3602 3603 if ((have_position 3604 && region.x1 >= r->x1 3605 && region.x1 <= r->x2 3606 && region.y1 >= r->y1 3607 && region.y1 <= r->y2) 3608 || (! have_position 3609 && r->x1 >= region.x1 3610 && r->y1 >= region.y1 3611 && r->x2 <= region.x2 3612 && r->y2 <= region.y2)) 3613 { 3614 char endc = *nextc; 3615 3616 printf ("%f %f %f %f:", 3617 r->x1 / width, r->y1 / height, 3618 r->x2 / width, r->y2 / height); 3619 *nextc = '\0'; 3620 print_response_string (text_p, NEWLINE); 3621 *nextc = endc; 3622 } 3623 text_p = nextc; 3624 } 3625 OK_END (); 3626 3627 g_free (rectangles); 3628 g_object_unref (page); 3629 g_free (text); 3630 3631 error: 3632 return; 3633 } 3634 3635 const document_option_t document_options [] = 3636 { 3637 DEC_DOPT (":render/usecolors", ARG_NATNUM, render.usecolors), 3638 DEC_DOPT (":render/printed", ARG_BOOL, render.printed), 3639 DEC_DOPT (":render/foreground", ARG_COLOR, render.fg), 3640 DEC_DOPT (":render/background", ARG_COLOR, render.bg), 3641 }; 3642 3643 const command_arg_type_t cmd_getoptions_spec[] = 3644 { 3645 ARG_DOC, 3646 }; 3647 3648 static void 3649 cmd_getoptions(const epdfinfo_t *ctx, const command_arg_t *args) 3650 { 3651 document_t *doc = args[0].value.doc; 3652 int i; 3653 OK_BEGIN (); 3654 for (i = 0; i < G_N_ELEMENTS (document_options); ++i) 3655 { 3656 command_arg_t arg; 3657 3658 arg.type = document_options[i].type; 3659 memcpy (&arg.value, 3660 ((char*) &doc->options) + document_options[i].offset, 3661 command_arg_type_size (arg.type)); 3662 print_response_string (document_options[i].name, COLON); 3663 command_arg_print (&arg); 3664 puts(""); 3665 } 3666 OK_END (); 3667 } 3668 3669 const command_arg_type_t cmd_setoptions_spec[] = 3670 { 3671 ARG_DOC, 3672 ARG_REST /* key value pairs */ 3673 }; 3674 3675 static void 3676 cmd_setoptions(const epdfinfo_t *ctx, const command_arg_t *args) 3677 { 3678 int i = 0; 3679 document_t *doc = args[0].value.doc; 3680 int nrest = args[1].value.rest.nargs; 3681 char * const *rest = args[1].value.rest.args; 3682 gchar *error_msg = NULL; 3683 document_options_t opts = doc->options; 3684 const size_t nopts = G_N_ELEMENTS (document_options); 3685 3686 perror_if_not (nrest % 2 == 0, "Even number of key/value pairs expected"); 3687 3688 while (i < nrest) 3689 { 3690 int j; 3691 command_arg_t key, value; 3692 3693 perror_if_not (command_arg_parse_arg 3694 (ctx, rest[i], &key, ARG_NONEMPTY_STRING, &error_msg), 3695 "%s", error_msg); 3696 3697 ++i; 3698 for (j = 0; j < nopts; ++j) 3699 { 3700 const document_option_t *dopt = &document_options[j]; 3701 if (! strcmp (key.value.string, dopt->name)) 3702 { 3703 perror_if_not (command_arg_parse_arg 3704 (ctx, rest[i], &value, dopt->type, &error_msg), 3705 "%s", error_msg); 3706 memcpy (((char*) &opts) + dopt->offset, 3707 &value.value, command_arg_type_size (value.type)); 3708 break; 3709 } 3710 } 3711 perror_if_not (j < nopts, "Unknown option: %s", key.value.string); 3712 ++i; 3713 } 3714 doc->options = opts; 3715 cmd_getoptions (ctx, args); 3716 3717 error: 3718 if (error_msg) g_free (error_msg); 3719 } 3720 3721 const command_arg_type_t cmd_pagelabels_spec[] = 3722 { 3723 ARG_DOC, 3724 }; 3725 3726 static void 3727 cmd_pagelabels(const epdfinfo_t *ctx, const command_arg_t *args) 3728 { 3729 PopplerDocument *doc = args[0].value.doc->pdf; 3730 int i; 3731 3732 OK_BEGIN (); 3733 for (i = 0; i < poppler_document_get_n_pages (doc); ++i) 3734 { 3735 PopplerPage *page = poppler_document_get_page(doc, i); 3736 gchar *label = poppler_page_get_label (page); 3737 3738 print_response_string (label ? label : "", NEWLINE); 3739 g_object_unref (page); 3740 g_free (label); 3741 } 3742 OK_END (); 3743 } 3744 3745 const command_arg_type_t cmd_ping_spec[] = 3746 { 3747 ARG_STRING /* any message */ 3748 }; 3749 3750 static void 3751 cmd_ping (const epdfinfo_t *ctx, const command_arg_t *args) 3752 { 3753 const gchar *msg = args[0].value.string; 3754 OK_BEGIN (); 3755 print_response_string (msg, NEWLINE); 3756 OK_END (); 3757 } 3758 3759 3760 /* ================================================================== * 3761 * Main 3762 * ================================================================== */ 3763 3764 static const command_t commands [] = 3765 { 3766 /* Basic */ 3767 DEC_CMD (ping), 3768 DEC_CMD (features), 3769 DEC_CMD (open), 3770 DEC_CMD (close), 3771 DEC_CMD (quit), 3772 DEC_CMD (getoptions), 3773 DEC_CMD (setoptions), 3774 3775 /* Searching */ 3776 DEC_CMD2 (search_string, "search-string"), 3777 DEC_CMD2 (search_regexp, "search-regexp"), 3778 DEC_CMD2 (regexp_flags, "regexp-flags"), 3779 3780 /* General Information */ 3781 DEC_CMD (metadata), 3782 DEC_CMD (outline), 3783 DEC_CMD2 (number_of_pages, "number-of-pages"), 3784 DEC_CMD (pagelinks), 3785 DEC_CMD (gettext), 3786 DEC_CMD (getselection), 3787 DEC_CMD (pagesize), 3788 DEC_CMD (boundingbox), 3789 DEC_CMD (charlayout), 3790 3791 /* General Information */ 3792 DEC_CMD (metadata), 3793 DEC_CMD (outline), 3794 DEC_CMD2 (number_of_pages, "number-of-pages"), 3795 DEC_CMD (pagelinks), 3796 DEC_CMD (gettext), 3797 DEC_CMD (getselection), 3798 DEC_CMD (pagesize), 3799 DEC_CMD (boundingbox), 3800 DEC_CMD (charlayout), 3801 DEC_CMD (pagelabels), 3802 3803 /* Annotations */ 3804 DEC_CMD (getannots), 3805 DEC_CMD (getannot), 3806 #ifdef HAVE_POPPLER_ANNOT_WRITE 3807 DEC_CMD (addannot), 3808 DEC_CMD (delannot), 3809 DEC_CMD (editannot), 3810 DEC_CMD (save), 3811 #endif 3812 3813 /* Attachments */ 3814 DEC_CMD2 (getattachment_from_annot, "getattachment-from-annot"), 3815 DEC_CMD (getattachments), 3816 3817 /* Synctex */ 3818 DEC_CMD2 (synctex_forward_search, "synctex-forward-search"), 3819 DEC_CMD2 (synctex_backward_search, "synctex-backward-search"), 3820 3821 /* Rendering */ 3822 DEC_CMD (renderpage), 3823 }; 3824 3825 int main(int argc, char **argv) 3826 { 3827 epdfinfo_t ctx = {0}; 3828 char *line = NULL; 3829 ssize_t read; 3830 size_t line_size; 3831 const char *error_log = "/dev/null"; 3832 3833 #ifdef __MINGW32__ 3834 error_log = "NUL"; 3835 _setmode(_fileno(stdin), _O_BINARY); 3836 _setmode(_fileno(stdout), _O_BINARY); 3837 #endif 3838 3839 if (argc > 2) 3840 { 3841 fprintf(stderr, "usage: epdfinfo [ERROR-LOGFILE]\n"); 3842 exit (EXIT_FAILURE); 3843 } 3844 if (argc == 2) 3845 error_log = argv[1]; 3846 3847 if (! freopen (error_log, "a", stderr)) 3848 err (2, "Unable to redirect stderr"); 3849 3850 #if ! GLIB_CHECK_VERSION(2,36,0) 3851 g_type_init (); 3852 #endif 3853 3854 ctx.documents = g_hash_table_new (g_str_hash, g_str_equal); 3855 3856 setvbuf (stdout, NULL, _IOFBF, BUFSIZ); 3857 3858 while ((read = getline (&line, &line_size, stdin)) != -1) 3859 { 3860 int nargs = 0; 3861 command_arg_t *cmd_args = NULL; 3862 char **args = NULL; 3863 gchar *error_msg = NULL; 3864 int i; 3865 3866 if (read <= 1 || line[read - 1] != '\n') 3867 { 3868 fprintf (stderr, "Skipped parts of a line: `%s'\n", line); 3869 goto next_line; 3870 } 3871 3872 line[read - 1] = '\0'; 3873 args = command_arg_split (line, &nargs); 3874 if (nargs == 0) 3875 continue; 3876 3877 for (i = 0; i < G_N_ELEMENTS (commands); i++) 3878 { 3879 if (! strcmp (commands[i].name, args[0])) 3880 { 3881 if (commands[i].nargs == 0 3882 || (cmd_args = command_arg_parse (&ctx, args + 1, nargs - 1, 3883 commands + i, &error_msg))) 3884 { 3885 commands[i].execute (&ctx, cmd_args); 3886 if (commands[i].nargs > 0) 3887 free_command_args (cmd_args, commands[i].nargs); 3888 } 3889 else 3890 { 3891 printf_error_response ("%s", error_msg ? error_msg : 3892 "Unknown error (this is a bug)"); 3893 } 3894 if (error_msg) 3895 g_free (error_msg); 3896 break; 3897 } 3898 } 3899 if (G_N_ELEMENTS (commands) == i) 3900 { 3901 printf_error_response ("Unknown command: %s", args[0]); 3902 } 3903 for (i = 0; i < nargs; ++i) 3904 g_free (args[i]); 3905 g_free (args); 3906 next_line: 3907 free (line); 3908 line = NULL; 3909 } 3910 3911 if (ferror (stdin)) 3912 err (2, NULL); 3913 exit (EXIT_SUCCESS); 3914 }