]> www.fi.muni.cz Git - evince.git/blobdiff - pdf/xpdf/Stream.cc
add a FIXME. We should probably not allocate a bookmark object every time
[evince.git] / pdf / xpdf / Stream.cc
index d7040cee18073721ce94d80439d35df1c828c183..3d19436774229ebc9b34245c2e9d856c43edc232 100644 (file)
@@ -2,11 +2,13 @@
 //
 // Stream.cc
 //
-// Copyright 1996 Derek B. Noonburg
+// Copyright 1996-2003 Glyph & Cog, LLC
 //
 //========================================================================
 
-#ifdef __GNUC__
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
 #pragma implementation
 #endif
 
 #include <string.h>
 #include <ctype.h>
 #include "gmem.h"
-#include "config.h"
+#include "gfile.h"
+#include "xpdfconfig.h"
 #include "Error.h"
 #include "Object.h"
+#ifndef NO_DECRYPTION
+#include "Decrypt.h"
+#endif
 #include "Stream.h"
+#include "JBIG2Stream.h"
+#include "JPXStream.h"
 #include "Stream-CCITT.h"
 
-#ifdef VMS
-#if (__VMS_VER < 70000000)
-extern "C" int unlink(char *filename);
+#ifdef __DJGPP__
+static GBool setDJSYSFLAGS = gFalse;
 #endif
+
+#ifdef VMS
 #ifdef __GNUC__
 #define SEEK_SET 0
 #define SEEK_CUR 1
@@ -36,42 +45,23 @@ extern "C" int unlink(char *filename);
 #endif
 #endif
 
-//------------------------------------------------------------------------
-
-#define headerSearchSize 1024  // read this many bytes at beginning of
-                               //   file to look for '%PDF'
-
 //------------------------------------------------------------------------
 // Stream (base class)
 //------------------------------------------------------------------------
 
 Stream::Stream() {
   ref = 1;
-  predictor = 1;
-  rawLine = NULL;
-  pixLine = NULL;
 }
 
 Stream::~Stream() {
-  gfree(rawLine);
-  gfree(pixLine);
-}
-
-void Stream::resetImage(int width1, int nComps1, int nBits1) {
-  reset();
-  if (predictor > 1 &&
-      (width1 != width || nComps != nComps || nBits1 != nBits))
-    error(-1, "Mismatched image parameters in predictor");
-  width = width1;
-  nComps = nComps1;
-  nBits = nBits1;
-  nVals = width * nComps;
-  pixBytes = (nComps * nBits + 7) >> 3;
-  rowBytes = (nVals * nBits + 7) >> 3;
-  rawLine = (Guchar *)grealloc(rawLine, rowBytes + pixBytes);
-  memset(rawLine, 0, rowBytes);
-  pixLine = (Guchar *)grealloc(pixLine, ((nVals + 7) & ~7) * sizeof(Guchar));
-  pixIdx = nVals;
+}
+
+void Stream::close() {
+}
+
+int Stream::getRawChar() {
+  error(-1, "Internal: called getRawChar() on non-predictor stream");
+  return EOF;
 }
 
 char *Stream::getLine(char *buf, int size) {
@@ -95,159 +85,7 @@ char *Stream::getLine(char *buf, int size) {
   return buf;
 }
 
-GBool Stream::getImagePixel(Guchar *pix) {
-  int curPred;
-  int left, up, upLeft, p, pa, pb, pc;
-  Guchar upLeftBuf[4];
-  Gulong buf, bitMask;
-  int c;
-  int bits;
-  int i, j;
-
-  // read an image line
-  if (pixIdx >= nVals) {
-
-    // get PNG optimum predictor number
-    if (predictor == 15) {
-      if ((curPred = getChar()) == EOF)
-       return EOF;
-      curPred += 10;
-    } else {
-      curPred = predictor;
-    }
-
-    // read the raw line, apply byte predictor
-    upLeftBuf[0] = upLeftBuf[1] = upLeftBuf[2] = upLeftBuf[3] = 0;
-    for (i = 0; i < rowBytes; ++i) {
-      upLeftBuf[3] = upLeftBuf[2];
-      upLeftBuf[2] = upLeftBuf[1];
-      upLeftBuf[1] = upLeftBuf[0];
-      upLeftBuf[0] = rawLine[pixBytes+i];
-      if ((c = getChar()) == EOF)
-       return EOF;
-      switch (curPred) {
-      case 11:                 // PNG sub
-       rawLine[pixBytes+i] = rawLine[i] + (Guchar)c;
-       break;
-      case 12:                 // PNG up
-       rawLine[pixBytes+i] = rawLine[pixBytes+i] + (Guchar)c;
-       break;
-      case 13:                 // PNG average
-       rawLine[pixBytes+i] = ((rawLine[i] + rawLine[pixBytes+i]) >> 1) +
-                             (Guchar)c;
-       break;
-      case 14:                 // PNG Paeth
-       left = rawLine[i];
-       up = rawLine[pixBytes+i];
-       upLeft = upLeftBuf[pixBytes];
-       p = left + up - upLeft;
-       if ((pa = p - left) < 0)
-         pa = -pa;
-       if ((pb = p - up) < 0)
-         pb = -pb;
-       if ((pc = p - upLeft) < 0)
-         pc = -pc;
-       if (pa <= pb && pa <= pc)
-         rawLine[pixBytes+i] = pa + (Guchar)c;
-       else if (pb <= pc)
-         rawLine[pixBytes+i] = pb + (Guchar)c;
-       else
-         rawLine[pixBytes+i] = pc + (Guchar)c;
-       break;
-      case 10:                 // PNG none
-      default:                 // no predictor or TIFF predictor
-       rawLine[pixBytes+i] = (Guchar)c;
-       break;
-      }
-    }
-
-    // convert into pixels, apply component predictor
-    if (predictor == 2) {
-      if (nBits == 1) {
-       for (i = 0, j = pixBytes; i < nVals; i += 8, ++j) {
-         c = rawLine[j];
-         pixLine[i+0] = (Guchar)((pixLine[i+0] + (c >> 7)) & 1);
-         pixLine[i+1] = (Guchar)((pixLine[i+1] + (c >> 6)) & 1);
-         pixLine[i+2] = (Guchar)((pixLine[i+2] + (c >> 5)) & 1);
-         pixLine[i+3] = (Guchar)((pixLine[i+3] + (c >> 4)) & 1);
-         pixLine[i+4] = (Guchar)((pixLine[i+4] + (c >> 3)) & 1);
-         pixLine[i+5] = (Guchar)((pixLine[i+5] + (c >> 2)) & 1);
-         pixLine[i+6] = (Guchar)((pixLine[i+6] + (c >> 1)) & 1);
-         pixLine[i+7] = (Guchar)((pixLine[i+7] + c) & 1);
-       }
-      } else if (nBits == 8) {
-       for (i = 0, j = pixBytes; i < nVals; ++i, ++j)
-         pixLine[i] = pixLine[i] + rawLine[j];
-      } else {
-       bitMask = (1 << nBits) - 1;
-       buf = 0;
-       bits = 0;
-       j = pixBytes;
-       for (i = 0; i < nVals; ++i) {
-         if (bits < nBits) {
-           buf = (buf << 8) | (rawLine[j++] & 0xff);
-           bits += 8;
-         }
-         pixLine[i] = (Guchar)((pixLine[i] +
-                                (buf >> (bits - nBits))) & bitMask);
-         bits -= nBits;
-       }
-      }
-    } else {
-      if (nBits == 1) {
-       for (i = 0, j = pixBytes; i < nVals; i += 8, ++j) {
-         c = rawLine[j];
-         pixLine[i+0] = (Guchar)((c >> 7) & 1);
-         pixLine[i+1] = (Guchar)((c >> 6) & 1);
-         pixLine[i+2] = (Guchar)((c >> 5) & 1);
-         pixLine[i+3] = (Guchar)((c >> 4) & 1);
-         pixLine[i+4] = (Guchar)((c >> 3) & 1);
-         pixLine[i+5] = (Guchar)((c >> 2) & 1);
-         pixLine[i+6] = (Guchar)((c >> 1) & 1);
-         pixLine[i+7] = (Guchar)(c & 1);
-       }
-      } else if (nBits == 8) {
-       for (i = 0, j = pixBytes; i < nVals; ++i, ++j)
-         pixLine[i] = rawLine[j];
-      } else {
-       bitMask = (1 << nBits) - 1;
-       buf = 0;
-       bits = 0;
-       j = pixBytes;
-       for (i = 0; i < nVals; ++i) {
-         if (bits < nBits) {
-           buf = (buf << 8) | (rawLine[j++] & 0xff);
-           bits += 8;
-         }
-         pixLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
-         bits -= nBits;
-       }
-      }
-    }
-
-    // read from start of line
-    pixIdx = 0;
-  }
-
-  for (i = 0; i < nComps; ++i)
-    pix[i] = pixLine[pixIdx++];
-  return gTrue;
-}
-
-void Stream::skipImageLine() {
-  int n, i;
-
-  n = (nVals * nBits + 7) / 8;
-  for (i = 0; i < n; ++i)
-    getChar();
-  pixIdx = nVals;
-}
-
-void Stream::setPos(int pos) {
-  error(-1, "Internal: called setPos() on non-FileStream");
-}
-
-GString *Stream::getPSFilter(char *indent) {
+GString *Stream::getPSFilter(int psLevel, char *indent) {
   return new GString();
 }
 
@@ -301,10 +139,9 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
   int bits;
   int early;
   int encoding;
-  GBool byteAlign;
-  GBool black;
+  GBool endOfLine, byteAlign, endOfBlock, black;
   int columns, rows;
-  Object obj;
+  Object globals, obj;
 
   if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) {
     str = new ASCIIHexStream(str);
@@ -343,33 +180,51 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
     str = new RunLengthStream(str);
   } else if (!strcmp(name, "CCITTFaxDecode") || !strcmp(name, "CCF")) {
     encoding = 0;
+    endOfLine = gFalse;
     byteAlign = gFalse;
     columns = 1728;
     rows = 0;
+    endOfBlock = gTrue;
     black = gFalse;
     if (params->isDict()) {
       params->dictLookup("K", &obj);
-      if (obj.isInt())
+      if (obj.isInt()) {
        encoding = obj.getInt();
+      }
+      obj.free();
+      params->dictLookup("EndOfLine", &obj);
+      if (obj.isBool()) {
+       endOfLine = obj.getBool();
+      }
       obj.free();
       params->dictLookup("EncodedByteAlign", &obj);
-      if (obj.isBool())
+      if (obj.isBool()) {
        byteAlign = obj.getBool();
+      }
       obj.free();
       params->dictLookup("Columns", &obj);
-      if (obj.isInt())
+      if (obj.isInt()) {
        columns = obj.getInt();
+      }
       obj.free();
       params->dictLookup("Rows", &obj);
-      if (obj.isInt())
+      if (obj.isInt()) {
        rows = obj.getInt();
+      }
+      obj.free();
+      params->dictLookup("EndOfBlock", &obj);
+      if (obj.isBool()) {
+       endOfBlock = obj.getBool();
+      }
       obj.free();
       params->dictLookup("BlackIs1", &obj);
-      if (obj.isBool())
+      if (obj.isBool()) {
        black = obj.getBool();
+      }
       obj.free();
     }
-    str = new CCITTFaxStream(str, encoding, byteAlign, columns, rows, black);
+    str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign,
+                            columns, rows, endOfBlock, black);
   } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) {
     str = new DCTStream(str);
   } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) {
@@ -396,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);
@@ -404,210 +267,678 @@ Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {
 }
 
 //------------------------------------------------------------------------
-// FileStream
+// BaseStream
 //------------------------------------------------------------------------
 
-FileStream::FileStream(FILE *f1, int start1, int length1, Object *dict1) {
-  f = f1;
-  start = start1;
-  length = length1;
-  bufPtr = bufEnd = buf;
-  bufPos = start;
-  savePos = -1;
-  dict = *dict1;
+BaseStream::BaseStream(Object *dictA) {
+  dict = *dictA;
+#ifndef NO_DECRYPTION
+  decrypt = NULL;
+#endif
 }
 
-FileStream::~FileStream() {
-  if (savePos >= 0)
-    fseek(f, savePos, SEEK_SET);
+BaseStream::~BaseStream() {
   dict.free();
+#ifndef NO_DECRYPTION
+  if (decrypt)
+    delete decrypt;
+#endif
 }
 
-void FileStream::reset() {
-  savePos = (int)ftell(f);
-  fseek(f, start, SEEK_SET);
-  bufPtr = bufEnd = buf;
-  bufPos = start;
+#ifndef NO_DECRYPTION
+void BaseStream::doDecryption(Guchar *fileKey, int keyLength,
+                             int objNum, int objGen) {
+  decrypt = new Decrypt(fileKey, keyLength, objNum, objGen);
 }
+#endif
 
-GBool FileStream::fillBuf() {
-  int n;
+//------------------------------------------------------------------------
+// FilterStream
+//------------------------------------------------------------------------
 
-  bufPos += bufEnd - buf;
-  bufPtr = bufEnd = buf;
-  if (length >= 0 && bufPos >= start + length)
-    return gFalse;
-  if (length >= 0 && bufPos + 256 > start + length)
-    n = start + length - bufPos;
-  else
-    n = 256;
-  n = fread(buf, 1, n, f);
-  bufEnd = buf + n;
-  if (bufPtr >= bufEnd)
-    return gFalse;
-  return gTrue;
+FilterStream::FilterStream(Stream *strA) {
+  str = strA;
+}
+
+FilterStream::~FilterStream() {
 }
 
-void FileStream::setPos(int pos1) {
-  long size;
+void FilterStream::close() {
+  str->close();
+}
+
+void FilterStream::setPos(Guint pos, int dir) {
+  error(-1, "Internal: called setPos() on FilterStream");
+}
+
+//------------------------------------------------------------------------
+// ImageStream
+//------------------------------------------------------------------------
+
+ImageStream::ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA) {
+  int imgLineSize;
+
+  str = strA;
+  width = widthA;
+  nComps = nCompsA;
+  nBits = nBitsA;
 
-  if (pos1 >= 0) {
-    fseek(f, pos1, SEEK_SET);
-    bufPos = pos1;
+  nVals = width * nComps;
+  if (nBits == 1) {
+    imgLineSize = (nVals + 7) & ~7;
   } else {
-    fseek(f, 0, SEEK_END);
-    size = ftell(f);
-    if (pos1 < -size)
-      pos1 = (int)(-size);
-    fseek(f, pos1, SEEK_END);
-    bufPos = (int)ftell(f);
+    imgLineSize = nVals;
   }
-  bufPtr = bufEnd = buf;
+  imgLine = (Guchar *)gmalloc(imgLineSize * sizeof(Guchar));
+  imgIdx = nVals;
 }
 
-GBool FileStream::checkHeader() {
-  char hdrBuf[headerSearchSize+1];
-  char *p;
-  double version;
+ImageStream::~ImageStream() {
+  gfree(imgLine);
+}
+
+void ImageStream::reset() {
+  str->reset();
+}
+
+GBool ImageStream::getPixel(Guchar *pix) {
   int i;
 
-  for (i = 0; i < headerSearchSize; ++i)
-    hdrBuf[i] = getChar();
-  hdrBuf[headerSearchSize] = '\0';
-  for (i = 0; i < headerSearchSize - 5; ++i) {
-    if (!strncmp(&hdrBuf[i], "%PDF-", 5))
-      break;
-  }
-  if (i >= headerSearchSize - 5) {
-    error(-1, "May not be a PDF file (continuing anyway)");
-    return gFalse;
+  if (imgIdx >= nVals) {
+    getLine();
+    imgIdx = 0;
   }
-  start += i;
-  p = strtok(&hdrBuf[i+5], " \t\n\r");
-  version = atof(p);
-  if (!(hdrBuf[i+5] >= '0' && hdrBuf[i+5] <= '9') || version > pdfVersionNum) {
-    error(getPos(), "PDF version %s -- xpdf supports version %s"
-         " (continuing anyway)", p, pdfVersion);
-    return gFalse;
+  for (i = 0; i < nComps; ++i) {
+    pix[i] = imgLine[imgIdx++];
   }
   return gTrue;
 }
 
-//------------------------------------------------------------------------
-// SubStream
-//------------------------------------------------------------------------
+Guchar *ImageStream::getLine() {
+  Gulong buf, bitMask;
+  int bits;
+  int c;
+  int i;
 
-SubStream::SubStream(Stream *str1, Object *dict1) {
-  str = str1;
-  dict = *dict1;
+  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;
+    }
+  }
+  return imgLine;
 }
 
-SubStream::~SubStream() {
-  dict.free();
+void ImageStream::skipLine() {
+  int n, i;
+
+  n = (nVals * nBits + 7) >> 3;
+  for (i = 0; i < n; ++i) {
+    str->getChar();
+  }
 }
 
 //------------------------------------------------------------------------
-// ASCIIHexStream
+// StreamPredictor
 //------------------------------------------------------------------------
 
-ASCIIHexStream::ASCIIHexStream(Stream *str1) {
-  str = str1;
-  buf = EOF;
-  eof = gFalse;
-}
+StreamPredictor::StreamPredictor(Stream *strA, int predictorA,
+                                int widthA, int nCompsA, int nBitsA) {
+  str = strA;
+  predictor = predictorA;
+  width = widthA;
+  nComps = nCompsA;
+  nBits = nBitsA;
 
-ASCIIHexStream::~ASCIIHexStream() {
-  delete str;
+  nVals = width * nComps;
+  pixBytes = (nComps * nBits + 7) >> 3;
+  rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes;
+  predLine = (Guchar *)gmalloc(rowBytes);
+  memset(predLine, 0, rowBytes);
+  predIdx = rowBytes;
 }
 
-void ASCIIHexStream::reset() {
-  str->reset();
-  buf = EOF;
-  eof = gFalse;
+StreamPredictor::~StreamPredictor() {
+  gfree(predLine);
 }
 
-int ASCIIHexStream::lookChar() {
-  int c1, c2, x;
-
-  if (buf != EOF)
-    return buf;
-  if (eof) {
-    buf = EOF;
-    return EOF;
-  }
-  do {
-    c1 = str->getChar();
-  } while (isspace(c1));
-  if (c1 == '>') {
-    eof = gTrue;
-    buf = EOF;
-    return buf;
+int StreamPredictor::lookChar() {
+  if (predIdx >= rowBytes) {
+    if (!getNextLine()) {
+      return EOF;
+    }
   }
-  do {
-    c2 = str->getChar();
-  } while (isspace(c2));
-  if (c2 == '>') {
-    eof = gTrue;
-    c2 = '0';
+  return predLine[predIdx];
+}
+
+int StreamPredictor::getChar() {
+  if (predIdx >= rowBytes) {
+    if (!getNextLine()) {
+      return EOF;
+    }
   }
-  if (c1 >= '0' && c1 <= '9') {
-    x = (c1 - '0') << 4;
-  } else if (c1 >= 'A' && c1 <= 'F') {
-    x = (c1 - 'A' + 10) << 4;
-  } else if (c1 >= 'a' && c1 <= 'f') {
-    x = (c1 - 'a' + 10) << 4;
-  } else if (c1 == EOF) {
-    eof = gTrue;
-    x = 0;
+  return predLine[predIdx++];
+}
+
+GBool StreamPredictor::getNextLine() {
+  int curPred;
+  Guchar upLeftBuf[4];
+  int left, up, upLeft, p, pa, pb, pc;
+  int c;
+  Gulong inBuf, outBuf, bitMask;
+  int inBits, outBits;
+  int i, j, k;
+
+  // get PNG optimum predictor number
+  if (predictor >= 10) {
+    if ((curPred = str->getRawChar()) == EOF) {
+      return gFalse;
+    }
+    curPred += 10;
   } else {
-    error(getPos(), "Illegal character <%02x> in ASCIIHex stream", c1);
-    x = 0;
+    curPred = predictor;
   }
-  if (c2 >= '0' && c2 <= '9') {
-    x += c2 - '0';
-  } else if (c2 >= 'A' && c2 <= 'F') {
-    x += c2 - 'A' + 10;
-  } else if (c2 >= 'a' && c2 <= 'f') {
-    x += c2 - 'a' + 10;
-  } else if (c2 == EOF) {
-    eof = gTrue;
-    x = 0;
-  } else {
-    error(getPos(), "Illegal character <%02x> in ASCIIHex stream", c2);
+
+  // read the raw line, apply PNG (byte) predictor
+  upLeftBuf[0] = upLeftBuf[1] = upLeftBuf[2] = upLeftBuf[3] = 0;
+  for (i = pixBytes; i < rowBytes; ++i) {
+    upLeftBuf[3] = upLeftBuf[2];
+    upLeftBuf[2] = upLeftBuf[1];
+    upLeftBuf[1] = upLeftBuf[0];
+    upLeftBuf[0] = predLine[i];
+    if ((c = str->getRawChar()) == EOF) {
+      return gFalse;
+    }
+    switch (curPred) {
+    case 11:                   // PNG sub
+      predLine[i] = predLine[i - pixBytes] + (Guchar)c;
+      break;
+    case 12:                   // PNG up
+      predLine[i] = predLine[i] + (Guchar)c;
+      break;
+    case 13:                   // PNG average
+      predLine[i] = ((predLine[i - pixBytes] + predLine[i]) >> 1) +
+                   (Guchar)c;
+      break;
+    case 14:                   // PNG Paeth
+      left = predLine[i - pixBytes];
+      up = predLine[i];
+      upLeft = upLeftBuf[pixBytes];
+      p = left + up - upLeft;
+      if ((pa = p - left) < 0)
+       pa = -pa;
+      if ((pb = p - up) < 0)
+       pb = -pb;
+      if ((pc = p - upLeft) < 0)
+       pc = -pc;
+      if (pa <= pb && pa <= pc)
+       predLine[i] = left + (Guchar)c;
+      else if (pb <= pc)
+       predLine[i] = up + (Guchar)c;
+      else
+       predLine[i] = upLeft + (Guchar)c;
+      break;
+    case 10:                   // PNG none
+    default:                   // no predictor or TIFF predictor
+      predLine[i] = (Guchar)c;
+      break;
+    }
   }
-  buf = x & 0xff;
-  return buf;
-}
 
-GString *ASCIIHexStream::getPSFilter(char *indent) {
-  GString *s;
+  // apply TIFF (component) predictor
+  if (predictor == 2) {
+    if (nBits == 1) {
+      inBuf = predLine[pixBytes - 1];
+      for (i = pixBytes; i < rowBytes; i += 8) {
+       // 1-bit add is just xor
+       inBuf = (inBuf << 8) | predLine[i];
+       predLine[i] ^= inBuf >> nComps;
+      }
+    } else if (nBits == 8) {
+      for (i = pixBytes; i < rowBytes; ++i) {
+       predLine[i] += predLine[i - nComps];
+      }
+    } else {
+      upLeftBuf[0] = upLeftBuf[1] = upLeftBuf[2] = upLeftBuf[3] = 0;
+      bitMask = (1 << nBits) - 1;
+      inBuf = outBuf = 0;
+      inBits = outBits = 0;
+      j = k = pixBytes;
+      for (i = 0; i < nVals; ++i) {
+       if (inBits < nBits) {
+         inBuf = (inBuf << 8) | (predLine[j++] & 0xff);
+         inBits += 8;
+       }
+       upLeftBuf[3] = upLeftBuf[2];
+       upLeftBuf[2] = upLeftBuf[1];
+       upLeftBuf[1] = upLeftBuf[0];
+       upLeftBuf[0] = (upLeftBuf[nComps] +
+                       (inBuf >> (inBits - nBits))) & bitMask;
+       outBuf = (outBuf << nBits) | upLeftBuf[0];
+       inBits -= nBits;
+       outBits += nBits;
+       if (outBits > 8) {
+         predLine[k++] = (Guchar)(outBuf >> (outBits - 8));
+       }
+      }
+      if (outBits > 0) {
+       predLine[k++] = (Guchar)(outBuf << (8 - outBits));
+      }
+    }
+  }
 
-  s = str->getPSFilter(indent);
-  s->append(indent)->append("/ASCIIHexDecode filter\n");
-  return s;
-}
+  // reset to start of line
+  predIdx = pixBytes;
 
-GBool ASCIIHexStream::isBinary(GBool last) {
-  return str->isBinary(gFalse);
+  return gTrue;
 }
 
 //------------------------------------------------------------------------
-// ASCII85Stream
+// FileStream
 //------------------------------------------------------------------------
 
-ASCII85Stream::ASCII85Stream(Stream *str1) {
-  str = str1;
-  index = n = 0;
-  eof = gFalse;
+FileStream::FileStream(FILE *fA, Guint startA, GBool limitedA,
+                      Guint lengthA, Object *dictA):
+    BaseStream(dictA) {
+  f = fA;
+  start = startA;
+  limited = limitedA;
+  length = lengthA;
+  bufPtr = bufEnd = buf;
+  bufPos = start;
+  savePos = 0;
+  saved = gFalse;
 }
 
-ASCII85Stream::~ASCII85Stream() {
-  delete str;
+FileStream::~FileStream() {
+  close();
 }
 
-void ASCII85Stream::reset() {
-  str->reset();
-  index = n = 0;
+Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,
+                                 Guint lengthA, Object *dictA) {
+  return new FileStream(f, startA, limitedA, lengthA, dictA);
+}
+
+void FileStream::reset() {
+#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
+  savePos = (Guint)ftell(f);
+  fseek(f, start, SEEK_SET);
+#endif
+  saved = gTrue;
+  bufPtr = bufEnd = buf;
+  bufPos = start;
+#ifndef NO_DECRYPTION
+  if (decrypt)
+    decrypt->reset();
+#endif
+}
+
+void FileStream::close() {
+  if (saved) {
+#if HAVE_FSEEKO
+    fseeko(f, savePos, SEEK_SET);
+#elif HAVE_FSEEK64
+    fseek64(f, savePos, SEEK_SET);
+#else
+    fseek(f, savePos, SEEK_SET);
+#endif
+    saved = gFalse;
+  }
+}
+
+GBool FileStream::fillBuf() {
+  int n;
+#ifndef NO_DECRYPTION
+  char *p;
+#endif
+
+  bufPos += bufEnd - buf;
+  bufPtr = bufEnd = buf;
+  if (limited && bufPos >= start + length) {
+    return gFalse;
+  }
+  if (limited && bufPos + fileStreamBufSize > start + length) {
+    n = start + length - bufPos;
+  } else {
+    n = fileStreamBufSize;
+  }
+  n = fread(buf, 1, n, f);
+  bufEnd = buf + n;
+  if (bufPtr >= bufEnd) {
+    return gFalse;
+  }
+#ifndef NO_DECRYPTION
+  if (decrypt) {
+    for (p = buf; p < bufEnd; ++p) {
+      *p = (char)decrypt->decryptByte((Guchar)*p);
+    }
+  }
+#endif
+  return gTrue;
+}
+
+void FileStream::setPos(Guint pos, int dir) {
+  Guint size;
+
+  if (dir >= 0) {
+#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_FSEEKO
+    fseeko(f, 0, SEEK_END);
+    size = (Guint)ftello(f);
+#elif HAVE_FSEEK64
+    fseek64(f, 0, SEEK_END);
+    size = (Guint)ftell64(f);
+#else
+    fseek(f, 0, SEEK_END);
+    size = (Guint)ftell(f);
+#endif
+    if (pos > size)
+      pos = (Guint)size;
+#ifdef __CYGWIN32__
+    //~ work around a bug in cygwin's implementation of fseek
+    rewind(f);
+#endif
+#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
+    fseek(f, -(int)pos, SEEK_END);
+    bufPos = (Guint)ftell(f);
+#endif
+  }
+  bufPtr = bufEnd = buf;
+}
+
+void FileStream::moveStart(int delta) {
+  start += delta;
+  bufPtr = bufEnd = buf;
+  bufPos = start;
+}
+
+//------------------------------------------------------------------------
+// MemStream
+//------------------------------------------------------------------------
+
+MemStream::MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA):
+    BaseStream(dictA) {
+  buf = bufA;
+  start = startA;
+  length = lengthA;
+  bufEnd = buf + start + length;
+  bufPtr = buf + start;
+  needFree = gFalse;
+}
+
+MemStream::~MemStream() {
+  if (needFree) {
+    gfree(buf);
+  }
+}
+
+Stream *MemStream::makeSubStream(Guint startA, GBool limited,
+                                Guint lengthA, Object *dictA) {
+  MemStream *subStr;
+  Guint newLength;
+
+  if (!limited || startA + lengthA > start + length) {
+    newLength = start + length - startA;
+  } else {
+    newLength = lengthA;
+  }
+  subStr = new MemStream(buf, startA, newLength, dictA);
+  return subStr;
+}
+
+void MemStream::reset() {
+  bufPtr = buf + start;
+#ifndef NO_DECRYPTION
+  if (decrypt) {
+    decrypt->reset();
+  }
+#endif
+}
+
+void MemStream::close() {
+}
+
+void MemStream::setPos(Guint pos, int dir) {
+  Guint i;
+
+  if (dir >= 0) {
+    i = pos;
+  } else {
+    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) {
+  start += delta;
+  bufPtr = buf + start;
+}
+
+#ifndef NO_DECRYPTION
+void MemStream::doDecryption(Guchar *fileKey, int keyLength,
+                            int objNum, int objGen) {
+  char *newBuf;
+  char *p, *q;
+
+  this->BaseStream::doDecryption(fileKey, keyLength, objNum, objGen);
+  if (decrypt) {
+    newBuf = (char *)gmalloc(length);
+    for (p = buf + start, q = newBuf; p < bufEnd; ++p, ++q) {
+      *q = (char)decrypt->decryptByte((Guchar)*p);
+    }
+    bufEnd = newBuf + length;
+    bufPtr = newBuf + (bufPtr - (buf + start));
+    start = 0;
+    buf = newBuf;
+    needFree = gTrue;
+  }
+}
+#endif
+
+//------------------------------------------------------------------------
+// EmbedStream
+//------------------------------------------------------------------------
+
+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 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");
+}
+
+Guint EmbedStream::getStart() {
+  error(-1, "Internal: called getStart() on EmbedStream");
+  return 0;
+}
+
+void EmbedStream::moveStart(int delta) {
+  error(-1, "Internal: called moveStart() on EmbedStream");
+}
+
+//------------------------------------------------------------------------
+// ASCIIHexStream
+//------------------------------------------------------------------------
+
+ASCIIHexStream::ASCIIHexStream(Stream *strA):
+    FilterStream(strA) {
+  buf = EOF;
+  eof = gFalse;
+}
+
+ASCIIHexStream::~ASCIIHexStream() {
+  delete str;
+}
+
+void ASCIIHexStream::reset() {
+  str->reset();
+  buf = EOF;
+  eof = gFalse;
+}
+
+int ASCIIHexStream::lookChar() {
+  int c1, c2, x;
+
+  if (buf != EOF)
+    return buf;
+  if (eof) {
+    buf = EOF;
+    return EOF;
+  }
+  do {
+    c1 = str->getChar();
+  } while (isspace(c1));
+  if (c1 == '>') {
+    eof = gTrue;
+    buf = EOF;
+    return buf;
+  }
+  do {
+    c2 = str->getChar();
+  } while (isspace(c2));
+  if (c2 == '>') {
+    eof = gTrue;
+    c2 = '0';
+  }
+  if (c1 >= '0' && c1 <= '9') {
+    x = (c1 - '0') << 4;
+  } else if (c1 >= 'A' && c1 <= 'F') {
+    x = (c1 - 'A' + 10) << 4;
+  } else if (c1 >= 'a' && c1 <= 'f') {
+    x = (c1 - 'a' + 10) << 4;
+  } else if (c1 == EOF) {
+    eof = gTrue;
+    x = 0;
+  } else {
+    error(getPos(), "Illegal character <%02x> in ASCIIHex stream", c1);
+    x = 0;
+  }
+  if (c2 >= '0' && c2 <= '9') {
+    x += c2 - '0';
+  } else if (c2 >= 'A' && c2 <= 'F') {
+    x += c2 - 'A' + 10;
+  } else if (c2 >= 'a' && c2 <= 'f') {
+    x += c2 - 'a' + 10;
+  } else if (c2 == EOF) {
+    eof = gTrue;
+    x = 0;
+  } else {
+    error(getPos(), "Illegal character <%02x> in ASCIIHex stream", c2);
+  }
+  buf = x & 0xff;
+  return buf;
+}
+
+GString *ASCIIHexStream::getPSFilter(int psLevel, char *indent) {
+  GString *s;
+
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
+    return NULL;
+  }
+  s->append(indent)->append("/ASCIIHexDecode filter\n");
+  return s;
+}
+
+GBool ASCIIHexStream::isBinary(GBool last) {
+  return str->isBinary(gFalse);
+}
+
+//------------------------------------------------------------------------
+// ASCII85Stream
+//------------------------------------------------------------------------
+
+ASCII85Stream::ASCII85Stream(Stream *strA):
+    FilterStream(strA) {
+  index = n = 0;
+  eof = gFalse;
+}
+
+ASCII85Stream::~ASCII85Stream() {
+  delete str;
+}
+
+void ASCII85Stream::reset() {
+  str->reset();
+  index = n = 0;
   eof = gFalse;
 }
 
@@ -655,10 +986,15 @@ int ASCII85Stream::lookChar() {
   return b[index];
 }
 
-GString *ASCII85Stream::getPSFilter(char *indent) {
+GString *ASCII85Stream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  s = str->getPSFilter(indent);
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
+    return NULL;
+  }
   s->append(indent)->append("/ASCII85Decode filter\n");
   return s;
 }
@@ -671,250 +1007,176 @@ GBool ASCII85Stream::isBinary(GBool last) {
 // LZWStream
 //------------------------------------------------------------------------
 
-LZWStream::LZWStream(Stream *str1, int predictor1, int columns1, int colors1,
-                    int bits1, int early1) {
-  str = str1;
-  predictor = predictor1;
-  if (predictor1 > 1) {
-    width = columns1;
-    nComps = colors1;
-    nBits = bits1;
-  }
-  early = early1;
-  zPipe = NULL;
-  bufPtr = bufEnd = buf;
+LZWStream::LZWStream(Stream *strA, int predictor, int columns, int colors,
+                    int bits, int earlyA):
+    FilterStream(strA) {
+  if (predictor != 1) {
+    pred = new StreamPredictor(this, predictor, columns, colors, bits);
+  } else {
+    pred = NULL;
+  }
+  early = earlyA;
+  eof = gFalse;
+  inputBits = 0;
+  clearTable();
 }
 
 LZWStream::~LZWStream() {
-  if (zPipe) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName);
+  if (pred) {
+    delete pred;
   }
   delete str;
 }
 
 int LZWStream::getChar() {
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff);
+  if (pred) {
+    return pred->getChar();
+  }
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex++];
 }
 
 int LZWStream::lookChar() {
-  return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff);
-}
-
-void LZWStream::reset() {
-  FILE *f;
-
-  str->reset();
-  bufPtr = bufEnd = buf;
-  if (zPipe) {
-#ifdef HAVE_POPEN
-    pclose(zPipe);
-#else
-    fclose(zPipe);
-#endif
-    zPipe = NULL;
-    unlink(zName);
-  }
-  strcpy(zCmd, uncompressCmd);
-  strcat(zCmd, " ");
-  zName = zCmd + strlen(zCmd);
-  tmpnam(zName);
-  strcat(zName, ".Z");
-  if (!(f = fopen(zName, "wb"))) {
-    error(getPos(), "Couldn't open temporary file '%s'", zName);
-    return;
-  }
-  dumpFile(f);
-  fclose(f);
-#ifdef HAVE_POPEN
-  if (!(zPipe = popen(zCmd, "r"))) {
-    error(getPos(), "Couldn't popen '%s'", zCmd);
-    unlink(zName);
-    return;
+  if (pred) {
+    return pred->lookChar();
   }
-#else
-#ifdef VMS
-  if (!system(zCmd)) {
-#else
-  if (system(zCmd)) {
-#endif
-    error(getPos(), "Couldn't execute '%s'", zCmd);
-    unlink(zName);
-    return;
+  if (eof) {
+    return EOF;
   }
-  zName[strlen(zName) - 2] = '\0';
-  if (!(zPipe = fopen(zName, "rb"))) {
-    error(getPos(), "Couldn't open uncompress file '%s'", zName);
-    unlink(zName);
-    return;
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
   }
-#endif
+  return seqBuf[seqIndex];
 }
 
-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;
-
-  // magic number
-  fputc(0x1f, f);
-  fputc(0x9d, f);
-
-  // max code length, block mode flag
-  fputc(0x8c, f);
+int LZWStream::getRawChar() {
+  if (eof) {
+    return EOF;
+  }
+  if (seqIndex >= seqLength) {
+    if (!processNextCode()) {
+      return EOF;
+    }
+  }
+  return seqBuf[seqIndex++];
+}
 
-  // init input side
-  inCodeBits = 9;
-  inputBuf = 0;
-  inputBits = 0;
+void LZWStream::reset() {
+  str->reset();
   eof = gFalse;
+  inputBits = 0;
+  clearTable();
+}
 
-  // init output side
-  outCodeBits = 9;
-
-  // clear table
-  first = gTrue;
-  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;
-      }
+GBool LZWStream::processNextCode() {
+  int code;
+  int nextLength;
+  int i, j;
 
-      // check for eof/clear
-      if (eof)
-       break;
-      if (clear) {
-       i = 8;
-       break;
-      }
-    }
+  // check for EOF
+  if (eof) {
+    return gFalse;
+  }
 
-    // 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 for eod and clear-table codes
+ start:
+  code = getCode();
+  if (code == EOF || code == 257) {
+    eof = gTrue;
+    return gFalse;
+  }
+  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;
+  }
+  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;
 
-    // check output code size
-    if (nextCode - 1 == 512 ||
-       nextCode - 1 == 1024 ||
-       nextCode - 1 == 2048 ||
-       nextCode - 1 == 4096) {
-      outCodeBits = inCodeBits;
-    }
+  return gTrue;
+}
 
-    // clear table if necessary
-    if (clear) {
-      inCodeBits = 9;
-      outCodeBits = 9;
-      first = gTrue;
-      nextCode = 258;
-      clear = gFalse;
-    }
-  } while (!eof);
+void LZWStream::clearTable() {
+  nextCode = 258;
+  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);
-  }
-  bufPtr = buf;
-  bufEnd = buf + n;
-  return n > 0;
-}
-
-GString *LZWStream::getPSFilter(char *indent) {
+GString *LZWStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  s = str->getPSFilter(indent);
+  if (psLevel < 2 || pred) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
+    return NULL;
+  }
   s->append(indent)->append("/LZWDecode filter\n");
   return s;
 }
@@ -927,8 +1189,8 @@ GBool LZWStream::isBinary(GBool last) {
 // RunLengthStream
 //------------------------------------------------------------------------
 
-RunLengthStream::RunLengthStream(Stream *str1) {
-  str = str1;
+RunLengthStream::RunLengthStream(Stream *strA):
+    FilterStream(strA) {
   bufPtr = bufEnd = buf;
   eof = gFalse;
 }
@@ -943,10 +1205,15 @@ void RunLengthStream::reset() {
   eof = gFalse;
 }
 
-GString *RunLengthStream::getPSFilter(char *indent) {
+GString *RunLengthStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  s = str->getPSFilter(indent);
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
+    return NULL;
+  }
   s->append(indent)->append("/RunLengthDecode filter\n");
   return s;
 }
@@ -985,18 +1252,22 @@ GBool RunLengthStream::fillBuf() {
 // CCITTFaxStream
 //------------------------------------------------------------------------
 
-CCITTFaxStream::CCITTFaxStream(Stream *str1, int encoding1, GBool byteAlign1,
-                              int columns1, int rows1, GBool black1) {
-  str = str1;
-  encoding = encoding1;
-  byteAlign = byteAlign1;
-  columns = columns1;
-  rows = rows1;
-  black = black1;
-  refLine = (short *)gmalloc((columns + 2) * sizeof(short));
+CCITTFaxStream::CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA,
+                              GBool byteAlignA, int columnsA, int rowsA,
+                              GBool endOfBlockA, GBool blackA):
+    FilterStream(strA) {
+  encoding = encodingA;
+  endOfLine = endOfLineA;
+  byteAlign = byteAlignA;
+  columns = columnsA;
+  rows = rowsA;
+  endOfBlock = endOfBlockA;
+  black = blackA;
+  refLine = (short *)gmalloc((columns + 3) * sizeof(short));
   codingLine = (short *)gmalloc((columns + 2) * sizeof(short));
 
   eof = gFalse;
+  row = 0;
   nextLine2D = encoding < 0;
   inputBits = 0;
   codingLine[0] = 0;
@@ -1013,39 +1284,48 @@ CCITTFaxStream::~CCITTFaxStream() {
 }
 
 void CCITTFaxStream::reset() {
+  short code1;
+
   str->reset();
   eof = gFalse;
+  row = 0;
   nextLine2D = encoding < 0;
   inputBits = 0;
-  if ((look13Bits() >> 1) == 0x001)
-    eatBits(12);
   codingLine[0] = 0;
   codingLine[1] = refLine[2] = columns;
   a0 = 1;
   buf = EOF;
+
+  // 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);
+    eatBits(1);
+  }
 }
 
 int CCITTFaxStream::lookChar() {
   short code1, code2, code3;
   int a0New;
+  GBool err, gotEOL;
   int ret;
   int bits, i;
 
   // if at eof just return EOF
-  if (eof && codingLine[a0] >= columns)
+  if (eof && codingLine[a0] >= columns) {
     return EOF;
+  }
 
   // read the next row
+  err = gFalse;
   if (codingLine[a0] >= columns) {
 
-    // check for end of file
-    i = look13Bits();
-    if (i == EOF || (i >> 1) == 0x001) {
-      eof = gTrue;
-      codingLine[a0 = 0] = columns;
-      return EOF;
-    }
-
     // 2-D encoding
     if (nextLine2D) {
       for (i = 0; codingLine[i] < columns; ++i)
@@ -1080,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];
@@ -1143,7 +1425,8 @@ int CCITTFaxStream::lookChar() {
          return EOF;
        default:
          error(getPos(), "Bad 2D code %04x in CCITTFax stream", code1);
-         return EOF;
+         err = gTrue;
+         break;
        }
       } while (codingLine[a0] < columns);
 
@@ -1170,13 +1453,83 @@ int CCITTFaxStream::lookChar() {
       }
     }
 
-    if (codingLine[a0] != columns)
+    if (codingLine[a0] != columns) {
       error(getPos(), "CCITTFax row is wrong length (%d)", codingLine[a0]);
+      // force the row to be the correct length
+      while (codingLine[a0] > columns) {
+       --a0;
+      }
+      codingLine[++a0] = columns;
+      err = gTrue;
+    }
+
+    // byte-align the row
+    if (byteAlign) {
+      inputBits &= ~7;
+    }
+
+    // check for end-of-line marker, skipping over any extra zero bits
+    gotEOL = gFalse;
+    if (!endOfBlock && row == rows - 1) {
+      eof = gTrue;
+    } else {
+      code1 = lookBits(12);
+      while (code1 == 0) {
+       eatBits(1);
+       code1 = lookBits(12);
+      }
+      if (code1 == 0x001) {
+       eatBits(12);
+       gotEOL = gTrue;
+      } else if (code1 == EOF) {
+       eof = gTrue;
+      }
+    }
+
+    // get 2D encoding tag
+    if (!eof && encoding > 0) {
+      nextLine2D = !lookBits(1);
+      eatBits(1);
+    }
 
-    // check for end-of-line marker
-    code1 = look13Bits();
-    if ((code1 >> 1) == 0x001) {
-      eatBits(12);
+    // check for end-of-block marker
+    if (endOfBlock && gotEOL) {
+      code1 = lookBits(12);
+      if (code1 == 0x001) {
+       eatBits(12);
+       if (encoding > 0) {
+         lookBits(1);
+         eatBits(1);
+       }
+       if (encoding >= 0) {
+         for (i = 0; i < 4; ++i) {
+           code1 = lookBits(12);
+           if (code1 != 0x001) {
+             error(getPos(), "Bad RTC code in CCITTFax stream");
+           }
+           eatBits(12);
+           if (encoding > 0) {
+             lookBits(1);
+             eatBits(1);
+           }
+         }
+       }
+       eof = gTrue;
+      }
+
+    // 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 = lookBits(13);
+      } while ((code1 >> 1) != 0x001);
+      eatBits(12); 
       if (encoding > 0) {
        eatBits(1);
        nextLine2D = !(code1 & 1);
@@ -1185,6 +1538,12 @@ int CCITTFaxStream::lookChar() {
 
     a0 = 0;
     outputBits = codingLine[1] - codingLine[0];
+    if (outputBits == 0) {
+      a0 = 1;
+      outputBits = codingLine[2] - codingLine[1];
+    }
+
+    ++row;
   }
 
   // get a byte
@@ -1192,8 +1551,9 @@ int CCITTFaxStream::lookChar() {
     ret = ((a0 & 1) == 0) ? 0xff : 0x00;
     if ((outputBits -= 8) == 0) {
       ++a0;
-      if (codingLine[a0] < columns)
+      if (codingLine[a0] < columns) {
        outputBits = codingLine[a0 + 1] - codingLine[a0];
+      }
     }
   } else {
     bits = 8;
@@ -1202,18 +1562,21 @@ int CCITTFaxStream::lookChar() {
       if (outputBits > bits) {
        i = bits;
        bits = 0;
-       if ((a0 & 1) == 0)
+       if ((a0 & 1) == 0) {
          ret |= 0xff >> (8 - i);
+       }
        outputBits -= i;
       } else {
        i = outputBits;
        bits -= outputBits;
-       if ((a0 & 1) == 0)
+       if ((a0 & 1) == 0) {
          ret |= (0xff >> (8 - i)) << bits;
+       }
        outputBits = 0;
        ++a0;
-       if (codingLine[a0] < columns)
+       if (codingLine[a0] < columns) {
          outputBits = codingLine[a0 + 1] - codingLine[a0];
+       }
       }
     } while (bits > 0 && codingLine[a0] < columns);
   }
@@ -1222,109 +1585,199 @@ int CCITTFaxStream::lookChar() {
 }
 
 short CCITTFaxStream::getTwoDimCode() {
-  short code, code0;
+  short code;
   CCITTCode *p;
+  int n;
 
-  code0 = look13Bits();
-  code = code0 >> 6;
-  if (code == 0x0002) {
-    eatBits(7);
-    return twoDimVertL3;
-  }
-  if (code == 0x0003) {
-    eatBits(7);
-    return twoDimVertR3;
-  }
-  code >>= 1;
-  if (code == 0x0002) {
-    eatBits(6);
-    return twoDimVertL2;
-  }
-  if (code == 0x0003) {
-    eatBits(6);
-    return twoDimVertR2;
-  }
-  code >>= 2;
-  p = &twoDimTab1[code];
-  if (p->bits > 0) {
-    eatBits(p->bits);
-    return p->n;
+  code = 0; // make gcc happy
+  if (endOfBlock) {
+    code = lookBits(7);
+    p = &twoDimTab1[code];
+    if (p->bits > 0) {
+      eatBits(p->bits);
+      return p->n;
+    }
+  } else {
+    for (n = 1; n <= 7; ++n) {
+      code = lookBits(n);
+      if (n < 7) {
+       code <<= 7 - n;
+      }
+      p = &twoDimTab1[code];
+      if (p->bits == n) {
+       eatBits(n);
+       return p->n;
+      }
+    }
   }
-  error(getPos(), "Bad two dim code (%04x) in CCITTFax stream", code0);
+  error(getPos(), "Bad two dim code (%04x) in CCITTFax stream", code);
   return EOF;
 }
 
 short CCITTFaxStream::getWhiteCode() {
   short code;
   CCITTCode *p;
+  int n;
 
-  code = look13Bits();
-  if ((code >> 6) == 0)
-    p = &whiteTab1[code >> 1];
-  else
-    p = &whiteTab2[code >> 4];
-  if (p->bits > 0) {
-    eatBits(p->bits);
-    return p->n;
+  code = 0; // make gcc happy
+  if (endOfBlock) {
+    code = lookBits(12);
+    if ((code >> 5) == 0) {
+      p = &whiteTab1[code];
+    } else {
+      p = &whiteTab2[code >> 3];
+    }
+    if (p->bits > 0) {
+      eatBits(p->bits);
+      return p->n;
+    }
+  } else {
+    for (n = 1; n <= 9; ++n) {
+      code = lookBits(n);
+      if (n < 9) {
+       code <<= 9 - n;
+      }
+      p = &whiteTab2[code];
+      if (p->bits == n) {
+       eatBits(n);
+       return p->n;
+      }
+    }
+    for (n = 11; n <= 12; ++n) {
+      code = lookBits(n);
+      if (n < 12) {
+       code <<= 12 - n;
+      }
+      p = &whiteTab1[code];
+      if (p->bits == n) {
+       eatBits(n);
+       return p->n;
+      }
+    }
   }
   error(getPos(), "Bad white code (%04x) in CCITTFax stream", code);
-  return EOF;
+  // eat a bit and return a positive number so that the caller doesn't
+  // go into an infinite loop
+  eatBits(1);
+  return 1;
 }
 
 short CCITTFaxStream::getBlackCode() {
   short code;
   CCITTCode *p;
+  int n;
 
-  code = look13Bits();
-  if ((code >> 7) == 0)
-    p = &blackTab1[code];
-  else if ((code >> 9) == 0)
-    p = &blackTab2[(code >> 1) - 64];
-  else
-    p = &blackTab3[code >> 7];
-  if (p->bits > 0) {
-    eatBits(p->bits);
-    return p->n;
+  code = 0; // make gcc happy
+  if (endOfBlock) {
+    code = lookBits(13);
+    if ((code >> 7) == 0) {
+      p = &blackTab1[code];
+    } else if ((code >> 9) == 0) {
+      p = &blackTab2[(code >> 1) - 64];
+    } else {
+      p = &blackTab3[code >> 7];
+    }
+    if (p->bits > 0) {
+      eatBits(p->bits);
+      return p->n;
+    }
+  } else {
+    for (n = 2; n <= 6; ++n) {
+      code = lookBits(n);
+      if (n < 6) {
+       code <<= 6 - n;
+      }
+      p = &blackTab3[code];
+      if (p->bits == n) {
+       eatBits(n);
+       return p->n;
+      }
+    }
+    for (n = 7; n <= 12; ++n) {
+      code = lookBits(n);
+      if (n < 12) {
+       code <<= 12 - n;
+      }
+      if (code >= 64) {
+       p = &blackTab2[code - 64];
+       if (p->bits == n) {
+         eatBits(n);
+         return p->n;
+       }
+      }
+    }
+    for (n = 10; n <= 13; ++n) {
+      code = lookBits(n);
+      if (n < 13) {
+       code <<= 13 - n;
+      }
+      p = &blackTab1[code];
+      if (p->bits == n) {
+       eatBits(n);
+       return p->n;
+      }
+    }
   }
   error(getPos(), "Bad black code (%04x) in CCITTFax stream", code);
-  return EOF;
+  // eat a bit and return a positive number so that the caller doesn't
+  // go into an infinite loop
+  eatBits(1);
+  return 1;
 }
 
-short CCITTFaxStream::look13Bits() {
+short CCITTFaxStream::lookBits(int n) {
   int c;
 
-  while (inputBits < 13) {
+  while (inputBits < n) {
     if ((c = str->getChar()) == EOF) {
-      if (inputBits == 0)
+      if (inputBits == 0) {
        return EOF;
-      c = 0;
+      }
+      // near the end of the stream, the caller may ask for more bits
+      // than are available, but there may still be a valid code in
+      // however many bits are available -- we need to return correct
+      // data in this case
+      return (inputBuf << (n - inputBits)) & (0xffff >> (16 - n));
     }
     inputBuf = (inputBuf << 8) + c;
     inputBits += 8;
   }
-  return (inputBuf >> (inputBits - 13)) & 0x1fff;
+  return (inputBuf >> (inputBits - n)) & (0xffff >> (16 - n));
 }
 
-GString *CCITTFaxStream::getPSFilter(char *indent) {
+GString *CCITTFaxStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
   char s1[50];
 
-  s = str->getPSFilter(indent);
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
+    return NULL;
+  }
   s->append(indent)->append("<< ");
   if (encoding != 0) {
     sprintf(s1, "/K %d ", encoding);
     s->append(s1);
   }
-  if (byteAlign)
+  if (endOfLine) {
+    s->append("/EndOfLine true ");
+  }
+  if (byteAlign) {
     s->append("/EncodedByteAlign true ");
+  }
   sprintf(s1, "/Columns %d ", columns);
   s->append(s1);
   if (rows != 0) {
     sprintf(s1, "/Rows %d ", rows);
     s->append(s1);
   }
-  if (black)
+  if (!endOfBlock) {
+    s->append("/EndOfBlock false ");
+  }
+  if (black) {
     s->append("/BlackIs1 true ");
+  }
   s->append(">> /CCITTFaxDecode filter\n");
   return s;
 }
@@ -1338,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)
@@ -1347,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
@@ -1391,18 +1830,22 @@ static int dctZigZag[64] = {
   63
 };
 
-DCTStream::DCTStream(Stream *str1) {
+DCTStream::DCTStream(Stream *strA):
+    FilterStream(strA) {
   int i, j;
 
-  str = str1;
+  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)
@@ -1419,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();
+
+  // 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;
+
+  // 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;
+    y = 0;
+    dy = mcuHeight;
+
+    restartMarker = 0xd0;
+    restart();
+  }
 }
 
 int DCTStream::getChar() {
   int c;
 
-  c = lookChar();
-  if (c == EOF)
+  if (y >= height) {
     return EOF;
-  if (++comp == numComps) {
-    comp = 0;
-    if (++x == width) {
+  }
+  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;
-      ++y;
-      ++dy;
+      dy = 0;
+    }
+    c = rowBuf[comp][dy][x];
+    if (++comp == numComps) {
+      comp = 0;
+      if (++x == width) {
+       x = 0;
+       ++y;
+       ++dy;
+       if (y == height) {
+         readTrailer();
+       }
+      }
     }
   }
-  if (y == height)
-    readTrailer();
   return c;
 }
 
 int DCTStream::lookChar() {
-  if (y >= height)
+  if (y >= height) {
     return EOF;
-  if (dy >= mcuHeight) {
-    if (!readMCURow()) {
-      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;
     }
-    comp = 0;
-    x = 0;
-    dy = 0;
+    return rowBuf[comp][dy][x];
   }
-  return rowBuf[comp][dy][x];
 }
 
 void DCTStream::restart() {
@@ -1473,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;
@@ -1509,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;
@@ -1546,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;
              }
            }
@@ -1567,7 +2141,7 @@ GBool DCTStream::readMCURow() {
            pCr = rowBuf[2][y2][x1+x2] - 128;
            pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
            rowBuf[0][y2][x1+x2] = dctClip[dctClipOffset + pR];
-           pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32678) >> 16;
+           pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
            rowBuf[1][y2][x1+x2] = dctClip[dctClipOffset + pG];
            pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
            rowBuf[2][y2][x1+x2] = dctClip[dctClipOffset + pB];
@@ -1582,7 +2156,7 @@ GBool DCTStream::readMCURow() {
            pCr = rowBuf[2][y2][x1+x2] - 128;
            pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
            rowBuf[0][y2][x1+x2] = 255 - dctClip[dctClipOffset + pR];
-           pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32678) >> 16;
+           pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
            rowBuf[1][y2][x1+x2] = 255 - dctClip[dctClipOffset + pG];
            pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
            rowBuf[2][y2][x1+x2] = 255 - dctClip[dctClipOffset + pB];
@@ -1594,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;
@@ -1689,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;
@@ -1719,203 +2707,43 @@ GBool DCTStream::readDataUnit(DCTHuffTable *dcHuffTable,
     t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;
     v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;
     v3 = t;
-    t = (v4 - v6 + 1) >> 1;
-    v4 = (v4 + v6 + 1) >> 1;
-    v6 = t;
-    t = (v7 + v5 + 1) >> 1;
-    v5 = (v7 - v5 + 1) >> 1;
-    v7 = t;
-
-    // stage 2
-    t = (v0 - v3 + 1) >> 1;
-    v0 = (v0 + v3 + 1) >> 1;
-    v3 = t;
-    t = (v1 - v2 + 1) >> 1;
-    v1 = (v1 + v2 + 1) >> 1;
-    v2 = t;
-    t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
-    v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
-    v7 = t;
-    t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
-    v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
-    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 + 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;
-  }
-  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);
+    t = (v4 - v6 + 1) >> 1;
+    v4 = (v4 + v6 + 1) >> 1;
     v6 = t;
-    t = 0.5 * (v7 + v5);
-    v5 = 0.5 * (v7 - v5);
+    t = (v7 + v5 + 1) >> 1;
+    v5 = (v7 - v5 + 1) >> 1;
     v7 = t;
 
     // stage 2
-    t = 0.5 * (v0 - v3);
-    v0 = 0.5 * (v0 + v3);
+    t = (v0 - v3 + 1) >> 1;
+    v0 = (v0 + v3 + 1) >> 1;
     v3 = t;
-    t = 0.5 * (v1 - v2);
-    v1 = 0.5 * (v1 + v2);
+    t = (v1 - v2 + 1) >> 1;
+    v1 = (v1 + v2 + 1) >> 1;
     v2 = t;
-    t = v4 * dctSin3 + v7 * dctCos3;
-    v4 = v4 * dctCos3 - v7 * dctSin3;
+    t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;
+    v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;
     v7 = t;
-    t = v5 * dctSin1 + v6 * dctCos1;
-    v5 = v5 * dctCos1 - v6 * dctSin1;
+    t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;
+    v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;
     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 + (int)(tmp1[i] + 128.5)];
-
-  return gTrue;
+  for (i = 0; i < 64; ++i) {
+    dataOut[i] = dctClip[dctClipOffset + 128 + ((dataIn[i] + 8) >> 4)];
+  }
 }
-#endif
 
 int DCTStream::readHuffSym(DCTHuffTable *table) {
   Gushort code;
@@ -1983,19 +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;
-  restartInterval = 0;
+  int i;
 
   // read headers
   doScan = gFalse;
@@ -2003,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");
@@ -2036,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;
@@ -2046,100 +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;
+  return gTrue;
+}
+
+GBool DCTStream::readBaselineSOF() {
+  int length;
+  int prec;
+  int i;
+  int c;
+
+  length = read16();
+  prec = str->getChar();
+  height = read16();
+  width = read16();
+  numComps = str->getChar();
+  if (prec != 8) {
+    error(getPos(), "Bad DCT precision %d", prec);
+    return gFalse;
   }
   for (i = 0; i < numComps; ++i) {
-    compInfo[i].hSample /= minHSample;
-    compInfo[i].vSample /= minVSample;
+    compInfo[i].id = str->getChar();
+    c = str->getChar();
+    compInfo[i].hSample = (c >> 4) & 0x0f;
+    compInfo[i].vSample = c & 0x0f;
+    compInfo[i].quantTable = str->getChar();
   }
-  mcuWidth = (mcuWidth / minHSample) * 8;
-  mcuHeight = (mcuHeight / minVSample) * 8;
-
-  // 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));
-
-  // initialize counters
-  comp = 0;
-  x = 0;
-  y = 0;
-  dy = mcuHeight;
-
+  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;
 }
 
@@ -2221,22 +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:
@@ -2279,10 +3160,15 @@ int DCTStream::read16() {
   return (c1 << 8) + c2;
 }
 
-GString *DCTStream::getPSFilter(char *indent) {
+GString *DCTStream::getPSFilter(int psLevel, char *indent) {
   GString *s;
 
-  s = str->getPSFilter(indent);
+  if (psLevel < 2) {
+    return NULL;
+  }
+  if (!(s = str->getPSFilter(psLevel, indent))) {
+    return NULL;
+  }
   s->append(indent)->append("<< >> /DCTDecode filter\n");
   return s;
 }
@@ -2364,24 +3250,38 @@ FlateDecode FlateStream::distDecode[flateMaxDistCodes] = {
   {13, 24577}
 };
 
-FlateStream::FlateStream(Stream *str1, int predictor1, int columns1,
-                        int colors1, int bits1) {
-  str = str1;
-  predictor = predictor1;
-  if (predictor1 > 1) {
-    width = columns1;
-    nComps = colors1;
-    nBits = bits1;
+FlateStream::FlateStream(Stream *strA, int predictor, int columns,
+                        int colors, int bits):
+    FilterStream(strA) {
+  if (predictor != 1) {
+    pred = new StreamPredictor(this, predictor, columns, colors, bits);
+  } else {
+    pred = NULL;
   }
+  litCodeTab.codes = NULL;
+  distCodeTab.codes = NULL;
 }
 
 FlateStream::~FlateStream() {
+  gfree(litCodeTab.codes);
+  gfree(distCodeTab.codes);
+  if (pred) {
+    delete pred;
+  }
   delete str;
 }
 
 void FlateStream::reset() {
   int cmf, flg;
 
+  index = 0;
+  remain = 0;
+  codeBuf = 0;
+  codeSize = 0;
+  compressedBlock = gFalse;
+  endOfBlock = gTrue;
+  eof = gTrue;
+
   str->reset();
 
   // read header
@@ -2404,19 +3304,15 @@ void FlateStream::reset() {
     return;
   }
 
-  // initialize
-  index = 0;
-  remain = 0;
-  codeBuf = 0;
-  codeSize = 0;
-  compressedBlock = gFalse;
-  endOfBlock = gTrue;
   eof = gFalse;
 }
 
 int FlateStream::getChar() {
   int c;
 
+  if (pred) {
+    return pred->getChar();
+  }
   while (remain == 0) {
     if (endOfBlock && eof)
       return EOF;
@@ -2431,6 +3327,9 @@ int FlateStream::getChar() {
 int FlateStream::lookChar() {
   int c;
 
+  if (pred) {
+    return pred->lookChar();
+  }
   while (remain == 0) {
     if (endOfBlock && eof)
       return EOF;
@@ -2440,8 +3339,31 @@ int FlateStream::lookChar() {
   return c;
 }
 
-GString *FlateStream::getPSFilter(char *indent) {
-  return NULL;
+int FlateStream::getRawChar() {
+  int c;
+
+  while (remain == 0) {
+    if (endOfBlock && eof)
+      return EOF;
+    readSome();
+  }
+  c = buf[index];
+  index = (index + 1) & flateMask;
+  --remain;
+  return c;
+}
+
+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) {
@@ -2518,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)
@@ -2552,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 {
@@ -2572,185 +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;
-  distCodeTab.start[5] = 0;
-  for (i = 6; i <= flateMaxHuffman+1; ++i)
-    distCodeTab.start[6] = flateMaxDistCodes;
+  // 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 = 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);
-
-  // set up code arrays
-  litCodeTab.codes = allCodes;
-  distCodeTab.codes = allCodes + numLitCodes;
+  compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab);
 
-  // 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) {
@@ -2772,8 +3712,8 @@ int FlateStream::getCodeWord(int bits) {
 // EOFStream
 //------------------------------------------------------------------------
 
-EOFStream::EOFStream(Stream *str1) {
-  str = str1;
+EOFStream::EOFStream(Stream *strA):
+    FilterStream(strA) {
 }
 
 EOFStream::~EOFStream() {
@@ -2784,9 +3724,9 @@ EOFStream::~EOFStream() {
 // FixedLengthEncoder
 //------------------------------------------------------------------------
 
-FixedLengthEncoder::FixedLengthEncoder(Stream *str1, int length1) {
-  str = str1;
-  length = length1;
+FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA):
+    FilterStream(strA) {
+  length = lengthA;
   count = 0;
 }
 
@@ -2813,12 +3753,63 @@ int FixedLengthEncoder::lookChar() {
   return str->getChar();
 }
 
+GBool FixedLengthEncoder::isBinary(GBool last) {
+  return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// ASCIIHexEncoder
+//------------------------------------------------------------------------
+
+ASCIIHexEncoder::ASCIIHexEncoder(Stream *strA):
+    FilterStream(strA) {
+  bufPtr = bufEnd = buf;
+  lineLen = 0;
+  eof = gFalse;
+}
+
+ASCIIHexEncoder::~ASCIIHexEncoder() {
+  if (str->isEncoder()) {
+    delete str;
+  }
+}
+
+void ASCIIHexEncoder::reset() {
+  str->reset();
+  bufPtr = bufEnd = buf;
+  lineLen = 0;
+  eof = gFalse;
+}
+
+GBool ASCIIHexEncoder::fillBuf() {
+  static char *hex = "0123456789abcdef";
+  int c;
+
+  if (eof) {
+    return gFalse;
+  }
+  bufPtr = bufEnd = buf;
+  if ((c = str->getChar()) == EOF) {
+    *bufEnd++ = '>';
+    eof = gTrue;
+  } else {
+    if (lineLen >= 64) {
+      *bufEnd++ = '\n';
+      lineLen = 0;
+    }
+    *bufEnd++ = hex[(c >> 4) & 0x0f];
+    *bufEnd++ = hex[c & 0x0f];
+    lineLen += 2;
+  }
+  return gTrue;
+}
+
 //------------------------------------------------------------------------
 // ASCII85Encoder
 //------------------------------------------------------------------------
 
-ASCII85Encoder::ASCII85Encoder(Stream *str1) {
-  str = str1;
+ASCII85Encoder::ASCII85Encoder(Stream *strA):
+    FilterStream(strA) {
   bufPtr = bufEnd = buf;
   lineLen = 0;
   eof = gFalse;
@@ -2886,8 +3877,8 @@ GBool ASCII85Encoder::fillBuf() {
 // RunLengthEncoder
 //------------------------------------------------------------------------
 
-RunLengthEncoder::RunLengthEncoder(Stream *str1) {
-  str = str1;
+RunLengthEncoder::RunLengthEncoder(Stream *strA):
+    FilterStream(strA) {
   bufPtr = bufEnd = nextEnd = buf;
   eof = gFalse;
 }
@@ -2942,6 +3933,7 @@ GBool RunLengthEncoder::fillBuf() {
   }
 
   // check for repeat
+  c = 0; // make gcc happy
   if (c1 == c2) {
     n = 2;
     while (n < 128 && (c = str->getChar()) == c1)