diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 47d455dd55be855e4cc691c32f687f723d9247ee (patch) | |
tree | 52e236aaa2576bdb3840ebede26619692fed6d7d /kpdf/xpdf/xpdf/Catalog.cc | |
download | tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.tar.gz tdegraphics-47d455dd55be855e4cc691c32f687f723d9247ee.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegraphics@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kpdf/xpdf/xpdf/Catalog.cc')
-rw-r--r-- | kpdf/xpdf/xpdf/Catalog.cc | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/kpdf/xpdf/xpdf/Catalog.cc b/kpdf/xpdf/xpdf/Catalog.cc new file mode 100644 index 00000000..198703a4 --- /dev/null +++ b/kpdf/xpdf/xpdf/Catalog.cc @@ -0,0 +1,443 @@ +//======================================================================== +// +// Catalog.cc +// +// Copyright 1996-2007 Glyph & Cog, LLC +// +//======================================================================== + +#include <aconf.h> + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include <stddef.h> +#include <stdlib.h> +#include "gmem.h" +#include "Object.h" +#include "XRef.h" +#include "Array.h" +#include "Dict.h" +#include "Page.h" +#include "Error.h" +#include "Link.h" +#include "Catalog.h" + +//------------------------------------------------------------------------ +// Catalog +//------------------------------------------------------------------------ + +Catalog::Catalog(XRef *xrefA) { + Object catDict, pagesDict, pagesDictRef; + Object obj, obj2; + char *alreadyRead; + int numPages0; + int i; + + ok = gTrue; + xref = xrefA; + pages = NULL; + pageRefs = NULL; + numPages = pagesSize = 0; + baseURI = NULL; + + xref->getCatalog(&catDict); + if (!catDict.isDict()) { + error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName()); + goto err1; + } + + // read page tree + catDict.dictLookup("Pages", &pagesDict); + // This should really be isDict("Pages"), but I've seen at least one + // PDF file where the /Type entry is missing. + if (!pagesDict.isDict()) { + error(-1, "Top-level pages object is wrong type (%s)", + pagesDict.getTypeName()); + goto err2; + } + pagesDict.dictLookup("Count", &obj); + // some PDF files actually use real numbers here ("/Count 9.0") + if (!obj.isNum()) { + error(-1, "Page count in top-level pages object is wrong type (%s)", + obj.getTypeName()); + goto err3; + } + pagesSize = numPages0 = (int)obj.getNum(); + obj.free(); + pages = (Page **)gmallocn(pagesSize, sizeof(Page *)); + pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref)); + for (i = 0; i < pagesSize; ++i) { + pages[i] = NULL; + pageRefs[i].num = -1; + pageRefs[i].gen = -1; + } + alreadyRead = (char *)gmalloc(xref->getNumObjects()); + memset(alreadyRead, 0, xref->getNumObjects()); + if (catDict.dictLookupNF("Pages", &pagesDictRef)->isRef() && + pagesDictRef.getRefNum() >= 0 && + pagesDictRef.getRefNum() < xref->getNumObjects()) { + alreadyRead[pagesDictRef.getRefNum()] = 1; + } + pagesDictRef.free(); + numPages = readPageTree(pagesDict.getDict(), NULL, 0, alreadyRead); + gfree(alreadyRead); + if (numPages != numPages0) { + error(-1, "Page count in top-level pages object is incorrect"); + } + pagesDict.free(); + + // read named destination dictionary + catDict.dictLookup("Dests", &dests); + + // read root of named destination tree + if (catDict.dictLookup("Names", &obj)->isDict()) { + obj.dictLookup("Dests", &obj2); + destNameTree.init(xref, &obj2); + obj2.free(); + } + obj.free(); + + // read base URI + if (catDict.dictLookup("URI", &obj)->isDict()) { + if (obj.dictLookup("Base", &obj2)->isString()) { + baseURI = obj2.getString()->copy(); + } + obj2.free(); + } + obj.free(); + + // read page mode + if (catDict.dictLookup("PageMode", &obj)->isName()) { + if (strcmp(obj.getName(), "UseNone") == 0) + pageMode = UseNone; + else if (strcmp(obj.getName(), "UseOutlines") == 0) + pageMode = UseOutlines; + else if (strcmp(obj.getName(), "UseThumbs") == 0) + pageMode = UseThumbs; + else if (strcmp(obj.getName(), "FullScreen") == 0) + pageMode = FullScreen; + else if (strcmp(obj.getName(), "UseOC") == 0) + pageMode = UseOC; + else + pageMode = UseNone; + } else { + pageMode = UseNone; + } + obj.free(); + + // get the metadata stream + catDict.dictLookup("Metadata", &metadata); + + // get the structure tree root + catDict.dictLookup("StructTreeRoot", &structTreeRoot); + + // get the outline dictionary + catDict.dictLookup("Outlines", &outline); + + // get the AcroForm dictionary + catDict.dictLookup("AcroForm", &acroForm); + + catDict.free(); + return; + + err3: + obj.free(); + err2: + pagesDict.free(); + err1: + catDict.free(); + dests.initNull(); + ok = gFalse; +} + +Catalog::~Catalog() { + int i; + + if (pages) { + for (i = 0; i < pagesSize; ++i) { + if (pages[i]) { + delete pages[i]; + } + } + gfree(pages); + gfree(pageRefs); + } + dests.free(); + destNameTree.free(); + if (baseURI) { + delete baseURI; + } + metadata.free(); + structTreeRoot.free(); + outline.free(); + acroForm.free(); +} + +GString *Catalog::readMetadata() { + GString *s; + Dict *dict; + Object obj; + int c; + + if (!metadata.isStream()) { + return NULL; + } + dict = metadata.streamGetDict(); + if (!dict->lookup("Subtype", &obj)->isName("XML")) { + error(-1, "Unknown Metadata type: '%s'", + obj.isName() ? obj.getName() : "???"); + } + obj.free(); + s = new GString(); + metadata.streamReset(); + while ((c = metadata.streamGetChar()) != EOF) { + s->append(c); + } + metadata.streamClose(); + return s; +} + +int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start, + char *alreadyRead) { + Object kids; + Object kid; + Object kidRef; + PageAttrs *attrs1, *attrs2; + Page *page; + int i, j; + + attrs1 = new PageAttrs(attrs, pagesDict); + pagesDict->lookup("Kids", &kids); + if (!kids.isArray()) { + error(-1, "Kids object (page %d) is wrong type (%s)", + start+1, kids.getTypeName()); + goto err1; + } + for (i = 0; i < kids.arrayGetLength(); ++i) { + kids.arrayGetNF(i, &kidRef); + if (kidRef.isRef() && + kidRef.getRefNum() >= 0 && + kidRef.getRefNum() < xref->getNumObjects()) { + if (alreadyRead[kidRef.getRefNum()]) { + error(-1, "Loop in Pages tree"); + kidRef.free(); + continue; + } + alreadyRead[kidRef.getRefNum()] = 1; + } + kids.arrayGet(i, &kid); + if (kid.isDict("Page")) { + attrs2 = new PageAttrs(attrs1, kid.getDict()); + page = new Page(xref, start+1, kid.getDict(), attrs2); + if (!page->isOk()) { + ++start; + goto err3; + } + if (start >= pagesSize) { + pagesSize += 32; + pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *)); + pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref)); + for (j = pagesSize - 32; j < pagesSize; ++j) { + pages[j] = NULL; + pageRefs[j].num = -1; + pageRefs[j].gen = -1; + } + } + pages[start] = page; + if (kidRef.isRef()) { + pageRefs[start].num = kidRef.getRefNum(); + pageRefs[start].gen = kidRef.getRefGen(); + } + ++start; + // This should really be isDict("Pages"), but I've seen at least one + // PDF file where the /Type entry is missing. + } else if (kid.isDict()) { + if ((start = readPageTree(kid.getDict(), attrs1, start, alreadyRead)) + < 0) + goto err2; + } else { + error(-1, "Kid object (page %d) is wrong type (%s)", + start+1, kid.getTypeName()); + } + kid.free(); + kidRef.free(); + } + delete attrs1; + kids.free(); + return start; + + err3: + delete page; + err2: + kid.free(); + err1: + kids.free(); + delete attrs1; + ok = gFalse; + return -1; +} + +int Catalog::findPage(int num, int gen) { + int i; + + for (i = 0; i < numPages; ++i) { + if (pageRefs[i].num == num && pageRefs[i].gen == gen) + return i + 1; + } + return 0; +} + +LinkDest *Catalog::findDest(GString *name) { + LinkDest *dest; + Object obj1, obj2; + GBool found; + + // try named destination dictionary then name tree + found = gFalse; + if (dests.isDict()) { + if (!dests.dictLookup(name->getCString(), &obj1)->isNull()) + found = gTrue; + else + obj1.free(); + } + if (!found) { + if (destNameTree.lookup(name, &obj1)) + found = gTrue; + else + obj1.free(); + } + if (!found) + return NULL; + + // construct LinkDest + dest = NULL; + if (obj1.isArray()) { + dest = new LinkDest(obj1.getArray()); + } else if (obj1.isDict()) { + if (obj1.dictLookup("D", &obj2)->isArray()) + dest = new LinkDest(obj2.getArray()); + else + error(-1, "Bad named destination value"); + obj2.free(); + } else { + error(-1, "Bad named destination value"); + } + obj1.free(); + if (dest && !dest->isOk()) { + delete dest; + dest = NULL; + } + + return dest; +} + +NameTree::NameTree() +{ + size = 0; + length = 0; + entries = NULL; +} + +NameTree::Entry::Entry(Array *array, int index) { + if (!array->getString(index, &name) || !array->getNF(index + 1, &value)) + error(-1, "Invalid page tree"); +} + +NameTree::Entry::~Entry() { + value.free(); +} + +void NameTree::addEntry(Entry *entry) +{ + if (length == size) { + if (length == 0) { + size = 8; + } else { + size *= 2; + } + entries = (Entry **) grealloc (entries, sizeof (Entry *) * size); + } + + entries[length] = entry; + ++length; +} + +void NameTree::init(XRef *xrefA, Object *tree) { + xref = xrefA; + parse(tree); +} + +void NameTree::parse(Object *tree) { + Object names; + Object kids, kid; + int i; + + if (!tree->isDict()) + return; + + // leaf node + if (tree->dictLookup("Names", &names)->isArray()) { + for (i = 0; i < names.arrayGetLength(); i += 2) { + NameTree::Entry *entry; + + entry = new Entry(names.getArray(), i); + addEntry(entry); + } + } + names.free(); + + // root or intermediate node + if (tree->dictLookup("Kids", &kids)->isArray()) { + for (i = 0; i < kids.arrayGetLength(); ++i) { + if (kids.arrayGet(i, &kid)->isDict()) + parse(&kid); + kid.free(); + } + } + kids.free(); +} + +int NameTree::Entry::cmp(const void *voidKey, const void *voidEntry) +{ + GString *key = (GString *) voidKey; + Entry *entry = *(NameTree::Entry **) voidEntry; + + return key->cmp(&entry->name); +} + +GBool NameTree::lookup(GString *name, Object *obj) +{ + Entry *entry; + + Entry **e = (Entry **) bsearch(name, entries, + length, sizeof(Entry *), Entry::cmp); + if (e) entry = *e; + else + { + error(-1, "failed to look up %s\n", name->getCString()); + obj->initNull(); + return gFalse; + } + if (entry != NULL) { + entry->value.fetch(xref, obj); + return gTrue; + } else { + error(-1, "failed to look up %s\n", name->getCString()); + + obj->initNull(); + + return gFalse; + } +} + +void NameTree::free() +{ + int i; + + for (i = 0; i < length; i++) + delete entries[i]; + + gfree(entries); +} |