diff --git a/PdfForQtLib/sources/pdfccittfaxdecoder.h b/PdfForQtLib/sources/pdfccittfaxdecoder.h index c863fdb..d172bb6 100644 --- a/PdfForQtLib/sources/pdfccittfaxdecoder.h +++ b/PdfForQtLib/sources/pdfccittfaxdecoder.h @@ -55,7 +55,7 @@ struct PDFCCITTFaxDecoderParameters /// to achieve byte alignment. bool hasEncodedByteAlign = false; - /// Flag indicating, that filter excepts the data be terminated by end of block bit pattern. + /// Flag indicating, that filter expects the data be terminated by end of block bit pattern. /// In this case, \p rows parameter is ignored. Otherwise, rows parameter is used, or image /// is terminated by end of data stream, whichever occurs first. The end of block is marked /// as end-of-facsimile block (EOFB), or return to control (RTC), according the K parameter. diff --git a/PdfForQtLib/sources/pdfjbig2decoder.cpp b/PdfForQtLib/sources/pdfjbig2decoder.cpp index a76d0db..e5a4c37 100644 --- a/PdfForQtLib/sources/pdfjbig2decoder.cpp +++ b/PdfForQtLib/sources/pdfjbig2decoder.cpp @@ -17,6 +17,7 @@ #include "pdfjbig2decoder.h" #include "pdfexception.h" +#include "pdfccittfaxdecoder.h" namespace pdf { @@ -606,12 +607,12 @@ PDFImageData PDFJBIG2Decoder::decode(PDFImageData::MaskingType maskingType) { PDFBitWriter writer(1); - const size_t columns = m_pageBitmap.getWidth(); - const size_t rows = m_pageBitmap.getHeight(); + const int columns = m_pageBitmap.getWidth(); + const int rows = m_pageBitmap.getHeight(); - for (size_t row = 0; row < rows; ++row) + for (int row = 0; row < rows; ++row) { - for (size_t column = 0; column < columns; ++column) + for (int column = 0; column < columns; ++column) { writer.write(m_pageBitmap.getPixel(column, row)); } @@ -734,7 +735,87 @@ void PDFJBIG2Decoder::processHalftoneRegion(const PDFJBIG2SegmentHeader& header) void PDFJBIG2Decoder::processGenericRegion(const PDFJBIG2SegmentHeader& header) { - // TODO: JBIG2 - processGenericRegion + const int segmentStartPosition = m_reader.getPosition(); + PDFJBIG2RegionSegmentInformationField field = readRegionSegmentInformationField(); + const uint8_t flags = m_reader.readUnsignedByte(); + + PDFJBIG2BitmapDecodingParameters parameters; + parameters.MMR = flags & 0b0001; + parameters.TPGDON = flags & 0b1000; + parameters.GBTEMPLATE = (flags >> 1) & 0b0011; + + if ((flags & 0b11110000) != 0) + { + throw PDFException(PDFTranslationContext::tr("JBIG2 - malformed generic region flags.")); + } + + if (!parameters.MMR) + { + // We will use arithmetic coding, read template pixels and reset arithmetic coder state + parameters.ATXY = readATTemplatePixelPositions((parameters.GBTEMPLATE == 0) ? 4 : 1); + resetArithmeticStatesGeneric(parameters.GBTEMPLATE); + } + + // Determine segment data length + const int segmentDataStartPosition = m_reader.getPosition(); + const int segmentHeaderBytes = segmentDataStartPosition - segmentStartPosition; + int segmentDataBytes = 0; + if (header.isSegmentDataLengthDefined()) + { + segmentDataBytes = header.getSegmentDataLength() - segmentHeaderBytes; + } + else + { + // We must find byte sequence { 0x00, 0x00 } for MMR and { 0xFF, 0xAC } for arithmetic decoder + const QByteArray* stream = m_reader.getStream(); + + QByteArray endSequence(2, 0); + if (!parameters.MMR) + { + endSequence[0] = char(0xFF); + endSequence[1] = char(0xAC); + } + + int endPosition = stream->indexOf(endSequence); + if (endPosition == -1) + { + throw PDFException(PDFTranslationContext::tr("JBIG2 - end of data byte sequence not found for generic region.")); + } + + // Add end bytes (they are also a part of stream) + endPosition += endSequence.size(); + + segmentDataBytes = endPosition - segmentDataStartPosition; + } + parameters.data = m_reader.getStream()->mid(segmentDataStartPosition, segmentDataBytes); + parameters.width = field.width; + parameters.height = field.height; + parameters.arithmeticDecoderState = &m_arithmeticDecoderStates[Generic]; + + PDFJBIG2Bitmap bitmap = readBitmap(parameters); + if (bitmap.isValid()) + { + if (header.isImmediate()) + { + m_pageBitmap.paint(bitmap, field.offsetX, field.offsetY, field.operation, m_pageSizeUndefined, m_pageDefaultPixelValue); + } + else + { + m_segments[header.getSegmentNumber()] = std::make_unique(qMove(bitmap)); + } + } + else + { + throw PDFException(PDFTranslationContext::tr("JBIG2 - invalid bitmap for generic region.")); + } + + // Now skip the data + m_reader.skipBytes(segmentDataBytes); + + if (header.isImmediate() && !header.isSegmentDataLengthDefined()) + { + m_reader.skipBytes(4); + } } void PDFJBIG2Decoder::processGenericRefinementRegion(const PDFJBIG2SegmentHeader& header) @@ -782,6 +863,7 @@ void PDFJBIG2Decoder::processPageInformation(const PDFJBIG2SegmentHeader&) const uint32_t correctedWidth = width; const uint32_t correctedHeight = (height != 0xFFFFFFFF) ? height : 0; + m_pageSizeUndefined = height == 0xFFFFFFFF; checkBitmapSize(correctedWidth); checkBitmapSize(correctedHeight); @@ -901,6 +983,289 @@ void PDFJBIG2Decoder::processExtension(const PDFJBIG2SegmentHeader& header) } } +PDFJBIG2Bitmap PDFJBIG2Decoder::readBitmap(const PDFJBIG2BitmapDecodingParameters& parameters) +{ + if (parameters.MMR) + { + // Use modified-modified-read (it corresponds to CCITT 2D encoding) + PDFCCITTFaxDecoderParameters ccittParameters; + ccittParameters.K = -1; + ccittParameters.columns = parameters.width; + ccittParameters.rows = parameters.height; + ccittParameters.hasEndOfBlock = false; + + PDFCCITTFaxDecoder decoder(¶meters.data, ccittParameters); + PDFImageData data = decoder.decode(); + + PDFJBIG2Bitmap bitmap(data.getWidth(), data.getHeight(), m_pageDefaultPixelValue); + + // Copy the data + PDFBitReader reader(&data.getData(), data.getBitsPerComponent()); + for (unsigned int row = 0; row < data.getHeight(); ++row) + { + for (unsigned int column = 0; column < data.getWidth(); ++column) + { + bitmap.setPixel(column, row, (reader.read()) ? 0xFF : 0x00); + } + + reader.alignToBytes(); + } + + return bitmap; + } + else + { + // Use arithmetic encoding. For templates, we fill bytes from right to left, from bottom to top bits, + // filling from lowest bit to highest bit. We will have a maximum of 16 bits. + + uint8_t LTP = 0; + uint16_t LTPContext = 0; + if (parameters.TPGDON) + { + switch (parameters.GBTEMPLATE) + { + case 0: + LTPContext = 0b1010010011011001; // 16-bit context, hexadecimal value is 0x9B25 + break; + + case 1: + LTPContext = 0b0011110010101; // 13-bit context, hexadecimal value is 0x0795 + break; + + case 2: + LTPContext = 0b0011100101; // 10-bit context, hexadecimal value is 0x00E5 + break; + + case 3: + LTPContext = 0b0110010101; // 10-bit context, hexadecimal value is 0x0195 + break; + + default: + Q_ASSERT(false); + break; + } + } + + PDFBitReader reader(¶meters.data, 1); + PDFJBIG2ArithmeticDecoder decoder(&reader); + decoder.initialize(); + + PDFJBIG2Bitmap bitmap(parameters.width, parameters.height, 0x00); + for (int y = 0; y < parameters.height; ++y) + { + // Check TPGDON prediction - if we use same pixels as in previous line + if (parameters.TPGDON) + { + LTP = LTP ^ decoder.readBit(LTPContext, parameters.arithmeticDecoderState); + if (LTP) + { + if (y > 0) + { + bitmap.copyRow(y, y - 1); + } + continue; + } + } + + for (int x = 0; x < parameters.width; ++x) + { + // Check, if we have to skip pixel. Pixel should be set to 0, but it is done + // in the initialization of the bitmap. + if (parameters.SKIP && parameters.SKIP->getPixelSafe(x, y)) + { + continue; + } + + uint16_t pixelContext = 0; + uint16_t pixelContextShift = 0; + auto createContextBit = [&](int offsetX, int offsetY) + { + uint16_t bit = bitmap.getPixelSafe(offsetX, offsetY) ? 1 : 0; + bit = bit << pixelContextShift; + pixelContext |= bit; + ++pixelContextShift; + }; + + // Create pixel context based on used template + switch (parameters.GBTEMPLATE) + { + case 0: + { + // 16-bit context + createContextBit(x - 1, y); + createContextBit(x - 2, y); + createContextBit(x - 3, y); + createContextBit(x - 4, y); + createContextBit(x + parameters.ATXY[0].x, y + parameters.ATXY[0].y); + createContextBit(x + 2, y - 1); + createContextBit(x + 1, y - 1); + createContextBit(x + 0, y - 1); + createContextBit(x - 1, y - 1); + createContextBit(x - 2, y - 1); + createContextBit(x + parameters.ATXY[1].x, y + parameters.ATXY[1].y); + createContextBit(x + parameters.ATXY[2].x, y + parameters.ATXY[2].y); + createContextBit(x + 1, y - 2); + createContextBit(x + 0, y - 2); + createContextBit(x - 1, y - 2); + createContextBit(x + parameters.ATXY[3].x, y + parameters.ATXY[3].y); + break; + } + + case 1: + { + // 13-bit context + createContextBit(x - 1, y); + createContextBit(x - 2, y); + createContextBit(x - 3, y); + createContextBit(x + parameters.ATXY[0].x, y + parameters.ATXY[0].y); + createContextBit(x + 2, y - 1); + createContextBit(x + 1, y - 1); + createContextBit(x + 0, y - 1); + createContextBit(x - 1, y - 1); + createContextBit(x - 2, y - 1); + createContextBit(x + 2, y - 2); + createContextBit(x + 1, y - 2); + createContextBit(x + 0, y - 2); + createContextBit(x - 1, y - 2); + break; + } + + case 2: + { + // 10-bit context + createContextBit(x - 1, y); + createContextBit(x - 2, y); + createContextBit(x + parameters.ATXY[0].x, y + parameters.ATXY[0].y); + createContextBit(x + 1, y - 1); + createContextBit(x + 0, y - 1); + createContextBit(x - 1, y - 1); + createContextBit(x - 2, y - 1); + createContextBit(x + 1, y - 2); + createContextBit(x + 0, y - 2); + createContextBit(x - 1, y - 2); + break; + } + + case 3: + { + // 10-bit context + createContextBit(x - 1, y); + createContextBit(x - 2, y); + createContextBit(x - 3, y); + createContextBit(x - 4, y); + createContextBit(x + parameters.ATXY[0].x, y + parameters.ATXY[0].y); + createContextBit(x + 1, y - 1); + createContextBit(x + 0, y - 1); + createContextBit(x - 1, y - 1); + createContextBit(x - 2, y - 1); + createContextBit(x - 3, y - 1); + break; + } + + default: + { + Q_ASSERT(false); + break; + } + } + + bitmap.setPixel(x, y, (decoder.readBit(pixelContext, parameters.arithmeticDecoderState)) ? 0xFF : 0x00); + } + } + + return bitmap; + } + + return PDFJBIG2Bitmap(); +} + +PDFJBIG2RegionSegmentInformationField PDFJBIG2Decoder::readRegionSegmentInformationField() +{ + PDFJBIG2RegionSegmentInformationField result; + + result.width = m_reader.readUnsignedInt(); + result.height = m_reader.readUnsignedInt(); + result.offsetX = m_reader.readUnsignedInt(); + result.offsetY = m_reader.readUnsignedInt(); + + // Parse flags + const uint8_t flags = m_reader.readUnsignedByte(); + + if ((flags & 0b11111000) != 0) + { + // This is forbidden by the specification + throw PDFException(PDFTranslationContext::tr("JBIG2 region segment information flags are invalid.")); + } + + switch (flags) + { + case 0: + result.operation = PDFJBIG2BitOperation::Or; + break; + + case 1: + result.operation = PDFJBIG2BitOperation::And; + break; + + case 2: + result.operation = PDFJBIG2BitOperation::Xor; + break; + + case 3: + result.operation = PDFJBIG2BitOperation::NotXor; + break; + + case 4: + result.operation = PDFJBIG2BitOperation::Replace; + break; + + default: + throw PDFException(PDFTranslationContext::tr("JBIG2 region segment information - invalid bit operation mode.")); + } + + checkRegionSegmentInformationField(result); + return result; +} + +PDFJBIG2ATPositions PDFJBIG2Decoder::readATTemplatePixelPositions(int count) +{ + PDFJBIG2ATPositions result = { }; + + for (int i = 0; i < count; ++i) + { + result[i].x = m_reader.readSignedByte(); + result[i].y = m_reader.readSignedByte(); + } + + return result; +} + +void PDFJBIG2Decoder::resetArithmeticStatesGeneric(const uint8_t templateMode) +{ + uint8_t bits = 0; + switch (templateMode) + { + case 0: + bits = 16; + break; + + case 1: + bits = 13; + break; + + case 2: + case 3: + bits = 10; + break; + + default: + Q_ASSERT(false); + break; + } + + m_arithmeticDecoderStates[Generic].reset(bits); +} + void PDFJBIG2Decoder::skipSegment(const PDFJBIG2SegmentHeader& header) { if (header.isSegmentDataLengthDefined()) @@ -921,6 +1286,24 @@ void PDFJBIG2Decoder::checkBitmapSize(const uint32_t size) } } +void PDFJBIG2Decoder::checkRegionSegmentInformationField(const PDFJBIG2RegionSegmentInformationField& field) +{ + checkBitmapSize(field.width); + checkBitmapSize(field.height); + checkBitmapSize(field.offsetX); + checkBitmapSize(field.offsetY); + + if (field.width == 0 || field.height == 0) + { + throw PDFException(PDFTranslationContext::tr("JBIG2 invalid bitmap size (%1 x %2).").arg(field.width).arg(field.height)); + } + + if (field.operation == PDFJBIG2BitOperation::Invalid) + { + throw PDFException(PDFTranslationContext::tr("JBIG2 invalid bit operation.")); + } +} + PDFJBIG2Bitmap::PDFJBIG2Bitmap() : m_width(0), m_height(0) @@ -928,20 +1311,94 @@ PDFJBIG2Bitmap::PDFJBIG2Bitmap() : } -PDFJBIG2Bitmap::PDFJBIG2Bitmap(size_t width, size_t height) : +PDFJBIG2Bitmap::PDFJBIG2Bitmap(int width, int height) : m_width(width), m_height(height) { m_data.resize(width * height, 0); } -PDFJBIG2Bitmap::PDFJBIG2Bitmap(size_t width, size_t height, uint8_t fill) : +PDFJBIG2Bitmap::PDFJBIG2Bitmap(int width, int height, uint8_t fill) : m_width(width), m_height(height) { m_data.resize(width * height, fill); } +void PDFJBIG2Bitmap::paint(const PDFJBIG2Bitmap& bitmap, int offsetX, int offsetY, PDFJBIG2BitOperation operation, bool expandY, const uint8_t expandPixel) +{ + if (!bitmap.isValid()) + { + return; + } + + // Expand, if it is allowed and target bitmap has too low height + if (expandY && offsetY + bitmap.getHeight() > m_height) + { + m_height = offsetY + bitmap.getHeight(); + m_data.resize(getPixelCount(), expandPixel); + } + + // Check out pathological cases + if (offsetX >= m_width || offsetY >= m_height) + { + return; + } + + const int targetStartX = offsetX; + const int targetEndX = qMin(offsetX + bitmap.getWidth(), m_width); + const int targetStartY = offsetY; + const int targetEndY = qMin(offsetY + bitmap.getHeight(), m_height); + + for (int targetY = targetStartY; targetY < targetEndY; ++targetY) + { + for (int targetX = targetStartX; targetX < targetEndX; ++targetX) + { + const int sourceX = targetX - targetStartX; + const int sourceY = targetY - targetStartY; + + switch (operation) + { + case PDFJBIG2BitOperation::Or: + setPixel(targetX, targetY, getPixel(targetX, targetY) | bitmap.getPixel(sourceX, sourceY)); + break; + + case PDFJBIG2BitOperation::And: + setPixel(targetX, targetY, getPixel(targetX, targetY) & bitmap.getPixel(sourceX, sourceY)); + break; + + case PDFJBIG2BitOperation::Xor: + setPixel(targetX, targetY, getPixel(targetX, targetY) ^ bitmap.getPixel(sourceX, sourceY)); + break; + + case PDFJBIG2BitOperation::NotXor: + setPixel(targetX, targetY, getPixel(targetX, targetY) ^ (~bitmap.getPixel(sourceX, sourceY))); + break; + + case PDFJBIG2BitOperation::Replace: + setPixel(targetX, targetY, bitmap.getPixel(sourceX, sourceY)); + break; + + default: + throw PDFException(PDFTranslationContext::tr("JBIG2 - invalid bitmap paint operation.")); + } + } + } +} + +void PDFJBIG2Bitmap::copyRow(int target, int source) +{ + if (target < 0 || target >= m_height || source < 0 || source >= m_height) + { + throw PDFException(PDFTranslationContext::tr("JBIG2 - invalid bitmap copy row operation.")); + } + + auto itSource = std::next(m_data.cbegin(), source * m_width); + auto itSourceEnd = std::next(itSource, m_width); + auto itTarget = std::next(m_data.begin(), target * m_width); + std::copy(itSource, itSourceEnd, itTarget); +} + PDFJBIG2HuffmanCodeTable::PDFJBIG2HuffmanCodeTable(std::vector&& entries) : m_entries(qMove(entries)) { diff --git a/PdfForQtLib/sources/pdfjbig2decoder.h b/PdfForQtLib/sources/pdfjbig2decoder.h index 21458e4..9ec5e9d 100644 --- a/PdfForQtLib/sources/pdfjbig2decoder.h +++ b/PdfForQtLib/sources/pdfjbig2decoder.h @@ -35,7 +35,8 @@ enum class PDFJBIG2BitOperation Or, And, Xor, - NotXor + NotXor, + Replace }; /// Arithmetic decoder state for JBIG2 data streams. It contains state for context, @@ -45,12 +46,24 @@ enum class PDFJBIG2BitOperation class PDFJBIG2ArithmeticDecoderState { public: + explicit inline PDFJBIG2ArithmeticDecoderState() = default; explicit inline PDFJBIG2ArithmeticDecoderState(size_t size) : m_state(size, 0) { } + /// Resets the context + inline void reset(const uint8_t bits) + { + size_t size = (1ULL << bits); + std::fill(m_state.begin(), m_state.end(), 0); + if (m_state.size() != size) + { + m_state.resize(size, 0); + } + } + /// Returns row index to Qe value table, according to document ISO/IEC 14492:2001, /// annex E, table E.1 (Qe values and probability estimation process). inline uint8_t getQeRowIndex(size_t context) const @@ -86,16 +99,17 @@ private: class PDFJBIG2ArithmeticDecoder { public: - explicit inline PDFJBIG2ArithmeticDecoder() : + explicit inline PDFJBIG2ArithmeticDecoder(PDFBitReader* reader) : m_c(0), m_a(0), m_ct(0), - m_reader(nullptr) + m_reader(reader) { } void initialize() { perform_INITDEC(); } + uint32_t readBit(size_t context, PDFJBIG2ArithmeticDecoderState* state) { return perform_DECODE(context, state); } private: /// Performs INITDEC operation as described in the specification @@ -220,17 +234,27 @@ class PDFJBIG2Bitmap : public PDFJBIG2Segment { public: explicit PDFJBIG2Bitmap(); - explicit PDFJBIG2Bitmap(size_t width, size_t height); - explicit PDFJBIG2Bitmap(size_t width, size_t height, uint8_t fill); + explicit PDFJBIG2Bitmap(int width, int height); + explicit PDFJBIG2Bitmap(int width, int height, uint8_t fill); virtual const PDFJBIG2Bitmap* asBitmap() const override { return this; } virtual PDFJBIG2Bitmap* asBitmap() override { return this; } - inline size_t getWidth() const { return m_width; } - inline size_t getHeight() const { return m_height; } - inline size_t getPixelCount() const { return m_width * m_height; } - inline uint8_t getPixel(size_t x, size_t y) const { return m_data[y * m_width + x]; } - inline void setPixel(size_t x, size_t y, uint8_t value) { m_data[y * m_width + x] = value; } + inline int getWidth() const { return m_width; } + inline int getHeight() const { return m_height; } + inline int getPixelCount() const { return m_width * m_height; } + inline uint8_t getPixel(int x, int y) const { return m_data[y * m_width + x]; } + inline void setPixel(int x, int y, uint8_t value) { m_data[y * m_width + x] = value; } + + inline uint8_t getPixelSafe(int x, int y) const + { + if (x < 0 || x >= m_width || y < 0 || y >= m_height) + { + return 0; + } + + return getPixel(x, y); + } inline void fill(uint8_t value) { std::fill(m_data.begin(), m_data.end(), value); } inline void fillZero() { fill(0); } @@ -238,12 +262,79 @@ public: inline bool isValid() const { return getPixelCount() > 0; } + /// Paints another bitmap onto this bitmap. If bitmap is invalid, nothing is done. + /// If \p expandY is true, height of target bitmap is expanded to fit source draw area. + /// \param bitmap Bitmap to be painted on this + /// \param offsetX Horizontal offset of paint area + /// \param offsetY Vertical offset of paint area + /// \param operation Paint operation to be performed + /// \param expandY Expand vertically, if painted bitmap exceeds current bitmap area + /// \param expandPixel Initialize pixels by this value during expanding + void paint(const PDFJBIG2Bitmap& bitmap, int offsetX, int offsetY, PDFJBIG2BitOperation operation, bool expandY, const uint8_t expandPixel); + + /// Copies data from source row to target row. If source or target row doesn't exists, + /// then exception is thrown. + /// \param target Target row + /// \param source Source row + void copyRow(int target, int source); + private: - size_t m_width; - size_t m_height; + int m_width; + int m_height; std::vector m_data; }; +/// Region segment information field, see chapter 7.4.1 in the specification +struct PDFJBIG2RegionSegmentInformationField +{ + uint32_t width = 0; + uint32_t height = 0; + uint32_t offsetX = 0; + uint32_t offsetY = 0; + PDFJBIG2BitOperation operation = PDFJBIG2BitOperation::Invalid; +}; + +/// Info structure for adaptative template +struct PDFJBIG2ATPosition +{ + int8_t x = 0; + int8_t y = 0; +}; + +using PDFJBIG2ATPositions = std::array; + +/// Info structure for bitmap decoding parameters +struct PDFJBIG2BitmapDecodingParameters +{ + /// Is Modified-Modified-Read encoding used? This encoding is simalr to CCITT pure 2D encoding. + bool MMR = false; + + /// Is typical prediction for generic direct coding used? + bool TPGDON = false; + + /// Width of the image + int width = 0; + + /// Height of the image + int height = 0; + + /// Template mode (not used for MMR). + uint8_t GBTEMPLATE = 0; + + /// Positions of adaptative pixels + PDFJBIG2ATPositions ATXY = { }; + + /// Data with encoded image + QByteArray data; + + /// State of arithmetic decoder + PDFJBIG2ArithmeticDecoderState* arithmeticDecoderState = nullptr; + + /// Skip bitmap (pixel is skipped if corresponding pixel in the + /// skip bitmap is 1). Set to nullptr, if not used. + const PDFJBIG2Bitmap* SKIP = nullptr; +}; + /// Decoder of JBIG2 data streams. Decodes the black/white monochrome image. /// Handles also global segments. Decoder decodes data using the specification /// ISO/IEC 14492:2001, T.88. @@ -257,7 +348,9 @@ public: m_reader(nullptr, 8), m_pageDefaultPixelValue(0), m_pageDefaultCompositionOperator(PDFJBIG2BitOperation::Invalid), - m_pageDefaultCompositionOperatorOverriden(false) + m_pageDefaultCompositionOperatorOverriden(false), + m_pageSizeUndefined(false), + m_arithmeticDecoderStates() { } @@ -270,6 +363,12 @@ public: private: static constexpr const uint32_t MAX_BITMAP_SIZE = 65536; + enum ArithmeticDecoderStates + { + Generic, + EndState + }; + /// Processes current data stream (reads all data from the stream, interprets /// them as segments and processes the segments). void processStream(); @@ -288,21 +387,35 @@ private: void processCodeTables(const PDFJBIG2SegmentHeader& header); void processExtension(const PDFJBIG2SegmentHeader& header); + /// Reads the bitmap using decoding parameters + /// \param parameters Decoding parameters + PDFJBIG2Bitmap readBitmap(const PDFJBIG2BitmapDecodingParameters& parameters); + + /// Reads the region segment information field (see chapter 7.4.1) + PDFJBIG2RegionSegmentInformationField readRegionSegmentInformationField(); + + /// Read adaptative pixel template positions, positions, which are not read, are filled with 0 + PDFJBIG2ATPositions readATTemplatePixelPositions(int count); + + /// Reset arithmetic decoder stats for generic + void resetArithmeticStatesGeneric(const uint8_t templateMode); + void skipSegment(const PDFJBIG2SegmentHeader& header); static void checkBitmapSize(const uint32_t size); + static void checkRegionSegmentInformationField(const PDFJBIG2RegionSegmentInformationField& field); QByteArray m_data; QByteArray m_globalData; PDFRenderErrorReporter* m_errorReporter; PDFBitReader m_reader; std::map> m_segments; - - /// Page default pixel value uint8_t m_pageDefaultPixelValue; PDFJBIG2BitOperation m_pageDefaultCompositionOperator; bool m_pageDefaultCompositionOperatorOverriden; + bool m_pageSizeUndefined; PDFJBIG2Bitmap m_pageBitmap; + std::array m_arithmeticDecoderStates; }; } // namespace pdf diff --git a/PdfForQtLib/sources/pdfutils.cpp b/PdfForQtLib/sources/pdfutils.cpp index 8f301a1..f8688af 100644 --- a/PdfForQtLib/sources/pdfutils.cpp +++ b/PdfForQtLib/sources/pdfutils.cpp @@ -126,6 +126,12 @@ int32_t PDFBitReader::readSignedInt() return *reinterpret_cast(&value); } +int8_t PDFBitReader::readSignedByte() +{ + const uint8_t value = read(8); + return *reinterpret_cast(&value); +} + PDFBitWriter::PDFBitWriter(Value bitsPerComponent) : m_bitsPerComponent(bitsPerComponent), m_mask((static_cast(1) << m_bitsPerComponent) - static_cast(1)), diff --git a/PdfForQtLib/sources/pdfutils.h b/PdfForQtLib/sources/pdfutils.h index d588668..b872f93 100644 --- a/PdfForQtLib/sources/pdfutils.h +++ b/PdfForQtLib/sources/pdfutils.h @@ -138,6 +138,9 @@ public: /// Reads signed 32-bit integer from the stream int32_t readSignedInt(); + /// Reads signed 8-bit integer from the stream + int8_t readSignedByte(); + /// Reads unsigned 32-bit integer from the stream uint32_t readUnsignedInt() { return read(32); } @@ -147,6 +150,9 @@ public: /// Reads unsigned 8-bit integer from the stream uint8_t readUnsignedByte() { return read(8); } + /// Return underlying byte stream + const QByteArray* getStream() const { return m_stream; } + private: const QByteArray* m_stream; int m_position;