/* $Header: /home/cvsroot/dvipdfmx/src/pdfnames.c,v 1.1 2004/09/02 12:23:07 hirata Exp $ This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. Copyright (C) 2002 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. */ #if HAVE_CONFIG_H #include "config.h" #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" struct obj_data { pdf_obj *object_ref; pdf_obj *object; int reserve; /* 1 if object is not actually defined. */ }; 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; } struct ht_table * pdf_new_name_tree (void) { struct ht_table *names; names = NEW(1, struct ht_table); ht_init_table(names); return names; } 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); } } static void CDECL hval_free (void *hval) { struct obj_data *value; value = (struct obj_data *) hval; 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; RELEASE(value); return; } void pdf_delete_name_tree (struct ht_table **names) { ASSERT(names && *names); flush_objects (*names); ht_clear_table(*names, hval_free); 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; ht_append_table(names, key, keylen, value); } else { if (value->reserve) { /* null object is used for undefined objects */ pdf_copy_object(value->object, object); pdf_release_obj(object); /* PLEASE FIX THIS!!! */ } else { if (value->object || value->object_ref) { WARN("Object reference with key \"%s\" is in use.", printable_key(key, keylen)); return -1; } else { value->object = object; } } value->reserve = 0; } return 0; } 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; } /* * 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; ASSERT(names); value = ht_lookup_table(names, key, keylen); /* Reserve object label */ if (!value) { 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); } 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) return NULL; else if (!value->object) { WARN("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 (value->object) { pdf_release_obj(value->object); value->object = NULL; } else { WARN("Trying to close object @%s twice?", printable_key(key, keylen)); return -1; } return 0; } struct named_object { char *key; int keylen; pdf_obj *value; }; static int CDECL cmp_key (const void *d1, const void *d2) { struct named_object *sd1, *sd2; int keylen, cmp; sd1 = (struct named_object *) d1; sd2 = (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)); break; 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 named_object *objects; struct ht_iter iter; long count; ASSERT(ht_tab); *num_entries = count = ht_tab->count; objects = NEW(count, struct named_object); if (ht_set_iter(ht_tab, &iter) >= 0) { do { char *key; int keylen; struct obj_data *value; count--; key = ht_iter_getkey(&iter, &keylen); value = ht_iter_getval(&iter); if (value->reserve) { 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(); } 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); } 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(); } } while (ht_iter_next(&iter) >= 0 && count > 0); ht_clear_iter(&iter); } return objects; } pdf_obj * pdf_names_create_tree (struct ht_table *names) { pdf_obj *name_tree; struct named_object *flat; long count; flat = flat_table(names, &count); if (!flat) name_tree = NULL; else { if (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; }