00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <aconf.h>
00010
00011 #ifdef USE_GCC_PRAGMAS
00012 #pragma implementation
00013 #endif
00014
00015 #include <limits.h>
00016 #include <stddef.h>
00017 #include "gmem.h"
00018 #include "Object.h"
00019 #include "XRef.h"
00020 #include "Array.h"
00021 #include "Dict.h"
00022 #include "Page.h"
00023 #include "Error.h"
00024 #include "Link.h"
00025 #include "Catalog.h"
00026
00027
00028
00029
00030
00031 Catalog::Catalog(XRef *xrefA) {
00032 Object catDict, pagesDict;
00033 Object obj, obj2;
00034 int numPages0;
00035 int i;
00036
00037 ok = gTrue;
00038 xref = xrefA;
00039 pages = NULL;
00040 pageRefs = NULL;
00041 numPages = pagesSize = 0;
00042 baseURI = NULL;
00043
00044 xref->getCatalog(&catDict);
00045 if (!catDict.isDict()) {
00046 error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
00047 goto err1;
00048 }
00049
00050
00051 catDict.dictLookup("Pages", &pagesDict);
00052
00053
00054 if (!pagesDict.isDict()) {
00055 error(-1, "Top-level pages object is wrong type (%s)",
00056 pagesDict.getTypeName());
00057 goto err2;
00058 }
00059 pagesDict.dictLookup("Count", &obj);
00060 if (!obj.isInt()) {
00061 error(-1, "Page count in top-level pages object is wrong type (%s)",
00062 obj.getTypeName());
00063 goto err3;
00064 }
00065 pagesSize = numPages0 = obj.getInt();
00066 obj.free();
00067 if ((unsigned) pagesSize >= INT_MAX / sizeof(Page *) ||
00068 (unsigned) pagesSize >= INT_MAX / sizeof(Ref)) {
00069 error(-1, "Invalid 'pagesSize'");
00070 ok = gFalse;
00071 return;
00072 }
00073 pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
00074 pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
00075 for (i = 0; i < pagesSize; ++i) {
00076 pages[i] = NULL;
00077 pageRefs[i].num = -1;
00078 pageRefs[i].gen = -1;
00079 }
00080 numPages = readPageTree(pagesDict.getDict(), NULL, 0);
00081 if (numPages != numPages0) {
00082 error(-1, "Page count in top-level pages object is incorrect");
00083 }
00084 pagesDict.free();
00085
00086
00087 catDict.dictLookup("Dests", &dests);
00088
00089
00090 if (catDict.dictLookup("Names", &obj)->isDict())
00091 obj.dictLookup("Dests", &nameTree);
00092 else
00093 nameTree.initNull();
00094 obj.free();
00095
00096
00097 if (catDict.dictLookup("URI", &obj)->isDict()) {
00098 if (obj.dictLookup("Base", &obj2)->isString()) {
00099 baseURI = obj2.getString()->copy();
00100 }
00101 obj2.free();
00102 }
00103 obj.free();
00104
00105
00106 catDict.dictLookup("Metadata", &metadata);
00107
00108
00109 catDict.dictLookup("StructTreeRoot", &structTreeRoot);
00110
00111
00112 catDict.dictLookup("Outlines", &outline);
00113
00114 catDict.free();
00115 return;
00116
00117 err3:
00118 obj.free();
00119 err2:
00120 pagesDict.free();
00121 err1:
00122 catDict.free();
00123 dests.initNull();
00124 nameTree.initNull();
00125 ok = gFalse;
00126 }
00127
00128 Catalog::~Catalog() {
00129 int i;
00130
00131 if (pages) {
00132 for (i = 0; i < pagesSize; ++i) {
00133 if (pages[i]) {
00134 delete pages[i];
00135 }
00136 }
00137 gfree(pages);
00138 gfree(pageRefs);
00139 }
00140 dests.free();
00141 nameTree.free();
00142 if (baseURI) {
00143 delete baseURI;
00144 }
00145 metadata.free();
00146 structTreeRoot.free();
00147 outline.free();
00148 }
00149
00150 GString *Catalog::readMetadata() {
00151 GString *s;
00152 Dict *dict;
00153 Object obj;
00154 int c;
00155
00156 if (!metadata.isStream()) {
00157 return NULL;
00158 }
00159 dict = metadata.streamGetDict();
00160 if (!dict->lookup("Subtype", &obj)->isName("XML")) {
00161 error(-1, "Unknown Metadata type: '%s'",
00162 obj.isName() ? obj.getName() : "???");
00163 }
00164 obj.free();
00165 s = new GString();
00166 metadata.streamReset();
00167 while ((c = metadata.streamGetChar()) != EOF) {
00168 s->append(c);
00169 }
00170 metadata.streamClose();
00171 return s;
00172 }
00173
00174 int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
00175 Object kids;
00176 Object kid;
00177 Object kidRef;
00178 PageAttrs *attrs1, *attrs2;
00179 Page *page;
00180 int i, j;
00181
00182 attrs1 = new PageAttrs(attrs, pagesDict);
00183 pagesDict->lookup("Kids", &kids);
00184 if (!kids.isArray()) {
00185 error(-1, "Kids object (page %d) is wrong type (%s)",
00186 start+1, kids.getTypeName());
00187 goto err1;
00188 }
00189 for (i = 0; i < kids.arrayGetLength(); ++i) {
00190 kids.arrayGet(i, &kid);
00191 if (kid.isDict("Page")) {
00192 attrs2 = new PageAttrs(attrs1, kid.getDict());
00193 page = new Page(xref, start+1, kid.getDict(), attrs2);
00194 if (!page->isOk()) {
00195 ++start;
00196 goto err3;
00197 }
00198 if (start >= pagesSize) {
00199 pagesSize += 32;
00200 if ((unsigned) pagesSize >= INT_MAX / sizeof(Page *) ||
00201 (unsigned) pagesSize >= INT_MAX / sizeof(Ref)) {
00202 error(-1, "Invalid 'pagesSize' parameter.");
00203 goto err3;
00204 }
00205 pages = (Page **)grealloc(pages, pagesSize * sizeof(Page *));
00206 pageRefs = (Ref *)grealloc(pageRefs, pagesSize * sizeof(Ref));
00207 for (j = pagesSize - 32; j < pagesSize; ++j) {
00208 pages[j] = NULL;
00209 pageRefs[j].num = -1;
00210 pageRefs[j].gen = -1;
00211 }
00212 }
00213 pages[start] = page;
00214 kids.arrayGetNF(i, &kidRef);
00215 if (kidRef.isRef()) {
00216 pageRefs[start].num = kidRef.getRefNum();
00217 pageRefs[start].gen = kidRef.getRefGen();
00218 }
00219 kidRef.free();
00220 ++start;
00221
00222
00223 } else if (kid.isDict()) {
00224 if ((start = readPageTree(kid.getDict(), attrs1, start))
00225 < 0)
00226 goto err2;
00227 } else {
00228 error(-1, "Kid object (page %d) is wrong type (%s)",
00229 start+1, kid.getTypeName());
00230 goto err2;
00231 }
00232 kid.free();
00233 }
00234 delete attrs1;
00235 kids.free();
00236 return start;
00237
00238 err3:
00239 delete page;
00240 err2:
00241 kid.free();
00242 err1:
00243 kids.free();
00244 delete attrs1;
00245 ok = gFalse;
00246 return -1;
00247 }
00248
00249 int Catalog::findPage(int num, int gen) {
00250 int i;
00251
00252 for (i = 0; i < numPages; ++i) {
00253 if (pageRefs[i].num == num && pageRefs[i].gen == gen)
00254 return i + 1;
00255 }
00256 return 0;
00257 }
00258
00259 LinkDest *Catalog::findDest(GString *name) {
00260 LinkDest *dest;
00261 Object obj1, obj2;
00262 GBool found;
00263
00264
00265 found = gFalse;
00266 if (dests.isDict()) {
00267 if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
00268 found = gTrue;
00269 else
00270 obj1.free();
00271 }
00272 if (!found && nameTree.isDict()) {
00273 if (!findDestInTree(&nameTree, name, &obj1)->isNull())
00274 found = gTrue;
00275 else
00276 obj1.free();
00277 }
00278 if (!found)
00279 return NULL;
00280
00281
00282 dest = NULL;
00283 if (obj1.isArray()) {
00284 dest = new LinkDest(obj1.getArray());
00285 } else if (obj1.isDict()) {
00286 if (obj1.dictLookup("D", &obj2)->isArray())
00287 dest = new LinkDest(obj2.getArray());
00288 else
00289 error(-1, "Bad named destination value");
00290 obj2.free();
00291 } else {
00292 error(-1, "Bad named destination value");
00293 }
00294 obj1.free();
00295 if (dest && !dest->isOk()) {
00296 delete dest;
00297 dest = NULL;
00298 }
00299
00300 return dest;
00301 }
00302
00303 Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
00304 Object names, name1;
00305 Object kids, kid, limits, low, high;
00306 GBool done, found;
00307 int cmp, i;
00308
00309
00310 if (tree->dictLookup("Names", &names)->isArray()) {
00311 done = found = gFalse;
00312 for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
00313 if (names.arrayGet(i, &name1)->isString()) {
00314 cmp = name->cmp(name1.getString());
00315 if (cmp == 0) {
00316 names.arrayGet(i+1, obj);
00317 found = gTrue;
00318 done = gTrue;
00319 } else if (cmp < 0) {
00320 done = gTrue;
00321 }
00322 name1.free();
00323 }
00324 }
00325 names.free();
00326 if (!found)
00327 obj->initNull();
00328 return obj;
00329 }
00330 names.free();
00331
00332
00333 done = gFalse;
00334 if (tree->dictLookup("Kids", &kids)->isArray()) {
00335 for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
00336 if (kids.arrayGet(i, &kid)->isDict()) {
00337 if (kid.dictLookup("Limits", &limits)->isArray()) {
00338 if (limits.arrayGet(0, &low)->isString() &&
00339 name->cmp(low.getString()) >= 0) {
00340 if (limits.arrayGet(1, &high)->isString() &&
00341 name->cmp(high.getString()) <= 0) {
00342 findDestInTree(&kid, name, obj);
00343 done = gTrue;
00344 }
00345 high.free();
00346 }
00347 low.free();
00348 }
00349 limits.free();
00350 }
00351 kid.free();
00352 }
00353 }
00354 kids.free();
00355
00356
00357 if (!done)
00358 obj->initNull();
00359
00360 return obj;
00361 }