Back to index

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