Add pdfioFileCreateTemporary function (Issue #29)

This commit is contained in:
Michael R Sweet 2022-05-15 22:52:53 -04:00
parent f8b471acfd
commit 316b0ad559
No known key found for this signature in database
GPG Key ID: 999559A027815955
8 changed files with 287 additions and 54 deletions

View File

@ -2,6 +2,12 @@ Changes in PDFio
================
v1.1.0 (Month DD, YYYY)
-----------------------
- Added `pdfioFileCreateTemporary` function (Issue #29)
v1.0.1 (March 2, 2022)
----------------------

View File

@ -26,7 +26,7 @@ DSONAME =
LDFLAGS =
LIBS = -lm -lz
RANLIB = ranlib
VERSION = 1.0.1
VERSION = 1.1
prefix = /usr/local

View File

@ -1,4 +1,4 @@
.TH pdfio 3 "pdf read/write library" "2022-03-02" "pdf read/write library"
.TH pdfio 3 "pdf read/write library" "2022-05-15" "pdf read/write library"
.SH NAME
pdfio \- pdf read/write library
.SH Introduction
@ -2302,6 +2302,20 @@ pdfio_stream_t * pdfioFileCreatePage (
pdfio_dict_t *dict
);
.fi
.SS pdfioFileCreateTemporary
.PP
.nf
pdfio_file_t * pdfioFileCreateTemporary (
char *buffer,
size_t bufsize,
const char *version,
pdfio_rect_t *media_box,
pdfio_rect_t *crop_box,
pdfio_error_cb_t error_cb,
void *error_data
);
.fi
.SS pdfioFileFindObj
Find an object using its object number.
.PP

View File

@ -1,13 +1,13 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>PDFio Programming Manual v1.0.1</title>
<title>PDFio Programming Manual v1.1</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta name="generator" content="codedoc v3.7">
<meta name="author" content="Michael R Sweet">
<meta name="language" content="en-US">
<meta name="copyright" content="Copyright © 2021-2022 by Michael R Sweet">
<meta name="version" content="1.0.1">
<meta name="version" content="1.1">
<style type="text/css"><!--
body {
background: white;
@ -245,7 +245,7 @@ span.string {
<body>
<div class="header">
<p><img class="title" src="pdfio-512.png"></p>
<h1 class="title">PDFio Programming Manual v1.0.1</h1>
<h1 class="title">PDFio Programming Manual v1.1</h1>
<p>Michael R Sweet</p>
<p>Copyright © 2021-2022 by Michael R Sweet</p>
</div>
@ -383,6 +383,7 @@ span.string {
<li><a href="#pdfioFileCreateObj">pdfioFileCreateObj</a></li>
<li><a href="#pdfioFileCreateOutput">pdfioFileCreateOutput</a></li>
<li><a href="#pdfioFileCreatePage">pdfioFileCreatePage</a></li>
<li><a href="#pdfioFileCreateTemporary">pdfioFileCreateTemporary</a></li>
<li><a href="#pdfioFileFindObj">pdfioFileFindObj</a></li>
<li><a href="#pdfioFileGetAuthor">pdfioFileGetAuthor</a></li>
<li><a href="#pdfioFileGetCreationDate">pdfioFileGetCreationDate</a></li>
@ -2730,6 +2731,35 @@ stored as indirect object references.</blockquote>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Contents stream</p>
<h3 class="function"><a id="pdfioFileCreateTemporary">pdfioFileCreateTemporary</a></h3>
<p class="description"></p>
<p class="code">
<a href="#pdfio_file_t">pdfio_file_t</a> *pdfioFileCreateTemporary(char *buffer, size_t bufsize, const char *version, <a href="#pdfio_rect_t">pdfio_rect_t</a> *media_box, <a href="#pdfio_rect_t">pdfio_rect_t</a> *crop_box, <a href="#pdfio_error_cb_t">pdfio_error_cb_t</a> error_cb, void *error_data);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>buffer</th>
<td class="description">Filename buffer</td></tr>
<tr><th>bufsize</th>
<td class="description">Size of filename buffer</td></tr>
<tr><th>version</th>
<td class="description">PDF version number or <code>NULL</code> for default (2.0)</td></tr>
<tr><th>media_box</th>
<td class="description">Default MediaBox for pages</td></tr>
<tr><th>crop_box</th>
<td class="description">Default CropBox for pages</td></tr>
<tr><th>error_cb</th>
<td class="description">Error callback or <code>NULL</code> for default</td></tr>
<tr><th>error_data</th>
<td class="description">Error callback data, if any</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Create a temporary PDF file.</p>
<p class="discussion">This function creates a PDF file with a unique filename in the current
temporary directory. The temporary file is stored in the string &quot;buffer&quot; an
will have a &quot;.pdf&quot; extension. Otherwise, this function works the same as
the <a href="#pdfioFileCreate"><code>pdfioFileCreate</code></a> function.
</p>
<h3 class="function"><a id="pdfioFileFindObj">pdfioFileFindObj</a></h3>
<p class="description">Find an object using its object number.</p>
<p class="code">

View File

@ -1,7 +1,7 @@
//
// PDF file functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2022 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -264,46 +264,26 @@ pdfioFileCreate(
// Write a standard PDF header...
if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version))
{
pdfioFileClose(pdf);
unlink(filename);
return (NULL);
}
goto error;
// Create the pages object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
{
pdfioFileClose(pdf);
unlink(filename);
return (NULL);
}
goto error;
pdfioDictSetName(dict, "Type", "Pages");
if ((pdf->pages_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
{
pdfioFileClose(pdf);
unlink(filename);
return (NULL);
}
goto error;
// Create the info object...
if ((info_dict = pdfioDictCreate(pdf)) == NULL)
{
pdfioFileClose(pdf);
unlink(filename);
return (NULL);
}
goto error;
pdfioDictSetDate(info_dict, "CreationDate", time(NULL));
pdfioDictSetString(info_dict, "Producer", "pdfio/" PDFIO_VERSION);
if ((pdf->info_obj = pdfioFileCreateObj(pdf, info_dict)) == NULL)
{
pdfioFileClose(pdf);
unlink(filename);
return (NULL);
}
goto error;
// Create random file ID values...
_pdfioCryptoMakeRandom(id_value, sizeof(id_value));
@ -315,6 +295,15 @@ pdfioFileCreate(
}
return (pdf);
// Common error handling code...
error:
pdfioFileClose(pdf);
unlink(filename);
return (NULL);
}
@ -535,41 +524,26 @@ pdfioFileCreateOutput(
// Write a standard PDF header...
if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version))
{
pdfioFileClose(pdf);
return (NULL);
}
goto error;
// Create the pages object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
goto error;
pdfioDictSetName(dict, "Type", "Pages");
if ((pdf->pages_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
goto error;
// Create the info object...
if ((info_dict = pdfioDictCreate(pdf)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
goto error;
pdfioDictSetDate(info_dict, "CreationDate", time(NULL));
pdfioDictSetString(info_dict, "Producer", "pdfio/" PDFIO_VERSION);
if ((pdf->info_obj = pdfioFileCreateObj(pdf, info_dict)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
goto error;
// Create random file ID values...
_pdfioCryptoMakeRandom(id_value, sizeof(id_value));
@ -581,6 +555,13 @@ pdfioFileCreateOutput(
}
return (pdf);
// Common error handling code...
error:
pdfioFileClose(pdf);
return (NULL);
}
@ -657,6 +638,194 @@ pdfioFileCreatePage(pdfio_file_t *pdf, // I - PDF file
}
//
// 'pdfioFileCreateTemporary()' - Create a temporary PDF file.
//
// This function creates a PDF file with a unique filename in the current
// temporary directory. The temporary file is stored in the string "buffer" an
// will have a ".pdf" extension. Otherwise, this function works the same as
// the @link pdfioFileCreate@ function.
//
// @since PDFio v1.1@
//
pdfio_file_t *
pdfioFileCreateTemporary(
char *buffer, // I - Filename buffer
size_t bufsize, // I - Size of filename buffer
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
unsigned char id_value[16]; // File ID value
int i; // Looping var
const char *tmpdir; // Temporary directory
#if _WIN32 || defined(__APPLE__)
char tmppath[256]; // Temporary directory path
#endif // _WIN32 || __APPLE__
unsigned tmpnum; // Temporary filename number
// Range check input...
if (!buffer || bufsize < 32)
{
if (buffer)
*buffer = '\0';
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 *)"temporary.pdf";
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno));
(error_cb)(&temp, message, error_data);
*buffer = '\0';
return (NULL);
}
// Create the file...
#if _WIN32
if ((tmpdir = getenv("TEMP")) == NULL)
{
GetTempPathA(sizeof(tmppath), tmppath);
tmpdir = tmppath;
}
#elif defined(__APPLE__)
if ((tmpdir = getenv("TMPDIR")) != NULL && access(tmpdir, W_OK))
tmpdir = NULL;
if (!tmpdir)
{
// Grab the per-process temporary directory for sandboxed apps...
# ifdef _CS_DARWIN_USER_TEMP_DIR
if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmppath, sizeof(tmppath)))
tmpdir = tmppath;
else
# endif // _CS_DARWIN_USER_TEMP_DIR
tmpdir = "/private/tmp";
}
#else
if ((tmpdir = getenv("TMPDIR")) == NULL || access(tmpdir, W_OK))
tmpdir = "/tmp";
#endif // _WIN32
for (i = 0; i < 1000; i ++)
{
_pdfioCryptoMakeRandom((uint8_t *)&tmpnum, sizeof(tmpnum));
snprintf(buffer, bufsize, "%s/%08x.pdf", tmpdir, tmpnum);
if ((pdf->fd = open(buffer, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC | O_EXCL, 0666)) >= 0)
break;
}
pdf->filename = strdup(buffer);
if (i >= 1000)
{
_pdfioFileError(pdf, "Unable to create file - %s", strerror(errno));
free(pdf->filename);
free(pdf);
*buffer = '\0';
return (NULL);
}
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->permissions = PDFIO_PERMISSION_ALL;
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;
}
// Write a standard PDF header...
if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version))
goto error;
// Create the pages object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
goto error;
pdfioDictSetName(dict, "Type", "Pages");
if ((pdf->pages_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
goto error;
// Create the info object...
if ((info_dict = pdfioDictCreate(pdf)) == NULL)
goto error;
pdfioDictSetDate(info_dict, "CreationDate", time(NULL));
pdfioDictSetString(info_dict, "Producer", "pdfio/" PDFIO_VERSION);
if ((pdf->info_obj = pdfioFileCreateObj(pdf, info_dict)) == NULL)
goto error;
// Create random file ID values...
_pdfioCryptoMakeRandom(id_value, sizeof(id_value));
if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL)
{
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
}
return (pdf);
// Common error handling code...
error:
pdfioFileClose(pdf);
unlink(buffer);
*buffer = '\0';
return (NULL);
}
//
// '_pdfioFileFindMappedObj()' - Find a mapped object.
//

View File

@ -1,7 +1,7 @@
//
// Public header file for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2022 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -185,6 +185,7 @@ extern pdfio_obj_t *pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_dict_t *dict) _P
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?
extern pdfio_stream_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateTemporary(char *buffer, size_t bufsize, 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 *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;
extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;

View File

@ -372,7 +372,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0.1;
CURRENT_PROJECT_VERSION = 1.1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -450,7 +450,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0.1;
CURRENT_PROJECT_VERSION = 1.1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;

View File

@ -506,6 +506,7 @@ do_unit_tests(void)
_pdfio_value_t value; // Value
size_t first_image, // First image object
num_pages; // Number of pages written
char temppdf[1024]; // Temporary PDF file
static const char *complex_dict = // Complex dictionary value
"<</Annots 5457 0 R/Contents 5469 0 R/CropBox[0 0 595.4 842]/Group 725 0 R"
"/MediaBox[0 0 595.4 842]/Parent 23513 0 R/Resources<</ColorSpace<<"
@ -1107,6 +1108,18 @@ do_unit_tests(void)
if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false))
return (1);
fputs("pdfioFileCreateTemporary: ", stdout);
if ((outpdf = pdfioFileCreateTemporary(temppdf, sizeof(temppdf), NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
printf("PASS (%s)\n", temppdf);
else
return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file(temppdf, num_pages, first_image, false))
return (1);
pdfioFileClose(inpdf);
return (0);