# Examples

## Example 1

    #include <stdio.h>
    #include "ppapi.h"
    #include "util/utiliof.h"

    static const char * sizenum (size_t s)
    {
      static char buffer[32];
      if (s < 1000)
        sprintf(buffer, "%uB", (unsigned)s);
      else if (s < 1000000)
        sprintf(buffer, "%.2fkB", (double)(s) / 1000);
      else
        sprintf(buffer, "%.2fMB", (double)(s) / 1000000);
      return buffer;
    }

    static const char * crypt_info (ppdoc *pdf)
    {
      switch (ppdoc_crypt_status(pdf))
      {
        case PPCRYPT_NONE:
          return "none";
        case PPCRYPT_DONE:
          return "empty password";
        case PPCRYPT_PASS:
          return "nonempty password";
        default:
          break;
      }
      return "this shouldn't happen";
    }

    static void print_info (ppdoc *pdf)
    {
      ppdict *info;
      ppstring *creator, *producer;
      size_t memused, memwaste;

      if ((info = ppdoc_info(pdf)) != NULL)
      {
        if ((creator = ppdict_rget_string(info, "Creator")) != NULL)
          printf("  creator: %s\n", ppstring_decoded_data(creator));
        if ((producer = ppdict_rget_string(info, "Producer")) != NULL)
          printf("  producer: %s\n", ppstring_decoded_data(producer));
      }
      printf("  version: %s\n", ppdoc_version_string(pdf));
      printf("  protection: %s\n", crypt_info(pdf));
      printf("  filesize: %s\n", sizenum(ppdoc_file_size(pdf)));
      printf("  objects: %lu\n", (unsigned long)ppdoc_objects(pdf));
      printf("  pagecount: %lu\n", (unsigned long)ppdoc_page_count(pdf));
      memused = ppdoc_memory(pdf, &memwaste);
      printf("  memused: %s\n", sizenum(memused));
      printf("  memwaste: %s\n", sizenum(memwaste));
    }

    static int usage (const char *argv0)
    {
      printf("pplib " pplib_version ", " pplib_author "\n");
      printf("usage: %s file1.pdf file2.pdf ...\n", argv0);
      return 0;
    }

    int main (int argc, const char **argv)
    {
      const char *filepath;
      int a;
      ppdoc *pdf;
      const void *data;
      size_t size;

      if (argc < 2)
        return usage(argv[0]);
      for (a = 1; a < argc; ++a)
      {
        filepath = argv[a];
        printf("loading %s... ", filepath);
        pdf = ppdoc_load(filepath);
        if (pdf == NULL)
        {
          printf("failed\n");
          continue;
        }
        printf("done.\n");
        print_info(pdf);
        ppdoc_free(pdf);
        /* now loading from memory buffer */
        printf("loading %s from mem buffer... ", filepath);
        data = iof_copy_file_data(filepath, &size);
        if (data != NULL)
        {
          pdf = ppdoc_mem(data, size);
          if (pdf == NULL)
          {
            printf("failed\n");
            continue;
          }
          printf("done.\n");
          //print_info(pdf);
          ppdoc_free(pdf);
        }
      }
      return 0;
    }

## Example 2

    #include <stdio.h>
    #include <assert.h>
    #include "ppapi.h"

    /*
    static const char * get_file_name (const char *path)
    {
      const char *fn, *p;
      for (fn = p = path; *p != '\0'; ++p)
        if (*p == '\\' || *p == '/')
          fn = p + 1;
      return fn;
    }
    */

    static void box_info (ppdict *pagedict, FILE *fh)
    {
      const char *boxes[] = {"MediaBox", "CropBox", "BleedBox", "TrimBox", "ArtBox"};
      pprect rect;
      size_t i;
      for (i = 0; i < sizeof(boxes) / sizeof(const char *); ++i)
        if (ppdict_get_box(pagedict, boxes[i], &rect))
          fprintf(fh, "%%%% %s [%f %f %f %f]\n", boxes[i], rect.lx, rect.ly, rect.rx, rect.ry);
    }

    static int usage (const char *argv0)
    {
      printf("pplib " pplib_version ", " pplib_author "\n");
      printf("usage: %s file1.pdf file2.pdf ...\n", argv0);
      return 0;
    }

    static void log_callback (const char *message, void *alien)
    {
      fprintf((FILE *)alien, "\nooops: %s\n", message);
    }

    int main (int argc, const char **argv)
    {
      const char *filepath;
      int a;
      ppdoc *pdf;
      ppref *pageref;
      ppdict *pagedict;
      int pageno;
      char outname[1024];
      FILE *fh;
      ppstream *stream;
      uint8_t *data;
      size_t size;
      ppcontext *context;
      ppobj *obj;
      ppname *op;
      size_t operators;

      if (argc < 2)
        return usage(argv[0]);
      ppstream_init_buffers();
      pplog_callback(log_callback, stderr);
      context = ppcontext_new();
      for (a = 1; a < argc; ++a)
      {
        filepath = argv[a];
        printf("loading %s... ", filepath);
        pdf = ppdoc_load(filepath);
        if (pdf == NULL)
        {
          printf("failed\n");
          continue;
        }
        printf("done.\n");
        switch (ppdoc_crypt_status(pdf))
        {
          case PPCRYPT_NONE:
          case PPCRYPT_DONE:
            break;
          case PPCRYPT_PASS:
            if (ppdoc_crypt_pass(pdf, "dummy", 5, NULL, 0) == PPCRYPT_DONE || ppdoc_crypt_pass(pdf, NULL, 0, "dummy", 5) == PPCRYPT_DONE)
              break;
            printf("sorry, password needed\n");
            ppdoc_free(pdf);
            continue;
          case PPCRYPT_FAIL:
            printf("sorry, encryption failed\n");
            ppdoc_free(pdf);
            continue;
        }
        sprintf(outname, "%s.out", filepath);
        fh = fopen(outname, "wb");
        if (fh == NULL)
        {
          printf("can't open %s for writing\n", outname);
          continue;
        }
        for (pageref = ppdoc_first_page(pdf), pageno = 1;
             pageref != NULL;
             pageref = ppdoc_next_page(pdf), ++pageno)
        {
          pagedict = pageref->object.dict;
          /* decompress contents data */
          fprintf(fh, "%%%% PAGE %d\n", pageno);
          box_info(pagedict, fh);
          for (stream = ppcontents_first(pagedict);
               stream != NULL;
               stream = ppcontents_next(pagedict, stream))
          {
            for (data = ppstream_first(stream, &size, 1);
                 data != NULL;
                 data = ppstream_next(stream, &size))
              fwrite(data, size, 1, fh);
            ppstream_done(stream);
          }
          /* now parse contents */
          for (stream = ppcontents_first(pagedict);
               stream != NULL;
               stream = ppcontents_next(pagedict, stream))
          {
            operators = 0;
            for (obj = ppcontents_first_op(context, stream, &size, &op);
                 obj != NULL;
                 obj = ppcontents_next_op(context, stream, &size, &op))
              ++operators;
            fprintf(fh, "%%%% OPERATORS count %lu\n", (unsigned long)operators);
            ppstream_done(stream);
            //obj = ppcontents_parse(context, stream, &size);
            //fprintf(fh, "%%%% items count %lu\n", (unsigned long)size);
            fprintf(fh, "\n");
          }
          ppcontext_done(context);
        }
        fclose(fh);
        ppdoc_free(pdf);
      }
      ppcontext_free(context);
      ppstream_free_buffers();
      return 0;
    }

## Example 3

    #include <stdio.h>
    //#include "ppapi.h"
    #include "pplib.h"
    #include "assert.h"

    static int usage (const char *argv0)
    {
      printf("pplib " pplib_version ", " pplib_author "\n");
      printf("usage: %s file1.pdf file2.pdf ...\n", argv0);
      return 0;
    }

    static void print_result_filter (ppstream *stream, int decode)
    {
      ppstream_filter info;
      size_t i;

      ppstream_filter_info(stream, &info, decode);
      printf("  when %s: /Filter [", decode ? "uncompressed" : "compressed");
      for (i = 0; i < info.count; ++i)
        printf(" /%s", ppstream_filter_name[info.filters[i]]);
      printf(" ]");
      if (info.params != NULL)
      {
        printf(" /DecodeParms [");
        for (i = 0; i < info.count; ++i)
          printf(" %s", info.params[i] != NULL ? "<<...>>" : "null");
        printf(" ]");
      }
      printf("\n");
    }

    static void print_stream_info (ppref *ref, ppstream *stream)
    {
      size_t length;
      printf("object %lu %lu R\n", (unsigned long)ref->number, (unsigned long)ref->version);
      if (stream->flags & PPSTREAM_FILTER)
        printf("  filtered ");
      else
        printf("  plain ");
      if (stream->flags & PPSTREAM_IMAGE)
        printf("image ");
      else
        printf("stream ");
      if (stream->flags & PPSTREAM_ENCRYPTED)
        printf("encrypted ");
      if (stream->flags & PPSTREAM_NOT_SUPPORTED)
        printf("invalid ");
      if (!ppdict_rget_uint(stream->dict, "Length", &length))
        length = 0;
      assert(stream->length == length);
      printf("length %lu (/Length %lu)\n", (unsigned long)stream->length, (unsigned long)length);
      print_result_filter(stream, 0);
      print_result_filter(stream, 1);
    }

    static void check_stream_chunks (ppstream *stream)
    {
      size_t sum, size;
      uint8_t *data;
      const int decode[2] = {0, 1};
      int d;

      for (d = 0; d < 2; ++d)
      {
        for (sum = 0, data = ppstream_first(stream, &size, decode[d]); data != NULL; data = ppstream_next(stream, &size))
          sum += size;
        ppstream_done(stream);
        ppstream_all(stream, &size, decode[d]);
        ppstream_done(stream);
        assert(sum == size);
        printf("  %s chunks size [%lu]\n", (decode[d] ? "decoded" : "raw"), (unsigned long)size);
      }
    }

    #define USE_BUFFERS_POOL 1

    int main (int argc, const char **argv)
    {
      const char *filepath;
      int a;
      ppdoc *pdf;
      ppxref *xref;
      ppxsec *xsec;
      size_t xi;
      ppuint refnum;
      ppref *ref;

      if (argc < 2)
        return usage(argv[0]);
      if (USE_BUFFERS_POOL)
        ppstream_init_buffers();
      for (a = 1; a < argc; ++a)
      {
        filepath = argv[a];
        printf("loading %s... ", filepath);
        pdf = ppdoc_load(filepath);
        if (pdf == NULL)
        {
          printf("failed\n");
          continue;
        }
        printf("done.\n");
        for (xref = ppdoc_xref(pdf); xref != NULL; xref = ppxref_prev(xref))
        {
          for (xi = 0, xsec = xref->sects; xi < xref->size; ++xi, ++xsec)
          {
            for (refnum = xsec->first, ref = xsec->refs; refnum <= xsec->last; ++refnum, ++ref)
            {
              if (ref->object.type != PPSTREAM)
                continue;
              print_stream_info(ref, ref->object.stream);
              check_stream_chunks(ref->object.stream);
            }
          }
        }
        ppdoc_free(pdf);
      }
      if (USE_BUFFERS_POOL)
        ppstream_free_buffers();
      return 0;
    }

## ppapi.h

    #ifndef PP_API_H
    #define PP_API_H

    #include <stdint.h>
    #include <stddef.h>
    #include <string.h>

    #include "ppconf.h"

    #define pplib_version "v2.05 less toxic i hope"
    #define pplib_author "p.jackowski@gust.org.pl"

    /* types */

    typedef int64_t ppint;
    typedef size_t ppuint; // machine word

    typedef char ppbyte;

    typedef double ppnum;

    typedef struct ppname ppname;
    typedef struct ppstring ppstring;

    struct ppname {
      ppbyte *data;
      size_t size;
      ppname *alterego;
      int flags;
    };

    struct ppstring {
      ppbyte *data;
      size_t size;
      ppstring *alterego;
      int flags;
    };

    typedef struct ppobj ppobj;
    typedef struct ppref ppref;

    typedef struct {
      ppobj *data;
      size_t size;
    } pparray;

    typedef struct {
      ppobj *data;
      ppname **keys;
      size_t size;
    } ppdict;

    typedef enum {
      PPSTREAM_BASE16 = 0,
      PPSTREAM_BASE85,
      PPSTREAM_RUNLENGTH,
      PPSTREAM_FLATE,
      PPSTREAM_LZW,
      PPSTREAM_CCITT,
      PPSTREAM_DCT,
      PPSTREAM_JBIG2,
      PPSTREAM_JPX,
      PPSTREAM_CRYPT
    } ppstreamtp;

    typedef struct {
      ppstreamtp *filters;
      ppdict **params;
      size_t count;
    } ppstream_filter;

    typedef struct {
      ppdict *dict;
      void *input, *I;
      size_t offset;
      size_t length;
      ppstream_filter filter;
      ppobj *filespec;
      ppstring *cryptkey;
      int flags;
    } ppstream;

    PPDEF extern const char * ppstream_filter_name[];
    PPAPI int ppstream_filter_type (ppname *filtername, ppstreamtp *filtertype);
    PPAPI void ppstream_filter_info (ppstream *stream, ppstream_filter *info, int decode);

    #define PPSTREAM_FILTER (1<<0)
    #define PPSTREAM_IMAGE (1<<1)
    #define PPSTREAM_ENCRYPTED_AES (1<<2)
    #define PPSTREAM_ENCRYPTED_RC4 (1<<3)
    #define PPSTREAM_ENCRYPTED (PPSTREAM_ENCRYPTED_AES|PPSTREAM_ENCRYPTED_RC4)
    #define PPSTREAM_ENCRYPTED_OWN (1<<4)
    #define PPSTREAM_NOT_SUPPORTED (1<<6)

    #define ppstream_compressed(stream) ((stream)->flags & (PPSTREAM_FILTER|PPSTREAM_IMAGE))
    #define ppstream_filtered(stream) ((stream)->flags & PPSTREAM_FILTER)
    #define ppstream_image(stream) ((stream)->flags & PPSTREAM_IMAGE)
    #define ppstream_encrypted(stream) ((stream)->flags & PPSTREAM_ENCRYPTED)

    typedef enum {
      PPNONE = 0,
      PPNULL,
      PPBOOL,
      PPINT,
      PPNUM,
      PPNAME,
      PPSTRING,
      PPARRAY,
      PPDICT,
      PPSTREAM,
      PPREF
    } ppobjtp;

    PPDEF extern const char * ppobj_kind[];

    struct ppobj {
      union {
        ppint integer;
        ppnum number;
        ppname *name;
        ppstring *string;
        pparray *array;
        ppdict *dict;
        ppstream *stream;
        ppref *ref;
        void *any;
      };
      ppobjtp type;
    };

    typedef struct ppxref ppxref;

    struct ppref {
      ppobj object;
      ppuint number, version;
      size_t offset;
      size_t length;
      ppxref *xref;
    };

    typedef struct ppdoc ppdoc;

    /* object */

    #define ppobj_get_null(o) ((o)->type == PPNULL ? 1 : 0)
    #define ppobj_get_bool(o, v) ((o)->type == PPBOOL ? ((v = ((o)->integer != 0)), 1) : 0)
    #define ppobj_get_int(o, v) ((o)->type == PPINT ? ((v = (o)->integer), 1) : 0)
    #define ppobj_get_uint(o, v) ((o)->type == PPINT && (o)->integer >= 0 ? ((v = (ppuint)((o)->integer)), 1) : 0)
    #define ppobj_get_num(o, v) ((o)->type == PPNUM ? ((v = (o)->number), 1) : (((o)->type == PPINT ? ((v = (ppnum)((o)->integer)), 1) : 0)))
    #define ppobj_get_name(o) ((o)->type == PPNAME ? (o)->name : NULL)
    #define ppobj_get_string(o) ((o)->type == PPSTRING ? (o)->string : NULL)
    #define ppobj_get_array(o) ((o)->type == PPARRAY ? (o)->array : NULL)
    #define ppobj_get_dict(o) ((o)->type == PPDICT ? (o)->dict : NULL)
    #define ppobj_get_stream(o) ((o)->type == PPSTREAM ? (o)->stream : NULL)
    #define ppobj_get_ref(o) ((o)->type == PPREF ? (o)->ref : NULL)

    #define ppobj_rget_obj(o) ((o)->type == PPREF ? ppref_obj((o)->ref) : o)
    #define ppobj_rget_null(o) ((o)->type == PPNULL ? 1 : ((o)->type == PPREF ? ppobj_get_null(ppref_obj((o)->ref)) : 0))
    #define ppobj_rget_bool(o, v) ((o)->type == PPBOOL ? ((v = ((o)->integer != 0)), 1) : ((o)->type == PPREF ? ppobj_get_bool(ppref_obj((o)->ref), v) : 0))
    #define ppobj_rget_int(o, v) ((o)->type == PPINT ? ((v = (o)->integer), 1) : ((o)->type == PPREF ? ppobj_get_int(ppref_obj((o)->ref), v) : 0))
    #define ppobj_rget_uint(o, v) ((o)->type == PPINT && (o)->integer >= 0 ? ((v = (ppuint)((o)->integer)), 1) : ((o)->type == PPREF ? ppobj_get_uint(ppref_obj((o)->ref), v) : 0))
    #define ppobj_rget_num(o, v) ((o)->type == PPNUM ? ((v = (o)->number), 1) : (((o)->type == PPINT ? ((v = (ppnum)((o)->integer)), 1) : ((o)->type == PPREF ? ppobj_get_num(ppref_obj((o)->ref), v) : 0))))
    #define ppobj_rget_name(o) ((o)->type == PPNAME ? (o)->name : ((o)->type == PPREF ? ppobj_get_name(ppref_obj((o)->ref)) : NULL))
    #define ppobj_rget_string(o) ((o)->type == PPSTRING ? (o)->string : ((o)->type == PPREF ? ppobj_get_string(ppref_obj((o)->ref)) : NULL))
    #define ppobj_rget_array(o) ((o)->type == PPARRAY ? (o)->array : ((o)->type == PPREF ? ppobj_get_array(ppref_obj((o)->ref)) : NULL))
    #define ppobj_rget_dict(o) ((o)->type == PPDICT ? (o)->dict : ((o)->type == PPREF ? ppobj_get_dict(ppref_obj((o)->ref)) : NULL))
    #define ppobj_rget_stream(o) ((o)->type == PPSTREAM ? (o)->stream : ((o)->type == PPREF ? ppobj_get_stream(ppref_obj((o)->ref)) : NULL))
    #define ppobj_rget_ref(o) ((o)->type == PPREF ? (o)->ref : ((o)->type == PPREF ? ppobj_get_ref(ppref_obj((o)->ref)) : NULL))

    #define ppobj_get_bool_value(o) ((o)->type == PPBOOL ? ((o)->integer != 0) : 0)
    #define ppobj_get_int_value(o) ((o)->type == PPINT ? (o)->integer : 0)
    #define ppobj_get_num_value(o) ((o)->type == PPNUM  ? (o)->number : ((o)->type == PPINT  ? (ppnum)((o)->integer) : 0.0))

    /* name */

    #define ppname_is(name, s) (memcmp((name)->data, s, sizeof("" s) - 1) == 0)
    #define ppname_eq(name, n) (memcmp((name)->data, s, (name)->size) == 0)

    #define ppname_size(name) ((name)->size)
    #define ppname_exec(name) ((name)->flags & PPNAME_EXEC)
    #define ppname_data(name) ((name)->data)

    #define PPNAME_ENCODED (1 << 0)
    #define PPNAME_DECODED (1 << 1)
    #define PPNAME_EXEC (1 << 1)

    PPAPI ppname * ppname_decoded (ppname *name);
    PPAPI ppname * ppname_encoded (ppname *name);

    PPAPI ppbyte * ppname_decoded_data (ppname *name);
    PPAPI ppbyte * ppname_encoded_data (ppname *name);

    /* string */

    #define ppstring_size(string) ((string)->size)
    #define ppstring_data(string) ((string)->data)

    #define PPSTRING_ENCODED (1 << 0)
    #define PPSTRING_DECODED (1 << 1)
    //#define PPSTRING_EXEC (1 << 2) // postscript only
    #define PPSTRING_PLAIN 0
    #define PPSTRING_BASE16 (1 << 3)
    #define PPSTRING_BASE85 (1 << 4)
    #define PPSTRING_UTF16BE (1 << 5)
    #define PPSTRING_UTF16LE (1 << 6)

    #define ppstring_type(string) ((string)->flags & (PPSTRING_BASE16|PPSTRING_BASE85))
    #define ppstring_hex(string) ((string)->flags & PPSTRING_BASE16)
    #define ppstring_utf(string) ((string)->flags & (PPSTRING_UTF16BE|PPSTRING_UTF16LE))

    PPAPI ppstring * ppstring_decoded (ppstring *string);
    PPAPI ppstring * ppstring_encoded (ppstring *string);

    PPAPI ppbyte * ppstring_decoded_data (ppstring *string);
    PPAPI ppbyte * ppstring_encoded_data (ppstring *string);

    /* array */

    #define pparray_size(array) ((array)->size)
    #define pparray_at(array, index) ((array)->data + index)

    #define pparray_first(array, index, obj) ((index) = 0, (obj) = pparray_at(array,  0))
    #define pparray_next(index, obj) (++(index), ++(obj))

    #define pparray_get(array, index) (index < (array)->size ? pparray_at(array, index) : NULL)

    PPAPI ppobj * pparray_get_obj (pparray *array, size_t index);
    PPAPI int pparray_get_bool (pparray *array, size_t index, int *v);
    PPAPI int pparray_get_int (pparray *array, size_t index, ppint *v);
    PPAPI int pparray_get_uint (pparray *array, size_t index, ppuint *v);
    PPAPI int pparray_get_num (pparray *array, size_t index, ppnum *v);
    PPAPI ppname * pparray_get_name (pparray *array, size_t index);
    PPAPI ppstring * pparray_get_string (pparray *array, size_t index);
    PPAPI pparray * pparray_get_array (pparray *array, size_t index);
    PPAPI ppdict * pparray_get_dict (pparray *array, size_t index);
    //PPAPI ppstream * pparray_get_stream (pparray *array, size_t index);
    PPAPI ppref * pparray_get_ref (pparray *array, size_t index);

    PPAPI ppobj * pparray_rget_obj (pparray *array, size_t index);
    PPAPI int pparray_rget_bool (pparray *array, size_t index, int *v);
    PPAPI int pparray_rget_int (pparray *array, size_t index, ppint *v);
    PPAPI int pparray_rget_uint (pparray *array, size_t index, ppuint *v);
    PPAPI int pparray_rget_num (pparray *array, size_t index, ppnum *v);
    PPAPI ppname * pparray_rget_name (pparray *array, size_t index);
    PPAPI ppstring * pparray_rget_string (pparray *array, size_t index);
    PPAPI pparray * pparray_rget_array (pparray *array, size_t index);
    PPAPI ppdict * pparray_rget_dict (pparray *array, size_t index);
    PPAPI ppstream * pparray_rget_stream (pparray *array, size_t index);
    PPAPI ppref * pparray_rget_ref (pparray *array, size_t index);

    /* dict */

    #define ppdict_size(dict) ((dict)->size)
    #define ppdict_at(dict, index) ((dict)->data + index)
    #define ppdict_key(dict, index) ((dict)->keys[index])

    PPAPI ppobj * ppdict_get_obj (ppdict *dict, const char *name);
    PPAPI int ppdict_get_bool (ppdict *dict, const char *name, int *v);
    PPAPI int ppdict_get_int (ppdict *dict, const char *name, ppint *v);
    PPAPI int ppdict_get_uint (ppdict *dict, const char *name, ppuint *v);
    PPAPI int ppdict_get_num (ppdict *dict, const char *name, ppnum *v);
    PPAPI ppname * ppdict_get_name (ppdict *dict, const char *name);
    PPAPI ppstring * ppdict_get_string (ppdict *dict, const char *name);
    PPAPI pparray * ppdict_get_array (ppdict *dict, const char *name);
    PPAPI ppdict * ppdict_get_dict (ppdict *dict, const char *name);
    //PPAPI ppstream * ppdict_get_stream (ppdict *dict, const char *name);
    PPAPI ppref * ppdict_get_ref (ppdict *dict, const char *name);

    PPAPI ppobj * ppdict_rget_obj (ppdict *dict, const char *name);
    PPAPI int ppdict_rget_bool (ppdict *dict, const char *name, int *v);
    PPAPI int ppdict_rget_int (ppdict *dict, const char *name, ppint *v);
    PPAPI int ppdict_rget_uint (ppdict *dict, const char *name, ppuint *v);
    PPAPI int ppdict_rget_num (ppdict *dict, const char *name, ppnum *v);
    PPAPI ppname * ppdict_rget_name (ppdict *dict, const char *name);
    PPAPI ppstring * ppdict_rget_string (ppdict *dict, const char *name);
    PPAPI pparray * ppdict_rget_array (ppdict *dict, const char *name);
    PPAPI ppdict * ppdict_rget_dict (ppdict *dict, const char *name);
    PPAPI ppstream * ppdict_rget_stream (ppdict *dict, const char *name);
    PPAPI ppref * ppdict_rget_ref (ppdict *dict, const char *name);

    #define ppdict_first(dict, pkey, obj) (pkey = (dict)->keys, obj = (dict)->data)
    #define ppdict_next(pkey, obj) (++(pkey), ++(obj))

    /* stream */

    #define ppstream_dict(stream) ((stream)->dict)

    PPAPI uint8_t * ppstream_first (ppstream *stream, size_t *size, int decode);
    PPAPI uint8_t * ppstream_next (ppstream *stream, size_t *size);
    PPAPI uint8_t * ppstream_all (ppstream *stream, size_t *size, int decode);
    PPAPI void ppstream_done (ppstream *stream);

    PPAPI void ppstream_init_buffers (void);
    PPAPI void ppstream_free_buffers (void);

    /* ref */

    #define ppref_obj(ref) (&(ref)->object)

    /* xref */

    PPAPI ppxref * ppdoc_xref (ppdoc *pdf);
    PPAPI ppxref * ppxref_prev (ppxref *xref);
    PPAPI ppdict * ppxref_trailer (ppxref *xref);
    PPAPI ppdict * ppxref_catalog (ppxref *xref);
    PPAPI ppdict * ppxref_info (ppxref *xref);
    PPAPI ppref * ppxref_pages (ppxref *xref);
    PPAPI ppref * ppxref_find (ppxref *xref, ppuint refnumber);

    /* doc */

    PPAPI ppdoc * ppdoc_load (const char *filename);
    PPAPI ppdoc * ppdoc_filehandle (FILE *file, int closefile);
    #define ppdoc_file(file) ppdoc_filehandle(file, 1)
    PPAPI ppdoc * ppdoc_mem (const void *data, size_t size);
    PPAPI void ppdoc_free (ppdoc *pdf);

    #define ppdoc_trailer(pdf) ppxref_trailer(ppdoc_xref(pdf))
    #define ppdoc_catalog(pdf) ppxref_catalog(ppdoc_xref(pdf))
    #define ppdoc_info(pdf) ppxref_info(ppdoc_xref(pdf))
    #define ppdoc_pages(pdf) ppxref_pages(ppdoc_xref(pdf))

    PPAPI ppuint ppdoc_page_count (ppdoc *pdf);
    PPAPI ppref * ppdoc_page (ppdoc *pdf, ppuint index);
    PPAPI ppref *  ppdoc_first_page (ppdoc *pdf);
    PPAPI ppref * ppdoc_next_page (ppdoc *pdf);

    PPAPI ppstream * ppcontents_first (ppdict *dict);
    PPAPI ppstream * ppcontents_next (ppdict *dict, ppstream *stream);

    /* crypt */

    typedef enum {
      PPCRYPT_NONE = 0,
      PPCRYPT_DONE = 1,
      PPCRYPT_FAIL = -1,
      PPCRYPT_PASS = -2
    } ppcrypt_status;

    PPAPI ppcrypt_status ppdoc_crypt_status (ppdoc *pdf);
    PPAPI ppcrypt_status ppdoc_crypt_pass (ppdoc *pdf, const void *userpass, size_t userpasslength, const void *ownerpass, size_t ownerpasslength);

    /* permission flags, effect in Acrobat File -> Properties -> Security tab */

    PPAPI ppint ppdoc_permissions (ppdoc *pdf);

    #define PPDOC_ALLOW_PRINT (1<<2)        // printing
    #define PPDOC_ALLOW_MODIFY (1<<3)       // filling form fields, signing, creating template pages
    #define PPDOC_ALLOW_COPY (1<<4)         // copying, copying for accessibility
    #define PPDOC_ALLOW_ANNOTS (1<<5)       // filling form fields, copying, signing
    #define PPDOC_ALLOW_EXTRACT (1<<9)      // contents copying for accessibility
    #define PPDOC_ALLOW_ASSEMBLY (1<<10)    // (no effect)
    #define PPDOC_ALLOW_PRINT_HIRES (1<<11) // (no effect)

    /* context */

    typedef struct ppcontext ppcontext;

    PPAPI ppcontext * ppcontext_new (void);
    PPAPI void ppcontext_done (ppcontext *context);
    PPAPI void ppcontext_free (ppcontext *context);

    /* contents parser */

    PPAPI ppobj * ppcontents_first_op (ppcontext *context, ppstream *stream, size_t *psize, ppname **pname);
    PPAPI ppobj * ppcontents_next_op (ppcontext *context, ppstream *stream, size_t *psize, ppname **pname);
    PPAPI ppobj * ppcontents_parse (ppcontext *context, ppstream *stream, size_t *psize);

    /* boxes and transforms */

    typedef struct {
      ppnum lx, ly, rx, ry;
    } pprect;

    PPAPI pprect * pparray_to_rect (pparray *array, pprect *rect);
    PPAPI pprect * ppdict_get_rect (ppdict *dict, const char *name, pprect *rect);
    PPAPI pprect * ppdict_get_box (ppdict *dict, const char *name, pprect *rect);

    typedef struct {
      ppnum xx, xy, yx, yy, x, y;
    } ppmatrix;

    PPAPI ppmatrix * pparray_to_matrix (pparray *array, ppmatrix *matrix);
    PPAPI ppmatrix * ppdict_get_matrix (ppdict *dict, const char *name, ppmatrix *matrix);

    /* logger */

    typedef void (*pplogger_callback) (const char *message, void *alien);
    PPAPI void pplog_callback (pplogger_callback logger, void *alien);
    PPAPI int pplog_prefix (const char *prefix);

    /* version */

    PPAPI const char * ppdoc_version_string (ppdoc *pdf);
    PPAPI int ppdoc_version_number (ppdoc *pdf, int *minor);

    /* doc info */

    PPAPI size_t ppdoc_file_size (ppdoc *pdf);
    PPAPI ppuint ppdoc_objects (ppdoc *pdf);
    PPAPI size_t ppdoc_memory (ppdoc *pdf, size_t *waste);

    #endif

# Changes

## v0.97

First release integrated with luatex sources, plus portability changes from Luigi.

## v0.98

Changed references resolving in case of incremental updates; tech notes ppxref_find() in ppxref.c.

## v0.99

Fixed streams handling; null characters should NOT be gobbled after “stream” keyword

## v1.00

Fixed streams handling (Luigi); object streams updated before other streams Revised streams handling, ppstream API extended

## v1.01

Fixed names handling (thanks Hans); digits after ‘#’ weren’t skipped

## v1.02

Fixed page finder (thanks Luigi)

## v1.03

Fixed TIFF predictor (thanks folks)

## v1.04

Fixed TIFF predictor for ARM

## v1.05

Attempt to fix parsing inline images crap

## v2.00

Deep rework on allocators. Deep rework on strings and names.

## v2.01

Fixed invalid stream buffer handling; `iof_discard()` no longer reclaims the source (`filter->next`) filter. Sanity alignment adjustments in `iof_heap`.

## v2.02

Fixed incorrect encoding of strings alterego with octal escaping, thanks Luigi. On Hans request added `ppdoc_filehandle()` function and `ppdoc_file()` macro for loading ppdoc from `FILE *`.

## v2.03

Fixed alloc/free of `ppdoc` heap; `ppdoc` is now mallocated, no longer taken from its own heap. Again, thanks Luigi. Fixed warnings about dereferencing type-puned pointers, and some others.

## v2.04

Byte lookups for names/strings loaders are now int8_t, as char might be signed. ppdoc_first_page() / ppdoc_next_page() iterator now handles a case when /Kids array is empty. Fixed generating keys for encrypted streams; ppstring_internal() returns the string of the proper size. More stream tests in pptest3.c. Some rework on md5 and sha2.

## v2.05

uint8_t instead of ppbyte in internals; ppbyte intent is “the most natural 8-bit integer”, so it is ‘char’, but internally we almost always need uint8_t (char may be signed or not..)

# TODO

- external streams (egzotic)