73 Commits

Author SHA1 Message Date
1d5310a5f3 Bump version to 1.0b2. 2021-11-07 11:15:35 -05:00
1e33878506 Fix conversion of nul-containing strings to binary.
Move key length checks to a common place.
2021-11-02 09:12:43 -04:00
af07f64bc3 Fix 'make test'. 2021-11-02 07:50:14 -04:00
2f0d622873 Save work on resolving PDF loading issues with random PDFs using different encryption methods and line endings. 2021-11-01 21:30:46 -04:00
6432187dea Fix sporadic test suite failures caused by greedy whitespace removal in token
reader.

Update read code to handle signal/temporary failures.

Add some more useful debug messages for the encryption code.

Eliminate more warnings from Clang.
2021-10-31 11:12:54 -04:00
9d121335f5 Make sure we free memory used for binary data. 2021-10-31 08:30:08 -04:00
9014ab7a20 Fix some minor Coverity-reported issues (added a check to suppress a warning,
removed an unnecessary check, and removed some dead code)
2021-10-31 07:04:17 -04:00
b3ca129a58 Bump NuGet versions. 2021-10-29 07:42:24 -04:00
fafe24bdb6 Fix Windows builds. 2021-10-26 07:12:41 -04:00
b865390b5d Update docos. 2021-10-25 22:00:25 -04:00
1d1ff88ebc Merge pull request #26 from michaelrsweet/crypto
Merge RC4/AES-128 crypto implementation.
2021-10-25 21:56:58 -04:00
8dfc2c6045 Fix LGTM issues. 2021-10-25 21:43:32 -04:00
895738682e Update DLL exports file. 2021-10-25 21:39:44 -04:00
90ad1e694a Fix early closing of input PDF. 2021-10-25 21:36:01 -04:00
e2b33a6cbb Merge branch 'master' into crypto 2021-10-25 21:25:12 -04:00
790cd440ea Fix up copying objects from unencrypted to AES-encrypted documents (still looks
like there are some issues with strings in dicts)
2021-10-25 21:22:59 -04:00
038046e6d5 Save work on encrypted PDF reading. 2021-10-25 19:36:39 -04:00
45c5a00252 Update Windows DLL exports file. 2021-10-24 11:05:33 -04:00
7e9c0afc23 Update summary text. 2021-10-24 11:03:19 -04:00
234c3a7381 Do some reorganization and start the implementation of decryption. 2021-10-24 10:59:25 -04:00
b7ecaeee07 Implement partial write buffering for AES. 2021-10-23 20:33:12 -04:00
208c3419ff Fix AES-128 writing/encryption. 2021-10-23 20:09:02 -04:00
dd56317635 Need object when reading/writing encrypted PDFs (to decrypt/encrypt strings),
RC4 writing is now working, AES-128 needs work, AES-256 hasn't been done yet.
2021-10-23 18:08:16 -04:00
3af39d5d1f Update crypto callback to return the number of output bytes (to account for AES
expansion).
2021-10-23 14:37:25 -04:00
19571d00f2 Fix AES cipher implementation.
Update test program to validate the key expansion using the FIPS-197 example.

Add password-protected RC4 test output.

Add no-password AES-128 test output.
2021-10-23 00:07:13 -04:00
af13376e6d Update docos. 2021-10-18 23:08:13 -04:00
22c245ffd1 Update pdfioContentSetDashPattern to accept doubles (Issue #25) 2021-10-16 09:41:19 -04:00
095a4c10d4 Fix some memory leaks (Issue #23) 2021-10-16 00:02:31 -04:00
f3689d6b3d Fix all-shared on Linux (Issue #22) 2021-10-15 19:32:08 -04:00
ea126c7e8d Save work. 2021-10-15 10:40:42 -04:00
e031254531 Fix 'all-shared' target. 2021-10-13 17:15:59 -04:00
493fbca31c Save work on unit tests for crypto. RC4 and AES are having trouble for some reason... 2021-10-12 17:11:10 -04:00
c24243a2bc Refactor crypto callback to have separate input/output pointers. Add initial writing support. 2021-10-12 09:13:30 -04:00
0caea44f32 Implement MakeReader/Writer functions. 2021-10-10 23:08:56 -04:00
3de55421b5 New member names to specify type of value. 2021-10-10 22:40:42 -04:00
61a7964d90 Implement pdfioFileSetPermissions. 2021-10-10 22:27:09 -04:00
37e80d67b1 Use new random number function to generate file IDs. 2021-10-09 23:10:46 -04:00
953de26f6b Add random number generation support. 2021-10-09 23:05:39 -04:00
2245c9d4f5 Move AES code to separate file, prep private API for making keys/contexts/callbacks for encryption. 2021-10-09 10:49:22 -04:00
27e4ce9f42 Update Xcode project to build the SHA-256 code. 2021-10-08 21:14:11 -04:00
9c05f802fc Add SHA-256 code from RFC 6234. 2021-10-08 18:55:25 -04:00
8aef2bfedd Fix warnings, update Xcode project. 2021-10-08 14:08:07 -04:00
f425952f36 Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
0c6b8f49d2 Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
8ad699c93a Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
a259c3a6b9 Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
0ec1dd936f Tweak coverity Github Actions. 2021-10-08 13:49:48 -04:00
1d63c6edd6 Add prototype coverity Github Actions integration. 2021-10-08 13:49:48 -04:00
d5173d14da Fix some Coverity-detected issues. 2021-10-08 13:49:48 -04:00
1168fd974f Fix pdfio_native.redist package name. 2021-10-08 13:49:48 -04:00
5cff1ca13c Bump NuGet versions. 2021-10-08 13:49:47 -04:00
835fbda363 Tweak coverity Github Actions. 2021-10-05 18:37:16 -04:00
494924a78c Tweak coverity Github Actions. 2021-10-05 18:26:51 -04:00
f23fd8de59 Tweak coverity Github Actions. 2021-10-05 18:21:37 -04:00
3c702096b7 Tweak coverity Github Actions. 2021-10-05 18:19:58 -04:00
e67866e29d Tweak coverity Github Actions. 2021-10-05 18:17:49 -04:00
89d9a7c471 Add prototype coverity Github Actions integration. 2021-10-05 18:13:14 -04:00
00fb962e84 Add prototype coverity Github Actions integration. 2021-10-05 18:08:07 -04:00
fd08ce1b1a Add prototype coverity Github Actions integration. 2021-10-05 18:06:03 -04:00
7fe093f3bd Save work on AES and RC4. 2021-10-04 21:13:01 -04:00
d1e8c966ed Fix some Coverity-detected issues. 2021-10-01 11:38:04 -04:00
85bfab49ab Fix pdfio_native.redist package name. 2021-09-29 11:48:50 -04:00
768cb33c47 Bump NuGet versions. 2021-09-29 11:05:40 -04:00
bb91fb4b13 Tweak macOS build command. 2021-09-27 10:23:32 -04:00
76b2faee0e Add EPUB book cover. 2021-09-27 10:02:10 -04:00
2d90b1325b Ignore new streamed output test file. 2021-09-27 08:57:15 -04:00
6b9f4ba8c9 Fix Windows DLL exports file. 2021-09-27 08:38:04 -04:00
43239eaf8a Add placeholder password callback to support reading of encrypted PDF files in the future. 2021-09-27 08:37:14 -04:00
ba9d03ecac Update docos. 2021-09-27 08:11:53 -04:00
7473bc3cd9 Add some supporting documentation files. 2021-09-27 07:42:19 -04:00
d6746c08a4 Add pdfioFileCreateOutput API (Issue #21) 2021-09-27 07:41:50 -04:00
9f1cadf78b Add redistributable NuGet package for DLL. 2021-09-03 09:13:16 -04:00
760871b8db Update NuGet package info. 2021-09-01 17:09:05 -04:00
39 changed files with 5323 additions and 458 deletions

38
.github/workflows/coverity.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Coverity Scan
on: workflow_dispatch
jobs:
coverity-scan:
runs-on: ubuntu-latest
environment: Coverity
steps:
- uses: actions/checkout@v2
- name: update build environment
run: sudo apt-get update --fix-missing -y
- name: install prerequisites
run: sudo apt-get install -y zlib1g-dev
- name: Download Coverity Build Tool
run: |
wget -q https://scan.coverity.com/download/linux64 --post-data token="$TOKEN&project=$GITHUB_REPOSITORY" -O cov-analysis-linux64.tar.gz
mkdir cov-analysis-linux64
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
- name: Build with cov-build
run: |
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
cov-build --dir cov-int make
- name: Submit the result to Coverity Scan
run: |
tar czvf cov.tgz cov-int
curl \
--form token=$TOKEN \
--form email=michael.r.sweet@gmail.com \
--form file=@cov.tgz \
--form version="$GITHUB_REF" \
--form description="$GITHUB_SHA" \
"https://scan.coverity.com/builds?project=$GITHUB_REPOSITORY"
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}

3
.gitignore vendored
View File

@ -4,8 +4,9 @@
*.o
*.so.1
/.vs
/doc/pdfio.epub
/packages
/pdfio.xcodeproj/xcshareddata
/testpdfio
/testpdfio-out.pdf
/testpdfio-*.pdf
/x64

19
CHANGES.md Normal file
View File

@ -0,0 +1,19 @@
Changes in PDFio
================
v1.0b2 (November 7, 2021)
-------------------------
- Added `pdfioFileCreateOutput` API to support streaming output of PDF
(Issue #21)
- Fixed `all-shared` target (Issue #22)
- Fixed memory leaks (Issue #23)
- Updated `pdfioContentSetDashPattern` to accept `double` values (Issue #25)
- Fixed some issues identified by a Coverity scan.
v1.0b1 (August 30, 2021)
------------------------
- Initial release

13
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,13 @@
Code of Conduct
===============
My goal is to provide quality open source software that everyone can use.
While I may not be able to address every request or accept every contribution
to this project, I will do my best to develop and maintain it for the common
good. As part of the open source community, I expect everyone to:
- Be friendly and patient.
- Be respectful, even if we disagree.
- Be honest.
- Be accepting of all people.
- Fully explain your concerns, issues, or ideas.

398
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,398 @@
Contributing to PDFio
=====================
PDFio is developed and distributed as open source software under the Apache
License, Version 2.0. Contributions should be submitted as pull requests on
the Github site:
http://github.com/michaelrsweet/pdfio/pulls
Contents
--------
- [Build System](#build-system)
- [Version Numbering](#version-numbering)
- [Coding Guidelines](#coding-guidelines)
- [Source Files](#source-files)
- [Header Files](#header-files)
- [Comments](#comments)
- [Indentation](#indentation)
- [Spacing](#spacing)
- [Return Values](#return-values)
- [Functions](#functions)
- [Variables](#variables)
- [Types](#types)
- [Structures](#structures)
- [Constants](#constants)
- [Shell Script Guidelines](#shell-script-guidelines)
- [Makefile Guidelines](#makefile-guidelines)
- [General Organization](#general-organization)
- [Makefile Documentation](#makefile-documentation)
- [Portable Makefile Construction](#portable-makefile-construction)
- [Standard Variables](#standard-variables)
- [Standard Targets](#standard-targets)
- [Object Files](#object-files)
- [Programs](#programs)
- [Static Libraries](#static-libraries)
- [Shared Libraries](#shared-libraries)
- [Dependencies](#dependencies)
- [Install/Uninstall Support](#installuninstall-support)
Build System
------------
The build system uses a simple POSIX makefile to build a static or shared
library. To improve portability, makefiles *must not* make use of features
unique to GNU make. See the [Makefile Guidelines](#makefile-guidelines) section
for a description of the allowed make features and makefile guidelines.
An Xcode project is provided for macOS/iOS developers, and a Visual Studio
solution and projects for Windows developers.
Version Numbering
-----------------
PDFio uses a three-part version number separated by periods to represent the
major, minor, and patch release numbers. Major release numbers indicate large
design changes or backwards-incompatible changes to the library. Minor release
numbers indicate new features and other smaller changes which are backwards-
compatible with previous releases. Patch numbers indicate bug fixes to the
previous feature or patch release.
Production releases use the plain version numbers:
MAJOR.MINOR.PATCH
1.0.0
1.0.1
1.0.2
...
1.1.0
...
2.0.0
The first production release in a MAJOR.MINOR series (MAJOR.MINOR.0) is called
a feature release. Feature releases are the only releases that may contain new
features. Subsequent production releases in a MAJOR.MINOR series may only
contain bug fixes.
Beta-test releases are identified by appending the letter B to the major and
minor version numbers followed by the beta release number:
MAJOR.MINORbNUMBER
1.0b1
Release candidates are identified by appending the letters RC to the major and
minor version numbers followed by the release candidate number:
MAJOR.MINORrcNUMBER
1.0rc1
> Note: While the beta/release candidate syntax is *not* strictly compatible
> with [Semantic Versioning](https://semver.org), it is better supported by the
> various traditional package formats. When packaging a pre-release version of
> PDFio in a format that requires the use of semantic version numbers, the
> version number should simply be converted to the form "MAJOR.MINOR.0-suffix".
Coding Guidelines
-----------------
Contributed source code must follow the guidelines below. While the examples
are for C source files, source code for other languages should conform to the
same guidelines as allowed by the language.
### Source Files
All source files names must be 16 characters or less in length to ensure
compatibility with older UNIX filesystems. Source files containing functions
have an extension of ".c" for C source files. All "include" files have an
extension of ".h". Tabs are set to 8 characters or columns.
The top of each source file contains a header giving the purpose or nature of
the source file and the copyright and licensing notice:
//
// Description of file contents.
//
// Copyright YYYY by AUTHOR.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
### Header Files
Private API header files must be named with the suffix "-private", for example
the "pdfio.h" header file defines all of the public APIs while the
"pdfio-private.h" header file defines all of the private APIs. Typically a
private API header file will include the corresponding public API header file.
### Comments
All source code utilizes block comments within functions to describe the
operations being performed by a group of statements; avoid putting a comment
per line unless absolutely necessary, and then consider refactoring the code
so that it is not necessary. C source files use the C99 comment format
("// comment"):
// Clear the state array before we begin...
for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
array[i] = PDFIO_STATE_IDLE;
// Wait for state changes on another thread...
do
{
for (i = 0; i < (sizeof(array) / sizeof(sizeof(array[0])); i ++)
if (array[i] != PDFIO_STATE_IDLE)
break;
if (i == (sizeof(array) / sizeof(array[0])))
sleep(1);
} while (i == (sizeof(array) / sizeof(array[0])));
### Indentation
All code blocks enclosed by brackets begin with the opening brace on a new
line. The code then follows starting on a new line after the brace and is
indented 2 spaces. The closing brace is then placed on a new line following
the code at the original indentation:
{
int i; // Looping var
// Process foobar values from 0 to 999...
for (i = 0; i < 1000; i ++)
{
do_this(i);
do_that(i);
}
}
Single-line statements following "do", "else", "for", "if", and "while" are
indented 2 spaces as well. Blocks of code in a "switch" block are indented 4
spaces after each "case" and "default" case:
switch (array[i])
{
case PDFIO_STATE_IDLE :
do_this(i);
do_that(i);
break;
default :
do_nothing(i);
break;
}
### Spacing
A space follows each reserved word such as `if`, `while`, etc. Spaces are not
inserted between a function name and the arguments in parenthesis.
### Return Values
Parenthesis surround values returned from a function:
return (PDFIO_STATE_IDLE);
### Functions
Functions with a global scope have a lowercase prefix followed by capitalized
words, e.g., `pdfioDoThis`, `pdfioDoThat`, `pdfioDoSomethingElse`, etc. Private
global functions begin with a leading underscore, e.g., `_pdfioDoThis`,
`_pdfioDoThat`, etc.
Functions with a local scope are declared static with lowercase names and
underscores between words, e.g., `do_this`, `do_that`, `do_something_else`, etc.
Function names follow the following pattern:
- "pdfioFooCreate" to create a Foo object,
- "pdfioFooDelete" to destroy (free) a Foo object,
- "pdfioFooGetBar" to get data element Bar from object Foo,
- "pdfioFooIsBar" to test condition Bar for object Foo, and
- "pdfioFooSetBar" to set data element Bar in object Foo.
- "pdfioFooVerb" to take an action with object Foo.
Each function begins with a comment header describing what the function does,
the possible input limits (if any), the possible output values (if any), and
any special information needed:
//
// 'pdfioDoThis()' - Short description of function.
//
// Longer documentation for function with examples using a subset of
// markdown. This is a bulleted list:
//
// - One fish
// - Two fish
// - Red fish
// - Blue fish
//
// > *Note:* Special notes for developer should be markdown block quotes.
//
float // O - Inverse power value, 0.0 <= y <= 1.1
pdfioDoThis(float x) // I - Power value (0.0 <= x <= 1.1)
{
...
return (y);
}
Return/output values are indicated using an "O" prefix, input values are
indicated using the "I" prefix, and values that are both input and output use
the "IO" prefix for the corresponding in-line comment.
The [`codedoc` documentation generator][1] also understands the following
special text in the function description comment:
@deprecated@ - Marks the function as deprecated (not recommended
for new development and scheduled for removal)
@since version@ - Marks the function as new in the specified version.
@private@ - Marks the function as private (same as starting the
function name with an underscore)
[1]: https://www.msweet.org/codedoc
### Variables
Variables with a global scope are capitalized, e.g., `ThisVariable`,
`ThatVariable`, `ThisStateVariable`, etc. Globals *must not* be used in the
PDFio library.
Variables with a local scope are lowercase with underscores between words,
e.g., `this_variable`, `that_variable`, etc. Any "local global" variables
shared by functions within a source file are declared static.
Each variable is declared on a separate line and is immediately followed by a
comment block describing the variable:
int ThisVariable; // The current state of this
static int that_variable; // The current state of that
### Types
All type names are lowercase with underscores between words and `_t` appended
to the end of the name, e.g., `pdfio_this_type_t`, `pdfio_that_type_t`, etc.
Type names start with the "pdfio\_" prefix to avoid conflicts with system types.
Private type names start with an underscore, e.g., `_pdfio_this_t`,
`_pdfio_that_t`, etc.
Each type has a comment block immediately after the typedef:
typedef int pdfio_this_type_t; // This type is for foobar options.
### Structures
All structure names are lowercase with underscores between words and `_s`
appended to the end of the name, e.g., `pdfio_this_s`, `pdfio_that_s`, etc.
Structure names start with the "pdfio\_" prefix to avoid conflicts with system
types. Private structure names start with an underscore, e.g., `_pdfio_this_s`,
`_pdfio_that_s`, etc.
Each structure has a comment block immediately after the struct and each member
is documented similar to the variable naming policy above:
struct pdfio_this_struct_s // This structure is for foobar options.
{
int this_member; // Current state for this
int that_member; // Current state for that
};
One common design pattern is to define a private structure with a public
typedef, for example:
// In public header
typedef struct _pdfio_foo_s pdfio_foo_t // Foo object
// In private header
struct _pdfio_foo_s // Foo object
{
int this_member; // Current state for this
int that_member; // Current state for that
};
### Constants
All constant names are uppercase with underscores between words, e.g.,
`PDFIO_THIS_CONSTANT`, `PDFIO_THAT_CONSTANT`, etc. Constants begin with the
"PDFio\_" prefix to avoid conflicts with system constants. Private constants
start with an underscore, e.g., `_PDFIO_THIS_CONSTANT`,
`_PDFIO_THAT_CONSTANT`, etc.
Typed enumerations should be used whenever possible to allow for type checking
by the compiler. The constants for typed enumerations must match the type name
in uppercase, for example a `pdfio_foo_e` enumeration has constant names
starting with `PDFIO_FOO_`.
Comment blocks immediately follow each constant:
typedef enum pdfio_style_e // Style enumerations
{
PDFIO_STYLE_THIS, // This style
PDFIO_STYLE_THAT // That style
} pdfio_style_t;
Shell Script Guidelines
-----------------------
All shell scripts in PDFio must conform to the [POSIX shell][POSIX-SHELL]
command language and should restrict their dependence on non-POSIX utility
commands.
[POSIX-SHELL]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18
Makefile Guidelines
-------------------
PDFio uses a single [POSIX makefile][POSIX-MAKE] to build it. GNU make
extensions MUST NOT be used.
[POSIX-MAKE]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html
The following variables are defined in the makefile:
- `AR`; the static library archiver command,
- `ARFLAGS`; options for the static library archiver command,
- `CC`; the C compiler command,
- `CFLAGS`; options for the C compiler command,
- `CODESIGN_IDENTITY`: the code signing identity,
- `COMMONFLAGS`; common compiler optimization options,
- `DESTDIR`/`DSTROOT`: the destination root directory when installing.
- `DSO`; the shared library building command,
- `DSOFLAGS`; options for the shared library building command,
- `DSONAME`: the root name of the shared library
- `LDFLAGS`; options for the linker,
- `LIBS`; libraries for all programs,
- `prefix`; the installation prefix directory,
- `RANLIB`; the static library indexing command,
- `SHELL`; the sh (POSIX shell) command,
- `VERSION`: the library version number.
The following standard targets are defined in the makefile:
- `all`; creates the static library and unit test program.
- `all-shared`; creates a shared library appropriate for the local system.
- `clean`; removes all target programs libraries, documentation files, and
object files,
- `debug`: creates a clean build of the static library and unit test program
with debug printfs and the clang address sanitizer enabled.
- `install`; installs all distribution files in their corresponding locations.
- `install-shared`; same as `install` but also installs the shared library.
- `test`; runs the unit test program, building it as needed.

17
FAQ.md
View File

@ -1,17 +0,0 @@
Frequently Asked Questions
==========================
Why Don't You Support Writing a PDF File with Encryption?
---------------------------------------------------------
PDF encryption offers very little protection:
- PDF encryption keys are reused and derived from the user password (padded
with a standard base string) and the object numbers in the file.
- RC4 encryption (40- and 128-bit) was broken years ago.
- AES encryption (128- and 256-bit) is better, but PDF uses Cipher Block
Chaining (CBC) which enables attacks that allow the original encryption key
to be recovered.
In addition, PDF usage controls (no print, no copy, etc.) are tied to this
encryption, making them trivial to bypass.

View File

@ -16,7 +16,8 @@ ARFLAGS = cr
CC = cc
CFLAGS =
CODESIGN_IDENTITY = Developer ID
COMMONFLAGS = -Os -g
#COMMONFLAGS = -Os -g
COMMONFLAGS = -O0 -g
CPPFLAGS = '-DPDFIO_VERSION="$(VERSION)"'
DESTDIR = $(DSTROOT)
DSO = cc
@ -25,7 +26,7 @@ DSONAME =
LDFLAGS =
LIBS = -lm -lz
RANLIB = ranlib
VERSION = 1.0b1
VERSION = 1.0b2
prefix = /usr/local
@ -40,13 +41,18 @@ PUBHEADERS = \
pdfio.h \
pdfio-content.h
PUBOBJS = \
pdfio-aes.o \
pdfio-array.o \
pdfio-common.o \
pdfio-content.o \
pdfio-crypto.o \
pdfio-dict.o \
pdfio-file.o \
pdfio-md5.o \
pdfio-object.o \
pdfio-page.o \
pdfio-rc4.o \
pdfio-sha256.o \
pdfio-stream.o \
pdfio-string.o \
pdfio-token.o \
@ -69,8 +75,8 @@ all: $(TARGETS)
all-shared:
if test `uname` = Darwin; then \
$(MAKE) DSONAME="libpdfio.1.dylib" -$(MAKEFLAGS) all; \
else
$(MAKE) DSONAME="libpdfio.so.1" -$(MAKEFLAGS) all; \
else \
$(MAKE) COMMONFLAGS="-g -Os -fPIC" DSONAME="libpdfio.so.1" -$(MAKEFLAGS) all; \
fi
debug:
@ -118,6 +124,9 @@ install-shared:
test: testpdfio
./testpdfio
valgrind: testpdfio
valgrind --leak-check=full ./testpdfio
# pdfio library
libpdfio.a: $(LIBOBJS)
@ -125,7 +134,7 @@ libpdfio.a: $(LIBOBJS)
$(RANLIB) $@
libpdfio.so.1: $(LIBOBJS)
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -shared -o $@ -Wl,soname,$@ $(LIBOBJS) $(LIBS)
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -shared -o $@ -Wl,-soname,$@ $(LIBOBJS) $(LIBS)
libpdfio.1.dylib: $(LIBOBJS)
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -dynamiclib -o $@ -install_name $(prefix)/lib/$@ -current_version $(VERSION) -compatibility_version 1.0 $(LIBOBJS) $(LIBS)
@ -141,16 +150,8 @@ pdfio1.def: $(LIBOBJS) Makefile
echo "LIBRARY pdfio1" >$@
echo "VERSION 1.0" >>$@
echo "EXPORTS" >>$@
(nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}' | \
grep -v '^_ttf' | grep -v '^__' | sed -e '1,$$s/^_//'; \
echo pdfioAdobeRGBGamma; echo pdfioAdobeRGBMatrix; \
echo pdfioAdobeRGBWhitePoint; \
echo pdfioDisplayP3Gamma; echo pdfioDisplayP3Matrix; \
echo pdfioDisplayP3WhitePoint; \
echo pdfioSRGBGamma; echo pdfioSRGBMatrix; \
echo pdfioSRGBWhitePoint; \
echo _pdfioTokenInit; \
echo _pdfioValueDebug; echo _pdfioValueRead) | sort >>$@
nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}' | \
grep -v '^_ttf' | sed -e '1,$$s/^_//' | sort >>$@
# pdfio test program
@ -173,6 +174,7 @@ DOCFLAGS = \
.PHONY: doc
doc:
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" $(PUBHEADERS) $(PUBOBJS:.o=.c) --body doc/pdfio.md --coverimage doc/pdfio-512.png pdfio.xml >doc/pdfio.html
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" --body doc/pdfio.md --coverimage doc/pdfio-epub.png pdfio.xml --epub doc/pdfio.epub
codedoc $(DOCFLAGS) --title "pdf read/write library" --man pdfio --section 3 --body doc/pdfio.md pdfio.xml >doc/pdfio.3
rm -f pdfio.xml

View File

@ -13,8 +13,7 @@ goals of PDFio are:
- Read and write any version of PDF file
- Provide access to pages, objects, and streams within a PDF file
- Support reading encrypted PDF files
- Support writing PDF files with digital signatures
- Support reading and writing of encrypted PDF files
- Extract or embed useful metadata (author, creator, page information, etc.)
- "Filter" PDF files, for example to extract a range of pages or to embed fonts
that are missing from a PDF
@ -102,8 +101,8 @@ Visual Studio Project
---------------------
The Visual Studio solution ("pdfio.sln") is provided for Windows developers and
generates both a static library and DLL. You can also use NuGet to install the
`pdfio_native` package.
generates the PDFIO1 DLL. You can also use NuGet to install the `pdfio_native`
package.
Xcode Project
@ -116,7 +115,7 @@ generates a static library that will be installed under "/usr/local" with:
You can reproduce this with the makefile using:
sudo make 'COMMONFLAGS="-Os -mmacosx-version-min=10.14 -arch x86_64 -arch arm64"' install
sudo make COMMONFLAGS="-Os -mmacosx-version-min=10.14 -arch x86_64 -arch arm64" install
Legal Stuff

121
SECURITY.md Normal file
View File

@ -0,0 +1,121 @@
Security Policy
===============
This file describes how security issues are reported and handled, and what the
expectations are for security issues reported to this project.
Responsible Disclosure
----------------------
With *responsible disclosure*, a security issue (and its fix) is disclosed only
after a mutually-agreed period of time (the "embargo date"). The issue and fix
are shared amongst and reviewed by the key stakeholders (Linux distributions,
OS vendors, etc.) and the CERT/CC. Fixes are released to the public on the
agreed-upon date.
> Responsible disclosure applies only to production releases. A security
> vulnerability that only affects unreleased code can be fixed immediately
> without coordination. Vendors *should not* package and release unstable
> snapshots, beta releases, or release candidates of this software.
Supported Versions
------------------
All production releases of this software are subject to this security policy. A
production release is tagged and given a semantic version number of the form:
MAJOR.MINOR.PATCH
where "MAJOR" is an integer starting at 1 and "MINOR" and "PATCH" are integers
starting at 0. A feature release has a "PATCH" value of 0, for example:
1.0.0
1.1.0
2.0.0
Beta releases and release candidates are *not* prodution releases and use
semantic version numbers of the form:
MAJOR.MINORbNUMBER
MAJOR.MINORrcNUMBER
where "MAJOR" and "MINOR" identify the new feature release version number and
"NUMBER" identifies a beta or release candidate number starting at 1, for
example:
1.0b1
1.0b2
1.0rc1
Reporting a Vulnerability
-------------------------
Report all security issues to "security AT msweet.org". 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.
PGP Public Key
--------------
The following PGP public key can be used for signing security messages.
```
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
mQINBF6L0RgBEAC8FTqc/1Al+pWW+ULE0OB2qdbiA2NBjEm0X0WhvpjkqihS1Oih
ij3fzFxKJ+DgutQyDb4QFD8tCFL0f0rtNL1Iz8TtiAJjvlhL4kG5cdq5HYEchO10
qFeZ1DqvnHXB4pbKouEQ7Q/FqB1PG+m6y2q1ntgW+VPKm/nFUWBCmhTQicY3FOEG
q9r90enc8vhQGOX4p01KR0+izI/g+97pWgMMj5N4zHuXV/GrPhlVgo3Wn1OfEuX4
9vmv7GX4G17Me3E3LOo0c6fmPHJsrRG5oifLpvEJXVZW/RhJR3/pKMPSI5gW8Sal
lKAkNeV7aZG3U0DCiIVL6E4FrqXP4PPj1KBixtxOHqzQW8EJwuqbszNN3vp9w6jM
GvGtl8w5Qrw/BwnGC6Dmw+Qv04p9JRY2lygzZYcKuwZbLzBdC2CYy7P2shoKiymX
ARv+i+bUl6OmtDe2aYaqRkNDgJkpuVInBlMHwOyLP6fN2o7ETXQZ+0a1vQsgjmD+
Mngkc44HRnzsIJ3Ga4WwW8ggnAwUzJ/DgJFYOSbRUF/djBT4/EFoU+/kjXRqq8/d
c8HjZtz2L27njmMw68/bYmY1TliLp50PXGzJA/KeY90stwKtTI0ufwAyi9i9BaYq
cGbdq5jnfSNMDdKW2kLCNTQeUWSSytMTsdU0Av3Jrv5KQF8x5GaXcpCOTwARAQAB
tExNaWNoYWVsIFN3ZWV0IChzZWN1cml0eUBtc3dlZXQub3JnKSAoU2VjdXJpdHkg
UEdQIEtleSkgPHNlY3VyaXR5QG1zd2VldC5vcmc+iQJUBBMBCgA+FiEEOElfSXYU
h91AF0sBpZiItz2feQIFAl6L0RgCGwMFCQeGH4AFCwkIBwMFFQoJCAsFFgIDAQAC
HgECF4AACgkQpZiItz2feQIhjhAAqZHuQJkPBsAKUvJtPiyunpR6JENTUIDxnVXG
nue+Zev+B7PzQ7C4CAx7vXwuWTt/BXoyQFKRUrm+YGiBTvLYQ8fPqudDnycSaf/A
n01Ushdlhyg1wmCBGHTgt29IkEZphNj6BebRd675RTOSD5y14jrqUb+gxRNuNDa5
ZiZBlBE4A8TV6nvlCyLP5oXyTvKQRFCh4dEiL5ZvpoxnhNvJpSe1ohL8iJ9aeAd5
JdakOKi8MmidRPYC5IldXwduW7VC7dtqSiPqT5aSN0GJ8nIhSpn/ZkOEAPHAtxxa
0VgjltXwUDktu74MUUghdg2vC1df2Z+PqHLsGEqOmxoBIJYXroIqSEpO3Ma7hz0r
Xg1AWHMR/xxiLXLxgaZRvTp7AlaNjbqww8JDG8g+nDIeGsgIwWN/6uPczledvDQa
HtlMfN97i+rt6sCu13UMZHpBKOGg7eAGRhgpOwpUqmlW1b+ojRHGkmZ8oJSE7sFT
gzSGNkmfVgA1ILl0mi8OBVZ4jlUg6EgVsiPlzolH92iscK7g50PdjzpQe0m3gmcL
dpOmSL8Fti05dPfamJzIvJd28kMZ6yMnACKj9rq/VpfgYBLK8dbNUjEOQ2oq7PyR
Ye/LE1OmAJwfZQkyQNI8yAFXoRJ8u3/bRb3SPvGGWquGBDKHv2K1XiCW65uyLe5B
RNJWmme5Ag0EXovRGAEQAJZMFeIMt/ocLskrp89ZyBTTiavFKn9+QW7C2Mb36A73
J2g9vRFBSRizb+t8lSzP/T1GbKS0cEmfEpQppWImTbOMV6ZgxrM0IUy1Yd7Kyc0K
oNMZvykRYwVMzxB5hiQ88kCLfqTNCveIvu1xcB9pWkf+cuDmGCxA3I+yc3Eh/SOP
urDsHObt7fyEmJpSxCXlMFHRCuWyGXhMNvhR186t9mANW0PyxKJ8efr+2Vhm1+pA
Vk9JESac/lREvx9PVFmlPdqgqRkQ0TQB5+ROo9Wy77cxQr5+rvSZZff630I1YgZf
Ph6xOV1/q6vJ3RBNA2nPSTjPeeWQ7pTn7PZGJwCjIUjhMbO+EJVKUJNOAEg033mG
tLfbFUYdhA/dRgFuKz90loCMfsnf3e4o/TFydSHUuwBUtOWkL1BBWEbk95M/Zr00
w5fD9knas1u5Lc4ogXzTFPnvJ6hM1RAFJEd+FYzJZIvzwrIx4Ag1DOKViVBpeLTu
HWj+xckEgvxEBglplALzfSIJ0CLQSNL8iMFbzCnPeUoQfPkqu37KHrB9syAA06Tb
qw1Ax0qBqKInGIgBd0w6dFLF3s04xVcPAXWyJ0w4I7h2bs+aD6YwwK6xxCtXxtN5
Q1LQM8s3tKNXER3mZ8zfwgwjsdLVwhXhysFi6Dlkvk/Vrbn1QDfJnzq+F9LsGRGb
ABEBAAGJAjwEGAEKACYWIQQ4SV9JdhSH3UAXSwGlmIi3PZ95AgUCXovRGAIbDAUJ
B4YfgAAKCRClmIi3PZ95AhDZD/40fShzDS/smZZL0oXN4GgZ62FrXWBdLjontkXo
d8hDh1wJZwqsLVbtO2Gu0CPeH9GclQ3bYsR19sGMM4FDgjMu57O/TU6GZl2Ywcjh
ayhRTHyAq/BKZn71AM0N7LS8MdNTaLbTbzEu5oGbAmOVv5f0SUnQoGxbeF8ih5bo
hR3ZcORujWMgnymL3+cerNyIDQAtfMAUTfpVcwem4CvquA9Wjtur8YN1t+N7I3o2
eMTNSyNUL9Yx3NxbyJ0yrrMvASo+ZVRaPW5+ET9Iqd68ILSY04Gnar3URJssggX8
+cuyEbP9bAG8qYqcr2aSC2dW84mL/RnZGR//1dfS0Ugk6Osj0LSF5i+mz0CbIjYQ
PKgLlgpycuGZBC5kG3RWWfanM0HxPDx10a7vEWA1A5Q+csx4yi3CW/giC1zAdhUO
cJ1l4Uj/oxpGeLN7BnT/2NzU/px2fpbaG+xU4HlwjzFM2cIOUIohHFhdvFZbFIIA
mePqTBnEB3HtXYRTgMoYDXLWhlOXjyVnMR45WDfvEA3KqbAz6sNMtaOJ6rHBWnR1
1YbpvDWUeaGSLXBoGyo3RgTrN9jON8lE/oUxFobnEdfZGD+uwIniylc5rw3+VkBU
+QGZDfgPgxjSmKsWq1cK6rNfBacGYrdyqf90VemEsvR8r0Ump0RPzBMlAAq0Xkup
WkiKlA==
=0GzT
-----END PGP PUBLIC KEY BLOCK-----
```

BIN
doc/pdfio-epub.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 788 KiB

View File

@ -1,4 +1,4 @@
.TH pdfio 3 "pdf read/write library" "2021-08-30" "pdf read/write library"
.TH pdfio 3 "pdf read/write library" "2021-10-25" "pdf read/write library"
.SH NAME
pdfio \- pdf read/write library
.SH Introduction
@ -6,11 +6,15 @@ pdfio \- pdf read/write library
PDFio is a simple C library for reading and writing PDF files. The primary goals of pdfio are:
.IP \(bu 5
.PP
Read any PDF file with or without encryption or linearization
Read and write any version of PDF file
.IP \(bu 5
.PP
Write PDF files without encryption or linearization
Provide access to pages, objects, and streams within a PDF file
.IP \(bu 5
.PP
Support reading and writing of encrypted PDF files
.IP \(bu 5
.PP
@ -152,7 +156,7 @@ There is also an Xcode project ("pdfio.xcodeproj") you can use on macOS which ge
You can reproduce this with the makefile using:
.nf
sudo make 'COMMONFLAGS="\-Os \-mmacosx\-version\-min=10.14 \-arch x86_64 \-arch arm64"' install
sudo make COMMONFLAGS="\-Os \-mmacosx\-version\-min=10.14 \-arch x86_64 \-arch arm64" install
.fi
.SS Detecting PDFio
.PP
@ -171,7 +175,7 @@ In a makefile you can add the necessary compiler and linker options with:
LIBS += `pkg\-config \-\-libs pdfio`
.fi
.PP
On Windows, you need to link to the PDFIO.LIB (static) or PDFIO1.LIB (DLL) libraries and include the "zlib" NuGet package dependency.
On Windows, you need to link to the PDFIO1.LIB (DLL) library and include the zlib_native NuGet package dependency. You can also use the published pdfio_native NuGet package.
.SS Header Files
.PP
PDFio provides a primary header file that is always used:
@ -270,6 +274,15 @@ You create a new PDF file using the pdfioFileCreate function:
.PP
where the six arguments to the function are the filename ("myoutputfile.pdf"), PDF version ("2.0"), media box (media_box), crop box (crop_box), an optional error callback function (error_cb), and an optional pointer value for the error callback function (error_data). The units for the media and crop boxes are points (1/72nd of an inch).
.PP
Alternately you can stream a PDF file using the pdfioFileCreateOutput function:
.nf
pdfio_rect_t media_box = { 0.0, 0.0, 612.0, 792.0 }; // US Letter
pdfio_rect_t crop_box = { 36.0, 36.0, 576.0, 756.0 }; // w/0.5" margins
pdfio_file_t *pdf = pdfioFileCreateOutput(output_cb, output_ctx, "2.0", &media_box, &crop_box, error_cb, error_data);
.fi
.PP
Once the file is created, use the pdfioFileCreateObj, pdfioFileCreatePage, and pdfioPageCopy functions to create objects and pages in the file.
.PP
Finally, the pdfioFileClose function writes the PDF cross\-reference and "trailer" information, closes the file, and frees all memory that was used for it.
@ -800,6 +813,28 @@ Display P3
PDFIO_CS_SRGB
.br
sRGB
.SS pdfio_encryption_e
PDF encryption modes
.TP 5
PDFIO_ENCRYPTION_AES_128
.br
128-bit AES encryption (PDF 1.6)
.TP 5
PDFIO_ENCRYPTION_AES_256
.br
256-bit AES encryption (PDF 2.0)
.TP 5
PDFIO_ENCRYPTION_NONE
.br
No encryption
.TP 5
PDFIO_ENCRYPTION_RC4_128
.br
128-bit RC4 encryption (PDF 1.4)
.TP 5
PDFIO_ENCRYPTION_RC4_40
.br
40-bit RC4 encryption (PDF 1.3)
.SS pdfio_filter_e
Compression/decompression filters for streams
.TP 5
@ -874,6 +909,51 @@ Miter joint
PDFIO_LINEJOIN_ROUND
.br
Round joint
.SS pdfio_permission_e
PDF permission bits
.TP 5
PDFIO_PERMISSION_ALL
.br
.TP 5
PDFIO_PERMISSION_ANNOTATE
.br
PDF allows annotation
.TP 5
PDFIO_PERMISSION_ASSEMBLE
.br
PDF allows assembly (insert, delete, or rotate pages, add document outlines and thumbnails)
.TP 5
PDFIO_PERMISSION_COPY
.br
PDF allows copying
.TP 5
PDFIO_PERMISSION_FORMS
.br
PDF allows filling in forms
.TP 5
PDFIO_PERMISSION_MODIFY
.br
PDF allows modification
.TP 5
PDFIO_PERMISSION_NONE
.br
No permissions
.TP 5
PDFIO_PERMISSION_PRINT
.br
PDF allows printing
.TP 5
PDFIO_PERMISSION_PRINT_HIGH
.br
PDF allows high quality printing
.TP 5
PDFIO_PERMISSION_READING
.br
PDF allows screen reading/accessibility (deprecated in PDF 2.0)
.TP 5
~0
.br
All permissions
.SS pdfio_textrendering_e
Text rendering modes
.TP 5
@ -1397,9 +1477,9 @@ Set the stroke pattern.
.nf
bool pdfioContentSetDashPattern (
pdfio_stream_t *st,
int phase,
int on,
int off
double phase,
double on,
double off
);
.fi
.SS pdfioContentSetFillColorDeviceCMYK
@ -1995,6 +2075,20 @@ pdfio_file_t * pdfioFileCreate (
void *error_data
);
.fi
.PP
This function creates a new PDF file. The "filename" argument specifies the
name of the PDF file to create.
.PP
The "version" argument specifies the PDF version number for the file or
\fBNULL\fR for the default ("2.0").
.PP
The "media_box" and "crop_box" arguments specify the default MediaBox and
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP
The "error_cb" and "error_data" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR the default error handler is used that
writes error messages to \fBstderr\fR.
.SS pdfioFileCreateArrayObj
Create a new object in a PDF file containing an array.
.PP
@ -2137,6 +2231,51 @@ pdfio_obj_t * pdfioFileCreateObj (
pdfio_dict_t *dict
);
.fi
.SS pdfioFileCreateOutput
Create a PDF file through an output callback.
.PP
.nf
pdfio_file_t * pdfioFileCreateOutput (
pdfio_output_cb_t output_cb,
void *output_ctx,
const char *version,
pdfio_rect_t *media_box,
pdfio_rect_t *crop_box,
pdfio_error_cb_t error_cb,
void *error_data
);
.fi
.PP
This function creates a new PDF file that is streamed though an output
callback. The "output_cb" and "output_ctx" arguments specify the output
callback and its context pointer which is called whenever data needs to be
written:
.PP
.nf
ssize_t
output_cb(void *output_ctx, const void *buffer, size_t bytes)
{
// Write buffer to output and return the number of bytes written
}
.fi
The "version" argument specifies the PDF version number for the file or
\fBNULL\fR for the default ("2.0").
.PP
The "media_box" and "crop_box" arguments specify the default MediaBox and
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP
The "error_cb" and "error_data" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR the default error handler is used that
writes error messages to \fBstderr\fR.
.PP
.IP 5
\fINote\fR: Files created using this API are slightly larger than those
.IP 5
created using the \fIpdfioFileCreate\fR function since stream lengths are
.IP 5
stored as indirect object references.
.SS pdfioFileCreatePage
Create a page in a PDF file.
.PP
@ -2240,6 +2379,18 @@ pdfio_obj_t * pdfioFileGetPage (
size_t n
);
.fi
.SS pdfioFileGetPermissions
Get the access permissions of a PDF file.
.PP
.nf
pdfio_permission_t pdfioFileGetPermissions (
pdfio_file_t *pdf,
pdfio_encryption_t *encryption
);
.fi
.PP
This function returns the access permissions of a PDF file and (optionally)
the type of encryption that has been used.
.SS pdfioFileGetProducer
Get the producer string for a PDF file.
.PP
@ -2278,10 +2429,25 @@ Open a PDF file for reading.
.nf
pdfio_file_t * pdfioFileOpen (
const char *filename,
pdfio_password_cb_t password_cb,
void *password_data,
pdfio_error_cb_t error_cb,
void *error_data
);
.fi
.PP
This function opens an existing PDF file. The "filename" argument specifies
the name of the PDF file to create.
.PP
The "password_cb" and "password_data" arguments specify a password callback
and its data pointer for PDF files that use one of the standard Adobe
"security" handlers. The callback returns a password string or \fBNULL\fR to
cancel the open. If \fBNULL\fR is specified for the callback function and the
PDF file requires a password, the open will always fail.
.PP
The "error_cb" and "error_data" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR the default error handler is used that
writes error messages to \fBstderr\fR.
.SS pdfioFileSetAuthor
Set the author for a PDF file.
.PP
@ -2318,6 +2484,30 @@ void pdfioFileSetKeywords (
const char *value
);
.fi
.SS pdfioFileSetPermissions
Set the PDF permissions, encryption mode, and passwords.
.PP
.nf
bool pdfioFileSetPermissions (
pdfio_file_t *pdf,
pdfio_permission_t permissions,
pdfio_encryption_t encryption,
const char *owner_password,
const char *user_password
);
.fi
.PP
This function sets the PDF usage permissions, encryption mode, and
passwords.
.PP
.IP 5
\fINote\fR: This function must be called before creating or copying any
.IP 5
objects. Due to fundamental limitations in the PDF format, PDF encryption
.IP 5
offers little protection from disclosure. Permissions are not enforced in
.IP 5
any meaningful way.
.SS pdfioFileSetSubject
Set the subject for a PDF file.
.PP
@ -2656,6 +2846,12 @@ Key/value dictionary
.nf
typedef struct _pdfio_dict_s pdfio_dict_t;
.fi
.SS pdfio_encryption_t
PDF encryption modes
.PP
.nf
typedef enum pdfio_encryption_e pdfio_encryption_t;
.fi
.SS pdfio_error_cb_t
Error callback
.PP
@ -2698,6 +2894,24 @@ Numbered object in PDF file
.nf
typedef struct _pdfio_obj_s pdfio_obj_t;
.fi
.SS pdfio_output_cb_t
Output callback for pdfioFileCreateOutput
.PP
.nf
typedef ssize_t(*)(void *ctx const void *data size_t datalen) pdfio_output_cb_t;
.fi
.SS pdfio_password_cb_t
Password callback for pdfioFileOpen
.PP
.nf
typedef const char *(*)(void *data const char *filename) pdfio_password_cb_t;
.fi
.SS pdfio_permission_t
PDF permission bitfield
.PP
.nf
typedef int pdfio_permission_t;
.fi
.SS pdfio_rect_t
PDF rectangle
.PP
@ -2722,6 +2936,12 @@ PDF value types
.nf
typedef enum pdfio_valtype_e pdfio_valtype_t;
.fi
.SS state_t[4][4]
4x4 AES state table
.PP
.nf
typedef uint8_t state_t[4][4];
.fi
.SH AUTHOR
.PP
Michael R Sweet

View File

@ -1,13 +1,13 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>PDFio Programming Manual v1.0b1</title>
<title>PDFio Programming Manual v1.0.0</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta name="generator" content="codedoc v3.7">
<meta name="author" content="Michael R Sweet">
<meta name="language" content="en-US">
<meta name="copyright" content="Copyright © 2021 by Michael R Sweet">
<meta name="version" content="1.0b1">
<meta name="version" content="1.0.0">
<style type="text/css"><!--
body {
background: white;
@ -245,7 +245,7 @@ span.string {
<body>
<div class="header">
<p><img class="title" src="pdfio-512.png"></p>
<h1 class="title">PDFio Programming Manual v1.0b1</h1>
<h1 class="title">PDFio Programming Manual v1.0.0</h1>
<p>Michael R Sweet</p>
<p>Copyright © 2021 by Michael R Sweet</p>
</div>
@ -381,6 +381,7 @@ span.string {
<li><a href="#pdfioFileCreateImageObjFromData">pdfioFileCreateImageObjFromData</a></li>
<li><a href="#pdfioFileCreateImageObjFromFile">pdfioFileCreateImageObjFromFile</a></li>
<li><a href="#pdfioFileCreateObj">pdfioFileCreateObj</a></li>
<li><a href="#pdfioFileCreateOutput">pdfioFileCreateOutput</a></li>
<li><a href="#pdfioFileCreatePage">pdfioFileCreatePage</a></li>
<li><a href="#pdfioFileFindObj">pdfioFileFindObj</a></li>
<li><a href="#pdfioFileGetAuthor">pdfioFileGetAuthor</a></li>
@ -393,6 +394,7 @@ span.string {
<li><a href="#pdfioFileGetNumPages">pdfioFileGetNumPages</a></li>
<li><a href="#pdfioFileGetObj">pdfioFileGetObj</a></li>
<li><a href="#pdfioFileGetPage">pdfioFileGetPage</a></li>
<li><a href="#pdfioFileGetPermissions">pdfioFileGetPermissions</a></li>
<li><a href="#pdfioFileGetProducer">pdfioFileGetProducer</a></li>
<li><a href="#pdfioFileGetSubject">pdfioFileGetSubject</a></li>
<li><a href="#pdfioFileGetTitle">pdfioFileGetTitle</a></li>
@ -402,6 +404,7 @@ span.string {
<li><a href="#pdfioFileSetCreationDate">pdfioFileSetCreationDate</a></li>
<li><a href="#pdfioFileSetCreator">pdfioFileSetCreator</a></li>
<li><a href="#pdfioFileSetKeywords">pdfioFileSetKeywords</a></li>
<li><a href="#pdfioFileSetPermissions">pdfioFileSetPermissions</a></li>
<li><a href="#pdfioFileSetSubject">pdfioFileSetSubject</a></li>
<li><a href="#pdfioFileSetTitle">pdfioFileSetTitle</a></li>
<li><a href="#pdfioImageGetBytesPerLine">pdfioImageGetBytesPerLine</a></li>
@ -438,6 +441,7 @@ span.string {
<li><a href="#pdfio_array_t">pdfio_array_t</a></li>
<li><a href="#pdfio_cs_t">pdfio_cs_t</a></li>
<li><a href="#pdfio_dict_t">pdfio_dict_t</a></li>
<li><a href="#pdfio_encryption_t">pdfio_encryption_t</a></li>
<li><a href="#pdfio_error_cb_t">pdfio_error_cb_t</a></li>
<li><a href="#pdfio_file_t">pdfio_file_t</a></li>
<li><a href="#pdfio_filter_t">pdfio_filter_t</a></li>
@ -445,19 +449,25 @@ span.string {
<li><a href="#pdfio_linejoin_t">pdfio_linejoin_t</a></li>
<li><a href="#pdfio_matrix_t[3][2]">pdfio_matrix_t[3][2]</a></li>
<li><a href="#pdfio_obj_t">pdfio_obj_t</a></li>
<li><a href="#pdfio_output_cb_t">pdfio_output_cb_t</a></li>
<li><a href="#pdfio_password_cb_t">pdfio_password_cb_t</a></li>
<li><a href="#pdfio_permission_t">pdfio_permission_t</a></li>
<li><a href="#pdfio_rect_t">pdfio_rect_t</a></li>
<li><a href="#pdfio_stream_t">pdfio_stream_t</a></li>
<li><a href="#pdfio_textrendering_t">pdfio_textrendering_t</a></li>
<li><a href="#pdfio_valtype_t">pdfio_valtype_t</a></li>
<li><a href="#state_t[4][4]">state_t[4][4]</a></li>
</ul></li>
<li><a href="#STRUCTURES">Structures</a><ul class="subcontents">
<li><a href="#pdfio_rect_s">pdfio_rect_s</a></li>
</ul></li>
<li><a href="#ENUMERATIONS">Enumerations</a><ul class="subcontents">
<li><a href="#pdfio_cs_e">pdfio_cs_e</a></li>
<li><a href="#pdfio_encryption_e">pdfio_encryption_e</a></li>
<li><a href="#pdfio_filter_e">pdfio_filter_e</a></li>
<li><a href="#pdfio_linecap_e">pdfio_linecap_e</a></li>
<li><a href="#pdfio_linejoin_e">pdfio_linejoin_e</a></li>
<li><a href="#pdfio_permission_e">pdfio_permission_e</a></li>
<li><a href="#pdfio_textrendering_e">pdfio_textrendering_e</a></li>
<li><a href="#pdfio_valtype_e">pdfio_valtype_e</a></li>
</ul></li>
@ -467,9 +477,11 @@ span.string {
<h2 class="title" id="introduction">Introduction</h2>
<p>PDFio is a simple C library for reading and writing PDF files. The primary goals of pdfio are:</p>
<ul>
<li><p>Read any PDF file with or without encryption or linearization</p>
<li><p>Read and write any version of PDF file</p>
</li>
<li><p>Write PDF files without encryption or linearization</p>
<li><p>Provide access to pages, objects, and streams within a PDF file</p>
</li>
<li><p>Support reading and writing of encrypted PDF files</p>
</li>
<li><p>Extract or embed useful metadata (author, creator, page information, etc.)</p>
</li>
@ -545,7 +557,7 @@ make install-shared
<pre><code>sudo xcodebuild install
</code></pre>
<p>You can reproduce this with the makefile using:</p>
<pre><code>sudo make 'COMMONFLAGS=&quot;-Os -mmacosx-version-min=10.14 -arch x86_64 -arch arm64&quot;' install
<pre><code>sudo make COMMONFLAGS=&quot;-Os -mmacosx-version-min=10.14 -arch x86_64 -arch arm64&quot; install
</code></pre>
<h3 class="title" id="detecting-pdfio">Detecting PDFio</h3>
<p>PDFio can be detected using the <code>pkg-config</code> command, for example:</p>
@ -557,7 +569,7 @@ fi
<pre><code class="language-make">CFLAGS += `pkg-config --cflags pdfio`
LIBS += `pkg-config --libs pdfio`
</code></pre>
<p>On Windows, you need to link to the <code>PDFIO.LIB</code> (static) or <code>PDFIO1.LIB</code> (DLL) libraries and include the &quot;zlib&quot; NuGet package dependency.</p>
<p>On Windows, you need to link to the <code>PDFIO1.LIB</code> (DLL) library and include the <code>zlib_native</code> NuGet package dependency. You can also use the published <code>pdfio_native</code> NuGet package.</p>
<h3 class="title" id="header-files">Header Files</h3>
<p>PDFio provides a primary header file that is always used:</p>
<pre><code class="language-c"><span class="directive">#include &lt;pdfio.h&gt;</span>
@ -621,6 +633,12 @@ pdfio_rect_t crop_box = { <span class="number">36.0</span>, <span class="number"
pdfio_file_t *pdf = pdfioFileCreate(<span class="string">&quot;myoutputfile.pdf&quot;</span>, <span class="string">&quot;2.0&quot;</span>, &amp;media_box, &amp;crop_box, error_cb, error_data);
</code></pre>
<p>where the six arguments to the function are the filename (&quot;myoutputfile.pdf&quot;), PDF version (&quot;2.0&quot;), media box (<code>media_box</code>), crop box (<code>crop_box</code>), an optional error callback function (<code>error_cb</code>), and an optional pointer value for the error callback function (<code>error_data</code>). The units for the media and crop boxes are points (1/72nd of an inch).</p>
<p>Alternately you can stream a PDF file using the <a href="#pdfioFileCreateOutput"><code>pdfioFileCreateOutput</code></a> function:</p>
<pre><code class="language-c">pdfio_rect_t media_box = { <span class="number">0.0</span>, <span class="number">0.0</span>, <span class="number">612.0</span>, <span class="number">792.0</span> }; <span class="comment">// US Letter</span>
pdfio_rect_t crop_box = { <span class="number">36.0</span>, <span class="number">36.0</span>, <span class="number">576.0</span>, <span class="number">756.0</span> }; <span class="comment">// w/0.5&quot; margins</span>
pdfio_file_t *pdf = pdfioFileCreateOutput(output_cb, output_ctx, <span class="string">&quot;2.0&quot;</span>, &amp;media_box, &amp;crop_box, error_cb, error_data);
</code></pre>
<p>Once the file is created, use the <a href="#pdfioFileCreateObj"><code>pdfioFileCreateObj</code></a>, <a href="#pdfioFileCreatePage"><code>pdfioFileCreatePage</code></a>, and <a href="#pdfioPageCopy"><code>pdfioPageCopy</code></a> functions to create objects and pages in the file.</p>
<p>Finally, the <a href="#pdfioFileClose"><code>pdfioFileClose</code></a> function writes the PDF cross-reference and &quot;trailer&quot; information, closes the file, and frees all memory that was used for it.</p>
<h3 class="title" id="pdf-objects">PDF Objects</h3>
@ -1554,7 +1572,7 @@ bool pdfioContentSave(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st);</p>
<h3 class="function"><a id="pdfioContentSetDashPattern">pdfioContentSetDashPattern</a></h3>
<p class="description">Set the stroke pattern.</p>
<p class="code">
bool pdfioContentSetDashPattern(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, int phase, int on, int off);</p>
bool pdfioContentSetDashPattern(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, double phase, double on, double off);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>st</th>
@ -2444,6 +2462,20 @@ bool pdfioFileClose(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf);</p>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">PDF file or <code>NULL</code> on error</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function creates a new PDF file. The &quot;filename&quot; argument specifies the
name of the PDF file to create.<br>
<br>
The &quot;version&quot; argument specifies the PDF version number for the file or
<code>NULL</code> for the default (&quot;2.0&quot;).<br>
<br>
The &quot;media_box&quot; and &quot;crop_box&quot; arguments specify the default MediaBox and
CropBox for pages in the PDF file - if <code>NULL</code> then a default &quot;Universal&quot; size
of 8.27x11in (the intersection of US Letter and ISO A4) is used.<br>
<br>
The &quot;error_cb&quot; and &quot;error_data&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> the default error handler is used that
writes error messages to <code>stderr</code>.</p>
<h3 class="function"><a id="pdfioFileCreateArrayObj">pdfioFileCreateArrayObj</a></h3>
<p class="description">Create a new object in a PDF file containing an array.</p>
<p class="code">
@ -2621,6 +2653,57 @@ is supported.</blockquote>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">New object</p>
<h3 class="function"><a id="pdfioFileCreateOutput">pdfioFileCreateOutput</a></h3>
<p class="description">Create a PDF file through an output callback.</p>
<p class="code">
<a href="#pdfio_file_t">pdfio_file_t</a> *pdfioFileCreateOutput(<a href="#pdfio_output_cb_t">pdfio_output_cb_t</a> output_cb, void *output_ctx, const char *version, <a href="#pdfio_rect_t">pdfio_rect_t</a> *media_box, <a href="#pdfio_rect_t">pdfio_rect_t</a> *crop_box, <a href="#pdfio_error_cb_t">pdfio_error_cb_t</a> error_cb, void *error_data);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>output_cb</th>
<td class="description">Output callback</td></tr>
<tr><th>output_ctx</th>
<td class="description">Output context</td></tr>
<tr><th>version</th>
<td class="description">PDF version number or <code>NULL</code> for default (2.0)</td></tr>
<tr><th>media_box</th>
<td class="description">Default MediaBox for pages</td></tr>
<tr><th>crop_box</th>
<td class="description">Default CropBox for pages</td></tr>
<tr><th>error_cb</th>
<td class="description">Error callback or <code>NULL</code> for default</td></tr>
<tr><th>error_data</th>
<td class="description">Error callback data, if any</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">PDF file or <code>NULL</code> on error</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function creates a new PDF file that is streamed though an output
callback. The &quot;output_cb&quot; and &quot;output_ctx&quot; arguments specify the output
callback and its context pointer which is called whenever data needs to be
written:
<pre>
ssize_t
output_cb(void *output_ctx, const void *buffer, size_t bytes)
{
// Write buffer to output and return the number of bytes written
}
</pre>
The &quot;version&quot; argument specifies the PDF version number for the file or
<code>NULL</code> for the default (&quot;2.0&quot;).<br>
<br>
The &quot;media_box&quot; and &quot;crop_box&quot; arguments specify the default MediaBox and
CropBox for pages in the PDF file - if <code>NULL</code> then a default &quot;Universal&quot; size
of 8.27x11in (the intersection of US Letter and ISO A4) is used.<br>
<br>
The &quot;error_cb&quot; and &quot;error_data&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> the default error handler is used that
writes error messages to <code>stderr</code>.<br>
<br>
</p><blockquote>
<em>Note</em>: Files created using this API are slightly larger than those
created using the <a href="#pdfioFileCreate"><code>pdfioFileCreate</code></a> function since stream lengths are
stored as indirect object references.</blockquote>
<h3 class="function"><a id="pdfioFileCreatePage">pdfioFileCreatePage</a></h3>
<p class="description">Create a page in a PDF file.</p>
<p class="code">
@ -2764,6 +2847,22 @@ size_t pdfioFileGetNumPages(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf);</p>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Object</p>
<h3 class="function"><a id="pdfioFileGetPermissions">pdfioFileGetPermissions</a></h3>
<p class="description">Get the access permissions of a PDF file.</p>
<p class="code">
<a href="#pdfio_permission_t">pdfio_permission_t</a> pdfioFileGetPermissions(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <a href="#pdfio_encryption_t">pdfio_encryption_t</a> *encryption);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
<tr><th>encryption</th>
<td class="description">Type of encryption used or <code>NULL</code> to ignore</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Permission bits</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function returns the access permissions of a PDF file and (optionally)
the type of encryption that has been used.</p>
<h3 class="function"><a id="pdfioFileGetProducer">pdfioFileGetProducer</a></h3>
<p class="description">Get the producer string for a PDF file.</p>
<p class="code">
@ -2811,11 +2910,15 @@ const char *pdfioFileGetVersion(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf);<
<h3 class="function"><a id="pdfioFileOpen">pdfioFileOpen</a></h3>
<p class="description">Open a PDF file for reading.</p>
<p class="code">
<a href="#pdfio_file_t">pdfio_file_t</a> *pdfioFileOpen(const char *filename, <a href="#pdfio_error_cb_t">pdfio_error_cb_t</a> error_cb, void *error_data);</p>
<a href="#pdfio_file_t">pdfio_file_t</a> *pdfioFileOpen(const char *filename, <a href="#pdfio_password_cb_t">pdfio_password_cb_t</a> password_cb, void *password_data, <a href="#pdfio_error_cb_t">pdfio_error_cb_t</a> error_cb, void *error_data);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>filename</th>
<td class="description">Filename</td></tr>
<tr><th>password_cb</th>
<td class="description">Password callback or <code>NULL</code> for none</td></tr>
<tr><th>password_data</th>
<td class="description">Password callback data, if any</td></tr>
<tr><th>error_cb</th>
<td class="description">Error callback or <code>NULL</code> for default</td></tr>
<tr><th>error_data</th>
@ -2823,6 +2926,19 @@ const char *pdfioFileGetVersion(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf);<
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">PDF file</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function opens an existing PDF file. The &quot;filename&quot; argument specifies
the name of the PDF file to create.<br>
<br>
The &quot;password_cb&quot; and &quot;password_data&quot; arguments specify a password callback
and its data pointer for PDF files that use one of the standard Adobe
&quot;security&quot; handlers. The callback returns a password string or <code>NULL</code> to
cancel the open. If <code>NULL</code> is specified for the callback function and the
PDF file requires a password, the open will always fail.<br>
<br>
The &quot;error_cb&quot; and &quot;error_data&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> the default error handler is used that
writes error messages to <code>stderr</code>.</p>
<h3 class="function"><a id="pdfioFileSetAuthor">pdfioFileSetAuthor</a></h3>
<p class="description">Set the author for a PDF file.</p>
<p class="code">
@ -2867,6 +2983,34 @@ void pdfioFileSetKeywords(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, const c
<tr><th>value</th>
<td class="description">Value</td></tr>
</tbody></table>
<h3 class="function"><a id="pdfioFileSetPermissions">pdfioFileSetPermissions</a></h3>
<p class="description">Set the PDF permissions, encryption mode, and passwords.</p>
<p class="code">
bool pdfioFileSetPermissions(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <a href="#pdfio_permission_t">pdfio_permission_t</a> permissions, <a href="#pdfio_encryption_t">pdfio_encryption_t</a> encryption, const char *owner_password, const char *user_password);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
<tr><th>permissions</th>
<td class="description">Use permissions</td></tr>
<tr><th>encryption</th>
<td class="description">Type of encryption to use</td></tr>
<tr><th>owner_password</th>
<td class="description">Owner password, if any</td></tr>
<tr><th>user_password</th>
<td class="description">User password, if any</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> otherwise</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function sets the PDF usage permissions, encryption mode, and
passwords.<br>
<br>
</p><blockquote>
<em>Note</em>: This function must be called before creating or copying any
objects. Due to fundamental limitations in the PDF format, PDF encryption
offers little protection from disclosure. Permissions are not enforced in
any meaningful way.</blockquote>
<h3 class="function"><a id="pdfioFileSetSubject">pdfioFileSetSubject</a></h3>
<p class="description">Set the subject for a PDF file.</p>
<p class="code">
@ -3306,6 +3450,11 @@ typedef enum <a href="#pdfio_cs_e">pdfio_cs_e</a> pdfio_cs_t;
<p class="code">
typedef struct _pdfio_dict_s pdfio_dict_t;
</p>
<h3 class="typedef"><a id="pdfio_encryption_t">pdfio_encryption_t</a></h3>
<p class="description">PDF encryption modes</p>
<p class="code">
typedef enum <a href="#pdfio_encryption_e">pdfio_encryption_e</a> pdfio_encryption_t;
</p>
<h3 class="typedef"><a id="pdfio_error_cb_t">pdfio_error_cb_t</a></h3>
<p class="description">Error callback</p>
<p class="code">
@ -3341,6 +3490,21 @@ typedef double pdfio_matrix_t[3][2];
<p class="code">
typedef struct _pdfio_obj_s pdfio_obj_t;
</p>
<h3 class="typedef"><a id="pdfio_output_cb_t">pdfio_output_cb_t</a></h3>
<p class="description">Output callback for pdfioFileCreateOutput</p>
<p class="code">
typedef ssize_t (*pdfio_output_cb_t)(void *ctx const void *data size_t datalen);
</p>
<h3 class="typedef"><a id="pdfio_password_cb_t">pdfio_password_cb_t</a></h3>
<p class="description">Password callback for pdfioFileOpen</p>
<p class="code">
typedef const char *(*pdfio_password_cb_t)(void *data const char *filename);
</p>
<h3 class="typedef"><a id="pdfio_permission_t">pdfio_permission_t</a></h3>
<p class="description">PDF permission bitfield</p>
<p class="code">
typedef int pdfio_permission_t;
</p>
<h3 class="typedef"><a id="pdfio_rect_t">pdfio_rect_t</a></h3>
<p class="description">PDF rectangle</p>
<p class="code">
@ -3361,6 +3525,11 @@ typedef enum <a href="#pdfio_textrendering_e">pdfio_textrendering_e</a> pdfio_te
<p class="code">
typedef enum <a href="#pdfio_valtype_e">pdfio_valtype_e</a> pdfio_valtype_t;
</p>
<h3 class="typedef"><a id="state_t[4][4]">state_t[4][4]</a></h3>
<p class="description">4x4 AES state table</p>
<p class="code">
typedef uint8_t state_t[4][4];
</p>
<h2 class="title"><a id="STRUCTURES">Structures</a></h2>
<h3 class="struct"><a id="pdfio_rect_s">pdfio_rect_s</a></h3>
<p class="description">PDF rectangle</p>
@ -3390,6 +3559,16 @@ typedef enum <a href="#pdfio_valtype_e">pdfio_valtype_e</a> pdfio_valtype_t;
<tr><th>PDFIO_CS_P3_D65 </th><td class="description">Display P3</td></tr>
<tr><th>PDFIO_CS_SRGB </th><td class="description">sRGB</td></tr>
</tbody></table>
<h3 class="enumeration"><a id="pdfio_encryption_e">pdfio_encryption_e</a></h3>
<p class="description">PDF encryption modes</p>
<h4 class="constants">Constants</h4>
<table class="list"><tbody>
<tr><th>PDFIO_ENCRYPTION_AES_128 </th><td class="description">128-bit AES encryption (PDF 1.6)</td></tr>
<tr><th>PDFIO_ENCRYPTION_AES_256 </th><td class="description">256-bit AES encryption (PDF 2.0)</td></tr>
<tr><th>PDFIO_ENCRYPTION_NONE </th><td class="description">No encryption</td></tr>
<tr><th>PDFIO_ENCRYPTION_RC4_128 </th><td class="description">128-bit RC4 encryption (PDF 1.4)</td></tr>
<tr><th>PDFIO_ENCRYPTION_RC4_40 </th><td class="description">40-bit RC4 encryption (PDF 1.3)</td></tr>
</tbody></table>
<h3 class="enumeration"><a id="pdfio_filter_e">pdfio_filter_e</a></h3>
<p class="description">Compression/decompression filters for streams</p>
<h4 class="constants">Constants</h4>
@ -3422,6 +3601,21 @@ typedef enum <a href="#pdfio_valtype_e">pdfio_valtype_e</a> pdfio_valtype_t;
<tr><th>PDFIO_LINEJOIN_MITER </th><td class="description">Miter joint</td></tr>
<tr><th>PDFIO_LINEJOIN_ROUND </th><td class="description">Round joint</td></tr>
</tbody></table>
<h3 class="enumeration"><a id="pdfio_permission_e">pdfio_permission_e</a></h3>
<p class="description">PDF permission bits</p>
<h4 class="constants">Constants</h4>
<table class="list"><tbody>
<tr><th>PDFIO_PERMISSION_ANNOTATE </th><td class="description">PDF allows annotation</td></tr>
<tr><th>PDFIO_PERMISSION_ASSEMBLE </th><td class="description">PDF allows assembly (insert, delete, or rotate pages, add document outlines and thumbnails)</td></tr>
<tr><th>PDFIO_PERMISSION_COPY </th><td class="description">PDF allows copying</td></tr>
<tr><th>PDFIO_PERMISSION_FORMS </th><td class="description">PDF allows filling in forms</td></tr>
<tr><th>PDFIO_PERMISSION_MODIFY </th><td class="description">PDF allows modification</td></tr>
<tr><th>PDFIO_PERMISSION_NONE </th><td class="description">No permissions</td></tr>
<tr><th>PDFIO_PERMISSION_PRINT </th><td class="description">PDF allows printing</td></tr>
<tr><th>PDFIO_PERMISSION_PRINT_HIGH </th><td class="description">PDF allows high quality printing</td></tr>
<tr><th>PDFIO_PERMISSION_READING </th><td class="description">PDF allows screen reading/accessibility (deprecated in PDF 2.0)</td></tr>
<tr><th>~0 </th><td class="description">All permissions</td></tr>
</tbody></table>
<h3 class="enumeration"><a id="pdfio_textrendering_e">pdfio_textrendering_e</a></h3>
<p class="description">Text rendering modes</p>
<h4 class="constants">Constants</h4>

View File

@ -4,8 +4,9 @@ Introduction
PDFio is a simple C library for reading and writing PDF files. The primary
goals of pdfio are:
- Read any PDF file with or without encryption or linearization
- Write PDF files without encryption or linearization
- Read and write any version of PDF file
- Provide access to pages, objects, and streams within a PDF file
- Support reading and writing of encrypted PDF files
- Extract or embed useful metadata (author, creator, page information, etc.)
- "Filter" PDF files, for example to extract a range of pages or to embed fonts
that are missing from a PDF
@ -103,7 +104,7 @@ generates a static library that will be installed under "/usr/local" with:
You can reproduce this with the makefile using:
sudo make 'COMMONFLAGS="-Os -mmacosx-version-min=10.14 -arch x86_64 -arch arm64"' install
sudo make COMMONFLAGS="-Os -mmacosx-version-min=10.14 -arch x86_64 -arch arm64" install
Detecting PDFio
@ -122,8 +123,9 @@ CFLAGS += `pkg-config --cflags pdfio`
LIBS += `pkg-config --libs pdfio`
```
On Windows, you need to link to the `PDFIO.LIB` (static) or `PDFIO1.LIB` (DLL)
libraries and include the "zlib" NuGet package dependency.
On Windows, you need to link to the `PDFIO1.LIB` (DLL) library and include the
`zlib_native` NuGet package dependency. You can also use the published
`pdfio_native` NuGet package.
Header Files
@ -235,6 +237,16 @@ error callback function (`error_cb`), and an optional pointer value for the
error callback function (`error_data`). The units for the media and crop boxes
are points (1/72nd of an inch).
Alternately you can stream a PDF file using the [`pdfioFileCreateOutput`](@@)
function:
```c
pdfio_rect_t media_box = { 0.0, 0.0, 612.0, 792.0 }; // US Letter
pdfio_rect_t crop_box = { 36.0, 36.0, 576.0, 756.0 }; // w/0.5" margins
pdfio_file_t *pdf = pdfioFileCreateOutput(output_cb, output_ctx, "2.0", &media_box, &crop_box, error_cb, error_data);
```
Once the file is created, use the [`pdfioFileCreateObj`](@@),
[`pdfioFileCreatePage`](@@), and [`pdfioPageCopy`](@@) functions to create
objects and pages in the file.

523
pdfio-aes.c Normal file
View File

@ -0,0 +1,523 @@
//
// AES functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// AES code is adapted from the "tiny-AES-c" project
// (<https://github.com/kokke/tiny-AES-c>)
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
//
// Local types...
//
typedef uint8_t state_t[4][4]; // 4x4 AES state table
//
// Local globals...
//
static const uint8_t sbox[256] = // S-box lookup table
{
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
static const uint8_t rsbox[256] = // Reverse S-box lookup table
{
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};
// The round constant word array, Rcon[i], contains the values given by
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] = // Round constants
{
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
};
//
// Local functions...
//
static void AddRoundKey(size_t round, state_t *state, const uint8_t *RoundKey);
static void SubBytes(state_t *state);
static void ShiftRows(state_t *state);
static uint8_t xtime(uint8_t x);
static void MixColumns(state_t *state);
static uint8_t Multiply(uint8_t x, uint8_t y);
static void InvMixColumns(state_t *state);
static void InvSubBytes(state_t *state);
static void InvShiftRows(state_t *state);
static void Cipher(state_t *state, const _pdfio_aes_t *ctx);
static void InvCipher(state_t *state, const _pdfio_aes_t *ctx);
static void XorWithIv(uint8_t *buf, const uint8_t *Iv);
//
// '_pdfioCryptoAESInit()' - Initialize an AES context.
//
void
_pdfioCryptoAESInit(
_pdfio_aes_t *ctx, // I - AES context
const uint8_t *key, // I - Key
size_t keylen, // I - Length of key (must be 16 or 32)
const uint8_t *iv) // I - 16-byte initialization vector
{
size_t i; // Looping var
uint8_t *rkptr0, // Previous round_key values
*rkptr, // Current round_key values
*rkend, // End of round_key values
tempa[4]; // Used for the column/row operations
// size_t roundlen = keylen + 24; // Length of round_key
size_t nwords = keylen / 4; // Number of 32-bit words in key
// Clear context
memset(ctx, 0, sizeof(_pdfio_aes_t));
ctx->round_size = keylen / 4 + 6;
// The first round key is the key itself.
memcpy(ctx->round_key, key, keylen);
// All other round keys are found from the previous round keys.
for (rkptr0 = ctx->round_key, rkptr = rkptr0 + keylen, rkend = rkptr + 16 * ctx->round_size, i = nwords; rkptr < rkend; i ++)
{
if ((i % nwords) == 0)
{
// Shifts word left once - [a0,a1,a2,a3] becomes [a1,a2,a3,a0], then
// apply the S-box to each of the four bytes to produce an output word.
tempa[0] = sbox[rkptr[-3]] ^ Rcon[i / nwords];
tempa[1] = sbox[rkptr[-2]];
tempa[2] = sbox[rkptr[-1]];
tempa[3] = sbox[rkptr[-4]];
}
else if (keylen == 32 && (i % nwords) == 4)
{
// Apply the S-box to each of the four bytes to produce an output word.
tempa[0] = sbox[rkptr[-4]];
tempa[1] = sbox[rkptr[-3]];
tempa[2] = sbox[rkptr[-2]];
tempa[3] = sbox[rkptr[-1]];
}
else
{
// Use unshifted values without S-box...
tempa[0] = rkptr[-4];
tempa[1] = rkptr[-3];
tempa[2] = rkptr[-2];
tempa[3] = rkptr[-1];
}
// TODO: Optimize to incorporate this into previous steps
*rkptr++ = *rkptr0++ ^ tempa[0];
*rkptr++ = *rkptr0++ ^ tempa[1];
*rkptr++ = *rkptr0++ ^ tempa[2];
*rkptr++ = *rkptr0++ ^ tempa[3];
}
// Copy the initialization vector...
if (iv)
memcpy(ctx->iv, iv, sizeof(ctx->iv));
}
//
// '_pdfioCryptoAESDecrypt()' - Decrypt a block of bytes with AES.
//
// "inbuffer" and "outbuffer" can point to the same memory. Length must be a
// multiple of 16 bytes (excess is not decrypted).
//
size_t // O - Number of bytes in output buffer
_pdfioCryptoAESDecrypt(
_pdfio_aes_t *ctx, // I - AES context
uint8_t *outbuffer, // I - Output buffer
const uint8_t *inbuffer, // I - Input buffer
size_t len) // I - Number of bytes to decrypt
{
uint8_t next_iv[16]; // Next IV value
size_t outbytes = 0; // Output bytes
if (inbuffer != outbuffer)
{
// Not the most efficient, but we can optimize later - the sample AES code
// manipulates the data directly in memory and doesn't support separate
// input and output buffers...
memcpy(outbuffer, inbuffer, len);
}
while (len > 15)
{
memcpy(next_iv, outbuffer, 16);
InvCipher((state_t *)outbuffer, ctx);
XorWithIv(outbuffer, ctx->iv);
memcpy(ctx->iv, next_iv, 16);
outbuffer += 16;
len -= 16;
outbytes += 16;
}
return (outbytes);
}
//
// '_pdfioCryptoAESEncrypt()' - Encrypt a block of bytes with AES.
//
// "inbuffer" and "outbuffer" can point to the same memory. "outbuffer" must
// be a multiple of 16 bytes.
//
size_t // O - Number of bytes in output buffer
_pdfioCryptoAESEncrypt(
_pdfio_aes_t *ctx, // I - AES context
uint8_t *outbuffer, // I - Output buffer
const uint8_t *inbuffer, // I - Input buffer
size_t len) // I - Number of bytes to decrypt
{
uint8_t *iv = ctx->iv; // Current IV for CBC
size_t outbytes = 0; // Output bytes
if (len == 0)
return (0);
if (inbuffer != outbuffer)
{
// Not the most efficient, but we can optimize later - the sample AES code
// manipulates the data directly in memory and doesn't support separate
// input and output buffers...
memcpy(outbuffer, inbuffer, len);
}
while (len > 15)
{
XorWithIv(outbuffer, iv);
Cipher((state_t*)outbuffer, ctx);
iv = outbuffer;
outbuffer += 16;
len -= 16;
outbytes += 16;
}
if (len > 0)
{
// Pad the final buffer with (16 - len)...
memset(outbuffer + len, 16 - len, 16 - len);
XorWithIv(outbuffer, iv);
Cipher((state_t*)outbuffer, ctx);
iv = outbuffer;
outbytes += 16;
}
/* store Iv in ctx for next call */
memcpy(ctx->iv, iv, 16);
return (outbytes);
}
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void
AddRoundKey(size_t round, state_t *state, const uint8_t *RoundKey)
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
for (RoundKey += round * 16, i = 16; i > 0; i --, sptr ++, RoundKey ++)
*sptr ^= *RoundKey;
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void
SubBytes(state_t *state)
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
for (i = 16; i > 0; i --, sptr ++)
*sptr = sbox[*sptr];
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void
ShiftRows(state_t *state)
{
uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t temp; // Temporary value
// Rotate first row 1 columns to left
temp = sptr[1];
sptr[1] = sptr[5];
sptr[5] = sptr[9];
sptr[9] = sptr[13];
sptr[13] = temp;
// Rotate second row 2 columns to left
temp = sptr[2];
sptr[2] = sptr[10];
sptr[10] = temp;
temp = sptr[6];
sptr[6] = sptr[14];
sptr[14] = temp;
// Rotate third row 3 columns to left
temp = sptr[3];
sptr[3] = sptr[15];
sptr[15] = sptr[11];
sptr[11] = sptr[7];
sptr[7] = temp;
}
static uint8_t
xtime(uint8_t x)
{
return ((uint8_t)((x << 1) ^ ((x >> 7) * 0x1b)));
}
// MixColumns function mixes the columns of the state matrix
static void
MixColumns(state_t *state)
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t Tmp, Tm, t; // Temporary values
for (i = 4; i > 0; i --, sptr += 4)
{
t = sptr[0];
Tmp = sptr[0] ^ sptr[1] ^ sptr[2] ^ sptr[3];
Tm = sptr[0] ^ sptr[1];
Tm = xtime(Tm);
sptr[0] ^= Tm ^ Tmp;
Tm = sptr[1] ^ sptr[2];
Tm = xtime(Tm);
sptr[1] ^= Tm ^ Tmp;
Tm = sptr[2] ^ sptr[3];
Tm = xtime(Tm);
sptr[2] ^= Tm ^ Tmp;
Tm = sptr[3] ^ t;
Tm = xtime(Tm);
sptr[3] ^= Tm ^ Tmp;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^
((y>>2 & 1) * xtime(xtime(x))) ^
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void
InvMixColumns(state_t *state)
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t a, b, c, d; // Temporary values
for (i = 4; i > 0; i --)
{
a = sptr[0];
b = sptr[1];
c = sptr[2];
d = sptr[3];
*sptr++ = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
*sptr++ = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
*sptr++ = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
*sptr++ = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void
InvSubBytes(state_t *state)
{
unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state
for (i = 16; i > 0; i --, sptr ++)
*sptr = rsbox[*sptr];
}
static void
InvShiftRows(state_t *state)
{
uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t temp; // Temporary value
// Rotate first row 1 columns to right
temp = sptr[13];
sptr[13] = sptr[9];
sptr[9] = sptr[5];
sptr[5] = sptr[1];
sptr[1] = temp;
// Rotate second row 2 columns to right
temp = sptr[2];
sptr[2] = sptr[10];
sptr[10] = temp;
temp = sptr[6];
sptr[6] = sptr[14];
sptr[14] = temp;
// Rotate third row 3 columns to right
temp = sptr[3];
sptr[3] = sptr[7];
sptr[7] = sptr[11];
sptr[11] = sptr[15];
sptr[15] = temp;
}
// Cipher is the main function that encrypts the PlainText.
static void
Cipher(state_t *state, const _pdfio_aes_t *ctx)
{
size_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, ctx->round_key);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without MixColumns()
for (round = 1; round < ctx->round_size; round ++)
{
SubBytes(state);
ShiftRows(state);
MixColumns(state);
AddRoundKey(round, state, ctx->round_key);
}
// Add round key to last round
SubBytes(state);
ShiftRows(state);
AddRoundKey(ctx->round_size, state, ctx->round_key);
}
static void
InvCipher(state_t *state, const _pdfio_aes_t *ctx)
{
size_t round;
// Add the First round key to the state before starting the rounds.
AddRoundKey(ctx->round_size, state, ctx->round_key);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without InvMixColumn()
for (round = ctx->round_size - 1; ; round --)
{
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, ctx->round_key);
if (round == 0)
break;
InvMixColumns(state);
}
}
static void
XorWithIv(uint8_t *buf, const uint8_t *Iv)
{
// 16-byte block...
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
*buf++ ^= *Iv++;
}

View File

@ -357,9 +357,16 @@ _pdfioArrayDebug(pdfio_array_t *a, // I - Array
void
_pdfioArrayDelete(pdfio_array_t *a) // I - Array
{
if (a)
free(a->values);
size_t i; // Looping var
for (i = 0; i < a->num_values; i ++)
{
if (a->values[i].type == PDFIO_VALTYPE_BINARY)
free(a->values[i].value.binary.data);
}
free(a->values);
free(a);
}
@ -389,17 +396,26 @@ pdfioArrayGetBinary(
size_t n, // I - Index
size_t *length) // O - Length of string
{
if (!a || n >= a->num_values || a->values[n].type != PDFIO_VALTYPE_BINARY || !length)
if (!a || n >= a->num_values || (a->values[n].type != PDFIO_VALTYPE_BINARY && a->values[n].type != PDFIO_VALTYPE_STRING))
{
if (length)
*length = 0;
return (NULL);
}
else if (a->values[n].type == PDFIO_VALTYPE_BINARY)
{
if (length)
*length = a->values[n].value.binary.datalen;
return (a->values[n].value.binary.data);
}
else
{
*length = a->values[n].value.binary.datalen;
return (a->values[n].value.binary.data);
if (length)
*length = strlen(a->values[n].value.string);
return ((unsigned char *)a->values[n].value.string);
}
}
@ -558,6 +574,7 @@ _pdfioArrayGetValue(pdfio_array_t *a, // I - Array
pdfio_array_t * // O - New array
_pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object, if any
_pdfio_token_t *tb) // I - Token buffer/stack
{
pdfio_array_t *array; // New array
@ -568,7 +585,8 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG("_pdfioArrayRead(pdf=%p, tb=%p)\n", pdf, tb);
// Create an array...
array = pdfioArrayCreate(pdf);
if ((array = pdfioArrayCreate(pdf)) == NULL)
return (NULL);
// Read until we get "]" to end the array...
while (_pdfioTokenGet(tb, token, sizeof(token)))
@ -581,7 +599,7 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
// Push the token and decode the value...
_pdfioTokenPush(tb, token);
if (!_pdfioValueRead(pdf, tb, &value))
if (!_pdfioValueRead(pdf, obj, tb, &value))
break;
// PDFIO_DEBUG("_pdfioArrayRead(%p): Appending ", (void *)array);
@ -600,7 +618,8 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
//
bool // O - `true` on success, `false` otherwise
_pdfioArrayWrite(pdfio_array_t *a) // I - Array
_pdfioArrayWrite(pdfio_array_t *a, // I - Array
pdfio_obj_t *obj) // I - Object, if any
{
pdfio_file_t *pdf = a->pdf; // PDF file
size_t i; // Looping var
@ -614,7 +633,7 @@ _pdfioArrayWrite(pdfio_array_t *a) // I - Array
// Write each value...
for (i = a->num_values, v = a->values; i > 0; i --, v ++)
{
if (!_pdfioValueWrite(pdf, v, NULL))
if (!_pdfioValueWrite(pdf, obj, v, NULL))
return (false);
}

View File

@ -329,6 +329,11 @@ _pdfioFileRead(pdfio_file_t *pdf, // I - PDF file
pdf->bufpos += rbytes;
continue;
}
else if (rbytes < 0 && (errno == EINTR || errno == EAGAIN))
{
rbytes = 0;
continue;
}
else
break;
}
@ -377,6 +382,11 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
// No, reset the read buffer
pdf->bufptr = pdf->bufend = NULL;
}
else if (pdf->output_cb)
{
_pdfioFileError(pdf, "Unable to seek within output stream.");
return (-1);
}
else
{
// Writing, make sure we write any buffered data...
@ -530,25 +540,37 @@ write_buffer(pdfio_file_t *pdf, // I - PDF file
ssize_t wbytes; // Bytes written...
// Write to the file...
while (bytes > 0)
if (pdf->output_cb)
{
while ((wbytes = write(pdf->fd, bufptr, bytes)) < 0)
// Write to a stream...
if ((pdf->output_cb)(pdf->output_ctx, buffer, bytes) < 0)
{
// Stop if we have an error that shouldn't be retried...
if (errno != EINTR && errno != EAGAIN)
break;
}
if (wbytes < 0)
{
// Hard error...
_pdfioFileError(pdf, "Unable to write to file - %s", strerror(errno));
_pdfioFileError(pdf, "Unable to write to output callback.");
return (false);
}
}
else
{
// Write to the file...
while (bytes > 0)
{
while ((wbytes = write(pdf->fd, bufptr, bytes)) < 0)
{
// Stop if we have an error that shouldn't be retried...
if (errno != EINTR && errno != EAGAIN)
break;
}
bufptr += wbytes;
bytes -= (size_t)wbytes;
if (wbytes < 0)
{
// Hard error...
_pdfioFileError(pdf, "Unable to write to file - %s", strerror(errno));
return (false);
}
bufptr += wbytes;
bytes -= (size_t)wbytes;
}
}
return (true);

View File

@ -660,11 +660,11 @@ pdfioContentSave(pdfio_stream_t *st) // I - Stream
bool // O - `true` on success, `false` on failure
pdfioContentSetDashPattern(
pdfio_stream_t *st, // I - Stream
int phase, // I - Phase (offset within pattern)
int on, // I - On length
int off) // I - Off length
double phase, // I - Phase (offset within pattern)
double on, // I - On length
double off) // I - Off length
{
return (pdfioStreamPrintf(st, "[%d %d] %d d\n", on, off, phase));
return (pdfioStreamPrintf(st, "[%g %g] %g d\n", on, off, phase));
}
@ -2358,7 +2358,7 @@ copy_png(pdfio_dict_t *dict, // I - Dictionary
PDFIO_DEBUG("copy_png: Adding %s ColorSpace value.\n", color_type == _PDFIO_PNG_TYPE_GRAY ? "CalGray" : "CalRGB");
if (wx != 0.0)
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromPrimaries(dict->pdf, color_type == _PDFIO_PNG_TYPE_GRAY ? 1 : 3, gamma, wx, wy, rx, ry, bx, by, gx, gy));
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromPrimaries(dict->pdf, color_type == _PDFIO_PNG_TYPE_GRAY ? 1 : 3, gamma, wx, wy, rx, ry, gx, gy, bx, by));
else
pdfioDictSetArray(dict, "ColorSpace", pdfioArrayCreateColorFromStandard(dict->pdf, color_type == _PDFIO_PNG_TYPE_GRAY ? 1 : 3, PDFIO_CS_SRGB));
}

View File

@ -96,7 +96,7 @@ extern bool pdfioContentPathMoveTo(pdfio_stream_t *st, double x, double y) _PDF
extern bool pdfioContentPathRect(pdfio_stream_t *st, double x, double y, double width, double height) _PDFIO_PUBLIC;
extern bool pdfioContentRestore(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentSave(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentSetDashPattern(pdfio_stream_t *st, int phase, int on, int off) _PDFIO_PUBLIC;
extern bool pdfioContentSetDashPattern(pdfio_stream_t *st, double phase, double on, double off) _PDFIO_PUBLIC;
extern bool pdfioContentSetFillColorDeviceCMYK(pdfio_stream_t *st, double c, double m, double y, double k) _PDFIO_PUBLIC;
extern bool pdfioContentSetFillColorDeviceGray(pdfio_stream_t *st, double g) _PDFIO_PUBLIC;
extern bool pdfioContentSetFillColorDeviceRGB(pdfio_stream_t *st, double r, double g, double b) _PDFIO_PUBLIC;

1042
pdfio-crypto.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,42 @@
static int compare_pairs(_pdfio_pair_t *a, _pdfio_pair_t *b);
//
// '_pdfioDictClear()' - Remove a key/value pair from a dictionary.
//
void
_pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary
const char *key) // I - Key
{
size_t idx; // Index into pairs
_pdfio_pair_t *pair, // Current pair
pkey; // Search key
PDFIO_DEBUG("_pdfioDictClear(dict=%p, key=\"%s\")\n", dict, key);
// See if the key is already set...
if (dict->num_pairs > 0)
{
pkey.key = key;
if ((pair = (_pdfio_pair_t *)bsearch(&pkey, dict->pairs, dict->num_pairs, sizeof(_pdfio_pair_t), (int (*)(const void *, const void *))compare_pairs)) != NULL)
{
// Yes, remove it...
if (pair->value.type == PDFIO_VALTYPE_BINARY)
free(pair->value.value.binary.data);
idx = (size_t)(pair - dict->pairs);
dict->num_pairs --;
if (idx < dict->num_pairs)
memmove(pair, pair + 1, (dict->num_pairs - idx) * sizeof(_pdfio_pair_t));
}
}
}
//
// 'pdfioDictCopy()' - Copy a dictionary to a PDF file.
//
@ -154,7 +190,18 @@ void
_pdfioDictDelete(pdfio_dict_t *dict) // I - Dictionary
{
if (dict)
{
size_t i; // Looping var
_pdfio_pair_t *pair; // Current pair
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{
if (pair->value.type == PDFIO_VALTYPE_BINARY)
free(pair->value.value.binary.data);
}
free(dict->pairs);
}
free(dict);
}
@ -198,6 +245,11 @@ pdfioDictGetBinary(pdfio_dict_t *dict, // I - Dictionary
*length = value->value.binary.datalen;
return (value->value.binary.data);
}
else if (value && value->type == PDFIO_VALTYPE_STRING)
{
*length = strlen(value->value.string);
return ((unsigned char *)value->value.string);
}
else
{
*length = 0;
@ -420,6 +472,7 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary
pdfio_dict_t * // O - New dictionary
_pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object, if any
_pdfio_token_t *tb) // I - Token buffer/stack
{
pdfio_dict_t *dict; // New dictionary
@ -448,7 +501,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
}
// Then get the next value...
if (!_pdfioValueRead(pdf, tb, &value))
if (!_pdfioValueRead(pdf, obj, tb, &value))
{
_pdfioFileError(pdf, "Missing value for dictionary key.");
break;
@ -802,6 +855,8 @@ _pdfioDictSetValue(
{
// Yes, replace the value...
PDFIO_DEBUG("_pdfioDictSetValue: Replacing existing value.\n");
if (pair->value.type == PDFIO_VALTYPE_BINARY)
free(pair->value.value.binary.data);
pair->value = *value;
return (true);
}
@ -850,6 +905,7 @@ _pdfioDictSetValue(
bool // O - `true` on success, `false` on failure
_pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary
pdfio_obj_t *obj, // I - Object, if any
off_t *length) // I - Offset to length value
{
pdfio_file_t *pdf = dict->pdf; // PDF file
@ -877,7 +933,7 @@ _pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary
if (!_pdfioFilePuts(pdf, " 9999999999"))
return (false);
}
else if (!_pdfioValueWrite(pdf, &pair->value, NULL))
else if (!_pdfioValueWrite(pdf, obj, &pair->value, NULL))
return (false);
}

View File

@ -26,7 +26,7 @@ static int compare_objmaps(_pdfio_objmap_t *a, _pdfio_objmap_t *b);
static int compare_objs(pdfio_obj_t **a, pdfio_obj_t **b);
static bool load_obj_stream(pdfio_obj_t *obj);
static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj);
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset);
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset, pdfio_password_cb_t password_cb, void *password_data);
static bool write_catalog(pdfio_file_t *pdf);
static bool write_pages(pdfio_file_t *pdf);
static bool write_trailer(pdfio_file_t *pdf);
@ -123,14 +123,14 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
{
ret = false;
if (pdfioObjClose(pdf->info))
if (pdfioObjClose(pdf->info_obj))
if (write_pages(pdf))
if (write_catalog(pdf))
if (write_trailer(pdf))
ret = _pdfioFileFlush(pdf);
}
if (close(pdf->fd) < 0)
if (pdf->fd >= 0 && close(pdf->fd) < 0)
ret = false;
// Free all data...
@ -166,6 +166,20 @@ pdfioFileClose(pdfio_file_t *pdf) // I - PDF file
//
// 'pdfioFileCreate()' - Create a PDF file.
//
// This function creates a new PDF file. The "filename" argument specifies the
// name of the PDF file to create.
//
// The "version" argument specifies the PDF version number for the file or
// `NULL` for the default ("2.0").
//
// The "media_box" and "crop_box" arguments specify the default MediaBox and
// CropBox for pages in the PDF file - if `NULL` then a default "Universal" size
// of 8.27x11in (the intersection of US Letter and ISO A4) is used.
//
// The "error_cb" and "error_data" arguments specify an error handler callback
// and its data pointer - if `NULL` the default error handler is used that
// writes error messages to `stderr`.
//
pdfio_file_t * // O - PDF file or `NULL` on error
pdfioFileCreate(
@ -179,6 +193,7 @@ pdfioFileCreate(
pdfio_file_t *pdf; // PDF file
pdfio_dict_t *dict; // Dictionary for pages object
pdfio_dict_t *info_dict; // Dictionary for information object
unsigned char id_value[16]; // File ID value
// Range check input...
@ -206,13 +221,14 @@ pdfioFileCreate(
return (NULL);
}
pdf->filename = strdup(filename);
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
pdf->filename = strdup(filename);
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->permissions = PDFIO_PERMISSION_ALL;
pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
if (media_box)
{
@ -264,7 +280,7 @@ pdfioFileCreate(
pdfioDictSetName(dict, "Type", "Pages");
if ((pdf->pages_root = pdfioFileCreateObj(pdf, dict)) == NULL)
if ((pdf->pages_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
{
pdfioFileClose(pdf);
unlink(filename);
@ -282,13 +298,22 @@ pdfioFileCreate(
pdfioDictSetDate(info_dict, "CreationDate", time(NULL));
pdfioDictSetString(info_dict, "Producer", "pdfio/" PDFIO_VERSION);
if ((pdf->info = pdfioFileCreateObj(pdf, info_dict)) == NULL)
if ((pdf->info_obj = pdfioFileCreateObj(pdf, info_dict)) == NULL)
{
pdfioFileClose(pdf);
unlink(filename);
return (NULL);
}
// Create random file ID values...
_pdfioCryptoMakeRandom(id_value, sizeof(id_value));
if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL)
{
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
}
return (pdf);
}
@ -399,6 +424,166 @@ _pdfioFileCreateObj(
}
//
// 'pdfioFileCreateOutput()' - Create a PDF file through an output callback.
//
// This function creates a new PDF file that is streamed though an output
// callback. The "output_cb" and "output_ctx" arguments specify the output
// callback and its context pointer which is called whenever data needs to be
// written:
//
// ```
// ssize_t
// output_cb(void *output_ctx, const void *buffer, size_t bytes)
// {
// // Write buffer to output and return the number of bytes written
// }
// ```
//
// The "version" argument specifies the PDF version number for the file or
// `NULL` for the default ("2.0").
//
// The "media_box" and "crop_box" arguments specify the default MediaBox and
// CropBox for pages in the PDF file - if `NULL` then a default "Universal" size
// of 8.27x11in (the intersection of US Letter and ISO A4) is used.
//
// The "error_cb" and "error_data" arguments specify an error handler callback
// and its data pointer - if `NULL` the default error handler is used that
// writes error messages to `stderr`.
//
// > *Note*: Files created using this API are slightly larger than those
// > created using the @link pdfioFileCreate@ function since stream lengths are
// > stored as indirect object references.
//
pdfio_file_t * // O - PDF file or `NULL` on error
pdfioFileCreateOutput(
pdfio_output_cb_t output_cb, // I - Output callback
void *output_ctx, // I - Output context
const char *version, // I - PDF version number or `NULL` for default (2.0)
pdfio_rect_t *media_box, // I - Default MediaBox for pages
pdfio_rect_t *crop_box, // I - Default CropBox for pages
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
void *error_data) // I - Error callback data, if any
{
pdfio_file_t *pdf; // PDF file
pdfio_dict_t *dict; // Dictionary for pages object
pdfio_dict_t *info_dict; // Dictionary for information object
unsigned char id_value[16]; // File ID value
// Range check input...
if (!output_cb)
return (NULL);
if (!version)
version = "2.0";
if (!error_cb)
{
error_cb = _pdfioFileDefaultError;
error_data = NULL;
}
// Allocate a PDF file structure...
if ((pdf = (pdfio_file_t *)calloc(1, sizeof(pdfio_file_t))) == NULL)
{
pdfio_file_t temp; // Dummy file
char message[8192]; // Message string
temp.filename = (char *)"output.pdf";
snprintf(message, sizeof(message), "Unable to allocate memory for PDF file - %s", strerror(errno));
(error_cb)(&temp, message, error_data);
return (NULL);
}
pdf->filename = strdup("output.pdf");
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->permissions = PDFIO_PERMISSION_ALL;
pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
if (media_box)
{
pdf->media_box = *media_box;
}
else
{
// Default to "universal" size (intersection of A4 and US Letter)
pdf->media_box.x2 = 210.0 * 72.0f / 25.4f;
pdf->media_box.y2 = 11.0f * 72.0f;
}
if (crop_box)
{
pdf->crop_box = *crop_box;
}
else
{
// Default to "universal" size (intersection of A4 and US Letter)
pdf->crop_box.x2 = 210.0 * 72.0f / 25.4f;
pdf->crop_box.y2 = 11.0f * 72.0f;
}
// Save output callback...
pdf->fd = -1;
pdf->output_cb = output_cb;
pdf->output_ctx = output_ctx;
// Write a standard PDF header...
if (!_pdfioFilePrintf(pdf, "%%PDF-%s\n%%\342\343\317\323\n", version))
{
pdfioFileClose(pdf);
return (NULL);
}
// Create the pages object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
pdfioDictSetName(dict, "Type", "Pages");
if ((pdf->pages_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
// Create the info object...
if ((info_dict = pdfioDictCreate(pdf)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
pdfioDictSetDate(info_dict, "CreationDate", time(NULL));
pdfioDictSetString(info_dict, "Producer", "pdfio/" PDFIO_VERSION);
if ((pdf->info_obj = pdfioFileCreateObj(pdf, info_dict)) == NULL)
{
pdfioFileClose(pdf);
return (NULL);
}
// Create random file ID values...
_pdfioCryptoMakeRandom(id_value, sizeof(id_value));
if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL)
{
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value));
}
return (pdf);
}
//
// 'pdfioFileCreatePage()' - Create a page in a PDF file.
//
@ -432,7 +617,7 @@ pdfioFileCreatePage(pdfio_file_t *pdf, // I - PDF file
if (!_pdfioDictGetValue(dict, "MediaBox"))
pdfioDictSetRect(dict, "MediaBox", &pdf->media_box);
pdfioDictSetObj(dict, "Parent", pdf->pages_root);
pdfioDictSetObj(dict, "Parent", pdf->pages_obj);
if (!_pdfioDictGetValue(dict, "Resources"))
pdfioDictSetDict(dict, "Resources", pdfioDictCreate(pdf));
@ -538,7 +723,7 @@ pdfioFileFindObj(
const char * // O - Author or `NULL` for none
pdfioFileGetAuthor(pdfio_file_t *pdf) // I - PDF file
{
return (pdf && pdf->info ? pdfioDictGetString(pdf->info->value.value.dict, "Author") : NULL);
return (pdf && pdf->info_obj ? pdfioDictGetString(pdf->info_obj->value.value.dict, "Author") : NULL);
}
@ -550,7 +735,7 @@ time_t // O - Creation date or `0` for none
pdfioFileGetCreationDate(
pdfio_file_t *pdf) // I - PDF file
{
return (pdf && pdf->info ? pdfioDictGetDate(pdf->info->value.value.dict, "CreationDate") : 0);
return (pdf && pdf->info_obj ? pdfioDictGetDate(pdf->info_obj->value.value.dict, "CreationDate") : 0);
}
@ -561,7 +746,7 @@ pdfioFileGetCreationDate(
const char * // O - Creator string or `NULL` for none
pdfioFileGetCreator(pdfio_file_t *pdf) // I - PDF file
{
return (pdf && pdf->info ? pdfioDictGetString(pdf->info->value.value.dict, "Creator") : NULL);
return (pdf && pdf->info_obj ? pdfioDictGetString(pdf->info_obj->value.value.dict, "Creator") : NULL);
}
@ -572,7 +757,7 @@ pdfioFileGetCreator(pdfio_file_t *pdf) // I - PDF file
pdfio_array_t * // O - Array with binary strings
pdfioFileGetID(pdfio_file_t *pdf) // I - PDF file
{
return (pdf && pdf->info ? pdfioDictGetArray(pdf->trailer, "ID") : NULL);
return (pdf ? pdf->id_array : NULL);
}
@ -583,7 +768,7 @@ pdfioFileGetID(pdfio_file_t *pdf) // I - PDF file
const char * // O - Keywords string or `NULL` for none
pdfioFileGetKeywords(pdfio_file_t *pdf) // I - PDF file
{
return (pdf && pdf->info ? pdfioDictGetString(pdf->info->value.value.dict, "Keywords") : NULL);
return (pdf && pdf->info_obj ? pdfioDictGetString(pdf->info_obj->value.value.dict, "Keywords") : NULL);
}
@ -651,6 +836,35 @@ pdfioFileGetPage(pdfio_file_t *pdf, // I - PDF file
}
//
// 'pdfioFileGetPermissions()' - Get the access permissions of a PDF file.
//
// This function returns the access permissions of a PDF file and (optionally)
// the type of encryption that has been used.
//
pdfio_permission_t // O - Permission bits
pdfioFileGetPermissions(
pdfio_file_t *pdf, // I - PDF file
pdfio_encryption_t *encryption) // O - Type of encryption used or `NULL` to ignore
{
// Range check input...
if (!pdf)
{
if (encryption)
*encryption = PDFIO_ENCRYPTION_NONE;
return (PDFIO_PERMISSION_ALL);
}
// Return values...
if (encryption)
*encryption = pdf->encryption;
return (pdf->permissions);
}
//
// 'pdfioFileGetProducer()' - Get the producer string for a PDF file.
//
@ -658,7 +872,7 @@ pdfioFileGetPage(pdfio_file_t *pdf, // I - PDF file
const char * // O - Producer string or `NULL` for none
pdfioFileGetProducer(pdfio_file_t *pdf) // I - PDF file
{
return (pdf && pdf->info ? pdfioDictGetString(pdf->info->value.value.dict, "Producer") : NULL);
return (pdf && pdf->info_obj ? pdfioDictGetString(pdf->info_obj->value.value.dict, "Producer") : NULL);
}
@ -669,7 +883,7 @@ pdfioFileGetProducer(pdfio_file_t *pdf) // I - PDF file
const char * // O - Subject or `NULL` for none
pdfioFileGetSubject(pdfio_file_t *pdf) // I - PDF file
{
return (pdf && pdf->info ? pdfioDictGetString(pdf->info->value.value.dict, "Subject") : NULL);
return (pdf && pdf->info_obj ? pdfioDictGetString(pdf->info_obj->value.value.dict, "Subject") : NULL);
}
@ -680,7 +894,7 @@ pdfioFileGetSubject(pdfio_file_t *pdf) // I - PDF file
const char * // O - Title or `NULL` for none
pdfioFileGetTitle(pdfio_file_t *pdf) // I - PDF file
{
return (pdf && pdf->info ? pdfioDictGetString(pdf->info->value.value.dict, "Title") : NULL);
return (pdf && pdf->info_obj ? pdfioDictGetString(pdf->info_obj->value.value.dict, "Title") : NULL);
}
@ -699,12 +913,27 @@ pdfioFileGetVersion(
//
// 'pdfioFileOpen()' - Open a PDF file for reading.
//
// This function opens an existing PDF file. The "filename" argument specifies
// the name of the PDF file to create.
//
// The "password_cb" and "password_data" arguments specify a password callback
// and its data pointer for PDF files that use one of the standard Adobe
// "security" handlers. The callback returns a password string or `NULL` to
// cancel the open. If `NULL` is specified for the callback function and the
// PDF file requires a password, the open will always fail.
//
// The "error_cb" and "error_data" arguments specify an error handler callback
// and its data pointer - if `NULL` the default error handler is used that
// writes error messages to `stderr`.
//
pdfio_file_t * // O - PDF file
pdfioFileOpen(
const char *filename, // I - Filename
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
void *error_data) // I - Error callback data, if any
const char *filename, // I - Filename
pdfio_password_cb_t password_cb, // I - Password callback or `NULL` for none
void *password_data, // I - Password callback data, if any
pdfio_error_cb_t error_cb, // I - Error callback or `NULL` for default
void *error_data) // I - Error callback data, if any
{
pdfio_file_t *pdf; // PDF file
char line[1024], // Line from file
@ -734,10 +963,11 @@ pdfioFileOpen(
return (NULL);
}
pdf->filename = strdup(filename);
pdf->mode = _PDFIO_MODE_READ;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->filename = strdup(filename);
pdf->mode = _PDFIO_MODE_READ;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->permissions = PDFIO_PERMISSION_ALL;
// Open the file...
if ((pdf->fd = open(filename, O_RDONLY | O_BINARY)) < 0)
@ -784,7 +1014,7 @@ pdfioFileOpen(
xref_offset = (off_t)strtol(ptr + 9, NULL, 10);
if (!load_xref(pdf, xref_offset))
if (!load_xref(pdf, xref_offset, password_cb, password_data))
goto error;
return (pdf);
@ -807,8 +1037,8 @@ void
pdfioFileSetAuthor(pdfio_file_t *pdf, // I - PDF file
const char *value) // I - Value
{
if (pdf && pdf->info)
pdfioDictSetString(pdf->info->value.value.dict, "Author", pdfioStringCreate(pdf, value));
if (pdf && pdf->info_obj)
pdfioDictSetString(pdf->info_obj->value.value.dict, "Author", pdfioStringCreate(pdf, value));
}
@ -821,8 +1051,8 @@ pdfioFileSetCreationDate(
pdfio_file_t *pdf, // I - PDF file
time_t value) // I - Value
{
if (pdf && pdf->info)
pdfioDictSetDate(pdf->info->value.value.dict, "CreationDate", value);
if (pdf && pdf->info_obj)
pdfioDictSetDate(pdf->info_obj->value.value.dict, "CreationDate", value);
}
@ -834,8 +1064,8 @@ void
pdfioFileSetCreator(pdfio_file_t *pdf, // I - PDF file
const char *value)// I - Value
{
if (pdf && pdf->info)
pdfioDictSetString(pdf->info->value.value.dict, "Creator", pdfioStringCreate(pdf, value));
if (pdf && pdf->info_obj)
pdfioDictSetString(pdf->info_obj->value.value.dict, "Creator", pdfioStringCreate(pdf, value));
}
@ -848,8 +1078,44 @@ pdfioFileSetKeywords(
pdfio_file_t *pdf, // I - PDF file
const char *value) // I - Value
{
if (pdf && pdf->info)
pdfioDictSetString(pdf->info->value.value.dict, "Keywords", pdfioStringCreate(pdf, value));
if (pdf && pdf->info_obj)
pdfioDictSetString(pdf->info_obj->value.value.dict, "Keywords", pdfioStringCreate(pdf, value));
}
//
// 'pdfioFileSetPermissions()' - Set the PDF permissions, encryption mode, and passwords.
//
// This function sets the PDF usage permissions, encryption mode, and
// passwords.
//
// > *Note*: This function must be called before creating or copying any
// > objects. Due to fundamental limitations in the PDF format, PDF encryption
// > offers little protection from disclosure. Permissions are not enforced in
// > any meaningful way.
//
bool // O - `true` on success, `false` otherwise
pdfioFileSetPermissions(
pdfio_file_t *pdf, // I - PDF file
pdfio_permission_t permissions, // I - Use permissions
pdfio_encryption_t encryption, // I - Type of encryption to use
const char *owner_password, // I - Owner password, if any
const char *user_password) // I - User password, if any
{
if (!pdf)
return (false);
if (pdf->num_objs > 2) // First two objects are pages and info
{
_pdfioFileError(pdf, "You must call pdfioFileSetPermissions before adding any objects.");
return (false);
}
if (encryption == PDFIO_ENCRYPTION_NONE)
return (true);
return (_pdfioCryptoLock(pdf, permissions, encryption, owner_password, user_password));
}
@ -858,11 +1124,12 @@ pdfioFileSetKeywords(
//
void
pdfioFileSetSubject(pdfio_file_t *pdf, // I - PDF file
const char *value)// I - Value
pdfioFileSetSubject(
pdfio_file_t *pdf, // I - PDF file
const char *value) // I - Value
{
if (pdf && pdf->info)
pdfioDictSetString(pdf->info->value.value.dict, "Subject", pdfioStringCreate(pdf, value));
if (pdf && pdf->info_obj)
pdfioDictSetString(pdf->info_obj->value.value.dict, "Subject", pdfioStringCreate(pdf, value));
}
@ -874,8 +1141,8 @@ void
pdfioFileSetTitle(pdfio_file_t *pdf, // I - PDF file
const char *value) // I - Value
{
if (pdf && pdf->info)
pdfioDictSetString(pdf->info->value.value.dict, "Title", pdfioStringCreate(pdf, value));
if (pdf && pdf->info_obj)
pdfioDictSetString(pdf->info_obj->value.value.dict, "Title", pdfioStringCreate(pdf, value));
}
@ -1045,7 +1312,7 @@ load_obj_stream(pdfio_obj_t *obj) // I - Object to load
// Read the objects themselves...
for (cur_obj = 0; cur_obj < num_objs; cur_obj ++)
{
if (!_pdfioValueRead(obj->pdf, &tb, &(objs[cur_obj]->value)))
if (!_pdfioValueRead(obj->pdf, obj, &tb, &(objs[cur_obj]->value)))
{
pdfioStreamClose(st);
return (false);
@ -1132,8 +1399,11 @@ load_pages(pdfio_file_t *pdf, // I - PDF file
//
static bool // O - `true` on success, `false` on failure
load_xref(pdfio_file_t *pdf, // I - PDF file
off_t xref_offset) // I - Offset to xref
load_xref(
pdfio_file_t *pdf, // I - PDF file
off_t xref_offset, // I - Offset to xref
pdfio_password_cb_t password_cb, // I - Password callback or `NULL` for none
void *password_data) // I - Password callback data, if any
{
bool done = false; // Are we done?
char line[1024], // Line from file
@ -1144,6 +1414,7 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
offset; // Offset in file
int generation; // Generation number
_pdfio_token_t tb; // Token buffer/stack
off_t line_offset; // Offset to start of line
while (!done)
@ -1156,6 +1427,8 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
do
{
line_offset = _pdfioFileTell(pdf);
if (!_pdfioFileGets(pdf, line, sizeof(line)))
{
_pdfioFileError(pdf, "Unable to read start of xref table.");
@ -1164,7 +1437,7 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
}
while (!line[0]);
PDFIO_DEBUG("load_xref: xref_offset=%lu, line='%s'\n", (unsigned long)xref_offset, line);
PDFIO_DEBUG("load_xref: line_offset=%lu, line='%s'\n", (unsigned long)line_offset, line);
if (isdigit(line[0] & 255) && strlen(line) > 4 && (!strcmp(line + strlen(line) - 4, " obj") || ((ptr = strstr(line, " obj")) != NULL && ptr[4] == '<')))
{
@ -1207,7 +1480,11 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
return (false);
}
_pdfioFileSeek(pdf, xref_offset + ptr + 3 - line, SEEK_SET);
if (_pdfioFileSeek(pdf, line_offset + ptr + 3 - line, SEEK_SET) < 0)
{
_pdfioFileError(pdf, "Unable to seek to xref object %lu %u.", (unsigned long)number, (unsigned)generation);
return (false);
}
PDFIO_DEBUG("load_xref: Loading object %lu %u.\n", (unsigned long)number, (unsigned)generation);
@ -1219,7 +1496,7 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf);
if (!_pdfioValueRead(pdf, &tb, &trailer))
if (!_pdfioValueRead(pdf, obj, &tb, &trailer))
{
_pdfioFileError(pdf, "Unable to read cross-reference stream dictionary.");
return (false);
@ -1229,12 +1506,6 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
_pdfioFileError(pdf, "Cross-reference stream does not have a dictionary.");
return (false);
}
else if (_pdfioDictGetValue(pdf->trailer, "Encrypt"))
{
// Encryption not yet supported...
_pdfioFileError(pdf, "Sorry, PDFio currently does not support encrypted PDF files.");
return (false);
}
obj->value = trailer;
@ -1369,6 +1640,20 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
pdfioStreamClose(st);
if (!pdf->trailer_dict)
{
// Save the trailer dictionary and grab the root (catalog) and info
// objects...
pdf->trailer_dict = trailer.value.dict;
pdf->info_obj = pdfioDictGetObj(pdf->trailer_dict, "Info");
pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID");
// If the trailer contains an Encrypt key, try unlocking the file...
if (pdf->encrypt_obj && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
return (false);
}
// Load any object streams that are left...
PDFIO_DEBUG("load_xref: %lu compressed object streams to load.\n", (unsigned long)num_sobjs);
@ -1466,7 +1751,7 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf);
if (!_pdfioValueRead(pdf, &tb, &trailer))
if (!_pdfioValueRead(pdf, NULL, &tb, &trailer))
{
_pdfioFileError(pdf, "Unable to read trailer dictionary.");
return (false);
@ -1476,14 +1761,22 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
_pdfioFileError(pdf, "Trailer is not a dictionary.");
return (false);
}
else if (_pdfioDictGetValue(pdf->trailer, "Encrypt"))
{
// Encryption not yet supported...
_pdfioFileError(pdf, "Sorry, PDFio currently does not support encrypted PDF files.");
return (false);
}
_pdfioTokenFlush(&tb);
if (!pdf->trailer_dict)
{
// Save the trailer dictionary and grab the root (catalog) and info
// objects...
pdf->trailer_dict = trailer.value.dict;
pdf->info_obj = pdfioDictGetObj(pdf->trailer_dict, "Info");
pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID");
// If the trailer contains an Encrypt key, try unlocking the file...
if (pdf->encrypt_obj && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
return (false);
}
}
else
{
@ -1496,32 +1789,21 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG_VALUE(&trailer);
PDFIO_DEBUG("\n");
if (!pdf->trailer)
{
// Save the trailer dictionary and grab the root (catalog) and info
// objects...
pdf->trailer = trailer.value.dict;
}
if ((xref_offset = (off_t)pdfioDictGetNumber(trailer.value.dict, "Prev")) <= 0)
done = true;
}
// Once we have all of the xref tables loaded, get the important objects and
// build the pages array...
if ((pdf->root = pdfioDictGetObj(pdf->trailer, "Root")) == NULL)
if ((pdf->root_obj = pdfioDictGetObj(pdf->trailer_dict, "Root")) == NULL)
{
_pdfioFileError(pdf, "Missing Root object.");
return (false);
}
PDFIO_DEBUG("load_xref: Root=%p(%lu)\n", pdf->root, (unsigned long)pdf->root->number);
PDFIO_DEBUG("load_xref: Root=%p(%lu)\n", pdf->root_obj, (unsigned long)pdf->root_obj->number);
pdf->info = pdfioDictGetObj(pdf->trailer, "Info");
pdf->encrypt = pdfioDictGetObj(pdf->trailer, "Encrypt");
pdf->id_array = pdfioDictGetArray(pdf->trailer, "ID");
return (load_pages(pdf, pdfioDictGetObj(pdfioObjGetDict(pdf->root), "Pages")));
return (load_pages(pdf, pdfioDictGetObj(pdfioObjGetDict(pdf->root_obj), "Pages")));
}
@ -1539,13 +1821,13 @@ write_catalog(pdfio_file_t *pdf) // I - PDF file
return (false);
pdfioDictSetName(dict, "Type", "Catalog");
pdfioDictSetObj(dict, "Pages", pdf->pages_root);
pdfioDictSetObj(dict, "Pages", pdf->pages_obj);
// TODO: Add support for all of the root object dictionary keys
if ((pdf->root = pdfioFileCreateObj(pdf, dict)) == NULL)
if ((pdf->root_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
return (false);
else
return (pdfioObjClose(pdf->root));
return (pdfioObjClose(pdf->root_obj));
}
@ -1567,11 +1849,11 @@ write_pages(pdfio_file_t *pdf) // I - PDF file
for (i = 0; i < pdf->num_pages; i ++)
pdfioArrayAppendObj(kids, pdf->pages[i]);
pdfioDictSetNumber(pdf->pages_root->value.value.dict, "Count", pdf->num_pages);
pdfioDictSetArray(pdf->pages_root->value.value.dict, "Kids", kids);
pdfioDictSetNumber(pdf->pages_obj->value.value.dict, "Count", pdf->num_pages);
pdfioDictSetArray(pdf->pages_obj->value.value.dict, "Kids", kids);
// Write the Pages object...
return (pdfioObjClose(pdf->pages_root));
return (pdfioObjClose(pdf->pages_obj));
}
@ -1585,8 +1867,6 @@ write_trailer(pdfio_file_t *pdf) // I - PDF file
bool ret = true; // Return value
off_t xref_offset; // Offset to xref table
size_t i; // Looping var
int fd; // File for /dev/urandom
unsigned char id_values[2][16]; // ID array values
// Write the xref table...
@ -1620,34 +1900,22 @@ write_trailer(pdfio_file_t *pdf) // I - PDF file
goto done;
}
if ((fd = open("/dev/urandom", O_RDONLY)) >= 0)
{
// Load ID array with random values from /dev/urandom...
if (read(fd, id_values[0], sizeof(id_values[0])) == (ssize_t)sizeof(id_values[0]) && read(fd, id_values[1], sizeof(id_values[1])) == (ssize_t)sizeof(id_values[1]))
{
pdf->id_array = pdfioArrayCreate(pdf);
pdfioArrayAppendBinary(pdf->id_array, id_values[0], sizeof(id_values[0]));
pdfioArrayAppendBinary(pdf->id_array, id_values[1], sizeof(id_values[1]));
}
close(fd);
}
if ((pdf->trailer = pdfioDictCreate(pdf)) == NULL)
if ((pdf->trailer_dict = pdfioDictCreate(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to create trailer.");
ret = false;
goto done;
}
if (pdf->encrypt)
pdfioDictSetObj(pdf->trailer, "Encrypt", pdf->encrypt);
if (pdf->encrypt_obj)
pdfioDictSetObj(pdf->trailer_dict, "Encrypt", pdf->encrypt_obj);
if (pdf->id_array)
pdfioDictSetArray(pdf->trailer, "ID", pdf->id_array);
pdfioDictSetObj(pdf->trailer, "Info", pdf->info);
pdfioDictSetObj(pdf->trailer, "Root", pdf->root);
pdfioDictSetNumber(pdf->trailer, "Size", pdf->num_objs + 1);
pdfioDictSetArray(pdf->trailer_dict, "ID", pdf->id_array);
pdfioDictSetObj(pdf->trailer_dict, "Info", pdf->info_obj);
pdfioDictSetObj(pdf->trailer_dict, "Root", pdf->root_obj);
pdfioDictSetNumber(pdf->trailer_dict, "Size", pdf->num_objs + 1);
if (!_pdfioDictWrite(pdf->trailer, NULL))
if (!_pdfioDictWrite(pdf->trailer_dict, NULL, NULL))
{
_pdfioFileError(pdf, "Unable to write trailer.");
ret = false;

338
pdfio-md5.c Normal file
View File

@ -0,0 +1,338 @@
//
// MD5 functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 1999 Aladdin Enterprises. All rights reserved.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//
// L. Peter Deutsch
// ghost@aladdin.com
//
#include "pdfio-private.h"
/*
Independent implementation of MD5 (RFC 1321).
This code implements the MD5 Algorithm defined in RFC 1321.
It is derived directly from the text of the RFC and not from the
reference implementation.
The original and principal author of md5.c is L. Peter Deutsch
<ghost@aladdin.com>. Other authors are noted in the change history
that follows (in reverse chronological order):
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
1999-05-03 lpd Original version.
*/
#define T1 0xd76aa478
#define T2 0xe8c7b756
#define T3 0x242070db
#define T4 0xc1bdceee
#define T5 0xf57c0faf
#define T6 0x4787c62a
#define T7 0xa8304613
#define T8 0xfd469501
#define T9 0x698098d8
#define T10 0x8b44f7af
#define T11 0xffff5bb1
#define T12 0x895cd7be
#define T13 0x6b901122
#define T14 0xfd987193
#define T15 0xa679438e
#define T16 0x49b40821
#define T17 0xf61e2562
#define T18 0xc040b340
#define T19 0x265e5a51
#define T20 0xe9b6c7aa
#define T21 0xd62f105d
#define T22 0x02441453
#define T23 0xd8a1e681
#define T24 0xe7d3fbc8
#define T25 0x21e1cde6
#define T26 0xc33707d6
#define T27 0xf4d50d87
#define T28 0x455a14ed
#define T29 0xa9e3e905
#define T30 0xfcefa3f8
#define T31 0x676f02d9
#define T32 0x8d2a4c8a
#define T33 0xfffa3942
#define T34 0x8771f681
#define T35 0x6d9d6122
#define T36 0xfde5380c
#define T37 0xa4beea44
#define T38 0x4bdecfa9
#define T39 0xf6bb4b60
#define T40 0xbebfbc70
#define T41 0x289b7ec6
#define T42 0xeaa127fa
#define T43 0xd4ef3085
#define T44 0x04881d05
#define T45 0xd9d4d039
#define T46 0xe6db99e5
#define T47 0x1fa27cf8
#define T48 0xc4ac5665
#define T49 0xf4292244
#define T50 0x432aff97
#define T51 0xab9423a7
#define T52 0xfc93a039
#define T53 0x655b59c3
#define T54 0x8f0ccc92
#define T55 0xffeff47d
#define T56 0x85845dd1
#define T57 0x6fa87e4f
#define T58 0xfe2ce6e0
#define T59 0xa3014314
#define T60 0x4e0811a1
#define T61 0xf7537e82
#define T62 0xbd3af235
#define T63 0x2ad7d2bb
#define T64 0xeb86d391
static void
md5_process(_pdfio_md5_t *pms, const uint8_t *data /*[64]*/)
{
uint32_t
a = pms->abcd[0], b = pms->abcd[1],
c = pms->abcd[2], d = pms->abcd[3];
uint32_t t;
#ifndef ARCH_IS_BIG_ENDIAN
# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */
#endif
#if ARCH_IS_BIG_ENDIAN
/*
* On big-endian machines, we must arrange the bytes in the right
* order. (This also works on machines of unknown byte order.)
*/
uint32_t X[16];
const uint8_t *xp = data;
int i;
for (i = 0; i < 16; ++i, xp += 4)
X[i] = xp[0] + (unsigned)(xp[1] << 8) + (unsigned)(xp[2] << 16) + (unsigned)(xp[3] << 24);
#else /* !ARCH_IS_BIG_ENDIAN */
/*
* On little-endian machines, we can process properly aligned data
* without copying it.
*/
uint32_t xbuf[16];
const uint32_t *X;
if (!((data - (const uint8_t *)0) & 3)) {
/* data are properly aligned */
X = (const uint32_t *)data;
} else {
/* not aligned */
memcpy(xbuf, data, 64);
X = xbuf;
}
#endif
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
/* Round 1. */
/* Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + F(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 7, T1);
SET(d, a, b, c, 1, 12, T2);
SET(c, d, a, b, 2, 17, T3);
SET(b, c, d, a, 3, 22, T4);
SET(a, b, c, d, 4, 7, T5);
SET(d, a, b, c, 5, 12, T6);
SET(c, d, a, b, 6, 17, T7);
SET(b, c, d, a, 7, 22, T8);
SET(a, b, c, d, 8, 7, T9);
SET(d, a, b, c, 9, 12, T10);
SET(c, d, a, b, 10, 17, T11);
SET(b, c, d, a, 11, 22, T12);
SET(a, b, c, d, 12, 7, T13);
SET(d, a, b, c, 13, 12, T14);
SET(c, d, a, b, 14, 17, T15);
SET(b, c, d, a, 15, 22, T16);
#undef SET
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + G(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19);
SET(b, c, d, a, 0, 20, T20);
SET(a, b, c, d, 5, 5, T21);
SET(d, a, b, c, 10, 9, T22);
SET(c, d, a, b, 15, 14, T23);
SET(b, c, d, a, 4, 20, T24);
SET(a, b, c, d, 9, 5, T25);
SET(d, a, b, c, 14, 9, T26);
SET(c, d, a, b, 3, 14, T27);
SET(b, c, d, a, 8, 20, T28);
SET(a, b, c, d, 13, 5, T29);
SET(d, a, b, c, 2, 9, T30);
SET(c, d, a, b, 7, 14, T31);
SET(b, c, d, a, 12, 20, T32);
#undef SET
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti)\
t = a + H(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35);
SET(b, c, d, a, 14, 23, T36);
SET(a, b, c, d, 1, 4, T37);
SET(d, a, b, c, 4, 11, T38);
SET(c, d, a, b, 7, 16, T39);
SET(b, c, d, a, 10, 23, T40);
SET(a, b, c, d, 13, 4, T41);
SET(d, a, b, c, 0, 11, T42);
SET(c, d, a, b, 3, 16, T43);
SET(b, c, d, a, 6, 23, T44);
SET(a, b, c, d, 9, 4, T45);
SET(d, a, b, c, 12, 11, T46);
SET(c, d, a, b, 15, 16, T47);
SET(b, c, d, a, 2, 23, T48);
#undef SET
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti)\
t = a + I(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51);
SET(b, c, d, a, 5, 21, T52);
SET(a, b, c, d, 12, 6, T53);
SET(d, a, b, c, 3, 10, T54);
SET(c, d, a, b, 10, 15, T55);
SET(b, c, d, a, 1, 21, T56);
SET(a, b, c, d, 8, 6, T57);
SET(d, a, b, c, 15, 10, T58);
SET(c, d, a, b, 6, 15, T59);
SET(b, c, d, a, 13, 21, T60);
SET(a, b, c, d, 4, 6, T61);
SET(d, a, b, c, 11, 10, T62);
SET(c, d, a, b, 2, 15, T63);
SET(b, c, d, a, 9, 21, T64);
#undef SET
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
pms->abcd[0] += a;
pms->abcd[1] += b;
pms->abcd[2] += c;
pms->abcd[3] += d;
}
void
_pdfioCryptoMD5Init(_pdfio_md5_t *pms)
{
pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301;
pms->abcd[1] = 0xefcdab89;
pms->abcd[2] = 0x98badcfe;
pms->abcd[3] = 0x10325476;
}
void
_pdfioCryptoMD5Append(_pdfio_md5_t *pms, const uint8_t *data, size_t nbytes)
{
const uint8_t *p = data;
size_t left = nbytes;
size_t offset = (pms->count[0] >> 3) & 63;
uint32_t nbits = (uint32_t)(nbytes << 3);
if (nbytes == 0)
return;
/* Update the message length. */
pms->count[1] += (unsigned)(nbytes >> 29);
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
/* Process an initial partial block. */
if (offset) {
size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy(pms->buf + offset, p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
/* Process full blocks. */
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
/* Process a final partial block. */
if (left)
memcpy(pms->buf, p, left);
}
void
_pdfioCryptoMD5Finish(_pdfio_md5_t *pms, uint8_t digest[16])
{
static const uint8_t pad[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
uint8_t data[8];
int i;
/* Save the length before padding. */
for (i = 0; i < 8; ++i)
data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */
_pdfioCryptoMD5Append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */
_pdfioCryptoMD5Append(pms, data, 8);
for (i = 0; i < 16; ++i)
digest[i] = (uint8_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}

View File

@ -96,6 +96,9 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file
if (!_pdfioValueCopy(pdf, &dstobj->value, srcobj->pdf, &srcobj->value))
return (NULL);
if (dstobj->value.type == PDFIO_VALTYPE_DICT)
_pdfioDictClear(dstobj->value.value.dict, "Length");
if (srcobj->stream_offset)
{
// Copy stream data...
@ -143,6 +146,9 @@ pdfioObjCreateStream(
pdfio_obj_t *obj, // I - Object
pdfio_filter_t filter) // I - Type of compression to apply
{
pdfio_obj_t *length_obj = NULL; // Length object, if any
// Range check input
if (!obj || obj->pdf->mode != _PDFIO_MODE_WRITE || obj->value.type != PDFIO_VALTYPE_DICT)
return (NULL);
@ -162,9 +168,23 @@ pdfioObjCreateStream(
// Write the header...
if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
{
// Need a Length key for the stream, add a placeholder that we can fill in
// later...
pdfioDictSetNumber(obj->value.value.dict, "Length", 0.0);
if (obj->pdf->output_cb)
{
// Streaming via an output callback, so add a placeholder length object
_pdfio_value_t length_value; // Length value
length_value.type = PDFIO_VALTYPE_NUMBER;
length_value.value.number = 0.0f;
length_obj = _pdfioFileCreateObj(obj->pdf, obj->pdf, &length_value);
pdfioDictSetObj(obj->value.value.dict, "Length", length_obj);
}
else
{
// Need a Length key for the stream, add a placeholder that we can fill in
// later...
pdfioDictSetNumber(obj->value.value.dict, "Length", 0.0);
}
}
if (!write_obj_header(obj))
@ -176,7 +196,7 @@ pdfioObjCreateStream(
obj->stream_offset = _pdfioFileTell(obj->pdf);
// Return the new stream...
return (_pdfioStreamCreate(obj, filter));
return (_pdfioStreamCreate(obj, length_obj, filter));
}
@ -392,7 +412,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
// Then grab the object value...
_pdfioTokenInit(&tb, obj->pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, obj->pdf);
if (!_pdfioValueRead(obj->pdf, &tb, &obj->value))
if (!_pdfioValueRead(obj->pdf, obj, &tb, &obj->value))
{
_pdfioFileError(obj->pdf, "Unable to read value for object %lu.", (unsigned long)obj->number);
return (false);
@ -462,7 +482,7 @@ write_obj_header(pdfio_obj_t *obj) // I - Object
if (!_pdfioFilePrintf(obj->pdf, "%lu %u obj\n", (unsigned long)obj->number, obj->generation))
return (false);
if (!_pdfioValueWrite(obj->pdf, &obj->value, &obj->length_offset))
if (!_pdfioValueWrite(obj->pdf, obj, &obj->value, &obj->length_offset))
return (false);
return (_pdfioFilePuts(obj->pdf, "\n"));

View File

@ -24,6 +24,7 @@
# include "pdfio.h"
# include <stdarg.h>
# include <stdint.h>
# include <string.h>
# include <errno.h>
# include <inttypes.h>
@ -174,6 +175,44 @@ typedef struct _pdfio_value_s // Value structure
} value; // Value union
} _pdfio_value_t;
typedef struct _pdfio_aes_s // AES encryption state
{
size_t round_size; // Size of round key
uint8_t round_key[240], // Round key
iv[16]; // Initialization vector
} _pdfio_aes_t;
typedef struct _pdfio_md5_s // MD5 hash state
{
uint32_t count[2]; // Message length in bits, lsw first
uint32_t abcd[4]; // Digest buffer
uint8_t buf[64]; // Accumulate block
} _pdfio_md5_t;
typedef struct _pdfio_rc4_s // RC4 encryption state
{
uint8_t sbox[256]; // S boxes for encryption
uint8_t i, j; // Current indices into S boxes
} _pdfio_rc4_t;
typedef struct _pdfio_sha265_s // SHA-256 hash state
{
uint32_t Intermediate_Hash[8]; // Message Digest
uint32_t Length_High; // Message length in bits
uint32_t Length_Low; // Message length in bits
int Message_Block_Index; // Message_Block array index
uint8_t Message_Block[64]; // 512-bit message blocks
int Computed; // Is the hash computed?
int Corrupted; // Cumulative corruption code
} _pdfio_sha256_t;
typedef union _pdfio_crypto_ctx_u // Cryptographic contexts
{
_pdfio_aes_t aes; // AES-128/256 context
_pdfio_rc4_t rc4; // RC4-40/128 context
} _pdfio_crypto_ctx_t;
typedef size_t (*_pdfio_crypto_cb_t)(_pdfio_crypto_ctx_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len);
struct _pdfio_array_s
{
pdfio_file_t *pdf; // PDF file
@ -210,20 +249,31 @@ struct _pdfio_file_s // PDF file structure
pdfio_rect_t media_box, // Default MediaBox value
crop_box; // Default CropBox value
_pdfio_mode_t mode; // Read/write mode
pdfio_output_cb_t output_cb; // Output callback
void *output_ctx; // Context for output callback
pdfio_error_cb_t error_cb; // Error callback
void *error_data; // Data for error callback
pdfio_encryption_t encryption; // Encryption mode
pdfio_permission_t permissions; // Access permissions (encrypted PDF files)
uint8_t file_key[16], // File encryption key
owner_key[32], // Owner encryption key
user_key[32]; // User encryption key
size_t file_keylen, // Length of file encryption key
owner_keylen, // Length of owner encryption key
user_keylen; // Length of user encryption key
// Active file data
int fd; // File descriptor
char buffer[8192], // Read/write buffer
*bufptr, // Pointer into buffer
*bufend; // End of buffer
off_t bufpos; // Position in file for start of buffer
pdfio_dict_t *trailer; // Trailer dictionary
pdfio_obj_t *root; // Root object/dictionary
pdfio_obj_t *info; // Information object
pdfio_obj_t *pages_root; // Root pages object
pdfio_obj_t *encrypt; // Encryption object/dictionary
pdfio_dict_t *trailer_dict; // Trailer dictionary
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 *cp1252_obj, // CP1252 font encoding object
*unicode_obj; // Unicode font encoding object
pdfio_array_t *id_array; // ID array
@ -266,6 +316,7 @@ struct _pdfio_stream_s // Stream
{
pdfio_file_t *pdf; // PDF file
pdfio_obj_t *obj; // Object
pdfio_obj_t *length_obj; // Length object, if any
pdfio_filter_t filter; // Compression/decompression filter
size_t remaining; // Remaining bytes in stream
char buffer[8192], // Read/write buffer
@ -278,6 +329,8 @@ struct _pdfio_stream_s // Stream
unsigned char cbuffer[4096], // Compressed data buffer
*prbuffer, // Raw buffer (previous line), as needed
*psbuffer; // PNG filter buffer, as needed
_pdfio_crypto_cb_t crypto_cb; // Encryption/descryption callback, if any
_pdfio_crypto_ctx_t crypto_ctx; // Cryptographic context
};
@ -288,15 +341,33 @@ struct _pdfio_stream_s // Stream
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, _pdfio_token_t *ts) _PDFIO_INTERNAL;
extern bool _pdfioArrayWrite(pdfio_array_t *a) _PDFIO_INTERNAL;
extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts) _PDFIO_INTERNAL;
extern bool _pdfioArrayWrite(pdfio_array_t *a, pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern void _pdfioCryptoAESInit(_pdfio_aes_t *ctx, const uint8_t *key, size_t keylen, const uint8_t *iv) _PDFIO_INTERNAL;
extern size_t _pdfioCryptoAESDecrypt(_pdfio_aes_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern size_t _pdfioCryptoAESEncrypt(_pdfio_aes_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern bool _pdfioCryptoLock(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_INTERNAL;
extern void _pdfioCryptoMakeRandom(uint8_t *buffer, size_t bytes) _PDFIO_INTERNAL;
extern _pdfio_crypto_cb_t _pdfioCryptoMakeReader(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL;
extern _pdfio_crypto_cb_t _pdfioCryptoMakeWriter(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL;
extern void _pdfioCryptoMD5Append(_pdfio_md5_t *pms, const uint8_t *data, size_t nbytes) _PDFIO_INTERNAL;
extern void _pdfioCryptoMD5Finish(_pdfio_md5_t *pms, uint8_t digest[16]) _PDFIO_INTERNAL;
extern void _pdfioCryptoMD5Init(_pdfio_md5_t *pms) _PDFIO_INTERNAL;
extern void _pdfioCryptoRC4Init(_pdfio_rc4_t *ctx, const uint8_t *key, size_t keylen) _PDFIO_INTERNAL;
extern size_t _pdfioCryptoRC4Crypt(_pdfio_rc4_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Append(_pdfio_sha256_t *, const uint8_t *bytes, size_t bytecount) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Init(_pdfio_sha256_t *ctx) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Digest) _PDFIO_INTERNAL;
extern bool _pdfioCryptoUnlock(pdfio_file_t *pdf, pdfio_password_cb_t password_cb, void *password_data) _PDFIO_INTERNAL;
extern void _pdfioDictClear(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
extern pdfio_dict_t *_pdfioDictRead(pdfio_file_t *pdf, _pdfio_token_t *ts) _PDFIO_INTERNAL;
extern pdfio_dict_t *_pdfioDictRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts) _PDFIO_INTERNAL;
extern bool _pdfioDictSetValue(pdfio_dict_t *dict, const char *key, _pdfio_value_t *value) _PDFIO_INTERNAL;
extern bool _pdfioDictWrite(pdfio_dict_t *dict, off_t *length) _PDFIO_INTERNAL;
extern bool _pdfioDictWrite(pdfio_dict_t *dict, pdfio_obj_t *obj, off_t *length) _PDFIO_INTERNAL;
extern bool _pdfioFileAddMappedObj(pdfio_file_t *pdf, pdfio_obj_t *dst_obj, pdfio_obj_t *src_obj) _PDFIO_INTERNAL;
extern bool _pdfioFileAddPage(pdfio_file_t *pdf, pdfio_obj_t *obj) _PDFIO_INTERNAL;
@ -319,7 +390,7 @@ extern bool _pdfioFileWrite(pdfio_file_t *pdf, const void *buffer, size_t bytes
extern void _pdfioObjDelete(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern bool _pdfioObjLoad(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_filter_t compression) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, pdfio_filter_t compression) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
@ -334,7 +405,7 @@ extern bool _pdfioTokenRead(_pdfio_token_t *tb, char *buffer, size_t bufsize);
extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL;
extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, _pdfio_token_t *ts, _pdfio_value_t *v) _PDFIO_INTERNAL;
extern bool _pdfioValueWrite(pdfio_file_t *pdf, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, _pdfio_value_t *v) _PDFIO_INTERNAL;
extern bool _pdfioValueWrite(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL;
#endif // !PDFIO_PRIVATE_H

113
pdfio-rc4.c Normal file
View File

@ -0,0 +1,113 @@
//
// RC4 functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
//
// Original code by Tim Martin
// Copyright © 1999 by Carnegie Mellon University, All Rights Reserved
//
// Permission to use, copy, modify, and distribute this software and its
// documentation for any purpose and without fee is hereby granted,
// provided that the above copyright notice appear in all copies and that
// both that copyright notice and this permission notice appear in
// supporting documentation, and that the name of Carnegie Mellon
// University not be used in advertising or publicity pertaining to
// distribution of the software without specific, written prior
// permission.
//
// CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
// THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
// ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
// OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
#include "pdfio-private.h"
//
// '_pdfioCryptoRC4Init()' - Initialize an RC4 context with the specified key.
//
void
_pdfioCryptoRC4Init(
_pdfio_rc4_t *ctx, // IO - Context
const uint8_t *key, // I - Key
size_t keylen) // I - Length of key
{
size_t i; // Looping var
uint8_t j, // S box counter
tmp; // Temporary variable
// Fill in linearly s0=0, s1=1, ...
for (i = 0; i < 256; i ++)
ctx->sbox[i] = (uint8_t)i;
for (i = 0, j = 0; i < 256; i ++)
{
// j = (j + Si + Ki) mod 256
j += ctx->sbox[i] + key[i % keylen];
// Swap Si and Sj...
tmp = ctx->sbox[i];
ctx->sbox[i] = ctx->sbox[j];
ctx->sbox[j] = tmp;
}
// Initialize counters to 0 and return...
ctx->i = 0;
ctx->j = 0;
}
//
// '_pdfioCryptoRC4Crypt()' - De/encrypt the given buffer.
//
// "inbuffer" and "outbuffer" can point to the same memory.
//
size_t // O - Number of output bytes
_pdfioCryptoRC4Crypt(
_pdfio_rc4_t *ctx, // I - Context
uint8_t *outbuffer, // I - Output buffer
const uint8_t *inbuffer, // I - Input buffer
size_t len) // I - Size of buffers
{
uint8_t tmp, // Swap variable
i, j, // Looping vars
t; // Current S box
size_t outbytes = len; // Number of output bytes
// Loop through the entire buffer...
i = ctx->i;
j = ctx->j;
while (len > 0)
{
// Get the next S box indices...
i ++;
j += ctx->sbox[i];
// Swap Si and Sj...
tmp = ctx->sbox[i];
ctx->sbox[i] = ctx->sbox[j];
ctx->sbox[j] = tmp;
// Get the S box index for this byte...
t = ctx->sbox[i] + ctx->sbox[j];
// Encrypt using the S box...
*outbuffer++ = *inbuffer++ ^ ctx->sbox[t];
len --;
}
// Copy current S box indices back to context...
ctx->i = i;
ctx->j = j;
return (outbytes);
}

480
pdfio-sha256.c Normal file
View File

@ -0,0 +1,480 @@
//
// SHA-256 functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2011 IETF Trust and the persons identified as authors of the
// code. All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the following
// conditions are met:
//
// - Redistributions of source code must retain the above
// copyright notice, this list of conditions and
// the following disclaimer.
//
// - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// - Neither the name of Internet Society, IETF or IETF Trust, nor
// the names of specific contributors, may be used to endorse or
// promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
/*
* Description:
* This file implements the Secure Hash Algorithms SHA-224 and
* SHA-256 as defined in the U.S. National Institute of Standards
* and Technology Federal Information Processing Standards
* Publication (FIPS PUB) 180-3 published in October 2008
* and formerly defined in its predecessors, FIPS PUB 180-1
* and FIP PUB 180-2.
*
* A combined document showing all algorithms is available at
* http://csrc.nist.gov/publications/fips/
* fips180-3/fips180-3_final.pdf
*
* The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit
* message digests for a given data stream. It should take about
* 2**n steps to find a message with the same digest as a given
* message and 2**(n/2) to find any two messages with the same
* digest, when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-224 and SHA-256 are defined in terms of 32-bit "words".
* This code uses <stdint.h> (included via "sha.h") to define 32-
* and 8-bit unsigned integer types. If your C compiler does not
* support 32-bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-224 and SHA-256 are designed to work with messages less
* than 2^64 bits long. This implementation uses SHA224/256Input()
* to hash the bits that are a multiple of the size of an 8-bit
* octet, and then optionally uses SHA224/256FinalBits()
* to hash the final few bits of the input.
*/
#include "pdfio-private.h"
/* Constants from sha.h */
enum {
SHA256_Message_Block_Size = 64,
SHA256HashSize = 32,
SHA256HashSizeBits = 256
};
enum {
shaSuccess = 0,
shaNull, /* Null pointer parameter */
shaInputTooLong, /* input data too long */
shaStateError, /* called Input after FinalBits or Result */
shaBadParam /* passed a bad parameter */
};
/* Macros from sha-private.h */
#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z))
/* Define the SHA shift, rotate left, and rotate right macros */
#define SHA256_SHR(bits,word) ((word) >> (bits))
#define SHA256_ROTL(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))
#define SHA256_ROTR(bits,word) \
(((word) >> (bits)) | ((word) << (32-(bits))))
/* Define the SHA SIGMA and sigma macros */
#define SHA256_SIGMA0(word) \
(SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word))
#define SHA256_SIGMA1(word) \
(SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word))
#define SHA256_sigma0(word) \
(SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word))
#define SHA256_sigma1(word) \
(SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word))
/*
* Add "length" to the length.
* Set Corrupted when overflow has occurred.
*/
static uint32_t addTemp;
#define SHA224_256AddLength(context, length) \
(addTemp = (context)->Length_Low, (context)->Corrupted = \
(((context)->Length_Low += (length)) < addTemp) && \
(++(context)->Length_High == 0) ? shaInputTooLong : \
(context)->Corrupted )
/* Local Function Prototypes */
static int SHA224_256Reset(_pdfio_sha256_t *context, uint32_t *H0);
static void SHA224_256ProcessMessageBlock(_pdfio_sha256_t *context);
static void SHA224_256Finalize(_pdfio_sha256_t *context,
uint8_t Pad_Byte);
static void SHA224_256PadMessage(_pdfio_sha256_t *context,
uint8_t Pad_Byte);
static int SHA224_256ResultN(_pdfio_sha256_t *context,
uint8_t Message_Digest[ ], int HashSize);
/* Initial Hash Values: FIPS 180-3 section 5.3.3 */
static uint32_t SHA256_H0[SHA256HashSize/4] = {
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
/*
* _pdfioCryptoSHA256Init
*
* Description:
* This function will initialize the _pdfio_sha256_t in preparation
* for computing a new SHA256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*/
void _pdfioCryptoSHA256Init(_pdfio_sha256_t *context)
{
SHA224_256Reset(context, SHA256_H0);
}
/*
* _pdfioCryptoSHA256Append
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_array[ ]: [in]
* An array of octets representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array.
*
* Returns:
* sha Error Code.
*/
void
_pdfioCryptoSHA256Append(_pdfio_sha256_t *context, const uint8_t *message_array,
size_t length)
{
if (!length) return;
while (length--) {
context->Message_Block[context->Message_Block_Index++] =
*message_array;
if ((SHA224_256AddLength(context, 8) == shaSuccess) &&
(context->Message_Block_Index == SHA256_Message_Block_Size))
SHA224_256ProcessMessageBlock(context);
message_array++;
}
}
/*
* _pdfioCryptoSHA256Finish
*
* Description:
* This function will return the 256-bit message digest
* into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 31.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*/
void
_pdfioCryptoSHA256Finish(_pdfio_sha256_t *context,
uint8_t Message_Digest[SHA256HashSize])
{
SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
}
/*
* SHA224_256Reset
*
* Description:
* This helper function will initialize the _pdfio_sha256_t in
* preparation for computing a new SHA-224 or SHA-256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* H0[ ]: [in]
* The initial hash value array to use.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256Reset(_pdfio_sha256_t *context, uint32_t *H0)
{
if (!context) return shaNull;
context->Length_High = context->Length_Low = 0;
context->Message_Block_Index = 0;
context->Intermediate_Hash[0] = H0[0];
context->Intermediate_Hash[1] = H0[1];
context->Intermediate_Hash[2] = H0[2];
context->Intermediate_Hash[3] = H0[3];
context->Intermediate_Hash[4] = H0[4];
context->Intermediate_Hash[5] = H0[5];
context->Intermediate_Hash[6] = H0[6];
context->Intermediate_Hash[7] = H0[7];
context->Computed = 0;
context->Corrupted = shaSuccess;
return shaSuccess;
}
/*
* SHA224_256ProcessMessageBlock
*
* Description:
* This helper function will process the next 512 bits of the
* message stored in the Message_Block array.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the Secure Hash Standard.
*/
static void SHA224_256ProcessMessageBlock(_pdfio_sha256_t *context)
{
/* Constants defined in FIPS 180-3, section 4.2.2 */
static const uint32_t K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
int t, t4; /* Loop counter */
uint32_t temp1, temp2; /* Temporary word value */
uint32_t W[64]; /* Word sequence */
uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for (t = t4 = 0; t < 16; t++, t4 += 4)
W[t] = (((uint32_t)context->Message_Block[t4]) << 24) |
(((uint32_t)context->Message_Block[t4 + 1]) << 16) |
(((uint32_t)context->Message_Block[t4 + 2]) << 8) |
(((uint32_t)context->Message_Block[t4 + 3]));
for (t = 16; t < 64; t++)
W[t] = SHA256_sigma1(W[t-2]) + W[t-7] +
SHA256_sigma0(W[t-15]) + W[t-16];
A = context->Intermediate_Hash[0];
B = context->Intermediate_Hash[1];
C = context->Intermediate_Hash[2];
D = context->Intermediate_Hash[3];
E = context->Intermediate_Hash[4];
F = context->Intermediate_Hash[5];
G = context->Intermediate_Hash[6];
H = context->Intermediate_Hash[7];
for (t = 0; t < 64; t++) {
temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t];
temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C);
H = G;
G = F;
F = E;
E = D + temp1;
D = C;
C = B;
B = A;
A = temp1 + temp2;
}
context->Intermediate_Hash[0] += A;
context->Intermediate_Hash[1] += B;
context->Intermediate_Hash[2] += C;
context->Intermediate_Hash[3] += D;
context->Intermediate_Hash[4] += E;
context->Intermediate_Hash[5] += F;
context->Intermediate_Hash[6] += G;
context->Intermediate_Hash[7] += H;
context->Message_Block_Index = 0;
}
/*
* SHA224_256Finalize
*
* Description:
* This helper function finishes off the digest calculations.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* Pad_Byte: [in]
* The last byte to add to the message block before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* sha Error Code.
*/
static void SHA224_256Finalize(_pdfio_sha256_t *context,
uint8_t Pad_Byte)
{
int i;
SHA224_256PadMessage(context, Pad_Byte);
/* message may be sensitive, so clear it out */
for (i = 0; i < SHA256_Message_Block_Size; ++i)
context->Message_Block[i] = 0;
context->Length_High = 0; /* and clear length */
context->Length_Low = 0;
context->Computed = 1;
}
/*
* SHA224_256PadMessage
*
* Description:
* According to the standard, the message must be padded to the next
* even multiple of 512 bits. The first padding bit must be a '1'.
* The last 64 bits represent the length of the original message.
* All bits in between should be 0. This helper function will pad
* the message according to those rules by filling the
* Message_Block array accordingly. When it returns, it can be
* assumed that the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad.
* Pad_Byte: [in]
* The last byte to add to the message block before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* Nothing.
*/
static void SHA224_256PadMessage(_pdfio_sha256_t *context,
uint8_t Pad_Byte)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) {
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < SHA256_Message_Block_Size)
context->Message_Block[context->Message_Block_Index++] = 0;
SHA224_256ProcessMessageBlock(context);
} else
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < (SHA256_Message_Block_Size-8))
context->Message_Block[context->Message_Block_Index++] = 0;
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
context->Message_Block[59] = (uint8_t)(context->Length_High);
context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
context->Message_Block[63] = (uint8_t)(context->Length_Low);
SHA224_256ProcessMessageBlock(context);
}
/*
* SHA224_256ResultN
*
* Description:
* This helper function will return the 224-bit or 256-bit message
* digest into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 27/31.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
* HashSize: [in]
* The size of the hash, either 28 or 32.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256ResultN(_pdfio_sha256_t *context,
uint8_t Message_Digest[ ], int HashSize)
{
int i;
if (!context) return shaNull;
if (!Message_Digest) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (!context->Computed)
SHA224_256Finalize(context, 0x80);
for (i = 0; i < HashSize; ++i)
Message_Digest[i] = (uint8_t)
(context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ));
return shaSuccess;
}

View File

@ -54,6 +54,10 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
while ((status = deflate(&st->flate, Z_FINISH)) != Z_STREAM_END)
{
size_t bytes = sizeof(st->cbuffer) - st->flate.avail_out,
// Bytes to write
outbytes; // Actual bytes written
if (status < Z_OK && status != Z_BUF_ERROR)
{
_pdfioFileError(st->pdf, "Flate compression failed: %s", zstrerror(status));
@ -61,25 +65,70 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
goto done;
}
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
if (st->crypto_cb)
{
// Encrypt it first...
outbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, bytes & (size_t)~15);
}
else
{
// No encryption
outbytes = bytes;
}
if (!_pdfioFileWrite(st->pdf, st->cbuffer, outbytes))
{
ret = false;
goto done;
}
st->flate.next_out = (Bytef *)st->cbuffer;
st->flate.avail_out = (uInt)sizeof(st->cbuffer);
if (bytes > outbytes)
{
bytes -= outbytes;
memmove(st->cbuffer, st->cbuffer + outbytes, bytes);
}
else
{
bytes = 0;
}
st->flate.next_out = (Bytef *)st->cbuffer + bytes;
st->flate.avail_out = (uInt)(sizeof(st->cbuffer) - bytes);
}
if (st->flate.avail_out < (uInt)sizeof(st->cbuffer))
{
// Write any residuals...
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
size_t bytes = sizeof(st->cbuffer) - st->flate.avail_out;
// Bytes to write
if (st->crypto_cb)
{
// Encrypt it first...
bytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, bytes);
}
if (!_pdfioFileWrite(st->pdf, st->cbuffer, bytes))
{
ret = false;
goto done;
}
}
deflateEnd(&st->flate);
}
else if (st->crypto_cb && st->bufptr > st->buffer)
{
// Encrypt and flush
uint8_t temp[8192]; // Temporary buffer
size_t outbytes; // Output bytes
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, (uint8_t *)st->buffer, (size_t)(st->bufptr - st->buffer));
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
{
ret = false;
goto done;
}
}
// Save the length of this stream...
@ -93,7 +142,12 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
}
// Update the length as needed...
if (st->obj->length_offset)
if (st->length_obj)
{
st->length_obj->value.value.number = st->obj->stream_length;
pdfioObjClose(st->length_obj);
}
else if (st->obj->length_offset)
{
// Seek back to the "/Length 9999999999" we wrote...
if (_pdfioFileSeek(st->pdf, st->obj->length_offset, SEEK_SET) < 0)
@ -137,6 +191,7 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
pdfio_stream_t * // O - Stream or `NULL` on error
_pdfioStreamCreate(
pdfio_obj_t *obj, // I - Object
pdfio_obj_t *length_obj, // I - Length object, if any
pdfio_filter_t compression) // I - Compression to apply
{
pdfio_stream_t *st; // Stream
@ -149,9 +204,28 @@ _pdfioStreamCreate(
return (NULL);
}
st->pdf = obj->pdf;
st->obj = obj;
st->filter = compression;
st->pdf = obj->pdf;
st->obj = obj;
st->length_obj = length_obj;
st->filter = compression;
st->bufptr = st->buffer;
st->bufend = st->buffer + sizeof(st->buffer);
if (obj->pdf->encryption)
{
uint8_t iv[64]; // Initialization vector
size_t ivlen = sizeof(iv); // Length of initialization vector, if any
if ((st->crypto_cb = _pdfioCryptoMakeWriter(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL)
{
// TODO: Add error message?
free(st);
return (NULL);
}
if (ivlen > 0)
_pdfioFileWrite(st->pdf, iv, ivlen);
}
if (compression == PDFIO_FILTER_FLATE)
{
@ -351,6 +425,27 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
return (NULL);
}
if (obj->pdf->encryption)
{
uint8_t iv[64]; // Initialization vector
size_t ivlen; // Length of initialization vector, if any
ivlen = (size_t)_pdfioFilePeek(st->pdf, iv, sizeof(iv));
if ((st->crypto_cb = _pdfioCryptoMakeReader(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL)
{
// TODO: Add error message?
free(st);
return (NULL);
}
if (ivlen > 0)
_pdfioFileConsume(st->pdf, ivlen);
if (st->pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
st->remaining = (st->remaining + 15) & (size_t)~15;
}
if (decode)
{
// Try to decode/decompress the contents of this object...
@ -465,6 +560,9 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
return (NULL);
}
if (st->crypto_cb)
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
@ -585,7 +683,7 @@ pdfioStreamPrintf(
//
// '()' - Write a single character to a stream.
// 'pdfioStreamPutChar()' - Write a single character to a stream.
//
bool // O - `true` on success, `false` on failure
@ -715,8 +813,63 @@ pdfioStreamWrite(
// Write it...
if (st->filter == PDFIO_FILTER_NONE)
{
// No filtering so just write it...
return (_pdfioFileWrite(st->pdf, buffer, bytes));
// No filtering...
if (st->crypto_cb)
{
// Encrypt data before writing...
uint8_t temp[8192]; // Temporary buffer
size_t cbytes, // Current bytes
outbytes; // Output bytes
bufptr = (const unsigned char *)buffer;
while (bytes > 0)
{
if (st->bufptr > st->buffer || bytes < 16)
{
// Write through the stream's buffer...
if ((cbytes = bytes) > (size_t)(st->bufend - st->bufptr))
cbytes = (size_t)(st->bufend - st->bufptr);
memcpy(st->bufptr, bufptr, cbytes);
st->bufptr += cbytes;
if (st->bufptr >= st->bufend)
{
// Encrypt and flush
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, (uint8_t *)st->buffer, sizeof(st->buffer));
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
return (false);
st->bufptr = st->buffer;
}
}
else
{
// Write directly up to sizeof(temp) bytes...
if ((cbytes = bytes) > sizeof(temp))
cbytes = sizeof(temp);
if (cbytes & 15)
{
// AES has a 16-byte block size, so save the last few bytes...
cbytes &= (size_t)~15;
}
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, bufptr, cbytes);
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
return (false);
}
bytes -= cbytes;
bufptr += cbytes;
}
return (true);
}
else
{
// Write unencrypted...
return (_pdfioFileWrite(st->pdf, buffer, bytes));
}
}
pbline = st->pbsize - 1;
@ -853,8 +1006,13 @@ stream_read(pdfio_stream_t *st, // I - Stream
rbytes = _pdfioFileRead(st->pdf, buffer, bytes);
if (rbytes > 0)
{
st->remaining -= (size_t)rbytes;
if (st->crypto_cb)
(st->crypto_cb)(&st->crypto_ctx, (uint8_t *)buffer, (uint8_t *)buffer, (size_t)rbytes);
}
return (rbytes);
}
else if (st->filter == PDFIO_FILTER_FLATE)
@ -878,6 +1036,9 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (rbytes <= 0)
return (-1); // End of file...
if (st->crypto_cb)
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
st->remaining -= (size_t)rbytes;
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
@ -931,6 +1092,9 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (rbytes <= 0)
return (-1); // End of file...
if (st->crypto_cb)
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
st->remaining -= (size_t)rbytes;
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
@ -995,6 +1159,9 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (rbytes <= 0)
return (-1); // End of file...
if (st->crypto_cb)
rbytes = (ssize_t)(st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, (size_t)rbytes);
st->remaining -= (size_t)rbytes;
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
@ -1091,11 +1258,36 @@ stream_write(pdfio_stream_t *st, // I - Stream
if (st->flate.avail_out < (sizeof(st->cbuffer) / 8))
{
// Flush the compression buffer...
if (!_pdfioFileWrite(st->pdf, st->cbuffer, sizeof(st->cbuffer) - st->flate.avail_out))
size_t cbytes = sizeof(st->cbuffer) - st->flate.avail_out,
outbytes;
if (st->crypto_cb)
{
// Encrypt it first...
outbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, cbytes & (size_t)~15);
}
else
{
outbytes = cbytes;
}
// fprintf(stderr, "stream_write: bytes=%u, outbytes=%u\n", (unsigned)bytes, (unsigned)outbytes);
if (!_pdfioFileWrite(st->pdf, st->cbuffer, outbytes))
return (false);
st->flate.next_out = (Bytef *)st->cbuffer;
st->flate.avail_out = sizeof(st->cbuffer);
if (cbytes > outbytes)
{
cbytes -= outbytes;
memmove(st->cbuffer, st->cbuffer + outbytes, cbytes);
}
else
{
cbytes = 0;
}
st->flate.next_out = (Bytef *)st->cbuffer + cbytes;
st->flate.avail_out = (uInt)(sizeof(st->cbuffer) - cbytes);
}
// Deflate what we can this time...

View File

@ -200,6 +200,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
char *bufptr, // Pointer into buffer
*bufend, // End of buffer
state = '\0'; // Current state
bool saw_nul = false; // Did we see a nul character?
//
@ -260,6 +261,9 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
case '(' : // Literal string
while ((ch = get_char(tb)) != EOF)
{
if (ch == 0)
saw_nul = true;
if (ch == '\\')
{
// Quoted character...
@ -280,7 +284,9 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
int tch = get_char(tb); // Next char
if (tch >= '0' && tch <= '7')
{
ch = (char)((ch << 3) | (tch - '0'));
}
else
{
tb->bufptr --;
@ -350,6 +356,36 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
_pdfioFileError(tb->pdf, "Unterminated string literal.");
return (false);
}
if (saw_nul)
{
// 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);
// Bytes of data...
static const char *hexchars = "0123456789ABCDEF";
// Hex digits
PDFIO_DEBUG("_pdfioTokenRead: Converting nul-containing string to binary.\n");
if ((2 * (bytes + 1)) > bufsize)
{
// Out of space...
_pdfioFileError(tb->pdf, "Token too large.");
return (false);
}
*buffer = '<';
for (litptr = bufptr - 1, hexptr = buffer + 2 * bytes - 1; litptr > buffer; litptr --, hexptr -= 2)
{
int litch = *litptr; // Grab the character
hexptr[0] = hexchars[(litch >> 4) & 15];
hexptr[1] = hexchars[litch & 15];
}
bufptr = buffer + 2 * bytes + 1;
}
break;
case 'K' : // keyword
@ -454,7 +490,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
return (false);
}
while ((ch = get_char(tb)) != EOF && ch != '>')
do
{
if (isxdigit(ch))
{
@ -476,6 +512,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
return (false);
}
}
while ((ch = get_char(tb)) != EOF && ch != '>');
if (ch == EOF)
{
@ -497,9 +534,6 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
break;
}
while (tb->bufptr < tb->bufend && isspace(*(tb->bufptr)))
tb->bufptr ++;
*bufptr = '\0';
PDFIO_DEBUG("_pdfioTokenRead: Read '%s'.\n", buffer);

View File

@ -194,6 +194,7 @@ _pdfioValueDelete(_pdfio_value_t *v) // I - Value
_pdfio_value_t * // O - Value or `NULL` on error/EOF
_pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object, if any
_pdfio_token_t *tb, // I - Token buffer/stack
_pdfio_value_t *v) // I - Value
{
@ -216,7 +217,8 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
#endif // DEBUG
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, v=%p)\n", pdf, v);
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v);
(void)obj; // TODO: Implement decryption
if (!_pdfioTokenGet(tb, token, sizeof(token)))
return (NULL);
@ -225,14 +227,14 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
{
// Start of array
v->type = PDFIO_VALTYPE_ARRAY;
if ((v->value.array = _pdfioArrayRead(pdf, tb)) == NULL)
if ((v->value.array = _pdfioArrayRead(pdf, obj, tb)) == NULL)
return (NULL);
}
else if (!strcmp(token, "<<"))
{
// Start of dictionary
v->type = PDFIO_VALTYPE_DICT;
if ((v->value.dict = _pdfioDictRead(pdf, tb)) == NULL)
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb)) == NULL)
return (NULL);
}
else if (!strncmp(token, "(D:", 3))
@ -278,6 +280,8 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
else
{
// Date value...
memset(&dateval, 0, sizeof(dateval));
dateval.tm_year = (token[3] - '0') * 1000 + (token[4] - '0') * 100 + (token[5] - '0') * 10 + token[6] - '0' - 1900;
dateval.tm_mon = (token[7] - '0') * 10 + token[8] - '0' - 1;
dateval.tm_mday = (token[9] - '0') * 10 + token[10] - '0';
@ -396,6 +400,9 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
tempptr = tb->bufptr;
while (tempptr < tb->bufend && isspace(*tempptr & 255))
tempptr ++; // Skip whitespace as needed...
if (tempptr < tb->bufend && isdigit(*tempptr & 255))
{
// Integer...
@ -408,7 +415,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
}
while (tempptr < tb->bufend && isspace(*tempptr & 255))
tempptr ++;
tempptr ++; // Skip whitespace
if (tempptr < tb->bufend && *tempptr == 'R')
{
@ -472,6 +479,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
bool // O - `true` on success, `false` on failure
_pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object, if any
_pdfio_value_t *v, // I - Value
off_t *length)// O - Offset to /Length value, if any
{
@ -481,23 +489,47 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
return (false);
case PDFIO_VALTYPE_ARRAY :
return (_pdfioArrayWrite(v->value.array));
return (_pdfioArrayWrite(v->value.array, obj));
case PDFIO_VALTYPE_BINARY :
{
size_t i; // Looping var
unsigned char *dataptr; // Pointer into data
size_t databytes; // Bytes to write
uint8_t temp[32768], // Temporary buffer for encryption
*dataptr; // Pointer into data
if (obj && pdf->encryption)
{
// Write encrypted string...
_pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback
size_t ivlen; // Number of initialization vector bytes
if (v->value.binary.datalen > (sizeof(temp) - 32))
{
_pdfioFileError(pdf, "Unable to write encrypted binary string - too long.");
return (false);
}
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
databytes = (cb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen;
dataptr = temp;
}
else
{
dataptr = v->value.binary.data;
databytes = v->value.binary.datalen;
}
if (!_pdfioFilePuts(pdf, "<"))
return (false);
for (i = v->value.binary.datalen, dataptr = v->value.binary.data; i > 1; i -= 2, dataptr += 2)
for (; databytes > 1; databytes -= 2, dataptr += 2)
{
if (!_pdfioFilePrintf(pdf, "%02X%02X", dataptr[0], dataptr[1]))
return (false);
}
if (i > 0)
if (databytes > 0)
return (_pdfioFilePrintf(pdf, "%02X>", dataptr[0]));
else
return (_pdfioFilePuts(pdf, ">"));
@ -512,6 +544,7 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
case PDFIO_VALTYPE_DATE :
{
struct tm date; // Date values
char datestr[32]; // Formatted date value
#ifdef _WIN32
gmtime_s(&date, &v->value.date);
@ -519,11 +552,45 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
gmtime_r(&v->value.date, &date);
#endif // _WIN32
return (_pdfioFilePrintf(pdf, "(D:%04d%02d%02d%02d%02d%02dZ)", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec));
snprintf(datestr, sizeof(datestr), "D:%04d%02d%02d%02d%02d%02dZ", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec);
if (obj && pdf->encryption)
{
// Write encrypted string...
uint8_t temp[32768], // Encrypted bytes
*tempptr; // Pointer into encrypted bytes
_pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback
size_t len = strlen(datestr),
// Length of value
ivlen, // Number of initialization vector bytes
tempbytes; // Number of output bytes
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)datestr, len) + ivlen;
if (!_pdfioFilePuts(pdf, "<"))
return (false);
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
{
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1]))
return (false);
}
if (tempbytes > 0)
return (_pdfioFilePrintf(pdf, "%02X>", *tempptr));
else
return (_pdfioFilePuts(pdf, ">"));
}
else
{
return (_pdfioFilePrintf(pdf, "(%s)", datestr));
}
}
case PDFIO_VALTYPE_DICT :
return (_pdfioDictWrite(v->value.dict, length));
return (_pdfioDictWrite(v->value.dict, obj, length));
case PDFIO_VALTYPE_INDIRECT :
return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation));
@ -538,7 +605,44 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
return (_pdfioFilePrintf(pdf, " %g", v->value.number));
case PDFIO_VALTYPE_STRING :
if (obj && pdf->encryption)
{
// Write encrypted string...
uint8_t temp[32768], // Encrypted bytes
*tempptr; // Pointer into encrypted bytes
_pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback
size_t len = strlen(v->value.string),
// Length of value
ivlen, // Number of initialization vector bytes
tempbytes; // Number of output bytes
if (len > (sizeof(temp) - 32))
{
_pdfioFileError(pdf, "Unable to write encrypted string - too long.");
return (false);
}
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen;
if (!_pdfioFilePuts(pdf, "<"))
return (false);
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
{
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1]))
return (false);
}
if (tempbytes > 0)
return (_pdfioFilePrintf(pdf, "%02X>", *tempptr));
else
return (_pdfioFilePuts(pdf, ">"));
}
else
{
// Write unencrypted string...
const char *start, // Start of fragment
*end; // End of fragment

31
pdfio.h
View File

@ -59,6 +59,14 @@ typedef struct _pdfio_file_s pdfio_file_t;
// PDF file
typedef bool (*pdfio_error_cb_t)(pdfio_file_t *pdf, const char *message, void *data);
// Error callback
typedef enum pdfio_encryption_e // PDF encryption modes
{
PDFIO_ENCRYPTION_NONE = 0, // No encryption
PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3)
PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4)
PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6)
PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0)
} pdfio_encryption_t;
typedef enum pdfio_filter_e // Compression/decompression filters for streams
{
PDFIO_FILTER_NONE, // No filter
@ -74,6 +82,24 @@ typedef enum pdfio_filter_e // Compression/decompression filters for streams
PDFIO_FILTER_RUNLENGTH, // RunLengthDecode filter (reading only)
} pdfio_filter_t;
typedef struct _pdfio_obj_s pdfio_obj_t;// Numbered object in PDF file
typedef ssize_t (*pdfio_output_cb_t)(void *ctx, const void *data, size_t datalen);
// Output callback for pdfioFileCreateOutput
typedef const char *(*pdfio_password_cb_t)(void *data, const char *filename);
// Password callback for pdfioFileOpen
enum pdfio_permission_e // PDF permission bits
{
PDFIO_PERMISSION_NONE = 0, // No permissions
PDFIO_PERMISSION_PRINT = 0x0004, // PDF allows printing
PDFIO_PERMISSION_MODIFY = 0x0008, // PDF allows modification
PDFIO_PERMISSION_COPY = 0x0010, // PDF allows copying
PDFIO_PERMISSION_ANNOTATE = 0x0020, // PDF allows annotation
PDFIO_PERMISSION_FORMS = 0x0100, // PDF allows filling in forms
PDFIO_PERMISSION_READING = 0x0200, // PDF allows screen reading/accessibility (deprecated in PDF 2.0)
PDFIO_PERMISSION_ASSEMBLE = 0x0400, // PDF allows assembly (insert, delete, or rotate pages, add document outlines and thumbnails)
PDFIO_PERMISSION_PRINT_HIGH = 0x0800, // PDF allows high quality printing
PDFIO_PERMISSION_ALL = ~0 // All permissions
};
typedef int pdfio_permission_t; // PDF permission bitfield
typedef struct pdfio_rect_s // PDF rectangle
{
double x1; // Lower-left X coordinate
@ -156,6 +182,7 @@ extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateArrayObj(pdfio_file_t *pdf, pdfio_array_t *array) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateOutput(pdfio_output_cb_t output_cb, void *output_ctx, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
// TODO: Add number, array, string, etc. versions of pdfioFileCreateObject?
extern pdfio_stream_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;
@ -169,15 +196,17 @@ extern size_t pdfioFileGetNumObjs(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern size_t pdfioFileGetNumPages(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileGetObj(pdfio_file_t *pdf, size_t n) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileGetPage(pdfio_file_t *pdf, size_t n) _PDFIO_PUBLIC;
extern pdfio_permission_t pdfioFileGetPermissions(pdfio_file_t *pdf, pdfio_encryption_t *encryption) _PDFIO_PUBLIC;
extern const char *pdfioFileGetProducer(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetSubject(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetTitle(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetVersion(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileOpen(const char *filename, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileOpen(const char *filename, pdfio_password_cb_t password_cb, void *password_data, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern void pdfioFileSetAuthor(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetCreationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC;
extern void pdfioFileSetCreator(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetKeywords(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern bool pdfioFileSetPermissions(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_PUBLIC;
extern void pdfioFileSetSubject(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetTitle(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;

View File

@ -87,7 +87,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b2";WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
@ -101,7 +101,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b2";WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
@ -115,7 +115,7 @@
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b2";_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
@ -130,7 +130,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b1";NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>PDFIO_VERSION="1.0b2";NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
@ -148,13 +148,18 @@
<ClInclude Include="ttf.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="pdfio-aes.c" />
<ClCompile Include="pdfio-array.c" />
<ClCompile Include="pdfio-common.c" />
<ClCompile Include="pdfio-content.c" />
<ClCompile Include="pdfio-crypto.c" />
<ClCompile Include="pdfio-dict.c" />
<ClCompile Include="pdfio-file.c" />
<ClCompile Include="pdfio-md5.c" />
<ClCompile Include="pdfio-object.c" />
<ClCompile Include="pdfio-page.c" />
<ClCompile Include="pdfio-rc4.c" />
<ClCompile Include="pdfio-sha256.c" />
<ClCompile Include="pdfio-stream.c" />
<ClCompile Include="pdfio-string.c" />
<ClCompile Include="pdfio-token.c" />

View File

@ -65,6 +65,21 @@
<ClCompile Include="ttf.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-aes.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-crypto.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-md5.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-rc4.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pdfio-sha256.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@ -25,7 +25,12 @@
279E1035267D043B00D3A349 /* ttf.h in Headers */ = {isa = PBXBuildFile; fileRef = 279E1033267D043B00D3A349 /* ttf.h */; };
279E1036267D043B00D3A349 /* ttf.c in Sources */ = {isa = PBXBuildFile; fileRef = 279E1034267D043B00D3A349 /* ttf.c */; };
279E103B267D04E600D3A349 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 279E103A267D04E600D3A349 /* libz.tbd */; };
27CF90442711DFFE00E50FE4 /* pdfio-aes.c in Sources */ = {isa = PBXBuildFile; fileRef = 27CF90432711DFFE00E50FE4 /* pdfio-aes.c */; };
27ECBD8926419DAB0025312A /* libpdfio.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 273440B0263D6FE200FBFD63 /* libpdfio.a */; };
27F2F0602710BE92008ECD36 /* pdfio-md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05D2710BE92008ECD36 /* pdfio-md5.c */; };
27F2F0612710BE92008ECD36 /* pdfio-rc4.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */; };
27F2F0622710BE92008ECD36 /* pdfio-crypto.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */; };
27F2F0642711243D008ECD36 /* pdfio-sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 27F2F0632711243D008ECD36 /* pdfio-sha256.c */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -79,6 +84,11 @@
279E1033267D043B00D3A349 /* ttf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ttf.h; sourceTree = "<group>"; };
279E1034267D043B00D3A349 /* ttf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ttf.c; sourceTree = "<group>"; };
279E103A267D04E600D3A349 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
27CF90432711DFFE00E50FE4 /* pdfio-aes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-aes.c"; sourceTree = "<group>"; };
27F2F05D2710BE92008ECD36 /* pdfio-md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-md5.c"; sourceTree = "<group>"; };
27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-rc4.c"; sourceTree = "<group>"; };
27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-crypto.c"; sourceTree = "<group>"; };
27F2F0632711243D008ECD36 /* pdfio-sha256.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "pdfio-sha256.c"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -150,19 +160,24 @@
279E1038267D045C00D3A349 /* Library */ = {
isa = PBXGroup;
children = (
27CF90432711DFFE00E50FE4 /* pdfio-aes.c */,
273440BA263D727800FBFD63 /* pdfio-array.c */,
273440BB263D727800FBFD63 /* pdfio-common.c */,
271EA703265B2B1000ACDD39 /* pdfio-content.c */,
27F2F05F2710BE92008ECD36 /* pdfio-crypto.c */,
273440BE263D727800FBFD63 /* pdfio-dict.c */,
273440BD263D727800FBFD63 /* pdfio-file.c */,
27F2F05D2710BE92008ECD36 /* pdfio-md5.c */,
273440BC263D727800FBFD63 /* pdfio-object.c */,
273440C2263D727800FBFD63 /* pdfio-page.c */,
27F2F05E2710BE92008ECD36 /* pdfio-rc4.c */,
27F2F0632711243D008ECD36 /* pdfio-sha256.c */,
273440BF263D727800FBFD63 /* pdfio-stream.c */,
273440B9263D727800FBFD63 /* pdfio-string.c */,
273440E3263DD7EA00FBFD63 /* pdfio-token.c */,
273440C0263D727800FBFD63 /* pdfio-value.c */,
279E1033267D043B00D3A349 /* ttf.h */,
279E1034267D043B00D3A349 /* ttf.c */,
279E1033267D043B00D3A349 /* ttf.h */,
);
name = Library;
sourceTree = "<group>";
@ -241,7 +256,7 @@
273440A8263D6FE200FBFD63 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1250;
LastUpgradeCheck = 1300;
TargetAttributes = {
273440AF263D6FE200FBFD63 = {
CreatedOnToolsVersion = 12.5;
@ -281,11 +296,16 @@
273440CB263D727800FBFD63 /* pdfio-value.c in Sources */,
273440CA263D727800FBFD63 /* pdfio-stream.c in Sources */,
273440CD263D727800FBFD63 /* pdfio-page.c in Sources */,
27F2F0622710BE92008ECD36 /* pdfio-crypto.c in Sources */,
27F2F0642711243D008ECD36 /* pdfio-sha256.c in Sources */,
273440C5263D727800FBFD63 /* pdfio-array.c in Sources */,
273440E4263DD7EA00FBFD63 /* pdfio-token.c in Sources */,
273440C7263D727800FBFD63 /* pdfio-object.c in Sources */,
27F2F0602710BE92008ECD36 /* pdfio-md5.c in Sources */,
273440C4263D727800FBFD63 /* pdfio-string.c in Sources */,
27CF90442711DFFE00E50FE4 /* pdfio-aes.c in Sources */,
271EA705265B2B1000ACDD39 /* pdfio-content.c in Sources */,
27F2F0612710BE92008ECD36 /* pdfio-rc4.c in Sources */,
273440C6263D727800FBFD63 /* pdfio-common.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -350,9 +370,9 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0;
CURRENT_PROJECT_VERSION = 1.0.0;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@ -428,9 +448,9 @@
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application";
CODE_SIGN_IDENTITY = "Apple Development";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1.0;
CURRENT_PROJECT_VERSION = 1.0.0;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_NS_ASSERTIONS = NO;

View File

@ -1,9 +1,67 @@
LIBRARY pdfio1
VERSION 1.0
EXPORTS
_pdfioArrayDebug
_pdfioArrayDelete
_pdfioArrayGetValue
_pdfioArrayRead
_pdfioArrayWrite
_pdfioCryptoAESDecrypt
_pdfioCryptoAESEncrypt
_pdfioCryptoAESInit
_pdfioCryptoLock
_pdfioCryptoMD5Append
_pdfioCryptoMD5Finish
_pdfioCryptoMD5Init
_pdfioCryptoMakeRandom
_pdfioCryptoMakeReader
_pdfioCryptoMakeWriter
_pdfioCryptoRC4Crypt
_pdfioCryptoRC4Init
_pdfioCryptoSHA256Append
_pdfioCryptoSHA256Finish
_pdfioCryptoSHA256Init
_pdfioCryptoUnlock
_pdfioDictClear
_pdfioDictDebug
_pdfioDictDelete
_pdfioDictGetValue
_pdfioDictRead
_pdfioDictSetValue
_pdfioDictWrite
_pdfioFileAddMappedObj
_pdfioFileAddPage
_pdfioFileConsume
_pdfioFileCreateObj
_pdfioFileDefaultError
_pdfioFileError
_pdfioFileFindMappedObj
_pdfioFileFlush
_pdfioFileGetChar
_pdfioFileGets
_pdfioFilePeek
_pdfioFilePrintf
_pdfioFilePuts
_pdfioFileRead
_pdfioFileSeek
_pdfioFileTell
_pdfioFileWrite
_pdfioObjDelete
_pdfioObjLoad
_pdfioStreamCreate
_pdfioStreamOpen
_pdfioStringIsAllocated
_pdfioTokenClear
_pdfioTokenFlush
_pdfioTokenGet
_pdfioTokenInit
_pdfioTokenPush
_pdfioTokenRead
_pdfioValueCopy
_pdfioValueDebug
_pdfioValueDelete
_pdfioValueRead
_pdfioValueWrite
pdfioArrayAppendArray
pdfioArrayAppendBinary
pdfioArrayAppendBoolean
@ -117,6 +175,7 @@ pdfioFileCreateICCObjFromFile
pdfioFileCreateImageObjFromData
pdfioFileCreateImageObjFromFile
pdfioFileCreateObj
pdfioFileCreateOutput
pdfioFileCreatePage
pdfioFileFindObj
pdfioFileGetAuthor
@ -129,6 +188,7 @@ pdfioFileGetNumObjs
pdfioFileGetNumPages
pdfioFileGetObj
pdfioFileGetPage
pdfioFileGetPermissions
pdfioFileGetProducer
pdfioFileGetSubject
pdfioFileGetTitle
@ -138,6 +198,7 @@ pdfioFileSetAuthor
pdfioFileSetCreationDate
pdfioFileSetCreator
pdfioFileSetKeywords
pdfioFileSetPermissions
pdfioFileSetSubject
pdfioFileSetTitle
pdfioImageGetBytesPerLine

View File

@ -3,7 +3,7 @@
<metadata>
<id>pdfio_native</id>
<title>PDFio Library for VS2019+</title>
<version>1.0.0-b2</version>
<version>1.0.0-b7</version>
<authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
@ -15,6 +15,10 @@
<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 exception to allow linking against GNU GPL2-only software.</summary>
<copyright>Copyright © 2019-2021 by Michael R Sweet</copyright>
<tags>pdf file native</tags>
<dependencies>
<dependency id="pdfio_native.redist" version="1.0.0-b7" />
<dependency id="zlib_native.redist" version="1.2.11" />
</dependencies>
</metadata>
<files>
<file src="doc\pdfio-128.png" target="build\native" />
@ -22,7 +26,7 @@
<file src="pdfio_native.props" target="build\native" />
<file src="pdfio.h" target="build\native\include" />
<file src="pdfio-content.h" target="build\native\include" />
<!--<file src="Win32\**\pdfio*.lib" target="build\native\lib\Win32" />-->
<file src="x64\**\pdfio.lib" target="build\native\lib\x64" />
<!--<file src="Win32\**\pdfio1.lib" target="build\native\lib\Win32" />-->
<file src="x64\**\pdfio1.lib" target="build\native\lib\x64" />
</files>
</package>

View File

@ -5,7 +5,7 @@
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies>$(MSBuildThisFileDirectory)\lib\$(Platform)\$(Configuration)\pdfio.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>$(MSBuildThisFileDirectory)\lib\$(Platform)\$(Configuration)\pdfio1.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
</Project>

View File

@ -0,0 +1,25 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
<metadata>
<id>pdfio_native.redist</id>
<title>PDFio Library for VS2019+</title>
<version>1.0.0-b7</version>
<authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
<license type="expression">Apache-2.0</license>
<icon>build/native/pdfio-128.png</icon>
<readme>build/native/README.md</readme>
<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 exception to allow linking against GNU GPL2-only software.</summary>
<copyright>Copyright © 2019-2021 by Michael R Sweet</copyright>
<tags>pdf file native</tags>
</metadata>
<files>
<file src="doc\pdfio-128.png" target="build\native" />
<file src="README.md" target="build\native" />
<!--<file src="Win32\**\pdfio1.dll" target="build\native\bin\Win32" />-->
<file src="x64\**\pdfio1.dll" target="build\native\bin\x64" />
</files>
</package>

View File

@ -29,10 +29,14 @@
// Local functions...
//
static int do_crypto_tests(void);
static int do_test_file(const char *filename, int objnum, 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);
static ssize_t output_cb(int *fd, const void *buffer, size_t bytes);
static const char *password_cb(void *data, const char *filename);
static int read_unit_file(const char *filename, size_t num_pages, size_t first_image, bool is_output);
static ssize_t token_consume_cb(const char **s, size_t bytes);
static ssize_t token_peek_cb(const char **s, char *buffer, size_t bytes);
static int verify_image(pdfio_file_t *pdf, size_t number);
@ -46,6 +50,7 @@ static int write_images_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_jpeg_test(pdfio_file_t *pdf, const char *title, int number, pdfio_obj_t *font, pdfio_obj_t *image);
static int write_png_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_text_test(pdfio_file_t *pdf, int first_page, pdfio_obj_t *font, const char *filename);
static int write_unit_file(pdfio_file_t *inpdf, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image);
//
@ -108,6 +113,249 @@ main(int argc, // I - Number of command-line arguments
}
//
// 'do_crypto_tests()' - Test the various cryptographic functions in PDFio.
//
static int // O - Exit status
do_crypto_tests(void)
{
int ret = 0; // Return value
size_t i; // Looping var
_pdfio_aes_t aes; // AES context
_pdfio_md5_t md5; // MD5 context
_pdfio_rc4_t rc4; // RC4 context
_pdfio_sha256_t sha256; // SHA256 context
uint8_t key[32], // Encryption/decryption key
iv[32], // Initialization vector
buffer[256], // Output buffer
buffer2[256]; // Second output buffer
const char *prefix, *suffix; // Prefix/suffix strings
static const char *text = "Hello, World! Now is the time for all good men to come to the aid of their country.\n";
// Test text
static uint8_t aes128key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
static uint8_t aes128rounds[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c, 0xa0, 0xfa, 0xfe, 0x17, 0x88, 0x54, 0x2c, 0xb1, 0x23, 0xa3, 0x39, 0x39, 0x2a, 0x6c, 0x76, 0x05, 0xf2, 0xc2, 0x95, 0xf2, 0x7a, 0x96, 0xb9, 0x43, 0x59, 0x35, 0x80, 0x7a, 0x73, 0x59, 0xf6, 0x7f, 0x3d, 0x80, 0x47, 0x7d, 0x47, 0x16, 0xfe, 0x3e, 0x1e, 0x23, 0x7e, 0x44, 0x6d, 0x7a, 0x88, 0x3b, 0xef, 0x44, 0xa5, 0x41, 0xa8, 0x52, 0x5b, 0x7f, 0xb6, 0x71, 0x25, 0x3b, 0xdb, 0x0b, 0xad, 0x00, 0xd4, 0xd1, 0xc6, 0xf8, 0x7c, 0x83, 0x9d, 0x87, 0xca, 0xf2, 0xb8, 0xbc, 0x11, 0xf9, 0x15, 0xbc, 0x6d, 0x88, 0xa3, 0x7a, 0x11, 0x0b, 0x3e, 0xfd, 0xdb, 0xf9, 0x86, 0x41, 0xca, 0x00, 0x93, 0xfd, 0x4e, 0x54, 0xf7, 0x0e, 0x5f, 0x5f, 0xc9, 0xf3, 0x84, 0xa6, 0x4f, 0xb2, 0x4e, 0xa6, 0xdc, 0x4f, 0xea, 0xd2, 0x73, 0x21, 0xb5, 0x8d, 0xba, 0xd2, 0x31, 0x2b, 0xf5, 0x60, 0x7f, 0x8d, 0x29, 0x2f, 0xac, 0x77, 0x66, 0xf3, 0x19, 0xfa, 0xdc, 0x21, 0x28, 0xd1, 0x29, 0x41, 0x57, 0x5c, 0x00, 0x6e, 0xd0, 0x14, 0xf9, 0xa8, 0xc9, 0xee, 0x25, 0x89, 0xe1, 0x3f, 0x0c, 0xc8, 0xb6, 0x63, 0x0c, 0xa6 };
// FIPS-197 example key expansion
static uint8_t aes128text[] = { 0xfb, 0x77, 0xac, 0xce, 0x3c, 0x95, 0x40, 0xcf, 0xca, 0xc8, 0x26, 0xbf, 0xc0, 0x69, 0x73, 0x3c, 0x01, 0xfd, 0x72, 0x01, 0xeb, 0x4d, 0x6f, 0xf7, 0xb4, 0x72, 0x6d, 0x84, 0x69, 0x9f, 0x89, 0xab, 0xe6, 0x2b, 0x9a, 0x9a, 0x6e, 0xc1, 0x61, 0xd7, 0x9d, 0x83, 0x2d, 0x58, 0x55, 0xa7, 0x58, 0x50, 0x00, 0xad, 0x19, 0x7b, 0xee, 0x6a, 0x36, 0x6f, 0xd1, 0xa7, 0xa4, 0x6b, 0xc5, 0x78, 0x9a, 0x18, 0x05, 0xf0, 0x2c, 0xd4, 0x60, 0x25, 0xe0, 0xa7, 0xb1, 0x36, 0xdb, 0x18, 0xd3, 0xf7, 0x59, 0x29, 0x22, 0xec, 0x25, 0x77, 0x0d, 0x9e, 0x5a, 0x01, 0xcc, 0xf6, 0x29, 0xc2, 0x08, 0xc2, 0xfc, 0x4f };
// Expected AES-128 CBC result
static uint8_t aes256text[] = { 0x2b, 0x94, 0x45, 0x9e, 0xed, 0xa0, 0x89, 0x7b, 0x35, 0x4e, 0xde, 0x06, 0x00, 0x4d, 0xda, 0x6b, 0x61, 0x2f, 0xb9, 0x06, 0xd5, 0x0f, 0x22, 0xed, 0xd2, 0xe3, 0x6b, 0x39, 0x5a, 0xa1, 0xe3, 0x7d, 0xa1, 0xcc, 0xd4, 0x0b, 0x6b, 0xa4, 0xff, 0xe9, 0x9c, 0x89, 0x0c, 0xc7, 0x95, 0x47, 0x19, 0x9b, 0x06, 0xdc, 0xc8, 0x7c, 0x5c, 0x5d, 0x56, 0x99, 0x1e, 0x90, 0x7d, 0x99, 0xc5, 0x7b, 0xc4, 0xe4, 0xfb, 0x02, 0x15, 0x50, 0x23, 0x2a, 0xe4, 0xc1, 0x20, 0xfd, 0xf4, 0x03, 0xfe, 0x6f, 0x15, 0x48, 0xd8, 0x62, 0x36, 0x98, 0x2a, 0x62, 0xf5, 0x2c, 0xa6, 0xfa, 0x7a, 0x43, 0x53, 0xcd, 0xad, 0x18 };
// Expected AES-256 CBC result
static uint8_t md5text[16] = { 0x74, 0x0c, 0x2c, 0xea, 0xe1, 0xab, 0x06, 0x7c, 0xdb, 0x1d, 0x49, 0x1d, 0x2d, 0x66, 0xf2, 0x93 };
// Expected MD5 hash result
static uint8_t rc4text[] = { 0xd2, 0xa2, 0xa0, 0xf6, 0x0f, 0xb1, 0x3e, 0xa0, 0xdd, 0xe1, 0x44, 0xfd, 0xec, 0xc4, 0x55, 0xf8, 0x25, 0x68, 0xad, 0xe6, 0xb0, 0x60, 0x7a, 0x0f, 0x4e, 0xfe, 0xed, 0x9c, 0x78, 0x3a, 0xf8, 0x73, 0x79, 0xbd, 0x82, 0x88, 0x39, 0x01, 0xc7, 0xd0, 0x34, 0xfe, 0x40, 0x16, 0x93, 0x5a, 0xec, 0x81, 0xda, 0x34, 0xdf, 0x5b, 0xd1, 0x47, 0x2c, 0xfa, 0xe0, 0x13, 0xc5, 0xe2, 0xb0, 0x57, 0x5c, 0x17, 0x62, 0xaa, 0x83, 0x1c, 0x4f, 0xa0, 0x0a, 0xed, 0x6c, 0x42, 0x41, 0x8a, 0x45, 0x03, 0xb8, 0x72, 0xa8, 0x99, 0xd7, 0x06 };
// Expected RC4 result
static uint8_t sha256text[32] = { 0x19, 0x71, 0x9b, 0xf0, 0xc6, 0xd8, 0x34, 0xc9, 0x6e, 0x8a, 0x56, 0xcc, 0x34, 0x45, 0xb7, 0x1d, 0x5b, 0x74, 0x9c, 0x52, 0x40, 0xcd, 0x30, 0xa2, 0xc2, 0x84, 0x53, 0x83, 0x16, 0xf8, 0x1a, 0xbb };
// Expected SHA-256 hash result
fputs("_pdfioAESInit(128-bit sample key): ", stdout);
_pdfioCryptoAESInit(&aes, aes128key, sizeof(aes128key), NULL);
if (!memcmp(aes128rounds, aes.round_key, sizeof(aes128rounds)))
{
puts("PASS");
}
else
{
for (i = 0; i < (sizeof(aes128rounds) - 4); i ++)
{
if (aes.round_key[i] != aes128rounds[i])
break;
}
prefix = i > 0 ? "..." : "";
suffix = i < (sizeof(aes128rounds) - 4) ? "..." : "";
printf("FAIL (got '%s%02X%02X%02X%02X%s', expected '%s%02X%02X%02X%02X%s')\n", prefix, aes.round_key[i], aes.round_key[i + 1], aes.round_key[i + 2], aes.round_key[i + 3], suffix, prefix, aes128rounds[i], aes128rounds[i + 1], aes128rounds[i + 2], aes128rounds[i + 3], suffix);
ret = 1;
}
fputs("_pdfioAESInit/Encrypt(128-bit CBC): ", stdout);
for (i = 0; i < 16; i ++)
{
key[i] = (uint8_t)i + 1;
iv[i] = (uint8_t)(0xff - i);
}
_pdfioCryptoAESInit(&aes, key, 16, iv);
_pdfioCryptoAESEncrypt(&aes, buffer, (uint8_t *)text, strlen(text));
if (!memcmp(aes128text, buffer, sizeof(aes128text)))
{
puts("PASS");
}
else
{
for (i = 0; i < (sizeof(aes128text) - 4); i ++)
{
if (buffer[i] != aes128text[i])
break;
}
prefix = i > 0 ? "..." : "";
suffix = i < (sizeof(aes128text) - 4) ? "..." : "";
printf("FAIL (got '%s%02X%02X%02X%02X%s', expected '%s%02X%02X%02X%02X%s')\n", prefix, buffer[i], buffer[i + 1], buffer[i + 2], buffer[i + 3], suffix, prefix, aes128text[i], aes128text[i + 1], aes128text[i + 2], aes128text[i + 3], suffix);
ret = 1;
}
fputs("_pdfioAESInit/Decrypt(128-bit CBC): ", stdout);
_pdfioCryptoAESInit(&aes, key, 16, iv);
_pdfioCryptoAESDecrypt(&aes, buffer2, buffer, sizeof(aes128text));
if (!memcmp(buffer2, text, strlen(text)))
{
puts("PASS");
}
else
{
for (i = 0; text[i + 4]; i ++)
{
if (buffer2[i] != text[i])
break;
}
prefix = i > 0 ? "..." : "";
suffix = text[i + 4] ? "..." : "";
printf("FAIL (got '%s%02X%02X%02X%02X%s', expected '%s%02X%02X%02X%02X%s')\n", prefix, buffer2[i], buffer2[i + 1], buffer2[i + 2], buffer2[i + 3], suffix, prefix, text[i], text[i + 1], text[i + 2], text[i + 3], suffix);
ret = 1;
}
fputs("_pdfioAESInit/Encrypt(256-bit CBC): ", stdout);
for (i = 0; i < 32; i ++)
{
key[i] = (uint8_t)i + 1;
iv[i] = (uint8_t)(0xff - i);
}
_pdfioCryptoAESInit(&aes, key, 32, iv);
_pdfioCryptoAESEncrypt(&aes, buffer, (uint8_t *)text, strlen(text));
if (!memcmp(aes256text, buffer, sizeof(aes256text)))
{
puts("PASS");
}
else
{
for (i = 0; i < (sizeof(aes256text) - 4); i ++)
{
if (buffer[i] != aes256text[i])
break;
}
prefix = i > 0 ? "..." : "";
suffix = i < (sizeof(aes256text) - 4) ? "..." : "";
printf("FAIL (got '%s%02X%02X%02X%02X%s', expected '%s%02X%02X%02X%02X%s')\n", prefix, buffer[i], buffer[i + 1], buffer[i + 2], buffer[i + 3], suffix, prefix, aes256text[i], aes256text[i + 1], aes256text[i + 2], aes256text[i + 3], suffix);
ret = 1;
}
fputs("_pdfioAESInit/Decrypt(256-bit CBC): ", stdout);
_pdfioCryptoAESInit(&aes, key, 32, iv);
_pdfioCryptoAESDecrypt(&aes, buffer2, buffer, sizeof(aes256text));
if (!memcmp(buffer2, text, strlen(text)))
{
puts("PASS");
}
else
{
for (i = 0; text[i + 4]; i ++)
{
if (buffer2[i] != text[i])
break;
}
prefix = i > 0 ? "..." : "";
suffix = text[i + 4] ? "..." : "";
printf("FAIL (got '%s%02X%02X%02X%02X%s', expected '%s%02X%02X%02X%02X%s')\n", prefix, buffer2[i], buffer2[i + 1], buffer2[i + 2], buffer2[i + 3], suffix, prefix, text[i], text[i + 1], text[i + 2], text[i + 3], suffix);
ret = 1;
}
fputs("_pdfioMD5Init/Append/Finish: ", stdout);
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, (uint8_t *)text, strlen(text));
_pdfioCryptoMD5Finish(&md5, buffer);
if (!memcmp(md5text, buffer, sizeof(md5text)))
{
puts("PASS");
}
else
{
printf("FAIL (got '%02X%02X%02X%02X...%02X%02X%02X%02X', expected '%02X%02X%02X%02X...%02X%02X%02X%02X')\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[12], buffer[13], buffer[14], buffer[15], md5text[0], md5text[1], md5text[2], md5text[3], md5text[12], md5text[13], md5text[14], md5text[15]);
ret = 1;
}
fputs("_pdfioRC4Init/Encrypt(128-bit): ", stdout);
for (i = 0; i < 16; i ++)
key[i] = (uint8_t)i + 1;
_pdfioCryptoRC4Init(&rc4, key, 16);
_pdfioCryptoRC4Crypt(&rc4, buffer, (uint8_t *)text, strlen(text));
if (!memcmp(rc4text, buffer, sizeof(rc4text)))
{
puts("PASS");
}
else
{
for (i = 0; i < (sizeof(rc4text) - 4); i ++)
{
if (buffer[i] != rc4text[i])
break;
}
prefix = i > 0 ? "..." : "";
suffix = i < (sizeof(rc4text) - 4) ? "..." : "";
printf("FAIL (got '%s%02X%02X%02X%02X%s', expected '%s%02X%02X%02X%02X%s')\n", prefix, buffer[i], buffer[i + 1], buffer[i + 2], buffer[i + 3], suffix, prefix, rc4text[i], rc4text[i + 1], rc4text[i + 2], rc4text[i + 3], suffix);
ret = 1;
}
fputs("_pdfioRC4Init/Decrypt(128-bit): ", stdout);
_pdfioCryptoRC4Init(&rc4, key, 16);
_pdfioCryptoRC4Crypt(&rc4, buffer2, buffer, strlen(text));
if (!memcmp(buffer2, text, strlen(text)))
{
puts("PASS");
}
else
{
for (i = 0; text[i + 4]; i ++)
{
if (buffer2[i] != text[i])
break;
}
prefix = i > 0 ? "..." : "";
suffix = text[i + 4] ? "..." : "";
printf("FAIL (got '%s%02X%02X%02X%02X%s', expected '%s%02X%02X%02X%02X%s')\n", prefix, buffer2[i], buffer2[i + 1], buffer2[i + 2], buffer2[i + 3], suffix, prefix, text[i], text[i + 1], text[i + 2], text[i + 3], suffix);
ret = 1;
}
fputs("_pdfioSHA256Init/Append/Finish: ", stdout);
_pdfioCryptoSHA256Init(&sha256);
_pdfioCryptoSHA256Append(&sha256, (uint8_t *)text, strlen(text));
_pdfioCryptoSHA256Finish(&sha256, buffer);
if (!memcmp(sha256text, buffer, sizeof(sha256text)))
{
puts("PASS");
}
else
{
printf("FAIL (got '%02X%02X%02X%02X...%02X%02X%02X%02X', expected '%02X%02X%02X%02X...%02X%02X%02X%02X')\n", buffer[0], buffer[1], buffer[2], buffer[3], buffer[28], buffer[29], buffer[30], buffer[31], sha256text[0], sha256text[1], sha256text[2], sha256text[3], sha256text[28], sha256text[29], sha256text[30], sha256text[31]);
ret = 1;
}
return (ret);
}
//
// 'do_test_file()' - Try loading a PDF file and listing pages and objects.
//
@ -133,7 +381,7 @@ do_test_file(const char *filename, // I - PDF filename
fflush(stdout);
}
if ((pdf = pdfioFileOpen(filename, (pdfio_error_cb_t)error_cb, &error)) != NULL)
if ((pdf = pdfioFileOpen(filename, /*password_cb*/NULL, /*password_data*/NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
{
if (objnum)
{
@ -249,17 +497,13 @@ do_test_file(const char *filename, // I - PDF filename
static int // O - Exit status
do_unit_tests(void)
{
int i; // Looping var
pdfio_file_t *pdf, // Test PDF file
pdfio_file_t *inpdf, // Input PDF file
*outpdf; // Output PDF file
int outfd; // Output file descriptor
bool error = false; // Error callback data
_pdfio_token_t tb; // Token buffer
const char *s; // String buffer
_pdfio_value_t value; // Value
pdfio_obj_t *color_jpg, // color.jpg image
*gray_jpg, // gray.jpg image
*helvetica, // Helvetica font
*page; // Page from test PDF file
size_t first_image, // First image object
num_pages; // Number of pages written
static const char *complex_dict = // Complex dictionary value
@ -716,7 +960,7 @@ do_unit_tests(void)
// First open the test PDF file...
fputs("pdfioFileOpen(\"testfiles/testpdfio.pdf\"): ", stdout);
if ((pdf = pdfioFileOpen("testfiles/testpdfio.pdf", (pdfio_error_cb_t)error_cb, &error)) != NULL)
if ((inpdf = pdfioFileOpen("testfiles/testpdfio.pdf", /*password_cb*/NULL, /*password_data*/NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
@ -726,8 +970,8 @@ do_unit_tests(void)
// Test the value parsers for edge cases...
fputs("_pdfioValueRead(complex_dict): ", stdout);
s = complex_dict;
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s);
if (_pdfioValueRead(pdf, &tb, &value))
_pdfioTokenInit(&tb, inpdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s);
if (_pdfioValueRead(inpdf, NULL, &tb, &value))
{
// TODO: Check value...
fputs("PASS: ", stdout);
@ -735,13 +979,13 @@ do_unit_tests(void)
puts("\n");
}
else
return (1);
goto fail;
// Test the value parsers for edge cases...
fputs("_pdfioValueRead(cid_dict): ", stdout);
s = cid_dict;
_pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s);
if (_pdfioValueRead(pdf, &tb, &value))
_pdfioTokenInit(&tb, inpdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s);
if (_pdfioValueRead(inpdf, NULL, &tb, &value))
{
// TODO: Check value...
fputs("PASS: ", stdout);
@ -749,6 +993,10 @@ do_unit_tests(void)
puts("\n");
}
else
goto fail;
// Do crypto tests...
if (do_crypto_tests())
return (1);
// Create a new PDF file...
@ -756,235 +1004,118 @@ do_unit_tests(void)
if ((outpdf = pdfioFileCreate("testpdfio-out.pdf", NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
goto fail;
// Set info values...
fputs("pdfioFileGet/SetAuthor: ", stdout);
pdfioFileSetAuthor(outpdf, "Michael R Sweet");
if ((s = pdfioFileGetAuthor(outpdf)) != NULL && !strcmp(s, "Michael R Sweet"))
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
goto fail;
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
goto fail;
// Stream a new PDF file...
if ((outfd = open("testpdfio-out2.pdf", O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0666)) < 0)
{
perror("Unable to open \"testpdfio-out2.pdf\"");
goto fail;
}
fputs("pdfioFileCreateOutput(...): ", stdout);
if ((outpdf = pdfioFileCreateOutput((pdfio_output_cb_t)output_cb, &outfd, NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Michael R Sweet')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Michael R Sweet')");
return (1);
}
goto fail;
fputs("pdfioFileGet/SetCreator: ", stdout);
pdfioFileSetCreator(outpdf, "testpdfio");
if ((s = pdfioFileGetCreator(outpdf)) != NULL && !strcmp(s, "testpdfio"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'testpdfio')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'testpdfio')");
return (1);
}
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
goto fail;
fputs("pdfioFileGet/SetKeywords: ", stdout);
pdfioFileSetKeywords(outpdf, "one fish,two fish,red fish,blue fish");
if ((s = pdfioFileGetKeywords(outpdf)) != NULL && !strcmp(s, "one fish,two fish,red fish,blue fish"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'one fish,two fish,red fish,blue fish')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'one fish,two fish,red fish,blue fish')");
return (1);
}
close(outfd);
fputs("pdfioFileGet/SetSubject: ", stdout);
pdfioFileSetSubject(outpdf, "Unit test document");
if ((s = pdfioFileGetSubject(outpdf)) != NULL && !strcmp(s, "Unit test document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Unit test document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Unit test document')");
return (1);
}
if (read_unit_file("testpdfio-out2.pdf", num_pages, first_image, true))
goto fail;
fputs("pdfioFileGet/SetTitle: ", stdout);
pdfioFileSetTitle(outpdf, "Test Document");
if ((s = pdfioFileGetTitle(outpdf)) != NULL && !strcmp(s, "Test Document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Test Document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Test Document')");
return (1);
}
// Create some image objects...
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/color.jpg\"): ", stdout);
if ((color_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/color.jpg", true)) != NULL)
// Create new encrypted PDF files...
fputs("pdfioFileCreate(\"testpdfio-rc4.pdf\", ...): ", stdout);
if ((outpdf = pdfioFileCreate("testpdfio-rc4.pdf", NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/gray.jpg\"): ", stdout);
if ((gray_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/gray.jpg", true)) != NULL)
fputs("pdfioFileSetPermissions(all, RC4-128, no passwords): ", stdout);
if (pdfioFileSetPermissions(outpdf, PDFIO_PERMISSION_ALL, PDFIO_ENCRYPTION_RC4_128, NULL, NULL))
puts("PASS");
else
return (1);
// Create fonts...
fputs("pdfioFileCreateFontObjFromBase(\"Helvetica\"): ", stdout);
if ((helvetica = pdfioFileCreateFontObjFromBase(outpdf, "Helvetica")) != NULL)
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false))
return (1);
// Create new encrypted PDF files...
fputs("pdfioFileCreate(\"testpdfio-rc4p.pdf\", ...): ", stdout);
if ((outpdf = pdfioFileCreate("testpdfio-rc4p.pdf", NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
// Copy the first page from the test PDF file...
fputs("pdfioFileGetPage(0): ", stdout);
if ((page = pdfioFileGetPage(pdf, 0)) != NULL)
fputs("pdfioFileSetPermissions(no-print, RC4-128, passwords='owner' and 'user'): ", stdout);
if (pdfioFileSetPermissions(outpdf, PDFIO_PERMISSION_ALL ^ PDFIO_PERMISSION_PRINT, PDFIO_ENCRYPTION_RC4_128, "owner", "user"))
puts("PASS");
else
return (1);
fputs("pdfioPageCopy(first page): ", stdout);
if (pdfioPageCopy(outpdf, page))
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file("testpdfio-rc4p.pdf", num_pages, first_image, false))
return (1);
fputs("pdfioFileCreate(\"testpdfio-aes.pdf\", ...): ", stdout);
if ((outpdf = pdfioFileCreate("testpdfio-aes.pdf", NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
// Write a page with a color image...
if (write_jpeg_test(outpdf, "Color JPEG Test", 2, helvetica, color_jpg))
return (1);
// Copy the third page from the test PDF file...
fputs("pdfioFileGetPage(2): ", stdout);
if ((page = pdfioFileGetPage(pdf, 2)) != NULL)
fputs("pdfioFileSetPermissions(all, AES-128, no passwords): ", stdout);
if (pdfioFileSetPermissions(outpdf, PDFIO_PERMISSION_ALL, PDFIO_ENCRYPTION_AES_128, NULL, NULL))
puts("PASS");
else
return (1);
fputs("pdfioPageCopy(third page): ", stdout);
if (pdfioPageCopy(outpdf, page))
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false))
return (1);
fputs("pdfioFileCreate(\"testpdfio-aesp.pdf\", ...): ", stdout);
if ((outpdf = pdfioFileCreate("testpdfio-aesp.pdf", NULL, NULL, NULL, (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
// Write a page with a grayscale image...
if (write_jpeg_test(outpdf, "Grayscale JPEG Test", 4, helvetica, gray_jpg))
return (1);
// Write a page with PNG images...
if (write_png_test(outpdf, 5, helvetica))
return (1);
// Write a page that tests multiple color spaces...
if (write_color_test(outpdf, 6, helvetica))
return (1);
// Write a page with test images...
first_image = pdfioFileGetNumObjs(outpdf) + 1;
if (write_images_test(outpdf, 7, helvetica))
return (1);
// Write a page width alpha (soft masks)...
if (write_alpha_test(outpdf, 8, helvetica))
return (1);
// Test TrueType fonts...
if (write_font_test(outpdf, 9, helvetica, false))
return (1);
if (write_font_test(outpdf, 10, helvetica, true))
return (1);
// Print this text file...
if (write_text_test(outpdf, 11, helvetica, "README.md"))
return (1);
// Close the test PDF file...
fputs("pdfioFileClose(\"testfiles/testpdfio.pdf\"): ", stdout);
if (pdfioFileClose(pdf))
fputs("pdfioFileSetPermissions(no-print, AES-128, passwords='owner' and 'user'): ", stdout);
if (pdfioFileSetPermissions(outpdf, PDFIO_PERMISSION_ALL ^ PDFIO_PERMISSION_PRINT, PDFIO_ENCRYPTION_AES_128, "owner", "user"))
puts("PASS");
else
return (1);
fputs("pdfioFileGetNumPages: ", stdout);
if ((num_pages = pdfioFileGetNumPages(outpdf)) > 0)
{
printf("PASS (%lu)\n", (unsigned long)num_pages);
}
else
{
puts("FAIL");
return (1);
}
// Close the new PDF file...
fputs("pdfioFileClose(\"testpdfio-out.pdf\"): ", stdout);
if (pdfioFileClose(outpdf))
puts("PASS");
else
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
return (1);
// Open the new PDF file to read it...
fputs("pdfioFileOpen(\"testpdfio-out.pdf\", ...): ", stdout);
if ((pdf = pdfioFileOpen("testpdfio-out.pdf", (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false))
return (1);
// Verify the number of pages is the same...
fputs("pdfioFileGetNumPages: ", stdout);
if (num_pages == pdfioFileGetNumPages(pdf))
{
puts("PASS");
}
else
{
printf("FAIL (%lu != %lu)\n", (unsigned long)num_pages, (unsigned long)pdfioFileGetNumPages(pdf));
return (1);
}
// Verify the images
for (i = 0; i < 7; i ++)
{
if (verify_image(pdf, first_image + (size_t)i))
return (1);
}
// Close the new PDF file...
fputs("pdfioFileClose(\"testpdfio-out.pdf\"): ", stdout);
if (pdfioFileClose(pdf))
puts("PASS");
else
return (1);
pdfioFileClose(inpdf);
return (0);
fail:
pdfioFileClose(inpdf);
return (1);
}
@ -1068,6 +1199,90 @@ error_cb(pdfio_file_t *pdf, // I - PDF file
}
//
// 'output_cb()' - Write output to a file.
//
static ssize_t // O - Number of bytes written
output_cb(int *fd, // I - File descriptor
const void *buffer, // I - Output buffer
size_t bytes) // I - Number of bytes to write
{
return (write(*fd, buffer, bytes));
}
//
// 'password_cb()' - Password callback for PDF file.
//
static const char * // O - Password string
password_cb(void *data, // I - Callback data
const char *filename) // I - Filename (not used)
{
(void)filename;
return ((const char *)data);
}
//
// 'read_unit_file()' - Read back a unit test file and confirm its contents.
//
static int // O - Exit status
read_unit_file(const char *filename, // I - File to read
size_t num_pages, // I - Expected number of pages
size_t first_image, // I - First image object
bool is_output) // I - File written with output callback?
{
pdfio_file_t *pdf; // PDF file
size_t i; // Looping var
bool error = false; // Error callback data
// Open the new PDF file to read it...
printf("pdfioFileOpen(\"%s\", ...): ", filename);
if ((pdf = pdfioFileOpen(filename, password_cb, (void *)"user", (pdfio_error_cb_t)error_cb, &error)) != NULL)
puts("PASS");
else
return (1);
// Verify the number of pages is the same...
fputs("pdfioFileGetNumPages: ", stdout);
if (num_pages == pdfioFileGetNumPages(pdf))
{
puts("PASS");
}
else
{
printf("FAIL (%lu != %lu)\n", (unsigned long)num_pages, (unsigned long)pdfioFileGetNumPages(pdf));
return (1);
}
// Verify the images
for (i = 0; i < 7; i ++)
{
if (is_output)
{
if (verify_image(pdf, first_image + (size_t)i * 2))
return (1);
}
else if (verify_image(pdf, first_image + (size_t)i))
return (1);
}
// Close the new PDF file...
fputs("pdfioFileClose(\"testpdfio-out.pdf\"): ", stdout);
if (pdfioFileClose(pdf))
puts("PASS");
else
return (1);
return (0);
}
//
// 'token_consume_cb()' - Consume bytes from a test string.
//
@ -2843,3 +3058,211 @@ write_text_test(pdfio_file_t *pdf, // I - PDF file
pdfioStreamClose(st);
return (1);
}
//
// 'write_unit_file()' - Write a unit test file.
//
static int // O - Exit status
write_unit_file(
pdfio_file_t *inpdf, // I - Input PDF file
pdfio_file_t *outpdf, // I - Output PDF file
size_t *num_pages, // O - Number of pages
size_t *first_image) // O - First image object
{
const char *s; // String buffer
pdfio_obj_t *color_jpg, // color.jpg image
*gray_jpg, // gray.jpg image
*helvetica, // Helvetica font
*page; // Page from test PDF file
// Set info values...
fputs("pdfioFileGet/SetAuthor: ", stdout);
pdfioFileSetAuthor(outpdf, "Michael R Sweet");
if ((s = pdfioFileGetAuthor(outpdf)) != NULL && !strcmp(s, "Michael R Sweet"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Michael R Sweet')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Michael R Sweet')");
return (1);
}
fputs("pdfioFileGet/SetCreator: ", stdout);
pdfioFileSetCreator(outpdf, "testpdfio");
if ((s = pdfioFileGetCreator(outpdf)) != NULL && !strcmp(s, "testpdfio"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'testpdfio')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'testpdfio')");
return (1);
}
fputs("pdfioFileGet/SetKeywords: ", stdout);
pdfioFileSetKeywords(outpdf, "one fish,two fish,red fish,blue fish");
if ((s = pdfioFileGetKeywords(outpdf)) != NULL && !strcmp(s, "one fish,two fish,red fish,blue fish"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'one fish,two fish,red fish,blue fish')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'one fish,two fish,red fish,blue fish')");
return (1);
}
fputs("pdfioFileGet/SetSubject: ", stdout);
pdfioFileSetSubject(outpdf, "Unit test document");
if ((s = pdfioFileGetSubject(outpdf)) != NULL && !strcmp(s, "Unit test document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Unit test document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Unit test document')");
return (1);
}
fputs("pdfioFileGet/SetTitle: ", stdout);
pdfioFileSetTitle(outpdf, "Test Document");
if ((s = pdfioFileGetTitle(outpdf)) != NULL && !strcmp(s, "Test Document"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'Test Document')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'Test Document')");
return (1);
}
// Create some image objects...
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/color.jpg\"): ", stdout);
if ((color_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/color.jpg", true)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioFileCreateImageObjFromFile(\"testfiles/gray.jpg\"): ", stdout);
if ((gray_jpg = pdfioFileCreateImageObjFromFile(outpdf, "testfiles/gray.jpg", true)) != NULL)
puts("PASS");
else
return (1);
// Create fonts...
fputs("pdfioFileCreateFontObjFromBase(\"Helvetica\"): ", stdout);
if ((helvetica = pdfioFileCreateFontObjFromBase(outpdf, "Helvetica")) != NULL)
puts("PASS");
else
return (1);
// Copy the first page from the test PDF file...
fputs("pdfioFileGetPage(0): ", stdout);
if ((page = pdfioFileGetPage(inpdf, 0)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioPageCopy(first page): ", stdout);
if (pdfioPageCopy(outpdf, page))
puts("PASS");
else
return (1);
// Write a page with a color image...
if (write_jpeg_test(outpdf, "Color JPEG Test", 2, helvetica, color_jpg))
return (1);
// Copy the third page from the test PDF file...
fputs("pdfioFileGetPage(2): ", stdout);
if ((page = pdfioFileGetPage(inpdf, 2)) != NULL)
puts("PASS");
else
return (1);
fputs("pdfioPageCopy(third page): ", stdout);
if (pdfioPageCopy(outpdf, page))
puts("PASS");
else
return (1);
// Write a page with a grayscale image...
if (write_jpeg_test(outpdf, "Grayscale JPEG Test", 4, helvetica, gray_jpg))
return (1);
// Write a page with PNG images...
if (write_png_test(outpdf, 5, helvetica))
return (1);
// Write a page that tests multiple color spaces...
if (write_color_test(outpdf, 6, helvetica))
return (1);
// Write a page with test images...
*first_image = pdfioFileGetNumObjs(outpdf) + 1;
if (write_images_test(outpdf, 7, helvetica))
return (1);
// Write a page width alpha (soft masks)...
if (write_alpha_test(outpdf, 8, helvetica))
return (1);
// Test TrueType fonts...
if (write_font_test(outpdf, 9, helvetica, false))
return (1);
if (write_font_test(outpdf, 10, helvetica, true))
return (1);
// Print this text file...
if (write_text_test(outpdf, 11, helvetica, "README.md"))
return (1);
fputs("pdfioFileGetNumPages: ", stdout);
if ((*num_pages = pdfioFileGetNumPages(outpdf)) > 0)
{
printf("PASS (%lu)\n", (unsigned long)*num_pages);
}
else
{
puts("FAIL");
return (1);
}
// Close the new PDF file...
fputs("pdfioFileClose(...): ", stdout);
if (pdfioFileClose(outpdf))
puts("PASS");
else
return (1);
return (0);
}

3
ttf.c
View File

@ -513,7 +513,8 @@ ttfDelete(ttf_t *font) // I - Font
return;
// Close the font file...
close(font->fd);
if (font->fd >= 0)
close(font->fd);
// Free all memory used...
free(font->copyright);