summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvipdfm/pdfobj.c
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
committerKarl Berry <karl@freefriends.org>2006-01-17 21:41:51 +0000
commit487ca4806cc046076293cf6cc5fbba0db282bac7 (patch)
tree847b412ab5158dd7bdd7ed7e5a4cc3fbca94be32 /Build/source/texk/dvipdfm/pdfobj.c
parenta3d3111bfe26b8e5f5bc6049dfb2a4ca2edc7881 (diff)
texk 1
git-svn-id: svn://tug.org/texlive/trunk@1485 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/dvipdfm/pdfobj.c')
-rw-r--r--Build/source/texk/dvipdfm/pdfobj.c1738
1 files changed, 1738 insertions, 0 deletions
diff --git a/Build/source/texk/dvipdfm/pdfobj.c b/Build/source/texk/dvipdfm/pdfobj.c
new file mode 100644
index 00000000000..69ad6aadf1e
--- /dev/null
+++ b/Build/source/texk/dvipdfm/pdfobj.c
@@ -0,0 +1,1738 @@
+/* $Header$
+
+ This is dvipdfm, a DVI to PDF translator.
+ Copyright (C) 1998, 1999 by Mark A. Wicks
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ The author may be contacted via the e-mail address
+
+ mwicks@kettering.edu
+*/
+
+
+#include <ctype.h>
+#include <string.h>
+#include "system.h"
+#include "config.h"
+#include "mem.h"
+#include "error.h"
+#include "mfileio.h"
+#include "pdflimits.h"
+#include "pdfobj.h"
+#include "pdfspecial.h"
+#include "pdfparse.h"
+#include "twiddle.h"
+
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif /* HAVE_ZLIB */
+
+FILE *pdf_output_file = NULL;
+FILE *pdf_input_file = NULL;
+unsigned long pdf_output_file_position = 0, compression_saved = 0;
+int pdf_output_line_position = 0;
+#define FORMAT_BUF_SIZE 4096
+char format_buffer[FORMAT_BUF_SIZE];
+
+static struct xref_entry
+{
+ unsigned long file_position;
+ pdf_obj *pdf_obj;
+} *output_xref = NULL;
+static unsigned long pdf_max_ind_objects = 0;
+static unsigned long next_label = 1;
+
+static unsigned long startxref;
+
+static unsigned pdf_root_obj = 0, pdf_info_obj = 0;
+
+/* Internal static routines */
+
+static void pdf_flush_obj (FILE *file, const pdf_obj *object);
+static void pdf_label_obj (pdf_obj *object);
+
+static void pdf_out_char (FILE *file, char c);
+static void pdf_out (FILE *file, void *buffer, int length);
+
+static void release_indirect (pdf_indirect *data);
+static void write_indirect (FILE *file, const pdf_indirect *indirect);
+
+static void release_boolean (pdf_obj *data);
+static void write_boolean (FILE *file, const pdf_boolean *data);
+
+static void write_null (FILE *file);
+static void release_null (void *data);
+
+static void release_number (pdf_number *data);
+static void write_number (FILE *file, const pdf_number
+ *number);
+
+static void write_string (FILE *file, const pdf_string *string);
+static void release_string (pdf_string *data);
+
+static void write_name (FILE *file, const pdf_name *name);
+static void release_name (pdf_name *data);
+
+static void write_array (FILE *file, const pdf_array *array);
+static void release_array (pdf_array *data);
+
+static void write_dict (FILE *file, const pdf_dict *dict);
+static void release_dict (pdf_dict *data);
+
+static void write_stream (FILE *file, pdf_stream *stream);
+static void release_stream (pdf_stream *stream);
+static int pdf_match_name (const pdf_obj *name_obj, const char *name); /* Name does not include the / */
+
+static unsigned char debug = 0, verbose = 0;
+static char compression_level = 9;
+
+void pdf_obj_set_compression (int level)
+{
+ if (level >= 0 && level <= 9)
+ compression_level = level;
+ else {
+ ERROR("set_compression: compression level");
+ }
+#ifndef HAVE_ZLIB_COMPRESS2
+ if (level != 0)
+ fprintf (stderr, "Unable to set compression level--your zlib doesn't have compress2()\n");
+#endif
+ return;
+}
+
+static unsigned pdf_version = 3;
+void pdf_set_version (unsigned version)
+{
+ if (version >= 2 && version <= 3) {
+ pdf_version = version;
+ }
+}
+
+void pdf_obj_set_debug(void)
+{
+ debug = 1;
+}
+
+void pdf_obj_set_verbose(void)
+{
+ if (verbose < 255)
+ verbose += 1;
+}
+
+
+void pdf_out_init (const char *filename)
+{
+ char v;
+#ifdef MEM_DEBUG
+MEM_START
+#endif
+ if (!(pdf_output_file = MFOPEN (filename, FOPEN_WBIN_MODE))) {
+ if (strlen(filename) < 128) {
+ sprintf (format_buffer, "Unable to open %s\n", filename);
+ } else
+ sprintf (format_buffer, "Unable to open file");
+ ERROR (format_buffer);
+ }
+ pdf_out (pdf_output_file, "%PDF-1.", strlen("%PDF-1."));
+ v = '0'+pdf_version;
+ pdf_out (pdf_output_file, &v, 1);
+ pdf_out (pdf_output_file, "\n", 1);
+#ifdef MEM_DEBUG
+MEM_END
+#endif
+}
+
+static void dump_xref(void)
+{
+ int length;
+ unsigned long i;
+ startxref = pdf_output_file_position; /* Record where this xref is for
+ trailer */
+ pdf_out (pdf_output_file, "xref\n", 5);
+ sprintf (format_buffer, "%d %lu\n", 0, next_label);
+ length = strlen (format_buffer);
+ pdf_out (pdf_output_file, format_buffer, length);
+ sprintf (format_buffer, "%010ld %05ld f \n", 0L, 65535L);
+ length = strlen (format_buffer);
+ /* 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++){
+ sprintf (format_buffer, "%010ld %05ld n \n",
+ output_xref[i-1].file_position, 0L);
+ length = strlen (format_buffer);
+ pdf_out (pdf_output_file, format_buffer, length);
+ }
+ /* Done with xref table */
+ RELEASE (output_xref);
+}
+
+static void dump_trailer(void)
+{
+ int length;
+ unsigned long starttrailer;
+ starttrailer = pdf_output_file_position;
+ pdf_out (pdf_output_file, "trailer\n", 8);
+ pdf_out (pdf_output_file, "<<\n", 3);
+ sprintf (format_buffer, "/Size %lu\n",
+ next_label);
+ length = strlen (format_buffer);
+ pdf_out (pdf_output_file, format_buffer, length);
+ if (pdf_root_obj == 0)
+ ERROR ("dump_trailer: Invalid root object");
+ sprintf (format_buffer, "/Root %u %u R\n", pdf_root_obj, 0);
+ length = strlen (format_buffer);
+ pdf_out (pdf_output_file, format_buffer, length);
+ if (pdf_info_obj != 0) {
+ sprintf (format_buffer, "/Info %u %u R\n", pdf_info_obj, 0);
+ length = strlen (format_buffer);
+ pdf_out (pdf_output_file, format_buffer, length);
+ }
+ pdf_out (pdf_output_file, ">>\n", 3);
+ pdf_out (pdf_output_file, "startxref\n", 10);
+ sprintf (format_buffer, "%lu\n", startxref);
+ length = strlen (format_buffer);
+ pdf_out (pdf_output_file, format_buffer, length);
+ pdf_out (pdf_output_file, "%%EOF\n", 6);
+}
+
+void pdf_out_flush (void)
+{
+ if (pdf_output_file) {
+ if (debug) fprintf (stderr, "pdf_obj_out_flush: dumping xref\n");
+ dump_xref();
+ if (debug) fprintf (stderr, "pdf_obj_out_flush: dumping trailer\n");
+ dump_trailer();
+ if (verbose) {
+ if (compression_level>0) {
+ fprintf (stderr, "\nCompression eliminated approximately %lu bytes",
+ compression_saved);
+ }
+ }
+ fprintf (stderr, "\n%lu bytes written",
+ pdf_output_file_position);
+ MFCLOSE (pdf_output_file);
+ }
+}
+
+void pdf_error_cleanup (void)
+{
+ /* This routine is the cleanup required for an abnormal exit.
+ For now, simply close the file. */
+ if (pdf_output_file)
+ MFCLOSE (pdf_output_file);
+}
+
+
+void pdf_set_root (pdf_obj *object)
+{
+ if (pdf_root_obj != 0) {
+ ERROR ("pdf_set_root: root object already set");
+ }
+ if (object -> label == 0) { /* Make sure this object has a label */
+ pdf_label_obj (object);
+ }
+ pdf_root_obj = object -> label;
+}
+
+void pdf_set_info (pdf_obj *object)
+{
+ if (pdf_info_obj != 0) {
+ ERROR ("pdf_set_info: info object already set");
+ }
+ if (object -> label == 0) { /* Make sure this object has a label */
+ pdf_label_obj (object);
+ }
+ pdf_info_obj = object -> label;
+}
+
+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;
+ pdf_output_line_position += 1;
+ }
+ if (file == pdf_output_file && c == '\n')
+ pdf_output_line_position = 0;
+}
+
+static void pdf_out (FILE *file, void *buffer, int 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;
+ if (length > 0 && ((char *)buffer)[length-1] == '\n')
+ pdf_output_line_position = 0;
+ }
+}
+
+static void pdf_out_white (FILE *file)
+{
+ if (file == pdf_output_file && pdf_output_line_position >= 80) {
+ pdf_out_char (file, '\n');
+ } else {
+ pdf_out_char (file, ' ');
+ }
+ return;
+}
+
+pdf_obj *pdf_new_obj(pdf_obj_type type)
+{
+ pdf_obj *result;
+ result = NEW (1, pdf_obj);
+ result -> type = type;
+ result -> data = NULL;
+ result -> label = 0;
+ result -> generation = 0;
+ result -> refcount = 1;
+ return result;
+}
+
+static void pdf_label_obj (pdf_obj *object)
+{
+ if (object == NULL)
+ return;
+ 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);
+ }
+ if (object -> label == 0) { /* Don't change label on an already labeled
+ object. Ignore such calls */
+ /* Save so we can lookup this object by its number */
+ output_xref[next_label-1].pdf_obj = object;
+ object -> label = next_label++;
+ object -> generation = 0;
+ }
+}
+
+/* This doesn't really copy the object, but allows
+ it to be used without fear that somebody else will free it */
+
+pdf_obj *pdf_link_obj (pdf_obj *object)
+{
+ if (object == NULL)
+ ERROR ("pdf_link_obj passed null pointer");
+ object -> refcount += 1;
+ return object;
+}
+
+
+pdf_obj *pdf_ref_obj(pdf_obj *object)
+{
+ pdf_obj *result;
+ pdf_indirect *indirect;
+
+ if (object == NULL)
+ ERROR ("pdf_ref_obj passed null pointer");
+
+ if (object -> refcount == 0) {
+ fprintf (stderr, "\npdf_ref_obj: Called with already released object");
+ pdf_write_obj (stderr, object);
+ ERROR ("Fatal Error\n");
+ }
+ result = pdf_new_obj (PDF_INDIRECT);
+ indirect = NEW (1, pdf_indirect);
+ result -> data = indirect;
+ if (object -> type == PDF_INDIRECT) { /* If an object is already an indirect reference,
+ reference the original
+ object, not the indirect
+ one */
+ 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;
+ } 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 result;
+}
+
+static void release_indirect (pdf_indirect *data)
+{
+ RELEASE (data);
+}
+
+static void write_indirect (FILE *file, const pdf_indirect *indirect)
+{
+ int length;
+#ifdef MEM_DEBUG
+MEM_START
+#endif
+ if (indirect -> dirty) {
+ if (file == stderr) {
+ pdf_out (file, "{d}", 3);
+ sprintf (format_buffer, "%d %d R", indirect -> label,
+ indirect -> generation);
+ length = strlen (format_buffer);
+ pdf_out (file, format_buffer, length);
+ }
+ else {
+ pdf_obj *clean;
+ if (indirect -> dirty_file != pdf_input_file) {
+ fprintf (stderr, "\nwrite_indirect, label=%d, from_file=%p, current_file=%p\n", indirect -> label, indirect->dirty_file, pdf_input_file);
+ ERROR ("write_indirect: input PDF file doesn't match object");
+ }
+ clean = pdf_ref_file_obj (indirect -> label);
+ pdf_write_obj (file, clean);
+ pdf_release_obj (clean);
+ }
+ } else {
+ sprintf (format_buffer, "%d %d R", indirect -> label,
+ indirect -> generation);
+ length = strlen (format_buffer);
+ pdf_out (file, format_buffer, length);
+ }
+#ifdef MEM_DEBUG
+MEM_END
+#endif
+}
+
+pdf_obj *pdf_new_null (void)
+{
+ pdf_obj *result;
+ result = pdf_new_obj (PDF_NULL);
+ result -> data = NULL;
+ return result;
+}
+
+static void release_null (void *data)
+{
+ return;
+}
+
+static void write_null (FILE *file)
+{
+ pdf_out (file, "null", 4);
+}
+
+pdf_obj *pdf_new_boolean (char value)
+{
+ pdf_obj *result;
+ pdf_boolean *data;
+ result = pdf_new_obj (PDF_BOOLEAN);
+ data = NEW (1, pdf_boolean);
+ result -> data = data;
+ data -> value = value;
+ return result;
+}
+
+static void release_boolean (pdf_obj *data)
+{
+ RELEASE (data);
+}
+
+static void write_boolean (FILE *file, const pdf_boolean *data)
+{
+ if (data -> value) {
+ pdf_out (file, "true", 4);
+ }
+ else {
+ pdf_out (file, "false", 5);
+ }
+}
+
+void pdf_set_boolean (pdf_obj *object, char value)
+{
+ if (object == NULL || object -> type != PDF_BOOLEAN) {
+ ERROR ("pdf_set_boolean: Passed non-boolean object");
+ }
+ ((pdf_boolean *) (object -> data)) -> value = value;
+}
+
+pdf_obj *pdf_new_number (double value)
+{
+ pdf_obj *result;
+ pdf_number *data;
+ result = pdf_new_obj (PDF_NUMBER);
+ data = NEW (1, pdf_number);
+ result -> data = data;
+ data -> value = value;
+ return result;
+}
+
+static void release_number (pdf_number *data)
+{
+ RELEASE (data);
+}
+
+static void write_number (FILE *file, const pdf_number *number)
+{
+ int count;
+ sprintf (format_buffer, "%.10g", number -> value);
+ count = strlen (format_buffer);
+ pdf_out (file, format_buffer, count);
+}
+
+
+void pdf_set_number (pdf_obj *object, double value)
+{
+ if (object == NULL || object -> type != PDF_NUMBER) {
+ ERROR ("pdf_set_number: Passed non-number object");
+ }
+ ((pdf_number *) (object -> data)) -> value = value;
+}
+
+double pdf_number_value (pdf_obj *object)
+{
+ if (object == NULL || object -> type != PDF_NUMBER) {
+ ERROR ("pdf_obj_number_value: Passed non-number object");
+ }
+ return ((pdf_number *)(object -> data)) -> value;
+}
+
+pdf_obj *pdf_new_string (const void *string, unsigned length)
+{
+ pdf_obj *result;
+ pdf_string *data;
+ result = pdf_new_obj (PDF_STRING);
+ data = NEW (1, pdf_string);
+ result -> data = data;
+ if (length != 0) {
+ data -> length = length;
+ data -> string = NEW (length+1, unsigned char);
+ memcpy (data -> string, string, length);
+ data -> string[length] = 0;
+ } else {
+ data -> length = 0;
+ data -> string = NULL;
+ }
+ return result;
+}
+
+void *pdf_string_value (pdf_obj *a_pdf_string)
+{
+ pdf_string *data;
+ data = a_pdf_string -> data;
+ return data -> string;
+}
+
+unsigned int pdf_string_length (pdf_obj *a_pdf_string)
+{
+ pdf_string *data;
+ data = a_pdf_string -> data;
+ return (data -> length);
+}
+
+/* This routine escapes non printable characters and control
+ characters in an output string. It optionally remaps
+ the problem characters in the encoding */
+
+int pdfobj_escape_str (char *buffer, int bufsize, unsigned char *s,
+ int len, int remap)
+{
+ int result = 0, i;
+ unsigned char ch;
+ for (i=0; i<len; i++) {
+ ch = remap? twiddle(s[i]): s[i];
+ /* Exit as fast as possible for printable characters */
+ if (result+4 > bufsize)
+ ERROR ("pdfobj_escape_str: Buffer overflow");
+ if (ch < 32 || ch > 126) {
+ buffer[result++] = '\\';
+ sprintf (buffer+result, "%03o", ch);
+ result += strlen (buffer+result);
+ continue;
+ }
+ switch (ch) {
+ case '(':
+ buffer[result++] = '\\';
+ buffer[result++] = '(';
+ break;
+ case ')':
+ buffer[result++] = '\\';
+ buffer[result++] = ')';
+ break;
+ case '\\':
+ buffer[result++] = '\\';
+ buffer[result++] = '\\';
+ break;
+ default:
+ buffer[result++] = ch;
+ break;
+ }
+ }
+ return result;
+}
+
+
+static void write_string (FILE *file, const pdf_string *string)
+{
+ unsigned char *s = string -> string;
+ int count, i;
+ pdf_out_char (file, '(');
+ /* This section of code probably isn't speed critical. Escaping the
+ characters in the string one at a time may seem slow, but it's
+ safe if the formatted string length exceeds FORMAT_BUF_SIZE.
+ Occasionally you see some long strings in PDF. pdfobj_escape_str
+ is also used for strings of text with no kerning. These must be
+ handled as quickly as possible since there are so many of them. */
+ for (i=0; i<string->length; i++) {
+ count = pdfobj_escape_str (format_buffer, FORMAT_BUF_SIZE, s+i,
+ 1, 0);
+ pdf_out (file, format_buffer, count);
+ }
+ pdf_out_char (file, ')');
+}
+
+static void release_string (pdf_string *data)
+{
+ if (data -> string != NULL)
+ RELEASE (data -> string);
+ RELEASE (data);
+}
+
+void pdf_set_string (pdf_obj *object, unsigned char *string, unsigned length)
+{
+ pdf_string *data;
+ if (object == NULL || object -> type != PDF_STRING) {
+ ERROR ("pdf_set_string: Passed non-string object");
+ }
+ data = object -> data;
+ if (data -> length != 0) {
+ RELEASE (data -> string);
+ }
+ if (length != 0) {
+ data -> length = length;
+ data -> string = NEW (length+1, unsigned char);
+ memcpy (data -> string, string, length);
+ (data->string)[length] = 0;
+ } else {
+ data -> length = 0;
+ data -> string = NULL;
+ }
+ return;
+}
+
+int pdf_check_name(const char *name)
+{
+ static char *valid_chars =
+ "!\"$&'*+,-.0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\^_`abcdefghijklmnopqrstuvwxyz|~";
+ if (strspn (name, valid_chars) == strlen (name))
+ return 1;
+ else
+ return 0;
+}
+
+pdf_obj *pdf_new_name (const char *name) /* name does *not* include the / */
+{
+ pdf_obj *result;
+ unsigned length = strlen (name);
+ pdf_name *data;
+ if (!pdf_check_name (name)) {
+ fprintf (stderr, "Invalid PDF name \"%s\"\n", name);
+ ERROR ("pdf_new_name: invalid PDF name");
+ }
+ result = pdf_new_obj (PDF_NAME);
+ data = NEW (1, pdf_name);
+ result -> data = data;
+ if (length != 0) {
+ data -> name = NEW (length+1, char);
+ memcpy (data -> name, name, length);
+ (data->name)[length] = 0;
+ } else
+ data -> name = NULL;
+ return result;
+}
+
+static void write_name (FILE *file, const pdf_name *name)
+{
+ char *s = name -> name;
+ int i, length;
+ pdf_out_char (file, '/');
+ if (name -> name == NULL)
+ length = 0;
+ else
+ length = strlen (name -> name);
+ for (i=0; i < length; i++) {
+ if (isprint (s[i]) &&
+ s[i] != '/' &&
+ s[i] != '%' &&
+ s[i] != '(' &&
+ s[i] != ')' &&
+ s[i] != '[' &&
+ s[i] != ']' &&
+ s[i] != '#')
+ pdf_out_char (file, s[i]);
+ }
+}
+
+
+static void release_name (pdf_name *data)
+{
+ if (data -> name != NULL)
+ RELEASE (data -> name);
+ RELEASE (data);
+}
+
+void pdf_set_name (pdf_obj *object, char *name)
+{
+ pdf_name *data;
+ unsigned length = strlen (name);
+ if (object == NULL || object -> type != PDF_NAME) {
+ ERROR ("pdf_set_name: Passed non-name object");
+ }
+ data = object -> data;
+ if (data -> name != NULL) {
+ RELEASE (data -> name);
+ }
+ if (length != 0) {
+ data -> name = NEW (length+1, char);
+ memcpy (data -> name, name, length);
+ (data->name)[length] = 0;
+ } else {
+ data -> name = NULL;
+ }
+}
+
+char *pdf_name_value (pdf_obj *object)
+{
+ pdf_name *data;
+ if (object == NULL || object -> type != PDF_NAME) {
+ ERROR ("pdf_name_value: Passed non-name object");
+ }
+ data = object -> data;
+ if (data -> name == NULL)
+ return NULL;
+ return data -> name;
+}
+
+
+pdf_obj *pdf_new_array (void)
+{
+ pdf_obj *result;
+ pdf_array *data;
+ result = pdf_new_obj (PDF_ARRAY);
+ data = NEW (1, pdf_array);
+ data -> values = NULL;
+ data -> max = 0;
+ data -> size = 0;
+ result -> data = data;
+ return result;
+}
+
+static void write_array (FILE *file, const pdf_array *array)
+{
+ if (array -> size > 0) {
+ unsigned long i;
+ pdf_out_char (file, '[');
+ for (i=0; i<array->size; i++) {
+ if (i != 0 &&
+ ((array->values)[i]->type) != PDF_STRING &&
+ ((array->values)[i]->type) != PDF_NAME &&
+ ((array->values)[i]->type) != PDF_ARRAY)
+ pdf_out_white (file);
+ pdf_write_obj (file, (array->values)[i]);
+ }
+ pdf_out_char (file, ']');
+ } else {
+ write_null (file);
+ }
+}
+
+pdf_obj *pdf_get_array (pdf_obj *array, unsigned long index)
+{
+ pdf_array *data;
+ pdf_obj *result = NULL;
+ if (array == NULL) {
+ ERROR ("pdf_get_array: passed NULL object");
+ }
+ if (array -> type != PDF_ARRAY) {
+ ERROR ("pdf_get_array: passed non array object");
+ }
+ data = array -> data;
+ if (index < data -> size) {
+ result = (data->values)[index];
+ }
+ return result;
+}
+
+static void release_array (pdf_array *data)
+{
+ unsigned long i;
+ for (i=0; i<data->size; i++) {
+ pdf_release_obj ((data ->values)[i]);
+ }
+ if (data->size > 0)
+ RELEASE (data->values);
+ RELEASE (data);
+}
+
+void pdf_add_array (pdf_obj *array, pdf_obj *object) /* Array is ended
+ by a node with NULL
+ this pointer */
+{
+ pdf_array *data;
+ if (array == NULL || array -> type != PDF_ARRAY) {
+ ERROR ("pdf_add_array: Passed non-array object");
+ }
+ data = array -> data;
+ if (data -> size >= data -> max) {
+ data->max += ARRAY_ALLOC_SIZE;
+ data->values = RENEW (data->values, data->max, pdf_obj *);
+ }
+ (data->values)[data->size++] = object;
+ return;
+}
+
+
+static void write_dict (FILE *file, const pdf_dict *dict)
+{
+ pdf_out (file, "<<\n", 3);
+ while (dict -> key != NULL) {
+ pdf_write_obj (file, dict -> key);
+ if (((dict -> value) -> type) == PDF_BOOLEAN ||
+ ((dict -> value) -> type) == PDF_NUMBER ||
+ ((dict -> value) -> type) == PDF_INDIRECT ||
+ ((dict -> value) -> type) == PDF_NULL)
+ pdf_out_white (file);
+ pdf_write_obj (file, dict -> value);
+ dict = dict -> next;
+ pdf_out_char (file, '\n');
+ }
+ pdf_out (file, ">>", 2);
+}
+
+pdf_obj *pdf_new_dict (void)
+{
+ pdf_obj *result;
+ pdf_dict *data;
+ result = pdf_new_obj (PDF_DICT);
+ data = NEW (1, pdf_dict);
+ data -> key = NULL;
+ data -> value = NULL;
+ data -> next = NULL;
+ result -> data = data;
+ return result;
+}
+
+static void release_dict (pdf_dict *data)
+{
+ pdf_dict *next;
+ while (data != NULL && data -> key != NULL) {
+ pdf_release_obj (data -> key);
+ pdf_release_obj (data -> value);
+ next = data -> next;
+ RELEASE (data);
+ data = next;
+ }
+ RELEASE (data);
+}
+
+void pdf_add_dict (pdf_obj *dict, pdf_obj *key, pdf_obj *value) /* Array is ended
+ by a node with NULL
+ this pointer */
+{
+ pdf_dict *data;
+ pdf_dict *new_node;
+ if (key == NULL || key -> type != PDF_NAME ) {
+ ERROR ("pdf_add_dict: Passed invalid key");
+ }
+ if (value != NULL &&
+ (value -> type == 0 || value -> type > PDF_INDIRECT )) {
+ ERROR ("pdf_add_dict: Passed invalid value");
+ }
+ if (dict == NULL || dict -> type != PDF_DICT) {
+ fprintf (stderr, "key:");
+ pdf_write_obj (stderr, key);
+ fprintf (stderr, "value:");
+ pdf_write_obj (stderr, value);
+ ERROR ("pdf_add_dict: Passed non-dict object");
+ }
+ data = dict -> data;
+ /* If this key already exists, simply replace the value */
+ while (data -> key != NULL) {
+ 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;
+ }
+ data = data -> next;
+ }
+ /* If we didn't find the key, build a new "end" node and add
+ the new key just before the end */
+ if (data -> key == NULL) {
+ 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;
+}
+
+/* pdf_merge_dict makes a link for each item in dict2 before
+ stealing it */
+void pdf_merge_dict (pdf_obj *dict1, pdf_obj *dict2)
+{
+ pdf_dict *data;
+ if (dict1 == NULL || dict1 -> type != PDF_DICT)
+ ERROR ("pdf_merge_dict: Passed invalid first dictionary");
+ if (dict2 == NULL || dict2 -> type != PDF_DICT)
+ ERROR ("pdf_merge_dict: Passed invalid second dictionary");
+ data = dict2 -> data;
+ while (data -> key != NULL) {
+ pdf_add_dict (dict1, pdf_link_obj(data -> key),
+ pdf_link_obj (data -> value));
+ data = data -> next;
+ }
+}
+
+static int pdf_match_name (const pdf_obj *name_obj, const char *name_string)
+{
+ pdf_name *data;
+ data = name_obj -> data;
+ return (!strcmp (data -> name, name_string));
+}
+
+pdf_obj *pdf_lookup_dict (const pdf_obj *dict, const char *name)
+{
+ pdf_dict *data;
+ if (dict == NULL || dict ->type != PDF_DICT)
+ ERROR ("pdf_lookup_dict: Passed invalid dictionary");
+ data = dict -> data;
+ while (data -> key != NULL) {
+ if (pdf_match_name (data -> key, name))
+ return data -> value;
+ data = data -> next;
+ }
+ return NULL;
+}
+
+void pdf_remove_dict (pdf_obj *dict, const char *name)
+{
+ pdf_dict *data, **data_p;
+ if (dict == NULL || dict->type != PDF_DICT)
+ ERROR ("pdf_remove_dict: Passed invalid dictionary");
+ data = (dict -> data);
+ data_p = (pdf_dict **) &(dict->data);
+ while (data->key != NULL) {
+ if (pdf_match_name (data -> key, name)) {
+ pdf_release_obj (data -> key);
+ pdf_release_obj (data -> value);
+ *data_p = data -> next;
+ RELEASE (data);
+ break;
+ }
+ data_p = &(data->next);
+ data = data -> next;
+ }
+ return;
+}
+
+char *pdf_get_dict (const pdf_obj *dict, int index)
+{
+ pdf_dict *data;
+ char *result;
+ if (dict == NULL) {
+ ERROR ("pdf_get_dict: passed NULL object");
+ }
+ if (dict -> type != PDF_DICT) {
+ ERROR ("pdf_get_dict: passed non array object");
+ }
+ data = dict -> data;
+ while (index-- > 0 && data -> next != NULL)
+ data = data -> next;
+ if (data -> next == NULL)
+ return NULL;
+ result = pdf_name_value (data -> key);
+ return result;
+}
+
+pdf_obj *pdf_new_stream (int flags)
+{
+ pdf_obj *result;
+#ifdef HAVE_ZLIB
+ pdf_obj *filters = NULL;
+#endif /* HAVE_ZLIB */
+ pdf_stream *data;
+ result = pdf_new_obj (PDF_STREAM);
+ data = NEW (1, pdf_stream);
+ result -> data = data;
+ data -> dict = pdf_new_dict (); /* Although we are using an arbitrary
+ pdf_object here, it must have
+ type=PDF_DICT and cannot be an
+ indirect reference. This will
+ be checked by the output routine
+ */
+ data -> _flags = flags;
+#ifdef HAVE_ZLIB
+ if ((flags & STREAM_COMPRESS) && compression_level > 0) {
+ if (!filters) {
+ filters = pdf_new_array();
+ pdf_add_dict (data -> dict, pdf_new_name ("Filter"), filters);
+ }
+ pdf_add_array (filters, pdf_new_name ("FlateDecode"));
+ }
+#endif /* HAVE_ZLIB */
+
+ data -> stream_length = 0;
+ data -> max_length = 0;
+ data -> stream = NULL;
+ return result;
+}
+
+
+static void write_stream (FILE *file, pdf_stream *stream)
+{
+#define COMPRESS_LEVEL 9
+#define THRESHOLD 100
+ unsigned char *filtered;
+ unsigned long filtered_length;
+#ifdef HAVE_ZLIB
+ unsigned long buffer_length;
+ unsigned char *buffer;
+#endif /* HAVE_ZLIB */
+ /* Always work from a copy of the stream */
+ /* All filters read from "filtered" and leave their result in
+ "filtered" */
+ filtered = NEW (stream->stream_length+1, unsigned char);
+ memcpy (filtered, stream->stream, stream->stream_length);
+ filtered_length = stream->stream_length;
+
+#ifdef HAVE_ZLIB
+ /* Apply compression filter if requested */
+ if ((stream -> _flags & STREAM_COMPRESS) && compression_level > 0) {
+ buffer_length = filtered_length + filtered_length/1000 + 14;
+ buffer = NEW (buffer_length, unsigned char);
+#ifdef HAVE_ZLIB_COMPRESS2
+ if (compress2 (buffer, &buffer_length, filtered,
+ filtered_length, compression_level))
+ ERROR ("Zlib error");
+#else
+ if (compress (buffer, &buffer_length, filtered,
+ filtered_length))
+ ERROR ("Zlib error");
+#endif /* HAVE_ZLIB_COMPRESS2 */
+ RELEASE (filtered);
+ filtered = buffer;
+ compression_saved += (filtered_length-buffer_length)-strlen("/Filter [/FlateDecode]\n");
+ filtered_length = buffer_length;
+ }
+#endif /* HAVE_ZLIB */
+ /* Add a '\n' if the last character wasn't one */
+ if (filtered_length > 0 && filtered[filtered_length-1] != '\n') {
+ filtered[filtered_length++] = '\n';
+ }
+ pdf_add_dict (stream->dict,
+ pdf_new_name ("Length"),
+ pdf_new_number(filtered_length));
+ pdf_write_obj (file, stream -> dict);
+ pdf_out (file, "\nstream\n", 8);
+
+ if (filtered_length > 0) {
+ pdf_out (file, filtered, filtered_length);
+ }
+
+ RELEASE (filtered);
+ /* This stream length "object" gets reset every time write_stream is
+ called for the stream object */
+ /* If this stream gets written more than once with different
+ filters, this could be a problem */
+ pdf_out (file, "endstream", 9);
+ return;
+}
+
+static void release_stream (pdf_stream *stream)
+{
+ pdf_release_obj (stream -> dict);
+ if (stream -> stream_length > 0)
+ RELEASE (stream -> stream);
+ RELEASE (stream);
+}
+
+pdf_obj *pdf_stream_dict (pdf_obj *stream)
+{
+ pdf_stream *data;
+ if (stream == NULL || stream -> type != PDF_STREAM) {
+ ERROR ("pdf_stream_dict: Passed non-stream object");
+ }
+ data = stream -> data;
+ return data -> dict;
+}
+
+void pdf_add_stream (pdf_obj *stream, char *stream_data, unsigned length)
+{
+ pdf_stream *data;
+ if (stream == NULL || stream -> type != PDF_STREAM) {
+ ERROR ("pdf_add_stream: Passed non-stream object");
+ }
+ if (length == 0)
+ return;
+ data = stream -> data;
+ if (data -> stream_length + length > data -> max_length) {
+ data -> max_length += length + STREAM_ALLOC_SIZE;
+ data -> stream = RENEW (data -> stream, data->max_length, char);
+ }
+ memcpy ((data->stream)+(data->stream_length), stream_data,
+ length);
+ data->stream_length += length;
+}
+
+void pdf_write_obj (FILE *file, const pdf_obj *object)
+{
+ if (object == NULL) {
+ write_null(file);
+ return;
+ }
+ if (object -> type > PDF_INDIRECT) {
+ fprintf (stderr, "Object type = %d\n", object -> type);
+ ERROR ("pdf_write_obj: Called with invalid object");
+ }
+ if (file == stderr)
+ fprintf (stderr, "{%d}", object -> refcount);
+ switch (object -> type) {
+ case PDF_BOOLEAN:
+ write_boolean (file, object -> data);
+ break;
+ case PDF_NUMBER:
+ write_number (file, object -> data);
+ break;
+ case PDF_STRING:
+ write_string (file, object -> data);
+ break;
+ case PDF_NAME:
+ write_name (file, object -> data);
+ break;
+ case PDF_ARRAY:
+ write_array (file, object -> data);
+ break;
+ case PDF_DICT:
+ write_dict (file, object -> data);
+ break;
+ case PDF_STREAM:
+ write_stream (file, object -> data);
+ break;
+ case PDF_NULL:
+ write_null (file);
+ break;
+ case PDF_INDIRECT:
+ write_indirect (file, object -> data);
+ break;
+ }
+}
+
+static void pdf_flush_obj (FILE *file, const pdf_obj *object)
+ /* Write the object to the file */
+{
+ int length;
+ /* Record file position. No object is numbered 0, so subtract 1
+ when using as an array index */
+ output_xref[object->label-1].file_position = pdf_output_file_position;
+ sprintf (format_buffer, "%lu %d obj\n", object -> label ,
+ object -> generation);
+ length = strlen (format_buffer);
+ pdf_out (file, format_buffer, length);
+ pdf_write_obj (file, object);
+ pdf_out (file, "\nendobj\n", 8);
+}
+
+
+void pdf_release_obj (pdf_obj *object)
+{
+ if (object == NULL)
+ return;
+ if (object -> type > PDF_INDIRECT ||
+ object -> refcount <= 0) {
+ fprintf (stderr, "pdf_release_obj: object = %p, type = %d\n", object, object ->
+ type);
+ pdf_write_obj (stderr, object);
+ ERROR ("pdf_release_obj: Called with invalid object");
+ }
+ object -> refcount -= 1;
+ if (object -> refcount == 0) { /* 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 (pdf_output_file, object);
+ }
+ switch (object -> type) {
+ case PDF_BOOLEAN:
+ release_boolean (object -> data);
+ break;
+ case PDF_NULL:
+ release_null (object -> data);
+ break;
+ case PDF_NUMBER:
+ release_number (object -> data);
+ break;
+ case PDF_STRING:
+ release_string (object -> data);
+ break;
+ case PDF_NAME:
+ release_name (object -> data);
+ break;
+ case PDF_ARRAY:
+ release_array (object -> data);
+ break;
+ case PDF_DICT:
+ release_dict (object -> data);
+ break;
+ case PDF_STREAM:
+ release_stream (object -> data);
+ break;
+ case PDF_INDIRECT:
+ release_indirect (object -> data);
+ break;
+ }
+ /* This might help detect freeing already freed objects */
+ /* object -> type = -1;*/
+ RELEASE (object);
+ }
+}
+
+static int backup_line (void)
+{
+ int ch;
+ ch = -1;
+ if (debug) {
+ fprintf (stderr, "\nbackup_line:\n");
+ }
+ /* Note: this code should work even if \r\n is eol.
+ It could fail on a machine where \n is eol and
+ there is a \r in the stream---Highly unlikely
+ in the last few bytes where this is likely to be used.
+ */
+ if (tell_position (pdf_input_file) > 1)
+ do {
+ seek_relative (pdf_input_file, -2);
+ if (debug)
+ fprintf (stderr, "%c", ch);
+ } while (tell_position (pdf_input_file) > 0 &&
+ (ch = fgetc (pdf_input_file)) >= 0 &&
+ (ch != '\n' && ch != '\r' ));
+ if (debug)
+ fprintf (stderr, "<-\n");
+ if (ch < 0) {
+ return 0;
+ }
+ return 1;
+}
+
+static unsigned long pdf_file_size = 0;
+
+static long find_xref(void)
+{
+ long currentpos, xref_pos;
+ int tries = 10;
+ char *start, *end, *number;
+ if (debug)
+ fprintf (stderr, "(find_xref");
+ seek_end (pdf_input_file);
+ pdf_file_size = tell_position (pdf_input_file);
+ do {
+ if (!backup_line()) {
+ tries = 0;
+ break;
+ }
+ currentpos = tell_position(pdf_input_file);
+ fread (work_buffer, sizeof(char), strlen("startxref"),
+ pdf_input_file);
+ if (debug) {
+ work_buffer[strlen("startxref")] = 0;
+ fprintf (stderr, "[%s]\n", work_buffer);
+ }
+ seek_absolute(pdf_input_file, currentpos);
+ tries--;
+ } while (tries > 0 && strncmp (work_buffer, "startxref", strlen ("startxref")));
+ if (tries <= 0)
+ return 0;
+ /* Skip rest of this line */
+ mfgets (work_buffer, WORK_BUFFER_SIZE, pdf_input_file);
+ /* Next line of input file should contain actual xref location */
+ mfgets (work_buffer, WORK_BUFFER_SIZE, pdf_input_file);
+ if (debug) {
+ fprintf (stderr, "\n->[%s]<-\n", work_buffer);
+ }
+ start = work_buffer;
+ end = start+strlen(work_buffer);
+ skip_white(&start, end);
+ xref_pos = (long) atof (number = parse_number (&start, end));
+ RELEASE (number);
+ if (debug) {
+ fprintf (stderr, ")\n");
+ fprintf (stderr, "xref @ %lu\n", xref_pos);
+ }
+ return xref_pos;
+}
+
+pdf_obj *parse_trailer (void)
+{
+ char *start;
+#ifdef MEM_DEBUG
+MEM_START
+#endif
+ /* This routine must be called with the file pointer located at
+ the start of the trailer */
+ /* Fill work_buffer and hope trailer fits. This should
+ be made a bit more robust sometime */
+ if (fread (work_buffer, sizeof(char), WORK_BUFFER_SIZE,
+ pdf_input_file) == 0 ||
+ strncmp (work_buffer, "trailer", strlen("trailer"))) {
+ fprintf (stderr, "No trailer. Are you sure this is a PDF file?\n");
+ fprintf (stderr, "\nbuffer:\n->%s<-\n", work_buffer);
+ return NULL;
+ }
+ start = work_buffer + strlen("trailer");
+ skip_white(&start, work_buffer+WORK_BUFFER_SIZE);
+#ifdef MEM_DEBUG
+MEM_END
+#endif
+ return (parse_pdf_dict (&start, work_buffer+WORK_BUFFER_SIZE));
+}
+
+struct object
+{
+ unsigned long 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;
+
+long next_object (unsigned long obj)
+{
+ /* 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.
+ The parsing routines require that the entire object be read into
+ memory. It would be a major pain to rewrite them. The worst case
+ is that an object before an xref table will grab the whole table
+ :-( */
+ unsigned long i;
+ long this_position, result = pdf_file_size; /* Worst case */
+ this_position = xref_table[obj].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].position > this_position &&
+ xref_table[i].position < result)
+ result = xref_table[i].position;
+ }
+ return result;
+}
+
+/* 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 */
+
+pdf_obj *pdf_ref_file_obj (unsigned long obj_no)
+{
+ pdf_obj *direct, *indirect;
+#ifdef MEM_DEBUG
+MEM_START
+#endif
+ if (obj_no >= num_input_objects) {
+ fprintf (stderr, "\n\npdf_ref_file_obj: nonexistent object\n");
+ return NULL;
+ }
+ if (xref_table[obj_no].indirect != NULL) {
+ return pdf_link_obj(xref_table[obj_no].indirect);
+ }
+ if ((direct = pdf_read_object (obj_no)) == NULL) {
+ fprintf (stderr, "\npdf_ref_file_obj: Could not read object\n");
+ return NULL;
+ }
+ indirect = pdf_ref_obj (direct);
+ xref_table[obj_no].indirect = indirect;
+ xref_table[obj_no].direct = direct;
+ /* Make sure the caller can't doesn't free this object */
+#ifdef MEM_DEBUG
+MEM_END
+#endif
+ return pdf_link_obj(indirect);
+}
+
+
+pdf_obj *pdf_new_ref (unsigned long label, int generation)
+{
+ pdf_obj *result;
+ pdf_indirect *indirect;
+ if (label >= num_input_objects || label < 0) {
+ fprintf (stderr, "pdf_new_ref: Object doesn't exist\n");
+ return NULL;
+ }
+ result = pdf_new_obj (PDF_INDIRECT);
+ indirect = NEW (1, pdf_indirect);
+ result -> data = indirect;
+ indirect -> label = label;
+ indirect -> generation = generation;
+ indirect -> dirty = 1;
+ indirect -> dirty_file = pdf_input_file;
+ return result;
+}
+
+pdf_obj *pdf_read_object (unsigned long obj_no)
+{
+ long start_pos, end_pos;
+ char *buffer, *number, *parse_pointer, *end;
+ pdf_obj *result;
+#ifdef MEM_DEBUG
+MEM_START
+#endif
+ if (debug) {
+ fprintf (stderr, "\nread_object: obj=%lu\n", obj_no);
+ }
+ if (obj_no < 0 || obj_no >= num_input_objects) {
+ fprintf (stderr, "\nTrying to read nonexistent object\n");
+ return NULL;
+ }
+ if (!xref_table[obj_no].used) {
+ fprintf (stderr, "\nTrying to read deleted object\n");
+ return NULL;
+ }
+ if (debug) {
+ fprintf (stderr, "\nobj@%lu\n", xref_table[obj_no].position);
+ }
+ seek_absolute (pdf_input_file, start_pos =
+ xref_table[obj_no].position);
+ end_pos = next_object (obj_no);
+ if (debug) {
+ fprintf (stderr, "\nendobj@%lu\n", end_pos);
+ }
+ buffer = NEW (end_pos - start_pos+1, char);
+ fread (buffer, sizeof(char), end_pos-start_pos, pdf_input_file);
+ buffer[end_pos-start_pos] = 0;
+ if (debug) {
+ fprintf (stderr, "\nobject:\n%s", buffer);
+ }
+ parse_pointer = buffer;
+ end = buffer+(end_pos-start_pos);
+ skip_white (&parse_pointer, end);
+ number = parse_number (&parse_pointer, end);
+ if ((int) atof(number) != obj_no) {
+ fprintf (stderr, "Object number doesn't match\n");
+ RELEASE (buffer);
+ return NULL;
+ }
+ if (number != NULL)
+ RELEASE(number);
+ skip_white (&parse_pointer, end);
+ number = parse_number (&parse_pointer, end);
+ if (number != NULL)
+ RELEASE(number);
+ skip_white(&parse_pointer, end);
+ if (strncmp(parse_pointer, "obj", strlen("obj"))) {
+ fprintf (stderr, "Didn't find \"obj\"\n");
+ RELEASE (buffer);
+ return (NULL);
+ }
+ parse_pointer += strlen("obj");
+ result = parse_pdf_object (&parse_pointer, end);
+ skip_white (&parse_pointer, end);
+ if (strncmp(parse_pointer, "endobj", strlen("endobj"))) {
+ fprintf (stderr, "Didn't find \"endobj\"\n");
+ if (result != NULL)
+ pdf_release_obj (result);
+ result = NULL;
+ }
+ RELEASE (buffer);
+ return (result);
+#ifdef MEM_DEBUG
+MEM_END
+#endif
+}
+/* pdf_deref_obj always returns a link instead of the original */
+pdf_obj *pdf_deref_obj (pdf_obj *obj)
+{
+ pdf_obj *result, *tmp;
+ pdf_indirect *indirect;
+ if (obj == NULL)
+ return NULL;
+ if (obj -> type != PDF_INDIRECT) {
+ return pdf_link_obj (obj);
+ }
+ indirect = obj -> data;
+ if (!(indirect -> dirty)) {
+ ERROR ("Tried to deref a non-file object");
+ }
+ result = pdf_read_object (indirect -> label);
+
+ if (debug){
+ fprintf (stderr, "\npdf_deref_obj: read_object returned\n");
+ pdf_write_obj (stderr, result);
+ }
+
+ while (result && result -> type == PDF_INDIRECT) {
+ tmp = pdf_read_object (result -> label);
+ pdf_release_obj (result);
+ result = tmp;
+ }
+ return result;
+}
+
+/* extends the xref table if we get another segment
+ with higher object numbers than the current object */
+static void extend_xref (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].position = 0;
+ }
+ num_input_objects = new_size;
+}
+
+
+
+static int parse_xref (void)
+{
+ unsigned long first_obj, num_table_objects;
+ unsigned long i;
+ /* This routine reads one xref segment. It must be called
+ positioned at the beginning of an xref table. It may be called
+ multiple times on the same file. xref tables sometimes come in
+ pieces */
+ mfgets (work_buffer, WORK_BUFFER_SIZE, pdf_input_file);
+ if (strncmp (work_buffer, "xref", strlen("xref"))) {
+ fprintf (stderr, "No xref. Are you sure this is a PDF file?\n");
+ return 0;
+ }
+ /* Next line in file has first item and size of table */
+ for (;;) {
+ 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 (!strncmp (work_buffer, "trailer", strlen ("trailer"))) {
+ /* Backup... This is ugly, but it seems like the safest thing to
+ do. It is possible the trailer dictionary starts on the same
+ logical line as the word trailer. In that case, the mfgets
+ call might have started to read the trailer dictionary and
+ parse_trailer would fail */
+ seek_absolute (pdf_input_file, current_pos);
+ break;
+ }
+ sscanf (work_buffer, "%lu %lu", &first_obj, &num_table_objects);
+ if (num_input_objects < first_obj+num_table_objects) {
+ extend_xref (first_obj+num_table_objects);
+ }
+ if (debug) {
+ fprintf (stderr, "\nfirstobj=%lu,number=%lu\n",
+ first_obj,num_table_objects);
+ }
+ for (i=first_obj; i<first_obj+num_table_objects; i++) {
+ fread (work_buffer, sizeof(char), 20, pdf_input_file);
+ /* Don't overwrite positions that have already been set by a
+ modified xref table. We are working our way backwards
+ through the reference table, so we only set "position"
+ if it hasn't been set yet. */
+ if (xref_table[i].position == 0) {
+ work_buffer[19] = 0;
+ sscanf (work_buffer, "%lu %u", &(xref_table[i].position),
+ &(xref_table[i].generation));
+ }
+ if (debug) {
+ fprintf (stderr, "pos: %lu gen: %u\n", xref_table[i].position,
+ xref_table[i].generation);
+ }
+ if (work_buffer[17] != 'n' && work_buffer[17] != 'f') {
+ fprintf (stderr, "PDF file is corrupt\n");
+ fprintf (stderr, "[%s]\n", work_buffer);
+ return 0;
+ }
+ if (work_buffer[17] == 'n')
+ xref_table[i].used = 1;
+ else
+ xref_table[i].used = 0;
+ xref_table[i].direct = NULL;
+ xref_table[i].indirect = NULL;
+ }
+ }
+ return 1;
+}
+
+pdf_obj *read_xref (void)
+{
+ pdf_obj *main_trailer, *prev_trailer, *prev_xref, *xref_size;
+ long xref_pos;
+#ifdef MEM_DEBUG
+MEM_START
+#endif
+ if ((xref_pos = find_xref()) == 0) {
+ fprintf (stderr, "Can't find xref table.\n");
+ return NULL;
+ }
+ if (debug) {
+ fprintf(stderr, "xref@%lu\n", xref_pos);
+ }
+ /* Read primary xref table */
+ seek_absolute (pdf_input_file, xref_pos);
+ if (!parse_xref()) {
+ fprintf (stderr,
+ "\nCouldn't read xref table. Is this a correct PDF file?\n");
+ return NULL;
+ }
+ if ((main_trailer = parse_trailer()) == NULL) {
+ fprintf (stderr,
+ "\nCouldn't read xref trailer. Is this a correct PDF file?\n");
+ return NULL;
+ }
+ if (pdf_lookup_dict (main_trailer, "Root") == NULL ||
+ (xref_size = pdf_lookup_dict (main_trailer, "Size")) == NULL) {
+ fprintf (stderr,
+ "\nTrailer doesn't have catalog or a size. Is this a correct PDF file?\n");
+ return NULL;
+ }
+ if (num_input_objects < pdf_number_value (xref_size)) {
+ extend_xref (pdf_number_value (xref_size));
+ }
+ /* Read any additional xref tables */
+ prev_trailer = pdf_link_obj (main_trailer);
+ while ((prev_xref = pdf_lookup_dict (prev_trailer, "Prev")) != NULL) {
+ xref_pos = pdf_number_value (prev_xref);
+ seek_absolute (pdf_input_file, xref_pos);
+ pdf_release_obj (prev_trailer);
+ if (!parse_xref()) {
+ fprintf (stderr,
+ "\nCouldn't read xref table. Is this a correct PDF file?\n");
+ return NULL;
+ }
+ if ((prev_trailer = parse_trailer()) == NULL) {
+ fprintf (stderr,
+ "\nCouldn't read xref trailer. Is this a correct PDF file?\n");
+ return NULL;
+ }
+ if (debug) {
+ fprintf (stderr, "\nprev_trailer:\n");
+ pdf_write_obj (stderr, prev_trailer);
+ }
+ }
+#ifdef MEM_DEBUG
+MEM_END
+#endif
+ pdf_release_obj (prev_trailer);
+ return main_trailer;
+}
+
+static char any_open = 0;
+
+pdf_obj *pdf_open (FILE *file)
+{
+ pdf_obj *trailer;
+#ifdef MEM_DEBUG
+MEM_START
+#endif
+ if (any_open) {
+ fprintf (stderr, "\nOnly one PDF file may be open at one time.\n");
+ any_open = 1;
+ exit(1);
+ }
+ pdf_input_file = file;
+ if (!check_for_pdf (pdf_input_file)) {
+ fprintf (stderr, "pdf_open: Not a PDF 1.[1-3] file\n");
+ return NULL;
+ }
+ if ((trailer = read_xref()) == NULL) {
+ fprintf (stderr, "No trailer.\n");
+ pdf_close ();
+ return NULL;
+ }
+ if (debug) {
+ fprintf (stderr, "\nDone with xref:\n");
+ }
+#ifdef MEM_DEBUG
+ MEM_END
+#endif
+ return trailer;
+}
+
+void pdf_close (void)
+{
+ /* Following loop must be iterated because each write could trigger
+ an additional indirect reference of an object with a lower
+ number! */
+ unsigned long i;
+ int done;
+ if (debug) {
+ fprintf (stderr, "\npdf_close:\n");
+ fprintf (stderr, "pdf_input_file=%p\n", pdf_input_file);
+ }
+ 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;
+ }
+ }
+ } 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);
+ }
+ }
+ RELEASE (xref_table);
+ xref_table = NULL;
+ num_input_objects = 0;
+ any_open = 0;
+ pdf_input_file = NULL;
+ if (debug) {
+ fprintf (stderr, "\nexiting pdf_close:\n");
+ }
+}
+
+int check_for_pdf (FILE *file)
+{
+ int result = 0;
+ rewind (file);
+ if (fread (work_buffer, sizeof(char), strlen("%PDF-1.x"), file) ==
+ strlen("%PDF-1.x") &&
+ !strncmp(work_buffer, "%PDF-1.", strlen("%PDF-1."))) {
+ if (work_buffer[7] >= '0' && work_buffer[7] <= '0'+pdf_version)
+ result = 1;
+ else {
+ fprintf (stderr, "\nVersion of PDF file (1.%c) is newer than version limit specification.\n", work_buffer[7]);
+ }
+ }
+ return result;
+}