source: trunk/poppler/mypoppler/poppler/Catalog.cc @ 2

Last change on this file since 2 was 2, checked in by Eugene Romanenko, 15 years ago

First import

File size: 13.5 KB
Line 
1//========================================================================
2//
3// Catalog.cc
4//
5// Copyright 1996-2003 Glyph & Cog, LLC
6//
7//========================================================================
8
9#include <config.h>
10
11#ifdef USE_GCC_PRAGMAS
12#pragma implementation
13#endif
14
15#include <stddef.h>
16#include <stdlib.h>
17#include "goo/gmem.h"
18#include "Object.h"
19#include "XRef.h"
20#include "Array.h"
21#include "Dict.h"
22#include "Page.h"
23#include "Error.h"
24#include "Link.h"
25#include "PageLabelInfo.h"
26#include "UGooString.h"
27#include "Catalog.h"
28
29//------------------------------------------------------------------------
30// Catalog
31//------------------------------------------------------------------------
32
33Catalog::Catalog(XRef *xrefA) {
34  Object catDict, pagesDict;
35  Object obj, obj2;
36  int numPages0;
37  int i;
38
39  ok = gTrue;
40  xref = xrefA;
41  pages = NULL;
42  pageRefs = NULL;
43  numPages = pagesSize = 0;
44  baseURI = NULL;
45  pageLabelInfo = NULL;
46
47  xref->getCatalog(&catDict);
48  if (!catDict.isDict()) {
49    error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
50    goto err1;
51  }
52
53  // read page tree
54  catDict.dictLookup("Pages", &pagesDict);
55  // This should really be isDict("Pages"), but I've seen at least one
56  // PDF file where the /Type entry is missing.
57  if (!pagesDict.isDict()) {
58    error(-1, "Top-level pages object is wrong type (%s)",
59          pagesDict.getTypeName());
60    goto err2;
61  }
62  pagesDict.dictLookup("Count", &obj);
63  // some PDF files actually use real numbers here ("/Count 9.0")
64  if (!obj.isNum()) {
65    error(-1, "Page count in top-level pages object is wrong type (%s)",
66          obj.getTypeName());
67    goto err3;
68  }
69  pagesSize = numPages0 = (int)obj.getNum();
70  obj.free();
71  pages = (Page **)gmallocn(pagesSize, sizeof(Page *));
72  pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref));
73  for (i = 0; i < pagesSize; ++i) {
74    pages[i] = NULL;
75    pageRefs[i].num = -1;
76    pageRefs[i].gen = -1;
77  }
78  numPages = readPageTree(pagesDict.getDict(), NULL, 0);
79  if (numPages != numPages0) {
80    error(-1, "Page count in top-level pages object is incorrect");
81  }
82  pagesDict.free();
83
84  // read named destination dictionary
85  catDict.dictLookup("Dests", &dests);
86
87  // read root of named destination tree - PDF1.6 table 3.28
88  if (catDict.dictLookup("Names", &obj)->isDict()) {
89    obj.dictLookup("Dests", &obj2);
90    destNameTree.init(xref, &obj2);
91    obj2.free();
92    obj.dictLookup("EmbeddedFiles", &obj2);
93    embeddedFileNameTree.init(xref, &obj2);
94    obj2.free();
95  }
96  obj.free();
97
98  if (catDict.dictLookup("PageLabels", &obj)->isDict())
99    pageLabelInfo = new PageLabelInfo(&obj, numPages);
100  obj.free();
101
102  // read page mode
103  pageMode = pageModeNone;
104  if (catDict.dictLookup("PageMode", &obj)->isName()) {
105    if (obj.isName("UseNone"))
106      pageMode = pageModeNone;
107    else if (obj.isName("UseOutlines"))
108      pageMode = pageModeOutlines;
109    else if (obj.isName("UseThumbs"))
110      pageMode = pageModeThumbs;
111    else if (obj.isName("FullScreen"))
112      pageMode = pageModeFullScreen;
113    else if (obj.isName("UseOC"))
114      pageMode = pageModeOC;
115    else if (obj.isName("UseAttachments"))
116      pageMode = pageModeAttach;
117  }
118  obj.free();
119
120  pageLayout = pageLayoutNone;
121  if (catDict.dictLookup("PageLayout", &obj)->isName()) {
122    if (obj.isName("SinglePage"))
123      pageLayout = pageLayoutSinglePage;
124    if (obj.isName("OneColumn"))
125      pageLayout = pageLayoutOneColumn;
126    if (obj.isName("TwoColumnLeft"))
127      pageLayout = pageLayoutTwoColumnLeft;
128    if (obj.isName("TwoColumnRight"))
129      pageLayout = pageLayoutTwoColumnRight;
130    if (obj.isName("TwoPageLeft"))
131      pageLayout = pageLayoutTwoPageLeft;
132    if (obj.isName("TwoPageRight"))
133      pageLayout = pageLayoutTwoPageRight;
134  }
135  obj.free();
136
137  // read base URI
138  if (catDict.dictLookup("URI", &obj)->isDict()) {
139    if (obj.dictLookup("Base", &obj2)->isString()) {
140      baseURI = obj2.getString()->copy();
141    }
142    obj2.free();
143  }
144  obj.free();
145
146  // get the metadata stream
147  catDict.dictLookup("Metadata", &metadata);
148
149  // get the structure tree root
150  catDict.dictLookup("StructTreeRoot", &structTreeRoot);
151
152  // get the outline dictionary
153  catDict.dictLookup("Outlines", &outline);
154
155  // get the AcroForm dictionary
156  catDict.dictLookup("AcroForm", &acroForm);
157
158  catDict.free();
159  return;
160
161 err3:
162  obj.free();
163 err2:
164  pagesDict.free();
165 err1:
166  catDict.free();
167  dests.initNull();
168  ok = gFalse;
169}
170
171Catalog::~Catalog() {
172  int i;
173
174  if (pages) {
175    for (i = 0; i < pagesSize; ++i) {
176      if (pages[i]) {
177        delete pages[i];
178      }
179    }
180    gfree(pages);
181    gfree(pageRefs);
182  }
183  dests.free();
184  destNameTree.free();
185  embeddedFileNameTree.free();
186  if (baseURI) {
187    delete baseURI;
188  }
189  delete pageLabelInfo;
190  metadata.free();
191  structTreeRoot.free();
192  outline.free();
193  acroForm.free();
194}
195
196GooString *Catalog::readMetadata() {
197  GooString *s;
198  Dict *dict;
199  Object obj;
200  int c;
201
202  if (!metadata.isStream()) {
203    return NULL;
204  }
205  dict = metadata.streamGetDict();
206  if (!dict->lookup("Subtype", &obj)->isName("XML")) {
207    error(-1, "Unknown Metadata type: '%s'",
208          obj.isName() ? obj.getName() : "???");
209  }
210  obj.free();
211  s = new GooString();
212  metadata.streamReset();
213  while ((c = metadata.streamGetChar()) != EOF) {
214    s->append(c);
215  }
216  metadata.streamClose();
217  return s;
218}
219
220int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
221  Object kids;
222  Object kid;
223  Object kidRef;
224  PageAttrs *attrs1, *attrs2;
225  Page *page;
226  int i, j;
227
228  attrs1 = new PageAttrs(attrs, pagesDict);
229  pagesDict->lookup("Kids", &kids);
230  if (!kids.isArray()) {
231    error(-1, "Kids object (page %d) is wrong type (%s)",
232          start+1, kids.getTypeName());
233    goto err1;
234  }
235  for (i = 0; i < kids.arrayGetLength(); ++i) {
236    kids.arrayGet(i, &kid);
237    if (kid.isDict("Page")) {
238      attrs2 = new PageAttrs(attrs1, kid.getDict());
239      page = new Page(xref, start+1, kid.getDict(), attrs2);
240      if (!page->isOk()) {
241        ++start;
242        goto err3;
243      }
244      if (start >= pagesSize) {
245        pagesSize += 32;
246        pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *));
247        pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref));
248        for (j = pagesSize - 32; j < pagesSize; ++j) {
249          pages[j] = NULL;
250          pageRefs[j].num = -1;
251          pageRefs[j].gen = -1;
252        }
253      }
254      pages[start] = page;
255      kids.arrayGetNF(i, &kidRef);
256      if (kidRef.isRef()) {
257        pageRefs[start].num = kidRef.getRefNum();
258        pageRefs[start].gen = kidRef.getRefGen();
259      }
260      kidRef.free();
261      ++start;
262    // This should really be isDict("Pages"), but I've seen at least one
263    // PDF file where the /Type entry is missing.
264    } else if (kid.isDict()) {
265      if ((start = readPageTree(kid.getDict(), attrs1, start))
266          < 0)
267        goto err2;
268    } else {
269      error(-1, "Kid object (page %d) is wrong type (%s)",
270            start+1, kid.getTypeName());
271    }
272    kid.free();
273  }
274  delete attrs1;
275  kids.free();
276  return start;
277
278 err3:
279  delete page;
280 err2:
281  kid.free();
282 err1:
283  kids.free();
284  delete attrs1;
285  ok = gFalse;
286  return -1;
287}
288
289int Catalog::findPage(int num, int gen) {
290  int i;
291
292  for (i = 0; i < numPages; ++i) {
293    if (pageRefs[i].num == num && pageRefs[i].gen == gen)
294      return i + 1;
295  }
296  return 0;
297}
298
299LinkDest *Catalog::findDest(UGooString *name) {
300  LinkDest *dest;
301  Object obj1, obj2;
302  GBool found;
303
304  // try named destination dictionary then name tree
305  found = gFalse;
306  if (dests.isDict()) {
307    if (!dests.dictLookup(*name, &obj1)->isNull())
308      found = gTrue;
309    else
310      obj1.free();
311  }
312  if (!found) {
313    if (destNameTree.lookup(name, &obj1))
314      found = gTrue;
315    else
316      obj1.free();
317  }
318  if (!found)
319    return NULL;
320
321  // construct LinkDest
322  dest = NULL;
323  if (obj1.isArray()) {
324    dest = new LinkDest(obj1.getArray());
325  } else if (obj1.isDict()) {
326    if (obj1.dictLookup("D", &obj2)->isArray())
327      dest = new LinkDest(obj2.getArray());
328    else
329      error(-1, "Bad named destination value");
330    obj2.free();
331  } else {
332    error(-1, "Bad named destination value");
333  }
334  obj1.free();
335  if (dest && !dest->isOk()) {
336    delete dest;
337    dest = NULL;
338  }
339
340  return dest;
341}
342
343EmbFile *Catalog::embeddedFile(int i)
344{
345    Object efDict;
346    Object fileSpec;
347    Object fileDesc;
348    Object paramDict;
349    Object paramObj;
350    Object strObj;
351    Object obj, obj2;
352    obj = embeddedFileNameTree.getValue(i);
353    GooString *fileName = new GooString();
354    char *descString = embeddedFileNameTree.getName(i)->getCString();
355    GooString *desc = new GooString(descString);
356    delete[] descString;
357    GooString *createDate = new GooString();
358    GooString *modDate = new GooString();
359    Stream *efStream;
360    if (obj.isRef()) {
361        if (obj.fetch(xref, &efDict)->isDict()) {
362            // efDict matches Table 3.40 in the PDF1.6 spec
363            efDict.dictLookup("F", &fileSpec);
364            if (fileSpec.isString()) {
365                delete fileName;
366                fileName = new GooString(fileSpec.getString());
367            }
368            fileSpec.free();
369
370            // the logic here is that the description from the name
371            // dictionary is used if we don't have a more specific
372            // description - see the Note: on page 157 of the PDF1.6 spec
373            efDict.dictLookup("Desc", &fileDesc);
374            if (fileDesc.isString()) {
375                delete desc;
376                desc = new GooString(fileDesc.getString());
377            } else {
378                efDict.dictLookup("Description", &fileDesc);
379                if (fileDesc.isString()) {
380                    delete desc;
381                    desc = new GooString(fileDesc.getString());
382                }
383            }
384            fileDesc.free();
385           
386            efDict.dictLookup("EF", &obj2);
387            if (obj2.isDict()) {
388                // This gives us the raw data stream bytes
389
390                obj2.dictLookup("F", &strObj);
391                if (strObj.isStream()) {
392                    efStream = strObj.getStream();
393                }
394
395                // dataDict corresponds to Table 3.41 in the PDF1.6 spec.
396                Dict *dataDict = efStream->getDict();
397
398                // subtype is normally mimetype. You can extract it with code like this:
399                // Object subtypeName;
400                // dataDict->lookup( "Subtype", &subtypeName );
401                // It is optional, so this will sometimes return a null object
402                // if (subtypeName.isName()) {
403                //        std::cout << "got subtype name: " << subtypeName.getName() << std::endl;
404                // }
405
406                // paramDict corresponds to Table 3.42 in the PDF1.6 spec
407                Object paramDict;
408                dataDict->lookup( "Params", &paramDict );
409                if (paramDict.isDict()) {
410                    paramDict.dictLookup("ModDate", &paramObj);
411                    if (paramObj.isString()) {
412                        delete modDate;
413                        modDate = new GooString(paramObj.getString());
414                    }
415                    paramObj.free();
416                    paramDict.dictLookup("CreationDate", &paramObj);
417                    if (paramObj.isString()) {
418                        delete createDate;
419                        createDate = new GooString(paramObj.getString());
420                    }
421                    paramObj.free();
422                }
423                paramDict.free();
424            }
425            efDict.free();
426            obj2.free();
427        }
428    }
429    EmbFile *embeddedFile = new EmbFile(fileName, desc, createDate, modDate, strObj);
430    strObj.free();
431    return embeddedFile;
432}
433
434NameTree::NameTree(void)
435{
436  size = 0;
437  length = 0;
438  entries = NULL;
439}
440
441NameTree::Entry::Entry(Array *array, int index) {
442    GooString n;
443    if (!array->getString(index, &n) || !array->getNF(index + 1, &value))
444        error(-1, "Invalid page tree");
445    name = new UGooString(n);
446}
447
448NameTree::Entry::~Entry() {
449  value.free();
450  delete name;
451}
452
453void NameTree::addEntry(Entry *entry)
454{
455  if (length == size) {
456    if (length == 0) {
457      size = 8;
458    } else {
459      size *= 2;
460    }
461    entries = (Entry **) grealloc (entries, sizeof (Entry *) * size);
462  }
463
464  entries[length] = entry;
465  ++length;
466}
467
468void NameTree::init(XRef *xrefA, Object *tree) {
469  xref = xrefA;
470  parse(tree);
471}
472
473void NameTree::parse(Object *tree) {
474  Object names;
475  Object kids, kid;
476  int i;
477
478  if (!tree->isDict())
479    return;
480
481  // leaf node
482  if (tree->dictLookup("Names", &names)->isArray()) {
483    for (i = 0; i < names.arrayGetLength(); i += 2) {
484      NameTree::Entry *entry;
485
486      entry = new Entry(names.getArray(), i);
487      addEntry(entry);
488    }
489  }
490
491  // root or intermediate node
492  if (tree->dictLookup("Kids", &kids)->isArray()) {
493    for (i = 0; i < kids.arrayGetLength(); ++i) {
494      if (kids.arrayGet(i, &kid)->isDict())
495        parse(&kid);
496      kid.free();
497    }
498  }
499  kids.free();
500}
501
502int NameTree::Entry::cmp(const void *voidKey, const void *voidEntry)
503{
504  UGooString *key = (UGooString *) voidKey;
505  Entry *entry = *(NameTree::Entry **) voidEntry;
506
507  return key->cmp(entry->name);
508}
509
510GBool NameTree::lookup(UGooString *name, Object *obj)
511{
512  Entry **entry;
513
514  entry = (Entry **) bsearch(name, entries,
515                             length, sizeof(Entry *), Entry::cmp);
516  if (entry != NULL) {
517    (*entry)->value.fetch(xref, obj);
518    return gTrue;
519  } else {
520    printf("failed to look up %s\n", name->getCString());
521    obj->initNull();
522    return gFalse;
523  }
524}
525
526Object NameTree::getValue(int index)
527{
528  if (index < length) {
529    return entries[index]->value;
530  } else {
531    return Object();
532  }
533}
534
535UGooString *NameTree::getName(int index)
536{
537    if (index < length) {
538        return entries[index]->name;
539    } else {
540        return NULL;
541    }
542}
543
544void NameTree::free()
545{
546  int i;
547
548  for (i = 0; i < length; i++)
549    delete entries[i];
550
551  gfree(entries);
552}
553
554GBool Catalog::labelToIndex(GooString *label, int *index)
555{
556  char *end;
557
558  if (pageLabelInfo != NULL) {
559    if (!pageLabelInfo->labelToIndex(label, index))
560      return gFalse;
561  } else {
562    *index = strtol(label->getCString(), &end, 10) - 1;
563    if (*end != '\0')
564      return gFalse;
565  }
566
567  if (*index < 0 || *index >= numPages)
568    return gFalse;
569
570  return gTrue;
571}
572
573GBool Catalog::indexToLabel(int index, GooString *label)
574{
575  char buffer[32];
576
577  if (index < 0 || index >= numPages)
578    return gFalse;
579
580  if (pageLabelInfo != NULL) {
581    return pageLabelInfo->indexToLabel(index, label);
582  } else {
583    snprintf(buffer, sizeof (buffer), "%d", index + 1);
584    label->append(buffer);           
585    return gTrue;
586  }
587}
Note: See TracBrowser for help on using the repository browser.