8 Commits

Author SHA1 Message Date
f040cc41c2 Add #define guard to allow MingW to build PDFio; note that MingW is NOT a supported toolchain for PDFio (Issue #66) 2024-06-24 09:03:46 -04:00
23883268e3 Add pdfioFileGetCatalog function (Issue #67)
Refactor the pdfioFileCreateXxx functions to use a common (private) function to
handle creating/initializing the pdfio_file_t object and base file objects.

Update unit tests to display the filename for the pdfioFileClose test.
2024-06-24 08:56:16 -04:00
a1e14503fd Bump version in other files, update makesrcdist to support checking. 2024-06-24 07:28:54 -04:00
0766869ad1 Bump version to 1.3.0. 2024-06-24 07:12:01 -04:00
6c1db141a1 Switch string pool code to an insertion sort - provides a modest 25% improvement
to open speeds on typical files.
2024-01-27 20:58:50 -05:00
b117959725 Make sure all output code paths set the locale information (Issue #61) 2024-01-27 19:23:51 -05:00
e882622233 Fix locale support (Issue #61) 2024-01-27 18:22:16 -05:00
c13b5a5e90 Bump version. 2024-01-27 18:20:36 -05:00
15 changed files with 778 additions and 423 deletions

View File

@ -2,6 +2,17 @@ Changes in PDFio
================ ================
v1.3.0 (Month DD, YYYY)
-----------------------
- Added `pdfioFileGetCatalog` API for accessing the root/catalog object of a
PDF file (Issue #67)
- Updated number support to avoid locale issues (Issue #61)
- Updated the PDFio private header to allow compilation with MingW; note that
MingW is NOT a supported toolchain for PDFio (Issue #66)
- Optimized string pool code.
v1.2.0 (January 24, 2024) v1.2.0 (January 24, 2024)
------------------------- -------------------------

View File

@ -1,7 +1,7 @@
# #
# Makefile for PDFio. # Makefile for PDFio.
# #
# Copyright © 2021-2023 by Michael R Sweet. # Copyright © 2021-2024 by Michael R Sweet.
# #
# Licensed under Apache License v2.0. See the file "LICENSE" for more # Licensed under Apache License v2.0. See the file "LICENSE" for more
# information. # information.
@ -163,7 +163,9 @@ install: $(TARGETS)
# Test everything # Test everything
test: testpdfio testttf test: testpdfio testttf
./testttf 2>test.log ./testttf 2>test.log
./testpdfio 2>test.log ./testpdfio 2>>test.log
LANG=fr_FR.UTF-8 ./testpdfio 2>>test.log
valgrind: testpdfio valgrind: testpdfio
valgrind --leak-check=full ./testpdfio valgrind --leak-check=full ./testpdfio

27
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for pdfio 1.2.0. # Generated by GNU Autoconf 2.71 for pdfio 1.3.0.
# #
# Report bugs to <https://github.com/michaelrsweet/pdfio/issues>. # Report bugs to <https://github.com/michaelrsweet/pdfio/issues>.
# #
@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='pdfio' PACKAGE_NAME='pdfio'
PACKAGE_TARNAME='pdfio' PACKAGE_TARNAME='pdfio'
PACKAGE_VERSION='1.2.0' PACKAGE_VERSION='1.3.0'
PACKAGE_STRING='pdfio 1.2.0' PACKAGE_STRING='pdfio 1.3.0'
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues' PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
PACKAGE_URL='https://www.msweet.org/pdfio' PACKAGE_URL='https://www.msweet.org/pdfio'
@ -1293,7 +1293,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures pdfio 1.2.0 to adapt to many kinds of systems. \`configure' configures pdfio 1.3.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1359,7 +1359,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of pdfio 1.2.0:";; short | recursive ) echo "Configuration of pdfio 1.3.0:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1456,7 +1456,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
pdfio configure 1.2.0 pdfio configure 1.3.0
generated by GNU Autoconf 2.71 generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc. Copyright (C) 2021 Free Software Foundation, Inc.
@ -1612,7 +1612,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by pdfio $as_me 1.2.0, which was It was created by pdfio $as_me 1.3.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw $ $0$ac_configure_args_raw
@ -2368,9 +2368,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
PDFIO_VERSION="1.2.0" PDFIO_VERSION="1.3.0"
PDFIO_VERSION_MAJOR="`echo 1.2.0 | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo 1.3.0 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.2.0 | awk -F. '{printf("%d\n",$2);}'`" PDFIO_VERSION_MINOR="`echo 1.3.0 | awk -F. '{printf("%d\n",$2);}'`"
@ -4029,8 +4029,8 @@ then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; } printf "%s\n" "yes" >&6; }
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS" CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
else $as_nop else $as_nop
@ -4093,6 +4093,7 @@ then :
fi fi
PKGCONFIG_REQUIRES=""
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE" PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
fi fi
@ -4934,7 +4935,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by pdfio $as_me 1.2.0, which was This file was extended by pdfio $as_me 1.3.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -4990,7 +4991,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped' ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\ ac_cs_version="\\
pdfio config.status 1.2.0 pdfio config.status 1.3.0
configured by $0, generated by GNU Autoconf 2.71, configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@ -21,7 +21,7 @@ AC_PREREQ([2.70])
dnl Package name and version... dnl Package name and version...
AC_INIT([pdfio], [1.2.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio]) AC_INIT([pdfio], [1.3.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
PDFIO_VERSION="AC_PACKAGE_VERSION" PDFIO_VERSION="AC_PACKAGE_VERSION"
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"

View File

@ -2,18 +2,61 @@
# #
# makesrcdist - make a source distribution of pdfio. # makesrcdist - make a source distribution of pdfio.
# #
# Usage:
#
# ./makesrcdist [--snapshot] VERSION
#
# Support "--snapshot" option...
if test "$1" == "--snapshot"; then
shift
snapshot=1
else
snapshot=0
fi
# Get version...
if test $# != 1; then if test $# != 1; then
echo "Usage: ./makesrcdist version" echo "Usage: ./makesrcdist [--snapshot] VERSION"
exit 1 exit 1
fi fi
version=$1 version=$1
echo Creating tag for release... # Check that version number has been updated everywhere...
git tag -m "Tag $version" v$version if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then
git push origin v$version echo "Still need to update AC_INIT version in 'configure.ac'."
exit 1
fi
if test $(grep PDFIO_VERSION= configure | awk -F \" '{print $2}') != "$version"; then
echo "Still need to run 'autoconf -f'."
exit 1
fi
if test $(grep '<version>' pdfio_native.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then
echo "Still need to update version in 'pdfio_native.nuspec'."
exit 1
fi
if test $(grep '<version>' pdfio_native.redist.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then
echo "Still need to update version in 'pdfio_native.redist.nuspec'."
exit 1
fi
if test $(grep PDFIO_VERSION pdfio.h | awk -F \" '{print $2}') != "$version"; then
echo "Still need to update PDFIO_VERSION in 'pdfio.h'."
exit 1
fi
# Tag release...
if test $snapshot = 0; then
echo Creating tag for release...
git tag -m "Tag $version" v$version
git push origin v$version
fi
# Make source archives...
echo Creating pdfio-$version.tar.gz... echo Creating pdfio-$version.tar.gz...
git archive --format tar --prefix=pdfio-$version/ HEAD | gzip -v9 >pdfio-$version.tar.gz git archive --format tar --prefix=pdfio-$version/ HEAD | gzip -v9 >pdfio-$version.tar.gz
gpg --detach-sign pdfio-$version.tar.gz gpg --detach-sign pdfio-$version.tar.gz

View File

@ -1,7 +1,7 @@
// //
// Common support functions for pdfio. // Common support functions for pdfio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -261,7 +261,7 @@ _pdfioFilePrintf(pdfio_file_t *pdf, // I - PDF file
// Format the string... // Format the string...
va_start(ap, format); va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap); _pdfio_vsnprintf(pdf, buffer, sizeof(buffer), format, ap);
va_end(ap); va_end(ap);
// Write it... // Write it...

View File

@ -1,7 +1,7 @@
// //
// PDF file functions for PDFio. // PDF file functions for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -19,11 +19,12 @@
static pdfio_obj_t *add_obj(pdfio_file_t *pdf, size_t number, unsigned short generation, off_t offset); static pdfio_obj_t *add_obj(pdfio_file_t *pdf, size_t number, unsigned short generation, off_t offset);
static int compare_objmaps(_pdfio_objmap_t *a, _pdfio_objmap_t *b); static int compare_objmaps(_pdfio_objmap_t *a, _pdfio_objmap_t *b);
static pdfio_file_t *create_common(const char *filename, int fd, pdfio_output_cb_t output_cb, void *output_cbdata, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_cbdata);
static const char *get_info_string(pdfio_file_t *pdf, const char *key); static const char *get_info_string(pdfio_file_t *pdf, const char *key);
static struct lconv *get_lconv(void);
static bool load_obj_stream(pdfio_obj_t *obj); static bool load_obj_stream(pdfio_obj_t *obj);
static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj, size_t depth); static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj, size_t depth);
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset, pdfio_password_cb_t password_cb, void *password_data); static bool load_xref(pdfio_file_t *pdf, off_t xref_offset, pdfio_password_cb_t password_cb, void *password_data);
static bool write_catalog(pdfio_file_t *pdf);
static bool write_pages(pdfio_file_t *pdf); static bool write_pages(pdfio_file_t *pdf);
static bool write_trailer(pdfio_file_t *pdf); static bool write_trailer(pdfio_file_t *pdf);
@ -119,11 +120,8 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
{ {
ret = false; ret = false;
if (pdfioObjClose(pdf->info_obj)) if (pdfioObjClose(pdf->info_obj) && write_pages(pdf) && pdfioObjClose(pdf->root_obj) && write_trailer(pdf))
if (write_pages(pdf)) ret = _pdfioFileFlush(pdf);
if (write_catalog(pdf))
if (write_trailer(pdf))
ret = _pdfioFileFlush(pdf);
} }
if (pdf->fd >= 0 && close(pdf->fd) < 0) if (pdf->fd >= 0 && close(pdf->fd) < 0)
@ -172,7 +170,7 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
// CropBox for pages in the PDF file - if `NULL` then a default "Universal" size // CropBox for pages in the PDF file - if `NULL` then a default "Universal" size
// of 8.27x11in (the intersection of US Letter and ISO A4) is used. // of 8.27x11in (the intersection of US Letter and ISO A4) is used.
// //
// The "error_cb" and "error_data" arguments specify an error handler callback // The "error_cb" and "error_cbdata" arguments specify an error handler callback
// and its data pointer - if `NULL` the default error handler is used that // and its data pointer - if `NULL` the default error handler is used that
// writes error messages to `stderr`. // writes error messages to `stderr`.
// //
@ -184,122 +182,37 @@ pdfioFileCreate(
pdfio_rect_t *media_box, // I - Default MediaBox for pages pdfio_rect_t *media_box, // I - Default MediaBox for pages
pdfio_rect_t *crop_box, // I - Default CropBox 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 pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
void *error_data) // I - Error callback data, if any void *error_cbdata) // I - Error callback data, if any
{ {
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
pdfio_dict_t *dict; // Dictionary for pages object int fd; // File descriptor
pdfio_dict_t *info_dict; // Dictionary for information object
unsigned char id_value[16]; // File ID value
// Range check input... // Range check input...
if (!filename) if (!filename)
return (NULL); return (NULL);
if (!version) // Create the file...
version = "2.0"; if ((fd = open(filename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666)) < 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 pdfio_file_t temp; // Dummy file
char message[8192]; // Message string char message[8192]; // Message string
temp.filename = (char *)filename; temp.filename = (char *)filename;
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno)); snprintf(message, sizeof(message), "Unable to create '%s': %s", filename, strerror(errno));
(error_cb)(&temp, message, error_data); (error_cb)(&temp, message, error_cbdata);
return (NULL); return (NULL);
} }
pdf->filename = strdup(filename); if ((pdf = create_common(filename, fd, /*output_cb*/NULL, /*output_cbdata*/NULL, version, media_box, crop_box, error_cb, error_cbdata)) == 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; // Remove the newly created file if we can't create the PDF file object...
} close(fd);
else unlink(filename);
{
// 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;
}
// Create the file...
if ((pdf->fd = open(filename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666)) < 0)
{
_pdfioFileError(pdf, "Unable to create file - %s", strerror(errno));
free(pdf->filename);
free(pdf->version);
free(pdf);
return (NULL);
}
// 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); return (pdf);
// Common error handling code...
error:
pdfioFileClose(pdf);
unlink(filename);
return (NULL);
} }
@ -439,13 +352,13 @@ _pdfioFileCreateObj(
// 'pdfioFileCreateOutput()' - Create a PDF file through an output callback. // 'pdfioFileCreateOutput()' - Create a PDF file through an output callback.
// //
// This function creates a new PDF file that is streamed though an output // This function creates a new PDF file that is streamed though an output
// callback. The "output_cb" and "output_ctx" arguments specify the output // callback. The "output_cb" and "output_cbdata" arguments specify the output
// callback and its context pointer which is called whenever data needs to be // callback and its data pointer which is called whenever data needs to be
// written: // written:
// //
// ``` // ```
// ssize_t // ssize_t
// output_cb(void *output_ctx, const void *buffer, size_t bytes) // output_cb(void *output_cbdata, const void *buffer, size_t bytes)
// { // {
// // Write buffer to output and return the number of bytes written // // Write buffer to output and return the number of bytes written
// } // }
@ -458,7 +371,7 @@ _pdfioFileCreateObj(
// CropBox for pages in the PDF file - if `NULL` then a default "Universal" size // CropBox for pages in the PDF file - if `NULL` then a default "Universal" size
// of 8.27x11in (the intersection of US Letter and ISO A4) is used. // of 8.27x11in (the intersection of US Letter and ISO A4) is used.
// //
// The "error_cb" and "error_data" arguments specify an error handler callback // The "error_cb" and "error_cbdata" arguments specify an error handler callback
// and its data pointer - if `NULL` the default error handler is used that // and its data pointer - if `NULL` the default error handler is used that
// writes error messages to `stderr`. // writes error messages to `stderr`.
// //
@ -469,121 +382,15 @@ _pdfioFileCreateObj(
pdfio_file_t * // O - PDF file or `NULL` on error pdfio_file_t * // O - PDF file or `NULL` on error
pdfioFileCreateOutput( pdfioFileCreateOutput(
pdfio_output_cb_t output_cb, // I - Output callback pdfio_output_cb_t output_cb, // I - Output callback function
void *output_ctx, // I - Output context void *output_cbdata, // I - Output callback data
const char *version, // I - PDF version number or `NULL` for default (2.0) 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 *media_box, // I - Default MediaBox for pages
pdfio_rect_t *crop_box, // I - Default CropBox 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 pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
void *error_data) // I - Error callback data, if any void *error_cbdata) // I - Error callback data, if any
{ {
pdfio_file_t *pdf; // PDF file return (create_common("output.pdf", /*fd*/-1, output_cb, output_cbdata, version, media_box, crop_box, error_cb, error_cbdata));
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
// 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->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;
}
// 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))
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);
return (NULL);
} }
@ -705,13 +512,11 @@ pdfioFileCreateTemporary(
pdfio_rect_t *media_box, // I - Default MediaBox for pages pdfio_rect_t *media_box, // I - Default MediaBox for pages
pdfio_rect_t *crop_box, // I - Default CropBox 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 pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
void *error_data) // I - Error callback data, if any void *error_cbdata) // I - Error callback data, if any
{ {
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
pdfio_dict_t *dict; // Dictionary for pages object int i, // Looping var
pdfio_dict_t *info_dict; // Dictionary for information object fd; // File descriptor
unsigned char id_value[16]; // File ID value
int i; // Looping var
const char *tmpdir; // Temporary directory const char *tmpdir; // Temporary directory
#if _WIN32 || defined(__APPLE__) #if _WIN32 || defined(__APPLE__)
char tmppath[256]; // Temporary directory path char tmppath[256]; // Temporary directory path
@ -727,31 +532,7 @@ pdfioFileCreateTemporary(
return (NULL); return (NULL);
} }
if (!version) // Create the temporary PDF file...
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 _WIN32
if ((tmpdir = getenv("TEMP")) == NULL) if ((tmpdir = getenv("TEMP")) == NULL)
{ {
@ -779,98 +560,35 @@ pdfioFileCreateTemporary(
tmpdir = "/tmp"; tmpdir = "/tmp";
#endif // _WIN32 #endif // _WIN32
for (i = 0; i < 1000; i ++) for (i = 0, fd = -1; i < 1000; i ++)
{ {
_pdfioCryptoMakeRandom((uint8_t *)&tmpnum, sizeof(tmpnum)); _pdfioCryptoMakeRandom((uint8_t *)&tmpnum, sizeof(tmpnum));
snprintf(buffer, bufsize, "%s/%08x.pdf", tmpdir, 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) if ((fd = open(buffer, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC | O_EXCL, 0666)) >= 0)
break; break;
} }
pdf->filename = strdup(buffer); if (fd < 0)
if (i >= 1000)
{ {
_pdfioFileError(pdf, "Unable to create file - %s", strerror(errno)); pdfio_file_t temp; // Dummy file
free(pdf->filename); char message[8192]; // Message string
free(pdf);
*buffer = '\0'; temp.filename = (char *)"<temporary>";
snprintf(message, sizeof(message), "Unable to create temporary PDF file: %s", strerror(errno));
(error_cb)(&temp, message, error_cbdata);
return (NULL); return (NULL);
} }
pdf->version = strdup(version); if ((pdf = create_common(buffer, fd, /*output_cb*/NULL, /*output_cbdata*/NULL, version, media_box, crop_box, error_cb, error_cbdata)) == NULL)
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; // Remove the temporary file if we can't create the PDF file object...
} close(fd);
else unlink(buffer);
{ *buffer = '\0';
// 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); return (pdf);
// Common error handling code...
error:
pdfioFileClose(pdf);
unlink(buffer);
*buffer = '\0';
return (NULL);
} }
@ -980,6 +698,19 @@ pdfioFileGetAuthor(pdfio_file_t *pdf) // I - PDF file
} }
//
// 'pdfioFileGetCatalog()' - Get the document catalog dictionary.
//
// @since PDFio 1.3@
//
pdfio_dict_t * // O - Catalog dictionary
pdfioFileGetCatalog(pdfio_file_t *pdf) // I - PDF file
{
return (pdf ? pdfioObjGetDict(pdf->root_obj) : NULL);
}
// //
// 'pdfioFileGetCreationDate()' - Get the creation date for a PDF file. // 'pdfioFileGetCreationDate()' - Get the creation date for a PDF file.
// //
@ -1169,13 +900,13 @@ pdfioFileGetVersion(
// This function opens an existing PDF file. The "filename" argument specifies // This function opens an existing PDF file. The "filename" argument specifies
// the name of the PDF file to create. // the name of the PDF file to create.
// //
// The "password_cb" and "password_data" arguments specify a password callback // The "password_cb" and "password_cbdata" arguments specify a password callback
// and its data pointer for PDF files that use one of the standard Adobe // and its data pointer for PDF files that use one of the standard Adobe
// "security" handlers. The callback returns a password string or `NULL` to // "security" handlers. The callback returns a password string or `NULL` to
// cancel the open. If `NULL` is specified for the callback function and the // cancel the open. If `NULL` is specified for the callback function and the
// PDF file requires a password, the open will always fail. // PDF file requires a password, the open will always fail.
// //
// The "error_cb" and "error_data" arguments specify an error handler callback // The "error_cb" and "error_cbdata" arguments specify an error handler callback
// and its data pointer - if `NULL` the default error handler is used that // and its data pointer - if `NULL` the default error handler is used that
// writes error messages to `stderr`. // writes error messages to `stderr`.
// //
@ -1184,9 +915,10 @@ pdfio_file_t * // O - PDF file
pdfioFileOpen( pdfioFileOpen(
const char *filename, // I - Filename const char *filename, // I - Filename
pdfio_password_cb_t password_cb, // I - Password callback or `NULL` for none pdfio_password_cb_t password_cb, // I - Password callback or `NULL` for none
void *password_data, // I - Password callback data, if any void *password_cbdata,
// I - Password callback data, if any
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
void *error_data) // I - Error callback data, if any void *error_cbdata) // I - Error callback data, if any
{ {
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
char line[1025], // Line from file char line[1025], // Line from file
@ -1202,8 +934,8 @@ pdfioFileOpen(
if (!error_cb) if (!error_cb)
{ {
error_cb = _pdfioFileDefaultError; error_cb = _pdfioFileDefaultError;
error_data = NULL; error_cbdata = NULL;
} }
// Allocate a PDF file structure... // Allocate a PDF file structure...
@ -1214,14 +946,15 @@ pdfioFileOpen(
temp.filename = (char *)filename; temp.filename = (char *)filename;
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno)); snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno));
(error_cb)(&temp, message, error_data); (error_cb)(&temp, message, error_cbdata);
return (NULL); return (NULL);
} }
pdf->loc = get_lconv();
pdf->filename = strdup(filename); pdf->filename = strdup(filename);
pdf->mode = _PDFIO_MODE_READ; pdf->mode = _PDFIO_MODE_READ;
pdf->error_cb = error_cb; pdf->error_cb = error_cb;
pdf->error_data = error_data; pdf->error_data = error_cbdata;
pdf->permissions = PDFIO_PERMISSION_ALL; pdf->permissions = PDFIO_PERMISSION_ALL;
// Open the file... // Open the file...
@ -1277,7 +1010,7 @@ pdfioFileOpen(
xref_offset = (off_t)strtol(ptr + 9, NULL, 10); xref_offset = (off_t)strtol(ptr + 9, NULL, 10);
if (!load_xref(pdf, xref_offset, password_cb, password_data)) if (!load_xref(pdf, xref_offset, password_cb, password_cbdata))
goto error; goto error;
return (pdf); return (pdf);
@ -1369,7 +1102,7 @@ pdfioFileSetPermissions(
if (!pdf) if (!pdf)
return (false); return (false);
if (pdf->num_objs > 2) // First two objects are pages and info if (pdf->num_objs > 3) // First three objects are pages, info, and root
{ {
_pdfioFileError(pdf, "You must call pdfioFileSetPermissions before adding any objects."); _pdfioFileError(pdf, "You must call pdfioFileSetPermissions before adding any objects.");
return (false); return (false);
@ -1531,6 +1264,142 @@ compare_objmaps(_pdfio_objmap_t *a, // I - First object map
} }
//
// 'create_common()' - Allocate and initialize a pdfio_file_t object for writing.
//
static pdfio_file_t * // O - New PDF file
create_common(
const char *filename, // I - Filename
int fd, // I - File descriptor, if any
pdfio_output_cb_t output_cb, // I - Output callback function, if any
void *output_cbdata, // I - Output callback data, if any
const char *version, // I - PDF version
pdfio_rect_t *media_box, // I - Media box or `NULL` for default
pdfio_rect_t *crop_box, // I - Crop box of `NULL` for default
pdfio_error_cb_t error_cb, // I - Error callback function
void *error_cbdata) // I - Error callback data
{
pdfio_file_t *pdf; // New PDF file
pdfio_dict_t *dict; // Dictionary
unsigned char id_value[16]; // File ID value
// Range check input...
if (!filename || (fd < 0 && !output_cb))
return (NULL);
if (!version)
version = "2.0";
if (!error_cb)
{
error_cb = _pdfioFileDefaultError;
error_cbdata = 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 *)filename;
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file: %s", strerror(errno));
(error_cb)(&temp, message, error_cbdata);
return (NULL);
}
// Initialize PDF object...
pdf->loc = get_lconv();
pdf->fd = fd;
pdf->output_cb = output_cb;
pdf->output_ctx = output_cbdata;
pdf->filename = strdup(filename);
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_cbdata;
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 ((dict = pdfioDictCreate(pdf)) == NULL)
goto error;
pdfioDictSetDate(dict, "CreationDate", time(NULL));
pdfioDictSetString(dict, "Producer", "pdfio/" PDFIO_VERSION);
if ((pdf->info_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
goto error;
// Create the root object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
goto error;
pdfioDictSetName(dict, "Type", "Catalog");
pdfioDictSetObj(dict, "Pages", pdf->pages_obj);
if ((pdf->root_obj = pdfioFileCreateObj(pdf, 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);
return (NULL);
}
// //
// 'get_info_string()' - Get a string value from the Info dictionary. // 'get_info_string()' - Get a string value from the Info dictionary.
// //
@ -1576,6 +1445,28 @@ get_info_string(pdfio_file_t *pdf, // I - PDF file
} }
//
// 'get_lconv()' - Get any locale-specific numeric information.
//
static struct lconv * // O - Locale information or `NULL`
get_lconv(void)
{
struct lconv *loc; // Locale information
if ((loc = localeconv()) != NULL)
{
PDFIO_DEBUG("get_lconv: loc=%p, loc->decimal_point=\"%s\"\n", loc, loc->decimal_point);
if (!loc->decimal_point || !strcmp(loc->decimal_point, "."))
loc = NULL;
}
return (loc);
}
// //
// 'load_obj_stream()' - Load an object stream. // 'load_obj_stream()' - Load an object stream.
// //
@ -2218,30 +2109,6 @@ load_xref(
} }
//
// '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");
pdfioDictSetObj(dict, "Pages", pdf->pages_obj);
// TODO: Add support for all of the root object dictionary keys
if ((pdf->root_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
return (false);
else
return (pdfioObjClose(pdf->root_obj));
}
// //
// 'write_pages()' - Write the PDF pages objects. // 'write_pages()' - Write the PDF pages objects.
// //

View File

@ -1,7 +1,7 @@
// //
// Private header file for PDFio. // Private header file for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -20,6 +20,7 @@
# include <errno.h> # include <errno.h>
# include <inttypes.h> # include <inttypes.h>
# include <fcntl.h> # include <fcntl.h>
# include <locale.h>
# ifdef _WIN32 # ifdef _WIN32
# include <io.h> # include <io.h>
# include <direct.h> # include <direct.h>
@ -37,9 +38,11 @@
# define unlink _unlink # define unlink _unlink
# define vsnprintf _vsnprintf # define vsnprintf _vsnprintf
# define write _write # define write _write
# define F_OK 00 // POSIX parameters/flags # ifndef F_OK
# define W_OK 02 # define F_OK 00 // POSIX parameters/flags
# define R_OK 04 # define W_OK 02
# define R_OK 04
# endif // !F_OK
# define O_RDONLY _O_RDONLY // Map standard POSIX open flags # define O_RDONLY _O_RDONLY // Map standard POSIX open flags
# define O_WRONLY _O_WRONLY # define O_WRONLY _O_WRONLY
# define O_CREAT _O_CREAT # define O_CREAT _O_CREAT
@ -224,6 +227,7 @@ typedef struct _pdfio_objmap_s // PDF object map
struct _pdfio_file_s // PDF file structure struct _pdfio_file_s // PDF file structure
{ {
char *filename; // Filename char *filename; // Filename
struct lconv *loc; // Locale data
char *version; // Version number char *version; // Version number
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
@ -322,6 +326,9 @@ struct _pdfio_stream_s // Stream
// Functions... // Functions...
// //
extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern ssize_t _pdfio_vsnprintf(pdfio_file_t *pdf, char *buffer, size_t bufsize, const char *format, va_list ap) _PDFIO_INTERNAL;
extern bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL; extern bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL; extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL; extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;

View File

@ -1,7 +1,7 @@
// //
// PDF stream functions for PDFio. // PDF stream functions for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -687,7 +687,7 @@ pdfioStreamPrintf(
// Format the string... // Format the string...
va_start(ap, format); va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap); _pdfio_vsnprintf(st->pdf, buffer, sizeof(buffer), format, ap);
va_end(ap); va_end(ap);
// Write the string... // Write the string...

View File

@ -1,7 +1,7 @@
// //
// PDF dictionary functions for PDFio. // PDF string functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -14,7 +14,360 @@
// Local functions... // Local functions...
// //
static int compare_strings(char **a, char **b); static size_t find_string(pdfio_file_t *pdf, const char *s, int *rdiff);
//
// '_pdfio_strtod()' - Convert a string to a double value.
//
// This function wraps strtod() to avoid locale issues.
//
double // O - Double value
_pdfio_strtod(pdfio_file_t *pdf, // I - PDF file
const char *s) // I - String
{
char temp[64], // Temporary buffer
*tempptr; // Pointer into temporary buffer
// See if the locale has a special decimal point string...
if (!pdf->loc)
return (strtod(s, NULL));
// Copy leading sign, numbers, period, and then numbers...
tempptr = temp;
temp[sizeof(temp) - 1] = '\0';
while (*s && *s != '.')
{
if (tempptr < (temp + sizeof(temp) - 1))
*tempptr++ = *s++;
else
return (0.0);
}
if (*s == '.')
{
// Convert decimal point to locale equivalent...
size_t declen = strlen(pdf->loc->decimal_point);
// Length of decimal point
s ++;
if (declen <= (sizeof(temp) - (size_t)(tempptr - temp)))
{
memcpy(tempptr, pdf->loc->decimal_point, declen);
tempptr += declen;
}
else
{
return (0.0);
}
}
// Copy any remaining characters...
while (*s)
{
if (tempptr < (temp + sizeof(temp) - 1))
*tempptr++ = *s++;
else
return (0.0);
}
// Nul-terminate the temporary string and convert the string...
*tempptr = '\0';
return (strtod(temp, NULL));
}
//
// '_pdfio_vsnprintf()' - Format a string.
//
// This function emulates vsnprintf() to avoid locale issues.
//
ssize_t // O - Number of bytes
_pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
char *buffer, // I - Output buffer
size_t bufsize, // I - Size of output buffer
const char *format, // I - printf-style format string
va_list ap) // I - Pointer to additional arguments
{
char *bufptr, // Pointer to position in buffer
*bufend, // Pointer to end of buffer
size, // Size character (h, l, L)
type; // Format type character
int width, // Width of field
prec; // Number of characters of precision
char tformat[100], // Temporary format string for snprintf()
*tptr, // Pointer into temporary format
temp[1024], // Buffer for formatted numbers
*tempptr; // Pointer into buffer
char *s; // Pointer to string
ssize_t bytes; // Total number of bytes needed
const char *dec = pdf->loc ? pdf->loc->decimal_point : ".";
// Decimal point string
char *decptr; // Pointer to decimal point
// Loop through the format string, formatting as needed...
bufptr = buffer;
bufend = buffer + bufsize - 1;
*bufend = '\0';
bytes = 0;
while (*format)
{
if (*format == '%')
{
// Format character...
tptr = tformat;
*tptr++ = *format++;
if (*format == '%')
{
if (bufptr < bufend)
*bufptr++ = *format;
bytes ++;
format ++;
continue;
}
else if (strchr(" -+#\'", *format))
{
*tptr++ = *format++;
}
if (*format == '*')
{
// Get width from argument...
format ++;
width = va_arg(ap, int);
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
tptr += strlen(tptr);
}
else
{
width = 0;
while (isdigit(*format & 255))
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
width = width * 10 + *format++ - '0';
}
}
if (*format == '.')
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
format ++;
if (*format == '*')
{
// Get precision from argument...
format ++;
prec = va_arg(ap, int);
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
tptr += strlen(tptr);
}
else
{
prec = 0;
while (isdigit(*format & 255))
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
prec = prec * 10 + *format++ - '0';
}
}
}
if (*format == 'l' && format[1] == 'l')
{
size = 'L';
if (tptr < (tformat + sizeof(tformat) - 2))
{
*tptr++ = 'l';
*tptr++ = 'l';
}
format += 2;
}
else if (*format == 'h' || *format == 'l' || *format == 'L')
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
size = *format++;
}
else
{
size = 0;
}
if (!*format)
break;
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
type = *format++;
*tptr = '\0';
switch (type)
{
case 'E' : // Floating point formats
case 'G' :
case 'e' :
case 'f' :
case 'g' :
if ((size_t)(width + 2) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, va_arg(ap, double));
if ((decptr = strstr(temp, dec)) != NULL)
{
// Convert locale decimal point to "."
PDFIO_DEBUG("_pdfio_vsnprintf: Before \"%s\"\n", temp);
tempptr = decptr + strlen(dec);
if (tempptr > (decptr + 1))
memmove(decptr + 1, tempptr, strlen(tempptr) + 1);
*decptr = '.';
// Strip trailing 0's...
for (tempptr = temp + strlen(temp) - 1; tempptr > temp && *tempptr == '0'; tempptr --)
*tempptr = '\0';
if (*tempptr == '.')
*tempptr = '\0'; // Strip trailing decimal point
PDFIO_DEBUG("_pdfio_vsnprintf: After \"%s\"\n", temp);
}
// Copy to the output buffer
bytes += (int)strlen(temp);
if (bufptr < bufend)
{
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
bufptr += strlen(bufptr);
}
break;
case 'B' : // Integer formats
case 'X' :
case 'b' :
case 'd' :
case 'i' :
case 'o' :
case 'u' :
case 'x' :
if ((size_t)(width + 2) > sizeof(temp))
break;
# ifdef HAVE_LONG_LONG
if (size == 'L')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
else
# endif // HAVE_LONG_LONG
if (size == 'l')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
else
snprintf(temp, sizeof(temp), tformat, va_arg(ap, int));
bytes += (int)strlen(temp);
if (bufptr < bufend)
{
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
bufptr += strlen(bufptr);
}
break;
case 'p' : // Pointer value
if ((size_t)(width + 2) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *));
bytes += (int)strlen(temp);
if (bufptr < bufend)
{
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
bufptr += strlen(bufptr);
}
break;
case 'c' : // Character or character array
bytes += width;
if (bufptr < bufend)
{
if (width <= 1)
{
*bufptr++ = (char)va_arg(ap, int);
}
else
{
if ((bufptr + width) > bufend)
width = (int)(bufend - bufptr);
memcpy(bufptr, va_arg(ap, char *), (size_t)width);
bufptr += width;
}
}
break;
case 's' : // String
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
bytes += strlen(s);
if (bufptr < bufend)
{
strncpy(bufptr, s, (size_t)(bufend - bufptr - 1));
bufptr += strlen(bufptr);
}
break;
case 'n' : // Output number of chars so far
*(va_arg(ap, int *)) = (int)bytes;
break;
}
}
else
{
// Literal character...
bytes ++;
if (bufptr < bufend)
*bufptr++ = *format++;
}
}
// Nul-terminate the string and return the number of characters needed.
if (bufptr < bufend)
{
// Everything fit in the buffer...
*bufptr = '\0';
}
PDFIO_DEBUG("_pdfio_vsnprintf: Returning %ld \"%s\"\n", (long)bytes, buffer);
return (bytes);
}
// //
@ -32,8 +385,9 @@ pdfioStringCreate(
pdfio_file_t *pdf, // I - PDF file pdfio_file_t *pdf, // I - PDF file
const char *s) // I - Nul-terminated string const char *s) // I - Nul-terminated string
{ {
char *news; // New string char *news; // New string
char **match; // Matching string size_t idx; // Index into strings
int diff; // Different
PDFIO_DEBUG("pdfioStringCreate(pdf=%p, s=\"%s\")\n", pdf, s); PDFIO_DEBUG("pdfioStringCreate(pdf=%p, s=\"%s\")\n", pdf, s);
@ -43,8 +397,17 @@ pdfioStringCreate(
return (NULL); return (NULL);
// See if the string has already been added... // See if the string has already been added...
if (pdf->num_strings > 0 && (match = (char **)bsearch(&s, pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings)) != NULL) if (pdf->num_strings > 0)
return (*match); {
idx = find_string(pdf, s, &diff);
if (diff == 0)
return (pdf->strings[idx]);
}
else
{
idx = 0;
diff = -1;
}
// Not already added, so add it... // Not already added, so add it...
if ((news = strdup(s)) == NULL) if ((news = strdup(s)) == NULL)
@ -65,11 +428,17 @@ pdfioStringCreate(
pdf->alloc_strings += 128; pdf->alloc_strings += 128;
} }
// TODO: Change to insertion sort as needed... // Insert the string...
pdf->strings[pdf->num_strings ++] = news; if (diff > 0)
idx ++;
if (pdf->num_strings > 1) PDFIO_DEBUG("pdfioStringCreate: Inserting \"%s\" at %u\n", news, (unsigned)idx);
qsort(pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings);
if (idx < pdf->num_strings)
memmove(pdf->strings + idx + 1, pdf->strings + idx, (pdf->num_strings - idx) * sizeof(char *));
pdf->strings[idx] = news;
pdf->num_strings ++;
PDFIO_DEBUG("pdfioStringCreate: %lu strings\n", (unsigned long)pdf->num_strings); PDFIO_DEBUG("pdfioStringCreate: %lu strings\n", (unsigned long)pdf->num_strings);
@ -120,17 +489,67 @@ _pdfioStringIsAllocated(
pdfio_file_t *pdf, // I - PDF file pdfio_file_t *pdf, // I - PDF file
const char *s) // I - String const char *s) // I - String
{ {
return (pdf->num_strings > 0 && bsearch(&s, pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings) != NULL); int diff; // Difference
if (pdf->num_strings == 0)
return (false);
find_string(pdf, s, &diff);
return (diff == 0);
} }
// //
// 'compare_strings()' - Compare two strings. // 'find_string()' - Find an element in the array.
// //
static int // O - Result of comparison static size_t // O - Index of match
compare_strings(char **a, // I - First string find_string(pdfio_file_t *pdf, // I - PDF file
char **b) // I - Second string const char *s, // I - String to find
int *rdiff) // O - Difference of match
{ {
return (strcmp(*a, *b)); size_t left, // Left side of search
right, // Right side of search
current; // Current element
int diff; // Comparison with current element
// Do a binary search for the string...
left = 0;
right = pdf->num_strings - 1;
do
{
current = (left + right) / 2;
diff = strcmp(s, pdf->strings[current]);
if (diff == 0)
break;
else if (diff < 0)
right = current;
else
left = current;
}
while ((right - left) > 1);
if (diff != 0)
{
// Check the last 1 or 2 elements...
if ((diff = strcmp(s, pdf->strings[left])) <= 0)
{
current = left;
}
else
{
diff = strcmp(s, pdf->strings[right]);
current = right;
}
}
// Return the closest string and the difference...
*rdiff = diff;
return (current);
} }

View File

@ -1,7 +1,7 @@
// //
// PDF value functions for PDFio. // PDF value functions for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -497,7 +497,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
// If we get here, we have a number... // If we get here, we have a number...
v->type = PDFIO_VALTYPE_NUMBER; v->type = PDFIO_VALTYPE_NUMBER;
v->value.number = (double)strtod(token, NULL); v->value.number = _pdfio_strtod(pdf, token);
} }
else if (!strcmp(token, "true") || !strcmp(token, "false")) else if (!strcmp(token, "true") || !strcmp(token, "false"))
{ {

View File

@ -1,7 +1,7 @@
// //
// Public header file for PDFio. // Public header file for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -23,7 +23,7 @@ extern "C" {
// Version number... // Version number...
// //
# define PDFIO_VERSION "1.2.0" # define PDFIO_VERSION "1.3.0"
// //
@ -191,6 +191,7 @@ extern pdfio_obj_t *pdfioFileCreateStringObj(pdfio_file_t *pdf, const char *s) _
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_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 pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;
extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioFileGetCatalog(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;

View File

@ -3,7 +3,7 @@
<metadata> <metadata>
<id>pdfio_native</id> <id>pdfio_native</id>
<title>PDFio Library for VS2019+</title> <title>PDFio Library for VS2019+</title>
<version>1.2.0</version> <version>1.3.0</version>
<authors>Michael R Sweet</authors> <authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners> <owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl> <projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
@ -16,7 +16,7 @@
<copyright>Copyright © 2019-2024 by Michael R Sweet</copyright> <copyright>Copyright © 2019-2024 by Michael R Sweet</copyright>
<tags>pdf file native</tags> <tags>pdf file native</tags>
<dependencies> <dependencies>
<dependency id="pdfio_native.redist" version="1.2.0" /> <dependency id="pdfio_native.redist" version="1.3.0" />
<dependency id="zlib_native.redist" version="1.2.11" /> <dependency id="zlib_native.redist" version="1.2.11" />
</dependencies> </dependencies>
</metadata> </metadata>

View File

@ -3,7 +3,7 @@
<metadata> <metadata>
<id>pdfio_native.redist</id> <id>pdfio_native.redist</id>
<title>PDFio Library for VS2019+</title> <title>PDFio Library for VS2019+</title>
<version>1.2.0</version> <version>1.3.0</version>
<authors>Michael R Sweet</authors> <authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners> <owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl> <projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>

View File

@ -1,7 +1,7 @@
// //
// Test program for PDFio. // Test program for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2024 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -16,6 +16,7 @@
#include "pdfio-private.h" #include "pdfio-private.h"
#include "pdfio-content.h" #include "pdfio-content.h"
#include <math.h> #include <math.h>
#include <locale.h>
#ifndef M_PI #ifndef M_PI
# define M_PI 3.14159265358979323846264338327950288 # define M_PI 3.14159265358979323846264338327950288
#endif // M_PI #endif // M_PI
@ -47,7 +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); static int write_unit_file(pdfio_file_t *inpdf, const char *outname, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image);
// //
@ -61,6 +62,8 @@ main(int argc, // I - Number of command-line arguments
int ret = 0; // Return value int ret = 0; // Return value
fprintf(stderr, "testpdfio: Test locale is \"%s\".\n", setlocale(LC_ALL, getenv("LANG")));
if (argc > 1) if (argc > 1)
{ {
int i; // Looping var int i; // Looping var
@ -1041,7 +1044,7 @@ do_unit_tests(void)
else else
goto fail; goto fail;
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-out.pdf", outpdf, &num_pages, &first_image))
goto fail; goto fail;
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
@ -1060,7 +1063,7 @@ do_unit_tests(void)
else else
goto fail; goto fail;
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-out2.pdf", outpdf, &num_pages, &first_image))
goto fail; goto fail;
close(outfd); close(outfd);
@ -1081,7 +1084,7 @@ do_unit_tests(void)
else else
return (1); return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-rc4.pdf", outpdf, &num_pages, &first_image))
return (1); return (1);
if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false))
@ -1100,7 +1103,7 @@ do_unit_tests(void)
else else
return (1); return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-rc4p.pdf", outpdf, &num_pages, &first_image))
return (1); return (1);
if (read_unit_file("testpdfio-rc4p.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-rc4p.pdf", num_pages, first_image, false))
@ -1118,7 +1121,7 @@ do_unit_tests(void)
else else
return (1); return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-aes.pdf", outpdf, &num_pages, &first_image))
return (1); return (1);
if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false))
@ -1136,7 +1139,7 @@ do_unit_tests(void)
else else
return (1); return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "testpdfio-aesp.pdf", outpdf, &num_pages, &first_image))
return (1); return (1);
if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false)) if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false))
@ -1148,7 +1151,7 @@ do_unit_tests(void)
else else
return (1); return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image)) if (write_unit_file(inpdf, "<temporary>", outpdf, &num_pages, &first_image))
return (1); return (1);
if (read_unit_file(temppdf, num_pages, first_image, false)) if (read_unit_file(temppdf, num_pages, first_image, false))
@ -3243,6 +3246,7 @@ write_text_test(pdfio_file_t *pdf, // I - PDF file
static int // O - Exit status static int // O - Exit status
write_unit_file( write_unit_file(
pdfio_file_t *inpdf, // I - Input PDF file pdfio_file_t *inpdf, // I - Input PDF file
const char *outname, // I - Output PDF file name
pdfio_file_t *outpdf, // I - Output PDF file pdfio_file_t *outpdf, // I - Output PDF file
size_t *num_pages, // O - Number of pages size_t *num_pages, // O - Number of pages
size_t *first_image) // O - First image object size_t *first_image) // O - First image object
@ -3437,7 +3441,7 @@ write_unit_file(
} }
// Close the new PDF file... // Close the new PDF file...
fputs("pdfioFileClose(...): ", stdout); printf("pdfioFileClose(\"%s\"): ", outname);
if (pdfioFileClose(outpdf)) if (pdfioFileClose(outpdf))
puts("PASS"); puts("PASS");
else else