Coverage Report

Created: 2026-05-30 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/xpdf/Catalog.cc
Line
Count
Source
1
//========================================================================
2
//
3
// Catalog.cc
4
//
5
// Copyright 1996-2013 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
#include <aconf.h>
10
11
#include <string.h>
12
#include <stddef.h>
13
#include <limits.h>
14
#include "gmem.h"
15
#include "gmempp.h"
16
#include "gfile.h"
17
#include "GList.h"
18
#include "Object.h"
19
#include "CharTypes.h"
20
#include "PDFDoc.h"
21
#include "XRef.h"
22
#include "Array.h"
23
#include "Dict.h"
24
#include "Page.h"
25
#include "Error.h"
26
#include "Link.h"
27
#include "AcroForm.h"
28
#include "TextString.h"
29
#include "Catalog.h"
30
31
//------------------------------------------------------------------------
32
// PageTreeNode
33
//------------------------------------------------------------------------
34
35
class PageTreeNode {
36
public:
37
38
  PageTreeNode(Ref refA, int countA, PageTreeNode *parentA);
39
  ~PageTreeNode();
40
41
  Ref ref;
42
  int count;
43
  PageTreeNode *parent;
44
  GList *kids;      // [PageTreeNode]
45
  PageAttrs *attrs;
46
};
47
48
62.5k
PageTreeNode::PageTreeNode(Ref refA, int countA, PageTreeNode *parentA) {
49
62.5k
  ref = refA;
50
62.5k
  count = countA;
51
62.5k
  parent = parentA;
52
62.5k
  kids = NULL;
53
62.5k
  attrs = NULL;
54
62.5k
}
55
56
62.5k
PageTreeNode::~PageTreeNode() {
57
62.5k
  delete attrs;
58
62.5k
  if (kids) {
59
14.0k
    deleteGList(kids, PageTreeNode);
60
14.0k
  }
61
62.5k
}
62
63
//------------------------------------------------------------------------
64
// EmbeddedFile
65
//------------------------------------------------------------------------
66
67
class EmbeddedFile {
68
public:
69
70
  EmbeddedFile(TextString *nameA, Object *streamRefA);
71
  ~EmbeddedFile();
72
73
  TextString *name;
74
  Object streamRef;
75
};
76
77
118
EmbeddedFile::EmbeddedFile(TextString *nameA, Object *streamRefA) {
78
118
  name = nameA;
79
118
  streamRefA->copy(&streamRef);
80
118
}
81
82
118
EmbeddedFile::~EmbeddedFile() {
83
118
  delete name;
84
118
  streamRef.free();
85
118
}
86
87
//------------------------------------------------------------------------
88
// PageLabelNode
89
//------------------------------------------------------------------------
90
91
class PageLabelNode {
92
public:
93
94
  PageLabelNode(int firstPageA, Dict *dict);
95
  ~PageLabelNode();
96
97
  int firstPage;    // first page number covered by this node
98
  int lastPage;     // last page number covered by this node
99
  TextString *prefix;   // label prefix (may be empty)
100
  int start;      // value of the numeric portion of this
101
        //   label for the first page in the range
102
  char style;     // page label style
103
};
104
105
951
PageLabelNode::PageLabelNode(int firstPageA, Dict *dict) {
106
951
  Object prefixObj, styleObj, startObj;
107
108
  // convert page index to page number
109
951
  firstPage = firstPageA + 1;
110
111
  // lastPage will be filled in later
112
951
  lastPage = -1;
113
114
951
  if (dict->lookup("P", &prefixObj)->isString()) {
115
364
    prefix = new TextString(prefixObj.getString());
116
587
  } else {
117
587
    prefix = new TextString();
118
587
  }
119
951
  prefixObj.free();
120
121
951
  style = '\0';
122
951
  if (dict->lookup("S", &styleObj)->isName()) {
123
302
    if (strlen(styleObj.getName()) == 1) {
124
237
      style = styleObj.getName()[0];
125
237
    }
126
302
  }
127
951
  styleObj.free();
128
129
951
  start = 1;
130
951
  if (dict->lookup("St", &startObj)->isInt()) {
131
159
    start = startObj.getInt();
132
159
  }
133
951
  startObj.free();
134
951
}
135
136
951
PageLabelNode::~PageLabelNode() {
137
951
  delete prefix;
138
951
}
139
140
//------------------------------------------------------------------------
141
// Catalog
142
//------------------------------------------------------------------------
143
144
15.3k
Catalog::Catalog(PDFDoc *docA) {
145
15.3k
  Object catDict;
146
15.3k
  Object obj, obj2;
147
148
15.3k
  ok = gTrue;
149
15.3k
  doc = docA;
150
15.3k
  xref = doc->getXRef();
151
15.3k
  pageTree = NULL;
152
15.3k
  pages = NULL;
153
15.3k
  pageRefs = NULL;
154
15.3k
  numPages = 0;
155
15.3k
  baseURI = NULL;
156
15.3k
  form = NULL;
157
15.3k
  embeddedFiles = NULL;
158
15.3k
  pageLabels = NULL;
159
15.3k
#if MULTITHREADED
160
15.3k
  gInitMutex(&pageMutex);
161
15.3k
#endif
162
163
15.3k
  xref->getCatalog(&catDict);
164
15.3k
  if (!catDict.isDict()) {
165
248
    error(errSyntaxError, -1, "Catalog object is wrong type ({0:s})",
166
248
    catDict.getTypeName());
167
248
    goto err1;
168
248
  }
169
170
  // read page tree
171
15.0k
  if (!readPageTree(&catDict)) {
172
302
    goto err1;
173
302
  }
174
175
  // read named destination dictionary
176
14.7k
  catDict.dictLookup("Dests", &dests);
177
178
  // read root of named destination tree
179
14.7k
  if (catDict.dictLookup("Names", &obj)->isDict())
180
878
    obj.dictLookup("Dests", &nameTree);
181
13.8k
  else
182
13.8k
    nameTree.initNull();
183
14.7k
  obj.free();
184
185
  // read base URI
186
14.7k
  if (catDict.dictLookup("URI", &obj)->isDict()) {
187
2
    if (obj.dictLookup("Base", &obj2)->isString()) {
188
1
      baseURI = obj2.getString()->copy();
189
1
    }
190
2
    obj2.free();
191
2
  }
192
14.7k
  obj.free();
193
14.7k
  if (!baseURI || baseURI->getLength() == 0) {
194
14.7k
    if (baseURI) {
195
0
      delete baseURI;
196
0
    }
197
14.7k
    if (doc->getFileName()) {
198
0
      baseURI = makePathAbsolute(grabPath(doc->getFileName()->getCString()));
199
0
      if (baseURI->getChar(0) == '/') {
200
0
  baseURI->insert(0, "file://localhost");
201
0
      } else {
202
0
  baseURI->insert(0, "file://localhost/");
203
0
      }
204
14.7k
    } else {
205
14.7k
      baseURI = new GString("file://localhost/");
206
14.7k
    }
207
14.7k
  }
208
209
  // get the metadata stream
210
14.7k
  catDict.dictLookup("Metadata", &metadata);
211
212
  // get the structure tree root
213
14.7k
  catDict.dictLookup("StructTreeRoot", &structTreeRoot);
214
215
  // get the outline dictionary
216
14.7k
  catDict.dictLookup("Outlines", &outline);
217
218
  // get the AcroForm dictionary
219
14.7k
  catDict.dictLookup("AcroForm", &acroForm);
220
221
  // get the NeedsRendering flag
222
  // NB: AcroForm::load() uses this value
223
14.7k
  needsRendering = catDict.dictLookup("NeedsRendering", &obj)->isBool() &&
224
11
                   obj.getBool();
225
14.7k
  obj.free();
226
227
  // create the Form
228
  // (if acroForm is a null object, this will still create an AcroForm
229
  // if there are unattached Widget-type annots)
230
14.7k
  form = AcroForm::load(doc, this, &acroForm);
231
232
  // get the OCProperties dictionary
233
14.7k
  catDict.dictLookup("OCProperties", &ocProperties);
234
235
  // get the list of embedded files
236
14.7k
  readEmbeddedFileList(catDict.getDict());
237
238
  // get the ViewerPreferences object
239
14.7k
  catDict.dictLookupNF("ViewerPreferences", &viewerPrefs);
240
241
14.7k
  if (catDict.dictLookup("PageLabels", &obj)->isDict()) {
242
456
    readPageLabelTree(&obj);
243
456
  }
244
14.7k
  obj.free();
245
246
14.7k
  catDict.free();
247
14.7k
  return;
248
249
550
 err1:
250
550
  catDict.free();
251
550
  dests.initNull();
252
550
  nameTree.initNull();
253
550
  ok = gFalse;
254
550
}
255
256
15.3k
Catalog::~Catalog() {
257
15.3k
  int i;
258
259
15.3k
  if (pageTree) {
260
14.7k
    delete pageTree;
261
14.7k
  }
262
15.3k
  if (pages) {
263
490k
    for (i = 0; i < numPages; ++i) {
264
475k
      if (pages[i]) {
265
475k
  delete pages[i];
266
475k
      }
267
475k
    }
268
14.7k
    gfree(pages);
269
14.7k
    gfree(pageRefs);
270
14.7k
  }
271
15.3k
#if MULTITHREADED
272
15.3k
  gDestroyMutex(&pageMutex);
273
15.3k
#endif
274
15.3k
  dests.free();
275
15.3k
  nameTree.free();
276
15.3k
  if (baseURI) {
277
14.7k
    delete baseURI;
278
14.7k
  }
279
15.3k
  metadata.free();
280
15.3k
  structTreeRoot.free();
281
15.3k
  outline.free();
282
15.3k
  acroForm.free();
283
15.3k
  if (form) {
284
2.67k
    delete form;
285
2.67k
  }
286
15.3k
  ocProperties.free();
287
15.3k
  if (embeddedFiles) {
288
8
    deleteGList(embeddedFiles, EmbeddedFile);
289
8
  }
290
15.3k
  if (pageLabels) {
291
156
    deleteGList(pageLabels, PageLabelNode);
292
156
  }
293
15.3k
  viewerPrefs.free();
294
15.3k
}
295
296
2.21M
Page *Catalog::getPage(int i) {
297
2.21M
  Page *page;
298
299
2.21M
#if MULTITHREADED
300
2.21M
  gLockMutex(&pageMutex);
301
2.21M
#endif
302
2.21M
  if (!pages[i-1]) {
303
475k
    loadPage(i);
304
475k
  }
305
2.21M
  page = pages[i-1];
306
2.21M
#if MULTITHREADED
307
2.21M
  gUnlockMutex(&pageMutex);
308
2.21M
#endif
309
2.21M
  return page;
310
2.21M
}
311
312
0
Ref *Catalog::getPageRef(int i) {
313
0
  Ref *pageRef;
314
315
0
#if MULTITHREADED
316
0
  gLockMutex(&pageMutex);
317
0
#endif
318
0
  if (!pages[i-1]) {
319
0
    loadPage(i);
320
0
  }
321
0
  pageRef = &pageRefs[i-1];
322
0
#if MULTITHREADED
323
0
  gUnlockMutex(&pageMutex);
324
0
#endif
325
0
  return pageRef;
326
0
}
327
328
0
void Catalog::doneWithPage(int i) {
329
0
#if MULTITHREADED
330
0
  gLockMutex(&pageMutex);
331
0
#endif
332
0
  if (pages[i-1]) {
333
0
    delete pages[i-1];
334
0
    pages[i-1] = NULL;
335
0
  }
336
0
#if MULTITHREADED
337
0
  gUnlockMutex(&pageMutex);
338
0
#endif
339
0
}
340
341
12.4k
GString *Catalog::readMetadata() {
342
12.4k
  GString *s;
343
12.4k
  Dict *dict;
344
12.4k
  Object obj;
345
12.4k
  char buf[4096];
346
12.4k
  int n;
347
348
12.4k
  if (!metadata.isStream()) {
349
11.4k
    return NULL;
350
11.4k
  }
351
929
  dict = metadata.streamGetDict();
352
929
  if (!dict->lookup("Subtype", &obj)->isName("XML")) {
353
169
    error(errSyntaxWarning, -1, "Unknown Metadata type: '{0:s}'",
354
169
    obj.isName() ? obj.getName() : "???");
355
169
  }
356
929
  obj.free();
357
929
  s = new GString();
358
929
  metadata.streamReset();
359
2.68k
  while ((n = metadata.streamGetBlock(buf, sizeof(buf))) > 0) {
360
1.75k
    s->append(buf, n);
361
1.75k
  }
362
929
  metadata.streamClose();
363
929
  return s;
364
12.4k
}
365
366
0
int Catalog::findPage(int num, int gen) {
367
0
  int i;
368
369
0
#if MULTITHREADED
370
0
  gLockMutex(&pageMutex);
371
0
#endif
372
0
  for (i = 0; i < numPages; ++i) {
373
0
    if (!pages[i]) {
374
0
      loadPage(i+1);
375
0
    }
376
0
    if (pageRefs[i].num == num && pageRefs[i].gen == gen) {
377
0
#if MULTITHREADED
378
0
      gUnlockMutex(&pageMutex);
379
0
#endif
380
0
      return i + 1;
381
0
    }
382
0
  }
383
0
#if MULTITHREADED
384
0
  gUnlockMutex(&pageMutex);
385
0
#endif
386
0
  return 0;
387
0
}
388
389
0
LinkDest *Catalog::findDest(GString *name) {
390
0
  LinkDest *dest;
391
0
  Object obj1, obj2;
392
0
  GBool found;
393
394
  // try named destination dictionary then name tree
395
0
  found = gFalse;
396
0
  if (dests.isDict()) {
397
0
    if (!dests.dictLookup(name->getCString(), &obj1)->isNull()) {
398
0
      found = gTrue;
399
0
    } else {
400
0
      obj1.free();
401
0
    }
402
0
  }
403
0
  if (!found && nameTree.isDict()) {
404
0
    char *touchedObjs = (char *)gmalloc(xref->getNumObjects());
405
0
    memset(touchedObjs, 0, xref->getNumObjects());
406
0
    if (!findDestInTree(&nameTree, &nameTree, name, &obj1, touchedObjs)
407
0
  ->isNull()) {
408
0
      found = gTrue;
409
0
    } else {
410
0
      obj1.free();
411
0
    }
412
0
    gfree(touchedObjs);
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
Object *Catalog::findDestInTree(Object *treeRef, Object *tree, GString *name,
442
0
        Object *obj, char *touchedObjs) {
443
0
  Object names, name1;
444
0
  Object kids, kidRef, kid, limits, low, high;
445
0
  GBool done, found;
446
0
  int cmp, i;
447
448
  // check for invalid reference
449
0
  if (treeRef->isRef() &&
450
0
      (treeRef->getRefNum() < 0 ||
451
0
       treeRef->getRefNum() >= xref->getNumObjects())) {
452
0
    obj->initNull();
453
0
    return obj;
454
0
  }
455
456
  // check for a destination tree loop
457
0
  if (treeRef->isRef()) {
458
0
    if (touchedObjs[treeRef->getRefNum()]) {
459
0
      error(errSyntaxError, -1, "Loop in destination name tree");
460
0
      obj->initNull();
461
0
      return obj;
462
0
    }
463
0
    touchedObjs[treeRef->getRefNum()] = 1;
464
0
  }
465
466
  // leaf node
467
0
  if (tree->dictLookup("Names", &names)->isArray()) {
468
0
    done = found = gFalse;
469
0
    for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
470
0
      if (names.arrayGet(i, &name1)->isString()) {
471
0
  cmp = name->cmp(name1.getString());
472
0
  if (cmp == 0) {
473
0
    names.arrayGet(i+1, obj);
474
0
    found = gTrue;
475
0
    done = gTrue;
476
0
  } else if (cmp < 0) {
477
0
    done = gTrue;
478
0
  }
479
0
      }
480
0
      name1.free();
481
0
    }
482
0
    names.free();
483
0
    if (!found) {
484
0
      obj->initNull();
485
0
    }
486
0
    return obj;
487
0
  }
488
0
  names.free();
489
490
  // root or intermediate node
491
0
  done = gFalse;
492
0
  if (tree->dictLookup("Kids", &kids)->isArray()) {
493
0
    for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
494
0
      kids.arrayGetNF(i, &kidRef);
495
0
      kids.arrayGet(i, &kid);
496
0
      if (kid.isDict()) {
497
0
  if (kid.dictLookup("Limits", &limits)->isArray()) {
498
0
    if (limits.arrayGet(0, &low)->isString() &&
499
0
        name->cmp(low.getString()) >= 0) {
500
0
      if (limits.arrayGet(1, &high)->isString() &&
501
0
    name->cmp(high.getString()) <= 0) {
502
0
        findDestInTree(&kidRef, &kid, name, obj, touchedObjs);
503
0
        done = gTrue;
504
0
      }
505
0
      high.free();
506
0
    }
507
0
    low.free();
508
0
  }
509
0
  limits.free();
510
0
      }
511
0
      kid.free();
512
0
      kidRef.free();
513
0
    }
514
0
  }
515
0
  kids.free();
516
517
  // name was outside of ranges of all kids
518
0
  if (!done) {
519
0
    obj->initNull();
520
0
  }
521
522
0
  return obj;
523
0
}
524
525
15.0k
GBool Catalog::readPageTree(Object *catDict) {
526
15.0k
  Object topPagesRef, topPagesObj, countObj;
527
15.0k
  int i;
528
529
15.0k
  if (!catDict->dictLookupNF("Pages", &topPagesRef)->isRef()) {
530
177
    error(errSyntaxError, -1, "Top-level pages reference is wrong type ({0:s})",
531
177
    topPagesRef.getTypeName());
532
177
    topPagesRef.free();
533
177
    return gFalse;
534
177
  }
535
14.8k
  if (!topPagesRef.fetch(xref, &topPagesObj)->isDict()) {
536
124
    error(errSyntaxError, -1, "Top-level pages object is wrong type ({0:s})",
537
124
    topPagesObj.getTypeName());
538
124
    topPagesObj.free();
539
124
    topPagesRef.free();
540
124
    return gFalse;
541
124
  }
542
14.7k
  if (topPagesObj.dictLookup("Count", &countObj)->isInt()) {
543
11.9k
    numPages = countObj.getInt();
544
11.9k
    if (numPages == 0 || numPages > 50000) {
545
      // 1. Acrobat apparently scans the page tree if it sees a zero
546
      //    count.
547
      // 2. Absurdly large page counts result in very slow loading,
548
      //    because other code tries to fetch pages 1 through n.
549
      // In both cases: ignore the given page count and scan the tree
550
      // instead.
551
269
      char *touchedObjs = (char *)gmalloc(xref->getNumObjects());
552
269
      memset(touchedObjs, 0, xref->getNumObjects());
553
269
      numPages = countPageTree(&topPagesRef, touchedObjs);
554
269
      gfree(touchedObjs);
555
269
    }
556
11.9k
  } else {
557
    // assume we got a Page node instead of a Pages node
558
2.79k
    numPages = 1;
559
2.79k
  }
560
14.7k
  countObj.free();
561
14.7k
  if (numPages < 0) {
562
1
    error(errSyntaxError, -1, "Invalid page count");
563
1
    topPagesObj.free();
564
1
    topPagesRef.free();
565
1
    numPages = 0;
566
1
    return gFalse;
567
1
  }
568
14.7k
  pageTree = new PageTreeNode(topPagesRef.getRef(), numPages, NULL);
569
14.7k
  topPagesObj.free();
570
14.7k
  topPagesRef.free();
571
14.7k
  pages = (Page **)greallocn(pages, numPages, sizeof(Page *));
572
14.7k
  pageRefs = (Ref *)greallocn(pageRefs, numPages, sizeof(Ref));
573
490k
  for (i = 0; i < numPages; ++i) {
574
475k
    pages[i] = NULL;
575
475k
    pageRefs[i].num = -1;
576
475k
    pageRefs[i].gen = -1;
577
475k
  }
578
14.7k
  return gTrue;
579
14.7k
}
580
581
122k
int Catalog::countPageTree(Object *pagesNodeRef, char *touchedObjs) {
582
  // check for invalid reference
583
122k
  if (pagesNodeRef->isRef() &&
584
20.2k
      (pagesNodeRef->getRefNum() < 0 ||
585
20.2k
       pagesNodeRef->getRefNum() >= xref->getNumObjects())) {
586
1.22k
    return 0;
587
1.22k
  }
588
589
  // check for a page tree loop; fetch the node object
590
121k
  Object pagesNode;
591
121k
  if (pagesNodeRef->isRef()) {
592
18.9k
    if (touchedObjs[pagesNodeRef->getRefNum()]) {
593
12.6k
      error(errSyntaxError, -1, "Loop in Pages tree");
594
12.6k
      return 0;
595
12.6k
    }
596
6.33k
    touchedObjs[pagesNodeRef->getRefNum()] = 1;
597
6.33k
    xref->fetch(pagesNodeRef->getRefNum(), pagesNodeRef->getRefGen(),
598
6.33k
    &pagesNode);
599
102k
  } else {
600
102k
    pagesNodeRef->copy(&pagesNode);
601
102k
  }
602
603
  // count the subtree
604
109k
  int n = 0;
605
109k
  if (pagesNode.isDict()) {
606
8.38k
    Object kidsRef, kids;
607
8.38k
    pagesNode.dictLookupNF("Kids", &kidsRef);
608
8.38k
    if (kidsRef.isRef() &&
609
82
  kidsRef.getRefNum() >= 0 &&
610
82
  kidsRef.getRefNum() < xref->getNumObjects()) {
611
71
      if (touchedObjs[kidsRef.getRefNum()]) {
612
57
  error(errSyntaxError, -1, "Loop in Pages tree");
613
57
  kidsRef.free();
614
57
  pagesNode.free();
615
57
  return 0;
616
57
      }
617
14
      touchedObjs[kidsRef.getRefNum()] = 1;
618
14
      xref->fetch(kidsRef.getRefNum(), kidsRef.getRefGen(), &kids);
619
8.31k
    } else {
620
8.31k
      kidsRef.copy(&kids);
621
8.31k
    }
622
8.33k
    kidsRef.free();
623
8.33k
    if (kids.isArray()) {
624
123k
      for (int i = 0; i < kids.arrayGetLength(); ++i) {
625
122k
  Object kid;
626
122k
  kids.arrayGetNF(i, &kid);
627
122k
  int n2 = countPageTree(&kid, touchedObjs);
628
122k
  if (n2 < INT_MAX - n) {
629
122k
    n += n2;
630
122k
  } else {
631
0
    error(errSyntaxError, -1, "Page tree contains too many pages");
632
0
    n = INT_MAX;
633
0
  }
634
122k
  kid.free();
635
122k
      }
636
7.02k
    } else {
637
7.02k
      n = 1;
638
7.02k
    }
639
8.33k
    kids.free();
640
8.33k
  }
641
642
109k
  pagesNode.free();
643
644
109k
  return n;
645
109k
}
646
647
475k
void Catalog::loadPage(int pg) {
648
475k
  loadPage2(pg, pg - 1, pageTree);
649
475k
}
650
651
505k
void Catalog::loadPage2(int pg, int relPg, PageTreeNode *node) {
652
505k
  Object pageRefObj, pageObj, kidsObj, kidRefObj, kidObj, countObj;
653
505k
  PageTreeNode *kidNode, *p;
654
505k
  PageAttrs *attrs;
655
505k
  int count, i;
656
657
505k
  if (relPg >= node->count) {
658
0
    error(errSyntaxError, -1, "Internal error in page tree");
659
0
    pages[pg-1] = new Page(doc, pg);
660
0
    return;
661
0
  }
662
663
  // if this node has not been filled in yet, it's either a leaf node
664
  // or an unread internal node
665
505k
  if (!node->kids) {
666
667
    // check for a loop in the page tree
668
325k
    for (p = node->parent; p; p = p->parent) {
669
28.0k
      if (node->ref.num == p->ref.num && node->ref.gen == p->ref.gen) {
670
3.65k
  error(errSyntaxError, -1, "Loop in Pages tree");
671
3.65k
  pages[pg-1] = new Page(doc, pg);
672
3.65k
  return;
673
3.65k
      }
674
28.0k
    }
675
676
    // fetch the Page/Pages object
677
296k
    pageRefObj.initRef(node->ref.num, node->ref.gen);
678
296k
    if (!pageRefObj.fetch(xref, &pageObj)->isDict()) {
679
0
      error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})",
680
0
      pageObj.getTypeName());
681
0
      pageObj.free();
682
0
      pageRefObj.free();
683
0
      pages[pg-1] = new Page(doc, pg);
684
0
      return;
685
0
    }
686
687
    // merge the PageAttrs
688
296k
    attrs = new PageAttrs(node->parent ? node->parent->attrs
689
296k
                     : (PageAttrs *)NULL,
690
296k
        pageObj.getDict(), xref);
691
692
    // if "Kids" exists, it's an internal node
693
296k
    if (pageObj.dictLookup("Kids", &kidsObj)->isArray()) {
694
695
      // save the PageAttrs
696
14.0k
      node->attrs = attrs;
697
698
      // read the kids
699
14.0k
      node->kids = new GList();
700
715k
      for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
701
701k
  if (kidsObj.arrayGetNF(i, &kidRefObj)->isRef()) {
702
165k
    if (kidRefObj.fetch(xref, &kidObj)->isDict()) {
703
47.8k
      if (kidObj.dictLookup("Count", &countObj)->isInt()) {
704
1.89k
        count = countObj.getInt();
705
45.9k
      } else {
706
45.9k
        count = 1;
707
45.9k
      }
708
47.8k
      countObj.free();
709
47.8k
      node->kids->append(new PageTreeNode(kidRefObj.getRef(), count,
710
47.8k
            node));
711
118k
    } else {
712
118k
      error(errSyntaxError, -1, "Page tree object is wrong type ({0:s})",
713
118k
      kidObj.getTypeName());
714
118k
    }
715
165k
    kidObj.free();
716
535k
  } else {
717
535k
    error(errSyntaxError, -1,
718
535k
    "Page tree reference is wrong type ({0:s})",
719
535k
    kidRefObj.getTypeName());
720
535k
  }
721
701k
  kidRefObj.free();
722
701k
      }
723
724
282k
    } else {
725
      
726
      // create the Page object
727
282k
      pageRefs[pg-1] = node->ref;
728
282k
      pages[pg-1] = new Page(doc, pg, pageObj.getDict(), attrs);
729
282k
      if (!pages[pg-1]->isOk()) {
730
3.02k
  delete pages[pg-1];
731
3.02k
  pages[pg-1] = new Page(doc, pg);
732
3.02k
      }
733
734
282k
    }
735
736
296k
    kidsObj.free();
737
296k
    pageObj.free();
738
296k
    pageRefObj.free();
739
296k
  }
740
741
  // recursively descend the tree
742
502k
  if (node->kids) {
743
1.30M
    for (i = 0; i < node->kids->getLength(); ++i) {
744
1.11M
      kidNode = (PageTreeNode *)node->kids->get(i);
745
1.11M
      if (relPg < kidNode->count) {
746
30.5k
  loadPage2(pg, relPg, kidNode);
747
30.5k
  break;
748
30.5k
      }
749
1.08M
      relPg -= kidNode->count;
750
1.08M
    }
751
752
    // this will only happen if the page tree is invalid
753
    // (i.e., parent count > sum of children counts)
754
219k
    if (i == node->kids->getLength()) {
755
188k
      error(errSyntaxError, -1, "Invalid page count in page tree");
756
188k
      pages[pg-1] = new Page(doc, pg);
757
188k
    }
758
219k
  }
759
502k
}
760
761
0
Object *Catalog::getDestOutputProfile(Object *destOutProf) {
762
0
  Object catDict, intents, intent, subtype;
763
0
  int i;
764
765
0
  if (!xref->getCatalog(&catDict)->isDict()) {
766
0
    goto err1;
767
0
  }
768
0
  if (!catDict.dictLookup("OutputIntents", &intents)->isArray()) {
769
0
    goto err2;
770
0
  }
771
0
  for (i = 0; i < intents.arrayGetLength(); ++i) {
772
0
    intents.arrayGet(i, &intent);
773
0
    if (!intent.isDict()) {
774
0
      intent.free();
775
0
      continue;
776
0
    }
777
0
    if (!intent.dictLookup("S", &subtype)->isName("GTS_PDFX")) {
778
0
      subtype.free();
779
0
      intent.free();
780
0
      continue;
781
0
    }
782
0
    subtype.free();
783
0
    if (!intent.dictLookup("DestOutputProfile", destOutProf)->isStream()) {
784
0
      destOutProf->free();
785
0
      intent.free();
786
0
      goto err2;
787
0
    }
788
0
    intent.free();
789
0
    intents.free();
790
0
    catDict.free();
791
0
    return destOutProf;
792
0
  }
793
794
0
 err2:
795
0
  intents.free();
796
0
 err1:
797
0
  catDict.free();
798
0
  return NULL;
799
0
}
800
801
14.7k
void Catalog::readEmbeddedFileList(Dict *catDict) {
802
14.7k
  Object obj1, obj2;
803
14.7k
  char *touchedObjs;
804
805
14.7k
  touchedObjs = (char *)gmalloc(xref->getNumObjects());
806
14.7k
  memset(touchedObjs, 0, xref->getNumObjects());
807
808
  // read the embedded file name tree
809
14.7k
  if (catDict->lookup("Names", &obj1)->isDict()) {
810
878
    obj1.dictLookupNF("EmbeddedFiles", &obj2);
811
878
    readEmbeddedFileTree(&obj2, touchedObjs);
812
878
    obj2.free();
813
878
  }
814
14.7k
  obj1.free();
815
816
  // look for file attachment annotations
817
14.7k
  readFileAttachmentAnnots(catDict->lookupNF("Pages", &obj1), touchedObjs);
818
14.7k
  obj1.free();
819
820
14.7k
  gfree(touchedObjs);
821
14.7k
}
822
823
62.1k
void Catalog::readEmbeddedFileTree(Object *nodeRef, char *touchedObjs) {
824
62.1k
  Object node, kidsObj, kidObj;
825
62.1k
  Object namesObj, nameObj, fileSpecObj;
826
62.1k
  int i;
827
828
  // check for an object loop
829
62.1k
  if (nodeRef->isRef()) {
830
11.3k
    if (nodeRef->getRefNum() < 0 ||
831
11.3k
  nodeRef->getRefNum() >= xref->getNumObjects() ||
832
10.2k
  touchedObjs[nodeRef->getRefNum()]) {
833
10.2k
      return;
834
10.2k
    }
835
1.10k
    touchedObjs[nodeRef->getRefNum()] = 1;
836
1.10k
    xref->fetch(nodeRef->getRefNum(), nodeRef->getRefGen(), &node);
837
50.8k
  } else {
838
50.8k
    nodeRef->copy(&node);
839
50.8k
  }
840
841
51.9k
  if (!node.isDict()) {
842
48.8k
    node.free();
843
48.8k
    return;
844
48.8k
  }
845
846
3.10k
  if (checkDictLookup(&node, "Kids", &kidsObj, touchedObjs)->isArray()) {
847
62.6k
    for (i = 0; i < kidsObj.arrayGetLength(); ++i) {
848
61.2k
      kidsObj.arrayGetNF(i, &kidObj);
849
61.2k
      readEmbeddedFileTree(&kidObj, touchedObjs);
850
61.2k
      kidObj.free();
851
61.2k
    }
852
1.75k
  } else {
853
1.75k
    if (checkDictLookup(&node, "Names", &namesObj, touchedObjs)->isArray()) {
854
9.45k
      for (i = 0; i+1 < namesObj.arrayGetLength(); ++i) {
855
9.30k
  namesObj.arrayGet(i, &nameObj);
856
9.30k
  namesObj.arrayGet(i+1, &fileSpecObj);
857
9.30k
  readEmbeddedFile(&fileSpecObj, &nameObj);
858
9.30k
  nameObj.free();
859
9.30k
  fileSpecObj.free();
860
9.30k
      }
861
157
    }
862
1.75k
    namesObj.free();
863
1.75k
  }
864
3.10k
  kidsObj.free();
865
866
3.10k
  node.free();
867
3.10k
}
868
869
void Catalog::readFileAttachmentAnnots(Object *pageNodeRef,
870
232k
               char *touchedObjs) {
871
232k
  Object pageNode, kids, kid, annots, annot, subtype, fileSpec, contents;
872
232k
  int i;
873
874
  // check for an invalid object reference (e.g., in a damaged PDF file)
875
232k
  if (pageNodeRef->isRef() &&
876
55.7k
      (pageNodeRef->getRefNum() < 0 ||
877
55.7k
       pageNodeRef->getRefNum() >= xref->getNumObjects())) {
878
3.21k
    return;
879
3.21k
  }
880
881
  // check for a page tree loop
882
229k
  if (pageNodeRef->isRef()) {
883
52.4k
    if (touchedObjs[pageNodeRef->getRefNum()]) {
884
16.4k
      return;
885
16.4k
    }
886
36.0k
    touchedObjs[pageNodeRef->getRefNum()] = 1;
887
36.0k
    xref->fetch(pageNodeRef->getRefNum(), pageNodeRef->getRefGen(), &pageNode);
888
177k
  } else {
889
177k
    pageNodeRef->copy(&pageNode);
890
177k
  }
891
892
213k
  if (pageNode.isDict()) {
893
41.5k
    if (checkDictLookup(&pageNode, "Kids", &kids, touchedObjs)->isArray()) {
894
232k
      for (i = 0; i < kids.arrayGetLength(); ++i) {
895
218k
  readFileAttachmentAnnots(kids.arrayGetNF(i, &kid), touchedObjs);
896
218k
  kid.free();
897
218k
      }
898
26.8k
    } else {
899
26.8k
      if (checkDictLookup(&pageNode, "Annots",
900
26.8k
        &annots, touchedObjs)->isArray()) {
901
62.9k
  for (i = 0; i < annots.arrayGetLength(); ++i) {
902
60.4k
    if (checkArrayGet(&annots, i, &annot, touchedObjs)->isDict()) {
903
10.2k
      if (checkDictLookup(&annot, "Subtype", &subtype, touchedObjs)
904
10.2k
      ->isName("FileAttachment")) {
905
0
        if (checkDictLookup(&annot, "FS", &fileSpec, touchedObjs)) {
906
0
    readEmbeddedFile(&fileSpec,
907
0
         checkDictLookup(&annot, "Contents",
908
0
             &contents, touchedObjs));
909
0
    contents.free();
910
0
        }
911
0
        fileSpec.free();
912
0
      }
913
10.2k
      subtype.free();
914
10.2k
    }
915
60.4k
    annot.free();
916
60.4k
  }
917
2.53k
      }
918
26.8k
      annots.free();
919
26.8k
    }
920
41.5k
    kids.free();
921
41.5k
  }
922
923
213k
  pageNode.free();
924
213k
}
925
926
9.30k
void Catalog::readEmbeddedFile(Object *fileSpec, Object *name1) {
927
9.30k
  Object name2, efObj, streamObj;
928
9.30k
  GString *s;
929
9.30k
  TextString *name;
930
931
9.30k
  if (fileSpec->isDict()) {
932
426
    if (fileSpec->dictLookup("UF", &name2)->isString()) {
933
10
      name = new TextString(name2.getString());
934
416
    } else {
935
416
      name2.free();
936
416
      if (fileSpec->dictLookup("F", &name2)->isString()) {
937
114
  name = new TextString(name2.getString());
938
302
      } else if (name1 && name1->isString()) {
939
10
  name = new TextString(name1->getString());
940
292
      } else {
941
292
  s = new GString("?");
942
292
  name = new TextString(s);
943
292
  delete s;
944
292
      }
945
416
    }
946
426
    name2.free();
947
426
    if (fileSpec->dictLookup("EF", &efObj)->isDict()) {
948
196
      if (efObj.dictLookupNF("F", &streamObj)->isRef()) {
949
118
  if (!embeddedFiles) {
950
8
    embeddedFiles = new GList();
951
8
  }
952
118
  embeddedFiles->append(new EmbeddedFile(name, &streamObj));
953
118
      } else {
954
78
  delete name;
955
78
      }
956
196
      streamObj.free();
957
230
    } else {
958
230
      delete name;
959
230
    }
960
426
    efObj.free();
961
426
  }
962
9.30k
}
963
964
0
int Catalog::getNumEmbeddedFiles() {
965
0
  return embeddedFiles ? embeddedFiles->getLength() : 0;
966
0
}
967
968
0
Unicode *Catalog::getEmbeddedFileName(int idx) {
969
0
  return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getUnicode();
970
0
}
971
972
0
int Catalog::getEmbeddedFileNameLength(int idx) {
973
0
  return ((EmbeddedFile *)embeddedFiles->get(idx))->name->getLength();
974
0
}
975
976
0
Object *Catalog::getEmbeddedFileStreamRef(int idx) {
977
0
  return &((EmbeddedFile *)embeddedFiles->get(idx))->streamRef;
978
0
}
979
980
0
Object *Catalog::getEmbeddedFileStreamObj(int idx, Object *strObj) {
981
0
  ((EmbeddedFile *)embeddedFiles->get(idx))->streamRef.fetch(xref, strObj);
982
0
  if (!strObj->isStream()) {
983
0
    strObj->free();
984
0
    return NULL;
985
0
  }
986
0
  return strObj;
987
0
}
988
989
456
void Catalog::readPageLabelTree(Object *root) {
990
456
  PageLabelNode *label0, *label1;
991
456
  char *touchedObjs;
992
456
  int i;
993
994
456
  touchedObjs = (char *)gmalloc(xref->getNumObjects());
995
456
  memset(touchedObjs, 0, xref->getNumObjects());
996
456
  pageLabels = new GList();
997
456
  readPageLabelTree2(root, touchedObjs);
998
456
  gfree(touchedObjs);
999
1000
456
  if (pageLabels->getLength() == 0) {
1001
300
    deleteGList(pageLabels, PageLabelNode);
1002
300
    pageLabels = NULL;
1003
300
    return;
1004
300
  }
1005
1006
  // set lastPage in each node
1007
156
  label0 = (PageLabelNode *)pageLabels->get(0);
1008
951
  for (i = 1; i < pageLabels->getLength(); ++i) {
1009
795
    label1 = (PageLabelNode *)pageLabels->get(i);
1010
795
    label0->lastPage = label1->firstPage - 1;
1011
795
    label0 = label1;
1012
795
  }
1013
156
  label0->lastPage = numPages;
1014
156
}
1015
1016
52.6k
void Catalog::readPageLabelTree2(Object *nodeRef, char *touchedObjs) {
1017
52.6k
  Object node, nums, num, labelObj, kidsRef, kids, kid;
1018
52.6k
  int i;
1019
1020
  // check for an object loop
1021
52.6k
  if (nodeRef->isRef()) {
1022
3.01k
    if (nodeRef->getRefNum() < 0 ||
1023
3.01k
  nodeRef->getRefNum() >= xref->getNumObjects() ||
1024
2.76k
  touchedObjs[nodeRef->getRefNum()]) {
1025
2.76k
      return;
1026
2.76k
    }
1027
251
    touchedObjs[nodeRef->getRefNum()] = 1;
1028
251
    xref->fetch(nodeRef->getRefNum(), nodeRef->getRefGen(), &node);
1029
49.6k
  } else {
1030
49.6k
    nodeRef->copy(&node);
1031
49.6k
  }
1032
1033
49.8k
  if (!node.isDict()) {
1034
46.4k
    node.free();
1035
46.4k
    return;
1036
46.4k
  }
1037
1038
3.38k
  if (node.dictLookup("Nums", &nums)->isArray()) {
1039
5.54k
    for (i = 0; i < nums.arrayGetLength() - 1; i += 2) {
1040
5.33k
      if (nums.arrayGet(i, &num)->isInt()) {
1041
2.30k
  if (nums.arrayGet(i+1, &labelObj)->isDict()) {
1042
951
    pageLabels->append(new PageLabelNode(num.getInt(),
1043
951
                 labelObj.getDict()));
1044
951
  }
1045
2.30k
  labelObj.free();
1046
2.30k
      }
1047
5.33k
      num.free();
1048
5.33k
    }
1049
213
  }
1050
3.38k
  nums.free();
1051
1052
  // check for an object loop in the Kids entry
1053
3.38k
  if (node.dictLookupNF("Kids", &kidsRef)->isRef()) {
1054
147
    if (kidsRef.getRefNum() < 0 ||
1055
147
  kidsRef.getRefNum() >= doc->getXRef()->getNumObjects() ||
1056
141
  touchedObjs[kidsRef.getRefNum()]) {
1057
141
      kidsRef.free();
1058
141
      node.free();
1059
141
      return;
1060
141
    }
1061
6
    touchedObjs[kidsRef.getRefNum()] = 1;
1062
6
    kidsRef.fetch(doc->getXRef(), &kids);
1063
3.23k
  } else {
1064
3.23k
    kidsRef.copy(&kids);
1065
3.23k
  }
1066
3.24k
  kidsRef.free();
1067
1068
3.24k
  if (kids.isArray()) {
1069
53.9k
    for (i = 0; i < kids.arrayGetLength(); ++i) {
1070
52.1k
      kids.arrayGetNF(i, &kid);
1071
52.1k
      readPageLabelTree2(&kid, touchedObjs);
1072
52.1k
      kid.free();
1073
52.1k
    }
1074
1.74k
  }
1075
3.24k
  kids.free();
1076
1077
3.24k
  node.free();
1078
3.24k
}
1079
1080
0
TextString *Catalog::getPageLabel(int pageNum) {
1081
0
  PageLabelNode *label;
1082
0
  TextString *ts;
1083
0
  int pageRangeNum;
1084
0
  GString *suffix;
1085
1086
0
  if (!pageLabels || !(label = findPageLabel(pageNum))) {
1087
0
    return NULL;
1088
0
  }
1089
1090
0
  ts = new TextString(label->prefix);
1091
1092
0
  pageRangeNum = label->start + (pageNum - label->firstPage);
1093
1094
0
  suffix = NULL;
1095
0
  if (label->style == 'D') {
1096
0
    suffix = GString::format("{0:d}", pageRangeNum);
1097
0
  } else if (label->style == 'R') {
1098
0
    suffix = makeRomanNumeral(pageRangeNum, gTrue);
1099
0
  } else if (label->style == 'r') {
1100
0
    suffix = makeRomanNumeral(pageRangeNum, gFalse);
1101
0
  } else if (label->style == 'A') {
1102
0
    suffix = makeLetterLabel(pageRangeNum, gTrue);
1103
0
  } else if (label->style == 'a') {
1104
0
    suffix = makeLetterLabel(pageRangeNum, gFalse);
1105
0
  }
1106
0
  if (suffix) {
1107
0
    ts->append(suffix);
1108
0
    delete suffix;
1109
0
  }
1110
1111
0
  return ts;
1112
0
}
1113
1114
0
PageLabelNode *Catalog::findPageLabel(int pageNum) {
1115
0
  PageLabelNode *label;
1116
0
  int i;
1117
1118
  //~ this could use a binary search
1119
0
  for (i = 0; i < pageLabels->getLength(); ++i) {
1120
0
    label = (PageLabelNode *)pageLabels->get(i);
1121
0
    if (pageNum >= label->firstPage && pageNum <= label->lastPage) {
1122
0
      return label;
1123
0
    }
1124
0
  }
1125
0
  return NULL;
1126
0
}
1127
1128
0
GString *Catalog::makeRomanNumeral(int num, GBool uppercase) {
1129
0
  GString *s;
1130
1131
0
  s = new GString();
1132
0
  while (num >= 1000) {
1133
0
    s->append(uppercase ? 'M' : 'm');
1134
0
    num -= 1000;
1135
0
  }
1136
0
  if (num >= 900) {
1137
0
    s->append(uppercase ? "CM" : "cm");
1138
0
    num -= 900;
1139
0
  } else if (num >= 500) {
1140
0
    s->append(uppercase ? 'D' : 'd');
1141
0
    num -= 500;
1142
0
  } else if (num >= 400) {
1143
0
    s->append(uppercase ? "CD" : "cd");
1144
0
    num -= 400;
1145
0
  }
1146
0
  while (num >= 100) {
1147
0
    s->append(uppercase ? 'C' : 'c');
1148
0
    num -= 100;
1149
0
  }
1150
0
  if (num >= 90) {
1151
0
    s->append(uppercase ? "XC" : "xc");
1152
0
    num -= 90;
1153
0
  } else if (num >= 50) {
1154
0
    s->append(uppercase ? 'L' : 'l');
1155
0
    num -= 50;
1156
0
  } else if (num >= 40) {
1157
0
    s->append(uppercase ? "XL" : "xl");
1158
0
    num -= 40;
1159
0
  }
1160
0
  while (num >= 10) {
1161
0
    s->append(uppercase ? 'X' : 'x');
1162
0
    num -= 10;
1163
0
  }
1164
0
  if (num >= 9) {
1165
0
    s->append(uppercase ? "IX" : "ix");
1166
0
    num -= 9;
1167
0
  } else if (num >= 5) {
1168
0
    s->append(uppercase ? 'V' : 'v');
1169
0
    num -= 5;
1170
0
  } else if (num >= 4) {
1171
0
    s->append(uppercase ? "IV" : "iv");
1172
0
    num -= 4;
1173
0
  }
1174
0
  while (num >= 1) {
1175
0
    s->append(uppercase ? 'I' : 'i');
1176
0
    num -= 1;
1177
0
  }
1178
0
  return s;
1179
0
}
1180
1181
0
GString *Catalog::makeLetterLabel(int num, GBool uppercase) {
1182
0
  GString *s;
1183
0
  int m, n, i;
1184
1185
0
  m = (num - 1) / 26 + 1;
1186
0
  n = (num - 1) % 26;
1187
0
  s = new GString();
1188
0
  for (i = 0; i < m; ++i) {
1189
0
    s->append((char)((uppercase ? 'A' : 'a') + n));
1190
0
  }
1191
0
  return s;
1192
0
}
1193
1194
0
int Catalog::getPageNumFromPageLabel(TextString *pageLabel) {
1195
0
  PageLabelNode *label;
1196
0
  int pageNum, prefixLength, i, n;
1197
1198
0
  if (!pageLabels) {
1199
0
    return -1;
1200
0
  }
1201
0
  for (i = 0; i < pageLabels->getLength(); ++i) {
1202
0
    label = (PageLabelNode *)pageLabels->get(i);
1203
0
    prefixLength = label->prefix->getLength();
1204
0
    if (pageLabel->getLength() < prefixLength ||
1205
0
  memcmp(pageLabel->getUnicode(), label->prefix->getUnicode(),
1206
0
         prefixLength * sizeof(Unicode))) {
1207
0
      continue;
1208
0
    }
1209
0
    if (label->style == '\0' && pageLabel->getLength() == prefixLength) {
1210
0
      return label->firstPage;
1211
0
    }
1212
0
    if (!convertPageLabelToInt(pageLabel, prefixLength, label->style, &n)) {
1213
0
      continue;
1214
0
    }
1215
0
    if (n < label->start) {
1216
0
      continue;
1217
0
    }
1218
0
    pageNum = label->firstPage + n - label->start;
1219
0
    if (pageNum <= label->lastPage) {
1220
0
      return pageNum;
1221
0
    }
1222
0
  }
1223
0
  return -1;
1224
0
}
1225
1226
// Attempts to convert pageLabel[prefixLength .. end] to an integer,
1227
// following the specified page label style.  If successful, sets *n
1228
// and returns true; else returns false.
1229
GBool Catalog::convertPageLabelToInt(TextString *pageLabel, int prefixLength,
1230
0
             char style, int *n) {
1231
0
  Unicode *u;
1232
0
  Unicode delta;
1233
0
  int len, i;
1234
1235
0
  len = pageLabel->getLength();
1236
0
  if (len <= prefixLength) {
1237
0
    return gFalse;
1238
0
  }
1239
0
  u = pageLabel->getUnicode();
1240
0
  if (style == 'D') {
1241
0
    *n = 0;
1242
0
    for (i = prefixLength; i < len; ++i) {
1243
0
      if (u[i] < (Unicode)'0' || u[i] > (Unicode)'9') {
1244
0
  return gFalse;
1245
0
      }
1246
0
      *n = *n * 10 + (u[i] - (Unicode)'0');
1247
0
    }
1248
0
    return gTrue;
1249
0
  } else if (style == 'R' || style == 'r') {
1250
0
    delta = style - 'R';
1251
0
    *n = 0;
1252
0
    i = prefixLength;
1253
0
    while (i < len && u[i] == (Unicode)'M' + delta) {
1254
0
      *n += 1000;
1255
0
      ++i;
1256
0
    }
1257
0
    if (i+1 < len && u[i] == (Unicode)'C' + delta &&
1258
0
  u[i+1] == (Unicode)'M' + delta) {
1259
0
      *n += 900;
1260
0
      i += 2;
1261
0
    } else if (i < len && u[i] == (Unicode)'D' + delta) {
1262
0
      *n += 500;
1263
0
      ++i;
1264
0
    } else if (i+1 < len && u[i] == (Unicode)'C' + delta &&
1265
0
         u[i+1] == (Unicode)'D' + delta) {
1266
0
      *n += 400;
1267
0
      i += 2;
1268
0
    }
1269
0
    while (i < len && u[i] == (Unicode)'C' + delta) {
1270
0
      *n += 100;
1271
0
      ++i;
1272
0
    }
1273
0
    if (i+1 < len && u[i] == (Unicode)'X' + delta &&
1274
0
  u[i+1] == (Unicode)'C' + delta) {
1275
0
      *n += 90;
1276
0
      i += 2;
1277
0
    } else if (i < len && u[i] == (Unicode)'L' + delta) {
1278
0
      *n += 50;
1279
0
      ++i;
1280
0
    } else if (i+1 < len && u[i] == (Unicode)'X' + delta &&
1281
0
         u[i+1] == (Unicode)'L' + delta) {
1282
0
      *n += 40;
1283
0
      i += 2;
1284
0
    }
1285
0
    while (i < len && u[i] == (Unicode)'X' + delta) {
1286
0
      *n += 10;
1287
0
      ++i;
1288
0
    }
1289
0
    if (i+1 < len && u[i] == (Unicode)'I' + delta &&
1290
0
  u[i+1] == (Unicode)'X' + delta) {
1291
0
      *n += 9;
1292
0
      i += 2;
1293
0
    } else if (i < len && u[i] == (Unicode)'V' + delta) {
1294
0
      *n += 5;
1295
0
      ++i;
1296
0
    } else if (i+1 < len && u[i] == (Unicode)'I' + delta &&
1297
0
         u[i+1] == (Unicode)'V' + delta) {
1298
0
      *n += 4;
1299
0
      i += 2;
1300
0
    }
1301
0
    while (i < len && u[i] == (Unicode)'I' + delta) {
1302
0
      *n += 1;
1303
0
      ++i;
1304
0
    }
1305
0
    return i == len;
1306
0
  } else if (style == 'A' || style == 'a') {
1307
0
    if (u[prefixLength] < (Unicode)style ||
1308
0
  u[prefixLength] > (Unicode)style + 25) {
1309
0
      return gFalse;
1310
0
    }
1311
0
    for (i = prefixLength + 1; i < len; ++i) {
1312
0
      if (u[i] != u[prefixLength]) {
1313
0
  return gFalse;
1314
0
      }
1315
0
    }
1316
0
    *n = (len - prefixLength - 1) * 26 + (u[prefixLength] - (Unicode)style) + 1;
1317
0
    return gTrue;
1318
0
  }
1319
0
  return gFalse;
1320
0
}
1321
1322
0
GBool Catalog::usesJavaScript() {
1323
0
  Object catDict;
1324
0
  if (!xref->getCatalog(&catDict)->isDict()) {
1325
0
    catDict.free();
1326
0
    return gFalse;
1327
0
  }
1328
1329
0
  GBool usesJS = gFalse;
1330
1331
  // check for Catalog.Names.JavaScript
1332
0
  Object namesObj;
1333
0
  if (catDict.dictLookup("Names", &namesObj)->isDict()) {
1334
0
    Object jsNamesObj;
1335
0
    namesObj.dictLookup("JavaScript", &jsNamesObj);
1336
0
    if (jsNamesObj.isDict()) {
1337
0
      usesJS = gTrue;
1338
0
    }
1339
0
    jsNamesObj.free();
1340
0
  }
1341
0
  namesObj.free();
1342
1343
  // look for JavaScript actionas in Page.AA
1344
0
  if (!usesJS) {
1345
0
    char *touchedObjs = (char *)gmalloc(xref->getNumObjects());
1346
0
    memset(touchedObjs, 0, xref->getNumObjects());
1347
0
    Object pagesObj;
1348
0
    usesJS = scanPageTreeForJavaScript(catDict.dictLookupNF("Pages", &pagesObj),
1349
0
               touchedObjs);
1350
0
    pagesObj.free();
1351
0
    gfree(touchedObjs);
1352
0
  }
1353
1354
0
  catDict.free();
1355
1356
0
  return usesJS;
1357
0
}
1358
1359
GBool Catalog::scanPageTreeForJavaScript(Object *pageNodeRef,
1360
0
           char *touchedObjs) {
1361
  // check for an invalid object reference (e.g., in a damaged PDF file)
1362
0
  if (pageNodeRef->isRef() &&
1363
0
      (pageNodeRef->getRefNum() < 0 ||
1364
0
       pageNodeRef->getRefNum() >= xref->getNumObjects())) {
1365
0
    return gFalse;
1366
0
  }
1367
1368
  // check for a page tree loop
1369
0
  Object pageNode;
1370
0
  if (pageNodeRef->isRef()) {
1371
0
    if (touchedObjs[pageNodeRef->getRefNum()]) {
1372
0
      return gFalse;
1373
0
    }
1374
0
    touchedObjs[pageNodeRef->getRefNum()] = 1;
1375
0
    xref->fetch(pageNodeRef->getRefNum(), pageNodeRef->getRefGen(), &pageNode);
1376
0
  } else {
1377
0
    pageNodeRef->copy(&pageNode);
1378
0
  }
1379
1380
  // scan the page tree node
1381
0
  GBool usesJS = gFalse;
1382
0
  if (pageNode.isDict()) {
1383
0
    Object kids;
1384
0
    if (checkDictLookup(&pageNode, "Kids", &kids, touchedObjs)->isArray()) {
1385
0
      for (int i = 0; i < kids.arrayGetLength() && !usesJS; ++i) {
1386
0
  Object kid;
1387
0
  if (scanPageTreeForJavaScript(kids.arrayGetNF(i, &kid), touchedObjs)) {
1388
0
    usesJS = gTrue;
1389
0
  }
1390
0
  kid.free();
1391
0
      }
1392
0
    } else {
1393
1394
      // scan Page.AA
1395
0
      Object pageAA;
1396
0
      if (checkDictLookup(&pageNode, "AA", &pageAA, touchedObjs)->isDict()) {
1397
0
  if (scanAAForJavaScript(&pageAA)) {
1398
0
    usesJS = gTrue;
1399
0
  }
1400
0
      }
1401
0
      pageAA.free();
1402
1403
      // scanPage.Annots
1404
0
      if (!usesJS) {
1405
0
  Object annots;
1406
0
  if (checkDictLookup(&pageNode, "Annots",
1407
0
          &annots, touchedObjs)->isArray()) {
1408
0
    for (int i = 0; i < annots.arrayGetLength() && !usesJS; ++i) {
1409
0
      Object annot;
1410
0
      if (checkArrayGet(&annots, i, &annot, touchedObjs)->isDict()) {
1411
0
        Object annotAA;
1412
0
        if (checkDictLookup(&annot, "AA", &annotAA,
1413
0
          touchedObjs)->isDict()) {
1414
0
    if (scanAAForJavaScript(&annotAA)) {
1415
0
      usesJS = gTrue;
1416
0
    }
1417
0
        }
1418
0
        annotAA.free();
1419
0
      }
1420
0
      annot.free();
1421
0
    }
1422
0
  }
1423
0
        annots.free();
1424
0
      }
1425
0
    }
1426
0
    kids.free();
1427
0
  }
1428
1429
0
  pageNode.free();
1430
1431
0
  return usesJS;
1432
0
}
1433
1434
0
GBool Catalog::scanAAForJavaScript(Object *aaObj) {
1435
0
  GBool usesJS = gFalse;
1436
0
  for (int i = 0; i < aaObj->dictGetLength() && !usesJS; ++i) {
1437
0
    Object action;
1438
0
    if (aaObj->dictGetVal(i, &action)->isDict()) {
1439
0
      Object js;
1440
0
      if (!action.dictLookupNF("JS", &js)->isNull()) {
1441
0
  usesJS = gTrue;
1442
0
      }
1443
0
      js.free();
1444
0
    }
1445
0
    action.free();
1446
0
  }
1447
0
  return usesJS;
1448
0
}
1449
1450
Object *Catalog::checkDictLookup(Object *dictObj, const char *key,
1451
83.4k
         Object *element, char *touchedObjs) {
1452
83.4k
  Object refObj;
1453
83.4k
  dictObj->dictLookupNF(key, &refObj);
1454
83.4k
  if (refObj.isRef()) {
1455
3.67k
    int num = refObj.getRefNum();
1456
3.67k
    if (num >= 0 && num < xref->getNumObjects() && !touchedObjs[num]) {
1457
1.17k
      touchedObjs[num] = 1;
1458
1.17k
      xref->fetch(num, refObj.getRefGen(), element);
1459
2.50k
    } else {
1460
2.50k
      element->initNull();
1461
2.50k
    }
1462
3.67k
    refObj.free();
1463
79.7k
  } else {
1464
79.7k
    *element = refObj;
1465
79.7k
  }
1466
83.4k
  return element;
1467
83.4k
}
1468
1469
Object *Catalog::checkArrayGet(Object *arrayObj, int i,
1470
60.4k
             Object *element, char *touchedObjs) {
1471
60.4k
  Object refObj;
1472
60.4k
  arrayObj->arrayGetNF(i, &refObj);
1473
60.4k
  if (refObj.isRef()) {
1474
22.2k
    int num = refObj.getRefNum();
1475
22.2k
    if (num >= 0 && num < xref->getNumObjects() && !touchedObjs[num]) {
1476
17.9k
      touchedObjs[num] = 1;
1477
17.9k
      xref->fetch(num, refObj.getRefGen(), element);
1478
17.9k
    } else {
1479
4.28k
      element->initNull();
1480
4.28k
    }
1481
22.2k
    refObj.free();
1482
38.1k
  } else {
1483
38.1k
    *element = refObj;
1484
38.1k
  }
1485
60.4k
  return element;
1486
60.4k
}