101 Commits

Author SHA1 Message Date
0c41a14919 Don't set extension pointer if object is NULL. 2025-04-25 12:40:54 -04:00
57a01a7317 Fix object map to use unique file hash instead of pointer values (Issue #125) 2025-04-24 14:00:24 -04:00
5b08046ece Add more range checking for streams. 2025-04-24 13:17:31 -04:00
d1bf9a92dc Fix whitespace. 2025-04-24 12:29:20 -04:00
749c0f845d Drop Xpdf and other checks from test-corpus.sh script. 2025-04-24 12:07:07 -04:00
cad8f450ab Multiple fixes to allow PDFio to read more edge-case PDFs.
- Update _pdfioFileGets to allow for really long lines where it
  doesn't matter if we lose the end of the line.
- Update "startxref" detection at the end of the file.
- Refactor repair logic so that you just get a single WARNING about
  the repair (debug messages available for testing)
- Allow whitespace after the "obj" in the object header.
- Make sure to close xref stream on error.
- Update predictor code to support Colors <= 32 (some implementations
  set Colors to the number of bytes per record in the xref stream,
  which prevents the predictor from doing anything...)
- Allow CR CR in xref table.
- Clear old trailer/root/pages/etc. objects when repairing, update
  existing objects that were already found in load_xref.
- Don't set current object in pdfioObjectCreate/OpenStream if the
  stream can't be created/opened.
2025-04-24 11:09:54 -04:00
278ddb7fa7 Clarify error callback API, and actually use the return value.
Improve repair implementation.
2025-04-23 14:43:14 -04:00
1e6bb710e3 Changelog. 2025-04-23 10:13:35 -04:00
4ca93bd34f Add support for EncryptMetadata key in encryption dictionary. 2025-04-23 10:07:44 -04:00
404ca72882 Protect against invalid predictor buffer size. 2025-04-23 10:07:26 -04:00
67704ce493 Update corpus test script to use Xpdf's pdfinfo to weed out known-bad PDFs for now. 2025-04-21 16:59:59 -04:00
e983ba98d8 Fix encrypted PDF files with P values >2^31-1. 2025-04-20 17:33:56 -04:00
c23c6dec9a Tweak script. 2025-04-18 20:00:14 -04:00
5e9617a742 One more Windows build warning. 2025-04-18 18:10:03 -04:00
09ee7d16c1 More test program cleanup to use test header. 2025-04-18 18:08:37 -04:00
078985fc20 Try to eliminate more Windows build warnings. 2025-04-18 17:58:06 -04:00
13fedfc424 Try a macro trick to get rid of some Windows build warnings. 2025-04-18 17:29:02 -04:00
c07927dc2d Bump copyright on test header since it was updated. 2025-04-18 17:22:15 -04:00
1393cd6c04 Hopefully fix Windows build issues. 2025-04-18 17:07:53 -04:00
b278e33aac Update unit test program to use test header. 2025-04-18 11:27:12 -04:00
1116e929f7 Add pdfioFileGet/SetLanguage functions (Issue #124) 2025-04-13 20:21:23 -04:00
c75611e274 Update documentation. 2025-04-13 17:15:53 -04:00
c507cabbda Update Windows DLL exports. 2025-04-13 16:56:50 -04:00
81aeef46d2 Add XMP metadata to output (Issue #103) 2025-04-13 16:56:30 -04:00
ba7371b2e1 Fix location of OutputIntents (catalog, not info dict) 2025-04-13 14:31:14 -04:00
ec64af8b20 Add pdfioFileAddOutputIntent API (Issue #104) 2025-04-13 14:16:53 -04:00
acd68df592 Start work on OutputIntent and better color support in PDFio:
- Add CGATS001-compatible "micro" ICC profile as a standard CMYK color space
  (this is the default used by several Adobe applications)
- Add `PDFIO_CS_CGATS001` color space enum.
- Extend `pdfioArrayCreateColorFromStandard` to support CMYK.
- Extend `pdfioFileCreateImageObjFromFile` to support CMYK JPEG files.
- Update `pdfioFileCreatePage` to add default grayscale, RGB, and CMYK color
  space resources as needed.
2025-04-13 13:31:19 -04:00
3d9c22aa49 Remove old per-object crypto stuff we dont need. 2025-04-13 09:11:43 -04:00
3ae95f1545 Fix decryption of UTF-16 strings (Issue #42) 2025-04-13 09:04:00 -04:00
cfdd6f55d9 Fix decryption of RC4-40 files. 2025-04-13 08:36:58 -04:00
06f38edcc7 Add pdfioFileCreateFontObjFromData function (Issue #120) 2025-04-12 16:25:34 -04:00
76c1cc694f Bump version for start of 1.6.x development. 2025-04-12 15:52:28 -04:00
4219b8fd77 Update release date. 2025-04-12 15:12:07 -04:00
064e7fa473 Fix makesrcdist script. 2025-04-12 15:11:36 -04:00
ea9b7843fc Bump version in NuGet files and update docos. 2025-04-12 14:38:40 -04:00
755efe08da Range check dictionary values in pdfioImageGetBytesPerLine (Issue #121) 2025-04-12 14:33:13 -04:00
0391df5bbd Add logging of when we are repairing the xref table. 2025-04-07 09:01:41 -04:00
49efd97cab Discard duplication key/value pairs in dictionaries with a warning message (Issue #118) 2025-04-07 08:58:18 -04:00
d7eb1fc540 One more tweak to the get_date_time function (Issue #115) 2025-04-07 08:20:08 -04:00
7afefda326 More date/time processing fixes (Issue #115) 2025-04-06 12:10:55 -04:00
cbea3ecc2a Fix timezone offset in date/time values (Issue #115) 2025-04-05 13:48:11 -04:00
130cef8702 Update pdfioinfo example to support Acrobat Form dictionaries as well as indirect references (Issue #114) 2025-04-04 21:24:42 -04:00
0bd9edc845 Move token buffers off the stack (Issue #117) 2025-04-04 21:20:23 -04:00
fe755eac3d Add PDFIO_MAX_STRING constant to control maximum allowed PDF strings (Issue #117) 2025-04-04 19:27:03 -04:00
8cca645835 Update date/time parsing (Issue #115) 2025-04-04 19:12:16 -04:00
b8ea9ea064 Bump version. 2025-04-04 19:11:54 -04:00
2874022aa4 Allow empty name tokens (Issue #116) 2025-04-04 18:26:35 -04:00
3befcf2fd5 Fix warning about shadowed loop variable. 2025-04-04 18:17:04 -04:00
3b2f7e21d9 Prep for 1.5.1 release. 2025-03-28 14:39:59 -04:00
7e01069c5a Fix UTF-16 LE support (Issue #112) 2025-03-28 14:29:24 -04:00
88839ccb56 Fix UTF-16 LE support (Issue #112) 2025-03-28 14:28:43 -04:00
ebd5aab39b Fix handling of 0-length streams (Issue #111) 2025-03-27 12:44:42 -04:00
71d33c03ff Add PDF merge example. 2025-03-27 11:48:41 -04:00
cfe91b4ea2 Fix output of special characters in name values (Issue #106)
Fix output of special characters in string values (Issue #107)
Fi output of large integers in dictionaries (Issue #108)

Bump version to 1.5.1.
2025-03-24 18:33:24 -04:00
458f366d78 Fix some Unicode font embedding issues:
- Reworked Widths array compression for CID fonts to require at least 4 repeated
  widths.
- Fixed the embedded CMap for Unicode fonts.
2025-03-06 17:09:27 -05:00
4165cd23ba Fix some issues discovered by some PDF checking tools:
- Extremely small floating point numbers would be written with exponential
  notation my the pdfioContent functions.  They are now written with up to 6
  decimal places of precision with excess trailing 0's removed.
- 8-bit (simple) TrueType fonts were embedded without a Widths array, which
  made Acrobat Reader sad but nobody else...
- Switched to using the WinANSI base encoding, which is CP1252.
2025-03-06 16:04:00 -05:00
7e56d26ff8 Prep for release. 2025-03-06 14:41:34 -05:00
712b213ec6 Enable libpng tests in testpdfio, too. 2025-03-06 14:41:38 -05:00
b7b6655db0 Update dependencies on Windows to include libpng. 2025-03-06 14:37:44 -05:00
e9debcd169 Add some more range checking to the cmap code. 2025-03-06 14:16:38 -05:00
2f925ccd3c Update documentation and pdf2text example (Issue #95) 2025-03-06 12:40:19 -05:00
89c2a75376 Fix a potential heap overflow in the TrueType cmap code. 2025-02-24 10:55:28 -05:00
1237599dea Clean up some compiler warnings. 2025-02-22 19:48:09 -05:00
6e2e4bbcc6 Remove unnnecessary length remaining check. 2025-02-22 11:04:31 -05:00
d535067c91 Fix pkg-config dependencies. 2025-02-22 08:30:38 -05:00
e996898b57 Back out object stream changes, as they would require much more significant
reworking of the "write value" private API that I don't want to do right now.
2025-02-21 16:57:01 -05:00
aa6a20c042 Lay the groundwork for object streams. 2025-02-21 15:33:27 -05:00
f09105dd3f Add support for writing the PCLm subset of PDF (Issue #99) 2025-02-20 18:18:53 -05:00
5be5552b2b Turn write_obj_header into private API. 2025-02-20 17:37:31 -05:00
492a4f51b2 Allocate stream compression buffer. 2025-02-16 13:20:51 -05:00
44827bac1a Cleanup. 2025-02-16 12:40:39 -05:00
3fad0d6f15 Support xref streams with encrypted output. 2025-02-16 12:35:45 -05:00
aeee24b856 Add xref stream support (Issue #10) 2025-02-15 21:54:16 -05:00
8d72f22efe Add support for 'repairing' damaged PDF files (Issue #45) 2025-02-15 17:26:23 -05:00
77117ac789 Update MD5 code with proper coding style/documentation for this project. 2025-02-15 13:35:54 -05:00
fceb5a807d Update AES code with proper coding style/documentation for this project. 2025-02-15 12:56:27 -05:00
4f123c2a01 Update makesrcdist script to report all issues before exiting and fix major/minor version checks. 2025-02-15 12:30:19 -05:00
c4c8fa6036 Make sure we have all the version numbers in pdfio.h. 2025-02-15 12:25:09 -05:00
9a5c5ec65d Add support for the sRGB chunk in PNG files in addition to the cHRM and gAMA
chunks.
2025-02-14 14:51:06 -05:00
3f4308b68d Add ICC support to PNG files. 2025-02-14 14:37:08 -05:00
9e930a7c5d Add new pdfioFileCreateICCObjFromData API to DLL exports. 2025-02-14 13:23:01 -05:00
afa010cea2 Add ICC color profile support for JPEG files (Issue #7) 2025-02-14 13:22:30 -05:00
c26b200a83 Add missing symbol to DLL. 2025-02-13 19:27:04 -05:00
eff02198ab Clean up pdfioinfo example changes. 2025-02-13 19:25:44 -05:00
5f98c7838c Rename pdfioFileGetModDate to pdfioFileGetModificationDate.
Add pdfioFileSetModificationDate API.

Update DLL exports file.

Update docos and changelog.
2025-02-13 18:56:43 -05:00
4f880bc0c1 Merge pull request #88 from tlaronde/info
Extend by adding pdfioGetModDate and extend the pdfioinfo example
2025-02-13 18:47:28 -05:00
d032483ed4 Merge branch 'michaelrsweet:master' into info 2025-02-12 15:54:47 +01:00
b2fc82f3a8 Update CI dependencies.
Add libpng_native to VC++ projects.
2025-02-12 09:25:57 -05:00
b81d01f319 Fix builds without libpng. 2025-02-11 22:59:23 -05:00
1b35321615 Add PngSuite to testpdfio (Issue #90) 2025-02-11 22:54:59 -05:00
990342f2a5 Add masking, color space, and variable bit depth support (Issue #90) 2025-02-11 22:07:02 -05:00
7f5fc456bc Fix image dictionary for new libpng-based PNG image support (Issue #90) 2025-02-11 20:23:59 -05:00
7c527cc908 Fix pdfio-512.png file. 2025-02-11 20:23:28 -05:00
41d17fc4e3 Update version number in NuGet files. 2025-02-11 20:23:17 -05:00
4e89137689 Use pkg-config for compiler options.
Fix some issues with the image2pdf example code.
2025-02-11 20:22:36 -05:00
e686669b9d Save work on libpng PNG loader (Issue #90) 2025-02-10 21:25:59 -05:00
1e5cc6ffd5 Do cleanup of PNG loading code, in preparation of adding full support (Issue #90) 2025-02-10 15:54:29 -05:00
4f1b373232 Add PngSuite from http://www.schaik.com/pngsuite/ for testing PNG image
support (Issue #90)
2025-02-10 11:04:39 -05:00
6f4bfe107f Refactor pdfioFileCreateImageObjFromData to do the image writing in a separate
function (Issue #90)
2025-02-10 10:28:28 -05:00
5b5de3aff6 Update pdf2txt example to support font encodings. 2025-01-28 14:26:33 -05:00
8b2b013b36 Extend by adding pdfioGetModDate and extend the pdfioinfo example
When exploring a PDF, it may be convenient to have the typical
informations delivered by some "Document Properties"---and some more
about the MediaBox(es).

So just add the function to get the ModDate and extend the
pdfioinfo example as an example of what the library do have
and pdfioinfo as a debugging tool also.

Signed-off-by: Thierry LARONDE <tlaronde@kergis.com>
2025-01-18 11:25:36 +01:00
132 changed files with 9465 additions and 2754 deletions

View File

@ -17,7 +17,7 @@ jobs:
- name: Update Build Environment - name: Update Build Environment
run: sudo apt-get update --fix-missing -y run: sudo apt-get update --fix-missing -y
- name: Install Prerequisites - name: Install Prerequisites
run: sudo apt-get install -y cppcheck zlib1g-dev run: sudo apt-get install -y cppcheck zlib1g-dev libpng-dev
- name: Configure PDFio - name: Configure PDFio
run: ./configure --enable-debug --enable-sanitizer --enable-maintainer run: ./configure --enable-debug --enable-sanitizer --enable-maintainer
- name: Build PDFio - name: Build PDFio

View File

@ -32,7 +32,7 @@ jobs:
run: sudo apt-get update --fix-missing -y run: sudo apt-get update --fix-missing -y
- name: Install Prerequisites - name: Install Prerequisites
run: sudo apt-get install -y zlib1g-dev run: sudo apt-get install -y zlib1g-dev libpng-dev
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@v2 uses: github/codeql-action/init@v2

View File

@ -12,7 +12,7 @@ jobs:
- name: Update Build Environment - name: Update Build Environment
run: sudo apt-get update --fix-missing -y run: sudo apt-get update --fix-missing -y
- name: Install Prerequisites - name: Install Prerequisites
run: sudo apt-get install -y zlib1g-dev run: sudo apt-get install -y zlib1g-dev libpng-dev
- name: Download Coverity Build Tool - name: Download Coverity Build Tool
run: | run: |
wget -q https://scan.coverity.com/download/linux64 --post-data token="$TOKEN&project=$GITHUB_REPOSITORY" -O cov-analysis-linux64.tar.gz wget -q https://scan.coverity.com/download/linux64 --post-data token="$TOKEN&project=$GITHUB_REPOSITORY" -O cov-analysis-linux64.tar.gz

1
.gitignore vendored
View File

@ -16,6 +16,7 @@
/examples/md2pdf /examples/md2pdf
/examples/pdf2text /examples/pdf2text
/examples/pdfioinfo /examples/pdfioinfo
/examples/pdfiomerge
/Makefile /Makefile
/packages /packages
/pdfio.pc /pdfio.pc

View File

@ -1,6 +1,78 @@
Changes in PDFio Changes in PDFio
================ ================
v1.6.0 - YYYY-MM-DD
-------------------
- Added generation of XMP metadata when writing PDFs (Issue #103)
- Added "standard" `PDFIO_CS_CGATS001` color space for non-device CMYK
(Issue #104)
- Added CMYK JPEG support with embedded ICC profiles or using the CGATS001
profile (Issue #104)
- Added `pdfioFileAddOutputIntent` function to adding output intent information
to a PDF file (Issue #104)
- Added `pdfioFileCreateFontObjFromData` function for embedding fonts in
memory (Issue #120)
- Added `pdfioFileGetLanguage` and `pdfioFileSetLanguage` functions for getting
and setting the default/primary language of a PDF file (Issue #124)
- Now add default grayscale, RGB, and CMYK profile resources to pages as needed
(Issue #104)
v1.5.3 - YYYY-MM-DD
-------------------
- Fixed decryption of PDF files "protected" by 40-bit RC4 (Issue #42)
- Fixed decryption of UTF-16 strings (Issue #42)
- Fixed decryption of PDF files with large permission values.
- Fixed support for EncryptMetadata key in the encryption dictionary.
- Fixed `pdfioObjCopy` and `pdfioPageCopy` to properly identify the source PDF
file being used (Issue #125)
v1.5.2 - 2025-04-12
-------------------
- Updated maximum allowed PDF string size to 64k (Issue #117)
- Updated dictionary reading code to discard duplicate key/value pairs with a
warning message (Issue #118)
- Fixed form detection in `pdfioinfo` example code (Issue #114)
- Fixed parsing of certain date/time values (Issue #115)
- Fixed support for empty name values (Issue #116)
- Fixed range checking in `pdfioImageGetBytesPerLine` (Issue #121)
v1.5.1 - 2025-03-28
-------------------
- Fixed output of special characters in name values (Issue #106)
- Fixed output of special characters in string values (Issue #107)
- Fixed output of large integers in dictionaries (Issue #108)
- Fixed handling of 0-length streams (Issue #111)
- Fixed detection of UTF-16 Big-Endian strings (Issue #112)
v1.5.0 - 2025-03-06
-------------------
- Added support for embedded color profiles in JPEG images (Issue #7)
- Added `pdfioFileCreateICCObjFromData` API.
- Added support for writing cross-reference streams for PDF 1.5 and newer files
(Issue #10)
- Added `pdfioFileGetModDate()` API (Issue #88)
- Added support for using libpng to embed PNG images in PDF output (Issue #90)
- Added support for writing the PCLm subset of PDF (Issue #99)
- Now support opening damaged PDF files (Issue #45)
- Updated documentation (Issue #95)
- Updated the pdf2txt example to support font encodings.
- Fixed potential heap/integer overflow issues in the TrueType cmap code.
- Fixed an output issue for extremely small `double` values with the
`pdfioContent` APIs.
- Fixed a missing Widths array issue for embedded TrueType fonts.
- Fixed some Unicode font embedding issues.
v1.4.1 - 2025-01-24 v1.4.1 - 2025-01-24
------------------- -------------------

View File

@ -15,7 +15,7 @@
.SILENT: .SILENT:
# Version number... # Version numbers...
PDFIO_VERSION = @PDFIO_VERSION@ PDFIO_VERSION = @PDFIO_VERSION@
PDFIO_VERSION_MAJOR = @PDFIO_VERSION_MAJOR@ PDFIO_VERSION_MAJOR = @PDFIO_VERSION_MAJOR@
PDFIO_VERSION_MINOR = @PDFIO_VERSION_MINOR@ PDFIO_VERSION_MINOR = @PDFIO_VERSION_MINOR@
@ -241,6 +241,7 @@ testttf: ttf.o testttf.o
# Dependencies # Dependencies
$(OBJS): pdfio.h pdfio-private.h Makefile $(OBJS): pdfio.h pdfio-private.h Makefile
pdfio-content.o: pdfio-content.h ttf.h pdfio-content.o: pdfio-content.h ttf.h
testpdfio.o: test.h
testttf.o: ttf.h testttf.o: ttf.h
ttf.o: ttf.h ttf.o: ttf.h

256
configure vendored
View File

@ -1,6 +1,6 @@
#! /bin/sh #! /bin/sh
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for pdfio 1.4.1. # Generated by GNU Autoconf 2.71 for pdfio 1.6.0.
# #
# Report bugs to <https://github.com/michaelrsweet/pdfio/issues>. # Report bugs to <https://github.com/michaelrsweet/pdfio/issues>.
# #
@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package. # Identity of this package.
PACKAGE_NAME='pdfio' PACKAGE_NAME='pdfio'
PACKAGE_TARNAME='pdfio' PACKAGE_TARNAME='pdfio'
PACKAGE_VERSION='1.4.1' PACKAGE_VERSION='1.6.0'
PACKAGE_STRING='pdfio 1.4.1' PACKAGE_STRING='pdfio 1.6.0'
PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues' PACKAGE_BUGREPORT='https://github.com/michaelrsweet/pdfio/issues'
PACKAGE_URL='https://www.msweet.org/pdfio' PACKAGE_URL='https://www.msweet.org/pdfio'
@ -653,6 +653,7 @@ WARNINGS
CSFLAGS CSFLAGS
LIBPDFIO_STATIC LIBPDFIO_STATIC
LIBPDFIO LIBPDFIO
PKGCONFIG_LIBPNG
PKGCONFIG_REQUIRES PKGCONFIG_REQUIRES
PKGCONFIG_LIBS_PRIVATE PKGCONFIG_LIBS_PRIVATE
PKGCONFIG_LIBS PKGCONFIG_LIBS
@ -729,6 +730,7 @@ SHELL'
ac_subst_files='' ac_subst_files=''
ac_user_opts=' ac_user_opts='
enable_option_checking enable_option_checking
enable_libpng
enable_static enable_static
enable_shared enable_shared
enable_debug enable_debug
@ -1293,7 +1295,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing. # 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. # This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF cat <<_ACEOF
\`configure' configures pdfio 1.4.1 to adapt to many kinds of systems. \`configure' configures pdfio 1.6.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]... Usage: $0 [OPTION]... [VAR=VALUE]...
@ -1359,7 +1361,7 @@ fi
if test -n "$ac_init_help"; then if test -n "$ac_init_help"; then
case $ac_init_help in case $ac_init_help in
short | recursive ) echo "Configuration of pdfio 1.4.1:";; short | recursive ) echo "Configuration of pdfio 1.6.0:";;
esac esac
cat <<\_ACEOF cat <<\_ACEOF
@ -1367,6 +1369,8 @@ Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options --disable-option-checking ignore unrecognized --enable/--with options
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--enable-libpng use libpng for pdfioFileCreateImageObjFromFile,
default=auto
--disable-static do not install static library --disable-static do not install static library
--enable-shared install shared library --enable-shared install shared library
--enable-debug turn on debugging, default=no --enable-debug turn on debugging, default=no
@ -1456,7 +1460,7 @@ fi
test -n "$ac_init_help" && exit $ac_status test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then if $ac_init_version; then
cat <<\_ACEOF cat <<\_ACEOF
pdfio configure 1.4.1 pdfio configure 1.6.0
generated by GNU Autoconf 2.71 generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc. Copyright (C) 2021 Free Software Foundation, Inc.
@ -1509,39 +1513,6 @@ fi
} # ac_fn_c_try_compile } # ac_fn_c_try_compile
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists and can be compiled using the include files in
# INCLUDES, setting the cache variable VAR accordingly.
ac_fn_c_check_header_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$3=yes"
else $as_nop
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
# ac_fn_c_try_link LINENO # ac_fn_c_try_link LINENO
# ----------------------- # -----------------------
# Try to link conftest.$ac_ext, and return whether this succeeded. # Try to link conftest.$ac_ext, and return whether this succeeded.
@ -1588,6 +1559,101 @@ fi
as_fn_set_status $ac_retval as_fn_set_status $ac_retval
} # ac_fn_c_try_link } # ac_fn_c_try_link
# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
# Tests whether FUNC exists, setting the cache variable VAR accordingly
ac_fn_c_check_func ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define $2 innocuous_$2
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $2 (); below. */
#include <limits.h>
#undef $2
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char $2 ();
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined __stub_$2 || defined __stub___$2
choke me
#endif
int
main (void)
{
return $2 ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"
then :
eval "$3=yes"
else $as_nop
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES
# -------------------------------------------------------
# Tests whether HEADER exists and can be compiled using the include files in
# INCLUDES, setting the cache variable VAR accordingly.
ac_fn_c_check_header_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$3=yes"
else $as_nop
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
ac_configure_args_raw= ac_configure_args_raw=
for ac_arg for ac_arg
do do
@ -1612,7 +1678,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake. running configure, to aid debugging if configure makes a mistake.
It was created by pdfio $as_me 1.4.1, which was It was created by pdfio $as_me 1.6.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw $ $0$ac_configure_args_raw
@ -2368,9 +2434,9 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
PDFIO_VERSION="1.4.1" PDFIO_VERSION="1.6.0"
PDFIO_VERSION_MAJOR="`echo 1.4.1 | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo 1.6.0 | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo 1.4.1 | awk -F. '{printf("%d\n",$2);}'`" PDFIO_VERSION_MINOR="`echo 1.6.0 | awk -F. '{printf("%d\n",$2);}'`"
@ -3873,6 +3939,56 @@ INSTALL="$(pwd)/install-sh"
printf "%s\n" "using $INSTALL" >&6; } printf "%s\n" "using $INSTALL" >&6; }
ac_fn_c_check_func "$LINENO" "timegm" "ac_cv_func_timegm"
if test "x$ac_cv_func_timegm" = xyes
then :
printf "%s\n" "#define HAVE_TIMEGM 1" >>confdefs.h
CPPFLAGS="-DHAVE_TIMEGM=1 $CPPFLAGS"
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for tm_gmtoff member in tm structure" >&5
printf %s "checking for tm_gmtoff member in tm structure... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <time.h>
int
main (void)
{
struct tm t;
int o = t.tm_gmtoff;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_TM_GMTOFF 1" >>confdefs.h
CPPFLAGS="-DHAVE_TM_GMTOFF=1 $CPPFLAGS"
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test -n "$ac_tool_prefix"; then if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args.
set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 set dummy ${ac_tool_prefix}pkg-config; ac_word=$2
@ -3994,7 +4110,6 @@ PKGCONFIG_REQUIRES="zlib"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib via pkg-config" >&5 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for zlib via pkg-config" >&5
printf %s "checking for zlib via pkg-config... " >&6; } printf %s "checking for zlib via pkg-config... " >&6; }
ac_header= ac_cache= ac_header= ac_cache=
for ac_item in $ac_header_c_list for ac_item in $ac_header_c_list
do do
@ -4099,6 +4214,55 @@ fi
fi fi
# Check whether --enable-libpng was given.
if test ${enable_libpng+y}
then :
enableval=$enable_libpng;
fi
PKGCONFIG_LIBPNG=""
if test "x$PKGCONFIG" != x -a x$enable_libpng != xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libpng-1.6.x" >&5
printf %s "checking for libpng-1.6.x... " >&6; }
if $PKGCONFIG --exists libpng16
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; };
printf "%s\n" "#define HAVE_LIBPNG 1" >>confdefs.h
CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS"
PKGCONFIG_LIBS_PRIVATE="$($PKGCONFIG --libs libpng16) $PKGCONFIG_LIBS_PRIVATE"
PKGCONFIG_REQUIRES="libpng >= 1.6,$PKGCONFIG_REQUIRES"
else $as_nop
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; };
if test x$enable_libpng = xyes
then :
as_fn_error $? "libpng-dev 1.6 or later required for --enable-libpng." "$LINENO" 5
fi
fi
elif test x$enable_libpng = xyes
then :
as_fn_error $? "libpng-dev 1.6 or later required for --enable-libpng." "$LINENO" 5
fi
# Check whether --enable-static was given. # Check whether --enable-static was given.
if test ${enable_static+y} if test ${enable_static+y}
then : then :
@ -4935,7 +5099,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their # report actual input values of CONFIG_FILES etc. instead of their
# values after options handling. # values after options handling.
ac_log=" ac_log="
This file was extended by pdfio $as_me 1.4.1, which was This file was extended by pdfio $as_me 1.6.0, which was
generated by GNU Autoconf 2.71. Invocation command line was generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES CONFIG_FILES = $CONFIG_FILES
@ -4991,7 +5155,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped' ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\ ac_cs_version="\\
pdfio config.status 1.4.1 pdfio config.status 1.6.0
configured by $0, generated by GNU Autoconf 2.71, configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\" with options \\"\$ac_cs_config\\"

View File

@ -1,7 +1,7 @@
dnl dnl
dnl Configuration script for PDFio dnl Configuration script for PDFio
dnl dnl
dnl Copyright © 2023-2024 by Michael R Sweet dnl Copyright © 2023-2025 by Michael R Sweet
dnl dnl
dnl Licensed under Apache License v2.0. See the file "LICENSE" for more dnl Licensed under Apache License v2.0. See the file "LICENSE" for more
dnl information. dnl information.
@ -21,7 +21,7 @@ AC_PREREQ([2.70])
dnl Package name and version... dnl Package name and version...
AC_INIT([pdfio], [1.4.1], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio]) AC_INIT([pdfio], [1.6.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
PDFIO_VERSION="AC_PACKAGE_VERSION" PDFIO_VERSION="AC_PACKAGE_VERSION"
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`" PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"
@ -88,6 +88,27 @@ AC_SUBST([INSTALL])
AC_MSG_RESULT([using $INSTALL]) AC_MSG_RESULT([using $INSTALL])
dnl Check for date/time functionality...
AC_CHECK_FUNC([timegm], [
AC_DEFINE([HAVE_TIMEGM], [1], [Do we have the timegm function?])
CPPFLAGS="-DHAVE_TIMEGM=1 $CPPFLAGS"
])
AC_MSG_CHECKING([for tm_gmtoff member in tm structure])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([[#include <time.h>]], [[
struct tm t;
int o = t.tm_gmtoff;
]])
], [
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_TM_GMTOFF], [1], [Have tm_gmtoff member in struct tm?])
CPPFLAGS="-DHAVE_TM_GMTOFF=1 $CPPFLAGS"
], [
AC_MSG_RESULT([no])
])
dnl Check for pkg-config, which is used for some other tests later on... dnl Check for pkg-config, which is used for some other tests later on...
AC_PATH_TOOL([PKGCONFIG], [pkg-config]) AC_PATH_TOOL([PKGCONFIG], [pkg-config])
@ -121,6 +142,32 @@ AS_IF([$PKGCONFIG --exists zlib], [
]) ])
dnl libpng...
AC_ARG_ENABLE([libpng], AS_HELP_STRING([--enable-libpng], [use libpng for pdfioFileCreateImageObjFromFile, default=auto]))
PKGCONFIG_LIBPNG=""
AC_SUBST([PKGCONFIG_LIBPNG])
AS_IF([test "x$PKGCONFIG" != x -a x$enable_libpng != xno], [
AC_MSG_CHECKING([for libpng-1.6.x])
AS_IF([$PKGCONFIG --exists libpng16], [
AC_MSG_RESULT([yes]);
AC_DEFINE([HAVE_LIBPNG], 1, [Have PNG library?])
CPPFLAGS="$($PKGCONFIG --cflags libpng16) -DHAVE_LIBPNG=1 $CPPFLAGS"
LIBS="$($PKGCONFIG --libs libpng16) -lz $LIBS"
PKGCONFIG_LIBS_PRIVATE="$($PKGCONFIG --libs libpng16) $PKGCONFIG_LIBS_PRIVATE"
PKGCONFIG_REQUIRES="libpng >= 1.6,$PKGCONFIG_REQUIRES"
], [
AC_MSG_RESULT([no]);
AS_IF([test x$enable_libpng = xyes], [
AC_MSG_ERROR([libpng-dev 1.6 or later required for --enable-libpng.])
])
])
], [test x$enable_libpng = xyes], [
AC_MSG_ERROR([libpng-dev 1.6 or later required for --enable-libpng.])
])
dnl Library target... dnl Library target...
AC_ARG_ENABLE([static], AS_HELP_STRING([--disable-static], [do not install static library])) AC_ARG_ENABLE([static], AS_HELP_STRING([--disable-static], [do not install static library]))
AC_ARG_ENABLE([shared], AS_HELP_STRING([--enable-shared], [install shared library])) AC_ARG_ENABLE([shared], AS_HELP_STRING([--enable-shared], [install shared library]))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,4 +1,4 @@
.TH pdfio 3 "pdf read/write library" "2025-01-24" "pdf read/write library" .TH pdfio 3 "pdf read/write library" "2025-04-23" "pdf read/write library"
.SH NAME .SH NAME
pdfio \- pdf read/write library pdfio \- pdf read/write library
.SH Introduction .SH Introduction
@ -34,7 +34,7 @@ PDFio is
.I not .I not
concerned with rendering or viewing a PDF file, although a PDF RIP or viewer could be written using it. concerned with rendering or viewing a PDF file, although a PDF RIP or viewer could be written using it.
.PP .PP
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. PDFio is Copyright \[co] 2021\-2025 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 .SS Requirements
.PP .PP
PDFio requires the following to build the software: PDFio requires the following to build the software:
@ -52,9 +52,11 @@ A POSIX\-compliant sh program
.IP \(bu 5 .IP \(bu 5
.PP .PP
ZLIB (https://www.zlib.net) 1.0 or higher ZLIB (https://www.zlib.net/) 1.0 or higher
.PP
PDFio will also use libpng 1.6 or higher (https://www.libpng.org/) to provide enhanced PNG image support.
.PP .PP
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided. IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
.SS Installing PDFio .SS Installing PDFio
@ -323,7 +325,7 @@ where the five arguments to the function are the filename ("myinputfile.pdf"), a
} }
.fi .fi
.PP .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: 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. It returns true to continue processing the file or false to stop, for example:
.nf .nf
bool bool
@ -333,12 +335,15 @@ The error callback is called for both errors and warnings and accepts the pdfio_
fprintf(stderr, "%s: %s\\n", pdfioFileGetName(pdf), message); fprintf(stderr, "%s: %s\\n", pdfioFileGetName(pdf), message);
// Return false to treat warnings as errors // Return true for warning messages (continue) and false for errors (stop)
return (false); return (!strncmp(message, "WARNING:", 8));
} }
.fi .fi
.PP .PP
The default error callback (NULL) does the equivalent of the above. The default error callback (NULL) does the equivalent of the above.
.PP
Note: Many errors are unrecoverable, so PDFio ignores the return value from the error callback and always stops processing the PDF file. Warning messages start with the prefix "WARNING:" while errors have no prefix.
.PP .PP
Each PDF file contains one or more pages. The pdfioFileGetNumPages function returns the number of pages in the file while the pdfioFileGetPage function gets the specified page in the PDF file: Each PDF file contains one or more pages. The pdfioFileGetNumPages function returns the number of pages in the file while the pdfioFileGetPage function gets the specified page in the PDF file:
.nf .nf
@ -1045,11 +1050,26 @@ The pdfioinfo.c example program opens a PDF file and prints the title, author, c
{ {
const char *filename; // PDF filename const char *filename; // PDF filename
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
const char *author; // Author name pdfio_dict_t *catalog; // Catalog dictionary
time_t creation_date; // Creation date const char *author, // Author name
struct tm *creation_tm; // Creation date/time information *creator, // Creator name
char creation_text[256]; // Creation date/time as a string *producer, // Producer name
const char *title; // Title *title; // Title
time_t creation_date, // Creation date
modification_date; // Modification date
struct tm *creation_tm, // Creation date/time information
*modification_tm; // Modification date/time information
char creation_text[256], // Creation date/time as a string
modification_text[256], // Modification date/time human fmt string
range_text[255]; // Page range text
size_t num_pages; // PDF number of pages
bool has_acroform; // Does the file have an AcroForm?
pdfio_obj_t *page; // Object
pdfio_dict_t *page_dict; // Object dictionary
size_t cur, // Current page index
prev; // Previous page index
pdfio_rect_t cur_box, // Current MediaBox
prev_box; // Previous MediaBox
// Get the filename from the command\-line... // Get the filename from the command\-line...
@ -1062,14 +1082,20 @@ The pdfioinfo.c example program opens a PDF file and prints the title, author, c
filename = argv[1]; filename = argv[1];
// Open the PDF file with the default callbacks... // Open the PDF file with the default callbacks...
pdf = pdfioFileOpen(filename, /*password_cb*/NULL, /*password_cbdata*/NULL, pdf = pdfioFileOpen(filename, /*password_cb*/NULL,
/*error_cb*/NULL, /*error_cbdata*/NULL); /*password_cbdata*/NULL, /*error_cb*/NULL,
/*error_cbdata*/NULL);
if (pdf == NULL) if (pdf == NULL)
return (1); return (1);
// Get the title and author... // Get the title, author, etc...
author = pdfioFileGetAuthor(pdf); catalog = pdfioFileGetCatalog(pdf);
title = pdfioFileGetTitle(pdf); author = pdfioFileGetAuthor(pdf);
creator = pdfioFileGetCreator(pdf);
has_acroform = pdfioDictGetType(catalog, "AcroForm") != PDFIO_VALTYPE_NONE;
num_pages = pdfioFileGetNumPages(pdf);
producer = pdfioFileGetProducer(pdf);
title = pdfioFileGetTitle(pdf);
// Get the creation date and convert to a string... // Get the creation date and convert to a string...
if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0) if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0)
@ -1082,12 +1108,76 @@ The pdfioinfo.c example program opens a PDF file and prints the title, author, c
snprintf(creation_text, sizeof(creation_text), "\-\- not set \-\-"); snprintf(creation_text, sizeof(creation_text), "\-\- not set \-\-");
} }
// Get the modification date and convert to a string...
if ((modification_date = pdfioFileGetModificationDate(pdf)) > 0)
{
modification_tm = localtime(&modification_date);
strftime(modification_text, sizeof(modification_text), "%c", modification_tm);
}
else
{
snprintf(modification_text, sizeof(modification_text), "\-\- not set \-\-");
}
// Print file information to stdout... // Print file information to stdout...
printf("%s:\\n", filename); printf("%s:\\n", filename);
printf(" Title: %s\\n", title ? title : "\-\- not set \-\-"); printf(" Title: %s\\n", title ? title : "\-\- not set \-\-");
printf(" Author: %s\\n", author ? author : "\-\- not set \-\-"); printf(" Author: %s\\n", author ? author : "\-\- not set \-\-");
printf(" Created On: %s\\n", creation_text); printf(" Creator: %s\\n", creator ? creator : "\-\- not set \-\-");
printf(" Number Pages: %u\\n", (unsigned)pdfioFileGetNumPages(pdf)); printf(" Producer: %s\\n", producer ? producer : "\-\- not set \-\-");
printf(" Created On: %s\\n", creation_text);
printf(" Modified On: %s\\n", modification_text);
printf(" Version: %s\\n", pdfioFileGetVersion(pdf));
printf(" AcroForm: %s\\n", has_acroform ? "Yes" : "No");
printf(" Number of Pages: %u\\n", (unsigned)num_pages);
// Report the MediaBox for all of the pages
prev_box.x1 = prev_box.x2 = prev_box.y1 = prev_box.y2 = 0.0;
for (cur = 0, prev = 0; cur < num_pages; cur ++)
{
// Find the MediaBox for this page in the page tree...
for (page = pdfioFileGetPage(pdf, cur);
page != NULL;
page = pdfioDictGetObj(page_dict, "Parent"))
{
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
page_dict = pdfioObjGetDict(page);
if (pdfioDictGetRect(page_dict, "MediaBox", &cur_box))
break;
}
// If this MediaBox is different from the previous one, show the range of
// pages that have that size...
if (cur == 0 ||
fabs(cur_box.x1 \- prev_box.x1) > 0.01 ||
fabs(cur_box.y1 \- prev_box.y1) > 0.01 ||
fabs(cur_box.x2 \- prev_box.x2) > 0.01 ||
fabs(cur_box.y2 \- prev_box.y2) > 0.01)
{
if (cur > prev)
{
snprintf(range_text, sizeof(range_text), "Pages %u\-%u",
(unsigned)(prev + 1), (unsigned)cur);
printf("%16s: [%g %g %g %g]\\n", range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
// Start a new series of pages with the new size...
prev = cur;
prev_box = cur_box;
}
}
// Show the last range as needed...
if (cur > prev)
{
snprintf(range_text, sizeof(range_text), "Pages %u\-%u",
(unsigned)(prev + 1), (unsigned)cur);
printf("%16s: [%g %g %g %g]\\n", range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
// Close the PDF file... // Close the PDF file...
pdfioFileClose(pdf); pdfioFileClose(pdf);
@ -1097,28 +1187,83 @@ The pdfioinfo.c example program opens a PDF file and prints the title, author, c
.fi .fi
.SS Extract Text from PDF File .SS Extract Text from PDF File
.PP .PP
The pdf2text.c example code extracts non\-Unicode text from a PDF file by scanning each page for strings and text drawing commands. Since it doesn't look at the font encoding or support Unicode text, it is really only useful to extract plain ASCII text from a PDF file. And since it writes text in the order it appears in the page stream, it may not come out in the same order as appears on the page. The pdf2text.c example code extracts text from a PDF file and writes it to the standard output. Unlike some other PDF tools, it outputs the text in the order it is seen in each page stream so the output might appear "jumbled" if the PDF producer doesn't output text in reading order. The code is able to handle different font encodings and produces UTF\-8 output.
.PP .PP
The pdfioStreamGetToken function is used to read individual tokens from the page streams. Tokens starting with the open parenthesis are text strings, while PDF operators are left as\-is. We use some simple logic to make sure that we include spaces between text strings and add newlines for the text operators that start a new line in a text block: The pdfioStreamGetToken function is used to read individual tokens from the page streams:
.nf .nf
pdfio_stream_t *st; // Page stream pdfio_stream_t *st; // Page stream
char buffer[1024], // Token buffer
*bufptr, // Pointer into buffer
name[256]; // Current (font) name
bool first = true; // First string on line? bool first = true; // First string on line?
char buffer[1024]; // Token buffer int encoding[256]; // Font encoding to Unicode
bool in_array = false; // Are we in an array?
// Read PDF tokens from the page stream... // Read PDF tokens from the page stream...
while (pdfioStreamGetToken(st, buffer, sizeof(buffer))) while (pdfioStreamGetToken(st, buffer, sizeof(buffer)))
{ {
if (buffer[0] == '(') .fi
.PP
Justified text can be found inside arrays ("[ ... ]"), so we look for the array delimiter tokens and any (spacing) numbers inside an array. Experimentation has shown that numbers greater than 100 can be treated as whitespace:
.nf
if (!strcmp(buffer, "["))
{
// Start of an array for justified text...
in_array = true;
}
else if (!strcmp(buffer, "]"))
{
// End of an array for justified text...
in_array = false;
}
else if (!first && in_array && (isdigit(buffer[0]) || buffer[0] == '\-') && fabs(atof(buffer)) > 100)
{
// Whitespace in a justified text block...
putchar(' ');
}
.fi
.PP
Tokens starting with \'(' or \'<' are text fragments. 8\-bit text starting with \'(' needs to be mapped to Unicode using the current font encoding while hex strings starting with \'<' are UTF\-16 (Unicode) that need to be converted to UTF\-8:
.nf
else if (buffer[0] == '(')
{ {
// Text string using an 8\-bit encoding // Text string using an 8\-bit encoding
if (first) first = false;
first = false;
else if (buffer[1] != ' ')
putchar(' ');
fputs(buffer + 1, stdout); for (bufptr = buffer + 1; *bufptr; bufptr ++)
put_utf8(encoding[*bufptr & 255]);
} }
else if (buffer[0] == '<')
{
// Unicode text string
first = false;
puts_utf16(buffer + 1);
}
.fi
.PP
Simple (8\-bit) fonts include an encoding table that maps the 8\-bit characters to one of 1051 Unicode glyph names. Since each font can use a different encoding, we look for font names starting with \'/' and the "Tf" (set text font) operator token and load that font's encoding using the load_encoding function:
.nf
else if (buffer[0] == '/')
{
// Save name...
strncpy(name, buffer + 1, sizeof(name) \- 1);
name[sizeof(name) \- 1] = '\\0';
}
else if (!strcmp(buffer, "Tf") && name[0])
{
// Set font...
load_encoding(obj, name, encoding);
}
.fi
.PP
Finally, some text operators start a new line in a text block, so when we see their tokens we output a newline:
.nf
else if (!strcmp(buffer, "Td") || !strcmp(buffer, "TD") || !strcmp(buffer, "T*") || else if (!strcmp(buffer, "Td") || !strcmp(buffer, "TD") || !strcmp(buffer, "T*") ||
!strcmp(buffer, "\\'") || !strcmp(buffer, "\\"")) !strcmp(buffer, "\\'") || !strcmp(buffer, "\\""))
{ {
@ -1127,9 +1272,150 @@ The pdfioStreamGetToken function is used to read individual tokens from the page
first = true; first = true;
} }
} }
.fi
.PP
The load_encoding Function
.PP
The load_encoding function looks up the named font in the page's "Resources" dictionary. Every PDF simple font contains an "Encoding" dictionary with a base encoding ("WinANSI", "MacRoman", or "MacExpert") and a differences array that lists character indexes and glyph names for an 8\-bit font.
.PP
We start by initializing the encoding array to the default WinANSI encoding and looking up the font object for the named font:
.nf
static void
load_encoding(
pdfio_obj_t *page_obj, // I \- Page object
const char *name, // I \- Font name
int encoding[256]) // O \- Encoding table
{
size_t i, j; // Looping vars
pdfio_dict_t *page_dict, // Page dictionary
*resources_dict, // Resources dictionary
*font_dict; // Font dictionary
pdfio_obj_t *font_obj, // Font object
*encoding_obj; // Encoding object
static int win_ansi[32] = // WinANSI characters from 128 to 159
{
...
};
static int mac_roman[128] = // MacRoman characters from 128 to 255
{
...
};
if (!first)
putchar('\\n'); // Initialize the encoding to be the "standard" WinAnsi...
for (i = 0; i < 128; i ++)
encoding[i] = i;
for (i = 160; i < 256; i ++)
encoding[i] = i;
memcpy(encoding + 128, win_ansi, sizeof(win_ansi));
// Find the named font...
if ((page_dict = pdfioObjGetDict(page_obj)) == NULL)
return;
if ((resources_dict = pdfioDictGetDict(page_dict, "Resources")) == NULL)
return;
if ((font_dict = pdfioDictGetDict(resources_dict, "Font")) == NULL)
{
// Font resources not a dictionary, see if it is an object...
if ((font_obj = pdfioDictGetObj(resources_dict, "Font")) != NULL)
font_dict = pdfioObjGetDict(font_obj);
if (!font_dict)
return;
}
if ((font_obj = pdfioDictGetObj(font_dict, name)) == NULL)
return;
.fi
.PP
Once we have found the font we see if it has an "Encoding" dictionary:
.nf
pdfio_dict_t *encoding_dict; // Encoding dictionary
if ((encoding_obj = pdfioDictGetObj(pdfioObjGetDict(font_obj), "Encoding")) == NULL)
return;
if ((encoding_dict = pdfioObjGetDict(encoding_obj)) == NULL)
return;
.fi
.PP
Once we have the encoding dictionary we can get the "BaseEncoding" and "Differences" values:
.nf
const char *base_encoding; // BaseEncoding name
pdfio_array_t *differences; // Differences array
// OK, have the encoding object, build the encoding using it...
base_encoding = pdfioDictGetName(encoding_dict, "BaseEncoding");
differences = pdfioDictGetArray(encoding_dict, "Differences");
.fi
.PP
If the base encoding is "MacRomainEncoding", we need to reset the upper 128 characters in the encoding array match it:
.nf
if (base_encoding && !strcmp(base_encoding, "MacRomanEncoding"))
{
// Map upper 128
memcpy(encoding + 128, mac_roman, sizeof(mac_roman));
}
.fi
.PP
Then we loop through the differences array, keeping track of the current index within the encoding array. A number indicates a new index while a name is the Unicode glyph for the current index:
.nf
typedef struct name_map_s
{
const char *name; // Character name
int unicode; // Unicode value
} name_map_t;
static name_map_t unicode_map[1051]; // List of glyph names
if (differences)
{
// Apply differences
size_t count = pdfioArrayGetSize(differences);
// Number of differences
const char *name; // Character name
size_t idx = 0; // Index in encoding array
for (i = 0; i < count; i ++)
{
switch (pdfioArrayGetType(differences, i))
{
case PDFIO_VALTYPE_NUMBER :
// Get the index of the next character...
idx = (size_t)pdfioArrayGetNumber(differences, i);
break;
case PDFIO_VALTYPE_NAME :
// Lookup name and apply to encoding...
if (idx < 0 || idx > 255)
break;
name = pdfioArrayGetName(differences, i);
for (j = 0; j < (sizeof(unicode_map) / sizeof(unicode_map[0])); j ++)
{
if (!strcmp(name, unicode_map[j].name))
{
encoding[idx] = unicode_map[j].unicode;
break;
}
}
idx ++;
break;
default :
// Do nothing for other values
break;
}
}
}
}
.fi .fi
.SS Create a PDF File With Text and an Image .SS Create a PDF File With Text and an Image
.PP .PP
@ -2166,6 +2452,10 @@ PDFIO_CS_ADOBE
.br .br
AdobeRGB 1998 AdobeRGB 1998
.TP 5 .TP 5
PDFIO_CS_CGATS001
.br
CGATS001 (CMYK)
.TP 5
PDFIO_CS_P3_D65 PDFIO_CS_P3_D65
.br .br
Display P3 Display P3
@ -2194,7 +2484,7 @@ PDFIO_ENCRYPTION_RC4_128
.TP 5 .TP 5
PDFIO_ENCRYPTION_RC4_40 PDFIO_ENCRYPTION_RC4_40
.br .br
40-bit RC4 encryption (PDF 1.3) 40-bit RC4 encryption (PDF 1.3, reading only)
.SS pdfio_filter_e .SS pdfio_filter_e
Compression/decompression filters for streams Compression/decompression filters for streams
.TP 5 .TP 5
@ -2555,7 +2845,8 @@ pdfio_array_t * pdfioArrayCreateColorFromStandard (
.fi .fi
.PP .PP
This function creates a color array for a standard \fBPDFIO_CS_\fR enumerated color space. This function creates a color array for a standard \fBPDFIO_CS_\fR enumerated color space.
The "num_colors" argument must be \fB1\fR for grayscale and \fB3\fR for RGB color. The "num_colors" argument must be \fB1\fR for grayscale, \fB3\fR for RGB color, and
\fB4\fR for CMYK color.
.SS pdfioArrayGetArray .SS pdfioArrayGetArray
Get an array value from an array. Get an array value from an array.
.PP .PP
@ -2664,6 +2955,8 @@ bool pdfioArrayRemove (
size_t n size_t n
); );
.fi .fi
.PP
.SS pdfioContentClip .SS pdfioContentClip
Clip output to the current path. Clip output to the current path.
.PP .PP
@ -2800,6 +3093,8 @@ bool pdfioContentPathEnd (
pdfio_stream_t *st pdfio_stream_t *st
); );
.fi .fi
.PP
.SS pdfioContentPathLineTo .SS pdfioContentPathLineTo
Add a straight line to the current path. Add a straight line to the current path.
.PP .PP
@ -3140,6 +3435,8 @@ double pdfioContentTextMeasure (
This function measures the given text string "s" and returns its width based This function measures the given text string "s" and returns its width based
on "size". The text string must always use the UTF-8 (Unicode) encoding but on "size". The text string must always use the UTF-8 (Unicode) encoding but
any control characters (such as newlines) are ignored. any control characters (such as newlines) are ignored.
.SS pdfioContentTextMoveLine .SS pdfioContentTextMoveLine
Move to the next line and offset. Move to the next line and offset.
.PP .PP
@ -3168,6 +3465,8 @@ bool pdfioContentTextNewLine (
pdfio_stream_t *st pdfio_stream_t *st
); );
.fi .fi
.PP
.SS pdfioContentTextNewLineShow .SS pdfioContentTextNewLineShow
Move to the next line and show text. Move to the next line and show text.
.PP .PP
@ -3185,6 +3484,8 @@ 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 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 that the current font maps to full Unicode. The "s" argument
specifies a UTF-8 encoded string. specifies a UTF-8 encoded string.
.SS pdfioContentTextNewLineShowf .SS pdfioContentTextNewLineShowf
Show formatted text. Show formatted text.
.PP .PP
@ -3203,6 +3504,8 @@ 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" 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 that the current font maps to full Unicode. The "format"
argument specifies a UTF-8 encoded \fBprintf\fR-style format string. argument specifies a UTF-8 encoded \fBprintf\fR-style format string.
.SS pdfioContentTextShow .SS pdfioContentTextShow
Show text. Show text.
.PP .PP
@ -3253,6 +3556,8 @@ bool pdfioDictClear (
const char *key const char *key
); );
.fi .fi
.PP
.SS pdfioDictCopy .SS pdfioDictCopy
Copy a dictionary to a PDF file. Copy a dictionary to a PDF file.
.PP .PP
@ -3325,6 +3630,8 @@ const char * pdfioDictGetKey (
size_t n size_t n
); );
.fi .fi
.PP
.SS pdfioDictGetName .SS pdfioDictGetName
Get a key name value from a dictionary. Get a key name value from a dictionary.
.PP .PP
@ -3342,6 +3649,8 @@ size_t pdfioDictGetNumPairs (
pdfio_dict_t *dict pdfio_dict_t *dict
); );
.fi .fi
.PP
.SS pdfioDictGetNumber .SS pdfioDictGetNumber
Get a key number value from a dictionary. Get a key number value from a dictionary.
.PP .PP
@ -3414,6 +3723,8 @@ function "cb":
The iteration continues as long as the callback returns \fBtrue\fR or all keys The iteration continues as long as the callback returns \fBtrue\fR or all keys
have been iterated. have been iterated.
.SS pdfioDictSetArray .SS pdfioDictSetArray
Set a key array in a dictionary. Set a key array in a dictionary.
.PP .PP
@ -3535,6 +3846,39 @@ bool pdfioDictSetStringf (
... ...
); );
.fi .fi
.SS pdfioFileAddOutputIntent
Add an OutputIntent to a file.
.PP
.nf
void pdfioFileAddOutputIntent (
pdfio_file_t *pdf,
const char *subtype,
const char *condition,
const char *cond_id,
const char *reg_name,
const char *info,
pdfio_obj_t *profile
);
.fi
.PP
This function adds an OutputIntent dictionary to the PDF file catalog.
The "subtype" argument specifies the intent subtype and is typically
"GTS_PDFX" for PDF/X, "GTS_PDFA1" for PDF/A, or "ISO_PDFE1" for PDF/E.
Passing \fBNULL\fR defaults the subtype to "GTS_PDFA1".
.PP
The "condition" argument specifies a short name for the output intent, while
the "info" argument specifies a longer description for the output intent.
Both can be \fBNULL\fR to omit this information.
.PP
The "cond_id" argument specifies a unique identifier such as a registration
("CGATS001") or color space name ("sRGB"). The "reg_name" argument provides
a URL for the identifier.
.PP
The "profile" argument specifies an ICC profile object for the output
condition. If \fBNULL\fR, the PDF consumer will attempt to look up the correct
profile using the "cond_id" value.
.SS pdfioFileClose .SS pdfioFileClose
Close a PDF file and free all memory used for it. Close a PDF file and free all memory used for it.
.PP .PP
@ -3561,15 +3905,17 @@ This function creates a new PDF file. The "filename" argument specifies the
name of the PDF file to create. name of the PDF file to create.
.PP .PP
The "version" argument specifies the PDF version number for the file or The "version" argument specifies the PDF version number for the file or
\fBNULL\fR for the default ("2.0"). \fBNULL\fR for the default ("2.0"). The value "PCLm-1.0" can be specified to
produce the PCLm subset of PDF.
.PP .PP
The "media_box" and "crop_box" arguments specify the default MediaBox and 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 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. of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP .PP
The "error_cb" and "error_cbdata" 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 and its data pointer - if \fBNULL\fR then the default error handler is used that
writes error messages to \fBstderr\fR. writes error messages to \fBstderr\fR. The error handler callback should return
\fBtrue\fR to continue writing the PDF file or \fBfalse\fR to stop.
.SS pdfioFileCreateArrayObj .SS pdfioFileCreateArrayObj
Create a new object in a PDF file containing an array. Create a new object in a PDF file containing an array.
.PP .PP
@ -3627,8 +3973,27 @@ specifies the font nane:
Aside from "Symbol" and "Zapf-Dingbats", Base fonts use the Windows CP1252 Aside from "Symbol" and "Zapf-Dingbats", Base fonts use the Windows CP1252
(ISO-8859-1 with additional characters such as the Euro symbol) subset of (ISO-8859-1 with additional characters such as the Euro symbol) subset of
Unicode. Unicode.
.SS pdfioFileCreateFontObjFromData
Add a font in memory to a PDF file.
.PP
.nf
pdfio_obj_t * pdfioFileCreateFontObjFromData (
pdfio_file_t *pdf,
const void *data,
size_t datasize,
bool unicode
);
.fi
.PP
This function embeds TrueType/OpenType font data into a PDF file. The
"unicode" parameter controls whether the font is encoded for two-byte
characters (potentially full Unicode, but more typically a subset)
or to only support the Windows CP1252 (ISO-8859-1 with additional
characters such as the Euro symbol) subset of Unicode.
.SS pdfioFileCreateFontObjFromFile .SS pdfioFileCreateFontObjFromFile
Add a font object to a PDF file. Add a font file to a PDF file.
.PP .PP
.nf .nf
pdfio_obj_t * pdfioFileCreateFontObjFromFile ( pdfio_obj_t * pdfioFileCreateFontObjFromFile (
@ -3638,13 +4003,24 @@ pdfio_obj_t * pdfioFileCreateFontObjFromFile (
); );
.fi .fi
.PP .PP
This function embeds a TrueType/OpenType font into a PDF file. The This function embeds a TrueType/OpenType font file into a PDF file. The
"unicode" parameter controls whether the font is encoded for two-byte "unicode" parameter controls whether the font is encoded for two-byte
characters (potentially full Unicode, but more typically a subset) characters (potentially full Unicode, but more typically a subset)
or to only support the Windows CP1252 (ISO-8859-1 with additional or to only support the Windows CP1252 (ISO-8859-1 with additional
characters such as the Euro symbol) subset of Unicode. characters such as the Euro symbol) subset of Unicode.
.SS pdfioFileCreateICCObjFromData
Add ICC profile data to a PDF file.
.PP
.nf
pdfio_obj_t * pdfioFileCreateICCObjFromData (
pdfio_file_t *pdf,
const unsigned char *data,
size_t datalen,
size_t num_colors
);
.fi
.SS pdfioFileCreateICCObjFromFile .SS pdfioFileCreateICCObjFromFile
Add an ICC profile object to a PDF file. Add an ICC profile file to a PDF file.
.PP .PP
.nf .nf
pdfio_obj_t * pdfioFileCreateICCObjFromFile ( pdfio_obj_t * pdfioFileCreateICCObjFromFile (
@ -3716,6 +4092,8 @@ pdfio_obj_t * pdfioFileCreateNameObj (
.PP .PP
This function creates a new object with a name value in a PDF file. This function creates a new object with a name value in a PDF file.
You must call \fIpdfioObjClose\fR to write the object to the file. You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateNumberObj .SS pdfioFileCreateNumberObj
Create a new object in a PDF file containing a number. Create a new object in a PDF file containing a number.
.PP .PP
@ -3728,6 +4106,8 @@ pdfio_obj_t * pdfioFileCreateNumberObj (
.PP .PP
This function creates a new object with a number value in a PDF file. 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. You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateObj .SS pdfioFileCreateObj
Create a new object in a PDF file. Create a new object in a PDF file.
.PP .PP
@ -3767,15 +4147,18 @@ written:
.fi .fi
The "version" argument specifies the PDF version number for the file or The "version" argument specifies the PDF version number for the file or
\fBNULL\fR for the default ("2.0"). \fBNULL\fR for the default ("2.0"). Unlike \fIpdfioFileCreate\fR and
\fIpdfioFileCreateTemporary\fR, it is generally not safe to pass the
"PCLm-1.0" version string.
.PP .PP
The "media_box" and "crop_box" arguments specify the default MediaBox and 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 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. of 8.27x11in (the intersection of US Letter and ISO A4) is used.
.PP .PP
The "error_cb" and "error_cbdata" 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 and its data pointer - if \fBNULL\fR then the default error handler is used that
writes error messages to \fBstderr\fR. writes error messages to \fBstderr\fR. The error handler callback should return
\fBtrue\fR to continue writing the PDF file or \fBfalse\fR to stop.
.PP .PP
.IP 5 .IP 5
\fINote\fR: Files created using this API are slightly larger than those \fINote\fR: Files created using this API are slightly larger than those
@ -3804,6 +4187,8 @@ pdfio_obj_t * pdfioFileCreateStringObj (
.PP .PP
This function creates a new object with a string value in a PDF file. 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. You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateTemporary .SS pdfioFileCreateTemporary
.PP .PP
@ -3880,6 +4265,29 @@ const char * pdfioFileGetKeywords (
pdfio_file_t *pdf pdfio_file_t *pdf
); );
.fi .fi
.SS pdfioFileGetLanguage
Get the language metadata for a PDF file.
.PP
.nf
const char * pdfioFileGetLanguage (
pdfio_file_t *pdf
);
.fi
.PP
This function gets the (primary/default) language metadata, if any, for a PDF
file. The returned string is an IETF BCP 47 language tag of the form
"lang-REGION". For example, the string "en-CA" specifies Canadian English
and the string "fr-CA" specifies Canadian French.
.SS pdfioFileGetModificationDate
Get the most recent modification date for a PDF file.
.PP
.nf
time_t pdfioFileGetModificationDate (
pdfio_file_t *pdf
);
.fi
.SS pdfioFileGetName .SS pdfioFileGetName
Get a PDF's filename. Get a PDF's filename.
.PP .PP
@ -3989,8 +4397,18 @@ cancel the open. If \fBNULL\fR is specified for the callback function and the
PDF file requires a password, the open will always fail. PDF file requires a password, the open will always fail.
.PP .PP
The "error_cb" and "error_cbdata" 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 and its data pointer - if \fBNULL\fR then the default error handler is used that
writes error messages to \fBstderr\fR. writes error messages to \fBstderr\fR. The error handler callback should return
\fBtrue\fR to continue reading the PDF file or \fBfalse\fR to stop.
.PP
.IP 5
Note: Error messages starting with "WARNING:" are actually warning
.IP 5
messages - the callback should normally return \fBtrue\fR to allow PDFio to
.IP 5
try to resolve the issue. In addition, some errors are unrecoverable and
.IP 5
ignore the return value of the error callback.
.SS pdfioFileSetAuthor .SS pdfioFileSetAuthor
Set the author for a PDF file. Set the author for a PDF file.
.PP .PP
@ -4027,6 +4445,31 @@ void pdfioFileSetKeywords (
const char *value const char *value
); );
.fi .fi
.SS pdfioFileSetLanguage
Set the language metadata for a PDF file.
.PP
.nf
void pdfioFileSetLanguage (
pdfio_file_t *pdf,
const char *value
);
.fi
.PP
This function sets the (primary/default) language metadata for a PDF file.
The "value" argument is an IETF BCP 47 language tag string of the form
"lang-REGION". For example, the string "en-CA" specifies Canadian English
and the string "fr-CA" specifies Canadian French.
.SS pdfioFileSetModificationDate
Set the modification date for a PDF file.
.PP
.nf
void pdfioFileSetModificationDate (
pdfio_file_t *pdf,
time_t value
);
.fi
.SS pdfioFileSetPermissions .SS pdfioFileSetPermissions
Set the PDF permissions, encryption mode, and passwords. Set the PDF permissions, encryption mode, and passwords.
.PP .PP
@ -4160,6 +4603,8 @@ const char * pdfioObjGetName (
pdfio_obj_t *obj pdfio_obj_t *obj
); );
.fi .fi
.PP
.SS pdfioObjGetNumber .SS pdfioObjGetNumber
Get the object's number. Get the object's number.
.PP .PP
@ -4334,12 +4779,13 @@ bool pdfioStreamGetToken (
); );
.fi .fi
.PP .PP
This function reads a single PDF token from a stream. Operator tokens, This function reads a single PDF token from a stream, skipping all whitespace
boolean values, and numbers are returned as-is in the provided string buffer. and comments. Operator tokens, boolean values, and numbers are returned
String values start with the opening parenthesis ('(') but have all escaping as-is in the provided string buffer. String values start with the opening
resolved and the terminating parenthesis removed. Hexadecimal string values parenthesis ('(') but have all escaping resolved and the terminating
start with the opening angle bracket ('<') and have all whitespace and the parenthesis removed. Hexadecimal string values start with the opening angle
terminating angle bracket removed. bracket ('<') and have all whitespace and the terminating angle bracket
removed.
.SS pdfioStreamPeek .SS pdfioStreamPeek
Peek at data in a stream. Peek at data in a stream.
.PP .PP
@ -4360,6 +4806,11 @@ bool pdfioStreamPrintf (
... ...
); );
.fi .fi
.PP
This function writes a formatted string to a stream. In addition to the
standard \fBprintf\fR format characters, you can use "%H" to format a HTML/XML
string value, "%N" to format a PDF name value ("/Name"), and "%S" to format
a PDF string ("(String)") value.
.SS pdfioStreamPutChar .SS pdfioStreamPutChar
Write a single character to a stream. Write a single character to a stream.
.PP .PP

View File

@ -1,13 +1,13 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en-US"> <html lang="en-US">
<head> <head>
<title>PDFio Programming Manual v1.4.1</title> <title>PDFio Programming Manual v1.6.0</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta name="generator" content="codedoc v3.8"> <meta name="generator" content="codedoc v3.8">
<meta name="author" content="Michael R Sweet"> <meta name="author" content="Michael R Sweet">
<meta name="language" content="en-US"> <meta name="language" content="en-US">
<meta name="copyright" content="Copyright © 2021-2025 by Michael R Sweet"> <meta name="copyright" content="Copyright © 2021-2025 by Michael R Sweet">
<meta name="version" content="1.4.1"> <meta name="version" content="1.6.0">
<style type="text/css"><!-- <style type="text/css"><!--
body { body {
background: white; background: white;
@ -251,7 +251,7 @@ span.string {
<body> <body>
<div class="header"> <div class="header">
<p><img class="title" src="pdfio-512.png"></p> <p><img class="title" src="pdfio-512.png"></p>
<h1 class="title">PDFio Programming Manual v1.4.1</h1> <h1 class="title">PDFio Programming Manual v1.6.0</h1>
<p>Michael R Sweet</p> <p>Michael R Sweet</p>
<p>Copyright © 2021-2025 by Michael R Sweet</p> <p>Copyright © 2021-2025 by Michael R Sweet</p>
</div> </div>
@ -395,11 +395,14 @@ span.string {
<li><a href="#pdfioDictSetRect">pdfioDictSetRect</a></li> <li><a href="#pdfioDictSetRect">pdfioDictSetRect</a></li>
<li><a href="#pdfioDictSetString">pdfioDictSetString</a></li> <li><a href="#pdfioDictSetString">pdfioDictSetString</a></li>
<li><a href="#pdfioDictSetStringf">pdfioDictSetStringf</a></li> <li><a href="#pdfioDictSetStringf">pdfioDictSetStringf</a></li>
<li><a href="#pdfioFileAddOutputIntent">pdfioFileAddOutputIntent</a></li>
<li><a href="#pdfioFileClose">pdfioFileClose</a></li> <li><a href="#pdfioFileClose">pdfioFileClose</a></li>
<li><a href="#pdfioFileCreate">pdfioFileCreate</a></li> <li><a href="#pdfioFileCreate">pdfioFileCreate</a></li>
<li><a href="#pdfioFileCreateArrayObj">pdfioFileCreateArrayObj</a></li> <li><a href="#pdfioFileCreateArrayObj">pdfioFileCreateArrayObj</a></li>
<li><a href="#pdfioFileCreateFontObjFromBase">pdfioFileCreateFontObjFromBase</a></li> <li><a href="#pdfioFileCreateFontObjFromBase">pdfioFileCreateFontObjFromBase</a></li>
<li><a href="#pdfioFileCreateFontObjFromData">pdfioFileCreateFontObjFromData</a></li>
<li><a href="#pdfioFileCreateFontObjFromFile">pdfioFileCreateFontObjFromFile</a></li> <li><a href="#pdfioFileCreateFontObjFromFile">pdfioFileCreateFontObjFromFile</a></li>
<li><a href="#pdfioFileCreateICCObjFromData">pdfioFileCreateICCObjFromData</a></li>
<li><a href="#pdfioFileCreateICCObjFromFile">pdfioFileCreateICCObjFromFile</a></li> <li><a href="#pdfioFileCreateICCObjFromFile">pdfioFileCreateICCObjFromFile</a></li>
<li><a href="#pdfioFileCreateImageObjFromData">pdfioFileCreateImageObjFromData</a></li> <li><a href="#pdfioFileCreateImageObjFromData">pdfioFileCreateImageObjFromData</a></li>
<li><a href="#pdfioFileCreateImageObjFromFile">pdfioFileCreateImageObjFromFile</a></li> <li><a href="#pdfioFileCreateImageObjFromFile">pdfioFileCreateImageObjFromFile</a></li>
@ -417,6 +420,8 @@ span.string {
<li><a href="#pdfioFileGetCreator">pdfioFileGetCreator</a></li> <li><a href="#pdfioFileGetCreator">pdfioFileGetCreator</a></li>
<li><a href="#pdfioFileGetID">pdfioFileGetID</a></li> <li><a href="#pdfioFileGetID">pdfioFileGetID</a></li>
<li><a href="#pdfioFileGetKeywords">pdfioFileGetKeywords</a></li> <li><a href="#pdfioFileGetKeywords">pdfioFileGetKeywords</a></li>
<li><a href="#pdfioFileGetLanguage">pdfioFileGetLanguage</a></li>
<li><a href="#pdfioFileGetModificationDate">pdfioFileGetModificationDate</a></li>
<li><a href="#pdfioFileGetName">pdfioFileGetName</a></li> <li><a href="#pdfioFileGetName">pdfioFileGetName</a></li>
<li><a href="#pdfioFileGetNumObjs">pdfioFileGetNumObjs</a></li> <li><a href="#pdfioFileGetNumObjs">pdfioFileGetNumObjs</a></li>
<li><a href="#pdfioFileGetNumPages">pdfioFileGetNumPages</a></li> <li><a href="#pdfioFileGetNumPages">pdfioFileGetNumPages</a></li>
@ -432,6 +437,8 @@ span.string {
<li><a href="#pdfioFileSetCreationDate">pdfioFileSetCreationDate</a></li> <li><a href="#pdfioFileSetCreationDate">pdfioFileSetCreationDate</a></li>
<li><a href="#pdfioFileSetCreator">pdfioFileSetCreator</a></li> <li><a href="#pdfioFileSetCreator">pdfioFileSetCreator</a></li>
<li><a href="#pdfioFileSetKeywords">pdfioFileSetKeywords</a></li> <li><a href="#pdfioFileSetKeywords">pdfioFileSetKeywords</a></li>
<li><a href="#pdfioFileSetLanguage">pdfioFileSetLanguage</a></li>
<li><a href="#pdfioFileSetModificationDate">pdfioFileSetModificationDate</a></li>
<li><a href="#pdfioFileSetPermissions">pdfioFileSetPermissions</a></li> <li><a href="#pdfioFileSetPermissions">pdfioFileSetPermissions</a></li>
<li><a href="#pdfioFileSetSubject">pdfioFileSetSubject</a></li> <li><a href="#pdfioFileSetSubject">pdfioFileSetSubject</a></li>
<li><a href="#pdfioFileSetTitle">pdfioFileSetTitle</a></li> <li><a href="#pdfioFileSetTitle">pdfioFileSetTitle</a></li>
@ -522,7 +529,7 @@ span.string {
</li> </li>
</ul> </ul>
<p>PDFio is <em>not</em> concerned with rendering or viewing a PDF file, although a PDF RIP or viewer could be written using it.</p> <p>PDFio is <em>not</em> concerned with rendering or viewing a PDF file, although a PDF RIP or viewer could be written using it.</p>
<p>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 &quot;LICENSE&quot; and &quot;NOTICE&quot; for more information.</p> <p>PDFio is Copyright © 2021-2025 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 &quot;LICENSE&quot; and &quot;NOTICE&quot; for more information.</p>
<h3 class="title" id="requirements">Requirements</h3> <h3 class="title" id="requirements">Requirements</h3>
<p>PDFio requires the following to build the software:</p> <p>PDFio requires the following to build the software:</p>
<ul> <ul>
@ -532,9 +539,10 @@ span.string {
</li> </li>
<li><p>A POSIX-compliant <code>sh</code> program</p> <li><p>A POSIX-compliant <code>sh</code> program</p>
</li> </li>
<li><p>ZLIB (<a href="https://www.zlib.net">https://www.zlib.net</a>) 1.0 or higher</p> <li><p>ZLIB (<a href="https://www.zlib.net/">https://www.zlib.net/</a>) 1.0 or higher</p>
</li> </li>
</ul> </ul>
<p>PDFio will also use libpng 1.6 or higher (<a href="https://www.libpng.org/">https://www.libpng.org/</a>) to provide enhanced PNG image support.</p>
<p>IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.</p> <p>IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.</p>
<h3 class="title" id="installing-pdfio">Installing PDFio</h3> <h3 class="title" id="installing-pdfio">Installing PDFio</h3>
<p>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:</p> <p>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:</p>
@ -724,7 +732,7 @@ password_cb(<span class="reserved">void</span> *data, <span class="reserved">con
<span class="reserved">return</span> (<span class="string">&quot;Password42&quot;</span>); <span class="reserved">return</span> (<span class="string">&quot;Password42&quot;</span>);
} }
</code></pre> </code></pre>
<p>The error callback is called for both errors and warnings and accepts the <code>pdfio_file_t</code> pointer, a message string, and the callback pointer value, for example:</p> <p>The error callback is called for both errors and warnings and accepts the <code>pdfio_file_t</code> pointer, a message string, and the callback pointer value. It returns <code>true</code> to continue processing the file or <code>false</code> to stop, for example:</p>
<pre><code class="language-c"><span class="reserved">bool</span> <pre><code class="language-c"><span class="reserved">bool</span>
error_cb(pdfio_file_t *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *message, <span class="reserved">void</span> *data) error_cb(pdfio_file_t *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *message, <span class="reserved">void</span> *data)
{ {
@ -732,11 +740,14 @@ error_cb(pdfio_file_t *pdf, <span class="reserved">const</span> <span class="res
fprintf(stderr, <span class="string">&quot;%s: %s\n&quot;</span>, pdfioFileGetName(pdf), message); fprintf(stderr, <span class="string">&quot;%s: %s\n&quot;</span>, pdfioFileGetName(pdf), message);
<span class="comment">// Return false to treat warnings as errors</span> <span class="comment">// Return true for warning messages (continue) and false for errors (stop)</span>
<span class="reserved">return</span> (<span class="reserved">false</span>); <span class="reserved">return</span> (!strncmp(message, <span class="string">&quot;WARNING:&quot;</span>, <span class="number">8</span>));
} }
</code></pre> </code></pre>
<p>The default error callback (<code>NULL</code>) does the equivalent of the above.</p> <p>The default error callback (<code>NULL</code>) does the equivalent of the above.</p>
<blockquote>
<p>Note: Many errors are unrecoverable, so PDFio ignores the return value from the error callback and always stops processing the PDF file. Warning messages start with the prefix &quot;WARNING:&quot; while errors have no prefix.</p>
</blockquote>
<p>Each PDF file contains one or more pages. The <a href="#pdfioFileGetNumPages"><code>pdfioFileGetNumPages</code></a> function returns the number of pages in the file while the <a href="#pdfioFileGetPage"><code>pdfioFileGetPage</code></a> function gets the specified page in the PDF file:</p> <p>Each PDF file contains one or more pages. The <a href="#pdfioFileGetNumPages"><code>pdfioFileGetNumPages</code></a> function returns the number of pages in the file while the <a href="#pdfioFileGetPage"><code>pdfioFileGetPage</code></a> function gets the specified page in the PDF file:</p>
<pre><code class="language-c">pdfio_file_t *pdf; <span class="comment">// PDF file</span> <pre><code class="language-c">pdfio_file_t *pdf; <span class="comment">// PDF file</span>
size_t i; <span class="comment">// Looping var</span> size_t i; <span class="comment">// Looping var</span>
@ -1161,11 +1172,26 @@ main(<span class="reserved">int</span> argc, <span clas
{ {
<span class="reserved">const</span> <span class="reserved">char</span> *filename; <span class="comment">// PDF filename</span> <span class="reserved">const</span> <span class="reserved">char</span> *filename; <span class="comment">// PDF filename</span>
pdfio_file_t *pdf; <span class="comment">// PDF file</span> pdfio_file_t *pdf; <span class="comment">// PDF file</span>
<span class="reserved">const</span> <span class="reserved">char</span> *author; <span class="comment">// Author name</span> pdfio_dict_t *catalog; <span class="comment">// Catalog dictionary</span>
time_t creation_date; <span class="comment">// Creation date</span> <span class="reserved">const</span> <span class="reserved">char</span> *author, <span class="comment">// Author name</span>
<span class="reserved">struct</span> tm *creation_tm; <span class="comment">// Creation date/time information</span> *creator, <span class="comment">// Creator name</span>
<span class="reserved">char</span> creation_text[<span class="number">256</span>]; <span class="comment">// Creation date/time as a string</span> *producer, <span class="comment">// Producer name</span>
<span class="reserved">const</span> <span class="reserved">char</span> *title; <span class="comment">// Title</span> *title; <span class="comment">// Title</span>
time_t creation_date, <span class="comment">// Creation date</span>
modification_date; <span class="comment">// Modification date</span>
<span class="reserved">struct</span> tm *creation_tm, <span class="comment">// Creation date/time information</span>
*modification_tm; <span class="comment">// Modification date/time information</span>
<span class="reserved">char</span> creation_text[<span class="number">256</span>], <span class="comment">// Creation date/time as a string</span>
modification_text[<span class="number">256</span>], <span class="comment">// Modification date/time human fmt string</span>
range_text[<span class="number">255</span>]; <span class="comment">// Page range text</span>
size_t num_pages; <span class="comment">// PDF number of pages</span>
<span class="reserved">bool</span> has_acroform; <span class="comment">// Does the file have an AcroForm?</span>
pdfio_obj_t *page; <span class="comment">// Object</span>
pdfio_dict_t *page_dict; <span class="comment">// Object dictionary</span>
size_t cur, <span class="comment">// Current page index</span>
prev; <span class="comment">// Previous page index</span>
pdfio_rect_t cur_box, <span class="comment">// Current MediaBox</span>
prev_box; <span class="comment">// Previous MediaBox</span>
<span class="comment">// Get the filename from the command-line...</span> <span class="comment">// Get the filename from the command-line...</span>
@ -1178,14 +1204,20 @@ main(<span class="reserved">int</span> argc, <span clas
filename = argv[<span class="number">1</span>]; filename = argv[<span class="number">1</span>];
<span class="comment">// Open the PDF file with the default callbacks...</span> <span class="comment">// Open the PDF file with the default callbacks...</span>
pdf = pdfioFileOpen(filename, <span class="comment">/*password_cb*/</span>NULL, <span class="comment">/*password_cbdata*/</span>NULL, pdf = pdfioFileOpen(filename, <span class="comment">/*password_cb*/</span>NULL,
<span class="comment">/*error_cb*/</span>NULL, <span class="comment">/*error_cbdata*/</span>NULL); <span class="comment">/*password_cbdata*/</span>NULL, <span class="comment">/*error_cb*/</span>NULL,
<span class="comment">/*error_cbdata*/</span>NULL);
<span class="reserved">if</span> (pdf == NULL) <span class="reserved">if</span> (pdf == NULL)
<span class="reserved">return</span> (<span class="number">1</span>); <span class="reserved">return</span> (<span class="number">1</span>);
<span class="comment">// Get the title and author...</span> <span class="comment">// Get the title, author, etc...</span>
author = pdfioFileGetAuthor(pdf); catalog = pdfioFileGetCatalog(pdf);
title = pdfioFileGetTitle(pdf); author = pdfioFileGetAuthor(pdf);
creator = pdfioFileGetCreator(pdf);
has_acroform = pdfioDictGetType(catalog, <span class="string">&quot;AcroForm&quot;</span>) != PDFIO_VALTYPE_NONE;
num_pages = pdfioFileGetNumPages(pdf);
producer = pdfioFileGetProducer(pdf);
title = pdfioFileGetTitle(pdf);
<span class="comment">// Get the creation date and convert to a string...</span> <span class="comment">// Get the creation date and convert to a string...</span>
<span class="reserved">if</span> ((creation_date = pdfioFileGetCreationDate(pdf)) &gt; <span class="number">0</span>) <span class="reserved">if</span> ((creation_date = pdfioFileGetCreationDate(pdf)) &gt; <span class="number">0</span>)
@ -1198,12 +1230,76 @@ main(<span class="reserved">int</span> argc, <span clas
snprintf(creation_text, <span class="reserved">sizeof</span>(creation_text), <span class="string">&quot;-- not set --&quot;</span>); snprintf(creation_text, <span class="reserved">sizeof</span>(creation_text), <span class="string">&quot;-- not set --&quot;</span>);
} }
<span class="comment">// Get the modification date and convert to a string...</span>
<span class="reserved">if</span> ((modification_date = pdfioFileGetModificationDate(pdf)) &gt; <span class="number">0</span>)
{
modification_tm = localtime(&amp;modification_date);
strftime(modification_text, <span class="reserved">sizeof</span>(modification_text), <span class="string">&quot;%c&quot;</span>, modification_tm);
}
<span class="reserved">else</span>
{
snprintf(modification_text, <span class="reserved">sizeof</span>(modification_text), <span class="string">&quot;-- not set --&quot;</span>);
}
<span class="comment">// Print file information to stdout...</span> <span class="comment">// Print file information to stdout...</span>
printf(<span class="string">&quot;%s:\n&quot;</span>, filename); printf(<span class="string">&quot;%s:\n&quot;</span>, filename);
printf(<span class="string">&quot; Title: %s\n&quot;</span>, title ? title : <span class="string">&quot;-- not set --&quot;</span>); printf(<span class="string">&quot; Title: %s\n&quot;</span>, title ? title : <span class="string">&quot;-- not set --&quot;</span>);
printf(<span class="string">&quot; Author: %s\n&quot;</span>, author ? author : <span class="string">&quot;-- not set --&quot;</span>); printf(<span class="string">&quot; Author: %s\n&quot;</span>, author ? author : <span class="string">&quot;-- not set --&quot;</span>);
printf(<span class="string">&quot; Created On: %s\n&quot;</span>, creation_text); printf(<span class="string">&quot; Creator: %s\n&quot;</span>, creator ? creator : <span class="string">&quot;-- not set --&quot;</span>);
printf(<span class="string">&quot; Number Pages: %u\n&quot;</span>, (<span class="reserved">unsigned</span>)pdfioFileGetNumPages(pdf)); printf(<span class="string">&quot; Producer: %s\n&quot;</span>, producer ? producer : <span class="string">&quot;-- not set --&quot;</span>);
printf(<span class="string">&quot; Created On: %s\n&quot;</span>, creation_text);
printf(<span class="string">&quot; Modified On: %s\n&quot;</span>, modification_text);
printf(<span class="string">&quot; Version: %s\n&quot;</span>, pdfioFileGetVersion(pdf));
printf(<span class="string">&quot; AcroForm: %s\n&quot;</span>, has_acroform ? <span class="string">&quot;Yes&quot;</span> : <span class="string">&quot;No&quot;</span>);
printf(<span class="string">&quot; Number of Pages: %u\n&quot;</span>, (<span class="reserved">unsigned</span>)num_pages);
<span class="comment">// Report the MediaBox for all of the pages</span>
prev_box.x1 = prev_box.x2 = prev_box.y1 = prev_box.y2 = <span class="number">0.0</span>;
<span class="reserved">for</span> (cur = <span class="number">0</span>, prev = <span class="number">0</span>; cur &lt; num_pages; cur ++)
{
<span class="comment">// Find the MediaBox for this page in the page tree...</span>
<span class="reserved">for</span> (page = pdfioFileGetPage(pdf, cur);
page != NULL;
page = pdfioDictGetObj(page_dict, <span class="string">&quot;Parent&quot;</span>))
{
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = <span class="number">0.0</span>;
page_dict = pdfioObjGetDict(page);
<span class="reserved">if</span> (pdfioDictGetRect(page_dict, <span class="string">&quot;MediaBox&quot;</span>, &amp;cur_box))
<span class="reserved">break</span>;
}
<span class="comment">// If this MediaBox is different from the previous one, show the range of</span>
<span class="comment">// pages that have that size...</span>
<span class="reserved">if</span> (cur == <span class="number">0</span> ||
fabs(cur_box.x1 - prev_box.x1) &gt; <span class="number">0.01</span> ||
fabs(cur_box.y1 - prev_box.y1) &gt; <span class="number">0.01</span> ||
fabs(cur_box.x2 - prev_box.x2) &gt; <span class="number">0.01</span> ||
fabs(cur_box.y2 - prev_box.y2) &gt; <span class="number">0.01</span>)
{
<span class="reserved">if</span> (cur &gt; prev)
{
snprintf(range_text, <span class="reserved">sizeof</span>(range_text), <span class="string">&quot;Pages %u-%u&quot;</span>,
(<span class="reserved">unsigned</span>)(prev + <span class="number">1</span>), (<span class="reserved">unsigned</span>)cur);
printf(<span class="string">&quot;%16s: [%g %g %g %g]\n&quot;</span>, range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
<span class="comment">// Start a new series of pages with the new size...</span>
prev = cur;
prev_box = cur_box;
}
}
<span class="comment">// Show the last range as needed...</span>
<span class="reserved">if</span> (cur &gt; prev)
{
snprintf(range_text, <span class="reserved">sizeof</span>(range_text), <span class="string">&quot;Pages %u-%u&quot;</span>,
(<span class="reserved">unsigned</span>)(prev + <span class="number">1</span>), (<span class="reserved">unsigned</span>)cur);
printf(<span class="string">&quot;%16s: [%g %g %g %g]\n&quot;</span>, range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
<span class="comment">// Close the PDF file...</span> <span class="comment">// Close the PDF file...</span>
pdfioFileClose(pdf); pdfioFileClose(pdf);
@ -1212,26 +1308,69 @@ main(<span class="reserved">int</span> argc, <span clas
} }
</code></pre> </code></pre>
<h3 class="title" id="extract-text-from-pdf-file">Extract Text from PDF File</h3> <h3 class="title" id="extract-text-from-pdf-file">Extract Text from PDF File</h3>
<p>The <code>pdf2text.c</code> example code extracts non-Unicode text from a PDF file by scanning each page for strings and text drawing commands. Since it doesn't look at the font encoding or support Unicode text, it is really only useful to extract plain ASCII text from a PDF file. And since it writes text in the order it appears in the page stream, it may not come out in the same order as appears on the page.</p> <p>The <code>pdf2text.c</code> example code extracts text from a PDF file and writes it to the standard output. Unlike some other PDF tools, it outputs the text in the order it is seen in each page stream so the output might appear &quot;jumbled&quot; if the PDF producer doesn't output text in reading order. The code is able to handle different font encodings and produces UTF-8 output.</p>
<p>The <a href="#pdfioStreamGetToken"><code>pdfioStreamGetToken</code></a> function is used to read individual tokens from the page streams. Tokens starting with the open parenthesis are text strings, while PDF operators are left as-is. We use some simple logic to make sure that we include spaces between text strings and add newlines for the text operators that start a new line in a text block:</p> <p>The <a href="#pdfioStreamGetToken"><code>pdfioStreamGetToken</code></a> function is used to read individual tokens from the page streams:</p>
<pre><code class="language-c">pdfio_stream_t *st; <span class="comment">// Page stream</span> <pre><code class="language-c">pdfio_stream_t *st; <span class="comment">// Page stream</span>
<span class="reserved">char</span> buffer[<span class="number">1024</span>], <span class="comment">// Token buffer</span>
*bufptr, <span class="comment">// Pointer into buffer</span>
name[<span class="number">256</span>]; <span class="comment">// Current (font) name</span>
<span class="reserved">bool</span> first = <span class="reserved">true</span>; <span class="comment">// First string on line?</span> <span class="reserved">bool</span> first = <span class="reserved">true</span>; <span class="comment">// First string on line?</span>
<span class="reserved">char</span> buffer[<span class="number">1024</span>]; <span class="comment">// Token buffer</span> <span class="reserved">int</span> encoding[<span class="number">256</span>]; <span class="comment">// Font encoding to Unicode</span>
<span class="reserved">bool</span> in_array = <span class="reserved">false</span>; <span class="comment">// Are we in an array?</span>
<span class="comment">// Read PDF tokens from the page stream...</span> <span class="comment">// Read PDF tokens from the page stream...</span>
<span class="reserved">while</span> (pdfioStreamGetToken(st, buffer, <span class="reserved">sizeof</span>(buffer))) <span class="reserved">while</span> (pdfioStreamGetToken(st, buffer, <span class="reserved">sizeof</span>(buffer)))
{ {
<span class="reserved">if</span> (buffer[<span class="number">0</span>] == <span class="string">'('</span>) </code></pre>
<p>Justified text can be found inside arrays (&quot;[ ... ]&quot;), so we look for the array delimiter tokens and any (spacing) numbers inside an array. Experimentation has shown that numbers greater than 100 can be treated as whitespace:</p>
<pre><code class="language-c"> <span class="reserved">if</span> (!strcmp(buffer, <span class="string">&quot;[&quot;</span>))
{
<span class="comment">// Start of an array for justified text...</span>
in_array = <span class="reserved">true</span>;
}
<span class="reserved">else</span> <span class="reserved">if</span> (!strcmp(buffer, <span class="string">&quot;]&quot;</span>))
{
<span class="comment">// End of an array for justified text...</span>
in_array = <span class="reserved">false</span>;
}
<span class="reserved">else</span> <span class="reserved">if</span> (!first &amp;&amp; in_array &amp;&amp; (isdigit(buffer[<span class="number">0</span>]) || buffer[<span class="number">0</span>] == <span class="string">'-'</span>) &amp;&amp; fabs(atof(buffer)) &gt; <span class="number">100</span>)
{
<span class="comment">// Whitespace in a justified text block...</span>
putchar(<span class="string">' '</span>);
}
</code></pre>
<p>Tokens starting with '(' or '&lt;' are text fragments. 8-bit text starting with '(' needs to be mapped to Unicode using the current font encoding while hex strings starting with '&lt;' are UTF-16 (Unicode) that need to be converted to UTF-8:</p>
<pre><code class="language-c"> <span class="reserved">else</span> <span class="reserved">if</span> (buffer[<span class="number">0</span>] == <span class="string">'('</span>)
{ {
<span class="comment">// Text string using an 8-bit encoding</span> <span class="comment">// Text string using an 8-bit encoding</span>
<span class="reserved">if</span> (first) first = <span class="reserved">false</span>;
first = <span class="reserved">false</span>;
<span class="reserved">else</span> <span class="reserved">if</span> (buffer[<span class="number">1</span>] != <span class="string">' '</span>)
putchar(<span class="string">' '</span>);
fputs(buffer + <span class="number">1</span>, stdout); <span class="reserved">for</span> (bufptr = buffer + <span class="number">1</span>; *bufptr; bufptr ++)
put_utf8(encoding[*bufptr &amp; <span class="number">255</span>]);
} }
<span class="reserved">else</span> <span class="reserved">if</span> (!strcmp(buffer, <span class="string">&quot;Td&quot;</span>) || !strcmp(buffer, <span class="string">&quot;TD&quot;</span>) || !strcmp(buffer, <span class="string">&quot;T*&quot;</span>) || <span class="reserved">else</span> <span class="reserved">if</span> (buffer[<span class="number">0</span>] == <span class="string">'&lt;'</span>)
{
<span class="comment">// Unicode text string</span>
first = <span class="reserved">false</span>;
puts_utf16(buffer + <span class="number">1</span>);
}
</code></pre>
<p>Simple (8-bit) fonts include an encoding table that maps the 8-bit characters to one of 1051 Unicode glyph names. Since each font can use a different encoding, we look for font names starting with '/' and the &quot;Tf&quot; (set text font) operator token and load that font's encoding using the <a href="#the-loadencoding-function">load_encoding</a> function:</p>
<pre><code class="language-c"> <span class="reserved">else</span> <span class="reserved">if</span> (buffer[<span class="number">0</span>] == <span class="string">'/'</span>)
{
<span class="comment">// Save name...</span>
strncpy(name, buffer + <span class="number">1</span>, <span class="reserved">sizeof</span>(name) - <span class="number">1</span>);
name[<span class="reserved">sizeof</span>(name) - <span class="number">1</span>] = <span class="string">'\0'</span>;
}
<span class="reserved">else</span> <span class="reserved">if</span> (!strcmp(buffer, <span class="string">&quot;Tf&quot;</span>) &amp;&amp; name[<span class="number">0</span>])
{
<span class="comment">// Set font...</span>
load_encoding(obj, name, encoding);
}
</code></pre>
<p>Finally, some text operators start a new line in a text block, so when we see their tokens we output a newline:</p>
<pre><code class="language-c"> <span class="reserved">else</span> <span class="reserved">if</span> (!strcmp(buffer, <span class="string">&quot;Td&quot;</span>) || !strcmp(buffer, <span class="string">&quot;TD&quot;</span>) || !strcmp(buffer, <span class="string">&quot;T*&quot;</span>) ||
!strcmp(buffer, <span class="string">&quot;\'&quot;</span>) || !strcmp(buffer, <span class="string">&quot;\&quot;&quot;</span>)) !strcmp(buffer, <span class="string">&quot;\'&quot;</span>) || !strcmp(buffer, <span class="string">&quot;\&quot;&quot;</span>))
{ {
<span class="comment">// Text operators that advance to the next line in the block</span> <span class="comment">// Text operators that advance to the next line in the block</span>
@ -1239,9 +1378,133 @@ main(<span class="reserved">int</span> argc, <span clas
first = <span class="reserved">true</span>; first = <span class="reserved">true</span>;
} }
} }
</code></pre>
<h4 id="the-loadencoding-function">The <code>load_encoding</code> Function</h4>
<p>The <code>load_encoding</code> function looks up the named font in the page's &quot;Resources&quot; dictionary. Every PDF simple font contains an &quot;Encoding&quot; dictionary with a base encoding (&quot;WinANSI&quot;, &quot;MacRoman&quot;, or &quot;MacExpert&quot;) and a differences array that lists character indexes and glyph names for an 8-bit font.</p>
<p>We start by initializing the encoding array to the default WinANSI encoding and looking up the font object for the named font:</p>
<pre><code class="language-c"><span class="reserved">static</span> <span class="reserved">void</span>
load_encoding(
pdfio_obj_t *page_obj, <span class="comment">// I - Page object</span>
<span class="reserved">const</span> <span class="reserved">char</span> *name, <span class="comment">// I - Font name</span>
<span class="reserved">int</span> encoding[<span class="number">256</span>]) <span class="comment">// O - Encoding table</span>
{
size_t i, j; <span class="comment">// Looping vars</span>
pdfio_dict_t *page_dict, <span class="comment">// Page dictionary</span>
*resources_dict, <span class="comment">// Resources dictionary</span>
*font_dict; <span class="comment">// Font dictionary</span>
pdfio_obj_t *font_obj, <span class="comment">// Font object</span>
*encoding_obj; <span class="comment">// Encoding object</span>
<span class="reserved">static</span> <span class="reserved">int</span> win_ansi[<span class="number">32</span>] = <span class="comment">// WinANSI characters from 128 to 159</span>
{
...
};
<span class="reserved">static</span> <span class="reserved">int</span> mac_roman[<span class="number">128</span>] = <span class="comment">// MacRoman characters from 128 to 255</span>
{
...
};
<span class="reserved">if</span> (!first)
putchar(<span class="string">'\n'</span>); <span class="comment">// Initialize the encoding to be the &quot;standard&quot; WinAnsi...</span>
<span class="reserved">for</span> (i = <span class="number">0</span>; i &lt; <span class="number">128</span>; i ++)
encoding[i] = i;
<span class="reserved">for</span> (i = <span class="number">160</span>; i &lt; <span class="number">256</span>; i ++)
encoding[i] = i;
memcpy(encoding + <span class="number">128</span>, win_ansi, <span class="reserved">sizeof</span>(win_ansi));
<span class="comment">// Find the named font...</span>
<span class="reserved">if</span> ((page_dict = pdfioObjGetDict(page_obj)) == NULL)
<span class="reserved">return</span>;
<span class="reserved">if</span> ((resources_dict = pdfioDictGetDict(page_dict, <span class="string">&quot;Resources&quot;</span>)) == NULL)
<span class="reserved">return</span>;
<span class="reserved">if</span> ((font_dict = pdfioDictGetDict(resources_dict, <span class="string">&quot;Font&quot;</span>)) == NULL)
{
<span class="comment">// Font resources not a dictionary, see if it is an object...</span>
<span class="reserved">if</span> ((font_obj = pdfioDictGetObj(resources_dict, <span class="string">&quot;Font&quot;</span>)) != NULL)
font_dict = pdfioObjGetDict(font_obj);
<span class="reserved">if</span> (!font_dict)
<span class="reserved">return</span>;
}
<span class="reserved">if</span> ((font_obj = pdfioDictGetObj(font_dict, name)) == NULL)
<span class="reserved">return</span>;
</code></pre>
<p>Once we have found the font we see if it has an &quot;Encoding&quot; dictionary:</p>
<pre><code class="language-c"> pdfio_dict_t *encoding_dict; <span class="comment">// Encoding dictionary</span>
<span class="reserved">if</span> ((encoding_obj = pdfioDictGetObj(pdfioObjGetDict(font_obj), <span class="string">&quot;Encoding&quot;</span>)) == NULL)
<span class="reserved">return</span>;
<span class="reserved">if</span> ((encoding_dict = pdfioObjGetDict(encoding_obj)) == NULL)
<span class="reserved">return</span>;
</code></pre>
<p>Once we have the encoding dictionary we can get the &quot;BaseEncoding&quot; and &quot;Differences&quot; values:</p>
<pre><code class="language-c"> <span class="reserved">const</span> <span class="reserved">char</span> *base_encoding; <span class="comment">// BaseEncoding name</span>
pdfio_array_t *differences; <span class="comment">// Differences array</span>
<span class="comment">// OK, have the encoding object, build the encoding using it...</span>
base_encoding = pdfioDictGetName(encoding_dict, <span class="string">&quot;BaseEncoding&quot;</span>);
differences = pdfioDictGetArray(encoding_dict, <span class="string">&quot;Differences&quot;</span>);
</code></pre>
<p>If the base encoding is &quot;MacRomainEncoding&quot;, we need to reset the upper 128 characters in the encoding array match it:</p>
<pre><code class="language-c"> <span class="reserved">if</span> (base_encoding &amp;&amp; !strcmp(base_encoding, <span class="string">&quot;MacRomanEncoding&quot;</span>))
{
<span class="comment">// Map upper 128</span>
memcpy(encoding + <span class="number">128</span>, mac_roman, <span class="reserved">sizeof</span>(mac_roman));
}
</code></pre>
<p>Then we loop through the differences array, keeping track of the current index within the encoding array. A number indicates a new index while a name is the Unicode glyph for the current index:</p>
<pre><code class="language-c"> <span class="reserved">typedef</span> <span class="reserved">struct</span> name_map_s
{
<span class="reserved">const</span> <span class="reserved">char</span> *name; <span class="comment">// Character name</span>
<span class="reserved">int</span> unicode; <span class="comment">// Unicode value</span>
} name_map_t;
<span class="reserved">static</span> name_map_t unicode_map[<span class="number">1051</span>]; <span class="comment">// List of glyph names</span>
<span class="reserved">if</span> (differences)
{
<span class="comment">// Apply differences</span>
size_t count = pdfioArrayGetSize(differences);
<span class="comment">// Number of differences</span>
<span class="reserved">const</span> <span class="reserved">char</span> *name; <span class="comment">// Character name</span>
size_t idx = <span class="number">0</span>; <span class="comment">// Index in encoding array</span>
<span class="reserved">for</span> (i = <span class="number">0</span>; i &lt; count; i ++)
{
<span class="reserved">switch</span> (pdfioArrayGetType(differences, i))
{
<span class="reserved">case</span> PDFIO_VALTYPE_NUMBER :
<span class="comment">// Get the index of the next character...</span>
idx = (size_t)pdfioArrayGetNumber(differences, i);
<span class="reserved">break</span>;
<span class="reserved">case</span> PDFIO_VALTYPE_NAME :
<span class="comment">// Lookup name and apply to encoding...</span>
<span class="reserved">if</span> (idx &lt; <span class="number">0</span> || idx &gt; <span class="number">255</span>)
<span class="reserved">break</span>;
name = pdfioArrayGetName(differences, i);
<span class="reserved">for</span> (j = <span class="number">0</span>; j &lt; (<span class="reserved">sizeof</span>(unicode_map) / <span class="reserved">sizeof</span>(unicode_map[<span class="number">0</span>])); j ++)
{
<span class="reserved">if</span> (!strcmp(name, unicode_map[j].name))
{
encoding[idx] = unicode_map[j].unicode;
<span class="reserved">break</span>;
}
}
idx ++;
<span class="reserved">break</span>;
<span class="reserved">default</span> :
<span class="comment">// Do nothing for other values</span>
<span class="reserved">break</span>;
}
}
}
}
</code></pre> </code></pre>
<h3 class="title" id="create-a-pdf-file-with-text-and-an-image">Create a PDF File With Text and an Image</h3> <h3 class="title" id="create-a-pdf-file-with-text-and-an-image">Create a PDF File With Text and an Image</h3>
<p>The <code>image2pdf.c</code> example code creates a PDF file containing a JPEG or PNG image file and optional caption on a single page. The <code>create_pdf_image_file</code> function creates the PDF file, embeds a base font and the named JPEG or PNG image file, and then creates a page with the image centered on the page with any text centered below:</p> <p>The <code>image2pdf.c</code> example code creates a PDF file containing a JPEG or PNG image file and optional caption on a single page. The <code>create_pdf_image_file</code> function creates the PDF file, embeds a base font and the named JPEG or PNG image file, and then creates a page with the image centered on the page with any text centered below:</p>
@ -2338,7 +2601,7 @@ dd-&gt;y = row_y - row-&gt;height;
<tr><th>pdf</th> <tr><th>pdf</th>
<td class="description">PDF file</td></tr> <td class="description">PDF file</td></tr>
<tr><th>num_colors</th> <tr><th>num_colors</th>
<td class="description">Number of colors (1 or 3)</td></tr> <td class="description">Number of colors (1, 3, or 4)</td></tr>
<tr><th>cs</th> <tr><th>cs</th>
<td class="description">Color space enumeration</td></tr> <td class="description">Color space enumeration</td></tr>
</tbody></table> </tbody></table>
@ -2346,7 +2609,8 @@ dd-&gt;y = row_y - row-&gt;height;
<p class="description">Color array</p> <p class="description">Color array</p>
<h4 class="discussion">Discussion</h4> <h4 class="discussion">Discussion</h4>
<p class="discussion">This function creates a color array for a standard <code>PDFIO_CS_</code> enumerated color space. <p class="discussion">This function creates a color array for a standard <code>PDFIO_CS_</code> enumerated color space.
The &quot;num_colors&quot; argument must be <code>1</code> for grayscale and <code>3</code> for RGB color.</p> The &quot;num_colors&quot; argument must be <code>1</code> for grayscale, <code>3</code> for RGB color, and
<code>4</code> for CMYK color.</p>
<h3 class="function"><a id="pdfioArrayGetArray">pdfioArrayGetArray</a></h3> <h3 class="function"><a id="pdfioArrayGetArray">pdfioArrayGetArray</a></h3>
<p class="description">Get an array value from an array.</p> <p class="description">Get an array value from an array.</p>
<p class="code"> <p class="code">
@ -2490,7 +2754,7 @@ size_t pdfioArrayGetSize(<a href="#pdfio_array_t">pdfio_array_t</a> *a);</p>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Value type</p> <p class="description">Value type</p>
<h3 class="function"><a id="pdfioArrayRemove">pdfioArrayRemove</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.4&#160;</span><a id="pdfioArrayRemove">pdfioArrayRemove</a></h3>
<p class="description">Remove an array entry.</p> <p class="description">Remove an array entry.</p>
<p class="code"> <p class="code">
<span class="reserved">bool</span> pdfioArrayRemove(<a href="#pdfio_array_t">pdfio_array_t</a> *a, size_t n);</p> <span class="reserved">bool</span> pdfioArrayRemove(<a href="#pdfio_array_t">pdfio_array_t</a> *a, size_t n);</p>
@ -2695,7 +2959,7 @@ using the <a href="#pdfioPageDictAddImage"><code>pdfioPageDictAddImage</code></a
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p> <p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h3 class="function"><a id="pdfioContentPathEnd">pdfioContentPathEnd</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.1&#160;</span><a id="pdfioContentPathEnd">pdfioContentPathEnd</a></h3>
<p class="description">Clear the current path.</p> <p class="description">Clear the current path.</p>
<p class="code"> <p class="code">
<span class="reserved">bool</span> pdfioContentPathEnd(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st);</p> <span class="reserved">bool</span> pdfioContentPathEnd(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st);</p>
@ -3185,7 +3449,7 @@ are 0, a solid line is drawn.</p>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p> <p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h3 class="function"><a id="pdfioContentTextMeasure">pdfioContentTextMeasure</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.2&#160;</span><a id="pdfioContentTextMeasure">pdfioContentTextMeasure</a></h3>
<p class="description">Measure a text string and return its width.</p> <p class="description">Measure a text string and return its width.</p>
<p class="code"> <p class="code">
<span class="reserved">double</span> pdfioContentTextMeasure(<a href="#pdfio_obj_t">pdfio_obj_t</a> *font, <span class="reserved">const</span> <span class="reserved">char</span> *s, <span class="reserved">double</span> size);</p> <span class="reserved">double</span> pdfioContentTextMeasure(<a href="#pdfio_obj_t">pdfio_obj_t</a> *font, <span class="reserved">const</span> <span class="reserved">char</span> *s, <span class="reserved">double</span> size);</p>
@ -3203,7 +3467,9 @@ are 0, a solid line is drawn.</p>
<h4 class="discussion">Discussion</h4> <h4 class="discussion">Discussion</h4>
<p class="discussion">This function measures the given text string &quot;s&quot; and returns its width based <p class="discussion">This function measures the given text string &quot;s&quot; and returns its width based
on &quot;size&quot;. The text string must always use the UTF-8 (Unicode) encoding but on &quot;size&quot;. The text string must always use the UTF-8 (Unicode) encoding but
any control characters (such as newlines) are ignored.</p> any control characters (such as newlines) are ignored.
</p>
<h3 class="function"><a id="pdfioContentTextMoveLine">pdfioContentTextMoveLine</a></h3> <h3 class="function"><a id="pdfioContentTextMoveLine">pdfioContentTextMoveLine</a></h3>
<p class="description">Move to the next line and offset.</p> <p class="description">Move to the next line and offset.</p>
<p class="code"> <p class="code">
@ -3234,7 +3500,7 @@ any control characters (such as newlines) are ignored.</p>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p> <p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h3 class="function"><a id="pdfioContentTextNewLine">pdfioContentTextNewLine</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.2&#160;</span><a id="pdfioContentTextNewLine">pdfioContentTextNewLine</a></h3>
<p class="description">Move to the next line.</p> <p class="description">Move to the next line.</p>
<p class="code"> <p class="code">
<span class="reserved">bool</span> pdfioContentTextNewLine(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st);</p> <span class="reserved">bool</span> pdfioContentTextNewLine(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st);</p>
@ -3245,7 +3511,7 @@ any control characters (such as newlines) are ignored.</p>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p> <p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h3 class="function"><a id="pdfioContentTextNewLineShow">pdfioContentTextNewLineShow</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.2&#160;</span><a id="pdfioContentTextNewLineShow">pdfioContentTextNewLineShow</a></h3>
<p class="description">Move to the next line and show text.</p> <p class="description">Move to the next line and show text.</p>
<p class="code"> <p class="code">
<span class="reserved">bool</span> pdfioContentTextNewLineShow(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, <span class="reserved">double</span> ws, <span class="reserved">double</span> cs, <span class="reserved">bool</span> unicode, <span class="reserved">const</span> <span class="reserved">char</span> *s);</p> <span class="reserved">bool</span> pdfioContentTextNewLineShow(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, <span class="reserved">double</span> ws, <span class="reserved">double</span> cs, <span class="reserved">bool</span> unicode, <span class="reserved">const</span> <span class="reserved">char</span> *s);</p>
@ -3268,8 +3534,10 @@ any control characters (such as newlines) are ignored.</p>
<p class="discussion">This function moves to the next line and then shows some text with optional <p class="discussion">This function moves to the next line and then shows some text with optional
word and character spacing in a PDF content stream. The &quot;unicode&quot; argument word and character spacing in a PDF content stream. The &quot;unicode&quot; argument
specifies that the current font maps to full Unicode. The &quot;s&quot; argument specifies that the current font maps to full Unicode. The &quot;s&quot; argument
specifies a UTF-8 encoded string.</p> specifies a UTF-8 encoded string.
<h3 class="function"><a id="pdfioContentTextNewLineShowf">pdfioContentTextNewLineShowf</a></h3>
</p>
<h3 class="function"><span class="info">&#160;PDFio v1.2&#160;</span><a id="pdfioContentTextNewLineShowf">pdfioContentTextNewLineShowf</a></h3>
<p class="description">Show formatted text.</p> <p class="description">Show formatted text.</p>
<p class="code"> <p class="code">
<span class="reserved">bool</span> pdfioContentTextNewLineShowf(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, <span class="reserved">double</span> ws, <span class="reserved">double</span> cs, <span class="reserved">bool</span> unicode, <span class="reserved">const</span> <span class="reserved">char</span> *format, ...);</p> <span class="reserved">bool</span> pdfioContentTextNewLineShowf(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, <span class="reserved">double</span> ws, <span class="reserved">double</span> cs, <span class="reserved">bool</span> unicode, <span class="reserved">const</span> <span class="reserved">char</span> *format, ...);</p>
@ -3294,7 +3562,9 @@ specifies a UTF-8 encoded string.</p>
<p class="discussion">This function moves to the next line and shows some formatted text with <p class="discussion">This function moves to the next line and shows some formatted text with
optional word and character spacing in a PDF content stream. The &quot;unicode&quot; optional word and character spacing in a PDF content stream. The &quot;unicode&quot;
argument specifies that the current font maps to full Unicode. The &quot;format&quot; argument specifies that the current font maps to full Unicode. The &quot;format&quot;
argument specifies a UTF-8 encoded <code>printf</code>-style format string.</p> argument specifies a UTF-8 encoded <code>printf</code>-style format string.
</p>
<h3 class="function"><a id="pdfioContentTextShow">pdfioContentTextShow</a></h3> <h3 class="function"><a id="pdfioContentTextShow">pdfioContentTextShow</a></h3>
<p class="description">Show text.</p> <p class="description">Show text.</p>
<p class="code"> <p class="code">
@ -3357,7 +3627,7 @@ argument specifies an array of UTF-8 encoded strings.</p>
<p class="discussion">This function shows some formatted text in a PDF content stream. The <p class="discussion">This function shows some formatted text in a PDF content stream. The
&quot;unicode&quot; argument specifies that the current font maps to full Unicode. &quot;unicode&quot; argument specifies that the current font maps to full Unicode.
The &quot;format&quot; argument specifies a UTF-8 encoded <code>printf</code>-style format string.</p> The &quot;format&quot; argument specifies a UTF-8 encoded <code>printf</code>-style format string.</p>
<h3 class="function"><a id="pdfioDictClear">pdfioDictClear</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.4&#160;</span><a id="pdfioDictClear">pdfioDictClear</a></h3>
<p class="description">Remove a key/value pair from a dictionary.</p> <p class="description">Remove a key/value pair from a dictionary.</p>
<p class="code"> <p class="code">
<span class="reserved">bool</span> pdfioDictClear(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict, <span class="reserved">const</span> <span class="reserved">char</span> *key);</p> <span class="reserved">bool</span> pdfioDictClear(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict, <span class="reserved">const</span> <span class="reserved">char</span> *key);</p>
@ -3461,7 +3731,7 @@ time_t pdfioDictGetDate(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict, <span cl
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Value</p> <p class="description">Value</p>
<h3 class="function"><a id="pdfioDictGetKey">pdfioDictGetKey</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.4&#160;</span><a id="pdfioDictGetKey">pdfioDictGetKey</a></h3>
<p class="description">Get the key for the specified pair.</p> <p class="description">Get the key for the specified pair.</p>
<p class="code"> <p class="code">
<span class="reserved">const</span> <span class="reserved">char</span> *pdfioDictGetKey(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict, size_t n);</p> <span class="reserved">const</span> <span class="reserved">char</span> *pdfioDictGetKey(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict, size_t n);</p>
@ -3487,7 +3757,7 @@ time_t pdfioDictGetDate(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict, <span cl
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Value</p> <p class="description">Value</p>
<h3 class="function"><a id="pdfioDictGetNumPairs">pdfioDictGetNumPairs</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.4&#160;</span><a id="pdfioDictGetNumPairs">pdfioDictGetNumPairs</a></h3>
<p class="description">Get the number of key/value pairs in a dictionary.</p> <p class="description">Get the number of key/value pairs in a dictionary.</p>
<p class="code"> <p class="code">
size_t pdfioDictGetNumPairs(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict);</p> size_t pdfioDictGetNumPairs(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict);</p>
@ -3565,7 +3835,7 @@ size_t pdfioDictGetNumPairs(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict);</p>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Value type</p> <p class="description">Value type</p>
<h3 class="function"><a id="pdfioDictIterateKeys">pdfioDictIterateKeys</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.1&#160;</span><a id="pdfioDictIterateKeys">pdfioDictIterateKeys</a></h3>
<p class="description">Iterate the keys in a dictionary.</p> <p class="description">Iterate the keys in a dictionary.</p>
<p class="code"> <p class="code">
<span class="reserved">void</span> pdfioDictIterateKeys(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict, <a href="#pdfio_dict_cb_t">pdfio_dict_cb_t</a> cb, <span class="reserved">void</span> *cb_data);</p> <span class="reserved">void</span> pdfioDictIterateKeys(<a href="#pdfio_dict_t">pdfio_dict_t</a> *dict, <a href="#pdfio_dict_cb_t">pdfio_dict_cb_t</a> cb, <span class="reserved">void</span> *cb_data);</p>
@ -3592,7 +3862,9 @@ my_dict_cb(pdfio_dict_t *dict, const char *key, void *cb_data)
</pre> </pre>
The iteration continues as long as the callback returns <code>true</code> or all keys The iteration continues as long as the callback returns <code>true</code> or all keys
have been iterated.</p> have been iterated.
</p>
<h3 class="function"><a id="pdfioDictSetArray">pdfioDictSetArray</a></h3> <h3 class="function"><a id="pdfioDictSetArray">pdfioDictSetArray</a></h3>
<p class="description">Set a key array in a dictionary.</p> <p class="description">Set a key array in a dictionary.</p>
<p class="code"> <p class="code">
@ -3775,6 +4047,46 @@ have been iterated.</p>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p> <p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h3 class="function"><span class="info">&#160;PDFio 1.6&#160;</span><a id="pdfioFileAddOutputIntent">pdfioFileAddOutputIntent</a></h3>
<p class="description">Add an OutputIntent to a file.</p>
<p class="code">
<span class="reserved">void</span> pdfioFileAddOutputIntent(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *subtype, <span class="reserved">const</span> <span class="reserved">char</span> *condition, <span class="reserved">const</span> <span class="reserved">char</span> *cond_id, <span class="reserved">const</span> <span class="reserved">char</span> *reg_name, <span class="reserved">const</span> <span class="reserved">char</span> *info, <a href="#pdfio_obj_t">pdfio_obj_t</a> *profile);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
<tr><th>subtype</th>
<td class="description">Intent subtype (standard)</td></tr>
<tr><th>condition</th>
<td class="description">Condition name or <code>NULL</code> for none</td></tr>
<tr><th>cond_id</th>
<td class="description">Identifier such as registration name or <code>NULL</code> for none</td></tr>
<tr><th>reg_name</th>
<td class="description">Registry URL or <code>NULL</code> for none</td></tr>
<tr><th>info</th>
<td class="description">Description or <code>NULL</code> for none</td></tr>
<tr><th>profile</th>
<td class="description">ICC profile object or <code>NULL</code> for none</td></tr>
</tbody></table>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function adds an OutputIntent dictionary to the PDF file catalog.
The &quot;subtype&quot; argument specifies the intent subtype and is typically
&quot;GTS_PDFX&quot; for PDF/X, &quot;GTS_PDFA1&quot; for PDF/A, or &quot;ISO_PDFE1&quot; for PDF/E.
Passing <code>NULL</code> defaults the subtype to &quot;GTS_PDFA1&quot;.<br>
<br>
The &quot;condition&quot; argument specifies a short name for the output intent, while
the &quot;info&quot; argument specifies a longer description for the output intent.
Both can be <code>NULL</code> to omit this information.<br>
<br>
The &quot;cond_id&quot; argument specifies a unique identifier such as a registration
(&quot;CGATS001&quot;) or color space name (&quot;sRGB&quot;). The &quot;reg_name&quot; argument provides
a URL for the identifier.<br>
<br>
The &quot;profile&quot; argument specifies an ICC profile object for the output
condition. If <code>NULL</code>, the PDF consumer will attempt to look up the correct
profile using the &quot;cond_id&quot; value.
</p>
<h3 class="function"><a id="pdfioFileClose">pdfioFileClose</a></h3> <h3 class="function"><a id="pdfioFileClose">pdfioFileClose</a></h3>
<p class="description">Close a PDF file and free all memory used for it.</p> <p class="description">Close a PDF file and free all memory used for it.</p>
<p class="code"> <p class="code">
@ -3812,15 +4124,17 @@ have been iterated.</p>
name of the PDF file to create.<br> name of the PDF file to create.<br>
<br> <br>
The &quot;version&quot; argument specifies the PDF version number for the file or The &quot;version&quot; argument specifies the PDF version number for the file or
<code>NULL</code> for the default (&quot;2.0&quot;).<br> <code>NULL</code> for the default (&quot;2.0&quot;). The value &quot;PCLm-1.0&quot; can be specified to
produce the PCLm subset of PDF.<br>
<br> <br>
The &quot;media_box&quot; and &quot;crop_box&quot; arguments specify the default MediaBox and The &quot;media_box&quot; and &quot;crop_box&quot; arguments specify the default MediaBox and
CropBox for pages in the PDF file - if <code>NULL</code> then a default &quot;Universal&quot; size CropBox for pages in the PDF file - if <code>NULL</code> then a default &quot;Universal&quot; size
of 8.27x11in (the intersection of US Letter and ISO A4) is used.<br> of 8.27x11in (the intersection of US Letter and ISO A4) is used.<br>
<br> <br>
The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> the default error handler is used that and its data pointer - if <code>NULL</code> then the default error handler is used that
writes error messages to <code>stderr</code>.</p> writes error messages to <code>stderr</code>. The error handler callback should return
<code>true</code> to continue writing the PDF file or <code>false</code> to stop.</p>
<h3 class="function"><a id="pdfioFileCreateArrayObj">pdfioFileCreateArrayObj</a></h3> <h3 class="function"><a id="pdfioFileCreateArrayObj">pdfioFileCreateArrayObj</a></h3>
<p class="description">Create a new object in a PDF file containing an array.</p> <p class="description">Create a new object in a PDF file containing an array.</p>
<p class="code"> <p class="code">
@ -3886,8 +4200,33 @@ specifies the font nane:
<p class="discussion">Aside from &quot;Symbol&quot; and &quot;Zapf-Dingbats&quot;, Base fonts use the Windows CP1252 <p class="discussion">Aside from &quot;Symbol&quot; and &quot;Zapf-Dingbats&quot;, Base fonts use the Windows CP1252
(ISO-8859-1 with additional characters such as the Euro symbol) subset of (ISO-8859-1 with additional characters such as the Euro symbol) subset of
Unicode.</p> Unicode.</p>
<h3 class="function"><span class="info">&#160;PDFio v1.6&#160;</span><a id="pdfioFileCreateFontObjFromData">pdfioFileCreateFontObjFromData</a></h3>
<p class="description">Add a font in memory to a PDF file.</p>
<p class="code">
<a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateFontObjFromData(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">void</span> *data, size_t datasize, <span class="reserved">bool</span> unicode);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
<tr><th>data</th>
<td class="description">Font data in memory</td></tr>
<tr><th>datasize</th>
<td class="description">Size of font in memory</td></tr>
<tr><th>unicode</th>
<td class="description">Force Unicode</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Font object</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function embeds TrueType/OpenType font data into a PDF file. The
&quot;unicode&quot; parameter controls whether the font is encoded for two-byte
characters (potentially full Unicode, but more typically a subset)
or to only support the Windows CP1252 (ISO-8859-1 with additional
characters such as the Euro symbol) subset of Unicode.
</p>
<h3 class="function"><a id="pdfioFileCreateFontObjFromFile">pdfioFileCreateFontObjFromFile</a></h3> <h3 class="function"><a id="pdfioFileCreateFontObjFromFile">pdfioFileCreateFontObjFromFile</a></h3>
<p class="description">Add a font object to a PDF file.</p> <p class="description">Add a font file to a PDF file.</p>
<p class="code"> <p class="code">
<a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateFontObjFromFile(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *filename, <span class="reserved">bool</span> unicode);</p> <a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateFontObjFromFile(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *filename, <span class="reserved">bool</span> unicode);</p>
<h4 class="parameters">Parameters</h4> <h4 class="parameters">Parameters</h4>
@ -3902,13 +4241,30 @@ Unicode.</p>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Font object</p> <p class="description">Font object</p>
<h4 class="discussion">Discussion</h4> <h4 class="discussion">Discussion</h4>
<p class="discussion">This function embeds a TrueType/OpenType font into a PDF file. The <p class="discussion">This function embeds a TrueType/OpenType font file into a PDF file. The
&quot;unicode&quot; parameter controls whether the font is encoded for two-byte &quot;unicode&quot; parameter controls whether the font is encoded for two-byte
characters (potentially full Unicode, but more typically a subset) characters (potentially full Unicode, but more typically a subset)
or to only support the Windows CP1252 (ISO-8859-1 with additional or to only support the Windows CP1252 (ISO-8859-1 with additional
characters such as the Euro symbol) subset of Unicode.</p> characters such as the Euro symbol) subset of Unicode.</p>
<h3 class="function"><a id="pdfioFileCreateICCObjFromData">pdfioFileCreateICCObjFromData</a></h3>
<p class="description">Add ICC profile data to a PDF file.</p>
<p class="code">
<a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateICCObjFromData(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">unsigned</span> <span class="reserved">char</span> *data, size_t datalen, size_t num_colors);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
<tr><th>data</th>
<td class="description">ICC profile buffer</td></tr>
<tr><th>datalen</th>
<td class="description">Length of ICC profile</td></tr>
<tr><th>num_colors</th>
<td class="description">Number of color components (1, 3, or 4)</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Object</p>
<h3 class="function"><a id="pdfioFileCreateICCObjFromFile">pdfioFileCreateICCObjFromFile</a></h3> <h3 class="function"><a id="pdfioFileCreateICCObjFromFile">pdfioFileCreateICCObjFromFile</a></h3>
<p class="description">Add an ICC profile object to a PDF file.</p> <p class="description">Add an ICC profile file to a PDF file.</p>
<p class="code"> <p class="code">
<a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateICCObjFromFile(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *filename, size_t num_colors);</p> <a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateICCObjFromFile(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *filename, size_t num_colors);</p>
<h4 class="parameters">Parameters</h4> <h4 class="parameters">Parameters</h4>
@ -3986,7 +4342,7 @@ image on the page.<br>
Note: Currently PNG support is limited to grayscale, RGB, or indexed files Note: Currently PNG support is limited to grayscale, RGB, or indexed files
without interlacing or alpha. Transparency (masking) based on color/index without interlacing or alpha. Transparency (masking) based on color/index
is supported.</blockquote> is supported.</blockquote>
<h3 class="function"><a id="pdfioFileCreateNameObj">pdfioFileCreateNameObj</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.4&#160;</span><a id="pdfioFileCreateNameObj">pdfioFileCreateNameObj</a></h3>
<p class="description">Create a new object in a PDF file containing a name.</p> <p class="description">Create a new object in a PDF file containing a name.</p>
<p class="code"> <p class="code">
<a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateNameObj(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *name);</p> <a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateNameObj(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *name);</p>
@ -4001,8 +4357,10 @@ is supported.</blockquote>
<p class="description">New object</p> <p class="description">New object</p>
<h4 class="discussion">Discussion</h4> <h4 class="discussion">Discussion</h4>
<p class="discussion">This function creates a new object with a name value in a PDF file. <p class="discussion">This function creates a new object with a name value in a PDF file.
You must call <a href="#pdfioObjClose"><code>pdfioObjClose</code></a> to write the object to the file.</p> You must call <a href="#pdfioObjClose"><code>pdfioObjClose</code></a> to write the object to the file.
<h3 class="function"><a id="pdfioFileCreateNumberObj">pdfioFileCreateNumberObj</a></h3>
</p>
<h3 class="function"><span class="info">&#160;PDFio v1.2&#160;</span><a id="pdfioFileCreateNumberObj">pdfioFileCreateNumberObj</a></h3>
<p class="description">Create a new object in a PDF file containing a number.</p> <p class="description">Create a new object in a PDF file containing a number.</p>
<p class="code"> <p class="code">
<a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateNumberObj(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">double</span> number);</p> <a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateNumberObj(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">double</span> number);</p>
@ -4017,7 +4375,9 @@ You must call <a href="#pdfioObjClose"><code>pdfioObjClose</code></a> to write t
<p class="description">New object</p> <p class="description">New object</p>
<h4 class="discussion">Discussion</h4> <h4 class="discussion">Discussion</h4>
<p class="discussion">This function creates a new object with a number value in a PDF file. <p class="discussion">This function creates a new object with a number value in a PDF file.
You must call <a href="#pdfioObjClose"><code>pdfioObjClose</code></a> to write the object to the file.</p> You must call <a href="#pdfioObjClose"><code>pdfioObjClose</code></a> to write the object to the file.
</p>
<h3 class="function"><a id="pdfioFileCreateObj">pdfioFileCreateObj</a></h3> <h3 class="function"><a id="pdfioFileCreateObj">pdfioFileCreateObj</a></h3>
<p class="description">Create a new object in a PDF file.</p> <p class="description">Create a new object in a PDF file.</p>
<p class="code"> <p class="code">
@ -4069,15 +4429,18 @@ output_cb(void *output_cbdata, const void *buffer, size_t bytes)
</pre> </pre>
The &quot;version&quot; argument specifies the PDF version number for the file or The &quot;version&quot; argument specifies the PDF version number for the file or
<code>NULL</code> for the default (&quot;2.0&quot;).<br> <code>NULL</code> for the default (&quot;2.0&quot;). Unlike <a href="#pdfioFileCreate"><code>pdfioFileCreate</code></a> and
<a href="#pdfioFileCreateTemporary"><code>pdfioFileCreateTemporary</code></a>, it is generally not safe to pass the
&quot;PCLm-1.0&quot; version string.<br>
<br> <br>
The &quot;media_box&quot; and &quot;crop_box&quot; arguments specify the default MediaBox and The &quot;media_box&quot; and &quot;crop_box&quot; arguments specify the default MediaBox and
CropBox for pages in the PDF file - if <code>NULL</code> then a default &quot;Universal&quot; size CropBox for pages in the PDF file - if <code>NULL</code> then a default &quot;Universal&quot; size
of 8.27x11in (the intersection of US Letter and ISO A4) is used.<br> of 8.27x11in (the intersection of US Letter and ISO A4) is used.<br>
<br> <br>
The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> the default error handler is used that and its data pointer - if <code>NULL</code> then the default error handler is used that
writes error messages to <code>stderr</code>.<br> writes error messages to <code>stderr</code>. The error handler callback should return
<code>true</code> to continue writing the PDF file or <code>false</code> to stop.<br>
<br> <br>
</p><blockquote> </p><blockquote>
<em>Note</em>: Files created using this API are slightly larger than those <em>Note</em>: Files created using this API are slightly larger than those
@ -4096,7 +4459,7 @@ stored as indirect object references.</blockquote>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Contents stream</p> <p class="description">Contents stream</p>
<h3 class="function"><a id="pdfioFileCreateStringObj">pdfioFileCreateStringObj</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.2&#160;</span><a id="pdfioFileCreateStringObj">pdfioFileCreateStringObj</a></h3>
<p class="description">Create a new object in a PDF file containing a string.</p> <p class="description">Create a new object in a PDF file containing a string.</p>
<p class="code"> <p class="code">
<a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateStringObj(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *string);</p> <a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateStringObj(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *string);</p>
@ -4111,7 +4474,9 @@ stored as indirect object references.</blockquote>
<p class="description">New object</p> <p class="description">New object</p>
<h4 class="discussion">Discussion</h4> <h4 class="discussion">Discussion</h4>
<p class="discussion">This function creates a new object with a string value in a PDF file. <p class="discussion">This function creates a new object with a string value in a PDF file.
You must call <a href="#pdfioObjClose"><code>pdfioObjClose</code></a> to write the object to the file.</p> You must call <a href="#pdfioObjClose"><code>pdfioObjClose</code></a> to write the object to the file.
</p>
<h3 class="function"><a id="pdfioFileCreateTemporary">pdfioFileCreateTemporary</a></h3> <h3 class="function"><a id="pdfioFileCreateTemporary">pdfioFileCreateTemporary</a></h3>
<p class="description"></p> <p class="description"></p>
<p class="code"> <p class="code">
@ -4137,8 +4502,19 @@ You must call <a href="#pdfioObjClose"><code>pdfioObjClose</code></a> to write t
<p class="description">Create a temporary PDF file.</p> <p class="description">Create a temporary PDF file.</p>
<p class="discussion">This function creates a PDF file with a unique filename in the current <p class="discussion">This function creates a PDF file with a unique filename in the current
temporary directory. The temporary file is stored in the string &quot;buffer&quot; an temporary directory. The temporary file is stored in the string &quot;buffer&quot; an
will have a &quot;.pdf&quot; extension. Otherwise, this function works the same as will have a &quot;.pdf&quot; extension.<br>
the <a href="#pdfioFileCreate"><code>pdfioFileCreate</code></a> function. <br>
The &quot;version&quot; argument specifies the PDF version number for the file or
<code>NULL</code> for the default (&quot;2.0&quot;). The value &quot;PCLm-1.0&quot; can be specified to
produce the PCLm subset of PDF.<br>
<br>
The &quot;media_box&quot; and &quot;crop_box&quot; arguments specify the default MediaBox and
CropBox for pages in the PDF file - if <code>NULL</code> then a default &quot;Universal&quot; size
of 8.27x11in (the intersection of US Letter and ISO A4) is used.<br>
<br>
The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> the default error handler is used that
writes error messages to <code>stderr</code>.
</p> </p>
<h3 class="function"><a id="pdfioFileFindObj">pdfioFileFindObj</a></h3> <h3 class="function"><a id="pdfioFileFindObj">pdfioFileFindObj</a></h3>
@ -4223,6 +4599,35 @@ time_t pdfioFileGetCreationDate(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf);<
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Keywords string or <code>NULL</code> for none</p> <p class="description">Keywords string or <code>NULL</code> for none</p>
<h3 class="function"><span class="info">&#160;PDFio 1.6&#160;</span><a id="pdfioFileGetLanguage">pdfioFileGetLanguage</a></h3>
<p class="description">Get the language metadata for a PDF file.</p>
<p class="code">
<span class="reserved">const</span> <span class="reserved">char</span> *pdfioFileGetLanguage(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Language or <code>NULL</code> for none</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function gets the (primary/default) language metadata, if any, for a PDF
file. The returned string is an IETF BCP 47 language tag of the form
&quot;lang-REGION&quot;. For example, the string &quot;en-CA&quot; specifies Canadian English
and the string &quot;fr-CA&quot; specifies Canadian French.
</p>
<h3 class="function"><a id="pdfioFileGetModificationDate">pdfioFileGetModificationDate</a></h3>
<p class="description">Get the most recent modification date for a PDF file.</p>
<p class="code">
time_t pdfioFileGetModificationDate(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Modification date or <code>0</code> for none</p>
<h3 class="function"><a id="pdfioFileGetName">pdfioFileGetName</a></h3> <h3 class="function"><a id="pdfioFileGetName">pdfioFileGetName</a></h3>
<p class="description">Get a PDF's filename.</p> <p class="description">Get a PDF's filename.</p>
<p class="code"> <p class="code">
@ -4372,8 +4777,15 @@ cancel the open. If <code>NULL</code> is specified for the callback function an
PDF file requires a password, the open will always fail.<br> PDF file requires a password, the open will always fail.<br>
<br> <br>
The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback The &quot;error_cb&quot; and &quot;error_cbdata&quot; arguments specify an error handler callback
and its data pointer - if <code>NULL</code> the default error handler is used that and its data pointer - if <code>NULL</code> then the default error handler is used that
writes error messages to <code>stderr</code>.</p> writes error messages to <code>stderr</code>. The error handler callback should return
<code>true</code> to continue reading the PDF file or <code>false</code> to stop.<br>
<br>
</p><blockquote>
Note: Error messages starting with &quot;WARNING:&quot; are actually warning
messages - the callback should normally return <code>true</code> to allow PDFio to
try to resolve the issue. In addition, some errors are unrecoverable and
ignore the return value of the error callback.</blockquote>
<h3 class="function"><a id="pdfioFileSetAuthor">pdfioFileSetAuthor</a></h3> <h3 class="function"><a id="pdfioFileSetAuthor">pdfioFileSetAuthor</a></h3>
<p class="description">Set the author for a PDF file.</p> <p class="description">Set the author for a PDF file.</p>
<p class="code"> <p class="code">
@ -4418,6 +4830,35 @@ writes error messages to <code>stderr</code>.</p>
<tr><th>value</th> <tr><th>value</th>
<td class="description">Value</td></tr> <td class="description">Value</td></tr>
</tbody></table> </tbody></table>
<h3 class="function"><span class="info">&#160;PDFio 1.6&#160;</span><a id="pdfioFileSetLanguage">pdfioFileSetLanguage</a></h3>
<p class="description">Set the language metadata for a PDF file.</p>
<p class="code">
<span class="reserved">void</span> pdfioFileSetLanguage(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, <span class="reserved">const</span> <span class="reserved">char</span> *value);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
<tr><th>value</th>
<td class="description">Value</td></tr>
</tbody></table>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function sets the (primary/default) language metadata for a PDF file.
The &quot;value&quot; argument is an IETF BCP 47 language tag string of the form
&quot;lang-REGION&quot;. For example, the string &quot;en-CA&quot; specifies Canadian English
and the string &quot;fr-CA&quot; specifies Canadian French.
</p>
<h3 class="function"><a id="pdfioFileSetModificationDate">pdfioFileSetModificationDate</a></h3>
<p class="description">Set the modification date for a PDF file.</p>
<p class="code">
<span class="reserved">void</span> pdfioFileSetModificationDate(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, time_t value);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
<tr><th>value</th>
<td class="description">Value</td></tr>
</tbody></table>
<h3 class="function"><a id="pdfioFileSetPermissions">pdfioFileSetPermissions</a></h3> <h3 class="function"><a id="pdfioFileSetPermissions">pdfioFileSetPermissions</a></h3>
<p class="description">Set the PDF permissions, encryption mode, and passwords.</p> <p class="description">Set the PDF permissions, encryption mode, and passwords.</p>
<p class="code"> <p class="code">
@ -4478,7 +4919,7 @@ size_t pdfioImageGetBytesPerLine(<a href="#pdfio_obj_t">pdfio_obj_t</a> *obj);</
<td class="description">Image object</td></tr> <td class="description">Image object</td></tr>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Number of bytes per line</p> <p class="description">Number of bytes per line or <code>0</code> on error</p>
<h3 class="function"><a id="pdfioImageGetHeight">pdfioImageGetHeight</a></h3> <h3 class="function"><a id="pdfioImageGetHeight">pdfioImageGetHeight</a></h3>
<p class="description">Get the height of an image object.</p> <p class="description">Get the height of an image object.</p>
<p class="code"> <p class="code">
@ -4583,7 +5024,7 @@ size_t pdfioObjGetLength(<a href="#pdfio_obj_t">pdfio_obj_t</a> *obj);</p>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description">Length in bytes or <code>0</code> for none</p> <p class="description">Length in bytes or <code>0</code> for none</p>
<h3 class="function"><a id="pdfioObjGetName">pdfioObjGetName</a></h3> <h3 class="function"><span class="info">&#160;PDFio v1.4&#160;</span><a id="pdfioObjGetName">pdfioObjGetName</a></h3>
<p class="description">Get the name value associated with an object.</p> <p class="description">Get the name value associated with an object.</p>
<p class="code"> <p class="code">
<span class="reserved">const</span> <span class="reserved">char</span> *pdfioObjGetName(<a href="#pdfio_obj_t">pdfio_obj_t</a> *obj);</p> <span class="reserved">const</span> <span class="reserved">char</span> *pdfioObjGetName(<a href="#pdfio_obj_t">pdfio_obj_t</a> *obj);</p>
@ -4818,14 +5259,15 @@ size_t pdfioPageGetNumStreams(<a href="#pdfio_obj_t">pdfio_obj_t</a> *page);</p>
<td class="description">Size of string buffer</td></tr> <td class="description">Size of string buffer</td></tr>
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on EOF</p> <p class="description"><code>true</code> on success, <code>false</code> on end-of-stream or error</p>
<h4 class="discussion">Discussion</h4> <h4 class="discussion">Discussion</h4>
<p class="discussion">This function reads a single PDF token from a stream. Operator tokens, <p class="discussion">This function reads a single PDF token from a stream, skipping all whitespace
boolean values, and numbers are returned as-is in the provided string buffer. and comments. Operator tokens, boolean values, and numbers are returned
String values start with the opening parenthesis ('(') but have all escaping as-is in the provided string buffer. String values start with the opening
resolved and the terminating parenthesis removed. Hexadecimal string values parenthesis ('(') but have all escaping resolved and the terminating
start with the opening angle bracket ('&lt;') and have all whitespace and the parenthesis removed. Hexadecimal string values start with the opening angle
terminating angle bracket removed.</p> bracket ('&lt;') and have all whitespace and the terminating angle bracket
removed.</p>
<h3 class="function"><a id="pdfioStreamPeek">pdfioStreamPeek</a></h3> <h3 class="function"><a id="pdfioStreamPeek">pdfioStreamPeek</a></h3>
<p class="description">Peek at data in a stream.</p> <p class="description">Peek at data in a stream.</p>
<p class="code"> <p class="code">
@ -4856,6 +5298,11 @@ ssize_t pdfioStreamPeek(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, <span
</tbody></table> </tbody></table>
<h4 class="returnvalue">Return Value</h4> <h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p> <p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h4 class="discussion">Discussion</h4>
<p class="discussion">This function writes a formatted string to a stream. In addition to the
standard <code>printf</code> format characters, you can use &quot;%H&quot; to format a HTML/XML
string value, &quot;%N&quot; to format a PDF name value (&quot;/Name&quot;), and &quot;%S&quot; to format
a PDF string (&quot;(String)&quot;) value.</p>
<h3 class="function"><a id="pdfioStreamPutChar">pdfioStreamPutChar</a></h3> <h3 class="function"><a id="pdfioStreamPutChar">pdfioStreamPutChar</a></h3>
<p class="description">Write a single character to a stream.</p> <p class="description">Write a single character to a stream.</p>
<p class="code"> <p class="code">
@ -5079,6 +5526,7 @@ typedef enum <a href="#pdfio_valtype_e">pdfio_valtype_e</a> pdfio_valtype_t;
<h4 class="constants">Constants</h4> <h4 class="constants">Constants</h4>
<table class="list"><tbody> <table class="list"><tbody>
<tr><th>PDFIO_CS_ADOBE </th><td class="description">AdobeRGB 1998</td></tr> <tr><th>PDFIO_CS_ADOBE </th><td class="description">AdobeRGB 1998</td></tr>
<tr><th>PDFIO_CS_CGATS001 </th><td class="description">CGATS001 (CMYK)</td></tr>
<tr><th>PDFIO_CS_P3_D65 </th><td class="description">Display P3</td></tr> <tr><th>PDFIO_CS_P3_D65 </th><td class="description">Display P3</td></tr>
<tr><th>PDFIO_CS_SRGB </th><td class="description">sRGB</td></tr> <tr><th>PDFIO_CS_SRGB </th><td class="description">sRGB</td></tr>
</tbody></table> </tbody></table>
@ -5089,7 +5537,7 @@ typedef enum <a href="#pdfio_valtype_e">pdfio_valtype_e</a> pdfio_valtype_t;
<tr><th>PDFIO_ENCRYPTION_AES_128 </th><td class="description">128-bit AES encryption (PDF 1.6)</td></tr> <tr><th>PDFIO_ENCRYPTION_AES_128 </th><td class="description">128-bit AES encryption (PDF 1.6)</td></tr>
<tr><th>PDFIO_ENCRYPTION_NONE </th><td class="description">No encryption</td></tr> <tr><th>PDFIO_ENCRYPTION_NONE </th><td class="description">No encryption</td></tr>
<tr><th>PDFIO_ENCRYPTION_RC4_128 </th><td class="description">128-bit RC4 encryption (PDF 1.4)</td></tr> <tr><th>PDFIO_ENCRYPTION_RC4_128 </th><td class="description">128-bit RC4 encryption (PDF 1.4)</td></tr>
<tr><th>PDFIO_ENCRYPTION_RC4_40 </th><td class="description">40-bit RC4 encryption (PDF 1.3)</td></tr> <tr><th>PDFIO_ENCRYPTION_RC4_40 </th><td class="description">40-bit RC4 encryption (PDF 1.3, reading only)</td></tr>
</tbody></table> </tbody></table>
<h3 class="enumeration"><a id="pdfio_filter_e">pdfio_filter_e</a></h3> <h3 class="enumeration"><a id="pdfio_filter_e">pdfio_filter_e</a></h3>
<p class="description">Compression/decompression filters for streams</p> <p class="description">Compression/decompression filters for streams</p>

View File

@ -15,7 +15,7 @@ goals of PDFio are:
PDFio is *not* concerned with rendering or viewing a PDF file, although a PDF PDFio is *not* concerned with rendering or viewing a PDF file, although a PDF
RIP or viewer could be written using it. RIP or viewer could be written using it.
PDFio is Copyright © 2021-2024 by Michael R Sweet and is licensed under the PDFio is Copyright © 2021-2025 by Michael R Sweet and is licensed under the
Apache License Version 2.0 with an (optional) exception to allow linking against 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. GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
@ -28,7 +28,10 @@ PDFio requires the following to build the software:
- A C99 compiler such as Clang, GCC, or MS Visual C - A C99 compiler such as Clang, GCC, or MS Visual C
- A POSIX-compliant `make` program - A POSIX-compliant `make` program
- A POSIX-compliant `sh` program - A POSIX-compliant `sh` program
- ZLIB (<https://www.zlib.net>) 1.0 or higher - ZLIB (<https://www.zlib.net/>) 1.0 or higher
PDFio will also use libpng 1.6 or higher (<https://www.libpng.org/>) to provide
enhanced PNG image support.
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided. IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
@ -340,8 +343,8 @@ password_cb(void *data, const char *filename)
``` ```
The error callback is called for both errors and warnings and accepts the 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 `pdfio_file_t` pointer, a message string, and the callback pointer value. It
example: returns `true` to continue processing the file or `false` to stop, for example:
```c ```c
bool bool
@ -351,13 +354,17 @@ error_cb(pdfio_file_t *pdf, const char *message, void *data)
fprintf(stderr, "%s: %s\n", pdfioFileGetName(pdf), message); fprintf(stderr, "%s: %s\n", pdfioFileGetName(pdf), message);
// Return false to treat warnings as errors // Return true for warning messages (continue) and false for errors (stop)
return (false); return (!strncmp(message, "WARNING:", 8));
} }
``` ```
The default error callback (`NULL`) does the equivalent of the above. The default error callback (`NULL`) does the equivalent of the above.
> Note: Many errors are unrecoverable, so PDFio ignores the return value from
> the error callback and always stops processing the PDF file. Warning messages
> start with the prefix "WARNING:" while errors have no prefix.
Each PDF file contains one or more pages. The [`pdfioFileGetNumPages`](@@) Each PDF file contains one or more pages. The [`pdfioFileGetNumPages`](@@)
function returns the number of pages in the file while the function returns the number of pages in the file while the
[`pdfioFileGetPage`](@@) function gets the specified page in the PDF file: [`pdfioFileGetPage`](@@) function gets the specified page in the PDF file:
@ -886,11 +893,26 @@ main(int argc, // I - Number of command-line arguments
{ {
const char *filename; // PDF filename const char *filename; // PDF filename
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
const char *author; // Author name pdfio_dict_t *catalog; // Catalog dictionary
time_t creation_date; // Creation date const char *author, // Author name
struct tm *creation_tm; // Creation date/time information *creator, // Creator name
char creation_text[256]; // Creation date/time as a string *producer, // Producer name
const char *title; // Title *title; // Title
time_t creation_date, // Creation date
modification_date; // Modification date
struct tm *creation_tm, // Creation date/time information
*modification_tm; // Modification date/time information
char creation_text[256], // Creation date/time as a string
modification_text[256], // Modification date/time human fmt string
range_text[255]; // Page range text
size_t num_pages; // PDF number of pages
bool has_acroform; // Does the file have an AcroForm?
pdfio_obj_t *page; // Object
pdfio_dict_t *page_dict; // Object dictionary
size_t cur, // Current page index
prev; // Previous page index
pdfio_rect_t cur_box, // Current MediaBox
prev_box; // Previous MediaBox
// Get the filename from the command-line... // Get the filename from the command-line...
@ -903,14 +925,20 @@ main(int argc, // I - Number of command-line arguments
filename = argv[1]; filename = argv[1];
// Open the PDF file with the default callbacks... // Open the PDF file with the default callbacks...
pdf = pdfioFileOpen(filename, /*password_cb*/NULL, /*password_cbdata*/NULL, pdf = pdfioFileOpen(filename, /*password_cb*/NULL,
/*error_cb*/NULL, /*error_cbdata*/NULL); /*password_cbdata*/NULL, /*error_cb*/NULL,
/*error_cbdata*/NULL);
if (pdf == NULL) if (pdf == NULL)
return (1); return (1);
// Get the title and author... // Get the title, author, etc...
author = pdfioFileGetAuthor(pdf); catalog = pdfioFileGetCatalog(pdf);
title = pdfioFileGetTitle(pdf); author = pdfioFileGetAuthor(pdf);
creator = pdfioFileGetCreator(pdf);
has_acroform = pdfioDictGetType(catalog, "AcroForm") != PDFIO_VALTYPE_NONE;
num_pages = pdfioFileGetNumPages(pdf);
producer = pdfioFileGetProducer(pdf);
title = pdfioFileGetTitle(pdf);
// Get the creation date and convert to a string... // Get the creation date and convert to a string...
if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0) if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0)
@ -923,12 +951,76 @@ main(int argc, // I - Number of command-line arguments
snprintf(creation_text, sizeof(creation_text), "-- not set --"); snprintf(creation_text, sizeof(creation_text), "-- not set --");
} }
// Get the modification date and convert to a string...
if ((modification_date = pdfioFileGetModificationDate(pdf)) > 0)
{
modification_tm = localtime(&modification_date);
strftime(modification_text, sizeof(modification_text), "%c", modification_tm);
}
else
{
snprintf(modification_text, sizeof(modification_text), "-- not set --");
}
// Print file information to stdout... // Print file information to stdout...
printf("%s:\n", filename); printf("%s:\n", filename);
printf(" Title: %s\n", title ? title : "-- not set --"); printf(" Title: %s\n", title ? title : "-- not set --");
printf(" Author: %s\n", author ? author : "-- not set --"); printf(" Author: %s\n", author ? author : "-- not set --");
printf(" Created On: %s\n", creation_text); printf(" Creator: %s\n", creator ? creator : "-- not set --");
printf(" Number Pages: %u\n", (unsigned)pdfioFileGetNumPages(pdf)); printf(" Producer: %s\n", producer ? producer : "-- not set --");
printf(" Created On: %s\n", creation_text);
printf(" Modified On: %s\n", modification_text);
printf(" Version: %s\n", pdfioFileGetVersion(pdf));
printf(" AcroForm: %s\n", has_acroform ? "Yes" : "No");
printf(" Number of Pages: %u\n", (unsigned)num_pages);
// Report the MediaBox for all of the pages
prev_box.x1 = prev_box.x2 = prev_box.y1 = prev_box.y2 = 0.0;
for (cur = 0, prev = 0; cur < num_pages; cur ++)
{
// Find the MediaBox for this page in the page tree...
for (page = pdfioFileGetPage(pdf, cur);
page != NULL;
page = pdfioDictGetObj(page_dict, "Parent"))
{
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
page_dict = pdfioObjGetDict(page);
if (pdfioDictGetRect(page_dict, "MediaBox", &cur_box))
break;
}
// If this MediaBox is different from the previous one, show the range of
// pages that have that size...
if (cur == 0 ||
fabs(cur_box.x1 - prev_box.x1) > 0.01 ||
fabs(cur_box.y1 - prev_box.y1) > 0.01 ||
fabs(cur_box.x2 - prev_box.x2) > 0.01 ||
fabs(cur_box.y2 - prev_box.y2) > 0.01)
{
if (cur > prev)
{
snprintf(range_text, sizeof(range_text), "Pages %u-%u",
(unsigned)(prev + 1), (unsigned)cur);
printf("%16s: [%g %g %g %g]\n", range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
// Start a new series of pages with the new size...
prev = cur;
prev_box = cur_box;
}
}
// Show the last range as needed...
if (cur > prev)
{
snprintf(range_text, sizeof(range_text), "Pages %u-%u",
(unsigned)(prev + 1), (unsigned)cur);
printf("%16s: [%g %g %g %g]\n", range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
// Close the PDF file... // Close the PDF file...
pdfioFileClose(pdf); pdfioFileClose(pdf);
@ -941,37 +1033,98 @@ main(int argc, // I - Number of command-line arguments
Extract Text from PDF File Extract Text from PDF File
-------------------------- --------------------------
The `pdf2text.c` example code extracts non-Unicode text from a PDF file by The `pdf2text.c` example code extracts text from a PDF file and writes it to the
scanning each page for strings and text drawing commands. Since it doesn't standard output. Unlike some other PDF tools, it outputs the text in the order
look at the font encoding or support Unicode text, it is really only useful to it is seen in each page stream so the output might appear "jumbled" if the PDF
extract plain ASCII text from a PDF file. And since it writes text in the order producer doesn't output text in reading order. The code is able to handle
it appears in the page stream, it may not come out in the same order as appears different font encodings and produces UTF-8 output.
on the page.
The [`pdfioStreamGetToken`](@@) function is used to read individual tokens from The [`pdfioStreamGetToken`](@@) function is used to read individual tokens from
the page streams. Tokens starting with the open parenthesis are text strings, the page streams:
while PDF operators are left as-is. We use some simple logic to make sure that
we include spaces between text strings and add newlines for the text operators
that start a new line in a text block:
```c ```c
pdfio_stream_t *st; // Page stream pdfio_stream_t *st; // Page stream
char buffer[1024], // Token buffer
*bufptr, // Pointer into buffer
name[256]; // Current (font) name
bool first = true; // First string on line? bool first = true; // First string on line?
char buffer[1024]; // Token buffer int encoding[256]; // Font encoding to Unicode
bool in_array = false; // Are we in an array?
// Read PDF tokens from the page stream... // Read PDF tokens from the page stream...
while (pdfioStreamGetToken(st, buffer, sizeof(buffer))) while (pdfioStreamGetToken(st, buffer, sizeof(buffer)))
{ {
if (buffer[0] == '(') ```
Justified text can be found inside arrays ("[ ... ]"), so we look for the array
delimiter tokens and any (spacing) numbers inside an array. Experimentation has
shown that numbers greater than 100 can be treated as whitespace:
```c
if (!strcmp(buffer, "["))
{
// Start of an array for justified text...
in_array = true;
}
else if (!strcmp(buffer, "]"))
{
// End of an array for justified text...
in_array = false;
}
else if (!first && in_array && (isdigit(buffer[0]) || buffer[0] == '-') && fabs(atof(buffer)) > 100)
{
// Whitespace in a justified text block...
putchar(' ');
}
```
Tokens starting with '(' or '<' are text fragments. 8-bit text starting with
'(' needs to be mapped to Unicode using the current font encoding while hex
strings starting with '<' are UTF-16 (Unicode) that need to be converted to
UTF-8:
```c
else if (buffer[0] == '(')
{ {
// Text string using an 8-bit encoding // Text string using an 8-bit encoding
if (first) first = false;
first = false;
else if (buffer[1] != ' ')
putchar(' ');
fputs(buffer + 1, stdout); for (bufptr = buffer + 1; *bufptr; bufptr ++)
put_utf8(encoding[*bufptr & 255]);
} }
else if (buffer[0] == '<')
{
// Unicode text string
first = false;
puts_utf16(buffer + 1);
}
```
Simple (8-bit) fonts include an encoding table that maps the 8-bit characters to
one of 1051 Unicode glyph names. Since each font can use a different encoding,
we look for font names starting with '/' and the "Tf" (set text font) operator
token and load that font's encoding using the
[load_encoding](#the-loadencoding-function) function:
```c
else if (buffer[0] == '/')
{
// Save name...
strncpy(name, buffer + 1, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0';
}
else if (!strcmp(buffer, "Tf") && name[0])
{
// Set font...
load_encoding(obj, name, encoding);
}
```
Finally, some text operators start a new line in a text block, so when we see
their tokens we output a newline:
```c
else if (!strcmp(buffer, "Td") || !strcmp(buffer, "TD") || !strcmp(buffer, "T*") || else if (!strcmp(buffer, "Td") || !strcmp(buffer, "TD") || !strcmp(buffer, "T*") ||
!strcmp(buffer, "\'") || !strcmp(buffer, "\"")) !strcmp(buffer, "\'") || !strcmp(buffer, "\""))
{ {
@ -980,9 +1133,160 @@ while (pdfioStreamGetToken(st, buffer, sizeof(buffer)))
first = true; first = true;
} }
} }
```
if (!first)
putchar('\n'); ### The `load_encoding` Function
The `load_encoding` function looks up the named font in the page's "Resources"
dictionary. Every PDF simple font contains an "Encoding" dictionary with a base
encoding ("WinANSI", "MacRoman", or "MacExpert") and a differences array that
lists character indexes and glyph names for an 8-bit font.
We start by initializing the encoding array to the default WinANSI encoding and
looking up the font object for the named font:
```c
static void
load_encoding(
pdfio_obj_t *page_obj, // I - Page object
const char *name, // I - Font name
int encoding[256]) // O - Encoding table
{
size_t i, j; // Looping vars
pdfio_dict_t *page_dict, // Page dictionary
*resources_dict, // Resources dictionary
*font_dict; // Font dictionary
pdfio_obj_t *font_obj, // Font object
*encoding_obj; // Encoding object
static int win_ansi[32] = // WinANSI characters from 128 to 159
{
...
};
static int mac_roman[128] = // MacRoman characters from 128 to 255
{
...
};
// Initialize the encoding to be the "standard" WinAnsi...
for (i = 0; i < 128; i ++)
encoding[i] = i;
for (i = 160; i < 256; i ++)
encoding[i] = i;
memcpy(encoding + 128, win_ansi, sizeof(win_ansi));
// Find the named font...
if ((page_dict = pdfioObjGetDict(page_obj)) == NULL)
return;
if ((resources_dict = pdfioDictGetDict(page_dict, "Resources")) == NULL)
return;
if ((font_dict = pdfioDictGetDict(resources_dict, "Font")) == NULL)
{
// Font resources not a dictionary, see if it is an object...
if ((font_obj = pdfioDictGetObj(resources_dict, "Font")) != NULL)
font_dict = pdfioObjGetDict(font_obj);
if (!font_dict)
return;
}
if ((font_obj = pdfioDictGetObj(font_dict, name)) == NULL)
return;
```
Once we have found the font we see if it has an "Encoding" dictionary:
```c
pdfio_dict_t *encoding_dict; // Encoding dictionary
if ((encoding_obj = pdfioDictGetObj(pdfioObjGetDict(font_obj), "Encoding")) == NULL)
return;
if ((encoding_dict = pdfioObjGetDict(encoding_obj)) == NULL)
return;
```
Once we have the encoding dictionary we can get the "BaseEncoding" and
"Differences" values:
```c
const char *base_encoding; // BaseEncoding name
pdfio_array_t *differences; // Differences array
// OK, have the encoding object, build the encoding using it...
base_encoding = pdfioDictGetName(encoding_dict, "BaseEncoding");
differences = pdfioDictGetArray(encoding_dict, "Differences");
```
If the base encoding is "MacRomainEncoding", we need to reset the upper 128
characters in the encoding array match it:
```c
if (base_encoding && !strcmp(base_encoding, "MacRomanEncoding"))
{
// Map upper 128
memcpy(encoding + 128, mac_roman, sizeof(mac_roman));
}
```
Then we loop through the differences array, keeping track of the current index
within the encoding array. A number indicates a new index while a name is the
Unicode glyph for the current index:
```c
typedef struct name_map_s
{
const char *name; // Character name
int unicode; // Unicode value
} name_map_t;
static name_map_t unicode_map[1051]; // List of glyph names
if (differences)
{
// Apply differences
size_t count = pdfioArrayGetSize(differences);
// Number of differences
const char *name; // Character name
size_t idx = 0; // Index in encoding array
for (i = 0; i < count; i ++)
{
switch (pdfioArrayGetType(differences, i))
{
case PDFIO_VALTYPE_NUMBER :
// Get the index of the next character...
idx = (size_t)pdfioArrayGetNumber(differences, i);
break;
case PDFIO_VALTYPE_NAME :
// Lookup name and apply to encoding...
if (idx < 0 || idx > 255)
break;
name = pdfioArrayGetName(differences, i);
for (j = 0; j < (sizeof(unicode_map) / sizeof(unicode_map[0])); j ++)
{
if (!strcmp(name, unicode_map[j].name))
{
encoding[idx] = unicode_map[j].unicode;
break;
}
}
idx ++;
break;
default :
// Do nothing for other values
break;
}
}
}
}
``` ```

View File

@ -14,8 +14,8 @@
# Common options # Common options
CFLAGS = -g $(CPPFLAGS) CFLAGS = -g $(CPPFLAGS)
#CFLAGS = -g -fsanitize=address $(CPPFLAGS) #CFLAGS = -g -fsanitize=address $(CPPFLAGS)
CPPFLAGS = -I.. -I/usr/local/include CPPFLAGS = -I.. $(shell PKG_CONFIG_PATH="..:$(PKG_CONFIG_PATH)" pkg-config pdfio --cflags)
LIBS = -L.. -L/usr/local/lib -lpdfio -lz -lm LIBS = -L.. $(shell PKG_CONFIG_PATH="..:$(PKG_CONFIG_PATH)" pkg-config pdfio --libs)
# Targets # Targets
@ -24,7 +24,8 @@ TARGETS = \
image2pdf \ image2pdf \
md2pdf \ md2pdf \
pdf2text \ pdf2text \
pdfioinfo pdfioinfo \
pdfiomerge
# Make everything # Make everything
@ -61,5 +62,10 @@ pdfioinfo: pdfioinfo.c
$(CC) $(CFLAGS) -o $@ pdfioinfo.c $(LIBS) $(CC) $(CFLAGS) -o $@ pdfioinfo.c $(LIBS)
# pdfiomerge
pdfiomerge: pdfiomerge.c
$(CC) $(CFLAGS) -o $@ pdfiomerge.c $(LIBS)
# Common dependencies... # Common dependencies...
$(TARGETS): Makefile ../pdfio.h ../pdfio-content.h $(TARGETS): Makefile ../pdfio.h ../pdfio-content.h

View File

@ -1,7 +1,7 @@
// //
// Image example for PDFio. // Image example for PDFio.
// //
// Copyright © 2023-2024 by Michael R Sweet. // Copyright © 2023-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -22,8 +22,8 @@
bool // O - True on success, false on failure bool // O - True on success, false on failure
create_pdf_image_file( create_pdf_image_file(
const char *pdfname, // I - PDF filename
const char *imagename, // I - Image filename const char *imagename, // I - Image filename
const char *pdfname, // I - PDF filename
const char *caption) // I - Caption filename const char *caption) // I - Caption filename
{ {
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
@ -36,6 +36,15 @@ create_pdf_image_file(
double tx, ty; // Position on page double tx, ty; // Position on page
// Default the caption...
if (!caption)
{
if ((caption = strrchr(imagename, '/')) != NULL)
caption ++;
else
caption = imagename;
}
// Create the PDF file... // Create the PDF file...
pdf = pdfioFileCreate(pdfname, /*version*/NULL, /*media_box*/NULL, pdf = pdfioFileCreate(pdfname, /*version*/NULL, /*media_box*/NULL,
/*crop_box*/NULL, /*error_cb*/NULL, /*crop_box*/NULL, /*error_cb*/NULL,

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
#include <pdfio.h> #include <pdfio.h>
#include <time.h> #include <time.h>
#include <math.h>
// //
@ -25,11 +26,26 @@ main(int argc, // I - Number of command-line arguments
{ {
const char *filename; // PDF filename const char *filename; // PDF filename
pdfio_file_t *pdf; // PDF file pdfio_file_t *pdf; // PDF file
const char *author; // Author name pdfio_dict_t *catalog; // Catalog dictionary
time_t creation_date; // Creation date const char *author, // Author name
struct tm *creation_tm; // Creation date/time information *creator, // Creator name
char creation_text[256]; // Creation date/time as a string *producer, // Producer name
const char *title; // Title *title; // Title
time_t creation_date, // Creation date
modification_date; // Modification date
struct tm *creation_tm, // Creation date/time information
*modification_tm; // Modification date/time information
char creation_text[256], // Creation date/time as a string
modification_text[256], // Modification date/time human fmt string
range_text[255]; // Page range text
size_t num_pages; // PDF number of pages
bool has_acroform; // Does the file have an AcroForm?
pdfio_obj_t *page; // Object
pdfio_dict_t *page_dict; // Object dictionary
size_t cur, // Current page index
prev; // Previous page index
pdfio_rect_t cur_box, // Current MediaBox
prev_box; // Previous MediaBox
// Get the filename from the command-line... // Get the filename from the command-line...
@ -48,9 +64,14 @@ main(int argc, // I - Number of command-line arguments
if (pdf == NULL) if (pdf == NULL)
return (1); return (1);
// Get the title and author... // Get the title, author, etc...
author = pdfioFileGetAuthor(pdf); catalog = pdfioFileGetCatalog(pdf);
title = pdfioFileGetTitle(pdf); author = pdfioFileGetAuthor(pdf);
creator = pdfioFileGetCreator(pdf);
has_acroform = pdfioDictGetType(catalog, "AcroForm") != PDFIO_VALTYPE_NONE;
num_pages = pdfioFileGetNumPages(pdf);
producer = pdfioFileGetProducer(pdf);
title = pdfioFileGetTitle(pdf);
// Get the creation date and convert to a string... // Get the creation date and convert to a string...
if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0) if ((creation_date = pdfioFileGetCreationDate(pdf)) > 0)
@ -63,12 +84,76 @@ main(int argc, // I - Number of command-line arguments
snprintf(creation_text, sizeof(creation_text), "-- not set --"); snprintf(creation_text, sizeof(creation_text), "-- not set --");
} }
// Get the modification date and convert to a string...
if ((modification_date = pdfioFileGetModificationDate(pdf)) > 0)
{
modification_tm = localtime(&modification_date);
strftime(modification_text, sizeof(modification_text), "%c", modification_tm);
}
else
{
snprintf(modification_text, sizeof(modification_text), "-- not set --");
}
// Print file information to stdout... // Print file information to stdout...
printf("%s:\n", filename); printf("%s:\n", filename);
printf(" Title: %s\n", title ? title : "-- not set --"); printf(" Title: %s\n", title ? title : "-- not set --");
printf(" Author: %s\n", author ? author : "-- not set --"); printf(" Author: %s\n", author ? author : "-- not set --");
printf(" Created On: %s\n", creation_text); printf(" Creator: %s\n", creator ? creator : "-- not set --");
printf(" Number Pages: %u\n", (unsigned)pdfioFileGetNumPages(pdf)); printf(" Producer: %s\n", producer ? producer : "-- not set --");
printf(" Created On: %s\n", creation_text);
printf(" Modified On: %s\n", modification_text);
printf(" Version: %s\n", pdfioFileGetVersion(pdf));
printf(" AcroForm: %s\n", has_acroform ? "Yes" : "No");
printf(" Number of Pages: %u\n", (unsigned)num_pages);
// Report the MediaBox for all of the pages
prev_box.x1 = prev_box.x2 = prev_box.y1 = prev_box.y2 = 0.0;
for (cur = 0, prev = 0; cur < num_pages; cur ++)
{
// Find the MediaBox for this page in the page tree...
for (page = pdfioFileGetPage(pdf, cur);
page != NULL;
page = pdfioDictGetObj(page_dict, "Parent"))
{
cur_box.x1 = cur_box.x2 = cur_box.y1 = cur_box.y2 = 0.0;
page_dict = pdfioObjGetDict(page);
if (pdfioDictGetRect(page_dict, "MediaBox", &cur_box))
break;
}
// If this MediaBox is different from the previous one, show the range of
// pages that have that size...
if (cur == 0 ||
fabs(cur_box.x1 - prev_box.x1) > 0.01 ||
fabs(cur_box.y1 - prev_box.y1) > 0.01 ||
fabs(cur_box.x2 - prev_box.x2) > 0.01 ||
fabs(cur_box.y2 - prev_box.y2) > 0.01)
{
if (cur > prev)
{
snprintf(range_text, sizeof(range_text), "Pages %u-%u",
(unsigned)(prev + 1), (unsigned)cur);
printf("%16s: [%g %g %g %g]\n", range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
// Start a new series of pages with the new size...
prev = cur;
prev_box = cur_box;
}
}
// Show the last range as needed...
if (cur > prev)
{
snprintf(range_text, sizeof(range_text), "Pages %u-%u",
(unsigned)(prev + 1), (unsigned)cur);
printf("%16s: [%g %g %g %g]\n", range_text,
prev_box.x1, prev_box.y1, prev_box.x2, prev_box.y2);
}
// Close the PDF file... // Close the PDF file...
pdfioFileClose(pdf); pdfioFileClose(pdf);

146
examples/pdfiomerge.c Normal file
View File

@ -0,0 +1,146 @@
//
// PDF merge program for PDFio.
//
// Copyright © 2025 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./pdfiomerge [-o OUTPUT.pdf] INPUT.pdf [... INPUT.pdf]
// ./pdfiomerge INPUT.pdf [... INPUT.pdf] >OUTPUT.pdf
//
#include <pdfio.h>
#include <string.h>
//
// Local functions...
//
static ssize_t output_cb(void *output_cbdata, const void *buffer, size_t bytes);
static int usage(FILE *out);
//
// 'main()' - Main entry.
//
int // O - Exit status
main(int argc, // I - Number of command-line arguments
char *argv[]) // I - Command-line arguments
{
int i; // Looping var
const char *opt; // Current option
pdfio_file_t *inpdf, // Input PDF file
*outpdf = NULL; // Output PDF file
// Parse command-line...
for (i = 1; i < argc; i ++)
{
if (!strcmp(argv[i], "--help"))
{
return (usage(stdout));
}
else if (!strncmp(argv[i], "--", 2))
{
fprintf(stderr, "pdfiomerge: Unknown option '%s'.\n", argv[i]);
return (usage(stderr));
}
else if (argv[i][0] == '-')
{
for (opt = argv[i] + 1; *opt; opt ++)
{
switch (*opt)
{
case 'o' : // -o OUTPUT.pdf
if (outpdf)
{
fputs("pdfiomerge: Only one output file can be specified.\n", stderr);
return (usage(stderr));
}
i ++;
if (i >= argc)
{
fputs("pdfiomerge: Missing output filename after '-o'.\n", stderr);
return (usage(stderr));
}
if ((outpdf = pdfioFileCreate(argv[i], /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
return (1);
break;
default :
fprintf(stderr, "pdfiomerge: Unknown option '-%c'.\n", *opt);
return (usage(stderr));
}
}
}
else if ((inpdf = pdfioFileOpen(argv[i], /*password_cb*/NULL, /*password_data*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
{
return (1);
}
else
{
// Copy PDF file...
size_t p, // Current page
nump; // Number of pages
if (!outpdf)
{
if ((outpdf = pdfioFileCreateOutput(output_cb, /*output_cbdata*/NULL, /*version*/NULL, /*media_box*/NULL, /*crop_box*/NULL, /*error_cb*/NULL, /*error_data*/NULL)) == NULL)
return (1);
}
for (p = 0, nump = pdfioFileGetNumPages(inpdf); p < nump; p ++)
{
if (!pdfioPageCopy(outpdf, pdfioFileGetPage(inpdf, p)))
return (1);
}
pdfioFileClose(inpdf);
}
}
if (!outpdf)
return (usage(stderr));
pdfioFileClose(outpdf);
return (0);
}
//
// 'output_cb()' - Write PDF data to the standard output...
//
static ssize_t // O - Number of bytes written
output_cb(void *output_cbdata, // I - Callback data (not used)
const void *buffer, // I - Buffer to write
size_t bytes) // I - Number of bytes to write
{
(void)output_cbdata;
return ((ssize_t)fwrite(buffer, 1, bytes, stdout));
}
//
// 'usage()' - Show program usage.
//
static int // O - Exit status
usage(FILE *out) // I - stdout or stderr
{
fputs("Usage: pdfmerge [OPTIONS] INPUT.pdf [... INPUT.pdf] >OUTPUT.pdf\n", out);
fputs("Options:\n", out);
fputs(" --help Show help.\n", out);
fputs(" -o OUTPUT.pdf Send output to filename instead of stdout.\n", out);
return (out == stdout ? 0 : 1);
}

View File

@ -21,40 +21,60 @@ if test $# != 1; then
exit 1 exit 1
fi fi
status=0
version=$1 version=$1
version_major=$(echo $1 | awk -F. '{print $1}')
version_minor=$(echo $1 | awk -F. '{print $2}')
# Check that version number has been updated everywhere... # Check that version number has been updated everywhere...
if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then if test $(grep AC_INIT configure.ac | awk '{print $2}') != "[$version],"; then
echo "Still need to update AC_INIT version in 'configure.ac'." echo "Still need to update AC_INIT version in 'configure.ac'."
exit 1 status=1
fi fi
if test $(head -4 CHANGES.md | tail -1 | awk '{print $1}') != "v$version"; then if test $(head -5 CHANGES.md | tail -1 | awk '{print $1}') != "v$version"; then
echo "Still need to update CHANGES.md version number." echo "Still need to update CHANGES.md version number."
exit 1 status=1
fi fi
if test $(head -4 CHANGES.md | tail -1 | awk '{print $3}') = "YYYY-MM-DD"; then if test $(head -5 CHANGES.md | tail -1 | awk '{print $3}') = "YYYY-MM-DD"; then
echo "Still need to update CHANGES.md release date." echo "Still need to update CHANGES.md release date."
exit 1 status=1
fi fi
if test $(grep PDFIO_VERSION= configure | awk -F \" '{print $2}') != "$version"; then if test $(grep PDFIO_VERSION= configure | awk -F \" '{print $2}') != "$version"; then
echo "Still need to run 'autoconf -f'." echo "Still need to run 'autoconf -f'."
exit 1 status=1
fi fi
if test $(grep '<version>' pdfio_native.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then 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'." echo "Still need to update version in 'pdfio_native.nuspec'."
exit 1 status=1
fi fi
if test $(grep '<version>' pdfio_native.redist.nuspec | sed -E -e '1,$s/^.*<version>([0-9.]+).*$/\1/') != "$version"; then 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'." echo "Still need to update version in 'pdfio_native.redist.nuspec'."
exit 1 status=1
fi fi
if test $(grep PDFIO_VERSION pdfio.h | awk -F \" '{print $2}') != "$version"; then if test $(grep PDFIO_VERSION pdfio.h | awk -F \" '{print $2}') != "$version"; then
echo "Still need to update PDFIO_VERSION in 'pdfio.h'." echo "Still need to update PDFIO_VERSION in 'pdfio.h'."
status=1
fi
if test $(grep PDFIO_VERSION_MAJOR pdfio.h | awk '{print $4}') != "$version_major"; then
echo "Still need to update PDFIO_VERSION_MAJOR in 'pdfio.h'."
status=1
fi
if test $(grep PDFIO_VERSION_MINOR pdfio.h | awk '{print $4}') != "$version_minor"; then
echo "Still need to update PDFIO_VERSION_MINOR in 'pdfio.h'."
status=1
fi
if test $(grep VERSION pdfio1.def | awk '{print $2}') != "$version_major.$version_minor"; then
echo "Still need to update VERSION in 'pdfio1.def'."
status=1
fi
if test $status = 1; then
exit 1 exit 1
fi fi

View File

@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="libpng_native" version="1.6.30" targetFramework="native" />
<package id="libpng_native.redist" version="1.6.30" targetFramework="native" />
<package id="zlib_native" version="1.2.11" targetFramework="native" /> <package id="zlib_native" version="1.2.11" targetFramework="native" />
<package id="zlib_native.redist" version="1.2.11" targetFramework="native" /> <package id="zlib_native.redist" version="1.2.11" targetFramework="native" />
</packages> </packages>

View File

@ -1,7 +1,7 @@
// //
// AES functions for PDFio. // AES functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -76,18 +76,18 @@ static const uint8_t Rcon[11] = // Round constants
// Local functions... // Local functions...
// //
static void AddRoundKey(size_t round, state_t *state, const uint8_t *RoundKey); static void add_round_key(size_t round, state_t *state, const uint8_t *round_key);
static void SubBytes(state_t *state); static void sub_bytes(state_t *state);
static void ShiftRows(state_t *state); static void shift_rows(state_t *state);
static uint8_t xtime(uint8_t x); static uint8_t xtime(uint8_t x);
static void MixColumns(state_t *state); static void mix_columns(state_t *state);
static uint8_t Multiply(uint8_t x, uint8_t y); static uint8_t multiply(uint8_t x, uint8_t y);
static void InvMixColumns(state_t *state); static void inv_mix_columns(state_t *state);
static void InvSubBytes(state_t *state); static void inv_sub_bytes(state_t *state);
static void InvShiftRows(state_t *state); static void inv_shift_rows(state_t *state);
static void Cipher(state_t *state, const _pdfio_aes_t *ctx); static void cipher(state_t *state, const _pdfio_aes_t *ctx);
static void InvCipher(state_t *state, const _pdfio_aes_t *ctx); static void inv_cipher(state_t *state, const _pdfio_aes_t *ctx);
static void XorWithIv(uint8_t *buf, const uint8_t *Iv); static void xor_with_iv(uint8_t *buf, const uint8_t *Iv);
// //
@ -106,7 +106,6 @@ _pdfioCryptoAESInit(
*rkptr, // Current round_key values *rkptr, // Current round_key values
*rkend, // End of round_key values *rkend, // End of round_key values
tempa[4]; // Used for the column/row operations tempa[4]; // Used for the column/row operations
// size_t roundlen = keylen + 24; // Length of round_key
size_t nwords = keylen / 4; // Number of 32-bit words in key size_t nwords = keylen / 4; // Number of 32-bit words in key
@ -188,8 +187,8 @@ _pdfioCryptoAESDecrypt(
while (len > 15) while (len > 15)
{ {
memcpy(next_iv, outbuffer, 16); memcpy(next_iv, outbuffer, 16);
InvCipher((state_t *)outbuffer, ctx); inv_cipher((state_t *)outbuffer, ctx);
XorWithIv(outbuffer, ctx->iv); xor_with_iv(outbuffer, ctx->iv);
memcpy(ctx->iv, next_iv, 16); memcpy(ctx->iv, next_iv, 16);
outbuffer += 16; outbuffer += 16;
len -= 16; len -= 16;
@ -231,8 +230,8 @@ _pdfioCryptoAESEncrypt(
while (len > 15) while (len > 15)
{ {
XorWithIv(outbuffer, iv); xor_with_iv(outbuffer, iv);
Cipher((state_t*)outbuffer, ctx); cipher((state_t*)outbuffer, ctx);
iv = outbuffer; iv = outbuffer;
outbuffer += 16; outbuffer += 16;
len -= 16; len -= 16;
@ -242,10 +241,10 @@ _pdfioCryptoAESEncrypt(
if (len > 0) if (len > 0)
{ {
// Pad the final buffer with (16 - len)... // Pad the final buffer with (16 - len)...
memset(outbuffer + len, 16 - len, 16 - len); memset(outbuffer + len, (int)(16 - len), 16 - len);
XorWithIv(outbuffer, iv); xor_with_iv(outbuffer, iv);
Cipher((state_t*)outbuffer, ctx); cipher((state_t*)outbuffer, ctx);
iv = outbuffer; iv = outbuffer;
outbytes += 16; outbytes += 16;
} }
@ -257,24 +256,32 @@ _pdfioCryptoAESEncrypt(
} }
// This function adds the round key to state. //
// 'add_round_key()' - Add the round key to state.
//
// The round key is added to the state by an XOR function. // The round key is added to the state by an XOR function.
//
static void static void
AddRoundKey(size_t round, state_t *state, const uint8_t *RoundKey) add_round_key(size_t round, // I - Which round
state_t *state, // I - Current state
const uint8_t *round_key) // I - Key
{ {
unsigned i; // Looping var unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state uint8_t *sptr = (*state)[0]; // Pointer into state
for (RoundKey += round * 16, i = 16; i > 0; i --, sptr ++, RoundKey ++) for (round_key += round * 16, i = 16; i > 0; i --, sptr ++, round_key ++)
*sptr ^= *RoundKey; *sptr ^= *round_key;
} }
// The SubBytes Function Substitutes the values in the //
// state matrix with values in an S-box. // 'sub_bytes()' - Substitute the values in the state matrix with values in an S-box.
//
static void static void
SubBytes(state_t *state) sub_bytes(state_t *state) // I - Current state
{ {
unsigned i; // Looping var unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state uint8_t *sptr = (*state)[0]; // Pointer into state
@ -284,11 +291,16 @@ SubBytes(state_t *state)
*sptr = sbox[*sptr]; *sptr = sbox[*sptr];
} }
// The ShiftRows() function shifts the rows in the state to the left.
//
// 'shift_rows()' - Shift the rows in the state to the left.
//
// Each row is shifted with different offset. // Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted. // Offset = Row number. So the first row is not shifted.
//
static void static void
ShiftRows(state_t *state) shift_rows(state_t *state) // I - Current state
{ {
uint8_t *sptr = (*state)[0]; // Pointer into state uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t temp; // Temporary value uint8_t temp; // Temporary value
@ -319,21 +331,29 @@ ShiftRows(state_t *state)
} }
static uint8_t //
xtime(uint8_t x) // 'xtime()' - Compute the AES xtime function.
//
static uint8_t // O - xtime(x)
xtime(uint8_t x) // I - Column value
{ {
return ((uint8_t)((x << 1) ^ ((x >> 7) * 0x1b))); return ((uint8_t)((x << 1) ^ ((x >> 7) * 0x1b)));
} }
// MixColumns function mixes the columns of the state matrix //
// 'mix_columns()' - Mix the columns of the state matrix.
//
static void static void
MixColumns(state_t *state) mix_columns(state_t *state) // I - Current state
{ {
unsigned i; // Looping var unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t Tmp, Tm, t; // Temporary values uint8_t Tmp, Tm, t; // Temporary values
for (i = 4; i > 0; i --, sptr += 4) for (i = 4; i > 0; i --, sptr += 4)
{ {
t = sptr[0]; t = sptr[0];
@ -357,11 +377,15 @@ MixColumns(state_t *state)
} }
// Multiply is used to multiply numbers in the field GF(2^8) //
// 'multiply()' - Multiply numbers in the field GF(2^8)
//
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary // Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way. // The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34 // See https://github.com/kokke/tiny-AES-c/pull/34
static uint8_t Multiply(uint8_t x, uint8_t y) //
static uint8_t multiply(uint8_t x, uint8_t y)
{ {
return (((y & 1) * x) ^ return (((y & 1) * x) ^
((y>>1 & 1) * xtime(x)) ^ ((y>>1 & 1) * xtime(x)) ^
@ -371,11 +395,15 @@ static uint8_t Multiply(uint8_t x, uint8_t y)
} }
// MixColumns function mixes the columns of the state matrix. //
// 'mix_columns()' - Mix the columns of the state matrix.
//
// The method used to multiply may be difficult to understand for the inexperienced. // The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information. // Please use the references to gain more information.
//
static void static void
InvMixColumns(state_t *state) inv_mix_columns(state_t *state) // I - Current state
{ {
unsigned i; // Looping var unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state uint8_t *sptr = (*state)[0]; // Pointer into state
@ -389,18 +417,20 @@ InvMixColumns(state_t *state)
c = sptr[2]; c = sptr[2];
d = sptr[3]; d = sptr[3];
*sptr++ = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); *sptr++ = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ multiply(c, 0x0d) ^ multiply(d, 0x09);
*sptr++ = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); *sptr++ = multiply(a, 0x09) ^ multiply(b, 0x0e) ^ multiply(c, 0x0b) ^ multiply(d, 0x0d);
*sptr++ = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); *sptr++ = multiply(a, 0x0d) ^ multiply(b, 0x09) ^ multiply(c, 0x0e) ^ multiply(d, 0x0b);
*sptr++ = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); *sptr++ = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^ multiply(c, 0x09) ^ multiply(d, 0x0e);
} }
} }
// The SubBytes Function Substitutes the values in the //
// state matrix with values in an S-box. // 'sub_bytes()' - Substitute the values in the state matrix with values in an S-box.
//
static void static void
InvSubBytes(state_t *state) inv_sub_bytes(state_t *state) // I - Current state
{ {
unsigned i; // Looping var unsigned i; // Looping var
uint8_t *sptr = (*state)[0]; // Pointer into state uint8_t *sptr = (*state)[0]; // Pointer into state
@ -411,8 +441,12 @@ InvSubBytes(state_t *state)
} }
//
// 'inv_shift_rows()' - Shift the rows in the state to the right.
//
static void static void
InvShiftRows(state_t *state) inv_shift_rows(state_t *state) // I - Current state
{ {
uint8_t *sptr = (*state)[0]; // Pointer into state uint8_t *sptr = (*state)[0]; // Pointer into state
uint8_t temp; // Temporary value uint8_t temp; // Temporary value
@ -443,40 +477,52 @@ InvShiftRows(state_t *state)
} }
// Cipher is the main function that encrypts the PlainText. //
// 'cipher()' - Encrypt the PlainText.
//
static void static void
Cipher(state_t *state, const _pdfio_aes_t *ctx) cipher(state_t *state, // I - Current state
const _pdfio_aes_t *ctx) // I - AES context
{ {
size_t round = 0; size_t round = 0; // Current round
// Add the First round key to the state before starting the rounds. // Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, ctx->round_key); add_round_key(0, state, ctx->round_key);
// There will be Nr rounds. // There will be Nr rounds.
// The first Nr-1 rounds are identical. // The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below. // These Nr rounds are executed in the loop below.
// Last one without MixColumns() // Last one without mix_columns()
for (round = 1; round < ctx->round_size; round ++) for (round = 1; round < ctx->round_size; round ++)
{ {
SubBytes(state); sub_bytes(state);
ShiftRows(state); shift_rows(state);
MixColumns(state); mix_columns(state);
AddRoundKey(round, state, ctx->round_key); add_round_key(round, state, ctx->round_key);
} }
// Add round key to last round // Add round key to last round
SubBytes(state); sub_bytes(state);
ShiftRows(state); shift_rows(state);
AddRoundKey(ctx->round_size, state, ctx->round_key); add_round_key(ctx->round_size, state, ctx->round_key);
} }
//
// 'inv_cipher()' - Decrypt the CipherText.
//
static void static void
InvCipher(state_t *state, const _pdfio_aes_t *ctx) inv_cipher(state_t *state, // I - Current state
const _pdfio_aes_t *ctx) // I - AES context
{ {
size_t round; size_t round; // Current round
// Add the First round key to the state before starting the rounds. // Add the First round key to the state before starting the rounds.
AddRoundKey(ctx->round_size, state, ctx->round_key); add_round_key(ctx->round_size, state, ctx->round_key);
// There will be Nr rounds. // There will be Nr rounds.
// The first Nr-1 rounds are identical. // The first Nr-1 rounds are identical.
@ -484,20 +530,25 @@ InvCipher(state_t *state, const _pdfio_aes_t *ctx)
// Last one without InvMixColumn() // Last one without InvMixColumn()
for (round = ctx->round_size - 1; ; round --) for (round = ctx->round_size - 1; ; round --)
{ {
InvShiftRows(state); inv_shift_rows(state);
InvSubBytes(state); inv_sub_bytes(state);
AddRoundKey(round, state, ctx->round_key); add_round_key(round, state, ctx->round_key);
if (round == 0) if (round == 0)
break; break;
InvMixColumns(state); inv_mix_columns(state);
} }
} }
//
// 'xor_with_iv()' - XOR a block with the initialization vector.
//
static void static void
XorWithIv(uint8_t *buf, const uint8_t *Iv) xor_with_iv(uint8_t *buf, // I - Block
const uint8_t *Iv) // I - Initialization vector
{ {
// 16-byte block... // 16-byte block...
*buf++ ^= *Iv++; *buf++ ^= *Iv++;

View File

@ -640,6 +640,8 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file
// //
// 'pdfioArrayRemove()' - Remove an array entry. // 'pdfioArrayRemove()' - Remove an array entry.
// //
// @since PDFio v1.4@
//
bool // O - `true` on success, `false` otherwise bool // O - `true` on success, `false` otherwise
pdfioArrayRemove(pdfio_array_t *a, // I - Array pdfioArrayRemove(pdfio_array_t *a, // I - Array

532
pdfio-cgats001-compat.h Normal file
View File

@ -0,0 +1,532 @@
/* CGATS001Compat-v2-micro.icc */
static unsigned char CGATS001Compat_v2_micro_icc[] = {
0,0,33,16,65,68,66,69,2,16,0,0,115,99,110,114,
67,77,89,75,76,97,98,32,7,230,0,12,0,7,0,22,
0,16,0,42,97,99,115,112,77,83,70,84,0,0,0,0,
115,97,119,115,99,116,114,108,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,246,214,0,1,0,0,0,0,211,45,
104,97,110,100,195,103,209,209,26,217,105,182,62,57,156,48,
22,179,165,88,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,4,100,101,115,99,0,0,0,180,0,0,0,95,
99,112,114,116,0,0,0,208,0,0,0,12,119,116,112,116,
0,0,0,220,0,0,0,20,65,50,66,48,0,0,0,240,
0,0,32,32,100,101,115,99,0,0,0,0,0,0,0,5,
117,67,77,89,0,0,0,0,0,0,0,0,0,0,0,0,
116,101,120,116,0,0,0,0,67,67,48,0,88,89,90,32,
0,0,0,0,0,0,246,214,0,1,0,0,0,0,211,45,
109,102,116,50,0,0,0,0,4,3,6,0,0,1,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,
0,48,0,2,0,0,10,35,16,0,22,155,28,150,35,8,
41,37,47,68,53,83,59,90,65,90,71,78,77,74,83,48,
89,25,94,245,100,188,106,133,112,43,117,193,123,79,128,187,
134,23,139,107,144,166,149,206,154,244,160,21,165,30,170,40,
175,51,180,64,185,71,190,81,195,91,200,103,205,100,210,85,
215,65,220,33,224,252,229,190,234,87,238,218,243,138,248,23,
252,104,255,255,0,0,8,214,13,206,19,98,24,129,30,6,
35,77,40,160,46,0,51,86,56,193,62,41,67,165,73,37,
78,162,84,51,89,175,95,46,100,173,106,10,111,107,116,191,
121,244,127,36,132,82,137,98,142,111,147,127,152,147,157,151,
162,172,167,211,173,14,178,88,183,186,189,52,194,199,200,90,
205,244,211,154,217,72,223,1,228,146,234,19,239,215,245,132,
251,7,255,255,0,0,9,30,14,44,19,200,24,222,30,99,
35,150,40,200,45,246,51,35,56,84,61,138,66,209,72,33,
77,126,82,234,88,89,93,211,99,74,104,190,110,47,115,148,
120,237,126,59,131,126,136,180,141,231,147,29,152,90,157,169,
163,18,168,158,174,79,180,41,186,39,192,69,198,114,204,164,
210,199,216,201,222,161,228,73,233,160,238,170,243,244,248,186,
252,225,255,255,0,0,8,135,13,57,18,115,23,60,28,123,
33,106,38,94,43,92,48,99,53,110,58,133,63,163,68,202,
73,244,79,35,84,77,89,109,94,139,99,158,104,170,109,176,
114,172,119,156,124,135,129,112,134,74,139,35,143,254,148,223,
153,201,158,194,163,209,168,251,174,67,179,163,185,36,190,202,
196,145,202,125,208,133,214,166,220,240,227,93,234,118,241,193,
249,40,255,255,255,0,128,0,128,0,214,110,127,181,126,251,
173,86,127,150,126,88,131,53,127,186,126,83,86,78,128,50,
126,192,31,100,129,254,128,28,251,120,125,171,146,104,211,247,
125,155,142,161,171,46,125,194,139,106,129,32,126,37,136,221,
84,57,126,196,134,230,28,170,128,62,134,99,248,103,123,237,
165,11,209,76,123,249,158,148,168,209,124,68,152,192,127,22,
124,213,147,218,82,110,125,139,143,116,26,73,126,145,140,106,
245,211,122,201,183,251,206,211,122,205,174,205,166,160,123,35,
166,133,125,42,123,200,159,52,80,217,124,141,151,243,24,82,
125,10,144,4,243,199,122,9,203,97,204,207,121,247,191,95,
164,205,122,74,180,169,123,179,123,1,171,70,79,144,123,201,
163,159,22,200,123,191,143,103,242,42,121,140,223,132,203,50,
121,98,208,125,163,73,121,169,195,108,122,115,122,107,184,86,
78,150,123,56,174,96,21,158,122,200,142,157,227,233,142,168,
125,57,191,208,140,54,124,173,154,204,138,30,124,141,116,50,
136,124,124,247,74,84,135,121,125,199,20,34,137,177,127,236,
224,239,140,172,141,230,189,193,138,73,138,227,152,241,136,91,
136,116,114,87,134,238,134,171,72,129,134,18,133,143,18,45,
135,202,133,165,222,108,139,13,159,8,187,88,136,200,153,179,
150,192,134,245,148,251,112,129,133,173,145,24,70,249,132,229,
142,34,16,97,134,27,137,235,220,58,137,245,176,93,185,57,
135,184,168,203,148,204,133,239,161,238,110,212,132,170,155,243,
69,169,131,235,151,169,15,9,132,165,138,43,218,113,137,45,
194,46,183,137,134,242,184,87,147,63,133,43,175,95,109,144,
131,243,167,166,68,161,131,41,161,166,14,7,131,135,137,124,
217,12,136,152,212,229,182,53,134,99,200,164,145,250,132,152,
189,153,108,133,131,103,180,155,67,209,130,165,170,33,13,68,
130,183,136,248,201,213,157,235,123,46,169,121,153,123,122,234,
136,53,149,117,123,19,101,12,146,14,123,193,62,13,143,191,
124,214,9,84,144,204,127,187,199,84,156,73,138,107,167,205,
151,198,136,7,134,185,147,217,134,42,99,150,144,153,132,246,
60,167,142,98,132,135,8,113,142,126,132,36,197,113,154,202,
153,237,165,235,150,98,149,123,132,247,146,119,145,165,98,4,
143,82,142,167,59,93,141,42,140,19,7,182,140,158,133,55,
195,166,153,193,169,229,164,50,149,94,163,134,131,66,145,135,
157,217,96,149,142,92,153,26,58,81,140,51,150,79,7,25,
139,41,132,204,194,42,153,10,186,48,162,214,148,166,177,236,
129,253,144,210,170,123,95,138,141,175,164,116,57,139,139,110,
159,13,6,174,138,21,132,132,192,255,152,123,203,106,161,198,
148,25,193,44,128,245,144,72,184,9,94,179,141,45,177,103,
56,235,138,244,165,100,6,89,137,77,132,75,177,37,173,245,
122,2,147,243,167,169,121,209,117,242,161,206,122,23,85,235,
156,180,122,225,49,88,150,201,124,2,0,239,149,92,127,195,
175,57,172,141,135,237,146,202,166,25,133,179,116,224,160,67,
132,66,84,210,155,60,131,120,48,93,152,11,131,193,0,226,
147,96,128,133,173,202,171,58,150,19,145,79,164,217,146,68,
115,118,159,2,143,29,83,154,154,5,140,217,47,124,150,201,
140,54,0,206,145,182,128,139,172,145,170,52,164,133,144,19,
163,219,159,13,114,58,158,5,154,89,82,143,152,241,150,198,
46,202,149,168,148,235,0,217,144,85,128,147,171,106,169,130,
179,137,143,32,163,20,172,85,113,80,157,63,162,160,81,202,
152,62,161,248,46,73,148,212,155,139,0,219,143,72,128,148,
170,119,169,1,195,84,142,87,162,131,186,91,112,151,156,179,
178,242,81,58,151,186,174,21,45,236,148,82,159,13,0,222,
142,134,128,150,154,49,190,221,121,79,127,198,182,195,121,6,
100,136,175,54,121,74,71,42,168,146,122,23,36,45,165,99,
123,14,0,0,143,153,128,0,152,249,189,121,134,22,127,13,
181,72,132,75,99,211,173,165,130,235,70,115,166,244,130,111,
35,202,163,124,130,239,0,0,142,213,128,0,152,46,188,21,
146,252,126,42,179,238,143,148,98,240,172,64,140,239,69,182,
165,143,139,90,35,118,161,226,138,239,0,0,142,33,128,0,
151,95,187,20,156,183,125,105,178,222,152,248,98,53,171,44,
148,44,69,34,164,102,145,119,35,60,160,141,143,228,0,0,
141,150,128,0,150,186,186,65,173,194,124,235,177,250,167,108,
97,205,170,70,162,133,68,210,163,131,159,209,35,33,159,106,
150,67,0,0,141,57,128,0,150,10,185,176,188,68,124,138,
177,69,180,40,97,126,169,128,174,82,68,159,162,198,170,52,
35,38,158,179,151,196,0,0,140,230,128,0,132,180,208,159,
120,243,108,190,198,205,120,107,83,203,189,201,120,144,56,144,
181,241,121,63,22,242,178,158,122,81,0,0,137,249,128,0,
132,84,206,246,132,213,108,102,197,80,130,218,83,105,188,24,
129,168,56,75,180,0,129,93,23,73,176,8,130,10,0,0,
138,37,128,0,132,67,205,78,144,147,108,56,195,164,141,107,
83,48,186,79,139,41,56,52,178,35,138,58,23,180,173,196,
137,41,0,0,138,73,128,0,132,35,203,251,156,155,108,55,
194,41,152,56,83,60,184,196,149,21,56,85,176,133,148,4,
24,42,171,234,144,154,0,0,138,101,128,0,131,240,202,242,
168,242,108,66,193,0,163,54,83,110,183,140,159,100,56,150,
175,80,157,234,24,158,170,120,144,165,0,0,138,121,128,0,
131,190,202,18,181,197,108,88,192,13,174,136,83,181,182,138,
170,24,56,250,174,76,165,33,25,38,169,99,145,1,0,0,
138,136,128,0,235,21,120,95,117,172,198,143,121,103,118,146,
160,155,122,106,119,154,120,244,123,117,120,218,78,61,124,157,
122,46,24,103,126,52,123,43,231,220,117,150,135,222,195,241,
118,230,133,232,158,88,120,46,132,101,118,244,121,124,131,68,
76,100,122,195,129,102,22,11,123,166,130,96,228,245,115,130,
154,56,193,46,116,236,149,151,155,232,118,93,145,116,116,232,
119,221,141,248,74,174,121,54,138,229,19,252,121,133,135,196,
226,139,114,5,172,159,190,208,115,112,165,140,153,177,116,238,
158,247,114,251,118,134,153,19,73,38,119,231,148,52,18,94,
119,220,139,241,224,194,113,11,191,52,189,12,114,105,181,200,
151,244,115,224,172,209,113,127,117,135,164,211,71,234,118,225,
158,108,17,36,118,168,139,151,223,129,112,120,210,33,187,195,
113,192,198,94,150,146,115,32,187,38,112,68,116,200,177,102,
70,235,118,28,168,223,16,59,117,202,138,249,209,115,134,135,
115,123,177,87,133,101,116,211,143,52,132,136,118,40,106,212,
131,237,119,166,66,203,131,210,121,44,13,68,133,161,122,166,
207,74,132,21,132,44,175,136,131,38,130,252,141,102,130,108,
130,15,105,23,129,246,129,111,65,44,129,242,129,3,11,242,
131,47,128,187,204,230,130,32,148,250,173,11,129,68,145,118,
139,42,128,171,142,60,103,60,128,104,139,143,63,171,128,112,
137,123,10,160,129,38,133,169,202,204,128,181,165,229,170,253,
127,222,160,68,137,59,127,80,154,224,101,154,127,25,150,30,
62,106,127,39,146,206,9,181,127,147,134,144,201,66,127,194,
183,38,169,111,126,232,175,109,135,176,126,82,167,231,100,77,
126,40,161,100,61,104,126,40,156,138,9,4,126,105,134,24,
200,52,127,42,201,23,168,66,126,72,191,65,134,106,125,151,
181,160,99,54,125,111,173,200,60,152,125,113,164,221,8,120,
125,143,133,186,184,173,149,106,113,228,156,34,146,61,115,98,
125,114,143,136,116,221,92,54,141,74,118,127,54,161,140,45,
120,8,3,238,139,88,123,49,182,255,147,72,129,49,154,137,
144,64,128,123,123,227,141,162,127,240,90,199,139,126,127,190,
53,89,138,93,127,196,3,128,136,228,128,74,181,64,145,117,
144,98,152,169,142,132,141,169,122,46,139,240,139,55,89,77,
137,236,137,71,52,47,136,204,136,38,3,22,134,234,130,22,
179,141,144,32,160,3,151,4,141,51,155,112,120,129,138,173,
151,29,87,228,136,172,147,114,51,37,135,135,145,82,2,193,
133,95,129,221,178,73,143,62,175,217,149,185,140,83,169,123,
119,61,137,196,163,94,86,209,135,201,158,101,50,96,134,132,
153,234,2,137,132,59,129,182,177,102,142,167,192,110,148,187,
139,188,184,74,118,48,137,28,176,117,85,236,135,30,170,208,
49,180,133,217,160,46,2,88,131,104,129,150,161,39,165,31,
112,232,135,104,160,39,114,94,107,178,155,186,115,225,77,78,
151,243,117,130,41,135,150,141,118,177,0,66,137,171,126,52,
160,0,163,78,126,58,134,90,158,94,125,202,106,159,153,235,
126,68,76,76,150,41,126,114,40,187,148,150,126,116,0,60,
135,218,127,249,158,179,161,163,140,195,132,230,156,194,138,139,
105,69,152,84,136,161,75,40,148,163,135,100,39,242,146,245,
134,224,0,50,134,74,128,34,157,132,160,84,154,226,131,173,
155,126,151,25,104,13,151,13,147,166,74,49,147,72,145,0,
39,88,145,123,143,118,0,46,133,35,128,31,156,138,159,124,
169,118,130,195,154,160,164,33,103,23,150,32,159,51,73,88,
146,97,155,208,38,210,144,100,150,43,0,40,132,62,128,27,
155,211,158,242,184,169,130,0,154,13,177,217,102,77,149,124,
171,165,72,175,145,184,167,171,38,95,143,176,153,237,0,36,
131,152,128,24,139,20,181,218,112,19,115,151,175,83,113,97,
90,95,169,85,114,209,62,89,164,67,116,78,27,220,162,240,
117,20,0,0,132,76,128,0,138,120,180,61,125,106,115,25,
173,150,124,255,89,217,167,107,124,81,61,228,162,62,124,151,
27,203,160,109,125,6,0,0,131,187,128,0,137,192,178,136,
137,145,114,53,171,233,135,226,88,255,165,185,134,144,61,56,
160,141,133,174,27,150,158,104,133,23,0,0,131,37,128,0,
136,247,177,65,150,121,113,106,170,160,145,137,88,50,164,101,
144,140,60,162,159,30,142,229,27,110,156,173,140,81,0,0,
130,194,128,0,136,101,176,88,163,151,112,222,169,174,159,42,
87,167,163,100,155,105,60,52,158,16,153,106,27,92,155,60,
144,208,0,0,130,140,128,0,135,225,175,208,177,149,112,130,
169,4,171,225,87,60,162,144,167,47,59,221,157,49,163,199,
27,68,154,81,146,112,0,0,130,90,128,0,118,47,199,165,
111,81,96,137,191,214,112,85,73,93,184,132,113,146,47,37,
178,156,114,188,14,114,176,12,115,215,0,0,129,66,128,0,
118,37,198,9,123,103,96,145,189,252,122,224,73,95,182,83,
122,140,47,79,176,27,122,242,15,16,173,6,123,149,0,0,
129,65,128,0,118,26,196,25,134,225,96,81,188,12,133,19,
73,32,180,78,131,219,47,67,173,249,131,127,15,152,170,90,
131,15,0,0,129,64,128,0,117,232,194,153,146,128,96,44,
186,112,143,187,72,249,178,157,141,124,47,72,172,34,141,8,
16,17,168,31,136,88,0,0,129,63,128,0,117,180,193,153,
158,127,96,36,185,65,154,198,72,250,177,80,152,1,47,90,
170,197,151,16,16,120,166,98,139,34,0,0,129,63,128,0,
117,138,192,236,171,6,96,44,184,93,166,88,73,12,176,73,
163,22,47,127,169,175,158,184,16,201,165,41,139,89,0,0,
129,62,128,0,215,58,112,125,107,98,182,48,114,183,110,9,
147,81,116,191,112,146,110,79,118,168,115,18,69,242,120,110,
117,99,17,41,122,4,118,21,212,37,109,25,125,61,179,174,
111,175,125,19,145,41,114,6,125,27,108,110,116,58,125,82,
68,67,118,17,125,118,15,115,119,100,124,211,209,76,106,99,
143,18,176,232,109,33,140,81,142,189,111,175,137,216,106,111,
114,31,135,215,66,173,115,252,133,236,14,12,117,65,131,106,
206,240,104,112,160,225,174,148,107,62,155,216,140,130,109,225,
150,255,104,143,112,110,146,160,65,64,114,73,142,92,12,238,
115,154,136,44,205,74,103,27,178,184,172,233,105,229,171,138,
138,211,108,132,164,100,103,24,111,33,157,249,64,30,112,232,
153,0,12,32,114,96,136,51,204,67,102,85,196,179,171,201,
105,11,187,103,137,134,107,139,178,19,101,232,110,39,169,242,
63,37,113,29,163,2,11,134,113,150,135,202,191,51,125,253,
105,204,162,88,126,40,108,203,130,244,126,117,111,124,96,224,
126,227,114,20,58,181,127,161,116,87,6,250,129,135,117,232,
189,59,122,243,122,101,160,121,123,114,122,237,129,38,123,241,
123,66,95,70,124,136,123,210,57,87,125,61,124,104,6,56,
126,240,124,161,186,231,120,104,138,147,158,7,121,10,136,216,
126,247,121,180,135,50,93,119,122,128,133,202,55,236,123,44,
132,147,5,122,125,75,129,223,184,229,118,148,155,6,156,7,
119,69,151,69,125,15,119,255,147,122,91,227,120,220,144,16,
54,189,121,122,141,190,4,240,123,230,131,75,183,122,117,78,
171,160,154,140,117,255,165,231,123,150,118,180,160,18,90,158,
119,159,154,248,53,206,120,29,151,54,4,139,123,11,131,18,
182,151,116,140,188,154,153,124,117,45,180,249,122,105,117,198,
173,42,89,148,118,175,166,195,52,247,119,27,159,91,4,58,
122,75,130,219,168,10,140,102,104,152,142,157,138,129,107,167,
114,84,137,23,110,98,83,5,136,13,112,248,46,178,136,50,
114,232,0,0,134,1,119,195,166,152,137,165,119,142,140,252,
136,21,120,113,112,187,134,199,121,68,81,165,133,209,122,43,
45,147,133,199,122,111,0,0,131,203,125,115,164,228,135,58,
134,91,139,30,133,208,133,102,111,24,132,156,132,87,80,93,
131,190,131,134,44,163,131,149,130,248,0,0,129,242,128,0,
163,51,133,129,149,157,137,135,132,33,146,220,109,118,130,247,
143,243,78,252,130,37,141,109,43,168,129,222,139,252,0,0,
128,80,128,0,162,3,132,83,164,223,136,69,130,249,160,109,
108,52,129,194,155,194,77,229,128,243,151,242,42,242,128,117,
148,112,0,0,128,0,128,0,161,66,131,152,180,173,135,81,
130,59,174,155,107,41,128,225,168,60,77,1,128,10,163,180,
42,74,127,120,154,175,0,0,128,0,128,0,145,188,155,172,
103,193,122,194,152,25,106,174,97,34,149,28,109,90,68,88,
146,185,111,205,33,58,147,6,113,3,0,0,128,158,125,23,
144,178,153,85,117,117,121,161,149,241,118,113,96,7,146,235,
119,201,67,104,144,128,120,192,32,163,144,90,120,208,0,0,
128,83,127,160,143,100,151,24,131,16,120,32,147,209,130,146,
94,175,144,218,130,3,66,85,142,130,129,188,32,1,142,10,
128,235,0,0,128,0,128,0,142,45,149,108,144,194,118,226,
146,54,142,170,93,120,143,61,140,135,65,117,140,210,138,222,
31,136,140,18,137,181,0,0,128,0,128,0,141,62,148,80,
158,204,117,254,145,26,155,76,92,129,142,10,151,180,64,146,
139,158,149,70,31,15,138,141,145,227,0,0,128,0,128,0,
140,159,147,161,173,59,117,64,144,98,168,119,91,182,141,51,
163,160,63,227,138,184,160,184,30,155,137,127,148,146,0,0,
128,0,128,0,124,171,172,10,102,190,103,126,167,39,105,111,
80,11,162,201,107,245,53,57,159,120,110,24,19,165,158,208,
111,20,0,0,128,0,128,0,123,249,170,24,116,224,102,230,
165,24,117,140,79,132,160,128,117,94,52,226,156,255,118,88,
19,187,155,193,119,9,0,0,128,0,128,0,123,42,167,228,
127,253,101,234,162,244,127,85,78,167,158,94,127,12,52,67,
154,215,126,238,19,159,153,39,127,38,0,0,128,0,128,0,
122,87,166,68,140,182,101,22,161,89,139,59,77,209,156,183,
137,52,51,180,153,17,136,91,19,135,150,246,134,122,0,0,
128,0,128,0,121,194,165,30,152,238,100,130,160,44,150,73,
77,54,155,119,147,192,51,58,151,189,146,136,19,131,149,48,
139,58,0,0,128,0,128,0,121,80,164,119,166,54,100,51,
159,99,162,165,76,201,154,118,159,46,50,220,150,162,156,195,
19,98,147,245,141,27,0,0,128,0,128,0,104,127,189,181,
101,120,84,142,183,253,103,201,62,206,178,149,106,4,37,57,
175,32,107,133,7,201,168,185,110,103,0,0,128,0,128,0,
104,19,188,17,113,56,84,117,181,220,113,249,62,221,175,253,
114,239,37,150,172,20,115,158,8,71,165,174,118,13,0,0,
128,0,128,0,103,206,185,191,124,128,84,14,179,133,124,35,
62,146,173,145,124,7,37,152,169,115,124,23,8,149,163,10,
125,3,0,0,128,0,128,0,103,132,183,243,135,237,83,215,
177,160,134,186,62,83,171,151,133,157,37,151,167,69,133,155,
8,210,160,225,134,24,0,0,128,0,128,0,103,71,182,193,
147,197,83,203,176,53,145,186,62,65,170,12,143,228,37,150,
165,157,143,124,9,4,159,47,134,25,0,0,128,0,128,0,
103,34,182,5,159,118,83,218,175,42,156,247,62,66,168,216,
154,228,37,162,164,81,151,207,9,28,157,237,134,41,0,0,
128,0,128,0,195,134,104,39,97,50,165,143,107,98,101,100,
133,177,110,87,105,86,99,107,113,26,109,23,61,117,115,78,
112,105,10,197,118,146,113,155,192,120,99,196,114,159,163,37,
103,127,116,32,131,159,106,221,117,164,97,161,109,247,119,42,
59,237,112,35,120,108,9,208,115,227,120,197,189,150,96,56,
131,182,160,101,100,60,130,212,129,66,103,227,130,4,95,178,
107,69,129,80,58,117,109,94,128,220,8,252,114,54,127,181,
187,44,93,116,149,0,158,28,97,167,145,220,127,29,101,119,
142,191,93,247,105,0,139,222,57,48,106,248,136,228,8,98,
114,207,133,15,185,130,91,150,166,49,156,129,95,216,160,239,
125,137,99,178,155,159,92,152,103,80,150,226,56,49,105,27,
147,108,7,231,114,249,133,88,184,132,90,105,183,86,155,115,
94,161,175,238,124,98,98,101,168,131,91,140,105,1,162,44,
55,84,103,155,156,250,7,143,115,57,133,29,173,93,116,242,
96,48,147,55,118,74,100,152,118,92,119,176,104,140,86,142,
121,30,108,57,50,50,122,128,111,45,2,87,126,109,114,86,
171,85,112,240,112,28,145,91,114,194,114,35,116,164,116,109,
115,251,85,28,118,12,117,186,49,17,119,53,119,0,2,43,
125,195,121,110,168,240,109,150,128,2,142,230,111,169,127,232,
114,129,113,144,127,187,83,92,115,109,127,135,47,194,116,98,
127,74,1,226,125,94,127,32,166,228,106,254,143,238,140,233,
109,54,141,221,112,164,111,63,139,156,81,229,113,51,137,125,
46,184,113,233,136,69,1,188,125,70,129,22,165,119,105,58,
159,231,139,118,107,126,155,238,111,59,109,141,151,183,80,175,
111,144,148,16,45,231,112,10,145,123,1,153,125,77,129,20,
164,153,104,24,176,2,138,114,106,82,170,42,110,35,111,42,
164,16,79,186,110,75,159,42,45,29,110,151,153,133,1,123,
125,127,129,0,151,185,130,155,95,107,128,155,130,26,99,200,
102,131,129,250,103,171,73,25,130,36,107,48,37,224,131,72,
109,106,0,0,128,15,119,32,150,37,126,232,109,252,126,233,
126,222,112,71,100,238,126,239,114,85,71,211,127,49,116,50,
36,254,127,224,117,40,0,0,128,0,125,1,148,89,123,191,
124,214,124,252,123,247,125,43,99,88,124,49,125,116,70,157,
124,147,125,171,36,61,124,241,125,162,0,0,128,0,128,0,
146,160,121,84,139,48,123,96,121,170,138,25,97,182,121,248,
136,132,69,80,122,100,134,203,35,88,122,135,134,48,0,0,
128,0,128,0,145,107,119,175,153,210,122,36,120,16,150,250,
96,132,120,91,147,207,68,71,120,200,145,64,34,188,120,174,
142,195,0,0,128,0,128,0,144,162,120,93,168,186,121,57,
120,129,164,90,95,144,120,148,159,155,67,117,120,204,156,88,
34,21,119,83,148,183,0,0,128,0,128,0,130,198,145,99,
94,162,109,209,143,77,98,193,86,12,141,196,102,123,58,184,
140,221,105,176,24,115,141,200,107,28,0,0,128,0,124,73,
129,132,142,31,107,235,108,146,140,92,110,64,84,240,138,218,
113,211,57,230,137,229,115,138,24,36,138,59,114,239,0,0,
128,0,127,132,128,22,139,40,121,37,107,6,137,153,122,194,
83,165,136,48,123,82,56,243,135,70,123,12,23,189,135,58,
123,75,0,0,128,0,128,0,126,190,136,215,135,50,105,197,
135,98,134,150,82,127,133,248,133,151,56,76,132,242,133,17,
23,130,132,161,132,185,0,0,128,0,128,0,125,190,135,83,
147,232,104,226,133,228,145,251,81,131,132,98,143,208,55,90,
131,82,142,43,23,27,130,174,140,195,0,0,128,0,128,0,
125,14,134,79,161,162,104,29,132,220,158,145,80,187,131,56,
154,254,54,173,130,12,153,42,22,191,129,62,143,48,0,0,
128,0,128,0,110,138,161,103,93,113,91,64,158,36,97,61,
69,98,155,115,100,180,43,134,154,24,103,87,11,238,152,5,
105,90,0,0,128,0,127,174,109,133,158,164,105,164,90,112,
155,103,107,227,68,195,152,129,109,252,43,62,150,221,111,137,
12,25,148,105,113,67,0,0,128,0,128,0,108,132,155,200,
117,179,89,77,152,172,118,164,67,211,149,207,119,119,42,162,
148,24,120,5,12,19,145,67,121,90,0,0,128,0,128,0,
107,134,153,143,129,221,88,92,150,130,129,173,66,235,147,154,
129,73,42,24,145,180,129,15,12,0,142,157,128,215,0,0,
128,0,128,0,106,209,152,11,143,26,87,176,149,0,141,197,
66,62,146,3,140,0,41,140,143,253,139,52,12,1,140,140,
133,216,0,0,128,0,128,0,106,74,151,18,154,199,87,83,
147,243,152,208,65,214,144,186,150,118,41,61,142,132,149,10,
11,233,139,8,136,14,0,0,128,0,128,0,90,217,179,10,
91,159,72,146,175,35,94,239,52,17,171,191,97,242,26,122,
171,135,99,103,2,147,158,4,105,195,0,0,128,0,128,0,
89,242,176,188,102,234,72,22,172,122,104,233,51,223,168,163,
106,202,26,208,167,186,107,134,3,13,154,120,113,121,0,0,
128,0,128,0,89,104,173,222,113,237,71,119,169,158,114,221,
51,113,165,180,115,185,26,205,164,114,115,225,3,91,151,84,
119,101,0,0,128,0,128,0,88,225,171,136,125,19,71,16,
167,63,125,46,51,16,163,58,125,29,26,204,161,154,125,2,
3,150,148,166,126,188,0,0,128,0,128,0,88,127,169,250,
136,100,70,230,165,137,135,210,50,233,161,96,135,19,26,190,
159,136,135,59,3,199,146,155,130,61,0,0,128,0,128,0,
88,66,168,239,147,235,70,221,164,67,146,236,50,224,159,237,
145,204,26,206,157,221,143,133,3,222,145,7,130,157,0,0,
128,0,128,0,176,156,94,195,87,72,149,115,98,233,92,217,
120,99,102,205,98,34,88,182,106,104,103,28,53,1,108,159,
107,102,5,152,118,139,109,251,173,120,88,199,104,44,147,0,
93,173,107,60,118,78,98,35,110,48,86,244,102,42,113,1,
51,155,104,36,115,88,5,62,119,36,117,123,170,129,83,218,
120,230,144,69,89,66,121,155,116,3,94,37,122,79,85,27,
98,129,123,12,50,72,100,77,123,164,4,235,119,176,124,186,
167,244,79,239,137,125,142,8,85,186,136,49,114,1,90,231,
134,220,83,144,95,120,133,94,49,50,97,23,132,122,4,171,
120,27,130,105,166,29,76,244,153,245,140,119,83,4,150,99,
112,161,88,98,146,223,82,98,93,18,143,209,48,103,94,148,
141,222,4,128,120,101,131,11,165,1,75,27,170,73,139,111,
81,61,164,132,111,168,86,158,159,0,81,141,91,86,154,125,
47,181,92,180,151,6,4,90,120,165,130,241,156,106,106,192,
86,220,132,107,109,76,92,113,105,223,111,205,97,148,76,63,
114,46,102,77,41,113,115,222,105,208,0,133,127,32,112,194,
154,6,101,52,102,67,130,120,104,112,105,168,104,51,107,92,
108,201,74,235,109,253,111,166,40,136,111,123,113,155,0,121,
127,52,119,248,151,123,96,136,117,195,127,250,100,52,119,22,
102,27,103,124,120,55,73,62,106,97,121,62,39,94,107,182,
121,207,0,101,127,85,125,231,149,85,92,216,133,116,125,254,
96,208,133,45,100,78,100,86,131,234,71,229,103,88,131,7,
38,123,104,160,130,168,0,90,127,104,128,21,147,206,90,13,
148,102,124,145,94,51,145,245,98,253,97,219,143,75,70,199,
100,234,141,18,37,213,102,40,139,156,0,82,127,118,128,55,
146,221,88,74,163,183,123,146,92,123,159,112,97,255,96,28,
154,247,69,241,99,40,151,144,37,38,100,94,147,151,0,74,
127,132,128,50,136,83,119,164,86,119,114,249,120,152,91,224,
90,200,121,197,96,207,63,24,123,14,101,47,28,228,124,239,
103,177,0,0,128,0,118,153,134,86,114,117,100,134,113,39,
116,9,104,12,89,55,117,132,107,62,61,232,118,232,110,10,
28,63,120,131,111,111,0,0,128,0,124,173,132,83,110,13,
114,153,111,34,112,8,116,89,87,166,113,196,117,216,60,195,
115,69,119,23,27,177,116,190,119,163,0,0,128,0,128,0,
130,119,106,149,129,11,109,119,108,205,130,35,86,4,110,179,
128,158,59,134,112,55,128,143,26,244,113,202,129,140,0,0,
128,0,128,0,129,36,103,242,142,177,108,51,106,79,140,179,
84,214,108,70,139,126,58,135,109,187,138,52,26,128,111,130,
137,83,0,0,128,0,128,0,128,71,102,62,157,11,107,73,
104,167,154,28,83,238,106,146,150,196,57,196,107,241,148,173,
25,237,109,244,142,180,0,0,128,0,128,0,116,102,133,188,
85,206,97,9,133,77,90,232,74,225,133,71,95,143,48,197,
133,211,99,100,15,223,135,31,101,112,0,0,128,0,123,165,
114,183,129,2,98,152,95,147,129,17,102,24,73,174,129,44,
105,65,47,247,129,153,107,201,15,177,130,194,109,55,0,0,
128,0,127,79,113,14,124,224,111,119,93,220,125,64,113,115,
72,73,125,135,115,40,46,243,125,235,116,107,15,86,127,106,
117,126,0,0,128,0,128,0,111,138,121,149,124,88,92,114,
122,42,124,241,71,9,122,128,125,56,46,66,122,185,125,99,
15,42,124,159,125,207,0,0,128,0,128,0,110,95,119,23,
138,202,91,117,119,207,138,10,70,19,120,28,137,0,45,94,
120,38,135,245,14,222,122,140,135,102,0,0,128,0,128,0,
109,149,121,89,150,108,90,170,121,119,148,158,69,84,118,115,
146,87,44,190,118,78,145,129,14,137,120,225,137,87,0,0,
128,0,128,0,96,221,149,113,84,82,79,24,147,218,89,8,
58,133,146,240,93,68,32,234,148,5,96,5,5,223,142,72,
100,242,0,0,128,0,127,76,95,109,145,55,96,1,77,234,
143,234,99,97,57,154,142,232,102,93,32,131,143,118,104,29,
6,7,138,144,108,199,0,0,128,0,128,0,94,36,141,70,
107,194,76,143,140,50,109,216,56,136,139,63,111,156,31,218,
139,127,112,111,5,254,135,60,116,52,0,0,128,0,128,0,
92,237,138,30,122,85,75,113,137,51,120,146,55,133,136,57,
121,39,31,91,136,14,123,110,5,246,132,117,123,118,0,0,
128,0,128,0,91,246,135,172,131,111,74,141,134,231,131,84,
54,186,133,222,130,234,30,202,133,72,130,209,6,11,130,149,
129,136,0,0,128,0,128,0,91,71,134,25,143,189,74,30,
133,105,142,218,54,108,132,29,141,96,30,192,131,24,140,170,
6,8,129,66,132,20,0,0,128,0,128,0,77,84,167,52,
81,238,60,225,164,229,86,29,41,102,163,178,89,171,15,187,
165,245,91,30,0,0,143,192,102,232,0,0,128,0,128,0,
76,0,163,132,92,179,59,195,161,83,95,199,40,139,159,206,
98,84,15,179,160,234,99,35,0,0,141,58,110,94,0,0,
128,0,128,0,75,38,159,158,103,107,58,198,157,140,105,113,
39,202,155,249,107,4,15,139,156,80,107,83,0,0,138,237,
113,76,0,0,128,0,128,0,74,77,156,103,114,86,58,10,
154,119,115,117,39,52,152,188,116,26,15,133,152,85,116,52,
0,33,136,220,120,138,0,0,128,0,128,0,73,149,153,242,
125,109,57,132,152,31,125,200,38,211,150,60,125,178,15,95,
149,64,125,240,0,63,135,71,127,146,0,0,128,0,128,0,
73,42,152,77,136,216,57,58,150,125,136,168,38,173,148,107,
136,30,15,114,147,1,134,175,0,80,134,33,128,54,0,0,
128,0,128,0,158,251,83,213,77,213,134,82,88,228,84,162,
107,209,93,194,91,44,78,135,98,47,97,81,44,208,100,17,
102,124,1,123,125,128,107,25,155,163,75,156,94,40,131,186,
81,193,98,171,105,163,87,113,102,252,76,186,92,94,107,9,
43,127,94,109,110,93,1,148,125,85,114,218,152,136,68,110,
110,104,128,251,75,123,112,169,103,104,81,210,114,214,74,252,
87,33,116,232,42,89,89,132,118,188,1,168,125,50,122,87,
149,187,62,95,127,39,126,199,70,68,127,37,101,151,77,45,
126,223,73,179,82,194,126,247,41,128,85,129,127,2,1,184,
125,23,128,57,147,155,57,144,141,195,125,55,66,42,139,246,
100,109,73,147,138,142,72,188,79,81,137,33,40,239,82,91,
136,147,1,196,125,4,129,50,146,51,54,15,157,180,124,41,
63,34,153,134,99,178,70,225,149,233,72,51,76,186,147,48,
40,130,79,242,145,93,1,205,124,245,129,56,140,172,95,13,
78,0,118,82,98,194,84,136,93,217,102,97,90,197,66,68,
105,148,96,126,32,220,108,22,100,131,0,0,128,0,112,38,
137,166,87,69,92,206,116,43,92,3,97,107,92,39,96,73,
101,193,64,246,99,194,105,173,32,26,102,123,108,69,0,0,
128,0,119,103,134,217,80,99,107,214,113,151,85,234,110,117,
90,18,90,193,112,228,63,85,94,121,115,18,31,23,97,180,
116,108,0,0,128,0,125,112,132,131,74,171,122,184,111,150,
80,207,123,118,88,87,86,26,124,13,62,35,89,237,124,145,
30,107,94,103,125,47,0,0,128,0,127,202,130,209,70,47,
137,129,110,40,76,204,136,111,87,33,82,108,135,54,61,36,
86,56,134,85,29,248,92,10,133,252,0,0,128,0,128,0,
129,180,66,231,151,227,109,43,73,215,149,17,86,72,79,164,
146,42,60,121,83,93,144,56,29,122,90,173,141,217,0,0,
128,0,128,0,122,59,107,13,78,9,101,239,109,128,84,51,
79,94,111,248,90,17,53,66,114,34,95,58,20,89,117,114,
98,63,0,0,128,0,118,48,119,113,99,183,91,125,99,224,
103,9,96,10,77,191,105,251,100,71,52,13,108,44,103,232,
19,212,112,104,105,245,0,0,128,0,124,88,117,9,93,44,
105,29,97,175,97,41,107,247,76,39,100,134,110,150,50,241,
102,174,112,192,19,113,108,243,114,23,0,0,128,0,128,0,
114,243,87,180,118,223,95,234,92,47,120,23,74,129,95,218,
121,16,49,195,97,217,121,223,18,225,106,136,122,228,0,0,
128,0,128,0,113,120,83,101,132,147,94,155,88,59,132,58,
73,87,92,22,131,141,48,207,93,197,131,35,18,148,104,170,
130,227,0,0,128,0,128,0,112,124,80,47,145,214,93,175,
85,72,144,47,72,123,89,48,142,34,48,28,90,139,141,35,
18,32,103,124,136,248,0,0,128,0,128,0,103,70,120,84,
77,117,84,232,121,161,83,63,64,11,123,28,88,172,38,210,
124,209,92,243,8,233,127,70,96,200,0,0,128,0,123,34,
100,213,113,135,89,167,83,25,115,126,94,24,62,167,117,48,
98,36,37,231,118,126,101,44,8,193,123,161,104,123,0,0,
128,0,127,24,98,194,107,80,102,30,81,34,109,203,105,23,
61,29,111,188,107,190,36,205,112,158,109,148,8,119,120,180,
112,173,0,0,128,0,128,0,96,242,102,12,114,156,79,130,
104,236,116,47,59,191,107,0,117,116,36,18,107,82,118,75,
8,91,118,101,120,166,0,0,128,0,128,0,95,146,97,206,
130,67,78,97,101,12,129,141,58,188,103,46,129,182,35,42,
103,31,127,144,8,40,116,175,127,220,0,0,128,0,128,0,
94,161,94,147,139,177,77,129,98,16,138,232,57,241,100,48,
137,174,34,137,99,241,137,147,7,229,115,133,132,115,0,0,
128,0,128,0,84,37,135,119,75,195,67,155,135,178,81,48,
48,13,136,148,86,4,22,132,139,57,88,202,1,153,133,28,
97,193,0,0,128,0,126,220,82,13,129,47,86,229,65,224,
129,244,91,44,46,166,130,201,94,217,21,210,132,77,96,184,
1,163,129,218,105,124,0,0,128,0,128,0,80,85,123,60,
98,71,64,40,124,97,101,75,45,71,125,64,103,204,20,248,
126,25,104,210,1,154,127,79,110,152,0,0,128,0,128,0,
78,197,118,17,114,119,62,180,119,146,116,238,44,4,120,106,
118,37,20,83,121,5,113,106,1,147,125,213,117,185,0,0,
128,0,128,0,77,131,113,206,121,100,61,140,115,179,122,30,
43,13,116,126,122,121,19,183,117,62,122,187,1,164,125,57,
126,172,0,0,128,0,128,0,76,158,110,125,133,67,60,216,
112,196,133,20,42,153,113,118,132,123,19,130,114,86,132,73,
1,142,125,96,129,13,0,0,128,0,128,0,64,153,153,69,
72,171,49,169,152,158,77,168,30,160,153,228,81,115,6,90,
154,60,83,247,0,0,130,209,102,37,0,0,128,0,128,0,
62,163,147,140,82,227,47,151,147,139,86,215,28,182,148,160,
89,168,5,183,147,71,91,203,0,0,128,103,109,199,0,0,
128,0,128,0,61,68,141,206,93,80,46,3,142,29,96,44,
27,97,143,2,98,7,5,78,141,29,99,223,0,0,128,0,
117,60,0,0,128,0,128,0,59,244,136,146,104,3,44,191,
137,80,105,212,26,96,137,238,106,205,5,45,136,30,108,152,
0,0,128,0,123,80,0,0,128,0,128,0,58,216,132,48,
114,233,43,191,133,110,115,203,25,174,133,218,116,14,4,250,
132,101,118,30,0,0,128,0,127,34,0,0,128,0,128,0,
58,13,128,170,126,39,42,253,130,99,126,61,25,57,130,182,
126,10,4,241,129,178,126,213,0,0,128,0,128,0,0,0,
128,0,128,0,0,0,255,255,0,0,255,255,0,0,255,255
};

View File

@ -47,7 +47,7 @@ _pdfioFileConsume(pdfio_file_t *pdf, // I - PDF file
// `false` to halt. // `false` to halt.
// //
bool // O - `false` to stop bool // O - `false` to stop, `true` to continue
_pdfioFileDefaultError( _pdfioFileDefaultError(
pdfio_file_t *pdf, // I - PDF file pdfio_file_t *pdf, // I - PDF file
const char *message, // I - Error message const char *message, // I - Error message
@ -57,7 +57,7 @@ _pdfioFileDefaultError(
fprintf(stderr, "%s: %s\n", pdf->filename, message); fprintf(stderr, "%s: %s\n", pdf->filename, message);
return (false); return (!strncmp(message, "WARNING:", 8));
} }
@ -98,7 +98,7 @@ _pdfioFileFlush(pdfio_file_t *pdf) // I - PDF file
if (!write_buffer(pdf, pdf->buffer, (size_t)(pdf->bufptr - pdf->buffer))) if (!write_buffer(pdf, pdf->buffer, (size_t)(pdf->bufptr - pdf->buffer)))
return (false); return (false);
pdf->bufpos += pdf->bufptr - pdf->buffer; pdf->bufpos += (off_t)(pdf->bufptr - pdf->buffer);
} }
pdf->bufptr = pdf->buffer; pdf->bufptr = pdf->buffer;
@ -134,19 +134,20 @@ _pdfioFileGetChar(pdfio_file_t *pdf) // I - PDF file
bool // O - `true` on success, `false` on error bool // O - `true` on success, `false` on error
_pdfioFileGets(pdfio_file_t *pdf, // I - PDF file _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
char *buffer, // I - Line buffer char *buffer, // I - Line buffer
size_t bufsize) // I - Size of line buffer size_t bufsize, // I - Size of line buffer
bool discard) // I - OK to discard excess line chars?
{ {
bool eol = false; // End of line? bool eol = false; // End of line?
char *bufptr = buffer, // Pointer into buffer char *bufptr = buffer, // Pointer into buffer
*bufend = buffer + bufsize - 1; // Pointer to end of buffer *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, 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))); PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu, discard=%s) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", pdf, buffer, (unsigned long)bufsize, discard ? "true" : "false", (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer)));
while (!eol) while (!eol)
{ {
// If there are characters ready in the buffer, use them... // If there are characters ready in the buffer, use them...
while (!eol && pdf->bufptr < pdf->bufend && bufptr < bufend) while (!eol && pdf->bufptr < pdf->bufend)
{ {
char ch = *(pdf->bufptr++); // Next character in buffer char ch = *(pdf->bufptr++); // Next character in buffer
@ -168,8 +169,10 @@ _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
pdf->bufptr ++; pdf->bufptr ++;
} }
} }
else else if (bufptr < bufend)
*bufptr++ = ch; *bufptr++ = ch;
else if (!discard)
break;
} }
// Fill the read buffer as needed... // Fill the read buffer as needed...
@ -216,7 +219,7 @@ _pdfioFilePeek(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG("_pdfioFilePeek: Sliding buffer, total=%ld\n", (long)total); PDFIO_DEBUG("_pdfioFilePeek: Sliding buffer, total=%ld\n", (long)total);
memmove(pdf->buffer, pdf->bufptr, total); memmove(pdf->buffer, pdf->bufptr, total);
pdf->bufpos += pdf->bufptr - pdf->buffer; pdf->bufpos += (off_t)(pdf->bufptr - pdf->buffer);
pdf->bufptr = pdf->buffer; pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + total; pdf->bufend = pdf->buffer + total;
@ -317,14 +320,14 @@ _pdfioFileRead(pdfio_file_t *pdf, // I - PDF file
// Advance current position in file as needed... // Advance current position in file as needed...
if (pdf->bufend) if (pdf->bufend)
{ {
pdf->bufpos += pdf->bufend - pdf->buffer; pdf->bufpos += (off_t)(pdf->bufend - pdf->buffer);
pdf->bufptr = pdf->bufend = NULL; pdf->bufptr = pdf->bufend = NULL;
} }
// Read directly from the file... // Read directly from the file...
if ((rbytes = read_buffer(pdf, bufptr, bytes)) > 0) if ((rbytes = read_buffer(pdf, bufptr, bytes)) > 0)
{ {
pdf->bufpos += rbytes; pdf->bufpos += (off_t)rbytes;
continue; continue;
} }
else if (rbytes < 0 && (errno == EINTR || errno == EAGAIN)) else if (rbytes < 0 && (errno == EINTR || errno == EAGAIN))
@ -361,14 +364,14 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
// Adjust offset for relative seeks... // Adjust offset for relative seeks...
if (whence == SEEK_CUR) if (whence == SEEK_CUR)
{ {
offset += pdf->bufpos + (pdf->bufptr - pdf->buffer); offset += pdf->bufpos + (off_t)(pdf->bufptr - pdf->buffer);
whence = SEEK_SET; whence = SEEK_SET;
} }
if (pdf->mode == _PDFIO_MODE_READ) if (pdf->mode == _PDFIO_MODE_READ)
{ {
// Reading, see if we already have the data we need... // Reading, see if we already have the data we need...
if (whence != SEEK_END && offset >= pdf->bufpos && pdf->bufend && offset < (pdf->bufpos + pdf->bufend - pdf->buffer)) if (whence != SEEK_END && offset >= pdf->bufpos && pdf->bufend && offset < (off_t)(pdf->bufpos + pdf->bufend - pdf->buffer))
{ {
// Yes, seek within existing buffer... // Yes, seek within existing buffer...
pdf->bufptr = pdf->buffer + (offset - pdf->bufpos); pdf->bufptr = pdf->buffer + (offset - pdf->bufpos);
@ -424,7 +427,7 @@ off_t // O - Offset from beginning of file
_pdfioFileTell(pdfio_file_t *pdf) // I - PDF file _pdfioFileTell(pdfio_file_t *pdf) // I - PDF file
{ {
if (pdf->bufptr) if (pdf->bufptr)
return (pdf->bufpos + (pdf->bufptr - pdf->buffer)); return (pdf->bufpos + (off_t)(pdf->bufptr - pdf->buffer));
else else
return (pdf->bufpos); return (pdf->bufpos);
} }
@ -452,7 +455,7 @@ _pdfioFileWrite(pdfio_file_t *pdf, // I - PDF file
if (!write_buffer(pdf, buffer, bytes)) if (!write_buffer(pdf, buffer, bytes))
return (false); return (false);
pdf->bufpos += bytes; pdf->bufpos += (off_t)bytes;
return (true); return (true);
} }
@ -478,7 +481,7 @@ fill_buffer(pdfio_file_t *pdf) // I - PDF file
// Advance current position in file as needed... // Advance current position in file as needed...
if (pdf->bufend) if (pdf->bufend)
pdf->bufpos += pdf->bufend - pdf->buffer; pdf->bufpos += (off_t)(pdf->bufend - pdf->buffer);
// Try reading from the file... // Try reading from the file...
if ((bytes = read_buffer(pdf, pdf->buffer, sizeof(pdf->buffer))) <= 0) if ((bytes = read_buffer(pdf, pdf->buffer, sizeof(pdf->buffer))) <= 0)

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
// //
// Public content header file for PDFio. // Public content header file for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -23,7 +23,8 @@ typedef enum pdfio_cs_e // Standard color spaces
{ {
PDFIO_CS_ADOBE, // AdobeRGB 1998 PDFIO_CS_ADOBE, // AdobeRGB 1998
PDFIO_CS_P3_D65, // Display P3 PDFIO_CS_P3_D65, // Display P3
PDFIO_CS_SRGB // sRGB PDFIO_CS_SRGB, // sRGB
PDFIO_CS_CGATS001 // CGATS001 (CMYK)
} pdfio_cs_t; } pdfio_cs_t;
typedef enum pdfio_linecap_e // Line capping modes typedef enum pdfio_linecap_e // Line capping modes
@ -120,15 +121,18 @@ extern bool pdfioContentTextMoveLine(pdfio_stream_t *st, double tx, double ty)
extern bool pdfioContentTextMoveTo(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 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 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 pdfioContentTextNewLineShowf(pdfio_stream_t *st, double ws, double cs, bool unicode, const char *format, ...) _PDFIO_PUBLIC;
extern bool pdfioContentTextNextLine(pdfio_stream_t *st) _PDFIO_PUBLIC; 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 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); extern bool pdfioContentTextShowf(pdfio_stream_t *st, bool unicode, const char *format, ...) _PDFIO_PUBLIC;
extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) _PDFIO_PUBLIC; extern bool pdfioContentTextShowJustified(pdfio_stream_t *st, bool unicode, size_t num_fragments, const double *offsets, const char * const *fragments) _PDFIO_PUBLIC;
// Resource helpers... // Resource helpers...
extern void pdfioFileAddOutputIntent(pdfio_file_t *pdf, const char *subtype, const char *condition, const char *cond_id, const char *reg_name, const char *info, pdfio_obj_t *profile) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromBase(pdfio_file_t *pdf, const char *name) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromData(pdfio_file_t *pdf, const void *data, size_t datasize, bool unicode) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateFontObjFromFile(pdfio_file_t *pdf, const char *filename, bool unicode) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateICCObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t datalen, size_t num_colors) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateICCObjFromFile(pdfio_file_t *pdf, const char *filename, size_t num_colors) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateImageObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t width, size_t height, size_t num_colors, pdfio_array_t *color_data, bool alpha, bool interpolate) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateImageObjFromData(pdfio_file_t *pdf, const unsigned char *data, size_t width, size_t height, size_t num_colors, pdfio_array_t *color_data, bool alpha, bool interpolate) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateImageObjFromFile(pdfio_file_t *pdf, const char *filename, bool interpolate) _PDFIO_PUBLIC; extern pdfio_obj_t *pdfioFileCreateImageObjFromFile(pdfio_file_t *pdf, const char *filename, bool interpolate) _PDFIO_PUBLIC;

View File

@ -1,7 +1,7 @@
// //
// Cryptographic support functions for PDFio. // Cryptographic support functions for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -98,7 +98,7 @@ static uint8_t pdf_passpad[32] = // Padding for passwords
static void decrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]); static void decrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]);
static void encrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]); static void encrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]);
static void make_file_key(pdfio_encryption_t encryption, pdfio_permission_t permissions, const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *owner_key, uint8_t file_key[16]); static void make_file_key(pdfio_encryption_t encryption, pdfio_permission_t permissions, const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *owner_key, bool encrypt_metadata, uint8_t file_key[16]);
static void make_owner_key(pdfio_encryption_t encryption, const uint8_t *owner_pad, const uint8_t *user_pad, uint8_t owner_key[32]); static void make_owner_key(pdfio_encryption_t encryption, const uint8_t *owner_pad, const uint8_t *user_pad, uint8_t owner_key[32]);
static void make_user_key(const unsigned char *file_id, size_t file_idlen, uint8_t user_key[32]); static void make_user_key(const unsigned char *file_id, size_t file_idlen, uint8_t user_key[32]);
static void pad_password(const char *password, uint8_t pad[32]); static void pad_password(const char *password, uint8_t pad[32]);
@ -158,7 +158,7 @@ _pdfioCryptoLock(
// Generate the encryption key // Generate the encryption key
file_id = pdfioArrayGetBinary(pdf->id_array, 0, &file_idlen); file_id = pdfioArrayGetBinary(pdf->id_array, 0, &file_idlen);
make_file_key(encryption, permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->file_key); make_file_key(encryption, permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->encrypt_metadata, pdf->file_key);
pdf->file_keylen = 16; pdf->file_keylen = 16;
// Generate the user key... // Generate the user key...
@ -409,13 +409,6 @@ _pdfioCryptoMakeReader(
uint8_t data[21]; // Key data uint8_t data[21]; // Key data
_pdfio_md5_t md5; // MD5 state _pdfio_md5_t md5; // MD5 state
uint8_t digest[16]; // MD5 digest value 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); 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);
@ -427,81 +420,29 @@ _pdfioCryptoMakeReader(
return (NULL); 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) switch (pdf->encryption)
{ {
default : default :
_pdfioFileError(pdf, "Unsupported encryption algorithm.");
*ivlen = 0; *ivlen = 0;
return (NULL); return (NULL);
case PDFIO_ENCRYPTION_RC4_40 : case PDFIO_ENCRYPTION_RC4_40 :
// Copy the key data for the MD5 hash. // Copy the key data for the MD5 hash.
memcpy(data, file_key, 16); memcpy(data, pdf->file_key, 5);
data[16] = (uint8_t)obj->number; data[5] = (uint8_t)obj->number;
data[17] = (uint8_t)(obj->number >> 8); data[6] = (uint8_t)(obj->number >> 8);
data[18] = (uint8_t)(obj->number >> 16); data[7] = (uint8_t)(obj->number >> 16);
data[19] = (uint8_t)obj->generation; data[8] = (uint8_t)obj->generation;
data[20] = (uint8_t)(obj->generation >> 8); data[9] = (uint8_t)(obj->generation >> 8);
// Hash it... // Hash it...
_pdfioCryptoMD5Init(&md5); _pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, data, sizeof(data)); _pdfioCryptoMD5Append(&md5, data, 10);
_pdfioCryptoMD5Finish(&md5, digest); _pdfioCryptoMD5Finish(&md5, digest);
// Initialize the RC4 context using 40 bits of the digest... // Initialize the RC4 context using 80 bits of the digest...
_pdfioCryptoRC4Init(&ctx->rc4, digest, 5); _pdfioCryptoRC4Init(&ctx->rc4, digest, 10);
*ivlen = 0; *ivlen = 0;
return ((_pdfio_crypto_cb_t)_pdfioCryptoRC4Crypt); return ((_pdfio_crypto_cb_t)_pdfioCryptoRC4Crypt);
@ -515,7 +456,7 @@ _pdfioCryptoMakeReader(
case PDFIO_ENCRYPTION_RC4_128 : case PDFIO_ENCRYPTION_RC4_128 :
// Copy the key data for the MD5 hash. // Copy the key data for the MD5 hash.
memcpy(data, file_key, 16); memcpy(data, pdf->file_key, 16);
data[16] = (uint8_t)obj->number; data[16] = (uint8_t)obj->number;
data[17] = (uint8_t)(obj->number >> 8); data[17] = (uint8_t)(obj->number >> 8);
data[18] = (uint8_t)(obj->number >> 16); data[18] = (uint8_t)(obj->number >> 16);
@ -641,6 +582,8 @@ _pdfioCryptoUnlock(
file_idlen; // Length of file ID file_idlen; // Length of file ID
_pdfio_md5_t md5; // MD5 context _pdfio_md5_t md5; // MD5 context
uint8_t file_digest[16]; // MD5 digest of file ID and pad uint8_t file_digest[16]; // MD5 digest of file ID and pad
double p; // Permissions value as a double
_pdfio_value_t *value; // Encrypt dictionary value, if any
// See if we support the type of encryption specified by the Encrypt object // See if we support the type of encryption specified by the Encrypt object
@ -656,7 +599,12 @@ _pdfioCryptoUnlock(
revision = (int)pdfioDictGetNumber(encrypt_dict, "R"); revision = (int)pdfioDictGetNumber(encrypt_dict, "R");
length = (int)pdfioDictGetNumber(encrypt_dict, "Length"); 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 ((value = _pdfioDictGetValue(encrypt_dict, "EncryptMetadata")) != NULL && value->type == PDFIO_VALTYPE_BOOLEAN)
pdf->encrypt_metadata = value->value.boolean;
else
pdf->encrypt_metadata = true;
PDFIO_DEBUG("_pdfioCryptoUnlock: handler=%p(%s), version=%d, revision=%d, length=%d, encrypt_metadata=%s\n", (void *)handler, handler ? handler : "(null)", version, revision, length, pdf->encrypt_metadata ? "true" : "false");
if (!handler || strcmp(handler, "Standard")) if (!handler || strcmp(handler, "Standard"))
{ {
@ -748,8 +696,13 @@ _pdfioCryptoUnlock(
// Grab the remaining values we need to unlock the PDF... // Grab the remaining values we need to unlock the PDF...
pdf->file_keylen = (size_t)(length / 8); pdf->file_keylen = (size_t)(length / 8);
pdf->permissions = (pdfio_permission_t)pdfioDictGetNumber(encrypt_dict, "P");
p = pdfioDictGetNumber(encrypt_dict, "P");
PDFIO_DEBUG("_pdfioCryptoUnlock: P=%.0f\n", p);
if (p < 0x7fffffff) // Handle integers > 2^31-1
pdf->permissions = (pdfio_permission_t)p;
else
pdf->permissions = (pdfio_permission_t)(p - 4294967296.0);
PDFIO_DEBUG("_pdfioCryptoUnlock: permissions=%d\n", pdf->permissions); PDFIO_DEBUG("_pdfioCryptoUnlock: permissions=%d\n", pdf->permissions);
owner_key = pdfioDictGetBinary(encrypt_dict, "O", &owner_keylen); owner_key = pdfioDictGetBinary(encrypt_dict, "O", &owner_keylen);
@ -821,7 +774,7 @@ _pdfioCryptoUnlock(
make_owner_key(pdf->encryption, pad, pdf->owner_key, user_pad); make_owner_key(pdf->encryption, pad, pdf->owner_key, user_pad);
PDFIO_DEBUG("_pdfioCryptoUnlock: Upad=%02X%02X%02X%02X...%02X%02X%02X%02X\n", user_pad[0], user_pad[1], user_pad[2], user_pad[3], user_pad[28], user_pad[29], user_pad[30], user_pad[31]); PDFIO_DEBUG("_pdfioCryptoUnlock: Upad=%02X%02X%02X%02X...%02X%02X%02X%02X\n", user_pad[0], user_pad[1], user_pad[2], user_pad[3], user_pad[28], user_pad[29], user_pad[30], user_pad[31]);
make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, user_pad, pdf->owner_key, file_key); make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->encrypt_metadata, file_key);
PDFIO_DEBUG("_pdfioCryptoUnlock: Fown=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]); PDFIO_DEBUG("_pdfioCryptoUnlock: Fown=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]);
make_user_key(file_id, file_idlen, own_user_key); make_user_key(file_id, file_idlen, own_user_key);
@ -839,7 +792,7 @@ _pdfioCryptoUnlock(
} }
// 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); make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, pad, pdf->owner_key, pdf->encrypt_metadata, file_key);
PDFIO_DEBUG("_pdfioCryptoUnlock: Fuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]); 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, own_user_key); make_user_key(file_id, file_idlen, own_user_key);
@ -971,6 +924,8 @@ make_file_key(
size_t file_idlen, // I - Length of file ID size_t file_idlen, // I - Length of file ID
const uint8_t *user_pad, // I - Padded user password const uint8_t *user_pad, // I - Padded user password
const uint8_t *owner_key, // I - Owner key const uint8_t *owner_key, // I - Owner key
bool encrypt_metadata,
// I - Encrypt metadata?
uint8_t file_key[16]) // O - Encryption key uint8_t file_key[16]) // O - Encryption key
{ {
size_t i; // Looping var size_t i; // Looping var
@ -984,13 +939,25 @@ make_file_key(
perm_bytes[2] = (uint8_t)(permissions >> 16); perm_bytes[2] = (uint8_t)(permissions >> 16);
perm_bytes[3] = (uint8_t)(permissions >> 24); perm_bytes[3] = (uint8_t)(permissions >> 24);
PDFIO_DEBUG("make_file_key: user_pad[32]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", user_pad[0], user_pad[1], user_pad[2], user_pad[3], user_pad[28], user_pad[29], user_pad[30], user_pad[31]);
PDFIO_DEBUG("make_file_key: owner_key[32]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", owner_key[0], owner_key[1], owner_key[2], owner_key[3], owner_key[28], owner_key[29], owner_key[30], owner_key[31]);
PDFIO_DEBUG("make_file_key: permissions(%d)=<%02X%02X%02X%02X>\n", permissions, perm_bytes[0], perm_bytes[1], perm_bytes[2], perm_bytes[3]);
_pdfioCryptoMD5Init(&md5); _pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, user_pad, 32); _pdfioCryptoMD5Append(&md5, user_pad, 32);
_pdfioCryptoMD5Append(&md5, owner_key, 32); _pdfioCryptoMD5Append(&md5, owner_key, 32);
_pdfioCryptoMD5Append(&md5, perm_bytes, 4); _pdfioCryptoMD5Append(&md5, perm_bytes, 4);
_pdfioCryptoMD5Append(&md5, file_id, file_idlen); _pdfioCryptoMD5Append(&md5, file_id, file_idlen);
if (!encrypt_metadata)
{
uint8_t meta_bytes[4] = { 0xff, 0xff, 0xff, 0xff };
// Metadata bytes
_pdfioCryptoMD5Append(&md5, meta_bytes, 4);
}
_pdfioCryptoMD5Finish(&md5, digest); _pdfioCryptoMD5Finish(&md5, digest);
PDFIO_DEBUG("make_file_key: first md5=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", digest[0], digest[1], digest[2], digest[3], digest[12], digest[13], digest[14], digest[15]);
if (encryption != PDFIO_ENCRYPTION_RC4_40) if (encryption != PDFIO_ENCRYPTION_RC4_40)
{ {
// MD5 the result 50 times.. // MD5 the result 50 times..
@ -1002,6 +969,8 @@ make_file_key(
} }
} }
PDFIO_DEBUG("make_file_key: file_key[16]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", digest[0], digest[1], digest[2], digest[3], digest[12], digest[13], digest[14], digest[15]);
memcpy(file_key, digest, 16); memcpy(file_key, digest, 16);
} }
@ -1052,9 +1021,11 @@ make_owner_key(
// Encrypt 20 times... // Encrypt 20 times...
uint8_t encrypt_key[16]; // RC4 encryption key uint8_t encrypt_key[16]; // RC4 encryption key
for (i = 0; i < 20; i ++) for (i = 20; i > 0;)
{ {
// XOR each byte in the digest with the loop counter to make a key... // XOR each byte in the digest with the loop counter to make a key...
i --;
for (j = 0; j < sizeof(encrypt_key); j ++) for (j = 0; j < sizeof(encrypt_key); j ++)
encrypt_key[j] = (uint8_t)(digest[j] ^ i); encrypt_key[j] = (uint8_t)(digest[j] ^ i);

View File

@ -1,7 +1,7 @@
// //
// PDF dictionary functions for PDFio. // PDF dictionary functions for PDFio.
// //
// Copyright © 2021-2024 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -20,6 +20,8 @@ static int compare_pairs(_pdfio_pair_t *a, _pdfio_pair_t *b);
// //
// 'pdfioDictClear()' - Remove a key/value pair from a dictionary. // 'pdfioDictClear()' - Remove a key/value pair from a dictionary.
// //
// @since PDFio v1.4@
//
bool // O - `true` if cleared, `false` otherwise bool // O - `true` if cleared, `false` otherwise
pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary pdfioDictClear(pdfio_dict_t *dict, // I - Dictionary
@ -345,6 +347,8 @@ pdfioDictGetDict(pdfio_dict_t *dict, // I - Dictionary
// //
// 'pdfioDictGetKey()' - Get the key for the specified pair. // 'pdfioDictGetKey()' - Get the key for the specified pair.
// //
// @since PDFio v1.4@
//
const char * // O - Key for specified pair const char * // O - Key for specified pair
pdfioDictGetKey(pdfio_dict_t *dict, // I - Dictionary pdfioDictGetKey(pdfio_dict_t *dict, // I - Dictionary
@ -375,6 +379,8 @@ pdfioDictGetName(pdfio_dict_t *dict, // I - Dictionary
// //
// 'pdfioDictGetNumPairs()' - Get the number of key/value pairs in a dictionary. // 'pdfioDictGetNumPairs()' - Get the number of key/value pairs in a dictionary.
// //
// @since PDFio v1.4@
//
size_t // O - Number of pairs size_t // O - Number of pairs
pdfioDictGetNumPairs(pdfio_dict_t *dict)// I - Dictionary pdfioDictGetNumPairs(pdfio_dict_t *dict)// I - Dictionary
@ -465,127 +471,12 @@ pdfioDictGetString(pdfio_dict_t *dict, // I - Dictionary
else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096) else if (value && value->type == PDFIO_VALTYPE_BINARY && value->value.binary.datalen < 4096)
{ {
// Convert binary string to regular string... // Convert binary string to regular string...
char temp[4096], // Temporary string char temp[4096]; // Temporary UTF-8 string
*tempptr; // Pointer into temporary string
unsigned char *dataptr; // Pointer into the data string
if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\377\376", 2)) if (!(value->value.binary.datalen & 1) && (!memcmp(value->value.binary.data, "\376\377", 2) || !memcmp(value->value.binary.data, "\377\376", 2)))
{ {
// Copy UTF-16 BE // Copy UTF-16...
int ch; // Unicode character _pdfio_utf16cpy(temp, value->value.binary.data, value->value.binary.datalen, sizeof(temp));
size_t remaining; // Remaining bytes
for (dataptr = value->value.binary.data + 2, remaining = value->value.binary.datalen - 2, tempptr = temp; remaining > 1 && tempptr < (temp + sizeof(temp) - 5); dataptr += 2, remaining -= 2)
{
ch = (dataptr[0] << 8) | dataptr[1];
if (ch >= 0xd800 && ch <= 0xdbff && remaining > 3)
{
// Multi-word UTF-16 char...
int lch; // Lower bits
lch = (dataptr[2] << 8) | dataptr[3];
if (lch < 0xdc00 || lch >= 0xdfff)
break;
ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
dataptr += 2;
remaining -= 2;
}
else if (ch >= 0xfffe)
{
continue;
}
if (ch < 128)
{
// ASCII
*tempptr++ = (char)ch;
}
else if (ch < 4096)
{
// 2-byte UTF-8
*tempptr++ = (char)(0xc0 | (ch >> 6));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else if (ch < 65536)
{
// 3-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 12));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 4-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 18));
*tempptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
}
*tempptr = '\0';
}
else if (!(value->value.binary.datalen & 1) && !memcmp(value->value.binary.data, "\376\377", 2))
{
// Copy UTF-16 LE
int ch; // Unicode character
size_t remaining; // Remaining bytes
for (dataptr = value->value.binary.data + 2, remaining = value->value.binary.datalen - 2, tempptr = temp; remaining > 1 && tempptr < (temp + sizeof(temp) - 5); dataptr += 2, remaining -= 2)
{
ch = (dataptr[1] << 8) | dataptr[0];
if (ch >= 0xd800 && ch <= 0xdbff && remaining > 3)
{
// Multi-word UTF-16 char...
int lch; // Lower bits
lch = (dataptr[3] << 8) | dataptr[2];
if (lch < 0xdc00 || lch >= 0xdfff)
break;
ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
dataptr += 2;
remaining -= 2;
}
else if (ch >= 0xfffe)
{
continue;
}
if (ch < 128)
{
// ASCII
*tempptr++ = (char)ch;
}
else if (ch < 4096)
{
// 2-byte UTF-8
*tempptr++ = (char)(0xc0 | (ch >> 6));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else if (ch < 65536)
{
// 3-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 12));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 4-byte UTF-8
*tempptr++ = (char)(0xe0 | (ch >> 18));
*tempptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
}
*tempptr = '\0';
} }
else else
{ {
@ -677,6 +568,8 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary
// The iteration continues as long as the callback returns `true` or all keys // The iteration continues as long as the callback returns `true` or all keys
// have been iterated. // have been iterated.
// //
// @since PDFio v1.1@
//
void void
pdfioDictIterateKeys( pdfioDictIterateKeys(
@ -737,11 +630,6 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
_pdfioFileError(pdf, "Invalid dictionary contents."); _pdfioFileError(pdf, "Invalid dictionary contents.");
break; break;
} }
else if (_pdfioDictGetValue(dict, key + 1))
{
_pdfioFileError(pdf, "Duplicate dictionary key '%s'.", key + 1);
return (NULL);
}
// Then get the next value... // Then get the next value...
PDFIO_DEBUG("_pdfioDictRead: Reading value for '%s'.\n", key + 1); PDFIO_DEBUG("_pdfioDictRead: Reading value for '%s'.\n", key + 1);
@ -751,8 +639,17 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file
_pdfioFileError(pdf, "Missing value for dictionary key '%s'.", key + 1); _pdfioFileError(pdf, "Missing value for dictionary key '%s'.", key + 1);
break; break;
} }
else if (_pdfioDictGetValue(dict, key + 1))
if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value)) {
// Issue 118: Discard duplicate key/value pairs, in the future this will
// be a warning message...
_pdfioValueDelete(&value);
if (_pdfioFileError(pdf, "WARNING: Discarding value for duplicate dictionary key '%s'.", key + 1))
continue;
else
break;
}
else if (!_pdfioDictSetValue(dict, pdfioStringCreate(pdf, key + 1), &value))
break; break;
PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key); PDFIO_DEBUG("_pdfioDictRead: Set %s.\n", key);
@ -1168,7 +1065,7 @@ _pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary
// Write all of the key/value pairs... // Write all of the key/value pairs...
for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++) for (i = dict->num_pairs, pair = dict->pairs; i > 0; i --, pair ++)
{ {
if (!_pdfioFilePrintf(pdf, "/%s", pair->key)) if (!_pdfioFilePrintf(pdf, "%N", pair->key))
return (false); return (false);
if (length && !strcmp(pair->key, "Length") && pair->value.type == PDFIO_VALTYPE_NUMBER && pair->value.value.number <= 0.0) if (length && !strcmp(pair->key, "Length") && pair->value.type == PDFIO_VALTYPE_NUMBER && pair->value.value.number <= 0.0)

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
// //
// MD5 functions for PDFio. // MD5 functions for PDFio.
// //
// Copyright © 2021 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// Copyright © 1999 Aladdin Enterprises. All rights reserved. // Copyright © 1999 Aladdin Enterprises. All rights reserved.
// //
// This software is provided 'as-is', without any express or implied // This software is provided 'as-is', without any express or implied
@ -108,231 +108,285 @@
#define T63 0x2ad7d2bb #define T63 0x2ad7d2bb
#define T64 0xeb86d391 #define T64 0xeb86d391
//
// Use the unoptimized (big-endian) implementation if we don't know the
// endian-ness of the platform.
//
#ifdef __BYTE_ORDER__
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define ARCH_IS_BIG_ENDIAN 0 // Use little endian optimized version
# else
# define ARCH_IS_BIG_ENDIAN 1 // Use generic version
# endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#elif !defined(ARCH_IS_BIG_ENDIAN)
# define ARCH_IS_BIG_ENDIAN 1 // Use generic version
#endif // !ARCH_IS_BIG_ENDIAN
//
// 'md5_process()' - Hash a block of data.
//
static void static void
md5_process(_pdfio_md5_t *pms, const uint8_t *data /*[64]*/) md5_process(_pdfio_md5_t *pms, // I - MD5 state
const uint8_t *data/*[64]*/)// I - Data
{ {
uint32_t uint32_t a = pms->abcd[0], // First word of state
a = pms->abcd[0], b = pms->abcd[1], b = pms->abcd[1], // Second word of state
c = pms->abcd[2], d = pms->abcd[3]; c = pms->abcd[2], // Third word of state
uint32_t t; d = pms->abcd[3]; // Fourth word of state
uint32_t t; // Temporary state
#ifndef ARCH_IS_BIG_ENDIAN
# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */
#endif
#if ARCH_IS_BIG_ENDIAN #if ARCH_IS_BIG_ENDIAN
// On big-endian machines, we must arrange the bytes in the right
// order. (This also works on machines of unknown byte order.)
uint32_t X[16]; // Little-endian representation
const uint8_t *xp; // Pointer into data
int i; // Looping var
/* for (i = 0, xp = data; i < 16; i ++, xp += 4)
* On big-endian machines, we must arrange the bytes in the right X[i] = xp[0] + (unsigned)(xp[1] << 8) + (unsigned)(xp[2] << 16) + (unsigned)(xp[3] << 24);
* order. (This also works on machines of unknown byte order.)
*/
uint32_t X[16];
const uint8_t *xp = data;
int i;
for (i = 0; i < 16; ++i, xp += 4)
X[i] = xp[0] + (unsigned)(xp[1] << 8) + (unsigned)(xp[2] << 16) + (unsigned)(xp[3] << 24);
#else /* !ARCH_IS_BIG_ENDIAN */ #else /* !ARCH_IS_BIG_ENDIAN */
// On little-endian machines, we can process properly aligned data without copying it.
uint32_t xbuf[16]; // Aligned buffer
const uint32_t *X; // Pointer to little-endian representation
/* if (!((data - (const uint8_t *)0) & 3))
* On little-endian machines, we can process properly aligned data {
* without copying it. // data is properly aligned, use it directly...
*/ X = (const uint32_t *)data;
uint32_t xbuf[16]; }
const uint32_t *X; else
{
if (!((data - (const uint8_t *)0) & 3)) { // data is not aligned, copy to the aligned buffer...
/* data are properly aligned */ memcpy(xbuf, data, 64);
X = (const uint32_t *)data; X = xbuf;
} else { }
/* not aligned */ #endif // ARCH_IS_BIG_ENDIAN
memcpy(xbuf, data, 64);
X = xbuf;
}
#endif
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
/* Round 1. */ // Round 1.
/* Let [abcd k s i] denote the operation // Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ // a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s).
#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
#define SET(a, b, c, d, k, s, Ti)\ #define SET(a, b, c, d, k, s, Ti) t = a + F(b,c,d) + X[k] + Ti; a = ROTATE_LEFT(t, s) + b
t = a + F(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b // Do the following 16 operations.
/* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1);
SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2);
SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3);
SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4);
SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5);
SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6);
SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7);
SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8);
SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9);
SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10);
SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11);
SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12);
SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13);
SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14);
SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15);
SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16);
SET(b, c, d, a, 15, 22, T16);
#undef SET #undef SET
/* Round 2. */ // Round 2.
/* Let [abcd k s i] denote the operation // Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ // a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s).
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti)\ #define SET(a, b, c, d, k, s, Ti) t = a + G(b,c,d) + X[k] + Ti; a = ROTATE_LEFT(t, s) + b
t = a + G(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b // Do the following 16 operations.
/* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17);
SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18);
SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19);
SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20);
SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21);
SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22);
SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23);
SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24);
SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25);
SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26);
SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27);
SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28);
SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29);
SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30);
SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31);
SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32);
SET(b, c, d, a, 12, 20, T32);
#undef SET #undef SET
/* Round 3. */ // Round 3.
/* Let [abcd k s t] denote the operation // Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ // a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s).
#define H(x, y, z) ((x) ^ (y) ^ (z)) #define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti)\ #define SET(a, b, c, d, k, s, Ti) t = a + H(b,c,d) + X[k] + Ti; a = ROTATE_LEFT(t, s) + b
t = a + H(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b // Do the following 16 operations.
/* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33);
SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34);
SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35);
SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36);
SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37);
SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38);
SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39);
SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40);
SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41);
SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42);
SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43);
SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44);
SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45);
SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46);
SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47);
SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48);
SET(b, c, d, a, 2, 23, T48);
#undef SET #undef SET
/* Round 4. */ // Round 4.
/* Let [abcd k s t] denote the operation // Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ // a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s).
#define I(x, y, z) ((y) ^ ((x) | ~(z))) #define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti)\ #define SET(a, b, c, d, k, s, Ti) t = a + I(b,c,d) + X[k] + Ti; a = ROTATE_LEFT(t, s) + b
t = a + I(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b // Do the following 16 operations.
/* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49);
SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50);
SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51);
SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52);
SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53);
SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54);
SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55);
SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56);
SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57);
SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58);
SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59);
SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60);
SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61);
SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62);
SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63);
SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64);
SET(b, c, d, a, 9, 21, T64);
#undef SET #undef SET
/* Then perform the following additions. (That is increment each // Then perform the following additions. (That is increment each of the four
of the four registers by the value it had before this block // registers by the value it had before this block was started.)
was started.) */ pms->abcd[0] += a;
pms->abcd[0] += a; pms->abcd[1] += b;
pms->abcd[1] += b; pms->abcd[2] += c;
pms->abcd[2] += c; pms->abcd[3] += d;
pms->abcd[3] += d;
} }
//
// '_pdfioCryptoMD5Init()' - Initialize an MD5 hash.
//
void void
_pdfioCryptoMD5Init(_pdfio_md5_t *pms) _pdfioCryptoMD5Init(_pdfio_md5_t *pms) // I - MD5 state
{ {
pms->count[0] = pms->count[1] = 0; pms->count[0] = pms->count[1] = 0;
pms->abcd[0] = 0x67452301; pms->abcd[0] = 0x67452301;
pms->abcd[1] = 0xefcdab89; pms->abcd[1] = 0xefcdab89;
pms->abcd[2] = 0x98badcfe; pms->abcd[2] = 0x98badcfe;
pms->abcd[3] = 0x10325476; pms->abcd[3] = 0x10325476;
} }
//
// '_pdfioCryptoMD5Append()' - Append bytes to the MD5 hash.
//
void void
_pdfioCryptoMD5Append(_pdfio_md5_t *pms, const uint8_t *data, size_t nbytes) _pdfioCryptoMD5Append(
_pdfio_md5_t *pms, // I - MD5 state
const uint8_t *data, // I - Data to add
size_t nbytes) // I - Number of bytes
{ {
const uint8_t *p = data; const uint8_t *p = data; // Pointer into data
size_t left = nbytes; size_t left = nbytes; // Remaining bytes
size_t offset = (pms->count[0] >> 3) & 63; size_t offset = (pms->count[0] >> 3) & 63;
uint32_t nbits = (uint32_t)(nbytes << 3); // Offset into state
uint32_t nbits = (uint32_t)(nbytes << 3);
// Number of bits to add
if (nbytes == 0)
return;
/* Update the message length. */ if (nbytes == 0)
pms->count[1] += (unsigned)(nbytes >> 29); return;
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
/* Process an initial partial block. */ // Update the message length.
if (offset) { pms->count[1] += (unsigned)(nbytes >> 29);
size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes); pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1] ++;
memcpy(pms->buf + offset, p, copy); // Process an initial partial block.
if (offset + copy < 64) if (offset)
return; {
p += copy; size_t copy = ((offset + nbytes) > 64 ? 64 - offset : nbytes);
left -= copy; // Number of bytes to copy
md5_process(pms, pms->buf);
}
/* Process full blocks. */ memcpy(pms->buf + offset, p, copy);
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
/* Process a final partial block. */ if ((offset + copy) < 64)
if (left) return;
memcpy(pms->buf, p, left);
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
// Process full blocks.
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
// Copy a final partial block.
if (left)
memcpy(pms->buf, p, left);
} }
//
// '_pdfioCryptoMD5Finish()' - Finalize the MD5 hash.
//
void void
_pdfioCryptoMD5Finish(_pdfio_md5_t *pms, uint8_t digest[16]) _pdfioCryptoMD5Finish(
_pdfio_md5_t *pms, // I - MD5 state
uint8_t digest[16]) // O - Digest value
{ {
static const uint8_t pad[64] = { int i; // Looping var
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, uint8_t data[8]; // Digest length data
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, static const uint8_t pad[64] = // Padding bytes
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
uint8_t data[8]; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
int i; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* Save the length before padding. */
for (i = 0; i < 8; ++i) // Save the length before padding.
data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3)); for (i = 0; i < 8; ++i)
/* Pad to 56 bytes mod 64. */ data[i] = (uint8_t)(pms->count[i >> 2] >> ((i & 3) << 3));
_pdfioCryptoMD5Append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */ // Pad to 56 bytes mod 64.
_pdfioCryptoMD5Append(pms, data, 8); _pdfioCryptoMD5Append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
for (i = 0; i < 16; ++i)
digest[i] = (uint8_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); // Append the length.
_pdfioCryptoMD5Append(pms, data, 8);
// Copy the digest from the state...
for (i = 0; i < 16; ++i)
digest[i] = (uint8_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
} }

View File

@ -1,7 +1,7 @@
// //
// PDF object functions for PDFio. // PDF object functions for PDFio.
// //
// Copyright © 2021-2024 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -10,13 +10,6 @@
#include "pdfio-private.h" #include "pdfio-private.h"
//
// Local functions...
//
static bool write_obj_header(pdfio_obj_t *obj);
// //
// 'pdfioObjClose()' - Close an object, writing any data as needed to the PDF // 'pdfioObjClose()' - Close an object, writing any data as needed to the PDF
// file. // file.
@ -42,7 +35,7 @@ pdfioObjClose(pdfio_obj_t *obj) // I - Object
if (!obj->offset) if (!obj->offset)
{ {
// Write the object value // Write the object value
if (!write_obj_header(obj)) if (!_pdfioObjWriteHeader(obj))
return (false); return (false);
// Write the "endobj" line... // Write the "endobj" line...
@ -86,6 +79,10 @@ pdfioObjCopy(pdfio_file_t *pdf, // I - PDF file
if (srcobj->value.type == PDFIO_VALTYPE_NONE) if (srcobj->value.type == PDFIO_VALTYPE_NONE)
_pdfioObjLoad(srcobj); _pdfioObjLoad(srcobj);
// See if we have already mapped this object...
if ((dstobj = _pdfioFileFindMappedObj(pdf, srcobj->pdf, srcobj->number)) != NULL)
return (dstobj); // Yes, return that one...
// Create the new object... // Create the new object...
if ((dstobj = _pdfioFileCreateObj(pdf, srcobj->pdf, NULL)) == NULL) if ((dstobj = _pdfioFileCreateObj(pdf, srcobj->pdf, NULL)) == NULL)
return (NULL); return (NULL);
@ -148,6 +145,7 @@ pdfioObjCreateStream(
pdfio_obj_t *obj, // I - Object pdfio_obj_t *obj, // I - Object
pdfio_filter_t filter) // I - Type of compression to apply pdfio_filter_t filter) // I - Type of compression to apply
{ {
pdfio_stream_t *st; // Stream
pdfio_obj_t *length_obj = NULL; // Length object, if any pdfio_obj_t *length_obj = NULL; // Length object, if any
@ -195,17 +193,19 @@ pdfioObjCreateStream(
} }
} }
if (!write_obj_header(obj)) if (!_pdfioObjWriteHeader(obj))
return (NULL); return (NULL);
if (!_pdfioFilePuts(obj->pdf, "stream\n")) if (!_pdfioFilePuts(obj->pdf, "stream\n"))
return (NULL); return (NULL);
obj->stream_offset = _pdfioFileTell(obj->pdf); obj->stream_offset = _pdfioFileTell(obj->pdf);
obj->pdf->current_obj = obj;
// Return the new stream... // Return the new stream...
return (_pdfioStreamCreate(obj, length_obj, filter)); if ((st = _pdfioStreamCreate(obj, length_obj, 0, filter)) != NULL)
obj->pdf->current_obj = obj;
return (st);
} }
@ -314,7 +314,8 @@ pdfioObjGetLength(pdfio_obj_t *obj) // I - Object
if ((lenobj = pdfioDictGetObj(obj->value.value.dict, "Length")) == NULL) if ((lenobj = pdfioDictGetObj(obj->value.value.dict, "Length")) == NULL)
{ {
_pdfioFileError(obj->pdf, "Unable to get length of stream."); if (!_pdfioDictGetValue(obj->value.value.dict, "Length"))
_pdfioFileError(obj->pdf, "Unable to get length of stream.");
return (0); return (0);
} }
@ -336,6 +337,8 @@ pdfioObjGetLength(pdfio_obj_t *obj) // I - Object
// //
// 'pdfioObjGetName()' - Get the name value associated with an object. // 'pdfioObjGetName()' - Get the name value associated with an object.
// //
// @since PDFio v1.4@
//
const char * // O - Dictionary or `NULL` on error const char * // O - Dictionary or `NULL` on error
pdfioObjGetName(pdfio_obj_t *obj) // I - Object pdfioObjGetName(pdfio_obj_t *obj) // I - Object
@ -511,7 +514,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
} }
// Decrypt as needed... // Decrypt as needed...
if (obj->pdf->encryption) if (obj->pdf->encryption && obj->pdf->encrypt_metadata)
{ {
PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n"); PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n");
@ -538,6 +541,9 @@ pdfio_stream_t * // O - Stream or `NULL` on error
pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
bool decode) // I - Decode/decompress data? bool decode) // I - Decode/decompress data?
{ {
pdfio_stream_t *st; // Stream
// Range check input... // Range check input...
if (!obj) if (!obj)
return (NULL); return (NULL);
@ -560,9 +566,10 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
return (NULL); return (NULL);
// Open the stream... // Open the stream...
obj->pdf->current_obj = obj; if ((st = _pdfioStreamOpen(obj, decode)) != NULL)
obj->pdf->current_obj = obj;
return (_pdfioStreamOpen(obj, decode)); return (st);
} }
@ -582,11 +589,11 @@ _pdfioObjSetExtension(
// //
// 'write_obj_header()' - Write the object header... // '_pdfioObjWriteHeader()' - Write the object header...
// //
static bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
write_obj_header(pdfio_obj_t *obj) // I - Object _pdfioObjWriteHeader(pdfio_obj_t *obj) // I - Object
{ {
obj->offset = _pdfioFileTell(obj->pdf); obj->offset = _pdfioFileTell(obj->pdf);

View File

@ -1,7 +1,7 @@
// //
// Private header file for PDFio. // Private header file for PDFio.
// //
// Copyright © 2021-2024 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -10,7 +10,7 @@
#ifndef PDFIO_PRIVATE_H #ifndef PDFIO_PRIVATE_H
# define PDFIO_PRIVATE_H # define PDFIO_PRIVATE_H
# ifdef _WIN32 # ifdef _WIN32
# define _CRT_SECURE_NO_WARNINGS // Disable bogus VS warnings/errors... # define _CRT_SECURE_NO_WARNINGS 1 // Disable bogus VS warnings/errors...
# endif // _WIN32 # endif // _WIN32
# include "pdfio.h" # include "pdfio.h"
# include <stdarg.h> # include <stdarg.h>
@ -28,16 +28,16 @@
# define access _access // Map standard POSIX/C99 names # define access _access // Map standard POSIX/C99 names
# define close _close # define close _close
# define fileno _fileno # define fileno _fileno
# define lseek _lseek # define lseek(f,o,w) (off_t)_lseek((f),(long)(o),(w))
# define mkdir(d,p) _mkdir(d) # define mkdir(d,p) _mkdir(d)
# define open _open # define open _open
# define read _read # define read(f,b,s) _read((f),(b),(unsigned)(s))
# define rmdir _rmdir # define rmdir _rmdir
# define snprintf _snprintf # define snprintf _snprintf
# define strdup _strdup # define strdup _strdup
# define unlink _unlink # define unlink _unlink
# define vsnprintf _vsnprintf # define vsnprintf _vsnprintf
# define write _write # define write(f,b,s) _write((f),(b),(unsigned)(s))
# ifndef F_OK # ifndef F_OK
# define F_OK 00 // POSIX parameters/flags # define F_OK 00 // POSIX parameters/flags
# define W_OK 02 # define W_OK 02
@ -94,6 +94,7 @@
// //
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values # define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
# define PDFIO_MAX_STRING 65536 // Maximum length of string
typedef void (*_pdfio_extfree_t)(void *); typedef void (*_pdfio_extfree_t)(void *);
// Extension data free function // Extension data free function
@ -107,7 +108,7 @@ typedef enum _pdfio_mode_e // Read/write mode
typedef enum _pdfio_predictor_e // PNG predictor constants typedef enum _pdfio_predictor_e // PNG predictor constants
{ {
_PDFIO_PREDICTOR_NONE = 1, // No predictor (default) _PDFIO_PREDICTOR_NONE = 1, // No predictor (default)
_PDFIO_PREDICTOR_TIFF2 = 2, // TIFF2 predictor (???) _PDFIO_PREDICTOR_TIFF2 = 2, // TIFF predictor 2 (difference from left neighbor)
_PDFIO_PREDICTOR_PNG_NONE = 10, // PNG None predictor (same as `_PDFIO_PREDICTOR_NONE`) _PDFIO_PREDICTOR_PNG_NONE = 10, // PNG None predictor (same as `_PDFIO_PREDICTOR_NONE`)
_PDFIO_PREDICTOR_PNG_SUB = 11, // PNG Sub predictor _PDFIO_PREDICTOR_PNG_SUB = 11, // PNG Sub predictor
_PDFIO_PREDICTOR_PNG_UP = 12, // PNG Up predictor _PDFIO_PREDICTOR_PNG_UP = 12, // PNG Up predictor
@ -220,13 +221,22 @@ struct _pdfio_dict_s // Dictionary
typedef struct _pdfio_objmap_s // PDF object map typedef struct _pdfio_objmap_s // PDF object map
{ {
pdfio_obj_t *obj; // Object for this file pdfio_obj_t *obj; // Object for this file
pdfio_file_t *src_pdf; // Source PDF file unsigned char src_id[32]; // Source PDF file file identifier
size_t src_number; // Source object number size_t src_number; // Source object number
} _pdfio_objmap_t; } _pdfio_objmap_t;
typedef struct _pdfio_strbuf_s // PDF string buffer
{
struct _pdfio_strbuf_s *next; // Next string buffer
bool bufused; // Is this string buffer being used?
char buffer[PDFIO_MAX_STRING + 32];
// String buffer
} _pdfio_strbuf_t;
struct _pdfio_file_s // PDF file structure struct _pdfio_file_s // PDF file structure
{ {
char *filename; // Filename char *filename; // Filename
unsigned char file_id[32]; // File identifier bytes
struct lconv *loc; // Locale data struct lconv *loc; // Locale data
char *version; // Version number char *version; // Version number
pdfio_rect_t media_box, // Default MediaBox value pdfio_rect_t media_box, // Default MediaBox value
@ -258,9 +268,11 @@ struct _pdfio_file_s // PDF file structure
pdfio_obj_t *info_obj; // Information object pdfio_obj_t *info_obj; // Information object
pdfio_obj_t *pages_obj; // Root pages object pdfio_obj_t *pages_obj; // Root pages object
pdfio_obj_t *encrypt_obj; // De/Encryption object/dictionary pdfio_obj_t *encrypt_obj; // De/Encryption object/dictionary
pdfio_obj_t *cp1252_obj, // CP1252 font encoding object pdfio_obj_t *cgats001_obj, // CGATS001 ICC profile object
*cp1252_obj, // CP1252 font encoding object
*unicode_obj; // Unicode font encoding object *unicode_obj; // Unicode font encoding object
pdfio_array_t *id_array; // ID array pdfio_array_t *id_array; // ID array
bool encrypt_metadata; // Encrypt metadata?
// Allocated data elements // Allocated data elements
size_t num_arrays, // Number of arrays size_t num_arrays, // Number of arrays
@ -283,6 +295,7 @@ struct _pdfio_file_s // PDF file structure
size_t num_strings, // Number of strings size_t num_strings, // Number of strings
alloc_strings; // Allocated strings alloc_strings; // Allocated strings
char **strings; // Nul-terminated strings char **strings; // Nul-terminated strings
_pdfio_strbuf_t *strbuffers; // String buffers
}; };
struct _pdfio_obj_s // Object struct _pdfio_obj_s // Object
@ -313,8 +326,9 @@ struct _pdfio_stream_s // Stream
z_stream flate; // Flate filter state z_stream flate; // Flate filter state
_pdfio_predictor_t predictor; // Predictor function, if any _pdfio_predictor_t predictor; // Predictor function, if any
size_t pbpixel, // Size of a pixel in bytes size_t pbpixel, // Size of a pixel in bytes
pbsize; // Predictor buffer size, if any pbsize, // Predictor buffer size, if any
unsigned char cbuffer[4096], // Compressed data buffer cbsize; // Compressed data buffer size
unsigned char *cbuffer, // Compressed data buffer
*prbuffer, // Raw buffer (previous line), as needed *prbuffer, // Raw buffer (previous line), as needed
*psbuffer; // PNG filter buffer, as needed *psbuffer; // PNG filter buffer, as needed
_pdfio_crypto_cb_t crypto_cb; // Encryption/descryption callback, if any _pdfio_crypto_cb_t crypto_cb; // Encryption/descryption callback, if any
@ -326,7 +340,9 @@ struct _pdfio_stream_s // Stream
// Functions... // Functions...
// //
extern size_t _pdfio_strlcpy(char *dst, const char *src, size_t dstsize) _PDFIO_INTERNAL;
extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL; extern double _pdfio_strtod(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern void _pdfio_utf16cpy(char *dst, const unsigned char *src, size_t srclen, size_t dstsize) _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 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 bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL;
@ -366,13 +382,13 @@ extern bool _pdfioFileAddPage(pdfio_file_t *pdf, pdfio_obj_t *obj) _PDFIO_INTER
extern bool _pdfioFileConsume(pdfio_file_t *pdf, size_t bytes) _PDFIO_INTERNAL; extern bool _pdfioFileConsume(pdfio_file_t *pdf, size_t bytes) _PDFIO_INTERNAL;
extern pdfio_obj_t *_pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_file_t *srcpdf, _pdfio_value_t *value) _PDFIO_INTERNAL; extern pdfio_obj_t *_pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_file_t *srcpdf, _pdfio_value_t *value) _PDFIO_INTERNAL;
extern bool _pdfioFileDefaultError(pdfio_file_t *pdf, const char *message, void *data) _PDFIO_INTERNAL; extern bool _pdfioFileDefaultError(pdfio_file_t *pdf, const char *message, void *data) _PDFIO_INTERNAL;
extern bool _pdfioFileError(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_INTERNAL; extern bool _pdfioFileError(pdfio_file_t *pdf, const char *format, ...) _PDFIO_INTERNAL;
extern pdfio_obj_t *_pdfioFileFindMappedObj(pdfio_file_t *pdf, pdfio_file_t *src_pdf, size_t src_number) _PDFIO_INTERNAL; extern pdfio_obj_t *_pdfioFileFindMappedObj(pdfio_file_t *pdf, pdfio_file_t *src_pdf, size_t src_number) _PDFIO_INTERNAL;
extern bool _pdfioFileFlush(pdfio_file_t *pdf) _PDFIO_INTERNAL; extern bool _pdfioFileFlush(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern int _pdfioFileGetChar(pdfio_file_t *pdf) _PDFIO_INTERNAL; extern int _pdfioFileGetChar(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern bool _pdfioFileGets(pdfio_file_t *pdf, char *buffer, size_t bufsize) _PDFIO_INTERNAL; extern bool _pdfioFileGets(pdfio_file_t *pdf, char *buffer, size_t bufsize, bool discard) _PDFIO_INTERNAL;
extern ssize_t _pdfioFilePeek(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL; extern ssize_t _pdfioFilePeek(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern bool _pdfioFilePrintf(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_INTERNAL; extern bool _pdfioFilePrintf(pdfio_file_t *pdf, const char *format, ...) _PDFIO_INTERNAL;
extern bool _pdfioFilePuts(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL; extern bool _pdfioFilePuts(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern ssize_t _pdfioFileRead(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL; extern ssize_t _pdfioFileRead(pdfio_file_t *pdf, void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern off_t _pdfioFileSeek(pdfio_file_t *pdf, off_t offset, int whence) _PDFIO_INTERNAL; extern off_t _pdfioFileSeek(pdfio_file_t *pdf, off_t offset, int whence) _PDFIO_INTERNAL;
@ -383,10 +399,13 @@ extern void _pdfioObjDelete(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern void *_pdfioObjGetExtension(pdfio_obj_t *obj) _PDFIO_INTERNAL; extern void *_pdfioObjGetExtension(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern bool _pdfioObjLoad(pdfio_obj_t *obj) _PDFIO_INTERNAL; extern bool _pdfioObjLoad(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern void _pdfioObjSetExtension(pdfio_obj_t *obj, void *data, _pdfio_extfree_t datafree) _PDFIO_INTERNAL; extern void _pdfioObjSetExtension(pdfio_obj_t *obj, void *data, _pdfio_extfree_t datafree) _PDFIO_INTERNAL;
extern bool _pdfioObjWriteHeader(pdfio_obj_t *obj) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, pdfio_filter_t compression) _PDFIO_INTERNAL; extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, size_t cbsize, pdfio_filter_t compression) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL; extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
extern char *_pdfioStringAllocBuffer(pdfio_file_t *pdf);
extern void _pdfioStringFreeBuffer(pdfio_file_t *pdf, char *buffer);
extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL; extern bool _pdfioStringIsAllocated(pdfio_file_t *pdf, const char *s) _PDFIO_INTERNAL;
extern void _pdfioTokenClear(_pdfio_token_t *tb) _PDFIO_INTERNAL; extern void _pdfioTokenClear(_pdfio_token_t *tb) _PDFIO_INTERNAL;

View File

@ -1,7 +1,7 @@
// //
// PDF stream functions for PDFio. // PDF stream functions for PDFio.
// //
// Copyright © 2021-2024 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -50,7 +50,7 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
while ((status = deflate(&st->flate, Z_FINISH)) != Z_STREAM_END) while ((status = deflate(&st->flate, Z_FINISH)) != Z_STREAM_END)
{ {
size_t bytes = sizeof(st->cbuffer) - st->flate.avail_out, size_t bytes = st->cbsize - st->flate.avail_out,
// Bytes to write // Bytes to write
outbytes; // Actual bytes written outbytes; // Actual bytes written
@ -89,13 +89,13 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
} }
st->flate.next_out = (Bytef *)st->cbuffer + bytes; st->flate.next_out = (Bytef *)st->cbuffer + bytes;
st->flate.avail_out = (uInt)(sizeof(st->cbuffer) - bytes); st->flate.avail_out = (uInt)(st->cbsize - bytes);
} }
if (st->flate.avail_out < (uInt)sizeof(st->cbuffer)) if (st->flate.avail_out < (uInt)st->cbsize)
{ {
// Write any residuals... // Write any residuals...
size_t bytes = sizeof(st->cbuffer) - st->flate.avail_out; size_t bytes = st->cbsize - st->flate.avail_out;
// Bytes to write // Bytes to write
if (st->crypto_cb) if (st->crypto_cb)
@ -140,7 +140,7 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
// Update the length as needed... // Update the length as needed...
if (st->length_obj) if (st->length_obj)
{ {
st->length_obj->value.value.number = st->obj->stream_length; st->length_obj->value.value.number = (double)st->obj->stream_length;
pdfioObjClose(st->length_obj); pdfioObjClose(st->length_obj);
} }
else if (st->obj->length_offset) else if (st->obj->length_offset)
@ -172,6 +172,7 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
st->pdf->current_obj = NULL; st->pdf->current_obj = NULL;
free(st->cbuffer);
free(st->prbuffer); free(st->prbuffer);
free(st->psbuffer); free(st->psbuffer);
free(st); free(st);
@ -190,6 +191,7 @@ pdfio_stream_t * // O - Stream or `NULL` on error
_pdfioStreamCreate( _pdfioStreamCreate(
pdfio_obj_t *obj, // I - Object pdfio_obj_t *obj, // I - Object
pdfio_obj_t *length_obj, // I - Length object, if any pdfio_obj_t *length_obj, // I - Length object, if any
size_t cbsize, // I - Size of compression buffer
pdfio_filter_t compression) // I - Compression to apply pdfio_filter_t compression) // I - Compression to apply
{ {
pdfio_stream_t *st; // Stream pdfio_stream_t *st; // Stream
@ -257,7 +259,7 @@ _pdfioStreamCreate(
{ {
colors = 1; colors = 1;
} }
else if (colors < 0 || colors > 4) else if (colors < 0 || colors > 32)
{ {
_pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors); _pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors);
free(st); free(st);
@ -268,7 +270,7 @@ _pdfioStreamCreate(
{ {
columns = 1; columns = 1;
} }
else if (columns < 0) else if (columns < 0 || columns > 65536)
{ {
_pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns); _pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns);
free(st); free(st);
@ -302,8 +304,21 @@ _pdfioStreamCreate(
else else
st->predictor = _PDFIO_PREDICTOR_NONE; st->predictor = _PDFIO_PREDICTOR_NONE;
if (cbsize == 0)
cbsize = 4096;
st->cbsize = cbsize;
if ((st->cbuffer = malloc(cbsize)) == NULL)
{
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Flate output buffer: %s", (unsigned long)cbsize, strerror(errno));
free(st->prbuffer);
free(st->psbuffer);
free(st);
return (NULL);
}
st->flate.next_out = (Bytef *)st->cbuffer; st->flate.next_out = (Bytef *)st->cbuffer;
st->flate.avail_out = (uInt)sizeof(st->cbuffer); st->flate.avail_out = (uInt)cbsize;
if ((status = deflateInit(&(st->flate), 9)) != Z_OK) if ((status = deflateInit(&(st->flate), 9)) != Z_OK)
{ {
@ -362,15 +377,16 @@ pdfioStreamConsume(pdfio_stream_t *st, // I - Stream
// //
// 'pdfioStreamGetToken()' - Read a single PDF token from a stream. // 'pdfioStreamGetToken()' - Read a single PDF token from a stream.
// //
// This function reads a single PDF token from a stream. Operator tokens, // This function reads a single PDF token from a stream, skipping all whitespace
// boolean values, and numbers are returned as-is in the provided string buffer. // and comments. Operator tokens, boolean values, and numbers are returned
// String values start with the opening parenthesis ('(') but have all escaping // as-is in the provided string buffer. String values start with the opening
// resolved and the terminating parenthesis removed. Hexadecimal string values // parenthesis ('(') but have all escaping resolved and the terminating
// start with the opening angle bracket ('<') and have all whitespace and the // parenthesis removed. Hexadecimal string values start with the opening angle
// terminating angle bracket removed. // bracket ('<') and have all whitespace and the terminating angle bracket
// removed.
// //
bool // O - `true` on success, `false` on EOF bool // O - `true` on success, `false` on end-of-stream or error
pdfioStreamGetToken( pdfioStreamGetToken(
pdfio_stream_t *st, // I - Stream pdfio_stream_t *st, // I - Stream
char *buffer, // I - String buffer char *buffer, // I - String buffer
@ -423,16 +439,16 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
st->pdf = obj->pdf; st->pdf = obj->pdf;
st->obj = obj; st->obj = obj;
if ((st->remaining = pdfioObjGetLength(obj)) == 0) if ((st->remaining = pdfioObjGetLength(obj)) == 0 && !_pdfioDictGetValue(pdfioObjGetDict(obj), "Length"))
{ {
free(st); _pdfioFileError(obj->pdf, "No stream data.");
return (NULL); goto error;
} }
if (_pdfioFileSeek(st->pdf, obj->stream_offset, SEEK_SET) != obj->stream_offset) if (_pdfioFileSeek(st->pdf, obj->stream_offset, SEEK_SET) != obj->stream_offset)
{ {
free(st); _pdfioFileError(obj->pdf, "Unable to seek to stream data.");
return (NULL); goto error;
} }
type = pdfioObjGetType(obj); type = pdfioObjGetType(obj);
@ -445,11 +461,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
ivlen = (size_t)_pdfioFilePeek(st->pdf, iv, sizeof(iv)); ivlen = (size_t)_pdfioFilePeek(st->pdf, iv, sizeof(iv));
if ((st->crypto_cb = _pdfioCryptoMakeReader(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL) if ((st->crypto_cb = _pdfioCryptoMakeReader(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL)
{ goto error;
// TODO: Add error message?
free(st);
return (NULL);
}
PDFIO_DEBUG("_pdfioStreamOpen: ivlen=%d\n", (int)ivlen); PDFIO_DEBUG("_pdfioStreamOpen: ivlen=%d\n", (int)ivlen);
if (ivlen > 0) if (ivlen > 0)
@ -480,8 +492,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
{ {
// TODO: Implement compound filters... // TODO: Implement compound filters...
_pdfioFileError(st->pdf, "Unsupported compound stream filter."); _pdfioFileError(st->pdf, "Unsupported compound stream filter.");
free(st); goto error;
return (NULL);
} }
// No filter, read as-is... // No filter, read as-is...
@ -514,37 +525,33 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
else if (bpc < 1 || bpc == 3 || (bpc > 4 && bpc < 8) || (bpc > 8 && bpc < 16) || bpc > 16) else if (bpc < 1 || bpc == 3 || (bpc > 4 && bpc < 8) || (bpc > 8 && bpc < 16) || bpc > 16)
{ {
_pdfioFileError(st->pdf, "Unsupported BitsPerColor value %d.", bpc); _pdfioFileError(st->pdf, "Unsupported BitsPerColor value %d.", bpc);
free(st); goto error;
return (NULL);
} }
if (colors == 0) if (colors == 0)
{ {
colors = 1; colors = 1;
} }
else if (colors < 0 || colors > 4) else if (colors < 0 || colors > 32)
{ {
_pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors); _pdfioFileError(st->pdf, "Unsupported Colors value %d.", colors);
free(st); goto error;
return (NULL);
} }
if (columns == 0) if (columns == 0)
{ {
columns = 1; columns = 1;
} }
else if (columns < 0) else if (columns < 0 || columns > 65536)
{ {
_pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns); _pdfioFileError(st->pdf, "Unsupported Columns value %d.", columns);
free(st); goto error;
return (NULL);
} }
if ((predictor > 2 && predictor < 10) || predictor > 15) if ((predictor > 2 && predictor < 10) || predictor > 15)
{ {
_pdfioFileError(st->pdf, "Unsupported Predictor function %d.", predictor); _pdfioFileError(st->pdf, "Unsupported Predictor function %d.", predictor);
free(st); goto error;
return (NULL);
} }
else if (predictor > 1) else if (predictor > 1)
{ {
@ -555,31 +562,41 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
if (predictor >= 10) if (predictor >= 10)
st->pbsize ++; // Add PNG predictor byte st->pbsize ++; // Add PNG predictor byte
if (st->pbsize < 2)
{
_pdfioFileError(st->pdf, "Bad Predictor buffer size %lu.", (unsigned long)st->pbsize);
goto error;
}
PDFIO_DEBUG("_pdfioStreamOpen: st->predictor=%d, st->pbpixel=%u, st->pbsize=%lu\n", st->predictor, (unsigned)st->pbpixel, (unsigned long)st->pbsize);
if ((st->prbuffer = calloc(1, st->pbsize - 1)) == NULL || (st->psbuffer = calloc(1, st->pbsize)) == NULL) if ((st->prbuffer = calloc(1, st->pbsize - 1)) == NULL || (st->psbuffer = calloc(1, st->pbsize)) == NULL)
{ {
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Predictor buffers.", (unsigned long)st->pbsize); _pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Predictor buffers.", (unsigned long)st->pbsize);
free(st->prbuffer); goto error;
free(st->psbuffer);
free(st);
return (NULL);
} }
} }
else else
{
st->predictor = _PDFIO_PREDICTOR_NONE; st->predictor = _PDFIO_PREDICTOR_NONE;
}
st->cbsize = 4096;
if ((st->cbuffer = malloc(st->cbsize)) == NULL)
{
_pdfioFileError(st->pdf, "Unable to allocate %lu bytes for Flate compression buffer.", (unsigned long)st->cbsize);
goto error;
}
PDFIO_DEBUG("_pdfioStreamOpen: pos=%ld\n", (long)_pdfioFileTell(st->pdf)); PDFIO_DEBUG("_pdfioStreamOpen: pos=%ld\n", (long)_pdfioFileTell(st->pdf));
if (sizeof(st->cbuffer) > st->remaining) if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining); rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer)); rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->cbsize);
if (rbytes <= 0) if (rbytes <= 0)
{ {
_pdfioFileError(st->pdf, "Unable to read bytes for stream."); _pdfioFileError(st->pdf, "Unable to read bytes for stream.");
free(st->prbuffer); goto error;
free(st->psbuffer);
free(st);
return (NULL);
} }
if (st->crypto_cb) if (st->crypto_cb)
@ -593,10 +610,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
if ((status = inflateInit(&(st->flate))) != Z_OK) if ((status = inflateInit(&(st->flate))) != Z_OK)
{ {
_pdfioFileError(st->pdf, "Unable to start Flate filter: %s", zstrerror(status)); _pdfioFileError(st->pdf, "Unable to start Flate filter: %s", zstrerror(status));
free(st->prbuffer); goto error;
free(st->psbuffer);
free(st);
return (NULL);
} }
st->remaining -= st->flate.avail_in; st->remaining -= st->flate.avail_in;
@ -609,9 +623,8 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
else else
{ {
// Something else we don't support // Something else we don't support
_pdfioFileError(st->pdf, "Unsupported stream filter '/%s'.", filter); _pdfioFileError(st->pdf, "Unsupported stream filter '%N'.", filter);
free(st); goto error;
return (NULL);
} }
} }
else else
@ -621,6 +634,16 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
} }
return (st); return (st);
// If we get here something went wrong...
error:
free(st->cbuffer);
free(st->prbuffer);
free(st->psbuffer);
free(st);
return (NULL);
} }
@ -673,6 +696,11 @@ pdfioStreamPeek(pdfio_stream_t *st, // I - Stream
// //
// 'pdfioStreamPrintf()' - Write a formatted string to a stream. // 'pdfioStreamPrintf()' - Write a formatted string to a stream.
// //
// This function writes a formatted string to a stream. In addition to the
// standard `printf` format characters, you can use "%H" to format a HTML/XML
// string value, "%N" to format a PDF name value ("/Name"), and "%S" to format
// a PDF string ("(String)") value.
//
bool // O - `true` on success, `false` on failure bool // O - `true` on success, `false` on failure
pdfioStreamPrintf( pdfioStreamPrintf(
@ -1045,10 +1073,10 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (st->flate.avail_in == 0) if (st->flate.avail_in == 0)
{ {
// Read more from the file... // Read more from the file...
if (sizeof(st->cbuffer) > st->remaining) if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining); rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer)); rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->cbsize);
if (rbytes <= 0) if (rbytes <= 0)
return (-1); // End of file... return (-1); // End of file...
@ -1101,10 +1129,10 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (st->flate.avail_in == 0) if (st->flate.avail_in == 0)
{ {
// Read more from the file... // Read more from the file...
if (sizeof(st->cbuffer) > st->remaining) if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining); rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer)); rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->cbsize);
if (rbytes <= 0) if (rbytes <= 0)
return (-1); // End of file... return (-1); // End of file...
@ -1171,10 +1199,10 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (st->flate.avail_in == 0) if (st->flate.avail_in == 0)
{ {
// Read more from the file... // Read more from the file...
if (sizeof(st->cbuffer) > st->remaining) if (st->cbsize > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining); rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else else
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, sizeof(st->cbuffer)); rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->cbsize);
if (rbytes <= 0) if (rbytes <= 0)
return (-1); // End of file... return (-1); // End of file...
@ -1207,7 +1235,18 @@ stream_read(pdfio_stream_t *st, // I - Stream
} }
// Apply predictor for this line // Apply predictor for this line
PDFIO_DEBUG("stream_read: Line %02X %02X %02X %02X %02X.\n", sptr[-1], sptr[0], sptr[0], sptr[2], sptr[3]); #ifdef DEBUG
if (remaining > 4)
PDFIO_DEBUG("stream_read: Line %02X %02X %02X %02X %02X ...\n", sptr[-1], sptr[0], sptr[1], sptr[2], sptr[3]);
else if (remaining > 3)
PDFIO_DEBUG("stream_read: Line %02X %02X %02X %02X %02X.\n", sptr[-1], sptr[0], sptr[1], sptr[2], sptr[3]);
else if (remaining > 2)
PDFIO_DEBUG("stream_read: Line %02X %02X %02X %02X.\n", sptr[-1], sptr[0], sptr[1], sptr[2]);
else if (remaining > 1)
PDFIO_DEBUG("stream_read: Line %02X %02X %02X.\n", sptr[-1], sptr[0], sptr[1]);
else
PDFIO_DEBUG("stream_read: Line %02X %02X.\n", sptr[-1], sptr[0]);
#endif // DEBUG
switch (sptr[-1]) switch (sptr[-1])
{ {
@ -1278,10 +1317,10 @@ stream_write(pdfio_stream_t *st, // I - Stream
while (st->flate.avail_in > 0) while (st->flate.avail_in > 0)
{ {
if (st->flate.avail_out < (sizeof(st->cbuffer) / 8)) if (st->flate.avail_out < (st->cbsize / 8))
{ {
// Flush the compression buffer... // Flush the compression buffer...
size_t cbytes = sizeof(st->cbuffer) - st->flate.avail_out, size_t cbytes = st->cbsize - st->flate.avail_out,
outbytes; outbytes;
if (st->crypto_cb) if (st->crypto_cb)
@ -1310,7 +1349,7 @@ stream_write(pdfio_stream_t *st, // I - Stream
} }
st->flate.next_out = (Bytef *)st->cbuffer + cbytes; st->flate.next_out = (Bytef *)st->cbuffer + cbytes;
st->flate.avail_out = (uInt)(sizeof(st->cbuffer) - cbytes); st->flate.avail_out = (uInt)(st->cbsize - cbytes);
} }
// Deflate what we can this time... // Deflate what we can this time...

View File

@ -1,7 +1,7 @@
// //
// PDF string functions for PDFio. // PDF string functions for PDFio.
// //
// Copyright © 2021-2024 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -17,6 +17,83 @@
static size_t find_string(pdfio_file_t *pdf, const char *s, int *rdiff); static size_t find_string(pdfio_file_t *pdf, const char *s, int *rdiff);
//
// '_pdfio_strlcpy()' - Safe string copy.
//
size_t // O - Length of source string
_pdfio_strlcpy(char *dst, // I - Destination string buffer
const char *src, // I - Source string
size_t dstsize) // I - Size of destination
{
size_t srclen; // Length of source string
// Range check input...
if (!dst || !src || dstsize == 0)
{
if (dst)
*dst = '\0';
return (0);
}
// Figure out how much room is needed...
dstsize --;
srclen = strlen(src);
// Copy the appropriate amount...
if (srclen <= dstsize)
{
// Source string will fit...
memmove(dst, src, srclen);
dst[srclen] = '\0';
}
else
{
// Source string too big, copy what we can and clean up the end...
char *ptr = dst + dstsize - 1, // Pointer into string
*end = ptr + 1; // Pointer to end of string
memmove(dst, src, dstsize);
dst[dstsize] = '\0';
// Validate last character in destination buffer...
if (ptr > dst && *ptr & 0x80)
{
while ((*ptr & 0xc0) == 0x80 && ptr > dst)
ptr --;
if ((*ptr & 0xe0) == 0xc0)
{
// Verify 2-byte UTF-8 sequence...
if ((end - ptr) != 2)
*ptr = '\0';
}
else if ((*ptr & 0xf0) == 0xe0)
{
// Verify 3-byte UTF-8 sequence...
if ((end - ptr) != 3)
*ptr = '\0';
}
else if ((*ptr & 0xf8) == 0xf0)
{
// Verify 4-byte UTF-8 sequence...
if ((end - ptr) != 4)
*ptr = '\0';
}
else if (*ptr & 0x80)
{
// Invalid sequence at end...
*ptr = '\0';
}
}
}
return (srclen);
}
// //
// '_pdfio_strtod()' - Convert a string to a double value. // '_pdfio_strtod()' - Convert a string to a double value.
// //
@ -81,6 +158,89 @@ _pdfio_strtod(pdfio_file_t *pdf, // I - PDF file
} }
//
// '_pdfio_utf16cpy()' - Convert UTF-16 to UTF-8.
//
void
_pdfio_utf16cpy(
char *dst, // I - Destination buffer for UTF-8
const unsigned char *src, // I - Source UTF-16
size_t srclen, // I - Length of UTF-16
size_t dstsize) // I - Destination buffer size
{
char *dstptr = dst, // Pointer into buffer
*dstend = dst + dstsize - 5; // End of buffer
int ch; // Unicode character
bool is_be = !memcmp(src, "\376\377", 2);
// Big-endian strings?
// Loop through the UTF-16 string, converting to Unicode then UTF-8...
for (src += 2, srclen -= 2; srclen > 1 && dstptr < dstend; src += 2, srclen -= 2)
{
// Initial character...
if (is_be)
ch = (src[0] << 8) | src[1];
else
ch = (src[1] << 8) | src[0];
if (ch >= 0xd800 && ch <= 0xdbff && srclen > 3)
{
// Multi-word UTF-16 char...
int lch; // Lower bits
if (is_be)
lch = (src[2] << 8) | src[3];
else
lch = (src[3] << 8) | src[2];
if (lch < 0xdc00 || lch >= 0xdfff)
break;
ch = (((ch & 0x3ff) << 10) | (lch & 0x3ff)) + 0x10000;
src += 2;
srclen -= 2;
}
else if (ch >= 0xfffe)
{
continue;
}
// Convert Unicode to UTF-8...
if (ch < 128)
{
// ASCII
*dstptr++ = (char)ch;
}
else if (ch < 4096)
{
// 2-byte UTF-8
*dstptr++ = (char)(0xc0 | (ch >> 6));
*dstptr++ = (char)(0x80 | (ch & 0x3f));
}
else if (ch < 65536)
{
// 3-byte UTF-8
*dstptr++ = (char)(0xe0 | (ch >> 12));
*dstptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*dstptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 4-byte UTF-8
*dstptr++ = (char)(0xe0 | (ch >> 18));
*dstptr++ = (char)(0x80 | ((ch >> 12) & 0x3f));
*dstptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*dstptr++ = (char)(0x80 | (ch & 0x3f));
}
}
// Nul-terminate the UTF-8 string...
*dstptr = '\0';
}
// //
// '_pdfio_vsnprintf()' - Format a string. // '_pdfio_vsnprintf()' - Format a string.
// //
@ -112,10 +272,9 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
// Loop through the format string, formatting as needed... // Loop through the format string, formatting as needed...
bufptr = buffer; bufptr = buffer;
bufend = buffer + bufsize - 1; bufend = buffer + bufsize - 1;
*bufend = '\0'; bytes = 0;
bytes = 0;
while (*format) while (*format)
{ {
@ -178,14 +337,12 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
} }
else else
{ {
prec = 0;
while (isdigit(*format & 255)) while (isdigit(*format & 255))
{ {
if (tptr < (tformat + sizeof(tformat) - 1)) if (tptr < (tformat + sizeof(tformat) - 1))
*tptr++ = *format; *tptr++ = *format;
prec = prec * 10 + *format++ - '0'; format ++;
} }
} }
} }
@ -259,7 +416,7 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
if (bufptr < bufend) if (bufptr < bufend)
{ {
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1)); _pdfio_strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr); bufptr += strlen(bufptr);
} }
break; break;
@ -289,7 +446,7 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
if (bufptr < bufend) if (bufptr < bufend)
{ {
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1)); _pdfio_strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr); bufptr += strlen(bufptr);
} }
break; break;
@ -304,7 +461,7 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
if (bufptr < bufend) if (bufptr < bufend)
{ {
strncpy(bufptr, temp, (size_t)(bufend - bufptr - 1)); _pdfio_strlcpy(bufptr, temp, (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr); bufptr += strlen(bufptr);
} }
break; break;
@ -329,19 +486,164 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
} }
break; break;
case 's' : // String case 'H' : // XML/HTML string
if ((s = va_arg(ap, char *)) == NULL) if ((s = va_arg(ap, char *)) == NULL)
s = "(null)"; s = "(null)";
// Loop through the literal string...
while (*s)
{
// Escape special characters
if (*s == '&')
{
// &amp;
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, "&amp;", (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
bytes += 5;
}
else if (*s == '<')
{
// &lt;
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, "&lt;", (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
bytes += 4;
}
else if (*s == '>')
{
// &gt;
if (bufptr < bufend)
{
_pdfio_strlcpy(bufptr, "&gt;", (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr);
}
bytes += 4;
}
else
{
// Literal character...
if (bufptr < bufend)
*bufptr++ = *s;
bytes ++;
}
s ++;
}
break;
case 'S' : // PDF string
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
// PDF strings start with "("...
if (bufptr < bufend)
*bufptr++ = '(';
bytes ++;
// Loop through the literal string...
while (*s)
{
// Escape special characters
if (*s == '\\' || *s == '(' || *s == ')')
{
// Simple escape...
if (bufptr < bufend)
*bufptr++ = '\\';
if (bufptr < bufend)
*bufptr++ = *s;
bytes += 2;
}
else if (*s < ' ')
{
// Octal escape...
snprintf(bufptr, (size_t)(bufend - bufptr + 1), "\\%03o", *s & 255);
bufptr += strlen(bufptr);
bytes += 4;
}
else
{
// Literal character...
if (bufptr < bufend)
*bufptr++ = *s;
bytes ++;
}
s ++;
}
// PDF strings end with ")"...
if (bufptr < bufend)
*bufptr++ = ')';
bytes ++;
break;
case 's' : // Literal string
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
if (width != 0)
{
// Format string to fit inside the specified width...
if ((size_t)(width + 1) > sizeof(temp))
break;
snprintf(temp, sizeof(temp), tformat, s);
s = temp;
}
bytes += strlen(s); bytes += strlen(s);
if (bufptr < bufend) if (bufptr < bufend)
{ {
strncpy(bufptr, s, (size_t)(bufend - bufptr - 1)); _pdfio_strlcpy(bufptr, s, (size_t)(bufend - bufptr + 1));
bufptr += strlen(bufptr); bufptr += strlen(bufptr);
} }
break; break;
case 'N' : // Output name string with proper escaping
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
// PDF names start with "/"...
if (bufptr < bufend)
*bufptr++ = '/';
bytes ++;
// Loop through the name string...
while (*s)
{
if (*s < 0x21 || *s > 0x7e || *s == '#')
{
// Output #XX for character...
snprintf(bufptr, (size_t)(bufend - bufptr + 1), "#%02X", *s & 255);
bufptr += strlen(bufptr);
bytes += 3;
}
else
{
// Output literal character...
if (bufptr < bufend)
*bufptr++ = *s;
bytes ++;
}
s ++;
}
break;
case 'n' : // Output number of chars so far case 'n' : // Output number of chars so far
*(va_arg(ap, int *)) = (int)bytes; *(va_arg(ap, int *)) = (int)bytes;
break; break;
@ -358,11 +660,7 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
} }
// Nul-terminate the string and return the number of characters needed. // Nul-terminate the string and return the number of characters needed.
if (bufptr < bufend) *bufptr = '\0';
{
// Everything fit in the buffer...
*bufptr = '\0';
}
PDFIO_DEBUG("_pdfio_vsnprintf: Returning %ld \"%s\"\n", (long)bytes, buffer); PDFIO_DEBUG("_pdfio_vsnprintf: Returning %ld \"%s\"\n", (long)bytes, buffer);
@ -370,6 +668,41 @@ _pdfio_vsnprintf(pdfio_file_t *pdf, // I - PDF file
} }
//
// '_pdfioStringAllocBuffer()' - Allocate a string buffer.
//
char * // O - Buffer or `NULL` on error
_pdfioStringAllocBuffer(
pdfio_file_t *pdf) // I - PDF file
{
_pdfio_strbuf_t *current; // Current string buffer
// See if we have an available string buffer...
for (current = pdf->strbuffers; current; current = current->next)
{
if (!current->bufused)
{
current->bufused = true;
return (current->buffer);
}
}
// Didn't find one, allocate a new one...
if ((current = calloc(1, sizeof(_pdfio_strbuf_t))) == NULL)
return (NULL);
// Add to the linked list of string buffers...
current->next = pdf->strbuffers;
current->bufused = true;
pdf->strbuffers = current;
return (current->buffer);
}
// //
// 'pdfioStringCreate()' - Create a durable literal string. // 'pdfioStringCreate()' - Create a durable literal string.
// //
@ -480,6 +813,29 @@ pdfioStringCreatef(
} }
//
// '_pdfioStringFreeBuffer()' - Free a string buffer.
//
void
_pdfioStringFreeBuffer(
pdfio_file_t *pdf, // I - PDF file
char *buffer) // I - String buffer
{
_pdfio_strbuf_t *current; // Current string buffer
for (current = pdf->strbuffers; current; current = current->next)
{
if (current->buffer == buffer)
{
current->bufused = false;
break;
}
}
}
// //
// '_pdfioStringIsAllocated()' - Check whether a string has been allocated. // '_pdfioStringIsAllocated()' - Check whether a string has been allocated.
// //

View File

@ -1,7 +1,7 @@
// //
// PDF token parsing functions for PDFio. // PDF token parsing functions for PDFio.
// //
// Copyright © 2021-2023 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -528,13 +528,6 @@ _pdfioTokenRead(_pdfio_token_t *tb, // I - Token buffer/stack
return (false); return (false);
} }
} }
if (bufptr == (buffer + 1))
{
_pdfioFileError(tb->pdf, "Empty name.");
*bufptr = '\0';
return (false);
}
break; break;
case '<' : // Potential hex string case '<' : // Potential hex string

View File

@ -1,7 +1,7 @@
// //
// PDF value functions for PDFio. // PDF value functions for PDFio.
// //
// Copyright © 2021-2024 by Michael R Sweet. // Copyright © 2021-2025 by Michael R Sweet.
// //
// Licensed under Apache License v2.0. See the file "LICENSE" for more // Licensed under Apache License v2.0. See the file "LICENSE" for more
// information. // information.
@ -125,7 +125,7 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
_pdfio_crypto_ctx_t ctx; // Decryption context _pdfio_crypto_ctx_t ctx; // Decryption context
_pdfio_crypto_cb_t cb; // Decryption callback _pdfio_crypto_cb_t cb; // Decryption callback
size_t ivlen; // Number of initialization vector bytes size_t ivlen; // Number of initialization vector bytes
uint8_t temp[32768]; // Temporary buffer for decryption uint8_t *temp = NULL; // Temporary buffer for decryption
size_t templen; // Number of actual data bytes size_t templen; // Number of actual data bytes
time_t timeval; // Date/time value time_t timeval; // Date/time value
@ -152,11 +152,16 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
case PDFIO_VALTYPE_BINARY : case PDFIO_VALTYPE_BINARY :
// Decrypt the binary string... // Decrypt the binary string...
if (v->value.binary.datalen > (sizeof(temp) - 32)) if (v->value.binary.datalen > PDFIO_MAX_STRING)
{ {
_pdfioFileError(pdf, "Unable to read encrypted binary string - too long."); _pdfioFileError(pdf, "Unable to read encrypted binary string - too long.");
return (false); return (false);
} }
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to read encrypted binary string - out of memory.");
return (false);
}
ivlen = v->value.binary.datalen; ivlen = v->value.binary.datalen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL) if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, v->value.binary.data, &ivlen)) == NULL)
@ -167,29 +172,59 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
// Copy the decrypted string back to the value and adjust the length... // Copy the decrypted string back to the value and adjust the length...
memcpy(v->value.binary.data, temp, templen); memcpy(v->value.binary.data, temp, templen);
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128) if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128 && temp[templen - 1] <= templen)
v->value.binary.datalen = templen - temp[templen - 1]; v->value.binary.datalen = templen - temp[templen - 1];
else else
v->value.binary.datalen = templen; v->value.binary.datalen = templen;
_pdfioStringFreeBuffer(pdf, (char *)temp);
break; break;
case PDFIO_VALTYPE_STRING : case PDFIO_VALTYPE_STRING :
// Decrypt regular string... // Decrypt regular string...
templen = strlen(v->value.string); templen = strlen(v->value.string);
if (templen > (sizeof(temp) - 33)) if (templen > (PDFIO_MAX_STRING - 1))
{ {
_pdfioFileError(pdf, "Unable to read encrypted string - too long."); _pdfioFileError(pdf, "Unable to read encrypted string - too long.");
return (false); return (false);
} }
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to read encrypted binary string - out of memory.");
return (false);
}
ivlen = templen; ivlen = templen;
if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, (uint8_t *)v->value.string, &ivlen)) == NULL) if ((cb = _pdfioCryptoMakeReader(pdf, obj, &ctx, (uint8_t *)v->value.string, &ivlen)) == NULL)
return (false); return (false);
templen = (cb)(&ctx, temp, (uint8_t *)v->value.string + ivlen, templen - ivlen); templen = (cb)(&ctx, temp, (uint8_t *)v->value.string + ivlen, templen - ivlen);
if (pdf->encryption >= PDFIO_ENCRYPTION_AES_128 && temp[templen - 1] <= templen)
templen -= temp[templen - 1];
temp[templen] = '\0'; temp[templen] = '\0';
if ((timeval = get_date_time((char *)temp)) != 0) if ((templen & 1) == 0 && (!memcmp(temp, "\376\377", 2) || !memcmp(temp, "\377\376", 2)))
{
// Convert UTF-16 to UTF-8...
char utf8[4096]; // Temporary string
_pdfio_utf16cpy(utf8, temp, templen, sizeof(utf8));
if ((timeval = get_date_time((char *)utf8)) != 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, utf8);
}
}
else if ((timeval = get_date_time((char *)temp)) != 0)
{ {
// Change the type to date... // Change the type to date...
v->type = PDFIO_VALTYPE_DATE; v->type = PDFIO_VALTYPE_DATE;
@ -200,6 +235,8 @@ _pdfioValueDecrypt(pdfio_file_t *pdf, // I - PDF file
// Copy the decrypted string back to the value... // Copy the decrypted string back to the value...
v->value.string = pdfioStringCreate(pdf, (char *)temp); v->value.string = pdfioStringCreate(pdf, (char *)temp);
} }
_pdfioStringFreeBuffer(pdf, (char *)temp);
break; break;
} }
@ -300,7 +337,9 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
_pdfio_value_t *v, // I - Value _pdfio_value_t *v, // I - Value
size_t depth) // I - Depth of value size_t depth) // I - Depth of value
{ {
char token[32768]; // Token buffer _pdfio_value_t *ret = NULL; // Return value
char *token = _pdfioStringAllocBuffer(pdf);
// Token buffer
time_t timeval; // Date/time value time_t timeval; // Date/time value
#ifdef DEBUG #ifdef DEBUG
static const char * const valtypes[] = static const char * const valtypes[] =
@ -322,8 +361,11 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v); PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v);
if (!_pdfioTokenGet(tb, token, sizeof(token))) if (!token)
return (NULL); goto done;
if (!_pdfioTokenGet(tb, token, PDFIO_MAX_STRING))
goto done;
if (!strcmp(token, "[")) if (!strcmp(token, "["))
{ {
@ -331,12 +373,14 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
if (depth >= PDFIO_MAX_DEPTH) if (depth >= PDFIO_MAX_DEPTH)
{ {
_pdfioFileError(pdf, "Too many nested arrays."); _pdfioFileError(pdf, "Too many nested arrays.");
return (NULL); goto done;
} }
v->type = PDFIO_VALTYPE_ARRAY; v->type = PDFIO_VALTYPE_ARRAY;
if ((v->value.array = _pdfioArrayRead(pdf, obj, tb, depth + 1)) == NULL) if ((v->value.array = _pdfioArrayRead(pdf, obj, tb, depth + 1)) == NULL)
return (NULL); goto done;
ret = v;
} }
else if (!strcmp(token, "<<")) else if (!strcmp(token, "<<"))
{ {
@ -344,29 +388,38 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
if (depth >= PDFIO_MAX_DEPTH) if (depth >= PDFIO_MAX_DEPTH)
{ {
_pdfioFileError(pdf, "Too many nested dictionaries."); _pdfioFileError(pdf, "Too many nested dictionaries.");
return (NULL); goto done;
} }
v->type = PDFIO_VALTYPE_DICT; v->type = PDFIO_VALTYPE_DICT;
if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL) if ((v->value.dict = _pdfioDictRead(pdf, obj, tb, depth + 1)) == NULL)
return (NULL); goto done;
}
else if (!strncmp(token, "(D:", 3) && (timeval = get_date_time(token + 1)) != 0) ret = v;
{
v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval;
} }
else if (token[0] == '(') else if (token[0] == '(')
{ {
// String if ((timeval = get_date_time(token + 1)) != 0)
v->type = PDFIO_VALTYPE_STRING; {
v->value.string = pdfioStringCreate(pdf, token + 1); // Date
v->type = PDFIO_VALTYPE_DATE;
v->value.date = timeval;
ret = v;
}
else
{
// String
v->type = PDFIO_VALTYPE_STRING;
v->value.string = pdfioStringCreate(pdf, token + 1);
ret = v;
}
} }
else if (token[0] == '/') else if (token[0] == '/')
{ {
// Name // Name
v->type = PDFIO_VALTYPE_NAME; v->type = PDFIO_VALTYPE_NAME;
v->value.name = pdfioStringCreate(pdf, token + 1); v->value.name = pdfioStringCreate(pdf, token + 1);
ret = v;
} }
else if (token[0] == '<') else if (token[0] == '<')
{ {
@ -379,7 +432,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
if ((v->value.binary.data = (unsigned char *)malloc(v->value.binary.datalen)) == NULL) if ((v->value.binary.data = (unsigned char *)malloc(v->value.binary.datalen)) == NULL)
{ {
_pdfioFileError(pdf, "Out of memory for hex string."); _pdfioFileError(pdf, "Out of memory for hex string.");
return (NULL); goto done;
} }
// Convert hex to binary... // Convert hex to binary...
@ -406,6 +459,8 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
*dataptr++ = (unsigned char)d; *dataptr++ = (unsigned char)d;
} }
ret = v;
} }
else if (strchr("0123456789-+.", token[0]) != NULL) else if (strchr("0123456789-+.", token[0]) != NULL)
{ {
@ -493,7 +548,8 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
PDFIO_DEBUG("_pdfioValueRead: Returning indirect value %lu %u R.\n", (unsigned long)v->value.indirect.number, v->value.indirect.generation); PDFIO_DEBUG("_pdfioValueRead: Returning indirect value %lu %u R.\n", (unsigned long)v->value.indirect.number, v->value.indirect.generation);
return (v); ret = v;
goto done;
} }
} }
} }
@ -501,27 +557,41 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file
// If we get here, we have a number... // If we get here, we have a number...
v->type = PDFIO_VALTYPE_NUMBER; v->type = PDFIO_VALTYPE_NUMBER;
v->value.number = _pdfio_strtod(pdf, token); v->value.number = _pdfio_strtod(pdf, token);
ret = v;
} }
else if (!strcmp(token, "true") || !strcmp(token, "false")) else if (!strcmp(token, "true") || !strcmp(token, "false"))
{ {
// Boolean value // Boolean value
v->type = PDFIO_VALTYPE_BOOLEAN; v->type = PDFIO_VALTYPE_BOOLEAN;
v->value.boolean = !strcmp(token, "true"); v->value.boolean = !strcmp(token, "true");
ret = v;
} }
else if (!strcmp(token, "null")) else if (!strcmp(token, "null"))
{ {
// null value // null value
v->type = PDFIO_VALTYPE_NULL; v->type = PDFIO_VALTYPE_NULL;
ret = v;
} }
else else
{ {
_pdfioFileError(pdf, "Unexpected '%s' token seen.", token); _pdfioFileError(pdf, "Unexpected '%s' token seen.", token);
return (NULL);
} }
PDFIO_DEBUG("_pdfioValueRead: Returning %s value.\n", valtypes[v->type]); done:
return (v); if (token)
_pdfioStringFreeBuffer(pdf, token);
if (ret)
{
PDFIO_DEBUG("_pdfioValueRead: Returning %s value.\n", valtypes[ret->type]);
return (ret);
}
else
{
PDFIO_DEBUG("_pdfioValueRead: Returning NULL.\n");
return (NULL);
}
} }
@ -546,8 +616,10 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
case PDFIO_VALTYPE_BINARY : case PDFIO_VALTYPE_BINARY :
{ {
size_t databytes; // Bytes to write size_t databytes; // Bytes to write
uint8_t temp[32768], // Temporary buffer for encryption uint8_t *temp = NULL, // Temporary buffer for encryption
*dataptr; // Pointer into data *dataptr; // Pointer into data
bool ret = false; // Return value
if (obj && pdf->encryption) if (obj && pdf->encryption)
{ {
@ -556,11 +628,16 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
_pdfio_crypto_cb_t cb; // Encryption callback _pdfio_crypto_cb_t cb; // Encryption callback
size_t ivlen; // Number of initialization vector bytes size_t ivlen; // Number of initialization vector bytes
if (v->value.binary.datalen > (sizeof(temp) - 32)) if (v->value.binary.datalen > PDFIO_MAX_STRING)
{ {
_pdfioFileError(pdf, "Unable to write encrypted binary string - too long."); _pdfioFileError(pdf, "Unable to write encrypted binary string - too long.");
return (false); return (false);
} }
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to write encrypted binary string - out of memory.");
return (false);
}
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen); cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
databytes = (cb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen; databytes = (cb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen;
@ -573,18 +650,25 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
} }
if (!_pdfioFilePuts(pdf, "<")) if (!_pdfioFilePuts(pdf, "<"))
return (false); goto bindone;
for (; databytes > 1; databytes -= 2, dataptr += 2) for (; databytes > 1; databytes -= 2, dataptr += 2)
{ {
if (!_pdfioFilePrintf(pdf, "%02X%02X", dataptr[0], dataptr[1])) if (!_pdfioFilePrintf(pdf, "%02X%02X", dataptr[0], dataptr[1]))
return (false); goto bindone;
} }
if (databytes > 0) if (databytes > 0 && !_pdfioFilePrintf(pdf, "%02X", dataptr[0]))
return (_pdfioFilePrintf(pdf, "%02X>", dataptr[0])); goto bindone;
else
return (_pdfioFilePuts(pdf, ">")); ret = _pdfioFilePuts(pdf, ">");
bindone:
if (temp)
_pdfioStringFreeBuffer(pdf, (char *)temp);
return (ret);
} }
case PDFIO_VALTYPE_BOOLEAN : case PDFIO_VALTYPE_BOOLEAN :
@ -609,7 +693,7 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
if (obj && pdf->encryption) if (obj && pdf->encryption)
{ {
// Write encrypted string... // Write encrypted string...
uint8_t temp[32768], // Encrypted bytes uint8_t temp[64], // Encrypted bytes
*tempptr; // Pointer into encrypted bytes *tempptr; // Pointer into encrypted bytes
_pdfio_crypto_ctx_t ctx; // Encryption context _pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback _pdfio_crypto_cb_t cb; // Encryption callback
@ -637,7 +721,7 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
} }
else else
{ {
return (_pdfioFilePrintf(pdf, "(%s)", datestr)); return (_pdfioFilePrintf(pdf, "%S", datestr));
} }
} }
@ -648,19 +732,19 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation)); return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation));
case PDFIO_VALTYPE_NAME : case PDFIO_VALTYPE_NAME :
return (_pdfioFilePrintf(pdf, "/%s", v->value.name)); return (_pdfioFilePrintf(pdf, "%N", v->value.name));
case PDFIO_VALTYPE_NULL : case PDFIO_VALTYPE_NULL :
return (_pdfioFilePuts(pdf, " null")); return (_pdfioFilePuts(pdf, " null"));
case PDFIO_VALTYPE_NUMBER : case PDFIO_VALTYPE_NUMBER :
return (_pdfioFilePrintf(pdf, " %g", v->value.number)); return (_pdfioFilePrintf(pdf, " %.6f", v->value.number));
case PDFIO_VALTYPE_STRING : case PDFIO_VALTYPE_STRING :
if (obj && pdf->encryption) if (obj && pdf->encryption)
{ {
// Write encrypted string... // Write encrypted string...
uint8_t temp[32768], // Encrypted bytes uint8_t *temp = NULL, // Encrypted bytes
*tempptr; // Pointer into encrypted bytes *tempptr; // Pointer into encrypted bytes
_pdfio_crypto_ctx_t ctx; // Encryption context _pdfio_crypto_ctx_t ctx; // Encryption context
_pdfio_crypto_cb_t cb; // Encryption callback _pdfio_crypto_cb_t cb; // Encryption callback
@ -668,74 +752,46 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
// Length of value // Length of value
ivlen, // Number of initialization vector bytes ivlen, // Number of initialization vector bytes
tempbytes; // Number of output bytes tempbytes; // Number of output bytes
bool ret = false; // Return value
if (len > (sizeof(temp) - 32)) if (len > PDFIO_MAX_STRING)
{ {
_pdfioFileError(pdf, "Unable to write encrypted string - too long."); _pdfioFileError(pdf, "Unable to write encrypted string - too long.");
return (false); return (false);
} }
else if ((temp = (uint8_t *)_pdfioStringAllocBuffer(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to write encrypted string - out of memory.");
return (false);
}
cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen); cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen);
tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen; tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen;
if (!_pdfioFilePuts(pdf, "<")) if (!_pdfioFilePuts(pdf, "<"))
return (false); goto strdone;
for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2) for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2)
{ {
if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1])) if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1]))
return (false); goto strdone;
} }
if (tempbytes > 0) if (tempbytes > 0 && !_pdfioFilePrintf(pdf, "%02X", *tempptr))
return (_pdfioFilePrintf(pdf, "%02X>", *tempptr)); goto strdone;
else
return (_pdfioFilePuts(pdf, ">")); ret = _pdfioFilePuts(pdf, ">");
strdone :
_pdfioStringFreeBuffer(pdf, (char *)temp);
return (ret);
} }
else else
{ {
// Write unencrypted string... // Write unencrypted string...
const char *start, // Start of fragment return (_pdfioFilePrintf(pdf, "%S", v->value.string));
*end; // End of fragment
if (!_pdfioFilePuts(pdf, "("))
return (false);
// Write a quoted string value...
for (start = v->value.string; *start; start = end)
{
// Find the next character that needs to be quoted...
for (end = start; *end; end ++)
{
if (*end == '\\' || *end == ')' || (*end & 255) < ' ')
break;
}
if (end > start)
{
// Write unquoted (safe) characters...
if (!_pdfioFileWrite(pdf, start, (size_t)(end - start)))
return (false);
}
if (*end)
{
// Quote this character...
bool success; // Did the write work?
if (*end == '\\' || *end == ')')
success = _pdfioFilePrintf(pdf, "\\%c", *end);
else
success = _pdfioFilePrintf(pdf, "\\%03o", *end);
if (!success)
return (false);
end ++;
}
}
return (_pdfioFilePuts(pdf, ")"));
} }
} }
@ -747,31 +803,59 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file
// 'get_date_time()' - Convert PDF date/time value to time_t. // 'get_date_time()' - Convert PDF date/time value to time_t.
// //
static time_t // O - Time in seconds static time_t // O - Time in seconds or `0` for none
get_date_time(const char *s) // I - PDF date/time value get_date_time(const char *s) // I - PDF date/time value
{ {
int i; // Looping var int i; // Looping var
struct tm dateval; // Date value struct tm dateval; // Date value
int offset; // Date offset int offset = 0; // Date offset in seconds
time_t t; // Time value
PDFIO_DEBUG("get_date_time(s=\"%s\")\n", s); PDFIO_DEBUG("get_date_time(s=\"%s\")\n", s);
// Possible date value of the form: // Possible date value of the form:
// //
// (D:YYYYMMDDhhmmssZ) // D:YYYYMMDDhhmmssZ
// (D:YYYYMMDDhhmmss+HH'mm) // D:YYYYMMDDhhmmss+HH'mm
// (D:YYYYMMDDhhmmss-HH'mm) // D:YYYYMMDDhhmmss-HH'mm
// //
if (strncmp(s, "D:", 2))
return (0);
for (i = 2; i < 16; i ++) for (i = 2; i < 16; i ++)
{ {
// Look for date/time digits...
if (!isdigit(s[i] & 255) || !s[i]) if (!isdigit(s[i] & 255) || !s[i])
break; break;
} }
if (i >= 16) if (i < 6 || (i & 1))
{ {
// Short year or missing digit...
return (0);
}
memset(&dateval, 0, sizeof(dateval));
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900;
if (i > 6)
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1;
if (i > 8)
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0';
else
dateval.tm_mday = 1;
if (i > 10)
dateval.tm_hour = (s[10] - '0') * 10 + s[11] - '0';
if (i > 12)
dateval.tm_min = (s[12] - '0') * 10 + s[13] - '0';
if (i > 14)
dateval.tm_sec = (s[14] - '0') * 10 + s[15] - '0';
if (i >= 16 && s[i])
{
// Get zone info...
if (s[i] == 'Z') if (s[i] == 'Z')
{ {
// UTC... // UTC...
@ -782,14 +866,20 @@ get_date_time(const char *s) // I - PDF date/time value
// Timezone offset from UTC... // Timezone offset from UTC...
if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255)) if (isdigit(s[i + 1] & 255) && isdigit(s[i + 2] & 255) && s[i + 3] == '\'' && isdigit(s[i + 4] & 255) && isdigit(s[i + 5] & 255))
{ {
offset = (s[i + 1] - '0') * 36000 + (s[i + 2] - '0') * 3600 + (s[i + 4] - '0') * 600 + (s[i + 5] - '0') * 60;
if (s[i] == '-')
offset = -offset;
i += 6; i += 6;
// Accept trailing quote, per PDF spec...
if (s[i] == '\'') if (s[i] == '\'')
i ++; i ++;
} }
} }
else if (!s[i]) else
{ {
// Missing zone info, invalid date string... // Random zone info, invalid date string...
return (0); return (0);
} }
} }
@ -800,26 +890,31 @@ get_date_time(const char *s) // I - PDF date/time value
return (0); return (0);
} }
// Date value... // Convert date value to time_t...
memset(&dateval, 0, sizeof(dateval)); #if _WIN32
if ((t = _mkgmtime(&dateval)) <= 0)
return (0);
dateval.tm_year = (s[2] - '0') * 1000 + (s[3] - '0') * 100 + (s[4] - '0') * 10 + s[5] - '0' - 1900; #elif defined(HAVE_TIMEGM)
dateval.tm_mon = (s[6] - '0') * 10 + s[7] - '0' - 1; if ((t = timegm(&dateval)) <= 0)
dateval.tm_mday = (s[8] - '0') * 10 + s[9] - '0'; return (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') #else
{ if ((t = mktime(&dateval)) <= 0)
offset = 0; return (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); # if defined(HAVE_TM_GMTOFF)
// Adjust the time value using the "tm_gmtoff" and "tm_isdst" members. As
// noted by M-HT on Github, this DST hack will fail in timezones where the
// DST offset is not one hour, such as Australia/Lord_Howe. Fortunately,
// this is unusual and most systems support the "timegm" function...
t += dateval.tm_gmtoff - 3600 * dateval.tm_isdst;
# else
// Adjust the time value using the even more legacy "timezone" variable,
// which also reflects any DST offset...
t += timezone;
# endif // HAVE_TM_GMTOFF
#endif // _WIN32
return (t - offset);
} }

22
pdfio.h
View File

@ -20,10 +20,12 @@ extern "C" {
// //
// Version number... // Version numbers...
// //
# define PDFIO_VERSION "1.4.1" # define PDFIO_VERSION "1.6.0"
# define PDFIO_VERSION_MAJOR 1
# define PDFIO_VERSION_MINOR 6
// //
@ -32,11 +34,9 @@ extern "C" {
# if defined(__has_extension) || defined(__GNUC__) # if defined(__has_extension) || defined(__GNUC__)
# define _PDFIO_PUBLIC __attribute__ ((visibility("default"))) # define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
# define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
# define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC # define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC
# else # else
# define _PDFIO_PUBLIC # define _PDFIO_PUBLIC
# define _PDFIO_FORMAT(a,b)
# define _PDFIO_DEPRECATED # define _PDFIO_DEPRECATED
# endif // __has_extension || __GNUC__ # endif // __has_extension || __GNUC__
@ -46,7 +46,7 @@ extern "C" {
// //
# if _WIN32 # if _WIN32
typedef __int64 ssize_t; // POSIX type not present on Windows... @private@ typedef __int64 ssize_t; // POSIX type not present on Windows @private@
# endif // _WIN32 # endif // _WIN32
typedef struct _pdfio_array_s pdfio_array_t; typedef struct _pdfio_array_s pdfio_array_t;
@ -62,7 +62,7 @@ typedef bool (*pdfio_error_cb_t)(pdfio_file_t *pdf, const char *message, void *d
typedef enum pdfio_encryption_e // PDF encryption modes typedef enum pdfio_encryption_e // PDF encryption modes
{ {
PDFIO_ENCRYPTION_NONE = 0, // No encryption PDFIO_ENCRYPTION_NONE = 0, // No encryption
PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3) PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3, reading only)
PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4) 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_128, // 128-bit AES encryption (PDF 1.6)
PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@ PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@
@ -181,7 +181,7 @@ extern bool pdfioDictSetNumber(pdfio_dict_t *dict, const char *key, double valu
extern bool pdfioDictSetObj(pdfio_dict_t *dict, const char *key, pdfio_obj_t *value) _PDFIO_PUBLIC; extern bool pdfioDictSetObj(pdfio_dict_t *dict, const char *key, pdfio_obj_t *value) _PDFIO_PUBLIC;
extern bool pdfioDictSetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *value) _PDFIO_PUBLIC; extern bool pdfioDictSetRect(pdfio_dict_t *dict, const char *key, pdfio_rect_t *value) _PDFIO_PUBLIC;
extern bool pdfioDictSetString(pdfio_dict_t *dict, const char *key, const char *value) _PDFIO_PUBLIC; extern bool pdfioDictSetString(pdfio_dict_t *dict, const char *key, const char *value) _PDFIO_PUBLIC;
extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(3,4); extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char *format, ...) _PDFIO_PUBLIC;
extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC; 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_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;
@ -201,6 +201,8 @@ extern time_t pdfioFileGetCreationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetCreator(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; extern pdfio_array_t *pdfioFileGetID(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetKeywords(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern const char *pdfioFileGetKeywords(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetLanguage(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern time_t pdfioFileGetModificationDate(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetName(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern const char *pdfioFileGetName(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern size_t pdfioFileGetNumObjs(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern size_t pdfioFileGetNumObjs(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern size_t pdfioFileGetNumPages(pdfio_file_t *pdf) _PDFIO_PUBLIC; extern size_t pdfioFileGetNumPages(pdfio_file_t *pdf) _PDFIO_PUBLIC;
@ -216,6 +218,8 @@ extern void pdfioFileSetAuthor(pdfio_file_t *pdf, const char *value) _PDFIO_PUB
extern void pdfioFileSetCreationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC; extern void pdfioFileSetCreationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC;
extern void pdfioFileSetCreator(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC; extern void pdfioFileSetCreator(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetKeywords(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC; extern void pdfioFileSetKeywords(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetLanguage(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetModificationDate(pdfio_file_t *pdf, time_t value) _PDFIO_PUBLIC;
extern bool pdfioFileSetPermissions(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_PUBLIC; extern bool pdfioFileSetPermissions(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_PUBLIC;
extern void pdfioFileSetSubject(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC; extern void pdfioFileSetSubject(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
extern void pdfioFileSetTitle(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC; extern void pdfioFileSetTitle(pdfio_file_t *pdf, const char *value) _PDFIO_PUBLIC;
@ -241,14 +245,14 @@ extern bool pdfioStreamClose(pdfio_stream_t *st) _PDFIO_PUBLIC;
extern bool pdfioStreamConsume(pdfio_stream_t *st, size_t bytes) _PDFIO_PUBLIC; extern bool pdfioStreamConsume(pdfio_stream_t *st, size_t bytes) _PDFIO_PUBLIC;
extern bool pdfioStreamGetToken(pdfio_stream_t *st, char *buffer, size_t bufsize) _PDFIO_PUBLIC; extern bool pdfioStreamGetToken(pdfio_stream_t *st, char *buffer, size_t bufsize) _PDFIO_PUBLIC;
extern ssize_t pdfioStreamPeek(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC; extern ssize_t pdfioStreamPeek(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
extern bool pdfioStreamPrintf(pdfio_stream_t *st, const char *format, ...) _PDFIO_PUBLIC _PDFIO_FORMAT(2,3); extern bool pdfioStreamPrintf(pdfio_stream_t *st, const char *format, ...) _PDFIO_PUBLIC;
extern bool pdfioStreamPutChar(pdfio_stream_t *st, int ch) _PDFIO_PUBLIC; extern bool pdfioStreamPutChar(pdfio_stream_t *st, int ch) _PDFIO_PUBLIC;
extern bool pdfioStreamPuts(pdfio_stream_t *st, const char *s) _PDFIO_PUBLIC; extern bool pdfioStreamPuts(pdfio_stream_t *st, const char *s) _PDFIO_PUBLIC;
extern ssize_t pdfioStreamRead(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC; extern ssize_t pdfioStreamRead(pdfio_stream_t *st, void *buffer, size_t bytes) _PDFIO_PUBLIC;
extern bool pdfioStreamWrite(pdfio_stream_t *st, const void *buffer, size_t bytes) _PDFIO_PUBLIC; extern bool pdfioStreamWrite(pdfio_stream_t *st, const void *buffer, size_t bytes) _PDFIO_PUBLIC;
extern char *pdfioStringCreate(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC; extern char *pdfioStringCreate(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_FORMAT(2,3) _PDFIO_PUBLIC; extern char *pdfioStringCreatef(pdfio_file_t *pdf, const char *format, ...) _PDFIO_PUBLIC;
# ifdef __cplusplus # ifdef __cplusplus

View File

@ -7,7 +7,7 @@ Name: pdfio
Description: PDF read/write library Description: PDF read/write library
Version: @PDFIO_VERSION@ Version: @PDFIO_VERSION@
URL: https://www.msweet.org/pdfio URL: https://www.msweet.org/pdfio
Requires: @PKGCONFIG_REQUIRES@ Cflags: @PKGCONFIG_CFLAGS@
Libs: @PKGCONFIG_LIBS@ Libs: @PKGCONFIG_LIBS@
Libs.private: @PKGCONFIG_LIBS_PRIVATE@ Libs.private: @PKGCONFIG_LIBS_PRIVATE@
Cflags: @PKGCONFIG_CFLAGS@ Requires: @PKGCONFIG_REQUIRES@

View File

@ -115,7 +115,7 @@
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>HAVE_LIBPNG;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
@ -130,7 +130,7 @@
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>HAVE_LIBPNG;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
</ClCompile> </ClCompile>
<Link> <Link>
@ -172,6 +172,8 @@
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
<Import Project="packages\libpng_native.redist.1.6.30\build\native\libpng_native.redist.targets" Condition="Exists('packages\libpng_native.redist.1.6.30\build\native\libpng_native.redist.targets')" />
<Import Project="packages\libpng_native.1.6.30\build\native\libpng_native.targets" Condition="Exists('packages\libpng_native.1.6.30\build\native\libpng_native.targets')" />
<Import Project="packages\zlib_native.redist.1.2.11\build\native\zlib_native.redist.targets" Condition="Exists('packages\zlib_native.redist.1.2.11\build\native\zlib_native.redist.targets')" /> <Import Project="packages\zlib_native.redist.1.2.11\build\native\zlib_native.redist.targets" Condition="Exists('packages\zlib_native.redist.1.2.11\build\native\zlib_native.redist.targets')" />
<Import Project="packages\zlib_native.1.2.11\build\native\zlib_native.targets" Condition="Exists('packages\zlib_native.1.2.11\build\native\zlib_native.targets')" /> <Import Project="packages\zlib_native.1.2.11\build\native\zlib_native.targets" Condition="Exists('packages\zlib_native.1.2.11\build\native\zlib_native.targets')" />
</ImportGroup> </ImportGroup>

View File

@ -1,5 +1,5 @@
LIBRARY pdfio1 LIBRARY pdfio1
VERSION 1.4 VERSION 1.6
EXPORTS EXPORTS
_pdfioArrayDebug _pdfioArrayDebug
_pdfioArrayDecrypt _pdfioArrayDecrypt
@ -51,8 +51,11 @@ _pdfioObjDelete
_pdfioObjGetExtension _pdfioObjGetExtension
_pdfioObjLoad _pdfioObjLoad
_pdfioObjSetExtension _pdfioObjSetExtension
_pdfioObjWriteHeader
_pdfioStreamCreate _pdfioStreamCreate
_pdfioStreamOpen _pdfioStreamOpen
_pdfioStringAllocBuffer
_pdfioStringFreeBuffer
_pdfioStringIsAllocated _pdfioStringIsAllocated
_pdfioTokenClear _pdfioTokenClear
_pdfioTokenFlush _pdfioTokenFlush
@ -66,7 +69,9 @@ _pdfioValueDecrypt
_pdfioValueDelete _pdfioValueDelete
_pdfioValueRead _pdfioValueRead
_pdfioValueWrite _pdfioValueWrite
_pdfio_strlcpy
_pdfio_strtod _pdfio_strtod
_pdfio_utf16cpy
_pdfio_vsnprintf _pdfio_vsnprintf
pdfioArrayAppendArray pdfioArrayAppendArray
pdfioArrayAppendBinary pdfioArrayAppendBinary
@ -182,11 +187,14 @@ pdfioDictSetObj
pdfioDictSetRect pdfioDictSetRect
pdfioDictSetString pdfioDictSetString
pdfioDictSetStringf pdfioDictSetStringf
pdfioFileAddOutputIntent
pdfioFileClose pdfioFileClose
pdfioFileCreate pdfioFileCreate
pdfioFileCreateArrayObj pdfioFileCreateArrayObj
pdfioFileCreateFontObjFromBase pdfioFileCreateFontObjFromBase
pdfioFileCreateFontObjFromData
pdfioFileCreateFontObjFromFile pdfioFileCreateFontObjFromFile
pdfioFileCreateICCObjFromData
pdfioFileCreateICCObjFromFile pdfioFileCreateICCObjFromFile
pdfioFileCreateImageObjFromData pdfioFileCreateImageObjFromData
pdfioFileCreateImageObjFromFile pdfioFileCreateImageObjFromFile
@ -204,6 +212,8 @@ pdfioFileGetCreationDate
pdfioFileGetCreator pdfioFileGetCreator
pdfioFileGetID pdfioFileGetID
pdfioFileGetKeywords pdfioFileGetKeywords
pdfioFileGetLanguage
pdfioFileGetModificationDate
pdfioFileGetName pdfioFileGetName
pdfioFileGetNumObjs pdfioFileGetNumObjs
pdfioFileGetNumPages pdfioFileGetNumPages
@ -219,6 +229,8 @@ pdfioFileSetAuthor
pdfioFileSetCreationDate pdfioFileSetCreationDate
pdfioFileSetCreator pdfioFileSetCreator
pdfioFileSetKeywords pdfioFileSetKeywords
pdfioFileSetLanguage
pdfioFileSetModificationDate
pdfioFileSetPermissions pdfioFileSetPermissions
pdfioFileSetSubject pdfioFileSetSubject
pdfioFileSetTitle pdfioFileSetTitle

View File

@ -3,7 +3,7 @@
<metadata> <metadata>
<id>pdfio_native</id> <id>pdfio_native</id>
<title>PDFio Library for VS2019+</title> <title>PDFio Library for VS2019+</title>
<version>1.4.1</version> <version>1.6.0</version>
<authors>Michael R Sweet</authors> <authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners> <owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl> <projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
@ -16,7 +16,8 @@
<copyright>Copyright © 2019-2025 by Michael R Sweet</copyright> <copyright>Copyright © 2019-2025 by Michael R Sweet</copyright>
<tags>pdf file native</tags> <tags>pdf file native</tags>
<dependencies> <dependencies>
<dependency id="pdfio_native.redist" version="1.4.1" /> <dependency id="pdfio_native.redist" version="1.6.0" />
<dependency id="libpng_native.redist" version="1.6.30" />
<dependency id="zlib_native.redist" version="1.2.11" /> <dependency id="zlib_native.redist" version="1.2.11" />
</dependencies> </dependencies>
</metadata> </metadata>

View File

@ -3,7 +3,7 @@
<metadata> <metadata>
<id>pdfio_native.redist</id> <id>pdfio_native.redist</id>
<title>PDFio Library for VS2019+</title> <title>PDFio Library for VS2019+</title>
<version>1.4.1</version> <version>1.6.0</version>
<authors>Michael R Sweet</authors> <authors>Michael R Sweet</authors>
<owners>michaelrsweet</owners> <owners>michaelrsweet</owners>
<projectUrl>https://github.com/michaelrsweet/pappl</projectUrl> <projectUrl>https://github.com/michaelrsweet/pappl</projectUrl>
@ -16,6 +16,7 @@
<copyright>Copyright © 2019-2025 by Michael R Sweet</copyright> <copyright>Copyright © 2019-2025 by Michael R Sweet</copyright>
<tags>pdf file native</tags> <tags>pdf file native</tags>
<dependencies> <dependencies>
<dependency id="libpng_native.redist" version="1.6.30" />
<dependency id="zlib_native.redist" version="1.2.11" /> <dependency id="zlib_native.redist" version="1.2.11" />
</dependencies> </dependencies>
</metadata> </metadata>

View File

@ -7,6 +7,8 @@
:: Copy dependent DLLs to the named build directory :: Copy dependent DLLs to the named build directory
echo Copying DLLs echo Copying DLLs
copy packages\libpng_native.redist.1.6.30\build\native\bin\x64\Debug\*.dll %1
copy packages\libpng_native.redist.1.6.30\build\native\bin\x64\Release\*.dll %1
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\Debug\*.dll %1
copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Release\*.dll %1 copy packages\zlib_native.redist.1.2.11\build\native\bin\x64\Release\*.dll %1

30
test-corpus.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/sh
#
# Script to test PDFio against a directory of PDF files.
#
# Copyright © 2025 by Michael R Sweet.
#
# Licensed under Apache License v2.0. See the file "LICENSE" for more
# information.
#
# Usage:
#
# ./test-corpus.sh DIRECTORY
#
if test $# = 0; then
echo "Usage: ./test-corpus.sh DIRECTORY"
exit 1
fi
for file in $(find "$@" -name \*.pdf -print); do
# Run testpdfio to test loading the file...
./testpdfio $file >$file.log 2>&1
if test $? = 0; then
# Passed
rm -f $file.log
else
# Failed, preserve log and write filename to stdout...
echo $file
fi
done

282
test.h Normal file
View File

@ -0,0 +1,282 @@
//
// Unit test header for C/C++ programs.
//
// Copyright © 2021-2025 by Michael R Sweet.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
#ifndef TEST_H
# define TEST_H
# include <stdio.h>
# include <stdlib.h>
# include <stdarg.h>
# include <stdbool.h>
# include <string.h>
# if _WIN32
# include <io.h>
# define isatty(f) _isatty(f)
# else
# include <unistd.h>
# endif // !_WIN32
# ifdef __cplusplus
extern "C" {
# endif // __cplusplus
//
// This header implements a simple unit test framework for C/C++ programs.
// Inline functions are provided to write a test summary to stdout and the
// details to stderr. This allows unit test programs to output a summary to
// stdout with details sent to stderr, e.g.:
//
// mytestprogram 2>test.log
//
// Documentation:
//
// void testBegin(const char *title, ...)
//
// Start a test with a printf-style title message. "Title:" (the formatted
// title followed by a colon) is output.
//
// void testEnd(bool pass)
//
// End a test without an additional message. "pass" should be `true` if the
// test passed and `false` otherwise. "PASS" or "FAIL" is output.
//
// void testEndMessage(bool pass, const char *message, ...)
//
// End a test with an additional printf-style message. "pass" should be
// `true` if the test passed and `false` otherwise. "PASS (message)" or
// "FAIL (message)" is output.
//
// testError(const char *error, ...)
//
// Sends a formatted error string to stderr.
//
// testHexDump(const unsigned char *buffer, size_t bytes)
//
// Sends a hex dump of the specified buffer to stderr.
//
// testMessage(const char *error, ...)
//
// Outputs a formatted message string.
//
// testProgress(void)
//
// Shows a progress spinner for long-running tests.
//
// bool testsPassed
//
// This global variable specifies whether all tests have passed (`true`)
// or one or more have failed (`false`).
//
static bool testsPassed = true; // All tests passed?
static int test_progress; // Current progress
static char test_title[1024] = ""; // Current test title
// Start a test
static inline void
testBegin(const char *title, ...) // I - printf-style title string
{
va_list ap; // Pointer to additional arguments
// Format the title string
va_start(ap, title);
vsnprintf(test_title, sizeof(test_title), title, ap);
va_end(ap);
// Send the title to stdout and stderr...
test_progress = 0;
printf("%s: ", test_title);
fflush(stdout);
if (!isatty(2))
fprintf(stderr, "%s: ", test_title);
}
// End a test with no additional information
static inline void
testEnd(bool pass) // I - `true` if the test passed, `false` otherwise
{
// Send the test result to stdout and stderr
if (test_progress)
putchar('\b');
if (!pass)
testsPassed = false;
puts(pass ? "PASS" : "FAIL");
if (!isatty(2))
fputs(pass ? "PASS\n" : "FAIL\n", stderr);
test_title[0] = '\0';
}
// End a test with no additional information
static inline void
testEndMessage(bool pass, // I - `true` if the test passed, `false` otherwise
const char *message, ...)// I - printf-style message
{
char buffer[1024]; // Formatted title string
va_list ap; // Pointer to additional arguments
// Format the title string
va_start(ap, message);
vsnprintf(buffer, sizeof(buffer), message, ap);
va_end(ap);
// Send the test result to stdout and stderr
if (test_progress)
putchar('\b');
printf(pass ? "PASS (%s)\n" : "FAIL (%s)\n", buffer);
if (!isatty(2))
fprintf(stderr, pass ? "PASS (%s)\n" : "FAIL (%s)\n", buffer);
test_title[0] = '\0';
}
// Show/update a progress spinner
static inline void
testProgress(void)
{
if (test_progress)
putchar('\b');
putchar("-\\|/"[test_progress & 3]);
fflush(stdout);
test_progress ++;
}
// Show an error to stderr...
static inline void
testError(const char *error, ...) // I - printf-style error string
{
char buffer[1024]; // Formatted title string
va_list ap; // Pointer to additional arguments
// Format the error string
va_start(ap, error);
vsnprintf(buffer, sizeof(buffer), error, ap);
va_end(ap);
// Send the error to stderr...
fprintf(stderr, "%s\n", buffer);
if (test_title[0])
fprintf(stderr, "%s: ", test_title);
}
// Show a message to stdout and stderr...
static inline void
testMessage(const char *error, ...) // I - printf-style error string
{
char buffer[1024]; // Formatted title string
va_list ap; // Pointer to additional arguments
// Format the error string
va_start(ap, error);
vsnprintf(buffer, sizeof(buffer), error, ap);
va_end(ap);
// Send the message to stdout and stderr too if needed...
printf("%s\n", buffer);
if (test_title[0])
{
printf("%s: ", test_title);
fflush(stdout);
}
if (!isatty(2))
{
fprintf(stderr, "%s\n", buffer);
if (test_title[0])
fprintf(stderr, "%s: ", test_title);
}
}
// Show a hex dump of a buffer to stderr...
static inline void
testHexDump(const unsigned char *buffer,// I - Buffer
size_t bytes) // I - Number of bytes
{
size_t i, j; // Looping vars
int ch; // Current ASCII char
if (test_title[0])
fputs("\n", stderr);
// Show lines of 16 bytes at a time...
for (i = 0; i < bytes; i += 16)
{
// Show the offset...
fprintf(stderr, "%04x ", (unsigned)i);
// Then up to 16 bytes in hex...
for (j = 0; j < 16; j ++)
{
if ((i + j) < bytes)
fprintf(stderr, " %02x", buffer[i + j]);
else
fputs(" ", stderr);
}
// Then the ASCII representation of the bytes...
fputs(" ", stderr);
for (j = 0; j < 16 && (i + j) < bytes; j ++)
{
ch = buffer[i + j] & 127;
if (ch < ' ' || ch == 127)
fputc('.', stderr);
else
fputc(ch, stderr);
}
fputc('\n', stderr);
}
if (test_title[0])
fprintf(stderr, "%s: ", test_title);
}
# ifdef __cplusplus
}
# endif // __cplusplus
#endif // !TEST_H

Binary file not shown.

View File

@ -0,0 +1,5 @@
https://github.com/saucecontrol/Compact-ICC-Profiles
All profiles in this collection are released to the public domain under the
Creative Commons CC0 license. They are free from restrictions on distribution
and use to the extent allowed by law.

View File

@ -0,0 +1,9 @@
PngSuite
--------
Permission to use, copy, modify and distribute these images for any
purpose and without fee is hereby granted.
(c) Willem van Schaik, 1996, 2011

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Some files were not shown because too many files have changed in this diff Show More