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