summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvipdfmx/src/pdfobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvipdfmx/src/pdfobj.c')
-rw-r--r--Build/source/texk/dvipdfmx/src/pdfobj.c1217
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)