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

Last change on this file since 277 was 277, checked in by rbri, 11 years ago

PDF plugin: Poppler library updated to version 0.12.3

File size: 17.7 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-2009 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    if (!optContent->isOk ()) {
206      delete optContent;
207      optContent = NULL;
208    }
209  }
210  optContentProps.free();
211
212  // perform form-related loading after all widgets have been loaded
213  if (form) 
214    form->postWidgetsLoad();
215
216  catDict.free();
217  return;
218
219 err3:
220  obj.free();
221 err2:
222  pagesDict.free();
223 err1:
224  catDict.free();
225  dests.initNull();
226  ok = gFalse;
227}
228
229Catalog::~Catalog() {
230  int i;
231
232  if (pages) {
233    for (i = 0; i < pagesSize; ++i) {
234      if (pages[i]) {
235        delete pages[i];
236      }
237    }
238    gfree(pages);
239    gfree(pageRefs);
240  }
241  dests.free();
242  destNameTree.free();
243  embeddedFileNameTree.free();
244  jsNameTree.free();
245  if (baseURI) {
246    delete baseURI;
247  }
248  delete pageLabelInfo;
249  delete form;
250  delete optContent;
251  metadata.free();
252  structTreeRoot.free();
253  outline.free();
254  acroForm.free();
255}
256
257GooString *Catalog::readMetadata() {
258  GooString *s;
259  Dict *dict;
260  Object obj;
261  int c;
262
263  if (!metadata.isStream()) {
264    return NULL;
265  }
266  dict = metadata.streamGetDict();
267  if (!dict->lookup("Subtype", &obj)->isName("XML")) {
268    error(-1, "Unknown Metadata type: '%s'",
269          obj.isName() ? obj.getName() : "???");
270  }
271  obj.free();
272  s = new GooString();
273  metadata.streamReset();
274  while ((c = metadata.streamGetChar()) != EOF) {
275    s->append(c);
276  }
277  metadata.streamClose();
278  return s;
279}
280
281int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,
282                          char *alreadyRead) {
283  Object kids;
284  Object kid;
285  Object kidRef;
286  PageAttrs *attrs1, *attrs2;
287  Page *page;
288  int i, j;
289
290  attrs1 = new PageAttrs(attrs, pagesDict);
291  pagesDict->lookup("Kids", &kids);
292  if (!kids.isArray()) {
293    error(-1, "Kids object (page %d) is wrong type (%s)",
294          start+1, kids.getTypeName());
295    goto err1;
296  }
297  for (i = 0; i < kids.arrayGetLength(); ++i) {
298    kids.arrayGetNF(i, &kidRef);
299    if (kidRef.isRef() &&
300        kidRef.getRefNum() >= 0 &&
301        kidRef.getRefNum() < xref->getNumObjects()) {
302      if (alreadyRead[kidRef.getRefNum()]) {
303        error(-1, "Loop in Pages tree");
304        kidRef.free();
305        continue;
306      }
307      alreadyRead[kidRef.getRefNum()] = 1;
308    }
309    kids.arrayGet(i, &kid);
310    if (kid.isDict("Page")) {
311      attrs2 = new PageAttrs(attrs1, kid.getDict());
312      page = new Page(xref, start+1, kid.getDict(), kidRef.getRef(), attrs2, form);
313      if (!page->isOk()) {
314        ++start;
315        goto err3;
316      }
317      if (start >= pagesSize) {
318        pagesSize += 32;
319        pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *));
320        pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref));
321        for (j = pagesSize - 32; j < pagesSize; ++j) {
322          pages[j] = NULL;
323          pageRefs[j].num = -1;
324          pageRefs[j].gen = -1;
325        }
326      }
327      pages[start] = page;
328      if (kidRef.isRef()) {
329        pageRefs[start].num = kidRef.getRefNum();
330        pageRefs[start].gen = kidRef.getRefGen();
331      }
332      ++start;
333    // This should really be isDict("Pages"), but I've seen at least one
334    // PDF file where the /Type entry is missing.
335    } else if (kid.isDict()) {
336      if ((start = readPageTree(kid.getDict(), attrs1, start, alreadyRead))
337          < 0)
338        goto err2;
339    } else {
340      error(-1, "Kid object (page %d) is wrong type (%s)",
341            start+1, kid.getTypeName());
342    }
343    kid.free();
344    kidRef.free();
345  }
346  delete attrs1;
347  kids.free();
348  return start;
349
350 err3:
351  delete page;
352 err2:
353  kid.free();
354  kidRef.free();
355 err1:
356  kids.free();
357  delete attrs1;
358  ok = gFalse;
359  return -1;
360}
361
362int Catalog::findPage(int num, int gen) {
363  int i;
364
365  for (i = 0; i < numPages; ++i) {
366    if (pageRefs[i].num == num && pageRefs[i].gen == gen)
367      return i + 1;
368  }
369  return 0;
370}
371
372LinkDest *Catalog::findDest(GooString *name) {
373  LinkDest *dest;
374  Object obj1, obj2;
375  GBool found;
376
377  // try named destination dictionary then name tree
378  found = gFalse;
379  if (dests.isDict()) {
380    if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
381      found = gTrue;
382    else
383      obj1.free();
384  }
385  if (!found) {
386    if (destNameTree.lookup(name, &obj1))
387      found = gTrue;
388    else
389      obj1.free();
390  }
391  if (!found)
392    return NULL;
393
394  // construct LinkDest
395  dest = NULL;
396  if (obj1.isArray()) {
397    dest = new LinkDest(obj1.getArray());
398  } else if (obj1.isDict()) {
399    if (obj1.dictLookup("D", &obj2)->isArray())
400      dest = new LinkDest(obj2.getArray());
401    else
402      error(-1, "Bad named destination value");
403    obj2.free();
404  } else {
405    error(-1, "Bad named destination value");
406  }
407  obj1.free();
408  if (dest && !dest->isOk()) {
409    delete dest;
410    dest = NULL;
411  }
412
413  return dest;
414}
415
416EmbFile *Catalog::embeddedFile(int i)
417{
418    Object efDict;
419    Object obj;
420    obj = embeddedFileNameTree.getValue(i);
421    EmbFile *embeddedFile = 0;
422    if (obj.isRef()) {
423        GooString desc(embeddedFileNameTree.getName(i));
424        embeddedFile = new EmbFile(obj.fetch(xref, &efDict), &desc);
425        efDict.free();
426    } else {
427        Object null;
428        embeddedFile = new EmbFile(&null);
429    }
430    return embeddedFile;
431}
432
433GooString *Catalog::getJS(int i)
434{
435  Object obj = jsNameTree.getValue(i);
436  if (obj.isRef()) {
437    Ref r = obj.getRef();
438    obj.free();
439    xref->fetch(r.num, r.gen, &obj);
440  }
441
442  if (!obj.isDict()) {
443    obj.free();
444    return 0;
445  }
446  Object obj2;
447  if (!obj.dictLookup("S", &obj2)->isName()) {
448    obj2.free();
449    obj.free();
450    return 0;
451  }
452  if (strcmp(obj2.getName(), "JavaScript")) {
453    obj2.free();
454    obj.free();
455    return 0;
456  }
457  obj2.free();
458  obj.dictLookup("JS", &obj2);
459  GooString *js = 0;
460  if (obj2.isString()) {
461    js = new GooString(obj2.getString());
462  }
463  else if (obj2.isStream()) {
464    Stream *stream = obj2.getStream();
465    js = new GooString();
466    stream->reset();
467    int j;
468    while ((j = stream->getChar()) != EOF) {
469      js->append((char)j);
470    }
471  }
472  obj2.free();
473  obj.free();
474  return js;
475}
476
477NameTree::NameTree()
478{
479  size = 0;
480  length = 0;
481  entries = NULL;
482}
483
484NameTree::Entry::Entry(Array *array, int index) {
485    if (!array->getString(index, &name) || !array->getNF(index + 1, &value)) {
486      Object aux;
487      array->get(index, &aux);
488      if (aux.isString() && array->getNF(index + 1, &value) )
489      {
490        name.append(aux.getString());
491      }
492      else
493        error(-1, "Invalid page tree");
494    }
495}
496
497NameTree::Entry::~Entry() {
498  value.free();
499}
500
501void NameTree::addEntry(Entry *entry)
502{
503  if (length == size) {
504    if (length == 0) {
505      size = 8;
506    } else {
507      size *= 2;
508    }
509    entries = (Entry **) grealloc (entries, sizeof (Entry *) * size);
510  }
511
512  entries[length] = entry;
513  ++length;
514}
515
516void NameTree::init(XRef *xrefA, Object *tree) {
517  xref = xrefA;
518  parse(tree);
519}
520
521void NameTree::parse(Object *tree) {
522  Object names;
523  Object kids, kid;
524  int i;
525
526  if (!tree->isDict())
527    return;
528
529  // leaf node
530  if (tree->dictLookup("Names", &names)->isArray()) {
531    for (i = 0; i < names.arrayGetLength(); i += 2) {
532      NameTree::Entry *entry;
533
534      entry = new Entry(names.getArray(), i);
535      addEntry(entry);
536    }
537  }
538  names.free();
539
540  // root or intermediate node
541  if (tree->dictLookup("Kids", &kids)->isArray()) {
542    for (i = 0; i < kids.arrayGetLength(); ++i) {
543      if (kids.arrayGet(i, &kid)->isDict())
544        parse(&kid);
545      kid.free();
546    }
547  }
548  kids.free();
549}
550
551int NameTree::Entry::cmp(const void *voidKey, const void *voidEntry)
552{
553  GooString *key = (GooString *) voidKey;
554  Entry *entry = *(NameTree::Entry **) voidEntry;
555
556  return key->cmp(&entry->name);
557}
558
559GBool NameTree::lookup(GooString *name, Object *obj)
560{
561  Entry **entry;
562
563  entry = (Entry **) bsearch(name, entries,
564                             length, sizeof(Entry *), Entry::cmp);
565  if (entry != NULL) {
566    (*entry)->value.fetch(xref, obj);
567    return gTrue;
568  } else {
569    printf("failed to look up %s\n", name->getCString());
570    obj->initNull();
571    return gFalse;
572  }
573}
574
575Object NameTree::getValue(int index)
576{
577  if (index < length) {
578    return entries[index]->value;
579  } else {
580    return Object();
581  }
582}
583
584GooString *NameTree::getName(int index)
585{
586    if (index < length) {
587        return &entries[index]->name;
588    } else {
589        return NULL;
590    }
591}
592
593void NameTree::free()
594{
595  int i;
596
597  for (i = 0; i < length; i++)
598    delete entries[i];
599
600  gfree(entries);
601}
602
603GBool Catalog::labelToIndex(GooString *label, int *index)
604{
605  char *end;
606
607  if (pageLabelInfo != NULL) {
608    if (!pageLabelInfo->labelToIndex(label, index))
609      return gFalse;
610  } else {
611    *index = strtol(label->getCString(), &end, 10) - 1;
612    if (*end != '\0')
613      return gFalse;
614  }
615
616  if (*index < 0 || *index >= numPages)
617    return gFalse;
618
619  return gTrue;
620}
621
622GBool Catalog::indexToLabel(int index, GooString *label)
623{
624  char buffer[32];
625
626  if (index < 0 || index >= numPages)
627    return gFalse;
628
629  if (pageLabelInfo != NULL) {
630    return pageLabelInfo->indexToLabel(index, label);
631  } else {
632    snprintf(buffer, sizeof (buffer), "%d", index + 1);
633    label->append(buffer);           
634    return gTrue;
635  }
636}
637
638EmbFile::EmbFile(Object *efDict, GooString *description)
639{
640  m_name = 0;
641  m_description = 0;
642  if (description)
643    m_description = description->copy();
644  m_size = -1;
645  m_createDate = 0;
646  m_modDate = 0;
647  m_checksum = 0;
648  m_mimetype = 0;
649  if (efDict->isDict()) {
650    Object fileSpec;
651    Object fileDesc;
652    Object paramDict;
653    Object paramObj;
654    Object obj2;
655    Stream *efStream = NULL;
656    // efDict matches Table 3.40 in the PDF1.6 spec
657    efDict->dictLookup("F", &fileSpec);
658    if (fileSpec.isString()) {
659      m_name = new GooString(fileSpec.getString());
660    }
661    fileSpec.free();
662
663    // the logic here is that the description from the name
664    // dictionary is used if we don't have a more specific
665    // description - see the Note: on page 157 of the PDF1.6 spec
666    efDict->dictLookup("Desc", &fileDesc);
667    if (fileDesc.isString()) {
668      delete m_description;
669      m_description = new GooString(fileDesc.getString());
670    } else {
671      efDict->dictLookup("Description", &fileDesc);
672      if (fileDesc.isString()) {
673        delete m_description;
674        m_description = new GooString(fileDesc.getString());
675      }
676    }
677    fileDesc.free();
678
679    efDict->dictLookup("EF", &obj2);
680    if (obj2.isDict()) {
681      // This gives us the raw data stream bytes
682
683      obj2.dictLookup("F", &m_objStr);
684      if (m_objStr.isStream()) {
685        efStream = m_objStr.getStream();
686
687        // dataDict corresponds to Table 3.41 in the PDF1.6 spec.
688        Dict *dataDict = efStream->getDict();
689
690        // subtype is normally the mimetype
691        Object subtypeName;
692        if (dataDict->lookup("Subtype", &subtypeName)->isName()) {
693          m_mimetype = new GooString(subtypeName.getName());
694        }
695        subtypeName.free();
696
697        // paramDict corresponds to Table 3.42 in the PDF1.6 spec
698        Object paramDict;
699        dataDict->lookup( "Params", &paramDict );
700        if (paramDict.isDict()) {
701          paramDict.dictLookup("ModDate", &paramObj);
702          if (paramObj.isString()) {
703            m_modDate = new GooString(paramObj.getString());
704          }
705          paramObj.free();
706          paramDict.dictLookup("CreationDate", &paramObj);
707          if (paramObj.isString()) {
708            m_createDate = new GooString(paramObj.getString());
709          }
710          paramObj.free();
711          paramDict.dictLookup("Size", &paramObj);
712          if (paramObj.isInt()) {
713            m_size = paramObj.getInt();
714          }
715          paramObj.free();
716          paramDict.dictLookup("CheckSum", &paramObj);
717          if (paramObj.isString()) {
718            m_checksum = new GooString(paramObj.getString());
719          }
720          paramObj.free();
721        }
722        paramDict.free();
723      }
724    }
725    obj2.free();
726  }
727  if (!m_name)
728    m_name = new GooString();
729  if (!m_description)
730    m_description = new GooString();
731  if (!m_createDate)
732    m_createDate = new GooString();
733  if (!m_modDate)
734    m_modDate = new GooString();
735  if (!m_checksum)
736    m_checksum = new GooString();
737  if (!m_mimetype)
738    m_mimetype = new GooString();
739}
Note: See TracBrowser for help on using the repository browser.