51 Commits

Author SHA1 Message Date
986cc512cd Bump NuGet project versions. 2024-08-05 21:50:18 -04:00
c35ddbec00 Changelog 2024-08-05 21:49:26 -04:00
e4e1c39578 Merge commit from fork
Add range checking to TTF loader.
2024-08-05 21:47:48 -04:00
1d4f77cab1 Add examples to documentation (Issue #69) 2024-08-05 21:44:56 -04:00
b035130cde Merge pull request #68 from devnibo/master
Update documentation
2024-08-05 19:56:40 -04:00
d6d5813b04 Update changelog with CVE number. 2024-08-05 16:34:12 -04:00
6492f210cf Bump version and changelog. 2024-08-05 10:23:51 -04:00
207062a996 Add size limiting for num_cmap and nGlyphs. 2024-08-05 10:16:00 -04:00
7d37abb0df Update documentation 2024-07-07 16:35:56 +02:00
0c1122b689 Prep for release. 2024-06-28 19:06:44 -04:00
d4f8dd46b5 Add Windows test script. 2024-06-28 19:00:51 -04:00
986c5f0438 Update docos. 2024-06-24 11:51:50 -04:00
a81907bdb9 Refactor get_info_string to rely on pdfioDictGetString to convert binary strings to regular ones. 2024-06-24 11:49:38 -04:00
63a7a2cdbd Add unit tests for new pdfioFileGetCatalog API (Issue #67)
Fix pdfioDictGetString to convert (formerly) encrypted binary strings to
regular strings.
2024-06-24 11:46:15 -04:00
f040cc41c2 Add #define guard to allow MingW to build PDFio; note that MingW is NOT a supported toolchain for PDFio (Issue #66) 2024-06-24 09:03:46 -04:00
23883268e3 Add pdfioFileGetCatalog function (Issue #67)
Refactor the pdfioFileCreateXxx functions to use a common (private) function to
handle creating/initializing the pdfio_file_t object and base file objects.

Update unit tests to display the filename for the pdfioFileClose test.
2024-06-24 08:56:16 -04:00
a1e14503fd Bump version in other files, update makesrcdist to support checking. 2024-06-24 07:28:54 -04:00
0766869ad1 Bump version to 1.3.0. 2024-06-24 07:12:01 -04:00
6c1db141a1 Switch string pool code to an insertion sort - provides a modest 25% improvement
to open speeds on typical files.
2024-01-27 20:58:50 -05:00
b117959725 Make sure all output code paths set the locale information (Issue #61) 2024-01-27 19:23:51 -05:00
e882622233 Fix locale support (Issue #61) 2024-01-27 18:22:16 -05:00
c13b5a5e90 Bump version. 2024-01-27 18:20:36 -05:00
cd1406e158 Update docos.
Fix static library build commands - remove archive before building it fresh.
2024-01-24 11:03:58 -05:00
59deee020a Fix some Clang warnings. 2024-01-24 10:58:11 -05:00
476013706e Prep for 1.2.0 release, bump copyright. 2024-01-24 10:53:53 -05:00
a43a9d9e32 Fix whitespace. 2023-12-18 10:04:24 -05:00
abc69b3361 Save work. 2023-12-18 10:04:00 -05:00
83bfb135c6 Add some more debug printfs, relocate extra newline detection after stream
token.
2023-12-15 12:57:31 -05:00
2dfb560f8b Add more debug logging. 2023-12-14 17:05:10 -05:00
7330cc35ba Defer object/value decryption to after the object is loaded (Issue #42) 2023-12-14 16:02:26 -05:00
5d760e7315 Update some debug printfs. 2023-12-13 12:48:31 -05:00
2a85baaf81 Increase the maximum number of object streams in a file (Issue #58) - most files
only contain 1 or 2...

Change the implementation of add/find object to use a custom binary insertion
sort algorithm rather than doing a qsort after every addition.  This results in
a significant improvement in open speed - from 2371 seconds (about 39.5 minutes)
to 3.1 seconds for one large test file (an ESRI standard).
2023-12-13 12:26:25 -05:00
2b92044504 Support per-object file IDs (Issue #42) 2023-12-12 21:48:58 -05:00
f4aa951165 Fix _pdfioFileSeek with whence==SEEK_CUR
Fix seek offset after trailer.

Look at the last 1k of the file to find the startxref marker.
2023-12-12 12:24:49 -05:00
038fd8686b Fix trailer dictionary handling (Issue #58)
Fix generation number handling for object 0 (Issue #59)
2023-12-11 19:56:00 -05:00
7084105dc4 Merge pull request #57 from eli-schwartz/pdfio-pc-redundancy
pdfio.pc: use -lm as specified in configure
2023-12-10 19:35:37 -05:00
9f06f22281 pdfio.pc: use -lm as specified in configure
It is already configured in, in the correct place. Currently, it is
listed twice in Libs.private, if --enable-shared is used. And it is
redundant if the build is static instead, since it is recorded in Libs.
2023-12-10 16:32:52 -05:00
cb6b493df6 Update configure script. 2023-12-10 15:38:35 -05:00
2753a82eb9 Merge pull request #56 from eli-schwartz/misspelled
fix misspelled variable: PKCONFIG
2023-12-10 15:38:12 -05:00
ddb8ddff9c fix misspelled variable: PKCONFIG
This prevented using pkg-config for zlib lookup.
2023-12-10 01:39:23 -05:00
c992b2ba89 Update the token reading code to protect against obvious format abuses.
Update the xref loading code to protect against looping xref tables.
2023-12-07 17:50:52 -05:00
ed723a46dc Make sure buffer is terminated on error. 2023-12-06 11:21:33 -05:00
6906a9a708 Fix docos for pdfioFileOpen. 2023-12-05 19:22:47 -05:00
6a381a55fe Update macOS build docos. 2023-12-05 18:41:26 -05:00
fc3580a948 Update build docos. 2023-12-05 18:39:20 -05:00
6b5c30b4be Remove PDFIO_ENCRYPTION_AES_256 from docos. 2023-12-05 14:30:41 -05:00
a0cdb261ff Update CONTRIBUTING docos. 2023-12-05 14:07:52 -05:00
34dbf6c2fe Documentation cleanup. 2023-12-05 13:49:58 -05:00
86d842167a Bring back mis-named pdfioContentTextNextLine. 2023-12-05 13:33:07 -05:00
16c8b830b8 Add pdfioFileCreateNumber/StringObj functions (Issue #14) 2023-12-05 08:16:41 -05:00
7ff051fc8b Add pdfioContentTextNewLineShow/f functions (Issue #24) 2023-12-05 07:49:49 -05:00
36 changed files with 2593 additions and 1083 deletions

View File

@ -60,3 +60,5 @@ jobs:
run: nuget restore pdfio.sln
- name: Build PDFio
run: msbuild pdfio.sln
- name: Test PDFio
run: .\runtests.bat x64\Debug

View File

@ -2,11 +2,47 @@ Changes in PDFio
================
v1.2.0 (Month DD, YYYY)
v1.3.1 (August 5, 2024)
-----------------------
- Added `pdfioContentTextMeasure` function (Issue #17)
- CVE 2024-42358: Updated TrueType font reader to avoid large memory
allocations.
- Fixed some documentation errors and added examples (Issue #68, Issue #69)
v1.3.0 (June 28, 2024)
----------------------
- Added `pdfioFileGetCatalog` API for accessing the root/catalog object of a
PDF file (Issue #67)
- Updated number support to avoid locale issues (Issue #61)
- Updated the PDFio private header to allow compilation with MingW; note that
MingW is NOT a supported toolchain for PDFio (Issue #66)
- Optimized string pool code.
v1.2.0 (January 24, 2024)
-------------------------
- Now use autoconf to configure the PDFio sources (Issue #54)
- Added `pdfioFileCreateNumberObj` and `pdfioFileCreateStringObj` functions
(Issue #14)
- Added `pdfioContentTextMeasure` function (Issue #17)
- Added `pdfioContentTextNewLineShow` and `pdfioContentTextNewLineShowf`
functions (Issue #24)
- Renamed `pdfioContentTextNextLine` to `pdfioContentTextNewLine`.
- Updated the maximum number of object streams in a single file from 4096 to
8192 (Issue #58)
- Updated the token reading code to protect against some obvious abuses of the
PDF format.
- Updated the xref reading code to protect against loops.
- Updated the object handling code to use a binary insertion algorithm -
provides a significant (~800x) improvement in open times.
- Fixed handling of encrypted PDFs with per-object file IDs (Issue #42)
- Fixed handling of of trailer dictionaries that started immediately after the
"trailer" keyword (Issue #58)
- Fixed handling of invalid, but common, PDF files with a generation number of
65536 in the xref table (Issue #59)
v1.1.4 (December 3, 2023)

View File

@ -43,14 +43,17 @@ Contents
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.
The build system uses [GNU autoconf][AUTOCONF] to create a simple POSIX makefile
to build static and/or shared libraries. 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.
[AUTOCONF]: https://www.gnu.org/software/autoconf/
Version Numbering
-----------------
@ -373,14 +376,15 @@ The following variables are defined in the makefile:
- `CC`; the C compiler command,
- `CFLAGS`; options for the C compiler,
- `CODESIGN_IDENTITY`: the code signing identity,
- `COMMONFLAGS`; common compiler optimization options,
- `CPPFLAGS`; options for the C preprocessor,
- `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,
- `LIBPDFIO`: the name of the primary (shared or static) library
- `LIBPDFIO_STATIC`: the name of the secondary (static) library
- `LIBS`; libraries for all programs,
- `OPTIM`; common compiler optimization options,
- `prefix`; the installation prefix directory,
- `RANLIB`; the static library indexing command,
- `SHELL`; the sh (POSIX shell) command,
@ -389,12 +393,7 @@ The following variables are defined in the makefile:
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.
- `macos`; same as `all` but creates a Universal Binary (X64 + ARM64).
- `test`; runs the unit test program, building it as needed.

View File

@ -1,7 +1,7 @@
#
# Makefile for PDFio.
#
# Copyright © 2021-2023 by Michael R Sweet.
# Copyright © 2021-2024 by Michael R Sweet.
#
# Licensed under Apache License v2.0. See the file "LICENSE" for more
# information.
@ -163,7 +163,9 @@ install: $(TARGETS)
# Test everything
test: testpdfio testttf
./testttf 2>test.log
./testpdfio 2>test.log
./testpdfio 2>>test.log
LANG=fr_FR.UTF-8 ./testpdfio 2>>test.log
valgrind: testpdfio
valgrind --leak-check=full ./testpdfio
@ -172,6 +174,7 @@ valgrind: testpdfio
# pdfio library
libpdfio.a: $(LIBOBJS)
echo Archiving $@...
$(RM) $@
$(AR) $(ARFLAGS) $@ $(LIBOBJS)
$(RANLIB) $@
@ -226,7 +229,7 @@ ttf.o: ttf.h
# Make documentation using Codedoc <https://www.msweet.org/codedoc>
DOCFLAGS = \
--author "Michael R Sweet" \
--copyright "Copyright (c) 2021-2023 by Michael R Sweet" \
--copyright "Copyright (c) 2021-2024 by Michael R Sweet" \
--docversion $(PDFIO_VERSION)
.PHONY: doc

2
NOTICE
View File

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

View File

@ -89,7 +89,7 @@ generates a static library that will be installed under "/usr/local" with:
Legal Stuff
-----------
PDFio is Copyright © 2021-2023 by Michael R Sweet.
PDFio is Copyright © 2021-2024 by Michael R Sweet.
This software is licensed under the Apache License Version 2.0 with an
(optional) exception to allow linking against GPL2/LGPL2 software. See the

View File

@ -54,7 +54,7 @@ starting at 0. A feature release has a "PATCH" value of 0, for example:
1.1.0
2.0.0
Beta releases and release candidates are *not* prodution releases and use
Beta releases and release candidates are *not* production releases and use
semantic version numbers of the form:
MAJOR.MINORbNUMBER

29
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for pdfio 1.2.0.
# Generated by GNU Autoconf 2.71 for pdfio 1.3.1.
#
# Report bugs to <https://github.com/michaelrsweet/pdfio/issues>.
#
@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='pdfio'
PACKAGE_TARNAME='pdfio'
PACKAGE_VERSION='1.2.0'
PACKAGE_STRING='pdfio 1.2.0'
PACKAGE_VERSION='1.3.1'
PACKAGE_STRING='pdfio 1.3.1'
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
PACKAGE_URL='https://www.msweet.org/pdfio'
@ -1293,7 +1293,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures pdfio 1.2.0 to adapt to many kinds of systems.
\`configure' configures pdfio 1.3.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1359,7 +1359,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of pdfio 1.2.0:";;
short | recursive ) echo "Configuration of pdfio 1.3.1:";;
esac
cat <<\_ACEOF
@ -1456,7 +1456,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
pdfio configure 1.2.0
pdfio configure 1.3.1
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@ -1612,7 +1612,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by pdfio $as_me 1.2.0, which was
It was created by pdfio $as_me 1.3.1, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@ -2368,9 +2368,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
PDFIO_VERSION="1.2.0"
PDFIO_VERSION_MAJOR="`echo 1.2.0 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.2.0 | awk -F. '{printf("%d\n",$2);}'`"
PDFIO_VERSION="1.3.1"
PDFIO_VERSION_MAJOR="`echo 1.3.1 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.3.1 | awk -F. '{printf("%d\n",$2);}'`"
@ -4024,13 +4024,13 @@ then :
printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
fi
if $PKCONFIG --exists zlib
if $PKGCONFIG --exists zlib
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
else $as_nop
@ -4093,6 +4093,7 @@ then :
fi
PKGCONFIG_REQUIRES=""
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
fi
@ -4934,7 +4935,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by pdfio $as_me 1.2.0, which was
This file was extended by pdfio $as_me 1.3.1, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@ -4990,7 +4991,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
pdfio config.status 1.2.0
pdfio config.status 1.3.1
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"

View File

@ -1,7 +1,7 @@
dnl
dnl Configuration script for PDFio
dnl
dnl Copyright © 2023 by Michael R Sweet
dnl Copyright © 2023-2024 by Michael R Sweet
dnl
dnl Licensed under Apache License v2.0. See the file "LICENSE" for more
dnl information.
@ -21,7 +21,7 @@ AC_PREREQ([2.70])
dnl Package name and version...
AC_INIT([pdfio], [1.2.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
AC_INIT([pdfio], [1.3.1], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
PDFIO_VERSION="AC_PACKAGE_VERSION"
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"
@ -103,10 +103,10 @@ AC_SUBST([PKGCONFIG_REQUIRES])
dnl ZLIB
AC_MSG_CHECKING([for zlib via pkg-config])
AS_IF([$PKCONFIG --exists zlib], [
AS_IF([$PKGCONFIG --exists zlib], [
AC_MSG_RESULT([yes])
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
],[
AC_MSG_RESULT([no])
AC_CHECK_HEADER([zlib.h])
@ -116,6 +116,7 @@ AS_IF([$PKCONFIG --exists zlib], [
AC_MSG_ERROR([Sorry, this software requires zlib 1.1 or higher.])
])
PKGCONFIG_REQUIRES=""
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
])

View File

@ -1,4 +1,4 @@
.TH pdfio 3 "pdf read/write library" "2023-12-04" "pdf read/write library"
.TH pdfio 3 "pdf read/write library" "2024-08-05" "pdf read/write library"
.SH NAME
pdfio \- pdf read/write library
.SH Introduction
@ -34,7 +34,7 @@ PDFio is
.I not
concerned with rendering or viewing a PDF file, although a PDF RIP or viewer could be written using it.
.PP
PDFio is Copyright \[co] 2021\-2023 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
PDFio is Copyright \[co] 2021\-2024 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
.SS Requirements
.PP
PDFio requires the following to build the software:
@ -46,6 +46,10 @@ A C99 compiler such as Clang, GCC, or MS Visual C
.PP
A POSIX\-compliant make program
.IP \(bu 5
.PP
A POSIX\-compliant sh program
.IP \(bu 5
.PP
ZLIB (https://www.zlib.net) 1.0 or higher
@ -55,10 +59,11 @@ ZLIB (https://www.zlib.net) 1.0 or higher
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
.SS Installing pdfio
.PP
PDFio comes with a portable makefile that will work on any POSIX\-compliant system with ZLIB installed. To make it, run:
PDFio comes with a configure script that creates a portable makefile that will work on any POSIX\-compliant system with ZLIB installed. To make it, run:
.nf
make all
\./configure
make
.fi
.PP
To test it, run:
@ -70,78 +75,28 @@ To test it, run:
To install it, run:
.nf
make install
sudo make install
.fi
.PP
If you want a shared library, run:
.nf
make all\-shared
make install\-shared
\./configure \-\-enable\-shared
make
sudo make install
.fi
.PP
The default installation location is "/usr/local". Pass the prefix variable to make to install it to another location:
The default installation location is "/usr/local". Pass the \-\-prefix option to make to install it to another location:
.nf
make install prefix=/some/other/directory
\./configure \-\-prefix=/some/other/directory
.fi
.PP
The makefile installs the pdfio header to "${prefix}/include", the library to "${prefix}/lib", the pkg\-config file to "${prefix}/lib/pkgconfig", the man page to "${prefix}/share/man/man3", and the documentation to "${prefix}/share/doc/pdfio".
.PP
The makefile supports the following variables that can be specified in the make command or as environment variables:
.IP \(bu 5
.PP
AR: the library archiver (default "ar")
.IP \(bu 5
.PP
ARFLAGS: options for the library archiver (default "cr")
.IP \(bu 5
.PP
CC: the C compiler (default "cc")
.IP \(bu 5
.PP
CFLAGS: options for the C compiler (default "")
.IP \(bu 5
.PP
CODESIGN_IDENTITY: the identity to use when code signing the shared library on macOS (default "Developer ID")
.IP \(bu 5
.PP
COMMONFLAGS: options for the C compiler and linker (typically architecture and optimization options, default is "\-Os \-g")
.IP \(bu 5
.PP
CPPFLAGS: options for the C preprocessor (default "")
.IP \(bu 5
.PP
DESTDIR and DSTROOT: specifies a root directory when installing (default is "", specify only one)
.IP \(bu 5
.PP
DSOFLAGS: options for the C compiler when linking the shared library (default "")
.IP \(bu 5
.PP
LDFLAGS: options for the C compiler when linking the test programs (default "")
.IP \(bu 5
.PP
LIBS: library options when linking the test programs (default "\-lz")
.IP \(bu 5
.PP
RANLIB: program that generates a table\-of\-contents in a library (default "ranlib")
.IP \(bu 5
.PP
prefix: specifies the installation directory (default "/usr/local")
Other configure options can be found using the \-\-help option:
.nf
\./configure \-\-help
.fi
.SS Visual Studio Project
.PP
The Visual Studio solution ("pdfio.sln") is provided for Windows developers and generates both a static library and DLL.
@ -152,12 +107,6 @@ There is also an Xcode project ("pdfio.xcodeproj") you can use on macOS which ge
sudo xcodebuild install
.fi
.PP
You can reproduce this with the makefile using:
.nf
sudo make macos install
.fi
.SS Detecting PDFio
.PP
PDFio can be detected using the pkg\-config command, for example:
@ -218,10 +167,25 @@ pdfio_stream_t: An object stream
You open an existing PDF file using the pdfioFileOpen function:
.nf
pdfio_file_t *pdf = pdfioFileOpen("myinputfile.pdf", error_cb, error_data);
pdfio_file_t *pdf = pdfioFileOpen("myinputfile.pdf", password_cb, password_data,
error_cb, error_data);
.fi
.PP
where the three arguments to the function are the filename ("myinputfile.pdf"), an optional error callback function (error_cb), and an optional pointer value for the error callback function (error_data). The error callback is called for both errors and warnings and accepts the pdfio_file_t pointer, a message string, and the callback pointer value, for example:
where the five arguments to the function are the filename ("myinputfile.pdf"), an optional password callback function (password_cb) and data pointer value (password_data), and an optional error callback function (error_cb) and data pointer value (error_data). The password callback is called for encrypted PDF files that are not using the default password, for example:
.nf
const char *
password_cb(void *data, const char *filename)
{
(void)data; // This callback doesn't use the data pointer
(void)filename; // This callback doesn't use the filename
// Return a password string for the file...
return ("Password42");
}
.fi
.PP
The error callback is called for both errors and warnings and accepts the pdfio_file_t pointer, a message string, and the callback pointer value, for example:
.nf
bool
@ -397,10 +361,10 @@ pdfioStreamWrite writes a buffer of data to the stream
.PP
The PDF content helper functions provide additional functions for writing specific PDF page stream commands.
.PP
When you are done writing the stream, call pdfioStreamCLose to close both the stream and the object.
When you are done writing the stream, call pdfioStreamClose to close both the stream and the object.
.SS PDF Content Helper Functions
.PP
PDFio includes many helper functions for embedding or writing specific kinds of content to a PDF file. These functions can be roughly grouped into ??? categories:
PDFio includes many helper functions for embedding or writing specific kinds of content to a PDF file. These functions can be roughly grouped into five categories:
.IP \(bu 5
.PP
Color Space Functions
@ -552,6 +516,7 @@ will embed an OpenSans Regular TrueType font using the Windows CP1252 subset of
will embed the NotoSansJP Regular OpenType font with full support for Unicode.
.PP
Note: Not all fonts support Unicode.
.PP
Image Object Functions
.PP
@ -799,7 +764,15 @@ pdfioContentTextMoveTo moves within the current line in a text block
.IP \(bu 5
.PP
pdfioContentTextNextLine moves to the beginning of the next line in a text block
pdfioContentTextNewLine moves to the beginning of the next line in a text block
.IP \(bu 5
.PP
pdfioContentTextNewLineShow moves to the beginning of the next line in a text block and shows literal text with optional word and character spacing
.IP \(bu 5
.PP
pdfioContentTextNewLineShowf moves to the beginning of the next line in a text block and shows formatted text with optional word and character spacing
.IP \(bu 5
.PP
@ -814,6 +787,125 @@ pdfioContentTextShowf draws a formatted string in a text block
pdfioContentTextShowJustified draws an array of literal strings with offsets between them
.SH Examples
.SS Read PDF Metadata
.PP
The following example function will open a PDF file and print the title, author, creation date, and number of pages:
.nf
#include <pdfio.h>
#include <time.h>
void
show_pdf_info(const char *filename)
{
pdfio_file_t *pdf;
time_t creation_date;
struct tm *creation_tm;
char creation_text[256];
// Open the PDF file with the default callbacks...
pdf = pdfioFileOpen(filename, /*password_cb*/NULL, /*password_cbdata*/NULL, /*error_cb*/NULL, /*error_cbdata*/NULL);
if (pdf == NULL)
return;
// Get the creation date and convert to a string...
creation_date = pdfioFileGetCreationDate(pdf);
creation_tm = localtime(&creation_date);
strftime(creation_text, sizeof(creation_text), "%c", &creation_tm);
// Print file information to stdout...
printf("%s:\\n", filename);
printf(" Title: %s\\n", pdfioFileGetTitle(pdf));
printf(" Author: %s\\n", pdfioFileGetAuthor(pdf));
printf(" Created On: %s\\n", creation_text);
printf(" Number Pages: %u\\n", (unsigned)pdfioFileGetNumPages(pdf));
// Close the PDF file...
pdfioFileClose(pdf);
}
.fi
.SS Create PDF File With Text and Image
.PP
The following example function will create a PDF file, embed a base font and the named JPEG or PNG image file, and then creates a page with the image centered on the page with the text centered below:
.nf
#include <pdfio.h>
#include <pdfio\-content.h>
#include <string.h>
void
create_pdf_image_file(const char *pdfname, const char *imagename, const char *caption)
{
pdfio_file_t *pdf;
pdfio_obj_t *font;
pdfio_obj_t *image;
pdfio_dict_t *dict;
pdfio_stream_t *page;
double width, height;
double swidth, sheight;
double tx, ty;
// Create the PDF file...
pdf = pdfioFileCreate(pdfname, /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_cbdata*/NULL);
// Create a Courier base font for the caption
font = pdfioFileCreateFontObjFromBase(pdf, "Courier");
// Create an image object from the JPEG/PNG image file...
image = pdfioFileCreateImageObjFromFile(pdf, imagename, true);
// Create a page dictionary with the font and image...
dict = pdfioDictCreate(pdf);
pdfioPageDictAddFont(dict, "F1", font);
pdfioPageDictAddImage(dict, "IM1", image);
// Create the page and its content stream...
page = pdfioFileCreatePage(pdf, dict);
// Position and scale the image on the page...
width = pdfioImageGetWidth(image);
height = pdfioImageGetHeight(image);
// Default media_box is "universal" 595.28x792 points (8.27x11in or 210x279mm)
// Use margins of 36 points (0.5in or 12.7mm) with another 36 points for the
// caption underneath...
swidth = 595.28 \- 72.0;
sheight = swidth * height / width;
if (sheight > (792.0 \- 36.0 \- 72.0))
{
sheight = 792.0 \- 36.0 \- 72.0;
swidth = sheight * width / height;
}
tx = 0.5 * (595.28 \- swidth);
ty = 0.5 * (792 \- 36 \- sheight);
pdfioContentDrawImage(page, "IM1", tx, ty + 36.0, swidth, sheight);
// Draw the caption in black...
pdfioContentSetFillColorDeviceGray(page, 0.0);
// Compute the starting point for the text \- Courier is monospaced with a
// nominal width of 0.6 times the text height...
tx = 0.5 * (595.28 \- 18.0 * 0.6 * strlen(caption));
// Position and draw the caption underneath...
pdfioContentTextBegin(page);
pdfioContentSetTextFont(page, "F1", 18.0);
pdfioContentTextMoveTo(page, tx, ty);
pdfioContentTextShow(page, /*unicode*/false, caption);
pdfioContentTextEnd(page);
// Close the page stream and the PDF file...
pdfioStreamClose(page);
pdfioFileClose(pdf);
}
.fi
.SH ENUMERATIONS
.SS pdfio_cs_e
@ -1808,14 +1900,49 @@ bool pdfioContentTextMoveTo (
double ty
);
.fi
.SS pdfioContentTextNextLine
.SS pdfioContentTextNewLine
Move to the next line.
.PP
.nf
bool pdfioContentTextNextLine (
bool pdfioContentTextNewLine (
pdfio_stream_t *st
);
.fi
.SS pdfioContentTextNewLineShow
Move to the next line and show text.
.PP
.nf
bool pdfioContentTextNewLineShow (
pdfio_stream_t *st,
double ws,
double cs,
bool unicode,
const char *s
);
.fi
.PP
This function moves to the next line and then shows some text with optional
word and character spacing in a PDF content stream. The "unicode" argument
specifies that the current font maps to full Unicode. The "s" argument
specifies a UTF-8 encoded string.
.SS pdfioContentTextNewLineShowf
Show formatted text.
.PP
.nf
bool pdfioContentTextNewLineShowf (
pdfio_stream_t *st,
double ws,
double cs,
bool unicode,
const char *format,
...
);
.fi
.PP
This function moves to the next line and shows some formatted text with
optional word and character spacing in a PDF content stream. The "unicode"
argument specifies that the current font maps to full Unicode. The "format"
argument specifies a UTF-8 encoded \fBprintf\fR-style format string.
.SS pdfioContentTextShow
Show text.
.PP
@ -2140,7 +2267,7 @@ pdfio_file_t * pdfioFileCreate (
pdfio_rect_t *media_box,
pdfio_rect_t *crop_box,
pdfio_error_cb_t error_cb,
void *error_data
void *error_cbdata
);
.fi
.PP
@ -2154,7 +2281,7 @@ The "media_box" and "crop_box" arguments specify the default MediaBox and
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP
The "error_cb" and "error_data" arguments specify an error handler callback
The "error_cb" and "error_cbdata" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR the default error handler is used that
writes error messages to \fBstderr\fR.
.SS pdfioFileCreateArrayObj
@ -2290,6 +2417,18 @@ Note: Currently PNG support is limited to grayscale, RGB, or indexed files
without interlacing or alpha. Transparency (masking) based on color/index
.IP 5
is supported.
.SS pdfioFileCreateNumberObj
Create a new object in a PDF file containing a number.
.PP
.nf
pdfio_obj_t * pdfioFileCreateNumberObj (
pdfio_file_t *pdf,
double number
);
.fi
.PP
This function creates a new object with a number value in a PDF file.
You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateObj
Create a new object in a PDF file.
.PP
@ -2305,23 +2444,23 @@ Create a PDF file through an output callback.
.nf
pdfio_file_t * pdfioFileCreateOutput (
pdfio_output_cb_t output_cb,
void *output_ctx,
void *output_cbdata,
const char *version,
pdfio_rect_t *media_box,
pdfio_rect_t *crop_box,
pdfio_error_cb_t error_cb,
void *error_data
void *error_cbdata
);
.fi
.PP
This function creates a new PDF file that is streamed though an output
callback. The "output_cb" and "output_ctx" arguments specify the output
callback and its context pointer which is called whenever data needs to be
callback. The "output_cb" and "output_cbdata" arguments specify the output
callback and its data pointer which is called whenever data needs to be
written:
.PP
.nf
ssize_t
output_cb(void *output_ctx, const void *buffer, size_t bytes)
output_cb(void *output_cbdata, const void *buffer, size_t bytes)
{
// Write buffer to output and return the number of bytes written
}
@ -2335,7 +2474,7 @@ The "media_box" and "crop_box" arguments specify the default MediaBox and
CropBox for pages in the PDF file - if \fBNULL\fR then a default "Universal" size
of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP
The "error_cb" and "error_data" arguments specify an error handler callback
The "error_cb" and "error_cbdata" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR the default error handler is used that
writes error messages to \fBstderr\fR.
.PP
@ -2354,6 +2493,18 @@ pdfio_stream_t * pdfioFileCreatePage (
pdfio_dict_t *dict
);
.fi
.SS pdfioFileCreateStringObj
Create a new object in a PDF file containing a string.
.PP
.nf
pdfio_obj_t * pdfioFileCreateStringObj (
pdfio_file_t *pdf,
const char *string
);
.fi
.PP
This function creates a new object with a string value in a PDF file.
You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateTemporary
.PP
@ -2365,7 +2516,7 @@ pdfio_file_t * pdfioFileCreateTemporary (
pdfio_rect_t *media_box,
pdfio_rect_t *crop_box,
pdfio_error_cb_t error_cb,
void *error_data
void *error_cbdata
);
.fi
.SS pdfioFileFindObj
@ -2388,6 +2539,16 @@ const char * pdfioFileGetAuthor (
pdfio_file_t *pdf
);
.fi
.SS pdfioFileGetCatalog
Get the document catalog dictionary.
.PP
.nf
pdfio_dict_t * pdfioFileGetCatalog (
pdfio_file_t *pdf
);
.fi
.PP
.SS pdfioFileGetCreationDate
Get the creation date for a PDF file.
.PP
@ -2513,22 +2674,22 @@ Open a PDF file for reading.
pdfio_file_t * pdfioFileOpen (
const char *filename,
pdfio_password_cb_t password_cb,
void *password_data,
void *password_cbdata,
pdfio_error_cb_t error_cb,
void *error_data
void *error_cbdata
);
.fi
.PP
This function opens an existing PDF file. The "filename" argument specifies
the name of the PDF file to create.
.PP
The "password_cb" and "password_data" arguments specify a password callback
The "password_cb" and "password_cbdata" arguments specify a password callback
and its data pointer for PDF files that use one of the standard Adobe
"security" handlers. The callback returns a password string or \fBNULL\fR to
cancel the open. If \fBNULL\fR is specified for the callback function and the
PDF file requires a password, the open will always fail.
.PP
The "error_cb" and "error_data" arguments specify an error handler callback
The "error_cb" and "error_cbdata" arguments specify an error handler callback
and its data pointer - if \fBNULL\fR the default error handler is used that
writes error messages to \fBstderr\fR.
.SS pdfioFileSetAuthor
@ -3050,15 +3211,9 @@ 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
.SH COPYRIGHT
.PP
Copyright (c) 2021-2023 by Michael R Sweet
Copyright (c) 2021-2024 by Michael R Sweet

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@ goals of pdfio are:
PDFio is *not* concerned with rendering or viewing a PDF file, although a PDF
RIP or viewer could be written using it.
PDFio is Copyright © 2021-2023 by Michael R Sweet and is licensed under the
PDFio is Copyright © 2021-2024 by Michael R Sweet and is licensed under the
Apache License Version 2.0 with an (optional) exception to allow linking against
GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
@ -27,6 +27,7 @@ PDFio requires the following to build the software:
- A C99 compiler such as Clang, GCC, or MS Visual C
- A POSIX-compliant `make` program
- A POSIX-compliant `sh` program
- ZLIB (<https://www.zlib.net>) 1.0 or higher
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
@ -35,10 +36,11 @@ IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
Installing pdfio
----------------
PDFio comes with a portable makefile that will work on any POSIX-compliant
system with ZLIB installed. To make it, run:
PDFio comes with a configure script that creates a portable makefile that will
work on any POSIX-compliant system with ZLIB installed. To make it, run:
make all
./configure
make
To test it, run:
@ -46,45 +48,22 @@ To test it, run:
To install it, run:
make install
sudo make install
If you want a shared library, run:
make all-shared
make install-shared
./configure --enable-shared
make
sudo make install
The default installation location is "/usr/local". Pass the `prefix` variable
The default installation location is "/usr/local". Pass the `--prefix` option
to make to install it to another location:
make install prefix=/some/other/directory
./configure --prefix=/some/other/directory
The makefile installs the pdfio header to "${prefix}/include", the library to
"${prefix}/lib", the `pkg-config` file to "${prefix}/lib/pkgconfig", the man
page to "${prefix}/share/man/man3", and the documentation to
"${prefix}/share/doc/pdfio".
Other configure options can be found using the `--help` option:
The makefile supports the following variables that can be specified in the make
command or as environment variables:
- `AR`: the library archiver (default "ar")
- `ARFLAGS`: options for the library archiver (default "cr")
- `CC`: the C compiler (default "cc")
- `CFLAGS`: options for the C compiler (default "")
- `CODESIGN_IDENTITY`: the identity to use when code signing the shared library
on macOS (default "Developer ID")
- `COMMONFLAGS`: options for the C compiler and linker (typically architecture
and optimization options, default is "-Os -g")
- `CPPFLAGS`: options for the C preprocessor (default "")
- `DESTDIR` and `DSTROOT`: specifies a root directory when installing
(default is "", specify only one)
- `DSOFLAGS`: options for the C compiler when linking the shared library
(default "")
- `LDFLAGS`: options for the C compiler when linking the test programs
(default "")
- `LIBS`: library options when linking the test programs (default "-lz")
- `RANLIB`: program that generates a table-of-contents in a library
(default "ranlib")
- `prefix`: specifies the installation directory (default "/usr/local")
./configure --help
Visual Studio Project
@ -102,10 +81,6 @@ generates a static library that will be installed under "/usr/local" with:
sudo xcodebuild install
You can reproduce this with the makefile using:
sudo make macos install
Detecting PDFio
---------------
@ -163,15 +138,32 @@ Reading PDF Files
You open an existing PDF file using the [`pdfioFileOpen`](@@) function:
```c
pdfio_file_t *pdf = pdfioFileOpen("myinputfile.pdf", error_cb, error_data);
pdfio_file_t *pdf = pdfioFileOpen("myinputfile.pdf", password_cb, password_data,
error_cb, error_data);
```
where the three arguments to the function are the filename ("myinputfile.pdf"),
an optional error callback function (`error_cb`), and an optional pointer value
for the error callback function (`error_data`). The error callback is called
for both errors and warnings and accepts the `pdfio_file_t` pointer, a message
string, and the callback pointer value, for example:
where the five arguments to the function are the filename ("myinputfile.pdf"),
an optional password callback function (`password_cb`) and data pointer value
(`password_data`), and an optional error callback function (`error_cb`) and data
pointer value (`error_data`). The password callback is called for encrypted PDF
files that are not using the default password, for example:
```c
const char *
password_cb(void *data, const char *filename)
{
(void)data; // This callback doesn't use the data pointer
(void)filename; // This callback doesn't use the filename
// Return a password string for the file...
return ("Password42");
}
```
The error callback is called for both errors and warnings and accepts the
`pdfio_file_t` pointer, a message string, and the callback pointer value, for
example:
```c
bool
@ -353,7 +345,7 @@ to the stream:
The [PDF content helper functions](@) provide additional functions for writing
specific PDF page stream commands.
When you are done writing the stream, call [`pdfioStreamCLose`](@@) to close
When you are done writing the stream, call [`pdfioStreamClose`](@@) to close
both the stream and the object.
@ -361,7 +353,7 @@ PDF Content Helper Functions
----------------------------
PDFio includes many helper functions for embedding or writing specific kinds of
content to a PDF file. These functions can be roughly grouped into ???
content to a PDF file. These functions can be roughly grouped into five
categories:
- [Color Space Functions](@)
@ -455,7 +447,7 @@ pdfio_obj_t *arial = pdfioFileCreateFontObjFromFile(pdf, "NotoSansJP-Regular.otf
will embed the NotoSansJP Regular OpenType font with full support for Unicode.
Note: Not all fonts support Unicode.
> Note: Not all fonts support Unicode.
### Image Object Functions
@ -584,9 +576,143 @@ escaping, as needed:
- [`pdfioContentTextMoveLine`](@@) moves to the next line with an offset in a
text block
- [`pdfioContentTextMoveTo`](@@) moves within the current line in a text block
- [`pdfioContentTextNextLine`](@@) moves to the beginning of the next line in a
- [`pdfioContentTextNewLine`](@@) moves to the beginning of the next line in a
text block
- [`pdfioContentTextNewLineShow`](@@) moves to the beginning of the next line in a
text block and shows literal text with optional word and character spacing
- [`pdfioContentTextNewLineShowf`](@@) moves to the beginning of the next line in a
text block and shows formatted text with optional word and character spacing
- [`pdfioContentTextShow`](@@) draws a literal string in a text block
- [`pdfioContentTextShowf`](@@) draws a formatted string in a text block
- [`pdfioContentTextShowJustified`](@@) draws an array of literal strings with
offsets between them
Examples
========
Read PDF Metadata
-----------------
The following example function will open a PDF file and print the title, author,
creation date, and number of pages:
```c
#include <pdfio.h>
#include <time.h>
void
show_pdf_info(const char *filename)
{
pdfio_file_t *pdf;
time_t creation_date;
struct tm *creation_tm;
char creation_text[256];
// Open the PDF file with the default callbacks...
pdf = pdfioFileOpen(filename, /*password_cb*/NULL, /*password_cbdata*/NULL, /*error_cb*/NULL, /*error_cbdata*/NULL);
if (pdf == NULL)
return;
// Get the creation date and convert to a string...
creation_date = pdfioFileGetCreationDate(pdf);
creation_tm = localtime(&creation_date);
strftime(creation_text, sizeof(creation_text), "%c", &creation_tm);
// Print file information to stdout...
printf("%s:\n", filename);
printf(" Title: %s\n", pdfioFileGetTitle(pdf));
printf(" Author: %s\n", pdfioFileGetAuthor(pdf));
printf(" Created On: %s\n", creation_text);
printf(" Number Pages: %u\n", (unsigned)pdfioFileGetNumPages(pdf));
// Close the PDF file...
pdfioFileClose(pdf);
}
```
Create PDF File With Text and Image
-----------------------------------
The following example function will create a PDF file, embed a base font and the
named JPEG or PNG image file, and then creates a page with the image centered on
the page with the text centered below:
```c
#include <pdfio.h>
#include <pdfio-content.h>
#include <string.h>
void
create_pdf_image_file(const char *pdfname, const char *imagename, const char *caption)
{
pdfio_file_t *pdf;
pdfio_obj_t *font;
pdfio_obj_t *image;
pdfio_dict_t *dict;
pdfio_stream_t *page;
double width, height;
double swidth, sheight;
double tx, ty;
// Create the PDF file...
pdf = pdfioFileCreate(pdfname, /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_cbdata*/NULL);
// Create a Courier base font for the caption
font = pdfioFileCreateFontObjFromBase(pdf, "Courier");
// Create an image object from the JPEG/PNG image file...
image = pdfioFileCreateImageObjFromFile(pdf, imagename, true);
// Create a page dictionary with the font and image...
dict = pdfioDictCreate(pdf);
pdfioPageDictAddFont(dict, "F1", font);
pdfioPageDictAddImage(dict, "IM1", image);
// Create the page and its content stream...
page = pdfioFileCreatePage(pdf, dict);
// Position and scale the image on the page...
width = pdfioImageGetWidth(image);
height = pdfioImageGetHeight(image);
// Default media_box is "universal" 595.28x792 points (8.27x11in or 210x279mm)
// Use margins of 36 points (0.5in or 12.7mm) with another 36 points for the
// caption underneath...
swidth = 595.28 - 72.0;
sheight = swidth * height / width;
if (sheight > (792.0 - 36.0 - 72.0))
{
sheight = 792.0 - 36.0 - 72.0;
swidth = sheight * width / height;
}
tx = 0.5 * (595.28 - swidth);
ty = 0.5 * (792 - 36 - sheight);
pdfioContentDrawImage(page, "IM1", tx, ty + 36.0, swidth, sheight);
// Draw the caption in black...
pdfioContentSetFillColorDeviceGray(page, 0.0);
// Compute the starting point for the text - Courier is monospaced with a
// nominal width of 0.6 times the text height...
tx = 0.5 * (595.28 - 18.0 * 0.6 * strlen(caption));
// Position and draw the caption underneath...
pdfioContentTextBegin(page);
pdfioContentSetTextFont(page, "F1", 18.0);
pdfioContentTextMoveTo(page, tx, ty);
pdfioContentTextShow(page, /*unicode*/false, caption);
pdfioContentTextEnd(page);
// Close the page stream and the PDF file...
pdfioStreamClose(page);
pdfioFileClose(pdf);
}
```

View File

@ -2,18 +2,61 @@
#
# makesrcdist - make a source distribution of pdfio.
#
# Usage:
#
# ./makesrcdist [--snapshot] VERSION
#
# Support "--snapshot" option...
if test "$1" == "--snapshot"; then
shift
snapshot=1
else
snapshot=0
fi
# Get version...
if test $# != 1; then
echo "Usage: ./makesrcdist version"
echo "Usage: ./makesrcdist [--snapshot] VERSION"
exit 1
fi
version=$1
# Check that version number has been updated everywhere...
if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then
echo "Still need to update AC_INIT version in 'configure.ac'."
exit 1
fi
if test $(grep PDFIO_VERSION= configure | awk -F \" '{print $2}') != "$version"; then
echo "Still need to run 'autoconf -f'."
exit 1
fi
if test $(grep '<version>' pdfio_native.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then
echo "Still need to update version in 'pdfio_native.nuspec'."
exit 1
fi
if test $(grep '<version>' pdfio_native.redist.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then
echo "Still need to update version in 'pdfio_native.redist.nuspec'."
exit 1
fi
if test $(grep PDFIO_VERSION pdfio.h | awk -F \" '{print $2}') != "$version"; then
echo "Still need to update PDFIO_VERSION in 'pdfio.h'."
exit 1
fi
# Tag release...
if test $snapshot = 0; then
echo Creating tag for release...
git tag -m "Tag $version" v$version
git push origin v$version
fi
# Make source archives...
echo Creating pdfio-$version.tar.gz...
git archive --format tar --prefix=pdfio-$version/ HEAD | gzip -v9 >pdfio-$version.tar.gz
gpg --detach-sign pdfio-$version.tar.gz

View File

@ -10,10 +10,6 @@
// (<https://github.com/kokke/tiny-AES-c>)
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -21,7 +17,7 @@
// Local types...
//
typedef uint8_t state_t[4][4]; // 4x4 AES state table
typedef uint8_t state_t[4][4]; // 4x4 AES state table @private@
//

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -331,6 +327,30 @@ pdfioArrayCreate(pdfio_file_t *pdf) // I - PDF file
}
//
// '_pdfioArrayDecrypt()' - Decrypt values in an array.
//
bool // O - `true` on success, `false` on error
_pdfioArrayDecrypt(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object
pdfio_array_t *a, // I - Array
size_t depth) // I - Depth
{
size_t i; // Looping var
_pdfio_value_t *v; // Current value
for (i = a->num_values, v = a->values; i > 0; i --, v ++)
{
if (!_pdfioValueDecrypt(pdf, obj, v, depth))
return (false);
}
return (true);
}
//
// '_pdfioArrayDebug()' - Print the contents of an array.
//

View File

@ -1,16 +1,12 @@
//
// Common support functions for pdfio.
//
// Copyright © 2021-2023 by Michael R Sweet.
// Copyright © 2021-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -145,7 +141,7 @@ _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
*bufend = buffer + bufsize - 1; // Pointer to end of buffer
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend);
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer)));
while (!eol)
{
@ -265,7 +261,7 @@ _pdfioFilePrintf(pdfio_file_t *pdf, // I - PDF file
// Format the string...
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
_pdfio_vsnprintf(pdf, buffer, sizeof(buffer), format, ap);
va_end(ap);
// Write it...
@ -360,12 +356,12 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
off_t offset, // I - Offset
int whence) // I - Offset base
{
PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d)\n", pdf, (long)offset, whence);
PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d) pdf->bufpos=%lu\n", pdf, (long)offset, whence, (unsigned long)(pdf ? pdf->bufpos : 0));
// Adjust offset for relative seeks...
if (whence == SEEK_CUR)
{
offset += pdf->bufpos;
offset += pdf->bufpos + (pdf->bufptr - pdf->buffer);
whence = SEEK_SET;
}
@ -408,7 +404,7 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
return (-1);
}
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld.\n", (long)pdf->bufpos);
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld, offset=%lu.\n", (long)pdf->bufpos, (unsigned long)offset);
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend);
pdf->bufpos = offset;

View File

@ -1131,20 +1131,20 @@ pdfioContentTextMeasure(
if (ch < 128)
{
// ASCII
*tempptr++ = ch;
*tempptr++ = (char)ch;
}
else if (ch < 2048)
{
// 2-byte UTF-8
*tempptr++ = 0xc0 | ((ch >> 6) & 0x1f);
*tempptr++ = 0x80 | (ch & 0x3f);
*tempptr++ = (char)(0xc0 | ((ch >> 6) & 0x1f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 3-byte UTF-8
*tempptr++ = 0xe0 | ((ch >> 12) & 0x0f);
*tempptr++ = 0x80 | ((ch >> 6) & 0x3f);
*tempptr++ = 0x80 | (ch & 0x3f);
*tempptr++ = (char)(0xe0 | ((ch >> 12) & 0x0f));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
}
@ -1152,7 +1152,7 @@ pdfioContentTextMeasure(
s = temp;
}
ttfGetExtents(ttf, size, s, &extents);
ttfGetExtents(ttf, (float)size, s, &extents);
return (extents.right - extents.left);
}
@ -1187,7 +1187,21 @@ pdfioContentTextMoveTo(
//
// 'pdfioContentTextNextLine()' - Move to the next line.
// 'pdfioContentTextNewLine()' - Move to the next line.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNewLine(
pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "T*\n"));
}
//
// 'pdfioContentTextNextLine()' - Legacy function name preserved for binary compatibility.
//
// @private@
//
bool // O - `true` on success, `false` on failure
@ -1198,6 +1212,86 @@ pdfioContentTextNextLine(
}
//
// 'pdfioContentTextNewLineShow()' - Move to the next line and show text.
//
// This function moves to the next line and then shows some text with optional
// word and character spacing in a PDF content stream. The "unicode" argument
// specifies that the current font maps to full Unicode. The "s" argument
// specifies a UTF-8 encoded string.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNewLineShow(
pdfio_stream_t *st, // I - Stream
double ws, // I - Word spacing or `0.0` for none
double cs, // I - Character spacing or `0.0` for none
bool unicode, // I - Unicode text?
const char *s) // I - String to show
{
bool newline = false; // New line?
char op; // Text operator
// Write word and/or character spacing as needed...
if (ws > 0.0 || cs > 0.0)
{
// Use " operator to show text with word and character spacing...
if (!pdfioStreamPrintf(st, "%g %g", ws, cs))
return (false);
op = '\"';
}
else
{
// Use ' operator to show text with the defaults...
op = '\'';
}
// Write the string...
if (!write_string(st, unicode, s, &newline))
return (false);
// Draw it...
if (newline)
return (pdfioStreamPrintf(st, "%c T*\n", op));
else
return (pdfioStreamPrintf(st, "%c\n", op));
}
//
// 'pdfioContentTextNewLineShowf()' - Show formatted text.
//
// This function moves to the next line and shows some formatted text with
// optional word and character spacing in a PDF content stream. The "unicode"
// argument specifies that the current font maps to full Unicode. The "format"
// argument specifies a UTF-8 encoded `printf`-style format string.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNewLineShowf(
pdfio_stream_t *st, // I - Stream
double ws, // I - Word spacing or `0.0` for none
double cs, // I - Character spacing or `0.0` for none
bool unicode, // I - Unicode text?
const char *format, // I - `printf`-style format string
...) // I - Additional arguments as needed
{
char buffer[8192]; // Text buffer
va_list ap; // Argument pointer
// Format the string...
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
// Show it...
return (pdfioContentTextNewLineShow(st, ws, cs, unicode, buffer));
}
//
// 'pdfioContentTextShow()' - Show text.
//
@ -1230,9 +1324,9 @@ pdfioContentTextShow(
//
// 'pdfioContentTextShowf()' - Show formatted text.
//
// This function shows some text in a PDF content stream. The "unicode" argument
// specifies that the current font maps to full Unicode. The "format" argument
// specifies a UTF-8 encoded `printf`-style format string.
// This function shows some formatted text in a PDF content stream. The
// "unicode" argument specifies that the current font maps to full Unicode.
// The "format" argument specifies a UTF-8 encoded `printf`-style format string.
//
bool
@ -1548,7 +1642,7 @@ pdfioFileCreateFontObjFromFile(
*bufptr++ = (unsigned char)(cmap[i] >> 8);
*bufptr++ = (unsigned char)(cmap[i] & 255);
glyphs[cmap[i]] = i;
glyphs[cmap[i]] = (unsigned short)i;
if (cmap[i] < min_glyph)
min_glyph = cmap[i];
if (cmap[i] > max_glyph)
@ -1633,9 +1727,9 @@ pdfioFileCreateFontObjFromFile(
if ((w_array = pdfioArrayCreate(pdf)) == NULL)
goto done;
for (start = 0, w0 = ttfGetWidth(font, 0), i = 1; i < 65536; start = i, w0 = w1, i ++)
for (start = 0, w0 = ttfGetWidth(font, 0), w1 = 0, i = 1; i < 65536; start = i, w0 = w1, i ++)
{
while (i < 65536 && (w1 = ttfGetWidth(font, i)) == w0)
while (i < 65536 && (w1 = ttfGetWidth(font, (int)i)) == w0)
i ++;
if ((i - start) > 1)
@ -1656,7 +1750,7 @@ pdfioFileCreateFontObjFromFile(
pdfioArrayAppendNumber(temp_array, w0);
for (w0 = w1, i ++; i < 65536; w0 = w1, i ++)
{
if ((w1 = ttfGetWidth(font, i)) == w0 && i < 65535)
if ((w1 = ttfGetWidth(font, (int)i)) == w0 && i < 65535)
break;
pdfioArrayAppendNumber(temp_array, w0);
@ -2255,7 +2349,7 @@ pdfioPageDictAddColorSpace(
bool // O - `true` on success, `false` on failure
pdfioPageDictAddFont(
pdfio_dict_t *dict, // I - Page dictionary
const char *name, // I - Font name
const char *name, // I - Font name; must not contain spaces
pdfio_obj_t *obj) // I - Font object
{
pdfio_dict_t *resources; // Resource dictionary

View File

@ -118,6 +118,9 @@ extern bool pdfioContentTextEnd(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern double pdfioContentTextMeasure(pdfio_obj_t *font, const char *s, double size) _PDFIO_PUBLIC;
extern bool pdfioContentTextMoveLine(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
extern bool pdfioContentTextMoveTo(pdfio_stream_t *st, double tx, double ty) _PDFIO_PUBLIC;
extern bool pdfioContentTextNewLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextNewLineShow(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *s) _PDFIO_PUBLIC;
extern bool pdfioContentTextNewLineShowf(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(5,6);
extern bool pdfioContentTextNextLine(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioContentTextShow(pdfio_stream_t *st, bool unicode, const char *s) _PDFIO_PUBLIC;
extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4);

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
#if _WIN32
# include <windows.h>
@ -410,9 +406,16 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
uint8_t *iv, // I - Buffer for initialization vector
size_t *ivlen) // IO - Size of initialization vector
{
uint8_t data[21]; /* Key data */
_pdfio_md5_t md5; /* MD5 state */
uint8_t digest[16]; /* MD5 digest value */
uint8_t data[21]; // Key data
_pdfio_md5_t md5; // MD5 state
uint8_t digest[16]; // MD5 digest value
#if PDFIO_OBJ_CRYPT
pdfio_array_t *id_array; // Object ID array
unsigned char *id_value; // Object ID value
size_t id_len; // Length of object ID
uint8_t temp_key[16]; // File key for object
#endif // PDFIO_OBJ_CRYPT
uint8_t *file_key; // Computed file key to use
PDFIO_DEBUG("_pdfioCryptoMakeReader(pdf=%p, obj=%p(%d), ctx=%p, iv=%p, ivlen=%p(%d))\n", pdf, obj, (int)obj->number, ctx, iv, ivlen, (int)*ivlen);
@ -424,6 +427,59 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
return (NULL);
}
#if PDFIO_OBJ_CRYPT
if ((id_array = pdfioDictGetArray(pdfioObjGetDict(obj), "ID")) != NULL)
{
// Object has its own ID that will get used for encryption...
_pdfio_md5_t md5; // MD5 context
uint8_t file_digest[16]; // MD5 digest of file ID and pad
uint8_t user_pad[32], // Padded user password
own_user_key[32], // Calculated user key
pdf_user_key[32]; // Decrypted user key
PDFIO_DEBUG("_pdfioCryptoMakeReader: Per-object file ID.\n");
if ((id_value = pdfioArrayGetBinary(id_array, 0, &id_len)) == NULL)
{
*ivlen = 0;
return (NULL);
}
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, pdf_passpad, 32);
_pdfioCryptoMD5Append(&md5, id_value, id_len);
_pdfioCryptoMD5Finish(&md5, file_digest);
make_owner_key(pdf->encryption, pdf->password, pdf->owner_key, user_pad);
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, user_pad, pdf->owner_key, temp_key);
make_user_key(id_value, id_len, own_user_key);
if (memcmp(own_user_key, pdf->user_key, sizeof(own_user_key)))
{
PDFIO_DEBUG("_pdfioCryptoMakeReader: Not user password, trying owner password.\n");
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, pdf->password, pdf->owner_key, temp_key);
make_user_key(id_value, id_len, own_user_key);
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
decrypt_user_key(pdf->encryption, temp_key, pdf_user_key);
if (memcmp(pdf->password, pdf_user_key, 32) && memcmp(own_user_key, pdf_user_key, 16))
{
*ivlen = 0;
return (NULL);
}
}
file_key = temp_key;
}
else
#endif // PDFIO_OBJ_CRYPT
{
// Use the default file key...
file_key = pdf->file_key;
}
switch (pdf->encryption)
{
default :
@ -432,7 +488,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
case PDFIO_ENCRYPTION_RC4_40 :
// Copy the key data for the MD5 hash.
memcpy(data, pdf->file_key, sizeof(pdf->file_key));
memcpy(data, file_key, 16);
data[16] = (uint8_t)obj->number;
data[17] = (uint8_t)(obj->number >> 8);
data[18] = (uint8_t)(obj->number >> 16);
@ -459,7 +515,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
case PDFIO_ENCRYPTION_RC4_128 :
// Copy the key data for the MD5 hash.
memcpy(data, pdf->file_key, sizeof(pdf->file_key));
memcpy(data, file_key, 16);
data[16] = (uint8_t)obj->number;
data[17] = (uint8_t)(obj->number >> 8);
data[18] = (uint8_t)(obj->number >> 16);
@ -600,6 +656,8 @@ _pdfioCryptoUnlock(
revision = (int)pdfioDictGetNumber(encrypt_dict, "R");
length = (int)pdfioDictGetNumber(encrypt_dict, "Length");
PDFIO_DEBUG("_pdfioCryptoUnlock: handler=%p(%s), version=%d, revision=%d, length=%d\n", (void *)handler, handler ? handler : "(null)", version, revision, length);
if (!handler || strcmp(handler, "Standard"))
{
_pdfioFileError(pdf, "Unsupported security handler '%s'.", handler ? handler : "(null)");
@ -648,6 +706,8 @@ _pdfioCryptoUnlock(
}
else
{
PDFIO_DEBUG("_pdfioCryptoUnlock: CFM=\"%s\"\n", cfm);
if (length < 40 || length > 128)
length = 128; // Default to 128 bits
@ -773,17 +833,16 @@ _pdfioCryptoUnlock(
{
// Matches!
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
memcpy(pdf->password, pad, sizeof(pdf->password));
return (true);
}
/*
* Not the owner password, try the user password...
*/
// Not the owner password, try the user password...
make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, pad, pdf->owner_key, file_key);
PDFIO_DEBUG("_pdfioCryptoUnlock: Fuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]);
make_user_key(file_id, file_idlen, user_key);
make_user_key(file_id, file_idlen, own_user_key);
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
decrypt_user_key(pdf->encryption, file_key, pdf_user_key);
@ -791,10 +850,12 @@ _pdfioCryptoUnlock(
PDFIO_DEBUG("_pdfioCryptoUnlock: Uuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", user_key[0], user_key[1], user_key[2], user_key[3], user_key[28], user_key[29], user_key[30], user_key[31]);
PDFIO_DEBUG("_pdfioCryptoUnlock: Updf=%02X%02X%02X%02X...%02X%02X%02X%02X\n", pdf_user_key[0], pdf_user_key[1], pdf_user_key[2], pdf_user_key[3], pdf_user_key[28], pdf_user_key[29], pdf_user_key[30], pdf_user_key[31]);
if (!memcmp(pad, pdf_user_key, 32) || !memcmp(user_key, pdf_user_key, 16))
if (!memcmp(pad, pdf_user_key, 32) || !memcmp(own_user_key, pdf_user_key, 16))
{
// Matches!
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
memcpy(pdf->password, pad, sizeof(pdf->password));
return (true);
}
}

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -162,6 +158,30 @@ pdfioDictCreate(pdfio_file_t *pdf) // I - PDF file
}
//
// '_pdfioDictDecrypt()' - Decrypt the values in a dictionary.
//
bool // O - `true` on success, `false` on error
_pdfioDictDecrypt(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object
pdfio_dict_t *dict, // I - Dictionary
size_t depth) // I - Depth
{
size_t i; // Looping var
_pdfio_pair_t *pair; // Current pair
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{
if (strcmp(pair->key, "ID") && !_pdfioValueDecrypt(pdf, obj, &pair->value, depth + 1))
return (false);
}
return (true);
}
//
// '_pdfioDictDebug()' - Dump a dictionary to stderr.
//
@ -406,10 +426,28 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
if (value && value->type == PDFIO_VALTYPE_STRING)
{
return (value->value.string);
}
else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
{
// Convert binary string to regular string...
char temp[4096]; // Temporary string
memcpy(temp, value->value.binary.data, value->value.binary.datalen);
temp[value->value.binary.datalen] = '\0';
free(value->value.binary.data);
value->type = PDFIO_VALTYPE_STRING;
value->value.string = pdfioStringCreate(dict->pdf, temp);
return (value->value.string);
}
else
{
return (NULL);
}
}
//
@ -522,7 +560,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
_pdfio_value_t value; // Dictionary value
PDFIO_DEBUG("_pdfioDictRead(pdf=%p)\n", pdf);
PDFIO_DEBUG("_pdfioDictRead(pdf=%p, obj=%p, tb=%p, depth=%lu)\n", pdf, obj, tb, (unsigned long)depth);
// Create a dictionary and start reading...
if ((dict = pdfioDictCreate(pdf)) == NULL)
@ -534,6 +572,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
if (!strcmp(key, ">>"))
{
// End of dictionary...
PDFIO_DEBUG("_pdfioDictRead: Returning dictionary value...\n");
return (dict);
}
else if (key[0] != '/')
@ -552,14 +591,14 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
if (!_pdfioValueRead(pdf, obj, tb, &value, depth))
{
_pdfioFileError(pdf, "Missing value for dictionary key.");
_pdfioFileError(pdf, "Missing value for dictionary key '%s'.", key + 1);
break;
}
if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
break;
// PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key);
PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key);
}
// Dictionary is invalid - pdfioFileClose will free the memory, return NULL

File diff suppressed because it is too large Load Diff

View File

@ -453,9 +453,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
return (false);
}
PDFIO_DEBUG("_pdfioObjLoad: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[0]=0x%02x\n", tb.bufptr, tb.bufend, tb.bufptr[0], tb.bufptr[1]);
if (tb.bufptr && tb.bufptr < tb.bufend && (tb.bufptr[0] == 0x0d || tb.bufptr[0] == 0x0a))
tb.bufptr ++; // Skip trailing CR or LF after token
PDFIO_DEBUG("_pdfioObjLoad: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[1]=0x%02x\n", tb.bufptr, tb.bufend, tb.bufptr[0], tb.bufptr[1]);
_pdfioTokenFlush(&tb);
@ -466,6 +464,18 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
PDFIO_DEBUG("_pdfioObjLoad: stream_offset=%lu.\n", (unsigned long)obj->stream_offset);
}
// Decrypt as needed...
if (obj->pdf->encryption)
{
PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n");
if (!_pdfioValueDecrypt(obj->pdf, obj, &obj->value, 0))
{
PDFIO_DEBUG("_pdfioObjLoad: Failed to decrypt.\n");
return (false);
}
}
PDFIO_DEBUG("_pdfioObjLoad: ");
PDFIO_DEBUG_VALUE(&obj->value);
PDFIO_DEBUG("\n");

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"

View File

@ -1,7 +1,7 @@
//
// Private header file for PDFio.
//
// Copyright © 2021-2023 by Michael R Sweet.
// Copyright © 2021-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -20,6 +20,7 @@
# include <errno.h>
# include <inttypes.h>
# include <fcntl.h>
# include <locale.h>
# ifdef _WIN32
# include <io.h>
# include <direct.h>
@ -37,9 +38,11 @@
# define unlink _unlink
# define vsnprintf _vsnprintf
# define write _write
# ifndef F_OK
# define F_OK 00 // POSIX parameters/flags
# define W_OK 02
# define R_OK 04
# endif // !F_OK
# define O_RDONLY _O_RDONLY // Map standard POSIX open flags
# define O_WRONLY _O_WRONLY
# define O_CREAT _O_CREAT
@ -224,6 +227,7 @@ typedef struct _pdfio_objmap_s // PDF object map
struct _pdfio_file_s // PDF file structure
{
char *filename; // Filename
struct lconv *loc; // Locale data
char *version; // Version number
pdfio_rect_t media_box, // Default MediaBox value
crop_box; // Default CropBox value
@ -237,7 +241,8 @@ struct _pdfio_file_s // PDF file structure
pdfio_permission_t permissions; // Access permissions (encrypted PDF files)
uint8_t file_key[16], // File encryption key
owner_key[32], // Owner encryption key
user_key[32]; // User encryption key
user_key[32], // User encryption key
password[32]; // Padded password
size_t file_keylen, // Length of file encryption key
owner_keylen, // Length of owner encryption key
user_keylen; // Length of user encryption key
@ -265,7 +270,8 @@ struct _pdfio_file_s // PDF file structure
alloc_dicts; // Allocated dictionaries
pdfio_dict_t **dicts; // Dictionaries
size_t num_objs, // Number of objects
alloc_objs; // Allocated objects
alloc_objs, // Allocated objects
last_obj; // Last object added
pdfio_obj_t **objs, // Objects
*current_obj; // Current object being written/read
size_t num_objmaps, // Number of object maps
@ -320,6 +326,10 @@ struct _pdfio_stream_s // Stream
// Functions...
//
extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern ssize_t _pdfio_vsnprintf(pdfio_file_t *pdf, char *buffer, size_t bufsize, const char *format, va_list ap) _PDFIO_INTERNAL;
extern bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
@ -344,6 +354,7 @@ extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Dig
extern bool _pdfioCryptoUnlock(pdfio_file_t *pdf, pdfio_password_cb_t password_cb, void *password_data) _PDFIO_INTERNAL;
extern void _pdfioDictClear(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
extern bool _pdfioDictDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_dict_t *dict, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
@ -387,6 +398,7 @@ extern void _pdfioTokenPush(_pdfio_token_t *tb, const char *token) _PDFIO_INTER
extern bool _pdfioTokenRead(_pdfio_token_t *tb, char *buffer, size_t bufsize);
extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL;
extern bool _pdfioValueDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;

View File

@ -1,16 +1,12 @@
//
// PDF stream functions for PDFio.
//
// Copyright © 2021-2023 by Michael R Sweet.
// Copyright © 2021-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -691,7 +687,7 @@ pdfioStreamPrintf(
// Format the string...
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
_pdfio_vsnprintf(st->pdf, buffer, sizeof(buffer), format, ap);
va_end(ap);
// Write the string...
@ -1070,7 +1066,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out)
@ -1131,7 +1127,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
@ -1201,7 +1197,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))

View File

@ -1,16 +1,12 @@
//
// PDF dictionary functions for PDFio.
// PDF string functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -18,7 +14,360 @@
// Local functions...
//
static int compare_strings(char **a, char **b);
static size_t find_string(pdfio_file_t *pdf, const char *s, int *rdiff);
//
// '_pdfio_strtod()' - Convert a string to a double value.
//
// This function wraps strtod() to avoid locale issues.
//
double // O - Double value
_pdfio_strtod(pdfio_file_t *pdf, // I - PDF file
const char *s) // I - String
{
char temp[64], // Temporary buffer
*tempptr; // Pointer into temporary buffer
// See if the locale has a special decimal point string...
if (!pdf->loc)
return (strtod(s, NULL));
// Copy leading sign, numbers, period, and then numbers...
tempptr = temp;
temp[sizeof(temp) - 1] = '\0';
while (*s && *s != '.')
{
if (tempptr < (temp + sizeof(temp) - 1))
*tempptr++ = *s++;
else
return (0.0);
}
if (*s == '.')
{
// Convert decimal point to locale equivalent...
size_t declen = strlen(pdf->loc->decimal_point);
// Length of decimal point
s ++;
if (declen <= (sizeof(temp) - (size_t)(tempptr - temp)))
{
memcpy(tempptr, pdf->loc->decimal_point, declen);
tempptr += declen;
}
else
{
return (0.0);
}
}
// Copy any remaining characters...
while (*s)
{
if (tempptr < (temp + sizeof(temp) - 1))
*tempptr++ = *s++;
else
return (0.0);
}
// Nul-terminate the temporary string and convert the string...
*tempptr = '\0';
return (strtod(temp, NULL));
}
//
// '_pdfio_vsnprintf()' - Format a string.
//
// This function emulates vsnprintf() to avoid locale issues.
//
ssize_t // O - Number of bytes
_pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
char *buffer, // I - Output buffer
size_t bufsize, // I - Size of output buffer
const char *format, // I - printf-style format string
va_list ap) // I - Pointer to additional arguments
{
char *bufptr, // Pointer to position in buffer
*bufend, // Pointer to end of buffer
size, // Size character (h, l, L)
type; // Format type character
int width, // Width of field
prec; // Number of characters of precision
char tformat[100], // Temporary format string for snprintf()
*tptr, // Pointer into temporary format
temp[1024], // Buffer for formatted numbers
*tempptr; // Pointer into buffer
char *s; // Pointer to string
ssize_t bytes; // Total number of bytes needed
const char *dec = pdf->loc ? pdf->loc->decimal_point : ".";
// Decimal point string
char *decptr; // Pointer to decimal point
// Loop through the format string, formatting as needed...
bufptr = buffer;
bufend = buffer + bufsize - 1;
*bufend = '\0';
bytes = 0;
while (*format)
{
if (*format == '%')
{
// Format character...
tptr = tformat;
*tptr++ = *format++;
if (*format == '%')
{
if (bufptr < bufend)
*bufptr++ = *format;
bytes ++;
format ++;
continue;
}
else if (strchr(" -+#\'", *format))
{
*tptr++ = *format++;
}
if (*format == '*')
{
// Get width from argument...
format ++;
width = va_arg(ap, int);
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", width);
tptr += strlen(tptr);
}
else
{
width = 0;
while (isdigit(*format & 255))
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
width = width * 10 + *format++ - '0';
}
}
if (*format == '.')
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
format ++;
if (*format == '*')
{
// Get precision from argument...
format ++;
prec = va_arg(ap, int);
snprintf(tptr, sizeof(tformat) - (size_t)(tptr - tformat), "%d", prec);
tptr += strlen(tptr);
}
else
{
prec = 0;
while (isdigit(*format & 255))
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
prec = prec * 10 + *format++ - '0';
}
}
}
if (*format == 'l' && format[1] == 'l')
{
size = 'L';
if (tptr < (tformat + sizeof(tformat) - 2))
{
*tptr++ = 'l';
*tptr++ = 'l';
}
format += 2;
}
else if (*format == 'h' || *format == 'l' || *format == 'L')
{
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
size = *format++;
}
else
{
size = 0;
}
if (!*format)
break;
if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format;
type = *format++;
*tptr = '\0';
switch (type)
{
case 'E' : // Floating point formats
case 'G' :
case 'e' :
case 'f' :
case 'g' :
if ((size_t)(width + 2) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, va_arg(ap, double));
if ((decptr = strstr(temp, dec)) != NULL)
{
// Convert locale decimal point to "."
PDFIO_DEBUG("_pdfio_vsnprintf: Before \"%s\"\n", temp);
tempptr = decptr + strlen(dec);
if (tempptr > (decptr + 1))
memmove(decptr + 1, tempptr, strlen(tempptr) + 1);
*decptr = '.';
// Strip trailing 0's...
for (tempptr = temp + strlen(temp) - 1; tempptr > temp && *tempptr == '0'; tempptr --)
*tempptr = '\0';
if (*tempptr == '.')
*tempptr = '\0'; // Strip trailing decimal point
PDFIO_DEBUG("_pdfio_vsnprintf: After \"%s\"\n", temp);
}
// Copy to the output buffer
bytes += (int)strlen(temp);
if (bufptr < bufend)
{
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
bufptr += strlen(bufptr);
}
break;
case 'B' : // Integer formats
case 'X' :
case 'b' :
case 'd' :
case 'i' :
case 'o' :
case 'u' :
case 'x' :
if ((size_t)(width + 2) > sizeof(temp))
break;
# ifdef HAVE_LONG_LONG
if (size == 'L')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
else
# endif // HAVE_LONG_LONG
if (size == 'l')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
else
snprintf(temp, sizeof(temp), tformat, va_arg(ap, int));
bytes += (int)strlen(temp);
if (bufptr < bufend)
{
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
bufptr += strlen(bufptr);
}
break;
case 'p' : // Pointer value
if ((size_t)(width + 2) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, va_arg(ap, void *));
bytes += (int)strlen(temp);
if (bufptr < bufend)
{
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1));
bufptr += strlen(bufptr);
}
break;
case 'c' : // Character or character array
bytes += width;
if (bufptr < bufend)
{
if (width <= 1)
{
*bufptr++ = (char)va_arg(ap, int);
}
else
{
if ((bufptr + width) > bufend)
width = (int)(bufend - bufptr);
memcpy(bufptr, va_arg(ap, char *), (size_t)width);
bufptr += width;
}
}
break;
case 's' : // String
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
bytes += strlen(s);
if (bufptr < bufend)
{
strncpy(bufptr, s, (size_t)(bufend - bufptr - 1));
bufptr += strlen(bufptr);
}
break;
case 'n' : // Output number of chars so far
*(va_arg(ap, int *)) = (int)bytes;
break;
}
}
else
{
// Literal character...
bytes ++;
if (bufptr < bufend)
*bufptr++ = *format++;
}
}
// Nul-terminate the string and return the number of characters needed.
if (bufptr < bufend)
{
// Everything fit in the buffer...
*bufptr = '\0';
}
PDFIO_DEBUG("_pdfio_vsnprintf: Returning %ld \"%s\"\n", (long)bytes, buffer);
return (bytes);
}
//
@ -37,7 +386,8 @@ pdfioStringCreate(
const char *s) // I - Nul-terminated string
{
char *news; // New string
char **match; // Matching string
size_t idx; // Index into strings
int diff; // Different
PDFIO_DEBUG("pdfioStringCreate(pdf=%p, s=\"%s\")\n", pdf, s);
@ -47,8 +397,17 @@ pdfioStringCreate(
return (NULL);
// See if the string has already been added...
if (pdf->num_strings > 0 && (match = (char **)bsearch(&s, pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings)) != NULL)
return (*match);
if (pdf->num_strings > 0)
{
idx = find_string(pdf, s, &diff);
if (diff == 0)
return (pdf->strings[idx]);
}
else
{
idx = 0;
diff = -1;
}
// Not already added, so add it...
if ((news = strdup(s)) == NULL)
@ -69,11 +428,17 @@ pdfioStringCreate(
pdf->alloc_strings += 128;
}
// TODO: Change to insertion sort as needed...
pdf->strings[pdf->num_strings ++] = news;
// Insert the string...
if (diff > 0)
idx ++;
if (pdf->num_strings > 1)
qsort(pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings);
PDFIO_DEBUG("pdfioStringCreate: Inserting \"%s\" at %u\n", news, (unsigned)idx);
if (idx < pdf->num_strings)
memmove(pdf->strings + idx + 1, pdf->strings + idx, (pdf->num_strings - idx) * sizeof(char *));
pdf->strings[idx] = news;
pdf->num_strings ++;
PDFIO_DEBUG("pdfioStringCreate: %lu strings\n", (unsigned long)pdf->num_strings);
@ -124,17 +489,67 @@ _pdfioStringIsAllocated(
pdfio_file_t *pdf, // I - PDF file
const char *s) // I - String
{
return (pdf->num_strings > 0 && bsearch(&s, pdf->strings, pdf->num_strings, sizeof(char *), (int (*)(const void *, const void *))compare_strings) != NULL);
int diff; // Difference
if (pdf->num_strings == 0)
return (false);
find_string(pdf, s, &diff);
return (diff == 0);
}
//
// 'compare_strings()' - Compare two strings.
// 'find_string()' - Find an element in the array.
//
static int // O - Result of comparison
compare_strings(char **a, // I - First string
char **b) // I - Second string
static size_t // O - Index of match
find_string(pdfio_file_t *pdf, // I - PDF file
const char *s, // I - String to find
int *rdiff) // O - Difference of match
{
return (strcmp(*a, *b));
size_t left, // Left side of search
right, // Right side of search
current; // Current element
int diff; // Comparison with current element
// Do a binary search for the string...
left = 0;
right = pdf->num_strings - 1;
do
{
current = (left + right) / 2;
diff = strcmp(s, pdf->strings[current]);
if (diff == 0)
break;
else if (diff < 0)
right = current;
else
left = current;
}
while ((right - left) > 1);
if (diff != 0)
{
// Check the last 1 or 2 elements...
if ((diff = strcmp(s, pdf->strings[left])) <= 0)
{
current = left;
}
else
{
diff = strcmp(s, pdf->strings[right]);
current = right;
}
}
// Return the closest string and the difference...
*rdiff = diff;
return (current);
}

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -212,9 +208,10 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
*bufend, // End of buffer
state = '\0'; // Current state
bool saw_nul = false; // Did we see a nul character?
size_t count = 0; // Number of whitespace/comment bytes
//
// "state" is:
//
// - '\0' for idle
@ -233,21 +230,45 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
// Skip leading whitespace...
while ((ch = get_char(tb)) != EOF)
{
count ++;
if (ch == '%')
{
// Skip comment
PDFIO_DEBUG("_pdfioTokenRead: Skipping comment...\n");
while ((ch = get_char(tb)) != EOF)
{
count ++;
if (ch == '\n' || ch == '\r')
{
break;
}
else if (count > 2048)
{
_pdfioFileError(tb->pdf, "Comment too long.");
*bufptr = '\0';
return (false);
}
}
}
else if (!isspace(ch))
{
break;
}
else if (count > 2048)
{
_pdfioFileError(tb->pdf, "Too much whitespace.");
*bufptr = '\0';
return (false);
}
}
if (ch == EOF)
{
*bufptr = '\0';
return (false);
}
// Check for delimiters...
if (strchr(PDFIO_DELIM_CHARS, ch) != NULL)
@ -267,6 +288,8 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
*bufptr++ = (char)ch;
}
PDFIO_DEBUG("_pdfioTokenRead: state='%c'\n", state);
switch (state)
{
case '(' : // Literal string
@ -358,6 +381,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Out of space
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
@ -365,6 +389,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if (ch != ')')
{
_pdfioFileError(tb->pdf, "Unterminated string literal.");
*bufptr = '\0';
return (false);
}
@ -384,6 +409,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Out of space...
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
@ -417,9 +443,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Out of space...
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
if (ch == '\r')
{
// Look for a trailing LF
if ((ch = get_char(tb)) != EOF && ch != '\n')
tb->bufptr --;
}
break;
case 'N' : // number
@ -428,6 +462,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if (!isdigit(ch) && ch != '.')
{
// End of number...
PDFIO_DEBUG("_pdfioTokenRead: End of number with ch=0x%02x\n", ch);
tb->bufptr --;
break;
}
@ -440,6 +475,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Out of space...
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
@ -466,14 +502,19 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
if (!isxdigit(tch & 255))
{
_pdfioFileError(tb->pdf, "Bad # escape in name.");
*bufptr = '\0';
return (false);
}
else if (isdigit(tch))
{
ch = ((ch & 255) << 4) | (tch - '0');
}
else
{
ch = ((ch & 255) << 4) | (tolower(tch) - 'a' + 10);
}
}
}
if (bufptr < bufend)
{
@ -483,9 +524,17 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Out of space
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
if (bufptr == (buffer + 1))
{
_pdfioFileError(tb->pdf, "Empty name.");
*bufptr = '\0';
return (false);
}
break;
case '<' : // Potential hex string
@ -505,9 +554,12 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
else if (!isspace(ch & 255) && !isxdigit(ch & 255))
{
_pdfioFileError(tb->pdf, "Syntax error: '<%c'", ch);
*bufptr = '\0';
return (false);
}
count = 0;
do
{
if (isxdigit(ch))
@ -516,25 +568,39 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
{
// Hex digit
*bufptr++ = (char)ch;
count = 0;
}
else
{
// Too large
_pdfioFileError(tb->pdf, "Token too large.");
*bufptr = '\0';
return (false);
}
}
else if (!isspace(ch))
{
_pdfioFileError(tb->pdf, "Invalid hex string character '%c'.", ch);
*bufptr = '\0';
return (false);
}
else
{
count ++;
if (count > 2048)
{
_pdfioFileError(tb->pdf, "Too much whitespace.");
*bufptr = '\0';
return (false);
}
}
}
while ((ch = get_char(tb)) != EOF && ch != '>');
if (ch == EOF)
{
_pdfioFileError(tb->pdf, "Unterminated hex string.");
*bufptr = '\0';
return (false);
}
break;
@ -547,6 +613,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
else
{
_pdfioFileError(tb->pdf, "Syntax error: '>%c'.", ch);
*bufptr = '\0';
return (false);
}
break;
@ -554,7 +621,7 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
*bufptr = '\0';
// PDFIO_DEBUG("_pdfioTokenRead: Read '%s'.\n", buffer);
PDFIO_DEBUG("_pdfioTokenRead: Read '%s'.\n", buffer);
return (bufptr > buffer);
}
@ -591,7 +658,6 @@ get_char(_pdfio_token_t *tb) // I - Token buffer
tb->bufptr = tb->buffer;
tb->bufend = tb->buffer + bytes;
#if 0
#ifdef DEBUG
unsigned char *ptr; // Pointer into buffer
@ -605,7 +671,6 @@ get_char(_pdfio_token_t *tb) // I - Token buffer
}
PDFIO_DEBUG("'\n");
#endif // DEBUG
#endif // 0
}
// Return the next character...

View File

@ -1,17 +1,20 @@
//
// PDF value functions for PDFio.
//
// Copyright © 2021-2023 by Michael R Sweet.
// Copyright © 2021-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
#include "pdfio-private.h"
//
// Include necessary headers...
// Local functions...
//
#include "pdfio-private.h"
static time_t get_date_time(const char *s);
//
@ -109,6 +112,101 @@ _pdfioValueCopy(pdfio_file_t *pdfdst, // I - Destination PDF file
}
//
// '_pdfioValueDecrypt()' - Decrypt a value.
//
bool // O - `true` on success, `false` on error
_pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - Object
_pdfio_value_t *v, // I - Value
size_t depth)// I - Depth
{
_pdfio_crypto_ctx_t ctx; // Decryption context
_pdfio_crypto_cb_t cb; // Decryption callback
size_t ivlen; // Number of initialization vector bytes
uint8_t temp[32768]; // Temporary buffer for decryption
size_t templen; // Number of actual data bytes
time_t timeval; // Date/time value
if (depth > PDFIO_MAX_DEPTH)
{
_pdfioFileError(pdf, "Value too deep.");
return (false);
}
switch (v->type)
{
default :
// Do nothing
break;
case PDFIO_VALTYPE_ARRAY :
return (_pdfioArrayDecrypt(pdf, obj, v->value.array, depth + 1));
break;
case PDFIO_VALTYPE_DICT :
return (_pdfioDictDecrypt(pdf, obj, v->value.dict, depth + 1));
break;
case PDFIO_VALTYPE_BINARY :
// Decrypt the binary string...
if (v->value.binary.datalen > (sizeof(temp) - 32))
{
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
return (false);
}
ivlen = v->value.binary.datalen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
return (false);
templen = (cb)(&ctx, temp, v->value.binary.data + ivlen, v->value.binary.datalen - ivlen);
// Copy the decrypted string back to the value and adjust the length...
memcpy(v->value.binary.data, temp, templen);
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
v->value.binary.datalen = templen - temp[templen - 1];
else
v->value.binary.datalen = templen;
break;
case PDFIO_VALTYPE_STRING :
// Decrypt regular string...
templen = strlen(v->value.string);
if (templen > (sizeof(temp) - 33))
{
_pdfioFileError(pdf, "Unable to read encrypted string - too long.");
return (false);
}
ivlen = templen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, (uint8_t *)v->value.string, &ivlen)) == NULL)
return (false);
templen = (cb)(&ctx, temp, (uint8_t *)v->value.string + ivlen, templen - ivlen);
temp[templen] = '\0';
if ((timeval = get_date_time((char *)temp)) != 0)
{
// Change the type to date...
v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval;
}
else
{
// Copy the decrypted string back to the value...
v->value.string = pdfioStringCreate(pdf, (char *)temp);
}
break;
}
return (true);
}
//
// '_pdfioValueDebug()' - Print the contents of a value.
//
@ -200,6 +298,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
size_t depth) // I - Depth of value
{
char token[32768]; // Token buffer
time_t timeval; // Date/time value
#ifdef DEBUG
static const char * const valtypes[] =
{
@ -249,73 +348,10 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
return (NULL);
}
else if (!strncmp(token, "(D:", 3))
else if (!strncmp(token, "(D:", 3) && (timeval = get_date_time(token + 1)) != 0)
{
// Possible date value of the form:
//
// (D:YYYYMMDDhhmmssZ)
// (D:YYYYMMDDhhmmss+HH'mm)
// (D:YYYYMMDDhhmmss-HH'mm)
//
int i; // Looping var
struct tm dateval; // Date value
int offset; // Date offset
for (i = 3; i < 17; i ++)
{
if (!isdigit(token[i] & 255))
break;
}
if (i >= 17)
{
if (token[i] == 'Z')
{
i ++;
}
else if (token[i] == '-' || token[i] == '+')
{
if (isdigit(token[i + 1] & 255) && isdigit(token[i + 2] & 255) && token[i + 3] == '\'' && isdigit(token[i + 4] & 255) && isdigit(token[i + 5] & 255))
{
i += 6;
if (token[i] == '\'')
i ++;
}
}
}
if (token[i])
{
// Just a string...
v->type = PDFIO_VALTYPE_STRING;
v->value.string = pdfioStringCreate(pdf, token + 1);
}
else
{
// Date value...
memset(&dateval, 0, sizeof(dateval));
dateval.tm_year = (token[3] - '0') * 1000 + (token[4] - '0') * 100 + (token[5] - '0') * 10 + token[6] - '0' - 1900;
dateval.tm_mon = (token[7] - '0') * 10 + token[8] - '0' - 1;
dateval.tm_mday = (token[9] - '0') * 10 + token[10] - '0';
dateval.tm_hour = (token[11] - '0') * 10 + token[12] - '0';
dateval.tm_min = (token[13] - '0') * 10 + token[14] - '0';
dateval.tm_sec = (token[15] - '0') * 10 + token[16] - '0';
if (token[17] == 'Z')
{
offset = 0;
}
else
{
offset = (token[18] - '0') * 600 + (token[19] - '0') * 60 + (token[20] - '0') * 10 + token[21] - '0';
if (token[17] == '-')
offset = -offset;
}
v->type = PDFIO_VALTYPE_DATE;
v->value.date = mktime(&dateval) + offset;
}
v->value.date = timeval;
}
else if (token[0] == '(')
{
@ -367,36 +403,6 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
*dataptr++ = (unsigned char)d;
}
if (obj && pdf->encryption)
{
// Decrypt the string...
_pdfio_crypto_ctx_t ctx; // Decryption context
_pdfio_crypto_cb_t cb; // Decryption callback
size_t ivlen; // Number of initialization vector bytes
uint8_t temp[32768]; // Temporary buffer for decryption
size_t templen; // Number of actual data bytes
if (v->value.binary.datalen > (sizeof(temp) - 32))
{
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
return (false);
}
ivlen = v->value.binary.datalen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
return (false);
templen = (cb)(&ctx, temp, v->value.binary.data + ivlen, v->value.binary.datalen - ivlen);
// Copy the decrypted string back to the value and adjust the length...
memcpy(v->value.binary.data, temp, templen);
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128)
v->value.binary.datalen = templen - temp[templen - 1];
else
v->value.binary.datalen = templen;
}
}
else if (strchr("0123456789-+.", token[0]) != NULL)
{
@ -491,7 +497,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
// If we get here, we have a number...
v->type = PDFIO_VALTYPE_NUMBER;
v->value.number = (double)strtod(token, NULL);
v->value.number = _pdfio_strtod(pdf, token);
}
else if (!strcmp(token, "true") || !strcmp(token, "false"))
{
@ -732,3 +738,76 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
return (false);
}
//
// 'get_date_time()' - Convert PDF date/time value to time_t.
//
static time_t // O - Time in seconds
get_date_time(const char *s) // I - PDF date/time value
{
int i; // Looping var
struct tm dateval; // Date value
int offset; // Date offset
// Possible date value of the form:
//
// (D:YYYYMMDDhhmmssZ)
// (D:YYYYMMDDhhmmss+HH'mm)
// (D:YYYYMMDDhhmmss-HH'mm)
//
for (i = 2; i < 16; i ++)
{
if (!isdigit(s[i] & 255) || !s[i])
break;
}
if (i >= 16)
{
if (s[i] == 'Z')
{
i ++;
}
else if (s[i] == '-' || s[i] == '+')
{
if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255))
{
i += 6;
if (s[i] == '\'')
i ++;
}
}
}
if (s[i])
{
// Just a string...
return (0);
}
// Date value...
memset(&dateval, 0, sizeof(dateval));
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900;
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1;
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0';
dateval.tm_hour = (s[10] - '0') * 10 + s[11] - '0';
dateval.tm_min = (s[12] - '0') * 10 + s[13] - '0';
dateval.tm_sec = (s[14] - '0') * 10 + s[15] - '0';
if (s[16] == 'Z')
{
offset = 0;
}
else
{
offset = (s[17] - '0') * 600 + (s[18] - '0') * 60 + (s[19] - '0') * 10 + s[20] - '0';
if (s[16] == '-')
offset = -offset;
}
return (mktime(&dateval) + offset);
}

11
pdfio.h
View File

@ -1,7 +1,7 @@
//
// Public header file for PDFio.
//
// Copyright © 2021-2023 by Michael R Sweet.
// Copyright © 2021-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -23,7 +23,7 @@ extern "C" {
// Version number...
//
# define PDFIO_VERSION "1.2.0"
# define PDFIO_VERSION "1.3.1"
//
@ -33,9 +33,11 @@ extern "C" {
# if defined(__has_extension) || defined(__GNUC__)
# define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
# define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
# define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC
# else
# define _PDFIO_PUBLIC
# define _PDFIO_FORMAT(a,b)
# define _PDFIO_DEPRECATED
# endif // __has_extension || __GNUC__
@ -63,7 +65,7 @@ typedef enum pdfio_encryption_e // PDF encryption modes
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_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@
} pdfio_encryption_t;
typedef enum pdfio_filter_e // Compression/decompression filters for streams
{
@ -180,13 +182,16 @@ extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char
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 *pdfioFileCreateNumberObj(pdfio_file_t *pdf, double number) _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 *pdfioFileCreateStringObj(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateTemporary(char *buffer, size_t bufsize, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;
extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_dict_t *pdfioFileGetCatalog(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetCreator(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;

View File

@ -9,5 +9,5 @@ Version: @PDFIO_VERSION@
URL: https://www.msweet.org/pdfio
Requires: @PKGCONFIG_REQUIRES@
Libs: @PKGCONFIG_LIBS@
Libs.private: @PKGCONFIG_LIBS_PRIVATE@ -lm
Libs.private: @PKGCONFIG_LIBS_PRIVATE@
Cflags: @PKGCONFIG_CFLAGS@

View File

@ -141,6 +141,9 @@ pdfioContentTextEnd
pdfioContentTextMeasure
pdfioContentTextMoveLine
pdfioContentTextMoveTo
pdfioContentTextNewLine
pdfioContentTextNewLineShow
pdfioContentTextNewLineShowf
pdfioContentTextNextLine
pdfioContentTextShow
pdfioContentTextShowJustified
@ -179,9 +182,11 @@ pdfioFileCreateFontObjFromFile
pdfioFileCreateICCObjFromFile
pdfioFileCreateImageObjFromData
pdfioFileCreateImageObjFromFile
pdfioFileCreateNumberObj
pdfioFileCreateObj
pdfioFileCreateOutput
pdfioFileCreatePage
pdfioFileCreateStringObj
pdfioFileCreateTemporary
pdfioFileFindObj
pdfioFileGetAuthor

View File

@ -3,7 +3,7 @@
<metadata>
<id>pdfio_native</id>
<title>PDFio Library for VS2019+</title>
<version>1.2.0</version>
<version>1.3.1</version>
<authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
@ -13,10 +13,10 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>PDFio Library for VS2019+</description>
<summary>PDFio is a simple C library for reading and writing PDF files. PDFio is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GNU GPL2-only software.</summary>
<copyright>Copyright © 2019-2023 by Michael R Sweet</copyright>
<copyright>Copyright © 2019-2024 by Michael R Sweet</copyright>
<tags>pdf file native</tags>
<dependencies>
<dependency id="pdfio_native.redist" version="1.2.0" />
<dependency id="pdfio_native.redist" version="1.3.1" />
<dependency id="zlib_native.redist" version="1.2.11" />
</dependencies>
</metadata>

View File

@ -3,7 +3,7 @@
<metadata>
<id>pdfio_native.redist</id>
<title>PDFio Library for VS2019+</title>
<version>1.2.0</version>
<version>1.3.1</version>
<authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
@ -13,7 +13,7 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>PDFio Library for VS2019+</description>
<summary>PDFio is a simple C library for reading and writing PDF files. This package provides the redistributable content for the PDFio library. PDFio is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GNU GPL2-only software.</summary>
<copyright>Copyright © 2019-2023 by Michael R Sweet</copyright>
<copyright>Copyright © 2019-2024 by Michael R Sweet</copyright>
<tags>pdf file native</tags>
<dependencies>
<dependency id="zlib_native.redist" version="1.2.11" />

16
runtests.bat Normal file
View File

@ -0,0 +1,16 @@
:: Script to run unit test program
::
:: Usage:
::
:: .\runtests.bat x64\{Debug|Release}
::
:: Copy dependent DLLs to the named build directory
echo Copying DLLs
copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Debug\*.dll %1
copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Release\*.dll %1
:: Run unit test program
echo Running %1\testpdfio.exe
cd %1
testpdfio.exe

View File

@ -1,7 +1,7 @@
//
// Test program for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -10,16 +10,13 @@
//
// ./testpdfio
//
// ./testpdfio FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ...
//
//
// Include necessary headers...
// ./testpdfio [--verbose] FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ...
//
#include "pdfio-private.h"
#include "pdfio-content.h"
#include <math.h>
#include <locale.h>
#ifndef M_PI
# define M_PI 3.14159265358979323846264338327950288
#endif // M_PI
@ -51,7 +48,7 @@ static int write_images_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_jpeg_test(pdfio_file_t *pdf, const char *title, int number, pdfio_obj_t *font, pdfio_obj_t *image);
static int write_png_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_text_test(pdfio_file_t *pdf, int first_page, pdfio_obj_t *font, const char *filename);
static int write_unit_file(pdfio_file_t *inpdf, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image);
static int write_unit_file(pdfio_file_t *inpdf, const char *outname, pdfio_file_t *outpdf, size_t *num_pages, size_t *first_image);
//
@ -65,6 +62,8 @@ main(int argc, // I - Number of command-line arguments
int ret = 0; // Return value
fprintf(stderr, "testpdfio: Test locale is \"%s\".\n", setlocale(LC_ALL, getenv("LANG")));
if (argc > 1)
{
int i; // Looping var
@ -1045,7 +1044,7 @@ do_unit_tests(void)
else
goto fail;
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
if (write_unit_file(inpdf, "testpdfio-out.pdf", outpdf, &num_pages, &first_image))
goto fail;
if (read_unit_file("testpdfio-out.pdf", num_pages, first_image, false))
@ -1064,7 +1063,7 @@ do_unit_tests(void)
else
goto fail;
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
if (write_unit_file(inpdf, "testpdfio-out2.pdf", outpdf, &num_pages, &first_image))
goto fail;
close(outfd);
@ -1085,7 +1084,7 @@ do_unit_tests(void)
else
return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
if (write_unit_file(inpdf, "testpdfio-rc4.pdf", outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file("testpdfio-rc4.pdf", num_pages, first_image, false))
@ -1104,7 +1103,7 @@ do_unit_tests(void)
else
return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
if (write_unit_file(inpdf, "testpdfio-rc4p.pdf", outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file("testpdfio-rc4p.pdf", num_pages, first_image, false))
@ -1122,7 +1121,7 @@ do_unit_tests(void)
else
return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
if (write_unit_file(inpdf, "testpdfio-aes.pdf", outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file("testpdfio-aes.pdf", num_pages, first_image, false))
@ -1140,7 +1139,7 @@ do_unit_tests(void)
else
return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
if (write_unit_file(inpdf, "testpdfio-aesp.pdf", outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file("testpdfio-aesp.pdf", num_pages, first_image, false))
@ -1152,7 +1151,7 @@ do_unit_tests(void)
else
return (1);
if (write_unit_file(inpdf, outpdf, &num_pages, &first_image))
if (write_unit_file(inpdf, "<temporary>", outpdf, &num_pages, &first_image))
return (1);
if (read_unit_file(temppdf, num_pages, first_image, false))
@ -1309,6 +1308,7 @@ read_unit_file(const char *filename, // I - File to read
bool is_output) // I - File written with output callback?
{
pdfio_file_t *pdf; // PDF file
pdfio_dict_t *catalog; // Catalog dictionary
size_t i; // Looping var
const char *s; // String
bool error = false; // Error callback data
@ -1321,6 +1321,83 @@ read_unit_file(const char *filename, // I - File to read
else
return (1);
// Get the root object/catalog dictionary
fputs("pdfioFileGetCatalog: ", stdout);
if ((catalog = pdfioFileGetCatalog(pdf)) != NULL)
{
puts("PASS");
}
else
{
puts("FAIL (got NULL, expected dictionary)");
return (1);
}
// Verify some catalog values...
fputs("pdfioDictGetName(PageLayout): ", stdout);
if ((s = pdfioDictGetName(catalog, "PageLayout")) != NULL && !strcmp(s, "SinglePage"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'SinglePage')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'SinglePage')");
return (1);
}
fputs("pdfioDictGetName(PageLayout): ", stdout);
if ((s = pdfioDictGetName(catalog, "PageLayout")) != NULL && !strcmp(s, "SinglePage"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'SinglePage')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'SinglePage')");
return (1);
}
fputs("pdfioDictGetName(PageMode): ", stdout);
if ((s = pdfioDictGetName(catalog, "PageMode")) != NULL && !strcmp(s, "UseThumbs"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'UseThumbs')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'UseThumbs')");
return (1);
}
fputs("pdfioDictGetString(Lang): ", stdout);
if ((s = pdfioDictGetString(catalog, "Lang")) != NULL && !strcmp(s, "en"))
{
puts("PASS");
}
else if (s)
{
printf("FAIL (got '%s', expected 'en')\n", s);
return (1);
}
else
{
puts("FAIL (got NULL, expected 'en')");
return (1);
}
// Verify metadata...
fputs("pdfioFileGetAuthor: ", stdout);
if ((s = pdfioFileGetAuthor(pdf)) != NULL && !strcmp(s, "Michael R Sweet"))
@ -3247,6 +3324,7 @@ write_text_test(pdfio_file_t *pdf, // I - PDF file
static int // O - Exit status
write_unit_file(
pdfio_file_t *inpdf, // I - Input PDF file
const char *outname, // I - Output PDF file name
pdfio_file_t *outpdf, // I - Output PDF file
size_t *num_pages, // O - Number of pages
size_t *first_image) // O - First image object
@ -3256,8 +3334,26 @@ write_unit_file(
*gray_jpg, // gray.jpg image
*helvetica, // Helvetica font
*page; // Page from test PDF file
pdfio_dict_t *catalog; // Catalog dictionary
// Get the root object/catalog dictionary
fputs("pdfioFileGetCatalog: ", stdout);
if ((catalog = pdfioFileGetCatalog(outpdf)) != NULL)
{
puts("PASS");
}
else
{
puts("FAIL (got NULL, expected dictionary)");
return (1);
}
// Set some catalog values...
pdfioDictSetName(catalog, "PageLayout", "SinglePage");
pdfioDictSetName(catalog, "PageMode", "UseThumbs");
pdfioDictSetString(catalog, "Lang", "en");
// Set info values...
fputs("pdfioFileGet/SetAuthor: ", stdout);
pdfioFileSetAuthor(outpdf, "Michael R Sweet");
@ -3441,7 +3537,7 @@ write_unit_file(
}
// Close the new PDF file...
fputs("pdfioFileClose(...): ", stdout);
printf("pdfioFileClose(\"%s\"): ", outname);
if (pdfioFileClose(outpdf))
puts("PASS");
else

39
ttf.c
View File

@ -3,7 +3,7 @@
//
// https://github.com/michaelrsweet/ttf
//
// Copyright © 2018-2023 by Michael R Sweet.
// Copyright © 2018-2024 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -99,7 +99,7 @@ typedef __int64 ssize_t; // POSIX type not present on Windows...
//
#define TTF_FONT_MAX_CHAR 262144 // Maximum number of character values
#define TTF_FONT_MAX_GROUPS 65536 // Maximum number of sub-groups
//
// TTF/OFF tag constants...
@ -1285,6 +1285,13 @@ read_cmap(ttf_t *font) // I - Font
// segCount --; // Last segment is not used (sigh)
font->num_cmap = segments[segCount - 1].endCode + 1;
if (font->num_cmap > TTF_FONT_MAX_CHAR)
{
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
return (false);
}
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
if (!font->cmap)
@ -1307,7 +1314,7 @@ read_cmap(ttf_t *font) // I - Font
{
// Use an "obscure indexing trick" (words from the spec, not
// mine) to look up the glyph index...
temp = segment->idRangeOffset / 2 - segCount + (ch - segment->startCode) + (segment - segments);
temp = (int)(segment->idRangeOffset / 2 - segCount + (ch - segment->startCode) + (segment - segments));
TTF_DEBUG("read_cmap: ch=%d, temp=%d\n", ch, temp);
if (temp < 0 || temp >= numGlyphIdArray)
@ -1356,6 +1363,12 @@ read_cmap(ttf_t *font) // I - Font
TTF_DEBUG("read_cmap: nGroups=%u\n", nGroups);
if (nGroups > TTF_FONT_MAX_GROUPS)
{
errorf(font, "Invalid cmap table with %u groups.", nGroups);
return (false);
}
if ((groups = (_ttf_off_cmap12_t *)calloc(nGroups, sizeof(_ttf_off_cmap12_t))) == NULL)
{
errorf(font, "Unable to allocate memory for cmap.");
@ -1376,6 +1389,13 @@ read_cmap(ttf_t *font) // I - Font
// Based on the end code of the segent table, allocate space for the
// uncompressed cmap table...
TTF_DEBUG("read_cmap: num_cmap=%u\n", (unsigned)font->num_cmap);
if (font->num_cmap > TTF_FONT_MAX_CHAR)
{
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
return (false);
}
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
if (!font->cmap)
@ -1426,6 +1446,12 @@ read_cmap(ttf_t *font) // I - Font
TTF_DEBUG("read_cmap: nGroups=%u\n", nGroups);
if (nGroups > TTF_FONT_MAX_GROUPS)
{
errorf(font, "Invalid cmap table with %u groups.", nGroups);
return (false);
}
if ((groups = (_ttf_off_cmap13_t *)calloc(nGroups, sizeof(_ttf_off_cmap13_t))) == NULL)
{
errorf(font, "Unable to allocate memory for cmap.");
@ -1446,6 +1472,13 @@ read_cmap(ttf_t *font) // I - Font
// Based on the end code of the segent table, allocate space for the
// uncompressed cmap table...
TTF_DEBUG("read_cmap: num_cmap=%u\n", (unsigned)font->num_cmap);
if (font->num_cmap > TTF_FONT_MAX_CHAR)
{
errorf(font, "Invalid cmap table with %u characters.", (unsigned)font->num_cmap);
return (false);
}
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
if (!font->cmap)