mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Better handling of images
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
OpenJPEG/bin/openjp2.lib
Normal file
BIN
OpenJPEG/bin/openjp2.lib
Normal file
Binary file not shown.
BIN
OpenJPEG/bin/openjp2.pdb
Normal file
BIN
OpenJPEG/bin/openjp2.pdb
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -333,7 +333,7 @@ typedef struct opj_poc {
|
|||||||
OPJ_PROG_ORDER prg1, prg;
|
OPJ_PROG_ORDER prg1, prg;
|
||||||
/** Progression order string*/
|
/** Progression order string*/
|
||||||
OPJ_CHAR progorder[5];
|
OPJ_CHAR progorder[5];
|
||||||
/** Tile number */
|
/** Tile number (starting at 1) */
|
||||||
OPJ_UINT32 tile;
|
OPJ_UINT32 tile;
|
||||||
/** Start and end values for Tile width and height*/
|
/** Start and end values for Tile width and height*/
|
||||||
OPJ_INT32 tx0, tx1, ty0, ty1;
|
OPJ_INT32 tx0, tx1, ty0, ty1;
|
||||||
|
@ -40,6 +40,7 @@ SOURCES += \
|
|||||||
sources/pdfparser.cpp \
|
sources/pdfparser.cpp \
|
||||||
sources/pdfdocument.cpp \
|
sources/pdfdocument.cpp \
|
||||||
sources/pdfdocumentreader.cpp \
|
sources/pdfdocumentreader.cpp \
|
||||||
|
sources/pdfutils.cpp \
|
||||||
sources/pdfxreftable.cpp \
|
sources/pdfxreftable.cpp \
|
||||||
sources/pdfvisitor.cpp \
|
sources/pdfvisitor.cpp \
|
||||||
sources/pdfencoding.cpp \
|
sources/pdfencoding.cpp \
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "pdfobject.h"
|
#include "pdfobject.h"
|
||||||
#include "pdfdocument.h"
|
#include "pdfdocument.h"
|
||||||
#include "pdfexception.h"
|
#include "pdfexception.h"
|
||||||
|
#include "pdfutils.h"
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
@ -637,6 +638,55 @@ size_t PDFIndexedColorSpace::getColorComponentCount() const
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage PDFIndexedColorSpace::getImage(const PDFImageData& imageData) const
|
||||||
|
{
|
||||||
|
if (imageData.isValid())
|
||||||
|
{
|
||||||
|
QImage image(imageData.getWidth(), imageData.getHeight(), QImage::Format_RGB888);
|
||||||
|
image.fill(QColor(Qt::white));
|
||||||
|
|
||||||
|
unsigned int componentCount = imageData.getComponents();
|
||||||
|
QDataStream stream(imageData.getData(), QIODevice::ReadOnly);
|
||||||
|
PDFBitReader reader(stream, imageData.getBitsPerComponent());
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (componentCount != getColorComponentCount())
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Invalid colors for color space. Color space has %1 colors. Provided color count is %4.").arg(getColorComponentCount()).arg(componentCount));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
PDFColor color;
|
||||||
|
color.resize(componentCount);
|
||||||
|
|
||||||
|
for (unsigned int i = 0, rowCount = imageData.getHeight(); i < rowCount; ++i)
|
||||||
|
{
|
||||||
|
reader.seek(i * imageData.getStride());
|
||||||
|
unsigned char* outputLine = image.scanLine(i);
|
||||||
|
|
||||||
|
for (unsigned int j = 0; j < imageData.getWidth(); ++j)
|
||||||
|
{
|
||||||
|
const unsigned char* currentData = rowData + (j * componentCount);
|
||||||
|
for (unsigned int k = 0; k < componentCount; ++k)
|
||||||
|
{
|
||||||
|
constexpr const double COEFFICIENT = 1.0 / 255.0;
|
||||||
|
color[k] = currentData[k] * COEFFICIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor transformedColor = getColor(color);
|
||||||
|
QRgb rgb = transformedColor.rgb();
|
||||||
|
|
||||||
|
*outputLine++ = qRed(rgb);
|
||||||
|
*outputLine++ = qGreen(rgb);
|
||||||
|
*outputLine++ = qBlue(rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QImage();
|
||||||
|
}
|
||||||
|
|
||||||
PDFColorSpacePointer PDFIndexedColorSpace::createIndexedColorSpace(const PDFDictionary* colorSpaceDictionary,
|
PDFColorSpacePointer PDFIndexedColorSpace::createIndexedColorSpace(const PDFDictionary* colorSpaceDictionary,
|
||||||
const PDFDocument* document,
|
const PDFDocument* document,
|
||||||
const PDFArray* array,
|
const PDFArray* array,
|
||||||
|
@ -107,6 +107,14 @@ public:
|
|||||||
unsigned int getWidth() const { return m_width; }
|
unsigned int getWidth() const { return m_width; }
|
||||||
unsigned int getHeight() const { return m_height; }
|
unsigned int getHeight() const { return m_height; }
|
||||||
unsigned int getStride() const { return m_stride; }
|
unsigned int getStride() const { return m_stride; }
|
||||||
|
const QByteArray& getData() const { return m_data; }
|
||||||
|
|
||||||
|
/// Returns number of color channels
|
||||||
|
unsigned int getColorChannels() const { return m_components; }
|
||||||
|
|
||||||
|
// TODO: Implement alpha channel functionality
|
||||||
|
/// Returns number of alpha transparency channels (0 or 1)
|
||||||
|
unsigned int getAlphaChannels() const { return 0; }
|
||||||
|
|
||||||
bool isValid() const { return m_width && m_height && m_components && m_bitsPerComponent; }
|
bool isValid() const { return m_width && m_height && m_components && m_bitsPerComponent; }
|
||||||
|
|
||||||
@ -432,6 +440,7 @@ public:
|
|||||||
virtual QColor getDefaultColor() const override;
|
virtual QColor getDefaultColor() const override;
|
||||||
virtual QColor getColor(const PDFColor& color) const override;
|
virtual QColor getColor(const PDFColor& color) const override;
|
||||||
virtual size_t getColorComponentCount() const override;
|
virtual size_t getColorComponentCount() const override;
|
||||||
|
virtual QImage getImage(const PDFImageData& imageData) const override;
|
||||||
|
|
||||||
/// Creates indexed color space from provided values.
|
/// Creates indexed color space from provided values.
|
||||||
/// \param colorSpaceDictionary Color space dictionary
|
/// \param colorSpaceDictionary Color space dictionary
|
||||||
|
@ -30,6 +30,7 @@ struct PDFJPEG2000ImageData
|
|||||||
{
|
{
|
||||||
const QByteArray* byteArray = nullptr;
|
const QByteArray* byteArray = nullptr;
|
||||||
OPJ_SIZE_T position = 0;
|
OPJ_SIZE_T position = 0;
|
||||||
|
std::vector<PDFRenderError> errors;
|
||||||
|
|
||||||
static OPJ_SIZE_T read(void* p_buffer, OPJ_SIZE_T p_nb_bytes, void* p_user_data);
|
static OPJ_SIZE_T read(void* p_buffer, OPJ_SIZE_T p_nb_bytes, void* p_user_data);
|
||||||
static OPJ_BOOL seek(OPJ_OFF_T p_nb_bytes, void* p_user_data);
|
static OPJ_BOOL seek(OPJ_OFF_T p_nb_bytes, void* p_user_data);
|
||||||
@ -42,7 +43,7 @@ struct PDFJPEGDCTSource
|
|||||||
const QByteArray* buffer = nullptr;
|
const QByteArray* buffer = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* stream, PDFColorSpacePointer colorSpace)
|
PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* stream, PDFColorSpacePointer colorSpace, PDFRenderErrorReporter* errorReporter)
|
||||||
{
|
{
|
||||||
PDFImage image;
|
PDFImage image;
|
||||||
image.m_colorSpace = colorSpace;
|
image.m_colorSpace = colorSpace;
|
||||||
@ -106,11 +107,6 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
|
|
||||||
if (imageFilterName == "DCTDecode" || imageFilterName == "DCT")
|
if (imageFilterName == "DCTDecode" || imageFilterName == "DCT")
|
||||||
{
|
{
|
||||||
// TODO: Check, if mutex is needed
|
|
||||||
// Used library is not thread safe. We must use a mutex!
|
|
||||||
static QMutex mutex;
|
|
||||||
QMutexLocker lock(&mutex);
|
|
||||||
|
|
||||||
int colorTransform = loader.readIntegerFromDictionary(dictionary, "ColorTransform", -1);
|
int colorTransform = loader.readIntegerFromDictionary(dictionary, "ColorTransform", -1);
|
||||||
|
|
||||||
jpeg_decompress_struct codec;
|
jpeg_decompress_struct codec;
|
||||||
@ -233,17 +229,22 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
imageData.byteArray = &content;
|
imageData.byteArray = &content;
|
||||||
imageData.position = 0;
|
imageData.position = 0;
|
||||||
|
|
||||||
opj_stream_t* stream = opj_stream_default_create(OPJ_TRUE);
|
auto warningCallback = [](const char* message, void* userData)
|
||||||
opj_stream_set_user_data(stream, &imageData, nullptr);
|
{
|
||||||
opj_stream_set_user_data_length(stream, sizeof(PDFJPEG2000ImageData));
|
PDFJPEG2000ImageData* data = reinterpret_cast<PDFJPEG2000ImageData*>(userData);
|
||||||
opj_stream_set_read_function(stream, &PDFJPEG2000ImageData::read);
|
data->errors.push_back(PDFRenderError(RenderErrorType::Warning, PDFTranslationContext::tr("JPEG 2000 Warning: %1").arg(QString::fromLatin1(message))));
|
||||||
opj_stream_set_seek_function(stream, &PDFJPEG2000ImageData::seek);
|
};
|
||||||
opj_stream_set_skip_function(stream, &PDFJPEG2000ImageData::skip);
|
|
||||||
|
auto errorCallback = [](const char* message, void* userData)
|
||||||
|
{
|
||||||
|
PDFJPEG2000ImageData* data = reinterpret_cast<PDFJPEG2000ImageData*>(userData);
|
||||||
|
data->errors.push_back(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("JPEG 2000 Error: %1").arg(QString::fromLatin1(message))));
|
||||||
|
};
|
||||||
|
|
||||||
opj_dparameters_t decompressParameters;
|
opj_dparameters_t decompressParameters;
|
||||||
opj_set_default_decoder_parameters(&decompressParameters);
|
opj_set_default_decoder_parameters(&decompressParameters);
|
||||||
|
|
||||||
CODEC_FORMAT formats[] = { OPJ_CODEC_J2K, OPJ_CODEC_JPT, OPJ_CODEC_JP2, OPJ_CODEC_JPP, OPJ_CODEC_JPX };
|
constexpr CODEC_FORMAT formats[] = { OPJ_CODEC_J2K, OPJ_CODEC_JP2, OPJ_CODEC_JPT, OPJ_CODEC_JPP, OPJ_CODEC_JPX };
|
||||||
for (CODEC_FORMAT format : formats)
|
for (CODEC_FORMAT format : formats)
|
||||||
{
|
{
|
||||||
opj_codec_t* codec = opj_create_decompress(format);
|
opj_codec_t* codec = opj_create_decompress(format);
|
||||||
@ -254,30 +255,157 @@ PDFImage PDFImage::createImage(const PDFDocument* document, const PDFStream* str
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opj_set_warning_handler(codec, warningCallback, &imageData);
|
||||||
|
opj_set_error_handler(codec, errorCallback, &imageData);
|
||||||
|
|
||||||
|
opj_stream_t* stream = opj_stream_create(content.size(), OPJ_TRUE);
|
||||||
|
opj_stream_set_user_data(stream, &imageData, nullptr);
|
||||||
|
opj_stream_set_user_data_length(stream, content.size());
|
||||||
|
opj_stream_set_read_function(stream, &PDFJPEG2000ImageData::read);
|
||||||
|
opj_stream_set_seek_function(stream, &PDFJPEG2000ImageData::seek);
|
||||||
|
opj_stream_set_skip_function(stream, &PDFJPEG2000ImageData::skip);
|
||||||
|
|
||||||
|
// Reset the stream position, clear the data
|
||||||
|
imageData.position = 0;
|
||||||
|
imageData.errors.clear();
|
||||||
|
|
||||||
|
opj_image_t* jpegImage = nullptr;
|
||||||
|
|
||||||
// Setup the decoder
|
// Setup the decoder
|
||||||
opj_setup_decoder(codec, &decompressParameters);
|
if (opj_setup_decoder(codec, &decompressParameters))
|
||||||
|
|
||||||
// Try to read the header
|
|
||||||
opj_image_t* image = nullptr;
|
|
||||||
if (opj_read_header(stream, codec, &image))
|
|
||||||
{
|
{
|
||||||
if (opj_set_decode_area(codec, image, decompressParameters.DA_x0, decompressParameters.DA_y0, decompressParameters.DA_x1, decompressParameters.DA_y1))
|
// Try to read the header
|
||||||
{
|
|
||||||
if (opj_decode(codec, stream, image))
|
|
||||||
{
|
|
||||||
if (opj_end_decompress(codec, stream))
|
|
||||||
{
|
|
||||||
|
|
||||||
|
if (opj_read_header(stream, codec, &jpegImage))
|
||||||
|
{
|
||||||
|
if (opj_set_decode_area(codec, jpegImage, decompressParameters.DA_x0, decompressParameters.DA_y0, decompressParameters.DA_x1, decompressParameters.DA_y1))
|
||||||
|
{
|
||||||
|
if (opj_decode(codec, stream, jpegImage))
|
||||||
|
{
|
||||||
|
if (opj_end_decompress(codec, stream))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
opj_stream_destroy(stream);
|
||||||
opj_destroy_codec(codec);
|
opj_destroy_codec(codec);
|
||||||
|
|
||||||
|
stream = nullptr;
|
||||||
|
codec = nullptr;
|
||||||
|
|
||||||
|
// If we have a valid image, then adjust it
|
||||||
|
if (jpegImage)
|
||||||
|
{
|
||||||
|
// First we must check, if all components are valid (i.e has same width/height/precision)
|
||||||
|
|
||||||
|
bool valid = true;
|
||||||
|
const OPJ_UINT32 componentCount = jpegImage->numcomps;
|
||||||
|
for (OPJ_UINT32 i = 1; i < componentCount; ++i)
|
||||||
|
{
|
||||||
|
if (jpegImage->comps[0].w != jpegImage->comps[i].w ||
|
||||||
|
jpegImage->comps[0].h != jpegImage->comps[i].h ||
|
||||||
|
jpegImage->comps[0].prec != jpegImage->comps[i].prec ||
|
||||||
|
jpegImage->comps[0].sgnd != jpegImage->comps[i].sgnd)
|
||||||
|
{
|
||||||
|
valid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Include alpha channel functionality - mask in image
|
||||||
|
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
const OPJ_UINT32 w = jpegImage->comps[0].w;
|
||||||
|
const OPJ_UINT32 h = jpegImage->comps[0].h;
|
||||||
|
const OPJ_UINT32 prec = jpegImage->comps[0].prec;
|
||||||
|
const OPJ_UINT32 sgnd = jpegImage->comps[0].sgnd;
|
||||||
|
|
||||||
|
const bool isIndexed = dynamic_cast<const PDFIndexedColorSpace*>(image.m_colorSpace.data());
|
||||||
|
int signumCorrection = (sgnd) ? (1 << (prec - 1)) : 0;
|
||||||
|
int shiftLeft = (jpegImage->comps[0].prec < 8) ? 8 - jpegImage->comps[0].prec : 0;
|
||||||
|
int shiftRight = (jpegImage->comps[0].prec > 8) ? jpegImage->comps[0].prec - 8 : 0;
|
||||||
|
|
||||||
|
auto transformValue = [signumCorrection, isIndexed, shiftLeft, shiftRight](int value) -> unsigned char
|
||||||
|
{
|
||||||
|
value += signumCorrection;
|
||||||
|
|
||||||
|
if (!isIndexed)
|
||||||
|
{
|
||||||
|
// Indexed color space should have at most 255 indices, do not modify indices in this case
|
||||||
|
|
||||||
|
if (shiftLeft > 0)
|
||||||
|
{
|
||||||
|
value = value << shiftLeft;
|
||||||
|
}
|
||||||
|
else if (shiftRight > 0)
|
||||||
|
{
|
||||||
|
// We clamp value to the lower part (so, we use similar algorithm as in 'floor' function).
|
||||||
|
//
|
||||||
|
value = value >> shiftRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = qBound(0, value, 255);
|
||||||
|
return static_cast<unsigned char>(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Variables for image data. We convert all components to the 8-bit format
|
||||||
|
unsigned int components = jpegImage->numcomps;
|
||||||
|
unsigned int bitsPerComponent = 8;
|
||||||
|
unsigned int width = w;
|
||||||
|
unsigned int height = h;
|
||||||
|
unsigned int stride = w * components;
|
||||||
|
|
||||||
|
QByteArray imageDataBuffer(components * width * height, 0);
|
||||||
|
for (unsigned int row = 0; row < h; ++row)
|
||||||
|
{
|
||||||
|
for (unsigned int col = 0; col < w; ++col)
|
||||||
|
{
|
||||||
|
for (unsigned int componentIndex = 0; componentIndex < components; ++ componentIndex)
|
||||||
|
{
|
||||||
|
int index = stride * row + col * components + componentIndex;
|
||||||
|
Q_ASSERT(index < imageDataBuffer.size());
|
||||||
|
|
||||||
|
imageDataBuffer[index] = transformValue(jpegImage->comps[componentIndex].data[w * row + h]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
image.m_imageData = PDFImageData(components, bitsPerComponent, width, height, stride, qMove(imageDataBuffer));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Easiest way is to
|
||||||
|
imageData.errors.push_back(PDFRenderError(RenderErrorType::Error, PDFTranslationContext::tr("Incompatible color components for JPEG 2000 image.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
opj_image_destroy(jpegImage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
opj_stream_destroy(stream);
|
// Report errors, if we have any
|
||||||
stream = nullptr;
|
if (!imageData.errors.empty())
|
||||||
|
{
|
||||||
|
for (const PDFRenderError& error : imageData.errors)
|
||||||
|
{
|
||||||
|
QString message = error.message.simplified().trimmed();
|
||||||
|
if (error.type == RenderErrorType::Error)
|
||||||
|
{
|
||||||
|
throw PDFRendererException(error.type, message);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errorReporter->reportRenderError(error.type, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
@ -316,6 +444,11 @@ OPJ_SIZE_T PDFJPEG2000ImageData::read(void* p_buffer, OPJ_SIZE_T p_nb_bytes, voi
|
|||||||
data->position += length;
|
data->position += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (length == 0)
|
||||||
|
{
|
||||||
|
return (OPJ_SIZE_T) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ namespace pdf
|
|||||||
{
|
{
|
||||||
class PDFStream;
|
class PDFStream;
|
||||||
class PDFDocument;
|
class PDFDocument;
|
||||||
|
class PDFRenderErrorReporter;
|
||||||
|
|
||||||
class PDFImage
|
class PDFImage
|
||||||
{
|
{
|
||||||
@ -37,7 +38,8 @@ public:
|
|||||||
/// \param document Document
|
/// \param document Document
|
||||||
/// \param stream Stream with image
|
/// \param stream Stream with image
|
||||||
/// \param colorSpace Color space of the image
|
/// \param colorSpace Color space of the image
|
||||||
static PDFImage createImage(const PDFDocument* document, const PDFStream* stream, PDFColorSpacePointer colorSpace);
|
/// \param errorReporter Error reporter for reporting errors (or warnings)
|
||||||
|
static PDFImage createImage(const PDFDocument* document, const PDFStream* stream, PDFColorSpacePointer colorSpace, PDFRenderErrorReporter* errorReporter);
|
||||||
|
|
||||||
/// Returns image transformed from image data and color space
|
/// Returns image transformed from image data and color space
|
||||||
QImage getImage() const;
|
QImage getImage() const;
|
||||||
|
@ -1796,7 +1796,7 @@ void PDFPageContentProcessor::operatorPaintXObject(PDFPageContentProcessor::PDFO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFImage pdfImage = PDFImage::createImage(m_document, stream, qMove(colorSpace));
|
PDFImage pdfImage = PDFImage::createImage(m_document, stream, qMove(colorSpace), this);
|
||||||
QImage image = pdfImage.getImage();
|
QImage image = pdfImage.getImage();
|
||||||
|
|
||||||
if (!image.isNull())
|
if (!image.isNull())
|
||||||
|
72
PdfForQtLib/sources/pdfutils.cpp
Normal file
72
PdfForQtLib/sources/pdfutils.cpp
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (C) 2019 Jakub Melka
|
||||||
|
//
|
||||||
|
// This file is part of PdfForQt.
|
||||||
|
//
|
||||||
|
// PdfForQt is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// PdfForQt is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with PDFForQt. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include "pdfutils.h"
|
||||||
|
#include "pdfexception.h"
|
||||||
|
|
||||||
|
namespace pdf
|
||||||
|
{
|
||||||
|
|
||||||
|
PDFBitReader::PDFBitReader(QDataStream* stream, Value bitsPerComponent) :
|
||||||
|
m_stream(stream),
|
||||||
|
m_bitsPerComponent(bitsPerComponent),
|
||||||
|
m_maximalValue((static_cast<Value>(1) << m_bitsPerComponent) - static_cast<Value>(1)),
|
||||||
|
m_buffer(0),
|
||||||
|
m_bitsInBuffer(0)
|
||||||
|
{
|
||||||
|
// We need reserve, so we allow number of length of component as 1-56 bits.
|
||||||
|
Q_ASSERT(bitsPerComponent > 0);
|
||||||
|
Q_ASSERT(bitsPerComponent < 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFBitReader::Value PDFBitReader::read()
|
||||||
|
{
|
||||||
|
while (m_bitsInBuffer < m_bitsPerComponent)
|
||||||
|
{
|
||||||
|
if (!m_stream->atEnd())
|
||||||
|
{
|
||||||
|
uint8_t currentByte = 0;
|
||||||
|
(*m_stream) >> currentByte;
|
||||||
|
m_buffer = (m_buffer << 8) | currentByte;
|
||||||
|
m_bitsInBuffer += 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Not enough data to read %1-bit value.").arg(m_bitsPerComponent));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we have enough bits to read the data
|
||||||
|
Value value = (m_buffer >> (m_bitsInBuffer - m_bitsPerComponent)) & m_maximalValue;
|
||||||
|
m_bitsInBuffer -= m_bitsPerComponent;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFBitReader::seek(qint64 position)
|
||||||
|
{
|
||||||
|
if (m_stream->device()->seek(position))
|
||||||
|
{
|
||||||
|
m_buffer = 0;
|
||||||
|
m_bitsInBuffer = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw PDFParserException(PDFTranslationContext::tr("Can't seek to position %1.").arg(position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace pdf
|
@ -19,6 +19,11 @@
|
|||||||
#ifndef PDFUTILS_H
|
#ifndef PDFUTILS_H
|
||||||
#define PDFUTILS_H
|
#define PDFUTILS_H
|
||||||
|
|
||||||
|
#include "pdfglobal.h"
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QDataStream>
|
||||||
|
|
||||||
namespace pdf
|
namespace pdf
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -64,6 +69,36 @@ private:
|
|||||||
T m_object;
|
T m_object;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Bit-reader, which can read n-bit unsigned integers from the stream.
|
||||||
|
/// Number of bits can be set in the constructor and is constant.
|
||||||
|
class PDFBitReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Value = uint64_t;
|
||||||
|
|
||||||
|
explicit PDFBitReader(QDataStream* stream, Value bitsPerComponent);
|
||||||
|
|
||||||
|
/// Returns maximal value of n-bit unsigned integer.
|
||||||
|
Value max() const { return m_maximalValue; }
|
||||||
|
|
||||||
|
/// Reads single n-bit value from the stream. If stream hasn't enough data,
|
||||||
|
/// then exception is thrown.
|
||||||
|
Value read();
|
||||||
|
|
||||||
|
/// Seeks the desired position in the data stream. If position can't be seeked,
|
||||||
|
/// then exception is thrown.
|
||||||
|
void seek(qint64 position);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QDataStream* m_stream;
|
||||||
|
|
||||||
|
const Value m_bitsPerComponent;
|
||||||
|
const Value m_maximalValue;
|
||||||
|
|
||||||
|
Value m_buffer;
|
||||||
|
Value m_bitsInBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace pdf
|
} // namespace pdf
|
||||||
|
|
||||||
#endif // PDFUTILS_H
|
#endif // PDFUTILS_H
|
||||||
|
Reference in New Issue
Block a user