dotemacs

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

epdfinfo.c (106982B)


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