mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2025-07-20 07:49:53 +02:00
Compare commits
21 Commits
5d760e7315
...
v1.3.0
Author | SHA1 | Date | |
---|---|---|---|
0c1122b689 | |||
d4f8dd46b5 | |||
986c5f0438 | |||
a81907bdb9 | |||
63a7a2cdbd | |||
f040cc41c2 | |||
23883268e3 | |||
a1e14503fd | |||
0766869ad1 | |||
6c1db141a1 | |||
b117959725 | |||
e882622233 | |||
c13b5a5e90 | |||
cd1406e158 | |||
59deee020a | |||
476013706e | |||
a43a9d9e32 | |||
abc69b3361 | |||
83bfb135c6 | |||
2dfb560f8b | |||
7330cc35ba |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -60,3 +60,5 @@ jobs:
|
||||
run: nuget restore pdfio.sln
|
||||
- name: Build PDFio
|
||||
run: msbuild pdfio.sln
|
||||
- name: Test PDFio
|
||||
run: .\runtests.bat x64\Debug
|
||||
|
15
CHANGES.md
15
CHANGES.md
@ -2,8 +2,19 @@ Changes in PDFio
|
||||
================
|
||||
|
||||
|
||||
v1.2.0 (Month DD, YYYY)
|
||||
-----------------------
|
||||
v1.3.0 (June 28, 2024)
|
||||
----------------------
|
||||
|
||||
- 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)
|
||||
-------------------------
|
||||
|
||||
- Now use autoconf to configure the PDFio sources (Issue #54)
|
||||
- Added `pdfioFileCreateNumberObj` and `pdfioFileCreateStringObj` functions
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# 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
|
||||
# information.
|
||||
@ -163,7 +163,9 @@ install: $(TARGETS)
|
||||
# Test everything
|
||||
test: testpdfio testttf
|
||||
./testttf 2>test.log
|
||||
./testpdfio 2>test.log
|
||||
./testpdfio 2>>test.log
|
||||
LANG=fr_FR.UTF-8 ./testpdfio 2>>test.log
|
||||
|
||||
|
||||
valgrind: testpdfio
|
||||
valgrind --leak-check=full ./testpdfio
|
||||
@ -172,6 +174,7 @@ valgrind: testpdfio
|
||||
# pdfio library
|
||||
libpdfio.a: $(LIBOBJS)
|
||||
echo Archiving $@...
|
||||
$(RM) $@
|
||||
$(AR) $(ARFLAGS) $@ $(LIBOBJS)
|
||||
$(RANLIB) $@
|
||||
|
||||
@ -226,7 +229,7 @@ ttf.o: ttf.h
|
||||
# Make documentation using Codedoc <https://www.msweet.org/codedoc>
|
||||
DOCFLAGS = \
|
||||
--author "Michael R Sweet" \
|
||||
--copyright "Copyright (c) 2021-2023 by Michael R Sweet" \
|
||||
--copyright "Copyright (c) 2021-2024 by Michael R Sweet" \
|
||||
--docversion $(PDFIO_VERSION)
|
||||
|
||||
.PHONY: doc
|
||||
|
2
NOTICE
2
NOTICE
@ -1,6 +1,6 @@
|
||||
PDFio - PDF Read/Write Library
|
||||
|
||||
Copyright © 2021-2023 by Michael R Sweet.
|
||||
Copyright © 2021-2024 by Michael R Sweet.
|
||||
|
||||
(Optional) Exceptions to the Apache 2.0 License:
|
||||
================================================
|
||||
|
@ -89,7 +89,7 @@ generates a static library that will be installed under "/usr/local" with:
|
||||
Legal Stuff
|
||||
-----------
|
||||
|
||||
PDFio is Copyright © 2021-2023 by Michael R Sweet.
|
||||
PDFio is Copyright © 2021-2024 by Michael R Sweet.
|
||||
|
||||
This software is licensed under the Apache License Version 2.0 with an
|
||||
(optional) exception to allow linking against GPL2/LGPL2 software. See the
|
||||
|
@ -54,7 +54,7 @@ starting at 0. A feature release has a "PATCH" value of 0, for example:
|
||||
1.1.0
|
||||
2.0.0
|
||||
|
||||
Beta releases and release candidates are *not* prodution releases and use
|
||||
Beta releases and release candidates are *not* production releases and use
|
||||
semantic version numbers of the form:
|
||||
|
||||
MAJOR.MINORbNUMBER
|
||||
|
27
configure
vendored
27
configure
vendored
@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# 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>.
|
||||
#
|
||||
@ -610,8 +610,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='pdfio'
|
||||
PACKAGE_TARNAME='pdfio'
|
||||
PACKAGE_VERSION='1.2.0'
|
||||
PACKAGE_STRING='pdfio 1.2.0'
|
||||
PACKAGE_VERSION='1.3.0'
|
||||
PACKAGE_STRING='pdfio 1.3.0'
|
||||
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
|
||||
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.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
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]...
|
||||
|
||||
@ -1359,7 +1359,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of pdfio 1.2.0:";;
|
||||
short | recursive ) echo "Configuration of pdfio 1.3.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@ -1456,7 +1456,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
pdfio configure 1.2.0
|
||||
pdfio configure 1.3.0
|
||||
generated by GNU Autoconf 2.71
|
||||
|
||||
Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
@ -1612,7 +1612,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
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
|
||||
|
||||
$ $0$ac_configure_args_raw
|
||||
@ -2368,9 +2368,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||
|
||||
|
||||
|
||||
PDFIO_VERSION="1.2.0"
|
||||
PDFIO_VERSION_MAJOR="`echo 1.2.0 | awk -F. '{print $1}'`"
|
||||
PDFIO_VERSION_MINOR="`echo 1.2.0 | awk -F. '{printf("%d\n",$2);}'`"
|
||||
PDFIO_VERSION="1.3.0"
|
||||
PDFIO_VERSION_MAJOR="`echo 1.3.0 | awk -F. '{print $1}'`"
|
||||
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" "yes" >&6; }
|
||||
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
|
||||
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
|
||||
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
|
||||
|
||||
else $as_nop
|
||||
|
||||
@ -4093,6 +4093,7 @@ then :
|
||||
|
||||
fi
|
||||
|
||||
PKGCONFIG_REQUIRES=""
|
||||
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
|
||||
|
||||
fi
|
||||
@ -4934,7 +4935,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
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
|
||||
|
||||
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
|
||||
ac_cs_config='$ac_cs_config_escaped'
|
||||
ac_cs_version="\\
|
||||
pdfio config.status 1.2.0
|
||||
pdfio config.status 1.3.0
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
dnl
|
||||
dnl Configuration script for PDFio
|
||||
dnl
|
||||
dnl Copyright © 2023 by Michael R Sweet
|
||||
dnl Copyright © 2023-2024 by Michael R Sweet
|
||||
dnl
|
||||
dnl Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||||
dnl information.
|
||||
@ -21,7 +21,7 @@ AC_PREREQ([2.70])
|
||||
|
||||
|
||||
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_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"
|
||||
@ -105,8 +105,8 @@ dnl ZLIB
|
||||
AC_MSG_CHECKING([for zlib via pkg-config])
|
||||
AS_IF([$PKGCONFIG --exists zlib], [
|
||||
AC_MSG_RESULT([yes])
|
||||
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
|
||||
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
|
||||
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
|
||||
],[
|
||||
AC_MSG_RESULT([no])
|
||||
AC_CHECK_HEADER([zlib.h])
|
||||
@ -116,6 +116,7 @@ AS_IF([$PKGCONFIG --exists zlib], [
|
||||
AC_MSG_ERROR([Sorry, this software requires zlib 1.1 or higher.])
|
||||
])
|
||||
|
||||
PKGCONFIG_REQUIRES=""
|
||||
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
|
||||
])
|
||||
|
||||
|
42
doc/pdfio.3
42
doc/pdfio.3
@ -1,4 +1,4 @@
|
||||
.TH pdfio 3 "pdf read/write library" "2023-12-05" "pdf read/write library"
|
||||
.TH pdfio 3 "pdf read/write library" "2024-06-24" "pdf read/write library"
|
||||
.SH NAME
|
||||
pdfio \- pdf read/write library
|
||||
.SH Introduction
|
||||
@ -34,7 +34,7 @@ PDFio is
|
||||
.I not
|
||||
concerned with rendering or viewing a PDF file, although a PDF RIP or viewer could be written using it.
|
||||
.PP
|
||||
PDFio is Copyright \[co] 2021\-2023 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
|
||||
PDFio is Copyright \[co] 2021\-2024 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
|
||||
.SS Requirements
|
||||
.PP
|
||||
PDFio requires the following to build the software:
|
||||
@ -2148,7 +2148,7 @@ pdfio_file_t * pdfioFileCreate (
|
||||
pdfio_rect_t *media_box,
|
||||
pdfio_rect_t *crop_box,
|
||||
pdfio_error_cb_t error_cb,
|
||||
void *error_data
|
||||
void *error_cbdata
|
||||
);
|
||||
.fi
|
||||
.PP
|
||||
@ -2162,7 +2162,7 @@ The "media_box" and "crop_box" arguments specify the default MediaBox and
|
||||
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
||||
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
||||
.PP
|
||||
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 \fBNULL\fR the default error handler is used that
|
||||
writes error messages to \fBstderr\fR.
|
||||
.SS pdfioFileCreateArrayObj
|
||||
@ -2325,23 +2325,23 @@ Create a PDF file through an output callback.
|
||||
.nf
|
||||
pdfio_file_t * pdfioFileCreateOutput (
|
||||
pdfio_output_cb_t output_cb,
|
||||
void *output_ctx,
|
||||
void *output_cbdata,
|
||||
const char *version,
|
||||
pdfio_rect_t *media_box,
|
||||
pdfio_rect_t *crop_box,
|
||||
pdfio_error_cb_t error_cb,
|
||||
void *error_data
|
||||
void *error_cbdata
|
||||
);
|
||||
.fi
|
||||
.PP
|
||||
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 and its context pointer which is called whenever data needs to be
|
||||
callback. The "output_cb" and "output_cbdata" arguments specify the output
|
||||
callback and its data pointer which is called whenever data needs to be
|
||||
written:
|
||||
.PP
|
||||
.nf
|
||||
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
|
||||
}
|
||||
@ -2355,7 +2355,7 @@ The "media_box" and "crop_box" arguments specify the default MediaBox and
|
||||
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
|
||||
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
|
||||
.PP
|
||||
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 \fBNULL\fR the default error handler is used that
|
||||
writes error messages to \fBstderr\fR.
|
||||
.PP
|
||||
@ -2397,7 +2397,7 @@ pdfio_file_t * pdfioFileCreateTemporary (
|
||||
pdfio_rect_t *media_box,
|
||||
pdfio_rect_t *crop_box,
|
||||
pdfio_error_cb_t error_cb,
|
||||
void *error_data
|
||||
void *error_cbdata
|
||||
);
|
||||
.fi
|
||||
.SS pdfioFileFindObj
|
||||
@ -2420,6 +2420,16 @@ const char * pdfioFileGetAuthor (
|
||||
pdfio_file_t *pdf
|
||||
);
|
||||
.fi
|
||||
.SS pdfioFileGetCatalog
|
||||
Get the document catalog dictionary.
|
||||
.PP
|
||||
.nf
|
||||
pdfio_dict_t * pdfioFileGetCatalog (
|
||||
pdfio_file_t *pdf
|
||||
);
|
||||
.fi
|
||||
.PP
|
||||
|
||||
.SS pdfioFileGetCreationDate
|
||||
Get the creation date for a PDF file.
|
||||
.PP
|
||||
@ -2545,22 +2555,22 @@ Open a PDF file for reading.
|
||||
pdfio_file_t * pdfioFileOpen (
|
||||
const char *filename,
|
||||
pdfio_password_cb_t password_cb,
|
||||
void *password_data,
|
||||
void *password_cbdata,
|
||||
pdfio_error_cb_t error_cb,
|
||||
void *error_data
|
||||
void *error_cbdata
|
||||
);
|
||||
.fi
|
||||
.PP
|
||||
This function opens an existing PDF file. The "filename" argument specifies
|
||||
the name of the PDF file to create.
|
||||
.PP
|
||||
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
|
||||
"security" handlers. The callback returns a password string or \fBNULL\fR to
|
||||
cancel the open. If \fBNULL\fR is specified for the callback function and the
|
||||
PDF file requires a password, the open will always fail.
|
||||
.PP
|
||||
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 \fBNULL\fR the default error handler is used that
|
||||
writes error messages to \fBstderr\fR.
|
||||
.SS pdfioFileSetAuthor
|
||||
@ -3087,4 +3097,4 @@ typedef enum pdfio_valtype_e pdfio_valtype_t;
|
||||
Michael R Sweet
|
||||
.SH COPYRIGHT
|
||||
.PP
|
||||
Copyright (c) 2021-2023 by Michael R Sweet
|
||||
Copyright (c) 2021-2024 by Michael R Sweet
|
||||
|
368
doc/pdfio.html
368
doc/pdfio.html
File diff suppressed because it is too large
Load Diff
@ -15,7 +15,7 @@ goals of pdfio are:
|
||||
PDFio is *not* concerned with rendering or viewing a PDF file, although a PDF
|
||||
RIP or viewer could be written using it.
|
||||
|
||||
PDFio is Copyright © 2021-2023 by Michael R Sweet and is licensed under the
|
||||
PDFio is Copyright © 2021-2024 by Michael R Sweet and is licensed under the
|
||||
Apache License Version 2.0 with an (optional) exception to allow linking against
|
||||
GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
|
||||
|
||||
|
53
makesrcdist
53
makesrcdist
@ -2,18 +2,61 @@
|
||||
#
|
||||
# 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
|
||||
echo "Usage: ./makesrcdist version"
|
||||
exit 1
|
||||
echo "Usage: ./makesrcdist [--snapshot] VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version=$1
|
||||
|
||||
echo Creating tag for release...
|
||||
git tag -m "Tag $version" v$version
|
||||
git push origin v$version
|
||||
# Check that version number has been updated everywhere...
|
||||
if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then
|
||||
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...
|
||||
git archive --format tar --prefix=pdfio-$version/ HEAD | gzip -v9 >pdfio-$version.tar.gz
|
||||
gpg --detach-sign pdfio-$version.tar.gz
|
||||
|
@ -327,6 +327,30 @@ pdfioArrayCreate(pdfio_file_t *pdf) // I - PDF file
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '_pdfioArrayDecrypt()' - Decrypt values in an array.
|
||||
//
|
||||
|
||||
bool // O - `true` on success, `false` on error
|
||||
_pdfioArrayDecrypt(pdfio_file_t *pdf, // I - PDF file
|
||||
pdfio_obj_t *obj, // I - Object
|
||||
pdfio_array_t *a, // I - Array
|
||||
size_t depth) // I - Depth
|
||||
{
|
||||
size_t i; // Looping var
|
||||
_pdfio_value_t *v; // Current value
|
||||
|
||||
|
||||
for (i = a->num_values, v = a->values; i > 0; i --, v ++)
|
||||
{
|
||||
if (!_pdfioValueDecrypt(pdf, obj, v, depth))
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '_pdfioArrayDebug()' - Print the contents of an array.
|
||||
//
|
||||
|
@ -1,7 +1,7 @@
|
||||
//
|
||||
// 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
|
||||
// information.
|
||||
@ -261,7 +261,7 @@ _pdfioFilePrintf(pdfio_file_t *pdf, // I - PDF file
|
||||
|
||||
// Format the string...
|
||||
va_start(ap, format);
|
||||
vsnprintf(buffer, sizeof(buffer), format, ap);
|
||||
_pdfio_vsnprintf(pdf, buffer, sizeof(buffer), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
// Write it...
|
||||
|
@ -1131,20 +1131,20 @@ pdfioContentTextMeasure(
|
||||
if (ch < 128)
|
||||
{
|
||||
// ASCII
|
||||
*tempptr++ = ch;
|
||||
*tempptr++ = (char)ch;
|
||||
}
|
||||
else if (ch < 2048)
|
||||
{
|
||||
// 2-byte UTF-8
|
||||
*tempptr++ = 0xc0 | ((ch >> 6) & 0x1f);
|
||||
*tempptr++ = 0x80 | (ch & 0x3f);
|
||||
*tempptr++ = (char)(0xc0 | ((ch >> 6) & 0x1f));
|
||||
*tempptr++ = (char)(0x80 | (ch & 0x3f));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 3-byte UTF-8
|
||||
*tempptr++ = 0xe0 | ((ch >> 12) & 0x0f);
|
||||
*tempptr++ = 0x80 | ((ch >> 6) & 0x3f);
|
||||
*tempptr++ = 0x80 | (ch & 0x3f);
|
||||
*tempptr++ = (char)(0xe0 | ((ch >> 12) & 0x0f));
|
||||
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
|
||||
*tempptr++ = (char)(0x80 | (ch & 0x3f));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1152,7 +1152,7 @@ pdfioContentTextMeasure(
|
||||
s = temp;
|
||||
}
|
||||
|
||||
ttfGetExtents(ttf, size, s, &extents);
|
||||
ttfGetExtents(ttf, (float)size, s, &extents);
|
||||
|
||||
return (extents.right - extents.left);
|
||||
}
|
||||
@ -1642,7 +1642,7 @@ pdfioFileCreateFontObjFromFile(
|
||||
*bufptr++ = (unsigned char)(cmap[i] >> 8);
|
||||
*bufptr++ = (unsigned char)(cmap[i] & 255);
|
||||
|
||||
glyphs[cmap[i]] = i;
|
||||
glyphs[cmap[i]] = (unsigned short)i;
|
||||
if (cmap[i] < min_glyph)
|
||||
min_glyph = cmap[i];
|
||||
if (cmap[i] > max_glyph)
|
||||
@ -1727,9 +1727,9 @@ pdfioFileCreateFontObjFromFile(
|
||||
if ((w_array = pdfioArrayCreate(pdf)) == NULL)
|
||||
goto done;
|
||||
|
||||
for (start = 0, w0 = ttfGetWidth(font, 0), i = 1; i < 65536; start = i, w0 = w1, i ++)
|
||||
for (start = 0, w0 = ttfGetWidth(font, 0), w1 = 0, i = 1; i < 65536; start = i, w0 = w1, i ++)
|
||||
{
|
||||
while (i < 65536 && (w1 = ttfGetWidth(font, i)) == w0)
|
||||
while (i < 65536 && (w1 = ttfGetWidth(font, (int)i)) == w0)
|
||||
i ++;
|
||||
|
||||
if ((i - start) > 1)
|
||||
@ -1750,7 +1750,7 @@ pdfioFileCreateFontObjFromFile(
|
||||
pdfioArrayAppendNumber(temp_array, w0);
|
||||
for (w0 = w1, i ++; i < 65536; w0 = w1, i ++)
|
||||
{
|
||||
if ((w1 = ttfGetWidth(font, i)) == w0 && i < 65535)
|
||||
if ((w1 = ttfGetWidth(font, (int)i)) == w0 && i < 65535)
|
||||
break;
|
||||
|
||||
pdfioArrayAppendNumber(temp_array, w0);
|
||||
|
@ -399,7 +399,7 @@ _pdfioCryptoMakeRandom(uint8_t *buffer, // I - Buffer
|
||||
//
|
||||
|
||||
_pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||
_pdfioCryptoMakeReader(
|
||||
_pdfioCryptoMakeReader(
|
||||
pdfio_file_t *pdf, // I - PDF file
|
||||
pdfio_obj_t *obj, // I - PDF object
|
||||
_pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context
|
||||
@ -409,11 +409,13 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||
uint8_t data[21]; // Key data
|
||||
_pdfio_md5_t md5; // MD5 state
|
||||
uint8_t digest[16]; // MD5 digest value
|
||||
#if PDFIO_OBJ_CRYPT
|
||||
pdfio_array_t *id_array; // Object ID array
|
||||
unsigned char *id_value; // Object ID value
|
||||
size_t id_len; // Length of object ID
|
||||
uint8_t *file_key, // Computed file key to use
|
||||
temp_key[16]; // File key for object
|
||||
uint8_t temp_key[16]; // File key for object
|
||||
#endif // PDFIO_OBJ_CRYPT
|
||||
uint8_t *file_key; // Computed file key to use
|
||||
|
||||
|
||||
PDFIO_DEBUG("_pdfioCryptoMakeReader(pdf=%p, obj=%p(%d), ctx=%p, iv=%p, ivlen=%p(%d))\n", pdf, obj, (int)obj->number, ctx, iv, ivlen, (int)*ivlen);
|
||||
@ -425,6 +427,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
#if PDFIO_OBJ_CRYPT
|
||||
if ((id_array = pdfioDictGetArray(pdfioObjGetDict(obj), "ID")) != NULL)
|
||||
{
|
||||
// Object has its own ID that will get used for encryption...
|
||||
@ -434,6 +437,8 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||
own_user_key[32], // Calculated user key
|
||||
pdf_user_key[32]; // Decrypted user key
|
||||
|
||||
PDFIO_DEBUG("_pdfioCryptoMakeReader: Per-object file ID.\n");
|
||||
|
||||
if ((id_value = pdfioArrayGetBinary(id_array, 0, &id_len)) == NULL)
|
||||
{
|
||||
*ivlen = 0;
|
||||
@ -451,6 +456,8 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||
|
||||
if (memcmp(own_user_key, pdf->user_key, sizeof(own_user_key)))
|
||||
{
|
||||
PDFIO_DEBUG("_pdfioCryptoMakeReader: Not user password, trying owner password.\n");
|
||||
|
||||
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, pdf->password, pdf->owner_key, temp_key);
|
||||
make_user_key(id_value, id_len, own_user_key);
|
||||
|
||||
@ -467,6 +474,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||
file_key = temp_key;
|
||||
}
|
||||
else
|
||||
#endif // PDFIO_OBJ_CRYPT
|
||||
{
|
||||
// Use the default file key...
|
||||
file_key = pdf->file_key;
|
||||
@ -543,7 +551,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||
//
|
||||
|
||||
_pdfio_crypto_cb_t // O - Encryption callback or `NULL` for none
|
||||
_pdfioCryptoMakeWriter(
|
||||
_pdfioCryptoMakeWriter(
|
||||
pdfio_file_t *pdf, // I - PDF file
|
||||
pdfio_obj_t *obj, // I - PDF object
|
||||
_pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context
|
||||
@ -648,6 +656,8 @@ _pdfioCryptoUnlock(
|
||||
revision = (int)pdfioDictGetNumber(encrypt_dict, "R");
|
||||
length = (int)pdfioDictGetNumber(encrypt_dict, "Length");
|
||||
|
||||
PDFIO_DEBUG("_pdfioCryptoUnlock: handler=%p(%s), version=%d, revision=%d, length=%d\n", (void *)handler, handler ? handler : "(null)", version, revision, length);
|
||||
|
||||
if (!handler || strcmp(handler, "Standard"))
|
||||
{
|
||||
_pdfioFileError(pdf, "Unsupported security handler '%s'.", handler ? handler : "(null)");
|
||||
@ -696,6 +706,8 @@ _pdfioCryptoUnlock(
|
||||
}
|
||||
else
|
||||
{
|
||||
PDFIO_DEBUG("_pdfioCryptoUnlock: CFM=\"%s\"\n", cfm);
|
||||
|
||||
if (length < 40 || length > 128)
|
||||
length = 128; // Default to 128 bits
|
||||
|
||||
|
42
pdfio-dict.c
42
pdfio-dict.c
@ -158,6 +158,30 @@ pdfioDictCreate(pdfio_file_t *pdf) // I - PDF file
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '_pdfioDictDecrypt()' - Decrypt the values in a dictionary.
|
||||
//
|
||||
|
||||
bool // O - `true` on success, `false` on error
|
||||
_pdfioDictDecrypt(pdfio_file_t *pdf, // I - PDF file
|
||||
pdfio_obj_t *obj, // I - Object
|
||||
pdfio_dict_t *dict, // I - Dictionary
|
||||
size_t depth) // I - Depth
|
||||
{
|
||||
size_t i; // Looping var
|
||||
_pdfio_pair_t *pair; // Current pair
|
||||
|
||||
|
||||
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
|
||||
{
|
||||
if (strcmp(pair->key, "ID") && !_pdfioValueDecrypt(pdf, obj, &pair->value, depth + 1))
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '_pdfioDictDebug()' - Dump a dictionary to stderr.
|
||||
//
|
||||
@ -402,9 +426,27 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
|
||||
|
||||
|
||||
if (value && value->type == PDFIO_VALTYPE_STRING)
|
||||
{
|
||||
return (value->value.string);
|
||||
}
|
||||
else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
|
||||
{
|
||||
// Convert binary string to regular string...
|
||||
char temp[4096]; // Temporary string
|
||||
|
||||
memcpy(temp, value->value.binary.data, value->value.binary.datalen);
|
||||
temp[value->value.binary.datalen] = '\0';
|
||||
|
||||
free(value->value.binary.data);
|
||||
value->type = PDFIO_VALTYPE_STRING;
|
||||
value->value.string = pdfioStringCreate(dict->pdf, temp);
|
||||
|
||||
return (value->value.string);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
612
pdfio-file.c
612
pdfio-file.c
@ -1,7 +1,7 @@
|
||||
//
|
||||
// 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
|
||||
// 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 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 struct lconv *get_lconv(void);
|
||||
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_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_trailer(pdfio_file_t *pdf);
|
||||
|
||||
@ -119,11 +120,8 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
|
||||
{
|
||||
ret = false;
|
||||
|
||||
if (pdfioObjClose(pdf->info_obj))
|
||||
if (write_pages(pdf))
|
||||
if (write_catalog(pdf))
|
||||
if (write_trailer(pdf))
|
||||
ret = _pdfioFileFlush(pdf);
|
||||
if (pdfioObjClose(pdf->info_obj) && write_pages(pdf) && pdfioObjClose(pdf->root_obj) && write_trailer(pdf))
|
||||
ret = _pdfioFileFlush(pdf);
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
// writes error messages to `stderr`.
|
||||
//
|
||||
@ -184,122 +182,37 @@ pdfioFileCreate(
|
||||
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
|
||||
void *error_cbdata) // 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 fd; // File descriptor
|
||||
|
||||
|
||||
// Range check input...
|
||||
if (!filename)
|
||||
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)
|
||||
// Create the file...
|
||||
if ((fd = open(filename, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, 0666)) < 0)
|
||||
{
|
||||
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_data);
|
||||
snprintf(message, sizeof(message), "Unable to create '%s': %s", filename, strerror(errno));
|
||||
(error_cb)(&temp, message, error_cbdata);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
pdf->filename = strdup(filename);
|
||||
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)
|
||||
if ((pdf = create_common(filename, fd, /*output_cb*/NULL, /*output_cbdata*/NULL, version, media_box, crop_box, error_cb, error_cbdata)) == NULL)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// 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));
|
||||
// Remove the newly created file if we can't create the PDF file object...
|
||||
close(fd);
|
||||
unlink(filename);
|
||||
}
|
||||
|
||||
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.
|
||||
//
|
||||
// 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 and its context pointer which is called whenever data needs to be
|
||||
// callback. The "output_cb" and "output_cbdata" arguments specify the output
|
||||
// callback and its data pointer which is called whenever data needs to be
|
||||
// written:
|
||||
//
|
||||
// ```
|
||||
// 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
|
||||
// }
|
||||
@ -458,7 +371,7 @@ _pdfioFileCreateObj(
|
||||
// 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.
|
||||
//
|
||||
// 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
|
||||
// writes error messages to `stderr`.
|
||||
//
|
||||
@ -469,121 +382,15 @@ _pdfioFileCreateObj(
|
||||
|
||||
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
|
||||
pdfio_output_cb_t output_cb, // I - Output callback function
|
||||
void *output_cbdata, // I - Output callback data
|
||||
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
|
||||
void *error_cbdata) // 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
|
||||
|
||||
|
||||
// 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);
|
||||
return (create_common("output.pdf", /*fd*/-1, output_cb, output_cbdata, version, media_box, crop_box, error_cb, error_cbdata));
|
||||
}
|
||||
|
||||
|
||||
@ -705,13 +512,11 @@ pdfioFileCreateTemporary(
|
||||
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
|
||||
void *error_cbdata) // 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
|
||||
int i, // Looping var
|
||||
fd; // File descriptor
|
||||
const char *tmpdir; // Temporary directory
|
||||
#if _WIN32 || defined(__APPLE__)
|
||||
char tmppath[256]; // Temporary directory path
|
||||
@ -727,31 +532,7 @@ pdfioFileCreateTemporary(
|
||||
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...
|
||||
// Create the temporary PDF file...
|
||||
#if _WIN32
|
||||
if ((tmpdir = getenv("TEMP")) == NULL)
|
||||
{
|
||||
@ -779,98 +560,35 @@ pdfioFileCreateTemporary(
|
||||
tmpdir = "/tmp";
|
||||
#endif // _WIN32
|
||||
|
||||
for (i = 0; i < 1000; i ++)
|
||||
for (i = 0, fd = -1; 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)
|
||||
if ((fd = open(buffer, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC | O_EXCL, 0666)) >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
pdf->filename = strdup(buffer);
|
||||
|
||||
if (i >= 1000)
|
||||
if (fd < 0)
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to create file - %s", strerror(errno));
|
||||
free(pdf->filename);
|
||||
free(pdf);
|
||||
*buffer = '\0';
|
||||
pdfio_file_t temp; // Dummy file
|
||||
char message[8192]; // Message string
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
if ((pdf = create_common(buffer, fd, /*output_cb*/NULL, /*output_cbdata*/NULL, version, media_box, crop_box, error_cb, error_cbdata)) == NULL)
|
||||
{
|
||||
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));
|
||||
// Remove the temporary file if we can't create the PDF file object...
|
||||
close(fd);
|
||||
unlink(buffer);
|
||||
*buffer = '\0';
|
||||
}
|
||||
|
||||
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.
|
||||
//
|
||||
@ -1169,13 +900,13 @@ pdfioFileGetVersion(
|
||||
// This function opens an existing PDF file. The "filename" argument specifies
|
||||
// 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
|
||||
// "security" handlers. The callback returns a password string or `NULL` to
|
||||
// cancel the open. If `NULL` is specified for the callback function and the
|
||||
// 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
|
||||
// writes error messages to `stderr`.
|
||||
//
|
||||
@ -1184,9 +915,10 @@ pdfio_file_t * // O - PDF file
|
||||
pdfioFileOpen(
|
||||
const char *filename, // I - Filename
|
||||
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
|
||||
void *error_data) // I - Error callback data, if any
|
||||
void *error_cbdata) // I - Error callback data, if any
|
||||
{
|
||||
pdfio_file_t *pdf; // PDF file
|
||||
char line[1025], // Line from file
|
||||
@ -1202,8 +934,8 @@ pdfioFileOpen(
|
||||
|
||||
if (!error_cb)
|
||||
{
|
||||
error_cb = _pdfioFileDefaultError;
|
||||
error_data = NULL;
|
||||
error_cb = _pdfioFileDefaultError;
|
||||
error_cbdata = NULL;
|
||||
}
|
||||
|
||||
// Allocate a PDF file structure...
|
||||
@ -1214,14 +946,15 @@ pdfioFileOpen(
|
||||
|
||||
temp.filename = (char *)filename;
|
||||
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);
|
||||
}
|
||||
|
||||
pdf->loc = get_lconv();
|
||||
pdf->filename = strdup(filename);
|
||||
pdf->mode = _PDFIO_MODE_READ;
|
||||
pdf->error_cb = error_cb;
|
||||
pdf->error_data = error_data;
|
||||
pdf->error_data = error_cbdata;
|
||||
pdf->permissions = PDFIO_PERMISSION_ALL;
|
||||
|
||||
// Open the file...
|
||||
@ -1277,7 +1010,7 @@ pdfioFileOpen(
|
||||
|
||||
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;
|
||||
|
||||
return (pdf);
|
||||
@ -1369,7 +1102,7 @@ pdfioFileSetPermissions(
|
||||
if (!pdf)
|
||||
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.");
|
||||
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.
|
||||
//
|
||||
@ -1543,36 +1412,35 @@ get_info_string(pdfio_file_t *pdf, // I - PDF file
|
||||
const char *key) // I - Dictionary key
|
||||
{
|
||||
pdfio_dict_t *dict; // Info dictionary
|
||||
_pdfio_value_t *value; // Value
|
||||
|
||||
|
||||
// Range check input...
|
||||
if (!pdf || !pdf->info_obj || (dict = pdfioObjGetDict(pdf->info_obj)) == NULL || (value = _pdfioDictGetValue(dict, key)) == NULL)
|
||||
if (!pdf || !pdf->info_obj || (dict = pdfioObjGetDict(pdf->info_obj)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
// If we already have a value, return it...
|
||||
if (value->type == PDFIO_VALTYPE_NAME || value->type == PDFIO_VALTYPE_STRING)
|
||||
{
|
||||
return (value->value.string);
|
||||
}
|
||||
else if (value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
|
||||
{
|
||||
// Convert binary string to regular string...
|
||||
char temp[4096]; // Temporary string
|
||||
|
||||
memcpy(temp, value->value.binary.data, value->value.binary.datalen);
|
||||
temp[value->value.binary.datalen] = '\0';
|
||||
|
||||
free(value->value.binary.data);
|
||||
value->type = PDFIO_VALTYPE_STRING;
|
||||
value->value.string = pdfioStringCreate(pdf, temp);
|
||||
|
||||
return (value->value.string);
|
||||
}
|
||||
else
|
||||
return (pdfioDictGetString(dict, key));
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '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)
|
||||
{
|
||||
// Something else that is not a string...
|
||||
return (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);
|
||||
}
|
||||
|
||||
|
||||
@ -2218,30 +2086,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.
|
||||
//
|
||||
|
@ -453,9 +453,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
|
||||
return (false);
|
||||
}
|
||||
|
||||
PDFIO_DEBUG("_pdfioObjLoad: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[0]=0x%02x\n", tb.bufptr, tb.bufend, tb.bufptr[0], tb.bufptr[1]);
|
||||
if (tb.bufptr && tb.bufptr < tb.bufend && (tb.bufptr[0] == 0x0d || tb.bufptr[0] == 0x0a))
|
||||
tb.bufptr ++; // Skip trailing CR or LF after token
|
||||
PDFIO_DEBUG("_pdfioObjLoad: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[1]=0x%02x\n", tb.bufptr, tb.bufend, tb.bufptr[0], tb.bufptr[1]);
|
||||
|
||||
_pdfioTokenFlush(&tb);
|
||||
|
||||
@ -466,6 +464,18 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
|
||||
PDFIO_DEBUG("_pdfioObjLoad: stream_offset=%lu.\n", (unsigned long)obj->stream_offset);
|
||||
}
|
||||
|
||||
// Decrypt as needed...
|
||||
if (obj->pdf->encryption)
|
||||
{
|
||||
PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n");
|
||||
|
||||
if (!_pdfioValueDecrypt(obj->pdf, obj, &obj->value, 0))
|
||||
{
|
||||
PDFIO_DEBUG("_pdfioObjLoad: Failed to decrypt.\n");
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
PDFIO_DEBUG("_pdfioObjLoad: ");
|
||||
PDFIO_DEBUG_VALUE(&obj->value);
|
||||
PDFIO_DEBUG("\n");
|
||||
|
@ -1,7 +1,7 @@
|
||||
//
|
||||
// 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
|
||||
// information.
|
||||
@ -20,6 +20,7 @@
|
||||
# include <errno.h>
|
||||
# include <inttypes.h>
|
||||
# include <fcntl.h>
|
||||
# include <locale.h>
|
||||
# ifdef _WIN32
|
||||
# include <io.h>
|
||||
# include <direct.h>
|
||||
@ -37,9 +38,11 @@
|
||||
# define unlink _unlink
|
||||
# define vsnprintf _vsnprintf
|
||||
# define write _write
|
||||
# define F_OK 00 // POSIX parameters/flags
|
||||
# define W_OK 02
|
||||
# define R_OK 04
|
||||
# ifndef F_OK
|
||||
# define F_OK 00 // POSIX parameters/flags
|
||||
# define W_OK 02
|
||||
# define R_OK 04
|
||||
# endif // !F_OK
|
||||
# define O_RDONLY _O_RDONLY // Map standard POSIX open flags
|
||||
# define O_WRONLY _O_WRONLY
|
||||
# define O_CREAT _O_CREAT
|
||||
@ -224,6 +227,7 @@ typedef struct _pdfio_objmap_s // PDF object map
|
||||
struct _pdfio_file_s // PDF file structure
|
||||
{
|
||||
char *filename; // Filename
|
||||
struct lconv *loc; // Locale data
|
||||
char *version; // Version number
|
||||
pdfio_rect_t media_box, // Default MediaBox value
|
||||
crop_box; // Default CropBox value
|
||||
@ -322,6 +326,10 @@ struct _pdfio_stream_s // Stream
|
||||
// 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 void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
|
||||
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
|
||||
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
|
||||
@ -346,6 +354,7 @@ extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Dig
|
||||
extern bool _pdfioCryptoUnlock(pdfio_file_t *pdf, pdfio_password_cb_t password_cb, void *password_data) _PDFIO_INTERNAL;
|
||||
|
||||
extern void _pdfioDictClear(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
|
||||
extern bool _pdfioDictDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_dict_t *dict, size_t depth) _PDFIO_INTERNAL;
|
||||
extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
|
||||
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
|
||||
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
|
||||
@ -389,6 +398,7 @@ extern void _pdfioTokenPush(_pdfio_token_t *tb, const char *token) _PDFIO_INTER
|
||||
extern bool _pdfioTokenRead(_pdfio_token_t *tb, char *buffer, size_t bufsize);
|
||||
|
||||
extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL;
|
||||
extern bool _pdfioValueDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;
|
||||
extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL;
|
||||
extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL;
|
||||
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;
|
||||
|
@ -1,7 +1,7 @@
|
||||
//
|
||||
// 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
|
||||
// information.
|
||||
@ -687,7 +687,7 @@ pdfioStreamPrintf(
|
||||
|
||||
// Format the string...
|
||||
va_start(ap, format);
|
||||
vsnprintf(buffer, sizeof(buffer), format, ap);
|
||||
_pdfio_vsnprintf(st->pdf, buffer, sizeof(buffer), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
// Write the string...
|
||||
@ -1066,7 +1066,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
|
||||
|
||||
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
|
||||
{
|
||||
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
|
||||
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
|
||||
return (-1);
|
||||
}
|
||||
else if (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out)
|
||||
@ -1127,7 +1127,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
|
||||
|
||||
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
|
||||
{
|
||||
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
|
||||
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
|
||||
return (-1);
|
||||
}
|
||||
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
|
||||
@ -1197,7 +1197,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
|
||||
|
||||
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
|
||||
{
|
||||
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
|
||||
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
|
||||
return (-1);
|
||||
}
|
||||
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
|
||||
|
453
pdfio-string.c
453
pdfio-string.c
@ -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
|
||||
// information.
|
||||
@ -14,7 +14,360 @@
|
||||
// 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
|
||||
const char *s) // I - Nul-terminated string
|
||||
{
|
||||
char *news; // New string
|
||||
char **match; // Matching string
|
||||
char *news; // New string
|
||||
size_t idx; // Index into strings
|
||||
int diff; // Different
|
||||
|
||||
|
||||
PDFIO_DEBUG("pdfioStringCreate(pdf=%p, s=\"%s\")\n", pdf, s);
|
||||
@ -43,8 +397,17 @@ pdfioStringCreate(
|
||||
return (NULL);
|
||||
|
||||
// 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)
|
||||
return (*match);
|
||||
if (pdf->num_strings > 0)
|
||||
{
|
||||
idx = find_string(pdf, s, &diff);
|
||||
if (diff == 0)
|
||||
return (pdf->strings[idx]);
|
||||
}
|
||||
else
|
||||
{
|
||||
idx = 0;
|
||||
diff = -1;
|
||||
}
|
||||
|
||||
// Not already added, so add it...
|
||||
if ((news = strdup(s)) == NULL)
|
||||
@ -65,11 +428,17 @@ pdfioStringCreate(
|
||||
pdf->alloc_strings += 128;
|
||||
}
|
||||
|
||||
// TODO: Change to insertion sort as needed...
|
||||
pdf->strings[pdf->num_strings ++] = news;
|
||||
// Insert the string...
|
||||
if (diff > 0)
|
||||
idx ++;
|
||||
|
||||
if (pdf->num_strings > 1)
|
||||
qsort(pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings);
|
||||
PDFIO_DEBUG("pdfioStringCreate: Inserting \"%s\" at %u\n", news, (unsigned)idx);
|
||||
|
||||
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);
|
||||
|
||||
@ -120,17 +489,67 @@ _pdfioStringIsAllocated(
|
||||
pdfio_file_t *pdf, // I - PDF file
|
||||
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
|
||||
compare_strings(char **a, // I - First string
|
||||
char **b) // I - Second string
|
||||
static size_t // O - Index of match
|
||||
find_string(pdfio_file_t *pdf, // I - PDF file
|
||||
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);
|
||||
}
|
||||
|
@ -447,6 +447,13 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '\r')
|
||||
{
|
||||
// Look for a trailing LF
|
||||
if ((ch = get_char(tb)) != EOF && ch != '\n')
|
||||
tb->bufptr --;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N' : // number
|
||||
|
279
pdfio-value.c
279
pdfio-value.c
@ -1,7 +1,7 @@
|
||||
//
|
||||
// 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
|
||||
// information.
|
||||
@ -10,6 +10,13 @@
|
||||
#include "pdfio-private.h"
|
||||
|
||||
|
||||
//
|
||||
// Local functions...
|
||||
//
|
||||
|
||||
static time_t get_date_time(const char *s);
|
||||
|
||||
|
||||
//
|
||||
// '_pdfioValueCopy()' - Copy a value to a PDF file.
|
||||
//
|
||||
@ -105,6 +112,101 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '_pdfioValueDecrypt()' - Decrypt a value.
|
||||
//
|
||||
|
||||
bool // O - `true` on success, `false` on error
|
||||
_pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
|
||||
pdfio_obj_t *obj, // I - Object
|
||||
_pdfio_value_t *v, // I - Value
|
||||
size_t depth)// I - Depth
|
||||
{
|
||||
_pdfio_crypto_ctx_t ctx; // Decryption context
|
||||
_pdfio_crypto_cb_t cb; // Decryption callback
|
||||
size_t ivlen; // Number of initialization vector bytes
|
||||
uint8_t temp[32768]; // Temporary buffer for decryption
|
||||
size_t templen; // Number of actual data bytes
|
||||
time_t timeval; // Date/time value
|
||||
|
||||
|
||||
if (depth > PDFIO_MAX_DEPTH)
|
||||
{
|
||||
_pdfioFileError(pdf, "Value too deep.");
|
||||
return (false);
|
||||
}
|
||||
|
||||
switch (v->type)
|
||||
{
|
||||
default :
|
||||
// Do nothing
|
||||
break;
|
||||
|
||||
case PDFIO_VALTYPE_ARRAY :
|
||||
return (_pdfioArrayDecrypt(pdf, obj, v->value.array, depth + 1));
|
||||
break;
|
||||
|
||||
case PDFIO_VALTYPE_DICT :
|
||||
return (_pdfioDictDecrypt(pdf, obj, v->value.dict, depth + 1));
|
||||
break;
|
||||
|
||||
case PDFIO_VALTYPE_BINARY :
|
||||
// Decrypt the binary string...
|
||||
if (v->value.binary.datalen > (sizeof(temp) - 32))
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
|
||||
return (false);
|
||||
}
|
||||
|
||||
ivlen = v->value.binary.datalen;
|
||||
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
|
||||
return (false);
|
||||
|
||||
templen = (cb)(&ctx, temp, v->value.binary.data + ivlen, v->value.binary.datalen - ivlen);
|
||||
|
||||
// Copy the decrypted string back to the value and adjust the length...
|
||||
memcpy(v->value.binary.data, temp, templen);
|
||||
|
||||
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
|
||||
v->value.binary.datalen = templen - temp[templen - 1];
|
||||
else
|
||||
v->value.binary.datalen = templen;
|
||||
break;
|
||||
|
||||
case PDFIO_VALTYPE_STRING :
|
||||
// Decrypt regular string...
|
||||
templen = strlen(v->value.string);
|
||||
if (templen > (sizeof(temp) - 33))
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to read encrypted string - too long.");
|
||||
return (false);
|
||||
}
|
||||
|
||||
ivlen = templen;
|
||||
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, (uint8_t *)v->value.string, &ivlen)) == NULL)
|
||||
return (false);
|
||||
|
||||
templen = (cb)(&ctx, temp, (uint8_t *)v->value.string + ivlen, templen - ivlen);
|
||||
temp[templen] = '\0';
|
||||
|
||||
if ((timeval = get_date_time((char *)temp)) != 0)
|
||||
{
|
||||
// Change the type to date...
|
||||
v->type = PDFIO_VALTYPE_DATE;
|
||||
v->value.date = timeval;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy the decrypted string back to the value...
|
||||
v->value.string = pdfioStringCreate(pdf, (char *)temp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// '_pdfioValueDebug()' - Print the contents of a value.
|
||||
//
|
||||
@ -196,6 +298,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
||||
size_t depth) // I - Depth of value
|
||||
{
|
||||
char token[32768]; // Token buffer
|
||||
time_t timeval; // Date/time value
|
||||
#ifdef DEBUG
|
||||
static const char * const valtypes[] =
|
||||
{
|
||||
@ -245,73 +348,10 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
||||
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
|
||||
return (NULL);
|
||||
}
|
||||
else if (!strncmp(token, "(D:", 3))
|
||||
else if (!strncmp(token, "(D:", 3) && (timeval = get_date_time(token + 1)) != 0)
|
||||
{
|
||||
// Possible date value of the form:
|
||||
//
|
||||
// (D:YYYYMMDDhhmmssZ)
|
||||
// (D:YYYYMMDDhhmmss+HH'mm)
|
||||
// (D:YYYYMMDDhhmmss-HH'mm)
|
||||
//
|
||||
int i; // Looping var
|
||||
struct tm dateval; // Date value
|
||||
int offset; // Date offset
|
||||
|
||||
for (i = 3; i < 17; i ++)
|
||||
{
|
||||
if (!isdigit(token[i] & 255))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= 17)
|
||||
{
|
||||
if (token[i] == 'Z')
|
||||
{
|
||||
i ++;
|
||||
}
|
||||
else if (token[i] == '-' || token[i] == '+')
|
||||
{
|
||||
if (isdigit(token[i + 1] & 255) && isdigit(token[i + 2] & 255) && token[i + 3] == '\'' && isdigit(token[i + 4] & 255) && isdigit(token[i + 5] & 255))
|
||||
{
|
||||
i += 6;
|
||||
if (token[i] == '\'')
|
||||
i ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (token[i])
|
||||
{
|
||||
// Just a string...
|
||||
v->type = PDFIO_VALTYPE_STRING;
|
||||
v->value.string = pdfioStringCreate(pdf, token + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Date value...
|
||||
memset(&dateval, 0, sizeof(dateval));
|
||||
|
||||
dateval.tm_year = (token[3] - '0') * 1000 + (token[4] - '0') * 100 + (token[5] - '0') * 10 + token[6] - '0' - 1900;
|
||||
dateval.tm_mon = (token[7] - '0') * 10 + token[8] - '0' - 1;
|
||||
dateval.tm_mday = (token[9] - '0') * 10 + token[10] - '0';
|
||||
dateval.tm_hour = (token[11] - '0') * 10 + token[12] - '0';
|
||||
dateval.tm_min = (token[13] - '0') * 10 + token[14] - '0';
|
||||
dateval.tm_sec = (token[15] - '0') * 10 + token[16] - '0';
|
||||
|
||||
if (token[17] == 'Z')
|
||||
{
|
||||
offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = (token[18] - '0') * 600 + (token[19] - '0') * 60 + (token[20] - '0') * 10 + token[21] - '0';
|
||||
if (token[17] == '-')
|
||||
offset = -offset;
|
||||
}
|
||||
|
||||
v->type = PDFIO_VALTYPE_DATE;
|
||||
v->value.date = mktime(&dateval) + offset;
|
||||
}
|
||||
v->type = PDFIO_VALTYPE_DATE;
|
||||
v->value.date = timeval;
|
||||
}
|
||||
else if (token[0] == '(')
|
||||
{
|
||||
@ -363,36 +403,6 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
||||
|
||||
*dataptr++ = (unsigned char)d;
|
||||
}
|
||||
|
||||
if (obj && pdf->encryption)
|
||||
{
|
||||
// Decrypt the string...
|
||||
_pdfio_crypto_ctx_t ctx; // Decryption context
|
||||
_pdfio_crypto_cb_t cb; // Decryption callback
|
||||
size_t ivlen; // Number of initialization vector bytes
|
||||
uint8_t temp[32768]; // Temporary buffer for decryption
|
||||
size_t templen; // Number of actual data bytes
|
||||
|
||||
if (v->value.binary.datalen > (sizeof(temp) - 32))
|
||||
{
|
||||
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
|
||||
return (false);
|
||||
}
|
||||
|
||||
ivlen = v->value.binary.datalen;
|
||||
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
|
||||
return (false);
|
||||
|
||||
templen = (cb)(&ctx, temp, v->value.binary.data + ivlen, v->value.binary.datalen - ivlen);
|
||||
|
||||
// Copy the decrypted string back to the value and adjust the length...
|
||||
memcpy(v->value.binary.data, temp, templen);
|
||||
|
||||
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
|
||||
v->value.binary.datalen = templen - temp[templen - 1];
|
||||
else
|
||||
v->value.binary.datalen = templen;
|
||||
}
|
||||
}
|
||||
else if (strchr("0123456789-+.", token[0]) != NULL)
|
||||
{
|
||||
@ -487,7 +497,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
|
||||
|
||||
// If we get here, we have a 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"))
|
||||
{
|
||||
@ -728,3 +738,76 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// 'get_date_time()' - Convert PDF date/time value to time_t.
|
||||
//
|
||||
|
||||
static time_t // O - Time in seconds
|
||||
get_date_time(const char *s) // I - PDF date/time value
|
||||
{
|
||||
int i; // Looping var
|
||||
struct tm dateval; // Date value
|
||||
int offset; // Date offset
|
||||
|
||||
|
||||
// Possible date value of the form:
|
||||
//
|
||||
// (D:YYYYMMDDhhmmssZ)
|
||||
// (D:YYYYMMDDhhmmss+HH'mm)
|
||||
// (D:YYYYMMDDhhmmss-HH'mm)
|
||||
//
|
||||
|
||||
for (i = 2; i < 16; i ++)
|
||||
{
|
||||
if (!isdigit(s[i] & 255) || !s[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= 16)
|
||||
{
|
||||
if (s[i] == 'Z')
|
||||
{
|
||||
i ++;
|
||||
}
|
||||
else if (s[i] == '-' || s[i] == '+')
|
||||
{
|
||||
if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255))
|
||||
{
|
||||
i += 6;
|
||||
if (s[i] == '\'')
|
||||
i ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s[i])
|
||||
{
|
||||
// Just a string...
|
||||
return (0);
|
||||
}
|
||||
|
||||
// Date value...
|
||||
memset(&dateval, 0, sizeof(dateval));
|
||||
|
||||
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900;
|
||||
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1;
|
||||
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0';
|
||||
dateval.tm_hour = (s[10] - '0') * 10 + s[11] - '0';
|
||||
dateval.tm_min = (s[12] - '0') * 10 + s[13] - '0';
|
||||
dateval.tm_sec = (s[14] - '0') * 10 + s[15] - '0';
|
||||
|
||||
if (s[16] == 'Z')
|
||||
{
|
||||
offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = (s[17] - '0') * 600 + (s[18] - '0') * 60 + (s[19] - '0') * 10 + s[20] - '0';
|
||||
if (s[16] == '-')
|
||||
offset = -offset;
|
||||
}
|
||||
|
||||
return (mktime(&dateval) + offset);
|
||||
}
|
||||
|
5
pdfio.h
5
pdfio.h
@ -1,7 +1,7 @@
|
||||
//
|
||||
// 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
|
||||
// information.
|
||||
@ -23,7 +23,7 @@ extern "C" {
|
||||
// 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_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _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 const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
||||
extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;
|
||||
|
@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>pdfio_native</id>
|
||||
<title>PDFio Library for VS2019+</title>
|
||||
<version>1.2.0</version>
|
||||
<version>1.3.0</version>
|
||||
<authors>Michael R Sweet</authors>
|
||||
<owners>michaelrsweet</owners>
|
||||
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
|
||||
@ -13,10 +13,10 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>PDFio Library for VS2019+</description>
|
||||
<summary>PDFio is a simple C library for reading and writing PDF files. PDFio is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GNU GPL2-only software.</summary>
|
||||
<copyright>Copyright © 2019-2023 by Michael R Sweet</copyright>
|
||||
<copyright>Copyright © 2019-2024 by Michael R Sweet</copyright>
|
||||
<tags>pdf file native</tags>
|
||||
<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" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>pdfio_native.redist</id>
|
||||
<title>PDFio Library for VS2019+</title>
|
||||
<version>1.2.0</version>
|
||||
<version>1.3.0</version>
|
||||
<authors>Michael R Sweet</authors>
|
||||
<owners>michaelrsweet</owners>
|
||||
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
|
||||
@ -13,7 +13,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>PDFio Library for VS2019+</description>
|
||||
<summary>PDFio is a simple C library for reading and writing PDF files. This package provides the redistributable content for the PDFio library. PDFio is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GNU GPL2-only software.</summary>
|
||||
<copyright>Copyright © 2019-2023 by Michael R Sweet</copyright>
|
||||
<copyright>Copyright © 2019-2024 by Michael R Sweet</copyright>
|
||||
<tags>pdf file native</tags>
|
||||
<dependencies>
|
||||
<dependency id="zlib_native.redist" version="1.2.11" />
|
||||
|
16
runtests.bat
Normal file
16
runtests.bat
Normal file
@ -0,0 +1,16 @@
|
||||
:: Script to run unit test program
|
||||
::
|
||||
:: Usage:
|
||||
::
|
||||
:: .\runtests.bat x64\{Debug|Release}
|
||||
::
|
||||
|
||||
:: Copy dependent DLLs to the named build directory
|
||||
echo Copying DLLs
|
||||
copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Debug\*.dll %1
|
||||
copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Release\*.dll %1
|
||||
|
||||
:: Run unit test program
|
||||
echo Running %1\testpdfio.exe
|
||||
cd %1
|
||||
testpdfio.exe
|
120
testpdfio.c
120
testpdfio.c
@ -1,7 +1,7 @@
|
||||
//
|
||||
// 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
|
||||
// information.
|
||||
@ -16,6 +16,7 @@
|
||||
#include "pdfio-private.h"
|
||||
#include "pdfio-content.h"
|
||||
#include <math.h>
|
||||
#include <locale.h>
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846264338327950288
|
||||
#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_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_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
|
||||
|
||||
|
||||
fprintf(stderr, "testpdfio: Test locale is \"%s\".\n", setlocale(LC_ALL, getenv("LANG")));
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
int i; // Looping var
|
||||
@ -1041,7 +1044,7 @@ do_unit_tests(void)
|
||||
else
|
||||
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;
|
||||
|
||||
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
|
||||
@ -1060,7 +1063,7 @@ do_unit_tests(void)
|
||||
else
|
||||
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;
|
||||
|
||||
close(outfd);
|
||||
@ -1081,7 +1084,7 @@ do_unit_tests(void)
|
||||
else
|
||||
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);
|
||||
|
||||
if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false))
|
||||
@ -1100,7 +1103,7 @@ do_unit_tests(void)
|
||||
else
|
||||
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);
|
||||
|
||||
if (read_unit_file("testpdfio-rc4p.pdf", num_pages, first_image, false))
|
||||
@ -1118,7 +1121,7 @@ do_unit_tests(void)
|
||||
else
|
||||
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);
|
||||
|
||||
if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false))
|
||||
@ -1136,7 +1139,7 @@ do_unit_tests(void)
|
||||
else
|
||||
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);
|
||||
|
||||
if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false))
|
||||
@ -1148,7 +1151,7 @@ do_unit_tests(void)
|
||||
else
|
||||
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);
|
||||
|
||||
if (read_unit_file(temppdf, num_pages, first_image, false))
|
||||
@ -1305,6 +1308,7 @@ read_unit_file(const char *filename, // I - File to read
|
||||
bool is_output) // I - File written with output callback?
|
||||
{
|
||||
pdfio_file_t *pdf; // PDF file
|
||||
pdfio_dict_t *catalog; // Catalog dictionary
|
||||
size_t i; // Looping var
|
||||
const char *s; // String
|
||||
bool error = false; // Error callback data
|
||||
@ -1317,6 +1321,83 @@ read_unit_file(const char *filename, // I - File to read
|
||||
else
|
||||
return (1);
|
||||
|
||||
// Get the root object/catalog dictionary
|
||||
fputs("pdfioFileGetCatalog: ", stdout);
|
||||
if ((catalog = pdfioFileGetCatalog(pdf)) != NULL)
|
||||
{
|
||||
puts("PASS");
|
||||
}
|
||||
else
|
||||
{
|
||||
puts("FAIL (got NULL, expected dictionary)");
|
||||
return (1);
|
||||
}
|
||||
|
||||
// Verify some catalog values...
|
||||
fputs("pdfioDictGetName(PageLayout): ", stdout);
|
||||
if ((s = pdfioDictGetName(catalog, "PageLayout")) != NULL && !strcmp(s, "SinglePage"))
|
||||
{
|
||||
puts("PASS");
|
||||
}
|
||||
else if (s)
|
||||
{
|
||||
printf("FAIL (got '%s', expected 'SinglePage')\n", s);
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
puts("FAIL (got NULL, expected 'SinglePage')");
|
||||
return (1);
|
||||
}
|
||||
|
||||
fputs("pdfioDictGetName(PageLayout): ", stdout);
|
||||
if ((s = pdfioDictGetName(catalog, "PageLayout")) != NULL && !strcmp(s, "SinglePage"))
|
||||
{
|
||||
puts("PASS");
|
||||
}
|
||||
else if (s)
|
||||
{
|
||||
printf("FAIL (got '%s', expected 'SinglePage')\n", s);
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
puts("FAIL (got NULL, expected 'SinglePage')");
|
||||
return (1);
|
||||
}
|
||||
|
||||
fputs("pdfioDictGetName(PageMode): ", stdout);
|
||||
if ((s = pdfioDictGetName(catalog, "PageMode")) != NULL && !strcmp(s, "UseThumbs"))
|
||||
{
|
||||
puts("PASS");
|
||||
}
|
||||
else if (s)
|
||||
{
|
||||
printf("FAIL (got '%s', expected 'UseThumbs')\n", s);
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
puts("FAIL (got NULL, expected 'UseThumbs')");
|
||||
return (1);
|
||||
}
|
||||
|
||||
fputs("pdfioDictGetString(Lang): ", stdout);
|
||||
if ((s = pdfioDictGetString(catalog, "Lang")) != NULL && !strcmp(s, "en"))
|
||||
{
|
||||
puts("PASS");
|
||||
}
|
||||
else if (s)
|
||||
{
|
||||
printf("FAIL (got '%s', expected 'en')\n", s);
|
||||
return (1);
|
||||
}
|
||||
else
|
||||
{
|
||||
puts("FAIL (got NULL, expected 'en')");
|
||||
return (1);
|
||||
}
|
||||
|
||||
// Verify metadata...
|
||||
fputs("pdfioFileGetAuthor: ", stdout);
|
||||
if ((s = pdfioFileGetAuthor(pdf)) != NULL && !strcmp(s, "Michael R Sweet"))
|
||||
@ -3243,6 +3324,7 @@ write_text_test(pdfio_file_t *pdf, // I - PDF file
|
||||
static int // O - Exit status
|
||||
write_unit_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
|
||||
size_t *num_pages, // O - Number of pages
|
||||
size_t *first_image) // O - First image object
|
||||
@ -3252,8 +3334,26 @@ write_unit_file(
|
||||
*gray_jpg, // gray.jpg image
|
||||
*helvetica, // Helvetica font
|
||||
*page; // Page from test PDF file
|
||||
pdfio_dict_t *catalog; // Catalog dictionary
|
||||
|
||||
|
||||
// Get the root object/catalog dictionary
|
||||
fputs("pdfioFileGetCatalog: ", stdout);
|
||||
if ((catalog = pdfioFileGetCatalog(outpdf)) != NULL)
|
||||
{
|
||||
puts("PASS");
|
||||
}
|
||||
else
|
||||
{
|
||||
puts("FAIL (got NULL, expected dictionary)");
|
||||
return (1);
|
||||
}
|
||||
|
||||
// Set some catalog values...
|
||||
pdfioDictSetName(catalog, "PageLayout", "SinglePage");
|
||||
pdfioDictSetName(catalog, "PageMode", "UseThumbs");
|
||||
pdfioDictSetString(catalog, "Lang", "en");
|
||||
|
||||
// Set info values...
|
||||
fputs("pdfioFileGet/SetAuthor: ", stdout);
|
||||
pdfioFileSetAuthor(outpdf, "Michael R Sweet");
|
||||
@ -3437,7 +3537,7 @@ write_unit_file(
|
||||
}
|
||||
|
||||
// Close the new PDF file...
|
||||
fputs("pdfioFileClose(...): ", stdout);
|
||||
printf("pdfioFileClose(\"%s\"): ", outname);
|
||||
if (pdfioFileClose(outpdf))
|
||||
puts("PASS");
|
||||
else
|
||||
|
2
ttf.c
2
ttf.c
@ -1307,7 +1307,7 @@ read_cmap(ttf_t *font) // I - Font
|
||||
{
|
||||
// Use an "obscure indexing trick" (words from the spec, not
|
||||
// mine) to look up the glyph index...
|
||||
temp = segment->idRangeOffset / 2 - segCount + (ch - segment->startCode) + (segment - segments);
|
||||
temp = (int)(segment->idRangeOffset / 2 - segCount + (ch - segment->startCode) + (segment - segments));
|
||||
|
||||
TTF_DEBUG("read_cmap: ch=%d, temp=%d\n", ch, temp);
|
||||
if (temp < 0 || temp >= numGlyphIdArray)
|
||||
|
Reference in New Issue
Block a user