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