summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.c')
-rw-r--r--Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.c1110
1 files changed, 1110 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.c b/Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.c
new file mode 100644
index 00000000000..6c3bc8876ee
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/lua/lpdfscannerlib.c
@@ -0,0 +1,1110 @@
+/* lpdfscannerlib.c
+
+ Copyright 2013 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/*tex
+
+ The scanner can read from a string or stream. Streams can be given directly as
+ |ppstream| object or as a |pparray| of streams. Here is an example of usage:
+
+ \starttyping
+ local operatortable = { }
+
+ operatortable.Do = function(scanner,info)
+ local resources = info.resources
+ if resources then
+ local val = scanner:pop()
+ local name = val[2]
+ local xobject = resources.XObject
+ print(info.space .. "Uses XObject " .. name)
+ local resources = xobject.Resources
+ if resources then
+ local newinfo = {
+ space = info.space .. " ",
+ resources = resources,
+ }
+ pdfscanner.scan(entry, operatortable, newinfo)
+ end
+ end
+ end
+
+ local function Analyze(filename)
+ local doc = pdfe.open(filename)
+ if doc then
+ local pages = doc.Pages
+ for i=1,#pages do
+ local page = pages[i]
+ local info = {
+ space = " " ,
+ resources = page.Resources,
+ }
+ print("Page " .. i)
+ pdfscanner.scan(page.Contents,operatortable,info)
+ pdfscanner.scan(page.Contents(),operatortable,info)
+ end
+ end
+ end
+
+ Analyze("foo.pdf")
+ \stoptyping
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+#include <math.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "luapplib/pplib.h"
+
+#include <lua/luatex-api.h>
+
+#define SCANNER "pdfscanner"
+
+#define MAXOPERANDS 1000
+
+typedef enum {
+ pdf_integer = 1,
+ pdf_real,
+ pdf_boolean,
+ pdf_name,
+ pdf_operator,
+ pdf_string,
+ pdf_startarray,
+ pdf_stoparray,
+ pdf_startdict,
+ pdf_stopdict,
+} pdf_token_type;
+
+typedef struct Token {
+ pdf_token_type type;
+ double value;
+ char *string;
+} Token;
+
+typedef struct ObjectList {
+ struct ObjectList *next;
+ ppstream *stream;
+} ObjectList;
+
+typedef struct scannerdata {
+ int _ininlineimage;
+ int _nextoperand;
+ Token **_operandstack;
+ ppstream *_stream;
+ ObjectList *_streams;
+ const char *buffer;
+ size_t position;
+ size_t size;
+ int uses_stream;
+} scannerdata;
+
+#define PDFE_METATABLE_ARRAY "luatex.pdfe.array"
+#define PDFE_METATABLE_STREAM "luatex.pdfe.stream"
+
+typedef struct {
+ void *d;
+ /*tex reference to |PdfDocument|, or |NULL| */
+ void *pd;
+ /*tex counter to detect |PDFDoc| change */
+ unsigned long pc;
+} udstruct;
+
+static void clear_operand_stack(scannerdata * self, int from);
+static Token *_parseToken(scannerdata * self, int c);
+static void push_token(lua_State * L, scannerdata * self);
+
+static void *priv_xmalloc(size_t size)
+{
+ void *new_mem = (void *) malloc(size);
+ if (new_mem == NULL) {
+ luaL_error(Luas, "no room for <pdfscanned> stream");
+ }
+ return new_mem;
+}
+
+static void *priv_xrealloc(void *old_ptr, size_t size)
+{
+ void *new_mem = (void *) realloc(old_ptr, size);
+ if (new_mem == NULL) {
+ luaL_error(Luas, "no room for <pdfscanned> stream");
+ }
+ return new_mem;
+}
+
+#define xreallocarray(ptr,type,size) ((type*)priv_xrealloc(ptr,(size+1)*sizeof(type)))
+
+#define INITBUFSIZE 64
+
+#define define_buffer(a) \
+ char *a = (char *)priv_xmalloc (INITBUFSIZE); \
+ int a##_size = INITBUFSIZE; \
+ int a##index = 0; \
+ memset (a,0,INITBUFSIZE)
+
+#define check_overflow(a, wsize) do { \
+ if (wsize >= a##_size) { \
+ int nsize = a##_size + a##_size / 4; \
+ a = (char *) xreallocarray(a, char, (unsigned) nsize); \
+ memset (a+a##_size, 0, a##_size / 4); \
+ a##_size = nsize; \
+ } \
+} while (0)
+
+
+static scannerdata *scanner_push(lua_State * L)
+{
+ scannerdata *a = (scannerdata *) lua_newuserdata(L, sizeof(scannerdata));
+ luaL_getmetatable(L, SCANNER);
+ lua_setmetatable(L, -2);
+ return a;
+}
+
+static scannerdata *scanner_check(lua_State * L, int index)
+{
+ scannerdata *bar;
+ luaL_checktype(L, index, LUA_TUSERDATA);
+ bar = (scannerdata *) luaL_checkudata(L, index, SCANNER);
+ if (bar == NULL)
+ luaL_argerror(L, index, SCANNER " expected");
+ return bar;
+}
+
+static void free_token(Token * token)
+{
+ if (token->string) {
+ free(token->string);
+ }
+ free(token);
+}
+
+static void clear_operand_stack(scannerdata * self, int from)
+{
+ int i = self->_nextoperand - 1;
+ while (i >= from) {
+ if (self->_operandstack[i]) {
+ free_token(self->_operandstack[i]);
+ self->_operandstack[i] = NULL;
+ }
+ i--;
+ }
+ self->_nextoperand = from;
+}
+
+static void push_operand(scannerdata * self, Token * token)
+{
+ if (self->_nextoperand + 1 > MAXOPERANDS) {
+ fprintf(stderr, "out of operand stack space");
+ exit(1);
+ }
+ self->_operandstack[self->_nextoperand++] = token;
+}
+
+static Token *new_operand(pdf_token_type c)
+{
+ Token *token = (Token *) priv_xmalloc(sizeof(Token));
+ memset(token, 0, sizeof(Token));
+ token->type = c;
+ return token;
+}
+
+static void _nextStream(scannerdata * self)
+{
+ ObjectList *rover = NULL;
+ if (self->uses_stream && self->buffer != NULL) {
+ if (self->uses_stream) {
+ ppstream_done(self->_stream);
+ } else {
+ free(self->_stream);
+ }
+ }
+ rover = self->_streams;
+ self->_stream = rover->stream;
+ if (self->uses_stream) {
+ self->buffer = (const char *) ppstream_all(self->_stream, &self->size, 1);
+ }
+ self->position = 0;
+ self->_streams = rover->next;
+ free(rover);
+}
+
+static int streamGetChar(scannerdata * self)
+{
+ int i = EOF;
+ if (self->position < self->size) {
+ const char c = self->buffer[self->position];
+ ++self->position;
+ i = (int) c;
+ }
+ if (i < 0 && self->_streams) {
+ _nextStream(self);
+ i = streamGetChar(self);
+ }
+ return i;
+}
+
+static int streamLookChar(scannerdata * self)
+{
+ int i = EOF;
+ if (self->position < self->size) {
+ const char c = self->buffer[self->position];
+ /*not |++self->position;| */
+ i = (int) c;
+ }
+ if (i < 0 && self->_streams) {
+ _nextStream(self);
+ i = streamGetChar(self);
+ }
+ return i;
+}
+
+static void streamReset(scannerdata * self)
+{
+ if (self->uses_stream) {
+ self->buffer = (const char *) ppstream_all(self->_stream, &self->size, 1);
+ }
+ self->position = 0;
+}
+
+static void streamClose(scannerdata * self)
+{
+ if (self->uses_stream) {
+ ppstream_done(self->_stream);
+ } else {
+ free(self->_stream);
+ }
+ self->buffer = NULL;
+ self->_stream = NULL;
+}
+
+/*tex end of stream interface */
+
+static Token *_parseSpace(scannerdata * self)
+{
+ return _parseToken(self, streamGetChar(self));
+}
+
+static Token *_parseString(scannerdata * self, int c)
+{
+ int level;
+ Token *token = NULL;
+ define_buffer(found);
+ level = 1;
+ while (1) {
+ c = streamGetChar(self);
+ if (c == '(') {
+ level = level + 1;
+ } else if (c == ')') {
+ level = level - 1;
+ if (level < 1)
+ break;
+ } else if (c == '\\') {
+ int next = streamGetChar(self);
+ if (next == '(' || next == ')' || next == '\\') {
+ c = next;
+ } else if (next == '\n' || next == '\r') {
+ c = '\0';
+ } else if (next == 'n') {
+ c = '\n';
+ } else if (next == 'r') {
+ c = '\r';
+ } else if (next == 't') {
+ c = '\t';
+ } else if (next == 'b') {
+ c = '\b';
+ } else if (next == 'f') {
+ c = '\f';
+ } else if (next >= '0' && next <= '7') {
+ int next2;
+ next = next - '0';
+ next2 = streamLookChar(self);
+ if (next2 >= '0' && next2 <= '7') {
+ int next3;
+ next2 = streamGetChar(self);
+ next2 = next2 - '0';
+ next3 = streamLookChar(self);
+ if (next3 >= '0' && next3 <= '7') {
+ next3 = streamGetChar(self);
+ next3 = next3 - '0';
+ c = (next * 64 + next2 * 8 + next3);
+ } else {
+ c = (next * 8 + next2);
+ }
+ } else {
+ c = next;
+ }
+ } else {
+ c = next;
+ }
+ }
+ check_overflow(found, foundindex);
+ if (c >= 0) {
+ found[foundindex++] = c;
+ }
+ }
+ token = new_operand(pdf_string);
+ token->value = foundindex;
+ token->string = found;
+ return token;
+}
+
+static Token *_parseNumber(scannerdata * self, int c)
+{
+ double value = 0;
+ pdf_token_type type = pdf_integer;
+ int isfraction = 0;
+ int isnegative = 0;
+ int i = 0;
+ Token *token = NULL;
+ if (c == '-') {
+ isnegative = 1;
+ c = streamGetChar(self);
+ }
+ if (c == '.') {
+ type = pdf_real;
+ isfraction = 1;
+ } else {
+ value = c - '0';
+ }
+ c = streamLookChar(self);
+ if ((c >= '0' && c <= '9') || c == '.') {
+ c = streamGetChar(self);
+ while (1) {
+ if (c == '.') {
+ type = pdf_real;
+ isfraction = 1;
+ } else {
+ i = c - '0';
+ if (isfraction > 0) {
+ value = value + (i / (pow(10.0, isfraction)));
+ isfraction = isfraction + 1;
+ } else {
+ value = (value * 10) + i;
+ }
+ }
+ c = streamLookChar(self);
+ if (!((c >= '0' && c <= '9') || c == '.'))
+ break;
+ c = streamGetChar(self);
+ }
+ }
+ if (isnegative) {
+ value = -value;
+ }
+ token = new_operand(type);
+ token->value = value;
+ return token;
+}
+
+static Token *_parseName(scannerdata * self, int c)
+{
+ Token *token = NULL;
+ define_buffer(found);
+ c = streamGetChar(self);
+ while (1) {
+ check_overflow(found, foundindex);
+ found[foundindex++] = c;
+ c = streamLookChar(self);
+ if (c == ' ' || c == '\n' || c == '\r' || c == '\t' ||
+ c == '/' || c == '[' || c == '(' || c == '<')
+ break;
+ c = streamGetChar(self);
+ }
+ token = new_operand(pdf_name);
+ token->string = found;
+ token->value = strlen(found);
+ return token;
+}
+
+#define hexdigit(c) \
+ (c>= '0' && c<= '9') ? (c - '0') : ((c>= 'A' && c<= 'F') ? (c - 'A' + 10) : (c - 'a' + 10))
+
+static Token *_parseHexstring(scannerdata * self, int c)
+{
+ int isodd = 1;
+ int hexval = 0;
+ Token *token = NULL;
+ define_buffer(found);
+ while (c != '>') {
+ if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
+ if (isodd == 1) {
+ int v = hexdigit(c);
+ hexval = 16 * v;
+ } else {
+ hexval += hexdigit(c);
+ check_overflow(found, foundindex);
+ found[foundindex++] = hexval;
+ }
+ isodd = (isodd == 1 ? 0 : 1);
+ }
+ c = streamGetChar(self);
+ }
+ token = new_operand(pdf_string);
+ token->value = foundindex;
+ token->string = found;
+ return token;
+}
+
+#define pdf_isspace(a) (a == '\0' || a == ' ' || a == '\n' || a == '\r' || a == '\t' || a == '\v')
+
+/*tex this is rather horrible */
+
+static Token *_parseInlineImage(scannerdata * self, int c)
+{
+ Token *token = NULL;
+ define_buffer(found);
+ if (c == ' ') {
+ /*tex first space can be ignored */
+ c = streamGetChar(self);
+ }
+ check_overflow(found, foundindex);
+ found[foundindex++] = c;
+ while (1) {
+ c = streamLookChar(self);
+ if (c == 'E'
+ && (found[foundindex - 1] == '\n'
+ || found[foundindex - 1] == '\r')) {
+ c = streamGetChar(self);
+ check_overflow(found, foundindex);
+ found[foundindex++] = c;
+ c = streamLookChar(self);
+ if (c == 'I') {
+ c = streamGetChar(self);
+ check_overflow(found, foundindex);
+ found[foundindex++] = c;
+ c = streamLookChar(self);
+ if (pdf_isspace(c)) {
+ /*tex |I| */
+ found[--foundindex] = '\0';
+ /*tex |E| */
+ found[--foundindex] = '\0';
+ /*tex remove end-of-line before |EI| */
+ if (found[foundindex - 1] == '\n') {
+ found[--foundindex] = '\0';
+ }
+ if (found[foundindex - 1] == '\r') {
+ found[--foundindex] = '\0';
+ }
+ break;
+ } else {
+ c = streamGetChar(self);
+ check_overflow(found, foundindex);
+ found[foundindex++] = c;
+ }
+ } else {
+ c = streamGetChar(self);
+ check_overflow(found, foundindex);
+ found[foundindex++] = c;
+ }
+ } else {
+ c = streamGetChar(self);
+ check_overflow(found, foundindex);
+ found[foundindex++] = c;
+ }
+ }
+ token = new_operand(pdf_string);
+ token->value = foundindex;
+ token->string = found;
+ return token;
+}
+
+static Token *_parseOperator(scannerdata * self, int c)
+{
+ define_buffer(found);
+ while (1) {
+ check_overflow(found, foundindex);
+ found[foundindex++] = c;
+ c = streamLookChar(self);
+ if ((c < 0) || (c == ' ' || c == '\n' || c == '\r' || c == '\t' ||
+ c == '/' || c == '[' || c == '(' || c == '<'))
+ break;
+ c = streamGetChar(self);
+ }
+ /*tex |print| (found) */
+ if (strcmp(found, "ID") == 0) {
+ self->_ininlineimage = 1;
+ }
+ if (strcmp(found, "false") == 0) {
+ Token *token = new_operand(pdf_boolean);
+ token->value = 0;
+ free(found);
+ return token;
+ } else if (strcmp(found, "true") == 0) {
+ Token *token = new_operand(pdf_boolean);
+ token->value = 1.0;
+ free(found);
+ return token;
+ } else {
+ Token *token = new_operand(pdf_operator);
+ token->string = found;
+ return token;
+ }
+}
+
+static Token *_parseComment(scannerdata * self, int c)
+{
+ do {
+ c = streamGetChar(self);
+ } while (c != '\n' && c != '\r' && c != -1);
+ return _parseToken(self, streamGetChar(self));
+}
+
+static Token *_parseLt(scannerdata * self, int c)
+{
+ c = streamGetChar(self);
+ if (c == '<') {
+ return new_operand(pdf_startdict);
+ } else {
+ return _parseHexstring(self, c);
+ }
+}
+
+static Token *_parseGt(scannerdata * self, int c)
+{
+ c = streamGetChar(self);
+ if (c == '>') {
+ return new_operand(pdf_stopdict);
+ } else {
+ fprintf(stderr, "stray > in stream");
+ return NULL;
+ }
+}
+
+static Token *_parseError(int c)
+{
+ fprintf(stderr, "stray %c [%d] in stream", c, c);
+ return NULL;
+}
+
+static Token *_parseStartarray(void)
+{
+ return new_operand(pdf_startarray);
+}
+
+static Token *_parseStoparray(void)
+{
+ return new_operand(pdf_stoparray);
+}
+
+static Token *_parseToken(scannerdata * self, int c)
+{
+ if (self->_ininlineimage == 1) {
+ self->_ininlineimage = 2;
+ return _parseInlineImage(self, c);
+ } else if (self->_ininlineimage == 2) {
+ Token *token = NULL;
+ self->_ininlineimage = 0;
+ token = new_operand(pdf_operator);
+ token->string = strdup("EI");
+ return token;
+ }
+ if (c < 0)
+ return NULL;
+ switch (c) {
+ case '(':
+ return _parseString(self, c);
+ break;
+ case ')':
+ return _parseError(c);
+ break;
+ case '[':
+ return _parseStartarray();
+ break;
+ case ']':
+ return _parseStoparray();
+ break;
+ case '/':
+ return _parseName(self, c);
+ break;
+ case '<':
+ return _parseLt(self, c);
+ break;
+ case '>':
+ return _parseGt(self, c);
+ break;
+ case '%':
+ return _parseComment(self, c);
+ break;
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\t':
+ return _parseSpace(self);
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ case '.':
+ return _parseNumber(self, c);
+ break;
+ default:
+ if (c <= 127) {
+ return _parseOperator(self, c);
+ } else {
+ return _parseError(c);
+ }
+ }
+}
+
+static int scanner_scan(lua_State * L)
+{
+ Token *token;
+ scannerdata *self;
+ if (lua_gettop(L) != 3) {
+ return 0;
+ }
+ luaL_checktype(L, 2, LUA_TTABLE);
+ luaL_checktype(L, 3, LUA_TTABLE);
+ self = scanner_push(L);
+ memset(self, 0, sizeof(scannerdata));
+ self->_operandstack = (Token **) priv_xmalloc(MAXOPERANDS * sizeof(Token));
+ memset(self->_operandstack, 0, (MAXOPERANDS * sizeof(Token)));
+ /*tex stack slot 4 = self */
+ self->uses_stream = 1;
+ if (lua_type(L, 1) == LUA_TSTRING) {
+ /*tex
+ We could make a temporary copy on the stack (or in the registry)
+ which saves memory.
+ */
+ char *buf = NULL;
+ const char *s = lua_tolstring(L, 1, &self->size);
+ if (s==NULL){
+ fprintf(stderr,"fatal: cannot convert the token to string.");
+ exit(1);
+ }
+ buf = priv_xmalloc(self->size+1);
+ buf[self->size]='\0';
+ self->uses_stream = 0;
+ memcpy(buf,s,self->size);
+ self->buffer = buf;
+ } else if (lua_type(L, 1) == LUA_TTABLE) {
+ udstruct *uin;
+ void *ud;
+ int i = 1;
+ while (1) {
+ lua_rawgeti(L, 1, i);
+ if (lua_type(L, -1) == LUA_TUSERDATA) {
+ ud = luaL_checkudata(L, -1, PDFE_METATABLE_STREAM);
+ if (ud != NULL) {
+ ObjectList *rover = NULL;
+ ObjectList *item = NULL;
+ uin = (udstruct *) ud;
+ rover = self->_streams;
+ item = (ObjectList *) priv_xmalloc(sizeof(ObjectList));
+ item->stream = ((ppstream *) uin->d);
+ item->next = NULL;
+ if (!rover) {
+ rover = item;
+ self->_streams = rover;
+ } else {
+ while (rover->next)
+ rover = rover->next;
+ rover->next = item;
+ }
+ }
+ } else {
+ ObjectList *rover = self->_streams;
+ self->_stream = rover->stream;
+ self->_streams = rover->next;
+ free(rover);
+ lua_pop(L, 1);
+ break;
+ }
+ lua_pop(L, 1);
+ i++;
+ }
+ } else {
+ udstruct *uin;
+ void *ud;
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ ud = luaL_checkudata(L, 1, PDFE_METATABLE_STREAM);
+ if (ud != NULL) {
+ uin = (udstruct *) ud;
+ self->_stream = ((ppstream *) uin->d);
+ } else {
+ ud = luaL_checkudata(L, 1, PDFE_METATABLE_ARRAY);
+ if (ud != NULL) {
+ ObjectList *rover = NULL;
+ pparray *array = NULL;
+ int count;
+ int i;
+ uin = (udstruct *) ud;
+ array = (pparray *) uin->d;
+ count = array->size;
+ for (i = 0; i < count; i++) {
+ ppobj *obj = pparray_at(array, i);
+ if (obj->type == PPSTREAM) {
+ ObjectList *rover = self->_streams;
+ ObjectList *item =
+ (ObjectList *)
+ priv_xmalloc(sizeof(ObjectList));
+ item->stream = obj->stream;
+ item->next = NULL;
+ if (!rover) {
+ rover = item;
+ self->_streams = rover;
+ } else {
+ while (rover->next)
+ rover = rover->next;
+ rover->next = item;
+ }
+ }
+ }
+ rover = self->_streams;
+ self->_stream = rover->stream;
+ self->_streams = rover->next;
+ }
+ }
+ }
+ streamReset(self);
+ token = _parseToken(self, streamGetChar(self));
+ while (token) {
+ if (token->type == pdf_operator) {
+ lua_pushstring(L, token->string);
+ free_token(token);
+ /*tex fetch operator table */
+ lua_rawget(L, 2);
+ if (lua_isfunction(L, -1)) {
+ lua_pushvalue(L, 4);
+ lua_pushvalue(L, 3);
+ (void) lua_call(L, 2, 0);
+ } else {
+ /*tex nil */
+ lua_pop(L, 1);
+ }
+ clear_operand_stack(self, 0);
+ } else {
+ push_operand(self, token);
+ }
+ if (self->uses_stream) {
+ if (!self->_stream) {
+ break;
+ }
+ } else {
+ if (self->buffer == NULL) {
+ break;
+ }
+ }
+ token = _parseToken(self, streamGetChar(self));
+ }
+ /*tex wrap up */
+ if (self->_stream) {
+ streamClose(self);
+ }
+ clear_operand_stack(self, 0);
+ free(self->_operandstack);
+ return 0;
+}
+
+static int scanner_done(lua_State * L)
+{
+ int c;
+ scannerdata *self = scanner_check(L, 1);
+ while ((c = streamGetChar(self)) >= 0);
+ return 0;
+}
+
+/*tex here are the stack popping functions, and their helpers */
+
+static void operandstack_backup(scannerdata * self)
+{
+ int i = self->_nextoperand - 1;
+ int balance = 0;
+ int backupstart = 0;
+ int backupstop = self->_operandstack[i]->type;
+ if (backupstop == pdf_stopdict) {
+ backupstart = pdf_startdict;
+ } else if (backupstop == pdf_stoparray) {
+ backupstart = pdf_startarray;
+ } else {
+ return;
+ }
+ for (; i >= 0; i--) {
+ if (self->_operandstack[i]->type == backupstop) {
+ balance++;
+ } else if (self->_operandstack[i]->type == backupstart) {
+ balance--;
+ }
+ if (balance == 0) {
+ break;
+ }
+ }
+ self->_nextoperand = i + 1;
+}
+
+static void push_array(lua_State * L, scannerdata * self)
+{
+ /*tex nesting tracking */
+ int balance = 1;
+ /*tex \LUA\ array index */
+ int index = 1;
+ Token *token = self->_operandstack[self->_nextoperand++];
+ lua_newtable(L);
+ while (token) {
+ if (token->type == pdf_stoparray)
+ balance--;
+ if (token->type == pdf_startarray)
+ balance++;
+ if (!balance) {
+ break;
+ } else {
+ push_token(L, self);
+ lua_rawseti(L, -2, index++);
+ }
+ token = self->_operandstack[self->_nextoperand++];
+ }
+}
+
+
+static void push_dict(lua_State * L, scannerdata * self)
+{
+ /*tex nesting tracking */
+ int balance = 1;
+ /*tex toggle between \LUA\ value and \LUA\ key */
+ int needskey = 1;
+ Token *token = self->_operandstack[self->_nextoperand++];
+ lua_newtable(L);
+ while (token) {
+ if (token->type == pdf_stopdict)
+ balance--;
+ if (token->type == pdf_startdict)
+ balance++;
+ if (!balance) {
+ break;
+ } else if (needskey) {
+ lua_pushlstring(L, token->string, token->value);
+ needskey = 0;
+ } else {
+ push_token(L, self);
+ needskey = 1;
+ lua_rawset(L, -3);
+ }
+ token = self->_operandstack[self->_nextoperand++];
+ }
+}
+
+const char *typenames[pdf_stopdict + 1] = {
+ "unknown", "integer", "real", "boolean", "name", "operator",
+ "string", "array", "array", "dict", "dict"
+};
+
+static void push_token(lua_State * L, scannerdata * self)
+{
+ Token *token = self->_operandstack[self->_nextoperand - 1];
+ lua_createtable(L, 2, 0);
+ lua_pushstring(L, typenames[token->type]);
+ lua_rawseti(L, -2, 1);
+ if (token->type == pdf_string || token->type == pdf_name) {
+ lua_pushlstring(L, token->string, token->value);
+ } else if (token->type == pdf_real || token->type == pdf_integer) {
+ /*tex This is an integer or float. */
+ lua_pushnumber(L, token->value);
+ } else if (token->type == pdf_boolean) {
+ lua_pushboolean(L, (int) token->value);
+ } else if (token->type == pdf_startarray) {
+ push_array(L, self);
+ } else if (token->type == pdf_startdict) {
+ push_dict(L, self);
+ } else {
+ lua_pushnil(L);
+ }
+ lua_rawseti(L, -2, 2);
+}
+
+static int scanner_popsingular(lua_State * L, int token_type)
+{
+ Token *token = NULL;
+ /*tex this keeps track of how much of the operand stack needs deleting: */
+ int clear = 0;
+ scannerdata *self = scanner_check(L, 1);
+ if (self->_nextoperand == 0) {
+ return 0;
+ }
+ clear = self->_nextoperand - 1;
+ token = self->_operandstack[self->_nextoperand - 1];
+ if (token == NULL || (token->type != token_type)) {
+ return 0;
+ }
+ /*tex
+ The simple cases can be written out directly, but dicts and
+ arrays are better done via the recursive function.
+ */
+ if (token_type == pdf_stoparray || token_type == pdf_stopdict) {
+ operandstack_backup(self);
+ clear = self->_nextoperand - 1;
+ push_token(L, self);
+ lua_rawgeti(L, -1, 2);
+ } else if (token_type == pdf_real || token_type == pdf_integer) {
+ /*tex the number can be an integer or float */
+ lua_pushnumber(L, token->value);
+ } else if (token_type == pdf_boolean) {
+ lua_pushboolean(L, (int) token->value);
+ } else if (token_type == pdf_name || token_type == pdf_string) {
+ lua_pushlstring(L, token->string, token->value);
+ } else {
+ return 0;
+ }
+ clear_operand_stack(self, clear);
+ return 1;
+}
+
+static int scanner_popanything(lua_State * L)
+{
+ Token *token = NULL;
+ /*tex how much of the operand stack needs deleting: */
+ int clear = 0;
+ int token_type;
+ scannerdata *self = scanner_check(L, 1);
+ if (self->_nextoperand == 0) {
+ return 0;
+ }
+ clear = self->_nextoperand - 1;
+ token = self->_operandstack[self->_nextoperand - 1];
+ if (token == NULL) {
+ return 0;
+ }
+ token_type = token->type;
+ /*tex
+ The simple cases can be written out directly, but dicts and
+ arrays are better done via the recursive function.
+ */
+ if (token_type == pdf_stoparray || token_type == pdf_stopdict) {
+ operandstack_backup(self);
+ clear = self->_nextoperand - 1;
+ push_token(L, self);
+ } else {
+ push_token(L, self);
+ }
+ clear_operand_stack(self, clear);
+ return 1;
+}
+
+static int scanner_popnumber(lua_State * L)
+{
+ if (scanner_popsingular(L, pdf_real))
+ return 1;
+ if (scanner_popsingular(L, pdf_integer))
+ return 1;
+ lua_pushnil(L);
+ return 1;
+}
+
+static int scanner_popboolean(lua_State * L)
+{
+ if (scanner_popsingular(L, pdf_boolean))
+ return 1;
+ lua_pushnil(L);
+ return 1;
+}
+
+static int scanner_popstring(lua_State * L)
+{
+ if (scanner_popsingular(L, pdf_string))
+ return 1;
+ lua_pushnil(L);
+ return 1;
+}
+
+static int scanner_popname(lua_State * L)
+{
+ if (scanner_popsingular(L, pdf_name))
+ return 1;
+ lua_pushnil(L);
+ return 1;
+}
+
+static int scanner_poparray(lua_State * L)
+{
+ if (scanner_popsingular(L, pdf_stoparray))
+ return 1;
+ lua_pushnil(L);
+ return 1;
+}
+
+static int scanner_popdictionary(lua_State * L)
+{
+ if (scanner_popsingular(L, pdf_stopdict))
+ return 1;
+ lua_pushnil(L);
+ return 1;
+}
+
+static int scanner_popany(lua_State * L)
+{
+ if (scanner_popanything(L))
+ return 1;
+ lua_pushnil(L);
+ return 1;
+}
+
+static const luaL_Reg scannerlib_meta[] = {
+ {0, 0}
+};
+
+static const struct luaL_Reg scannerlib_m[] = {
+ { "done", scanner_done },
+ { "pop", scanner_popany },
+ { "popnumber", scanner_popnumber },
+ { "popname", scanner_popname },
+ { "popstring", scanner_popstring },
+ { "poparray", scanner_poparray },
+ { "popdictionary", scanner_popdictionary },
+ { "popboolean", scanner_popboolean },
+ /*tex For old times sake: */
+ { "popNumber", scanner_popnumber },
+ { "popName", scanner_popname },
+ { "popString", scanner_popstring },
+ { "popArray", scanner_poparray },
+ { "popDict", scanner_popdictionary },
+ { "popBool", scanner_popboolean },
+ /*tex Sentinel: */
+ { NULL, NULL }
+};
+
+static const luaL_Reg scannerlib[] = {
+ { "scan", scanner_scan },
+ /*tex Sentinel: */
+ { NULL, NULL }
+};
+
+LUALIB_API int luaopen_pdfscanner(lua_State * L)
+{
+ luaL_newmetatable(L, SCANNER);
+ luaL_openlib(L, 0, scannerlib_meta, 0);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_openlib(L, NULL, scannerlib_m, 0);
+ luaL_openlib(L, "pdfscanner", scannerlib, 0);
+ return 1;
+}