epdfinfo.c (107258B)
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 if (nargs == 0) 1639 { 1640 PopplerQuadrilateral q; 1641 1642 q.p1.x = r->x1; 1643 q.p1.y = r->y1; 1644 q.p2.x = r->x2; 1645 q.p2.y = r->y1; 1646 q.p4.x = r->x2; 1647 q.p4.y = r->y2; 1648 q.p3.x = r->x1; 1649 q.p3.y = r->y2; 1650 1651 g_array_append_val (garray, q); 1652 } 1653 for (i = 0; i < nargs; ++i) 1654 { 1655 PopplerRectangle *rr = &carg.value.rectangle; 1656 1657 error_if_not (command_arg_parse_arg (ctx, args[i], &carg, 1658 ARG_EDGES, error_msg)); 1659 rr->x1 *= width; rr->x2 *= width; 1660 rr->y1 *= height; rr->y2 *= height; 1661 annotation_markup_append_text_region (page, selection_style, rr, garray); 1662 } 1663 cerror_if_not (garray->len > 0, error_msg, "%s", 1664 "Unable to create empty markup annotation"); 1665 1666 if (! strcmp (type, "highlight")) 1667 a = poppler_annot_text_markup_new_highlight (doc->pdf, r, garray); 1668 else if (! strcmp (type, "squiggly")) 1669 a = poppler_annot_text_markup_new_squiggly (doc->pdf, r, garray); 1670 else if (! strcmp (type, "strike-out")) 1671 a = poppler_annot_text_markup_new_strikeout (doc->pdf, r, garray); 1672 else if (! strcmp (type, "underline")) 1673 a = poppler_annot_text_markup_new_underline (doc->pdf, r, garray); 1674 else 1675 cerror_if_not (0, error_msg, "Unknown annotation type: %s", type); 1676 1677 #endif 1678 error: 1679 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1680 if (garray) g_array_unref (garray); 1681 if (region) cairo_region_destroy (region); 1682 #endif 1683 return a; 1684 } 1685 1686 static gboolean 1687 annotation_edit_validate (const epdfinfo_t *ctx, const command_arg_t *rest, 1688 PopplerAnnot *annotation, char **error_msg) 1689 { 1690 int nargs = rest->value.rest.nargs; 1691 char * const *args = rest->value.rest.args; 1692 int i = 0; 1693 command_arg_t carg; 1694 1695 const char* error_fmt = 1696 "Can modify `%s' property only for %s annotations"; 1697 1698 while (i < nargs) 1699 { 1700 command_arg_type_t atype = ARG_INVALID; 1701 const char *key = args[i++]; 1702 1703 cerror_if_not (i < nargs, error_msg, "Missing a value argument"); 1704 1705 if (! strcmp (key, "flags")) 1706 atype = ARG_NATNUM; 1707 else if (! strcmp (key, "color")) 1708 atype = ARG_COLOR; 1709 else if (! strcmp (key, "contents")) 1710 atype = ARG_STRING; 1711 else if (! strcmp (key, "edges")) 1712 atype = ARG_EDGES_OR_POSITION; 1713 else if (! strcmp (key, "label")) 1714 { 1715 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1716 error_fmt, key, "markup"); 1717 atype = ARG_STRING; 1718 } 1719 else if (! strcmp (key, "opacity")) 1720 { 1721 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1722 error_fmt, key, "markup"); 1723 atype = ARG_EDGE; 1724 } 1725 else if (! strcmp (key, "popup")) 1726 { 1727 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1728 error_fmt, key, "markup"); 1729 atype = ARG_EDGES; 1730 } 1731 else if (! strcmp (key, "popup-is-open")) 1732 { 1733 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1734 error_fmt, key, "markup"); 1735 atype = ARG_BOOL; 1736 } 1737 else if (! strcmp (key, "icon")) 1738 { 1739 cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg, 1740 error_fmt, key, "text"); 1741 atype = ARG_STRING; 1742 } 1743 else if (! strcmp (key, "is-open")) 1744 { 1745 cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg, 1746 error_fmt, key, "text"); 1747 atype = ARG_BOOL; 1748 } 1749 else 1750 { 1751 cerror_if_not (0, error_msg, 1752 "Unable to modify property `%s'", key); 1753 } 1754 1755 if (! command_arg_parse_arg (ctx, args[i++], &carg, atype, error_msg)) 1756 return FALSE; 1757 } 1758 1759 return TRUE; 1760 1761 error: 1762 return FALSE; 1763 } 1764 1765 static void 1766 annotation_print (const annotation_t *annot, /* const */ PopplerPage *page) 1767 { 1768 double width, height; 1769 PopplerAnnotMapping *m; 1770 const gchar *key; 1771 PopplerAnnot *a; 1772 PopplerAnnotMarkup *ma; 1773 PopplerAnnotText *ta; 1774 PopplerRectangle r; 1775 PopplerColor *color; 1776 gchar *text; 1777 gdouble opacity; 1778 cairo_region_t *region = NULL; 1779 GDate *date; 1780 1781 if (! annot || ! page) 1782 return; 1783 1784 m = annot->amap; 1785 key = annot->key; 1786 a = m->annot; 1787 poppler_page_get_size (page, &width, &height); 1788 1789 r.x1 = m->area.x1; 1790 r.x2 = m->area.x2; 1791 r.y1 = height - m->area.y2; 1792 r.y2 = height - m->area.y1; 1793 1794 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1795 if (POPPLER_IS_ANNOT_TEXT_MARKUP (a)) 1796 { 1797 region = annotation_markup_get_text_regions (page, POPPLER_ANNOT_TEXT_MARKUP (a)); 1798 perror_if_not (region, "%s", "Unable to extract annotation's text regions"); 1799 } 1800 #endif 1801 1802 /* >>> Any Annotation >>> */ 1803 /* Page */ 1804 printf ("%d:", poppler_page_get_index (page) + 1); 1805 /* Area */ 1806 printf ("%f %f %f %f:", r.x1 / width, r.y1 / height 1807 , r.x2 / width, r.y2 / height); 1808 1809 /* Type */ 1810 printf ("%s:", xpoppler_annot_type_string (poppler_annot_get_annot_type (a))); 1811 /* Internal Key */ 1812 print_response_string (key, COLON); 1813 1814 /* Flags */ 1815 printf ("%d:", poppler_annot_get_flags (a)); 1816 1817 /* Color */ 1818 color = poppler_annot_get_color (a); 1819 if (color) 1820 { 1821 /* Reduce 2 Byte to 1 Byte color space */ 1822 printf ("#%.2x%.2x%.2x", (color->red >> 8) 1823 , (color->green >> 8) 1824 , (color->blue >> 8)); 1825 g_free (color); 1826 } 1827 1828 putchar (':'); 1829 1830 /* Text Contents */ 1831 text = poppler_annot_get_contents (a); 1832 print_response_string (text, COLON); 1833 g_free (text); 1834 1835 /* Modified Date */ 1836 text = poppler_annot_get_modified (a); 1837 print_response_string (text, NONE); 1838 g_free (text); 1839 1840 /* <<< Any Annotation <<< */ 1841 1842 /* >>> Markup Annotation >>> */ 1843 if (! POPPLER_IS_ANNOT_MARKUP (a)) 1844 { 1845 putchar ('\n'); 1846 goto theend; 1847 } 1848 1849 putchar (':'); 1850 ma = POPPLER_ANNOT_MARKUP (a); 1851 /* Label */ 1852 text = poppler_annot_markup_get_label (ma); 1853 print_response_string (text, COLON); 1854 g_free (text); 1855 1856 /* Subject */ 1857 text = poppler_annot_markup_get_subject (ma); 1858 print_response_string (text, COLON); 1859 g_free (text); 1860 1861 /* Opacity */ 1862 opacity = poppler_annot_markup_get_opacity (ma); 1863 printf ("%f:", opacity); 1864 1865 /* Popup (Area + isOpen) */ 1866 if (poppler_annot_markup_has_popup (ma) 1867 && poppler_annot_markup_get_popup_rectangle (ma, &r)) 1868 { 1869 gdouble tmp = r.y1; 1870 r.y1 = height - r.y2; 1871 r.y2 = height - tmp; 1872 printf ("%f %f %f %f:%d:", r.x1 / width, r.y1 / height 1873 , r.x2 / width, r.y2 / height 1874 , poppler_annot_markup_get_popup_is_open (ma) ? 1 : 0); 1875 1876 } 1877 else 1878 printf ("::"); 1879 1880 /* Creation Date */ 1881 date = poppler_annot_markup_get_date (ma); 1882 if (date != NULL && g_date_valid(date)) 1883 { 1884 gchar datebuf[128]; 1885 g_date_strftime (datebuf, 127, "%x", date); 1886 print_response_string (datebuf, NONE); 1887 g_date_free (date); 1888 } 1889 1890 /* <<< Markup Annotation <<< */ 1891 1892 /* >>> Text Annotation >>> */ 1893 if (POPPLER_IS_ANNOT_TEXT (a)) 1894 { 1895 putchar (':'); 1896 ta = POPPLER_ANNOT_TEXT (a); 1897 /* Text Icon */ 1898 text = poppler_annot_text_get_icon (ta); 1899 print_response_string (text, COLON); 1900 g_free (text); 1901 /* Text State */ 1902 printf ("%s:%d", 1903 xpoppler_annot_text_state_string (poppler_annot_text_get_state (ta)), 1904 poppler_annot_text_get_is_open (ta)); 1905 } 1906 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1907 /* <<< Text Annotation <<< */ 1908 else if (POPPLER_IS_ANNOT_TEXT_MARKUP (a)) 1909 { 1910 /* >>> Markup Text Annotation >>> */ 1911 putchar (':'); 1912 region_print (region, width, height); 1913 /* <<< Markup Text Annotation <<< */ 1914 } 1915 #endif 1916 putchar ('\n'); 1917 theend: 1918 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1919 error: 1920 #endif 1921 if (region) cairo_region_destroy (region); 1922 } 1923 1924 static void 1925 attachment_print (PopplerAttachment *att, const char *id, gboolean do_save) 1926 { 1927 time_t time; 1928 1929 print_response_string (id, COLON); 1930 print_response_string (att->name, COLON); 1931 print_response_string (att->description, COLON); 1932 if (att->size + 1 != 0) 1933 printf ("%" G_GSIZE_FORMAT ":", att->size); 1934 else 1935 printf ("-1:"); 1936 time = (time_t) att->mtime; 1937 print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON); 1938 time = (time_t) att->ctime; 1939 print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON); 1940 print_response_string (att->checksum ? att->checksum->str : "" , COLON); 1941 if (do_save) 1942 { 1943 char *filename = mktempfile (); 1944 GError *error = NULL; 1945 if (filename) 1946 { 1947 if (! poppler_attachment_save (att, filename, &error)) 1948 { 1949 fprintf (stderr, "Writing attachment failed: %s" 1950 , error ? error->message : "reason unknown"); 1951 if (error) 1952 g_free (error); 1953 } 1954 else 1955 { 1956 print_response_string (filename, NONE); 1957 } 1958 free (filename); 1959 } 1960 } 1961 putchar ('\n'); 1962 } 1963 1964 1965 1966 /* ================================================================== * 1967 * Server command implementations 1968 * ================================================================== */ 1969 1970 /* Name: features 1971 Args: None 1972 Returns: A list of compile-time features. 1973 Errors: None 1974 */ 1975 1976 const command_arg_type_t cmd_features_spec[] = {}; 1977 1978 static void 1979 cmd_features (const epdfinfo_t *ctx, const command_arg_t *args) 1980 { 1981 const char *features[] = { 1982 #ifdef HAVE_POPPLER_FIND_OPTS 1983 "case-sensitive-search", 1984 #else 1985 "no-case-sensitive-search", 1986 #endif 1987 #ifdef HAVE_POPPLER_ANNOT_WRITE 1988 "writable-annotations", 1989 #else 1990 "no-writable-annotations", 1991 #endif 1992 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1993 "markup-annotations" 1994 #else 1995 "no-markup-annotations" 1996 #endif 1997 }; 1998 int i; 1999 OK_BEGIN (); 2000 for (i = 0; i < G_N_ELEMENTS (features); ++i) 2001 { 2002 printf ("%s", features[i]); 2003 if (i < G_N_ELEMENTS (features) - 1) 2004 putchar (':'); 2005 } 2006 putchar ('\n'); 2007 OK_END (); 2008 } 2009 2010 2011 /* Name: open 2012 Args: filename password 2013 Returns: Nothing 2014 Errors: If file can't be opened or is not a PDF document. 2015 */ 2016 2017 const command_arg_type_t cmd_open_spec[] = 2018 { 2019 ARG_NONEMPTY_STRING, /* filename */ 2020 ARG_STRING, /* password */ 2021 }; 2022 2023 static void 2024 cmd_open (const epdfinfo_t *ctx, const command_arg_t *args) 2025 { 2026 const char *filename = args[0].value.string; 2027 const char *passwd = args[1].value.string; 2028 GError *gerror = NULL; 2029 document_t *doc; 2030 2031 if (! *passwd) 2032 passwd = NULL; 2033 2034 doc = document_open(ctx, filename, passwd, &gerror); 2035 perror_if_not (doc, "Error opening %s:%s", filename, 2036 gerror ? gerror->message : "unknown error"); 2037 OK (); 2038 2039 error: 2040 if (gerror) 2041 { 2042 g_error_free (gerror); 2043 gerror = NULL; 2044 } 2045 } 2046 2047 /* Name: close 2048 Args: filename 2049 Returns: 1 if file was open, otherwise 0. 2050 Errors: None 2051 */ 2052 2053 const command_arg_type_t cmd_close_spec[] = 2054 { 2055 ARG_NONEMPTY_STRING /* filename */ 2056 }; 2057 2058 static void 2059 cmd_close (const epdfinfo_t *ctx, const command_arg_t *args) 2060 { 2061 document_t *doc = g_hash_table_lookup(ctx->documents, args->value.string); 2062 2063 g_hash_table_remove (ctx->documents, args->value.string); 2064 free_document (doc); 2065 OK_BEGIN (); 2066 puts (doc ? "1" : "0"); 2067 OK_END (); 2068 } 2069 2070 /* Name: closeall 2071 Args: None 2072 Returns: Nothing 2073 Errors: None 2074 */ 2075 static void 2076 cmd_closeall (const epdfinfo_t *ctx, const command_arg_t *args) 2077 { 2078 GHashTableIter iter; 2079 gpointer key, value; 2080 2081 g_hash_table_iter_init (&iter, ctx->documents); 2082 while (g_hash_table_iter_next (&iter, &key, &value)) 2083 { 2084 document_t *doc = (document_t*) value; 2085 free_document (doc); 2086 g_hash_table_iter_remove (&iter); 2087 } 2088 OK (); 2089 } 2090 2091 2092 const command_arg_type_t cmd_search_regexp_spec[] = 2093 { 2094 ARG_DOC, 2095 ARG_NATNUM, /* first page */ 2096 ARG_NATNUM, /* last page */ 2097 ARG_NONEMPTY_STRING, /* regexp */ 2098 ARG_NATNUM, /* compile flags */ 2099 ARG_NATNUM /* match flags */ 2100 }; 2101 2102 static void 2103 cmd_search_regexp(const epdfinfo_t *ctx, const command_arg_t *args) 2104 { 2105 PopplerDocument *doc = args[0].value.doc->pdf; 2106 int first = args[1].value.natnum; 2107 int last = args[2].value.natnum; 2108 const gchar *regexp = args[3].value.string; 2109 GRegexCompileFlags cflags = args[4].value.natnum; 2110 GRegexMatchFlags mflags = args[5].value.natnum; 2111 double width, height; 2112 int pn; 2113 GError *gerror = NULL; 2114 GRegex *re = NULL; 2115 2116 NORMALIZE_PAGE_ARG (doc, &first, &last); 2117 2118 re = g_regex_new (regexp, cflags, mflags, &gerror); 2119 perror_if_not (NULL == gerror, "Invalid regexp: %s", gerror->message); 2120 2121 OK_BEGIN (); 2122 for (pn = first; pn <= last; ++pn) 2123 { 2124 PopplerPage *page = poppler_document_get_page(doc, pn - 1); 2125 char *text; 2126 PopplerRectangle *rectangles = NULL; 2127 guint nrectangles; 2128 GMatchInfo *match = NULL; 2129 2130 if (! page) 2131 continue; 2132 2133 text = poppler_page_get_text (page); 2134 poppler_page_get_text_layout (page, &rectangles, &nrectangles); 2135 poppler_page_get_size (page, &width, &height); 2136 g_regex_match (re, text, 0, &match); 2137 2138 while (g_match_info_matches (match)) 2139 { 2140 const double scale = 100.0; 2141 gint start, end, ustart, ulen; 2142 gchar *string = NULL; 2143 gchar *line = NULL; 2144 int i; 2145 2146 /* Does this ever happen ? */ 2147 if (! g_match_info_fetch_pos (match, 0, &start, &end)) 2148 continue; 2149 2150 string = g_match_info_fetch (match, 0); 2151 ustart = g_utf8_strlen (text, start); 2152 ulen = g_utf8_strlen (string, -1); 2153 2154 cairo_region_t *region = cairo_region_create (); 2155 /* Merge matched glyph rectangles. Scale them so we're able 2156 to use cairo . */ 2157 if (ulen > 0) 2158 { 2159 assert (ustart < nrectangles 2160 && ustart + ulen <= nrectangles); 2161 line = poppler_page_get_selected_text 2162 (page, POPPLER_SELECTION_LINE, rectangles + ustart); 2163 2164 for (i = ustart; i < ustart + ulen; ++i) 2165 { 2166 PopplerRectangle *r = rectangles + i; 2167 cairo_rectangle_int_t c; 2168 2169 c.x = (int) (scale * r->x1 + 0.5); 2170 c.y = (int) (scale * r->y1 + 0.5); 2171 c.width = (int) (scale * (r->x2 - r->x1) + 0.5); 2172 c.height = (int) (scale * (r->y2 - r->y1) + 0.5); 2173 2174 cairo_region_union_rectangle (region, &c); 2175 } 2176 2177 } 2178 2179 printf ("%d:", pn); 2180 print_response_string (string, COLON); 2181 print_response_string (strchomp (line), COLON); 2182 region_print (region, width * scale, height * scale); 2183 putchar ('\n'); 2184 cairo_region_destroy (region); 2185 g_free (string); 2186 g_free (line); 2187 g_match_info_next (match, NULL); 2188 } 2189 g_free (rectangles); 2190 g_object_unref (page); 2191 g_free (text); 2192 g_match_info_free (match); 2193 } 2194 OK_END (); 2195 2196 error: 2197 if (re) g_regex_unref (re); 2198 if (gerror) g_error_free (gerror); 2199 } 2200 2201 const command_arg_type_t cmd_regexp_flags_spec[] = 2202 { 2203 }; 2204 2205 static void 2206 cmd_regexp_flags (const epdfinfo_t *ctx, const command_arg_t *args) 2207 { 2208 OK_BEGIN (); 2209 printf ("caseless:%d\n", G_REGEX_CASELESS); 2210 printf ("multiline:%d\n", G_REGEX_MULTILINE); 2211 printf ("dotall:%d\n", G_REGEX_DOTALL); 2212 printf ("extended:%d\n", G_REGEX_EXTENDED); 2213 printf ("anchored:%d\n", G_REGEX_ANCHORED); 2214 printf ("dollar-endonly:%d\n", G_REGEX_DOLLAR_ENDONLY); 2215 printf ("ungreedy:%d\n", G_REGEX_UNGREEDY); 2216 printf ("raw:%d\n", G_REGEX_RAW); 2217 printf ("no-auto-capture:%d\n", G_REGEX_NO_AUTO_CAPTURE); 2218 printf ("optimize:%d\n", G_REGEX_OPTIMIZE); 2219 printf ("dupnames:%d\n", G_REGEX_DUPNAMES); 2220 printf ("newline-cr:%d\n", G_REGEX_NEWLINE_CR); 2221 printf ("newline-lf:%d\n", G_REGEX_NEWLINE_LF); 2222 printf ("newline-crlf:%d\n", G_REGEX_NEWLINE_CRLF); 2223 2224 printf ("match-anchored:%d\n", G_REGEX_MATCH_ANCHORED); 2225 printf ("match-notbol:%d\n", G_REGEX_MATCH_NOTBOL); 2226 printf ("match-noteol:%d\n", G_REGEX_MATCH_NOTEOL); 2227 printf ("match-notempty:%d\n", G_REGEX_MATCH_NOTEMPTY); 2228 printf ("match-partial:%d\n", G_REGEX_MATCH_PARTIAL); 2229 printf ("match-newline-cr:%d\n", G_REGEX_MATCH_NEWLINE_CR); 2230 printf ("match-newline-lf:%d\n", G_REGEX_MATCH_NEWLINE_LF); 2231 printf ("match-newline-crlf:%d\n", G_REGEX_MATCH_NEWLINE_CRLF); 2232 printf ("match-newline-any:%d\n", G_REGEX_MATCH_NEWLINE_ANY); 2233 2234 OK_END (); 2235 } 2236 2237 2238 const command_arg_type_t cmd_search_string_spec[] = 2239 { 2240 ARG_DOC, 2241 ARG_NATNUM, /* first page */ 2242 ARG_NATNUM, /* last page */ 2243 ARG_NONEMPTY_STRING, /* search string */ 2244 ARG_BOOL, /* ignore-case */ 2245 }; 2246 2247 static void 2248 cmd_search_string(const epdfinfo_t *ctx, const command_arg_t *args) 2249 { 2250 PopplerDocument *doc = args[0].value.doc->pdf; 2251 int first = args[1].value.natnum; 2252 int last = args[2].value.natnum; 2253 const char *string = args[3].value.string; 2254 gboolean ignore_case = args[4].value.flag; 2255 GList *list, *item; 2256 double width, height; 2257 int pn; 2258 #ifdef HAVE_POPPLER_FIND_OPTS 2259 PopplerFindFlags flags = ignore_case ? 0 : POPPLER_FIND_CASE_SENSITIVE; 2260 #endif 2261 2262 NORMALIZE_PAGE_ARG (doc, &first, &last); 2263 OK_BEGIN (); 2264 for (pn = first; pn <= last; ++pn) 2265 { 2266 PopplerPage *page = poppler_document_get_page(doc, pn - 1); 2267 2268 if (! page) 2269 continue; 2270 2271 #ifdef HAVE_POPPLER_FIND_OPTS 2272 list = poppler_page_find_text_with_options(page, string, flags); 2273 #else 2274 list = poppler_page_find_text(page, string); 2275 #endif 2276 2277 poppler_page_get_size (page, &width, &height); 2278 2279 for (item = list; item; item = item->next) 2280 { 2281 gchar *line, *match; 2282 PopplerRectangle *r = item->data; 2283 gdouble y1 = r->y1; 2284 2285 r->y1 = height - r->y2; 2286 r->y2 = height - y1; 2287 2288 printf ("%d:", pn); 2289 line = strchomp (poppler_page_get_selected_text 2290 (page, POPPLER_SELECTION_LINE, r)); 2291 match = strchomp (poppler_page_get_selected_text 2292 (page, POPPLER_SELECTION_GLYPH, r)); 2293 print_response_string (match, COLON); 2294 print_response_string (line, COLON); 2295 printf ("%f %f %f %f\n", 2296 r->x1 / width, r->y1 / height, 2297 r->x2 / width, r->y2 / height); 2298 g_free (line); 2299 g_free (match); 2300 poppler_rectangle_free (r); 2301 } 2302 g_list_free (list); 2303 g_object_unref (page); 2304 } 2305 OK_END (); 2306 } 2307 2308 /* Name: metadata 2309 Args: filename 2310 Returns: PDF's metadata 2311 Errors: None 2312 2313 title author subject keywords creator producer pdf-version create-date mod-date 2314 2315 Dates are in seconds since the epoche. 2316 2317 */ 2318 2319 const command_arg_type_t cmd_metadata_spec[] = 2320 { 2321 ARG_DOC, 2322 }; 2323 2324 static void 2325 cmd_metadata (const epdfinfo_t *ctx, const command_arg_t *args) 2326 { 2327 PopplerDocument *doc = args[0].value.doc->pdf; 2328 time_t date; 2329 gchar *md[6]; 2330 gchar *title; 2331 int i; 2332 char *time_str; 2333 2334 OK_BEGIN (); 2335 2336 title = poppler_document_get_title (doc); 2337 print_response_string (title, COLON); 2338 g_free (title); 2339 2340 md[0] = poppler_document_get_author (doc); 2341 md[1] = poppler_document_get_subject (doc); 2342 md[2] = poppler_document_get_keywords (doc); 2343 md[3] = poppler_document_get_creator (doc); 2344 md[4] = poppler_document_get_producer (doc); 2345 md[5] = poppler_document_get_pdf_version_string (doc); 2346 2347 for (i = 0; i < 6; ++i) 2348 { 2349 print_response_string (md[i], COLON); 2350 g_free (md[i]); 2351 } 2352 2353 date = poppler_document_get_creation_date (doc); 2354 time_str = strchomp (ctime (&date)); 2355 print_response_string (time_str ? time_str : "", COLON); 2356 date = poppler_document_get_modification_date (doc); 2357 time_str = strchomp (ctime (&date)); 2358 print_response_string (time_str ? time_str : "", NEWLINE); 2359 OK_END (); 2360 } 2361 2362 /* Name: outline 2363 Args: filename 2364 2365 Returns: The documents outline (or index) as a, possibly empty, 2366 list of records: 2367 2368 tree-level ACTION 2369 2370 See cmd_pagelinks for how ACTION is constructed. 2371 2372 Errors: None 2373 */ 2374 2375 static void 2376 cmd_outline_walk (PopplerDocument *doc, PopplerIndexIter *iter, int depth) 2377 { 2378 do 2379 { 2380 PopplerIndexIter *child; 2381 PopplerAction *action = poppler_index_iter_get_action (iter); 2382 2383 if (! action) 2384 continue; 2385 2386 if (action_is_handled (action)) 2387 { 2388 printf ("%d:", depth); 2389 action_print (doc, action); 2390 } 2391 2392 child = poppler_index_iter_get_child (iter); 2393 if (child) 2394 { 2395 cmd_outline_walk (doc, child, depth + 1); 2396 } 2397 poppler_action_free (action); 2398 poppler_index_iter_free (child); 2399 } while (poppler_index_iter_next (iter)); 2400 } 2401 2402 const command_arg_type_t cmd_outline_spec[] = 2403 { 2404 ARG_DOC, 2405 }; 2406 2407 static void 2408 cmd_outline (const epdfinfo_t *ctx, const command_arg_t *args) 2409 { 2410 PopplerIndexIter *iter = poppler_index_iter_new (args->value.doc->pdf); 2411 OK_BEGIN (); 2412 if (iter) 2413 { 2414 cmd_outline_walk (args->value.doc->pdf, iter, 1); 2415 poppler_index_iter_free (iter); 2416 } 2417 OK_END (); 2418 } 2419 2420 /* Name: quit 2421 Args: None 2422 Returns: Nothing 2423 Errors: None 2424 2425 Close all documents and exit. 2426 */ 2427 2428 2429 const command_arg_type_t cmd_quit_spec[] = {}; 2430 2431 static void 2432 cmd_quit (const epdfinfo_t *ctx, const command_arg_t *args) 2433 { 2434 cmd_closeall (ctx, args); 2435 exit (EXIT_SUCCESS); 2436 } 2437 2438 /* Name: number-of-pages 2439 Args: filename 2440 Returns: The number of pages. 2441 Errors: None 2442 */ 2443 2444 2445 const command_arg_type_t cmd_number_of_pages_spec[] = 2446 { 2447 ARG_DOC 2448 }; 2449 2450 static void 2451 cmd_number_of_pages (const epdfinfo_t *ctx, const command_arg_t *args) 2452 { 2453 int npages = poppler_document_get_n_pages (args->value.doc->pdf); 2454 OK_BEGIN (); 2455 printf ("%d\n", npages); 2456 OK_END (); 2457 } 2458 2459 /* Name: pagelinks 2460 Args: filename page 2461 Returns: A list of linkmaps: 2462 2463 edges ACTION , 2464 2465 where ACTION is one of 2466 2467 'goto-dest' title page top 2468 'goto-remote' title filename page top 2469 'uri' title URI 2470 'launch' title program arguments 2471 2472 top is desired vertical position, filename is the target PDF of the 2473 `goto-remote' link. 2474 2475 Errors: None 2476 */ 2477 2478 2479 const command_arg_type_t cmd_pagelinks_spec[] = 2480 { 2481 ARG_DOC, 2482 ARG_NATNUM /* page number */ 2483 }; 2484 2485 static void 2486 cmd_pagelinks(const epdfinfo_t *ctx, const command_arg_t *args) 2487 { 2488 PopplerDocument *doc = args[0].value.doc->pdf; 2489 PopplerPage *page = NULL; 2490 int pn = args[1].value.natnum; 2491 double width, height; 2492 GList *link_map = NULL, *item; 2493 2494 page = poppler_document_get_page (doc, pn - 1); 2495 perror_if_not (page, "No such page %d", pn); 2496 poppler_page_get_size (page, &width, &height); 2497 link_map = poppler_page_get_link_mapping (page); 2498 2499 OK_BEGIN (); 2500 for (item = g_list_last (link_map); item; item = item->prev) 2501 { 2502 2503 PopplerLinkMapping *link = item->data; 2504 PopplerRectangle *r = &link->area; 2505 gdouble y1 = r->y1; 2506 /* LinkMappings have a different gravity. */ 2507 r->y1 = height - r->y2; 2508 r->y2 = height - y1; 2509 2510 if (! action_is_handled (link->action)) 2511 continue; 2512 2513 printf ("%f %f %f %f:", 2514 r->x1 / width, r->y1 / height, 2515 r->x2 / width, r->y2 / height); 2516 action_print (doc, link->action); 2517 } 2518 OK_END (); 2519 error: 2520 if (page) g_object_unref (page); 2521 if (link_map) poppler_page_free_link_mapping (link_map); 2522 } 2523 2524 /* Name: gettext 2525 Args: filename page edges selection-style 2526 Returns: The selection's text. 2527 Errors: If page is out of range. 2528 2529 For the selection-style argument see getselection command. 2530 */ 2531 2532 2533 const command_arg_type_t cmd_gettext_spec[] = 2534 { 2535 ARG_DOC, 2536 ARG_NATNUM, /* page number */ 2537 ARG_EDGES, /* selection */ 2538 ARG_NATNUM /* selection-style */ 2539 }; 2540 2541 static void 2542 cmd_gettext(const epdfinfo_t *ctx, const command_arg_t *args) 2543 { 2544 PopplerDocument *doc = args[0].value.doc->pdf; 2545 int pn = args[1].value.natnum; 2546 PopplerRectangle r = args[2].value.rectangle; 2547 int selection_style = args[3].value.natnum; 2548 PopplerPage *page = NULL; 2549 double width, height; 2550 gchar *text = NULL; 2551 2552 selection_style = xpoppler_validate_selection_style (selection_style); 2553 page = poppler_document_get_page (doc, pn - 1); 2554 perror_if_not (page, "No such page %d", pn); 2555 poppler_page_get_size (page, &width, &height); 2556 r.x1 = r.x1 * width; 2557 r.x2 = r.x2 * width; 2558 r.y1 = r.y1 * height; 2559 r.y2 = r.y2 * height; 2560 /* printf ("%f %f %f %f , %f %f\n", r.x1, r.y1, r.x2, r.y2, width, height); */ 2561 text = poppler_page_get_selected_text (page, selection_style, &r); 2562 2563 OK_BEGIN (); 2564 print_response_string (text, NEWLINE); 2565 OK_END (); 2566 2567 error: 2568 g_free (text); 2569 if (page) g_object_unref (page); 2570 } 2571 2572 /* Name: getselection 2573 Args: filename page edges selection-selection_style 2574 Returns: The selection's text. 2575 Errors: If page is out of range. 2576 2577 selection-selection_style should be as follows. 2578 2579 0 (POPPLER_SELECTION_GLYPH) 2580 glyph is the minimum unit for selection 2581 2582 1 (POPPLER_SELECTION_WORD) 2583 word is the minimum unit for selection 2584 2585 2 (POPPLER_SELECTION_LINE) 2586 line is the minimum unit for selection 2587 */ 2588 2589 2590 const command_arg_type_t cmd_getselection_spec[] = 2591 { 2592 ARG_DOC, 2593 ARG_NATNUM, /* page number */ 2594 ARG_EDGES, /* selection */ 2595 ARG_NATNUM /* selection-style */ 2596 }; 2597 2598 static void 2599 cmd_getselection (const epdfinfo_t *ctx, const command_arg_t *args) 2600 { 2601 PopplerDocument *doc = args[0].value.doc->pdf; 2602 int pn = args[1].value.natnum; 2603 PopplerRectangle r = args[2].value.rectangle; 2604 int selection_style = args[3].value.natnum; 2605 gdouble width, height; 2606 cairo_region_t *region = NULL; 2607 PopplerPage *page = NULL; 2608 int i; 2609 2610 selection_style = xpoppler_validate_selection_style (selection_style); 2611 page = poppler_document_get_page (doc, pn - 1); 2612 perror_if_not (page, "No such page %d", pn); 2613 poppler_page_get_size (page, &width, &height); 2614 2615 r.x1 = r.x1 * width; 2616 r.x2 = r.x2 * width; 2617 r.y1 = r.y1 * height; 2618 r.y2 = r.y2 * height; 2619 2620 region = poppler_page_get_selected_region (page, 1.0, selection_style, &r); 2621 2622 OK_BEGIN (); 2623 for (i = 0; i < cairo_region_num_rectangles (region); ++i) 2624 { 2625 cairo_rectangle_int_t r; 2626 2627 cairo_region_get_rectangle (region, i, &r); 2628 printf ("%f %f %f %f\n", 2629 r.x / width, 2630 r.y / height, 2631 (r.x + r.width) / width, 2632 (r.y + r.height) / height); 2633 } 2634 OK_END (); 2635 2636 error: 2637 if (region) cairo_region_destroy (region); 2638 if (page) g_object_unref (page); 2639 } 2640 2641 /* Name: pagesize 2642 Args: filename page 2643 Returns: width height 2644 Errors: If page is out of range. 2645 */ 2646 2647 2648 const command_arg_type_t cmd_pagesize_spec[] = 2649 { 2650 ARG_DOC, 2651 ARG_NATNUM /* page number */ 2652 }; 2653 2654 static void 2655 cmd_pagesize(const epdfinfo_t *ctx, const command_arg_t *args) 2656 { 2657 PopplerDocument *doc = args[0].value.doc->pdf; 2658 int pn = args[1].value.natnum; 2659 PopplerPage *page = NULL; 2660 double width, height; 2661 2662 2663 page = poppler_document_get_page (doc, pn - 1); 2664 perror_if_not (page, "No such page %d", pn); 2665 poppler_page_get_size (page, &width, &height); 2666 2667 OK_BEGIN (); 2668 printf ("%f:%f\n", width, height); 2669 OK_END (); 2670 2671 error: 2672 if (page) g_object_unref (page); 2673 } 2674 2675 /* Annotations */ 2676 2677 /* Name: getannots 2678 Args: filename firstpage lastpage 2679 Returns: The list of annotations of this page. 2680 2681 For all annotations 2682 2683 page edges type key flags color contents mod-date 2684 2685 ,where 2686 2687 name is a document-unique name, 2688 flags is PopplerAnnotFlag bitmask, 2689 color is 3-byte RGB hex number and 2690 2691 Then 2692 2693 label subject opacity popup-edges popup-is-open create-date 2694 2695 if this is a markup annotation and additionally 2696 2697 text-icon text-state 2698 2699 for markup text annotations. 2700 2701 Errors: If page is out of range. 2702 */ 2703 2704 2705 const command_arg_type_t cmd_getannots_spec[] = 2706 { 2707 ARG_DOC, 2708 ARG_NATNUM, /* first page */ 2709 ARG_NATNUM /* last page */ 2710 }; 2711 2712 static void 2713 cmd_getannots(const epdfinfo_t *ctx, const command_arg_t *args) 2714 { 2715 PopplerDocument *doc = args[0].value.doc->pdf; 2716 gint first = args[1].value.natnum; 2717 gint last = args[2].value.natnum; 2718 GList *list; 2719 gint pn; 2720 2721 first = MAX(1, first); 2722 if (last <= 0) 2723 last = poppler_document_get_n_pages (doc); 2724 else 2725 last = MIN(last, poppler_document_get_n_pages (doc)); 2726 2727 OK_BEGIN (); 2728 for (pn = first; pn <= last; ++pn) 2729 { 2730 GList *annots = annoation_get_for_page (args->value.doc, pn); 2731 PopplerPage *page = poppler_document_get_page (doc, pn - 1); 2732 2733 if (! page) 2734 continue; 2735 2736 for (list = annots; list; list = list->next) 2737 { 2738 annotation_t *annot = (annotation_t *)list->data; 2739 annotation_print (annot, page); 2740 } 2741 g_object_unref (page); 2742 } 2743 OK_END (); 2744 } 2745 2746 /* Name: getannot 2747 Args: filename name 2748 Returns: The annotation for name, see cmd_getannots. 2749 Errors: If no annotation named ,name' exists. 2750 */ 2751 2752 2753 const command_arg_type_t cmd_getannot_spec[] = 2754 { 2755 ARG_DOC, 2756 ARG_NONEMPTY_STRING, /* annotation's key */ 2757 }; 2758 2759 static void 2760 cmd_getannot (const epdfinfo_t *ctx, const command_arg_t *args) 2761 { 2762 document_t *doc = args->value.doc; 2763 const gchar *key = args[1].value.string; 2764 PopplerPage *page = NULL; 2765 annotation_t *a = annotation_get_by_key (doc, key); 2766 gint index; 2767 2768 perror_if_not (a, "No such annotation: %s", key); 2769 index = poppler_annot_get_page_index (a->amap->annot); 2770 if (index >= 0) 2771 page = poppler_document_get_page (doc->pdf, index); 2772 perror_if_not (page, "Unable to get page %d", index + 1); 2773 2774 OK_BEGIN (); 2775 annotation_print (a, page); 2776 OK_END (); 2777 2778 error: 2779 if (page) g_object_unref (page); 2780 } 2781 2782 /* Name: getannot_attachment 2783 Args: filename name [output-filename] 2784 Returns: name description size mtime ctime output-filename 2785 Errors: If no annotation named ,name' exists or output-filename is 2786 not writable. 2787 */ 2788 2789 2790 const command_arg_type_t cmd_getattachment_from_annot_spec[] = 2791 { 2792 ARG_DOC, 2793 ARG_NONEMPTY_STRING, /* annotation's name */ 2794 ARG_BOOL /* save attachment */ 2795 }; 2796 2797 static void 2798 cmd_getattachment_from_annot (const epdfinfo_t *ctx, const command_arg_t *args) 2799 { 2800 document_t *doc = args->value.doc; 2801 const gchar *key = args[1].value.string; 2802 gboolean do_save = args[2].value.flag; 2803 PopplerAttachment *att = NULL; 2804 annotation_t *a = annotation_get_by_key (doc, key); 2805 gchar *id = NULL; 2806 2807 perror_if_not (a, "No such annotation: %s", key); 2808 perror_if_not (POPPLER_IS_ANNOT_FILE_ATTACHMENT (a->amap->annot), 2809 "Not a file annotation: %s", key); 2810 att = poppler_annot_file_attachment_get_attachment 2811 (POPPLER_ANNOT_FILE_ATTACHMENT (a->amap->annot)); 2812 perror_if_not (att, "Unable to get attachment: %s", key); 2813 id = g_strdup_printf ("attachment-%s", key); 2814 2815 OK_BEGIN (); 2816 attachment_print (att, id, do_save); 2817 OK_END (); 2818 2819 error: 2820 if (att) g_object_unref (att); 2821 if (id) g_free (id); 2822 } 2823 2824 2825 /* document-level attachments */ 2826 const command_arg_type_t cmd_getattachments_spec[] = 2827 { 2828 ARG_DOC, 2829 ARG_BOOL, /* save attachments */ 2830 }; 2831 2832 static void 2833 cmd_getattachments (const epdfinfo_t *ctx, const command_arg_t *args) 2834 { 2835 document_t *doc = args->value.doc; 2836 gboolean do_save = args[1].value.flag; 2837 GList *item; 2838 GList *attmnts = poppler_document_get_attachments (doc->pdf); 2839 int i; 2840 2841 OK_BEGIN (); 2842 for (item = attmnts, i = 0; item; item = item->next, ++i) 2843 { 2844 PopplerAttachment *att = (PopplerAttachment*) item->data; 2845 gchar *id = g_strdup_printf ("attachment-document-%d", i); 2846 2847 attachment_print (att, id, do_save); 2848 g_object_unref (att); 2849 g_free (id); 2850 } 2851 g_list_free (attmnts); 2852 2853 OK_END (); 2854 } 2855 2856 #ifdef HAVE_POPPLER_ANNOT_WRITE 2857 2858 const command_arg_type_t cmd_addannot_spec[] = 2859 { 2860 ARG_DOC, 2861 ARG_NATNUM, /* page number */ 2862 ARG_STRING, /* type */ 2863 ARG_NATNUM, /* selection-style */ 2864 ARG_EDGES_OR_POSITION, /* edges or position (uses default size) */ 2865 ARG_REST, /* markup regions */ 2866 }; 2867 2868 static void 2869 cmd_addannot (const epdfinfo_t *ctx, const command_arg_t *args) 2870 { 2871 2872 document_t *doc = args->value.doc; 2873 gint pn = args[1].value.natnum; 2874 const char *type_string = args[2].value.string; 2875 int selection_style = args[3].value.natnum; 2876 PopplerRectangle r = args[4].value.rectangle; 2877 int i; 2878 PopplerPage *page = NULL; 2879 double width, height; 2880 PopplerAnnot *pa; 2881 PopplerAnnotMapping *amap; 2882 annotation_t *a; 2883 gchar *key; 2884 GList *annotations; 2885 gdouble y2; 2886 char *error_msg = NULL; 2887 2888 selection_style = xpoppler_validate_selection_style (selection_style); 2889 page = poppler_document_get_page (doc->pdf, pn - 1); 2890 perror_if_not (page, "Unable to get page %d", pn); 2891 poppler_page_get_size (page, &width, &height); 2892 r.x1 *= width; r.x2 *= width; 2893 r.y1 *= height; r.y2 *= height; 2894 if (r.y2 < 0) 2895 r.y2 = r.y1 + 24; 2896 if (r.x2 < 0) 2897 r.x2 = r.x1 + 24; 2898 y2 = r.y2; 2899 r.y2 = height - r.y1; 2900 r.y1 = height - y2; 2901 2902 pa = annotation_new (ctx, doc, page, type_string, selection_style, &r, &args[5], 2903 &error_msg); 2904 perror_if_not (pa, "Creating annotation failed: %s", 2905 error_msg ? error_msg : "Reason unknown"); 2906 amap = poppler_annot_mapping_new (); 2907 amap->area = r; 2908 amap->annot = pa; 2909 annotations = annoation_get_for_page (doc, pn); 2910 2911 i = g_list_length (annotations); 2912 key = g_strdup_printf ("annot-%d-%d", pn, i); 2913 while (g_hash_table_lookup (doc->annotations.keys, key)) 2914 { 2915 g_free (key); 2916 key = g_strdup_printf ("annot-%d-%d", pn, ++i); 2917 } 2918 a = g_malloc (sizeof (annotation_t)); 2919 a->amap = amap; 2920 a->key = key; 2921 doc->annotations.pages[pn - 1] = 2922 g_list_prepend (annotations, a); 2923 g_hash_table_insert (doc->annotations.keys, key, a); 2924 poppler_page_add_annot (page, pa); 2925 OK_BEGIN (); 2926 annotation_print (a, page); 2927 OK_END (); 2928 2929 error: 2930 if (page) g_object_unref (page); 2931 if (error_msg) g_free (error_msg); 2932 } 2933 2934 2935 const command_arg_type_t cmd_delannot_spec[] = 2936 { 2937 ARG_DOC, 2938 ARG_NONEMPTY_STRING /* Annotation's key */ 2939 }; 2940 2941 static void 2942 cmd_delannot (const epdfinfo_t *ctx, const command_arg_t *args) 2943 { 2944 document_t *doc = args->value.doc; 2945 const gchar *key = args[1].value.string; 2946 PopplerPage *page = NULL; 2947 annotation_t *a = annotation_get_by_key (doc, key); 2948 gint pn; 2949 2950 perror_if_not (a, "No such annotation: %s", key); 2951 pn = poppler_annot_get_page_index (a->amap->annot) + 1; 2952 if (pn >= 1) 2953 page = poppler_document_get_page (doc->pdf, pn - 1); 2954 perror_if_not (page, "Unable to get page %d", pn); 2955 poppler_page_remove_annot (page, a->amap->annot); 2956 doc->annotations.pages[pn - 1] = 2957 g_list_remove (doc->annotations.pages[pn - 1], a); 2958 g_hash_table_remove (doc->annotations.keys, a->key); 2959 poppler_annot_mapping_free(a->amap); 2960 OK (); 2961 2962 error: 2963 if (a) 2964 { 2965 g_free (a->key); 2966 g_free (a); 2967 } 2968 if (page) g_object_unref (page); 2969 } 2970 2971 const command_arg_type_t cmd_editannot_spec[] = 2972 { 2973 ARG_DOC, 2974 ARG_NONEMPTY_STRING, /* annotation key */ 2975 ARG_REST /* (KEY VALUE ...) */ 2976 }; 2977 2978 static void 2979 cmd_editannot (const epdfinfo_t *ctx, const command_arg_t *args) 2980 { 2981 document_t *doc = args->value.doc; 2982 const char *key = args[1].value.string; 2983 int nrest_args = args[2].value.rest.nargs; 2984 char * const *rest_args = args[2].value.rest.args; 2985 annotation_t *a = annotation_get_by_key (doc, key); 2986 PopplerAnnot *pa; 2987 PopplerPage *page = NULL; 2988 int i = 0; 2989 gint index; 2990 char *error_msg = NULL; 2991 command_arg_t carg; 2992 const char *unexpected_parse_error = "Internal error while parsing arg `%s'"; 2993 2994 perror_if_not (a, "No such annotation: %s", key); 2995 pa = a->amap->annot; 2996 perror_if_not (annotation_edit_validate (ctx, &args[2], pa, &error_msg), 2997 "%s", error_msg); 2998 index = poppler_annot_get_page_index (pa); 2999 page = poppler_document_get_page (doc->pdf, index); 3000 perror_if_not (page, "Unable to get page %d for annotation", index); 3001 3002 for (i = 0; i < nrest_args; ++i) 3003 { 3004 const char *key = rest_args[i++]; 3005 3006 if (! strcmp (key, "flags")) 3007 { 3008 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3009 ARG_NATNUM, NULL), 3010 unexpected_parse_error, rest_args[i]); 3011 poppler_annot_set_flags (pa, carg.value.natnum); 3012 } 3013 else if (! strcmp (key, "color")) 3014 { 3015 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3016 ARG_COLOR, NULL), 3017 unexpected_parse_error, rest_args[i]); 3018 poppler_annot_set_color (pa, &carg.value.color); 3019 } 3020 else if (! strcmp (key, "contents")) 3021 { 3022 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3023 ARG_STRING, NULL), 3024 unexpected_parse_error, rest_args[i]); 3025 poppler_annot_set_contents (pa, carg.value.string); 3026 } 3027 else if (! strcmp (key, "edges")) 3028 { 3029 PopplerRectangle *area = &a->amap->area; 3030 gdouble width, height; 3031 PopplerRectangle r; 3032 3033 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3034 ARG_EDGES_OR_POSITION, NULL), 3035 unexpected_parse_error, rest_args[i]); 3036 r = carg.value.rectangle; 3037 poppler_page_get_size (page, &width, &height); 3038 3039 /* Translate Gravity and maybe keep the width and height. */ 3040 if (r.x2 < 0) 3041 area->x2 += (r.x1 * width) - area->x1; 3042 else 3043 area->x2 = r.x2 * width; 3044 3045 if (r.y2 < 0) 3046 area->y1 -= (r.y1 * height) - (height - area->y2); 3047 else 3048 area->y1 = height - (r.y2 * height); 3049 3050 area->x1 = r.x1 * width; 3051 area->y2 = height - (r.y1 * height); 3052 3053 poppler_annot_set_rectangle (pa, area); 3054 } 3055 else if (! strcmp (key, "label")) 3056 { 3057 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 3058 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3059 ARG_STRING, NULL), 3060 unexpected_parse_error, rest_args[i]); 3061 poppler_annot_markup_set_label (ma, carg.value.string); 3062 } 3063 else if (! strcmp (key, "opacity")) 3064 { 3065 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 3066 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3067 ARG_EDGE, NULL), 3068 unexpected_parse_error, rest_args[i]); 3069 poppler_annot_markup_set_opacity (ma, carg.value.edge); 3070 } 3071 else if (! strcmp (key, "popup")) 3072 { 3073 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 3074 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3075 ARG_EDGES, NULL), 3076 unexpected_parse_error, rest_args[i]); 3077 poppler_annot_markup_set_popup (ma, &carg.value.rectangle); 3078 } 3079 else if (! strcmp (key, "popup-is-open")) 3080 { 3081 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 3082 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3083 ARG_BOOL, NULL), 3084 unexpected_parse_error, rest_args[i]); 3085 poppler_annot_markup_set_popup_is_open (ma, carg.value.flag); 3086 } 3087 else if (! strcmp (key, "icon")) 3088 { 3089 PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa); 3090 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3091 ARG_STRING, NULL), 3092 unexpected_parse_error, rest_args[i]); 3093 poppler_annot_text_set_icon (ta, carg.value.string); 3094 } 3095 else if (! strcmp (key, "is-open")) 3096 { 3097 PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa); 3098 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 3099 ARG_BOOL, NULL), 3100 unexpected_parse_error, rest_args[i]); 3101 poppler_annot_text_set_is_open (ta, carg.value.flag); 3102 } 3103 else 3104 { 3105 perror_if_not (0, "internal error: annotation property validation failed"); 3106 } 3107 } 3108 3109 OK_BEGIN (); 3110 annotation_print (a, page); 3111 OK_END (); 3112 3113 error: 3114 if (error_msg) g_free (error_msg); 3115 if (page) g_object_unref (page); 3116 } 3117 3118 const command_arg_type_t cmd_save_spec[] = 3119 { 3120 ARG_DOC, 3121 }; 3122 3123 static void 3124 cmd_save (const epdfinfo_t *ctx, const command_arg_t *args) 3125 { 3126 document_t *doc = args->value.doc; 3127 char *filename = mktempfile (); 3128 GError *gerror = NULL; 3129 gchar *uri; 3130 gboolean success = FALSE; 3131 3132 if (!filename) 3133 { 3134 printf_error_response ("Unable to create temporary file"); 3135 return; 3136 } 3137 3138 uri = g_filename_to_uri (filename, NULL, &gerror); 3139 3140 if (uri) 3141 { 3142 success = poppler_document_save (doc->pdf, uri, &gerror); 3143 g_free (uri); 3144 } 3145 if (! success) 3146 { 3147 printf_error_response ("Error while saving %s:%s" 3148 , filename, gerror ? gerror->message : "?"); 3149 if (gerror) 3150 g_error_free (gerror); 3151 return; 3152 } 3153 OK_BEGIN (); 3154 print_response_string (filename, NEWLINE); 3155 OK_END (); 3156 } 3157 3158 #endif /* HAVE_POPPLER_ANNOT_WRITE */ 3159 3160 3161 const command_arg_type_t cmd_synctex_forward_search_spec[] = 3162 { 3163 ARG_DOC, 3164 ARG_NONEMPTY_STRING, /* source file */ 3165 ARG_NATNUM, /* line number */ 3166 ARG_NATNUM /* column number */ 3167 }; 3168 3169 static void 3170 cmd_synctex_forward_search (const epdfinfo_t *ctx, const command_arg_t *args) 3171 { 3172 document_t *doc = args[0].value.doc; 3173 const char *source = args[1].value.string; 3174 int line = args[2].value.natnum; 3175 int column = args[3].value.natnum; 3176 synctex_scanner_p scanner = NULL; 3177 synctex_node_p node; 3178 float x1, y1, x2, y2; 3179 PopplerPage *page = NULL; 3180 double width, height; 3181 int pn; 3182 3183 scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1); 3184 perror_if_not (scanner, "Unable to create synctex scanner,\ 3185 did you run latex with `--synctex=1' ?"); 3186 3187 perror_if_not (synctex_display_query (scanner, source, line, column, 0) 3188 && (node = synctex_scanner_next_result (scanner)), 3189 "Destination not found"); 3190 3191 pn = synctex_node_page (node); 3192 page = poppler_document_get_page(doc->pdf, pn - 1); 3193 perror_if_not (page, "Page not found"); 3194 x1 = synctex_node_box_visible_h (node); 3195 y1 = synctex_node_box_visible_v (node) 3196 - synctex_node_box_visible_height (node); 3197 x2 = synctex_node_box_visible_width (node) + x1; 3198 y2 = synctex_node_box_visible_depth (node) 3199 + synctex_node_box_visible_height (node) + y1; 3200 poppler_page_get_size (page, &width, &height); 3201 x1 /= width; 3202 y1 /= height; 3203 x2 /= width; 3204 y2 /= height; 3205 3206 OK_BEGIN (); 3207 printf("%d:%f:%f:%f:%f\n", pn, x1, y1, x2, y2); 3208 OK_END (); 3209 3210 error: 3211 if (page) g_object_unref (page); 3212 if (scanner) synctex_scanner_free (scanner); 3213 } 3214 3215 3216 const command_arg_type_t cmd_synctex_backward_search_spec[] = 3217 { 3218 ARG_DOC, 3219 ARG_NATNUM, /* page number */ 3220 ARG_EDGE, /* x */ 3221 ARG_EDGE /* y */ 3222 }; 3223 3224 static void 3225 cmd_synctex_backward_search (const epdfinfo_t *ctx, const command_arg_t *args) 3226 { 3227 document_t *doc = args[0].value.doc; 3228 int pn = args[1].value.natnum; 3229 double x = args[2].value.edge; 3230 double y = args[3].value.edge; 3231 synctex_scanner_p scanner = NULL; 3232 const char *filename; 3233 PopplerPage *page = NULL; 3234 synctex_node_p node; 3235 double width, height; 3236 int line, column; 3237 3238 scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1); 3239 perror_if_not (scanner, "Unable to create synctex scanner,\ 3240 did you run latex with `--synctex=1' ?"); 3241 3242 page = poppler_document_get_page(doc->pdf, pn - 1); 3243 perror_if_not (page, "Page not found"); 3244 poppler_page_get_size (page, &width, &height); 3245 x = x * width; 3246 y = y * height; 3247 3248 if (! synctex_edit_query (scanner, pn, x, y) 3249 || ! (node = synctex_scanner_next_result (scanner)) 3250 || ! (filename = 3251 synctex_scanner_get_name (scanner, synctex_node_tag (node)))) 3252 { 3253 printf_error_response ("Destination not found"); 3254 goto error; 3255 } 3256 3257 line = synctex_node_line (node); 3258 column = synctex_node_column (node); 3259 3260 OK_BEGIN (); 3261 print_response_string (filename, COLON); 3262 printf("%d:%d\n", line, column); 3263 OK_END (); 3264 3265 error: 3266 if (page) g_object_unref (page); 3267 if (scanner) synctex_scanner_free (scanner); 3268 } 3269 3270 3271 const command_arg_type_t cmd_renderpage_spec[] = 3272 { 3273 ARG_DOC, 3274 ARG_NATNUM, /* page number */ 3275 ARG_NATNUM, /* width */ 3276 ARG_REST, /* commands */ 3277 }; 3278 3279 static void 3280 cmd_renderpage (const epdfinfo_t *ctx, const command_arg_t *args) 3281 { 3282 document_t *doc = args[0].value.doc; 3283 int pn = args[1].value.natnum; 3284 int width = args[2].value.natnum; 3285 int nrest_args = args[3].value.rest.nargs; 3286 char * const *rest_args = args[3].value.rest.args; 3287 PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1); 3288 cairo_surface_t *surface = NULL; 3289 cairo_t *cr = NULL; 3290 command_arg_t rest_arg; 3291 gchar *error_msg = NULL; 3292 double pt_width, pt_height; 3293 PopplerColor fg = { 0, 0, 0 }; 3294 PopplerColor bg = { 65535, 0, 0 }; 3295 double alpha = 1.0; 3296 double line_width = 1.5; 3297 PopplerSelectionStyle selection_style = POPPLER_SELECTION_GLYPH; 3298 PopplerRectangle cb = {0.0, 0.0, 1.0, 1.0}; 3299 int i = 0; 3300 3301 perror_if_not (page, "No such page %d", pn); 3302 poppler_page_get_size (page, &pt_width, &pt_height); 3303 surface = image_render_page (doc->pdf, page, width, 1, 3304 &doc->options.render); 3305 perror_if_not (surface, "Failed to render page %d", pn); 3306 3307 if (! nrest_args) 3308 goto theend; 3309 3310 cr = cairo_create (surface); 3311 cairo_scale (cr, width / pt_width, width / pt_width); 3312 3313 while (i < nrest_args) 3314 { 3315 const char* keyword; 3316 3317 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3318 ARG_STRING, &error_msg), 3319 "%s", error_msg); 3320 keyword = rest_arg.value.string; 3321 ++i; 3322 3323 perror_if_not (i < nrest_args, "Keyword is `%s' missing an argument", 3324 keyword); 3325 3326 if (! strcmp (keyword, ":foreground") 3327 || ! strcmp (keyword, ":background")) 3328 { 3329 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3330 ARG_COLOR, &error_msg), 3331 "%s", error_msg); 3332 ++i; 3333 if (! strcmp (keyword, ":foreground")) 3334 fg = rest_arg.value.color; 3335 else 3336 bg = rest_arg.value.color; 3337 3338 } 3339 else if (! strcmp (keyword, ":alpha")) 3340 { 3341 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3342 ARG_EDGE, &error_msg), 3343 "%s", error_msg); 3344 ++i; 3345 alpha = rest_arg.value.edge; 3346 } 3347 else if (! strcmp (keyword, ":crop-to") 3348 || ! strcmp (keyword, ":highlight-region") 3349 || ! strcmp (keyword, ":highlight-text") 3350 || ! strcmp (keyword, ":highlight-line")) 3351 { 3352 PopplerRectangle *r; 3353 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3354 ARG_EDGES, &error_msg), 3355 "%s", error_msg); 3356 3357 ++i; 3358 r = &rest_arg.value.rectangle; 3359 3360 if (! strcmp (keyword, ":crop-to")) 3361 { 3362 gdouble w = (cb.x2 - cb.x1); 3363 gdouble h = (cb.y2 - cb.y1); 3364 gdouble x1 = cb.x1; 3365 gdouble y1 = cb.y1; 3366 3367 cb.x1 = r->x1 * w + x1; 3368 cb.x2 = r->x2 * w + x1; 3369 cb.y1 = r->y1 * h + y1; 3370 cb.y2 = r->y2 * h + y1; 3371 3372 } 3373 else 3374 { 3375 r->x1 = pt_width * r->x1 * (cb.x2 - cb.x1) + pt_width * cb.x1; 3376 r->x2 = pt_width * r->x2 * (cb.x2 - cb.x1) + pt_width * cb.x1; 3377 r->y1 = pt_height * r->y1 * (cb.y2 - cb.y1) + pt_height * cb.y1; 3378 r->y2 = pt_height * r->y2 * (cb.y2 - cb.y1) + pt_height * cb.y1; 3379 3380 if (! strcmp (keyword, ":highlight-region")) 3381 { 3382 const double deg = M_PI / 180.0; 3383 double rad; 3384 3385 r->x1 += line_width / 2; 3386 r->x2 -= line_width / 2; 3387 r->y1 += line_width / 2; 3388 r->y2 -= line_width / 2; 3389 3390 rad = MIN (5, MIN (r->x2 - r->x1, r->y2 - r->y1) / 2.0); 3391 3392 cairo_move_to (cr, r->x1 , r->y1 + rad); 3393 cairo_arc (cr, r->x1 + rad, r->y1 + rad, rad, 180 * deg, 270 * deg); 3394 cairo_arc (cr, r->x2 - rad, r->y1 + rad, rad, 270 * deg, 360 * deg); 3395 cairo_arc (cr, r->x2 - rad, r->y2 - rad, rad, 0 * deg, 90 * deg); 3396 cairo_arc (cr, r->x1 + rad, r->y2 - rad, rad, 90 * deg, 180 * deg); 3397 cairo_close_path (cr); 3398 3399 cairo_set_source_rgba (cr, 3400 bg.red / 65535.0, 3401 bg.green / 65535.0, 3402 bg.blue / 65535.0, alpha); 3403 cairo_fill_preserve (cr); 3404 cairo_set_source_rgba (cr, 3405 fg.red / 65535.0, 3406 fg.green / 65535.0, 3407 fg.blue / 65535.0, 1.0); 3408 cairo_set_line_width (cr, line_width); 3409 cairo_stroke (cr); 3410 } 3411 else 3412 { 3413 gboolean is_single_line = ! strcmp (keyword, ":highlight-line"); 3414 3415 if (is_single_line) 3416 { 3417 gdouble m = r->y1 + (r->y2 - r->y1) / 2; 3418 3419 /* Make the rectangle flat, otherwise poppler frequently 3420 renders neighboring lines.*/ 3421 r->y1 = m; 3422 r->y2 = m; 3423 } 3424 3425 poppler_page_render_selection (page, cr, r, NULL, 3426 selection_style, &fg, &bg); 3427 } 3428 } 3429 } 3430 else if (! strcmp (keyword, ":selection-style")) 3431 { 3432 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3433 ARG_NATNUM, &error_msg), 3434 "%s", error_msg); 3435 ++i; 3436 selection_style = xpoppler_validate_selection_style (rest_arg.value.natnum); 3437 } 3438 else 3439 perror_if_not (0, "Unknown render command: %s", keyword); 3440 } 3441 if (cb.x1 != 0 || cb.y1 != 0 || cb.x2 != 1 || cb.y2 != 1) 3442 { 3443 int height = cairo_image_surface_get_height (surface); 3444 cairo_rectangle_int_t r = {(int) (width * cb.x1 + 0.5), 3445 (int) (height * cb.y1 + 0.5), 3446 (int) (width * (cb.x2 - cb.x1) + 0.5), 3447 (int) (height * (cb.y2 - cb.y1) + 0.5)}; 3448 cairo_surface_t *nsurface = 3449 cairo_image_surface_create (CAIRO_FORMAT_ARGB32, r.width, r.height); 3450 perror_if_not (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS, 3451 "%s", "Failed to create cairo surface"); 3452 cairo_destroy (cr); 3453 cr = cairo_create (nsurface); 3454 perror_if_not (cairo_status (cr) == CAIRO_STATUS_SUCCESS, 3455 "%s", "Failed to create cairo context"); 3456 cairo_set_source_surface (cr, surface, -r.x, -r.y); 3457 cairo_paint (cr); 3458 cairo_surface_destroy (surface); 3459 surface = nsurface; 3460 } 3461 3462 theend: 3463 image_write_print_response (surface, PNG); 3464 3465 error: 3466 if (error_msg) g_free (error_msg); 3467 if (cr) cairo_destroy (cr); 3468 if (surface) cairo_surface_destroy (surface); 3469 if (page) g_object_unref (page); 3470 } 3471 3472 const command_arg_type_t cmd_boundingbox_spec[] = 3473 { 3474 ARG_DOC, 3475 ARG_NATNUM, /* page number */ 3476 /* region */ 3477 }; 3478 3479 static void 3480 cmd_boundingbox (const epdfinfo_t *ctx, const command_arg_t *args) 3481 { 3482 document_t *doc = args[0].value.doc; 3483 int pn = args[1].value.natnum; 3484 PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1); 3485 cairo_surface_t *surface = NULL; 3486 int width, height; 3487 double pt_width, pt_height; 3488 unsigned char *data, *data_p; 3489 PopplerRectangle bbox; 3490 int i, j; 3491 3492 perror_if_not (page, "No such page %d", pn); 3493 poppler_page_get_size (page, &pt_width, &pt_height); 3494 surface = image_render_page (doc->pdf, page, (int) pt_width, 1, 3495 &doc->options.render); 3496 3497 perror_if_not (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS, 3498 "Failed to render page"); 3499 3500 width = cairo_image_surface_get_width (surface); 3501 height = cairo_image_surface_get_height (surface); 3502 data = cairo_image_surface_get_data (surface); 3503 3504 /* Determine the bbox by comparing each pixel in the 4 corner 3505 stripes with the origin. */ 3506 for (i = 0; i < width; ++i) 3507 { 3508 data_p = data + 4 * i; 3509 for (j = 0; j < height; ++j, data_p += 4 * width) 3510 { 3511 if (! ARGB_EQUAL (data, data_p)) 3512 break; 3513 } 3514 if (j < height) 3515 break; 3516 } 3517 bbox.x1 = i; 3518 3519 for (i = width - 1; i > -1; --i) 3520 { 3521 data_p = data + 4 * i; 3522 for (j = 0; j < height; ++j, data_p += 4 * width) 3523 { 3524 if (! ARGB_EQUAL (data, data_p)) 3525 break; 3526 } 3527 if (j < height) 3528 break; 3529 } 3530 bbox.x2 = i + 1; 3531 3532 for (i = 0; i < height; ++i) 3533 { 3534 data_p = data + 4 * i * width; 3535 for (j = 0; j < width; ++j, data_p += 4) 3536 { 3537 if (! ARGB_EQUAL (data, data_p)) 3538 break; 3539 } 3540 if (j < width) 3541 break; 3542 } 3543 bbox.y1 = i; 3544 3545 for (i = height - 1; i > -1; --i) 3546 { 3547 data_p = data + 4 * i * width; 3548 for (j = 0; j < width; ++j, data_p += 4) 3549 { 3550 if (! ARGB_EQUAL (data, data_p)) 3551 break; 3552 } 3553 if (j < width) 3554 break; 3555 } 3556 bbox.y2 = i + 1; 3557 3558 OK_BEGIN (); 3559 if (bbox.x1 >= bbox.x2 || bbox.y1 >= bbox.y2) 3560 { 3561 /* empty page */ 3562 puts ("0:0:1:1"); 3563 } 3564 else 3565 { 3566 printf ("%f:%f:%f:%f\n", 3567 bbox.x1 / width, 3568 bbox.y1 / height, 3569 bbox.x2 / width, 3570 bbox.y2 / height); 3571 } 3572 OK_END (); 3573 3574 error: 3575 if (surface) cairo_surface_destroy (surface); 3576 if (page) g_object_unref (page); 3577 } 3578 3579 const command_arg_type_t cmd_charlayout_spec[] = 3580 { 3581 ARG_DOC, 3582 ARG_NATNUM, /* page number */ 3583 ARG_EDGES_OR_POSITION, /* region or position */ 3584 }; 3585 3586 static void 3587 cmd_charlayout(const epdfinfo_t *ctx, const command_arg_t *args) 3588 { 3589 PopplerDocument *doc = args[0].value.doc->pdf; 3590 int pn = args[1].value.natnum; 3591 PopplerRectangle region = args[2].value.rectangle; 3592 double width, height; 3593 PopplerPage *page = poppler_document_get_page(doc, pn - 1); 3594 char *text = NULL; 3595 char *text_p; 3596 PopplerRectangle *rectangles = NULL; 3597 guint nrectangles; 3598 int i; 3599 gboolean have_position = region.y2 < 0; 3600 3601 perror_if_not (page, "No such page %d", pn); 3602 3603 text = poppler_page_get_text (page); 3604 text_p = text; 3605 poppler_page_get_text_layout (page, &rectangles, &nrectangles); 3606 poppler_page_get_size (page, &width, &height); 3607 region.x1 *= width; 3608 region.x2 *= width; 3609 region.y1 *= height; 3610 region.y2 *= height; 3611 3612 OK_BEGIN (); 3613 for (i = 0; i < nrectangles && *text_p; ++i) 3614 { 3615 PopplerRectangle *r = &rectangles[i]; 3616 char *nextc = g_utf8_offset_to_pointer (text_p, 1); 3617 3618 if ((have_position 3619 && region.x1 >= r->x1 3620 && region.x1 <= r->x2 3621 && region.y1 >= r->y1 3622 && region.y1 <= r->y2) 3623 || (! have_position 3624 && r->x1 >= region.x1 3625 && r->y1 >= region.y1 3626 && r->x2 <= region.x2 3627 && r->y2 <= region.y2)) 3628 { 3629 char endc = *nextc; 3630 3631 printf ("%f %f %f %f:", 3632 r->x1 / width, r->y1 / height, 3633 r->x2 / width, r->y2 / height); 3634 *nextc = '\0'; 3635 print_response_string (text_p, NEWLINE); 3636 *nextc = endc; 3637 } 3638 text_p = nextc; 3639 } 3640 OK_END (); 3641 3642 g_free (rectangles); 3643 g_object_unref (page); 3644 g_free (text); 3645 3646 error: 3647 return; 3648 } 3649 3650 const document_option_t document_options [] = 3651 { 3652 DEC_DOPT (":render/usecolors", ARG_NATNUM, render.usecolors), 3653 DEC_DOPT (":render/printed", ARG_BOOL, render.printed), 3654 DEC_DOPT (":render/foreground", ARG_COLOR, render.fg), 3655 DEC_DOPT (":render/background", ARG_COLOR, render.bg), 3656 }; 3657 3658 const command_arg_type_t cmd_getoptions_spec[] = 3659 { 3660 ARG_DOC, 3661 }; 3662 3663 static void 3664 cmd_getoptions(const epdfinfo_t *ctx, const command_arg_t *args) 3665 { 3666 document_t *doc = args[0].value.doc; 3667 int i; 3668 OK_BEGIN (); 3669 for (i = 0; i < G_N_ELEMENTS (document_options); ++i) 3670 { 3671 command_arg_t arg; 3672 3673 arg.type = document_options[i].type; 3674 memcpy (&arg.value, 3675 ((char*) &doc->options) + document_options[i].offset, 3676 command_arg_type_size (arg.type)); 3677 print_response_string (document_options[i].name, COLON); 3678 command_arg_print (&arg); 3679 puts(""); 3680 } 3681 OK_END (); 3682 } 3683 3684 const command_arg_type_t cmd_setoptions_spec[] = 3685 { 3686 ARG_DOC, 3687 ARG_REST /* key value pairs */ 3688 }; 3689 3690 static void 3691 cmd_setoptions(const epdfinfo_t *ctx, const command_arg_t *args) 3692 { 3693 int i = 0; 3694 document_t *doc = args[0].value.doc; 3695 int nrest = args[1].value.rest.nargs; 3696 char * const *rest = args[1].value.rest.args; 3697 gchar *error_msg = NULL; 3698 document_options_t opts = doc->options; 3699 const size_t nopts = G_N_ELEMENTS (document_options); 3700 3701 perror_if_not (nrest % 2 == 0, "Even number of key/value pairs expected"); 3702 3703 while (i < nrest) 3704 { 3705 int j; 3706 command_arg_t key, value; 3707 3708 perror_if_not (command_arg_parse_arg 3709 (ctx, rest[i], &key, ARG_NONEMPTY_STRING, &error_msg), 3710 "%s", error_msg); 3711 3712 ++i; 3713 for (j = 0; j < nopts; ++j) 3714 { 3715 const document_option_t *dopt = &document_options[j]; 3716 if (! strcmp (key.value.string, dopt->name)) 3717 { 3718 perror_if_not (command_arg_parse_arg 3719 (ctx, rest[i], &value, dopt->type, &error_msg), 3720 "%s", error_msg); 3721 memcpy (((char*) &opts) + dopt->offset, 3722 &value.value, command_arg_type_size (value.type)); 3723 break; 3724 } 3725 } 3726 perror_if_not (j < nopts, "Unknown option: %s", key.value.string); 3727 ++i; 3728 } 3729 doc->options = opts; 3730 cmd_getoptions (ctx, args); 3731 3732 error: 3733 if (error_msg) g_free (error_msg); 3734 } 3735 3736 const command_arg_type_t cmd_pagelabels_spec[] = 3737 { 3738 ARG_DOC, 3739 }; 3740 3741 static void 3742 cmd_pagelabels(const epdfinfo_t *ctx, const command_arg_t *args) 3743 { 3744 PopplerDocument *doc = args[0].value.doc->pdf; 3745 int i; 3746 3747 OK_BEGIN (); 3748 for (i = 0; i < poppler_document_get_n_pages (doc); ++i) 3749 { 3750 PopplerPage *page = poppler_document_get_page(doc, i); 3751 gchar *label = poppler_page_get_label (page); 3752 3753 print_response_string (label ? label : "", NEWLINE); 3754 g_object_unref (page); 3755 g_free (label); 3756 } 3757 OK_END (); 3758 } 3759 3760 const command_arg_type_t cmd_ping_spec[] = 3761 { 3762 ARG_STRING /* any message */ 3763 }; 3764 3765 static void 3766 cmd_ping (const epdfinfo_t *ctx, const command_arg_t *args) 3767 { 3768 const gchar *msg = args[0].value.string; 3769 OK_BEGIN (); 3770 print_response_string (msg, NEWLINE); 3771 OK_END (); 3772 } 3773 3774 3775 /* ================================================================== * 3776 * Main 3777 * ================================================================== */ 3778 3779 static const command_t commands [] = 3780 { 3781 /* Basic */ 3782 DEC_CMD (ping), 3783 DEC_CMD (features), 3784 DEC_CMD (open), 3785 DEC_CMD (close), 3786 DEC_CMD (quit), 3787 DEC_CMD (getoptions), 3788 DEC_CMD (setoptions), 3789 3790 /* Searching */ 3791 DEC_CMD2 (search_string, "search-string"), 3792 DEC_CMD2 (search_regexp, "search-regexp"), 3793 DEC_CMD2 (regexp_flags, "regexp-flags"), 3794 3795 /* General Information */ 3796 DEC_CMD (metadata), 3797 DEC_CMD (outline), 3798 DEC_CMD2 (number_of_pages, "number-of-pages"), 3799 DEC_CMD (pagelinks), 3800 DEC_CMD (gettext), 3801 DEC_CMD (getselection), 3802 DEC_CMD (pagesize), 3803 DEC_CMD (boundingbox), 3804 DEC_CMD (charlayout), 3805 3806 /* General Information */ 3807 DEC_CMD (metadata), 3808 DEC_CMD (outline), 3809 DEC_CMD2 (number_of_pages, "number-of-pages"), 3810 DEC_CMD (pagelinks), 3811 DEC_CMD (gettext), 3812 DEC_CMD (getselection), 3813 DEC_CMD (pagesize), 3814 DEC_CMD (boundingbox), 3815 DEC_CMD (charlayout), 3816 DEC_CMD (pagelabels), 3817 3818 /* Annotations */ 3819 DEC_CMD (getannots), 3820 DEC_CMD (getannot), 3821 #ifdef HAVE_POPPLER_ANNOT_WRITE 3822 DEC_CMD (addannot), 3823 DEC_CMD (delannot), 3824 DEC_CMD (editannot), 3825 DEC_CMD (save), 3826 #endif 3827 3828 /* Attachments */ 3829 DEC_CMD2 (getattachment_from_annot, "getattachment-from-annot"), 3830 DEC_CMD (getattachments), 3831 3832 /* Synctex */ 3833 DEC_CMD2 (synctex_forward_search, "synctex-forward-search"), 3834 DEC_CMD2 (synctex_backward_search, "synctex-backward-search"), 3835 3836 /* Rendering */ 3837 DEC_CMD (renderpage), 3838 }; 3839 3840 int main(int argc, char **argv) 3841 { 3842 epdfinfo_t ctx = {0}; 3843 char *line = NULL; 3844 ssize_t read; 3845 size_t line_size; 3846 const char *error_log = "/dev/null"; 3847 3848 #ifdef __MINGW32__ 3849 error_log = "NUL"; 3850 _setmode(_fileno(stdin), _O_BINARY); 3851 _setmode(_fileno(stdout), _O_BINARY); 3852 #endif 3853 3854 if (argc > 2) 3855 { 3856 fprintf(stderr, "usage: epdfinfo [ERROR-LOGFILE]\n"); 3857 exit (EXIT_FAILURE); 3858 } 3859 if (argc == 2) 3860 error_log = argv[1]; 3861 3862 if (! freopen (error_log, "a", stderr)) 3863 err (2, "Unable to redirect stderr"); 3864 3865 #if ! GLIB_CHECK_VERSION(2,36,0) 3866 g_type_init (); 3867 #endif 3868 3869 ctx.documents = g_hash_table_new (g_str_hash, g_str_equal); 3870 3871 setvbuf (stdout, NULL, _IOFBF, BUFSIZ); 3872 3873 while ((read = getline (&line, &line_size, stdin)) != -1) 3874 { 3875 int nargs = 0; 3876 command_arg_t *cmd_args = NULL; 3877 char **args = NULL; 3878 gchar *error_msg = NULL; 3879 int i; 3880 3881 if (read <= 1 || line[read - 1] != '\n') 3882 { 3883 fprintf (stderr, "Skipped parts of a line: `%s'\n", line); 3884 goto next_line; 3885 } 3886 3887 line[read - 1] = '\0'; 3888 args = command_arg_split (line, &nargs); 3889 if (nargs == 0) 3890 continue; 3891 3892 for (i = 0; i < G_N_ELEMENTS (commands); i++) 3893 { 3894 if (! strcmp (commands[i].name, args[0])) 3895 { 3896 if (commands[i].nargs == 0 3897 || (cmd_args = command_arg_parse (&ctx, args + 1, nargs - 1, 3898 commands + i, &error_msg))) 3899 { 3900 commands[i].execute (&ctx, cmd_args); 3901 if (commands[i].nargs > 0) 3902 free_command_args (cmd_args, commands[i].nargs); 3903 } 3904 else 3905 { 3906 printf_error_response ("%s", error_msg ? error_msg : 3907 "Unknown error (this is a bug)"); 3908 } 3909 if (error_msg) 3910 g_free (error_msg); 3911 break; 3912 } 3913 } 3914 if (G_N_ELEMENTS (commands) == i) 3915 { 3916 printf_error_response ("Unknown command: %s", args[0]); 3917 } 3918 for (i = 0; i < nargs; ++i) 3919 g_free (args[i]); 3920 g_free (args); 3921 next_line: 3922 free (line); 3923 line = NULL; 3924 } 3925 3926 if (ferror (stdin)) 3927 err (2, NULL); 3928 exit (EXIT_SUCCESS); 3929 }