From de569c5d3871e3aea62a0d01e2f86d50f1202591 Mon Sep 17 00:00:00 2001 From: Peter Breitenlohner Date: Mon, 29 Jul 2013 12:13:50 +0000 Subject: dvipdfm-x: Merging dvipdfmx and xdvipdfmx, now just one set of source files git-svn-id: svn://tug.org/texlive/trunk@31302 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/texk/dvipdfm-x/pdfnames.c | 584 +++++++++++++++++++++++++++++++++ 1 file changed, 584 insertions(+) create mode 100644 Build/source/texk/dvipdfm-x/pdfnames.c (limited to 'Build/source/texk/dvipdfm-x/pdfnames.c') diff --git a/Build/source/texk/dvipdfm-x/pdfnames.c b/Build/source/texk/dvipdfm-x/pdfnames.c new file mode 100644 index 00000000000..894382a2591 --- /dev/null +++ b/Build/source/texk/dvipdfm-x/pdfnames.c @@ -0,0 +1,584 @@ +/* + + This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. + + Copyright (C) 2002-2012 by Jin-Hwan Cho and Shunsaku Hirata, + the dvipdfmx project team. + + Copyright (C) 1998, 1999 by Mark A. Wicks + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "system.h" +#include "mem.h" +#include "error.h" +#include "numbers.h" + +/* Hash */ +#include "dpxutil.h" + +#include "pdfobj.h" + +#include "pdfnames.h" + +#include "dvipdfmx.h" + +struct obj_data +{ + pdf_obj *object; + pdf_obj *object_ref; + int reserve; /* 1 if object is not actually defined. */ + int closed; /* 1 if object is closed */ +}; + +char * +printable_key (const char *key, int keylen) +{ +#define MAX_KEY 32 + static char pkey[MAX_KEY+4]; + int i, len; + unsigned char hi, lo; + + for (i = 0, len = 0; + i < keylen && len < MAX_KEY; i++) { + if (isprint(key[i])) { + pkey[len++] = key[i]; + } else { + hi = (key[i] >> 4) & 0xff; + lo = key[i] & 0xff; + pkey[len++] = '#'; + pkey[len++] = (hi < 10) ? hi + '0' : (hi - 10) + 'A'; + pkey[len++] = (lo < 10) ? lo + '0' : (lo - 10) + 'A'; + } + } + pkey[len] = '\0'; + + return (char *) pkey; +} + +static void CDECL +hval_free (void *hval) +{ + struct obj_data *value; + + value = (struct obj_data *) hval; + + if (value->object) { + pdf_release_obj(value->object); + value->object = NULL; + } + if (value->object_ref) { + pdf_release_obj(value->object_ref); + value->object_ref = NULL; + } + value->reserve = 0; + value->closed = 0; + + RELEASE(value); + + return; +} + +struct ht_table * +pdf_new_name_tree (void) +{ + struct ht_table *names; + + names = NEW(1, struct ht_table); + ht_init_table(names, hval_free); + + return names; +} + +#ifdef XETEX +static void +flush_objects (struct ht_table *ht_tab) +{ + struct ht_iter iter; + + if (ht_set_iter(ht_tab, &iter) >= 0) { + do { + char *key; + int keylen; + struct obj_data *value; + + key = ht_iter_getkey(&iter, &keylen); + value = ht_iter_getval(&iter); + if (value->reserve) { + WARN("Unresolved object reference \"%s\" found!!!", + printable_key(key, keylen)); + } + if (value->object) { + pdf_release_obj(value->object); + } + if (value->object_ref) { + pdf_release_obj(value->object_ref); + } + value->object = NULL; + value->object_ref = NULL; + value->reserve = 0; + } while (ht_iter_next(&iter) >= 0); + ht_clear_iter(&iter); + } +} +#else +static void +check_objects_defined (struct ht_table *ht_tab) +{ + struct ht_iter iter; + + if (ht_set_iter(ht_tab, &iter) >= 0) { + do { + char *key; + int keylen; + struct obj_data *value; + + key = ht_iter_getkey(&iter, &keylen); + value = ht_iter_getval(&iter); + ASSERT(value->object); + if (PDF_OBJ_UNDEFINED(value->object)) { + pdf_names_add_object(ht_tab, key, keylen, pdf_new_null()); + WARN("Object @%s used, but not defined. Replaced by null.", + printable_key(key, keylen)); + } + } while (ht_iter_next(&iter) >= 0); + ht_clear_iter(&iter); + } +} +#endif + +void +pdf_delete_name_tree (struct ht_table **names) +{ + ASSERT(names && *names); + +#ifdef XETEX + flush_objects (*names); +#else + check_objects_defined(*names); +#endif + + ht_clear_table(*names); + RELEASE(*names); + *names = NULL; +} + +int +pdf_names_add_object (struct ht_table *names, + const void *key, int keylen, pdf_obj *object) +{ + struct obj_data *value; + + ASSERT(names && object); + + if (!key || keylen < 1) { + WARN("Null string used for name tree key."); + return -1; + } + + value = ht_lookup_table(names, key, keylen); + if (!value) { + value = NEW(1, struct obj_data); + value->object = object; + value->object_ref = NULL; + value->reserve = 0; + value->closed = 0; + ht_append_table(names, key, keylen, value); + } else { +#ifdef XETEX + if (value->reserve) { + /* null object is used for undefined objects */ + pdf_copy_object(value->object, object); + pdf_release_obj(object); /* PLEASE FIX THIS!!! */ +#else + ASSERT(value->object); + if (PDF_OBJ_UNDEFINED(value->object)) { + pdf_transfer_label(object, value->object); + pdf_release_obj(value->object); + value->object = object; +#endif + } else { +#ifdef XETEX + if (value->object || value->object_ref) { + if (pdf_obj_get_verbose()) { + WARN("Object reference with key \"%s\" is in use.", printable_key(key, keylen)); + } + pdf_release_obj(object); + return -1; + } else { + value->object = object; + } +#else + WARN("Object @%s already defined.", printable_key(key, keylen)); + pdf_release_obj(object); + return -1; +#endif + } + value->reserve = 0; + } + + return 0; +} + +#ifdef XETEX +int +pdf_names_add_reference (struct ht_table *names, + const void *key, int keylen, pdf_obj *object_ref) +{ + struct obj_data *value; + + ASSERT(names); + + if (!PDF_OBJ_INDIRECTTYPE(object_ref)) { + WARN("Invalid type: @%s is not reference...", + printable_key(key, keylen)); + return -1; + } + + value = ht_lookup_table(names, key, keylen); + if (!value) { + value = NEW(1, struct obj_data); + value->object = NULL; + value->object_ref = object_ref; + value->reserve = 0; + ht_append_table(names, key, keylen, value); + } else { + if (value->object || value->object_ref) { + WARN("Object reference \"%s\" is in use.", + printable_key(key, keylen)); + WARN("Please close it before redefining."); + return -1; + } else { + value->object = NULL; + value->object_ref = object_ref; + } + value->reserve = 0; + } + + return 0; +} +#endif + +/* + * The following routine returns copies, not the original object. + */ +pdf_obj * +pdf_names_lookup_reference (struct ht_table *names, + const void *key, int keylen) +{ + struct obj_data *value; +#ifndef XETEX + pdf_obj *object; +#endif + + ASSERT(names); + + value = ht_lookup_table(names, key, keylen); + +#ifdef XETEX + if (!value) { + /* Reserve object label */ + value = NEW(1, struct obj_data); + value->object = pdf_new_null(); /* dummy */ + value->object_ref = NULL; + value->reserve = 1; + ht_append_table(names, key, keylen, value); + } + + if (!value->object_ref) { + if (value->object) + value->object_ref = pdf_ref_obj(value->object); + else { + WARN("Object @%s not defined or already closed.", + printable_key(key, keylen)); + } + } + + return pdf_link_obj(value->object_ref); +#else + if (value) { + object = value->object; + } else { + /* A null object as dummy would create problems because as value + * of a dictionary entry, a null object is be equivalent to no entry + * at all. This matters for optimization of PDF destinations. + */ + object = pdf_new_undefined(); + pdf_names_add_object(names, key, keylen, object); + } + + return pdf_ref_obj(object); +#endif +} + +pdf_obj * +pdf_names_lookup_object (struct ht_table *names, + const void *key, int keylen) +{ + struct obj_data *value; + + ASSERT(names); + + value = ht_lookup_table(names, key, keylen); + if (!value || PDF_OBJ_UNDEFINED(value->object)) + return NULL; + if (!value->object) + ERROR("Object @%s not defined or already closed.", + printable_key(key, keylen)); + + return value->object; +} + +int +pdf_names_close_object (struct ht_table *names, + const void *key, int keylen) +{ + struct obj_data *value; + + ASSERT(names); + + value = ht_lookup_table(names, key, keylen); + if (!value) { + WARN("Tried to release nonexistent reference: %s", + printable_key(key, keylen)); + return -1; + } + if (PDF_OBJ_UNDEFINED(value->object) ) { + WARN("Cannot close undefined object @%s.", printable_key(key, keylen)); + return -1; + } + if (!value->object || value->closed) { + WARN("Object @%s already closed.", printable_key(key, keylen)); + return -1; + } + + pdf_release_obj(value->object); + value->object = NULL; + value->closed = 1; + + return 0; +} + +struct named_object +{ + char *key; + int keylen; + pdf_obj *value; +}; + +static int CDECL +cmp_key (const void *d1, const void *d2) +{ + const struct named_object *sd1, *sd2; + int keylen, cmp; + + sd1 = (const struct named_object *) d1; + sd2 = (const struct named_object *) d2; + + if (!sd1->key) + cmp = -1; + else if (!sd2->key) + cmp = 1; + else { + keylen = MIN(sd1->keylen, sd2->keylen); + cmp = memcmp(sd1->key, sd2->key, keylen); + if (!cmp) { + cmp = sd1->keylen - sd2->keylen; + } + } + + return cmp; +} + +#define NAME_CLUSTER 4 +static pdf_obj * +build_name_tree (struct named_object *first, long num_leaves, int is_root) +{ + pdf_obj *result; + int i; + + result = pdf_new_dict(); + /* + * According to PDF Refrence, Third Edition (p.101-102), a name tree + * always has exactly one root node, which contains a SINGLE entry: + * either Kids or Names but not both. If the root node has a Names + * entry, it is the only node in the tree. If it has a Kids entry, + * then each of the remaining nodes is either an intermediate node, + * containing a Limits entry and a Kids entry, or a leaf node, + * containing a Limits entry and a Names entry. + */ + if (!is_root) { + struct named_object *last; + pdf_obj *limits; + + limits = pdf_new_array(); + last = &first[num_leaves - 1]; + pdf_add_array(limits, pdf_new_string(first->key, first->keylen)); + pdf_add_array(limits, pdf_new_string(last->key , last->keylen )); + pdf_add_dict (result, pdf_new_name("Limits"), limits); + } + + if (num_leaves > 0 && + num_leaves <= 2 * NAME_CLUSTER) { + pdf_obj *names; + + /* Create leaf nodes. */ + names = pdf_new_array(); + for (i = 0; i < num_leaves; i++) { + struct named_object *cur; + + cur = &first[i]; + pdf_add_array(names, pdf_new_string(cur->key, cur->keylen)); + switch (PDF_OBJ_TYPEOF(cur->value)) { + case PDF_ARRAY: + case PDF_DICT: + case PDF_STREAM: + case PDF_STRING: + pdf_add_array(names, pdf_ref_obj(cur->value)); + break; + case PDF_OBJ_INVALID: + ERROR("Invalid object...: %s", printable_key(cur->key, cur->keylen)); + default: + pdf_add_array(names, pdf_link_obj(cur->value)); + break; + } + pdf_release_obj(cur->value); + cur->value = NULL; + } + pdf_add_dict(result, pdf_new_name("Names"), names); + } else if (num_leaves > 0) { + pdf_obj *kids; + + /* Intermediate node */ + kids = pdf_new_array(); + for (i = 0; i < NAME_CLUSTER; i++) { + pdf_obj *subtree; + long start, end; + + start = (i*num_leaves) / NAME_CLUSTER; + end = ((i+1)*num_leaves) / NAME_CLUSTER; + subtree = build_name_tree(&first[start], (end - start), 0); + pdf_add_array (kids, pdf_ref_obj(subtree)); + pdf_release_obj(subtree); + } + pdf_add_dict(result, pdf_new_name("Kids"), kids); + } + + return result; +} + +static struct named_object * +flat_table (struct ht_table *ht_tab, long *num_entries, + struct ht_table *filter) +{ + struct named_object *objects; + struct ht_iter iter; + long count; + + ASSERT(ht_tab); + + objects = NEW(ht_tab->count, struct named_object); + count = 0; + if (ht_set_iter(ht_tab, &iter) >= 0) { + do { + char *key; + int keylen; + struct obj_data *value; + + key = ht_iter_getkey(&iter, &keylen); + + if (filter) { + pdf_obj *new_obj = ht_lookup_table(filter, key, keylen); + + if (!new_obj) + continue; + + key = pdf_string_value(new_obj); + keylen = pdf_string_length(new_obj); + } + + value = ht_iter_getval(&iter); +#ifdef XETEX + if (value->reserve) { + WARN("Named object \"%s\" not defined!!!", +#else + ASSERT(value->object); + if (PDF_OBJ_UNDEFINED(value->object)) { + WARN("Object @%s\" not defined!!!", +#endif + printable_key(key, keylen)); + WARN("Replacing with null."); + objects[count].key = (char *) key; + objects[count].keylen = keylen; + objects[count].value = pdf_new_null(); + } else if (value->object_ref) { + objects[count].key = (char *) key; + objects[count].keylen = keylen; + objects[count].value = pdf_link_obj(value->object_ref); + } else if (value->object) { + objects[count].key = (char *) key; + objects[count].keylen = keylen; + objects[count].value = pdf_link_obj(value->object); +#ifdef XETEX + } else { + WARN("Named object \"%s\" not defined!!!", + printable_key(key, keylen)); + WARN("Replacing with null."); + objects[count].key = (char *) key; + objects[count].keylen = keylen; + objects[count].value = pdf_new_null(); +#endif + } + } while (ht_iter_next(&iter) >= 0); + ht_clear_iter(&iter); + } + + *num_entries = count; + objects = RENEW(objects, count, struct named_object); + + return objects; +} + +pdf_obj * +pdf_names_create_tree (struct ht_table *names, long *count, + struct ht_table *filter) +{ + pdf_obj *name_tree; + struct named_object *flat; + + flat = flat_table(names, count, filter); + if (!flat) + name_tree = NULL; + else { + if (is_xetex && *count < 1) + name_tree = NULL; + else { + qsort(flat, *count, sizeof(struct named_object), cmp_key); + name_tree = build_name_tree(flat, *count, 1); + } + RELEASE(flat); + } + + return name_tree; +} -- cgit v1.2.3