mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2024-12-27 05:48:20 +01:00
Initial writing support.
This commit is contained in:
parent
4bb81417a8
commit
548ff7d119
@ -573,7 +573,7 @@ _pdfioArrayWrite(pdfio_array_t *a) // I - Array
|
||||
// Write each value...
|
||||
for (i = a->num_values, v = a->values; i > 0; i --, v ++)
|
||||
{
|
||||
if (!_pdfioValueWrite(pdf, v))
|
||||
if (!_pdfioValueWrite(pdf, v, NULL))
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
@ -805,7 +805,7 @@ _pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary
|
||||
if (!_pdfioFilePuts(pdf, " 9999999999"))
|
||||
return (false);
|
||||
}
|
||||
else if (!_pdfioValueWrite(pdf, &pair->value))
|
||||
else if (!_pdfioValueWrite(pdf, &pair->value, NULL))
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
251
pdfio-file.c
251
pdfio-file.c
@ -27,6 +27,8 @@ static int compare_objs(pdfio_obj_t **a, pdfio_obj_t **b);
|
||||
static bool load_obj_stream(pdfio_obj_t *obj);
|
||||
static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj);
|
||||
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset);
|
||||
static bool write_catalog(pdfio_file_t *pdf);
|
||||
static bool write_pages(pdfio_file_t *pdf);
|
||||
static bool write_trailer(pdfio_file_t *pdf);
|
||||
|
||||
|
||||
@ -89,7 +91,14 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
|
||||
|
||||
// Close the file itself...
|
||||
if (pdf->mode == _PDFIO_MODE_WRITE)
|
||||
ret = write_trailer(pdf);
|
||||
{
|
||||
ret = false;
|
||||
|
||||
if (write_pages(pdf))
|
||||
if (write_catalog(pdf))
|
||||
if (write_trailer(pdf))
|
||||
ret = _pdfioFileFlush(pdf);
|
||||
}
|
||||
|
||||
if (close(pdf->fd) < 0)
|
||||
ret = false;
|
||||
@ -132,10 +141,13 @@ pdfio_file_t * // O - PDF file or `NULL` on error
|
||||
pdfioFileCreate(
|
||||
const char *filename, // I - Filename
|
||||
const char *version, // I - PDF version number or `NULL` for default (2.0)
|
||||
pdfio_rect_t *media_box, // I - Default MediaBox for pages
|
||||
pdfio_rect_t *crop_box, // I - Default CropBox for pages
|
||||
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
|
||||
void *error_data) // I - Error callback data, if any
|
||||
{
|
||||
pdfio_file_t *pdf; // PDF file
|
||||
pdfio_dict_t *dict; // Dictionary for pages object
|
||||
|
||||
|
||||
// Range check input...
|
||||
@ -169,6 +181,30 @@ pdfioFileCreate(
|
||||
pdf->error_cb = error_cb;
|
||||
pdf->error_data = error_data;
|
||||
|
||||
if (media_box)
|
||||
{
|
||||
pdf->media_box = *media_box;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default to "universal" size (intersection of A4 and US Letter)
|
||||
pdf->media_box.x2 = 210.0f * 72.0f / 25.4f;
|
||||
pdf->media_box.y2 = 11.0f * 72.0f;
|
||||
}
|
||||
|
||||
if (crop_box)
|
||||
{
|
||||
pdf->crop_box = *crop_box;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default to "universal" size (intersection of A4 and US Letter) with 1/4" margins
|
||||
pdf->crop_box.x1 = 18.0f;
|
||||
pdf->crop_box.y1 = 18.0f;
|
||||
pdf->crop_box.x2 = 210.0f * 72.0f / 25.4f - 18.0f;
|
||||
pdf->crop_box.y2 = 11.0f * 72.0f - 18.0f;
|
||||
}
|
||||
|
||||
// Create the file...
|
||||
if ((pdf->fd = open(filename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666)) < 0)
|
||||
{
|
||||
@ -187,6 +223,23 @@ pdfioFileCreate(
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
// Create the pages object...
|
||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
||||
{
|
||||
pdfioFileClose(pdf);
|
||||
unlink(filename);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
pdfioDictSetName(dict, "Type", "Pages");
|
||||
|
||||
if ((pdf->pages_root = pdfioFileCreateObject(pdf, dict)) == NULL)
|
||||
{
|
||||
pdfioFileClose(pdf);
|
||||
unlink(filename);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (pdf);
|
||||
}
|
||||
|
||||
@ -246,7 +299,6 @@ pdfioFileCreateObject(
|
||||
// Initialize the object...
|
||||
obj->pdf = pdf;
|
||||
obj->number = pdf->num_objs;
|
||||
obj->offset = _pdfioFileTell(pdf);
|
||||
obj->value.type = PDFIO_VALTYPE_DICT;
|
||||
obj->value.value.dict = dict;
|
||||
|
||||
@ -259,14 +311,73 @@ pdfioFileCreateObject(
|
||||
// 'pdfioFileCreatePage()' - Create a page in a PDF file.
|
||||
//
|
||||
|
||||
pdfio_obj_t * // O - New object
|
||||
pdfio_stream_t * // O - Contents stream
|
||||
pdfioFileCreatePage(pdfio_file_t *pdf, // I - PDF file
|
||||
pdfio_dict_t *dict) // I - Page dictionary
|
||||
{
|
||||
// TODO: Implement pdfioFileCreatePage
|
||||
(void)pdf;
|
||||
(void)dict;
|
||||
pdfio_obj_t *page, // Page object
|
||||
*contents; // Contents object
|
||||
pdfio_dict_t *contents_dict; // Dictionary for Contents object
|
||||
|
||||
|
||||
// Range check input...
|
||||
if (!pdf)
|
||||
return (NULL);
|
||||
|
||||
// Copy the page dictionary...
|
||||
if (dict)
|
||||
dict = pdfioDictCopy(pdf, dict);
|
||||
else
|
||||
dict = pdfioDictCreate(pdf);
|
||||
|
||||
// Make sure the page dictionary has all of the required keys...
|
||||
if (!_pdfioDictGetValue(dict, "CropBox"))
|
||||
pdfioDictSetRect(dict, "CropBox", &pdf->crop_box);
|
||||
|
||||
if (!_pdfioDictGetValue(dict, "MediaBox"))
|
||||
pdfioDictSetRect(dict, "MediaBox", &pdf->media_box);
|
||||
|
||||
pdfioDictSetObject(dict, "Parent", pdf->pages_root);
|
||||
|
||||
if (!_pdfioDictGetValue(dict, "Resources"))
|
||||
pdfioDictSetDict(dict, "Resources", pdfioDictCreate(pdf));
|
||||
|
||||
if (!_pdfioDictGetValue(dict, "Type"))
|
||||
pdfioDictSetName(dict, "Type", "Page");
|
||||
|
||||
// Create the page object...
|
||||
page = pdfioFileCreateObject(pdf, dict);
|
||||
|
||||
// Create a contents object to hold the contents of the page...
|
||||
contents_dict = pdfioDictCreate(pdf);
|
||||
pdfioDictSetName(contents_dict, "Filter", "FlateDecode");
|
||||
|
||||
contents = pdfioFileCreateObject(pdf, contents_dict);
|
||||
|
||||
// Add the contents stream to the pages object and write it...
|
||||
pdfioDictSetObject(dict, "Contents", contents);
|
||||
if (!pdfioObjClose(page))
|
||||
return (NULL);
|
||||
|
||||
// Add the page to the array of pages...
|
||||
if (pdf->num_pages >= pdf->alloc_pages)
|
||||
{
|
||||
pdfio_obj_t **temp = (pdfio_obj_t **)realloc(pdf->pages, (pdf->alloc_pages + 16) * sizeof(pdfio_obj_t *));
|
||||
|
||||
if (!temp)
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to allocate memory for pages.");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
pdf->alloc_pages += 16;
|
||||
pdf->pages = temp;
|
||||
}
|
||||
|
||||
pdf->pages[pdf->num_pages ++] = page;
|
||||
|
||||
// Create the contents stream...
|
||||
return (pdfioObjCreateStream(contents, PDFIO_FILTER_FLATE));
|
||||
}
|
||||
|
||||
|
||||
@ -1114,6 +1225,56 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'write_catalog()' - Write the PDF root object/catalog.
|
||||
//
|
||||
|
||||
static bool // O - `true` on success, `false` on failure
|
||||
write_catalog(pdfio_file_t *pdf) // I - PDF file
|
||||
{
|
||||
pdfio_dict_t *dict; // Dictionary for catalog...
|
||||
|
||||
|
||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
||||
return (false);
|
||||
|
||||
pdfioDictSetName(dict, "Type", "Catalog");
|
||||
pdfioDictSetObject(dict, "Pages", pdf->pages_root);
|
||||
// TODO: Add support for all of the root object dictionary keys
|
||||
|
||||
if ((pdf->root = pdfioFileCreateObject(pdf, dict)) == NULL)
|
||||
return (false);
|
||||
else
|
||||
return (pdfioObjClose(pdf->root));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'write_pages()' - Write the PDF pages objects.
|
||||
//
|
||||
|
||||
static bool // O - `true` on success, `false` on failure
|
||||
write_pages(pdfio_file_t *pdf) // I - PDF file
|
||||
{
|
||||
pdfio_array_t *kids; // Pages array
|
||||
size_t i; // Looping var
|
||||
|
||||
|
||||
// Build the "Kids" array pointing to each page...
|
||||
if ((kids = pdfioArrayCreate(pdf)) == NULL)
|
||||
return (false);
|
||||
|
||||
for (i = 0; i < pdf->num_pages; i ++)
|
||||
pdfioArrayAppendObject(kids, pdf->pages[i]);
|
||||
|
||||
pdfioDictSetNumber(pdf->pages_root->value.value.dict, "Count", pdf->num_pages);
|
||||
pdfioDictSetArray(pdf->pages_root->value.value.dict, "Kids", kids);
|
||||
|
||||
// Write the Pages object...
|
||||
return (pdfioObjClose(pdf->pages_root));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'write_trailer()' - Write the PDF catalog object, xref table, and trailer.
|
||||
//
|
||||
@ -1121,8 +1282,80 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
|
||||
static bool // O - `true` on success, `false` on failure
|
||||
write_trailer(pdfio_file_t *pdf) // I - PDF file
|
||||
{
|
||||
// TODO: Write trailer
|
||||
(void)pdf;
|
||||
bool ret = true; // Return value
|
||||
off_t xref_offset; // Offset to xref table
|
||||
size_t i; // Looping var
|
||||
int fd; // File for /dev/urandom
|
||||
unsigned char id_values[2][16]; // ID array values
|
||||
|
||||
return (false);
|
||||
|
||||
// Write the xref table...
|
||||
// TODO: Look at adding support for xref streams...
|
||||
xref_offset = _pdfioFileTell(pdf);
|
||||
|
||||
if (!_pdfioFilePrintf(pdf, "xref\n0 %lu \n0000000000 65535 f \n", (unsigned long)pdf->num_objs))
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to write cross-reference table.");
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdf->num_objs; i ++)
|
||||
{
|
||||
pdfio_obj_t *obj = pdf->objs[i]; // Current object
|
||||
|
||||
if (!_pdfioFilePrintf(pdf, "%010lu %05u n \n", (unsigned long)obj->offset, obj->generation))
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to write cross-reference table.");
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the trailer...
|
||||
if (!_pdfioFilePuts(pdf, "trailer\n"))
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to write trailer.");
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((fd = open("/dev/urandom", O_RDONLY)) >= 0)
|
||||
{
|
||||
// Load ID array with random values from /dev/urandom...
|
||||
memset(id_values, 0, sizeof(id_values));
|
||||
read(fd, id_values[0], sizeof(id_values[0]));
|
||||
read(fd, id_values[1], sizeof(id_values[1]));
|
||||
close(fd);
|
||||
|
||||
pdf->id_array = pdfioArrayCreate(pdf);
|
||||
pdfioArrayAppendBinary(pdf->id_array, id_values[0], sizeof(id_values[0]));
|
||||
pdfioArrayAppendBinary(pdf->id_array, id_values[1], sizeof(id_values[1]));
|
||||
}
|
||||
|
||||
pdf->trailer = pdfioDictCreate(pdf);
|
||||
if (pdf->encrypt)
|
||||
pdfioDictSetObject(pdf->trailer, "Encrypt", pdf->encrypt);
|
||||
if (pdf->id_array)
|
||||
pdfioDictSetArray(pdf->trailer, "ID", pdf->id_array);
|
||||
pdfioDictSetObject(pdf->trailer, "Info", pdf->info);
|
||||
pdfioDictSetObject(pdf->trailer, "Root", pdf->root);
|
||||
pdfioDictSetNumber(pdf->trailer, "Size", pdf->num_objs + 1);
|
||||
|
||||
if (!_pdfioDictWrite(pdf->trailer, NULL))
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to write trailer.");
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!_pdfioFilePrintf(pdf, "\nstartxref\n%lu\n%%EOF\n", (unsigned long)xref_offset))
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to write xref offset.");
|
||||
ret = false;
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
@ -14,6 +14,13 @@
|
||||
#include "pdfio-private.h"
|
||||
|
||||
|
||||
//
|
||||
// Local functions...
|
||||
//
|
||||
|
||||
static bool write_obj_header(pdfio_obj_t *obj);
|
||||
|
||||
|
||||
//
|
||||
// 'pdfioObjClose()' - Close an object, writing any data as needed to the PDF
|
||||
// file.
|
||||
@ -22,10 +29,20 @@
|
||||
bool // O - `true` on success, `false` on failure
|
||||
pdfioObjClose(pdfio_obj_t *obj) // I - Object
|
||||
{
|
||||
// TODO: Implement pdfioObjClose
|
||||
(void)obj;
|
||||
|
||||
// Range check input
|
||||
if (!obj)
|
||||
return (false);
|
||||
|
||||
if (obj->pdf->mode != _PDFIO_MODE_WRITE)
|
||||
return (true); // Nothing to do when reading
|
||||
|
||||
// Write what remains for the object...
|
||||
if (!obj->offset)
|
||||
return (write_obj_header(obj)); // Just write the object value
|
||||
else if (obj->stream)
|
||||
return (pdfioStreamClose(obj->stream));
|
||||
else
|
||||
return (true); // Already closed
|
||||
}
|
||||
|
||||
|
||||
@ -54,11 +71,40 @@ pdfioObjCreateStream(
|
||||
pdfio_obj_t *obj, // I - Object
|
||||
pdfio_filter_t filter) // I - Type of compression to apply
|
||||
{
|
||||
// TODO: Implement pdfioObjCreateStream
|
||||
(void)obj;
|
||||
(void)filter;
|
||||
|
||||
// Range check input
|
||||
if (!obj || obj->pdf->mode != _PDFIO_MODE_WRITE || obj->value.type != PDFIO_VALTYPE_DICT)
|
||||
return (NULL);
|
||||
|
||||
if (obj->offset)
|
||||
{
|
||||
_pdfioFileError(obj->pdf, "Object has already been written.");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (filter != PDFIO_FILTER_NONE && filter != PDFIO_FILTER_FLATE)
|
||||
{
|
||||
_pdfioFileError(obj->pdf, "Unsupported filter value for pdfioObjCreateStream.");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
// Write the header...
|
||||
if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
|
||||
{
|
||||
// Need a Length key for the stream, add a placeholder that we can fill in
|
||||
// later...
|
||||
pdfioDictSetNumber(obj->value.value.dict, "Length", 0.0f);
|
||||
}
|
||||
|
||||
if (!write_obj_header(obj))
|
||||
return (NULL);
|
||||
|
||||
if (!_pdfioFilePuts(obj->pdf, "stream\n"))
|
||||
return (NULL);
|
||||
|
||||
obj->stream_offset = _pdfioFileTell(obj->pdf);
|
||||
|
||||
// Return the new stream...
|
||||
return (_pdfioStreamCreate(obj, filter));
|
||||
}
|
||||
|
||||
|
||||
@ -295,3 +341,22 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
|
||||
// Open the stream...
|
||||
return (_pdfioStreamOpen(obj, decode));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'write_obj_header()' - Write the object header...
|
||||
//
|
||||
|
||||
static bool // O - `true` on success, `false` on failure
|
||||
write_obj_header(pdfio_obj_t *obj) // I - Object
|
||||
{
|
||||
obj->offset = _pdfioFileTell(obj->pdf);
|
||||
|
||||
if (!_pdfioFilePrintf(obj->pdf, "%lu %u obj\n", (unsigned long)obj->number, obj->generation))
|
||||
return (false);
|
||||
|
||||
if (!_pdfioValueWrite(obj->pdf, &obj->value, &obj->length_offset))
|
||||
return (false);
|
||||
|
||||
return (_pdfioFilePuts(obj->pdf, "\n"));
|
||||
}
|
||||
|
@ -77,7 +77,8 @@ typedef enum _pdfio_predictor_e // PNG predictor constants
|
||||
_PDFIO_PREDICTOR_PNG_SUB = 11, // PNG Sub predictor
|
||||
_PDFIO_PREDICTOR_PNG_UP = 12, // PNG Up predictor
|
||||
_PDFIO_PREDICTOR_PNG_AVERAGE = 13, // PNG Average predictor
|
||||
_PDFIO_PREDICTOR_PNG_PAETH = 14 // PNG Paeth predictor
|
||||
_PDFIO_PREDICTOR_PNG_PAETH = 14, // PNG Paeth predictor
|
||||
_PDFIO_PREDICTOR_PNG_AUTO = 15 // PNG "auto" predictor (currently mapped to Paeth)
|
||||
} _pdfio_predictor_t;
|
||||
|
||||
typedef ssize_t (*_pdfio_tconsume_cb_t)(void *data, size_t bytes);
|
||||
@ -154,6 +155,8 @@ struct _pdfio_file_s // PDF file structure
|
||||
{
|
||||
char *filename; // Filename
|
||||
char *version; // Version number
|
||||
pdfio_rect_t media_box, // Default MediaBox value
|
||||
crop_box; // Default CropBox value
|
||||
_pdfio_mode_t mode; // Read/write mode
|
||||
pdfio_error_cb_t error_cb; // Error callback
|
||||
void *error_data; // Data for error callback
|
||||
@ -167,6 +170,7 @@ struct _pdfio_file_s // PDF file structure
|
||||
pdfio_dict_t *trailer; // Trailer dictionary
|
||||
pdfio_obj_t *root; // Root object/dictionary
|
||||
pdfio_obj_t *info; // Information object/dictionary
|
||||
pdfio_obj_t *pages_root; // Root pages object
|
||||
pdfio_obj_t *encrypt; // Encryption object/dictionary
|
||||
pdfio_array_t *id_array; // ID array
|
||||
|
||||
@ -275,6 +279,6 @@ extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vds
|
||||
extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) PDFIO_INTERNAL;
|
||||
extern void _pdfioValueDelete(_pdfio_value_t *v) PDFIO_INTERNAL;
|
||||
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, _pdfio_token_t *ts, _pdfio_value_t *v) PDFIO_INTERNAL;
|
||||
extern bool _pdfioValueWrite(pdfio_file_t *pdf, _pdfio_value_t *v) PDFIO_INTERNAL;
|
||||
extern bool _pdfioValueWrite(pdfio_file_t *pdf, _pdfio_value_t *v, off_t *length) PDFIO_INTERNAL;
|
||||
|
||||
#endif // !PDFIO_PRIVATE_H
|
||||
|
331
pdfio-stream.c
331
pdfio-stream.c
@ -20,6 +20,7 @@
|
||||
|
||||
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);
|
||||
static bool stream_write(pdfio_stream_t *st, const void *buffer, size_t bytes);
|
||||
|
||||
|
||||
//
|
||||
@ -29,6 +30,9 @@ static ssize_t stream_read(pdfio_stream_t *st, char *buffer, size_t bytes);
|
||||
bool // O - `true` on success, `false` on failure
|
||||
pdfioStreamClose(pdfio_stream_t *st) // I - Stream
|
||||
{
|
||||
bool ret = true; // Return value
|
||||
|
||||
|
||||
// Range check input...
|
||||
if (!st)
|
||||
return (false);
|
||||
@ -41,15 +45,85 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Implement close for writing
|
||||
return (false);
|
||||
// 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)
|
||||
{
|
||||
if (status < Z_OK && status != Z_BUF_ERROR)
|
||||
{
|
||||
_pdfioFileError(st->pdf, "Flate compression failed (%d)", status);
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
|
||||
{
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
st->flate.next_out = (Bytef *)st->cbuffer;
|
||||
st->flate.avail_out = (uInt)sizeof(st->cbuffer);
|
||||
}
|
||||
|
||||
if (st->flate.avail_out < (uInt)sizeof(st->cbuffer))
|
||||
{
|
||||
// Write any residuals...
|
||||
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
|
||||
{
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save the length of this stream...
|
||||
st->obj->stream_length = (size_t)(_pdfioFileTell(st->pdf) - st->obj->stream_offset);
|
||||
|
||||
// End of stream marker...
|
||||
if (!_pdfioFilePuts(st->pdf, "\nendstream\n"))
|
||||
{
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
|
||||
// Update the length as needed...
|
||||
if (st->obj->length_offset)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
free(st->pbuffers[0]);
|
||||
free(st->pbuffers[1]);
|
||||
free(st);
|
||||
|
||||
return (true);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
@ -64,13 +138,112 @@ _pdfioStreamCreate(
|
||||
pdfio_obj_t *obj, // I - Object
|
||||
pdfio_filter_t compression) // I - Compression to apply
|
||||
{
|
||||
// TODO: Implement _pdfioStreamCreate
|
||||
(void)obj;
|
||||
(void)compression;
|
||||
pdfio_stream_t *st; // Stream
|
||||
|
||||
|
||||
// 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;
|
||||
st->filter = compression;
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
if ((st->pbuffers[0] = calloc(1, st->pbsize)) == NULL || (st->pbuffers[1] = calloc(1, st->pbsize)) == NULL)
|
||||
{
|
||||
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Predictor buffers.", (unsigned long)st->pbsize);
|
||||
free(st->pbuffers[0]);
|
||||
free(st->pbuffers[1]);
|
||||
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);
|
||||
|
||||
if (deflateInit(&(st->flate), 9) != Z_OK)
|
||||
{
|
||||
_pdfioFileError(st->pdf, "Unable to start Flate filter.");
|
||||
free(st->pbuffers[0]);
|
||||
free(st->pbuffers[1]);
|
||||
free(st);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return (st);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'pdfioStreamConsume()' - Consume bytes from the stream.
|
||||
@ -268,13 +441,8 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
|
||||
else
|
||||
st->predictor = _PDFIO_PREDICTOR_NONE;
|
||||
|
||||
st->flate.zalloc = (alloc_func)0;
|
||||
st->flate.zfree = (free_func)0;
|
||||
st->flate.opaque = (voidpf)0;
|
||||
st->flate.next_in = (Bytef *)st->cbuffer;
|
||||
st->flate.next_out = NULL;
|
||||
st->flate.avail_in = (uInt)_pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer));
|
||||
st->flate.avail_out = 0;
|
||||
|
||||
if (inflateInit(&(st->flate)) != Z_OK)
|
||||
{
|
||||
@ -474,14 +642,112 @@ pdfioStreamWrite(
|
||||
const void *buffer, // I - Data to write
|
||||
size_t bytes) // I - Number of bytes to write
|
||||
{
|
||||
size_t pbpixel = st->pbpixel,
|
||||
// Size of pixel in bytes
|
||||
pbline = st->pbsize - 1,
|
||||
// Bytes per line
|
||||
remaining, // Remaining bytes on this line
|
||||
firstcol = pbline - pbpixel;
|
||||
// First column bytes remaining
|
||||
const unsigned char *bufptr = (const unsigned char *)buffer;
|
||||
// Pointer into buffer
|
||||
unsigned char *thisptr, // Current raw buffer
|
||||
*prevptr; // Previous raw buffer
|
||||
|
||||
|
||||
// Range check input...
|
||||
if (!st || st->pdf->mode != _PDFIO_MODE_WRITE || !buffer || !bytes)
|
||||
return (false);
|
||||
|
||||
// TODO: Implement pdfioStreamWrite
|
||||
// Write it...
|
||||
if (st->filter == PDFIO_FILTER_NONE)
|
||||
{
|
||||
// No filtering so just write it...
|
||||
return (_pdfioFileWrite(st->pdf, buffer, bytes));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
while (bytes > 0)
|
||||
{
|
||||
// Store the PNG predictor in the first byte of the buffer...
|
||||
if (st->predictor == _PDFIO_PREDICTOR_PNG_AUTO)
|
||||
st->pbuffers[st->pbcurrent][0] = 4;
|
||||
else
|
||||
st->pbuffers[st->pbcurrent][0] = (unsigned char)(st->predictor - 10);
|
||||
|
||||
// Then process the current line using the specified PNG predictor...
|
||||
thisptr = st->pbuffers[st->pbcurrent] + 1;
|
||||
prevptr = st->pbuffers[!st->pbcurrent] + 1;
|
||||
|
||||
switch (st->predictor)
|
||||
{
|
||||
default :
|
||||
// Anti-compiler-warning code (NONE is handled above, TIFF is not supported for writing)
|
||||
return (false);
|
||||
|
||||
case _PDFIO_PREDICTOR_PNG_SUB :
|
||||
// Encode the difference from the previous column
|
||||
for (remaining = pbline; remaining > 0; remaining --, bufptr ++, thisptr ++)
|
||||
{
|
||||
if (remaining < firstcol)
|
||||
*thisptr = *bufptr - thisptr[-pbpixel];
|
||||
else
|
||||
*thisptr = *bufptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case _PDFIO_PREDICTOR_PNG_UP :
|
||||
// Encode the difference from the previous line
|
||||
for (remaining = pbline; remaining > 0; remaining --, bufptr ++, thisptr ++, prevptr ++)
|
||||
{
|
||||
*thisptr = *bufptr - *prevptr;
|
||||
}
|
||||
break;
|
||||
|
||||
case _PDFIO_PREDICTOR_PNG_AVERAGE :
|
||||
// Encode the difference with the average of the previous column and line
|
||||
for (remaining = pbline; remaining > 0; remaining --, bufptr ++, thisptr ++, prevptr ++)
|
||||
{
|
||||
if (remaining < firstcol)
|
||||
*thisptr = *bufptr - (thisptr[-pbpixel] + *prevptr) / 2;
|
||||
else
|
||||
*thisptr = *bufptr - *prevptr / 2;
|
||||
}
|
||||
break;
|
||||
|
||||
case _PDFIO_PREDICTOR_PNG_PAETH :
|
||||
case _PDFIO_PREDICTOR_PNG_AUTO :
|
||||
// Encode the difference with a linear transform function
|
||||
for (remaining = pbline; remaining > 0; remaining --, bufptr ++, thisptr ++, prevptr ++)
|
||||
{
|
||||
if (remaining < firstcol)
|
||||
*thisptr = *bufptr - stream_paeth(thisptr[-pbpixel], *prevptr, prevptr[-pbpixel]);
|
||||
else
|
||||
*thisptr = *bufptr - stream_paeth(0, *prevptr, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Write the encoded line...
|
||||
if (!stream_write(st, st->pbuffers[st->pbcurrent], st->pbsize))
|
||||
return (false);
|
||||
|
||||
st->pbcurrent = !st->pbcurrent;
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'stream_paeth()' - PaethPredictor function for PNG decompression filter.
|
||||
@ -674,3 +940,44 @@ stream_read(pdfio_stream_t *st, // I - Stream
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '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...
|
||||
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
|
||||
return (false);
|
||||
|
||||
st->flate.next_out = (Bytef *)st->cbuffer;
|
||||
st->flate.avail_out = sizeof(st->cbuffer);
|
||||
}
|
||||
|
||||
// Deflate what we can this time...
|
||||
status = deflate(&st->flate, Z_NO_FLUSH);
|
||||
|
||||
if (status < Z_OK && status != Z_BUF_ERROR)
|
||||
{
|
||||
_pdfioFileError(st->pdf, "Flate compression failed (%d)", status);
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
@ -349,7 +349,8 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
||||
|
||||
bool // O - `true` on success, `false` on failure
|
||||
_pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
||||
_pdfio_value_t *v) // I - Value
|
||||
_pdfio_value_t *v, // I - Value
|
||||
off_t *length)// O - Offset to /Length value, if any
|
||||
{
|
||||
switch (v->type)
|
||||
{
|
||||
@ -394,7 +395,7 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
||||
}
|
||||
|
||||
case PDFIO_VALTYPE_DICT :
|
||||
return (_pdfioDictWrite(v->value.dict, NULL));
|
||||
return (_pdfioDictWrite(v->value.dict, length));
|
||||
|
||||
case PDFIO_VALTYPE_INDIRECT :
|
||||
return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation));
|
||||
|
5
pdfio.h
5
pdfio.h
@ -147,9 +147,10 @@ extern bool pdfioDictSetString(pdfio_dict_t *dict, const char *key, const char
|
||||
extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char *format, ...) PDFIO_PUBLIC PDFIO_FORMAT(3,4);
|
||||
|
||||
extern bool pdfioFileClose(pdfio_file_t *pdf) PDFIO_PUBLIC;
|
||||
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_error_cb_t error_cb, void *error_data) PDFIO_PUBLIC;
|
||||
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) PDFIO_PUBLIC;
|
||||
extern pdfio_obj_t *pdfioFileCreateObject(pdfio_file_t *pdf, pdfio_dict_t *dict) PDFIO_PUBLIC;
|
||||
extern pdfio_obj_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) PDFIO_PUBLIC;
|
||||
// TODO: Add number, array, string, etc. versions of pdfioFileCreateObject?
|
||||
extern pdfio_stream_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) PDFIO_PUBLIC;
|
||||
extern pdfio_obj_t *pdfioFileFindObject(pdfio_file_t *pdf, size_t number) PDFIO_PUBLIC;
|
||||
extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) PDFIO_PUBLIC;
|
||||
extern const char *pdfioFileGetName(pdfio_file_t *pdf) PDFIO_PUBLIC;
|
||||
|
Loading…
Reference in New Issue
Block a user