From 90108002e586d7d92f4074ba7dbf1fc1db66f3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sat, 2 May 2020 11:48:15 +0200 Subject: [PATCH] Add bug hunting test case for CVE-2019-10025 --- test/bug-hunting/cve/CVE-2019-10025/Stream.cc | 5849 +++++++++++++++++ test/bug-hunting/cve/CVE-2019-10025/Stream.h | 1035 +++ 2 files changed, 6884 insertions(+) create mode 100644 test/bug-hunting/cve/CVE-2019-10025/Stream.cc create mode 100644 test/bug-hunting/cve/CVE-2019-10025/Stream.h diff --git a/test/bug-hunting/cve/CVE-2019-10025/Stream.cc b/test/bug-hunting/cve/CVE-2019-10025/Stream.cc new file mode 100644 index 000000000..d8fe24fff --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-10025/Stream.cc @@ -0,0 +1,5849 @@ +//======================================================================== +// +// Stream.cc +// +// Copyright 1996-2003 Glyph & Cog, LLC +// +//======================================================================== + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma implementation +#endif + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif +#include +#include +#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 + +//------------------------------------------------------------------------ +// 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) { + 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); + if (obj.isNull()) { + obj.free(); + dict->dictLookup("F", &obj); + } + dict->dictLookup("DecodeParms", ¶ms); + if (params.isNull()) { + params.free(); + dict->dictLookup("DP", ¶ms); + } + if (obj.isName()) { + str = makeFilter(obj.getName(), str, ¶ms, recursion); + } else if (obj.isArray()) { + for (i = 0; i < obj.arrayGetLength(); ++i) { + obj.arrayGet(i, &obj2, recursion); + if (params.isArray()) + params.arrayGet(i, ¶ms2, recursion); + else + params2.initNull(); + if (obj2.isName()) { + str = makeFilter(obj2.getName(), str, ¶ms2, 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 { + 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) { // cppcheck-suppress bughuntingDivByZero + // 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->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 { + 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) { + GString *s; + + if (psLevel < 2) { + return NULL; + } + if (!(s = str->getPSFilter(psLevel, indent))) { + return NULL; + } + s->append(indent)->append("/ASCIIHexDecode filter\n"); + return s; +} + +GBool ASCIIHexStream::isBinary(GBool last) { + return str->isBinary(gFalse); +} + +//------------------------------------------------------------------------ +// ASCII85Stream +//------------------------------------------------------------------------ + +ASCII85Stream::ASCII85Stream(Stream *strA): + FilterStream(strA) { + index = n = 0; + eof = gFalse; +} + +ASCII85Stream::~ASCII85Stream() { + delete str; +} + +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) { + GString *s; + + if (psLevel < 2) { + return NULL; + } + if (!(s = str->getPSFilter(psLevel, indent))) { + 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(); +} + +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); + } +} + +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(); +} + +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; + + // 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; + } + code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1); + inputBits -= nextBits; + return code; +} + +GString *LZWStream::getPSFilter(int psLevel, const char *indent) { + GString *s; + + if (psLevel < 2 || pred) { + return NULL; + } + if (!(s = str->getPSFilter(psLevel, indent))) { + 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) { + GString *s; + + if (psLevel < 2) { + return NULL; + } + if (!(s = str->getPSFilter(psLevel, indent))) { + 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) { + GString *s; + char s1[50]; + + if (psLevel < 2) { + return NULL; + } + if (!(s = str->getPSFilter(psLevel, indent))) { + return NULL; + } + s->append(indent)->append("<< "); + if (encoding != 0) { + sprintf(s1, "/K %d ", encoding); + s->append(s1); + } + if (endOfLine) { + s->append("/EndOfLine true "); + } + if (byteAlign) { + s->append("/EncodedByteAlign true "); + } + sprintf(s1, "/Columns %d ", columns); + s->append(s1); + if (rows != 0) { + sprintf(s1, "/Rows %d ", rows); + s->append(s1); + } + if (!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); +} + +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; + + 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; + 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; + } + } + + 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; + return; + } + for (i = 0; i < numComps; ++i) { + frameBuf[i] = (int *)gmallocn(bufWidth * bufHeight, sizeof(int)); + memset(frameBuf[i], 0, bufWidth * bufHeight * sizeof(int)); + } + + // 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; + 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(); + } +} + +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 (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 (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 (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::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; + 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; + } + 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; + } + } + + 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(); + if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || + compInfo[i].vSample < 1 || 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(); + if (compInfo[i].hSample < 1 || compInfo[i].hSample > 4 || + compInfo[i].vSample < 1 || 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; + } + interleaved = scanInfo.numComps == numComps; + 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) { + GString *s; + + if (psLevel < 2) { + return NULL; + } + if (!(s = str->getPSFilter(psLevel, indent))) { + 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); +} + +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::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(); + 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; + + if (pred) { + return pred->getBlock(blk, size); + } + + n = 0; + while (n < size) { + if (remain == 0) { + if (endOfBlock && eof) { + break; + } + readSome(); + } + while (remain && n < size) { + blk[n++] = buf[index]; + index = (index + 1) & flateMask; + --remain; + } + } + return n; +} + +GString *FlateStream::getPSFilter(int psLevel, const char *indent) { + GString *s; + + if (psLevel < 3 || pred) { + return NULL; + } + if (!(s = str->getPSFilter(psLevel, indent))) { + return NULL; + } + s->append(indent)->append("<< >> /FlateDecode filter\n"); + return s; +} + +GBool FlateStream::isBinary(GBool last) { + return str->isBinary(gTrue); +} + +void FlateStream::readSome() { + int code1, code2; + int len, dist; + int 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; + i = index; + j = (index - dist) & flateMask; + for (k = 0; k < len; ++k) { + buf[i] = buf[j]; + i = (i + 1) & flateMask; + j = (j + 1) & flateMask; + } + 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; + } + + 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)) + error(errSyntaxError, getPos(), + "Bad uncompressed block length in flate stream"); + codeBuf = 0; + codeSize = 0; + + // 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 of 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; + } + 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; + } + 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; + } +} diff --git a/test/bug-hunting/cve/CVE-2019-10025/Stream.h b/test/bug-hunting/cve/CVE-2019-10025/Stream.h new file mode 100644 index 000000000..c25d46b2c --- /dev/null +++ b/test/bug-hunting/cve/CVE-2019-10025/Stream.h @@ -0,0 +1,1035 @@ +//======================================================================== +// +// Stream.h +// +// Copyright 1996-2003 Glyph & Cog, LLC +// +//======================================================================== + +#ifndef STREAM_H +#define STREAM_H + +#include + +#ifdef USE_GCC_PRAGMAS +#pragma interface +#endif + +#include +#if HAVE_JPEGLIB +#include +#include +#endif +#include "gtypes.h" +#include "gfile.h" +#include "Object.h" + +class BaseStream; +class SharedFile; + +//------------------------------------------------------------------------ + +enum StreamKind { + strFile, + strASCIIHex, + strASCII85, + strLZW, + strRunLength, + strCCITTFax, + strDCT, + strFlate, + strJBIG2, + strJPX, + strWeird // internal-use stream types +}; + +enum StreamColorSpaceMode { + streamCSNone, + streamCSDeviceGray, + streamCSDeviceRGB, + streamCSDeviceCMYK +}; + +//------------------------------------------------------------------------ + +// This is in Stream.h instead of Decrypt.h to avoid really annoying +// include file dependency loops. +enum CryptAlgorithm { + cryptRC4, + cryptAES, + cryptAES256 +}; + +//------------------------------------------------------------------------ +// Stream (base class) +//------------------------------------------------------------------------ + +class Stream { +public: + + // Constructor. + Stream(); + + // Destructor. + virtual ~Stream(); + + virtual Stream *copy() = 0; + + // Get kind of stream. + virtual StreamKind getKind() = 0; + + virtual GBool isEmbedStream() { return gFalse; } + + // Reset stream to beginning. + virtual void reset() = 0; + + // Close down the stream. + virtual void close(); + + // Get next char from stream. + virtual int getChar() = 0; + + // Peek at next char in stream. + virtual int lookChar() = 0; + + // Get next char from stream without using the predictor. + // This is only used by StreamPredictor. + virtual int getRawChar(); + + // Get exactly bytes from stream. Returns the number of + // bytes read -- the returned count will be less than at EOF. + virtual int getBlock(char *blk, int size); + + // Get next line from stream. + virtual char *getLine(char *buf, int size); + + // Discard the next bytes from stream. Returns the number of + // bytes discarded, which will be less than only if EOF is + // reached. + virtual Guint discardChars(Guint n); + + // Get current position in file. + virtual GFileOffset getPos() = 0; + + // Go to a position in the stream. If is negative, the + // position is from the end of the file; otherwise the position is + // from the start of the file. + virtual void setPos(GFileOffset pos, int dir = 0) = 0; + + // Get PostScript command for the filter(s). + virtual GString *getPSFilter(int psLevel, const char *indent); + + // Does this stream type potentially contain non-printable chars? + virtual GBool isBinary(GBool last = gTrue) = 0; + + // Get the BaseStream of this stream. + virtual BaseStream *getBaseStream() = 0; + + // Get the stream after the last decoder (this may be a BaseStream + // or a DecryptStream). + virtual Stream *getUndecodedStream() = 0; + + // Get the dictionary associated with this stream. + virtual Dict *getDict() = 0; + + // Is this an encoding filter? + virtual GBool isEncoder() { return gFalse; } + + // Get image parameters which are defined by the stream contents. + virtual void getImageParams(int *bitsPerComponent, + StreamColorSpaceMode *csMode) {} + + // Return the next stream in the "stack". + virtual Stream *getNextStream() { return NULL; } + + // Add filters to this stream according to the parameters in . + // Returns the new stream. + Stream *addFilters(Object *dict, int recursion = 0); + +private: + + Stream *makeFilter(char *name, Stream *str, Object *params, int recursion); +}; + +//------------------------------------------------------------------------ +// BaseStream +// +// This is the base class for all streams that read directly from a file. +//------------------------------------------------------------------------ + +class BaseStream: public Stream { +public: + + BaseStream(Object *dictA); + virtual ~BaseStream(); + virtual Stream *makeSubStream(GFileOffset start, GBool limited, + GFileOffset length, Object *dict) = 0; + virtual void setPos(GFileOffset pos, int dir = 0) = 0; + virtual GBool isBinary(GBool last = gTrue) { return last; } + virtual BaseStream *getBaseStream() { return this; } + virtual Stream *getUndecodedStream() { return this; } + virtual Dict *getDict() { return dict.getDict(); } + virtual GString *getFileName() { return NULL; } + + // Get/set position of first byte of stream within the file. + virtual GFileOffset getStart() = 0; + virtual void moveStart(int delta) = 0; + +protected: + + Object dict; +}; + +//------------------------------------------------------------------------ +// FilterStream +// +// This is the base class for all streams that filter another stream. +//------------------------------------------------------------------------ + +class FilterStream: public Stream { +public: + + FilterStream(Stream *strA); + virtual ~FilterStream(); + virtual void close(); + virtual GFileOffset getPos() { return str->getPos(); } + virtual void setPos(GFileOffset pos, int dir = 0); + virtual BaseStream *getBaseStream() { return str->getBaseStream(); } + virtual Stream *getUndecodedStream() { return str->getUndecodedStream(); } + virtual Dict *getDict() { return str->getDict(); } + virtual Stream *getNextStream() { return str; } + +protected: + + Stream *str; +}; + +//------------------------------------------------------------------------ +// ImageStream +//------------------------------------------------------------------------ + +class ImageStream { +public: + + // Create an image stream object for an image with the specified + // parameters. Note that these are the actual image parameters, + // which may be different from the predictor parameters. + ImageStream(Stream *strA, int widthA, int nCompsA, int nBitsA); + + ~ImageStream(); + + // Reset the stream. + void reset(); + + // Close down the stream. + void close(); + + // Gets the next pixel from the stream. should be able to hold + // at least nComps elements. Returns false at end of file. + GBool getPixel(Guchar *pix); + + // Returns a pointer to the next line of pixels. Returns NULL at + // end of file. + Guchar *getLine(); + + // Skip an entire line from the image. + void skipLine(); + +private: + + Stream *str; // base stream + int width; // pixels per line + int nComps; // components per pixel + int nBits; // bits per component + int nVals; // components per line + int inputLineSize; // input line buffer size + char *inputLine; // input line buffer + Guchar *imgLine; // line buffer + int imgIdx; // current index in imgLine +}; + + +//------------------------------------------------------------------------ +// StreamPredictor +//------------------------------------------------------------------------ + +class StreamPredictor { +public: + + // Create a predictor object. Note that the parameters are for the + // predictor, and may not match the actual image parameters. + StreamPredictor(Stream *strA, int predictorA, + int widthA, int nCompsA, int nBitsA); + + ~StreamPredictor(); + + GBool isOk() { return ok; } + + void reset(); + + int lookChar(); + int getChar(); + int getBlock(char *blk, int size); + + int getPredictor() { return predictor; } + int getWidth() { return width; } + int getNComps() { return nComps; } + int getNBits() { return nBits; } + +private: + + GBool getNextLine(); + + Stream *str; // base stream + int predictor; // predictor + int width; // pixels per line + int nComps; // components per pixel + int nBits; // bits per component + int nVals; // components per line + int pixBytes; // bytes per pixel + int rowBytes; // bytes per line + Guchar *predLine; // line buffer + int predIdx; // current index in predLine + GBool ok; +}; + +//------------------------------------------------------------------------ +// FileStream +//------------------------------------------------------------------------ + +#define fileStreamBufSize 256 + +class FileStream: public BaseStream { +public: + + FileStream(FILE *fA, GFileOffset startA, GBool limitedA, + GFileOffset lengthA, Object *dictA); + virtual ~FileStream(); + virtual Stream *copy(); + virtual Stream *makeSubStream(GFileOffset startA, GBool limitedA, + GFileOffset lengthA, Object *dictA); + virtual StreamKind getKind() { return strFile; } + virtual void reset(); + virtual int getChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } + virtual int lookChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } + virtual int getBlock(char *blk, int size); + virtual GFileOffset getPos() { return bufPos + (int)(bufPtr - buf); } + virtual void setPos(GFileOffset pos, int dir = 0); + virtual GFileOffset getStart() { return start; } + virtual void moveStart(int delta); + +private: + + FileStream(SharedFile *fA, GFileOffset startA, GBool limitedA, + GFileOffset lengthA, Object *dictA); + GBool fillBuf(); + + SharedFile *f; + GFileOffset start; + GBool limited; + GFileOffset length; + char buf[fileStreamBufSize]; + char *bufPtr; + char *bufEnd; + GFileOffset bufPos; +}; + +//------------------------------------------------------------------------ +// MemStream +//------------------------------------------------------------------------ + +class MemStream: public BaseStream { +public: + + MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA); + virtual ~MemStream(); + virtual Stream *copy(); + virtual Stream *makeSubStream(GFileOffset start, GBool limited, + GFileOffset lengthA, Object *dictA); + virtual StreamKind getKind() { return strWeird; } + virtual void reset(); + virtual void close(); + virtual int getChar() + { return (bufPtr < bufEnd) ? (*bufPtr++ & 0xff) : EOF; } + virtual int lookChar() + { return (bufPtr < bufEnd) ? (*bufPtr & 0xff) : EOF; } + virtual int getBlock(char *blk, int size); + virtual GFileOffset getPos() { return (GFileOffset)(bufPtr - buf); } + virtual void setPos(GFileOffset pos, int dir = 0); + virtual GFileOffset getStart() { return start; } + virtual void moveStart(int delta); + +private: + + char *buf; + Guint start; + Guint length; + char *bufEnd; + char *bufPtr; + GBool needFree; +}; + +//------------------------------------------------------------------------ +// EmbedStream +// +// This is a special stream type used for embedded streams (inline +// images). It reads directly from the base stream -- after the +// EmbedStream is deleted, reads from the base stream will proceed where +// the BaseStream left off. Note that this is very different behavior +// that creating a new FileStream (using makeSubStream). +//------------------------------------------------------------------------ + +class EmbedStream: public BaseStream { +public: + + EmbedStream(Stream *strA, Object *dictA, GBool limitedA, GFileOffset lengthA); + virtual ~EmbedStream(); + virtual Stream *copy(); + virtual Stream *makeSubStream(GFileOffset start, GBool limitedA, + GFileOffset lengthA, Object *dictA); + virtual StreamKind getKind() { return str->getKind(); } + virtual GBool isEmbedStream() { return gTrue; } + virtual void reset() {} + virtual int getChar(); + virtual int lookChar(); + virtual int getBlock(char *blk, int size); + virtual GFileOffset getPos() { return str->getPos(); } + virtual void setPos(GFileOffset pos, int dir = 0); + virtual GFileOffset getStart(); + virtual void moveStart(int delta); + +private: + + Stream *str; + GBool limited; + GFileOffset length; +}; + +//------------------------------------------------------------------------ +// ASCIIHexStream +//------------------------------------------------------------------------ + +class ASCIIHexStream: public FilterStream { +public: + + ASCIIHexStream(Stream *strA); + virtual ~ASCIIHexStream(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strASCIIHex; } + virtual void reset(); + virtual int getChar() + { int c = lookChar(); buf = EOF; return c; } + virtual int lookChar(); + virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GBool isBinary(GBool last = gTrue); + +private: + + int buf; + GBool eof; +}; + +//------------------------------------------------------------------------ +// ASCII85Stream +//------------------------------------------------------------------------ + +class ASCII85Stream: public FilterStream { +public: + + ASCII85Stream(Stream *strA); + virtual ~ASCII85Stream(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strASCII85; } + virtual void reset(); + virtual int getChar() + { int ch = lookChar(); ++index; return ch; } + virtual int lookChar(); + virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GBool isBinary(GBool last = gTrue); + +private: + + int c[5]; + int b[4]; + int index, n; + GBool eof; +}; + +//------------------------------------------------------------------------ +// LZWStream +//------------------------------------------------------------------------ + +class LZWStream: public FilterStream { +public: + + LZWStream(Stream *strA, int predictor, int columns, int colors, + int bits, int earlyA); + virtual ~LZWStream(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strLZW; } + virtual void reset(); + virtual int getChar(); + virtual int lookChar(); + virtual int getRawChar(); + virtual int getBlock(char *blk, int size); + virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GBool isBinary(GBool last = gTrue); + +private: + + StreamPredictor *pred; // predictor + int early; // early parameter + GBool eof; // true if at eof + int inputBuf; // input buffer + int inputBits; // number of bits in input buffer + struct { // decoding table + int length; + int head; + Guchar tail; + } table[4097]; + int nextCode; // next code to be used + int nextBits; // number of bits in next code word + int prevCode; // previous code used in stream + int newChar; // next char to be added to table + Guchar seqBuf[4097]; // buffer for current sequence + int seqLength; // length of current sequence + int seqIndex; // index into current sequence + GBool first; // first code after a table clear + + GBool processNextCode(); + void clearTable(); + int getCode(); +}; + +//------------------------------------------------------------------------ +// RunLengthStream +//------------------------------------------------------------------------ + +class RunLengthStream: public FilterStream { +public: + + RunLengthStream(Stream *strA); + virtual ~RunLengthStream(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strRunLength; } + virtual void reset(); + virtual int getChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } + virtual int lookChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } + virtual int getBlock(char *blk, int size); + virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GBool isBinary(GBool last = gTrue); + +private: + + char buf[128]; // buffer + char *bufPtr; // next char to read + char *bufEnd; // end of buffer + GBool eof; + + GBool fillBuf(); +}; + +//------------------------------------------------------------------------ +// CCITTFaxStream +//------------------------------------------------------------------------ + +struct CCITTCodeTable; + +class CCITTFaxStream: public FilterStream { +public: + + CCITTFaxStream(Stream *strA, int encodingA, GBool endOfLineA, + GBool byteAlignA, int columnsA, int rowsA, + GBool endOfBlockA, GBool blackA); + virtual ~CCITTFaxStream(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strCCITTFax; } + virtual void reset(); + virtual int getChar(); + virtual int lookChar(); + virtual int getBlock(char *blk, int size); + virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GBool isBinary(GBool last = gTrue); + +private: + + int encoding; // 'K' parameter + GBool endOfLine; // 'EndOfLine' parameter + GBool byteAlign; // 'EncodedByteAlign' parameter + int columns; // 'Columns' parameter + int rows; // 'Rows' parameter + GBool endOfBlock; // 'EndOfBlock' parameter + GBool black; // 'BlackIs1' parameter + int blackXOR; + GBool eof; // true if at eof + GBool nextLine2D; // true if next line uses 2D encoding + int row; // current row + Guint inputBuf; // input buffer + int inputBits; // number of bits in input buffer + int *codingLine; // coding line changing elements + int *refLine; // reference line changing elements + int nextCol; // next column to read + int a0i; // index into codingLine + GBool err; // error on current line + int nErrors; // number of errors so far in this stream + + void addPixels(int a1, int blackPixels); + void addPixelsNeg(int a1, int blackPixels); + GBool readRow(); + short getTwoDimCode(); + short getWhiteCode(); + short getBlackCode(); + short lookBits(int n); + void eatBits(int n) { if ((inputBits -= n) < 0) inputBits = 0; } +}; + +//------------------------------------------------------------------------ +// DCTStream +//------------------------------------------------------------------------ + +#if HAVE_JPEGLIB + +class DCTStream; + +#define dctStreamBufSize 4096 + +struct DCTSourceMgr { + jpeg_source_mgr src; + DCTStream *str; + char buf[dctStreamBufSize]; +}; + +struct DCTErrorMgr { + struct jpeg_error_mgr err; + jmp_buf setjmpBuf; +}; + +#else // HAVE_JPEGLIB + +// DCT component info +struct DCTCompInfo { + int id; // component ID + int hSample, vSample; // horiz/vert sampling resolutions + int quantTable; // quantization table number + int prevDC; // DC coefficient accumulator +}; + +struct DCTScanInfo { + GBool comp[4]; // comp[i] is set if component i is + // included in this scan + int numComps; // number of components in the scan + int dcHuffTable[4]; // DC Huffman table numbers + int acHuffTable[4]; // AC Huffman table numbers + int firstCoeff, lastCoeff; // first and last DCT coefficient + int ah, al; // successive approximation parameters +}; + +// DCT Huffman decoding table +struct DCTHuffTable { + Guchar firstSym[17]; // first symbol for this bit length + Gushort firstCode[17]; // first code for this bit length + Gushort numCodes[17]; // number of codes of this bit length + Guchar sym[256]; // symbols +}; + +#endif // HAVE_JPEGLIB + +class DCTStream: public FilterStream { +public: + + DCTStream(Stream *strA, int colorXformA); + virtual ~DCTStream(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strDCT; } + virtual void reset(); + virtual void close(); + virtual int getChar(); + virtual int lookChar(); + virtual int getBlock(char *blk, int size); + virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GBool isBinary(GBool last = gTrue); + Stream *getRawStream() { return str; } + +private: + +#if HAVE_JPEGLIB + + int colorXform; // color transform: -1 = unspecified + // 0 = none + // 1 = YUV/YUVK -> RGB/CMYK + jpeg_decompress_struct decomp; + DCTErrorMgr errorMgr; + DCTSourceMgr sourceMgr; + GBool error; + char *lineBuf; + int lineBufHeight; + char *lineBufRows[4]; + char *bufPtr; + char *bufEnd; + GBool inlineImage; + + GBool fillBuf(); + static void errorExit(j_common_ptr d); + static void errorMessage(j_common_ptr d); + static void initSourceCbk(j_decompress_ptr d); + static boolean fillInputBufferCbk(j_decompress_ptr d); + static void skipInputDataCbk(j_decompress_ptr d, long numBytes); + static void termSourceCbk(j_decompress_ptr d); + +#else // HAVE_JPEGLIB + + GBool progressive; // set if in progressive mode + GBool interleaved; // set if in interleaved mode + int width, height; // image size + int mcuWidth, mcuHeight; // size of min coding unit, in data units + int bufWidth, bufHeight; // frameBuf size + DCTCompInfo compInfo[4]; // info for each component + DCTScanInfo scanInfo; // info for the current scan + int numComps; // number of components in image + int colorXform; // color transform: -1 = unspecified + // 0 = none + // 1 = YUV/YUVK -> RGB/CMYK + GBool gotJFIFMarker; // set if APP0 JFIF marker was present + GBool gotAdobeMarker; // set if APP14 Adobe marker was present + int restartInterval; // restart interval, in MCUs + Gushort quantTables[4][64]; // quantization tables + int numQuantTables; // number of quantization tables + DCTHuffTable dcHuffTables[4]; // DC Huffman tables + DCTHuffTable acHuffTables[4]; // AC Huffman tables + int numDCHuffTables; // number of DC Huffman tables + int numACHuffTables; // number of AC Huffman tables + Guchar *rowBuf; + Guchar *rowBufPtr; // current position within rowBuf + Guchar *rowBufEnd; // end of valid data in rowBuf + int *frameBuf[4]; // buffer for frame (progressive mode) + int comp, x, y; // current position within image/MCU + int restartCtr; // MCUs left until restart + int restartMarker; // next restart marker + int eobRun; // number of EOBs left in the current run + int inputBuf; // input buffer for variable length codes + int inputBits; // number of valid bits in input buffer + + void restart(); + GBool readMCURow(); + void readScan(); + GBool readDataUnit(DCTHuffTable *dcHuffTable, + DCTHuffTable *acHuffTable, + int *prevDC, int data[64]); + GBool readProgressiveDataUnit(DCTHuffTable *dcHuffTable, + DCTHuffTable *acHuffTable, + int *prevDC, int data[64]); + void decodeImage(); + void transformDataUnit(Gushort *quantTable, + int dataIn[64], Guchar dataOut[64]); + int readHuffSym(DCTHuffTable *table); + int readAmp(int size); + int readBit(); + GBool readHeader(GBool frame); + GBool readBaselineSOF(); + GBool readProgressiveSOF(); + GBool readScanInfo(); + GBool readQuantTables(); + GBool readHuffmanTables(); + GBool readRestartInterval(); + GBool readJFIFMarker(); + GBool readAdobeMarker(); + GBool readTrailer(); + int readMarker(); + int read16(); + +#endif // HAVE_JPEGLIB +}; + +//------------------------------------------------------------------------ +// FlateStream +//------------------------------------------------------------------------ + +#define flateWindow 32768 // buffer size +#define flateMask (flateWindow-1) +#define flateMaxHuffman 15 // max Huffman code length +#define flateMaxCodeLenCodes 19 // max # code length codes +#define flateMaxLitCodes 288 // max # literal codes +#define flateMaxDistCodes 30 // max # distance codes + +// Huffman code table entry +struct FlateCode { + Gushort len; // code length, in bits + Gushort val; // value represented by this code +}; + +struct FlateHuffmanTab { + FlateCode *codes; + int maxLen; +}; + +// Decoding info for length and distance code words +struct FlateDecode { + int bits; // # extra bits + int first; // first length/distance +}; + +class FlateStream: public FilterStream { +public: + + FlateStream(Stream *strA, int predictor, int columns, + int colors, int bits); + virtual ~FlateStream(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strFlate; } + virtual void reset(); + virtual int getChar(); + virtual int lookChar(); + virtual int getRawChar(); + virtual int getBlock(char *blk, int size); + virtual GString *getPSFilter(int psLevel, const char *indent); + virtual GBool isBinary(GBool last = gTrue); + +private: + + StreamPredictor *pred; // predictor + Guchar buf[flateWindow]; // output data buffer + int index; // current index into output buffer + int remain; // number valid bytes in output buffer + int codeBuf; // input buffer + int codeSize; // number of bits in input buffer + int // literal and distance code lengths + codeLengths[flateMaxLitCodes + flateMaxDistCodes]; + FlateHuffmanTab litCodeTab; // literal code table + FlateHuffmanTab distCodeTab; // distance code table + GBool compressedBlock; // set if reading a compressed block + int blockLen; // remaining length of uncompressed block + GBool endOfBlock; // set when end of block is reached + GBool eof; // set when end of stream is reached + + static int // code length code reordering + codeLenCodeMap[flateMaxCodeLenCodes]; + static FlateDecode // length decoding info + lengthDecode[flateMaxLitCodes-257]; + static FlateDecode // distance decoding info + distDecode[flateMaxDistCodes]; + static FlateHuffmanTab // fixed literal code table + fixedLitCodeTab; + static FlateHuffmanTab // fixed distance code table + fixedDistCodeTab; + + void readSome(); + GBool startBlock(); + void loadFixedCodes(); + GBool readDynamicCodes(); + void compHuffmanCodes(int *lengths, int n, FlateHuffmanTab *tab); + int getHuffmanCodeWord(FlateHuffmanTab *tab); + int getCodeWord(int bits); +}; + +//------------------------------------------------------------------------ +// EOFStream +//------------------------------------------------------------------------ + +class EOFStream: public FilterStream { +public: + + EOFStream(Stream *strA); + virtual ~EOFStream(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strWeird; } + virtual void reset() {} + virtual int getChar() { return EOF; } + virtual int lookChar() { return EOF; } + virtual int getBlock(char *blk, int size) { return 0; } + virtual GString *getPSFilter(int psLevel, const char *indent) + { return NULL; } + virtual GBool isBinary(GBool last = gTrue) { return gFalse; } +}; + +//------------------------------------------------------------------------ +// BufStream +//------------------------------------------------------------------------ + +class BufStream: public FilterStream { +public: + + BufStream(Stream *strA, int bufSizeA); + virtual ~BufStream(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strWeird; } + virtual void reset(); + virtual int getChar(); + virtual int lookChar(); + virtual GString *getPSFilter(int psLevel, const char *indent) + { return NULL; } + virtual GBool isBinary(GBool last = gTrue); + + int lookChar(int idx); + +private: + + int *buf; + int bufSize; +}; + +//------------------------------------------------------------------------ +// FixedLengthEncoder +//------------------------------------------------------------------------ + +class FixedLengthEncoder: public FilterStream { +public: + + FixedLengthEncoder(Stream *strA, int lengthA); + ~FixedLengthEncoder(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strWeird; } + virtual void reset(); + virtual int getChar(); + virtual int lookChar(); + virtual GString *getPSFilter(int psLevel, const char *indent) + { return NULL; } + virtual GBool isBinary(GBool last = gTrue); + virtual GBool isEncoder() { return gTrue; } + +private: + + int length; + int count; +}; + +//------------------------------------------------------------------------ +// ASCIIHexEncoder +//------------------------------------------------------------------------ + +class ASCIIHexEncoder: public FilterStream { +public: + + ASCIIHexEncoder(Stream *strA); + virtual ~ASCIIHexEncoder(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strWeird; } + virtual void reset(); + virtual int getChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } + virtual int lookChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } + virtual GString *getPSFilter(int psLevel, const char *indent) + { return NULL; } + virtual GBool isBinary(GBool last = gTrue) { return gFalse; } + virtual GBool isEncoder() { return gTrue; } + +private: + + char buf[4]; + char *bufPtr; + char *bufEnd; + int lineLen; + GBool eof; + + GBool fillBuf(); +}; + +//------------------------------------------------------------------------ +// ASCII85Encoder +//------------------------------------------------------------------------ + +class ASCII85Encoder: public FilterStream { +public: + + ASCII85Encoder(Stream *strA); + virtual ~ASCII85Encoder(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strWeird; } + virtual void reset(); + virtual int getChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } + virtual int lookChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } + virtual GString *getPSFilter(int psLevel, const char *indent) + { return NULL; } + virtual GBool isBinary(GBool last = gTrue) { return gFalse; } + virtual GBool isEncoder() { return gTrue; } + +private: + + char buf[8]; + char *bufPtr; + char *bufEnd; + int lineLen; + GBool eof; + + GBool fillBuf(); +}; + +//------------------------------------------------------------------------ +// RunLengthEncoder +//------------------------------------------------------------------------ + +class RunLengthEncoder: public FilterStream { +public: + + RunLengthEncoder(Stream *strA); + virtual ~RunLengthEncoder(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strWeird; } + virtual void reset(); + virtual int getChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); } + virtual int lookChar() + { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); } + virtual GString *getPSFilter(int psLevel, const char *indent) + { return NULL; } + virtual GBool isBinary(GBool last = gTrue) { return gTrue; } + virtual GBool isEncoder() { return gTrue; } + +private: + + char buf[131]; + char *bufPtr; + char *bufEnd; + char *nextEnd; + GBool eof; + + GBool fillBuf(); +}; + +//------------------------------------------------------------------------ +// LZWEncoder +//------------------------------------------------------------------------ + +struct LZWEncoderNode { + int byte; + LZWEncoderNode *next; // next sibling + LZWEncoderNode *children; // first child +}; + +class LZWEncoder: public FilterStream { +public: + + LZWEncoder(Stream *strA); + virtual ~LZWEncoder(); + virtual Stream *copy(); + virtual StreamKind getKind() { return strWeird; } + virtual void reset(); + virtual int getChar(); + virtual int lookChar(); + virtual GString *getPSFilter(int psLevel, const char *indent) + { return NULL; } + virtual GBool isBinary(GBool last = gTrue) { return gTrue; } + virtual GBool isEncoder() { return gTrue; } + +private: + + LZWEncoderNode table[4096]; + int nextSeq; + int codeLen; + Guchar inBuf[8192]; + int inBufStart; + int inBufLen; + int outBuf; + int outBufLen; + GBool needEOD; + + void fillBuf(); +}; + +#endif