filters

Catalog.cc

00001 //========================================================================
00002 //
00003 // Catalog.cc
00004 //
00005 // Copyright 1996-2002 Glyph & Cog, LLC
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 // Catalog
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   // read page tree
00051   catDict.dictLookup("Pages", &pagesDict);
00052   // This should really be isDict("Pages"), but I've seen at least one
00053   // PDF file where the /Type entry is missing.
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   // read named destination dictionary
00087   catDict.dictLookup("Dests", &dests);
00088 
00089   // read root of named destination tree
00090   if (catDict.dictLookup("Names", &obj)->isDict())
00091     obj.dictLookup("Dests", &nameTree);
00092   else
00093     nameTree.initNull();
00094   obj.free();
00095 
00096   // read base URI
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   // get the metadata stream
00106   catDict.dictLookup("Metadata", &metadata);
00107 
00108   // get the structure tree root
00109   catDict.dictLookup("StructTreeRoot", &structTreeRoot);
00110 
00111   // get the outline dictionary
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     // This should really be isDict("Pages"), but I've seen at least one
00222     // PDF file where the /Type entry is missing.
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   // try named destination dictionary then name tree
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   // construct LinkDest
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   // leaf node
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   // root or intermediate node
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   // name was outside of ranges of all kids
00357   if (!done)
00358     obj->initNull();
00359 
00360   return obj;
00361 }
KDE Home | KDE Accessibility Home | Description of Access Keys