upgraded to xpdf-3.01pl1
authorkramm <kramm>
Sat, 3 Dec 2005 14:28:56 +0000 (14:28 +0000)
committerkramm <kramm>
Sat, 3 Dec 2005 14:28:56 +0000 (14:28 +0000)
56 files changed:
pdf2swf/xpdf/Annot.cc
pdf2swf/xpdf/Annot.h
pdf2swf/xpdf/Array.cc
pdf2swf/xpdf/BuiltinFont.cc
pdf2swf/xpdf/CMap.cc
pdf2swf/xpdf/CMap.h
pdf2swf/xpdf/Catalog.cc
pdf2swf/xpdf/Catalog.h
pdf2swf/xpdf/CharCodeToUnicode.cc
pdf2swf/xpdf/CharCodeToUnicode.h
pdf2swf/xpdf/Decrypt.cc
pdf2swf/xpdf/Decrypt.h
pdf2swf/xpdf/Dict.cc
pdf2swf/xpdf/FoFiTrueType.cc
pdf2swf/xpdf/FoFiTrueType.h
pdf2swf/xpdf/FoFiType1.cc
pdf2swf/xpdf/FoFiType1C.h
pdf2swf/xpdf/Function.cc
pdf2swf/xpdf/Function.h
pdf2swf/xpdf/GHash.cc
pdf2swf/xpdf/GHash.h
pdf2swf/xpdf/GList.cc
pdf2swf/xpdf/GList.h
pdf2swf/xpdf/GString.cc
pdf2swf/xpdf/GString.h
pdf2swf/xpdf/Gfx.h
pdf2swf/xpdf/GfxFont.cc
pdf2swf/xpdf/GfxState.h
pdf2swf/xpdf/JArithmeticDecoder.cc
pdf2swf/xpdf/JArithmeticDecoder.h
pdf2swf/xpdf/JBIG2Stream.cc
pdf2swf/xpdf/JBIG2Stream.h
pdf2swf/xpdf/JPXStream.cc
pdf2swf/xpdf/JPXStream.h
pdf2swf/xpdf/Lexer.cc
pdf2swf/xpdf/Lexer.h
pdf2swf/xpdf/Link.cc
pdf2swf/xpdf/NameToCharCode.cc
pdf2swf/xpdf/NameToUnicodeTable.h
pdf2swf/xpdf/Object.cc
pdf2swf/xpdf/Outline.cc
pdf2swf/xpdf/OutputDev.cc
pdf2swf/xpdf/PDFDoc.cc
pdf2swf/xpdf/PDFDoc.h
pdf2swf/xpdf/Page.h
pdf2swf/xpdf/Parser.cc
pdf2swf/xpdf/Parser.h
pdf2swf/xpdf/SecurityHandler.cc [new file with mode: 0644]
pdf2swf/xpdf/SecurityHandler.h [new file with mode: 0644]
pdf2swf/xpdf/Stream.h
pdf2swf/xpdf/UnicodeMap.cc
pdf2swf/xpdf/UnicodeMap.h
pdf2swf/xpdf/XRef.cc
pdf2swf/xpdf/XRef.h
pdf2swf/xpdf/gmem.c
pdf2swf/xpdf/gmem.h

index 245780d..68bfb6d 100644 (file)
 #pragma implementation
 #endif
 
+#include <stdlib.h>
 #include "gmem.h"
 #include "Object.h"
+#include "Catalog.h"
 #include "Gfx.h"
+#include "Lexer.h"
 #include "Annot.h"
 
 //------------------------------------------------------------------------
 // Annot
 //------------------------------------------------------------------------
 
-Annot::Annot(XRef *xrefA, Dict *dict) {
+Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict) {
   Object apObj, asObj, obj1, obj2;
+  GBool regen, isTextField;
   double t;
 
   ok = gFalse;
   xref = xrefA;
-
-  if (dict->lookup("AP", &apObj)->isDict()) {
-    if (dict->lookup("AS", &asObj)->isName()) {
-      if (apObj.dictLookup("N", &obj1)->isDict()) {
-       if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {
-         obj2.copy(&appearance);
-         ok = gTrue;
-       }
-       obj2.free();
-      }
-      obj1.free();
-    } else {
-      if (apObj.dictLookupNF("N", &obj1)->isRef()) {
-       obj1.copy(&appearance);
-       ok = gTrue;
-      }
-      obj1.free();
-    }
-    asObj.free();
-  }
-  apObj.free();
+  appearBuf = NULL;
 
   if (dict->lookup("Rect", &obj1)->isArray() &&
       obj1.arrayGetLength() == 4) {
@@ -76,10 +60,204 @@ Annot::Annot(XRef *xrefA, Dict *dict) {
     xMax = yMax = 1;
   }
   obj1.free();
+
+  // check if field apperances need to be regenerated
+  regen = gFalse;
+  if (acroForm) {
+    acroForm->lookup("NeedAppearances", &obj1);
+    if (obj1.isBool() && obj1.getBool()) {
+      regen = gTrue;
+    }
+    obj1.free();
+  }
+
+  // check for a text-type field
+  isTextField = dict->lookup("FT", &obj1)->isName("Tx");
+  obj1.free();
+
+#if 0 //~ appearance stream generation is not finished yet
+  if (regen && isTextField) {
+    generateAppearance(acroForm, dict);
+  } else {
+#endif
+    if (dict->lookup("AP", &apObj)->isDict()) {
+      if (dict->lookup("AS", &asObj)->isName()) {
+       if (apObj.dictLookup("N", &obj1)->isDict()) {
+         if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {
+           obj2.copy(&appearance);
+           ok = gTrue;
+         } else {
+           obj2.free();
+           if (obj1.dictLookupNF("Off", &obj2)->isRef()) {
+             obj2.copy(&appearance);
+             ok = gTrue;
+           }
+         }
+         obj2.free();
+       }
+       obj1.free();
+      } else {
+       if (apObj.dictLookupNF("N", &obj1)->isRef()) {
+         obj1.copy(&appearance);
+         ok = gTrue;
+       }
+       obj1.free();
+      }
+      asObj.free();
+    }
+    apObj.free();
+#if 0 //~ appearance stream generation is not finished yet
+  }
+#endif
 }
 
 Annot::~Annot() {
   appearance.free();
+  if (appearBuf) {
+    delete appearBuf;
+  }
+}
+
+void Annot::generateAppearance(Dict *acroForm, Dict *dict) {
+  MemStream *appearStream;
+  Object daObj, vObj, drObj, appearDict, obj1, obj2;
+  GString *daStr, *daStr1, *vStr, *s;
+  char buf[256];
+  double fontSize;
+  int c;
+  int i0, i1;
+
+  //~ DA can be inherited
+  if (dict->lookup("DA", &daObj)->isString()) {
+    daStr = daObj.getString();
+
+    // look for a font size
+    //~ may want to parse the DS entry in place of this (if it exists)
+    daStr1 = NULL;
+    fontSize = 10;
+    for (i1 = daStr->getLength() - 2; i1 >= 0; --i1) {
+      if (daStr->getChar(i1) == 'T' && daStr->getChar(i1+1) == 'f') {
+       for (--i1; i1 >= 0 && Lexer::isSpace(daStr->getChar(i1)); --i1) ;
+       for (i0 = i1; i0 >= 0 && !Lexer::isSpace(daStr->getChar(i0)); --i0) ;
+       if (i0 >= 0) {
+         ++i0;
+         ++i1;
+         s = new GString(daStr, i0, i1 - i0);
+         fontSize = atof(s->getCString());
+         delete s;
+
+         // autosize the font
+         if (fontSize == 0) {
+           fontSize = 0.67 * (yMax - yMin);
+           daStr1 = new GString(daStr, 0, i0);
+           sprintf(buf, "%.2f", fontSize);
+           daStr1->append(buf);
+           daStr1->append(daStr->getCString() + i1,
+                          daStr->getLength() - i1);
+         }
+       }
+       break;
+      }
+    }
+
+    // build the appearance stream contents
+    appearBuf = new GString();
+    appearBuf->append("/Tx BMC\n");
+    appearBuf->append("q BT\n");
+    appearBuf->append(daStr1 ? daStr1 : daStr)->append("\n");
+    if (dict->lookup("V", &vObj)->isString()) {
+      //~ handle quadding -- this requires finding the font and using
+      //~   the encoding and char widths
+      sprintf(buf, "1 0 0 1 %.2f %.2f Tm\n", 2.0, yMax - yMin - fontSize);
+      appearBuf->append(buf);
+      sprintf(buf, "%g TL\n", fontSize);
+      appearBuf->append(buf);
+      vStr = vObj.getString();
+      i0 = 0;
+      while (i0 < vStr->getLength()) {
+       for (i1 = i0;
+            i1 < vStr->getLength() &&
+              vStr->getChar(i1) != '\n' && vStr->getChar(i1) != '\r';
+            ++i1) ;
+       if (i0 > 0) {
+         appearBuf->append("T*\n");
+       }
+       appearBuf->append('(');
+       for (; i0 < i1; ++i0) {
+         c = vStr->getChar(i0);
+         if (c == '(' || c == ')' || c == '\\') {
+           appearBuf->append('\\');
+           appearBuf->append(c);
+         } else if (c < 0x20 || c >= 0x80) {
+           sprintf(buf, "\\%03o", c);
+           appearBuf->append(buf);
+         } else {
+           appearBuf->append(c);
+         }
+       }
+       appearBuf->append(") Tj\n");
+       if (i1 + 1 < vStr->getLength() &&
+           vStr->getChar(i1) == '\r' && vStr->getChar(i1 + 1) == '\n') {
+         i0 = i1 + 2;
+       } else {
+         i0 = i1 + 1;
+       }
+      }
+    }
+    vObj.free();
+    appearBuf->append("ET Q\n");
+    appearBuf->append("EMC\n");
+
+    // build the appearance stream dictionary
+    appearDict.initDict(xref);
+    appearDict.dictAdd(copyString("Length"),
+                      obj1.initInt(appearBuf->getLength()));
+    appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));
+    obj1.initArray(xref);
+    obj1.arrayAdd(obj2.initReal(0));
+    obj1.arrayAdd(obj2.initReal(0));
+    obj1.arrayAdd(obj2.initReal(xMax - xMin));
+    obj1.arrayAdd(obj2.initReal(yMax - yMin));
+    appearDict.dictAdd(copyString("BBox"), &obj1);
+
+    // find the resource dictionary
+    dict->lookup("DR", &drObj);
+    if (!drObj.isDict()) {
+      dict->lookup("Parent", &obj1);
+      while (obj1.isDict()) {
+       drObj.free();
+       obj1.dictLookup("DR", &drObj);
+       if (drObj.isDict()) {
+         break;
+       }
+       obj1.dictLookup("Parent", &obj2);
+       obj1.free();
+       obj1 = obj2;
+      }
+      obj1.free();
+      if (!drObj.isDict()) {
+       if (acroForm) {
+         drObj.free();
+         acroForm->lookup("DR", &drObj);
+       }
+      }
+    }
+    if (drObj.isDict()) {
+      appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));
+    }
+    drObj.free();
+
+    // build the appearance stream
+    appearStream = new MemStream(appearBuf->getCString(), 0,
+                                appearBuf->getLength(), &appearDict);
+    appearance.initStream(appearStream);
+    ok = gTrue;
+
+    if (daStr1) {
+      delete daStr1;
+    }
+  }
+  daObj.free();
 }
 
 void Annot::draw(Gfx *gfx) {
@@ -95,7 +273,8 @@ void Annot::draw(Gfx *gfx) {
 // Annots
 //------------------------------------------------------------------------
 
-Annots::Annots(XRef *xref, Object *annotsObj) {
+Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {
+  Dict *acroForm;
   Annot *annot;
   Object obj1;
   int size;
@@ -105,14 +284,16 @@ Annots::Annots(XRef *xref, Object *annotsObj) {
   size = 0;
   nAnnots = 0;
 
+  acroForm = catalog->getAcroForm()->isDict() ?
+               catalog->getAcroForm()->getDict() : NULL;
   if (annotsObj->isArray()) {
     for (i = 0; i < annotsObj->arrayGetLength(); ++i) {
       if (annotsObj->arrayGet(i, &obj1)->isDict()) {
-       annot = new Annot(xref, obj1.getDict());
+       annot = new Annot(xref, acroForm, obj1.getDict());
        if (annot->isOk()) {
          if (nAnnots >= size) {
            size += 16;
-           annots = (Annot **)grealloc(annots, size * sizeof(Annot *));
+           annots = (Annot **)greallocn(annots, size, sizeof(Annot *));
          }
          annots[nAnnots++] = annot;
        } else {
index 89dde0f..e5fd841 100644 (file)
@@ -16,6 +16,7 @@
 #endif
 
 class XRef;
+class Catalog;
 class Gfx;
 
 //------------------------------------------------------------------------
@@ -25,7 +26,7 @@ class Gfx;
 class Annot {
 public:
 
-  Annot(XRef *xrefA, Dict *dict);
+  Annot(XRef *xrefA, Dict *acroForm, Dict *dict);
   ~Annot();
   GBool isOk() { return ok; }
 
@@ -35,10 +36,13 @@ public:
   Object *getAppearance(Object *obj) { return appearance.fetch(xref, obj); }
 
 private:
+  void generateAppearance(Dict *acroForm, Dict *dict);
 
   XRef *xref;                  // the xref table for this PDF file
   Object appearance;           // a reference to the Form XObject stream
                                //   for the normal appearance
+  GString *appearBuf;
   double xMin, yMin,           // annotation rectangle
          xMax, yMax;
   GBool ok;
@@ -52,7 +56,7 @@ class Annots {
 public:
 
   // Extract non-link annotations from array of annotations.
-  Annots(XRef *xref, Object *annotsObj);
+  Annots(XRef *xref, Catalog *catalog, Object *annotsObj);
 
   ~Annots();
 
index a6c6db1..10ded14 100644 (file)
@@ -44,7 +44,7 @@ void Array::add(Object *elem) {
     } else {
       size *= 2;
     }
-    elems = (Object *)grealloc(elems, size * sizeof(Object));
+    elems = (Object *)greallocn(elems, size, sizeof(Object));
   }
   elems[length] = *elem;
   ++length;
index a687e73..ce98957 100644 (file)
@@ -24,7 +24,7 @@ BuiltinFontWidths::BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA) {
   int i, h;
 
   size = sizeA;
-  tab = (BuiltinFontWidth **)gmalloc(size * sizeof(BuiltinFontWidth *));
+  tab = (BuiltinFontWidth **)gmallocn(size, sizeof(BuiltinFontWidth *));
   for (i = 0; i < size; ++i) {
     tab[i] = NULL;
   }
index 25f3af7..303cf09 100644 (file)
@@ -49,7 +49,7 @@ CMap *CMap::parse(CMapCache *cache, GString *collectionA,
   PSTokenizer *pst;
   char tok1[256], tok2[256], tok3[256];
   int n1, n2, n3;
-  Guint start, end;
+  Guint start, end, code;
 
   if (!(f = globalParams->findCMapFile(collectionA, cMapNameA))) {
 
@@ -99,6 +99,30 @@ CMap *CMap::parse(CMapCache *cache, GString *collectionA,
        }
       }
       pst->getToken(tok1, sizeof(tok1), &n1);
+    } else if (!strcmp(tok2, "begincidchar")) {
+      while (pst->getToken(tok1, sizeof(tok1), &n1)) {
+       if (!strcmp(tok1, "endcidchar")) {
+         break;
+       }
+       if (!pst->getToken(tok2, sizeof(tok2), &n2) ||
+           !strcmp(tok2, "endcidchar")) {
+         error(-1, "Illegal entry in cidchar block in CMap");
+         break;
+       }
+       if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&
+             n1 >= 4 && (n1 & 1) == 0)) {
+         error(-1, "Illegal entry in cidchar block in CMap");
+         continue;
+       }
+       tok1[n1 - 1] = '\0';
+       if (sscanf(tok1 + 1, "%x", &code) != 1) {
+         error(-1, "Illegal entry in cidchar block in CMap");
+         continue;
+       }
+       n1 = (n1 - 2) / 2;
+       cmap->addCIDs(code, code, n1, (CID)atoi(tok2));
+      }
+      pst->getToken(tok1, sizeof(tok1), &n1);
     } else if (!strcmp(tok2, "begincidrange")) {
       while (pst->getToken(tok1, sizeof(tok1), &n1)) {
        if (!strcmp(tok1, "endcidrange")) {
@@ -138,7 +162,7 @@ CMap::CMap(GString *collectionA, GString *cMapNameA) {
   collection = collectionA;
   cMapName = cMapNameA;
   wMode = 0;
-  vector = (CMapVectorEntry *)gmalloc(256 * sizeof(CMapVectorEntry));
+  vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
   for (i = 0; i < 256; ++i) {
     vector[i].isVector = gFalse;
     vector[i].cid = 0;
@@ -182,7 +206,7 @@ void CMap::copyVector(CMapVectorEntry *dest, CMapVectorEntry *src) {
       if (!dest[i].isVector) {
        dest[i].isVector = gTrue;
        dest[i].vector =
-         (CMapVectorEntry *)gmalloc(256 * sizeof(CMapVectorEntry));
+         (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
        for (j = 0; j < 256; ++j) {
          dest[i].vector[j].isVector = gFalse;
          dest[i].vector[j].cid = 0;
@@ -213,7 +237,7 @@ void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,
       if (!vec[i].isVector) {
        vec[i].isVector = gTrue;
        vec[i].vector =
-         (CMapVectorEntry *)gmalloc(256 * sizeof(CMapVectorEntry));
+         (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));
        for (j = 0; j < 256; ++j) {
          vec[i].vector[j].isVector = gFalse;
          vec[i].vector[j].cid = 0;
@@ -234,7 +258,7 @@ void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
   for (i = nBytes - 1; i >= 1; --i) {
     byte = (start >> (8 * i)) & 0xff;
     if (!vec[byte].isVector) {
-      error(-1, "Invalid CID (%*x - %*x) in CMap",
+      error(-1, "Invalid CID (%0*x - %0*x) in CMap",
            2*nBytes, start, 2*nBytes, end);
       return;
     }
@@ -243,7 +267,7 @@ void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {
   cid = firstCID;
   for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {
     if (vec[byte].isVector) {
-      error(-1, "Invalid CID (%*x - %*x) in CMap",
+      error(-1, "Invalid CID (%0*x - %0*x) in CMap",
            2*nBytes, start, 2*nBytes, end);
     } else {
       vec[byte].cid = cid;
index eff2a81..c321a57 100644 (file)
@@ -73,7 +73,7 @@ private:
   CMapVectorEntry *vector;     // vector for first byte (NULL for
                                //   identity CMap)
   int refCnt;
-#ifdef MULTITHREADED
+#if MULTITHREADED
   GMutex mutex;
 #endif
 };
index c645fd0..fd235d6 100644 (file)
@@ -64,8 +64,8 @@ Catalog::Catalog(XRef *xrefA) {
   }
   pagesSize = numPages0 = (int)obj.getNum();
   obj.free();
-  pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
-  pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
+  pages = (Page **)gmallocn(pagesSize, sizeof(Page *));
+  pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref));
   for (i = 0; i < pagesSize; ++i) {
     pages[i] = NULL;
     pageRefs[i].num = -1;
@@ -105,6 +105,9 @@ Catalog::Catalog(XRef *xrefA) {
   // get the outline dictionary
   catDict.dictLookup("Outlines", &outline);
 
+  // get the AcroForm dictionary
+  catDict.dictLookup("AcroForm", &acroForm);
+
   catDict.free();
   return;
 
@@ -139,6 +142,7 @@ Catalog::~Catalog() {
   metadata.free();
   structTreeRoot.free();
   outline.free();
+  acroForm.free();
 }
 
 GString *Catalog::readMetadata() {
@@ -191,8 +195,8 @@ int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
       }
       if (start >= pagesSize) {
        pagesSize += 32;
-       pages = (Page **)grealloc(pages, pagesSize * sizeof(Page *));
-       pageRefs = (Ref *)grealloc(pageRefs, pagesSize * sizeof(Ref));
+       pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *));
+       pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref));
        for (j = pagesSize - 32; j < pagesSize; ++j) {
          pages[j] = NULL;
          pageRefs[j].num = -1;
@@ -216,7 +220,6 @@ int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
     } else {
       error(-1, "Kid object (page %d) is wrong type (%s)",
            start+1, kid.getTypeName());
-      goto err2;
     }
     kid.free();
   }
index 8ab7c61..c38f109 100644 (file)
@@ -67,6 +67,8 @@ public:
 
   Object *getOutline() { return &outline; }
 
+  Object *getAcroForm() { return &acroForm; }
+
 private:
 
   XRef *xref;                  // the xref table for this PDF file
@@ -80,6 +82,7 @@ private:
   Object metadata;             // metadata stream
   Object structTreeRoot;       // structure tree root dictionary
   Object outline;              // outline dictionary
+  Object acroForm;             // AcroForm dictionary
   GBool ok;                    // true if catalog is valid
 
   int readPageTree(Dict *pages, PageAttrs *attrs, int start);
index 2e2ad47..3702a16 100644 (file)
@@ -70,13 +70,13 @@ CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *fileName,
   }
 
   size = 32768;
-  mapA = (Unicode *)gmalloc(size * sizeof(Unicode));
+  mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
   mapLenA = 0;
 
   while (getLine(buf, sizeof(buf), f)) {
     if (mapLenA == size) {
       size *= 2;
-      mapA = (Unicode *)grealloc(mapA, size * sizeof(Unicode));
+      mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
     }
     if (sscanf(buf, "%x", &u) == 1) {
       mapA[mapLenA] = u;
@@ -115,7 +115,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
   }
 
   size = 4096;
-  mapA = (Unicode *)gmalloc(size * sizeof(Unicode));
+  mapA = (Unicode *)gmallocn(size, sizeof(Unicode));
   memset(mapA, 0, size * sizeof(Unicode));
   len = 0;
   sMapA = NULL;
@@ -152,7 +152,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
       while (u0 >= size) {
        size *= 2;
       }
-      mapA = (Unicode *)grealloc(mapA, size * sizeof(Unicode));
+      mapA = (Unicode *)greallocn(mapA, size, sizeof(Unicode));
       memset(mapA + oldSize, 0, (size - oldSize) * sizeof(Unicode));
     }
     if (n == 1) {
@@ -162,7 +162,7 @@ CharCodeToUnicode *CharCodeToUnicode::parseUnicodeToUnicode(
       if (sMapLenA == sMapSizeA) {
        sMapSizeA += 16;
        sMapA = (CharCodeToUnicodeString *)
-                 grealloc(sMapA, sMapSizeA * sizeof(CharCodeToUnicodeString));
+                 greallocn(sMapA, sMapSizeA, sizeof(CharCodeToUnicodeString));
       }
       sMapA[sMapLenA].c = u0;
       for (i = 0; i < n; ++i) {
@@ -251,7 +251,7 @@ void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,
          error(-1, "Illegal entry in bfchar block in ToUnicode CMap");
          continue;
        }
-       addMapping(code1, tok2 + 1, n2 - 1, 0);
+       addMapping(code1, tok2 + 1, n2 - 2, 0);
       }
       pst->getToken(tok1, sizeof(tok1), &n1);
     } else if (!strcmp(tok2, "beginbfrange")) {
@@ -320,7 +320,7 @@ void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
   if (code >= mapLen) {
     oldLen = mapLen;
     mapLen = (code + 256) & ~255;
-    map = (Unicode *)grealloc(map, mapLen * sizeof(Unicode));
+    map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode));
     for (i = oldLen; i < mapLen; ++i) {
       map[i] = 0;
     }
@@ -335,7 +335,7 @@ void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,
     if (sMapLen >= sMapSize) {
       sMapSize = sMapSize + 16;
       sMap = (CharCodeToUnicodeString *)
-              grealloc(sMap, sMapSize * sizeof(CharCodeToUnicodeString));
+              greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
     }
     map[code] = 0;
     sMap[sMapLen].c = code;
@@ -357,7 +357,7 @@ CharCodeToUnicode::CharCodeToUnicode(GString *tagA) {
 
   tag = tagA;
   mapLen = 256;
-  map = (Unicode *)gmalloc(mapLen * sizeof(Unicode));
+  map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
   for (i = 0; i < mapLen; ++i) {
     map[i] = 0;
   }
@@ -376,7 +376,7 @@ CharCodeToUnicode::CharCodeToUnicode(GString *tagA, Unicode *mapA,
   tag = tagA;
   mapLen = mapLenA;
   if (copyMap) {
-    map = (Unicode *)gmalloc(mapLen * sizeof(Unicode));
+    map = (Unicode *)gmallocn(mapLen, sizeof(Unicode));
     memcpy(map, mapA, mapLen * sizeof(Unicode));
   } else {
     map = mapA;
@@ -433,23 +433,30 @@ GBool CharCodeToUnicode::match(GString *tagA) {
 }
 
 void CharCodeToUnicode::setMapping(CharCode c, Unicode *u, int len) {
-  int i;
+  int i, j;
 
   if (len == 1) {
     map[c] = u[0];
   } else {
-    map[c] = 0;
-    if (sMapLen == sMapSize) {
-      sMapSize += 8;
-      sMap = (CharCodeToUnicodeString *)
-              grealloc(sMap, sMapSize * sizeof(CharCodeToUnicodeString));
+    for (i = 0; i < sMapLen; ++i) {
+      if (sMap[i].c == c) {
+       break;
+      }
     }
-    sMap[sMapLen].c = c;
-    sMap[sMapLen].len = len;
-    for (i = 0; i < len && i < maxUnicodeString; ++i) {
-      sMap[sMapLen].u[i] = u[i];
+    if (i == sMapLen) {
+      if (sMapLen == sMapSize) {
+       sMapSize += 8;
+       sMap = (CharCodeToUnicodeString *)
+                greallocn(sMap, sMapSize, sizeof(CharCodeToUnicodeString));
+      }
+      ++sMapLen;
+    }
+    map[c] = 0;
+    sMap[i].c = c;
+    sMap[i].len = len;
+    for (j = 0; j < len && j < maxUnicodeString; ++j) {
+      sMap[i].u[j] = u[j];
     }
-    ++sMapLen;
   }
 }
 
@@ -480,7 +487,7 @@ CharCodeToUnicodeCache::CharCodeToUnicodeCache(int sizeA) {
   int i;
 
   size = sizeA;
-  cache = (CharCodeToUnicode **)gmalloc(size * sizeof(CharCodeToUnicode *));
+  cache = (CharCodeToUnicode **)gmallocn(size, sizeof(CharCodeToUnicode *));
   for (i = 0; i < size; ++i) {
     cache[i] = NULL;
   }
index 605e2bd..04852ae 100644 (file)
@@ -67,6 +67,10 @@ public:
   // Map a CharCode to Unicode.
   int mapToUnicode(CharCode c, Unicode *u, int size);
 
+  // Return the mapping's length, i.e., one more than the max char
+  // code supported by the mapping.
+  CharCode getLength() { return mapLen; }
+
 private:
 
   void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);
@@ -83,7 +87,7 @@ private:
   CharCodeToUnicodeString *sMap;
   int sMapLen, sMapSize;
   int refCnt;
-#ifdef MULTITHREADED
+#if MULTITHREADED
   GMutex mutex;
 #endif
 };
index dab0750..91eb452 100644 (file)
@@ -12,6 +12,7 @@
 #pragma implementation
 #endif
 
+#include <string.h>
 #include "gmem.h"
 #include "Decrypt.h"
 
@@ -65,7 +66,8 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
                           GString *ownerKey, GString *userKey,
                           int permissions, GString *fileID,
                           GString *ownerPassword, GString *userPassword,
-                          Guchar *fileKey, GBool *ownerPasswordOk) {
+                          Guchar *fileKey, GBool encryptMetadata,
+                          GBool *ownerPasswordOk) {
   Guchar test[32], test2[32];
   GString *userPassword2;
   Guchar fState[256];
@@ -110,7 +112,8 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
     }
     userPassword2 = new GString((char *)test2, 32);
     if (makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
-                    permissions, fileID, userPassword2, fileKey)) {
+                    permissions, fileID, userPassword2, fileKey,
+                    encryptMetadata)) {
       *ownerPasswordOk = gTrue;
       delete userPassword2;
       return gTrue;
@@ -120,13 +123,15 @@ GBool Decrypt::makeFileKey(int encVersion, int encRevision, int keyLength,
 
   // try using the supplied user password
   return makeFileKey2(encVersion, encRevision, keyLength, ownerKey, userKey,
-                     permissions, fileID, userPassword, fileKey);
+                     permissions, fileID, userPassword, fileKey,
+                     encryptMetadata);
 }
 
 GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
                            GString *ownerKey, GString *userKey,
                            int permissions, GString *fileID,
-                           GString *userPassword, Guchar *fileKey) {
+                           GString *userPassword, Guchar *fileKey,
+                           GBool encryptMetadata) {
   Guchar *buf;
   Guchar test[32];
   Guchar fState[256];
@@ -136,7 +141,7 @@ GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
   GBool ok;
 
   // generate file key
-  buf = (Guchar *)gmalloc(68 + fileID->getLength());
+  buf = (Guchar *)gmalloc(72 + fileID->getLength());
   if (userPassword) {
     len = userPassword->getLength();
     if (len < 32) {
@@ -154,7 +159,14 @@ GBool Decrypt::makeFileKey2(int encVersion, int encRevision, int keyLength,
   buf[66] = (permissions >> 16) & 0xff;
   buf[67] = (permissions >> 24) & 0xff;
   memcpy(buf + 68, fileID->getCString(), fileID->getLength());
-  md5(buf, 68 + fileID->getLength(), fileKey);
+  len = 68 + fileID->getLength();
+  if (!encryptMetadata) {
+    buf[len++] = 0xff;
+    buf[len++] = 0xff;
+    buf[len++] = 0xff;
+    buf[len++] = 0xff;
+  }
+  md5(buf, len, fileKey);
   if (encRevision == 3) {
     for (i = 0; i < 50; ++i) {
       md5(fileKey, keyLength, fileKey);
index 71f9457..2beba58 100644 (file)
@@ -43,14 +43,16 @@ public:
                           GString *ownerKey, GString *userKey,
                           int permissions, GString *fileID,
                           GString *ownerPassword, GString *userPassword,
-                          Guchar *fileKey, GBool *ownerPasswordOk);
+                          Guchar *fileKey, GBool encryptMetadata,
+                          GBool *ownerPasswordOk);
 
 private:
 
   static GBool makeFileKey2(int encVersion, int encRevision, int keyLength,
                            GString *ownerKey, GString *userKey,
                            int permissions, GString *fileID,
-                           GString *userPassword, Guchar *fileKey);
+                           GString *userPassword, Guchar *fileKey,
+                           GBool encryptMetadata);
 
   int objKeyLength;
   Guchar objKey[21];
index 6274590..dd1517f 100644 (file)
@@ -47,7 +47,7 @@ void Dict::add(char *key, Object *val) {
     } else {
       size *= 2;
     }
-    entries = (DictEntry *)grealloc(entries, size * sizeof(DictEntry));
+    entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));
   }
   entries[length].key = key;
   entries[length].val = *val;
index a4cf43c..46b61ec 100644 (file)
@@ -13,6 +13,7 @@
 #endif
 
 #include <stdlib.h>
+#include <string.h>
 #include "gtypes.h"
 #include "gmem.h"
 #include "GString.h"
 
 //------------------------------------------------------------------------
 
+#define ttcfTag 0x74746366
+
+//------------------------------------------------------------------------
+
 struct TrueTypeTable {
   Guint tag;
   Guint checksum;
@@ -102,6 +107,7 @@ struct TrueTypeLoca {
 
 #define cmapTag 0x636d6170
 #define glyfTag 0x676c7966
+#define headTag 0x68656164
 #define locaTag 0x6c6f6361
 #define nameTag 0x6e616d65
 #define postTag 0x706f7374
@@ -153,9 +159,11 @@ static T42Table t42Tables[nT42Tables] = {
   { "vhea", gFalse },
   { "vmtx", gFalse }
 };
-#define t42HeadTable 3
-#define t42LocaTable 6
-#define t42GlyfTable 2
+#define t42HeadTable  3
+#define t42LocaTable  6
+#define t42GlyfTable  2
+#define t42VheaTable  9
+#define t42VmtxTable 10
 
 //------------------------------------------------------------------------
 
@@ -276,7 +284,9 @@ FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA):
 FoFiTrueType::~FoFiTrueType() {
   gfree(tables);
   gfree(cmaps);
-  delete nameToGID;
+  if (nameToGID) {
+    delete nameToGID;
+  }
 }
 
 int FoFiTrueType::getNumCmaps() {
@@ -432,7 +442,7 @@ void FoFiTrueType::convertToType42(char *psName, char **encoding,
   // write the guts of the dictionary
   cvtEncoding(encoding, outputFunc, outputStream);
   cvtCharStrings(encoding, codeToGID, outputFunc, outputStream);
-  cvtSfnts(outputFunc, outputStream, NULL);
+  cvtSfnts(outputFunc, outputStream, NULL, gFalse);
 
   // end the dictionary and define the font
   (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);
@@ -440,6 +450,7 @@ void FoFiTrueType::convertToType42(char *psName, char **encoding,
 
 void FoFiTrueType::convertToCIDType2(char *psName,
                                     Gushort *cidMap, int nCIDs,
+                                    GBool needVerticalMetrics,
                                     FoFiOutputFunc outputFunc,
                                     void *outputStream) {
   char buf[512];
@@ -540,7 +551,7 @@ void FoFiTrueType::convertToCIDType2(char *psName,
   (*outputFunc)(outputStream, "  end readonly def\n", 19);
 
   // write the guts of the dictionary
-  cvtSfnts(outputFunc, outputStream, NULL);
+  cvtSfnts(outputFunc, outputStream, NULL, needVerticalMetrics);
 
   // end the dictionary and define the font
   (*outputFunc)(outputStream,
@@ -549,6 +560,7 @@ void FoFiTrueType::convertToCIDType2(char *psName,
 }
 
 void FoFiTrueType::convertToType0(char *psName, Gushort *cidMap, int nCIDs,
+                                 GBool needVerticalMetrics,
                                  FoFiOutputFunc outputFunc,
                                  void *outputStream) {
   char buf[512];
@@ -557,7 +569,7 @@ void FoFiTrueType::convertToType0(char *psName, Gushort *cidMap, int nCIDs,
 
   // write the Type 42 sfnts array
   sfntsName = (new GString(psName))->append("_sfnts");
-  cvtSfnts(outputFunc, outputStream, sfntsName);
+  cvtSfnts(outputFunc, outputStream, sfntsName, needVerticalMetrics);
   delete sfntsName;
 
   // write the descendant Type 42 fonts
@@ -620,18 +632,28 @@ void FoFiTrueType::convertToType0(char *psName, Gushort *cidMap, int nCIDs,
 }
 
 void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
-                           void *outputStream) {
-  static char cmapTab[20] = {
+                           void *outputStream, char *name,
+                           Gushort *codeToGID) {
+  // this substitute cmap table maps char codes 0000-ffff directly to
+  // glyphs 0000-ffff
+  static char cmapTab[36] = {
     0, 0,                      // table version number
     0, 1,                      // number of encoding tables
     0, 1,                      // platform ID
     0, 0,                      // encoding ID
     0, 0, 0, 12,               // offset of subtable
-    0, 0,                      // subtable format
-    0, 1,                      // subtable length
-    0, 1,                      // subtable version
-    0,                         // map char 0 -> glyph 0
-    0                          // pad to multiple of four bytes
+    0, 4,                      // subtable format
+    0, 24,                     // subtable length
+    0, 0,                      // subtable version
+    0, 2,                      // segment count * 2
+    0, 2,                      // 2 * 2 ^ floor(log2(segCount))
+    0, 0,                      // floor(log2(segCount))
+    0, 0,                      // 2*segCount - 2*2^floor(log2(segCount))
+    (char)0xff, (char)0xff,    // endCount[0]
+    0, 0,                      // reserved
+    0, 0,                      // startCount[0]
+    0, 0,                      // idDelta[0]
+    0, 0                       // pad to a mulitple of four bytes
   };
   static char nameTab[8] = {
     0, 0,                      // format
@@ -654,9 +676,11 @@ void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
   int nZeroLengthTables;
   TrueTypeLoca *locaTable;
   TrueTypeTable *newTables;
-  int nNewTables, cmapIdx, cmapLen, glyfLen;
+  char *newNameTab, *newCmapTab;
+  int nNewTables, cmapIdx, cmapLen, glyfLen, newNameLen, newCmapLen, next;
+  Guint locaChecksum, glyfChecksum, fileChecksum;
   char *tableDir;
-  char locaBuf[4];
+  char locaBuf[4], checksumBuf[4];
   GBool ok;
   Guint t;
   int pos, i, j, k, n;
@@ -667,7 +691,7 @@ void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
   missingPost = seekTable("post") < 0;
 
   // read the loca table, check to see if it's sorted
-  locaTable = (TrueTypeLoca *)gmalloc((nGlyphs + 1) * sizeof(TrueTypeLoca));
+  locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
   unsortedLoca = gFalse;
   i = seekTable("loca");
   pos = tables[i].offset;
@@ -710,7 +734,7 @@ void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
 
   // if nothing is broken, just write the TTF file as is
   if (!missingCmap && !missingName && !missingPost && !unsortedLoca &&
-      !badCmapLen && nZeroLengthTables == 0) {
+      !badCmapLen && nZeroLengthTables == 0 && !name && !codeToGID) {
     (*outputFunc)(outputStream, (char *)file, len);
     goto done1;
   }
@@ -742,46 +766,206 @@ void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
     glyfLen = pos;
   }
 
+  // compute checksums for the loca and glyf tables
+  locaChecksum = glyfChecksum = 0;
+  if (unsortedLoca) {
+    if (locaFmt) {
+      for (j = 0; j <= nGlyphs; ++j) {
+       locaChecksum += locaTable[j].newOffset;
+      }
+    } else {
+      for (j = 0; j <= nGlyphs; j += 2) {
+       locaChecksum += locaTable[j].newOffset << 16;
+       if (j + 1 <= nGlyphs) {
+         locaChecksum += locaTable[j+1].newOffset;
+       }
+      }
+    }
+    pos = tables[seekTable("glyf")].offset;
+    for (j = 0; j < nGlyphs; ++j) {
+      n = locaTable[j].len;
+      if (n > 0) {
+       k = locaTable[j].origOffset;
+       if (checkRegion(pos + k, n)) {
+         glyfChecksum += computeTableChecksum(file + pos + k, n);
+       }
+      }
+    }
+  }
+
+  // construct the new name table
+  if (name) {
+    n = strlen(name);
+    newNameLen = (6 + 4*12 + 2 * (3*n + 7) + 3) & ~3;
+    newNameTab = (char *)gmalloc(newNameLen);
+    memset(newNameTab, 0, newNameLen);
+    newNameTab[0] = 0;         // format selector
+    newNameTab[1] = 0;
+    newNameTab[2] = 0;         // number of name records
+    newNameTab[3] = 4;
+    newNameTab[4] = 0;         // offset to start of string storage
+    newNameTab[5] = 6 + 4*12;
+    next = 0;
+    for (i = 0; i < 4; ++i) {
+      newNameTab[6 + i*12 + 0] = 0;    // platform ID = Microsoft
+      newNameTab[6 + i*12 + 1] = 3;
+      newNameTab[6 + i*12 + 2] = 0;    // encoding ID = Unicode
+      newNameTab[6 + i*12 + 3] = 1;
+      newNameTab[6 + i*12 + 4] = 0x04; // language ID = American English
+      newNameTab[6 + i*12 + 5] = 0x09;
+      newNameTab[6 + i*12 + 6] = 0;    // name ID
+      newNameTab[6 + i*12 + 7] = i + 1;
+      newNameTab[6 + i*12 + 8] = i+1 == 2 ? 0 : ((2*n) >> 8); // string length
+      newNameTab[6 + i*12 + 9] = i+1 == 2 ? 14 : ((2*n) & 0xff);
+      newNameTab[6 + i*12 + 10] = next >> 8;               // string offset
+      newNameTab[6 + i*12 + 11] = next & 0xff;
+      if (i+1 == 2) {
+       memcpy(newNameTab + 6 + 4*12 + next, "\0R\0e\0g\0u\0l\0a\0r", 14);
+       next += 14;
+      } else {
+       for (j = 0; j < n; ++j) {
+         newNameTab[6 + 4*12 + next + 2*j] = 0;
+         newNameTab[6 + 4*12 + next + 2*j + 1] = name[j];
+       }
+       next += 2*n;
+      }
+    }
+  } else {
+    newNameLen = 0;
+    newNameTab = NULL;
+  }
+
+  // construct the new cmap table
+  if (codeToGID) {
+    newCmapLen = 44 + 256 * 2;
+    newCmapTab = (char *)gmalloc(newCmapLen);
+    newCmapTab[0] = 0;         // table version number = 0
+    newCmapTab[1] = 0;
+    newCmapTab[2] = 0;         // number of encoding tables = 1
+    newCmapTab[3] = 1;
+    newCmapTab[4] = 0;         // platform ID = Microsoft
+    newCmapTab[5] = 3;
+    newCmapTab[6] = 0;         // encoding ID = Unicode
+    newCmapTab[7] = 1;
+    newCmapTab[8] = 0;         // offset of subtable
+    newCmapTab[9] = 0;
+    newCmapTab[10] = 0;
+    newCmapTab[11] = 12;
+    newCmapTab[12] = 0;                // subtable format = 4
+    newCmapTab[13] = 4;
+    newCmapTab[14] = 0x02;     // subtable length
+    newCmapTab[15] = 0x20;
+    newCmapTab[16] = 0;                // subtable version = 0
+    newCmapTab[17] = 0;
+    newCmapTab[18] = 0;                // segment count * 2
+    newCmapTab[19] = 4;
+    newCmapTab[20] = 0;                // 2 * 2 ^ floor(log2(segCount))
+    newCmapTab[21] = 4;
+    newCmapTab[22] = 0;                // floor(log2(segCount))
+    newCmapTab[23] = 1;
+    newCmapTab[24] = 0;                // 2*segCount - 2*2^floor(log2(segCount))
+    newCmapTab[25] = 0;
+    newCmapTab[26] = 0x00;     // endCount[0]
+    newCmapTab[27] = (char)0xff;
+    newCmapTab[28] = (char)0xff; // endCount[1]
+    newCmapTab[29] = (char)0xff;
+    newCmapTab[30] = 0;                // reserved
+    newCmapTab[31] = 0;
+    newCmapTab[32] = 0x00;     // startCount[0]
+    newCmapTab[33] = 0x00;
+    newCmapTab[34] = (char)0xff; // startCount[1]
+    newCmapTab[35] = (char)0xff;
+    newCmapTab[36] = 0;                // idDelta[0]
+    newCmapTab[37] = 0;
+    newCmapTab[38] = 0;                // idDelta[1]
+    newCmapTab[39] = 1;
+    newCmapTab[40] = 0;                // idRangeOffset[0]
+    newCmapTab[41] = 4;
+    newCmapTab[42] = 0;                // idRangeOffset[1]
+    newCmapTab[43] = 0;
+    for (i = 0; i < 256; ++i) {
+      newCmapTab[44 + 2*i] = codeToGID[i] >> 8;
+      newCmapTab[44 + 2*i + 1] = codeToGID[i] & 0xff;
+    }
+  } else {
+    newCmapLen = 0;
+    newCmapTab = NULL;
+  }
+
   // construct the new table directory:
   // - keep all original tables with non-zero length
   // - fix the cmap table's length, if necessary
   // - add missing tables
   // - sort the table by tag
   // - compute new table positions, including 4-byte alignment
+  // - (re)compute table checksums
   nNewTables = nTables - nZeroLengthTables +
                (missingCmap ? 1 : 0) + (missingName ? 1 : 0) +
                (missingPost ? 1 : 0);
-  newTables = (TrueTypeTable *)gmalloc(nNewTables * sizeof(TrueTypeTable));
+  newTables = (TrueTypeTable *)gmallocn(nNewTables, sizeof(TrueTypeTable));
   j = 0;
   for (i = 0; i < nTables; ++i) {
     if (tables[i].len > 0) {
       newTables[j] = tables[i];
       newTables[j].origOffset = tables[i].offset;
-      if (newTables[j].tag == cmapTag && badCmapLen) {
+      if (checkRegion(tables[i].offset, newTables[i].len)) {
+       newTables[j].checksum =
+           computeTableChecksum(file + tables[i].offset, tables[i].len);
+       if (tables[i].tag == headTag) {
+         // don't include the file checksum
+         newTables[j].checksum -= getU32BE(tables[i].offset + 8, &ok);
+       }
+      }
+      if (newTables[j].tag == cmapTag && codeToGID) {
+       newTables[j].len = newCmapLen;
+       newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
+                                                    newCmapLen);
+      } else if (newTables[j].tag == cmapTag && badCmapLen) {
        newTables[j].len = cmapLen;
       } else if (newTables[j].tag == locaTag && unsortedLoca) {
        newTables[j].len = (nGlyphs + 1) * (locaFmt ? 4 : 2);
+       newTables[j].checksum = locaChecksum;
       } else if (newTables[j].tag == glyfTag && unsortedLoca) {
        newTables[j].len = glyfLen;
+       newTables[j].checksum = glyfChecksum;
+      } else if (newTables[j].tag == nameTag && name) {
+       newTables[j].len = newNameLen;
+       newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
+                                                    newNameLen);
       }
       ++j;
     }
   }
   if (missingCmap) {
     newTables[j].tag = cmapTag;
-    newTables[j].checksum = 0; //~ should compute the checksum
-    newTables[j].len = sizeof(cmapTab);
+    if (codeToGID) {
+      newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,
+                                                  newCmapLen);
+      newTables[j].len = newCmapLen;
+    } else {
+      newTables[j].checksum = computeTableChecksum((Guchar *)cmapTab,
+                                                  sizeof(cmapTab));
+      newTables[j].len = sizeof(cmapTab);
+    }
     ++j;
   }
   if (missingName) {
     newTables[j].tag = nameTag;
-    newTables[j].checksum = 0; //~ should compute the checksum
-    newTables[j].len = sizeof(nameTab);
+    if (name) {
+      newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab,
+                                                  newNameLen);
+      newTables[j].len = newNameLen;
+    } else {
+      newTables[j].checksum = computeTableChecksum((Guchar *)nameTab,
+                                                  sizeof(nameTab));
+      newTables[j].len = sizeof(nameTab);
+    }
     ++j;
   }
   if (missingPost) {
     newTables[j].tag = postTag;
-    newTables[j].checksum = 0; //~ should compute the checksum
+    newTables[j].checksum = computeTableChecksum((Guchar *)postTab,
+                                                sizeof(postTab));
     newTables[j].len = sizeof(postTab);
     ++j;
   }
@@ -835,10 +1019,38 @@ void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
   }
   (*outputFunc)(outputStream, tableDir, 12 + nNewTables * 16);
 
+  // compute the file checksum
+  fileChecksum = computeTableChecksum((Guchar *)tableDir,
+                                     12 + nNewTables * 16);
+  for (i = 0; i < nNewTables; ++i) {
+    fileChecksum += newTables[i].checksum;
+  }
+  fileChecksum = 0xb1b0afba - fileChecksum;
+
   // write the tables
   for (i = 0; i < nNewTables; ++i) {
-    if (newTables[i].tag == cmapTag && missingCmap) {
+    if (newTables[i].tag == headTag) {
+      if (checkRegion(newTables[i].origOffset, newTables[i].len)) {
+       (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset, 8);
+       checksumBuf[0] = fileChecksum >> 24;
+       checksumBuf[1] = fileChecksum >> 16;
+       checksumBuf[2] = fileChecksum >> 8;
+       checksumBuf[3] = fileChecksum;
+       (*outputFunc)(outputStream, checksumBuf, 4);
+       (*outputFunc)(outputStream,
+                     (char *)file + newTables[i].origOffset + 12,
+                     newTables[i].len - 12);
+      } else {
+       for (j = 0; j < newTables[i].len; ++j) {
+         (*outputFunc)(outputStream, "\0", 1);
+       }
+      }
+    } else if (newTables[i].tag == cmapTag && codeToGID) {
+      (*outputFunc)(outputStream, newCmapTab, newTables[i].len);
+    } else if (newTables[i].tag == cmapTag && missingCmap) {
       (*outputFunc)(outputStream, cmapTab, newTables[i].len);
+    } else if (newTables[i].tag == nameTag && name) {
+      (*outputFunc)(outputStream, newNameTab, newTables[i].len);
     } else if (newTables[i].tag == nameTag && missingName) {
       (*outputFunc)(outputStream, nameTab, newTables[i].len);
     } else if (newTables[i].tag == postTag && missingPost) {
@@ -890,6 +1102,8 @@ void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,
     }
   }
 
+  gfree(newCmapTab);
+  gfree(newNameTab);
   gfree(tableDir);
   gfree(newTables);
  done1:
@@ -974,7 +1188,8 @@ void FoFiTrueType::cvtCharStrings(char **encoding,
 }
 
 void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
-                           void *outputStream, GString *name) {
+                           void *outputStream, GString *name,
+                           GBool needVerticalMetrics) {
   Guchar headData[54];
   TrueTypeLoca *locaTable;
   Guchar *locaData;
@@ -984,6 +1199,28 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
   Guint checksum;
   int nNewTables;
   int length, pos, glyfPos, i, j, k;
+  Guchar vheaTab[36] = {
+    0, 1, 0, 0,                        // table version number
+    0, 0,                      // ascent
+    0, 0,                      // descent
+    0, 0,                      // reserved
+    0, 0,                      // max advance height
+    0, 0,                      // min top side bearing
+    0, 0,                      // min bottom side bearing
+    0, 0,                      // y max extent
+    0, 0,                      // caret slope rise
+    0, 1,                      // caret slope run
+    0, 0,                      // caret offset
+    0, 0,                      // reserved
+    0, 0,                      // reserved
+    0, 0,                      // reserved
+    0, 0,                      // reserved
+    0, 0,                      // metric data format
+    0, 1                       // number of advance heights in vmtx table
+  };
+  Guchar *vmtxTab;
+  GBool needVhea, needVmtx;
+  int advance;
 
   // construct the 'head' table, zero out the font checksum
   i = seekTable("head");
@@ -1001,7 +1238,7 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
   // table, cmpTrueTypeLocaPos uses offset as its primary sort key,
   // and idx as its secondary key (ensuring that adjacent entries with
   // the same pos value remain in the same order)
-  locaTable = (TrueTypeLoca *)gmalloc((nGlyphs + 1) * sizeof(TrueTypeLoca));
+  locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));
   i = seekTable("loca");
   pos = tables[i].offset;
   ok = gTrue;
@@ -1031,7 +1268,7 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
   }
 
   // construct the new 'loca' table
-  locaData = (Guchar *)gmalloc((nGlyphs + 1) * (locaFmt ? 4 : 2));
+  locaData = (Guchar *)gmallocn(nGlyphs + 1, (locaFmt ? 4 : 2));
   for (i = 0; i <= nGlyphs; ++i) {
     pos = locaTable[i].newOffset;
     if (locaFmt) {
@@ -1053,6 +1290,22 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
       ++nNewTables;
     }
   }
+  vmtxTab = NULL; // make gcc happy
+  advance = 0; // make gcc happy
+  if (needVerticalMetrics) {
+    needVhea = seekTable("vhea") < 0;
+    needVmtx = seekTable("vmtx") < 0;
+    if (needVhea || needVmtx) {
+      i = seekTable("head");
+      advance = getU16BE(tables[i].offset + 18, &ok); // units per em
+      if (needVhea) {
+       ++nNewTables;
+      }
+      if (needVmtx) {
+       ++nNewTables;
+      }
+    }
+  }
 
   // construct the new table headers, including table checksums
   // (pad each table out to a multiple of 4 bytes)
@@ -1088,6 +1341,21 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
        if (checkRegion(tables[j].offset, length)) {
          checksum = computeTableChecksum(file + tables[j].offset, length);
        }
+      } else if (needVerticalMetrics && i == t42VheaTable) {
+       vheaTab[10] = advance / 256;    // max advance height
+       vheaTab[11] = advance % 256;
+       length = sizeof(vheaTab);
+       checksum = computeTableChecksum(vheaTab, length);
+      } else if (needVerticalMetrics && i == t42VmtxTable) {
+       length = 4 + (nGlyphs - 1) * 4;
+       vmtxTab = (Guchar *)gmalloc(length);
+       vmtxTab[0] = advance / 256;
+       vmtxTab[1] = advance % 256;
+       for (j = 2; j < length; j += 2) {
+         vmtxTab[j] = 0;
+         vmtxTab[j+1] = 0;
+       }
+       checksum = computeTableChecksum(vmtxTab, length);
       } else if (t42Tables[i].required) {
        //~ error(-1, "Embedded TrueType font is missing a required table ('%s')",
        //~       t42Tables[i].tag);
@@ -1193,6 +1461,11 @@ void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,
            checkRegion(tables[j].offset, tables[j].len)) {
          dumpString(file + tables[j].offset, tables[j].len,
                     outputFunc, outputStream);
+       } else if (needVerticalMetrics && i == t42VheaTable) {
+         dumpString(vheaTab, length, outputFunc, outputStream);
+       } else if (needVerticalMetrics && i == t42VmtxTable) {
+         dumpString(vmtxTab, length, outputFunc, outputStream);
+         gfree(vmtxTab);
        }
       }
     }
@@ -1263,17 +1536,32 @@ Guint FoFiTrueType::computeTableChecksum(Guchar *data, int length) {
 }
 
 void FoFiTrueType::parse() {
+  Guint topTag;
   int pos, i, j;
 
   parsedOk = gTrue;
 
+  // look for a collection (TTC)
+  topTag = getU32BE(0, &parsedOk);
+  if (!parsedOk) {
+    return;
+  }
+  if (topTag == ttcfTag) {
+    pos = getU32BE(12, &parsedOk);
+    if (!parsedOk) {
+      return;
+    }
+  } else {
+    pos = 0;
+  }
+
   // read the table directory
-  nTables = getU16BE(4, &parsedOk);
+  nTables = getU16BE(pos + 4, &parsedOk);
   if (!parsedOk) {
     return;
   }
-  tables = (TrueTypeTable *)gmalloc(nTables * sizeof(TrueTypeTable));
-  pos = 12;
+  tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable));
+  pos += 12;
   for (i = 0; i < nTables; ++i) {
     tables[i].tag = getU32BE(pos, &parsedOk);
     tables[i].checksum = getU32BE(pos + 4, &parsedOk);
@@ -1309,7 +1597,7 @@ void FoFiTrueType::parse() {
     if (!parsedOk) {
       return;
     }
-    cmaps = (TrueTypeCmap *)gmalloc(nCmaps * sizeof(TrueTypeCmap));
+    cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap));
     for (j = 0; j < nCmaps; ++j) {
       cmaps[j].platform = getU16BE(pos, &parsedOk);
       cmaps[j].encoding = getU16BE(pos + 2, &parsedOk);
@@ -1343,25 +1631,45 @@ void FoFiTrueType::parse() {
     return;
   }
 
-  // read the post table
-  readPostTable();
+  // make sure the loca table is sane (correct length and entries are
+  // in bounds)
+  i = seekTable("loca");
+  if (tables[i].len < (nGlyphs + 1) * (locaFmt ? 4 : 2)) {
+    parsedOk = gFalse;
+    return;
+  }
+  for (j = 0; j <= nGlyphs; ++j) {
+    if (locaFmt) {
+      pos = (int)getU32BE(tables[i].offset + j*4, &parsedOk);
+    } else {
+      pos = getU16BE(tables[i].offset + j*2, &parsedOk);
+    }
+    if (pos < 0 || pos > len) {
+      parsedOk = gFalse;
+    }
+  }
   if (!parsedOk) {
     return;
   }
+
+  // read the post table
+  readPostTable();
 }
 
 void FoFiTrueType::readPostTable() {
   GString *name;
   int tablePos, postFmt, stringIdx, stringPos;
+  GBool ok;
   int i, j, n, m;
 
+  ok = gTrue;
   if ((i = seekTable("post")) < 0) {
     return;
   }
   tablePos = tables[i].offset;
-  postFmt = getU32BE(tablePos, &parsedOk);
-  if (!parsedOk) {
-    return;
+  postFmt = getU32BE(tablePos, &ok);
+  if (!ok) {
+    goto err;
   }
   if (postFmt == 0x00010000) {
     nameToGID = new GHash(gTrue);
@@ -1370,9 +1678,9 @@ void FoFiTrueType::readPostTable() {
     }
   } else if (postFmt == 0x00020000) {
     nameToGID = new GHash(gTrue);
-    n = getU16BE(tablePos + 32, &parsedOk);
-    if (!parsedOk) {
-      return;
+    n = getU16BE(tablePos + 32, &ok);
+    if (!ok) {
+      goto err;
     }
     if (n > nGlyphs) {
       n = nGlyphs;
@@ -1380,7 +1688,7 @@ void FoFiTrueType::readPostTable() {
     stringIdx = 0;
     stringPos = tablePos + 34 + 2*n;
     for (i = 0; i < n; ++i) {
-      j = getU16BE(tablePos + 34 + 2*i, &parsedOk);
+      j = getU16BE(tablePos + 34 + 2*i, &ok);
       if (j < 258) {
        nameToGID->removeInt(macGlyphNames[j]);
        nameToGID->add(new GString(macGlyphNames[j]), i);
@@ -1389,15 +1697,14 @@ void FoFiTrueType::readPostTable() {
        if (j != stringIdx) {
          for (stringIdx = 0, stringPos = tablePos + 34 + 2*n;
               stringIdx < j;
-              ++stringIdx, stringPos += 1 + getU8(stringPos, &parsedOk)) ;
-         if (!parsedOk) {
-           return;
+              ++stringIdx, stringPos += 1 + getU8(stringPos, &ok)) ;
+         if (!ok) {
+           goto err;
          }
        }
-       m = getU8(stringPos, &parsedOk);
-       if (!parsedOk || !checkRegion(stringPos + 1, m)) {
-         parsedOk = gFalse;
-         return;
+       m = getU8(stringPos, &ok);
+       if (!ok || !checkRegion(stringPos + 1, m)) {
+         goto err;
        }
        name = new GString((char *)&file[stringPos + 1], m);
        nameToGID->removeInt(name);
@@ -1409,9 +1716,9 @@ void FoFiTrueType::readPostTable() {
   } else if (postFmt == 0x00028000) {
     nameToGID = new GHash(gTrue);
     for (i = 0; i < nGlyphs; ++i) {
-      j = getU8(tablePos + 32 + i, &parsedOk);
-      if (!parsedOk) {
-       return;
+      j = getU8(tablePos + 32 + i, &ok);
+      if (!ok) {
+       goto err;
       }
       if (j < 258) {
        nameToGID->removeInt(macGlyphNames[j]);
@@ -1419,6 +1726,14 @@ void FoFiTrueType::readPostTable() {
       }
     }
   }
+
+  return;
+
+ err:
+  if (nameToGID) {
+    delete nameToGID;
+    nameToGID = NULL;
+  }
 }
 
 int FoFiTrueType::seekTable(char *tag) {
index ea05eec..f7b09d6 100644 (file)
@@ -18,6 +18,7 @@
 #include "gtypes.h"
 #include "FoFiBase.h"
 
+class GString;
 class GHash;
 struct TrueTypeTable;
 struct TrueTypeCmap;
@@ -83,6 +84,7 @@ public:
   // font).  The <cidMap> array maps CIDs to GIDs; it has <nCIDs>
   // entries.
   void convertToCIDType2(char *psName, Gushort *cidMap, int nCIDs,
+                        GBool needVerticalMetrics,
                         FoFiOutputFunc outputFunc, void *outputStream);
 
   // Convert to a Type 0 (but non-CID) composite font, suitable for
@@ -91,12 +93,16 @@ public:
   // table in the font).  The <cidMap> array maps CIDs to GIDs; it has
   // <nCIDs> entries.
   void convertToType0(char *psName, Gushort *cidMap, int nCIDs,
+                     GBool needVerticalMetrics,
                      FoFiOutputFunc outputFunc, void *outputStream);
 
   // Write a clean TTF file, filling in missing tables and correcting
-  // various other errors.  If the font is complete and correct, it
-  // will be written unmodified.
-  void writeTTF(FoFiOutputFunc outputFunc, void *outputStream);
+  // various other errors.  If <name> is non-NULL, the font is renamed
+  // to <name>.  If <codeToGID> is non-NULL, the font is re-encoded,
+  // using a Windows Unicode cmap.  If <name> is NULL and the font is
+  // complete and correct, it will be written unmodified.
+  void writeTTF(FoFiOutputFunc outputFunc, void *outputStream,
+               char *name = NULL, Gushort *codeToGID = NULL);
 
 private:
 
@@ -109,7 +115,8 @@ private:
                      FoFiOutputFunc outputFunc,
                      void *outputStream);
   void cvtSfnts(FoFiOutputFunc outputFunc,
-               void *outputStream, GString *name);
+               void *outputStream, GString *name,
+               GBool needVerticalMetrics);
   void dumpString(Guchar *s, int length,
                  FoFiOutputFunc outputFunc,
                  void *outputStream);
index fe54a63..a8a69fd 100644 (file)
@@ -108,6 +108,9 @@ void FoFiType1::writeEncoded(char **newEncoding,
     for (line = getNextLine(line);
         line && strncmp(line, "readonly def", 12);
         line = getNextLine(line)) ;
+    if (line) {
+      line = getNextLine(line);
+    }
   }
   if (line) {
     (*outputFunc)(outputStream, line, ((char *)file + len) - line);
@@ -156,13 +159,13 @@ void FoFiType1::parse() {
       encoding = fofiType1StandardEncoding;
     } else if (!encoding &&
               !strncmp(line, "/Encoding 256 array", 19)) {
-      encoding = (char **)gmalloc(256 * sizeof(char *));
+      encoding = (char **)gmallocn(256, sizeof(char *));
       for (j = 0; j < 256; ++j) {
        encoding[j] = NULL;
       }
-      line = getNextLine(line);
-      for (j = 0; j < 300 && line; ++j) {
-       line1 = getNextLine(line);
+      for (j = 0, line = getNextLine(line);
+          j < 300 && line && (line1 = getNextLine(line));
+          ++j, line = line1) {
        if ((n = line1 - line) > 255) {
          n = 255;
        }
@@ -187,20 +190,22 @@ void FoFiType1::parse() {
            }
          }
        } else {
-         if (strtok(buf, " \t") &&
-             (p = strtok(NULL, " \t\n\r")) && !strcmp(p, "def")) {
-           break;
+         p = strtok(buf, " \t\n\r");
+         if (p)
+         {
+           if (!strcmp(p, "def")) break;
+           if (!strcmp(p, "readonly")) break;
+           // the spec does not says this but i'm mantaining old xpdf behaviour that accepts "foo def" as end of the encoding array
+           p = strtok(buf, " \t\n\r");
+           if (p && !strcmp(p, "def")) break;
          }
        }
-       line = line1;
       }
       //~ check for getinterval/putinterval junk
 
     } else {
       line = getNextLine(line);
     }
-
-    ++i;
   }
 
   parsed = gTrue;
index e6f2b64..62649ed 100644 (file)
@@ -51,6 +51,9 @@ struct Type1CTopDict {
   int paintType;
   int charstringType;
   double fontMatrix[6];
+  GBool hasFontMatrix;         // CID fonts are allowed to put their
+                               //   FontMatrix in the FD instead of the
+                               //   top dict
   int uniqueID;
   double fontBBox[4];
   double strokeWidth;
@@ -73,6 +76,8 @@ struct Type1CTopDict {
 #define type1CMaxStemSnap   12
 
 struct Type1CPrivateDict {
+  double fontMatrix[6];
+  GBool hasFontMatrix;
   int blueValues[type1CMaxBlueValues];
   int nBlueValues;
   int otherBlues[type1CMaxOtherBlues];
@@ -221,6 +226,7 @@ private:
   int nOps;                    // number of operands
   int nHints;                  // number of hints for the current glyph
   GBool firstOp;               // true if we haven't hit the first op yet
+  GBool openPath;              // true if there is an unclosed path
 };
 
 #endif
index 46b1912..f699641 100644 (file)
@@ -185,7 +185,7 @@ void IdentityFunction::transform(double *in, double *out) {
 
 SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
   Stream *str;
-  int nSamples, sampleBits;
+  int sampleBits;
   double sampleMul;
   Object obj1, obj2;
   Guint buf, bitMask;
@@ -228,6 +228,10 @@ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
     obj2.free();
   }
   obj1.free();
+  idxMul[0] = n;
+  for (i = 1; i < m; ++i) {
+    idxMul[i] = idxMul[i-1] * sampleSize[i-1];
+  }
 
   //----- BitsPerSample
   if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {
@@ -264,6 +268,10 @@ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
     }
   }
   obj1.free();
+  for (i = 0; i < m; ++i) {
+    inputMul[i] = (encode[i][1] - encode[i][0]) /
+                  (domain[i][1] - domain[i][0]);
+  }
 
   //----- Decode
   if (dict->lookup("Decode", &obj1)->isArray() &&
@@ -296,7 +304,7 @@ SampledFunction::SampledFunction(Object *funcObj, Dict *dict) {
   nSamples = n;
   for (i = 0; i < m; ++i)
     nSamples *= sampleSize[i];
-  samples = (double *)gmalloc(nSamples * sizeof(double));
+  samples = (double *)gmallocn(nSamples, sizeof(double));
   buf = 0;
   bits = 0;
   bitMask = (1 << sampleBits) - 1;
@@ -342,37 +350,34 @@ SampledFunction::~SampledFunction() {
 }
 
 SampledFunction::SampledFunction(SampledFunction *func) {
-  int nSamples, i;
-
   memcpy(this, func, sizeof(SampledFunction));
-
-  nSamples = n;
-  for (i = 0; i < m; ++i) {
-    nSamples *= sampleSize[i];
-  }
-  samples = (double *)gmalloc(nSamples * sizeof(double));
+  samples = (double *)gmallocn(nSamples, sizeof(double));
   memcpy(samples, func->samples, nSamples * sizeof(double));
 }
 
 void SampledFunction::transform(double *in, double *out) {
   double x;
-  int e[2][funcMaxInputs];
-  double efrac[funcMaxInputs];
-  double s0[1 << funcMaxInputs], s1[1 << funcMaxInputs];
-  int i, j, k, idx;
+  int e[funcMaxInputs][2];
+  double efrac0[funcMaxInputs];
+  double efrac1[funcMaxInputs];
+  double s[1 << funcMaxInputs];
+  int i, j, k, idx, t;
 
   // map input values into sample array
   for (i = 0; i < m; ++i) {
-    x = ((in[i] - domain[i][0]) / (domain[i][1] - domain[i][0])) *
-        (encode[i][1] - encode[i][0]) + encode[i][0];
+    x = (in[i] - domain[i][0]) * inputMul[i] + encode[i][0];
     if (x < 0) {
       x = 0;
     } else if (x > sampleSize[i] - 1) {
       x = sampleSize[i] - 1;
     }
-    e[0][i] = (int)floor(x);
-    e[1][i] = (int)ceil(x);
-    efrac[i] = x - e[0][i];
+    e[i][0] = (int)x;
+    if ((e[i][1] = e[i][0] + 1) >= sampleSize[i]) {
+      // this happens if in[i] = domain[i][1]
+      e[i][1] = e[i][0];
+    }
+    efrac1[i] = x - e[i][0];
+    efrac0[i] = 1 - efrac1[i];
   }
 
   // for each output, do m-linear interpolation
@@ -380,24 +385,22 @@ void SampledFunction::transform(double *in, double *out) {
 
     // pull 2^m values out of the sample array
     for (j = 0; j < (1<<m); ++j) {
-      idx = 0;
-      for (k = m - 1; k >= 0; --k) {
-       idx = idx * sampleSize[k] + e[(j >> k) & 1][k];
+      idx = i;
+      for (k = 0, t = j; k < m; ++k, t >>= 1) {
+       idx += idxMul[k] * (e[k][t & 1]);
       }
-      idx = idx * n + i;
-      s0[j] = samples[idx];
+      s[j] = samples[idx];
     }
 
     // do m sets of interpolations
-    for (j = 0; j < m; ++j) {
-      for (k = 0; k < (1 << (m - j)); k += 2) {
-       s1[k >> 1] = (1 - efrac[j]) * s0[k] + efrac[j] * s0[k+1];
+    for (j = 0, t = (1<<m); j < m; ++j, t >>= 1) {
+      for (k = 0; k < t; k += 2) {
+       s[k >> 1] = efrac0[j] * s[k] + efrac1[j] * s[k+1];
       }
-      memcpy(s0, s1, (1 << (m - j - 1)) * sizeof(double));
     }
 
     // map output value to range
-    out[i] = s0[0] * (decode[i][1] - decode[i][0]) + decode[i][0];
+    out[i] = s[0] * (decode[i][1] - decode[i][0]) + decode[i][0];
     if (out[i] < range[i][0]) {
       out[i] = range[i][0];
     } else if (out[i] > range[i][1]) {
@@ -553,9 +556,9 @@ StitchingFunction::StitchingFunction(Object *funcObj, Dict *dict) {
     goto err1;
   }
   k = obj1.arrayGetLength();
-  funcs = (Function **)gmalloc(k * sizeof(Function *));
-  bounds = (double *)gmalloc((k + 1) * sizeof(double));
-  encode = (double *)gmalloc(2 * k * sizeof(double));
+  funcs = (Function **)gmallocn(k, sizeof(Function *));
+  bounds = (double *)gmallocn(k + 1, sizeof(double));
+  encode = (double *)gmallocn(2 * k, sizeof(double));
   for (i = 0; i < k; ++i) {
     funcs[i] = NULL;
   }
@@ -619,13 +622,13 @@ StitchingFunction::StitchingFunction(StitchingFunction *func) {
   int i;
 
   k = func->k;
-  funcs = (Function **)gmalloc(k * sizeof(Function *));
+  funcs = (Function **)gmallocn(k, sizeof(Function *));
   for (i = 0; i < k; ++i) {
     funcs[i] = func->funcs[i]->copy();
   }
-  bounds = (double *)gmalloc((k + 1) * sizeof(double));
+  bounds = (double *)gmallocn(k + 1, sizeof(double));
   memcpy(bounds, func->bounds, (k + 1) * sizeof(double));
-  encode = (double *)gmalloc(2 * k * sizeof(double));
+  encode = (double *)gmallocn(2 * k, sizeof(double));
   memcpy(encode, func->encode, 2 * k * sizeof(double));
   ok = gTrue;
 }
@@ -916,10 +919,14 @@ double PSStack::popNum() {
 void PSStack::copy(int n) {
   int i;
 
+  if (sp + n > psStackSize) {
+    error(-1, "Stack underflow in PostScript function");
+    return;
+  }
   if (!checkOverflow(n)) {
     return;
   }
-  for (i = sp + n - 1; i <= sp; ++i) {
+  for (i = sp + n - 1; i >= sp; --i) {
     stack[i - n] = stack[i];
   }
   sp -= n;
@@ -990,6 +997,7 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
   str = funcObj->getStream();
 
   //----- parse the function
+  codeString = new GString();
   str->reset();
   if (!(tok = getToken(str)) || tok->cmp("{")) {
     error(-1, "Expected '{' at start of PostScript function");
@@ -1015,12 +1023,14 @@ PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {
 
 PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {
   memcpy(this, func, sizeof(PostScriptFunction));
-  code = (PSObject *)gmalloc(codeSize * sizeof(PSObject));
+  code = (PSObject *)gmallocn(codeSize, sizeof(PSObject));
   memcpy(code, func->code, codeSize * sizeof(PSObject));
+  codeString = func->codeString->copy();
 }
 
 PostScriptFunction::~PostScriptFunction() {
   gfree(code);
+  delete codeString;
 }
 
 void PostScriptFunction::transform(double *in, double *out) {
@@ -1174,6 +1184,9 @@ GString *PostScriptFunction::getToken(Stream *str) {
   s = new GString();
   do {
     c = str->getChar();
+    if (c != EOF) {
+      codeString->append(c);
+    }
   } while (c != EOF && isspace(c));
   if (c == '{' || c == '}') {
     s->append((char)c);
@@ -1185,6 +1198,7 @@ GString *PostScriptFunction::getToken(Stream *str) {
        break;
       }
       str->getChar();
+      codeString->append(c);
     }
   } else {
     while (1) {
@@ -1194,6 +1208,7 @@ GString *PostScriptFunction::getToken(Stream *str) {
        break;
       }
       str->getChar();
+      codeString->append(c);
     }
   }
   return s;
@@ -1202,7 +1217,7 @@ GString *PostScriptFunction::getToken(Stream *str) {
 void PostScriptFunction::resizeCode(int newSize) {
   if (newSize >= codeSize) {
     codeSize += 64;
-    code = (PSObject *)grealloc(code, codeSize * sizeof(PSObject));
+    code = (PSObject *)greallocn(code, codeSize, sizeof(PSObject));
   }
 }
 
index 0ceb035..bfaf83e 100644 (file)
@@ -45,10 +45,24 @@ public:
 
   virtual Function *copy() = 0;
 
+  // Return the function type:
+  //   -1 : identity
+  //    0 : sampled
+  //    2 : exponential
+  //    3 : stitching
+  //    4 : PostScript
+  virtual int getType() = 0;
+
   // Return size of input and output tuples.
   int getInputSize() { return m; }
   int getOutputSize() { return n; }
 
+  double getDomainMin(int i) { return domain[i][0]; }
+  double getDomainMax(int i) { return domain[i][1]; }
+  double getRangeMin(int i) { return range[i][0]; }
+  double getRangeMax(int i) { return range[i][1]; }
+  GBool getHasRange() { return hasRange; }
+
   // Transform an input tuple into an output tuple.
   virtual void transform(double *in, double *out) = 0;
 
@@ -74,6 +88,7 @@ public:
   IdentityFunction();
   virtual ~IdentityFunction();
   virtual Function *copy() { return new IdentityFunction(); }
+  virtual int getType() { return -1; }
   virtual void transform(double *in, double *out);
   virtual GBool isOk() { return gTrue; }
 
@@ -90,9 +105,17 @@ public:
   SampledFunction(Object *funcObj, Dict *dict);
   virtual ~SampledFunction();
   virtual Function *copy() { return new SampledFunction(this); }
+  virtual int getType() { return 0; }
   virtual void transform(double *in, double *out);
   virtual GBool isOk() { return ok; }
 
+  int getSampleSize(int i) { return sampleSize[i]; }
+  double getEncodeMin(int i) { return encode[i][0]; }
+  double getEncodeMax(int i) { return encode[i][1]; }
+  double getDecodeMin(int i) { return decode[i][0]; }
+  double getDecodeMax(int i) { return decode[i][1]; }
+  double *getSamples() { return samples; }
+
 private:
 
   SampledFunction(SampledFunction *func);
@@ -103,7 +126,11 @@ private:
     encode[funcMaxInputs][2];
   double                       // min and max values for range decoder
     decode[funcMaxOutputs][2];
+  double                       // input multipliers
+    inputMul[funcMaxInputs];
+  int idxMul[funcMaxInputs];   // sample array index multipliers
   double *samples;             // the samples
+  int nSamples;                        // size of the samples array
   GBool ok;
 };
 
@@ -117,9 +144,14 @@ public:
   ExponentialFunction(Object *funcObj, Dict *dict);
   virtual ~ExponentialFunction();
   virtual Function *copy() { return new ExponentialFunction(this); }
+  virtual int getType() { return 2; }
   virtual void transform(double *in, double *out);
   virtual GBool isOk() { return ok; }
 
+  double *getC0() { return c0; }
+  double *getC1() { return c1; }
+  double getE() { return e; }
+
 private:
 
   ExponentialFunction(ExponentialFunction *func);
@@ -140,9 +172,15 @@ public:
   StitchingFunction(Object *funcObj, Dict *dict);
   virtual ~StitchingFunction();
   virtual Function *copy() { return new StitchingFunction(this); }
+  virtual int getType() { return 3; }
   virtual void transform(double *in, double *out);
   virtual GBool isOk() { return ok; }
 
+  int getNumFuncs() { return k; }
+  Function *getFunc(int i) { return funcs[i]; }
+  double *getBounds() { return bounds; }
+  double *getEncode() { return encode; }
+
 private:
 
   StitchingFunction(StitchingFunction *func);
@@ -164,9 +202,12 @@ public:
   PostScriptFunction(Object *funcObj, Dict *dict);
   virtual ~PostScriptFunction();
   virtual Function *copy() { return new PostScriptFunction(this); }
+  virtual int getType() { return 4; }
   virtual void transform(double *in, double *out);
   virtual GBool isOk() { return ok; }
 
+  GString *getCodeString() { return codeString; }
+
 private:
 
   PostScriptFunction(PostScriptFunction *func);
@@ -175,6 +216,7 @@ private:
   void resizeCode(int newSize);
   void exec(PSStack *stack, int codePtr);
 
+  GString *codeString;
   PSObject *code;
   int codeSize;
   GBool ok;
index 1dd0e26..b51a764 100644 (file)
@@ -39,7 +39,7 @@ GHash::GHash(GBool deleteKeysA) {
 
   deleteKeys = deleteKeysA;
   size = 7;
-  tab = (GHashBucket **)gmalloc(size * sizeof(GHashBucket *));
+  tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
   for (h = 0; h < size; ++h) {
     tab[h] = NULL;
   }
@@ -101,6 +101,30 @@ void GHash::add(GString *key, int val) {
   ++len;
 }
 
+void GHash::replace(GString *key, void *val) {
+  GHashBucket *p;
+  int h;
+
+  if ((p = find(key, &h))) {
+    p->val.p = val;
+    delete key;
+  } else {
+    add(key, val);
+  }
+}
+
+void GHash::replace(GString *key, int val) {
+  GHashBucket *p;
+  int h;
+
+  if ((p = find(key, &h))) {
+    p->val.i = val;
+    delete key;
+  } else {
+    add(key, val);
+  }
+}
+
 void *GHash::lookup(GString *key) {
   GHashBucket *p;
   int h;
@@ -292,7 +316,7 @@ void GHash::expand() {
   oldSize = size;
   oldTab = tab;
   size = 2*size + 1;
-  tab = (GHashBucket **)gmalloc(size * sizeof(GHashBucket *));
+  tab = (GHashBucket **)gmallocn(size, sizeof(GHashBucket *));
   for (h = 0; h < size; ++h) {
     tab[h] = NULL;
   }
index 4a6e08d..31aba93 100644 (file)
@@ -30,6 +30,8 @@ public:
   ~GHash();
   void add(GString *key, void *val);
   void add(GString *key, int val);
+  void replace(GString *key, void *val);
+  void replace(GString *key, int val);
   void *lookup(GString *key);
   int lookupInt(GString *key);
   void *lookup(char *key);
index 9534232..fb5fd62 100644 (file)
@@ -12,6 +12,7 @@
 #pragma implementation
 #endif
 
+#include <stdlib.h>
 #include <string.h>
 #include "gmem.h"
 #include "GList.h"
 
 GList::GList() {
   size = 8;
-  data = (void **)gmalloc(size * sizeof(void*));
+  data = (void **)gmallocn(size, sizeof(void*));
   length = 0;
   inc = 0;
 }
 
 GList::GList(int sizeA) {
   size = sizeA;
-  data = (void **)gmalloc(size * sizeof(void*));
+  data = (void **)gmallocn(size, sizeof(void*));
   length = 0;
   inc = 0;
 }
@@ -81,12 +82,16 @@ void *GList::del(int i) {
   return p;
 }
 
+void GList::sort(int (*cmp)(const void *obj1, const void *obj2)) {
+  qsort(data, length, sizeof(void *), cmp);
+}
+
 void GList::expand() {
   size += (inc > 0) ? inc : size;
-  data = (void **)grealloc(data, size * sizeof(void*));
+  data = (void **)greallocn(data, size, sizeof(void*));
 }
 
 void GList::shrink() {
   size -= (inc > 0) ? inc : size/2;
-  data = (void **)grealloc(data, size * sizeof(void*));
+  data = (void **)greallocn(data, size, sizeof(void*));
 }
index 4c52489..e4d8ff8 100644 (file)
@@ -58,6 +58,11 @@ public:
   // Assumes 0 <= i < length.
   void *del(int i);
 
+  // Sort the list accoring to the given comparison function.
+  // NB: this sorts an array of pointers, so the pointer args need to
+  // be double-dereferenced.
+  void sort(int (*cmp)(const void *ptr1, const void *ptr2));
+
   //----- control
 
   // Set allocation increment to <inc>.  If inc > 0, that many
index 7653fd0..049dcf3 100644 (file)
@@ -35,7 +35,12 @@ inline void GString::resize(int length1) {
     s = new char[size(length1)];
   } else if (size(length1) != size(length)) {
     s1 = new char[size(length1)];
-    memcpy(s1, s, length + 1);
+    if (length1 < length) {
+      memcpy(s1, s, length1);
+      s1[length1] = '\0';
+    } else {
+      memcpy(s1, s, length + 1);
+    }
     delete[] s;
     s = s1;
   }
@@ -234,3 +239,81 @@ GString *GString::lowerCase() {
   }
   return this;
 }
+
+int GString::cmp(GString *str) {
+  int n1, n2, i, x;
+  char *p1, *p2;
+
+  n1 = length;
+  n2 = str->length;
+  for (i = 0, p1 = s, p2 = str->s; i < n1 && i < n2; ++i, ++p1, ++p2) {
+    x = *p1 - *p2;
+    if (x != 0) {
+      return x;
+    }
+  }
+  return n1 - n2;
+}
+
+int GString::cmpN(GString *str, int n) {
+  int n1, n2, i, x;
+  char *p1, *p2;
+
+  n1 = length;
+  n2 = str->length;
+  for (i = 0, p1 = s, p2 = str->s;
+       i < n1 && i < n2 && i < n;
+       ++i, ++p1, ++p2) {
+    x = *p1 - *p2;
+    if (x != 0) {
+      return x;
+    }
+  }
+  if (i == n) {
+    return 0;
+  }
+  return n1 - n2;
+}
+
+int GString::cmp(const char *sA) {
+  int n1, i, x;
+  const char *p1, *p2;
+
+  n1 = length;
+  for (i = 0, p1 = s, p2 = sA; i < n1 && *p2; ++i, ++p1, ++p2) {
+    x = *p1 - *p2;
+    if (x != 0) {
+      return x;
+    }
+  }
+  if (i < n1) {
+    return 1;
+  }
+  if (*p2) {
+    return -1;
+  }
+  return 0;
+}
+
+int GString::cmpN(const char *sA, int n) {
+  int n1, i, x;
+  const char *p1, *p2;
+
+  n1 = length;
+  for (i = 0, p1 = s, p2 = sA; i < n1 && *p2 && i < n; ++i, ++p1, ++p2) {
+    x = *p1 - *p2;
+    if (x != 0) {
+      return x;
+    }
+  }
+  if (i == n) {
+    return 0;
+  }
+  if (i < n1) {
+    return 1;
+  }
+  if (*p2) {
+    return -1;
+  }
+  return 0;
+}
index 2083802..f4ff7c6 100644 (file)
@@ -17,8 +17,6 @@
 #pragma interface
 #endif
 
-#include <string.h>
-
 class GString {
 public:
 
@@ -83,11 +81,10 @@ public:
   GString *lowerCase();
 
   // Compare two strings:  -1:<  0:=  +1:>
-  // These functions assume the strings do not contain null characters.
-  int cmp(GString *str) { return strcmp(s, str->getCString()); }
-  int cmpN(GString *str, int n) { return strncmp(s, str->getCString(), n); }
-  int cmp(const char *sA) { return strcmp(s, sA); }
-  int cmpN(const char *sA, int n) { return strncmp(s, sA, n); }
+  int cmp(GString *str);
+  int cmpN(GString *str, int n);
+  int cmp(const char *sA);
+  int cmpN(const char *sA, int n);
 
 private:
 
index 2e40a57..168206d 100644 (file)
@@ -33,6 +33,9 @@ class GfxShading;
 class GfxFunctionShading;
 class GfxAxialShading;
 class GfxRadialShading;
+class GfxGouraudTriangleShading;
+class GfxPatchMeshShading;
+struct GfxPatch;
 class GfxState;
 struct GfxColor;
 class Gfx;
@@ -101,14 +104,14 @@ public:
 
   // Constructor for regular output.
   Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,
-      double hDPI, double vDPI, PDFRectangle *box, GBool crop,
+      double hDPI, double vDPI, PDFRectangle *box,
       PDFRectangle *cropBox, int rotate,
       GBool (*abortCheckCbkA)(void *data) = NULL,
       void *abortCheckCbkDataA = NULL);
 
   // Constructor for a sub-page object.
   Gfx(XRef *xrefA, OutputDev *outA, Dict *resDict,
-      PDFRectangle *box, GBool crop, PDFRectangle *cropBox,
+      PDFRectangle *box, PDFRectangle *cropBox,
       GBool (*abortCheckCbkA)(void *data) = NULL,
       void *abortCheckCbkDataA = NULL);
 
@@ -128,6 +131,9 @@ public:
   // Restore graphics state.
   void restoreState();
 
+  // Get the current graphics state object.
+  GfxState *getState() { return state; }
+
 private:
 
   XRef *xref;                  // the xref table for this PDF file
@@ -216,6 +222,13 @@ private:
                         GfxColor *colors, int depth);
   void doAxialShFill(GfxAxialShading *shading);
   void doRadialShFill(GfxRadialShading *shading);
+  void doGouraudTriangleShFill(GfxGouraudTriangleShading *shading);
+  void gouraudFillTriangle(double x0, double y0, GfxColor *color0,
+                          double x1, double y1, GfxColor *color1,
+                          double x2, double y2, GfxColor *color2,
+                          int nComps, int depth);
+  void doPatchMeshShFill(GfxPatchMeshShading *shading);
+  void fillPatch(GfxPatch *patch, int nComps, int depth);
   void doEndPath();
 
   // path clipping operators
index ed9f076..a4581c0 100644 (file)
@@ -55,8 +55,8 @@ static StdFontMapEntry stdFontMap[] = {
   { "Arial-ItalicMT",               "Helvetica-Oblique" },
   { "ArialMT",                      "Helvetica" },
   { "Courier,Bold",                 "Courier-Bold" },
-  { "Courier,Italic",               "Courier-Oblique" },
   { "Courier,BoldItalic",           "Courier-BoldOblique" },
+  { "Courier,Italic",               "Courier-Oblique" },
   { "CourierNew",                   "Courier" },
   { "CourierNew,Bold",              "Courier-Bold" },
   { "CourierNew,BoldItalic",        "Courier-BoldOblique" },
@@ -191,19 +191,19 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
 
     // look for embedded font file
     if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) {
-      if (type == fontType1) {
-       embFontID = obj2.getRef();
-      } else {
+      embFontID = obj2.getRef();
+      if (type != fontType1) {
        error(-1, "Mismatch between font type and embedded font file");
+       type = fontType1;
       }
     }
     obj2.free();
     if (embFontID.num == -1 &&
        obj1.dictLookupNF("FontFile2", &obj2)->isRef()) {
-      if (type == fontTrueType || type == fontCIDType2) {
-       embFontID = obj2.getRef();
-      } else {
+      embFontID = obj2.getRef();
+      if (type != fontTrueType && type != fontCIDType2) {
        error(-1, "Mismatch between font type and embedded font file");
+       type = type == fontCIDType0 ? fontCIDType2 : fontTrueType;
       }
     }
     obj2.free();
@@ -212,33 +212,29 @@ void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
       if (obj2.fetch(xref, &obj3)->isStream()) {
        obj3.streamGetDict()->lookup("Subtype", &obj4);
        if (obj4.isName("Type1")) {
-         if (type == fontType1) {
-           embFontID = obj2.getRef();
-         } else {
+         embFontID = obj2.getRef();
+         if (type != fontType1) {
            error(-1, "Mismatch between font type and embedded font file");
+           type = fontType1;
          }
        } else if (obj4.isName("Type1C")) {
-         if (type == fontType1) {
-           type = fontType1C;
-           embFontID = obj2.getRef();
-         } else if (type == fontType1C) {
-           embFontID = obj2.getRef();
-         } else {
+         embFontID = obj2.getRef();
+         if (type != fontType1 && type != fontType1C) {
            error(-1, "Mismatch between font type and embedded font file");
          }
+         type = fontType1C;
        } else if (obj4.isName("TrueType")) {
-         if (type == fontTrueType) {
-           embFontID = obj2.getRef();
-         } else {
+         embFontID = obj2.getRef();
+         if (type != fontTrueType) {
            error(-1, "Mismatch between font type and embedded font file");
+           type = fontTrueType;
          }
        } else if (obj4.isName("CIDFontType0C")) {
-         if (type == fontCIDType0) {
-           type = fontCIDType0C;
-           embFontID = obj2.getRef();
-         } else {
+         embFontID = obj2.getRef();
+         if (type != fontCIDType0) {
            error(-1, "Mismatch between font type and embedded font file");
          }
+         type = fontCIDType0C;
        } else {
          error(-1, "Unknown embedded font type '%s'",
                obj4.isName() ? obj4.getName() : "???");
@@ -399,6 +395,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
                         GfxFontType typeA, Dict *fontDict):
   GfxFont(tagA, idA, nameA)
 {
+  GString *name2;
   BuiltinFont *builtinFont;
   char **baseEnc;
   GBool baseEncFromFontFile;
@@ -424,20 +421,30 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   // do font name substitution for various aliases of the Base 14 font
   // names
   if (name) {
+    name2 = name->copy();
+    i = 0;
+    while (i < name2->getLength()) {
+      if (name2->getChar(i) == ' ') {
+       name2->del(i);
+      } else {
+       ++i;
+      }
+    }
     a = 0;
     b = sizeof(stdFontMap) / sizeof(StdFontMapEntry);
-    // invariant: stdFontMap[a].altName <= name < stdFontMap[b].altName
+    // invariant: stdFontMap[a].altName <= name2 < stdFontMap[b].altName
     while (b - a > 1) {
       m = (a + b) / 2;
-      if (name->cmp(stdFontMap[m].altName) >= 0) {
+      if (name2->cmp(stdFontMap[m].altName) >= 0) {
        a = m;
       } else {
        b = m;
       }
     }
-    if (!name->cmp(stdFontMap[a].altName)) {
+    if (!name2->cmp(stdFontMap[a].altName)) {
       name = new GString(stdFontMap[a].properName);
     }
+    delete name2;
   }
 
   // is it a built-in font?
@@ -468,6 +475,17 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   // get info from font descriptor
   readFontDescriptor(xref, fontDict);
 
+  // for non-embedded fonts, don't trust the ascent/descent/bbox
+  // values from the font descriptor
+  if (builtinFont && embFontID.num < 0) {
+    ascent = 0.001 * builtinFont->ascent;
+    descent = 0.001 * builtinFont->descent;
+    fontBBox[0] = 0.001 * builtinFont->bbox[0];
+    fontBBox[1] = 0.001 * builtinFont->bbox[1];
+    fontBBox[2] = 0.001 * builtinFont->bbox[2];
+    fontBBox[3] = 0.001 * builtinFont->bbox[3];
+  }
+
   // look for an external font file
   findExtFontFile();
 
@@ -513,7 +531,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   //   2. embedded or external font file
   //   3. default:
   //        - builtin --> builtin encoding
-  //        - TrueType --> MacRomanEncoding
+  //        - TrueType --> WinAnsiEncoding
   //        - others --> StandardEncoding
   // and then add a list of differences (if any) from
   // FontDict.Encoding.Differences.
@@ -536,9 +554,6 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     } else if (obj2.isName("WinAnsiEncoding")) {
       hasEncoding = gTrue;
       baseEnc = winAnsiEncoding;
-    } else if (obj2.isName("StandardEncoding")) {
-      hasEncoding = gTrue;
-      baseEnc = standardEncoding;
     }
     obj2.free();
   } else if (obj1.isName("MacRomanEncoding")) {
@@ -551,9 +566,6 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   } else if (obj1.isName("WinAnsiEncoding")) {
     hasEncoding = gTrue;
     baseEnc = winAnsiEncoding;
-  } else if (obj1.isName("StandardEncoding")) {
-    hasEncoding = gTrue;
-    baseEnc = standardEncoding;
   }
 
   // check embedded or external font file for base encoding
@@ -607,7 +619,7 @@ Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
 
   // get default base encoding
   if (!baseEnc) {
-    if (builtinFont) {
+    if (builtinFont && embFontID.num < 0) {
       baseEnc = builtinFont->defaultBaseEnc;
       hasEncoding = gTrue;
     } else if (type == fontTrueType) {
@@ -899,7 +911,7 @@ Gushort *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
   Unicode u;
   int code, i, n;
 
-  map = (Gushort *)gmalloc(256 * sizeof(Gushort));
+  map = (Gushort *)gmallocn(256, sizeof(Gushort));
   for (i = 0; i < 256; ++i) {
     map[i] = 0;
   }
@@ -919,7 +931,8 @@ Gushort *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
   //        directly (possibly with an offset of 0xf000).
   //    1d. If the TrueType font has a Macintosh Roman cmap, use it,
   //        as in case 1a.
-  // 2. If the PDF font does not have an encoding:
+  // 2. If the PDF font does not have an encoding or the PDF font is
+  //    symbolic:
   //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
   //        and use char codes directly (possibly with an offset of
   //        0xf000).
@@ -953,6 +966,8 @@ Gushort *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
       useUnicode = gTrue;
     } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) {
       cmap = msSymbolCmap;
+    } else if ((flags & fontSymbolic) && macRomanCmap >= 0) {
+      cmap = macRomanCmap;
     } else if (macRomanCmap >= 0) {
       cmap = macRomanCmap;
       useMacRoman = gTrue;
@@ -979,7 +994,9 @@ Gushort *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
   // map Unicode through the cmap
   } else if (useUnicode) {
     for (i = 0; i < 256; ++i) {
-      if ((n = ctu->mapToUnicode((CharCode)i, &u, 1))) {
+      if (((charName = enc[i]) &&
+          (u = globalParams->mapNameToUnicode(charName))) ||
+         (n = ctu->mapToUnicode((CharCode)i, &u, 1))) {
        map[i] = ff->mapCodeToGID(cmap, u);
       }
     }
@@ -1043,8 +1060,11 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
   GString *collection, *cMapName;
   Object desFontDictObj;
   Object obj1, obj2, obj3, obj4, obj5, obj6;
+  CharCodeToUnicode *utu;
+  CharCode c;
+  Unicode uBuf[8];
   int c1, c2;
-  int excepsSize, i, j, k;
+  int excepsSize, i, j, k, n;
 
   ascent = 0.95;
   descent = -0.35;
@@ -1126,9 +1146,27 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
       if (!(ctu = globalParams->getCIDToUnicode(collection))) {
        error(-1, "Unknown character collection '%s'",
              collection->getCString());
-       delete collection;
-       goto err2;
+       // fall-through, assuming the Identity mapping -- this appears
+       // to match Adobe's behavior
+      }
+    }
+  }
+
+  // look for a Unicode-to-Unicode mapping
+  if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
+    if (ctu) {
+      for (c = 0; c < ctu->getLength(); ++c) {
+       n = ctu->mapToUnicode(c, uBuf, 8);
+       if (n >= 1) {
+         n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
+         if (n >= 1) {
+           ctu->setMapping(c, uBuf, n);
+         }
+       }
       }
+      utu->decRefCnt();
+    } else {
+      ctu = utu;
     }
   }
 
@@ -1158,13 +1196,13 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
     if (obj1.isStream()) {
       cidToGIDLen = 0;
       i = 64;
-      cidToGID = (Gushort *)gmalloc(i * sizeof(Gushort));
+      cidToGID = (Gushort *)gmallocn(i, sizeof(Gushort));
       obj1.streamReset();
       while ((c1 = obj1.streamGetChar()) != EOF &&
             (c2 = obj1.streamGetChar()) != EOF) {
        if (cidToGIDLen == i) {
          i *= 2;
-         cidToGID = (Gushort *)grealloc(cidToGID, i * sizeof(Gushort));
+         cidToGID = (Gushort *)greallocn(cidToGID, i, sizeof(Gushort));
        }
        cidToGID[cidToGIDLen++] = (Gushort)((c1 << 8) + c2);
       }
@@ -1194,8 +1232,8 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
          if (widths.nExceps == excepsSize) {
            excepsSize += 16;
            widths.exceps = (GfxFontCIDWidthExcep *)
-             grealloc(widths.exceps,
-                      excepsSize * sizeof(GfxFontCIDWidthExcep));
+             greallocn(widths.exceps,
+                       excepsSize, sizeof(GfxFontCIDWidthExcep));
          }
          widths.exceps[widths.nExceps].first = obj2.getInt();
          widths.exceps[widths.nExceps].last = obj3.getInt();
@@ -1210,8 +1248,8 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
        if (widths.nExceps + obj3.arrayGetLength() > excepsSize) {
          excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15;
          widths.exceps = (GfxFontCIDWidthExcep *)
-           grealloc(widths.exceps,
-                    excepsSize * sizeof(GfxFontCIDWidthExcep));
+           greallocn(widths.exceps,
+                     excepsSize, sizeof(GfxFontCIDWidthExcep));
        }
        j = obj2.getInt();
        for (k = 0; k < obj3.arrayGetLength(); ++k) {
@@ -1267,8 +1305,8 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
          if (widths.nExcepsV == excepsSize) {
            excepsSize += 16;
            widths.excepsV = (GfxFontCIDWidthExcepV *)
-             grealloc(widths.excepsV,
-                      excepsSize * sizeof(GfxFontCIDWidthExcepV));
+             greallocn(widths.excepsV,
+                       excepsSize, sizeof(GfxFontCIDWidthExcepV));
          }
          widths.excepsV[widths.nExcepsV].first = obj2.getInt();
          widths.excepsV[widths.nExcepsV].last = obj3.getInt();
@@ -1288,8 +1326,8 @@ GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
          excepsSize =
            (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15;
          widths.excepsV = (GfxFontCIDWidthExcepV *)
-           grealloc(widths.excepsV,
-                    excepsSize * sizeof(GfxFontCIDWidthExcepV));
+           greallocn(widths.excepsV,
+                     excepsSize, sizeof(GfxFontCIDWidthExcepV));
        }
        j = obj2.getInt();
        for (k = 0; k < obj3.arrayGetLength(); k += 3) {
@@ -1452,7 +1490,7 @@ GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) {
   Ref r;
 
   numFonts = fontDict->getLength();
-  fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *));
+  fonts = (GfxFont **)gmallocn(numFonts, sizeof(GfxFont *));
   for (i = 0; i < numFonts; ++i) {
     fontDict->getValNF(i, &obj1);
     obj1.fetch(xref, &obj2);
index f747a83..e1d6801 100644 (file)
@@ -25,21 +25,80 @@ class PDFRectangle;
 class GfxShading;
 
 //------------------------------------------------------------------------
+// GfxBlendMode
+//------------------------------------------------------------------------
+
+enum GfxBlendMode {
+  gfxBlendNormal,
+  gfxBlendMultiply,
+  gfxBlendScreen,
+  gfxBlendOverlay,
+  gfxBlendDarken,
+  gfxBlendLighten,
+  gfxBlendColorDodge,
+  gfxBlendColorBurn,
+  gfxBlendHardLight,
+  gfxBlendSoftLight,
+  gfxBlendDifference,
+  gfxBlendExclusion,
+  gfxBlendHue,
+  gfxBlendSaturation,
+  gfxBlendColor,
+  gfxBlendLuminosity
+};
+
+//------------------------------------------------------------------------
+// GfxColorComp
+//------------------------------------------------------------------------
+
+// 16.16 fixed point color component
+typedef int GfxColorComp;
+
+#define gfxColorComp1 0x10000
+
+static inline GfxColorComp dblToCol(double x) {
+  return (GfxColorComp)(x * gfxColorComp1);
+}
+
+static inline double colToDbl(GfxColorComp x) {
+  return (double)x / (double)gfxColorComp1;
+}
+
+static inline GfxColorComp byteToCol(Guchar x) {
+  // (x / 255) << 16  =  (0.0000000100000001... * x) << 16
+  //                  =  ((x << 8) + (x) + (x >> 8) + ...) << 16
+  //                  =  (x << 8) + (x) + (x >> 7)
+  //                                      [for rounding]
+  return (GfxColorComp)((x << 8) + x + (x >> 7));
+}
+
+static inline Guchar colToByte(GfxColorComp x) {
+  // 255 * x + 0.5  =  256 * x - x + 0x8000
+  return (Guchar)(((x << 8) - x + 0x8000) >> 16);
+}
+
+//------------------------------------------------------------------------
 // GfxColor
 //------------------------------------------------------------------------
 
 #define gfxColorMaxComps funcMaxOutputs
 
 struct GfxColor {
-  double c[gfxColorMaxComps];
+  GfxColorComp c[gfxColorMaxComps];
 };
 
 //------------------------------------------------------------------------
+// GfxGray
+//------------------------------------------------------------------------
+
+typedef GfxColorComp GfxGray;
+
+//------------------------------------------------------------------------
 // GfxRGB
 //------------------------------------------------------------------------
 
 struct GfxRGB {
-  double r, g, b;
+  GfxColorComp r, g, b;
 };
 
 //------------------------------------------------------------------------
@@ -47,7 +106,7 @@ struct GfxRGB {
 //------------------------------------------------------------------------
 
 struct GfxCMYK {
-  double c, m, y, k;
+  GfxColorComp c, m, y, k;
 };
 
 //------------------------------------------------------------------------
@@ -82,7 +141,7 @@ public:
   static GfxColorSpace *parse(Object *csObj);
 
   // Convert to gray, RGB, or CMYK.
-  virtual void getGray(GfxColor *color, double *gray) = 0;
+  virtual void getGray(GfxColor *color, GfxGray *gray) = 0;
   virtual void getRGB(GfxColor *color, GfxRGB *rgb) = 0;
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk) = 0;
 
@@ -115,7 +174,7 @@ public:
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csDeviceGray; }
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -139,7 +198,7 @@ public:
   // Construct a CalGray color space.  Returns NULL if unsuccessful.
   static GfxColorSpace *parse(Array *arr);
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -173,7 +232,7 @@ public:
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csDeviceRGB; }
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -197,7 +256,7 @@ public:
   // Construct a CalRGB color space.  Returns NULL if unsuccessful.
   static GfxColorSpace *parse(Array *arr);
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -220,7 +279,7 @@ private:
   double whiteX, whiteY, whiteZ;    // white point
   double blackX, blackY, blackZ;    // black point
   double gammaR, gammaG, gammaB;    // gamma values
-  double mat[9];               // ABC -> XYZ transform matrix
+  double mat[9];                   // ABC -> XYZ transform matrix
 };
 
 //------------------------------------------------------------------------
@@ -235,7 +294,7 @@ public:
   virtual GfxColorSpace *copy();
   virtual GfxColorSpaceMode getMode() { return csDeviceCMYK; }
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -259,7 +318,7 @@ public:
   // Construct a Lab color space.  Returns NULL if unsuccessful.
   static GfxColorSpace *parse(Array *arr);
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -304,7 +363,7 @@ public:
   // Construct an ICCBased color space.  Returns NULL if unsuccessful.
   static GfxColorSpace *parse(Array *arr);
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -340,7 +399,7 @@ public:
   // Construct a Lab color space.  Returns NULL if unsuccessful.
   static GfxColorSpace *parse(Array *arr);
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -378,7 +437,7 @@ public:
   // Construct a Separation color space.  Returns NULL if unsuccessful.
   static GfxColorSpace *parse(Array *arr);
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -411,7 +470,7 @@ public:
   // Construct a DeviceN color space.  Returns NULL if unsuccessful.
   static GfxColorSpace *parse(Array *arr);
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -446,7 +505,7 @@ public:
   // Construct a Pattern color space.  Returns NULL if unsuccessful.
   static GfxColorSpace *parse(Array *arr);
 
-  virtual void getGray(GfxColor *color, double *gray);
+  virtual void getGray(GfxColor *color, GfxGray *gray);
   virtual void getRGB(GfxColor *color, GfxRGB *rgb);
   virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);
 
@@ -599,6 +658,8 @@ public:
   void getDomain(double *x0A, double *y0A, double *x1A, double *y1A)
     { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
   double *getMatrix() { return matrix; }
+  int getNFuncs() { return nFuncs; }
+  Function *getFunc(int i) { return funcs[i]; }
   void getColor(double x, double y, GfxColor *color);
 
 private:
@@ -632,9 +693,11 @@ public:
     { *x0A = x0; *y0A = y0; *x1A = x1; *y1A = y1; }
   double getDomain0() { return t0; }
   double getDomain1() { return t1; }
-  void getColor(double t, GfxColor *color);
   GBool getExtend0() { return extend0; }
   GBool getExtend1() { return extend1; }
+  int getNFuncs() { return nFuncs; }
+  Function *getFunc(int i) { return funcs[i]; }
+  void getColor(double t, GfxColor *color);
 
 private:
 
@@ -669,9 +732,11 @@ public:
     { *x0A = x0; *y0A = y0; *r0A = r0; *x1A = x1; *y1A = y1; *r1A = r1; }
   double getDomain0() { return t0; }
   double getDomain1() { return t1; }
-  void getColor(double t, GfxColor *color);
   GBool getExtend0() { return extend0; }
   GBool getExtend1() { return extend1; }
+  int getNFuncs() { return nFuncs; }
+  Function *getFunc(int i) { return funcs[i]; }
+  void getColor(double t, GfxColor *color);
 
 private:
 
@@ -683,6 +748,77 @@ private:
 };
 
 //------------------------------------------------------------------------
+// GfxGouraudTriangleShading
+//------------------------------------------------------------------------
+
+struct GfxGouraudVertex {
+  double x, y;
+  GfxColor color;
+};
+
+class GfxGouraudTriangleShading: public GfxShading {
+public:
+
+  GfxGouraudTriangleShading(int typeA,
+                           GfxGouraudVertex *verticesA, int nVerticesA,
+                           int (*trianglesA)[3], int nTrianglesA,
+                           Function **funcsA, int nFuncsA);
+  GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading);
+  virtual ~GfxGouraudTriangleShading();
+
+  static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str);
+
+  virtual GfxShading *copy();
+
+  int getNTriangles() { return nTriangles; }
+  void getTriangle(int i, double *x0, double *y0, GfxColor *color0,
+                  double *x1, double *y1, GfxColor *color1,
+                  double *x2, double *y2, GfxColor *color2);
+
+private:
+
+  GfxGouraudVertex *vertices;
+  int nVertices;
+  int (*triangles)[3];
+  int nTriangles;
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+};
+
+//------------------------------------------------------------------------
+// GfxPatchMeshShading
+//------------------------------------------------------------------------
+
+struct GfxPatch {
+  double x[4][4];
+  double y[4][4];
+  GfxColor color[2][2];
+};
+
+class GfxPatchMeshShading: public GfxShading {
+public:
+
+  GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA,
+                     Function **funcsA, int nFuncsA);
+  GfxPatchMeshShading(GfxPatchMeshShading *shading);
+  virtual ~GfxPatchMeshShading();
+
+  static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str);
+
+  virtual GfxShading *copy();
+
+  int getNPatches() { return nPatches; }
+  GfxPatch *getPatch(int i) { return &patches[i]; }
+
+private:
+
+  GfxPatch *patches;
+  int nPatches;
+  Function *funcs[gfxColorMaxComps];
+  int nFuncs;
+};
+
+//------------------------------------------------------------------------
 // GfxImageColorMap
 //------------------------------------------------------------------------
 
@@ -713,7 +849,7 @@ public:
   double getDecodeHigh(int i) { return decodeLow[i] + decodeRange[i]; }
 
   // Convert an image pixel to a color.
-  void getGray(Guchar *x, double *gray);
+  void getGray(Guchar *x, GfxGray *gray);
   void getRGB(Guchar *x, GfxRGB *rgb);
   void getCMYK(Guchar *x, GfxCMYK *cmyk);
   void getColor(Guchar *x, GfxColor *color);
@@ -727,7 +863,8 @@ private:
   int nComps;                  // number of components in a pixel
   GfxColorSpace *colorSpace2;  // secondary color space
   int nComps2;                 // number of components in colorSpace2
-  double *lookup;              // lookup table
+  GfxColorComp *               // lookup table
+    lookup[gfxColorMaxComps];
   double                       // minimum values for each component
     decodeLow[gfxColorMaxComps];
   double                       // max - min value for each component
@@ -853,10 +990,10 @@ class GfxState {
 public:
 
   // Construct a default GfxState, for a device with resolution <hDPI>
-  // x <vDPI>, page box <pageBox>, page rotation <rotate>, and
+  // x <vDPI>, page box <pageBox>, page rotation <rotateA>, and
   // coordinate system specified by <upsideDown>.
   GfxState(double hDPI, double vDPI, PDFRectangle *pageBox,
-          int rotate, GBool upsideDown);
+          int rotateA, GBool upsideDown);
 
   // Destructor.
   ~GfxState();
@@ -872,11 +1009,12 @@ public:
   double getY2() { return py2; }
   double getPageWidth() { return pageWidth; }
   double getPageHeight() { return pageHeight; }
+  int getRotate() { return rotate; }
   GfxColor *getFillColor() { return &fillColor; }
   GfxColor *getStrokeColor() { return &strokeColor; }
-  void getFillGray(double *gray)
+  void getFillGray(GfxGray *gray)
     { fillColorSpace->getGray(&fillColor, gray); }
-  void getStrokeGray(double *gray)
+  void getStrokeGray(GfxGray *gray)
     { strokeColorSpace->getGray(&strokeColor, gray); }
   void getFillRGB(GfxRGB *rgb)
     { fillColorSpace->getRGB(&fillColor, rgb); }
@@ -890,8 +1028,11 @@ public:
   GfxColorSpace *getStrokeColorSpace() { return strokeColorSpace; }
   GfxPattern *getFillPattern() { return fillPattern; }
   GfxPattern *getStrokePattern() { return strokePattern; }
+  GfxBlendMode getBlendMode() { return blendMode; }
   double getFillOpacity() { return fillOpacity; }
   double getStrokeOpacity() { return strokeOpacity; }
+  GBool getFillOverprint() { return fillOverprint; }
+  GBool getStrokeOverprint() { return strokeOverprint; }
   double getLineWidth() { return lineWidth; }
   void getLineDash(double **dash, int *length, double *start)
     { *dash = lineDash; *length = lineDashLength; *start = lineDashStart; }
@@ -952,8 +1093,11 @@ public:
   void setStrokeColor(GfxColor *color) { strokeColor = *color; }
   void setFillPattern(GfxPattern *pattern);
   void setStrokePattern(GfxPattern *pattern);
+  void setBlendMode(GfxBlendMode mode) { blendMode = mode; }
   void setFillOpacity(double opac) { fillOpacity = opac; }
   void setStrokeOpacity(double opac) { strokeOpacity = opac; }
+  void setFillOverprint(GBool op) { fillOverprint = op; }
+  void setStrokeOverprint(GBool op) { strokeOverprint = op; }
   void setLineWidth(double width) { lineWidth = width; }
   void setLineDash(double *dash, int length, double start);
   void setFlatness(int flatness1) { flatness = flatness1; }
@@ -1006,11 +1150,15 @@ public:
   GfxState *restore();
   GBool hasSaves() { return saved != NULL; }
 
+  // Misc
+  GBool parseBlendMode(Object *obj, GfxBlendMode *mode);
+
 private:
 
   double ctm[6];               // coord transform matrix
   double px1, py1, px2, py2;   // page corners (user coords)
   double pageWidth, pageHeight;        // page size (pixels)
+  int rotate;                  // page rotation angle
 
   GfxColorSpace *fillColorSpace;   // fill color space
   GfxColorSpace *strokeColorSpace; // stroke color space
@@ -1018,8 +1166,11 @@ private:
   GfxColor strokeColor;                // stroke color
   GfxPattern *fillPattern;     // fill pattern
   GfxPattern *strokePattern;   // stroke pattern
+  GfxBlendMode blendMode;      // transparency blend mode
   double fillOpacity;          // fill opacity
   double strokeOpacity;                // stroke opacity
+  GBool fillOverprint;         // fill overprint
+  GBool strokeOverprint;       // stroke overprint
 
   double lineWidth;            // line width
   double *lineDash;            // line dash
index fd29744..195b73e 100644 (file)
@@ -22,7 +22,7 @@
 
 JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) {
   contextSize = contextSizeA;
-  cxTab = (Guchar *)gmalloc(contextSize * sizeof(Guchar));
+  cxTab = (Guchar *)gmallocn(contextSize, sizeof(Guchar));
   reset();
 }
 
@@ -89,24 +89,24 @@ int JArithmeticDecoder::switchTab[47] = {
 
 JArithmeticDecoder::JArithmeticDecoder() {
   str = NULL;
-}
-
-JArithmeticDecoder::~JArithmeticDecoder() {
-  while (dataLen > 0) {
-    readByte();
-  }
+  dataLen = 0;
+  limitStream = gFalse;
 }
 
 inline Guint JArithmeticDecoder::readByte() {
-  if (dataLen == 0) {
-    return 0xff;
-  }
-  if (dataLen > 0) {
+  if (limitStream) {
     --dataLen;
+    if (dataLen < 0) {
+      return 0xff;
+    }
   }
   return (Guint)str->getChar() & 0xff;
 }
 
+JArithmeticDecoder::~JArithmeticDecoder() {
+  cleanup();
+}
+
 void JArithmeticDecoder::start() {
   buf0 = readByte();
   buf1 = readByte();
@@ -119,6 +119,28 @@ void JArithmeticDecoder::start() {
   a = 0x80000000;
 }
 
+void JArithmeticDecoder::restart(int dataLenA) {
+  int oldDataLen;
+
+  oldDataLen = dataLen;
+  dataLen = dataLenA;
+  if (oldDataLen == -1) {
+    buf1 = readByte();
+  } else if (oldDataLen <= -2) {
+    buf0 = readByte();
+    buf1 = readByte();
+  }
+}
+
+void JArithmeticDecoder::cleanup() {
+  if (limitStream) {
+    while (dataLen > 0) {
+      buf0 = buf1;
+      buf1 = readByte();
+    }
+  }
+}
+
 int JArithmeticDecoder::decodeBit(Guint context,
                                  JArithmeticDecoderStats *stats) {
   int bit;
index a348017..a40823d 100644 (file)
@@ -53,12 +53,29 @@ public:
 
   JArithmeticDecoder();
   ~JArithmeticDecoder();
+
   void setStream(Stream *strA)
-    { str = strA; dataLen = -1; }
+    { str = strA; dataLen = 0; limitStream = gFalse; }
   void setStream(Stream *strA, int dataLenA)
-    { str = strA; dataLen = dataLenA; }
+    { str = strA; dataLen = dataLenA; limitStream = gTrue; }
+
+  // Start decoding on a new stream.  This fills the byte buffers and
+  // runs INITDEC.
   void start();
+
+  // Restart decoding on an interrupted stream.  This refills the
+  // buffers if needed, but does not run INITDEC.  (This is used in
+  // JPEG 2000 streams when codeblock data is split across multiple
+  // packets/layers.)
+  void restart(int dataLenA);
+
+  // Read any leftover data in the stream.
+  void cleanup();
+
+  // Decode one bit.
   int decodeBit(Guint context, JArithmeticDecoderStats *stats);
+
+  // Decode eight bits.
   int decodeByte(Guint context, JArithmeticDecoderStats *stats);
 
   // Returns false for OOB, otherwise sets *<x> and returns true.
@@ -86,6 +103,7 @@ private:
 
   Stream *str;
   int dataLen;
+  GBool limitStream;
 };
 
 #endif
index c1bf4f7..a90db80 100644 (file)
@@ -681,7 +681,9 @@ JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, int wA, int hA):
   w = wA;
   h = hA;
   line = (wA + 7) >> 3;
-  data = (Guchar *)gmalloc(h * line);
+  // need to allocate one extra guard byte for use in combine()
+  data = (Guchar *)gmalloc(h * line + 1);
+  data[h * line] = 0;
 }
 
 JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap):
@@ -690,8 +692,10 @@ JBIG2Bitmap::JBIG2Bitmap(Guint segNumA, JBIG2Bitmap *bitmap):
   w = bitmap->w;
   h = bitmap->h;
   line = bitmap->line;
-  data = (Guchar *)gmalloc(h * line);
+  // need to allocate one extra guard byte for use in combine()
+  data = (Guchar *)gmalloc(h * line + 1);
   memcpy(data, bitmap->data, h * line);
+  data[h * line] = 0;
 }
 
 JBIG2Bitmap::~JBIG2Bitmap() {
@@ -719,13 +723,15 @@ void JBIG2Bitmap::expand(int newH, Guint pixel) {
   if (newH <= h) {
     return;
   }
-  data = (Guchar *)grealloc(data, newH * line);
+  // need to allocate one extra guard byte for use in combine()
+  data = (Guchar *)grealloc(data, newH * line + 1);
   if (pixel) {
     memset(data + h * line, 0xff, (newH - h) * line);
   } else {
     memset(data + h * line, 0x00, (newH - h) * line);
   }
   h = newH;
+  data[h * line] = 0;
 }
 
 void JBIG2Bitmap::clearToZero() {
@@ -933,6 +939,10 @@ void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y,
       }
 
       // right-most byte
+      // note: this last byte (src1) may not actually be used, depending
+      // on the values of s1, m1, and m2 - and in fact, it may be off
+      // the edge of the source bitmap, which means we need to allocate
+      // one extra guard byte at the end of each bitmap
       dest = *destPtr;
       src0 = src1;
       src1 = *srcPtr++;
@@ -993,7 +1003,7 @@ JBIG2SymbolDict::JBIG2SymbolDict(Guint segNumA, Guint sizeA):
   JBIG2Segment(segNumA)
 {
   size = sizeA;
-  bitmaps = (JBIG2Bitmap **)gmalloc(size * sizeof(JBIG2Bitmap *));
+  bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
   genericRegionStats = NULL;
   refinementRegionStats = NULL;
 }
@@ -1037,7 +1047,7 @@ JBIG2PatternDict::JBIG2PatternDict(Guint segNumA, Guint sizeA):
   JBIG2Segment(segNumA)
 {
   size = sizeA;
-  bitmaps = (JBIG2Bitmap **)gmalloc(size * sizeof(JBIG2Bitmap *));
+  bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));
 }
 
 JBIG2PatternDict::~JBIG2PatternDict() {
@@ -1233,7 +1243,7 @@ void JBIG2Stream::readSegments() {
     }
 
     // referred-to segment numbers
-    refSegs = (Guint *)gmalloc(nRefSegs * sizeof(Guint));
+    refSegs = (Guint *)gmallocn(nRefSegs, sizeof(Guint));
     if (segNum <= 256) {
       for (i = 0; i < nRefSegs; ++i) {
        if (!readUByte(&refSegs[i])) {
@@ -1273,7 +1283,9 @@ void JBIG2Stream::readSegments() {
     // read the segment data
     switch (segType) {
     case 0:
-      readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs);
+      if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) {
+       goto syntaxError;
+      }
       break;
     case 4:
       readTextRegionSeg(segNum, gFalse, gFalse, segLength, refSegs, nRefSegs);
@@ -1350,14 +1362,18 @@ void JBIG2Stream::readSegments() {
 
   return;
 
+ syntaxError:
+  gfree(refSegs);
+  return;
+
  eofError2:
   gfree(refSegs);
  eofError1:
   error(getPos(), "Unexpected EOF in JBIG2 stream");
 }
 
-void JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
-                                   Guint *refSegs, Guint nRefSegs) {
+GBool JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
+                                    Guint *refSegs, Guint nRefSegs) {
   JBIG2SymbolDict *symbolDict;
   JBIG2HuffmanTable *huffDHTable, *huffDWTable;
   JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable;
@@ -1451,8 +1467,11 @@ void JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
   }
 
   // get the input symbol bitmaps
-  bitmaps = (JBIG2Bitmap **)gmalloc((numInputSyms + numNewSyms) *
-                                   sizeof(JBIG2Bitmap *));
+  bitmaps = (JBIG2Bitmap **)gmallocn(numInputSyms + numNewSyms,
+                                    sizeof(JBIG2Bitmap *));
+  for (i = 0; i < numInputSyms + numNewSyms; ++i) {
+    bitmaps[i] = NULL;
+  }
   k = 0;
   inputSymbolDict = NULL;
   for (i = 0; i < nRefSegs; ++i) {
@@ -1527,7 +1546,7 @@ void JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
   // allocate symbol widths storage
   symWidths = NULL;
   if (huff && !refAgg) {
-    symWidths = (Guint *)gmalloc(numNewSyms * sizeof(Guint));
+    symWidths = (Guint *)gmallocn(numNewSyms, sizeof(Guint));
   }
 
   symHeight = 0;
@@ -1540,6 +1559,10 @@ void JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
     } else {
       arithDecoder->decodeInt(&dh, iadhStats);
     }
+    if (dh < 0 && (Guint)-dh >= symHeight) {
+      error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
+      goto syntaxError;
+    }
     symHeight += dh;
     symWidth = 0;
     totalWidth = 0;
@@ -1558,6 +1581,10 @@ void JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
          break;
        }
       }
+      if (dw < 0 && (Guint)-dw >= symWidth) {
+       error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");
+       goto syntaxError;
+      }
       symWidth += dw;
 
       // using a collective bitmap, so don't read a bitmap here
@@ -1690,10 +1717,23 @@ void JBIG2Stream::readSymbolDictSeg(Guint segNum, Guint length,
   // store the new symbol dict
   segments->append(symbolDict);
 
-  return;
+  return gTrue;
+
+ syntaxError:
+  for (i = 0; i < numNewSyms; ++i) {
+    if (bitmaps[numInputSyms + i]) {
+      delete bitmaps[numInputSyms + i];
+    }
+  }
+  gfree(bitmaps);
+  if (symWidths) {
+    gfree(symWidths);
+  }
+  return gFalse;
 
  eofError:
   error(getPos(), "Unexpected EOF in JBIG2 stream");
+  return gFalse;
 }
 
 void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
@@ -1773,11 +1813,14 @@ void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
   codeTables = new GList();
   numSyms = 0;
   for (i = 0; i < nRefSegs; ++i) {
-    seg = findSegment(refSegs[i]);
-    if (seg->getType() == jbig2SegSymbolDict) {
-      numSyms += ((JBIG2SymbolDict *)seg)->getSize();
-    } else if (seg->getType() == jbig2SegCodeTable) {
-      codeTables->append(seg);
+    if ((seg = findSegment(refSegs[i]))) {
+      if (seg->getType() == jbig2SegSymbolDict) {
+       numSyms += ((JBIG2SymbolDict *)seg)->getSize();
+      } else if (seg->getType() == jbig2SegCodeTable) {
+       codeTables->append(seg);
+      }
+    } else {
+      error(getPos(), "Invalid segment reference in JBIG2 text region");
     }
   }
   symCodeLen = 0;
@@ -1788,14 +1831,15 @@ void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
   }
 
   // get the symbol bitmaps
-  syms = (JBIG2Bitmap **)gmalloc(numSyms * sizeof(JBIG2Bitmap *));
+  syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *));
   kk = 0;
   for (i = 0; i < nRefSegs; ++i) {
-    seg = findSegment(refSegs[i]);
-    if (seg->getType() == jbig2SegSymbolDict) {
-      symbolDict = (JBIG2SymbolDict *)seg;
-      for (k = 0; k < symbolDict->getSize(); ++k) {
-       syms[kk++] = symbolDict->getBitmap(k);
+    if ((seg = findSegment(refSegs[i]))) {
+      if (seg->getType() == jbig2SegSymbolDict) {
+       symbolDict = (JBIG2SymbolDict *)seg;
+       for (k = 0; k < symbolDict->getSize(); ++k) {
+         syms[kk++] = symbolDict->getBitmap(k);
+       }
       }
     }
   }
@@ -1888,8 +1932,8 @@ void JBIG2Stream::readTextRegionSeg(Guint segNum, GBool imm,
     runLengthTab[35].prefixLen = 0;
     runLengthTab[35].rangeLen = jbig2HuffmanEOT;
     huffDecoder->buildTable(runLengthTab, 35);
-    symCodeTab = (JBIG2HuffmanTable *)gmalloc((numSyms + 1) *
-                                             sizeof(JBIG2HuffmanTable));
+    symCodeTab = (JBIG2HuffmanTable *)gmallocn(numSyms + 1,
+                                              sizeof(JBIG2HuffmanTable));
     for (i = 0; i < numSyms; ++i) {
       symCodeTab[i].val = i;
       symCodeTab[i].rangeLen = 0;
@@ -2052,85 +2096,90 @@ JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine,
        symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);
       }
 
-      // get the symbol bitmap
-      symbolBitmap = NULL;
-      if (refine) {
-       if (huff) {
-         ri = (int)huffDecoder->readBit();
+      if (symID >= (Guint)numSyms) {
+       error(getPos(), "Invalid symbol number in JBIG2 text region");
+      } else {
+
+       // get the symbol bitmap
+       symbolBitmap = NULL;
+       if (refine) {
+         if (huff) {
+           ri = (int)huffDecoder->readBit();
+         } else {
+           arithDecoder->decodeInt(&ri, iariStats);
+         }
        } else {
-         arithDecoder->decodeInt(&ri, iariStats);
+         ri = 0;
        }
-      } else {
-       ri = 0;
-      }
-      if (ri) {
-       if (huff) {
-         huffDecoder->decodeInt(&rdw, huffRDWTable);
-         huffDecoder->decodeInt(&rdh, huffRDHTable);
-         huffDecoder->decodeInt(&rdx, huffRDXTable);
-         huffDecoder->decodeInt(&rdy, huffRDYTable);
-         huffDecoder->decodeInt(&bmSize, huffRSizeTable);
-         huffDecoder->reset();
-         arithDecoder->start();
+       if (ri) {
+         if (huff) {
+           huffDecoder->decodeInt(&rdw, huffRDWTable);
+           huffDecoder->decodeInt(&rdh, huffRDHTable);
+           huffDecoder->decodeInt(&rdx, huffRDXTable);
+           huffDecoder->decodeInt(&rdy, huffRDYTable);
+           huffDecoder->decodeInt(&bmSize, huffRSizeTable);
+           huffDecoder->reset();
+           arithDecoder->start();
+         } else {
+           arithDecoder->decodeInt(&rdw, iardwStats);
+           arithDecoder->decodeInt(&rdh, iardhStats);
+           arithDecoder->decodeInt(&rdx, iardxStats);
+           arithDecoder->decodeInt(&rdy, iardyStats);
+         }
+         refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx;
+         refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy;
+
+         symbolBitmap =
+           readGenericRefinementRegion(rdw + syms[symID]->getWidth(),
+                                       rdh + syms[symID]->getHeight(),
+                                       templ, gFalse, syms[symID],
+                                       refDX, refDY, atx, aty);
+         //~ do we need to use the bmSize value here (in Huffman mode)?
        } else {
-         arithDecoder->decodeInt(&rdw, iardwStats);
-         arithDecoder->decodeInt(&rdh, iardhStats);
-         arithDecoder->decodeInt(&rdx, iardxStats);
-         arithDecoder->decodeInt(&rdy, iardyStats);
+         symbolBitmap = syms[symID];
        }
-       refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx;
-       refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy;
-
-       symbolBitmap =
-         readGenericRefinementRegion(rdw + syms[symID]->getWidth(),
-                                     rdh + syms[symID]->getHeight(),
-                                     templ, gFalse, syms[symID],
-                                     refDX, refDY, atx, aty);
-       //~ do we need to use the bmSize value here (in Huffman mode)?
-      } else {
-       symbolBitmap = syms[symID];
-      }
 
-      // combine the symbol bitmap into the region bitmap
-      //~ something is wrong here - refCorner shouldn't degenerate into
-      //~   two cases
-      bw = symbolBitmap->getWidth() - 1;
-      bh = symbolBitmap->getHeight() - 1;
-      if (transposed) {
-       switch (refCorner) {
-       case 0: // bottom left
-         bitmap->combine(symbolBitmap, tt, s, combOp);
-         break;
-       case 1: // top left
-         bitmap->combine(symbolBitmap, tt, s, combOp);
-         break;
-       case 2: // bottom right
-         bitmap->combine(symbolBitmap, tt - bw, s, combOp);
-         break;
-       case 3: // top right
-         bitmap->combine(symbolBitmap, tt - bw, s, combOp);
-         break;
+       // combine the symbol bitmap into the region bitmap
+       //~ something is wrong here - refCorner shouldn't degenerate into
+       //~   two cases
+       bw = symbolBitmap->getWidth() - 1;
+       bh = symbolBitmap->getHeight() - 1;
+       if (transposed) {
+         switch (refCorner) {
+         case 0: // bottom left
+           bitmap->combine(symbolBitmap, tt, s, combOp);
+           break;
+         case 1: // top left
+           bitmap->combine(symbolBitmap, tt, s, combOp);
+           break;
+         case 2: // bottom right
+           bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+           break;
+         case 3: // top right
+           bitmap->combine(symbolBitmap, tt - bw, s, combOp);
+           break;
+         }
+         s += bh;
+       } else {
+         switch (refCorner) {
+         case 0: // bottom left
+           bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+           break;
+         case 1: // top left
+           bitmap->combine(symbolBitmap, s, tt, combOp);
+           break;
+         case 2: // bottom right
+           bitmap->combine(symbolBitmap, s, tt - bh, combOp);
+           break;
+         case 3: // top right
+           bitmap->combine(symbolBitmap, s, tt, combOp);
+           break;
+         }
+         s += bw;
        }
-       s += bh;
-      } else {
-       switch (refCorner) {
-       case 0: // bottom left
-         bitmap->combine(symbolBitmap, s, tt - bh, combOp);
-         break;
-       case 1: // top left
-         bitmap->combine(symbolBitmap, s, tt, combOp);
-         break;
-       case 2: // bottom right
-         bitmap->combine(symbolBitmap, s, tt - bh, combOp);
-         break;
-       case 3: // top right
-         bitmap->combine(symbolBitmap, s, tt, combOp);
-         break;
+       if (ri) {
+         delete symbolBitmap;
        }
-       s += bw;
-      }
-      if (ri) {
-       delete symbolBitmap;
       }
 
       // next instance
@@ -2298,7 +2347,7 @@ void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm,
   }
 
   // read the gray-scale image
-  grayImg = (Guint *)gmalloc(gridW * gridH * sizeof(Guint));
+  grayImg = (Guint *)gmallocn(gridW * gridH, sizeof(Guint));
   memset(grayImg, 0, gridW * gridH * sizeof(Guint));
   atx[0] = templ <= 1 ? 3 : 2;  aty[0] = -1;
   atx[1] = -3;                  aty[1] = -1;
@@ -2451,8 +2500,8 @@ JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,
   if (mmr) {
 
     mmrDecoder->reset();
-    refLine = (int *)gmalloc((w + 2) * sizeof(int));
-    codingLine = (int *)gmalloc((w + 2) * sizeof(int));
+    refLine = (int *)gmallocn(w + 2, sizeof(int));
+    codingLine = (int *)gmallocn(w + 2, sizeof(int));
     codingLine[0] = codingLine[1] = w;
 
     for (y = 0; y < h; ++y) {
@@ -3111,14 +3160,14 @@ void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
   huffDecoder->reset();
   huffTabSize = 8;
   huffTab = (JBIG2HuffmanTable *)
-                gmalloc(huffTabSize * sizeof(JBIG2HuffmanTable));
+                gmallocn(huffTabSize, sizeof(JBIG2HuffmanTable));
   i = 0;
   val = lowVal;
   while (val < highVal) {
     if (i == huffTabSize) {
       huffTabSize *= 2;
       huffTab = (JBIG2HuffmanTable *)
-                   grealloc(huffTab, huffTabSize * sizeof(JBIG2HuffmanTable));
+                   greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
     }
     huffTab[i].val = val;
     huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
@@ -3129,7 +3178,7 @@ void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {
   if (i + oob + 3 > huffTabSize) {
     huffTabSize = i + oob + 3;
     huffTab = (JBIG2HuffmanTable *)
-                  grealloc(huffTab, huffTabSize * sizeof(JBIG2HuffmanTable));
+                  greallocn(huffTab, huffTabSize, sizeof(JBIG2HuffmanTable));
   }
   huffTab[i].val = lowVal - 1;
   huffTab[i].prefixLen = huffDecoder->readBits(prefixBits);
index ed26d4e..44e09db 100644 (file)
@@ -45,8 +45,8 @@ public:
 private:
 
   void readSegments();
-  void readSymbolDictSeg(Guint segNum, Guint length,
-                        Guint *refSegs, Guint nRefSegs);
+  GBool readSymbolDictSeg(Guint segNum, Guint length,
+                         Guint *refSegs, Guint nRefSegs);
   void readTextRegionSeg(Guint segNum, GBool imm,
                         GBool lossless, Guint length,
                         Guint *refSegs, Guint nRefSegs);
index defa7d2..336b7ba 100644 (file)
@@ -243,6 +243,9 @@ JPXStream::~JPXStream() {
                        for (k = 0; k < subband->nXCBs * subband->nYCBs; ++k) {
                          cb = &subband->cbs[k];
                          gfree(cb->coeffs);
+                         if (cb->arithDecoder) {
+                           delete cb->arithDecoder;
+                         }
                          if (cb->stats) {
                            delete cb->stats;
                          }
@@ -374,6 +377,122 @@ GBool JPXStream::isBinary(GBool last) {
   return str->isBinary(gTrue);
 }
 
+void JPXStream::getImageParams(int *bitsPerComponent,
+                              StreamColorSpaceMode *csMode) {
+  Guint boxType, boxLen, dataLen, csEnum;
+  Guint bpc1, dummy, i;
+  int csMeth, csPrec, csPrec1, dummy2;
+  StreamColorSpaceMode csMode1;
+  GBool haveBPC, haveCSMode;
+
+  csPrec = 0; // make gcc happy
+  haveBPC = haveCSMode = gFalse;
+  str->reset();
+  if (str->lookChar() == 0xff) {
+    getImageParams2(bitsPerComponent, csMode);
+  } else {
+    while (readBoxHdr(&boxType, &boxLen, &dataLen)) {
+      if (boxType == 0x6a703268) { // JP2 header
+       // skip the superbox
+      } else if (boxType == 0x69686472) { // image header
+       if (readULong(&dummy) &&
+           readULong(&dummy) &&
+           readUWord(&dummy) &&
+           readUByte(&bpc1) &&
+           readUByte(&dummy) &&
+           readUByte(&dummy) &&
+           readUByte(&dummy)) {
+         *bitsPerComponent = bpc1 + 1;
+         haveBPC = gTrue;
+       }
+      } else if (boxType == 0x636F6C72) { // color specification
+       if (readByte(&csMeth) &&
+           readByte(&csPrec1) &&
+           readByte(&dummy2)) {
+         if (csMeth == 1) {
+           if (readULong(&csEnum)) {
+             csMode1 = streamCSNone;
+             if (csEnum == jpxCSBiLevel ||
+                 csEnum == jpxCSGrayscale) {
+               csMode1 = streamCSDeviceGray;
+             } else if (csEnum == jpxCSCMYK) {
+               csMode1 = streamCSDeviceCMYK;
+             } else if (csEnum == jpxCSsRGB ||
+                        csEnum == jpxCSCISesRGB ||
+                        csEnum == jpxCSROMMRGB) {
+               csMode1 = streamCSDeviceRGB;
+             }
+             if (csMode1 != streamCSNone &&
+                 (!haveCSMode || csPrec1 > csPrec)) {
+               *csMode = csMode1;
+               csPrec = csPrec1;
+               haveCSMode = gTrue;
+             }
+             for (i = 0; i < dataLen - 7; ++i) {
+               str->getChar();
+             }
+           }
+         } else {
+           for (i = 0; i < dataLen - 3; ++i) {
+             str->getChar();
+           }
+         }
+       }
+      } else if (boxType == 0x6A703263) { // codestream
+       if (!(haveBPC && haveCSMode)) {
+         getImageParams2(bitsPerComponent, csMode);
+       }
+       break;
+      } else {
+       for (i = 0; i < dataLen; ++i) {
+         str->getChar();
+       }
+      }
+    }
+  }
+  str->close();
+}
+
+// Get image parameters from the codestream.
+void JPXStream::getImageParams2(int *bitsPerComponent,
+                               StreamColorSpaceMode *csMode) {
+  int segType;
+  Guint segLen, nComps1, bpc1, dummy, i;
+
+  while (readMarkerHdr(&segType, &segLen)) {
+    if (segType == 0x51) { // SIZ - image and tile size
+      if (readUWord(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readULong(&dummy) &&
+         readUWord(&nComps1) &&
+         readUByte(&bpc1)) {
+       *bitsPerComponent = (bpc1 & 0x7f) + 1;
+       // if there's no color space info, take a guess
+       if (nComps1 == 1) {
+         *csMode = streamCSDeviceGray;
+       } else if (nComps1 == 3) {
+         *csMode = streamCSDeviceRGB;
+       } else if (nComps1 == 4) {
+         *csMode = streamCSDeviceCMYK;
+       }
+      }
+      break;
+    } else {
+      if (segLen > 2) {
+       for (i = 0; i < segLen - 2; ++i) {
+         str->getChar();
+       }
+      }
+    }
+  }
+}
+
 GBool JPXStream::readBoxes() {
   Guint boxType, boxLen, dataLen;
   Guint bpc1, compression, unknownColorspace, ipr;
@@ -388,7 +507,7 @@ GBool JPXStream::readBoxes() {
     error(getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper");
     readCodestream(0);
     nComps = img.nComps;
-    bpc = (Guint *)gmalloc(nComps * sizeof(Guint));
+    bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
     for (i = 0; i < nComps; ++i) {
       bpc[i] = img.tiles[0].tileComps[i].prec;
     }
@@ -421,7 +540,7 @@ GBool JPXStream::readBoxes() {
        error(getPos(), "Unknown compression type in JPX stream");
        return gFalse;
       }
-      bpc = (Guint *)gmalloc(nComps * sizeof(Guint));
+      bpc = (Guint *)gmallocn(nComps, sizeof(Guint));
       for (i = 0; i < nComps; ++i) {
        bpc[i] = bpc1;
       }
@@ -454,9 +573,9 @@ GBool JPXStream::readBoxes() {
        error(getPos(), "Unexpected EOF in JPX stream");
        return gFalse;
       }
-      palette.bpc = (Guint *)gmalloc(palette.nComps * sizeof(Guint));
+      palette.bpc = (Guint *)gmallocn(palette.nComps, sizeof(Guint));
       palette.c =
-          (int *)gmalloc(palette.nEntries * palette.nComps * sizeof(int));
+          (int *)gmallocn(palette.nEntries * palette.nComps, sizeof(int));
       for (i = 0; i < palette.nComps; ++i) {
        if (!readUByte(&palette.bpc[i])) {
          error(getPos(), "Unexpected EOF in JPX stream");
@@ -478,9 +597,9 @@ GBool JPXStream::readBoxes() {
       break;
     case 0x636d6170:           // component mapping
       compMap.nChannels = dataLen / 4;
-      compMap.comp = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint));
-      compMap.type = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint));
-      compMap.pComp = (Guint *)gmalloc(compMap.nChannels * sizeof(Guint));
+      compMap.comp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+      compMap.type = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
+      compMap.pComp = (Guint *)gmallocn(compMap.nChannels, sizeof(Guint));
       for (i = 0; i < compMap.nChannels; ++i) {
        if (!readUWord(&compMap.comp[i]) ||
            !readUByte(&compMap.type[i]) ||
@@ -497,11 +616,11 @@ GBool JPXStream::readBoxes() {
        return gFalse;
       }
       channelDefn.idx =
-         (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint));
+         (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
       channelDefn.type =
-         (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint));
+         (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
       channelDefn.assoc =
-         (Guint *)gmalloc(channelDefn.nChannels * sizeof(Guint));
+         (Guint *)gmallocn(channelDefn.nChannels, sizeof(Guint));
       for (i = 0; i < channelDefn.nChannels; ++i) {
        if (!readUWord(&channelDefn.idx[i]) ||
            !readUWord(&channelDefn.type[i]) ||
@@ -515,11 +634,9 @@ GBool JPXStream::readBoxes() {
     case 0x6A703263:           // contiguous codestream
       if (!bpc) {
        error(getPos(), "JPX stream is missing the image header box");
-       return gFalse;
       }
       if (!haveCS) {
        error(getPos(), "JPX stream has no supported color spec");
-       return gFalse;
       }
       if (!readCodestream(dataLen)) {
        return gFalse;
@@ -582,7 +699,7 @@ GBool JPXStream::readColorSpecBox(Guint dataLen) {
       ok = gTrue;
       break;
     case jpxCSCIELab:
-      if (dataLen == 3 + 7*4) {
+      if (dataLen == 7 + 7*4) {
        if (!readULong(&newCS.enumerated.cieLab.rl) ||
            !readULong(&newCS.enumerated.cieLab.ol) ||
            !readULong(&newCS.enumerated.cieLab.ra) ||
@@ -592,7 +709,7 @@ GBool JPXStream::readColorSpecBox(Guint dataLen) {
            !readULong(&newCS.enumerated.cieLab.il)) {
          goto err;
        }
-      } else if (dataLen == 3) {
+      } else if (dataLen == 7) {
        //~ this assumes the 8-bit case
        newCS.enumerated.cieLab.rl = 100;
        newCS.enumerated.cieLab.ol = 0;
@@ -666,7 +783,7 @@ GBool JPXStream::readCodestream(Guint len) {
   int segType;
   GBool haveSIZ, haveCOD, haveQCD, haveSOT;
   Guint precinctSize, style;
-  Guint segLen, capabilities, comp, i, j, r;
+  Guint segLen, capabilities, nTiles, comp, i, j, r;
 
   //----- main header
   haveSIZ = haveCOD = haveQCD = haveSOT = gFalse;
@@ -701,11 +818,16 @@ GBool JPXStream::readCodestream(Guint len) {
                    / img.xTileSize;
       img.nYTiles = (img.ySize - img.yTileOffset + img.yTileSize - 1)
                    / img.yTileSize;
-      img.tiles = (JPXTile *)gmalloc(img.nXTiles * img.nYTiles *
-                                    sizeof(JPXTile));
+      nTiles = img.nXTiles * img.nYTiles;
+      // check for overflow before allocating memory
+      if (nTiles == 0 || nTiles / img.nXTiles != img.nYTiles) {
+       error(getPos(), "Bad tile count in JPX SIZ marker segment");
+       return gFalse;
+      }
+      img.tiles = (JPXTile *)gmallocn(nTiles, sizeof(JPXTile));
       for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {
-       img.tiles[i].tileComps = (JPXTileComp *)gmalloc(img.nComps *
-                                                       sizeof(JPXTileComp));
+       img.tiles[i].tileComps = (JPXTileComp *)gmallocn(img.nComps,
+                                                        sizeof(JPXTileComp));
        for (comp = 0; comp < img.nComps; ++comp) {
          img.tiles[i].tileComps[comp].quantSteps = NULL;
          img.tiles[i].tileComps[comp].data = NULL;
@@ -767,8 +889,8 @@ GBool JPXStream::readCodestream(Guint len) {
                img.tiles[0].tileComps[0].transform;
          }
          img.tiles[i].tileComps[comp].resLevels =
-             (JPXResLevel *)gmalloc(
-                    (img.tiles[i].tileComps[comp].nDecompLevels + 1) *
+             (JPXResLevel *)gmallocn(
+                    (img.tiles[i].tileComps[comp].nDecompLevels + 1),
                     sizeof(JPXResLevel));
          for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
            img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
@@ -841,10 +963,10 @@ GBool JPXStream::readCodestream(Guint len) {
              img.tiles[0].tileComps[comp].transform;
        }
        img.tiles[i].tileComps[comp].resLevels =
-           (JPXResLevel *)grealloc(
+           (JPXResLevel *)greallocn(
                     img.tiles[i].tileComps[comp].resLevels,
-                    (img.tiles[i].tileComps[comp].nDecompLevels + 1) *
-                      sizeof(JPXResLevel));
+                    (img.tiles[i].tileComps[comp].nDecompLevels + 1),
+                    sizeof(JPXResLevel));
        for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {
          img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;
        }
@@ -881,9 +1003,9 @@ GBool JPXStream::readCodestream(Guint len) {
       if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {
        img.tiles[0].tileComps[0].nQuantSteps = segLen - 3;
        img.tiles[0].tileComps[0].quantSteps =
-           (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps,
-                             img.tiles[0].tileComps[0].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+                              img.tiles[0].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
        for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
          if (!readUByte(&img.tiles[0].tileComps[0].quantSteps[i])) {
            error(getPos(), "Error in JPX QCD marker segment");
@@ -893,9 +1015,9 @@ GBool JPXStream::readCodestream(Guint len) {
       } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x01) {
        img.tiles[0].tileComps[0].nQuantSteps = 1;
        img.tiles[0].tileComps[0].quantSteps =
-           (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps,
-                             img.tiles[0].tileComps[0].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+                              img.tiles[0].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
        if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[0])) {
          error(getPos(), "Error in JPX QCD marker segment");
          return gFalse;
@@ -903,9 +1025,9 @@ GBool JPXStream::readCodestream(Guint len) {
       } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {
        img.tiles[0].tileComps[0].nQuantSteps = (segLen - 3) / 2;
        img.tiles[0].tileComps[0].quantSteps =
-           (Guint *)grealloc(img.tiles[0].tileComps[0].quantSteps,
-                             img.tiles[0].tileComps[0].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[0].tileComps[0].quantSteps,
+                              img.tiles[0].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
        for (i = 0; i < img.tiles[0].tileComps[0].nQuantSteps; ++i) {
          if (!readUWord(&img.tiles[0].tileComps[0].quantSteps[i])) {
            error(getPos(), "Error in JPX QCD marker segment");
@@ -924,9 +1046,9 @@ GBool JPXStream::readCodestream(Guint len) {
            img.tiles[i].tileComps[comp].nQuantSteps =
                img.tiles[0].tileComps[0].nQuantSteps;
            img.tiles[i].tileComps[comp].quantSteps = 
-               (Guint *)grealloc(img.tiles[i].tileComps[comp].quantSteps,
-                                 img.tiles[0].tileComps[0].nQuantSteps *
-                                   sizeof(Guint));
+               (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
+                                  img.tiles[0].tileComps[0].nQuantSteps,
+                                  sizeof(Guint));
            for (j = 0; j < img.tiles[0].tileComps[0].nQuantSteps; ++j) {
              img.tiles[i].tileComps[comp].quantSteps[j] =
                  img.tiles[0].tileComps[0].quantSteps[j];
@@ -952,9 +1074,9 @@ GBool JPXStream::readCodestream(Guint len) {
        img.tiles[0].tileComps[comp].nQuantSteps =
            segLen - (img.nComps > 256 ? 5 : 4);
        img.tiles[0].tileComps[comp].quantSteps =
-           (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps,
-                             img.tiles[0].tileComps[comp].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+                              img.tiles[0].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
        for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
          if (!readUByte(&img.tiles[0].tileComps[comp].quantSteps[i])) {
            error(getPos(), "Error in JPX QCC marker segment");
@@ -964,9 +1086,9 @@ GBool JPXStream::readCodestream(Guint len) {
       } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x01) {
        img.tiles[0].tileComps[comp].nQuantSteps = 1;
        img.tiles[0].tileComps[comp].quantSteps =
-           (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps,
-                             img.tiles[0].tileComps[comp].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+                              img.tiles[0].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
        if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[0])) {
          error(getPos(), "Error in JPX QCC marker segment");
          return gFalse;
@@ -975,9 +1097,9 @@ GBool JPXStream::readCodestream(Guint len) {
        img.tiles[0].tileComps[comp].nQuantSteps =
            (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
        img.tiles[0].tileComps[comp].quantSteps =
-           (Guint *)grealloc(img.tiles[0].tileComps[comp].quantSteps,
-                             img.tiles[0].tileComps[comp].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[0].tileComps[comp].quantSteps,
+                              img.tiles[0].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
        for (i = 0; i < img.tiles[0].tileComps[comp].nQuantSteps; ++i) {
          if (!readUWord(&img.tiles[0].tileComps[comp].quantSteps[i])) {
            error(getPos(), "Error in JPX QCD marker segment");
@@ -994,9 +1116,9 @@ GBool JPXStream::readCodestream(Guint len) {
        img.tiles[i].tileComps[comp].nQuantSteps =
            img.tiles[0].tileComps[comp].nQuantSteps;
        img.tiles[i].tileComps[comp].quantSteps = 
-           (Guint *)grealloc(img.tiles[i].tileComps[comp].quantSteps,
-                             img.tiles[0].tileComps[comp].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[i].tileComps[comp].quantSteps,
+                              img.tiles[0].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
        for (j = 0; j < img.tiles[0].tileComps[comp].nQuantSteps; ++j) {
          img.tiles[i].tileComps[comp].quantSteps[j] =
              img.tiles[0].tileComps[comp].quantSteps[j];
@@ -1034,7 +1156,7 @@ GBool JPXStream::readCodestream(Guint len) {
       }
 #else
       nProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
-      progs = (JPXProgOrder *)gmalloc(nProgs * sizeof(JPXProgOrder));
+      progs = (JPXProgOrder *)gmallocn(nProgs, sizeof(JPXProgOrder));
       for (i = 0; i < nProgs; ++i) {
        if (!readUByte(&progs[i].startRes) ||
            !(img.nComps > 256 && readUWord(&progs[i].startComp)) ||
@@ -1231,10 +1353,10 @@ GBool JPXStream::readTilePart() {
              img.tiles[tileIdx].tileComps[0].transform;
        }
        img.tiles[tileIdx].tileComps[comp].resLevels =
-           (JPXResLevel *)grealloc(
+           (JPXResLevel *)greallocn(
                     img.tiles[tileIdx].tileComps[comp].resLevels,
-                    (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1) *
-                      sizeof(JPXResLevel));
+                    (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),
+                    sizeof(JPXResLevel));
        for (r = 0;
             r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;
             ++r) {
@@ -1285,10 +1407,10 @@ GBool JPXStream::readTilePart() {
       img.tiles[tileIdx].tileComps[comp].codeBlockW += 2;
       img.tiles[tileIdx].tileComps[comp].codeBlockH += 2;
       img.tiles[tileIdx].tileComps[comp].resLevels =
-         (JPXResLevel *)grealloc(
+         (JPXResLevel *)greallocn(
                     img.tiles[tileIdx].tileComps[comp].resLevels,
-                    (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1) *
-                      sizeof(JPXResLevel));
+                    (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),
+                    sizeof(JPXResLevel));
       for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {
        img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;
       }
@@ -1317,9 +1439,9 @@ GBool JPXStream::readTilePart() {
        img.tiles[tileIdx].tileComps[0].nQuantSteps =
            segLen - 3;
        img.tiles[tileIdx].tileComps[0].quantSteps =
-           (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps,
-                             img.tiles[tileIdx].tileComps[0].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+                              img.tiles[tileIdx].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
        for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
          if (!readUByte(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
            error(getPos(), "Error in JPX QCD marker segment");
@@ -1329,9 +1451,9 @@ GBool JPXStream::readTilePart() {
       } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x01) {
        img.tiles[tileIdx].tileComps[0].nQuantSteps = 1;
        img.tiles[tileIdx].tileComps[0].quantSteps =
-           (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps,
-                             img.tiles[tileIdx].tileComps[0].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+                              img.tiles[tileIdx].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
        if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[0])) {
          error(getPos(), "Error in JPX QCD marker segment");
          return gFalse;
@@ -1339,9 +1461,9 @@ GBool JPXStream::readTilePart() {
       } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) {
        img.tiles[tileIdx].tileComps[0].nQuantSteps = (segLen - 3) / 2;
        img.tiles[tileIdx].tileComps[0].quantSteps =
-           (Guint *)grealloc(img.tiles[tileIdx].tileComps[0].quantSteps,
-                             img.tiles[tileIdx].tileComps[0].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[0].quantSteps,
+                              img.tiles[tileIdx].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
        for (i = 0; i < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++i) {
          if (!readUWord(&img.tiles[tileIdx].tileComps[0].quantSteps[i])) {
            error(getPos(), "Error in JPX QCD marker segment");
@@ -1358,9 +1480,9 @@ GBool JPXStream::readTilePart() {
        img.tiles[tileIdx].tileComps[comp].nQuantSteps =
            img.tiles[tileIdx].tileComps[0].nQuantSteps;
        img.tiles[tileIdx].tileComps[comp].quantSteps = 
-           (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps,
-                             img.tiles[tileIdx].tileComps[0].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                              img.tiles[tileIdx].tileComps[0].nQuantSteps,
+                              sizeof(Guint));
        for (j = 0; j < img.tiles[tileIdx].tileComps[0].nQuantSteps; ++j) {
          img.tiles[tileIdx].tileComps[comp].quantSteps[j] =
              img.tiles[tileIdx].tileComps[0].quantSteps[j];
@@ -1379,9 +1501,9 @@ GBool JPXStream::readTilePart() {
        img.tiles[tileIdx].tileComps[comp].nQuantSteps =
            segLen - (img.nComps > 256 ? 5 : 4);
        img.tiles[tileIdx].tileComps[comp].quantSteps =
-           (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps,
-                             img.tiles[tileIdx].tileComps[comp].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                              img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
        for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
          if (!readUByte(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
            error(getPos(), "Error in JPX QCC marker segment");
@@ -1392,9 +1514,9 @@ GBool JPXStream::readTilePart() {
                 == 0x01) {
        img.tiles[tileIdx].tileComps[comp].nQuantSteps = 1;
        img.tiles[tileIdx].tileComps[comp].quantSteps =
-           (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps,
-                             img.tiles[tileIdx].tileComps[comp].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                              img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
        if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[0])) {
          error(getPos(), "Error in JPX QCC marker segment");
          return gFalse;
@@ -1404,9 +1526,9 @@ GBool JPXStream::readTilePart() {
        img.tiles[tileIdx].tileComps[comp].nQuantSteps =
            (segLen - (img.nComps > 256 ? 5 : 4)) / 2;
        img.tiles[tileIdx].tileComps[comp].quantSteps =
-           (Guint *)grealloc(img.tiles[tileIdx].tileComps[comp].quantSteps,
-                             img.tiles[tileIdx].tileComps[comp].nQuantSteps *
-                               sizeof(Guint));
+           (Guint *)greallocn(img.tiles[tileIdx].tileComps[comp].quantSteps,
+                              img.tiles[tileIdx].tileComps[comp].nQuantSteps,
+                              sizeof(Guint));
        for (i = 0; i < img.tiles[tileIdx].tileComps[comp].nQuantSteps; ++i) {
          if (!readUWord(&img.tiles[tileIdx].tileComps[comp].quantSteps[i])) {
            error(getPos(), "Error in JPX QCD marker segment");
@@ -1449,7 +1571,7 @@ GBool JPXStream::readTilePart() {
       }
 #else
       nTileProgs = (segLen - 2) / (img.nComps > 256 ? 9 : 7);
-      tileProgs = (JPXProgOrder *)gmalloc(nTileProgs * sizeof(JPXProgOrder));
+      tileProgs = (JPXProgOrder *)gmallocn(nTileProgs, sizeof(JPXProgOrder));
       for (i = 0; i < nTileProgs; ++i) {
        if (!readUByte(&tileProgs[i].startRes) ||
            !(img.nComps > 256 && readUWord(&tileProgs[i].startComp)) ||
@@ -1541,15 +1663,15 @@ GBool JPXStream::readTilePart() {
       tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->hSep);
       tileComp->cbW = 1 << tileComp->codeBlockW;
       tileComp->cbH = 1 << tileComp->codeBlockH;
-      tileComp->data = (int *)gmalloc((tileComp->x1 - tileComp->x0) *
-                                     (tileComp->y1 - tileComp->y0) *
-                                     sizeof(int));
+      tileComp->data = (int *)gmallocn((tileComp->x1 - tileComp->x0) *
+                                      (tileComp->y1 - tileComp->y0),
+                                      sizeof(int));
       if (tileComp->x1 - tileComp->x0 > tileComp->y1 - tileComp->y0) {
        n = tileComp->x1 - tileComp->x0;
       } else {
        n = tileComp->y1 - tileComp->y0;
       }
-      tileComp->buf = (int *)gmalloc((n + 8) * sizeof(int));
+      tileComp->buf = (int *)gmallocn(n + 8, sizeof(int));
       for (r = 0; r <= tileComp->nDecompLevels; ++r) {
        resLevel = &tileComp->resLevels[r];
        k = r == 0 ? tileComp->nDecompLevels
@@ -1577,7 +1699,7 @@ GBool JPXStream::readTilePart() {
          resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);
          resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);
        }
-       resLevel->precincts = (JPXPrecinct *)gmalloc(1 * sizeof(JPXPrecinct));
+       resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct));
        for (pre = 0; pre < 1; ++pre) {
          precinct = &resLevel->precincts[pre];
          precinct->x0 = resLevel->x0;
@@ -1586,7 +1708,7 @@ GBool JPXStream::readTilePart() {
          precinct->y1 = resLevel->y1;
          nSBs = r == 0 ? 1 : 3;
          precinct->subbands =
-             (JPXSubband *)gmalloc(nSBs * sizeof(JPXSubband));
+             (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband));
          for (sb = 0; sb < nSBs; ++sb) {
            subband = &precinct->subbands[sb];
            subband->x0 = resLevel->bx0[sb];
@@ -1613,18 +1735,18 @@ GBool JPXStream::readTilePart() {
              n += nx * ny;
            }
            subband->inclusion =
-               (JPXTagTreeNode *)gmalloc(n * sizeof(JPXTagTreeNode));
+               (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
            subband->zeroBitPlane =
-               (JPXTagTreeNode *)gmalloc(n * sizeof(JPXTagTreeNode));
+               (JPXTagTreeNode *)gmallocn(n, sizeof(JPXTagTreeNode));
            for (k = 0; k < n; ++k) {
              subband->inclusion[k].finished = gFalse;
              subband->inclusion[k].val = 0;
              subband->zeroBitPlane[k].finished = gFalse;
              subband->zeroBitPlane[k].val = 0;
            }
-           subband->cbs = (JPXCodeBlock *)gmalloc(subband->nXCBs *
-                                                  subband->nYCBs *
-                                                  sizeof(JPXCodeBlock));
+           subband->cbs = (JPXCodeBlock *)gmallocn(subband->nXCBs *
+                                                     subband->nYCBs,
+                                                   sizeof(JPXCodeBlock));
            sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW);
            sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH);
            cb = subband->cbs;
@@ -1651,9 +1773,9 @@ GBool JPXStream::readTilePart() {
                cb->nextPass = jpxPassCleanup;
                cb->nZeroBitPlanes = 0;
                cb->coeffs =
-                   (JPXCoeff *)gmalloc((1 << (tileComp->codeBlockW
-                                              + tileComp->codeBlockH))
-                                       * sizeof(JPXCoeff));
+                   (JPXCoeff *)gmallocn((1 << (tileComp->codeBlockW
+                                               + tileComp->codeBlockH)),
+                                        sizeof(JPXCoeff));
                for (cbi = 0;
                     cbi < (Guint)(1 << (tileComp->codeBlockW
                                         + tileComp->codeBlockH));
@@ -1662,10 +1784,8 @@ GBool JPXStream::readTilePart() {
                  cb->coeffs[cbi].len = 0;
                  cb->coeffs[cbi].mag = 0;
                }
-               cb->stats = new JArithmeticDecoderStats(jpxNContexts);
-               cb->stats->setEntry(jpxContextSigProp, 4, 0);
-               cb->stats->setEntry(jpxContextRunLength, 3, 0);
-               cb->stats->setEntry(jpxContextUniform, 46, 0);
+               cb->arithDecoder = NULL;
+               cb->stats = NULL;
                ++cb;
              }
            }
@@ -1963,14 +2083,21 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                                   Guint res, Guint sb,
                                   JPXCodeBlock *cb) {
   JPXCoeff *coeff0, *coeff1, *coeff;
-  JArithmeticDecoder *arithDecoder;
   Guint horiz, vert, diag, all, cx, xorBit;
   int horizSign, vertSign;
   Guint i, x, y0, y1, y2;
 
-  arithDecoder = new JArithmeticDecoder();
-  arithDecoder->setStream(str, cb->dataLen);
-  arithDecoder->start();
+  if (cb->arithDecoder) {
+    cb->arithDecoder->restart(cb->dataLen);
+  } else {
+    cb->arithDecoder = new JArithmeticDecoder();
+    cb->arithDecoder->setStream(str, cb->dataLen);
+    cb->arithDecoder->start();
+    cb->stats = new JArithmeticDecoderStats(jpxNContexts);
+    cb->stats->setEntry(jpxContextSigProp, 4, 0);
+    cb->stats->setEntry(jpxContextRunLength, 3, 0);
+    cb->stats->setEntry(jpxContextUniform, 46, 0);
+  }
 
   for (i = 0; i < cb->nCodingPasses; ++i) {
     switch (cb->nextPass) {
@@ -1995,7 +2122,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                  horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
                }
                if (y0+y1 > cb->y0) {
-                 diag += (coeff[-tileComp->cbW - 1].flags
+                 diag += (coeff[-(int)tileComp->cbW - 1].flags
                           >> jpxCoeffSignificantB) & 1;
                }
                if (y0+y1 < cb->y1 - 1) {
@@ -2009,7 +2136,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                  horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
                }
                if (y0+y1 > cb->y0) {
-                 diag += (coeff[-tileComp->cbW + 1].flags
+                 diag += (coeff[-(int)tileComp->cbW + 1].flags
                           >> jpxCoeffSignificantB) & 1;
                }
                if (y0+y1 < cb->y1 - 1) {
@@ -2018,9 +2145,9 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                }
              }
              if (y0+y1 > cb->y0) {
-               if (coeff[-tileComp->cbW].flags & jpxCoeffSignificant) {
+               if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {
                  ++vert;
-                 vertSign += (coeff[-tileComp->cbW].flags & jpxCoeffSign)
+                 vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)
                              ? -1 : 1;
                }
              }
@@ -2033,12 +2160,12 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
              }
              cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
              if (cx != 0) {
-               if (arithDecoder->decodeBit(cx, cb->stats)) {
+               if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
                  coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
                  coeff->mag = (coeff->mag << 1) | 1;
                  cx = signContext[horizSign][vertSign][0];
                  xorBit = signContext[horizSign][vertSign][1];
-                 if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+                 if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
                    coeff->flags |= jpxCoeffSign;
                  }
                }
@@ -2070,7 +2197,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                if (x > cb->x0) {
                  all += (coeff[-1].flags >> jpxCoeffSignificantB) & 1;
                  if (y0+y1 > cb->y0) {
-                   all += (coeff[-tileComp->cbW - 1].flags
+                   all += (coeff[-(int)tileComp->cbW - 1].flags
                            >> jpxCoeffSignificantB) & 1;
                  }
                  if (y0+y1 < cb->y1 - 1) {
@@ -2081,7 +2208,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                if (x < cb->x1 - 1) {
                  all += (coeff[1].flags >> jpxCoeffSignificantB) & 1;
                  if (y0+y1 > cb->y0) {
-                   all += (coeff[-tileComp->cbW + 1].flags
+                   all += (coeff[-(int)tileComp->cbW + 1].flags
                            >> jpxCoeffSignificantB) & 1;
                  }
                  if (y0+y1 < cb->y1 - 1) {
@@ -2090,7 +2217,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                  }
                }
                if (y0+y1 > cb->y0) {
-                 all += (coeff[-tileComp->cbW].flags
+                 all += (coeff[-(int)tileComp->cbW].flags
                          >> jpxCoeffSignificantB) & 1;
                }
                if (y0+y1 < cb->y1 - 1) {
@@ -2102,7 +2229,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                cx = 16;
              }
              coeff->mag = (coeff->mag << 1) |
-                          arithDecoder->decodeBit(cx, cb->stats);
+                          cb->arithDecoder->decodeBit(cx, cb->stats);
              ++coeff->len;
              coeff->flags |= jpxCoeffTouched;
              coeff->flags &= ~jpxCoeffFirstMagRef;
@@ -2128,12 +2255,14 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
              !(coeff1[2 * tileComp->cbW].flags & jpxCoeffTouched) &&
              !(coeff1[3 * tileComp->cbW].flags & jpxCoeffTouched) &&
              (x == cb->x0 || y0 == cb->y0 ||
-              !(coeff1[-tileComp->cbW - 1].flags
+              !(coeff1[-(int)tileComp->cbW - 1].flags
                 & jpxCoeffSignificant)) &&
              (y0 == cb->y0 ||
-              !(coeff1[-tileComp->cbW].flags & jpxCoeffSignificant)) &&
+              !(coeff1[-(int)tileComp->cbW].flags
+                & jpxCoeffSignificant)) &&
              (x == cb->x1 - 1 || y0 == cb->y0 ||
-              !(coeff1[-tileComp->cbW + 1].flags & jpxCoeffSignificant)) &&
+              !(coeff1[-(int)tileComp->cbW + 1].flags
+                & jpxCoeffSignificant)) &&
              (x == cb->x0 ||
               (!(coeff1[-1].flags & jpxCoeffSignificant) &&
                !(coeff1[tileComp->cbW - 1].flags
@@ -2157,10 +2286,10 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
              (x == cb->x1 - 1 || y0+4 == cb->y1 ||
               !(coeff1[4 * tileComp->cbW + 1].flags
                 & jpxCoeffSignificant))) {
-           if (arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) {
-             y1 = arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+           if (cb->arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) {
+             y1 = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
              y1 = (y1 << 1) |
-                  arithDecoder->decodeBit(jpxContextUniform, cb->stats);
+                  cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);
              for (y2 = 0, coeff = coeff1;
                   y2 < y1;
                   ++y2, coeff += tileComp->cbW) {
@@ -2171,7 +2300,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
              ++coeff->len;
              cx = signContext[2][2][0];
              xorBit = signContext[2][2][1];
-             if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+             if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
                coeff->flags |= jpxCoeffSign;
              }
              ++y1;
@@ -2196,7 +2325,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                  horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;
                }
                if (y0+y1 > cb->y0) {
-                 diag += (coeff[-tileComp->cbW - 1].flags
+                 diag += (coeff[-(int)tileComp->cbW - 1].flags
                           >> jpxCoeffSignificantB) & 1;
                }
                if (y0+y1 < cb->y1 - 1) {
@@ -2210,7 +2339,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                  horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;
                }
                if (y0+y1 > cb->y0) {
-                 diag += (coeff[-tileComp->cbW + 1].flags
+                 diag += (coeff[-(int)tileComp->cbW + 1].flags
                           >> jpxCoeffSignificantB) & 1;
                }
                if (y0+y1 < cb->y1 - 1) {
@@ -2219,9 +2348,9 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                }
              }
              if (y0+y1 > cb->y0) {
-               if (coeff[-tileComp->cbW].flags & jpxCoeffSignificant) {
+               if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {
                  ++vert;
-                 vertSign += (coeff[-tileComp->cbW].flags & jpxCoeffSign)
+                 vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)
                              ? -1 : 1;
                }
              }
@@ -2233,12 +2362,12 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
                }
              }
              cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];
-             if (arithDecoder->decodeBit(cx, cb->stats)) {
+             if (cb->arithDecoder->decodeBit(cx, cb->stats)) {
                coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;
                coeff->mag = (coeff->mag << 1) | 1;
                cx = signContext[horizSign][vertSign][0];
                xorBit = signContext[horizSign][vertSign][1];
-               if (arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
+               if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {
                  coeff->flags |= jpxCoeffSign;
                }
              }
@@ -2254,7 +2383,7 @@ GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,
     }
   }
 
-  delete arithDecoder;
+  cb->arithDecoder->cleanup();
   return gTrue;
 }
 
@@ -2266,7 +2395,8 @@ void JPXStream::inverseTransform(JPXTileComp *tileComp) {
   JPXSubband *subband;
   JPXCodeBlock *cb;
   JPXCoeff *coeff0, *coeff;
-  Guint qStyle, guard, eps, shift, shift2;
+  Guint qStyle, guard, eps, shift;
+  int shift2;
   double mu;
   int val;
   int *dataPtr;
@@ -2368,7 +2498,8 @@ void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,
   JPXSubband *subband;
   JPXCodeBlock *cb;
   JPXCoeff *coeff0, *coeff;
-  Guint qStyle, guard, eps, shift, shift2, t;
+  Guint qStyle, guard, eps, shift, t;
+  int shift2;
   double mu;
   int val;
   int *dataPtr;
@@ -2578,7 +2709,7 @@ void JPXStream::inverseTransform1D(JPXTileComp *tileComp,
 // converts fixed point samples back to integers.
 GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
   JPXTileComp *tileComp;
-  int coeff, d0, d1, d2, minVal, maxVal, zeroVal;
+  int coeff, d0, d1, d2, t, minVal, maxVal, zeroVal;
   int *dataPtr;
   Guint j, comp, x, y;
 
@@ -2617,9 +2748,9 @@ GBool JPXStream::inverseMultiCompAndDC(JPXTile *tile) {
          d0 = tile->tileComps[0].data[j];
          d1 = tile->tileComps[1].data[j];
          d2 = tile->tileComps[2].data[j];
-         tile->tileComps[0].data[j] = d0 - ((d2 + d1) >> 2);
-         tile->tileComps[1].data[j] = d2 - d1;
-         tile->tileComps[2].data[j] = d0 - d1;
+         tile->tileComps[1].data[j] = t = d0 - ((d2 + d1) >> 2);
+         tile->tileComps[0].data[j] = d2 + t;
+         tile->tileComps[2].data[j] = d1 + t;
          ++j;
        }
       }
index eb84fe6..e64731d 100644 (file)
@@ -44,18 +44,22 @@ enum JPXColorSpaceType {
   jpxCSYPbPr1250 = 24
 };
 
+struct JPXColorSpecCIELab {
+  Guint rl, ol, ra, oa, rb, ob, il;
+};
+
+struct JPXColorSpecEnumerated {
+  JPXColorSpaceType type;      // color space type
+  union {
+    JPXColorSpecCIELab cieLab;
+  };
+};
+
 struct JPXColorSpec {
   Guint meth;                  // method
   int prec;                    // precedence
   union {
-    struct {
-      JPXColorSpaceType type;  // color space type
-      union {
-       struct {
-         Guint rl, ol, ra, oa, rb, ob, il;
-       } cieLab;
-      };
-    } enumerated;
+    JPXColorSpecEnumerated enumerated;
   };
 };
 
@@ -135,6 +139,8 @@ struct JPXCodeBlock {
 
   //----- coefficient data
   JPXCoeff *coeffs;            // the coefficients
+  JArithmeticDecoder           // arithmetic decoder
+    *arithDecoder;
   JArithmeticDecoderStats      // arithmetic decoder stats
     *stats;
 };
@@ -275,10 +281,13 @@ public:
   virtual int lookChar();
   virtual GString *getPSFilter(int psLevel, char *indent);
   virtual GBool isBinary(GBool last = gTrue);
+  virtual void getImageParams(int *bitsPerComponent,
+                             StreamColorSpaceMode *csMode);
 
 private:
 
   void fillReadBuf();
+  void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode);
   GBool readBoxes();
   GBool readColorSpecBox(Guint dataLen);
   GBool readCodestream(Guint len);
index 1fa166f..9f0c3ca 100644 (file)
@@ -171,6 +171,13 @@ Object *Lexer::getObj(Object *obj) {
     scale = 0.1;
     while (1) {
       c = lookChar();
+      if (c == '-') {
+       // ignore minus signs in the middle of numbers to match
+       // Adobe's behavior
+       error(getPos(), "Badly formatted number");
+       getChar();
+       continue;
+      }
       if (!isdigit(c)) {
        break;
       }
@@ -472,3 +479,7 @@ void Lexer::skipToNextLine() {
     }
   }
 }
+
+GBool Lexer::isSpace(int c) {
+  return c >= 0 && c <= 0xff && specialChars[c] == 1;
+}
index 398d27c..f6ad9ce 100644 (file)
@@ -62,6 +62,9 @@ public:
   void setPos(Guint pos, int dir = 0)
     { if (!curStr.isNone()) curStr.streamSetPos(pos, dir); }
 
+  // Returns true if <c> is a whitespace character.
+  static GBool isSpace(int c);
+
 private:
 
   int getChar();
index 2d146b5..898ca20 100644 (file)
@@ -117,14 +117,19 @@ GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
 
   // dictionary
   } else if (fileSpecObj->isDict()) {
+#ifdef WIN32
+    if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
+#else
     if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
+#endif
       obj1.free();
       fileSpecObj->dictLookup("F", &obj1);
     }
-    if (obj1.isString())
+    if (obj1.isString()) {
       name = obj1.getString()->copy();
-    else
+    } else {
       error(-1, "Illegal file spec in link");
+    }
     obj1.free();
 
   // error
@@ -132,6 +137,55 @@ GString *LinkAction::getFileSpecName(Object *fileSpecObj) {
     error(-1, "Illegal file spec in link");
   }
 
+  // system-dependent path manipulation
+  if (name) {
+#ifdef WIN32
+    int i, j;
+
+    // "//...."             --> "\...."
+    // "/x/...."            --> "x:\...."
+    // "/server/share/...." --> "\\server\share\...."
+    // convert escaped slashes to slashes and unescaped slashes to backslashes
+    i = 0;
+    if (name->getChar(0) == '/') {
+      if (name->getLength() >= 2 && name->getChar(1) == '/') {
+       name->del(0);
+       i = 0;
+      } else if (name->getLength() >= 2 &&
+                ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') ||
+                 (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) &&
+                (name->getLength() == 2 || name->getChar(2) == '/')) {
+       name->setChar(0, name->getChar(1));
+       name->setChar(1, ':');
+       i = 2;
+      } else {
+       for (j = 2; j < name->getLength(); ++j) {
+         if (name->getChar(j-1) != '\\' &&
+             name->getChar(j) == '/') {
+           break;
+         }
+       }
+       if (j < name->getLength()) {
+         name->setChar(0, '\\');
+         name->insert(0, '\\');
+         i = 2;
+       }
+      }
+    }
+    for (; i < name->getLength(); ++i) {
+      if (name->getChar(i) == '/') {
+       name->setChar(i, '\\');
+      } else if (name->getChar(i) == '\\' &&
+                i+1 < name->getLength() &&
+                name->getChar(i+1) == '/') {
+       name->del(i);
+      }
+    }
+#else
+    // no manipulation needed for Unix
+#endif
+  }
+
   return name;
 }
 
@@ -497,7 +551,7 @@ LinkURI::LinkURI(Object *uriObj, GString *baseURI) {
   uri = NULL;
   if (uriObj->isString()) {
     uri2 = uriObj->getString()->copy();
-    if (baseURI) {
+    if (baseURI && baseURI->getLength() > 0) {
       n = strcspn(uri2->getCString(), "/:");
       if (n == uri2->getLength() || uri2->getChar(n) == '/') {
        uri = baseURI->copy();
@@ -690,7 +744,7 @@ Link::Link(Dict *dict, GString *baseURI) {
     obj2.free();
     if (obj1.dictLookup("D", &obj2)->isArray()) {
       borderDashLength = obj2.arrayGetLength();
-      borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
+      borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
       for (i = 0; i < borderDashLength; ++i) {
        if (obj2.arrayGet(i, &obj3)->isNum()) {
          borderDash[i] = obj3.getNum();
@@ -713,7 +767,7 @@ Link::Link(Dict *dict, GString *baseURI) {
          if (obj1.arrayGet(3, &obj2)->isArray()) {
            borderType = linkBorderDashed;
            borderDashLength = obj2.arrayGetLength();
-           borderDash = (double *)gmalloc(borderDashLength * sizeof(double));
+           borderDash = (double *)gmallocn(borderDashLength, sizeof(double));
            for (i = 0; i < borderDashLength; ++i) {
              if (obj2.arrayGet(i, &obj3)->isNum()) {
                borderDash[i] = obj3.getNum();
@@ -722,6 +776,10 @@ Link::Link(Dict *dict, GString *baseURI) {
              }
              obj3.free();
            }
+         } else {
+           // Adobe draws no border at all if the last element is of
+           // the wrong type.
+           borderWidth = 0;
          }
          obj2.free();
        }
@@ -805,7 +863,7 @@ Links::Links(Object *annots, GString *baseURI) {
          if (link->isOk()) {
            if (numLinks >= size) {
              size += 16;
-             links = (Link **)grealloc(links, size * sizeof(Link *));
+             links = (Link **)greallocn(links, size, sizeof(Link *));
            }
            links[numLinks++] = link;
          } else {
index 8f22a90..7ebf4e1 100644 (file)
@@ -30,7 +30,7 @@ NameToCharCode::NameToCharCode() {
 
   size = 31;
   len = 0;
-  tab = (NameToCharCodeEntry *)gmalloc(size * sizeof(NameToCharCodeEntry));
+  tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
   for (i = 0; i < size; ++i) {
     tab[i].name = NULL;
   }
@@ -56,7 +56,7 @@ void NameToCharCode::add(char *name, CharCode c) {
     oldSize = size;
     oldTab = tab;
     size = 2*size + 1;
-    tab = (NameToCharCodeEntry *)gmalloc(size * sizeof(NameToCharCodeEntry));
+    tab = (NameToCharCodeEntry *)gmallocn(size, sizeof(NameToCharCodeEntry));
     for (h = 0; h < size; ++h) {
       tab[h].name = NULL;
     }
index 4f28fff..c5ecba4 100644 (file)
@@ -2,7 +2,7 @@
 //
 // NameToUnicodeTable.h
 //
-// Copyright 2001-2003 Glyph & Cog, LLC
+// Copyright 2001-2004 Glyph & Cog, LLC
 //
 //========================================================================
 
@@ -44,45 +44,45 @@ static struct {
   {0x0041, "A"},
   {0x00c6, "AE"},
   {0x01fc, "AEacute"},
-  {0x00c6, "AEsmall"},
+  {0xf7e6, "AEsmall"},
   {0x00c1, "Aacute"},
-  {0x00c1, "Aacutesmall"},
+  {0xf7e1, "Aacutesmall"},
   {0x0102, "Abreve"},
   {0x00c2, "Acircumflex"},
-  {0x00c2, "Acircumflexsmall"},
+  {0xf7e2, "Acircumflexsmall"},
   {0xf6c9, "Acute"},
-  {0xf6c9, "Acutesmall"},
+  {0xf7b4, "Acutesmall"},
   {0x00c4, "Adieresis"},
-  {0x00c4, "Adieresissmall"},
+  {0xf7e4, "Adieresissmall"},
   {0x00c0, "Agrave"},
-  {0x00c0, "Agravesmall"},
+  {0xf7e0, "Agravesmall"},
   {0x0391, "Alpha"},
   {0x0386, "Alphatonos"},
   {0x0100, "Amacron"},
   {0x0104, "Aogonek"},
   {0x00c5, "Aring"},
   {0x01fa, "Aringacute"},
-  {0x00c5, "Aringsmall"},
-  {0x0041, "Asmall"},
+  {0xf7e5, "Aringsmall"},
+  {0xf761, "Asmall"},
   {0x00c3, "Atilde"},
-  {0x00c3, "Atildesmall"},
+  {0xf7e3, "Atildesmall"},
   {0x0042, "B"},
   {0x0392, "Beta"},
   {0xf6f4, "Brevesmall"},
-  {0x0042, "Bsmall"},
+  {0xf762, "Bsmall"},
   {0x0043, "C"},
   {0x0106, "Cacute"},
   {0xf6ca, "Caron"},
-  {0xf6ca, "Caronsmall"},
+  {0xf6f5, "Caronsmall"},
   {0x010c, "Ccaron"},
   {0x00c7, "Ccedilla"},
-  {0x00c7, "Ccedillasmall"},
+  {0xf7e7, "Ccedillasmall"},
   {0x0108, "Ccircumflex"},
   {0x010a, "Cdotaccent"},
   {0xf7b8, "Cedillasmall"},
   {0x03a7, "Chi"},
   {0xf6f6, "Circumflexsmall"},
-  {0x0043, "Csmall"},
+  {0xf763, "Csmall"},
   {0x0044, "D"},
   {0x010e, "Dcaron"},
   {0x0110, "Dcroat"},
@@ -90,34 +90,34 @@ static struct {
   {0xf6cb, "Dieresis"},
   {0xf6cc, "DieresisAcute"},
   {0xf6cd, "DieresisGrave"},
-  {0xf6cb, "Dieresissmall"},
+  {0xf7a8, "Dieresissmall"},
   {0xf6f7, "Dotaccentsmall"},
-  {0x0044, "Dsmall"},
+  {0xf764, "Dsmall"},
   {0x0045, "E"},
   {0x00c9, "Eacute"},
-  {0x00c9, "Eacutesmall"},
+  {0xf7e9, "Eacutesmall"},
   {0x0114, "Ebreve"},
   {0x011a, "Ecaron"},
   {0x00ca, "Ecircumflex"},
-  {0x00ca, "Ecircumflexsmall"},
+  {0xf7ea, "Ecircumflexsmall"},
   {0x00cb, "Edieresis"},
-  {0x00cb, "Edieresissmall"},
+  {0xf7eb, "Edieresissmall"},
   {0x0116, "Edotaccent"},
   {0x00c8, "Egrave"},
-  {0x00c8, "Egravesmall"},
+  {0xf7e8, "Egravesmall"},
   {0x0112, "Emacron"},
   {0x014a, "Eng"},
   {0x0118, "Eogonek"},
   {0x0395, "Epsilon"},
   {0x0388, "Epsilontonos"},
-  {0x0045, "Esmall"},
+  {0xf765, "Esmall"},
   {0x0397, "Eta"},
   {0x0389, "Etatonos"},
   {0x00d0, "Eth"},
-  {0x00d0, "Ethsmall"},
+  {0xf7f0, "Ethsmall"},
   {0x20ac, "Euro"},
   {0x0046, "F"},
-  {0x0046, "Fsmall"},
+  {0xf766, "Fsmall"},
   {0x0047, "G"},
   {0x0393, "Gamma"},
   {0x011e, "Gbreve"},
@@ -126,8 +126,8 @@ static struct {
   {0x0122, "Gcommaaccent"},
   {0x0120, "Gdotaccent"},
   {0xf6ce, "Grave"},
-  {0xf6ce, "Gravesmall"},
-  {0x0047, "Gsmall"},
+  {0xf760, "Gravesmall"},
+  {0xf767, "Gsmall"},
   {0x0048, "H"},
   {0x25cf, "H18533"},
   {0x25aa, "H18543"},
@@ -135,36 +135,36 @@ static struct {
   {0x25a1, "H22073"},
   {0x0126, "Hbar"},
   {0x0124, "Hcircumflex"},
-  {0x0048, "Hsmall"},
+  {0xf768, "Hsmall"},
   {0xf6cf, "Hungarumlaut"},
-  {0xf6cf, "Hungarumlautsmall"},
+  {0xf6f8, "Hungarumlautsmall"},
   {0x0049, "I"},
   {0x0132, "IJ"},
   {0x00cd, "Iacute"},
-  {0x00cd, "Iacutesmall"},
+  {0xf7ed, "Iacutesmall"},
   {0x012c, "Ibreve"},
   {0x00ce, "Icircumflex"},
-  {0x00ce, "Icircumflexsmall"},
+  {0xf7ee, "Icircumflexsmall"},
   {0x00cf, "Idieresis"},
-  {0x00cf, "Idieresissmall"},
+  {0xf7ef, "Idieresissmall"},
   {0x0130, "Idotaccent"},
   {0x2111, "Ifraktur"},
   {0x00cc, "Igrave"},
-  {0x00cc, "Igravesmall"},
+  {0xf7ec, "Igravesmall"},
   {0x012a, "Imacron"},
   {0x012e, "Iogonek"},
   {0x0399, "Iota"},
   {0x03aa, "Iotadieresis"},
   {0x038a, "Iotatonos"},
-  {0x0049, "Ismall"},
+  {0xf769, "Ismall"},
   {0x0128, "Itilde"},
   {0x004a, "J"},
   {0x0134, "Jcircumflex"},
-  {0x004a, "Jsmall"},
+  {0xf76a, "Jsmall"},
   {0x004b, "K"},
   {0x039a, "Kappa"},
   {0x0136, "Kcommaaccent"},
-  {0x004b, "Ksmall"},
+  {0xf76b, "Ksmall"},
   {0x004c, "L"},
   {0xf6bf, "LL"},
   {0x0139, "Lacute"},
@@ -173,34 +173,34 @@ static struct {
   {0x013b, "Lcommaaccent"},
   {0x013f, "Ldot"},
   {0x0141, "Lslash"},
-  {0x0141, "Lslashsmall"},
-  {0x004c, "Lsmall"},
+  {0xf6f9, "Lslashsmall"},
+  {0xf76c, "Lsmall"},
   {0x004d, "M"},
   {0xf6d0, "Macron"},
-  {0xf6d0, "Macronsmall"},
-  {0x004d, "Msmall"},
+  {0xf7af, "Macronsmall"},
+  {0xf76d, "Msmall"},
   {0x039c, "Mu"},
   {0x004e, "N"},
   {0x0143, "Nacute"},
   {0x0147, "Ncaron"},
   {0x0145, "Ncommaaccent"},
-  {0x004e, "Nsmall"},
+  {0xf76e, "Nsmall"},
   {0x00d1, "Ntilde"},
-  {0x00d1, "Ntildesmall"},
+  {0xf7f1, "Ntildesmall"},
   {0x039d, "Nu"},
   {0x004f, "O"},
   {0x0152, "OE"},
-  {0x0152, "OEsmall"},
+  {0xf6fa, "OEsmall"},
   {0x00d3, "Oacute"},
-  {0x00d3, "Oacutesmall"},
+  {0xf7f3, "Oacutesmall"},
   {0x014e, "Obreve"},
   {0x00d4, "Ocircumflex"},
-  {0x00d4, "Ocircumflexsmall"},
+  {0xf7f4, "Ocircumflexsmall"},
   {0x00d6, "Odieresis"},
-  {0x00d6, "Odieresissmall"},
+  {0xf7f6, "Odieresissmall"},
   {0xf6fb, "Ogoneksmall"},
   {0x00d2, "Ograve"},
-  {0x00d2, "Ogravesmall"},
+  {0xf7f2, "Ogravesmall"},
   {0x01a0, "Ohorn"},
   {0x0150, "Ohungarumlaut"},
   {0x014c, "Omacron"},
@@ -210,17 +210,17 @@ static struct {
   {0x038c, "Omicrontonos"},
   {0x00d8, "Oslash"},
   {0x01fe, "Oslashacute"},
-  {0x00d8, "Oslashsmall"},
-  {0x004f, "Osmall"},
+  {0xf7f8, "Oslashsmall"},
+  {0xf76f, "Osmall"},
   {0x00d5, "Otilde"},
-  {0x00d5, "Otildesmall"},
+  {0xf7f5, "Otildesmall"},
   {0x0050, "P"},
   {0x03a6, "Phi"},
   {0x03a0, "Pi"},
   {0x03a8, "Psi"},
-  {0x0050, "Psmall"},
+  {0xf770, "Psmall"},
   {0x0051, "Q"},
-  {0x0051, "Qsmall"},
+  {0xf771, "Qsmall"},
   {0x0052, "R"},
   {0x0154, "Racute"},
   {0x0158, "Rcaron"},
@@ -228,7 +228,7 @@ static struct {
   {0x211c, "Rfraktur"},
   {0x03a1, "Rho"},
   {0xf6fc, "Ringsmall"},
-  {0x0052, "Rsmall"},
+  {0xf772, "Rsmall"},
   {0x0053, "S"},
   {0x250c, "SF010000"},
   {0x2514, "SF020000"},
@@ -272,12 +272,12 @@ static struct {
   {0x256a, "SF540000"},
   {0x015a, "Sacute"},
   {0x0160, "Scaron"},
-  {0x0160, "Scaronsmall"},
+  {0xf6fd, "Scaronsmall"},
   {0x015e, "Scedilla"},
   {0x015c, "Scircumflex"},
   {0x0218, "Scommaaccent"},
   {0x03a3, "Sigma"},
-  {0x0053, "Ssmall"},
+  {0xf773, "Ssmall"},
   {0x0054, "T"},
   {0x03a4, "Tau"},
   {0x0166, "Tbar"},
@@ -285,19 +285,19 @@ static struct {
   {0x0162, "Tcommaaccent"},
   {0x0398, "Theta"},
   {0x00de, "Thorn"},
-  {0x00de, "Thornsmall"},
+  {0xf7fe, "Thornsmall"},
   {0xf6fe, "Tildesmall"},
-  {0x0054, "Tsmall"},
+  {0xf774, "Tsmall"},
   {0x0055, "U"},
   {0x00da, "Uacute"},
-  {0x00da, "Uacutesmall"},
+  {0xf7fa, "Uacutesmall"},
   {0x016c, "Ubreve"},
   {0x00db, "Ucircumflex"},
-  {0x00db, "Ucircumflexsmall"},
+  {0xf7fb, "Ucircumflexsmall"},
   {0x00dc, "Udieresis"},
-  {0x00dc, "Udieresissmall"},
+  {0xf7fc, "Udieresissmall"},
   {0x00d9, "Ugrave"},
-  {0x00d9, "Ugravesmall"},
+  {0xf7f9, "Ugravesmall"},
   {0x01af, "Uhorn"},
   {0x0170, "Uhungarumlaut"},
   {0x016a, "Umacron"},
@@ -307,34 +307,34 @@ static struct {
   {0x03ab, "Upsilondieresis"},
   {0x038e, "Upsilontonos"},
   {0x016e, "Uring"},
-  {0x0055, "Usmall"},
+  {0xf775, "Usmall"},
   {0x0168, "Utilde"},
   {0x0056, "V"},
-  {0x0056, "Vsmall"},
+  {0xf776, "Vsmall"},
   {0x0057, "W"},
   {0x1e82, "Wacute"},
   {0x0174, "Wcircumflex"},
   {0x1e84, "Wdieresis"},
   {0x1e80, "Wgrave"},
-  {0x0057, "Wsmall"},
+  {0xf777, "Wsmall"},
   {0x0058, "X"},
   {0x039e, "Xi"},
-  {0x0058, "Xsmall"},
+  {0xf778, "Xsmall"},
   {0x0059, "Y"},
   {0x00dd, "Yacute"},
-  {0x00dd, "Yacutesmall"},
+  {0xf7fd, "Yacutesmall"},
   {0x0176, "Ycircumflex"},
   {0x0178, "Ydieresis"},
-  {0x0178, "Ydieresissmall"},
+  {0xf7ff, "Ydieresissmall"},
   {0x1ef2, "Ygrave"},
-  {0x0059, "Ysmall"},
+  {0xf779, "Ysmall"},
   {0x005a, "Z"},
   {0x0179, "Zacute"},
   {0x017d, "Zcaron"},
-  {0x017d, "Zcaronsmall"},
+  {0xf6ff, "Zcaronsmall"},
   {0x017b, "Zdotaccent"},
   {0x0396, "Zeta"},
-  {0x005a, "Zsmall"},
+  {0xf77a, "Zsmall"},
   {0x0022, "\""},
   {0x005c, "\\"},
   {0x005d, "]"},
@@ -601,7 +601,7 @@ static struct {
   {0x03ac, "alphatonos"},
   {0x0101, "amacron"},
   {0x0026, "ampersand"},
-  {0x0026, "ampersandsmall"},
+  {0xf726, "ampersandsmall"},
   {0x2220, "angle"},
   {0x2329, "angleleft"},
   {0x232a, "angleright"},
@@ -668,7 +668,7 @@ static struct {
   {0x00b8, "cedilla"},
   {0x00a2, "cent"},
   {0xf6df, "centinferior"},
-  {0x00a2, "centoldstyle"},
+  {0xf7a2, "centoldstyle"},
   {0xf6e0, "centsuperior"},
   {0x03c7, "chi"},
   {0x25cb, "circle"},
@@ -710,7 +710,7 @@ static struct {
   {0x2584, "dnblock"},
   {0x0024, "dollar"},
   {0xf6e3, "dollarinferior"},
-  {0x0024, "dollaroldstyle"},
+  {0xf724, "dollaroldstyle"},
   {0xf6e4, "dollarsuperior"},
   {0x20ab, "dong"},
   {0x02d9, "dotaccent"},
@@ -729,7 +729,7 @@ static struct {
   {0x00e8, "egrave"},
   {0x0038, "eight"},
   {0x2088, "eightinferior"},
-  {0x0038, "eightoldstyle"},
+  {0xf738, "eightoldstyle"},
   {0x2078, "eightsuperior"},
   {0x2208, "element"},
   {0x2026, "ellipsis"},
@@ -751,9 +751,9 @@ static struct {
   {0x0021, "exclam"},
   {0x203c, "exclamdbl"},
   {0x00a1, "exclamdown"},
-  {0x00a1, "exclamdownsmall"},
+  {0xf7a1, "exclamdownsmall"},
   {0x0021, "exclamleft"},
-  {0x0021, "exclamsmall"},
+  {0xf721, "exclamsmall"},
   {0x2203, "existential"},
   {0x0066, "f"},
   {0x2640, "female"},
@@ -767,13 +767,13 @@ static struct {
   {0x0035, "five"},
   {0x215d, "fiveeighths"},
   {0x2085, "fiveinferior"},
-  {0x0035, "fiveoldstyle"},
+  {0xf735, "fiveoldstyle"},
   {0x2075, "fivesuperior"},
   {0xfb02, "fl"},
   {0x0192, "florin"},
   {0x0034, "four"},
   {0x2084, "fourinferior"},
-  {0x0034, "fouroldstyle"},
+  {0xf734, "fouroldstyle"},
   {0x2074, "foursuperior"},
   {0x2044, "fraction"},
   {0x20a3, "franc"},
@@ -871,7 +871,7 @@ static struct {
   {0x0146, "ncommaaccent"},
   {0x0039, "nine"},
   {0x2089, "nineinferior"},
-  {0x0039, "nineoldstyle"},
+  {0xf739, "nineoldstyle"},
   {0x2079, "ninesuperior"},
   {0x00a0, "nonbreakingspace"},
   {0x2209, "notelement"},
@@ -903,7 +903,7 @@ static struct {
   {0xf6dc, "onefitted"},
   {0x00bd, "onehalf"},
   {0x2081, "oneinferior"},
-  {0x0031, "oneoldstyle"},
+  {0xf731, "oneoldstyle"},
   {0x00bc, "onequarter"},
   {0x00b9, "onesuperior"},
   {0x2153, "onethird"},
@@ -952,8 +952,8 @@ static struct {
   {0x0071, "q"},
   {0x003f, "question"},
   {0x00bf, "questiondown"},
-  {0x00bf, "questiondownsmall"},
-  {0x003f, "questionsmall"},
+  {0xf7bf, "questiondownsmall"},
+  {0xf73f, "questionsmall"},
   {0x0022, "quotedbl"},
   {0x201e, "quotedblbase"},
   {0x201c, "quotedblleft"},
@@ -992,7 +992,7 @@ static struct {
   {0x0037, "seven"},
   {0x215e, "seveneighths"},
   {0x2087, "seveninferior"},
-  {0x0037, "sevenoldstyle"},
+  {0xf737, "sevenoldstyle"},
   {0x2077, "sevensuperior"},
   {0x2592, "shade"},
   {0x03c3, "sigma"},
@@ -1000,7 +1000,7 @@ static struct {
   {0x223c, "similar"},
   {0x0036, "six"},
   {0x2086, "sixinferior"},
-  {0x0036, "sixoldstyle"},
+  {0xf736, "sixoldstyle"},
   {0x2076, "sixsuperior"},
   {0x002f, "slash"},
   {0x263a, "smileface"},
@@ -1023,7 +1023,7 @@ static struct {
   {0x0033, "three"},
   {0x215c, "threeeighths"},
   {0x2083, "threeinferior"},
-  {0x0033, "threeoldstyle"},
+  {0xf733, "threeoldstyle"},
   {0x00be, "threequarters"},
   {0xf6de, "threequartersemdash"},
   {0x00b3, "threesuperior"},
@@ -1041,7 +1041,7 @@ static struct {
   {0x0032, "two"},
   {0x2025, "twodotenleader"},
   {0x2082, "twoinferior"},
-  {0x0032, "twooldstyle"},
+  {0xf732, "twooldstyle"},
   {0x00b2, "twosuperior"},
   {0x2154, "twothirds"},
   {0x0075, "u"},
@@ -1086,7 +1086,7 @@ static struct {
   {0x017c, "zdotaccent"},
   {0x0030, "zero"},
   {0x2080, "zeroinferior"},
-  {0x0030, "zerooldstyle"},
+  {0xf730, "zerooldstyle"},
   {0x2070, "zerosuperior"},
   {0x03b6, "zeta"},
   {0x007b, "{"},
index ddd6da6..71c632a 100644 (file)
@@ -161,7 +161,7 @@ void Object::print(FILE *f) {
     break;
   case objString:
     fprintf(f, "(");
-    fwrite(string->getCString(), 1, string->getLength(), stdout);
+    fwrite(string->getCString(), 1, string->getLength(), f);
     fprintf(f, ")");
     break;
   case objName:
index 04891f3..39e89a3 100644 (file)
@@ -58,14 +58,14 @@ OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
     if ((s->getChar(0) & 0xff) == 0xfe &&
        (s->getChar(1) & 0xff) == 0xff) {
       titleLen = (s->getLength() - 2) / 2;
-      title = (Unicode *)gmalloc(titleLen * sizeof(Unicode));
+      title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
       for (i = 0; i < titleLen; ++i) {
        title[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |
                   (s->getChar(3 + 2*i) & 0xff);
       }
     } else {
       titleLen = s->getLength();
-      title = (Unicode *)gmalloc(titleLen * sizeof(Unicode));
+      title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));
       for (i = 0; i < titleLen; ++i) {
        title[i] = pdfDocEncoding[s->getChar(i) & 0xff];
       }
@@ -79,7 +79,7 @@ OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {
     action = LinkAction::parseDest(&obj1);
   } else {
     obj1.free();
-    if (dict->lookup("A", &obj1)) {
+    if (!dict->lookup("A", &obj1)->isNull()) {
       action = LinkAction::parseAction(&obj1);
     }
   }
index e83882d..cfa4161 100644 (file)
@@ -55,8 +55,15 @@ void OutputDev::updateAll(GfxState *state) {
   updateLineCap(state);
   updateMiterLimit(state);
   updateLineWidth(state);
+  updateFillColorSpace(state);
   updateFillColor(state);
+  updateStrokeColorSpace(state);
   updateStrokeColor(state);
+  updateBlendMode(state);
+  updateFillOpacity(state);
+  updateStrokeOpacity(state);
+  updateFillOverprint(state);
+  updateStrokeOverprint(state);
   updateFont(state);
 }
 
@@ -95,6 +102,24 @@ void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
   }
 }
 
+void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,
+                               int width, int height,
+                               GfxImageColorMap *colorMap,
+                               Stream *maskStr,
+                               int maskWidth, int maskHeight,
+                               GBool maskInvert) {
+  drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+}
+
+void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,
+                                   int width, int height,
+                                   GfxImageColorMap *colorMap,
+                                   Stream *maskStr,
+                                   int maskWidth, int maskHeight,
+                                   GfxImageColorMap *maskColorMap) {
+  drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);
+}
+
 #if OPI_SUPPORT
 void OutputDev::opiBegin(GfxState *state, Dict *opiDict) {
 }
index b5981d9..92863e4 100644 (file)
@@ -16,6 +16,9 @@
 #include <stdlib.h>
 #include <stddef.h>
 #include <string.h>
+#ifdef WIN32
+#  include <windows.h>
+#endif
 #include "GString.h"
 #include "config.h"
 #include "GlobalParams.h"
@@ -29,6 +32,7 @@
 #include "ErrorCodes.h"
 #include "Lexer.h"
 #include "Parser.h"
+#include "SecurityHandler.h"
 #ifndef DISABLE_OUTLINE
 #include "Outline.h"
 #endif
 //------------------------------------------------------------------------
 
 PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
-              GString *userPassword) {
+              GString *userPassword, void *guiDataA) {
   Object obj;
   GString *fileName1, *fileName2;
 
   ok = gFalse;
   errCode = errNone;
 
+  guiData = guiDataA;
+
   file = NULL;
   str = NULL;
   xref = NULL;
@@ -96,10 +102,68 @@ PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,
   ok = setup(ownerPassword, userPassword);
 }
 
+#ifdef WIN32
+PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,
+              GString *userPassword, void *guiDataA) {
+  OSVERSIONINFO version;
+  wchar_t fileName2[_MAX_PATH + 1];
+  Object obj;
+  int i;
+
+  ok = gFalse;
+  errCode = errNone;
+
+  guiData = guiDataA;
+
+  file = NULL;
+  str = NULL;
+  xref = NULL;
+  catalog = NULL;
+  links = NULL;
+#ifndef DISABLE_OUTLINE
+  outline = NULL;
+#endif
+
+  //~ file name should be stored in Unicode (?)
+  fileName = new GString();
+  for (i = 0; i < fileNameLen; ++i) {
+    fileName->append((char)fileNameA[i]);
+  }
+
+  // zero-terminate the file name string
+  for (i = 0; i < fileNameLen && i < _MAX_PATH; ++i) {
+    fileName2[i] = fileNameA[i];
+  }
+  fileName2[i] = 0;
+
+  // try to open file
+  // NB: _wfopen is only available in NT
+  version.dwOSVersionInfoSize = sizeof(version);
+  GetVersionEx(&version);
+  if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+    file = _wfopen(fileName2, L"rb");
+  } else {
+    file = fopen(fileName->getCString(), "rb");
+  }
+  if (!file) {
+    error(-1, "Couldn't open file '%s'", fileName->getCString());
+    errCode = errOpenFile;
+    return;
+  }
+
+  // create stream
+  obj.initNull();
+  str = new FileStream(file, 0, gFalse, 0, &obj);
+
+  ok = setup(ownerPassword, userPassword);
+}
+#endif
+
 PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,
-              GString *userPassword) {
+              GString *userPassword, void *guiDataA) {
   ok = gFalse;
   errCode = errNone;
+  guiData = guiDataA;
   fileName = NULL;
   file = NULL;
   str = strA;
@@ -119,13 +183,19 @@ GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {
   checkHeader();
 
   // read xref table
-  xref = new XRef(str, ownerPassword, userPassword);
+  xref = new XRef(str);
   if (!xref->isOk()) {
     error(-1, "Couldn't read xref table");
     errCode = xref->getErrorCode();
     return gFalse;
   }
 
+  // check for encryption
+  if (!checkEncryption(ownerPassword, userPassword)) {
+    errCode = errEncrypted;
+    return gFalse;
+  }
+
   // read catalog
   catalog = new Catalog(xref);
   if (!catalog->isOk()) {
@@ -191,7 +261,10 @@ void PDFDoc::checkHeader() {
     return;
   }
   str->moveStart(i);
-  p = strtok(&hdrBuf[i+5], " \t\n\r");
+  if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) {
+    error(-1, "May not be a PDF file (continuing anyway)");
+    return;
+  }
   pdfVersion = atof(p);
   if (!(hdrBuf[i+5] >= '0' && hdrBuf[i+5] <= '9') ||
       pdfVersion > supportedPDFVersionNum + 0.0001) {
@@ -200,8 +273,43 @@ void PDFDoc::checkHeader() {
   }
 }
 
+GBool PDFDoc::checkEncryption(GString *ownerPassword, GString *userPassword) {
+  Object encrypt;
+  GBool encrypted;
+  SecurityHandler *secHdlr;
+  GBool ret;
+
+  xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
+  if ((encrypted = encrypt.isDict())) {
+    if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
+      if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
+       // authorization succeeded
+               xref->setEncryption(secHdlr->getPermissionFlags(),
+                           secHdlr->getOwnerPasswordOk(),
+                           secHdlr->getFileKey(),
+                           secHdlr->getFileKeyLength(),
+                           secHdlr->getEncVersion());
+       ret = gTrue;
+      } else {
+       // authorization failed
+       ret = gFalse;
+      }
+      delete secHdlr;
+    } else {
+      // couldn't find the matching security handler
+      ret = gFalse;
+    }
+  } else {
+    // document is not encrypted
+    ret = gTrue;
+  }
+  encrypt.free();
+  return ret;
+}
+
 void PDFDoc::displayPage(OutputDev *out, int page, double hDPI, double vDPI,
-                        int rotate, GBool crop, GBool doLinks,
+                        int rotate, GBool useMediaBox, GBool crop,
+                        GBool doLinks,
                         GBool (*abortCheckCbk)(void *data),
                         void *abortCheckCbkData) {
   Page *p;
@@ -215,39 +323,57 @@ void PDFDoc::displayPage(OutputDev *out, int page, double hDPI, double vDPI,
       delete links;
     }
     getLinks(p);
-    p->display(out, hDPI, vDPI, rotate, crop, links, catalog,
+    p->display(out, hDPI, vDPI, rotate, useMediaBox, crop, links, catalog,
               abortCheckCbk, abortCheckCbkData);
   } else {
-    p->display(out, hDPI, vDPI, rotate, crop, NULL, catalog,
+    p->display(out, hDPI, vDPI, rotate, useMediaBox, crop, NULL, catalog,
               abortCheckCbk, abortCheckCbkData);
   }
 }
 
 void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
                          double hDPI, double vDPI, int rotate,
-                         GBool crop, GBool doLinks,
+                         GBool useMediaBox, GBool crop, GBool doLinks,
                          GBool (*abortCheckCbk)(void *data),
                          void *abortCheckCbkData) {
   int page;
 
   for (page = firstPage; page <= lastPage; ++page) {
-    displayPage(out, page, hDPI, vDPI, rotate, crop, doLinks,
+    displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, doLinks,
                abortCheckCbk, abortCheckCbkData);
   }
 }
 
 void PDFDoc::displayPageSlice(OutputDev *out, int page,
-                             double hDPI, double vDPI,
-                             int rotate, GBool crop,
+                             double hDPI, double vDPI, int rotate,
+                             GBool useMediaBox, GBool crop, GBool doLinks,
                              int sliceX, int sliceY, int sliceW, int sliceH,
                              GBool (*abortCheckCbk)(void *data),
                              void *abortCheckCbkData) {
   Page *p;
 
   p = catalog->getPage(page);
-  p->displaySlice(out, hDPI, vDPI, rotate, crop,
-                 sliceX, sliceY, sliceW, sliceH,
-                 NULL, catalog, abortCheckCbk, abortCheckCbkData);
+  if (doLinks) {
+    if (links) {
+      delete links;
+    }
+    getLinks(p);
+    p->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+                   sliceX, sliceY, sliceW, sliceH,
+                   links, catalog, abortCheckCbk, abortCheckCbkData);
+  } else {
+    p->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
+                   sliceX, sliceY, sliceW, sliceH,
+                   NULL, catalog, abortCheckCbk, abortCheckCbkData);
+  }
+}
+
+Links *PDFDoc::takeLinks() {
+  Links *ret;
+
+  ret = links;
+  links = NULL;
+  return ret;
 }
 
 GBool PDFDoc::isLinearized() {
index bdcbd65..a1f42c0 100644 (file)
@@ -17,7 +17,6 @@
 
 #include <stdio.h>
 #include "XRef.h"
-#include "Link.h"
 #include "Catalog.h"
 #include "Page.h"
 
@@ -37,9 +36,13 @@ class PDFDoc {
 public:
 
   PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,
-        GString *userPassword = NULL);
+        GString *userPassword = NULL, void *guiDataA = NULL);
+#ifdef WIN32
+  PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword = NULL,
+        GString *userPassword = NULL, void *guiDataA = NULL);
+#endif
   PDFDoc(BaseStream *strA, GString *ownerPassword = NULL,
-        GString *userPassword = NULL);
+        GString *userPassword = NULL, void *guiDataA = NULL);
   ~PDFDoc();
 
   // Was PDF document successfully opened?
@@ -61,10 +64,14 @@ public:
   BaseStream *getBaseStream() { return str; }
 
   // Get page parameters.
-  double getPageWidth(int page)
-    { return catalog->getPage(page)->getWidth(); }
-  double getPageHeight(int page)
-    { return catalog->getPage(page)->getHeight(); }
+  double getPageMediaWidth(int page)
+    { return catalog->getPage(page)->getMediaWidth(); }
+  double getPageMediaHeight(int page)
+    { return catalog->getPage(page)->getMediaHeight(); }
+  double getPageCropWidth(int page)
+    { return catalog->getPage(page)->getCropWidth(); }
+  double getPageCropHeight(int page)
+    { return catalog->getPage(page)->getCropHeight(); }
   int getPageRotate(int page)
     { return catalog->getPage(page)->getRotate(); }
 
@@ -80,21 +87,22 @@ public:
 
   // Display a page.
   void displayPage(OutputDev *out, int page, double hDPI, double vDPI,
-                  int rotate, GBool crop, GBool doLinks,
+                  int rotate, GBool useMediaBox, GBool crop,
+                  GBool doLinks,
                   GBool (*abortCheckCbk)(void *data) = NULL,
                   void *abortCheckCbkData = NULL);
 
   // Display a range of pages.
   void displayPages(OutputDev *out, int firstPage, int lastPage,
                    double hDPI, double vDPI, int rotate,
-                   GBool crop, GBool doLinks,
+                   GBool useMediaBox, GBool crop, GBool doLinks,
                    GBool (*abortCheckCbk)(void *data) = NULL,
                    void *abortCheckCbkData = NULL);
 
   // Display part of a page.
   void displayPageSlice(OutputDev *out, int page,
-                       double hDPI, double vDPI,
-                       int rotate, GBool crop,
+                       double hDPI, double vDPI, int rotate,
+                       GBool useMediaBox, GBool crop, GBool doLinks,
                        int sliceX, int sliceY, int sliceW, int sliceH,
                        GBool (*abortCheckCbk)(void *data) = NULL,
                        void *abortCheckCbkData = NULL);
@@ -103,13 +111,9 @@ public:
   // not found.
   int findPage(int num, int gen) { return catalog->findPage(num, gen); }
 
-  // If point <x>,<y> is in a link, return the associated action;
-  // else return NULL.
-  LinkAction *findLink(double x, double y)
-    { return links ? links->find(x, y) : (LinkAction *)NULL; }
-
-  // Return true if <x>,<y> is in a link.
-  GBool onLink(double x, double y) { return links->onLink(x, y); }
+  // Returns the links for the current page, transferring ownership to
+  // the caller.
+  Links *takeLinks();
 
   // Find a named destination.  Returns the link destination, or
   // NULL if <name> is not a destination.
@@ -147,16 +151,21 @@ public:
   // Save this file with another name.
   GBool saveAs(GString *name);
 
+  // Return a pointer to the GUI (XPDFCore or WinPDFCore object).
+  void *getGUIData() { return guiData; }
+
 
 private:
 
   GBool setup(GString *ownerPassword, GString *userPassword);
   void checkHeader();
+  GBool checkEncryption(GString *ownerPassword, GString *userPassword);
   void getLinks(Page *page);
 
   GString *fileName;
   FILE *file;
   BaseStream *str;
+  void *guiData;
   double pdfVersion;
   XRef *xref;
   Catalog *catalog;
index 2376cb4..36f96e1 100644 (file)
@@ -51,7 +51,6 @@ public:
   ~PageAttrs();
 
   // Accessors.
-  PDFRectangle *getBox() { return limitToCropBox ? &cropBox : &mediaBox; }
   PDFRectangle *getMediaBox() { return &mediaBox; }
   PDFRectangle *getCropBox() { return &cropBox; }
   GBool isCropped() { return haveCropBox; }
@@ -83,7 +82,6 @@ private:
   PDFRectangle mediaBox;
   PDFRectangle cropBox;
   GBool haveCropBox;
-  GBool limitToCropBox;
   PDFRectangle bleedBox;
   PDFRectangle trimBox;
   PDFRectangle artBox;
@@ -114,12 +112,17 @@ public:
   GBool isOk() { return ok; }
 
   // Get page parameters.
-  PDFRectangle *getBox() { return attrs->getBox(); }
   PDFRectangle *getMediaBox() { return attrs->getMediaBox(); }
   PDFRectangle *getCropBox() { return attrs->getCropBox(); }
   GBool isCropped() { return attrs->isCropped(); }
-  double getWidth() { return attrs->getBox()->x2 - attrs->getBox()->x1; }
-  double getHeight() { return attrs->getBox()->y2 - attrs->getBox()->y1; }
+  double getMediaWidth() 
+    { return attrs->getMediaBox()->x2 - attrs->getMediaBox()->x1; }
+  double getMediaHeight()
+    { return attrs->getMediaBox()->y2 - attrs->getMediaBox()->y1; }
+  double getCropWidth() 
+    { return attrs->getCropBox()->x2 - attrs->getCropBox()->x1; }
+  double getCropHeight()
+    { return attrs->getCropBox()->y2 - attrs->getCropBox()->y1; }
   PDFRectangle *getBleedBox() { return attrs->getBleedBox(); }
   PDFRectangle *getTrimBox() { return attrs->getTrimBox(); }
   PDFRectangle *getArtBox() { return attrs->getArtBox(); }
@@ -142,19 +145,23 @@ public:
 
   // Display a page.
   void display(OutputDev *out, double hDPI, double vDPI,
-              int rotate, GBool crop,
+              int rotate, GBool useMediaBox, GBool crop,
               Links *links, Catalog *catalog,
               GBool (*abortCheckCbk)(void *data) = NULL,
               void *abortCheckCbkData = NULL);
 
   // Display part of a page.
   void displaySlice(OutputDev *out, double hDPI, double vDPI,
-                   int rotate, GBool crop,
+                   int rotate, GBool useMediaBox, GBool crop,
                    int sliceX, int sliceY, int sliceW, int sliceH,
                    Links *links, Catalog *catalog,
                    GBool (*abortCheckCbk)(void *data) = NULL,
                    void *abortCheckCbkData = NULL);
 
+  // Get the page's default CTM.
+  void getDefaultCTM(double *ctm, double hDPI, double vDPI,
+                    int rotate, GBool upsideDown);
+
 private:
 
   XRef *xref;                  // the xref table for this PDF file
index 0aa66d3..af7c933 100644 (file)
@@ -19,9 +19,7 @@
 #include "Parser.h"
 #include "XRef.h"
 #include "Error.h"
-#ifndef NO_DECRYPTION
 #include "Decrypt.h"
-#endif
 
 Parser::Parser(XRef *xrefA, Lexer *lexerA) {
   xref = xrefA;
@@ -37,23 +35,17 @@ Parser::~Parser() {
   delete lexer;
 }
 
-#ifndef NO_DECRYPTION
 Object *Parser::getObj(Object *obj,
                       Guchar *fileKey, int keyLength,
                       int objNum, int objGen) {
-#else
-Object *Parser::getObj(Object *obj) {
-#endif
   char *key;
   Stream *str;
   Object obj2;
   int num;
-#ifndef NO_DECRYPTION
   Decrypt *decrypt;
   GString *s;
   char *p;
   int i;
-#endif
 
   // refill buffer after inline image data
   if (inlineImg == 2) {
@@ -69,11 +61,7 @@ Object *Parser::getObj(Object *obj) {
     shift();
     obj->initArray(xref);
     while (!buf1.isCmd("]") && !buf1.isEOF())
-#ifndef NO_DECRYPTION
       obj->arrayAdd(getObj(&obj2, fileKey, keyLength, objNum, objGen));
-#else
-      obj->arrayAdd(getObj(&obj2));
-#endif
     if (buf1.isEOF())
       error(getPos(), "End of file inside array");
     shift();
@@ -93,11 +81,7 @@ Object *Parser::getObj(Object *obj) {
          gfree(key);
          break;
        }
-#ifndef NO_DECRYPTION
        obj->dictAdd(key, getObj(&obj2, fileKey, keyLength, objNum, objGen));
-#else
-       obj->dictAdd(key, getObj(&obj2));
-#endif
       }
     }
     if (buf1.isEOF())
@@ -105,12 +89,10 @@ Object *Parser::getObj(Object *obj) {
     if (buf2.isCmd("stream")) {
       if ((str = makeStream(obj))) {
        obj->initStream(str);
-#ifndef NO_DECRYPTION
        if (fileKey) {
          str->getBaseStream()->doDecryption(fileKey, keyLength,
                                             objNum, objGen);
        }
-#endif
       } else {
        obj->free();
        obj->initError();
@@ -131,7 +113,6 @@ Object *Parser::getObj(Object *obj) {
       obj->initInt(num);
     }
 
-#ifndef NO_DECRYPTION
   // string
   } else if (buf1.isString() && fileKey) {
     buf1.copy(obj);
@@ -144,7 +125,6 @@ Object *Parser::getObj(Object *obj) {
     }
     delete decrypt;
     shift();
-#endif
 
   // simple object
   } else {
@@ -157,6 +137,7 @@ Object *Parser::getObj(Object *obj) {
 
 Stream *Parser::makeStream(Object *dict) {
   Object obj;
+  BaseStream *baseStr;
   Stream *str;
   Guint pos, endPos, length;
 
@@ -185,13 +166,7 @@ Stream *Parser::makeStream(Object *dict) {
   if (!lexer->getStream()) {
     return NULL;
   }
-
-  // make base stream
-  str = lexer->getStream()->getBaseStream()->makeSubStream(pos, gTrue,
-                                                          length, dict);
-
-  // get filters
-  str = str->addFilters(dict);
+  baseStr = lexer->getStream()->getBaseStream();
 
   // skip over stream data
   lexer->setPos(pos + length);
@@ -203,9 +178,17 @@ Stream *Parser::makeStream(Object *dict) {
     shift();
   } else {
     error(getPos(), "Missing 'endstream'");
-    str->ignoreLength();
+    // kludge for broken PDF files: just add 5k to the length, and
+    // hope its enough
+    length += 5000;
   }
 
+  // make base stream
+  str = baseStr->makeSubStream(pos, gTrue, length, dict);
+
+  // get filters
+  str = str->addFilters(dict);
+
   return str;
 }
 
index 3bc3ab2..b583baf 100644 (file)
@@ -31,13 +31,9 @@ public:
   ~Parser();
 
   // Get the next object from the input stream.
-#ifndef NO_DECRYPTION
   Object *getObj(Object *obj,
                 Guchar *fileKey = NULL, int keyLength = 0,
                 int objNum = 0, int objGen = 0);
-#else
-  Object *getObj(Object *obj);
-#endif
 
   // Get stream.
   Stream *getStream() { return lexer->getStream(); }
diff --git a/pdf2swf/xpdf/SecurityHandler.cc b/pdf2swf/xpdf/SecurityHandler.cc
new file mode 100644 (file)
index 0000000..8825ce8
--- /dev/null
@@ -0,0 +1,376 @@
+//========================================================================
+//
+// SecurityHandler.cc
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include "GString.h"
+#include "PDFDoc.h"
+#include "Decrypt.h"
+#include "Error.h"
+#include "GlobalParams.h"
+#if HAVE_XPDFCORE
+#  include "XPDFCore.h"
+#elif HAVE_WINPDFCORE
+#  include "WinPDFCore.h"
+#endif
+#ifdef ENABLE_PLUGINS
+#  include "XpdfPluginAPI.h"
+#endif
+#include "SecurityHandler.h"
+
+//------------------------------------------------------------------------
+// SecurityHandler
+//------------------------------------------------------------------------
+
+SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) {
+  Object filterObj;
+  SecurityHandler *secHdlr;
+  XpdfSecurityHandler *xsh;
+
+  encryptDictA->dictLookup("Filter", &filterObj);
+  if (filterObj.isName("Standard")) {
+    secHdlr = new StandardSecurityHandler(docA, encryptDictA);
+  } else if (filterObj.isName()) {
+#ifdef ENABLE_PLUGINS
+    if ((xsh = globalParams->getSecurityHandler(filterObj.getName()))) {
+      secHdlr = new ExternalSecurityHandler(docA, encryptDictA, xsh);
+    } else {
+#endif
+      error(-1, "Couldn't find the '%s' security handler",
+           filterObj.getName());
+      secHdlr = NULL;
+#ifdef ENABLE_PLUGINS
+    }
+#endif
+  } else {
+    error(-1, "Missing or invalid 'Filter' entry in encryption dictionary");
+    secHdlr = NULL;
+  }
+  filterObj.free();
+  return secHdlr;
+}
+
+SecurityHandler::SecurityHandler(PDFDoc *docA) {
+  doc = docA;
+}
+
+SecurityHandler::~SecurityHandler() {
+}
+
+GBool SecurityHandler::checkEncryption(GString *ownerPassword,
+                                      GString *userPassword) {
+  void *authData;
+  GBool ok;
+  int i;
+
+  if (ownerPassword || userPassword) {
+    authData = makeAuthData(ownerPassword, userPassword);
+  } else {
+    authData = NULL;
+  }
+  ok = authorize(authData);
+  if (authData) {
+    freeAuthData(authData);
+  }
+  for (i = 0; !ok && i < 3; ++i) {
+    if (!(authData = getAuthData())) {
+      break;
+    }
+    ok = authorize(authData);
+    if (authData) {
+      freeAuthData(authData);
+    }
+  }
+  if (!ok) {
+    error(-1, "Incorrect password");
+  }
+  return ok;
+}
+
+//------------------------------------------------------------------------
+// StandardSecurityHandler
+//------------------------------------------------------------------------
+
+class StandardAuthData {
+public:
+
+  StandardAuthData(GString *ownerPasswordA, GString *userPasswordA) {
+    ownerPassword = ownerPasswordA;
+    userPassword = userPasswordA;
+  }
+
+  ~StandardAuthData() {
+    if (ownerPassword) {
+      delete ownerPassword;
+    }
+    if (userPassword) {
+      delete userPassword;
+    }
+  }
+
+  GString *ownerPassword;
+  GString *userPassword;
+};
+
+StandardSecurityHandler::StandardSecurityHandler(PDFDoc *docA,
+                                                Object *encryptDictA):
+  SecurityHandler(docA)
+{
+  Object versionObj, revisionObj, lengthObj;
+  Object ownerKeyObj, userKeyObj, permObj, fileIDObj;
+  Object fileIDObj1;
+  Object cryptFiltersObj, streamFilterObj, stringFilterObj;
+  Object cryptFilterObj, cfmObj, cfLengthObj;
+  Object encryptMetadataObj;
+
+  ok = gFalse;
+  fileID = NULL;
+  ownerKey = NULL;
+  userKey = NULL;
+
+  encryptDictA->dictLookup("V", &versionObj);
+  encryptDictA->dictLookup("R", &revisionObj);
+  encryptDictA->dictLookup("Length", &lengthObj);
+  encryptDictA->dictLookup("O", &ownerKeyObj);
+  encryptDictA->dictLookup("U", &userKeyObj);
+  encryptDictA->dictLookup("P", &permObj);
+  doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj);
+  if (versionObj.isInt() &&
+      revisionObj.isInt() &&
+      ownerKeyObj.isString() && ownerKeyObj.getString()->getLength() == 32 &&
+      userKeyObj.isString() && userKeyObj.getString()->getLength() == 32 &&
+      permObj.isInt()) {
+    encVersion = versionObj.getInt();
+    encRevision = revisionObj.getInt();
+    // revision 2 forces a 40-bit key - some buggy PDF generators
+    // set the Length value incorrectly
+    if (encRevision == 2 || !lengthObj.isInt()) {
+      fileKeyLength = 5;
+    } else {
+      fileKeyLength = lengthObj.getInt() / 8;
+    }
+    encryptMetadata = gTrue;
+    //~ this currently only handles a subset of crypt filter functionality
+    if (encVersion == 4 && encRevision == 4) {
+      encryptDictA->dictLookup("CF", &cryptFiltersObj);
+      encryptDictA->dictLookup("StmF", &streamFilterObj);
+      encryptDictA->dictLookup("StrF", &stringFilterObj);
+      if (cryptFiltersObj.isDict() &&
+         streamFilterObj.isName() &&
+         stringFilterObj.isName() &&
+         !strcmp(streamFilterObj.getName(), stringFilterObj.getName())) {
+       if (cryptFiltersObj.dictLookup(streamFilterObj.getName(),
+                                      &cryptFilterObj)->isDict()) {
+         if (cryptFilterObj.dictLookup("CFM", &cfmObj)->isName("V2")) {
+           encVersion = 2;
+           encRevision = 3;
+           if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) {
+             //~ according to the spec, this should be cfLengthObj / 8
+             fileKeyLength = cfLengthObj.getInt();
+           }
+           cfLengthObj.free();
+         }
+         cfmObj.free();
+       }
+       cryptFilterObj.free();
+      }
+      stringFilterObj.free();
+      streamFilterObj.free();
+      cryptFiltersObj.free();
+      if (encryptDictA->dictLookup("EncryptMetadata",
+                                  &encryptMetadataObj)->isBool()) {
+       encryptMetadata = encryptMetadataObj.getBool();
+      }
+      encryptMetadataObj.free();
+    }
+    permFlags = permObj.getInt();
+    ownerKey = ownerKeyObj.getString()->copy();
+    userKey = userKeyObj.getString()->copy();
+    if (encVersion >= 1 && encVersion <= 2 &&
+       encRevision >= 2 && encRevision <= 3) {
+      if (fileIDObj.isArray()) {
+       if (fileIDObj.arrayGet(0, &fileIDObj1)->isString()) {
+         fileID = fileIDObj1.getString()->copy();
+       } else {
+         fileID = new GString();
+       }
+       fileIDObj1.free();
+      } else {
+       fileID = new GString();
+      }
+      ok = gTrue;
+    } else {
+      error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
+           encVersion, encRevision);
+    }
+  } else {
+    error(-1, "Weird encryption info");
+  }
+  if (fileKeyLength > 16) {
+    fileKeyLength = 16;
+  }
+  fileIDObj.free();
+  permObj.free();
+  userKeyObj.free();
+  ownerKeyObj.free();
+  lengthObj.free();
+  revisionObj.free();
+  versionObj.free();
+}
+
+StandardSecurityHandler::~StandardSecurityHandler() {
+  if (fileID) {
+    delete fileID;
+  }
+  if (ownerKey) {
+    delete ownerKey;
+  }
+  if (userKey) {
+    delete userKey;
+  }
+}
+
+void *StandardSecurityHandler::makeAuthData(GString *ownerPassword,
+                                           GString *userPassword) {
+  return new StandardAuthData(ownerPassword ? ownerPassword->copy()
+                                           : (GString *)NULL,
+                             userPassword ? userPassword->copy()
+                                          : (GString *)NULL);
+}
+
+void *StandardSecurityHandler::getAuthData() {
+#if HAVE_XPDFCORE
+  XPDFCore *core;
+  GString *password;
+
+  if (!(core = (XPDFCore *)doc->getGUIData()) ||
+      !(password = core->getPassword())) {
+    return NULL;
+  }
+  return new StandardAuthData(password, password->copy());
+#elif HAVE_WINPDFCORE
+  WinPDFCore *core;
+  GString *password;
+
+  if (!(core = (WinPDFCore *)doc->getGUIData()) ||
+      !(password = core->getPassword())) {
+    return NULL;
+  }
+  return new StandardAuthData(password, password->copy());
+#else
+  return NULL;
+#endif
+}
+
+void StandardSecurityHandler::freeAuthData(void *authData) {
+  delete (StandardAuthData *)authData;
+}
+
+GBool StandardSecurityHandler::authorize(void *authData) {
+  GString *ownerPassword, *userPassword;
+
+  if (!ok) {
+    return gFalse;
+  }
+  if (authData) {
+    ownerPassword = ((StandardAuthData *)authData)->ownerPassword;
+    userPassword = ((StandardAuthData *)authData)->userPassword;
+  } else {
+    ownerPassword = NULL;
+    userPassword = NULL;
+  }
+  if (!Decrypt::makeFileKey(encVersion, encRevision, fileKeyLength,
+                           ownerKey, userKey, permFlags, fileID,
+                           ownerPassword, userPassword, fileKey,
+                           encryptMetadata, &ownerPasswordOk)) {
+    return gFalse;
+  }
+  return gTrue;
+}
+
+#ifdef ENABLE_PLUGINS
+
+//------------------------------------------------------------------------
+// ExternalSecurityHandler
+//------------------------------------------------------------------------
+
+ExternalSecurityHandler::ExternalSecurityHandler(PDFDoc *docA,
+                                                Object *encryptDictA,
+                                                XpdfSecurityHandler *xshA):
+  SecurityHandler(docA)
+{
+  encryptDictA->copy(&encryptDict);
+  xsh = xshA;
+  ok = gFalse;
+
+  if (!(*xsh->newDoc)(xsh->handlerData, (XpdfDoc)docA,
+                     (XpdfObject)encryptDictA, &docData)) {
+    return;
+  }
+
+  ok = gTrue;
+}
+
+ExternalSecurityHandler::~ExternalSecurityHandler() {
+  (*xsh->freeDoc)(xsh->handlerData, docData);
+  encryptDict.free();
+}
+
+void *ExternalSecurityHandler::makeAuthData(GString *ownerPassword,
+                                           GString *userPassword) {
+  char *opw, *upw;
+  void *authData;
+
+  opw = ownerPassword ? ownerPassword->getCString() : (char *)NULL;
+  upw = userPassword ? userPassword->getCString() : (char *)NULL;
+  if (!(*xsh->makeAuthData)(xsh->handlerData, docData, opw, upw, &authData)) {
+    return NULL;
+  }
+  return authData;
+}
+
+void *ExternalSecurityHandler::getAuthData() {
+  void *authData;
+
+  if (!(*xsh->getAuthData)(xsh->handlerData, docData, &authData)) {
+    return NULL;
+  }
+  return authData;
+}
+
+void ExternalSecurityHandler::freeAuthData(void *authData) {
+  (*xsh->freeAuthData)(xsh->handlerData, docData, authData);
+}
+
+GBool ExternalSecurityHandler::authorize(void *authData) {
+  char *key;
+  int length;
+
+  if (!ok) {
+    return gFalse;
+  }
+  permFlags = (*xsh->authorize)(xsh->handlerData, docData, authData);
+  if (!(permFlags & xpdfPermissionOpen)) {
+    return gFalse;
+  }
+  if (!(*xsh->getKey)(xsh->handlerData, docData, &key, &length, &encVersion)) {
+    return gFalse;
+  }
+  if ((fileKeyLength = length) > 16) {
+    fileKeyLength = 16;
+  }
+  memcpy(fileKey, key, fileKeyLength);
+  (*xsh->freeKey)(xsh->handlerData, docData, key, length);
+  return gTrue;
+}
+
+#endif // ENABLE_PLUGINS
diff --git a/pdf2swf/xpdf/SecurityHandler.h b/pdf2swf/xpdf/SecurityHandler.h
new file mode 100644 (file)
index 0000000..127acb7
--- /dev/null
@@ -0,0 +1,155 @@
+//========================================================================
+//
+// SecurityHandler.h
+//
+// Copyright 2004 Glyph & Cog, LLC
+//
+//========================================================================
+
+#ifndef SECURITYHANDLER_H
+#define SECURITYHANDLER_H
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma interface
+#endif
+
+#include "gtypes.h"
+#include "Object.h"
+
+class GString;
+class PDFDoc;
+struct XpdfSecurityHandler;
+
+//------------------------------------------------------------------------
+// SecurityHandler
+//------------------------------------------------------------------------
+
+class SecurityHandler {
+public:
+
+  static SecurityHandler *make(PDFDoc *docA, Object *encryptDictA);
+
+  SecurityHandler(PDFDoc *docA);
+  virtual ~SecurityHandler();
+
+  // Check the document's encryption.  If the document is encrypted,
+  // this will first try <ownerPassword> and <userPassword> (in
+  // "batch" mode), and if those fail, it will attempt to request a
+  // password from the user.  This is the high-level function that
+  // calls the lower level functions for the specific security handler
+  // (requesting a password three times, etc.).  Returns true if the
+  // document can be opened (if it's unencrypted, or if a correct
+  // password is obtained); false otherwise (encrypted and no correct
+  // password).
+  GBool checkEncryption(GString *ownerPassword,
+                       GString *userPassword);
+
+  // Create authorization data for the specified owner and user
+  // passwords.  If the security handler doesn't support "batch" mode,
+  // this function should return NULL.
+  virtual void *makeAuthData(GString *ownerPassword,
+                            GString *userPassword) = 0;
+
+  // Construct authorization data, typically by prompting the user for
+  // a password.  Returns an authorization data object, or NULL to
+  // cancel.
+  virtual void *getAuthData() = 0;
+
+  // Free the authorization data returned by makeAuthData or
+  // getAuthData.
+  virtual void freeAuthData(void *authData) = 0;
+
+  // Attempt to authorize the document, using the supplied
+  // authorization data (which may be NULL).  Returns true if
+  // successful (i.e., if at least the right to open the document was
+  // granted).
+  virtual GBool authorize(void *authData) = 0;
+
+  // Return the various authorization parameters.  These are only
+  // valid after authorize has returned true.
+  virtual int getPermissionFlags() = 0;
+  virtual GBool getOwnerPasswordOk() = 0;
+  virtual Guchar *getFileKey() = 0;
+  virtual int getFileKeyLength() = 0;
+  virtual int getEncVersion() = 0;
+
+protected:
+
+  PDFDoc *doc;
+};
+
+//------------------------------------------------------------------------
+// StandardSecurityHandler
+//------------------------------------------------------------------------
+
+class StandardSecurityHandler: public SecurityHandler {
+public:
+
+  StandardSecurityHandler(PDFDoc *docA, Object *encryptDictA);
+  virtual ~StandardSecurityHandler();
+
+  virtual void *makeAuthData(GString *ownerPassword,
+                            GString *userPassword);
+  virtual void *getAuthData();
+  virtual void freeAuthData(void *authData);
+  virtual GBool authorize(void *authData);
+  virtual int getPermissionFlags() { return permFlags; }
+  virtual GBool getOwnerPasswordOk() { return ownerPasswordOk; }
+  virtual Guchar *getFileKey() { return fileKey; }
+  virtual int getFileKeyLength() { return fileKeyLength; }
+  virtual int getEncVersion() { return encVersion; }
+
+private:
+
+  int permFlags;
+  GBool ownerPasswordOk;
+  Guchar fileKey[16];
+  int fileKeyLength;
+  int encVersion;
+  int encRevision;
+  GBool encryptMetadata;
+
+  GString *ownerKey, *userKey;
+  GString *fileID;
+  GBool ok;
+};
+
+#ifdef ENABLE_PLUGINS
+//------------------------------------------------------------------------
+// ExternalSecurityHandler
+//------------------------------------------------------------------------
+
+class ExternalSecurityHandler: public SecurityHandler {
+public:
+
+  ExternalSecurityHandler(PDFDoc *docA, Object *encryptDictA,
+                         XpdfSecurityHandler *xshA);
+  virtual ~ExternalSecurityHandler();
+
+  virtual void *makeAuthData(GString *ownerPassword,
+                            GString *userPassword);
+  virtual void *getAuthData();
+  virtual void freeAuthData(void *authData);
+  virtual GBool authorize(void *authData);
+  virtual int getPermissionFlags() { return permFlags; }
+  virtual GBool getOwnerPasswordOk() { return gFalse; }
+  virtual Guchar *getFileKey() { return fileKey; }
+  virtual int getFileKeyLength() { return fileKeyLength; }
+  virtual int getEncVersion() { return encVersion; }
+
+private:
+
+  Object encryptDict;
+  XpdfSecurityHandler *xsh;
+  void *docData;
+  int permFlags;
+  Guchar fileKey[16];
+  int fileKeyLength;
+  int encVersion;
+  GBool ok;
+};
+#endif // ENABLE_PLUGINS
+
+#endif
index 0b70afa..e1e7bae 100644 (file)
@@ -19,9 +19,7 @@
 #include "gtypes.h"
 #include "Object.h"
 
-#ifndef NO_DECRYPTION
 class Decrypt;
-#endif
 class BaseStream;
 
 //------------------------------------------------------------------------
@@ -40,6 +38,13 @@ enum StreamKind {
   strWeird                     // internal-use stream types
 };
 
+enum StreamColorSpaceMode {
+  streamCSNone,
+  streamCSDeviceGray,
+  streamCSDeviceRGB,
+  streamCSDeviceCMYK
+};
+
 //------------------------------------------------------------------------
 // Stream (base class)
 //------------------------------------------------------------------------
@@ -102,15 +107,14 @@ public:
   // Is this an encoding filter?
   virtual GBool isEncoder() { return gFalse; }
 
+  // Get image parameters which are defined by the stream contents.
+  virtual void getImageParams(int *bitsPerComponent,
+                             StreamColorSpaceMode *csMode) {}
+
   // Add filters to this stream according to the parameters in <dict>.
   // Returns the new stream.
   Stream *addFilters(Object *dict);
 
-  // Tell this stream to ignore any length limitation -- this only
-  // applies to BaseStream subclasses, and is used as a hack to work
-  // around broken PDF files with incorrect stream lengths.
-  virtual void ignoreLength() {}
-
 private:
 
   Stream *makeFilter(char *name, Stream *str, Object *params);
@@ -140,17 +144,13 @@ public:
   virtual Guint getStart() = 0;
   virtual void moveStart(int delta) = 0;
 
-#ifndef NO_DECRYPTION
   // Set decryption for this stream.
   virtual void doDecryption(Guchar *fileKey, int keyLength,
                            int objNum, int objGen);
-#endif
 
-#ifndef NO_DECRYPTION
 protected:
 
   Decrypt *decrypt;
-#endif
 
 private:
 
@@ -173,7 +173,6 @@ public:
   virtual void setPos(Guint pos, int dir = 0);
   virtual BaseStream *getBaseStream() { return str->getBaseStream(); }
   virtual Dict *getDict() { return str->getDict(); }
-  virtual void ignoreLength() { str->ignoreLength(); }
 
 protected:
 
@@ -233,6 +232,8 @@ public:
 
   ~StreamPredictor();
 
+  GBool isOk() { return ok; }
+
   int lookChar();
   int getChar();
 
@@ -250,6 +251,7 @@ private:
   int rowBytes;                        // bytes per line
   Guchar *predLine;            // line buffer
   int predIdx;                 // current index in predLine
+  GBool ok;
 };
 
 //------------------------------------------------------------------------
@@ -275,7 +277,6 @@ public:
     { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }
   virtual int getPos() { return bufPos + (bufPtr - buf); }
   virtual void setPos(Guint pos, int dir = 0);
-  virtual void ignoreLength() { limited = gFalse; }
   virtual Guint getStart() { return start; }
   virtual void moveStart(int delta);
 
@@ -317,10 +318,8 @@ public:
   virtual void setPos(Guint pos, int dir = 0);
   virtual Guint getStart() { return start; }
   virtual void moveStart(int delta);
-#ifndef NO_DECRYPTION
   virtual void doDecryption(Guchar *fileKey, int keyLength,
                            int objNum, int objGen);
-#endif
 
 private:
 
@@ -591,7 +590,7 @@ private:
   GBool gotJFIFMarker;         // set if APP0 JFIF marker was present
   GBool gotAdobeMarker;                // set if APP14 Adobe marker was present
   int restartInterval;         // restart interval, in MCUs
-  Guchar quantTables[4][64];   // quantization tables
+  Gushort quantTables[4][64];  // quantization tables
   int numQuantTables;          // number of quantization tables
   DCTHuffTable dcHuffTables[4];        // DC Huffman tables
   DCTHuffTable acHuffTables[4];        // AC Huffman tables
@@ -616,7 +615,7 @@ private:
                                DCTHuffTable *acHuffTable,
                                int *prevDC, int data[64]);
   void decodeImage();
-  void transformDataUnit(Guchar *quantTable,
+  void transformDataUnit(Gushort *quantTable,
                         int dataIn[64], Guchar dataOut[64]);
   int readHuffSym(DCTHuffTable *table);
   int readAmp(int size);
@@ -700,6 +699,10 @@ private:
     lengthDecode[flateMaxLitCodes-257];
   static FlateDecode           // distance decoding info
     distDecode[flateMaxDistCodes];
+  static FlateHuffmanTab       // fixed literal code table
+    fixedLitCodeTab;
+  static FlateHuffmanTab       // fixed distance code table
+    fixedDistCodeTab;
 
   void readSome();
   GBool startBlock();
index 300d802..2b8cb1f 100644 (file)
@@ -53,7 +53,7 @@ UnicodeMap *UnicodeMap::parse(GString *encodingNameA) {
   map = new UnicodeMap(encodingNameA->copy());
 
   size = 8;
-  map->ranges = (UnicodeMapRange *)gmalloc(size * sizeof(UnicodeMapRange));
+  map->ranges = (UnicodeMapRange *)gmallocn(size, sizeof(UnicodeMapRange));
   eMapsSize = 0;
 
   line = 1;
@@ -69,7 +69,7 @@ UnicodeMap *UnicodeMap::parse(GString *encodingNameA) {
        if (map->len == size) {
          size *= 2;
          map->ranges = (UnicodeMapRange *)
-           grealloc(map->ranges, size * sizeof(UnicodeMapRange));
+           greallocn(map->ranges, size, sizeof(UnicodeMapRange));
        }
        range = &map->ranges[map->len];
        sscanf(tok1, "%x", &range->start);
@@ -81,7 +81,7 @@ UnicodeMap *UnicodeMap::parse(GString *encodingNameA) {
        if (map->eMapsLen == eMapsSize) {
          eMapsSize += 16;
          map->eMaps = (UnicodeMapExt *)
-           grealloc(map->eMaps, eMapsSize * sizeof(UnicodeMapExt));
+           greallocn(map->eMaps, eMapsSize, sizeof(UnicodeMapExt));
        }
        eMap = &map->eMaps[map->eMapsLen];
        sscanf(tok1, "%x", &eMap->u);
index 6fd4ed2..0f86101 100644 (file)
@@ -95,7 +95,7 @@ private:
   UnicodeMapExt *eMaps;                // (user)
   int eMapsLen;                        // (user)
   int refCnt;
-#ifdef MULTITHREADED
+#if MULTITHREADED
   GMutex mutex;
 #endif
 };
index e0d82d2..b371541 100644 (file)
@@ -22,9 +22,6 @@
 #include "Lexer.h"
 #include "Parser.h"
 #include "Dict.h"
-#ifndef NO_DECRYPTION
-#include "Decrypt.h"
-#endif
 #include "Error.h"
 #include "ErrorCodes.h"
 #include "XRef.h"
@@ -34,7 +31,6 @@
 #define xrefSearchSize 1024    // read this many bytes at end of file
                                //   to look for 'startxref'
 
-#ifndef NO_DECRYPTION
 //------------------------------------------------------------------------
 // Permission bits
 //------------------------------------------------------------------------
@@ -44,7 +40,6 @@
 #define permCopy     (1<<4)
 #define permNotes    (1<<5)
 #define defPermFlags 0xfffc
-#endif
 
 //------------------------------------------------------------------------
 // ObjectStream
@@ -96,7 +91,7 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
   }
   nObjects = obj1.getInt();
   obj1.free();
-  if (nObjects == 0) {
+  if (nObjects <= 0) {
     goto err1;
   }
 
@@ -106,10 +101,13 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
   }
   first = obj1.getInt();
   obj1.free();
+  if (first < 0) {
+    goto err1;
+  }
 
   objs = new Object[nObjects];
-  objNums = (int *)gmalloc(nObjects * sizeof(int));
-  offsets = (int *)gmalloc(nObjects * sizeof(int));
+  objNums = (int *)gmallocn(nObjects, sizeof(int));
+  offsets = (int *)gmallocn(nObjects, sizeof(int));
 
   // parse the header: object numbers and offsets
   objStr.streamReset();
@@ -130,6 +128,12 @@ ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
     offsets[i] = obj2.getInt();
     obj1.free();
     obj2.free();
+    if (objNums[i] < 0 || offsets[i] < 0 ||
+       (i > 0 && offsets[i] < offsets[i-1])) {
+      delete parser;
+      gfree(offsets);
+      goto err1;
+    }
   }
   while (str->getChar() != EOF) ;
   delete parser;
@@ -186,7 +190,7 @@ Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
 // XRef
 //------------------------------------------------------------------------
 
-XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
+XRef::XRef(BaseStream *strA) {
   Guint pos;
   Object obj;
 
@@ -198,6 +202,10 @@ XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
   streamEndsLen = 0;
   objStr = NULL;
 
+  encrypted = gFalse;
+  permFlags = defPermFlags;
+  ownerPasswordOk = gFalse;
+
   // read the trailer
   str = strA;
   start = str->getStart();
@@ -242,16 +250,6 @@ XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
   // now set the trailer dictionary's xref pointer so we can fetch
   // indirect objects from it
   trailerDict.getDict()->setXRef(this);
-
-  // check for encryption
-#ifndef NO_DECRYPTION
-  encrypted = gFalse;
-#endif
-  if (checkEncrypted(ownerPassword, userPassword)) {
-    ok = gFalse;
-    errCode = errEncrypted;
-    return;
-  }
 }
 
 XRef::~XRef() {
@@ -369,11 +367,17 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
     }
     n = obj.getInt();
     obj.free();
+    if (first < 0 || n < 0 || first + n < 0) {
+      goto err1;
+    }
     if (first + n > size) {
       for (newSize = size ? 2 * size : 1024;
-          first + n > newSize;
+          first + n > newSize && newSize > 0;
           newSize <<= 1) ;
-      entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
+      if (newSize < 0) {
+       goto err1;
+      }
+      entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
       for (i = size; i < newSize; ++i) {
        entries[i].offset = 0xffffffff;
        entries[i].type = xrefEntryFree;
@@ -443,9 +447,10 @@ GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
 
   // check for an 'XRefStm' key
   if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
-    pos2 = obj2.getInt();
+    pos2 = (Guint)obj2.getInt();
     readXRef(&pos2);
     if (!ok) {
+      obj2.free();
       goto err1;
     }
   }
@@ -474,8 +479,11 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
   }
   newSize = obj.getInt();
   obj.free();
+  if (newSize < 0) {
+    goto err1;
+  }
   if (newSize > size) {
-    entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
+    entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
     for (i = size; i < newSize; ++i) {
       entries[i].offset = 0xffffffff;
       entries[i].type = xrefEntryFree;
@@ -494,6 +502,9 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
     }
     w[i] = obj2.getInt();
     obj2.free();
+    if (w[i] < 0 || w[i] > 4) {
+      goto err1;
+    }
   }
   obj.free();
 
@@ -513,13 +524,14 @@ GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
       }
       n = obj.getInt();
       obj.free();
-      if (!readXRefStreamSection(xrefStr, w, first, n)) {
+      if (first < 0 || n < 0 ||
+         !readXRefStreamSection(xrefStr, w, first, n)) {
        idx.free();
        goto err0;
       }
     }
   } else {
-    if (!readXRefStreamSection(xrefStr, w, 0, size)) {
+    if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
       idx.free();
       goto err0;
     }
@@ -551,11 +563,17 @@ GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
   Guint offset;
   int type, gen, c, newSize, i, j;
 
+  if (first + n < 0) {
+    return gFalse;
+  }
   if (first + n > size) {
     for (newSize = size ? 2 * size : 1024;
-        first + n > newSize;
+        first + n > newSize && newSize > 0;
         newSize <<= 1) ;
-    entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
+    if (newSize < 0) {
+      return gFalse;
+    }
+    entries = (XRefEntry *)greallocn(entries, newSize, sizeof(XRefEntry));
     for (i = size; i < newSize; ++i) {
       entries[i].offset = 0xffffffff;
       entries[i].type = xrefEntryFree;
@@ -585,24 +603,26 @@ GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
       }
       gen = (gen << 8) + c;
     }
-    switch (type) {
-    case 0:
-      entries[i].offset = offset;
-      entries[i].gen = gen;
-      entries[i].type = xrefEntryFree;
-      break;
-    case 1:
-      entries[i].offset = offset;
-      entries[i].gen = gen;
-      entries[i].type = xrefEntryUncompressed;
-      break;
-    case 2:
-      entries[i].offset = offset;
-      entries[i].gen = gen;
-      entries[i].type = xrefEntryCompressed;
-      break;
-    default:
-      return gFalse;
+    if (entries[i].offset == 0xffffffff) {
+      switch (type) {
+      case 0:
+       entries[i].offset = offset;
+       entries[i].gen = gen;
+       entries[i].type = xrefEntryFree;
+       break;
+      case 1:
+       entries[i].offset = offset;
+       entries[i].gen = gen;
+       entries[i].type = xrefEntryUncompressed;
+       break;
+      case 2:
+       entries[i].offset = offset;
+       entries[i].gen = gen;
+       entries[i].type = xrefEntryCompressed;
+       break;
+      default:
+       return gFalse;
+      }
     }
   }
 
@@ -643,7 +663,7 @@ GBool XRef::constructXRef() {
       obj.initNull();
       parser = new Parser(NULL,
                 new Lexer(NULL,
-                  str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
+                  str->makeSubStream(pos + 7, gFalse, 0, &obj)));
       parser->getObj(&newTrailerDict);
       if (newTrailerDict.isDict()) {
        newTrailerDict.dictLookupNF("Root", &obj);
@@ -664,38 +684,44 @@ GBool XRef::constructXRef() {
     // look for object
     } else if (isdigit(*p)) {
       num = atoi(p);
-      do {
-       ++p;
-      } while (*p && isdigit(*p));
-      if (isspace(*p)) {
+      if (num > 0) {
        do {
          ++p;
-       } while (*p && isspace(*p));
-       if (isdigit(*p)) {
-         gen = atoi(p);
+       } while (*p && isdigit(*p));
+       if (isspace(*p)) {
          do {
            ++p;
-         } while (*p && isdigit(*p));
-         if (isspace(*p)) {
+         } while (*p && isspace(*p));
+         if (isdigit(*p)) {
+           gen = atoi(p);
            do {
              ++p;
-           } while (*p && isspace(*p));
-           if (!strncmp(p, "obj", 3)) {
-             if (num >= size) {
-               newSize = (num + 1 + 255) & ~255;
-               entries = (XRefEntry *)
-                           grealloc(entries, newSize * sizeof(XRefEntry));
-               for (i = size; i < newSize; ++i) {
-                 entries[i].offset = 0xffffffff;
-                 entries[i].type = xrefEntryFree;
+           } while (*p && isdigit(*p));
+           if (isspace(*p)) {
+             do {
+               ++p;
+             } while (*p && isspace(*p));
+             if (!strncmp(p, "obj", 3)) {
+               if (num >= size) {
+                 newSize = (num + 1 + 255) & ~255;
+                 if (newSize < 0) {
+                   error(-1, "Bad object number");
+                   return gFalse;
+                 }
+                 entries = (XRefEntry *)
+                     greallocn(entries, newSize, sizeof(XRefEntry));
+                 for (i = size; i < newSize; ++i) {
+                   entries[i].offset = 0xffffffff;
+                   entries[i].type = xrefEntryFree;
+                 }
+                 size = newSize;
+               }
+               if (entries[num].type == xrefEntryFree ||
+                   gen >= entries[num].gen) {
+                 entries[num].offset = pos - start;
+                 entries[num].gen = gen;
+                 entries[num].type = xrefEntryUncompressed;
                }
-               size = newSize;
-             }
-             if (entries[num].type == xrefEntryFree ||
-                 gen >= entries[num].gen) {
-               entries[num].offset = pos - start;
-               entries[num].gen = gen;
-               entries[num].type = xrefEntryUncompressed;
              }
            }
          }
@@ -705,8 +731,8 @@ GBool XRef::constructXRef() {
     } else if (!strncmp(p, "endstream", 9)) {
       if (streamEndsLen == streamEndsSize) {
        streamEndsSize += 64;
-       streamEnds = (Guint *)grealloc(streamEnds,
-                                      streamEndsSize * sizeof(int));
+       streamEnds = (Guint *)greallocn(streamEnds,
+                                       streamEndsSize, sizeof(int));
       }
       streamEnds[streamEndsLen++] = pos;
     }
@@ -719,137 +745,38 @@ GBool XRef::constructXRef() {
   return gFalse;
 }
 
-#ifndef NO_DECRYPTION
-GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
-  Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
-  Object ownerKey, userKey, permissions, fileID, fileID1;
-  GBool encrypted1;
-  GBool ret;
-
-  keyLength = 0;
-  encVersion = encRevision = 0;
-  ret = gFalse;
+void XRef::setEncryption(int permFlagsA, GBool ownerPasswordOkA,
+                        Guchar *fileKeyA, int keyLengthA, int encVersionA) {
+  int i;
 
-  permFlags = defPermFlags;
-  ownerPasswordOk = gFalse;
-  trailerDict.dictLookup("Encrypt", &encrypt);
-  if ((encrypted1 = encrypt.isDict())) {
-    ret = gTrue;
-    encrypt.dictLookup("Filter", &filterObj);
-    if (filterObj.isName("Standard")) {
-      encrypt.dictLookup("V", &versionObj);
-      encrypt.dictLookup("R", &revisionObj);
-      encrypt.dictLookup("Length", &lengthObj);
-      encrypt.dictLookup("O", &ownerKey);
-      encrypt.dictLookup("U", &userKey);
-      encrypt.dictLookup("P", &permissions);
-      trailerDict.dictLookup("ID", &fileID);
-      if (versionObj.isInt() &&
-         revisionObj.isInt() &&
-         ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
-         userKey.isString() && userKey.getString()->getLength() == 32 &&
-         permissions.isInt() &&
-         fileID.isArray()) {
-       encVersion = versionObj.getInt();
-       encRevision = revisionObj.getInt();
-       if (lengthObj.isInt()) {
-         keyLength = lengthObj.getInt() / 8;
-       } else {
-         keyLength = 5;
-       }
-       permFlags = permissions.getInt();
-       if (encVersion >= 1 && encVersion <= 2 &&
-           encRevision >= 2 && encRevision <= 3) {
-         fileID.arrayGet(0, &fileID1);
-         if (fileID1.isString()) {
-           if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
-                                    ownerKey.getString(), userKey.getString(),
-                                    permFlags, fileID1.getString(),
-                                    ownerPassword, userPassword, fileKey,
-                                    &ownerPasswordOk)) {
-             if (ownerPassword && !ownerPasswordOk) {
-               error(-1, "Incorrect owner password");
-             }
-             ret = gFalse;
-           } else {
-             error(-1, "Incorrect password");
-           }
-         } else {
-           error(-1, "Weird encryption info");
-         }
-         fileID1.free();
-       } else {
-         error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
-               encVersion, encRevision);
-       }
-      } else {
-       error(-1, "Weird encryption info");
-      }
-      fileID.free();
-      permissions.free();
-      userKey.free();
-      ownerKey.free();
-      lengthObj.free();
-      revisionObj.free();
-      versionObj.free();
-    } else {
-      error(-1, "Unknown security handler '%s'",
-           filterObj.isName() ? filterObj.getName() : "???");
-    }
-    filterObj.free();
+  encrypted = gTrue;
+  permFlags = permFlagsA;
+  ownerPasswordOk = ownerPasswordOkA;
+  if (keyLengthA <= 16) {
+    keyLength = keyLengthA;
+  } else {
+    keyLength = 16;
   }
-  encrypt.free();
-
-  // this flag has to be set *after* we read the O/U/P strings
-  encrypted = encrypted1;
-
-  return ret;
-}
-#else
-GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
-  Object obj;
-  GBool encrypted;
-
-  trailerDict.dictLookup("Encrypt", &obj);
-  if ((encrypted = !obj.isNull())) {
-    error(-1, "PDF file is encrypted and this version of the Xpdf tools");
-    error(-1, "was built without decryption support.");
+  for (i = 0; i < keyLength; ++i) {
+    fileKey[i] = fileKeyA[i];
   }
-  obj.free();
-  return encrypted;
+  encVersion = encVersionA;
 }
-#endif
 
 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
-#ifndef NO_DECRYPTION
   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
-#else
-  return gTrue;
-#endif
 }
 
 GBool XRef::okToChange(GBool ignoreOwnerPW) {
-#ifndef NO_DECRYPTION
   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
-#else
-  return gTrue;
-#endif
 }
 
 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
-#ifndef NO_DECRYPTION
   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
-#else
-  return gTrue;
-#endif
 }
 
 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
-#ifndef NO_DECRYPTION
   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
-#else
-  return gTrue;
-#endif
 }
 
 Object *XRef::fetch(int num, int gen, Object *obj) {
@@ -879,14 +806,14 @@ Object *XRef::fetch(int num, int gen, Object *obj) {
     if (!obj1.isInt() || obj1.getInt() != num ||
        !obj2.isInt() || obj2.getInt() != gen ||
        !obj3.isCmd("obj")) {
+      obj1.free();
+      obj2.free();
+      obj3.free();
+      delete parser;
       goto err;
     }
-#ifndef NO_DECRYPTION
     parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
                   num, gen);
-#else
-    parser->getObj(obj);
-#endif
     obj1.free();
     obj2.free();
     obj3.free();
index bec487a..f9dede3 100644 (file)
@@ -43,7 +43,7 @@ class XRef {
 public:
 
   // Constructor.  Read xref table from stream.
-  XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword);
+  XRef(BaseStream *strA);
 
   // Destructor.
   ~XRef();
@@ -54,12 +54,12 @@ public:
   // Get the error code (if isOk() returns false).
   int getErrorCode() { return errCode; }
 
+  // Set the encryption parameters.
+  void setEncryption(int permFlagsA, GBool ownerPasswordOkA,
+                    Guchar *fileKeyA, int keyLengthA, int encVersionA);
+
   // Is the file encrypted?
-#ifndef NO_DECRYPTION
   GBool isEncrypted() { return encrypted; }
-#else
-  GBool isEncrypted() { return gFalse; }
-#endif
 
   // Check various permissions.
   GBool okToPrint(GBool ignoreOwnerPW = gFalse);
@@ -112,15 +112,12 @@ private:
                                //   damaged files
   int streamEndsLen;           // number of valid entries in streamEnds
   ObjectStream *objStr;                // cached object stream
-#ifndef NO_DECRYPTION
   GBool encrypted;             // true if file is encrypted
-  int encVersion;              // encryption algorithm
-  int encRevision;             // security handler revision
-  int keyLength;               // length of key, in bytes
   int permFlags;               // permission bits
-  Guchar fileKey[16];          // file decryption key
   GBool ownerPasswordOk;       // true if owner password is correct
-#endif
+  Guchar fileKey[16];          // file decryption key
+  int keyLength;               // length of key, in bytes
+  int encVersion;              // encryption algorithm
 
   Guint getStartXref();
   GBool readXRef(Guint *pos);
@@ -128,7 +125,6 @@ private:
   GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n);
   GBool readXRefStream(Stream *xrefStr, Guint *pos);
   GBool constructXRef();
-  GBool checkEncrypted(GString *ownerPassword, GString *userPassword);
   Guint strToUnsigned(char *s);
 };
 
index 07bbf81..a0f2cf5 100644 (file)
@@ -50,6 +50,7 @@ static GMemHdr *gMemList[gMemNLists] = {
 
 static int gMemIndex = 0;
 static int gMemAlloc = 0;
+static int gMemInUse = 0;
 
 #endif /* DEBUG_MEM */
 
@@ -78,6 +79,7 @@ void *gmalloc(int size) {
   hdr->next = gMemList[lst];
   gMemList[lst] = hdr;
   ++gMemAlloc;
+  gMemInUse += size;
   for (p = (unsigned long *)data; p <= trl; ++p)
     *p = gMemDeadVal;
   return data;
@@ -135,6 +137,28 @@ void *grealloc(void *p, int size) {
 #endif
 }
 
+void *gmallocn(int nObjs, int objSize) {
+  int n;
+
+  n = nObjs * objSize;
+  if (objSize == 0 || n / objSize != nObjs) {
+    fprintf(stderr, "Bogus memory allocation size\n");
+    exit(1);
+  }
+  return gmalloc(n);
+}
+
+void *greallocn(void *p, int nObjs, int objSize) {
+  int n;
+
+  n = nObjs * objSize;
+  if (objSize == 0 || n / objSize != nObjs) {
+    fprintf(stderr, "Bogus memory allocation size\n");
+    exit(1);
+  }
+  return grealloc(p, n);
+}
+
 void gfree(void *p) {
 #ifdef DEBUG_MEM
   int size;
@@ -156,6 +180,7 @@ void gfree(void *p) {
       else
        gMemList[lst] = hdr->next;
       --gMemAlloc;
+      gMemInUse -= hdr->size;
       size = gMemDataSize(hdr->size);
       trl = (unsigned long *)((char *)hdr + gMemHdrSize + size);
       if (*trl != gMemDeadVal) {
index 587e7fa..e74d182 100644 (file)
@@ -28,6 +28,15 @@ extern void *gmalloc(int size);
 extern void *grealloc(void *p, int size);
 
 /*
+ * These are similar to gmalloc and grealloc, but take an object count
+ * and size.  The result is similar to allocating nObjs * objSize
+ * bytes, but there is an additional error check that the total size
+ * doesn't overflow an int.
+ */
+extern void *gmallocn(int nObjs, int objSize);
+extern void *greallocn(void *p, int nObjs, int objSize);
+
+/*
  * Same as free, but checks for and ignores NULL pointers.
  */
 extern void gfree(void *p);