dotemacs

My Emacs configuration
git clone git://git.entf.net/dotemacs
Log | Files | Refs | LICENSE

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 }