epdfinfo.c (99693B)
1 /* Copyright (C) 2013, 2014 Andreas Politz 2 * 3 * Author: Andreas Politz <politza@fh-trier.de> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18 #include <config.h> 19 20 #include <assert.h> 21 #ifdef HAVE_ERR_H 22 # include <err.h> 23 #endif 24 #ifdef HAVE_ERROR_H 25 # include <error.h> 26 #endif 27 #include <glib.h> 28 #include <poppler.h> 29 #include <cairo.h> 30 #include <stdarg.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <strings.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <errno.h> 39 #include <png.h> 40 #include <math.h> 41 #include <regex.h> 42 #include "synctex_parser.h" 43 #include "epdfinfo.h" 44 45 46 /* ================================================================== * 47 * Helper Functions 48 * ================================================================== */ 49 50 #ifndef HAVE_ERR_H 51 /** 52 * Print error message and quit. 53 * 54 * @param eval Return code 55 * @param fmt Formatting string 56 */ 57 static void 58 err(int eval, const char *fmt, ...) 59 { 60 va_list args; 61 62 fprintf (stderr, "epdfinfo: "); 63 if (fmt != NULL) 64 { 65 va_start (args, fmt); 66 vfprintf (stderr, fmt, args); 67 va_end (args); 68 fprintf (stderr, ": %s\n", strerror(errno)); 69 } 70 else 71 { 72 fprintf (stderr, "\n"); 73 } 74 75 fflush (stderr); 76 exit (eval); 77 } 78 #endif 79 80 #ifndef HAVE_GETLINE 81 /** 82 * Read one line from a file. 83 * 84 * @param lineptr Pointer to malloc() allocated buffer 85 * @param n Pointer to size of buffer 86 * @param stream File pointer to read from 87 */ 88 static ssize_t 89 getline(char **lineptr, size_t *n, FILE *stream) 90 { 91 size_t len = 0; 92 int ch; 93 94 if ((lineptr == NULL) || (n == NULL)) 95 { 96 errno = EINVAL; 97 return -1; 98 } 99 100 if (*lineptr == NULL) 101 { 102 *lineptr = malloc (128); 103 *n = 128; 104 } 105 106 while ((ch = fgetc (stream)) != EOF) 107 { 108 (*lineptr)[len] = ch; 109 110 if (++len >= *n) 111 { 112 *n += 128; 113 *lineptr = realloc (*lineptr, *n); 114 } 115 116 if (ch == '\n') 117 break; 118 } 119 (*lineptr)[len] = '\0'; 120 121 if (!len) 122 { 123 len = -1; 124 } 125 126 return len; 127 } 128 #endif 129 130 /** 131 * Free a list of command arguments. 132 * 133 * @param args An array of command arguments. 134 * @param n The length of the array. 135 */ 136 static void 137 free_command_args (command_arg_t *args, size_t n) 138 { 139 if (! args) 140 return; 141 142 g_free (args); 143 } 144 145 /** 146 * Free resources held by document. 147 * 148 * @param doc The document to be freed. 149 */ 150 static void 151 free_document (document_t *doc) 152 { 153 if (! doc) 154 return; 155 156 g_free (doc->filename); 157 g_free (doc->passwd); 158 if (doc->annotations.pages) 159 { 160 int npages = poppler_document_get_n_pages (doc->pdf); 161 int i; 162 for (i = 0; i < npages; ++i) 163 { 164 GList *item; 165 GList *annots = doc->annotations.pages[i]; 166 for (item = annots; item; item = item->next) 167 { 168 annotation_t *a = (annotation_t*) item->data; 169 poppler_annot_mapping_free(a->amap); 170 g_free (a->key); 171 g_free (a); 172 } 173 g_list_free (annots); 174 } 175 g_hash_table_destroy (doc->annotations.keys); 176 g_free (doc->annotations.pages); 177 } 178 g_object_unref (doc->pdf); 179 g_free (doc); 180 } 181 182 /** 183 * Parse a list of whitespace separated double values. 184 * 185 * @param str The input string. 186 * @param values[out] Values are put here. 187 * @param nvalues How many values to parse. 188 * 189 * @return TRUE, if str contained exactly nvalues, else FALSE. 190 */ 191 static gboolean 192 parse_double_list (const char *str, gdouble *values, size_t nvalues) 193 { 194 char *end; 195 int i; 196 197 if (! str) 198 return FALSE; 199 200 errno = 0; 201 for (i = 0; i < nvalues; ++i) 202 { 203 gdouble n = g_ascii_strtod (str, &end); 204 205 if (str == end || errno) 206 return FALSE; 207 208 values[i] = n; 209 str = end; 210 } 211 212 if (*end) 213 return FALSE; 214 215 return TRUE; 216 } 217 218 static gboolean 219 parse_rectangle (const char *str, PopplerRectangle *r) 220 { 221 gdouble values[4]; 222 223 if (! r) 224 return FALSE; 225 226 if (! parse_double_list (str, values, 4)) 227 return FALSE; 228 229 r->x1 = values[0]; 230 r->y1 = values[1]; 231 r->x2 = values[2]; 232 r->y2 = values[3]; 233 234 return TRUE; 235 } 236 237 static gboolean 238 parse_edges_or_position (const char *str, PopplerRectangle *r) 239 { 240 return (parse_rectangle (str, r) 241 && r->x1 >= 0 && r->x1 <= 1 242 && r->x2 <= 1 243 && r->y1 >= 0 && r->y1 <= 1 244 && r->y2 <= 1); 245 } 246 247 static gboolean 248 parse_edges (const char *str, PopplerRectangle *r) 249 { 250 return (parse_rectangle (str, r) 251 && r->x1 >= 0 && r->x1 <= 1 252 && r->x2 >= 0 && r->x2 <= 1 253 && r->y1 >= 0 && r->y1 <= 1 254 && r->y2 >= 0 && r->y2 <= 1); 255 } 256 257 /** 258 * Print a string properly escaped for a response. 259 * 260 * @param str The string to be printed. 261 * @param suffix_char Append a newline if NEWLINE, a colon if COLON. 262 */ 263 static void 264 print_response_string (const char *str, enum suffix_char suffix) 265 { 266 if (str) 267 { 268 while (*str) 269 { 270 switch (*str) 271 { 272 case '\n': 273 printf ("\\n"); 274 break; 275 case '\\': 276 printf ("\\\\"); 277 break; 278 case ':': 279 printf ("\\:"); 280 break; 281 default: 282 putchar (*str); 283 } 284 ++str; 285 } 286 } 287 288 switch (suffix) 289 { 290 case NEWLINE: 291 putchar ('\n'); 292 break; 293 case COLON: 294 putchar (':'); 295 break; 296 default: ; 297 } 298 } 299 300 301 /** 302 * Print a formatted error response. 303 * 304 * @param fmt The printf-like format string. 305 */ 306 static void 307 printf_error_response (const char *fmt, ...) 308 { 309 va_list va; 310 puts ("ERR"); 311 va_start (va, fmt); 312 vprintf (fmt, va); 313 va_end (va); 314 puts ("\n."); 315 fflush (stdout); 316 } 317 318 /** 319 * Remove one trailing newline character. Does nothing, if str does 320 * not end with a newline. 321 * 322 * @param str The string. 323 * 324 * @return str with trailing newline removed. 325 */ 326 static char* 327 strchomp (char *str) 328 { 329 size_t length; 330 331 if (! str) 332 return str; 333 334 length = strlen (str); 335 if (str[length - 1] == '\n') 336 str[length - 1] = '\0'; 337 338 return str; 339 } 340 341 /** 342 * Create a new, temporary file and returns its name. 343 * 344 * @return The filename. 345 */ 346 static char* 347 mktempfile() 348 { 349 char *filename = NULL; 350 int tries = 3; 351 while (! filename && tries-- > 0) 352 { 353 354 filename = tempnam(NULL, "epdfinfo"); 355 if (filename) 356 { 357 int fd = open(filename, O_CREAT | O_EXCL | O_RDONLY, S_IRUSR | S_IWUSR); 358 if (fd > 0) 359 close (fd); 360 else 361 { 362 free (filename); 363 filename = NULL; 364 } 365 } 366 } 367 if (! filename) 368 fprintf (stderr, "Unable to create tempfile"); 369 370 return filename; 371 } 372 373 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 Its 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 Its 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 Its 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 static cairo_region_t* 1313 annotation_markup_get_text_regions (PopplerPage *page, PopplerAnnotTextMarkup *a) 1314 { 1315 GArray *quads = poppler_annot_text_markup_get_quadrilaterals (a); 1316 int i; 1317 cairo_region_t *region = cairo_region_create (); 1318 gdouble height; 1319 1320 poppler_page_get_size (page, NULL, &height); 1321 1322 for (i = 0; i < quads->len; ++i) 1323 { 1324 PopplerQuadrilateral *q = &g_array_index (quads, PopplerQuadrilateral, i); 1325 cairo_rectangle_int_t r; 1326 1327 q->p1.y = height - q->p1.y; 1328 q->p2.y = height - q->p2.y; 1329 q->p3.y = height - q->p3.y; 1330 q->p4.y = height - q->p4.y; 1331 1332 r.x = (int) (MIN (q->p1.x, MIN (q->p2.x, MIN (q->p3.x, q->p4.x))) + 0.5); 1333 r.y = (int) (MIN (q->p1.y, MIN (q->p2.y, MIN (q->p3.y, q->p4.y))) + 0.5); 1334 r.width = (int) (MAX (q->p1.x, MAX (q->p2.x, MAX (q->p3.x, q->p4.x))) + 0.5) 1335 - r.x; 1336 r.height = (int) (MAX (q->p1.y, MAX (q->p2.y, MAX (q->p3.y, q->p4.y))) + 0.5) 1337 - r.y; 1338 1339 cairo_region_union_rectangle (region, &r); 1340 } 1341 g_array_unref (quads); 1342 return region; 1343 } 1344 1345 /** 1346 * Append quadrilaterals equivalent to region to an array. 1347 * 1348 * @param page The page of the annotation. This is used to get the 1349 * text regions and pagesize. 1350 * @param region The region to add. 1351 * @param garray[in,out] An array of PopplerQuadrilateral, where the 1352 * new quadrilaterals will be appended. 1353 */ 1354 static void 1355 annotation_markup_append_text_region (PopplerPage *page, PopplerRectangle *region, 1356 GArray *garray) 1357 { 1358 gdouble height; 1359 /* poppler_page_get_selection_region is deprecated w/o a 1360 replacement. (poppler_page_get_selected_region returns a union 1361 of rectangles.) */ 1362 GList *regions = 1363 poppler_page_get_selection_region (page, 1.0, POPPLER_SELECTION_GLYPH, region); 1364 GList *item; 1365 1366 poppler_page_get_size (page, NULL, &height); 1367 for (item = regions; item; item = item->next) 1368 { 1369 PopplerRectangle *r = item->data; 1370 PopplerQuadrilateral q; 1371 1372 q.p1.x = r->x1; 1373 q.p1.y = height - r->y1; 1374 q.p2.x = r->x2; 1375 q.p2.y = height - r->y1; 1376 q.p4.x = r->x2; 1377 q.p4.y = height - r->y2; 1378 q.p3.x = r->x1; 1379 q.p3.y = height - r->y2; 1380 1381 g_array_append_val (garray, q); 1382 } 1383 g_list_free (regions); 1384 } 1385 1386 #endif 1387 /** 1388 * Create a new annotation. 1389 * 1390 * @param doc The document for which to create it. 1391 * @param type The type of the annotation. 1392 * @param r The rectangle where annotation will end up on the page. 1393 * 1394 * @return The new annotation, or NULL, if the annotation type is 1395 * not available. 1396 */ 1397 static PopplerAnnot* 1398 annotation_new (const epdfinfo_t *ctx, document_t *doc, PopplerPage *page, 1399 const char *type, PopplerRectangle *r, 1400 const command_arg_t *rest, char **error_msg) 1401 { 1402 1403 PopplerAnnot *a = NULL; 1404 int nargs = rest->value.rest.nargs; 1405 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1406 char * const *args = rest->value.rest.args; 1407 int i; 1408 GArray *garray = NULL; 1409 command_arg_t carg; 1410 double width, height; 1411 cairo_region_t *region = NULL; 1412 #endif 1413 1414 if (! strcmp (type, "text")) 1415 { 1416 cerror_if_not (nargs == 0, error_msg, "%s", "Too many arguments"); 1417 return poppler_annot_text_new (doc->pdf, r); 1418 } 1419 1420 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1421 garray = g_array_new (FALSE, FALSE, sizeof (PopplerQuadrilateral)); 1422 poppler_page_get_size (page, &width, &height); 1423 for (i = 0; i < nargs; ++i) 1424 { 1425 PopplerRectangle *rr = &carg.value.rectangle; 1426 1427 error_if_not (command_arg_parse_arg (ctx, args[i], &carg, 1428 ARG_EDGES, error_msg)); 1429 rr->x1 *= width; rr->x2 *= width; 1430 rr->y1 *= height; rr->y2 *= height; 1431 annotation_markup_append_text_region (page, rr, garray); 1432 } 1433 cerror_if_not (garray->len > 0, error_msg, "%s", 1434 "Unable to create empty markup annotation"); 1435 1436 if (! strcmp (type, "highlight")) 1437 a = poppler_annot_text_markup_new_highlight (doc->pdf, r, garray); 1438 else if (! strcmp (type, "squiggly")) 1439 a = poppler_annot_text_markup_new_squiggly (doc->pdf, r, garray); 1440 else if (! strcmp (type, "strike-out")) 1441 a = poppler_annot_text_markup_new_strikeout (doc->pdf, r, garray); 1442 else if (! strcmp (type, "underline")) 1443 a = poppler_annot_text_markup_new_underline (doc->pdf, r, garray); 1444 else 1445 cerror_if_not (0, error_msg, "Unknown annotation type: %s", type); 1446 1447 #endif 1448 error: 1449 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1450 if (garray) g_array_unref (garray); 1451 if (region) cairo_region_destroy (region); 1452 #endif 1453 return a; 1454 } 1455 1456 static gboolean 1457 annotation_edit_validate (const epdfinfo_t *ctx, const command_arg_t *rest, 1458 PopplerAnnot *annotation, char **error_msg) 1459 { 1460 int nargs = rest->value.rest.nargs; 1461 char * const *args = rest->value.rest.args; 1462 int i = 0; 1463 command_arg_t carg; 1464 1465 const char* error_fmt = 1466 "Can modify `%s' property only for %s annotations"; 1467 1468 while (i < nargs) 1469 { 1470 command_arg_type_t atype = ARG_INVALID; 1471 const char *key = args[i++]; 1472 1473 cerror_if_not (i < nargs, error_msg, "Missing a value argument"); 1474 1475 if (! strcmp (key, "flags")) 1476 atype = ARG_NATNUM; 1477 else if (! strcmp (key, "color")) 1478 atype = ARG_COLOR; 1479 else if (! strcmp (key, "contents")) 1480 atype = ARG_STRING; 1481 else if (! strcmp (key, "edges")) 1482 atype = ARG_EDGES_OR_POSITION; 1483 else if (! strcmp (key, "label")) 1484 { 1485 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1486 error_fmt, key, "markup"); 1487 atype = ARG_STRING; 1488 } 1489 else if (! strcmp (key, "opacity")) 1490 { 1491 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1492 error_fmt, key, "markup"); 1493 atype = ARG_EDGE; 1494 } 1495 else if (! strcmp (key, "popup")) 1496 { 1497 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1498 error_fmt, key, "markup"); 1499 atype = ARG_EDGES; 1500 } 1501 else if (! strcmp (key, "popup-is-open")) 1502 { 1503 cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg, 1504 error_fmt, key, "markup"); 1505 atype = ARG_BOOL; 1506 } 1507 else if (! strcmp (key, "icon")) 1508 { 1509 cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg, 1510 error_fmt, key, "text"); 1511 atype = ARG_STRING; 1512 } 1513 else if (! strcmp (key, "is-open")) 1514 { 1515 cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg, 1516 error_fmt, key, "text"); 1517 atype = ARG_BOOL; 1518 } 1519 else 1520 { 1521 cerror_if_not (0, error_msg, 1522 "Unable to modify property `%s'", key); 1523 } 1524 1525 if (! command_arg_parse_arg (ctx, args[i++], &carg, atype, error_msg)) 1526 return FALSE; 1527 } 1528 1529 return TRUE; 1530 1531 error: 1532 return FALSE; 1533 } 1534 1535 static void 1536 annotation_print (const annotation_t *annot, /* const */ PopplerPage *page) 1537 { 1538 double width, height; 1539 PopplerAnnotMapping *m; 1540 const gchar *key; 1541 PopplerAnnot *a; 1542 PopplerAnnotMarkup *ma; 1543 PopplerAnnotText *ta; 1544 PopplerRectangle r; 1545 PopplerColor *color; 1546 gchar *text; 1547 gdouble opacity; 1548 cairo_region_t *region = NULL; 1549 GDate *date; 1550 1551 if (! annot || ! page) 1552 return; 1553 1554 m = annot->amap; 1555 key = annot->key; 1556 a = m->annot; 1557 poppler_page_get_size (page, &width, &height); 1558 1559 r.x1 = m->area.x1; 1560 r.x2 = m->area.x2; 1561 r.y1 = height - m->area.y2; 1562 r.y2 = height - m->area.y1; 1563 1564 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1565 if (POPPLER_IS_ANNOT_TEXT_MARKUP (a)) 1566 { 1567 region = annotation_markup_get_text_regions (page, POPPLER_ANNOT_TEXT_MARKUP (a)); 1568 perror_if_not (region, "%s", "Unable to extract annotation's text regions"); 1569 } 1570 #endif 1571 1572 /* >>> Any Annotation >>> */ 1573 /* Page */ 1574 printf ("%d:", poppler_page_get_index (page) + 1); 1575 /* Area */ 1576 printf ("%f %f %f %f:", r.x1 / width, r.y1 / height 1577 , r.x2 / width, r.y2 / height); 1578 1579 /* Type */ 1580 printf ("%s:", xpoppler_annot_type_string (poppler_annot_get_annot_type (a))); 1581 /* Internal Key */ 1582 print_response_string (key, COLON); 1583 1584 /* Flags */ 1585 printf ("%d:", poppler_annot_get_flags (a)); 1586 1587 /* Color */ 1588 color = poppler_annot_get_color (a); 1589 if (color) 1590 { 1591 /* Reduce 2 Byte to 1 Byte color space */ 1592 printf ("#%.2x%.2x%.2x", (color->red >> 8) 1593 , (color->green >> 8) 1594 , (color->blue >> 8)); 1595 g_free (color); 1596 } 1597 1598 putchar (':'); 1599 1600 /* Text Contents */ 1601 text = poppler_annot_get_contents (a); 1602 print_response_string (text, COLON); 1603 g_free (text); 1604 1605 /* Modified Date */ 1606 text = poppler_annot_get_modified (a); 1607 print_response_string (text, NONE); 1608 g_free (text); 1609 1610 /* <<< Any Annotation <<< */ 1611 1612 /* >>> Markup Annotation >>> */ 1613 if (! POPPLER_IS_ANNOT_MARKUP (a)) 1614 { 1615 putchar ('\n'); 1616 goto theend; 1617 } 1618 1619 putchar (':'); 1620 ma = POPPLER_ANNOT_MARKUP (a); 1621 /* Label */ 1622 text = poppler_annot_markup_get_label (ma); 1623 print_response_string (text, COLON); 1624 g_free (text); 1625 1626 /* Subject */ 1627 text = poppler_annot_markup_get_subject (ma); 1628 print_response_string (text, COLON); 1629 g_free (text); 1630 1631 /* Opacity */ 1632 opacity = poppler_annot_markup_get_opacity (ma); 1633 printf ("%f:", opacity); 1634 1635 /* Popup (Area + isOpen) */ 1636 if (poppler_annot_markup_has_popup (ma) 1637 && poppler_annot_markup_get_popup_rectangle (ma, &r)) 1638 { 1639 gdouble tmp = r.y1; 1640 r.y1 = height - r.y2; 1641 r.y2 = height - tmp; 1642 printf ("%f %f %f %f:%d:", r.x1 / width, r.y1 / height 1643 , r.x2 / width, r.y2 / height 1644 , poppler_annot_markup_get_popup_is_open (ma) ? 1 : 0); 1645 1646 } 1647 else 1648 printf ("::"); 1649 1650 /* Creation Date */ 1651 date = poppler_annot_markup_get_date (ma); 1652 if (date != NULL && g_date_valid(date)) 1653 { 1654 gchar datebuf[128]; 1655 g_date_strftime (datebuf, 127, "%x", date); 1656 print_response_string (datebuf, NONE); 1657 g_date_free (date); 1658 } 1659 1660 /* <<< Markup Annotation <<< */ 1661 1662 /* >>> Text Annotation >>> */ 1663 if (POPPLER_IS_ANNOT_TEXT (a)) 1664 { 1665 putchar (':'); 1666 ta = POPPLER_ANNOT_TEXT (a); 1667 /* Text Icon */ 1668 text = poppler_annot_text_get_icon (ta); 1669 print_response_string (text, COLON); 1670 g_free (text); 1671 /* Text State */ 1672 printf ("%s:%d", 1673 xpoppler_annot_text_state_string (poppler_annot_text_get_state (ta)), 1674 poppler_annot_text_get_is_open (ta)); 1675 } 1676 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1677 /* <<< Text Annotation <<< */ 1678 else if (POPPLER_IS_ANNOT_TEXT_MARKUP (a)) 1679 { 1680 /* >>> Markup Text Annotation >>> */ 1681 putchar (':'); 1682 region_print (region, width, height); 1683 /* <<< Markup Text Annotation <<< */ 1684 } 1685 #endif 1686 putchar ('\n'); 1687 theend: 1688 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1689 error: 1690 #endif 1691 if (region) cairo_region_destroy (region); 1692 } 1693 1694 static void 1695 attachment_print (PopplerAttachment *att, const char *id, gboolean do_save) 1696 { 1697 time_t time; 1698 1699 print_response_string (id, COLON); 1700 print_response_string (att->name, COLON); 1701 print_response_string (att->description, COLON); 1702 if (att->size + 1 != 0) 1703 printf ("%" G_GSIZE_FORMAT ":", att->size); 1704 else 1705 printf ("-1:"); 1706 time = (time_t) att->mtime; 1707 print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON); 1708 time = (time_t) att->ctime; 1709 print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON); 1710 print_response_string (att->checksum ? att->checksum->str : "" , COLON); 1711 if (do_save) 1712 { 1713 char *filename = mktempfile (); 1714 GError *error = NULL; 1715 if (filename) 1716 { 1717 if (! poppler_attachment_save (att, filename, &error)) 1718 { 1719 fprintf (stderr, "Writing attachment failed: %s" 1720 , error ? error->message : "reason unknown"); 1721 if (error) 1722 g_free (error); 1723 } 1724 else 1725 { 1726 print_response_string (filename, NONE); 1727 } 1728 free (filename); 1729 } 1730 } 1731 putchar ('\n'); 1732 } 1733 1734 1735 1736 /* ================================================================== * 1737 * Server command implementations 1738 * ================================================================== */ 1739 1740 /* Name: features 1741 Args: None 1742 Returns: A list of compile-time features. 1743 Errors: None 1744 */ 1745 1746 const command_arg_type_t cmd_features_spec[] = {}; 1747 1748 static void 1749 cmd_features (const epdfinfo_t *ctx, const command_arg_t *args) 1750 { 1751 const char *features[] = { 1752 #ifdef HAVE_POPPLER_FIND_OPTS 1753 "case-sensitive-search", 1754 #else 1755 "no-case-sensitive-search", 1756 #endif 1757 #ifdef HAVE_POPPLER_ANNOT_WRITE 1758 "writable-annotations", 1759 #else 1760 "no-writable-annotations", 1761 #endif 1762 #ifdef HAVE_POPPLER_ANNOT_MARKUP 1763 "markup-annotations" 1764 #else 1765 "no-markup-annotations" 1766 #endif 1767 }; 1768 int i; 1769 OK_BEGIN (); 1770 for (i = 0; i < G_N_ELEMENTS (features); ++i) 1771 { 1772 printf ("%s", features[i]); 1773 if (i < G_N_ELEMENTS (features) - 1) 1774 putchar (':'); 1775 } 1776 putchar ('\n'); 1777 OK_END (); 1778 } 1779 1780 1781 /* Name: open 1782 Args: filename password 1783 Returns: Nothing 1784 Errors: If file can't be opened or is not a PDF document. 1785 */ 1786 1787 const command_arg_type_t cmd_open_spec[] = 1788 { 1789 ARG_NONEMPTY_STRING, /* filename */ 1790 ARG_STRING, /* password */ 1791 }; 1792 1793 static void 1794 cmd_open (const epdfinfo_t *ctx, const command_arg_t *args) 1795 { 1796 const char *filename = args[0].value.string; 1797 const char *passwd = args[1].value.string; 1798 GError *gerror = NULL; 1799 document_t *doc; 1800 1801 if (! *passwd) 1802 passwd = NULL; 1803 1804 doc = document_open(ctx, filename, passwd, &gerror); 1805 perror_if_not (doc, "Error opening %s:%s", filename, 1806 gerror ? gerror->message : "unknown error"); 1807 OK (); 1808 1809 error: 1810 if (gerror) 1811 { 1812 g_error_free (gerror); 1813 gerror = NULL; 1814 } 1815 } 1816 1817 /* Name: close 1818 Args: filename 1819 Returns: 1 if file was open, otherwise 0. 1820 Errors: None 1821 */ 1822 1823 const command_arg_type_t cmd_close_spec[] = 1824 { 1825 ARG_NONEMPTY_STRING /* filename */ 1826 }; 1827 1828 static void 1829 cmd_close (const epdfinfo_t *ctx, const command_arg_t *args) 1830 { 1831 document_t *doc = g_hash_table_lookup(ctx->documents, args->value.string); 1832 1833 g_hash_table_remove (ctx->documents, args->value.string); 1834 free_document (doc); 1835 OK_BEGIN (); 1836 puts (doc ? "1" : "0"); 1837 OK_END (); 1838 } 1839 1840 /* Name: closeall 1841 Args: None 1842 Returns: Nothing 1843 Errors: None 1844 */ 1845 static void 1846 cmd_closeall (const epdfinfo_t *ctx, const command_arg_t *args) 1847 { 1848 GHashTableIter iter; 1849 gpointer key, value; 1850 1851 g_hash_table_iter_init (&iter, ctx->documents); 1852 while (g_hash_table_iter_next (&iter, &key, &value)) 1853 { 1854 document_t *doc = (document_t*) value; 1855 free_document (doc); 1856 g_hash_table_iter_remove (&iter); 1857 } 1858 OK (); 1859 } 1860 1861 1862 const command_arg_type_t cmd_search_regexp_spec[] = 1863 { 1864 ARG_DOC, 1865 ARG_NATNUM, /* first page */ 1866 ARG_NATNUM, /* last page */ 1867 ARG_NONEMPTY_STRING, /* regexp */ 1868 ARG_NATNUM, /* compile flags */ 1869 ARG_NATNUM /* match flags */ 1870 }; 1871 1872 static void 1873 cmd_search_regexp(const epdfinfo_t *ctx, const command_arg_t *args) 1874 { 1875 PopplerDocument *doc = args[0].value.doc->pdf; 1876 int first = args[1].value.natnum; 1877 int last = args[2].value.natnum; 1878 const gchar *regexp = args[3].value.string; 1879 GRegexCompileFlags cflags = args[4].value.natnum; 1880 GRegexMatchFlags mflags = args[5].value.natnum; 1881 double width, height; 1882 int pn; 1883 GError *gerror = NULL; 1884 GRegex *re = NULL; 1885 1886 NORMALIZE_PAGE_ARG (doc, &first, &last); 1887 1888 re = g_regex_new (regexp, cflags, mflags, &gerror); 1889 perror_if_not (NULL == gerror, "Invalid regexp: %s", gerror->message); 1890 1891 OK_BEGIN (); 1892 for (pn = first; pn <= last; ++pn) 1893 { 1894 PopplerPage *page = poppler_document_get_page(doc, pn - 1); 1895 char *text; 1896 PopplerRectangle *rectangles = NULL; 1897 guint nrectangles; 1898 GMatchInfo *match = NULL; 1899 1900 if (! page) 1901 continue; 1902 1903 text = poppler_page_get_text (page); 1904 poppler_page_get_text_layout (page, &rectangles, &nrectangles); 1905 poppler_page_get_size (page, &width, &height); 1906 g_regex_match (re, text, 0, &match); 1907 1908 while (g_match_info_matches (match)) 1909 { 1910 const double scale = 100.0; 1911 gint start, end, ustart, ulen; 1912 gchar *string = NULL; 1913 gchar *line = NULL; 1914 int i; 1915 1916 /* Does this ever happen ? */ 1917 if (! g_match_info_fetch_pos (match, 0, &start, &end)) 1918 continue; 1919 1920 string = g_match_info_fetch (match, 0); 1921 ustart = g_utf8_strlen (text, start); 1922 ulen = g_utf8_strlen (string, -1); 1923 1924 cairo_region_t *region = cairo_region_create (); 1925 /* Merge matched glyph rectangles. Scale them so we're able 1926 to use cairo . */ 1927 if (ulen > 0) 1928 { 1929 assert (ustart < nrectangles 1930 && ustart + ulen <= nrectangles); 1931 line = poppler_page_get_selected_text 1932 (page, POPPLER_SELECTION_LINE, rectangles + ustart); 1933 1934 for (i = ustart; i < ustart + ulen; ++i) 1935 { 1936 PopplerRectangle *r = rectangles + i; 1937 cairo_rectangle_int_t c; 1938 1939 c.x = (int) (scale * r->x1 + 0.5); 1940 c.y = (int) (scale * r->y1 + 0.5); 1941 c.width = (int) (scale * (r->x2 - r->x1) + 0.5); 1942 c.height = (int) (scale * (r->y2 - r->y1) + 0.5); 1943 1944 cairo_region_union_rectangle (region, &c); 1945 } 1946 1947 } 1948 1949 printf ("%d:", pn); 1950 print_response_string (string, COLON); 1951 print_response_string (strchomp (line), COLON); 1952 region_print (region, width * scale, height * scale); 1953 putchar ('\n'); 1954 cairo_region_destroy (region); 1955 g_free (string); 1956 g_free (line); 1957 g_match_info_next (match, NULL); 1958 } 1959 g_free (rectangles); 1960 g_object_unref (page); 1961 g_free (text); 1962 g_match_info_free (match); 1963 } 1964 OK_END (); 1965 1966 error: 1967 if (re) g_regex_unref (re); 1968 if (gerror) g_error_free (gerror); 1969 } 1970 1971 const command_arg_type_t cmd_regexp_flags_spec[] = 1972 { 1973 }; 1974 1975 static void 1976 cmd_regexp_flags (const epdfinfo_t *ctx, const command_arg_t *args) 1977 { 1978 OK_BEGIN (); 1979 printf ("caseless:%d\n", G_REGEX_CASELESS); 1980 printf ("multiline:%d\n", G_REGEX_MULTILINE); 1981 printf ("dotall:%d\n", G_REGEX_DOTALL); 1982 printf ("extended:%d\n", G_REGEX_EXTENDED); 1983 printf ("anchored:%d\n", G_REGEX_ANCHORED); 1984 printf ("dollar-endonly:%d\n", G_REGEX_DOLLAR_ENDONLY); 1985 printf ("ungreedy:%d\n", G_REGEX_UNGREEDY); 1986 printf ("raw:%d\n", G_REGEX_RAW); 1987 printf ("no-auto-capture:%d\n", G_REGEX_NO_AUTO_CAPTURE); 1988 printf ("optimize:%d\n", G_REGEX_OPTIMIZE); 1989 printf ("dupnames:%d\n", G_REGEX_DUPNAMES); 1990 printf ("newline-cr:%d\n", G_REGEX_NEWLINE_CR); 1991 printf ("newline-lf:%d\n", G_REGEX_NEWLINE_LF); 1992 printf ("newline-crlf:%d\n", G_REGEX_NEWLINE_CRLF); 1993 1994 printf ("match-anchored:%d\n", G_REGEX_MATCH_ANCHORED); 1995 printf ("match-notbol:%d\n", G_REGEX_MATCH_NOTBOL); 1996 printf ("match-noteol:%d\n", G_REGEX_MATCH_NOTEOL); 1997 printf ("match-notempty:%d\n", G_REGEX_MATCH_NOTEMPTY); 1998 printf ("match-partial:%d\n", G_REGEX_MATCH_PARTIAL); 1999 printf ("match-newline-cr:%d\n", G_REGEX_MATCH_NEWLINE_CR); 2000 printf ("match-newline-lf:%d\n", G_REGEX_MATCH_NEWLINE_LF); 2001 printf ("match-newline-crlf:%d\n", G_REGEX_MATCH_NEWLINE_CRLF); 2002 printf ("match-newline-any:%d\n", G_REGEX_MATCH_NEWLINE_ANY); 2003 2004 OK_END (); 2005 } 2006 2007 2008 const command_arg_type_t cmd_search_string_spec[] = 2009 { 2010 ARG_DOC, 2011 ARG_NATNUM, /* first page */ 2012 ARG_NATNUM, /* last page */ 2013 ARG_NONEMPTY_STRING, /* search string */ 2014 ARG_BOOL, /* ignore-case */ 2015 }; 2016 2017 static void 2018 cmd_search_string(const epdfinfo_t *ctx, const command_arg_t *args) 2019 { 2020 PopplerDocument *doc = args[0].value.doc->pdf; 2021 int first = args[1].value.natnum; 2022 int last = args[2].value.natnum; 2023 const char *string = args[3].value.string; 2024 gboolean ignore_case = args[4].value.flag; 2025 GList *list, *item; 2026 double width, height; 2027 int pn; 2028 #ifdef HAVE_POPPLER_FIND_OPTS 2029 PopplerFindFlags flags = ignore_case ? 0 : POPPLER_FIND_CASE_SENSITIVE; 2030 #endif 2031 2032 NORMALIZE_PAGE_ARG (doc, &first, &last); 2033 OK_BEGIN (); 2034 for (pn = first; pn <= last; ++pn) 2035 { 2036 PopplerPage *page = poppler_document_get_page(doc, pn - 1); 2037 2038 if (! page) 2039 continue; 2040 2041 #ifdef HAVE_POPPLER_FIND_OPTS 2042 list = poppler_page_find_text_with_options(page, string, flags); 2043 #else 2044 list = poppler_page_find_text(page, string); 2045 #endif 2046 2047 poppler_page_get_size (page, &width, &height); 2048 2049 for (item = list; item; item = item->next) 2050 { 2051 gchar *line, *match; 2052 PopplerRectangle *r = item->data; 2053 gdouble y1 = r->y1; 2054 2055 r->y1 = height - r->y2; 2056 r->y2 = height - y1; 2057 2058 printf ("%d:", pn); 2059 line = strchomp (poppler_page_get_selected_text 2060 (page, POPPLER_SELECTION_LINE, r)); 2061 match = strchomp (poppler_page_get_selected_text 2062 (page, POPPLER_SELECTION_GLYPH, r)); 2063 print_response_string (match, COLON); 2064 print_response_string (line, COLON); 2065 printf ("%f %f %f %f\n", 2066 r->x1 / width, r->y1 / height, 2067 r->x2 / width, r->y2 / height); 2068 g_free (line); 2069 g_free (match); 2070 poppler_rectangle_free (r); 2071 } 2072 g_list_free (list); 2073 g_object_unref (page); 2074 } 2075 OK_END (); 2076 } 2077 2078 /* Name: metadata 2079 Args: filename 2080 Returns: PDF's metadata 2081 Errors: None 2082 2083 title author subject keywords creator producer pdf-version create-date mod-date 2084 2085 Dates are in seconds since the epoche. 2086 2087 */ 2088 2089 const command_arg_type_t cmd_metadata_spec[] = 2090 { 2091 ARG_DOC, 2092 }; 2093 2094 static void 2095 cmd_metadata (const epdfinfo_t *ctx, const command_arg_t *args) 2096 { 2097 PopplerDocument *doc = args[0].value.doc->pdf; 2098 time_t date; 2099 gchar *md[6]; 2100 gchar *title; 2101 int i; 2102 char *time_str; 2103 2104 OK_BEGIN (); 2105 2106 title = poppler_document_get_title (doc); 2107 print_response_string (title, COLON); 2108 g_free (title); 2109 2110 md[0] = poppler_document_get_author (doc); 2111 md[1] = poppler_document_get_subject (doc); 2112 md[2] = poppler_document_get_keywords (doc); 2113 md[3] = poppler_document_get_creator (doc); 2114 md[4] = poppler_document_get_producer (doc); 2115 md[5] = poppler_document_get_pdf_version_string (doc); 2116 2117 for (i = 0; i < 6; ++i) 2118 { 2119 print_response_string (md[i], COLON); 2120 g_free (md[i]); 2121 } 2122 2123 date = poppler_document_get_creation_date (doc); 2124 time_str = strchomp (ctime (&date)); 2125 print_response_string (time_str ? time_str : "", COLON); 2126 date = poppler_document_get_modification_date (doc); 2127 time_str = strchomp (ctime (&date)); 2128 print_response_string (time_str ? time_str : "", NEWLINE); 2129 OK_END (); 2130 } 2131 2132 /* Name: outline 2133 Args: filename 2134 2135 Returns: The documents outline (or index) as a, possibly empty, 2136 list of records: 2137 2138 tree-level ACTION 2139 2140 See cmd_pagelinks for how ACTION is constructed. 2141 2142 Errors: None 2143 */ 2144 2145 static void 2146 cmd_outline_walk (PopplerDocument *doc, PopplerIndexIter *iter, int depth) 2147 { 2148 do 2149 { 2150 PopplerIndexIter *child; 2151 PopplerAction *action = poppler_index_iter_get_action (iter); 2152 2153 if (! action) 2154 continue; 2155 2156 if (action_is_handled (action)) 2157 { 2158 printf ("%d:", depth); 2159 action_print (doc, action); 2160 } 2161 2162 child = poppler_index_iter_get_child (iter); 2163 if (child) 2164 { 2165 cmd_outline_walk (doc, child, depth + 1); 2166 } 2167 poppler_action_free (action); 2168 poppler_index_iter_free (child); 2169 } while (poppler_index_iter_next (iter)); 2170 } 2171 2172 const command_arg_type_t cmd_outline_spec[] = 2173 { 2174 ARG_DOC, 2175 }; 2176 2177 static void 2178 cmd_outline (const epdfinfo_t *ctx, const command_arg_t *args) 2179 { 2180 PopplerIndexIter *iter = poppler_index_iter_new (args->value.doc->pdf); 2181 OK_BEGIN (); 2182 if (iter) 2183 { 2184 cmd_outline_walk (args->value.doc->pdf, iter, 1); 2185 poppler_index_iter_free (iter); 2186 } 2187 OK_END (); 2188 } 2189 2190 /* Name: quit 2191 Args: None 2192 Returns: Nothing 2193 Errors: None 2194 2195 Close all documents and exit. 2196 */ 2197 2198 2199 const command_arg_type_t cmd_quit_spec[] = {}; 2200 2201 static void 2202 cmd_quit (const epdfinfo_t *ctx, const command_arg_t *args) 2203 { 2204 cmd_closeall (ctx, args); 2205 exit (EXIT_SUCCESS); 2206 } 2207 2208 /* Name: number-of-pages 2209 Args: filename 2210 Returns: The number of pages. 2211 Errors: None 2212 */ 2213 2214 2215 const command_arg_type_t cmd_number_of_pages_spec[] = 2216 { 2217 ARG_DOC 2218 }; 2219 2220 static void 2221 cmd_number_of_pages (const epdfinfo_t *ctx, const command_arg_t *args) 2222 { 2223 int npages = poppler_document_get_n_pages (args->value.doc->pdf); 2224 OK_BEGIN (); 2225 printf ("%d\n", npages); 2226 OK_END (); 2227 } 2228 2229 /* Name: pagelinks 2230 Args: filename page 2231 Returns: A list of linkmaps: 2232 2233 edges ACTION , 2234 2235 where ACTION is one of 2236 2237 'goto-dest' title page top 2238 'goto-remote' title filename page top 2239 'uri' title URI 2240 'launch' title program arguments 2241 2242 top is desired vertical position, filename is the target PDF of the 2243 `goto-remote' link. 2244 2245 Errors: None 2246 */ 2247 2248 2249 const command_arg_type_t cmd_pagelinks_spec[] = 2250 { 2251 ARG_DOC, 2252 ARG_NATNUM /* page number */ 2253 }; 2254 2255 static void 2256 cmd_pagelinks(const epdfinfo_t *ctx, const command_arg_t *args) 2257 { 2258 PopplerDocument *doc = args[0].value.doc->pdf; 2259 PopplerPage *page = NULL; 2260 int pn = args[1].value.natnum; 2261 double width, height; 2262 GList *link_map = NULL, *item; 2263 2264 page = poppler_document_get_page (doc, pn - 1); 2265 perror_if_not (page, "No such page %d", pn); 2266 poppler_page_get_size (page, &width, &height); 2267 link_map = poppler_page_get_link_mapping (page); 2268 2269 OK_BEGIN (); 2270 for (item = g_list_last (link_map); item; item = item->prev) 2271 { 2272 2273 PopplerLinkMapping *link = item->data; 2274 PopplerRectangle *r = &link->area; 2275 gdouble y1 = r->y1; 2276 /* LinkMappings have a different gravity. */ 2277 r->y1 = height - r->y2; 2278 r->y2 = height - y1; 2279 2280 if (! action_is_handled (link->action)) 2281 continue; 2282 2283 printf ("%f %f %f %f:", 2284 r->x1 / width, r->y1 / height, 2285 r->x2 / width, r->y2 / height); 2286 action_print (doc, link->action); 2287 } 2288 OK_END (); 2289 error: 2290 if (page) g_object_unref (page); 2291 if (link_map) poppler_page_free_link_mapping (link_map); 2292 } 2293 2294 /* Name: gettext 2295 Args: filename page edges selection-style 2296 Returns: The selection's text. 2297 Errors: If page is out of range. 2298 2299 For the selection-style argument see getselection command. 2300 */ 2301 2302 2303 const command_arg_type_t cmd_gettext_spec[] = 2304 { 2305 ARG_DOC, 2306 ARG_NATNUM, /* page number */ 2307 ARG_EDGES, /* selection */ 2308 ARG_NATNUM /* selection-style */ 2309 }; 2310 2311 static void 2312 cmd_gettext(const epdfinfo_t *ctx, const command_arg_t *args) 2313 { 2314 PopplerDocument *doc = args[0].value.doc->pdf; 2315 int pn = args[1].value.natnum; 2316 PopplerRectangle r = args[2].value.rectangle; 2317 int selection_style = args[3].value.natnum; 2318 PopplerPage *page = NULL; 2319 double width, height; 2320 gchar *text = NULL; 2321 2322 switch (selection_style) 2323 { 2324 case POPPLER_SELECTION_GLYPH: break; 2325 case POPPLER_SELECTION_LINE: break; 2326 case POPPLER_SELECTION_WORD: break; 2327 default: selection_style = POPPLER_SELECTION_GLYPH; 2328 } 2329 2330 page = poppler_document_get_page (doc, pn - 1); 2331 perror_if_not (page, "No such page %d", pn); 2332 poppler_page_get_size (page, &width, &height); 2333 r.x1 = r.x1 * width; 2334 r.x2 = r.x2 * width; 2335 r.y1 = r.y1 * height; 2336 r.y2 = r.y2 * height; 2337 /* printf ("%f %f %f %f , %f %f\n", r.x1, r.y1, r.x2, r.y2, width, height); */ 2338 text = poppler_page_get_selected_text (page, selection_style, &r); 2339 2340 OK_BEGIN (); 2341 print_response_string (text, NEWLINE); 2342 OK_END (); 2343 2344 error: 2345 g_free (text); 2346 if (page) g_object_unref (page); 2347 } 2348 2349 /* Name: getselection 2350 Args: filename page edges selection-selection_style 2351 Returns: The selection's text. 2352 Errors: If page is out of range. 2353 2354 selection-selection_style should be as follows. 2355 2356 0 (POPPLER_SELECTION_GLYPH) 2357 glyph is the minimum unit for selection 2358 2359 1 (POPPLER_SELECTION_WORD) 2360 word is the minimum unit for selection 2361 2362 2 (POPPLER_SELECTION_LINE) 2363 line is the minimum unit for selection 2364 */ 2365 2366 2367 const command_arg_type_t cmd_getselection_spec[] = 2368 { 2369 ARG_DOC, 2370 ARG_NATNUM, /* page number */ 2371 ARG_EDGES, /* selection */ 2372 ARG_NATNUM /* selection-style */ 2373 }; 2374 2375 static void 2376 cmd_getselection (const epdfinfo_t *ctx, const command_arg_t *args) 2377 { 2378 PopplerDocument *doc = args[0].value.doc->pdf; 2379 int pn = args[1].value.natnum; 2380 PopplerRectangle r = args[2].value.rectangle; 2381 int selection_style = args[3].value.natnum; 2382 gdouble width, height; 2383 cairo_region_t *region = NULL; 2384 PopplerPage *page = NULL; 2385 int i; 2386 2387 switch (selection_style) 2388 { 2389 case POPPLER_SELECTION_GLYPH: break; 2390 case POPPLER_SELECTION_LINE: break; 2391 case POPPLER_SELECTION_WORD: break; 2392 default: selection_style = POPPLER_SELECTION_GLYPH; 2393 } 2394 2395 page = poppler_document_get_page (doc, pn - 1); 2396 perror_if_not (page, "No such page %d", pn); 2397 poppler_page_get_size (page, &width, &height); 2398 2399 r.x1 = r.x1 * width; 2400 r.x2 = r.x2 * width; 2401 r.y1 = r.y1 * height; 2402 r.y2 = r.y2 * height; 2403 2404 region = poppler_page_get_selected_region (page, 1.0, selection_style, &r); 2405 2406 OK_BEGIN (); 2407 for (i = 0; i < cairo_region_num_rectangles (region); ++i) 2408 { 2409 cairo_rectangle_int_t r; 2410 2411 cairo_region_get_rectangle (region, i, &r); 2412 printf ("%f %f %f %f\n", 2413 r.x / width, 2414 r.y / height, 2415 (r.x + r.width) / width, 2416 (r.y + r.height) / height); 2417 } 2418 OK_END (); 2419 2420 error: 2421 if (region) cairo_region_destroy (region); 2422 if (page) g_object_unref (page); 2423 } 2424 2425 /* Name: pagesize 2426 Args: filename page 2427 Returns: width height 2428 Errors: If page is out of range. 2429 */ 2430 2431 2432 const command_arg_type_t cmd_pagesize_spec[] = 2433 { 2434 ARG_DOC, 2435 ARG_NATNUM /* page number */ 2436 }; 2437 2438 static void 2439 cmd_pagesize(const epdfinfo_t *ctx, const command_arg_t *args) 2440 { 2441 PopplerDocument *doc = args[0].value.doc->pdf; 2442 int pn = args[1].value.natnum; 2443 PopplerPage *page = NULL; 2444 double width, height; 2445 2446 2447 page = poppler_document_get_page (doc, pn - 1); 2448 perror_if_not (page, "No such page %d", pn); 2449 poppler_page_get_size (page, &width, &height); 2450 2451 OK_BEGIN (); 2452 printf ("%f:%f\n", width, height); 2453 OK_END (); 2454 2455 error: 2456 if (page) g_object_unref (page); 2457 } 2458 2459 /* Annotations */ 2460 2461 /* Name: getannots 2462 Args: filename firstpage lastpage 2463 Returns: The list of annotations of this page. 2464 2465 For all annotations 2466 2467 page edges type key flags color contents mod-date 2468 2469 ,where 2470 2471 name is a document-unique name, 2472 flags is PopplerAnnotFlag bitmask, 2473 color is 3-byte RGB hex number and 2474 2475 Then 2476 2477 label subject opacity popup-edges popup-is-open create-date 2478 2479 if this is a markup annotation and additionally 2480 2481 text-icon text-state 2482 2483 for markup text annotations. 2484 2485 Errors: If page is out of range. 2486 */ 2487 2488 2489 const command_arg_type_t cmd_getannots_spec[] = 2490 { 2491 ARG_DOC, 2492 ARG_NATNUM, /* first page */ 2493 ARG_NATNUM /* last page */ 2494 }; 2495 2496 static void 2497 cmd_getannots(const epdfinfo_t *ctx, const command_arg_t *args) 2498 { 2499 PopplerDocument *doc = args[0].value.doc->pdf; 2500 gint first = args[1].value.natnum; 2501 gint last = args[2].value.natnum; 2502 GList *list; 2503 gint pn; 2504 2505 first = MAX(1, first); 2506 if (last <= 0) 2507 last = poppler_document_get_n_pages (doc); 2508 else 2509 last = MIN(last, poppler_document_get_n_pages (doc)); 2510 2511 OK_BEGIN (); 2512 for (pn = first; pn <= last; ++pn) 2513 { 2514 GList *annots = annoation_get_for_page (args->value.doc, pn); 2515 PopplerPage *page = poppler_document_get_page (doc, pn - 1); 2516 2517 if (! page) 2518 continue; 2519 2520 for (list = annots; list; list = list->next) 2521 { 2522 annotation_t *annot = (annotation_t *)list->data; 2523 annotation_print (annot, page); 2524 } 2525 g_object_unref (page); 2526 } 2527 OK_END (); 2528 } 2529 2530 /* Name: getannot 2531 Args: filename name 2532 Returns: The annotation for name, see cmd_getannots. 2533 Errors: If no annotation named ,name' exists. 2534 */ 2535 2536 2537 const command_arg_type_t cmd_getannot_spec[] = 2538 { 2539 ARG_DOC, 2540 ARG_NONEMPTY_STRING, /* annotation's key */ 2541 }; 2542 2543 static void 2544 cmd_getannot (const epdfinfo_t *ctx, const command_arg_t *args) 2545 { 2546 document_t *doc = args->value.doc; 2547 const gchar *key = args[1].value.string; 2548 PopplerPage *page = NULL; 2549 annotation_t *a = annotation_get_by_key (doc, key); 2550 gint index; 2551 2552 perror_if_not (a, "No such annotation: %s", key); 2553 index = poppler_annot_get_page_index (a->amap->annot); 2554 if (index >= 0) 2555 page = poppler_document_get_page (doc->pdf, index); 2556 perror_if_not (page, "Unable to get page %d", index + 1); 2557 2558 OK_BEGIN (); 2559 annotation_print (a, page); 2560 OK_END (); 2561 2562 error: 2563 if (page) g_object_unref (page); 2564 } 2565 2566 /* Name: getannot_attachment 2567 Args: filename name [output-filename] 2568 Returns: name description size mtime ctime output-filename 2569 Errors: If no annotation named ,name' exists or output-filename is 2570 not writable. 2571 */ 2572 2573 2574 const command_arg_type_t cmd_getattachment_from_annot_spec[] = 2575 { 2576 ARG_DOC, 2577 ARG_NONEMPTY_STRING, /* annotation's name */ 2578 ARG_BOOL /* save attachment */ 2579 }; 2580 2581 static void 2582 cmd_getattachment_from_annot (const epdfinfo_t *ctx, const command_arg_t *args) 2583 { 2584 document_t *doc = args->value.doc; 2585 const gchar *key = args[1].value.string; 2586 gboolean do_save = args[2].value.flag; 2587 PopplerAttachment *att = NULL; 2588 annotation_t *a = annotation_get_by_key (doc, key); 2589 gchar *id = NULL; 2590 2591 perror_if_not (a, "No such annotation: %s", key); 2592 perror_if_not (POPPLER_IS_ANNOT_FILE_ATTACHMENT (a->amap->annot), 2593 "Not a file annotation: %s", key); 2594 att = poppler_annot_file_attachment_get_attachment 2595 (POPPLER_ANNOT_FILE_ATTACHMENT (a->amap->annot)); 2596 perror_if_not (att, "Unable to get attachment: %s", key); 2597 id = g_strdup_printf ("attachment-%s", key); 2598 2599 OK_BEGIN (); 2600 attachment_print (att, id, do_save); 2601 OK_END (); 2602 2603 error: 2604 if (att) g_object_unref (att); 2605 if (id) g_free (id); 2606 } 2607 2608 2609 /* document-level attachments */ 2610 const command_arg_type_t cmd_getattachments_spec[] = 2611 { 2612 ARG_DOC, 2613 ARG_BOOL, /* save attachments */ 2614 }; 2615 2616 static void 2617 cmd_getattachments (const epdfinfo_t *ctx, const command_arg_t *args) 2618 { 2619 document_t *doc = args->value.doc; 2620 gboolean do_save = args[1].value.flag; 2621 GList *item; 2622 GList *attmnts = poppler_document_get_attachments (doc->pdf); 2623 int i; 2624 2625 OK_BEGIN (); 2626 for (item = attmnts, i = 0; item; item = item->next, ++i) 2627 { 2628 PopplerAttachment *att = (PopplerAttachment*) item->data; 2629 gchar *id = g_strdup_printf ("attachment-document-%d", i); 2630 2631 attachment_print (att, id, do_save); 2632 g_object_unref (att); 2633 g_free (id); 2634 } 2635 g_list_free (attmnts); 2636 2637 OK_END (); 2638 } 2639 2640 #ifdef HAVE_POPPLER_ANNOT_WRITE 2641 2642 const command_arg_type_t cmd_addannot_spec[] = 2643 { 2644 ARG_DOC, 2645 ARG_NATNUM, /* page number */ 2646 ARG_STRING, /* type */ 2647 ARG_EDGES_OR_POSITION, /* edges or position (uses default size) */ 2648 ARG_REST, /* markup regions */ 2649 }; 2650 2651 static void 2652 cmd_addannot (const epdfinfo_t *ctx, const command_arg_t *args) 2653 { 2654 2655 document_t *doc = args->value.doc; 2656 gint pn = args[1].value.natnum; 2657 const char *type_string = args[2].value.string; 2658 PopplerRectangle r = args[3].value.rectangle; 2659 int i; 2660 PopplerPage *page = NULL; 2661 double width, height; 2662 PopplerAnnot *pa; 2663 PopplerAnnotMapping *amap; 2664 annotation_t *a; 2665 gchar *key; 2666 GList *annotations; 2667 gdouble y2; 2668 char *error_msg = NULL; 2669 2670 page = poppler_document_get_page (doc->pdf, pn - 1); 2671 perror_if_not (page, "Unable to get page %d", pn); 2672 poppler_page_get_size (page, &width, &height); 2673 r.x1 *= width; r.x2 *= width; 2674 r.y1 *= height; r.y2 *= height; 2675 if (r.y2 < 0) 2676 r.y2 = r.y1 + 24; 2677 if (r.x2 < 0) 2678 r.x2 = r.x1 + 24; 2679 y2 = r.y2; 2680 r.y2 = height - r.y1; 2681 r.y1 = height - y2; 2682 2683 pa = annotation_new (ctx, doc, page, type_string, &r, &args[4], &error_msg); 2684 perror_if_not (pa, "Creating annotation failed: %s", 2685 error_msg ? error_msg : "Reason unknown"); 2686 amap = poppler_annot_mapping_new (); 2687 amap->area = r; 2688 amap->annot = pa; 2689 annotations = annoation_get_for_page (doc, pn); 2690 2691 i = g_list_length (annotations); 2692 key = g_strdup_printf ("annot-%d-%d", pn, i); 2693 while (g_hash_table_lookup (doc->annotations.keys, key)) 2694 { 2695 g_free (key); 2696 key = g_strdup_printf ("annot-%d-%d", pn, ++i); 2697 } 2698 a = g_malloc (sizeof (annotation_t)); 2699 a->amap = amap; 2700 a->key = key; 2701 doc->annotations.pages[pn - 1] = 2702 g_list_prepend (annotations, a); 2703 g_hash_table_insert (doc->annotations.keys, key, a); 2704 poppler_page_add_annot (page, pa); 2705 OK_BEGIN (); 2706 annotation_print (a, page); 2707 OK_END (); 2708 2709 error: 2710 if (page) g_object_unref (page); 2711 if (error_msg) g_free (error_msg); 2712 } 2713 2714 2715 const command_arg_type_t cmd_delannot_spec[] = 2716 { 2717 ARG_DOC, 2718 ARG_NONEMPTY_STRING /* Annotation's key */ 2719 }; 2720 2721 static void 2722 cmd_delannot (const epdfinfo_t *ctx, const command_arg_t *args) 2723 { 2724 document_t *doc = args->value.doc; 2725 const gchar *key = args[1].value.string; 2726 PopplerPage *page = NULL; 2727 annotation_t *a = annotation_get_by_key (doc, key); 2728 gint pn; 2729 2730 perror_if_not (a, "No such annotation: %s", key); 2731 pn = poppler_annot_get_page_index (a->amap->annot) + 1; 2732 if (pn >= 1) 2733 page = poppler_document_get_page (doc->pdf, pn - 1); 2734 perror_if_not (page, "Unable to get page %d", pn); 2735 poppler_page_remove_annot (page, a->amap->annot); 2736 doc->annotations.pages[pn - 1] = 2737 g_list_remove (doc->annotations.pages[pn - 1], a); 2738 g_hash_table_remove (doc->annotations.keys, a->key); 2739 poppler_annot_mapping_free(a->amap); 2740 OK (); 2741 2742 error: 2743 if (a) 2744 { 2745 g_free (a->key); 2746 g_free (a); 2747 } 2748 if (page) g_object_unref (page); 2749 } 2750 2751 const command_arg_type_t cmd_editannot_spec[] = 2752 { 2753 ARG_DOC, 2754 ARG_NONEMPTY_STRING, /* annotation key */ 2755 ARG_REST /* (KEY VALUE ...) */ 2756 }; 2757 2758 static void 2759 cmd_editannot (const epdfinfo_t *ctx, const command_arg_t *args) 2760 { 2761 document_t *doc = args->value.doc; 2762 const char *key = args[1].value.string; 2763 int nrest_args = args[2].value.rest.nargs; 2764 char * const *rest_args = args[2].value.rest.args; 2765 annotation_t *a = annotation_get_by_key (doc, key); 2766 PopplerAnnot *pa; 2767 PopplerPage *page = NULL; 2768 int i = 0; 2769 gint index; 2770 char *error_msg = NULL; 2771 command_arg_t carg; 2772 const char *unexpected_parse_error = "Internal error while parsing arg `%s'"; 2773 2774 perror_if_not (a, "No such annotation: %s", key); 2775 pa = a->amap->annot; 2776 perror_if_not (annotation_edit_validate (ctx, &args[2], pa, &error_msg), 2777 "%s", error_msg); 2778 index = poppler_annot_get_page_index (pa); 2779 page = poppler_document_get_page (doc->pdf, index); 2780 perror_if_not (page, "Unable to get page %d for annotation", index); 2781 2782 for (i = 0; i < nrest_args; ++i) 2783 { 2784 const char *key = rest_args[i++]; 2785 2786 if (! strcmp (key, "flags")) 2787 { 2788 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2789 ARG_NATNUM, NULL), 2790 unexpected_parse_error, rest_args[i]); 2791 poppler_annot_set_flags (pa, carg.value.natnum); 2792 } 2793 else if (! strcmp (key, "color")) 2794 { 2795 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2796 ARG_COLOR, NULL), 2797 unexpected_parse_error, rest_args[i]); 2798 poppler_annot_set_color (pa, &carg.value.color); 2799 } 2800 else if (! strcmp (key, "contents")) 2801 { 2802 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2803 ARG_STRING, NULL), 2804 unexpected_parse_error, rest_args[i]); 2805 poppler_annot_set_contents (pa, carg.value.string); 2806 } 2807 else if (! strcmp (key, "edges")) 2808 { 2809 PopplerRectangle *area = &a->amap->area; 2810 gdouble width, height; 2811 PopplerRectangle r; 2812 2813 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2814 ARG_EDGES_OR_POSITION, NULL), 2815 unexpected_parse_error, rest_args[i]); 2816 r = carg.value.rectangle; 2817 poppler_page_get_size (page, &width, &height); 2818 2819 /* Translate Gravity and maybe keep the width and height. */ 2820 if (r.x2 < 0) 2821 area->x2 += (r.x1 * width) - area->x1; 2822 else 2823 area->x2 = r.x2 * width; 2824 2825 if (r.y2 < 0) 2826 area->y1 -= (r.y1 * height) - (height - area->y2); 2827 else 2828 area->y1 = height - (r.y2 * height); 2829 2830 area->x1 = r.x1 * width; 2831 area->y2 = height - (r.y1 * height); 2832 2833 poppler_annot_set_rectangle (pa, area); 2834 } 2835 else if (! strcmp (key, "label")) 2836 { 2837 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 2838 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2839 ARG_STRING, NULL), 2840 unexpected_parse_error, rest_args[i]); 2841 poppler_annot_markup_set_label (ma, carg.value.string); 2842 } 2843 else if (! strcmp (key, "opacity")) 2844 { 2845 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 2846 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2847 ARG_EDGE, NULL), 2848 unexpected_parse_error, rest_args[i]); 2849 poppler_annot_markup_set_opacity (ma, carg.value.edge); 2850 } 2851 else if (! strcmp (key, "popup")) 2852 { 2853 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 2854 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2855 ARG_EDGES, NULL), 2856 unexpected_parse_error, rest_args[i]); 2857 poppler_annot_markup_set_popup (ma, &carg.value.rectangle); 2858 } 2859 else if (! strcmp (key, "popup-is-open")) 2860 { 2861 PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa); 2862 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2863 ARG_BOOL, NULL), 2864 unexpected_parse_error, rest_args[i]); 2865 poppler_annot_markup_set_popup_is_open (ma, carg.value.flag); 2866 } 2867 else if (! strcmp (key, "icon")) 2868 { 2869 PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa); 2870 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2871 ARG_STRING, NULL), 2872 unexpected_parse_error, rest_args[i]); 2873 poppler_annot_text_set_icon (ta, carg.value.string); 2874 } 2875 else if (! strcmp (key, "is-open")) 2876 { 2877 PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa); 2878 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &carg, 2879 ARG_BOOL, NULL), 2880 unexpected_parse_error, rest_args[i]); 2881 poppler_annot_text_set_is_open (ta, carg.value.flag); 2882 } 2883 else 2884 { 2885 perror_if_not (0, "internal error: annotation property validation failed"); 2886 } 2887 } 2888 2889 OK_BEGIN (); 2890 annotation_print (a, page); 2891 OK_END (); 2892 2893 error: 2894 if (error_msg) g_free (error_msg); 2895 if (page) g_object_unref (page); 2896 } 2897 2898 const command_arg_type_t cmd_save_spec[] = 2899 { 2900 ARG_DOC, 2901 }; 2902 2903 static void 2904 cmd_save (const epdfinfo_t *ctx, const command_arg_t *args) 2905 { 2906 document_t *doc = args->value.doc; 2907 char *filename = mktempfile (); 2908 GError *gerror = NULL; 2909 gchar *uri; 2910 gboolean success = FALSE; 2911 2912 if (!filename) 2913 { 2914 printf_error_response ("Unable to create temporary file"); 2915 return; 2916 } 2917 2918 uri = g_filename_to_uri (filename, NULL, &gerror); 2919 2920 if (uri) 2921 { 2922 success = poppler_document_save (doc->pdf, uri, &gerror); 2923 g_free (uri); 2924 } 2925 if (! success) 2926 { 2927 printf_error_response ("Error while saving %s:%s" 2928 , filename, gerror ? gerror->message : "?"); 2929 if (gerror) 2930 g_error_free (gerror); 2931 return; 2932 } 2933 OK_BEGIN (); 2934 print_response_string (filename, NEWLINE); 2935 OK_END (); 2936 } 2937 2938 #endif /* HAVE_POPPLER_ANNOT_WRITE */ 2939 2940 2941 const command_arg_type_t cmd_synctex_forward_search_spec[] = 2942 { 2943 ARG_DOC, 2944 ARG_NONEMPTY_STRING, /* source file */ 2945 ARG_NATNUM, /* line number */ 2946 ARG_NATNUM /* column number */ 2947 }; 2948 2949 static void 2950 cmd_synctex_forward_search (const epdfinfo_t *ctx, const command_arg_t *args) 2951 { 2952 document_t *doc = args[0].value.doc; 2953 const char *source = args[1].value.string; 2954 int line = args[2].value.natnum; 2955 int column = args[3].value.natnum; 2956 synctex_scanner_p scanner = NULL; 2957 synctex_node_p node; 2958 float x1, y1, x2, y2; 2959 PopplerPage *page = NULL; 2960 double width, height; 2961 int pn; 2962 2963 scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1); 2964 perror_if_not (scanner, "Unable to create synctex scanner,\ 2965 did you run latex with `--synctex=1' ?"); 2966 2967 perror_if_not (synctex_display_query (scanner, source, line, column, 0) 2968 && (node = synctex_scanner_next_result (scanner)), 2969 "Destination not found"); 2970 2971 pn = synctex_node_page (node); 2972 page = poppler_document_get_page(doc->pdf, pn - 1); 2973 perror_if_not (page, "Page not found"); 2974 x1 = synctex_node_box_visible_h (node); 2975 y1 = synctex_node_box_visible_v (node) 2976 - synctex_node_box_visible_height (node); 2977 x2 = synctex_node_box_visible_width (node) + x1; 2978 y2 = synctex_node_box_visible_depth (node) 2979 + synctex_node_box_visible_height (node) + y1; 2980 poppler_page_get_size (page, &width, &height); 2981 x1 /= width; 2982 y1 /= height; 2983 x2 /= width; 2984 y2 /= height; 2985 2986 OK_BEGIN (); 2987 printf("%d:%f:%f:%f:%f\n", pn, x1, y1, x2, y2); 2988 OK_END (); 2989 2990 error: 2991 if (page) g_object_unref (page); 2992 if (scanner) synctex_scanner_free (scanner); 2993 } 2994 2995 2996 const command_arg_type_t cmd_synctex_backward_search_spec[] = 2997 { 2998 ARG_DOC, 2999 ARG_NATNUM, /* page number */ 3000 ARG_EDGE, /* x */ 3001 ARG_EDGE /* y */ 3002 }; 3003 3004 static void 3005 cmd_synctex_backward_search (const epdfinfo_t *ctx, const command_arg_t *args) 3006 { 3007 document_t *doc = args[0].value.doc; 3008 int pn = args[1].value.natnum; 3009 double x = args[2].value.edge; 3010 double y = args[3].value.edge; 3011 synctex_scanner_p scanner = NULL; 3012 const char *filename; 3013 PopplerPage *page = NULL; 3014 synctex_node_p node; 3015 double width, height; 3016 int line, column; 3017 3018 scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1); 3019 perror_if_not (scanner, "Unable to create synctex scanner,\ 3020 did you run latex with `--synctex=1' ?"); 3021 3022 page = poppler_document_get_page(doc->pdf, pn - 1); 3023 perror_if_not (page, "Page not found"); 3024 poppler_page_get_size (page, &width, &height); 3025 x = x * width; 3026 y = y * height; 3027 3028 if (! synctex_edit_query (scanner, pn, x, y) 3029 || ! (node = synctex_scanner_next_result (scanner)) 3030 || ! (filename = 3031 synctex_scanner_get_name (scanner, synctex_node_tag (node)))) 3032 { 3033 printf_error_response ("Destination not found"); 3034 goto error; 3035 } 3036 3037 line = synctex_node_line (node); 3038 column = synctex_node_column (node); 3039 3040 OK_BEGIN (); 3041 print_response_string (filename, COLON); 3042 printf("%d:%d\n", line, column); 3043 OK_END (); 3044 3045 error: 3046 if (page) g_object_unref (page); 3047 if (scanner) synctex_scanner_free (scanner); 3048 } 3049 3050 3051 const command_arg_type_t cmd_renderpage_spec[] = 3052 { 3053 ARG_DOC, 3054 ARG_NATNUM, /* page number */ 3055 ARG_NATNUM, /* width */ 3056 ARG_REST, /* commands */ 3057 }; 3058 3059 static void 3060 cmd_renderpage (const epdfinfo_t *ctx, const command_arg_t *args) 3061 { 3062 document_t *doc = args[0].value.doc; 3063 int pn = args[1].value.natnum; 3064 int width = args[2].value.natnum; 3065 int nrest_args = args[3].value.rest.nargs; 3066 char * const *rest_args = args[3].value.rest.args; 3067 PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1); 3068 cairo_surface_t *surface = NULL; 3069 cairo_t *cr = NULL; 3070 command_arg_t rest_arg; 3071 gchar *error_msg = NULL; 3072 double pt_width, pt_height; 3073 PopplerColor fg = { 0, 0, 0 }; 3074 PopplerColor bg = { 65535, 0, 0 }; 3075 double alpha = 1.0; 3076 double line_width = 1.5; 3077 PopplerRectangle cb = {0.0, 0.0, 1.0, 1.0}; 3078 int i = 0; 3079 3080 perror_if_not (page, "No such page %d", pn); 3081 poppler_page_get_size (page, &pt_width, &pt_height); 3082 surface = image_render_page (doc->pdf, page, width, 1, 3083 &doc->options.render); 3084 perror_if_not (surface, "Failed to render page %d", pn); 3085 3086 if (! nrest_args) 3087 goto theend; 3088 3089 cr = cairo_create (surface); 3090 cairo_scale (cr, width / pt_width, width / pt_width); 3091 3092 while (i < nrest_args) 3093 { 3094 const char* keyword; 3095 3096 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3097 ARG_STRING, &error_msg), 3098 "%s", error_msg); 3099 keyword = rest_arg.value.string; 3100 ++i; 3101 3102 perror_if_not (i < nrest_args, "Keyword is `%s' missing an argument", 3103 keyword); 3104 3105 if (! strcmp (keyword, ":foreground") 3106 || ! strcmp (keyword, ":background")) 3107 { 3108 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3109 ARG_COLOR, &error_msg), 3110 "%s", error_msg); 3111 ++i; 3112 if (! strcmp (keyword, ":foreground")) 3113 fg = rest_arg.value.color; 3114 else 3115 bg = rest_arg.value.color; 3116 3117 } 3118 else if (! strcmp (keyword, ":alpha")) 3119 { 3120 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3121 ARG_EDGE, &error_msg), 3122 "%s", error_msg); 3123 ++i; 3124 alpha = rest_arg.value.edge; 3125 } 3126 else if (! strcmp (keyword, ":crop-to") 3127 || ! strcmp (keyword, ":highlight-region") 3128 || ! strcmp (keyword, ":highlight-text") 3129 || ! strcmp (keyword, ":highlight-line")) 3130 { 3131 PopplerRectangle *r; 3132 perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg, 3133 ARG_EDGES, &error_msg), 3134 "%s", error_msg); 3135 3136 ++i; 3137 r = &rest_arg.value.rectangle; 3138 3139 if (! strcmp (keyword, ":crop-to")) 3140 { 3141 gdouble w = (cb.x2 - cb.x1); 3142 gdouble h = (cb.y2 - cb.y1); 3143 gdouble x1 = cb.x1; 3144 gdouble y1 = cb.y1; 3145 3146 cb.x1 = r->x1 * w + x1; 3147 cb.x2 = r->x2 * w + x1; 3148 cb.y1 = r->y1 * h + y1; 3149 cb.y2 = r->y2 * h + y1; 3150 3151 } 3152 else 3153 { 3154 r->x1 = pt_width * r->x1 * (cb.x2 - cb.x1) + pt_width * cb.x1; 3155 r->x2 = pt_width * r->x2 * (cb.x2 - cb.x1) + pt_width * cb.x1; 3156 r->y1 = pt_height * r->y1 * (cb.y2 - cb.y1) + pt_height * cb.y1; 3157 r->y2 = pt_height * r->y2 * (cb.y2 - cb.y1) + pt_height * cb.y1; 3158 3159 if (! strcmp (keyword, ":highlight-region")) 3160 { 3161 const double deg = M_PI / 180.0; 3162 double rad; 3163 3164 r->x1 += line_width / 2; 3165 r->x2 -= line_width / 2; 3166 r->y1 += line_width / 2; 3167 r->y2 -= line_width / 2; 3168 3169 rad = MIN (5, MIN (r->x2 - r->x1, r->y2 - r->y1) / 2.0); 3170 3171 cairo_move_to (cr, r->x1 , r->y1 + rad); 3172 cairo_arc (cr, r->x1 + rad, r->y1 + rad, rad, 180 * deg, 270 * deg); 3173 cairo_arc (cr, r->x2 - rad, r->y1 + rad, rad, 270 * deg, 360 * deg); 3174 cairo_arc (cr, r->x2 - rad, r->y2 - rad, rad, 0 * deg, 90 * deg); 3175 cairo_arc (cr, r->x1 + rad, r->y2 - rad, rad, 90 * deg, 180 * deg); 3176 cairo_close_path (cr); 3177 3178 cairo_set_source_rgba (cr, 3179 bg.red / 65535.0, 3180 bg.green / 65535.0, 3181 bg.blue / 65535.0, alpha); 3182 cairo_fill_preserve (cr); 3183 cairo_set_source_rgba (cr, 3184 fg.red / 65535.0, 3185 fg.green / 65535.0, 3186 fg.blue / 65535.0, 1.0); 3187 cairo_set_line_width (cr, line_width); 3188 cairo_stroke (cr); 3189 } 3190 else 3191 { 3192 gboolean is_single_line = ! strcmp (keyword, ":highlight-line"); 3193 3194 if (is_single_line) 3195 { 3196 gdouble m = r->y1 + (r->y2 - r->y1) / 2; 3197 3198 /* Make the rectangle flat, otherwise poppler frequently 3199 renders neighboring lines.*/ 3200 r->y1 = m; 3201 r->y2 = m; 3202 } 3203 3204 poppler_page_render_selection (page, cr, r, NULL, 3205 POPPLER_SELECTION_GLYPH, &fg, &bg); 3206 } 3207 } 3208 } 3209 else 3210 perror_if_not (0, "Unknown render command: %s", keyword); 3211 } 3212 if (cb.x1 != 0 || cb.y1 != 0 || cb.x2 != 1 || cb.y2 != 1) 3213 { 3214 int height = cairo_image_surface_get_height (surface); 3215 cairo_rectangle_int_t r = {(int) (width * cb.x1 + 0.5), 3216 (int) (height * cb.y1 + 0.5), 3217 (int) (width * (cb.x2 - cb.x1) + 0.5), 3218 (int) (height * (cb.y2 - cb.y1) + 0.5)}; 3219 cairo_surface_t *nsurface = 3220 cairo_image_surface_create (CAIRO_FORMAT_ARGB32, r.width, r.height); 3221 perror_if_not (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS, 3222 "%s", "Failed to create cairo surface"); 3223 cairo_destroy (cr); 3224 cr = cairo_create (nsurface); 3225 perror_if_not (cairo_status (cr) == CAIRO_STATUS_SUCCESS, 3226 "%s", "Failed to create cairo context"); 3227 cairo_set_source_surface (cr, surface, -r.x, -r.y); 3228 cairo_paint (cr); 3229 cairo_surface_destroy (surface); 3230 surface = nsurface; 3231 } 3232 3233 theend: 3234 image_write_print_response (surface, PNG); 3235 3236 error: 3237 if (error_msg) g_free (error_msg); 3238 if (cr) cairo_destroy (cr); 3239 if (surface) cairo_surface_destroy (surface); 3240 if (page) g_object_unref (page); 3241 } 3242 3243 const command_arg_type_t cmd_boundingbox_spec[] = 3244 { 3245 ARG_DOC, 3246 ARG_NATNUM, /* page number */ 3247 /* region */ 3248 }; 3249 3250 static void 3251 cmd_boundingbox (const epdfinfo_t *ctx, const command_arg_t *args) 3252 { 3253 document_t *doc = args[0].value.doc; 3254 int pn = args[1].value.natnum; 3255 PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1); 3256 cairo_surface_t *surface = NULL; 3257 int width, height; 3258 double pt_width, pt_height; 3259 unsigned char *data, *data_p; 3260 PopplerRectangle bbox; 3261 int i, j; 3262 3263 perror_if_not (page, "No such page %d", pn); 3264 poppler_page_get_size (page, &pt_width, &pt_height); 3265 surface = image_render_page (doc->pdf, page, (int) pt_width, 1, 3266 &doc->options.render); 3267 3268 perror_if_not (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS, 3269 "Failed to render page"); 3270 3271 width = cairo_image_surface_get_width (surface); 3272 height = cairo_image_surface_get_height (surface); 3273 data = cairo_image_surface_get_data (surface); 3274 3275 /* Determine the bbox by comparing each pixel in the 4 corner 3276 stripes with the origin. */ 3277 for (i = 0; i < width; ++i) 3278 { 3279 data_p = data + 4 * i; 3280 for (j = 0; j < height; ++j, data_p += 4 * width) 3281 { 3282 if (! ARGB_EQUAL (data, data_p)) 3283 break; 3284 } 3285 if (j < height) 3286 break; 3287 } 3288 bbox.x1 = i; 3289 3290 for (i = width - 1; i > -1; --i) 3291 { 3292 data_p = data + 4 * i; 3293 for (j = 0; j < height; ++j, data_p += 4 * width) 3294 { 3295 if (! ARGB_EQUAL (data, data_p)) 3296 break; 3297 } 3298 if (j < height) 3299 break; 3300 } 3301 bbox.x2 = i + 1; 3302 3303 for (i = 0; i < height; ++i) 3304 { 3305 data_p = data + 4 * i * width; 3306 for (j = 0; j < width; ++j, data_p += 4) 3307 { 3308 if (! ARGB_EQUAL (data, data_p)) 3309 break; 3310 } 3311 if (j < width) 3312 break; 3313 } 3314 bbox.y1 = i; 3315 3316 for (i = height - 1; i > -1; --i) 3317 { 3318 data_p = data + 4 * i * width; 3319 for (j = 0; j < width; ++j, data_p += 4) 3320 { 3321 if (! ARGB_EQUAL (data, data_p)) 3322 break; 3323 } 3324 if (j < width) 3325 break; 3326 } 3327 bbox.y2 = i + 1; 3328 3329 OK_BEGIN (); 3330 if (bbox.x1 >= bbox.x2 || bbox.y1 >= bbox.y2) 3331 { 3332 /* empty page */ 3333 puts ("0:0:1:1"); 3334 } 3335 else 3336 { 3337 printf ("%f:%f:%f:%f\n", 3338 bbox.x1 / width, 3339 bbox.y1 / height, 3340 bbox.x2 / width, 3341 bbox.y2 / height); 3342 } 3343 OK_END (); 3344 3345 error: 3346 if (surface) cairo_surface_destroy (surface); 3347 if (page) g_object_unref (page); 3348 } 3349 3350 const command_arg_type_t cmd_charlayout_spec[] = 3351 { 3352 ARG_DOC, 3353 ARG_NATNUM, /* page number */ 3354 ARG_EDGES_OR_POSITION, /* region or position */ 3355 }; 3356 3357 static void 3358 cmd_charlayout(const epdfinfo_t *ctx, const command_arg_t *args) 3359 { 3360 PopplerDocument *doc = args[0].value.doc->pdf; 3361 int pn = args[1].value.natnum; 3362 PopplerRectangle region = args[2].value.rectangle; 3363 double width, height; 3364 PopplerPage *page = poppler_document_get_page(doc, pn - 1); 3365 char *text = NULL; 3366 char *text_p; 3367 PopplerRectangle *rectangles = NULL; 3368 guint nrectangles; 3369 int i; 3370 gboolean have_position = region.y2 < 0; 3371 3372 perror_if_not (page, "No such page %d", pn); 3373 3374 text = poppler_page_get_text (page); 3375 text_p = text; 3376 poppler_page_get_text_layout (page, &rectangles, &nrectangles); 3377 poppler_page_get_size (page, &width, &height); 3378 region.x1 *= width; 3379 region.x2 *= width; 3380 region.y1 *= height; 3381 region.y2 *= height; 3382 3383 OK_BEGIN (); 3384 for (i = 0; i < nrectangles && *text_p; ++i) 3385 { 3386 PopplerRectangle *r = &rectangles[i]; 3387 char *nextc = g_utf8_offset_to_pointer (text_p, 1); 3388 3389 if ((have_position 3390 && region.x1 >= r->x1 3391 && region.x1 <= r->x2 3392 && region.y1 >= r->y1 3393 && region.y1 <= r->y2) 3394 || (! have_position 3395 && r->x1 >= region.x1 3396 && r->y1 >= region.y1 3397 && r->x2 <= region.x2 3398 && r->y2 <= region.y2)) 3399 { 3400 char endc = *nextc; 3401 3402 printf ("%f %f %f %f:", 3403 r->x1 / width, r->y1 / height, 3404 r->x2 / width, r->y2 / height); 3405 *nextc = '\0'; 3406 print_response_string (text_p, NEWLINE); 3407 *nextc = endc; 3408 } 3409 text_p = nextc; 3410 } 3411 OK_END (); 3412 3413 g_free (rectangles); 3414 g_object_unref (page); 3415 g_free (text); 3416 3417 error: 3418 return; 3419 } 3420 3421 const document_option_t document_options [] = 3422 { 3423 DEC_DOPT (":render/usecolors", ARG_BOOL, render.usecolors), 3424 DEC_DOPT (":render/printed", ARG_BOOL, render.printed), 3425 DEC_DOPT (":render/foreground", ARG_COLOR, render.fg), 3426 DEC_DOPT (":render/background", ARG_COLOR, render.bg), 3427 }; 3428 3429 const command_arg_type_t cmd_getoptions_spec[] = 3430 { 3431 ARG_DOC, 3432 }; 3433 3434 static void 3435 cmd_getoptions(const epdfinfo_t *ctx, const command_arg_t *args) 3436 { 3437 document_t *doc = args[0].value.doc; 3438 int i; 3439 OK_BEGIN (); 3440 for (i = 0; i < G_N_ELEMENTS (document_options); ++i) 3441 { 3442 command_arg_t arg; 3443 3444 arg.type = document_options[i].type; 3445 memcpy (&arg.value, 3446 ((char*) &doc->options) + document_options[i].offset, 3447 command_arg_type_size (arg.type)); 3448 print_response_string (document_options[i].name, COLON); 3449 command_arg_print (&arg); 3450 puts(""); 3451 } 3452 OK_END (); 3453 } 3454 3455 const command_arg_type_t cmd_setoptions_spec[] = 3456 { 3457 ARG_DOC, 3458 ARG_REST /* key value pairs */ 3459 }; 3460 3461 static void 3462 cmd_setoptions(const epdfinfo_t *ctx, const command_arg_t *args) 3463 { 3464 int i = 0; 3465 document_t *doc = args[0].value.doc; 3466 int nrest = args[1].value.rest.nargs; 3467 char * const *rest = args[1].value.rest.args; 3468 gchar *error_msg = NULL; 3469 document_options_t opts = doc->options; 3470 const size_t nopts = G_N_ELEMENTS (document_options); 3471 3472 perror_if_not (nrest % 2 == 0, "Even number of key/value pairs expected"); 3473 3474 while (i < nrest) 3475 { 3476 int j; 3477 command_arg_t key, value; 3478 3479 perror_if_not (command_arg_parse_arg 3480 (ctx, rest[i], &key, ARG_NONEMPTY_STRING, &error_msg), 3481 "%s", error_msg); 3482 3483 ++i; 3484 for (j = 0; j < nopts; ++j) 3485 { 3486 const document_option_t *dopt = &document_options[j]; 3487 if (! strcmp (key.value.string, dopt->name)) 3488 { 3489 perror_if_not (command_arg_parse_arg 3490 (ctx, rest[i], &value, dopt->type, &error_msg), 3491 "%s", error_msg); 3492 memcpy (((char*) &opts) + dopt->offset, 3493 &value.value, command_arg_type_size (value.type)); 3494 break; 3495 } 3496 } 3497 perror_if_not (j < nopts, "Unknown option: %s", key.value.string); 3498 ++i; 3499 } 3500 doc->options = opts; 3501 cmd_getoptions (ctx, args); 3502 3503 error: 3504 if (error_msg) g_free (error_msg); 3505 } 3506 3507 const command_arg_type_t cmd_pagelabels_spec[] = 3508 { 3509 ARG_DOC, 3510 }; 3511 3512 static void 3513 cmd_pagelabels(const epdfinfo_t *ctx, const command_arg_t *args) 3514 { 3515 PopplerDocument *doc = args[0].value.doc->pdf; 3516 int i; 3517 3518 OK_BEGIN (); 3519 for (i = 0; i < poppler_document_get_n_pages (doc); ++i) 3520 { 3521 PopplerPage *page = poppler_document_get_page(doc, i); 3522 gchar *label = poppler_page_get_label (page); 3523 3524 print_response_string (label ? label : "", NEWLINE); 3525 g_object_unref (page); 3526 g_free (label); 3527 } 3528 OK_END (); 3529 } 3530 3531 const command_arg_type_t cmd_ping_spec[] = 3532 { 3533 ARG_STRING /* any message */ 3534 }; 3535 3536 static void 3537 cmd_ping (const epdfinfo_t *ctx, const command_arg_t *args) 3538 { 3539 const gchar *msg = args[0].value.string; 3540 OK_BEGIN (); 3541 print_response_string (msg, NEWLINE); 3542 OK_END (); 3543 } 3544 3545 3546 /* ================================================================== * 3547 * Main 3548 * ================================================================== */ 3549 3550 static const command_t commands [] = 3551 { 3552 /* Basic */ 3553 DEC_CMD (ping), 3554 DEC_CMD (features), 3555 DEC_CMD (open), 3556 DEC_CMD (close), 3557 DEC_CMD (quit), 3558 DEC_CMD (getoptions), 3559 DEC_CMD (setoptions), 3560 3561 /* Searching */ 3562 DEC_CMD2 (search_string, "search-string"), 3563 DEC_CMD2 (search_regexp, "search-regexp"), 3564 DEC_CMD2 (regexp_flags, "regexp-flags"), 3565 3566 /* General Information */ 3567 DEC_CMD (metadata), 3568 DEC_CMD (outline), 3569 DEC_CMD2 (number_of_pages, "number-of-pages"), 3570 DEC_CMD (pagelinks), 3571 DEC_CMD (gettext), 3572 DEC_CMD (getselection), 3573 DEC_CMD (pagesize), 3574 DEC_CMD (boundingbox), 3575 DEC_CMD (charlayout), 3576 3577 /* General Information */ 3578 DEC_CMD (metadata), 3579 DEC_CMD (outline), 3580 DEC_CMD2 (number_of_pages, "number-of-pages"), 3581 DEC_CMD (pagelinks), 3582 DEC_CMD (gettext), 3583 DEC_CMD (getselection), 3584 DEC_CMD (pagesize), 3585 DEC_CMD (boundingbox), 3586 DEC_CMD (charlayout), 3587 DEC_CMD (pagelabels), 3588 3589 /* Annotations */ 3590 DEC_CMD (getannots), 3591 DEC_CMD (getannot), 3592 #ifdef HAVE_POPPLER_ANNOT_WRITE 3593 DEC_CMD (addannot), 3594 DEC_CMD (delannot), 3595 DEC_CMD (editannot), 3596 DEC_CMD (save), 3597 #endif 3598 3599 /* Attachments */ 3600 DEC_CMD2 (getattachment_from_annot, "getattachment-from-annot"), 3601 DEC_CMD (getattachments), 3602 3603 /* Synctex */ 3604 DEC_CMD2 (synctex_forward_search, "synctex-forward-search"), 3605 DEC_CMD2 (synctex_backward_search, "synctex-backward-search"), 3606 3607 /* Rendering */ 3608 DEC_CMD (renderpage), 3609 }; 3610 3611 int main(int argc, char **argv) 3612 { 3613 epdfinfo_t ctx = {0}; 3614 char *line = NULL; 3615 ssize_t read; 3616 size_t line_size; 3617 const char *error_log = "/dev/null"; 3618 3619 #ifdef __MINGW32__ 3620 error_log = "NUL"; 3621 _setmode(_fileno(stdin), _O_BINARY); 3622 _setmode(_fileno(stdout), _O_BINARY); 3623 #endif 3624 3625 if (argc > 2) 3626 { 3627 fprintf(stderr, "usage: epdfinfo [ERROR-LOGFILE]\n"); 3628 exit (EXIT_FAILURE); 3629 } 3630 if (argc == 2) 3631 error_log = argv[1]; 3632 3633 if (! freopen (error_log, "a", stderr)) 3634 err (2, "Unable to redirect stderr"); 3635 3636 #if ! GLIB_CHECK_VERSION(2,36,0) 3637 g_type_init (); 3638 #endif 3639 3640 ctx.documents = g_hash_table_new (g_str_hash, g_str_equal); 3641 3642 setvbuf (stdout, NULL, _IOFBF, BUFSIZ); 3643 3644 while ((read = getline (&line, &line_size, stdin)) != -1) 3645 { 3646 int nargs = 0; 3647 command_arg_t *cmd_args = NULL; 3648 char **args = NULL; 3649 gchar *error_msg = NULL; 3650 int i; 3651 3652 if (read <= 1 || line[read - 1] != '\n') 3653 { 3654 fprintf (stderr, "Skipped parts of a line: `%s'\n", line); 3655 goto next_line; 3656 } 3657 3658 line[read - 1] = '\0'; 3659 args = command_arg_split (line, &nargs); 3660 if (nargs == 0) 3661 continue; 3662 3663 for (i = 0; i < G_N_ELEMENTS (commands); i++) 3664 { 3665 if (! strcmp (commands[i].name, args[0])) 3666 { 3667 if (commands[i].nargs == 0 3668 || (cmd_args = command_arg_parse (&ctx, args + 1, nargs - 1, 3669 commands + i, &error_msg))) 3670 { 3671 commands[i].execute (&ctx, cmd_args); 3672 if (commands[i].nargs > 0) 3673 free_command_args (cmd_args, commands[i].nargs); 3674 } 3675 else 3676 { 3677 printf_error_response ("%s", error_msg ? error_msg : 3678 "Unknown error (this is a bug)"); 3679 } 3680 if (error_msg) 3681 g_free (error_msg); 3682 break; 3683 } 3684 } 3685 if (G_N_ELEMENTS (commands) == i) 3686 { 3687 printf_error_response ("Unknown command: %s", args[0]); 3688 } 3689 for (i = 0; i < nargs; ++i) 3690 g_free (args[i]); 3691 g_free (args); 3692 next_line: 3693 free (line); 3694 line = NULL; 3695 } 3696 3697 if (ferror (stdin)) 3698 err (2, NULL); 3699 exit (EXIT_SUCCESS); 3700 }