config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

epdfinfo.c (107258B)


      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   if (nargs == 0)
   1639     {
   1640       PopplerQuadrilateral q;
   1641 
   1642       q.p1.x = r->x1;
   1643       q.p1.y = r->y1;
   1644       q.p2.x = r->x2;
   1645       q.p2.y = r->y1;
   1646       q.p4.x = r->x2;
   1647       q.p4.y = r->y2;
   1648       q.p3.x = r->x1;
   1649       q.p3.y = r->y2;
   1650 
   1651       g_array_append_val (garray, q);
   1652     }
   1653   for (i = 0; i < nargs; ++i)
   1654     {
   1655       PopplerRectangle *rr = &carg.value.rectangle;
   1656 
   1657       error_if_not (command_arg_parse_arg (ctx, args[i], &carg,
   1658                                            ARG_EDGES, error_msg));
   1659       rr->x1 *= width; rr->x2 *= width;
   1660       rr->y1 *= height; rr->y2 *= height;
   1661       annotation_markup_append_text_region (page, selection_style, rr, garray);
   1662     }
   1663   cerror_if_not (garray->len > 0, error_msg, "%s",
   1664                  "Unable to create empty markup annotation");
   1665 
   1666   if (! strcmp (type, "highlight"))
   1667     a = poppler_annot_text_markup_new_highlight (doc->pdf, r, garray);
   1668   else if (! strcmp (type, "squiggly"))
   1669     a = poppler_annot_text_markup_new_squiggly (doc->pdf, r, garray);
   1670   else if (! strcmp (type, "strike-out"))
   1671     a = poppler_annot_text_markup_new_strikeout (doc->pdf, r, garray);
   1672   else if (! strcmp (type, "underline"))
   1673     a = poppler_annot_text_markup_new_underline (doc->pdf, r, garray);
   1674   else
   1675     cerror_if_not (0, error_msg, "Unknown annotation type: %s", type);
   1676 
   1677 #endif
   1678  error:
   1679 #ifdef HAVE_POPPLER_ANNOT_MARKUP
   1680   if (garray) g_array_unref (garray);
   1681   if (region) cairo_region_destroy (region);
   1682 #endif
   1683   return a;
   1684 }
   1685 
   1686 static gboolean
   1687 annotation_edit_validate (const epdfinfo_t *ctx, const command_arg_t *rest,
   1688                           PopplerAnnot *annotation, char **error_msg)
   1689 {
   1690   int nargs = rest->value.rest.nargs;
   1691   char * const *args = rest->value.rest.args;
   1692   int i = 0;
   1693   command_arg_t carg;
   1694 
   1695   const char* error_fmt =
   1696     "Can modify `%s' property only for %s annotations";
   1697 
   1698   while (i < nargs)
   1699     {
   1700       command_arg_type_t atype = ARG_INVALID;
   1701       const char *key = args[i++];
   1702 
   1703       cerror_if_not (i < nargs, error_msg, "Missing a value argument");
   1704 
   1705       if (! strcmp (key, "flags"))
   1706         atype = ARG_NATNUM;
   1707       else if (! strcmp (key, "color"))
   1708         atype = ARG_COLOR;
   1709       else if (! strcmp (key, "contents"))
   1710         atype = ARG_STRING;
   1711       else if (! strcmp (key, "edges"))
   1712         atype = ARG_EDGES_OR_POSITION;
   1713       else if (! strcmp (key, "label"))
   1714         {
   1715           cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg,
   1716                          error_fmt, key, "markup");
   1717           atype = ARG_STRING;
   1718         }
   1719       else if (! strcmp (key, "opacity"))
   1720         {
   1721           cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg,
   1722                          error_fmt, key, "markup");
   1723           atype = ARG_EDGE;
   1724         }
   1725       else if (! strcmp (key, "popup"))
   1726         {
   1727           cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg,
   1728                          error_fmt, key, "markup");
   1729           atype = ARG_EDGES;
   1730         }
   1731       else if (! strcmp (key, "popup-is-open"))
   1732         {
   1733           cerror_if_not (POPPLER_IS_ANNOT_MARKUP (annotation), error_msg,
   1734                          error_fmt, key, "markup");
   1735           atype = ARG_BOOL;
   1736         }
   1737       else if (! strcmp (key, "icon"))
   1738         {
   1739           cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg,
   1740                          error_fmt, key, "text");
   1741           atype = ARG_STRING;
   1742         }
   1743       else if (! strcmp (key, "is-open"))
   1744         {
   1745           cerror_if_not (POPPLER_IS_ANNOT_TEXT (annotation), error_msg,
   1746                          error_fmt, key, "text");
   1747           atype = ARG_BOOL;
   1748         }
   1749       else
   1750         {
   1751           cerror_if_not (0, error_msg,
   1752                          "Unable to modify property `%s'", key);
   1753         }
   1754 
   1755       if (! command_arg_parse_arg (ctx, args[i++], &carg, atype, error_msg))
   1756         return FALSE;
   1757     }
   1758 
   1759   return TRUE;
   1760 
   1761  error:
   1762   return FALSE;
   1763 }
   1764 
   1765 static void
   1766 annotation_print (const annotation_t *annot, /* const */ PopplerPage *page)
   1767 {
   1768   double width, height;
   1769   PopplerAnnotMapping *m;
   1770   const gchar *key;
   1771   PopplerAnnot *a;
   1772   PopplerAnnotMarkup *ma;
   1773   PopplerAnnotText *ta;
   1774   PopplerRectangle r;
   1775   PopplerColor *color;
   1776   gchar *text;
   1777   gdouble opacity;
   1778   cairo_region_t *region = NULL;
   1779   GDate *date;
   1780 
   1781   if (! annot || ! page)
   1782     return;
   1783 
   1784   m = annot->amap;
   1785   key = annot->key;
   1786   a = m->annot;
   1787   poppler_page_get_size (page, &width, &height);
   1788 
   1789   r.x1 = m->area.x1;
   1790   r.x2 = m->area.x2;
   1791   r.y1 = height - m->area.y2;
   1792   r.y2 = height - m->area.y1;
   1793 
   1794 #ifdef HAVE_POPPLER_ANNOT_MARKUP
   1795   if (POPPLER_IS_ANNOT_TEXT_MARKUP (a))
   1796     {
   1797       region = annotation_markup_get_text_regions (page, POPPLER_ANNOT_TEXT_MARKUP (a));
   1798       perror_if_not (region, "%s", "Unable to extract annotation's text regions");
   1799     }
   1800 #endif
   1801 
   1802   /* >>> Any Annotation >>> */
   1803   /* Page */
   1804   printf ("%d:", poppler_page_get_index (page) + 1);
   1805   /* Area */
   1806   printf ("%f %f %f %f:", r.x1 / width, r.y1 / height
   1807           , r.x2 / width, r.y2 / height);
   1808 
   1809   /* Type */
   1810   printf ("%s:", xpoppler_annot_type_string (poppler_annot_get_annot_type (a)));
   1811   /* Internal Key */
   1812   print_response_string (key, COLON);
   1813 
   1814   /* Flags */
   1815   printf ("%d:", poppler_annot_get_flags (a));
   1816 
   1817   /* Color */
   1818   color = poppler_annot_get_color (a);
   1819   if (color)
   1820     {
   1821       /* Reduce 2 Byte to 1 Byte color space  */
   1822       printf ("#%.2x%.2x%.2x", (color->red >> 8)
   1823               , (color->green >> 8)
   1824               , (color->blue >> 8));
   1825       g_free (color);
   1826     }
   1827 
   1828   putchar (':');
   1829 
   1830   /* Text Contents */
   1831   text = poppler_annot_get_contents (a);
   1832   print_response_string (text, COLON);
   1833   g_free (text);
   1834 
   1835   /* Modified Date */
   1836   text = poppler_annot_get_modified (a);
   1837   print_response_string (text, NONE);
   1838   g_free (text);
   1839 
   1840   /* <<< Any Annotation <<< */
   1841 
   1842   /* >>> Markup Annotation >>> */
   1843   if (! POPPLER_IS_ANNOT_MARKUP (a))
   1844     {
   1845       putchar ('\n');
   1846       goto theend;
   1847     }
   1848 
   1849   putchar (':');
   1850   ma = POPPLER_ANNOT_MARKUP (a);
   1851   /* Label */
   1852   text = poppler_annot_markup_get_label (ma);
   1853   print_response_string (text, COLON);
   1854   g_free (text);
   1855 
   1856   /* Subject */
   1857   text = poppler_annot_markup_get_subject (ma);
   1858   print_response_string (text, COLON);
   1859   g_free (text);
   1860 
   1861   /* Opacity */
   1862   opacity = poppler_annot_markup_get_opacity (ma);
   1863   printf ("%f:", opacity);
   1864 
   1865   /* Popup (Area + isOpen) */
   1866   if (poppler_annot_markup_has_popup (ma)
   1867       && poppler_annot_markup_get_popup_rectangle (ma, &r))
   1868     {
   1869       gdouble tmp = r.y1;
   1870       r.y1 = height - r.y2;
   1871       r.y2 = height - tmp;
   1872       printf ("%f %f %f %f:%d:", r.x1 / width, r.y1 / height
   1873               , r.x2 / width, r.y2 / height
   1874               , poppler_annot_markup_get_popup_is_open (ma) ? 1 : 0);
   1875 
   1876     }
   1877   else
   1878     printf ("::");
   1879 
   1880   /* Creation Date */
   1881   date = poppler_annot_markup_get_date (ma);
   1882   if (date != NULL && g_date_valid(date))
   1883     {
   1884       gchar datebuf[128];
   1885       g_date_strftime (datebuf, 127, "%x", date);
   1886       print_response_string (datebuf, NONE);
   1887       g_date_free (date);
   1888     }
   1889 
   1890   /* <<< Markup Annotation <<< */
   1891 
   1892   /* >>>  Text Annotation >>> */
   1893   if (POPPLER_IS_ANNOT_TEXT (a))
   1894     {
   1895       putchar (':');
   1896       ta = POPPLER_ANNOT_TEXT (a);
   1897       /* Text Icon */
   1898       text = poppler_annot_text_get_icon (ta);
   1899       print_response_string (text, COLON);
   1900       g_free (text);
   1901       /* Text State */
   1902       printf ("%s:%d",
   1903               xpoppler_annot_text_state_string (poppler_annot_text_get_state (ta)),
   1904               poppler_annot_text_get_is_open (ta));
   1905     }
   1906 #ifdef HAVE_POPPLER_ANNOT_MARKUP
   1907   /* <<< Text Annotation <<< */
   1908   else if (POPPLER_IS_ANNOT_TEXT_MARKUP (a))
   1909     {
   1910       /* >>> Markup Text Annotation >>> */
   1911       putchar (':');
   1912       region_print (region, width, height);
   1913       /* <<< Markup Text Annotation <<< */
   1914     }
   1915 #endif
   1916   putchar ('\n');
   1917  theend:
   1918 #ifdef HAVE_POPPLER_ANNOT_MARKUP
   1919  error:
   1920 #endif
   1921   if (region) cairo_region_destroy (region);
   1922 }
   1923 
   1924 static void
   1925 attachment_print (PopplerAttachment *att, const char *id, gboolean do_save)
   1926 {
   1927   time_t time;
   1928 
   1929   print_response_string (id, COLON);
   1930   print_response_string (att->name, COLON);
   1931   print_response_string (att->description, COLON);
   1932   if (att->size + 1 != 0)
   1933     printf ("%" G_GSIZE_FORMAT ":", att->size);
   1934   else
   1935     printf ("-1:");
   1936   time = (time_t) att->mtime;
   1937   print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON);
   1938   time = (time_t) att->ctime;
   1939   print_response_string (time > 0 ? strchomp (ctime (&time)) : "", COLON);
   1940   print_response_string (att->checksum ? att->checksum->str : "" , COLON);
   1941   if (do_save)
   1942     {
   1943       char *filename = mktempfile ();
   1944       GError *error = NULL;
   1945       if (filename)
   1946         {
   1947           if (! poppler_attachment_save (att, filename, &error))
   1948             {
   1949               fprintf (stderr, "Writing attachment failed: %s"
   1950                        , error ? error->message : "reason unknown");
   1951               if (error)
   1952                 g_free (error);
   1953             }
   1954           else
   1955             {
   1956               print_response_string (filename, NONE);
   1957             }
   1958           free (filename);
   1959         }
   1960     }
   1961   putchar ('\n');
   1962 }
   1963 
   1964 
   1965 
   1966 /* ================================================================== *
   1967  * Server command implementations
   1968  * ================================================================== */
   1969 
   1970 /* Name: features
   1971    Args: None
   1972    Returns: A list of compile-time features.
   1973    Errors: None
   1974 */
   1975 
   1976 const command_arg_type_t cmd_features_spec[] = {};
   1977 
   1978 static void
   1979 cmd_features (const epdfinfo_t *ctx, const command_arg_t *args)
   1980 {
   1981   const char *features[] = {
   1982 #ifdef HAVE_POPPLER_FIND_OPTS
   1983     "case-sensitive-search",
   1984 #else
   1985     "no-case-sensitive-search",
   1986 #endif
   1987 #ifdef HAVE_POPPLER_ANNOT_WRITE
   1988     "writable-annotations",
   1989 #else
   1990     "no-writable-annotations",
   1991 #endif
   1992 #ifdef HAVE_POPPLER_ANNOT_MARKUP
   1993     "markup-annotations"
   1994 #else
   1995     "no-markup-annotations"
   1996 #endif
   1997   };
   1998   int i;
   1999   OK_BEGIN ();
   2000   for (i = 0; i < G_N_ELEMENTS (features); ++i)
   2001     {
   2002       printf ("%s", features[i]);
   2003       if (i < G_N_ELEMENTS (features) - 1)
   2004         putchar (':');
   2005     }
   2006   putchar ('\n');
   2007   OK_END ();
   2008 }
   2009 
   2010 
   2011 /* Name: open
   2012    Args: filename password
   2013    Returns: Nothing
   2014    Errors: If file can't be opened or is not a PDF document.
   2015 */
   2016 
   2017 const command_arg_type_t cmd_open_spec[] =
   2018   {
   2019     ARG_NONEMPTY_STRING,        /* filename */
   2020     ARG_STRING,                 /* password */
   2021   };
   2022 
   2023 static void
   2024 cmd_open (const epdfinfo_t *ctx, const command_arg_t *args)
   2025 {
   2026   const char *filename = args[0].value.string;
   2027   const char *passwd = args[1].value.string;
   2028   GError *gerror = NULL;
   2029   document_t *doc;
   2030 
   2031   if (! *passwd)
   2032     passwd = NULL;
   2033 
   2034   doc = document_open(ctx, filename, passwd, &gerror);
   2035   perror_if_not (doc, "Error opening %s:%s", filename,
   2036                 gerror ? gerror->message : "unknown error");
   2037   OK ();
   2038 
   2039  error:
   2040   if (gerror)
   2041     {
   2042       g_error_free (gerror);
   2043       gerror = NULL;
   2044     }
   2045 }
   2046 
   2047 /* Name: close
   2048    Args: filename
   2049    Returns: 1 if file was open, otherwise 0.
   2050    Errors: None
   2051 */
   2052 
   2053 const command_arg_type_t cmd_close_spec[] =
   2054   {
   2055     ARG_NONEMPTY_STRING         /* filename */
   2056   };
   2057 
   2058 static void
   2059 cmd_close (const epdfinfo_t *ctx, const command_arg_t *args)
   2060 {
   2061   document_t *doc = g_hash_table_lookup(ctx->documents, args->value.string);
   2062 
   2063   g_hash_table_remove (ctx->documents, args->value.string);
   2064   free_document (doc);
   2065   OK_BEGIN ();
   2066   puts (doc ? "1" : "0");
   2067   OK_END ();
   2068 }
   2069 
   2070 /* Name: closeall
   2071    Args: None
   2072    Returns: Nothing
   2073    Errors: None
   2074 */
   2075 static void
   2076 cmd_closeall (const epdfinfo_t *ctx, const command_arg_t *args)
   2077 {
   2078   GHashTableIter iter;
   2079   gpointer key, value;
   2080 
   2081   g_hash_table_iter_init (&iter, ctx->documents);
   2082   while (g_hash_table_iter_next (&iter, &key, &value))
   2083     {
   2084       document_t *doc = (document_t*) value;
   2085       free_document (doc);
   2086       g_hash_table_iter_remove (&iter);
   2087     }
   2088   OK ();
   2089 }
   2090 
   2091 
   2092 const command_arg_type_t cmd_search_regexp_spec[] =
   2093   {
   2094     ARG_DOC,
   2095     ARG_NATNUM,                 /* first page */
   2096     ARG_NATNUM,                 /* last page */
   2097     ARG_NONEMPTY_STRING,        /* regexp */
   2098     ARG_NATNUM,                 /* compile flags */
   2099     ARG_NATNUM                  /* match flags */
   2100   };
   2101 
   2102 static void
   2103 cmd_search_regexp(const epdfinfo_t *ctx, const command_arg_t *args)
   2104 {
   2105   PopplerDocument *doc = args[0].value.doc->pdf;
   2106   int first = args[1].value.natnum;
   2107   int last = args[2].value.natnum;
   2108   const gchar *regexp = args[3].value.string;
   2109   GRegexCompileFlags cflags = args[4].value.natnum;
   2110   GRegexMatchFlags mflags = args[5].value.natnum;
   2111   double width, height;
   2112   int pn;
   2113   GError *gerror = NULL;
   2114   GRegex *re = NULL;
   2115 
   2116   NORMALIZE_PAGE_ARG (doc, &first, &last);
   2117 
   2118   re = g_regex_new (regexp, cflags, mflags, &gerror);
   2119   perror_if_not (NULL == gerror, "Invalid regexp: %s", gerror->message);
   2120 
   2121   OK_BEGIN ();
   2122   for (pn = first; pn <= last; ++pn)
   2123     {
   2124       PopplerPage *page = poppler_document_get_page(doc, pn - 1);
   2125       char *text;
   2126       PopplerRectangle *rectangles = NULL;
   2127       guint nrectangles;
   2128       GMatchInfo *match = NULL;
   2129 
   2130       if (! page)
   2131         continue;
   2132 
   2133       text = poppler_page_get_text (page);
   2134       poppler_page_get_text_layout (page, &rectangles, &nrectangles);
   2135       poppler_page_get_size (page, &width, &height);
   2136       g_regex_match (re, text, 0, &match);
   2137 
   2138       while (g_match_info_matches (match))
   2139         {
   2140           const double scale = 100.0;
   2141           gint start, end, ustart, ulen;
   2142           gchar *string = NULL;
   2143           gchar *line = NULL;
   2144           int i;
   2145 
   2146           /* Does this ever happen ? */
   2147           if (! g_match_info_fetch_pos (match, 0, &start, &end))
   2148             continue;
   2149 
   2150           string = g_match_info_fetch (match, 0);
   2151           ustart = g_utf8_strlen (text, start);
   2152           ulen = g_utf8_strlen (string, -1);
   2153 
   2154           cairo_region_t *region = cairo_region_create ();
   2155           /* Merge matched glyph rectangles. Scale them so we're able
   2156              to use cairo . */
   2157           if (ulen > 0)
   2158             {
   2159               assert (ustart < nrectangles
   2160                       && ustart + ulen <= nrectangles);
   2161               line = poppler_page_get_selected_text
   2162                      (page, POPPLER_SELECTION_LINE, rectangles + ustart);
   2163 
   2164               for (i = ustart; i < ustart + ulen; ++i)
   2165                 {
   2166                   PopplerRectangle *r = rectangles + i;
   2167                   cairo_rectangle_int_t c;
   2168 
   2169                   c.x = (int) (scale * r->x1 + 0.5);
   2170                   c.y = (int) (scale * r->y1 + 0.5);
   2171                   c.width = (int) (scale * (r->x2 - r->x1) + 0.5);
   2172                   c.height = (int) (scale * (r->y2 - r->y1) + 0.5);
   2173 
   2174                   cairo_region_union_rectangle (region, &c);
   2175                 }
   2176 
   2177             }
   2178 
   2179           printf ("%d:", pn);
   2180           print_response_string (string, COLON);
   2181           print_response_string (strchomp (line), COLON);
   2182           region_print (region, width * scale, height * scale);
   2183           putchar ('\n');
   2184           cairo_region_destroy (region);
   2185           g_free (string);
   2186           g_free (line);
   2187           g_match_info_next (match, NULL);
   2188         }
   2189       g_free (rectangles);
   2190       g_object_unref (page);
   2191       g_free (text);
   2192       g_match_info_free (match);
   2193     }
   2194   OK_END ();
   2195 
   2196  error:
   2197   if (re) g_regex_unref (re);
   2198   if (gerror) g_error_free (gerror);
   2199 }
   2200 
   2201 const command_arg_type_t cmd_regexp_flags_spec[] =
   2202   {
   2203   };
   2204 
   2205 static void
   2206 cmd_regexp_flags (const epdfinfo_t *ctx, const command_arg_t *args)
   2207 {
   2208   OK_BEGIN ();
   2209   printf ("caseless:%d\n", G_REGEX_CASELESS);
   2210   printf ("multiline:%d\n", G_REGEX_MULTILINE);
   2211   printf ("dotall:%d\n", G_REGEX_DOTALL);
   2212   printf ("extended:%d\n", G_REGEX_EXTENDED);
   2213   printf ("anchored:%d\n", G_REGEX_ANCHORED);
   2214   printf ("dollar-endonly:%d\n", G_REGEX_DOLLAR_ENDONLY);
   2215   printf ("ungreedy:%d\n", G_REGEX_UNGREEDY);
   2216   printf ("raw:%d\n", G_REGEX_RAW);
   2217   printf ("no-auto-capture:%d\n", G_REGEX_NO_AUTO_CAPTURE);
   2218   printf ("optimize:%d\n", G_REGEX_OPTIMIZE);
   2219   printf ("dupnames:%d\n", G_REGEX_DUPNAMES);
   2220   printf ("newline-cr:%d\n", G_REGEX_NEWLINE_CR);
   2221   printf ("newline-lf:%d\n", G_REGEX_NEWLINE_LF);
   2222   printf ("newline-crlf:%d\n", G_REGEX_NEWLINE_CRLF);
   2223 
   2224   printf ("match-anchored:%d\n", G_REGEX_MATCH_ANCHORED);
   2225   printf ("match-notbol:%d\n", G_REGEX_MATCH_NOTBOL);
   2226   printf ("match-noteol:%d\n", G_REGEX_MATCH_NOTEOL);
   2227   printf ("match-notempty:%d\n", G_REGEX_MATCH_NOTEMPTY);
   2228   printf ("match-partial:%d\n", G_REGEX_MATCH_PARTIAL);
   2229   printf ("match-newline-cr:%d\n", G_REGEX_MATCH_NEWLINE_CR);
   2230   printf ("match-newline-lf:%d\n", G_REGEX_MATCH_NEWLINE_LF);
   2231   printf ("match-newline-crlf:%d\n", G_REGEX_MATCH_NEWLINE_CRLF);
   2232   printf ("match-newline-any:%d\n", G_REGEX_MATCH_NEWLINE_ANY);
   2233 
   2234   OK_END ();
   2235 }
   2236 
   2237 
   2238 const command_arg_type_t cmd_search_string_spec[] =
   2239   {
   2240     ARG_DOC,
   2241     ARG_NATNUM,                 /* first page */
   2242     ARG_NATNUM,                 /* last page */
   2243     ARG_NONEMPTY_STRING,        /* search string */
   2244     ARG_BOOL,                   /* ignore-case */
   2245   };
   2246 
   2247 static void
   2248 cmd_search_string(const epdfinfo_t *ctx, const command_arg_t *args)
   2249 {
   2250   PopplerDocument *doc = args[0].value.doc->pdf;
   2251   int first = args[1].value.natnum;
   2252   int last = args[2].value.natnum;
   2253   const char *string = args[3].value.string;
   2254   gboolean ignore_case = args[4].value.flag;
   2255   GList *list, *item;
   2256   double width, height;
   2257   int pn;
   2258 #ifdef HAVE_POPPLER_FIND_OPTS
   2259   PopplerFindFlags flags = ignore_case ? 0 : POPPLER_FIND_CASE_SENSITIVE;
   2260 #endif
   2261 
   2262   NORMALIZE_PAGE_ARG (doc, &first, &last);
   2263   OK_BEGIN ();
   2264   for (pn = first; pn <= last; ++pn)
   2265     {
   2266       PopplerPage *page = poppler_document_get_page(doc, pn - 1);
   2267 
   2268       if (! page)
   2269         continue;
   2270 
   2271 #ifdef HAVE_POPPLER_FIND_OPTS
   2272       list = poppler_page_find_text_with_options(page, string, flags);
   2273 #else
   2274       list = poppler_page_find_text(page, string);
   2275 #endif
   2276 
   2277       poppler_page_get_size (page, &width, &height);
   2278 
   2279       for (item = list; item; item = item->next)
   2280         {
   2281           gchar *line, *match;
   2282           PopplerRectangle *r = item->data;
   2283           gdouble y1 =  r->y1;
   2284 
   2285           r->y1 = height - r->y2;
   2286           r->y2 = height - y1;
   2287 
   2288           printf ("%d:", pn);
   2289           line = strchomp (poppler_page_get_selected_text
   2290                            (page, POPPLER_SELECTION_LINE, r));
   2291           match = strchomp (poppler_page_get_selected_text
   2292                            (page, POPPLER_SELECTION_GLYPH, r));
   2293           print_response_string (match, COLON);
   2294           print_response_string (line, COLON);
   2295           printf ("%f %f %f %f\n",
   2296                   r->x1 / width, r->y1 / height,
   2297                   r->x2 / width, r->y2 / height);
   2298           g_free (line);
   2299           g_free (match);
   2300           poppler_rectangle_free (r);
   2301         }
   2302       g_list_free (list);
   2303       g_object_unref (page);
   2304     }
   2305   OK_END ();
   2306 }
   2307 
   2308 /* Name: metadata
   2309    Args: filename
   2310    Returns: PDF's metadata
   2311    Errors: None
   2312 
   2313    title author subject keywords creator producer pdf-version create-date mod-date
   2314 
   2315    Dates are in seconds since the epoche.
   2316 
   2317 */
   2318 
   2319 const command_arg_type_t cmd_metadata_spec[] =
   2320   {
   2321     ARG_DOC,
   2322   };
   2323 
   2324 static void
   2325 cmd_metadata (const epdfinfo_t *ctx, const command_arg_t *args)
   2326 {
   2327   PopplerDocument *doc = args[0].value.doc->pdf;
   2328   time_t date;
   2329   gchar *md[6];
   2330   gchar *title;
   2331   int i;
   2332   char *time_str;
   2333 
   2334   OK_BEGIN ();
   2335 
   2336   title = poppler_document_get_title (doc);
   2337   print_response_string (title, COLON);
   2338   g_free (title);
   2339 
   2340   md[0] = poppler_document_get_author (doc);
   2341   md[1] = poppler_document_get_subject (doc);
   2342   md[2] = poppler_document_get_keywords (doc);
   2343   md[3] = poppler_document_get_creator (doc);
   2344   md[4] = poppler_document_get_producer (doc);
   2345   md[5] = poppler_document_get_pdf_version_string (doc);
   2346 
   2347   for (i = 0; i < 6; ++i)
   2348     {
   2349       print_response_string (md[i], COLON);
   2350       g_free (md[i]);
   2351     }
   2352 
   2353   date = poppler_document_get_creation_date (doc);
   2354   time_str = strchomp (ctime (&date));
   2355   print_response_string (time_str ? time_str : "", COLON);
   2356   date = poppler_document_get_modification_date (doc);
   2357   time_str = strchomp (ctime (&date));
   2358   print_response_string (time_str ? time_str : "", NEWLINE);
   2359   OK_END ();
   2360 }
   2361 
   2362 /* Name: outline
   2363    Args: filename
   2364 
   2365    Returns: The documents outline (or index) as a, possibly empty,
   2366    list of records:
   2367 
   2368    tree-level ACTION
   2369 
   2370    See cmd_pagelinks for how ACTION is constructed.
   2371 
   2372    Errors: None
   2373 */
   2374 
   2375 static void
   2376 cmd_outline_walk (PopplerDocument *doc, PopplerIndexIter *iter, int depth)
   2377 {
   2378   do
   2379     {
   2380       PopplerIndexIter *child;
   2381       PopplerAction *action = poppler_index_iter_get_action (iter);
   2382 
   2383       if (! action)
   2384         continue;
   2385 
   2386       if (action_is_handled (action))
   2387         {
   2388           printf ("%d:", depth);
   2389           action_print (doc, action);
   2390         }
   2391 
   2392       child = poppler_index_iter_get_child (iter);
   2393       if (child)
   2394         {
   2395           cmd_outline_walk (doc, child, depth + 1);
   2396         }
   2397       poppler_action_free (action);
   2398       poppler_index_iter_free (child);
   2399     } while (poppler_index_iter_next (iter));
   2400 }
   2401 
   2402 const command_arg_type_t cmd_outline_spec[] =
   2403   {
   2404     ARG_DOC,
   2405   };
   2406 
   2407 static void
   2408 cmd_outline (const epdfinfo_t *ctx, const command_arg_t *args)
   2409 {
   2410   PopplerIndexIter *iter = poppler_index_iter_new (args->value.doc->pdf);
   2411   OK_BEGIN ();
   2412   if (iter)
   2413     {
   2414       cmd_outline_walk (args->value.doc->pdf, iter, 1);
   2415       poppler_index_iter_free (iter);
   2416     }
   2417   OK_END ();
   2418 }
   2419 
   2420 /* Name: quit
   2421    Args: None
   2422    Returns: Nothing
   2423    Errors: None
   2424 
   2425    Close all documents and exit.
   2426 */
   2427 
   2428 
   2429 const command_arg_type_t cmd_quit_spec[] = {};
   2430 
   2431 static void
   2432 cmd_quit (const epdfinfo_t *ctx, const command_arg_t *args)
   2433 {
   2434   cmd_closeall (ctx, args);
   2435   exit (EXIT_SUCCESS);
   2436 }
   2437 
   2438 /* Name: number-of-pages
   2439    Args: filename
   2440    Returns: The number of pages.
   2441    Errors: None
   2442 */
   2443 
   2444 
   2445 const command_arg_type_t cmd_number_of_pages_spec[] =
   2446   {
   2447     ARG_DOC
   2448   };
   2449 
   2450 static void
   2451 cmd_number_of_pages (const epdfinfo_t *ctx, const command_arg_t *args)
   2452 {
   2453   int npages = poppler_document_get_n_pages (args->value.doc->pdf);
   2454   OK_BEGIN ();
   2455   printf ("%d\n", npages);
   2456   OK_END ();
   2457 }
   2458 
   2459 /* Name: pagelinks
   2460    Args: filename page
   2461    Returns: A list of linkmaps:
   2462 
   2463    edges ACTION ,
   2464 
   2465    where ACTION is one of
   2466 
   2467    'goto-dest' title page top
   2468    'goto-remote' title filename page top
   2469    'uri' title URI
   2470    'launch' title program arguments
   2471 
   2472    top is desired vertical position, filename is the target PDF of the
   2473    `goto-remote' link.
   2474 
   2475    Errors: None
   2476 */
   2477 
   2478 
   2479 const command_arg_type_t cmd_pagelinks_spec[] =
   2480   {
   2481     ARG_DOC,
   2482     ARG_NATNUM                  /* page number */
   2483   };
   2484 
   2485 static void
   2486 cmd_pagelinks(const epdfinfo_t *ctx, const command_arg_t *args)
   2487 {
   2488   PopplerDocument *doc = args[0].value.doc->pdf;
   2489   PopplerPage *page = NULL;
   2490   int pn = args[1].value.natnum;
   2491   double width, height;
   2492   GList *link_map = NULL, *item;
   2493 
   2494   page = poppler_document_get_page (doc, pn - 1);
   2495   perror_if_not (page, "No such page %d", pn);
   2496   poppler_page_get_size (page, &width, &height);
   2497   link_map = poppler_page_get_link_mapping (page);
   2498 
   2499   OK_BEGIN ();
   2500   for (item = g_list_last (link_map); item; item = item->prev)
   2501     {
   2502 
   2503       PopplerLinkMapping *link = item->data;
   2504       PopplerRectangle *r = &link->area;
   2505       gdouble y1 = r->y1;
   2506       /* LinkMappings have a different gravity. */
   2507       r->y1 = height - r->y2;
   2508       r->y2 = height - y1;
   2509 
   2510       if (! action_is_handled (link->action))
   2511         continue;
   2512 
   2513       printf ("%f %f %f %f:",
   2514               r->x1 / width, r->y1 / height,
   2515               r->x2 / width, r->y2 / height);
   2516       action_print (doc, link->action);
   2517     }
   2518   OK_END ();
   2519  error:
   2520   if (page) g_object_unref (page);
   2521   if (link_map) poppler_page_free_link_mapping (link_map);
   2522 }
   2523 
   2524 /* Name: gettext
   2525    Args: filename page edges selection-style
   2526    Returns: The selection's text.
   2527    Errors: If page is out of range.
   2528 
   2529    For the selection-style argument see getselection command.
   2530 */
   2531 
   2532 
   2533 const command_arg_type_t cmd_gettext_spec[] =
   2534   {
   2535     ARG_DOC,
   2536     ARG_NATNUM,                 /* page number */
   2537     ARG_EDGES,                  /* selection */
   2538     ARG_NATNUM                  /* selection-style */
   2539   };
   2540 
   2541 static void
   2542 cmd_gettext(const epdfinfo_t *ctx, const command_arg_t *args)
   2543 {
   2544   PopplerDocument *doc = args[0].value.doc->pdf;
   2545   int pn = args[1].value.natnum;
   2546   PopplerRectangle r = args[2].value.rectangle;
   2547   int selection_style = args[3].value.natnum;
   2548   PopplerPage *page = NULL;
   2549   double width, height;
   2550   gchar *text = NULL;
   2551 
   2552   selection_style = xpoppler_validate_selection_style (selection_style);
   2553   page = poppler_document_get_page (doc, pn - 1);
   2554   perror_if_not (page, "No such page %d", pn);
   2555   poppler_page_get_size (page, &width, &height);
   2556   r.x1 = r.x1 * width;
   2557   r.x2 = r.x2 * width;
   2558   r.y1 = r.y1 * height;
   2559   r.y2 = r.y2 * height;
   2560   /* printf ("%f %f %f %f , %f %f\n", r.x1, r.y1, r.x2, r.y2, width, height); */
   2561   text = poppler_page_get_selected_text (page, selection_style, &r);
   2562 
   2563   OK_BEGIN ();
   2564   print_response_string (text, NEWLINE);
   2565   OK_END ();
   2566 
   2567  error:
   2568   g_free (text);
   2569   if (page) g_object_unref (page);
   2570 }
   2571 
   2572 /* Name: getselection
   2573    Args: filename page edges selection-selection_style
   2574    Returns: The selection's text.
   2575    Errors: If page is out of range.
   2576 
   2577    selection-selection_style should be as follows.
   2578 
   2579    0 (POPPLER_SELECTION_GLYPH)
   2580 	glyph is the minimum unit for selection
   2581 
   2582    1 (POPPLER_SELECTION_WORD)
   2583 	word is the minimum unit for selection
   2584 
   2585    2 (POPPLER_SELECTION_LINE)
   2586 	line is the minimum unit for selection
   2587 */
   2588 
   2589 
   2590 const command_arg_type_t cmd_getselection_spec[] =
   2591   {
   2592     ARG_DOC,
   2593     ARG_NATNUM,                 /* page number */
   2594     ARG_EDGES,                  /* selection */
   2595     ARG_NATNUM                  /* selection-style */
   2596   };
   2597 
   2598 static void
   2599 cmd_getselection (const epdfinfo_t *ctx, const command_arg_t *args)
   2600 {
   2601   PopplerDocument *doc = args[0].value.doc->pdf;
   2602   int pn = args[1].value.natnum;
   2603   PopplerRectangle r = args[2].value.rectangle;
   2604   int selection_style = args[3].value.natnum;
   2605   gdouble width, height;
   2606   cairo_region_t *region = NULL;
   2607   PopplerPage *page = NULL;
   2608   int i;
   2609 
   2610   selection_style = xpoppler_validate_selection_style (selection_style);
   2611   page = poppler_document_get_page (doc, pn - 1);
   2612   perror_if_not (page, "No such page %d", pn);
   2613   poppler_page_get_size (page, &width, &height);
   2614 
   2615   r.x1 = r.x1 * width;
   2616   r.x2 = r.x2 * width;
   2617   r.y1 = r.y1 * height;
   2618   r.y2 = r.y2 * height;
   2619 
   2620   region = poppler_page_get_selected_region (page, 1.0, selection_style, &r);
   2621 
   2622   OK_BEGIN ();
   2623   for (i = 0; i < cairo_region_num_rectangles (region); ++i)
   2624     {
   2625       cairo_rectangle_int_t r;
   2626 
   2627       cairo_region_get_rectangle (region, i, &r);
   2628       printf ("%f %f %f %f\n",
   2629               r.x / width,
   2630               r.y / height,
   2631               (r.x + r.width) / width,
   2632               (r.y + r.height) / height);
   2633     }
   2634   OK_END ();
   2635 
   2636  error:
   2637   if (region) cairo_region_destroy (region);
   2638   if (page) g_object_unref (page);
   2639 }
   2640 
   2641 /* Name: pagesize
   2642    Args: filename page
   2643    Returns: width height
   2644    Errors: If page is out of range.
   2645 */
   2646 
   2647 
   2648 const command_arg_type_t cmd_pagesize_spec[] =
   2649   {
   2650     ARG_DOC,
   2651     ARG_NATNUM                  /* page number */
   2652   };
   2653 
   2654 static void
   2655 cmd_pagesize(const epdfinfo_t *ctx, const command_arg_t *args)
   2656 {
   2657   PopplerDocument *doc = args[0].value.doc->pdf;
   2658   int pn = args[1].value.natnum;
   2659   PopplerPage *page = NULL;
   2660   double width, height;
   2661 
   2662 
   2663   page = poppler_document_get_page (doc, pn - 1);
   2664   perror_if_not (page, "No such page %d", pn);
   2665   poppler_page_get_size (page, &width, &height);
   2666 
   2667   OK_BEGIN ();
   2668   printf ("%f:%f\n", width, height);
   2669   OK_END ();
   2670 
   2671  error:
   2672   if (page) g_object_unref (page);
   2673 }
   2674 
   2675 /* Annotations */
   2676 
   2677 /* Name: getannots
   2678    Args: filename firstpage lastpage
   2679    Returns: The list of annotations of this page.
   2680 
   2681    For all annotations
   2682 
   2683    page edges type key flags color contents mod-date
   2684 
   2685    ,where
   2686 
   2687    name is a document-unique name,
   2688    flags is PopplerAnnotFlag bitmask,
   2689    color is 3-byte RGB hex number and
   2690 
   2691    Then
   2692 
   2693    label subject opacity popup-edges popup-is-open create-date
   2694 
   2695    if this is a markup annotation and additionally
   2696 
   2697    text-icon text-state
   2698 
   2699    for markup text annotations.
   2700 
   2701    Errors: If page is out of range.
   2702 */
   2703 
   2704 
   2705 const command_arg_type_t cmd_getannots_spec[] =
   2706   {
   2707     ARG_DOC,
   2708     ARG_NATNUM,                 /* first page */
   2709     ARG_NATNUM                  /* last page */
   2710   };
   2711 
   2712 static void
   2713 cmd_getannots(const epdfinfo_t *ctx, const command_arg_t *args)
   2714 {
   2715   PopplerDocument *doc = args[0].value.doc->pdf;
   2716   gint first = args[1].value.natnum;
   2717   gint last = args[2].value.natnum;
   2718   GList *list;
   2719   gint pn;
   2720 
   2721   first = MAX(1, first);
   2722   if (last <= 0)
   2723     last = poppler_document_get_n_pages (doc);
   2724   else
   2725     last = MIN(last, poppler_document_get_n_pages (doc));
   2726 
   2727   OK_BEGIN ();
   2728   for (pn = first; pn <= last; ++pn)
   2729     {
   2730       GList *annots = annoation_get_for_page (args->value.doc, pn);
   2731       PopplerPage *page = poppler_document_get_page (doc, pn - 1);
   2732 
   2733       if (! page)
   2734         continue;
   2735 
   2736       for (list = annots; list; list = list->next)
   2737         {
   2738           annotation_t *annot = (annotation_t *)list->data;
   2739           annotation_print (annot, page);
   2740         }
   2741       g_object_unref (page);
   2742     }
   2743   OK_END ();
   2744 }
   2745 
   2746 /* Name: getannot
   2747    Args: filename name
   2748    Returns: The annotation for name, see cmd_getannots.
   2749    Errors: If no annotation named ,name' exists.
   2750 */
   2751 
   2752 
   2753 const command_arg_type_t cmd_getannot_spec[] =
   2754   {
   2755     ARG_DOC,
   2756     ARG_NONEMPTY_STRING,        /* annotation's key */
   2757   };
   2758 
   2759 static void
   2760 cmd_getannot (const epdfinfo_t *ctx, const command_arg_t *args)
   2761 {
   2762   document_t *doc = args->value.doc;
   2763   const gchar *key = args[1].value.string;
   2764   PopplerPage *page = NULL;
   2765   annotation_t *a = annotation_get_by_key (doc, key);
   2766   gint index;
   2767 
   2768   perror_if_not (a, "No such annotation: %s", key);
   2769   index = poppler_annot_get_page_index (a->amap->annot);
   2770   if (index >= 0)
   2771     page = poppler_document_get_page (doc->pdf, index);
   2772   perror_if_not (page, "Unable to get page %d", index + 1);
   2773 
   2774   OK_BEGIN ();
   2775   annotation_print (a, page);
   2776   OK_END ();
   2777 
   2778  error:
   2779   if (page) g_object_unref (page);
   2780 }
   2781 
   2782 /* Name: getannot_attachment
   2783    Args: filename name [output-filename]
   2784    Returns: name description size mtime ctime output-filename
   2785    Errors: If no annotation named ,name' exists or output-filename is
   2786    not writable.
   2787 */
   2788 
   2789 
   2790 const command_arg_type_t cmd_getattachment_from_annot_spec[] =
   2791   {
   2792     ARG_DOC,
   2793     ARG_NONEMPTY_STRING,        /* annotation's name */
   2794     ARG_BOOL                    /* save attachment */
   2795   };
   2796 
   2797 static void
   2798 cmd_getattachment_from_annot (const epdfinfo_t *ctx, const command_arg_t *args)
   2799 {
   2800   document_t *doc = args->value.doc;
   2801   const gchar *key = args[1].value.string;
   2802   gboolean do_save = args[2].value.flag;
   2803   PopplerAttachment *att = NULL;
   2804   annotation_t *a = annotation_get_by_key (doc, key);
   2805   gchar *id = NULL;
   2806 
   2807   perror_if_not (a, "No such annotation: %s", key);
   2808   perror_if_not (POPPLER_IS_ANNOT_FILE_ATTACHMENT (a->amap->annot),
   2809                 "Not a file annotation: %s", key);
   2810   att = poppler_annot_file_attachment_get_attachment
   2811         (POPPLER_ANNOT_FILE_ATTACHMENT (a->amap->annot));
   2812   perror_if_not (att, "Unable to get attachment: %s", key);
   2813   id = g_strdup_printf ("attachment-%s", key);
   2814 
   2815   OK_BEGIN ();
   2816   attachment_print (att, id, do_save);
   2817   OK_END ();
   2818 
   2819  error:
   2820   if (att) g_object_unref (att);
   2821   if (id) g_free (id);
   2822 }
   2823 
   2824 
   2825 /* document-level attachments */
   2826 const command_arg_type_t cmd_getattachments_spec[] =
   2827   {
   2828     ARG_DOC,
   2829     ARG_BOOL,        /* save attachments */
   2830   };
   2831 
   2832 static void
   2833 cmd_getattachments (const epdfinfo_t *ctx, const command_arg_t *args)
   2834 {
   2835   document_t *doc = args->value.doc;
   2836   gboolean do_save = args[1].value.flag;
   2837   GList *item;
   2838   GList *attmnts = poppler_document_get_attachments (doc->pdf);
   2839   int i;
   2840 
   2841   OK_BEGIN ();
   2842   for (item = attmnts, i = 0; item; item = item->next, ++i)
   2843     {
   2844       PopplerAttachment *att = (PopplerAttachment*) item->data;
   2845       gchar *id = g_strdup_printf ("attachment-document-%d", i);
   2846 
   2847       attachment_print (att, id, do_save);
   2848       g_object_unref (att);
   2849       g_free (id);
   2850     }
   2851   g_list_free (attmnts);
   2852 
   2853   OK_END ();
   2854 }
   2855 
   2856 #ifdef HAVE_POPPLER_ANNOT_WRITE
   2857 
   2858 const command_arg_type_t cmd_addannot_spec[] =
   2859   {
   2860     ARG_DOC,
   2861     ARG_NATNUM,                 /* page number */
   2862     ARG_STRING,                 /* type */
   2863     ARG_NATNUM,                 /* selection-style */
   2864     ARG_EDGES_OR_POSITION,      /* edges or position (uses default size) */
   2865     ARG_REST,                  /* markup regions */
   2866   };
   2867 
   2868 static void
   2869 cmd_addannot (const epdfinfo_t *ctx, const command_arg_t *args)
   2870 {
   2871 
   2872   document_t *doc = args->value.doc;
   2873   gint pn = args[1].value.natnum;
   2874   const char *type_string = args[2].value.string;
   2875   int selection_style = args[3].value.natnum;
   2876   PopplerRectangle r = args[4].value.rectangle;
   2877   int i;
   2878   PopplerPage *page = NULL;
   2879   double width, height;
   2880   PopplerAnnot *pa;
   2881   PopplerAnnotMapping *amap;
   2882   annotation_t *a;
   2883   gchar *key;
   2884   GList *annotations;
   2885   gdouble y2;
   2886   char *error_msg = NULL;
   2887 
   2888   selection_style = xpoppler_validate_selection_style (selection_style);
   2889   page = poppler_document_get_page (doc->pdf, pn - 1);
   2890   perror_if_not (page, "Unable to get page %d", pn);
   2891   poppler_page_get_size (page, &width, &height);
   2892   r.x1 *= width; r.x2 *= width;
   2893   r.y1 *= height; r.y2 *= height;
   2894   if (r.y2 < 0)
   2895     r.y2 = r.y1 + 24;
   2896   if (r.x2 < 0)
   2897     r.x2 = r.x1 + 24;
   2898   y2 = r.y2;
   2899   r.y2 = height - r.y1;
   2900   r.y1 = height - y2;
   2901 
   2902   pa = annotation_new (ctx, doc, page, type_string, selection_style, &r, &args[5],
   2903                        &error_msg);
   2904   perror_if_not (pa, "Creating annotation failed: %s",
   2905                  error_msg ? error_msg : "Reason unknown");
   2906   amap = poppler_annot_mapping_new ();
   2907   amap->area = r;
   2908   amap->annot = pa;
   2909   annotations = annoation_get_for_page (doc, pn);
   2910 
   2911   i = g_list_length (annotations);
   2912   key = g_strdup_printf ("annot-%d-%d", pn, i);
   2913   while (g_hash_table_lookup (doc->annotations.keys, key))
   2914     {
   2915       g_free (key);
   2916       key = g_strdup_printf ("annot-%d-%d", pn, ++i);
   2917     }
   2918   a = g_malloc (sizeof (annotation_t));
   2919   a->amap = amap;
   2920   a->key = key;
   2921   doc->annotations.pages[pn - 1] =
   2922     g_list_prepend (annotations, a);
   2923   g_hash_table_insert (doc->annotations.keys, key, a);
   2924   poppler_page_add_annot (page, pa);
   2925   OK_BEGIN ();
   2926   annotation_print (a, page);
   2927   OK_END ();
   2928 
   2929  error:
   2930   if (page) g_object_unref (page);
   2931   if (error_msg) g_free (error_msg);
   2932 }
   2933 
   2934 
   2935 const command_arg_type_t cmd_delannot_spec[] =
   2936   {
   2937     ARG_DOC,
   2938     ARG_NONEMPTY_STRING         /* Annotation's key */
   2939   };
   2940 
   2941 static void
   2942 cmd_delannot (const epdfinfo_t *ctx, const command_arg_t *args)
   2943 {
   2944   document_t *doc = args->value.doc;
   2945   const gchar *key = args[1].value.string;
   2946   PopplerPage *page = NULL;
   2947   annotation_t *a = annotation_get_by_key (doc, key);
   2948   gint pn;
   2949 
   2950   perror_if_not (a, "No such annotation: %s", key);
   2951   pn = poppler_annot_get_page_index (a->amap->annot) + 1;
   2952   if (pn >= 1)
   2953     page = poppler_document_get_page (doc->pdf, pn - 1);
   2954   perror_if_not (page, "Unable to get page %d", pn);
   2955   poppler_page_remove_annot (page, a->amap->annot);
   2956   doc->annotations.pages[pn - 1] =
   2957     g_list_remove (doc->annotations.pages[pn - 1], a);
   2958   g_hash_table_remove (doc->annotations.keys, a->key);
   2959   poppler_annot_mapping_free(a->amap);
   2960   OK ();
   2961 
   2962  error:
   2963   if (a)
   2964     {
   2965       g_free (a->key);
   2966       g_free (a);
   2967     }
   2968   if (page) g_object_unref (page);
   2969 }
   2970 
   2971 const command_arg_type_t cmd_editannot_spec[] =
   2972   {
   2973     ARG_DOC,
   2974     ARG_NONEMPTY_STRING,        /* annotation key */
   2975     ARG_REST                    /* (KEY VALUE ...) */
   2976   };
   2977 
   2978 static void
   2979 cmd_editannot (const epdfinfo_t *ctx, const command_arg_t *args)
   2980 {
   2981   document_t *doc = args->value.doc;
   2982   const char *key = args[1].value.string;
   2983   int nrest_args = args[2].value.rest.nargs;
   2984   char * const *rest_args = args[2].value.rest.args;
   2985   annotation_t *a = annotation_get_by_key (doc, key);
   2986   PopplerAnnot *pa;
   2987   PopplerPage *page = NULL;
   2988   int i = 0;
   2989   gint index;
   2990   char *error_msg = NULL;
   2991   command_arg_t carg;
   2992   const char *unexpected_parse_error = "Internal error while parsing arg `%s'";
   2993 
   2994   perror_if_not (a, "No such annotation: %s", key);
   2995   pa = a->amap->annot;
   2996   perror_if_not (annotation_edit_validate (ctx, &args[2], pa, &error_msg),
   2997                  "%s", error_msg);
   2998   index = poppler_annot_get_page_index (pa);
   2999   page = poppler_document_get_page (doc->pdf, index);
   3000   perror_if_not (page, "Unable to get page %d for annotation", index);
   3001 
   3002   for (i = 0; i < nrest_args; ++i)
   3003     {
   3004       const char *key = rest_args[i++];
   3005 
   3006       if (! strcmp (key, "flags"))
   3007         {
   3008           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3009                                                  ARG_NATNUM, NULL),
   3010                          unexpected_parse_error, rest_args[i]);
   3011           poppler_annot_set_flags (pa, carg.value.natnum);
   3012         }
   3013       else if (! strcmp (key, "color"))
   3014         {
   3015           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3016                                                  ARG_COLOR, NULL),
   3017                          unexpected_parse_error, rest_args[i]);
   3018           poppler_annot_set_color (pa, &carg.value.color);
   3019         }
   3020       else if (! strcmp (key, "contents"))
   3021         {
   3022           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3023                                                  ARG_STRING, NULL),
   3024                          unexpected_parse_error, rest_args[i]);
   3025           poppler_annot_set_contents (pa, carg.value.string);
   3026         }
   3027       else if (! strcmp (key, "edges"))
   3028         {
   3029           PopplerRectangle *area = &a->amap->area;
   3030           gdouble width, height;
   3031           PopplerRectangle r;
   3032 
   3033           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3034                                                  ARG_EDGES_OR_POSITION, NULL),
   3035                          unexpected_parse_error, rest_args[i]);
   3036           r = carg.value.rectangle;
   3037           poppler_page_get_size (page, &width, &height);
   3038 
   3039           /* Translate Gravity and maybe keep the width and height. */
   3040           if (r.x2 < 0)
   3041             area->x2 +=  (r.x1 * width) - area->x1;
   3042           else
   3043             area->x2 = r.x2 * width;
   3044 
   3045           if (r.y2 < 0)
   3046             area->y1 -=  (r.y1 * height) - (height - area->y2);
   3047           else
   3048             area->y1 = height - (r.y2 * height);
   3049 
   3050           area->x1 = r.x1 * width;
   3051           area->y2 = height - (r.y1 * height);
   3052 
   3053           poppler_annot_set_rectangle (pa, area);
   3054         }
   3055       else if (! strcmp (key, "label"))
   3056         {
   3057           PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa);
   3058           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3059                                                  ARG_STRING, NULL),
   3060                          unexpected_parse_error, rest_args[i]);
   3061           poppler_annot_markup_set_label (ma, carg.value.string);
   3062         }
   3063       else if (! strcmp (key, "opacity"))
   3064         {
   3065           PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa);
   3066           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3067                                                  ARG_EDGE, NULL),
   3068                          unexpected_parse_error, rest_args[i]);
   3069           poppler_annot_markup_set_opacity (ma, carg.value.edge);
   3070         }
   3071       else if (! strcmp (key, "popup"))
   3072         {
   3073           PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa);
   3074           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3075                                                  ARG_EDGES, NULL),
   3076                          unexpected_parse_error, rest_args[i]);
   3077           poppler_annot_markup_set_popup (ma, &carg.value.rectangle);
   3078         }
   3079       else if (! strcmp (key, "popup-is-open"))
   3080         {
   3081           PopplerAnnotMarkup *ma = POPPLER_ANNOT_MARKUP (pa);
   3082           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3083                                                  ARG_BOOL, NULL),
   3084                          unexpected_parse_error, rest_args[i]);
   3085           poppler_annot_markup_set_popup_is_open (ma, carg.value.flag);
   3086         }
   3087       else if (! strcmp (key, "icon"))
   3088         {
   3089           PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa);
   3090           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3091                                                  ARG_STRING, NULL),
   3092                          unexpected_parse_error, rest_args[i]);
   3093           poppler_annot_text_set_icon (ta, carg.value.string);
   3094         }
   3095       else if (! strcmp (key, "is-open"))
   3096         {
   3097           PopplerAnnotText *ta = POPPLER_ANNOT_TEXT (pa);
   3098           perror_if_not (command_arg_parse_arg  (ctx, rest_args[i], &carg,
   3099                                                  ARG_BOOL, NULL),
   3100                          unexpected_parse_error, rest_args[i]);
   3101           poppler_annot_text_set_is_open (ta, carg.value.flag);
   3102         }
   3103       else
   3104         {
   3105           perror_if_not (0, "internal error: annotation property validation failed");
   3106         }
   3107     }
   3108 
   3109   OK_BEGIN ();
   3110   annotation_print (a, page);
   3111   OK_END ();
   3112 
   3113  error:
   3114   if (error_msg) g_free (error_msg);
   3115   if (page) g_object_unref (page);
   3116 }
   3117 
   3118 const command_arg_type_t cmd_save_spec[] =
   3119   {
   3120     ARG_DOC,
   3121   };
   3122 
   3123 static void
   3124 cmd_save (const epdfinfo_t *ctx, const command_arg_t *args)
   3125 {
   3126   document_t *doc = args->value.doc;
   3127   char *filename = mktempfile ();
   3128   GError *gerror = NULL;
   3129   gchar *uri;
   3130   gboolean success = FALSE;
   3131 
   3132   if (!filename)
   3133     {
   3134       printf_error_response ("Unable to create temporary file");
   3135       return;
   3136     }
   3137 
   3138   uri = g_filename_to_uri (filename, NULL, &gerror);
   3139 
   3140   if (uri)
   3141     {
   3142       success = poppler_document_save (doc->pdf, uri, &gerror);
   3143       g_free (uri);
   3144     }
   3145   if (! success)
   3146     {
   3147       printf_error_response ("Error while saving %s:%s"
   3148                     , filename, gerror ? gerror->message : "?");
   3149       if (gerror)
   3150         g_error_free (gerror);
   3151       return;
   3152     }
   3153   OK_BEGIN ();
   3154   print_response_string (filename, NEWLINE);
   3155   OK_END ();
   3156 }
   3157 
   3158 #endif  /* HAVE_POPPLER_ANNOT_WRITE */
   3159 
   3160 
   3161 const command_arg_type_t cmd_synctex_forward_search_spec[] =
   3162   {
   3163     ARG_DOC,
   3164     ARG_NONEMPTY_STRING,        /* source file */
   3165     ARG_NATNUM,                 /* line number */
   3166     ARG_NATNUM                  /* column number */
   3167   };
   3168 
   3169 static void
   3170 cmd_synctex_forward_search (const epdfinfo_t *ctx, const command_arg_t *args)
   3171 {
   3172   document_t *doc = args[0].value.doc;
   3173   const char *source = args[1].value.string;
   3174   int line = args[2].value.natnum;
   3175   int column = args[3].value.natnum;
   3176   synctex_scanner_p scanner = NULL;
   3177   synctex_node_p node;
   3178   float x1, y1, x2, y2;
   3179   PopplerPage *page = NULL;
   3180   double width, height;
   3181   int pn;
   3182 
   3183   scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1);
   3184   perror_if_not (scanner, "Unable to create synctex scanner,\
   3185  did you run latex with `--synctex=1' ?");
   3186 
   3187   perror_if_not (synctex_display_query (scanner, source, line, column, 0)
   3188                 && (node = synctex_scanner_next_result (scanner)),
   3189                 "Destination not found");
   3190 
   3191   pn = synctex_node_page (node);
   3192   page = poppler_document_get_page(doc->pdf, pn - 1);
   3193   perror_if_not (page, "Page not found");
   3194   x1 =  synctex_node_box_visible_h (node);
   3195   y1 =  synctex_node_box_visible_v (node)
   3196         - synctex_node_box_visible_height (node);
   3197   x2 = synctex_node_box_visible_width (node) + x1;
   3198   y2 = synctex_node_box_visible_depth (node)
   3199        + synctex_node_box_visible_height (node) + y1;
   3200   poppler_page_get_size (page, &width, &height);
   3201   x1 /= width;
   3202   y1 /= height;
   3203   x2 /= width;
   3204   y2 /= height;
   3205 
   3206   OK_BEGIN ();
   3207   printf("%d:%f:%f:%f:%f\n", pn, x1, y1, x2, y2);
   3208   OK_END ();
   3209 
   3210  error:
   3211   if (page) g_object_unref (page);
   3212   if (scanner) synctex_scanner_free (scanner);
   3213 }
   3214 
   3215 
   3216 const command_arg_type_t cmd_synctex_backward_search_spec[] =
   3217   {
   3218     ARG_DOC,
   3219     ARG_NATNUM,                 /* page number */
   3220     ARG_EDGE,                   /* x */
   3221     ARG_EDGE                    /* y */
   3222   };
   3223 
   3224 static void
   3225 cmd_synctex_backward_search (const epdfinfo_t *ctx, const command_arg_t *args)
   3226 {
   3227   document_t *doc = args[0].value.doc;
   3228   int pn = args[1].value.natnum;
   3229   double x = args[2].value.edge;
   3230   double y = args[3].value.edge;
   3231   synctex_scanner_p scanner = NULL;
   3232   const char *filename;
   3233   PopplerPage *page = NULL;
   3234   synctex_node_p node;
   3235   double width, height;
   3236   int line, column;
   3237 
   3238   scanner = synctex_scanner_new_with_output_file (doc->filename, NULL, 1);
   3239   perror_if_not (scanner, "Unable to create synctex scanner,\
   3240  did you run latex with `--synctex=1' ?");
   3241 
   3242   page = poppler_document_get_page(doc->pdf, pn - 1);
   3243   perror_if_not (page, "Page not found");
   3244   poppler_page_get_size (page, &width, &height);
   3245   x = x * width;
   3246   y = y * height;
   3247 
   3248   if (! synctex_edit_query (scanner, pn, x, y)
   3249       || ! (node = synctex_scanner_next_result (scanner))
   3250       || ! (filename =
   3251             synctex_scanner_get_name (scanner, synctex_node_tag (node))))
   3252     {
   3253       printf_error_response ("Destination not found");
   3254       goto error;
   3255     }
   3256 
   3257   line = synctex_node_line (node);
   3258   column = synctex_node_column (node);
   3259 
   3260   OK_BEGIN ();
   3261   print_response_string (filename, COLON);
   3262   printf("%d:%d\n", line, column);
   3263   OK_END ();
   3264 
   3265  error:
   3266   if (page) g_object_unref (page);
   3267   if (scanner) synctex_scanner_free (scanner);
   3268 }
   3269 
   3270 
   3271 const command_arg_type_t cmd_renderpage_spec[] =
   3272   {
   3273     ARG_DOC,
   3274     ARG_NATNUM,                 /* page number */
   3275     ARG_NATNUM,                 /* width */
   3276     ARG_REST,                   /* commands */
   3277   };
   3278 
   3279 static void
   3280 cmd_renderpage (const epdfinfo_t *ctx, const command_arg_t *args)
   3281 {
   3282   document_t *doc = args[0].value.doc;
   3283   int pn = args[1].value.natnum;
   3284   int width = args[2].value.natnum;
   3285   int nrest_args = args[3].value.rest.nargs;
   3286   char * const *rest_args = args[3].value.rest.args;
   3287   PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1);
   3288   cairo_surface_t *surface = NULL;
   3289   cairo_t *cr = NULL;
   3290   command_arg_t rest_arg;
   3291   gchar *error_msg = NULL;
   3292   double pt_width, pt_height;
   3293   PopplerColor fg = { 0, 0, 0 };
   3294   PopplerColor bg = { 65535, 0, 0 };
   3295   double alpha = 1.0;
   3296   double line_width = 1.5;
   3297   PopplerSelectionStyle selection_style = POPPLER_SELECTION_GLYPH;
   3298   PopplerRectangle cb = {0.0, 0.0, 1.0, 1.0};
   3299   int i = 0;
   3300 
   3301   perror_if_not (page, "No such page %d", pn);
   3302   poppler_page_get_size (page, &pt_width, &pt_height);
   3303   surface = image_render_page (doc->pdf, page, width, 1,
   3304                                &doc->options.render);
   3305   perror_if_not (surface, "Failed to render page %d", pn);
   3306 
   3307   if (! nrest_args)
   3308     goto theend;
   3309 
   3310   cr = cairo_create (surface);
   3311   cairo_scale (cr, width / pt_width, width / pt_width);
   3312 
   3313   while (i < nrest_args)
   3314     {
   3315       const char* keyword;
   3316 
   3317       perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg,
   3318                                             ARG_STRING, &error_msg),
   3319                      "%s", error_msg);
   3320       keyword = rest_arg.value.string;
   3321       ++i;
   3322 
   3323       perror_if_not (i < nrest_args, "Keyword is `%s' missing an argument",
   3324                      keyword);
   3325 
   3326       if (! strcmp (keyword, ":foreground")
   3327           || ! strcmp (keyword, ":background"))
   3328         {
   3329           perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg,
   3330                                                 ARG_COLOR, &error_msg),
   3331                          "%s", error_msg);
   3332           ++i;
   3333           if (! strcmp (keyword, ":foreground"))
   3334             fg = rest_arg.value.color;
   3335           else
   3336             bg = rest_arg.value.color;
   3337 
   3338         }
   3339       else if (! strcmp (keyword, ":alpha"))
   3340         {
   3341           perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg,
   3342                                                 ARG_EDGE, &error_msg),
   3343                          "%s", error_msg);
   3344           ++i;
   3345           alpha = rest_arg.value.edge;
   3346         }
   3347       else if (! strcmp (keyword, ":crop-to")
   3348                || ! strcmp (keyword, ":highlight-region")
   3349                || ! strcmp (keyword, ":highlight-text")
   3350                || ! strcmp (keyword, ":highlight-line"))
   3351         {
   3352           PopplerRectangle *r;
   3353           perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg,
   3354                                                 ARG_EDGES, &error_msg),
   3355                          "%s", error_msg);
   3356 
   3357           ++i;
   3358           r = &rest_arg.value.rectangle;
   3359 
   3360           if (! strcmp (keyword, ":crop-to"))
   3361             {
   3362               gdouble w = (cb.x2 - cb.x1);
   3363               gdouble h = (cb.y2 - cb.y1);
   3364               gdouble x1 = cb.x1;
   3365               gdouble y1 = cb.y1;
   3366 
   3367               cb.x1 = r->x1 * w + x1;
   3368               cb.x2 = r->x2 * w + x1;
   3369               cb.y1 = r->y1 * h + y1;
   3370               cb.y2 = r->y2 * h + y1;
   3371 
   3372             }
   3373           else
   3374             {
   3375               r->x1 = pt_width * r->x1 * (cb.x2 - cb.x1) + pt_width * cb.x1;
   3376               r->x2 = pt_width * r->x2 * (cb.x2 - cb.x1) + pt_width * cb.x1;
   3377               r->y1 = pt_height * r->y1 * (cb.y2 - cb.y1) + pt_height * cb.y1;
   3378               r->y2 = pt_height * r->y2 * (cb.y2 - cb.y1) + pt_height * cb.y1;
   3379 
   3380               if (! strcmp (keyword, ":highlight-region"))
   3381                 {
   3382                   const double deg = M_PI / 180.0;
   3383                   double rad;
   3384 
   3385                   r->x1 += line_width / 2;
   3386                   r->x2 -= line_width / 2;
   3387                   r->y1 += line_width / 2;
   3388                   r->y2 -= line_width / 2;
   3389 
   3390                   rad = MIN (5, MIN (r->x2 - r->x1, r->y2 - r->y1) / 2.0);
   3391 
   3392                   cairo_move_to (cr, r->x1 , r->y1 + rad);
   3393                   cairo_arc (cr, r->x1 + rad, r->y1 + rad, rad, 180 * deg, 270 * deg);
   3394                   cairo_arc (cr, r->x2 - rad, r->y1 + rad, rad, 270 * deg, 360 * deg);
   3395                   cairo_arc (cr, r->x2 - rad, r->y2 - rad, rad, 0 * deg, 90 * deg);
   3396                   cairo_arc (cr, r->x1 + rad, r->y2 - rad, rad, 90 * deg, 180 * deg);
   3397                   cairo_close_path (cr);
   3398 
   3399                   cairo_set_source_rgba (cr,
   3400                                          bg.red / 65535.0,
   3401                                          bg.green / 65535.0,
   3402                                          bg.blue / 65535.0, alpha);
   3403                   cairo_fill_preserve (cr);
   3404                   cairo_set_source_rgba (cr,
   3405                                          fg.red / 65535.0,
   3406                                          fg.green / 65535.0,
   3407                                          fg.blue / 65535.0, 1.0);
   3408                   cairo_set_line_width (cr, line_width);
   3409                   cairo_stroke (cr);
   3410                 }
   3411               else
   3412                 {
   3413                   gboolean is_single_line = ! strcmp (keyword, ":highlight-line");
   3414 
   3415                   if (is_single_line)
   3416                     {
   3417                       gdouble m = r->y1 + (r->y2 - r->y1) / 2;
   3418 
   3419                       /* Make the rectangle flat, otherwise poppler frequently
   3420                          renders neighboring lines.*/
   3421                       r->y1 = m;
   3422                       r->y2 = m;
   3423                     }
   3424 
   3425                   poppler_page_render_selection (page, cr, r, NULL,
   3426                                                  selection_style, &fg, &bg);
   3427                 }
   3428             }
   3429         }
   3430       else if (! strcmp (keyword, ":selection-style"))
   3431         {
   3432           perror_if_not (command_arg_parse_arg (ctx, rest_args[i], &rest_arg,
   3433                                                 ARG_NATNUM, &error_msg),
   3434                          "%s", error_msg);
   3435           ++i;
   3436 	  selection_style = xpoppler_validate_selection_style (rest_arg.value.natnum);
   3437         }
   3438       else
   3439         perror_if_not (0, "Unknown render command: %s", keyword);
   3440     }
   3441   if (cb.x1 != 0 || cb.y1 != 0 || cb.x2 != 1 || cb.y2 != 1)
   3442     {
   3443       int height = cairo_image_surface_get_height (surface);
   3444       cairo_rectangle_int_t r = {(int) (width * cb.x1 + 0.5),
   3445                                  (int) (height * cb.y1 + 0.5),
   3446                                  (int) (width * (cb.x2 - cb.x1) + 0.5),
   3447                                  (int) (height * (cb.y2 - cb.y1) + 0.5)};
   3448       cairo_surface_t *nsurface =
   3449         cairo_image_surface_create (CAIRO_FORMAT_ARGB32, r.width, r.height);
   3450       perror_if_not (cairo_surface_status (surface) == CAIRO_STATUS_SUCCESS,
   3451                      "%s", "Failed to create cairo surface");
   3452       cairo_destroy (cr);
   3453       cr = cairo_create (nsurface);
   3454       perror_if_not (cairo_status (cr) == CAIRO_STATUS_SUCCESS,
   3455                      "%s", "Failed to create cairo context");
   3456       cairo_set_source_surface (cr, surface, -r.x, -r.y);
   3457       cairo_paint (cr);
   3458       cairo_surface_destroy (surface);
   3459       surface = nsurface;
   3460     }
   3461 
   3462  theend:
   3463   image_write_print_response (surface, PNG);
   3464 
   3465  error:
   3466   if (error_msg) g_free (error_msg);
   3467   if (cr) cairo_destroy (cr);
   3468   if (surface) cairo_surface_destroy (surface);
   3469   if (page) g_object_unref (page);
   3470 }
   3471 
   3472 const command_arg_type_t cmd_boundingbox_spec[] =
   3473   {
   3474     ARG_DOC,
   3475     ARG_NATNUM,                 /* page number */
   3476     /* region */
   3477   };
   3478 
   3479 static void
   3480 cmd_boundingbox (const epdfinfo_t *ctx, const command_arg_t *args)
   3481 {
   3482   document_t *doc = args[0].value.doc;
   3483   int pn = args[1].value.natnum;
   3484   PopplerPage *page = poppler_document_get_page(doc->pdf, pn - 1);
   3485   cairo_surface_t *surface = NULL;
   3486   int width, height;
   3487   double pt_width, pt_height;
   3488   unsigned char *data, *data_p;
   3489   PopplerRectangle bbox;
   3490   int i, j;
   3491 
   3492   perror_if_not (page, "No such page %d", pn);
   3493   poppler_page_get_size (page, &pt_width, &pt_height);
   3494   surface = image_render_page (doc->pdf, page, (int) pt_width, 1,
   3495                                &doc->options.render);
   3496 
   3497   perror_if_not (cairo_surface_status(surface) == CAIRO_STATUS_SUCCESS,
   3498                 "Failed to render page");
   3499 
   3500   width = cairo_image_surface_get_width (surface);
   3501   height = cairo_image_surface_get_height (surface);
   3502   data = cairo_image_surface_get_data (surface);
   3503 
   3504   /* Determine the bbox by comparing each pixel in the 4 corner
   3505      stripes with the origin. */
   3506   for (i = 0; i < width; ++i)
   3507     {
   3508       data_p = data + 4 * i;
   3509       for (j = 0; j < height; ++j, data_p += 4 * width)
   3510         {
   3511           if (! ARGB_EQUAL (data, data_p))
   3512             break;
   3513         }
   3514       if (j < height)
   3515         break;
   3516     }
   3517   bbox.x1 = i;
   3518 
   3519   for (i = width - 1; i > -1; --i)
   3520     {
   3521       data_p = data + 4 * i;
   3522       for (j = 0; j < height; ++j, data_p += 4 * width)
   3523         {
   3524           if (! ARGB_EQUAL (data, data_p))
   3525             break;
   3526         }
   3527       if (j < height)
   3528         break;
   3529     }
   3530   bbox.x2 = i + 1;
   3531 
   3532   for (i = 0; i < height; ++i)
   3533     {
   3534       data_p = data + 4 * i * width;
   3535       for (j = 0; j < width; ++j, data_p += 4)
   3536         {
   3537           if (! ARGB_EQUAL (data, data_p))
   3538             break;
   3539         }
   3540       if (j < width)
   3541         break;
   3542     }
   3543   bbox.y1 = i;
   3544 
   3545   for (i = height - 1; i > -1; --i)
   3546     {
   3547       data_p = data + 4 * i * width;
   3548       for (j = 0; j < width; ++j, data_p += 4)
   3549         {
   3550           if (! ARGB_EQUAL (data, data_p))
   3551             break;
   3552         }
   3553       if (j < width)
   3554         break;
   3555     }
   3556   bbox.y2 = i + 1;
   3557 
   3558   OK_BEGIN ();
   3559   if (bbox.x1 >= bbox.x2 || bbox.y1 >= bbox.y2)
   3560     {
   3561       /* empty page */
   3562       puts ("0:0:1:1");
   3563     }
   3564   else
   3565     {
   3566       printf ("%f:%f:%f:%f\n",
   3567               bbox.x1 / width,
   3568               bbox.y1 / height,
   3569               bbox.x2 / width,
   3570               bbox.y2 / height);
   3571     }
   3572   OK_END ();
   3573 
   3574  error:
   3575   if (surface) cairo_surface_destroy (surface);
   3576   if (page) g_object_unref (page);
   3577 }
   3578 
   3579 const command_arg_type_t cmd_charlayout_spec[] =
   3580   {
   3581     ARG_DOC,
   3582     ARG_NATNUM,                 /* page number */
   3583     ARG_EDGES_OR_POSITION,      /* region or position */
   3584   };
   3585 
   3586 static void
   3587 cmd_charlayout(const epdfinfo_t *ctx, const command_arg_t *args)
   3588 {
   3589   PopplerDocument *doc = args[0].value.doc->pdf;
   3590   int pn = args[1].value.natnum;
   3591   PopplerRectangle region = args[2].value.rectangle;
   3592   double width, height;
   3593   PopplerPage *page = poppler_document_get_page(doc, pn - 1);
   3594   char *text = NULL;
   3595   char *text_p;
   3596   PopplerRectangle *rectangles = NULL;
   3597   guint nrectangles;
   3598   int i;
   3599   gboolean have_position = region.y2 < 0;
   3600 
   3601   perror_if_not (page, "No such page %d", pn);
   3602 
   3603   text = poppler_page_get_text (page);
   3604   text_p = text;
   3605   poppler_page_get_text_layout (page, &rectangles, &nrectangles);
   3606   poppler_page_get_size (page, &width, &height);
   3607   region.x1 *= width;
   3608   region.x2 *= width;
   3609   region.y1 *= height;
   3610   region.y2 *= height;
   3611 
   3612   OK_BEGIN ();
   3613   for (i = 0; i < nrectangles && *text_p; ++i)
   3614     {
   3615       PopplerRectangle *r = &rectangles[i];
   3616       char *nextc = g_utf8_offset_to_pointer (text_p, 1);
   3617 
   3618       if ((have_position
   3619            && region.x1 >= r->x1
   3620            && region.x1 <= r->x2
   3621            && region.y1 >= r->y1
   3622            && region.y1 <= r->y2)
   3623           || (! have_position
   3624               && r->x1 >= region.x1
   3625               && r->y1 >= region.y1
   3626               && r->x2 <= region.x2
   3627               && r->y2 <= region.y2))
   3628         {
   3629           char endc = *nextc;
   3630 
   3631           printf ("%f %f %f %f:",
   3632                   r->x1 / width, r->y1 / height,
   3633                   r->x2 / width, r->y2 / height);
   3634           *nextc = '\0';
   3635           print_response_string (text_p, NEWLINE);
   3636           *nextc = endc;
   3637         }
   3638       text_p = nextc;
   3639     }
   3640   OK_END ();
   3641 
   3642   g_free (rectangles);
   3643   g_object_unref (page);
   3644   g_free (text);
   3645 
   3646  error:
   3647   return;
   3648 }
   3649 
   3650 const document_option_t document_options [] =
   3651   {
   3652     DEC_DOPT (":render/usecolors", ARG_NATNUM, render.usecolors),
   3653     DEC_DOPT (":render/printed", ARG_BOOL, render.printed),
   3654     DEC_DOPT (":render/foreground", ARG_COLOR, render.fg),
   3655     DEC_DOPT (":render/background", ARG_COLOR, render.bg),
   3656   };
   3657 
   3658 const command_arg_type_t cmd_getoptions_spec[] =
   3659   {
   3660     ARG_DOC,
   3661   };
   3662 
   3663 static void
   3664 cmd_getoptions(const epdfinfo_t *ctx, const command_arg_t *args)
   3665 {
   3666   document_t *doc = args[0].value.doc;
   3667   int i;
   3668   OK_BEGIN ();
   3669   for (i = 0; i < G_N_ELEMENTS (document_options); ++i)
   3670     {
   3671       command_arg_t arg;
   3672 
   3673       arg.type = document_options[i].type;
   3674       memcpy (&arg.value,
   3675               ((char*) &doc->options) + document_options[i].offset,
   3676               command_arg_type_size (arg.type));
   3677       print_response_string (document_options[i].name, COLON);
   3678       command_arg_print (&arg);
   3679       puts("");
   3680     }
   3681   OK_END ();
   3682 }
   3683 
   3684 const command_arg_type_t cmd_setoptions_spec[] =
   3685   {
   3686     ARG_DOC,
   3687     ARG_REST                    /* key value pairs */
   3688   };
   3689 
   3690 static void
   3691 cmd_setoptions(const epdfinfo_t *ctx, const command_arg_t *args)
   3692 {
   3693   int i = 0;
   3694   document_t *doc = args[0].value.doc;
   3695   int nrest = args[1].value.rest.nargs;
   3696   char * const *rest = args[1].value.rest.args;
   3697   gchar *error_msg = NULL;
   3698   document_options_t opts = doc->options;
   3699   const size_t nopts = G_N_ELEMENTS (document_options);
   3700 
   3701   perror_if_not (nrest % 2 == 0, "Even number of key/value pairs expected");
   3702 
   3703   while (i < nrest)
   3704     {
   3705       int j;
   3706       command_arg_t key, value;
   3707 
   3708       perror_if_not (command_arg_parse_arg
   3709                      (ctx, rest[i], &key, ARG_NONEMPTY_STRING, &error_msg),
   3710                      "%s", error_msg);
   3711 
   3712       ++i;
   3713       for (j = 0; j < nopts; ++j)
   3714         {
   3715           const document_option_t *dopt = &document_options[j];
   3716           if (! strcmp (key.value.string, dopt->name))
   3717             {
   3718               perror_if_not (command_arg_parse_arg
   3719                              (ctx, rest[i], &value, dopt->type, &error_msg),
   3720                              "%s", error_msg);
   3721               memcpy (((char*) &opts) + dopt->offset,
   3722                       &value.value, command_arg_type_size (value.type));
   3723               break;
   3724             }
   3725         }
   3726       perror_if_not (j < nopts, "Unknown option: %s", key.value.string);
   3727       ++i;
   3728     }
   3729   doc->options = opts;
   3730   cmd_getoptions (ctx, args);
   3731 
   3732  error:
   3733   if (error_msg) g_free (error_msg);
   3734 }
   3735 
   3736 const command_arg_type_t cmd_pagelabels_spec[] =
   3737   {
   3738     ARG_DOC,
   3739   };
   3740 
   3741 static void
   3742 cmd_pagelabels(const epdfinfo_t *ctx, const command_arg_t *args)
   3743 {
   3744   PopplerDocument *doc = args[0].value.doc->pdf;
   3745   int i;
   3746 
   3747   OK_BEGIN ();
   3748   for (i = 0; i < poppler_document_get_n_pages (doc); ++i)
   3749     {
   3750       PopplerPage *page = poppler_document_get_page(doc, i);
   3751       gchar *label = poppler_page_get_label (page);
   3752 
   3753       print_response_string (label ? label : "", NEWLINE);
   3754       g_object_unref (page);
   3755       g_free (label);
   3756     }
   3757   OK_END ();
   3758 }
   3759 
   3760 const command_arg_type_t cmd_ping_spec[] =
   3761   {
   3762     ARG_STRING                  /* any message */
   3763   };
   3764 
   3765 static void
   3766 cmd_ping (const epdfinfo_t *ctx, const command_arg_t *args)
   3767 {
   3768   const gchar *msg = args[0].value.string;
   3769   OK_BEGIN ();
   3770   print_response_string (msg, NEWLINE);
   3771   OK_END ();
   3772 }
   3773 
   3774 
   3775 /* ================================================================== *
   3776  * Main
   3777  * ================================================================== */
   3778 
   3779 static const command_t commands [] =
   3780   {
   3781     /* Basic */
   3782     DEC_CMD (ping),
   3783     DEC_CMD (features),
   3784     DEC_CMD (open),
   3785     DEC_CMD (close),
   3786     DEC_CMD (quit),
   3787     DEC_CMD (getoptions),
   3788     DEC_CMD (setoptions),
   3789 
   3790     /* Searching */
   3791     DEC_CMD2 (search_string, "search-string"),
   3792     DEC_CMD2 (search_regexp, "search-regexp"),
   3793     DEC_CMD2 (regexp_flags, "regexp-flags"),
   3794 
   3795     /* General Information */
   3796     DEC_CMD (metadata),
   3797     DEC_CMD (outline),
   3798     DEC_CMD2 (number_of_pages, "number-of-pages"),
   3799     DEC_CMD (pagelinks),
   3800     DEC_CMD (gettext),
   3801     DEC_CMD (getselection),
   3802     DEC_CMD (pagesize),
   3803     DEC_CMD (boundingbox),
   3804     DEC_CMD (charlayout),
   3805 
   3806     /* General Information */
   3807     DEC_CMD (metadata),
   3808     DEC_CMD (outline),
   3809     DEC_CMD2 (number_of_pages, "number-of-pages"),
   3810     DEC_CMD (pagelinks),
   3811     DEC_CMD (gettext),
   3812     DEC_CMD (getselection),
   3813     DEC_CMD (pagesize),
   3814     DEC_CMD (boundingbox),
   3815     DEC_CMD (charlayout),
   3816     DEC_CMD (pagelabels),
   3817 
   3818     /* Annotations */
   3819     DEC_CMD (getannots),
   3820     DEC_CMD (getannot),
   3821 #ifdef HAVE_POPPLER_ANNOT_WRITE
   3822     DEC_CMD (addannot),
   3823     DEC_CMD (delannot),
   3824     DEC_CMD (editannot),
   3825     DEC_CMD (save),
   3826 #endif
   3827 
   3828     /* Attachments */
   3829     DEC_CMD2 (getattachment_from_annot, "getattachment-from-annot"),
   3830     DEC_CMD (getattachments),
   3831 
   3832     /* Synctex */
   3833     DEC_CMD2 (synctex_forward_search, "synctex-forward-search"),
   3834     DEC_CMD2 (synctex_backward_search, "synctex-backward-search"),
   3835 
   3836     /* Rendering */
   3837     DEC_CMD (renderpage),
   3838   };
   3839 
   3840 int main(int argc, char **argv)
   3841 {
   3842   epdfinfo_t ctx = {0};
   3843   char *line = NULL;
   3844   ssize_t read;
   3845   size_t line_size;
   3846   const char *error_log = "/dev/null";
   3847 
   3848 #ifdef __MINGW32__
   3849   error_log = "NUL";
   3850   _setmode(_fileno(stdin), _O_BINARY);
   3851   _setmode(_fileno(stdout), _O_BINARY);
   3852 #endif
   3853 
   3854   if (argc > 2)
   3855     {
   3856       fprintf(stderr, "usage: epdfinfo [ERROR-LOGFILE]\n");
   3857       exit (EXIT_FAILURE);
   3858     }
   3859   if (argc == 2)
   3860     error_log = argv[1];
   3861 
   3862   if (! freopen (error_log, "a", stderr))
   3863     err (2, "Unable to redirect stderr");
   3864 
   3865 #if ! GLIB_CHECK_VERSION(2,36,0)
   3866   g_type_init ();
   3867 #endif
   3868 
   3869   ctx.documents = g_hash_table_new (g_str_hash, g_str_equal);
   3870 
   3871   setvbuf (stdout, NULL, _IOFBF, BUFSIZ);
   3872 
   3873   while ((read = getline (&line, &line_size, stdin)) != -1)
   3874     {
   3875       int nargs = 0;
   3876       command_arg_t *cmd_args = NULL;
   3877       char **args = NULL;
   3878       gchar *error_msg = NULL;
   3879       int i;
   3880 
   3881       if (read <= 1 || line[read - 1] != '\n')
   3882         {
   3883           fprintf (stderr, "Skipped parts of a line: `%s'\n", line);
   3884           goto next_line;
   3885         }
   3886 
   3887       line[read - 1] = '\0';
   3888       args = command_arg_split (line, &nargs);
   3889       if (nargs == 0)
   3890         continue;
   3891 
   3892       for (i = 0; i < G_N_ELEMENTS (commands);  i++)
   3893         {
   3894           if (! strcmp (commands[i].name, args[0]))
   3895             {
   3896               if (commands[i].nargs == 0
   3897                   || (cmd_args = command_arg_parse (&ctx, args + 1, nargs - 1,
   3898                                                     commands + i, &error_msg)))
   3899                 {
   3900                   commands[i].execute (&ctx, cmd_args);
   3901                   if (commands[i].nargs > 0)
   3902                     free_command_args (cmd_args, commands[i].nargs);
   3903                 }
   3904               else
   3905                 {
   3906                   printf_error_response ("%s", error_msg ? error_msg :
   3907                                          "Unknown error (this is a bug)");
   3908                 }
   3909               if (error_msg)
   3910                 g_free (error_msg);
   3911               break;
   3912             }
   3913         }
   3914       if (G_N_ELEMENTS (commands) == i)
   3915         {
   3916           printf_error_response ("Unknown command: %s", args[0]);
   3917         }
   3918       for (i = 0; i < nargs; ++i)
   3919         g_free (args[i]);
   3920       g_free (args);
   3921     next_line:
   3922       free (line);
   3923       line = NULL;
   3924     }
   3925 
   3926   if (ferror (stdin))
   3927     err (2, NULL);
   3928   exit (EXIT_SUCCESS);
   3929 }