diff options
Diffstat (limited to 'source/luametatex/source/luarest/lmtpdfelib.c')
-rw-r--r-- | source/luametatex/source/luarest/lmtpdfelib.c | 1850 |
1 files changed, 1850 insertions, 0 deletions
diff --git a/source/luametatex/source/luarest/lmtpdfelib.c b/source/luametatex/source/luarest/lmtpdfelib.c new file mode 100644 index 000000000..b22626749 --- /dev/null +++ b/source/luametatex/source/luarest/lmtpdfelib.c @@ -0,0 +1,1850 @@ +/* + See license.txt in the root of this project. +*/ + +/*tex + + This file hosts the encapsulated \PDF\ support code used for inclusion and access from \LUA. + +*/ + +# include "luametatex.h" + +// # define PDFE_METATABLE_INSTANCE "pdfe.instance" +// # define PDFE_METATABLE_DICTIONARY "pdfe.dictionary" +// # define PDFE_METATABLE_ARRAY "pdfe.array" +// # define PDFE_METATABLE_STREAM "pdfe.stream" +// # define PDFE_METATABLE_REFERENCE "pdfe.reference" + +# include "../libraries/pplib/pplib.h" + +/*tex + + We start with some housekeeping. Dictionaries, arrays, streams and references get userdata, + while strings, names, integers, floats and booleans become regular \LUA\ objects. We need to + define a few metatable identifiers too. + +*/ + +typedef struct pdfe_document { + ppdoc *document; + int open; + int isfile; + char *memstream; + int pages; + int index; +} pdfe_document ; + +typedef struct pdfe_dictionary { + ppdict *dictionary; +} pdfe_dictionary; + +typedef struct pdfe_array { + pparray *array; +} pdfe_array; + +typedef struct pdfe_stream { + ppstream *stream; + int decode; + int open; +} pdfe_stream; + +typedef struct pdfe_reference { + /* ppref *reference; */ + ppxref *xref; + int onum; +} pdfe_reference; + +/*tex + + We need to check if we have the right userdata. A similar warning is issued when encounter a + problem. We don't exit. + +*/ + +static void pdfe_invalid_object_warning(const char *detail) +{ + tex_formatted_warning("pdfe lib", "lua <pdfe %s> expected",detail); +} + +/* todo: use luaL_checkudata */ + +static pdfe_document *pdfelib_aux_check_isdocument(lua_State *L, int n) +{ + pdfe_document *p = (pdfe_document *) lua_touserdata(L, n); + if (p && lua_getmetatable(L, n)) { + lua_get_metatablelua(pdfe_instance); + if (! lua_rawequal(L, -1, -2)) { + p = NULL; + } + lua_pop(L, 2); + if (p) { + return p; + } + } + pdfe_invalid_object_warning("document"); + return NULL; +} + +static pdfe_dictionary *pdfelib_aux_check_isdictionary(lua_State *L, int n) +{ + pdfe_dictionary *p = (pdfe_dictionary *) lua_touserdata(L, n); + if (p && lua_getmetatable(L, n)) { + lua_get_metatablelua(pdfe_dictionary); + if (! lua_rawequal(L, -1, -2)) { + p = NULL; + } + lua_pop(L, 2); + if (p) { + return p; + } + } + pdfe_invalid_object_warning("dictionary"); + return NULL; +} + +static pdfe_array *pdfelib_aux_check_isarray(lua_State *L, int n) +{ + pdfe_array *p = (pdfe_array *) lua_touserdata(L, n); + if (p && lua_getmetatable(L, n)) { + lua_get_metatablelua(pdfe_array); + if (! lua_rawequal(L, -1, -2)) { + p = NULL; + } + lua_pop(L, 2); + if (p) { + return p; + } + } + pdfe_invalid_object_warning("array"); + return NULL; +} + +static pdfe_stream *pdfelib_aux_check_isstream(lua_State *L, int n) +{ + pdfe_stream *p = (pdfe_stream *) lua_touserdata(L, n); + if (p && lua_getmetatable(L, n)) { + lua_get_metatablelua(pdfe_stream); + if (! lua_rawequal(L, -1, -2)) { + p = NULL; + } + lua_pop(L, 2); + if (p) { + return p; + } + } + pdfe_invalid_object_warning("stream"); + return NULL; +} + +static pdfe_reference *pdfelib_aux_check_isreference(lua_State *L, int n) +{ + pdfe_reference *p = (pdfe_reference *) lua_touserdata(L, n); + if (p && lua_getmetatable(L, n)) { + lua_get_metatablelua(pdfe_reference); + if (! lua_rawequal(L, -1, -2)) { + p = NULL; + } + lua_pop(L, 2); + if (p) { + return p; + } + } + pdfe_invalid_object_warning("reference"); + return NULL; +} + +/*tex + + Reporting the type of a userdata is just a sequence of tests till we find the right one. We + return nothing is it is no pdfe type. + + \starttyping + t = pdfe.type(<pdfe document|dictionary|array|reference|stream>) + \stoptyping + +*/ + +/* +# define check_type(field,meta,name) do { \ + lua_get_metatablelua(meta); \ + if (lua_rawequal(L, -1, -2)) { \ + lua_pushstring(L, name); \ + return 1; \ + } \ + lua_pop(L, 1); \ +} while (0) + +static int pdfelib_type(lua_State *L) +{ + void *p = lua_touserdata(L, 1); + if (p && lua_getmetatable(L, 1)) { + check_type(document, pdfe_instance, PDFE_METATABLE_INSTANCE); + check_type(dictionary, pdfe_dictionary, PDFE_METATABLE_DICTIONARY); + check_type(array, pdfe_array, PDFE_METATABLE_ARRAY); + check_type(reference, pdfe_reference, PDFE_METATABLE_REFERENCE); + check_type(stream, pdfe_stream, PDFE_METATABLE_STREAM); + } + return 0; +} +*/ + +# define check_type(field,meta) do { \ + lua_get_metatablelua(meta); \ + if (lua_rawequal(L, -1, -2)) { \ + lua_push_key(meta); \ + return 1; \ + } \ + lua_pop(L, 1); \ +} while (0) + +static int pdfelib_type(lua_State *L) +{ + void *p = lua_touserdata(L, 1); + if (p && lua_getmetatable(L, 1)) { + check_type(document, pdfe_instance); + check_type(dictionary, pdfe_dictionary); + check_type(array, pdfe_array); + check_type(reference, pdfe_reference); + check_type(stream, pdfe_stream); + } + return 0; +} + +/*tex + + The \type {tostring} metamethods are similar and report a pdfe type plus a pointer value, as is + rather usual in \LUA. I ditched the macro that defined them and are now verbose. + +*/ + +static int pdfelib_document_tostring(lua_State *L) { + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + lua_pushfstring(L, "<pdfe.document %p>", p->document); + return 1; + } else { + return 0; + } +} + +static int pdfelib_dictionary_tostring(lua_State *L) { + pdfe_dictionary *p = pdfelib_aux_check_isdictionary(L, 1); + if (p) { + lua_pushfstring(L, "<pdfe.dictionary %p>", p->dictionary); + return 1; + } else { + return 0; + } +} + +static int pdfelib_array_tostring(lua_State *L) { + pdfe_array *p = pdfelib_aux_check_isarray(L, 1); + if (p) { + lua_pushfstring(L, "<pdfe.array %p>", p->array); + return 1; + } else { + return 0; + } +} + +static int pdfelib_stream_tostring(lua_State *L) { + pdfe_stream *p = pdfelib_aux_check_isstream(L, 1); + if (p) { + lua_pushfstring(L, "<pdfe.stream %p>", p->stream); + return 1; + } else { + return 0; + } +} + +static int pdfelib_reference_tostring(lua_State *L) { + pdfe_reference *p = pdfelib_aux_check_isreference(L, 1); + if (p) { + lua_pushfstring(L, "<pdfe.reference %d>", p->onum); + return 1; + } else { + return 0; + } +} + +/*tex + + The pushers look rather similar. We have two variants, one that just pushes the object, and + another that also pushes some extra information. + +*/ + +inline static void pdfe_push_dictionary(lua_State *L, ppdict *dictionary) +{ + pdfe_dictionary *d = (pdfe_dictionary *) lua_newuserdatauv(L, sizeof(pdfe_dictionary), 0); + luaL_getmetatable(L, PDFE_METATABLE_DICTIONARY); + lua_setmetatable(L, -2); + d->dictionary = dictionary; +} + +static int pdfelib_aux_pushdictionary(lua_State *L, ppdict *dictionary) +{ + if (dictionary) { + pdfe_push_dictionary(L, dictionary); + lua_pushinteger(L, (lua_Integer) dictionary->size); + return 2; + } else { + return 0; + } +} + +static int pdfelib_aux_pushdictionaryonly(lua_State *L, ppdict *dictionary) +{ + if (dictionary) { + pdfe_push_dictionary(L, dictionary); + return 1; + } else { + return 0; + } +} + +inline static void pdfe_push_array(lua_State *L, pparray *array) +{ + pdfe_array *a = (pdfe_array *) lua_newuserdatauv(L, sizeof(pdfe_array), 0); + luaL_getmetatable(L, PDFE_METATABLE_ARRAY); + lua_setmetatable(L, -2); + a->array = array; +} + +static int pdfelib_aux_pusharray(lua_State *L, pparray *array) +{ + if (array) { + pdfe_push_array(L, array); + lua_pushinteger(L, (lua_Integer) array->size); + return 2; + } else { + return 0; + } +} + +static int pdfelib_aux_pusharrayonly(lua_State *L, pparray *array) +{ + if (array) { + pdfe_push_array(L, array); + return 1; + } else { + return 0; + } +} + +inline static void pdfe_push_stream(lua_State *L, ppstream *stream) +{ + pdfe_stream *s = (pdfe_stream *) lua_newuserdatauv(L, sizeof(pdfe_stream), 0); + luaL_getmetatable(L, PDFE_METATABLE_STREAM); + lua_setmetatable(L, -2); + s->stream = stream; + s->open = 0; + s->decode = 0; +} + +static int pdfelib_aux_pushstream(lua_State *L, ppstream *stream) +{ + if (stream) { + pdfe_push_stream(L, stream); + if (pdfelib_aux_pushdictionary(L, stream->dict) > 0) { + return 3; + } else { + return 1; + } + } else { + return 0; + } +} + +static int pdfelib_aux_pushstreamonly(lua_State *L, ppstream *stream) +{ + if (stream) { + pdfe_push_stream(L, stream); + if (pdfelib_aux_pushdictionaryonly(L, stream->dict) > 0) { + return 2; + } else { + return 1; + } + } else { + return 0; + } +} + +inline static void pdfe_push_reference(lua_State *L, ppref *reference) +{ + pdfe_reference *r = (pdfe_reference *) lua_newuserdatauv(L, sizeof(pdfe_reference), 0); + luaL_getmetatable(L, PDFE_METATABLE_REFERENCE); + lua_setmetatable(L, -2); + r->xref = reference->xref; + r->onum = (int) reference->number; + } + +static int pdfelib_aux_pushreference(lua_State *L, ppref *reference) +{ + if (reference && reference->number != 0) { + pdfe_push_reference(L, reference); + lua_pushinteger(L, (lua_Integer) reference->number); + return 2; + } else { + return 0; + } +} + +/*tex + + The next function checks for the type and then pushes the matching data on the stack. + + \starttabulate[|c|l|l|l|] + \BC type \BC meaning \BC value \BC detail \NC \NR + \NC \type {0} \NC none \NC nil \NC \NC \NR + \NC \type {1} \NC null \NC nil \NC \NC \NR + \NC \type {2} \NC boolean \NC boolean \NC \NC \NR + \NC \type {3} \NC boolean \NC integer \NC \NC \NR + \NC \type {4} \NC number \NC float \NC \NC \NR + \NC \type {5} \NC name \NC string \NC \NC \NR + \NC \type {6} \NC string \NC string \NC type \NC \NR + \NC \type {7} \NC array \NC arrayobject \NC size \NC \NR + \NC \type {8} \NC dictionary \NC dictionaryobject \NC size \NC \NR + \NC \type {9} \NC stream \NC streamobject \NC dictionary size \NC \NR + \NC \type {10} \NC reference \NC integer \NC \NC \NR + \LL + \stoptabulate + + A name and string can be distinguished by the extra type value that a string has. + +*/ + +static int pdfelib_aux_pushvalue(lua_State *L, ppobj *object) +{ + switch (object->type) { + case PPNONE: + case PPNULL: + lua_pushnil(L); + return 1; + case PPBOOL: + lua_pushboolean(L, (int) object->integer); + return 1; + case PPINT: + lua_pushinteger(L, (lua_Integer) object-> integer); + return 1; + case PPNUM: + lua_pushnumber(L, (double) object->number); + return 1; + case PPNAME: + { + ppname *n = ppname_decoded(object->name) ; + lua_pushlstring(L, ppname_data(n), ppname_size(n)); + return 1; + } + case PPSTRING: + lua_pushlstring(L, ppstring_data(object->string), ppstring_size(object->string)); + lua_pushboolean(L, ppstring_hex(object->string)); + return 2; + case PPARRAY: + return pdfelib_aux_pusharray(L, object->array); + case PPDICT: + return pdfelib_aux_pushdictionary(L, object->dict); + case PPSTREAM: + return pdfelib_aux_pushstream(L, object->stream); + case PPREF: + return pdfelib_aux_pushreference(L, object->ref); + } + return 0; +} + +/*tex + + We need to start someplace when we traverse a document's tree. There are three places: + + \starttyping + catalogdictionary = getcatalog(documentobject) + trailerdictionary = gettrailer(documentobject) + infodictionary = getinfo (documentobject) + \stoptyping + +*/ + +static int pdfelib_getcatalog(lua_State *L) +{ + pdfe_document* p = pdfelib_aux_check_isdocument (L, 1); + if (p) { + return pdfelib_aux_pushdictionaryonly (L, ppdoc_catalog (p->document)); + } else { + return 0; + } +} + +static int pdfelib_gettrailer(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + return pdfelib_aux_pushdictionaryonly (L, ppdoc_trailer (p->document)); + } else { + return 0; + } +} + +static int pdfelib_getinfo(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + return pdfelib_aux_pushdictionaryonly (L, ppdoc_info (p->document)); + } else { + return 0; + } +} + +/*tex + + We have three more helpers. + + \starttyping + [key,] type, value, detail = getfromdictionary(dictionaryobject,name|index) + type, value, detail = getfromarray (arrayobject,index) + [key,] type, value, detail = getfromstream (streamobject,name|index) + \stoptyping + +*/ + +static int pdfelib_getfromarray(lua_State *L) +{ + pdfe_array *a = pdfelib_aux_check_isarray(L, 1); + if (a) { + unsigned int index = lmt_checkinteger(L, 2) - 1; + if (index < a->array->size) { + ppobj *object = pparray_at(a->array,index); + if (object) { + lua_pushinteger(L, (lua_Integer) object->type); + return 1 + pdfelib_aux_pushvalue(L, object); + } + } + } + return 0; +} + +static int pdfelib_getfromdictionary(lua_State *L) +{ + pdfe_dictionary *d = pdfelib_aux_check_isdictionary(L, 1); + if (d) { + if (lua_type(L, 2) == LUA_TSTRING) { + const char *name = luaL_checkstring(L, 2); + ppobj *object = ppdict_get_obj(d->dictionary, name); + if (object) { + lua_pushinteger(L, (lua_Integer) object->type); + return 1 + pdfelib_aux_pushvalue(L, object); + } + } else { + unsigned int index = lmt_checkinteger(L, 2) - 1; + if (index < d->dictionary->size) { + ppobj *object = ppdict_at(d->dictionary,index); + if (object) { + ppname *key = ppname_decoded(ppdict_key(d->dictionary, index)); + lua_pushlstring(L, ppname_data(key), ppname_size(key)); + lua_pushinteger(L, (lua_Integer) object->type); + return 2 + pdfelib_aux_pushvalue(L, object); + } + } + } + } + return 0; +} + +static int pdfelib_getfromstream(lua_State *L) +{ + pdfe_stream *s = (pdfe_stream *) lua_touserdata(L, 1); + // pdfe_stream *s = check_isstream(L, 1); + if (s) { + ppdict *d = s->stream->dict; + if (lua_type(L, 2) == LUA_TSTRING) { + const char *name = luaL_checkstring(L, 2); + ppobj *object = ppdict_get_obj(d, name); + if (object) { + lua_pushinteger(L, (lua_Integer) object->type); + return 1 + pdfelib_aux_pushvalue(L, object); + } + } else { + unsigned int index = lmt_checkinteger(L, 2) - 1; + if (index < d->size) { + ppobj *object = ppdict_at(d, index); + if (object) { + ppname *key = ppname_decoded(ppdict_key(d, index)); + lua_pushlstring(L, ppname_data(key), ppname_size(key)); + lua_pushinteger(L, (lua_Integer) object->type); + return 2 + pdfelib_aux_pushvalue(L, object); + } + } + } + } + return 0; +} + +/*tex + + An indexed table with all entries in an array can be fetched with:: + + \starttyping + t = arraytotable(arrayobject) + \stoptyping + + An hashed table with all entries in an dictionary can be fetched with:: + + \starttyping + t = dictionarytotable(arrayobject) + \stoptyping + +*/ + +static void pdfelib_totable(lua_State *L, ppobj *object, int flat) +{ + int n = pdfelib_aux_pushvalue(L, object); + if (flat && n < 2) { + return; + } else { + /* [value] [extra] [more] */ + lua_createtable(L, n + 1, 0); + if (n == 1) { + /* value { nil, nil } */ + lua_insert(L, -2); + /* { nil, nil } value */ + lua_rawseti(L, -2, 2); + /* { nil , value } */ + } else if (n == 2) { + /* value extra { nil, nil, nil } */ + lua_insert(L, -3); + /* { nil, nil, nil } value extra */ + lua_rawseti(L, -3, 3); + /* { nil, nil, extra } value */ + lua_rawseti(L, -2, 2); + /* { nil, value, extra } */ + } else if (n == 3) { + /* value extra more { nil, nil, nil, nil } */ + lua_insert(L, -4); + /* { nil, nil, nil, nil, nil } value extra more */ + lua_rawseti(L, -4, 4); + /* { nil, nil, nil, more } value extra */ + lua_rawseti(L, -3, 3); + /* { nil, nil, extra, more } value */ + lua_rawseti(L, -2, 2); + /* { nil, value, extra, more } */ + } + lua_pushinteger(L, (lua_Integer) object->type); + /* { nil, [value], [extra], [more] } type */ + lua_rawseti(L, -2, 1); + /* { type, [value], [extra], [more] } */ + } +} + +static int pdfelib_arraytotable(lua_State *L) +{ + pdfe_array *a = pdfelib_aux_check_isarray(L, 1); + if (a) { + int flat = lua_isboolean(L, 2); + int j = 0; + lua_createtable(L, (int) a->array->size, 0); + /* table */ + for (unsigned int i = 0; i < a->array->size; i++) { + ppobj *object = pparray_at(a->array,i); + if (object) { + pdfelib_totable(L, object,flat); + /* table { type, [value], [extra], [more] } */ + lua_rawseti(L, -2, ++j); + /* table[i] = { type, [value], [extra], [more] } */ + } + } + return 1; + } else { + return 0; + } +} + +static int pdfelib_dictionarytotable(lua_State *L) +{ + pdfe_dictionary *d = pdfelib_aux_check_isdictionary(L, 1); + if (d) { + int flat = lua_isboolean(L, 2); + lua_createtable(L, 0, (int) d->dictionary->size); + /* table */ + for (unsigned int i = 0; i < d->dictionary->size; i++) { + ppobj *object = ppdict_at(d->dictionary, i); + if (object) { + ppname *key = ppname_decoded(ppdict_key(d->dictionary, i)); + lua_pushlstring(L, ppname_data(key), ppname_size(key)); + /* table key */ + pdfelib_totable(L, object, flat); + /* table key { type, [value], [extra], [more] } */ + lua_rawset(L, -3); + /* table[key] = { type, [value], [extra] } */ + } + } + return 1; + } else { + return 0; + } +} + +/*tex + + All pages are collected with: + + \starttyping + { { dict, size, objnum }, ... } = pagestotable(document) + \stoptyping + +*/ + +static int pdfelib_pagestotable(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + ppdoc *d = p->document; + int i = 1; + int j = 0; + lua_createtable(L, (int) ppdoc_page_count(d), 0); + /* pages[1..n] */ + for (ppref *r = ppdoc_first_page(d); r; r = ppdoc_next_page(d), ++i) { + lua_createtable(L, 3, 0); + if (ppref_obj(r)) { + pdfelib_aux_pushdictionary(L, ppref_obj(r)->dict); + /* table dictionary n */ + lua_rawseti(L, -3, 2); + /* table dictionary */ + lua_rawseti(L, -2, 1); + /* table */ + lua_pushinteger(L, r->number); + /* table reference */ + lua_rawseti(L, -2, 3); + /* table */ + lua_rawseti(L, -2, ++j); + /* pages[i] = { dictionary, size, objnum } */ + } + } + return 1; + } else { + return 0; + } +} + +/*tex + + Streams can be fetched on one go: + + \starttyping + string, n = readwholestream(streamobject,decode) + \stoptyping + +*/ + +static int pdfelib_stream_readwhole(lua_State *L) +{ + pdfe_stream *s = pdfelib_aux_check_isstream(L, 1); + if (s) { + uint8_t *b = NULL; + int decode = 0; + size_t n = 0; + if (s->open > 0) { + ppstream_done(s->stream); + s->open = 0; + s->decode = 0; + } + if (lua_gettop(L) > 1 && lua_isboolean(L, 2)) { + decode = lua_toboolean(L, 2); + } + b = ppstream_all(s->stream, &n, decode); + lua_pushlstring(L, (const char *) b, n); + lua_pushinteger(L, (lua_Integer) n); + ppstream_done(s->stream); + return 2; + } else { + return 0; + } +} + +/*tex + + Alternatively streams can be fetched stepwise: + + \starttyping + okay = openstream(streamobject,[decode]) + string, n = readfromstream(streamobject) + closestream(streamobject) + \stoptyping + +*/ + +static int pdfelib_stream_open(lua_State *L) +{ + pdfe_stream *s = pdfelib_aux_check_isstream(L, 1); + if (s) { + if (s->open == 0) { + if (lua_gettop(L) > 1) { + s->decode = lua_isboolean(L, 2); + } + s->open = 1; + } + lua_pushboolean(L,1); + return 1; + } else { + return 0; + } +} + +static int pdfelib_stream_close(lua_State *L) +{ + pdfe_stream *s = pdfelib_aux_check_isstream(L, 1); + if (s && s->open > 0) { + ppstream_done(s->stream); + s->open = 0; + s->decode = 0; + } + return 0; +} + +static int pdfelib_stream_read(lua_State *L) +{ + pdfe_stream *s = pdfelib_aux_check_isstream(L, 1); + if (s) { + size_t n = 0; + uint8_t *d = NULL; + if (s->open == 1) { + d = ppstream_first(s->stream, &n, s->decode); + s->open = 2; + } else if (s->open == 2) { + d = ppstream_next(s->stream, &n); + } else { + return 0; + } + lua_pushlstring(L, (const char *) d, n); + lua_pushinteger(L, (lua_Integer) n); + return 2; + } else { + return 0; + } +} + +/*tex + + There are two methods for opening a document: files and strings. + + \starttyping + documentobject = open(filename) + documentobject = new(string,length) + \stoptyping + + Closing happens with: + + \starttyping + close(documentobject) + \stoptyping + + When the \type {new} function gets a peudo filename as third argument, no user data will be + created but the stream is accessible as image. + +*/ + +/* +static int pdfelib_test(lua_State *L) +{ + const char *filename = luaL_checkstring(L, 1); + ppdoc *d = ppdoc_load(filename); + if (d) { + lua_pushboolean(L,1); + ppdoc_free(d); + } else { + lua_pushboolean(L,0); + } + return 1; +} +*/ + +static void aux_pdfelib_open(lua_State *L, FILE *f) +{ + pdfe_document *p = (pdfe_document *) lua_newuserdatauv(L, sizeof(pdfe_document), 0); + ppdoc *d = ppdoc_filehandle(f, 1); + luaL_getmetatable(L, PDFE_METATABLE_INSTANCE); + lua_setmetatable(L, -2); + p->document = d; + p->open = 1; + p->isfile = 1; + p->memstream = NULL; +} + +static int pdfelib_open(lua_State *L) +{ + const char *filename = luaL_checkstring(L, 1); + FILE *f = aux_utf8_fopen(filename, "rb"); + if (f) { + aux_pdfelib_open(L, f); + return 1; + } else { + tex_formatted_warning("pdfe lib", "no valid pdf file '%s'", filename); + return 0; + } +} + +static int pdfelib_openfile(lua_State *L) +{ + luaL_Stream *fs = ((luaL_Stream *) luaL_checkudata(L, 1, LUA_FILEHANDLE)); + FILE *f = (fs->closef) ? fs->f : NULL; + if (f) { + aux_pdfelib_open(L, f); + /*tex We trick \LUA\ in believing the file is closed. */ + fs->closef = NULL; + return 1; + } else { + tex_formatted_warning("pdfe lib", "no valid file handle"); + return 0; + } +} + +static int pdfelib_new(lua_State *L) +{ + size_t streamsize = 0; + const char *docstream = NULL; + switch (lua_type(L, 1)) { + case LUA_TSTRING: + docstream = lua_tolstring(L, 1, &streamsize); + if (! docstream) { + tex_normal_warning("pdfe lib", "invalid string"); + return 0; + } else { + break; + } + case LUA_TLIGHTUSERDATA: + /*tex + The stream comes as a sequence of bytes. This could happen from a library (we used + this for swiglib gm output tests). + */ + docstream = (const char *) lua_touserdata(L, 1); + if (! docstream) { + tex_normal_warning("pdfe lib", "invalid lightuserdata"); + return 0; + } else { + break; + } + default: + tex_normal_warning("pdfe lib", "string or lightuserdata expected"); + return 0; + } + streamsize = luaL_optinteger(L, 2, streamsize); + if (streamsize > 0) { + char *memstream = lmt_generic_malloc((unsigned) (streamsize + 1)); /* we have no hook into pdfe free */ + if (memstream) { + ppdoc *d = NULL; + memcpy(memstream, docstream, (streamsize + 1)); + memstream[streamsize] = '\0'; + d = ppdoc_mem(memstream, streamsize); + if (d) { + pdfe_document *p = (pdfe_document *) lua_newuserdatauv(L, sizeof(pdfe_document), 0); + luaL_getmetatable(L, PDFE_METATABLE_INSTANCE); + lua_setmetatable(L, -2); + p->document = d; + p->open = 1; + p->isfile = 0; + p->memstream = memstream; + return 1; + } else { + tex_normal_warning("pdfe lib", "unable to handle stream"); + } + } else { + tex_normal_warning("pdfe lib", "not enough memory for new stream"); + } + } else { + tex_normal_warning("pdfe lib", "stream with size > 0 expected"); + } + return 0; +} + +/* + + There is no garbage collection needed as the library itself manages the objects. Normally + objects don't take much space. Streams use buffers so (I assume) that they are not + persistent. The only collector is in the parent object (the document). + +*/ + +static int pdfelib_document_free(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p && p->open) { + if (p->document) { + ppdoc_free(p->document); + p->document = NULL; + } + if (p->memstream) { + /* pplib does this: xfree(p->memstream); */ + p->memstream = NULL; + } + p->open = 0; + } + return 0; +} + +static int pdfelib_close(lua_State *L) +{ + return pdfelib_document_free(L); +} + +/*tex + + A document is can be uncrypted with: + + \starttyping + status = unencrypt(documentobject,user,owner) + \stoptyping + + Instead of a password \type {nil} can be passed, so there are three possible useful combinations. + +*/ + +static int pdfelib_unencrypt(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + size_t u = 0; + size_t o = 0; + const char* user = NULL; + const char* owner = NULL; + int top = lua_gettop(L); + if (top > 1) { + if (lua_type(L,2) == LUA_TSTRING) { + user = lua_tolstring(L, 2, &u); + } else { + /*tex we're not too picky but normally it will be nil or false */ + } + if (top > 2) { + if (lua_type(L,3) == LUA_TSTRING) { + owner = lua_tolstring(L, 3, &o); + } else { + /*tex we're not too picky but normally it will be nil or false */ + } + } + lua_pushinteger(L, (lua_Integer) ppdoc_crypt_pass(p->document, user, u, owner, o)); + return 1; + } + } + lua_pushinteger(L, (lua_Integer) PPCRYPT_FAIL); + return 1; +} + +/*tex + + There are a couple of ways to get information about the document: + + \starttyping + n = getsize (documentobject) + major, minor = getversion (documentobject) + status = getstatus (documentobject) + n = getnofobjects (documentobject) + n = getnofpages (documentobject) + bytes, waste = getmemoryusage(documentobject) + \stoptyping + +*/ + +static int pdfelib_getsize(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + lua_pushinteger(L, (lua_Integer) ppdoc_file_size(p->document)); + return 1; + } else { + return 0; + } +} + + +static int pdfelib_getversion(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + int minor; + int major = ppdoc_version_number(p->document, &minor); + lua_pushinteger(L, (lua_Integer) major); + lua_pushinteger(L, (lua_Integer) minor); + return 2; + } else { + return 0; + } +} + +static int pdfelib_getstatus(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + lua_pushinteger(L, (lua_Integer) ppdoc_crypt_status(p->document)); + return 1; + } else { + return 0; + } +} + +static int pdfelib_getnofobjects(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + lua_pushinteger(L, (lua_Integer) ppdoc_objects(p->document)); + return 1; + } else { + return 0; + } +} + +static int pdfelib_getnofpages(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + lua_pushinteger(L, (lua_Integer) ppdoc_page_count(p->document)); + return 1; + } else { + return 0; + } +} + +static int pdfelib_getmemoryusage(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + size_t w = 0; + size_t m = ppdoc_memory(p->document, &w); + lua_pushinteger(L, (lua_Integer) m); + lua_pushinteger(L, (lua_Integer) w); + return 2; + } else { + return 0; + } +} + +/* + A specific page dictionary can be filtered with the next command. So, there is no need to parse + the document page tree (with these \type {kids} arrays). + + \starttyping + dictionaryobject = getpage(documentobject,pagenumber) + \stoptyping + +*/ + +static int pdfelib_aux_pushpage(lua_State *L, ppdoc *d, int page) +{ + if ((page <= 0) || (page > ((int) ppdoc_page_count(d)))) { + return 0; + } else { + ppref *pp = ppdoc_page(d, page); + return pdfelib_aux_pushdictionaryonly(L, ppref_obj(pp)->dict); + } +} + +static int pdfelib_getpage(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + return pdfelib_aux_pushpage(L, p->document, lmt_checkinteger(L, 2)); + } else { + return 0; + } +} + +static int pdfelib_aux_pushpages(lua_State *L, ppdoc *d) +{ + int i = 1; + lua_createtable(L, (int) ppdoc_page_count(d), 0); + /* pages[1..n] */ + for (ppref *r = ppdoc_first_page(d); r; r = ppdoc_next_page(d), ++i) { + pdfelib_aux_pushdictionaryonly(L,ppref_obj(r)->dict); + lua_rawseti(L, -2, i); + } + return 1 ; +} + +static int pdfelib_getpages(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + return pdfelib_aux_pushpages(L, p->document); + } else { + return 0; + } +} + +/*tex + + The boundingbox (\type {MediaBox) and similar boxes can be available in a (page) doctionary but + also in a parent object. Therefore a helper is available that does the (backtracked) lookup. + + \starttyping + { lx, ly, rx, ry } = getbox(dictionaryobject) + \stoptyping + +*/ + +static int pdfelib_getbox(lua_State *L) +{ + if (lua_gettop(L) > 1 && lua_type(L,2) == LUA_TSTRING) { + pdfe_dictionary *p = pdfelib_aux_check_isdictionary(L, 1); + if (p) { + const char *key = lua_tostring(L, 2); + pprect box = { 0, 0, 0, 0 }; + pprect *r = ppdict_get_box(p->dictionary, key, &box); + if (r) { + lua_createtable(L, 4, 0); + lua_pushnumber(L, r->lx); + lua_rawseti(L, -2, 1); + lua_pushnumber(L, r->ly); + lua_rawseti(L, -2, 2); + lua_pushnumber(L, r->rx); + lua_rawseti(L, -2, 3); + lua_pushnumber(L, r->ry); + lua_rawseti(L, -2, 4); + return 1; + } + } + } + return 0; +} + +/*tex + + This one is needed when you use the detailed getters and run into an object reference. The + regular getters resolve this automatically. + + \starttyping + [dictionary|array|stream]object = getfromreference(referenceobject) + \stoptyping + +*/ + +static int pdfelib_getfromreference(lua_State *L) +{ + pdfe_reference *r = pdfelib_aux_check_isreference(L, 1); + if (r && r->xref) { + ppref *rr = ppxref_find(r->xref, (ppuint) r->onum); + if (rr) { + ppobj *o = ppref_obj(rr); + if (o) { + lua_pushinteger(L, (lua_Integer) o->type); + return 1 + pdfelib_aux_pushvalue(L, o); + } + } + } + return 0; +} + +static int pdfelib_getfromobject(lua_State *L) +{ + pdfe_document *p = pdfelib_aux_check_isdocument(L, 1); + if (p) { + ppref *rr = ppxref_find(p->document->xref, lua_tointeger(L, 2)); + if (rr) { + ppobj *o = ppref_obj(rr); + if (o) { + lua_pushinteger(L, (lua_Integer) o->type); + return 1 + pdfelib_aux_pushvalue(L, o); + } + } + } + return 0; +} + +/*tex + + Here are some convenient getters: + + \starttyping + <string> = getstring (array|dict|ref,index|key) + <integer> = getinteger (array|dict|ref,index|key) + <number> = getnumber (array|dict|ref,index|key) + <boolan> = getboolean (array|dict|ref,index|key) + <string> = getname (array|dict|ref,index|key) + <dictionary> = getdictionary(array|dict|ref,index|key) + <array> = getarray (array|dict|ref,index|key) + <stream>, <dict> = getstream (array|dict|ref,index|key) + \stoptyping + + We report issues when reasonable but are silent when it makes sense. We don't error on this + because we expect the user code to act reasonable on a return value. + +*/ + +static int pdfelib_valid_index(lua_State *L, void **p, int *t) +{ + *t = lua_type(L, 2); + *p = lua_touserdata(L, 1); + lua_settop(L, 2); + if (! *p) { + switch (*t) { + case LUA_TSTRING: + tex_normal_warning("pdfe lib", "lua <pdfe dictionary> expected"); + break; + case LUA_TNUMBER: + tex_normal_warning("pdfe lib", "lua <pdfe array> expected"); + break; + default: + tex_normal_warning("pdfe lib", "invalid arguments"); + break; + } + return 0; + } else if (! lua_getmetatable(L, 1)) { + tex_normal_warning("pdfe lib", "first argument should be a <pde array> or <pde dictionary>"); + return 0; + } else { + return 1; + } +} + +static void pdfelib_invalid_index_warning(void) +{ + tex_normal_warning("pdfe lib", "second argument should be integer or string"); +} + +/*tex + + The direct fetcher returns the result or |NULL| when there is nothing found. The indirect + fetcher passes a pointer to the target variable and returns success state. + + The next two functions used to be macros but as we try to avoid large ones with much code, they + are now functions. + +*/ + +typedef void * (*pp_a_direct) (void *a, size_t index); +typedef void * (*pp_d_direct) (void *d, const char *key); +typedef int (*pp_a_indirect) (void *a, size_t index, void **value); +typedef int (*pp_d_indirect) (void *d, const char *key, void **value); + +static int pdfelib_get_value_direct(lua_State *L, void **value, pp_d_direct get_d, pp_a_direct get_a) +{ + int t = 0; + void *p = NULL; + if (pdfelib_valid_index(L, &p, &t)) { + switch (t) { + case LUA_TSTRING: + { + const char *key = lua_tostring(L, 2); + lua_get_metatablelua(pdfe_dictionary); + if (lua_rawequal(L, -1, -2)) { + *value = get_d(((pdfe_dictionary *) p)->dictionary, key); + return 1; + } else { + lua_get_metatablelua(pdfe_reference); + if (lua_rawequal(L, -1, -3)) { + ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL; \ + ppobj *o = (r) ? ppref_obj(r) : NULL; + if (o && o->type == PPDICT) { + *value = get_d((ppdict *) o->dict, key); + return 1; + } + } + } + } + break; + case LUA_TNUMBER: + { + size_t index = lua_tointeger(L, 2); + lua_get_metatablelua(pdfe_array); + if (lua_rawequal(L, -1, -2)) { + *value = get_a(((pdfe_array *) p)->array, index); + return 2; + } else { + lua_get_metatablelua(pdfe_reference); + if (lua_rawequal(L, -1, -3)) { + ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL; \ + ppobj *o = (r) ? ppref_obj(r) : NULL; + if (o && o->type == PPARRAY) { + *value = get_a((pparray *) o->array, index); + return 2; + } + } + } + } + break; + default: + pdfelib_invalid_index_warning(); + break; + } + } + return 0; +} + +static int pdfelib_get_value_indirect(lua_State *L, void **value, pp_d_indirect get_d, pp_a_indirect get_a) +{ + int t = 0; + void *p = NULL; + if (pdfelib_valid_index(L, &p, &t)) { + switch (t) { + case LUA_TSTRING: + { + const char *key = lua_tostring(L, 2); + lua_get_metatablelua(pdfe_dictionary); + if (lua_rawequal(L, -1, -2)) { + return get_d(((pdfe_dictionary *) p)->dictionary, key, value); + } else { + lua_get_metatablelua(pdfe_reference); + if (lua_rawequal(L, -1, -3)) { + ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL; + ppobj *o = (r) ? ppref_obj(r) : NULL; + if (o && o->type == PPDICT) + return get_d(o->dict, key, value); + } + } + } + break; + case LUA_TNUMBER: + { + size_t index = lua_tointeger(L, 2); + lua_get_metatablelua(pdfe_array); + if (lua_rawequal(L, -1, -2)) { + return get_a(((pdfe_array *) p)->array, index, value); + } else { + lua_get_metatablelua(pdfe_reference); + if (lua_rawequal(L, -1, -3)) { + ppref *r = (((pdfe_reference *) p)->xref) ? ppxref_find(((pdfe_reference *) p)->xref, (ppuint) (((pdfe_reference *) p)->onum)) : NULL; + ppobj *o = (r) ? ppref_obj(r) : NULL; + if (o && o->type == PPARRAY) + return get_a(o->array, index, value); + } + } + } + break; + default: + pdfelib_invalid_index_warning(); + break; + } + } + return 0; +} + +static int pdfelib_getstring(lua_State *L) +{ + if (lua_gettop(L) > 1) { + ppstring *value = NULL; + int okay = 0; + int how = 0; + if (lua_type(L, 3) == LUA_TBOOLEAN) { + if (lua_toboolean(L, 3)) { + how = 1; + } else { + how = 2; + } + } + okay = pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_string, (void *) &pparray_rget_string); + if (okay && value) { + if (how == 1) { + value = ppstring_decoded(value); + } + /*tex This used to return one value but we made it \LUATEX\ compatible. */ + lua_pushlstring(L, ppstring_data(value), ppstring_size(value)); + if (how == 2) { + lua_pushboolean(L, ppstring_hex(value)); + return 2; + } else { + return 1; + } + } + } + return 0; +} + +static int pdfelib_getinteger(lua_State *L) +{ + if (lua_gettop(L) > 1) { + ppint value = 0; + if (pdfelib_get_value_indirect(L, (void *) &value, (void *) &ppdict_rget_int, (void *) &pparray_rget_int)) { + lua_pushinteger(L, (lua_Integer) value); + return 1; + } + } + return 0; +} + +static int pdfelib_getnumber(lua_State *L) +{ + if (lua_gettop(L) > 1) { + ppnum value = 0; + if (pdfelib_get_value_indirect(L, (void *) &value, (void *) &ppdict_rget_num, (void *) &pparray_rget_num)) { + lua_pushnumber(L, value); + return 1; + } + } + return 0; +} + +static int pdfelib_getboolean(lua_State *L) +{ + if (lua_gettop(L) > 1) { + int value = 0; + if (pdfelib_get_value_indirect(L, (void *) &value, (void *) &ppdict_rget_bool, (void *) &pparray_rget_bool)) { + lua_pushboolean(L, value); + return 1; + } + } + return 0; +} + +static int pdfelib_getname(lua_State *L) +{ + if (lua_gettop(L) > 1) { + ppname *value = NULL; + pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_name, (void *) &pparray_rget_name); + if (value) { + value = ppname_decoded(value) ; + lua_pushlstring(L, ppname_data(value), ppname_size(value)); + return 1; + } + } + return 0; +} + +static int pdfelib_getdictionary(lua_State *L) +{ + if (lua_gettop(L) > 1) { + ppdict *value = NULL; + pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_dict, (void *) &pparray_rget_dict); + if (value) { + return pdfelib_aux_pushdictionaryonly(L, value); + } + } + return 0; +} + +static int pdfelib_getarray(lua_State *L) +{ + if (lua_gettop(L) > 1) { + pparray *value = NULL; + pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_array, (void *) &pparray_rget_array); + if (value) { + return pdfelib_aux_pusharrayonly(L, value); + } + } + return 0; +} + +static int pdfelib_getstream(lua_State *L) +{ + if (lua_gettop(L) > 1) { + ppobj *value = NULL; + pdfelib_get_value_direct(L, (void *) &value, (void *) &ppdict_rget_obj, (void *) &pparray_rget_obj); + if (value && value->type == PPSTREAM) { + return pdfelib_aux_pushstreamonly(L, (ppstream *) value->stream); + } + } + return 0; +} + +/*tex + + The generic pushed that does a similar job as the previous getters acts upon + the type. + +*/ + +static int pdfelib_pushvalue(lua_State *L, ppobj *object) +{ + switch (object->type) { + case PPNONE: + case PPNULL: + lua_pushnil(L); + break; + case PPBOOL: + lua_pushboolean(L, (int) object->integer); + break; + case PPINT: + lua_pushinteger(L, (lua_Integer) object->integer); + break; + case PPNUM: + lua_pushnumber(L, (double) object->number); + break; + case PPNAME: + { + ppname *n = ppname_decoded(object->name) ; + lua_pushlstring(L, ppname_data(n), ppname_size(n)); + } + break; + case PPSTRING: + lua_pushlstring(L, ppstring_data(object->string), ppstring_size(object->string)); + break; + case PPARRAY: + return pdfelib_aux_pusharrayonly(L, object->array); + case PPDICT: + return pdfelib_aux_pushdictionary(L, object->dict); + case PPSTREAM: + return pdfelib_aux_pushstream(L, object->stream); + case PPREF: + pdfelib_aux_pushreference(L, object->ref); + break; + /*tex We get a funny message in clang about covering all cases. */ + /* + default: + lua_pushnil(L); + break; + */ + } + return 1; +} + +/*tex + + Finally we arrived at the acessors for the userdata objects. The use previously defined helpers. + +*/ + +static int pdfelib_document_access(lua_State *L) +{ + if (lua_type(L, 2) == LUA_TSTRING) { + pdfe_document *p = (pdfe_document *) lua_touserdata(L, 1); + const char *s = lua_tostring(L, 2); + if (lua_key_eq(s, catalog) || lua_key_eq(s, Catalog)) { + return pdfelib_aux_pushdictionaryonly(L, ppdoc_catalog(p->document)); + } else if (lua_key_eq(s, info) || lua_key_eq(s, Info)) { + return pdfelib_aux_pushdictionaryonly(L, ppdoc_info(p->document)); + } else if (lua_key_eq(s, trailer) || lua_key_eq(s, Trailer)) { + return pdfelib_aux_pushdictionaryonly(L, ppdoc_trailer(p->document)); + } else if (lua_key_eq(s, pages) || lua_key_eq(s, Pages)) { + return pdfelib_aux_pushpages(L, p->document); + } + } + return 0; +} + +static int pdfelib_array_access(lua_State *L) +{ + if (lua_type(L, 2) == LUA_TNUMBER) { + pdfe_array *p = (pdfe_array *) lua_touserdata(L, 1); + ppint index = lua_tointeger(L, 2) - 1; + ppobj *o = pparray_rget_obj(p->array, index); + if (o) { + return pdfelib_pushvalue(L, o); + } + } + return 0; +} + +static int pdfelib_dictionary_access(lua_State *L) +{ + pdfe_dictionary *p = (pdfe_dictionary *) lua_touserdata(L, 1); + switch (lua_type(L, 2)) { + case LUA_TSTRING: + { + const char *key = lua_tostring(L, 2); + ppobj *o = ppdict_rget_obj(p->dictionary, key); + if (o) { + return pdfelib_pushvalue(L, o); + } + } + break; + case LUA_TNUMBER: + { + ppint index = lua_tointeger(L, 2) - 1; + ppobj *o = ppdict_at(p->dictionary, index); + if (o) { + return pdfelib_pushvalue(L, o); + } + } + break; + } + return 0; +} + +static int pdfelib_stream_access(lua_State *L) +{ + pdfe_stream *p = (pdfe_stream *) lua_touserdata(L, 1); + switch (lua_type(L, 2)) { + case LUA_TSTRING: + { + const char *key = lua_tostring(L, 2); + ppobj *o = ppdict_rget_obj(p->stream->dict, key); + if (o) { + return pdfelib_pushvalue(L, o); + } + } + break; + case LUA_TNUMBER: + { + ppint index = lua_tointeger(L, 2) - 1; + ppobj *o = ppdict_at(p->stream->dict, index); + if (o) { + return pdfelib_pushvalue(L, o); + } + } + break; + } + return 0; +} + +/*tex + + The length metamethods are defined last. + +*/ + +static int pdfelib_array_size(lua_State *L) +{ + pdfe_array *p = (pdfe_array *) lua_touserdata(L, 1); + lua_pushinteger(L, (lua_Integer) p->array->size); + return 1; +} + +static int pdfelib_dictionary_size(lua_State *L) +{ + pdfe_dictionary *p = (pdfe_dictionary *) lua_touserdata(L, 1); + lua_pushinteger(L, (lua_Integer) p->dictionary->size); + return 1; +} + +static int pdfelib_stream_size(lua_State *L) +{ + pdfe_stream *p = (pdfe_stream *) lua_touserdata(L, 1); + lua_pushinteger(L, (lua_Integer) p->stream->dict->size); + return 1; +} + +/*tex + + We now initialize the main interface. We might add few more informational helpers but this is + it. + +*/ + +static const struct luaL_Reg pdfelib_function_list[] = { + /* management */ + { "type", pdfelib_type }, + { "open", pdfelib_open }, + { "openfile", pdfelib_openfile }, + { "new", pdfelib_new }, + { "close", pdfelib_close }, + { "unencrypt", pdfelib_unencrypt }, + /* statistics */ + { "getversion", pdfelib_getversion }, + { "getstatus", pdfelib_getstatus }, + { "getsize", pdfelib_getsize }, + { "getnofobjects", pdfelib_getnofobjects }, + { "getnofpages", pdfelib_getnofpages }, + { "getmemoryusage", pdfelib_getmemoryusage }, + /* getters */ + { "getcatalog", pdfelib_getcatalog }, + { "gettrailer", pdfelib_gettrailer }, + { "getinfo", pdfelib_getinfo }, + { "getpage", pdfelib_getpage }, + { "getpages", pdfelib_getpages }, + { "getbox", pdfelib_getbox }, + { "getfromreference", pdfelib_getfromreference }, + { "getfromdictionary", pdfelib_getfromdictionary }, + { "getfromarray", pdfelib_getfromarray }, + { "getfromstream", pdfelib_getfromstream }, + /* handy too */ + { "getfromobject", pdfelib_getfromobject }, + /* collectors */ + { "dictionarytotable", pdfelib_dictionarytotable }, + { "arraytotable", pdfelib_arraytotable }, + { "pagestotable", pdfelib_pagestotable }, + /* more getters */ + { "getstring", pdfelib_getstring }, + { "getinteger", pdfelib_getinteger }, + { "getnumber", pdfelib_getnumber }, + { "getboolean", pdfelib_getboolean }, + { "getname", pdfelib_getname }, + { "getdictionary", pdfelib_getdictionary }, + { "getarray", pdfelib_getarray }, + { "getstream", pdfelib_getstream }, + /* streams */ + { "readwholestream", pdfelib_stream_readwhole }, + /* not really needed */ + { "openstream", pdfelib_stream_open }, + { "readfromstream", pdfelib_stream_read }, + { "closestream", pdfelib_stream_close }, + /* only for me, a test hook */ + /* { "test", pdfelib_test }, */ + /* done */ + { NULL, NULL } +}; + +/*tex + + The user data metatables are defined as follows. Watch how only the document needs a garbage + collector. + +*/ + +static const struct luaL_Reg pdfelib_instance_metatable[] = { + { "__tostring", pdfelib_document_tostring }, + { "__gc", pdfelib_document_free }, + { "__index", pdfelib_document_access }, + { NULL, NULL }, +}; + +static const struct luaL_Reg pdfelib_dictionary_metatable[] = { + { "__tostring", pdfelib_dictionary_tostring }, + { "__index", pdfelib_dictionary_access }, + { "__len", pdfelib_dictionary_size }, + { NULL, NULL }, +}; + +static const struct luaL_Reg pdfelib_array_metatable[] = { + { "__tostring", pdfelib_array_tostring }, + { "__index", pdfelib_array_access }, + { "__len", pdfelib_array_size }, + { NULL, NULL }, +}; + +static const struct luaL_Reg pdfelib_stream_metatable[] = { + { "__tostring", pdfelib_stream_tostring }, + { "__index", pdfelib_stream_access }, + { "__len", pdfelib_stream_size }, + { "__call", pdfelib_stream_readwhole }, + { NULL, NULL }, +}; + +static const struct luaL_Reg pdfelib_reference_metatable[] = { + { "__tostring", pdfelib_reference_tostring }, + { NULL, NULL }, +}; + +/*tex + + Finally we have arrived at the main initialiser that will be called as part of \LUATEX's + initializer. + +*/ + +/*tex + + Here we hook in the error handler. + +*/ + +static void pdfelib_message(const char *message, void *alien) +{ + (void) (alien); + tex_normal_warning("pdfe", message); +} + +int luaopen_pdfe(lua_State *L) +{ + /*tex First the four userdata object get their metatables defined. */ + + luaL_newmetatable(L, PDFE_METATABLE_DICTIONARY); + luaL_setfuncs(L, pdfelib_dictionary_metatable, 0); + + luaL_newmetatable(L, PDFE_METATABLE_ARRAY); + luaL_setfuncs(L, pdfelib_array_metatable, 0); + + luaL_newmetatable(L, PDFE_METATABLE_STREAM); + luaL_setfuncs(L, pdfelib_stream_metatable, 0); + + luaL_newmetatable(L, PDFE_METATABLE_REFERENCE); + luaL_setfuncs(L, pdfelib_reference_metatable, 0); + + /*tex Then comes the main (document) metatable: */ + + luaL_newmetatable(L, PDFE_METATABLE_INSTANCE); + luaL_setfuncs(L, pdfelib_instance_metatable, 0); + + /*tex Last the library opens up itself to the world. */ + + lua_newtable(L); + luaL_setfuncs(L, pdfelib_function_list, 0); + + pplog_callback(pdfelib_message, stderr); + + return 1; +} |