mirror of
https://invent.kde.org/graphics/okular
synced 2024-09-19 07:51:27 +00:00
3abd57ea99
- Remove not used files of xpdf (images, etc) that we don't need to distribute Please test if your kpdf behaves equally that older versions (it should only have somewhat better support to PDF 1.5) svn path=/trunk/kdegraphics/kpdf/; revision=339928
351 lines
7.8 KiB
C++
351 lines
7.8 KiB
C++
//========================================================================
|
|
//
|
|
// Catalog.cc
|
|
//
|
|
// Copyright 1996-2003 Glyph & Cog, LLC
|
|
//
|
|
//========================================================================
|
|
|
|
#include <aconf.h>
|
|
|
|
#ifdef USE_GCC_PRAGMAS
|
|
#pragma implementation
|
|
#endif
|
|
|
|
#include <stddef.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;
|
|
Object obj, obj2;
|
|
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 **)gmalloc(pagesSize * sizeof(Page *));
|
|
pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
|
|
for (i = 0; i < pagesSize; ++i) {
|
|
pages[i] = NULL;
|
|
pageRefs[i].num = -1;
|
|
pageRefs[i].gen = -1;
|
|
}
|
|
numPages = readPageTree(pagesDict.getDict(), NULL, 0);
|
|
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", &nameTree);
|
|
else
|
|
nameTree.initNull();
|
|
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();
|
|
|
|
// 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);
|
|
|
|
catDict.free();
|
|
return;
|
|
|
|
err3:
|
|
obj.free();
|
|
err2:
|
|
pagesDict.free();
|
|
err1:
|
|
catDict.free();
|
|
dests.initNull();
|
|
nameTree.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();
|
|
nameTree.free();
|
|
if (baseURI) {
|
|
delete baseURI;
|
|
}
|
|
metadata.free();
|
|
structTreeRoot.free();
|
|
outline.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) {
|
|
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.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 **)grealloc(pages, pagesSize * sizeof(Page *));
|
|
pageRefs = (Ref *)grealloc(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;
|
|
kids.arrayGetNF(i, &kidRef);
|
|
if (kidRef.isRef()) {
|
|
pageRefs[start].num = kidRef.getRefNum();
|
|
pageRefs[start].gen = kidRef.getRefGen();
|
|
}
|
|
kidRef.free();
|
|
++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))
|
|
< 0)
|
|
goto err2;
|
|
} else {
|
|
error(-1, "Kid object (page %d) is wrong type (%s)",
|
|
start+1, kid.getTypeName());
|
|
goto err2;
|
|
}
|
|
kid.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 && nameTree.isDict()) {
|
|
if (!findDestInTree(&nameTree, name, &obj1)->isNull())
|
|
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;
|
|
}
|
|
|
|
Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
|
|
Object names, name1;
|
|
Object kids, kid, limits, low, high;
|
|
GBool done, found;
|
|
int cmp, i;
|
|
|
|
// leaf node
|
|
if (tree->dictLookup("Names", &names)->isArray()) {
|
|
done = found = gFalse;
|
|
for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
|
|
if (names.arrayGet(i, &name1)->isString()) {
|
|
cmp = name->cmp(name1.getString());
|
|
if (cmp == 0) {
|
|
names.arrayGet(i+1, obj);
|
|
found = gTrue;
|
|
done = gTrue;
|
|
} else if (cmp < 0) {
|
|
done = gTrue;
|
|
}
|
|
}
|
|
name1.free();
|
|
}
|
|
names.free();
|
|
if (!found)
|
|
obj->initNull();
|
|
return obj;
|
|
}
|
|
names.free();
|
|
|
|
// root or intermediate node
|
|
done = gFalse;
|
|
if (tree->dictLookup("Kids", &kids)->isArray()) {
|
|
for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
|
|
if (kids.arrayGet(i, &kid)->isDict()) {
|
|
if (kid.dictLookup("Limits", &limits)->isArray()) {
|
|
if (limits.arrayGet(0, &low)->isString() &&
|
|
name->cmp(low.getString()) >= 0) {
|
|
if (limits.arrayGet(1, &high)->isString() &&
|
|
name->cmp(high.getString()) <= 0) {
|
|
findDestInTree(&kid, name, obj);
|
|
done = gTrue;
|
|
}
|
|
high.free();
|
|
}
|
|
low.free();
|
|
}
|
|
limits.free();
|
|
}
|
|
kid.free();
|
|
}
|
|
}
|
|
kids.free();
|
|
|
|
// name was outside of ranges of all kids
|
|
if (!done)
|
|
obj->initNull();
|
|
|
|
return obj;
|
|
}
|