mirror of
https://github.com/michaelrsweet/pdfio.git
synced 2026-01-18 17:50:09 +01:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
084c458974 | ||
|
|
a9210c114a | ||
|
|
a16a3130f8 | ||
|
|
635035efd1 | ||
|
|
0bbdd6aa86 | ||
|
|
b26d143fcc | ||
|
|
6ad96ced0b | ||
|
|
3f581308a1 | ||
|
|
8f7d8a58c4 | ||
|
|
fb72b141cd | ||
|
|
6b59bffd92 | ||
|
|
1832dfcd3d | ||
|
|
e7b74e94f7 |
13
CHANGES.md
13
CHANGES.md
@@ -2,6 +2,19 @@ Changes in PDFio
|
||||
================
|
||||
|
||||
|
||||
v1.6.2 - YYYY-MM-DD
|
||||
-------------------
|
||||
|
||||
- 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)
|
||||
- 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 Clang warning.
|
||||
|
||||
|
||||
v1.6.1 - 2025-12-26
|
||||
-------------------
|
||||
|
||||
|
||||
2
NOTICE
2
NOTICE
@@ -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:
|
||||
================================================
|
||||
|
||||
@@ -3,8 +3,8 @@ pdfio - PDF Read/Write Library
|
||||
|
||||

|
||||

|
||||
[](https://github.com/michaelrsweet/pdfio/actions/workflows/build.yml)
|
||||
[](https://scan.coverity.com/projects/michaelrsweet-pdfio)
|
||||
[](https://github.com/michaelrsweet/pdfio/actions/workflows/build.yml)
|
||||
[](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
|
||||
|
||||
38
SECURITY.md
38
SECURITY.md
@@ -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
|
||||
--------------
|
||||
|
||||
24
configure
vendored
24
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.6.1.
|
||||
# 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.1'
|
||||
PACKAGE_STRING='pdfio 1.6.1'
|
||||
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'
|
||||
|
||||
@@ -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.1 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.1:";;
|
||||
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.1
|
||||
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.1, 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.1"
|
||||
PDFIO_VERSION_MAJOR="`echo 1.6.1 | awk -F. '{print $1}'`"
|
||||
PDFIO_VERSION_MINOR="`echo 1.6.1 | 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);}'`"
|
||||
|
||||
|
||||
|
||||
@@ -5115,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.1, 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
|
||||
@@ -5171,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.1
|
||||
pdfio config.status 1.6.2
|
||||
configured by $0, generated by GNU Autoconf 2.71,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ AC_PREREQ([2.70])
|
||||
|
||||
|
||||
dnl Package name and version...
|
||||
AC_INIT([pdfio], [1.6.1], [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}'`"
|
||||
|
||||
@@ -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);
|
||||
|
||||
233
pdfio-crypto.c
233
pdfio-crypto.c
@@ -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...
|
||||
@@ -691,6 +693,11 @@ _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);
|
||||
@@ -733,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;
|
||||
@@ -744,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);
|
||||
@@ -755,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);
|
||||
@@ -832,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
|
||||
@@ -849,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
|
||||
@@ -890,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
|
||||
{
|
||||
@@ -900,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -917,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
|
||||
@@ -962,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -980,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1085,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));
|
||||
}
|
||||
}
|
||||
|
||||
26
pdfio-dict.c
26
pdfio-dict.c
@@ -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);
|
||||
@@ -650,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;
|
||||
|
||||
39
pdfio-file.c
39
pdfio-file.c
@@ -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)
|
||||
@@ -2471,13 +2474,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
|
||||
@@ -2600,19 +2621,19 @@ 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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
25
pdfio-page.c
25
pdfio-page.c
@@ -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,24 @@ 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"));
|
||||
contents = _pdfioDictGetValue(page->value.value.dict, "Contents");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -397,7 +397,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
|
||||
{
|
||||
// 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, bufptr - buffer - 1, bufsize - 1);
|
||||
_pdfio_utf16cpy(buffer + 1, (unsigned char *)buffer + 1, (size_t)(bufptr - buffer - 1), bufsize - 1);
|
||||
|
||||
PDFIO_DEBUG("_pdfioTokenRead: Read '%s'.\n", buffer);
|
||||
return (true);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -398,7 +398,7 @@
|
||||
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;
|
||||
@@ -460,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;
|
||||
@@ -497,7 +497,7 @@
|
||||
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;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>pdfio_native</id>
|
||||
<title>PDFio Library for VS2019+</title>
|
||||
<version>1.6.1</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.1" />
|
||||
<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>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<metadata>
|
||||
<id>pdfio_native.redist</id>
|
||||
<title>PDFio Library for VS2019+</title>
|
||||
<version>1.6.1</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" />
|
||||
|
||||
151
testpdfio.c
151
testpdfio.c
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user