upgrade to xpdf 3.00.
[swftools.git] / pdf2swf / xpdf / Stream.cc
index c47c5af..49bbb46 100644 (file)
@@ -2,22 +2,21 @@
 //
 // Stream.cc
 //
-// Copyright 1996-2002 Glyph & Cog, LLC
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
-#include <aconf.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
 #ifndef WIN32
 #include <unistd.h>
-#else
-extern "C" int unlink(char *filename);
 #endif
 #include <string.h>
 #include <ctype.h>
@@ -30,6 +29,8 @@ extern "C" int unlink(char *filename);
 #include "Decrypt.h"
 #endif
 #include "Stream.h"
+#include "JBIG2Stream.h"
+#include "JPXStream.h"
 #include "Stream-CCITT.h"
 
 #ifdef __DJGPP__
@@ -37,9 +38,6 @@ static GBool setDJSYSFLAGS = gFalse;
 #endif
 
 #ifdef VMS
-#if (__VMS_VER < 70000000)
-extern "C" int unlink(char *filename);
-#endif
 #ifdef __GNUC__
 #define SEEK_SET 0
 #define SEEK_CUR 1
@@ -47,10 +45,6 @@ extern "C" int unlink(char *filename);
 #endif
 #endif
 
-#ifdef MACOS
-#include "StuffItEngineLib.h"
-#endif
-
 //------------------------------------------------------------------------
 // Stream (base class)
 //------------------------------------------------------------------------
@@ -91,7 +85,7 @@ char *Stream::getLine(char *buf, int size) {
   return buf;
 }
 
-GString *Stream::getPSFilter(char *indent) {
+GString *Stream::getPSFilter(int psLevel, char *indent) {
   return new GString();
 }
 
@@ -147,7 +141,7 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
   int encoding;
   GBool endOfLine, byteAlign, endOfBlock, black;
   int columns, rows;
-  Object obj;
+  Object globals, obj;
 
   if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) {
     str = new ASCIIHexStream(str);
@@ -257,6 +251,14 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
       obj.free();
     }
     str = new FlateStream(str, pred, columns, colors, bits);
+  } else if (!strcmp(name, "JBIG2Decode")) {
+    if (params->isDict()) {
+      params->dictLookup("JBIG2Globals", &globals);
+    }
+    str = new JBIG2Stream(str, &globals);
+    globals.free();
+  } else if (!strcmp(name, "JPXDecode")) {
+    str = new JPXStream(str);
   } else {
     error(getPos(), "Unknown filter '%s'", name);
     str = new EOFStream(str);
@@ -340,51 +342,54 @@ void ImageStream::reset() {
 }
 
 GBool ImageStream::getPixel(Guchar *pix) {
+  int i;
+
+  if (imgIdx >= nVals) {
+    getLine();
+    imgIdx = 0;
+  }
+  for (i = 0; i < nComps; ++i) {
+    pix[i] = imgLine[imgIdx++];
+  }
+  return gTrue;
+}
+
+Guchar *ImageStream::getLine() {
   Gulong buf, bitMask;
   int bits;
   int c;
   int i;
 
-  if (imgIdx >= nVals) {
-
-    // read one line of image pixels
-    if (nBits == 1) {
-      for (i = 0; i < nVals; i += 8) {
-       c = str->getChar();
-       imgLine[i+0] = (Guchar)((c >> 7) & 1);
-       imgLine[i+1] = (Guchar)((c >> 6) & 1);
-       imgLine[i+2] = (Guchar)((c >> 5) & 1);
-       imgLine[i+3] = (Guchar)((c >> 4) & 1);
-       imgLine[i+4] = (Guchar)((c >> 3) & 1);
-       imgLine[i+5] = (Guchar)((c >> 2) & 1);
-       imgLine[i+6] = (Guchar)((c >> 1) & 1);
-       imgLine[i+7] = (Guchar)(c & 1);
-      }
-    } else if (nBits == 8) {
-      for (i = 0; i < nVals; ++i) {
-       imgLine[i] = str->getChar();
-      }
-    } else {
-      bitMask = (1 << nBits) - 1;
-      buf = 0;
-      bits = 0;
-      for (i = 0; i < nVals; ++i) {
-       if (bits < nBits) {
-         buf = (buf << 8) | (str->getChar() & 0xff);
-         bits += 8;
-       }
-       imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
-       bits -= nBits;
+  if (nBits == 1) {
+    for (i = 0; i < nVals; i += 8) {
+      c = str->getChar();
+      imgLine[i+0] = (Guchar)((c >> 7) & 1);
+      imgLine[i+1] = (Guchar)((c >> 6) & 1);
+      imgLine[i+2] = (Guchar)((c >> 5) & 1);
+      imgLine[i+3] = (Guchar)((c >> 4) & 1);
+      imgLine[i+4] = (Guchar)((c >> 3) & 1);
+      imgLine[i+5] = (Guchar)((c >> 2) & 1);
+      imgLine[i+6] = (Guchar)((c >> 1) & 1);
+      imgLine[i+7] = (Guchar)(c & 1);
+    }
+  } else if (nBits == 8) {
+    for (i = 0; i < nVals; ++i) {
+      imgLine[i] = str->getChar();
+    }
+  } else {
+    bitMask = (1 << nBits) - 1;
+    buf = 0;
+    bits = 0;
+    for (i = 0; i < nVals; ++i) {
+      if (bits < nBits) {
+       buf = (buf << 8) | (str->getChar() & 0xff);
+       bits += 8;
       }
+      imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
+      bits -= nBits;
     }
-
-    // reset to start of line
-    imgIdx = 0;
   }
-
-  for (i = 0; i < nComps; ++i)
-    pix[i] = imgLine[imgIdx++];
-  return gTrue;
+  return imgLine;
 }
 
 void ImageStream::skipLine() {
@@ -448,7 +453,7 @@ GBool StreamPredictor::getNextLine() {
   int i, j, k;
 
   // get PNG optimum predictor number
-  if (predictor == 15) {
+  if (predictor >= 10) {
     if ((curPred = str->getRawChar()) == EOF) {
       return gFalse;
     }
@@ -465,7 +470,7 @@ GBool StreamPredictor::getNextLine() {
     upLeftBuf[1] = upLeftBuf[0];
     upLeftBuf[0] = predLine[i];
     if ((c = str->getRawChar()) == EOF) {
-      break;
+      return gFalse;
     }
     switch (curPred) {
     case 11:                   // PNG sub
@@ -504,7 +509,6 @@ GBool StreamPredictor::getNextLine() {
   }
 
   // apply TIFF (component) predictor
-  //~ this is completely untested
   if (predictor == 2) {
     if (nBits == 1) {
       inBuf = predLine[pixBytes - 1];
@@ -579,7 +583,10 @@ Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
 }
 
 void FileStream::reset() {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+  savePos = (Guint)ftello(f);
+  fseeko(f, start, SEEK_SET);
+#elif HAVE_FSEEK64
   savePos = (Guint)ftell64(f);
   fseek64(f, start, SEEK_SET);
 #else
@@ -597,7 +604,9 @@ void FileStream::reset() {
 
 void FileStream::close() {
   if (saved) {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, savePos, SEEK_SET);
+#elif HAVE_FSEEK64
     fseek64(f, savePos, SEEK_SET);
 #else
     fseek(f, savePos, SEEK_SET);
@@ -641,14 +650,19 @@ void FileStream::setPos(Guint pos, int dir) {
   Guint size;
 
   if (dir >= 0) {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, pos, SEEK_SET);
+#elif HAVE_FSEEK64
     fseek64(f, pos, SEEK_SET);
 #else
     fseek(f, pos, SEEK_SET);
 #endif
     bufPos = pos;
   } else {
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, 0, SEEK_END);
+    size = (Guint)ftello(f);
+#elif HAVE_FSEEK64
     fseek64(f, 0, SEEK_END);
     size = (Guint)ftell64(f);
 #else
@@ -661,7 +675,10 @@ void FileStream::setPos(Guint pos, int dir) {
     //~ work around a bug in cygwin's implementation of fseek
     rewind(f);
 #endif
-#if HAVE_FSEEK64
+#if HAVE_FSEEKO
+    fseeko(f, -(int)pos, SEEK_END);
+    bufPos = (Guint)ftello(f);
+#elif HAVE_FSEEK64
     fseek64(f, -(int)pos, SEEK_END);
     bufPos = (Guint)ftell64(f);
 #else
@@ -682,13 +699,14 @@ void FileStream::moveStart(int delta) {
 // MemStream
 //------------------------------------------------------------------------
 
-MemStream::MemStream(char *bufA, Guint lengthA, Object *dictA):
+MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA):
     BaseStream(dictA) {
   buf = bufA;
-  needFree = gFalse;
+  start = startA;
   length = lengthA;
-  bufEnd = buf + length;
-  bufPtr = buf;
+  bufEnd = buf + start + length;
+  bufPtr = buf + start;
+  needFree = gFalse;
 }
 
 MemStream::~MemStream() {
@@ -697,20 +715,22 @@ MemStream::~MemStream() {
   }
 }
 
-Stream *MemStream::makeSubStream(Guint start, GBool limited,
+Stream *MemStream::makeSubStream(Guint startA, GBool limited,
                                 Guint lengthA, Object *dictA) {
+  MemStream *subStr;
   Guint newLength;
 
-  if (!limited || start + lengthA > length) {
-    newLength = length - start;
+  if (!limited || startA + lengthA > start + length) {
+    newLength = start + length - startA;
   } else {
     newLength = lengthA;
   }
-  return new MemStream(buf + start, newLength, dictA);
+  subStr = new MemStream(buf, startA, newLength, dictA);
+  return subStr;
 }
 
 void MemStream::reset() {
-  bufPtr = buf;
+  bufPtr = buf + start;
 #ifndef NO_DECRYPTION
   if (decrypt) {
     decrypt->reset();
@@ -722,24 +742,24 @@ void MemStream::close() {
 }
 
 void MemStream::setPos(Guint pos, int dir) {
+  Guint i;
+
   if (dir >= 0) {
-    if (pos > length) {
-      bufPtr = bufEnd;
-    } else {
-      bufPtr = buf + pos;
-    }
+    i = pos;
   } else {
-    if (pos > length) {
-      bufPtr = buf;
-    } else {
-      bufPtr = bufEnd - pos;
-    }
+    i = start + length - pos;
+  }
+  if (i < start) {
+    i = start;
+  } else if (i > start + length) {
+    i = start + length;
   }
+  bufPtr = buf + i;
 }
 
 void MemStream::moveStart(int delta) {
-  buf += delta;
-  bufPtr = buf;
+  start += delta;
+  bufPtr = buf + start;
 }
 
 #ifndef NO_DECRYPTION
@@ -750,12 +770,13 @@ void MemStream::doDecryption(Guchar *fileKey, int keyLength,
 
   this->BaseStream::doDecryption(fileKey, keyLength, objNum, objGen);
   if (decrypt) {
-    newBuf = (char *)gmalloc(bufEnd - buf);
-    for (p = buf, q = newBuf; p < bufEnd; ++p, ++q) {
+    newBuf = (char *)gmalloc(length);
+    for (p = buf + start, q = newBuf; p < bufEnd; ++p, ++q) {
       *q = (char)decrypt->decryptByte((Guchar)*p);
     }
-    bufEnd = newBuf + (bufEnd - buf);
-    bufPtr = newBuf + (bufPtr - buf);
+    bufEnd = newBuf + length;
+    bufPtr = newBuf + (bufPtr - (buf + start));
+    start = 0;
     buf = newBuf;
     needFree = gTrue;
   }
@@ -766,20 +787,38 @@ void MemStream::doDecryption(Guchar *fileKey, int keyLength,
 // EmbedStream
 //------------------------------------------------------------------------
 
-EmbedStream::EmbedStream(Stream *strA, Object *dictA):
+EmbedStream::EmbedStream(Stream *strA, Object *dictA,
+                        GBool limitedA, Guint lengthA):
     BaseStream(dictA) {
   str = strA;
+  limited = limitedA;
+  length = lengthA;
 }
 
 EmbedStream::~EmbedStream() {
 }
 
-Stream *EmbedStream::makeSubStream(Guint start, GBool limited,
-                                  Guint length, Object *dictA) {
+Stream *EmbedStream::makeSubStream(Guint start, GBool limitedA,
+                                  Guint lengthA, Object *dictA) {
   error(-1, "Internal: called makeSubStream() on EmbedStream");
   return NULL;
 }
 
+int EmbedStream::getChar() {
+  if (limited && !length) {
+    return EOF;
+  }
+  --length;
+  return str->getChar();
+}
+
+int EmbedStream::lookChar() {
+  if (limited && !length) {
+    return EOF;
+  }
+  return str->lookChar();
+}
+
 void EmbedStream::setPos(Guint pos, int dir) {
   error(-1, "Internal: called setPos() on EmbedStream");
 }
@@ -866,10 +905,13 @@ int ASCIIHexStream::lookChar() {
   return buf;
 }
 
-GString *ASCIIHexStream::getPSFilter(char *indent) {
+GString *ASCIIHexStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/ASCIIHexDecode filter\n");
@@ -944,10 +986,13 @@ int ASCII85Stream::lookChar() {
   return b[index];
 }
 
-GString *ASCII85Stream::getPSFilter(char *indent) {
+GString *ASCII85Stream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/ASCII85Decode filter\n");
@@ -971,21 +1016,12 @@ LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors,
     pred = NULL;
   }
   early = earlyA;
-  zPipe = NULL;
-  bufPtr = bufEnd = buf;
+  eof = gFalse;
+  inputBits = 0;
+  clearTable();
 }
 
 LZWStream::~LZWStream() {
-  if (zPipe) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName->getCString());
-    delete zName;
-  }
   if (pred) {
     delete pred;
   }
@@ -996,275 +1032,149 @@ int LZWStream::getChar() {
   if (pred) {
     return pred->getChar();
   }
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff);
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex++];
 }
 
 int LZWStream::lookChar() {
   if (pred) {
     return pred->lookChar();
   }
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff);
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex];
 }
 
 int LZWStream::getRawChar() {
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff);
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex++];
 }
 
 void LZWStream::reset() {
-  FILE *f;
-  GString *zCmd;
-
-  //----- close old LZW stream
-  if (zPipe) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName->getCString());
-    delete zName;
-  }
+  str->reset();
+  eof = gFalse;
+  inputBits = 0;
+  clearTable();
+}
 
-  //----- tell Delorie runtime to spawn a new instance of COMMAND.COM
-  //      to run gzip
-#if __DJGPP__
-  if (!setDJSYSFLAGS) {
-    setenv("DJSYSFLAGS", "0x0002", 0);
-    setDJSYSFLAGS = gTrue;
-  }
-#endif
+GBool LZWStream::processNextCode() {
+  int code;
+  int nextLength;
+  int i, j;
 
-  //----- create the .Z file
-  if (!openTempFile(&zName, &f, "wb", ".Z")) {
-    error(getPos(), "Couldn't create temporary file for LZW stream");
-    return;
+  // check for EOF
+  if (eof) {
+    return gFalse;
   }
-  dumpFile(f);
-  fclose(f);
 
-  //----- execute uncompress / gzip
-#ifdef WIN32
-  zCmd = new GString("c:\\swftools\\gzip.exe");
-#else
-  zCmd = new GString(uncompressCmd);
-#endif
-  zCmd->append(' ');
-  zCmd->append(zName);
-#if defined(MACOS)
-  long magicCookie;
-  // first we open the engine up
-  OSErr err = OpenSITEngine(kUseExternalEngine, &magicCookie);
-  // if we found it - let's use it!
-  if (!err && magicCookie) {
-    // make sure we have the correct version of the Engine
-    if (GetSITEngineVersion(magicCookie) >= kFirstSupportedEngine) {
-      FSSpec myFSS;
-      Str255 pName;
-      strcpy((char *)pName, zName->getCString());
-      c2pstr((char *)pName);
-      FSMakeFSSpec(0, 0, pName, &myFSS);
-      short ftype = DetermineFileType(magicCookie, &myFSS);
-      OSErr expandErr = ExpandFSSpec(magicCookie, ftype, &myFSS,
-                                    NULL, NULL, kCreateFolderNever,
-                                    kDeleteOriginal, kTextConvertSmart);
-    }
-  }
-#elif defined(HAVE_POPEN)
-  if (!(zPipe = popen(zCmd->getCString(), POPEN_READ_MODE))) {
-    error(getPos(), "Couldn't popen '%s'", zCmd->getCString());
-    unlink(zName->getCString());
-    delete zName;
-    return;
-  }
-#else // HAVE_POPEN
-  if (!executeCommand(zCmd->getCString())) {
-    error(getPos(), "Couldn't execute '%s'", zCmd->getCString());
-    unlink(zName->getCString());
-    delete zName;
-    return;
+  // check for eod and clear-table codes
+ start:
+  code = getCode();
+  if (code == EOF || code == 257) {
+    eof = gTrue;
+    return gFalse;
   }
-  zName->del(zName->getLength() - 2, 2);
-  if (!(zPipe = fopen(zName->getCString(), "rb"))) {
-    error(getPos(), "Couldn't open uncompress file '%s'", zName->getCString());
-    unlink(zName->getCString());
-    delete zName;
-    return;
+  if (code == 256) {
+    clearTable();
+    goto start;
+  }
+  if (nextCode >= 4097) {
+    error(getPos(), "Bad LZW stream - expected clear-table code");
+    clearTable();
+  }
+
+  // process the next code
+  nextLength = seqLength + 1;
+  if (code < 256) {
+    seqBuf[0] = code;
+    seqLength = 1;
+  } else if (code < nextCode) {
+    seqLength = table[code].length;
+    for (i = seqLength - 1, j = code; i > 0; --i) {
+      seqBuf[i] = table[j].tail;
+      j = table[j].head;
+    }
+    seqBuf[0] = j;
+  } else if (code == nextCode) {
+    seqBuf[seqLength] = newChar;
+    ++seqLength;
+  } else {
+    error(getPos(), "Bad LZW stream - unexpected code");
+    eof = gTrue;
+    return gFalse;
   }
-#endif // HAVE_POPEN
-
-  //----- clean up
-  delete zCmd;
+  newChar = seqBuf[0];
+  if (first) {
+    first = gFalse;
+  } else {
+    table[nextCode].length = nextLength;
+    table[nextCode].head = prevCode;
+    table[nextCode].tail = newChar;
+    ++nextCode;
+    if (nextCode + early == 512)
+      nextBits = 10;
+    else if (nextCode + early == 1024)
+      nextBits = 11;
+    else if (nextCode + early == 2048)
+      nextBits = 12;
+  }
+  prevCode = code;
+
+  // reset buffer
+  seqIndex = 0;
 
-  //----- initialize buffer
-  bufPtr = bufEnd = buf;
+  return gTrue;
 }
 
-void LZWStream::dumpFile(FILE *f) {
-  int outCodeBits;             // size of output code
-  int outBits;                 // max output code
-  int outBuf[8];               // output buffer
-  int outData;                 // temporary output buffer
-  int inCode, outCode;         // input and output codes
-  int nextCode;                        // next code index
-  GBool eof;                   // set when EOF is reached
-  GBool clear;                 // set if table needs to be cleared
-  GBool first;                 // indicates first code word after clear
-  int i, j;
-
-  str->reset();
-
-  // magic number
-  fputc(0x1f, f);
-  fputc(0x9d, f);
-
-  // max code length, block mode flag
-  fputc(0x8c, f);
-
-  // init input side
-  inCodeBits = 9;
-  inputBuf = 0;
-  inputBits = 0;
-  eof = gFalse;
-
-  // init output side
-  outCodeBits = 9;
-
-  // clear table
-  first = gTrue;
+void LZWStream::clearTable() {
   nextCode = 258;
-
-  clear = gFalse;
-  do {
-    for (i = 0; i < 8; ++i) {
-      // check for table overflow
-      if (nextCode + early > 0x1001) {
-       inCode = 256;
-
-      // read input code
-      } else {
-       do {
-         inCode = getCode();
-         if (inCode == EOF) {
-           eof = gTrue;
-           inCode = 0;
-         }
-       } while (first && inCode == 256);
-      }
-
-      // compute output code
-      if (inCode < 256) {
-       outCode = inCode;
-      } else if (inCode == 256) {
-       outCode = 256;
-       clear = gTrue;
-      } else if (inCode == 257) {
-       outCode = 0;
-       eof = gTrue;
-      } else {
-       outCode = inCode - 1;
-      }
-      outBuf[i] = outCode;
-
-      // next code index
-      if (first)
-       first = gFalse;
-      else
-       ++nextCode;
-
-      // check input code size
-      if (nextCode + early == 0x200)
-       inCodeBits = 10;
-      else if (nextCode + early == 0x400) {
-       inCodeBits = 11;
-      } else if (nextCode + early == 0x800) {
-       inCodeBits = 12;
-      }
-
-      // check for eof/clear
-      if (eof)
-       break;
-      if (clear) {
-       i = 8;
-       break;
-      }
-    }
-
-    // write output block
-    outData = 0;
-    outBits = 0;
-    j = 0;
-    while (j < i || outBits > 0) {
-      if (outBits < 8 && j < i) {
-       outData = outData | (outBuf[j++] << outBits);
-       outBits += outCodeBits;
-      }
-      fputc(outData & 0xff, f);
-      outData >>= 8;
-      outBits -= 8;
-    }
-
-    // check output code size
-    if (nextCode - 1 == 512 ||
-       nextCode - 1 == 1024 ||
-       nextCode - 1 == 2048 ||
-       nextCode - 1 == 4096) {
-      outCodeBits = inCodeBits;
-    }
-
-    // clear table if necessary
-    if (clear) {
-      inCodeBits = 9;
-      outCodeBits = 9;
-      first = gTrue;
-      nextCode = 258;
-      clear = gFalse;
-    }
-  } while (!eof);
+  nextBits = 9;
+  seqIndex = seqLength = 0;
+  first = gTrue;
 }
 
 int LZWStream::getCode() {
   int c;
   int code;
 
-  while (inputBits < inCodeBits) {
+  while (inputBits < nextBits) {
     if ((c = str->getChar()) == EOF)
       return EOF;
     inputBuf = (inputBuf << 8) | (c & 0xff);
     inputBits += 8;
   }
-  code = (inputBuf >> (inputBits - inCodeBits)) & ((1 << inCodeBits) - 1);
-  inputBits -= inCodeBits;
+  code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);
+  inputBits -= nextBits;
   return code;
 }
 
-GBool LZWStream::fillBuf() {
-  int n;
-
-  if (!zPipe)
-    return gFalse;
-  if ((n = fread(buf, 1, 256, zPipe)) < 256) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName->getCString());
-    delete zName;
-  }
-  bufPtr = buf;
-  bufEnd = buf + n;
-  return n > 0;
-}
-
-GString *LZWStream::getPSFilter(char *indent) {
+GString *LZWStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (pred) {
+  if (psLevel < 2 || pred) {
     return NULL;
   }
-  if (!(s = str->getPSFilter(indent))) {
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/LZWDecode filter\n");
@@ -1295,10 +1205,13 @@ void RunLengthStream::reset() {
   eof = gFalse;
 }
 
-GString *RunLengthStream::getPSFilter(char *indent) {
+GString *RunLengthStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("/RunLengthDecode filter\n");
@@ -1371,7 +1284,7 @@ CCITTFaxStream::~CCITTFaxStream() {
 }
 
 void CCITTFaxStream::reset() {
-  int n;
+  short code1;
 
   str->reset();
   eof = gFalse;
@@ -1383,16 +1296,13 @@ void CCITTFaxStream::reset() {
   a0 = 1;
   buf = EOF;
 
-  // get initial end-of-line marker and 2D encoding tag
-  if (endOfBlock) {
-    if (lookBits(12) == 0x001) {
-      eatBits(12);
-    }
-  } else {
-    for (n = 0; n < 11 && lookBits(n) == 0; ++n) ;
-    if (n == 11 && lookBits(12) == 0x001) {
-      eatBits(12);
-    }
+  // skip any initial zero bits and end-of-line marker, and get the 2D
+  // encoding tag
+  while ((code1 = lookBits(12)) == 0) {
+    eatBits(1);
+  }
+  if (code1 == 0x001) {
+    eatBits(12);
   }
   if (encoding > 0) {
     nextLine2D = !lookBits(1);
@@ -1403,10 +1313,7 @@ void CCITTFaxStream::reset() {
 int CCITTFaxStream::lookChar() {
   short code1, code2, code3;
   int a0New;
-#if 0
-  GBool err;
-#endif
-  GBool gotEOL;
+  GBool err, gotEOL;
   int ret;
   int bits, i;
 
@@ -1416,9 +1323,7 @@ int CCITTFaxStream::lookChar() {
   }
 
   // read the next row
-#if 0
   err = gFalse;
-#endif
   if (codingLine[a0] >= columns) {
 
     // 2-D encoding
@@ -1455,12 +1360,14 @@ int CCITTFaxStream::lookChar() {
              code2 += code3 = getWhiteCode();
            } while (code3 >= 64);
          }
-         codingLine[a0 + 1] = a0New + code1;
-         ++a0;
-         a0New = codingLine[a0 + 1] = codingLine[a0] + code2;
-         ++a0;
-         while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
-           b1 += 2;
+         if (code1 > 0 || code2 > 0) {
+           codingLine[a0 + 1] = a0New + code1;
+           ++a0;
+           a0New = codingLine[a0 + 1] = codingLine[a0] + code2;
+           ++a0;
+           while (refLine[b1] <= codingLine[a0] && refLine[b1] < columns)
+             b1 += 2;
+         }
          break;
        case twoDimVert0:
          a0New = codingLine[++a0] = refLine[b1];
@@ -1518,13 +1425,8 @@ int CCITTFaxStream::lookChar() {
          return EOF;
        default:
          error(getPos(), "Bad 2D code %04x in CCITTFax stream", code1);
-#if 0
          err = gTrue;
          break;
-#else
-         eof = gTrue;
-         return EOF;
-#endif
        }
       } while (codingLine[a0] < columns);
 
@@ -1553,9 +1455,12 @@ int CCITTFaxStream::lookChar() {
 
     if (codingLine[a0] != columns) {
       error(getPos(), "CCITTFax row is wrong length (%d)", codingLine[a0]);
-#if 0
+      // force the row to be the correct length
+      while (codingLine[a0] > columns) {
+       --a0;
+      }
+      codingLine[++a0] = columns;
       err = gTrue;
-#endif
     }
 
     // byte-align the row
@@ -1611,30 +1516,25 @@ int CCITTFaxStream::lookChar() {
        }
        eof = gTrue;
       }
-    }
 
-#if 0
-    // This looks for an end-of-line marker after an error, however
-    // some (most?) CCITT streams in PDF files don't use end-of-line
-    // markers, and the just-plow-on technique works better in those
-    // cases.
-    else if (err) {
+    // look for an end-of-line marker after an error -- we only do
+    // this if we know the stream contains end-of-line markers because
+    // the "just plow on" technique tends to work better otherwise
+    } else if (err && endOfLine) {
       do {
        if (code1 == EOF) {
          eof = gTrue;
          return EOF;
        }
        eatBits(1);
-       code1 = look13Bits();
+       code1 = lookBits(13);
       } while ((code1 >> 1) != 0x001);
       eatBits(12); 
-      codingLine[++a0] = columns;
       if (encoding > 0) {
        eatBits(1);
        nextLine2D = !(code1 & 1);
       }
     }
-#endif
 
     a0 = 0;
     outputBits = codingLine[1] - codingLine[0];
@@ -1845,11 +1745,14 @@ short CCITTFaxStream::lookBits(int n) {
   return (inputBuf >> (inputBits - n)) & (0xffff >> (16 - n));
 }
 
-GString *CCITTFaxStream::getPSFilter(char *indent) {
+GString *CCITTFaxStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
   char s1[50];
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("<< ");
@@ -1888,7 +1791,6 @@ GBool CCITTFaxStream::isBinary(GBool last) {
 //------------------------------------------------------------------------
 
 // IDCT constants (20.12 fixed point format)
-#ifndef FP_IDCT
 #define dctCos1    4017                // cos(pi/16)
 #define dctSin1     799                // sin(pi/16)
 #define dctCos3    3406                // cos(3*pi/16)
@@ -1897,19 +1799,6 @@ GBool CCITTFaxStream::isBinary(GBool last) {
 #define dctSin6    3784                // sin(6*pi/16)
 #define dctSqrt2   5793                // sqrt(2)
 #define dctSqrt1d2 2896                // sqrt(2) / 2
-#endif
-
-// IDCT constants
-#ifdef FP_IDCT
-#define dctCos1    0.98078528  // cos(pi/16)
-#define dctSin1    0.19509032  // sin(pi/16)
-#define dctCos3    0.83146961  // cos(3*pi/16)
-#define dctSin3    0.55557023  // sin(3*pi/16)
-#define dctCos6    0.38268343  // cos(6*pi/16)
-#define dctSin6    0.92387953  // sin(6*pi/16)
-#define dctSqrt2   1.41421356  // sqrt(2)
-#define dctSqrt1d2 0.70710678  // sqrt(2) / 2
-#endif
 
 // color conversion parameters (16.16 fixed point format)
 #define dctCrToR   91881       //  1.4020
@@ -1945,14 +1834,18 @@ DCTStream::DCTStream(Stream *strA):
     FilterStream(strA) {
   int i, j;
 
+  progressive = interleaved = gFalse;
   width = height = 0;
   mcuWidth = mcuHeight = 0;
   numComps = 0;
   comp = 0;
   x = y = dy = 0;
-  for (i = 0; i < 4; ++i)
-    for (j = 0; j < 32; ++j)
+  for (i = 0; i < 4; ++i) {
+    for (j = 0; j < 32; ++j) {
       rowBuf[i][j] = NULL;
+    }
+    frameBuf[i] = NULL;
+  }
 
   if (!dctClipInit) {
     for (i = -256; i < 0; ++i)
@@ -1969,53 +1862,178 @@ DCTStream::~DCTStream() {
   int i, j;
 
   delete str;
-  for (i = 0; i < numComps; ++i)
-    for (j = 0; j < mcuHeight; ++j)
-      gfree(rowBuf[i][j]);
+  if (progressive || !interleaved) {
+    for (i = 0; i < numComps; ++i) {
+      gfree(frameBuf[i]);
+    }
+  } else {
+    for (i = 0; i < numComps; ++i) {
+      for (j = 0; j < mcuHeight; ++j) {
+       gfree(rowBuf[i][j]);
+      }
+    }
+  }
 }
 
 void DCTStream::reset() {
+  int minHSample, minVSample;
+  int i, j;
+
   str->reset();
+
+  progressive = interleaved = gFalse;
+  width = height = 0;
+  numComps = 0;
+  numQuantTables = 0;
+  numDCHuffTables = 0;
+  numACHuffTables = 0;
+  colorXform = 0;
+  gotJFIFMarker = gFalse;
+  gotAdobeMarker = gFalse;
+  restartInterval = 0;
+
   if (!readHeader()) {
     y = height;
     return;
   }
-  restartMarker = 0xd0;
-  restart();
-}
 
-int DCTStream::getChar() {
-  int c;
-
-  c = lookChar();
-  if (c == EOF)
-    return EOF;
-  if (++comp == numComps) {
-    comp = 0;
-    if (++x == width) {
-      x = 0;
-      ++y;
-      ++dy;
-    }
+  // compute MCU size
+  mcuWidth = minHSample = compInfo[0].hSample;
+  mcuHeight = minVSample = compInfo[0].vSample;
+  for (i = 1; i < numComps; ++i) {
+    if (compInfo[i].hSample < minHSample)
+      minHSample = compInfo[i].hSample;
+    if (compInfo[i].vSample < minVSample)
+      minVSample = compInfo[i].vSample;
+    if (compInfo[i].hSample > mcuWidth)
+      mcuWidth = compInfo[i].hSample;
+    if (compInfo[i].vSample > mcuHeight)
+      mcuHeight = compInfo[i].vSample;
   }
-  if (y == height)
-    readTrailer();
-  return c;
-}
+  for (i = 0; i < numComps; ++i) {
+    compInfo[i].hSample /= minHSample;
+    compInfo[i].vSample /= minVSample;
+  }
+  mcuWidth = (mcuWidth / minHSample) * 8;
+  mcuHeight = (mcuHeight / minVSample) * 8;
 
-int DCTStream::lookChar() {
-  if (y >= height)
-    return EOF;
-  if (dy >= mcuHeight) {
-    if (!readMCURow()) {
-      y = height;
-      return EOF;
+  // figure out color transform
+  if (!gotAdobeMarker && numComps == 3) {
+    if (gotJFIFMarker) {
+      colorXform = 1;
+    } else if (compInfo[0].id == 82 && compInfo[1].id == 71 &&
+              compInfo[2].id == 66) { // ASCII "RGB"
+      colorXform = 0;
+    } else {
+      colorXform = 1;
+    }
+  }
+
+  if (progressive || !interleaved) {
+
+    // allocate a buffer for the whole image
+    bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+    bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight;
+    for (i = 0; i < numComps; ++i) {
+      frameBuf[i] = (int *)gmalloc(bufWidth * bufHeight * sizeof(int));
+      memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int));
+    }
+
+    // read the image data
+    do {
+      restartMarker = 0xd0;
+      restart();
+      readScan();
+    } while (readHeader());
+
+    // decode
+    decodeImage();
+
+    // initialize counters
+    comp = 0;
+    x = 0;
+    y = 0;
+
+  } else {
+
+    // allocate a buffer for one row of MCUs
+    bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+    for (i = 0; i < numComps; ++i) {
+      for (j = 0; j < mcuHeight; ++j) {
+       rowBuf[i][j] = (Guchar *)gmalloc(bufWidth * sizeof(Guchar));
+      }
     }
+
+    // initialize counters
     comp = 0;
     x = 0;
-    dy = 0;
+    y = 0;
+    dy = mcuHeight;
+
+    restartMarker = 0xd0;
+    restart();
+  }
+}
+
+int DCTStream::getChar() {
+  int c;
+
+  if (y >= height) {
+    return EOF;
+  }
+  if (progressive || !interleaved) {
+    c = frameBuf[comp][y * bufWidth + x];
+    if (++comp == numComps) {
+      comp = 0;
+      if (++x == width) {
+       x = 0;
+       ++y;
+      }
+    }
+  } else {
+    if (dy >= mcuHeight) {
+      if (!readMCURow()) {
+       y = height;
+       return EOF;
+      }
+      comp = 0;
+      x = 0;
+      dy = 0;
+    }
+    c = rowBuf[comp][dy][x];
+    if (++comp == numComps) {
+      comp = 0;
+      if (++x == width) {
+       x = 0;
+       ++y;
+       ++dy;
+       if (y == height) {
+         readTrailer();
+       }
+      }
+    }
+  }
+  return c;
+}
+
+int DCTStream::lookChar() {
+  if (y >= height) {
+    return EOF;
+  }
+  if (progressive || !interleaved) {
+    return frameBuf[comp][y * bufWidth + x];
+  } else {
+    if (dy >= mcuHeight) {
+      if (!readMCURow()) {
+       y = height;
+       return EOF;
+      }
+      comp = 0;
+      x = 0;
+      dy = 0;
+    }
+    return rowBuf[comp][dy][x];
   }
-  return rowBuf[comp][dy][x];
 }
 
 void DCTStream::restart() {
@@ -2023,12 +2041,16 @@ void DCTStream::restart() {
 
   inputBits = 0;
   restartCtr = restartInterval;
-  for (i = 0; i < numComps; ++i)
+  for (i = 0; i < numComps; ++i) {
     compInfo[i].prevDC = 0;
+  }
+  eobRun = 0;
 }
 
+// Read one row of MCUs from a sequential JPEG stream.
 GBool DCTStream::readMCURow() {
-  Guchar data[64];
+  int data1[64];
+  Guchar data2[64];
   Guchar *p1, *p2;
   int pY, pCb, pCr, pR, pG, pB;
   int h, v, horiz, vert, hSub, vSub;
@@ -2059,36 +2081,38 @@ GBool DCTStream::readMCURow() {
       vSub = vert / 8;
       for (y2 = 0; y2 < mcuHeight; y2 += vert) {
        for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
-         if (!readDataUnit(&dcHuffTables[compInfo[cc].dcHuffTable],
-                           &acHuffTables[compInfo[cc].acHuffTable],
-                           quantTables[compInfo[cc].quantTable],
+         if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
+                           &acHuffTables[scanInfo.acHuffTable[cc]],
                            &compInfo[cc].prevDC,
-                           data))
+                           data1)) {
            return gFalse;
+         }
+         transformDataUnit(quantTables[compInfo[cc].quantTable],
+                           data1, data2);
          if (hSub == 1 && vSub == 1) {
            for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
              p1 = &rowBuf[cc][y2+y3][x1+x2];
-             p1[0] = data[i];
-             p1[1] = data[i+1];
-             p1[2] = data[i+2];
-             p1[3] = data[i+3];
-             p1[4] = data[i+4];
-             p1[5] = data[i+5];
-             p1[6] = data[i+6];
-             p1[7] = data[i+7];
+             p1[0] = data2[i];
+             p1[1] = data2[i+1];
+             p1[2] = data2[i+2];
+             p1[3] = data2[i+3];
+             p1[4] = data2[i+4];
+             p1[5] = data2[i+5];
+             p1[6] = data2[i+6];
+             p1[7] = data2[i+7];
            }
          } else if (hSub == 2 && vSub == 2) {
            for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
              p1 = &rowBuf[cc][y2+y3][x1+x2];
              p2 = &rowBuf[cc][y2+y3+1][x1+x2];
-             p1[0] = p1[1] = p2[0] = p2[1] = data[i];
-             p1[2] = p1[3] = p2[2] = p2[3] = data[i+1];
-             p1[4] = p1[5] = p2[4] = p2[5] = data[i+2];
-             p1[6] = p1[7] = p2[6] = p2[7] = data[i+3];
-             p1[8] = p1[9] = p2[8] = p2[9] = data[i+4];
-             p1[10] = p1[11] = p2[10] = p2[11] = data[i+5];
-             p1[12] = p1[13] = p2[12] = p2[13] = data[i+6];
-             p1[14] = p1[15] = p2[14] = p2[15] = data[i+7];
+             p1[0] = p1[1] = p2[0] = p2[1] = data2[i];
+             p1[2] = p1[3] = p2[2] = p2[3] = data2[i+1];
+             p1[4] = p1[5] = p2[4] = p2[5] = data2[i+2];
+             p1[6] = p1[7] = p2[6] = p2[7] = data2[i+3];
+             p1[8] = p1[9] = p2[8] = p2[9] = data2[i+4];
+             p1[10] = p1[11] = p2[10] = p2[11] = data2[i+5];
+             p1[12] = p1[13] = p2[12] = p2[13] = data2[i+6];
+             p1[14] = p1[15] = p2[14] = p2[15] = data2[i+7];
            }
          } else {
            i = 0;
@@ -2096,7 +2120,7 @@ GBool DCTStream::readMCURow() {
              for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
                for (y5 = 0; y5 < vSub; ++y5)
                  for (x5 = 0; x5 < hSub; ++x5)
-                   rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data[i];
+                   rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data2[i];
                ++i;
              }
            }
@@ -2144,71 +2168,469 @@ GBool DCTStream::readMCURow() {
   return gTrue;
 }
 
-// This IDCT algorithm is taken from:
-//   Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
-//   "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
-//   IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
-//   988-991.
-// The stage numbers mentioned in the comments refer to Figure 1 in this
-// paper.
-#ifndef FP_IDCT
+// Read one scan from a progressive or non-interleaved JPEG stream.
+void DCTStream::readScan() {
+  int data[64];
+  int x1, y1, dx1, dy1, x2, y2, y3, cc, i;
+  int h, v, horiz, vert, vSub;
+  int *p1;
+  int c;
+
+  if (scanInfo.numComps == 1) {
+    for (cc = 0; cc < numComps; ++cc) {
+      if (scanInfo.comp[cc]) {
+       break;
+      }
+    }
+    dx1 = mcuWidth / compInfo[cc].hSample;
+    dy1 = mcuHeight / compInfo[cc].vSample;
+  } else {
+    dx1 = mcuWidth;
+    dy1 = mcuHeight;
+  }
+
+  for (y1 = 0; y1 < height; y1 += dy1) {
+    for (x1 = 0; x1 < width; x1 += dx1) {
+
+      // deal with restart marker
+      if (restartInterval > 0 && restartCtr == 0) {
+       c = readMarker();
+       if (c != restartMarker) {
+         error(getPos(), "Bad DCT data: incorrect restart marker");
+         return;
+       }
+       if (++restartMarker == 0xd8) {
+         restartMarker = 0xd0;
+       }
+       restart();
+      }
+
+      // read one MCU
+      for (cc = 0; cc < numComps; ++cc) {
+       if (!scanInfo.comp[cc]) {
+         continue;
+       }
+
+       h = compInfo[cc].hSample;
+       v = compInfo[cc].vSample;
+       horiz = mcuWidth / h;
+       vert = mcuHeight / v;
+       vSub = vert / 8;
+       for (y2 = 0; y2 < dy1; y2 += vert) {
+         for (x2 = 0; x2 < dx1; x2 += horiz) {
+
+           // pull out the current values
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+             data[i] = p1[0];
+             data[i+1] = p1[1];
+             data[i+2] = p1[2];
+             data[i+3] = p1[3];
+             data[i+4] = p1[4];
+             data[i+5] = p1[5];
+             data[i+6] = p1[6];
+             data[i+7] = p1[7];
+             p1 += bufWidth * vSub;
+           }
+
+           // read one data unit
+           if (progressive) {
+             if (!readProgressiveDataUnit(
+                      &dcHuffTables[scanInfo.dcHuffTable[cc]],
+                      &acHuffTables[scanInfo.acHuffTable[cc]],
+                      &compInfo[cc].prevDC,
+                      data)) {
+               return;
+             }
+           } else {
+             if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
+                               &acHuffTables[scanInfo.acHuffTable[cc]],
+                               &compInfo[cc].prevDC,
+                               data)) {
+               return;
+             }
+           }
+
+           // add the data unit into frameBuf
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+             p1[0] = data[i];
+             p1[1] = data[i+1];
+             p1[2] = data[i+2];
+             p1[3] = data[i+3];
+             p1[4] = data[i+4];
+             p1[5] = data[i+5];
+             p1[6] = data[i+6];
+             p1[7] = data[i+7];
+             p1 += bufWidth * vSub;
+           }
+         }
+       }
+      }
+      --restartCtr;
+    }
+  }
+}
+
+// Read one data unit from a sequential JPEG stream.
 GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
                              DCTHuffTable *acHuffTable,
-                             Guchar quantTable[64], int *prevDC,
-                             Guchar data[64]) {
-  int tmp1[64];
-  int v0, v1, v2, v3, v4, v5, v6, v7, t;
+                             int *prevDC, int data[64]) {
   int run, size, amp;
   int c;
   int i, j;
 
-  // Huffman decode and dequantize
-  size = readHuffSym(dcHuffTable);
-  if (size == 9999)
+  if ((size = readHuffSym(dcHuffTable)) == 9999) {
     return gFalse;
+  }
   if (size > 0) {
-    amp = readAmp(size);
-    if (amp == 9999)
+    if ((amp = readAmp(size)) == 9999) {
       return gFalse;
+    }
   } else {
     amp = 0;
   }
-  tmp1[0] = (*prevDC += amp) * quantTable[0];
-  for (i = 1; i < 64; ++i)
-    tmp1[i] = 0;
+  data[0] = *prevDC += amp;
+  for (i = 1; i < 64; ++i) {
+    data[i] = 0;
+  }
   i = 1;
   while (i < 64) {
     run = 0;
-    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30)
+    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30) {
       run += 0x10;
-    if (c == 9999)
+    }
+    if (c == 9999) {
       return gFalse;
+    }
     if (c == 0x00) {
       break;
     } else {
       run += (c >> 4) & 0x0f;
       size = c & 0x0f;
       amp = readAmp(size);
-      if (amp == 9999)
+      if (amp == 9999) {
        return gFalse;
+      }
       i += run;
+      if (i < 64) {
+       j = dctZigZag[i++];
+       data[j] = amp;
+      }
+    }
+  }
+  return gTrue;
+}
+
+// Read one data unit from a sequential JPEG stream.
+GBool DCTStream::readProgressiveDataUnit(DCTHuffTable *dcHuffTable,
+                                        DCTHuffTable *acHuffTable,
+                                        int *prevDC, int data[64]) {
+  int run, size, amp, bit, c;
+  int i, j, k;
+
+  // get the DC coefficient
+  i = scanInfo.firstCoeff;
+  if (i == 0) {
+    if (scanInfo.ah == 0) {
+      if ((size = readHuffSym(dcHuffTable)) == 9999) {
+       return gFalse;
+      }
+      if (size > 0) {
+       if ((amp = readAmp(size)) == 9999) {
+         return gFalse;
+       }
+      } else {
+       amp = 0;
+      }
+      data[0] += (*prevDC += amp) << scanInfo.al;
+    } else {
+      if ((bit = readBit()) == 9999) {
+       return gFalse;
+      }
+      data[0] += bit << scanInfo.al;
+    }
+    ++i;
+  }
+  if (scanInfo.lastCoeff == 0) {
+    return gTrue;
+  }
+
+  // check for an EOB run
+  if (eobRun > 0) {
+    while (i <= scanInfo.lastCoeff) {
       j = dctZigZag[i++];
-      tmp1[j] = amp * quantTable[j];
+      if (data[j] != 0) {
+       if ((bit = readBit()) == EOF) {
+         return gFalse;
+       }
+       if (bit) {
+         data[j] += 1 << scanInfo.al;
+       }
+      }
+    }
+    --eobRun;
+    return gTrue;
+  }
+
+  // read the AC coefficients
+  while (i <= scanInfo.lastCoeff) {
+    if ((c = readHuffSym(acHuffTable)) == 9999) {
+      return gFalse;
+    }
+
+    // ZRL
+    if (c == 0xf0) {
+      k = 0;
+      while (k < 16) {
+       j = dctZigZag[i++];
+       if (data[j] == 0) {
+         ++k;
+       } else {
+         if ((bit = readBit()) == EOF) {
+           return gFalse;
+         }
+         if (bit) {
+           data[j] += 1 << scanInfo.al;
+         }
+       }
+      }
+
+    // EOB run
+    } else if ((c & 0x0f) == 0x00) {
+      j = c >> 4;
+      eobRun = 0;
+      for (k = 0; k < j; ++k) {
+       if ((bit = readBit()) == EOF) {
+         return gFalse;
+       }
+       eobRun = (eobRun << 1) | bit;
+      }
+      eobRun += 1 << j;
+      while (i <= scanInfo.lastCoeff) {
+       j = dctZigZag[i++];
+       if (data[j] != 0) {
+         if ((bit = readBit()) == EOF) {
+           return gFalse;
+         }
+         if (bit) {
+           data[j] += 1 << scanInfo.al;
+         }
+       }
+      }
+      --eobRun;
+      break;
+
+    // zero run and one AC coefficient
+    } else {
+      run = (c >> 4) & 0x0f;
+      size = c & 0x0f;
+      if ((amp = readAmp(size)) == 9999) {
+       return gFalse;
+      }
+      k = 0;
+      do {
+       j = dctZigZag[i++];
+       while (data[j] != 0) {
+         if ((bit = readBit()) == EOF) {
+           return gFalse;
+         }
+         if (bit) {
+           data[j] += 1 << scanInfo.al;
+         }
+         j = dctZigZag[i++];
+       }
+       ++k;
+      } while (k <= run);
+      data[j] = amp << scanInfo.al;
+    }
+  }
+
+  return gTrue;
+}
+
+// Decode a progressive JPEG image.
+void DCTStream::decodeImage() {
+  int dataIn[64];
+  Guchar dataOut[64];
+  Guchar *quantTable;
+  int pY, pCb, pCr, pR, pG, pB;
+  int x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
+  int h, v, horiz, vert, hSub, vSub;
+  int *p0, *p1, *p2;
+
+  for (y1 = 0; y1 < bufHeight; y1 += mcuHeight) {
+    for (x1 = 0; x1 < bufWidth; x1 += mcuWidth) {
+      for (cc = 0; cc < numComps; ++cc) {
+       quantTable = quantTables[compInfo[cc].quantTable];
+       h = compInfo[cc].hSample;
+       v = compInfo[cc].vSample;
+       horiz = mcuWidth / h;
+       vert = mcuHeight / v;
+       hSub = horiz / 8;
+       vSub = vert / 8;
+       for (y2 = 0; y2 < mcuHeight; y2 += vert) {
+         for (x2 = 0; x2 < mcuWidth; x2 += horiz) {
+
+           // pull out the coded data unit
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+             dataIn[i]   = p1[0];
+             dataIn[i+1] = p1[1];
+             dataIn[i+2] = p1[2];
+             dataIn[i+3] = p1[3];
+             dataIn[i+4] = p1[4];
+             dataIn[i+5] = p1[5];
+             dataIn[i+6] = p1[6];
+             dataIn[i+7] = p1[7];
+             p1 += bufWidth * vSub;
+           }
+
+           // transform
+           transformDataUnit(quantTable, dataIn, dataOut);
+
+           // store back into frameBuf, doing replication for
+           // subsampled components
+           p1 = &frameBuf[cc][(y1+y2) * bufWidth + (x1+x2)];
+           if (hSub == 1 && vSub == 1) {
+             for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+               p1[0] = dataOut[i] & 0xff;
+               p1[1] = dataOut[i+1] & 0xff;
+               p1[2] = dataOut[i+2] & 0xff;
+               p1[3] = dataOut[i+3] & 0xff;
+               p1[4] = dataOut[i+4] & 0xff;
+               p1[5] = dataOut[i+5] & 0xff;
+               p1[6] = dataOut[i+6] & 0xff;
+               p1[7] = dataOut[i+7] & 0xff;
+               p1 += bufWidth;
+             }
+           } else if (hSub == 2 && vSub == 2) {
+             p2 = p1 + bufWidth;
+             for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
+               p1[0] = p1[1] = p2[0] = p2[1] = dataOut[i] & 0xff;
+               p1[2] = p1[3] = p2[2] = p2[3] = dataOut[i+1] & 0xff;
+               p1[4] = p1[5] = p2[4] = p2[5] = dataOut[i+2] & 0xff;
+               p1[6] = p1[7] = p2[6] = p2[7] = dataOut[i+3] & 0xff;
+               p1[8] = p1[9] = p2[8] = p2[9] = dataOut[i+4] & 0xff;
+               p1[10] = p1[11] = p2[10] = p2[11] = dataOut[i+5] & 0xff;
+               p1[12] = p1[13] = p2[12] = p2[13] = dataOut[i+6] & 0xff;
+               p1[14] = p1[15] = p2[14] = p2[15] = dataOut[i+7] & 0xff;
+               p1 += bufWidth * 2;
+               p2 += bufWidth * 2;
+             }
+           } else {
+             i = 0;
+             for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
+               for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
+                 p2 = p1 + x4;
+                 for (y5 = 0; y5 < vSub; ++y5) {
+                   for (x5 = 0; x5 < hSub; ++x5) {
+                     p2[x5] = dataOut[i] & 0xff;
+                   }
+                   p2 += bufWidth;
+                 }
+                 ++i;
+               }
+               p1 += bufWidth * vSub;
+             }
+           }
+         }
+       }
+      }
+
+      // color space conversion
+      if (colorXform) {
+       // convert YCbCr to RGB
+       if (numComps == 3) {
+         for (y2 = 0; y2 < mcuHeight; ++y2) {
+           p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
+           p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
+           p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
+           for (x2 = 0; x2 < mcuWidth; ++x2) {
+             pY = *p0;
+             pCb = *p1 - 128;
+             pCr = *p2 - 128;
+             pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+             *p0++ = dctClip[dctClipOffset + pR];
+             pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+                   32768) >> 16;
+             *p1++ = dctClip[dctClipOffset + pG];
+             pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+             *p2++ = dctClip[dctClipOffset + pB];
+           }
+         }
+       // convert YCbCrK to CMYK (K is passed through unchanged)
+       } else if (numComps == 4) {
+         for (y2 = 0; y2 < mcuHeight; ++y2) {
+           p0 = &frameBuf[0][(y1+y2) * bufWidth + x1];
+           p1 = &frameBuf[1][(y1+y2) * bufWidth + x1];
+           p2 = &frameBuf[2][(y1+y2) * bufWidth + x1];
+           for (x2 = 0; x2 < mcuWidth; ++x2) {
+             pY = *p0;
+             pCb = *p1 - 128;
+             pCr = *p2 - 128;
+             pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+             *p0++ = 255 - dctClip[dctClipOffset + pR];
+             pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+                   32768) >> 16;
+             *p1++ = 255 - dctClip[dctClipOffset + pG];
+             pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+             *p2++ = 255 - dctClip[dctClipOffset + pB];
+           }
+         }
+       }
+      }
     }
   }
+}
+
+// Transform one data unit -- this performs the dequantization and
+// IDCT steps.  This IDCT algorithm is taken from:
+//   Christoph Loeffler, Adriaan Ligtenberg, George S. Moschytz,
+//   "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
+//   IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
+//   988-991.
+// The stage numbers mentioned in the comments refer to Figure 1 in this
+// paper.
+void DCTStream::transformDataUnit(Guchar *quantTable,
+                                 int dataIn[64], Guchar dataOut[64]) {
+  int v0, v1, v2, v3, v4, v5, v6, v7, t;
+  int *p;
+  int i;
+
+  // dequant
+  for (i = 0; i < 64; ++i) {
+    dataIn[i] *= quantTable[i];
+  }
 
   // inverse DCT on rows
   for (i = 0; i < 64; i += 8) {
+    p = dataIn + i;
+
+    // check for all-zero AC coefficients
+    if (p[1] == 0 && p[2] == 0 && p[3] == 0 &&
+       p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0) {
+      t = (dctSqrt2 * p[0] + 512) >> 10;
+      p[0] = t;
+      p[1] = t;
+      p[2] = t;
+      p[3] = t;
+      p[4] = t;
+      p[5] = t;
+      p[6] = t;
+      p[7] = t;
+      continue;
+    }
 
     // stage 4
-    v0 = (dctSqrt2 * tmp1[i+0] + 128) >> 8;
-    v1 = (dctSqrt2 * tmp1[i+4] + 128) >> 8;
-    v2 = tmp1[i+2];
-    v3 = tmp1[i+6];
-    v4 = (dctSqrt1d2 * (tmp1[i+1] - tmp1[i+7]) + 128) >> 8;
-    v7 = (dctSqrt1d2 * (tmp1[i+1] + tmp1[i+7]) + 128) >> 8;
-    v5 = tmp1[i+3] << 4;
-    v6 = tmp1[i+5] << 4;
+    v0 = (dctSqrt2 * p[0] + 128) >> 8;
+    v1 = (dctSqrt2 * p[4] + 128) >> 8;
+    v2 = p[2];
+    v3 = p[6];
+    v4 = (dctSqrt1d2 * (p[1] - p[7]) + 128) >> 8;
+    v7 = (dctSqrt1d2 * (p[1] + p[7]) + 128) >> 8;
+    v5 = p[3] << 4;
+    v6 = p[5] << 4;
 
     // stage 3
     t = (v0 - v1+ 1) >> 1;
@@ -2239,28 +2661,44 @@ GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
     v6 = t;
 
     // stage 1
-    tmp1[i+0] = v0 + v7;
-    tmp1[i+7] = v0 - v7;
-    tmp1[i+1] = v1 + v6;
-    tmp1[i+6] = v1 - v6;
-    tmp1[i+2] = v2 + v5;
-    tmp1[i+5] = v2 - v5;
-    tmp1[i+3] = v3 + v4;
-    tmp1[i+4] = v3 - v4;
+    p[0] = v0 + v7;
+    p[7] = v0 - v7;
+    p[1] = v1 + v6;
+    p[6] = v1 - v6;
+    p[2] = v2 + v5;
+    p[5] = v2 - v5;
+    p[3] = v3 + v4;
+    p[4] = v3 - v4;
   }
 
   // inverse DCT on columns
   for (i = 0; i < 8; ++i) {
+    p = dataIn + i;
+
+    // check for all-zero AC coefficients
+    if (p[1*8] == 0 && p[2*8] == 0 && p[3*8] == 0 &&
+       p[4*8] == 0 && p[5*8] == 0 && p[6*8] == 0 && p[7*8] == 0) {
+      t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14;
+      p[0*8] = t;
+      p[1*8] = t;
+      p[2*8] = t;
+      p[3*8] = t;
+      p[4*8] = t;
+      p[5*8] = t;
+      p[6*8] = t;
+      p[7*8] = t;
+      continue;
+    }
 
     // stage 4
-    v0 = (dctSqrt2 * tmp1[0*8+i] + 2048) >> 12;
-    v1 = (dctSqrt2 * tmp1[4*8+i] + 2048) >> 12;
-    v2 = tmp1[2*8+i];
-    v3 = tmp1[6*8+i];
-    v4 = (dctSqrt1d2 * (tmp1[1*8+i] - tmp1[7*8+i]) + 2048) >> 12;
-    v7 = (dctSqrt1d2 * (tmp1[1*8+i] + tmp1[7*8+i]) + 2048) >> 12;
-    v5 = tmp1[3*8+i];
-    v6 = tmp1[5*8+i];
+    v0 = (dctSqrt2 * p[0*8] + 2048) >> 12;
+    v1 = (dctSqrt2 * p[4*8] + 2048) >> 12;
+    v2 = p[2*8];
+    v3 = p[6*8];
+    v4 = (dctSqrt1d2 * (p[1*8] - p[7*8]) + 2048) >> 12;
+    v7 = (dctSqrt1d2 * (p[1*8] + p[7*8]) + 2048) >> 12;
+    v5 = p[3*8];
+    v6 = p[5*8];
 
     // stage 3
     t = (v0 - v1 + 1) >> 1;
@@ -2291,181 +2729,21 @@ GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
     v6 = t;
 
     // stage 1
-    tmp1[0*8+i] = v0 + v7;
-    tmp1[7*8+i] = v0 - v7;
-    tmp1[1*8+i] = v1 + v6;
-    tmp1[6*8+i] = v1 - v6;
-    tmp1[2*8+i] = v2 + v5;
-    tmp1[5*8+i] = v2 - v5;
-    tmp1[3*8+i] = v3 + v4;
-    tmp1[4*8+i] = v3 - v4;
+    p[0*8] = v0 + v7;
+    p[7*8] = v0 - v7;
+    p[1*8] = v1 + v6;
+    p[6*8] = v1 - v6;
+    p[2*8] = v2 + v5;
+    p[5*8] = v2 - v5;
+    p[3*8] = v3 + v4;
+    p[4*8] = v3 - v4;
   }
 
   // convert to 8-bit integers
-  for (i = 0; i < 64; ++i)
-    data[i] = dctClip[dctClipOffset + 128 + ((tmp1[i] + 8) >> 4)];
-
-  return gTrue;
-}
-#endif
-
-#ifdef FP_IDCT
-GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
-                             DCTHuffTable *acHuffTable,
-                             Guchar quantTable[64], int *prevDC,
-                             Guchar data[64]) {
-  double tmp1[64];
-  double v0, v1, v2, v3, v4, v5, v6, v7, t;
-  int run, size, amp;
-  int c;
-  int i, j;
-
-  // Huffman decode and dequantize
-  size = readHuffSym(dcHuffTable);
-  if (size == 9999)
-    return gFalse;
-  if (size > 0) {
-    amp = readAmp(size);
-    if (amp == 9999)
-      return gFalse;
-  } else {
-    amp = 0;
+  for (i = 0; i < 64; ++i) {
+    dataOut[i] = dctClip[dctClipOffset + 128 + ((dataIn[i] + 8) >> 4)];
   }
-  tmp1[0] = (*prevDC += amp) * quantTable[0];
-  for (i = 1; i < 64; ++i)
-    tmp1[i] = 0;
-  i = 1;
-  while (i < 64) {
-    run = 0;
-    while ((c = readHuffSym(acHuffTable)) == 0xf0 && run < 0x30)
-      run += 0x10;
-    if (c == 9999)
-      return gFalse;
-    if (c == 0x00) {
-      break;
-    } else {
-      run += (c >> 4) & 0x0f;
-      size = c & 0x0f;
-      amp = readAmp(size);
-      if (amp == 9999)
-       return gFalse;
-      i += run;
-      j = dctZigZag[i++];
-      tmp1[j] = amp * quantTable[j];
-    }
-  }
-
-  // inverse DCT on rows
-  for (i = 0; i < 64; i += 8) {
-
-    // stage 4
-    v0 = dctSqrt2 * tmp1[i+0];
-    v1 = dctSqrt2 * tmp1[i+4];
-    v2 = tmp1[i+2];
-    v3 = tmp1[i+6];
-    v4 = dctSqrt1d2 * (tmp1[i+1] - tmp1[i+7]);
-    v7 = dctSqrt1d2 * (tmp1[i+1] + tmp1[i+7]);
-    v5 = tmp1[i+3];
-    v6 = tmp1[i+5];
-
-    // stage 3
-    t = 0.5 * (v0 - v1);
-    v0 = 0.5 * (v0 + v1);
-    v1 = t;
-    t = v2 * dctSin6 + v3 * dctCos6;
-    v2 = v2 * dctCos6 - v3 * dctSin6;
-    v3 = t;
-    t = 0.5 * (v4 - v6);
-    v4 = 0.5 * (v4 + v6);
-    v6 = t;
-    t = 0.5 * (v7 + v5);
-    v5 = 0.5 * (v7 - v5);
-    v7 = t;
-
-    // stage 2
-    t = 0.5 * (v0 - v3);
-    v0 = 0.5 * (v0 + v3);
-    v3 = t;
-    t = 0.5 * (v1 - v2);
-    v1 = 0.5 * (v1 + v2);
-    v2 = t;
-    t = v4 * dctSin3 + v7 * dctCos3;
-    v4 = v4 * dctCos3 - v7 * dctSin3;
-    v7 = t;
-    t = v5 * dctSin1 + v6 * dctCos1;
-    v5 = v5 * dctCos1 - v6 * dctSin1;
-    v6 = t;
-
-    // stage 1
-    tmp1[i+0] = v0 + v7;
-    tmp1[i+7] = v0 - v7;
-    tmp1[i+1] = v1 + v6;
-    tmp1[i+6] = v1 - v6;
-    tmp1[i+2] = v2 + v5;
-    tmp1[i+5] = v2 - v5;
-    tmp1[i+3] = v3 + v4;
-    tmp1[i+4] = v3 - v4;
-  }
-
-  // inverse DCT on columns
-  for (i = 0; i < 8; ++i) {
-
-    // stage 4
-    v0 = dctSqrt2 * tmp1[0*8+i];
-    v1 = dctSqrt2 * tmp1[4*8+i];
-    v2 = tmp1[2*8+i];
-    v3 = tmp1[6*8+i];
-    v4 = dctSqrt1d2 * (tmp1[1*8+i] - tmp1[7*8+i]);
-    v7 = dctSqrt1d2 * (tmp1[1*8+i] + tmp1[7*8+i]);
-    v5 = tmp1[3*8+i];
-    v6 = tmp1[5*8+i];
-
-    // stage 3
-    t = 0.5 * (v0 - v1);
-    v0 = 0.5 * (v0 + v1);
-    v1 = t;
-    t = v2 * dctSin6 + v3 * dctCos6;
-    v2 = v2 * dctCos6 - v3 * dctSin6;
-    v3 = t;
-    t = 0.5 * (v4 - v6);
-    v4 = 0.5 * (v4 + v6);
-    v6 = t;
-    t = 0.5 * (v7 + v5);
-    v5 = 0.5 * (v7 - v5);
-    v7 = t;
-
-    // stage 2
-    t = 0.5 * (v0 - v3);
-    v0 = 0.5 * (v0 + v3);
-    v3 = t;
-    t = 0.5 * (v1 - v2);
-    v1 = 0.5 * (v1 + v2);
-    v2 = t;
-    t = v4 * dctSin3 + v7 * dctCos3;
-    v4 = v4 * dctCos3 - v7 * dctSin3;
-    v7 = t;
-    t = v5 * dctSin1 + v6 * dctCos1;
-    v5 = v5 * dctCos1 - v6 * dctSin1;
-    v6 = t;
-
-    // stage 1
-    tmp1[0*8+i] = v0 + v7;
-    tmp1[7*8+i] = v0 - v7;
-    tmp1[1*8+i] = v1 + v6;
-    tmp1[6*8+i] = v1 - v6;
-    tmp1[2*8+i] = v2 + v5;
-    tmp1[5*8+i] = v2 - v5;
-    tmp1[3*8+i] = v3 + v4;
-    tmp1[4*8+i] = v3 - v4;
-  }
-
-  // convert to 8-bit integers
-  for (i = 0; i < 64; ++i)
-    data[i] = dctClip[dctClipOffset + (int)(tmp1[i] + 128.5)];
-
-  return gTrue;
 }
-#endif
 
 int DCTStream::readHuffSym(DCTHuffTable *table) {
   Gushort code;
@@ -2533,20 +2811,9 @@ int DCTStream::readBit() {
 
 GBool DCTStream::readHeader() {
   GBool doScan;
-  int minHSample, minVSample;
-  int bufWidth;
   int n;
   int c = 0;
-  int i, j;
-
-  width = height = 0;
-  numComps = 0;
-  numQuantTables = 0;
-  numDCHuffTables = 0;
-  numACHuffTables = 0;
-  colorXform = 0;
-  gotAdobeMarker = gFalse;
-  restartInterval = 0;
+  int i;
 
   // read headers
   doScan = gFalse;
@@ -2554,31 +2821,49 @@ GBool DCTStream::readHeader() {
     c = readMarker();
     switch (c) {
     case 0xc0:                 // SOF0
-      if (!readFrameInfo())
+      if (!readBaselineSOF()) {
        return gFalse;
+      }
+      break;
+    case 0xc2:                 // SOF2
+      if (!readProgressiveSOF()) {
+       return gFalse;
+      }
       break;
     case 0xc4:                 // DHT
-      if (!readHuffmanTables())
+      if (!readHuffmanTables()) {
        return gFalse;
+      }
       break;
     case 0xd8:                 // SOI
       break;
+    case 0xd9:                 // EOI
+      return gFalse;
     case 0xda:                 // SOS
-      if (!readScanInfo())
+      if (!readScanInfo()) {
        return gFalse;
+      }
       doScan = gTrue;
       break;
     case 0xdb:                 // DQT
-      if (!readQuantTables())
+      if (!readQuantTables()) {
        return gFalse;
+      }
       break;
     case 0xdd:                 // DRI
-      if (!readRestartInterval())
+      if (!readRestartInterval()) {
        return gFalse;
+      }
+      break;
+    case 0xe0:                 // APP0
+      if (!readJFIFMarker()) {
+       return gFalse;
+      }
       break;
     case 0xee:                 // APP14
-      if (!readAdobeMarker())
+      if (!readAdobeMarker()) {
        return gFalse;
+      }
       break;
     case EOF:
       error(getPos(), "Bad DCT header");
@@ -2587,8 +2872,9 @@ GBool DCTStream::readHeader() {
       // skip APPn / COM / etc.
       if (c >= 0xe0) {
        n = read16() - 2;
-       for (i = 0; i < n; ++i)
+       for (i = 0; i < n; ++i) {
          str->getChar();
+       }
       } else {
        error(getPos(), "Unknown DCT marker <%02x>", c);
        return gFalse;
@@ -2597,107 +2883,105 @@ GBool DCTStream::readHeader() {
     }
   }
 
-  // compute MCU size
-  mcuWidth = minHSample = compInfo[0].hSample;
-  mcuHeight = minVSample = compInfo[0].vSample;
-  for (i = 1; i < numComps; ++i) {
-    if (compInfo[i].hSample < minHSample)
-      minHSample = compInfo[i].hSample;
-    if (compInfo[i].vSample < minVSample)
-      minVSample = compInfo[i].vSample;
-    if (compInfo[i].hSample > mcuWidth)
-      mcuWidth = compInfo[i].hSample;
-    if (compInfo[i].vSample > mcuHeight)
-      mcuHeight = compInfo[i].vSample;
-  }
-  for (i = 0; i < numComps; ++i) {
-    compInfo[i].hSample /= minHSample;
-    compInfo[i].vSample /= minVSample;
-  }
-  mcuWidth = (mcuWidth / minHSample) * 8;
-  mcuHeight = (mcuHeight / minVSample) * 8;
+  return gTrue;
+}
 
-  // allocate buffers
-  bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
-  for (i = 0; i < numComps; ++i)
-    for (j = 0; j < mcuHeight; ++j)
-      rowBuf[i][j] = (Guchar *)gmalloc(bufWidth * sizeof(Guchar));
+GBool DCTStream::readBaselineSOF() {
+  int length;
+  int prec;
+  int i;
+  int c;
 
-  // figure out color transform
-  if (!gotAdobeMarker && numComps == 3) {
-    if (compInfo[0].id == 1 && compInfo[1].id == 2 && compInfo[2].id == 3) {
-      colorXform = 1;
-    }
+  length = read16();
+  prec = str->getChar();
+  height = read16();
+  width = read16();
+  numComps = str->getChar();
+  if (prec != 8) {
+    error(getPos(), "Bad DCT precision %d", prec);
+    return gFalse;
   }
-
-  // initialize counters
-  comp = 0;
-  x = 0;
-  y = 0;
-  dy = mcuHeight;
-
+  for (i = 0; i < numComps; ++i) {
+    compInfo[i].id = str->getChar();
+    c = str->getChar();
+    compInfo[i].hSample = (c >> 4) & 0x0f;
+    compInfo[i].vSample = c & 0x0f;
+    compInfo[i].quantTable = str->getChar();
+  }
+  progressive = gFalse;
   return gTrue;
 }
 
-GBool DCTStream::readFrameInfo() {
+GBool DCTStream::readProgressiveSOF() {
   int length;
   int prec;
   int i;
   int c;
 
-  length = read16() - 2;
+  length = read16();
   prec = str->getChar();
   height = read16();
   width = read16();
   numComps = str->getChar();
-  length -= 6;
   if (prec != 8) {
     error(getPos(), "Bad DCT precision %d", prec);
     return gFalse;
   }
   for (i = 0; i < numComps; ++i) {
     compInfo[i].id = str->getChar();
-    compInfo[i].inScan = gFalse;
     c = str->getChar();
     compInfo[i].hSample = (c >> 4) & 0x0f;
     compInfo[i].vSample = c & 0x0f;
     compInfo[i].quantTable = str->getChar();
-    compInfo[i].dcHuffTable = 0;
-    compInfo[i].acHuffTable = 0;
   }
+  progressive = gTrue;
   return gTrue;
 }
 
 GBool DCTStream::readScanInfo() {
   int length;
-  int scanComps, id, c;
+  int id, c;
   int i, j;
 
   length = read16() - 2;
-  scanComps = str->getChar();
+  scanInfo.numComps = str->getChar();
   --length;
-  if (length != 2 * scanComps + 3) {
+  if (length != 2 * scanInfo.numComps + 3) {
     error(getPos(), "Bad DCT scan info block");
     return gFalse;
   }
-  for (i = 0; i < scanComps; ++i) {
+  interleaved = scanInfo.numComps == numComps;
+  for (j = 0; j < numComps; ++j) {
+    scanInfo.comp[j] = gFalse;
+  }
+  for (i = 0; i < scanInfo.numComps; ++i) {
     id = str->getChar();
-    for (j = 0; j < numComps; ++j) {
-      if (id == compInfo[j].id)
-       break;
-    }
-    if (j == numComps) {
-      error(getPos(), "Bad DCT component ID in scan info block");
-      return gFalse;
+    // some (broken) DCT streams reuse ID numbers, but at least they
+    // keep the components in order, so we check compInfo[i] first to
+    // work around the problem
+    if (id == compInfo[i].id) {
+      j = i;
+    } else {
+      for (j = 0; j < numComps; ++j) {
+       if (id == compInfo[j].id) {
+         break;
+       }
+      }
+      if (j == numComps) {
+       error(getPos(), "Bad DCT component ID in scan info block");
+       return gFalse;
+      }
     }
-    compInfo[j].inScan = gTrue;
+    scanInfo.comp[j] = gTrue;
     c = str->getChar();
-    compInfo[j].dcHuffTable = (c >> 4) & 0x0f;
-    compInfo[j].acHuffTable = c & 0x0f;
+    scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f;
+    scanInfo.acHuffTable[j] = c & 0x0f;
   }
-  str->getChar();
-  str->getChar();
-  str->getChar();
+  scanInfo.firstCoeff = str->getChar();
+  scanInfo.lastCoeff = str->getChar();
+  c = str->getChar();
+  scanInfo.ah = (c >> 4) & 0x0f;
+  scanInfo.al = c & 0x0f;
   return gTrue;
 }
 
@@ -2779,23 +3063,61 @@ GBool DCTStream::readRestartInterval() {
   return gTrue;
 }
 
+GBool DCTStream::readJFIFMarker() {
+  int length, i;
+  char buf[5];
+  int c;
+
+  length = read16();
+  length -= 2;
+  if (length >= 5) {
+    for (i = 0; i < 5; ++i) {
+      if ((c = str->getChar()) == EOF) {
+       error(getPos(), "Bad DCT APP0 marker");
+       return gFalse;
+      }
+      buf[i] = c;
+    }
+    length -= 5;
+    if (!memcmp(buf, "JFIF\0", 5)) {
+      gotJFIFMarker = gTrue;
+    }
+  }
+  while (length > 0) {
+    if (str->getChar() == EOF) {
+      error(getPos(), "Bad DCT APP0 marker");
+      return gFalse;
+    }
+    --length;
+  }
+  return gTrue;
+}
+
 GBool DCTStream::readAdobeMarker() {
   int length, i;
   char buf[12];
   int c;
 
   length = read16();
-  if (length != 14)
+  if (length < 14) {
     goto err;
+  }
   for (i = 0; i < 12; ++i) {
-    if ((c = str->getChar()) == EOF)
+    if ((c = str->getChar()) == EOF) {
       goto err;
+    }
     buf[i] = c;
   }
-  if (strncmp(buf, "Adobe", 5))
+  if (strncmp(buf, "Adobe", 5)) {
     goto err;
+  }
   colorXform = buf[11];
   gotAdobeMarker = gTrue;
+  for (i = 14; i < length; ++i) {
+    if (str->getChar() == EOF) {
+      goto err;
+    }
+  }
   return gTrue;
 
  err:
@@ -2838,10 +3160,13 @@ int DCTStream::read16() {
   return (c1 << 8) + c2;
 }
 
-GString *DCTStream::getPSFilter(char *indent) {
+GString *DCTStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  if (!(s = str->getPSFilter(indent))) {
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
     return NULL;
   }
   s->append(indent)->append("<< >> /DCTDecode filter\n");
@@ -2933,9 +3258,13 @@ FlateStream::FlateStream(Stream *strA, int predictor, int columns,
   } else {
     pred = NULL;
   }
+  litCodeTab.codes = NULL;
+  distCodeTab.codes = NULL;
 }
 
 FlateStream::~FlateStream() {
+  gfree(litCodeTab.codes);
+  gfree(distCodeTab.codes);
   if (pred) {
     delete pred;
   }
@@ -3024,8 +3353,17 @@ int FlateStream::getRawChar() {
   return c;
 }
 
-GString *FlateStream::getPSFilter(char *indent) {
-  return NULL;
+GString *FlateStream::getPSFilter(int psLevel, char *indent) {
+  GString *s;
+
+  if (psLevel < 3 || pred) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
+    return NULL;
+  }
+  s->append(indent)->append("<< >> /FlateDecode filter\n");
+  return s;
 }
 
 GBool FlateStream::isBinary(GBool last) {
@@ -3102,6 +3440,12 @@ GBool FlateStream::startBlock() {
   int c;
   int check;
 
+  // free the code tables from the previous block
+  gfree(litCodeTab.codes);
+  litCodeTab.codes = NULL;
+  gfree(distCodeTab.codes);
+  distCodeTab.codes = NULL;
+
   // read block header
   blockHdr = getCodeWord(3);
   if (blockHdr & 1)
@@ -3136,8 +3480,9 @@ GBool FlateStream::startBlock() {
   // compressed block with dynamic codes
   } else if (blockHdr == 2) {
     compressedBlock = gTrue;
-    if (!readDynamicCodes())
+    if (!readDynamicCodes()) {
       goto err;
+    }
 
   // unknown block type
   } else {
@@ -3156,186 +3501,196 @@ err:
 void FlateStream::loadFixedCodes() {
   int i;
 
-  // set up code arrays
-  litCodeTab.codes = allCodes;
-  distCodeTab.codes = allCodes + flateMaxLitCodes;
-
-  // initialize literal code table
-  for (i = 0; i <= 143; ++i)
-    litCodeTab.codes[i].len = 8;
-  for (i = 144; i <= 255; ++i)
-    litCodeTab.codes[i].len = 9;
-  for (i = 256; i <= 279; ++i)
-    litCodeTab.codes[i].len = 7;
-  for (i = 280; i <= 287; ++i)
-    litCodeTab.codes[i].len = 8;
-  compHuffmanCodes(&litCodeTab, flateMaxLitCodes);
-
-  // initialize distance code table
-  for (i = 0; i <= 5; ++i) {
-    distCodeTab.start[i] = 0;
+  // build the literal code table
+  for (i = 0; i <= 143; ++i) {
+    codeLengths[i] = 8;
+  }
+  for (i = 144; i <= 255; ++i) {
+    codeLengths[i] = 9;
   }
-  for (i = 6; i <= flateMaxHuffman+1; ++i) {
-    distCodeTab.start[i] = flateMaxDistCodes;
+  for (i = 256; i <= 279; ++i) {
+    codeLengths[i] = 7;
   }
+  for (i = 280; i <= 287; ++i) {
+    codeLengths[i] = 8;
+  }
+  compHuffmanCodes(codeLengths, flateMaxLitCodes, &litCodeTab);
+
+  // build the distance code table
   for (i = 0; i < flateMaxDistCodes; ++i) {
-    distCodeTab.codes[i].len = 5;
-    distCodeTab.codes[i].code = i;
-    distCodeTab.codes[i].val = i;
+    codeLengths[i] = 5;
   }
+  compHuffmanCodes(codeLengths, flateMaxDistCodes, &distCodeTab);
 }
 
 GBool FlateStream::readDynamicCodes() {
   int numCodeLenCodes;
   int numLitCodes;
   int numDistCodes;
-  FlateCode codeLenCodes[flateMaxCodeLenCodes];
+  int codeLenCodeLengths[flateMaxCodeLenCodes];
   FlateHuffmanTab codeLenCodeTab;
   int len, repeat, code;
   int i;
 
+  codeLenCodeTab.codes = NULL;
+
   // read lengths
-  if ((numLitCodes = getCodeWord(5)) == EOF)
+  if ((numLitCodes = getCodeWord(5)) == EOF) {
     goto err;
+  }
   numLitCodes += 257;
-  if ((numDistCodes = getCodeWord(5)) == EOF)
+  if ((numDistCodes = getCodeWord(5)) == EOF) {
     goto err;
+  }
   numDistCodes += 1;
-  if ((numCodeLenCodes = getCodeWord(4)) == EOF)
+  if ((numCodeLenCodes = getCodeWord(4)) == EOF) {
     goto err;
+  }
   numCodeLenCodes += 4;
   if (numLitCodes > flateMaxLitCodes ||
       numDistCodes > flateMaxDistCodes ||
-      numCodeLenCodes > flateMaxCodeLenCodes)
+      numCodeLenCodes > flateMaxCodeLenCodes) {
     goto err;
+  }
 
-  // read code length code table
-  codeLenCodeTab.codes = codeLenCodes;
-  for (i = 0; i < flateMaxCodeLenCodes; ++i)
-    codeLenCodes[i].len = 0;
+  // build the code length code table
+  for (i = 0; i < flateMaxCodeLenCodes; ++i) {
+    codeLenCodeLengths[i] = 0;
+  }
   for (i = 0; i < numCodeLenCodes; ++i) {
-    if ((codeLenCodes[codeLenCodeMap[i]].len = getCodeWord(3)) == -1)
+    if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) {
       goto err;
+    }
   }
-  compHuffmanCodes(&codeLenCodeTab, flateMaxCodeLenCodes);
+  compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab);
 
-  // set up code arrays
-  litCodeTab.codes = allCodes;
-  distCodeTab.codes = allCodes + numLitCodes;
-
-  // read literal and distance code tables
+  // build the literal and distance code tables
   len = 0;
   repeat = 0;
   i = 0;
   while (i < numLitCodes + numDistCodes) {
-    if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF)
+    if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) {
       goto err;
+    }
     if (code == 16) {
-      if ((repeat = getCodeWord(2)) == EOF)
+      if ((repeat = getCodeWord(2)) == EOF) {
+       goto err;
+      }
+      repeat += 3;
+      if (i + repeat > numLitCodes + numDistCodes) {
        goto err;
-      for (repeat += 3; repeat > 0; --repeat)
-       allCodes[i++].len = len;
+      }
+      for (; repeat > 0; --repeat) {
+       codeLengths[i++] = len;
+      }
     } else if (code == 17) {
-      if ((repeat = getCodeWord(3)) == EOF)
+      if ((repeat = getCodeWord(3)) == EOF) {
+       goto err;
+      }
+      repeat += 3;
+      if (i + repeat > numLitCodes + numDistCodes) {
        goto err;
+      }
       len = 0;
-      for (repeat += 3; repeat > 0; --repeat)
-       allCodes[i++].len = 0;
+      for (; repeat > 0; --repeat) {
+       codeLengths[i++] = 0;
+      }
     } else if (code == 18) {
-      if ((repeat = getCodeWord(7)) == EOF)
+      if ((repeat = getCodeWord(7)) == EOF) {
+       goto err;
+      }
+      repeat += 11;
+      if (i + repeat > numLitCodes + numDistCodes) {
        goto err;
+      }
       len = 0;
-      for (repeat += 11; repeat > 0; --repeat)
-       allCodes[i++].len = 0;
+      for (; repeat > 0; --repeat) {
+       codeLengths[i++] = 0;
+      }
     } else {
-      allCodes[i++].len = len = code;
+      codeLengths[i++] = len = code;
     }
   }
-  compHuffmanCodes(&litCodeTab, numLitCodes);
-  compHuffmanCodes(&distCodeTab, numDistCodes);
+  compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab);
+  compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab);
 
+  gfree(codeLenCodeTab.codes);
   return gTrue;
 
 err:
   error(getPos(), "Bad dynamic code table in flate stream");
+  gfree(codeLenCodeTab.codes);
   return gFalse;
 }
 
-// On entry, the <tab->codes> array contains the lengths of each code,
-// stored in code value order.  This function computes the code words.
-// The result is sorted in order of (1) code length and (2) code word.
-// The length values are no longer valid.  The <tab->start> array is
-// filled with the indexes of the first code of each length.
-void FlateStream::compHuffmanCodes(FlateHuffmanTab *tab, int n) {
-  int numLengths[flateMaxHuffman+1];
-  int nextCode[flateMaxHuffman+1];
-  int nextIndex[flateMaxHuffman+2];
-  int code;
-  int i, j;
+// Convert an array <lengths> of <n> lengths, in value order, into a
+// Huffman code lookup table.
+void FlateStream::compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab) {
+  int tabSize, len, code, code2, skip, val, i, t;
 
-  // count number of codes for each code length
-  for (i = 0; i <= flateMaxHuffman; ++i)
-    numLengths[i] = 0;
-  for (i = 0; i < n; ++i)
-    ++numLengths[tab->codes[i].len];
+  // find max code length
+  tab->maxLen = 0;
+  for (val = 0; val < n; ++val) {
+    if (lengths[val] > tab->maxLen) {
+      tab->maxLen = lengths[val];
+    }
+  }
 
-  // compute first index for each length
-  tab->start[0] = nextIndex[0] = 0;
-  for (i = 1; i <= flateMaxHuffman + 1; ++i)
-    tab->start[i] = nextIndex[i] = tab->start[i-1] + numLengths[i-1];
+  // allocate the table
+  tabSize = 1 << tab->maxLen;
+  tab->codes = (FlateCode *)gmalloc(tabSize * sizeof(FlateCode));
 
-  // compute first code for each length
-  code = 0;
-  numLengths[0] = 0;
-  for (i = 1; i <= flateMaxHuffman; ++i) {
-    code = (code + numLengths[i-1]) << 1;
-    nextCode[i] = code;
+  // clear the table
+  for (i = 0; i < tabSize; ++i) {
+    tab->codes[i].len = 0;
+    tab->codes[i].val = 0;
   }
 
-  // compute the codes -- this permutes the codes array from value
-  // order to length/code order
-  for (i = 0; i < n; ++i) {
-    j = nextIndex[tab->codes[i].len]++;
-    if (tab->codes[i].len == 0)
-      tab->codes[j].code = 0;
-    else
-      tab->codes[j].code = nextCode[tab->codes[i].len]++;
-    tab->codes[j].val = i;
+  // build the table
+  for (len = 1, code = 0, skip = 2;
+       len <= tab->maxLen;
+       ++len, code <<= 1, skip <<= 1) {
+    for (val = 0; val < n; ++val) {
+      if (lengths[val] == len) {
+
+       // bit-reverse the code
+       code2 = 0;
+       t = code;
+       for (i = 0; i < len; ++i) {
+         code2 = (code2 << 1) | (t & 1);
+         t >>= 1;
+       }
+
+       // fill in the table entries
+       for (i = code2; i < tabSize; i += skip) {
+         tab->codes[i].len = (Gushort)len;
+         tab->codes[i].val = (Gushort)val;
+       }
+
+       ++code;
+      }
+    }
   }
 }
 
 int FlateStream::getHuffmanCodeWord(FlateHuffmanTab *tab) {
-  int len;
-  int code;
+  FlateCode *code;
   int c;
-  int i, j;
 
-  code = 0;
-  for (len = 1; len <= flateMaxHuffman; ++len) {
-
-    // add a bit to the code
-    if (codeSize == 0) {
-      if ((c = str->getChar()) == EOF)
-       return EOF;
-      codeBuf = c & 0xff;
-      codeSize = 8;
-    }
-    code = (code << 1) | (codeBuf & 1);
-    codeBuf >>= 1;
-    --codeSize;
-
-    // look for code
-    i = tab->start[len];
-    j = tab->start[len + 1];
-    if (i < j && code >= tab->codes[i].code && code <= tab->codes[j-1].code) {
-      i += code - tab->codes[i].code;
-      return tab->codes[i].val;
+  while (codeSize < tab->maxLen) {
+    if ((c = str->getChar()) == EOF) {
+      break;
     }
+    codeBuf |= (c & 0xff) << codeSize;
+    codeSize += 8;
   }
-
-  // not found
-  error(getPos(), "Bad code (%04x) in flate stream", code);
-  return EOF;
+  code = &tab->codes[codeBuf & ((1 << tab->maxLen) - 1)];
+  if (codeSize == 0 || codeSize < code->len || code->len == 0) {
+    return EOF;
+  }
+  codeBuf >>= code->len;
+  codeSize -= code->len;
+  return (int)code->val;
 }
 
 int FlateStream::getCodeWord(int bits) {
@@ -3385,9 +3740,6 @@ void FixedLengthEncoder::reset() {
   count = 0;
 }
 
-void FixedLengthEncoder::close() {
-}
-
 int FixedLengthEncoder::getChar() {
   if (length >= 0 && count >= length)
     return EOF;
@@ -3401,6 +3753,10 @@ int FixedLengthEncoder::lookChar() {
   return str->getChar();
 }
 
+GBool FixedLengthEncoder::isBinary(GBool last) {
+  return str->isBinary(gTrue);
+}
+
 //------------------------------------------------------------------------
 // ASCIIHexEncoder
 //------------------------------------------------------------------------
@@ -3425,9 +3781,6 @@ void ASCIIHexEncoder::reset() {
   eof = gFalse;
 }
 
-void ASCIIHexEncoder::close() {
-}
-
 GBool ASCIIHexEncoder::fillBuf() {
   static char *hex = "0123456789abcdef";
   int c;
@@ -3474,9 +3827,6 @@ void ASCII85Encoder::reset() {
   eof = gFalse;
 }
 
-void ASCII85Encoder::close() {
-}
-
 GBool ASCII85Encoder::fillBuf() {
   Gulong t;
   char buf1[5];
@@ -3544,9 +3894,6 @@ void RunLengthEncoder::reset() {
   eof = gFalse;
 }
 
-void RunLengthEncoder::close() {
-}
-
 //
 // When fillBuf finishes, buf[] looks like this:
 //   +-----+--------------+-----------------+--