2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
2021-05-30 13:10:44 +02:00
|
|
|
|
// PDF stream functions for PDFio.
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
2023-01-11 23:13:58 +01:00
|
|
|
|
// Copyright © 2021-2023 by Michael R Sweet.
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
|
|
|
|
// Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
|
|
|
// information.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include "pdfio-private.h"
|
|
|
|
|
|
|
|
|
|
|
2021-05-07 14:47:49 +02:00
|
|
|
|
//
|
|
|
|
|
// Local functions...
|
|
|
|
|
//
|
|
|
|
|
|
2021-05-09 01:04:42 +02:00
|
|
|
|
static unsigned char stream_paeth(unsigned char a, unsigned char b, unsigned char c);
|
|
|
|
|
static ssize_t stream_read(pdfio_stream_t *st, char *buffer, size_t bytes);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
static bool stream_write(pdfio_stream_t *st, const void *buffer, size_t bytes);
|
2021-08-25 15:30:03 +02:00
|
|
|
|
static const char *zstrerror(int error);
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
|
|
|
|
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
2021-04-30 13:06:56 +02:00
|
|
|
|
// 'pdfioStreamClose()' - Close a (data) stream in a PDF file.
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
|
|
|
|
|
2021-04-30 13:06:56 +02:00
|
|
|
|
bool // O - `true` on success, `false` on failure
|
|
|
|
|
pdfioStreamClose(pdfio_stream_t *st) // I - Stream
|
2021-04-17 02:41:46 +02:00
|
|
|
|
{
|
2021-05-16 17:39:05 +02:00
|
|
|
|
bool ret = true; // Return value
|
|
|
|
|
|
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
// Range check input...
|
|
|
|
|
if (!st)
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
// Finish reads/writes and free memory...
|
|
|
|
|
if (st->pdf->mode == _PDFIO_MODE_READ)
|
|
|
|
|
{
|
|
|
|
|
if (st->filter == PDFIO_FILTER_FLATE)
|
|
|
|
|
inflateEnd(&(st->flate));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-05-16 17:39:05 +02:00
|
|
|
|
// Close stream for writing...
|
|
|
|
|
if (st->filter == PDFIO_FILTER_FLATE)
|
|
|
|
|
{
|
|
|
|
|
// Finalize flate compression stream...
|
|
|
|
|
int status; // Deflate status
|
|
|
|
|
|
|
|
|
|
while ((status = deflate(&st->flate, Z_FINISH)) != Z_STREAM_END)
|
|
|
|
|
{
|
2021-10-24 02:33:12 +02:00
|
|
|
|
size_t bytes = sizeof(st->cbuffer) - st->flate.avail_out,
|
2021-10-23 20:37:25 +02:00
|
|
|
|
// Bytes to write
|
2021-10-24 02:33:12 +02:00
|
|
|
|
outbytes; // Actual bytes written
|
2021-10-23 20:37:25 +02:00
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
if (status < Z_OK && status != Z_BUF_ERROR)
|
|
|
|
|
{
|
2021-08-25 15:30:03 +02:00
|
|
|
|
_pdfioFileError(st->pdf, "Flate compression failed: %s", zstrerror(status));
|
2021-05-16 17:39:05 +02:00
|
|
|
|
ret = false;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-12 15:13:30 +02:00
|
|
|
|
if (st->crypto_cb)
|
|
|
|
|
{
|
|
|
|
|
// Encrypt it first...
|
2021-10-31 16:12:54 +01:00
|
|
|
|
outbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, bytes & (size_t)~15);
|
2021-10-24 02:33:12 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// No encryption
|
|
|
|
|
outbytes = bytes;
|
2021-10-12 15:13:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 02:33:12 +02:00
|
|
|
|
if (!_pdfioFileWrite(st->pdf, st->cbuffer, outbytes))
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
|
|
|
|
ret = false;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 02:33:12 +02:00
|
|
|
|
if (bytes > outbytes)
|
|
|
|
|
{
|
|
|
|
|
bytes -= outbytes;
|
|
|
|
|
memmove(st->cbuffer, st->cbuffer + outbytes, bytes);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
bytes = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st->flate.next_out = (Bytef *)st->cbuffer + bytes;
|
2021-10-31 16:12:54 +01:00
|
|
|
|
st->flate.avail_out = (uInt)(sizeof(st->cbuffer) - bytes);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (st->flate.avail_out < (uInt)sizeof(st->cbuffer))
|
|
|
|
|
{
|
|
|
|
|
// Write any residuals...
|
2021-10-23 20:37:25 +02:00
|
|
|
|
size_t bytes = sizeof(st->cbuffer) - st->flate.avail_out;
|
|
|
|
|
// Bytes to write
|
|
|
|
|
|
2021-10-12 15:13:30 +02:00
|
|
|
|
if (st->crypto_cb)
|
|
|
|
|
{
|
|
|
|
|
// Encrypt it first...
|
2021-10-23 20:37:25 +02:00
|
|
|
|
bytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, bytes);
|
2021-10-12 15:13:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-23 20:37:25 +02:00
|
|
|
|
if (!_pdfioFileWrite(st->pdf, st->cbuffer, bytes))
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
|
|
|
|
ret = false;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-16 06:02:31 +02:00
|
|
|
|
|
|
|
|
|
deflateEnd(&st->flate);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
}
|
2021-10-24 02:33:12 +02:00
|
|
|
|
else if (st->crypto_cb && st->bufptr > st->buffer)
|
|
|
|
|
{
|
|
|
|
|
// Encrypt and flush
|
|
|
|
|
uint8_t temp[8192]; // Temporary buffer
|
|
|
|
|
size_t outbytes; // Output bytes
|
|
|
|
|
|
2021-10-24 16:59:25 +02:00
|
|
|
|
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, (uint8_t *)st->buffer, (size_t)(st->bufptr - st->buffer));
|
2021-10-24 02:33:12 +02:00
|
|
|
|
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
|
|
|
|
|
{
|
|
|
|
|
ret = false;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-16 17:39:05 +02:00
|
|
|
|
|
|
|
|
|
// Save the length of this stream...
|
|
|
|
|
st->obj->stream_length = (size_t)(_pdfioFileTell(st->pdf) - st->obj->stream_offset);
|
|
|
|
|
|
|
|
|
|
// End of stream marker...
|
2021-05-30 02:00:48 +02:00
|
|
|
|
if (!_pdfioFilePuts(st->pdf, "\nendstream\nendobj\n"))
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
|
|
|
|
ret = false;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update the length as needed...
|
2021-09-27 13:41:50 +02:00
|
|
|
|
if (st->length_obj)
|
|
|
|
|
{
|
|
|
|
|
st->length_obj->value.value.number = st->obj->stream_length;
|
|
|
|
|
pdfioObjClose(st->length_obj);
|
|
|
|
|
}
|
|
|
|
|
else if (st->obj->length_offset)
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
|
|
|
|
// Seek back to the "/Length 9999999999" we wrote...
|
|
|
|
|
if (_pdfioFileSeek(st->pdf, st->obj->length_offset, SEEK_SET) < 0)
|
|
|
|
|
{
|
|
|
|
|
ret = false;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write the updated length value...
|
|
|
|
|
if (!_pdfioFilePrintf(st->pdf, "%-10lu", (unsigned long)st->obj->stream_length))
|
|
|
|
|
{
|
|
|
|
|
ret = false;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Seek to the end of the PDF file...
|
|
|
|
|
if (_pdfioFileSeek(st->pdf, 0, SEEK_END) < 0)
|
|
|
|
|
{
|
|
|
|
|
ret = false;
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-08 01:51:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
done:
|
|
|
|
|
|
2022-07-04 19:03:11 +02:00
|
|
|
|
st->pdf->current_obj = NULL;
|
|
|
|
|
|
2021-06-01 23:48:36 +02:00
|
|
|
|
free(st->prbuffer);
|
|
|
|
|
free(st->psbuffer);
|
2021-05-08 01:51:38 +02:00
|
|
|
|
free(st);
|
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
return (ret);
|
2021-04-17 02:41:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-05-06 22:07:58 +02:00
|
|
|
|
//
|
|
|
|
|
// '_pdfioStreamCreate()' - Create a stream for writing.
|
|
|
|
|
//
|
|
|
|
|
// Note: pdfioObjCreateStream handles writing the object and its dictionary.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
pdfio_stream_t * // O - Stream or `NULL` on error
|
|
|
|
|
_pdfioStreamCreate(
|
|
|
|
|
pdfio_obj_t *obj, // I - Object
|
2021-09-27 13:41:50 +02:00
|
|
|
|
pdfio_obj_t *length_obj, // I - Length object, if any
|
2021-05-06 22:07:58 +02:00
|
|
|
|
pdfio_filter_t compression) // I - Compression to apply
|
|
|
|
|
{
|
2021-05-16 17:39:05 +02:00
|
|
|
|
pdfio_stream_t *st; // Stream
|
|
|
|
|
|
2021-05-06 22:07:58 +02:00
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
// Allocate a new stream object...
|
|
|
|
|
if ((st = (pdfio_stream_t *)calloc(1, sizeof(pdfio_stream_t))) == NULL)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(obj->pdf, "Unable to allocate memory for a stream.");
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-27 13:41:50 +02:00
|
|
|
|
st->pdf = obj->pdf;
|
|
|
|
|
st->obj = obj;
|
|
|
|
|
st->length_obj = length_obj;
|
|
|
|
|
st->filter = compression;
|
2021-10-24 02:33:12 +02:00
|
|
|
|
st->bufptr = st->buffer;
|
|
|
|
|
st->bufend = st->buffer + sizeof(st->buffer);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
|
2021-10-12 15:13:30 +02:00
|
|
|
|
if (obj->pdf->encryption)
|
|
|
|
|
{
|
|
|
|
|
uint8_t iv[64]; // Initialization vector
|
|
|
|
|
size_t ivlen = sizeof(iv); // Length of initialization vector, if any
|
|
|
|
|
|
|
|
|
|
if ((st->crypto_cb = _pdfioCryptoMakeWriter(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL)
|
|
|
|
|
{
|
|
|
|
|
// TODO: Add error message?
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ivlen > 0)
|
|
|
|
|
_pdfioFileWrite(st->pdf, iv, ivlen);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
if (compression == PDFIO_FILTER_FLATE)
|
|
|
|
|
{
|
|
|
|
|
// Flate compression
|
|
|
|
|
pdfio_dict_t *params = pdfioDictGetDict(obj->value.value.dict, "DecodeParms");
|
|
|
|
|
// Decoding parameters
|
|
|
|
|
int bpc = (int)pdfioDictGetNumber(params, "BitsPerComponent");
|
|
|
|
|
// Bits per component
|
|
|
|
|
int colors = (int)pdfioDictGetNumber(params, "Colors");
|
|
|
|
|
// Number of colors
|
|
|
|
|
int columns = (int)pdfioDictGetNumber(params, "Columns");
|
|
|
|
|
// Number of columns
|
|
|
|
|
int predictor = (int)pdfioDictGetNumber(params, "Predictor");
|
|
|
|
|
// Predictory value, if any
|
2021-08-25 15:30:03 +02:00
|
|
|
|
int status; // ZLIB status code
|
2021-05-16 17:39:05 +02:00
|
|
|
|
|
|
|
|
|
PDFIO_DEBUG("_pdfioStreamCreate: FlateDecode - BitsPerComponent=%d, Colors=%d, Columns=%d, Predictor=%d\n", bpc, colors, columns, predictor);
|
|
|
|
|
|
|
|
|
|
if (bpc == 0)
|
|
|
|
|
{
|
|
|
|
|
bpc = 8;
|
|
|
|
|
}
|
|
|
|
|
else if (bpc < 1 || bpc == 3 || (bpc > 4 && bpc < 8) || (bpc > 8 && bpc < 16) || bpc > 16)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported BitsPerColor value %d.", bpc);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (colors == 0)
|
|
|
|
|
{
|
|
|
|
|
colors = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (colors < 0 || colors > 4)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (columns == 0)
|
|
|
|
|
{
|
|
|
|
|
columns = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (columns < 0)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((predictor > 1 && predictor < 10) || predictor > 15)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported Predictor function %d.", predictor);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
else if (predictor)
|
|
|
|
|
{
|
|
|
|
|
// Using a PNG predictor function
|
|
|
|
|
st->predictor = (_pdfio_predictor_t)predictor;
|
|
|
|
|
st->pbpixel = (size_t)(bpc * colors + 7) / 8;
|
|
|
|
|
st->pbsize = (size_t)(bpc * colors * columns + 7) / 8;
|
|
|
|
|
if (predictor >= 10)
|
|
|
|
|
st->pbsize ++; // Add PNG predictor byte
|
|
|
|
|
|
2021-06-01 23:48:36 +02:00
|
|
|
|
if ((st->prbuffer = calloc(1, st->pbsize - 1)) == NULL || (st->psbuffer = calloc(1, st->pbsize)) == NULL)
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Predictor buffers.", (unsigned long)st->pbsize);
|
2021-06-01 23:48:36 +02:00
|
|
|
|
free(st->prbuffer);
|
|
|
|
|
free(st->psbuffer);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
st->predictor = _PDFIO_PREDICTOR_NONE;
|
|
|
|
|
|
|
|
|
|
st->flate.next_out = (Bytef *)st->cbuffer;
|
|
|
|
|
st->flate.avail_out = (uInt)sizeof(st->cbuffer);
|
|
|
|
|
|
2021-08-25 15:30:03 +02:00
|
|
|
|
if ((status = deflateInit(&(st->flate), 9)) != Z_OK)
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
2021-08-25 15:30:03 +02:00
|
|
|
|
_pdfioFileError(st->pdf, "Unable to start Flate filter: %s", zstrerror(status));
|
2021-06-01 23:48:36 +02:00
|
|
|
|
free(st->prbuffer);
|
|
|
|
|
free(st->psbuffer);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (st);
|
2021-05-06 22:07:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-05-02 04:38:11 +02:00
|
|
|
|
//
|
|
|
|
|
// 'pdfioStreamConsume()' - Consume bytes from the stream.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
bool // O - `true` on success, `false` on EOF
|
|
|
|
|
pdfioStreamConsume(pdfio_stream_t *st, // I - Stream
|
|
|
|
|
size_t bytes)// I - Number of bytes to consume
|
|
|
|
|
{
|
2021-05-07 14:47:49 +02:00
|
|
|
|
size_t remaining; // Remaining bytes in buffer
|
|
|
|
|
ssize_t rbytes; // Bytes read
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Range check input...
|
|
|
|
|
if (!st || st->pdf->mode != _PDFIO_MODE_READ || !bytes)
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
// Skip bytes in the stream buffer until we've consumed the requested number
|
|
|
|
|
// or get to the end of the stream...
|
|
|
|
|
while ((remaining = (size_t)(st->bufend - st->bufptr)) < bytes)
|
|
|
|
|
{
|
|
|
|
|
bytes -= remaining;
|
|
|
|
|
|
|
|
|
|
if ((rbytes = stream_read(st, st->buffer, sizeof(st->buffer))) > 0)
|
|
|
|
|
{
|
|
|
|
|
st->bufptr = st->buffer;
|
|
|
|
|
st->bufend = st->buffer + rbytes;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
st->bufptr = st->bufend = st->buffer;
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st->bufptr += bytes;
|
|
|
|
|
|
|
|
|
|
return (true);
|
2021-05-02 04:38:11 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-04-26 16:42:01 +02:00
|
|
|
|
//
|
2021-04-30 13:06:56 +02:00
|
|
|
|
// 'pdfioStreamGetToken()' - Read a single PDF token from a stream.
|
2021-04-26 16:42:01 +02:00
|
|
|
|
//
|
2023-01-11 23:13:58 +01:00
|
|
|
|
// This function reads a single PDF token from a stream. Operator tokens,
|
|
|
|
|
// boolean values, and numbers are returned as-is in the provided string buffer.
|
|
|
|
|
// String values start with the opening parenthesis ('(') but have all escaping
|
|
|
|
|
// resolved and the terminating parenthesis removed. Hexadecimal string values
|
|
|
|
|
// start with the opening angle bracket ('<') and have all whitespace and the
|
|
|
|
|
// terminating angle bracket removed.
|
|
|
|
|
//
|
2021-04-26 16:42:01 +02:00
|
|
|
|
|
2021-04-30 13:06:56 +02:00
|
|
|
|
bool // O - `true` on success, `false` on EOF
|
|
|
|
|
pdfioStreamGetToken(
|
|
|
|
|
pdfio_stream_t *st, // I - Stream
|
|
|
|
|
char *buffer, // I - String buffer
|
|
|
|
|
size_t bufsize) // I - Size of string buffer
|
2021-05-01 23:50:52 +02:00
|
|
|
|
{
|
2021-05-08 13:38:44 +02:00
|
|
|
|
_pdfio_token_t tb; // Token buffer/stack
|
2022-03-01 15:18:56 +01:00
|
|
|
|
bool ret; // Return value
|
2021-05-08 13:38:44 +02:00
|
|
|
|
|
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
// Range check input...
|
|
|
|
|
if (!st || st->pdf->mode != _PDFIO_MODE_READ || !buffer || !bufsize)
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
// Read using the token engine...
|
2021-05-08 13:38:44 +02:00
|
|
|
|
_pdfioTokenInit(&tb, st->pdf, (_pdfio_tconsume_cb_t)pdfioStreamConsume, (_pdfio_tpeek_cb_t)pdfioStreamPeek, st);
|
|
|
|
|
|
2022-03-01 15:18:56 +01:00
|
|
|
|
ret = _pdfioTokenRead(&tb, buffer, bufsize);
|
|
|
|
|
_pdfioTokenFlush(&tb);
|
|
|
|
|
|
|
|
|
|
return (ret);
|
2021-05-01 23:50:52 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-05-06 22:07:58 +02:00
|
|
|
|
//
|
|
|
|
|
// '_pdfioStreamOpen()' - Create a stream for reading.
|
|
|
|
|
//
|
|
|
|
|
// Note: pdfioObjOpenStream handles loading the object's dictionary and
|
|
|
|
|
// getting the start of the stream data.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
pdfio_stream_t * // O - Stream or `NULL` on error
|
|
|
|
|
_pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
|
|
|
|
|
bool decode) // I - Decode/decompress the stream?
|
|
|
|
|
{
|
|
|
|
|
pdfio_stream_t *st; // Stream
|
2021-05-07 14:47:49 +02:00
|
|
|
|
pdfio_dict_t *dict = pdfioObjGetDict(obj);
|
|
|
|
|
// Object dictionary
|
2021-05-06 22:07:58 +02:00
|
|
|
|
|
|
|
|
|
|
2021-06-21 22:18:06 +02:00
|
|
|
|
PDFIO_DEBUG("_pdfioStreamOpen(obj=%p(%u), decode=%s)\n", obj, (unsigned)obj->number, decode ? "true" : "false");
|
|
|
|
|
|
2021-05-06 22:07:58 +02:00
|
|
|
|
// Allocate a new stream object...
|
|
|
|
|
if ((st = (pdfio_stream_t *)calloc(1, sizeof(pdfio_stream_t))) == NULL)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(obj->pdf, "Unable to allocate memory for a stream.");
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st->pdf = obj->pdf;
|
|
|
|
|
st->obj = obj;
|
|
|
|
|
|
2021-06-21 22:18:06 +02:00
|
|
|
|
if ((st->remaining = pdfioObjGetLength(obj)) == 0)
|
2021-06-04 16:56:23 +02:00
|
|
|
|
{
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
2021-05-06 22:07:58 +02:00
|
|
|
|
|
2021-06-21 22:18:06 +02:00
|
|
|
|
if (_pdfioFileSeek(st->pdf, obj->stream_offset, SEEK_SET) != obj->stream_offset)
|
2021-05-07 14:47:49 +02:00
|
|
|
|
{
|
2021-05-09 01:16:27 +02:00
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
2021-05-07 14:47:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 16:59:25 +02:00
|
|
|
|
if (obj->pdf->encryption)
|
|
|
|
|
{
|
|
|
|
|
uint8_t iv[64]; // Initialization vector
|
|
|
|
|
size_t ivlen; // Length of initialization vector, if any
|
|
|
|
|
|
2021-10-31 16:12:54 +01:00
|
|
|
|
ivlen = (size_t)_pdfioFilePeek(st->pdf, iv, sizeof(iv));
|
2021-10-24 16:59:25 +02:00
|
|
|
|
|
|
|
|
|
if ((st->crypto_cb = _pdfioCryptoMakeReader(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL)
|
|
|
|
|
{
|
|
|
|
|
// TODO: Add error message?
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 14:38:47 +01:00
|
|
|
|
PDFIO_DEBUG("_pdfioStreamOpen: ivlen=%d\n", (int)ivlen);
|
2021-10-24 16:59:25 +02:00
|
|
|
|
if (ivlen > 0)
|
|
|
|
|
_pdfioFileConsume(st->pdf, ivlen);
|
2021-10-31 16:12:54 +01:00
|
|
|
|
|
|
|
|
|
if (st->pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
|
|
|
|
|
st->remaining = (st->remaining + 15) & (size_t)~15;
|
2021-10-24 16:59:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 22:07:58 +02:00
|
|
|
|
if (decode)
|
|
|
|
|
{
|
|
|
|
|
// Try to decode/decompress the contents of this object...
|
|
|
|
|
const char *filter = pdfioDictGetName(dict, "Filter");
|
|
|
|
|
// Filter value
|
2023-10-10 13:24:27 +02:00
|
|
|
|
pdfio_array_t *fa = pdfioDictGetArray(dict, "Filter");
|
|
|
|
|
// Filter array
|
2023-10-10 13:14:12 +02:00
|
|
|
|
|
2023-10-10 13:24:27 +02:00
|
|
|
|
if (!filter && fa && pdfioArrayGetSize(fa) == 1)
|
2023-10-10 13:14:12 +02:00
|
|
|
|
{
|
|
|
|
|
// Support single-valued arrays...
|
|
|
|
|
filter = pdfioArrayGetName(fa, 0);
|
|
|
|
|
}
|
2021-05-06 22:07:58 +02:00
|
|
|
|
|
|
|
|
|
if (!filter)
|
|
|
|
|
{
|
|
|
|
|
// No single filter name, do we have a compound filter?
|
2023-10-10 13:14:12 +02:00
|
|
|
|
if (fa)
|
2021-05-06 22:07:58 +02:00
|
|
|
|
{
|
|
|
|
|
// TODO: Implement compound filters...
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported compound stream filter.");
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// No filter, read as-is...
|
|
|
|
|
st->filter = PDFIO_FILTER_NONE;
|
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(filter, "FlateDecode"))
|
|
|
|
|
{
|
|
|
|
|
// Flate compression
|
2021-05-08 01:51:38 +02:00
|
|
|
|
pdfio_dict_t *params = pdfioDictGetDict(dict, "DecodeParms");
|
|
|
|
|
// Decoding parameters
|
|
|
|
|
int bpc = (int)pdfioDictGetNumber(params, "BitsPerComponent");
|
2021-05-06 22:07:58 +02:00
|
|
|
|
// Bits per component
|
2021-05-08 01:51:38 +02:00
|
|
|
|
int colors = (int)pdfioDictGetNumber(params, "Colors");
|
2021-05-06 22:07:58 +02:00
|
|
|
|
// Number of colors
|
2021-05-08 01:51:38 +02:00
|
|
|
|
int columns = (int)pdfioDictGetNumber(params, "Columns");
|
2021-05-06 22:07:58 +02:00
|
|
|
|
// Number of columns
|
2021-05-08 01:51:38 +02:00
|
|
|
|
int predictor = (int)pdfioDictGetNumber(params, "Predictor");
|
2021-05-06 22:07:58 +02:00
|
|
|
|
// Predictory value, if any
|
2021-08-25 15:30:03 +02:00
|
|
|
|
int status; // ZLIB status
|
2021-08-30 16:55:45 +02:00
|
|
|
|
ssize_t rbytes; // Bytes read
|
2021-05-08 01:51:38 +02:00
|
|
|
|
|
|
|
|
|
PDFIO_DEBUG("_pdfioStreamOpen: FlateDecode - BitsPerComponent=%d, Colors=%d, Columns=%d, Predictor=%d\n", bpc, colors, columns, predictor);
|
2021-05-06 22:07:58 +02:00
|
|
|
|
|
|
|
|
|
st->filter = PDFIO_FILTER_FLATE;
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
if (bpc == 0)
|
|
|
|
|
{
|
|
|
|
|
bpc = 8;
|
|
|
|
|
}
|
|
|
|
|
else if (bpc < 1 || bpc == 3 || (bpc > 4 && bpc < 8) || (bpc > 8 && bpc < 16) || bpc > 16)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported BitsPerColor value %d.", bpc);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (colors == 0)
|
|
|
|
|
{
|
|
|
|
|
colors = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (colors < 0 || colors > 4)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (columns == 0)
|
|
|
|
|
{
|
|
|
|
|
columns = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (columns < 0)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((predictor > 2 && predictor < 10) || predictor > 15)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported Predictor function %d.", predictor);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
else if (predictor > 1)
|
|
|
|
|
{
|
|
|
|
|
// Using a predictor function
|
|
|
|
|
st->predictor = (_pdfio_predictor_t)predictor;
|
|
|
|
|
st->pbpixel = (size_t)(bpc * colors + 7) / 8;
|
|
|
|
|
st->pbsize = (size_t)(bpc * colors * columns + 7) / 8;
|
|
|
|
|
if (predictor >= 10)
|
|
|
|
|
st->pbsize ++; // Add PNG predictor byte
|
|
|
|
|
|
2021-06-01 23:48:36 +02:00
|
|
|
|
if ((st->prbuffer = calloc(1, st->pbsize - 1)) == NULL || (st->psbuffer = calloc(1, st->pbsize)) == NULL)
|
2021-05-08 01:51:38 +02:00
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Predictor buffers.", (unsigned long)st->pbsize);
|
2021-06-01 23:48:36 +02:00
|
|
|
|
free(st->prbuffer);
|
|
|
|
|
free(st->psbuffer);
|
2021-05-08 01:51:38 +02:00
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
st->predictor = _PDFIO_PREDICTOR_NONE;
|
|
|
|
|
|
2023-11-15 14:38:47 +01:00
|
|
|
|
PDFIO_DEBUG("_pdfioStreamOpen: pos=%ld\n", (long)_pdfioFileTell(st->pdf));
|
2021-08-30 16:55:45 +02:00
|
|
|
|
if (sizeof(st->cbuffer) > st->remaining)
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
|
|
|
|
|
else
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer));
|
|
|
|
|
|
|
|
|
|
if (rbytes <= 0)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Unable to read bytes for stream.");
|
|
|
|
|
free(st->prbuffer);
|
|
|
|
|
free(st->psbuffer);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-24 16:59:25 +02:00
|
|
|
|
if (st->crypto_cb)
|
2021-10-31 16:12:54 +01:00
|
|
|
|
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
|
2021-10-24 16:59:25 +02:00
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
st->flate.next_in = (Bytef *)st->cbuffer;
|
2021-08-30 16:55:45 +02:00
|
|
|
|
st->flate.avail_in = (uInt)rbytes;
|
|
|
|
|
|
2021-06-21 22:18:06 +02:00
|
|
|
|
PDFIO_DEBUG("_pdfioStreamOpen: avail_in=%u, cbuffer=<%02X%02X%02X%02X%02X%02X%02X%02X...>\n", st->flate.avail_in, st->cbuffer[0], st->cbuffer[1], st->cbuffer[2], st->cbuffer[3], st->cbuffer[4], st->cbuffer[5], st->cbuffer[6], st->cbuffer[7]);
|
2021-06-21 22:03:05 +02:00
|
|
|
|
|
2021-08-25 15:30:03 +02:00
|
|
|
|
if ((status = inflateInit(&(st->flate))) != Z_OK)
|
2021-05-07 14:47:49 +02:00
|
|
|
|
{
|
2021-08-25 15:30:03 +02:00
|
|
|
|
_pdfioFileError(st->pdf, "Unable to start Flate filter: %s", zstrerror(status));
|
2021-06-01 23:48:36 +02:00
|
|
|
|
free(st->prbuffer);
|
|
|
|
|
free(st->psbuffer);
|
2021-05-07 14:47:49 +02:00
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st->remaining -= st->flate.avail_in;
|
2021-05-06 22:07:58 +02:00
|
|
|
|
}
|
|
|
|
|
else if (!strcmp(filter, "LZWDecode"))
|
|
|
|
|
{
|
|
|
|
|
// LZW compression
|
|
|
|
|
st->filter = PDFIO_FILTER_LZW;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Something else we don't support
|
|
|
|
|
_pdfioFileError(st->pdf, "Unsupported stream filter '/%s'.", filter);
|
|
|
|
|
free(st);
|
|
|
|
|
return (NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Just return the stream data as-is...
|
|
|
|
|
st->filter = PDFIO_FILTER_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (st);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-05-01 23:50:52 +02:00
|
|
|
|
//
|
|
|
|
|
// 'pdfioStreamPeek()' - Peek at data in a stream.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
ssize_t // O - Bytes returned or `-1` on error
|
|
|
|
|
pdfioStreamPeek(pdfio_stream_t *st, // I - Stream
|
|
|
|
|
void *buffer, // I - Buffer
|
|
|
|
|
size_t bytes) // I - Size of buffer
|
2021-04-26 16:42:01 +02:00
|
|
|
|
{
|
2021-05-07 14:47:49 +02:00
|
|
|
|
size_t remaining; // Remaining bytes in buffer
|
2021-04-30 13:06:56 +02:00
|
|
|
|
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
|
|
|
|
// Range check input...
|
|
|
|
|
if (!st || st->pdf->mode != _PDFIO_MODE_READ || !buffer || !bytes)
|
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
|
|
// See if we have enough bytes in the buffer...
|
|
|
|
|
if ((remaining = (size_t)(st->bufend - st->bufptr)) < bytes)
|
|
|
|
|
{
|
|
|
|
|
// No, shift the buffer and read more
|
|
|
|
|
ssize_t rbytes; // Bytes read
|
|
|
|
|
|
|
|
|
|
if (remaining > 0)
|
|
|
|
|
memmove(st->buffer, st->bufptr, remaining);
|
|
|
|
|
|
|
|
|
|
st->bufptr = st->buffer;
|
|
|
|
|
st->bufend = st->buffer + remaining;
|
|
|
|
|
|
2021-08-24 19:49:43 +02:00
|
|
|
|
if ((rbytes = stream_read(st, st->bufend, sizeof(st->buffer) - remaining)) > 0)
|
2021-05-07 14:47:49 +02:00
|
|
|
|
{
|
|
|
|
|
st->bufend += rbytes;
|
|
|
|
|
remaining += (size_t)rbytes;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy bytes from the buffer...
|
|
|
|
|
if (bytes > remaining)
|
|
|
|
|
bytes = remaining;
|
|
|
|
|
|
|
|
|
|
memcpy(buffer, st->bufptr, bytes);
|
|
|
|
|
|
|
|
|
|
// Return the number of bytes that were copied...
|
|
|
|
|
return ((ssize_t)bytes);
|
2021-04-26 16:42:01 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
2021-04-30 13:06:56 +02:00
|
|
|
|
// 'pdfioStreamPrintf()' - Write a formatted string to a stream.
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
|
|
|
|
|
2021-04-30 13:06:56 +02:00
|
|
|
|
bool // O - `true` on success, `false` on failure
|
|
|
|
|
pdfioStreamPrintf(
|
|
|
|
|
pdfio_stream_t *st, // I - Stream
|
|
|
|
|
const char *format, // I - `printf`-style format string
|
|
|
|
|
...) // I - Additional arguments as needed
|
2021-04-17 02:41:46 +02:00
|
|
|
|
{
|
2021-04-30 13:06:56 +02:00
|
|
|
|
char buffer[8192]; // String buffer
|
|
|
|
|
va_list ap; // Argument pointer
|
|
|
|
|
|
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
// Range check input...
|
|
|
|
|
if (!st || st->pdf->mode != _PDFIO_MODE_WRITE || !format)
|
|
|
|
|
return (false);
|
|
|
|
|
|
2021-04-30 13:06:56 +02:00
|
|
|
|
// Format the string...
|
|
|
|
|
va_start(ap, format);
|
|
|
|
|
vsnprintf(buffer, sizeof(buffer), format, ap);
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
|
|
// Write the string...
|
|
|
|
|
return (pdfioStreamWrite(st, buffer, strlen(buffer)));
|
2021-04-17 02:41:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-06-21 13:58:23 +02:00
|
|
|
|
//
|
2021-10-12 15:13:30 +02:00
|
|
|
|
// 'pdfioStreamPutChar()' - Write a single character to a stream.
|
2021-06-21 13:58:23 +02:00
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
bool // O - `true` on success, `false` on failure
|
|
|
|
|
pdfioStreamPutChar(pdfio_stream_t *st, // I - Stream
|
|
|
|
|
int ch) // I - Character
|
|
|
|
|
{
|
|
|
|
|
char buffer[1]; // Write buffer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!st || st->pdf->mode != _PDFIO_MODE_WRITE)
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
buffer[0] = (char)ch;
|
|
|
|
|
|
|
|
|
|
return (pdfioStreamWrite(st, buffer, 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
2021-04-30 13:06:56 +02:00
|
|
|
|
// 'pdfioStreamPuts()' - Write a literal string to a stream.
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
|
|
|
|
|
2021-04-30 13:06:56 +02:00
|
|
|
|
bool // O - `true` on success, `false` on failure
|
|
|
|
|
pdfioStreamPuts(pdfio_stream_t *st, // I - Stream
|
|
|
|
|
const char *s) // I - Literal string
|
2021-04-17 02:41:46 +02:00
|
|
|
|
{
|
2021-05-08 01:51:38 +02:00
|
|
|
|
if (!st || st->pdf->mode != _PDFIO_MODE_WRITE || !s)
|
2021-04-30 13:06:56 +02:00
|
|
|
|
return (false);
|
|
|
|
|
else
|
|
|
|
|
return (pdfioStreamWrite(st, s, strlen(s)));
|
2021-04-17 02:41:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
2021-04-30 13:06:56 +02:00
|
|
|
|
// 'pdfioStreamRead()' - Read data from a stream.
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
2021-06-04 16:33:39 +02:00
|
|
|
|
// This function reads data from a stream. When reading decoded image data
|
|
|
|
|
// from a stream, you *must* read whole scanlines. The
|
|
|
|
|
// @link pdfioImageGetBytesPerLine@ function can be used to determine the
|
|
|
|
|
// proper read length.
|
|
|
|
|
//
|
2021-04-17 02:41:46 +02:00
|
|
|
|
|
2021-04-30 13:06:56 +02:00
|
|
|
|
ssize_t // O - Number of bytes read or `-1` on error
|
|
|
|
|
pdfioStreamRead(
|
|
|
|
|
pdfio_stream_t *st, // I - Stream
|
|
|
|
|
void *buffer, // I - Buffer
|
|
|
|
|
size_t bytes) // I - Bytes to read
|
2021-04-17 02:41:46 +02:00
|
|
|
|
{
|
2021-05-07 14:47:49 +02:00
|
|
|
|
char *bufptr = (char *)buffer;
|
|
|
|
|
// Pointer into buffer
|
|
|
|
|
size_t remaining; // Remaining bytes in buffer
|
|
|
|
|
ssize_t rbytes; // Bytes read
|
2021-04-30 13:06:56 +02:00
|
|
|
|
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
|
|
|
|
// Range check input...
|
|
|
|
|
if (!st || st->pdf->mode != _PDFIO_MODE_READ || !buffer || !bytes)
|
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
|
|
// Loop until we have the requested bytes or hit the end of the stream...
|
|
|
|
|
while ((remaining = (size_t)(st->bufend - st->bufptr)) < bytes)
|
|
|
|
|
{
|
|
|
|
|
memcpy(bufptr, st->bufptr, remaining);
|
|
|
|
|
bufptr += remaining;
|
|
|
|
|
bytes -= remaining;
|
|
|
|
|
|
|
|
|
|
if (bytes >= sizeof(st->buffer))
|
|
|
|
|
{
|
|
|
|
|
// Read large amounts directly to caller's buffer...
|
|
|
|
|
if ((rbytes = stream_read(st, bufptr, bytes)) > 0)
|
|
|
|
|
bufptr += rbytes;
|
|
|
|
|
|
2021-05-30 02:00:48 +02:00
|
|
|
|
bytes = 0;
|
2021-05-07 14:47:49 +02:00
|
|
|
|
st->bufptr = st->bufend = st->buffer;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if ((rbytes = stream_read(st, st->buffer, sizeof(st->buffer))) > 0)
|
|
|
|
|
{
|
|
|
|
|
st->bufptr = st->buffer;
|
|
|
|
|
st->bufend = st->buffer + rbytes;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
st->bufptr = st->bufend = st->buffer;
|
2021-05-08 01:51:38 +02:00
|
|
|
|
bytes = 0;
|
2021-05-07 14:47:49 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy any remaining bytes from the stream buffer...
|
|
|
|
|
if (bytes > 0)
|
|
|
|
|
{
|
|
|
|
|
memcpy(bufptr, st->bufptr, bytes);
|
|
|
|
|
bufptr += bytes;
|
|
|
|
|
st->bufptr += bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the number of bytes that were read...
|
|
|
|
|
return (bufptr - (char *)buffer);
|
2021-04-17 02:41:46 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
2021-04-30 13:06:56 +02:00
|
|
|
|
// 'pdfioStreamWrite()' - Write data to a stream.
|
2021-04-17 02:41:46 +02:00
|
|
|
|
//
|
|
|
|
|
|
2021-04-30 13:06:56 +02:00
|
|
|
|
bool // O - `true` on success or `false` on failure
|
|
|
|
|
pdfioStreamWrite(
|
|
|
|
|
pdfio_stream_t *st, // I - Stream
|
|
|
|
|
const void *buffer, // I - Data to write
|
|
|
|
|
size_t bytes) // I - Number of bytes to write
|
2021-04-17 02:41:46 +02:00
|
|
|
|
{
|
2021-06-04 16:56:23 +02:00
|
|
|
|
size_t pbpixel, // Size of pixel in bytes
|
|
|
|
|
pbline, // Bytes per line
|
2021-06-02 01:29:48 +02:00
|
|
|
|
remaining; // Remaining bytes on this line
|
2021-06-04 16:56:23 +02:00
|
|
|
|
const unsigned char *bufptr, // Pointer into buffer
|
|
|
|
|
*bufsecond; // Pointer to second pixel in buffer
|
2021-06-01 23:48:36 +02:00
|
|
|
|
unsigned char *sptr, // Pointer into sbuffer
|
|
|
|
|
*pptr; // Previous raw buffer
|
2021-05-16 17:39:05 +02:00
|
|
|
|
|
|
|
|
|
|
2021-06-01 23:10:36 +02:00
|
|
|
|
PDFIO_DEBUG("pdfioStreamWrite(st=%p, buffer=%p, bytes=%lu)\n", st, buffer, (unsigned long)bytes);
|
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
// Range check input...
|
|
|
|
|
if (!st || st->pdf->mode != _PDFIO_MODE_WRITE || !buffer || !bytes)
|
|
|
|
|
return (false);
|
2021-04-17 02:41:46 +02:00
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
// Write it...
|
|
|
|
|
if (st->filter == PDFIO_FILTER_NONE)
|
|
|
|
|
{
|
2021-10-12 15:13:30 +02:00
|
|
|
|
// No filtering...
|
|
|
|
|
if (st->crypto_cb)
|
|
|
|
|
{
|
|
|
|
|
// Encrypt data before writing...
|
2021-10-24 02:33:12 +02:00
|
|
|
|
uint8_t temp[8192]; // Temporary buffer
|
|
|
|
|
size_t cbytes, // Current bytes
|
|
|
|
|
outbytes; // Output bytes
|
2021-10-12 15:13:30 +02:00
|
|
|
|
|
|
|
|
|
bufptr = (const unsigned char *)buffer;
|
|
|
|
|
|
|
|
|
|
while (bytes > 0)
|
|
|
|
|
{
|
2021-10-24 02:33:12 +02:00
|
|
|
|
if (st->bufptr > st->buffer || bytes < 16)
|
|
|
|
|
{
|
|
|
|
|
// Write through the stream's buffer...
|
2021-10-31 16:12:54 +01:00
|
|
|
|
if ((cbytes = bytes) > (size_t)(st->bufend - st->bufptr))
|
|
|
|
|
cbytes = (size_t)(st->bufend - st->bufptr);
|
2021-10-24 02:33:12 +02:00
|
|
|
|
|
|
|
|
|
memcpy(st->bufptr, bufptr, cbytes);
|
|
|
|
|
st->bufptr += cbytes;
|
|
|
|
|
if (st->bufptr >= st->bufend)
|
|
|
|
|
{
|
|
|
|
|
// Encrypt and flush
|
2021-10-24 16:59:25 +02:00
|
|
|
|
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, (uint8_t *)st->buffer, sizeof(st->buffer));
|
2021-10-24 02:33:12 +02:00
|
|
|
|
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
|
|
|
|
|
return (false);
|
|
|
|
|
|
|
|
|
|
st->bufptr = st->buffer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Write directly up to sizeof(temp) bytes...
|
|
|
|
|
if ((cbytes = bytes) > sizeof(temp))
|
|
|
|
|
cbytes = sizeof(temp);
|
|
|
|
|
if (cbytes & 15)
|
|
|
|
|
{
|
|
|
|
|
// AES has a 16-byte block size, so save the last few bytes...
|
2021-10-31 16:12:54 +01:00
|
|
|
|
cbytes &= (size_t)~15;
|
2021-10-24 02:33:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, bufptr, cbytes);
|
|
|
|
|
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
2021-10-12 15:13:30 +02:00
|
|
|
|
|
|
|
|
|
bytes -= cbytes;
|
|
|
|
|
bufptr += cbytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (true);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Write unencrypted...
|
|
|
|
|
return (_pdfioFileWrite(st->pdf, buffer, bytes));
|
|
|
|
|
}
|
2021-05-16 17:39:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-04 16:56:23 +02:00
|
|
|
|
pbline = st->pbsize - 1;
|
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
if (st->predictor == _PDFIO_PREDICTOR_NONE)
|
|
|
|
|
{
|
|
|
|
|
// No predictor, just write it out straight...
|
|
|
|
|
return (stream_write(st, buffer, bytes));
|
|
|
|
|
}
|
|
|
|
|
else if ((bytes % pbline) != 0)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Write buffer size must be a multiple of a complete row.");
|
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-04 16:56:23 +02:00
|
|
|
|
pbpixel = st->pbpixel;
|
|
|
|
|
bufptr = (const unsigned char *)buffer;
|
2021-06-21 17:39:06 +02:00
|
|
|
|
bufsecond = bufptr + pbpixel;
|
2021-06-04 16:56:23 +02:00
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
while (bytes > 0)
|
|
|
|
|
{
|
|
|
|
|
// Store the PNG predictor in the first byte of the buffer...
|
|
|
|
|
if (st->predictor == _PDFIO_PREDICTOR_PNG_AUTO)
|
2021-06-01 23:48:36 +02:00
|
|
|
|
st->psbuffer[0] = 4; // Use Paeth predictor for auto...
|
2021-05-16 17:39:05 +02:00
|
|
|
|
else
|
2021-06-01 23:48:36 +02:00
|
|
|
|
st->psbuffer[0] = (unsigned char)(st->predictor - 10);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
|
|
|
|
|
// Then process the current line using the specified PNG predictor...
|
2021-06-01 23:48:36 +02:00
|
|
|
|
sptr = st->psbuffer + 1;
|
|
|
|
|
pptr = st->prbuffer;
|
2021-05-16 17:39:05 +02:00
|
|
|
|
|
|
|
|
|
switch (st->predictor)
|
|
|
|
|
{
|
|
|
|
|
default :
|
|
|
|
|
// Anti-compiler-warning code (NONE is handled above, TIFF is not supported for writing)
|
|
|
|
|
return (false);
|
|
|
|
|
|
2021-06-01 23:10:36 +02:00
|
|
|
|
case _PDFIO_PREDICTOR_PNG_NONE :
|
|
|
|
|
// No PNG predictor...
|
2021-06-01 23:48:36 +02:00
|
|
|
|
memcpy(sptr, buffer, pbline);
|
2021-06-01 23:10:36 +02:00
|
|
|
|
break;
|
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
case _PDFIO_PREDICTOR_PNG_SUB :
|
|
|
|
|
// Encode the difference from the previous column
|
2021-06-01 23:48:36 +02:00
|
|
|
|
for (remaining = pbline; remaining > 0; remaining --, bufptr ++, sptr ++)
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
2021-06-02 01:29:48 +02:00
|
|
|
|
if (bufptr >= bufsecond)
|
2021-06-21 17:39:06 +02:00
|
|
|
|
*sptr = *bufptr - bufptr[-(int)pbpixel];
|
2021-05-16 17:39:05 +02:00
|
|
|
|
else
|
2021-06-01 23:48:36 +02:00
|
|
|
|
*sptr = *bufptr;
|
2021-05-16 17:39:05 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case _PDFIO_PREDICTOR_PNG_UP :
|
|
|
|
|
// Encode the difference from the previous line
|
2021-06-01 23:48:36 +02:00
|
|
|
|
for (remaining = pbline; remaining > 0; remaining --, bufptr ++, sptr ++, pptr ++)
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
2021-06-01 23:48:36 +02:00
|
|
|
|
*sptr = *bufptr - *pptr;
|
2021-05-16 17:39:05 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case _PDFIO_PREDICTOR_PNG_AVERAGE :
|
|
|
|
|
// Encode the difference with the average of the previous column and line
|
2021-06-01 23:48:36 +02:00
|
|
|
|
for (remaining = pbline; remaining > 0; remaining --, bufptr ++, sptr ++, pptr ++)
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
2021-06-02 01:29:48 +02:00
|
|
|
|
if (bufptr >= bufsecond)
|
2021-06-21 17:39:06 +02:00
|
|
|
|
*sptr = *bufptr - (bufptr[-(int)pbpixel] + *pptr) / 2;
|
2021-05-16 17:39:05 +02:00
|
|
|
|
else
|
2021-06-01 23:48:36 +02:00
|
|
|
|
*sptr = *bufptr - *pptr / 2;
|
2021-05-16 17:39:05 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case _PDFIO_PREDICTOR_PNG_PAETH :
|
|
|
|
|
case _PDFIO_PREDICTOR_PNG_AUTO :
|
|
|
|
|
// Encode the difference with a linear transform function
|
2021-06-01 23:48:36 +02:00
|
|
|
|
for (remaining = pbline; remaining > 0; remaining --, bufptr ++, sptr ++, pptr ++)
|
2021-05-16 17:39:05 +02:00
|
|
|
|
{
|
2021-06-02 01:29:48 +02:00
|
|
|
|
if (bufptr >= bufsecond)
|
2021-06-21 17:39:06 +02:00
|
|
|
|
*sptr = *bufptr - stream_paeth(bufptr[-(int)pbpixel], *pptr, pptr[-(int)pbpixel]);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
else
|
2021-06-01 23:48:36 +02:00
|
|
|
|
*sptr = *bufptr - stream_paeth(0, *pptr, 0);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Write the encoded line...
|
2021-06-01 23:48:36 +02:00
|
|
|
|
if (!stream_write(st, st->psbuffer, st->pbsize))
|
2021-05-16 17:39:05 +02:00
|
|
|
|
return (false);
|
|
|
|
|
|
2021-06-01 23:48:36 +02:00
|
|
|
|
memcpy(st->prbuffer, buffer, pbline);
|
|
|
|
|
bytes -= pbline;
|
2021-05-16 17:39:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (true);
|
2021-04-30 13:06:56 +02:00
|
|
|
|
}
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
|
|
|
|
|
2021-05-09 01:04:42 +02:00
|
|
|
|
//
|
|
|
|
|
// 'stream_paeth()' - PaethPredictor function for PNG decompression filter.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
static unsigned char // O - Predictor value
|
|
|
|
|
stream_paeth(unsigned char a, // I - Left pixel
|
|
|
|
|
unsigned char b, // I - Top pixel
|
|
|
|
|
unsigned char c) // I - Top-left pixel
|
|
|
|
|
{
|
|
|
|
|
int p = a + b - c; // Initial estimate
|
|
|
|
|
int pa = abs(p - a); // Distance to a
|
|
|
|
|
int pb = abs(p - b); // Distance to b
|
|
|
|
|
int pc = abs(p - c); // Distance to c
|
|
|
|
|
|
|
|
|
|
return ((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-05-07 14:47:49 +02:00
|
|
|
|
//
|
|
|
|
|
// 'stream_read()' - Read data from a stream, including filters.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
static ssize_t // O - Number of bytes read or `-1` on error
|
|
|
|
|
stream_read(pdfio_stream_t *st, // I - Stream
|
|
|
|
|
char *buffer, // I - Buffer
|
|
|
|
|
size_t bytes) // I - Number of bytes to read
|
|
|
|
|
{
|
|
|
|
|
ssize_t rbytes; // Bytes read
|
2023-03-20 14:27:19 +01:00
|
|
|
|
uInt avail_in, avail_out; // Previous flate values
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (st->filter == PDFIO_FILTER_NONE)
|
|
|
|
|
{
|
|
|
|
|
// No filtering, but limit reads to the length of the stream...
|
|
|
|
|
if (bytes > st->remaining)
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, buffer, st->remaining);
|
|
|
|
|
else
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, buffer, bytes);
|
|
|
|
|
|
|
|
|
|
if (rbytes > 0)
|
2021-10-24 16:59:25 +02:00
|
|
|
|
{
|
2021-05-07 14:47:49 +02:00
|
|
|
|
st->remaining -= (size_t)rbytes;
|
|
|
|
|
|
2021-10-24 16:59:25 +02:00
|
|
|
|
if (st->crypto_cb)
|
2021-10-31 16:12:54 +01:00
|
|
|
|
(st->crypto_cb)(&st->crypto_ctx, (uint8_t *)buffer, (uint8_t *)buffer, (size_t)rbytes);
|
2021-10-24 16:59:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-07 14:47:49 +02:00
|
|
|
|
return (rbytes);
|
|
|
|
|
}
|
|
|
|
|
else if (st->filter == PDFIO_FILTER_FLATE)
|
|
|
|
|
{
|
|
|
|
|
// Deflate compression...
|
|
|
|
|
int status; // Status of decompression
|
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
if (st->predictor == _PDFIO_PREDICTOR_NONE)
|
2021-05-07 14:47:49 +02:00
|
|
|
|
{
|
2021-05-08 01:51:38 +02:00
|
|
|
|
// Decompress into the buffer...
|
|
|
|
|
PDFIO_DEBUG("stream_read: No predictor.\n");
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
if (st->flate.avail_in == 0)
|
|
|
|
|
{
|
|
|
|
|
// Read more from the file...
|
|
|
|
|
if (sizeof(st->cbuffer) > st->remaining)
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
|
|
|
|
|
else
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer));
|
|
|
|
|
|
|
|
|
|
if (rbytes <= 0)
|
|
|
|
|
return (-1); // End of file...
|
|
|
|
|
|
2021-10-24 16:59:25 +02:00
|
|
|
|
if (st->crypto_cb)
|
2021-10-31 16:12:54 +01:00
|
|
|
|
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
|
2021-10-24 16:59:25 +02:00
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
st->remaining -= (size_t)rbytes;
|
|
|
|
|
st->flate.next_in = (Bytef *)st->cbuffer;
|
|
|
|
|
st->flate.avail_in = (uInt)rbytes;
|
|
|
|
|
}
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
st->flate.next_out = (Bytef *)buffer;
|
|
|
|
|
st->flate.avail_out = (uInt)bytes;
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
2023-03-20 14:27:19 +01:00
|
|
|
|
avail_in = st->flate.avail_in;
|
|
|
|
|
avail_out = st->flate.avail_out;
|
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
|
|
|
|
|
{
|
2023-12-18 16:04:00 +01:00
|
|
|
|
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
|
2021-05-08 01:51:38 +02:00
|
|
|
|
return (-1);
|
|
|
|
|
}
|
2023-03-20 14:27:19 +01:00
|
|
|
|
else if (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Corrupt stream data.");
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
return (st->flate.next_out - (Bytef *)buffer);
|
|
|
|
|
}
|
|
|
|
|
else if (st->predictor == _PDFIO_PREDICTOR_TIFF2)
|
2021-05-07 14:47:49 +02:00
|
|
|
|
{
|
2021-08-22 04:51:25 +02:00
|
|
|
|
size_t pbpixel = st->pbpixel,
|
|
|
|
|
// Size of pixel in bytes
|
|
|
|
|
remaining = st->pbsize;
|
|
|
|
|
// Remaining bytes
|
|
|
|
|
unsigned char *bufptr = (unsigned char *)buffer,
|
|
|
|
|
// Pointer into buffer
|
|
|
|
|
*bufsecond = (unsigned char *)buffer + pbpixel,
|
|
|
|
|
// Pointer to second pixel in buffer
|
|
|
|
|
*sptr = st->psbuffer;
|
|
|
|
|
// Current (raw) line
|
|
|
|
|
|
|
|
|
|
PDFIO_DEBUG("stream_read: TIFF predictor 2.\n");
|
|
|
|
|
|
|
|
|
|
if (bytes < st->pbsize)
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Read buffer too small for stream.");
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st->flate.next_out = (Bytef *)sptr;
|
|
|
|
|
st->flate.avail_out = (uInt)st->pbsize;
|
|
|
|
|
|
|
|
|
|
while (st->flate.avail_out > 0)
|
|
|
|
|
{
|
|
|
|
|
if (st->flate.avail_in == 0)
|
|
|
|
|
{
|
|
|
|
|
// Read more from the file...
|
|
|
|
|
if (sizeof(st->cbuffer) > st->remaining)
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
|
|
|
|
|
else
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer));
|
|
|
|
|
|
|
|
|
|
if (rbytes <= 0)
|
|
|
|
|
return (-1); // End of file...
|
|
|
|
|
|
2021-10-24 16:59:25 +02:00
|
|
|
|
if (st->crypto_cb)
|
2021-10-31 16:12:54 +01:00
|
|
|
|
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
|
2021-10-24 16:59:25 +02:00
|
|
|
|
|
2021-08-22 04:51:25 +02:00
|
|
|
|
st->remaining -= (size_t)rbytes;
|
|
|
|
|
st->flate.next_in = (Bytef *)st->cbuffer;
|
|
|
|
|
st->flate.avail_in = (uInt)rbytes;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-20 14:27:19 +01:00
|
|
|
|
avail_in = st->flate.avail_in;
|
|
|
|
|
avail_out = st->flate.avail_out;
|
|
|
|
|
|
2021-08-22 04:51:25 +02:00
|
|
|
|
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
|
|
|
|
|
{
|
2023-12-18 16:04:00 +01:00
|
|
|
|
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
|
2021-08-22 04:51:25 +02:00
|
|
|
|
return (-1);
|
|
|
|
|
}
|
2023-03-20 14:27:19 +01:00
|
|
|
|
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
|
2021-08-22 04:51:25 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (st->flate.avail_out > 0)
|
|
|
|
|
return (-1); // Early end of stream
|
|
|
|
|
|
|
|
|
|
for (; bufptr < bufsecond; remaining --, sptr ++)
|
|
|
|
|
*bufptr++ = *sptr;
|
|
|
|
|
for (; remaining > 0; remaining --, sptr ++, bufptr ++)
|
|
|
|
|
*bufptr = *sptr + bufptr[-(int)pbpixel];
|
|
|
|
|
|
|
|
|
|
return ((ssize_t)st->pbsize);
|
2021-05-07 14:47:49 +02:00
|
|
|
|
}
|
2021-05-08 01:51:38 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// PNG predictor
|
|
|
|
|
size_t pbpixel = st->pbpixel,
|
|
|
|
|
// Size of pixel in bytes
|
2021-06-02 01:29:48 +02:00
|
|
|
|
remaining = st->pbsize - 1;
|
2021-05-08 01:51:38 +02:00
|
|
|
|
// Remaining bytes
|
|
|
|
|
unsigned char *bufptr = (unsigned char *)buffer,
|
|
|
|
|
// Pointer into buffer
|
2021-06-02 01:29:48 +02:00
|
|
|
|
*bufsecond = (unsigned char *)buffer + pbpixel,
|
|
|
|
|
// Pointer to second pixel in buffer
|
2021-06-01 23:48:36 +02:00
|
|
|
|
*sptr = st->psbuffer + 1,
|
2021-05-08 01:51:38 +02:00
|
|
|
|
// Current (raw) line
|
2021-06-01 23:48:36 +02:00
|
|
|
|
*pptr = st->prbuffer;
|
2021-05-08 01:51:38 +02:00
|
|
|
|
// Previous (raw) line
|
|
|
|
|
|
|
|
|
|
PDFIO_DEBUG("stream_read: PNG predictor.\n");
|
|
|
|
|
|
|
|
|
|
if (bytes < (st->pbsize - 1))
|
|
|
|
|
{
|
|
|
|
|
_pdfioFileError(st->pdf, "Read buffer too small for stream.");
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-01 23:48:36 +02:00
|
|
|
|
st->flate.next_out = (Bytef *)sptr - 1;
|
2021-05-08 01:51:38 +02:00
|
|
|
|
st->flate.avail_out = (uInt)st->pbsize;
|
2021-05-07 14:47:49 +02:00
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
while (st->flate.avail_out > 0)
|
|
|
|
|
{
|
|
|
|
|
if (st->flate.avail_in == 0)
|
|
|
|
|
{
|
|
|
|
|
// Read more from the file...
|
|
|
|
|
if (sizeof(st->cbuffer) > st->remaining)
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
|
|
|
|
|
else
|
|
|
|
|
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer));
|
|
|
|
|
|
|
|
|
|
if (rbytes <= 0)
|
2021-08-22 04:51:25 +02:00
|
|
|
|
return (-1); // End of file...
|
2021-05-08 01:51:38 +02:00
|
|
|
|
|
2021-10-24 16:59:25 +02:00
|
|
|
|
if (st->crypto_cb)
|
2021-10-31 16:12:54 +01:00
|
|
|
|
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
|
2021-10-24 16:59:25 +02:00
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
st->remaining -= (size_t)rbytes;
|
|
|
|
|
st->flate.next_in = (Bytef *)st->cbuffer;
|
|
|
|
|
st->flate.avail_in = (uInt)rbytes;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-20 14:27:19 +01:00
|
|
|
|
avail_in = st->flate.avail_in;
|
|
|
|
|
avail_out = st->flate.avail_out;
|
|
|
|
|
|
2021-05-08 01:51:38 +02:00
|
|
|
|
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
|
|
|
|
|
{
|
2023-12-18 16:04:00 +01:00
|
|
|
|
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
|
2021-05-08 01:51:38 +02:00
|
|
|
|
return (-1);
|
|
|
|
|
}
|
2023-03-20 14:27:19 +01:00
|
|
|
|
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
|
2021-05-08 01:51:38 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (st->flate.avail_out > 0)
|
2021-08-30 16:55:45 +02:00
|
|
|
|
{
|
|
|
|
|
// Early end of stream
|
|
|
|
|
PDFIO_DEBUG("stream_read: Early EOF (remaining=%u, avail_in=%d, avail_out=%d, data_type=%d, next_in=<%02X%02X%02X%02X...>).\n", (unsigned)st->remaining, st->flate.avail_in, st->flate.avail_out, st->flate.data_type, st->flate.next_in[0], st->flate.next_in[1], st->flate.next_in[2], st->flate.next_in[3]);
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
2021-05-08 01:51:38 +02:00
|
|
|
|
|
|
|
|
|
// Apply predictor for this line
|
2021-06-01 23:48:36 +02:00
|
|
|
|
PDFIO_DEBUG("stream_read: Line %02X %02X %02X %02X %02X.\n", sptr[-1], sptr[0], sptr[0], sptr[2], sptr[3]);
|
2021-05-08 01:51:38 +02:00
|
|
|
|
|
2021-06-01 23:48:36 +02:00
|
|
|
|
switch (sptr[-1])
|
2021-05-08 01:51:38 +02:00
|
|
|
|
{
|
|
|
|
|
case 0 : // None
|
2021-08-25 15:30:03 +02:00
|
|
|
|
case 10 : // None (for buggy PDF writers)
|
2021-06-01 23:48:36 +02:00
|
|
|
|
memcpy(buffer, sptr, remaining);
|
2021-05-08 01:51:38 +02:00
|
|
|
|
break;
|
|
|
|
|
case 1 : // Sub
|
2021-08-25 15:30:03 +02:00
|
|
|
|
case 11 : // Sub (for buggy PDF writers)
|
2021-06-02 01:29:48 +02:00
|
|
|
|
for (; bufptr < bufsecond; remaining --, sptr ++)
|
2021-06-01 23:48:36 +02:00
|
|
|
|
*bufptr++ = *sptr;
|
|
|
|
|
for (; remaining > 0; remaining --, sptr ++, bufptr ++)
|
2021-06-21 17:39:06 +02:00
|
|
|
|
*bufptr = *sptr + bufptr[-(int)pbpixel];
|
2021-05-08 01:51:38 +02:00
|
|
|
|
break;
|
|
|
|
|
case 2 : // Up
|
2021-08-25 15:30:03 +02:00
|
|
|
|
case 12 : // Up (for buggy PDF writers)
|
2021-06-01 23:48:36 +02:00
|
|
|
|
for (; remaining > 0; remaining --, sptr ++, pptr ++)
|
|
|
|
|
*bufptr++ = *sptr + *pptr;
|
2021-05-08 01:51:38 +02:00
|
|
|
|
break;
|
|
|
|
|
case 3 : // Average
|
2021-08-25 15:30:03 +02:00
|
|
|
|
case 13 : // Average (for buggy PDF writers)
|
2021-06-02 01:29:48 +02:00
|
|
|
|
for (; bufptr < bufsecond; remaining --, sptr ++, pptr ++)
|
2021-06-01 23:48:36 +02:00
|
|
|
|
*bufptr++ = *sptr + *pptr / 2;
|
|
|
|
|
for (; remaining > 0; remaining --, sptr ++, pptr ++, bufptr ++)
|
2021-06-21 17:39:06 +02:00
|
|
|
|
*bufptr = *sptr + (bufptr[-(int)pbpixel] + *pptr) / 2;
|
2021-05-08 01:51:38 +02:00
|
|
|
|
break;
|
|
|
|
|
case 4 : // Paeth
|
2021-08-25 15:30:03 +02:00
|
|
|
|
case 14 : // Paeth (for buggy PDF writers)
|
2021-06-02 01:29:48 +02:00
|
|
|
|
for (; bufptr < bufsecond; remaining --, sptr ++, pptr ++)
|
2021-06-01 23:48:36 +02:00
|
|
|
|
*bufptr++ = *sptr + stream_paeth(0, *pptr, 0);
|
|
|
|
|
for (; remaining > 0; remaining --, sptr ++, pptr ++, bufptr ++)
|
2021-06-21 17:39:06 +02:00
|
|
|
|
*bufptr = *sptr + stream_paeth(bufptr[-(int)pbpixel], *pptr, pptr[-(int)pbpixel]);
|
2021-05-08 01:51:38 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default :
|
2021-06-01 23:48:36 +02:00
|
|
|
|
_pdfioFileError(st->pdf, "Bad PNG filter %d in data stream.", sptr[-1]);
|
2021-05-08 01:51:38 +02:00
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy the computed line and swap buffers...
|
2021-06-01 23:48:36 +02:00
|
|
|
|
memcpy(st->prbuffer, buffer, st->pbsize - 1);
|
2021-05-08 01:51:38 +02:00
|
|
|
|
|
|
|
|
|
// Return the number of bytes we copied for this line...
|
|
|
|
|
return ((ssize_t)(st->pbsize - 1));
|
|
|
|
|
}
|
2021-05-07 14:47:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If we get here something bad happened...
|
|
|
|
|
return (-1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-16 17:39:05 +02:00
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// 'stream_write()' - Write flate-compressed data...
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
static bool // O - `true` on success, `false` on failure
|
|
|
|
|
stream_write(pdfio_stream_t *st, // I - Stream
|
|
|
|
|
const void *buffer, // I - Buffer to write
|
|
|
|
|
size_t bytes) // I - Number of bytes to write
|
|
|
|
|
{
|
|
|
|
|
int status; // Compression status
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Flate-compress the buffer...
|
|
|
|
|
st->flate.avail_in = (uInt)bytes;
|
|
|
|
|
st->flate.next_in = (Bytef *)buffer;
|
|
|
|
|
|
|
|
|
|
while (st->flate.avail_in > 0)
|
|
|
|
|
{
|
|
|
|
|
if (st->flate.avail_out < (sizeof(st->cbuffer) / 8))
|
|
|
|
|
{
|
|
|
|
|
// Flush the compression buffer...
|
2021-10-26 03:43:32 +02:00
|
|
|
|
size_t cbytes = sizeof(st->cbuffer) - st->flate.avail_out,
|
2021-10-24 02:33:12 +02:00
|
|
|
|
outbytes;
|
2021-10-23 20:37:25 +02:00
|
|
|
|
|
2021-10-12 15:13:30 +02:00
|
|
|
|
if (st->crypto_cb)
|
|
|
|
|
{
|
|
|
|
|
// Encrypt it first...
|
2021-10-31 16:12:54 +01:00
|
|
|
|
outbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, cbytes & (size_t)~15);
|
2021-10-24 02:33:12 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-10-26 03:43:32 +02:00
|
|
|
|
outbytes = cbytes;
|
2021-10-12 15:13:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-26 03:22:59 +02:00
|
|
|
|
// fprintf(stderr, "stream_write: bytes=%u, outbytes=%u\n", (unsigned)bytes, (unsigned)outbytes);
|
|
|
|
|
|
|
|
|
|
if (!_pdfioFileWrite(st->pdf, st->cbuffer, outbytes))
|
2021-05-16 17:39:05 +02:00
|
|
|
|
return (false);
|
|
|
|
|
|
2021-10-26 03:43:32 +02:00
|
|
|
|
if (cbytes > outbytes)
|
2021-10-24 02:33:12 +02:00
|
|
|
|
{
|
2021-10-26 03:43:32 +02:00
|
|
|
|
cbytes -= outbytes;
|
|
|
|
|
memmove(st->cbuffer, st->cbuffer + outbytes, cbytes);
|
2021-10-24 02:33:12 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-10-26 03:43:32 +02:00
|
|
|
|
cbytes = 0;
|
2021-10-24 02:33:12 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-26 03:43:32 +02:00
|
|
|
|
st->flate.next_out = (Bytef *)st->cbuffer + cbytes;
|
2021-10-31 16:12:54 +01:00
|
|
|
|
st->flate.avail_out = (uInt)(sizeof(st->cbuffer) - cbytes);
|
2021-05-16 17:39:05 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Deflate what we can this time...
|
|
|
|
|
status = deflate(&st->flate, Z_NO_FLUSH);
|
|
|
|
|
|
|
|
|
|
if (status < Z_OK && status != Z_BUF_ERROR)
|
|
|
|
|
{
|
2021-08-25 15:30:03 +02:00
|
|
|
|
_pdfioFileError(st->pdf, "Flate compression failed: %s", zstrerror(status));
|
2021-05-16 17:39:05 +02:00
|
|
|
|
return (false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (true);
|
|
|
|
|
}
|
2021-08-25 15:30:03 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// 'zstrerror()' - Return a string for a zlib error number.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
static const char * // O - Error string
|
|
|
|
|
zstrerror(int error) // I - Error number
|
|
|
|
|
{
|
|
|
|
|
switch (error)
|
|
|
|
|
{
|
|
|
|
|
case Z_OK :
|
|
|
|
|
return ("No error.");
|
|
|
|
|
|
|
|
|
|
case Z_STREAM_END :
|
|
|
|
|
return ("End of stream.");
|
|
|
|
|
|
|
|
|
|
case Z_NEED_DICT :
|
|
|
|
|
return ("Need a huffman dictinary.");
|
|
|
|
|
|
|
|
|
|
case Z_ERRNO :
|
|
|
|
|
return (strerror(errno));
|
|
|
|
|
|
|
|
|
|
case Z_STREAM_ERROR :
|
|
|
|
|
return ("Stream error.");
|
|
|
|
|
|
|
|
|
|
case Z_DATA_ERROR :
|
|
|
|
|
return ("Data error.");
|
|
|
|
|
|
|
|
|
|
case Z_MEM_ERROR :
|
|
|
|
|
return ("Out of memory.");
|
|
|
|
|
|
|
|
|
|
case Z_BUF_ERROR :
|
|
|
|
|
return ("Out of buffers.");
|
|
|
|
|
|
|
|
|
|
case Z_VERSION_ERROR :
|
|
|
|
|
return ("Mismatched zlib library.");
|
|
|
|
|
|
|
|
|
|
default :
|
|
|
|
|
return ("Unknown error.");
|
|
|
|
|
}
|
|
|
|
|
}
|