diff options
Diffstat (limited to 'Build/source/texk/xdvipdfmx/src/pdfobj.c')
-rw-r--r-- | Build/source/texk/xdvipdfmx/src/pdfobj.c | 537 |
1 files changed, 370 insertions, 167 deletions
diff --git a/Build/source/texk/xdvipdfmx/src/pdfobj.c b/Build/source/texk/xdvipdfmx/src/pdfobj.c index 386eaaff850..1ad7de2a928 100644 --- a/Build/source/texk/xdvipdfmx/src/pdfobj.c +++ b/Build/source/texk/xdvipdfmx/src/pdfobj.c @@ -1,8 +1,8 @@ -/* $Header: /home/cvsroot/dvipdfmx/src/pdfobj.c,v 1.40 2006/12/06 13:14:54 chofchof Exp $ +/* $Header: /home/cvsroot/dvipdfmx/src/pdfobj.c,v 1.49 2008/02/08 19:02:38 matthias Exp $ This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. - Copyright (C) 2002 by Jin-Hwan Cho and Shunsaku Hirata, + Copyright (C) 2007 by Jin-Hwan Cho and Shunsaku Hirata, the dvipdfmx project team <dvipdfmx@project.ktug.or.kr> Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu> @@ -42,11 +42,19 @@ #endif /* HAVE_ZLIB */ #include "pdfobj.h" +#include "pdfdev.h" #define STREAM_ALLOC_SIZE 4096u #define ARRAY_ALLOC_SIZE 256 #define IND_OBJECTS_ALLOC_SIZE 512 +#define OBJ_NO_OBJSTM (1 << 0) +/* Objects with this flag will not be put into an object stream. + For instance, all stream objects have this flag set. */ +#define OBJ_NO_ENCRYPT (1 << 1) +/* Objects with this flag will not be encrypted. + This implies OBJ_NO_OBJSTM if encryption is turned on. */ + /* Any of these types can be represented as follows */ struct pdf_obj { @@ -56,6 +64,7 @@ struct pdf_obj all other "label" to zero */ unsigned short generation; /* Only used if "label" is used */ unsigned refcount; /* Number of links to this object */ + int flags; void *data; }; @@ -105,8 +114,8 @@ struct pdf_stream struct pdf_indirect { - unsigned label; - unsigned generation; + 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 @@ -136,19 +145,35 @@ static char format_buffer[FORMAT_BUF_SIZE]; static struct xref_entry { - long file_position; - pdf_obj *object; - int used; /* Used by PDF import */ -} *output_xref = NULL; + unsigned char type; + unsigned long field2; + unsigned short field3; + int used; /* Used by PDF import */ +} *output_xref; -static unsigned long pdf_max_ind_objects = 0; -static unsigned long next_label = 1; +static unsigned long pdf_max_ind_objects; +static unsigned long next_label; static unsigned long startxref; -static unsigned pdf_root_obj = 0; -static unsigned pdf_info_obj = 0; -static unsigned pdf_encrypt_obj = 0; +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; + +static pdf_obj *trailer_dict; +static pdf_obj *xref_stream; /* Internal static routines */ @@ -156,6 +181,8 @@ 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 pdf_out_char (FILE *file, char c); static void pdf_out (FILE *file, const void *buffer, long length); @@ -186,8 +213,8 @@ 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 obj_gen); -static pdf_obj *pdf_read_object (unsigned long obj_num, unsigned obj_gen); +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; @@ -212,7 +239,7 @@ pdf_set_compression (int level) return; } -static unsigned pdf_version = 3; +static unsigned pdf_version = 4; void pdf_set_version (unsigned version) @@ -235,13 +262,69 @@ 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 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[label].type = type; + output_xref[label].field2 = field2; + output_xref[label].field3 = field3; +} + #define BINARY_MARKER "%\344\360\355\370\n" void -pdf_out_init (const char *filename) +pdf_out_init (const char *filename, int do_encryption) { char v; - pdf_output_file = MFOPEN(filename, FOPEN_WBIN_MODE); + output_xref = NULL; + pdf_max_ind_objects = 0; + add_xref_entry(0, 0, 0, 0xffff); + next_label = 1; + + if (pdf_version >= 5) { + xref_stream = pdf_new_stream(STREAM_COMPRESS); + 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 + trailer_dict = pdf_new_dict(); + + output_stream = NULL; + + if (filename == NULL) { /* no filename: writing to stdout */ +#ifdef WIN32 + setmode(fileno(stdout), _O_BINARY); +#endif + pdf_output_file = stdout; + } + else + pdf_output_file = MFOPEN(filename, FOPEN_WBIN_MODE); + if (!pdf_output_file) { if (strlen(filename) < 128) ERROR("Unable to open \"%s\".", filename); @@ -253,94 +336,134 @@ pdf_out_init (const char *filename) pdf_out(pdf_output_file, &v, 1); pdf_out(pdf_output_file, "\n", 1); pdf_out(pdf_output_file, BINARY_MARKER, strlen(BINARY_MARKER)); + + enc_mode = 0; + doc_enc_mode = do_encryption; } static void -dump_xref (void) +dump_xref_table (void) { long length; unsigned long i; - /* Record where this xref is for trailer */ - startxref = pdf_output_file_position; - pdf_out(pdf_output_file, "xref\n", 5); length = sprintf(format_buffer, "%d %lu\n", 0, next_label); pdf_out(pdf_output_file, format_buffer, length); - length = sprintf(format_buffer, "%010ld %05ld f \n", 0L, 65535L); /* * Every space counts. The space after the 'f' and 'n' is * *essential*. * The PDF spec says the lines must be 20 characters long including the * end of line character. */ - pdf_out(pdf_output_file, format_buffer, length); - for (i = 1; i < next_label; i++){ - length = sprintf(format_buffer, "%010ld %05ld n \n", - output_xref[i-1].file_position, 0L); + for (i = 0; i < next_label; i++) { + unsigned char type = output_xref[i].type; + if (type > 1) + ERROR("object type %hu not allowed in xref table", type); + length = sprintf(format_buffer, "%010lu %05hu %c \n", + output_xref[i].field2, output_xref[i].field3, + type ? 'n' : 'f'); pdf_out(pdf_output_file, format_buffer, length); } - /* Done with xref table */ - RELEASE(output_xref); - output_xref = NULL; } static void -dump_trailer (void) +dump_trailer_dict (void) { - long length; - unsigned long starttrailer; - - starttrailer = pdf_output_file_position; - pdf_out(pdf_output_file, "trailer\n", 8); - pdf_out(pdf_output_file, "<<\n", 3); - - length = sprintf(format_buffer, "/Size %lu\n", next_label); - pdf_out(pdf_output_file, format_buffer, length); - - if (pdf_root_obj == 0) - ERROR ("dump_trailer: Invalid root object"); - length = sprintf(format_buffer, "/Root %u %u R\n", pdf_root_obj, 0); - pdf_out(pdf_output_file, format_buffer, length); + enc_mode = 0; + write_dict(trailer_dict->data, pdf_output_file); + pdf_release_obj(trailer_dict); + pdf_out_char(pdf_output_file, '\n'); +} - if (pdf_encrypt_obj != 0) { - length = sprintf(format_buffer, "/Encrypt %u %u R\n", pdf_encrypt_obj, 0); - pdf_out(pdf_output_file, format_buffer, length); - } - if (pdf_info_obj != 0) { - length = sprintf(format_buffer, "/Info %u %u R\n", pdf_info_obj, 0); - pdf_out(pdf_output_file, format_buffer, length); - } - if (pdf_encrypt_obj != 0) { - unsigned char *id; - id = pdf_enc_id_string(); - length = sprintf(format_buffer, "/ID [<%s> <%s>]\n", id, id); - pdf_out(pdf_output_file, format_buffer, length); +/* + * output a PDF 1.5 cross-reference stream; + * contributed by Matthias Franz (March 21, 2007) + */ +static void +dump_xref_stream (void) +{ + unsigned long pos, i; + unsigned poslen; + unsigned char buf[7] = {0, 0, 0, 0, 0}; + + pdf_obj *w; + + pdf_label_obj(xref_stream); + + /* determine the necessary size of the offset field */ + pos = startxref; /* maximal offset value */ + poslen = 1; + while (pos >>= 8) + poslen++; + + w = pdf_new_array(); + pdf_add_array(w, pdf_new_number(1)); /* type */ + pdf_add_array(w, pdf_new_number(poslen)); /* offset (big-endian) */ + pdf_add_array(w, pdf_new_number(2)); /* generation */ + pdf_add_dict(trailer_dict, pdf_new_name("W"), w); + + /* We need the xref entry for the xref stream right now */ + add_xref_entry(next_label-1, 1, startxref, 0); + + for (i = 0; i < next_label; i++) { + unsigned j; + buf[0] = output_xref[i].type; + pos = output_xref[i].field2; + for (j = poslen; j--; ) { + buf[1+j] = (unsigned char) pos; + pos >>= 8; + } + unsigned short f3 = output_xref[i].field3; + buf[poslen+1] = (unsigned char) (f3 >> 8); + buf[poslen+2] = (unsigned char) (f3); + pdf_add_stream(xref_stream, &buf, poslen+3); } - pdf_out(pdf_output_file, ">>\n", 3); - pdf_out(pdf_output_file, "startxref\n", 10); - - length = sprintf(format_buffer, "%lu\n", startxref); - pdf_out(pdf_output_file, format_buffer, length); - pdf_out(pdf_output_file, "%%EOF\n", 6); + pdf_release_obj(xref_stream); } void pdf_out_flush (void) { if (pdf_output_file) { - dump_xref(); - dump_trailer(); + long length; + + /* Flush current object stream */ + if (current_objstm && current_objstm->pos) + pdf_flush_objstm(current_objstm); + + /* Record where this xref is for trailer */ + startxref = pdf_output_file_position; + + pdf_add_dict(trailer_dict, pdf_new_name("Size"), + pdf_new_number(next_label)); + + if (pdf_version >= 5) + dump_xref_stream(); + else { + dump_xref_table(); + dump_trailer_dict(); + } + + /* Done with xref table */ + RELEASE(output_xref); + + pdf_out(pdf_output_file, "startxref\n", 10); + length = sprintf(format_buffer, "%lu\n", startxref); + pdf_out(pdf_output_file, format_buffer, length); + pdf_out(pdf_output_file, "%%EOF\n", 6); + MESG("\n"); if (verbose) { if (compression_level > 0) { - MESG("Compression eliminated approximately %lu bytes\n", compression_saved); + MESG("Compression saved %ld bytes%s\n", compression_saved, + pdf_version < 5 ? ". Try \"-V 5\" for better compression" : ""); } } - MESG("%lu bytes written", pdf_output_file_position); + MESG("%ld bytes written", pdf_output_file_position); MFCLOSE(pdf_output_file); } @@ -361,50 +484,52 @@ pdf_error_cleanup (void) void pdf_set_root (pdf_obj *object) { - if (pdf_root_obj != 0) { + if (pdf_add_dict(trailer_dict, pdf_new_name("Root"), pdf_ref_obj(object))) { ERROR("Root object already set!"); } - if (object->label == 0) { /* Make sure this object has a label */ - pdf_label_obj(object); - } - pdf_root_obj = object->label; + /* Adobe Readers don't like a document catalog inside an encrypted + * object stream, although the PDF v1.5 spec seems to allow this. + * Note that we don't set OBJ_NO_ENCRYPT since the name dictionary in + * a document catalog may contain strings, which should be encrypted. + */ + if (doc_enc_mode) + object->flags |= OBJ_NO_OBJSTM; } void pdf_set_info (pdf_obj *object) { - if (pdf_info_obj != 0) { + if (pdf_add_dict(trailer_dict, pdf_new_name("Info"), pdf_ref_obj(object))) { ERROR ("Info object already set!"); } - if (object->label == 0) { /* Make sure this object has a label */ - pdf_label_obj(object); - } - pdf_info_obj = object->label; } void -pdf_set_encrypt (pdf_obj *object) +pdf_set_encrypt (pdf_obj *encrypt, pdf_obj *id) { - if (pdf_encrypt_obj != 0) { + if (pdf_add_dict(trailer_dict, pdf_new_name("Encrypt"), pdf_ref_obj(encrypt))) { ERROR("Encrypt object already set!"); } - if (object->label == 0) { /* Make sure this object has a label */ - pdf_label_obj(object); - } - pdf_encrypt_obj = object->label; + encrypt->flags |= OBJ_NO_ENCRYPT; + + pdf_add_dict(trailer_dict, pdf_new_name("ID"), id); } static void pdf_out_char (FILE *file, char c) { - fputc(c, file); - /* Keep tallys for xref table *only* if writing a pdf file. */ - if (file == pdf_output_file) { - pdf_output_file_position += 1; - if (c == '\n') - pdf_output_line_position = 0; - else - pdf_output_line_position += 1; + if (output_stream && file == pdf_output_file) + pdf_add_stream(output_stream, &c, 1); + else { + fputc(c, file); + /* Keep tallys for xref table *only* if writing a pdf file. */ + if (file == pdf_output_file) { + pdf_output_file_position += 1; + if (c == '\n') + pdf_output_line_position = 0; + else + pdf_output_line_position += 1; + } } } @@ -419,18 +544,32 @@ void pdf_out_char (FILE *file, char c) static void pdf_out (FILE *file, const void *buffer, long length) { - fwrite(buffer, 1, length, file); - /* Keep tallys for xref table *only* if writing a pdf file */ - if (file == pdf_output_file) { - pdf_output_file_position += length; - pdf_output_line_position += length; - /* "foo\nbar\n "... */ - if (length > 0 && + if (output_stream && file == pdf_output_file) + pdf_add_stream(output_stream, buffer, length); + else { + fwrite(buffer, 1, length, file); + /* Keep tallys for xref table *only* if writing a pdf file */ + if (file == pdf_output_file) { + pdf_output_file_position += length; + pdf_output_line_position += length; + /* "foo\nbar\n "... */ + if (length > 0 && ((char *)buffer)[length-1] == '\n') - pdf_output_line_position = 0; + pdf_output_line_position = 0; + } } } +/* 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) { @@ -461,6 +600,7 @@ pdf_new_obj(int type) result->label = 0; result->generation = 0; result->refcount = 1; + result->flags = 0; return result; } @@ -480,20 +620,12 @@ pdf_label_obj (pdf_obj *object) if (INVALIDOBJ(object)) ERROR("pdf_label_obj(): passed invalid object."); - if (next_label > pdf_max_ind_objects) { - pdf_max_ind_objects += IND_OBJECTS_ALLOC_SIZE; - output_xref = RENEW(output_xref, pdf_max_ind_objects, struct xref_entry); - } /* * Don't change label on an already labeled object. Ignore such calls. */ if (object->label == 0) { - /* Save so we can lookup this object by its number */ - output_xref[next_label-1].object = object; - output_xref[next_label-1].file_position = 0L; - object->label = next_label; + object->label = next_label++; object->generation = 0; - next_label++; } } @@ -563,13 +695,13 @@ write_indirect (pdf_indirect *indirect, FILE *file) if (indirect->dirty) { if (file == stderr) { pdf_out(file, "{d}", 3); - length = sprintf(format_buffer, "%d %d R", indirect->label, indirect->generation); + length = sprintf(format_buffer, "%lu %hu R", indirect->label, indirect->generation); pdf_out(stderr, format_buffer, length); } else { pdf_obj *clean; if (indirect->dirty_file != pdf_input_file) { - ERROR("Input PDF file doesn't match object: label=%d, from_file=%p, current_file=%p", + 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); @@ -577,7 +709,7 @@ write_indirect (pdf_indirect *indirect, FILE *file) pdf_release_obj(clean); } } else { - length = sprintf(format_buffer, "%d %d R", indirect->label, indirect->generation); + length = sprintf(format_buffer, "%lu %hu R", indirect->label, indirect->generation); pdf_out(file, format_buffer, length); } } @@ -821,7 +953,10 @@ write_string (pdf_string *str, FILE *file) int nescc = 0, i, count; s = str->string; - pdf_encrypt_data(s, str->length); + + if (enc_mode) + pdf_encrypt_data(s, str->length); + /* * Count all ASCII non-printable characters. */ @@ -1022,13 +1157,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, ']'); @@ -1201,10 +1340,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); @@ -1248,10 +1384,11 @@ release_dict (pdf_dict *data) } /* Array is ended by a node with NULL this pointer */ -void +/* pdf_add_dict returns 0 if the key is new and non-zero otherwise */ +int pdf_add_dict (pdf_obj *dict, pdf_obj *key, pdf_obj *value) { - pdf_dict *data; + pdf_dict *data, *new_node; TYPECHECK(dict, PDF_DICT); TYPECHECK(key, PDF_NAME); @@ -1260,34 +1397,29 @@ pdf_add_dict (pdf_obj *dict, pdf_obj *key, pdf_obj *value) if (value != NULL && INVALIDOBJ(value)) ERROR("pdf_add_dict(): Passed invalid value"); - data = dict->data; /* If this key already exists, simply replace the value */ - while (data->key != NULL) { + for (data = dict->data; data->key != NULL; data = data->next) { if (!strcmp(pdf_name_value(key), pdf_name_value(data->key))) { /* Release the old value */ pdf_release_obj(data->value); /* Release the new key (we don't need it) */ pdf_release_obj(key); data->value = value; - break; + return 1; } - data = data->next; } /* - * If we didn't find the key, build a new "end" node and add + * We didn't find the key. We build a new "end" node and add * the new key just before the end */ - if (data->key == NULL) { - pdf_dict *new_node; - - new_node = NEW (1, pdf_dict); - new_node->key = NULL; - new_node->value = NULL; - new_node->next = NULL; - data->next = new_node; - data->key = key; - data->value = value; - } + new_node = NEW (1, pdf_dict); + new_node->key = NULL; + new_node->value = NULL; + new_node->next = NULL; + data->next = new_node; + data->key = key; + data->value = value; + return 0; } void @@ -1456,6 +1588,7 @@ pdf_new_stream (int flags) data->max_length = 0; result->data = data; + result->flags |= OBJ_NO_OBJSTM; return result; } @@ -1490,19 +1623,25 @@ write_stream (pdf_stream *stream, FILE *file) (stream->_flags & STREAM_COMPRESS) && compression_level > 0) { + pdf_obj *filters = pdf_lookup_dict(stream->dict, "Filter"); + buffer_length = filtered_length + filtered_length/1000 + 14; buffer = NEW(buffer_length, unsigned char); { - pdf_obj *filters; + pdf_obj *filter_name = pdf_new_name("FlateDecode"); - filters = pdf_lookup_dict(stream->dict, "Filter"); - if (!filters) - filters = pdf_new_array(); - /* - * FlateDecode is the first filter to be applied to the stream. - */ - pdf_unshift_array(filters, pdf_new_name("FlateDecode")); - pdf_add_dict(stream->dict, pdf_new_name("Filter"), filters); + if (filters) + /* + * FlateDecode is the first filter to be applied to the stream. + */ + pdf_unshift_array(filters, filter_name); + else + /* + * Adding the filter as a name instead of a one-element array + * is crucial because otherwise Adobe Reader cannot read the + * cross-reference stream any more, cf. the PDF v1.5 Errata. + */ + pdf_add_dict(stream->dict, pdf_new_name("Filter"), filter_name); } #ifdef HAVE_ZLIB_COMPRESS2 if (compress2(buffer, &buffer_length, filtered, @@ -1516,7 +1655,8 @@ write_stream (pdf_stream *stream, FILE *file) } #endif /* HAVE_ZLIB_COMPRESS2 */ RELEASE(filtered); - compression_saved += filtered_length - buffer_length - strlen("/Filter [/FlateDecode]\n"); + compression_saved += filtered_length - buffer_length + - (filters ? strlen("/FlateDecode "): strlen("/Filter/FlateDecode\n")); filtered = buffer; filtered_length = buffer_length; @@ -1541,7 +1681,10 @@ write_stream (pdf_stream *stream, FILE *file) pdf_write_obj(stream->dict, file); pdf_out(file, "\nstream\n", 8); - pdf_encrypt_data(filtered, filtered_length); + + if (enc_mode) + pdf_encrypt_data(filtered, filtered_length); + if (filtered_length > 0) { pdf_out(file, filtered, filtered_length); } @@ -1700,11 +1843,12 @@ pdf_flush_obj (pdf_obj *object, FILE *file) long length; /* - * Record file position. No object is numbered 0, so subtract 1 - * when using as an array index + * Record file position */ - output_xref[object->label-1].file_position = pdf_output_file_position; - length = sprintf(format_buffer, "%lu %d obj\n", object->label, object->generation); + add_xref_entry(object->label, 1, + pdf_output_file_position, object->generation); + length = sprintf(format_buffer, "%lu %hu obj\n", object->label, object->generation); + enc_mode = doc_enc_mode && !(object->flags & OBJ_NO_ENCRYPT); pdf_enc_set_label(object->label); pdf_enc_set_generation(object->generation); pdf_out(file, format_buffer, length); @@ -1712,6 +1856,64 @@ 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) +{ + unsigned char pos = objstm->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; + 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); +} + +static void +pdf_flush_objstm (pdf_objstm *objstm) +{ + unsigned char pos = objstm->pos; + pdf_obj *stream_obj = objstm->stream; + TYPECHECK(stream_obj, PDF_STREAM); + pdf_stream *stream = (pdf_stream *) stream_obj->data; + + /* Precede stream data by offset table */ + unsigned char *old_buf = stream->stream; + unsigned long old_length = stream->stream_length; + /* 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; + + unsigned char i; + 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); + } + + pdf_obj *dict = pdf_stream_dict(stream_obj); + 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); + RELEASE(old_buf); + pdf_release_obj(stream_obj); + objstm->pos = 0; +} + void pdf_release_obj (pdf_obj *object) { @@ -1729,8 +1931,13 @@ pdf_release_obj (pdf_obj *object) * Nothing is using this object so it's okay to remove it. * Nonzero "label" means object needs to be written before it's destroyed. */ - if (object->label && pdf_output_file != NULL) { - pdf_flush_obj(object, pdf_output_file); + if (object->label && pdf_output_file != NULL) { + if (!current_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); } switch (object->type) { case PDF_BOOLEAN: @@ -2020,14 +2227,14 @@ next_object_offset (unsigned long obj_num) * returns the retained existing reference to that object */ static pdf_obj * -pdf_ref_file_obj (unsigned long obj_num, unsigned obj_gen) +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; + return pdf_new_null(); } ref = xref_table[obj_num].indirect; if (ref != NULL) @@ -2050,15 +2257,11 @@ pdf_ref_file_obj (unsigned long obj_num, unsigned obj_gen) pdf_obj * -pdf_new_ref (unsigned long obj_num, int obj_gen) +pdf_new_ref (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 %d", obj_num, obj_gen); - return NULL; - } result = pdf_new_obj(PDF_INDIRECT); indirect = NEW(1, pdf_indirect); result->data = indirect; @@ -2075,7 +2278,7 @@ pdf_new_ref (unsigned long obj_num, int obj_gen) * be replaced with "null". But we won't do that. */ static pdf_obj * -pdf_read_object (unsigned long obj_num, unsigned obj_gen) +pdf_read_object (unsigned long obj_num, unsigned short obj_gen) { pdf_obj *result; long offset, limit, length; @@ -2084,7 +2287,7 @@ pdf_read_object (unsigned long obj_num, unsigned obj_gen) if (!checklabel(obj_num, obj_gen)) { WARN("Trying to read nonexistent object: %lu %u", obj_num, obj_gen); - return NULL; + return pdf_new_null(); } if (labelfreed(obj_num, obj_gen)) { WARN("Trying to read deleted object: %lu %u", @@ -2361,7 +2564,7 @@ pdf_open (FILE *file) } pdf_input_file = file; if (!check_for_pdf(pdf_input_file)) { - WARN("pdf_open: Not a PDF 1.[1-3] file."); + WARN("pdf_open: Not a PDF 1.[1-5] file."); return NULL; } if ((trailer = read_xref()) == NULL) { |