54 Commits

Author SHA1 Message Date
cd1406e158 Update docos.
Fix static library build commands - remove archive before building it fresh.
2024-01-24 11:03:58 -05:00
59deee020a Fix some Clang warnings. 2024-01-24 10:58:11 -05:00
476013706e Prep for 1.2.0 release, bump copyright. 2024-01-24 10:53:53 -05:00
a43a9d9e32 Fix whitespace. 2023-12-18 10:04:24 -05:00
abc69b3361 Save work. 2023-12-18 10:04:00 -05:00
83bfb135c6 Add some more debug printfs, relocate extra newline detection after stream
token.
2023-12-15 12:57:31 -05:00
2dfb560f8b Add more debug logging. 2023-12-14 17:05:10 -05:00
7330cc35ba Defer object/value decryption to after the object is loaded (Issue #42) 2023-12-14 16:02:26 -05:00
5d760e7315 Update some debug printfs. 2023-12-13 12:48:31 -05:00
2a85baaf81 Increase the maximum number of object streams in a file (Issue #58) - most files
only contain 1 or 2...

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

Look at the last 1k of the file to find the startxref marker.
2023-12-12 12:24:49 -05:00
038fd8686b Fix trailer dictionary handling (Issue #58)
Fix generation number handling for object 0 (Issue #59)
2023-12-11 19:56:00 -05:00
7084105dc4 Merge pull request #57 from eli-schwartz/pdfio-pc-redundancy
pdfio.pc: use -lm as specified in configure
2023-12-10 19:35:37 -05:00
9f06f22281 pdfio.pc: use -lm as specified in configure
It is already configured in, in the correct place. Currently, it is
listed twice in Libs.private, if --enable-shared is used. And it is
redundant if the build is static instead, since it is recorded in Libs.
2023-12-10 16:32:52 -05:00
cb6b493df6 Update configure script. 2023-12-10 15:38:35 -05:00
2753a82eb9 Merge pull request #56 from eli-schwartz/misspelled
fix misspelled variable: PKCONFIG
2023-12-10 15:38:12 -05:00
ddb8ddff9c fix misspelled variable: PKCONFIG
This prevented using pkg-config for zlib lookup.
2023-12-10 01:39:23 -05:00
c992b2ba89 Update the token reading code to protect against obvious format abuses.
Update the xref loading code to protect against looping xref tables.
2023-12-07 17:50:52 -05:00
ed723a46dc Make sure buffer is terminated on error. 2023-12-06 11:21:33 -05:00
6906a9a708 Fix docos for pdfioFileOpen. 2023-12-05 19:22:47 -05:00
6a381a55fe Update macOS build docos. 2023-12-05 18:41:26 -05:00
fc3580a948 Update build docos. 2023-12-05 18:39:20 -05:00
6b5c30b4be Remove PDFIO_ENCRYPTION_AES_256 from docos. 2023-12-05 14:30:41 -05:00
a0cdb261ff Update CONTRIBUTING docos. 2023-12-05 14:07:52 -05:00
34dbf6c2fe Documentation cleanup. 2023-12-05 13:49:58 -05:00
86d842167a Bring back mis-named pdfioContentTextNextLine. 2023-12-05 13:33:07 -05:00
16c8b830b8 Add pdfioFileCreateNumber/StringObj functions (Issue #14) 2023-12-05 08:16:41 -05:00
7ff051fc8b Add pdfioContentTextNewLineShow/f functions (Issue #24) 2023-12-05 07:49:49 -05:00
927452d1eb Changelog and exports updates. 2023-12-04 21:22:13 -05:00
f1ad982fd1 Update docos. 2023-12-04 21:21:18 -05:00
c188cb8dad Finish implementation of pdfioContentTextMeasure (Issue #17) 2023-12-04 21:20:51 -05:00
4919783da5 Save work on string measure support (Issue #17) 2023-12-04 18:54:33 -05:00
86281750e5 Fix man page installation to use mandir (Issue #55) 2023-12-04 08:50:28 -05:00
d92b72ed02 Add math library. 2023-12-03 20:26:27 -05:00
a195c023af Fix CI scripts. 2023-12-03 20:20:23 -05:00
43000ff01f Fix up targets. 2023-12-03 19:56:25 -05:00
c6f17cc20f Fix some warnings. 2023-12-03 19:23:36 -05:00
41146adbdf Adopt autoconf (Issue #54) 2023-12-03 19:16:34 -05:00
cd80c3037d Prep for 1.1.4 release. 2023-12-03 16:55:57 -05:00
97934ab995 Add ToUnicode map. 2023-11-19 20:49:30 -05:00
088646e1cf Drop the FEFF prefix on Unicode strings. 2023-11-19 07:30:17 -05:00
3f0aad7564 Fix another bug in the CMAPv4 code, and a bug in the unit test program. 2023-11-19 07:06:35 -05:00
d36df63b57 Fix a TrueType CMAP decoding bug and add a NotoSans-Regular test page. 2023-11-18 22:15:52 -05:00
a5dfac7495 Bump version. 2023-11-18 18:23:22 -05:00
0258384d53 Range check encrypted string length (Issue #52) 2023-11-18 18:22:11 -05:00
9fec2195d0 Update changelog. 2023-11-15 10:14:02 -05:00
8ccbdaed94 Update documentation. 2023-11-15 09:28:57 -05:00
4804db38a5 Fix missing ivlen initializer for 40-bit RC4 (Issue #51) 2023-11-15 08:43:07 -05:00
ddd984215a Save work (debug printfs, etc.) 2023-11-15 08:38:47 -05:00
efe7c01015 Fix typo and 'make debug'. 2023-11-14 18:38:26 -05:00
600fa4ce59 Fix Unicode font handling (Issue #16) 2023-11-14 18:19:34 -05:00
688810f143 Save work. 2023-11-13 16:18:02 -05:00
858cc101b6 Save work. 2023-11-13 13:39:06 -05:00
42 changed files with 11896 additions and 913 deletions

View File

@ -9,23 +9,26 @@ on:
jobs:
build-linux:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: update build environment
- name: Checkout PDFio sources
uses: actions/checkout@v4
- name: Update Build Environment
run: sudo apt-get update --fix-missing -y
- name: install prerequisites
- name: Install Prerequisites
run: sudo apt-get install -y cppcheck zlib1g-dev
- name: make
- name: Configure PDFio
run: ./configure --enable-debug --enable-sanitizer --enable-maintainer
- name: Build PDFio
run: make "COMMONFLAGS=-g -fsanitize=address"
- name: test
- name: Test PDFio
env:
ASAN_OPTIONS: leak_check_at_exit=false
run: make test
- name: clang static analyzer
- name: Run Clang Static Analyzer
run: make CC=clang "GHA_ERROR=::error::" clang
- name: cppcheck
- name: Run cppcheck
run: make "GHA_ERROR=::error::" cppcheck
build-macos:
@ -33,12 +36,15 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: make
- name: Checkout PDFio sources
uses: actions/checkout@v4
- name: Configure PDFio
run: ./configure --enable-debug --enable-sanitizer --enable-maintainer
- name: Build PDFio
run: make "COMMONFLAGS=-g -fsanitize=address"
- name: test
- name: Test PDFio
run: make test
- name: clang static analyzer
- name: Run Clang Static Analyzer
run: make CC=clang "GHA_ERROR=::error::" clang
build-windows:
@ -46,10 +52,11 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: setup-msbuild
- name: Checkout PDFio sources
uses: actions/checkout@v4
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1.0.2
- name: nuget restore
- name: Nuget Restore
run: nuget restore pdfio.sln
- name: msbuild
- name: Build PDFio
run: msbuild pdfio.sln

View File

@ -23,15 +23,15 @@ jobs:
language: [ cpp ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Checkout PDFio sources
uses: actions/checkout@v4
with:
submodules: recursive
- name: Update build environment
- name: Update Build Environment
run: sudo apt-get update --fix-missing -y
- name: Install prerequisites
- name: Install Prerequisites
run: sudo apt-get install -y zlib1g-dev
- name: Initialize CodeQL

View File

@ -7,10 +7,11 @@ jobs:
runs-on: ubuntu-latest
environment: Coverity
steps:
- uses: actions/checkout@v2
- name: update build environment
- name: Checkout PDFio sources
uses: actions/checkout@v4
- name: Update Build Environment
run: sudo apt-get update --fix-missing -y
- name: install prerequisites
- name: Install Prerequisites
run: sudo apt-get install -y zlib1g-dev
- name: Download Coverity Build Tool
run: |
@ -20,11 +21,15 @@ jobs:
env:
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
- name: Build with cov-build
- name: Configure PDFio
run: ./configure --enable-debug --enable-maintainer
- name: Build PDFio with cov-build
run: |
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
cov-build --dir cov-int make
- name: Submit the result to Coverity Scan
- name: Submit the Result to Coverity Scan
run: |
tar czvf cov.tgz cov-int
curl \

6
.gitignore vendored
View File

@ -5,12 +5,18 @@
*.so.1
/.vs
/afl-output
/autom4te.cache
/config.log
/config.status
/doc/pdfio.epub
/Makefile
/packages
/pdfio.pc
/pdfio.xcodeproj/xcshareddata
/pdfio-*.tar.gz*
/pdfio-*.zip*
/pdfiototext
/testpdfio
/testpdfio-*.pdf
/testttf
/x64

View File

@ -2,6 +2,46 @@ Changes in PDFio
================
v1.2.0 (January 24, 2024)
-------------------------
- Now use autoconf to configure the PDFio sources (Issue #54)
- Added `pdfioFileCreateNumberObj` and `pdfioFileCreateStringObj` functions
(Issue #14)
- Added `pdfioContentTextMeasure` function (Issue #17)
- Added `pdfioContentTextNewLineShow` and `pdfioContentTextNewLineShowf`
functions (Issue #24)
- Renamed `pdfioContentTextNextLine` to `pdfioContentTextNewLine`.
- Updated the maximum number of object streams in a single file from 4096 to
8192 (Issue #58)
- Updated the token reading code to protect against some obvious abuses of the
PDF format.
- Updated the xref reading code to protect against loops.
- Updated the object handling code to use a binary insertion algorithm -
provides a significant (~800x) improvement in open times.
- Fixed handling of encrypted PDFs with per-object file IDs (Issue #42)
- Fixed handling of of trailer dictionaries that started immediately after the
"trailer" keyword (Issue #58)
- Fixed handling of invalid, but common, PDF files with a generation number of
65536 in the xref table (Issue #59)
v1.1.4 (December 3, 2023)
-------------------------
- Fixed detection of encrypted strings that are too short (Issue #52)
- Fixed a TrueType CMAP decoding bug.
- Fixed a text rendering issue for Asian text.
- Added a ToUnicode map for Unicode text to support text copying.
v1.1.3 (November 15, 2023)
--------------------------
- Fixed Unicode font support (Issue #16)
- Fixed missing initializer for 40-bit RC4 encryption (Issue #51)
v1.1.2 (October 10, 2023)
-------------------------

View File

@ -43,14 +43,17 @@ Contents
Build System
------------
The build system uses a simple POSIX makefile to build a static or shared
library. To improve portability, makefiles *must not* make use of features
unique to GNU make. See the [Makefile Guidelines](#makefile-guidelines) section
for a description of the allowed make features and makefile guidelines.
The build system uses [GNU autoconf][AUTOCONF] to create a simple POSIX makefile
to build static and/or shared libraries. To improve portability, makefiles
*must not* make use of features unique to GNU make. See the
[Makefile Guidelines](#makefile-guidelines) section for a description of the
allowed make features and makefile guidelines.
An Xcode project is provided for macOS/iOS developers, and a Visual Studio
solution and projects for Windows developers.
[AUTOCONF]: https://www.gnu.org/software/autoconf/
Version Numbering
-----------------
@ -373,14 +376,15 @@ The following variables are defined in the makefile:
- `CC`; the C compiler command,
- `CFLAGS`; options for the C compiler,
- `CODESIGN_IDENTITY`: the code signing identity,
- `COMMONFLAGS`; common compiler optimization options,
- `CPPFLAGS`; options for the C preprocessor,
- `DESTDIR`/`DSTROOT`: the destination root directory when installing.
- `DSO`; the shared library building command,
- `DSOFLAGS`; options for the shared library building command,
- `DSONAME`: the root name of the shared library
- `LDFLAGS`; options for the linker,
- `LIBPDFIO`: the name of the primary (shared or static) library
- `LIBPDFIO_STATIC`: the name of the secondary (static) library
- `LIBS`; libraries for all programs,
- `OPTIM`; common compiler optimization options,
- `prefix`; the installation prefix directory,
- `RANLIB`; the static library indexing command,
- `SHELL`; the sh (POSIX shell) command,
@ -389,12 +393,7 @@ The following variables are defined in the makefile:
The following standard targets are defined in the makefile:
- `all`; creates the static library and unit test program.
- `all-shared`; creates a shared library appropriate for the local system.
- `clean`; removes all target programs libraries, documentation files, and
object files,
- `debug`: creates a clean build of the static library and unit test program
with debug printfs and the clang address sanitizer enabled.
- `install`; installs all distribution files in their corresponding locations.
- `install-shared`; same as `install` but also installs the shared library.
- `macos`; same as `all` but creates a Universal Binary (X64 + ARM64).
- `test`; runs the unit test program, building it as needed.

224
Makefile
View File

@ -1,224 +0,0 @@
#
# Makefile for PDFio.
#
# Copyright © 2021-2023 by Michael R Sweet.
#
# Licensed under Apache License v2.0. See the file "LICENSE" for more
# information.
#
# POSIX makefile
.POSIX:
# Build silently
.SILENT:
# Variables
AR = ar
ARFLAGS = cr
CC = cc
CFLAGS =
CODESIGN_IDENTITY = Developer ID
COMMONFLAGS = -Os -g
#COMMONFLAGS = -O0 -g -fsanitize=address
CPPFLAGS =
DESTDIR = $(DSTROOT)
DSO = cc
DSOFLAGS =
DSONAME =
LDFLAGS =
LIBS = -lm -lz
RANLIB = ranlib
VERSION = 1.1.2
prefix = /usr/local
# Base rules
.SUFFIXES: .c .h .o
.c.o:
echo Compiling $<...
$(CC) $(CFLAGS) $(CPPFLAGS) $(COMMONFLAGS) -c $<
# Files
PUBHEADERS = \
pdfio.h \
pdfio-content.h
PUBOBJS = \
pdfio-aes.o \
pdfio-array.o \
pdfio-common.o \
pdfio-content.o \
pdfio-crypto.o \
pdfio-dict.o \
pdfio-file.o \
pdfio-md5.o \
pdfio-object.o \
pdfio-page.o \
pdfio-rc4.o \
pdfio-sha256.o \
pdfio-stream.o \
pdfio-string.o \
pdfio-token.o \
pdfio-value.o
LIBOBJS = \
$(PUBOBJS) \
ttf.o
OBJS = \
$(LIBOBJS) \
pdfiototext.o \
testpdfio.o
TARGETS = \
$(DSONAME) \
libpdfio.a \
pdfiototext \
testpdfio
# Make everything
all: $(TARGETS)
all-shared:
if test `uname` = Darwin; then \
$(MAKE) DSONAME="libpdfio.1.dylib" -$(MAKEFLAGS) all; \
else \
$(MAKE) COMMONFLAGS="-g -Os -fPIC" DSONAME="libpdfio.so.1" -$(MAKEFLAGS) all; \
fi
debug:
$(MAKE) -$(MAKEFLAGS) COMMONFLAGS="-g -fsanitize=address -DDEBUG=1" clean all
macos:
$(MAKE) -$(MAKEFLAGS) COMMONFLAGS="-Os -mmacosx-version-min=11 -arch x86_64 -arch arm64" clean all
# Clean everything
clean:
rm -f $(TARGETS) $(OBJS)
# Install everything
install: $(TARGETS)
echo Installing header files to $(DESTDIR)$(prefix)/include...
-mkdir -p $(DESTDIR)$(prefix)/include
cp $(PUBHEADERS) $(DESTDIR)$(prefix)/include
echo Installing library files to $(DESTDIR)$(prefix)/lib...
-mkdir -p $(DESTDIR)$(prefix)/lib
cp libpdfio.a $(DESTDIR)$(prefix)/lib
$(RANLIB) $(DESTDIR)$(prefix)/lib/libpdfio.a
if test "x$(DSONAME)" = xlibpdfio.so.1; then \
cp $(DSONAME) $(DESTDIR)$(prefix)/lib; \
ln -sf libpdfio.so.1 $(DESTDIR)$(prefix)/lib/libpdfio.so; \
elif test "x$(DSONAME)" = xlibpdfio.1.dylib; then \
cp $(DSONAME) $(DESTDIR)$(prefix)/lib; \
codesign -s "$(CODESIGN_IDENTITY)" -o runtime --timestamp $(DESTDIR)$(prefix)/lib/libpdfio.1.dylib; \
ln -sf libpdfio.1.dylib $(DESTDIR)$(prefix)/lib/libpdfio.dylib; \
fi
echo Installing pkg-config files to $(DESTDIR)$(prefix)/lib/pkgconfig...
-mkdir -p $(DESTDIR)$(prefix)/lib/pkgconfig
echo 'prefix="$(prefix)"' >$(DESTDIR)$(prefix)/lib/pkgconfig/pdfio.pc
echo 'Version: $(VERSION)' >>$(DESTDIR)$(prefix)/lib/pkgconfig/pdfio.pc
cat pdfio.pc.in >>$(DESTDIR)$(prefix)/lib/pkgconfig/pdfio.pc
echo Installing documentation to $(DESTDIR)$(prefix)/share/doc/pdfio...
-mkdir -p $(DESTDIR)$(prefix)/share/doc/pdfio
cp doc/pdfio.html doc/pdfio-512.png LICENSE NOTICE $(DESTDIR)$(prefix)/share/doc/pdfio
echo Installing man page to $(DESTDIR)$(prefix)/share/man/man3...
-mkdir -p $(DESTDIR)$(prefix)/share/man/man3
cp doc/pdfio.3 $(DESTDIR)$(prefix)/share/man/man3
install-shared:
if test `uname` = Darwin; then \
$(MAKE) DSONAME="libpdfio.1.dylib" -$(MAKEFLAGS) install; \
else \
$(MAKE) DSONAME="libpdfio.so.1" -$(MAKEFLAGS) install; \
fi
# Test everything
test: testpdfio
./testpdfio 2>test.log
valgrind: testpdfio
valgrind --leak-check=full ./testpdfio
# pdfio library
libpdfio.a: $(LIBOBJS)
echo Archiving $@...
$(AR) $(ARFLAGS) $@ $(LIBOBJS)
$(RANLIB) $@
libpdfio.so.1: $(LIBOBJS)
echo Linking $@...
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -shared -o $@ -Wl,-soname,$@ $(LIBOBJS) $(LIBS)
libpdfio.1.dylib: $(LIBOBJS)
echo Linking $@...
$(CC) $(DSOFLAGS) $(COMMONFLAGS) -dynamiclib -o $@ -install_name $(prefix)/lib/$@ -current_version $(VERSION) -compatibility_version 1.0 $(LIBOBJS) $(LIBS)
# pdfio1.def (Windows DLL exports file...)
#
# I'd love to use __declspec(dllexport) but MS puts it before the function
# declaration instead of after like everyone else, and it breaks Codedoc and
# other tools I rely on...
pdfio1.def: $(LIBOBJS) Makefile
echo Generating $@...
echo "LIBRARY pdfio1" >$@
echo "VERSION 1.0" >>$@
echo "EXPORTS" >>$@
nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}' | \
grep -v '^_ttf' | sed -e '1,$$s/^_//' | sort >>$@
# pdfio text extraction (demo, doesn't handle a lot of things yet)
pdfiototext: pdfiototext.o libpdfio.a
echo Linking $@...
$(CC) $(LDFLAGS) $(COMMONFLAGS) -o $@ pdfiototext.o libpdfio.a $(LIBS)
# pdfio test program
testpdfio: testpdfio.o libpdfio.a
echo Linking $@...
$(CC) $(LDFLAGS) $(COMMONFLAGS) -o $@ testpdfio.o libpdfio.a $(LIBS)
# Dependencies
$(OBJS): pdfio.h pdfio-private.h Makefile
pdfio-content.o: pdfio-content.h ttf.h
ttf.o: ttf.h
# Make documentation using Codedoc <https://www.msweet.org/codedoc>
DOCFLAGS = \
--author "Michael R Sweet" \
--copyright "Copyright (c) 2021-2023 by Michael R Sweet" \
--docversion $(VERSION)
.PHONY: doc
doc:
echo Generating documentation...
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" $(PUBHEADERS) $(PUBOBJS:.o=.c) --body doc/pdfio.md --coverimage doc/pdfio-512.png pdfio.xml >doc/pdfio.html
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(VERSION)" --body doc/pdfio.md --coverimage doc/pdfio-epub.png pdfio.xml --epub doc/pdfio.epub
codedoc $(DOCFLAGS) --title "pdf read/write library" --man pdfio --section 3 --body doc/pdfio.md pdfio.xml >doc/pdfio.3
rm -f pdfio.xml
# Fuzz-test the library <>
.PHONY: afl
afl:
$(MAKE) -$(MAKEFLAGS) CC="afl-clang-fast" COMMONFLAGS="-g" clean all
test afl-output || rm -rf afl-output
afl-fuzz -x afl-pdf.dict -i afl-input -o afl-output -V 600 -e pdf -t 5000 ./testpdfio @@
# Analyze code with the Clang static analyzer <https://clang-analyzer.llvm.org>
clang:
clang $(CPPFLAGS) --analyze $(OBJS:.o=.c) 2>clang.log
rm -rf $(OBJS:.o=.plist)
test -s clang.log && (echo "$(GHA_ERROR)Clang detected issues."; echo ""; cat clang.log; exit 1) || exit 0
# Analyze code using Cppcheck <http://cppcheck.sourceforge.net>
cppcheck:
cppcheck $(CPPFLAGS) --template=gcc --addon=cert.py --suppressions-list=.cppcheck $(OBJS:.o=.c) 2>cppcheck.log
test -s cppcheck.log && (echo "$(GHA_ERROR)Cppcheck detected issues."; echo ""; cat cppcheck.log; exit 1) || exit 0

260
Makefile.in Normal file
View File

@ -0,0 +1,260 @@
#
# Makefile for PDFio.
#
# Copyright © 2021-2023 by Michael R Sweet.
#
# Licensed under Apache License v2.0. See the file "LICENSE" for more
# information.
#
# POSIX makefile
.POSIX:
# Build silently
.SILENT:
# Version number...
PDFIO_VERSION = @PDFIO_VERSION@
PDFIO_VERSION_MAJOR = @PDFIO_VERSION_MAJOR@
PDFIO_VERSION_MINOR = @PDFIO_VERSION_MINOR@
# Programs and options...
AR = @AR@
ARFLAGS = @ARFLAGS@
CC = @CC@
CFLAGS = @CFLAGS@ $(CPPFLAGS) $(OPTIM) $(WARNINGS)
CODE_SIGN = @CODE_SIGN@
CODESIGN_IDENTITY = -
CPPFLAGS = @CPPFLAGS@
CSFLAGS = -s "$(CODESIGN_IDENTITY)" @CSFLAGS@ --timestamp
DSOFLAGS = @DSOFLAGS@ $(CFLAGS)
INSTALL = @INSTALL@
LDFLAGS = @LDFLAGS@ $(OPTIM)
LIBS = @LIBS@ -lm
LN = @LN@
OPTIM = @OPTIM@
RANLIB = @RANLIB@
RM = @RM@ -f
RMDIR = @RMDIR@
SHELL = /bin/sh
WARNINGS = @WARNINGS@
# Targets
LIBPDFIO = @LIBPDFIO@
LIBPDFIO_STATIC = @LIBPDFIO_STATIC@
# Directories...
bindir = @bindir@
datadir = @datadir@
datarootdir = @datarootdir@
exec_prefix = @exec_prefix@
includedir = @includedir@
infodir = @infodir@
libdir = @libdir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
mandir = @mandir@
oldincludedir = @oldincludedir@
prefix = @prefix@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
top_srcdir = @top_srcdir@
BUILDROOT = $(DSTROOT)$(RPM_BUILD_ROOT)$(DESTDIR)
# Build commands...
.SUFFIXES: .c .h .o
.c.o:
echo Compiling $<...
$(CC) $(CFLAGS) -c -o $@ $<
# Files
PUBHEADERS = \
pdfio.h \
pdfio-content.h
PUBOBJS = \
pdfio-aes.o \
pdfio-array.o \
pdfio-common.o \
pdfio-content.o \
pdfio-crypto.o \
pdfio-dict.o \
pdfio-file.o \
pdfio-md5.o \
pdfio-object.o \
pdfio-page.o \
pdfio-rc4.o \
pdfio-sha256.o \
pdfio-stream.o \
pdfio-string.o \
pdfio-token.o \
pdfio-value.o
LIBOBJS = \
$(PUBOBJS) \
ttf.o
OBJS = \
$(LIBOBJS) \
pdfiototext.o \
testpdfio.o \
testttf.o
TARGETS = \
$(LIBPDFIO) \
$(LIBPDFIO_STATIC) \
pdfiototext \
testpdfio \
testttf
# Make everything
all: $(TARGETS)
# Clean everything
clean:
rm -f $(TARGETS) $(OBJS)
# Install everything
install: $(TARGETS)
echo Installing header files to $(BUILDROOT)$(includedir)...
$(INSTALL) -d -m 755 $(BUILDROOT)$(includedir)
for file in $(PUBHEADERS); do \
$(INSTALL) -c -m 644 $$file $(BUILDROOT)$(includedir); \
done
echo Installing library files to $(BUILDROOT)$(libdir)...
$(INSTALL) -d -m 755 $(BUILDROOT)$(libdir)
if test "x$(LIBPDFIO_STATIC)" != x; then \
$(INSTALL) -c -m 644 $(LIBPDFIO_STATIC) $(BUILDROOT)$(libdir); \
$(RANLIB) $(BUILDROOT)$(libdir)/$(LIBPDFIO_STATIC); \
fi
if test "x$(LIBPDFIO)" = xlibpdfio.so.1; then \
$(INSTALL) -c -m 755 libpdfio.so.1 $(BUILDROOT)$(libdir); \
ln -sf libpdfio.so.1 $(BUILDROOT)$(libdir)/libpdfio.so; \
elif test "x$(LIBPDFIO)" = xlibpdfio.1.dylib; then \
$(INSTALL) -c -m 755 libpdfio.1.dylib $(BUILDROOT)$(libdir); \
codesign -s "$(CODESIGN_IDENTITY)" -o runtime --timestamp $(BUILDROOT)$(libdir)/libpdfio.1.dylib; \
ln -sf libpdfio.1.dylib $(BUILDROOT)$(libdir)/libpdfio.dylib; \
else \
$(INSTALL) -c -m 644 $(LIBPDFIO) $(BUILDROOT)$(libdir); \
$(RANLIB) $(BUILDROOT)$(libdir)/$(LIBPDFIO); \
fi
echo Installing pkg-config files to $(BUILDROOT)$(libdir)/pkgconfig...
$(INSTALL) -d -m 755 $(BUILDROOT)$(libdir)/pkgconfig
$(INSTALL) -c -m 644 pdfio.pc $(BUILDROOT)$(libdir)/pkgconfig
echo Installing documentation to $(BUILDROOT)$(datadir)/doc/pdfio...
$(INSTALL) -d -m 755 $(BUILDROOT)$(datadir)/doc/pdfio
for file in doc/pdfio.html doc/pdfio-512.png LICENSE NOTICE; do \
$(INSTALL) -c -m 644 $$file $(BUILDROOT)$(datadir)/doc/pdfio; \
done
echo Installing man page to $(BUILDROOT)$(mandir)/man3...
$(INSTALL) -d -m 755 $(BUILDROOT)$(mandir)/man3
$(INSTALL) -c -m 644 doc/pdfio.3 $(BUILDROOT)$(mandir)/man3
# Test everything
test: testpdfio testttf
./testttf 2>test.log
./testpdfio 2>test.log
valgrind: testpdfio
valgrind --leak-check=full ./testpdfio
# pdfio library
libpdfio.a: $(LIBOBJS)
echo Archiving $@...
$(RM) $@
$(AR) $(ARFLAGS) $@ $(LIBOBJS)
$(RANLIB) $@
libpdfio.so.1: $(LIBOBJS)
echo Linking $@...
$(CC) $(DSOFLAGS) -shared -o $@ -Wl,-soname,$@ $(LIBOBJS) $(LIBS)
libpdfio.1.dylib: $(LIBOBJS)
echo Linking $@...
$(CC) $(DSOFLAGS) -dynamiclib -o $@ -install_name $(libdir)/$@ -current_version $(PDFIO_VERSION_MAJOR).$(PDFIO_VERSION_MINOR) -compatibility_version 1.0 $(LIBOBJS) $(LIBS)
# pdfio1.def (Windows DLL exports file...)
#
# I'd love to use __declspec(dllexport) but MS puts it before the function
# declaration instead of after like everyone else, and it breaks Codedoc and
# other tools I rely on...
pdfio1.def: $(LIBOBJS) Makefile
echo Generating $@...
echo "LIBRARY pdfio1" >$@
echo "VERSION $(PDFIO_VERSION_MAJOR).$(PDFIO_VERSION_MINOR)" >>$@
echo "EXPORTS" >>$@
nm $(LIBOBJS) 2>/dev/null | grep "T _" | awk '{print $$3}' | \
grep -v '^_ttf' | sed -e '1,$$s/^_//' | sort >>$@
# pdfio text extraction (demo, doesn't handle a lot of things yet)
pdfiototext: pdfiototext.o libpdfio.a
echo Linking $@...
$(CC) $(LDFLAGS) -o $@ pdfiototext.o libpdfio.a $(LIBS)
# pdfio test program
testpdfio: testpdfio.o libpdfio.a
echo Linking $@...
$(CC) $(LDFLAGS) -o $@ testpdfio.o libpdfio.a $(LIBS)
# TTF test program
testttf: ttf.o testttf.o
echo Linking $@...
$(CC) $(LDFLAGS) -o testttf ttf.o testttf.o $(LIBS)
# Dependencies
$(OBJS): pdfio.h pdfio-private.h Makefile
pdfio-content.o: pdfio-content.h ttf.h
testttf.o: ttf.h
ttf.o: ttf.h
# Make documentation using Codedoc <https://www.msweet.org/codedoc>
DOCFLAGS = \
--author "Michael R Sweet" \
--copyright "Copyright (c) 2021-2024 by Michael R Sweet" \
--docversion $(PDFIO_VERSION)
.PHONY: doc
doc:
echo Generating documentation...
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(PDFIO_VERSION)" $(PUBHEADERS) $(PUBOBJS:.o=.c) --body doc/pdfio.md --coverimage doc/pdfio-512.png pdfio.xml >doc/pdfio.html
codedoc $(DOCFLAGS) --title "PDFio Programming Manual v$(PDFIO_VERSION)" --body doc/pdfio.md --coverimage doc/pdfio-epub.png pdfio.xml --epub doc/pdfio.epub
codedoc $(DOCFLAGS) --title "pdf read/write library" --man pdfio --section 3 --body doc/pdfio.md pdfio.xml >doc/pdfio.3
rm -f pdfio.xml
# Fuzz-test the library <https://lcamtuf.coredump.cx/afl/>
.PHONY: afl
afl:
$(MAKE) -$(MAKEFLAGS) CC="afl-clang-fast" COMMONFLAGS="-g" clean all
test afl-output || rm -rf afl-output
afl-fuzz -x afl-pdf.dict -i afl-input -o afl-output -V 600 -e pdf -t 5000 ./testpdfio @@
# Analyze code with the Clang static analyzer <https://clang-analyzer.llvm.org>
clang:
clang $(CPPFLAGS) --analyze $(OBJS:.o=.c) 2>clang.log
rm -rf $(OBJS:.o=.plist)
test -s clang.log && (echo "$(GHA_ERROR)Clang detected issues."; echo ""; cat clang.log; exit 1) || exit 0
# Analyze code using Cppcheck <http://cppcheck.sourceforge.net>
cppcheck:
cppcheck $(CPPFLAGS) --template=gcc --addon=cert.py --suppressions-list=.cppcheck $(OBJS:.o=.c) 2>cppcheck.log
test -s cppcheck.log && (echo "$(GHA_ERROR)Cppcheck detected issues."; echo ""; cat cppcheck.log; exit 1) || exit 0

2
NOTICE
View File

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

View File

@ -28,7 +28,7 @@ PDFio requires the following to build the software:
- A C99 compiler such as Clang, GCC, or MS Visual C
- A POSIX-compliant `make` program
- ZLIB (<https://www.zlib.net>) 1.0 or higher
- ZLIB (<https://www.zlib.net>) 1.1 or higher
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
@ -36,15 +36,27 @@ IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
Documentation
-------------
See the man page (`pdfio.3`), frequently ask questions (`FAQ.md`), and full HTML
documentation (`pdfio.html`) for information on using PDFio.
See the man page (`pdfio.3`) and full HTML documentation (`pdfio.html`) for
information on using PDFio.
Installing pdfio
Installing PDFio
----------------
PDFio comes with a portable makefile that will work on any POSIX-compliant
system with ZLIB installed. To make it, run:
PDFio uses a configure script on Unix systems to generate a makefile:
./configure
If you want a shared library, run:
./configure --enable-shared
The default installation location is "/usr/local". Pass the `--prefix` option
to make to install it to another location:
./configure --prefix=/some/other/directory
Once configured, run the following to make the library:
make all
@ -54,45 +66,7 @@ To test it, run:
To install it, run:
make install
If you want a shared library, run:
make all-shared
make install-shared
The default installation location is "/usr/local". Pass the `prefix` variable
to make to install it to another location:
make install prefix=/some/other/directory
The makefile installs the pdfio header to "${prefix}/include", the library to
"${prefix}/lib", the `pkg-config` file to "${prefix}/lib/pkgconfig", the man
page to "${prefix}/share/man/man3", and the documentation to
"${prefix}/share/doc/pdfio".
The makefile supports the following variables that can be specified in the make
command or as environment variables:
- `AR`: the library archiver (default "ar")
- `ARFLAGS`: options for the library archiver (default "cr")
- `CC`: the C compiler (default "cc")
- `CFLAGS`: options for the C compiler (default "")
- `CODESIGN_IDENTITY`: the identity to use when code signing the shared library
on macOS (default "Developer ID")
- `COMMONFLAGS`: options for the C compiler and linker (typically architecture
and optimization options, default is "-Os -g")
- `CPPFLAGS`: options for the C preprocessor (default "")
- `DESTDIR` and `DSTROOT`: specifies a root directory when installing
(default is "", specify only one)
- `DSOFLAGS`: options for the C compiler when linking the shared library
(default "")
- `LDFLAGS`: options for the C compiler when linking the test programs
(default "")
- `LIBS`: library options when linking the test programs (default "-lz")
- `RANLIB`: program that generates a table-of-contents in a library
(default "ranlib")
- `prefix`: specifies the installation directory (default "/usr/local")
sudo make install
Visual Studio Project
@ -111,15 +85,11 @@ generates a static library that will be installed under "/usr/local" with:
sudo xcodebuild install
You can reproduce this with the makefile using:
sudo make macos install
Legal Stuff
-----------
PDFio is Copyright © 2021-2023 by Michael R Sweet.
PDFio is Copyright © 2021-2024 by Michael R Sweet.
This software is licensed under the Apache License Version 2.0 with an
(optional) exception to allow linking against GPL2/LGPL2 software. See the

View File

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

1774
config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

1907
config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

5554
configure vendored Executable file

File diff suppressed because it is too large Load Diff

273
configure.ac Normal file
View File

@ -0,0 +1,273 @@
dnl
dnl Configuration script for PDFio
dnl
dnl Copyright © 2023-2024 by Michael R Sweet
dnl
dnl Licensed under Apache License v2.0. See the file "LICENSE" for more
dnl information.
dnl
dnl ***********************************************************************
dnl
dnl Note: Using autoheader or automake on this project will break the PDFio
dnl build system. Use "autoconf -f" to regenerate the configure script if
dnl you make changes to this file.
dnl
dnl ***********************************************************************
dnl We need at least autoconf 2.70 for --runstatedir...
AC_PREREQ([2.70])
dnl Package name and version...
AC_INIT([pdfio], [1.2.0], [https://github.com/michaelrsweet/pdfio/issues], [pdfio], [https://www.msweet.org/pdfio])
PDFIO_VERSION="AC_PACKAGE_VERSION"
PDFIO_VERSION_MAJOR="`echo AC_PACKAGE_VERSION | awk -F. '{print $1}'`"
PDFIO_VERSION_MINOR="`echo AC_PACKAGE_VERSION | awk -F. '{printf("%d\n",$2);}'`"
AC_SUBST([PDFIO_VERSION])
AC_SUBST([PDFIO_VERSION_MAJOR])
AC_SUBST([PDFIO_VERSION_MINOR])
dnl This line is provided to ensure that you don't run the autoheader program
dnl against this project. Doing so is completely unsupported and WILL cause
dnl problems!
AH_TOP([#error "Somebody ran autoheader on this project which is unsupported and WILL cause problems."])
dnl Get the build and host platforms and split the host_os value
AC_CANONICAL_BUILD
AC_CANONICAL_HOST
[host_os_name="$(echo $host_os | sed -e '1,$s/[0-9.]*$//g')"]
[host_os_version="$(echo $host_os | sed -e '1,$s/^[^0-9.]*//g' | awk -F. '{print $1 $2}')"]
# Linux often does not yield an OS version we can use...
AS_IF([test "x$host_os_version" = x], [
host_os_version="0"
])
dnl Compiler options...
CFLAGS="${CFLAGS:=}"
CPPFLAGS="${CPPFLAGS:=}"
DSOFLAGS="${DSOFLAGS:=}"
LDFLAGS="${LDFLAGS:=}"
LIBS="${LIBS:=}"
OPTIM="${OPTIM:=}"
AC_SUBST([DSOFLAGS])
AC_SUBST([OPTIM])
dnl Standard programs...
AC_PROG_CC
AC_PROG_RANLIB
AC_PATH_PROG([AR], [ar])
AC_PATH_PROGS([CODE_SIGN], [codesign true])
AC_PATH_PROG([MKDIR], [mkdir])
AC_PATH_PROG([RM], [rm])
AC_PATH_PROG([RMDIR], [rmdir])
AC_PATH_PROG([LN], [ln])
dnl Figure out the correct "ar" command flags...
AS_IF([test "$ac_cv_prog_ranlib" = ":"], [
ARFLAGS="crs"
], [
ARFLAGS="cr"
])
AC_SUBST([ARFLAGS])
dnl install-sh
AC_MSG_CHECKING([for install-sh script])
INSTALL="$(pwd)/install-sh"
AC_SUBST([INSTALL])
AC_MSG_RESULT([using $INSTALL])
dnl Check for pkg-config, which is used for some other tests later on...
AC_PATH_TOOL([PKGCONFIG], [pkg-config])
PKGCONFIG_CFLAGS="-I\${includedir}"
PKGCONFIG_LIBS="-L\${libdir} -lpdfio"
PKGCONFIG_LIBS_PRIVATE="-lm"
PKGCONFIG_REQUIRES="zlib"
AC_SUBST([PKGCONFIG_CFLAGS])
AC_SUBST([PKGCONFIG_LIBS])
AC_SUBST([PKGCONFIG_LIBS_PRIVATE])
AC_SUBST([PKGCONFIG_REQUIRES])
dnl ZLIB
AC_MSG_CHECKING([for zlib via pkg-config])
AS_IF([$PKGCONFIG --exists zlib], [
AC_MSG_RESULT([yes])
CPPFLAGS="$($PKGCONFIG --cflags zlib) $CPPFLAGS"
LIBS="$($PKGCONFIG --libs zlib) $LIBS"
],[
AC_MSG_RESULT([no])
AC_CHECK_HEADER([zlib.h])
AC_CHECK_LIB([z], [inflateCopy])
AS_IF([test x$ac_cv_header_zlib_h != xyes -o x$ac_cv_lib_z_inflateCopy != xyes], [
AC_MSG_ERROR([Sorry, this software requires zlib 1.1 or higher.])
])
PKGCONFIG_REQUIRES=""
PKGCONFIG_LIBS_PRIVATE="-lz $PKGCONFIG_LIBS_PRIVATE"
])
dnl Library target...
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]))
AS_IF([test x$enable_shared = xyes], [
AS_IF([test "$host_os_name" = darwin], [
LIBPDFIO="libpdfio.1.dylib"
], [
LIBPDFIO="libpdfio.so.1"
])
AS_IF([test x$enable_static != xno], [
LIBPDFIO_STATIC="libpdfio.a"
], [
LIBPDFIO_STATIC=""
])
], [
LIBPDFIO="libpdfio.a"
LIBPDFIO_STATIC=""
PKGCONFIG_LIBS="$PKGCONFIG_LIBS $PKGCONFIG_LIBS_PRIVATE"
PKGCONFIG_LIBS_PRIVATE=""
])
AC_SUBST([LIBPDFIO])
AC_SUBST([LIBPDFIO_STATIC])
dnl Extra compiler options...
AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [turn on debugging, default=no]))
AC_ARG_ENABLE([maintainer], AS_HELP_STRING([--enable-maintainer], [turn on maintainer mode, default=no]))
AC_ARG_ENABLE([sanitizer], AS_HELP_STRING([--enable-sanitizer], [build with AddressSanitizer, default=no]))
AS_IF([test x$enable_debug = xyes], [
OPTIM="$OPTIM -g"
CSFLAGS=""
], [
OPTIM="$OPTIM -g -Os"
CSFLAGS="-o runtime"
])
AC_SUBST([CSFLAGS])
WARNINGS=""
AC_SUBST([WARNINGS])
AS_IF([test -n "$GCC"], [
AS_IF([test x$enable_sanitizer = xyes], [
# Use -fsanitize=address with debugging...
OPTIM="$OPTIM -fsanitize=address"
], [
# Otherwise use the Fortify enhancements to catch any unbounded
# string operations...
CPPFLAGS="$CPPFLAGS -D_FORTIFY_SOURCE=2"
])
dnl Show all standard warnings + unused variables when compiling...
WARNINGS="-Wall -Wunused"
dnl Drop some not-useful/unreliable warnings...
for warning in char-subscripts format-truncation format-y2k switch unused-result; do
AC_MSG_CHECKING([whether compiler supports -Wno-$warning])
OLDCFLAGS="$CFLAGS"
CFLAGS="$CFLAGS -Wno-$warning -Werror"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [
AC_MSG_RESULT(yes)
WARNINGS="$WARNINGS -Wno-$warning"
], [
AC_MSG_RESULT(no)
])
CFLAGS="$OLDCFLAGS"
done
dnl Maintainer mode enables -Werror...
AS_IF([test x$enable_maintainer = xyes], [
WARNINGS="$WARNINGS -Werror -Wno-error=deprecated"
])
dnl See if PIE options are supported...
AC_MSG_CHECKING(whether compiler supports -fPIE)
OLDCFLAGS="$CFLAGS"
AS_CASE(["$host_os_name"],
[darwin*], [
CFLAGS="$CFLAGS -fPIC -fPIE -Wl,-pie"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[
OLDCFLAGS="-fPIC $OLDCFLAGS"
LDFLAGS="-fPIE -Wl,-pie $LDFLAGS"
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
])
], [*], [
CFLAGS="$CFLAGS -fPIC -fPIE -pie"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[
OLDCFLAGS="-fPIC $OLDCFLAGS"
LDFLAGS="-fPIE -pie $LDFLAGS"
AC_MSG_RESULT(yes)
],[
AC_MSG_RESULT(no)
])
])
CFLAGS="$OLDCFLAGS"
dnl OS-specific compiler options...
AC_MSG_CHECKING([for OS-specific compiler options])
AS_CASE(["$host_os_name"], [linux*], [
# Make sure we get the full set of 64-bit Linux APIs from the headers...
CPPFLAGS="$CPPFLAGS -D__USE_MISC -D_GNU_SOURCE -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64"
# Mark read-only sections as relocatable to random addresses...
LDFLAGS="$LDFLAGS -Wl,-z,relro,-z,now"
AC_MSG_RESULT([-D__USE_MISC -D_GNU_SOURCE -D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64 -Wl,-z,relro,-z,now])
], [darwin*], [
# When not building for debug, target macOS 11 or later, "universal"
# binaries when possible...
AS_IF([echo "$CPPFLAGS $CFLAGS $LDFLAGS $OPTIM" | grep -q "\\-arch "], [
# Don't add architecture/min-version flags if they are already present
AC_MSG_RESULT([none])
], [echo "$CPPFLAGS $CFLAGS $LDFLAGS $OPTIM" | grep -q "\\-mmacosx-version-"], [
# Don't add architecture/min-version flags if they are already present
AC_MSG_RESULT([none])
], [test "$host_os_version" -ge 200 -a x$enable_debug != xyes], [
# macOS 11.0 and higher support the Apple Silicon (arm64) CPUs
OPTIM="$OPTIM -mmacosx-version-min=11.0 -arch x86_64 -arch arm64"
AC_MSG_RESULT([-mmacosx-version-min=11.0 -arch x86_64 -arch arm64])
], [
# Don't add architecture/min-version flags if debug enabled
AC_MSG_RESULT([none])
])
], [*], [
AC_MSG_RESULT([none])
])
])
dnl Extra linker options...
AC_ARG_WITH([dsoflags], AS_HELP_STRING([--with-dsoflags=...], [Specify additional DSOFLAGS]), [
DSOFLAGS="$withval $DSOFLAGS"
])
AC_ARG_WITH([ldflags], AS_HELP_STRING([--with-ldflags=...], [Specify additional LDFLAGS]), [
LDFLAGS="$withval $LDFLAGS"
])
dnl Generate the Makefile and pkg-config file...
AC_CONFIG_FILES([Makefile pdfio.pc])
AC_OUTPUT

View File

@ -1,4 +1,4 @@
.TH pdfio 3 "pdf read/write library" "2023-10-06" "pdf read/write library"
.TH pdfio 3 "pdf read/write library" "2024-01-24" "pdf read/write library"
.SH NAME
pdfio \- pdf read/write library
.SH Introduction
@ -34,7 +34,7 @@ PDFio is
.I not
concerned with rendering or viewing a PDF file, although a PDF RIP or viewer could be written using it.
.PP
PDFio is Copyright \[co] 2021\-2023 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
PDFio is Copyright \[co] 2021\-2024 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
.SS Requirements
.PP
PDFio requires the following to build the software:
@ -46,6 +46,10 @@ A C99 compiler such as Clang, GCC, or MS Visual C
.PP
A POSIX\-compliant make program
.IP \(bu 5
.PP
A POSIX\-compliant sh program
.IP \(bu 5
.PP
ZLIB (https://www.zlib.net) 1.0 or higher
@ -55,10 +59,11 @@ ZLIB (https://www.zlib.net) 1.0 or higher
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
.SS Installing pdfio
.PP
PDFio comes with a portable makefile that will work on any POSIX\-compliant system with ZLIB installed. To make it, run:
PDFio comes with a configure script that creates a portable makefile that will work on any POSIX\-compliant system with ZLIB installed. To make it, run:
.nf
make all
\./configure
make
.fi
.PP
To test it, run:
@ -70,78 +75,28 @@ To test it, run:
To install it, run:
.nf
make install
sudo make install
.fi
.PP
If you want a shared library, run:
.nf
make all\-shared
make install\-shared
\./configure \-\-enable\-shared
make
sudo make install
.fi
.PP
The default installation location is "/usr/local". Pass the prefix variable to make to install it to another location:
The default installation location is "/usr/local". Pass the \-\-prefix option to make to install it to another location:
.nf
make install prefix=/some/other/directory
\./configure \-\-prefix=/some/other/directory
.fi
.PP
The makefile installs the pdfio header to "${prefix}/include", the library to "${prefix}/lib", the pkg\-config file to "${prefix}/lib/pkgconfig", the man page to "${prefix}/share/man/man3", and the documentation to "${prefix}/share/doc/pdfio".
.PP
The makefile supports the following variables that can be specified in the make command or as environment variables:
.IP \(bu 5
.PP
AR: the library archiver (default "ar")
.IP \(bu 5
.PP
ARFLAGS: options for the library archiver (default "cr")
.IP \(bu 5
.PP
CC: the C compiler (default "cc")
.IP \(bu 5
.PP
CFLAGS: options for the C compiler (default "")
.IP \(bu 5
.PP
CODESIGN_IDENTITY: the identity to use when code signing the shared library on macOS (default "Developer ID")
.IP \(bu 5
.PP
COMMONFLAGS: options for the C compiler and linker (typically architecture and optimization options, default is "\-Os \-g")
.IP \(bu 5
.PP
CPPFLAGS: options for the C preprocessor (default "")
.IP \(bu 5
.PP
DESTDIR and DSTROOT: specifies a root directory when installing (default is "", specify only one)
.IP \(bu 5
.PP
DSOFLAGS: options for the C compiler when linking the shared library (default "")
.IP \(bu 5
.PP
LDFLAGS: options for the C compiler when linking the test programs (default "")
.IP \(bu 5
.PP
LIBS: library options when linking the test programs (default "\-lz")
.IP \(bu 5
.PP
RANLIB: program that generates a table\-of\-contents in a library (default "ranlib")
.IP \(bu 5
.PP
prefix: specifies the installation directory (default "/usr/local")
Other configure options can be found using the \-\-help option:
.nf
\./configure \-\-help
.fi
.SS Visual Studio Project
.PP
The Visual Studio solution ("pdfio.sln") is provided for Windows developers and generates both a static library and DLL.
@ -152,12 +107,6 @@ There is also an Xcode project ("pdfio.xcodeproj") you can use on macOS which ge
sudo xcodebuild install
.fi
.PP
You can reproduce this with the makefile using:
.nf
sudo make macos install
.fi
.SS Detecting PDFio
.PP
PDFio can be detected using the pkg\-config command, for example:
@ -218,10 +167,25 @@ pdfio_stream_t: An object stream
You open an existing PDF file using the pdfioFileOpen function:
.nf
pdfio_file_t *pdf = pdfioFileOpen("myinputfile.pdf", error_cb, error_data);
pdfio_file_t *pdf = pdfioFileOpen("myinputfile.pdf", password_cb, password_data,
error_cb, error_data);
.fi
.PP
where the three arguments to the function are the filename ("myinputfile.pdf"), an optional error callback function (error_cb), and an optional pointer value for the error callback function (error_data). The error callback is called for both errors and warnings and accepts the pdfio_file_t pointer, a message string, and the callback pointer value, for example:
where the five arguments to the function are the filename ("myinputfile.pdf"), an optional password callback function (password_cb) and data pointer value (password_data), and an optional error callback function (error_cb) and data pointer value (error_data). The password callback is called for encrypted PDF files that are not using the default password, for example:
.nf
const char *
password_cb(void *data, const char *filename)
{
(void)data; // This callback doesn't use the data pointer
(void)filename; // This callback doesn't use the filename
// Return a password string for the file...
return ("Password42");
}
.fi
.PP
The error callback is called for both errors and warnings and accepts the pdfio_file_t pointer, a message string, and the callback pointer value, for example:
.nf
bool
@ -400,7 +364,7 @@ The PDF content helper functions provide additional functions for writing specif
When you are done writing the stream, call pdfioStreamCLose to close both the stream and the object.
.SS PDF Content Helper Functions
.PP
PDFio includes many helper functions for embedding or writing specific kinds of content to a PDF file. These functions can be roughly grouped into ??? categories:
PDFio includes many helper functions for embedding or writing specific kinds of content to a PDF file. These functions can be roughly grouped into five categories:
.IP \(bu 5
.PP
Color Space Functions
@ -552,6 +516,7 @@ will embed an OpenSans Regular TrueType font using the Windows CP1252 subset of
will embed the NotoSansJP Regular OpenType font with full support for Unicode.
.PP
Note: Not all fonts support Unicode.
.PP
Image Object Functions
.PP
@ -799,7 +764,15 @@ pdfioContentTextMoveTo moves within the current line in a text block
.IP \(bu 5
.PP
pdfioContentTextNextLine moves to the beginning of the next line in a text block
pdfioContentTextNewLine moves to the beginning of the next line in a text block
.IP \(bu 5
.PP
pdfioContentTextNewLineShow moves to the beginning of the next line in a text block and shows literal text with optional word and character spacing
.IP \(bu 5
.PP
pdfioContentTextNewLineShowf moves to the beginning of the next line in a text block and shows formatted text with optional word and character spacing
.IP \(bu 5
.PP
@ -839,7 +812,7 @@ PDFIO_ENCRYPTION_AES_128
.TP 5
PDFIO_ENCRYPTION_AES_256
.br
256-bit AES encryption (PDF 2.0)
256-bit AES encryption (PDF 2.0)
.TP 5
PDFIO_ENCRYPTION_NONE
.br
@ -1774,6 +1747,20 @@ bool pdfioContentTextEnd (
pdfio_stream_t *st
);
.fi
.SS pdfioContentTextMeasure
Measure a text string and return its width.
.PP
.nf
double pdfioContentTextMeasure (
pdfio_obj_t *font,
const char *s,
double size
);
.fi
.PP
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
any control characters (such as newlines) are ignored.
.SS pdfioContentTextMoveLine
Move to the next line and offset.
.PP
@ -1794,14 +1781,49 @@ bool pdfioContentTextMoveTo (
double ty
);
.fi
.SS pdfioContentTextNextLine
.SS pdfioContentTextNewLine
Move to the next line.
.PP
.nf
bool pdfioContentTextNextLine (
bool pdfioContentTextNewLine (
pdfio_stream_t *st
);
.fi
.SS pdfioContentTextNewLineShow
Move to the next line and show text.
.PP
.nf
bool pdfioContentTextNewLineShow (
pdfio_stream_t *st,
double ws,
double cs,
bool unicode,
const char *s
);
.fi
.PP
This function moves to the next line and then shows some text with optional
word and character spacing in a PDF content stream. The "unicode" argument
specifies that the current font maps to full Unicode. The "s" argument
specifies a UTF-8 encoded string.
.SS pdfioContentTextNewLineShowf
Show formatted text.
.PP
.nf
bool pdfioContentTextNewLineShowf (
pdfio_stream_t *st,
double ws,
double cs,
bool unicode,
const char *format,
...
);
.fi
.PP
This function moves to the next line and shows some formatted text with
optional word and character spacing in a PDF content stream. The "unicode"
argument specifies that the current font maps to full Unicode. The "format"
argument specifies a UTF-8 encoded \fBprintf\fR-style format string.
.SS pdfioContentTextShow
Show text.
.PP
@ -2276,6 +2298,18 @@ Note: Currently PNG support is limited to grayscale, RGB, or indexed files
without interlacing or alpha. Transparency (masking) based on color/index
.IP 5
is supported.
.SS pdfioFileCreateNumberObj
Create a new object in a PDF file containing a number.
.PP
.nf
pdfio_obj_t * pdfioFileCreateNumberObj (
pdfio_file_t *pdf,
double number
);
.fi
.PP
This function creates a new object with a number value in a PDF file.
You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateObj
Create a new object in a PDF file.
.PP
@ -2340,6 +2374,18 @@ pdfio_stream_t * pdfioFileCreatePage (
pdfio_dict_t *dict
);
.fi
.SS pdfioFileCreateStringObj
Create a new object in a PDF file containing a string.
.PP
.nf
pdfio_obj_t * pdfioFileCreateStringObj (
pdfio_file_t *pdf,
const char *string
);
.fi
.PP
This function creates a new object with a string value in a PDF file.
You must call \fIpdfioObjClose\fR to write the object to the file.
.SS pdfioFileCreateTemporary
.PP
@ -3036,15 +3082,9 @@ PDF value types
.nf
typedef enum pdfio_valtype_e pdfio_valtype_t;
.fi
.SS state_t[4][4]
4x4 AES state table
.PP
.nf
typedef uint8_t state_t[4][4];
.fi
.SH AUTHOR
.PP
Michael R Sweet
.SH COPYRIGHT
.PP
Copyright (c) 2021-2023 by Michael R Sweet
Copyright (c) 2021-2024 by Michael R Sweet

View File

@ -1,13 +1,13 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<title>PDFio Programming Manual v1.1.2</title>
<title>PDFio Programming Manual v1.2.0</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta name="generator" content="codedoc v3.7">
<meta name="author" content="Michael R Sweet">
<meta name="language" content="en-US">
<meta name="copyright" content="Copyright © 2021-2023 by Michael R Sweet">
<meta name="version" content="1.1.2">
<meta name="copyright" content="Copyright © 2021-2024 by Michael R Sweet">
<meta name="version" content="1.2.0">
<style type="text/css"><!--
body {
background: white;
@ -245,9 +245,9 @@ span.string {
<body>
<div class="header">
<p><img class="title" src="pdfio-512.png"></p>
<h1 class="title">PDFio Programming Manual v1.1.2</h1>
<h1 class="title">PDFio Programming Manual v1.2.0</h1>
<p>Michael R Sweet</p>
<p>Copyright © 2021-2023 by Michael R Sweet</p>
<p>Copyright © 2021-2024 by Michael R Sweet</p>
</div>
<div class="contents">
<h2 class="title">Contents</h2>
@ -342,9 +342,12 @@ span.string {
<li><a href="#pdfioContentStroke">pdfioContentStroke</a></li>
<li><a href="#pdfioContentTextBegin">pdfioContentTextBegin</a></li>
<li><a href="#pdfioContentTextEnd">pdfioContentTextEnd</a></li>
<li><a href="#pdfioContentTextMeasure">pdfioContentTextMeasure</a></li>
<li><a href="#pdfioContentTextMoveLine">pdfioContentTextMoveLine</a></li>
<li><a href="#pdfioContentTextMoveTo">pdfioContentTextMoveTo</a></li>
<li><a href="#pdfioContentTextNextLine">pdfioContentTextNextLine</a></li>
<li><a href="#pdfioContentTextNewLine">pdfioContentTextNewLine</a></li>
<li><a href="#pdfioContentTextNewLineShow">pdfioContentTextNewLineShow</a></li>
<li><a href="#pdfioContentTextNewLineShowf">pdfioContentTextNewLineShowf</a></li>
<li><a href="#pdfioContentTextShow">pdfioContentTextShow</a></li>
<li><a href="#pdfioContentTextShowJustified">pdfioContentTextShowJustified</a></li>
<li><a href="#pdfioContentTextShowf">pdfioContentTextShowf</a></li>
@ -382,9 +385,11 @@ span.string {
<li><a href="#pdfioFileCreateICCObjFromFile">pdfioFileCreateICCObjFromFile</a></li>
<li><a href="#pdfioFileCreateImageObjFromData">pdfioFileCreateImageObjFromData</a></li>
<li><a href="#pdfioFileCreateImageObjFromFile">pdfioFileCreateImageObjFromFile</a></li>
<li><a href="#pdfioFileCreateNumberObj">pdfioFileCreateNumberObj</a></li>
<li><a href="#pdfioFileCreateObj">pdfioFileCreateObj</a></li>
<li><a href="#pdfioFileCreateOutput">pdfioFileCreateOutput</a></li>
<li><a href="#pdfioFileCreatePage">pdfioFileCreatePage</a></li>
<li><a href="#pdfioFileCreateStringObj">pdfioFileCreateStringObj</a></li>
<li><a href="#pdfioFileCreateTemporary">pdfioFileCreateTemporary</a></li>
<li><a href="#pdfioFileFindObj">pdfioFileFindObj</a></li>
<li><a href="#pdfioFileGetAuthor">pdfioFileGetAuthor</a></li>
@ -462,7 +467,6 @@ span.string {
<li><a href="#pdfio_stream_t">pdfio_stream_t</a></li>
<li><a href="#pdfio_textrendering_t">pdfio_textrendering_t</a></li>
<li><a href="#pdfio_valtype_t">pdfio_valtype_t</a></li>
<li><a href="#state_t[4][4]">state_t[4][4]</a></li>
</ul></li>
<li><a href="#STRUCTURES">Structures</a><ul class="subcontents">
<li><a href="#pdfio_rect_s">pdfio_rect_s</a></li>
@ -497,7 +501,7 @@ span.string {
</li>
</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 Copyright © 2021-2023 by Michael R Sweet and is licensed under the Apache License Version 2.0 with an (optional) exception to allow linking against GPL2/LGPL2 software. See the files &quot;LICENSE&quot; and &quot;NOTICE&quot; for more information.</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>
<h3 class="title" id="requirements">Requirements</h3>
<p>PDFio requires the following to build the software:</p>
<ul>
@ -505,66 +509,40 @@ span.string {
</li>
<li><p>A POSIX-compliant <code>make</code> program</p>
</li>
<li><p>A POSIX-compliant <code>sh</code> program</p>
</li>
<li><p>ZLIB (<a href="https://www.zlib.net">https://www.zlib.net</a>) 1.0 or higher</p>
</li>
</ul>
<p>IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.</p>
<h3 class="title" id="installing-pdfio">Installing pdfio</h3>
<p>PDFio comes with a portable makefile that will work on any POSIX-compliant system with ZLIB installed. To make it, run:</p>
<pre><code>make all
<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>
<pre><code>./configure
make
</code></pre>
<p>To test it, run:</p>
<pre><code>make test
</code></pre>
<p>To install it, run:</p>
<pre><code>make install
<pre><code>sudo make install
</code></pre>
<p>If you want a shared library, run:</p>
<pre><code>make all-shared
make install-shared
<pre><code>./configure --enable-shared
make
sudo make install
</code></pre>
<p>The default installation location is &quot;/usr/local&quot;. Pass the <code>prefix</code> variable to make to install it to another location:</p>
<pre><code>make install prefix=/some/other/directory
<p>The default installation location is &quot;/usr/local&quot;. Pass the <code>--prefix</code> option to make to install it to another location:</p>
<pre><code>./configure --prefix=/some/other/directory
</code></pre>
<p>Other configure options can be found using the <code>--help</code> option:</p>
<pre><code>./configure --help
</code></pre>
<p>The makefile installs the pdfio header to &quot;${prefix}/include&quot;, the library to &quot;${prefix}/lib&quot;, the <code>pkg-config</code> file to &quot;${prefix}/lib/pkgconfig&quot;, the man page to &quot;${prefix}/share/man/man3&quot;, and the documentation to &quot;${prefix}/share/doc/pdfio&quot;.</p>
<p>The makefile supports the following variables that can be specified in the make command or as environment variables:</p>
<ul>
<li><p><code>AR</code>: the library archiver (default &quot;ar&quot;)</p>
</li>
<li><p><code>ARFLAGS</code>: options for the library archiver (default &quot;cr&quot;)</p>
</li>
<li><p><code>CC</code>: the C compiler (default &quot;cc&quot;)</p>
</li>
<li><p><code>CFLAGS</code>: options for the C compiler (default &quot;&quot;)</p>
</li>
<li><p><code>CODESIGN_IDENTITY</code>: the identity to use when code signing the shared library on macOS (default &quot;Developer ID&quot;)</p>
</li>
<li><p><code>COMMONFLAGS</code>: options for the C compiler and linker (typically architecture and optimization options, default is &quot;-Os -g&quot;)</p>
</li>
<li><p><code>CPPFLAGS</code>: options for the C preprocessor (default &quot;&quot;)</p>
</li>
<li><p><code>DESTDIR</code> and <code>DSTROOT</code>: specifies a root directory when installing (default is &quot;&quot;, specify only one)</p>
</li>
<li><p><code>DSOFLAGS</code>: options for the C compiler when linking the shared library (default &quot;&quot;)</p>
</li>
<li><p><code>LDFLAGS</code>: options for the C compiler when linking the test programs (default &quot;&quot;)</p>
</li>
<li><p><code>LIBS</code>: library options when linking the test programs (default &quot;-lz&quot;)</p>
</li>
<li><p><code>RANLIB</code>: program that generates a table-of-contents in a library (default &quot;ranlib&quot;)</p>
</li>
<li><p><code>prefix</code>: specifies the installation directory (default &quot;/usr/local&quot;)</p>
</li>
</ul>
<h3 class="title" id="visual-studio-project">Visual Studio Project</h3>
<p>The Visual Studio solution (&quot;pdfio.sln&quot;) is provided for Windows developers and generates both a static library and DLL.</p>
<h3 class="title" id="xcode-project">Xcode Project</h3>
<p>There is also an Xcode project (&quot;pdfio.xcodeproj&quot;) you can use on macOS which generates a static library that will be installed under &quot;/usr/local&quot; with:</p>
<pre><code>sudo xcodebuild install
</code></pre>
<p>You can reproduce this with the makefile using:</p>
<pre><code>sudo make macos install
</code></pre>
<h3 class="title" id="detecting-pdfio">Detecting PDFio</h3>
<p>PDFio can be detected using the <code>pkg-config</code> command, for example:</p>
<pre><code>if pkg-config --exists pdfio; then
@ -599,9 +577,21 @@ LIBS += `pkg-config --libs pdfio`
</ul>
<h3 class="title" id="reading-pdf-files">Reading PDF Files</h3>
<p>You open an existing PDF file using the <a href="#pdfioFileOpen"><code>pdfioFileOpen</code></a> function:</p>
<pre><code class="language-c">pdfio_file_t *pdf = pdfioFileOpen(<span class="string">&quot;myinputfile.pdf&quot;</span>, error_cb, error_data);
<pre><code class="language-c">pdfio_file_t *pdf = pdfioFileOpen(<span class="string">&quot;myinputfile.pdf&quot;</span>, password_cb, password_data,
error_cb, error_data);
</code></pre>
<p>where the three arguments to the function are the filename (&quot;myinputfile.pdf&quot;), an optional error callback function (<code>error_cb</code>), and an optional pointer value for the error callback function (<code>error_data</code>). 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>where the five arguments to the function are the filename (&quot;myinputfile.pdf&quot;), an optional password callback function (<code>password_cb</code>) and data pointer value (<code>password_data</code>), and an optional error callback function (<code>error_cb</code>) and data pointer value (<code>error_data</code>). The password callback is called for encrypted PDF files that are not using the default password, for example:</p>
<pre><code class="language-c"><span class="reserved">const</span> <span class="reserved">char</span> *
password_cb(<span class="reserved">void</span> *data, <span class="reserved">const</span> <span class="reserved">char</span> *filename)
{
(<span class="reserved">void</span>)data; <span class="comment">// This callback doesn't use the data pointer</span>
(<span class="reserved">void</span>)filename; <span class="comment">// This callback doesn't use the filename</span>
<span class="comment">// Return a password string for the file...</span>
<span class="reserved">return</span> (<span class="string">&quot;Password42&quot;</span>);
}
</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>
<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)
{
@ -714,7 +704,7 @@ pdfio_stream_t *st = pdfioFileCreatePage(pdf, dict);
<p>The <a href="#pdf-content-helper-functions">PDF content helper functions</a> provide additional functions for writing specific PDF page stream commands.</p>
<p>When you are done writing the stream, call <a href="#pdfioStreamCLose"><code>pdfioStreamCLose</code></a> to close both the stream and the object.</p>
<h3 class="title" id="pdf-content-helper-functions">PDF Content Helper Functions</h3>
<p>PDFio includes many helper functions for embedding or writing specific kinds of content to a PDF file. These functions can be roughly grouped into ??? categories:</p>
<p>PDFio includes many helper functions for embedding or writing specific kinds of content to a PDF file. These functions can be roughly grouped into five categories:</p>
<ul>
<li><p><a href="#color-space-functions">Color Space Functions</a></p>
</li>
@ -800,7 +790,9 @@ pdfio_obj_t *arial = pdfioFileCreateFontObjFromFile(pdf, <span class="string">&q
pdfio_obj_t *arial = pdfioFileCreateFontObjFromFile(pdf, <span class="string">&quot;NotoSansJP-Regular.otf&quot;</span>, <span class="reserved">true</span>);
</code></pre>
<p>will embed the NotoSansJP Regular OpenType font with full support for Unicode.</p>
<blockquote>
<p>Note: Not all fonts support Unicode.</p>
</blockquote>
<h4 id="image-object-functions">Image Object Functions</h4>
<p>PDF supports images with many different color spaces and bit depths with optional transparency. PDFio provides two helper functions for creating image objects that can be referenced in page streams. The first function is <a href="#pdfioFileCreateImageObjFromData"><code>pdfioFileCreateImageObjFromData</code></a> which creates an image object from data in memory, for example:</p>
<pre><code class="language-c">pdfio_file_t *pdf = pdfioFileCreate(...);
@ -931,7 +923,11 @@ pdfio_obj_t *img = pdfioFileCreateImageObjFromFile(pdf, <span class="string">&qu
</li>
<li><p><a href="#pdfioContentTextMoveTo"><code>pdfioContentTextMoveTo</code></a> moves within the current line in a text block</p>
</li>
<li><p><a href="#pdfioContentTextNextLine"><code>pdfioContentTextNextLine</code></a> moves to the beginning of the next line in a text block</p>
<li><p><a href="#pdfioContentTextNewLine"><code>pdfioContentTextNewLine</code></a> moves to the beginning of the next line in a text block</p>
</li>
<li><p><a href="#pdfioContentTextNewLineShow"><code>pdfioContentTextNewLineShow</code></a> moves to the beginning of the next line in a text block and shows literal text with optional word and character spacing</p>
</li>
<li><p><a href="#pdfioContentTextNewLineShowf"><code>pdfioContentTextNewLineShowf</code></a> moves to the beginning of the next line in a text block and shows formatted text with optional word and character spacing</p>
</li>
<li><p><a href="#pdfioContentTextShow"><code>pdfioContentTextShow</code></a> draws a literal string in a text block</p>
</li>
@ -2005,6 +2001,25 @@ bool pdfioContentTextEnd(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st);</p>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h3 class="function"><a id="pdfioContentTextMeasure">pdfioContentTextMeasure</a></h3>
<p class="description">Measure a text string and return its width.</p>
<p class="code">
double pdfioContentTextMeasure(<a href="#pdfio_obj_t">pdfio_obj_t</a> *font, const char *s, double size);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>font</th>
<td class="description">Font object created by <a href="#pdfioFileCreateFontObjFromFile"><code>pdfioFileCreateFontObjFromFile</code></a></td></tr>
<tr><th>s</th>
<td class="description">UTF-8 string</td></tr>
<tr><th>size</th>
<td class="description">Font size/height</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Width</p>
<h4 class="discussion">Discussion</h4>
<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
any control characters (such as newlines) are ignored.</p>
<h3 class="function"><a id="pdfioContentTextMoveLine">pdfioContentTextMoveLine</a></h3>
<p class="description">Move to the next line and offset.</p>
<p class="code">
@ -2035,10 +2050,10 @@ bool pdfioContentTextMoveTo(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, do
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h3 class="function"><a id="pdfioContentTextNextLine">pdfioContentTextNextLine</a></h3>
<h3 class="function"><a id="pdfioContentTextNewLine">pdfioContentTextNewLine</a></h3>
<p class="description">Move to the next line.</p>
<p class="code">
bool pdfioContentTextNextLine(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st);</p>
bool pdfioContentTextNewLine(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>st</th>
@ -2046,6 +2061,56 @@ bool pdfioContentTextNextLine(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st);
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h3 class="function"><a id="pdfioContentTextNewLineShow">pdfioContentTextNewLineShow</a></h3>
<p class="description">Move to the next line and show text.</p>
<p class="code">
bool pdfioContentTextNewLineShow(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, double ws, double cs, bool unicode, const char *s);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>st</th>
<td class="description">Stream</td></tr>
<tr><th>ws</th>
<td class="description">Word spacing or <code>0.0</code> for none</td></tr>
<tr><th>cs</th>
<td class="description">Character spacing or <code>0.0</code> for none</td></tr>
<tr><th>unicode</th>
<td class="description">Unicode text?</td></tr>
<tr><th>s</th>
<td class="description">String to show</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h4 class="discussion">Discussion</h4>
<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
specifies that the current font maps to full Unicode. The &quot;s&quot; argument
specifies a UTF-8 encoded string.</p>
<h3 class="function"><a id="pdfioContentTextNewLineShowf">pdfioContentTextNewLineShowf</a></h3>
<p class="description">Show formatted text.</p>
<p class="code">
bool pdfioContentTextNewLineShowf(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, double ws, double cs, bool unicode, const char *format, ...);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>st</th>
<td class="description">Stream</td></tr>
<tr><th>ws</th>
<td class="description">Word spacing or <code>0.0</code> for none</td></tr>
<tr><th>cs</th>
<td class="description">Character spacing or <code>0.0</code> for none</td></tr>
<tr><th>unicode</th>
<td class="description">Unicode text?</td></tr>
<tr><th>format</th>
<td class="description"><code>printf</code>-style format string</td></tr>
<tr><th>...</th>
<td class="description">Additional arguments as needed</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description"><code>true</code> on success, <code>false</code> on failure</p>
<h4 class="discussion">Discussion</h4>
<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;
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>
<h3 class="function"><a id="pdfioContentTextShow">pdfioContentTextShow</a></h3>
<p class="description">Show text.</p>
<p class="code">
@ -2105,9 +2170,9 @@ bool pdfioContentTextShowf(<a href="#pdfio_stream_t">pdfio_stream_t</a> *st, boo
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Show formatted text.</p>
<p class="discussion">This function shows some text 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 a UTF-8 encoded <code>printf</code>-style format string.</p>
<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.
The &quot;format&quot; argument specifies a UTF-8 encoded <code>printf</code>-style format string.</p>
<h3 class="function"><a id="pdfioDictCopy">pdfioDictCopy</a></h3>
<p class="description">Copy a dictionary to a PDF file.</p>
<p class="code">
@ -2699,6 +2764,22 @@ image on the page.<br>
Note: Currently PNG support is limited to grayscale, RGB, or indexed files
without interlacing or alpha. Transparency (masking) based on color/index
is supported.</blockquote>
<h3 class="function"><a id="pdfioFileCreateNumberObj">pdfioFileCreateNumberObj</a></h3>
<p class="description">Create a new object in a PDF file containing a number.</p>
<p class="code">
<a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateNumberObj(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, double number);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
<tr><th>number</th>
<td class="description">Number value</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">New object</p>
<h4 class="discussion">Discussion</h4>
<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>
<h3 class="function"><a id="pdfioFileCreateObj">pdfioFileCreateObj</a></h3>
<p class="description">Create a new object in a PDF file.</p>
<p class="code">
@ -2777,6 +2858,22 @@ stored as indirect object references.</blockquote>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">Contents stream</p>
<h3 class="function"><a id="pdfioFileCreateStringObj">pdfioFileCreateStringObj</a></h3>
<p class="description">Create a new object in a PDF file containing a string.</p>
<p class="code">
<a href="#pdfio_obj_t">pdfio_obj_t</a> *pdfioFileCreateStringObj(<a href="#pdfio_file_t">pdfio_file_t</a> *pdf, const char *string);</p>
<h4 class="parameters">Parameters</h4>
<table class="list"><tbody>
<tr><th>pdf</th>
<td class="description">PDF file</td></tr>
<tr><th>string</th>
<td class="description">String</td></tr>
</tbody></table>
<h4 class="returnvalue">Return Value</h4>
<p class="description">New object</p>
<h4 class="discussion">Discussion</h4>
<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>
<h3 class="function"><a id="pdfioFileCreateTemporary">pdfioFileCreateTemporary</a></h3>
<p class="description"></p>
<p class="code">
@ -3652,11 +3749,6 @@ typedef enum <a href="#pdfio_textrendering_e">pdfio_textrendering_e</a> pdfio_te
<p class="code">
typedef enum <a href="#pdfio_valtype_e">pdfio_valtype_e</a> pdfio_valtype_t;
</p>
<h3 class="typedef"><a id="state_t[4][4]">state_t[4][4]</a></h3>
<p class="description">4x4 AES state table</p>
<p class="code">
typedef uint8_t state_t[4][4];
</p>
<h2 class="title"><a id="STRUCTURES">Structures</a></h2>
<h3 class="struct"><a id="pdfio_rect_s">pdfio_rect_s</a></h3>
<p class="description">PDF rectangle</p>
@ -3691,7 +3783,6 @@ typedef uint8_t state_t[4][4];
<h4 class="constants">Constants</h4>
<table class="list"><tbody>
<tr><th>PDFIO_ENCRYPTION_AES_128 </th><td class="description">128-bit AES encryption (PDF 1.6)</td></tr>
<tr><th>PDFIO_ENCRYPTION_AES_256 </th><td class="description">256-bit AES encryption (PDF 2.0)</td></tr>
<tr><th>PDFIO_ENCRYPTION_NONE </th><td class="description">No encryption</td></tr>
<tr><th>PDFIO_ENCRYPTION_RC4_128 </th><td class="description">128-bit RC4 encryption (PDF 1.4)</td></tr>
<tr><th>PDFIO_ENCRYPTION_RC4_40 </th><td class="description">40-bit RC4 encryption (PDF 1.3)</td></tr>

View File

@ -15,7 +15,7 @@ goals of pdfio are:
PDFio is *not* concerned with rendering or viewing a PDF file, although a PDF
RIP or viewer could be written using it.
PDFio is Copyright © 2021-2023 by Michael R Sweet and is licensed under the
PDFio is Copyright © 2021-2024 by Michael R Sweet and is licensed under the
Apache License Version 2.0 with an (optional) exception to allow linking against
GPL2/LGPL2 software. See the files "LICENSE" and "NOTICE" for more information.
@ -27,6 +27,7 @@ PDFio requires the following to build the software:
- A C99 compiler such as Clang, GCC, or MS Visual C
- A POSIX-compliant `make` program
- A POSIX-compliant `sh` program
- ZLIB (<https://www.zlib.net>) 1.0 or higher
IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
@ -35,10 +36,11 @@ IDE files for Xcode (macOS/iOS) and Visual Studio (Windows) are also provided.
Installing pdfio
----------------
PDFio comes with a portable makefile that will work on any POSIX-compliant
system with ZLIB installed. To make it, run:
PDFio comes with a configure script that creates a portable makefile that will
work on any POSIX-compliant system with ZLIB installed. To make it, run:
make all
./configure
make
To test it, run:
@ -46,45 +48,22 @@ To test it, run:
To install it, run:
make install
sudo make install
If you want a shared library, run:
make all-shared
make install-shared
./configure --enable-shared
make
sudo make install
The default installation location is "/usr/local". Pass the `prefix` variable
The default installation location is "/usr/local". Pass the `--prefix` option
to make to install it to another location:
make install prefix=/some/other/directory
./configure --prefix=/some/other/directory
The makefile installs the pdfio header to "${prefix}/include", the library to
"${prefix}/lib", the `pkg-config` file to "${prefix}/lib/pkgconfig", the man
page to "${prefix}/share/man/man3", and the documentation to
"${prefix}/share/doc/pdfio".
Other configure options can be found using the `--help` option:
The makefile supports the following variables that can be specified in the make
command or as environment variables:
- `AR`: the library archiver (default "ar")
- `ARFLAGS`: options for the library archiver (default "cr")
- `CC`: the C compiler (default "cc")
- `CFLAGS`: options for the C compiler (default "")
- `CODESIGN_IDENTITY`: the identity to use when code signing the shared library
on macOS (default "Developer ID")
- `COMMONFLAGS`: options for the C compiler and linker (typically architecture
and optimization options, default is "-Os -g")
- `CPPFLAGS`: options for the C preprocessor (default "")
- `DESTDIR` and `DSTROOT`: specifies a root directory when installing
(default is "", specify only one)
- `DSOFLAGS`: options for the C compiler when linking the shared library
(default "")
- `LDFLAGS`: options for the C compiler when linking the test programs
(default "")
- `LIBS`: library options when linking the test programs (default "-lz")
- `RANLIB`: program that generates a table-of-contents in a library
(default "ranlib")
- `prefix`: specifies the installation directory (default "/usr/local")
./configure --help
Visual Studio Project
@ -102,10 +81,6 @@ generates a static library that will be installed under "/usr/local" with:
sudo xcodebuild install
You can reproduce this with the makefile using:
sudo make macos install
Detecting PDFio
---------------
@ -163,15 +138,32 @@ Reading PDF Files
You open an existing PDF file using the [`pdfioFileOpen`](@@) function:
```c
pdfio_file_t *pdf = pdfioFileOpen("myinputfile.pdf", error_cb, error_data);
pdfio_file_t *pdf = pdfioFileOpen("myinputfile.pdf", password_cb, password_data,
error_cb, error_data);
```
where the three arguments to the function are the filename ("myinputfile.pdf"),
an optional error callback function (`error_cb`), and an optional pointer value
for the error callback function (`error_data`). The error callback is called
for both errors and warnings and accepts the `pdfio_file_t` pointer, a message
string, and the callback pointer value, for example:
where the five arguments to the function are the filename ("myinputfile.pdf"),
an optional password callback function (`password_cb`) and data pointer value
(`password_data`), and an optional error callback function (`error_cb`) and data
pointer value (`error_data`). The password callback is called for encrypted PDF
files that are not using the default password, for example:
```c
const char *
password_cb(void *data, const char *filename)
{
(void)data; // This callback doesn't use the data pointer
(void)filename; // This callback doesn't use the filename
// Return a password string for the file...
return ("Password42");
}
```
The error callback is called for both errors and warnings and accepts the
`pdfio_file_t` pointer, a message string, and the callback pointer value, for
example:
```c
bool
@ -361,7 +353,7 @@ PDF Content Helper Functions
----------------------------
PDFio includes many helper functions for embedding or writing specific kinds of
content to a PDF file. These functions can be roughly grouped into ???
content to a PDF file. These functions can be roughly grouped into five
categories:
- [Color Space Functions](@)
@ -455,7 +447,7 @@ pdfio_obj_t *arial = pdfioFileCreateFontObjFromFile(pdf, "NotoSansJP-Regular.otf
will embed the NotoSansJP Regular OpenType font with full support for Unicode.
Note: Not all fonts support Unicode.
> Note: Not all fonts support Unicode.
### Image Object Functions
@ -584,8 +576,12 @@ escaping, as needed:
- [`pdfioContentTextMoveLine`](@@) moves to the next line with an offset in a
text block
- [`pdfioContentTextMoveTo`](@@) moves within the current line in a text block
- [`pdfioContentTextNextLine`](@@) moves to the beginning of the next line in a
- [`pdfioContentTextNewLine`](@@) moves to the beginning of the next line in a
text block
- [`pdfioContentTextNewLineShow`](@@) moves to the beginning of the next line in a
text block and shows literal text with optional word and character spacing
- [`pdfioContentTextNewLineShowf`](@@) moves to the beginning of the next line in a
text block and shows formatted text with optional word and character spacing
- [`pdfioContentTextShow`](@@) draws a literal string in a text block
- [`pdfioContentTextShowf`](@@) draws a formatted string in a text block
- [`pdfioContentTextShowJustified`](@@) draws an array of literal strings with

232
install-sh Executable file
View File

@ -0,0 +1,232 @@
#!/bin/sh
#
# Install a program, script, or datafile.
#
# Copyright 2008-2012 by Apple Inc.
#
# This script is not compatible with BSD (or any other) install program, as it
# allows owner and group changes to fail with a warning and makes sure that the
# destination directory permissions are as specified - BSD install and the
# original X11 install script did not change permissions of existing
# directories. It also does not support the transform options since CUPS does
# not use them...
#
# Original script from X11R5 (mit/util/scripts/install.sh)
# Copyright 1991 by the Massachusetts Institute of Technology
#
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, and that the name of M.I.T. not be used in advertising or
# publicity pertaining to distribution of the software without specific,
# written prior permission. M.I.T. makes no representations about the
# suitability of this software for any purpose. It is provided "as is"
# without express or implied warranty.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# Force umask to 022...
umask 022
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
gzipprog="${GZIPPROG-gzip}"
transformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
gzipcp() {
# gzipcp from to
$gzipprog -9 <"$1" >"$2"
}
while [ x"$1" != x ]; do
case $1 in
-c)
instcmd="$cpprog"
shift
continue
;;
-d)
dir_arg=true
shift
continue
;;
-m)
chmodcmd="$chmodprog $2"
shift
shift
continue
;;
-o)
chowncmd="$chownprog $2"
shift
shift
continue
;;
-g)
chgrpcmd="$chgrpprog $2"
shift
shift
continue
;;
-s)
stripcmd="$stripprog"
shift
continue
;;
-z)
instcmd="gzipcp"
shift
continue
;;
*)
if [ x"$src" = x ]; then
src="$1"
else
dst="$1"
fi
shift
continue
;;
esac
done
if [ x"$src" = x ]; then
echo "install-sh: No input file specified"
exit 1
fi
if [ x"$dir_arg" != x ]; then
dst="$src"
src=""
if [ -d "$dst" ]; then
instcmd=:
else
instcmd=$mkdirprog
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ ! -f "$src" -a ! -d "$src" ]; then
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]; then
echo "install: No destination specified"
exit 1
fi
# If destination is a directory, append the input filename.
if [ -d "$dst" ]; then
dst="$dst/`basename $src`"
fi
fi
## this sed command emulates the dirname command
dstdir="`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`"
# Make sure that the destination directory exists.
# This part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ]; then $doit $mkdirprog "${pathcomp}"; fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]; then
# Make a directory...
$doit $instcmd $dst || exit 1
# Allow chown/chgrp to fail, but log a warning
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst || echo "warning: Unable to change owner of $dst!"; fi
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst || echo "warning: Unable to change group of $dst!"; fi
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst || exit 1; fi
else
# Install a file...
dstfile="`basename $dst`"
# Check the destination file - for libraries just use the "-x" option
# to strip...
case "$dstfile" in
*.a | *.dylib | *.sl | *.sl.* | *.so | *.so.*)
stripopt="-x"
;;
*)
stripopt=""
;;
esac
# Make a temp file name in the proper directory.
dsttmp="$dstdir/#inst.$$#"
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp || exit 1
# Update permissions and strip as needed, then move to the final name.
# If the chmod, strip, rm, or mv commands fail, remove the installed
# file...
if [ x"$stripcmd" != x ]; then $doit $stripcmd $stripopt "$dsttmp" || echo "warning: Unable to strip $dst!"; fi
if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp" || echo "warning: Unable to change owner of $dst!"; fi
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp" || echo "warning: Unable to change group of $dst!"; fi
trap "rm -f ${dsttmp}" 0 &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; fi &&
$doit $rmcmd -f "$dstdir/$dstfile" &&
$doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
fi
exit 0

View File

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

View File

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

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -145,7 +141,7 @@ _pdfioFileGets(pdfio_file_t *pdf, // I - PDF file
*bufend = buffer + bufsize - 1; // Pointer to end of buffer
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend);
PDFIO_DEBUG("_pdfioFileGets(pdf=%p, buffer=%p, bufsize=%lu) bufpos=%ld, buffer=%p, bufptr=%p, bufend=%p, offset=%lu\n", pdf, buffer, (unsigned long)bufsize, (long)pdf->bufpos, pdf->buffer, pdf->bufptr, pdf->bufend, (unsigned long)(pdf->bufpos + (pdf->bufptr - pdf->buffer)));
while (!eol)
{
@ -360,12 +356,12 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
off_t offset, // I - Offset
int whence) // I - Offset base
{
PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d)\n", pdf, (long)offset, whence);
PDFIO_DEBUG("_pdfioFileSeek(pdf=%p, offset=%ld, whence=%d) pdf->bufpos=%lu\n", pdf, (long)offset, whence, (unsigned long)(pdf ? pdf->bufpos : 0));
// Adjust offset for relative seeks...
if (whence == SEEK_CUR)
{
offset += pdf->bufpos;
offset += pdf->bufpos + (pdf->bufptr - pdf->buffer);
whence = SEEK_SET;
}
@ -375,9 +371,9 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
if (whence != SEEK_END && offset >= pdf->bufpos && offset < (pdf->bufpos + pdf->bufend - pdf->buffer))
{
// Yes, seek within existing buffer...
pdf->bufptr = pdf->buffer + offset - pdf->bufpos;
pdf->bufptr = pdf->buffer + (offset - pdf->bufpos);
PDFIO_DEBUG("_pdfioFileSeek: Seek within buffer, bufpos=%ld.\n", (long)pdf->bufpos);
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend);
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p(<%02X%02X...>), bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufptr[0] & 255, pdf->bufptr[1] & 255, pdf->bufend);
return (offset);
}
@ -408,7 +404,7 @@ _pdfioFileSeek(pdfio_file_t *pdf, // I - PDF file
return (-1);
}
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld.\n", (long)pdf->bufpos);
PDFIO_DEBUG("_pdfioFileSeek: Reset bufpos=%ld, offset=%lu.\n", (long)pdf->bufpos, (unsigned long)offset);
PDFIO_DEBUG("_pdfioFileSeek: buffer=%p, bufptr=%p, bufend=%p\n", pdf->buffer, pdf->bufptr, pdf->bufend);
pdf->bufpos = offset;

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
#include "pdfio-content.h"
#include "ttf.h"
@ -1064,6 +1060,104 @@ pdfioContentTextEnd(pdfio_stream_t *st) // I - Stream
}
//
// 'pdfioContentTextMeasure()' - Measure a text string and return its width.
//
// 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
// any control characters (such as newlines) are ignored.
//
double // O - Width
pdfioContentTextMeasure(
pdfio_obj_t *font, // I - Font object created by @link pdfioFileCreateFontObjFromFile@
const char *s, // I - UTF-8 string
double size) // I - Font size/height
{
const char *subtype; // Font sub-type
ttf_t *ttf = (ttf_t *)_pdfioObjGetExtension(font);
// TrueType font data
ttf_rect_t extents; // Text extents
int ch; // Unicode character
char temp[1024], // Temporary string
*tempptr; // Pointer into temporary string
if ((subtype = pdfioObjGetSubtype(font)) == NULL || strcmp(subtype, "Type0"))
{
// Map non-CP1282 characters to '?', everything else as-is...
tempptr = temp;
while (*s && tempptr < (temp + sizeof(temp) - 3))
{
if ((*s & 0xe0) == 0xc0)
{
// Two-byte UTF-8
ch = ((s[0] & 0x1f) << 6) | (s[1] & 0x3f);
s += 2;
}
else if ((*s & 0xf0) == 0xe0)
{
// Three-byte UTF-8
ch = ((s[0] & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f);
s += 3;
}
else if ((*s & 0xf8) == 0xf0)
{
// Four-byte UTF-8
ch = ((s[0] & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
s += 4;
}
else
{
ch = *s++;
}
if (ch > 255)
{
// Try mapping from Unicode to CP1252...
size_t i; // Looping var
for (i = 0; i < (sizeof(_pdfio_cp1252) / sizeof(_pdfio_cp1252[0])); i ++)
{
if (ch == _pdfio_cp1252[i])
break;
}
if (i >= (sizeof(_pdfio_cp1252) / sizeof(_pdfio_cp1252[0])))
ch = '?'; // Unsupported chars map to ?
}
if (ch < 128)
{
// ASCII
*tempptr++ = (char)ch;
}
else if (ch < 2048)
{
// 2-byte UTF-8
*tempptr++ = (char)(0xc0 | ((ch >> 6) & 0x1f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
else
{
// 3-byte UTF-8
*tempptr++ = (char)(0xe0 | ((ch >> 12) & 0x0f));
*tempptr++ = (char)(0x80 | ((ch >> 6) & 0x3f));
*tempptr++ = (char)(0x80 | (ch & 0x3f));
}
}
*tempptr = '\0';
s = temp;
}
ttfGetExtents(ttf, (float)size, s, &extents);
return (extents.right - extents.left);
}
//
// 'pdfioContentTextMoveLine()' - Move to the next line and offset.
//
@ -1093,7 +1187,21 @@ pdfioContentTextMoveTo(
//
// 'pdfioContentTextNextLine()' - Move to the next line.
// 'pdfioContentTextNewLine()' - Move to the next line.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNewLine(
pdfio_stream_t *st) // I - Stream
{
return (pdfioStreamPuts(st, "T*\n"));
}
//
// 'pdfioContentTextNextLine()' - Legacy function name preserved for binary compatibility.
//
// @private@
//
bool // O - `true` on success, `false` on failure
@ -1104,6 +1212,86 @@ pdfioContentTextNextLine(
}
//
// 'pdfioContentTextNewLineShow()' - Move to the next line and show text.
//
// This function moves to the next line and then shows some text with optional
// word and character spacing in a PDF content stream. The "unicode" argument
// specifies that the current font maps to full Unicode. The "s" argument
// specifies a UTF-8 encoded string.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNewLineShow(
pdfio_stream_t *st, // I - Stream
double ws, // I - Word spacing or `0.0` for none
double cs, // I - Character spacing or `0.0` for none
bool unicode, // I - Unicode text?
const char *s) // I - String to show
{
bool newline = false; // New line?
char op; // Text operator
// Write word and/or character spacing as needed...
if (ws > 0.0 || cs > 0.0)
{
// Use " operator to show text with word and character spacing...
if (!pdfioStreamPrintf(st, "%g %g", ws, cs))
return (false);
op = '\"';
}
else
{
// Use ' operator to show text with the defaults...
op = '\'';
}
// Write the string...
if (!write_string(st, unicode, s, &newline))
return (false);
// Draw it...
if (newline)
return (pdfioStreamPrintf(st, "%c T*\n", op));
else
return (pdfioStreamPrintf(st, "%c\n", op));
}
//
// 'pdfioContentTextNewLineShowf()' - Show formatted text.
//
// This function moves to the next line and shows some formatted text with
// optional word and character spacing in a PDF content stream. The "unicode"
// argument specifies that the current font maps to full Unicode. The "format"
// argument specifies a UTF-8 encoded `printf`-style format string.
//
bool // O - `true` on success, `false` on failure
pdfioContentTextNewLineShowf(
pdfio_stream_t *st, // I - Stream
double ws, // I - Word spacing or `0.0` for none
double cs, // I - Character spacing or `0.0` for none
bool unicode, // I - Unicode text?
const char *format, // I - `printf`-style format string
...) // I - Additional arguments as needed
{
char buffer[8192]; // Text buffer
va_list ap; // Argument pointer
// Format the string...
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
// Show it...
return (pdfioContentTextNewLineShow(st, ws, cs, unicode, buffer));
}
//
// 'pdfioContentTextShow()' - Show text.
//
@ -1136,9 +1324,9 @@ pdfioContentTextShow(
//
// 'pdfioContentTextShowf()' - Show formatted text.
//
// This function shows some text in a PDF content stream. The "unicode" argument
// specifies that the current font maps to full Unicode. The "format" argument
// specifies a UTF-8 encoded `printf`-style format string.
// This function shows some formatted text in a PDF content stream. The
// "unicode" argument specifies that the current font maps to full Unicode.
// The "format" argument specifies a UTF-8 encoded `printf`-style format string.
//
bool
@ -1289,13 +1477,13 @@ pdfioFileCreateFontObjFromFile(
pdfio_dict_t *dict, // Font dictionary
*desc, // Font descriptor
*file; // Font file dictionary
pdfio_obj_t *obj, // Font object
pdfio_obj_t *obj = NULL, // Font object
*desc_obj, // Font descriptor object
*file_obj; // Font file object
const char *basefont; // Base font name
pdfio_array_t *bbox; // Font bounding box array
pdfio_stream_t *st; // Font stream
int fd; // File
int fd = -1; // File
unsigned char buffer[16384]; // Read buffer
ssize_t bytes; // Bytes read
@ -1324,48 +1512,32 @@ pdfioFileCreateFontObjFromFile(
// Create the font file dictionary and object...
if ((file = pdfioDictCreate(pdf)) == NULL)
{
ttfDelete(font);
close(fd);
return (NULL);
}
goto done;
pdfioDictSetName(file, "Filter", "FlateDecode");
if ((file_obj = pdfioFileCreateObj(pdf, file)) == NULL)
{
ttfDelete(font);
close(fd);
return (NULL);
}
goto done;
if ((st = pdfioObjCreateStream(file_obj, PDFIO_FILTER_FLATE)) == NULL)
{
ttfDelete(font);
close(fd);
return (NULL);
}
goto done;
while ((bytes = read(fd, buffer, sizeof(buffer))) > 0)
{
if (!pdfioStreamWrite(st, buffer, (size_t)bytes))
{
ttfDelete(font);
close(fd);
pdfioStreamClose(st);
return (NULL);
goto done;
}
}
close(fd);
fd = -1;
pdfioStreamClose(st);
// Create the font descriptor dictionary and object...
if ((bbox = pdfioArrayCreate(pdf)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
ttfGetBounds(font, &bounds);
@ -1375,10 +1547,7 @@ pdfioFileCreateFontObjFromFile(
pdfioArrayAppendNumber(bbox, bounds.top);
if ((desc = pdfioDictCreate(pdf)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
basefont = pdfioStringCreate(pdf, ttfGetPostScriptName(font));
@ -1397,56 +1566,64 @@ pdfioFileCreateFontObjFromFile(
pdfioDictSetNumber(desc, "StemV", ttfGetWeight(font) / 4 + 25);
if ((desc_obj = pdfioFileCreateObj(pdf, desc)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
pdfioObjClose(desc_obj);
if (unicode)
{
// Unicode (CID) font...
pdfio_dict_t *cid2gid; // CIDToGIDMap dictionary
pdfio_obj_t *cid2gid_obj; // CIDToGIDMap object
pdfio_dict_t *cid2gid, // CIDToGIDMap dictionary
*to_unicode; // ToUnicode dictionary
pdfio_obj_t *cid2gid_obj, // CIDToGIDMap object
*to_unicode_obj;// ToUnicode object
size_t i, // Looping var
start, // Start character
num_cmap; // Number of CMap entries
const int *cmap; // CMap entries
int min_glyph, // First glyph
max_glyph; // Last glyph
unsigned short glyphs[65536]; // Glyph to Unicode mapping
unsigned char *bufptr, // Pointer into buffer
*bufend; // End of buffer
pdfio_dict_t *type2; // CIDFontType2 font dictionary
pdfio_obj_t *type2_obj; // CIDFontType2 font object
pdfio_array_t *descendants; // Decendant font list
pdfio_dict_t *sidict; // CIDSystemInfo dictionary
pdfio_array_t *w_array, // Width array
*temp_array; // Temporary width sub-array
int w0, w1; // Widths
// Create a CIDSystemInfo mapping to Adobe UCS2 v0 (Unicode)
if ((sidict = pdfioDictCreate(pdf)) == NULL)
goto done;
pdfioDictSetString(sidict, "Registry", "Adobe");
pdfioDictSetString(sidict, "Ordering", "Identity");
pdfioDictSetNumber(sidict, "Supplement", 0);
// Create a CIDToGIDMap object for the Unicode font...
if ((cid2gid = pdfioDictCreate(pdf)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
#ifndef DEBUG
pdfioDictSetName(cid2gid, "Filter", "FlateDecode");
#endif // !DEBUG
if ((cid2gid_obj = pdfioFileCreateObj(pdf, cid2gid)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
#ifdef DEBUG
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_NONE)) == NULL)
#else
if ((st = pdfioObjCreateStream(cid2gid_obj, PDFIO_FILTER_FLATE)) == NULL)
#endif // DEBUG
{
ttfDelete(font);
return (NULL);
}
goto done;
cmap = ttfGetCMap(font, &num_cmap);
cmap = ttfGetCMap(font, &num_cmap);
min_glyph = 65536;
max_glyph = 0;
memset(glyphs, 0, sizeof(glyphs));
PDFIO_DEBUG("pdfioFileCreateFontObjFromFile: num_cmap=%u\n", (unsigned)num_cmap);
@ -1464,6 +1641,12 @@ pdfioFileCreateFontObjFromFile(
// Map to specified glyph...
*bufptr++ = (unsigned char)(cmap[i] >> 8);
*bufptr++ = (unsigned char)(cmap[i] & 255);
glyphs[cmap[i]] = (unsigned short)i;
if (cmap[i] < min_glyph)
min_glyph = cmap[i];
if (cmap[i] > max_glyph)
max_glyph = cmap[i];
}
if (bufptr >= bufend)
@ -1472,8 +1655,7 @@ pdfioFileCreateFontObjFromFile(
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
{
pdfioStreamClose(st);
ttfDelete(font);
return (NULL);
goto done;
}
bufptr = buffer;
@ -1486,30 +1668,102 @@ pdfioFileCreateFontObjFromFile(
if (!pdfioStreamWrite(st, buffer, (size_t)(bufptr - buffer)))
{
pdfioStreamClose(st);
ttfDelete(font);
return (NULL);
goto done;
}
}
pdfioStreamClose(st);
// ToUnicode mapping object
to_unicode = pdfioDictCreate(pdf);
pdfioDictSetName(to_unicode, "Type", "CMap");
pdfioDictSetName(to_unicode, "CMapName", "Adobe-Identity-UCS2");
pdfioDictSetDict(to_unicode, "CIDSystemInfo", sidict);
#ifndef DEBUG
pdfioDictSetName(to_unicode, "Filter", "FlateDecode");
#endif // !DEBUG
if ((to_unicode_obj = pdfioFileCreateObj(pdf, to_unicode)) == NULL)
goto done;
#ifdef DEBUG
if ((st = pdfioObjCreateStream(to_unicode_obj, PDFIO_FILTER_NONE)) == NULL)
#else
if ((st = pdfioObjCreateStream(to_unicode_obj, PDFIO_FILTER_FLATE)) == NULL)
#endif // DEBUG
goto done;
pdfioStreamPuts(st,
"stream\n"
"/CIDInit /ProcSet findresource begin\n"
"12 dict begin\n"
"begincmap\n"
"/CIDSystemInfo<<\n"
"/Registry (Adobe)\n"
"/Ordering (UCS2)\n"
"/Supplement 0\n"
">> def\n"
"/CMapName /Adobe-Identity-UCS2 def\n"
"/CMapType 2 def\n"
"1 begincodespacerange\n"
"<0000> <FFFF>\n"
"endcodespacerange\n"
"1 beginbfrange\n"
"<0000> <FFFF> <0000>\n"
"endbfrange\n"
"endcmap\n"
"CMapName currentdict /CMap defineresource pop\n"
"end\n"
"end\n");
pdfioStreamClose(st);
// Create a CIDFontType2 dictionary for the Unicode font...
if ((type2 = pdfioDictCreate(pdf)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
if ((sidict = pdfioDictCreate(pdf)) == NULL)
{
ttfDelete(font);
return (NULL);
}
// Width array
if ((w_array = pdfioArrayCreate(pdf)) == NULL)
goto done;
// CIDSystemInfo mapping to Adobe UCS2 v0 (Unicode)
pdfioDictSetString(sidict, "Registry", "Adobe");
pdfioDictSetString(sidict, "Ordering", "Identity-H");
pdfioDictSetNumber(sidict, "Supplement", 0);
for (start = 0, w0 = ttfGetWidth(font, 0), w1 = 0, i = 1; i < 65536; start = i, w0 = w1, i ++)
{
while (i < 65536 && (w1 = ttfGetWidth(font, (int)i)) == w0)
i ++;
if ((i - start) > 1)
{
// Encode a repeating sequence...
pdfioArrayAppendNumber(w_array, start);
pdfioArrayAppendNumber(w_array, i - 1);
pdfioArrayAppendNumber(w_array, w0);
}
else
{
// Encode a non-repeating sequence...
pdfioArrayAppendNumber(w_array, start);
if ((temp_array = pdfioArrayCreate(pdf)) == NULL)
goto done;
pdfioArrayAppendNumber(temp_array, w0);
for (w0 = w1, i ++; i < 65536; w0 = w1, i ++)
{
if ((w1 = ttfGetWidth(font, (int)i)) == w0 && i < 65535)
break;
pdfioArrayAppendNumber(temp_array, w0);
}
if (i == 65536)
pdfioArrayAppendNumber(temp_array, w0);
else
i --;
pdfioArrayAppendArray(w_array, temp_array);
}
}
// Then the dictionary for the CID base font...
pdfioDictSetName(type2, "Type", "Font");
@ -1518,56 +1772,41 @@ pdfioFileCreateFontObjFromFile(
pdfioDictSetDict(type2, "CIDSystemInfo", sidict);
pdfioDictSetObj(type2, "CIDToGIDMap", cid2gid_obj);
pdfioDictSetObj(type2, "FontDescriptor", desc_obj);
pdfioDictSetArray(type2, "W", w_array);
if ((type2_obj = pdfioFileCreateObj(pdf, type2)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
pdfioObjClose(type2_obj);
// Create a Type 0 font object...
if ((descendants = pdfioArrayCreate(pdf)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
pdfioArrayAppendObj(descendants, type2_obj);
if ((dict = pdfioDictCreate(pdf)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
pdfioDictSetName(dict, "Type", "Font");
pdfioDictSetName(dict, "Subtype", "Type0");
pdfioDictSetName(dict, "BaseFont", basefont);
pdfioDictSetArray(dict, "DescendantFonts", descendants);
pdfioDictSetName(dict, "Encoding", "Identity-H");
pdfioDictSetObj(dict, "ToUnicode", to_unicode_obj);
if ((obj = pdfioFileCreateObj(pdf, dict)) == NULL)
return (NULL);
pdfioObjClose(obj);
if ((obj = pdfioFileCreateObj(pdf, dict)) != NULL)
pdfioObjClose(obj);
}
else
{
// Simple (CP1282 or custom encoding) 8-bit font...
if (ttfGetMaxChar(font) >= 255 && !pdf->cp1252_obj && !create_cp1252(pdf))
{
ttfDelete(font);
return (NULL);
}
goto done;
// Create a TrueType font object...
if ((dict = pdfioDictCreate(pdf)) == NULL)
{
ttfDelete(font);
return (NULL);
}
goto done;
pdfioDictSetName(dict, "Type", "Font");
pdfioDictSetName(dict, "Subtype", "TrueType");
@ -1577,16 +1816,16 @@ pdfioFileCreateFontObjFromFile(
pdfioDictSetObj(dict, "FontDescriptor", desc_obj);
if ((obj = pdfioFileCreateObj(pdf, dict)) == NULL)
{
ttfDelete(font);
return (NULL);
}
pdfioObjClose(obj);
if ((obj = pdfioFileCreateObj(pdf, dict)) != NULL)
pdfioObjClose(obj);
}
ttfDelete(font);
done:
if (fd >= 0)
close(fd);
_pdfioObjSetExtension(obj, font, (_pdfio_extfree_t)ttfDelete);
return (obj);
}
@ -3000,7 +3239,7 @@ write_string(pdfio_stream_t *st, // I - Stream
// Start the string...
if (!pdfioStreamPuts(st, unicode ? "<FEFF" : "("))
if (!pdfioStreamPuts(st, unicode ? "<" : "("))
return (false);
// Loop through the string, handling UTF-8 as needed...

View File

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

View File

@ -1,16 +1,12 @@
//
// Cryptographic support functions for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2023 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
#if _WIN32
# include <windows.h>
@ -403,16 +399,23 @@ _pdfioCryptoMakeRandom(uint8_t *buffer, // I - Buffer
//
_pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
_pdfioCryptoMakeReader(
_pdfioCryptoMakeReader(
pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - PDF object
_pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context
uint8_t *iv, // I - Buffer for initialization vector
size_t *ivlen) // IO - Size of initialization vector
{
uint8_t data[21]; /* Key data */
_pdfio_md5_t md5; /* MD5 state */
uint8_t digest[16]; /* MD5 digest value */
uint8_t data[21]; // Key data
_pdfio_md5_t md5; // MD5 state
uint8_t digest[16]; // MD5 digest value
#if PDFIO_OBJ_CRYPT
pdfio_array_t *id_array; // Object ID array
unsigned char *id_value; // Object ID value
size_t id_len; // Length of object ID
uint8_t temp_key[16]; // File key for object
#endif // PDFIO_OBJ_CRYPT
uint8_t *file_key; // Computed file key to use
PDFIO_DEBUG("_pdfioCryptoMakeReader(pdf=%p, obj=%p(%d), ctx=%p, iv=%p, ivlen=%p(%d))\n", pdf, obj, (int)obj->number, ctx, iv, ivlen, (int)*ivlen);
@ -424,6 +427,59 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
return (NULL);
}
#if PDFIO_OBJ_CRYPT
if ((id_array = pdfioDictGetArray(pdfioObjGetDict(obj), "ID")) != NULL)
{
// Object has its own ID that will get used for encryption...
_pdfio_md5_t md5; // MD5 context
uint8_t file_digest[16]; // MD5 digest of file ID and pad
uint8_t user_pad[32], // Padded user password
own_user_key[32], // Calculated user key
pdf_user_key[32]; // Decrypted user key
PDFIO_DEBUG("_pdfioCryptoMakeReader: Per-object file ID.\n");
if ((id_value = pdfioArrayGetBinary(id_array, 0, &id_len)) == NULL)
{
*ivlen = 0;
return (NULL);
}
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, pdf_passpad, 32);
_pdfioCryptoMD5Append(&md5, id_value, id_len);
_pdfioCryptoMD5Finish(&md5, file_digest);
make_owner_key(pdf->encryption, pdf->password, pdf->owner_key, user_pad);
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, user_pad, pdf->owner_key, temp_key);
make_user_key(id_value, id_len, own_user_key);
if (memcmp(own_user_key, pdf->user_key, sizeof(own_user_key)))
{
PDFIO_DEBUG("_pdfioCryptoMakeReader: Not user password, trying owner password.\n");
make_file_key(pdf->encryption, pdf->permissions, id_value, id_len, pdf->password, pdf->owner_key, temp_key);
make_user_key(id_value, id_len, own_user_key);
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
decrypt_user_key(pdf->encryption, temp_key, pdf_user_key);
if (memcmp(pdf->password, pdf_user_key, 32) && memcmp(own_user_key, pdf_user_key, 16))
{
*ivlen = 0;
return (NULL);
}
}
file_key = temp_key;
}
else
#endif // PDFIO_OBJ_CRYPT
{
// Use the default file key...
file_key = pdf->file_key;
}
switch (pdf->encryption)
{
default :
@ -432,7 +488,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
case PDFIO_ENCRYPTION_RC4_40 :
// Copy the key data for the MD5 hash.
memcpy(data, pdf->file_key, sizeof(pdf->file_key));
memcpy(data, file_key, 16);
data[16] = (uint8_t)obj->number;
data[17] = (uint8_t)(obj->number >> 8);
data[18] = (uint8_t)(obj->number >> 16);
@ -446,12 +502,20 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
// Initialize the RC4 context using 40 bits of the digest...
_pdfioCryptoRC4Init(&ctx->rc4, digest, 5);
*ivlen = 0;
return ((_pdfio_crypto_cb_t)_pdfioCryptoRC4Crypt);
case PDFIO_ENCRYPTION_RC4_128 :
case PDFIO_ENCRYPTION_AES_128 :
if (*ivlen < 16)
{
*ivlen = 0;
_pdfioFileError(pdf, "Value too short for AES encryption.");
return (NULL);
}
case PDFIO_ENCRYPTION_RC4_128 :
// Copy the key data for the MD5 hash.
memcpy(data, pdf->file_key, sizeof(pdf->file_key));
memcpy(data, file_key, 16);
data[16] = (uint8_t)obj->number;
data[17] = (uint8_t)(obj->number >> 8);
data[18] = (uint8_t)(obj->number >> 16);
@ -487,7 +551,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none
//
_pdfio_crypto_cb_t // O - Encryption callback or `NULL` for none
_pdfioCryptoMakeWriter(
_pdfioCryptoMakeWriter(
pdfio_file_t *pdf, // I - PDF file
pdfio_obj_t *obj, // I - PDF object
_pdfio_crypto_ctx_t *ctx, // I - Pointer to crypto context
@ -592,6 +656,8 @@ _pdfioCryptoUnlock(
revision = (int)pdfioDictGetNumber(encrypt_dict, "R");
length = (int)pdfioDictGetNumber(encrypt_dict, "Length");
PDFIO_DEBUG("_pdfioCryptoUnlock: handler=%p(%s), version=%d, revision=%d, length=%d\n", (void *)handler, handler ? handler : "(null)", version, revision, length);
if (!handler || strcmp(handler, "Standard"))
{
_pdfioFileError(pdf, "Unsupported security handler '%s'.", handler ? handler : "(null)");
@ -640,6 +706,8 @@ _pdfioCryptoUnlock(
}
else
{
PDFIO_DEBUG("_pdfioCryptoUnlock: CFM=\"%s\"\n", cfm);
if (length < 40 || length > 128)
length = 128; // Default to 128 bits
@ -765,17 +833,16 @@ _pdfioCryptoUnlock(
{
// Matches!
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
memcpy(pdf->password, pad, sizeof(pdf->password));
return (true);
}
/*
* Not the owner password, try the user password...
*/
// Not the owner password, try the user password...
make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, pad, pdf->owner_key, file_key);
PDFIO_DEBUG("_pdfioCryptoUnlock: Fuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]);
make_user_key(file_id, file_idlen, user_key);
make_user_key(file_id, file_idlen, own_user_key);
memcpy(pdf_user_key, pdf->user_key, sizeof(pdf_user_key));
decrypt_user_key(pdf->encryption, file_key, pdf_user_key);
@ -783,10 +850,12 @@ _pdfioCryptoUnlock(
PDFIO_DEBUG("_pdfioCryptoUnlock: Uuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", user_key[0], user_key[1], user_key[2], user_key[3], user_key[28], user_key[29], user_key[30], user_key[31]);
PDFIO_DEBUG("_pdfioCryptoUnlock: Updf=%02X%02X%02X%02X...%02X%02X%02X%02X\n", pdf_user_key[0], pdf_user_key[1], pdf_user_key[2], pdf_user_key[3], pdf_user_key[28], pdf_user_key[29], pdf_user_key[30], pdf_user_key[31]);
if (!memcmp(pad, pdf_user_key, 32) || !memcmp(user_key, pdf_user_key, 16))
if (!memcmp(pad, pdf_user_key, 32) || !memcmp(own_user_key, pdf_user_key, 16))
{
// Matches!
memcpy(pdf->file_key, file_key, sizeof(pdf->file_key));
memcpy(pdf->password, pad, sizeof(pdf->password));
return (true);
}
}

View File

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

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
#ifndef O_BINARY
# define O_BINARY 0
@ -23,7 +19,6 @@
static pdfio_obj_t *add_obj(pdfio_file_t *pdf, size_t number, unsigned short generation, off_t offset);
static int compare_objmaps(_pdfio_objmap_t *a, _pdfio_objmap_t *b);
static int compare_objs(pdfio_obj_t **a, pdfio_obj_t **b);
static const char *get_info_string(pdfio_file_t *pdf, const char *key);
static bool load_obj_stream(pdfio_obj_t *obj);
static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj, size_t depth);
@ -334,6 +329,32 @@ pdfioFileCreateArrayObj(
}
//
// 'pdfioFileCreateNumberObj()' - Create a new object in a PDF file containing a number.
//
// This function creates a new object with a number value in a PDF file.
// You must call @link pdfioObjClose@ to write the object to the file.
//
pdfio_obj_t * // O - New object
pdfioFileCreateNumberObj(
pdfio_file_t *pdf, // I - PDF file
double number) // I - Number value
{
_pdfio_value_t value; // Object value
// Range check input...
if (!pdf)
return (NULL);
value.type = PDFIO_VALTYPE_NUMBER;
value.value.number = number;
return (_pdfioFileCreateObj(pdf, NULL, &value));
}
//
// 'pdfioFileCreateObj()' - Create a new object in a PDF file.
//
@ -639,6 +660,32 @@ pdfioFileCreatePage(pdfio_file_t *pdf, // I - PDF file
}
//
// 'pdfioFileCreateStringObj()' - Create a new object in a PDF file containing a string.
//
// This function creates a new object with a string value in a PDF file.
// You must call @link pdfioObjClose@ to write the object to the file.
//
pdfio_obj_t * // O - New object
pdfioFileCreateStringObj(
pdfio_file_t *pdf, // I - PDF file
const char *string) // I - String
{
_pdfio_value_t value; // Object value
// Range check input...
if (!pdf)
return (NULL);
value.type = PDFIO_VALTYPE_STRING;
value.value.string = string;
return (_pdfioFileCreateObj(pdf, NULL, &value));
}
//
// 'pdfioFileCreateTemporary()' - Create a temporary PDF file.
//
@ -868,21 +915,57 @@ pdfioFileFindObj(
pdfio_file_t *pdf, // I - PDF file
size_t number) // I - Object number (1 to N)
{
pdfio_obj_t key, // Search key
*keyptr, // Pointer to key
**match; // Pointer to match
size_t left, // Left object
right, // Right object
current; // Current object
if (pdf->num_objs > 0)
PDFIO_DEBUG("pdfioFileFindObj(pdf=%p, number=%lu) alloc_objs=%lu, num_objs=%lu, objs=%p\n", (void *)pdf, (unsigned long)number, (unsigned long)(pdf ? pdf->alloc_objs : 0), (unsigned long)(pdf ? pdf->num_objs : 0), (void *)(pdf ? pdf->objs : NULL));
// Range check input...
if (!pdf || pdf->num_objs == 0 || number < 1)
return (NULL);
// Do a binary search for the object...
if ((current = number - 1) >= pdf->num_objs)
current = pdf->num_objs / 2;
PDFIO_DEBUG("pdfioFileFindObj: objs[current=%lu]=%p\n", (unsigned long)current, (void *)pdf->objs[current]);
if (number == pdf->objs[current]->number)
{
key.number = number;
keyptr = &key;
match = (pdfio_obj_t **)bsearch(&keyptr, pdf->objs, pdf->num_objs, sizeof(pdfio_obj_t *), (int (*)(const void *, const void *))compare_objs);
return (match ? *match : NULL);
// Fast match...
return (pdf->objs[current]);
}
else if (number < pdf->objs[current]->number)
{
left = 0;
right = current;
}
else
{
left = current;
right = pdf->num_objs - 1;
}
return (NULL);
while ((right - left) > 1)
{
current = (left + right) / 2;
if (number == pdf->objs[current]->number)
return (pdf->objs[current]);
else if (number < pdf->objs[current]->number)
right = current;
else
left = current;
}
if (number == pdf->objs[left]->number)
return (pdf->objs[left]);
else if (number == pdf->objs[right]->number)
return (pdf->objs[right]);
else
return (NULL);
}
@ -1106,8 +1189,10 @@ pdfioFileOpen(
void *error_data) // I - Error callback data, if any
{
pdfio_file_t *pdf; // PDF file
char line[1024], // Line from file
*ptr; // Pointer into line
char line[1025], // Line from file
*ptr, // Pointer into line
*end; // End of line
ssize_t bytes; // Bytes read
off_t xref_offset; // Offset to xref table
@ -1162,21 +1247,29 @@ pdfioFileOpen(
// Copy the version number...
pdf->version = strdup(line + 5);
// Grab the last 32 characters of the file to find the start of the xref table...
if (_pdfioFileSeek(pdf, -32, SEEK_END) < 0)
// Grab the last 1k of the file to find the start of the xref table...
if (_pdfioFileSeek(pdf, -1024, SEEK_END) < 0)
{
_pdfioFileError(pdf, "Unable to read startxref data.");
goto error;
}
if (_pdfioFileRead(pdf, line, 32) < 32)
if ((bytes = _pdfioFileRead(pdf, line, sizeof(line) - 1)) < 1)
{
_pdfioFileError(pdf, "Unable to read startxref data.");
goto error;
}
line[32] = '\0';
if ((ptr = strstr(line, "startxref")) == NULL)
line[bytes] = '\0';
end = line + bytes - 9;
for (ptr = line; ptr < end; ptr ++)
{
if (!memcmp(ptr, "startxref", 9))
break;
}
if (ptr >= end)
{
_pdfioFileError(pdf, "Unable to find start of xref table.");
goto error;
@ -1327,6 +1420,9 @@ add_obj(pdfio_file_t *pdf, // I - PDF file
off_t offset) // I - Offset in file
{
pdfio_obj_t *obj; // Object
size_t left, // Left object
right, // Right object
current; // Current object (center)
// Allocate memory for the object...
@ -1352,18 +1448,63 @@ add_obj(pdfio_file_t *pdf, // I - PDF file
pdf->alloc_objs += 32;
}
pdf->objs[pdf->num_objs ++] = obj;
obj->pdf = pdf;
obj->number = number;
obj->generation = generation;
obj->offset = offset;
PDFIO_DEBUG("add_obj: obj=%p, ->pdf=%p, ->number=%lu\n", obj, pdf, (unsigned long)obj->number);
PDFIO_DEBUG("add_obj: obj=%p, ->pdf=%p, ->number=%lu, ->offset=%lu\n", obj, pdf, (unsigned long)obj->number, (unsigned long)offset);
// Re-sort object array as needed...
if (pdf->num_objs > 1 && pdf->objs[pdf->num_objs - 2]->number > number)
qsort(pdf->objs, pdf->num_objs, sizeof(pdfio_obj_t *), (int (*)(const void *, const void *))compare_objs);
// Insert object into array as needed...
if (pdf->num_objs == 0 || obj->number > pdf->objs[pdf->num_objs - 1]->number)
{
// Append object...
PDFIO_DEBUG("add_obj: Appending at %lu\n", (unsigned long)pdf->num_objs);
pdf->objs[pdf->num_objs] = obj;
pdf->last_obj = pdf->num_objs;
}
else
{
// Insert object...
if (obj->number < pdf->objs[pdf->last_obj]->number)
{
left = 0;
right = pdf->last_obj;
}
else
{
left = pdf->last_obj;
right = pdf->num_objs - 1;
}
while ((right - left) > 1)
{
current = (left + right) / 2;
if (obj->number < pdf->objs[current]->number)
right = current;
else
left = current;
}
if (obj->number < pdf->objs[left]->number)
current = left;
else if (obj->number < pdf->objs[right]->number)
current = right;
else
current = right;
PDFIO_DEBUG("add_obj: Inserting at %lu\n", (unsigned long)current);
if (current < pdf->num_objs)
memmove(pdf->objs + current + 1, pdf->objs + current, (pdf->num_objs - current) * sizeof(pdfio_obj_t *));
pdf->objs[current] = obj;
pdf->last_obj = current;
}
pdf->num_objs ++;
return (obj);
}
@ -1390,23 +1531,6 @@ compare_objmaps(_pdfio_objmap_t *a, // I - First object map
}
//
// 'compare_objs()' - Compare the object numbers of two objects.
//
static int // O - Result of comparison
compare_objs(pdfio_obj_t **a, // I - First object
pdfio_obj_t **b) // I - Second object
{
if ((*a)->number < (*b)->number)
return (-1);
else if ((*a)->number == (*b)->number)
return (0);
else
return (1);
}
//
// 'get_info_string()' - Get a string value from the Info dictionary.
//
@ -1514,6 +1638,7 @@ load_obj_stream(pdfio_obj_t *obj) // I - Object to load
// Skip offset
_pdfioTokenGet(&tb, buffer, sizeof(buffer));
PDFIO_DEBUG("load_obj_stream: %ld at offset %s\n", (long)number, buffer);
}
if (!buffer[0])
@ -1678,7 +1803,7 @@ load_xref(
pdfio_stream_t *st; // Stream
unsigned char buffer[32]; // Read buffer
size_t num_sobjs = 0, // Number of object streams
sobjs[4096]; // Object streams to load
sobjs[8192]; // Object streams to load
pdfio_obj_t *current; // Current object
if ((number = strtoimax(line, &ptr, 10)) < 1)
@ -1687,7 +1812,7 @@ load_xref(
return (false);
}
if ((generation = (int)strtol(ptr, &ptr, 10)) < 0 || generation > 65535)
if ((generation = (int)strtol(ptr, &ptr, 10)) < 0 || (generation > 65535 && number != 0))
{
_pdfioFileError(pdf, "Bad xref table header '%s'.", line);
return (false);
@ -1865,8 +1990,18 @@ load_xref(
break;
}
if (i >= num_sobjs && num_sobjs < (sizeof(sobjs) / sizeof(sobjs[0])))
sobjs[num_sobjs ++] = (size_t)offset;
if (i >= num_sobjs)
{
if (num_sobjs < (sizeof(sobjs) / sizeof(sobjs[0])))
{
sobjs[num_sobjs ++] = (size_t)offset;
}
else
{
_pdfioFileError(pdf, "Too many object streams.");
return (false);
}
}
}
else if (!current)
{
@ -1914,15 +2049,35 @@ load_xref(
}
}
}
else if (!strncmp(line, "xref", 4) && !line[4] || isspace(line[4] & 255))
else if (!strncmp(line, "xref", 4) && (!line[4] || isspace(line[4] & 255)))
{
// Read the xref tables
off_t trailer_offset = _pdfioFileTell(pdf);
// Offset of current line
PDFIO_DEBUG("load_xref: Reading xref table starting at offset %lu\n", (unsigned long)trailer_offset);
while (_pdfioFileGets(pdf, line, sizeof(line)))
{
if (!strncmp(line, "trailer", 7) && !line[7] || isspace(line[7] & 255))
PDFIO_DEBUG("load_xref: '%s' at offset %lu\n", line, (unsigned long)trailer_offset);
if (!strncmp(line, "trailer", 7) && (!line[7] || isspace(line[7] & 255)))
{
if (line[7])
{
// Probably the start of the trailer dictionary, rewind the file so
// we can read it...
_pdfioFileSeek(pdf, trailer_offset + 7, SEEK_SET);
}
break;
else if (!line[0])
continue;
}
else
{
trailer_offset = _pdfioFileTell(pdf);
if (!line[0])
continue;
}
if (sscanf(line, "%jd%jd", &number, &num_objects) != 2)
{
@ -1953,7 +2108,7 @@ load_xref(
return (false);
}
if ((generation = (int)strtol(ptr, &ptr, 10)) < 0 || generation > 65535)
if ((generation = (int)strtol(ptr, &ptr, 10)) < 0 || (generation > 65535 && offset != 0))
{
_pdfioFileError(pdf, "Malformed xref table entry '%s'.", line);
return (false);
@ -1982,6 +2137,8 @@ load_xref(
if (!add_obj(pdf, (size_t)number, (unsigned short)generation, offset))
return (false);
}
trailer_offset = _pdfioFileTell(pdf);
}
if (strncmp(line, "trailer", 7))
@ -2032,8 +2189,19 @@ load_xref(
PDFIO_DEBUG_VALUE(&trailer);
PDFIO_DEBUG("\n");
if ((xref_offset = (off_t)pdfioDictGetNumber(trailer.value.dict, "Prev")) <= 0)
off_t new_offset = (off_t)pdfioDictGetNumber(trailer.value.dict, "Prev");
if (new_offset <= 0)
{
done = true;
}
else if (new_offset == xref_offset)
{
_pdfioFileError(pdf, "Recursive xref table.");
return (false);
}
xref_offset = new_offset;
}
// Once we have all of the xref tables loaded, get the important objects and

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -221,8 +217,13 @@ void
_pdfioObjDelete(pdfio_obj_t *obj) // I - Object
{
if (obj)
{
pdfioStreamClose(obj->stream);
if (obj->datafree)
(obj->datafree)(obj->data);
}
free(obj);
}
@ -267,6 +268,17 @@ pdfioObjGetDict(pdfio_obj_t *obj) // I - Object
}
//
// '_pdfioObjGetExtension()' - Get the extension pointer for an object.
//
void * // O - Extension data
_pdfioObjGetExtension(pdfio_obj_t *obj) // I - Object
{
return (obj->data);
}
//
// 'pdfioObjGetGeneration()' - Get the object's generation number.
//
@ -441,9 +453,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
return (false);
}
PDFIO_DEBUG("_pdfioObjLoad: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[0]=0x%02x\n", tb.bufptr, tb.bufend, tb.bufptr[0], tb.bufptr[1]);
if (tb.bufptr && tb.bufptr < tb.bufend && (tb.bufptr[0] == 0x0d || tb.bufptr[0] == 0x0a))
tb.bufptr ++; // Skip trailing CR or LF after token
PDFIO_DEBUG("_pdfioObjLoad: tb.bufptr=%p, tb.bufend=%p, tb.bufptr[0]=0x%02x, tb.bufptr[1]=0x%02x\n", tb.bufptr, tb.bufend, tb.bufptr[0], tb.bufptr[1]);
_pdfioTokenFlush(&tb);
@ -454,6 +464,18 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object
PDFIO_DEBUG("_pdfioObjLoad: stream_offset=%lu.\n", (unsigned long)obj->stream_offset);
}
// Decrypt as needed...
if (obj->pdf->encryption)
{
PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n");
if (!_pdfioValueDecrypt(obj->pdf, obj, &obj->value, 0))
{
PDFIO_DEBUG("_pdfioObjLoad: Failed to decrypt.\n");
return (false);
}
}
PDFIO_DEBUG("_pdfioObjLoad: ");
PDFIO_DEBUG_VALUE(&obj->value);
PDFIO_DEBUG("\n");
@ -498,6 +520,21 @@ pdfioObjOpenStream(pdfio_obj_t *obj, // I - Object
}
//
// '_pdfioObjSetExtension()' - Set extension data for an object.
//
void
_pdfioObjSetExtension(
pdfio_obj_t *obj, // I - Object
void *data, // I - Data
_pdfio_extfree_t datafree) // I - Free function
{
obj->data = data;
obj->datafree = datafree;
}
//
// 'write_obj_header()' - Write the object header...
//

View File

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

View File

@ -92,6 +92,9 @@
# define PDFIO_MAX_DEPTH 32 // Maximum nesting depth for values
typedef void (*_pdfio_extfree_t)(void *);
// Extension data free function
typedef enum _pdfio_mode_e // Read/write mode
{
_PDFIO_MODE_READ, // Read a PDF file
@ -234,7 +237,8 @@ struct _pdfio_file_s // PDF file structure
pdfio_permission_t permissions; // Access permissions (encrypted PDF files)
uint8_t file_key[16], // File encryption key
owner_key[32], // Owner encryption key
user_key[32]; // User encryption key
user_key[32], // User encryption key
password[32]; // Padded password
size_t file_keylen, // Length of file encryption key
owner_keylen, // Length of owner encryption key
user_keylen; // Length of user encryption key
@ -262,7 +266,8 @@ struct _pdfio_file_s // PDF file structure
alloc_dicts; // Allocated dictionaries
pdfio_dict_t **dicts; // Dictionaries
size_t num_objs, // Number of objects
alloc_objs; // Allocated objects
alloc_objs, // Allocated objects
last_obj; // Last object added
pdfio_obj_t **objs, // Objects
*current_obj; // Current object being written/read
size_t num_objmaps, // Number of object maps
@ -287,6 +292,8 @@ struct _pdfio_obj_s // Object
size_t stream_length; // Length of stream, if any
_pdfio_value_t value; // Dictionary/number/etc. value
pdfio_stream_t *stream; // Open stream, if any
void *data; // Extension data, if any
_pdfio_extfree_t datafree; // Free callback for extension data
};
struct _pdfio_stream_s // Stream
@ -315,6 +322,7 @@ struct _pdfio_stream_s // Stream
// Functions...
//
extern bool _pdfioArrayDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_array_t *a, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL;
@ -339,6 +347,7 @@ extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Dig
extern bool _pdfioCryptoUnlock(pdfio_file_t *pdf, pdfio_password_cb_t password_cb, void *password_data) _PDFIO_INTERNAL;
extern void _pdfioDictClear(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
extern bool _pdfioDictDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, pdfio_dict_t *dict, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL;
@ -365,7 +374,9 @@ extern off_t _pdfioFileTell(pdfio_file_t *pdf) _PDFIO_INTERNAL;
extern bool _pdfioFileWrite(pdfio_file_t *pdf, const void *buffer, size_t bytes) _PDFIO_INTERNAL;
extern void _pdfioObjDelete(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 void _pdfioObjSetExtension(pdfio_obj_t *obj, void *data, _pdfio_extfree_t datafree) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamCreate(pdfio_obj_t *obj, pdfio_obj_t *length_obj, pdfio_filter_t compression) _PDFIO_INTERNAL;
extern pdfio_stream_t *_pdfioStreamOpen(pdfio_obj_t *obj, bool decode) _PDFIO_INTERNAL;
@ -380,6 +391,7 @@ extern void _pdfioTokenPush(_pdfio_token_t *tb, const char *token) _PDFIO_INTER
extern bool _pdfioTokenRead(_pdfio_token_t *tb, char *buffer, size_t bufsize);
extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL;
extern bool _pdfioValueDecrypt(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;
extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL;
extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, _pdfio_value_t *v, size_t depth) _PDFIO_INTERNAL;

View File

@ -7,10 +7,6 @@
// information.
//
//
// Include necessary headers...
//
#include "pdfio-private.h"
@ -452,6 +448,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
return (NULL);
}
PDFIO_DEBUG("_pdfioStreamOpen: ivlen=%d\n", (int)ivlen);
if (ivlen > 0)
_pdfioFileConsume(st->pdf, ivlen);
@ -567,6 +564,7 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
else
st->predictor = _PDFIO_PREDICTOR_NONE;
PDFIO_DEBUG("_pdfioStreamOpen: pos=%ld\n", (long)_pdfioFileTell(st->pdf));
if (sizeof(st->cbuffer) > st->remaining)
rbytes = _pdfioFileRead(st->pdf, st->cbuffer, st->remaining);
else
@ -1068,7 +1066,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out)
@ -1129,7 +1127,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))
@ -1199,7 +1197,7 @@ stream_read(pdfio_stream_t *st, // I - Stream
if ((status = inflate(&(st->flate), Z_NO_FLUSH)) < Z_OK)
{
_pdfioFileError(st->pdf, "Unable to decompress stream data: %s", zstrerror(status));
_pdfioFileError(st->pdf, "Unable to decompress stream data for object %ld: %s", (long)st->obj->number, zstrerror(status));
return (-1);
}
else if (status == Z_STREAM_END || (avail_in == st->flate.avail_in && avail_out == st->flate.avail_out))

View File

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

View File

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

View File

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

View File

@ -23,7 +23,7 @@ extern "C" {
// Version number...
//
# define PDFIO_VERSION "1.1.2"
# define PDFIO_VERSION "1.2.0"
//
@ -33,9 +33,11 @@ extern "C" {
# if defined(__has_extension) || defined(__GNUC__)
# define _PDFIO_PUBLIC __attribute__ ((visibility("default")))
# define _PDFIO_FORMAT(a,b) __attribute__ ((__format__(__printf__, a,b)))
# define _PDFIO_DEPRECATED __attribute__ ((deprecated)) _PDFIO_PUBLIC
# else
# define _PDFIO_PUBLIC
# define _PDFIO_FORMAT(a,b)
# define _PDFIO_DEPRECATED
# endif // __has_extension || __GNUC__
@ -63,7 +65,7 @@ typedef enum pdfio_encryption_e // PDF encryption modes
PDFIO_ENCRYPTION_RC4_40, // 40-bit RC4 encryption (PDF 1.3)
PDFIO_ENCRYPTION_RC4_128, // 128-bit RC4 encryption (PDF 1.4)
PDFIO_ENCRYPTION_AES_128, // 128-bit AES encryption (PDF 1.6)
PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0)
PDFIO_ENCRYPTION_AES_256 // 256-bit AES encryption (PDF 2.0) @exclude all@
} pdfio_encryption_t;
typedef enum pdfio_filter_e // Compression/decompression filters for streams
{
@ -180,10 +182,12 @@ extern bool pdfioDictSetStringf(pdfio_dict_t *dict, const char *key, const char
extern bool pdfioFileClose(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreate(const char *filename, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateArrayObj(pdfio_file_t *pdf, pdfio_array_t *array) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateNumberObj(pdfio_file_t *pdf, double number) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateObj(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateOutput(pdfio_output_cb_t output_cb, void *output_ctx, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
// TODO: Add number, array, string, etc. versions of pdfioFileCreateObject?
extern pdfio_stream_t *pdfioFileCreatePage(pdfio_file_t *pdf, pdfio_dict_t *dict) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileCreateStringObj(pdfio_file_t *pdf, const char *s) _PDFIO_PUBLIC;
extern pdfio_file_t *pdfioFileCreateTemporary(char *buffer, size_t bufsize, const char *version, pdfio_rect_t *media_box, pdfio_rect_t *crop_box, pdfio_error_cb_t error_cb, void *error_data) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileFindObj(pdfio_file_t *pdf, size_t number) _PDFIO_PUBLIC;
extern const char *pdfioFileGetAuthor(pdfio_file_t *pdf) _PDFIO_PUBLIC;

View File

@ -1,6 +1,13 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: pdfio
Description: PDF read/write library
Version: @PDFIO_VERSION@
URL: https://www.msweet.org/pdfio
Requires: zlib >= 1.0
Libs: -L${prefix}/lib -lpdfio -lm
Cflags: -I${prefix}/include
Requires: @PKGCONFIG_REQUIRES@
Libs: @PKGCONFIG_LIBS@
Libs.private: @PKGCONFIG_LIBS_PRIVATE@
Cflags: @PKGCONFIG_CFLAGS@

View File

@ -1,5 +1,5 @@
LIBRARY pdfio1
VERSION 1.0
VERSION 1.2
EXPORTS
_pdfioArrayDebug
_pdfioArrayDelete
@ -47,7 +47,9 @@ _pdfioFileSeek
_pdfioFileTell
_pdfioFileWrite
_pdfioObjDelete
_pdfioObjGetExtension
_pdfioObjLoad
_pdfioObjSetExtension
_pdfioStreamCreate
_pdfioStreamOpen
_pdfioStringIsAllocated
@ -136,8 +138,12 @@ pdfioContentSetTextXScaling
pdfioContentStroke
pdfioContentTextBegin
pdfioContentTextEnd
pdfioContentTextMeasure
pdfioContentTextMoveLine
pdfioContentTextMoveTo
pdfioContentTextNewLine
pdfioContentTextNewLineShow
pdfioContentTextNewLineShowf
pdfioContentTextNextLine
pdfioContentTextShow
pdfioContentTextShowJustified
@ -176,9 +182,11 @@ pdfioFileCreateFontObjFromFile
pdfioFileCreateICCObjFromFile
pdfioFileCreateImageObjFromData
pdfioFileCreateImageObjFromFile
pdfioFileCreateNumberObj
pdfioFileCreateObj
pdfioFileCreateOutput
pdfioFileCreatePage
pdfioFileCreateStringObj
pdfioFileCreateTemporary
pdfioFileFindObj
pdfioFileGetAuthor

View File

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

View File

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

View File

@ -1,7 +1,7 @@
//
// Test program for PDFio.
//
// Copyright © 2021 by Michael R Sweet.
// Copyright © 2021-2023 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
@ -10,11 +10,7 @@
//
// ./testpdfio
//
// ./testpdfio FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ...
//
//
// Include necessary headers...
// ./testpdfio [--verbose] FILENAME [OBJECT-NUMBER] [FILENAME [OBJECT-NUMBER]] ...
//
#include "pdfio-private.h"
@ -44,7 +40,7 @@ static int verify_image(pdfio_file_t *pdf, size_t number);
static int write_alpha_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_color_patch(pdfio_stream_t *st, bool device);
static int write_color_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
static int write_font_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font, bool unicode);
static int write_font_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font, const char *textfontfile, bool unicode);
static int write_header_footer(pdfio_stream_t *st, const char *title, int number);
static pdfio_obj_t *write_image_object(pdfio_file_t *pdf, _pdfio_predictor_t predictor);
static int write_images_test(pdfio_file_t *pdf, int number, pdfio_obj_t *font);
@ -455,7 +451,7 @@ do_test_file(const char *filename, // I - PDF filename
}
}
printf(" Page #%d is %gx%g.\n", (int)n + 1, media_box.x2, media_box.y2);
printf(" Page #%d (obj %d) is %gx%g.\n", (int)n + 1, (int)pdfioObjGetNumber(obj), media_box.x2, media_box.y2);
}
}
@ -2229,14 +2225,20 @@ write_color_test(pdfio_file_t *pdf, // I - PDF file
//
static int // O - 1 on failure, 0 on success
write_font_test(pdfio_file_t *pdf, // I - PDF file
int number, // I - Page number
pdfio_obj_t *font, // I - Page number font
bool unicode) // I - Use Unicode font?
write_font_test(
pdfio_file_t *pdf, // I - PDF file
int number, // I - Page number
pdfio_obj_t *font, // I - Page number font
const char *textfontfile, // I - Text font file
bool unicode) // I - Use Unicode font?
{
pdfio_dict_t *dict; // Page dictionary
pdfio_stream_t *st; // Page contents stream
pdfio_obj_t *opensans; // OpenSans-Regular font
pdfio_obj_t *textfont; // Text font
char title[256], // Page title
textname[256], // Name of text font
*ptr; // Pointer into name
double width; // Text width
int i; // Looping var
static const char * const welcomes[] =// "Welcome" in many languages
{
@ -2387,28 +2389,16 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
"Märr-ŋamathirri",
"Ẹ ku abọ",
"Kíimak 'oolal",
"Ngiyakwemukela"
"Ngiyakwemukela",
"いらっしゃいませ"
};
#if 0
if (unicode)
{
fputs("pdfioFileCreateFontObjFromFile(NotoSansJP-Regular.otf): ", stdout);
if ((opensans = pdfioFileCreateFontObjFromFile(pdf, "testfiles/NotoSansJP-Regular.otf", true)) != NULL)
puts("PASS");
else
return (1);
}
printf("pdfioFileCreateFontObjFromFile(%s): ", textfontfile);
if ((textfont = pdfioFileCreateFontObjFromFile(pdf, textfontfile, unicode)) != NULL)
puts("PASS");
else
#endif // 0
{
fputs("pdfioFileCreateFontObjFromFile(OpenSans-Regular.ttf): ", stdout);
if ((opensans = pdfioFileCreateFontObjFromFile(pdf, "testfiles/OpenSans-Regular.ttf", unicode)) != NULL)
puts("PASS");
else
return (1);
}
return (1);
fputs("pdfioDictCreate: ", stdout);
if ((dict = pdfioDictCreate(pdf)) != NULL)
@ -2423,7 +2413,7 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
return (1);
fputs("pdfioPageDictAddFont(F2): ", stdout);
if (pdfioPageDictAddFont(dict, "F2", opensans))
if (pdfioPageDictAddFont(dict, "F2", textfont))
puts("PASS");
else
return (1);
@ -2435,7 +2425,21 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
else
return (1);
if (write_header_footer(st, unicode ? "Unicode TrueType Font Test" : "CP1252 TrueType Font Test", number))
if ((ptr = strrchr(textfontfile, '/')) != NULL)
strncpy(textname, ptr + 1, sizeof(textname) - 1);
else
strncpy(textname, textfontfile, sizeof(textname) - 1);
textname[sizeof(textname) - 1] = '\0';
if ((ptr = strrchr(textname, '.')) != NULL)
*ptr = '\0';
if (unicode)
snprintf(title, sizeof(title), "Unicode %s Font Test", textname);
else
snprintf(title, sizeof(title), "CP1252 %s Font Test", textname);
if (write_header_footer(st, title, number))
goto error;
fputs("pdfioContentTextBegin(): ", stdout);
@ -2456,8 +2460,8 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
else
goto error;
fputs("pdfioContentTextMoveTo(36.0, 702.0): ", stdout);
if (pdfioContentTextMoveTo(st, 36.0, 702.0))
fputs("pdfioContentTextMoveTo(198.0, 702.0): ", stdout);
if (pdfioContentTextMoveTo(st, 198.0, 702.0))
puts("PASS");
else
return (1);
@ -2466,18 +2470,36 @@ write_font_test(pdfio_file_t *pdf, // I - PDF file
{
if (i > 0 && (i % 50) == 0)
{
fputs("pdfioContentTextMoveTo(200.0, 600.0): ", stdout);
if (pdfioContentTextMoveTo(st, 200.0, 600.0))
fputs("pdfioContentTextMoveTo(162.0, 600.0): ", stdout);
if (pdfioContentTextMoveTo(st, 162.0, 600.0))
puts("PASS");
else
return (1);
}
printf("pdfioContentTextMeasure(\"%s\"): ", welcomes[i]);
if ((width = pdfioContentTextMeasure(textfont, welcomes[i], 10.0)) >= 0.0)
puts("PASS");
else
return (1);
printf("pdfioContextTextMoveTo(%g, 0.0): ", -width);
if (pdfioContentTextMoveTo(st, -width, 0.0))
puts("PASS");
else
return (1);
printf("pdfioContentTextShowf(\"%s\"): ", welcomes[i]);
if (pdfioContentTextShowf(st, unicode, "%s\n", welcomes[i]))
puts("PASS");
else
return (1);
printf("pdfioContextTextMoveTo(%g, 0.0): ", width);
if (pdfioContentTextMoveTo(st, width, 0.0))
puts("PASS");
else
return (1);
}
fputs("pdfioContentTextEnd(): ", stdout);
@ -3390,14 +3412,17 @@ write_unit_file(
return (1);
// Test TrueType fonts...
if (write_font_test(outpdf, 9, helvetica, false))
if (write_font_test(outpdf, 9, helvetica, "testfiles/OpenSans-Regular.ttf", false))
return (1);
if (write_font_test(outpdf, 10, helvetica, true))
if (write_font_test(outpdf, 10, helvetica, "testfiles/OpenSans-Regular.ttf", true))
return (1);
if (write_font_test(outpdf, 11, helvetica, "testfiles/NotoSansJP-Regular.otf", true))
return (1);
// Print this text file...
if (write_text_test(outpdf, 11, helvetica, "README.md"))
if (write_text_test(outpdf, 12, helvetica, "README.md"))
return (1);
fputs("pdfioFileGetNumPages: ", stdout);

304
testttf.c Normal file
View File

@ -0,0 +1,304 @@
//
// Unit test program for TTF library
//
// https://github.com/michaelrsweet/ttf
//
// Copyright © 2018-2023 by Michael R Sweet.
//
// Licensed under Apache License v2.0. See the file "LICENSE" for more
// information.
//
// Usage:
//
// ./testttf [FILENAME]
//
#include <stdio.h>
#include "ttf.h"
//
// Local functions...
//
static void error_cb(void *data, const char *message);
static int test_font(const char *filename);
//
// 'main()' - Main entry for unit tests.
//
int // O - Exit status
main(int argc, // I - Number of command-line arguments
char *argv[]) // I - Command-line arguments
{
int i; // Looping var
int errors = 0; // Number of errors
if (argc > 1)
{
for (i = 1; i < argc; i ++)
errors += test_font(argv[i]);
}
else
{
// Test with the bundled TrueType files...
errors += test_font("testfiles/OpenSans-Bold.ttf");
errors += test_font("testfiles/OpenSans-Regular.ttf");
errors += test_font("testfiles/NotoSansJP-Regular.otf");
}
if (!errors)
puts("\nALL TESTS PASSED");
else
printf("\n%d TEST(S) FAILED\n", errors);
return (errors);
}
//
// 'error_cb()' - Error callback.
//
static void
error_cb(void *data, // I - User data (not used)
const char *message) // I - Message string
{
fprintf(stderr, "FAIL (%s)\n", message);
}
//
// 'test_font()' - Test a font file.
//
static int // O - Number of errors
test_font(const char *filename) // I - Font filename
{
int i, // Looping var
errors = 0; // Number of errors
ttf_t *font; // Font
const char *value; // Font (string) value
int intvalue; // Font (integer) value
float realvalue; // Font (real) value
ttf_rect_t bounds; // Bounds
ttf_rect_t extents; // Extents
size_t num_fonts; // Number of fonts
ttf_style_t style; // Font style
ttf_weight_t weight; // Font weight
static const char * const stretches[] =
{ // Font stretch strings
"TTF_STRETCH_NORMAL", // normal
"TTF_STRETCH_ULTRA_CONDENSED", // ultra-condensed
"TTF_STRETCH_EXTRA_CONDENSED", // extra-condensed
"TTF_STRETCH_CONDENSED", // condensed
"TTF_STRETCH_SEMI_CONDENSED", // semi-condensed
"TTF_STRETCH_SEMI_EXPANDED", // semi-expanded
"TTF_STRETCH_EXPANDED", // expanded
"TTF_STRETCH_EXTRA_EXPANDED", // extra-expanded
"TTF_STRETCH_ULTRA_EXPANDED" // ultra-expanded
};
static const char * const strings[] = // Test strings
{
"Hello, World!", // English
"مرحبا بالعالم!", // Arabic
"Bonjour le monde!", // French
"Γειά σου Κόσμε!", // Greek
"שלום עולם!", // Hebrew
"Привет мир!", // Russian
"こんにちは世界!" // Japanese
};
static const char * const styles[] = // Font style names
{
"TTF_STYLE_NORMAL",
"TTF_STYLE_ITALIC",
"TTF_STYLE_OBLIQUE"
};
printf("ttfCreate(\"%s\"): ", filename);
if ((font = ttfCreate(filename, 0, error_cb, NULL)) != NULL)
puts("PASS");
else
errors ++;
fputs("ttfGetAscent: ", stdout);
if ((intvalue = ttfGetAscent(font)) > 0)
{
printf("PASS (%d)\n", intvalue);
}
else
{
printf("FAIL (%d)\n", intvalue);
errors ++;
}
fputs("ttfGetBounds: ", stdout);
if (ttfGetBounds(font, &bounds))
{
printf("PASS (%g %g %g %g)\n", bounds.left, bounds.bottom, bounds.right, bounds.top);
}
else
{
puts("FAIL");
errors ++;
}
fputs("ttfGetCapHeight: ", stdout);
if ((intvalue = ttfGetCapHeight(font)) > 0)
{
printf("PASS (%d)\n", intvalue);
}
else
{
printf("FAIL (%d)\n", intvalue);
errors ++;
}
fputs("ttfGetCopyright: ", stdout);
if ((value = ttfGetCopyright(font)) != NULL)
{
printf("PASS (%s)\n", value);
}
else
{
puts("WARNING (no copyright found)");
}
for (i = 0; i < (int)(sizeof(strings) / sizeof(strings[0])); i ++)
{
printf("ttfGetExtents(\"%s\"): ", strings[i]);
if (ttfGetExtents(font, 12.0f, strings[i], &extents))
{
printf("PASS (%.1f %.1f %.1f %.1f)\n", extents.left, extents.bottom, extents.right, extents.top);
}
else
{
puts("FAIL");
errors ++;
}
}
fputs("ttfGetFamily: ", stdout);
if ((value = ttfGetFamily(font)) != NULL)
{
printf("PASS (%s)\n", value);
}
else
{
puts("FAIL");
errors ++;
}
fputs("ttfGetItalicAngle: ", stdout);
if ((realvalue = ttfGetItalicAngle(font)) >= -180.0 && realvalue <= 180.0)
{
printf("PASS (%g)\n", realvalue);
}
else
{
printf("FAIL (%g)\n", realvalue);
errors ++;
}
fputs("ttfGetNumFonts: ", stdout);
if ((num_fonts = ttfGetNumFonts(font)) > 0)
{
printf("PASS (%u)\n", (unsigned)num_fonts);
}
else
{
puts("FAIL");
errors ++;
}
fputs("ttfGetPostScriptName: ", stdout);
if ((value = ttfGetPostScriptName(font)) != NULL)
{
printf("PASS (%s)\n", value);
}
else
{
puts("FAIL");
errors ++;
}
fputs("ttfGetStretch: ", stdout);
if ((intvalue = (int)ttfGetStretch(font)) >= TTF_STRETCH_NORMAL && intvalue <= TTF_STRETCH_ULTRA_EXPANDED)
{
printf("PASS (%s)\n", stretches[intvalue]);
}
else
{
printf("FAIL (%d)\n", intvalue);
errors ++;
}
fputs("ttfGetStyle: ", stdout);
if ((style = ttfGetStyle(font)) >= TTF_STYLE_NORMAL && style <= TTF_STYLE_ITALIC)
{
printf("PASS (%s)\n", styles[style]);
}
else
{
puts("FAIL");
errors ++;
}
fputs("ttfGetVersion: ", stdout);
if ((value = ttfGetVersion(font)) != NULL)
{
printf("PASS (%s)\n", value);
}
else
{
puts("FAIL");
errors ++;
}
fputs("ttfGetWeight: ", stdout);
if ((weight = ttfGetWeight(font)) >= 0)
{
printf("PASS (%u)\n", (unsigned)weight);
}
else
{
puts("FAIL");
errors ++;
}
fputs("ttfGetWidth(' '): ", stdout);
if ((intvalue = ttfGetWidth(font, ' ')) > 0)
{
printf("PASS (%d)\n", intvalue);
}
else
{
printf("FAIL (%d)\n", intvalue);
errors ++;
}
fputs("ttfGetXHeight: ", stdout);
if ((intvalue = ttfGetXHeight(font)) > 0)
{
printf("PASS (%d)\n", intvalue);
}
else
{
printf("FAIL (%d)\n", intvalue);
errors ++;
}
fputs("ttfIsFixedPitch: ", stdout);
if (ttfIsFixedPitch(font))
puts("PASS (true)");
else
puts("PASS (false)");
ttfDelete(font);
return (errors);
}

52
ttf.c
View File

@ -71,7 +71,7 @@ typedef __int64 ssize_t; // POSIX type not present on Windows...
//
// DEBUG is typically defined for debug builds. TTF_DEBUG maps to printf when
// DEBUG is typically defined for debug builds. TTF_DEBUG maps to fprintf when
// DEBUG is defined and is a no-op otherwise...
//
@ -448,7 +448,7 @@ ttfCreate(const char *filename, // I - Filename
font->cap_height = font->ascent;
if (font->x_height == 0)
font->x_height = 3 * font->ascent / 5;
font->x_height = 3 * font->ascent / 5;
// Build a sparse glyph widths table...
font->min_char = -1;
@ -477,6 +477,11 @@ ttfCreate(const char *filename, // I - Filename
else
font->widths[bin][i & 255] = widths[glyph];
}
#ifdef DEBUG
if (i >= ' ' && i < 127 && font->widths[0])
TTF_DEBUG("ttfCreate: width['%c']=%d(%d)\n", (char)i, font->widths[0][i].width, font->widths[0][i].left_bearing);
#endif // DEBUG
}
// Cleanup and return the font...
@ -687,9 +692,6 @@ ttfGetExtents(
ch = *s++;
}
// Issue #1: Offset past ".notdef"...
ch ++;
// Find its width...
if ((widths = font->widths[ch / 256]) != NULL)
{
@ -736,17 +738,6 @@ ttfGetFamily(ttf_t *font) // I - Font
}
//
// 'ttfIsFixedPitch()' - Determine whether a font is fixedpitch.
//
bool // O - `true` if fixed pitch, `false` otherwise
ttfIsFixedPitch(ttf_t *font) // I - Font
{
return (font ? font->is_fixed : false);
}
//
// 'ttfGetItalicAngle()' - Get the italic angle.
//
@ -855,13 +846,14 @@ int // O - Width in 1000ths
ttfGetWidth(ttf_t *font, // I - Font
int ch) // I - Unicode character
{
int bin = ch >> 8; // Bin in widths array
int bin = ch >> 8; // Bin in widths array
// Range check input...
if (!font || ch < ' ' || ch == 0x7f)
return (0);
else if (font->widths[bin])
if (font->widths[bin])
return ((int)(1000.0f * font->widths[bin][ch & 255].width / font->units));
else if (font->widths[0]) // .notdef
return ((int)(1000.0f * font->widths[0][0].width / font->units));
@ -881,6 +873,17 @@ ttfGetXHeight(ttf_t *font) // I - Font
}
//
// 'ttfIsFixedPitch()' - Determine whether a font is fixedpitch.
//
bool // O - `true` if fixed pitch, `false` otherwise
ttfIsFixedPitch(ttf_t *font) // I - Font
{
return (font ? font->is_fixed : false);
}
//
// 'copy_name()' - Copy a name string from a font.
//
@ -1279,7 +1282,7 @@ read_cmap(ttf_t *font) // I - Font
// Based on the end code of the segent table, allocate space for the
// uncompressed cmap table...
segCount --; // Last segment is not used (sigh)
// segCount --; // Last segment is not used (sigh)
font->num_cmap = segments[segCount - 1].endCode + 1;
font->cmap = cmapptr = (int *)malloc(font->num_cmap * sizeof(int));
@ -1304,17 +1307,18 @@ read_cmap(ttf_t *font) // I - Font
{
// Use an "obscure indexing trick" (words from the spec, not
// mine) to look up the glyph index...
temp = segment->idRangeOffset / 2 + ch - segment->startCode + seg - segCount - 1;
temp = (int)(segment->idRangeOffset / 2 - segCount + (ch - segment->startCode) + (segment - segments));
if (temp < 0 || temp >= numGlyphIdArray || !glyphIdArray[temp])
TTF_DEBUG("read_cmap: ch=%d, temp=%d\n", ch, temp);
if (temp < 0 || temp >= numGlyphIdArray)
glyph = -1;
else
glyph = ((glyphIdArray[temp] + segment->idDelta) & 65535) - 1;
glyph = (glyphIdArray[temp] + segment->idDelta) & 65535;
}
else
{
// Just use idDelta to compute a glyph index...
glyph = ((ch + segment->idDelta) & 65535) - 1;
glyph = (ch + segment->idDelta) & 65535;
}
cmapptr[ch] = glyph;
@ -1581,6 +1585,8 @@ read_hmtx(ttf_t *font, // I - Font
{
widths[i].width = (short)read_ushort(font);
widths[i].left_bearing = (short)read_short(font);
TTF_DEBUG("read_hmtx: widths[%d].width=%d, .left_bearing=%d\n", i, widths[i].width, widths[i].left_bearing);
}
return (widths);