lmtpdfelib.c /size: 58 Kb    last modification: 2025-02-21 11:03
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 (! lua_rawequal(L, -1, -2)) {
102            p = NULL; // todo: no document 
103        } else if (! p->document) { 
104            p = NULL; // todo: invalid 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
306static inline 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
336static inline 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
366static inline 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
405static inline 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[|rT|l|l|l|]
431        \BC type \BC meaning    \BC value            \BC detail          \NC \NR
432        \NC  0   \NC none       \NC nil              \NC                 \NC \NR
433        \NC  1   \NC null       \NC nil              \NC                 \NC \NR
434        \NC  2   \NC boolean    \NC boolean          \NC                 \NC \NR
435        \NC  3   \NC integer    \NC integer          \NC                 \NC \NR
436        \NC  4   \NC number     \NC float            \NC                 \NC \NR
437        \NC  5   \NC name       \NC string           \NC                 \NC \NR
438        \NC  6   \NC string     \NC string           \NC type            \NC \NR
439        \NC  7   \NC array      \NC arrayobject      \NC size            \NC \NR
440        \NC  8   \NC dictionary \NC dictionaryobject \NC size            \NC \NR
441        \NC  9   \NC stream     \NC streamobject     \NC dictionary size \NC \NR
442        \NC 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            {        
476                ppstring *s = ppstring_decoded(object->string);
477                lua_pushlstring(L, ppstring_data(s), ppstring_size(s));
478                lua_pushinteger(L, (s)->flags);
479                return 2;
480            }
481        case PPARRAY:
482            return pdfelib_aux_pusharray(L, object->array);
483        case PPDICT:
484            return pdfelib_aux_pushdictionary(L, object->dict);
485        case PPSTREAM:
486            return pdfelib_aux_pushstream(L, object->stream);
487        case PPREF:
488            return pdfelib_aux_pushreference(L, object->ref);
489    }
490    return 0;
491}
492
493/*tex
494
495    We need to start someplace when we traverse a document's tree. There are three places:
496
497    \starttyping
498    catalogdictionary = getcatalog(documentobject)
499    trailerdictionary = gettrailer(documentobject)
500    infodictionary    = getinfo   (documentobject)
501    \stoptyping
502
503*/
504
505static int pdfelib_getcatalog(lua_State *L)
506{
507    pdfe_document* p = pdfelib_aux_check_isdocument (L, 1, get_catalog_error);
508    if (p) {
509        return pdfelib_aux_pushdictionaryonly (L, ppdoc_catalog (p->document));
510    } else {
511        return 0;
512    }
513}
514
515static int pdfelib_gettrailer(lua_State *L)
516{
517    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_trailer_error);
518    if (p) {
519        return pdfelib_aux_pushdictionaryonly (L, ppdoc_trailer (p->document));
520    } else {
521        return 0;
522    }
523}
524
525static int pdfelib_getinfo(lua_State *L)
526{
527    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_info_error);
528    if (p) {
529        return pdfelib_aux_pushdictionaryonly (L, ppdoc_info (p->document));
530    } else {
531        return 0;
532    }
533}
534
535static int pdfelib_getpermissions(lua_State *L)
536{
537    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_info_error);
538    if (p) {
539        lua_pushinteger(L, ppdoc_permissions(p->document));
540    } else {
541        lua_pushnil(L);
542    }
543    return 1;
544}
545
546/*tex
547
548    We have three more helpers.
549
550    \starttyping
551    [key,] type, value, detail = getfromdictionary(dictionaryobject,name|index)
552           type, value, detail = getfromarray     (arrayobject,index)
553    [key,] type, value, detail = getfromstream    (streamobject,name|index)
554    \stoptyping
555
556*/
557
558static int pdfelib_getfromarray(lua_State *L)
559{
560    pdfe_array *a = pdfelib_aux_check_isarray(L, 1, get_from_error);
561    if (a) {
562        unsigned int index = lmt_checkinteger(L, 2) - 1;
563        if (index < a->array->size) {
564            ppobj *object = pparray_at(a->array,index);
565            if (object) {
566                lua_pushinteger(L, (lua_Integer) object->type);
567                return 1 + pdfelib_aux_pushvalue(L, object);
568            }
569        }
570    }
571    return 0;
572}
573
574static int pdfelib_getfromdictionary(lua_State *L)
575{
576    pdfe_dictionary *d = pdfelib_aux_check_isdictionary(L, 1, get_from_error);
577    if (d) {
578        if (lua_type(L, 2) == LUA_TSTRING) {
579            const char *name = luaL_checkstring(L, 2);
580            ppobj *object = ppdict_get_obj(d->dictionary, name);
581            if (object) {
582                lua_pushinteger(L, (lua_Integer) object->type);
583                return 1 + pdfelib_aux_pushvalue(L, object);
584            }
585        } else {
586            unsigned int index = lmt_checkinteger(L, 2) - 1;
587            if (index < d->dictionary->size) {
588                ppobj *object = ppdict_at(d->dictionary,index);
589                if (object) {
590                    ppname *key = ppname_decoded(ppdict_key(d->dictionary, index));
591                    lua_pushlstring(L, ppname_data(key), ppname_size(key));
592                    lua_pushinteger(L, (lua_Integer) object->type);
593                    return 2 + pdfelib_aux_pushvalue(L, object);
594                }
595            }
596        }
597    }
598    return 0;
599}
600
601static int pdfelib_getfromstream(lua_State *L)
602{
603    pdfe_stream *s = (pdfe_stream *) lua_touserdata(L, get_from_error);
604 // pdfe_stream *s = check_isstream(L, 1);
605    if (s) {
606        ppdict *d = s->stream->dict;
607        if (lua_type(L, 2) == LUA_TSTRING) {
608            const char *name = luaL_checkstring(L, 2);
609            ppobj *object = ppdict_get_obj(d, name);
610            if (object) {
611                lua_pushinteger(L, (lua_Integer) object->type);
612                return 1 + pdfelib_aux_pushvalue(L, object);
613            }
614        } else {
615            unsigned int index = lmt_checkinteger(L, 2) - 1;
616            if (index < d->size) {
617                ppobj *object = ppdict_at(d, index);
618                if (object) {
619                    ppname *key = ppname_decoded(ppdict_key(d, index));
620                    lua_pushlstring(L, ppname_data(key), ppname_size(key));
621                    lua_pushinteger(L, (lua_Integer) object->type);
622                    return 2 + pdfelib_aux_pushvalue(L, object);
623                }
624            }
625        }
626    }
627    return 0;
628}
629
630/*tex
631
632    An indexed table with all entries in an array can be fetched with::
633
634    \starttyping
635    t = arraytotable(arrayobject)
636    \stoptyping
637
638    An hashed table with all entries in an dictionary can be fetched with::
639
640    \starttyping
641    t = dictionarytotable(arrayobject)
642    \stoptyping
643
644*/
645
646static void pdfelib_totable(lua_State *L, ppobj *object, int flat)
647{
648    int n = pdfelib_aux_pushvalue(L, object);
649    if (flat && n < 2) {
650        return;
651    } else {
652        /* [value] [extra] [more] */
653        lua_createtable(L, n + 1, 0);
654        if (n == 1) {
655            /* value { nil, nil } */
656            lua_insert(L, -2);
657            /* { nil, nil } value */
658            lua_rawseti(L, -2, 2);
659            /* { nil , value } */
660        } else if (n == 2) {
661            /* value extra { nil, nil, nil } */
662            lua_insert(L, -3);
663            /* { nil, nil, nil } value extra */
664            lua_rawseti(L, -3, 3);
665            /* { nil, nil, extra } value */
666            lua_rawseti(L, -2, 2);
667            /* { nil, value, extra } */
668        } else if (n == 3) {
669            /* value extra more { nil, nil, nil, nil } */
670            lua_insert(L, -4);
671            /* { nil, nil, nil, nil, nil } value extra more */
672            lua_rawseti(L, -4, 4);
673            /* { nil, nil, nil, more } value extra */
674            lua_rawseti(L, -3, 3);
675            /* { nil, nil, extra, more } value */
676            lua_rawseti(L, -2, 2);
677            /* { nil, value, extra, more } */
678        }
679        lua_pushinteger(L, (lua_Integer) object->type);
680        /* { nil, [value], [extra], [more] } type */
681        lua_rawseti(L, -2, 1);
682        /* { type, [value], [extra], [more] } */
683    }
684}
685
686static int pdfelib_arraytotable(lua_State *L)
687{
688    pdfe_array *a = pdfelib_aux_check_isarray(L, 1, to_table_error);
689    if (a) {
690        int flat = lua_isboolean(L, 2);
691        int j = 0;
692        lua_createtable(L, (int) a->array->size, 0);
693        /* table */
694        for (unsigned int i = 0; i < a->array->size; i++) {
695            ppobj *object = pparray_at(a->array,i);
696            if (object) {
697                pdfelib_totable(L, object, flat);
698                /* table { type, [value], [extra], [more] } */
699                lua_rawseti(L, -2, ++j);
700                /* table[i] = { type, [value], [extra], [more] } */
701            }
702        }
703        return 1;
704    } else {
705        return 0;
706    }
707}
708
709static int pdfelib_dictionarytotable(lua_State *L)
710{
711    pdfe_dictionary *d = pdfelib_aux_check_isdictionary(L, 1, to_table_error);
712    if (d) {
713        int flat = lua_isboolean(L, 2);
714        lua_createtable(L, 0, (int) d->dictionary->size);
715        /* table */
716        for (unsigned int i = 0; i < d->dictionary->size; i++) {
717            ppobj *object = ppdict_at(d->dictionary, i);
718            if (object) {
719                ppname *key = ppname_decoded(ppdict_key(d->dictionary, i));
720                lua_pushlstring(L, ppname_data(key), ppname_size(key));
721                /* table key */
722                pdfelib_totable(L, object, flat);
723                /* table key { type, [value], [extra], [more] } */
724                lua_rawset(L, -3);
725                /* table[key] = { type, [value], [extra] } */
726            }
727        }
728        return 1;
729    } else {
730        return 0;
731    }
732}
733
734/*tex
735
736    All pages are collected with:
737
738    \starttyping
739    { { dict, size, objnum }, ... } = pagestotable(document)
740    \stoptyping
741
742*/
743
744static int pdfelib_pagestotable(lua_State *L)
745{
746    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, to_table_error);
747    if (p) {
748        ppdoc *d = p->document;
749        int i = 1;
750        int j = 0;
751        lua_createtable(L, (int) ppdoc_page_count(d), 0);
752        /* pages[1..n] */
753        for (ppref *r = ppdoc_first_page(d); r; r = ppdoc_next_page(d), ++i) {
754            lua_createtable(L, 3, 0);
755            if (ppref_obj(r)) {
756                pdfelib_aux_pushdictionary(L, ppref_obj(r)->dict);
757                /* table dictionary n */
758                lua_rawseti(L, -3, 2);
759                /* table dictionary */
760                lua_rawseti(L, -2, 1);
761                /* table */
762                lua_pushinteger(L, r->number);
763                /* table reference */
764                lua_rawseti(L, -2, 3);
765                /* table */
766                lua_rawseti(L, -2, ++j);
767                /* pages[i] = { dictionary, size, objnum } */
768            }
769        }
770        return 1;
771    } else {
772        return 0;
773    }
774}
775
776/*tex
777
778    Streams can be fetched on one go:
779
780    \starttyping
781    string, n = readwholestream(streamobject,decode)
782    \stoptyping
783
784*/
785
786static int pdfelib_stream_readwhole(lua_State *L)
787{
788    pdfe_stream *s = pdfelib_aux_check_isstream(L, 1, read_error);
789    if (s) {
790        uint8_t *b = NULL;
791        int decode = 0;
792        size_t n = 0;
793        if (s->open > 0) {
794            ppstream_done(s->stream);
795            s->open = 0;
796            s->decode = 0;
797        }
798        if (lua_gettop(L) > 1 && lua_isboolean(L, 2)) {
799            decode = lua_toboolean(L, 2);
800        }
801        b = ppstream_all(s->stream, &n, decode);
802        lua_pushlstring(L, (const char *) b, n);
803        lua_pushinteger(L, (lua_Integer) n);
804        ppstream_done(s->stream);
805        return 2;
806    } else {
807        return 0;
808    }
809}
810
811/*tex
812
813    Alternatively streams can be fetched stepwise:
814
815    \starttyping
816    okay = openstream(streamobject,[decode])
817    string, n = readfromstream(streamobject)
818    closestream(streamobject)
819    \stoptyping
820
821*/
822
823static int pdfelib_stream_open(lua_State *L)
824{
825    pdfe_stream *s = pdfelib_aux_check_isstream(L, 1, open_error);
826    if (s) {
827        if (s->open == 0) {
828            if (lua_gettop(L) > 1) {
829                s->decode = lua_isboolean(L, 2);
830            }
831            s->open = 1;
832        }
833        lua_pushboolean(L,1);
834        return 1;
835    } else {
836        return 0;
837    }
838}
839
840static int pdfelib_stream_close(lua_State *L)
841{
842    pdfe_stream *s = pdfelib_aux_check_isstream(L, 1, close_error);
843    if (s && s->open > 0) {
844        ppstream_done(s->stream);
845        s->open = 0;
846        s->decode = 0;
847    }
848    return 0;
849}
850
851static int pdfelib_stream_read(lua_State *L)
852{
853    pdfe_stream *s = pdfelib_aux_check_isstream(L, 1, read_error);
854    if (s) {
855        size_t n = 0;
856        uint8_t *d = NULL;
857        if (s->open == 1) {
858            d = ppstream_first(s->stream, &n, s->decode);
859            s->open = 2;
860        } else if (s->open == 2) {
861            d = ppstream_next(s->stream, &n);
862        } else {
863            return 0;
864        }
865        lua_pushlstring(L, (const char *) d, n);
866        lua_pushinteger(L, (lua_Integer) n);
867        return 2;
868    } else {
869        return 0;
870    }
871}
872
873/*tex
874
875    There are two methods for opening a document: files and strings.
876
877    \starttyping
878    documentobject = open(filename)
879    documentobject = new(string,length)
880    \stoptyping
881
882    Closing happens with:
883
884    \starttyping
885    close(documentobject)
886    \stoptyping
887
888    When the \type {new} function gets a peudo filename as third argument, no user data will be
889    created but the stream is accessible as image.
890
891*/
892
893/*
894static int pdfelib_test(lua_State *L)
895{
896    const char *filename = luaL_checkstring(L, 1);
897    ppdoc *d = ppdoc_load(filename);
898    if (d) {
899        lua_pushboolean(L,1);
900        ppdoc_free(d);
901    } else {
902        lua_pushboolean(L,0);
903    }
904    return 1;
905}
906*/
907
908static int aux_pdfelib_open(lua_State *L, FILE *f)
909{
910    ppdoc *d = ppdoc_filehandle(f, 1);
911    if (d) {
912        pdfe_document *p = (pdfe_document *) lua_newuserdatauv(L, sizeof(pdfe_document), 0);
913     // luaL_getmetatable(L, PDFE_METATABLE_INSTANCE);
914        lua_get_metatablelua(pdfe_instance);
915        lua_setmetatable(L, -2);
916        p->document = d;
917        p->open = 1;
918        p->isfile = 1;
919        p->memstream = NULL;
920        return 1;
921    } else { 
922        return 0;
923    }
924}
925
926static int pdfelib_open(lua_State *L)
927{
928    const char *filename = luaL_checkstring(L, 1);
929    FILE *f = aux_utf8_fopen(filename, "rb");
930    if (f && aux_pdfelib_open(L, f)) {
931        return 1;
932    } else {
933     /* tex_normal_warning("pdfe lib", "no valid file handle"); */ /* the caller should handle it */
934        return 0;
935    }
936}
937
938static int pdfelib_openfile(lua_State *L)
939{
940    luaL_Stream *fs = ((luaL_Stream *) luaL_checkudata(L, 1, LUA_FILEHANDLE));
941    FILE *f = (fs->closef) ? fs->f : NULL;
942    if (f && aux_pdfelib_open(L, f)) {
943        /*tex We trick \LUA\ in believing the file is closed. */
944        fs->closef = NULL;
945        return 1;
946    } else {
947     /* tex_normal_warning("pdfe lib", "no valid file handle"); */ /* the caller should handle it */
948        return 0;
949    }
950}
951
952static int pdfelib_new(lua_State *L)
953{
954    size_t streamsize = 0;
955    const char *docstream = NULL;
956    switch (lua_type(L, 1)) {
957        case LUA_TSTRING:
958            docstream = lua_tolstring(L, 1, &streamsize);
959            if (! docstream) {
960                tex_normal_warning("pdfe lib", "invalid string");
961                return 0;
962            } else {
963                break;
964            }
965        case LUA_TLIGHTUSERDATA:
966            /*tex
967                The stream comes as a sequence of bytes. This could happen from a library (we used
968                this for swiglib gm output tests).
969            */
970            docstream = (const char *) lua_touserdata(L, 1);
971            if (! docstream) {
972                tex_normal_warning("pdfe lib", "invalid lightuserdata");
973                return 0;
974            } else {
975                break;
976            }
977        default:
978            tex_normal_warning("pdfe lib", "string or lightuserdata expected");
979            return 0;
980    }
981    streamsize = luaL_optinteger(L, 2, streamsize);
982    if (streamsize > 0) {
983        char *memstream = lmt_generic_malloc((unsigned) (streamsize + 1)); /* we have no hook into pdfe free */
984        if (memstream) {
985            ppdoc *d = NULL;
986            memcpy(memstream, docstream, (streamsize + 1));
987            memstream[streamsize] = '\0';
988            d = ppdoc_mem(memstream, streamsize);
989            if (d) {
990                pdfe_document *p = (pdfe_document *) lua_newuserdatauv(L, sizeof(pdfe_document), 0);
991             // luaL_getmetatable(L, PDFE_METATABLE_INSTANCE);
992                lua_get_metatablelua(pdfe_instance);
993                lua_setmetatable(L, -2);
994                p->document = d;
995                p->open = 1;
996                p->isfile = 0;
997                p->memstream = memstream;
998                return 1;
999            } else {
1000                tex_normal_warning("pdfe lib", "unable to handle stream");
1001            }
1002        } else {
1003            tex_normal_warning("pdfe lib", "not enough memory for new stream");
1004        }
1005    } else {
1006        tex_normal_warning("pdfe lib", "stream with size > 0 expected");
1007    }
1008    return 0;
1009}
1010
1011/*
1012
1013    There is no garbage collection needed as the library itself manages the objects. Normally
1014    objects don't take much space. Streams use buffers so (I assume) that they are not
1015    persistent. The only collector is in the parent object (the document).
1016
1017*/
1018
1019static int pdfelib_document_free(lua_State *L)
1020{
1021    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, free_error);
1022    if (p && p->open) {
1023        if (p->document) {
1024            ppdoc_free(p->document);
1025            p->document = NULL;
1026        }
1027        if (p->memstream) {
1028         /* pplib does this: xfree(p->memstream); */
1029            p->memstream = NULL;
1030        }
1031        p->open = 0;
1032    }
1033    return 0;
1034}
1035
1036static int pdfelib_close(lua_State *L)
1037{
1038    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, close_error);
1039    if (p && p->open) {
1040        if (p->document) {
1041            ppdoc_free(p->document);
1042            p->document = NULL;
1043        }
1044        if (p->memstream) {
1045         /* pplib does this: xfree(p->memstream); */
1046            p->memstream = NULL;
1047        }
1048        p->open = 0;
1049    }
1050    return 0;
1051}
1052
1053/*tex
1054
1055    A document is can be uncrypted with:
1056
1057    \starttyping
1058    status = unencrypt(documentobject,user,owner)
1059    \stoptyping
1060
1061    Instead of a password \type {nil} can be passed, so there are three possible useful combinations.
1062
1063*/
1064
1065static int pdfelib_unencrypt(lua_State *L)
1066{
1067    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, unencrypt_error);
1068    if (p) {
1069        size_t u = 0;
1070        size_t o = 0;
1071        const char* user = NULL;
1072        const char* owner = NULL;
1073        int top = lua_gettop(L);
1074        if (top > 1) {
1075            if (lua_type(L,2) == LUA_TSTRING) {
1076                user = lua_tolstring(L, 2, &u);
1077            } else {
1078                /*tex we're not too picky but normally it will be nil or false */
1079            }
1080            if (top > 2) {
1081                if (lua_type(L,3) == LUA_TSTRING) {
1082                    owner = lua_tolstring(L, 3, &o);
1083                } else {
1084                    /*tex we're not too picky but normally it will be nil or false */
1085                }
1086            }
1087            lua_pushinteger(L, (lua_Integer) ppdoc_crypt_pass(p->document, user, u, owner, o));
1088            return 1;
1089        }
1090    }
1091    lua_pushinteger(L, (lua_Integer) PPCRYPT_FAIL);
1092    return 1;
1093}
1094
1095/*tex
1096
1097    There are a couple of ways to get information about the document:
1098
1099    \starttyping
1100    n             = getsize       (documentobject)
1101    major, minor  = getversion    (documentobject)
1102    status        = getstatus     (documentobject)
1103    n             = getnofobjects (documentobject)
1104    n             = getnofpages   (documentobject)
1105    bytes, waste  = getmemoryusage(documentobject)
1106    \stoptyping
1107
1108*/
1109
1110static int pdfelib_getsize(lua_State *L)
1111{
1112    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_size_error);
1113    if (p) {
1114        lua_pushinteger(L, (lua_Integer) ppdoc_file_size(p->document));
1115        return 1;
1116    } else {
1117        return 0;
1118    }
1119}
1120
1121
1122static int pdfelib_getversion(lua_State *L)
1123{
1124    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_version_error);
1125    if (p) {
1126        int minor;
1127        int major = ppdoc_version_number(p->document, &minor);
1128        lua_pushinteger(L, (lua_Integer) major);
1129        lua_pushinteger(L, (lua_Integer) minor);
1130        return 2;
1131    } else {
1132        return 0;
1133    }
1134}
1135
1136static int pdfelib_getstatus(lua_State *L)
1137{
1138    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_status_error);
1139    if (p) {
1140        lua_pushinteger(L, (lua_Integer) ppdoc_crypt_status(p->document));
1141        return 1;
1142    } else {
1143        return 0;
1144    }
1145}
1146
1147static int pdfelib_getnofobjects(lua_State *L)
1148{
1149    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_n_of_objects_error);
1150    if (p) {
1151        lua_pushinteger(L, (lua_Integer) ppdoc_objects(p->document));
1152        return 1;
1153    } else {
1154        return 0;
1155    }
1156}
1157
1158static int pdfelib_getnofpages(lua_State *L)
1159{
1160    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_n_of_pages_error);
1161    if (p) {
1162        lua_pushinteger(L, (lua_Integer) ppdoc_page_count(p->document));
1163        return 1;
1164    } else {
1165        return 0;
1166    }
1167}
1168
1169static int pdfelib_getmemoryusage(lua_State *L)
1170{
1171    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_memory_usage_error);
1172    if (p) {
1173        size_t w = 0;
1174        size_t m = ppdoc_memory(p->document, &w);
1175        lua_pushinteger(L, (lua_Integer) m);
1176        lua_pushinteger(L, (lua_Integer) w);
1177        return 2;
1178    } else {
1179        return 0;
1180    }
1181}
1182
1183/*
1184    A specific page dictionary can be filtered with the next command. So, there is no need to parse
1185    the document page tree (with these \type {kids} arrays).
1186
1187    \starttyping
1188    dictionaryobject = getpage(documentobject,pagenumber)
1189    \stoptyping
1190
1191*/
1192
1193static int pdfelib_aux_pushpage(lua_State *L, ppdoc *d, int page)
1194{
1195    if ((page <= 0) || (page > ((int) ppdoc_page_count(d)))) {
1196        return 0;
1197    } else {
1198        ppref *pp = ppdoc_page(d, page);
1199        return pdfelib_aux_pushdictionaryonly(L, ppref_obj(pp)->dict);
1200    }
1201}
1202
1203static int pdfelib_getpage(lua_State *L)
1204{
1205    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_page_error);
1206    if (p) {
1207        return pdfelib_aux_pushpage(L, p->document, lmt_checkinteger(L, 2));
1208    } else {
1209        return 0;
1210    }
1211}
1212
1213static int pdfelib_aux_pushpages(lua_State *L, ppdoc *d)
1214{
1215    int i = 1;
1216    lua_createtable(L, (int) ppdoc_page_count(d), 0);
1217    /* pages[1..n] */
1218    for (ppref *r = ppdoc_first_page(d); r; r = ppdoc_next_page(d), ++i) {
1219        pdfelib_aux_pushdictionaryonly(L,ppref_obj(r)->dict);
1220        lua_rawseti(L, -2, i);
1221    }
1222    return 1 ;
1223}
1224
1225static int pdfelib_getpages(lua_State *L)
1226{
1227    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_pages_error);
1228    if (p) {
1229        return pdfelib_aux_pushpages(L, p->document);
1230    } else {
1231        return 0;
1232    }
1233}
1234
1235/*tex
1236
1237    The boundingbox (\type {MediaBox) and similar boxes can be available in a (page) doctionary but
1238    also in a parent object. Therefore a helper is available that does the (backtracked) lookup.
1239
1240    \starttyping
1241    { lx, ly, rx, ry } = getbox(dictionaryobject)
1242    \stoptyping
1243
1244*/
1245
1246static int pdfelib_getbox(lua_State *L)
1247{
1248    if (lua_gettop(L) > 1 && lua_type(L,2) == LUA_TSTRING) {
1249        pdfe_dictionary *p = pdfelib_aux_check_isdictionary(L, 1, get_box_error);
1250        if (p) {
1251            const char *key = lua_tostring(L, 2);
1252            pprect box = { 0, 0, 0, 0 };
1253            pprect *r = ppdict_get_box(p->dictionary, key, &box);
1254            if (r) {
1255                lua_createtable(L, 4, 0);
1256                lua_pushnumber(L, r->lx);
1257                lua_rawseti(L, -2, 1);
1258                lua_pushnumber(L, r->ly);
1259                lua_rawseti(L, -2, 2);
1260                lua_pushnumber(L, r->rx);
1261                lua_rawseti(L, -2, 3);
1262                lua_pushnumber(L, r->ry);
1263                lua_rawseti(L, -2, 4);
1264                return 1;
1265            }
1266        }
1267    }
1268    return 0;
1269}
1270
1271/*tex
1272
1273    This one is needed when you use the detailed getters and run into an object reference. The
1274    regular getters resolve this automatically.
1275
1276    \starttyping
1277    [dictionary|array|stream]object = getfromreference(referenceobject)
1278    \stoptyping
1279
1280*/
1281
1282static int pdfelib_getfromreference(lua_State *L)
1283{
1284    pdfe_reference *r = pdfelib_aux_check_isreference(L, 1, get_from_error);
1285    if (r && r->xref) {
1286        ppref *rr = ppxref_find(r->xref, (ppuint) r->onum);
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_getfromobject(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             ppobj *o = ppref_obj(rr);
1305             if (o) {
1306                 lua_pushinteger(L, (lua_Integer) o->type);
1307                 return 1 + pdfelib_aux_pushvalue(L, o);
1308             }
1309        }
1310    }
1311    return 0;
1312}
1313
1314static int pdfelib_getobjectrange(lua_State *L)
1315{
1316    pdfe_document *p = pdfelib_aux_check_isdocument(L, 1, get_from_error);
1317    if (p) {
1318        ppref *rr = ppxref_find(p->document->xref, lua_tointeger(L, 2));
1319        if (rr) {
1320             lua_pushinteger(L, (lua_Integer) rr->offset);
1321             lua_pushinteger(L, (lua_Integer) rr->length);
1322             return 2;
1323        }
1324    }
1325    return 0;
1326}
1327
1328/*tex
1329
1330    Here are some convenient getters:
1331
1332    \starttyping
1333    <string>         = getstring    (array|dict|ref,index|key)
1334    <integer>        = getinteger   (array|dict|ref,index|key)
1335    <number>         = getnumber    (array|dict|ref,index|key)
1336    <boolan>         = getboolean   (array|dict|ref,index|key)
1337    <string>         = getname      (array|dict|ref,index|key)
1338    <dictionary>     = getdictionary(array|dict|ref,index|key)
1339    <array>          = getarray     (array|dict|ref,index|key)
1340    <stream>, <dict> = getstream    (array|dict|ref,index|key)
1341    \stoptyping
1342
1343    We report issues when reasonable but are silent when it makes sense. We don't error on this
1344    because we expect the user code to act reasonable on a return value.
1345
1346*/
1347
1348static int pdfelib_valid_index(lua_State *L, void **p, int *t)
1349{
1350    *t = lua_type(L, 2);
1351    *p = lua_touserdata(L, 1);
1352    lua_settop(L, 2);
1353    if (! *p) {
1354        switch (*t) {
1355            case LUA_TSTRING:
1356                tex_normal_warning("pdfe lib", "lua <pdfe dictionary> expected");
1357                break;
1358            case LUA_TNUMBER:
1359                tex_normal_warning("pdfe lib", "lua <pdfe array> expected");
1360                break;
1361            default:
1362                tex_normal_warning("pdfe lib", "invalid arguments");
1363                break;
1364        }
1365        return 0;
1366    } else if (! lua_getmetatable(L, 1)) {
1367        tex_normal_warning("pdfe lib", "first argument should be a <pde array> or <pde dictionary>");
1368        return 0;
1369    } else {
1370        return 1;
1371    }
1372}
1373
1374static void pdfelib_invalid_index_warning(void)
1375{
1376    tex_normal_warning("pdfe lib", "second argument should be integer or string");
1377}
1378
1379/*tex
1380
1381    The direct fetcher returns the result or |NULL| when there is nothing found. The indirect
1382    fetcher passes a pointer to the target variable and returns success state.
1383
1384    The next two functions used to be macros but as we try to avoid large ones with much code, they
1385    are now functions.
1386
1387*/
1388
1389typedef void * (*pp_a_direct)   (void *a, size_t      index);
1390typedef void * (*pp_d_direct)   (void *d, const char *key);
1391typedef int    (*pp_a_indirect) (void *a, size_t      index, void **value);
1392typedef int    (*pp_d_indirect) (void *d, const char *key,   void **value);
1393
1394static int pdfelib_get_value_direct(lua_State *L, void **value, pp_d_direct get_d, pp_a_direct get_a)
1395{
1396    int t = 0;
1397    void *p = NULL;
1398    if (pdfelib_valid_index(L, &p, &t)) {
1399        switch (t) {
1400            case LUA_TSTRING:
1401                {
1402                    const char *key = lua_tostring(L, 2);
1403                    lua_get_metatablelua(pdfe_dictionary_instance);
1404                    if (lua_rawequal(L, -1, -2)) {
1405                        *value = get_d(((pdfe_dictionary *) p)->dictionary, key);
1406                        return 1;
1407                    } else {
1408                        lua_get_metatablelua(pdfe_reference_instance);
1409                        if (lua_rawequal(L, -1, -3)) {
1410                            ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL; \
1411                            ppobj *o = (r) ? ppref_obj(r) : NULL;
1412                            if (o && o->type == PPDICT) {
1413                                *value = get_d((ppdict *) o->dict, key);
1414                                return 1;
1415                            }
1416                        }
1417                    }
1418                }
1419                break;
1420            case LUA_TNUMBER:
1421                {
1422                    size_t index = lua_tointeger(L, 2);
1423                    lua_get_metatablelua(pdfe_array_instance);
1424                    if (lua_rawequal(L, -1, -2)) {
1425                        *value = get_a(((pdfe_array *) p)->array, index);
1426                        return 2;
1427                    } else {
1428                        lua_get_metatablelua(pdfe_reference_instance);
1429                        if (lua_rawequal(L, -1, -3)) {
1430                            ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL; \
1431                            ppobj *o = (r) ? ppref_obj(r) : NULL;
1432                            if (o && o->type == PPARRAY) {
1433                                *value = get_a((pparray *) o->array, index);
1434                                return 2;
1435                            }
1436                        }
1437                    }
1438                }
1439                break;
1440            default:
1441                pdfelib_invalid_index_warning();
1442                break;
1443        }
1444    }
1445    return 0;
1446}
1447
1448static int pdfelib_get_value_indirect(lua_State *L, void **value, pp_d_indirect get_d, pp_a_indirect get_a)
1449{
1450    int t = 0;
1451    void *p = NULL;
1452    if (pdfelib_valid_index(L, &p, &t)) {
1453        switch (t) {
1454            case LUA_TSTRING:
1455                {
1456                    const char *key = lua_tostring(L, 2);
1457                    lua_get_metatablelua(pdfe_dictionary_instance);
1458                    if (lua_rawequal(L, -1, -2)) {
1459                        return get_d(((pdfe_dictionary *) p)->dictionary, key, value);
1460                    } else {
1461                        lua_get_metatablelua(pdfe_reference_instance);
1462                        if (lua_rawequal(L, -1, -3)) {
1463                            ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL;
1464                            ppobj *o = (r) ? ppref_obj(r) : NULL;
1465                            if (o && o->type == PPDICT)
1466                                return get_d(o->dict, key, value);
1467                        }
1468                    }
1469                }
1470                break;
1471            case LUA_TNUMBER:
1472                {
1473                    size_t index = lua_tointeger(L, 2);
1474                    lua_get_metatablelua(pdfe_array_instance);
1475                    if (lua_rawequal(L, -1, -2)) {
1476                        return get_a(((pdfe_array *) p)->array, index, value);
1477                    } else {
1478                        lua_get_metatablelua(pdfe_reference_instance);
1479                        if (lua_rawequal(L, -1, -3)) {
1480                            ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL;
1481                            ppobj *o = (r) ? ppref_obj(r) : NULL;
1482                            if (o && o->type == PPARRAY)
1483                                return get_a(o->array, index, value);
1484                        }
1485                    }
1486                }
1487                break;
1488            default:
1489                pdfelib_invalid_index_warning();
1490                break;
1491        }
1492    }
1493    return 0;
1494}
1495
1496static int pdfelib_getstring(lua_State *L)
1497{
1498    if (lua_gettop(L) > 1) {
1499        ppstring *value = NULL;
1500        int okay = 0;
1501        int how = 0;
1502        if (lua_type(L, 3) == LUA_TBOOLEAN) {
1503            if (lua_toboolean(L, 3)) {
1504                how = 1;
1505            } else {
1506                how = 2;
1507            }
1508        }
1509        okay = pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_string, (void *) &pparray_rget_string);
1510        if (okay && value) {
1511            if (how == 1) {
1512                value = ppstring_decoded(value); /* we probably always need that */
1513            }
1514            /*tex This used to return one value but we made it \LUATEX\ compatible. */
1515            lua_pushlstring(L, ppstring_data(value), ppstring_size(value));
1516            if (how == 2) {
1517                lua_pushboolean(L, ppstring_hex(value));
1518                return 2;
1519            } else {
1520                return 1;
1521            }
1522        }
1523    }
1524    return 0;
1525}
1526
1527static int pdfelib_getinteger(lua_State *L)
1528{
1529    if (lua_gettop(L) > 1) {
1530        ppint value = 0;
1531        if (pdfelib_get_value_indirect(L, (void *) &value, (void *) &ppdict_rget_int, (void *) &pparray_rget_int)) {
1532            lua_pushinteger(L, (lua_Integer) value);
1533            return 1;
1534        }
1535    }
1536    return 0;
1537}
1538
1539static int pdfelib_getnumber(lua_State *L)
1540{
1541    if (lua_gettop(L) > 1) {
1542        ppnum value = 0;
1543        if (pdfelib_get_value_indirect(L, (void *) &value, (void *) &ppdict_rget_num, (void *) &pparray_rget_num)) {
1544            lua_pushnumber(L, value);
1545            return 1;
1546        }
1547    }
1548    return 0;
1549}
1550
1551static int pdfelib_getboolean(lua_State *L)
1552{
1553    if (lua_gettop(L) > 1) {
1554        int value = 0;
1555        if (pdfelib_get_value_indirect(L, (void *) &value, (void *) &ppdict_rget_bool, (void *) &pparray_rget_bool)) {
1556            lua_pushboolean(L, value);
1557            return 1;
1558        }
1559    }
1560    return 0;
1561}
1562
1563static int pdfelib_getname(lua_State *L)
1564{
1565    if (lua_gettop(L) > 1) {
1566        ppname *value = NULL;
1567        pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_name, (void *) &pparray_rget_name);
1568        if (value) {
1569            value = ppname_decoded(value) ;
1570            lua_pushlstring(L, ppname_data(value), ppname_size(value));
1571            return 1;
1572        }
1573    }
1574    return 0;
1575}
1576
1577static int pdfelib_getdictionary(lua_State *L)
1578{
1579    if (lua_gettop(L) > 1) {
1580        ppdict *value = NULL;
1581        pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_dict, (void *) &pparray_rget_dict);
1582        if (value) {
1583            return pdfelib_aux_pushdictionaryonly(L, value);
1584        }
1585    }
1586    return 0;
1587}
1588
1589static int pdfelib_getarray(lua_State *L)
1590{
1591    if (lua_gettop(L) > 1) {
1592        pparray *value = NULL;
1593        pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_array, (void *) &pparray_rget_array);
1594        if (value) {
1595            return pdfelib_aux_pusharrayonly(L, value);
1596        }
1597    }
1598    return 0;
1599}
1600
1601static int pdfelib_getstream(lua_State *L)
1602{
1603    if (lua_gettop(L) > 1) {
1604        ppobj *value = NULL;
1605        pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_obj, (void *) &pparray_rget_obj);
1606        if (value && value->type == PPSTREAM) {
1607            return pdfelib_aux_pushstreamonly(L, (ppstream *) value->stream);
1608        }
1609    }
1610    return 0;
1611}
1612
1613/*tex
1614
1615    The generic pushed that does a similar job as the previous getters acts upon
1616    the type.
1617
1618*/
1619
1620static int pdfelib_pushvalue(lua_State *L, ppobj *object)
1621{
1622    switch (object->type) {
1623        case PPNONE:
1624        case PPNULL:
1625            lua_pushnil(L);
1626            break;
1627        case PPBOOL:
1628            lua_pushboolean(L, (int) object->integer);
1629            break;
1630        case PPINT:
1631            lua_pushinteger(L, (lua_Integer) object->integer);
1632            break;
1633        case PPNUM:
1634            lua_pushnumber(L, (double) object->number);
1635            break;
1636        case PPNAME:
1637            {
1638                ppname *n = ppname_decoded(object->name) ;
1639                lua_pushlstring(L, ppname_data(n), ppname_size(n));
1640            }
1641            break;
1642        case PPSTRING:
1643            lua_pushlstring(L, ppstring_data(object->string), ppstring_size(object->string));
1644            break;
1645        case PPARRAY:
1646            return pdfelib_aux_pusharrayonly(L, object->array);
1647        case PPDICT:
1648            return pdfelib_aux_pushdictionary(L, object->dict);
1649        case PPSTREAM:
1650            return pdfelib_aux_pushstream(L, object->stream);
1651        case PPREF:
1652            pdfelib_aux_pushreference(L, object->ref);
1653            break;
1654        /*tex We get a funny message in clang about covering all cases. */
1655        /*
1656        default:
1657            lua_pushnil(L);
1658            break;
1659        */
1660    }
1661    return 1;
1662}
1663
1664/*tex
1665
1666    Finally we arrived at the acessors for the userdata objects. The use previously defined helpers.
1667
1668*/
1669
1670static int pdfelib_document_access(lua_State *L)
1671{
1672    if (lua_type(L, 2) == LUA_TSTRING) {
1673     // pdfe_document *p = (pdfe_document *) lua_touserdata(L, 1);
1674        const char *s = lua_tostring(L, 2);
1675        if (lua_key_eq(s, catalog) || lua_key_eq(s, Catalog)) {
1676         // return pdfelib_aux_pushdictionaryonly(L, ppdoc_catalog(p->document));
1677            return pdfelib_getcatalog(L);
1678        } else if (lua_key_eq(s, info) || lua_key_eq(s, Info)) {
1679         // return pdfelib_aux_pushdictionaryonly(L, ppdoc_info(p->document));
1680            return pdfelib_getinfo(L);
1681        } else if (lua_key_eq(s, trailer) || lua_key_eq(s, Trailer)) {
1682         // return pdfelib_aux_pushdictionaryonly(L, ppdoc_trailer(p->document));
1683            return pdfelib_getcatalog(L);
1684        } else if (lua_key_eq(s, pages) || lua_key_eq(s, Pages)) {
1685         // return pdfelib_aux_pushpages(L, p->document);
1686            return pdfelib_getpages(L);
1687        }
1688    }
1689    return 0;
1690}
1691
1692static int pdfelib_array_access(lua_State *L)
1693{
1694    if (lua_type(L, 2) == LUA_TNUMBER) {
1695        pdfe_array *p = (pdfe_array *) lua_touserdata(L, 1);
1696        ppint index = lua_tointeger(L, 2) - 1;
1697        ppobj *o = pparray_rget_obj(p->array, index);
1698        if (o) {
1699            return pdfelib_pushvalue(L, o);
1700        }
1701    }
1702    return 0;
1703}
1704
1705static int pdfelib_dictionary_access(lua_State *L)
1706{
1707    pdfe_dictionary *p = (pdfe_dictionary *) lua_touserdata(L, 1);
1708    switch (lua_type(L, 2)) {
1709        case LUA_TSTRING:
1710            {
1711                const char *key = lua_tostring(L, 2);
1712                ppobj *o = ppdict_rget_obj(p->dictionary, key);
1713                if (o) {
1714                    return pdfelib_pushvalue(L, o);
1715                }
1716            }
1717            break;
1718        case LUA_TNUMBER:
1719            {
1720                ppint index = lua_tointeger(L, 2) - 1;
1721                ppobj *o = ppdict_at(p->dictionary, index);
1722                if (o) {
1723                    return pdfelib_pushvalue(L, o);
1724                }
1725            }
1726            break;
1727    }
1728    return 0;
1729}
1730
1731static int pdfelib_stream_access(lua_State *L)
1732{
1733    pdfe_stream *p = (pdfe_stream *) lua_touserdata(L, 1);
1734    switch (lua_type(L, 2)) {
1735        case LUA_TSTRING:
1736            {
1737                const char *key = lua_tostring(L, 2);
1738                ppobj *o = ppdict_rget_obj(p->stream->dict, key);
1739                if (o) {
1740                    return pdfelib_pushvalue(L, o);
1741                }
1742            }
1743            break;
1744        case LUA_TNUMBER:
1745            {
1746                ppint index = lua_tointeger(L, 2) - 1;
1747                ppobj *o = ppdict_at(p->stream->dict, index);
1748                if (o) {
1749                    return pdfelib_pushvalue(L, o);
1750                }
1751            }
1752            break;
1753    }
1754    return 0;
1755}
1756
1757/*tex
1758
1759    The length metamethods are defined last.
1760
1761*/
1762
1763static int pdfelib_array_size(lua_State *L)
1764{
1765    pdfe_array *p = (pdfe_array *) lua_touserdata(L, 1);
1766    lua_pushinteger(L, (lua_Integer) p->array->size);
1767    return 1;
1768}
1769
1770static int pdfelib_dictionary_size(lua_State *L)
1771{
1772    pdfe_dictionary *p = (pdfe_dictionary *) lua_touserdata(L, 1);
1773    lua_pushinteger(L, (lua_Integer) p->dictionary->size);
1774    return 1;
1775}
1776
1777static int pdfelib_stream_size(lua_State *L)
1778{
1779    pdfe_stream *p = (pdfe_stream *) lua_touserdata(L, 1);
1780    lua_pushinteger(L, (lua_Integer) p->stream->dict->size);
1781    return 1;
1782}
1783
1784/*tex
1785
1786    We now initialize the main interface. We might add few more informational helpers but this is
1787    it.
1788
1789*/
1790
1791static int pdfelib_getencodingvalues(lua_State *L)
1792{
1793    lua_createtable(L, 2, 6);
1794    lua_set_string_by_index(L, PPSTRING_PLAIN,    "plain"  );    /*  0 */
1795    lua_set_string_by_index(L, PPSTRING_ENCODED,  "encoded");    /*  1 */ 
1796    lua_set_string_by_index(L, PPSTRING_DECODED,  "decoded");    /*  2  */
1797 /* lua_set_string_by_index(L, PPSTRING_EXEC,     "exec"   ); */ /*  4 */
1798    lua_set_string_by_index(L, PPSTRING_BASE16,   "base16" );    /*  8 */
1799    lua_set_string_by_index(L, PPSTRING_BASE85,   "base85" );    /* 16 */
1800    lua_set_string_by_index(L, PPSTRING_UTF16BE,  "utf16be");    /* 32 */
1801    lua_set_string_by_index(L, PPSTRING_UTF16LE,  "utf16le");    /* 64 */
1802    return 1;
1803}
1804
1805static int pdfelib_getstatusvalues(lua_State *L)
1806{
1807    lua_createtable(L, 1, 3);
1808    lua_set_string_by_index(L, PPCRYPT_PASS, "is protected");
1809    lua_set_string_by_index(L, PPCRYPT_FAIL, "failed to open");
1810    lua_set_string_by_index(L, PPCRYPT_NONE, "not encrypted");
1811    lua_set_string_by_index(L, PPCRYPT_DONE, "is decrypted"); 
1812    return 1;
1813}
1814
1815static int pdfelib_getfieldtypes(lua_State *L)
1816{
1817    lua_createtable(L, 10, 1);
1818    lua_set_string_by_index(L, PPNONE,   "none");
1819    lua_set_string_by_index(L, PPNULL,   "null");
1820    lua_set_string_by_index(L, PPBOOL,   "boolean");
1821    lua_set_string_by_index(L, PPINT,    "integer");
1822    lua_set_string_by_index(L, PPNUM,    "number");
1823    lua_set_string_by_index(L, PPNAME,   "name");
1824    lua_set_string_by_index(L, PPSTRING, "string");
1825    lua_set_string_by_index(L, PPARRAY,  "array");
1826    lua_set_string_by_index(L, PPDICT,   "dictionary");
1827    lua_set_string_by_index(L, PPSTREAM, "stream");
1828    lua_set_string_by_index(L, PPREF,    "reference");
1829    return 1;
1830}
1831
1832// PPDOC_ALLOW_PRINT
1833// PPDOC_ALLOW_MODIFY
1834// PPDOC_ALLOW_COPY
1835// PPDOC_ALLOW_ANNOTS
1836// PPDOC_ALLOW_EXTRACT
1837// PPDOC_ALLOW_ASSEMBLY
1838// # define PPDOC_ALLOW_PRINT_HIRES
1839
1840static const struct luaL_Reg pdfelib_function_list[] = {
1841    /* management */
1842    { "type",               pdfelib_type              },
1843    { "open",               pdfelib_open              },
1844    { "openfile",           pdfelib_openfile          },
1845    { "new",                pdfelib_new               },
1846    { "close",              pdfelib_close             },
1847    { "unencrypt",          pdfelib_unencrypt         },
1848    { "getencodingvalues",  pdfelib_getencodingvalues },
1849    { "getstatusvalues",    pdfelib_getstatusvalues   },
1850    { "getfieldtypes",      pdfelib_getfieldtypes     },
1851    /* statistics */
1852    { "getversion",         pdfelib_getversion        },
1853    { "getstatus",          pdfelib_getstatus         },
1854    { "getsize",            pdfelib_getsize           },
1855    { "getnofobjects",      pdfelib_getnofobjects     },
1856    { "getnofpages",        pdfelib_getnofpages       },
1857    { "getmemoryusage",     pdfelib_getmemoryusage    },
1858    /* getters */
1859    { "getcatalog",         pdfelib_getcatalog        },
1860    { "gettrailer",         pdfelib_gettrailer        },
1861    { "getinfo",            pdfelib_getinfo           },
1862    { "getpermissions",     pdfelib_getpermissions    },
1863    { "getpage",            pdfelib_getpage           },
1864    { "getpages",           pdfelib_getpages          },
1865    { "getbox",             pdfelib_getbox            },
1866    { "getfromreference",   pdfelib_getfromreference  },
1867    { "getfromdictionary",  pdfelib_getfromdictionary },
1868    { "getfromarray",       pdfelib_getfromarray      },
1869    { "getfromstream",      pdfelib_getfromstream     },
1870    /* handy too */
1871    { "getfromobject",      pdfelib_getfromobject     },
1872    { "getobjectrange",     pdfelib_getobjectrange    },
1873    /* collectors */
1874    { "dictionarytotable",  pdfelib_dictionarytotable },
1875    { "arraytotable",       pdfelib_arraytotable      },
1876    { "pagestotable",       pdfelib_pagestotable      },
1877    /* more getters */
1878    { "getstring",          pdfelib_getstring         },
1879    { "getinteger",         pdfelib_getinteger        },
1880    { "getnumber",          pdfelib_getnumber         },
1881    { "getboolean",         pdfelib_getboolean        },
1882    { "getname",            pdfelib_getname           },
1883    { "getdictionary",      pdfelib_getdictionary     },
1884    { "getarray",           pdfelib_getarray          },
1885    { "getstream",          pdfelib_getstream         },
1886    /* streams */
1887    { "readwholestream",    pdfelib_stream_readwhole  },
1888    /* not really needed */
1889    { "openstream",         pdfelib_stream_open       },
1890    { "readfromstream",     pdfelib_stream_read       },
1891    { "closestream",        pdfelib_stream_close      },
1892    /* only for me, a test hook */
1893 /* { "test",               pdfelib_test              }, */
1894    /* done */
1895    { NULL,                 NULL                      }
1896};
1897
1898/*tex
1899
1900    The user data metatables are defined as follows. Watch how only the document needs a garbage
1901    collector.
1902
1903*/
1904
1905static const struct luaL_Reg pdfelib_instance_metatable[] = {
1906    { "__tostring", pdfelib_document_tostring },
1907    { "__gc",       pdfelib_document_free     },
1908    { "__index",    pdfelib_document_access   },
1909    { NULL,         NULL                      },
1910};
1911
1912static const struct luaL_Reg pdfelib_dictionary_metatable[] = {
1913    { "__tostring", pdfelib_dictionary_tostring },
1914    { "__index",    pdfelib_dictionary_access   },
1915    { "__len",      pdfelib_dictionary_size     },
1916    { NULL,         NULL                        },
1917};
1918
1919static const struct luaL_Reg pdfelib_array_metatable[] = {
1920    { "__tostring", pdfelib_array_tostring },
1921    { "__index",    pdfelib_array_access   },
1922    { "__len",      pdfelib_array_size     },
1923    { NULL,         NULL                   },
1924};
1925
1926static const struct luaL_Reg pdfelib_stream_metatable[] = {
1927    { "__tostring", pdfelib_stream_tostring  },
1928    { "__index",    pdfelib_stream_access    },
1929    { "__len",      pdfelib_stream_size      },
1930    { "__call",     pdfelib_stream_readwhole },
1931    { NULL,         NULL                     },
1932};
1933
1934static const struct luaL_Reg pdfelib_reference_metatable[] = {
1935    { "__tostring", pdfelib_reference_tostring },
1936    { NULL,         NULL                       },
1937};
1938
1939/*tex
1940
1941    Finally we have arrived at the main initialiser that will be called as part of \LUATEX's
1942    initializer.
1943
1944*/
1945
1946/*tex
1947
1948    Here we hook in the error handler.
1949
1950*/
1951
1952static void pdfelib_message(const char *message, void *alien)
1953{
1954    (void) (alien);
1955    tex_normal_warning("pdfe", message);
1956}
1957
1958int luaopen_pdfe(lua_State *L)
1959{
1960    /*tex First the four userdata object get their metatables defined. */
1961
1962    luaL_newmetatable(L, PDFE_METATABLE_DICTIONARY);
1963    luaL_setfuncs(L, pdfelib_dictionary_metatable, 0);
1964
1965    luaL_newmetatable(L, PDFE_METATABLE_ARRAY);
1966    luaL_setfuncs(L, pdfelib_array_metatable, 0);
1967
1968    luaL_newmetatable(L, PDFE_METATABLE_STREAM);
1969    luaL_setfuncs(L, pdfelib_stream_metatable, 0);
1970
1971    luaL_newmetatable(L, PDFE_METATABLE_REFERENCE);
1972    luaL_setfuncs(L, pdfelib_reference_metatable, 0);
1973
1974    /*tex Then comes the main (document) metatable: */
1975
1976    luaL_newmetatable(L, PDFE_METATABLE_INSTANCE);
1977    luaL_setfuncs(L, pdfelib_instance_metatable, 0);
1978
1979    /*tex Last the library opens up itself to the world. */
1980
1981    lua_newtable(L);
1982    luaL_setfuncs(L, pdfelib_function_list, 0);
1983
1984    pplog_callback(pdfelib_message, stderr);
1985
1986    return 1;
1987}
1988