Coverage Report

Created: 2023-09-25 06:41

/src/xpdf-4.04/xpdf/Catalog.cc
Line
Count
Source (jump to first uncovered line)
1
//========================================================================
2
//
3
// Catalog.cc
4
//
5
// Copyright 1996-2013 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#ifdef USE_GCC_PRAGMAS
12
#pragma implementation
13
#endif
14
15
#include <string.h>
16
#include <stddef.h>
17
#include <limits.h>
18
#include "gmem.h"
19
#include "gmempp.h"
20
#include "gfile.h"
21
#include "GList.h"
22
#include "Object.h"
23
#include "CharTypes.h"
24
#include "PDFDoc.h"
25
#include "XRef.h"
26
#include "Array.h"
27
#include "Dict.h"
28
#include "Page.h"
29
#include "Error.h"
30
#include "Link.h"
31
#include "AcroForm.h"
32
#include "TextString.h"
33
#include "Catalog.h"
34
35
//------------------------------------------------------------------------
36
// PageTreeNode
37
//------------------------------------------------------------------------
38
39
class PageTreeNode {
40
public:
41
42
  PageTreeNode(Ref refA, int countA, PageTreeNode *parentA);
43
  ~PageTreeNode();
44
45
  Ref ref;
46
  int count;
47
  PageTreeNode *parent;
48
  GList *kids;      // [PageTreeNode]
49
  PageAttrs *attrs;
50
};
51
52
0
PageTreeNode::PageTreeNode(Ref refA, int countA, PageTreeNode *parentA) {
53
0
  ref = refA;
54
0
  count = countA;
55
0
  parent = parentA;
56
0
  kids = NULL;
57
0
  attrs = NULL;
58
0
}
59
60
0
PageTreeNode::~PageTreeNode() {
61
0
  delete attrs;
62
0
  if (kids) {
63
0
    deleteGList(kids, PageTreeNode);
64
0
  }
65
0
}
66
67
//------------------------------------------------------------------------
68
// EmbeddedFile
69
//------------------------------------------------------------------------
70
71
class EmbeddedFile {
72
public:
73
74
  EmbeddedFile(TextString *nameA, Object *streamRefA);
75
  ~EmbeddedFile();
76
77
  TextString *name;
78
  Object streamRef;
79
};
80
81
0
EmbeddedFile::EmbeddedFile(TextString *nameA, Object *streamRefA) {
82
0
  name = nameA;
83
0
  streamRefA->copy(&streamRef);
84
0
}
85
86
0
EmbeddedFile::~EmbeddedFile() {
87
0
  delete name;
88
0
  streamRef.free();
89
0
}
90
91
//------------------------------------------------------------------------
92
// PageLabelNode
93
//------------------------------------------------------------------------
94
95
class PageLabelNode {
96
public:
97
98
  PageLabelNode(int firstPageA, Dict *dict);
99
  ~PageLabelNode();
100
101
  int firstPage;    // first page number covered by this node
102
  int lastPage;     // last page number covered by this node
103
  TextString *prefix;   // label prefix (may be empty)
104
  int start;      // value of the numeric portion of this
105
        //   label for the first page in the range
106
  char style;     // page label style
107
};
108
109
0
PageLabelNode::PageLabelNode(int firstPageA, Dict *dict) {
110
0
  Object prefixObj, styleObj, startObj;
111
112
  // convert page index to page number
113
0
  firstPage = firstPageA + 1;
114
115
  // lastPage will be filled in later
116
0
  lastPage = -1;
117
118
0
  if (dict->lookup("P", &prefixObj)->isString()) {
119
0
    prefix = new TextString(prefixObj.getString());
120
0
  } else {
121
0
    prefix = new TextString();
122
0
  }
123
0
  prefixObj.free();
124
125
0
  style = '\0';
126
0
  if (dict->lookup("S", &styleObj)->isName()) {
127
0
    if (strlen(styleObj.getName()) == 1) {
128
0
      style = styleObj.getName()[0];
129
0
    }
130
0
  }
131
0
  styleObj.free();
132
133
0
  start = 1;
134
0
  if (dict->lookup("St", &startObj)->isInt()) {
135
0
    start = startObj.getInt();
136
0
  }
137
0
  startObj.free();
138
0
}
139
140
0
PageLabelNode::~PageLabelNode() {
141
0
  delete prefix;
142
0
}
143
144
//------------------------------------------------------------------------
145
// Catalog
146
//------------------------------------------------------------------------
147
148
3
Catalog::Catalog(PDFDoc *docA) {
149
3
  Object catDict;
150
3
  Object obj, obj2;
151
152
3
  ok = gTrue;
153
3
  doc = docA;
154
3
  xref = doc->getXRef();
155
3
  pageTree = NULL;
156
3
  pages = NULL;
157
3
  pageRefs = NULL;
158
3
  numPages = 0;
159
3
  baseURI = NULL;
160
3
  form = NULL;
161
3
  embeddedFiles = NULL;
162
3
  pageLabels = NULL;
163
3
#if MULTITHREADED
164
3
  gInitMutex(&pageMutex);
165
3
#endif
166
167
3
  xref->getCatalog(&catDict);
168
3
  if (!catDict.isDict()) {
169
1
    error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})",
170
1
    catDict.getTypeName());
171
1
    goto err1;
172
1
  }
173
174
  // read page tree
175
2
  if (!readPageTree(&catDict)) {
176
2
    goto err1;
177
2
  }
178
179
  // read named destination dictionary
180
0
  catDict.dictLookup("Dests", &dests);
181
182
  // read root of named destination tree
183
0
  if (catDict.dictLookup("Names", &obj)->isDict())
184
0
    obj.dictLookup("Dests", &nameTree);
185
0
  else
186
0
    nameTree.initNull();
187
0
  obj.free();
188
189
  // read base URI
190
0
  if (catDict.dictLookup("URI", &obj)->isDict()) {
191
0
    if (obj.dictLookup("Base", &obj2)->isString()) {
192
0
      baseURI = obj2.getString()->copy();
193
0
    }
194
0
    obj2.free();
195
0
  }
196
0
  obj.free();
197
0
  if (!baseURI || baseURI->getLength() == 0) {
198
0
    if (baseURI) {
199
0
      delete baseURI;
200
0
    }
201
0
    if (doc->getFileName()) {
202
0
      baseURI = makePathAbsolute(grabPath(doc->getFileName()->getCString()));
203
0
      if (baseURI->getChar(0) == '/') {
204
0
  baseURI->insert(0, "file://localhost");
205
0
      } else {
206
0
  baseURI->insert(0, "file://localhost/");
207
0
      }
208
0
    } else {
209
0
      baseURI = new GString("file://localhost/");
210
0
    }
211
0
  }
212
213
  // get the metadata stream
214
0
  catDict.dictLookup("Metadata", &metadata);
215
216
  // get the structure tree root
217
0
  catDict.dictLookup("StructTreeRoot", &structTreeRoot);
218
219
  // get the outline dictionary
220
0
  catDict.dictLookup("Outlines", &outline);
221
222
  // get the AcroForm dictionary
223
0
  catDict.dictLookup("AcroForm", &acroForm);
224
225
  // get the NeedsRendering flag
226
  // NB: AcroForm::load() uses this value
227
0
  needsRendering = catDict.dictLookup("NeedsRendering", &obj)->isBool() &&
228
0
                   obj.getBool();
229
0
  obj.free();
230
231
  // create the Form
232
  // (if acroForm is a null object, this will still create an AcroForm
233
  // if there are unattached Widget-type annots)
234
0
  form = AcroForm::load(doc, this, &acroForm);
235
236
  // get the OCProperties dictionary
237
0
  catDict.dictLookup("OCProperties", &ocProperties);
238
239
  // get the list of embedded files
240
0
  readEmbeddedFileList(catDict.getDict());
241
242
  // get the ViewerPreferences object
243
0
  catDict.dictLookupNF("ViewerPreferences", &viewerPrefs);
244
245
0
  if (catDict.dictLookup("PageLabels", &obj)->isDict()) {
246
0
    readPageLabelTree(&obj);
247
0
  }
248
0
  obj.free();
249
250
0
  catDict.free();
251
0
  return;
252
253
3
 err1:
254
3
  catDict.free();
255
3
  dests.initNull();
256
3
  nameTree.initNull();
257
3
  ok = gFalse;
258
3
}
259
260
3
Catalog::~Catalog() {
261
3
  int i;
262
263
3
  if (pageTree) {
264
0
    delete pageTree;
265
0
  }
266
3
  if (pages) {
267
0
    for (i = 0; i < numPages; ++i) {
268
0
      if (pages[i]) {
269
0
  delete pages[i];
270
0
      }
271
0
    }
272
0
    gfree(pages);
273
0
    gfree(pageRefs);
274
0
  }
275
3
#if MULTITHREADED
276
3
  gDestroyMutex(&pageMutex);
277
3
#endif
278
3
  dests.free();
279
3
  nameTree.free();
280
3
  if (baseURI) {
281
0
    delete baseURI;
282
0
  }
283
3
  metadata.free();
284
3
  structTreeRoot.free();
285
3
  outline.free();
286
3
  acroForm.free();
287
3
  if (form) {
288
0
    delete form;
289
0
  }
290
3
  ocProperties.free();
291
3
  if (embeddedFiles) {
292
0
    deleteGList(embeddedFiles, EmbeddedFile);
293
0
  }
294
3
  if (pageLabels) {
295
0
    deleteGList(pageLabels, PageLabelNode);
296
0
  }
297
3
  viewerPrefs.free();
298
3
}
299
300
0
Page *Catalog::getPage(int i) {
301
0
  Page *page;
302
303
0
#if MULTITHREADED
304
0
  gLockMutex(&pageMutex);
305
0
#endif
306
0
  if (!pages[i-1]) {
307
0
    loadPage(i);
308
0
  }
309
0
  page = pages[i-1];
310
0
#if MULTITHREADED
311
0
  gUnlockMutex(&pageMutex);
312
0
#endif
313
0
  return page;
314
0
}
315
316
0
Ref *Catalog::getPageRef(int i) {
317
0
  Ref *pageRef;
318
319
0
#if MULTITHREADED
320
0
  gLockMutex(&pageMutex);
321
0
#endif
322
0
  if (!pages[i-1]) {
323
0
    loadPage(i);
324
0
  }
325
0
  pageRef = &pageRefs[i-1];
326
0
#if MULTITHREADED
327
0
  gUnlockMutex(&pageMutex);
328
0
#endif
329
0
  return pageRef;
330
0
}
331
332
0
void Catalog::doneWithPage(int i) {
333
0
#if MULTITHREADED
334
0
  gLockMutex(&pageMutex);
335
0
#endif
336
0
  if (pages[i-1]) {
337
0
    delete pages[i-1];
338
0
    pages[i-1] = NULL;
339
0
  }
340
0
#if MULTITHREADED
341
0
  gUnlockMutex(&pageMutex);
342
0
#endif
343
0
}
344
345
0
GString *Catalog::readMetadata() {
346
0
  GString *s;
347
0
  Dict *dict;
348
0
  Object obj;
349
0
  char buf[4096];
350
0
  int n;
351
352
0
  if (!metadata.isStream()) {
353
0
    return NULL;
354
0
  }
355
0
  dict = metadata.streamGetDict();
356
0
  if (!dict->lookup("Subtype", &obj)->isName("XML")) {
357
0
    error(errSyntaxWarning, -1, "Unknown Metadata type: '{0:s}'",
358
0
    obj.isName() ? obj.getName() : "???");
359
0
  }
360
0
  obj.free();
361
0
  s = new GString();
362
0
  metadata.streamReset();
363
0
  while ((n = metadata.streamGetBlock(buf, sizeof(buf))) > 0) {
364
0
    s->append(buf, n);
365
0
  }
366
0
  metadata.streamClose();
367
0
  return s;
368
0
}
369
370
0
int Catalog::findPage(int num, int gen) {
371
0
  int i;
372
373
0
#if MULTITHREADED
374
0
  gLockMutex(&pageMutex);
375
0
#endif
376
0
  for (i = 0; i < numPages; ++i) {
377
0
    if (!pages[i]) {
378
0
      loadPage(i+1);
379
0
    }
380
0
    if (pageRefs[i].num == num && pageRefs[i].gen == gen) {
381
0
#if MULTITHREADED
382
0
      gUnlockMutex(&pageMutex);
383
0
#endif
384
0
      return i + 1;
385
0
    }
386
0
  }
387
0
#if MULTITHREADED
388
0
  gUnlockMutex(&pageMutex);
389
0
#endif
390
0
  return 0;
391
0
}
392
393
0
LinkDest *Catalog::findDest(GString *name) {
394
0
  LinkDest *dest;
395
0
  Object obj1, obj2;
396
0
  GBool found;
397
398
  // try named destination dictionary then name tree
399
0
  found = gFalse;
400
0
  if (dests.isDict()) {
401
0
    if (!dests.dictLookup(name->getCString(), &obj1)->isNull()) {
402
0
      found = gTrue;
403
0
    } else {
404
0
      obj1.free();
405
0
    }
406
0
  }
407
0
  if (!found && nameTree.isDict()) {
408
0
    if (!findDestInTree(&nameTree, name, &obj1)->isNull()) {
409
0
      found = gTrue;
410
0
    } else {
411
0
      obj1.free();
412
0
    }
413
0
  }
414
0
  if (!found) {
415
0
    return NULL;
416
0
  }
417
418
  // construct LinkDest
419
0
  dest = NULL;
420
0
  if (obj1.isArray()) {
421
0
    dest = new LinkDest(obj1.getArray());
422
0
  } else if (obj1.isDict()) {
423
0
    if (obj1.dictLookup("D", &obj2)->isArray()) {
424
0
      dest = new LinkDest(obj2.getArray());
425
0
    } else {
426
0
      error(errSyntaxWarning, -1, "Bad named destination value");
427
0
    }
428
0
    obj2.free();
429
0
  } else {
430
0
    error(errSyntaxWarning, -1, "Bad named destination value");
431
0
  }
432
0
  obj1.free();
433
0
  if (dest && !dest->isOk()) {
434
0
    delete dest;
435
0
    dest = NULL;
436
0
  }
437
438
0
  return dest;
439
0
}
440
441
0
Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
442
0
  Object names, name1;
443
0
  Object kids, kid, limits, low, high;
444
0
  GBool done, found;
445
0
  int cmp, i;
446
447
  // leaf node
448
0
  if (tree->dictLookup("Names", &names)->isArray()) {
449
0
    done = found = gFalse;
450
0
    for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
451
0
      if (names.arrayGet(i, &name1)->isString()) {
452
0
  cmp = name->cmp(name1.getString());
453
0
  if (cmp == 0) {
454
0
    names.arrayGet(i+1, obj);
455
0
    found = gTrue;
456
0
    done = gTrue;
457
0
  } else if (cmp < 0) {
458
0
    done = gTrue;
459
0
  }
460
0
      }
461
0
      name1.free();
462
0
    }
463
0
    names.free();
464
0
    if (!found) {
465
0
      obj->initNull();
466
0
    }
467
0
    return obj;
468
0
  }
469
0
  names.free();
470
471
  // root or intermediate node
472
0
  done = gFalse;
473
0
  if (tree->dictLookup("Kids", &kids)->isArray()) {
474
0
    for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
475
0
      if (kids.arrayGet(i, &kid)->isDict()) {
476
0
  if (kid.dictLookup("Limits", &limits)->isArray()) {
477
0
    if (limits.arrayGet(0, &low)->isString() &&
478
0
        name->cmp(low.getString()) >= 0) {
479
0
      if (limits.arrayGet(1, &high)->isString() &&
480
0
    name->cmp(high.getString()) <= 0) {
481
0
        findDestInTree(&kid, name, obj);
482
0
        done = gTrue;
483
0
      }
484
0
      high.free();
485
0
    }
486
0
    low.free();
487
0
  }
488
0
  limits.free();
489
0
      }
490
0
      kid.free();
491
0
    }
492
0
  }
493
0
  kids.free();
494
495
  // name was outside of ranges of all kids
496
0
  if (!done) {
497
0
    obj->initNull();
498
0
  }
499
500
0
  return obj;
501
0
}
502
503
2
GBool Catalog::readPageTree(Object *catDict) {
504
2
  Object topPagesRef, topPagesObj, countObj;
505
2
  int i;
506
507
2
  if (!catDict->dictLookupNF("Pages", &topPagesRef)->isRef()) {
508
0
    error(errSyntaxError, -1, "Top-level pages reference is wrong type ({0:s})",
509
0
    topPagesRef.getTypeName());
510
0
    topPagesRef.free();
511
0
    return gFalse;
512
0
  }
513
2
  if (!topPagesRef.fetch(xref, &topPagesObj)->isDict()) {
514
2
    error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})",
515
2
    topPagesObj.getTypeName());
516
2
    topPagesObj.free();
517
2
    topPagesRef.free();
518
2
    return gFalse;
519
2
  }
520
0
  if (topPagesObj.dictLookup("Count", &countObj)->isInt()) {
521
0
    numPages = countObj.getInt();
522
0
    if (numPages == 0 || numPages > 50000) {
523
      // 1. Acrobat apparently scans the page tree if it sees a zero
524
      //    count.
525
      // 2. Absurdly large page counts result in very slow loading,
526
      //    because other code tries to fetch pages 1 through n.
527
      // In both cases: ignore the given page count and scan the tree
528
      // instead.
529
0
      numPages = countPageTree(&topPagesObj);
530
0
    }
531
0
  } else {
532
    // assume we got a Page node instead of a Pages node
533
0
    numPages = 1;
534
0
  }
535
0
  countObj.free();
536
0
  if (numPages < 0) {
537
0
    error(errSyntaxError, -1, "Invalid page count");
538
0
    topPagesObj.free();
539
0
    topPagesRef.free();
540
0
    numPages = 0;
541
0
    return gFalse;
542
0
  }
543
0
  pageTree = new PageTreeNode(topPagesRef.getRef(), numPages, NULL);
544
0
  topPagesObj.free();
545
0
  topPagesRef.free();
546
0
  pages = (Page **)greallocn(pages, numPages, sizeof(Page *));
547
0
  pageRefs = (Ref *)greallocn(pageRefs, numPages, sizeof(Ref));
548
0
  for (i = 0; i < numPages; ++i) {
549
0
    pages[i] = NULL;
550
0
    pageRefs[i].num = -1;
551
0
    pageRefs[i].gen = -1;
552
0
  }
553
0
  return gTrue;
554
0
}
555
556
0
int Catalog::countPageTree(Object *pagesObj) {
557
0
  Object kids, kid;
558
0
  int n, n2, i;
559
560
0
  if (!pagesObj->isDict()) {
561
0
    return 0;
562
0
  }
563
0
  if (pagesObj->dictLookup("Kids", &kids)->isArray()) {
564
0
    n = 0;
565
0
    for (i = 0; i < kids.arrayGetLength(); ++i) {
566
0
      kids.arrayGet(i, &kid);
567
0
      n2 = countPageTree(&kid);
568
0
      if (n2 < INT_MAX - n) {
569
0
  n += n2;
570
0
      } else {
571
0
  error(errSyntaxError, -1, "Page tree contains too many pages");
572
0
  n = INT_MAX;
573
0
      }
574
0
      kid.free();
575
0
    }
576
0
  } else {
577
0
    n = 1;
578
0
  }
579
0
  kids.free();
580
0
  return n;
581
0
}
582
583
0
void Catalog::loadPage(int pg) {
584
0
  loadPage2(pg, pg - 1, pageTree);
585
0
}
586
587
0
void Catalog::loadPage2(int pg, int relPg, PageTreeNode *node) {
588
0
  Object pageRefObj, pageObj, kidsObj, kidRefObj, kidObj, countObj;
589
0
  PageTreeNode *kidNode, *p;
590
0
  PageAttrs *attrs;
591
0
  int count, i;
592
593
0
  if (relPg >= node->count) {
594
0
    error(errSyntaxError, -1, "Internal error in page tree");
595
0
    pages[pg-1] = new Page(doc, pg);
596
0
    return;
597
0
  }
598
599
  // if this node has not been filled in yet, it's either a leaf node
600
  // or an unread internal node
601
0
  if (!node->kids) {
602
603
    // check for a loop in the page tree
604
0
    for (p = node->parent; p; p = p->parent) {
605
0
      if (node->ref.num == p->ref.num && node->ref.gen == p->ref.gen) {
606
0
  error(errSyntaxError, -1, "Loop in Pages tree");
607
0
  pages[pg-1] = new Page(doc, pg);
608
0
  return;
609
0
      }
610
0
    }
611
612
    // fetch the Page/Pages object
613
0
    pageRefObj.initRef(node->ref.num, node->ref.gen);
614
0
    if (!pageRefObj.fetch(xref, &pageObj)->isDict()) {
615
0
      error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})",
616
0
      pageObj.getTypeName());
617
0
      pageObj.free();
618
0
      pageRefObj.free();
619
0
      pages[pg-1] = new Page(doc, pg);
620
0
      return;
621
0
    }
622
623
    // merge the PageAttrs
624
0
    attrs = new PageAttrs(node->parent ? node->parent->attrs
625
0
                     : (PageAttrs *)NULL,
626
0
        pageObj.getDict(), xref);
627
628
    // if "Kids" exists, it's an internal node
629
0
    if (pageObj.dictLookup("Kids", &kidsObj)->isArray()) {
630
631
      // save the PageAttrs
632
0
      node->attrs = attrs;
633
634
      // read the kids
635
0
      node->kids = new GList();
636
0
      for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
637
0
  if (kidsObj.arrayGetNF(i, &kidRefObj)->isRef()) {
638
0
    if (kidRefObj.fetch(xref, &kidObj)->isDict()) {
639
0
      if (kidObj.dictLookup("Count", &countObj)->isInt()) {
640
0
        count = countObj.getInt();
641
0
      } else {
642
0
        count = 1;
643
0
      }
644
0
      countObj.free();
645
0
      node->kids->append(new PageTreeNode(kidRefObj.getRef(), count,
646
0
            node));
647
0
    } else {
648
0
      error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})",
649
0
      kidObj.getTypeName());
650
0
    }
651
0
    kidObj.free();
652
0
  } else {
653
0
    error(errSyntaxError, -1,
654
0
    "Page tree reference is wrong type ({0:s})",
655
0
    kidRefObj.getTypeName());
656
0
  }
657
0
  kidRefObj.free();
658
0
      }
659
660
0
    } else {
661
      
662
      // create the Page object
663
0
      pageRefs[pg-1] = node->ref;
664
0
      pages[pg-1] = new Page(doc, pg, pageObj.getDict(), attrs);
665
0
      if (!pages[pg-1]->isOk()) {
666
0
  delete pages[pg-1];
667
0
  pages[pg-1] = new Page(doc, pg);
668
0
      }
669
670
0
    }
671
672
0
    kidsObj.free();
673
0
    pageObj.free();
674
0
    pageRefObj.free();
675
0
  }
676
677
  // recursively descend the tree
678
0
  if (node->kids) {
679
0
    for (i = 0; i < node->kids->getLength(); ++i) {
680
0
      kidNode = (PageTreeNode *)node->kids->get(i);
681
0
      if (relPg < kidNode->count) {
682
0
  loadPage2(pg, relPg, kidNode);
683
0
  break;
684
0
      }
685
0
      relPg -= kidNode->count;
686
0
    }
687
688
    // this will only happen if the page tree is invalid
689
    // (i.e., parent count > sum of children counts)
690
0
    if (i == node->kids->getLength()) {
691
0
      error(errSyntaxError, -1, "Invalid page count in page tree");
692
0
      pages[pg-1] = new Page(doc, pg);
693
0
    }
694
0
  }
695
0
}
696
697
0
Object *Catalog::getDestOutputProfile(Object *destOutProf) {
698
0
  Object catDict, intents, intent, subtype;
699
0
  int i;
700
701
0
  if (!xref->getCatalog(&catDict)->isDict()) {
702
0
    goto err1;
703
0
  }
704
0
  if (!catDict.dictLookup("OutputIntents", &intents)->isArray()) {
705
0
    goto err2;
706
0
  }
707
0
  for (i = 0; i < intents.arrayGetLength(); ++i) {
708
0
    intents.arrayGet(i, &intent);
709
0
    if (!intent.isDict()) {
710
0
      intent.free();
711
0
      continue;
712
0
    }
713
0
    if (!intent.dictLookup("S", &subtype)->isName("GTS_PDFX")) {
714
0
      subtype.free();
715
0
      intent.free();
716
0
      continue;
717
0
    }
718
0
    subtype.free();
719
0
    if (!intent.dictLookup("DestOutputProfile", destOutProf)->isStream()) {
720
0
      destOutProf->free();
721
0
      intent.free();
722
0
      goto err2;
723
0
    }
724
0
    intent.free();
725
0
    intents.free();
726
0
    catDict.free();
727
0
    return destOutProf;
728
0
  }
729
730
0
 err2:
731
0
  intents.free();
732
0
 err1:
733
0
  catDict.free();
734
0
  return NULL;
735
0
}
736
737
0
void Catalog::readEmbeddedFileList(Dict *catDict) {
738
0
  Object obj1, obj2;
739
0
  char *touchedObjs;
740
741
  // read the embedded file name tree
742
0
  if (catDict->lookup("Names", &obj1)->isDict()) {
743
0
    if (obj1.dictLookup("EmbeddedFiles", &obj2)->isDict()) {
744
0
      readEmbeddedFileTree(&obj2);
745
0
    }
746
0
    obj2.free();
747
0
  }
748
0
  obj1.free();
749
750
  // look for file attachment annotations
751
0
  touchedObjs = (char *)gmalloc(xref->getNumObjects());
752
0
  memset(touchedObjs, 0, xref->getNumObjects());
753
0
  readFileAttachmentAnnots(catDict->lookupNF("Pages", &obj1), touchedObjs);
754
0
  obj1.free();
755
0
  gfree(touchedObjs);
756
0
}
757
758
0
void Catalog::readEmbeddedFileTree(Object *node) {
759
0
  Object kidsObj, kidObj;
760
0
  Object namesObj, nameObj, fileSpecObj;
761
0
  int i;
762
763
0
  if (node->dictLookup("Kids", &kidsObj)->isArray()) {
764
0
    for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
765
0
      if (kidsObj.arrayGet(i, &kidObj)->isDict()) {
766
0
  readEmbeddedFileTree(&kidObj);
767
0
      }
768
0
      kidObj.free();
769
0
    }
770
0
  } else {
771
0
    if (node->dictLookup("Names", &namesObj)->isArray()) {
772
0
      for (i = 0; i+1 < namesObj.arrayGetLength(); ++i) {
773
0
  namesObj.arrayGet(i, &nameObj);
774
0
  namesObj.arrayGet(i+1, &fileSpecObj);
775
0
  readEmbeddedFile(&fileSpecObj, &nameObj);
776
0
  nameObj.free();
777
0
  fileSpecObj.free();
778
0
      }
779
0
    }
780
0
    namesObj.free();
781
0
  }
782
0
  kidsObj.free();
783
0
}
784
785
void Catalog::readFileAttachmentAnnots(Object *pageNodeRef,
786
0
               char *touchedObjs) {
787
0
  Object pageNode, kids, kid, annots, annot, subtype, fileSpec, contents;
788
0
  int i;
789
790
  // check for an invalid object reference (e.g., in a damaged PDF file)
791
0
  if (pageNodeRef->getRefNum() < 0 ||
792
0
      pageNodeRef->getRefNum() >= xref->getNumObjects()) {
793
0
    return;
794
0
  }
795
796
  // check for a page tree loop
797
0
  if (pageNodeRef->isRef()) {
798
0
    if (touchedObjs[pageNodeRef->getRefNum()]) {
799
0
      return;
800
0
    }
801
0
    touchedObjs[pageNodeRef->getRefNum()] = 1;
802
0
    xref->fetch(pageNodeRef->getRefNum(), pageNodeRef->getRefGen(), &pageNode);
803
0
  } else {
804
0
    pageNodeRef->copy(&pageNode);
805
0
  }
806
807
0
  if (pageNode.isDict()) {
808
0
    if (pageNode.dictLookup("Kids", &kids)->isArray()) {
809
0
      for (i = 0; i < kids.arrayGetLength(); ++i) {
810
0
  readFileAttachmentAnnots(kids.arrayGetNF(i, &kid), touchedObjs);
811
0
  kid.free();
812
0
      }
813
0
    } else {
814
0
      if (pageNode.dictLookup("Annots", &annots)->isArray()) {
815
0
  for (i = 0; i < annots.arrayGetLength(); ++i) {
816
0
    if (annots.arrayGet(i, &annot)->isDict()) {
817
0
      if (annot.dictLookup("Subtype", &subtype)
818
0
      ->isName("FileAttachment")) {
819
0
        if (annot.dictLookup("FS", &fileSpec)) {
820
0
    readEmbeddedFile(&fileSpec,
821
0
         annot.dictLookup("Contents", &contents));
822
0
    contents.free();
823
0
        }
824
0
        fileSpec.free();
825
0
      }
826
0
      subtype.free();
827
0
    }
828
0
    annot.free();
829
0
  }
830
0
      }
831
0
      annots.free();
832
0
    }
833
0
    kids.free();
834
0
  }
835
836
0
  pageNode.free();
837
0
}
838
839
0
void Catalog::readEmbeddedFile(Object *fileSpec, Object *name1) {
840
0
  Object name2, efObj, streamObj;
841
0
  GString *s;
842
0
  TextString *name;
843
844
0
  if (fileSpec->isDict()) {
845
0
    if (fileSpec->dictLookup("UF", &name2)->isString()) {
846
0
      name = new TextString(name2.getString());
847
0
    } else {
848
0
      name2.free();
849
0
      if (fileSpec->dictLookup("F", &name2)->isString()) {
850
0
  name = new TextString(name2.getString());
851
0
      } else if (name1 && name1->isString()) {
852
0
  name = new TextString(name1->getString());
853
0
      } else {
854
0
  s = new GString("?");
855
0
  name = new TextString(s);
856
0
  delete s;
857
0
      }
858
0
    }
859
0
    name2.free();
860
0
    if (fileSpec->dictLookup("EF", &efObj)->isDict()) {
861
0
      if (efObj.dictLookupNF("F", &streamObj)->isRef()) {
862
0
  if (!embeddedFiles) {
863
0
    embeddedFiles = new GList();
864
0
  }
865
0
  embeddedFiles->append(new EmbeddedFile(name, &streamObj));
866
0
      } else {
867
0
  delete name;
868
0
      }
869
0
      streamObj.free();
870
0
    } else {
871
0
      delete name;
872
0
    }
873
0
    efObj.free();
874
0
  }
875
0
}
876
877
0
int Catalog::getNumEmbeddedFiles() {
878
0
  return embeddedFiles ? embeddedFiles->getLength() : 0;
879
0
}
880
881
0
Unicode *Catalog::getEmbeddedFileName(int idx) {
882
0
  return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getUnicode();
883
0
}
884
885
0
int Catalog::getEmbeddedFileNameLength(int idx) {
886
0
  return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getLength();
887
0
}
888
889
0
Object *Catalog::getEmbeddedFileStreamRef(int idx) {
890
0
  return &((EmbeddedFile *)embeddedFiles->get(idx))->streamRef;
891
0
}
892
893
0
Object *Catalog::getEmbeddedFileStreamObj(int idx, Object *strObj) {
894
0
  ((EmbeddedFile *)embeddedFiles->get(idx))->streamRef.fetch(xref, strObj);
895
0
  if (!strObj->isStream()) {
896
0
    strObj->free();
897
0
    return NULL;
898
0
  }
899
0
  return strObj;
900
0
}
901
902
0
void Catalog::readPageLabelTree(Object *root) {
903
0
  PageLabelNode *label0, *label1;
904
0
  int i;
905
906
0
  pageLabels = new GList();
907
0
  readPageLabelTree2(root);
908
909
0
  if (pageLabels->getLength() == 0) {
910
0
    deleteGList(pageLabels, PageLabelNode);
911
0
    pageLabels = NULL;
912
0
    return;
913
0
  }
914
915
  // set lastPage in each node
916
0
  label0 = (PageLabelNode *)pageLabels->get(0);
917
0
  for (i = 1; i < pageLabels->getLength(); ++i) {
918
0
    label1 = (PageLabelNode *)pageLabels->get(i);
919
0
    label0->lastPage = label1->firstPage - 1;
920
0
    label0 = label1;
921
0
  }
922
0
  label0->lastPage = numPages;
923
0
}
924
925
0
void Catalog::readPageLabelTree2(Object *node) {
926
0
  Object nums, num, labelObj, kids, kid;
927
0
  int i;
928
929
0
  if (!node->isDict()) {
930
0
    return;
931
0
  }
932
933
0
  if (node->dictLookup("Nums", &nums)->isArray()) {
934
0
    for (i = 0; i < nums.arrayGetLength() - 1; i += 2) {
935
0
      if (nums.arrayGet(i, &num)->isInt()) {
936
0
  if (nums.arrayGet(i+1, &labelObj)->isDict()) {
937
0
    pageLabels->append(new PageLabelNode(num.getInt(),
938
0
                 labelObj.getDict()));
939
0
  }
940
0
  labelObj.free();
941
0
      }
942
0
      num.free();
943
0
    }
944
0
  }
945
0
  nums.free();
946
947
0
  if (node->dictLookup("Kids", &kids)->isArray()) {
948
0
    for (i = 0; i < kids.arrayGetLength(); ++i) {
949
0
      kids.arrayGet(i, &kid);
950
0
      readPageLabelTree2(&kid);
951
0
      kid.free();
952
0
    }
953
0
  }
954
0
  kids.free();
955
0
}
956
957
0
TextString *Catalog::getPageLabel(int pageNum) {
958
0
  PageLabelNode *label;
959
0
  TextString *ts;
960
0
  int pageRangeNum;
961
0
  GString *suffix;
962
963
0
  if (!pageLabels || !(label = findPageLabel(pageNum))) {
964
0
    return NULL;
965
0
  }
966
967
0
  ts = new TextString(label->prefix);
968
969
0
  pageRangeNum = label->start + (pageNum - label->firstPage);
970
971
0
  suffix = NULL;
972
0
  if (label->style == 'D') {
973
0
    suffix = GString::format("{0:d}", pageRangeNum);
974
0
  } else if (label->style == 'R') {
975
0
    suffix = makeRomanNumeral(pageRangeNum, gTrue);
976
0
  } else if (label->style == 'r') {
977
0
    suffix = makeRomanNumeral(pageRangeNum, gFalse);
978
0
  } else if (label->style == 'A') {
979
0
    suffix = makeLetterLabel(pageRangeNum, gTrue);
980
0
  } else if (label->style == 'a') {
981
0
    suffix = makeLetterLabel(pageRangeNum, gFalse);
982
0
  }
983
0
  if (suffix) {
984
0
    ts->append(suffix);
985
0
    delete suffix;
986
0
  }
987
988
0
  return ts;
989
0
}
990
991
0
PageLabelNode *Catalog::findPageLabel(int pageNum) {
992
0
  PageLabelNode *label;
993
0
  int i;
994
995
  //~ this could use a binary search
996
0
  for (i = 0; i < pageLabels->getLength(); ++i) {
997
0
    label = (PageLabelNode *)pageLabels->get(i);
998
0
    if (pageNum >= label->firstPage && pageNum <= label->lastPage) {
999
0
      return label;
1000
0
    }
1001
0
  }
1002
0
  return NULL;
1003
0
}
1004
1005
0
GString *Catalog::makeRomanNumeral(int num, GBool uppercase) {
1006
0
  GString *s;
1007
1008
0
  s = new GString();
1009
0
  while (num >= 1000) {
1010
0
    s->append(uppercase ? 'M' : 'm');
1011
0
    num -= 1000;
1012
0
  }
1013
0
  if (num >= 900) {
1014
0
    s->append(uppercase ? "CM" : "cm");
1015
0
    num -= 900;
1016
0
  } else if (num >= 500) {
1017
0
    s->append(uppercase ? 'D' : 'd');
1018
0
    num -= 500;
1019
0
  } else if (num >= 400) {
1020
0
    s->append(uppercase ? "CD" : "cd");
1021
0
    num -= 400;
1022
0
  }
1023
0
  while (num >= 100) {
1024
0
    s->append(uppercase ? 'C' : 'c');
1025
0
    num -= 100;
1026
0
  }
1027
0
  if (num >= 90) {
1028
0
    s->append(uppercase ? "XC" : "xc");
1029
0
    num -= 90;
1030
0
  } else if (num >= 50) {
1031
0
    s->append(uppercase ? 'L' : 'l');
1032
0
    num -= 50;
1033
0
  } else if (num >= 40) {
1034
0
    s->append(uppercase ? "XL" : "xl");
1035
0
    num -= 40;
1036
0
  }
1037
0
  while (num >= 10) {
1038
0
    s->append(uppercase ? 'X' : 'x');
1039
0
    num -= 10;
1040
0
  }
1041
0
  if (num >= 9) {
1042
0
    s->append(uppercase ? "IX" : "ix");
1043
0
    num -= 9;
1044
0
  } else if (num >= 5) {
1045
0
    s->append(uppercase ? 'V' : 'v');
1046
0
    num -= 5;
1047
0
  } else if (num >= 4) {
1048
0
    s->append(uppercase ? "IV" : "iv");
1049
0
    num -= 4;
1050
0
  }
1051
0
  while (num >= 1) {
1052
0
    s->append(uppercase ? 'I' : 'i');
1053
0
    num -= 1;
1054
0
  }
1055
0
  return s;
1056
0
}
1057
1058
0
GString *Catalog::makeLetterLabel(int num, GBool uppercase) {
1059
0
  GString *s;
1060
0
  int m, n, i;
1061
1062
0
  m = (num - 1) / 26 + 1;
1063
0
  n = (num - 1) % 26;
1064
0
  s = new GString();
1065
0
  for (i = 0; i < m; ++i) {
1066
0
    s->append((char)((uppercase ? 'A' : 'a') + n));
1067
0
  }
1068
0
  return s;
1069
0
}
1070
1071
0
int Catalog::getPageNumFromPageLabel(TextString *pageLabel) {
1072
0
  PageLabelNode *label;
1073
0
  int pageNum, prefixLength, i, n;
1074
1075
0
  if (!pageLabels) {
1076
0
    return -1;
1077
0
  }
1078
0
  for (i = 0; i < pageLabels->getLength(); ++i) {
1079
0
    label = (PageLabelNode *)pageLabels->get(i);
1080
0
    prefixLength = label->prefix->getLength();
1081
0
    if (pageLabel->getLength() < prefixLength ||
1082
0
  memcmp(pageLabel->getUnicode(), label->prefix->getUnicode(),
1083
0
         prefixLength * sizeof(Unicode))) {
1084
0
      continue;
1085
0
    }
1086
0
    if (label->style == '\0' && pageLabel->getLength() == prefixLength) {
1087
0
      return label->firstPage;
1088
0
    }
1089
0
    if (!convertPageLabelToInt(pageLabel, prefixLength, label->style, &n)) {
1090
0
      continue;
1091
0
    }
1092
0
    if (n < label->start) {
1093
0
      continue;
1094
0
    }
1095
0
    pageNum = label->firstPage + n - label->start;
1096
0
    if (pageNum <= label->lastPage) {
1097
0
      return pageNum;
1098
0
    }
1099
0
  }
1100
0
  return -1;
1101
0
}
1102
1103
// Attempts to convert pageLabel[prefixLength .. end] to an integer,
1104
// following the specified page label style.  If successful, sets *n
1105
// and returns true; else returns false.
1106
GBool Catalog::convertPageLabelToInt(TextString *pageLabel, int prefixLength,
1107
0
             char style, int *n) {
1108
0
  Unicode *u;
1109
0
  Unicode delta;
1110
0
  int len, i;
1111
1112
0
  len = pageLabel->getLength();
1113
0
  if (len <= prefixLength) {
1114
0
    return gFalse;
1115
0
  }
1116
0
  u = pageLabel->getUnicode();
1117
0
  if (style == 'D') {
1118
0
    *n = 0;
1119
0
    for (i = prefixLength; i < len; ++i) {
1120
0
      if (u[i] < (Unicode)'0' || u[i] > (Unicode)'9') {
1121
0
  return gFalse;
1122
0
      }
1123
0
      *n = *n * 10 + (u[i] - (Unicode)'0');
1124
0
    }
1125
0
    return gTrue;
1126
0
  } else if (style == 'R' || style == 'r') {
1127
0
    delta = style - 'R';
1128
0
    *n = 0;
1129
0
    i = prefixLength;
1130
0
    while (i < len && u[i] == (Unicode)'M' + delta) {
1131
0
      *n += 1000;
1132
0
      ++i;
1133
0
    }
1134
0
    if (i+1 < len && u[i] == (Unicode)'C' + delta &&
1135
0
  u[i+1] == (Unicode)'M' + delta) {
1136
0
      *n += 900;
1137
0
      i += 2;
1138
0
    } else if (i < len && u[i] == (Unicode)'D' + delta) {
1139
0
      *n += 500;
1140
0
      ++i;
1141
0
    } else if (i+1 < len && u[i] == (Unicode)'C' + delta &&
1142
0
         u[i+1] == (Unicode)'D' + delta) {
1143
0
      *n += 400;
1144
0
      i += 2;
1145
0
    }
1146
0
    while (i < len && u[i] == (Unicode)'C' + delta) {
1147
0
      *n += 100;
1148
0
      ++i;
1149
0
    }
1150
0
    if (i+1 < len && u[i] == (Unicode)'X' + delta &&
1151
0
  u[i+1] == (Unicode)'C' + delta) {
1152
0
      *n += 90;
1153
0
      i += 2;
1154
0
    } else if (i < len && u[i] == (Unicode)'L' + delta) {
1155
0
      *n += 50;
1156
0
      ++i;
1157
0
    } else if (i+1 < len && u[i] == (Unicode)'X' + delta &&
1158
0
         u[i+1] == (Unicode)'L' + delta) {
1159
0
      *n += 40;
1160
0
      i += 2;
1161
0
    }
1162
0
    while (i < len && u[i] == (Unicode)'X' + delta) {
1163
0
      *n += 10;
1164
0
      ++i;
1165
0
    }
1166
0
    if (i+1 < len && u[i] == (Unicode)'I' + delta &&
1167
0
  u[i+1] == (Unicode)'X' + delta) {
1168
0
      *n += 9;
1169
0
      i += 2;
1170
0
    } else if (i < len && u[i] == (Unicode)'V' + delta) {
1171
0
      *n += 5;
1172
0
      ++i;
1173
0
    } else if (i+1 < len && u[i] == (Unicode)'I' + delta &&
1174
0
         u[i+1] == (Unicode)'V' + delta) {
1175
0
      *n += 4;
1176
0
      i += 2;
1177
0
    }
1178
0
    while (i < len && u[i] == (Unicode)'I' + delta) {
1179
0
      *n += 1;
1180
0
      ++i;
1181
0
    }
1182
0
    return i == len;
1183
0
  } else if (style == 'A' || style == 'a') {
1184
0
    if (u[prefixLength] < (Unicode)style ||
1185
0
  u[prefixLength] > (Unicode)style + 25) {
1186
0
      return gFalse;
1187
0
    }
1188
0
    for (i = prefixLength + 1; i < len; ++i) {
1189
0
      if (u[i] != u[prefixLength]) {
1190
0
  return gFalse;
1191
0
      }
1192
0
    }
1193
0
    *n = (len - prefixLength - 1) * 26 + (u[prefixLength] - (Unicode)style) + 1;
1194
0
    return gTrue;
1195
0
  }
1196
0
  return gFalse;
1197
0
}