| //======================================================================== |
| // |
| // JBIG2Stream.cc |
| // |
| // Copyright 2002-2003 Glyph & Cog, LLC |
| // |
| //======================================================================== |
| |
| //======================================================================== |
| // |
| // Modified under the Poppler project - http://poppler.freedesktop.org |
| // |
| // All changes made under the Poppler project to this file are licensed |
| // under GPL version 2 or later |
| // |
| // Copyright (C) 2006 Raj Kumar <rkumar@archive.org> |
| // Copyright (C) 2006 Paul Walmsley <paul@booyaka.com> |
| // Copyright (C) 2006-2010, 2012, 2014-2019 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2009 David Benjamin <davidben@mit.edu> |
| // Copyright (C) 2011 Edward Jiang <ejiang@google.com> |
| // Copyright (C) 2012 William Bader <williambader@hotmail.com> |
| // Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de> |
| // Copyright (C) 2013 Adrian Johnson <ajohnson@redneon.com> |
| // Copyright (C) 2013, 2014 Fabio D'Urso <fabiodurso@hotmail.it> |
| // Copyright (C) 2015 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp> |
| // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
| // Copyright (C) 2019 LE GARREC Vincent <legarrec.vincent@gmail.com> |
| // Copyright (C) 2019 Oliver Sander <oliver.sander@tu-dresden.de> |
| // |
| // To see a description of the changes please see the Changelog file that |
| // came with your tarball or type make ChangeLog if you are building from git |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #include <stdlib.h> |
| #include <limits.h> |
| #include "Error.h" |
| #include "JArithmeticDecoder.h" |
| #include "JBIG2Stream.h" |
| |
| //~ share these tables |
| #include "Stream-CCITT.h" |
| |
| //------------------------------------------------------------------------ |
| |
| static const int contextSize[4] = { 16, 13, 10, 10 }; |
| static const int refContextSize[2] = { 13, 10 }; |
| |
| //------------------------------------------------------------------------ |
| // JBIG2HuffmanTable |
| //------------------------------------------------------------------------ |
| |
| #define jbig2HuffmanLOW 0xfffffffd |
| #define jbig2HuffmanOOB 0xfffffffe |
| #define jbig2HuffmanEOT 0xffffffff |
| |
| struct JBIG2HuffmanTable { |
| int val; |
| unsigned int prefixLen; |
| unsigned int rangeLen; // can also be LOW, OOB, or EOT |
| unsigned int prefix; |
| }; |
| |
| static JBIG2HuffmanTable huffTableA[] = { |
| { 0, 1, 4, 0x000 }, |
| { 16, 2, 8, 0x002 }, |
| { 272, 3, 16, 0x006 }, |
| { 65808, 3, 32, 0x007 }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableB[] = { |
| { 0, 1, 0, 0x000 }, |
| { 1, 2, 0, 0x002 }, |
| { 2, 3, 0, 0x006 }, |
| { 3, 4, 3, 0x00e }, |
| { 11, 5, 6, 0x01e }, |
| { 75, 6, 32, 0x03e }, |
| { 0, 6, jbig2HuffmanOOB, 0x03f }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableC[] = { |
| { 0, 1, 0, 0x000 }, |
| { 1, 2, 0, 0x002 }, |
| { 2, 3, 0, 0x006 }, |
| { 3, 4, 3, 0x00e }, |
| { 11, 5, 6, 0x01e }, |
| { 0, 6, jbig2HuffmanOOB, 0x03e }, |
| { 75, 7, 32, 0x0fe }, |
| { -256, 8, 8, 0x0fe }, |
| { -257, 8, jbig2HuffmanLOW, 0x0ff }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableD[] = { |
| { 1, 1, 0, 0x000 }, |
| { 2, 2, 0, 0x002 }, |
| { 3, 3, 0, 0x006 }, |
| { 4, 4, 3, 0x00e }, |
| { 12, 5, 6, 0x01e }, |
| { 76, 5, 32, 0x01f }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableE[] = { |
| { 1, 1, 0, 0x000 }, |
| { 2, 2, 0, 0x002 }, |
| { 3, 3, 0, 0x006 }, |
| { 4, 4, 3, 0x00e }, |
| { 12, 5, 6, 0x01e }, |
| { 76, 6, 32, 0x03e }, |
| { -255, 7, 8, 0x07e }, |
| { -256, 7, jbig2HuffmanLOW, 0x07f }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableF[] = { |
| { 0, 2, 7, 0x000 }, |
| { 128, 3, 7, 0x002 }, |
| { 256, 3, 8, 0x003 }, |
| { -1024, 4, 9, 0x008 }, |
| { -512, 4, 8, 0x009 }, |
| { -256, 4, 7, 0x00a }, |
| { -32, 4, 5, 0x00b }, |
| { 512, 4, 9, 0x00c }, |
| { 1024, 4, 10, 0x00d }, |
| { -2048, 5, 10, 0x01c }, |
| { -128, 5, 6, 0x01d }, |
| { -64, 5, 5, 0x01e }, |
| { -2049, 6, jbig2HuffmanLOW, 0x03e }, |
| { 2048, 6, 32, 0x03f }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableG[] = { |
| { -512, 3, 8, 0x000 }, |
| { 256, 3, 8, 0x001 }, |
| { 512, 3, 9, 0x002 }, |
| { 1024, 3, 10, 0x003 }, |
| { -1024, 4, 9, 0x008 }, |
| { -256, 4, 7, 0x009 }, |
| { -32, 4, 5, 0x00a }, |
| { 0, 4, 5, 0x00b }, |
| { 128, 4, 7, 0x00c }, |
| { -128, 5, 6, 0x01a }, |
| { -64, 5, 5, 0x01b }, |
| { 32, 5, 5, 0x01c }, |
| { 64, 5, 6, 0x01d }, |
| { -1025, 5, jbig2HuffmanLOW, 0x01e }, |
| { 2048, 5, 32, 0x01f }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableH[] = { |
| { 0, 2, 1, 0x000 }, |
| { 0, 2, jbig2HuffmanOOB, 0x001 }, |
| { 4, 3, 4, 0x004 }, |
| { -1, 4, 0, 0x00a }, |
| { 22, 4, 4, 0x00b }, |
| { 38, 4, 5, 0x00c }, |
| { 2, 5, 0, 0x01a }, |
| { 70, 5, 6, 0x01b }, |
| { 134, 5, 7, 0x01c }, |
| { 3, 6, 0, 0x03a }, |
| { 20, 6, 1, 0x03b }, |
| { 262, 6, 7, 0x03c }, |
| { 646, 6, 10, 0x03d }, |
| { -2, 7, 0, 0x07c }, |
| { 390, 7, 8, 0x07d }, |
| { -15, 8, 3, 0x0fc }, |
| { -5, 8, 1, 0x0fd }, |
| { -7, 9, 1, 0x1fc }, |
| { -3, 9, 0, 0x1fd }, |
| { -16, 9, jbig2HuffmanLOW, 0x1fe }, |
| { 1670, 9, 32, 0x1ff }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableI[] = { |
| { 0, 2, jbig2HuffmanOOB, 0x000 }, |
| { -1, 3, 1, 0x002 }, |
| { 1, 3, 1, 0x003 }, |
| { 7, 3, 5, 0x004 }, |
| { -3, 4, 1, 0x00a }, |
| { 43, 4, 5, 0x00b }, |
| { 75, 4, 6, 0x00c }, |
| { 3, 5, 1, 0x01a }, |
| { 139, 5, 7, 0x01b }, |
| { 267, 5, 8, 0x01c }, |
| { 5, 6, 1, 0x03a }, |
| { 39, 6, 2, 0x03b }, |
| { 523, 6, 8, 0x03c }, |
| { 1291, 6, 11, 0x03d }, |
| { -5, 7, 1, 0x07c }, |
| { 779, 7, 9, 0x07d }, |
| { -31, 8, 4, 0x0fc }, |
| { -11, 8, 2, 0x0fd }, |
| { -15, 9, 2, 0x1fc }, |
| { -7, 9, 1, 0x1fd }, |
| { -32, 9, jbig2HuffmanLOW, 0x1fe }, |
| { 3339, 9, 32, 0x1ff }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableJ[] = { |
| { -2, 2, 2, 0x000 }, |
| { 6, 2, 6, 0x001 }, |
| { 0, 2, jbig2HuffmanOOB, 0x002 }, |
| { -3, 5, 0, 0x018 }, |
| { 2, 5, 0, 0x019 }, |
| { 70, 5, 5, 0x01a }, |
| { 3, 6, 0, 0x036 }, |
| { 102, 6, 5, 0x037 }, |
| { 134, 6, 6, 0x038 }, |
| { 198, 6, 7, 0x039 }, |
| { 326, 6, 8, 0x03a }, |
| { 582, 6, 9, 0x03b }, |
| { 1094, 6, 10, 0x03c }, |
| { -21, 7, 4, 0x07a }, |
| { -4, 7, 0, 0x07b }, |
| { 4, 7, 0, 0x07c }, |
| { 2118, 7, 11, 0x07d }, |
| { -5, 8, 0, 0x0fc }, |
| { 5, 8, 0, 0x0fd }, |
| { -22, 8, jbig2HuffmanLOW, 0x0fe }, |
| { 4166, 8, 32, 0x0ff }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableK[] = { |
| { 1, 1, 0, 0x000 }, |
| { 2, 2, 1, 0x002 }, |
| { 4, 4, 0, 0x00c }, |
| { 5, 4, 1, 0x00d }, |
| { 7, 5, 1, 0x01c }, |
| { 9, 5, 2, 0x01d }, |
| { 13, 6, 2, 0x03c }, |
| { 17, 7, 2, 0x07a }, |
| { 21, 7, 3, 0x07b }, |
| { 29, 7, 4, 0x07c }, |
| { 45, 7, 5, 0x07d }, |
| { 77, 7, 6, 0x07e }, |
| { 141, 7, 32, 0x07f }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableL[] = { |
| { 1, 1, 0, 0x000 }, |
| { 2, 2, 0, 0x002 }, |
| { 3, 3, 1, 0x006 }, |
| { 5, 5, 0, 0x01c }, |
| { 6, 5, 1, 0x01d }, |
| { 8, 6, 1, 0x03c }, |
| { 10, 7, 0, 0x07a }, |
| { 11, 7, 1, 0x07b }, |
| { 13, 7, 2, 0x07c }, |
| { 17, 7, 3, 0x07d }, |
| { 25, 7, 4, 0x07e }, |
| { 41, 8, 5, 0x0fe }, |
| { 73, 8, 32, 0x0ff }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableM[] = { |
| { 1, 1, 0, 0x000 }, |
| { 2, 3, 0, 0x004 }, |
| { 7, 3, 3, 0x005 }, |
| { 3, 4, 0, 0x00c }, |
| { 5, 4, 1, 0x00d }, |
| { 4, 5, 0, 0x01c }, |
| { 15, 6, 1, 0x03a }, |
| { 17, 6, 2, 0x03b }, |
| { 21, 6, 3, 0x03c }, |
| { 29, 6, 4, 0x03d }, |
| { 45, 6, 5, 0x03e }, |
| { 77, 7, 6, 0x07e }, |
| { 141, 7, 32, 0x07f }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableN[] = { |
| { 0, 1, 0, 0x000 }, |
| { -2, 3, 0, 0x004 }, |
| { -1, 3, 0, 0x005 }, |
| { 1, 3, 0, 0x006 }, |
| { 2, 3, 0, 0x007 }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| static JBIG2HuffmanTable huffTableO[] = { |
| { 0, 1, 0, 0x000 }, |
| { -1, 3, 0, 0x004 }, |
| { 1, 3, 0, 0x005 }, |
| { -2, 4, 0, 0x00c }, |
| { 2, 4, 0, 0x00d }, |
| { -4, 5, 1, 0x01c }, |
| { 3, 5, 1, 0x01d }, |
| { -8, 6, 2, 0x03c }, |
| { 5, 6, 2, 0x03d }, |
| { -24, 7, 4, 0x07c }, |
| { 9, 7, 4, 0x07d }, |
| { -25, 7, jbig2HuffmanLOW, 0x07e }, |
| { 25, 7, 32, 0x07f }, |
| { 0, 0, jbig2HuffmanEOT, 0 } |
| }; |
| |
| //------------------------------------------------------------------------ |
| // JBIG2HuffmanDecoder |
| //------------------------------------------------------------------------ |
| |
| class JBIG2HuffmanDecoder { |
| public: |
| |
| JBIG2HuffmanDecoder(); |
| ~JBIG2HuffmanDecoder(); |
| void setStream(Stream *strA) { str = strA; } |
| |
| void reset(); |
| |
| // Returns false for OOB, otherwise sets *<x> and returns true. |
| bool decodeInt(int *x, JBIG2HuffmanTable *table); |
| |
| unsigned int readBits(unsigned int n); |
| unsigned int readBit(); |
| |
| // Sort the table by prefix length and assign prefix values. |
| static bool buildTable(JBIG2HuffmanTable *table, unsigned int len); |
| |
| private: |
| |
| Stream *str; |
| unsigned int buf; |
| unsigned int bufLen; |
| }; |
| |
| JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() { |
| str = nullptr; |
| reset(); |
| } |
| |
| JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() { |
| } |
| |
| void JBIG2HuffmanDecoder::reset() { |
| buf = 0; |
| bufLen = 0; |
| } |
| |
| //~ optimize this |
| bool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) { |
| unsigned int i, len, prefix; |
| |
| i = 0; |
| len = 0; |
| prefix = 0; |
| while (table[i].rangeLen != jbig2HuffmanEOT) { |
| while (len < table[i].prefixLen) { |
| prefix = (prefix << 1) | readBit(); |
| ++len; |
| } |
| if (prefix == table[i].prefix) { |
| if (table[i].rangeLen == jbig2HuffmanOOB) { |
| return false; |
| } |
| if (table[i].rangeLen == jbig2HuffmanLOW) { |
| *x = table[i].val - readBits(32); |
| } else if (table[i].rangeLen > 0) { |
| *x = table[i].val + readBits(table[i].rangeLen); |
| } else { |
| *x = table[i].val; |
| } |
| return true; |
| } |
| ++i; |
| } |
| return false; |
| } |
| |
| unsigned int JBIG2HuffmanDecoder::readBits(unsigned int n) { |
| unsigned int x, mask, nLeft; |
| |
| mask = (n == 32) ? 0xffffffff : ((1 << n) - 1); |
| if (bufLen >= n) { |
| x = (buf >> (bufLen - n)) & mask; |
| bufLen -= n; |
| } else { |
| x = buf & ((1 << bufLen) - 1); |
| nLeft = n - bufLen; |
| bufLen = 0; |
| while (nLeft >= 8) { |
| x = (x << 8) | (str->getChar() & 0xff); |
| nLeft -= 8; |
| } |
| if (nLeft > 0) { |
| buf = str->getChar(); |
| bufLen = 8 - nLeft; |
| x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1)); |
| } |
| } |
| return x; |
| } |
| |
| unsigned int JBIG2HuffmanDecoder::readBit() { |
| if (bufLen == 0) { |
| buf = str->getChar(); |
| bufLen = 8; |
| } |
| --bufLen; |
| return (buf >> bufLen) & 1; |
| } |
| |
| bool JBIG2HuffmanDecoder::buildTable(JBIG2HuffmanTable *table, unsigned int len) { |
| unsigned int i, j, k, prefix; |
| JBIG2HuffmanTable tab; |
| |
| // stable selection sort: |
| // - entries with prefixLen > 0, in ascending prefixLen order |
| // - entry with prefixLen = 0, rangeLen = EOT |
| // - all other entries with prefixLen = 0 |
| // (on entry, table[len] has prefixLen = 0, rangeLen = EOT) |
| for (i = 0; i < len; ++i) { |
| for (j = i; j < len && table[j].prefixLen == 0; ++j) ; |
| if (j == len) { |
| break; |
| } |
| for (k = j + 1; k < len; ++k) { |
| if (table[k].prefixLen > 0 && |
| table[k].prefixLen < table[j].prefixLen) { |
| j = k; |
| } |
| } |
| if (j != i) { |
| tab = table[j]; |
| for (k = j; k > i; --k) { |
| table[k] = table[k - 1]; |
| } |
| table[i] = tab; |
| } |
| } |
| table[i] = table[len]; |
| |
| // assign prefixes |
| if (table[0].rangeLen != jbig2HuffmanEOT) { |
| i = 0; |
| prefix = 0; |
| table[i++].prefix = prefix++; |
| for (; table[i].rangeLen != jbig2HuffmanEOT; ++i) { |
| if (table[i].prefixLen - table[i-1].prefixLen > 32) { |
| error(errSyntaxError, -1, "Failed to build table for JBIG2 stream"); |
| return false; |
| } else { |
| prefix <<= table[i].prefixLen - table[i-1].prefixLen; |
| } |
| table[i].prefix = prefix++; |
| } |
| } |
| |
| return true; |
| } |
| |
| //------------------------------------------------------------------------ |
| // JBIG2MMRDecoder |
| //------------------------------------------------------------------------ |
| |
| class JBIG2MMRDecoder { |
| public: |
| |
| JBIG2MMRDecoder(); |
| ~JBIG2MMRDecoder(); |
| void setStream(Stream *strA) { str = strA; } |
| void reset(); |
| int get2DCode(); |
| int getBlackCode(); |
| int getWhiteCode(); |
| unsigned int get24Bits(); |
| void skipTo(unsigned int length); |
| |
| private: |
| |
| Stream *str; |
| unsigned int buf; |
| unsigned int bufLen; |
| unsigned int nBytesRead; |
| }; |
| |
| JBIG2MMRDecoder::JBIG2MMRDecoder() { |
| str = nullptr; |
| reset(); |
| } |
| |
| JBIG2MMRDecoder::~JBIG2MMRDecoder() { |
| } |
| |
| void JBIG2MMRDecoder::reset() { |
| buf = 0; |
| bufLen = 0; |
| nBytesRead = 0; |
| } |
| |
| int JBIG2MMRDecoder::get2DCode() { |
| const CCITTCode *p = nullptr; |
| |
| if (bufLen == 0) { |
| buf = str->getChar() & 0xff; |
| bufLen = 8; |
| ++nBytesRead; |
| p = &twoDimTab1[(buf >> 1) & 0x7f]; |
| } else if (bufLen == 8) { |
| p = &twoDimTab1[(buf >> 1) & 0x7f]; |
| } else if (bufLen < 8) { |
| p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f]; |
| if (p->bits < 0 || p->bits > (int)bufLen) { |
| buf = (buf << 8) | (str->getChar() & 0xff); |
| bufLen += 8; |
| ++nBytesRead; |
| p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f]; |
| } |
| } |
| if (p == nullptr || p->bits < 0) { |
| error(errSyntaxError, str->getPos(), "Bad two dim code in JBIG2 MMR stream"); |
| return EOF; |
| } |
| bufLen -= p->bits; |
| return p->n; |
| } |
| |
| int JBIG2MMRDecoder::getWhiteCode() { |
| const CCITTCode *p; |
| unsigned int code; |
| |
| if (bufLen == 0) { |
| buf = str->getChar() & 0xff; |
| bufLen = 8; |
| ++nBytesRead; |
| } |
| while (1) { |
| if (bufLen >= 11 && ((buf >> (bufLen - 7)) & 0x7f) == 0) { |
| if (bufLen <= 12) { |
| code = buf << (12 - bufLen); |
| } else { |
| code = buf >> (bufLen - 12); |
| } |
| p = &whiteTab1[code & 0x1f]; |
| } else { |
| if (bufLen <= 9) { |
| code = buf << (9 - bufLen); |
| } else { |
| code = buf >> (bufLen - 9); |
| } |
| p = &whiteTab2[code & 0x1ff]; |
| } |
| if (p->bits > 0 && p->bits <= (int)bufLen) { |
| bufLen -= p->bits; |
| return p->n; |
| } |
| if (bufLen >= 12) { |
| break; |
| } |
| buf = (buf << 8) | (str->getChar() & 0xff); |
| bufLen += 8; |
| ++nBytesRead; |
| } |
| error(errSyntaxError, str->getPos(), "Bad white code in JBIG2 MMR stream"); |
| // eat a bit and return a positive number so that the caller doesn't |
| // go into an infinite loop |
| --bufLen; |
| return 1; |
| } |
| |
| int JBIG2MMRDecoder::getBlackCode() { |
| const CCITTCode *p; |
| unsigned int code; |
| |
| if (bufLen == 0) { |
| buf = str->getChar() & 0xff; |
| bufLen = 8; |
| ++nBytesRead; |
| } |
| while (1) { |
| if (bufLen >= 10 && ((buf >> (bufLen - 6)) & 0x3f) == 0) { |
| if (bufLen <= 13) { |
| code = buf << (13 - bufLen); |
| } else { |
| code = buf >> (bufLen - 13); |
| } |
| p = &blackTab1[code & 0x7f]; |
| } else if (bufLen >= 7 && ((buf >> (bufLen - 4)) & 0x0f) == 0 && |
| ((buf >> (bufLen - 6)) & 0x03) != 0) { |
| if (bufLen <= 12) { |
| code = buf << (12 - bufLen); |
| } else { |
| code = buf >> (bufLen - 12); |
| } |
| if (unlikely((code & 0xff) < 64)) { |
| break; |
| } |
| p = &blackTab2[(code & 0xff) - 64]; |
| } else { |
| if (bufLen <= 6) { |
| code = buf << (6 - bufLen); |
| } else { |
| code = buf >> (bufLen - 6); |
| } |
| p = &blackTab3[code & 0x3f]; |
| } |
| if (p->bits > 0 && p->bits <= (int)bufLen) { |
| bufLen -= p->bits; |
| return p->n; |
| } |
| if (bufLen >= 13) { |
| break; |
| } |
| buf = (buf << 8) | (str->getChar() & 0xff); |
| bufLen += 8; |
| ++nBytesRead; |
| } |
| error(errSyntaxError, str->getPos(), "Bad black code in JBIG2 MMR stream"); |
| // eat a bit and return a positive number so that the caller doesn't |
| // go into an infinite loop |
| --bufLen; |
| return 1; |
| } |
| |
| unsigned int JBIG2MMRDecoder::get24Bits() { |
| while (bufLen < 24) { |
| buf = (buf << 8) | (str->getChar() & 0xff); |
| bufLen += 8; |
| ++nBytesRead; |
| } |
| return (buf >> (bufLen - 24)) & 0xffffff; |
| } |
| |
| void JBIG2MMRDecoder::skipTo(unsigned int length) { |
| while (nBytesRead < length) { |
| str->getChar(); |
| ++nBytesRead; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // JBIG2Segment |
| //------------------------------------------------------------------------ |
| |
| enum JBIG2SegmentType { |
| jbig2SegBitmap, |
| jbig2SegSymbolDict, |
| jbig2SegPatternDict, |
| jbig2SegCodeTable |
| }; |
| |
| class JBIG2Segment { |
| public: |
| |
| JBIG2Segment(unsigned int segNumA) { segNum = segNumA; } |
| virtual ~JBIG2Segment() {} |
| JBIG2Segment(const JBIG2Segment &) = delete; |
| JBIG2Segment& operator=(const JBIG2Segment &) = delete; |
| void setSegNum(unsigned int segNumA) { segNum = segNumA; } |
| unsigned int getSegNum() { return segNum; } |
| virtual JBIG2SegmentType getType() = 0; |
| |
| private: |
| |
| unsigned int segNum; |
| }; |
| |
| //------------------------------------------------------------------------ |
| // JBIG2Bitmap |
| //------------------------------------------------------------------------ |
| |
| struct JBIG2BitmapPtr { |
| unsigned char *p; |
| int shift; |
| int x; |
| }; |
| |
| class JBIG2Bitmap: public JBIG2Segment { |
| public: |
| |
| JBIG2Bitmap(unsigned int segNumA, int wA, int hA); |
| ~JBIG2Bitmap(); |
| JBIG2SegmentType getType() override { return jbig2SegBitmap; } |
| JBIG2Bitmap *copy() { return new JBIG2Bitmap(0, this); } |
| JBIG2Bitmap *getSlice(unsigned int x, unsigned int y, unsigned int wA, unsigned int hA); |
| void expand(int newH, unsigned int pixel); |
| void clearToZero(); |
| void clearToOne(); |
| int getWidth() { return w; } |
| int getHeight() { return h; } |
| int getLineSize() { return line; } |
| int getPixel(int x, int y) |
| { return (x < 0 || x >= w || y < 0 || y >= h) ? 0 : |
| (data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; } |
| void setPixel(int x, int y) |
| { data[y * line + (x >> 3)] |= 1 << (7 - (x & 7)); } |
| void clearPixel(int x, int y) |
| { data[y * line + (x >> 3)] &= 0x7f7f >> (x & 7); } |
| void getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr); |
| int nextPixel(JBIG2BitmapPtr *ptr); |
| void duplicateRow(int yDest, int ySrc); |
| void combine(JBIG2Bitmap *bitmap, int x, int y, unsigned int combOp); |
| unsigned char *getDataPtr() { return data; } |
| int getDataSize() { return h * line; } |
| bool isOk() { return data != nullptr; } |
| |
| private: |
| |
| JBIG2Bitmap(unsigned int segNumA, JBIG2Bitmap *bitmap); |
| |
| int w, h, line; |
| unsigned char *data; |
| }; |
| |
| JBIG2Bitmap::JBIG2Bitmap(unsigned int segNumA, int wA, int hA): |
| JBIG2Segment(segNumA) |
| { |
| w = wA; |
| h = hA; |
| line = (wA + 7) >> 3; |
| |
| if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) { |
| error(errSyntaxError, -1, "invalid width/height"); |
| data = nullptr; |
| return; |
| } |
| // need to allocate one extra guard byte for use in combine() |
| data = (unsigned char *)gmalloc_checkoverflow(h * line + 1); |
| if (data != nullptr) { |
| data[h * line] = 0; |
| } |
| } |
| |
| JBIG2Bitmap::JBIG2Bitmap(unsigned int segNumA, JBIG2Bitmap *bitmap): |
| JBIG2Segment(segNumA) |
| { |
| if (unlikely(bitmap == nullptr)) { |
| error(errSyntaxError, -1, "NULL bitmap in JBIG2Bitmap"); |
| w = h = line = 0; |
| data = nullptr; |
| return; |
| } |
| |
| w = bitmap->w; |
| h = bitmap->h; |
| line = bitmap->line; |
| |
| if (w <= 0 || h <= 0 || line <= 0 || h >= (INT_MAX - 1) / line) { |
| error(errSyntaxError, -1, "invalid width/height"); |
| data = nullptr; |
| return; |
| } |
| // need to allocate one extra guard byte for use in combine() |
| data = (unsigned char *)gmalloc(h * line + 1); |
| memcpy(data, bitmap->data, h * line); |
| data[h * line] = 0; |
| } |
| |
| JBIG2Bitmap::~JBIG2Bitmap() { |
| gfree(data); |
| } |
| |
| //~ optimize this |
| JBIG2Bitmap *JBIG2Bitmap::getSlice(unsigned int x, unsigned int y, unsigned int wA, unsigned int hA) { |
| JBIG2Bitmap *slice; |
| unsigned int xx, yy; |
| |
| if (!data) { |
| return nullptr; |
| } |
| |
| slice = new JBIG2Bitmap(0, wA, hA); |
| if (slice->isOk()) { |
| slice->clearToZero(); |
| for (yy = 0; yy < hA; ++yy) { |
| for (xx = 0; xx < wA; ++xx) { |
| if (getPixel(x + xx, y + yy)) { |
| slice->setPixel(xx, yy); |
| } |
| } |
| } |
| } else { |
| delete slice; |
| slice = nullptr; |
| } |
| return slice; |
| } |
| |
| void JBIG2Bitmap::expand(int newH, unsigned int pixel) { |
| if (newH <= h || line <= 0 || newH >= (INT_MAX - 1) / line) { |
| error(errSyntaxError, -1, "invalid width/height"); |
| gfree(data); |
| data = nullptr; |
| return; |
| } |
| // need to allocate one extra guard byte for use in combine() |
| data = (unsigned char *)grealloc(data, newH * line + 1); |
| if (pixel) { |
| memset(data + h * line, 0xff, (newH - h) * line); |
| } else { |
| memset(data + h * line, 0x00, (newH - h) * line); |
| } |
| h = newH; |
| data[h * line] = 0; |
| } |
| |
| void JBIG2Bitmap::clearToZero() { |
| memset(data, 0, h * line); |
| } |
| |
| void JBIG2Bitmap::clearToOne() { |
| memset(data, 0xff, h * line); |
| } |
| |
| inline void JBIG2Bitmap::getPixelPtr(int x, int y, JBIG2BitmapPtr *ptr) { |
| if (y < 0 || y >= h || x >= w) { |
| ptr->p = nullptr; |
| ptr->shift = 0; // make gcc happy |
| ptr->x = 0; // make gcc happy |
| } else if (x < 0) { |
| ptr->p = &data[y * line]; |
| ptr->shift = 7; |
| ptr->x = x; |
| } else { |
| ptr->p = &data[y * line + (x >> 3)]; |
| ptr->shift = 7 - (x & 7); |
| ptr->x = x; |
| } |
| } |
| |
| inline int JBIG2Bitmap::nextPixel(JBIG2BitmapPtr *ptr) { |
| int pix; |
| |
| if (!ptr->p) { |
| pix = 0; |
| } else if (ptr->x < 0) { |
| ++ptr->x; |
| pix = 0; |
| } else { |
| pix = (*ptr->p >> ptr->shift) & 1; |
| if (++ptr->x == w) { |
| ptr->p = nullptr; |
| } else if (ptr->shift == 0) { |
| ++ptr->p; |
| ptr->shift = 7; |
| } else { |
| --ptr->shift; |
| } |
| } |
| return pix; |
| } |
| |
| void JBIG2Bitmap::duplicateRow(int yDest, int ySrc) { |
| memcpy(data + yDest * line, data + ySrc * line, line); |
| } |
| |
| void JBIG2Bitmap::combine(JBIG2Bitmap *bitmap, int x, int y, |
| unsigned int combOp) { |
| int x0, x1, y0, y1, xx, yy; |
| unsigned char *srcPtr, *destPtr; |
| unsigned int src0, src1, src, dest, s1, s2, m1, m2, m3; |
| bool oneByte; |
| |
| // check for the pathological case where y = -2^31 |
| if (y < -0x7fffffff) { |
| return; |
| } |
| if (y < 0) { |
| y0 = -y; |
| } else { |
| y0 = 0; |
| } |
| if (y + bitmap->h > h) { |
| y1 = h - y; |
| } else { |
| y1 = bitmap->h; |
| } |
| if (y0 >= y1) { |
| return; |
| } |
| |
| if (x >= 0) { |
| x0 = x & ~7; |
| } else { |
| x0 = 0; |
| } |
| x1 = x + bitmap->w; |
| if (x1 > w) { |
| x1 = w; |
| } |
| if (x0 >= x1) { |
| return; |
| } |
| |
| s1 = x & 7; |
| s2 = 8 - s1; |
| m1 = 0xff >> (x1 & 7); |
| m2 = 0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7)); |
| m3 = (0xff >> s1) & m2; |
| |
| oneByte = x0 == ((x1 - 1) & ~7); |
| |
| for (yy = y0; yy < y1; ++yy) { |
| if (unlikely((y + yy >= h) || (y + yy < 0))) |
| continue; |
| |
| // one byte per line -- need to mask both left and right side |
| if (oneByte) { |
| if (x >= 0) { |
| destPtr = data + (y + yy) * line + (x >> 3); |
| srcPtr = bitmap->data + yy * bitmap->line; |
| dest = *destPtr; |
| src1 = *srcPtr; |
| switch (combOp) { |
| case 0: // or |
| dest |= (src1 >> s1) & m2; |
| break; |
| case 1: // and |
| dest &= ((0xff00 | src1) >> s1) | m1; |
| break; |
| case 2: // xor |
| dest ^= (src1 >> s1) & m2; |
| break; |
| case 3: // xnor |
| dest ^= ((src1 ^ 0xff) >> s1) & m2; |
| break; |
| case 4: // replace |
| dest = (dest & ~m3) | ((src1 >> s1) & m3); |
| break; |
| } |
| *destPtr = dest; |
| } else { |
| destPtr = data + (y + yy) * line; |
| srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); |
| dest = *destPtr; |
| src1 = *srcPtr; |
| switch (combOp) { |
| case 0: // or |
| dest |= src1 & m2; |
| break; |
| case 1: // and |
| dest &= src1 | m1; |
| break; |
| case 2: // xor |
| dest ^= src1 & m2; |
| break; |
| case 3: // xnor |
| dest ^= (src1 ^ 0xff) & m2; |
| break; |
| case 4: // replace |
| dest = (src1 & m2) | (dest & m1); |
| break; |
| } |
| *destPtr = dest; |
| } |
| |
| // multiple bytes per line -- need to mask left side of left-most |
| // byte and right side of right-most byte |
| } else { |
| |
| // left-most byte |
| if (x >= 0) { |
| destPtr = data + (y + yy) * line + (x >> 3); |
| srcPtr = bitmap->data + yy * bitmap->line; |
| src1 = *srcPtr++; |
| dest = *destPtr; |
| switch (combOp) { |
| case 0: // or |
| dest |= src1 >> s1; |
| break; |
| case 1: // and |
| dest &= (0xff00 | src1) >> s1; |
| break; |
| case 2: // xor |
| dest ^= src1 >> s1; |
| break; |
| case 3: // xnor |
| dest ^= (src1 ^ 0xff) >> s1; |
| break; |
| case 4: // replace |
| dest = (dest & (0xff << s2)) | (src1 >> s1); |
| break; |
| } |
| *destPtr++ = dest; |
| xx = x0 + 8; |
| } else { |
| destPtr = data + (y + yy) * line; |
| srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3); |
| src1 = *srcPtr++; |
| xx = x0; |
| } |
| |
| // middle bytes |
| for (; xx < x1 - 8; xx += 8) { |
| dest = *destPtr; |
| src0 = src1; |
| src1 = *srcPtr++; |
| src = (((src0 << 8) | src1) >> s1) & 0xff; |
| switch (combOp) { |
| case 0: // or |
| dest |= src; |
| break; |
| case 1: // and |
| dest &= src; |
| break; |
| case 2: // xor |
| dest ^= src; |
| break; |
| case 3: // xnor |
| dest ^= src ^ 0xff; |
| break; |
| case 4: // replace |
| dest = src; |
| break; |
| } |
| *destPtr++ = dest; |
| } |
| |
| // right-most byte |
| // note: this last byte (src1) may not actually be used, depending |
| // on the values of s1, m1, and m2 - and in fact, it may be off |
| // the edge of the source bitmap, which means we need to allocate |
| // one extra guard byte at the end of each bitmap |
| dest = *destPtr; |
| src0 = src1; |
| src1 = *srcPtr++; |
| src = (((src0 << 8) | src1) >> s1) & 0xff; |
| switch (combOp) { |
| case 0: // or |
| dest |= src & m2; |
| break; |
| case 1: // and |
| dest &= src | m1; |
| break; |
| case 2: // xor |
| dest ^= src & m2; |
| break; |
| case 3: // xnor |
| dest ^= (src ^ 0xff) & m2; |
| break; |
| case 4: // replace |
| dest = (src & m2) | (dest & m1); |
| break; |
| } |
| *destPtr = dest; |
| } |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // JBIG2SymbolDict |
| //------------------------------------------------------------------------ |
| |
| class JBIG2SymbolDict: public JBIG2Segment { |
| public: |
| |
| JBIG2SymbolDict(unsigned int segNumA, unsigned int sizeA); |
| ~JBIG2SymbolDict(); |
| JBIG2SegmentType getType() override { return jbig2SegSymbolDict; } |
| unsigned int getSize() { return size; } |
| void setBitmap(unsigned int idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; } |
| JBIG2Bitmap *getBitmap(unsigned int idx) { return bitmaps[idx]; } |
| bool isOk() { return bitmaps != nullptr; } |
| void setGenericRegionStats(JArithmeticDecoderStats *stats) |
| { genericRegionStats = stats; } |
| void setRefinementRegionStats(JArithmeticDecoderStats *stats) |
| { refinementRegionStats = stats; } |
| JArithmeticDecoderStats *getGenericRegionStats() |
| { return genericRegionStats; } |
| JArithmeticDecoderStats *getRefinementRegionStats() |
| { return refinementRegionStats; } |
| |
| private: |
| |
| unsigned int size; |
| JBIG2Bitmap **bitmaps; |
| JArithmeticDecoderStats *genericRegionStats; |
| JArithmeticDecoderStats *refinementRegionStats; |
| }; |
| |
| JBIG2SymbolDict::JBIG2SymbolDict(unsigned int segNumA, unsigned int sizeA): |
| JBIG2Segment(segNumA) |
| { |
| unsigned int i; |
| |
| size = sizeA; |
| bitmaps = (JBIG2Bitmap **)gmallocn_checkoverflow(size, sizeof(JBIG2Bitmap *)); |
| if (!bitmaps) size = 0; |
| for (i = 0; i < size; ++i) { |
| bitmaps[i] = nullptr; |
| } |
| genericRegionStats = nullptr; |
| refinementRegionStats = nullptr; |
| } |
| |
| JBIG2SymbolDict::~JBIG2SymbolDict() { |
| unsigned int i; |
| |
| for (i = 0; i < size; ++i) { |
| delete bitmaps[i]; |
| } |
| gfree(bitmaps); |
| if (genericRegionStats) { |
| delete genericRegionStats; |
| } |
| if (refinementRegionStats) { |
| delete refinementRegionStats; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // JBIG2PatternDict |
| //------------------------------------------------------------------------ |
| |
| class JBIG2PatternDict: public JBIG2Segment { |
| public: |
| |
| JBIG2PatternDict(unsigned int segNumA, unsigned int sizeA); |
| ~JBIG2PatternDict(); |
| JBIG2SegmentType getType() override { return jbig2SegPatternDict; } |
| unsigned int getSize() { return size; } |
| void setBitmap(unsigned int idx, JBIG2Bitmap *bitmap) { if (likely(idx < size)) bitmaps[idx] = bitmap; } |
| JBIG2Bitmap *getBitmap(unsigned int idx) { return (idx < size) ? bitmaps[idx] : nullptr; } |
| |
| private: |
| |
| unsigned int size; |
| JBIG2Bitmap **bitmaps; |
| }; |
| |
| JBIG2PatternDict::JBIG2PatternDict(unsigned int segNumA, unsigned int sizeA): |
| JBIG2Segment(segNumA) |
| { |
| bitmaps = (JBIG2Bitmap **)gmallocn_checkoverflow(sizeA, sizeof(JBIG2Bitmap *)); |
| if (bitmaps) { |
| size = sizeA; |
| } else { |
| size = 0; |
| error(errSyntaxError, -1, "JBIG2PatternDict: can't allocate bitmaps"); |
| } |
| } |
| |
| JBIG2PatternDict::~JBIG2PatternDict() { |
| unsigned int i; |
| |
| for (i = 0; i < size; ++i) { |
| delete bitmaps[i]; |
| } |
| gfree(bitmaps); |
| } |
| |
| //------------------------------------------------------------------------ |
| // JBIG2CodeTable |
| //------------------------------------------------------------------------ |
| |
| class JBIG2CodeTable: public JBIG2Segment { |
| public: |
| |
| JBIG2CodeTable(unsigned int segNumA, JBIG2HuffmanTable *tableA); |
| ~JBIG2CodeTable(); |
| JBIG2SegmentType getType() override { return jbig2SegCodeTable; } |
| JBIG2HuffmanTable *getHuffTable() { return table; } |
| |
| private: |
| |
| JBIG2HuffmanTable *table; |
| }; |
| |
| JBIG2CodeTable::JBIG2CodeTable(unsigned int segNumA, JBIG2HuffmanTable *tableA): |
| JBIG2Segment(segNumA) |
| { |
| table = tableA; |
| } |
| |
| JBIG2CodeTable::~JBIG2CodeTable() { |
| gfree(table); |
| } |
| |
| //------------------------------------------------------------------------ |
| // JBIG2Stream |
| //------------------------------------------------------------------------ |
| |
| JBIG2Stream::JBIG2Stream(Stream *strA, Object &&globalsStreamA, Object *globalsStreamRefA): |
| FilterStream(strA) |
| { |
| pageBitmap = nullptr; |
| |
| arithDecoder = new JArithmeticDecoder(); |
| genericRegionStats = new JArithmeticDecoderStats(1 << 1); |
| refinementRegionStats = new JArithmeticDecoderStats(1 << 1); |
| iadhStats = new JArithmeticDecoderStats(1 << 9); |
| iadwStats = new JArithmeticDecoderStats(1 << 9); |
| iaexStats = new JArithmeticDecoderStats(1 << 9); |
| iaaiStats = new JArithmeticDecoderStats(1 << 9); |
| iadtStats = new JArithmeticDecoderStats(1 << 9); |
| iaitStats = new JArithmeticDecoderStats(1 << 9); |
| iafsStats = new JArithmeticDecoderStats(1 << 9); |
| iadsStats = new JArithmeticDecoderStats(1 << 9); |
| iardxStats = new JArithmeticDecoderStats(1 << 9); |
| iardyStats = new JArithmeticDecoderStats(1 << 9); |
| iardwStats = new JArithmeticDecoderStats(1 << 9); |
| iardhStats = new JArithmeticDecoderStats(1 << 9); |
| iariStats = new JArithmeticDecoderStats(1 << 9); |
| iaidStats = new JArithmeticDecoderStats(1 << 1); |
| huffDecoder = new JBIG2HuffmanDecoder(); |
| mmrDecoder = new JBIG2MMRDecoder(); |
| |
| if (globalsStreamA.isStream()) { |
| globalsStream = std::move(globalsStreamA); |
| if (globalsStreamRefA->isRef()) |
| globalsStreamRef = globalsStreamRefA->getRef(); |
| } |
| |
| segments = globalSegments = nullptr; |
| curStr = nullptr; |
| dataPtr = dataEnd = nullptr; |
| } |
| |
| JBIG2Stream::~JBIG2Stream() { |
| close(); |
| delete arithDecoder; |
| delete genericRegionStats; |
| delete refinementRegionStats; |
| delete iadhStats; |
| delete iadwStats; |
| delete iaexStats; |
| delete iaaiStats; |
| delete iadtStats; |
| delete iaitStats; |
| delete iafsStats; |
| delete iadsStats; |
| delete iardxStats; |
| delete iardyStats; |
| delete iardwStats; |
| delete iardhStats; |
| delete iariStats; |
| delete iaidStats; |
| delete huffDecoder; |
| delete mmrDecoder; |
| delete str; |
| } |
| |
| void JBIG2Stream::reset() { |
| // read the globals stream |
| globalSegments = new std::vector<JBIG2Segment*>(); |
| if (globalsStream.isStream()) { |
| segments = globalSegments; |
| curStr = globalsStream.getStream(); |
| curStr->reset(); |
| arithDecoder->setStream(curStr); |
| huffDecoder->setStream(curStr); |
| mmrDecoder->setStream(curStr); |
| readSegments(); |
| curStr->close(); |
| } |
| |
| // read the main stream |
| segments = new std::vector<JBIG2Segment*>(); |
| curStr = str; |
| curStr->reset(); |
| arithDecoder->setStream(curStr); |
| huffDecoder->setStream(curStr); |
| mmrDecoder->setStream(curStr); |
| readSegments(); |
| |
| if (pageBitmap) { |
| dataPtr = pageBitmap->getDataPtr(); |
| dataEnd = dataPtr + pageBitmap->getDataSize(); |
| } else { |
| dataPtr = dataEnd = nullptr; |
| } |
| } |
| |
| void JBIG2Stream::close() { |
| if (pageBitmap) { |
| delete pageBitmap; |
| pageBitmap = nullptr; |
| } |
| if (segments) { |
| for (auto entry : *segments) { |
| delete entry; |
| } |
| delete segments; |
| segments = nullptr; |
| } |
| if (globalSegments) { |
| for (auto entry : *globalSegments) { |
| delete entry; |
| } |
| delete globalSegments; |
| globalSegments = nullptr; |
| } |
| dataPtr = dataEnd = nullptr; |
| FilterStream::close(); |
| } |
| |
| int JBIG2Stream::getChar() { |
| if (dataPtr && dataPtr < dataEnd) { |
| return (*dataPtr++ ^ 0xff) & 0xff; |
| } |
| return EOF; |
| } |
| |
| int JBIG2Stream::lookChar() { |
| if (dataPtr && dataPtr < dataEnd) { |
| return (*dataPtr ^ 0xff) & 0xff; |
| } |
| return EOF; |
| } |
| |
| Goffset JBIG2Stream::getPos() { |
| if (pageBitmap == nullptr) { |
| return 0; |
| } |
| return dataPtr - pageBitmap->getDataPtr(); |
| } |
| |
| int JBIG2Stream::getChars(int nChars, unsigned char *buffer) { |
| int n, i; |
| |
| if (nChars <= 0 || !dataPtr) { |
| return 0; |
| } |
| if (dataEnd - dataPtr < nChars) { |
| n = (int)(dataEnd - dataPtr); |
| } else { |
| n = nChars; |
| } |
| for (i = 0; i < n; ++i) { |
| buffer[i] = *dataPtr++ ^ 0xff; |
| } |
| return n; |
| } |
| |
| GooString *JBIG2Stream::getPSFilter(int psLevel, const char *indent) { |
| return nullptr; |
| } |
| |
| bool JBIG2Stream::isBinary(bool last) { |
| return str->isBinary(true); |
| } |
| |
| void JBIG2Stream::readSegments() { |
| unsigned int segNum, segFlags, segType, page, segLength; |
| unsigned int refFlags, nRefSegs; |
| unsigned int *refSegs; |
| Goffset segDataPos; |
| int c1, c2, c3; |
| |
| while (readULong(&segNum)) { |
| |
| // segment header flags |
| if (!readUByte(&segFlags)) { |
| goto eofError1; |
| } |
| segType = segFlags & 0x3f; |
| |
| // referred-to segment count and retention flags |
| if (!readUByte(&refFlags)) { |
| goto eofError1; |
| } |
| nRefSegs = refFlags >> 5; |
| if (nRefSegs == 7) { |
| if ((c1 = curStr->getChar()) == EOF || |
| (c2 = curStr->getChar()) == EOF || |
| (c3 = curStr->getChar()) == EOF) { |
| goto eofError1; |
| } |
| refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3; |
| nRefSegs = refFlags & 0x1fffffff; |
| for (unsigned int i = 0; i < (nRefSegs + 9) >> 3; ++i) { |
| if ((c1 = curStr->getChar()) == EOF) { |
| goto eofError1; |
| } |
| } |
| } |
| |
| // referred-to segment numbers |
| refSegs = (unsigned int *)gmallocn(nRefSegs, sizeof(unsigned int)); |
| if (segNum <= 256) { |
| for (unsigned int i = 0; i < nRefSegs; ++i) { |
| if (!readUByte(&refSegs[i])) { |
| goto eofError2; |
| } |
| } |
| } else if (segNum <= 65536) { |
| for (unsigned int i = 0; i < nRefSegs; ++i) { |
| if (!readUWord(&refSegs[i])) { |
| goto eofError2; |
| } |
| } |
| } else { |
| for (unsigned int i = 0; i < nRefSegs; ++i) { |
| if (!readULong(&refSegs[i])) { |
| goto eofError2; |
| } |
| } |
| } |
| |
| // segment page association |
| if (segFlags & 0x40) { |
| if (!readULong(&page)) { |
| goto eofError2; |
| } |
| } else { |
| if (!readUByte(&page)) { |
| goto eofError2; |
| } |
| } |
| |
| // segment data length |
| if (!readULong(&segLength)) { |
| goto eofError2; |
| } |
| |
| // keep track of the start of the segment data |
| segDataPos = curStr->getPos(); |
| |
| // check for missing page information segment |
| if (!pageBitmap && ((segType >= 4 && segType <= 7) || |
| (segType >= 20 && segType <= 43))) { |
| error(errSyntaxError, curStr->getPos(), "First JBIG2 segment associated with a page must be a page information segment"); |
| goto syntaxError; |
| } |
| |
| // read the segment data |
| switch (segType) { |
| case 0: |
| if (!readSymbolDictSeg(segNum, segLength, refSegs, nRefSegs)) { |
| goto syntaxError; |
| } |
| break; |
| case 4: |
| readTextRegionSeg(segNum, false, false, segLength, refSegs, nRefSegs); |
| break; |
| case 6: |
| readTextRegionSeg(segNum, true, false, segLength, refSegs, nRefSegs); |
| break; |
| case 7: |
| readTextRegionSeg(segNum, true, true, segLength, refSegs, nRefSegs); |
| break; |
| case 16: |
| readPatternDictSeg(segNum, segLength); |
| break; |
| case 20: |
| readHalftoneRegionSeg(segNum, false, false, segLength, |
| refSegs, nRefSegs); |
| break; |
| case 22: |
| readHalftoneRegionSeg(segNum, true, false, segLength, |
| refSegs, nRefSegs); |
| break; |
| case 23: |
| readHalftoneRegionSeg(segNum, true, true, segLength, |
| refSegs, nRefSegs); |
| break; |
| case 36: |
| readGenericRegionSeg(segNum, false, false, segLength); |
| break; |
| case 38: |
| readGenericRegionSeg(segNum, true, false, segLength); |
| break; |
| case 39: |
| readGenericRegionSeg(segNum, true, true, segLength); |
| break; |
| case 40: |
| readGenericRefinementRegionSeg(segNum, false, false, segLength, |
| refSegs, nRefSegs); |
| break; |
| case 42: |
| readGenericRefinementRegionSeg(segNum, true, false, segLength, |
| refSegs, nRefSegs); |
| break; |
| case 43: |
| readGenericRefinementRegionSeg(segNum, true, true, segLength, |
| refSegs, nRefSegs); |
| break; |
| case 48: |
| readPageInfoSeg(segLength); |
| break; |
| case 50: |
| readEndOfStripeSeg(segLength); |
| break; |
| case 52: |
| readProfilesSeg(segLength); |
| break; |
| case 53: |
| readCodeTableSeg(segNum, segLength); |
| break; |
| case 62: |
| readExtensionSeg(segLength); |
| break; |
| default: |
| error(errSyntaxError, curStr->getPos(), "Unknown segment type in JBIG2 stream"); |
| for (unsigned int i = 0; i < segLength; ++i) { |
| if ((c1 = curStr->getChar()) == EOF) { |
| goto eofError2; |
| } |
| } |
| break; |
| } |
| |
| // Make sure the segment handler read all of the bytes in the |
| // segment data, unless this segment is marked as having an |
| // unknown length (section 7.2.7 of the JBIG2 Final Committee Draft) |
| |
| if (segLength != 0xffffffff) { |
| |
| Goffset segExtraBytes = segDataPos + segLength - curStr->getPos(); |
| if (segExtraBytes > 0) { |
| |
| // If we didn't read all of the bytes in the segment data, |
| // indicate an error, and throw away the rest of the data. |
| |
| // v.3.1.01.13 of the LuraTech PDF Compressor Server will |
| // sometimes generate an extraneous NULL byte at the end of |
| // arithmetic-coded symbol dictionary segments when numNewSyms |
| // == 0. Segments like this often occur for blank pages. |
| |
| error(errSyntaxError, curStr->getPos(), "{0:lld} extraneous byte{1:s} after segment", |
| segExtraBytes, (segExtraBytes > 1) ? "s" : ""); |
| |
| // Burn through the remaining bytes -- inefficient, but |
| // hopefully we're not doing this much |
| |
| int trash; |
| for (Goffset i = segExtraBytes; i > 0; i--) { |
| readByte(&trash); |
| } |
| |
| } else if (segExtraBytes < 0) { |
| |
| // If we read more bytes than we should have, according to the |
| // segment length field, note an error. |
| |
| error(errSyntaxError, curStr->getPos(), "Previous segment handler read too many bytes"); |
| |
| } |
| |
| } |
| |
| gfree(refSegs); |
| } |
| |
| return; |
| |
| syntaxError: |
| gfree(refSegs); |
| return; |
| |
| eofError2: |
| gfree(refSegs); |
| eofError1: |
| error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); |
| } |
| |
| bool JBIG2Stream::readSymbolDictSeg(unsigned int segNum, unsigned int length, |
| unsigned int *refSegs, unsigned int nRefSegs) { |
| JBIG2SymbolDict *symbolDict; |
| JBIG2HuffmanTable *huffDHTable, *huffDWTable; |
| JBIG2HuffmanTable *huffBMSizeTable, *huffAggInstTable; |
| JBIG2Segment *seg; |
| std::vector<JBIG2Segment*> *codeTables; |
| JBIG2SymbolDict *inputSymbolDict; |
| unsigned int flags, sdTemplate, sdrTemplate, huff, refAgg; |
| unsigned int huffDH, huffDW, huffBMSize, huffAggInst; |
| unsigned int contextUsed, contextRetained; |
| int sdATX[4], sdATY[4], sdrATX[2], sdrATY[2]; |
| unsigned int numExSyms, numNewSyms, numInputSyms, symCodeLen; |
| JBIG2Bitmap **bitmaps; |
| JBIG2Bitmap *collBitmap, *refBitmap; |
| unsigned int *symWidths; |
| unsigned int symHeight, symWidth, totalWidth, x, symID; |
| int dh = 0, dw, refAggNum, refDX = 0, refDY = 0, bmSize; |
| bool ex; |
| int run, cnt, c; |
| unsigned int i, j, k; |
| unsigned char *p; |
| |
| symWidths = nullptr; |
| |
| // symbol dictionary flags |
| if (!readUWord(&flags)) { |
| goto eofError; |
| } |
| sdTemplate = (flags >> 10) & 3; |
| sdrTemplate = (flags >> 12) & 1; |
| huff = flags & 1; |
| refAgg = (flags >> 1) & 1; |
| huffDH = (flags >> 2) & 3; |
| huffDW = (flags >> 4) & 3; |
| huffBMSize = (flags >> 6) & 1; |
| huffAggInst = (flags >> 7) & 1; |
| contextUsed = (flags >> 8) & 1; |
| contextRetained = (flags >> 9) & 1; |
| |
| // symbol dictionary AT flags |
| if (!huff) { |
| if (sdTemplate == 0) { |
| if (!readByte(&sdATX[0]) || |
| !readByte(&sdATY[0]) || |
| !readByte(&sdATX[1]) || |
| !readByte(&sdATY[1]) || |
| !readByte(&sdATX[2]) || |
| !readByte(&sdATY[2]) || |
| !readByte(&sdATX[3]) || |
| !readByte(&sdATY[3])) { |
| goto eofError; |
| } |
| } else { |
| if (!readByte(&sdATX[0]) || |
| !readByte(&sdATY[0])) { |
| goto eofError; |
| } |
| } |
| } |
| |
| // symbol dictionary refinement AT flags |
| if (refAgg && !sdrTemplate) { |
| if (!readByte(&sdrATX[0]) || |
| !readByte(&sdrATY[0]) || |
| !readByte(&sdrATX[1]) || |
| !readByte(&sdrATY[1])) { |
| goto eofError; |
| } |
| } |
| |
| // SDNUMEXSYMS and SDNUMNEWSYMS |
| if (!readULong(&numExSyms) || !readULong(&numNewSyms)) { |
| goto eofError; |
| } |
| |
| // get referenced segments: input symbol dictionaries and code tables |
| codeTables = new std::vector<JBIG2Segment*>(); |
| numInputSyms = 0; |
| for (i = 0; i < nRefSegs; ++i) { |
| // This is need by bug 12014, returning false makes it not crash |
| // but we end up with a empty page while acroread is able to render |
| // part of it |
| if ((seg = findSegment(refSegs[i]))) { |
| if (seg->getType() == jbig2SegSymbolDict) { |
| j = ((JBIG2SymbolDict *)seg)->getSize(); |
| if (numInputSyms > UINT_MAX - j) { |
| error(errSyntaxError, curStr->getPos(), "Too many input symbols in JBIG2 symbol dictionary"); |
| delete codeTables; |
| goto eofError; |
| } |
| numInputSyms += j; |
| } else if (seg->getType() == jbig2SegCodeTable) { |
| codeTables->push_back(seg); |
| } |
| } else { |
| delete codeTables; |
| return false; |
| } |
| } |
| if (numInputSyms > UINT_MAX - numNewSyms) { |
| error(errSyntaxError, curStr->getPos(), "Too many input symbols in JBIG2 symbol dictionary"); |
| delete codeTables; |
| goto eofError; |
| } |
| |
| // compute symbol code length, per 6.5.8.2.3 |
| // symCodeLen = ceil( log2( numInputSyms + numNewSyms ) ) |
| i = numInputSyms + numNewSyms; |
| if (i <= 1) { |
| symCodeLen = huff ? 1 : 0; |
| } else { |
| --i; |
| symCodeLen = 0; |
| // i = floor((numSyms-1) / 2^symCodeLen) |
| while (i > 0) { |
| ++symCodeLen; |
| i >>= 1; |
| } |
| } |
| |
| // get the input symbol bitmaps |
| bitmaps = (JBIG2Bitmap **)gmallocn_checkoverflow(numInputSyms + numNewSyms, |
| sizeof(JBIG2Bitmap *)); |
| if (!bitmaps && (numInputSyms + numNewSyms > 0)) { |
| error(errSyntaxError, curStr->getPos(), "Too many input symbols in JBIG2 symbol dictionary"); |
| delete codeTables; |
| goto eofError; |
| } |
| for (i = 0; i < numInputSyms + numNewSyms; ++i) { |
| bitmaps[i] = nullptr; |
| } |
| k = 0; |
| inputSymbolDict = nullptr; |
| for (i = 0; i < nRefSegs; ++i) { |
| seg = findSegment(refSegs[i]); |
| if (seg != nullptr && seg->getType() == jbig2SegSymbolDict) { |
| inputSymbolDict = (JBIG2SymbolDict *)seg; |
| for (j = 0; j < inputSymbolDict->getSize(); ++j) { |
| bitmaps[k++] = inputSymbolDict->getBitmap(j); |
| } |
| } |
| } |
| |
| // get the Huffman tables |
| huffDHTable = huffDWTable = nullptr; // make gcc happy |
| huffBMSizeTable = huffAggInstTable = nullptr; // make gcc happy |
| i = 0; |
| if (huff) { |
| if (huffDH == 0) { |
| huffDHTable = huffTableD; |
| } else if (huffDH == 1) { |
| huffDHTable = huffTableE; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffDHTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffDW == 0) { |
| huffDWTable = huffTableB; |
| } else if (huffDW == 1) { |
| huffDWTable = huffTableC; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffDWTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffBMSize == 0) { |
| huffBMSizeTable = huffTableA; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffBMSizeTable = |
| ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffAggInst == 0) { |
| huffAggInstTable = huffTableA; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffAggInstTable = |
| ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| } |
| delete codeTables; |
| |
| // set up the Huffman decoder |
| if (huff) { |
| huffDecoder->reset(); |
| |
| // set up the arithmetic decoder |
| } else { |
| if (contextUsed && inputSymbolDict) { |
| resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats()); |
| } else { |
| resetGenericStats(sdTemplate, nullptr); |
| } |
| resetIntStats(symCodeLen); |
| arithDecoder->start(); |
| } |
| |
| // set up the arithmetic decoder for refinement/aggregation |
| if (refAgg) { |
| if (contextUsed && inputSymbolDict) { |
| resetRefinementStats(sdrTemplate, |
| inputSymbolDict->getRefinementRegionStats()); |
| } else { |
| resetRefinementStats(sdrTemplate, nullptr); |
| } |
| } |
| |
| // allocate symbol widths storage |
| if (huff && !refAgg) { |
| symWidths = (unsigned int *)gmallocn(numNewSyms, sizeof(unsigned int)); |
| } |
| |
| symHeight = 0; |
| i = 0; |
| while (i < numNewSyms) { |
| |
| // read the height class delta height |
| if (huff) { |
| huffDecoder->decodeInt(&dh, huffDHTable); |
| } else { |
| arithDecoder->decodeInt(&dh, iadhStats); |
| } |
| if (dh < 0 && (unsigned int)-dh >= symHeight) { |
| error(errSyntaxError, curStr->getPos(), "Bad delta-height value in JBIG2 symbol dictionary"); |
| goto syntaxError; |
| } |
| symHeight += dh; |
| if (unlikely(symHeight > 0x40000000)) { |
| error(errSyntaxError, curStr->getPos(), "Bad height value in JBIG2 symbol dictionary"); |
| goto syntaxError; |
| } |
| symWidth = 0; |
| totalWidth = 0; |
| j = i; |
| |
| // read the symbols in this height class |
| while (1) { |
| |
| // read the delta width |
| if (huff) { |
| if (!huffDecoder->decodeInt(&dw, huffDWTable)) { |
| break; |
| } |
| } else { |
| if (!arithDecoder->decodeInt(&dw, iadwStats)) { |
| break; |
| } |
| } |
| if (dw < 0 && (unsigned int)-dw >= symWidth) { |
| error(errSyntaxError, curStr->getPos(), "Bad delta-height value in JBIG2 symbol dictionary"); |
| goto syntaxError; |
| } |
| symWidth += dw; |
| if (i >= numNewSyms) { |
| error(errSyntaxError, curStr->getPos(), "Too many symbols in JBIG2 symbol dictionary"); |
| goto syntaxError; |
| } |
| |
| // using a collective bitmap, so don't read a bitmap here |
| if (huff && !refAgg) { |
| symWidths[i] = symWidth; |
| totalWidth += symWidth; |
| |
| // refinement/aggregate coding |
| } else if (refAgg) { |
| if (huff) { |
| if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) { |
| break; |
| } |
| } else { |
| if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) { |
| break; |
| } |
| } |
| #if 0 //~ This special case was added about a year before the final draft |
| //~ of the JBIG2 spec was released. I have encountered some old |
| //~ JBIG2 images that predate it. |
| if (0) { |
| #else |
| if (refAggNum == 1) { |
| #endif |
| if (huff) { |
| symID = huffDecoder->readBits(symCodeLen); |
| huffDecoder->decodeInt(&refDX, huffTableO); |
| huffDecoder->decodeInt(&refDY, huffTableO); |
| huffDecoder->decodeInt(&bmSize, huffTableA); |
| huffDecoder->reset(); |
| arithDecoder->start(); |
| } else { |
| symID = arithDecoder->decodeIAID(symCodeLen, iaidStats); |
| arithDecoder->decodeInt(&refDX, iardxStats); |
| arithDecoder->decodeInt(&refDY, iardyStats); |
| } |
| if (symID >= numInputSyms + i) { |
| error(errSyntaxError, curStr->getPos(), "Invalid symbol ID in JBIG2 symbol dictionary"); |
| goto syntaxError; |
| } |
| refBitmap = bitmaps[symID]; |
| if (unlikely(refBitmap == nullptr)) { |
| error(errSyntaxError, curStr->getPos(), "Invalid ref bitmap for symbol ID {0:ud} in JBIG2 symbol dictionary", symID); |
| goto syntaxError; |
| } |
| bitmaps[numInputSyms + i] = |
| readGenericRefinementRegion(symWidth, symHeight, |
| sdrTemplate, false, |
| refBitmap, refDX, refDY, |
| sdrATX, sdrATY); |
| //~ do we need to use the bmSize value here (in Huffman mode)? |
| } else { |
| bitmaps[numInputSyms + i] = |
| readTextRegion(huff, true, symWidth, symHeight, |
| refAggNum, 0, numInputSyms + i, nullptr, |
| symCodeLen, bitmaps, 0, 0, 0, 1, 0, |
| huffTableF, huffTableH, huffTableK, huffTableO, |
| huffTableO, huffTableO, huffTableO, huffTableA, |
| sdrTemplate, sdrATX, sdrATY); |
| } |
| |
| // non-ref/agg coding |
| } else { |
| bitmaps[numInputSyms + i] = |
| readGenericBitmap(false, symWidth, symHeight, |
| sdTemplate, false, false, nullptr, |
| sdATX, sdATY, 0); |
| } |
| |
| ++i; |
| } |
| |
| // read the collective bitmap |
| if (huff && !refAgg) { |
| huffDecoder->decodeInt(&bmSize, huffBMSizeTable); |
| huffDecoder->reset(); |
| if (bmSize == 0) { |
| collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight); |
| bmSize = symHeight * ((totalWidth + 7) >> 3); |
| p = collBitmap->getDataPtr(); |
| if (unlikely(p == nullptr)) { |
| delete collBitmap; |
| goto syntaxError; |
| } |
| for (k = 0; k < (unsigned int)bmSize; ++k) { |
| if ((c = curStr->getChar()) == EOF) { |
| memset(p, 0, bmSize - k); |
| break; |
| } |
| *p++ = (unsigned char)c; |
| } |
| } else { |
| collBitmap = readGenericBitmap(true, totalWidth, symHeight, |
| 0, false, false, nullptr, nullptr, nullptr, |
| bmSize); |
| } |
| if (likely(collBitmap != nullptr)) { |
| x = 0; |
| for (; j < i; ++j) { |
| bitmaps[numInputSyms + j] = |
| collBitmap->getSlice(x, 0, symWidths[j], symHeight); |
| x += symWidths[j]; |
| } |
| delete collBitmap; |
| } else { |
| error(errSyntaxError, curStr->getPos(), "collBitmap was null"); |
| goto syntaxError; |
| } |
| } |
| } |
| |
| // create the symbol dict object |
| symbolDict = new JBIG2SymbolDict(segNum, numExSyms); |
| if (!symbolDict->isOk()) { |
| delete symbolDict; |
| goto syntaxError; |
| } |
| |
| // exported symbol list |
| i = j = 0; |
| ex = false; |
| run = 0; // initialize it once in case the first decodeInt fails |
| // we do not want to use uninitialized memory |
| while (i < numInputSyms + numNewSyms) { |
| if (huff) { |
| huffDecoder->decodeInt(&run, huffTableA); |
| } else { |
| arithDecoder->decodeInt(&run, iaexStats); |
| } |
| if (i + run > numInputSyms + numNewSyms || |
| (ex && j + run > numExSyms)) { |
| error(errSyntaxError, curStr->getPos(), "Too many exported symbols in JBIG2 symbol dictionary"); |
| for ( ; j < numExSyms; ++j) symbolDict->setBitmap(j, nullptr); |
| delete symbolDict; |
| goto syntaxError; |
| } |
| if (ex) { |
| for (cnt = 0; cnt < run; ++cnt) { |
| symbolDict->setBitmap(j++, bitmaps[i++]->copy()); |
| } |
| } else { |
| i += run; |
| } |
| ex = !ex; |
| } |
| if (j != numExSyms) { |
| error(errSyntaxError, curStr->getPos(), "Too few symbols in JBIG2 symbol dictionary"); |
| for ( ; j < numExSyms; ++j) symbolDict->setBitmap(j, nullptr); |
| delete symbolDict; |
| goto syntaxError; |
| } |
| |
| for (i = 0; i < numNewSyms; ++i) { |
| delete bitmaps[numInputSyms + i]; |
| } |
| gfree(bitmaps); |
| if (symWidths) { |
| gfree(symWidths); |
| } |
| |
| // save the arithmetic decoder stats |
| if (!huff && contextRetained) { |
| symbolDict->setGenericRegionStats(genericRegionStats->copy()); |
| if (refAgg) { |
| symbolDict->setRefinementRegionStats(refinementRegionStats->copy()); |
| } |
| } |
| |
| // store the new symbol dict |
| segments->push_back(symbolDict); |
| |
| return true; |
| |
| codeTableError: |
| error(errSyntaxError, curStr->getPos(), "Missing code table in JBIG2 symbol dictionary"); |
| delete codeTables; |
| |
| syntaxError: |
| for (i = 0; i < numNewSyms; ++i) { |
| if (bitmaps[numInputSyms + i]) { |
| delete bitmaps[numInputSyms + i]; |
| } |
| } |
| gfree(bitmaps); |
| if (symWidths) { |
| gfree(symWidths); |
| } |
| return false; |
| |
| eofError: |
| error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); |
| return false; |
| } |
| |
| void JBIG2Stream::readTextRegionSeg(unsigned int segNum, bool imm, |
| bool lossless, unsigned int length, |
| unsigned int *refSegs, unsigned int nRefSegs) { |
| JBIG2Bitmap *bitmap; |
| JBIG2HuffmanTable runLengthTab[36]; |
| JBIG2HuffmanTable *symCodeTab = nullptr; |
| JBIG2HuffmanTable *huffFSTable, *huffDSTable, *huffDTTable; |
| JBIG2HuffmanTable *huffRDWTable, *huffRDHTable; |
| JBIG2HuffmanTable *huffRDXTable, *huffRDYTable, *huffRSizeTable; |
| JBIG2Segment *seg; |
| std::vector<JBIG2Segment*> *codeTables; |
| JBIG2SymbolDict *symbolDict; |
| JBIG2Bitmap **syms; |
| unsigned int w, h, x, y, segInfoFlags, extCombOp; |
| unsigned int flags, huff, refine, logStrips, refCorner, transposed; |
| unsigned int combOp, defPixel, templ; |
| int sOffset; |
| unsigned int huffFlags, huffFS, huffDS, huffDT; |
| unsigned int huffRDW, huffRDH, huffRDX, huffRDY, huffRSize; |
| unsigned int numInstances, numSyms, symCodeLen; |
| int atx[2], aty[2]; |
| unsigned int i, k, kk; |
| int j = 0; |
| |
| // region segment info field |
| if (!readULong(&w) || !readULong(&h) || |
| !readULong(&x) || !readULong(&y) || |
| !readUByte(&segInfoFlags)) { |
| goto eofError; |
| } |
| extCombOp = segInfoFlags & 7; |
| |
| // rest of the text region header |
| if (!readUWord(&flags)) { |
| goto eofError; |
| } |
| huff = flags & 1; |
| refine = (flags >> 1) & 1; |
| logStrips = (flags >> 2) & 3; |
| refCorner = (flags >> 4) & 3; |
| transposed = (flags >> 6) & 1; |
| combOp = (flags >> 7) & 3; |
| defPixel = (flags >> 9) & 1; |
| sOffset = (flags >> 10) & 0x1f; |
| if (sOffset & 0x10) { |
| sOffset |= -1 - 0x0f; |
| } |
| templ = (flags >> 15) & 1; |
| huffFS = huffDS = huffDT = 0; // make gcc happy |
| huffRDW = huffRDH = huffRDX = huffRDY = huffRSize = 0; // make gcc happy |
| if (huff) { |
| if (!readUWord(&huffFlags)) { |
| goto eofError; |
| } |
| huffFS = huffFlags & 3; |
| huffDS = (huffFlags >> 2) & 3; |
| huffDT = (huffFlags >> 4) & 3; |
| huffRDW = (huffFlags >> 6) & 3; |
| huffRDH = (huffFlags >> 8) & 3; |
| huffRDX = (huffFlags >> 10) & 3; |
| huffRDY = (huffFlags >> 12) & 3; |
| huffRSize = (huffFlags >> 14) & 1; |
| } |
| if (refine && templ == 0) { |
| if (!readByte(&atx[0]) || !readByte(&aty[0]) || |
| !readByte(&atx[1]) || !readByte(&aty[1])) { |
| goto eofError; |
| } |
| } |
| if (!readULong(&numInstances)) { |
| goto eofError; |
| } |
| |
| // get symbol dictionaries and tables |
| codeTables = new std::vector<JBIG2Segment*>(); |
| numSyms = 0; |
| for (i = 0; i < nRefSegs; ++i) { |
| if ((seg = findSegment(refSegs[i]))) { |
| if (seg->getType() == jbig2SegSymbolDict) { |
| numSyms += ((JBIG2SymbolDict *)seg)->getSize(); |
| } else if (seg->getType() == jbig2SegCodeTable) { |
| codeTables->push_back(seg); |
| } |
| } else { |
| error(errSyntaxError, curStr->getPos(), "Invalid segment reference in JBIG2 text region"); |
| delete codeTables; |
| return; |
| } |
| } |
| i = numSyms; |
| if (i <= 1) { |
| symCodeLen = huff ? 1 : 0; |
| } else { |
| --i; |
| symCodeLen = 0; |
| // i = floor((numSyms-1) / 2^symCodeLen) |
| while (i > 0) { |
| ++symCodeLen; |
| i >>= 1; |
| } |
| } |
| |
| // get the symbol bitmaps |
| syms = (JBIG2Bitmap **)gmallocn(numSyms, sizeof(JBIG2Bitmap *)); |
| kk = 0; |
| for (i = 0; i < nRefSegs; ++i) { |
| if ((seg = findSegment(refSegs[i]))) { |
| if (seg->getType() == jbig2SegSymbolDict) { |
| symbolDict = (JBIG2SymbolDict *)seg; |
| for (k = 0; k < symbolDict->getSize(); ++k) { |
| syms[kk++] = symbolDict->getBitmap(k); |
| } |
| } |
| } |
| } |
| |
| // get the Huffman tables |
| huffFSTable = huffDSTable = huffDTTable = nullptr; // make gcc happy |
| huffRDWTable = huffRDHTable = nullptr; // make gcc happy |
| huffRDXTable = huffRDYTable = huffRSizeTable = nullptr; // make gcc happy |
| i = 0; |
| if (huff) { |
| if (huffFS == 0) { |
| huffFSTable = huffTableF; |
| } else if (huffFS == 1) { |
| huffFSTable = huffTableG; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffFSTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffDS == 0) { |
| huffDSTable = huffTableH; |
| } else if (huffDS == 1) { |
| huffDSTable = huffTableI; |
| } else if (huffDS == 2) { |
| huffDSTable = huffTableJ; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffDSTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffDT == 0) { |
| huffDTTable = huffTableK; |
| } else if (huffDT == 1) { |
| huffDTTable = huffTableL; |
| } else if (huffDT == 2) { |
| huffDTTable = huffTableM; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffDTTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffRDW == 0) { |
| huffRDWTable = huffTableN; |
| } else if (huffRDW == 1) { |
| huffRDWTable = huffTableO; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffRDWTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffRDH == 0) { |
| huffRDHTable = huffTableN; |
| } else if (huffRDH == 1) { |
| huffRDHTable = huffTableO; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffRDHTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffRDX == 0) { |
| huffRDXTable = huffTableN; |
| } else if (huffRDX == 1) { |
| huffRDXTable = huffTableO; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffRDXTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffRDY == 0) { |
| huffRDYTable = huffTableN; |
| } else if (huffRDY == 1) { |
| huffRDYTable = huffTableO; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffRDYTable = ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| if (huffRSize == 0) { |
| huffRSizeTable = huffTableA; |
| } else { |
| if (i >= codeTables->size()) { |
| goto codeTableError; |
| } |
| huffRSizeTable = |
| ((JBIG2CodeTable *)(*codeTables)[i++])->getHuffTable(); |
| } |
| } |
| delete codeTables; |
| |
| // symbol ID Huffman decoding table |
| if (huff) { |
| huffDecoder->reset(); |
| for (i = 0; i < 32; ++i) { |
| runLengthTab[i].val = i; |
| runLengthTab[i].prefixLen = huffDecoder->readBits(4); |
| runLengthTab[i].rangeLen = 0; |
| } |
| runLengthTab[32].val = 0x103; |
| runLengthTab[32].prefixLen = huffDecoder->readBits(4); |
| runLengthTab[32].rangeLen = 2; |
| runLengthTab[33].val = 0x203; |
| runLengthTab[33].prefixLen = huffDecoder->readBits(4); |
| runLengthTab[33].rangeLen = 3; |
| runLengthTab[34].val = 0x20b; |
| runLengthTab[34].prefixLen = huffDecoder->readBits(4); |
| runLengthTab[34].rangeLen = 7; |
| runLengthTab[35].prefixLen = 0; |
| runLengthTab[35].rangeLen = jbig2HuffmanEOT; |
| if (!JBIG2HuffmanDecoder::buildTable(runLengthTab, 35)) { |
| huff = false; |
| } |
| } |
| |
| if (huff) { |
| symCodeTab = (JBIG2HuffmanTable *)gmallocn(numSyms + 1, |
| sizeof(JBIG2HuffmanTable)); |
| for (i = 0; i < numSyms; ++i) { |
| symCodeTab[i].val = i; |
| symCodeTab[i].rangeLen = 0; |
| } |
| i = 0; |
| while (i < numSyms) { |
| huffDecoder->decodeInt(&j, runLengthTab); |
| if (j > 0x200) { |
| for (j -= 0x200; j && i < numSyms; --j) { |
| symCodeTab[i++].prefixLen = 0; |
| } |
| } else if (j > 0x100) { |
| if (unlikely(i == 0)) ++i; |
| for (j -= 0x100; j && i < numSyms; --j) { |
| symCodeTab[i].prefixLen = symCodeTab[i-1].prefixLen; |
| ++i; |
| } |
| } else { |
| symCodeTab[i++].prefixLen = j; |
| } |
| } |
| symCodeTab[numSyms].prefixLen = 0; |
| symCodeTab[numSyms].rangeLen = jbig2HuffmanEOT; |
| if (!JBIG2HuffmanDecoder::buildTable(symCodeTab, numSyms)) { |
| huff = false; |
| gfree(symCodeTab); |
| symCodeTab = nullptr; |
| } |
| huffDecoder->reset(); |
| |
| // set up the arithmetic decoder |
| } |
| |
| if (!huff) { |
| resetIntStats(symCodeLen); |
| arithDecoder->start(); |
| } |
| if (refine) { |
| resetRefinementStats(templ, nullptr); |
| } |
| |
| bitmap = readTextRegion(huff, refine, w, h, numInstances, |
| logStrips, numSyms, symCodeTab, symCodeLen, syms, |
| defPixel, combOp, transposed, refCorner, sOffset, |
| huffFSTable, huffDSTable, huffDTTable, |
| huffRDWTable, huffRDHTable, |
| huffRDXTable, huffRDYTable, huffRSizeTable, |
| templ, atx, aty); |
| |
| gfree(syms); |
| |
| if (bitmap) { |
| // combine the region bitmap into the page bitmap |
| if (imm) { |
| if (pageH == 0xffffffff && y + h > curPageH) { |
| pageBitmap->expand(y + h, pageDefPixel); |
| } |
| pageBitmap->combine(bitmap, x, y, extCombOp); |
| delete bitmap; |
| |
| // store the region bitmap |
| } else { |
| bitmap->setSegNum(segNum); |
| segments->push_back(bitmap); |
| } |
| } |
| |
| // clean up the Huffman decoder |
| if (huff) { |
| gfree(symCodeTab); |
| } |
| |
| return; |
| |
| codeTableError: |
| error(errSyntaxError, curStr->getPos(), "Missing code table in JBIG2 text region"); |
| delete codeTables; |
| gfree(syms); |
| return; |
| |
| eofError: |
| error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); |
| return; |
| } |
| |
| JBIG2Bitmap *JBIG2Stream::readTextRegion(bool huff, bool refine, |
| int w, int h, |
| unsigned int numInstances, |
| unsigned int logStrips, |
| int numSyms, |
| JBIG2HuffmanTable *symCodeTab, |
| unsigned int symCodeLen, |
| JBIG2Bitmap **syms, |
| unsigned int defPixel, unsigned int combOp, |
| unsigned int transposed, unsigned int refCorner, |
| int sOffset, |
| JBIG2HuffmanTable *huffFSTable, |
| JBIG2HuffmanTable *huffDSTable, |
| JBIG2HuffmanTable *huffDTTable, |
| JBIG2HuffmanTable *huffRDWTable, |
| JBIG2HuffmanTable *huffRDHTable, |
| JBIG2HuffmanTable *huffRDXTable, |
| JBIG2HuffmanTable *huffRDYTable, |
| JBIG2HuffmanTable *huffRSizeTable, |
| unsigned int templ, |
| int *atx, int *aty) { |
| JBIG2Bitmap *bitmap; |
| JBIG2Bitmap *symbolBitmap; |
| unsigned int strips; |
| int t = 0, dt = 0, tt, s, ds = 0, sFirst, j = 0; |
| int rdw, rdh, rdx, rdy, ri = 0, refDX, refDY, bmSize; |
| unsigned int symID, inst, bw, bh; |
| |
| strips = 1 << logStrips; |
| |
| // allocate the bitmap |
| bitmap = new JBIG2Bitmap(0, w, h); |
| if (!bitmap->isOk()) { |
| delete bitmap; |
| return nullptr; |
| } |
| if (defPixel) { |
| bitmap->clearToOne(); |
| } else { |
| bitmap->clearToZero(); |
| } |
| |
| // decode initial T value |
| if (huff) { |
| huffDecoder->decodeInt(&t, huffDTTable); |
| } else { |
| arithDecoder->decodeInt(&t, iadtStats); |
| } |
| t *= -(int)strips; |
| |
| inst = 0; |
| sFirst = 0; |
| while (inst < numInstances) { |
| |
| // decode delta-T |
| if (huff) { |
| huffDecoder->decodeInt(&dt, huffDTTable); |
| } else { |
| arithDecoder->decodeInt(&dt, iadtStats); |
| } |
| t += dt * strips; |
| |
| // first S value |
| if (huff) { |
| huffDecoder->decodeInt(&ds, huffFSTable); |
| } else { |
| arithDecoder->decodeInt(&ds, iafsStats); |
| } |
| sFirst += ds; |
| s = sFirst; |
| |
| // read the instances |
| // (this loop test is here to avoid an infinite loop with damaged |
| // JBIG2 streams where the normal loop exit doesn't get triggered) |
| while (inst < numInstances) { |
| |
| // T value |
| if (strips == 1) { |
| dt = 0; |
| } else if (huff) { |
| dt = huffDecoder->readBits(logStrips); |
| } else { |
| arithDecoder->decodeInt(&dt, iaitStats); |
| } |
| tt = t + dt; |
| |
| // symbol ID |
| if (huff) { |
| if (symCodeTab) { |
| huffDecoder->decodeInt(&j, symCodeTab); |
| symID = (unsigned int)j; |
| } else { |
| symID = huffDecoder->readBits(symCodeLen); |
| } |
| } else { |
| symID = arithDecoder->decodeIAID(symCodeLen, iaidStats); |
| } |
| |
| if (symID >= (unsigned int)numSyms) { |
| error(errSyntaxError, curStr->getPos(), "Invalid symbol number in JBIG2 text region"); |
| if (unlikely(numInstances - inst > 0x800)) { |
| // don't loop too often with damaged JBIg2 streams |
| delete bitmap; |
| return nullptr; |
| } |
| } else { |
| |
| // get the symbol bitmap |
| symbolBitmap = nullptr; |
| if (refine) { |
| if (huff) { |
| ri = (int)huffDecoder->readBit(); |
| } else { |
| arithDecoder->decodeInt(&ri, iariStats); |
| } |
| } else { |
| ri = 0; |
| } |
| if (ri) { |
| bool decodeSuccess; |
| if (huff) { |
| decodeSuccess = huffDecoder->decodeInt(&rdw, huffRDWTable); |
| decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&rdh, huffRDHTable); |
| decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&rdx, huffRDXTable); |
| decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&rdy, huffRDYTable); |
| decodeSuccess = decodeSuccess && huffDecoder->decodeInt(&bmSize, huffRSizeTable); |
| huffDecoder->reset(); |
| arithDecoder->start(); |
| } else { |
| decodeSuccess = arithDecoder->decodeInt(&rdw, iardwStats); |
| decodeSuccess = decodeSuccess && arithDecoder->decodeInt(&rdh, iardhStats); |
| decodeSuccess = decodeSuccess && arithDecoder->decodeInt(&rdx, iardxStats); |
| decodeSuccess = decodeSuccess && arithDecoder->decodeInt(&rdy, iardyStats); |
| } |
| |
| if (decodeSuccess && syms[symID]) |
| { |
| refDX = ((rdw >= 0) ? rdw : rdw - 1) / 2 + rdx; |
| refDY = ((rdh >= 0) ? rdh : rdh - 1) / 2 + rdy; |
| |
| symbolBitmap = |
| readGenericRefinementRegion(rdw + syms[symID]->getWidth(), |
| rdh + syms[symID]->getHeight(), |
| templ, false, syms[symID], |
| refDX, refDY, atx, aty); |
| } |
| //~ do we need to use the bmSize value here (in Huffman mode)? |
| } else { |
| symbolBitmap = syms[symID]; |
| } |
| |
| if (symbolBitmap) { |
| // combine the symbol bitmap into the region bitmap |
| //~ something is wrong here - refCorner shouldn't degenerate into |
| //~ two cases |
| bw = symbolBitmap->getWidth() - 1; |
| if (unlikely(symbolBitmap->getHeight() == 0)) { |
| error(errSyntaxError, curStr->getPos(), "Invalid symbol bitmap height"); |
| if (ri) { |
| delete symbolBitmap; |
| } |
| delete bitmap; |
| return nullptr; |
| } |
| bh = symbolBitmap->getHeight() - 1; |
| if (transposed) { |
| if (unlikely(s > 2 * bitmap->getHeight())) { |
| error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); |
| if (ri) { |
| delete symbolBitmap; |
| } |
| delete bitmap; |
| return nullptr; |
| } |
| switch (refCorner) { |
| case 0: // bottom left |
| bitmap->combine(symbolBitmap, tt, s, combOp); |
| break; |
| case 1: // top left |
| bitmap->combine(symbolBitmap, tt, s, combOp); |
| break; |
| case 2: // bottom right |
| bitmap->combine(symbolBitmap, tt - bw, s, combOp); |
| break; |
| case 3: // top right |
| bitmap->combine(symbolBitmap, tt - bw, s, combOp); |
| break; |
| } |
| s += bh; |
| } else { |
| switch (refCorner) { |
| case 0: // bottom left |
| if (unlikely(tt - (int) bh > 2 * bitmap->getHeight())) { |
| error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); |
| if (ri) { |
| delete symbolBitmap; |
| } |
| delete bitmap; |
| return nullptr; |
| } |
| bitmap->combine(symbolBitmap, s, tt - bh, combOp); |
| break; |
| case 1: // top left |
| if (unlikely(tt > 2 * bitmap->getHeight())) { |
| error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); |
| if (ri) { |
| delete symbolBitmap; |
| } |
| delete bitmap; |
| return nullptr; |
| } |
| bitmap->combine(symbolBitmap, s, tt, combOp); |
| break; |
| case 2: // bottom right |
| if (unlikely(tt - (int) bh > 2 * bitmap->getHeight())) { |
| error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); |
| if (ri) { |
| delete symbolBitmap; |
| } |
| delete bitmap; |
| return nullptr; |
| } |
| bitmap->combine(symbolBitmap, s, tt - bh, combOp); |
| break; |
| case 3: // top right |
| if (unlikely(tt > 2 * bitmap->getHeight())) { |
| error(errSyntaxError, curStr->getPos(), "Invalid JBIG2 combine"); |
| if (ri) { |
| delete symbolBitmap; |
| } |
| delete bitmap; |
| return nullptr; |
| } |
| bitmap->combine(symbolBitmap, s, tt, combOp); |
| break; |
| } |
| s += bw; |
| } |
| if (ri) { |
| delete symbolBitmap; |
| } |
| } else { |
| // NULL symbolBitmap only happens on error |
| delete bitmap; |
| return nullptr; |
| } |
| } |
| |
| // next instance |
| ++inst; |
| |
| // next S value |
| if (huff) { |
| if (!huffDecoder->decodeInt(&ds, huffDSTable)) { |
| break; |
| } |
| } else { |
| if (!arithDecoder->decodeInt(&ds, iadsStats)) { |
| break; |
| } |
| } |
| if (checkedAdd(s, sOffset + ds, &s)) { |
| delete bitmap; |
| return nullptr; |
| } |
| } |
| } |
| |
| return bitmap; |
| } |
| |
| void JBIG2Stream::readPatternDictSeg(unsigned int segNum, unsigned int length) { |
| JBIG2PatternDict *patternDict; |
| JBIG2Bitmap *bitmap; |
| unsigned int flags, patternW, patternH, grayMax, templ, mmr; |
| int atx[4], aty[4]; |
| unsigned int i, x; |
| |
| // halftone dictionary flags, pattern width and height, max gray value |
| if (!readUByte(&flags) || |
| !readUByte(&patternW) || |
| !readUByte(&patternH) || |
| !readULong(&grayMax)) { |
| goto eofError; |
| } |
| templ = (flags >> 1) & 3; |
| mmr = flags & 1; |
| |
| // set up the arithmetic decoder |
| if (!mmr) { |
| resetGenericStats(templ, nullptr); |
| arithDecoder->start(); |
| } |
| |
| // read the bitmap |
| atx[0] = -(int)patternW; aty[0] = 0; |
| atx[1] = -3; aty[1] = -1; |
| atx[2] = 2; aty[2] = -2; |
| atx[3] = -2; aty[3] = -2; |
| bitmap = readGenericBitmap(mmr, (grayMax + 1) * patternW, patternH, |
| templ, false, false, nullptr, |
| atx, aty, length - 7); |
| |
| if (!bitmap) |
| return; |
| |
| // create the pattern dict object |
| patternDict = new JBIG2PatternDict(segNum, grayMax + 1); |
| |
| // split up the bitmap |
| x = 0; |
| for (i = 0; i <= grayMax && i < patternDict->getSize(); ++i) { |
| patternDict->setBitmap(i, bitmap->getSlice(x, 0, patternW, patternH)); |
| x += patternW; |
| } |
| |
| // free memory |
| delete bitmap; |
| |
| // store the new pattern dict |
| segments->push_back(patternDict); |
| |
| return; |
| |
| eofError: |
| error(errSyntaxError, curStr->getPos(), "Unexpected EOF in JBIG2 stream"); |
| } |
| |
| void JBIG2Stream::readHalftoneRegionSeg(unsigned int segNum, bool imm, |
| bool lossless, unsigned int length, |
| unsigned int *refSegs, unsigned int nRefSegs) { |
| JBIG2Bitmap *bitmap; |
|