# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: address@hidden # target_branch: file:///home/michael/src/%2Blocal/gnupdf/trunk/ # testament_sha1: a0d08e3d67e5d8a18f046cc0d6b7dccda30dfd1c # timestamp: 2009-01-21 23:02:21 -0500 # base_revision_id: address@hidden # # Begin patch === modified file 'src/Makefile.am' --- src/Makefile.am 2009-01-11 21:37:29 +0000 +++ src/Makefile.am 2009-01-21 07:33:48 +0000 @@ -93,6 +93,14 @@ $(CRYPT_MODULE_SOURCES) +# Object layer sources + +READER_MODULE_SOURCES = object/pdf-rd-tokeniser.c object/pdf-rd-tokeniser.h \ + object/pdf-rd-parser.c object/pdf-rd-parser.h + +OBJECT_LAYER_SOURCES = object/pdf-obj.c object/pdf-obj.h \ + $(READER_MODULE_SOURCES) + # Library sources libgnupdf_la_SOURCES = pdf-global.c pdf-global.h @@ -100,6 +108,9 @@ if COMPILE_BASE_LAYER libgnupdf_la_SOURCES += $(BASE_LAYER_SOURCES) endif +if COMPILE_OBJECT_LAYER + libgnupdf_la_SOURCES += $(OBJECT_LAYER_SOURCES) +endif include_HEADERS = pdf.h libgnupdf_la_LIBADD = $(top_builddir)/lib/libgnu.la $(LIBGCRYPT_LIBS) @@ -133,7 +144,9 @@ base/pdf-stm.h \ base/pdf-hash-helper.h \ base/pdf-crypt.h \ - base/pdf-fp-func.h + base/pdf-fp-func.h \ + object/pdf-obj.h \ + object/pdf-rd-tokeniser.h pdf.h : $(PUBLIC_HDRS) chmod +x $(top_builddir)/src/extract-public-hdr === modified file 'src/base/pdf-error.h' --- src/base/pdf-error.h 2009-01-14 20:39:58 +0000 +++ src/base/pdf-error.h 2009-01-22 04:01:59 +0000 @@ -92,8 +92,11 @@ ERROR_ENTRY (PDF_EBADSAMPLES, "/FunctionType 0: error while reading sample table"), \ ERROR_ENTRY (PDF_EBADAESKEY, "the size of an AES key should be a multiple of 16"), \ ERROR_ENTRY (PDF_EBADV2KEY, "a V2 key should be at least 40 bits long"), \ - ERROR_ENTRY (PDF_EINVOP, "invalid operation") - + ERROR_ENTRY (PDF_EINVOP, "invalid operation"), \ + ERROR_ENTRY (PDF_EBADFILE, "file violates PDF specifications"), \ + ERROR_ENTRY (PDF_EIMPLLIMIT, "implementation limit exceeded"), \ + ERROR_ENTRY (PDF_EEXIST, "name already in use") + #define ERROR_ENTRY(id,string) id enum pdf_status_e === modified file 'src/object/pdf-obj.c' --- src/object/pdf-obj.c 2008-09-08 21:04:39 +0000 +++ src/object/pdf-obj.c 2009-01-22 03:38:16 +0000 @@ -23,461 +23,156 @@ * along with this program. If not, see . */ -#include - #include - -#ifdef HAVE_MALLOC_H - #include -#else - #include -#endif /* HAVE_MALLOC_H */ - -#include -#include +#include + +#include "config.h" +#include "pdf-obj.h" +#include "pdf-alloc.h" /* Private functions prototypes */ -static pdf_obj_t pdf_alloc_obj (void); -static void pdf_dealloc_obj (pdf_obj_t obj); -static void pdf_dealloc_obj_list_elt (const void *elt); -static bool pdf_compare_obj_list_elt (const void *elt1, const void *elt2); - - -static pdf_dict_entry_t pdf_alloc_dict_entry (void); -static void pdf_dealloc_dict_entry (pdf_dict_entry_t entry); -static void pdf_dealloc_dict_entry_list_elt (const void *elt); -static bool pdf_compare_dict_entry_list_elt (const void *elt1, const void *elt2); - -static int pdf_string_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); -static int pdf_name_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); -static int pdf_array_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); -static int pdf_dict_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); +static INLINE pdf_status_t pdf_obj_new (pdf_obj_type_t type, pdf_obj_t *obj); +static void pdf_obj_child_destroy_cb (const void *obj); +static void pdf_obj_dict_dealloc_key_cb (const void *obj); + +static bool pdf_obj_child_equal_p_cb (const void *elt1, const void *elt2); +static int pdf_obj_buffer_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); +static pdf_bool_t pdf_obj_array_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); +static int pdf_obj_dict_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); static int pdf_stream_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); -static pdf_obj_t pdf_array_dup (pdf_obj_t obj); -static pdf_obj_t pdf_dict_dup (pdf_obj_t obj); -static pdf_obj_t pdf_stream_dup (pdf_obj_t obj); - -/* Public functions */ - -pdf_obj_t -pdf_create_null (void) -{ - pdf_obj_t null_obj; - - null_obj = pdf_alloc_obj (); - null_obj->type = PDF_NULL_OBJ; - - return null_obj; -} - -pdf_obj_t -pdf_create_boolean (int value) -{ - pdf_obj_t bool_obj; - - bool_obj = pdf_alloc_obj (); - bool_obj->type = PDF_BOOLEAN_OBJ; - bool_obj->value.boolean = value; - - return bool_obj; -} - -pdf_obj_t -pdf_create_integer (int value) -{ - pdf_obj_t int_obj; - - int_obj = pdf_alloc_obj (); - int_obj->type = PDF_INT_OBJ; - int_obj->value.integer = value; - - return int_obj; -} - -pdf_obj_t -pdf_create_real (float value) -{ - pdf_obj_t real_obj; - - real_obj = pdf_alloc_obj (); - real_obj->type = PDF_REAL_OBJ; - real_obj->value.real = value; - - return real_obj; -} - -pdf_obj_t -pdf_create_string (unsigned char *value, - int size) -{ - pdf_obj_t string_obj; - - string_obj = pdf_alloc_obj (); - string_obj->type = PDF_STRING_OBJ; - string_obj->value.string.data = - xmalloc (size); - memcpy (string_obj->value.string.data, - value, - size); - string_obj->value.string.size = size; - - return string_obj; -} - -pdf_obj_t -pdf_create_name (unsigned char *value, - int size) -{ - pdf_obj_t name_obj; - - name_obj = pdf_alloc_obj (); - name_obj->type = PDF_NAME_OBJ; - name_obj->value.name.data = - xmalloc (size); - memcpy (name_obj->value.name.data, - value, - size); - name_obj->value.name.size = size; - - return name_obj; -} - -pdf_obj_t -pdf_create_array (void) -{ - pdf_obj_t new_array; - - new_array = pdf_alloc_obj (); - new_array->type = PDF_ARRAY_OBJ; - new_array->value.array.objs = - pdf_list_new (pdf_compare_obj_list_elt, - pdf_dealloc_obj_list_elt, - PDF_TRUE); /* allow duplicates */ - - return new_array; -} - -pdf_obj_t -pdf_create_dict (void) -{ - pdf_obj_t new_dict; - - new_dict = pdf_alloc_obj (); - new_dict->type = PDF_DICT_OBJ; - new_dict->value.dict.entries = - pdf_list_new (pdf_compare_dict_entry_list_elt, - pdf_dealloc_dict_entry_list_elt, - PDF_FALSE); /* disallow duplicates. */ - - return new_dict; -} - -pdf_obj_t -pdf_create_indirect (unsigned int on, - unsigned int gn) -{ - pdf_obj_t new_indirect; - - new_indirect = pdf_alloc_obj (); - new_indirect->type = PDF_INDIRECT_OBJ; - new_indirect->value.indirect.on = on; - new_indirect->value.indirect.gn = gn; - - return new_indirect; -} - -pdf_obj_t -pdf_create_stream (pdf_obj_t dict, - pdf_stm_t stm, - pdf_stm_pos_t data) -{ - pdf_obj_t new_stream; - - new_stream = pdf_alloc_obj (); - new_stream->type = PDF_STREAM_OBJ; - new_stream->value.stream.dict = pdf_obj_dup (dict); - new_stream->value.stream.stm = stm; - new_stream->value.stream.data = data; - - return new_stream; -} - -inline int -pdf_destroy_obj (pdf_obj_t obj) -{ - pdf_dealloc_obj (obj); - return PDF_OK; -} - -inline int -pdf_get_bool (pdf_obj_t obj) -{ +static INLINE pdf_status_t pdf_obj_array_dup (pdf_obj_t obj, pdf_obj_t *new); +static INLINE pdf_status_t pdf_obj_dict_dup (pdf_obj_t obj, pdf_obj_t *new); +static INLINE pdf_status_t pdf_obj_stream_dup (pdf_obj_t obj, pdf_obj_t *new); + +static pdf_status_t pdf_obj_array_ins_priv (pdf_obj_t array, pdf_size_t index, + pdf_obj_t obj, int atend); + + +/* General functions */ + +pdf_status_t +pdf_obj_null_new (pdf_obj_t *obj) +{ + return pdf_obj_new (PDF_NULL_OBJ, obj); +} + +pdf_status_t +pdf_obj_boolean_new (pdf_bool_t value, pdf_obj_t *obj) +{ + pdf_status_t rv = pdf_obj_new (PDF_BOOLEAN_OBJ, obj); + if (rv == PDF_OK) + (*obj)->value.boolean = value; + + return rv; +} + +pdf_status_t +pdf_obj_integer_new (int value, pdf_obj_t *obj) +{ + pdf_status_t rv = pdf_obj_new (PDF_INT_OBJ, obj); + if (rv == PDF_OK) + (*obj)->value.integer = value; + + return rv; +} + +pdf_status_t +pdf_obj_real_new (float value, pdf_obj_t *obj) +{ + pdf_status_t rv = pdf_obj_new (PDF_REAL_OBJ, obj); + if (rv == PDF_OK) + (*obj)->value.real = value; + + return rv; +} + +pdf_status_t +pdf_obj_indirect_new (unsigned int on, + unsigned int gn, + pdf_obj_t *obj) +{ + pdf_status_t rv = pdf_obj_new (PDF_INDIRECT_OBJ, obj); + if (rv == PDF_OK) + { + (*obj)->value.indirect.on = on; + (*obj)->value.indirect.gn = gn; + } + return rv; +} + +pdf_status_t +pdf_tok_valueless_new (pdf_obj_type_t type, + pdf_obj_t *obj) +{ + return pdf_obj_new (type, obj); +} + +pdf_obj_type_t +pdf_obj_type (const pdf_obj_t obj) +{ + assert (obj); + return obj->type; +} + +pdf_bool_t +pdf_obj_bool_value (const pdf_obj_t obj) +{ + assert (obj && obj->type == PDF_BOOLEAN_OBJ); return obj->value.boolean; } -inline void -pdf_set_bool (pdf_obj_t obj, - int value) -{ - obj->value.boolean = value; -} - -inline int -pdf_get_int (pdf_obj_t obj) -{ +int +pdf_obj_int_value (const pdf_obj_t obj) +{ + assert (obj && obj->type == PDF_INT_OBJ); return obj->value.integer; } -inline void -pdf_set_int (pdf_obj_t obj, - int value) -{ - obj->value.integer = value; -} - -inline pdf_real_t -pdf_get_real (pdf_obj_t obj) -{ +pdf_real_t +pdf_obj_real_value (const pdf_obj_t obj) +{ + assert (obj && obj->type == PDF_REAL_OBJ); return obj->value.real; } -inline void -pdf_set_real (pdf_obj_t obj, - float value) -{ - obj->value.real = value; -} - -inline int -pdf_get_string_size (pdf_obj_t obj) -{ - return obj->value.string.size; -} - -inline char * -pdf_get_string_data (pdf_obj_t obj) -{ - char *data; - - data = xmalloc (obj->value.string.size); - memcpy (data, - obj->value.string.data, - obj->value.string.size); - - return data; -} - -inline int -pdf_get_name_size (pdf_obj_t obj) -{ - return obj->value.name.size; -} - -inline char * -pdf_get_name_data (pdf_obj_t obj) -{ - char *data; - - data = xmalloc (obj->value.name.size); - memcpy (data, - obj->value.name.data, - obj->value.string.size); - - return data; -} - -inline int -pdf_get_array_size (pdf_obj_t obj) -{ - return pdf_list_size (obj->value.array.objs); -} - -inline int -pdf_get_dict_size (pdf_obj_t obj) -{ - return pdf_list_size (obj->value.dict.entries); -} - -int -pdf_dict_key_p (pdf_obj_t obj, - pdf_obj_t key) -{ - int entry_p; - pdf_dict_entry_t entry; - - if ((obj->type != PDF_DICT_OBJ) || - (key->type != PDF_NAME_OBJ) || - (pdf_list_size (obj->value.dict.entries) == 0)) - { - return PDF_FALSE; - } - - entry = pdf_alloc_dict_entry (); - entry->key = pdf_obj_dup (key); - entry->value = pdf_create_null (); - - if (pdf_list_search (obj->value.dict.entries, - (const void *) entry) != NULL) - { - entry_p = PDF_TRUE; - } - else - { - entry_p = PDF_FALSE; - } - - pdf_dealloc_dict_entry (entry); - return entry_p; -} - -pdf_obj_t -pdf_get_dict_entry (pdf_obj_t obj, - pdf_obj_t key) -{ - pdf_dict_entry_t entry; - pdf_dict_entry_t result_entry; - pdf_list_node_t list_node; - - if ((obj->type != PDF_DICT_OBJ) || - (key->type != PDF_NAME_OBJ) || - (pdf_list_size (obj->value.dict.entries) == 0)) - { - return NULL; - } - - entry = pdf_alloc_dict_entry (); - entry->key = pdf_obj_dup (key); - entry->value = pdf_create_null (); - - list_node = pdf_list_search (obj->value.dict.entries, - entry); - pdf_dealloc_dict_entry (entry); - - if (list_node == NULL) - { - return NULL; - } - else - { - result_entry = (pdf_dict_entry_t) - pdf_list_node_value (obj->value.dict.entries, list_node); - - return result_entry->value; - } - - /* Not reached */ -} - -int -pdf_remove_dict_entry (pdf_obj_t obj, - pdf_obj_t key) -{ - int status; - pdf_dict_entry_t entry; - - if (!pdf_dict_key_p (obj, key)) - { - return PDF_ERROR; - } - - entry = pdf_alloc_dict_entry (); - entry->key = pdf_obj_dup (key); - entry->value = pdf_create_null (); - - if (pdf_list_remove (obj->value.dict.entries, - entry)) - { - status = PDF_OK; - } - else - { - status = PDF_ERROR; - } - - pdf_dealloc_dict_entry (entry); - - return status; -} - -int -pdf_create_dict_entry (pdf_obj_t obj, - pdf_obj_t key, - pdf_obj_t value) -{ - pdf_dict_entry_t entry; - - if ((obj->type != PDF_DICT_OBJ) || - (key->type != PDF_NAME_OBJ) || - (pdf_dict_key_p (obj, key))) - { - return PDF_ERROR; - } - - /* Create a new dictionary entry */ - entry = pdf_alloc_dict_entry (); - entry->key = key; - entry->value = value; - if (pdf_list_add_last (obj->value.dict.entries, - entry) == NULL) - { - pdf_dealloc_dict_entry (entry); - return PDF_ERROR; - } - else - { - return PDF_OK; - } -} - -pdf_obj_t -pdf_get_stream_dict (pdf_obj_t stream) -{ - if (stream->type != PDF_STREAM_OBJ) - { - return NULL; - } - - return pdf_obj_dup(stream->value.stream.dict); -} - -pdf_stm_t -pdf_get_stream_stm (pdf_obj_t stream) -{ - if (stream->type != PDF_STREAM_OBJ) - { - return NULL; - } - - return stream->value.stream.stm; -} - -pdf_stm_pos_t -pdf_get_stream_data (pdf_obj_t stream) -{ - if (stream->type != PDF_STREAM_OBJ) - { - return NO_POS; - } - - return stream->value.stream.data; -} - -int +unsigned int +pdf_obj_indirect_on (const pdf_obj_t obj) +{ + assert (obj && obj->type == PDF_INDIRECT_OBJ); + return obj->value.indirect.on; +} + +unsigned int +pdf_obj_indirect_gn (const pdf_obj_t obj) +{ + assert (obj && obj->type == PDF_INDIRECT_OBJ); + return obj->value.indirect.gn; +} + +pdf_bool_t pdf_obj_equal_p (pdf_obj_t obj1, pdf_obj_t obj2) { int equal_p; + assert (obj1); + assert (obj2); - if (obj1->type != obj2->type) - { - return PDF_FALSE; - } + if (obj1 == obj2) + return PDF_TRUE; + else if (obj1->type != obj2->type) + return PDF_FALSE; switch (obj1->type) { - case PDF_NULL_OBJ: + case PDF_NULL_OBJ: /* fall through */ + case PDF_DICT_START_TOK: /* fall through */ + case PDF_DICT_END_TOK: /* fall through */ + case PDF_ARRAY_START_TOK: /* fall through */ + case PDF_ARRAY_END_TOK: /* fall through */ + case PDF_PROC_START_TOK: /* fall through */ + case PDF_PROC_END_TOK: { equal_p = PDF_TRUE; break; @@ -497,31 +192,34 @@ equal_p = (obj1->value.real == obj2->value.real); break; } - case PDF_STRING_OBJ: + case PDF_STRING_OBJ: /* fall through */ + case PDF_NAME_OBJ: /* fall through */ + case PDF_KEYWORD_TOK: { - equal_p = pdf_string_equal_p (obj1, obj2); + equal_p = pdf_obj_buffer_equal_p (obj1, obj2); break; } - case PDF_NAME_OBJ: + case PDF_COMMENT_TOK: { - equal_p = pdf_name_equal_p (obj1, obj2); + equal_p = ((obj1->value.comment.continuation + == obj2->value.comment.continuation) + && pdf_obj_buffer_equal_p (obj1, obj2)); break; } case PDF_ARRAY_OBJ: { - equal_p = pdf_array_equal_p (obj1, obj2); + equal_p = pdf_obj_array_equal_p (obj1, obj2); break; } case PDF_DICT_OBJ: { - equal_p = pdf_dict_equal_p (obj1, obj2); + equal_p = pdf_obj_dict_equal_p (obj1, obj2); break; } case PDF_INDIRECT_OBJ: { - equal_p = - (obj1->value.indirect.on == obj2->value.indirect.on) && - (obj1->value.indirect.gn == obj2->value.indirect.gn); + equal_p = (obj1->value.indirect.on == obj2->value.indirect.on + && obj1->value.indirect.gn == obj2->value.indirect.gn); break; } case PDF_STREAM_OBJ: @@ -539,186 +237,105 @@ return equal_p; } -pdf_obj_t -pdf_obj_dup (pdf_obj_t obj) +pdf_status_t +pdf_obj_dup (const pdf_obj_t obj, pdf_obj_t *new) { - pdf_obj_t new_obj; - + assert (obj); switch (obj->type) { - case PDF_NULL_OBJ: - { - new_obj = pdf_create_null(); - break; - } + case PDF_NULL_OBJ: /* fall through */ + case PDF_DICT_START_TOK: /* fall through */ + case PDF_DICT_END_TOK: /* fall through */ + case PDF_ARRAY_START_TOK: /* fall through */ + case PDF_ARRAY_END_TOK: /* fall through */ + case PDF_PROC_START_TOK: /* fall through */ + case PDF_PROC_END_TOK: + return pdf_tok_valueless_new (obj->type, new); + case PDF_BOOLEAN_OBJ: - { - new_obj = pdf_create_boolean (GET_BOOL(obj)); - break; - } + return pdf_obj_boolean_new (GET_BOOL(obj), new); + case PDF_INT_OBJ: - { - new_obj = pdf_create_integer (GET_INT(obj)); - break; - } + return pdf_obj_integer_new (GET_INT(obj), new); + case PDF_REAL_OBJ: - { - new_obj = pdf_create_real (GET_REAL(obj)); - break; - } + return pdf_obj_real_new (GET_REAL(obj), new); + case PDF_STRING_OBJ: - { - new_obj = pdf_create_string (obj->value.string.data, - obj->value.string.size); - break; - } + return pdf_obj_string_new (obj->value.buffer.data, + obj->value.buffer.size, + new); case PDF_NAME_OBJ: - { - new_obj = pdf_create_name (obj->value.name.data, - obj->value.name.size); - break; - } + return pdf_obj_name_new (obj->value.buffer.data, + obj->value.buffer.size, + new); + case PDF_KEYWORD_TOK: + return pdf_tok_keyword_new (obj->value.buffer.data, + obj->value.buffer.size, + new); + case PDF_COMMENT_TOK: + return pdf_tok_comment_new (obj->value.comment.data, + obj->value.comment.size, + obj->value.comment.continuation, + new); case PDF_ARRAY_OBJ: - { - new_obj = pdf_array_dup (obj); - break; - } + return pdf_obj_array_dup (obj, new); + case PDF_DICT_OBJ: - { - new_obj = pdf_dict_dup (obj); - break; - } + return pdf_obj_dict_dup (obj, new); + case PDF_INDIRECT_OBJ: - { - new_obj = pdf_create_indirect (obj->value.indirect.on, - obj->value.indirect.gn); - break; - } + return pdf_obj_indirect_new (obj->value.indirect.on, + obj->value.indirect.gn, + new); case PDF_STREAM_OBJ: - { - new_obj = pdf_stream_dup (obj); - break; - } + return pdf_obj_stream_dup (obj, new); + default: - { - /* Should not be reached: make the compiler happy */ - new_obj = NULL; - break; - } - } - - return new_obj; -} - -int -pdf_remove_array_elt (pdf_obj_t obj, - int index) -{ - if ((obj->type != PDF_ARRAY_OBJ) || - (index < 0) || - (index >= pdf_list_size (obj->value.array.objs))) - { - return PDF_ERROR; - } - - pdf_list_remove_at (obj->value.array.objs, index); - return PDF_OK; -} - -pdf_obj_t -pdf_get_array_elt (pdf_obj_t obj, - int index) -{ - if ((obj->type != PDF_ARRAY_OBJ) || - (index < 0) || - (index >= pdf_list_size (obj->value.array.objs))) - { - return NULL; - } - - return (pdf_obj_t) pdf_list_get_at (obj->value.array.objs, index); -} - -int -pdf_set_array_elt (pdf_obj_t obj, - int index, - pdf_obj_t elt) -{ - if ((obj->type != PDF_ARRAY_OBJ) || - (index < 0) || - (index >= pdf_list_size (obj->value.array.objs))) - { - return PDF_ERROR; - } - - pdf_list_set_at (obj->value.array.objs, - index, - elt); - return PDF_OK; -} - -int -pdf_add_array_elt (pdf_obj_t obj, - int index, - pdf_obj_t elt) -{ - if ((obj->type != PDF_ARRAY_OBJ) || - (index < 0) || - (index > pdf_list_size (obj->value.array.objs))) - { - return PDF_ERROR; - } - - pdf_list_add_at (obj->value.array.objs, - index, - elt); - - return PDF_OK; -} - -inline int -pdf_append_array_elt (pdf_obj_t obj, - pdf_obj_t elt) -{ - return pdf_add_array_elt (obj, - pdf_get_array_size (obj), - elt); + /* Should not be reached: make the compiler happy */ + return PDF_EBADDATA; + } } /* Private functions */ -static pdf_obj_t -pdf_alloc_obj (void) +static INLINE pdf_status_t +pdf_obj_new (pdf_obj_type_t type, pdf_obj_t *obj) { pdf_obj_t new_obj; - - new_obj = (pdf_obj_t) xmalloc (sizeof(struct pdf_obj_s)); - return new_obj; + assert (obj); + + new_obj = (pdf_obj_t) pdf_alloc (sizeof (struct pdf_obj_s)); + if (!new_obj) + return PDF_ENOMEM; + + new_obj->type = type; + *obj = new_obj; + return PDF_OK; } -static void -pdf_dealloc_obj (pdf_obj_t obj) +pdf_status_t +pdf_obj_destroy (pdf_obj_t obj) { + assert (obj); switch (obj->type) { - case PDF_STRING_OBJ: - { - free (obj->value.string.data); - break; - } - case PDF_NAME_OBJ: - { - free (obj->value.name.data); + case PDF_STRING_OBJ: /* fall through */ + case PDF_NAME_OBJ: /* fall through */ + case PDF_KEYWORD_TOK: /* fall through */ + case PDF_COMMENT_TOK: + { + pdf_dealloc (obj->value.buffer.data); break; } case PDF_ARRAY_OBJ: { - pdf_list_destroy (obj->value.array.objs); + pdf_list_destroy (obj->value.array); break; } case PDF_DICT_OBJ: { - pdf_list_destroy (obj->value.dict.entries); + pdf_hash_destroy (obj->value.dict); break; } default: @@ -728,135 +345,640 @@ } } - free (obj); -} - -void -pdf_dealloc_obj_list_elt (const void* elt) -{ - pdf_dealloc_obj ((pdf_obj_t) elt); -} - -static bool -pdf_compare_obj_list_elt (const void *elt1, - const void *elt2) -{ - return pdf_obj_equal_p ((pdf_obj_t) elt1, - (pdf_obj_t) elt2); -} - - -static pdf_dict_entry_t -pdf_alloc_dict_entry (void) -{ - pdf_dict_entry_t entry; - - entry = (pdf_dict_entry_t) xmalloc (sizeof(struct pdf_dict_entry_s)); - return entry; -} - -static void -pdf_dealloc_dict_entry (pdf_dict_entry_t entry) -{ - pdf_dealloc_obj (entry->key); - pdf_dealloc_obj (entry->value); - free (entry); + pdf_dealloc (obj); + return PDF_OK; +} + + + +/*** streams **************************************************/ + +pdf_status_t +pdf_obj_stream_new (pdf_obj_t dict, + pdf_stm_t stm, + pdf_off_t data, + pdf_obj_t *obj) +{ + pdf_status_t rv = pdf_obj_new (PDF_STREAM_OBJ, obj); + if (rv == PDF_OK) + { + (*obj)->value.stream.dict = dict; + (*obj)->value.stream.stm = stm; + (*obj)->value.stream.data = data; + } + return rv; +} + +/* Note that the new stream will use the same stm */ +//TODO: avoid storing stm? rename .data to .offset +static INLINE pdf_status_t +pdf_obj_stream_dup (pdf_obj_t obj, pdf_obj_t *new) +{ + pdf_status_t rv; + pdf_obj_t new_dict = NULL; + + rv = pdf_obj_dup (obj->value.stream.dict, &new_dict); + if (rv != PDF_OK) + goto fail; + + rv = pdf_obj_stream_new (new_dict, + obj->value.stream.stm, + obj->value.stream.data, + new); + if (rv != PDF_OK) + goto fail; + + return PDF_OK; + +fail: + if (new_dict) + pdf_obj_destroy (new_dict); + return rv; +} + +/* Two PDF streams are considered equal if both uses the same stm + object, its dictionaries are equal and the data pointer points to + the same position into the stm */ +static int +pdf_stream_equal_p (pdf_obj_t obj1, + pdf_obj_t obj2) +{ + return ((obj1->value.stream.stm == obj2->value.stream.stm) + && (obj1->value.stream.data == obj2->value.stream.data) + && pdf_obj_dict_equal_p (obj1->value.stream.dict, + obj2->value.stream.dict)); +} + +pdf_obj_t +pdf_obj_stream_dict (pdf_obj_t stream) +{ + assert (stream && stream->type == PDF_STREAM_OBJ); + if (stream->type != PDF_STREAM_OBJ) + { + return NULL; + } + + return stream->value.stream.dict; +} + +pdf_stm_t +pdf_obj_stream_stm (pdf_obj_t stream) +{ + assert (stream && stream->type == PDF_STREAM_OBJ); + if (stream->type != PDF_STREAM_OBJ) + { + return NULL; + } + + return stream->value.stream.stm; +} + +pdf_off_t +pdf_obj_stream_data (pdf_obj_t stream) +{ + assert (stream && stream->type == PDF_STREAM_OBJ); + if (stream->type != PDF_STREAM_OBJ) + { + return NO_POS; + } + + return stream->value.stream.data; +} + + + +/*** objects with buffers *************************************/ + +static pdf_status_t +pdf_obj_buffer_new (pdf_obj_type_t type, + const pdf_char_t *value, + pdf_size_t size, + pdf_bool_t nullterm, + pdf_obj_t *obj) +{ + pdf_obj_t new_obj = NULL; + pdf_status_t rv = pdf_obj_new (type, &new_obj); + if (rv != PDF_OK) + goto fail; + + rv = PDF_ENOMEM; + new_obj->value.buffer.data = pdf_alloc (nullterm ? size + 1 : size); + if (!new_obj->value.buffer.data) + goto fail; + + new_obj->value.buffer.size = size; + memcpy (new_obj->value.buffer.data, value, size); + if (nullterm) + new_obj->value.buffer.data[size] = 0; + + *obj = new_obj; + return PDF_OK; + +fail: + if (new_obj) + pdf_dealloc (new_obj); + return rv; +} + +static int +pdf_obj_buffer_equal_p (pdf_obj_t obj1, + pdf_obj_t obj2) +{ + struct pdf_obj_buffer_s *buf1 = &obj1->value.buffer; + struct pdf_obj_buffer_s *buf2 = &obj2->value.buffer; + return (buf1->size == buf2->size + && ( buf1->data == buf2->data + || !memcmp (buf1->data, buf2->data, buf1->size) )); +} + + +/** names *****/ + +pdf_status_t +pdf_obj_name_new (const pdf_char_t *value, + pdf_size_t size, + pdf_obj_t *obj) +{ + return pdf_obj_buffer_new (PDF_NAME_OBJ, value, size, 1, obj); +} + +pdf_size_t +pdf_obj_name_size (pdf_obj_t name) +{ + assert (name && name->type == PDF_NAME_OBJ); + return name->value.buffer.size; +} + +const pdf_char_t * +pdf_obj_name_data (pdf_obj_t name) +{ + assert (name && name->type == PDF_NAME_OBJ); + return name->value.buffer.data; +} + +pdf_bool_t +pdf_obj_name_equal_p (pdf_obj_t obj, const pdf_char_t *data) +{ + size_t len = strlen ((const char*)data); + return (obj && obj->type == PDF_NAME_OBJ + && len == obj->value.buffer.size + && memcmp (obj->value.buffer.data, data, len+1) == 0); +} + + +/** strings *****/ + +pdf_status_t +pdf_obj_string_new (const pdf_char_t *value, + pdf_size_t size, + pdf_obj_t *obj) +{ + return pdf_obj_buffer_new (PDF_STRING_OBJ, value, size, 0, obj); +} + +pdf_size_t +pdf_obj_string_size (pdf_obj_t obj) +{ + assert (obj && obj->type == PDF_STRING_OBJ); + return obj->value.buffer.size; +} + +const pdf_char_t * +pdf_obj_string_data (pdf_obj_t obj) +{ + assert (obj && obj->type == PDF_STRING_OBJ); + return obj->value.buffer.data; +} + + +/** comments *****/ + +pdf_status_t +pdf_tok_comment_new (const pdf_char_t *value, + pdf_size_t size, + pdf_bool_t continuation, + pdf_obj_t *obj) +{ + pdf_status_t rv = pdf_obj_buffer_new (PDF_COMMENT_TOK, value, size, 0, obj); + if (rv == PDF_OK) + (*obj)->value.comment.continuation = !!continuation; + + return rv; +} + +pdf_size_t +pdf_tok_comment_size (pdf_obj_t comment) +{ + assert (comment && comment->type == PDF_COMMENT_TOK); + return comment->value.buffer.size; +} + +const pdf_char_t * +pdf_tok_comment_data (pdf_obj_t comment) +{ + assert (comment && comment->type == PDF_COMMENT_TOK); + return comment->value.buffer.data; +} + + +/** keywords *****/ + +pdf_status_t +pdf_tok_keyword_new (const pdf_char_t *value, + pdf_size_t size, + pdf_obj_t *obj) +{ + return pdf_obj_buffer_new (PDF_KEYWORD_TOK, value, size, 1, obj); +} + +pdf_size_t +pdf_tok_keyword_size (pdf_obj_t keyword) +{ + assert (keyword && keyword->type == PDF_KEYWORD_TOK); + return keyword->value.buffer.size; +} + +const pdf_char_t * +pdf_tok_keyword_data (pdf_obj_t keyword) +{ + assert (keyword && keyword->type == PDF_KEYWORD_TOK); + return keyword->value.buffer.data; +} + +pdf_bool_t +pdf_tok_keyword_equal_p (pdf_obj_t obj, const pdf_char_t *data) +{ + size_t len = strlen ((const char*)data); + return (obj && obj->type == PDF_KEYWORD_TOK + && len == obj->value.buffer.size + && memcmp (obj->value.buffer.data, data, len+1) == 0); +} + + + +/*** static functions for container types (array/dict) ********/ + +static pdf_status_t +pdf_obj_child_new (pdf_obj_t key, pdf_obj_t value, pdf_obj_child_t *elt) +{ + pdf_obj_child_t new = pdf_alloc (sizeof (*new)); + if (!new) + return PDF_ENOMEM; + + new->owned = 1; + new->key = key; + assert (value); + new->value = value; + + assert (elt); + *elt = new; + return PDF_OK; } static void -pdf_dealloc_dict_entry_list_elt (const void *elt) +pdf_obj_child_destroy_cb (const void *ptr) { - pdf_dealloc_dict_entry ((pdf_dict_entry_t) elt); + pdf_obj_child_t elt = (pdf_obj_child_t)ptr; + if (elt->owned) + { + elt->owned = 0; + pdf_obj_destroy (elt->value); + if (elt->key) + pdf_obj_destroy (elt->key); + } + pdf_dealloc (elt); } static bool -pdf_compare_dict_entry_list_elt (const void *elt1, - const void *elt2) -{ - pdf_dict_entry_t entry1; - pdf_dict_entry_t entry2; - - entry1 = (pdf_dict_entry_t) elt1; - entry2 = (pdf_dict_entry_t) elt2; - - return pdf_obj_equal_p (entry1->key, entry2->key); -} - -static int -pdf_string_equal_p (pdf_obj_t obj1, - pdf_obj_t obj2) -{ - return ((obj1->value.string.size == obj2->value.string.size) && - (!memcmp (obj1->value.string.data, - obj2->value.string.data, - obj1->value.string.size))); -} - -static int -pdf_name_equal_p (pdf_obj_t obj1, - pdf_obj_t obj2) -{ - return ((obj1->value.name.size == obj2->value.name.size) && - (!memcmp (obj1->value.name.data, - obj2->value.name.data, - obj1->value.name.size))); -} +pdf_obj_child_equal_p_cb (const void *ptr1, + const void *ptr2) +{ + pdf_obj_child_t elt1 = (pdf_obj_child_t)ptr1; + pdf_obj_child_t elt2 = (pdf_obj_child_t)ptr2; + return pdf_obj_equal_p (elt1->value, elt2->value); +} + + + +/*** arrays ***************************************************/ + +pdf_status_t +pdf_obj_array_new (pdf_obj_t *array) +{ + pdf_status_t rv; + pdf_obj_t new_array = NULL; + + rv = pdf_obj_new (PDF_ARRAY_OBJ, &new_array); + if (rv != PDF_OK) + goto fail; + + rv = pdf_list_new (pdf_obj_child_equal_p_cb, + pdf_obj_child_destroy_cb, + PDF_TRUE, /* allow duplicates */ + &new_array->value.array); + if (rv != PDF_OK) + goto fail; + + assert (array); + *array = new_array; + return PDF_OK; + +fail: + if (new_array) + pdf_dealloc (new_array); + return rv; +} + + +static pdf_status_t +pdf_obj_array_dup (pdf_obj_t array, pdf_obj_t *new) +{ +return PDF_ERROR;//TODO +#if 0 + pdf_obj_t new_array; + pdf_obj_t obj_elt; + pdf_list_iterator_t iter; + pdf_list_node_t list_node; + + + new_array = pdf_create_array (); + + iter = pdf_list_iterator (obj->value.array); + while (pdf_list_iterator_next (&iter, (const void**) &obj_elt, &list_node)) + { + pdf_list_add_last (new_array->value.array, + pdf_obj_dup (obj_elt)); + } + pdf_list_iterator_free (&iter); + + return new_array; +#endif +} + + +pdf_size_t +pdf_obj_array_size (pdf_obj_t array) +{ + assert (array && array->type == PDF_ARRAY_OBJ); + return pdf_list_size (array->value.array); +} + /* Two PDF arrays are considered equal if the equal-intersection between the two sets of objects is empty and if the objects are contained in the same order */ -static int -pdf_array_equal_p (pdf_obj_t obj1, - pdf_obj_t obj2) +static pdf_bool_t +pdf_obj_array_equal_p (pdf_obj_t array1, + pdf_obj_t array2) { int equal_p; - pdf_obj_t obj_elt1; - pdf_obj_t obj_elt2; - pdf_list_node_t list_node1; - pdf_list_node_t list_node2; - pdf_list_iterator_t iter1; - pdf_list_iterator_t iter2; + pdf_status_t rv; + pdf_obj_child_t elt1, elt2; + pdf_list_node_t node1, node2; + pdf_list_iterator_t iter1, iter2; - if ((pdf_list_size (obj1->value.array.objs) != - pdf_list_size (obj2->value.array.objs))) + if (pdf_list_size (array1->value.array) + != pdf_list_size (array2->value.array)) { return PDF_FALSE; } - if (pdf_list_size (obj1->value.array.objs) == 0) + if (pdf_list_size (array1->value.array) == 0) { return PDF_TRUE; } + equal_p = PDF_FALSE; + + /* FIXME: this shouldn't be able to fail */ + + rv = pdf_list_iterator (array1->value.array, &iter1); + if (rv != PDF_OK) + goto free1; + + rv = pdf_list_iterator (array2->value.array, &iter2); + if (rv != PDF_OK) + goto free2; + equal_p = PDF_TRUE; - - iter1 = pdf_list_iterator (obj1->value.array.objs); - iter2 = pdf_list_iterator (obj2->value.array.objs); - - while (pdf_list_iterator_next (&iter1, (const void **) &obj_elt1, &list_node1) && - pdf_list_iterator_next (&iter2, (const void **) &obj_elt2, &list_node2)) + while (pdf_list_iterator_next (&iter1, (const void **) &elt1, &node1) + && pdf_list_iterator_next (&iter2, (const void **) &elt2, &node2)) { /* Note the indirect recursion there => avoid loops!!! */ - if (!pdf_obj_equal_p (obj_elt1, obj_elt2)) + if (!pdf_obj_equal_p (elt1->value, elt2->value)) { equal_p = PDF_FALSE; break; } } - + +free2: + pdf_list_iterator_free (&iter2); +free1: pdf_list_iterator_free (&iter1); - pdf_list_iterator_free (&iter2); return equal_p; } + +pdf_status_t pdf_obj_array_get (const pdf_obj_t array, + pdf_size_t index, + pdf_obj_t *obj) +{ + pdf_status_t rv; + pdf_obj_child_t elt; + if (array->type != PDF_ARRAY_OBJ) + return PDF_EBADDATA; + + assert (obj); + rv = pdf_list_get_at (array->value.array, index, (const void**)&elt); + if (rv == PDF_OK) + *obj = elt->value; + + return rv; +} + + +pdf_status_t pdf_obj_array_set (pdf_obj_t array, + pdf_size_t index, + const pdf_obj_t new_obj, + pdf_obj_t *old_obj) +{ + pdf_status_t rv; + pdf_obj_child_t elt; + if (array->type != PDF_ARRAY_OBJ) + return PDF_EBADDATA; + + rv = pdf_list_get_at (array->value.array, index, (const void**)&elt); + if (rv != PDF_OK) + return rv; + + assert (!elt->owned); + if (old_obj) + *old_obj = elt->value; + else + { + elt->owned = 0; + pdf_obj_destroy (elt->value); + } + + elt->owned = 1; + elt->value = new_obj; + return PDF_OK; +} + + +pdf_status_t +pdf_obj_array_ins_priv (pdf_obj_t array, + pdf_size_t index, + pdf_obj_t obj, + int atend) +{ + pdf_status_t rv; + pdf_obj_child_t elt = NULL; + pdf_size_t size; + + if (array->type != PDF_ARRAY_OBJ) + return PDF_EBADDATA; + + size = pdf_list_size (array->value.array); + if (atend) + index = size; + + rv = pdf_obj_child_new (NULL, obj, &elt); + if (rv != PDF_OK) + goto fail; + + /* add_at doesn't work for the last element */ + if (index == size) + rv = pdf_list_add_last (array->value.array, elt, NULL); + else + rv = pdf_list_add_at (array->value.array, index, elt, NULL); + + if (rv != PDF_OK) + goto fail; + + return PDF_OK; + +fail: + if (elt) + pdf_dealloc (elt); + return rv; +} + +pdf_status_t +pdf_obj_array_insert (pdf_obj_t array, + pdf_size_t index, + pdf_obj_t obj) +{ + return pdf_obj_array_ins_priv (array, index, obj, 0 /* use index */); +} + +pdf_status_t +pdf_obj_array_append (pdf_obj_t array, + const pdf_obj_t obj) +{ + return pdf_obj_array_ins_priv (array, 0 /*unused*/, obj, 1 /*at end*/); +} + + +pdf_status_t +pdf_obj_array_remove (pdf_obj_t array, + pdf_size_t index, + pdf_obj_t *obj) +{ + if (array->type != PDF_ARRAY_OBJ) + return PDF_EBADDATA; + + if (obj) + { + pdf_status_t rv; + pdf_obj_child_t elt; + + rv = pdf_list_get_at (array->value.array, index, (const void**)&elt); + if (rv != PDF_OK) + return rv; + + *obj = elt->value; + assert (elt->owned); + elt->owned = 0; + } + + return pdf_list_remove_at (array->value.array, index); +} + + +pdf_status_t +pdf_obj_array_clear (pdf_obj_t array) +{ + pdf_size_t size = pdf_obj_array_size (array); + while (size) + { + if (pdf_obj_array_remove (array, --size, NULL) != PDF_OK) + return PDF_ERROR; /* shouldn't happen */ + } + return PDF_OK; +} + +pdf_status_t +pdf_obj_array_clear_nodestroy (pdf_obj_t array) +{ + /* Clear the array, but don't free its objects (presumably because + * they're still owned by someone else). */ + + pdf_size_t size = pdf_obj_array_size (array); + while (size) + { + pdf_obj_t dummy; + if (pdf_obj_array_remove (array, --size, &dummy) != PDF_OK) + return PDF_ERROR; /* shouldn't happen */ + } + return PDF_OK; +} + + + +/*** dictionaries *********************************************/ + +pdf_status_t +pdf_obj_dict_new (pdf_obj_t *obj) +{ + pdf_status_t rv; + pdf_obj_t new_dict = NULL; + + rv = pdf_obj_new (PDF_DICT_OBJ, &new_dict); + if (rv != PDF_OK) + goto fail; + + rv = pdf_hash_new (pdf_obj_dict_dealloc_key_cb, + &new_dict->value.dict); + if (rv != PDF_OK) + goto fail; + + *obj = new_dict; + return PDF_OK; + +fail: + if (new_dict) + pdf_dealloc (new_dict); + return rv; +} + +static void +pdf_obj_dict_dealloc_key_cb (const void *obj) +{ + /* key is deallocated with its value in pdf_obj_child_destroy_cb */ +} + /* Two PDF dictionaries are considered equal if the equal-intersection between the two sets of objects is empty. Internal ordering doesnt matter. */ static int -pdf_dict_equal_p (pdf_obj_t obj1, - pdf_obj_t obj2) +pdf_obj_dict_equal_p (pdf_obj_t obj1, + pdf_obj_t obj2) { +return 0; +#if 0 int equal_p; pdf_list_t int_list; pdf_list_node_t list_node1; @@ -915,45 +1037,16 @@ /* Bye bye */ return equal_p; -} - -/* Two PDF streams are considered equal if both uses the same stm - object, its dictionaries are equal and the data pointer points to - the same position into the stm */ -static int -pdf_stream_equal_p (pdf_obj_t obj1, - pdf_obj_t obj2) -{ - return ((obj1->value.stream.stm == obj2->value.stream.stm) && - (obj1->value.stream.data == obj2->value.stream.data) && - pdf_dict_equal_p (obj1->value.stream.dict, obj2->value.stream.dict)); -} - -static pdf_obj_t -pdf_array_dup (pdf_obj_t obj) -{ - pdf_obj_t new_array; - pdf_obj_t obj_elt; - pdf_list_iterator_t iter; - pdf_list_node_t list_node; - - - new_array = pdf_create_array (); - - iter = pdf_list_iterator (obj->value.array.objs); - while (pdf_list_iterator_next (&iter, (const void**) &obj_elt, &list_node)) - { - pdf_list_add_last (new_array->value.array.objs, - pdf_obj_dup (obj_elt)); - } - pdf_list_iterator_free (&iter); - - return new_array; -} - -static pdf_obj_t -pdf_dict_dup (pdf_obj_t obj) -{ +#endif +} + + + +static pdf_status_t +pdf_obj_dict_dup (pdf_obj_t obj, pdf_obj_t *new) +{ +return PDF_ERROR;//TODO +#if 0 pdf_obj_t new_dict; pdf_dict_entry_t entry_elt; pdf_dict_entry_t new_entry_elt; @@ -975,19 +1068,117 @@ pdf_list_iterator_free (&iter); return new_dict; -} - -/* Note that the new stream will use the same stm */ -static pdf_obj_t -pdf_stream_dup (pdf_obj_t obj) -{ - pdf_obj_t new_stream; - - new_stream = pdf_create_stream (pdf_obj_dup (obj->value.stream.dict), - obj->value.stream.stm, - obj->value.stream.data); - - return new_stream; +#endif +} + +pdf_size_t +pdf_obj_dict_size (const pdf_obj_t dict) +{ + return pdf_hash_size (dict->value.dict); +} + +pdf_status_t +pdf_obj_dict_get (const pdf_obj_t dict, + const pdf_obj_t key, + pdf_obj_t *value) +{ + if (key->type != PDF_NAME_OBJ) + return PDF_EBADDATA; + + return pdf_obj_dict_getc (dict, key->value.buffer.data, value); +} + +pdf_status_t +pdf_obj_dict_getc (const pdf_obj_t dict, + const pdf_char_t *key, + pdf_obj_t *value) +{ + pdf_obj_child_t elt = NULL; + pdf_status_t rv; + + assert (value); + if (dict->type != PDF_DICT_OBJ) + return PDF_EBADDATA; + + rv = pdf_hash_get (dict->value.dict, (char*)key, (void*)&elt); + if (rv != PDF_OK) + return rv; + + *value = elt->value; + return PDF_OK; +} + +pdf_bool_t +pdf_obj_dict_key_p (const pdf_obj_t dict, + const pdf_obj_t key) +{ + return ((dict->type == PDF_DICT_OBJ) + && (key->type == PDF_NAME_OBJ) + && pdf_hash_key_p (dict->value.dict, + (char*)key->value.buffer.data)); +} + +pdf_bool_t +pdf_obj_dict_keyc_p (const pdf_obj_t dict, + const pdf_char_t *key) +{ + return ((dict->type == PDF_DICT_OBJ) + && pdf_hash_key_p (dict->value.dict, (char*)key)); +} + +pdf_status_t +pdf_obj_dict_remove (pdf_obj_t dict, + pdf_obj_t key) +{ + if ((dict->type != PDF_DICT_OBJ) + || (key->type != PDF_NAME_OBJ)) + return PDF_EBADDATA; + else if (!pdf_obj_dict_key_p (dict, key)) + return PDF_ENONODE; + + return pdf_hash_remove (dict->value.dict, + (char*)key->value.buffer.data); +} + +pdf_status_t +pdf_obj_dict_add (pdf_obj_t dict, + pdf_obj_t key, + pdf_obj_t value) +{ + pdf_status_t rv; + pdf_obj_child_t elt = NULL; + char *keystr = NULL; + if ((dict->type != PDF_DICT_OBJ) + || (key->type != PDF_NAME_OBJ)) + return PDF_EBADDATA; + else if (pdf_obj_dict_key_p (dict, key)) + return PDF_EEXIST; + + rv = pdf_obj_child_new (key, value, &elt); + if (rv != PDF_OK) + goto fail; + + keystr = (char*)key->value.buffer.data; + rv = pdf_hash_add (dict->value.dict, keystr, elt, pdf_obj_child_destroy_cb); + if (rv != PDF_OK) + goto fail; + + return PDF_OK; + +fail: + if (elt) + pdf_dealloc (elt); + return rv; +} + +pdf_status_t +pdf_obj_dict_clear_nodestroy (pdf_obj_t dict) +{ + /* Clear the dict, but don't free its objects (presumably because + * they're still owned by someone else). */ + + //TODO + return PDF_ERROR; } /* End of pdf_obj.c */ === modified file 'src/object/pdf-obj.h' --- src/object/pdf-obj.h 2008-03-07 14:58:37 +0000 +++ src/object/pdf-obj.h 2009-01-22 03:38:16 +0000 @@ -26,27 +26,165 @@ /* This library module implement the basic objects that compound a PDF document: - - Null object (PDF_NULL_OBJECT) - - Boolean value (pdf_boolean_t) - - Integer number (pdf_integer_t) + - Null object (PDF_NULL_OBJ) + - Boolean value (pdf_bool_t) + - Integer number (int) - Real number (pdf_real_t) - - String (pdf_string_t) - - Name (pdf_name_t) - - Array (pdf_array_t) - - Dictionary (pdf_dict_t) - - Indirect object (pdf_indirect_t) + - String + - Name + - Array + - Dictionary + - Indirect object - Stream object + And some object types used by the tokeniser: + - Comment + - Keyword + - Valueless tokens: + - PDF_DICT_START_TOK + - PDF_DICT_END_TOK + - PDF_ARRAY_START_TOK + - PDF_ARRAY_END_TOK + - PDF_PROC_START_TOK + - PDF_PROC_END_TOK + A generic data type capable to contain any kind of PDF object is also included (pdf_obj_t). */ #ifndef PDF_OBJ_H #define PDF_OBJ_H -#include -#include -#include -#include +#include "config.h" +#include "pdf-types.h" +#include "pdf-list.h" +#include "pdf-base.h" +#include "pdf-stm.h" + +/* BEGIN PUBLIC */ +/* pdf-obj.h */ + +enum pdf_obj_type_e +{ + PDF_NULL_OBJ = 0, + PDF_BOOLEAN_OBJ = 1, + PDF_INT_OBJ = 2, + PDF_REAL_OBJ = 3, + PDF_STRING_OBJ = 4, + PDF_NAME_OBJ = 5, + PDF_ARRAY_OBJ = 6, + PDF_DICT_OBJ = 7, + PDF_INDIRECT_OBJ = 8, + PDF_STREAM_OBJ = 9, + + /* the following types are only used by the tokeniser */ + PDF_COMMENT_TOK = 10, + PDF_KEYWORD_TOK = 11, + PDF_DICT_START_TOK = 12, + PDF_DICT_END_TOK = 13, + PDF_ARRAY_START_TOK = 14, + PDF_ARRAY_END_TOK = 15, + PDF_PROC_START_TOK = 16, + PDF_PROC_END_TOK = 17, +}; +typedef enum pdf_obj_type_e pdf_obj_type_t; + +struct pdf_obj_s; /* opaque type */ +typedef struct pdf_obj_s *pdf_obj_t; + +/* Object creation */ +pdf_status_t pdf_obj_null_new (pdf_obj_t *obj); +pdf_status_t pdf_obj_boolean_new (pdf_bool_t value, pdf_obj_t *obj); +pdf_status_t pdf_obj_integer_new (int value, pdf_obj_t *obj); +pdf_status_t pdf_obj_real_new (pdf_real_t value, pdf_obj_t *obj); +pdf_status_t pdf_obj_string_new (const pdf_char_t *value, pdf_size_t size, + pdf_obj_t *obj); +pdf_status_t pdf_obj_name_new (const pdf_char_t *value, pdf_size_t size, + pdf_obj_t *obj); +pdf_status_t pdf_obj_array_new (pdf_obj_t *obj); +pdf_status_t pdf_obj_dict_new (pdf_obj_t *obj); +pdf_status_t pdf_obj_indirect_new (unsigned int on, unsigned int gn, + pdf_obj_t *obj); +pdf_status_t pdf_obj_stream_new (pdf_obj_t dict, pdf_stm_t stm, + pdf_off_t data, pdf_obj_t *obj); +pdf_status_t pdf_tok_valueless_new (pdf_obj_type_t type, pdf_obj_t *obj); +pdf_status_t pdf_tok_comment_new (const pdf_char_t *value, pdf_size_t size, + pdf_bool_t continuation, pdf_obj_t *obj); +pdf_status_t pdf_tok_keyword_new (const pdf_char_t *value, pdf_size_t size, + pdf_obj_t *obj); +pdf_status_t pdf_obj_dup (const pdf_obj_t obj, pdf_obj_t *new); + +/* Object destruction */ +pdf_status_t pdf_obj_destroy (pdf_obj_t obj); + +/* Common functions */ +pdf_obj_type_t pdf_obj_type (pdf_obj_t obj); +pdf_bool_t pdf_obj_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); + +/* Managing objects of basic types */ +pdf_bool_t pdf_obj_bool_value (pdf_obj_t bool_obj); +int pdf_obj_int_value (pdf_obj_t int_obj); +pdf_real_t pdf_obj_real_value (pdf_obj_t real_obj); + +/* Managing strings */ +pdf_size_t pdf_obj_string_size (pdf_obj_t str_obj); +const pdf_char_t *pdf_obj_string_data (pdf_obj_t str_obj); + +/* Managing names */ +pdf_size_t pdf_obj_name_size (const pdf_obj_t name); +const pdf_char_t *pdf_obj_name_data (const pdf_obj_t name); +pdf_bool_t pdf_obj_name_equal_p (const pdf_obj_t obj, const pdf_char_t *data); + +/* Managing keywords */ +pdf_size_t pdf_tok_keyword_size (const pdf_obj_t keyword); +const pdf_char_t *pdf_tok_keyword_data (const pdf_obj_t keyword); +pdf_bool_t pdf_tok_keyword_equal_p (const pdf_obj_t obj, + const pdf_char_t *data); + +/* Managing comments */ +pdf_size_t pdf_tok_comment_size (const pdf_obj_t comment); +const pdf_char_t *pdf_tok_comment_data (const pdf_obj_t comment); + +/* Managing arrays */ +pdf_size_t pdf_obj_array_size (const pdf_obj_t array); +pdf_status_t pdf_obj_array_get (const pdf_obj_t array, pdf_size_t index, + pdf_obj_t *obj); +pdf_status_t pdf_obj_array_set (pdf_obj_t array, pdf_size_t index, + const pdf_obj_t new_obj, + pdf_obj_t *old_obj); +pdf_status_t pdf_obj_array_insert (pdf_obj_t array, pdf_size_t index, + const pdf_obj_t obj); +pdf_status_t pdf_obj_array_append (pdf_obj_t array, const pdf_obj_t obj); +pdf_status_t pdf_obj_array_remove (pdf_obj_t array, pdf_size_t index, + pdf_obj_t *obj); +pdf_status_t pdf_obj_array_clear (pdf_obj_t array); +pdf_status_t pdf_obj_array_clear_nodestroy (pdf_obj_t array); + +/* Managing dictionaries */ +pdf_size_t pdf_obj_dict_size (const pdf_obj_t dict); +pdf_bool_t pdf_obj_dict_key_p (const pdf_obj_t dict, const pdf_obj_t key); +pdf_bool_t pdf_obj_dict_keyc_p (const pdf_obj_t dict, const pdf_char_t *key); +pdf_status_t pdf_obj_dict_get (const pdf_obj_t dict, const pdf_obj_t key, + pdf_obj_t *value); +pdf_status_t pdf_obj_dict_getc (const pdf_obj_t dict, const pdf_char_t *key, + pdf_obj_t *value); +pdf_status_t pdf_obj_dict_add (pdf_obj_t dict, const pdf_obj_t key, + pdf_obj_t value); +pdf_status_t pdf_obj_dict_remove (pdf_obj_t dict, const pdf_obj_t key); +pdf_status_t pdf_obj_dict_clear (pdf_obj_t dict); +pdf_status_t pdf_obj_dict_clear_nodestroy (pdf_obj_t dict); + +/* Managing streams */ +pdf_obj_t pdf_obj_stream_dict (pdf_obj_t stream); +pdf_stm_t pdf_obj_stream_stm (pdf_obj_t stream); +pdf_off_t pdf_obj_stream_data (pdf_obj_t stream); + +/* Managing indirect objects */ +unsigned int pdf_obj_indirect_on (pdf_obj_t indir); +unsigned int pdf_obj_indirect_gn (pdf_obj_t indir); + +/* END PUBLIC */ + + /* The PDF NULL object has a type and a value that are unequal to those of any other object. There is only one possible value for @@ -66,42 +204,30 @@ - Values in PostScript calculator functions */ -typedef int pdf_boolean_t; - /* NOTE: PDF integers and reals are implemented in pdf_base.h */ /* According to the PDF reference, a PDF name object is an atomic - symbol uniquely defined by a sequence of regular characters. It has + symbol uniquely defined by a sequence of non-null characters. It has no internal structure. In the practice, PDF uses name objects in order to store information (such as font names). In that situation, it is recommended to code such information in UTF-8. Due to this stupidity we should store the entire byte sequence that conform the - name. */ - -struct pdf_name_s -{ - unsigned char *data; - int size; -}; - -typedef struct pdf_name_s *pdf_name_t; + name. + + A pdf_obj_buffer_s structure is used to store a name's value. */ + /* A PDF string is a sequence of bytes, in the range of 0-255. In particular it may contain NULL characters (code 0 in the ASCII CCS). Corollary: NEVER NEVER NEVER EVER use a PDF string as an input - expecting null-terminated strings. You have been warned. */ - -struct pdf_string_s -{ - unsigned char *data; - int size; -}; - -typedef struct pdf_string_s *pdf_string_t; + expecting null-terminated strings. You have been warned. + + A pdf_obj_buffer_s structure is used to store a string's value. */ + /* A PDF array object is a one-dimensional collection of objects arranged sequentially. @@ -109,36 +235,11 @@ Note that the list of objects is heterogeneous, so we must use `pdf_obj_s'. */ -struct pdf_array_s -{ - pdf_list_t objs; -}; - -typedef struct pdf_array_s *pdf_array_t; - -/* A PDF dictionary object is an associative table containing pairs of - objects. The first element of the pair is the `key' and the second - element is the `value'. - + +/* A PDF dictionary object is stored using pdf_hash_t. Any `key' object should be a name pdf object. On the other hand, the values may have any pdf object type. */ -struct pdf_obj_s; /* Forward reference */ - -struct pdf_dict_entry_s -{ - struct pdf_obj_s *key; - struct pdf_obj_s *value; -}; - -typedef struct pdf_dict_entry_s *pdf_dict_entry_t; - -struct pdf_dict_s -{ - pdf_list_t entries; -}; - -typedef struct pdf_dict_s *pdf_dict_t; /* A PDF indirect object is composed by: @@ -154,66 +255,64 @@ unsigned int gn; }; -typedef struct pdf_indirect_s *pdf_indirect_t; - /* A PDF stream object is composed by a dictionary describing the stream, a reference to a stm structure, and a pointer (octects offset) that point to the beginning of the stream data in the stm backend. - Note that the actual data of the stream may dont be stored in + Note that the actual data of the stream may not be stored in memory. It depends on the backend type of the stm. */ -struct pdf_obj_s; - -struct pdf_stream_s +struct pdf_stream_s { struct pdf_obj_s *dict; pdf_stm_t stm; - pdf_stm_pos_t data; -}; - -typedef struct pdf_stream_s *pdf_stream_t; - -enum pdf_obj_type_t -{ - PDF_NULL_OBJ = 0, - PDF_BOOLEAN_OBJ, - PDF_INT_OBJ, - PDF_REAL_OBJ, - PDF_STRING_OBJ, - PDF_NAME_OBJ, - PDF_ARRAY_OBJ, - PDF_DICT_OBJ, - PDF_INDIRECT_OBJ, - PDF_STREAM_OBJ -}; + pdf_off_t data; +}; + +/* pdf_obj_buffer_s is an internal structure used for tokens with an + * associated byte array. 'data' may or may not be null-terminated, + * but size never includes the trailing null. */ +struct pdf_obj_buffer_s +{ + pdf_char_t *data; + pdf_size_t size; +}; + +struct pdf_comment_s +{ + pdf_char_t *data; + pdf_size_t size; + /* This structure shares a common initial sequence with pdf_obj_buffer_s, + * so the above fields can be accessed via obj->value.buffer. */ + + pdf_bool_t continuation; /* is data continued from a previous token? */ +}; + /* A `pdf_obj_s' structure stores a PDF object. The object may be of any type (including NULL). */ struct pdf_obj_s { - enum pdf_obj_type_t type; - + pdf_obj_type_t type; + union { - pdf_boolean_t boolean; - pdf_integer_t integer; + struct pdf_obj_buffer_s buffer; + pdf_bool_t boolean; + int integer; pdf_real_t real; - struct pdf_string_s string; - struct pdf_name_s name; - struct pdf_array_s array; - struct pdf_dict_s dict; + pdf_list_t array; + pdf_hash_t dict; struct pdf_indirect_s indirect; struct pdf_stream_s stream; + struct pdf_comment_s comment; } value; }; -typedef struct pdf_obj_s *pdf_obj_t; - /* Accessing macros */ #define IS_NULL(obj) \ @@ -247,64 +346,13 @@ #define GET_REAL(obj) \ ((obj)->value.real) -/* - * Forward declarations - */ - -/* Object creation */ -pdf_obj_t pdf_create_null (void); -pdf_obj_t pdf_create_boolean (int value); -pdf_obj_t pdf_create_integer (int value); -pdf_obj_t pdf_create_real (float value); -pdf_obj_t pdf_create_string (unsigned char *value, int size); -pdf_obj_t pdf_create_name (unsigned char *value, int size); -pdf_obj_t pdf_create_array (void); -pdf_obj_t pdf_create_dict (void); -pdf_obj_t pdf_create_indirect (unsigned int on, unsigned int gn); -pdf_obj_t pdf_create_stream (pdf_obj_t dict, pdf_stm_t stm, pdf_stm_pos_t data); -pdf_obj_t pdf_obj_dup (pdf_obj_t obj); - -/* Object destruction */ -int pdf_destroy_obj (pdf_obj_t obj); - -/* Testing for equality */ -int pdf_obj_equal_p (pdf_obj_t obj1, pdf_obj_t obj2); - -/* Managing objects of basic types */ -int pdf_get_bool (pdf_obj_t obj); -void pdf_set_bool (pdf_obj_t obj, int value); -int pdf_get_int (pdf_obj_t obj); -void pdf_set_int (pdf_obj_t obj, int value); -pdf_real_t pdf_get_real (pdf_obj_t obj); -void pdf_set_real (pdf_obj_t obj, pdf_real_t value); - -/* Managing strings */ -int pdf_get_string_size (pdf_obj_t obj); -char *pdf_get_string_data (pdf_obj_t obj); - -/* Managing names */ -int pdf_get_name_size (pdf_obj_t obj); -char *pdf_get_name_data (pdf_obj_t obj); - -/* Managing arrays */ -int pdf_get_array_size (pdf_obj_t obj); -int pdf_remove_array_elt (pdf_obj_t obj, int index); -pdf_obj_t pdf_get_array_elt (pdf_obj_t obj, int index); -int pdf_set_array_elt (pdf_obj_t obj, int index, pdf_obj_t elt); -int pdf_add_array_elt (pdf_obj_t obj, int index, pdf_obj_t elt); -int pdf_append_array_elt (pdf_obj_t obj, pdf_obj_t elt); - -/* Managing dictionaries */ -int pdf_get_dict_size (pdf_obj_t obj); -int pdf_dict_key_p (pdf_obj_t obj, pdf_obj_t key); -pdf_obj_t pdf_get_dict_entry (pdf_obj_t obj, pdf_obj_t key); -int pdf_remove_dict_entry (pdf_obj_t obj, pdf_obj_t key); -int pdf_create_dict_entry (pdf_obj_t obj, pdf_obj_t key, pdf_obj_t value); - -/* Managing streams */ -pdf_obj_t pdf_get_stream_dict (pdf_obj_t stream); -pdf_stm_t pdf_get_stream_stm (pdf_obj_t stream); -pdf_stm_pos_t pdf_get_stream_data (pdf_obj_t stream); +struct pdf_obj_child_s +{ + pdf_obj_t key; + pdf_obj_t value; + int owned; +}; +typedef struct pdf_obj_child_s *pdf_obj_child_t; #endif /* pdf_obj.h */ === added file 'src/object/pdf-rd-parser.c' --- src/object/pdf-rd-parser.c 1970-01-01 00:00:00 +0000 +++ src/object/pdf-rd-parser.c 2009-01-22 03:38:16 +0000 @@ -0,0 +1,416 @@ +/* -*- mode: C -*- Time-stamp: "2009-01-14 21:21:44 mgold" + * + * File: pdf-rd-tokeniser.c + * Date: Wed Jan 14 21:21:40 2009 + * + * GNU PDF Library - Stream parser + * + */ + +/* Copyright (C) 2009 Michael Gold */ + +/* This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "pdf-rd-parser.h" +#include "pdf-obj.h" + +static const pdf_char_t PDF_KW_null[] = {0x6e, 0x75, 0x6c, 0x6c}; +static const pdf_char_t PDF_KW_true[] = {0x74, 0x72, 0x75, 0x65}; +static const pdf_char_t PDF_KW_false[] = {0x66, 0x61, 0x6c, 0x73, 0x65}; + +static pdf_status_t open_stack (pdf_parser_t parser, pdf_bool_t is_dict); +static pdf_status_t close_stack (pdf_parser_t parser, pdf_bool_t is_dict); + +pdf_status_t +pdf_parser_new (pdf_stm_t stm, pdf_parser_t *parser) +{ + pdf_status_t rv; + pdf_parser_t new_parser; + + rv = PDF_ENOMEM; + new_parser = pdf_alloc (sizeof (*new_parser)); + if (!new_parser) + goto fail; + + new_parser->tokr = NULL; + rv = pdf_tokeniser_new (stm, &new_parser->tokr); + if (rv != PDF_OK) + goto fail; + + new_parser->token = NULL; + + /* open the top-level stack (parent==NULL) */ + new_parser->stack = NULL; + rv = open_stack (new_parser, 0); + if (rv != PDF_OK) + goto fail; + + *parser = new_parser; + return PDF_OK; + +fail: + if (new_parser) + { + if (new_parser->tokr) + pdf_tokeniser_destroy (new_parser->tokr); + pdf_dealloc (new_parser); + } + return rv; +} + +pdf_status_t +pdf_parser_destroy (pdf_parser_t parser) +{ + if (!parser) return PDF_EBADDATA; + + assert (parser->tokr); + if (parser->tokr) + pdf_tokeniser_destroy (parser->tokr); + if (parser->token) + pdf_obj_destroy (parser->token); + + while (parser->stack) + { + pdf_parser_stack_t parent = parser->stack->parent; + pdf_obj_destroy (parser->stack->items); + pdf_dealloc (parser->stack); + parser->stack = parent; + } + + pdf_dealloc (parser); + return PDF_OK; +} + +static pdf_status_t +handle_keyword (pdf_parser_t parser) +{ + pdf_status_t rv; + pdf_obj_t new_token = NULL; + const pdf_char_t *keyword = pdf_tok_keyword_data (parser->token); + + switch (pdf_tok_keyword_size (parser->token)) + { + case 1: + if (keyword[0] == 82) /* 'R' (indirect object) */ + { + pdf_obj_t on, gn; + pdf_obj_t items = parser->stack->items; + pdf_size_t size = pdf_obj_array_size (items); + if (size < 2) + return PDF_EBADFILE; + + rv = pdf_obj_array_get (items, size-2, &on); + if (rv != PDF_OK) + return rv; + + rv = pdf_obj_array_get (items, size-1, &gn); + if (rv != PDF_OK) + return rv; + + if (pdf_obj_type (on) != PDF_INT_OBJ + || pdf_obj_type (gn) != PDF_INT_OBJ) + return PDF_EBADFILE; + + rv = pdf_obj_indirect_new (pdf_obj_int_value (on), + pdf_obj_int_value (gn), + &new_token); + if (rv != PDF_OK) + return rv; + + pdf_obj_array_remove (items, size-1, NULL); + pdf_obj_array_remove (items, size-2, NULL); + } + break; + case 4: + if (memcmp (keyword, PDF_KW_null, 4) == 0) + { + rv = pdf_obj_null_new (&new_token); + if (rv != PDF_OK) + return rv; + } + else if (memcmp (keyword, PDF_KW_true, 4) == 0) + { + rv = pdf_obj_boolean_new (PDF_TRUE, &new_token); + if (rv != PDF_OK) + return rv; + } + break; + case 5: + if (memcmp (keyword, PDF_KW_false, 5) == 0) + { + rv = pdf_obj_boolean_new (PDF_FALSE, &new_token); + if (rv != PDF_OK) + return rv; + } + break; + } + + if (new_token) + { + pdf_obj_destroy (parser->token); + parser->token = new_token; + } + else if (parser->stack->parent) + { + /* The token looks like a command, but isn't at the top level. */ + return PDF_EBADFILE; + } + + return PDF_OK; +} + +static pdf_status_t +handle_token (pdf_parser_t parser) +{ + pdf_status_t rv; + assert (parser->token); + + switch (pdf_obj_type (parser->token)) + { + case PDF_ARRAY_START_TOK: + rv = open_stack (parser, 0); + if (rv != PDF_OK) + return rv; + + pdf_obj_destroy (parser->token); + parser->token = NULL; + return PDF_OK; + + case PDF_DICT_START_TOK: + rv = open_stack (parser, 1); + if (rv != PDF_OK) + return rv; + + pdf_obj_destroy (parser->token); + parser->token = NULL; + return PDF_OK; + + case PDF_ARRAY_END_TOK: + rv = close_stack (parser, 0); + if (rv != PDF_OK) + return rv; + + assert (pdf_obj_type (parser->token) != PDF_ARRAY_END_TOK); + break; + + case PDF_DICT_END_TOK: + rv = close_stack (parser, 1); + if (rv != PDF_OK) + return rv; + + assert (pdf_obj_type (parser->token) != PDF_DICT_END_TOK); + break; + + case PDF_PROC_START_TOK: /* fall through */ + case PDF_PROC_END_TOK: + return PDF_EBADFILE; + + case PDF_NULL_OBJ: /* fall through */ + case PDF_BOOLEAN_OBJ: /* fall through */ + case PDF_INDIRECT_OBJ: /* fall through */ + /* Note: the above types are never produced by the tokeniser, + * but may appear in parser->token if we didn't finish + * processing a keyword last time. */ + case PDF_INT_OBJ: /* fall through */ + case PDF_REAL_OBJ: /* fall through */ + case PDF_STRING_OBJ: /* fall through */ + case PDF_NAME_OBJ: /* fall through */ + case PDF_COMMENT_TOK: + break; + + case PDF_KEYWORD_TOK: + rv = handle_keyword (parser); + if (rv != PDF_OK) + return rv; + else if (!parser->token) + return PDF_OK; + + break; + + default: + assert (0); + return PDF_ERROR; + } + + /* store the token */ /*TODO: limit the length and depth of the stack */ + assert (parser->token); + rv = pdf_obj_array_append (parser->stack->items, parser->token); + if (rv != PDF_OK) + return rv; + + parser->token = NULL; + return PDF_OK; +} + +static pdf_status_t +open_stack (pdf_parser_t parser, pdf_bool_t is_dict) +{ + pdf_status_t rv; + pdf_parser_stack_t new_stack; + + new_stack = pdf_alloc (sizeof (*new_stack)); + if (!new_stack) + return PDF_ENOMEM; + + rv = pdf_obj_array_new (&new_stack->items); + if (rv != PDF_OK) + goto fail; + + new_stack->is_dict = is_dict; + new_stack->parent = parser->stack; + parser->stack = new_stack; + return PDF_OK; + +fail: + if (new_stack) + pdf_dealloc (new_stack); + return rv; +} + +static pdf_status_t +close_stack (pdf_parser_t parser, pdf_bool_t is_dict) +{ + pdf_status_t rv; + pdf_obj_t new_dict = NULL; + pdf_parser_stack_t stack = parser->stack; + + assert (parser->token); + if (!stack || !stack->parent || (stack->is_dict != is_dict)) + return PDF_EBADFILE; + + if (is_dict) + { + size_t i, size; + pdf_obj_t items = stack->items; + size = pdf_obj_array_size (items); + + rv = pdf_obj_dict_new (&new_dict); + if (rv != PDF_OK) + goto fail; + + /* turn the object stack into a dictionary */ + for (i = 0; i < size; i += 2) + { + pdf_obj_t key, value; + + rv = PDF_EBADFILE; + if (i+1 == size) /* key with no value */ + goto fail; + + rv = pdf_obj_array_get (items, i, &key); + if (rv != PDF_OK) + goto fail; + + rv = PDF_EBADFILE; + if (pdf_obj_type (key) != PDF_NAME_OBJ) + goto fail; + + rv = pdf_obj_array_get (items, i+1, &value); + if (rv != PDF_OK) + goto fail; + + rv = pdf_obj_dict_add (new_dict, key, value); + if (rv != PDF_OK) + goto fail; + } + + /* now delete the original stack without deleting its objects, + * which were transferred to the dictionary */ + pdf_obj_array_clear_nodestroy (stack->items); + pdf_obj_destroy (stack->items); + + /* replace the token with a PDF_DICT_OBJ */ + pdf_obj_destroy (parser->token); + parser->token = new_dict; + } + else + { + /* replace the token with a PDF_LIST_OBJ */ + pdf_obj_destroy (parser->token); + parser->token = stack->items; + } + + /* close this stack level */ + parser->stack = stack->parent; + pdf_dealloc (stack); + return PDF_OK; + +fail: + if (new_dict) + { + pdf_obj_dict_clear_nodestroy (new_dict); + pdf_obj_destroy (new_dict); + } + return rv; +} + +pdf_status_t +pdf_parser_read_to_command (pdf_parser_t parser, pdf_obj_t *stack_ref) +{ + pdf_status_t rv; + assert (parser->stack); + for (;;) + { + if (!parser->token) + { + rv = pdf_tokeniser_read (parser->tokr, &parser->token); + if (rv == PDF_EEOF) + break; + else if (rv != PDF_OK) + return rv; + } + + assert (parser->token); + rv = handle_token (parser); + if (rv != PDF_OK) + return rv; + + assert (parser->stack); + if (!parser->stack->parent) /* at the top level */ + { + pdf_obj_t last_obj; + pdf_size_t size = pdf_obj_array_size (parser->stack->items); + if (size) + { + rv = pdf_obj_array_get (parser->stack->items, + size - 1, + &last_obj); + assert (rv == PDF_OK); + if (rv != PDF_OK) + return PDF_ERROR; /* shouldn't happen */ + + assert (last_obj); + if (pdf_obj_type (last_obj) == PDF_KEYWORD_TOK) + { + /* this token is a command */ + *stack_ref = parser->stack->items; + return PDF_OK; + } + } + } + } + + /* end of file */ + if (parser->stack->parent) + { + /* got EOF before all arrays/dicts were closed */ + return PDF_EBADFILE; + } + + return PDF_EEOF; +} + +/* End of pdf-rd-parser.c */ === added file 'src/object/pdf-rd-parser.h' --- src/object/pdf-rd-parser.h 1970-01-01 00:00:00 +0000 +++ src/object/pdf-rd-parser.h 2009-01-22 03:38:16 +0000 @@ -0,0 +1,64 @@ +/* -*- mode: C -*- Time-stamp: "2009-01-14 20:47:21 mgold" + * + * File: pdf-rd-tokeniser.h + * Date: Wed Jan 14 20:47:23 2009 + * + * GNU PDF Library - Stream parser + * + */ + +/* Copyright (C) 2009 Michael Gold */ + +/* This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PDF_RD_PARSER +#define PDF_RD_PARSER +#include "pdf-types.h" +#include "pdf-obj.h" +#include "pdf-rd-tokeniser.h" + +/* BEGIN PUBLIC */ +/* pdf-rd-parser.h */ +typedef struct pdf_parser_s *pdf_parser_t; +pdf_status_t pdf_parser_new(pdf_stm_t stm, pdf_parser_t *parser); +pdf_status_t pdf_parser_destroy(pdf_parser_t parser); +pdf_status_t pdf_parser_read(pdf_parser_t parser, pdf_obj_t *obj); +/* END PUBLIC */ + +typedef struct pdf_parser_stack_s *pdf_parser_stack_t; +struct pdf_parser_stack_s +{ + pdf_parser_stack_t parent; + pdf_bool_t is_dict; + pdf_obj_t items; +}; + +enum pdf_parser_state_e +{ + PDF_PARSER_STATE_TOPLEVEL, + PDF_PARSER_STATE_TOPLEVEL_FINDSTREAM, + PDF_PARSER_STATE_INSTREAM, +}; + +struct pdf_parser_s +{ + pdf_tokeniser_t tokr; + pdf_obj_t token; /* the next token to process, or NULL */ + pdf_parser_stack_t stack; +}; + +#endif + +/* End of pdf-rd-parser.h */ === added file 'src/object/pdf-rd-tokeniser.c' --- src/object/pdf-rd-tokeniser.c 1970-01-01 00:00:00 +0000 +++ src/object/pdf-rd-tokeniser.c 2009-01-22 03:56:26 +0000 @@ -0,0 +1,950 @@ +/* -*- mode: C -*- Time-stamp: "2009-01-05 08:53:24 mgold" + * + * File: pdf-rd-tokeniser.c + * Date: Mon Dec 29 00:45:09 2008 + * + * GNU PDF Library - Stream tokeniser + * + */ + +/* Copyright (C) 2008 Michael Gold */ + +/* This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" +#include +#include +#include +#include +#include "pdf-rd-tokeniser.h" + +static INLINE int can_store_char (pdf_tokeniser_t context); +static INLINE pdf_status_t store_char (pdf_tokeniser_t context, pdf_char_t ch); +static pdf_status_t exit_state (pdf_tokeniser_t context, pdf_obj_t *token); +static pdf_status_t flush_token (pdf_tokeniser_t context, pdf_obj_t *token); +static pdf_status_t handle_char (pdf_tokeniser_t context, + pdf_char_t ch, + pdf_obj_t *token); +static INLINE pdf_status_t handle_string_char (pdf_tokeniser_t context, + pdf_char_t ch, + pdf_obj_t *token); +static INLINE pdf_status_t handle_hexstring_char (pdf_tokeniser_t context, + pdf_char_t ch, + pdf_obj_t *token); +static int recognize_number (pdf_buffer_t buffer, int *int_value); +static INLINE int parse_integer (pdf_buffer_t buffer, int *int_value, + int *int_state); +static INLINE pdf_status_t parse_real (pdf_buffer_t buffer, + char *locale_dec_pt, + double *value); +static INLINE int validate_real (pdf_buffer_t buffer, int int_state); + + +pdf_status_t +pdf_tokeniser_new (pdf_stm_t stm, pdf_tokeniser_t *context) +{ + pdf_status_t err; + pdf_tokeniser_t new_tokr; + + err = PDF_ENOMEM; + new_tokr = pdf_alloc (sizeof (*new_tokr)); + if (!new_tokr) + goto fail; + + /* determine the current locale's decimal point + * (avoid using localeconv since it may not be thread-safe) */ + new_tokr->decimal_point = NULL; + { + int len; + char decpt[16]; + + err = PDF_ERROR; + len = snprintf (decpt, sizeof (decpt), "%#.0f", 1.0); + if (len <= 0 || len >= sizeof (decpt)) /* shouldn't happen */ + goto fail; + + err = PDF_ENOMEM; + new_tokr->decimal_point = pdf_alloc (len); + if (!new_tokr->decimal_point) + goto fail; + + /* this copies the trailing '\0' due to the starting offset */ + memcpy (new_tokr->decimal_point, &decpt[1], len); + } + + /* max string size 32767 + terminating '\0' */ + new_tokr->buffer = pdf_buffer_new (32768); + if (!new_tokr->buffer) + goto fail; + + new_tokr->flags = 0; + new_tokr->stream = stm; + pdf_tokeniser_reset_state (new_tokr); + + *context = new_tokr; + return PDF_OK; + +fail: + if (new_tokr) + { + if (new_tokr->decimal_point) + pdf_dealloc (new_tokr->decimal_point); + pdf_dealloc (new_tokr); + } + + return err; +} + +pdf_status_t +pdf_tokeniser_reset_state (pdf_tokeniser_t context) +{ + context->state = PDF_TOKENISER_STATE_NONE; + context->substate = 0; + context->findstream = 0; + return PDF_OK; +} + +pdf_status_t +pdf_tokeniser_destroy (pdf_tokeniser_t context) +{ + if (!context) return PDF_EBADDATA; + + assert (context->buffer); + if (context->buffer) + pdf_buffer_destroy (context->buffer); + pdf_dealloc (context->decimal_point); + pdf_dealloc (context); + + return PDF_OK; +} + +static INLINE int +pdf_is_wspace_char (pdf_char_t ch) +{ + /* ASCII codes for NUL, HT, LF, FF, CR, SP */ + return (ch == 0 || ch == 9 || ch == 10 || ch == 12 || ch == 13 || ch == 32); +} + +static INLINE int +pdf_is_delim_char (pdf_char_t ch) +{ + /* ASCII codes for '%', '(', ')', '/'; '<', '>', '[', ']'; '{', '}' */ + return (ch == 37 || ch == 40 || ch == 41 || ch == 47 + || ch == 60 || ch == 62 || ch == 91 || ch == 93 + || ch == 123 || ch == 125); +} + +static INLINE int +pdf_is_eol_char (pdf_char_t ch) +{ + return ch == 10 || ch == 13; +} + +static INLINE int +pdf_is_regular_char (pdf_char_t ch) +{ + return !pdf_is_wspace_char (ch) && !pdf_is_delim_char (ch); +} + +static INLINE pdf_char_t +hexval (pdf_char_t ch) +{ + if (ch >= 48 && ch <= 48+9) /* '0'--'9' */ + return ch - 48; + if (ch >= 64+1 && ch <= 64+6) /* 'A'--'F' */ + return ch - (64+1) + 10; + if (ch >= 96+1 && ch <= 96+6) /* 'a'--'f' */ + return ch - (96+1) + 10; + return 255; +} + + +/* Tries to handle the given character and possibly produce a token. + * Sets (*token) if a token is produced, and leaves it unmodified otherwise. + * + * Returns PDF_OK if the character was accepted. Otherwise, an error code + * is returned, and the call can be repeated later with the same ch value. + * A token may be produced even if the character isn't accepted. + */ +static pdf_status_t +handle_char (pdf_tokeniser_t context, pdf_char_t ch, pdf_obj_t *token) +{ + pdf_status_t rv; + + /* first, handle the states that shouldn't be exited when whitespace + * or a delimiter is seen */ + + switch (context->state) + { + case PDF_TOKENISER_STATE_EOF: + return PDF_EEOF; + + case PDF_TOKENISER_STATE_STRING: + return handle_string_char (context, ch, token); + + case PDF_TOKENISER_STATE_HEXSTRING: + return handle_hexstring_char (context, ch, token); + + case PDF_TOKENISER_STATE_DICTEND: + if (ch != 62) /* '>' */ + return PDF_EBADFILE; + context->substate = 1; /* saw the closing '>' */ + return exit_state (context, token); + + case PDF_TOKENISER_STATE_COMMENT: + if (pdf_is_eol_char (ch)) + { + rv = exit_state (context, token); + if (rv != PDF_OK) + return rv; + + /* don't accept this character, but process it next time */ + return PDF_EAGAIN; + } + + if (store_char (context, ch) != PDF_OK) + { + /* the comment buffer is full, so split the token */ + rv = flush_token (context, token); + if (rv != PDF_OK) + return rv; + + context->intparam = 1; /* mark the next token as a continuation */ + return store_char (context, ch); /* can't fail this time */ + } + + return PDF_OK; + + default: ; + } + + /* now handle delimiters and whitespace */ + + if (pdf_is_wspace_char (ch)) + { + if (context->state) + { + rv = exit_state (context, token); + if (rv != PDF_OK) + return rv; + + /* avoid reading this byte so pdf_tokeniser_end_at_stream + * will work properly if it's '\r' */ + return PDF_EAGAIN; + } + + if (context->findstream && ch == 10) /* LF */ + { + /* found the beginning of a stream */ + context->state = PDF_TOKENISER_STATE_EOF; + context->findstream = 0; + } + return PDF_OK; + } + else if (context->findstream && ch != 37) /* 37=='%' */ + { + /* only allow whitespace/comments after the "stream" keyword */ + return PDF_EBADFILE; + } + + if (pdf_is_delim_char (ch)) + { + /* set state 0 (UNINIT), substate 0, bufpos 0 */ + if (context->state) + { + rv = exit_state (context, token); + if (rv != PDF_OK) + return rv; + return PDF_EAGAIN; + } + + switch (ch) + { + case 37: /* '%' */ + context->state = PDF_TOKENISER_STATE_COMMENT; + context->intparam = 0; + return store_char (context, ch); + case 40: /* '(' */ + context->state = PDF_TOKENISER_STATE_STRING; + context->intparam = 0; + return PDF_OK; + case 41: /* ')' */ + /* this shouldn't occur outside the STRING and COMMENT states */ + return PDF_EBADFILE; + case 47: /* '/' */ + context->state = PDF_TOKENISER_STATE_NAME; + return PDF_OK; + case 60: /* '<' */ + context->state = PDF_TOKENISER_STATE_HEXSTRING; + return PDF_OK; + case 62: /* '>' */ + context->state = PDF_TOKENISER_STATE_DICTEND; + return PDF_OK; + case 91: /* '[' */ + /* fall through */ + case 93: /* ']' */ + /* fall through */ + case 123: /* '{' */ + /* fall through */ + case 125: /* '}' */ + /* exit_state may have emitted a token, so we can't emit another + * one now; we'll do it when exiting the PENDING state */ + context->state = PDF_TOKENISER_STATE_PENDING; + context->charparam = ch; + return PDF_OK; + } + + /* not reached (all delimiter chars should be handled) */ + assert (0); + } + + /* ch is a regular character */ + + switch (context->state) + { + case PDF_TOKENISER_STATE_PENDING: + rv = exit_state (context, token); + if (rv != PDF_OK) + return rv; + return PDF_EAGAIN; + + case PDF_TOKENISER_STATE_NONE: + context->state = PDF_TOKENISER_STATE_KEYWORD; + /* fall through */ + + case PDF_TOKENISER_STATE_KEYWORD: + return store_char (context, ch); + + case PDF_TOKENISER_STATE_NAME: + if (context->substate == 0) + { + if (ch != 35 /* '#' */ + || (context->flags & PDF_TOKENISER_FLAG_PDF11) ) + return store_char (context, ch); + + context->substate = 1; + return PDF_OK; + } + + if ( (ch = hexval (ch)) >= 16 ) + return PDF_EBADFILE; + + if (context->substate == 1) /* the first hex digit of an escape */ + { + context->substate = 2; + context->charparam = ch; + return PDF_OK; + } + + ch = (context->charparam << 4) | ch; + if (ch == 0) /* the PDF spec forbids "#00" */ + return PDF_EBADFILE; + + rv = store_char (context, ch); + if (rv == PDF_OK) context->substate = 0; + return rv; + + default: + assert (0); + } + + return store_char (context, ch); +} + + +static INLINE int +can_store_char (const pdf_tokeniser_t context) +{ + return context->buffer->wp < (context->buffer->size - 1); +} + +static INLINE pdf_status_t +store_char (pdf_tokeniser_t context, pdf_char_t ch) +{ + if (!can_store_char (context)) + return PDF_EIMPLLIMIT; + context->buffer->data[context->buffer->wp++] = ch; + return PDF_OK; +} + + +static pdf_status_t +flush_token (pdf_tokeniser_t context, pdf_obj_t *token) +{ + pdf_status_t rv; + pdf_obj_t obj; + pdf_char_t *data = context->buffer->data; + int datasize = context->buffer->wp; + + switch (context->state) + { + case PDF_TOKENISER_STATE_NONE: + return PDF_OK; /* no state to exit */ + + case PDF_TOKENISER_STATE_EOF: + return PDF_EEOF; /* can't continue parsing after EOF */ + + case PDF_TOKENISER_STATE_COMMENT: + if (!(context->flags & PDF_TOKENISER_FLAG_RET_COMMENTS)) + goto finish; + + rv = pdf_tok_comment_new (data, datasize, + context->intparam /*continuation*/, &obj); + break; + + case PDF_TOKENISER_STATE_KEYWORD: + { + int value; + int ntyp = recognize_number (context->buffer, &value); + if (ntyp == 1) + rv = pdf_obj_integer_new (value, &obj); + else if (ntyp == 2) + { + double realvalue; + rv = parse_real (context->buffer, + context->decimal_point, + &realvalue); + if (rv != PDF_OK) + return rv; + rv = pdf_obj_real_new ((float)realvalue, &obj); + } + else + rv = pdf_tok_keyword_new (data, datasize, &obj); + } + break; + + case PDF_TOKENISER_STATE_NAME: + if (context->substate != 0) /* reading an escape sequence */ + return PDF_EBADFILE; + + rv = pdf_obj_name_new (data, datasize, &obj); + break; + + case PDF_TOKENISER_STATE_STRING: + if (context->intparam >= 0) /* didn't see the closing ')' */ + return PDF_EBADFILE; + + rv = pdf_obj_string_new (data, datasize, &obj); + break; + + case PDF_TOKENISER_STATE_HEXSTRING: + if (context->substate != 3) /* didn't see the closing '>' */ + return PDF_EBADFILE; + + rv = pdf_obj_string_new (data, datasize, &obj); + break; + + case PDF_TOKENISER_STATE_DICTEND: + if (context->substate != 1) /* didn't see a second '>' */ + return PDF_EBADFILE; + + rv = pdf_tok_valueless_new (PDF_DICT_END_TOK, &obj); + break; + + case PDF_TOKENISER_STATE_PENDING: + switch (context->charparam) + { + case 60: /* '<' */ + rv = pdf_tok_valueless_new (PDF_DICT_START_TOK, &obj); + break; + case 91: /* '[' */ + rv = pdf_tok_valueless_new (PDF_ARRAY_START_TOK, &obj); + break; + case 93: /* ']' */ + rv = pdf_tok_valueless_new (PDF_ARRAY_END_TOK, &obj); + break; + case 123: /* '{' */ + rv = pdf_tok_valueless_new (PDF_PROC_START_TOK, &obj); + break; + case 125: /* '}' */ + rv = pdf_tok_valueless_new (PDF_PROC_END_TOK, &obj); + break; + default: + assert (0); + return PDF_ERROR; + } + break; + + default: + assert (0); + return PDF_ERROR; + } + + if (rv != PDF_OK) + return rv; + + *token = obj; +finish: + context->buffer->wp = 0; + return PDF_OK; +} + + +static pdf_status_t +exit_state (pdf_tokeniser_t context, pdf_obj_t *token) +{ + pdf_status_t rv = flush_token (context, token); + if (rv == PDF_OK) + { + context->state = PDF_TOKENISER_STATE_NONE; + context->substate = 0; + } + return rv; +} + + +static INLINE pdf_status_t +handle_string_char (pdf_tokeniser_t context, + pdf_char_t ch, + pdf_obj_t *token) +{ + pdf_status_t rv; +start: + switch (context->substate) + { + case 1: /* ignore LF */ + context->substate = 0; + if (ch == 10) + return PDF_OK; + /* fall through */ + + case 0: /* no special state */ + if (ch == 92) /* '\\' */ + { + context->substate = 2; /* start an escape sequence */ + return PDF_OK; + } + else if (ch == 41 && context->intparam <= 0) /* ')'; end of string */ + { + context->intparam = -1; + return exit_state (context, token); + } + + if (!can_store_char (context)) + return PDF_EIMPLLIMIT; + else if (ch == 40) /* '(' */ + ++context->intparam; + else if (ch == 41) /* ')' */ + --context->intparam; + else if (ch == 13) /* '\r' */ + { + ch = 10; /* treat as LF */ + context->substate = 1; /* ignore the next char if it's LF */ + } + + return store_char (context, ch); + + case 2: /* just saw a '\\' (starting an escape sequence) */ + context->substate = 0; + if (ch == 98) /* 'b' */ + ch = 8; /* BS: backspace */ + else if (ch == 102) /* 'f' */ + ch = 12; /* FF: formfeed */ + else if (ch == 110) /* 'n' */ + ch = 10; /* NL: newline */ + else if (ch == 114) /* 'r' */ + ch = 13; /* CR: carriage return */ + else if (ch == 116) /* 't' */ + ch = 9; /* HT: horizontal tab */ + else if (ch == 10) /* NL */ + return PDF_OK; /* ignore the line break */ + else if (ch == 13) /* CR */ + { + /* ignore the line break; also ignore the next byte if it's LF */ + context->substate = 1; + return PDF_OK; + } + else if (ch >= 48 && ch <= 48+7) /* digits '0'--'7' */ + { + /* starting an octal escape; we'll read three digits even if the + * first is '4'--'7' (and calculate the final char modulo 256), + * since the PDF/PS specs say to ignore high-order overflow */ + context->substate = 3; + context->charparam = (ch-48); + return PDF_OK; + } + + /* for any other character, including '(', ')', and '\\', + * store the same character (dropping the leading backslash) */ + return store_char (context, ch); + + case 3: /* saw 1 digit of an octal escape */ + /* fall through */ + case 4: /* saw 2 digits of an octal escape */ + if (ch < 48 || ch > 48+7) /* not digits '0'--'7' */ + { + rv = store_char (context, context->charparam); + if (rv != PDF_OK) return rv; + + /* ch isn't part of the escape sequence, so retry */ + context->substate = 0; + goto start; + } + + /* ch is a digit from '0'--'7' */ + context->charparam = ((context->charparam & 0x1f) << 3) | (ch - 48); + if (context->substate == 4) /* this was the final digit */ + { + rv = store_char (context, context->charparam); + if (rv != PDF_OK) return rv; + + context->substate = 0; + return PDF_OK; + } + + context->substate = 4; + return PDF_OK; + + default: + assert (0); + } +} + + +static INLINE pdf_status_t +handle_hexstring_char (pdf_tokeniser_t context, + pdf_char_t ch, + pdf_obj_t *token) +{ + pdf_status_t rv; + + if (context->substate == 0) + { + /* this is the first character after the initial '<' */ + if (ch == 60) /* '<' */ + { + /* this was actually the start of a dictionary */ + context->state = PDF_TOKENISER_STATE_PENDING; + context->charparam = ch; + return exit_state (context, token); + } + + context->substate = 1; + } + + if (pdf_is_wspace_char (ch)) + return PDF_OK; + + if (ch == 62) /* '>': end of hex string */ + { + if (context->substate == 2) + { + /* the last digit is missing; assume it's '0' */ + rv = store_char (context, context->charparam << 4); + if (rv != PDF_OK) return rv; + } + + context->substate = 3; /* saw end of string */ + return exit_state (context, token); + } + + if ( (ch = hexval (ch)) == 255 ) + return PDF_EBADFILE; + + if (context->substate == 1) /* first character in a pair */ + { + context->substate = 2; + context->charparam = ch; + return PDF_OK; + } + + rv = store_char (context, (context->charparam << 4) | ch); + if (rv == PDF_OK) + context->substate = 1; + return rv; +} + +pdf_status_t +pdf_tokeniser_read (pdf_tokeniser_t context, pdf_obj_t *token) +{ + pdf_status_t rv; + pdf_char_t ch; + pdf_obj_t new_token = NULL; + + if (!context || !context->stream || !token) + return PDF_EBADDATA; + + while ( (rv = pdf_stm_peek_char (context->stream, &ch)) == PDF_OK ) + { + rv = handle_char (context, ch, &new_token); + if (rv == PDF_OK) + { + /* The character we peeked at was accepted, so get rid of it. */ + pdf_stm_read_char (context->stream, &ch); + } + + if (new_token) + { + /* Don't return an error code if we got a valid token. + * We'll probably see the same error on the next call since we + * didn't call read_char. */ + assert (rv == PDF_OK || rv == PDF_EAGAIN); + goto ret_token; + } + else if (rv != PDF_OK && rv != PDF_EAGAIN) + return rv; + } + + /* peek_char returned an error code (rv) */ + if (rv != PDF_EEOF) + return rv; + + rv = exit_state (context, &new_token); + if (rv != PDF_OK) + return rv; + + context->state = PDF_TOKENISER_STATE_EOF; + if (new_token) + goto ret_token; + else + return PDF_EEOF; + +ret_token: + assert (new_token); + *token = new_token; + return PDF_OK; +} + + +pdf_status_t +pdf_tokeniser_end_at_stream (pdf_tokeniser_t context) +{ + if (context->state && (context->state != PDF_TOKENISER_STATE_COMMENT)) + return PDF_EBADFILE; + + context->findstream = 1; + return PDF_OK; +} + + +static INLINE int +parse_integer (pdf_buffer_t buffer, int *int_value, int *int_state) +{ + /* Parse an ASCII integer with the given radix, at the beginning of + * the buffer (possibly leaving unread bytes at the end). + * + * Return value is 0 on failure, or a bitmask otherwise: + * 1 = valid integer + * 2 = signed + * 4 = overflowed (no value stored in *int_value) + */ + + int sign = 0, tmpint = 0, overflowed = 0, ret; + /* Integer states (int_state): + * 0 = at start (looking for sign or digits) + * 1 = saw sign + * 2 = saw digits + */ + + *int_state = 0; + for (; buffer->rp < buffer->wp; ++buffer->rp) + { + int chval; + pdf_char_t ch = buffer->data[buffer->rp]; + if (ch == 43 || ch == 45) /* '+','-' */ + { + if (*int_state != 0) + goto out; + + *int_state = 1; + sign = (ch == 43) ? 1 : -1; + continue; + } + + chval = ch - 48; /* assume this is a digit */ + if (chval < 0 || chval > 9) + goto out; /* not a valid number */ + + *int_state = 2; + if (overflowed) + continue; + + /* convert the digits to an integer, if possible */ + if (sign < 0) + { + chval = -chval; + if ( tmpint < (INT_MIN/10) + || (tmpint == (INT_MIN/10) && chval < (INT_MIN%10)) ) + { + overflowed = 1; /* would overflow */ + continue; + } + } + else + { + if ( tmpint > (INT_MAX/10) + || (tmpint == (INT_MAX/10) && chval > (INT_MAX%10)) ) + { + overflowed = 1; /* would overflow */ + continue; + } + } + + tmpint += chval + (tmpint * 9); + } + +out: + if (*int_state != 2) + return 0; /* never saw any digits */ + + ret = 1; + if (sign) ret += 2; + if (overflowed) + ret += 4; + else + *int_value = tmpint; + + return ret; +} + + +static INLINE int +validate_real (pdf_buffer_t buffer, int int_state) +{ + /* Determines whether the given number is a valid PS/PDF real number; + * assumes the initial sign was already read (if present), and any data + * before buffer->rp is a valid integer. + * + * Return value: + * 0 = not a real number + * 1 = valid PDF/PS real + */ + + int seen_point = 0; + /* Integer states (int_state): + * 0 = at start + * 1 = saw sign + * 2 = saw digits + */ + + for (; buffer->rp < buffer->wp; ++buffer->rp) + { + pdf_char_t ch = buffer->data[buffer->rp]; + if (ch == 46) /* '.' */ + { + if (!seen_point) + seen_point = 1; + else + return 0; + } + else if (ch == 43 || ch == 45) /* '+','-' */ + { + if (int_state == 0) + int_state = 1; + else + return 0; + } + else if (ch >= 48+0 && ch <= 48+9) + int_state = 2; + else + return 0; + } + + return (int_state == 2); /* only valid if we saw a digit */ +} + + +/* Given a buffer containing a validated PDF real (in ASCII), convert it to a + * double by translating it to the execution character set, replacing '.' with + * the locale's decimal point, and calling strtod. */ +static INLINE pdf_status_t +parse_real (pdf_buffer_t buffer, char *locale_dec_pt, double *value) +{ + pdf_status_t ret; + size_t tmplen, wpos, ptlen; + char *tmp, *endptr; + + ptlen = strlen (locale_dec_pt); + /* we may remove 1 byte ('.') and replace it with ptlen bytes */ + tmplen = buffer->wp - 1 + ptlen; + + tmp = pdf_alloc (tmplen + 1); + if (!tmp) + return PDF_ENOMEM; + + wpos = 0; + ret = PDF_ERROR; /* nothing should fail if the buffer was validated */ + for (buffer->rp = 0; buffer->rp < buffer->wp; ++buffer->rp) + { + pdf_char_t ch = buffer->data[buffer->rp]; + if (wpos >= tmplen) + goto out; + + if (ch == 46) /* '.' */ + { + if (wpos + ptlen > tmplen) + goto out; + + memcpy (tmp + wpos, locale_dec_pt, ptlen); + wpos += ptlen; + } + else if (ch == 43) /* '+' */ + tmp[wpos++] = '+'; + else if (ch == 45) /* '-' */ + tmp[wpos++] = '-'; + else if (ch >= 48+0 && ch <= 48+9) /* '0'--'9' */ + tmp[wpos++] = '0' + (ch-48); + else + goto out; + } + + /* null-terminate the new string, and call strtod to get its value + * (strtof would also work if it's available) */ + tmp[wpos] = '\0'; + *value = strtod (tmp, &endptr); + if (endptr == tmp + wpos) + ret = PDF_OK; + +out: + pdf_dealloc (tmp); + return ret; +} + + +/* + * Return value: + * 0 = not a number + * 1 = integer (stored in *int_value) + * 2 = real + */ +static int +recognize_number (pdf_buffer_t buffer, int *int_value) +{ + int rv, tmpint = 0, int_state = 0; + + /* try to parse as an integer */ + + buffer->rp = 0; + rv = parse_integer (buffer, &tmpint, &int_state); + + if (buffer->rp < buffer->wp) /* didn't look at the whole buffer */ + { + rv = validate_real (buffer, int_state); + if (rv == 1) + return 2; + + return 0; + } + + if (!rv) return 0; + else if (rv & 4) + return 2; /* integer overflowed, but could be read as a real */ + + *int_value = tmpint; + return 1; +} + + +/* End of pdf-rd-tokeniser.c */ === added file 'src/object/pdf-rd-tokeniser.h' --- src/object/pdf-rd-tokeniser.h 1970-01-01 00:00:00 +0000 +++ src/object/pdf-rd-tokeniser.h 2009-01-21 22:01:25 +0000 @@ -0,0 +1,120 @@ +/* -*- mode: C -*- Time-stamp: "2009-01-05 08:53:02 mgold" + * + * File: pdf-rd-tokeniser.h + * Date: Mon Dec 29 00:45:09 2008 + * + * GNU PDF Library - Stream tokeniser + * + */ + +/* Copyright (C) 2008 Michael Gold */ + +/* This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PDF_RD_TOKENISER +#define PDF_RD_TOKENISER +#include "pdf-types.h" +#include "pdf-stm.h" +#include "pdf-obj.h" + +/* BEGIN PUBLIC */ +/* pdf-rd-tokeniser.h */ + +struct pdf_tokeniser_s; /* opaque type */ +typedef struct pdf_tokeniser_s *pdf_tokeniser_t; + +pdf_status_t pdf_tokeniser_new(pdf_stm_t stm, pdf_tokeniser_t *tokr); +pdf_status_t pdf_tokeniser_destroy(pdf_tokeniser_t tokr); +pdf_status_t pdf_tokeniser_read(pdf_tokeniser_t tokr, pdf_obj_t *token); +pdf_status_t pdf_tokeniser_end_at_stream(pdf_tokeniser_t tokr); +pdf_status_t pdf_tokeniser_reset_state(pdf_tokeniser_t tokr); +/* END PUBLIC */ + +enum pdf_tokeniser_state_e { + PDF_TOKENISER_STATE_NONE = 0, + PDF_TOKENISER_STATE_COMMENT, + PDF_TOKENISER_STATE_KEYWORD, + PDF_TOKENISER_STATE_NAME, + PDF_TOKENISER_STATE_STRING, + PDF_TOKENISER_STATE_HEXSTRING, + PDF_TOKENISER_STATE_DICTEND, + PDF_TOKENISER_STATE_STREAMSTART, + PDF_TOKENISER_STATE_PENDING, + PDF_TOKENISER_STATE_EOF +}; + +/* Tokeniser states (from pdf_tokeniser_state_e): + * NONE - Initial state; not reading a token. + * COMMENT - Reading a comment. buffer collects the comment bytes, excluding + * intparam is 1 if this token is continued from a previous token, + * and 0 otherwise. + * KEYWORD - Reading some regular characters into buffer; this could result + * in a symbol like "null", or a number. + * NAME - Reading a name (which starts with '/'). + * Substates: + * 0 - normal state + * 1 - just read a '#' (escape prefix) + * 2 - read the first hex digit after '#'; the value is in charparam + * buffer collects the name, excluding the initial '/'. + * STRING - Reading a literal string (enclosed in '(', ')'). + * Substates: + * 0 - normal state + * 1 - ignore the next byte if its value is 10 (ASCII LF; + * this is used to treat CRLF as a single line ending) + * 2 - just saw a backslash (escape prefix) + * 3 - read 1 octal digit; the value is in charparam + * 4 - read 2 octal digits; the value is in charparam + * intparam is the bracket nesting level; ')' at level 0 ends the string. + * buffer collects the string. + * HEXSTRING - Reading a hex string. + * Substates: + * 0 - initial state: we just saw the opening '<', and if the next byte is + * also '<' this is the start of a dictionary rather than a string + * 1 - normal state (the next hex digit will be the first in a pair) + * 2 - read the first hex digit; its value is in charparam + * 3 - end state; saw the closing '>' + * buffer collects the string. + * DICTEND - Just got a '>'; expecting another. + * Substates: + * 0 - starting state + * 1 - saw the second '>' + * PENDING - Need to emit a token (determined by charparam) ASAP. + * EOF - Can't continue tokenising (reached EOF, or beginning of stream) + */ + +enum pdf_tokeniser_flag_e { + PDF_TOKENISER_FLAG_RET_COMMENTS = 1, /* return comments as tokens */ + PDF_TOKENISER_FLAG_PDF11 = 2, /* disallow '#' escapes in names */ +}; + +/* Internal state */ +struct pdf_tokeniser_s { + int flags; /* miscellaneous settings (from pdf_tokeniser_flag_e) */ + pdf_stm_t stream; /* stream to read bytes from */ + char *decimal_point; + + /* variables used by the state machine (described above) */ + int state; + int substate; + pdf_char_t charparam; + int intparam; + pdf_buffer_t buffer; + pdf_bool_t findstream; + /***/ +}; + +#endif + +/* End of pdf-rd-tokeniser.h */ === modified file 'torture/unit/Makefile.am' --- torture/unit/Makefile.am 2009-01-01 19:35:42 +0000 +++ torture/unit/Makefile.am 2009-01-20 18:51:58 +0000 @@ -181,6 +181,8 @@ TEST_SUITE_FP = base/fp/pdf-fp-func-eval.c +TEST_SUITE_READER = object/rd/pdf-tokeniser-read.c + TEST_ENVIRONMENT = CHARSETALIASDIR=$(top_srcdir)/lib TEST_FILES = $(TEST_SUITE_LIST) \ @@ -191,7 +193,8 @@ $(TEST_SUITE_CRYPT) \ $(TEST_SUITE_ALLOC) \ $(TEST_SUITE_STM) \ - $(TEST_SUITE_FP) + $(TEST_SUITE_FP) \ + $(TEST_SUITE_READER) TSUITE_FILES = base/alloc/tsuite-alloc.c \ base/list/tsuite-list.c \ @@ -201,7 +204,8 @@ base/types/tsuite-types.c \ base/crypt/tsuite-crypt.c \ base/stm/tsuite-stm.c \ - base/fp/tsuite-fp.c + base/fp/tsuite-fp.c \ + object/rd/tsuite-rd.c TORTUTILS_FILES = $(top_srcdir)/torture/tortutils/tortutils.h \ $(top_srcdir)/torture/tortutils/tortutils.c === added directory 'torture/unit/object' === added directory 'torture/unit/object/rd' === added file 'torture/unit/object/rd/pdf-tokeniser-read.c' --- torture/unit/object/rd/pdf-tokeniser-read.c 1970-01-01 00:00:00 +0000 +++ torture/unit/object/rd/pdf-tokeniser-read.c 2009-01-22 03:38:16 +0000 @@ -0,0 +1,168 @@ +/* -*- mode: C -*- Time-stamp: "2009-01-14 05:44:48 mgold" + * + * File: pdf-tokeniser-read.c + * Date: Wed Jan 14 05:44:48 2009 + * + * GNU PDF Library - Unit tests for pdf_tokeniser_read + * + */ + +#include +#include +#include +#include "config.h" +#include "pdf.h" + +#define STR_AND_LEN(s) (s),(sizeof(s)-1) + +/* Initialize an in-memory reader stream (pdf_stm_t *stm) + * with the given string (a char* constant). */ +#define INIT_STM_STR(stm,str) do{ \ + fail_unless(PDF_OK==pdf_stm_mem_new( STR_AND_LEN(str), \ + 0 /*cache_size*/, PDF_STM_READ /*mode*/, (stm) )); \ + }while(0) + +#define INIT_TOKR(tokr,stm) do{ fail_unless( \ + PDF_OK==pdf_tokeniser_new((stm),(tokr)) ); }while(0) +#define _EXPECT_TOK(tokr,tokexpr) do{ \ + pdf_obj_t _exp_tok; \ + fail_unless(PDF_OK == (tokexpr)); \ + fail_unless(get_token( (tokr), _exp_tok )); \ + pdf_obj_destroy(_exp_tok); \ + }while(0) +#define EXPECT_VALUELESS(tokr,val) _EXPECT_TOK((tokr), \ + pdf_tok_valueless_new(val, &_exp_tok) ) +#define EXPECT_KEYWORD(tokr,val) _EXPECT_TOK((tokr), \ + pdf_tok_keyword_new(STR_AND_LEN(val), &_exp_tok) ) +#define EXPECT_NAME(tokr,val) _EXPECT_TOK((tokr), \ + pdf_obj_name_new(STR_AND_LEN(val), &_exp_tok) ) +#define EXPECT_INTEGER(tokr,val) _EXPECT_TOK((tokr), \ + pdf_obj_integer_new(val, &_exp_tok) ) +#define EXPECT_REAL(tokr,val) _EXPECT_TOK((tokr), \ + pdf_obj_real_new(val, &_exp_tok) ) +#define EXPECT_STRING(tokr,val) _EXPECT_TOK((tokr), \ + pdf_obj_string_new(STR_AND_LEN(val), &_exp_tok) ) + +static void *nonnull(void *ptr) +{ + if (ptr) return ptr; + + fprintf(stderr, "error: got unexpected null pointer; aborting"); + abort(); +} + +/* Read a token; return 1 if it's equal to 'expected', or 0 otherwise. */ +static int +get_token(pdf_tokeniser_t tokr, pdf_obj_t expected) +{ + int ret = 0; + pdf_obj_t token = NULL; + pdf_status_t rv = pdf_tokeniser_read(tokr, &token); + + nonnull(expected); + ret = (rv == PDF_OK) && pdf_obj_equal_p(token, expected); + + if (token) pdf_obj_destroy(token); + return ret; +} + +static int +tokr_eof(pdf_tokeniser_t tokr) +{ + pdf_obj_t token = NULL; + pdf_status_t rv = pdf_tokeniser_read(tokr, &token); +if(token) +{ +printf("tok type %d\n", pdf_obj_type(token)); +} +else if (rv != PDF_EEOF) +{ +printf("eof rv %d\n", rv); +} + if (token) pdf_obj_destroy(token); + return (rv == PDF_EEOF); +} + +/* + * Test: pdf_tokeniser_read_001 + * Description: + * Read various tokens from an in-memory stream, and check whether they + * match the expected values. + * Success condition: + * Each token matches the expected one (according to pdf_obj_equal_p), + * and no tokens remain afterwards. + */ +START_TEST(pdf_tokeniser_read_001) +{ + pdf_stm_t stm; + pdf_tokeniser_t tokr; + + INIT_STM_STR(&stm, "abc}{/kw/k#20w%com\n" + "]1 2.0[>><4142434a4F4>" + "(str(\\551ng(\0\r)\\60x\\\r\n)>>)" + "<<1208925819614629174706176" /* 2^80 */ + "\0\n \r\t\f" /* whitespace */ + "(\\n\\r\\t\\b\\f\\)\\(\\\\\\X\08)"); /* string escapes */ + INIT_TOKR(&tokr, stm); + + EXPECT_KEYWORD( tokr, "abc" ); + EXPECT_VALUELESS( tokr, PDF_PROC_END_TOK ); + EXPECT_VALUELESS( tokr, PDF_PROC_START_TOK ); + EXPECT_NAME( tokr, "kw" ); + EXPECT_NAME( tokr, "k w" ); + EXPECT_VALUELESS( tokr, PDF_ARRAY_END_TOK ); + EXPECT_INTEGER( tokr, 1 ); + EXPECT_REAL( tokr, 2 ); + EXPECT_VALUELESS( tokr, PDF_ARRAY_START_TOK ); + EXPECT_VALUELESS( tokr, PDF_DICT_END_TOK ); + EXPECT_STRING( tokr, "ABCJO@" ); + EXPECT_STRING( tokr, "str(ing(\0\n)0x)>>" ); + EXPECT_VALUELESS( tokr, PDF_DICT_START_TOK ); + EXPECT_REAL( tokr, 1208925819614629174706176.0 ); /* 2^80 */ + EXPECT_STRING( tokr, "\n\r\t\b\f)(\\X\0\070" ); + + fail_unless( tokr_eof(tokr) ); +} +END_TEST + +/* + * Test: pdf_tokeniser_read_002 + * Description: + * Test the pdf_tokeniser_end_at_stream function. + * Success condition: + * The stream should be positioned after the '\n' character (at '<'), and + * the tokeniser should act as if it reached the end of the input file. + */ +START_TEST(pdf_tokeniser_read_002) +{ + pdf_stm_t stm; + pdf_tokeniser_t tokr; + pdf_char_t ch; + + INIT_STM_STR(&stm, "stream \r\t %com \n" + ">UNPARSABLE DATA IN STREAM\n\n\n\n\n\nx"); + INIT_TOKR(&tokr, stm); + + EXPECT_KEYWORD( tokr, "stream" ); + fail_unless( PDF_OK==pdf_tokeniser_end_at_stream(tokr) ); + fail_unless( tokr_eof(tokr) ); + fail_unless( PDF_OK==pdf_stm_peek_char(stm, &ch) ); + fail_unless( ch == '>' ); + /*TODO: verify stream position */ +} +END_TEST + +/* + * Test case creation function + */ +TCase * +test_pdf_tokeniser_read (void) +{ + TCase *tc = tcase_create("pdf_tokeniser_read"); + tcase_add_test(tc, pdf_tokeniser_read_001); + tcase_add_test(tc, pdf_tokeniser_read_002); + + return tc; +} + +/* End of pdf-tokeniser-read.c */ === added file 'torture/unit/object/rd/tsuite-rd.c' --- torture/unit/object/rd/tsuite-rd.c 1970-01-01 00:00:00 +0000 +++ torture/unit/object/rd/tsuite-rd.c 2009-01-20 18:51:58 +0000 @@ -0,0 +1,43 @@ +/* -*- mode: C -*- Time-stamp: "2009-01-14 05:43:09 mgold" + * + * File: tsuite-rd.c + * Date: Wed Jan 14 05:43:09 2009 + * + * GNU PDF Library - Testcase definition for the reader module + * + */ + +/* Copyright (C) 2009 Free Software Foundation, Inc. */ + +/* This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +extern TCase *test_pdf_tokeniser_read (void); + +Suite * +tsuite_rd () +{ + Suite *s; + + s = suite_create("rd"); + + suite_add_tcase (s, test_pdf_tokeniser_read ()); + + return s; +} + + +/* End of tsuite-rd.c */ === modified file 'torture/unit/runtests.c' --- torture/unit/runtests.c 2008-12-10 20:45:27 +0000 +++ torture/unit/runtests.c 2009-01-20 18:51:58 +0000 @@ -18,6 +18,7 @@ extern Suite *tsuite_crypt (void); extern Suite *tsuite_error (void); extern Suite *tsuite_fp (void); +extern Suite *tsuite_rd (void); int main (int argc, char **argv) @@ -37,6 +38,7 @@ srunner_add_suite (sr, tsuite_error ()); srunner_add_suite (sr, tsuite_stm ()); srunner_add_suite (sr, tsuite_fp ()); + srunner_add_suite (sr, tsuite_rd ()); /* Set log file */ srunner_set_log (sr, "ut.log"); === modified file 'utils/Makefile.am' --- utils/Makefile.am 2009-01-01 17:00:46 +0000 +++ utils/Makefile.am 2009-01-21 07:33:48 +0000 @@ -22,7 +22,7 @@ ICONV_LIBS = -liconv endif #ICONV -bin_PROGRAMS = pdf-filter +bin_PROGRAMS = pdf-filter toktest parsetest LDADD = $(top_builddir)/src/libgnupdf.la \ $(ICONV_LIBS) @@ -31,5 +31,7 @@ -I$(top_srcdir)/src/base -I $(top_srcdir)/src/object pdf_filter_SOURCES = pdf-filter.h pdf-filter.c +toktest_SOURCES = toktest.c printobj.c +parsetest_SOURCES = parsetest.c printobj.c # End of Makefile.am === added file 'utils/parsetest.c' --- utils/parsetest.c 1970-01-01 00:00:00 +0000 +++ utils/parsetest.c 2009-01-22 03:38:16 +0000 @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include "pdf-rd-parser.h" +#include "pdf-obj.h" +#include "pdf-error.h" + +void print_file(FILE *file) +{ + pdf_status_t rv; + pdf_parser_t parser = NULL; + pdf_obj_t stack; + pdf_stm_t stm = NULL; + + if (file == NULL) + { + fprintf(stderr, "file==NULL\n"); + return; + } + + rv = pdf_stm_cfile_new (file, 0, 0 /*cache_size*/, PDF_STM_READ, &stm); + if (rv != PDF_OK) + { + fprintf(stderr, "failed to create stream\n"); + goto out; + } + + rv = pdf_parser_new(stm, &parser); + if (rv != PDF_OK) + { + fprintf(stderr, "failed to create parser\n"); + goto out; + } + + while (( rv = pdf_parser_read_to_command(parser, &stack) ) == PDF_OK) + { + pdf_size_t stacksize = pdf_obj_array_size(stack); + pdf_obj_t cmdobj = NULL; + int atstream; + + print_obj(stack); + + rv = pdf_obj_array_get(stack, stacksize-1, &cmdobj); + assert (rv == PDF_OK); + assert (cmdobj && pdf_obj_type(cmdobj) == PDF_KEYWORD_TOK); + + /* We can't easily read a stream's length since the value may be an + * indirect object, so we'll stop at the first stream. */ + atstream = pdf_tok_keyword_equal_p(cmdobj, "stream"); + + rv = pdf_obj_array_clear(stack); + assert (rv == PDF_OK); + + if (atstream) + { + printf("stopping at stream keyword\n"); + goto out; + } + } + + if (rv != PDF_EEOF) + { + fprintf(stderr, "parser read error %d\n", rv); + goto out; + } + + fprintf(stderr, "done\n"); +out: + if (parser) pdf_parser_destroy(parser); + if (stm) pdf_stm_destroy(stm); +} + +int main(int argc, char **argv) +{ + setlocale(LC_ALL, ""); + //print_file(stdin); + + FILE *file = fopen("/home/michael/howtoread-pdf.pdf", "rb"); + print_file(file); + if (file) fclose(file); + return 0; +} === added file 'utils/printobj.c' --- utils/printobj.c 1970-01-01 00:00:00 +0000 +++ utils/printobj.c 2009-01-21 22:01:25 +0000 @@ -0,0 +1,198 @@ +#include +#include +#include +#include +#include +#include "pdf-rd-parser.h" +#include "pdf-obj.h" +#include "pdf-error.h" + +static char *fmtbin(const char *data, int size) +{ + int i; + char *ret, *retp; + + if (size == -1) + size = strlen(data); + + ret = malloc(3 + (size*4)); + if (!ret) abort(); + + retp = ret; + (*retp++) = '"'; + for (i = 0; i < size; ++i) + { + unsigned char ch = data[i]; + if (ch == '"' || ch == '\\') + { + *(retp++) = '\\'; + *(retp++) = ch; + } + else if (data[i] >= 32 && data[i] < 127) + *(retp++) = ch; + else + { + sprintf(retp, "\\x%02x", (int)ch); + retp += 4; + } + } + (*retp++) = '"'; + (*retp++) = '\0'; + return ret; +} + +static void print_obj_priv(pdf_obj_t obj, int indent) +{ + char tabbuf[10], tmpbuf[256]; + const char *typ, *str = NULL; + int strsize = -1, recurse = 0; + pdf_size_t i; + + switch(pdf_obj_type(obj)) + { + case PDF_NULL_OBJ: + typ = "NULL"; + + case PDF_BOOLEAN_OBJ: + typ = "BOOLEAN"; + str = pdf_obj_bool_value(obj) ? "true" : "false"; + break; + + case PDF_INT_OBJ: + typ = "INT"; + snprintf(tmpbuf, sizeof(tmpbuf), "%d", pdf_obj_int_value(obj)); + str = tmpbuf; + break; + + case PDF_REAL_OBJ: + typ = "REAL"; + snprintf(tmpbuf, sizeof(tmpbuf), "%f", pdf_obj_real_value(obj)); + str = tmpbuf; + break; + + case PDF_STRING_OBJ: + typ = "STRING"; + str = pdf_obj_string_data(obj); + strsize = pdf_obj_string_size(obj); + break; + + case PDF_NAME_OBJ: + typ = "NAME"; + str = pdf_obj_string_data(obj); + strsize = pdf_obj_name_size(obj); + break; + + case PDF_ARRAY_OBJ: + typ = "ARRAY"; + recurse = 1; + break; + + case PDF_DICT_OBJ: + typ = "DICT"; + recurse = 2; + break; + + case PDF_INDIRECT_OBJ: + typ = "INDIRECT"; + snprintf(tmpbuf, sizeof(tmpbuf), "%d,%d", + pdf_obj_indirect_on(obj), + pdf_obj_indirect_gn(obj) ); + str = tmpbuf; + break; + + case PDF_STREAM_OBJ: + typ = "STREAM"; + snprintf(tmpbuf, sizeof(tmpbuf), "%d,", + pdf_obj_stream_data(obj) ); + str = tmpbuf; + + /* print the dictionary too */ + recurse = 2; + obj = pdf_obj_stream_dict(obj); + break; + + case PDF_COMMENT_TOK: + typ = "COMMENT"; + str = pdf_tok_comment_data(obj); + strsize = pdf_tok_comment_size(obj); + break; + + case PDF_KEYWORD_TOK: + typ = "KEYWORD"; + str = pdf_tok_keyword_data(obj); + strsize = pdf_tok_keyword_size(obj); + break; + + case PDF_DICT_START_TOK: + typ = "DICT_START"; + break; + case PDF_DICT_END_TOK: + typ = "DICT_END"; + break; + case PDF_ARRAY_START_TOK: + typ = "ARRAY_START"; + break; + case PDF_ARRAY_END_TOK: + typ = "ARRAY_END"; + break; + case PDF_PROC_START_TOK: + typ = "PROC_START"; + break; + case PDF_PROC_END_TOK: + typ = "PROC_END"; + break; + + default: + typ = "[unknown]"; + sprintf(tmpbuf, "%d", pdf_obj_type(obj)); + str = tmpbuf; + } + + for (i = 0; i < indent && i < sizeof(tabbuf)-1; ++i) + tabbuf[i] = '\t'; + tabbuf[i] = '\0'; + + if (recurse) + { + char *op = (recurse==1) ? "[" : "<<"; + char *ed = (recurse==1) ? "]" : ">>"; + char *ext = str ? str : ""; + + printf("%s%s(%s%s\n", tabbuf, typ, ext, op); + if (recurse == 1) + { + pdf_size_t size = pdf_obj_array_size(obj); + for (i = 0; i < size; ++i) + { + pdf_obj_t child; + pdf_obj_array_get(obj, i, &child); + print_obj_priv(child, indent+1); + } + } + else + { + printf("%s....TODO [size %d]\n", tabbuf, pdf_obj_dict_size(obj)); + } + printf("%s%s)\n", tabbuf, ed); + } + else + { + if (str == NULL) + { + tmpbuf[0] = '\0'; + str = tmpbuf; + } + if (str != tmpbuf) str = fmtbin(str, strsize); + printf("%s%s(%s)\n", tabbuf, typ, str); + if (str != tmpbuf) + { + free((void*)str); + } + } + +}; + +void print_obj(pdf_obj_t obj) +{ + print_obj_priv(obj, 0); +} === added file 'utils/toktest.c' --- utils/toktest.c 1970-01-01 00:00:00 +0000 +++ utils/toktest.c 2009-01-21 07:33:48 +0000 @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include "pdf-rd-tokeniser.h" +#include "pdf-obj.h" +#include "pdf-error.h" + +void print_file(FILE *file) +{ + pdf_status_t rv; + pdf_tokeniser_t tokeniser = NULL; + pdf_obj_t token; + pdf_stm_t stm = NULL; + + rv = pdf_stm_cfile_new (file, 0, 0 /*cache_size*/, PDF_STM_READ, &stm); + if (rv != PDF_OK) + { + fprintf(stderr, "failed to create stream\n"); + goto out; + } + + rv = pdf_tokeniser_new(stm, &tokeniser); + if (rv != PDF_OK) + { + fprintf(stderr, "failed to create tokeniser\n"); + goto out; + } + + while (( rv = pdf_tokeniser_read(tokeniser, &token) ) == PDF_OK) + { + print_obj(token); + pdf_obj_destroy(token); + } + + if (rv != PDF_EEOF) + { + fprintf(stderr, "read_token error %d\n", rv); + goto out; + } + + fprintf(stderr, "done\n"); +out: + if (tokeniser) pdf_tokeniser_destroy(tokeniser); + if (stm) pdf_stm_destroy(stm); +} + +int main(int argc, char **argv) +{ + setlocale(LC_ALL, ""); + print_file(stdin); + return 0; +} # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWTJ9d1MAbmR/gH////////// ///f77////5giv73od7OF3feHvWF5YM77gHlubfffDWAG9q7y6KH2577fXx98rdXt1W9nuG9QCgB 6UAa+7npV83cjfJ9XbXTJ5V0cuj7u+IvGdvWj5Prp77dmdZ7ZHWbsAPe98woBCF9u+Oh28wAPXb7 457vfd332+7jV93tb1WM+Vp9718e+ehlncHO5zkl10d3HDu7giNNA+7jvZnQxKoOxrtbCVaAybaR 5Jm3zegPfWbx6B8DtVO3SXRQW2MwZY8e6Hj1rrfbmR3UrMt58Jb3YlT2VXgT3tXFFj2z1Nu4Lx3r d3Vsd7777d4T0w3uz73r6O+V99x9zq7sHF8ek20ndFhZ9zp9eI9vdod23Op9elwzZbwO3ZlzuuLZ 0Oqhdh7c9JUD6N2Nfc+ze8kVmt1u1zUNvjPvvdvieul1MzHrA7YDSty5Hve7KzbznT27vburQ7jd mSEkQCACEYgIAkzKamPSJhkJtAppkh7U9qkDR5ID0mQaD9UGmQEIiQ0mUNNqGmkyTam9E0mgNG0n qANADQAAA9QNABIJEImk0UyYJonlPIU9lNpJo8TU/VPKepoaHqaNPUAGgaaepobSHqGjQQpIQQIa AmhMjTJonpGmo8I0mxQNp6mp5TTTT2kj1A9EZDQBo0ESSECaNNGmjTQTEE2hPJNMmlT8aVPYUzSb Sn6p+TSh7U9JPSaAPUGQekESQgTQCCRhTyg2VHqbKek001H6m01NA9SAADR6mmjEZGgxGTTv8Anb naGfCd1gtEESBBQSXJQI1AD9MCoOQoalETJShEEhE/nP2v6riBujRH/vwL2j/vzTrcgRkbhIO2tf bMTRtj81qeZsoamikadEORgUfu5lQX38/vS6WA3sgDPAxwgNMRR97NSFGozHIkKIzMiIwnvhM1ZR hJQUBBCZIYUQYQYRfe5FfqZfym181OSGl/kNkWiIdiEkP+eJGMdP0GtB+5QsgguD/s/7a+xP5wd/ N+73T1083Cq5myO69Wbfs8o8vlzKk1ZFf+IM9eZDhmN5sMi8+ZURHKcgr+uPPOq2zMysjIPbBqCI vSZljdfYs8OSMceOsVbYtsG40N7/mtTs7c5eCNsjmpZTxaZJ8Exob+Fr/dx5ZhH01JI+WH9vcG1j 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