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

Last change on this file since 27 was 27, checked in by Eugene Romanenko, 16 years ago

poppler updated to version 0.5.2, also needed changes to be compatible with new poppler

File size: 13.6 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  char *cname;
514
515  entry = (Entry **) bsearch(name, entries,
516                             length, sizeof(Entry *), Entry::cmp);
517  if (entry != NULL) {
518    (*entry)->value.fetch(xref, obj);
519    return gTrue;
520  } else {
521    cname = name->getCString();
522    printf("failed to look up %s\n", cname);
523    delete[] cname;
524    obj->initNull();
525    return gFalse;
526  }
527}
528
529Object NameTree::getValue(int index)
530{
531  if (index < length) {
532    return entries[index]->value;
533  } else {
534    return Object();
535  }
536}
537
538UGooString *NameTree::getName(int index)
539{
540    if (index < length) {
541        return entries[index]->name;
542    } else {
543        return NULL;
544    }
545}
546
547void NameTree::free()
548{
549  int i;
550
551  for (i = 0; i < length; i++)
552    delete entries[i];
553
554  gfree(entries);
555}
556
557GBool Catalog::labelToIndex(GooString *label, int *index)
558{
559  char *end;
560
561  if (pageLabelInfo != NULL) {
562    if (!pageLabelInfo->labelToIndex(label, index))
563      return gFalse;
564  } else {
565    *index = strtol(label->getCString(), &end, 10) - 1;
566    if (*end != '\0')
567      return gFalse;
568  }
569
570  if (*index < 0 || *index >= numPages)
571    return gFalse;
572
573  return gTrue;
574}
575
576GBool Catalog::indexToLabel(int index, GooString *label)
577{
578  char buffer[32];
579
580  if (index < 0 || index >= numPages)
581    return gFalse;
582
583  if (pageLabelInfo != NULL) {
584    return pageLabelInfo->indexToLabel(index, label);
585  } else {
586    snprintf(buffer, sizeof (buffer), "%d", index + 1);
587    label->append(buffer);           
588    return gTrue;
589  }
590}
Note: See TracBrowser for help on using the repository browser.