32 Commits

Author SHA1 Message Date
Michael R Sweet
b5b4e80f98 Bump version. 2026-02-15 10:07:57 -05:00
Michael R Sweet
b0795b424d Prep for release. 2026-02-15 10:07:25 -05:00
Michael R Sweet
833398933e Fix conversions of Unicode characters above plane 0 (Issue #159) 2026-02-14 09:52:25 -05:00
Michael R Sweet
5b197b01f8 When a duplicate object is seen in an xref stream, only replace the object if it has a higher generation number (Issue #155) 2026-01-29 13:02:27 -05:00
Michael R Sweet
75185f8be6 Add NULL check to new page contents code. 2026-01-27 19:20:55 -05:00
Michael R Sweet
084c458974 Strip out non-working LZW from the 1.6.x branch. 2026-01-16 20:21:12 -05:00
Michael R Sweet
a9210c114a Fix 0 key length regression (Issue #149) 2026-01-16 20:19:50 -05:00
Michael R Sweet
a16a3130f8 Fix repaired xref stream offsets and support indirect Contents arrays for pages. 2026-01-16 16:57:45 -05:00
Michael R Sweet
635035efd1 Add an xref table offset array to better detect xref table loops (Issue #148) 2026-01-13 18:40:55 -05:00
Michael R Sweet
0bbdd6aa86 Clarify security policy. 2026-01-13 14:11:44 -05:00
Michael R Sweet
b26d143fcc Refactor PDF encryption handler to work with more files. 2026-01-11 13:37:53 -05:00
Michael R Sweet
6ad96ced0b Add missing range checks to pdfioArrayCopy and pdfioDictCopy. 2026-01-08 15:04:29 -05:00
Michael R Sweet
3f581308a1 Fix Clang warning about signedness.
Update Xcode project settings a la master.
2026-01-08 12:42:05 -05:00
Michael R Sweet
8f7d8a58c4 Bump version to 1.6.2. 2026-01-06 11:39:36 -05:00
Michael R Sweet
fb72b141cd Increase PDFIO_MAX_STRING to 128k (Issue #146) 2026-01-06 11:22:12 -05:00
Michael R Sweet
6b59bffd92 Fix an error propagation bug in _pdfioValueCopy (Issue #146) 2026-01-06 11:20:04 -05:00
Michael R Sweet
1832dfcd3d Fix Coverity status badge link. 2026-01-01 10:07:04 -05:00
Michael R Sweet
e7b74e94f7 Fix build badge. 2026-01-01 10:02:00 -05:00
Michael R Sweet
932f237c3f Prep for 1.6.1 release. 2025-12-26 12:09:56 -05:00
Michael R Sweet
09198056a5 Fix the pkg-config file generation. 2025-12-21 20:24:09 -05:00
Michael R Sweet
1c9e675cf6 Bump version. 2025-12-21 19:17:27 -05:00
Michael R Sweet
f16f0c10ed Bump version. 2025-12-21 19:15:29 -05:00
Michael R Sweet
019f0e8003 Support Encrypt dictionaries as well as indirect references (Issue #139) 2025-12-21 19:03:34 -05:00
Michael R Sweet
d5d089c560 Update autoconf cross-compilation files. 2025-12-19 13:40:17 -05:00
Michael R Sweet
23eb44e80b Changelog. 2025-12-02 15:02:25 -05:00
Michael R Sweet
7d412cf855 Add range check to ttfGetExtents. 2025-12-02 15:01:16 -05:00
Michael R Sweet
597c9f8cda Update support for UTF-16 string tokens (Issue #141) 2025-12-02 14:37:18 -05:00
Michael R Sweet
789b74af39 Update CI versions. 2025-12-01 10:37:03 -05:00
Michael R Sweet
5032ce926e Fix some clang warnings.
Update Xcode project to use installed PNG library.
2025-11-10 16:14:21 -05:00
Michael R Sweet
2977b82f35 Fix CI. 2025-11-10 15:41:25 -05:00
Michael R Sweet
b6008bae76 Add input checking to pdfioFileCreateFontObjFromBase. 2025-10-21 11:29:41 -04:00
Michael R Sweet
8459d97ff3 Update markdown 'library'. 2025-10-15 15:03:07 -04:00
31 changed files with 1384 additions and 564 deletions

View File

@@ -13,7 +13,7 @@ jobs:
steps:
- name: Checkout PDFio sources
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Update Build Environment
run: sudo apt-get update --fix-missing -y
- name: Install Prerequisites
@@ -21,7 +21,7 @@ jobs:
- name: Configure PDFio
run: ./configure --enable-debug --enable-sanitizer --enable-maintainer
- name: Build PDFio
run: make "COMMONFLAGS=-g -fsanitize=address"
run: make
- name: Test PDFio
env:
ASAN_OPTIONS: leak_check_at_exit=false
@@ -37,7 +37,7 @@ jobs:
steps:
- name: Checkout PDFio sources
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Configure PDFio
run: ./configure --enable-debug --enable-sanitizer --enable-maintainer
- name: Build PDFio
@@ -53,9 +53,9 @@ jobs:
steps:
- name: Checkout PDFio sources
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.0.2
uses: microsoft/setup-msbuild@v2
- name: Nuget Restore
run: nuget restore pdfio.sln
- name: Build PDFio

View File

@@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout PDFio sources
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive

View File

@@ -8,7 +8,7 @@ jobs:
environment: Coverity
steps:
- name: Checkout PDFio sources
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Update Build Environment
run: sudo apt-get update --fix-missing -y
- name: Install Prerequisites

View File

@@ -2,6 +2,34 @@ Changes in PDFio
================
v1.6.2 - 2026-02-15
-------------------
- Increased the maximum length of a single string to 128k (Issue #146)
- Added missing range checks to `pdfioArrayCopy` and `pdfioDictCopy`.
- Refactored PDF encryption code to fix unlocking with certain files.
- Improved xref table loop detection (Issue #148)
- Changed how duplicate objects are handled in PDF files (Issue #155)
- Fixed xref reconstruction for objects lacking a `Type` value.
- Fixed `pdfioPageOpenStream` for indirect `Contents` arrays.
- Fixed an error propagation bug when reading too-long values (Issue #146)
- Fixed a bug when converting Unicode characters above plane 0 (issue #159)
- Fixed a Clang warning.
v1.6.1 - 2025-12-26
-------------------
- Added missing input checking to `pdfioFileCreateFontObjFromBase` function.
- Updated support for UTF-16 strings (Issue #141)
- Updated Xcode project to use installed PNG library.
- Fixed decryption of PDF files using an Encrypt dictionary instead of an
indirect reference (Issue #139)
- Fixed character range checking in a TTF support function.
- Fixed some clang warnings.
- Fixed the generated pkg-config file.
v1.6.0 - 2025-10-06
-------------------

2
NOTICE
View File

@@ -1,6 +1,6 @@
PDFio - PDF Read/Write Library
Copyright © 2021-2025 by Michael R Sweet.
Copyright © 2021-2026 by Michael R Sweet.
(Optional) Exceptions to the Apache 2.0 License:
================================================

View File

@@ -3,8 +3,8 @@ pdfio - PDF Read/Write Library
![Version](https://img.shields.io/github/v/release/michaelrsweet/pdfio?include_prereleases)
![Apache 2.0](https://img.shields.io/github/license/michaelrsweet/pdfio)
[![Build Status](https://img.shields.io/github/workflow/status/michaelrsweet/pdfio/Build)](https://github.com/michaelrsweet/pdfio/actions/workflows/build.yml)
[![Coverity Scan Status](https://img.shields.io/coverity/scan/22385.svg)](https://scan.coverity.com/projects/michaelrsweet-pdfio)
[![Build Status](https://img.shields.io/github/actions/workflow/status/michaelrsweet/pdfio/build.yml)](https://github.com/michaelrsweet/pdfio/actions/workflows/build.yml)
[![Coverity Scan Status](https://img.shields.io/coverity/scan/23194.svg)](https://scan.coverity.com/projects/michaelrsweet-pdfio)
PDFio is a simple C library for reading and writing PDF files. The primary
goals of PDFio are:
@@ -89,7 +89,7 @@ generates a static library that will be installed under "/usr/local" with:
Legal Stuff
-----------
PDFio is Copyright © 2021-2025 by Michael R Sweet.
PDFio is Copyright © 2021-2026 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

View File

@@ -5,12 +5,40 @@ This file describes how security issues are reported and handled, and what the
expectations are for security issues reported to this project.
What is a Security Bug?
-----------------------
Not every bug is a security bug.
Certain bugs that might be considered security bugs in a program, such as bugs
that lead to a Denial of Service, are *not* considered security bugs simply
because this project *does not provide a service*. Some might argue that, "my
server uses this library and the bug in this library causes a denial of service
for my server", however it is the responsibility of the *server* to protect
against DoS attacks, not a subordinate library, because only the server knows
what is an appropriate use of memory, CPU, time, and other resources.
Similarly, bugs caused by incorrect API usage such as passing `NULL` pointers
where such pointers are not allowed, passing the wrong kinds of pointers or
objects to an API, or using a private API are not security bugs because they
are not caused by an attacker but by the developer.
Finally, bugs that only exist in unreleased (non-production) or inactive code
are not security bugs because they do not affect ordinary users. See the
[Supported Versions](#supported-versions) section below for more information
about what versions of the project are covered by this security policy.
If the bug you've found falls into one of these three categories, please report
the bug as an the ordinary project issue at
<https://github.com/michaelrsweet/pdfio/issues>.
Reporting a Security Bug
------------------------
For the purposes of this project, a security bug is a software defect that
allows a *local or remote user* to gain unauthorized access or privileges on the
host computer or to cause the software to crash. Such defects should be
host computer or to causes the software to crash. Such defects should be
reported to the project security advisory page at
<https://github.com/michaelrsweet/pdfio/security/advisories>.
@@ -18,11 +46,6 @@ Alternately, security bugs can be reported to "security AT msweet.org" using the
PGP public key below. Expect a response within 5 business days. Any proposed
embargo date should be at least 30 days and no more than 90 days in the future.
> *Note:* If you've found a software defect that allows a *program* to gain
> unauthorized access or privileges on the host computer or causes the program
> to crash, that defect should be reported as an ordinary project issue at
> <https://github.com/michaelrsweet/pdfio/issues>.
Responsible Disclosure
----------------------
@@ -68,6 +91,9 @@ example:
1.0b2
1.0rc1
Pre-release code in a Git branch ("master", "v1.6.x", etc.) is similarly *not*
production release code.
PGP Public Key
--------------

78
config.guess vendored
View File

@@ -1,10 +1,10 @@
#! /bin/sh
# Attempt to guess a canonical system name.
# Copyright 1992-2022 Free Software Foundation, Inc.
# Copyright 1992-2025 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale
timestamp='2022-09-17'
timestamp='2025-07-10'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -47,7 +47,7 @@ me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
Usage: $0 [OPTION]
Output the configuration name of the system \`$me' is run on.
Output the configuration name of the system '$me' is run on.
Options:
-h, --help print this help, then exit
@@ -60,13 +60,13 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
Copyright 1992-2022 Free Software Foundation, Inc.
Copyright 1992-2025 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
help="
Try \`$me --help' for more information."
Try '$me --help' for more information."
# Parse command line
while test $# -gt 0 ; do
@@ -102,8 +102,8 @@ GUESS=
# temporary files to be created and, as you can see below, it is a
# headache to deal with in a portable fashion.
# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
# use `HOST_CC' if defined, but it is deprecated.
# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still
# use 'HOST_CC' if defined, but it is deprecated.
# Portable tmp directory creation inspired by the Autoconf team.
@@ -123,7 +123,7 @@ set_cc_for_build() {
dummy=$tmp/dummy
case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
,,) echo "int x;" > "$dummy.c"
for driver in cc gcc c89 c99 ; do
for driver in cc gcc c17 c99 c89 ; do
if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
CC_FOR_BUILD=$driver
break
@@ -155,6 +155,9 @@ Linux|GNU|GNU/*)
set_cc_for_build
cat <<-EOF > "$dummy.c"
#if defined(__ANDROID__)
LIBC=android
#else
#include <features.h>
#if defined(__UCLIBC__)
LIBC=uclibc
@@ -162,6 +165,8 @@ Linux|GNU|GNU/*)
LIBC=dietlibc
#elif defined(__GLIBC__)
LIBC=gnu
#elif defined(__LLVM_LIBC__)
LIBC=llvm
#else
#include <stdarg.h>
/* First heuristic to detect musl libc. */
@@ -169,6 +174,7 @@ Linux|GNU|GNU/*)
LIBC=musl
#endif
#endif
#endif
EOF
cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
eval "$cc_set_libc"
@@ -459,7 +465,7 @@ case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
UNAME_RELEASE=`uname -v`
;;
esac
# Japanese Language versions have a version number like `4.1.3-JL'.
# Japanese Language versions have a version number like '4.1.3-JL'.
SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
GUESS=sparc-sun-sunos$SUN_REL
;;
@@ -628,7 +634,8 @@ EOF
sed 's/^ //' << EOF > "$dummy.c"
#include <sys/systemcfg.h>
main()
int
main ()
{
if (!__power_pc())
exit(1);
@@ -712,7 +719,8 @@ EOF
#include <stdlib.h>
#include <unistd.h>
int main ()
int
main ()
{
#if defined(_SC_KERNEL_BITS)
long bits = sysconf(_SC_KERNEL_BITS);
@@ -904,7 +912,7 @@ EOF
fi
;;
*:FreeBSD:*:*)
UNAME_PROCESSOR=`/usr/bin/uname -p`
UNAME_PROCESSOR=`uname -p`
case $UNAME_PROCESSOR in
amd64)
UNAME_PROCESSOR=x86_64 ;;
@@ -976,7 +984,27 @@ EOF
GUESS=$UNAME_MACHINE-unknown-minix
;;
aarch64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
set_cc_for_build
CPU=$UNAME_MACHINE
LIBCABI=$LIBC
if test "$CC_FOR_BUILD" != no_compiler_found; then
ABI=64
sed 's/^ //' << EOF > "$dummy.c"
#ifdef __ARM_EABI__
#ifdef __ARM_PCS_VFP
ABI=eabihf
#else
ABI=eabi
#endif
#endif
EOF
cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
eval "$cc_set_abi"
case $ABI in
eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;;
esac
fi
GUESS=$CPU-unknown-linux-$LIBCABI
;;
aarch64_be:Linux:*:*)
UNAME_MACHINE=aarch64_be
@@ -1042,6 +1070,15 @@ EOF
k1om:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
kvx:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
kvx:cos:*:*)
GUESS=$UNAME_MACHINE-unknown-cos
;;
kvx:mbr:*:*)
GUESS=$UNAME_MACHINE-unknown-mbr
;;
loongarch32:Linux:*:* | loongarch64:Linux:*:*)
GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
;;
@@ -1197,7 +1234,7 @@ EOF
GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
;;
i*86:OS/2:*:*)
# If we were able to find `uname', then EMX Unix compatibility
# If we were able to find 'uname', then EMX Unix compatibility
# is probably installed.
GUESS=$UNAME_MACHINE-pc-os2-emx
;;
@@ -1338,7 +1375,7 @@ EOF
GUESS=ns32k-sni-sysv
fi
;;
PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
# says <Richard.M.Bartel@ccMail.Census.GOV>
GUESS=i586-unisys-sysv4
;;
@@ -1560,6 +1597,12 @@ EOF
*:Unleashed:*:*)
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;;
x86_64:[Ii]ronclad:*:*|i?86:[Ii]ronclad:*:*)
GUESS=$UNAME_MACHINE-pc-ironclad-mlibc
;;
*:[Ii]ronclad:*:*)
GUESS=$UNAME_MACHINE-unknown-ironclad-mlibc
;;
esac
# Do we have a guess based on uname results?
@@ -1583,6 +1626,7 @@ cat > "$dummy.c" <<EOF
#endif
#endif
#endif
int
main ()
{
#if defined (sony)
@@ -1767,8 +1811,8 @@ fi
exit 1
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# eval: (add-hook 'before-save-hook 'time-stamp nil t)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-format: "%Y-%02m-%02d"
# time-stamp-end: "'"
# End:

953
config.sub vendored

File diff suppressed because it is too large Load Diff

49
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for pdfio 1.6.0.
# Generated by GNU Autoconf 2.71 for pdfio 1.6.2.
#
# 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.6.0'
PACKAGE_STRING='pdfio 1.6.0'
PACKAGE_VERSION='1.6.2'
PACKAGE_STRING='pdfio 1.6.2'
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
PACKAGE_URL='https://www.msweet.org/pdfio'
@@ -653,7 +653,7 @@ WARNINGS
CSFLAGS
LIBPDFIO_STATIC
LIBPDFIO
PKGCONFIG_LIBPNG
PKGCONFIG_REQUIRES_PRIVATE
PKGCONFIG_REQUIRES
PKGCONFIG_LIBS_PRIVATE
PKGCONFIG_LIBS
@@ -1295,7 +1295,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.6.0 to adapt to many kinds of systems.
\`configure' configures pdfio 1.6.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1361,7 +1361,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of pdfio 1.6.0:";;
short | recursive ) echo "Configuration of pdfio 1.6.2:";;
esac
cat <<\_ACEOF
@@ -1460,7 +1460,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
pdfio configure 1.6.0
pdfio configure 1.6.2
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1678,7 +1678,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.6.0, which was
It was created by pdfio $as_me 1.6.2, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -2434,9 +2434,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
PDFIO_VERSION="1.6.0"
PDFIO_VERSION_MAJOR="`echo 1.6.0 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.6.0 | awk -F. '{printf("%d\n",$2);}'`"
PDFIO_VERSION="1.6.2"
PDFIO_VERSION_MAJOR="`echo 1.6.2 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.6.2 | awk -F. '{printf("%d\n",$2);}'`"
@@ -4138,7 +4138,9 @@ fi
PKGCONFIG_CFLAGS="-I\${includedir}"
PKGCONFIG_LIBS="-L\${libdir} -lpdfio"
PKGCONFIG_LIBS_PRIVATE="-lm"
PKGCONFIG_REQUIRES="zlib"
PKBCONFIG_REQUIRES=""
PKGCONFIG_REQUIRES_PRIVATE=""
@@ -4154,6 +4156,7 @@ then :
printf "%s\n" "yes" >&6; }
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
PKGCONFIG_REQUIRES_PRIVATE="zlib"
else $as_nop
@@ -4216,7 +4219,6 @@ then :
fi
PKGCONFIG_REQUIRES=""
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
fi
@@ -4228,9 +4230,6 @@ then :
fi
PKGCONFIG_LIBPNG=""
if test "x$PKGCONFIG" != x -a x$enable_libpng != xno
then :
@@ -4246,8 +4245,16 @@ printf "%s\n" "#define HAVE_LIBPNG 1" >>confdefs.h
CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS"
PKGCONFIG_LIBS_PRIVATE="$($PKGCONFIG --libs libpng16) $PKGCONFIG_LIBS_PRIVATE"
PKGCONFIG_REQUIRES="libpng >= 1.6,$PKGCONFIG_REQUIRES"
if test "x$PKGCONFIG_REQUIRES_PRIVATE" = x
then :
PKGCONFIG_REQUIRES_PRIVATE="libpng >= 1.6"
else $as_nop
PKGCONFIG_REQUIRES_PRIVATE="libpng >= 1.6, $PKGCONFIG_REQUIRES_PRIVATE"
fi
else $as_nop
@@ -4314,6 +4321,8 @@ else $as_nop
LIBPDFIO_STATIC=""
PKGCONFIG_LIBS="$PKGCONFIG_LIBS $PKGCONFIG_LIBS_PRIVATE"
PKGCONFIG_LIBS_PRIVATE=""
PKGCONFIG_REQUIRES="$PKGCONFIG_REQUIRES_PRIVATE"
PKGCONFIG_REQUIRES_PRIVATE=""
fi
@@ -5106,7 +5115,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.6.0, which was
This file was extended by pdfio $as_me 1.6.2, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -5162,7 +5171,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.6.0
pdfio config.status 1.6.2
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"

View File

@@ -21,7 +21,7 @@ AC_PREREQ([2.70])
dnl Package name and version...
AC_INIT([pdfio], [1.6.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
AC_INIT([pdfio], [1.6.2], [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}'`"
@@ -119,11 +119,13 @@ AC_PATH_TOOL([PKGCONFIG], [pkg-config])
PKGCONFIG_CFLAGS="-I\${includedir}"
PKGCONFIG_LIBS="-L\${libdir} -lpdfio"
PKGCONFIG_LIBS_PRIVATE="-lm"
PKGCONFIG_REQUIRES="zlib"
PKBCONFIG_REQUIRES=""
PKGCONFIG_REQUIRES_PRIVATE=""
AC_SUBST([PKGCONFIG_CFLAGS])
AC_SUBST([PKGCONFIG_LIBS])
AC_SUBST([PKGCONFIG_LIBS_PRIVATE])
AC_SUBST([PKGCONFIG_REQUIRES])
AC_SUBST([PKGCONFIG_REQUIRES_PRIVATE])
dnl ZLIB
@@ -132,6 +134,7 @@ AS_IF([$PKGCONFIG --exists zlib], [
AC_MSG_RESULT([yes])
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
PKGCONFIG_REQUIRES_PRIVATE="zlib"
],[
AC_MSG_RESULT([no])
AC_CHECK_HEADER([zlib.h])
@@ -141,16 +144,12 @@ 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"
])
dnl libpng...
AC_ARG_ENABLE([libpng], AS_HELP_STRING([--enable-libpng], [use libpng for pdfioFileCreateImageObjFromFile, default=auto]))
PKGCONFIG_LIBPNG=""
AC_SUBST([PKGCONFIG_LIBPNG])
AS_IF([test "x$PKGCONFIG" != x -a x$enable_libpng != xno], [
AC_MSG_CHECKING([for libpng-1.6.x])
AS_IF([$PKGCONFIG --exists libpng16], [
@@ -158,8 +157,11 @@ AS_IF([test "x$PKGCONFIG" != x -a x$enable_libpng != xno], [
AC_DEFINE([HAVE_LIBPNG], 1, [Have PNG library?])
CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS"
PKGCONFIG_LIBS_PRIVATE="$($PKGCONFIG --libs libpng16) $PKGCONFIG_LIBS_PRIVATE"
PKGCONFIG_REQUIRES="libpng >= 1.6,$PKGCONFIG_REQUIRES"
AS_IF([test "x$PKGCONFIG_REQUIRES_PRIVATE" = x], [
PKGCONFIG_REQUIRES_PRIVATE="libpng >= 1.6"
], [
PKGCONFIG_REQUIRES_PRIVATE="libpng >= 1.6, $PKGCONFIG_REQUIRES_PRIVATE"
])
], [
AC_MSG_RESULT([no]);
AS_IF([test x$enable_libpng = xyes], [
@@ -192,6 +194,8 @@ AS_IF([test x$enable_shared = xyes], [
LIBPDFIO_STATIC=""
PKGCONFIG_LIBS="$PKGCONFIG_LIBS $PKGCONFIG_LIBS_PRIVATE"
PKGCONFIG_LIBS_PRIVATE=""
PKGCONFIG_REQUIRES="$PKGCONFIG_REQUIRES_PRIVATE"
PKGCONFIG_REQUIRES_PRIVATE=""
])
AC_SUBST([LIBPDFIO])

View File

@@ -3,7 +3,7 @@
//
// https://www.msweet.org/mmd
//
// Copyright © 2017-2024 by Michael R Sweet.
// Copyright © 2017-2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -166,7 +166,7 @@ mmdCopyAllText(mmd_t *node) // I - Parent node
char *all = NULL, // String buffer
*allptr = NULL, // Pointer into string buffer
*temp; // Temporary pointer
size_t allsize = 1, // Size of "all" buffer
size_t allsize = 0, // Size of "all" buffer
textlen; // Length of "text" string
mmd_t *current, // Current node
*next; // Next node
@@ -179,6 +179,8 @@ mmdCopyAllText(mmd_t *node) // I - Parent node
if (current->text)
{
// Append this node's text to the string...
long alloff = allptr - all; // Offset within current buffer
textlen = strlen(current->text);
allsize += textlen + (size_t)current->whitespace;
temp = realloc(all, allsize);
@@ -189,8 +191,8 @@ mmdCopyAllText(mmd_t *node) // I - Parent node
return (NULL);
}
allptr = temp + (allptr - all);
all = temp;
allptr = all + alloff;
if (current->whitespace)
*allptr++ = ' ';
@@ -1059,6 +1061,8 @@ mmdLoadIO(mmd_t *root, // I - Root node for document or `NULL` for a new d
break;
else if (line[0] == '>' && *ptr == '>')
memmove(ptr, ptr + 1, strlen(ptr));
DEBUG2_printf(" line=\"%s\"\n", line);
}
mmd_parse_inline(&doc, block, lineptr);
@@ -1493,7 +1497,7 @@ mmd_parse_inline(_mmd_doc_t *doc, // I - Document
for (text = NULL, type = MMD_TYPE_NORMAL_TEXT; *lineptr; lineptr ++)
{
DEBUG2_printf("mmd_parse_inline: lineptr=%p(\"%32.32s...\"), type=%d, text=%p, whitespace=%d\n", lineptr, lineptr, type, text, whitespace);
DEBUG2_printf("mmd_parse_inline: lineptr=%p(\"%s\"), type=%d, text=%p, whitespace=%d\n", lineptr, lineptr, type, text, whitespace);
if (isspace(*lineptr & 255) && type != MMD_TYPE_CODE_TEXT)
{
@@ -2090,6 +2094,8 @@ mmd_read_buffer(_mmd_filebuf_t *file) // I - File buffer
if (file->bufptr && file->bufptr > file->buffer)
{
// Discard previous characters in the buffer.
DEBUG2_printf("mmd_read_buffer: before buffer=\"%s\"\n", file->bufptr);
memmove(file->buffer, file->bufptr, file->bufend - file->bufptr);
file->bufend -= (file->bufptr - file->buffer);
}
@@ -2099,11 +2105,13 @@ mmd_read_buffer(_mmd_filebuf_t *file) // I - File buffer
file->bufend = file->buffer;
}
if ((bytes = (file->cb)(file->cbdata, file->bufend, sizeof(file->buffer) - (size_t)(file->bufend - file->buffer - 1))) > 0)
if ((bytes = (file->cb)(file->cbdata, file->bufend, sizeof(file->buffer) - (size_t)(file->bufend - file->buffer) - 1)) > 0)
file->bufend += bytes;
*(file->bufend) = '\0';
file->bufptr = file->buffer;
DEBUG2_printf("mmd_read_buffer: after buffer=\"%s\"\n", file->buffer);
}
@@ -2161,6 +2169,8 @@ mmd_read_line(_mmd_filebuf_t *file, // I - File buffer
else if (!strchr(file->bufptr, '\n'))
mmd_read_buffer(file);
DEBUG2_printf("mmd_read_line: Returning \"%s\"\n", line);
return (line);
}

View File

@@ -1,7 +1,7 @@
//
// PDF array functions for PDFio.
//
// Copyright © 2021-2024 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -264,6 +264,10 @@ pdfioArrayCopy(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG("pdfioArrayCopy(pdf=%p, a=%p(%p))\n", (void *)pdf, (void *)a, a ? (void *)a->pdf : NULL);
// Range check input...
if (!pdf || !a)
return (NULL);
// Create the new array...
if ((na = pdfioArrayCreate(pdf)) == NULL)
return (NULL);

View File

@@ -1712,12 +1712,23 @@ pdfioFileCreateFontObjFromBase(
pdfio_obj_t *obj; // Font object
if (pdf && pdf->profile >= _PDFIO_PROFILE_PDFA_1A && pdf->profile <= _PDFIO_PROFILE_PDFA_4)
// Range check input...
if (!pdf)
return (NULL);
if (!name)
{
_pdfioFileError(pdf, "No base font name specified.");
return (NULL);
}
if (pdf->profile >= _PDFIO_PROFILE_PDFA_1A && pdf->profile <= _PDFIO_PROFILE_PDFA_4)
{
_pdfioFileError(pdf, "Base fonts are not allowed in PDF/A files; use pdfioFileCreateFontObjFromFile to embed a font.");
return (NULL);
}
// Create a base font object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
return (NULL);
@@ -2691,7 +2702,8 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
png_infop info = NULL; // PNG info pointers
png_bytep *rows = NULL; // PNG row pointers
unsigned char *pixels = NULL; // PNG image data
unsigned i, // Looping var
int i; // Looping var
unsigned y, // Row
color_type, // PNG color mode
width, // Width in columns
height, // Height in lines
@@ -2789,8 +2801,8 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
goto finish_png;
}
for (i = 0; i < height; i ++)
rows[i] = pixels + i * linesize;
for (y = 0; y < height; y ++)
rows[y] = pixels + y * linesize;
// Read the image...
for (i = png_set_interlace_handling(pp); i > 0; i --)
@@ -2804,7 +2816,7 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
// Grab any color space/palette information...
if (png_get_PLTE(pp, info, &palette, &num_palette))
{
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromPalette(dict->pdf, num_palette, (unsigned char *)palette));
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromPalette(dict->pdf, (size_t)num_palette, (unsigned char *)palette));
}
else if (png_get_iCCP(pp, info, &icc_name, /*compression_type*/NULL, &icc_data, &icc_datalen))
{
@@ -3834,14 +3846,12 @@ png_read_func(png_structp pp, // I - PNG pointer
png_bytep data, // I - Read buffer
size_t length) // I - Number of bytes to read
{
int *fd = (int *)png_get_io_ptr(pp);
// Pointer to file descriptor
ssize_t bytes; // Bytes read
int *fd = (int *)png_get_io_ptr(pp);// Pointer to file descriptor
PDFIO_DEBUG("png_read_func(pp=%p, data=%p, length=%lu)\n", (void *)pp, (void *)data, (unsigned long)length);
if ((bytes = read(*fd, data, length)) < (ssize_t)length)
if (read(*fd, data, length) < (ssize_t)length)
png_error(pp, "Unable to read from PNG file.");
}
#endif // HAVE_LIBPNG

View File

@@ -1,7 +1,7 @@
//
// Cryptographic support functions for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -96,12 +96,14 @@ static uint8_t pdf_passpad[32] = // Padding for passwords
// Local functions...
//
static void decrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]);
static void encrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]);
static void make_file_key(pdfio_encryption_t encryption, pdfio_permission_t permissions, const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *owner_key, bool encrypt_metadata, uint8_t file_key[16]);
static void make_owner_key(pdfio_encryption_t encryption, const uint8_t *owner_pad, const uint8_t *user_pad, uint8_t owner_key[32]);
static void decrypt_ou_key(pdfio_encryption_t encryption, const uint8_t *file_key, size_t file_keylen, uint8_t ou_key[32]);
static void encrypt_ou_key(pdfio_encryption_t encryption, const uint8_t *file_key, size_t file_keylen, uint8_t ou_key[32]);
static void make_file_key(pdfio_encryption_t encryption, size_t file_keylen, pdfio_permission_t permissions, const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *owner_key, bool encrypt_metadata, uint8_t file_key[16]);
static void make_owner_key(pdfio_encryption_t encryption, size_t file_keylen, const uint8_t *owner_pad, const uint8_t *user_pad, uint8_t owner_key[32]);
static void make_password_key(pdfio_encryption_t encryption, size_t file_keylen, const uint8_t *pad, uint8_t *key);
static void make_user_key(const unsigned char *file_id, size_t file_idlen, uint8_t user_key[32]);
static void pad_password(const char *password, uint8_t pad[32]);
static bool test_password(pdfio_encryption_t encryption, size_t file_keylen, pdfio_permission_t permisions,const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *user_key, const uint8_t *owner_key, bool encrypt_metadata, uint8_t file_key[16]);
//
@@ -139,6 +141,7 @@ _pdfioCryptoLock(
case PDFIO_ENCRYPTION_AES_128 :
// Create the 128-bit encryption keys...
pad_password(user_password, user_pad);
pdf->file_keylen = 16;
if (!owner_password && user_password && *user_password)
{
@@ -152,18 +155,17 @@ _pdfioCryptoLock(
}
// Compute the owner key...
make_owner_key(encryption, owner_pad, user_pad, pdf->owner_key);
make_owner_key(encryption, pdf->file_keylen, owner_pad, user_pad, pdf->owner_key);
pdf->owner_keylen = 32;
// Generate the encryption key
file_id = pdfioArrayGetBinary(pdf->id_array, 0, &file_idlen);
make_file_key(encryption, permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->encrypt_metadata, pdf->file_key);
pdf->file_keylen = 16;
make_file_key(encryption, pdf->file_keylen, permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->encrypt_metadata, pdf->file_key);
// Generate the user key...
make_user_key(file_id, file_idlen, pdf->user_key);
encrypt_user_key(encryption, pdf->file_key, pdf->user_key);
encrypt_ou_key(encryption, pdf->file_key, pdf->file_keylen, pdf->user_key);
pdf->user_keylen = 32;
// Save everything in the dictionary...
@@ -214,8 +216,9 @@ _pdfioCryptoLock(
pdfioObjClose(pdf->encrypt_obj);
pdf->encryption = encryption;
pdf->permissions = permissions;
pdf->encrypt_dict = dict;
pdf->encryption = encryption;
pdf->permissions = permissions;
return (true);
}
@@ -570,7 +573,6 @@ _pdfioCryptoUnlock(
{
int tries; // Number of tries
const char *password = NULL; // Password to try
pdfio_dict_t *encrypt_dict; // Encrypt objection dictionary
int version, // Version value
revision, // Revision value
length; // Key length value
@@ -590,20 +592,14 @@ _pdfioCryptoUnlock(
_pdfio_value_t *value; // Encrypt dictionary value, if any
// See if we support the type of encryption specified by the Encrypt object
// See if we support the type of encryption specified by the Encrypt
// dictionary...
if ((encrypt_dict = pdfioObjGetDict(pdf->encrypt_obj)) == NULL)
{
_pdfioFileError(pdf, "Unable to get encryption dictionary.");
return (false);
}
handler = pdfioDictGetName(pdf->encrypt_dict, "Filter");
version = (int)pdfioDictGetNumber(pdf->encrypt_dict, "V");
revision = (int)pdfioDictGetNumber(pdf->encrypt_dict, "R");
length = (int)pdfioDictGetNumber(pdf->encrypt_dict, "Length");
handler = pdfioDictGetName(encrypt_dict, "Filter");
version = (int)pdfioDictGetNumber(encrypt_dict, "V");
revision = (int)pdfioDictGetNumber(encrypt_dict, "R");
length = (int)pdfioDictGetNumber(encrypt_dict, "Length");
if ((value = _pdfioDictGetValue(encrypt_dict, "EncryptMetadata")) != NULL && value->type == PDFIO_VALTYPE_BOOLEAN)
if ((value = _pdfioDictGetValue(pdf->encrypt_dict, "EncryptMetadata")) != NULL && value->type == PDFIO_VALTYPE_BOOLEAN)
pdf->encrypt_metadata = value->value.boolean;
else
pdf->encrypt_metadata = true;
@@ -622,9 +618,9 @@ _pdfioCryptoUnlock(
pdfio_dict_t *filter; // Crypt Filter
const char *cfm; // Crypt filter method
stream_filter = pdfioDictGetName(encrypt_dict, "StmF");
string_filter = pdfioDictGetName(encrypt_dict, "StrF");
cf_dict = pdfioDictGetDict(encrypt_dict, "CF");
stream_filter = pdfioDictGetName(pdf->encrypt_dict, "StmF");
string_filter = pdfioDictGetName(pdf->encrypt_dict, "StrF");
cf_dict = pdfioDictGetDict(pdf->encrypt_dict, "CF");
if (!cf_dict)
{
@@ -697,11 +693,16 @@ _pdfioCryptoUnlock(
_pdfioFileError(pdf, "Unsupported encryption V%d R%d.", version, revision);
return (false);
}
else if (length < 40 || length > 128)
{
_pdfioFileError(pdf, "Unsupported key length %d.", length);
return (false);
}
// Grab the remaining values we need to unlock the PDF...
pdf->file_keylen = (size_t)(length / 8);
p = pdfioDictGetNumber(encrypt_dict, "P");
p = pdfioDictGetNumber(pdf->encrypt_dict, "P");
PDFIO_DEBUG("_pdfioCryptoUnlock: P=%.0f\n", p);
if (p < 0x7fffffff) // Handle integers > 2^31-1
pdf->permissions = (pdfio_permission_t)p;
@@ -709,8 +710,8 @@ _pdfioCryptoUnlock(
pdf->permissions = (pdfio_permission_t)(p - 4294967296.0);
PDFIO_DEBUG("_pdfioCryptoUnlock: permissions=%d\n", pdf->permissions);
owner_key = pdfioDictGetBinary(encrypt_dict, "O", &owner_keylen);
user_key = pdfioDictGetBinary(encrypt_dict, "U", &user_keylen);
owner_key = pdfioDictGetBinary(pdf->encrypt_dict, "O", &owner_keylen);
user_key = pdfioDictGetBinary(pdf->encrypt_dict, "U", &user_keylen);
if (!owner_key)
{
@@ -739,7 +740,7 @@ _pdfioCryptoUnlock(
return (false);
}
PDFIO_DEBUG("_pdfioCryptoUnlock: user_key[%d]=%02X%02X%02X%02X...%02X%02X%02X%02X\n", (int)user_keylen, 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: user_key[%d]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", (int)user_keylen, user_key[0], user_key[1], user_key[2], user_key[3], user_key[28], user_key[29], user_key[30], user_key[31]);
memcpy(pdf->user_key, user_key, user_keylen);
pdf->user_keylen = user_keylen;
@@ -750,6 +751,9 @@ _pdfioCryptoUnlock(
return (false);
}
PDFIO_DEBUG("_pdfioCryptoUnlock: P=%d\n", pdf->permissions);
PDFIO_DEBUG("_pdfioCryptoUnlock: file_id(%d)=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", (int)file_idlen, file_id[0], file_id[1], file_id[2], file_id[3], file_id[12], file_id[13], file_id[14], file_id[15]);
// Generate a base hash from known values...
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, pdf_passpad, 32);
@@ -761,56 +765,34 @@ _pdfioCryptoUnlock(
{
if (pdf->encryption <= PDFIO_ENCRYPTION_AES_128)
{
// RC4 and AES-128 encryption...
uint8_t pad[32], // Padded password
file_key[16], // File key
user_pad[32], // Padded user password
own_user_key[32], // Calculated user key
pdf_user_key[32]; // Decrypted user key
padkey[16], // Password key
file_key[16]; // File key
// Pad the supplied password, if any...
pad_password(password, pad);
// Generate keys to see if things match...
PDFIO_DEBUG("_pdfioCryptoUnlock: Trying %02X%02X%02X%02X...%02X%02X%02X%02X\n", pad[0], pad[1], pad[2], pad[3], pad[28], pad[29], pad[30], pad[31]);
PDFIO_DEBUG("_pdfioCryptoUnlock: P=%d\n", pdf->permissions);
PDFIO_DEBUG("_pdfioCryptoUnlock: Fid(%d)=%02X%02X%02X%02X...%02X%02X%02X%02X\n", (int)file_idlen, file_id[0], file_id[1], file_id[2], file_id[3], file_id[12], file_id[13], file_id[14], file_id[15]);
// Test the user password...
PDFIO_DEBUG("_pdfioCryptoUnlock: Trying <%02X%02X%02X%02X...%02X%02X%02X%02X>\n", pad[0], pad[1], pad[2], pad[3], pad[28], pad[29], pad[30], pad[31]);
make_owner_key(pdf->encryption, pad, pdf->owner_key, user_pad);
PDFIO_DEBUG("_pdfioCryptoUnlock: Upad=%02X%02X%02X%02X...%02X%02X%02X%02X\n", user_pad[0], user_pad[1], user_pad[2], user_pad[3], user_pad[28], user_pad[29], user_pad[30], user_pad[31]);
make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->encrypt_metadata, file_key);
PDFIO_DEBUG("_pdfioCryptoUnlock: Fown=%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, own_user_key);
PDFIO_DEBUG("_pdfioCryptoUnlock: U=%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]);
PDFIO_DEBUG("_pdfioCryptoUnlock: Uown=%02X%02X%02X%02X...%02X%02X%02X%02X\n", own_user_key[0], own_user_key[1], own_user_key[2], own_user_key[3], own_user_key[28], own_user_key[29], own_user_key[30], own_user_key[31]);
if (!memcmp(own_user_key, pdf->user_key, sizeof(own_user_key)))
if (test_password(pdf->encryption, pdf->file_keylen, pdf->permissions, file_id, file_idlen, pad, pdf->user_key, pdf->owner_key, pdf->encrypt_metadata, file_key))
{
// Matches!
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
memcpy(pdf->file_key, file_key, pdf->file_keylen);
memcpy(pdf->password, pad, sizeof(pdf->password));
return (true);
}
// Not the owner password, try the user password...
make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, pad, pdf->owner_key, pdf->encrypt_metadata, 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]);
// Test the owner password...
make_password_key(pdf->encryption, pdf->file_keylen, pad, padkey);
make_user_key(file_id, file_idlen, own_user_key);
memcpy(pad, pdf->owner_key, sizeof(pad));
decrypt_ou_key(pdf->encryption, padkey, pdf->file_keylen, pad);
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
decrypt_user_key(pdf->encryption, file_key, pdf_user_key);
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(own_user_key, pdf_user_key, 16))
if (test_password(pdf->encryption, pdf->file_keylen, pdf->permissions, file_id, file_idlen, pad, pdf->user_key, pdf->owner_key, pdf->encrypt_metadata, file_key))
{
// Matches!
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
memcpy(pdf->file_key, file_key, pdf->file_keylen);
memcpy(pdf->password, pad, sizeof(pdf->password));
return (true);
@@ -838,14 +820,15 @@ _pdfioCryptoUnlock(
//
// 'decrypt_user_key()' - Decrypt the user key.
// 'decrypt_ou_key()' - Decrypt the user key.
//
static void
decrypt_user_key(
decrypt_ou_key(
pdfio_encryption_t encryption, // I - Type of encryption
const uint8_t *file_key, // I - File encryption key
uint8_t user_key[32]) // IO - User key
size_t file_keylen, // I - Length of file encryption key
uint8_t ou_key[32]) // IO - Owner/User key
{
size_t i, j; // Looping vars
_pdfio_rc4_t rc4; // RC4 encryption context
@@ -855,38 +838,38 @@ decrypt_user_key(
{
// Encrypt the result once...
_pdfioCryptoRC4Init(&rc4, file_key, 5);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32);
_pdfioCryptoRC4Crypt(&rc4, ou_key, ou_key, 32);
}
else
{
// Encrypt the result 20 times...
uint8_t key[16]; // Current encryption key
for (i = 19; i > 0; i --)
for (i = 20; i > 0;)
{
// XOR each byte in the key with the loop counter...
for (j = 0; j < 16; j ++)
i --;
for (j = 0; j < file_keylen; j ++)
key[j] = (uint8_t)(file_key[j] ^ i);
_pdfioCryptoRC4Init(&rc4, key, 16);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32);
_pdfioCryptoRC4Init(&rc4, key, file_keylen);
_pdfioCryptoRC4Crypt(&rc4, ou_key, ou_key, 32);
}
_pdfioCryptoRC4Init(&rc4, file_key, 16);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32);
}
}
//
// 'encrypt_user_key()' - Encrypt the user key.
// 'encrypt_ou_key()' - Encrypt the owner/user key.
//
static void
encrypt_user_key(
encrypt_ou_key(
pdfio_encryption_t encryption, // I - Type of encryption
const uint8_t *file_key, // I - File encryption key
uint8_t user_key[32]) // IO - User key
size_t file_keylen, // I - Length of file encryption key
uint8_t ou_key[32]) // IO - Owner/User key
{
size_t i, j; // Looping vars
_pdfio_rc4_t rc4; // RC4 encryption context
@@ -896,7 +879,7 @@ encrypt_user_key(
{
// Encrypt the result once...
_pdfioCryptoRC4Init(&rc4, file_key, 5);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32);
_pdfioCryptoRC4Crypt(&rc4, ou_key, ou_key, 32);
}
else
{
@@ -906,11 +889,11 @@ encrypt_user_key(
for (i = 0; i < 20; i ++)
{
// XOR each byte in the key with the loop counter...
for (j = 0; j < 16; j ++)
for (j = 0; j < file_keylen; j ++)
key[j] = (uint8_t)(file_key[j] ^ i);
_pdfioCryptoRC4Init(&rc4, key, 16);
_pdfioCryptoRC4Crypt(&rc4, user_key, user_key, 32);
_pdfioCryptoRC4Init(&rc4, key, file_keylen);
_pdfioCryptoRC4Crypt(&rc4, ou_key, ou_key, 32);
}
}
}
@@ -923,6 +906,7 @@ encrypt_user_key(
static void
make_file_key(
pdfio_encryption_t encryption, // I - Type of encryption
size_t file_keylen, // I - Encryption key length
pdfio_permission_t permissions, // I - File permissions
const unsigned char *file_id, // I - File ID value
size_t file_idlen, // I - Length of file ID
@@ -968,14 +952,14 @@ make_file_key(
for (i = 0; i < 50; i ++)
{
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, digest, 16);
_pdfioCryptoMD5Append(&md5, digest, file_keylen);
_pdfioCryptoMD5Finish(&md5, digest);
}
}
PDFIO_DEBUG("make_file_key: file_key[16]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", digest[0], digest[1], digest[2], digest[3], digest[12], digest[13], digest[14], digest[15]);
memcpy(file_key, digest, 16);
memcpy(file_key, digest, file_keylen);
}
@@ -986,57 +970,57 @@ make_file_key(
static void
make_owner_key(
pdfio_encryption_t encryption, // I - Type of encryption
size_t file_keylen, // I - Length of encryption key
const uint8_t *owner_pad, // I - Padded owner password
const uint8_t *user_pad, // I - Padded user password
uint8_t owner_key[32]) // O - Owner key value
{
size_t i, j; // Looping vars
_pdfio_md5_t md5; // MD5 context
uint8_t digest[16]; // 128-bit MD5 digest
_pdfio_rc4_t rc4; // RC4 encryption context
uint8_t key[16]; // Base encryption key
// Hash the owner password...
make_password_key(encryption, file_keylen, owner_pad, key);
// Copy and encrypt the padded user password...
memcpy(owner_key, user_pad, 32);
decrypt_ou_key(encryption, key, file_keylen, owner_key);
}
//
// 'make_password_key()' - Make a password key.
//
static void
make_password_key(
pdfio_encryption_t encryption, // I - Type of encryption
size_t file_keylen, // I - Length of encryption key
const uint8_t *pad, // I - Padded password
uint8_t *key) // O - Key data
{
size_t i; // Looping var
_pdfio_md5_t md5; // MD5 context
uint8_t digest[16]; // 128-bit MD5 digest
// Hash the padded password...
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, owner_pad, 32);
_pdfioCryptoMD5Append(&md5, pad, 32);
_pdfioCryptoMD5Finish(&md5, digest);
if (encryption != PDFIO_ENCRYPTION_RC4_40)
{
// Hash the hash another 50 times...
for (i = 0; i < 50; i ++)
{
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, digest, 16);
_pdfioCryptoMD5Append(&md5, digest, file_keylen);
_pdfioCryptoMD5Finish(&md5, digest);
}
}
// Copy and encrypt the padded user password...
memcpy(owner_key, user_pad, 32);
if (encryption == PDFIO_ENCRYPTION_RC4_40)
{
// Encrypt once...
_pdfioCryptoRC4Init(&rc4, digest, 5);
_pdfioCryptoRC4Crypt(&rc4, owner_key, owner_key, 32);
}
else
{
// Encrypt 20 times...
uint8_t encrypt_key[16]; // RC4 encryption key
for (i = 20; i > 0;)
{
// XOR each byte in the digest with the loop counter to make a key...
i --;
for (j = 0; j < sizeof(encrypt_key); j ++)
encrypt_key[j] = (uint8_t)(digest[j] ^ i);
_pdfioCryptoRC4Init(&rc4, encrypt_key, sizeof(encrypt_key));
_pdfioCryptoRC4Crypt(&rc4, owner_key, owner_key, 32);
}
}
// Copy the key portion of the hashed password to the key...
memcpy(key, digest, file_keylen);
}
@@ -1091,3 +1075,52 @@ pad_password(const char *password, // I - Password string or `NULL`
if (len < 32)
memcpy(pad + len, pdf_passpad, 32 - len);
}
//
// 'test_password()' - Test a user password...
//
static bool // O - `true` if password is correct, `false` otherwise
test_password(
pdfio_encryption_t encryption, // I - Type of encryption
size_t file_keylen, // I - Length of encryption key
pdfio_permission_t permissions, // I - File permissions
const unsigned char *file_id, // I - File ID value
size_t file_idlen, // I - Length of file ID
const uint8_t *user_pad, // I - Padded user password
const uint8_t *user_key, // I - User key
const uint8_t *owner_key, // I - Owner key
bool encrypt_metadata,
// I - Encrypt metadata?
uint8_t file_key[16]) // O - Encryption key
{
uint8_t pdf_user_key[32]; // Decrypted user key
// Make the file key...
make_file_key(encryption, file_keylen, permissions, file_id, file_idlen, user_pad, owner_key, encrypt_metadata, file_key);
// Decrypt the user key using the file key...
memcpy(pdf_user_key, user_key, sizeof(pdf_user_key));
decrypt_ou_key(encryption, file_key, file_keylen, pdf_user_key);
PDFIO_DEBUG("test_password: pdf_user_key[32]=<%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 (encryption == PDFIO_ENCRYPTION_RC4_40 && !memcmp(pdf_user_key, pdf_passpad, 32))
{
// 40-bit encryption just compares the eecrypted user key matches...
return (true);
}
else
{
// 128-bit encryption needs to match the calculated user key...
uint8_t own_user_key[32]; // Calculated user key
// Calculate what the user key should be...
make_user_key(file_id, file_idlen, own_user_key);
PDFIO_DEBUG("test_password: own_user_key[32]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", own_user_key[0], own_user_key[1], own_user_key[2], own_user_key[3], own_user_key[28], own_user_key[29], own_user_key[30], own_user_key[31]);
// Return whether they match...
return (!memcmp(pdf_user_key, own_user_key, 16));
}
}

View File

@@ -1,7 +1,7 @@
//
// PDF dictionary functions for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -79,6 +79,10 @@ pdfioDictCopy(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG("pdfioDictCopy(pdf=%p, dict=%p(%p))\n", (void *)pdf, (void *)dict, dict ? (void *)dict->pdf : NULL);
// Range check input...
if (!pdf || !dict)
return (NULL);
// Create the new dictionary...
if ((ndict = pdfioDictCreate(pdf)) == NULL)
return (NULL);
@@ -92,6 +96,8 @@ pdfioDictCopy(pdfio_file_t *pdf, // I - PDF file
// Copy and add each of the source dictionary's key/value pairs...
for (i = dict->num_pairs, p = dict->pairs; i > 0; i --, p ++)
{
PDFIO_DEBUG("pdfioDictCopy: key=\"%s\", value.type=%d\n", p->key, p->value.type);
if (!strcmp(p->key, "Length") && p->value.type == PDFIO_VALTYPE_INDIRECT && dict->pdf != pdf)
{
// Don't use indirect stream lengths for copied objects...
@@ -102,15 +108,28 @@ pdfioDictCopy(pdfio_file_t *pdf, // I - PDF file
if (lenobj)
{
if (lenobj->value.type == PDFIO_VALTYPE_NONE)
_pdfioObjLoad(lenobj);
{
if (!_pdfioObjLoad(lenobj))
{
PDFIO_DEBUG("pdfioDictCopy: Unable to copy value of \"%s\", returning NULL.\n", p->key);
return (NULL);
}
}
v.value.number = lenobj->value.value.number;
}
else
{
v.value.number = 0.0;
}
PDFIO_DEBUG("pdfioDictCopy: Length is %.0f.\n", v.value.number);
}
else if (!_pdfioValueCopy(pdf, &v, dict->pdf, &p->value))
{
PDFIO_DEBUG("pdfioDictCopy: Unable to copy value of \"%s\", returning NULL.\n", p->key);
return (NULL); // Let pdfioFileClose do the cleanup...
}
if (_pdfioStringIsAllocated(dict->pdf, p->key))
key = pdfioStringCreate(pdf, p->key);
@@ -464,8 +483,12 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
_pdfio_value_t *value = _pdfioDictGetValue(dict, key);
PDFIO_DEBUG("pdfioDictGetString(dict=%p, key=\"%s\")\n", (void *)dict, key);
PDFIO_DEBUG("pdfioDictGetString: value=%p(type=%d)\n", (void *)value, value ? value->type : 0);
if (value && value->type == PDFIO_VALTYPE_STRING)
{
PDFIO_DEBUG("pdfioDictGetString: Returning \"%s\".\n", value->value.string);
return (value->value.string);
}
else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
@@ -476,6 +499,8 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
if (!(value->value.binary.datalen & 1) && (!memcmp(value->value.binary.data, "\376\377", 2) || !memcmp(value->value.binary.data, "\377\376", 2)))
{
// Copy UTF-16...
PDFIO_DEBUG("pdfioDictGetString: Converting UTF-16 to UTF-8 string.\n");
_pdfio_utf16cpy(temp, value->value.binary.data, value->value.binary.datalen, sizeof(temp));
}
else
@@ -489,10 +514,13 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
value->type = PDFIO_VALTYPE_STRING;
value->value.string = pdfioStringCreate(dict->pdf, temp);
PDFIO_DEBUG("pdfioDictGetString: Returning \"%s\".\n", value->value.string);
return (value->value.string);
}
else
{
PDFIO_DEBUG("pdfioDictGetString: Returning NULL.\n");
return (NULL);
}
}
@@ -641,8 +669,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
}
else if (_pdfioDictGetValue(dict, key + 1))
{
// Issue 118: Discard duplicate key/value pairs, in the future this will
// be a warning message...
// Issue 118: Discard duplicate key/value pairs...
_pdfioValueDelete(&value);
if (_pdfioFileError(pdf, "WARNING: Discarding value for duplicate dictionary key '%s'.", key + 1))
continue;

View File

@@ -1,7 +1,7 @@
//
// PDF file functions for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -2006,6 +2006,9 @@ load_xref(
_pdfio_token_t tb; // Token buffer/stack
off_t line_offset; // Offset to start of line
pdfio_obj_t *pages_obj; // Pages object
size_t num_xrefs = 1; // Number of xref offsets
off_t xrefs[100] = { xref_offset };
// xref offsets
while (!done)
@@ -2223,20 +2226,26 @@ load_xref(
// Create a placeholder for the object in memory...
if ((current = pdfioFileFindObj(pdf, (size_t)number)) != NULL)
{
PDFIO_DEBUG("load_xref: existing object, prev offset=%u\n", (unsigned)current->offset);
PDFIO_DEBUG("load_xref: existing object, prev offset=%u, generation=%u, new generation=%u\n", (unsigned)current->offset, (unsigned)current->generation, (unsigned)generation);
if (w[0] == 0 || buffer[0] == 1)
if (generation > current->generation)
{
// Location of object...
current->offset = (off_t)offset;
}
else if (number != offset)
{
// Object is part of a stream, offset is the object number...
current->offset = 0;
}
// Newer version of an existing object - update the references...
current->generation = generation;
PDFIO_DEBUG("load_xref: new offset=%u\n", (unsigned)current->offset);
if (w[0] == 0 || buffer[0] == 1)
{
// Location of object...
current->offset = (off_t)offset;
}
else if (number != offset)
{
// Object is part of a stream, offset is the object number...
current->offset = 0;
}
PDFIO_DEBUG("load_xref: new offset=%u\n", (unsigned)current->offset);
}
}
if (w[0] > 0 && buffer[0] == 2)
@@ -2283,12 +2292,18 @@ load_xref(
{
// Save the trailer dictionary and grab the root (catalog) and info
// objects...
pdfio_obj_t *encrypt_obj; // Encryption object
pdf->trailer_dict = trailer.value.dict;
pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID");
if ((encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL)
pdf->encrypt_dict = pdfioObjGetDict(encrypt_obj);
else
pdf->encrypt_dict = pdfioDictGetDict(pdf->trailer_dict, "Encrypt");
// If the trailer contains an Encrypt key, try unlocking the file...
if (pdf->encrypt_obj && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
if (pdf->encrypt_dict && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
return (false);
}
@@ -2434,12 +2449,18 @@ load_xref(
{
// Save the trailer dictionary and grab the root (catalog) and info
// objects...
pdfio_obj_t *encrypt_obj; // Encryption object
pdf->trailer_dict = trailer.value.dict;
pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID");
if ((encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL)
pdf->encrypt_dict = pdfioObjGetDict(encrypt_obj);
else
pdf->encrypt_dict = pdfioDictGetDict(pdf->trailer_dict, "Encrypt");
// If the trailer contains an Encrypt key, try unlocking the file...
if (pdf->encrypt_obj && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
if (pdf->encrypt_dict && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
return (false);
}
}
@@ -2459,13 +2480,31 @@ load_xref(
{
done = true;
}
else if (new_offset == xref_offset)
else
{
_pdfioFileError(pdf, "Recursive xref table.");
return (false);
}
// See if we've seen this xref table before...
size_t i; // Looping var
xref_offset = new_offset;
for (i = 0; i < num_xrefs; i ++)
{
if (new_offset == xrefs[i])
{
// Yes, error out...
_pdfioFileError(pdf, "Recursive xref table.");
return (false);
}
}
// No, save it...
if (i >= (sizeof(xrefs) / sizeof(xrefs[0])))
{
// Too many xref tables...
_pdfioFileError(pdf, "Too many xref tables.");
return (false);
}
xrefs[num_xrefs ++] = xref_offset = new_offset;
}
}
// Once we have all of the xref tables loaded, get the important objects and
@@ -2529,7 +2568,7 @@ repair_xref(
pdf->root_obj = NULL;
pdf->info_obj = NULL;
pdf->pages_obj = NULL;
pdf->encrypt_obj = NULL;
pdf->encrypt_dict = NULL;
// Read from the beginning of the file, looking for objects...
if ((line_offset = _pdfioFileSeek(pdf, 0, SEEK_SET)) < 0)
@@ -2588,25 +2627,32 @@ repair_xref(
_pdfioTokenFlush(&tb);
if (type && !strcmp(line, "stream"))
if (!strcmp(line, "stream"))
{
// Possible object or XRef stream...
obj->stream_offset = _pdfioFileTell(pdf);
if (!strcmp(type, "ObjStm") && num_sobjs < (sizeof(sobjs) / sizeof(sobjs[0])))
if (type && !strcmp(type, "ObjStm") && num_sobjs < (sizeof(sobjs) / sizeof(sobjs[0])))
{
PDFIO_DEBUG("repair_xref: Object stream...\n");
sobjs[num_sobjs] = obj;
num_sobjs ++;
}
if (!strcmp(type, "XRef") && !pdf->trailer_dict)
if (type && !strcmp(type, "XRef") && !pdf->trailer_dict)
{
// Save the trailer dictionary...
pdfio_obj_t *encrypt_obj;
// Encryption object
PDFIO_DEBUG("repair_xref: XRef stream...\n");
pdf->trailer_dict = pdfioObjGetDict(obj);
pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID");
if ((encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL)
pdf->encrypt_dict = pdfioObjGetDict(encrypt_obj);
else
pdf->encrypt_dict = pdfioDictGetDict(pdf->trailer_dict, "Encrypt");
}
}
else if (type && !strcmp(line, "endobj"))
@@ -2660,11 +2706,17 @@ repair_xref(
{
// Save the trailer dictionary and grab the root (catalog) and info
// objects...
pdfio_obj_t *encrypt_obj; // Encryption object
PDFIO_DEBUG("repair_xref: Using this trailer dictionary.\n");
pdf->trailer_dict = trailer.value.dict;
pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID");
if ((encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL)
pdf->encrypt_dict = pdfioObjGetDict(encrypt_obj);
else
pdf->encrypt_dict = pdfioDictGetDict(pdf->trailer_dict, "Encrypt");
}
}
@@ -2678,7 +2730,7 @@ repair_xref(
pdf->trailer_dict = backup_trailer;
// If the trailer contains an Encrypt key, try unlocking the file...
if (pdf->encrypt_obj && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
if (pdf->encrypt_dict && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
return (false);
// Load any stream objects...

View File

@@ -1,7 +1,7 @@
//
// PDF object functions for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -69,7 +69,7 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file
ssize_t bytes; // Bytes read
PDFIO_DEBUG("pdfioObjCopy(pdf=%p, srcobj=%p(%p))\n", (void *)pdf, (void *)srcobj, srcobj ? (void *)srcobj->pdf : NULL);
PDFIO_DEBUG("pdfioObjCopy(pdf=%p, srcobj=%p(%u,%p))\n", (void *)pdf, (void *)srcobj, srcobj ? (unsigned)srcobj->number : 0, srcobj ? (void *)srcobj->pdf : NULL);
// Range check input
if (!pdf || !srcobj)
@@ -77,7 +77,10 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file
// Load the object value if needed...
if (srcobj->value.type == PDFIO_VALTYPE_NONE)
_pdfioObjLoad(srcobj);
{
if (!_pdfioObjLoad(srcobj))
return (NULL);
}
// See if we have already mapped this object...
if ((dstobj = _pdfioFileFindMappedObj(pdf, srcobj->pdf, srcobj->number)) != NULL)
@@ -544,6 +547,8 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
pdfio_stream_t *st; // Stream
PDFIO_DEBUG("pdfioObjOpenStream(obj=%p(%lu), decode=%s)\n", (void *)obj, obj ? (unsigned long)obj->number : 0, decode ? "true" : "false");
// Range check input...
if (!obj)
return (NULL);
@@ -563,7 +568,10 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
// No stream if there is no dict or offset to a stream...
if (obj->value.type != PDFIO_VALTYPE_DICT || !obj->stream_offset)
{
PDFIO_DEBUG("pdfioObjOpenStream: value.type=%d, stream_offset=%ld\n", obj->value.type, (long)obj->stream_offset);
return (NULL);
}
// Open the stream...
if ((st = _pdfioStreamOpen(obj, decode)) != NULL)

View File

@@ -1,7 +1,7 @@
//
// PDF page functions for PDFio.
//
// Copyright © 2021-2022 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -105,6 +105,10 @@ pdfioPageOpenStream(
static _pdfio_value_t * // O - Value or NULL on error
get_contents(pdfio_obj_t *page) // I - Page object
{
_pdfio_value_t *contents; // Contents value
pdfio_obj_t *obj; // Contents object
// Range check input...
if (!page)
return (NULL);
@@ -119,5 +123,25 @@ get_contents(pdfio_obj_t *page) // I - Page object
if (page->value.type != PDFIO_VALTYPE_DICT)
return (NULL);
return (_pdfioDictGetValue(page->value.value.dict, "Contents"));
if ((contents = _pdfioDictGetValue(page->value.value.dict, "Contents")) == NULL)
return (NULL);
if (contents->type == PDFIO_VALTYPE_INDIRECT)
{
// See if the indirect object is a stream or an array of indirect object
// references...
if ((obj = pdfioFileFindObj(page->pdf, contents->value.indirect.number)) != NULL)
{
if (obj->value.type == PDFIO_VALTYPE_NONE)
{
if (!_pdfioObjLoad(obj))
return (NULL);
}
if (obj->value.type == PDFIO_VALTYPE_ARRAY)
contents = &(obj->value);
}
}
return (contents);
}

View File

@@ -94,7 +94,7 @@
//
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
# define PDFIO_MAX_STRING 65536 // Maximum length of string
# define PDFIO_MAX_STRING 131072 // Maximum length of string
typedef void (*_pdfio_extfree_t)(void *);
// Extension data free function
@@ -283,7 +283,8 @@ struct _pdfio_file_s // PDF file structure
pdfio_obj_t *root_obj; // Root object/dictionary
pdfio_obj_t *info_obj; // Information object
pdfio_obj_t *pages_obj; // Root pages object
pdfio_obj_t *encrypt_obj; // De/Encryption object/dictionary
pdfio_obj_t *encrypt_obj; // Encryption object (not used for reading)
pdfio_dict_t *encrypt_dict; // De/Encryption dictionary
pdfio_obj_t *cgats001_obj, // CGATS001 ICC profile object
*cp1252_obj, // CP1252 font encoding object
*unicode_obj; // Unicode font encoding object

View File

@@ -1,7 +1,7 @@
//
// PDF stream functions for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -615,11 +615,6 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
st->remaining -= st->flate.avail_in;
}
else if (!strcmp(filter, "LZWDecode"))
{
// LZW compression
st->filter = PDFIO_FILTER_LZW;
}
else
{
// Something else we don't support

View File

@@ -229,7 +229,7 @@ _pdfio_utf16cpy(
else
{
// 4-byte UTF-8
*dstptr++ = (char)(0xe0 | (ch >> 18));
*dstptr++ = (char)(0xf0 | (ch >> 18));
*dstptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
*dstptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*dstptr++ = (char)(0x80 | (ch & 0x3f));

View File

@@ -393,9 +393,18 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
return (false);
}
if (saw_nul)
if ((bufptr - buffer) > 3 && ((bufptr - buffer) & 1) != 0 && (!memcmp(buffer, "(\377\376", 3) || !memcmp(buffer, "(\376\377", 3)))
{
// UTF-16 string, convert to UTF-8...
PDFIO_DEBUG("_pdfioTokenRead: Converting string to UTF-8.\n", stderr);
_pdfio_utf16cpy(buffer + 1, (unsigned char *)buffer + 1, (size_t)(bufptr - buffer - 1), bufsize - 1);
PDFIO_DEBUG("_pdfioTokenRead: Read '%s'.\n", buffer);
return (true);
}
else if (saw_nul)
{
// Convert to a hex (binary) string...
// Contains nul characters, convert to a hex (binary) string...
char *litptr, // Pointer to literal character
*hexptr; // Pointer to hex character
size_t bytes = (size_t)(bufptr - buffer - 1);

View File

@@ -1,7 +1,7 @@
//
// PDF value functions for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -76,7 +76,8 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
return (NULL);
case PDFIO_VALTYPE_ARRAY :
vdst->value.array = pdfioArrayCopy(pdfdst, vsrc->value.array);
if ((vdst->value.array = pdfioArrayCopy(pdfdst, vsrc->value.array)) == NULL)
return (NULL);
break;
case PDFIO_VALTYPE_BINARY :
@@ -97,12 +98,14 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
return (vdst);
case PDFIO_VALTYPE_DICT :
vdst->value.dict = pdfioDictCopy(pdfdst, vsrc->value.dict);
if ((vdst->value.dict = pdfioDictCopy(pdfdst, vsrc->value.dict)) == NULL)
return (NULL);
break;
case PDFIO_VALTYPE_NAME :
case PDFIO_VALTYPE_STRING :
vdst->value.name = pdfioStringCreate(pdfdst, vsrc->value.name);
if ((vdst->value.name = pdfioStringCreate(pdfdst, vsrc->value.name)) == NULL)
return (NULL);
break;
}

View File

@@ -1,7 +1,7 @@
//
// Public header file for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@@ -23,7 +23,7 @@ extern "C" {
// Version numbers...
//
# define PDFIO_VERSION "1.6.0"
# define PDFIO_VERSION "1.6.2"
# define PDFIO_VERSION_MAJOR 1
# define PDFIO_VERSION_MINOR 6

View File

@@ -11,3 +11,4 @@ Cflags: @PKGCONFIG_CFLAGS@
Libs: @PKGCONFIG_LIBS@
Libs.private: @PKGCONFIG_LIBS_PRIVATE@
Requires: @PKGCONFIG_REQUIRES@
Requires.private: @PKGCONFIG_REQUIRES_PRIVATE@

View File

@@ -360,7 +360,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = NO;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
@@ -379,7 +379,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.1.2;
CURRENT_PROJECT_VERSION = 1.6.1;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -390,12 +390,15 @@
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=1",
"HAVE_LIBPNG=1",
"HAVE_TM_GMTOFF=1",
"HAVE_TIMEGM=1",
);
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -409,10 +412,21 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
/usr/local/include,
/opt/local/include,
/opt/homebrew/include,
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = (
"-L/usr/local/lib",
"-L/opt/local/lib",
"-L/opt/homebrew/lib",
"-lpng16",
);
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -446,7 +460,7 @@
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = YES;
CLANG_WARN_IMPLICIT_FALLTHROUGH = NO;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
@@ -465,7 +479,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.1.2;
CURRENT_PROJECT_VERSION = 1.6.1;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
@@ -474,11 +488,16 @@
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"HAVE_LIBPNG=1",
"HAVE_TM_GMTOFF=1",
"HAVE_TIMEGM=1",
);
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -492,9 +511,20 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = (
/usr/local/include,
/opt/local/include,
/opt/homebrew/include,
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
OTHER_LDFLAGS = (
"-L/usr/local/lib",
"-L/opt/local/lib",
"-L/opt/homebrew/lib",
"-lpng16",
);
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;
STRING_CATALOG_GENERATE_SYMBOLS = YES;

View File

@@ -3,7 +3,7 @@
<metadata>
<id>pdfio_native</id>
<title>PDFio Library for VS2019+</title>
<version>1.6.0</version>
<version>1.6.2</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-2025 by Michael R Sweet</copyright>
<copyright>Copyright © 2019-2026 by Michael R Sweet</copyright>
<tags>pdf file native</tags>
<dependencies>
<dependency id="pdfio_native.redist" version="1.6.0" />
<dependency id="pdfio_native.redist" version="1.6.2" />
<dependency id="libpng_native.redist" version="1.6.30" />
<dependency id="zlib_native.redist" version="1.2.11" />
</dependencies>

View File

@@ -3,7 +3,7 @@
<metadata>
<id>pdfio_native.redist</id>
<title>PDFio Library for VS2019+</title>
<version>1.6.0</version>
<version>1.6.2</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-2025 by Michael R Sweet</copyright>
<copyright>Copyright © 2019-2026 by Michael R Sweet</copyright>
<tags>pdf file native</tags>
<dependencies>
<dependency id="libpng_native.redist" version="1.6.30" />

View File

@@ -1,16 +1,20 @@
//
// Test program for PDFio.
//
// Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 2021-2026 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./testpdfio
// ./testpdfio OPTIONS [FILENAME {OBJECT-NUMBER,OUT-FILENAME}] ...
//
// ./testpdfio [--verbose] FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ...
// Options:
//
// --help Show help
// --password PASSWORD Set access password
// --verbose Be verbose
//
#include "pdfio-private.h"
@@ -29,7 +33,7 @@
static int do_crypto_tests(void);
static int do_pdfa_tests(void);
static int do_test_file(const char *filename, int objnum, const char *password, bool verbose);
static int do_test_file(const char *filename, const char *outfile, int objnum, const char *password, bool verbose);
static int do_unit_tests(void);
static int draw_image(pdfio_stream_t *st, const char *name, double x, double y, double w, double h, const char *label);
static bool error_cb(pdfio_file_t *pdf, const char *message, bool *error);
@@ -103,14 +107,18 @@ main(int argc, // I - Number of command-line arguments
else if ((i + 1) < argc && isdigit(argv[i + 1][0] & 255))
{
// filename.pdf object-number
if (do_test_file(argv[i], atoi(argv[i + 1]), password, verbose))
if (do_test_file(argv[i], /*outfile*/NULL, atoi(argv[i + 1]), password, verbose))
ret = 1;
i ++;
}
else if (do_test_file(argv[i], 0, password, verbose))
else
{
ret = 1;
if (do_test_file(argv[i], argv[i + 1], /*objnum*/0, password, verbose))
ret = 1;
if (argv[i + 1])
i ++;
}
}
}
@@ -130,6 +138,7 @@ main(int argc, // I - Number of command-line arguments
return (ret);
}
//
// 'do_crypto_tests()' - Test the various cryptographic functions in PDFio.
//
@@ -429,12 +438,15 @@ do_pdfa_tests(void)
static int // O - Exit status
do_test_file(const char *filename, // I - PDF filename
const char *outfile, // I - Output filename, if any
int objnum, // I - Object number to dump, if any
const char *password, // I - Password for file
bool verbose) // I - Be verbose?
{
int status = 0; // Exit status
bool error = false; // Have we shown an error yet?
pdfio_file_t *pdf; // PDF file
pdfio_file_t *pdf, // PDF file
*outpdf; // Output PDF file, if any
size_t n, // Object/page index
num_objs, // Number of objects
num_pages; // Number of pages
@@ -444,7 +456,12 @@ do_test_file(const char *filename, // I - PDF filename
// Try opening the file...
if (!objnum)
testBegin("%s", filename);
{
if (outfile)
testBegin("%s -> %s", filename, outfile);
else
testBegin("%s", filename);
}
if ((pdf = pdfioFileOpen(filename, password_cb, (void *)password, (pdfio_error_cb_t)error_cb, &error)) != NULL)
{
@@ -458,6 +475,7 @@ do_test_file(const char *filename, // I - PDF filename
if ((obj = pdfioFileFindObj(pdf, (size_t)objnum)) == NULL)
{
puts("Not found.");
pdfioFileClose(pdf);
return (1);
}
@@ -465,6 +483,7 @@ do_test_file(const char *filename, // I - PDF filename
{
_pdfioValueDebug(&obj->value, stdout);
putchar('\n');
pdfioFileClose(pdf);
return (0);
}
@@ -474,6 +493,7 @@ do_test_file(const char *filename, // I - PDF filename
{
_pdfioValueDebug(&obj->value, stdout);
putchar('\n');
pdfioFileClose(pdf);
return (0);
}
@@ -481,6 +501,7 @@ do_test_file(const char *filename, // I - PDF filename
fwrite(buffer, 1, (size_t)bytes, stdout);
pdfioStreamClose(st);
pdfioFileClose(pdf);
return (0);
}
@@ -488,56 +509,80 @@ do_test_file(const char *filename, // I - PDF filename
{
testEnd(true);
// Show basic stats...
num_objs = pdfioFileGetNumObjs(pdf);
num_pages = pdfioFileGetNumPages(pdf);
printf(" PDF %s, %d pages, %d objects.\n", pdfioFileGetVersion(pdf), (int)num_pages, (int)num_objs);
if (verbose)
if (outfile)
{
// Show a summary of each page...
for (n = 0; n < num_pages; n ++)
{
if ((obj = pdfioFileGetPage(pdf, n)) == NULL)
{
printf("%s: Unable to get page #%d.\n", filename, (int)n + 1);
}
else
{
pdfio_rect_t media_box; // MediaBox value
memset(&media_box, 0, sizeof(media_box));
dict = pdfioObjGetDict(obj);
if (!pdfioDictGetRect(dict, "MediaBox", &media_box))
// Copy pages to the output file...
if ((outpdf = pdfioFileCreate(outfile, pdfioFileGetVersion(pdf), /*media_box*/NULL, /*crop_box*/NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
{
for (n = 0, num_pages = pdfioFileGetNumPages(pdf); n < num_pages; n ++)
{
if (!pdfioPageCopy(outpdf, pdfioFileGetPage(pdf, n)))
{
if ((obj = pdfioDictGetObj(dict, "Parent")) != NULL)
{
dict = pdfioObjGetDict(obj);
pdfioDictGetRect(dict, "MediaBox", &media_box);
}
status = 1;
break;
}
}
printf(" Page #%d (obj %d) is %gx%g.\n", (int)n + 1, (int)pdfioObjGetNumber(obj), media_box.x2, media_box.y2);
}
}
pdfioFileClose(outpdf);
}
}
else
{
// Show basic stats...
num_objs = pdfioFileGetNumObjs(pdf);
num_pages = pdfioFileGetNumPages(pdf);
// Show the associated value with each object...
for (n = 0; n < num_objs; n ++)
printf(" PDF %s, %d pages, %d objects.\n", pdfioFileGetVersion(pdf), (int)num_pages, (int)num_objs);
if (verbose)
{
if ((obj = pdfioFileGetObj(pdf, n)) == NULL)
// Show a summary of each page...
for (n = 0; n < num_pages; n ++)
{
printf(" Unable to get object #%d.\n", (int)n);
}
else
{
dict = pdfioObjGetDict(obj);
if ((obj = pdfioFileGetPage(pdf, n)) == NULL)
{
printf("%s: Unable to get page #%d.\n", filename, (int)n + 1);
}
else
{
pdfio_rect_t media_box; // MediaBox value
printf(" %u %u obj dict=%p(%lu pairs)\n", (unsigned)pdfioObjGetNumber(obj), (unsigned)pdfioObjGetGeneration(obj), (void *)dict, dict ? (unsigned long)dict->num_pairs : 0UL);
fputs(" ", stdout);
_pdfioValueDebug(&obj->value, stdout);
putchar('\n');
memset(&media_box, 0, sizeof(media_box));
dict = pdfioObjGetDict(obj);
if (!pdfioDictGetRect(dict, "MediaBox", &media_box))
{
pdfio_obj_t *parent; // Parent object
while ((parent = pdfioDictGetObj(dict, "Parent")) != NULL)
{
dict = pdfioObjGetDict(parent);
if (pdfioDictGetRect(dict, "MediaBox", &media_box))
break;
}
}
printf(" Page #%d (obj %d) is %gx%g.\n", (int)n + 1, (int)pdfioObjGetNumber(obj), media_box.x2, media_box.y2);
}
}
// Show the associated value with each object...
for (n = 0; n < num_objs; n ++)
{
if ((obj = pdfioFileGetObj(pdf, n)) == NULL)
{
printf(" Unable to get object #%d.\n", (int)n);
status = 1;
}
else
{
dict = pdfioObjGetDict(obj);
printf(" %u %u obj dict=%p(%lu pairs)\n", (unsigned)pdfioObjGetNumber(obj), (unsigned)pdfioObjGetGeneration(obj), (void *)dict, dict ? (unsigned long)dict->num_pairs : 0UL);
fputs(" ", stdout);
_pdfioValueDebug(&obj->value, stdout);
putchar('\n');
}
}
}
}
@@ -545,7 +590,7 @@ do_test_file(const char *filename, // I - PDF filename
// Close the file and return success...
pdfioFileClose(pdf);
return (0);
return (status);
}
else
{
@@ -1739,7 +1784,7 @@ token_peek_cb(const char **s, // IO - Test string
static int // O - Exit status
usage(FILE *fp) // I - Output file
{
fputs("Usage: ./testpdfio [OPTIONS] [FILENAME [OBJNUM]] ...\n", fp);
fputs("Usage: ./testpdfio [OPTIONS] [FILENAME {OBJECT-NUM,OUT-FILENAME}] ...\n", fp);
fputs("Options:\n", fp);
fputs(" --help Show program help.\n", fp);
fputs(" --password PASSWORD Set PDF password.\n", fp);
@@ -3486,7 +3531,7 @@ write_pdfa_file(
// Embed a base font, which are not allowed for PDF/A
testBegin("pdfioFileCreateFontObjFromBase(Helvetica)");
if ((font = pdfioFileCreateFontObjFromBase(pdf, "Helvetica")) != NULL)
if (pdfioFileCreateFontObjFromBase(pdf, "Helvetica") != NULL)
{
testEnd(false);
goto done;

2
ttf.c
View File

@@ -608,7 +608,7 @@ ttfGetExtents(
}
// Find its width...
if ((widths = font->widths[ch / 256]) != NULL)
if (ch < TTF_FONT_MAX_CHAR && (widths = font->widths[ch / 256]) != NULL)
{
if (first)
{