Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

Catalog.cc

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

Generated on Sat Nov 5 16:18:13 2005 for OPIE by  doxygen 1.4.2