diff options
Diffstat (limited to 'Build/source/texk/dvipdfmx/src/pdfobj.c')
-rw-r--r-- | Build/source/texk/dvipdfmx/src/pdfobj.c | 1217 |
1 files changed, 814 insertions, 403 deletions
diff --git a/Build/source/texk/dvipdfmx/src/pdfobj.c b/Build/source/texk/dvipdfmx/src/pdfobj.c index 0c7227859df..f770cbeaba0 100644 --- a/Build/source/texk/dvipdfmx/src/pdfobj.c +++ b/Build/source/texk/dvipdfmx/src/pdfobj.c @@ -1,4 +1,4 @@ -/* $Header: /home/cvsroot/dvipdfmx/src/pdfobj.c,v 1.47 2007/11/14 03:36:01 chofchof Exp $ +/* $Header: /home/cvsroot/dvipdfmx/src/pdfobj.c,v 1.57 2008/05/20 13:05:14 matthias Exp $ This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. @@ -33,6 +33,7 @@ #include "mem.h" #include "error.h" #include "mfileio.h" +#include "dpxutil.h" #include "pdflimits.h" #include "pdfencrypt.h" #include "pdfparse.h" @@ -107,6 +108,7 @@ struct pdf_stream { struct pdf_obj *dict; unsigned char *stream; + long *objstm_data; /* used for object streams */ unsigned long stream_length; unsigned long max_length; unsigned char _flags; @@ -114,13 +116,9 @@ struct pdf_stream struct pdf_indirect { + pdf_file *pf; unsigned long label; unsigned short generation; - int dirty; /* Dirty objects came from an input file and were not - generated by this program. They have a label in a - different numbering sequence. These are translated - when the object is written out */ - FILE *dirty_file; }; typedef void pdf_null; @@ -134,7 +132,6 @@ typedef struct pdf_stream pdf_stream; typedef struct pdf_indirect pdf_indirect; static FILE *pdf_output_file = NULL; -static FILE *pdf_input_file = NULL; static long pdf_output_file_position = 0; static long pdf_output_line_position = 0; @@ -143,32 +140,37 @@ static long compression_saved = 0; #define FORMAT_BUF_SIZE 4096 static char format_buffer[FORMAT_BUF_SIZE]; -static struct xref_entry +typedef struct xref_entry { - unsigned char type; - unsigned long field2; - unsigned short field3; - int used; /* Used by PDF import */ -} *output_xref; + unsigned char type; /* object storage type */ + unsigned long field2; /* offset in file or object stream */ + unsigned short field3; /* generation or index */ + pdf_obj *direct; /* used for imported objects */ + pdf_obj *indirect; /* used for imported objects */ +} xref_entry; + +static xref_entry *output_xref; static unsigned long pdf_max_ind_objects; static unsigned long next_label; static unsigned long startxref; +struct pdf_file +{ + FILE *file; + pdf_obj *trailer; + xref_entry *xref_table; + long num_obj; + long file_size; + int version; +}; + static pdf_obj *output_stream; #define OBJSTM_MAX_OBJS 200 /* the limit is only 100 for linearized PDF */ -typedef struct pdf_objstm -{ - pdf_obj *stream; - unsigned long obj_label[OBJSTM_MAX_OBJS]; - unsigned long obj_pos [OBJSTM_MAX_OBJS]; - unsigned char pos; -} pdf_objstm; - static int enc_mode; static int doc_enc_mode; @@ -181,7 +183,9 @@ static void pdf_flush_obj (pdf_obj *object, FILE *file); static void pdf_label_obj (pdf_obj *object); static void pdf_write_obj (pdf_obj *object, FILE *file); -static void pdf_flush_objstm (pdf_objstm *objstm); +static void set_objstm_data (pdf_obj *objstm, long *data); +static long *get_objstm_data (pdf_obj *objstm); +static void release_objstm (pdf_obj *objstm); static void pdf_out_char (FILE *file, char c); static void pdf_out (FILE *file, const void *buffer, long length); @@ -213,9 +217,6 @@ static void release_dict (pdf_dict *dict); static void write_stream (pdf_stream *stream, FILE *file); static void release_stream (pdf_stream *stream); -static pdf_obj *pdf_ref_file_obj (unsigned long obj_num, unsigned short obj_gen); -static pdf_obj *pdf_read_object (unsigned long obj_num, unsigned short obj_gen); - static int verbose = 0; static char compression_level = 9; @@ -262,37 +263,22 @@ pdf_obj_set_verbose(void) verbose++; } -static pdf_objstm *current_objstm = NULL; - -void -pdf_objstm_init () -{ - if (pdf_version >= 5) { - current_objstm = NEW(1, pdf_objstm); - current_objstm->pos = 0; - } -} - -void -pdf_objstm_close () -{ - if (current_objstm) { - RELEASE(current_objstm); - current_objstm = NULL; - } -} +static pdf_obj *current_objstm = NULL; +static int do_objstm; static void add_xref_entry (unsigned long label, unsigned char type, unsigned long field2, unsigned short field3) { if (label >= pdf_max_ind_objects) { pdf_max_ind_objects = (label/IND_OBJECTS_ALLOC_SIZE+1)*IND_OBJECTS_ALLOC_SIZE; - output_xref = RENEW(output_xref, pdf_max_ind_objects, struct xref_entry); + output_xref = RENEW(output_xref, pdf_max_ind_objects, xref_entry); } output_xref[label].type = type; output_xref[label].field2 = field2; output_xref[label].field3 = field3; + output_xref[label].direct = NULL; + output_xref[label].indirect = NULL; } #define BINARY_MARKER "%\344\360\355\370\n" @@ -311,8 +297,12 @@ pdf_out_init (const char *filename, int do_encryption) xref_stream->flags |= OBJ_NO_ENCRYPT; trailer_dict = pdf_stream_dict(xref_stream); pdf_add_dict(trailer_dict, pdf_new_name("Type"), pdf_new_name("XRef")); - } else + do_objstm = 1; + } else { + xref_stream = NULL; trailer_dict = pdf_new_dict(); + do_objstm = 0; + } output_stream = NULL; @@ -383,8 +373,6 @@ dump_xref_stream (void) pdf_obj *w; - pdf_label_obj(xref_stream); - /* determine the necessary size of the offset field */ pos = startxref; /* maximal offset value */ poslen = 1; @@ -425,8 +413,18 @@ pdf_out_flush (void) long length; /* Flush current object stream */ - if (current_objstm && current_objstm->pos) - pdf_flush_objstm(current_objstm); + if (current_objstm) { + release_objstm(current_objstm); + current_objstm =NULL; + } + + /* + * Label xref stream - we need the number of correct objects + * for the xref stream dictionary (= trailer). + * Labelling it in pdf_out_init (with 1) does not work (why?). + */ + if (xref_stream) + pdf_label_obj(xref_stream); /* Record where this xref is for trailer */ startxref = pdf_output_file_position; @@ -434,7 +432,7 @@ pdf_out_flush (void) pdf_add_dict(trailer_dict, pdf_new_name("Size"), pdf_new_number(next_label)); - if (pdf_version >= 5) + if (xref_stream) dump_xref_stream(); else { dump_xref_table(); @@ -553,6 +551,16 @@ void pdf_out (FILE *file, const void *buffer, long length) } } +/* returns 1 if a white-space character is necessary to separate + an object of type1 followed by an object of type2 */ +static +int pdf_need_white (int type1, int type2) +{ + return !(type1 == PDF_STRING || type1 == PDF_ARRAY || type1 == PDF_DICT || + type2 == PDF_STRING || type2 == PDF_NAME || + type2 == PDF_ARRAY || type2 == PDF_DICT); +} + static void pdf_out_white (FILE *file) { @@ -564,7 +572,7 @@ void pdf_out_white (FILE *file) } #define TYPECHECK(o,t) if (!(o) || (o)->type != (t)) {\ - ERROR("typecheck: Invalid object type. %d %d", (o) ? (o)->type : -1, (t));\ + ERROR("typecheck: Invalid object type: %d %d (line %d)", (o) ? (o)->type : -1, (t), __LINE__);\ } #define INVALIDOBJ(o) ((o) == NULL || (o)->type <= 0 || (o)->type > PDF_INDIRECT) @@ -631,9 +639,6 @@ pdf_link_obj (pdf_obj *object) pdf_obj * pdf_ref_obj (pdf_obj *object) { - pdf_obj *result; - pdf_indirect *indirect; - if (INVALIDOBJ(object)) ERROR("pdf_ref_obj(): passed invalid object."); @@ -643,25 +648,14 @@ pdf_ref_obj (pdf_obj *object) ERROR("Cannot continue..."); } - result = pdf_new_obj(PDF_INDIRECT); - indirect = NEW(1, pdf_indirect); - result->data = indirect; - if (object->type == PDF_INDIRECT) { - indirect->label = ((pdf_indirect *) (object->data))->label; - indirect->generation = ((pdf_indirect *) (object->data))->generation; - indirect->dirty = ((pdf_indirect *) (object->data))->dirty; - indirect->dirty_file = ((pdf_indirect *) (object->data))->dirty_file; + if (PDF_OBJ_INDIRECTTYPE(object)) { + return pdf_link_obj(object); } else { if (object->label == 0) { pdf_label_obj(object); } - indirect->label = object->label; - indirect->generation = object->generation; - indirect->dirty = 0; - indirect->dirty_file = NULL; + return pdf_new_indirect(NULL, object->label, object->generation); } - - return result; } static void @@ -675,26 +669,10 @@ write_indirect (pdf_indirect *indirect, FILE *file) { long length; - if (indirect->dirty) { - if (file == stderr) { - pdf_out(file, "{d}", 3); - length = sprintf(format_buffer, "%lu %hu R", indirect->label, indirect->generation); - pdf_out(stderr, format_buffer, length); - } else { - pdf_obj *clean; + ASSERT(!indirect->pf); - if (indirect->dirty_file != pdf_input_file) { - ERROR("Input PDF file doesn't match object: label=%lu, from_file=%p, current_file=%p", - indirect->label, indirect->dirty_file, pdf_input_file); - } - clean = pdf_ref_file_obj(indirect->label, indirect->generation); - pdf_write_obj(clean, file); - pdf_release_obj(clean); - } - } else { - length = sprintf(format_buffer, "%lu %hu R", indirect->label, indirect->generation); - pdf_out(file, format_buffer, length); - } + length = sprintf(format_buffer, "%lu %hu R", indirect->label, indirect->generation); + pdf_out(file, format_buffer, length); } pdf_obj * @@ -1140,13 +1118,17 @@ write_array (pdf_array *array, FILE *file) pdf_out_char(file, '['); if (array->size > 0) { unsigned long i; - + int type1 = PDF_UNDEFINED, type2; + for (i = 0; i < array->size; i++) { - if (i > 0) - pdf_out_white(file); - if (!array->values[i]) + if (array->values[i]) { + type2 = array->values[i]->type; + if (type1 != PDF_UNDEFINED && pdf_need_white(type1, type2)) + pdf_out_white(file); + type1 = type2; + pdf_write_obj(array->values[i], file); + } else WARN("PDF array element #ld undefined.", i); - pdf_write_obj(array->values[i], file); } } pdf_out_char(file, ']'); @@ -1319,10 +1301,7 @@ write_dict (pdf_dict *dict, FILE *file) pdf_out (file, "<<\n", 3); /* dropping \n saves few kb. */ while (dict->key != NULL) { pdf_write_obj(dict->key, file); - if (((dict -> value)->type) == PDF_BOOLEAN || - ((dict -> value)->type) == PDF_NUMBER || - ((dict -> value)->type) == PDF_INDIRECT || - ((dict -> value)->type) == PDF_NULL) { + if (pdf_need_white(PDF_NAME, (dict->value)->type)) { pdf_out_white(file); } pdf_write_obj(dict->value, file); @@ -1568,6 +1547,7 @@ pdf_new_stream (int flags) data->stream = NULL; data->stream_length = 0; data->max_length = 0; + data->objstm_data = NULL; result->data = data; result->flags |= OBJ_NO_OBJSTM; @@ -1693,6 +1673,12 @@ release_stream (pdf_stream *stream) RELEASE(stream->stream); stream->stream = NULL; } + + if (stream->objstm_data) { + RELEASE(stream->objstm_data); + stream->objstm_data = NULL; + } + RELEASE(stream); } @@ -1732,6 +1718,20 @@ pdf_stream_length (pdf_obj *stream) return (long) data->stream_length; } +static void +set_objstm_data (pdf_obj *objstm, long *data) { + TYPECHECK(objstm, PDF_STREAM); + + ((pdf_stream *) objstm->data)->objstm_data = data; +} + +static long * +get_objstm_data (pdf_obj *objstm) { + TYPECHECK(objstm, PDF_STREAM); + + return ((pdf_stream *) objstm->data)->objstm_data; +} + void pdf_add_stream (pdf_obj *stream, const void *stream_data, long length) { @@ -1750,6 +1750,119 @@ pdf_add_stream (pdf_obj *stream, const void *stream_data, long length) data->stream_length += length; } +#if HAVE_ZLIB +#define WBUF_SIZE 4096 +int +pdf_add_stream_flate (pdf_obj *dst, const void *data, long len) +{ + z_stream z; + Bytef wbuf[WBUF_SIZE]; + + z.zalloc = Z_NULL; z.zfree = Z_NULL; z.opaque = Z_NULL; + + z.next_in = (Bytef *) data; z.avail_in = len; + z.next_out = (Bytef *) wbuf; z.avail_out = WBUF_SIZE; + + if (inflateInit(&z) != Z_OK) { + WARN("inflateInit() failed."); + return -1; + } + + for (;;) { + int status; + status = inflate(&z, Z_NO_FLUSH); + if (status == Z_STREAM_END) + break; + else if (status != Z_OK) { + WARN("inflate() failed. Broken PDF file?"); + inflateEnd(&z); + return -1; + } + + if (z.avail_out == 0) { + pdf_add_stream(dst, wbuf, WBUF_SIZE); + z.next_out = wbuf; + z.avail_out = WBUF_SIZE; + } + } + + if (WBUF_SIZE - z.avail_out > 0) + pdf_add_stream(dst, wbuf, WBUF_SIZE - z.avail_out); + + return (inflateEnd(&z) == Z_OK ? 0 : -1); +} +#endif + + +int +pdf_concat_stream (pdf_obj *dst, pdf_obj *src) +{ + const char *stream_data; + long stream_length; + pdf_obj *stream_dict; + pdf_obj *filter; + + if (!PDF_OBJ_STREAMTYPE(dst) || !PDF_OBJ_STREAMTYPE(src)) + ERROR("Invalid type."); + + stream_data = pdf_stream_dataptr(src); + stream_length = pdf_stream_length (src); + stream_dict = pdf_stream_dict (src); + + if (pdf_lookup_dict(stream_dict, "DecodeParms")) { + WARN("Streams with DecodeParams not supported."); + return -1; + } + + filter = pdf_lookup_dict(stream_dict, "Filter"); + if (!filter) { + pdf_add_stream(dst, stream_data, stream_length); + return 0; +#if HAVE_ZLIB + } else { + char *filter_name; + if (PDF_OBJ_NAMETYPE(filter)) { + filter_name = pdf_name_value(filter); + if (filter_name && !strcmp(filter_name, "FlateDecode")) + return pdf_add_stream_flate(dst, stream_data, stream_length); + else { + WARN("DecodeFilter \"%s\" not supported.", filter_name); + return -1; + } + } else if (PDF_OBJ_ARRAYTYPE(filter)) { + if (pdf_array_length(filter) > 1) { + WARN("Multiple DecodeFilter not supported."); + return -1; + } else { + filter_name = pdf_name_value(pdf_get_array(filter, 0)); + if (filter_name && !strcmp(filter_name, "FlateDecode")) + return pdf_add_stream_flate(dst, stream_data, stream_length); + else { + WARN("DecodeFilter \"%s\" not supported.", filter_name); + return -1; + } + } + } else + ERROR("Broken PDF file?"); +#endif /* HAVE_ZLIB */ + } + + return -1; +} + +static pdf_obj * +pdf_stream_uncompress (pdf_obj *src) { + pdf_obj *dst = pdf_new_stream(0); + + TYPECHECK(src, PDF_STREAM); + + pdf_merge_dict(pdf_stream_dict(dst), pdf_stream_dict(src)); + pdf_remove_dict(pdf_stream_dict(dst), "Length"); + pdf_concat_stream(dst, src); + + return dst; +} + void pdf_stream_set_flags (pdf_obj *stream, int flags) { @@ -1838,41 +1951,41 @@ pdf_flush_obj (pdf_obj *object, FILE *file) pdf_out(file, "\nendobj\n", 8); } -static void -pdf_add_objstm (pdf_objstm *objstm, pdf_obj *object) +static long +pdf_add_objstm (pdf_obj *objstm, pdf_obj *object) { - unsigned char pos = objstm->pos++; + long *data, pos; - if (!pos) { - pdf_obj *stream = pdf_new_stream(STREAM_COMPRESS); - pdf_label_obj(stream); - objstm->stream = stream; - } - objstm->obj_label[pos] = object->label; - objstm->obj_pos[pos] = pdf_stream_length(objstm->stream); - - add_xref_entry(object->label, 2, objstm->stream->label, pos); - - output_stream = objstm->stream; + TYPECHECK(objstm, PDF_STREAM); + + data = get_objstm_data(objstm); + pos = ++data[0]; + + data[2*pos] = object->label; + data[2*pos+1] = pdf_stream_length(objstm); + + add_xref_entry(object->label, 2, objstm->label, pos-1); + + /* redirect output into objstm */ + output_stream = objstm; enc_mode = 0; pdf_write_obj(object, pdf_output_file); pdf_out_char(pdf_output_file, '\n'); output_stream = NULL; - - if (pos == OBJSTM_MAX_OBJS-1) - pdf_flush_objstm(objstm); + + return pos; } static void -pdf_flush_objstm (pdf_objstm *objstm) +release_objstm (pdf_obj *objstm) { - unsigned char pos = objstm->pos; - pdf_obj *stream_obj = objstm->stream, *dict; + long *data = get_objstm_data(objstm); + long pos = data[0]; + pdf_obj *dict; pdf_stream *stream; - unsigned char i, *old_buf; + unsigned char *old_buf; unsigned long old_length; - TYPECHECK(stream_obj, PDF_STREAM); - stream = (pdf_stream *) stream_obj->data; + stream = (pdf_stream *) objstm->data; /* Precede stream data by offset table */ old_buf = stream->stream; @@ -1880,22 +1993,23 @@ pdf_flush_objstm (pdf_objstm *objstm) /* Reserve 22 bytes for each entry (two 10 digit numbers plus two spaces) */ stream->stream = NEW(old_length + 22*pos, unsigned char); stream->stream_length = 0; - - for (i = 0; i < pos; i++) { - long length = sprintf(format_buffer, "%lu %lu ", - objstm->obj_label[i], objstm->obj_pos[i]); - pdf_add_stream(stream_obj, format_buffer, length); + + { + long i = 2*pos, *val = data+2; + while (i--) { + long length = sprintf(format_buffer, "%ld ", *(val++)); + pdf_add_stream(objstm, format_buffer, length); + } } - dict = pdf_stream_dict(stream_obj); + dict = pdf_stream_dict(objstm); pdf_add_dict(dict, pdf_new_name("Type"), pdf_new_name("ObjStm")); pdf_add_dict(dict, pdf_new_name("N"), pdf_new_number(pos)); pdf_add_dict(dict, pdf_new_name("First"), pdf_new_number(stream->stream_length)); - pdf_add_stream(stream_obj, old_buf, old_length); + pdf_add_stream(objstm, old_buf, old_length); RELEASE(old_buf); - pdf_release_obj(stream_obj); - objstm->pos = 0; + pdf_release_obj(objstm); } void @@ -1916,12 +2030,23 @@ pdf_release_obj (pdf_obj *object) * Nonzero "label" means object needs to be written before it's destroyed. */ if (object->label && pdf_output_file != NULL) { - if (!current_objstm || object->flags & OBJ_NO_OBJSTM + if (!do_objstm || object->flags & OBJ_NO_OBJSTM || (doc_enc_mode && object->flags & OBJ_NO_ENCRYPT) || object->generation) pdf_flush_obj(object, pdf_output_file); - else - pdf_add_objstm(current_objstm, object); + else { + if (!current_objstm) { + long *data = NEW(2*OBJSTM_MAX_OBJS+2, long); + data[0] = data[1] = 0; + current_objstm = pdf_new_stream(STREAM_COMPRESS); + set_objstm_data(current_objstm, data); + pdf_label_obj(current_objstm); + } + if (pdf_add_objstm(current_objstm, object) == OBJSTM_MAX_OBJS) { + release_objstm(current_objstm); + current_objstm = NULL; + } + } } switch (object->type) { case PDF_BOOLEAN: @@ -2045,10 +2170,9 @@ pdf_copy_object (pdf_obj *dst, pdf_obj *src) pdf_indirect *data; dst->data = data = NEW(1, pdf_indirect); + data->pf = ((pdf_indirect *) (src->data))->pf; data->label = ((pdf_indirect *) (src->data))->label; data->generation = ((pdf_indirect *) (src->data))->generation; - data->dirty = ((pdf_indirect *) (src->data))->dirty; - data->dirty_file = ((pdf_indirect *) (src->data))->dirty_file; } break; } @@ -2057,7 +2181,7 @@ pdf_copy_object (pdf_obj *dst, pdf_obj *src) } static int -backup_line (void) +backup_line (FILE *pdf_input_file) { int ch = -1; @@ -2079,21 +2203,16 @@ backup_line (void) return 1; } -static unsigned long pdf_file_size = 0; - static long -find_xref (void) +find_xref (FILE *pdf_input_file) { long xref_pos; int tries = 10; - seek_end(pdf_input_file); - - pdf_file_size = tell_position(pdf_input_file); do { long currentpos; - if (!backup_line()) { + if (!backup_line(pdf_input_file)) { tries = 0; break; } @@ -2130,7 +2249,7 @@ find_xref (void) * at the start of the trailer. */ static pdf_obj * -parse_trailer (void) +parse_trailer (pdf_file *pf) { pdf_obj *result; /* @@ -2138,7 +2257,7 @@ parse_trailer (void) * be made a bit more robust sometime. */ if (fread(work_buffer, sizeof(char), - WORK_BUFFER_SIZE, pdf_input_file) == 0 || + WORK_BUFFER_SIZE, pf->file) == 0 || strncmp(work_buffer, "trailer", strlen("trailer"))) { WARN("No trailer. Are you sure this is a PDF file?"); WARN("buffer:\n->%s<-\n", work_buffer); @@ -2146,31 +2265,12 @@ parse_trailer (void) } else { char *p = work_buffer + strlen("trailer"); skip_white(&p, work_buffer + WORK_BUFFER_SIZE); - result = parse_pdf_dict(&p, work_buffer + WORK_BUFFER_SIZE); + result = parse_pdf_dict(&p, work_buffer + WORK_BUFFER_SIZE, pf); } return result; } -struct object -{ - unsigned long file_position; - unsigned generation; - /* - * Object numbers in original file and new file must have different - * object numbers. - * new_ref provides a reference for the object in the new file object - * space. When it is first set, an object in the old file is copied - * to the new file with a new number. new_ref remains set until the - * file is closed so that future references can access the object via - * new_ref instead of copying the object again. - */ - pdf_obj *direct; - pdf_obj *indirect; - int used; -} *xref_table = NULL; -long num_input_objects; - /* * This routine tries to estimate an upper bound for character position * of the end of the object, so it knows how big the buffer must be. @@ -2180,110 +2280,52 @@ long num_input_objects; * :-( */ static long -next_object_offset (unsigned long obj_num) +next_object_offset (pdf_file *pf, unsigned long obj_num) { - long next = pdf_file_size; /* Worst case */ + long next = pf->file_size; /* Worst case */ long i, curr; - curr = xref_table[obj_num].file_position; - /* Check all other objects to find next one */ - for (i = 0; i < num_input_objects; i++) { - if (xref_table[i].used && - xref_table[i].file_position > curr && - xref_table[i].file_position < next) - next = xref_table[i].file_position; + curr = pf->xref_table[obj_num].field2; + /* Check all other type 1 objects to find next one */ + for (i = 0; i < pf->num_obj; i++) { + if (pf->xref_table[i].type == 1 && + pf->xref_table[i].field2 > curr && + pf->xref_table[i].field2 < next) + next = pf->xref_table[i].field2; } return next; } -#define checklabel(n,g) ((n) > 0 && (n) < num_input_objects && \ - xref_table[(n)].generation == (g)) -#define labelfreed(n,g) (!xref_table[(n)].used) - -/* - * The following routine returns a reference to an object existing - * only in the input file. It does this as follows. If the object - * has never been referenced before, it reads the object - * in and creates a reference to it. Then it writes - * the object out, keeping the existing reference. If the - * object has been read in (and written out) before, it simply - * returns the retained existing reference to that object - */ -static pdf_obj * -pdf_ref_file_obj (unsigned long obj_num, unsigned short obj_gen) -{ - pdf_obj *obj, *ref; - - if (!checklabel(obj_num, obj_gen)) { - WARN("Can't resolve object: %lu %u", - obj_num, obj_gen); - return NULL; - } - ref = xref_table[obj_num].indirect; - if (ref != NULL) - return pdf_link_obj(ref); - - obj = pdf_read_object(obj_num, obj_gen); - if (!obj) { - WARN("Could not read object: %lu %u", - obj_num, obj_gen); - return NULL; - } - - ref = pdf_ref_obj(obj); - xref_table[obj_num].indirect = ref; - xref_table[obj_num].direct = obj; - /* Make sure the caller doesn't free this object */ - - return pdf_link_obj(ref); -} - +#define checklabel(pf, n, g) ((n) > 0 && (n) < (pf)->num_obj && ( \ + ((pf)->xref_table[(n)].type == 1 && (pf)->xref_table[(n)].field3 == (g)) || \ + ((pf)->xref_table[(n)].type == 2 && !(g)))) pdf_obj * -pdf_new_ref (unsigned long obj_num, unsigned short obj_gen) +pdf_new_indirect (pdf_file *pf, unsigned long obj_num, unsigned short obj_gen) { pdf_obj *result; pdf_indirect *indirect; - if (!checklabel(obj_num, obj_gen)) { - WARN("Invalid object label: %lu %hu", obj_num, obj_gen); - return NULL; - } - result = pdf_new_obj(PDF_INDIRECT); indirect = NEW(1, pdf_indirect); - result->data = indirect; + indirect->pf = pf; indirect->label = obj_num; indirect->generation = obj_gen; - indirect->dirty = 1; - indirect->dirty_file = pdf_input_file; + + result = pdf_new_obj(PDF_INDIRECT); + result->data = indirect; return result; } - -/* Label without corresponding object definition should - * be replaced with "null". But we won't do that. - */ static pdf_obj * -pdf_read_object (unsigned long obj_num, unsigned short obj_gen) +pdf_read_object (unsigned long obj_num, unsigned short obj_gen, + pdf_file *pf, long offset, long limit) { - pdf_obj *result; - long offset, limit, length; + long length; char *buffer, *p, *endptr; + pdf_obj *result; - if (!checklabel(obj_num, obj_gen)) { - WARN("Trying to read nonexistent object: %lu %u", - obj_num, obj_gen); - return NULL; - } - if (labelfreed(obj_num, obj_gen)) { - WARN("Trying to read deleted object: %lu %u", - obj_num, obj_gen); - return NULL; - } - offset = xref_table[obj_num].file_position; - limit = next_object_offset(obj_num); length = limit - offset; if (length <= 0) @@ -2291,8 +2333,8 @@ pdf_read_object (unsigned long obj_num, unsigned short obj_gen) buffer = NEW(length + 1, char); - seek_absolute(pdf_input_file, offset); - fread(buffer, sizeof(char), length, pdf_input_file); + seek_absolute(pf->file, offset); + fread(buffer, sizeof(char), length, pf->file); p = buffer; endptr = p + length; @@ -2321,7 +2363,7 @@ pdf_read_object (unsigned long obj_num, unsigned short obj_gen) g = strtoul(sp, NULL, 10); RELEASE(sp); - if (n != obj_num || g != obj_gen) { + if (obj_num && (n != obj_num || g != obj_gen)) { RELEASE(buffer); return NULL; } @@ -2338,7 +2380,7 @@ pdf_read_object (unsigned long obj_num, unsigned short obj_gen) } p += strlen("obj"); - result = parse_pdf_object(&p, endptr); + result = parse_pdf_object(&p, endptr, pf); skip_white(&p, endptr); if (memcmp(p, "endobj", strlen("endobj"))) { @@ -2349,60 +2391,202 @@ pdf_read_object (unsigned long obj_num, unsigned short obj_gen) } RELEASE(buffer); - return result; + return result; } -/* pdf_deref_obj always returns a link instead of the original */ -pdf_obj * -pdf_deref_obj (pdf_obj *obj) +static pdf_obj * +read_objstm (pdf_file *pf, unsigned long num) { - pdf_obj *result; + unsigned long offset = pf->xref_table[num].field2; + unsigned short gen = pf->xref_table[num].field3; + long limit = next_object_offset(pf, num), n, first, *header = NULL; + char *data, data1, *p, *q; + int i; - if (obj == NULL) - return NULL; - else if (pdf_obj_typeof(obj) != PDF_INDIRECT) { - return pdf_link_obj(obj); + pdf_obj *objstm, *dict, *type, *n_obj, *first_obj; + + objstm = pdf_read_object(num, gen, pf, offset, limit); + + if (!PDF_OBJ_STREAMTYPE(objstm)) + goto error; + + { + pdf_obj *tmp = pdf_stream_uncompress(objstm); + if (!tmp) + goto error; + pdf_release_obj(objstm); + objstm = tmp; + } + + dict = pdf_stream_dict(objstm); + + type = pdf_lookup_dict(dict, "Type"); + if (!PDF_OBJ_NAMETYPE(type) || + strcmp(pdf_name_value(type), "ObjStm")) + goto error; + + n_obj = pdf_lookup_dict(dict, "N"); + if (!PDF_OBJ_NUMBERTYPE(n_obj)) + goto error; + n = (long) pdf_number_value(n_obj); + + first_obj = pdf_lookup_dict(dict, "First"); + if (!PDF_OBJ_NUMBERTYPE(first_obj)) + goto error; + first = (long) pdf_number_value(first_obj); + /* reject object streams without object data */ + if (first >= pdf_stream_length(objstm)) + goto error; + + header = NEW(2*(n+1), long); + set_objstm_data(objstm, header); + *(header++) = n; + *(header++) = first; + + data = (char *) pdf_stream_dataptr(objstm); + + /* hack to avoid parsing beyond offset table */ + data1 = data[first]; + data[first] = 0; + + p = data; + i = 2*n; + while (i--) { + *(header++) = strtoul(p, &q, 10); + if (q == p) + goto error; + p = q; + } + data[first] = data1; + + /* Any garbage after last entry? */ + skip_white(&p, data+first); + if (p != data+first) + goto error; + + return pf->xref_table[num].direct = objstm; + + error: + WARN("Cannot parse object stream."); + if (objstm) + pdf_release_obj(objstm); + return NULL; +} + +/* Label without corresponding object definition are replaced by the + * null object, as required by the PDF spec. This is important to parse + * several cross-reference sections. + */ +static pdf_obj * +pdf_get_object (pdf_file *pf, unsigned long obj_num, unsigned short obj_gen) +{ + pdf_obj *result; + + if (!checklabel(pf, obj_num, obj_gen)) { + WARN("Trying to read nonexistent or deleted object: %lu %u", + obj_num, obj_gen); + return pdf_new_null(); } -#define OBJ_NUM(o) (((pdf_indirect *)((o)->data))->label) -#define OBJ_GEN(o) (((pdf_indirect *)((o)->data))->generation) -#define ISDIRTY(o) (((pdf_indirect *)((o)->data))->dirty) -#define OBJ_SRC(o) (((pdf_indirect *)((o)->data))->dirty_file) - if (!ISDIRTY(obj)) - ERROR("Tried to deref a non-file object"); - result = pdf_read_object(OBJ_NUM(obj), OBJ_GEN(obj)); - while (result && - pdf_obj_typeof(result) == PDF_INDIRECT - ) { - pdf_obj *tmp; + if ((result = pf->xref_table[obj_num].direct)) { + return pdf_link_obj(result); + } - tmp = pdf_read_object(OBJ_NUM(result), OBJ_GEN(result)); - pdf_release_obj(result); - result = tmp; + if (pf->xref_table[obj_num].type == 1) { + /* type == 1 */ + unsigned long offset; + long limit; + offset = pf->xref_table[obj_num].field2; + limit = next_object_offset(pf, obj_num); + result = pdf_read_object(obj_num, obj_gen, pf, offset, limit); + } else { + /* type == 2 */ + unsigned long objstm_num = pf->xref_table[obj_num].field2; + unsigned short index = pf->xref_table[obj_num].field3; + pdf_obj *objstm; + long *data, n, first, length; + char *p, *q; + + if (objstm_num >= pf->num_obj || + pf->xref_table[objstm_num].type != 1 || + !((objstm = pf->xref_table[objstm_num].direct) || + (objstm = read_objstm(pf, objstm_num)))) + goto error; + + data = get_objstm_data(objstm); + n = *(data++); + first = *(data++); + + if (index >= n || data[2*index] != obj_num) + goto error; + + length = pdf_stream_length(objstm); + p = (char *) pdf_stream_dataptr(objstm) + first + data[2*index+1]; + q = p + (index == n-1 ? length : first+data[2*index+3]); + result = parse_pdf_object(&p, q, pf); + if (!result) + goto error; } - return result; + /* Make sure the caller doesn't free this object */ + pf->xref_table[obj_num].direct = pdf_link_obj(result); + + return result; + + error: + WARN("Could not read object from object stream."); + result = pdf_new_null(); +} + +#define OBJ_FILE(o) (((pdf_indirect *)((o)->data))->pf) +#define OBJ_NUM(o) (((pdf_indirect *)((o)->data))->label) +#define OBJ_GEN(o) (((pdf_indirect *)((o)->data))->generation) + +/* pdf_deref_obj always returns a link instead of the original */ +/* It never return the null object, but the NULL pointer instead */ +pdf_obj * +pdf_deref_obj (pdf_obj *obj) +{ + if (obj) + obj = pdf_link_obj(obj); + + while (PDF_OBJ_INDIRECTTYPE(obj)) { + pdf_file *pf = OBJ_FILE(obj); + unsigned long obj_num = OBJ_NUM(obj); + unsigned short obj_gen = OBJ_GEN(obj); + if (!pf) + ERROR("Tried to deref a non-file object"); + pdf_release_obj(obj); + obj = pdf_get_object(pf, obj_num, obj_gen); + } + + if (PDF_OBJ_NULLTYPE(obj)) { + pdf_release_obj(obj); + return NULL; + } else + return obj; } static void -extend_xref (long new_size) +extend_xref (pdf_file *pf, long new_size) { unsigned long i; - xref_table = RENEW(xref_table, new_size, struct object); - for (i = num_input_objects; i < new_size; i++) { - xref_table[i].direct = NULL; - xref_table[i].indirect = NULL; - xref_table[i].used = 0; - xref_table[i].generation = 0; - xref_table[i].file_position = 0L; + pf->xref_table = RENEW(pf->xref_table, new_size, xref_entry); + for (i = pf->num_obj; i < new_size; i++) { + pf->xref_table[i].direct = NULL; + pf->xref_table[i].indirect = NULL; + pf->xref_table[i].type = 0; + pf->xref_table[i].field3 = 0; + pf->xref_table[i].field2 = 0L; } - num_input_objects = new_size; + pf->num_obj = new_size; } static int -parse_xref (void) +parse_xref_table (pdf_file *pf, long xref_pos) { + FILE *pdf_input_file = pf->file; unsigned long first, size; unsigned long i, offset; unsigned int obj_gen; @@ -2410,13 +2594,15 @@ parse_xref (void) int r; /* - * This routine reads one xref segment. It must be called positioned - * at the beginning of an xref table. It may be called multiple times + * This routine reads one xref segment. It may be called multiple times * on the same file. xref tables sometimes come in pieces. */ + + seek_absolute(pf->file, xref_pos); + mfgets(work_buffer, WORK_BUFFER_SIZE, pdf_input_file); if (memcmp(work_buffer, "xref", strlen("xref"))) { - WARN("No xref. Are you sure this is a PDF file?"); + /* Might be an xref stream and not an xref table */ return 0; } /* Next line in file has first item and size of table */ @@ -2424,8 +2610,10 @@ parse_xref (void) unsigned long current_pos; current_pos = tell_position(pdf_input_file); - if (mfgets(work_buffer, WORK_BUFFER_SIZE, pdf_input_file) == NULL) - ERROR("parse_xref: premature end of PDF file while parsing xref"); + if (mfgets(work_buffer, WORK_BUFFER_SIZE, pdf_input_file) == NULL) { + WARN("Premature end of PDF file while parsing xref table."); + return -1; + } if (!strncmp(work_buffer, "trailer", strlen ("trailer"))) { /* * Backup... This is ugly, but it seems like the safest thing to @@ -2438,8 +2626,8 @@ parse_xref (void) break; } sscanf(work_buffer, "%lu %lu", &first, &size); - if (num_input_objects < first + size) { - extend_xref (first + size); + if (pf->num_obj < first + size) { + extend_xref(pf, first + size); } for (i = first; i < first + size; i++) { @@ -2450,155 +2638,339 @@ parse_xref (void) * through the reference table, so we only set "position" * if it hasn't been set yet. */ -#define POSITION_UNSET(t,n) ((t)[(n)].file_position == 0) -#define SET_XREF_ENTRY(t,n,o,g) \ -do { \ - (t)[(n)].file_position = (o); \ - (t)[(n)].generation = (g); \ -} while (0) -#define SET_XREF_FLAG(t,n,f) ((t)[(n)].used = ((f) == 'n') ? 1 : 0) work_buffer[19] = 0; offset = 0UL; obj_gen = 0; flag = 0; r = sscanf(work_buffer, "%010lu %05u %c", &offset, &obj_gen, &flag); if ( r != 3 || ((flag != 'n' && flag != 'f') || (flag == 'n' && - (offset >= pdf_file_size || (offset > 0 && offset < 4))))) { + (offset >= pf->file_size || (offset > 0 && offset < 4))))) { WARN("Invalid xref table entry [%lu]. PDF file is corrupt...", i); - return 0; + return -1; + } + if (!pf->xref_table[i].field2) { + pf->xref_table[i].type = (flag == 'n'); + pf->xref_table[i].field2 = offset; + pf->xref_table[i].field3 = obj_gen; } - if (POSITION_UNSET(xref_table, i)) - SET_XREF_ENTRY(xref_table, i, offset, obj_gen); - SET_XREF_FLAG(xref_table, i, flag); } } return 1; } -static pdf_obj * -read_xref (void) +static unsigned long +parse_xrefstm_field (const char **p, int length, unsigned long def) { - pdf_obj *main_trailer; - long xref_pos; + unsigned long val = 0; - xref_pos = find_xref(); - if (xref_pos == 0) { - WARN("Can't find xref table."); - return NULL; - } + if (!length) + return def; - /* Read primary xref table */ - seek_absolute(pdf_input_file, xref_pos); - if (!parse_xref()) { - WARN("Couldn't read xref table. Is this a correct PDF file?"); - return NULL; + while (length--) { + val <<= 8; + val |= (unsigned char) *((*p)++); } - main_trailer = parse_trailer(); - if (main_trailer == NULL) { - WARN("Couldn't read xref trailer. Is this a correct PDF file?"); - return NULL; + + return val; +} + +static int +parse_xrefstm_subsec (pdf_file *pf, + const char **p, long *length, + int *W, int wsum, + long first, long size) { + xref_entry *e; + + if ((*length -= wsum*size) < 0) + return -1; + + if (pf->num_obj < first+size) + extend_xref(pf, first+size); /* TODO: change! why? */ + + e = pf->xref_table + first; + while (size--) { + unsigned char type; + unsigned long field2; + unsigned short field3; + + type = (unsigned char) parse_xrefstm_field(p, W[0], 1); + if (type > 2) + WARN("Unknown cross-reference stream entry type."); + else if (!W[1] || (type != 1 && !W[2])) + return -1; + + field2 = (unsigned long) parse_xrefstm_field(p, W[1], 0); + field3 = (unsigned short) parse_xrefstm_field(p, W[2], 0); + + if (!e->field2) { + e->type = type; + e->field2 = field2; + e->field3 = field3; + } + e++; } - { - pdf_obj *xref_size; + return 0; +} - xref_size = pdf_lookup_dict(main_trailer, "Size"); - if (xref_size == NULL || - pdf_lookup_dict(main_trailer, "Root") == NULL) { - WARN("Trailer doesn't have catalog or a size. Is this a correct PDF file?"); - return NULL; - } - if (num_input_objects < pdf_number_value(xref_size)) { - extend_xref((long)pdf_number_value(xref_size)); +static int +parse_xref_stream (pdf_file *pf, long xref_pos, pdf_obj **trailer) +{ + pdf_obj *xrefstm, *size_obj, *W_obj, *index; + unsigned long size; + long length; + int W[3], i, wsum = 0; + const char *p; + + xrefstm = pdf_read_object(0, 0, pf, xref_pos, pf->file_size); + if (!PDF_OBJ_STREAMTYPE(xrefstm)) + goto error; + + { + pdf_obj *tmp = pdf_stream_uncompress(xrefstm); + if (!tmp) + goto error; + pdf_release_obj(xrefstm); + xrefstm = tmp; + } + + *trailer = pdf_link_obj(pdf_stream_dict(xrefstm)); + + size_obj = pdf_lookup_dict(*trailer, "Size"); + if (!PDF_OBJ_NUMBERTYPE(size_obj)) + goto error; + size = (unsigned long) pdf_number_value(size_obj); + + length = pdf_stream_length(xrefstm); + + W_obj = pdf_lookup_dict(*trailer, "W"); + if (!PDF_OBJ_ARRAYTYPE(W_obj) || pdf_array_length(W_obj) != 3) + goto error; + + for (i = 0; i < 3; i++) { + pdf_obj *tmp = pdf_get_array(W_obj, i); + if (!PDF_OBJ_NUMBERTYPE(tmp)) + goto error; + wsum += (W[i] = (int) pdf_number_value(tmp)); + } + + p = pdf_stream_dataptr(xrefstm); + + index = pdf_lookup_dict(*trailer, "Index"); + if (index) { + unsigned int index_len; + if (!PDF_OBJ_ARRAYTYPE(index) || + ((index_len = pdf_array_length(index)) % 2 )) + goto error; + + i = 0; + while (i < index_len) { + pdf_obj *first = pdf_get_array(index, i++); + pdf_obj *size = pdf_get_array(index, i++); + if (!PDF_OBJ_NUMBERTYPE(first) || + !PDF_OBJ_NUMBERTYPE(size) || + parse_xrefstm_subsec(pf, &p, &length, W, wsum, + (long) pdf_number_value(first), + (long) pdf_number_value(size))) + goto error; } + } else if (parse_xrefstm_subsec(pf, &p, &length, W, wsum, 0, size)) + goto error; + + if (length) + WARN("Garbage in xref stream."); + + pdf_release_obj(xrefstm); + + return 1; + + error: + WARN("Cannot parse cross-reference stream."); + if (xrefstm) + pdf_release_obj(xrefstm); + if (*trailer) { + pdf_release_obj(*trailer); + *trailer = NULL; } + return 0; +} - /* Read any additional xref tables */ - { - pdf_obj *prev_trailer, *prev_xref; - - prev_trailer = pdf_link_obj(main_trailer); - while ((prev_xref = pdf_lookup_dict(prev_trailer, "Prev")) != NULL) { - xref_pos = (long) pdf_number_value(prev_xref); - seek_absolute(pdf_input_file, xref_pos); - pdf_release_obj(prev_trailer); - if (!parse_xref()) { - WARN("Couldn't read xref table. Is this a correct PDF file?"); - return NULL; - } - prev_trailer = parse_trailer(); - if (prev_trailer == NULL) { - WARN("Couldn't read xref trailer. Is this a correct PDF file?"); - return NULL; +/* TODO: parse Version entry */ +static pdf_obj * +read_xref (pdf_file *pf) +{ + pdf_obj *trailer = NULL, *main_trailer = NULL; + long xref_pos; + + if (!(xref_pos = find_xref(pf->file))) + goto error; + + while (xref_pos) { + pdf_obj *prev; + + int res = parse_xref_table(pf, xref_pos); + if (res > 0) { + /* cross-reference table */ + pdf_obj *xrefstm; + + if (!(trailer = parse_trailer(pf))) + goto error; + + if (!main_trailer) + main_trailer = pdf_link_obj(trailer); + + if ((xrefstm = pdf_lookup_dict(trailer, "XRefStm"))) { + pdf_obj *new_trailer = NULL; + if (PDF_OBJ_NUMBERTYPE(xrefstm) && + parse_xref_stream(pf, (long) pdf_number_value(xrefstm), + &new_trailer)) + pdf_release_obj(new_trailer); + else + WARN("Skipping hybrid reference section."); + /* Many PDF 1.5 xref streams use DecodeParms, which we cannot + parse. This way we can use at least xref tables in hybrid + documents. Or should we better stop parsing the file? + */ } - } - pdf_release_obj(prev_trailer); + + } else if (!res && parse_xref_stream(pf, xref_pos, &trailer)) { + /* cross-reference stream */ + if (!main_trailer) + main_trailer = pdf_link_obj(trailer); + } else + goto error; + + if ((prev = pdf_lookup_dict(trailer, "Prev"))) { + if (PDF_OBJ_NUMBERTYPE(prev)) + xref_pos = (long) pdf_number_value(prev); + else + goto error; + } else + xref_pos = 0; + + pdf_release_obj(trailer); } +#if 0 + if (!pdf_lookup_dict(main_trailer, "Root")) { + WARN("Trailer doesn't have catalog. Is this a correct PDF file?"); + goto error; + } +#endif + return main_trailer; + + error: + WARN("Error while parsing PDF file."); + if (trailer) + pdf_release_obj(trailer); + if (main_trailer) + pdf_release_obj(main_trailer); + return NULL; } -static char any_open = 0; +static struct ht_table *pdf_files = NULL; -pdf_obj * -pdf_open (FILE *file) +pdf_file * +pdf_file_new (FILE *file) { - pdf_obj *trailer; + pdf_file *pf; + ASSERT(file); + pf = NEW(1, pdf_file); + pf->file = file; + pf->trailer = NULL; + pf->xref_table = NULL; + pf->num_obj = 0; + pf->version = 0; - if (any_open) { - WARN("Only one PDF file may be open at one time."); - any_open = 1; - exit(1); - } - pdf_input_file = file; - if (!check_for_pdf(pdf_input_file)) { - WARN("pdf_open: Not a PDF 1.[1-5] file."); - return NULL; + seek_end(file); + pf->file_size = tell_position(file); + + return pf; +} + +static void +pdf_file_free (pdf_file *pf) +{ + unsigned long i; + + if (!pf) { + return; } - if ((trailer = read_xref()) == NULL) { - WARN("No trailer."); - pdf_close(); - return NULL; + + for (i = 0; i < pf->num_obj; i++) { + if (pf->xref_table[i].direct) + pdf_release_obj(pf->xref_table[i].direct); + if (pf->xref_table[i].indirect) + pdf_release_obj(pf->xref_table[i].indirect); } - return trailer; + RELEASE(pf->xref_table); + pdf_release_obj(pf->trailer); + + RELEASE(pf); } void -pdf_close (void) +pdf_files_init (void) { - unsigned long i; - int done; + pdf_files = NEW(1, struct ht_table); + ht_init_table(pdf_files); +} - /* - * Following loop must be iterated because each write could trigger - * an additional indirect reference of an object with a lower number! - */ - do { - done = 1; - for (i = 0; i < num_input_objects; i++) { - if (xref_table[i].direct != NULL) { - pdf_release_obj(xref_table[i].direct); - xref_table[i].direct = NULL; - done = 0; - } +pdf_obj * +pdf_file_get_trailer (pdf_file *pf) +{ + ASSERT(pf); + return pdf_link_obj(pf->trailer); +} + +pdf_file * +pdf_open (char *ident, FILE *file) +{ + pdf_file *pf; + + ASSERT(pdf_files); + + pf = (pdf_file *) ht_lookup_table(pdf_files, ident, strlen(ident)); + + if (pf) { + pf->file = file; + } else { + int version = check_for_pdf(file); + if (!version) { + WARN("pdf_open: Not a PDF 1.[1-5] file."); + return NULL; } - } while (!done); - /* - * Now take care of the indirect objects. They had to be left around - * until all the direct objects were flushed. - */ - for (i = 0; i < num_input_objects; i++) { - if (xref_table[i].indirect != NULL) { - pdf_release_obj(xref_table[i].indirect); + + pf = pdf_file_new(file); + pf->version = version; + + if (!(pf->trailer = read_xref(pf))) { + pdf_file_free(pf); + return NULL; } + + ht_append_table(pdf_files, ident, strlen(ident), pf); } - RELEASE(xref_table); - xref_table = NULL; - num_input_objects = 0; - any_open = 0; - pdf_input_file = NULL; + + return pf; +} + +void +pdf_close (pdf_file *pf) +{ + if (pf) + pf->file = NULL; +} + +void +pdf_files_close (void) +{ + ASSERT(pdf_files); + ht_clear_table(pdf_files, (void (*)(void *)) pdf_file_free); + RELEASE(pdf_files); } int @@ -2638,6 +3010,51 @@ import_dict (pdf_obj *key, pdf_obj *value, void *pdata) return 0; } +static pdf_obj * +pdf_import_indirect (pdf_obj *object) +{ + pdf_file *pf = OBJ_FILE(object); + unsigned long obj_num = OBJ_NUM(object); + unsigned short obj_gen = OBJ_GEN(object); + + pdf_obj *ref; + + ASSERT(pf); + + if (!checklabel(pf, obj_num, obj_gen)) { + WARN("Can't resolve object: %lu %u", obj_num, obj_gen); + return pdf_new_null(); + } + + if ((ref = pf->xref_table[obj_num].indirect)) { + return pdf_link_obj(ref); + } else { + pdf_obj *obj, *tmp; + + obj = pdf_get_object(pf, obj_num, obj_gen); + if (!obj) { + WARN("Could not read object: %lu %u", obj_num, obj_gen); + return NULL; + } + + tmp = pdf_import_object(obj); + + pf->xref_table[obj_num].indirect = ref = pdf_ref_obj(tmp); + + pdf_release_obj(tmp); + pdf_release_obj(obj); + + return pdf_link_obj(ref); + } +} + +/* + * pdf_import_object recursively copies the object and those + * referenced by it and changes the indirect references so that + * they refer to the current output file. New indirect references + * are remembered, which avoids duplicating objects when they + * are imported several times. + */ pdf_obj * pdf_import_object (pdf_obj *object) { @@ -2648,12 +3065,8 @@ pdf_import_object (pdf_obj *object) switch (pdf_obj_typeof(object)) { case PDF_INDIRECT: - if (ISDIRTY(object)) { - if (OBJ_SRC(object) != pdf_input_file) { - WARN("Different file is opened before object is imported?"); - return NULL; - } - imported = pdf_ref_file_obj(OBJ_NUM(object), OBJ_GEN(object)); + if (OBJ_FILE(object)) { + imported = pdf_import_indirect(object); } else { imported = pdf_link_obj(object); } @@ -2721,10 +3134,8 @@ pdf_compare_reference (pdf_obj *ref1, pdf_obj *ref2) data1 = (pdf_indirect *) ref1->data; data2 = (pdf_indirect *) ref2->data; - if (data1->dirty != data2->dirty) - return (int) (data1->dirty - data2->dirty); - if (data1->dirty_file != data2->dirty_file) - return (int) (data1->dirty_file - data2->dirty_file); + if (data1->pf != data2->pf) + return (int) (data1->pf - data2->pf); if (data1->label != data2->label) return (int) (data1->label - data2->label); if (data1->generation != data2->generation) |