Add pdfioFileCreateOutput API (Issue #21)

This commit is contained in:
Michael R Sweet 2021-09-27 07:41:50 -04:00
parent 9f1cadf78b
commit d6746c08a4
No known key found for this signature in database
GPG Key ID: BE67C75EC81F3244
10 changed files with 503 additions and 257 deletions

View File

@ -25,7 +25,7 @@ DSONAME =
LDFLAGS = LDFLAGS =
LIBS = -lm -lz LIBS = -lm -lz
RANLIB = ranlib RANLIB = ranlib
VERSION = 1.0b1 VERSION = 1.0.0
prefix = /usr/local prefix = /usr/local

View File

@ -377,6 +377,11 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
// No, reset the read buffer // No, reset the read buffer
pdf->bufptr = pdf->bufend = NULL; pdf->bufptr = pdf->bufend = NULL;
} }
else if (pdf->output_cb)
{
_pdfioFileError(pdf, "Unable to seek within output stream.");
return (-1);
}
else else
{ {
// Writing, make sure we write any buffered data... // Writing, make sure we write any buffered data...
@ -530,6 +535,17 @@ write_buffer(pdfio_file_t *pdf, // I - PDF file
ssize_t wbytes; // Bytes written... ssize_t wbytes; // Bytes written...
if (pdf->output_cb)
{
// Write to a stream...
if ((pdf->output_cb)(pdf->output_ctx, buffer, bytes) < 0)
{
_pdfioFileError(pdf, "Unable to write to output callback.");
return (false);
}
}
else
{
// Write to the file... // Write to the file...
while (bytes > 0) while (bytes > 0)
{ {
@ -550,6 +566,7 @@ write_buffer(pdfio_file_t *pdf, // I - PDF file
bufptr += wbytes; bufptr += wbytes;
bytes -= (size_t)wbytes; bytes -= (size_t)wbytes;
} }
}
return (true); return (true);
} }

View File

@ -130,7 +130,7 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
ret = _pdfioFileFlush(pdf); ret = _pdfioFileFlush(pdf);
} }
if (close(pdf->fd) < 0) if (pdf->fd >= 0 && close(pdf->fd) < 0)
ret = false; ret = false;
// Free all data... // Free all data...
@ -399,6 +399,127 @@ _pdfioFileCreateObj(
} }
//
// 'pdfioFileCreateOutput()' - Create a PDF file through an output callback.
//
pdfio_file_t * // O - PDF file or `NULL` on error
pdfioFileCreateOutput(
pdfio_output_cb_t output_cb, // I - Output callback
void *output_ctx, // I - Output context
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
pdfio_dict_t *info_dict; // Dictionary for information object
// Range check input...
if (!output_cb)
return (NULL);
if (!version)
version = "2.0";
if (!error_cb)
{
error_cb = _pdfioFileDefaultError;
error_data = NULL;
}
// Allocate a PDF file structure...
if ((pdf = (pdfio_file_t *)calloc(1, sizeof(pdfio_file_t))) == NULL)
{
pdfio_file_t temp; // Dummy file
char message[8192]; // Message string
temp.filename = (char *)"output.pdf";
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno));
(error_cb)(&temp, message, error_data);
return (NULL);
}
pdf->filename = strdup("output.pdf");
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
if (media_box)
{
pdf->media_box = *media_box;
}
else
{
// Default to "universal" size (intersection of A4 and US Letter)
pdf->media_box.x2 = 210.0 * 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)
pdf->crop_box.x2 = 210.0 * 72.0f / 25.4f;
pdf->crop_box.y2 = 11.0f * 72.0f;
}
// Save output callback...
pdf->fd = -1;
pdf->output_cb = output_cb;
pdf->output_ctx = output_ctx;
// Write a standard PDF header...
if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version))
{
pdfioFileClose(pdf);
return (NULL);
}
// Create the pages object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
pdfioDictSetName(dict, "Type", "Pages");
if ((pdf->pages_root = pdfioFileCreateObj(pdf, dict)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
// Create the info object...
if ((info_dict = pdfioDictCreate(pdf)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
pdfioDictSetDate(info_dict, "CreationDate", time(NULL));
pdfioDictSetString(info_dict, "Producer", "pdfio/" PDFIO_VERSION);
if ((pdf->info = pdfioFileCreateObj(pdf, info_dict)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
return (pdf);
}
// //
// 'pdfioFileCreatePage()' - Create a page in a PDF file. // 'pdfioFileCreatePage()' - Create a page in a PDF file.
// //

View File

@ -143,6 +143,9 @@ pdfioObjCreateStream(
pdfio_obj_t *obj, // I - Object pdfio_obj_t *obj, // I - Object
pdfio_filter_t filter) // I - Type of compression to apply pdfio_filter_t filter) // I - Type of compression to apply
{ {
pdfio_obj_t *length_obj = NULL; // Length object, if any
// Range check input // Range check input
if (!obj || obj->pdf->mode != _PDFIO_MODE_WRITE || obj->value.type != PDFIO_VALTYPE_DICT) if (!obj || obj->pdf->mode != _PDFIO_MODE_WRITE || obj->value.type != PDFIO_VALTYPE_DICT)
return (NULL); return (NULL);
@ -161,11 +164,25 @@ pdfioObjCreateStream(
// Write the header... // Write the header...
if (!_pdfioDictGetValue(obj->value.value.dict, "Length")) if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
{
if (obj->pdf->output_cb)
{
// Streaming via an output callback, so add a placeholder length object
_pdfio_value_t length_value; // Length value
length_value.type = PDFIO_VALTYPE_NUMBER;
length_value.value.number = 0.0f;
length_obj = _pdfioFileCreateObj(obj->pdf, obj->pdf, &length_value);
pdfioDictSetObj(obj->value.value.dict, "Length", length_obj);
}
else
{ {
// Need a Length key for the stream, add a placeholder that we can fill in // Need a Length key for the stream, add a placeholder that we can fill in
// later... // later...
pdfioDictSetNumber(obj->value.value.dict, "Length", 0.0); pdfioDictSetNumber(obj->value.value.dict, "Length", 0.0);
} }
}
if (!write_obj_header(obj)) if (!write_obj_header(obj))
return (NULL); return (NULL);
@ -176,7 +193,7 @@ pdfioObjCreateStream(
obj->stream_offset = _pdfioFileTell(obj->pdf); obj->stream_offset = _pdfioFileTell(obj->pdf);
// Return the new stream... // Return the new stream...
return (_pdfioStreamCreate(obj, filter)); return (_pdfioStreamCreate(obj, length_obj, filter));
} }

View File

@ -210,6 +210,8 @@ struct _pdfio_file_s // PDF file structure
pdfio_rect_t media_box, // Default MediaBox value pdfio_rect_t media_box, // Default MediaBox value
crop_box; // Default CropBox value crop_box; // Default CropBox value
_pdfio_mode_t mode; // Read/write mode _pdfio_mode_t mode; // Read/write mode
pdfio_output_cb_t output_cb; // Output callback
void *output_ctx; // Context for output callback
pdfio_error_cb_t error_cb; // Error callback pdfio_error_cb_t error_cb; // Error callback
void *error_data; // Data for error callback void *error_data; // Data for error callback
@ -266,6 +268,7 @@ struct _pdfio_stream_s // Stream
{ {
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
pdfio_obj_t *obj; // Object pdfio_obj_t *obj; // Object
pdfio_obj_t *length_obj; // Length object, if any
pdfio_filter_t filter; // Compression/decompression filter pdfio_filter_t filter; // Compression/decompression filter
size_t remaining; // Remaining bytes in stream size_t remaining; // Remaining bytes in stream
char buffer[8192], // Read/write buffer char buffer[8192], // Read/write buffer
@ -319,7 +322,7 @@ extern bool _pdfioFileWrite(pdfio_file_t *pdf, const void *buffer, size_t bytes
extern void _pdfioObjDelete(pdfio_obj_t *obj) _PDFIO_INTERNAL; extern void _pdfioObjDelete(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern bool _pdfioObjLoad(pdfio_obj_t *obj) _PDFIO_INTERNAL; extern bool _pdfioObjLoad(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_filter_t compression) _PDFIO_INTERNAL; extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, pdfio_filter_t compression) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL; extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL; extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;

View File

@ -93,7 +93,12 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
} }
// Update the length as needed... // Update the length as needed...
if (st->obj->length_offset) if (st->length_obj)
{
st->length_obj->value.value.number = st->obj->stream_length;
pdfioObjClose(st->length_obj);
}
else if (st->obj->length_offset)
{ {
// Seek back to the "/Length 9999999999" we wrote... // Seek back to the "/Length 9999999999" we wrote...
if (_pdfioFileSeek(st->pdf, st->obj->length_offset, SEEK_SET) < 0) if (_pdfioFileSeek(st->pdf, st->obj->length_offset, SEEK_SET) < 0)
@ -137,6 +142,7 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
pdfio_stream_t * // O - Stream or `NULL` on error pdfio_stream_t * // O - Stream or `NULL` on error
_pdfioStreamCreate( _pdfioStreamCreate(
pdfio_obj_t *obj, // I - Object pdfio_obj_t *obj, // I - Object
pdfio_obj_t *length_obj, // I - Length object, if any
pdfio_filter_t compression) // I - Compression to apply pdfio_filter_t compression) // I - Compression to apply
{ {
pdfio_stream_t *st; // Stream pdfio_stream_t *st; // Stream
@ -151,6 +157,7 @@ _pdfioStreamCreate(
st->pdf = obj->pdf; st->pdf = obj->pdf;
st->obj = obj; st->obj = obj;
st->length_obj = length_obj;
st->filter = compression; st->filter = compression;
if (compression == PDFIO_FILTER_FLATE) if (compression == PDFIO_FILTER_FLATE)

View File

@ -74,6 +74,8 @@ typedef enum pdfio_filter_e // Compression/decompression filters for streams
PDFIO_FILTER_RUNLENGTH, // RunLengthDecode filter (reading only) PDFIO_FILTER_RUNLENGTH, // RunLengthDecode filter (reading only)
} pdfio_filter_t; } pdfio_filter_t;
typedef struct _pdfio_obj_s pdfio_obj_t;// Numbered object in PDF file typedef struct _pdfio_obj_s pdfio_obj_t;// Numbered object in PDF file
typedef ssize_t (*pdfio_output_cb_t)(void *ctx, const void *data, size_t datalen);
// Output callback for pdfioFileCreateOutput
typedef struct pdfio_rect_s // PDF rectangle typedef struct pdfio_rect_s // PDF rectangle
{ {
double x1; // Lower-left X coordinate double x1; // Lower-left X coordinate
@ -156,6 +158,7 @@ extern bool pdfioFileClose(pdfio_file_t *pdf) _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_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 *pdfioFileCreateArrayObj(pdfio_file_t *pdf, pdfio_array_t *array) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateArrayObj(pdfio_file_t *pdf, pdfio_array_t *array) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateOutput(pdfio_output_cb_t output_cb, void *output_ctx, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
// TODO: Add number, array, string, etc. versions of pdfioFileCreateObject? // 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_stream_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;

View File

@ -87,7 +87,7 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>PDFIO_VERSION="1.0.0";WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
@ -101,7 +101,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>PDFIO_VERSION="1.0.0";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
@ -115,7 +115,7 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>PDFIO_VERSION="1.0.0";_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
@ -130,7 +130,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>PDFIO_VERSION="1.0.0";NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>

View File

@ -241,7 +241,7 @@
273440A8263D6FE200FBFD63 /* Project object */ = { 273440A8263D6FE200FBFD63 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 1250; LastUpgradeCheck = 1300;
TargetAttributes = { TargetAttributes = {
273440AF263D6FE200FBFD63 = { 273440AF263D6FE200FBFD63 = {
CreatedOnToolsVersion = 12.5; CreatedOnToolsVersion = 12.5;
@ -350,9 +350,9 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0; CURRENT_PROJECT_VERSION = 1.0.0;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
@ -428,9 +428,9 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0; CURRENT_PROJECT_VERSION = 1.0.0;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES; ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;

View File

@ -33,6 +33,8 @@ static int do_test_file(const char *filename, int objnum, bool verbose);
static int do_unit_tests(void); static int do_unit_tests(void);
static int draw_image(pdfio_stream_t *st, const char *name, double x, double y, double w, double h, const char *label); static int draw_image(pdfio_stream_t *st, const char *name, double x, double y, double w, double h, const char *label);
static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error); static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error);
static ssize_t output_cb(int *fd, const void *buffer, size_t bytes);
static int read_unit_file(const char *filename, size_t num_pages, size_t first_image, bool is_output);
static ssize_t token_consume_cb(const char **s, size_t bytes); static ssize_t token_consume_cb(const char **s, size_t bytes);
static ssize_t token_peek_cb(const char **s, char *buffer, size_t bytes); static ssize_t token_peek_cb(const char **s, char *buffer, size_t bytes);
static int verify_image(pdfio_file_t *pdf, size_t number); static int verify_image(pdfio_file_t *pdf, size_t number);
@ -46,6 +48,7 @@ static int write_images_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_jpeg_test(pdfio_file_t *pdf, const char *title, int number, pdfio_obj_t *font, pdfio_obj_t *image); static int write_jpeg_test(pdfio_file_t *pdf, const char *title, int number, pdfio_obj_t *font, pdfio_obj_t *image);
static int write_png_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font); static int write_png_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_text_test(pdfio_file_t *pdf, int first_page, pdfio_obj_t *font, const char *filename); static int write_text_test(pdfio_file_t *pdf, int first_page, pdfio_obj_t *font, const char *filename);
static int write_unit_file(pdfio_file_t *inpdf, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image);
// //
@ -249,17 +252,13 @@ do_test_file(const char *filename, // I - PDF filename
static int // O - Exit status static int // O - Exit status
do_unit_tests(void) do_unit_tests(void)
{ {
int i; // Looping var pdfio_file_t *inpdf, // Input PDF file
pdfio_file_t *pdf, // Test PDF file
*outpdf; // Output PDF file *outpdf; // Output PDF file
int outfd; // Output file descriptor
bool error = false; // Error callback data bool error = false; // Error callback data
_pdfio_token_t tb; // Token buffer _pdfio_token_t tb; // Token buffer
const char *s; // String buffer const char *s; // String buffer
_pdfio_value_t value; // Value _pdfio_value_t value; // Value
pdfio_obj_t *color_jpg, // color.jpg image
*gray_jpg, // gray.jpg image
*helvetica, // Helvetica font
*page; // Page from test PDF file
size_t first_image, // First image object size_t first_image, // First image object
num_pages; // Number of pages written num_pages; // Number of pages written
static const char *complex_dict = // Complex dictionary value static const char *complex_dict = // Complex dictionary value
@ -716,7 +715,7 @@ do_unit_tests(void)
// First open the test PDF file... // First open the test PDF file...
fputs("pdfioFileOpen(\"testfiles/testpdfio.pdf\"): ", stdout); fputs("pdfioFileOpen(\"testfiles/testpdfio.pdf\"): ", stdout);
if ((pdf = pdfioFileOpen("testfiles/testpdfio.pdf", (pdfio_error_cb_t)error_cb, &error)) != NULL) if ((inpdf = pdfioFileOpen("testfiles/testpdfio.pdf", (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS"); puts("PASS");
else else
return (1); return (1);
@ -726,8 +725,8 @@ do_unit_tests(void)
// Test the value parsers for edge cases... // Test the value parsers for edge cases...
fputs("_pdfioValueRead(complex_dict): ", stdout); fputs("_pdfioValueRead(complex_dict): ", stdout);
s = complex_dict; s = complex_dict;
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s); _pdfioTokenInit(&tb, inpdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s);
if (_pdfioValueRead(pdf, &tb, &value)) if (_pdfioValueRead(inpdf, &tb, &value))
{ {
// TODO: Check value... // TODO: Check value...
fputs("PASS: ", stdout); fputs("PASS: ", stdout);
@ -740,8 +739,8 @@ do_unit_tests(void)
// Test the value parsers for edge cases... // Test the value parsers for edge cases...
fputs("_pdfioValueRead(cid_dict): ", stdout); fputs("_pdfioValueRead(cid_dict): ", stdout);
s = cid_dict; s = cid_dict;
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s); _pdfioTokenInit(&tb, inpdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s);
if (_pdfioValueRead(pdf, &tb, &value)) if (_pdfioValueRead(inpdf, &tb, &value))
{ {
// TODO: Check value... // TODO: Check value...
fputs("PASS: ", stdout); fputs("PASS: ", stdout);
@ -758,230 +757,31 @@ do_unit_tests(void)
else else
return (1); return (1);
// Set info values... if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
fputs("pdfioFileGet/SetAuthor: ", stdout);
pdfioFileSetAuthor(outpdf, "Michael R Sweet");
if ((s = pdfioFileGetAuthor(outpdf)) != NULL && !strcmp(s, "Michael R Sweet"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Michael R Sweet')\n", s);
return (1); return (1);
}
else if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
return (1);
// Create a new PDF file...
if ((outfd = open("testpdfio-out2.pdf", O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0)
{ {
puts("FAIL (got NULL, expected 'Michael R Sweet')"); perror("Unable to open \"testpdfio-out2.pdf\"");
return (1); return (1);
} }
fputs("pdfioFileGet/SetCreator: ", stdout); fputs("pdfioFileCreateOutput(...): ", stdout);
pdfioFileSetCreator(outpdf, "testpdfio"); if ((outpdf = pdfioFileCreateOutput((pdfio_output_cb_t)output_cb, &outfd, NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
if ((s = pdfioFileGetCreator(outpdf)) != NULL && !strcmp(s, "testpdfio"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'testpdfio')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'testpdfio')");
return (1);
}
fputs("pdfioFileGet/SetKeywords: ", stdout);
pdfioFileSetKeywords(outpdf, "one fish,two fish,red fish,blue fish");
if ((s = pdfioFileGetKeywords(outpdf)) != NULL && !strcmp(s, "one fish,two fish,red fish,blue fish"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'one fish,two fish,red fish,blue fish')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'one fish,two fish,red fish,blue fish')");
return (1);
}
fputs("pdfioFileGet/SetSubject: ", stdout);
pdfioFileSetSubject(outpdf, "Unit test document");
if ((s = pdfioFileGetSubject(outpdf)) != NULL && !strcmp(s, "Unit test document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Unit test document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Unit test document')");
return (1);
}
fputs("pdfioFileGet/SetTitle: ", stdout);
pdfioFileSetTitle(outpdf, "Test Document");
if ((s = pdfioFileGetTitle(outpdf)) != NULL && !strcmp(s, "Test Document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Test Document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Test Document')");
return (1);
}
// Create some image objects...
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/color.jpg\"): ", stdout);
if ((color_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/color.jpg", true)) != NULL)
puts("PASS"); puts("PASS");
else else
return (1); return (1);
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/gray.jpg\"): ", stdout); if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
if ((gray_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/gray.jpg", true)) != NULL)
puts("PASS");
else
return (1); return (1);
// Create fonts... close(outfd);
fputs("pdfioFileCreateFontObjFromBase(\"Helvetica\"): ", stdout);
if ((helvetica = pdfioFileCreateFontObjFromBase(outpdf, "Helvetica")) != NULL)
puts("PASS");
else
return (1);
// Copy the first page from the test PDF file... if (read_unit_file("testpdfio-out2.pdf", num_pages, first_image, true))
fputs("pdfioFileGetPage(0): ", stdout);
if ((page = pdfioFileGetPage(pdf, 0)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioPageCopy(first page): ", stdout);
if (pdfioPageCopy(outpdf, page))
puts("PASS");
else
return (1);
// Write a page with a color image...
if (write_jpeg_test(outpdf, "Color JPEG Test", 2, helvetica, color_jpg))
return (1);
// Copy the third page from the test PDF file...
fputs("pdfioFileGetPage(2): ", stdout);
if ((page = pdfioFileGetPage(pdf, 2)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioPageCopy(third page): ", stdout);
if (pdfioPageCopy(outpdf, page))
puts("PASS");
else
return (1);
// Write a page with a grayscale image...
if (write_jpeg_test(outpdf, "Grayscale JPEG Test", 4, helvetica, gray_jpg))
return (1);
// Write a page with PNG images...
if (write_png_test(outpdf, 5, helvetica))
return (1);
// Write a page that tests multiple color spaces...
if (write_color_test(outpdf, 6, helvetica))
return (1);
// Write a page with test images...
first_image = pdfioFileGetNumObjs(outpdf) + 1;
if (write_images_test(outpdf, 7, helvetica))
return (1);
// Write a page width alpha (soft masks)...
if (write_alpha_test(outpdf, 8, helvetica))
return (1);
// Test TrueType fonts...
if (write_font_test(outpdf, 9, helvetica, false))
return (1);
if (write_font_test(outpdf, 10, helvetica, true))
return (1);
// Print this text file...
if (write_text_test(outpdf, 11, helvetica, "README.md"))
return (1);
// Close the test PDF file...
fputs("pdfioFileClose(\"testfiles/testpdfio.pdf\"): ", stdout);
if (pdfioFileClose(pdf))
puts("PASS");
else
return (1);
fputs("pdfioFileGetNumPages: ", stdout);
if ((num_pages = pdfioFileGetNumPages(outpdf)) > 0)
{
printf("PASS (%lu)\n", (unsigned long)num_pages);
}
else
{
puts("FAIL");
return (1);
}
// Close the new PDF file...
fputs("pdfioFileClose(\"testpdfio-out.pdf\"): ", stdout);
if (pdfioFileClose(outpdf))
puts("PASS");
else
return (1);
// Open the new PDF file to read it...
fputs("pdfioFileOpen(\"testpdfio-out.pdf\", ...): ", stdout);
if ((pdf = pdfioFileOpen("testpdfio-out.pdf", (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
// Verify the number of pages is the same...
fputs("pdfioFileGetNumPages: ", stdout);
if (num_pages == pdfioFileGetNumPages(pdf))
{
puts("PASS");
}
else
{
printf("FAIL (%lu != %lu)\n", (unsigned long)num_pages, (unsigned long)pdfioFileGetNumPages(pdf));
return (1);
}
// Verify the images
for (i = 0; i < 7; i ++)
{
if (verify_image(pdf, first_image + (size_t)i))
return (1);
}
// Close the new PDF file...
fputs("pdfioFileClose(\"testpdfio-out.pdf\"): ", stdout);
if (pdfioFileClose(pdf))
puts("PASS");
else
return (1); return (1);
return (0); return (0);
@ -1068,6 +868,76 @@ error_cb(pdfio_file_t *pdf, // I - PDF file
} }
//
// 'output_cb()' - Write output to a file.
//
static ssize_t // O - Number of bytes written
output_cb(int *fd, // I - File descriptor
const void *buffer, // I - Output buffer
size_t bytes) // I - Number of bytes to write
{
return (write(*fd, buffer, bytes));
}
//
// 'read_unit_file()' - Read back a unit test file and confirm its contents.
//
static int // O - Exit status
read_unit_file(const char *filename, // I - File to read
size_t num_pages, // I - Expected number of pages
size_t first_image, // I - First image object
bool is_output) // I - File written with output callback?
{
pdfio_file_t *pdf; // PDF file
size_t i; // Looping var
bool error = false; // Error callback data
// Open the new PDF file to read it...
printf("pdfioFileOpen(\"%s\", ...): ", filename);
if ((pdf = pdfioFileOpen(filename, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
// Verify the number of pages is the same...
fputs("pdfioFileGetNumPages: ", stdout);
if (num_pages == pdfioFileGetNumPages(pdf))
{
puts("PASS");
}
else
{
printf("FAIL (%lu != %lu)\n", (unsigned long)num_pages, (unsigned long)pdfioFileGetNumPages(pdf));
return (1);
}
// Verify the images
for (i = 0; i < 7; i ++)
{
if (is_output)
{
if (verify_image(pdf, first_image + (size_t)i * 2))
return (1);
}
else if (verify_image(pdf, first_image + (size_t)i))
return (1);
}
// Close the new PDF file...
fputs("pdfioFileClose(\"testpdfio-out.pdf\"): ", stdout);
if (pdfioFileClose(pdf))
puts("PASS");
else
return (1);
return (0);
}
// //
// 'token_consume_cb()' - Consume bytes from a test string. // 'token_consume_cb()' - Consume bytes from a test string.
// //
@ -2843,3 +2713,211 @@ write_text_test(pdfio_file_t *pdf, // I - PDF file
pdfioStreamClose(st); pdfioStreamClose(st);
return (1); return (1);
} }
//
// 'write_unit_file()' - Write a unit test file.
//
static int // O - Exit status
write_unit_file(
pdfio_file_t *inpdf, // I - Input PDF file
pdfio_file_t *outpdf, // I - Output PDF file
size_t *num_pages, // O - Number of pages
size_t *first_image) // O - First image object
{
const char *s; // String buffer
pdfio_obj_t *color_jpg, // color.jpg image
*gray_jpg, // gray.jpg image
*helvetica, // Helvetica font
*page; // Page from test PDF file
// Set info values...
fputs("pdfioFileGet/SetAuthor: ", stdout);
pdfioFileSetAuthor(outpdf, "Michael R Sweet");
if ((s = pdfioFileGetAuthor(outpdf)) != NULL && !strcmp(s, "Michael R Sweet"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Michael R Sweet')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Michael R Sweet')");
return (1);
}
fputs("pdfioFileGet/SetCreator: ", stdout);
pdfioFileSetCreator(outpdf, "testpdfio");
if ((s = pdfioFileGetCreator(outpdf)) != NULL && !strcmp(s, "testpdfio"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'testpdfio')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'testpdfio')");
return (1);
}
fputs("pdfioFileGet/SetKeywords: ", stdout);
pdfioFileSetKeywords(outpdf, "one fish,two fish,red fish,blue fish");
if ((s = pdfioFileGetKeywords(outpdf)) != NULL && !strcmp(s, "one fish,two fish,red fish,blue fish"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'one fish,two fish,red fish,blue fish')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'one fish,two fish,red fish,blue fish')");
return (1);
}
fputs("pdfioFileGet/SetSubject: ", stdout);
pdfioFileSetSubject(outpdf, "Unit test document");
if ((s = pdfioFileGetSubject(outpdf)) != NULL && !strcmp(s, "Unit test document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Unit test document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Unit test document')");
return (1);
}
fputs("pdfioFileGet/SetTitle: ", stdout);
pdfioFileSetTitle(outpdf, "Test Document");
if ((s = pdfioFileGetTitle(outpdf)) != NULL && !strcmp(s, "Test Document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Test Document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Test Document')");
return (1);
}
// Create some image objects...
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/color.jpg\"): ", stdout);
if ((color_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/color.jpg", true)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/gray.jpg\"): ", stdout);
if ((gray_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/gray.jpg", true)) != NULL)
puts("PASS");
else
return (1);
// Create fonts...
fputs("pdfioFileCreateFontObjFromBase(\"Helvetica\"): ", stdout);
if ((helvetica = pdfioFileCreateFontObjFromBase(outpdf, "Helvetica")) != NULL)
puts("PASS");
else
return (1);
// Copy the first page from the test PDF file...
fputs("pdfioFileGetPage(0): ", stdout);
if ((page = pdfioFileGetPage(inpdf, 0)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioPageCopy(first page): ", stdout);
if (pdfioPageCopy(outpdf, page))
puts("PASS");
else
return (1);
// Write a page with a color image...
if (write_jpeg_test(outpdf, "Color JPEG Test", 2, helvetica, color_jpg))
return (1);
// Copy the third page from the test PDF file...
fputs("pdfioFileGetPage(2): ", stdout);
if ((page = pdfioFileGetPage(inpdf, 2)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioPageCopy(third page): ", stdout);
if (pdfioPageCopy(outpdf, page))
puts("PASS");
else
return (1);
// Write a page with a grayscale image...
if (write_jpeg_test(outpdf, "Grayscale JPEG Test", 4, helvetica, gray_jpg))
return (1);
// Write a page with PNG images...
if (write_png_test(outpdf, 5, helvetica))
return (1);
// Write a page that tests multiple color spaces...
if (write_color_test(outpdf, 6, helvetica))
return (1);
// Write a page with test images...
*first_image = pdfioFileGetNumObjs(outpdf) + 1;
if (write_images_test(outpdf, 7, helvetica))
return (1);
// Write a page width alpha (soft masks)...
if (write_alpha_test(outpdf, 8, helvetica))
return (1);
// Test TrueType fonts...
if (write_font_test(outpdf, 9, helvetica, false))
return (1);
if (write_font_test(outpdf, 10, helvetica, true))
return (1);
// Print this text file...
if (write_text_test(outpdf, 11, helvetica, "README.md"))
return (1);
fputs("pdfioFileGetNumPages: ", stdout);
if ((*num_pages = pdfioFileGetNumPages(outpdf)) > 0)
{
printf("PASS (%lu)\n", (unsigned long)*num_pages);
}
else
{
puts("FAIL");
return (1);
}
// Close the new PDF file...
fputs("pdfioFileClose(...): ", stdout);
if (pdfioFileClose(outpdf))
puts("PASS");
else
return (1);
return (0);
}