aboutsummaryrefslogtreecommitdiff
path: root/xpdf/Stream.cc
diff options
context:
space:
mode:
Diffstat (limited to 'xpdf/Stream.cc')
-rw-r--r--xpdf/Stream.cc6064
1 files changed, 6064 insertions, 0 deletions
diff --git a/xpdf/Stream.cc b/xpdf/Stream.cc
new file mode 100644
index 0000000..383ac54
--- /dev/null
+++ b/xpdf/Stream.cc
@@ -0,0 +1,6064 @@
+//========================================================================
+//
+// Stream.cc
+//
+// Copyright 1996-2003 Glyph & Cog, LLC
+//
+//========================================================================
+
+#include <aconf.h>
+
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <limits.h>
+#ifdef _WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include "gmem.h"
+#include "gmempp.h"
+#include "gfile.h"
+#if MULTITHREADED
+#include "GMutex.h"
+#endif
+#include "config.h"
+#include "Error.h"
+#include "Object.h"
+#include "Lexer.h"
+#include "GfxState.h"
+#include "Stream.h"
+#include "JBIG2Stream.h"
+#include "JPXStream.h"
+#include "Stream-CCITT.h"
+
+#ifdef __DJGPP__
+static GBool setDJSYSFLAGS = gFalse;
+#endif
+
+#ifdef VMS
+#ifdef __GNUC__
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#endif
+#endif
+
+//------------------------------------------------------------------------
+
+// An LZW/Flate decompression bomb is detected if the output size
+// exceeds decompressionBombSizeThreshold and the decompression ratio
+// exceeds decompressionBombRatioThreshold.
+#define decompressionBombSizeThreshold 50000000
+#define decompressionBombRatioThreshold 200
+
+//------------------------------------------------------------------------
+// Stream (base class)
+//------------------------------------------------------------------------
+
+Stream::Stream() {
+}
+
+Stream::~Stream() {
+}
+
+void Stream::close() {
+}
+
+int Stream::getRawChar() {
+ error(errInternal, -1, "Called getRawChar() on non-predictor stream");
+ return EOF;
+}
+
+int Stream::getBlock(char *buf, int size) {
+ int n, c;
+
+ n = 0;
+ while (n < size) {
+ if ((c = getChar()) == EOF) {
+ break;
+ }
+ buf[n++] = (char)c;
+ }
+ return n;
+}
+
+char *Stream::getLine(char *buf, int size) {
+ int i;
+ int c;
+
+ if (lookChar() == EOF || size < 0)
+ return NULL;
+ for (i = 0; i < size - 1; ++i) {
+ c = getChar();
+ if (c == EOF || c == '\n')
+ break;
+ if (c == '\r') {
+ if ((c = lookChar()) == '\n')
+ getChar();
+ break;
+ }
+ buf[i] = (char)c;
+ }
+ buf[i] = '\0';
+ return buf;
+}
+
+Guint Stream::discardChars(Guint n) {
+ char buf[4096];
+ Guint count, i, j;
+
+ count = 0;
+ while (count < n) {
+ if ((i = n - count) > sizeof(buf)) {
+ i = (Guint)sizeof(buf);
+ }
+ j = (Guint)getBlock(buf, (int)i);
+ count += j;
+ if (j != i) {
+ break;
+ }
+ }
+ return count;
+}
+
+GString *Stream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ return new GString();
+}
+
+Stream *Stream::addFilters(Object *dict, int recursion) {
+ Object obj, obj2;
+ Object params, params2;
+ Stream *str;
+ int i;
+
+ str = this;
+ dict->dictLookup("Filter", &obj, recursion);
+ if (obj.isNull()) {
+ obj.free();
+ dict->dictLookup("F", &obj, recursion);
+ }
+ dict->dictLookup("DecodeParms", &params, recursion);
+ if (params.isNull()) {
+ params.free();
+ dict->dictLookup("DP", &params, recursion);
+ }
+ if (obj.isName()) {
+ str = makeFilter(obj.getName(), str, &params, recursion);
+ } else if (obj.isArray()) {
+ for (i = 0; i < obj.arrayGetLength(); ++i) {
+ obj.arrayGet(i, &obj2, recursion);
+ if (params.isArray() && i < params.arrayGetLength())
+ params.arrayGet(i, &params2, recursion);
+ else
+ params2.initNull();
+ if (obj2.isName()) {
+ str = makeFilter(obj2.getName(), str, &params2, recursion);
+ } else {
+ error(errSyntaxError, getPos(), "Bad filter name");
+ str = new EOFStream(str);
+ }
+ obj2.free();
+ params2.free();
+ }
+ } else if (!obj.isNull()) {
+ error(errSyntaxError, getPos(), "Bad 'Filter' attribute in stream");
+ }
+ obj.free();
+ params.free();
+
+ return str;
+}
+
+Stream *Stream::makeFilter(char *name, Stream *str, Object *params,
+ int recursion) {
+ int pred; // parameters
+ int colors;
+ int bits;
+ int early;
+ int encoding;
+ GBool endOfLine, byteAlign, endOfBlock, black;
+ int columns, rows;
+ int colorXform;
+ Object globals, obj;
+
+ if (!strcmp(name, "ASCIIHexDecode") || !strcmp(name, "AHx")) {
+ str = new ASCIIHexStream(str);
+ } else if (!strcmp(name, "ASCII85Decode") || !strcmp(name, "A85")) {
+ str = new ASCII85Stream(str);
+ } else if (!strcmp(name, "LZWDecode") || !strcmp(name, "LZW")) {
+ pred = 1;
+ columns = 1;
+ colors = 1;
+ bits = 8;
+ early = 1;
+ if (params->isDict()) {
+ params->dictLookup("Predictor", &obj, recursion);
+ if (obj.isInt())
+ pred = obj.getInt();
+ obj.free();
+ params->dictLookup("Columns", &obj, recursion);
+ if (obj.isInt())
+ columns = obj.getInt();
+ obj.free();
+ params->dictLookup("Colors", &obj, recursion);
+ if (obj.isInt())
+ colors = obj.getInt();
+ obj.free();
+ params->dictLookup("BitsPerComponent", &obj, recursion);
+ if (obj.isInt())
+ bits = obj.getInt();
+ obj.free();
+ params->dictLookup("EarlyChange", &obj, recursion);
+ if (obj.isInt())
+ early = obj.getInt();
+ obj.free();
+ }
+ str = new LZWStream(str, pred, columns, colors, bits, early);
+ } else if (!strcmp(name, "RunLengthDecode") || !strcmp(name, "RL")) {
+ 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, recursion);
+ if (obj.isInt()) {
+ encoding = obj.getInt();
+ }
+ obj.free();
+ params->dictLookup("EndOfLine", &obj, recursion);
+ if (obj.isBool()) {
+ endOfLine = obj.getBool();
+ }
+ obj.free();
+ params->dictLookup("EncodedByteAlign", &obj, recursion);
+ if (obj.isBool()) {
+ byteAlign = obj.getBool();
+ }
+ obj.free();
+ params->dictLookup("Columns", &obj, recursion);
+ if (obj.isInt()) {
+ columns = obj.getInt();
+ }
+ obj.free();
+ params->dictLookup("Rows", &obj, recursion);
+ if (obj.isInt()) {
+ rows = obj.getInt();
+ }
+ obj.free();
+ params->dictLookup("EndOfBlock", &obj, recursion);
+ if (obj.isBool()) {
+ endOfBlock = obj.getBool();
+ }
+ obj.free();
+ params->dictLookup("BlackIs1", &obj, recursion);
+ if (obj.isBool()) {
+ black = obj.getBool();
+ }
+ obj.free();
+ }
+ str = new CCITTFaxStream(str, encoding, endOfLine, byteAlign,
+ columns, rows, endOfBlock, black);
+ } else if (!strcmp(name, "DCTDecode") || !strcmp(name, "DCT")) {
+ colorXform = -1;
+ if (params->isDict()) {
+ if (params->dictLookup("ColorTransform", &obj, recursion)->isInt()) {
+ colorXform = obj.getInt();
+ }
+ obj.free();
+ }
+ str = new DCTStream(str, colorXform);
+ } else if (!strcmp(name, "FlateDecode") || !strcmp(name, "Fl")) {
+ pred = 1;
+ columns = 1;
+ colors = 1;
+ bits = 8;
+ if (params->isDict()) {
+ params->dictLookup("Predictor", &obj, recursion);
+ if (obj.isInt())
+ pred = obj.getInt();
+ obj.free();
+ params->dictLookup("Columns", &obj, recursion);
+ if (obj.isInt())
+ columns = obj.getInt();
+ obj.free();
+ params->dictLookup("Colors", &obj, recursion);
+ if (obj.isInt())
+ colors = obj.getInt();
+ obj.free();
+ params->dictLookup("BitsPerComponent", &obj, recursion);
+ if (obj.isInt())
+ bits = obj.getInt();
+ obj.free();
+ }
+ str = new FlateStream(str, pred, columns, colors, bits);
+ } else if (!strcmp(name, "JBIG2Decode")) {
+ if (params->isDict()) {
+ params->dictLookup("JBIG2Globals", &globals, recursion);
+ }
+ str = new JBIG2Stream(str, &globals);
+ globals.free();
+ } else if (!strcmp(name, "JPXDecode")) {
+ str = new JPXStream(str);
+ } else if (!strcmp(name, "Crypt")) {
+ // this is handled in Parser::makeStream()
+ } else {
+ error(errSyntaxError, getPos(), "Unknown filter '{0:s}'", name);
+ str = new EOFStream(str);
+ }
+ return str;
+}
+
+//------------------------------------------------------------------------
+// BaseStream
+//------------------------------------------------------------------------
+
+BaseStream::BaseStream(Object *dictA) {
+ dict = *dictA;
+}
+
+BaseStream::~BaseStream() {
+ dict.free();
+}
+
+//------------------------------------------------------------------------
+// FilterStream
+//------------------------------------------------------------------------
+
+FilterStream::FilterStream(Stream *strA) {
+ str = strA;
+}
+
+FilterStream::~FilterStream() {
+}
+
+void FilterStream::close() {
+ str->close();
+}
+
+void FilterStream::setPos(GFileOffset pos, int dir) {
+ error(errInternal, -1, "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;
+
+ nVals = width * nComps;
+ inputLineSize = (nVals * nBits + 7) >> 3;
+ if (width > INT_MAX / nComps ||
+ nVals > (INT_MAX - 7) / nBits) {
+ // force a call to gmallocn(-1,...), which will throw an exception
+ inputLineSize = -1;
+ }
+ inputLine = (char *)gmallocn(inputLineSize, sizeof(char));
+ if (nBits == 8) {
+ imgLine = (Guchar *)inputLine;
+ } else {
+ if (nBits == 1) {
+ imgLineSize = (nVals + 7) & ~7;
+ } else {
+ imgLineSize = nVals;
+ }
+ imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar));
+ }
+ imgIdx = nVals;
+}
+
+ImageStream::~ImageStream() {
+ if (imgLine != (Guchar *)inputLine) {
+ gfree(imgLine);
+ }
+ gfree(inputLine);
+}
+
+void ImageStream::reset() {
+ str->disableDecompressionBombChecking();
+ str->reset();
+}
+
+void ImageStream::close() {
+ str->close();
+}
+
+GBool ImageStream::getPixel(Guchar *pix) {
+ int i;
+
+ if (imgIdx >= nVals) {
+ if (!getLine()) {
+ return gFalse;
+ }
+ imgIdx = 0;
+ }
+ for (i = 0; i < nComps; ++i) {
+ pix[i] = imgLine[imgIdx++];
+ }
+ return gTrue;
+}
+
+Guchar *ImageStream::getLine() {
+ Gulong buf, bitMask;
+ int bits;
+ int c;
+ int i;
+ char *p;
+
+ if (str->getBlock(inputLine, inputLineSize) != inputLineSize) {
+ return NULL;
+ }
+ if (nBits == 1) {
+ p = inputLine;
+ for (i = 0; i < nVals; i += 8) {
+ c = *p++;
+ 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) {
+ // special case: imgLine == inputLine
+ } else if (nBits == 16) {
+ for (i = 0; i < nVals; ++i) {
+ imgLine[i] = (Guchar)inputLine[2*i];
+ }
+ } else {
+ bitMask = (1 << nBits) - 1;
+ buf = 0;
+ bits = 0;
+ p = inputLine;
+ for (i = 0; i < nVals; ++i) {
+ if (bits < nBits) {
+ buf = (buf << 8) | (*p++ & 0xff);
+ bits += 8;
+ }
+ imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);
+ bits -= nBits;
+ }
+ }
+ return imgLine;
+}
+
+void ImageStream::skipLine() {
+ str->getBlock(inputLine, inputLineSize);
+}
+
+
+//------------------------------------------------------------------------
+// StreamPredictor
+//------------------------------------------------------------------------
+
+StreamPredictor::StreamPredictor(Stream *strA, int predictorA,
+ int widthA, int nCompsA, int nBitsA) {
+ str = strA;
+ predictor = predictorA;
+ width = widthA;
+ nComps = nCompsA;
+ nBits = nBitsA;
+ predLine = NULL;
+ ok = gFalse;
+
+ nVals = width * nComps;
+ pixBytes = (nComps * nBits + 7) >> 3;
+ rowBytes = ((nVals * nBits + 7) >> 3) + pixBytes;
+ if (width <= 0 || nComps <= 0 || nBits <= 0 ||
+ nComps > gfxColorMaxComps ||
+ nBits > 16 ||
+ width >= INT_MAX / nComps || // check for overflow in nVals
+ nVals >= (INT_MAX - 7) / nBits) { // check for overflow in rowBytes
+ return;
+ }
+ predLine = (Guchar *)gmalloc(rowBytes);
+
+ reset();
+
+ ok = gTrue;
+}
+
+StreamPredictor::~StreamPredictor() {
+ gfree(predLine);
+}
+
+void StreamPredictor::reset() {
+ memset(predLine, 0, rowBytes);
+ predIdx = rowBytes;
+}
+
+int StreamPredictor::lookChar() {
+ if (predIdx >= rowBytes) {
+ if (!getNextLine()) {
+ return EOF;
+ }
+ }
+ return predLine[predIdx];
+}
+
+int StreamPredictor::getChar() {
+ if (predIdx >= rowBytes) {
+ if (!getNextLine()) {
+ return EOF;
+ }
+ }
+ return predLine[predIdx++];
+}
+
+int StreamPredictor::getBlock(char *blk, int size) {
+ int n, m;
+
+ n = 0;
+ while (n < size) {
+ if (predIdx >= rowBytes) {
+ if (!getNextLine()) {
+ break;
+ }
+ }
+ m = rowBytes - predIdx;
+ if (m > size - n) {
+ m = size - n;
+ }
+ memcpy(blk + n, predLine + predIdx, m);
+ predIdx += m;
+ n += m;
+ }
+ return n;
+}
+
+GBool StreamPredictor::getNextLine() {
+ int curPred;
+ Guchar upLeftBuf[gfxColorMaxComps * 2 + 1];
+ int left, up, upLeft, p, pa, pb, pc;
+ int c;
+ Gulong inBuf, outBuf, bitMask;
+ int inBits, outBits;
+ int i, j, k, kk;
+
+ // get PNG optimum predictor number
+ if (predictor >= 10) {
+ if ((curPred = str->getRawChar()) == EOF) {
+ return gFalse;
+ }
+ curPred += 10;
+ } else {
+ curPred = predictor;
+ }
+
+ // read the raw line, apply PNG (byte) predictor
+ memset(upLeftBuf, 0, pixBytes + 1);
+ for (i = pixBytes; i < rowBytes; ++i) {
+ for (j = pixBytes; j > 0; --j) {
+ upLeftBuf[j] = upLeftBuf[j-1];
+ }
+ upLeftBuf[0] = predLine[i];
+ if ((c = str->getRawChar()) == EOF) {
+ if (i > pixBytes) {
+ // this ought to return false, but some (broken) PDF files
+ // contain truncated image data, and Adobe apparently reads the
+ // last partial line
+ break;
+ }
+ return gFalse;
+ }
+ switch (curPred) {
+ case 11: // PNG sub
+ predLine[i] = (Guchar)(predLine[i - pixBytes] + c);
+ break;
+ case 12: // PNG up
+ predLine[i] = (Guchar)(predLine[i] + c);
+ break;
+ case 13: // PNG average
+ predLine[i] = (Guchar)(((predLine[i - pixBytes] + predLine[i]) >> 1) + 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] = (Guchar)(left + c);
+ else if (pb <= pc)
+ predLine[i] = (Guchar)(up + c);
+ else
+ predLine[i] = (Guchar)(upLeft + c);
+ break;
+ case 10: // PNG none
+ default: // no predictor or TIFF predictor
+ predLine[i] = (Guchar)c;
+ break;
+ }
+ }
+
+ // apply TIFF (component) predictor
+ if (predictor == 2) {
+ if (nBits == 8) {
+ for (i = pixBytes; i < rowBytes; ++i) {
+ predLine[i] = (Guchar)(predLine[i] + predLine[i - nComps]);
+ }
+ } else if (nBits == 16) {
+ for (i = pixBytes; i < rowBytes; i += 2) {
+ c = ((predLine[i] + predLine[i - 2*nComps]) << 8) +
+ predLine[i + 1] + predLine[i + 1 - 2*nComps];
+ predLine[i] = (Guchar)(c >> 8);
+ predLine[i+1] = (Guchar)(c & 0xff);
+ }
+ } else {
+ memset(upLeftBuf, 0, nComps);
+ bitMask = (1 << nBits) - 1;
+ inBuf = outBuf = 0;
+ inBits = outBits = 0;
+ j = k = pixBytes;
+ for (i = 0; i < width; ++i) {
+ for (kk = 0; kk < nComps; ++kk) {
+ if (inBits < nBits) {
+ inBuf = (inBuf << 8) | (predLine[j++] & 0xff);
+ inBits += 8;
+ }
+ upLeftBuf[kk] = (Guchar)((upLeftBuf[kk] +
+ (inBuf >> (inBits - nBits))) & bitMask);
+ inBits -= nBits;
+ outBuf = (outBuf << nBits) | upLeftBuf[kk];
+ outBits += nBits;
+ if (outBits >= 8) {
+ predLine[k++] = (Guchar)(outBuf >> (outBits - 8));
+ outBits -= 8;
+ }
+ }
+ }
+ if (outBits > 0) {
+ predLine[k++] = (Guchar)((outBuf << (8 - outBits)) +
+ (inBuf & ((1 << (8 - outBits)) - 1)));
+ }
+ }
+ }
+
+ // reset to start of line
+ predIdx = pixBytes;
+
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// SharedFile
+//------------------------------------------------------------------------
+
+class SharedFile {
+public:
+
+ SharedFile(FILE *fA);
+ SharedFile *copy();
+ void free();
+ int readBlock(char *buf, GFileOffset pos, int size);
+ GFileOffset getSize();
+
+private:
+
+ ~SharedFile();
+
+ FILE *f;
+ int refCnt;
+#if MULTITHREADED
+ GMutex mutex;
+#endif
+};
+
+SharedFile::SharedFile(FILE *fA) {
+ f = fA;
+ refCnt = 1;
+#if MULTITHREADED
+ gInitMutex(&mutex);
+#endif
+}
+
+SharedFile::~SharedFile() {
+#if MULTITHREADED
+ gDestroyMutex(&mutex);
+#endif
+}
+
+SharedFile *SharedFile::copy() {
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ ++refCnt;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ return this;
+}
+
+void SharedFile::free() {
+ int newCount;
+
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ newCount = --refCnt;
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ if (newCount == 0) {
+ delete this;
+ }
+}
+
+int SharedFile::readBlock(char *buf, GFileOffset pos, int size) {
+ int n;
+
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ gfseek(f, pos, SEEK_SET);
+ n = (int)fread(buf, 1, size, f);
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ return n;
+}
+
+GFileOffset SharedFile::getSize() {
+ GFileOffset size;
+
+#if MULTITHREADED
+ gLockMutex(&mutex);
+#endif
+ gfseek(f, 0, SEEK_END);
+ size = gftell(f);
+#if MULTITHREADED
+ gUnlockMutex(&mutex);
+#endif
+ return size;
+}
+
+//------------------------------------------------------------------------
+// FileStream
+//------------------------------------------------------------------------
+
+FileStream::FileStream(FILE *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA):
+ BaseStream(dictA) {
+ f = new SharedFile(fA);
+ start = startA;
+ limited = limitedA;
+ length = lengthA;
+ bufPtr = bufEnd = buf;
+ bufPos = start;
+}
+
+FileStream::FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA):
+ BaseStream(dictA) {
+ f = fA->copy();
+ start = startA;
+ limited = limitedA;
+ length = lengthA;
+ bufPtr = bufEnd = buf;
+ bufPos = start;
+}
+
+FileStream::~FileStream() {
+ f->free();
+}
+
+Stream *FileStream::copy() {
+ Object dictA;
+
+ dict.copy(&dictA);
+ return new FileStream(f, start, limited, length, &dictA);
+}
+
+Stream *FileStream::makeSubStream(GFileOffset startA, GBool limitedA,
+ GFileOffset lengthA, Object *dictA) {
+ return new FileStream(f, startA, limitedA, lengthA, dictA);
+}
+
+void FileStream::reset() {
+ bufPtr = bufEnd = buf;
+ bufPos = start;
+}
+
+int FileStream::getBlock(char *blk, int size) {
+ int n, m;
+
+ n = 0;
+ while (n < size) {
+ if (bufPtr >= bufEnd) {
+ if (!fillBuf()) {
+ break;
+ }
+ }
+ m = (int)(bufEnd - bufPtr);
+ if (m > size - n) {
+ m = size - n;
+ }
+ memcpy(blk + n, bufPtr, m);
+ bufPtr += m;
+ n += m;
+ }
+ return n;
+}
+
+GBool FileStream::fillBuf() {
+ int n;
+
+ bufPos += (int)(bufEnd - buf);
+ bufPtr = bufEnd = buf;
+ if (limited && bufPos >= start + length) {
+ return gFalse;
+ }
+ if (limited && bufPos + fileStreamBufSize > start + length) {
+ n = (int)(start + length - bufPos);
+ } else {
+ n = fileStreamBufSize;
+ }
+ n = f->readBlock(buf, bufPos, n);
+ bufEnd = buf + n;
+ if (bufPtr >= bufEnd) {
+ return gFalse;
+ }
+ return gTrue;
+}
+
+void FileStream::setPos(GFileOffset pos, int dir) {
+ GFileOffset size;
+
+ if (dir >= 0) {
+ bufPos = pos;
+ } else {
+ size = f->getSize();
+ if (pos <= size) {
+ bufPos = size - pos;
+ } else {
+ bufPos = 0;
+ }
+ }
+ 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::copy() {
+ Object dictA;
+
+ dict.copy(&dictA);
+ return new MemStream(buf, start, length, &dictA);
+}
+
+Stream *MemStream::makeSubStream(GFileOffset startA, GBool limited,
+ GFileOffset lengthA, Object *dictA) {
+ MemStream *subStr;
+ Guint newStart, newLength;
+
+ if (startA < start) {
+ newStart = start;
+ } else if (startA > start + length) {
+ newStart = start + (int)length;
+ } else {
+ newStart = (int)startA;
+ }
+ if (!limited || newStart + lengthA > start + length) {
+ newLength = start + length - newStart;
+ } else {
+ newLength = (Guint)lengthA;
+ }
+ subStr = new MemStream(buf, newStart, newLength, dictA);
+ return subStr;
+}
+
+void MemStream::reset() {
+ bufPtr = buf + start;
+}
+
+void MemStream::close() {
+}
+
+int MemStream::getBlock(char *blk, int size) {
+ int n;
+
+ if (size <= 0) {
+ return 0;
+ }
+ if (bufEnd - bufPtr < size) {
+ n = (int)(bufEnd - bufPtr);
+ } else {
+ n = size;
+ }
+ memcpy(blk, bufPtr, n);
+ bufPtr += n;
+ return n;
+}
+
+void MemStream::setPos(GFileOffset pos, int dir) {
+ Guint i;
+
+ if (dir >= 0) {
+ i = (Guint)pos;
+ } else {
+ if (pos > start + length) {
+ i = 0;
+ } else {
+ i = (Guint)(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;
+ length -= delta;
+ bufPtr = buf + start;
+}
+
+//------------------------------------------------------------------------
+// EmbedStream
+//------------------------------------------------------------------------
+
+EmbedStream::EmbedStream(Stream *strA, Object *dictA,
+ GBool limitedA, GFileOffset lengthA):
+ BaseStream(dictA) {
+ str = strA;
+ limited = limitedA;
+ length = lengthA;
+}
+
+EmbedStream::~EmbedStream() {
+}
+
+Stream *EmbedStream::copy() {
+ Object dictA;
+
+ dict.copy(&dictA);
+ return new EmbedStream(str, &dictA, limited, length);
+}
+
+Stream *EmbedStream::makeSubStream(GFileOffset start, GBool limitedA,
+ GFileOffset lengthA, Object *dictA) {
+ error(errInternal, -1, "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();
+}
+
+int EmbedStream::getBlock(char *blk, int size) {
+ if (size <= 0) {
+ return 0;
+ }
+ if (limited && length < (Guint)size) {
+ size = (int)length;
+ }
+ length -= size;
+ return str->getBlock(blk, size);
+}
+
+void EmbedStream::setPos(GFileOffset pos, int dir) {
+ error(errInternal, -1, "Called setPos() on EmbedStream");
+}
+
+GFileOffset EmbedStream::getStart() {
+ error(errInternal, -1, "Called getStart() on EmbedStream");
+ return 0;
+}
+
+void EmbedStream::moveStart(int delta) {
+ error(errInternal, -1, "Called moveStart() on EmbedStream");
+}
+
+//------------------------------------------------------------------------
+// ASCIIHexStream
+//------------------------------------------------------------------------
+
+ASCIIHexStream::ASCIIHexStream(Stream *strA):
+ FilterStream(strA) {
+ buf = EOF;
+ eof = gFalse;
+}
+
+ASCIIHexStream::~ASCIIHexStream() {
+ delete str;
+}
+
+Stream *ASCIIHexStream::copy() {
+ return new ASCIIHexStream(str->copy());
+}
+
+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(errSyntaxError, getPos(),
+ "Illegal character <{0: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(errSyntaxError, getPos(),
+ "Illegal character <{0:02x}> in ASCIIHex stream", c2);
+ }
+ buf = x & 0xff;
+ return buf;
+}
+
+GString *ASCIIHexStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ 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;
+}
+
+Stream *ASCII85Stream::copy() {
+ return new ASCII85Stream(str->copy());
+}
+
+void ASCII85Stream::reset() {
+ str->reset();
+ index = n = 0;
+ eof = gFalse;
+}
+
+int ASCII85Stream::lookChar() {
+ int k;
+ Gulong t;
+
+ if (index >= n) {
+ if (eof)
+ return EOF;
+ index = 0;
+ do {
+ c[0] = str->getChar();
+ } while (Lexer::isSpace(c[0]));
+ if (c[0] == '~' || c[0] == EOF) {
+ eof = gTrue;
+ n = 0;
+ return EOF;
+ } else if (c[0] == 'z') {
+ b[0] = b[1] = b[2] = b[3] = 0;
+ n = 4;
+ } else {
+ for (k = 1; k < 5; ++k) {
+ do {
+ c[k] = str->getChar();
+ } while (Lexer::isSpace(c[k]));
+ if (c[k] == '~' || c[k] == EOF)
+ break;
+ }
+ n = k - 1;
+ if (k < 5 && (c[k] == '~' || c[k] == EOF)) {
+ for (++k; k < 5; ++k)
+ c[k] = 0x21 + 84;
+ eof = gTrue;
+ }
+ t = 0;
+ for (k = 0; k < 5; ++k)
+ t = t * 85 + (c[k] - 0x21);
+ for (k = 3; k >= 0; --k) {
+ b[k] = (int)(t & 0xff);
+ t >>= 8;
+ }
+ }
+ }
+ return b[index];
+}
+
+GString *ASCII85Stream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("/ASCII85Decode filter\n");
+ return s;
+}
+
+GBool ASCII85Stream::isBinary(GBool last) {
+ return str->isBinary(gFalse);
+}
+
+//------------------------------------------------------------------------
+// LZWStream
+//------------------------------------------------------------------------
+
+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);
+ if (!pred->isOk()) {
+ delete pred;
+ pred = NULL;
+ }
+ } else {
+ pred = NULL;
+ }
+ early = earlyA;
+ eof = gFalse;
+ inputBits = 0;
+ clearTable();
+ checkForDecompressionBombs = gTrue;
+}
+
+LZWStream::~LZWStream() {
+ if (pred) {
+ delete pred;
+ }
+ delete str;
+}
+
+Stream *LZWStream::copy() {
+ if (pred) {
+ return new LZWStream(str->copy(), pred->getPredictor(),
+ pred->getWidth(), pred->getNComps(),
+ pred->getNBits(), early);
+ } else {
+ return new LZWStream(str->copy(), 1, 0, 0, 0, early);
+ }
+}
+
+void LZWStream::disableDecompressionBombChecking() {
+ checkForDecompressionBombs = gFalse;
+ FilterStream::disableDecompressionBombChecking();
+}
+
+int LZWStream::getChar() {
+ if (pred) {
+ return pred->getChar();
+ }
+ if (eof) {
+ return EOF;
+ }
+ if (seqIndex >= seqLength) {
+ if (!processNextCode()) {
+ return EOF;
+ }
+ }
+ return seqBuf[seqIndex++];
+}
+
+int LZWStream::lookChar() {
+ if (pred) {
+ return pred->lookChar();
+ }
+ if (eof) {
+ return EOF;
+ }
+ if (seqIndex >= seqLength) {
+ if (!processNextCode()) {
+ return EOF;
+ }
+ }
+ return seqBuf[seqIndex];
+}
+
+int LZWStream::getRawChar() {
+ if (eof) {
+ return EOF;
+ }
+ if (seqIndex >= seqLength) {
+ if (!processNextCode()) {
+ return EOF;
+ }
+ }
+ return seqBuf[seqIndex++];
+}
+
+int LZWStream::getBlock(char *blk, int size) {
+ int n, m;
+
+ if (pred) {
+ return pred->getBlock(blk, size);
+ }
+ if (eof) {
+ return 0;
+ }
+ n = 0;
+ while (n < size) {
+ if (seqIndex >= seqLength) {
+ if (!processNextCode()) {
+ break;
+ }
+ }
+ m = seqLength - seqIndex;
+ if (m > size - n) {
+ m = size - n;
+ }
+ memcpy(blk + n, seqBuf + seqIndex, m);
+ seqIndex += m;
+ n += m;
+ }
+ return n;
+}
+
+void LZWStream::reset() {
+ str->reset();
+ if (pred) {
+ pred->reset();
+ }
+ eof = gFalse;
+ inputBits = 0;
+ clearTable();
+ totalIn = totalOut = 0;
+}
+
+GBool LZWStream::processNextCode() {
+ int code;
+ int nextLength;
+ int i, j;
+
+ // check for EOF
+ if (eof) {
+ return gFalse;
+ }
+
+ // 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(errSyntaxError, getPos(),
+ "Bad LZW stream - expected clear-table code");
+ clearTable();
+ }
+
+ // process the next code
+ nextLength = seqLength + 1;
+ if (code < 256) {
+ seqBuf[0] = (Guchar)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] = (Guchar)j;
+ } else if (code == nextCode) {
+ seqBuf[seqLength] = (Guchar)newChar;
+ ++seqLength;
+ } else {
+ error(errSyntaxError, 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 = (Guchar)newChar;
+ ++nextCode;
+ if (nextCode + early == 512)
+ nextBits = 10;
+ else if (nextCode + early == 1024)
+ nextBits = 11;
+ else if (nextCode + early == 2048)
+ nextBits = 12;
+ }
+ prevCode = code;
+ totalOut += seqLength;
+
+ // check for a 'decompression bomb'
+ if (checkForDecompressionBombs &&
+ totalOut > decompressionBombSizeThreshold &&
+ totalIn < totalOut / decompressionBombRatioThreshold) {
+ error(errSyntaxError, getPos(), "Decompression bomb in LZW stream");
+ eof = gTrue;
+ return gFalse;
+ }
+
+ // reset buffer
+ seqIndex = 0;
+
+ return gTrue;
+}
+
+void LZWStream::clearTable() {
+ nextCode = 258;
+ nextBits = 9;
+ seqIndex = seqLength = 0;
+ first = gTrue;
+}
+
+int LZWStream::getCode() {
+ int c;
+ int code;
+
+ while (inputBits < nextBits) {
+ if ((c = str->getChar()) == EOF)
+ return EOF;
+ inputBuf = (inputBuf << 8) | (c & 0xff);
+ inputBits += 8;
+ ++totalIn;
+ }
+ code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);
+ inputBits -= nextBits;
+ return code;
+}
+
+GString *LZWStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2 || pred) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("<< ");
+ if (!early) {
+ s->append("/EarlyChange 0 ");
+ }
+ s->append(">> /LZWDecode filter\n");
+ return s;
+}
+
+GBool LZWStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// RunLengthStream
+//------------------------------------------------------------------------
+
+RunLengthStream::RunLengthStream(Stream *strA):
+ FilterStream(strA) {
+ bufPtr = bufEnd = buf;
+ eof = gFalse;
+}
+
+RunLengthStream::~RunLengthStream() {
+ delete str;
+}
+
+Stream *RunLengthStream::copy() {
+ return new RunLengthStream(str->copy());
+}
+
+void RunLengthStream::reset() {
+ str->reset();
+ bufPtr = bufEnd = buf;
+ eof = gFalse;
+}
+
+int RunLengthStream::getBlock(char *blk, int size) {
+ int n, m;
+
+ n = 0;
+ while (n < size) {
+ if (bufPtr >= bufEnd) {
+ if (!fillBuf()) {
+ break;
+ }
+ }
+ m = (int)(bufEnd - bufPtr);
+ if (m > size - n) {
+ m = size - n;
+ }
+ memcpy(blk + n, bufPtr, m);
+ bufPtr += m;
+ n += m;
+ }
+ return n;
+}
+
+GString *RunLengthStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("/RunLengthDecode filter\n");
+ return s;
+}
+
+GBool RunLengthStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+GBool RunLengthStream::fillBuf() {
+ int c;
+ int n, i;
+
+ if (eof)
+ return gFalse;
+ c = str->getChar();
+ if (c == 0x80 || c == EOF) {
+ eof = gTrue;
+ return gFalse;
+ }
+ if (c < 0x80) {
+ n = c + 1;
+ for (i = 0; i < n; ++i)
+ buf[i] = (char)str->getChar();
+ } else {
+ n = 0x101 - c;
+ c = str->getChar();
+ for (i = 0; i < n; ++i)
+ buf[i] = (char)c;
+ }
+ bufPtr = buf;
+ bufEnd = buf + n;
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// CCITTFaxStream
+//------------------------------------------------------------------------
+
+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;
+ if (columns < 1) {
+ columns = 1;
+ } else if (columns > INT_MAX - 3) {
+ columns = INT_MAX - 3;
+ }
+ rows = rowsA;
+ endOfBlock = endOfBlockA;
+ black = blackA;
+ blackXOR = black ? 0xff : 0x00;
+ // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = columns
+ // ---> max codingLine size = columns + 1
+ // refLine has two extra guard entries at the end
+ // ---> max refLine size = columns + 3
+ codingLine = (int *)gmallocn(columns + 1, sizeof(int));
+ refLine = (int *)gmallocn(columns + 3, sizeof(int));
+
+ eof = gFalse;
+ row = 0;
+ nextLine2D = encoding < 0;
+ inputBits = 0;
+ codingLine[0] = columns;
+ nextCol = columns;
+ a0i = 0;
+ err = gFalse;
+ nErrors = 0;
+}
+
+CCITTFaxStream::~CCITTFaxStream() {
+ delete str;
+ gfree(refLine);
+ gfree(codingLine);
+}
+
+Stream *CCITTFaxStream::copy() {
+ return new CCITTFaxStream(str->copy(), encoding, endOfLine,
+ byteAlign, columns, rows, endOfBlock, black);
+}
+
+void CCITTFaxStream::reset() {
+ int code1;
+
+ str->reset();
+ eof = gFalse;
+ row = 0;
+ nextLine2D = encoding < 0;
+ inputBits = 0;
+ codingLine[0] = columns;
+ nextCol = columns;
+ a0i = 0;
+
+ // 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);
+ endOfLine = gTrue;
+ }
+ if (encoding > 0) {
+ nextLine2D = !lookBits(1);
+ eatBits(1);
+ }
+}
+
+int CCITTFaxStream::getChar() {
+ int c, bitsNeeded, bitsAvail, bitsUsed;
+
+ if (nextCol >= columns) {
+ if (eof) {
+ return EOF;
+ }
+ if (!readRow()) {
+ return EOF;
+ }
+ }
+ bitsAvail = codingLine[a0i] - nextCol;
+ if (bitsAvail > 8) {
+ c = (a0i & 1) ? 0x00 : 0xff;
+ } else {
+ c = 0;
+ bitsNeeded = 8;
+ do {
+ bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded;
+ c <<= bitsUsed;
+ if (!(a0i & 1)) {
+ c |= 0xff >> (8 - bitsUsed);
+ }
+ bitsAvail -= bitsUsed;
+ bitsNeeded -= bitsUsed;
+ if (bitsAvail == 0) {
+ if (codingLine[a0i] >= columns) {
+ c <<= bitsNeeded;
+ break;
+ }
+ ++a0i;
+ bitsAvail = codingLine[a0i] - codingLine[a0i - 1];
+ }
+ } while (bitsNeeded > 0);
+ }
+ nextCol += 8;
+ c ^= blackXOR;
+ return c;
+}
+
+int CCITTFaxStream::lookChar() {
+ int c, bitsNeeded, bitsAvail, bitsUsed, i;
+
+ if (nextCol >= columns) {
+ if (eof) {
+ return EOF;
+ }
+ if (!readRow()) {
+ return EOF;
+ }
+ }
+ bitsAvail = codingLine[a0i] - nextCol;
+ if (bitsAvail >= 8) {
+ c = (a0i & 1) ? 0x00 : 0xff;
+ } else {
+ i = a0i;
+ c = 0;
+ bitsNeeded = 8;
+ do {
+ bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded;
+ c <<= bitsUsed;
+ if (!(i & 1)) {
+ c |= 0xff >> (8 - bitsUsed);
+ }
+ bitsAvail -= bitsUsed;
+ bitsNeeded -= bitsUsed;
+ if (bitsAvail == 0) {
+ if (codingLine[i] >= columns) {
+ c <<= bitsNeeded;
+ break;
+ }
+ ++i;
+ bitsAvail = codingLine[i] - codingLine[i - 1];
+ }
+ } while (bitsNeeded > 0);
+ }
+ c ^= blackXOR;
+ return c;
+}
+
+int CCITTFaxStream::getBlock(char *blk, int size) {
+ int bytesRead, bitsAvail, bitsNeeded, bitsUsed, byte, c;
+
+ bytesRead = 0;
+ while (bytesRead < size) {
+ if (nextCol >= columns) {
+ if (eof) {
+ break;
+ }
+ if (!readRow()) {
+ break;
+ }
+ }
+ bitsAvail = codingLine[a0i] - nextCol;
+ byte = (a0i & 1) ? 0x00 : 0xff;
+ if (bitsAvail > 8) {
+ c = byte;
+ bitsAvail -= 8;
+ } else {
+ c = 0;
+ bitsNeeded = 8;
+ do {
+ bitsUsed = (bitsAvail < bitsNeeded) ? bitsAvail : bitsNeeded;
+ c <<= bitsUsed;
+ c |= byte >> (8 - bitsUsed);
+ bitsAvail -= bitsUsed;
+ bitsNeeded -= bitsUsed;
+ if (bitsAvail == 0) {
+ if (codingLine[a0i] >= columns) {
+ c <<= bitsNeeded;
+ break;
+ }
+ ++a0i;
+ bitsAvail = codingLine[a0i] - codingLine[a0i - 1];
+ byte ^= 0xff;
+ }
+ } while (bitsNeeded > 0);
+ }
+ nextCol += 8;
+ blk[bytesRead++] = (char)(c ^ blackXOR);
+ }
+ return bytesRead;
+}
+
+inline void CCITTFaxStream::addPixels(int a1, int blackPixels) {
+ if (a1 > codingLine[a0i]) {
+ if (a1 > columns) {
+ error(errSyntaxError, getPos(),
+ "CCITTFax row is wrong length ({0:d})", a1);
+ err = gTrue;
+ ++nErrors;
+ a1 = columns;
+ }
+ if ((a0i & 1) ^ blackPixels) {
+ ++a0i;
+ }
+ codingLine[a0i] = a1;
+ }
+}
+
+inline void CCITTFaxStream::addPixelsNeg(int a1, int blackPixels) {
+ if (a1 > codingLine[a0i]) {
+ if (a1 > columns) {
+ error(errSyntaxError, getPos(),
+ "CCITTFax row is wrong length ({0:d})", a1);
+ err = gTrue;
+ ++nErrors;
+ a1 = columns;
+ }
+ if ((a0i & 1) ^ blackPixels) {
+ ++a0i;
+ }
+ codingLine[a0i] = a1;
+ } else if (a1 < codingLine[a0i]) {
+ if (a1 < 0) {
+ error(errSyntaxError, getPos(), "Invalid CCITTFax code");
+ err = gTrue;
+ ++nErrors;
+ a1 = 0;
+ }
+ while (a0i > 0 && a1 <= codingLine[a0i - 1]) {
+ --a0i;
+ }
+ codingLine[a0i] = a1;
+ }
+}
+
+GBool CCITTFaxStream::readRow() {
+ int code1, code2, code3;
+ int b1i, blackPixels, i;
+ GBool gotEOL;
+
+ // if at eof just return EOF
+ if (eof) {
+ return gFalse;
+ }
+
+ err = gFalse;
+
+ // 2-D encoding
+ if (nextLine2D) {
+ for (i = 0; codingLine[i] < columns; ++i) {
+ refLine[i] = codingLine[i];
+ }
+ refLine[i++] = columns;
+ refLine[i++] = columns;
+ refLine[i] = columns;
+ codingLine[0] = 0;
+ a0i = 0;
+ b1i = 0;
+ blackPixels = 0;
+ // invariant:
+ // refLine[b1i-1] <= codingLine[a0i] < refLine[b1i] < refLine[b1i+1]
+ // <= columns
+ // exception at left edge:
+ // codingLine[a0i = 0] = refLine[b1i = 0] = 0 is possible
+ // exception at right edge:
+ // refLine[b1i] = refLine[b1i+1] = columns is possible
+ while (codingLine[a0i] < columns) {
+ code1 = getTwoDimCode();
+ switch (code1) {
+ case twoDimPass:
+ addPixels(refLine[b1i + 1], blackPixels);
+ if (refLine[b1i + 1] < columns) {
+ b1i += 2;
+ }
+ break;
+ case twoDimHoriz:
+ code1 = code2 = 0;
+ if (blackPixels) {
+ do {
+ code1 += code3 = getBlackCode();
+ } while (code3 >= 64);
+ do {
+ code2 += code3 = getWhiteCode();
+ } while (code3 >= 64);
+ } else {
+ do {
+ code1 += code3 = getWhiteCode();
+ } while (code3 >= 64);
+ do {
+ code2 += code3 = getBlackCode();
+ } while (code3 >= 64);
+ }
+ addPixels(codingLine[a0i] + code1, blackPixels);
+ if (codingLine[a0i] < columns) {
+ addPixels(codingLine[a0i] + code2, blackPixels ^ 1);
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ break;
+ case twoDimVertR3:
+ addPixels(refLine[b1i] + 3, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertR2:
+ addPixels(refLine[b1i] + 2, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertR1:
+ addPixels(refLine[b1i] + 1, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVert0:
+ addPixels(refLine[b1i], blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ ++b1i;
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertL3:
+ addPixelsNeg(refLine[b1i] - 3, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ if (b1i > 0) {
+ --b1i;
+ } else {
+ ++b1i;
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertL2:
+ addPixelsNeg(refLine[b1i] - 2, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ if (b1i > 0) {
+ --b1i;
+ } else {
+ ++b1i;
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case twoDimVertL1:
+ addPixelsNeg(refLine[b1i] - 1, blackPixels);
+ blackPixels ^= 1;
+ if (codingLine[a0i] < columns) {
+ if (b1i > 0) {
+ --b1i;
+ } else {
+ ++b1i;
+ }
+ while (refLine[b1i] <= codingLine[a0i] && refLine[b1i] < columns) {
+ b1i += 2;
+ }
+ }
+ break;
+ case EOF:
+ addPixels(columns, 0);
+ err = gTrue;
+ break;
+ default:
+ error(errSyntaxError, getPos(),
+ "Bad 2D code {0:04x} in CCITTFax stream", code1);
+ addPixels(columns, 0);
+ err = gTrue;
+ ++nErrors;
+ break;
+ }
+ }
+
+ // 1-D encoding
+ } else {
+ codingLine[0] = 0;
+ a0i = 0;
+ blackPixels = 0;
+ while (codingLine[a0i] < columns) {
+ code1 = 0;
+ if (blackPixels) {
+ do {
+ code1 += code3 = getBlackCode();
+ } while (code3 >= 64);
+ } else {
+ do {
+ code1 += code3 = getWhiteCode();
+ } while (code3 >= 64);
+ }
+ addPixels(codingLine[a0i] + code1, blackPixels);
+ blackPixels ^= 1;
+ }
+ }
+
+ // check for end-of-line marker, skipping over any extra zero bits
+ // (if EncodedByteAlign is true and EndOfLine is false, there can
+ // be "false" EOL markers -- i.e., if the last n unused bits in
+ // row i are set to zero, and the first 11-n bits in row i+1
+ // happen to be zero -- so we don't look for EOL markers in this
+ // case)
+ gotEOL = gFalse;
+ if (!endOfBlock && row == rows - 1) {
+ eof = gTrue;
+ } else if (endOfLine || !byteAlign) {
+ code1 = lookBits(12);
+ if (endOfLine) {
+ while (code1 != EOF && code1 != 0x001) {
+ eatBits(1);
+ code1 = lookBits(12);
+ }
+ } else {
+ while (code1 == 0) {
+ eatBits(1);
+ code1 = lookBits(12);
+ }
+ }
+ if (code1 == 0x001) {
+ eatBits(12);
+ gotEOL = gTrue;
+ }
+ }
+
+ // byte-align the row
+ // (Adobe apparently doesn't do byte alignment after EOL markers
+ // -- I've seen CCITT image data streams in two different formats,
+ // both with the byteAlign flag set:
+ // 1. xx:x0:01:yy:yy
+ // 2. xx:00:1y:yy:yy
+ // where xx is the previous line, yy is the next line, and colons
+ // separate bytes.)
+ if (byteAlign && !gotEOL) {
+ inputBits &= ~7;
+ }
+
+ // check for end of stream
+ if (lookBits(1) == EOF) {
+ eof = gTrue;
+ }
+
+ // get 2D encoding tag
+ if (!eof && encoding > 0) {
+ nextLine2D = !lookBits(1);
+ eatBits(1);
+ }
+
+ // check for end-of-block marker
+ if (endOfBlock && !endOfLine && byteAlign) {
+ // in this case, we didn't check for an EOL code above, so we
+ // need to check here
+ code1 = lookBits(24);
+ if (code1 == 0x001001) {
+ eatBits(12);
+ gotEOL = gTrue;
+ }
+ }
+ 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(errSyntaxError, getPos(),
+ "Bad RTC code in CCITTFax stream");
+ ++nErrors;
+ }
+ 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) {
+ while (1) {
+ code1 = lookBits(13);
+ if (code1 == EOF) {
+ eof = gTrue;
+ return gFalse;
+ }
+ if ((code1 >> 1) == 0x001) {
+ break;
+ }
+ eatBits(1);
+ }
+ eatBits(12);
+ if (encoding > 0) {
+ eatBits(1);
+ nextLine2D = !(code1 & 1);
+ }
+ }
+
+ // corrupt CCITTFax streams can generate huge data expansion -- we
+ // avoid that case by aborting decode after 1000 errors
+ if (nErrors > 1000) {
+ error(errSyntaxError, getPos(), "Too many errors in CCITTFaxStream - aborting decode");
+ eof = gTrue;
+ return gFalse;
+ }
+
+ // set up for output
+ nextCol = 0;
+ a0i = (codingLine[0] > 0) ? 0 : 1;
+
+ ++row;
+
+ return gTrue;
+}
+
+short CCITTFaxStream::getTwoDimCode() {
+ int code;
+ CCITTCode *p;
+ int n;
+
+ code = 0; // make gcc happy
+ if (endOfBlock) {
+ if ((code = lookBits(7)) != EOF) {
+ p = &twoDimTab1[code];
+ if (p->bits > 0) {
+ eatBits(p->bits);
+ return p->n;
+ }
+ }
+ } else {
+ for (n = 1; n <= 7; ++n) {
+ if ((code = lookBits(n)) == EOF) {
+ break;
+ }
+ if (n < 7) {
+ code <<= 7 - n;
+ }
+ p = &twoDimTab1[code];
+ if (p->bits == n) {
+ eatBits(n);
+ return p->n;
+ }
+ }
+ }
+ error(errSyntaxError, getPos(),
+ "Bad two dim code ({0:04x}) in CCITTFax stream", code);
+ ++nErrors;
+ return EOF;
+}
+
+short CCITTFaxStream::getWhiteCode() {
+ short code;
+ CCITTCode *p;
+ int n;
+
+ code = 0; // make gcc happy
+ if (endOfBlock) {
+ code = lookBits(12);
+ if (code == EOF) {
+ return 1;
+ }
+ 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 (code == EOF) {
+ return 1;
+ }
+ if (n < 9) {
+ code = (short)(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 (code == EOF) {
+ return 1;
+ }
+ if (n < 12) {
+ code = (short)(code << (12 - n));
+ }
+ p = &whiteTab1[code];
+ if (p->bits == n) {
+ eatBits(n);
+ return p->n;
+ }
+ }
+ }
+ error(errSyntaxError, getPos(),
+ "Bad white code ({0:04x}) in CCITTFax stream", code);
+ ++nErrors;
+ // 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 = 0; // make gcc happy
+ if (endOfBlock) {
+ code = lookBits(13);
+ if (code == EOF) {
+ return 1;
+ }
+ if ((code >> 7) == 0) {
+ p = &blackTab1[code];
+ } else if ((code >> 9) == 0 && (code >> 7) != 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 (code == EOF) {
+ return 1;
+ }
+ if (n < 6) {
+ code = (short)(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 (code == EOF) {
+ return 1;
+ }
+ if (n < 12) {
+ code = (short)(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 (code == EOF) {
+ return 1;
+ }
+ if (n < 13) {
+ code = (short)(code << (13 - n));
+ }
+ p = &blackTab1[code];
+ if (p->bits == n) {
+ eatBits(n);
+ return p->n;
+ }
+ }
+ }
+ error(errSyntaxError, getPos(),
+ "Bad black code ({0:04x}) in CCITTFax stream", code);
+ ++nErrors;
+ // 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::lookBits(int n) {
+ int c;
+
+ while (inputBits < n) {
+ if ((c = str->getChar()) == EOF) {
+ if (inputBits == 0) {
+ return EOF;
+ }
+ // 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 (short)((inputBuf << (n - inputBits)) & (0xffffffff >> (32 - n)));
+ }
+ inputBuf = (inputBuf << 8) + c;
+ inputBits += 8;
+ }
+ return (short)((inputBuf >> (inputBits - n)) & (0xffffffff >> (32 - n)));
+}
+
+GString *CCITTFaxStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("<< ");
+ if (encoding != 0) {
+ s->appendf("/K {0:d} ", encoding);
+ }
+ if (endOfLine) {
+ s->append("/EndOfLine true ");
+ }
+ if (byteAlign) {
+ s->append("/EncodedByteAlign true ");
+ }
+ s->appendf("/Columns {0:d} ", columns);
+ if (rows != 0) {
+ s->appendf("/Rows {0:d} ", rows);
+ }
+ if (!endOfBlock) {
+ s->append("/EndOfBlock false ");
+ }
+ if (black) {
+ s->append("/BlackIs1 true ");
+ }
+ s->append(">> /CCITTFaxDecode filter\n");
+ return s;
+}
+
+GBool CCITTFaxStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// DCTStream
+//------------------------------------------------------------------------
+
+#if HAVE_JPEGLIB
+
+DCTStream::DCTStream(Stream *strA, GBool colorXformA):
+ FilterStream(strA) {
+ colorXform = colorXformA;
+ lineBuf = NULL;
+ inlineImage = str->isEmbedStream();
+}
+
+DCTStream::~DCTStream() {
+ delete str;
+}
+
+Stream *DCTStream::copy() {
+ return new DCTStream(str->copy(), colorXform);
+}
+
+void DCTStream::reset() {
+ int i;
+
+ lineBuf = NULL;
+ error = gFalse;
+
+ str->reset();
+
+ // initialize the libjpeg decompression object
+ decomp.err = jpeg_std_error(&errorMgr.err);
+ errorMgr.err.error_exit = &errorExit;
+ errorMgr.err.output_message = &errorMessage;
+ if (setjmp(errorMgr.setjmpBuf)) {
+ error = gTrue;
+ return;
+ }
+ jpeg_create_decompress(&decomp);
+
+ // set up the data source manager
+ sourceMgr.src.next_input_byte = NULL;
+ sourceMgr.src.bytes_in_buffer = 0;
+ sourceMgr.src.init_source = &initSourceCbk;
+ sourceMgr.src.fill_input_buffer = &fillInputBufferCbk;
+ sourceMgr.src.skip_input_data = &skipInputDataCbk;
+ sourceMgr.src.resync_to_restart = &jpeg_resync_to_restart;
+ sourceMgr.src.term_source = &termSourceCbk;
+ sourceMgr.str = this;
+ decomp.src = &sourceMgr.src;
+
+ // read the header
+ jpeg_read_header(&decomp, TRUE);
+ jpeg_calc_output_dimensions(&decomp);
+
+ // set up the color transform
+ if (!decomp.saw_Adobe_marker && colorXform >= 0) {
+ if (decomp.num_components == 3) {
+ decomp.jpeg_color_space = colorXform ? JCS_YCbCr : JCS_RGB;
+ decomp.out_color_space = JCS_RGB;
+ decomp.out_color_components = 3;
+ } else if (decomp.num_components == 4) {
+ decomp.jpeg_color_space = colorXform ? JCS_YCCK : JCS_CMYK;
+ decomp.out_color_space = JCS_CMYK;
+ decomp.out_color_components = 4;
+ }
+ }
+
+ // allocate a line buffer
+ if ((lineBufHeight = decomp.rec_outbuf_height) > 4) {
+ lineBufHeight = 4;
+ }
+ lineBuf = (char *)gmallocn(lineBufHeight * decomp.out_color_components,
+ decomp.output_width);
+ for (i = 0; i < lineBufHeight; ++i) {
+ lineBufRows[i] = lineBuf +
+ i * decomp.out_color_components * decomp.output_width;
+ }
+ bufPtr = bufEnd = lineBuf;
+
+ // start up the decompression process
+ jpeg_start_decompress(&decomp);
+}
+
+GBool DCTStream::checkSequentialInterleaved() {
+ //~ this is unimplemented
+ return gTrue;
+}
+
+void DCTStream::close() {
+ // we don't call jpeg_finish_decompress() here because it will report
+ // an error if the full image wasn't read
+ if (setjmp(errorMgr.setjmpBuf)) {
+ goto skip;
+ }
+ jpeg_destroy_decompress(&decomp);
+ skip:
+ gfree(lineBuf);
+ FilterStream::close();
+}
+
+int DCTStream::getChar() {
+ if (error) {
+ return EOF;
+ }
+ if (bufPtr == bufEnd) {
+ if (!fillBuf()) {
+ return EOF;
+ }
+ }
+ return *bufPtr++ & 0xff;
+}
+
+int DCTStream::lookChar() {
+ if (error) {
+ return EOF;
+ }
+ if (bufPtr == bufEnd) {
+ if (!fillBuf()) {
+ return EOF;
+ }
+ }
+ return *bufPtr & 0xff;
+}
+
+int DCTStream::getBlock(char *blk, int size) {
+ int nRead, nAvail, n;
+
+ if (error) {
+ return 0;
+ }
+ nRead = 0;
+ while (nRead < size) {
+ if (bufPtr == bufEnd) {
+ if (!fillBuf()) {
+ break;
+ }
+ }
+ nAvail = bufEnd - bufPtr;
+ n = (nAvail < size - nRead) ? nAvail : size - nRead;
+ memcpy(blk + nRead, bufPtr, n);
+ bufPtr += n;
+ nRead += n;
+ }
+ return nRead;
+}
+
+GBool DCTStream::fillBuf() {
+ int nLines;
+
+ if (setjmp(errorMgr.setjmpBuf)) {
+ error = gTrue;
+ return gFalse;
+ }
+ nLines = jpeg_read_scanlines(&decomp, (JSAMPARRAY)lineBufRows,
+ lineBufHeight);
+ bufPtr = lineBuf;
+ bufEnd = lineBuf +
+ nLines * decomp.out_color_components * decomp.output_width;
+ return nLines > 0;
+}
+
+void DCTStream::errorExit(j_common_ptr d) {
+ DCTErrorMgr *errMgr = (DCTErrorMgr *)d->err;
+ longjmp(errMgr->setjmpBuf, 1);
+}
+
+void DCTStream::errorMessage(j_common_ptr d) {
+#if 0 // for debugging
+ char buf[JMSG_LENGTH_MAX];
+
+ (*d->err->format_message)(d, buf);
+ fprintf(stderr, "%s\n", buf);
+#endif
+}
+
+void DCTStream::initSourceCbk(j_decompress_ptr d) {
+ DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src;
+
+ sourceMgr->src.next_input_byte = NULL;
+ sourceMgr->src.bytes_in_buffer = 0;
+}
+
+boolean DCTStream::fillInputBufferCbk(j_decompress_ptr d) {
+ DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src;
+ int c, n;
+
+ // for inline images, we need to read one byte at a time so we don't
+ // read past the end of the input data
+ if (sourceMgr->str->inlineImage) {
+ c = sourceMgr->str->str->getChar();
+ if (c == EOF) {
+ sourceMgr->buf[0] = (char)0xff;
+ sourceMgr->buf[1] = (char)JPEG_EOI;
+ sourceMgr->src.bytes_in_buffer = 2;
+ } else {
+ sourceMgr->buf[0] = (char)c;
+ sourceMgr->src.bytes_in_buffer = 1;
+ }
+ } else {
+ n = sourceMgr->str->str->getBlock(sourceMgr->buf, dctStreamBufSize);
+ if (n > 0) {
+ sourceMgr->src.bytes_in_buffer = (size_t)n;
+ } else {
+ sourceMgr->buf[0] = (char)0xff;
+ sourceMgr->buf[1] = (char)JPEG_EOI;
+ sourceMgr->src.bytes_in_buffer = 2;
+ }
+ }
+ sourceMgr->src.next_input_byte = (JOCTET *)sourceMgr->buf;
+ return TRUE;
+}
+
+void DCTStream::skipInputDataCbk(j_decompress_ptr d, long numBytes) {
+ DCTSourceMgr *sourceMgr = (DCTSourceMgr *)d->src;
+
+ if (numBytes > 0) {
+ if ((long)sourceMgr->src.bytes_in_buffer < numBytes) {
+ sourceMgr->str->str->discardChars(
+ (Guint)(numBytes - sourceMgr->src.bytes_in_buffer));
+ sourceMgr->src.bytes_in_buffer = 0;
+ } else {
+ sourceMgr->src.bytes_in_buffer -= numBytes;
+ sourceMgr->src.next_input_byte += numBytes;
+ }
+ }
+}
+
+void DCTStream::termSourceCbk(j_decompress_ptr d) {
+}
+
+#else // HAVE_JPEGLIB
+
+#define idctScaleA 1024
+#define idctScaleB 1138
+#define idctScaleC 1730
+#define idctScaleD 1609
+#define idctScaleE 1264
+#define idctScaleF 1922
+#define idctScaleG 1788
+#define idctScaleH 2923
+#define idctScaleI 2718
+#define idctScaleJ 2528
+
+static int idctScaleMat[64] = {
+ idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB,
+ idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE,
+ idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF,
+ idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG,
+ idctScaleA, idctScaleB, idctScaleC, idctScaleD, idctScaleA, idctScaleD, idctScaleC, idctScaleB,
+ idctScaleD, idctScaleG, idctScaleI, idctScaleJ, idctScaleD, idctScaleJ, idctScaleI, idctScaleG,
+ idctScaleC, idctScaleF, idctScaleH, idctScaleI, idctScaleC, idctScaleI, idctScaleH, idctScaleF,
+ idctScaleB, idctScaleE, idctScaleF, idctScaleG, idctScaleB, idctScaleG, idctScaleF, idctScaleE
+};
+
+// color conversion parameters (16.16 fixed point format)
+#define dctCrToR 91881 // 1.4020
+#define dctCbToG -22553 // -0.3441363
+#define dctCrToG -46802 // -0.71413636
+#define dctCbToB 116130 // 1.772
+
+// The dctClip function clips signed integers to the [0,255] range.
+// To handle valid DCT inputs, this must support an input range of at
+// least [-256,511]. Invalid DCT inputs (e.g., from damaged PDF
+// files) can result in arbitrary values, so we want to mask those
+// out. We round the input range size up to a power of 2 (so we can
+// use a bit mask), which gives us an input range of [-384,639]. The
+// end result is:
+// input output
+// ---------- ------
+// <-384 X invalid inputs -> output is "don't care"
+// -384..-257 0 invalid inputs, clipped
+// -256..-1 0 valid inputs, need to be clipped
+// 0..255 0..255
+// 256..511 255 valid inputs, need to be clipped
+// 512..639 255 invalid inputs, clipped
+// >=512 X invalid inputs -> output is "don't care"
+
+#define dctClipOffset 384
+#define dctClipMask 1023
+static Guchar dctClipData[1024];
+
+static inline void dctClipInit() {
+ static int initDone = 0;
+ int i;
+ if (!initDone) {
+ for (i = -384; i < 0; ++i) {
+ dctClipData[dctClipOffset + i] = 0;
+ }
+ for (i = 0; i < 256; ++i) {
+ dctClipData[dctClipOffset + i] = (Guchar)i;
+ }
+ for (i = 256; i < 639; ++i) {
+ dctClipData[dctClipOffset + i] = 255;
+ }
+ initDone = 1;
+ }
+}
+
+static inline Guchar dctClip(int x) {
+ return dctClipData[(dctClipOffset + x) & dctClipMask];
+}
+
+// zig zag decode map
+static int dctZigZag[64] = {
+ 0,
+ 1, 8,
+ 16, 9, 2,
+ 3, 10, 17, 24,
+ 32, 25, 18, 11, 4,
+ 5, 12, 19, 26, 33, 40,
+ 48, 41, 34, 27, 20, 13, 6,
+ 7, 14, 21, 28, 35, 42, 49, 56,
+ 57, 50, 43, 36, 29, 22, 15,
+ 23, 30, 37, 44, 51, 58,
+ 59, 52, 45, 38, 31,
+ 39, 46, 53, 60,
+ 61, 54, 47,
+ 55, 62,
+ 63
+};
+
+DCTStream::DCTStream(Stream *strA, GBool colorXformA):
+ FilterStream(strA) {
+ int i;
+
+ prepared = gFalse;
+ colorXform = colorXformA;
+ progressive = interleaved = gFalse;
+ width = height = 0;
+ mcuWidth = mcuHeight = 0;
+ numComps = 0;
+ comp = 0;
+ x = y = 0;
+ for (i = 0; i < 4; ++i) {
+ frameBuf[i] = NULL;
+ }
+ rowBuf = NULL;
+ memset(dcHuffTables, 0, sizeof(dcHuffTables));
+ memset(acHuffTables, 0, sizeof(acHuffTables));
+
+ dctClipInit();
+}
+
+DCTStream::~DCTStream() {
+ close();
+ delete str;
+}
+
+Stream *DCTStream::copy() {
+ return new DCTStream(str->copy(), colorXform);
+}
+
+void DCTStream::reset() {
+ int i;
+
+ str->reset();
+
+ progressive = interleaved = gFalse;
+ width = height = 0;
+ numComps = 0;
+ numQuantTables = 0;
+ numDCHuffTables = 0;
+ numACHuffTables = 0;
+ gotJFIFMarker = gFalse;
+ gotAdobeMarker = gFalse;
+ restartInterval = 0;
+
+ if (!readHeader(gTrue)) {
+ // force an EOF condition
+ progressive = gTrue;
+ y = height;
+ prepared = gTrue;
+ return;
+ }
+
+ // compute MCU size
+ if (numComps == 1) {
+ compInfo[0].hSample = compInfo[0].vSample = 1;
+ }
+ mcuWidth = compInfo[0].hSample;
+ mcuHeight = compInfo[0].vSample;
+ for (i = 1; i < numComps; ++i) {
+ if (compInfo[i].hSample > mcuWidth) {
+ mcuWidth = compInfo[i].hSample;
+ }
+ if (compInfo[i].vSample > mcuHeight) {
+ mcuHeight = compInfo[i].vSample;
+ }
+ }
+ mcuWidth *= 8;
+ mcuHeight *= 8;
+
+ // figure out color transform
+ if (colorXform == -1) {
+ if (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;
+ }
+ } else {
+ colorXform = 0;
+ }
+ }
+
+ prepared = gFalse;
+}
+
+GBool DCTStream::checkSequentialInterleaved() {
+ GBool headerOk;
+
+ str->reset();
+
+ progressive = interleaved = gFalse;
+ width = height = 0;
+ numComps = 0;
+ numQuantTables = 0;
+ numDCHuffTables = 0;
+ numACHuffTables = 0;
+ gotJFIFMarker = gFalse;
+ gotAdobeMarker = gFalse;
+ restartInterval = 0;
+
+ headerOk = readHeader(gTrue);
+
+ FilterStream::close();
+
+ return headerOk && !progressive && interleaved;
+}
+
+void DCTStream::close() {
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ gfree(frameBuf[i]);
+ frameBuf[i] = NULL;
+ }
+ gfree(rowBuf);
+ rowBuf = NULL;
+ FilterStream::close();
+}
+
+int DCTStream::getChar() {
+ int c;
+
+ if (!prepared) {
+ prepare();
+ }
+ if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
+ c = frameBuf[comp][y * bufWidth + x];
+ if (++comp == numComps) {
+ comp = 0;
+ if (++x == width) {
+ x = 0;
+ ++y;
+ }
+ }
+ } else {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
+ y += mcuHeight;
+ if (!readMCURow()) {
+ y = height;
+ return EOF;
+ }
+ }
+ c = *rowBufPtr++;
+ }
+ return c;
+}
+
+int DCTStream::lookChar() {
+ if (!prepared) {
+ prepare();
+ }
+ if (progressive || !interleaved) {
+ if (y >= height) {
+ return EOF;
+ }
+ return frameBuf[comp][y * bufWidth + x];
+ } else {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ return EOF;
+ }
+ if (!readMCURow()) {
+ y = height;
+ return EOF;
+ }
+ }
+ return *rowBufPtr;
+ }
+}
+
+int DCTStream::getBlock(char *blk, int size) {
+ int nRead, nAvail, n;
+
+ if (!prepared) {
+ prepare();
+ }
+ if (progressive || !interleaved) {
+ if (y >= height) {
+ return 0;
+ }
+ for (nRead = 0; nRead < size; ++nRead) {
+ blk[nRead] = (char)frameBuf[comp][y * bufWidth + x];
+ if (++comp == numComps) {
+ comp = 0;
+ if (++x == width) {
+ x = 0;
+ ++y;
+ if (y >= height) {
+ ++nRead;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ nRead = 0;
+ while (nRead < size) {
+ if (rowBufPtr == rowBufEnd) {
+ if (y + mcuHeight >= height) {
+ break;
+ }
+ y += mcuHeight;
+ if (!readMCURow()) {
+ y = height;
+ break;
+ }
+ }
+ nAvail = (int)(rowBufEnd - rowBufPtr);
+ n = (nAvail < size - nRead) ? nAvail : size - nRead;
+ memcpy(blk + nRead, rowBufPtr, n);
+ rowBufPtr += n;
+ nRead += n;
+ }
+ }
+ return nRead;
+}
+
+void DCTStream::prepare() {
+ int i;
+
+ if (progressive || !interleaved) {
+
+ // allocate a buffer for the whole image
+ bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+ bufHeight = ((height + mcuHeight - 1) / mcuHeight) * mcuHeight;
+ if (bufWidth <= 0 || bufHeight <= 0 ||
+ bufWidth > INT_MAX / bufWidth / (int)sizeof(int)) {
+ error(errSyntaxError, getPos(), "Invalid image size in DCT stream");
+ y = height;
+ prepared = gTrue;
+ return;
+ }
+#if USE_EXCEPTIONS
+ try {
+#endif
+ for (i = 0; i < numComps; ++i) {
+ frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int));
+ memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int));
+ }
+#if USE_EXCEPTIONS
+ } catch (GMemException) {
+ error(errSyntaxError, getPos(), "Out of memory in DCT stream");
+ y = height;
+ prepared = gTrue;
+ return;
+ }
+#endif
+
+ // read the image data
+ do {
+ restartMarker = 0xd0;
+ restart();
+ readScan();
+ } while (readHeader(gFalse));
+
+ // decode
+ decodeImage();
+
+ // initialize counters
+ comp = 0;
+ x = 0;
+ y = 0;
+
+ } else {
+
+ if (scanInfo.numComps != numComps) {
+ error(errSyntaxError, getPos(), "Invalid scan in sequential DCT stream");
+ y = height;
+ prepared = gTrue;
+ return;
+ }
+
+ // allocate a buffer for one row of MCUs
+ bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;
+ rowBuf = (Guchar *)gmallocn(numComps * mcuHeight, bufWidth);
+ rowBufPtr = rowBufEnd = rowBuf;
+
+ // initialize counters
+ y = -mcuHeight;
+
+ restartMarker = 0xd0;
+ restart();
+ }
+
+ prepared = gTrue;
+}
+
+void DCTStream::restart() {
+ int i;
+
+ inputBits = 0;
+ restartCtr = restartInterval;
+ 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() {
+ int data1[64];
+ Guchar data2[64];
+ Guchar *p1, *p2;
+ int pY, pCb, pCr, pR, pG, pB;
+ int h, v, horiz, vert, hSub, vSub;
+ int x1, x2, y2, x3, y3, x4, y4, x5, y5, cc, i;
+ int c;
+
+ for (cc = 0; cc < numComps; ++cc) {
+ if (scanInfo.dcHuffTable[cc] >= numDCHuffTables ||
+ scanInfo.acHuffTable[cc] >= numACHuffTables) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: invalid Huffman table index");
+ return gFalse;
+ }
+ if (compInfo[cc].quantTable > numQuantTables) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: invalid quant table index");
+ return gFalse;
+ }
+ }
+
+ for (x1 = 0; x1 < width; x1 += mcuWidth) {
+
+ // deal with restart marker
+ if (restartInterval > 0 && restartCtr == 0) {
+ c = readMarker();
+ if (c != restartMarker) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: incorrect restart marker");
+ return gFalse;
+ }
+ if (++restartMarker == 0xd8)
+ restartMarker = 0xd0;
+ restart();
+ }
+
+ // read one MCU
+ for (cc = 0; cc < numComps; ++cc) {
+ 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) {
+ if (!readDataUnit(&dcHuffTables[scanInfo.dcHuffTable[cc]],
+ &acHuffTables[scanInfo.acHuffTable[cc]],
+ &compInfo[cc].prevDC,
+ data1)) {
+ return gFalse;
+ }
+ transformDataUnit(quantTables[compInfo[cc].quantTable],
+ data1, data2);
+ if (hSub == 1 && vSub == 1 && x1+x2+8 <= width) {
+ for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p1[0] = data2[i];
+ p1[ numComps] = data2[i+1];
+ p1[2*numComps] = data2[i+2];
+ p1[3*numComps] = data2[i+3];
+ p1[4*numComps] = data2[i+4];
+ p1[5*numComps] = data2[i+5];
+ p1[6*numComps] = data2[i+6];
+ p1[7*numComps] = data2[i+7];
+ }
+ } else if (hSub == 2 && vSub == 2 && x1+x2+16 <= width) {
+ for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {
+ p1 = &rowBuf[((y2+y3) * width + (x1+x2)) * numComps + cc];
+ p2 = p1 + width * numComps;
+ p1[0] = p1[numComps] =
+ p2[0] = p2[numComps] = data2[i];
+ p1[2*numComps] = p1[3*numComps] =
+ p2[2*numComps] = p2[3*numComps] = data2[i+1];
+ p1[4*numComps] = p1[5*numComps] =
+ p2[4*numComps] = p2[5*numComps] = data2[i+2];
+ p1[6*numComps] = p1[7*numComps] =
+ p2[6*numComps] = p2[7*numComps] = data2[i+3];
+ p1[8*numComps] = p1[9*numComps] =
+ p2[8*numComps] = p2[9*numComps] = data2[i+4];
+ p1[10*numComps] = p1[11*numComps] =
+ p2[10*numComps] = p2[11*numComps] = data2[i+5];
+ p1[12*numComps] = p1[13*numComps] =
+ p2[12*numComps] = p2[13*numComps] = data2[i+6];
+ p1[14*numComps] = p1[15*numComps] =
+ p2[14*numComps] = p2[15*numComps] = data2[i+7];
+ }
+ } else {
+ p1 = &rowBuf[(y2 * width + (x1+x2)) * numComps + cc];
+ i = 0;
+ for (y3 = 0, y4 = 0; y3 < 8; ++y3, y4 += vSub) {
+ for (x3 = 0, x4 = 0; x3 < 8; ++x3, x4 += hSub) {
+ for (y5 = 0; y5 < vSub; ++y5) {
+ for (x5 = 0; x5 < hSub && x1+x2+x4+x5 < width; ++x5) {
+ p1[((y4+y5) * width + (x4+x5)) * numComps] = data2[i];
+ }
+ }
+ ++i;
+ }
+ }
+ }
+ }
+ }
+ }
+ --restartCtr;
+ }
+
+ // color space conversion
+ if (colorXform) {
+ // convert YCbCr to RGB
+ if (numComps == 3) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 3) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = dctClip(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = dctClip(pB);
+ }
+ // convert YCbCrK to CMYK (K is passed through unchanged)
+ } else if (numComps == 4) {
+ for (i = 0, p1 = rowBuf; i < width * mcuHeight; ++i, p1 += 4) {
+ pY = p1[0];
+ pCb = p1[1] - 128;
+ pCr = p1[2] - 128;
+ pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;
+ p1[0] = (Guchar)(255 - dctClip(pR));
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;
+ p1[1] = (Guchar)(255 - dctClip(pG));
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ p1[2] = (Guchar)(255 - dctClip(pB));
+ }
+ }
+ }
+
+ rowBufPtr = rowBuf;
+ if (y + mcuHeight <= height) {
+ rowBufEnd = rowBuf + numComps * width * mcuHeight;
+ } else {
+ rowBufEnd = rowBuf + numComps * width * (height - y);
+ }
+
+ return gTrue;
+}
+
+// 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;
+
+ for (cc = 0; cc < numComps; ++cc) {
+ if (scanInfo.comp[cc] &&
+ (scanInfo.dcHuffTable[cc] >= numDCHuffTables ||
+ ((!progressive || scanInfo.lastCoeff > 0) &&
+ scanInfo.acHuffTable[cc] >= numACHuffTables))) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: invalid Huffman table index");
+ return;
+ }
+ if (compInfo[cc].quantTable > numQuantTables) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT data: invalid quant table index");
+ return;
+ }
+ }
+
+ 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(errSyntaxError, 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,
+ int *prevDC, int data[64]) {
+ int run, size, amp;
+ int c;
+ int i, j;
+
+ if ((size = readHuffSym(dcHuffTable)) == 9999) {
+ return gFalse;
+ }
+ if (size > 0) {
+ if ((amp = readAmp(size)) == 9999) {
+ return gFalse;
+ }
+ } else {
+ amp = 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) {
+ 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;
+ if (i < 64) {
+ j = dctZigZag[i++];
+ data[j] = amp;
+ }
+ }
+ }
+ return gTrue;
+}
+
+// Read one data unit from a progressive 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;
+ }
+ if (bit) {
+ data[0] += 1 << scanInfo.al;
+ }
+ }
+ ++i;
+ }
+ if (scanInfo.lastCoeff == 0) {
+ return gTrue;
+ }
+
+ // check for an EOB run
+ if (eobRun > 0) {
+ while (i <= scanInfo.lastCoeff) {
+ j = dctZigZag[i++];
+ if (data[j] != 0) {
+ if ((bit = readBit()) == EOF) {
+ return gFalse;
+ }
+ if (bit) {
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ 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 && i <= scanInfo.lastCoeff) {
+ j = dctZigZag[i++];
+ if (data[j] == 0) {
+ ++k;
+ } else {
+ if ((bit = readBit()) == EOF) {
+ return gFalse;
+ }
+ if (bit) {
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ 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) {
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ 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;
+ }
+ j = 0; // make gcc happy
+ for (k = 0; k <= run && i <= scanInfo.lastCoeff; ++k) {
+ j = dctZigZag[i++];
+ while (data[j] != 0 && i <= scanInfo.lastCoeff) {
+ if ((bit = readBit()) == EOF) {
+ return gFalse;
+ }
+ if (bit) {
+ if (data[j] >= 0) {
+ data[j] += 1 << scanInfo.al;
+ } else {
+ data[j] -= 1 << scanInfo.al;
+ }
+ }
+ j = dctZigZag[i++];
+ }
+ }
+ data[j] = amp << scanInfo.al;
+ }
+ }
+
+ return gTrue;
+}
+
+// Decode a progressive JPEG image.
+void DCTStream::decodeImage() {
+ int dataIn[64];
+ Guchar dataOut[64];
+ Gushort *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(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+ 32768) >> 16;
+ *p1++ = dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ *p2++ = dctClip(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(pR);
+ pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr +
+ 32768) >> 16;
+ *p1++ = 255 - dctClip(pG);
+ pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;
+ *p2++ = 255 - dctClip(pB);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// Transform one data unit -- this performs the dequantization and
+// IDCT steps. This IDCT algorithm is taken from:
+// Y. A. Reznik, A. T. Hinds, L. Yu, Z. Ni, and C-X. Zhang,
+// "Efficient fixed-point approximations of the 8x8 inverse discrete
+// cosine transform" (invited paper), Proc. SPIE Vol. 6696, Sep. 24,
+// 2007.
+// which is based on:
+// 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 the
+// Loeffler paper.
+void DCTStream::transformDataUnit(Gushort *quantTable,
+ int dataIn[64], Guchar dataOut[64]) {
+ int v0, v1, v2, v3, v4, v5, v6, v7;
+ int t0, t1, t2, t3, t4, t5, t6, t7;
+ int *p, *scale;
+ Gushort *q;
+ int i;
+
+ // dequant; inverse DCT on rows
+ for (i = 0; i < 64; i += 8) {
+ p = dataIn + i;
+ q = quantTable + i;
+ scale = idctScaleMat + 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) {
+ t0 = p[0] * q[0] * scale[0];
+ if (i == 0) {
+ t0 += 1 << 12; // rounding bias
+ }
+ p[0] = t0;
+ p[1] = t0;
+ p[2] = t0;
+ p[3] = t0;
+ p[4] = t0;
+ p[5] = t0;
+ p[6] = t0;
+ p[7] = t0;
+ continue;
+ }
+
+ // stage 4
+ v0 = p[0] * q[0] * scale[0];
+ if (i == 0) {
+ v0 += 1 << 12; // rounding bias
+ }
+ v1 = p[4] * q[4] * scale[4];
+ v2 = p[2] * q[2] * scale[2];
+ v3 = p[6] * q[6] * scale[6];
+ t0 = p[1] * q[1] * scale[1];
+ t1 = p[7] * q[7] * scale[7];
+ v4 = t0 - t1;
+ v7 = t0 + t1;
+ v5 = p[3] * q[3] * scale[3];
+ v6 = p[5] * q[5] * scale[5];
+
+ // stage 3
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = v2 + (v2 >> 5);
+ t1 = t0 >> 2;
+ t2 = t1 + (v2 >> 4); // 41/128 * v2
+ t3 = t0 - t1; // 99/128 * v2
+ t4 = v3 + (v3 >> 5);
+ t5 = t4 >> 2;
+ t6 = t5 + (v3 >> 4); // 41/128 * v3
+ t7 = t4 - t5; // 99/128 * v3
+ v2 = t2 - t7;
+ v3 = t3 + t6;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
+
+ // stage 2
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = (v4 >> 9) - v4;
+ t1 = v4 >> 1; // 1/2 * v4
+ t2 = (t0 >> 2) - t0; // 1533/2048 * v4
+ t3 = (v7 >> 9) - v7;
+ t4 = v7 >> 1; // 1/2 * v7
+ t5 = (t3 >> 2) - t3; // 1533/2048 * v7
+ v4 = t2 - t4;
+ v7 = t1 + t5;
+ t0 = (v5 >> 3) - (v5 >> 7);
+ t1 = t0 - (v5 >> 11);
+ t2 = t0 + (t1 >> 1); // 719/4096 * v5
+ t3 = v5 - t0; // 113/256 * v5
+ t4 = (v6 >> 3) - (v6 >> 7);
+ t5 = t4 - (v6 >> 11);
+ t6 = t4 + (t5 >> 1); // 719/4096 * v6
+ t7 = v6 - t4; // 113/256 * v6
+ v5 = t3 - t6;
+ v6 = t2 + t7;
+
+ // stage 1
+ 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) {
+ t0 = p[0*8];
+ p[1*8] = t0;
+ p[2*8] = t0;
+ p[3*8] = t0;
+ p[4*8] = t0;
+ p[5*8] = t0;
+ p[6*8] = t0;
+ p[7*8] = t0;
+ continue;
+ }
+
+ // stage 4
+ v0 = p[0*8];
+ v1 = p[4*8];
+ v2 = p[2*8];
+ v3 = p[6*8];
+ t0 = p[1*8];
+ t1 = p[7*8];
+ v4 = t0 - t1;
+ v7 = t0 + t1;
+ v5 = p[3*8];
+ v6 = p[5*8];
+
+ // stage 3
+ t0 = v0 - v1;
+ v0 = v0 + v1;
+ v1 = t0;
+ t0 = v2 + (v2 >> 5);
+ t1 = t0 >> 2;
+ t2 = t1 + (v2 >> 4); // 41/128 * v2
+ t3 = t0 - t1; // 99/128 * v2
+ t4 = v3 + (v3 >> 5);
+ t5 = t4 >> 2;
+ t6 = t5 + (v3 >> 4); // 41/128 * v3
+ t7 = t4 - t5; // 99/128 * v3
+ v2 = t2 - t7;
+ v3 = t3 + t6;
+ t0 = v4 - v6;
+ v4 = v4 + v6;
+ v6 = t0;
+ t0 = v7 + v5;
+ v5 = v7 - v5;
+ v7 = t0;
+
+ // stage 2
+ t0 = v0 - v3;
+ v0 = v0 + v3;
+ v3 = t0;
+ t0 = v1 - v2;
+ v1 = v1 + v2;
+ v2 = t0;
+ t0 = (v4 >> 9) - v4;
+ t1 = v4 >> 1; // 1/2 * v4
+ t2 = (t0 >> 2) - t0; // 1533/2048 * v4
+ t3 = (v7 >> 9) - v7;
+ t4 = v7 >> 1; // 1/2 * v7
+ t5 = (t3 >> 2) - t3; // 1533/2048 * v7
+ v4 = t2 - t4;
+ v7 = t1 + t5;
+ t0 = (v5 >> 3) - (v5 >> 7);
+ t1 = t0 - (v5 >> 11);
+ t2 = t0 + (t1 >> 1); // 719/4096 * v5
+ t3 = v5 - t0; // 113/256 * v5
+ t4 = (v6 >> 3) - (v6 >> 7);
+ t5 = t4 - (v6 >> 11);
+ t6 = t4 + (t5 >> 1); // 719/4096 * v6
+ t7 = v6 - t4; // 113/256 * v6
+ v5 = t3 - t6;
+ v6 = t2 + t7;
+
+ // stage 1
+ 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) {
+ dataOut[i] = dctClip(128 + (dataIn[i] >> 13));
+ }
+}
+
+int DCTStream::readHuffSym(DCTHuffTable *table) {
+ Gushort code;
+ int bit;
+ int codeBits;
+
+ code = 0;
+ codeBits = 0;
+ do {
+ // add a bit to the code
+ if ((bit = readBit()) == EOF) {
+ return 9999;
+ }
+ code = (Gushort)((code << 1) + bit);
+ ++codeBits;
+
+ // look up code
+ if (code < table->firstCode[codeBits]) {
+ break;
+ }
+ if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) {
+ code = (Gushort)(code - table->firstCode[codeBits]);
+ return table->sym[table->firstSym[codeBits] + code];
+ }
+ } while (codeBits < 16);
+
+ error(errSyntaxError, getPos(), "Bad Huffman code in DCT stream");
+ return 9999;
+}
+
+int DCTStream::readAmp(int size) {
+ int amp, bit;
+ int bits;
+
+ amp = 0;
+ for (bits = 0; bits < size; ++bits) {
+ if ((bit = readBit()) == EOF)
+ return 9999;
+ amp = (amp << 1) + bit;
+ }
+ if (amp < (1 << (size - 1)))
+ amp -= (1 << size) - 1;
+ return amp;
+}
+
+int DCTStream::readBit() {
+ int bit;
+ int c, c2;
+
+ if (inputBits == 0) {
+ if ((c = str->getChar()) == EOF)
+ return EOF;
+ if (c == 0xff) {
+ do {
+ c2 = str->getChar();
+ } while (c2 == 0xff);
+ if (c2 != 0x00) {
+ error(errSyntaxError, getPos(), "Bad DCT data: missing 00 after ff");
+ return EOF;
+ }
+ }
+ inputBuf = c;
+ inputBits = 8;
+ }
+ bit = (inputBuf >> (inputBits - 1)) & 1;
+ --inputBits;
+ return bit;
+}
+
+GBool DCTStream::readHeader(GBool frame) {
+ GBool doScan;
+ int n, i;
+ int c = 0;
+
+ // read headers
+ doScan = gFalse;
+ while (!doScan) {
+ c = readMarker();
+ switch (c) {
+ case 0xc0: // SOF0 (sequential)
+ case 0xc1: // SOF1 (extended sequential)
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ if (!readBaselineSOF()) {
+ return gFalse;
+ }
+ break;
+ case 0xc2: // SOF2 (progressive)
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ if (!readProgressiveSOF()) {
+ return gFalse;
+ }
+ break;
+ case 0xc4: // DHT
+ if (!readHuffmanTables()) {
+ return gFalse;
+ }
+ break;
+ case 0xd8: // SOI
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ break;
+ case 0xd9: // EOI
+ return gFalse;
+ case 0xda: // SOS
+ if (!readScanInfo()) {
+ return gFalse;
+ }
+ if (frame) {
+ interleaved = scanInfo.numComps == numComps;
+ }
+ doScan = gTrue;
+ break;
+ case 0xdb: // DQT
+ if (!readQuantTables()) {
+ return gFalse;
+ }
+ break;
+ case 0xdd: // DRI
+ if (!readRestartInterval()) {
+ return gFalse;
+ }
+ break;
+ case 0xe0: // APP0
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ if (!readJFIFMarker()) {
+ return gFalse;
+ }
+ break;
+ case 0xee: // APP14
+ if (!frame) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT marker in scan <{0:02x}>", c);
+ return gFalse;
+ }
+ if (!readAdobeMarker()) {
+ return gFalse;
+ }
+ break;
+ case EOF:
+ error(errSyntaxError, getPos(), "Bad DCT header");
+ return gFalse;
+ default:
+ // skip APPn / COM / etc.
+ if (c >= 0xe0) {
+ n = read16() - 2;
+ str->discardChars(n);
+ } else {
+ error(errSyntaxError, getPos(), "Unknown DCT marker <{0:02x}>", c);
+ return gFalse;
+ }
+ break;
+ }
+ }
+
+ for (i = 0; i < numComps; ++i) {
+ if (compInfo[i].quantTable >= numQuantTables) {
+ error(errSyntaxError, getPos(), "Invalid DCT quant table selector");
+ return gFalse;
+ }
+ }
+
+ return gTrue;
+}
+
+GBool DCTStream::readBaselineSOF() {
+ int prec;
+ int i;
+ int c;
+
+ read16(); // length
+ prec = str->getChar();
+ height = read16();
+ width = read16();
+ numComps = str->getChar();
+ if (numComps <= 0 || numComps > 4) {
+ error(errSyntaxError, getPos(), "Bad number of components in DCT stream");
+ numComps = 0;
+ return gFalse;
+ }
+ if (prec != 8) {
+ error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec);
+ return gFalse;
+ }
+ for (i = 0; i < numComps; ++i) {
+ compInfo[i].id = str->getChar();
+ c = str->getChar();
+ compInfo[i].hSample = (c >> 4) & 0x0f;
+ compInfo[i].vSample = c & 0x0f;
+ compInfo[i].quantTable = str->getChar();
+ // a sampling factor of 3 is allowed by the spec, but requires
+ // messy upsampling, and appears not to be used in practice
+ if (!(compInfo[i].hSample == 1 ||
+ compInfo[i].hSample == 2 ||
+ compInfo[i].hSample == 4) ||
+ !(compInfo[i].vSample == 1 ||
+ compInfo[i].vSample == 2 ||
+ compInfo[i].vSample == 4)) {
+ error(errSyntaxError, getPos(), "Bad DCT sampling factor");
+ return gFalse;
+ }
+ if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) {
+ error(errSyntaxError, getPos(), "Bad DCT quant table selector");
+ return gFalse;
+ }
+ }
+ progressive = gFalse;
+ return gTrue;
+}
+
+GBool DCTStream::readProgressiveSOF() {
+ int prec;
+ int i;
+ int c;
+
+ read16(); // length
+ prec = str->getChar();
+ height = read16();
+ width = read16();
+ numComps = str->getChar();
+ if (numComps <= 0 || numComps > 4) {
+ error(errSyntaxError, getPos(), "Bad number of components in DCT stream");
+ numComps = 0;
+ return gFalse;
+ }
+ if (prec != 8) {
+ error(errSyntaxError, getPos(), "Bad DCT precision {0:d}", prec);
+ return gFalse;
+ }
+ for (i = 0; i < numComps; ++i) {
+ compInfo[i].id = str->getChar();
+ c = str->getChar();
+ compInfo[i].hSample = (c >> 4) & 0x0f;
+ compInfo[i].vSample = c & 0x0f;
+ compInfo[i].quantTable = str->getChar();
+ // a sampling factor of 3 is allowed by the spec, but requires
+ // messy upsampling, and appears not to be used in practice
+ if (!(compInfo[i].hSample == 1 ||
+ compInfo[i].hSample == 2 ||
+ compInfo[i].hSample == 4) ||
+ !(compInfo[i].vSample == 1 ||
+ compInfo[i].vSample == 2 ||
+ compInfo[i].vSample == 4)) {
+ error(errSyntaxError, getPos(), "Bad DCT sampling factor");
+ return gFalse;
+ }
+ if (compInfo[i].quantTable < 0 || compInfo[i].quantTable > 3) {
+ error(errSyntaxError, getPos(), "Bad DCT quant table selector");
+ return gFalse;
+ }
+ }
+ progressive = gTrue;
+ return gTrue;
+}
+
+GBool DCTStream::readScanInfo() {
+ int length;
+ int id, c;
+ int i, j;
+
+ length = read16() - 2;
+ scanInfo.numComps = str->getChar();
+ if (scanInfo.numComps <= 0 || scanInfo.numComps > 4) {
+ error(errSyntaxError, getPos(), "Bad number of components in DCT stream");
+ scanInfo.numComps = 0;
+ return gFalse;
+ }
+ --length;
+ if (length != 2 * scanInfo.numComps + 3) {
+ error(errSyntaxError, getPos(), "Bad DCT scan info block");
+ return gFalse;
+ }
+ for (j = 0; j < numComps; ++j) {
+ scanInfo.comp[j] = gFalse;
+ }
+ for (i = 0; i < scanInfo.numComps; ++i) {
+ id = str->getChar();
+ // 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(errSyntaxError, getPos(),
+ "Bad DCT component ID in scan info block");
+ return gFalse;
+ }
+ }
+ if (scanInfo.comp[j]) {
+ error(errSyntaxError, getPos(),
+ "Invalid DCT component ID in scan info block");
+ return gFalse;
+ }
+ scanInfo.comp[j] = gTrue;
+ c = str->getChar();
+ scanInfo.dcHuffTable[j] = (c >> 4) & 0x0f;
+ scanInfo.acHuffTable[j] = c & 0x0f;
+ }
+ scanInfo.firstCoeff = str->getChar();
+ scanInfo.lastCoeff = str->getChar();
+ if (scanInfo.firstCoeff < 0 || scanInfo.lastCoeff > 63 ||
+ scanInfo.firstCoeff > scanInfo.lastCoeff) {
+ error(errSyntaxError, getPos(),
+ "Bad DCT coefficient numbers in scan info block");
+ return gFalse;
+ }
+ c = str->getChar();
+ scanInfo.ah = (c >> 4) & 0x0f;
+ scanInfo.al = c & 0x0f;
+ return gTrue;
+}
+
+GBool DCTStream::readQuantTables() {
+ int length, prec, i, index;
+
+ length = read16() - 2;
+ while (length > 0) {
+ index = str->getChar();
+ prec = (index >> 4) & 0x0f;
+ index &= 0x0f;
+ if (prec > 1 || index >= 4) {
+ error(errSyntaxError, getPos(), "Bad DCT quantization table");
+ return gFalse;
+ }
+ if (index >= numQuantTables) {
+ numQuantTables = index + 1;
+ }
+ for (i = 0; i < 64; ++i) {
+ if (prec) {
+ quantTables[index][dctZigZag[i]] = (Gushort)read16();
+ } else {
+ quantTables[index][dctZigZag[i]] = (Gushort)str->getChar();
+ }
+ }
+ if (prec) {
+ length -= 129;
+ } else {
+ length -= 65;
+ }
+ }
+ return gTrue;
+}
+
+GBool DCTStream::readHuffmanTables() {
+ DCTHuffTable *tbl;
+ int length;
+ int index;
+ Gushort code;
+ Guchar sym;
+ int i;
+ int c;
+
+ length = read16() - 2;
+ while (length > 0) {
+ index = str->getChar();
+ --length;
+ if ((index & 0x0f) >= 4) {
+ error(errSyntaxError, getPos(), "Bad DCT Huffman table");
+ return gFalse;
+ }
+ if (index & 0x10) {
+ index &= 0x0f;
+ if (index >= numACHuffTables)
+ numACHuffTables = index+1;
+ tbl = &acHuffTables[index];
+ } else {
+ index &= 0x0f;
+ if (index >= numDCHuffTables)
+ numDCHuffTables = index+1;
+ tbl = &dcHuffTables[index];
+ }
+ sym = 0;
+ code = 0;
+ for (i = 1; i <= 16; ++i) {
+ c = str->getChar();
+ tbl->firstSym[i] = sym;
+ tbl->firstCode[i] = code;
+ tbl->numCodes[i] = (Gushort)c;
+ sym = (Guchar)(sym + c);
+ code = (Gushort)((code + c) << 1);
+ }
+ length -= 16;
+ for (i = 0; i < sym; ++i)
+ tbl->sym[i] = (Guchar)str->getChar();
+ length -= sym;
+ }
+ return gTrue;
+}
+
+GBool DCTStream::readRestartInterval() {
+ int length;
+
+ length = read16();
+ if (length != 4) {
+ error(errSyntaxError, getPos(), "Bad DCT restart interval");
+ return gFalse;
+ }
+ restartInterval = read16();
+ 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(errSyntaxError, getPos(), "Bad DCT APP0 marker");
+ return gFalse;
+ }
+ buf[i] = (char)c;
+ }
+ length -= 5;
+ if (!memcmp(buf, "JFIF\0", 5)) {
+ gotJFIFMarker = gTrue;
+ }
+ }
+ while (length > 0) {
+ if (str->getChar() == EOF) {
+ error(errSyntaxError, 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) {
+ goto err;
+ }
+ for (i = 0; i < 12; ++i) {
+ if ((c = str->getChar()) == EOF) {
+ goto err;
+ }
+ buf[i] = (char)c;
+ }
+ if (!strncmp(buf, "Adobe", 5)) {
+ colorXform = buf[11];
+ gotAdobeMarker = gTrue;
+ }
+ for (i = 14; i < length; ++i) {
+ if (str->getChar() == EOF) {
+ goto err;
+ }
+ }
+ return gTrue;
+
+ err:
+ error(errSyntaxError, getPos(), "Bad DCT Adobe APP14 marker");
+ return gFalse;
+}
+
+GBool DCTStream::readTrailer() {
+ int c;
+
+ c = readMarker();
+ if (c != 0xd9) { // EOI
+ error(errSyntaxError, getPos(), "Bad DCT trailer");
+ return gFalse;
+ }
+ return gTrue;
+}
+
+int DCTStream::readMarker() {
+ int c;
+
+ do {
+ do {
+ c = str->getChar();
+ } while (c != 0xff && c != EOF);
+ do {
+ c = str->getChar();
+ } while (c == 0xff);
+ } while (c == 0x00);
+ return c;
+}
+
+int DCTStream::read16() {
+ int c1, c2;
+
+ if ((c1 = str->getChar()) == EOF)
+ return EOF;
+ if ((c2 = str->getChar()) == EOF)
+ return EOF;
+ return (c1 << 8) + c2;
+}
+
+#endif // HAVE_JPEGLIB
+
+GString *DCTStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 2) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ if (okToReadStream && !checkSequentialInterleaved()) {
+ // PostScript does not allow progressive or interleaved JPEG
+ delete s;
+ return NULL;
+ }
+ s->append(indent)->append("<< >> /DCTDecode filter\n");
+ return s;
+}
+
+GBool DCTStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// FlateStream
+//------------------------------------------------------------------------
+
+int FlateStream::codeLenCodeMap[flateMaxCodeLenCodes] = {
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+FlateDecode FlateStream::lengthDecode[flateMaxLitCodes-257] = {
+ {0, 3},
+ {0, 4},
+ {0, 5},
+ {0, 6},
+ {0, 7},
+ {0, 8},
+ {0, 9},
+ {0, 10},
+ {1, 11},
+ {1, 13},
+ {1, 15},
+ {1, 17},
+ {2, 19},
+ {2, 23},
+ {2, 27},
+ {2, 31},
+ {3, 35},
+ {3, 43},
+ {3, 51},
+ {3, 59},
+ {4, 67},
+ {4, 83},
+ {4, 99},
+ {4, 115},
+ {5, 131},
+ {5, 163},
+ {5, 195},
+ {5, 227},
+ {0, 258},
+ {0, 258},
+ {0, 258}
+};
+
+FlateDecode FlateStream::distDecode[flateMaxDistCodes] = {
+ { 0, 1},
+ { 0, 2},
+ { 0, 3},
+ { 0, 4},
+ { 1, 5},
+ { 1, 7},
+ { 2, 9},
+ { 2, 13},
+ { 3, 17},
+ { 3, 25},
+ { 4, 33},
+ { 4, 49},
+ { 5, 65},
+ { 5, 97},
+ { 6, 129},
+ { 6, 193},
+ { 7, 257},
+ { 7, 385},
+ { 8, 513},
+ { 8, 769},
+ { 9, 1025},
+ { 9, 1537},
+ {10, 2049},
+ {10, 3073},
+ {11, 4097},
+ {11, 6145},
+ {12, 8193},
+ {12, 12289},
+ {13, 16385},
+ {13, 24577}
+};
+
+static FlateCode flateFixedLitCodeTabCodes[512] = {
+ {7, 0x0100},
+ {8, 0x0050},
+ {8, 0x0010},
+ {8, 0x0118},
+ {7, 0x0110},
+ {8, 0x0070},
+ {8, 0x0030},
+ {9, 0x00c0},
+ {7, 0x0108},
+ {8, 0x0060},
+ {8, 0x0020},
+ {9, 0x00a0},
+ {8, 0x0000},
+ {8, 0x0080},
+ {8, 0x0040},
+ {9, 0x00e0},
+ {7, 0x0104},
+ {8, 0x0058},
+ {8, 0x0018},
+ {9, 0x0090},
+ {7, 0x0114},
+ {8, 0x0078},
+ {8, 0x0038},
+ {9, 0x00d0},
+ {7, 0x010c},
+ {8, 0x0068},
+ {8, 0x0028},
+ {9, 0x00b0},
+ {8, 0x0008},
+ {8, 0x0088},
+ {8, 0x0048},
+ {9, 0x00f0},
+ {7, 0x0102},
+ {8, 0x0054},
+ {8, 0x0014},
+ {8, 0x011c},
+ {7, 0x0112},
+ {8, 0x0074},
+ {8, 0x0034},
+ {9, 0x00c8},
+ {7, 0x010a},
+ {8, 0x0064},
+ {8, 0x0024},
+ {9, 0x00a8},
+ {8, 0x0004},
+ {8, 0x0084},
+ {8, 0x0044},
+ {9, 0x00e8},
+ {7, 0x0106},
+ {8, 0x005c},
+ {8, 0x001c},
+ {9, 0x0098},
+ {7, 0x0116},
+ {8, 0x007c},
+ {8, 0x003c},
+ {9, 0x00d8},
+ {7, 0x010e},
+ {8, 0x006c},
+ {8, 0x002c},
+ {9, 0x00b8},
+ {8, 0x000c},
+ {8, 0x008c},
+ {8, 0x004c},
+ {9, 0x00f8},
+ {7, 0x0101},
+ {8, 0x0052},
+ {8, 0x0012},
+ {8, 0x011a},
+ {7, 0x0111},
+ {8, 0x0072},
+ {8, 0x0032},
+ {9, 0x00c4},
+ {7, 0x0109},
+ {8, 0x0062},
+ {8, 0x0022},
+ {9, 0x00a4},
+ {8, 0x0002},
+ {8, 0x0082},
+ {8, 0x0042},
+ {9, 0x00e4},
+ {7, 0x0105},
+ {8, 0x005a},
+ {8, 0x001a},
+ {9, 0x0094},
+ {7, 0x0115},
+ {8, 0x007a},
+ {8, 0x003a},
+ {9, 0x00d4},
+ {7, 0x010d},
+ {8, 0x006a},
+ {8, 0x002a},
+ {9, 0x00b4},
+ {8, 0x000a},
+ {8, 0x008a},
+ {8, 0x004a},
+ {9, 0x00f4},
+ {7, 0x0103},
+ {8, 0x0056},
+ {8, 0x0016},
+ {8, 0x011e},
+ {7, 0x0113},
+ {8, 0x0076},
+ {8, 0x0036},
+ {9, 0x00cc},
+ {7, 0x010b},
+ {8, 0x0066},
+ {8, 0x0026},
+ {9, 0x00ac},
+ {8, 0x0006},
+ {8, 0x0086},
+ {8, 0x0046},
+ {9, 0x00ec},
+ {7, 0x0107},
+ {8, 0x005e},
+ {8, 0x001e},
+ {9, 0x009c},
+ {7, 0x0117},
+ {8, 0x007e},
+ {8, 0x003e},
+ {9, 0x00dc},
+ {7, 0x010f},
+ {8, 0x006e},
+ {8, 0x002e},
+ {9, 0x00bc},
+ {8, 0x000e},
+ {8, 0x008e},
+ {8, 0x004e},
+ {9, 0x00fc},
+ {7, 0x0100},
+ {8, 0x0051},
+ {8, 0x0011},
+ {8, 0x0119},
+ {7, 0x0110},
+ {8, 0x0071},
+ {8, 0x0031},
+ {9, 0x00c2},
+ {7, 0x0108},
+ {8, 0x0061},
+ {8, 0x0021},
+ {9, 0x00a2},
+ {8, 0x0001},
+ {8, 0x0081},
+ {8, 0x0041},
+ {9, 0x00e2},
+ {7, 0x0104},
+ {8, 0x0059},
+ {8, 0x0019},
+ {9, 0x0092},
+ {7, 0x0114},
+ {8, 0x0079},
+ {8, 0x0039},
+ {9, 0x00d2},
+ {7, 0x010c},
+ {8, 0x0069},
+ {8, 0x0029},
+ {9, 0x00b2},
+ {8, 0x0009},
+ {8, 0x0089},
+ {8, 0x0049},
+ {9, 0x00f2},
+ {7, 0x0102},
+ {8, 0x0055},
+ {8, 0x0015},
+ {8, 0x011d},
+ {7, 0x0112},
+ {8, 0x0075},
+ {8, 0x0035},
+ {9, 0x00ca},
+ {7, 0x010a},
+ {8, 0x0065},
+ {8, 0x0025},
+ {9, 0x00aa},
+ {8, 0x0005},
+ {8, 0x0085},
+ {8, 0x0045},
+ {9, 0x00ea},
+ {7, 0x0106},
+ {8, 0x005d},
+ {8, 0x001d},
+ {9, 0x009a},
+ {7, 0x0116},
+ {8, 0x007d},
+ {8, 0x003d},
+ {9, 0x00da},
+ {7, 0x010e},
+ {8, 0x006d},
+ {8, 0x002d},
+ {9, 0x00ba},
+ {8, 0x000d},
+ {8, 0x008d},
+ {8, 0x004d},
+ {9, 0x00fa},
+ {7, 0x0101},
+ {8, 0x0053},
+ {8, 0x0013},
+ {8, 0x011b},
+ {7, 0x0111},
+ {8, 0x0073},
+ {8, 0x0033},
+ {9, 0x00c6},
+ {7, 0x0109},
+ {8, 0x0063},
+ {8, 0x0023},
+ {9, 0x00a6},
+ {8, 0x0003},
+ {8, 0x0083},
+ {8, 0x0043},
+ {9, 0x00e6},
+ {7, 0x0105},
+ {8, 0x005b},
+ {8, 0x001b},
+ {9, 0x0096},
+ {7, 0x0115},
+ {8, 0x007b},
+ {8, 0x003b},
+ {9, 0x00d6},
+ {7, 0x010d},
+ {8, 0x006b},
+ {8, 0x002b},
+ {9, 0x00b6},
+ {8, 0x000b},
+ {8, 0x008b},
+ {8, 0x004b},
+ {9, 0x00f6},
+ {7, 0x0103},
+ {8, 0x0057},
+ {8, 0x0017},
+ {8, 0x011f},
+ {7, 0x0113},
+ {8, 0x0077},
+ {8, 0x0037},
+ {9, 0x00ce},
+ {7, 0x010b},
+ {8, 0x0067},
+ {8, 0x0027},
+ {9, 0x00ae},
+ {8, 0x0007},
+ {8, 0x0087},
+ {8, 0x0047},
+ {9, 0x00ee},
+ {7, 0x0107},
+ {8, 0x005f},
+ {8, 0x001f},
+ {9, 0x009e},
+ {7, 0x0117},
+ {8, 0x007f},
+ {8, 0x003f},
+ {9, 0x00de},
+ {7, 0x010f},
+ {8, 0x006f},
+ {8, 0x002f},
+ {9, 0x00be},
+ {8, 0x000f},
+ {8, 0x008f},
+ {8, 0x004f},
+ {9, 0x00fe},
+ {7, 0x0100},
+ {8, 0x0050},
+ {8, 0x0010},
+ {8, 0x0118},
+ {7, 0x0110},
+ {8, 0x0070},
+ {8, 0x0030},
+ {9, 0x00c1},
+ {7, 0x0108},
+ {8, 0x0060},
+ {8, 0x0020},
+ {9, 0x00a1},
+ {8, 0x0000},
+ {8, 0x0080},
+ {8, 0x0040},
+ {9, 0x00e1},
+ {7, 0x0104},
+ {8, 0x0058},
+ {8, 0x0018},
+ {9, 0x0091},
+ {7, 0x0114},
+ {8, 0x0078},
+ {8, 0x0038},
+ {9, 0x00d1},
+ {7, 0x010c},
+ {8, 0x0068},
+ {8, 0x0028},
+ {9, 0x00b1},
+ {8, 0x0008},
+ {8, 0x0088},
+ {8, 0x0048},
+ {9, 0x00f1},
+ {7, 0x0102},
+ {8, 0x0054},
+ {8, 0x0014},
+ {8, 0x011c},
+ {7, 0x0112},
+ {8, 0x0074},
+ {8, 0x0034},
+ {9, 0x00c9},
+ {7, 0x010a},
+ {8, 0x0064},
+ {8, 0x0024},
+ {9, 0x00a9},
+ {8, 0x0004},
+ {8, 0x0084},
+ {8, 0x0044},
+ {9, 0x00e9},
+ {7, 0x0106},
+ {8, 0x005c},
+ {8, 0x001c},
+ {9, 0x0099},
+ {7, 0x0116},
+ {8, 0x007c},
+ {8, 0x003c},
+ {9, 0x00d9},
+ {7, 0x010e},
+ {8, 0x006c},
+ {8, 0x002c},
+ {9, 0x00b9},
+ {8, 0x000c},
+ {8, 0x008c},
+ {8, 0x004c},
+ {9, 0x00f9},
+ {7, 0x0101},
+ {8, 0x0052},
+ {8, 0x0012},
+ {8, 0x011a},
+ {7, 0x0111},
+ {8, 0x0072},
+ {8, 0x0032},
+ {9, 0x00c5},
+ {7, 0x0109},
+ {8, 0x0062},
+ {8, 0x0022},
+ {9, 0x00a5},
+ {8, 0x0002},
+ {8, 0x0082},
+ {8, 0x0042},
+ {9, 0x00e5},
+ {7, 0x0105},
+ {8, 0x005a},
+ {8, 0x001a},
+ {9, 0x0095},
+ {7, 0x0115},
+ {8, 0x007a},
+ {8, 0x003a},
+ {9, 0x00d5},
+ {7, 0x010d},
+ {8, 0x006a},
+ {8, 0x002a},
+ {9, 0x00b5},
+ {8, 0x000a},
+ {8, 0x008a},
+ {8, 0x004a},
+ {9, 0x00f5},
+ {7, 0x0103},
+ {8, 0x0056},
+ {8, 0x0016},
+ {8, 0x011e},
+ {7, 0x0113},
+ {8, 0x0076},
+ {8, 0x0036},
+ {9, 0x00cd},
+ {7, 0x010b},
+ {8, 0x0066},
+ {8, 0x0026},
+ {9, 0x00ad},
+ {8, 0x0006},
+ {8, 0x0086},
+ {8, 0x0046},
+ {9, 0x00ed},
+ {7, 0x0107},
+ {8, 0x005e},
+ {8, 0x001e},
+ {9, 0x009d},
+ {7, 0x0117},
+ {8, 0x007e},
+ {8, 0x003e},
+ {9, 0x00dd},
+ {7, 0x010f},
+ {8, 0x006e},
+ {8, 0x002e},
+ {9, 0x00bd},
+ {8, 0x000e},
+ {8, 0x008e},
+ {8, 0x004e},
+ {9, 0x00fd},
+ {7, 0x0100},
+ {8, 0x0051},
+ {8, 0x0011},
+ {8, 0x0119},
+ {7, 0x0110},
+ {8, 0x0071},
+ {8, 0x0031},
+ {9, 0x00c3},
+ {7, 0x0108},
+ {8, 0x0061},
+ {8, 0x0021},
+ {9, 0x00a3},
+ {8, 0x0001},
+ {8, 0x0081},
+ {8, 0x0041},
+ {9, 0x00e3},
+ {7, 0x0104},
+ {8, 0x0059},
+ {8, 0x0019},
+ {9, 0x0093},
+ {7, 0x0114},
+ {8, 0x0079},
+ {8, 0x0039},
+ {9, 0x00d3},
+ {7, 0x010c},
+ {8, 0x0069},
+ {8, 0x0029},
+ {9, 0x00b3},
+ {8, 0x0009},
+ {8, 0x0089},
+ {8, 0x0049},
+ {9, 0x00f3},
+ {7, 0x0102},
+ {8, 0x0055},
+ {8, 0x0015},
+ {8, 0x011d},
+ {7, 0x0112},
+ {8, 0x0075},
+ {8, 0x0035},
+ {9, 0x00cb},
+ {7, 0x010a},
+ {8, 0x0065},
+ {8, 0x0025},
+ {9, 0x00ab},
+ {8, 0x0005},
+ {8, 0x0085},
+ {8, 0x0045},
+ {9, 0x00eb},
+ {7, 0x0106},
+ {8, 0x005d},
+ {8, 0x001d},
+ {9, 0x009b},
+ {7, 0x0116},
+ {8, 0x007d},
+ {8, 0x003d},
+ {9, 0x00db},
+ {7, 0x010e},
+ {8, 0x006d},
+ {8, 0x002d},
+ {9, 0x00bb},
+ {8, 0x000d},
+ {8, 0x008d},
+ {8, 0x004d},
+ {9, 0x00fb},
+ {7, 0x0101},
+ {8, 0x0053},
+ {8, 0x0013},
+ {8, 0x011b},
+ {7, 0x0111},
+ {8, 0x0073},
+ {8, 0x0033},
+ {9, 0x00c7},
+ {7, 0x0109},
+ {8, 0x0063},
+ {8, 0x0023},
+ {9, 0x00a7},
+ {8, 0x0003},
+ {8, 0x0083},
+ {8, 0x0043},
+ {9, 0x00e7},
+ {7, 0x0105},
+ {8, 0x005b},
+ {8, 0x001b},
+ {9, 0x0097},
+ {7, 0x0115},
+ {8, 0x007b},
+ {8, 0x003b},
+ {9, 0x00d7},
+ {7, 0x010d},
+ {8, 0x006b},
+ {8, 0x002b},
+ {9, 0x00b7},
+ {8, 0x000b},
+ {8, 0x008b},
+ {8, 0x004b},
+ {9, 0x00f7},
+ {7, 0x0103},
+ {8, 0x0057},
+ {8, 0x0017},
+ {8, 0x011f},
+ {7, 0x0113},
+ {8, 0x0077},
+ {8, 0x0037},
+ {9, 0x00cf},
+ {7, 0x010b},
+ {8, 0x0067},
+ {8, 0x0027},
+ {9, 0x00af},
+ {8, 0x0007},
+ {8, 0x0087},
+ {8, 0x0047},
+ {9, 0x00ef},
+ {7, 0x0107},
+ {8, 0x005f},
+ {8, 0x001f},
+ {9, 0x009f},
+ {7, 0x0117},
+ {8, 0x007f},
+ {8, 0x003f},
+ {9, 0x00df},
+ {7, 0x010f},
+ {8, 0x006f},
+ {8, 0x002f},
+ {9, 0x00bf},
+ {8, 0x000f},
+ {8, 0x008f},
+ {8, 0x004f},
+ {9, 0x00ff}
+};
+
+FlateHuffmanTab FlateStream::fixedLitCodeTab = {
+ flateFixedLitCodeTabCodes, 9
+};
+
+static FlateCode flateFixedDistCodeTabCodes[32] = {
+ {5, 0x0000},
+ {5, 0x0010},
+ {5, 0x0008},
+ {5, 0x0018},
+ {5, 0x0004},
+ {5, 0x0014},
+ {5, 0x000c},
+ {5, 0x001c},
+ {5, 0x0002},
+ {5, 0x0012},
+ {5, 0x000a},
+ {5, 0x001a},
+ {5, 0x0006},
+ {5, 0x0016},
+ {5, 0x000e},
+ {0, 0x0000},
+ {5, 0x0001},
+ {5, 0x0011},
+ {5, 0x0009},
+ {5, 0x0019},
+ {5, 0x0005},
+ {5, 0x0015},
+ {5, 0x000d},
+ {5, 0x001d},
+ {5, 0x0003},
+ {5, 0x0013},
+ {5, 0x000b},
+ {5, 0x001b},
+ {5, 0x0007},
+ {5, 0x0017},
+ {5, 0x000f},
+ {0, 0x0000}
+};
+
+FlateHuffmanTab FlateStream::fixedDistCodeTab = {
+ flateFixedDistCodeTabCodes, 5
+};
+
+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);
+ if (!pred->isOk()) {
+ delete pred;
+ pred = NULL;
+ }
+ } else {
+ pred = NULL;
+ }
+ litCodeTab.codes = NULL;
+ distCodeTab.codes = NULL;
+ memset(buf, 0, flateWindow);
+ checkForDecompressionBombs = gTrue;
+}
+
+FlateStream::~FlateStream() {
+ if (litCodeTab.codes != fixedLitCodeTab.codes) {
+ gfree(litCodeTab.codes);
+ }
+ if (distCodeTab.codes != fixedDistCodeTab.codes) {
+ gfree(distCodeTab.codes);
+ }
+ if (pred) {
+ delete pred;
+ }
+ delete str;
+}
+
+Stream *FlateStream::copy() {
+ if (pred) {
+ return new FlateStream(str->copy(), pred->getPredictor(),
+ pred->getWidth(), pred->getNComps(),
+ pred->getNBits());
+ } else {
+ return new FlateStream(str->copy(), 1, 0, 0, 0);
+ }
+}
+
+void FlateStream::disableDecompressionBombChecking() {
+ checkForDecompressionBombs = gFalse;
+ FilterStream::disableDecompressionBombChecking();
+}
+
+void FlateStream::reset() {
+ int cmf, flg;
+
+ index = 0;
+ remain = 0;
+ codeBuf = 0;
+ codeSize = 0;
+ compressedBlock = gFalse;
+ endOfBlock = gTrue;
+ eof = gTrue;
+
+ str->reset();
+ if (pred) {
+ pred->reset();
+ }
+
+ // read header
+ //~ need to look at window size?
+ endOfBlock = eof = gTrue;
+ cmf = str->getChar();
+ flg = str->getChar();
+ totalIn = 2;
+ totalOut = 0;
+ if (cmf == EOF || flg == EOF)
+ return;
+ if ((cmf & 0x0f) != 0x08) {
+ error(errSyntaxError, getPos(),
+ "Unknown compression method in flate stream");
+ return;
+ }
+ if ((((cmf << 8) + flg) % 31) != 0) {
+ error(errSyntaxError, getPos(), "Bad FCHECK in flate stream");
+ return;
+ }
+ if (flg & 0x20) {
+ error(errSyntaxError, getPos(), "FDICT bit set in flate stream");
+ return;
+ }
+
+ eof = gFalse;
+}
+
+int FlateStream::getChar() {
+ int c;
+
+ if (pred) {
+ return pred->getChar();
+ }
+ while (remain == 0) {
+ if (endOfBlock && eof)
+ return EOF;
+ readSome();
+ }
+ c = buf[index];
+ index = (index + 1) & flateMask;
+ --remain;
+ return c;
+}
+
+int FlateStream::lookChar() {
+ int c;
+
+ if (pred) {
+ return pred->lookChar();
+ }
+ while (remain == 0) {
+ if (endOfBlock && eof)
+ return EOF;
+ readSome();
+ }
+ c = buf[index];
+ return c;
+}
+
+int FlateStream::getRawChar() {
+ int c;
+
+ while (remain == 0) {
+ if (endOfBlock && eof)
+ return EOF;
+ readSome();
+ }
+ c = buf[index];
+ index = (index + 1) & flateMask;
+ --remain;
+ return c;
+}
+
+int FlateStream::getBlock(char *blk, int size) {
+ int n, k;
+
+ if (pred) {
+ return pred->getBlock(blk, size);
+ }
+
+ n = 0;
+ while (n < size) {
+ if (remain == 0) {
+ if (endOfBlock && eof) {
+ break;
+ }
+ readSome();
+ }
+ k = remain;
+ if (size - n < k) {
+ k = size - n;
+ }
+ if (flateWindow - index < k) {
+ k = flateWindow - index;
+ }
+ memcpy(blk + n, buf + index, k);
+ n += k;
+ index = (index + k) & flateMask;
+ remain -= k;
+ }
+ return n;
+}
+
+GString *FlateStream::getPSFilter(int psLevel, const char *indent,
+ GBool okToReadStream) {
+ GString *s;
+
+ if (psLevel < 3 || pred) {
+ return NULL;
+ }
+ if (!(s = str->getPSFilter(psLevel, indent, okToReadStream))) {
+ return NULL;
+ }
+ s->append(indent)->append("<< >> /FlateDecode filter\n");
+ return s;
+}
+
+GBool FlateStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+void FlateStream::readSome() {
+ int code1, code2;
+ int len, dist;
+ int src, dest, n1, n2, n3, i, j, k;
+ int c;
+
+ if (endOfBlock) {
+ if (!startBlock())
+ return;
+ }
+
+ if (compressedBlock) {
+ if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF)
+ goto err;
+ if (code1 < 256) {
+ buf[index] = (Guchar)code1;
+ remain = 1;
+ } else if (code1 == 256) {
+ endOfBlock = gTrue;
+ remain = 0;
+ } else {
+ code1 -= 257;
+ code2 = lengthDecode[code1].bits;
+ if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF)
+ goto err;
+ len = lengthDecode[code1].first + code2;
+ if ((code1 = getHuffmanCodeWord(&distCodeTab)) == EOF)
+ goto err;
+ code2 = distDecode[code1].bits;
+ if (code2 > 0 && (code2 = getCodeWord(code2)) == EOF)
+ goto err;
+ dist = distDecode[code1].first + code2;
+ dest = index;
+ src = (index - dist) & flateMask;
+ // the following is an optimized version of:
+ // for (k = 0; k < len; ++k) {
+ // buf[dest] = buf[src];
+ // dest = (dest + 1) & flateMask;
+ // src = (src + 1) & flateMask;
+ // }
+ if (dest + len <= flateWindow) {
+ if (src + len <= flateWindow) {
+ for (k = 0; k < len; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ } else {
+ n1 = flateWindow - src;
+ n2 = len - n1;
+ for (k = 0; k < n1; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = dest + n1;
+ src = 0;
+ for (k = 0; k < n2; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ }
+ } else {
+ if (src + len <= flateWindow) {
+ n1 = flateWindow - dest;
+ n2 = len - n1;
+ for (k = 0; k < n1; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = 0;
+ src = src + n1;
+ for (k = 0; k < n2; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ } else if (src < dest) {
+ n1 = flateWindow - dest;
+ n2 = dest - src;
+ n3 = len - n1 - n2;
+ for (k = 0; k < n1; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = 0;
+ src = src + n1;
+ for (k = 0; k < n2; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = n2;
+ src = 0;
+ for (k = 0; k < n3; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ } else {
+ n1 = flateWindow - src;
+ n2 = src - dest;
+ n3 = len - n1 - n2;
+ for (k = 0; k < n1; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = dest + n1;
+ src = 0;
+ for (k = 0; k < n2; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ dest = 0;
+ src = n2;
+ for (k = 0; k < n3; ++k) {
+ buf[dest + k] = buf[src + k];
+ }
+ }
+ }
+ remain = len;
+ }
+
+ } else {
+ len = (blockLen < flateWindow) ? blockLen : flateWindow;
+ for (i = 0, j = index; i < len; ++i, j = (j + 1) & flateMask) {
+ if ((c = str->getChar()) == EOF) {
+ endOfBlock = eof = gTrue;
+ break;
+ }
+ buf[j] = (Guchar)c;
+ }
+ remain = i;
+ blockLen -= len;
+ if (blockLen == 0)
+ endOfBlock = gTrue;
+ totalIn += remain;
+ }
+ totalOut += remain;
+
+ // check for a 'decompression bomb'
+ if (checkForDecompressionBombs &&
+ totalOut > decompressionBombSizeThreshold &&
+ totalIn < totalOut / decompressionBombRatioThreshold) {
+ error(errSyntaxError, getPos(), "Decompression bomb in flate stream");
+ endOfBlock = eof = gTrue;
+ remain = 0;
+ }
+
+ return;
+
+err:
+ error(errSyntaxError, getPos(), "Unexpected end of file in flate stream");
+ endOfBlock = eof = gTrue;
+ remain = 0;
+}
+
+GBool FlateStream::startBlock() {
+ int blockHdr;
+ int c;
+ int check;
+
+ // free the code tables from the previous block
+ if (litCodeTab.codes != fixedLitCodeTab.codes) {
+ gfree(litCodeTab.codes);
+ }
+ litCodeTab.codes = NULL;
+ if (distCodeTab.codes != fixedDistCodeTab.codes) {
+ gfree(distCodeTab.codes);
+ }
+ distCodeTab.codes = NULL;
+
+ // read block header
+ blockHdr = getCodeWord(3);
+ if (blockHdr & 1)
+ eof = gTrue;
+ blockHdr >>= 1;
+
+ // uncompressed block
+ if (blockHdr == 0) {
+ compressedBlock = gFalse;
+ if ((c = str->getChar()) == EOF)
+ goto err;
+ blockLen = c & 0xff;
+ if ((c = str->getChar()) == EOF)
+ goto err;
+ blockLen |= (c & 0xff) << 8;
+ if ((c = str->getChar()) == EOF)
+ goto err;
+ check = c & 0xff;
+ if ((c = str->getChar()) == EOF)
+ goto err;
+ check |= (c & 0xff) << 8;
+ if (check != (~blockLen & 0xffff))
+ goto err;
+ codeBuf = 0;
+ codeSize = 0;
+ totalIn += 4;
+
+ // compressed block with fixed codes
+ } else if (blockHdr == 1) {
+ compressedBlock = gTrue;
+ loadFixedCodes();
+
+ // compressed block with dynamic codes
+ } else if (blockHdr == 2) {
+ compressedBlock = gTrue;
+ if (!readDynamicCodes()) {
+ goto err;
+ }
+
+ // unknown block type
+ } else {
+ goto err;
+ }
+
+ endOfBlock = gFalse;
+ return gTrue;
+
+err:
+ error(errSyntaxError, getPos(), "Bad block header in flate stream");
+ endOfBlock = eof = gTrue;
+ return gFalse;
+}
+
+void FlateStream::loadFixedCodes() {
+ litCodeTab.codes = fixedLitCodeTab.codes;
+ litCodeTab.maxLen = fixedLitCodeTab.maxLen;
+ distCodeTab.codes = fixedDistCodeTab.codes;
+ distCodeTab.maxLen = fixedDistCodeTab.maxLen;
+}
+
+GBool FlateStream::readDynamicCodes() {
+ int numCodeLenCodes;
+ int numLitCodes;
+ int numDistCodes;
+ int codeLenCodeLengths[flateMaxCodeLenCodes];
+ FlateHuffmanTab codeLenCodeTab;
+ int len, repeat, code;
+ int i;
+
+ codeLenCodeTab.codes = NULL;
+
+ // read lengths
+ if ((numLitCodes = getCodeWord(5)) == EOF) {
+ goto err;
+ }
+ numLitCodes += 257;
+ if ((numDistCodes = getCodeWord(5)) == EOF) {
+ goto err;
+ }
+ numDistCodes += 1;
+ if ((numCodeLenCodes = getCodeWord(4)) == EOF) {
+ goto err;
+ }
+ numCodeLenCodes += 4;
+ if (numLitCodes > flateMaxLitCodes ||
+ numDistCodes > flateMaxDistCodes ||
+ numCodeLenCodes > flateMaxCodeLenCodes) {
+ goto err;
+ }
+
+ // build the code length code table
+ for (i = 0; i < flateMaxCodeLenCodes; ++i) {
+ codeLenCodeLengths[i] = 0;
+ }
+ for (i = 0; i < numCodeLenCodes; ++i) {
+ if ((codeLenCodeLengths[codeLenCodeMap[i]] = getCodeWord(3)) == -1) {
+ goto err;
+ }
+ }
+ compHuffmanCodes(codeLenCodeLengths, flateMaxCodeLenCodes, &codeLenCodeTab);
+
+ // build the literal and distance code tables
+ len = 0;
+ repeat = 0;
+ i = 0;
+ while (i < numLitCodes + numDistCodes) {
+ if ((code = getHuffmanCodeWord(&codeLenCodeTab)) == EOF) {
+ goto err;
+ }
+ if (code == 16) {
+ if ((repeat = getCodeWord(2)) == EOF) {
+ goto err;
+ }
+ repeat += 3;
+ if (i + repeat > numLitCodes + numDistCodes) {
+ goto err;
+ }
+ for (; repeat > 0; --repeat) {
+ codeLengths[i++] = len;
+ }
+ } else if (code == 17) {
+ if ((repeat = getCodeWord(3)) == EOF) {
+ goto err;
+ }
+ repeat += 3;
+ if (i + repeat > numLitCodes + numDistCodes) {
+ goto err;
+ }
+ len = 0;
+ for (; repeat > 0; --repeat) {
+ codeLengths[i++] = 0;
+ }
+ } else if (code == 18) {
+ if ((repeat = getCodeWord(7)) == EOF) {
+ goto err;
+ }
+ repeat += 11;
+ if (i + repeat > numLitCodes + numDistCodes) {
+ goto err;
+ }
+ len = 0;
+ for (; repeat > 0; --repeat) {
+ codeLengths[i++] = 0;
+ }
+ } else {
+ codeLengths[i++] = len = code;
+ }
+ }
+ compHuffmanCodes(codeLengths, numLitCodes, &litCodeTab);
+ compHuffmanCodes(codeLengths + numLitCodes, numDistCodes, &distCodeTab);
+
+ gfree(codeLenCodeTab.codes);
+ return gTrue;
+
+err:
+ error(errSyntaxError, getPos(), "Bad dynamic code table in flate stream");
+ gfree(codeLenCodeTab.codes);
+ return gFalse;
+}
+
+// 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;
+
+ // find max code length
+ tab->maxLen = 0;
+ for (val = 0; val < n; ++val) {
+ if (lengths[val] > tab->maxLen) {
+ tab->maxLen = lengths[val];
+ }
+ }
+
+ // allocate the table
+ tabSize = 1 << tab->maxLen;
+ tab->codes = (FlateCode *)gmallocn(tabSize, sizeof(FlateCode));
+
+ // clear the table
+ for (i = 0; i < tabSize; ++i) {
+ tab->codes[i].len = 0;
+ tab->codes[i].val = 0;
+ }
+
+ // 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) {
+ FlateCode *code;
+ int c;
+
+ while (codeSize < tab->maxLen) {
+ if ((c = str->getChar()) == EOF) {
+ break;
+ }
+ codeBuf |= (c & 0xff) << codeSize;
+ codeSize += 8;
+ ++totalIn;
+ }
+ 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) {
+ int c;
+
+ while (codeSize < bits) {
+ if ((c = str->getChar()) == EOF)
+ return EOF;
+ codeBuf |= (c & 0xff) << codeSize;
+ codeSize += 8;
+ ++totalIn;
+ }
+ c = codeBuf & ((1 << bits) - 1);
+ codeBuf >>= bits;
+ codeSize -= bits;
+ return c;
+}
+
+//------------------------------------------------------------------------
+// EOFStream
+//------------------------------------------------------------------------
+
+EOFStream::EOFStream(Stream *strA):
+ FilterStream(strA) {
+}
+
+EOFStream::~EOFStream() {
+ delete str;
+}
+
+Stream *EOFStream::copy() {
+ return new EOFStream(str->copy());
+}
+
+//------------------------------------------------------------------------
+// BufStream
+//------------------------------------------------------------------------
+
+BufStream::BufStream(Stream *strA, int bufSizeA): FilterStream(strA) {
+ bufSize = bufSizeA;
+ buf = (int *)gmallocn(bufSize, sizeof(int));
+}
+
+BufStream::~BufStream() {
+ gfree(buf);
+ delete str;
+}
+
+Stream *BufStream::copy() {
+ return new BufStream(str->copy(), bufSize);
+}
+
+void BufStream::reset() {
+ int i;
+
+ str->reset();
+ for (i = 0; i < bufSize; ++i) {
+ buf[i] = str->getChar();
+ }
+}
+
+int BufStream::getChar() {
+ int c, i;
+
+ c = buf[0];
+ for (i = 1; i < bufSize; ++i) {
+ buf[i-1] = buf[i];
+ }
+ buf[bufSize - 1] = str->getChar();
+ return c;
+}
+
+int BufStream::lookChar() {
+ return buf[0];
+}
+
+int BufStream::lookChar(int idx) {
+ return buf[idx];
+}
+
+GBool BufStream::isBinary(GBool last) {
+ return str->isBinary(gTrue);
+}
+
+//------------------------------------------------------------------------
+// FixedLengthEncoder
+//------------------------------------------------------------------------
+
+FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA):
+ FilterStream(strA) {
+ length = lengthA;
+ count = 0;
+}
+
+FixedLengthEncoder::~FixedLengthEncoder() {
+ if (str->isEncoder())
+ delete str;
+}
+
+Stream *FixedLengthEncoder::copy() {
+ error(errInternal, -1, "Called copy() on FixedLengthEncoder");
+ return NULL;
+}
+
+void FixedLengthEncoder::reset() {
+ str->reset();
+ count = 0;
+}
+
+int FixedLengthEncoder::getChar() {
+ if (length >= 0 && count >= length)
+ return EOF;
+ ++count;
+ return str->getChar();
+}
+
+int FixedLengthEncoder::lookChar() {
+ if (length >= 0 && count >= length)
+ return EOF;
+ 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;
+ }
+}
+
+Stream *ASCIIHexEncoder::copy() {
+ error(errInternal, -1, "Called copy() on ASCIIHexEncoder");
+ return NULL;
+}
+
+void ASCIIHexEncoder::reset() {
+ str->reset();
+ bufPtr = bufEnd = buf;
+ lineLen = 0;
+ eof = gFalse;
+}
+
+GBool ASCIIHexEncoder::fillBuf() {
+ static const 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 *strA):
+ FilterStream(strA) {
+ bufPtr = bufEnd = buf;
+ lineLen = 0;
+ eof = gFalse;
+}
+
+ASCII85Encoder::~ASCII85Encoder() {
+ if (str->isEncoder())
+ delete str;
+}
+
+Stream *ASCII85Encoder::copy() {
+ error(errInternal, -1, "Called copy() on ASCII85Encoder");
+ return NULL;
+}
+
+void ASCII85Encoder::reset() {
+ str->reset();
+ bufPtr = bufEnd = buf;
+ lineLen = 0;
+ eof = gFalse;
+}
+
+GBool ASCII85Encoder::fillBuf() {
+ Guint t;
+ char buf1[5];
+ int c0, c1, c2, c3;
+ int n, i;
+
+ if (eof) {
+ return gFalse;
+ }
+ c0 = str->getChar();
+ c1 = str->getChar();
+ c2 = str->getChar();
+ c3 = str->getChar();
+ bufPtr = bufEnd = buf;
+ if (c3 == EOF) {
+ if (c0 == EOF) {
+ n = 0;
+ t = 0;
+ } else {
+ if (c1 == EOF) {
+ n = 1;
+ t = c0 << 24;
+ } else if (c2 == EOF) {
+ n = 2;
+ t = (c0 << 24) | (c1 << 16);
+ } else {
+ n = 3;
+ t = (c0 << 24) | (c1 << 16) | (c2 << 8);
+ }
+ for (i = 4; i >= 0; --i) {
+ buf1[i] = (char)(t % 85 + 0x21);
+ t /= 85;
+ }
+ for (i = 0; i <= n; ++i) {
+ *bufEnd++ = buf1[i];
+ if (++lineLen == 65) {
+ *bufEnd++ = '\n';
+ lineLen = 0;
+ }
+ }
+ }
+ *bufEnd++ = '~';
+ *bufEnd++ = '>';
+ eof = gTrue;
+ } else {
+ t = (c0 << 24) | (c1 << 16) | (c2 << 8) | c3;
+ if (t == 0) {
+ *bufEnd++ = 'z';
+ if (++lineLen == 65) {
+ *bufEnd++ = '\n';
+ lineLen = 0;
+ }
+ } else {
+ for (i = 4; i >= 0; --i) {
+ buf1[i] = (char)(t % 85 + 0x21);
+ t /= 85;
+ }
+ for (i = 0; i <= 4; ++i) {
+ *bufEnd++ = buf1[i];
+ if (++lineLen == 65) {
+ *bufEnd++ = '\n';
+ lineLen = 0;
+ }
+ }
+ }
+ }
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// RunLengthEncoder
+//------------------------------------------------------------------------
+
+RunLengthEncoder::RunLengthEncoder(Stream *strA):
+ FilterStream(strA) {
+ bufPtr = bufEnd = nextEnd = buf;
+ eof = gFalse;
+}
+
+RunLengthEncoder::~RunLengthEncoder() {
+ if (str->isEncoder())
+ delete str;
+}
+
+Stream *RunLengthEncoder::copy() {
+ error(errInternal, -1, "Called copy() on RunLengthEncoder");
+ return NULL;
+}
+
+void RunLengthEncoder::reset() {
+ str->reset();
+ bufPtr = bufEnd = nextEnd = buf;
+ eof = gFalse;
+}
+
+//
+// When fillBuf finishes, buf[] looks like this:
+// +-----+--------------+-----------------+--
+// + tag | ... data ... | next 0, 1, or 2 |
+// +-----+--------------+-----------------+--
+// ^ ^ ^
+// bufPtr bufEnd nextEnd
+//
+GBool RunLengthEncoder::fillBuf() {
+ int c, c1, c2;
+ int n;
+
+ // already hit EOF?
+ if (eof)
+ return gFalse;
+
+ // grab two bytes
+ if (nextEnd < bufEnd + 1) {
+ if ((c1 = str->getChar()) == EOF) {
+ eof = gTrue;
+ return gFalse;
+ }
+ } else {
+ c1 = bufEnd[0] & 0xff;
+ }
+ if (nextEnd < bufEnd + 2) {
+ if ((c2 = str->getChar()) == EOF) {
+ eof = gTrue;
+ buf[0] = 0;
+ buf[1] = (char)c1;
+ bufPtr = buf;
+ bufEnd = &buf[2];
+ return gTrue;
+ }
+ } else {
+ c2 = bufEnd[1] & 0xff;
+ }
+
+ // check for repeat
+ c = 0; // make gcc happy
+ if (c1 == c2) {
+ n = 2;
+ while (n < 128 && (c = str->getChar()) == c1)
+ ++n;
+ buf[0] = (char)(257 - n);
+ buf[1] = (char)c1;
+ bufEnd = &buf[2];
+ if (c == EOF) {
+ eof = gTrue;
+ } else if (n < 128) {
+ buf[2] = (char)c;
+ nextEnd = &buf[3];
+ } else {
+ nextEnd = bufEnd;
+ }
+
+ // get up to 128 chars
+ } else {
+ buf[1] = (char)c1;
+ buf[2] = (char)c2;
+ n = 2;
+ while (n < 128) {
+ if ((c = str->getChar()) == EOF) {
+ eof = gTrue;
+ break;
+ }
+ ++n;
+ buf[n] = (char)c;
+ if (buf[n] == buf[n-1])
+ break;
+ }
+ if (buf[n] == buf[n-1]) {
+ buf[0] = (char)(n-2-1);
+ bufEnd = &buf[n-1];
+ nextEnd = &buf[n+1];
+ } else {
+ buf[0] = (char)(n-1);
+ bufEnd = nextEnd = &buf[n+1];
+ }
+ }
+ bufPtr = buf;
+ return gTrue;
+}
+
+//------------------------------------------------------------------------
+// LZWEncoder
+//------------------------------------------------------------------------
+
+LZWEncoder::LZWEncoder(Stream *strA):
+ FilterStream(strA)
+{
+ inBufStart = 0;
+ inBufLen = 0;
+ outBufLen = 0;
+}
+
+LZWEncoder::~LZWEncoder() {
+ if (str->isEncoder()) {
+ delete str;
+ }
+}
+
+Stream *LZWEncoder::copy() {
+ error(errInternal, -1, "Called copy() on LZWEncoder");
+ return NULL;
+}
+
+void LZWEncoder::reset() {
+ int i;
+
+ str->reset();
+
+ // initialize code table
+ for (i = 0; i < 256; ++i) {
+ table[i].byte = i;
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+
+ // initialize input buffer
+ inBufLen = str->getBlock((char *)inBuf, sizeof(inBuf));
+ inBufStart = 0;
+
+ // initialize output buffer with a clear-table code
+ outBuf = 256;
+ outBufLen = 9;
+ needEOD = gFalse;
+}
+
+int LZWEncoder::getChar() {
+ int ret;
+
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ ret = (outBuf >> (outBufLen - 8)) & 0xff;
+ outBufLen -= 8;
+ } else {
+ ret = (outBuf << (8 - outBufLen)) & 0xff;
+ outBufLen = 0;
+ }
+ return ret;
+}
+
+int LZWEncoder::lookChar() {
+ if (inBufLen == 0 && !needEOD && outBufLen == 0) {
+ return EOF;
+ }
+ if (outBufLen < 8 && (inBufLen > 0 || needEOD)) {
+ fillBuf();
+ }
+ if (outBufLen >= 8) {
+ return (outBuf >> (outBufLen - 8)) & 0xff;
+ } else {
+ return (outBuf << (8 - outBufLen)) & 0xff;
+ }
+}
+
+// On input, outBufLen < 8.
+// This function generates, at most, 2 12-bit codes
+// --> outBufLen < 8 + 12 + 12 = 32
+void LZWEncoder::fillBuf() {
+ LZWEncoderNode *p0, *p1;
+ int seqLen, code, i;
+
+ if (needEOD) {
+ outBuf = (outBuf << codeLen) | 257;
+ outBufLen += codeLen;
+ needEOD = gFalse;
+ return;
+ }
+
+ // find longest matching sequence (if any)
+ p0 = table + inBuf[inBufStart];
+ seqLen = 1;
+ while (inBufLen > seqLen) {
+ for (p1 = p0->children; p1; p1 = p1->next) {
+ if (p1->byte == inBuf[inBufStart + seqLen]) {
+ break;
+ }
+ }
+ if (!p1) {
+ break;
+ }
+ p0 = p1;
+ ++seqLen;
+ }
+ code = (int)(p0 - table);
+
+ // generate an output code
+ outBuf = (outBuf << codeLen) | code;
+ outBufLen += codeLen;
+
+ // update the table
+ table[nextSeq].byte = seqLen < inBufLen ? inBuf[inBufStart + seqLen] : 0;
+ table[nextSeq].children = NULL;
+ if (table[code].children) {
+ table[nextSeq].next = table[code].children;
+ } else {
+ table[nextSeq].next = NULL;
+ }
+ table[code].children = table + nextSeq;
+ ++nextSeq;
+
+ // update the input buffer
+ inBufStart += seqLen;
+ inBufLen -= seqLen;
+ if (inBufStart >= 4096 && inBufStart + inBufLen == sizeof(inBuf)) {
+ memcpy(inBuf, inBuf + inBufStart, inBufLen);
+ inBufStart = 0;
+ inBufLen += str->getBlock((char *)inBuf + inBufLen,
+ (int)sizeof(inBuf) - inBufLen);
+ }
+
+ // increment codeLen; generate clear-table code
+ if (nextSeq == (1 << codeLen)) {
+ ++codeLen;
+ if (codeLen == 13) {
+ outBuf = (outBuf << 12) | 256;
+ outBufLen += 12;
+ for (i = 0; i < 256; ++i) {
+ table[i].next = NULL;
+ table[i].children = NULL;
+ }
+ nextSeq = 258;
+ codeLen = 9;
+ }
+ }
+
+ // generate EOD next time
+ if (inBufLen == 0) {
+ needEOD = gTrue;
+ }
+}