summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvipdfm-x/pdfobj.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvipdfm-x/pdfobj.c')
-rw-r--r--Build/source/texk/dvipdfm-x/pdfobj.c4624
1 files changed, 0 insertions, 4624 deletions
diff --git a/Build/source/texk/dvipdfm-x/pdfobj.c b/Build/source/texk/dvipdfm-x/pdfobj.c
deleted file mode 100644
index 804466378bc..00000000000
--- a/Build/source/texk/dvipdfm-x/pdfobj.c
+++ /dev/null
@@ -1,4624 +0,0 @@
-/* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
-
- Copyright (C) 2002-2020 by Jin-Hwan Cho and Shunsaku Hirata,
- the dvipdfmx project team.
-
- Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
-
- 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.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <ctype.h>
-#include <string.h>
-/* floor and abs */
-#include <math.h>
-
-#include "system.h"
-#include "mem.h"
-#include "error.h"
-#include "mfileio.h"
-#include "dpxconf.h"
-#include "dpxutil.h"
-
-#include "pdflimits.h"
-#include "pdfencrypt.h"
-#include "pdfparse.h"
-
-#ifdef HAVE_ZLIB
-#include <zlib.h>
-#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
-{
- int type;
-
- uint32_t label; /* Only used for indirect objects all other "label" to zero */
- uint16_t generation; /* Only used if "label" is used */
- int refcount; /* Number of links to this object */
- int32_t flags;
- void *data;
-
-#if defined(PDFOBJ_DEBUG)
- int obj_id;
-#endif
-};
-
-#if defined(PDFOBJ_DEBUG)
-static pdf_obj *bucket[65535];
-static int cur_obj_id = 0;
-#endif
-
-struct pdf_boolean
-{
- char value;
-};
-
-struct pdf_number
-{
- double value;
-};
-
-struct pdf_string
-{
- unsigned char *string;
- size_t length;
-};
-
-struct pdf_name
-{
- char *name;
-};
-
-struct pdf_array
-{
- size_t max;
- size_t size;
- struct pdf_obj **values;
-};
-
-struct pdf_dict
-{
- struct pdf_obj *key;
- struct pdf_obj *value;
- struct pdf_dict *next;
-};
-
-/* DecodeParms for FlateDecode */
- struct decode_parms {
- int predictor;
- int colors;
- int bits_per_component;
- int32_t columns;
- };
-
-/* 2015/12/27 Added support for predictor functions
- *
- * There are yet no way to specify the use of predictor functions.
- * Using TIFF2 or PNG predictor usually gives better compression for images
- * but there is a case that compression speed becomes significantly slower.
- * Please use -C 0x20 option to disable the use of predictor functions.
- *
- * See, e.g., for a heuristic approach for selecting filters
- * http://www.w3.org/TR/PNG-Encoders.html#E.Filter-selection
- */
-
-struct pdf_stream
-{
- struct pdf_obj *dict;
- unsigned char *stream;
- int *objstm_data; /* used for object streams */
- size_t stream_length;
- size_t max_length;
- int32_t _flags;
- struct decode_parms decodeparms;
-};
-
-struct pdf_indirect
-{
- pdf_file *pf;
- pdf_obj *obj; /* used when PF == NULL */
- uint32_t label;
- uint16_t generation;
-};
-
-typedef void pdf_null;
-typedef struct pdf_boolean pdf_boolean;
-typedef struct pdf_number pdf_number;
-typedef struct pdf_string pdf_string;
-typedef struct pdf_name pdf_name;
-typedef struct pdf_array pdf_array;
-typedef struct pdf_dict pdf_dict;
-typedef struct pdf_stream pdf_stream;
-typedef struct pdf_indirect pdf_indirect;
-
-typedef struct xref_entry
-{
- uint8_t type; /* object storage type */
- uint32_t field2; /* offset in file or object stream */
- uint16_t field3; /* generation or index */
- pdf_obj *direct; /* used for imported objects */
- pdf_obj *indirect; /* used for imported objects */
-} xref_entry;
-
-struct pdf_file
-{
- FILE *file;
- pdf_obj *trailer;
- xref_entry *xref_table;
- pdf_obj *catalog;
- int num_obj;
- int file_size;
- int version;
-};
-
-static int error_out = 0;
-
-#define OBJSTM_MAX_OBJS 200
-/* the limit is only 100 for linearized PDF */
-
-struct pdf_out {
- struct {
- int enc_mode; /* boolean */
- } state;
-
- unsigned char id1[16];
- unsigned char id2[16];
-
- struct {
- int major;
- int minor;
- } version;
-
- struct {
- struct {
- int level;
- int use_predictor;
- } compression;
-
- int enable_encrypt;
- int use_objstm;
- } options;
-
- struct {
- FILE *file;
- size_t file_position;
- int line_position;
- size_t compression_saved;
- } output;
-
- struct {
- uint32_t next_label;
- uint32_t max_ind_objects;
- } obj;
-
- pdf_sec *sec_data;
-
- pdf_obj *trailer;
- uint32_t startxref;
- xref_entry * xref_table;
-
- pdf_obj *xref_stream;
- pdf_obj *output_stream;
- pdf_obj *current_objstm;
- /* The following flag bits are (8,338,607+1)/8 bytes data
- * each bit represenging if the object is freed.
- * Where the value 8,338,607 is taken from PDF ref. manual, v.1.7,
- * Appendix C, "Implementation Limits".
- */
- char *free_list;
-};
-
-#if defined(LIBDPX)
-size_t output_file_size;
-#endif /* LIBDPX */
-
-/* Underway to reform PDF related code... For a moment place pdf_out
- * object as a static variable. */
-static pdf_out pout;
-
-static pdf_out *
-current_output (void)
-{
- return &pout;
-}
-
-static void
-init_pdf_out_struct (pdf_out *p)
-{
- ASSERT(p);
-
- p->state.enc_mode = 0;
-
- memset(p->id1, 0, 16);
- memset(p->id2, 0, 16);
-
- p->version.major = 1;
- p->version.minor = PDF_VERSION_DEFAULT;
-
- p->options.compression.level = 9;
- p->options.compression.use_predictor = 1;
- p->options.enable_encrypt = 0;
- p->options.use_objstm = 1;
-
- p->output.file = NULL;
- p->output.file_position = 0;
- p->output.line_position = 0;
- p->output.compression_saved = 0;
-#if defined(LIBDPX)
- output_file_size = 0;
-#endif /* LIBDPX */
-
- p->obj.next_label = 1;
- p->obj.max_ind_objects = 0;
-
- p->sec_data = NULL;
- p->trailer = NULL;
- p->startxref = 0;
- p->xref_table = NULL;
-
- p->xref_stream = NULL;
- p->output_stream = NULL;
- p->current_objstm = NULL;
-
- p->free_list = NEW((PDF_NUM_INDIRECT_MAX+1)/8, char);
- memset(p->free_list, 0, (PDF_NUM_INDIRECT_MAX+1)/8);
-}
-
-static void
-clean_pdf_out_struct (pdf_out *p)
-{
- if (p->free_list)
- RELEASE(p->free_list);
- memset(p, 0, sizeof(pdf_out));
-}
-
-/* Internal static routines */
-
-static int check_for_pdf_version (FILE *file);
-
-static void pdf_flush_obj (pdf_out *p, pdf_obj *object);
-static void pdf_label_obj (pdf_out *p, pdf_obj *object);
-static void pdf_write_obj (pdf_out *p, pdf_obj *object);
-
-static void set_objstm_data (pdf_obj *objstm, int *data);
-static int *get_objstm_data (pdf_obj *objstm);
-static void release_objstm (pdf_obj *objstm);
-
-static void pdf_out_char (pdf_out *p, char c);
-static void pdf_out_str (pdf_out *p, const void *buffer, size_t length);
-
-static pdf_obj *pdf_new_ref (pdf_out *p, pdf_obj *object);
-static void release_indirect (pdf_indirect *data);
-static void write_indirect (pdf_out *p, pdf_indirect *indirect);
-
-static void release_boolean (pdf_obj *data);
-static void write_boolean (pdf_out *p, pdf_boolean *data);
-
-static void write_null (pdf_out *p);
-
-static void release_number (pdf_number *number);
-static void write_number (pdf_out *p, pdf_number *number);
-
-static void write_string (pdf_out *p, pdf_string *str);
-static void release_string (pdf_string *str);
-
-static void write_name (pdf_out *p, pdf_name *name);
-static void release_name (pdf_name *name);
-
-static void write_array (pdf_out *p, pdf_array *array);
-static void release_array (pdf_array *array);
-
-static void write_dict (pdf_out *p, pdf_dict *dict);
-static void release_dict (pdf_dict *dict);
-
-static void write_stream (pdf_out *p, pdf_stream *stream);
-static void release_stream (pdf_stream *stream);
-
-static void
-pdf_out_set_compression (pdf_out *p, int level)
-{
- ASSERT(p);
-
-#ifndef HAVE_ZLIB
- ERROR("You don't have compression compiled in. Possibly libz wasn't found by configure.");
-#else
-#ifndef HAVE_ZLIB_COMPRESS2
- if (level != 0)
- WARN("Unable to set compression level -- your zlib doesn't have compress2().");
-#endif
- if (level >= 0 && level <= 9)
- p->options.compression.level = level;
- else {
- ERROR("set_compression: invalid compression level: %d", level);
- }
-#endif /* !HAVE_ZLIB */
-
- return;
-}
-
-FILE *
-pdf_get_output_file (void)
-{
- pdf_out *p = current_output();
- return p->output.file;
-}
-
-static void
-pdf_out_set_version (pdf_out *p, int ver_major, int ver_minor)
-{
- int version;
-
- ASSERT(p);
-
- version = ver_major * 10 + ver_minor;
- /* Don't forget to update CIDFont_stdcc_def[] in cid.c too! */
- if (version >= PDF_VERSION_MIN && version <= PDF_VERSION_MAX) {
- p->version.major = ver_major;
- p->version.minor = ver_minor;
- } else {
- WARN("Unsupported PDF version %d.%d ... Ignoring.", ver_major, ver_minor);
- }
-}
-
-int
-pdf_get_version (void)
-{
- pdf_out *p = current_output();
- return (p->version.major * 10 + p->version.minor);
-}
-
-int
-pdf_get_version_major (void)
-{
- pdf_out *p = current_output();
- return p->version.major;
-}
-
-int
-pdf_get_version_minor (void)
-{
- pdf_out *p = current_output();
- return p->version.minor;
-}
-
-int
-pdf_check_version (int major, int minor)
-{
- pdf_out *p = current_output();
- if (p->version.major > major)
- return 0;
- else if (p->version.major < major)
- return -1;
- else {
- return (p->version.minor >= minor) ? 0 : -1;
- }
-
- return -1;
-}
-
-static void
-add_xref_entry (pdf_out *p,
- uint32_t label, uint8_t type, uint32_t field2, uint16_t field3)
-{
- ASSERT(p);
-
- if (label >= p->obj.max_ind_objects) {
- p->obj.max_ind_objects = (label/IND_OBJECTS_ALLOC_SIZE+1)*IND_OBJECTS_ALLOC_SIZE;
- p->xref_table = RENEW(p->xref_table, p->obj.max_ind_objects, xref_entry);
- }
-
- p->xref_table[label].type = type;
- p->xref_table[label].field2 = field2;
- p->xref_table[label].field3 = field3;
- p->xref_table[label].direct = NULL;
- p->xref_table[label].indirect = NULL;
-}
-
-#define BINARY_MARKER "%\344\360\355\370\n"
-pdf_out *
-pdf_out_init (const char *filename,
- const unsigned char *id1,
- const unsigned char *id2,
- int ver_major, int ver_minor, int compression_level,
- int enable_encrypt,
- int enable_objstm,
- int enable_predictor)
-{
- pdf_out *p = current_output();
- char v;
-
- init_pdf_out_struct(p);
-
- pdf_out_set_version(p, ver_major, ver_minor);
- pdf_out_set_compression(p, compression_level);
-
- add_xref_entry(p, 0, 0, 0, 0xffff);
-
- /* This must be set before pdf_set_root() is called */
- p->options.enable_encrypt = enable_encrypt;
- if (pdf_check_version(1, 5) == 0) {
- if (enable_objstm) {
- p->xref_stream = pdf_new_stream(STREAM_COMPRESS);
- p->xref_stream->flags |= OBJ_NO_ENCRYPT;
- p->trailer = pdf_stream_dict(p->xref_stream);
- pdf_add_dict(p->trailer, pdf_new_name("Type"), pdf_new_name("XRef"));
- p->options.use_objstm = 1;
- } else {
- p->xref_stream = NULL;
- p->trailer = pdf_new_dict();
- p->options.use_objstm = 0;
- }
- } else {
- p->xref_stream = NULL;
- p->trailer = pdf_new_dict();
- p->options.use_objstm = 0;
- }
-
- p->output_stream = NULL;
-
- if (filename == NULL) { /* no filename: writing to stdout */
-#ifdef WIN32
- setmode(fileno(stdout), _O_BINARY);
-#endif
- p->output.file = stdout;
- } else {
- p->output.file = MFOPEN(filename, FOPEN_WBIN_MODE);
- if (!p->output.file) {
- if (strlen(filename) < 128)
- ERROR("Unable to open \"%s\".", filename);
- else
- ERROR("Unable to open file.");
- }
- }
- pdf_out_str(p, "%PDF-", strlen("%PDF-"));
- v = '0' + p->version.major;
- pdf_out_str(p, &v, 1);
- pdf_out_str(p, ".", 1);
- v = '0' + p->version.minor;
- pdf_out_str(p, &v, 1);
- pdf_out_str(p, "\n", 1);
- pdf_out_str(p, BINARY_MARKER, strlen(BINARY_MARKER));
-
- /* Set trailer ID and setup security handler */
- {
- pdf_obj *id_array;
-
- memcpy(p->id1, id1, 16);
- memcpy(p->id2, id2, 16);
- id_array = pdf_new_array();
- pdf_add_array(id_array, pdf_new_string(p->id1, 16));
- pdf_add_array(id_array, pdf_new_string(p->id2, 16));
- pdf_add_dict(p->trailer, pdf_new_name("ID"), id_array);
- }
- p->state.enc_mode = 0;
- p->options.compression.use_predictor = enable_predictor;
-
- return p;
-}
-
-void
-pdf_out_set_encrypt (int keybits, int32_t permission,
- const char *opasswd, const char *upasswd,
- int use_aes, int encrypt_metadata)
-{
- pdf_out *p = current_output();
-
- pdf_obj *encrypt, *extension, *catalog;
-
- p->sec_data = pdf_enc_init(p->id1, keybits, permission,
- opasswd, upasswd, use_aes, encrypt_metadata);
- if (!p->sec_data) {
- p->options.enable_encrypt = 0;
- return;
- }
-
- encrypt = pdf_enc_get_encrypt_dict(p->sec_data);
- pdf_add_dict(p->trailer,
- pdf_new_name("Encrypt"), pdf_ref_obj(encrypt));
- encrypt->flags |= OBJ_NO_ENCRYPT;
- encrypt->flags |= OBJ_NO_OBJSTM;
- pdf_release_obj(encrypt);
-
- extension = pdf_enc_get_extension_dict(p->sec_data);
- if (extension) {
- catalog = pdf_doc_catalog();
- pdf_add_dict(catalog, pdf_new_name("Extensions"), extension);
- }
-}
-
-static void
-dump_xref_table (pdf_out *p)
-{
- int i, length;
- char buf[32];
-
- ASSERT(p);
-
- pdf_out_str(p, "xref\n", 5);
-
- length = sprintf(buf, "%d %u\n", 0, p->obj.next_label);
- pdf_out_str(p, buf, length);
-
- /*
- * 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.
- */
- for (i = 0; i < p->obj.next_label; i++) {
- uint8_t type = p->xref_table[i].type;
- if (type > 1)
- ERROR("object type %hu not allowed in xref table", type);
- length = sprintf(buf, "%010u %05hu %c \n",
- p->xref_table[i].field2, p->xref_table[i].field3,
- type ? 'n' : 'f');
- pdf_out_str(p, buf, length);
- }
-}
-
-static void
-dump_trailer (pdf_out *p)
-{
- ASSERT(p);
-
- pdf_out_str(p, "trailer\n", 8);
- p->state.enc_mode = 0;
- write_dict(p, p->trailer->data);
- pdf_release_obj(p->trailer);
- p->trailer = NULL;
- pdf_out_char(p, '\n');
-}
-
-/*
- * output a PDF 1.5 cross-reference stream;
- * contributed by Matthias Franz (March 21, 2007)
- */
-static void
-dump_xref_stream (pdf_out *p)
-{
- uint32_t pos, i;
- uint32_t poslen;
- unsigned char buf[7] = {0, 0, 0, 0, 0};
- pdf_obj *w;
-
- ASSERT(p);
-
- /* determine the necessary size of the offset field */
- pos = p->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(p->trailer, pdf_new_name("W"), w);
-
- /* We need the xref entry for the xref stream right now */
- add_xref_entry(p, p->obj.next_label - 1, 1, p->startxref, 0);
-
- for (i = 0; i < p->obj.next_label; i++) {
- size_t j;
- uint16_t f3;
- buf[0] = p->xref_table[i].type;
- pos = p->xref_table[i].field2;
- for (j = poslen; j--; ) {
- buf[1+j] = (unsigned char) pos;
- pos >>= 8;
- }
- f3 = p->xref_table[i].field3;
- buf[poslen+1] = (unsigned char) (f3 >> 8);
- buf[poslen+2] = (unsigned char) (f3);
- pdf_add_stream(p->xref_stream, &buf, poslen+3);
- }
-
- pdf_release_obj(p->xref_stream);
- p->xref_stream = NULL;
-}
-
-#if defined(LIBDPX)
-long
-pdf_output_stats (void)
-{
- return (long) output_file_size;
-}
-#endif /* LIBDPX */
-
-void
-pdf_out_flush (void)
-{
- pdf_out *p = current_output();
- char buf[16];
-
- if (p->output.file) {
- int length;
-
- /* Flush current object stream */
- if (p->current_objstm) {
- release_objstm(p->current_objstm);
- p->current_objstm =NULL;
- }
-
- /*
- * Label xref stream - we need the number of correct objects
- * for the xref stream dictionary (= trailer).
- * Labelling it in pdf_out_init (with 1) does not work (why?).
- */
- if (p->xref_stream)
- pdf_label_obj(p, p->xref_stream);
-
- /* Record where this xref is for trailer */
- p->startxref = p->output.file_position;
-
- pdf_add_dict(p->trailer,
- pdf_new_name("Size"), pdf_new_number(p->obj.next_label));
-
- if (p->xref_stream)
- dump_xref_stream(p);
- else {
- dump_xref_table(p);
- dump_trailer(p);
- }
-
- /* Done with xref table */
- RELEASE(p->xref_table);
- p->xref_table = NULL;
-
- pdf_out_str(p, "startxref\n", 10);
- length = sprintf(buf, "%u\n", p->startxref);
- pdf_out_str(p, buf, length);
- pdf_out_str(p, "%%EOF\n", 6);
-
-#if !defined(LIBDPX)
- MESG("\n");
-#endif /* !LIBDPX */
- if (dpx_conf.verbose_level > 0) {
- if (p->options.compression.level > 0) {
- MESG("Compression saved %ld bytes\n", p->output.compression_saved);
- }
- }
-#if !defined(LIBDPX)
- MESG("%ld bytes written", p->output.file_position);
-#else
- output_file_size = p->output.file_position;
-#endif /* !LIBDPX */
-
- MFCLOSE(p->output.file);
- p->output.file = NULL;
- p->output.file_position = 0;
- p->output.line_position = 0;
- }
- if (p->sec_data)
- pdf_enc_close(&p->sec_data);
-#if defined(PDFOBJ_DEBUG)
- {
- int i;
- error_out = 1;
- MESG("\ndebug>> %d PDF objects created.", cur_obj_id);
- for (i = 0; i < cur_obj_id; i++) {
- pdf_obj *obj = bucket[i];
- if (obj) {
- if (obj->label > 0) {
- WARN("Object obj_id=<%lu, %u> unreleased...", obj->label, obj->generation);
- WARN("Reference count=%d", obj->refcount);
- pdf_write_obj(p, obj);
- MESG("\n");
- } else {
- WARN("Unreleased object found: (unlabeled) id=%d", i);
- pdf_write_obj(p, obj);
- MESG("\n");
- }
- }
- }
- }
-#endif
- clean_pdf_out_struct(p);
-}
-
-void
-pdf_error_cleanup (void)
-{
- pdf_out *p = current_output();
- /*
- * This routine is the cleanup required for an abnormal exit.
- * For now, simply close the file.
- */
- if (p->output.file)
- MFCLOSE(p->output.file);
- p->output.file = NULL;
-}
-
-
-void
-pdf_set_root (pdf_obj *object)
-{
- pdf_out *p = current_output();
-
- if (pdf_lookup_dict(p->trailer, "Root"))
- ERROR("Root object already set!");
- pdf_add_dict(p->trailer, pdf_new_name("Root"), pdf_ref_obj(object));
- /* 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 (p->options.enable_encrypt)
- object->flags |= OBJ_NO_OBJSTM;
-}
-
-void
-pdf_set_info (pdf_obj *object)
-{
- pdf_out *p = current_output();
-
- if (pdf_lookup_dict(p->trailer, "Info"))
- ERROR ("Info object already set!");
- pdf_add_dict(p->trailer, pdf_new_name("Info"), pdf_ref_obj(object));
-}
-
-
-
-static void
-pdf_out_char (pdf_out *p, char c)
-{
- ASSERT(p);
-
- if (error_out) {
- fputc(c, stderr);
- } else {
- if (p->output_stream)
- pdf_add_stream(p->output_stream, &c, 1);
- else {
- fputc(c, p->output.file);
- p->output.file_position += 1;
- if (c == '\n')
- p->output.line_position = 0;
- else
- p->output.line_position += 1;
- }
- }
-}
-
-static char xchar[] = "0123456789abcdef";
-
-static void
-pdf_out_xchar (pdf_out *p, char c)
-{
- ASSERT(p);
-
- pdf_out_char(p, xchar[(c >> 4) & 0x0f]);
- pdf_out_char(p, xchar[c & 0x0f]);
-}
-
-static void
-pdf_out_str (pdf_out *p, const void *buffer, size_t length)
-{
- ASSERT(p);
-
- if (error_out)
- fwrite(buffer, 1, length, stderr);
- else {
- if (p->output_stream)
- pdf_add_stream(p->output_stream, buffer, length);
- else {
- fwrite(buffer, 1, length, p->output.file);
- p->output.file_position += length;
- p->output.line_position += length;
- /* "foo\nbar\n "... */
- if (length > 0 &&
- ((const char *)buffer)[length-1] == '\n')
- p->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 (pdf_out *p)
-{
- ASSERT(p);
-
- if (p->output.line_position >= 80) {
- pdf_out_char(p, '\n');
- } else {
- pdf_out_char(p, ' ');
- }
-}
-
-#define TYPECHECK(o,t) if (!(o) || (o)->type != (t)) {\
- ERROR("typecheck: Invalid object type: %d %d (line %d)", (o) ? (o)->type : -1, (t), __LINE__);\
-}
-
-#define INVALIDOBJ(o) ((o) == NULL || (o)->type <= 0 || (o)->type > PDF_UNDEFINED)
-
-static pdf_obj *
-pdf_new_obj(int type)
-{
- pdf_obj *result;
-
- if (type > PDF_UNDEFINED || type < 0)
- ERROR("Invalid object type: %d", type);
-
- result = NEW(1, pdf_obj);
- result->type = type;
- result->data = NULL;
- result->label = 0;
- result->generation = 0;
- result->refcount = 1;
- result->flags = 0;
-
-#if defined(PDFOBJ_DEBUG)
- result->obj_id = cur_obj_id;
- bucket[cur_obj_id] = result;
- cur_obj_id++;
-#endif
-
- return result;
-}
-
-int
-pdf_obj_typeof (pdf_obj *object)
-{
- if (INVALIDOBJ(object))
- return PDF_OBJ_INVALID;
-
- return object->type;
-}
-
-static void
-pdf_label_obj (pdf_out *p, pdf_obj *object)
-{
- ASSERT(p);
-
- if (INVALIDOBJ(object))
- ERROR("pdf_label_obj(): passed invalid object.");
-
- /*
- * Don't change label on an already labeled object. Ignore such calls.
- */
- if (object->label == 0) {
- if (p->obj.next_label == PDF_NUM_INDIRECT_MAX) {
- ERROR("Number of indirect object has reached its maximum value!");
- }
- object->label = p->obj.next_label++;
- object->generation = 0;
- }
-}
-
-/*
- * Transfer the label assigned to the object src to the object dst.
- * The object dst must not yet have been labeled.
- */
-void
-pdf_transfer_label (pdf_obj *dst, pdf_obj *src)
-{
- ASSERT(dst && !dst->label && src);
-
- dst->label = src->label;
- dst->generation = src->generation;
- src->label = 0;
- src->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 (INVALIDOBJ(object))
- ERROR("pdf_link_obj(): passed invalid object.");
-
- object->refcount += 1;
-
- return object;
-}
-
-
-pdf_obj *
-pdf_ref_obj (pdf_obj *object)
-{
- pdf_out *p = current_output();
-
- if (INVALIDOBJ(object))
- ERROR("pdf_ref_obj(): passed invalid object.");
-
- if (object->refcount == 0) {
- MESG("\nTrying to refer already released object!!!\n");
- error_out = 1;
- pdf_write_obj(p, object);
- ERROR("Cannot continue...");
- error_out = 0;
- }
-
- if (PDF_OBJ_INDIRECTTYPE(object)) {
- return pdf_link_obj(object);
- } else {
- return pdf_new_ref(p, object);
- }
-}
-
-static void
-release_indirect (pdf_indirect *data)
-{
- RELEASE(data);
-}
-
-static void
-write_indirect (pdf_out *p, pdf_indirect *indirect)
-{
- int length;
- char buf[64];
-
- ASSERT(p);
- ASSERT(!indirect->pf);
-
- length = sprintf(buf, "%u %hu R",
- indirect->label, indirect->generation);
- pdf_out_str(p, buf, length);
-}
-
-/* The undefined object is used as a placeholder in pdfnames.c
- * for objects which are referenced before they are defined.
- */
-pdf_obj *
-pdf_new_undefined (void)
-{
- pdf_obj *result;
-
- result = pdf_new_obj(PDF_UNDEFINED);
- result->data = NULL;
-
- return result;
-}
-
-pdf_obj *
-pdf_new_null (void)
-{
- pdf_obj *result;
-
- result = pdf_new_obj(PDF_NULL);
- result->data = NULL;
-
- return result;
-}
-
-static void
-write_null (pdf_out *p)
-{
- pdf_out_str(p, "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);
- data->value = value;
- result->data = data;
-
- return result;
-}
-
-static void
-release_boolean (pdf_obj *data)
-{
- RELEASE (data);
-}
-
-static void
-write_boolean (pdf_out *p, pdf_boolean *data)
-{
- if (data->value) {
- pdf_out_str(p, "true", 4);
- } else {
- pdf_out_str(p, "false", 5);
- }
-}
-
-char
-pdf_boolean_value (pdf_obj *object)
-{
- pdf_boolean *data;
-
- TYPECHECK(object, PDF_BOOLEAN);
-
- data = object->data;
-
- return data->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);
- data->value = value;
- result->data = data;
-
- return result;
-}
-
-static void
-release_number (pdf_number *data)
-{
- RELEASE (data);
-}
-
-static void
-write_number (pdf_out *p, pdf_number *number)
-{
- int count;
- char buf[512];
-
- count = pdf_sprint_number(buf, number->value);
-
- pdf_out_str(p, buf, count);
-}
-
-
-void
-pdf_set_number (pdf_obj *object, double value)
-{
- pdf_number *data;
-
- TYPECHECK(object, PDF_NUMBER);
-
- data = object->data;
- data->value = value;
-}
-
-double
-pdf_number_value (pdf_obj *object)
-{
- pdf_number *data;
-
- TYPECHECK(object, PDF_NUMBER);
-
- data = object->data;
-
- return data->value;
-}
-
-pdf_obj *
-pdf_new_string (const void *str, unsigned length)
-{
- pdf_obj *result;
- pdf_string *data;
-
- ASSERT(str);
-
- result = pdf_new_obj(PDF_STRING);
- data = NEW(1, pdf_string);
- result->data = data;
- data->length = length;
-
- if (length) {
- data->string = NEW(length+1, unsigned char);
- memcpy(data->string, str, length);
- /* Shouldn't assume NULL terminated. */
- data->string[length] = '\0';
- } else
- data->string = NULL;
-
- return result;
-}
-
-void *
-pdf_string_value (pdf_obj *object)
-{
- pdf_string *data;
-
- TYPECHECK(object, PDF_STRING);
-
- data = object->data;
-
- return data->string;
-}
-
-unsigned
-pdf_string_length (pdf_obj *object)
-{
- pdf_string *data;
-
- TYPECHECK(object, PDF_STRING);
-
- data = object->data;
-
- return (unsigned) data->length;
-}
-
-/*
- * This routine escapes non printable characters and control
- * characters in an output string.
- */
-int
-pdfobj_escape_str (char *buffer, int bufsize, const unsigned char *s, int len)
-{
- size_t result = 0;
- size_t i;
-
- for (i = 0; i < len; i++) {
- unsigned char ch;
-
- ch = s[i];
- if (result > bufsize - 4)
- ERROR("pdfobj_escape_str: Buffer overflow");
-
- /*
- * We always write three octal digits. Optimization only gives few Kb
- * smaller size for most documents when zlib compressed.
- */
- if (ch < 32 || ch > 126) {
- buffer[result++] = '\\';
-#if 0
- if (i < len - 1 && !isdigit(s[i+1]))
- result += sprintf(buffer+result, "%o", ch);
- else
- result += sprintf(buffer+result, "%03o", ch);
-#endif
- result += sprintf(buffer+result, "%03o", ch);
- } else {
- 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 (int) result;
-}
-
-static void
-write_string (pdf_out *p, pdf_string *str)
-{
- unsigned char *s = NULL;
- size_t i, nescc = 0;
- size_t len = 0;
-
- ASSERT(p);
-
- if (p->state.enc_mode) {
- pdf_encrypt_data(p->sec_data, str->string, str->length, &s, &len);
- } else {
- s = str->string;
- len = str->length;
- }
-
- /*
- * Count all ASCII non-printable characters.
- */
- for (i = 0; i < len; i++) {
- if (!isprint(s[i]))
- nescc++;
- }
- /*
- * If the string contains much escaped chars, then we write it as
- * ASCII hex string.
- */
- if (nescc > len / 3) {
- pdf_out_char(p, '<');
- for (i = 0; i < len; i++) {
- pdf_out_xchar(p, s[i]);
- }
- pdf_out_char(p, '>');
- } else {
- char *buf;
- size_t size, count;
-
- /* At most len/3 is to be escaped here. (see above)
- * Thus 4*len/3 + 2*len/3 + 2 is enough for buffer size.
- */
- size = len * 2 + 3;
- buf = NEW(size, char);
-
- pdf_out_char(p, '(');
- /*
- * 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 < len; i++) {
- count = pdfobj_escape_str(buf, size, &(s[i]), 1);
- pdf_out_str(p, buf, count);
- }
- pdf_out_char(p, ')');
- RELEASE(buf);
- }
-
- if (p->state.enc_mode && s)
- RELEASE(s);
-}
-
-static void
-release_string (pdf_string *data)
-{
- if (data->string != NULL) {
- RELEASE(data->string);
- data->string = NULL;
- }
- RELEASE(data);
-}
-
-void
-pdf_set_string (pdf_obj *object, unsigned char *str, unsigned length)
-{
- pdf_string *data;
-
- TYPECHECK(object, PDF_STRING);
-
- data = object->data;
- if (data->string != 0) {
- RELEASE(data->string);
- }
- if (length != 0) {
- data->length = length;
- data->string = NEW(length + 1, unsigned char);
- memcpy(data->string, str, length);
- data->string[length] = '\0';
- } else {
- data->length = 0;
- data->string = NULL;
- }
-}
-
-/* Name does *not* include the /. */
-pdf_obj *
-pdf_new_name (const char *name)
-{
- pdf_obj *result;
- size_t length;
- pdf_name *data;
-
- result = pdf_new_obj(PDF_NAME);
- data = NEW (1, pdf_name);
- result->data = data;
- length = strlen(name);
- 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 (pdf_out *p, pdf_name *name)
-{
- char *s;
- size_t i, length;
-
- ASSERT(p);
-
- s = name->name;
- length = name->name ? strlen(name->name) : 0;
- /*
- * From PDF Reference, 3rd ed., p.33:
- *
- * Beginning with PDF 1.2, any character except null (character code 0)
- * may be included in a name by writing its 2-digit hexadecimal code,
- * preceded bythe number sign character (#); see implementation notes 3
- * and 4 in Appendix H. This syntax is required in order to represent
- * any of the delimiter or white-space characters or the number sign
- * character itself; it is recommended but not required for characters
- * whose codes are outside the range 33 (!) to 126 (~).
- */
-#ifndef is_delim
- /* Avoid '{' and '}' for PostScript compatibility? */
-#define is_delim(c) ((c) == '(' || (c) == ')' || \
- (c) == '/' || \
- (c) == '<' || (c) == '>' || \
- (c) == '[' || (c) == ']' || \
- (c) == '{' || (c) == '}' || \
- (c) == '%')
-#endif
- pdf_out_char(p, '/');
- for (i = 0; i < length; i++) {
- if (s[i] < '!' || s[i] > '~' || s[i] == '#' || is_delim(s[i])) {
- /* ^ "space" is here. */
- pdf_out_char (p, '#');
- pdf_out_xchar(p, s[i]);
- } else {
- pdf_out_char (p, s[i]);
- }
- }
-}
-
-static void
-release_name (pdf_name *data)
-{
- if (data->name != NULL) {
- RELEASE(data->name);
- data->name = NULL;
- }
- RELEASE(data);
-}
-
-char *
-pdf_name_value (pdf_obj *object)
-{
- pdf_name *data;
-
- TYPECHECK(object, PDF_NAME);
-
- data = object->data;
-
- return data->name;
-}
-
-/*
- * We do not have pdf_name_length() since '\0' is not allowed
- * in PDF name object.
- */
-
-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 (pdf_out *p, pdf_array *array)
-{
- ASSERT(p);
-
- pdf_out_char(p, '[');
- if (array->size > 0) {
- size_t i;
- int type1 = PDF_UNDEFINED, type2;
-
- for (i = 0; i < array->size; i++) {
- if (array->values[i]) {
- type2 = array->values[i]->type;
- if (type1 != PDF_UNDEFINED && pdf_need_white(type1, type2))
- pdf_out_white(p);
- type1 = type2;
- pdf_write_obj(p, array->values[i]);
- } else
- WARN("PDF array element %ld undefined.", i);
- }
- }
- pdf_out_char(p, ']');
-}
-
-pdf_obj *
-pdf_get_array (pdf_obj *array, int idx)
-{
- pdf_obj *result = NULL;
- pdf_array *data;
-
- TYPECHECK(array, PDF_ARRAY);
-
- data = array->data;
- if (idx < 0)
- result = data->values[idx + data->size];
- else if (idx < data->size) {
- result = data->values[idx];
- }
-
- return result;
-}
-
-unsigned
-pdf_array_length (pdf_obj *array)
-{
- pdf_array *data;
-
- TYPECHECK(array, PDF_ARRAY);
-
- data = (pdf_array *) array->data;
-
- return (unsigned) data->size;
-}
-
-static void
-release_array (pdf_array *data)
-{
- size_t i;
-
- if (data->values) {
- for (i = 0; i < data->size; i++) {
- pdf_release_obj(data->values[i]);
- data->values[i] = NULL;
- }
- RELEASE(data->values);
- data->values = NULL;
- }
- RELEASE(data);
-}
-
-/*
- * The name pdf_add_array is misleading. It behaves differently than
- * pdf_add_dict(). This should be pdf_push_array().
- */
-void
-pdf_add_array (pdf_obj *array, pdf_obj *object)
-{
- pdf_array *data;
-
- TYPECHECK(array, PDF_ARRAY);
-
- 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;
- data->size++;
-
- return;
-}
-
-#if 0
-void
-pdf_put_array (pdf_obj *array, int idx, pdf_obj *object)
-{
- pdf_array *data;
- int i;
-
- TYPECHECK(array, PDF_ARRAY);
-
- data = array->data;
- if (idx + 1 > data->max) {
- data->max += ARRAY_ALLOC_SIZE;
- data->values = RENEW(data->values, data->max, pdf_obj *);
- }
- /*
- * Rangecheck error in PostScript interpreters if
- * idx > data->size - 1. But pdf_new_array() doesn't set
- * array size, pdf_add_array() dynamically increases size
- * of array. This might confusing...
- */
- if (idx + 1 > data->size) {
- for (i = data->size; i < idx; i++)
- data->values[i] = pdf_new_null(); /* release_array() won't work without this */
- data->values[idx] = object;
- data->size = idx + 1;
- } else {
- if (data->values[idx])
- pdf_release_obj(data->values[idx]);
- data->values[idx] = object;
- }
-}
-
-/* Easily leaks memory... */
-pdf_obj *
-pdf_shift_array (pdf_obj *array)
-{
- pdf_obj *result = NULL;
- pdf_array *data;
-
- TYPECHECK(array, PDF_ARRAY);
-
- data = array->data;
- if (data->size > 0) {
- size_t i;
-
- result = data->values[0];
- for (i = 1; i < data->size; i++)
- data->values[i-1] = data->values[i];
- data->size--;
- }
-
- return result;
-}
-#endif
-
-/* Prepend an object to an array */
-static void
-pdf_unshift_array (pdf_obj *array, pdf_obj *object)
-{
- pdf_array *data;
-
- TYPECHECK(array, PDF_ARRAY);
-
- data = array->data;
- if (data->size >= data->max) {
- data->max += ARRAY_ALLOC_SIZE;
- data->values = RENEW(data->values, data->max, pdf_obj *);
- }
- memmove(&data->values[1], data->values, data->size * sizeof(pdf_obj *));
- data->values[0] = object;
- data->size++;
-}
-
-#if 0
-pdf_obj *
-pdf_pop_array (pdf_obj *array)
-{
- pdf_obj *result;
- pdf_array *data;
-
- TYPECHECK(array, PDF_ARRAY);
-
- data = array->data;
- if (data->size > 0) {
- result = data->values[data->size - 1];
- data->size--;
- } else {
- result = NULL;
- }
-
- return result;
-}
-#endif
-
-static void
-write_dict (pdf_out *p, pdf_dict *dict)
-{
-#if 0
- pdf_out_str (p, "<<\n", 3); /* dropping \n saves few kb. */
-#else
- pdf_out_str(p, "<<", 2);
-#endif
- while (dict->key != NULL) {
- pdf_write_obj(p, dict->key);
- if (pdf_need_white(PDF_NAME, (dict->value)->type)) {
- pdf_out_white(p);
- }
- pdf_write_obj(p, dict->value);
-#if 0
- pdf_out_char (file, '\n'); /* removing this saves few kb. */
-#endif
- dict = dict->next;
- }
- pdf_out_str(p, ">>", 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);
- data->key = NULL;
- data->value = NULL;
- next = data->next;
- RELEASE(data);
- data = next;
- }
- RELEASE(data);
-}
-
-/* Array is ended by a node with NULL this pointer */
-/* 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, *new_node;
-
- TYPECHECK(dict, PDF_DICT);
- TYPECHECK(key, PDF_NAME);
-
- /* It seems that NULL is sometimes used for null object... */
- if (value != NULL && INVALIDOBJ(value))
- ERROR("pdf_add_dict(): Passed invalid value");
-
- /* If this key already exists, simply replace the value */
- 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;
- return 1;
- }
- }
- /*
- * We didn't find the key. We build a new "end" node and add
- * the new key just before the end
- */
- 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;
-}
-
-#if 0
-void
-pdf_put_dict (pdf_obj *dict, const char *key, pdf_obj *value)
-{
- pdf_dict *data;
-
- TYPECHECK(dict, PDF_DICT);
-
- if (!key) {
- ERROR("pdf_put_dict(): Passed invalid key.");
- }
- /* It seems that NULL is sometimes used for null object... */
- if (value != NULL && INVALIDOBJ(value)) {
- ERROR("pdf_add_dict(): Passed invalid value.");
- }
-
- data = dict->data;
-
- while (data->key != NULL) {
- if (!strcmp(key, pdf_name_value(data->key))) {
- pdf_release_obj(data->value);
- 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) {
- 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 = pdf_new_name(key);
- data->value = value;
- }
-}
-#endif
-
-/* 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;
-
- TYPECHECK(dict1, PDF_DICT);
- TYPECHECK(dict2, PDF_DICT);
-
- data = dict2->data;
- while (data->key != NULL) {
- pdf_add_dict(dict1, pdf_link_obj(data->key), pdf_link_obj(data->value));
- data = data->next;
- }
-}
-
-int
-pdf_foreach_dict (pdf_obj *dict,
- int (*proc) (pdf_obj *, pdf_obj *, void *), void *pdata)
-{
- int error = 0;
- pdf_dict *data;
-
- ASSERT(proc);
-
- TYPECHECK(dict, PDF_DICT);
-
- data = dict->data;
- while (!error &&
- data->key != NULL) {
- error = proc(data->key, data->value, pdata);
- data = data->next;
- }
-
- return error;
-}
-
-#define pdf_match_name(o,s) ((o) && (s) && !strcmp(((pdf_name *)(o)->data)->name, (s)))
-pdf_obj *
-pdf_lookup_dict (pdf_obj *dict, const char *name)
-{
- pdf_dict *data;
-
- ASSERT(name);
-
- TYPECHECK(dict, PDF_DICT);
-
- data = dict->data;
- while (data->key != NULL) {
- if (!strcmp(name, pdf_name_value(data->key))) {
- return data->value;
- }
- data = data->next;
- }
-
- return NULL;
-}
-
-/* Returns array of dictionary keys */
-pdf_obj *
-pdf_dict_keys (pdf_obj *dict)
-{
- pdf_obj *keys;
- pdf_dict *data;
-
- TYPECHECK(dict, PDF_DICT);
-
- keys = pdf_new_array();
- for (data = dict->data; (data &&
- data->key != NULL); data = data->next) {
- /* We duplicate name object rather than linking keys.
- * If we forget to free keys, broken PDF is generated.
- */
- pdf_add_array(keys, pdf_new_name(pdf_name_value(data->key)));
- }
-
- return keys;
-}
-
-void
-pdf_remove_dict (pdf_obj *dict, const char *name)
-{
- pdf_dict *data, **data_p;
-
- TYPECHECK(dict, PDF_DICT);
-
- data = dict->data;
- data_p = (pdf_dict **) (void *) &(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;
- }
-}
-
-pdf_obj *
-pdf_new_stream (int flags)
-{
- pdf_obj *result;
- pdf_stream *data;
-
- result = pdf_new_obj(PDF_STREAM);
- data = NEW(1, pdf_stream);
- /*
- * 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->dict = pdf_new_dict();
- data->_flags = flags;
- data->stream = NULL;
- data->stream_length = 0;
- data->max_length = 0;
- data->objstm_data = NULL;
-
- data->decodeparms.predictor = 2;
- data->decodeparms.columns = 0;
- data->decodeparms.bits_per_component = 0;
- data->decodeparms.colors = 0;
-
- result->data = data;
- result->flags |= OBJ_NO_OBJSTM;
-
- return result;
-}
-
-void
-pdf_stream_set_predictor (pdf_obj *stream,
- int predictor, int32_t columns, int bpc, int colors)
-{
- struct pdf_stream *data;
-
- if (pdf_obj_typeof(stream) != PDF_STREAM)
- return;
- else if (columns < 0 || bpc < 0 || colors < 0)
- return;
-
- data = (struct pdf_stream *) stream->data;
- data->decodeparms.predictor = predictor;
- data->decodeparms.columns = columns;
- data->decodeparms.bits_per_component = bpc;
- data->decodeparms.colors = colors;
- data->_flags |= STREAM_USE_PREDICTOR;
-}
-
-/* Adaptive PNG filter
- * We use the "minimum sum of absolute differences" heuristic approach
- * for finding the most optimal filter to be used.
- *
- * From http://www.libpng.org/pub/png/book/chapter09.html
- *
- * For grayscale and truecolor images of 8 or more bits per sample, with or
- * without alpha channels, dynamic filtering is almost always beneficial. The
- * approach that has by now become standard is known as the minimum sum of
- * absolute differences heuristic and was first proposed by Lee Daniel
- * Crocker in February 1995.
- */
-static unsigned char *
-filter_PNG15_apply_filter (unsigned char *raster,
- int32_t columns, int32_t rows,
- int8_t bpc, int8_t colors, int32_t *length)
-{
- unsigned char *dst;
- int bits_per_pixel = colors * bpc;
- int bytes_per_pixel = (bits_per_pixel + 7) / 8;
- int32_t rowbytes = columns * bytes_per_pixel;
- int32_t i, j;
-
- ASSERT(raster && length);
-
- /* Result */
- dst = NEW((rowbytes+1)*rows, unsigned char);
- *length = (rowbytes + 1) * rows;
-
- for (j = 0; j < rows; j++) {
- int type = 0;
- unsigned char *pp = dst + j * (rowbytes + 1);
- unsigned char *p = raster + j * rowbytes;
- uint32_t sum[5] = {0, 0, 0, 0, 0};
- /* First calculated sum of values to make a heuristic guess
- * of optimal predictor function.
- */
- for (i = 0; i < rowbytes; i++) {
- int left = (i - bytes_per_pixel >= 0) ? p[i - bytes_per_pixel] : 0;
- int up = (j > 0) ? *(p+i-rowbytes) : 0;
- int uplft = (j > 0) ?
- ((i - bytes_per_pixel >= 0) ?
- *(p+i-rowbytes-bytes_per_pixel) : 0) : 0;
- /* Type 0 -- None */
- sum[0] += p[i];
- /* Type 1 -- Sub */
- sum[1] += abs((int) p[i] - left);
- /* Type 2 -- Up */
- sum[2] += abs((int) p[i] - up);
- /* Type 3 -- Average */
- {
- int tmp = floor((up + left) / 2);
- sum[3] += abs((int) p[i] - tmp);
- }
- /* Type 4 -- Peath */
- {
- int q = left + up - uplft;
- int qa = abs(q - left), qb = abs(q - up), qc = abs(q - uplft);
- if (qa <= qb && qa <= qc)
- sum[4] += abs((int) p[i] - left);
- else if (qb <= qc)
- sum[4] += abs((int) p[i] - up);
- else
- sum[4] += abs((int) p[i] - uplft);
- }
- }
- {
- int min = sum[0], min_idx = 0;
- for (i = 0; i < 5; i++) {
- if (sum[i] < min) {
- min = sum[i]; min_idx = i;
- }
- }
- type = min_idx;
- }
- /* Now we actually apply filter. */
- pp[0] = type;
- switch (type) {
- case 0:
- memcpy(pp+1, p, rowbytes);
- break;
- case 1:
- for (i = 0; i < rowbytes; i++) {
- int left = (i - bytes_per_pixel >= 0) ? p[i - bytes_per_pixel] : 0;
- pp[i+1] = p[i] - left;
- }
- break;
- case 2:
- for (i = 0; i < rowbytes; i++) {
- int up = (j > 0) ? *(p+i - rowbytes) : 0;
- pp[i+1] = p[i] - up;
- }
- break;
- case 3:
- {
- for (i = 0; i < rowbytes; i++) {
- int up = (j > 0) ? *(p+i-rowbytes) : 0;
- int left = (i - bytes_per_pixel >= 0) ? p[i - bytes_per_pixel] : 0;
- int tmp = floor((up + left) / 2);
- pp[i+1] = p[i] - tmp;
- }
- }
- break;
- case 4: /* Peath */
- {
- for (i = 0; i < rowbytes; i++) {
- int up = (j > 0) ? *(p+i-rowbytes) : 0;
- int left = (i - bytes_per_pixel >= 0) ? p[i - bytes_per_pixel] : 0;
- int uplft = (j > 0) ?
- ((i - bytes_per_pixel >= 0) ?
- *(p+i-rowbytes-bytes_per_pixel) : 0) : 0;
- int q = left + up - uplft;
- int qa = abs(q - left), qb = abs(q - up), qc = abs(q - uplft);
- if (qa <= qb && qa <= qc)
- pp[i+1] = p[i] - left;
- else if (qb <= qc)
- pp[i+1] = p[i] - up;
- else
- pp[i+1] = p[i] - uplft;
- }
- }
- break;
- }
- }
-
- return dst;
-}
-
-/* TIFF predictor filter support
- * This modifies "raster" itself!
- */
-static void
-apply_filter_TIFF2_1_2_4 (unsigned char *raster,
- int32_t width, int32_t height,
- int8_t bpc, int8_t num_comp)
-{
- int32_t rowbytes = (bpc * num_comp * width + 7) / 8;
- uint8_t mask = (1 << bpc) - 1;
- uint16_t *prev;
- int32_t i, j;
-
- ASSERT(raster);
- ASSERT( bpc > 0 && bpc <= 8 );
-
- prev = NEW(num_comp, uint16_t);
-
- /* Generic routine for 1 to 16 bit.
- * It supports, e.g., 7 bpc images too.
- * Actually, it is not necessary to have 16 bit inbuf and outbuf
- * since we only need 1, 2, and 4 bit support here. 8 bit is enough.
- */
- for (j = 0; j < height; j++) {
- int32_t k, l, inbits, outbits;
- uint16_t inbuf, outbuf;
- int c;
-
- memset(prev, 0, sizeof(uint16_t)*num_comp);
- inbuf = outbuf = 0; inbits = outbits = 0;
- l = k = j * rowbytes;
- for (i = 0; i < width; i++) {
- for (c = 0; c < num_comp; c++) {
- uint8_t cur;
- int8_t sub;
- if (inbits < bpc) { /* need more byte */
- inbuf = (inbuf << 8) | raster[l]; l++;
- inbits += 8;
- }
- cur = (inbuf >> (inbits - bpc)) & mask;
- inbits -= bpc; /* consumed bpc bits */
- sub = cur - prev[c];
- prev[c] = cur;
- if (sub < 0)
- sub += (1 << bpc);
- /* Append newly filtered component value */
- outbuf = (outbuf << bpc) | sub;
- outbits += bpc;
- /* flush */
- if (outbits >= 8) {
- raster[k] = (outbuf >> (outbits - 8)); k++;
- outbits -= 8;
- }
- }
- }
- if (outbits > 0) {
- raster[k] = (outbuf << (8 - outbits));
- k++;
- }
- }
- RELEASE(prev);
-}
-
-static unsigned char *
-filter_TIFF2_apply_filter (unsigned char *raster,
- int32_t columns, int32_t rows,
- int8_t bpc, int8_t colors, int32_t *length)
-{
- unsigned char *dst;
- uint16_t *prev;
- int32_t rowbytes = (bpc * colors * columns + 7) / 8;
- int32_t i, j;
-
- ASSERT(raster && length);
-
- dst = NEW(rowbytes*rows, unsigned char);
- memcpy(dst, raster, rowbytes*rows);
- *length = rowbytes * rows;
-
- switch (bpc) {
- case 1: case 2: case 4:
- apply_filter_TIFF2_1_2_4(dst, columns, rows, bpc, colors);
- break;
-
- case 8:
- prev = NEW(colors, uint16_t);
- for (j = 0; j < rows; j++) {
- memset(prev, 0, sizeof(uint16_t)*colors);
- for (i = 0; i < columns; i++) {
- int c;
- int32_t pos = colors * (columns * j + i);
- for (c = 0; c < colors; c++) {
- uint8_t cur = raster[pos+c];
- int32_t sub = cur - prev[c];
- prev[c] = cur;
- dst[pos+c] = sub;
- }
- }
- }
- RELEASE(prev);
- break;
-
- case 16:
- prev = NEW(colors, uint16_t);
- for (j = 0; j < rows; j++) {
- memset(prev, 0, sizeof(uint16_t)*colors);
- for (i = 0; i < columns; i++) {
- int c;
- int32_t pos = 2 * colors * (columns * j + i);
- for (c = 0; c < colors; c++) {
- uint16_t cur = ((uint8_t)raster[pos+2*c])*256 +
- (uint8_t)raster[pos+2*c+1];
- uint16_t sub = cur - prev[c];
- prev[c] = cur;
- dst[pos+2*c ] = (sub >> 8) & 0xff;
- dst[pos+2*c+1] = sub & 0xff;
- }
- }
- }
- RELEASE(prev);
- break;
-
- }
-
- return dst;
-}
-
-static pdf_obj *
-filter_create_predictor_dict (int predictor, int32_t columns,
- int bpc, int colors)
-{
- pdf_obj *parms;
-
- parms = pdf_new_dict();
- pdf_add_dict(parms, pdf_new_name("BitsPerComponent"), pdf_new_number(bpc));
- pdf_add_dict(parms, pdf_new_name("Colors"), pdf_new_number(colors));
- pdf_add_dict(parms, pdf_new_name("Columns"), pdf_new_number(columns));
- pdf_add_dict(parms, pdf_new_name("Predictor"), pdf_new_number(predictor));
-
- return parms;
-}
-
-static void
-write_stream (pdf_out *p, pdf_stream *stream)
-{
- unsigned char *filtered;
- size_t filtered_length;
-#ifdef HAVE_ZLIB
- uLong buffer_length;
-#else
- size_t buffer_length;
-#endif
- unsigned char *buffer;
-
- ASSERT(p);
-
- /*
- * Always work from a copy of the stream. All filters read from
- * "filtered" and leave their result in "filtered".
- */
-#if 0
- filtered = NEW(stream->stream_length + 1, unsigned char);
-#endif
- filtered = NEW(stream->stream_length, unsigned char);
- memcpy(filtered, stream->stream, stream->stream_length);
- filtered_length = stream->stream_length;
-
- /* PDF/A requires Metadata to be not filtered. */
- {
- pdf_obj *type;
- type = pdf_lookup_dict(stream->dict, "Type");
- if (type && !strcmp("Metadata", pdf_name_value(type))) {
- stream->_flags &= ~STREAM_COMPRESS;
- }
- }
-
-#ifdef HAVE_ZLIB
- /* Apply compression filter if requested */
- if (stream->stream_length > 0 &&
- (stream->_flags & STREAM_COMPRESS) &&
- p->options.compression.level > 0) {
- pdf_obj *filters;
-
- /* First apply predictor filter if requested. */
- if ( p->options.compression.use_predictor &&
- (stream->_flags & STREAM_USE_PREDICTOR) &&
- !pdf_lookup_dict(stream->dict, "DecodeParms")) {
- int bits_per_pixel = stream->decodeparms.colors *
- stream->decodeparms.bits_per_component;
- int32_t len = (stream->decodeparms.columns * bits_per_pixel + 7) / 8;
- int32_t rows = stream->stream_length / len;
- unsigned char *filtered2 = NULL;
- int32_t length2 = stream->stream_length;
- pdf_obj *parms;
-
- parms = filter_create_predictor_dict(stream->decodeparms.predictor,
- stream->decodeparms.columns,
- stream->decodeparms.bits_per_component,
- stream->decodeparms.colors);
-
- switch (stream->decodeparms.predictor) {
- case 2: /* TIFF2 */
- filtered2 = filter_TIFF2_apply_filter(filtered,
- stream->decodeparms.columns,
- rows,
- stream->decodeparms.bits_per_component,
- stream->decodeparms.colors, &length2);
- break;
- case 15: /* PNG optimun */
- filtered2 = filter_PNG15_apply_filter(filtered,
- stream->decodeparms.columns,
- rows,
- stream->decodeparms.bits_per_component,
- stream->decodeparms.colors, &length2);
- break;
- default:
- WARN("Unknown/unsupported Predictor function %d.",
- stream->decodeparms.predictor);
- break;
- }
- if (parms && filtered2) {
- RELEASE(filtered);
- filtered = filtered2;
- filtered_length = length2;
- pdf_add_dict(stream->dict, pdf_new_name("DecodeParms"), parms);
- }
- }
-
- filters = pdf_lookup_dict(stream->dict, "Filter");
-
- buffer_length = filtered_length + filtered_length/1000 + 14;
- buffer = NEW(buffer_length, unsigned char);
- {
- pdf_obj *filter_name = pdf_new_name("FlateDecode");
-
- 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,
- filtered_length, p->options.compression.level)) {
- ERROR("Zlib error");
- }
-#else
- if (compress(buffer, &buffer_length, filtered,
- filtered_length)) {
- ERROR ("Zlib error");
- }
-#endif /* HAVE_ZLIB_COMPRESS2 */
- RELEASE(filtered);
- p->output.compression_saved +=
- filtered_length - buffer_length
- - (filters ? strlen("/FlateDecode "): strlen("/Filter/FlateDecode\n"));
-
- filtered = buffer;
- filtered_length = buffer_length;
- }
-#endif /* HAVE_ZLIB */
-
- /* AES will change the size of data! */
- if (p->state.enc_mode) {
- unsigned char *cipher = NULL;
- size_t cipher_len = 0;
- pdf_encrypt_data(p->sec_data, filtered, filtered_length, &cipher, &cipher_len);
- RELEASE(filtered);
- filtered = cipher;
- filtered_length = cipher_len;
- }
-
- #if 0
- /*
- * An optional end-of-line marker preceding the "endstream" is
- * not part of stream data. See, PDF Reference 4th ed., p. 38.
- */
- /* Add a '\n' if the last character wasn't one */
- if (filtered_length > 0 &&
- filtered[filtered_length-1] != '\n') {
- filtered[filtered_length] = '\n';
- filtered_length++;
- }
-#endif
-
- pdf_add_dict(stream->dict,
- pdf_new_name("Length"), pdf_new_number(filtered_length));
-
- pdf_write_obj(p, stream->dict);
-
- pdf_out_str(p, "\nstream\n", 8);
-
- if (filtered_length > 0)
- pdf_out_str(p, 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_str(p, "\n", 1);
- pdf_out_str(p, "endstream", 9);
-}
-
-static void
-release_stream (pdf_stream *stream)
-{
- pdf_release_obj(stream->dict);
- stream->dict = NULL;
-
- if (stream->stream) {
- RELEASE(stream->stream);
- stream->stream = NULL;
- }
-
- if (stream->objstm_data) {
- RELEASE(stream->objstm_data);
- stream->objstm_data = NULL;
- }
-
- RELEASE(stream);
-}
-
-pdf_obj *
-pdf_stream_dict (pdf_obj *stream)
-{
- pdf_stream *data;
-
- TYPECHECK(stream, PDF_STREAM);
-
- data = stream->data;
-
- return data->dict;
-}
-
-const void *
-pdf_stream_dataptr (pdf_obj *stream)
-{
- pdf_stream *data;
-
- TYPECHECK(stream, PDF_STREAM);
-
- data = stream->data;
-
- return (const void *) data->stream;
-}
-
-int
-pdf_stream_length (pdf_obj *stream)
-{
- pdf_stream *data;
-
- TYPECHECK(stream, PDF_STREAM);
-
- data = stream->data;
-
- return (int) data->stream_length;
-}
-
-static void
-set_objstm_data (pdf_obj *objstm, int *data) {
- TYPECHECK(objstm, PDF_STREAM);
-
- ((pdf_stream *) objstm->data)->objstm_data = data;
-}
-
-static int *
-get_objstm_data (pdf_obj *objstm) {
- TYPECHECK(objstm, PDF_STREAM);
-
- return ((pdf_stream *) objstm->data)->objstm_data;
-}
-
-void
-pdf_add_stream (pdf_obj *stream, const void *stream_data, int length)
-{
- pdf_stream *data;
-
- TYPECHECK(stream, PDF_STREAM);
-
- if (length < 1)
- 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, unsigned char);
- }
- memcpy(data->stream + data->stream_length, stream_data, length);
- data->stream_length += length;
-}
-
-#if HAVE_ZLIB
-#define WBUF_SIZE 4096
-
-static int
-filter_get_DecodeParms_FlateDecode (struct decode_parms *parms, pdf_obj *dict)
-{
- pdf_obj *tmp;
-
- ASSERT(dict && parms);
- ASSERT(PDF_OBJ_DICTTYPE(dict));
-
- /* Fill with default values */
- parms->predictor = 1;
- parms->colors = 1;
- parms->bits_per_component = 8;
- parms->columns = 1;
-
- tmp = pdf_deref_obj(pdf_lookup_dict(dict, "Predictor"));
- if (tmp) {
- parms->predictor = pdf_number_value(tmp);
- pdf_release_obj(tmp);
- }
- tmp = pdf_deref_obj(pdf_lookup_dict(dict, "Colors"));
- if (tmp) {
- parms->colors = pdf_number_value(tmp);
- pdf_release_obj(tmp);
- }
- tmp = pdf_deref_obj(pdf_lookup_dict(dict, "BitsPerComponent"));
- if (tmp) {
- parms->bits_per_component = pdf_number_value(tmp);
- pdf_release_obj(tmp);
- }
- tmp = pdf_deref_obj(pdf_lookup_dict(dict, "Columns"));
- if (tmp) {
- parms->columns = pdf_number_value(tmp);
- pdf_release_obj(tmp);
- }
- if (parms->bits_per_component != 1 &&
- parms->bits_per_component != 2 &&
- parms->bits_per_component != 4 &&
- parms->bits_per_component != 8 &&
- parms->bits_per_component != 16) {
- WARN("Invalid BPC value in DecodeParms: %d", parms->bits_per_component);
- return -1;
- } else if (parms->predictor <= 0 || parms->colors <= 0 ||
- parms->columns <= 0)
- return -1;
- return 0;
-}
-
-/* From Xpdf version 3.04
- * I'm not sure if I properly ported... Untested.
- */
-static int
-filter_row_TIFF2 (unsigned char *dst, const unsigned char *src,
- struct decode_parms *parms)
-{
- const unsigned char *p = src;
- unsigned char *col;
- /* bits_per_component < 8 here */
- int mask = (1 << parms->bits_per_component) - 1;
- int inbuf, outbuf; /* 2 bytes buffer */
- int i, ci, j, k, inbits, outbits;
-
- col = NEW(parms->colors, unsigned char);
- memset(col, 0, parms->colors);
- inbuf = outbuf = 0; inbits = outbits = 0;
- j = k = 0;
- for (i = 0; i < parms->columns; i++) {
- /* expanding each color component into an 8-bits bytes array */
- for (ci = 0; ci < parms->colors; ci++) {
- if (inbits < parms->bits_per_component) {
- /* need more byte */
- inbuf = (inbuf << 8) | p[j++];
- inbits += 8;
- }
- /* predict current color component */
- col[ci] = (unsigned char) ((col[ci] +
- (inbuf >> (inbits - parms->bits_per_component))) & mask);
- inbits -= parms->bits_per_component; /* consumed bpc bits */
- /* append newly predicted color component value */
- outbuf = (outbuf << parms->bits_per_component) | col[ci];
- outbits += parms->bits_per_component;
- if (outbits >= 8) { /* flush */
- dst[k++] = (unsigned char) (outbuf >> (outbits - 8));
- outbits -= 8;
- }
- }
- }
- if (outbits > 0) {
- dst[k] = (unsigned char) (outbuf << (8 - outbits));
- }
- RELEASE(col);
-
- return 0;
-}
-
-/* This routine is inefficient. Length is typically 4 for Xref streams.
- * Especially, calling pdf_add_stream() for each 4 bytes append is highly
- * inefficient.
- */
-static pdf_obj *
-filter_stream_decode_Predictor (const void *src, size_t srclen, struct decode_parms *parms)
-{
- pdf_obj *dst;
- const unsigned char *p = (const unsigned char *) src;
- const unsigned char *endptr = p + srclen;
- unsigned char *prev, *buf;
- int bits_per_pixel = parms->colors * parms->bits_per_component;
- int bytes_per_pixel = (bits_per_pixel + 7) / 8;
- int length = (parms->columns * bits_per_pixel + 7) / 8;
- int i, error = 0;
-
- dst = pdf_new_stream(0);
-
- prev = NEW(length, unsigned char);
- buf = NEW(length, unsigned char);
-
- memset(prev, 0, length);
- switch (parms->predictor) {
- case 1 : /* No prediction */
- pdf_add_stream(dst, src, srclen); /* Just copy */
- break;
- case 2: /* TIFF Predictor 2 */
- {
- if (parms->bits_per_component == 8) {
- while (p + length < endptr) {
- /* Same as PNG Sub */
- for (i = 0; i < length; i++) {
- int pv = i - bytes_per_pixel >= 0 ? buf[i - bytes_per_pixel] : 0;
- buf[i] = (unsigned char)(((int) p[i] + pv) & 0xff);
- }
- pdf_add_stream(dst, buf, length);
- p += length;
- }
- } else if (parms->bits_per_component == 16) {
- while (p + length < endptr) {
- for (i = 0; i < length; i += 2) {
- int b = i - bytes_per_pixel;
- char hi = b >= 0 ? buf[b] : 0;
- char lo = b >= 0 ? buf[b + 1] : 0;
- int pv = (hi << 8) | lo;
- int cv = (p[i] << 8) | p[i + 1];
- int c = pv + cv;
- buf[i] = (unsigned char) (c >> 8);
- buf[i + 1] = (unsigned char) (c & 0xff);
- }
- pdf_add_stream(dst, buf, length);
- p += length;
- }
- } else { /* bits per component 1, 2, 4 */
- while (!error && p + length < endptr) {
- error = filter_row_TIFF2(buf, p, parms);
- if (!error) {
- pdf_add_stream(dst, buf, length);
- p += length;
- }
- }
- }
- }
- break;
- /* PNG predictors: first byte of each rows is predictor type */
- case 10: /* PNG None */
- case 11: /* PNG Sub on all rows */
- case 12: /* PNG UP on all rows */
- case 13: /* PNG Average on all rows */
- case 14: /* PNG Paeth on all rows */
- case 15: /* PNG optimun: prediction algorithm can change from line to line. */
- {
- int type = parms->predictor - 10;
-
- while (!error && p + length < endptr) {
- if (parms->predictor == 15)
- type = *p;
- else if (*p != type) {
- WARN("Mismatched Predictor type in data stream.");
- error = -1;
- }
- p++;
- switch (type) {
- case 0: /* Do nothing just skip first byte */
- memcpy(buf, p, length);
- break;
- case 1:
- for (i = 0; i < length; i++) {
- int pv = i - bytes_per_pixel >= 0 ? buf[i - bytes_per_pixel] : 0;
- buf[i] = (unsigned char)(((int) p[i] + pv) & 0xff);
- }
- break;
- case 2:
- for (i = 0; i < length; i++) {
- buf[i] = (unsigned char)(((int) p[i] + (int) prev[i]) & 0xff);
- }
- break;
- case 3:
- for (i = 0; i < length; i++) {
- int up = prev[i];
- int left = i - bytes_per_pixel >= 0 ? buf[i - bytes_per_pixel] : 0;
- int tmp = floor((up + left) / 2);
- buf[i] = (unsigned char)((p[i] + tmp) & 0xff);
- }
- break;
- case 4:
- for (i = 0; i < length; i++) {
- int a = i - bytes_per_pixel >= 0 ? buf[i - bytes_per_pixel] : 0; /* left */
- int b = prev[i]; /* above */
- int c = i - bytes_per_pixel >= 0 ? prev[i - bytes_per_pixel] : 0; /* upper left */
- int q = a + b - c;
- int qa = q - a, qb = q - b, qc = q - c;
- qa = qa < 0 ? -qa : qa;
- qb = qb < 0 ? -qb : qb;
- qc = qc < 0 ? -qc : qc;
- if (qa <= qb && qa <= qc)
- buf[i] = (unsigned char) (((int) p[i] + a) & 0xff);
- else if (qb <= qc)
- buf[i] = (unsigned char) (((int) p[i] + b) & 0xff);
- else
- buf[i] = (unsigned char) (((int) p[i] + c) & 0xff);
- }
- break;
- default:
- WARN("Unknown PNG predictor type: %d", type);
- error = -1;
- }
- if (!error) {
- pdf_add_stream(dst, buf, length); /* highly inefficient */
- memcpy(prev, buf, length);
- p += length;
- }
- }
- }
- break;
- default:
- WARN("Unknown Predictor type value :%d", parms->predictor);
- error = -1;
- }
-
- RELEASE(prev);
- RELEASE(buf);
-
- if (error) {
- pdf_release_obj(dst);
- dst = NULL;
- }
-
- return dst;
-}
-
-static pdf_obj *
-filter_stream_decode_FlateDecode (const void *data, size_t len, struct decode_parms *parms)
-{
- pdf_obj *dst;
- pdf_obj *tmp;
- z_stream z;
- Bytef wbuf[WBUF_SIZE];
-
- z.zalloc = Z_NULL; z.zfree = Z_NULL; z.opaque = Z_NULL;
-
- z.next_in = (z_const Bytef *) data; z.avail_in = len;
- z.next_out = (Bytef *) wbuf; z.avail_out = WBUF_SIZE;
-
- if (inflateInit(&z) != Z_OK) {
- WARN("inflateInit() failed.");
- return NULL;
- }
-
- tmp = pdf_new_stream(0);
- for (;;) {
- int status;
- status = inflate(&z, Z_NO_FLUSH);
- if (status == Z_STREAM_END) {
- break;
- } else if (status == Z_DATA_ERROR && z.avail_in == 0) {
- WARN("Ignoring zlib error: status=%d, message=\"%s\"", status, z.msg);
- break;
- } else if (status != Z_OK) {
- WARN("inflate() failed. Broken PDF file?");
- inflateEnd(&z);
- pdf_release_obj(tmp);
- return NULL;
- }
-
- if (z.avail_out == 0) {
- pdf_add_stream(tmp, wbuf, WBUF_SIZE);
- z.next_out = wbuf;
- z.avail_out = WBUF_SIZE;
- }
- }
-
- if (WBUF_SIZE - z.avail_out > 0)
- pdf_add_stream(tmp, wbuf, WBUF_SIZE - z.avail_out);
-
- if (inflateEnd(&z) == Z_OK) {
- if (parms) {
- dst = filter_stream_decode_Predictor(pdf_stream_dataptr(tmp), pdf_stream_length(tmp), parms);
- } else {
- dst = pdf_link_obj(tmp);
- }
- } else {
- dst = NULL;
- }
- pdf_release_obj(tmp);
-
- return dst;
-}
-#endif
-
-static pdf_obj *
-filter_stream_decode_ASCIIHexDecode (const void *data, size_t len)
-{
- pdf_obj *dst;
- int eod, error;
- const char *p = (const char *) data;
- const char *endptr = p + len;
- unsigned char *buf, ch;
- size_t pos, n;
-
- buf = NEW((len+1)/2, unsigned char);
- skip_white(&p, endptr);
- ch = 0; n = 0; pos = 0; eod = 0; error = 0;
- while (p < endptr && !error && !eod) {
- char c1, val;
- c1 = p[0];
- if (c1 >= 'A' && c1 <= 'F') {
- val = c1 - 'A' + 10;
- } else if (c1 >= 'a' && c1 <= 'f') {
- val = c1 - 'a' + 10;
- } else if (c1 >= '0' && c1 <= '9') {
- val = c1 - '0';
- } else if (c1 == '>') {
- val = 0;
- eod = 1;
- if ((pos % 2) == 0)
- break;
- } else {
- error = -1;
- break;
- }
- if (pos % 2) {
- buf[n] = ch + val;
- n++;
- ch = 0;
- } else {
- ch = val << 4;
- }
- pos++; p++;
- skip_white(&p, endptr);
- }
- if (error || !eod) {
- WARN("Invalid ASCIIHex data seen: %s", error ? "Invalid character" : "No EOD marker");
- dst = NULL;
- } else {
- dst = pdf_new_stream(0);
- pdf_add_stream(dst, buf, n);
- }
- RELEASE(buf);
-
- return dst;
-}
-
-/* Percent sign is not start of comment here.
- * We need this for reading Ascii85 encoded data.
- */
-#define is_space(c) ((c) == ' ' || (c) == '\t' || (c) == '\f' || \
- (c) == '\r' || (c) == '\n' || (c) == '\0')
-static void
-skip_white_a85 (const char **p, const char *endptr)
-{
- while (*p < endptr && (is_space(**p))) {
- (*p)++;
- }
-}
-
-static pdf_obj *
-filter_stream_decode_ASCII85Decode (const void *data, size_t len)
-{
- pdf_obj *dst;
- int eod, error;
- const char *p = (const char *) data;
- const char *endptr = p + len;
- unsigned char *buf;
- size_t n;
-
- buf = NEW(((len+4)/5)*4, unsigned char);
- skip_white_a85(&p, endptr);
- n = 0; eod = 0; error = 0;
- while (p < endptr && !error && !eod) {
- char q[5] = {'u', 'u', 'u', 'u', 'u'};
- int m;
- char ch;
-
- ch = p[0];
- p++;
- skip_white_a85(&p, endptr);
- if (ch == 'z') {
- memset(buf+n, 0, 4);
- n += 4;
- continue;
- } else if (ch == '~') {
- if (p < endptr && p[0] == '>') {
- eod = 1;
- p++;
- } else {
- error = -1;
- }
- break;
- }
- q[0] = ch;
- for (m = 1; m < 5 && p < endptr; m++) {
- ch = p[0];
- p++;
- skip_white_a85(&p, endptr);
- if (ch == '~') {
- if (p < endptr && p[0] == '>') {
- eod = 1;
- p++;
- } else {
- error = -1;
- }
- break;
- } else if (ch < '!' || ch > 'u') {
- error = -1;
- break;
- } else {
- q[m] = ch;
- }
- }
- if (!error) {
- uint32_t val = 0;
- int i;
- if (m <= 1) {
- error = -1;
- break;
- }
- val = 85*85*85*(q[0] - '!') + 85*85*(q[1] - '!')
- + 85*(q[2] - '!') + (q[3] - '!');
- /* Check overflow */
- if (val > UINT32_MAX / 85) {
- error = -1;
- break;
- } else {
- val = 85 * val;
- if (val > UINT32_MAX - (q[4] - '!')) {
- error = -1;
- break;
- }
- val += (q[4] - '!');
- }
- if (!error) {
- for (i = 3; i >= 0; i--) {
- buf[n + i] = val & 0xff;
- val /= 256;
- }
- n += m - 1;
- }
- }
- }
-
- if (error) {
- WARN("Error in reading ASCII85 data.");
- dst = NULL;
- } else if (!eod) {
- WARN("Error in reading ASCII85 data: No EOD");
- dst = NULL;
- } else {
- dst = pdf_new_stream(0);
- pdf_add_stream(dst, buf, n);
- }
- RELEASE(buf);
-
- return dst;
-}
-
-static pdf_obj *
-filter_stream_decode (const char *filter_name, pdf_obj *src, pdf_obj *parm)
-{
- pdf_obj *dec;
- const char *stream_data;
- size_t stream_length;
-
- if (!filter_name)
- return pdf_link_obj(src);
-
- stream_data = pdf_stream_dataptr(src);
- stream_length = pdf_stream_length(src);
-
- if (!strcmp(filter_name, "ASCIIHexDecode")) {
- dec = filter_stream_decode_ASCIIHexDecode(stream_data, stream_length);
- } else if (!strcmp(filter_name, "ASCII85Decode")) {
- dec = filter_stream_decode_ASCII85Decode(stream_data, stream_length);
-#if HAVE_ZLIB
- } else if (!strcmp(filter_name, "FlateDecode")) {
- struct decode_parms decode_parm;
- if (parm)
- filter_get_DecodeParms_FlateDecode(&decode_parm, parm);
- dec = filter_stream_decode_FlateDecode(stream_data, stream_length, parm ? &decode_parm : NULL);
-#endif /* HAVE_ZLIB */
- } else {
- WARN("DecodeFilter \"%s\" not supported.", filter_name);
- dec = NULL;
- }
-
- return dec;
-}
-
-int
-pdf_concat_stream (pdf_obj *dst, pdf_obj *src)
-{
- pdf_obj *filtered;
- pdf_obj *stream_dict;
- pdf_obj *filter, *parms;
- int error = 0;
-
- if (!PDF_OBJ_STREAMTYPE(dst) || !PDF_OBJ_STREAMTYPE(src)) {
- WARN("Passed invalid type in pdf_concat_stream().");
- return -1;
- }
-
- stream_dict = pdf_stream_dict(src);
-
- filter = pdf_lookup_dict(stream_dict, "Filter");
- if (!filter) {
- pdf_add_stream(dst, pdf_stream_dataptr(src), pdf_stream_length(src));
- return 0;
- }
- if (pdf_lookup_dict(stream_dict, "DecodeParms")) {
- /* Dictionary or array */
- parms = pdf_deref_obj(pdf_lookup_dict(stream_dict, "DecodeParms"));
- if (!parms) {
- WARN("Failed to deref DeocdeParms...");
- return -1;
- } else if (!PDF_OBJ_ARRAYTYPE(parms) && !PDF_OBJ_DICTTYPE(parms)) {
- WARN("PDF dict or array expected for DecodeParms...");
- pdf_release_obj(parms);
- return -1;
- }
- } else {
- parms = NULL;
- }
- if (PDF_OBJ_ARRAYTYPE(filter)) {
- int i, num;
- pdf_obj *prev = NULL;
-
- num = pdf_array_length(filter);
- if (parms) {
- if (!PDF_OBJ_ARRAYTYPE(parms) || pdf_array_length(parms) != num) {
- WARN("Invalid DecodeParam object found.");
- pdf_release_obj(parms);
- return -1;
- }
- }
- if (num == 0) {
- filtered = pdf_link_obj(src);
- } else {
- filtered = NULL;
- prev = pdf_link_obj(src);
- for (i = 0; i < num && prev != NULL; i++) {
- pdf_obj *tmp1, *tmp2;
-
- tmp1 = pdf_deref_obj(pdf_get_array(filter, i));
- if (parms) {
- tmp2 = pdf_deref_obj(pdf_get_array(parms, i));
- } else {
- tmp2 = NULL;
- }
- if (PDF_OBJ_NAMETYPE(tmp1)) {
- filtered = filter_stream_decode(pdf_name_value(tmp1), prev, tmp2);
- } else if (PDF_OBJ_NULLTYPE(tmp1)) {
- filtered = pdf_link_obj(prev);
- } else {
- WARN("Unexpected object found for /Filter...");
- filtered = NULL;
- }
- if (prev)
- pdf_release_obj(prev);
- if (tmp1)
- pdf_release_obj(tmp1);
- if (tmp2)
- pdf_release_obj(tmp2);
- prev = filtered;
- }
- }
- } else if (PDF_OBJ_NAMETYPE(filter)) {
- filtered = filter_stream_decode(pdf_name_value(filter), src, parms);
- } else {
- WARN("Invalid value for /Filter found.");
- filtered = NULL;
- }
- if (parms)
- pdf_release_obj(parms);
- if (filtered) {
- pdf_add_stream(dst, pdf_stream_dataptr(filtered), pdf_stream_length(filtered));
- pdf_release_obj(filtered);
- error = 0;
- } else {
- error = -1;
- }
-
- return error;
-}
-
-static pdf_obj *
-pdf_stream_uncompress (pdf_obj *src) {
- pdf_obj *dst = pdf_new_stream(0);
-
- TYPECHECK(src, PDF_STREAM);
-
- pdf_merge_dict(pdf_stream_dict(dst), pdf_stream_dict(src));
- pdf_remove_dict(pdf_stream_dict(dst), "Length");
- pdf_concat_stream(dst, src);
-
- return dst;
-}
-
-#if 0
-void
-pdf_stream_set_flags (pdf_obj *stream, int flags)
-{
- pdf_stream *data;
-
- TYPECHECK(stream, PDF_STREAM);
-
- data = stream->data;
- data->_flags = flags;
-}
-
-int
-pdf_stream_get_flags (pdf_obj *stream)
-{
- pdf_stream *data;
-
- TYPECHECK(stream, PDF_STREAM);
-
- data = stream->data;
-
- return data->_flags;
-}
-#endif
-
-static void
-pdf_write_obj (pdf_out *p, pdf_obj *object)
-{
- ASSERT(p);
-
- if (object == NULL) {
- write_null(p);
- return;
- }
-
- if (INVALIDOBJ(object) || PDF_OBJ_UNDEFINED(object))
- ERROR("pdf_write_obj: Invalid object, type = %d\n", object->type);
-
- if (error_out)
- fprintf(stderr, "{%d}", object->refcount);
-
- switch (object->type) {
- case PDF_BOOLEAN:
- write_boolean(p, object->data);
- break;
- case PDF_NUMBER:
- write_number (p, object->data);
- break;
- case PDF_STRING:
- write_string (p, object->data);
- break;
- case PDF_NAME:
- write_name(p, object->data);
- break;
- case PDF_ARRAY:
- write_array(p, object->data);
- break;
- case PDF_DICT:
- write_dict (p, object->data);
- break;
- case PDF_STREAM:
- write_stream(p, object->data);
- break;
- case PDF_NULL:
- write_null(p);
- break;
- case PDF_INDIRECT:
- write_indirect(p, object->data);
- break;
- }
-}
-
-/* Write the object to the file */
-static void
-pdf_flush_obj (pdf_out *p, pdf_obj *object)
-{
- size_t length;
- char buf[64];
-
- /*
- * Record file position
- */
- add_xref_entry(p, object->label, 1,
- p->output.file_position, object->generation);
- length = sprintf(buf, "%u %hu obj\n", object->label, object->generation);
- p->state.enc_mode =
- (p->options.enable_encrypt && !(object->flags & OBJ_NO_ENCRYPT)) ? 1 : 0;
- if (p->state.enc_mode) {
- pdf_enc_set_label(p->sec_data, object->label);
- pdf_enc_set_generation(p->sec_data, object->generation);
- }
- pdf_out_str(p, buf, length);
- pdf_write_obj(p, object);
- pdf_out_str(p, "\nendobj\n", 8);
-}
-
-static int
-pdf_add_objstm (pdf_out *p, pdf_obj *objstm, pdf_obj *object)
-{
- int *data, pos;
-
- TYPECHECK(objstm, PDF_STREAM);
- ASSERT(p);
-
- data = get_objstm_data(objstm);
- pos = ++data[0];
-
- data[2*pos] = object->label;
- data[2*pos+1] = pdf_stream_length(objstm);
-
- add_xref_entry(p, object->label, 2, objstm->label, pos-1);
-
- /* redirect output into objstm */
- p->output_stream = objstm;
- p->state.enc_mode = 0;
- pdf_write_obj(p, object);
- pdf_out_char(p, '\n');
- p->output_stream = NULL;
-
- return pos;
-}
-
-static void
-release_objstm (pdf_obj *objstm)
-{
- int *data = get_objstm_data(objstm);
- int pos = data[0];
- pdf_obj *dict;
- pdf_stream *stream;
- unsigned char *old_buf;
- size_t old_length;
- stream = (pdf_stream *) objstm->data;
-
- /* Precede stream data by offset table */
- old_buf = stream->stream;
- 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;
-
- {
- int i = 2*pos, *val = data+2;
- while (i--) {
- char buf[32];
- size_t length = sprintf(buf, "%d ", *(val++));
- pdf_add_stream(objstm, buf, length);
- }
- }
-
- dict = pdf_stream_dict(objstm);
- pdf_add_dict(dict, pdf_new_name("Type"), pdf_new_name("ObjStm"));
- pdf_add_dict(dict, pdf_new_name("N"), pdf_new_number(pos));
- pdf_add_dict(dict, pdf_new_name("First"), pdf_new_number(stream->stream_length));
-
- pdf_add_stream(objstm, old_buf, old_length);
- RELEASE(old_buf);
- pdf_release_obj(objstm);
-}
-
-#define is_free(b,c) (((b)[(c)/8]) & (1 << (7-((c)%8))))
-
-void
-pdf_release_obj (pdf_obj *object)
-{
- pdf_out *p = current_output();
-
- if (object == NULL)
- return;
- if (INVALIDOBJ(object) || object->refcount <= 0) {
- MESG("\npdf_release_obj: object=%p, type=%d, refcount=%d\n",
- object, object->type, object->refcount);
- error_out = 1;
- pdf_write_obj(p, object);
- ERROR("pdf_release_obj: Called with invalid object.");
- error_out = 0;
- }
- object->refcount -= 1;
- if (object->refcount == 0) {
-#if defined(PDFOBJ_DEBUG)
- bucket[object->obj_id] = NULL;
-#endif
- /*
- * 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) {
- p->free_list[object->label/8] |= (1 << (7-(object->label % 8)));
- if (p->output.file != NULL) {
- if (!p->options.use_objstm || object->flags & OBJ_NO_OBJSTM ||
- (p->options.enable_encrypt && (object->flags & OBJ_NO_ENCRYPT)) ||
- object->generation) {
- pdf_flush_obj(p, object);
- } else {
- if (!p->current_objstm) {
- int *data = NEW(2*OBJSTM_MAX_OBJS+2, int);
- data[0] = data[1] = 0;
- p->current_objstm = pdf_new_stream(STREAM_COMPRESS);
- set_objstm_data(p->current_objstm, data);
- pdf_label_obj(p, p->current_objstm);
- }
- if (pdf_add_objstm(p, p->current_objstm, object) == OBJSTM_MAX_OBJS) {
- release_objstm(p->current_objstm);
- p->current_objstm = NULL;
- }
- }
- }
- }
- switch (object->type) {
- case PDF_BOOLEAN:
- release_boolean(object->data);
- break;
- case PDF_NULL:
- 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;
- object->data = NULL;
- RELEASE(object);
- }
-}
-
-/* Reading external PDF files
- *
- */
-static int
-backup_line (FILE *pdf_input_file)
-{
- int ch = -1;
-
- /*
- * 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);
- } while (tell_position(pdf_input_file) > 0 &&
- (ch = fgetc(pdf_input_file)) >= 0 &&
- (ch != '\n' && ch != '\r' ));
- if (ch < 0) {
- return 0;
- }
-
- return 1;
-}
-
-static int
-find_xref (FILE *pdf_input_file)
-{
- size_t len, xref_pos = 0;
- int tries = 10;
-
- do {
- size_t currentpos;
-
- if (!backup_line(pdf_input_file)) {
- tries = 0;
- break;
- }
- currentpos = tell_position(pdf_input_file);
- fread(work_buffer, sizeof(char), strlen("startxref"), pdf_input_file);
- 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 */
- len = mfreadln(work_buffer, WORK_BUFFER_SIZE, pdf_input_file);
- if (len <= 0)
- WARN("Reading xref location data failed... Not a PDF file?");
- else {
- const char *start, *end;
- char *number;
-
- start = work_buffer;
- end = start + len;
- skip_white(&start, end);
- number = parse_number(&start, end);
- xref_pos = (int) atof(number);
- RELEASE(number);
- }
-
- return xref_pos;
-}
-
-/*
- * This routine must be called with the file pointer located
- * at the start of the trailer.
- */
-static pdf_obj *
-parse_trailer (pdf_file *pf)
-{
- pdf_obj *result;
- /*
- * 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, pf->file) == 0 ||
- strncmp(work_buffer, "trailer", strlen("trailer"))) {
- WARN("No trailer. Are you sure this is a PDF file?");
- WARN("buffer:\n->%s<-\n", work_buffer);
- result = NULL;
- } else {
- const char *p = work_buffer + strlen("trailer");
- skip_white(&p, work_buffer + WORK_BUFFER_SIZE);
- result = parse_pdf_dict(&p, work_buffer + WORK_BUFFER_SIZE, pf);
- }
-
- return result;
-}
-
-/*
- * This routine tries to estimate an upper bound for character position
- * of the end of the object, so it knows how big the buffer must be.
- * 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
- * :-(
- */
-static int
-next_object_offset (pdf_file *pf, uint32_t obj_num)
-{
- uint32_t next = pf->file_size; /* Worst case */
- size_t i;
- uint32_t curr;
-
- curr = pf->xref_table[obj_num].field2;
- /* Check all other type 1 objects to find next one */
- for (i = 0; i < pf->num_obj; i++) {
- if (pf->xref_table[i].type == 1 &&
- pf->xref_table[i].field2 > curr &&
- pf->xref_table[i].field2 < next)
- next = pf->xref_table[i].field2;
- }
-
- return next;
-}
-
-#define checklabel(pf, n, g) ((n) > 0 && (n) < (pf)->num_obj && ( \
- ((pf)->xref_table[(n)].type == 1 && (pf)->xref_table[(n)].field3 == (g)) || \
- ((pf)->xref_table[(n)].type == 2 && !(g))))
-
-pdf_obj *
-pdf_new_indirect (pdf_file *pf, uint32_t obj_num, uint16_t obj_gen)
-{
- pdf_obj *result;
- pdf_indirect *indirect;
-
- indirect = NEW(1, pdf_indirect);
- indirect->pf = pf;
- indirect->obj = NULL;
- indirect->label = obj_num;
- indirect->generation = obj_gen;
-
- result = pdf_new_obj(PDF_INDIRECT);
- result->data = indirect;
-
- return result;
-}
-
-static pdf_obj *
-pdf_read_object (uint32_t obj_num, uint16_t obj_gen, pdf_file *pf, size_t offset, size_t limit)
-{
- pdf_obj *result = NULL;
- size_t length;
- char *buffer;
- const char *p, *endptr;
-
- length = limit - offset;
-
- if (length <= 0)
- return NULL;
-
- buffer = NEW(length + 1, char);
-
- seek_absolute(pf->file, offset);
- fread(buffer, sizeof(char), length, pf->file);
-
- p = buffer;
- endptr = p + length;
-
- /* Check for obj_num and obj_gen */
- {
- const char *q = p; /* <== p */
- char *sp;
- uint32_t n, g;
-
- skip_white(&q, endptr);
- sp = parse_unsigned(&q, endptr);
- if (!sp) {
- RELEASE(buffer);
- return NULL;
- }
- n = strtoul(sp, NULL, 10);
- RELEASE(sp);
-
- skip_white(&q, endptr);
- sp = parse_unsigned(&q, endptr);
- if (!sp) {
- RELEASE(buffer);
- return NULL;
- }
- g = strtoul(sp, NULL, 10);
- RELEASE(sp);
-
- if (obj_num && (n != obj_num || g != obj_gen)) {
- RELEASE(buffer);
- return NULL;
- }
-
- p = q; /* ==> p */
- }
-
-
- skip_white(&p, endptr);
- if (memcmp(p, "obj", strlen("obj"))) {
- WARN("Didn't find \"obj\".");
- RELEASE(buffer);
- return NULL;
- }
- p += strlen("obj");
-
- result = parse_pdf_object(&p, endptr, pf);
-
- skip_white(&p, endptr);
- if (memcmp(p, "endobj", strlen("endobj"))) {
- WARN("Didn't find \"endobj\".");
- if (result)
- pdf_release_obj(result);
- result = NULL;
- }
- RELEASE(buffer);
-
- return result;
-}
-
-static pdf_obj *
-read_objstm (pdf_file *pf, uint32_t num)
-{
- size_t offset = pf->xref_table[num].field2;
- uint16_t gen = pf->xref_table[num].field3;
- size_t limit = next_object_offset(pf, num);
- int n, first, *header = NULL;
- char *data = NULL, *q;
- const char *p, *endptr;
- int i;
- pdf_obj *objstm, *dict, *type, *n_obj, *first_obj;
-
- objstm = pdf_read_object(num, gen, pf, offset, limit);
-
- if (!PDF_OBJ_STREAMTYPE(objstm))
- goto error;
-
- {
- pdf_obj *tmp = pdf_stream_uncompress(objstm);
- if (!tmp)
- goto error;
- pdf_release_obj(objstm);
- objstm = tmp;
- }
-
- dict = pdf_stream_dict(objstm);
-
- type = pdf_lookup_dict(dict, "Type");
- if (!PDF_OBJ_NAMETYPE(type) ||
- strcmp(pdf_name_value(type), "ObjStm"))
- goto error;
-
- n_obj = pdf_lookup_dict(dict, "N");
- if (!PDF_OBJ_NUMBERTYPE(n_obj))
- goto error;
- n = (int) pdf_number_value(n_obj);
-
- first_obj = pdf_lookup_dict(dict, "First");
- if (!PDF_OBJ_NUMBERTYPE(first_obj))
- goto error;
- first = (int) pdf_number_value(first_obj);
- /* reject object streams without object data */
- if (first >= pdf_stream_length(objstm))
- goto error;
-
- header = NEW(2*(n+1), int);
- set_objstm_data(objstm, header);
- *(header++) = n;
- *(header++) = first;
-
- /* avoid parsing beyond offset table */
- data = NEW(first + 1, char);
- memcpy(data, pdf_stream_dataptr(objstm), first);
- data[first] = 0;
-
- p = data;
- endptr = p + first;
- i = 2*n;
- while (i--) {
- *(header++) = strtoul(p, &q, 10);
- if (q == p)
- goto error;
- p = q;
- }
-
- /* Any garbage after last entry? */
- skip_white(&p, endptr);
- if (p != endptr)
- goto error;
- RELEASE(data);
-
- return pf->xref_table[num].direct = objstm;
-
- error:
- WARN("Cannot parse object stream.");
- if (data)
- RELEASE(data);
- if (objstm)
- pdf_release_obj(objstm);
- return NULL;
-}
-
-/* Label without corresponding object definition are replaced by the
- * null object, as required by the PDF spec. This is important to parse
- * several cross-reference sections.
- */
-static pdf_obj *
-pdf_get_object (pdf_file *pf, uint32_t obj_num, uint16_t obj_gen)
-{
- pdf_obj *result;
-
- if (!checklabel(pf, obj_num, obj_gen)) {
- WARN("Trying to read nonexistent or deleted object: %lu %u",
- obj_num, obj_gen);
- return pdf_new_null();
- }
-
- if ((result = pf->xref_table[obj_num].direct)) {
- return pdf_link_obj(result);
- }
-
- if (pf->xref_table[obj_num].type == 1) {
- /* type == 1 */
- uint32_t offset;
- size_t limit;
- offset = pf->xref_table[obj_num].field2;
- limit = next_object_offset(pf, obj_num);
- result = pdf_read_object(obj_num, obj_gen, pf, offset, limit);
- } else {
- /* type == 2 */
- uint32_t objstm_num = pf->xref_table[obj_num].field2;
- uint16_t index = pf->xref_table[obj_num].field3;
- pdf_obj *objstm;
- int *data, n, first, length;
- const char *p, *q;
-
- if (objstm_num >= pf->num_obj ||
- pf->xref_table[objstm_num].type != 1 ||
- !((objstm = pf->xref_table[objstm_num].direct) ||
- (objstm = read_objstm(pf, objstm_num))))
- goto error;
-
- data = get_objstm_data(objstm);
- n = *(data++);
- first = *(data++);
-
- if (index >= n || data[2*index] != obj_num)
- goto error;
-
- length = pdf_stream_length(objstm);
- p = (const char *) pdf_stream_dataptr(objstm) + first + data[2*index+1];
- q = p + (index == n-1 ? length : first+data[2*index+3]);
- result = parse_pdf_object(&p, q, pf);
- if (!result)
- goto error;
- }
-
- /* Make sure the caller doesn't free this object */
- pf->xref_table[obj_num].direct = pdf_link_obj(result);
-
- return result;
-
- error:
- WARN("Could not read object from object stream.");
- return pdf_new_null();
-}
-
-#define OBJ_FILE(o) (((pdf_indirect *)((o)->data))->pf)
-#define OBJ_OBJ(o) (((pdf_indirect *)((o)->data))->obj)
-#define OBJ_NUM(o) (((pdf_indirect *)((o)->data))->label)
-#define OBJ_GEN(o) (((pdf_indirect *)((o)->data))->generation)
-
-static pdf_obj *
-pdf_new_ref (pdf_out *p, pdf_obj *object)
-{
- pdf_obj *result;
-
- ASSERT(p);
-
- if (object->label == 0) {
- pdf_label_obj(p, object);
- }
- result = pdf_new_indirect(NULL, object->label, object->generation);
- OBJ_OBJ(result) = object;
- return result;
-}
-
-/* pdf_deref_obj always returns a link instead of the original */
-/* It never return the null object, but the NULL pointer instead */
-pdf_obj *
-pdf_deref_obj (pdf_obj *obj)
-{
- int count = PDF_OBJ_MAX_DEPTH;
-
- if (obj)
- obj = pdf_link_obj(obj);
-
- while (PDF_OBJ_INDIRECTTYPE(obj) && --count) {
- pdf_file *pf = OBJ_FILE(obj);
- if (pf) {
- uint32_t obj_num = OBJ_NUM(obj);
- uint16_t obj_gen = OBJ_GEN(obj);
- pdf_release_obj(obj);
- obj = pdf_get_object(pf, obj_num, obj_gen);
- } else {
- pdf_out *p = current_output();
- pdf_indirect *data = obj->data;
-
- if ((p->free_list[data->label/8] & (1 << (7-((data->label) % 8))))) {
- pdf_release_obj(obj);
- return NULL;
- } else {
- pdf_obj *next_obj = OBJ_OBJ(obj);
- if (!next_obj) {
- ERROR("Undefined object reference");
- }
- pdf_release_obj(obj);
- obj = pdf_link_obj(next_obj);
- }
- }
- }
-
- if (!count)
- ERROR("Loop in object hierarchy detected. Broken PDF file?");
-
- if (PDF_OBJ_NULLTYPE(obj)) {
- pdf_release_obj(obj);
- return NULL;
- } else
- return obj;
-}
-
-static void
-extend_xref (pdf_file *pf, size_t new_size)
-{
- size_t i;
-
- pf->xref_table = RENEW(pf->xref_table, new_size, xref_entry);
- for (i = pf->num_obj; i < new_size; i++) {
- pf->xref_table[i].direct = NULL;
- pf->xref_table[i].indirect = NULL;
- pf->xref_table[i].type = 0;
- pf->xref_table[i].field3 = 0;
- pf->xref_table[i].field2 = 0L;
- }
- pf->num_obj = new_size;
-}
-
-/* Returns < 0 for error, 1 for success, and 0 when xref stream found. */
-static int
-parse_xref_table (pdf_file *pf, size_t xref_pos)
-{
- FILE *pdf_input_file = pf->file;
- const char *p, *endptr;
- char buf[256]; /* See, PDF ref. v.1.7, p.91 for "255+1" here. */
- size_t len;
-
- /*
- * This routine reads one xref segment. It may be called multiple times
- * on the same file. xref tables sometimes come in pieces.
- */
- seek_absolute(pf->file, xref_pos);
- len = mfreadln(buf, 255, pdf_input_file);
- /* We should have already checked that "startxref" section exists.
- * So, EOF here (len = -1) is impossible. We don't treat too long line
- * case seriously.
- */
- if (len < 0) {
- WARN("Something went wrong while reading xref table...giving up.");
- return -1;
- }
- p = buf;
- endptr = buf + len;
- /* No skip_white() here. There should not be any white-spaces here. */
- if (memcmp(p, "xref", strlen("xref"))) {
- /* Might be an xref stream and not an xref table */
- return 0;
- }
- p += strlen("xref");
- skip_white(&p, endptr);
- if (p != endptr) {
- WARN("Garbage after \"xref\" keyword found.");
- return -1;
- }
-
- /* Next line in file has first item and size of table */
- for (;;) {
- char flag;
- size_t current_pos, size, offset;
- int i;
- uint32_t first, obj_gen;
-
- current_pos = tell_position(pdf_input_file);
- len = mfreadln(buf, 255, pdf_input_file);
- if (len == 0) /* empty line... just skip. */
- continue;
- else if (len < 0) {
- WARN("Reading a line failed in xref table.");
- return -1;
- }
-
- p = buf;
- endptr = buf + len;
- skip_white(&p, endptr);
- if (p == endptr) { /* Only white-spaces and/or comment found. */
- continue;
- } else if (!strncmp(p, "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.
- */
- current_pos += p - buf; /* Jump to the beginning of "trailer" keyword. */
- seek_absolute(pdf_input_file, current_pos);
- break;
- }
- /* Line containing something other than white-space characters found.
- *
- * Start reading xref subsection
- *
- * This section just reads two nusigned integers, namely, the object number
- * of first object and the size of the xref subsection. PDF reference says
- * that only "a space" is allowed between those two numbers but we allow
- * more white-space characters.
- */
- {
- char *q;
-
- /* Object number of the first object whithin this xref subsection. */
- q = parse_unsigned(&p, endptr);
- if (!q) {
- WARN("An unsigned integer expected but could not find. (xref)");
- return -1;
- }
- first = atoi(q);
- RELEASE(q);
- skip_white(&p, endptr);
-
- /* Nnumber of objects in this xref subsection. */
- q = parse_unsigned(&p, endptr);
- if (!q) {
- WARN("An unsigned integer expected but could not find. (xref)");
- return -1;
- }
- size = atoi(q);
- RELEASE(q);
- skip_white(&p, endptr);
-
- /* Check for unrecognized tokens */
- if (p != endptr) {
- WARN("Unexpected token found in xref table.");
- return -1;
- }
- }
-
- /* The first line of a xref subsection OK. */
- if (pf->num_obj < first + size) {
- extend_xref(pf, first + size);
- }
-
- /* Start parsing xref subsection body... */
- for (i = first; i < first + size; ) {
- /* PDF spec. requires each xref subsection lines being exactly 20 bytes
- * long [including end-of-line marker(s)], offset 10 decimal digits,
- * generation number being 5 decimal digits, and each entries delimitted
- * by "a single space". However, we don't srtictly follow this rule:
- * More than one "white-spaces" allowed, can be ended with a comment,
- * and so on.
- */
- len = mfreadln(buf, 255, pdf_input_file);
- if (len == 0) /* empty line...just skip. */
- continue;
- else if (len < 0) {
- WARN("Something went wrong while reading xref subsection...");
- return -1;
- }
- p = buf;
- endptr = buf + len;
- skip_white(&p, endptr);
- if (p == endptr) /* Only white-spaces and/or comment. */
- continue;
-
- /*
- * 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.
- */
- offset = 0UL; obj_gen = 0; flag = 0;
- {
- char *q;
-
- /* Offset value -- 10 digits (0 padded) */
- q = parse_unsigned(&p, endptr);
- if (!q) {
- WARN("An unsigned integer expected but could not find. (xref)");
- return -1;
- } else if (strlen(q) != 10) { /* exactly 10 digits */
- WARN(("Offset must be a 10 digits number. (xref)"));
- RELEASE(q);
- return -1;
- }
- /* FIXME: Possible overflow here. Consider using strtoll(). */
- offset = atoi(q);
- RELEASE(q);
- skip_white(&p, endptr);
-
- /* Generation number -- 5 digits (0 padded) */
- q = parse_unsigned(&p, endptr);
- if (!q) {
- WARN("An unsigned integer expected but could not find. (xref)");
- return -1;
- } else if (strlen(q) != 5) { /* exactly 5 digits */
- WARN(("Expecting a 5 digits number. (xref)"));
- RELEASE(q);
- return -1;
- }
- obj_gen = atoi(q);
- RELEASE(q);
- skip_white(&p, endptr);
- }
- if (p == endptr) {
- WARN("Unexpected EOL reached while reading a xref subsection entry.");
- return -1;
- }
-
- /* Flag -- a char */
- flag = *p; p++;
- skip_white(&p, endptr);
- if (p < endptr) {
- WARN("Garbage in xref subsection entry found...");
- return -1;
- } else if (((flag != 'n' && flag != 'f') ||
- (flag == 'n' &&
- (offset >= pf->file_size || (offset > 0 && offset < 4))))) {
- WARN("Invalid xref table entry [%lu]. PDF file is corrupt...", i);
- return -1;
- }
-
- /* Everything seems to be OK. */
- if (!pf->xref_table[i].field2) {
- pf->xref_table[i].type = (flag == 'n');
- pf->xref_table[i].field2 = offset;
- pf->xref_table[i].field3 = obj_gen;
- }
- i++;
- }
- }
-
- return 1;
-}
-
-static uint32_t
-parse_xrefstm_field (const char **p, size_t length, uint32_t def)
-{
- uint32_t val = 0;
-
- if (!length)
- return def;
-
- while (length--) {
- val <<= 8;
- val |= (unsigned char) *((*p)++);
- }
-
- return val;
-}
-
-static int
-parse_xrefstm_subsec (pdf_file *pf,
- const char **p, size_t *length,
- int *W, int wsum, int first, int size)
-{
- xref_entry *e;
-
- if ((*length -= wsum*size) < 0)
- return -1;
-
- if (pf->num_obj < first+size)
- extend_xref(pf, first+size); /* TODO: change! why? */
-
- e = pf->xref_table + first;
- while (size--) {
- uint8_t type;
- uint32_t field2;
- uint16_t field3;
-
- type = (unsigned char) parse_xrefstm_field(p, W[0], 1);
- if (type > 2)
- WARN("Unknown cross-reference stream entry type.");
-#if 0
- /* Not sure */
- else if (!W[1] || (type != 1 && !W[2]))
- return -1;
-#endif
-
- field2 = (uint32_t) parse_xrefstm_field(p, W[1], 0);
- field3 = (uint16_t) parse_xrefstm_field(p, W[2], 0);
-
- if (!e->field2) {
- e->type = type;
- e->field2 = field2;
- e->field3 = field3;
- }
- e++;
- }
-
- return 0;
-}
-
-static int
-parse_xref_stream (pdf_file *pf, size_t xref_pos, pdf_obj **trailer)
-{
- pdf_obj *xrefstm, *size_obj, *W_obj, *index_obj;
- uint32_t size;
- size_t length;
- int W[3], i, wsum = 0;
- const char *p;
-
- xrefstm = pdf_read_object(0, 0, pf, xref_pos, pf->file_size);
- if (!PDF_OBJ_STREAMTYPE(xrefstm))
- goto error;
-
- {
- pdf_obj *tmp = pdf_stream_uncompress(xrefstm);
- if (!tmp)
- goto error;
- pdf_release_obj(xrefstm);
- xrefstm = tmp;
- }
-
- *trailer = pdf_link_obj(pdf_stream_dict(xrefstm));
-
- size_obj = pdf_lookup_dict(*trailer, "Size");
- if (!PDF_OBJ_NUMBERTYPE(size_obj))
- goto error;
- size = (uint32_t) pdf_number_value(size_obj);
-
- length = pdf_stream_length(xrefstm);
-
- W_obj = pdf_lookup_dict(*trailer, "W");
- if (!PDF_OBJ_ARRAYTYPE(W_obj) || pdf_array_length(W_obj) != 3)
- goto error;
-
- for (i = 0; i < 3; i++) {
- pdf_obj *tmp = pdf_get_array(W_obj, i);
- if (!PDF_OBJ_NUMBERTYPE(tmp))
- goto error;
- wsum += (W[i] = (int) pdf_number_value(tmp));
- }
-
- p = pdf_stream_dataptr(xrefstm);
-
- index_obj = pdf_lookup_dict(*trailer, "Index");
- if (index_obj) {
- size_t index_len;
- if (!PDF_OBJ_ARRAYTYPE(index_obj) ||
- ((index_len = pdf_array_length(index_obj)) % 2 ))
- goto error;
-
- i = 0;
- while (i < index_len) {
- pdf_obj *first = pdf_get_array(index_obj, i++);
- size_obj = pdf_get_array(index_obj, i++);
- if (!PDF_OBJ_NUMBERTYPE(first) || !PDF_OBJ_NUMBERTYPE(size_obj) ||
- parse_xrefstm_subsec(pf, &p, &length, W, wsum,
- (int) pdf_number_value(first), (int) pdf_number_value(size_obj)))
- goto error;
- }
- } else if (parse_xrefstm_subsec(pf, &p, &length, W, wsum, 0, size))
- goto error;
-
- if (length)
- WARN("Garbage in xref stream.");
-
- pdf_release_obj(xrefstm);
-
- return 1;
-
- error:
- WARN("Cannot parse cross-reference stream.");
- if (xrefstm)
- pdf_release_obj(xrefstm);
- if (*trailer) {
- pdf_release_obj(*trailer);
- *trailer = NULL;
- }
- return 0;
-}
-
-/* TODO: parse Version entry */
-static pdf_obj *
-read_xref (pdf_file *pf)
-{
- pdf_obj *trailer = NULL, *main_trailer = NULL;
- size_t xref_pos;
-
- if (!(xref_pos = find_xref(pf->file)))
- goto error;
-
- while (xref_pos) {
- pdf_obj *prev;
-
- int res = parse_xref_table(pf, xref_pos);
- if (res > 0) {
- /* cross-reference table */
- pdf_obj *xrefstm;
-
- if (!(trailer = parse_trailer(pf)))
- goto error;
-
- if (!main_trailer)
- main_trailer = pdf_link_obj(trailer);
-
- if ((xrefstm = pdf_lookup_dict(trailer, "XRefStm"))) {
- pdf_obj *new_trailer = NULL;
- if (PDF_OBJ_NUMBERTYPE(xrefstm) &&
- parse_xref_stream(pf, (int) pdf_number_value(xrefstm),
- &new_trailer))
- pdf_release_obj(new_trailer);
- else
- WARN("Skipping hybrid reference section.");
- /* Many PDF 1.5 xref streams use DecodeParms, which we cannot
- * parse. This way we can use at least xref tables in hybrid
- * documents. Or should we better stop parsing the file?
- */
- }
-
- } else if (!res && parse_xref_stream(pf, xref_pos, &trailer)) {
- /* cross-reference stream */
- if (!main_trailer)
- main_trailer = pdf_link_obj(trailer);
- } else
- goto error;
-
- if ((prev = pdf_lookup_dict(trailer, "Prev"))) {
- if (PDF_OBJ_NUMBERTYPE(prev))
- xref_pos = (size_t) pdf_number_value(prev);
- else
- goto error;
- } else
- xref_pos = 0;
-
- pdf_release_obj(trailer);
- }
-
-#if 0
- if (!pdf_lookup_dict(main_trailer, "Root")) {
- WARN("Trailer doesn't have catalog. Is this a correct PDF file?");
- goto error;
- }
-#endif
-
- return main_trailer;
-
- error:
- WARN("Error while parsing PDF file.");
- if (trailer)
- pdf_release_obj(trailer);
- if (main_trailer)
- pdf_release_obj(main_trailer);
- return NULL;
-}
-
-static struct ht_table *pdf_files = NULL;
-
-static pdf_file *
-pdf_file_new (FILE *file)
-{
- pdf_file *pf;
- ASSERT(file);
- pf = NEW(1, pdf_file);
- pf->file = file;
- pf->trailer = NULL;
- pf->xref_table = NULL;
- pf->catalog = NULL;
- pf->num_obj = 0;
- pf->version = 0;
-
- seek_end(file);
- pf->file_size = tell_position(file);
-
- return pf;
-}
-
-static void
-pdf_file_free (pdf_file *pf)
-{
- size_t i;
-
- if (!pf) {
- return;
- }
-
- for (i = 0; i < pf->num_obj; i++) {
- if (pf->xref_table[i].direct)
- pdf_release_obj(pf->xref_table[i].direct);
- if (pf->xref_table[i].indirect)
- pdf_release_obj(pf->xref_table[i].indirect);
- }
-
- RELEASE(pf->xref_table);
- if (pf->trailer)
- pdf_release_obj(pf->trailer);
- if (pf->catalog)
- pdf_release_obj(pf->catalog);
-
- RELEASE(pf);
-}
-
-void
-pdf_files_init (void)
-{
- pdf_files = NEW(1, struct ht_table);
- ht_init_table(pdf_files, (void (*)(void *)) pdf_file_free);
-}
-
-int
-pdf_file_get_version (pdf_file *pf)
-{
- ASSERT(pf);
- return pf->version;
-}
-
-pdf_obj *
-pdf_file_get_trailer (pdf_file *pf)
-{
- ASSERT(pf);
- return pdf_link_obj(pf->trailer);
-}
-
-/* FIXME:
- * pdf_file_get_trailer() does pdf_link_obj() but
- * pdf_file_get_catalog() does not. Why?
- */
-pdf_obj *
-pdf_file_get_catalog (pdf_file *pf)
-{
- ASSERT(pf);
- return pf->catalog;
-}
-
-pdf_file *
-pdf_open (const char *ident, FILE *file)
-{
- pdf_out *p = current_output();
- pdf_file *pf = NULL;
-
- ASSERT(pdf_files);
-
- if (ident)
- pf = (pdf_file *) ht_lookup_table(pdf_files, ident, strlen(ident));
-
- if (pf) {
- pf->file = file;
- } else {
- pdf_obj *new_version;
- int version = check_for_pdf_version(file);
-
- if (!dpx_conf.is_xbb) {
- int ver_major, ver_minor;
-
- ver_major = version / 10;
- ver_minor = version % 10;
-
- if (version < 10)
- WARN("Unrecognized PDF version specified for input PDF file: %d.%d",
- ver_major, ver_minor);
- else if (pdf_check_version(ver_major, ver_minor) < 0) {
- WARN("Trying to include PDF file with version (%d.%d), which is " \
- "newer than current output PDF setting (%d.%d).",
- ver_major, ver_minor, p->version.major, p->version.minor);
- }
- }
- pf = pdf_file_new(file);
- pf->version = version;
-
- if (!(pf->trailer = read_xref(pf)))
- goto error;
-
- if (pdf_lookup_dict(pf->trailer, "Encrypt")) {
- WARN("PDF document is encrypted.");
- goto error;
- }
-
- pf->catalog = pdf_deref_obj(pdf_lookup_dict(pf->trailer, "Root"));
- if (!PDF_OBJ_DICTTYPE(pf->catalog)) {
- WARN("Cannot read PDF document catalog. Broken PDF file?");
- goto error;
- }
-
- new_version = pdf_deref_obj(pdf_lookup_dict(pf->catalog, "Version"));
- if (new_version) {
- unsigned int major, minor;
-
- if (!PDF_OBJ_NAMETYPE(new_version) ||
- sscanf(pdf_name_value(new_version), "%u.%u", &major, &minor) != 2) {
- pdf_release_obj(new_version);
- WARN("Illegal Version entry in document catalog. Broken PDF file?");
- goto error;
- }
-
- if (pf->version < major*10+minor)
- pf->version = major*10+minor;
-
- pdf_release_obj(new_version);
- }
-
- if (ident)
- ht_append_table(pdf_files, ident, strlen(ident), pf);
- }
-
- return pf;
-
- error:
- pdf_file_free(pf);
- return NULL;
-}
-
-void
-pdf_close (pdf_file *pf)
-{
- if (pf)
- pf->file = NULL;
-}
-
-void
-pdf_files_close (void)
-{
- ASSERT(pdf_files);
- ht_clear_table(pdf_files);
- RELEASE(pdf_files);
-}
-
-static int
-check_for_pdf_version (FILE *file)
-{
- unsigned int major, minor;
-
- rewind(file);
-
- return (ungetc(fgetc(file), file) == '%' &&
- fscanf(file, "%%PDF-%u.%u", &major, &minor) == 2) ? (major*10+minor) : -1;
-}
-
-int
-check_for_pdf (FILE *file)
-{
- int version = check_for_pdf_version(file);
-
- if (version < 0) /* not a PDF file */
- return 0;
-
- if (version <= pdf_get_version())
- return 1;
-
- return 1;
-}
-
-static inline int
-import_dict (pdf_obj *key, pdf_obj *value, void *pdata)
-{
- pdf_obj *copy;
- pdf_obj *tmp;
-
- copy = (pdf_obj *) pdata;
-
- tmp = pdf_import_object(value);
- if (!tmp) {
- return -1;
- }
- pdf_add_dict(copy, pdf_link_obj(key), tmp);
-
- return 0;
-}
-
-static pdf_obj *
-pdf_import_indirect (pdf_obj *object)
-{
- pdf_out *p = current_output();
- pdf_file *pf = OBJ_FILE(object);
- uint32_t obj_num = OBJ_NUM(object);
- uint16_t obj_gen = OBJ_GEN(object);
- pdf_obj *ref;
-
- ASSERT(pf);
-
- if (!checklabel(pf, obj_num, obj_gen)) {
- WARN("Can't resolve object: %lu %u", obj_num, obj_gen);
- return pdf_new_null();
- }
-
- ref = pf->xref_table[obj_num].indirect;
- if (!ref) {
- pdf_obj *obj, *reserved, *imported;
-
- obj = pdf_get_object(pf, obj_num, obj_gen);
- if (!obj) {
- WARN("Could not read object: %lu %u", obj_num, obj_gen);
- return NULL;
- }
-
- /* Fix for circular reference issue
- *
- * Older version of dvipdfmx disallowed the following case of
- * circular reference:
- * obj #1 --> << /Kids [2 0 R] >>
- * obj #2 --> << /Parents [1 0 R] >>
- * The problem is in that dvipdfmx gives new labels to objects after they
- * are completely read.
- */
- reserved = pdf_new_null(); /* for reservation of label */
- pf->xref_table[obj_num].indirect = ref = pdf_new_ref(p, reserved);
- imported = pdf_import_object(obj);
- if (imported) {
- if (imported->label) {
- WARN("Imported object already has a label: obj_id=%lu", imported->label);
- }
- OBJ_OBJ(ref) = imported;
- imported->label = reserved->label;
- imported->generation = reserved->generation;
- reserved->label = 0;
- reserved->generation = 0;
- pdf_release_obj(imported);
- }
-
- pdf_release_obj(reserved);
- pdf_release_obj(obj);
- }
-
- return pdf_link_obj(ref);
-}
-
-/*
- * pdf_import_object recursively copies the object and those
- * referenced by it and changes the indirect references so that
- * they refer to the current output file. New indirect references
- * are remembered, which avoids duplicating objects when they
- * are imported several times.
- */
-pdf_obj *
-pdf_import_object (pdf_obj *object)
-{
- pdf_obj *imported;
- pdf_obj *tmp;
- size_t i;
-
- switch (pdf_obj_typeof(object)) {
-
- case PDF_INDIRECT:
- if (OBJ_FILE(object)) {
- imported = pdf_import_indirect(object);
- } else {
- imported = pdf_link_obj(object);
- }
- break;
-
- case PDF_STREAM:
- {
- pdf_obj *stream_dict;
-
- tmp = pdf_import_object(pdf_stream_dict(object));
- if (!tmp)
- return NULL;
-
- imported = pdf_new_stream(0);
- stream_dict = pdf_stream_dict(imported);
- pdf_merge_dict(stream_dict, tmp);
- pdf_release_obj(tmp);
- pdf_add_stream(imported,
- pdf_stream_dataptr(object),
- pdf_stream_length(object));
- }
- break;
-
- case PDF_DICT:
-
- imported = pdf_new_dict();
- if (pdf_foreach_dict(object, import_dict, imported) < 0) {
- pdf_release_obj(imported);
- return NULL;
- }
-
- break;
-
- case PDF_ARRAY:
-
- imported = pdf_new_array();
- for (i = 0; i < pdf_array_length(object); i++) {
- tmp = pdf_import_object(pdf_get_array(object, i));
- if (!tmp) {
- pdf_release_obj(imported);
- return NULL;
- }
- pdf_add_array(imported, tmp);
- }
- break;
-
- default:
- imported = pdf_link_obj(object);
- }
-
- return imported;
-}
-
-
-/* returns 0 if indirect references point to the same object */
-int
-pdf_compare_reference (pdf_obj *ref1, pdf_obj *ref2)
-{
- pdf_indirect *data1, *data2;
-
- ASSERT(PDF_OBJ_INDIRECTTYPE(ref1) && PDF_OBJ_INDIRECTTYPE(ref2));
-
- data1 = (pdf_indirect *) ref1->data;
- data2 = (pdf_indirect *) ref2->data;
-
- return data1->pf != data2->pf || data1->label != data2->label
- || data1->generation != data2->generation;
-}
-
-int
-pdf_compare_object (pdf_obj *obj1, pdf_obj *obj2)
-{
- int r = 0;
-
- if (!obj1 && !obj2) {
- return 0;
- } else if (!obj1 && obj2) {
- return 1;
- } else if (obj1 && !obj2) {
- return 1;
- } else if (pdf_obj_typeof(obj1) != pdf_obj_typeof(obj2)) {
- return 1;
- }
-
- switch (pdf_obj_typeof(obj1)) {
- case PDF_BOOLEAN:
- r = pdf_boolean_value(obj1) - pdf_boolean_value(obj2);
- break;
- case PDF_NUMBER:
- if (pdf_number_value(obj1) < pdf_number_value(obj2)) {
- r = -1;
- } else if (pdf_number_value(obj1) > pdf_number_value(obj2)) {
- r = 1;
- } else {
- r = 0;
- }
- break;
- case PDF_STRING:
- if (pdf_string_length(obj1) < pdf_string_length(obj2)) {
- r = -1;
- } else if (pdf_string_length(obj1) > pdf_string_length(obj2)) {
- r = 1;
- } else {
- r = memcmp(pdf_string_value(obj1), pdf_string_value(obj2), pdf_string_length(obj1));
- }
- break;
- case PDF_NAME:
- r = strcmp(pdf_name_value(obj1), pdf_name_value(obj2));
- break;
- case PDF_NULL:
- /* Always same */
- r = 0;
- break;
- case PDF_INDIRECT:
- r = pdf_compare_reference(obj1, obj2);
- break;
- case PDF_ARRAY:
- if (pdf_array_length(obj1) < pdf_array_length(obj2)) {
- r = -1;
- } else if (pdf_array_length(obj1) > pdf_array_length(obj2)) {
- r = 1;
- } else {
- int i;
- for (i = 0; r == 0 && i < pdf_array_length(obj1); i++) {
- pdf_obj *v1, *v2;
- v1 = pdf_get_array(obj1, i);
- v2 = pdf_get_array(obj2, i);
- r = pdf_compare_object(v1, v2);
- }
- }
- break;
- case PDF_DICT:
- {
- pdf_obj *keys1, *keys2;
- keys1 = pdf_dict_keys(obj1);
- keys2 = pdf_dict_keys(obj2);
- r = pdf_compare_object(keys1, keys2);
- if (r == 0) {
- int i;
- for (i = 0; r == 0 && i < pdf_array_length(keys1); i++) {
- pdf_obj *key, *v1, *v2;
- key = pdf_get_array(keys1, i);
- v1 = pdf_lookup_dict(obj1, pdf_name_value(key));
- v2 = pdf_lookup_dict(obj2, pdf_name_value(key));
- r = pdf_compare_object(v1, v2);
- }
- }
- pdf_release_obj(keys1);
- pdf_release_obj(keys2);
- }
- break;
- case PDF_STREAM:
- /* Not seriously testing... */
- r = pdf_compare_object(pdf_stream_dict(obj1), pdf_stream_dict(obj2));
- if (r == 0) {
- size_t len1, len2;
- len1 = pdf_stream_length(obj1);
- len2 = pdf_stream_length(obj2);
- if (len1 < len2) {
- r = -1;
- } else if (len1 > len2) {
- r = 1;
- } else {
- r = 0;
- }
- }
- break;
- default:
- r = 1;
- }
-
- return r;
-}