dotemacs

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

epdfinfo.c (100117B)


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