summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/lua/lpdfelib.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/lua/lpdfelib.c')
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/lpdfelib.c1666
1 files changed, 1666 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/lua/lpdfelib.c b/Build/source/texk/web2c/luatexdir/lua/lpdfelib.c
new file mode 100644
index 00000000000..31fad9f632d
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/lua/lpdfelib.c
@@ -0,0 +1,1666 @@
+/*tex
+
+ This file will host the encapsulated \PDF\ support code used for inclusion
+ and access from \LUA.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+ We need to avoid collision with some defines in |cpascal.h|.
+
+*/
+
+#undef lpdfelib_orig_input
+#undef lpdfelib_orig_output
+
+#ifdef input
+#define lpdfelib_orig_input input
+#undef input
+#endif
+
+#ifdef output
+#define lpdfelib_orig_output output
+#undef output
+#endif
+
+#include "luapplib/pplib.h"
+
+#include "image/epdf.h"
+
+#ifdef lpdfelib_orig_input
+#define input lpdfelib_orig_input
+#undef lpdfelib_orig_input
+#endif
+
+#ifdef lpdfelib_orig_output
+#define output lpdfelib_orig_output
+#undef lpdfelib_orig_output
+#endif
+
+#include "lua/luatex-api.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.
+
+*/
+
+#define PDFE_METATABLE "luatex.pdfe"
+#define PDFE_METATABLE_DICTIONARY "luatex.pdfe.dictionary"
+#define PDFE_METATABLE_ARRAY "luatex.pdfe.array"
+#define PDFE_METATABLE_STREAM "luatex.pdfe.stream"
+#define PDFE_METATABLE_REFERENCE "luatex.pdfe.reference"
+
+typedef struct {
+ ppdoc *document;
+ boolean open;
+ boolean isfile;
+ char *memstream;
+ int pages;
+ int index;
+} pdfe_document ;
+
+typedef struct {
+ ppdict *dictionary;
+ ppref *ref;
+} pdfe_dictionary;
+
+typedef struct {
+ pparray *array;
+ ppref *ref;
+} pdfe_array;
+
+typedef struct {
+ ppstream *stream;
+ ppref *ref;
+ int decode;
+ int open;
+} pdfe_stream;
+
+typedef struct {
+ ppref *reference;
+} 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)
+{
+ formatted_warning("pdfe lib","lua <pdfe %s> expected",detail);
+}
+
+static pdfe_document *check_isdocument(lua_State * L, int n)
+{
+ pdfe_document *p = (pdfe_document *)lua_touserdata(L, n);
+ if (p != NULL && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(luatex_pdfe);
+ if (!lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p != NULL) {
+ return p;
+ }
+ }
+ pdfe_invalid_object_warning("document");
+ return NULL;
+}
+
+static pdfe_dictionary *check_isdictionary(lua_State * L, int n)
+{
+ pdfe_dictionary *p = (pdfe_dictionary *)lua_touserdata(L, n);
+ if (p != NULL && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(luatex_pdfe_dictionary);
+ if (!lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p != NULL) {
+ return p;
+ }
+ }
+ pdfe_invalid_object_warning("dictionary");
+ return NULL;
+}
+
+static pdfe_array *check_isarray(lua_State * L, int n)
+{
+ pdfe_array *p = (pdfe_array *)lua_touserdata(L, n);
+ if (p != NULL && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(luatex_pdfe_array);
+ if (!lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p != NULL) {
+ return p;
+ }
+ }
+ pdfe_invalid_object_warning("array");
+ return NULL;
+}
+
+static pdfe_stream *check_isstream(lua_State * L, int n)
+{
+ pdfe_stream *p = (pdfe_stream *)lua_touserdata(L, n);
+ if (p != NULL && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(luatex_pdfe_stream);
+ if (!lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p != NULL) {
+ return p;
+ }
+ }
+ pdfe_invalid_object_warning("stream");
+ return NULL;
+}
+
+static pdfe_reference *check_isreference(lua_State * L, int n)
+{
+ pdfe_reference *p = (pdfe_reference *)lua_touserdata(L, n);
+ if (p != NULL && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(luatex_pdfe_reference);
+ if (!lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p != NULL) {
+ 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(luatex_##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 != NULL && lua_getmetatable(L, 1)) {
+ check_type(document, pdfe, "pdfe");
+ check_type(dictionary,pdfe_dictionary,"pdfe.dictionary");
+ check_type(array, pdfe_array, "pdfe.array");
+ check_type(reference, pdfe_reference, "pdfe.reference");
+ check_type(stream, pdfe_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.
+
+*/
+
+#define define_to_string(field,what) \
+static int pdfelib_tostring_##field(lua_State * L) { \
+ pdfe_##field *p = check_is##field(L, 1); \
+ if (p != NULL) { \
+ lua_pushfstring(L, "<" what " %p>", (ppdoc *) p->field); \
+ return 1; \
+ } \
+ return 0; \
+}
+
+define_to_string(document, "pdfe")
+define_to_string(dictionary,"pdfe.dictionary")
+define_to_string(array, "pdfe.array")
+define_to_string(reference, "pdfe.reference")
+define_to_string(stream, "pdfe.stream")
+
+/*tex
+
+ The pushers look rather similar. We have two variants, one that just pushes
+ the object, and another that also pushes some extra information.
+
+*/
+
+#define pdfe_push_dictionary do { \
+ pdfe_dictionary *d = (pdfe_dictionary *)lua_newuserdata(L, sizeof(pdfe_dictionary)); \
+ luaL_getmetatable(L, PDFE_METATABLE_DICTIONARY); \
+ lua_setmetatable(L, -2); \
+ d->dictionary = dictionary; \
+} while(0)
+
+static int pushdictionary(lua_State * L, ppdict *dictionary)
+{
+ if (dictionary != NULL) {
+ pdfe_push_dictionary;
+ lua_pushinteger(L,dictionary->size);
+ return 2;
+ }
+ return 0;
+}
+
+static int pushdictionaryonly(lua_State * L, ppdict *dictionary)
+{
+ if (dictionary != NULL) {
+ pdfe_push_dictionary;
+ return 1;
+ }
+ return 0;
+}
+
+#define pdfe_push_array do { \
+ pdfe_array *a = (pdfe_array *)lua_newuserdata(L, sizeof(pdfe_array)); \
+ luaL_getmetatable(L, PDFE_METATABLE_ARRAY); \
+ lua_setmetatable(L, -2); \
+ a->array = array; \
+ } while (0)
+
+static int pusharray(lua_State * L, pparray * array)
+{
+ if (array != NULL) {
+ pdfe_push_array;
+ lua_pushinteger(L,array->size);
+ return 2;
+ }
+ return 0;
+}
+
+static int pusharrayonly(lua_State * L, pparray * array)
+{
+ if (array != NULL) {
+ pdfe_push_array;
+ return 1;
+ }
+ return 0;
+}
+
+#define pdfe_push_stream do { \
+ pdfe_stream *s = (pdfe_stream *)lua_newuserdata(L, sizeof(pdfe_stream)); \
+ luaL_getmetatable(L, PDFE_METATABLE_STREAM); \
+ lua_setmetatable(L, -2); \
+ s->stream = stream; \
+ s->open = 0; \
+ s->decode = 0; \
+} while(0)
+
+static int pushstream(lua_State * L, ppstream * stream)
+{
+ if (stream != NULL) {
+ pdfe_push_stream;
+ if (pushdictionary(L, stream->dict) > 0)
+ return 3;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int pushstreamonly(lua_State * L, ppstream * stream)
+{
+ if (stream != NULL) {
+ pdfe_push_stream;
+ if (pushdictionaryonly(L, stream->dict) > 0)
+ return 2;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+#define pdfe_push_reference do { \
+ pdfe_reference *r = (pdfe_reference *)lua_newuserdata(L, sizeof(pdfe_reference)); \
+ luaL_getmetatable(L, PDFE_METATABLE_REFERENCE); \
+ lua_setmetatable(L, -2); \
+ r->reference = reference; \
+ } while (0)
+
+static int pushreference(lua_State * L, ppref * reference)
+{
+ if (reference != NULL) {
+ pdfe_push_reference;
+ lua_pushinteger(L,reference->number);
+ return 2;
+ }
+ 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 pushvalue(lua_State * L, ppobj *object)
+{
+ switch (object->type) {
+ case PPNONE:
+ case PPNULL:
+ lua_pushnil(L);
+ return 1;
+ break;
+ case PPBOOL:
+ lua_pushboolean(L,object->integer);
+ return 1;
+ break;
+ case PPINT:
+ lua_pushinteger(L, object-> integer);
+ return 1;
+ break;
+ case PPNUM:
+ lua_pushnumber(L, object->number);
+ return 1;
+ break;
+ case PPNAME:
+ lua_pushstring(L, (const char *) ppname_decoded(object->name));
+ return 1;
+ break;
+ case PPSTRING:
+ lua_pushlstring(L,(const char *) object->string, ppstring_size((void *)object->string));
+ lua_pushboolean(L, ppstring_hex((void *)object->string));
+ return 2;
+ break;
+ case PPARRAY:
+ return pusharray(L, object->array);
+ break;
+ case PPDICT:
+ return pushdictionary(L, object->dict);
+ break;
+ case PPSTREAM:
+ return pushstream(L, object->stream);
+ break;
+ case PPREF:
+ return pushreference(L, object->ref);
+ break;
+ }
+ 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 = check_isdocument(L, 1);
+ if (p == NULL)
+ return 0;
+ return pushdictionaryonly(L,ppdoc_catalog(p->document));
+}
+
+static int pdfelib_gettrailer(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p == NULL)
+ return 0;
+ return pushdictionaryonly(L,ppdoc_trailer(p->document));
+}
+
+static int pdfelib_getinfo(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p == NULL)
+ return 0;
+ return pushdictionaryonly(L,ppdoc_info(p->document));
+}
+
+/*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 = check_isarray(L, 1);
+ if (a != NULL) {
+ int index = luaL_checkint(L, 2) - 1;
+ if (index < a->array->size) {
+ ppobj *object = pparray_at(a->array,index);
+ lua_pushinteger(L,(int) object->type);
+ return 1 + pushvalue(L,object);
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_getfromdictionary(lua_State * L)
+{
+ pdfe_dictionary *d = check_isdictionary(L, 1);
+ if (d != NULL) {
+ if (lua_type(L,2) == LUA_TSTRING) {
+ const char *name = luaL_checkstring(L, 2);
+ ppobj *object = ppdict_get_obj(d->dictionary,name);
+ if (object != NULL) {
+ lua_pushinteger(L,(int) object->type);
+ return 1 + pushvalue(L,object);
+ }
+ } else {
+ int index = luaL_checkint(L, 2) - 1;
+ if (index < d->dictionary->size) {
+ ppobj *object = ppdict_at(d->dictionary,index);
+ ppname key = ppdict_key(d->dictionary,index);
+ lua_pushstring(L,(const char *) key);
+ lua_pushinteger(L,(int) object->type);
+ return 2 + pushvalue(L,object);
+ }
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_getfromstream(lua_State * L)
+{
+ pdfe_stream *s = (pdfe_stream *)lua_touserdata(L, 1);
+ if (s != NULL) {
+ 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 != NULL) {
+ lua_pushinteger(L,(int) object->type);
+ return 1 + pushvalue(L,object);
+ }
+ } else {
+ int index = luaL_checkint(L, 2) - 1;
+ if (index < d->size) {
+ ppobj *object = ppdict_at(d,index);
+ ppname key = ppdict_key(d,index);
+ lua_pushstring(L,(const char *) key);
+ lua_pushinteger(L,(int) object->type);
+ return 2 + 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 = pushvalue(L,object);
+ if (flat && n < 2) {
+ return;
+ }
+ /* [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,(int) object->type);
+ /* { nil, [value], [extra], [more] } type */
+ lua_rawseti(L,-2,1);
+ /* { type, [value], [extra], [more] } */
+ return;
+}
+
+static int pdfelib_arraytotable(lua_State * L)
+{
+ pdfe_array *a = check_isarray(L, 1);
+ if (a != NULL) {
+ int flat = lua_isboolean(L,2);
+ int i = 0;
+ lua_createtable(L,i,0);
+ /* table */
+ for (i=0;i<a->array->size;i++) {
+ ppobj *object = pparray_at(a->array,i);
+ pdfelib_totable(L,object,flat);
+ /* table { type, [value], [extra], [more] } */
+ lua_rawseti(L,-2,i+1);
+ /* table[i] = { type, [value], [extra], [more] } */
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int pdfelib_dictionarytotable(lua_State * L)
+{
+ pdfe_dictionary *d = check_isdictionary(L, 1);
+ if (d != NULL) {
+ int flat = lua_isboolean(L,2);
+ int i = 0;
+ lua_createtable(L,0,i);
+ /* table */
+ for (i=0;i<d->dictionary->size;i++) {
+ ppobj *object = ppdict_at(d->dictionary,i);
+ ppname key = ppdict_key(d->dictionary,i);
+ lua_pushstring(L,(const char *) 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;
+ }
+ 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 = check_isdocument(L, 1);
+ if (p != NULL) {
+ ppdoc *d = p->document;
+ ppref *r = NULL;
+ int i = 0;
+ lua_createtable(L,ppdoc_page_count(d),0);
+ /* pages[1..n] */
+ for (r = ppdoc_first_page(d), i = 1; r != NULL; r = ppdoc_next_page(d), ++i) {
+ lua_createtable(L,3,0);
+ 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,i);
+ /* pages[i] = { dictionary, size, objnum } */
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/*tex
+
+ Streams can be fetched on one go:
+
+ \starttyping
+ string, n = readwholestream(streamobject,decode)
+ \stoptyping
+
+*/
+
+static int pdfelib_readwholestream(lua_State * L)
+{
+ pdfe_stream *s = check_isstream(L, 1);
+ if (s != NULL) {
+ 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, (int) n);
+ ppstream_done(s->stream);
+ return 2;
+ }
+ return 0;
+}
+
+/*tex
+
+ Alternatively streams can be fetched stepwise:
+
+ okay = openstream(streamobject,[decode])
+ string, n = readfromstream(streamobject)
+ closestream(streamobject)
+
+*/
+
+static int pdfelib_openstream(lua_State * L)
+{
+ pdfe_stream *s = check_isstream(L, 1);
+ if (s != NULL) {
+ if (s->open == 0) {
+ if (lua_gettop(L) > 1) {
+ s->decode = lua_isboolean(L, 2);
+ }
+ s->open = 1;
+ }
+ lua_pushboolean(L,1);
+ return 1;
+ }
+ return 0;
+}
+
+static int pdfelib_closestream(lua_State * L)
+{
+ pdfe_stream *s = check_isstream(L, 1);
+ if (s != NULL) {
+ if (s->open >0) {
+ ppstream_done(s->stream);
+ s->open = 0;
+ s->decode = 0;
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_readfromstream(lua_State * L)
+{
+ pdfe_stream *s = check_isstream(L, 1);
+ if (s != NULL) {
+ 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, (int) n);
+ return 2;
+ }
+ 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_open(lua_State * L)
+{
+ const char *filename = luaL_checkstring(L, 1);
+ ppdoc *d = ppdoc_load(filename);
+ if (d == NULL) {
+ formatted_warning("pdfe lib","no valid pdf file '%s'",filename);
+ } else {
+ pdfe_document *p = (pdfe_document *) lua_newuserdata(L, sizeof(pdfe_document));
+ luaL_getmetatable(L, PDFE_METATABLE);
+ lua_setmetatable(L, -2);
+ p->document = d;
+ p->open = true;
+ p->isfile = true;
+ p->memstream = NULL;
+ return 1;
+ }
+ return 0;
+}
+
+static int pdfelib_new(lua_State * L)
+{
+ const char *docstream = NULL;
+ char *memstream = NULL ;
+ unsigned long long streamsize;
+ switch (lua_type(L, 1)) {
+ case LUA_TSTRING:
+ /* stream as Lua string */
+ docstream = luaL_checkstring(L, 1);
+ break;
+ case LUA_TLIGHTUSERDATA:
+ /* stream as sequence of bytes */
+ docstream = (const char *) lua_touserdata(L, 1);
+ break;
+ default:
+ luaL_error(L, "bad <pdfe> argument: string or lightuserdata expected");
+ break;
+ }
+ if (docstream == NULL) {
+ luaL_error(L, "bad <pdfe> document");
+ }
+ /* size of the stream */
+ streamsize = (unsigned long long) luaL_checkint(L, 2);
+ memstream = xmalloc((unsigned) (streamsize + 1));
+ if (! memstream) {
+ luaL_error(L, "no room for <pdfe> stream");
+ }
+ memcpy(memstream, docstream, (streamsize + 1));
+ memstream[streamsize]='\0';
+ if (lua_gettop(L) == 2) {
+ /* we stay at the lua end */
+ ppdoc *d = ppdoc_mem(memstream, streamsize);
+ if (d == NULL) {
+ normal_warning("pdfe lib","no valid pdf mem stream");
+ } else {
+ pdfe_document *p = (pdfe_document *) lua_newuserdata(L, sizeof(pdfe_document));
+ luaL_getmetatable(L, PDFE_METATABLE);
+ lua_setmetatable(L, -2);
+ p->document = d;
+ p->open = true;
+ p->isfile = false;
+ p->memstream = memstream;
+ return 1;
+ }
+ } else {
+ /* pseudo file name */
+ PdfDocument *pdf_doc;
+ const char *file_id = luaL_checkstring(L, 3);
+ if (file_id == NULL) {
+ luaL_error(L, "<pdfe> stream has an invalid id");
+ }
+ if (strlen(file_id) > STREAM_FILE_ID_LEN ) {
+ /* a limit to the length of the string */
+ luaL_error(L, "<pdfe> stream has a too long id");
+ }
+ pdf_doc = refMemStreamPdfDocument(memstream, streamsize, file_id);
+ if (pdf_doc != NULL) {
+ lua_pushstring(L,pdf_doc->file_path);
+ return 1;
+ } else {
+ /* pplib does this: xfree(memstream); */
+ }
+ }
+ 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_free(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p != NULL && p->open) {
+ if (p->document != NULL) {
+ ppdoc_free(p->document);
+ p->document = NULL;
+ }
+ if (p->memstream != NULL) {
+ /* pplib does this: xfree(p->memstream); */
+ p->memstream = NULL;
+ }
+ p->open = false;
+ }
+ return 0;
+}
+
+static int pdfelib_close(lua_State * L)
+{
+ return pdfelib_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 = check_isdocument(L, 1);
+ if (p != NULL) {
+ 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, (int) ppdoc_crypt_pass(p->document,user,u,owner,o));
+ return 1;
+ }
+ }
+ lua_pushinteger(L, (int) 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 = check_isdocument(L, 1);
+ if (p == NULL)
+ return 0;
+ lua_pushinteger(L,(int) ppdoc_file_size(p->document));
+ return 1;
+}
+
+
+static int pdfelib_getversion(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p == NULL) {
+ return 0;
+ } else {
+ int minor;
+ int major = ppdoc_version_number(p->document, &minor);
+ lua_pushinteger(L,(int) major);
+ lua_pushinteger(L,(int) minor);
+ return 2;
+ }
+}
+
+static int pdfelib_getstatus(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p == NULL)
+ return 0;
+ lua_pushinteger(L,(int) ppdoc_crypt_status(p->document));
+ return 1;
+}
+
+static int pdfelib_getnofobjects(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p == NULL)
+ return 0;
+ lua_pushinteger(L,(int) ppdoc_objects(p->document));
+ return 1;
+}
+
+static int pdfelib_getnofpages(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p == NULL)
+ return 0;
+ lua_pushinteger(L,(int) ppdoc_page_count(p->document));
+ return 1;
+}
+
+static int pdfelib_getmemoryusage(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p != NULL) {
+ size_t w = 0;
+ size_t m = ppdoc_memory(p->document,&w);
+ lua_pushinteger(L,(int) m);
+ lua_pushinteger(L,(int) w);
+ return 2;
+ }
+ 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 pushpage(lua_State * L, ppdoc * d, int page)
+{
+ if (page <= 0 || page > ppdoc_page_count(d)) {
+ return 0;
+ } else {
+ ppref *pp = ppdoc_page(d,page);
+ return pushdictionaryonly(L, ppref_obj(pp)->dict);
+ }
+}
+
+static int pdfelib_getpage(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p == NULL) {
+ return 0;
+ } else {
+ return pushpage(L, p->document, luaL_checkint(L, 2));
+ }
+}
+
+static int pushpages(lua_State * L, ppdoc * d)
+{
+ int i = 0;
+ ppref *r;
+ lua_createtable(L,ppdoc_page_count(d),0);
+ /* pages[1..n] */
+ for (r = ppdoc_first_page(d), i = 1; r != NULL; r = ppdoc_next_page(d), ++i) {
+ pushdictionaryonly(L,ppref_obj(r)->dict);
+ lua_rawseti(L,-2,i);
+ }
+ return 1 ;
+}
+
+static int pdfelib_getpages(lua_State * L)
+{
+ pdfe_document *p = check_isdocument(L, 1);
+ if (p == NULL) {
+ return 0;
+ } else {
+ return pushpages(L, p->document);
+ }
+}
+
+/*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 = check_isdictionary(L, 1);
+ if (p != NULL) {
+ const char *key = lua_tostring(L,2);
+ pprect box;
+ pprect *r;
+ box.lx = box.rx = box.ly = box.ry = 0;
+ r = ppdict_get_box(p->dictionary,key,&box);
+ if (r != NULL) {
+ 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 = check_isreference(L, 1);
+ if (r != NULL) {
+ ppobj *o = ppref_obj(r->reference);
+ lua_pushinteger(L,o->type);
+ return 1 + 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.
+
+*/
+
+#define pdfelib_get_value_check_1 do { \
+ if (p == NULL) { \
+ if (t == LUA_TSTRING) { \
+ normal_warning("pdfe lib","lua <pdfe dictionary> expected"); \
+ } else if (t == LUA_TNUMBER) { \
+ normal_warning("pdfe lib","lua <pdfe array> expected"); \
+ } else { \
+ normal_warning("pdfe lib","invalid arguments"); \
+ } \
+ return 0; \
+ } else if (! lua_getmetatable(L, 1)) { \
+ normal_warning("pdfe lib","first argument should be a <pde array> or <pde dictionary>"); \
+ } \
+} while (0)
+
+#define pdfelib_get_value_check_2 \
+ 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.
+
+*/
+
+#define pdfelib_get_value_direct(get_d,get_a) do { \
+ int t = lua_type(L,2); \
+ void *p = lua_touserdata(L, 1); \
+ pdfelib_get_value_check_1; \
+ if (t == LUA_TSTRING) { \
+ const char *key = lua_tostring(L,-2); \
+ lua_get_metatablelua(luatex_pdfe_dictionary); \
+ if (lua_rawequal(L, -1, -2)) { \
+ value = get_d(((pdfe_dictionary *) p)->dictionary, key); \
+ } else { \
+ lua_pop(L,1); \
+ lua_get_metatablelua(luatex_pdfe_reference); \
+ if (lua_rawequal(L, -1, -2)) { \
+ ppobj * o = ppref_obj((ppref *) (((pdfe_reference *) p)->reference)); \
+ if (o != NULL && o->type == PPDICT) { \
+ value = get_d((ppdict *)o->dict, key); \
+ } \
+ } \
+ } \
+ } else if (t == LUA_TNUMBER) { \
+ size_t index = lua_tointeger(L,-2); \
+ lua_get_metatablelua(luatex_pdfe_array); \
+ if (lua_rawequal(L, -1, -2)) { \
+ value = get_a(((pdfe_array *) p)->array, index); \
+ } else { \
+ lua_pop(L,1); \
+ lua_get_metatablelua(luatex_pdfe_reference); \
+ if (lua_rawequal(L, -1, -2)) { \
+ ppobj * o = ppref_obj((ppref *) (((pdfe_reference *) p)->reference)); \
+ if (o != NULL && o->type == PPARRAY) { \
+ value = get_a((pparray *) o->array, index); \
+ } \
+ } \
+ } \
+ } else { \
+ pdfelib_get_value_check_2; \
+ } \
+} while (0)
+
+/*tex
+
+ The indirect fetcher passes a pointer to the target variable and returns
+ success state.
+
+*/
+
+#define pdfelib_get_value_indirect(get_d,get_a) do { \
+ int t = lua_type(L,2); \
+ void *p = lua_touserdata(L, 1); \
+ pdfelib_get_value_check_1; \
+ if (t == LUA_TSTRING) { \
+ const char *key = lua_tostring(L,-2); \
+ lua_get_metatablelua(luatex_pdfe_dictionary); \
+ if (lua_rawequal(L, -1, -2)) { \
+ okay = get_d(((pdfe_dictionary *) p)->dictionary, key, &value); \
+ } else { \
+ lua_pop(L,1); \
+ lua_get_metatablelua(luatex_pdfe_reference); \
+ if (lua_rawequal(L, -1, -2)) { \
+ ppobj * o = ppref_obj((ppref *) (((pdfe_reference *) p)->reference)); \
+ if (o != NULL && o->type == PPDICT) \
+ okay = get_d(o->dict, key, &value); \
+ } \
+ } \
+ } else if (t == LUA_TNUMBER) { \
+ size_t index = lua_tointeger(L,-2); \
+ lua_get_metatablelua(luatex_pdfe_array); \
+ if (lua_rawequal(L, -1, -2)) { \
+ okay = get_a(((pdfe_array *) p)->array, index, &value); \
+ } else { \
+ lua_pop(L,1); \
+ lua_get_metatablelua(luatex_pdfe_reference); \
+ if (lua_rawequal(L, -1, -2)) { \
+ ppobj * o = ppref_obj((ppref *) (((pdfe_reference *) p)->reference)); \
+ if (o != NULL && o->type == PPARRAY) \
+ okay = get_a(o->array, index, &value); \
+ } \
+ } \
+ } else { \
+ pdfelib_get_value_check_2; \
+ } \
+} while (0)
+
+static int pdfelib_getstring(lua_State * L)
+{
+ if (lua_gettop(L) > 1) {
+ ppstring value = NULL;
+ pdfelib_get_value_direct(ppdict_rget_string,pparray_rget_string);
+ if (value != NULL) {
+ lua_pushstring(L,(const char *) value);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_getinteger(lua_State * L)
+{
+ if (lua_gettop(L) > 1) {
+ ppint value = 0;
+ int okay = 0;
+ pdfelib_get_value_indirect(ppdict_rget_int,pparray_rget_int);
+ if (okay) {
+ lua_pushinteger(L,(int) value);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_getnumber(lua_State * L)
+{
+ if (lua_gettop(L) > 1) {
+ ppnum value = 0;
+ int okay = 0;
+ pdfelib_get_value_indirect(ppdict_rget_num,pparray_rget_num);
+ if (okay) {
+ lua_pushnumber(L,value);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_getboolean(lua_State * L)
+{
+ if (lua_gettop(L) > 1) {
+ int value = 0;
+ int okay = 0;
+ pdfelib_get_value_indirect(ppdict_rget_bool,pparray_rget_bool);
+ if (okay) {
+ 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(ppdict_rget_name,pparray_rget_name);
+ if (value != NULL) {
+ lua_pushstring(L,(const char *) ppname_decoded(value));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_getdictionary(lua_State * L)
+{
+ if (lua_gettop(L) > 1) {
+ ppdict * value = NULL;
+ pdfelib_get_value_direct(ppdict_rget_dict,pparray_rget_dict);
+ if (value != NULL) {
+ return pushdictionaryonly(L,value);
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_getarray(lua_State * L)
+{
+ if (lua_gettop(L) > 1) {
+ pparray * value = NULL;
+ pdfelib_get_value_direct(ppdict_rget_array,pparray_rget_array);
+ if (value != NULL) {
+ return pusharrayonly(L,value);
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_getstream(lua_State * L)
+{
+ if (lua_gettop(L) > 1) {
+ ppobj * value = NULL;
+ pdfelib_get_value_direct(ppdict_rget_obj,pparray_rget_obj);
+ if (value != NULL && value->type == PPSTREAM) {
+ return 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, object->integer);
+ break;
+ case PPINT:
+ lua_pushinteger(L, object->integer);
+ break;
+ case PPNUM:
+ lua_pushnumber(L, object->number);
+ break;
+ case PPNAME:
+ lua_pushstring(L, (const char *) ppname_decoded(object->name));
+ break;
+ case PPSTRING:
+ lua_pushlstring(L,(const char *) object->string, ppstring_size((void *)object->string));
+ break;
+ case PPARRAY:
+ return pusharrayonly(L, object->array);
+ break;
+ case PPDICT:
+ return pushdictionary(L, object->dict);
+ break;
+ case PPSTREAM:
+ return pushstream(L, object->stream);
+ break;
+ case PPREF:
+ pushreference(L, object->ref);
+ break;
+ 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_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 pushdictionaryonly(L,ppdoc_catalog(p->document));
+ } else if (lua_key_eq(s,info) || lua_key_eq(s,Info)) {
+ return pushdictionaryonly(L,ppdoc_info(p->document));
+ } else if (lua_key_eq(s,trailer) || lua_key_eq(s,Trailer)) {
+ return pushdictionaryonly(L,ppdoc_trailer(p->document));
+ } else if (lua_key_eq(s,pages) || lua_key_eq(s,Pages)) {
+ return 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 != NULL) {
+ return pdfelib_pushvalue(L,o);
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_dictionary_access(lua_State * L)
+{
+ pdfe_dictionary *p = (pdfe_dictionary *)lua_touserdata(L, 1);
+ if (lua_type(L,2) == LUA_TSTRING) {
+ const char *key = lua_tostring(L,2);
+ ppobj *o = ppdict_rget_obj(p->dictionary,key);
+ if (o != NULL) {
+ return pdfelib_pushvalue(L,o);
+ }
+ } else if (lua_type(L,2) == LUA_TNUMBER) {
+ ppint index = lua_tointeger(L,2) - 1;
+ ppobj *o = ppdict_at(p->dictionary,index);
+ if (o != NULL) {
+ return pdfelib_pushvalue(L,o);
+ }
+ }
+ return 0;
+}
+
+static int pdfelib_stream_access(lua_State * L)
+{
+ pdfe_stream *p = (pdfe_stream *)lua_touserdata(L, 1);
+ if (lua_type(L,2) == LUA_TSTRING) {
+ const char *key = lua_tostring(L,2);
+ ppobj *o = ppdict_rget_obj(p->stream->dict,key);
+ if (o != NULL) {
+ return pdfelib_pushvalue(L,o);
+ }
+ } else if (lua_type(L,2) == LUA_TNUMBER) {
+ ppint index = lua_tointeger(L,2) - 1;
+ ppobj *o = ppdict_at(p->stream->dict,index);
+ if (o != NULL) {
+ return pdfelib_pushvalue(L,o);
+ }
+ }
+ 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,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,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,p->stream->dict->size);
+ return 1;
+}
+
+/*tex
+
+ We now initialize the main interface. We might aa few more
+ informational helpers but this is it.
+
+*/
+
+static const struct luaL_Reg pdfelib[] = {
+ /* management */
+ { "type", pdfelib_type },
+ { "open", pdfelib_open },
+ { "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 },
+ /* 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_readwholestream },
+ /* not really needed */
+ { "openstream", pdfelib_openstream },
+ { "readfromstream", pdfelib_readfromstream },
+ { "closestream", pdfelib_closestream },
+ /* 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_m[] = {
+ { "__tostring", pdfelib_tostring_document },
+ { "__gc", pdfelib_free },
+ { "__index", pdfelib_access },
+ { NULL, NULL}
+};
+
+static const struct luaL_Reg pdfelib_m_dictionary[] = {
+ { "__tostring", pdfelib_tostring_dictionary },
+ { "__index", pdfelib_dictionary_access },
+ { "__len", pdfelib_dictionary_size },
+ { NULL, NULL}
+};
+
+static const struct luaL_Reg pdfelib_m_array[] = {
+ { "__tostring", pdfelib_tostring_array },
+ { "__index", pdfelib_array_access },
+ { "__len", pdfelib_array_size },
+ { NULL, NULL}
+};
+
+static const struct luaL_Reg pdfelib_m_stream[] = {
+ { "__tostring", pdfelib_tostring_stream },
+ { "__index", pdfelib_stream_access },
+ { "__len", pdfelib_stream_size },
+ { "__call", pdfelib_readwholestream },
+ { NULL, NULL}
+};
+
+static const struct luaL_Reg pdfelib_m_reference[] = {
+ { "__tostring", pdfelib_tostring_reference },
+ { NULL, NULL}
+};
+
+/*tex
+
+ Finally we hav earrived 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)
+{
+ 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_openlib(L, NULL, pdfelib_m_dictionary, 0);
+
+ luaL_newmetatable(L, PDFE_METATABLE_ARRAY);
+ luaL_openlib(L, NULL, pdfelib_m_array, 0);
+
+ luaL_newmetatable(L, PDFE_METATABLE_STREAM);
+ luaL_openlib(L, NULL, pdfelib_m_stream, 0);
+
+ luaL_newmetatable(L, PDFE_METATABLE_REFERENCE);
+ luaL_openlib(L, NULL, pdfelib_m_reference, 0);
+
+ /*tex Then comes the main (document) metatable: */
+
+ luaL_newmetatable(L, PDFE_METATABLE);
+ luaL_openlib(L, NULL, pdfelib_m, 0);
+
+ /*tex Last the library opens up itself to the world. */
+
+ luaL_openlib(L, "pdfe", pdfelib, 0);
+
+ pplog_callback(pdfelib_message, stderr);
+
+ return 1;
+}