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

Last change on this file since 257 was 257, checked in by Eugene Romanenko, 13 years ago

PDF plugin: Poppler library updated to version 0.10.0

File size: 17.5 KB
Line 
1//========================================================================
2//
3// Catalog.cc
4//
5// Copyright 1996-2007 Glyph & Cog, LLC
6//
7//========================================================================
8
9//========================================================================
10//
11// Modified under the Poppler project - http://poppler.freedesktop.org
12//
13// All changes made under the Poppler project to this file are licensed
14// under GPL version 2 or later
15//
16// Copyright (C) 2005 Kristian HÞgsberg <krh@redhat.com>
17// Copyright (C) 2005-2007 Albert Astals Cid <aacid@kde.org>
18// Copyright (C) 2005 Jeff Muizelaar <jrmuizel@nit.ca>
19// Copyright (C) 2005 Jonathan Blandford <jrb@redhat.com>
20// Copyright (C) 2005 Marco Pesenti Gritti <mpg@redhat.com>
21// Copyright (C) 2005, 2006, 2008 Brad Hards <bradh@frogmouth.net>
22// Copyright (C) 2006, 2008 Carlos Garcia Campos <carlosgc@gnome.org>
23// Copyright (C) 2007 Julien Rebetez <julienr@svn.gnome.org>
24// Copyright (C) 2008 Pino Toscano <pino@kde.org>
25//
26// To see a description of the changes please see the Changelog file that
27// came with your tarball or type make ChangeLog if you are building from git
28//
29//========================================================================
30
31#include <config.h>
32
33#ifdef USE_GCC_PRAGMAS
34#pragma implementation
35#endif
36
37#include <stddef.h>
38#include <stdlib.h>
39#include "goo/gmem.h"
40#include "Object.h"
41#include "XRef.h"
42#include "Array.h"
43#include "Dict.h"
44#include "Page.h"
45#include "Error.h"
46#include "Link.h"
47#include "PageLabelInfo.h"
48#include "Catalog.h"
49#include "Form.h"
50#include "OptionalContent.h"
51
52//------------------------------------------------------------------------
53// Catalog
54//------------------------------------------------------------------------
55
56Catalog::Catalog(XRef *xrefA) {
57  Object catDict, pagesDict, pagesDictRef;
58  Object obj, obj2;
59  Object optContentProps;
60  char *alreadyRead;
61  int numPages0;
62  int i;
63
64  ok = gTrue;
65  xref = xrefA;
66  pages = NULL;
67  pageRefs = NULL;
68  numPages = pagesSize = 0;
69  baseURI = NULL;
70  pageLabelInfo = NULL;
71  form = NULL;
72  optContent = NULL;
73
74  xref->getCatalog(&catDict);
75  if (!catDict.isDict()) {
76    error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
77    goto err1;
78  }
79  // get the AcroForm dictionary
80  catDict.dictLookup("AcroForm", &acroForm);
81
82  // load Forms
83  if (acroForm.isDict()) {
84    form = new Form(xref,&acroForm);
85  }
86
87
88  // read page tree
89  catDict.dictLookup("Pages", &pagesDict);
90  // This should really be isDict("Pages"), but I've seen at least one
91  // PDF file where the /Type entry is missing.
92  if (!pagesDict.isDict()) {
93    error(-1, "Top-level pages object is wrong type (%s)",
94          pagesDict.getTypeName());
95    goto err2;
96  }
97  pagesDict.dictLookup("Count", &obj);
98  // some PDF files actually use real numbers here ("/Count 9.0")
99  if (!obj.isNum()) {
100    error(-1, "Page count in top-level pages object is wrong type (%s)",
101          obj.getTypeName());
102    goto err3;
103  }
104  pagesSize = numPages0 = (int)obj.getNum();
105  obj.free();
106  pages = (Page **)gmallocn(pagesSize, sizeof(Page *));
107  pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref));
108  for (i = 0; i < pagesSize; ++i) {
109    pages[i] = NULL;
110    pageRefs[i].num = -1;
111    pageRefs[i].gen = -1;
112  }
113  alreadyRead = (char *)gmalloc(xref->getNumObjects());
114  memset(alreadyRead, 0, xref->getNumObjects());
115  if (catDict.dictLookupNF("Pages", &pagesDictRef)->isRef() &&
116      pagesDictRef.getRefNum() >= 0 &&
117      pagesDictRef.getRefNum() < xref->getNumObjects()) {
118    alreadyRead[pagesDictRef.getRefNum()] = 1;
119  }
120  pagesDictRef.free();
121  numPages = readPageTree(pagesDict.getDict(), NULL, 0, alreadyRead);
122  gfree(alreadyRead);
123  if (numPages != numPages0) {
124    error(-1, "Page count in top-level pages object is incorrect");
125  }
126  pagesDict.free();
127
128  // read named destination dictionary
129  catDict.dictLookup("Dests", &dests);
130
131  // read root of named destination tree - PDF1.6 table 3.28
132  if (catDict.dictLookup("Names", &obj)->isDict()) {
133    obj.dictLookup("Dests", &obj2);
134    destNameTree.init(xref, &obj2);
135    obj2.free();
136    obj.dictLookup("EmbeddedFiles", &obj2);
137    embeddedFileNameTree.init(xref, &obj2);
138    obj2.free();
139    obj.dictLookup("JavaScript", &obj2);
140    jsNameTree.init(xref, &obj2);
141    obj2.free();
142  }
143  obj.free();
144
145  if (catDict.dictLookup("PageLabels", &obj)->isDict())
146    pageLabelInfo = new PageLabelInfo(&obj, numPages);
147  obj.free();
148
149  // read page mode
150  pageMode = pageModeNone;
151  if (catDict.dictLookup("PageMode", &obj)->isName()) {
152    if (obj.isName("UseNone"))
153      pageMode = pageModeNone;
154    else if (obj.isName("UseOutlines"))
155      pageMode = pageModeOutlines;
156    else if (obj.isName("UseThumbs"))
157      pageMode = pageModeThumbs;
158    else if (obj.isName("FullScreen"))
159      pageMode = pageModeFullScreen;
160    else if (obj.isName("UseOC"))
161      pageMode = pageModeOC;
162    else if (obj.isName("UseAttachments"))
163      pageMode = pageModeAttach;
164  }
165  obj.free();
166
167  pageLayout = pageLayoutNone;
168  if (catDict.dictLookup("PageLayout", &obj)->isName()) {
169    if (obj.isName("SinglePage"))
170      pageLayout = pageLayoutSinglePage;
171    if (obj.isName("OneColumn"))
172      pageLayout = pageLayoutOneColumn;
173    if (obj.isName("TwoColumnLeft"))
174      pageLayout = pageLayoutTwoColumnLeft;
175    if (obj.isName("TwoColumnRight"))
176      pageLayout = pageLayoutTwoColumnRight;
177    if (obj.isName("TwoPageLeft"))
178      pageLayout = pageLayoutTwoPageLeft;
179    if (obj.isName("TwoPageRight"))
180      pageLayout = pageLayoutTwoPageRight;
181  }
182  obj.free();
183
184  // read base URI
185  if (catDict.dictLookup("URI", &obj)->isDict()) {
186    if (obj.dictLookup("Base", &obj2)->isString()) {
187      baseURI = obj2.getString()->copy();
188    }
189    obj2.free();
190  }
191  obj.free();
192
193  // get the metadata stream
194  catDict.dictLookup("Metadata", &metadata);
195
196  // get the structure tree root
197  catDict.dictLookup("StructTreeRoot", &structTreeRoot);
198
199  // get the outline dictionary
200  catDict.dictLookup("Outlines", &outline);
201
202  // get the Optional Content dictionary
203  if (catDict.dictLookup("OCProperties", &optContentProps)->isDict()) {
204    optContent = new OCGs(&optContentProps, xref);
205  }
206  optContentProps.free();
207
208  // perform form-related loading after all widgets have been loaded
209  if (form) 
210    form->postWidgetsLoad();
211
212  catDict.free();
213  return;
214
215 err3:
216  obj.free();
217 err2:
218  pagesDict.free();
219 err1:
220  catDict.free();
221  dests.initNull();
222  ok = gFalse;
223}
224
225Catalog::~Catalog() {
226  int i;
227
228  if (pages) {
229    for (i = 0; i < pagesSize; ++i) {
230      if (pages[i]) {
231        delete pages[i];
232      }
233    }
234    gfree(pages);
235    gfree(pageRefs);
236  }
237  dests.free();
238  destNameTree.free();
239  embeddedFileNameTree.free();
240  jsNameTree.free();
241  if (baseURI) {
242    delete baseURI;
243  }
244  delete pageLabelInfo;
245  delete form;
246  delete optContent;
247  metadata.free();
248  structTreeRoot.free();
249  outline.free();
250  acroForm.free();
251}
252
253GooString *Catalog::readMetadata() {
254  GooString *s;
255  Dict *dict;
256  Object obj;
257  int c;
258
259  if (!metadata.isStream()) {
260    return NULL;
261  }
262  dict = metadata.streamGetDict();
263  if (!dict->lookup("Subtype", &obj)->isName("XML")) {
264    error(-1, "Unknown Metadata type: '%s'",
265          obj.isName() ? obj.getName() : "???");
266  }
267  obj.free();
268  s = new GooString();
269  metadata.streamReset();
270  while ((c = metadata.streamGetChar()) != EOF) {
271    s->append(c);
272  }
273  metadata.streamClose();
274  return s;
275}
276
277int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
278                          char *alreadyRead) {
279  Object kids;
280  Object kid;
281  Object kidRef;
282  PageAttrs *attrs1, *attrs2;
283  Page *page;
284  int i, j;
285
286  attrs1 = new PageAttrs(attrs, pagesDict);
287  pagesDict->lookup("Kids", &kids);
288  if (!kids.isArray()) {
289    error(-1, "Kids object (page %d) is wrong type (%s)",
290          start+1, kids.getTypeName());
291    goto err1;
292  }
293  for (i = 0; i < kids.arrayGetLength(); ++i) {
294    kids.arrayGetNF(i, &kidRef);
295    if (kidRef.isRef() &&
296        kidRef.getRefNum() >= 0 &&
297        kidRef.getRefNum() < xref->getNumObjects()) {
298      if (alreadyRead[kidRef.getRefNum()]) {
299        error(-1, "Loop in Pages tree");
300        kidRef.free();
301        continue;
302      }
303      alreadyRead[kidRef.getRefNum()] = 1;
304    }
305    kids.arrayGet(i, &kid);
306    if (kid.isDict("Page")) {
307      attrs2 = new PageAttrs(attrs1, kid.getDict());
308      page = new Page(xref, start+1, kid.getDict(), attrs2, form);
309      if (!page->isOk()) {
310        ++start;
311        goto err3;
312      }
313      if (start >= pagesSize) {
314        pagesSize += 32;
315        pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *));
316        pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref));
317        for (j = pagesSize - 32; j < pagesSize; ++j) {
318          pages[j] = NULL;
319          pageRefs[j].num = -1;
320          pageRefs[j].gen = -1;
321        }
322      }
323      pages[start] = page;
324      if (kidRef.isRef()) {
325        pageRefs[start].num = kidRef.getRefNum();
326        pageRefs[start].gen = kidRef.getRefGen();
327      }
328      ++start;
329    // This should really be isDict("Pages"), but I've seen at least one
330    // PDF file where the /Type entry is missing.
331    } else if (kid.isDict()) {
332      if ((start = readPageTree(kid.getDict(), attrs1, start, alreadyRead))
333          < 0)
334        goto err2;
335    } else {
336      error(-1, "Kid object (page %d) is wrong type (%s)",
337            start+1, kid.getTypeName());
338    }
339    kid.free();
340    kidRef.free();
341  }
342  delete attrs1;
343  kids.free();
344  return start;
345
346 err3:
347  delete page;
348 err2:
349  kid.free();
350 err1:
351  kids.free();
352  delete attrs1;
353  ok = gFalse;
354  return -1;
355}
356
357int Catalog::findPage(int num, int gen) {
358  int i;
359
360  for (i = 0; i < numPages; ++i) {
361    if (pageRefs[i].num == num && pageRefs[i].gen == gen)
362      return i + 1;
363  }
364  return 0;
365}
366
367LinkDest *Catalog::findDest(GooString *name) {
368  LinkDest *dest;
369  Object obj1, obj2;
370  GBool found;
371
372  // try named destination dictionary then name tree
373  found = gFalse;
374  if (dests.isDict()) {
375    if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
376      found = gTrue;
377    else
378      obj1.free();
379  }
380  if (!found) {
381    if (destNameTree.lookup(name, &obj1))
382      found = gTrue;
383    else
384      obj1.free();
385  }
386  if (!found)
387    return NULL;
388
389  // construct LinkDest
390  dest = NULL;
391  if (obj1.isArray()) {
392    dest = new LinkDest(obj1.getArray());
393  } else if (obj1.isDict()) {
394    if (obj1.dictLookup("D", &obj2)->isArray())
395      dest = new LinkDest(obj2.getArray());
396    else
397      error(-1, "Bad named destination value");
398    obj2.free();
399  } else {
400    error(-1, "Bad named destination value");
401  }
402  obj1.free();
403  if (dest && !dest->isOk()) {
404    delete dest;
405    dest = NULL;
406  }
407
408  return dest;
409}
410
411EmbFile *Catalog::embeddedFile(int i)
412{
413    Object efDict;
414    Object obj;
415    obj = embeddedFileNameTree.getValue(i);
416    EmbFile *embeddedFile = 0;
417    if (obj.isRef()) {
418        GooString desc(embeddedFileNameTree.getName(i));
419        embeddedFile = new EmbFile(obj.fetch(xref, &efDict), &desc);
420        efDict.free();
421    } else {
422        Object null;
423        embeddedFile = new EmbFile(&null);
424    }
425    return embeddedFile;
426}
427
428GooString *Catalog::getJS(int i)
429{
430  Object obj = jsNameTree.getValue(i);
431  if (obj.isRef()) {
432    Ref r = obj.getRef();
433    obj.free();
434    xref->fetch(r.num, r.gen, &obj);
435  }
436
437  if (!obj.isDict()) {
438    obj.free();
439    return 0;
440  }
441  Object obj2;
442  if (!obj.dictLookup("S", &obj2)->isName()) {
443    obj2.free();
444    obj.free();
445    return 0;
446  }
447  if (strcmp(obj2.getName(), "JavaScript")) {
448    obj2.free();
449    obj.free();
450    return 0;
451  }
452  obj.dictLookup("JS", &obj2);
453  GooString *js = 0;
454  if (obj2.isString()) {
455    js = new GooString(obj2.getString());
456  }
457  else if (obj2.isStream()) {
458    Stream *stream = obj2.getStream();
459    js = new GooString();
460    stream->reset();
461    int i;
462    while ((i = stream->getChar()) != EOF) {
463      js->append((char)i);
464    }
465  }
466  obj2.free();
467  obj.free();
468  return js;
469}
470
471NameTree::NameTree()
472{
473  size = 0;
474  length = 0;
475  entries = NULL;
476}
477
478NameTree::Entry::Entry(Array *array, int index) {
479    if (!array->getString(index, &name) || !array->getNF(index + 1, &value)) {
480      Object aux;
481      array->get(index, &aux);
482      if (aux.isString() && array->getNF(index + 1, &value) )
483      {
484        name.append(aux.getString());
485      }
486      else
487        error(-1, "Invalid page tree");
488    }
489}
490
491NameTree::Entry::~Entry() {
492  value.free();
493}
494
495void NameTree::addEntry(Entry *entry)
496{
497  if (length == size) {
498    if (length == 0) {
499      size = 8;
500    } else {
501      size *= 2;
502    }
503    entries = (Entry **) grealloc (entries, sizeof (Entry *) * size);
504  }
505
506  entries[length] = entry;
507  ++length;
508}
509
510void NameTree::init(XRef *xrefA, Object *tree) {
511  xref = xrefA;
512  parse(tree);
513}
514
515void NameTree::parse(Object *tree) {
516  Object names;
517  Object kids, kid;
518  int i;
519
520  if (!tree->isDict())
521    return;
522
523  // leaf node
524  if (tree->dictLookup("Names", &names)->isArray()) {
525    for (i = 0; i < names.arrayGetLength(); i += 2) {
526      NameTree::Entry *entry;
527
528      entry = new Entry(names.getArray(), i);
529      addEntry(entry);
530    }
531  }
532  names.free();
533
534  // root or intermediate node
535  if (tree->dictLookup("Kids", &kids)->isArray()) {
536    for (i = 0; i < kids.arrayGetLength(); ++i) {
537      if (kids.arrayGet(i, &kid)->isDict())
538        parse(&kid);
539      kid.free();
540    }
541  }
542  kids.free();
543}
544
545int NameTree::Entry::cmp(const void *voidKey, const void *voidEntry)
546{
547  GooString *key = (GooString *) voidKey;
548  Entry *entry = *(NameTree::Entry **) voidEntry;
549
550  return key->cmp(&entry->name);
551}
552
553GBool NameTree::lookup(GooString *name, Object *obj)
554{
555  Entry **entry;
556
557  entry = (Entry **) bsearch(name, entries,
558                             length, sizeof(Entry *), Entry::cmp);
559  if (entry != NULL) {
560    (*entry)->value.fetch(xref, obj);
561    return gTrue;
562  } else {
563    printf("failed to look up %s\n", name->getCString());
564    obj->initNull();
565    return gFalse;
566  }
567}
568
569Object NameTree::getValue(int index)
570{
571  if (index < length) {
572    return entries[index]->value;
573  } else {
574    return Object();
575  }
576}
577
578GooString *NameTree::getName(int index)
579{
580    if (index < length) {
581        return &entries[index]->name;
582    } else {
583        return NULL;
584    }
585}
586
587void NameTree::free()
588{
589  int i;
590
591  for (i = 0; i < length; i++)
592    delete entries[i];
593
594  gfree(entries);
595}
596
597GBool Catalog::labelToIndex(GooString *label, int *index)
598{
599  char *end;
600
601  if (pageLabelInfo != NULL) {
602    if (!pageLabelInfo->labelToIndex(label, index))
603      return gFalse;
604  } else {
605    *index = strtol(label->getCString(), &end, 10) - 1;
606    if (*end != '\0')
607      return gFalse;
608  }
609
610  if (*index < 0 || *index >= numPages)
611    return gFalse;
612
613  return gTrue;
614}
615
616GBool Catalog::indexToLabel(int index, GooString *label)
617{
618  char buffer[32];
619
620  if (index < 0 || index >= numPages)
621    return gFalse;
622
623  if (pageLabelInfo != NULL) {
624    return pageLabelInfo->indexToLabel(index, label);
625  } else {
626    snprintf(buffer, sizeof (buffer), "%d", index + 1);
627    label->append(buffer);           
628    return gTrue;
629  }
630}
631
632EmbFile::EmbFile(Object *efDict, GooString *description)
633{
634  m_name = 0;
635  m_description = 0;
636  if (description)
637    m_description = description->copy();
638  m_size = -1;
639  m_createDate = 0;
640  m_modDate = 0;
641  m_checksum = 0;
642  m_mimetype = 0;
643  if (efDict->isDict()) {
644    Object fileSpec;
645    Object fileDesc;
646    Object paramDict;
647    Object paramObj;
648    Object obj2;
649    Stream *efStream = NULL;
650    // efDict matches Table 3.40 in the PDF1.6 spec
651    efDict->dictLookup("F", &fileSpec);
652    if (fileSpec.isString()) {
653      m_name = new GooString(fileSpec.getString());
654    }
655    fileSpec.free();
656
657    // the logic here is that the description from the name
658    // dictionary is used if we don't have a more specific
659    // description - see the Note: on page 157 of the PDF1.6 spec
660    efDict->dictLookup("Desc", &fileDesc);
661    if (fileDesc.isString()) {
662      delete m_description;
663      m_description = new GooString(fileDesc.getString());
664    } else {
665      efDict->dictLookup("Description", &fileDesc);
666      if (fileDesc.isString()) {
667        delete m_description;
668        m_description = new GooString(fileDesc.getString());
669      }
670    }
671    fileDesc.free();
672
673    efDict->dictLookup("EF", &obj2);
674    if (obj2.isDict()) {
675      // This gives us the raw data stream bytes
676
677      obj2.dictLookup("F", &m_objStr);
678      if (m_objStr.isStream()) {
679        efStream = m_objStr.getStream();
680
681        // dataDict corresponds to Table 3.41 in the PDF1.6 spec.
682        Dict *dataDict = efStream->getDict();
683
684        // subtype is normally the mimetype
685        Object subtypeName;
686        if (dataDict->lookup("Subtype", &subtypeName)->isName()) {
687          m_mimetype = new GooString(subtypeName.getName());
688        }
689        subtypeName.free();
690
691        // paramDict corresponds to Table 3.42 in the PDF1.6 spec
692        Object paramDict;
693        dataDict->lookup( "Params", &paramDict );
694        if (paramDict.isDict()) {
695          paramDict.dictLookup("ModDate", &paramObj);
696          if (paramObj.isString()) {
697            m_modDate = new GooString(paramObj.getString());
698          }
699          paramObj.free();
700          paramDict.dictLookup("CreationDate", &paramObj);
701          if (paramObj.isString()) {
702            m_createDate = new GooString(paramObj.getString());
703          }
704          paramObj.free();
705          paramDict.dictLookup("Size", &paramObj);
706          if (paramObj.isInt()) {
707            m_size = paramObj.getInt();
708          }
709          paramObj.free();
710          paramDict.dictLookup("CheckSum", &paramObj);
711          if (paramObj.isString()) {
712            m_checksum = new GooString(paramObj.getString());
713          }
714          paramObj.free();
715        }
716        paramDict.free();
717      }
718    }
719    obj2.free();
720  }
721  if (!m_name)
722    m_name = new GooString();
723  if (!m_description)
724    m_description = new GooString();
725  if (!m_createDate)
726    m_createDate = new GooString();
727  if (!m_modDate)
728    m_modDate = new GooString();
729  if (!m_checksum)
730    m_checksum = new GooString();
731  if (!m_mimetype)
732    m_mimetype = new GooString();
733}
Note: See TracBrowser for help on using the repository browser.