lmtpdfelib.c /size: 55 Kb    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5/*tex
6
7    This file hosts the encapsulated \PDF\ support code used for inclusion and access from \LUA.
8
9*/
10
11# include "luametatex.h"
12
13// # define PDFE_METATABLE_INSTANCE   "pdfe.instance"
14// # define PDFE_METATABLE_DICTIONARY "pdfe.dictionary"
15// # define PDFE_METATABLE_ARRAY      "pdfe.array"
16// # define PDFE_METATABLE_STREAM     "pdfe.stream"
17// # define PDFE_METATABLE_REFERENCE  "pdfe.reference"
18
19# include "../libraries/pplib/pplib.h"
20
21/*tex
22
23    We start with some housekeeping. Dictionaries, arrays, streams and references get userdata,
24    while strings, names, integers, floats and booleans become regular \LUA\ objects. We need to
25    define a few metatable identifiers too.
26
27*/
28
29typedef struct pdfe_document {
30    ppdoc *document;
31    int    open;
32    int    isfile;
33    char  *memstream;
34    int    pages;
35    int    index;
36} pdfe_document ;
37
38typedef struct pdfe_dictionary {
39    ppdict *dictionary;
40} pdfe_dictionary;
41
42typedef struct pdfe_array {
43    pparray *array;
44} pdfe_array;
45
46typedef struct pdfe_stream {
47    ppstream *stream;
48    int       decode;
49    int       open;
50} pdfe_stream;
51
52typedef struct pdfe_reference {
53 /* ppref  *reference; */
54    ppxref *xref;
55    int     onum;
56} pdfe_reference;
57
58/*tex
59
60    We need to check if we have the right userdata. A similar warning is issued when encounter a
61    problem. We don't exit.
62
63*/
64
65static void pdfe_invalid_object_warning(const char *detail, int where)
66{
67    tex_formatted_warning("pdfe lib", "lua <pdfe %s> expected, case %i", detail, where);
68}
69
70/* todo: use luaL_checkudata */
71
72typedef enum pdfelib_errors { 
73    no_error,
74    to_string_error,
75    to_table_error,
76    get_catalog_error,
77    get_trailer_error,
78    get_info_error,
79    get_page_error,
80    get_pages_error,
81    get_size_error,
82    get_status_error,
83    get_version_error,
84    get_n_of_pages_error,
85    get_n_of_objects_error,
86    get_memory_usage_error,
87    get_from_error,
88    get_box_error,
89    open_error,
90    close_error,
91    read_error,
92    free_error,
93    unencrypt_error,
94} pdfelib_errors;
95
96static pdfe_document *pdfelib_aux_check_isdocument(lua_State *L, int n, int where)
97{
98    pdfe_document *p = (pdfe_document *) lua_touserdata(L, n);
99    if (p && lua_getmetatable(L, n)) {
100        lua_get_metatablelua(pdfe_instance);
101        if (! p->document) { 
102            p = NULL; // todo: invalid document
103        } else if (! lua_rawequal(L, -1, -2)) {
104            p = NULL; // todo: no document 
105        }
106        lua_pop(L, 2);
107        if (p) {
108            return p;
109        }
110    }
111    if (where != free_error) {
112        pdfe_invalid_object_warning("document", where);
113    }
114    return NULL;
115}
116
117static pdfe_dictionary *pdfelib_aux_check_isdictionary(lua_State *L, int n, int where)
118{
119    pdfe_dictionary *p = (pdfe_dictionary *) lua_touserdata(L, n);
120    if (p && lua_getmetatable(L, n)) {
121        lua_get_metatablelua(pdfe_dictionary_instance);
122        if (! lua_rawequal(L, -1, -2)) {
123            p = NULL;
124        }
125        lua_pop(L, 2);
126        if (p) {
127            return p;
128        }
129    }
130    pdfe_invalid_object_warning("dictionary",  where);
131    return NULL;
132}
133
134static pdfe_array *pdfelib_aux_check_isarray(lua_State *L, int n, int where)
135{
136    pdfe_array *p = (pdfe_array *) lua_touserdata(L, n);
137    if (p && lua_getmetatable(L, n)) {
138        lua_get_metatablelua(pdfe_array_instance);
139        if (! lua_rawequal(L, -1, -2)) {
140            p = NULL;
141        }
142        lua_pop(L, 2);
143        if (p) {
144            return p;
145        }
146    }
147    pdfe_invalid_object_warning("array", where);
148    return NULL;
149}
150
151static pdfe_stream *pdfelib_aux_check_isstream(lua_State *L, int n, int where)
152{
153    pdfe_stream *p = (pdfe_stream *) lua_touserdata(L, n);
154    if (p && lua_getmetatable(L, n)) {
155        lua_get_metatablelua(pdfe_stream_instance);
156        if (! lua_rawequal(L, -1, -2)) {
157            p = NULL;
158        }
159        lua_pop(L, 2);
160        if (p) {
161            return p;
162        }
163    }
164    pdfe_invalid_object_warning("stream", where);
165    return NULL;
166}
167
168static pdfe_reference *pdfelib_aux_check_isreference(lua_State *L, int n, int where)
169{
170    pdfe_reference *p = (pdfe_reference *) lua_touserdata(L, n);
171    if (p && lua_getmetatable(L, n)) {
172        lua_get_metatablelua(pdfe_reference_instance);
173        if (! lua_rawequal(L, -1, -2)) {
174            p = NULL;
175        }
176        lua_pop(L, 2);
177        if (p) {
178            return p;
179        }
180    }
181    pdfe_invalid_object_warning("reference", where);
182    return NULL;
183}
184
185/*tex
186
187    Reporting the type of a userdata is just a sequence of tests till we find the right one. We
188    return nothing is it is no pdfe type.
189
190 \starttyping
191    t = pdfe.type(<pdfe document|dictionary|array|reference|stream>)
192 \stoptyping
193
194*/
195
196/*
197# define check_type(field,meta,name) do { \
198    lua_get_metatablelua(meta); \
199    if (lua_rawequal(L, -1, -2)) { \
200        lua_pushstring(L, name); \
201        return 1; \
202    } \
203    lua_pop(L, 1); \
204} while (0)
205
206static int pdfelib_type(lua_State *L)
207{
208    void *p = lua_touserdata(L, 1);
209    if (p && lua_getmetatable(L, 1)) {
210        check_type(document,   pdfe_instance,   PDFE_METATABLE_INSTANCE);
211        check_type(dictionary, pdfe_dictionary, PDFE_METATABLE_DICTIONARY);
212        check_type(array,      pdfe_array,      PDFE_METATABLE_ARRAY);
213        check_type(reference,  pdfe_reference,  PDFE_METATABLE_REFERENCE);
214        check_type(stream,     pdfe_stream,     PDFE_METATABLE_STREAM);
215    }
216    return 0;
217}
218*/
219
220# define check_type(field,meta) do { \
221    lua_get_metatablelua(meta); \
222    if (lua_rawequal(L, -1, -2)) { \
223        lua_push_key(meta); \
224        return 1; \
225    } \
226    lua_pop(L, 1); \
227} while (0)
228
229static int pdfelib_type(lua_State *L)
230{
231    void *p = lua_touserdata(L, 1);
232    if (p && lua_getmetatable(L, 1)) {
233        check_type(document, pdfe_instance);
234        check_type(dictionary, pdfe_dictionary_instance);
235        check_type(array, pdfe_array_instance);
236        check_type(reference, pdfe_reference_instance);
237        check_type(stream, pdfe_stream_instance);
238    }
239    return 0;
240}
241
242/*tex
243
244    The \type {tostring} metamethods are similar and report a pdfe type plus a pointer value, as is
245    rather usual in \LUA. I ditched the macro that defined them and are now verbose.
246
247*/
248
249static int pdfelib_document_tostring(lua_State *L) {
250    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, to_string_error);
251    if (p) {
252        lua_pushfstring(L, "<pdfe.document %p>", p->document);
253        return 1;
254    } else {
255        return 0;
256    }
257}
258
259static int pdfelib_dictionary_tostring(lua_State *L) {
260    pdfe_dictionary *p = pdfelib_aux_check_isdictionary(L, 1, to_string_error);
261    if (p) {
262        lua_pushfstring(L, "<pdfe.dictionary %p>", p->dictionary);
263        return 1;
264    } else {
265        return 0;
266    }
267}
268
269static int pdfelib_array_tostring(lua_State *L) {
270    pdfe_array *p = pdfelib_aux_check_isarray(L, 1, to_string_error);
271    if (p) {
272        lua_pushfstring(L, "<pdfe.array %p>", p->array);
273        return 1;
274    } else {
275        return 0;
276    }
277}
278
279static int pdfelib_stream_tostring(lua_State *L) {
280    pdfe_stream *p = pdfelib_aux_check_isstream(L, 1, to_string_error);
281    if (p) {
282        lua_pushfstring(L, "<pdfe.stream %p>", p->stream);
283        return 1;
284    } else {
285        return 0;
286    }
287}
288
289static int pdfelib_reference_tostring(lua_State *L) {
290    pdfe_reference *p = pdfelib_aux_check_isreference(L, 1, to_string_error);
291    if (p) {
292        lua_pushfstring(L, "<pdfe.reference %d>", p->onum);
293        return 1;
294    } else {
295        return 0;
296    }
297}
298
299/*tex
300
301    The pushers look rather similar. We have two variants, one that just pushes the object, and
302    another that also pushes some extra information.
303
304*/
305
306inline static void pdfe_push_dictionary(lua_State *L, ppdict *dictionary)
307{
308    pdfe_dictionary *d = (pdfe_dictionary *) lua_newuserdatauv(L, sizeof(pdfe_dictionary), 0);
309 // luaL_getmetatable(L, PDFE_METATABLE_DICTIONARY);
310    lua_get_metatablelua(pdfe_dictionary_instance);
311    lua_setmetatable(L, -2);
312    d->dictionary = dictionary;
313}
314
315static int pdfelib_aux_pushdictionary(lua_State *L, ppdict *dictionary)
316{
317    if (dictionary) {
318        pdfe_push_dictionary(L, dictionary);
319        lua_pushinteger(L, (lua_Integer) dictionary->size);
320        return 2;
321    } else {
322        return 0;
323    }
324}
325
326static int pdfelib_aux_pushdictionaryonly(lua_State *L, ppdict *dictionary)
327{
328    if (dictionary) {
329        pdfe_push_dictionary(L, dictionary);
330        return 1;
331    } else {
332        return 0;
333    }
334}
335
336inline static void pdfe_push_array(lua_State *L, pparray *array)
337{
338    pdfe_array *a = (pdfe_array *) lua_newuserdatauv(L, sizeof(pdfe_array), 0);
339 // luaL_getmetatable(L, PDFE_METATABLE_ARRAY);
340    lua_get_metatablelua(pdfe_array_instance);
341    lua_setmetatable(L, -2);
342    a->array = array;
343}
344
345static int pdfelib_aux_pusharray(lua_State *L, pparray *array)
346{
347    if (array) {
348        pdfe_push_array(L, array);
349        lua_pushinteger(L, (lua_Integer) array->size);
350        return 2;
351    } else {
352        return 0;
353    }
354}
355
356static int pdfelib_aux_pusharrayonly(lua_State *L, pparray *array)
357{
358    if (array) {
359        pdfe_push_array(L, array);
360        return 1;
361    } else {
362        return 0;
363    }
364}
365
366inline static void pdfe_push_stream(lua_State *L, ppstream *stream)
367{
368    pdfe_stream *s = (pdfe_stream *) lua_newuserdatauv(L, sizeof(pdfe_stream), 0);
369 // luaL_getmetatable(L, PDFE_METATABLE_STREAM);
370    lua_get_metatablelua(pdfe_stream_instance);
371    lua_setmetatable(L, -2);
372    s->stream = stream;
373    s->open = 0;
374    s->decode = 0;
375}
376
377static int pdfelib_aux_pushstream(lua_State *L, ppstream *stream)
378{
379    if (stream) {
380        pdfe_push_stream(L, stream);
381        if (pdfelib_aux_pushdictionary(L, stream->dict) > 0) {
382            return 3;
383        } else {
384            return 1;
385        }
386    } else {
387        return 0;
388    }
389}
390
391static int pdfelib_aux_pushstreamonly(lua_State *L, ppstream *stream)
392{
393    if (stream) {
394        pdfe_push_stream(L, stream);
395        if (pdfelib_aux_pushdictionaryonly(L, stream->dict) > 0) {
396            return 2;
397        } else {
398            return 1;
399        }
400    } else {
401        return 0;
402    }
403}
404
405inline static void pdfe_push_reference(lua_State *L, ppref *reference)
406{
407    pdfe_reference *r = (pdfe_reference *) lua_newuserdatauv(L, sizeof(pdfe_reference), 0);
408 // luaL_getmetatable(L, PDFE_METATABLE_REFERENCE);
409    lua_get_metatablelua(pdfe_reference_instance);
410    lua_setmetatable(L, -2);
411    r->xref = reference->xref;
412    r->onum = (int) reference->number;
413 }
414
415static int pdfelib_aux_pushreference(lua_State *L, ppref *reference)
416{
417    if (reference && reference->number != 0) {
418        pdfe_push_reference(L, reference);
419        lua_pushinteger(L, (lua_Integer) reference->number);
420        return 2;
421    } else {
422        return 0;
423    }
424}
425
426/*tex
427
428    The next function checks for the type and then pushes the matching data on the stack.
429
430    \starttabulate[|c|l|l|l|]
431        \BC type \BC meaning \BC value \BC detail \NC \NR
432        \NC \type {0} \NC none \NC nil \NC \NC \NR
433        \NC \type {1} \NC null \NC nil \NC \NC \NR
434        \NC \type {2} \NC boolean \NC boolean \NC \NC \NR
435        \NC \type {3} \NC boolean \NC integer \NC \NC \NR
436        \NC \type {4} \NC number \NC float \NC \NC \NR
437        \NC \type {5} \NC name \NC string \NC \NC \NR
438        \NC \type {6} \NC string \NC string \NC type \NC \NR
439        \NC \type {7} \NC array \NC arrayobject \NC size \NC \NR
440        \NC \type {8} \NC dictionary \NC dictionaryobject \NC size \NC \NR
441        \NC \type {9} \NC stream \NC streamobject \NC dictionary size \NC \NR
442        \NC \type {10} \NC reference \NC integer \NC \NC \NR
443        \LL
444    \stoptabulate
445
446    A name and string can be distinguished by the extra type value that a string has.
447
448*/
449
450static int pdfelib_aux_pushvalue(lua_State *L, ppobj *object)
451{
452    switch (object->type) {
453        case PPNONE:
454        case PPNULL:
455            lua_pushnil(L);
456            return 1;
457        case PPBOOL:
458            lua_pushboolean(L, (int) object->integer);
459            return 1;
460        case PPINT:
461            lua_pushinteger(L, (lua_Integer) object-> integer);
462            return 1;
463        case PPNUM:
464            lua_pushnumber(L, (double) object->number);
465            return 1;
466        case PPNAME:
467            {
468                ppname *n = ppname_decoded(object->name) ;
469                lua_pushlstring(L, ppname_data(n), ppname_size(n));
470                return 1;
471            }
472        case PPSTRING:
473            lua_pushlstring(L, ppstring_data(object->string), ppstring_size(object->string));
474            lua_pushboolean(L, ppstring_hex(object->string));
475            return 2;
476        case PPARRAY:
477            return pdfelib_aux_pusharray(L, object->array);
478        case PPDICT:
479            return pdfelib_aux_pushdictionary(L, object->dict);
480        case PPSTREAM:
481            return pdfelib_aux_pushstream(L, object->stream);
482        case PPREF:
483            return pdfelib_aux_pushreference(L, object->ref);
484    }
485    return 0;
486}
487
488/*tex
489
490    We need to start someplace when we traverse a document's tree. There are three places:
491
492    \starttyping
493    catalogdictionary = getcatalog(documentobject)
494    trailerdictionary = gettrailer(documentobject)
495    infodictionary    = getinfo   (documentobject)
496    \stoptyping
497
498*/
499
500static int pdfelib_getcatalog(lua_State *L)
501{
502    pdfe_document* p = pdfelib_aux_check_isdocument (L, 1, get_catalog_error);
503    if (p) {
504        return pdfelib_aux_pushdictionaryonly (L, ppdoc_catalog (p->document));
505    } else {
506        return 0;
507    }
508}
509
510static int pdfelib_gettrailer(lua_State *L)
511{
512    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_trailer_error);
513    if (p) {
514        return pdfelib_aux_pushdictionaryonly (L, ppdoc_trailer (p->document));
515    } else {
516        return 0;
517    }
518}
519
520static int pdfelib_getinfo(lua_State *L)
521{
522    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_info_error);
523    if (p) {
524        return pdfelib_aux_pushdictionaryonly (L, ppdoc_info (p->document));
525    } else {
526        return 0;
527    }
528}
529
530/*tex
531
532    We have three more helpers.
533
534    \starttyping
535    [key,] type, value, detail = getfromdictionary(dictionaryobject,name|index)
536           type, value, detail = getfromarray     (arrayobject,index)
537    [key,] type, value, detail = getfromstream    (streamobject,name|index)
538    \stoptyping
539
540*/
541
542static int pdfelib_getfromarray(lua_State *L)
543{
544    pdfe_array *a = pdfelib_aux_check_isarray(L, 1, get_from_error);
545    if (a) {
546        unsigned int index = lmt_checkinteger(L, 2) - 1;
547        if (index < a->array->size) {
548            ppobj *object = pparray_at(a->array,index);
549            if (object) {
550                lua_pushinteger(L, (lua_Integer) object->type);
551                return 1 + pdfelib_aux_pushvalue(L, object);
552            }
553        }
554    }
555    return 0;
556}
557
558static int pdfelib_getfromdictionary(lua_State *L)
559{
560    pdfe_dictionary *d = pdfelib_aux_check_isdictionary(L, 1, get_from_error);
561    if (d) {
562        if (lua_type(L, 2) == LUA_TSTRING) {
563            const char *name = luaL_checkstring(L, 2);
564            ppobj *object = ppdict_get_obj(d->dictionary, name);
565            if (object) {
566                lua_pushinteger(L, (lua_Integer) object->type);
567                return 1 + pdfelib_aux_pushvalue(L, object);
568            }
569        } else {
570            unsigned int index = lmt_checkinteger(L, 2) - 1;
571            if (index < d->dictionary->size) {
572                ppobj *object = ppdict_at(d->dictionary,index);
573                if (object) {
574                    ppname *key = ppname_decoded(ppdict_key(d->dictionary, index));
575                    lua_pushlstring(L, ppname_data(key), ppname_size(key));
576                    lua_pushinteger(L, (lua_Integer) object->type);
577                    return 2 + pdfelib_aux_pushvalue(L, object);
578                }
579            }
580        }
581    }
582    return 0;
583}
584
585static int pdfelib_getfromstream(lua_State *L)
586{
587    pdfe_stream *s = (pdfe_stream *) lua_touserdata(L, get_from_error);
588 // pdfe_stream *s = check_isstream(L, 1);
589    if (s) {
590        ppdict *d = s->stream->dict;
591        if (lua_type(L, 2) == LUA_TSTRING) {
592            const char *name = luaL_checkstring(L, 2);
593            ppobj *object = ppdict_get_obj(d, name);
594            if (object) {
595                lua_pushinteger(L, (lua_Integer) object->type);
596                return 1 + pdfelib_aux_pushvalue(L, object);
597            }
598        } else {
599            unsigned int index = lmt_checkinteger(L, 2) - 1;
600            if (index < d->size) {
601                ppobj *object = ppdict_at(d, index);
602                if (object) {
603                    ppname *key = ppname_decoded(ppdict_key(d, index));
604                    lua_pushlstring(L, ppname_data(key), ppname_size(key));
605                    lua_pushinteger(L, (lua_Integer) object->type);
606                    return 2 + pdfelib_aux_pushvalue(L, object);
607                }
608            }
609        }
610    }
611    return 0;
612}
613
614/*tex
615
616    An indexed table with all entries in an array can be fetched with::
617
618    \starttyping
619    t = arraytotable(arrayobject)
620    \stoptyping
621
622    An hashed table with all entries in an dictionary can be fetched with::
623
624    \starttyping
625    t = dictionarytotable(arrayobject)
626    \stoptyping
627
628*/
629
630static void pdfelib_totable(lua_State *L, ppobj *object, int flat)
631{
632    int n = pdfelib_aux_pushvalue(L, object);
633    if (flat && n < 2) {
634        return;
635    } else {
636        /* [value] [extra] [more] */
637        lua_createtable(L, n + 1, 0);
638        if (n == 1) {
639            /* value { nil, nil } */
640            lua_insert(L, -2);
641            /* { nil, nil } value */
642            lua_rawseti(L, -2, 2);
643            /* { nil , value } */
644        } else if (n == 2) {
645            /* value extra { nil, nil, nil } */
646            lua_insert(L, -3);
647            /* { nil, nil, nil } value extra */
648            lua_rawseti(L, -3, 3);
649            /* { nil, nil, extra } value */
650            lua_rawseti(L, -2, 2);
651            /* { nil, value, extra } */
652        } else if (n == 3) {
653            /* value extra more { nil, nil, nil, nil } */
654            lua_insert(L, -4);
655            /* { nil, nil, nil, nil, nil } value extra more */
656            lua_rawseti(L, -4, 4);
657            /* { nil, nil, nil, more } value extra */
658            lua_rawseti(L, -3, 3);
659            /* { nil, nil, extra, more } value */
660            lua_rawseti(L, -2, 2);
661            /* { nil, value, extra, more } */
662        }
663        lua_pushinteger(L, (lua_Integer) object->type);
664        /* { nil, [value], [extra], [more] } type */
665        lua_rawseti(L, -2, 1);
666        /* { type, [value], [extra], [more] } */
667    }
668}
669
670static int pdfelib_arraytotable(lua_State *L)
671{
672    pdfe_array *a = pdfelib_aux_check_isarray(L, 1, to_table_error);
673    if (a) {
674        int flat = lua_isboolean(L, 2);
675        int j = 0;
676        lua_createtable(L, (int) a->array->size, 0);
677        /* table */
678        for (unsigned int i = 0; i < a->array->size; i++) {
679            ppobj *object = pparray_at(a->array,i);
680            if (object) {
681                pdfelib_totable(L, object, flat);
682                /* table { type, [value], [extra], [more] } */
683                lua_rawseti(L, -2, ++j);
684                /* table[i] = { type, [value], [extra], [more] } */
685            }
686        }
687        return 1;
688    } else {
689        return 0;
690    }
691}
692
693static int pdfelib_dictionarytotable(lua_State *L)
694{
695    pdfe_dictionary *d = pdfelib_aux_check_isdictionary(L, 1, to_table_error);
696    if (d) {
697        int flat = lua_isboolean(L, 2);
698        lua_createtable(L, 0, (int) d->dictionary->size);
699        /* table */
700        for (unsigned int i = 0; i < d->dictionary->size; i++) {
701            ppobj *object = ppdict_at(d->dictionary, i);
702            if (object) {
703                ppname *key = ppname_decoded(ppdict_key(d->dictionary, i));
704                lua_pushlstring(L, ppname_data(key), ppname_size(key));
705                /* table key */
706                pdfelib_totable(L, object, flat);
707                /* table key { type, [value], [extra], [more] } */
708                lua_rawset(L, -3);
709                /* table[key] = { type, [value], [extra] } */
710            }
711        }
712        return 1;
713    } else {
714        return 0;
715    }
716}
717
718/*tex
719
720    All pages are collected with:
721
722    \starttyping
723    { { dict, size, objnum }, ... } = pagestotable(document)
724    \stoptyping
725
726*/
727
728static int pdfelib_pagestotable(lua_State *L)
729{
730    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, to_table_error);
731    if (p) {
732        ppdoc *d = p->document;
733        int i = 1;
734        int j = 0;
735        lua_createtable(L, (int) ppdoc_page_count(d), 0);
736        /* pages[1..n] */
737        for (ppref *r = ppdoc_first_page(d); r; r = ppdoc_next_page(d), ++i) {
738            lua_createtable(L, 3, 0);
739            if (ppref_obj(r)) {
740                pdfelib_aux_pushdictionary(L, ppref_obj(r)->dict);
741                /* table dictionary n */
742                lua_rawseti(L, -3, 2);
743                /* table dictionary */
744                lua_rawseti(L, -2, 1);
745                /* table */
746                lua_pushinteger(L, r->number);
747                /* table reference */
748                lua_rawseti(L, -2, 3);
749                /* table */
750                lua_rawseti(L, -2, ++j);
751                /* pages[i] = { dictionary, size, objnum } */
752            }
753        }
754        return 1;
755    } else {
756        return 0;
757    }
758}
759
760/*tex
761
762    Streams can be fetched on one go:
763
764    \starttyping
765    string, n = readwholestream(streamobject,decode)
766    \stoptyping
767
768*/
769
770static int pdfelib_stream_readwhole(lua_State *L)
771{
772    pdfe_stream *s = pdfelib_aux_check_isstream(L, 1, read_error);
773    if (s) {
774        uint8_t *b = NULL;
775        int decode = 0;
776        size_t n = 0;
777        if (s->open > 0) {
778            ppstream_done(s->stream);
779            s->open = 0;
780            s->decode = 0;
781        }
782        if (lua_gettop(L) > 1 && lua_isboolean(L, 2)) {
783            decode = lua_toboolean(L, 2);
784        }
785        b = ppstream_all(s->stream, &n, decode);
786        lua_pushlstring(L, (const char *) b, n);
787        lua_pushinteger(L, (lua_Integer) n);
788        ppstream_done(s->stream);
789        return 2;
790    } else {
791        return 0;
792    }
793}
794
795/*tex
796
797    Alternatively streams can be fetched stepwise:
798
799    \starttyping
800    okay = openstream(streamobject,[decode])
801    string, n = readfromstream(streamobject)
802    closestream(streamobject)
803    \stoptyping
804
805*/
806
807static int pdfelib_stream_open(lua_State *L)
808{
809    pdfe_stream *s = pdfelib_aux_check_isstream(L, 1, open_error);
810    if (s) {
811        if (s->open == 0) {
812            if (lua_gettop(L) > 1) {
813                s->decode = lua_isboolean(L, 2);
814            }
815            s->open = 1;
816        }
817        lua_pushboolean(L,1);
818        return 1;
819    } else {
820        return 0;
821    }
822}
823
824static int pdfelib_stream_close(lua_State *L)
825{
826    pdfe_stream *s = pdfelib_aux_check_isstream(L, 1, close_error);
827    if (s && s->open > 0) {
828        ppstream_done(s->stream);
829        s->open = 0;
830        s->decode = 0;
831    }
832    return 0;
833}
834
835static int pdfelib_stream_read(lua_State *L)
836{
837    pdfe_stream *s = pdfelib_aux_check_isstream(L, 1, read_error);
838    if (s) {
839        size_t n = 0;
840        uint8_t *d = NULL;
841        if (s->open == 1) {
842            d = ppstream_first(s->stream, &n, s->decode);
843            s->open = 2;
844        } else if (s->open == 2) {
845            d = ppstream_next(s->stream, &n);
846        } else {
847            return 0;
848        }
849        lua_pushlstring(L, (const char *) d, n);
850        lua_pushinteger(L, (lua_Integer) n);
851        return 2;
852    } else {
853        return 0;
854    }
855}
856
857/*tex
858
859    There are two methods for opening a document: files and strings.
860
861    \starttyping
862    documentobject = open(filename)
863    documentobject = new(string,length)
864    \stoptyping
865
866    Closing happens with:
867
868    \starttyping
869    close(documentobject)
870    \stoptyping
871
872    When the \type {new} function gets a peudo filename as third argument, no user data will be
873    created but the stream is accessible as image.
874
875*/
876
877/*
878static int pdfelib_test(lua_State *L)
879{
880    const char *filename = luaL_checkstring(L, 1);
881    ppdoc *d = ppdoc_load(filename);
882    if (d) {
883        lua_pushboolean(L,1);
884        ppdoc_free(d);
885    } else {
886        lua_pushboolean(L,0);
887    }
888    return 1;
889}
890*/
891
892static int aux_pdfelib_open(lua_State *L, FILE *f)
893{
894    ppdoc *d = ppdoc_filehandle(f, 1);
895    if (d) {
896        pdfe_document *p = (pdfe_document *) lua_newuserdatauv(L, sizeof(pdfe_document), 0);
897     // luaL_getmetatable(L, PDFE_METATABLE_INSTANCE);
898        lua_get_metatablelua(pdfe_instance);
899        lua_setmetatable(L, -2);
900        p->document = d;
901        p->open = 1;
902        p->isfile = 1;
903        p->memstream = NULL;
904        return 1;
905    } else { 
906        return 0;
907    }
908}
909
910static int pdfelib_open(lua_State *L)
911{
912    const char *filename = luaL_checkstring(L, 1);
913    FILE *f = aux_utf8_fopen(filename, "rb");
914    if (f && aux_pdfelib_open(L, f)) {
915        return 1;
916    } else {
917     /* tex_normal_warning("pdfe lib", "no valid file handle"); */ /* the caller should handle it */
918        return 0;
919    }
920}
921
922static int pdfelib_openfile(lua_State *L)
923{
924    luaL_Stream *fs = ((luaL_Stream *) luaL_checkudata(L, 1, LUA_FILEHANDLE));
925    FILE *f = (fs->closef) ? fs->f : NULL;
926    if (f && aux_pdfelib_open(L, f)) {
927        /*tex We trick \LUA\ in believing the file is closed. */
928        fs->closef = NULL;
929        return 1;
930    } else {
931     /* tex_normal_warning("pdfe lib", "no valid file handle"); */ /* the caller should handle it */
932        return 0;
933    }
934}
935
936static int pdfelib_new(lua_State *L)
937{
938    size_t streamsize = 0;
939    const char *docstream = NULL;
940    switch (lua_type(L, 1)) {
941        case LUA_TSTRING:
942            docstream = lua_tolstring(L, 1, &streamsize);
943            if (! docstream) {
944                tex_normal_warning("pdfe lib", "invalid string");
945                return 0;
946            } else {
947                break;
948            }
949        case LUA_TLIGHTUSERDATA:
950            /*tex
951                The stream comes as a sequence of bytes. This could happen from a library (we used
952                this for swiglib gm output tests).
953            */
954            docstream = (const char *) lua_touserdata(L, 1);
955            if (! docstream) {
956                tex_normal_warning("pdfe lib", "invalid lightuserdata");
957                return 0;
958            } else {
959                break;
960            }
961        default:
962            tex_normal_warning("pdfe lib", "string or lightuserdata expected");
963            return 0;
964    }
965    streamsize = luaL_optinteger(L, 2, streamsize);
966    if (streamsize > 0) {
967        char *memstream = lmt_generic_malloc((unsigned) (streamsize + 1)); /* we have no hook into pdfe free */
968        if (memstream) {
969            ppdoc *d = NULL;
970            memcpy(memstream, docstream, (streamsize + 1));
971            memstream[streamsize] = '\0';
972            d = ppdoc_mem(memstream, streamsize);
973            if (d) {
974                pdfe_document *p = (pdfe_document *) lua_newuserdatauv(L, sizeof(pdfe_document), 0);
975             // luaL_getmetatable(L, PDFE_METATABLE_INSTANCE);
976                lua_get_metatablelua(pdfe_instance);
977                lua_setmetatable(L, -2);
978                p->document = d;
979                p->open = 1;
980                p->isfile = 0;
981                p->memstream = memstream;
982                return 1;
983            } else {
984                tex_normal_warning("pdfe lib", "unable to handle stream");
985            }
986        } else {
987            tex_normal_warning("pdfe lib", "not enough memory for new stream");
988        }
989    } else {
990        tex_normal_warning("pdfe lib", "stream with size > 0 expected");
991    }
992    return 0;
993}
994
995/*
996
997    There is no garbage collection needed as the library itself manages the objects. Normally
998    objects don't take much space. Streams use buffers so (I assume) that they are not
999    persistent. The only collector is in the parent object (the document).
1000
1001*/
1002
1003static int pdfelib_document_free(lua_State *L)
1004{
1005    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, free_error);
1006    if (p && p->open) {
1007        if (p->document) {
1008            ppdoc_free(p->document);
1009            p->document = NULL;
1010        }
1011        if (p->memstream) {
1012         /* pplib does this: xfree(p->memstream); */
1013            p->memstream = NULL;
1014        }
1015        p->open = 0;
1016    }
1017    return 0;
1018}
1019
1020static int pdfelib_close(lua_State *L)
1021{
1022    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, close_error);
1023    if (p && p->open) {
1024        if (p->document) {
1025            ppdoc_free(p->document);
1026            p->document = NULL;
1027        }
1028        if (p->memstream) {
1029         /* pplib does this: xfree(p->memstream); */
1030            p->memstream = NULL;
1031        }
1032        p->open = 0;
1033    }
1034    return 0;
1035}
1036
1037/*tex
1038
1039    A document is can be uncrypted with:
1040
1041    \starttyping
1042    status = unencrypt(documentobject,user,owner)
1043    \stoptyping
1044
1045    Instead of a password \type {nil} can be passed, so there are three possible useful combinations.
1046
1047*/
1048
1049static int pdfelib_unencrypt(lua_State *L)
1050{
1051    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, unencrypt_error);
1052    if (p) {
1053        size_t u = 0;
1054        size_t o = 0;
1055        const char* user = NULL;
1056        const char* owner = NULL;
1057        int top = lua_gettop(L);
1058        if (top > 1) {
1059            if (lua_type(L,2) == LUA_TSTRING) {
1060                user = lua_tolstring(L, 2, &u);
1061            } else {
1062                /*tex we're not too picky but normally it will be nil or false */
1063            }
1064            if (top > 2) {
1065                if (lua_type(L,3) == LUA_TSTRING) {
1066                    owner = lua_tolstring(L, 3, &o);
1067                } else {
1068                    /*tex we're not too picky but normally it will be nil or false */
1069                }
1070            }
1071            lua_pushinteger(L, (lua_Integer) ppdoc_crypt_pass(p->document, user, u, owner, o));
1072            return 1;
1073        }
1074    }
1075    lua_pushinteger(L, (lua_Integer) PPCRYPT_FAIL);
1076    return 1;
1077}
1078
1079/*tex
1080
1081    There are a couple of ways to get information about the document:
1082
1083    \starttyping
1084    n             = getsize       (documentobject)
1085    major, minor  = getversion    (documentobject)
1086    status        = getstatus     (documentobject)
1087    n             = getnofobjects (documentobject)
1088    n             = getnofpages   (documentobject)
1089    bytes, waste  = getmemoryusage(documentobject)
1090    \stoptyping
1091
1092*/
1093
1094static int pdfelib_getsize(lua_State *L)
1095{
1096    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_size_error);
1097    if (p) {
1098        lua_pushinteger(L, (lua_Integer) ppdoc_file_size(p->document));
1099        return 1;
1100    } else {
1101        return 0;
1102    }
1103}
1104
1105
1106static int pdfelib_getversion(lua_State *L)
1107{
1108    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_version_error);
1109    if (p) {
1110        int minor;
1111        int major = ppdoc_version_number(p->document, &minor);
1112        lua_pushinteger(L, (lua_Integer) major);
1113        lua_pushinteger(L, (lua_Integer) minor);
1114        return 2;
1115    } else {
1116        return 0;
1117    }
1118}
1119
1120static int pdfelib_getstatus(lua_State *L)
1121{
1122    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_status_error);
1123    if (p) {
1124        lua_pushinteger(L, (lua_Integer) ppdoc_crypt_status(p->document));
1125        return 1;
1126    } else {
1127        return 0;
1128    }
1129}
1130
1131static int pdfelib_getnofobjects(lua_State *L)
1132{
1133    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_n_of_objects_error);
1134    if (p) {
1135        lua_pushinteger(L, (lua_Integer) ppdoc_objects(p->document));
1136        return 1;
1137    } else {
1138        return 0;
1139    }
1140}
1141
1142static int pdfelib_getnofpages(lua_State *L)
1143{
1144    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_n_of_pages_error);
1145    if (p) {
1146        lua_pushinteger(L, (lua_Integer) ppdoc_page_count(p->document));
1147        return 1;
1148    } else {
1149        return 0;
1150    }
1151}
1152
1153static int pdfelib_getmemoryusage(lua_State *L)
1154{
1155    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_memory_usage_error);
1156    if (p) {
1157        size_t w = 0;
1158        size_t m = ppdoc_memory(p->document, &w);
1159        lua_pushinteger(L, (lua_Integer) m);
1160        lua_pushinteger(L, (lua_Integer) w);
1161        return 2;
1162    } else {
1163        return 0;
1164    }
1165}
1166
1167/*
1168    A specific page dictionary can be filtered with the next command. So, there is no need to parse
1169    the document page tree (with these \type {kids} arrays).
1170
1171    \starttyping
1172    dictionaryobject = getpage(documentobject,pagenumber)
1173    \stoptyping
1174
1175*/
1176
1177static int pdfelib_aux_pushpage(lua_State *L, ppdoc *d, int page)
1178{
1179    if ((page <= 0) || (page > ((int) ppdoc_page_count(d)))) {
1180        return 0;
1181    } else {
1182        ppref *pp = ppdoc_page(d, page);
1183        return pdfelib_aux_pushdictionaryonly(L, ppref_obj(pp)->dict);
1184    }
1185}
1186
1187static int pdfelib_getpage(lua_State *L)
1188{
1189    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_page_error);
1190    if (p) {
1191        return pdfelib_aux_pushpage(L, p->document, lmt_checkinteger(L, 2));
1192    } else {
1193        return 0;
1194    }
1195}
1196
1197static int pdfelib_aux_pushpages(lua_State *L, ppdoc *d)
1198{
1199    int i = 1;
1200    lua_createtable(L, (int) ppdoc_page_count(d), 0);
1201    /* pages[1..n] */
1202    for (ppref *r = ppdoc_first_page(d); r; r = ppdoc_next_page(d), ++i) {
1203        pdfelib_aux_pushdictionaryonly(L,ppref_obj(r)->dict);
1204        lua_rawseti(L, -2, i);
1205    }
1206    return 1 ;
1207}
1208
1209static int pdfelib_getpages(lua_State *L)
1210{
1211    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_pages_error);
1212    if (p) {
1213        return pdfelib_aux_pushpages(L, p->document);
1214    } else {
1215        return 0;
1216    }
1217}
1218
1219/*tex
1220
1221    The boundingbox (\type {MediaBox) and similar boxes can be available in a (page) doctionary but
1222    also in a parent object. Therefore a helper is available that does the (backtracked) lookup.
1223
1224    \starttyping
1225    { lx, ly, rx, ry } = getbox(dictionaryobject)
1226    \stoptyping
1227
1228*/
1229
1230static int pdfelib_getbox(lua_State *L)
1231{
1232    if (lua_gettop(L) > 1 && lua_type(L,2) == LUA_TSTRING) {
1233        pdfe_dictionary *p = pdfelib_aux_check_isdictionary(L, 1, get_box_error);
1234        if (p) {
1235            const char *key = lua_tostring(L, 2);
1236            pprect box = { 0, 0, 0, 0 };
1237            pprect *r = ppdict_get_box(p->dictionary, key, &box);
1238            if (r) {
1239                lua_createtable(L, 4, 0);
1240                lua_pushnumber(L, r->lx);
1241                lua_rawseti(L, -2, 1);
1242                lua_pushnumber(L, r->ly);
1243                lua_rawseti(L, -2, 2);
1244                lua_pushnumber(L, r->rx);
1245                lua_rawseti(L, -2, 3);
1246                lua_pushnumber(L, r->ry);
1247                lua_rawseti(L, -2, 4);
1248                return 1;
1249            }
1250        }
1251    }
1252    return 0;
1253}
1254
1255/*tex
1256
1257    This one is needed when you use the detailed getters and run into an object reference. The
1258    regular getters resolve this automatically.
1259
1260    \starttyping
1261    [dictionary|array|stream]object = getfromreference(referenceobject)
1262    \stoptyping
1263
1264*/
1265
1266static int pdfelib_getfromreference(lua_State *L)
1267{
1268    pdfe_reference *r = pdfelib_aux_check_isreference(L, 1, get_from_error);
1269    if (r && r->xref) {
1270        ppref *rr = ppxref_find(r->xref, (ppuint) r->onum);
1271        if (rr) {
1272            ppobj *o = ppref_obj(rr);
1273            if (o) {
1274                lua_pushinteger(L, (lua_Integer) o->type);
1275                return 1 + pdfelib_aux_pushvalue(L, o);
1276            }
1277        }
1278    }
1279    return 0;
1280}
1281
1282static int pdfelib_getfromobject(lua_State *L)
1283{
1284    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_from_error);
1285    if (p) {
1286        ppref *rr = ppxref_find(p->document->xref, lua_tointeger(L, 2));
1287        if (rr) {
1288             ppobj *o = ppref_obj(rr);
1289             if (o) {
1290                 lua_pushinteger(L, (lua_Integer) o->type);
1291                 return 1 + pdfelib_aux_pushvalue(L, o);
1292             }
1293        }
1294    }
1295    return 0;
1296}
1297
1298static int pdfelib_getobjectrange(lua_State *L)
1299{
1300    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_from_error);
1301    if (p) {
1302        ppref *rr = ppxref_find(p->document->xref, lua_tointeger(L, 2));
1303        if (rr) {
1304             lua_pushinteger(L, (lua_Integer) rr->offset);
1305             lua_pushinteger(L, (lua_Integer) rr->length);
1306             return 2;
1307        }
1308    }
1309    return 0;
1310}
1311
1312/*tex
1313
1314    Here are some convenient getters:
1315
1316    \starttyping
1317    <string>         = getstring    (array|dict|ref,index|key)
1318    <integer>        = getinteger   (array|dict|ref,index|key)
1319    <number>         = getnumber    (array|dict|ref,index|key)
1320    <boolan>         = getboolean   (array|dict|ref,index|key)
1321    <string>         = getname      (array|dict|ref,index|key)
1322    <dictionary>     = getdictionary(array|dict|ref,index|key)
1323    <array>          = getarray     (array|dict|ref,index|key)
1324    <stream>, <dict> = getstream    (array|dict|ref,index|key)
1325    \stoptyping
1326
1327    We report issues when reasonable but are silent when it makes sense. We don't error on this
1328    because we expect the user code to act reasonable on a return value.
1329
1330*/
1331
1332static int pdfelib_valid_index(lua_State *L, void **p, int *t)
1333{
1334    *t = lua_type(L, 2);
1335    *p = lua_touserdata(L, 1);
1336    lua_settop(L, 2);
1337    if (! *p) {
1338        switch (*t) {
1339            case LUA_TSTRING:
1340                tex_normal_warning("pdfe lib", "lua <pdfe dictionary> expected");
1341                break;
1342            case LUA_TNUMBER:
1343                tex_normal_warning("pdfe lib", "lua <pdfe array> expected");
1344                break;
1345            default:
1346                tex_normal_warning("pdfe lib", "invalid arguments");
1347                break;
1348        }
1349        return 0;
1350    } else if (! lua_getmetatable(L, 1)) {
1351        tex_normal_warning("pdfe lib", "first argument should be a <pde array> or <pde dictionary>");
1352        return 0;
1353    } else {
1354        return 1;
1355    }
1356}
1357
1358static void pdfelib_invalid_index_warning(void)
1359{
1360    tex_normal_warning("pdfe lib", "second argument should be integer or string");
1361}
1362
1363/*tex
1364
1365    The direct fetcher returns the result or |NULL| when there is nothing found. The indirect
1366    fetcher passes a pointer to the target variable and returns success state.
1367
1368    The next two functions used to be macros but as we try to avoid large ones with much code, they
1369    are now functions.
1370
1371*/
1372
1373typedef void * (*pp_a_direct)   (void *a, size_t      index);
1374typedef void * (*pp_d_direct)   (void *d, const char *key);
1375typedef int    (*pp_a_indirect) (void *a, size_t      index, void **value);
1376typedef int    (*pp_d_indirect) (void *d, const char *key,   void **value);
1377
1378static int pdfelib_get_value_direct(lua_State *L, void **value, pp_d_direct get_d, pp_a_direct get_a)
1379{
1380    int t = 0;
1381    void *p = NULL;
1382    if (pdfelib_valid_index(L, &p, &t)) {
1383        switch (t) {
1384            case LUA_TSTRING:
1385                {
1386                    const char *key = lua_tostring(L, 2);
1387                    lua_get_metatablelua(pdfe_dictionary_instance);
1388                    if (lua_rawequal(L, -1, -2)) {
1389                        *value = get_d(((pdfe_dictionary *) p)->dictionary, key);
1390                        return 1;
1391                    } else {
1392                        lua_get_metatablelua(pdfe_reference_instance);
1393                        if (lua_rawequal(L, -1, -3)) {
1394                            ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL; \
1395                            ppobj *o = (r) ? ppref_obj(r) : NULL;
1396                            if (o && o->type == PPDICT) {
1397                                *value = get_d((ppdict *) o->dict, key);
1398                                return 1;
1399                            }
1400                        }
1401                    }
1402                }
1403                break;
1404            case LUA_TNUMBER:
1405                {
1406                    size_t index = lua_tointeger(L, 2);
1407                    lua_get_metatablelua(pdfe_array_instance);
1408                    if (lua_rawequal(L, -1, -2)) {
1409                        *value = get_a(((pdfe_array *) p)->array, index);
1410                        return 2;
1411                    } else {
1412                        lua_get_metatablelua(pdfe_reference_instance);
1413                        if (lua_rawequal(L, -1, -3)) {
1414                            ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL; \
1415                            ppobj *o = (r) ? ppref_obj(r) : NULL;
1416                            if (o && o->type == PPARRAY) {
1417                                *value = get_a((pparray *) o->array, index);
1418                                return 2;
1419                            }
1420                        }
1421                    }
1422                }
1423                break;
1424            default:
1425                pdfelib_invalid_index_warning();
1426                break;
1427        }
1428    }
1429    return 0;
1430}
1431
1432static int pdfelib_get_value_indirect(lua_State *L, void **value, pp_d_indirect get_d, pp_a_indirect get_a)
1433{
1434    int t = 0;
1435    void *p = NULL;
1436    if (pdfelib_valid_index(L, &p, &t)) {
1437        switch (t) {
1438            case LUA_TSTRING:
1439                {
1440                    const char *key = lua_tostring(L, 2);
1441                    lua_get_metatablelua(pdfe_dictionary_instance);
1442                    if (lua_rawequal(L, -1, -2)) {
1443                        return get_d(((pdfe_dictionary *) p)->dictionary, key, value);
1444                    } else {
1445                        lua_get_metatablelua(pdfe_reference_instance);
1446                        if (lua_rawequal(L, -1, -3)) {
1447                            ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL;
1448                            ppobj *o = (r) ? ppref_obj(r) : NULL;
1449                            if (o && o->type == PPDICT)
1450                                return get_d(o->dict, key, value);
1451                        }
1452                    }
1453                }
1454                break;
1455            case LUA_TNUMBER:
1456                {
1457                    size_t index = lua_tointeger(L, 2);
1458                    lua_get_metatablelua(pdfe_array_instance);
1459                    if (lua_rawequal(L, -1, -2)) {
1460                        return get_a(((pdfe_array *) p)->array, index, value);
1461                    } else {
1462                        lua_get_metatablelua(pdfe_reference_instance);
1463                        if (lua_rawequal(L, -1, -3)) {
1464                            ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL;
1465                            ppobj *o = (r) ? ppref_obj(r) : NULL;
1466                            if (o && o->type == PPARRAY)
1467                                return get_a(o->array, index, value);
1468                        }
1469                    }
1470                }
1471                break;
1472            default:
1473                pdfelib_invalid_index_warning();
1474                break;
1475        }
1476    }
1477    return 0;
1478}
1479
1480static int pdfelib_getstring(lua_State *L)
1481{
1482    if (lua_gettop(L) > 1) {
1483        ppstring *value = NULL;
1484        int okay = 0;
1485        int how = 0;
1486        if (lua_type(L, 3) == LUA_TBOOLEAN) {
1487            if (lua_toboolean(L, 3)) {
1488                how = 1;
1489            } else {
1490                how = 2;
1491            }
1492        }
1493        okay = pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_string, (void *) &pparray_rget_string);
1494        if (okay && value) {
1495            if (how == 1) {
1496                value = ppstring_decoded(value);
1497            }
1498            /*tex This used to return one value but we made it \LUATEX\ compatible. */
1499            lua_pushlstring(L, ppstring_data(value), ppstring_size(value));
1500            if (how == 2) {
1501                lua_pushboolean(L, ppstring_hex(value));
1502                return 2;
1503            } else {
1504                return 1;
1505            }
1506        }
1507    }
1508    return 0;
1509}
1510
1511static int pdfelib_getinteger(lua_State *L)
1512{
1513    if (lua_gettop(L) > 1) {
1514        ppint value = 0;
1515        if (pdfelib_get_value_indirect(L, (void *) &value, (void *) &ppdict_rget_int, (void *) &pparray_rget_int)) {
1516            lua_pushinteger(L, (lua_Integer) value);
1517            return 1;
1518        }
1519    }
1520    return 0;
1521}
1522
1523static int pdfelib_getnumber(lua_State *L)
1524{
1525    if (lua_gettop(L) > 1) {
1526        ppnum value = 0;
1527        if (pdfelib_get_value_indirect(L, (void *) &value, (void *) &ppdict_rget_num, (void *) &pparray_rget_num)) {
1528            lua_pushnumber(L, value);
1529            return 1;
1530        }
1531    }
1532    return 0;
1533}
1534
1535static int pdfelib_getboolean(lua_State *L)
1536{
1537    if (lua_gettop(L) > 1) {
1538        int value = 0;
1539        if (pdfelib_get_value_indirect(L, (void *) &value, (void *) &ppdict_rget_bool, (void *) &pparray_rget_bool)) {
1540            lua_pushboolean(L, value);
1541            return 1;
1542        }
1543    }
1544    return 0;
1545}
1546
1547static int pdfelib_getname(lua_State *L)
1548{
1549    if (lua_gettop(L) > 1) {
1550        ppname *value = NULL;
1551        pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_name, (void *) &pparray_rget_name);
1552        if (value) {
1553            value = ppname_decoded(value) ;
1554            lua_pushlstring(L, ppname_data(value), ppname_size(value));
1555            return 1;
1556        }
1557    }
1558    return 0;
1559}
1560
1561static int pdfelib_getdictionary(lua_State *L)
1562{
1563    if (lua_gettop(L) > 1) {
1564        ppdict *value = NULL;
1565        pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_dict, (void *) &pparray_rget_dict);
1566        if (value) {
1567            return pdfelib_aux_pushdictionaryonly(L, value);
1568        }
1569    }
1570    return 0;
1571}
1572
1573static int pdfelib_getarray(lua_State *L)
1574{
1575    if (lua_gettop(L) > 1) {
1576        pparray *value = NULL;
1577        pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_array, (void *) &pparray_rget_array);
1578        if (value) {
1579            return pdfelib_aux_pusharrayonly(L, value);
1580        }
1581    }
1582    return 0;
1583}
1584
1585static int pdfelib_getstream(lua_State *L)
1586{
1587    if (lua_gettop(L) > 1) {
1588        ppobj *value = NULL;
1589        pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_obj, (void *) &pparray_rget_obj);
1590        if (value && value->type == PPSTREAM) {
1591            return pdfelib_aux_pushstreamonly(L, (ppstream *) value->stream);
1592        }
1593    }
1594    return 0;
1595}
1596
1597/*tex
1598
1599    The generic pushed that does a similar job as the previous getters acts upon
1600    the type.
1601
1602*/
1603
1604static int pdfelib_pushvalue(lua_State *L, ppobj *object)
1605{
1606    switch (object->type) {
1607        case PPNONE:
1608        case PPNULL:
1609            lua_pushnil(L);
1610            break;
1611        case PPBOOL:
1612            lua_pushboolean(L, (int) object->integer);
1613            break;
1614        case PPINT:
1615            lua_pushinteger(L, (lua_Integer) object->integer);
1616            break;
1617        case PPNUM:
1618            lua_pushnumber(L, (double) object->number);
1619            break;
1620        case PPNAME:
1621            {
1622                ppname *n = ppname_decoded(object->name) ;
1623                lua_pushlstring(L, ppname_data(n), ppname_size(n));
1624            }
1625            break;
1626        case PPSTRING:
1627            lua_pushlstring(L, ppstring_data(object->string), ppstring_size(object->string));
1628            break;
1629        case PPARRAY:
1630            return pdfelib_aux_pusharrayonly(L, object->array);
1631        case PPDICT:
1632            return pdfelib_aux_pushdictionary(L, object->dict);
1633        case PPSTREAM:
1634            return pdfelib_aux_pushstream(L, object->stream);
1635        case PPREF:
1636            pdfelib_aux_pushreference(L, object->ref);
1637            break;
1638        /*tex We get a funny message in clang about covering all cases. */
1639        /*
1640        default:
1641            lua_pushnil(L);
1642            break;
1643        */
1644    }
1645    return 1;
1646}
1647
1648/*tex
1649
1650    Finally we arrived at the acessors for the userdata objects. The use previously defined helpers.
1651
1652*/
1653
1654static int pdfelib_document_access(lua_State *L)
1655{
1656    if (lua_type(L, 2) == LUA_TSTRING) {
1657     // pdfe_document *p = (pdfe_document *) lua_touserdata(L, 1);
1658        const char *s = lua_tostring(L, 2);
1659        if (lua_key_eq(s, catalog) || lua_key_eq(s, Catalog)) {
1660         // return pdfelib_aux_pushdictionaryonly(L, ppdoc_catalog(p->document));
1661            return pdfelib_getcatalog(L);
1662        } else if (lua_key_eq(s, info) || lua_key_eq(s, Info)) {
1663         // return pdfelib_aux_pushdictionaryonly(L, ppdoc_info(p->document));
1664            return pdfelib_getinfo(L);
1665        } else if (lua_key_eq(s, trailer) || lua_key_eq(s, Trailer)) {
1666         // return pdfelib_aux_pushdictionaryonly(L, ppdoc_trailer(p->document));
1667            return pdfelib_getcatalog(L);
1668        } else if (lua_key_eq(s, pages) || lua_key_eq(s, Pages)) {
1669         // return pdfelib_aux_pushpages(L, p->document);
1670            return pdfelib_getpages(L);
1671        }
1672    }
1673    return 0;
1674}
1675
1676static int pdfelib_array_access(lua_State *L)
1677{
1678    if (lua_type(L, 2) == LUA_TNUMBER) {
1679        pdfe_array *p = (pdfe_array *) lua_touserdata(L, 1);
1680        ppint index = lua_tointeger(L, 2) - 1;
1681        ppobj *o = pparray_rget_obj(p->array, index);
1682        if (o) {
1683            return pdfelib_pushvalue(L, o);
1684        }
1685    }
1686    return 0;
1687}
1688
1689static int pdfelib_dictionary_access(lua_State *L)
1690{
1691    pdfe_dictionary *p = (pdfe_dictionary *) lua_touserdata(L, 1);
1692    switch (lua_type(L, 2)) {
1693        case LUA_TSTRING:
1694            {
1695                const char *key = lua_tostring(L, 2);
1696                ppobj *o = ppdict_rget_obj(p->dictionary, key);
1697                if (o) {
1698                    return pdfelib_pushvalue(L, o);
1699                }
1700            }
1701            break;
1702        case LUA_TNUMBER:
1703            {
1704                ppint index = lua_tointeger(L, 2) - 1;
1705                ppobj *o = ppdict_at(p->dictionary, index);
1706                if (o) {
1707                    return pdfelib_pushvalue(L, o);
1708                }
1709            }
1710            break;
1711    }
1712    return 0;
1713}
1714
1715static int pdfelib_stream_access(lua_State *L)
1716{
1717    pdfe_stream *p = (pdfe_stream *) lua_touserdata(L, 1);
1718    switch (lua_type(L, 2)) {
1719        case LUA_TSTRING:
1720            {
1721                const char *key = lua_tostring(L, 2);
1722                ppobj *o = ppdict_rget_obj(p->stream->dict, key);
1723                if (o) {
1724                    return pdfelib_pushvalue(L, o);
1725                }
1726            }
1727            break;
1728        case LUA_TNUMBER:
1729            {
1730                ppint index = lua_tointeger(L, 2) - 1;
1731                ppobj *o = ppdict_at(p->stream->dict, index);
1732                if (o) {
1733                    return pdfelib_pushvalue(L, o);
1734                }
1735            }
1736            break;
1737    }
1738    return 0;
1739}
1740
1741/*tex
1742
1743    The length metamethods are defined last.
1744
1745*/
1746
1747static int pdfelib_array_size(lua_State *L)
1748{
1749    pdfe_array *p = (pdfe_array *) lua_touserdata(L, 1);
1750    lua_pushinteger(L, (lua_Integer) p->array->size);
1751    return 1;
1752}
1753
1754static int pdfelib_dictionary_size(lua_State *L)
1755{
1756    pdfe_dictionary *p = (pdfe_dictionary *) lua_touserdata(L, 1);
1757    lua_pushinteger(L, (lua_Integer) p->dictionary->size);
1758    return 1;
1759}
1760
1761static int pdfelib_stream_size(lua_State *L)
1762{
1763    pdfe_stream *p = (pdfe_stream *) lua_touserdata(L, 1);
1764    lua_pushinteger(L, (lua_Integer) p->stream->dict->size);
1765    return 1;
1766}
1767
1768/*tex
1769
1770    We now initialize the main interface. We might add few more informational helpers but this is
1771    it.
1772
1773*/
1774
1775static const struct luaL_Reg pdfelib_function_list[] = {
1776    /* management */
1777    { "type",               pdfelib_type              },
1778    { "open",               pdfelib_open              },
1779    { "openfile",           pdfelib_openfile          },
1780    { "new",                pdfelib_new               },
1781    { "close",              pdfelib_close             },
1782    { "unencrypt",          pdfelib_unencrypt         },
1783    /* statistics */
1784    { "getversion",         pdfelib_getversion        },
1785    { "getstatus",          pdfelib_getstatus         },
1786    { "getsize",            pdfelib_getsize           },
1787    { "getnofobjects",      pdfelib_getnofobjects     },
1788    { "getnofpages",        pdfelib_getnofpages       },
1789    { "getmemoryusage",     pdfelib_getmemoryusage    },
1790    /* getters */
1791    { "getcatalog",         pdfelib_getcatalog        },
1792    { "gettrailer",         pdfelib_gettrailer        },
1793    { "getinfo",            pdfelib_getinfo           },
1794    { "getpage",            pdfelib_getpage           },
1795    { "getpages",           pdfelib_getpages          },
1796    { "getbox",             pdfelib_getbox            },
1797    { "getfromreference",   pdfelib_getfromreference  },
1798    { "getfromdictionary",  pdfelib_getfromdictionary },
1799    { "getfromarray",       pdfelib_getfromarray      },
1800    { "getfromstream",      pdfelib_getfromstream     },
1801    /* handy too */
1802    { "getfromobject",      pdfelib_getfromobject     },
1803    { "getobjectrange",     pdfelib_getobjectrange    },
1804    /* collectors */
1805    { "dictionarytotable",  pdfelib_dictionarytotable },
1806    { "arraytotable",       pdfelib_arraytotable      },
1807    { "pagestotable",       pdfelib_pagestotable      },
1808    /* more getters */
1809    { "getstring",          pdfelib_getstring         },
1810    { "getinteger",         pdfelib_getinteger        },
1811    { "getnumber",          pdfelib_getnumber         },
1812    { "getboolean",         pdfelib_getboolean        },
1813    { "getname",            pdfelib_getname           },
1814    { "getdictionary",      pdfelib_getdictionary     },
1815    { "getarray",           pdfelib_getarray          },
1816    { "getstream",          pdfelib_getstream         },
1817    /* streams */
1818    { "readwholestream",    pdfelib_stream_readwhole  },
1819    /* not really needed */
1820    { "openstream",         pdfelib_stream_open       },
1821    { "readfromstream",     pdfelib_stream_read       },
1822    { "closestream",        pdfelib_stream_close      },
1823    /* only for me, a test hook */
1824 /* { "test",               pdfelib_test              }, */
1825    /* done */
1826    { NULL,                 NULL                      }
1827};
1828
1829/*tex
1830
1831    The user data metatables are defined as follows. Watch how only the document needs a garbage
1832    collector.
1833
1834*/
1835
1836static const struct luaL_Reg pdfelib_instance_metatable[] = {
1837    { "__tostring", pdfelib_document_tostring },
1838    { "__gc",       pdfelib_document_free     },
1839    { "__index",    pdfelib_document_access   },
1840    { NULL,         NULL                      },
1841};
1842
1843static const struct luaL_Reg pdfelib_dictionary_metatable[] = {
1844    { "__tostring", pdfelib_dictionary_tostring },
1845    { "__index",    pdfelib_dictionary_access   },
1846    { "__len",      pdfelib_dictionary_size     },
1847    { NULL,         NULL                        },
1848};
1849
1850static const struct luaL_Reg pdfelib_array_metatable[] = {
1851    { "__tostring", pdfelib_array_tostring },
1852    { "__index",    pdfelib_array_access   },
1853    { "__len",      pdfelib_array_size     },
1854    { NULL,         NULL                   },
1855};
1856
1857static const struct luaL_Reg pdfelib_stream_metatable[] = {
1858    { "__tostring", pdfelib_stream_tostring  },
1859    { "__index",    pdfelib_stream_access    },
1860    { "__len",      pdfelib_stream_size      },
1861    { "__call",     pdfelib_stream_readwhole },
1862    { NULL,         NULL                     },
1863};
1864
1865static const struct luaL_Reg pdfelib_reference_metatable[] = {
1866    { "__tostring", pdfelib_reference_tostring },
1867    { NULL,         NULL                       },
1868};
1869
1870/*tex
1871
1872    Finally we have arrived at the main initialiser that will be called as part of \LUATEX's
1873    initializer.
1874
1875*/
1876
1877/*tex
1878
1879    Here we hook in the error handler.
1880
1881*/
1882
1883static void pdfelib_message(const char *message, void *alien)
1884{
1885    (void) (alien);
1886    tex_normal_warning("pdfe", message);
1887}
1888
1889int luaopen_pdfe(lua_State *L)
1890{
1891    /*tex First the four userdata object get their metatables defined. */
1892
1893    luaL_newmetatable(L, PDFE_METATABLE_DICTIONARY);
1894    luaL_setfuncs(L, pdfelib_dictionary_metatable, 0);
1895
1896    luaL_newmetatable(L, PDFE_METATABLE_ARRAY);
1897    luaL_setfuncs(L, pdfelib_array_metatable, 0);
1898
1899    luaL_newmetatable(L, PDFE_METATABLE_STREAM);
1900    luaL_setfuncs(L, pdfelib_stream_metatable, 0);
1901
1902    luaL_newmetatable(L, PDFE_METATABLE_REFERENCE);
1903    luaL_setfuncs(L, pdfelib_reference_metatable, 0);
1904
1905    /*tex Then comes the main (document) metatable: */
1906
1907    luaL_newmetatable(L, PDFE_METATABLE_INSTANCE);
1908    luaL_setfuncs(L, pdfelib_instance_metatable, 0);
1909
1910    /*tex Last the library opens up itself to the world. */
1911
1912    lua_newtable(L);
1913    luaL_setfuncs(L, pdfelib_function_list, 0);
1914
1915    pplog_callback(pdfelib_message, stderr);
1916
1917    return 1;
1918}
1919