mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2025-07-17 22:39:52 +02:00
Compare commits
33 Commits
6906a9a708
...
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 | |||
5d760e7315 | |||
2a85baaf81 | |||
2b92044504 | |||
f4aa951165 | |||
038fd8686b | |||
7084105dc4 | |||
9f06f22281 | |||
cb6b493df6 | |||
2753a82eb9 | |||
ddb8ddff9c | |||
c992b2ba89 | |||
ed723a46dc |
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
|
||||
|
29
CHANGES.md
29
CHANGES.md
@ -2,16 +2,39 @@ 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
|
||||
(Issue #14)
|
||||
- Added `pdfioContentTextMeasure` function (Issue #17)
|
||||
- Added `pdfioContentTextNewLineShow` and `pdfioContentTextNewLineShowf`
|
||||
functions (Issue #24)
|
||||
- Renamed `pdfioContentTextNextLine` to `pdfioContentTextNewLine`.
|
||||
- Now use autoconf to configure the PDFio sources (Issue #54)
|
||||
- Updated the maximum number of object streams in a single file from 4096 to
|
||||
8192 (Issue #58)
|
||||
- Updated the token reading code to protect against some obvious abuses of the
|
||||
PDF format.
|
||||
- Updated the xref reading code to protect against loops.
|
||||
- Updated the object handling code to use a binary insertion algorithm -
|
||||
provides a significant (~800x) improvement in open times.
|
||||
- Fixed handling of encrypted PDFs with per-object file IDs (Issue #42)
|
||||
- Fixed handling of of trailer dictionaries that started immediately after the
|
||||
"trailer" keyword (Issue #58)
|
||||
- Fixed handling of invalid, but common, PDF files with a generation number of
|
||||
65536 in the xref table (Issue #59)
|
||||
|
||||
|
||||
v1.1.4 (December 3, 2023)
|
||||
|
@ -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
|
||||
|
29
configure
vendored
29
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);}'`"
|
||||
|
||||
|
||||
|
||||
@ -4024,13 +4024,13 @@ then :
|
||||
printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
if $PKCONFIG --exists zlib
|
||||
if $PKGCONFIG --exists zlib
|
||||
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}'`"
|
||||
@ -103,10 +103,10 @@ AC_SUBST([PKGCONFIG_REQUIRES])
|
||||
|
||||
dnl ZLIB
|
||||
AC_MSG_CHECKING([for zlib via pkg-config])
|
||||
AS_IF([$PKCONFIG --exists zlib], [
|
||||
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([$PKCONFIG --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.
|
||||
@ -141,7 +141,7 @@ _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
|
||||
*bufend = buffer + bufsize - 1; // Pointer to end of buffer
|
||||
|
||||
|
||||
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend);
|
||||
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer)));
|
||||
|
||||
while (!eol)
|
||||
{
|
||||
@ -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...
|
||||
@ -356,12 +356,12 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
|
||||
off_t offset, // I - Offset
|
||||
int whence) // I - Offset base
|
||||
{
|
||||
PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d)\n", pdf, (long)offset, whence);
|
||||
PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d) pdf->bufpos=%lu\n", pdf, (long)offset, whence, (unsigned long)(pdf ? pdf->bufpos : 0));
|
||||
|
||||
// Adjust offset for relative seeks...
|
||||
if (whence == SEEK_CUR)
|
||||
{
|
||||
offset += pdf->bufpos;
|
||||
offset += pdf->bufpos + (pdf->bufptr - pdf->buffer);
|
||||
whence = SEEK_SET;
|
||||
}
|
||||
|
||||
@ -404,7 +404,7 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
|
||||
return (-1);
|
||||
}
|
||||
|
||||
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld.\n", (long)pdf->bufpos);
|
||||
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld, offset=%lu.\n", (long)pdf->bufpos, (unsigned long)offset);
|
||||
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend);
|
||||
|
||||
pdf->bufpos = offset;
|
||||
|
@ -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,16 +399,23 @@ _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
|
||||
uint8_t *iv, // I - Buffer for initialization vector
|
||||
size_t *ivlen) // IO - Size of initialization vector
|
||||
{
|
||||
uint8_t data[21]; /* Key data */
|
||||
_pdfio_md5_t md5; /* MD5 state */
|
||||
uint8_t digest[16]; /* MD5 digest value */
|
||||
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 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);
|
||||
@ -420,6 +427,59 @@ _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...
|
||||
_pdfio_md5_t md5; // MD5 context
|
||||
uint8_t file_digest[16]; // MD5 digest of file ID and pad
|
||||
uint8_t user_pad[32], // Padded user password
|
||||
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;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
_pdfioCryptoMD5Init(&md5);
|
||||
_pdfioCryptoMD5Append(&md5, pdf_passpad, 32);
|
||||
_pdfioCryptoMD5Append(&md5, id_value, id_len);
|
||||
_pdfioCryptoMD5Finish(&md5, file_digest);
|
||||
|
||||
make_owner_key(pdf->encryption, pdf->password, pdf->owner_key, user_pad);
|
||||
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, user_pad, pdf->owner_key, temp_key);
|
||||
make_user_key(id_value, id_len, own_user_key);
|
||||
|
||||
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);
|
||||
|
||||
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
|
||||
decrypt_user_key(pdf->encryption, temp_key, pdf_user_key);
|
||||
|
||||
if (memcmp(pdf->password, pdf_user_key, 32) && memcmp(own_user_key, pdf_user_key, 16))
|
||||
{
|
||||
*ivlen = 0;
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
file_key = temp_key;
|
||||
}
|
||||
else
|
||||
#endif // PDFIO_OBJ_CRYPT
|
||||
{
|
||||
// Use the default file key...
|
||||
file_key = pdf->file_key;
|
||||
}
|
||||
|
||||
switch (pdf->encryption)
|
||||
{
|
||||
default :
|
||||
@ -428,7 +488,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||
|
||||
case PDFIO_ENCRYPTION_RC4_40 :
|
||||
// Copy the key data for the MD5 hash.
|
||||
memcpy(data, pdf->file_key, sizeof(pdf->file_key));
|
||||
memcpy(data, file_key, 16);
|
||||
data[16] = (uint8_t)obj->number;
|
||||
data[17] = (uint8_t)(obj->number >> 8);
|
||||
data[18] = (uint8_t)(obj->number >> 16);
|
||||
@ -455,7 +515,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
|
||||
|
||||
case PDFIO_ENCRYPTION_RC4_128 :
|
||||
// Copy the key data for the MD5 hash.
|
||||
memcpy(data, pdf->file_key, sizeof(pdf->file_key));
|
||||
memcpy(data, file_key, 16);
|
||||
data[16] = (uint8_t)obj->number;
|
||||
data[17] = (uint8_t)(obj->number >> 8);
|
||||
data[18] = (uint8_t)(obj->number >> 16);
|
||||
@ -491,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
|
||||
@ -596,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)");
|
||||
@ -644,6 +706,8 @@ _pdfioCryptoUnlock(
|
||||
}
|
||||
else
|
||||
{
|
||||
PDFIO_DEBUG("_pdfioCryptoUnlock: CFM=\"%s\"\n", cfm);
|
||||
|
||||
if (length < 40 || length > 128)
|
||||
length = 128; // Default to 128 bits
|
||||
|
||||
@ -769,17 +833,16 @@ _pdfioCryptoUnlock(
|
||||
{
|
||||
// Matches!
|
||||
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
|
||||
memcpy(pdf->password, pad, sizeof(pdf->password));
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Not the owner password, try the user password...
|
||||
*/
|
||||
|
||||
// Not the owner password, try the user password...
|
||||
make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, pad, pdf->owner_key, file_key);
|
||||
PDFIO_DEBUG("_pdfioCryptoUnlock: Fuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]);
|
||||
|
||||
make_user_key(file_id, file_idlen, user_key);
|
||||
make_user_key(file_id, file_idlen, own_user_key);
|
||||
|
||||
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
|
||||
decrypt_user_key(pdf->encryption, file_key, pdf_user_key);
|
||||
@ -787,10 +850,12 @@ _pdfioCryptoUnlock(
|
||||
PDFIO_DEBUG("_pdfioCryptoUnlock: Uuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", user_key[0], user_key[1], user_key[2], user_key[3], user_key[28], user_key[29], user_key[30], user_key[31]);
|
||||
PDFIO_DEBUG("_pdfioCryptoUnlock: Updf=%02X%02X%02X%02X...%02X%02X%02X%02X\n", pdf_user_key[0], pdf_user_key[1], pdf_user_key[2], pdf_user_key[3], pdf_user_key[28], pdf_user_key[29], pdf_user_key[30], pdf_user_key[31]);
|
||||
|
||||
if (!memcmp(pad, pdf_user_key, 32) || !memcmp(user_key, pdf_user_key, 16))
|
||||
if (!memcmp(pad, pdf_user_key, 32) || !memcmp(own_user_key, pdf_user_key, 16))
|
||||
{
|
||||
// Matches!
|
||||
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
|
||||
memcpy(pdf->password, pad, sizeof(pdf->password));
|
||||
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
49
pdfio-dict.c
49
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -518,7 +560,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
|
||||
_pdfio_value_t value; // Dictionary value
|
||||
|
||||
|
||||
PDFIO_DEBUG("_pdfioDictRead(pdf=%p)\n", pdf);
|
||||
PDFIO_DEBUG("_pdfioDictRead(pdf=%p, obj=%p, tb=%p, depth=%lu)\n", pdf, obj, tb, (unsigned long)depth);
|
||||
|
||||
// Create a dictionary and start reading...
|
||||
if ((dict = pdfioDictCreate(pdf)) == NULL)
|
||||
@ -530,6 +572,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
|
||||
if (!strcmp(key, ">>"))
|
||||
{
|
||||
// End of dictionary...
|
||||
PDFIO_DEBUG("_pdfioDictRead: Returning dictionary value...\n");
|
||||
return (dict);
|
||||
}
|
||||
else if (key[0] != '/')
|
||||
@ -548,14 +591,14 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
|
||||
|
||||
if (!_pdfioValueRead(pdf, obj, tb, &value, depth))
|
||||
{
|
||||
_pdfioFileError(pdf, "Missing value for dictionary key.");
|
||||
_pdfioFileError(pdf, "Missing value for dictionary key '%s'.", key + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
|
||||
break;
|
||||
|
||||
// PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key);
|
||||
PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key);
|
||||
}
|
||||
|
||||
// Dictionary is invalid - pdfioFileClose will free the memory, return NULL
|
||||
|
799
pdfio-file.c
799
pdfio-file.c
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -237,7 +241,8 @@ struct _pdfio_file_s // PDF file structure
|
||||
pdfio_permission_t permissions; // Access permissions (encrypted PDF files)
|
||||
uint8_t file_key[16], // File encryption key
|
||||
owner_key[32], // Owner encryption key
|
||||
user_key[32]; // User encryption key
|
||||
user_key[32], // User encryption key
|
||||
password[32]; // Padded password
|
||||
size_t file_keylen, // Length of file encryption key
|
||||
owner_keylen, // Length of owner encryption key
|
||||
user_keylen; // Length of user encryption key
|
||||
@ -265,7 +270,8 @@ struct _pdfio_file_s // PDF file structure
|
||||
alloc_dicts; // Allocated dictionaries
|
||||
pdfio_dict_t **dicts; // Dictionaries
|
||||
size_t num_objs, // Number of objects
|
||||
alloc_objs; // Allocated objects
|
||||
alloc_objs, // Allocated objects
|
||||
last_obj; // Last object added
|
||||
pdfio_obj_t **objs, // Objects
|
||||
*current_obj; // Current object being written/read
|
||||
size_t num_objmaps, // Number of object maps
|
||||
@ -320,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;
|
||||
@ -344,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;
|
||||
@ -387,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);
|
||||
}
|
||||
|
@ -208,9 +208,10 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
*bufend, // End of buffer
|
||||
state = '\0'; // Current state
|
||||
bool saw_nul = false; // Did we see a nul character?
|
||||
size_t count = 0; // Number of whitespace/comment bytes
|
||||
|
||||
|
||||
|
||||
//
|
||||
// "state" is:
|
||||
//
|
||||
// - '\0' for idle
|
||||
@ -229,21 +230,45 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
// Skip leading whitespace...
|
||||
while ((ch = get_char(tb)) != EOF)
|
||||
{
|
||||
count ++;
|
||||
|
||||
if (ch == '%')
|
||||
{
|
||||
// Skip comment
|
||||
PDFIO_DEBUG("_pdfioTokenRead: Skipping comment...\n");
|
||||
while ((ch = get_char(tb)) != EOF)
|
||||
{
|
||||
count ++;
|
||||
|
||||
if (ch == '\n' || ch == '\r')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (count > 2048)
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Comment too long.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!isspace(ch))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (count > 2048)
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Too much whitespace.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == EOF)
|
||||
{
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
|
||||
// Check for delimiters...
|
||||
if (strchr(PDFIO_DELIM_CHARS, ch) != NULL)
|
||||
@ -263,6 +288,8 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
*bufptr++ = (char)ch;
|
||||
}
|
||||
|
||||
PDFIO_DEBUG("_pdfioTokenRead: state='%c'\n", state);
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case '(' : // Literal string
|
||||
@ -354,6 +381,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
{
|
||||
// Out of space
|
||||
_pdfioFileError(tb->pdf, "Token too large.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
@ -361,6 +389,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
if (ch != ')')
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Unterminated string literal.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
|
||||
@ -380,6 +409,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
{
|
||||
// Out of space...
|
||||
_pdfioFileError(tb->pdf, "Token too large.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
|
||||
@ -413,9 +443,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
{
|
||||
// Out of space...
|
||||
_pdfioFileError(tb->pdf, "Token too large.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '\r')
|
||||
{
|
||||
// Look for a trailing LF
|
||||
if ((ch = get_char(tb)) != EOF && ch != '\n')
|
||||
tb->bufptr --;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N' : // number
|
||||
@ -424,6 +462,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
if (!isdigit(ch) && ch != '.')
|
||||
{
|
||||
// End of number...
|
||||
PDFIO_DEBUG("_pdfioTokenRead: End of number with ch=0x%02x\n", ch);
|
||||
tb->bufptr --;
|
||||
break;
|
||||
}
|
||||
@ -436,6 +475,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
{
|
||||
// Out of space...
|
||||
_pdfioFileError(tb->pdf, "Token too large.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
@ -462,12 +502,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
if (!isxdigit(tch & 255))
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Bad # escape in name.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
else if (isdigit(tch))
|
||||
{
|
||||
ch = ((ch & 255) << 4) | (tch - '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
ch = ((ch & 255) << 4) | (tolower(tch) - 'a' + 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,9 +524,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
{
|
||||
// Out of space
|
||||
_pdfioFileError(tb->pdf, "Token too large.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
|
||||
if (bufptr == (buffer + 1))
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Empty name.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
break;
|
||||
|
||||
case '<' : // Potential hex string
|
||||
@ -501,9 +554,12 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
else if (!isspace(ch & 255) && !isxdigit(ch & 255))
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Syntax error: '<%c'", ch);
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
|
||||
count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (isxdigit(ch))
|
||||
@ -512,25 +568,39 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
{
|
||||
// Hex digit
|
||||
*bufptr++ = (char)ch;
|
||||
count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too large
|
||||
_pdfioFileError(tb->pdf, "Token too large.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
else if (!isspace(ch))
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Invalid hex string character '%c'.", ch);
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
else
|
||||
{
|
||||
count ++;
|
||||
if (count > 2048)
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Too much whitespace.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
while ((ch = get_char(tb)) != EOF && ch != '>');
|
||||
|
||||
if (ch == EOF)
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Unterminated hex string.");
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
break;
|
||||
@ -543,6 +613,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
else
|
||||
{
|
||||
_pdfioFileError(tb->pdf, "Syntax error: '>%c'.", ch);
|
||||
*bufptr = '\0';
|
||||
return (false);
|
||||
}
|
||||
break;
|
||||
@ -550,7 +621,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
|
||||
*bufptr = '\0';
|
||||
|
||||
// PDFIO_DEBUG("_pdfioTokenRead: Read '%s'.\n", buffer);
|
||||
PDFIO_DEBUG("_pdfioTokenRead: Read '%s'.\n", buffer);
|
||||
|
||||
return (bufptr > buffer);
|
||||
}
|
||||
@ -587,7 +658,6 @@ get_char(_pdfio_token_t *tb) // I - Token buffer
|
||||
tb->bufptr = tb->buffer;
|
||||
tb->bufend = tb->buffer + bytes;
|
||||
|
||||
#if 0
|
||||
#ifdef DEBUG
|
||||
unsigned char *ptr; // Pointer into buffer
|
||||
|
||||
@ -601,7 +671,6 @@ get_char(_pdfio_token_t *tb) // I - Token buffer
|
||||
}
|
||||
PDFIO_DEBUG("'\n");
|
||||
#endif // DEBUG
|
||||
#endif // 0
|
||||
}
|
||||
|
||||
// Return the next character...
|
||||
|
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;
|
||||
|
@ -9,5 +9,5 @@ Version: @PDFIO_VERSION@
|
||||
URL: https://www.msweet.org/pdfio
|
||||
Requires: @PKGCONFIG_REQUIRES@
|
||||
Libs: @PKGCONFIG_LIBS@
|
||||
Libs.private: @PKGCONFIG_LIBS_PRIVATE@ -lm
|
||||
Libs.private: @PKGCONFIG_LIBS_PRIVATE@
|
||||
Cflags: @PKGCONFIG_CFLAGS@
|
||||
|
@ -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