home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 110 / EnigmaAmiga110CD.iso / indispensabili / utility / apdf / xpdf-0.80 / xpdf / xref.cc < prev    next >
C/C++ Source or Header  |  1999-04-27  |  10KB  |  444 lines

  1. //========================================================================
  2. //
  3. // XRef.cc
  4. //
  5. // Copyright 1996 Derek B. Noonburg
  6. //
  7. //========================================================================
  8.  
  9. #ifdef __GNUC__
  10. #pragma implementation
  11. #endif
  12.  
  13. #include <stdlib.h>
  14. #include <stddef.h>
  15. #include <string.h>
  16. #include <ctype.h>
  17. #include "gmem.h"
  18. #include "Object.h"
  19. #include "Stream.h"
  20. #include "Lexer.h"
  21. #include "Parser.h"
  22. #include "Dict.h"
  23. #include "Error.h"
  24. #include "XRef.h"
  25.  
  26. //------------------------------------------------------------------------
  27.  
  28. #define xrefSearchSize 1024    // read this many bytes at end of file
  29.                 //   to look for 'startxref'
  30.  
  31. //------------------------------------------------------------------------
  32. // The global xref table
  33. //------------------------------------------------------------------------
  34.  
  35. XRef *xref = NULL;
  36.  
  37. //------------------------------------------------------------------------
  38. // XRef
  39. //------------------------------------------------------------------------
  40.  
  41. XRef::XRef(FileStream *str) {
  42.   XRef *oldXref;
  43.   int pos;
  44.   int i;
  45.  
  46.   ok = gTrue;
  47.   size = 0;
  48.   entries = NULL;
  49.  
  50.   // get rid of old xref (otherwise it will try to fetch the Root object
  51.   // in the new document, using the old xref)
  52.   oldXref = xref;
  53.   xref = NULL;
  54.  
  55.   // read the trailer
  56.   file = str->getFile();
  57.   start = str->getStart();
  58.   pos = readTrailer(str);
  59.  
  60.   // if there was a problem with the trailer,
  61.   // try to reconstruct the xref table
  62.   if (pos == 0) {
  63.     if (!(ok = constructXRef(str))) {
  64.       xref = oldXref;
  65.       return;
  66.     }
  67.  
  68.   // trailer is ok - read the xref table
  69.   } else {
  70.     entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
  71.     for (i = 0; i < size; ++i) {
  72.       entries[i].offset = -1;
  73.       entries[i].used = gFalse;
  74.     }
  75.     while (readXRef(str, &pos)) ;
  76.  
  77.     // if there was a problem with the xref table,
  78.     // try to reconstruct it
  79.     if (!ok) {
  80.       gfree(entries);
  81.       size = 0;
  82.       entries = NULL;
  83.       if (!(ok = constructXRef(str))) {
  84.     xref = oldXref;
  85.     return;
  86.       }
  87.     }
  88.   }
  89.  
  90.   // set up new xref table
  91.   xref = this;
  92.  
  93.   // check for encryption
  94.   if (checkEncrypted()) {
  95.     ok = gFalse;
  96.     xref = oldXref;
  97.     return;
  98.   }
  99. }
  100.  
  101. XRef::~XRef() {
  102.   gfree(entries);
  103.   trailerDict.free();
  104. }
  105.  
  106. // Read startxref position, xref table size, and root.  Returns
  107. // first xref position.
  108. int XRef::readTrailer(FileStream *str) {
  109.   Parser *parser;
  110.   Object obj;
  111.   char buf[xrefSearchSize+1];
  112.   int n, pos, pos1;
  113.   char *p;
  114.   int c;
  115.   int i;
  116.  
  117.   // read last xrefSearchSize bytes
  118.   str->setPos(-xrefSearchSize);
  119.   for (n = 0; n < xrefSearchSize; ++n) {
  120.     if ((c = str->getChar()) == EOF)
  121.       break;
  122.     buf[n] = c;
  123.   }
  124.   buf[n] = '\0';
  125.  
  126.   // find startxref
  127.   for (i = n - 9; i >= 0; --i) {
  128.     if (!strncmp(&buf[i], "startxref", 9))
  129.       break;
  130.   }
  131.   if (i < 0)
  132.     return 0;
  133.   for (p = &buf[i+9]; isspace(*p); ++p) ;
  134.   pos = atoi(p);
  135.  
  136.   // find trailer dict by looking after first xref table
  137.   // (NB: we can't just use the trailer dict at the end of the file --
  138.   // this won't work for linearized files.)
  139.   str->setPos(start + pos);
  140.   for (i = 0; i < 4; ++i)
  141.     buf[i] = str->getChar();
  142.   if (strncmp(buf, "xref", 4))
  143.     return 0;
  144.   pos1 = pos + 4;
  145.   while (1) {
  146.     str->setPos(start + pos1);
  147.     for (i = 0; i < 35; ++i) {
  148.       if ((c = str->getChar()) == EOF)
  149.     return 0;
  150.       buf[i] = c;
  151.     }
  152.     if (!strncmp(buf, "trailer", 7))
  153.       break;
  154.     p = buf;
  155.     while (isspace(*p)) ++p;
  156.     while ('0' <= *p && *p <= '9') ++p;
  157.     while (isspace(*p)) ++p;
  158.     n = atoi(p);
  159.     while ('0' <= *p && *p <= '9') ++p;
  160.     while (isspace(*p)) ++p;
  161.     if (p == buf)
  162.       return 0;
  163.     pos1 += (p - buf) + n * 20;
  164.   }
  165.   pos1 += 7;
  166.  
  167.   // read trailer dict
  168.   obj.initNull();
  169.   parser = new Parser(new Lexer(new FileStream(file, start + pos1, -1, &obj)));
  170.   parser->getObj(&trailerDict);
  171.   if (trailerDict.isDict()) {
  172.     trailerDict.dictLookupNF("Size", &obj);
  173.     if (obj.isInt())
  174.       size = obj.getInt();
  175.     else
  176.       pos = 0;
  177.     obj.free();
  178.     trailerDict.dictLookupNF("Root", &obj);
  179.     if (obj.isRef()) {
  180.       rootNum = obj.getRefNum();
  181.       rootGen = obj.getRefGen();
  182.     } else {
  183.       pos = 0;
  184.     }
  185.     obj.free();
  186.   } else {
  187.     pos = 0;
  188.   }
  189.   delete parser;
  190.  
  191.   // return first xref position
  192.   return pos;
  193. }
  194.  
  195. // Read an xref table and the prev pointer from the trailer.
  196. GBool XRef::readXRef(FileStream *str, int *pos) {
  197.   Parser *parser;
  198.   Object obj, obj2;
  199.   char s[20];
  200.   GBool more;
  201.   int first, n, i, j;
  202.   int c;
  203.  
  204.   // seek to xref in stream
  205.   str->setPos(start + *pos);
  206.  
  207.   // make sure it's an xref table
  208.   while ((c = str->getChar()) != EOF && isspace(c)) ;
  209.   s[0] = (char)c;
  210.   s[1] = (char)str->getChar();
  211.   s[2] = (char)str->getChar();
  212.   s[3] = (char)str->getChar();
  213.   if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f'))
  214.     goto err2;
  215.  
  216.   // read xref
  217.   while (1) {
  218.     while ((c = str->lookChar()) != EOF && isspace(c))
  219.       str->getChar();
  220.     if (c == 't')
  221.       break;
  222.     for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i)
  223.       s[i] = (char)c;
  224.     if (i == 0)
  225.       goto err2;
  226.     s[i] = '\0';
  227.     first = atoi(s);
  228.     while ((c = str->lookChar()) != EOF && isspace(c))
  229.       str->getChar();
  230.     for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i)
  231.       s[i] = (char)c;
  232.     if (i == 0)
  233.       goto err2;
  234.     s[i] = '\0';
  235.     n = atoi(s);
  236.     while ((c = str->lookChar()) != EOF && isspace(c))
  237.       str->getChar();
  238.     for (i = first; i < first + n; ++i) {
  239.       for (j = 0; j < 20; ++j) {
  240.     if ((c = str->getChar()) == EOF)
  241.       goto err2;
  242.     s[j] = (char)c;
  243.       }
  244.       if (entries[i].offset < 0) {
  245.     s[10] = '\0';
  246.     entries[i].offset = atoi(s);
  247.     s[16] = '\0';
  248.     entries[i].gen = atoi(&s[11]);
  249.     if (s[17] == 'n')
  250.       entries[i].used = gTrue;
  251.     else if (s[17] == 'f')
  252.       entries[i].used = gFalse;
  253.     else
  254.       goto err2;
  255.       }
  256.     }
  257.   }
  258.  
  259.   // read prev pointer from trailer dictionary
  260.   obj.initNull();
  261.   parser = new Parser(new Lexer(
  262.     new FileStream(file, str->getPos(), -1, &obj)));
  263.   parser->getObj(&obj);
  264.   if (!obj.isCmd("trailer"))
  265.     goto err1;
  266.   obj.free();
  267.   parser->getObj(&obj);
  268.   if (!obj.isDict())
  269.     goto err1;
  270.   obj.getDict()->lookupNF("Prev", &obj2);
  271.   if (obj2.isInt()) {
  272.     *pos = obj2.getInt();
  273.     more = gTrue;
  274.   } else {
  275.     more = gFalse;
  276.   }
  277.   obj.free();
  278.   obj2.free();
  279.  
  280.   delete parser;
  281.   return more;
  282.  
  283.  err1:
  284.   obj.free();
  285.  err2:
  286.   ok = gFalse;
  287.   return gFalse;
  288. }
  289.  
  290. // Attempt to construct an xref table for a damaged file.
  291. GBool XRef::constructXRef(FileStream *str) {
  292.   Parser *parser;
  293.   Object obj;
  294.   char buf[256];
  295.   int pos;
  296.   int num, gen;
  297.   int newSize;
  298.   char *p;
  299.   int i;
  300.   GBool gotRoot;
  301.  
  302.   error(0, "PDF file is damaged - attempting to reconstruct xref table...");
  303.   gotRoot = gFalse;
  304.  
  305.   str->reset();
  306.   while (1) {
  307.     pos = str->getPos();
  308.     if (!str->getLine(buf, 256))
  309.       break;
  310.     p = buf;
  311.  
  312.     // got trailer dictionary
  313.     if (!strncmp(p, "trailer", 7)) {
  314.       obj.initNull();
  315.       parser = new Parser(new Lexer(
  316.          new FileStream(file, start + pos + 8, -1, &obj)));
  317.       if (!trailerDict.isNone())
  318.     trailerDict.free();
  319.       parser->getObj(&trailerDict);
  320.       if (trailerDict.isDict()) {
  321.     trailerDict.dictLookupNF("Root", &obj);
  322.     if (obj.isRef()) {
  323.       rootNum = obj.getRefNum();
  324.       rootGen = obj.getRefGen();
  325.       gotRoot = gTrue;
  326.     }
  327.     obj.free();
  328.       } else {
  329.     pos = 0;
  330.       }
  331.       delete parser;
  332.  
  333.     // look for object
  334.     } else if (isdigit(*p)) {
  335.       num = atoi(p);
  336.       do {
  337.     ++p;
  338.       } while (*p && isdigit(*p));
  339.       if (isspace(*p)) {
  340.     do {
  341.       ++p;
  342.     } while (*p && isspace(*p));
  343.     if (isdigit(*p)) {
  344.       gen = atoi(p);
  345.       do {
  346.         ++p;
  347.       } while (*p && isdigit(*p));
  348.       if (isspace(*p)) {
  349.         do {
  350.           ++p;
  351.         } while (*p && isspace(*p));
  352.         if (!strncmp(p, "obj", 3)) {
  353.           if (num >= size) {
  354.         newSize = (num + 1 + 255) & ~255;
  355.         entries = (XRefEntry *)
  356.                     grealloc(entries, newSize * sizeof(XRefEntry));
  357.         for (i = size; i < newSize; ++i) {
  358.           entries[i].offset = -1;
  359.           entries[i].used = gFalse;
  360.         }
  361.         size = newSize;
  362.           }
  363.           if (!entries[num].used || gen >= entries[num].gen) {
  364.         entries[num].offset = pos - start;
  365.         entries[num].gen = gen;
  366.         entries[num].used = gTrue;
  367.           }
  368.         }
  369.       }
  370.     }
  371.       }
  372.     }
  373.   }
  374.  
  375.   if (gotRoot)
  376.     return gTrue;
  377.  
  378.   error(-1, "Couldn't find trailer dictionary");
  379.   return gFalse;
  380. }
  381.  
  382. GBool XRef::checkEncrypted() {
  383.   Object obj;
  384.   GBool encrypted;
  385.  
  386.   trailerDict.dictLookup("Encrypt", &obj);
  387.   if ((encrypted = !obj.isNull())) {
  388.     error(-1, "PDF file is encrypted and cannot be displayed");
  389.     error(-1, "* Decryption support is currently not included in xpdf");
  390.     error(-1, "* due to legal restrictions: the U.S.A. still has bogus");
  391.     error(-1, "* export controls on cryptography software.");
  392.   }
  393.   obj.free();
  394.   return encrypted;
  395. }
  396.  
  397. GBool XRef::okToPrint() {
  398.   return gTrue;
  399. }
  400.  
  401. GBool XRef::okToCopy() {
  402.   return gTrue;
  403. }
  404.  
  405. Object *XRef::fetch(int num, int gen, Object *obj) {
  406.   XRefEntry *e;
  407.   Parser *parser;
  408.   Object obj1, obj2, obj3;
  409.  
  410.   // check for bogus ref - this can happen in corrupted PDF files
  411.   if (num < 0 || num >= size) {
  412.     obj->initNull();
  413.     return obj;
  414.   }
  415.  
  416.   e = &entries[num];
  417.   if (e->gen == gen && e->offset >= 0) {
  418.     obj1.initNull();
  419.     parser = new Parser(new Lexer(
  420.       new FileStream(file, start + e->offset, -1, &obj1)));
  421.     parser->getObj(&obj1);
  422.     parser->getObj(&obj2);
  423.     parser->getObj(&obj3);
  424.     if (obj1.isInt() && obj1.getInt() == num &&
  425.     obj2.isInt() && obj2.getInt() == gen &&
  426.     obj3.isCmd("obj")) {
  427.       parser->getObj(obj);
  428.     } else {
  429.       obj->initNull();
  430.     }
  431.     obj1.free();
  432.     obj2.free();
  433.     obj3.free();
  434.     delete parser;
  435.   } else {
  436.     obj->initNull();
  437.   }
  438.   return obj;
  439. }
  440.  
  441. Object *XRef::getDocInfo(Object *obj) {
  442.   return trailerDict.dictLookup("Info", obj);
  443. }
  444.